diff --git a/.agent/rules/commit_style.md b/.agent/rules/commit_style.md new file mode 100644 index 000000000000..668bbbbde84e --- /dev/null +++ b/.agent/rules/commit_style.md @@ -0,0 +1,28 @@ +--- +trigger: always_on +--- + +# Agent Commit Rules + +The user requires all commit messages generated by the agent to follow a specific style and include a proper sign-off. + +## Commit Message Style + +Commit messages must be formatted with a subject line and a body, following this format: +``` +: + + +``` + +* **``**: A short name or tag representing the feature or component being modified. +* **``**: A succinct one-line description of the change. +* **``**: A detailed description of the changes made in the commit. The body must be at least one sentence describing the changes. + +## Sign-off + +Every commit message must end with a `Signed-off-by:` line using the patch author's name and email (from the local git config): + +``` +Signed-off-by: +``` diff --git a/.agent/rules/documentation.md b/.agent/rules/documentation.md new file mode 100644 index 000000000000..5d88e6aa3dd4 --- /dev/null +++ b/.agent/rules/documentation.md @@ -0,0 +1,13 @@ +# Documentation Rules + +The user expects all new features and in-code documentation to adhere to Doxygen standards. + +## Doxygen Requirements + +1. **Mandatory Documentation:** All new features, functions, and structures must include Doxygen comments describing their purpose, parameters, and return values. +2. **Clean Build:** Any in-code documentation added or modified must build with Doxygen without producing any new errors or warnings. +3. **Format:** Use standard Doxygen formatting tags (e.g., `@brief`, `@param`, `@return` or `\brief`, `\param`, `\return`). Ensure the styling matches the existing codebase conventions. + +## Directory Documentation + +When creating a new file or modifying an existing one, check if there is an `architecture.md` or `readme.md` (or `README.md`) file in the same directory. If present, evaluate whether the code changes require an update to these documentation files and make the necessary updates to keep them synchronized with the code. diff --git a/.agent/rules/topology2_design.md b/.agent/rules/topology2_design.md new file mode 100644 index 000000000000..35d98473a57c --- /dev/null +++ b/.agent/rules/topology2_design.md @@ -0,0 +1,12 @@ +--- +trigger: glob +glob: tools/topology/topology2/** +--- + +# Topology2 Design Rule + +When working in tools/topology/topology2, follow the canonical guidance in +.github/instructions/topology2-design.instructions.md. + +Apply those instructions for topology structure, ID assignment, routing, +platform overrides, and topology target registration. \ No newline at end of file diff --git a/.agent/workflows/build_and_validate.md b/.agent/workflows/build_and_validate.md new file mode 100644 index 000000000000..7a3614fc8254 --- /dev/null +++ b/.agent/workflows/build_and_validate.md @@ -0,0 +1,22 @@ +--- +description: Build and validate new C code features +--- + +This workflow describes the process for building and validating any new C code features in the SOF repository. + +**Note:** The QEMU build targets must be used for both building and testing. The user requires the build must be error and warning free and the ztests must all pass. + +// turbo-all +1. Build the new C code feature using the `xtensa-build-zephyr.py` script. + ```bash + source ../.venv/bin/activate + ./scripts/xtensa-build-zephyr.py qemu_xtensa + ``` + +2. Validate the feature with a ztest run using the `sof-qemu-run.sh` script. + ```bash + source ../.venv/bin/activate + ./scripts/sof-qemu-run.sh build-qemu_xtensa + ``` + +3. Ensure that all new features and functions have appropriate Doxygen comments and that the Doxygen documentation builds without errors or warnings. \ No newline at end of file diff --git a/.agent/workflows/module_development.md b/.agent/workflows/module_development.md new file mode 100644 index 000000000000..6036da58034d --- /dev/null +++ b/.agent/workflows/module_development.md @@ -0,0 +1,22 @@ +--- +description: Develop and validate new audio processing modules +--- + +This workflow describes the expected steps to create and validate a new audio processing module within the SOF repository. + +// turbo-all +1. **(Optional)** Generate the module skeleton using the `sdk-create-module.py` script. + ```bash + # Run the script with relevant arguments to create a new module template + ./scripts/sdk-create-module.py --name --version + ``` + +2. Develop the module logic within the generated skeleton. + +3. Validate the module by executing the module within the host testbench. This ensures that the module functions as expected outside of full system simulations. + ```bash + # Configure and run the testbench against the developed module + ./scripts/host-testbench.sh -l + ``` + +4. Document the new module using Doxygen comments. Validate that the Doxygen build completes without errors or warnings. Add a README.md for the module. \ No newline at end of file diff --git a/.clang-format b/.clang-format new file mode 100644 index 000000000000..145353dd0a2d --- /dev/null +++ b/.clang-format @@ -0,0 +1,121 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# Note: The list of ForEachMacros can be obtained using: +# +# git grep -h '^#define [^[:space:]]*FOR_EACH[^[:space:]]*(' include/ \ +# | sed "s,^#define \([^[:space:]]*FOR_EACH[^[:space:]]*\)(.*$, - '\1'," \ +# | sort | uniq +# +# References: +# - https://clang.llvm.org/docs/ClangFormatStyleOptions.html +# +# Based on https://github.com/zephyrproject-rtos/zephyr/blob/main/.clang-format + +--- +BasedOnStyle: LLVM +AlignConsecutiveMacros: AcrossComments +AllowShortBlocksOnASingleLine: Never +AllowShortCaseLabelsOnASingleLine: false +AllowShortEnumsOnASingleLine: false +AllowShortFunctionsOnASingleLine: None +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +AttributeMacros: + - __aligned + - __deprecated + - __packed + - __printf_like + - __syscall + - __syscall_always_inline + - __subsystem +BitFieldColonSpacing: After +BreakBeforeBraces: Linux +ColumnLimit: 100 +ConstructorInitializerIndentWidth: 8 +ContinuationIndentWidth: 8 +ForEachMacros: + - 'ARRAY_FOR_EACH' + - 'ARRAY_FOR_EACH_PTR' + - 'FOR_EACH' + - 'FOR_EACH_FIXED_ARG' + - 'FOR_EACH_IDX' + - 'FOR_EACH_IDX_FIXED_ARG' + - 'FOR_EACH_NONEMPTY_TERM' + - 'FOR_EACH_FIXED_ARG_NONEMPTY_TERM' + - 'RB_FOR_EACH' + - 'RB_FOR_EACH_CONTAINER' + - 'SYS_DLIST_FOR_EACH_CONTAINER' + - 'SYS_DLIST_FOR_EACH_CONTAINER_SAFE' + - 'SYS_DLIST_FOR_EACH_NODE' + - 'SYS_DLIST_FOR_EACH_NODE_SAFE' + - 'SYS_SEM_LOCK' + - 'SYS_SFLIST_FOR_EACH_CONTAINER' + - 'SYS_SFLIST_FOR_EACH_CONTAINER_SAFE' + - 'SYS_SFLIST_FOR_EACH_NODE' + - 'SYS_SFLIST_FOR_EACH_NODE_SAFE' + - 'SYS_SLIST_FOR_EACH_CONTAINER' + - 'SYS_SLIST_FOR_EACH_CONTAINER_SAFE' + - 'SYS_SLIST_FOR_EACH_NODE' + - 'SYS_SLIST_FOR_EACH_NODE_SAFE' + - '_WAIT_Q_FOR_EACH' + - '_WAIT_Q_FOR_EACH_SAFE' + - 'Z_FOR_EACH' + - 'Z_FOR_EACH_ENGINE' + - 'Z_FOR_EACH_EXEC' + - 'Z_FOR_EACH_FIXED_ARG' + - 'Z_FOR_EACH_FIXED_ARG_EXEC' + - 'Z_FOR_EACH_IDX' + - 'Z_FOR_EACH_IDX_EXEC' + - 'Z_FOR_EACH_IDX_FIXED_ARG' + - 'Z_FOR_EACH_IDX_FIXED_ARG_EXEC' + - 'Z_GENLIST_FOR_EACH_CONTAINER' + - 'Z_GENLIST_FOR_EACH_CONTAINER_SAFE' + - 'Z_GENLIST_FOR_EACH_NODE' + - 'Z_GENLIST_FOR_EACH_NODE_SAFE' + - 'STRUCT_SECTION_FOREACH' + - 'STRUCT_SECTION_FOREACH_ALTERNATE' + - 'TYPE_SECTION_FOREACH' + - 'K_SPINLOCK' + - 'COAP_RESOURCE_FOREACH' + - 'COAP_SERVICE_FOREACH' + - 'COAP_SERVICE_FOREACH_RESOURCE' + - 'HTTP_RESOURCE_FOREACH' + - 'HTTP_SERVER_CONTENT_TYPE_FOREACH' + - 'HTTP_SERVICE_FOREACH' + - 'HTTP_SERVICE_FOREACH_RESOURCE' + - 'I3C_BUS_FOR_EACH_I3CDEV' + - 'I3C_BUS_FOR_EACH_I3CDEV_SAFE' + - 'I3C_BUS_FOR_EACH_I2CDEV' + - 'I3C_BUS_FOR_EACH_I2CDEV_SAFE' + - 'MIN_HEAP_FOREACH' +IfMacros: + - 'CHECKIF' +# Disabled for now, see bug https://github.com/zephyrproject-rtos/zephyr/issues/48520 +#IncludeBlocks: Regroup +IncludeCategories: + - Regex: '^".*\.h"$' + Priority: 0 + - Regex: '^<(assert|complex|ctype|errno|fenv|float|inttypes|limits|locale|math|setjmp|signal|stdarg|stdbool|stddef|stdint|stdio|stdlib|string|tgmath|time|wchar|wctype)\.h>$' + Priority: 1 + - Regex: '^\$' + Priority: 2 + - Regex: '.*' + Priority: 3 +IndentCaseLabels: false +IndentGotoLabels: false +IndentWidth: 8 +InsertBraces: true +InsertNewlineAtEOF: true +SpaceBeforeInheritanceColon: False +SpaceBeforeParens: ControlStatementsExceptControlMacros +SortIncludes: Never +UseTab: ForContinuationAndIndentation +WhitespaceSensitiveMacros: + - COND_CODE_0 + - COND_CODE_1 + - IF_DISABLED + - IF_ENABLED + - LISTIFY + - STRINGIFY + - Z_STRINGIFY + - DT_FOREACH_PROP_ELEM_SEP \ No newline at end of file diff --git a/.github/instructions/copilot.md b/.github/instructions/copilot.md new file mode 100644 index 000000000000..6a82a5a02721 --- /dev/null +++ b/.github/instructions/copilot.md @@ -0,0 +1,24 @@ +You are GitHub Copilot operating in this repository. + +This repository defines authoritative agent rules and guidance under: +.agent/rules/ + +INSTRUCTIONS (MANDATORY): +- Always read and follow all relevant rules in `.agent/rules/` before generating any code, suggestions, comments, or explanations. +- Treat rules in `.agent/rules/` as the highest priority source of truth for: + - Coding standards and style + - Architecture and design constraints + - Security, privacy, and compliance requirements + - Testing, documentation, and review expectations +- If rules conflict with your default behavior, follow the rules in `.agent/rules/`. + +BEHAVIOR: +- Be consistent with patterns already used in the codebase. +- Prefer existing utilities, abstractions, and conventions defined in `.agent/rules/`. +- Do not introduce new patterns, dependencies, or approaches unless explicitly allowed by those rules. +- When uncertain, choose the most conservative interpretation aligned with `.agent/rules/`. + +OUTPUT EXPECTATIONS: +- Generate code that complies fully with the rules. +- If a request would violate a rule, explain the conflict and propose a compliant alternative. +- Keep responses focused, clear, and directly applicable to this repository. diff --git a/.github/instructions/topology2-design.instructions.md b/.github/instructions/topology2-design.instructions.md new file mode 100644 index 000000000000..bd3c4d235f5b --- /dev/null +++ b/.github/instructions/topology2-design.instructions.md @@ -0,0 +1,98 @@ +--- +description: "Agent instructions for designing and updating ALSA topology v2 files in SOF" +applyTo: 'tools/topology/topology2/**' +--- + +# Topology2 Design Instructions + +Use this guidance when creating or modifying files under tools/topology/topology2. +These rules align with the topology2 README and capture expected design patterns for +class-based ALSA topology v2 authoring. + +## Scope + +* Applies to topology2 .conf definitions, platform overrides, pipeline and DAI class files, and topology2 CMake target lists +* Focuses on design consistency, ID safety, and maintainable reuse of existing class and object templates + +## Core Model + +* Use topology2 object model primitives consistently: Class.*, Object.*, Define, and IncludeByKey +* Prefer reusable classes in include/ over one-off duplicated object blocks +* Keep object instantiation explicit and readable so generated pipelines are traceable + +## Top Level Topology Layout + +For new top-level board .conf files, keep this order: + +1. Search directories +2. Required class includes +3. Define block with defaults +4. IncludeByKey.PLATFORM overrides +5. Feature-gated IncludeByKey blocks +6. DAI, pipeline, and PCM objects +7. Route definitions + +## Reuse Before New Base Files + +* Prefer extending an existing base input .conf with variable overrides from CMake targets +* Add a new base .conf only when existing topologies cannot represent the use case cleanly +* When adding a new target, use this tuple structure. In CMake quoted strings, escape each semicolon as `\;`. + +```text +Logical tuple format: +"input-conf;output-name;variable1=value1,variable2=value2" + +CMake string form: +"input-conf\;output-name\;variable1=value1,variable2=value2" +``` + +## ID Conventions and Safety + +* Keep PCM IDs unique within a single topology +* Keep pipeline indexes unique within a single topology +* Pair FE and BE pipelines as N and N+1 where applicable +* For SoundWire pipelines, follow index equals PCM ID times 10 unless a documented topology-specific exception exists +* For HDMI pipelines, keep stride-10 layout with host at N0 and DAI at N1 +* When combining features such as SDW, PCH DMIC, HDMI, deep buffer, or compress, verify there are no ID collisions after overrides + +## Routing Rules + +* Connect FE mixin outputs to BE mixout inputs using Object.Base.route +* Keep route naming and widget references aligned with topology naming patterns +* Validate that each route endpoint maps to a declared widget in the same compiled topology + +## Widget Naming + +* Follow naming pattern type.pipeline-index.instance +* Keep naming stable and descriptive for easier graph inspection and debug + +## Platform Overrides + +* Use IncludeByKey.PLATFORM for platform-specific Define overrides +* Restrict platform-specific tuning to platform/intel/*.conf instead of duplicating board-level logic +* Ensure platform keys remain consistent with the authoritative in-tree overrides under tools/topology/topology2/platform/intel/*.conf; current examples include tgl, adl, mtl, lnl, ptl, and nvl + +## CMake Target Placement + +Register targets in the correct file for platform generation: + +* Tiger Lake and Alder Lake: production/tplg-targets-cavs25.cmake +* Meteor Lake: production/tplg-targets-ace1.cmake +* Lunar Lake: production/tplg-targets-ace2.cmake +* Panther Lake: production/tplg-targets-ace3.cmake +* Nova Lake / sof-nvl-*: production/tplg-targets-ace4.cmake +* i.MX8 platforms: production/tplg-targets-imx8.cmake +* SDCA generic topologies: production/tplg-targets-sdca-generic.cmake +* HDA generic: production/tplg-targets-hda-generic.cmake +* Development and test topologies: development/tplg-targets.cmake + +If a target family is not listed above, use the existing tplg-targets-*.cmake +file that already contains similar topologies as the source of truth, and keep new +targets grouped with the same platform or product family in either production/ or +development/. + +## Validation Expectations + +* Keep topology2 buildable through the topologies2 target +* Preserve compatibility with alsatplg pre-processing mode used by the build system +* Ensure topology edits remain synchronized with nearby architecture or README documentation when design behavior changes diff --git a/.github/workflows/SPDX-README.md b/.github/workflows/SPDX-README.md index 14d50291dbe9..5bc30f0cc9b5 100644 --- a/.github/workflows/SPDX-README.md +++ b/.github/workflows/SPDX-README.md @@ -1,7 +1,7 @@ Read this section if there are some SPDX warnings above. -Pleasing checkpatch is hard when adding new files because: +Adding correct SPDX headers to new files can be tricky because: -- checkpatch requests a different SPDX style for .c versus .h files. +- a different SPDX style is expected for .c versus .h files. This is because some .h files are included in linker scripts or assembly code. - Some SOF reviewers reject C99 comments starting with // diff --git a/.github/workflows/build_all.yml b/.github/workflows/build_all.yml index fba00fbb61f2..19fee4a98ca4 100644 --- a/.github/workflows/build_all.yml +++ b/.github/workflows/build_all.yml @@ -8,6 +8,9 @@ name: Build test all components # yamllint disable-line rule:truthy on: [pull_request, workflow_dispatch, workflow_call] +permissions: + contents: read + jobs: stub-build: diff --git a/.github/workflows/checkpatch_list.sh b/.github/workflows/checkpatch_list.sh deleted file mode 100755 index 921f35ad25b1..000000000000 --- a/.github/workflows/checkpatch_list.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/bin/sh -# SPDX-License-Identifier: BSD-3-Clause - -set -e - -# The purpose of this script is not to stop on the first checkpatch -# failure and report at the end instead. -# -# Sample invocation: -# $0 --codespell --strict < PR_SHAs.txt -main() -{ - local failures=0 - while read -r sha ; do - printf '\n -------------- \n\n' - ./scripts/checkpatch.pl $@ -g $sha || failures=$((failures+1)) - done - printf '\n -------------- \n\n' - - if [ $failures -ne 0 ]; then - cat .github/workflows/SPDX-README.md - fi - - return $failures -} - -main "$@" diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 000000000000..4f7c29d6c831 --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,50 @@ +--- +name: "CodeQL Analysis" +# yamllint disable-line rule:truthy +on: + pull_request: + branches: + - 'main' + +permissions: + contents: read + +# Specifies group name that stops previous workflows if the name matches +concurrency: + group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.head_ref || github.ref }} + cancel-in-progress: true + +jobs: + analyze: + name: Analyze GitHub Actions Workflows + runs-on: ubuntu-latest + permissions: + security-events: write # Required to upload SARIF results + actions: read # Required to read workflow information + contents: read # Required to checkout repository + + strategy: + fail-fast: false + matrix: + language: ['actions'] # Analyze GitHub Actions workflows + + steps: + - name: Checkout repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + + - name: Initialize CodeQL + uses: github/codeql-action/init@0d579ffd059c29b07949a3cce3983f0780820c98 # v4 + with: + languages: ${{ matrix.language }} + # Optional: Specify custom queries + # queries: security-extended,security-and-quality + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@0d579ffd059c29b07949a3cce3983f0780820c98 # v4 + with: + category: "/language:${{ matrix.language }}" + upload: true + # Upload SARIF results to GitHub Security tab + output: sarif-results diff --git a/.github/workflows/codestyle.yml b/.github/workflows/codestyle.yml index 63a076fc086a..ea9e0a20d150 100644 --- a/.github/workflows/codestyle.yml +++ b/.github/workflows/codestyle.yml @@ -14,45 +14,10 @@ name: codestyle # yamllint disable-line rule:truthy on: [pull_request, workflow_call, workflow_dispatch] -jobs: - checkpatch: - runs-on: ubuntu-22.04 - - strategy: - fail-fast: false - matrix: - strictness: [null, --subjective] - - env: - PR_NUM: ${{github.event.number}} - # TODO: reduce duplication with scripts/sof-*-commit-hook.sh - # thanks to either some .conf file or some wrapper script - CHK_CMD_OPTS: --ignore UNKNOWN_COMMIT_ID --codespell - - steps: - # depth 2 so: - # ^1. we can show the Subject of the current target branch tip - # ^2. we reconnect/graft to the later fetch pull/1234/head, - - uses: actions/checkout@v4 - with: {fetch-depth: 2} - - - name: install codespell - run: sudo apt-get -y install codespell && dpkg -L codespell | grep dict - - # See shallowness issue https://github.com/thesofproject/linux/issues/2556 - - name: fetch PR commits - run: | - .github/workflows/shallowfetchPRcommits.sh \ - ${GITHUB_REPOSITORY} "$PR_NUM" - # show what we got - git --no-pager log --oneline --graph --decorate --max-count=50 - - - name: checkpatch - env: - STRICTNESS: ${{ matrix.strictness }} - run: .github/workflows/checkpatch_list.sh ${CHK_CMD_OPTS} - ${STRICTNESS} < PR_SHAs.txt +permissions: + contents: read +jobs: yamllint: runs-on: ubuntu-latest steps: diff --git a/.github/workflows/daily-tests.yml b/.github/workflows/daily-tests.yml index 40b90b01fe8e..139c946c3d84 100644 --- a/.github/workflows/daily-tests.yml +++ b/.github/workflows/daily-tests.yml @@ -13,6 +13,9 @@ on: # Allows you to run this workflow manually from the Actions tab workflow_dispatch: +permissions: + contents: read + jobs: # Keep in .yml alphabetical order diff --git a/.github/workflows/ipc_fuzzer.yml b/.github/workflows/ipc_fuzzer.yml index 9b21ca03041b..755dbf4b5d9b 100644 --- a/.github/workflows/ipc_fuzzer.yml +++ b/.github/workflows/ipc_fuzzer.yml @@ -22,6 +22,9 @@ on: pull_request: # TODO: can we provide a default inputs here too? +permissions: + contents: read + jobs: simple-IPC-fuzz_sh: diff --git a/.github/workflows/llext.yml b/.github/workflows/llext.yml index 21b812b47600..149c9e2e3571 100644 --- a/.github/workflows/llext.yml +++ b/.github/workflows/llext.yml @@ -7,9 +7,18 @@ name: Zephyr LLEXT # yamllint disable-line rule:truthy on: [pull_request, workflow_dispatch] +defaults: + run: + shell: bash + +permissions: + contents: read + jobs: build: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 + container: + image: thesofproject/zephyr-lite:v0.29.0 strategy: fail-fast: false @@ -17,33 +26,30 @@ jobs: platform: [mtl, lnl] steps: - - name: free space - run: | - sudo rm -rf /usr/share/dotnet - sudo rm -rf /opt/ghc - - - name: git clone sof + - name: checkout uses: actions/checkout@v4 with: - path: ./workspace/sof + path: sof fetch-depth: 0 # fix git describe filter: 'tree:0' - - name: west clones - run: pip3 install west && cd workspace/sof/ && west init -l && - west update --narrow --fetch-opt=--depth=5 + - name: west update + working-directory: sof + run: | + west init -l + west update --narrow --fetch-opt=--depth=5 - - name: Download docker image && ls /opt/toolchains/ - run: cd workspace && ./sof/zephyr/docker-run.sh ls -l /opt/toolchains/ + - name: print all available sdks in /opt/toolchains/ + run: | + ls -l /opt/toolchains/ - name: llext build run: | - cd workspace && ./sof/zephyr/docker-run.sh /bin/sh -c \ - "ln -s /opt/toolchains/zephyr-sdk-* ~/; - python sof/scripts/xtensa-build-zephyr.py \ - --cmake-args=-DEXTRA_CFLAGS=-Werror \ - --cmake-args=-DEXTRA_CXXFLAGS=-Werror \ - --cmake-args=-DEXTRA_AFLAGS='-Werror -Wa,--fatal-warnings' \ - --cmake-args=--warn-uninitialized \ - --overlay=sof/app/configs/${{ matrix.platform }}/modules.conf \ - ${{ matrix.platform }}" + ln -s /opt/toolchains/zephyr-sdk-* ~/ + python sof/scripts/xtensa-build-zephyr.py \ + --cmake-args=-DEXTRA_CFLAGS=-Werror \ + --cmake-args=-DEXTRA_CXXFLAGS=-Werror \ + --cmake-args=-DEXTRA_AFLAGS='-Werror -Wa,--fatal-warnings' \ + --cmake-args=--warn-uninitialized \ + --overlay=sof/app/configs/${{ matrix.platform }}/modules.conf \ + ${{ matrix.platform }} diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index 886d1b328ba7..0bd3865dec97 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -31,10 +31,13 @@ on: # Allows to call this forkflow from other workflows workflow_call: +permissions: + contents: read + jobs: doxygen: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/repro-build.yml b/.github/workflows/repro-build.yml index 034ae4ac286a..abe48a8cecb2 100644 --- a/.github/workflows/repro-build.yml +++ b/.github/workflows/repro-build.yml @@ -14,9 +14,12 @@ name: Reproducible builds # yamllint disable-line rule:truthy on: [pull_request, workflow_dispatch, workflow_call] +permissions: + contents: read + jobs: main: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/rimage.yml b/.github/workflows/rimage.yml index b6e66c23c449..53059681ae9d 100644 --- a/.github/workflows/rimage.yml +++ b/.github/workflows/rimage.yml @@ -22,11 +22,14 @@ on: paths: - tools/rimage/** +permissions: + contents: read + jobs: # Basic build test build: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 env: # FIXME: add -Wpointer-arith _CFLGS: -Werror -Wall -Wmissing-prototypes @@ -46,7 +49,7 @@ jobs: # cppcheck cppcheck: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 with: {submodules: recursive, fetch-depth: 0, filter: 'tree:0'} diff --git a/.github/workflows/sof-docs.yml b/.github/workflows/sof-docs.yml index 5b647816b93c..63af4acfc714 100644 --- a/.github/workflows/sof-docs.yml +++ b/.github/workflows/sof-docs.yml @@ -17,6 +17,9 @@ on: # Allows to call this forkflow from other workflows workflow_call: +permissions: + contents: read + jobs: # This is unfortunately a mix of sof-docs/.github/ + pull-request.yml#doxygen @@ -56,4 +59,4 @@ jobs: run: pip install -r sof-docs/scripts/requirements.txt - name: build sof-docs - run: make -C sof-docs/ html SOF_DOC_BUILD="$(pwd)"/doxybuild/ + run: LAX=1 make -C sof-docs/ html SOF_DOC_BUILD="$(pwd)"/doxybuild/ diff --git a/.github/workflows/sparse-zephyr.yml b/.github/workflows/sparse-zephyr.yml index edb484c42a50..5afa6361f51a 100644 --- a/.github/workflows/sparse-zephyr.yml +++ b/.github/workflows/sparse-zephyr.yml @@ -7,6 +7,13 @@ name: Sparse Zephyr # yamllint disable-line rule:truthy on: [push, pull_request, workflow_dispatch, workflow_call] +defaults: + run: + shell: bash + +permissions: + contents: read + jobs: # As of sparse commit ce1a6720f69e / Sept 2022, the exit status of # sparse.c is an unusable mess and always zero in practice. Moreover @@ -14,10 +21,15 @@ jobs: # small subset of specific warnings defined in # sof/scripts/parse_sparse_output.sh warnings-subset: + # disable until https://github.com/zephyrproject-rtos/zephyr/issues/93444 + # is fixed + if: false # We're sharing the sparse binary with the zephyr-build container so keep # this in sync with it. runs-on: ubuntu-24.04 + container: + image: thesofproject/zephyr-lite:v0.29.0 strategy: fail-fast: false @@ -25,43 +37,39 @@ jobs: platform: [tgl, mtl, lnl] steps: - - name: free space - run: | - sudo rm -rf /usr/share/dotnet - sudo rm -rf /opt/ghc - - name: git clone sparse analyzer uses: actions/checkout@v4 with: repository: thesofproject/sparse fetch-depth: 0 filter: 'tree:0' - path: workspace/sparse + path: sparse # As of its 2023 commit 98b203419679, sparse-llvm.c uses symbols # (LLVMConstGEP, LLVMBuildLoad, LLVMBuildCall,...) which are: # - -Wdeprecated in LLVM v14 # - Removed in LLVM v16 - name: build sparse analyzer - run: cd workspace/sparse && make -j4 # HAVE_LLVM=no + working-directory: sparse + run: | + make -j4 # HAVE_LLVM=no - name: git clone sof uses: actions/checkout@v4 with: - path: ./workspace/sof + path: sof fetch-depth: 0 # fix git describe filter: 'tree:0' - - name: west clones - run: pip3 install west && cd workspace/sof/ && west init -l && - west update --narrow --fetch-opt=--depth=5 + - name: west update + working-directory: sof + run: | + west init -l + west update --narrow --fetch-opt=--depth=5 - # Not strictly necessary but saves a lot of scrolling in the next step - # Caching a 12G image is unfortunately not possible: - # https://github.com/ScribeMD/docker-cache/issues/304 - # For faster builds we would have to pay for some persistent runners. - - name: Download docker image && ls /opt/toolchains/ - run: cd workspace && ./sof/zephyr/docker-run.sh ls -l /opt/toolchains/ + - name: print all available sdks in /opt/toolchains/ + run: | + ls -l /opt/toolchains/ # --pristine is important to reproduce _warnings_. It makes no # difference for github but it's useful for anyone trying to @@ -69,13 +77,11 @@ jobs: # "sparse" is currently incompatible with PICOLIBC (the new Zephyr default), # see https://github.com/zephyrproject-rtos/zephyr/issues/63003 - name: analyze zephyr - working-directory: ./workspace run: | - ./sof/zephyr/docker-run.sh \ - ./sof/zephyr/docker-build.sh ${{ matrix.platform }} \ - --cmake-args=-DZEPHYR_SCA_VARIANT=sparse --cmake-args=-DCONFIG_LOG_USE_VLA=n \ - --cmake-args=-DCONFIG_MINIMAL_LIBC=y \ - --pristine 2>&1 | tee _.log + ./sof/zephyr/docker-build.sh ${{ matrix.platform }} \ + --cmake-args=-DZEPHYR_SCA_VARIANT=sparse --cmake-args=-DCONFIG_LOG_USE_VLA=n \ + --cmake-args=-DCONFIG_MINIMAL_LIBC=y \ + --pristine 2>&1 | tee _.log - printf '\n\n\t\t\t ---- Messages below are treated as sparse errors --- \n\n\n' - (set -x; ./sof/scripts/parse_sparse_output.sh ${{ matrix.platforms.platform }} <_.log) + printf '\n\n\t\t\t ---- Messages below are treated as sparse errors --- \n\n\n' + (set -x; ./sof/scripts/parse_sparse_output.sh ${{ matrix.platforms.platform }} <_.log) diff --git a/.github/workflows/testbench.yml b/.github/workflows/testbench.yml index c6d16920abd0..290fe8e56dee 100644 --- a/.github/workflows/testbench.yml +++ b/.github/workflows/testbench.yml @@ -28,6 +28,9 @@ on: workflow_dispatch: workflow_call: +permissions: + contents: read + jobs: build-and-test: runs-on: ubuntu-24.04 diff --git a/.github/workflows/tools.yml b/.github/workflows/tools.yml index 9c8197a6cbcc..7c70e93ddb5f 100644 --- a/.github/workflows/tools.yml +++ b/.github/workflows/tools.yml @@ -7,6 +7,9 @@ name: User space tools/ directory # yamllint disable-line rule:truthy on: [pull_request, workflow_dispatch, workflow_call] +permissions: + contents: read + jobs: # This is not the same as building every ./build-tools.sh option. top-level_default_CMake_target_ALL: @@ -32,7 +35,7 @@ jobs: SOF-alsa-plugin: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 with: {filter: 'tree:0'} diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index 391807f6a336..de8577e87171 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -10,6 +10,9 @@ name: Unit tests # yamllint disable-line rule:truthy on: [pull_request, workflow_dispatch, workflow_call] +permissions: + contents: read + jobs: cmocka_utests: runs-on: ubuntu-latest diff --git a/.github/workflows/zephyr-main.yml b/.github/workflows/zephyr-main.yml new file mode 100644 index 000000000000..0b6f0cbe3e28 --- /dev/null +++ b/.github/workflows/zephyr-main.yml @@ -0,0 +1,17 @@ +--- +name: Zephyr Main Branch + +# yamllint disable-line rule:truthy +on: + schedule: + - cron: '0 4,16 * * *' + workflow_dispatch: + +permissions: + contents: read + +jobs: + zephyr-main-builds: + uses: ./.github/workflows/zephyr.yml + with: + zephyr_revision: 'zmain' diff --git a/.github/workflows/zephyr-shell.yml b/.github/workflows/zephyr-shell.yml new file mode 100644 index 000000000000..23f8e6947b22 --- /dev/null +++ b/.github/workflows/zephyr-shell.yml @@ -0,0 +1,64 @@ +--- +# SPDX-License-Identifier: BSD-3-Clause +# Tools that can save round-trips to github and a lot of time: +# +# yamllint -f parsable zephyr-shell.yml +# pip3 install ruamel.yaml.cmd +# yaml merge-expand zephyr-shell.yml exp.yml && diff -w -u zephyr-shell.yml exp.yml +# +# github.com also has a powerful web editor that can be used without +# committing. + +name: Zephyr Shell + +# 'workflow_dispatch' allows running this workflow manually from the +# 'Actions' tab +# yamllint disable-line rule:truthy +on: [pull_request, workflow_dispatch] + +defaults: + run: + shell: bash + +permissions: + contents: read + +jobs: + build: + runs-on: ubuntu-24.04 + container: + image: thesofproject/zephyr-lite:v0.29.0 + + strategy: + fail-fast: false + matrix: + platform: [tgl, mtl, lnl] + + steps: + - name: checkout + uses: actions/checkout@v4 + with: + path: sof + fetch-depth: 0 # fix git describe + filter: 'tree:0' + + - name: west update + working-directory: sof + run: | + west init -l + west update --narrow --fetch-opt=--depth=5 + + - name: print all available sdks in /opt/toolchains/ + run: | + ls -l /opt/toolchains/ + + - name: shell build + run: | + ln -s /opt/toolchains/zephyr-sdk-* ~/ + python sof/scripts/xtensa-build-zephyr.py \ + --cmake-args=-DEXTRA_CFLAGS=-Werror \ + --cmake-args=-DEXTRA_CXXFLAGS=-Werror \ + --cmake-args=-DEXTRA_AFLAGS='-Werror -Wa,--fatal-warnings' \ + --cmake-args=--warn-uninitialized \ + --overlay=sof/app/shell_overlay.conf \ + ${{ matrix.platform }} diff --git a/.github/workflows/zephyr-unit-tests.yml b/.github/workflows/zephyr-unit-tests.yml index 2db2e7d220af..ed089a148597 100644 --- a/.github/workflows/zephyr-unit-tests.yml +++ b/.github/workflows/zephyr-unit-tests.yml @@ -51,7 +51,7 @@ jobs: - name: Build and run unit tests run: | cd workspace - export ZEPHYR_TOOLCHAIN_VARIANT=llvm + export ZEPHYR_TOOLCHAIN_VARIANT=host/llvm west twister --testsuite-root sof/test/ztest/unit/ --platform native_sim --verbose \ --inline-logs # This part is commented out because it is not needed at the moment. diff --git a/.github/workflows/zephyr.yml b/.github/workflows/zephyr.yml index d02d67861560..ecfc6f4089ee 100644 --- a/.github/workflows/zephyr.yml +++ b/.github/workflows/zephyr.yml @@ -5,7 +5,17 @@ name: Zephyr # 'workflow_dispatch' allows running this workflow manually from the # 'Actions' tab # yamllint disable-line rule:truthy -on: [push, pull_request, workflow_dispatch, workflow_call] +on: + push: + pull_request: + workflow_dispatch: + workflow_call: + inputs: + zephyr_revision: + description: 'Zephyr revision to build against' + type: string + required: false + default: 'mnfst' # Specifies group name that stops previous wokrflows if the name matches concurrency: @@ -14,19 +24,26 @@ concurrency: group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.head_ref || github.ref }} cancel-in-progress: true -jobs: +permissions: + contents: read +jobs: manifest-check: runs-on: ubuntu-latest + defaults: + run: + shell: bash + container: + image: thesofproject/zephyr-lite:v0.29.0 steps: - uses: actions/checkout@v4 with: - path: ./workspace/sof + path: sof filter: 'tree:0' - name: plain west update + working-directory: sof run: | - : This plain 'west update' does not provide 100% certainty that : all the manifest revisions make sense but it is quick and : will catch many revision problems. Other jobs typically @@ -35,8 +52,6 @@ jobs: : is useful for testing unmerged Zephyr commits but risks : accepting "invalid" ones, this will not. - pip3 install west - cd workspace/sof/ west init -l west update --fetch-opt=--filter=tree:0 @@ -45,9 +60,8 @@ jobs: # XTOS submodules and... temporarily break every CI, which is why # it hasn't been done yet. - name: git submodules consistency + working-directory: sof run: | - - cd workspace/sof git submodule update --init --recursive west update @@ -65,61 +79,65 @@ jobs: # sof/scripts/xtensa-build-zephyr.py configuration script. Then this # job will be disappear, folded back in the regular build-* jobs below. LP64-WIP: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 + defaults: + run: + shell: bash + container: + image: thesofproject/zephyr-lite:v0.29.0 steps: - uses: actions/checkout@v4 with: - path: ./workspace/sof + path: sof filter: 'tree:0' - - name: free space + - name: west update + working-directory: sof run: | - sudo rm -rf /usr/share/dotnet - sudo rm -rf /opt/ghc - - - name: west clones - run: pip3 install west && cd workspace/sof/ && west init -l && - west update --narrow --fetch-opt=--filter=tree:0 + west init -l + west update --narrow --fetch-opt=--filter=tree:0 - # Not strictly necessary but saves a lot of scrolling in the next step - # Caching a 12G image is unfortunately not possible: - # https://github.com/ScribeMD/docker-cache/issues/304 - # For faster builds we would have to pay for some persistent runners. - - name: Download docker image && ls /opt/toolchains/ - run: cd workspace && ./sof/zephyr/docker-run.sh ls -l /opt/toolchains/ + - name: print all available sdks in /opt/toolchains/ + run: | + ls -l /opt/toolchains/ - name: 64 bits build run: | - cd workspace && ./sof/zephyr/docker-run.sh /bin/sh -c \ - 'ln -s /opt/toolchains/zephyr-sdk-* ~/; - west build --board imx93_evk/mimx9352/a55 sof/app \ - -- -DEXTRA_CFLAGS=-Werror -DEXTRA_CXXFLAGS=-Werror \ - -DEXTRA_AFLAGS=-Werror' - + ln -s /opt/toolchains/zephyr-sdk-* ~/ + west build --board imx93_evk/mimx9352/a55 sof/app \ + -- -DEXTRA_CFLAGS=-Werror -DEXTRA_CXXFLAGS=-Werror \ + -DEXTRA_AFLAGS=-Werror build-linux: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 + defaults: + run: + shell: bash + container: + image: thesofproject/zephyr-lite:v0.29.0 strategy: fail-fast: false matrix: # These keys are kept short because Github's left column is not resizable. # Search "zephyr_revision" and see below what they expand to. zephyr_revision: [ - mnfst, # special value: don't override sof/west.yml - zmain, # Zephyr's main branch + "${{ inputs.zephyr_revision || 'mnfst' }}" ] # Using groups to avoid spamming the small results box with too # many lines. Pay attention to COMMAS. IPC_platforms: [ # - IPC3 default - imx8 imx8x imx8m imx8ulp, + imx8 imx8x, + imx8m imx8ulp, # - IPC4 default, released - mtl lnl, + mtl, + lnl, # active development ptl, # Temporary testbed for Zephyr development. - tgl tgl-h, + tgl, + tgl-h ] build_opts: [""] # Sparse matrices are complicated, you must read this page slowly: @@ -130,10 +148,12 @@ jobs: zephyr_revision: mnfst IPC_platforms: lnl - # This is "duplication of effort" but it makes sure no one - # breaks --all, see for instance #9262 and previous commit. + # Due to GitHub size limitations we can't afford to run --all and + # duplicate builds already built in previous steps. + # This will now build any platform that would've run with --all that + # isn't already built above. - zephyr_revision: mnfst - IPC_platforms: --all + IPC_platforms: wcl imx95 steps: - uses: actions/checkout@v4 @@ -143,20 +163,15 @@ jobs: with: fetch-depth: 0 filter: 'tree:0' - path: ./workspace/sof + path: sof - - name: free space + - name: west update + working-directory: sof run: | - sudo rm -rf /usr/share/dotnet - sudo rm -rf /opt/ghc - - - name: west clones - - run: pip3 install west && cd workspace/sof/ && west init -l && - time west update --narrow --fetch-opt=--filter=tree:0 + west init -l + west update --narrow --fetch-opt=--filter=tree:0 - name: select zephyr revision - working-directory: ${{ github.workspace }}/workspace run: | if [ 'mnfst' = '${{ matrix.zephyr_revision }}' ]; then rem_rev=$(git -C zephyr rev-parse HEAD) @@ -167,7 +182,8 @@ jobs: esac ( cd sof/submanifests/ sed -e "s#=sof_zephyr_revision_override=#${rem_rev}#" \ - sof-ci-jenkins/zephyr-override-template.yml > test-zephyr-main.yml + sof-ci-jenkins/zephyr-override-template.yml \ + > test-zephyr-main.yml ) time west update --narrow --fetch-opt=--filter=tree:0 fi @@ -175,7 +191,7 @@ jobs: # Get some tags to fix `git describe` hence BUILD_VERSION, etc. # Keep in sync with build-windows below - name: Fetch tags for git describe - working-directory: ${{ github.workspace }}/workspace/zephyr + working-directory: zephyr run: | # Because we used git tricks to speed things up, we now have two git # problems: @@ -186,8 +202,7 @@ jobs: # does not use --tags. # # 2. west fetches using the remote URL, not the remote name. So remote - # branches are missing from --decorate below. Cosmetic but annoying; - # especially in the "zmain" case. + # branches are missing from --decorate below. Cosmetic but annoying. set -x # Fix problem 2. Do NOT assume anything about remote names: nothing is guaranteed. @@ -205,57 +220,59 @@ jobs: git describe --long --always --dirty git describe --long --always --dirty --tags - # Not strictly necessary but saves a lot of scrolling in the next step - # Caching a 12G image is unfortunately not possible: - # https://github.com/ScribeMD/docker-cache/issues/304 - # For faster builds we would have to pay for some persistent runners. - - name: Download docker image && ls /opt/toolchains/ - run: cd workspace && ./sof/zephyr/docker-run.sh ls -l /opt/toolchains/ + - name: print all available sdks in /opt/toolchains/ + run: | + ls -l /opt/toolchains/ - # https://github.com/zephyrproject-rtos/docker-image - # Note: env variables can be passed to the container with - # -e https_proxy=... - name: build - run: cd workspace && ./sof/zephyr/docker-run.sh - ./sof/zephyr/docker-build.sh --cmake-args=-DEXTRA_CFLAGS=-Werror - --cmake-args=-DEXTRA_CXXFLAGS=-Werror - --cmake-args=-DEXTRA_AFLAGS='-Werror -Wa,--fatal-warnings' - --cmake-args=--warn-uninitialized - --overlay=sof/app/configs/repro-build.conf - --no-tarball - ${{ matrix.build_opts }} ${{ matrix.IPC_platforms }} + run: | + ./sof/zephyr/docker-build.sh --cmake-args=-DEXTRA_CFLAGS=-Werror \ + --cmake-args=-DEXTRA_CXXFLAGS=-Werror \ + --cmake-args=-DEXTRA_AFLAGS='-Werror -Wa,--fatal-warnings' \ + --cmake-args=--warn-uninitialized \ + --overlay=sof/app/configs/repro-build.conf \ + --no-tarball \ + ${{ matrix.build_opts }} ${{ matrix.IPC_platforms }} - name: Upload build artifacts uses: actions/upload-artifact@v4 - if: ${{ matrix.zephyr_revision == 'mnfst' }} + if: always() with: name: linux-build ${{ matrix.build_opts }} ${{ matrix.IPC_platforms }} if-no-files-found: error path: | - ${{ github.workspace }}/workspace/build-sof-staging - ${{ github.workspace }}/workspace/**/compile_commands.json + build-sof-staging + ./**/compile_commands.json build-windows: runs-on: windows-latest strategy: fail-fast: false matrix: + # Search "zephyr_revision" and see below what they expand to. + zephyr_revision: [ + "${{ inputs.zephyr_revision || 'mnfst' }}" + ] # Using groups to avoid spamming the small results box with too # many lines. Pay attention to COMMAS. platforms: [ # - IPC3 default - imx8 imx8x imx8m imx8ulp, + imx8 imx8x, + imx8m imx8ulp, # - IPC4 default, released - mtl lnl, + mtl, + lnl, # active development ptl, # legacy - tgl tgl-h, + tgl, + tgl-h ] build_opts: [""] # Sparse matrices are complicated, see comments on Linux matrix above. include: - build_opts: -d + zephyr_revision: mnfst platforms: lnl @@ -285,12 +302,12 @@ jobs: # Keep this SDK version identical to the one in # sof/zephyr/docker-run.sh - - name: Cache Zephyr SDK 0.17.0 + - name: Cache Zephyr SDK 1.0.0 id: cache-zephyr-sdk uses: actions/cache@v4 with: - path: zephyr-sdk-0.17.0_windows-x86_64.7z - key: ${{ runner.os }}-cache-zephyr-sdk-0-17-0 + path: zephyr-sdk-1.0.0_windows-x86_64_gnu.7z + key: ${{ runner.os }}-cache-zephyr-sdk-1-0-0 # Wget is needed by Zephyr SDK setup.cmd installation script - name: Download wget @@ -298,11 +315,11 @@ jobs: run: | curl -L -O http://downloads.sourceforge.net/gnuwin32/wget-1.11.4-1-bin.zip - - name: Download Zephyr SDK 0.17.0 + - name: Download Zephyr SDK 1.0.0 if: ${{ steps.cache-zephyr-sdk.outputs.cache-hit != 'true' }} run: | # yamllint disable-line rule:line-length curl -L -O ` - https://github.com/zephyrproject-rtos/sdk-ng/releases/download/v0.17.0/zephyr-sdk-0.17.0_windows-x86_64.7z + https://github.com/zephyrproject-rtos/sdk-ng/releases/download/v1.0.0/zephyr-sdk-1.0.0_windows-x86_64_gnu.7z # Unzips every .zip package to directory matching its name without extension - name: Unzip downloaded packages @@ -325,12 +342,12 @@ jobs: # setup.cmd may not be called in from msys shell as it does not parse # forward slash script input arguments correctly. - name: Install Zephyr SDK - run: zephyr-sdk-0.17.0_windows-x86_64/zephyr-sdk-0.17.0/setup.cmd /t all /h /c + run: zephyr-sdk-1.0.0_windows-x86_64_gnu/zephyr-sdk-1.0.0/setup.cmd /t all /h /c - name: Setup Python uses: actions/setup-python@v5 with: - python-version: '3.10' + python-version: '3.12' - name: West install run: pip3 install west @@ -342,6 +359,24 @@ jobs: west init -l sof west update --narrow --fetch-opt=--filter=tree:0 + - name: select zephyr revision + working-directory: ${{ github.workspace }}/workspace + run: | + if ('mnfst' -eq '${{ matrix.zephyr_revision }}') { + $rem_rev = $(git -C zephyr rev-parse HEAD) + } else { + switch ('${{ matrix.zephyr_revision }}') { + 'zmain' { $rem_rev = 'main' } + default { Write-Error 'Unknown matrix.zephyr_revision'; exit 1 } + } + Push-Location sof/submanifests/ + (Get-Content sof-ci-jenkins/zephyr-override-template.yml) ` + -replace '=sof_zephyr_revision_override=', "$rem_rev" ` + | Set-Content test-zephyr-main.yml + Pop-Location + west update --narrow --fetch-opt=--filter=tree:0 + } + # Get some tags to fix `git describe` etc., see detailed build-linux comments above. - name: Fetch tags for git describe working-directory: ${{ github.workspace }}/workspace/zephyr @@ -367,7 +402,7 @@ jobs: uses: actions/setup-python@v5 id: cache-python with: - python-version: '3.10' + python-version: '3.12' cache: 'pip' cache-dependency-path: workspace/zephyr/scripts/requirements.txt @@ -382,17 +417,17 @@ jobs: choco install ninja ninja.exe --version - # MSYS2 provides gcc x64_86 toolchain & openssl + # MSYS2 provides gcc x86_64 toolchain, openssl & gperf # Installs in D:/a/_temp/msys64 # # Note there is already C:/msys64/ provided by # https://github.com/actions/runner-images/blob/win22/20230918.1/images/win/Windows2022-Readme.md # Is it not good enough? Maybe it could save 20-30s. - name: Initialize MSYS2 - uses: msys2/setup-msys2@v2 + uses: msys2/setup-msys2@cafece8e6baf9247cf9b1bf95097b0b983cc558d # v2 with: msystem: MSYS - install: gcc openssl-devel + install: gcc openssl-devel gperf path-type: inherit - name: Build @@ -410,6 +445,7 @@ jobs: - name: Upload build artifacts uses: actions/upload-artifact@v4 + if: always() with: name: windows-build ${{ matrix.build_opts }} ${{ matrix.platforms }} if-no-files-found: error @@ -462,7 +498,8 @@ jobs: for regdir in 'linux-build *-d *.*' \ 'linux-build *.*lnl.*' \ 'windows-build *.*mtl.*' \ - 'windows-build *tgl tgl-h'; do + 'windows-build *tgl-h' \ + 'windows-build *tgl'; do find . -maxdepth 1 | grep -q "\./${regdir}\$" || { >&2 printf 'Missing %s\n' "${regdir}"; exit 1; } done diff --git a/.gitignore b/.gitignore index 9d50048ab954..043112c6ff78 100644 --- a/.gitignore +++ b/.gitignore @@ -49,7 +49,6 @@ tools/test/audio/reports/* tools/test/audio/zeros_in.raw __pycache__ -.checkpatch-camelcase.git.* build*/ diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 000000000000..462c39f0d343 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,22 @@ +# Sound Open Firmware (SOF) Agent Workflows and Rules + +This document outlines the rules AI agents must follow when checking out branches, writing code, documenting features, and running the development environment. Follow these rules carefully: + +## Development Standards + +### Commitment and Sign-off Rules +* **Commit Subject:** Must follow the format `subsystem: component: short description` matching the SOF project convention. Use the git history of the modified file(s) (`git log --oneline -10 -- `) to determine the correct subsystem and component prefix. Examples: + * `audio: chain_dma: add user-space scheduling support` + * `ipc4: handler: fix large config dispatch` + * `schedule: zephyr_domain: add user-space LL thread` +* **Commit Body:** Should describe the changes in detail in the commit message body. +* **Sign-off:** All commits must be signed off by the developer (`Signed-off-by: Name `) using the identity from the local git config. + +### Documentation Requirements +* **Doxygen Comments:** Any new C code or features must include Doxygen comments. +* **Documentation Builds:** Integration of new code must not introduce any new Doxygen errors or warnings. Code additions should be verified against a clean documentation build. +* **Architectural Consistency:** When adding or updating a file, any `architecture.md` or `README.md` in the same directory must be reviewed. The agent is responsible for ensuring documentation stays in sync with code logic changes. + +### Codestyle and Linting +* **Standard:** Use `clangd` instead of `checkpatch` for codestyle verification. +* **Rationale:** `checkpatch` is prone to confusion with assembly and non-standard C; `clangd` provides better integration with IDEs and AI tools and is easier to maintain. diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md new file mode 100644 index 000000000000..e236a2b659e6 --- /dev/null +++ b/ARCHITECTURE.md @@ -0,0 +1,46 @@ +# Sound Open Firmware (SOF) Architecture + +This document provides a high-level overview of the Sound Open Firmware (SOF) architecture. SOF provides an open-source audio DSP firmware and an SDK for audio development. + +## 1. High-Level Overview + +At a macro level, the SOF stack consists of: +* **Host Driver (OS):** The OS-level driver (Linux ALSA/ASoC, Windows, etc.) that acts as the frontend, managing audio devices, streams, and IPC communication. +* **Firmware (DSP):** The embedded firmware running on the Audio DSP (typically Xtensa/Tensilica or other DSPs). +* **Topology:** A configuration file loaded by the host driver that defines the hardware constraints, routing, and instantiated audio processing graphs (pipelines). + +## 2. Firmware Architecture + +The SOF firmware is built with a modular, layered architecture to support multiple platforms, DSP architectures, and operating systems. + +### 2.1 OS Abstraction and RTOS +* **Zephyr RTOS:** Modern SOF extensively leverages the Zephyr RTOS for core operating system services, including thread scheduling, synchronization, logging, and hardware abstraction. +* **XTOS / Bare-metal (Legacy):** Older platforms or specific stripped-down environments might use XTOS or bare-metal abstractions. + +### 2.2 Inter-Process Communication (IPC) +The IPC subsystem is responsible for all communication between the Host OS and the DSP Firmware. +* **Message Passing:** Uses hardware mailboxes, shared memory, and interrupts to send messages. +* **Commands:** Includes stream setup, pipeline instantiation, run/pause/stop commands, volume changes, and parameter updates. +* **Versions:** IPC relies on a versioned message protocol (e.g., IPC3, IPC4) allowing the host and firmware to understand each other's commands. + +### 2.3 Audio Processing Pipelines +At the core of the SOF firmware is the DSP pipeline. A pipeline is a directed graph consisting of connected audio modules (components) that process streams of data. +* **Endpoints:** Pipelines connect external interfaces (like I2S/HDA/DMIC DAIs - Digital Audio Interfaces) to host DMA buffers. +* **Scheduling:** Each pipeline is scheduled to run periodically based on its timing requirements and deadlines. SOF provides an LL (Low Latency) scheduler and an EDF (Earliest Deadline First) scheduler. + +### 2.4 Audio Components (Modules) +The firmware processing graph is assembled using individual audio modules, which are dynamically instantiated based on the topology file: +* **Host / DAI Components:** Read/write audio frames from/to DMA buffers or hardware interfaces. +* **Copier:** Moves data between buffers or other components without modification. +* **Volume / Mixer:** Changes signal amplitude or mixes multiple streams. +* **SRC (Sample Rate Converter):** Resamples audio streams. +* **Effect Modules:** EQs (Equalizers), DRCs (Dynamic Range Compressors), smart amplifiers, arrays, and other algorithmic components. + +### 2.5 Memory Management +The DSP handles multiple memory domains depending on the hardware (SRAM, L1 Cache, L2, external memory). +* **Heaps:** Different memory heaps exist for dynamic allocation (e.g., system heap, buffer heap, runtime heap) to ensure real-time constraints and avoid fragmentation. +* **Data Buffers:** Audio buffers are carefully allocated in appropriate memory to minimize power consumption and pipeline latency. + +## 3. Topologies + +The host driver parses a topology file (`.tplg`) which describes the expected processing graphs. Instead of hardcoding audio routing in the firmware, SOF uses these topology constraints. The firmware receives the component creation sequences, parameter configurations, and connections over IPC to construct pipelines at runtime. diff --git a/CODEOWNERS b/CODEOWNERS index ec5ab51af9b6..9cd3ec9bcb48 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -12,8 +12,8 @@ # include files src/include/sof/drivers/dmic.h @singalsu src/include/ipc/** @thesofproject/steering-committee -src/include/ipc/** @randerwang @marcinszkudlinski @pblaszko -src/include/ipc4/** @randerwang @marcinszkudlinski @pblaszko +src/include/ipc/** @randerwang @tmleman @pblaszko +src/include/ipc4/** @randerwang @tmleman @pblaszko src/include/kernel/** @thesofproject/steering-committee src/include/user/** @thesofproject/steering-committee src/include/sof/debug/gdb/* @abonislawski @@ -62,7 +62,7 @@ src/drivers/mediatek/mt8195/* @yaochunhung @kuanhsuncheng # other libs src/math/* @singalsu -src/ipc/* @bardliao @marcinszkudlinski @pblaszko +src/ipc/* @bardliao @tmleman @pblaszko # src/lib/* TODO src/debug/gdb/* @abonislawski src/schedule @pblaszko @marcinszkudlinski @dbaluta @LaurentiuM1234 diff --git a/Kconfig.sof b/Kconfig.sof index 60a00aac8e82..29c6f549fca8 100644 --- a/Kconfig.sof +++ b/Kconfig.sof @@ -47,6 +47,14 @@ config FAST_GET counting. Source is src/lib/fast-get.c. The option should be selected on platforms, where __cold_rodata is supported. +config SOF_OS_LINUX_COMPAT_PRIORITY + bool "Prioritize backwards compatibility for old Linux kernels" + help + Build option to maintain maximal backwards compatibility with old + versions of Linux SOF driver. When this is not set, firmware may + require newer version of host, and/or use features that are not + available in stable Linux kernel trees. + rsource "Kconfig.xtos" rsource "src/Kconfig" diff --git a/README.md b/README.md index 1cf8da093bfa..e1fa62ce9b09 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,7 @@ ### Status [![Daily Actions](https://github.com/thesofproject/sof/actions/workflows/daily-tests.yml/badge.svg)](https://github.com/thesofproject/sof/actions/workflows/daily-tests.yml) +[![Zephyr Main Branch](https://github.com/thesofproject/sof/actions/workflows/zephyr-main.yml/badge.svg)](https://github.com/thesofproject/sof/actions/workflows/zephyr-main.yml) ### Community @@ -13,6 +14,17 @@ Additional community support is available via the `#sof` channel in the Zephyr P See [docs](https://thesofproject.github.io/latest/index.html) +## Quickstart + +You can easily set up the complete SOF development environment, including Zephyr SDK and QEMU, by running our interactive installer script. To run the installer locally: + +```bash +curl -fsSLo sdk-install.sh https://raw.githubusercontent.com/thesofproject/vscode-workspace/main/sdk-install.sh +bash sdk-install.sh +``` + +The script will guide you through the process of installing system dependencies, cloning the repositories, configuring Python virtual environments, and setting up the Zephyr SDK and QEMU. + ## Running the tests See [unit testing documentation](https://thesofproject.github.io/latest/developer_guides/unit_tests.html) diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index da9db799d387..832d4eb12098 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -10,6 +10,6 @@ target_sources(app PRIVATE ) zephyr_library_include_directories(app PUBLIC - ${sof_module}/src/arch/xtensa/include - ${sof_module}/src/include + ${CMAKE_CURRENT_SOURCE_DIR}/../src/arch/xtensa/include + ${CMAKE_CURRENT_SOURCE_DIR}/../src/include ) diff --git a/app/Kconfig b/app/Kconfig index e1f5bd67295d..56ada456367e 100644 --- a/app/Kconfig +++ b/app/Kconfig @@ -4,6 +4,11 @@ config SCHED_CPU_MASK_PIN_ONLY config SMP_BOOT_DELAY default y if SMP +# Allow compiler to determine whether to generate FLIX instructions. +choice COMPILER_CODEGEN_VLIW + default COMPILER_CODEGEN_VLIW_AUTO if "$(ZEPHYR_TOOLCHAIN_VARIANT)" = "xt-clang" +endchoice + source "Kconfig.zephyr" if SOC_FAMILY_INTEL_ADSP diff --git a/app/boards/acp_6_0_adsp.conf b/app/boards/acp_6_0_adsp.conf index 2e4332e31dcd..b527ea3753a7 100644 --- a/app/boards/acp_6_0_adsp.conf +++ b/app/boards/acp_6_0_adsp.conf @@ -8,3 +8,33 @@ CONFIG_ZEPHYR_LOG=n CONFIG_DMA=y CONFIG_ZEPHYR_NATIVE_DRIVERS=n CONFIG_AMS=n +CONFIG_COMP_SRC=n +CONFIG_COMP_FIR=n +CONFIG_COMP_IIR=n +CONFIG_COMP_DCBLOCK=n +CONFIG_COMP_CROSSOVER=n +CONFIG_COMP_DRC=n +CONFIG_COMP_MULTIBAND_DRC=n +CONFIG_COMP_TONE=n +CONFIG_COMP_KPB=n +CONFIG_MAXIM_DSM=n +CONFIG_COMP_ASRC=n +CONFIG_COMP_IGO_NR=n +CONFIG_COMP_COPIER=n +CONFIG_COMP_RTNR=n +CONFIG_COMP_ARIA=n +CONFIG_COMP_BASEFW_IPC4=n +CONFIG_COMP_UP_DOWN_MIXER=n +CONFIG_COMP_TDFB=n +CONFIG_COMP_SEL=n +CONFIG_COMP_MIXER=n +CONFIG_ASRC_SUPPORT_CONVERSION_24000_TO_08000=n +CONFIG_ASRC_SUPPORT_CONVERSION_24000_TO_16000=n +CONFIG_ASRC_SUPPORT_CONVERSION_48000_TO_08000=n +CONFIG_ASRC_SUPPORT_CONVERSION_48000_TO_11025=n +CONFIG_ASRC_SUPPORT_CONVERSION_48000_TO_12000=n +CONFIG_ASRC_SUPPORT_CONVERSION_48000_TO_16000=n +CONFIG_ASRC_SUPPORT_CONVERSION_48000_TO_22050=n +CONFIG_ASRC_SUPPORT_CONVERSION_48000_TO_24000=n +CONFIG_ASRC_SUPPORT_CONVERSION_48000_TO_32000=n +CONFIG_ASRC_SUPPORT_CONVERSION_48000_TO_44100=n diff --git a/app/boards/acp_7_0_adsp.conf b/app/boards/acp_7_0_adsp.conf new file mode 100644 index 000000000000..3c5f791fdcb6 --- /dev/null +++ b/app/boards/acp_7_0_adsp.conf @@ -0,0 +1,52 @@ +CONFIG_ACP_7_0=y +CONFIG_HAVE_AGENT=n +CONFIG_DCACHE_LINE_SIZE_DETECT=n +CONFIG_DCACHE_LINE_SIZE=128 +CONFIG_DYNAMIC_INTERRUPTS=y +CONFIG_SHARED_INTERRUPTS=n +CONFIG_ZEPHYR_LOG=y +CONFIG_LOG_MODE_DEFERRED=n +CONFIG_LOG_MODE_MINIMAL=y +CONFIG_DMA=y +CONFIG_DMA_DOMAIN=n +CONFIG_ZEPHYR_NATIVE_DRIVERS=y +CONFIG_INTC_AMD_ACP=y +CONFIG_DMA_AMD_ACP_HOST=y +CONFIG_DMA_AMD_ACP_SDW=y +CONFIG_DAI_AMD_SDW=y +CONFIG_AMS=n +CONFIG_WRAP_ACTUAL_POSITION=y +CONFIG_TRACE=n +CONFIG_COMP_VOLUME=y +CONFIG_COMP_SRC=n +CONFIG_COMP_FIR=n +CONFIG_COMP_IIR=n +CONFIG_COMP_DCBLOCK=n +CONFIG_COMP_CROSSOVER=n +CONFIG_COMP_DRC=n +CONFIG_COMP_MULTIBAND_DRC=n +CONFIG_COMP_TONE=n +CONFIG_COMP_KPB=n +CONFIG_MAXIM_DSM=n +CONFIG_COMP_ASRC=n +CONFIG_COMP_IGO_NR=n +CONFIG_COMP_COPIER=n +CONFIG_COMP_RTNR=n +CONFIG_COMP_ARIA=n +CONFIG_COMP_BASEFW_IPC4=n +CONFIG_COMP_UP_DOWN_MIXER=n +CONFIG_COMP_TDFB=n +CONFIG_COMP_SEL=n +CONFIG_COMP_MIXER=n +CONFIG_ASRC_SUPPORT_CONVERSION_24000_TO_08000=n +CONFIG_ASRC_SUPPORT_CONVERSION_24000_TO_16000=n +CONFIG_ASRC_SUPPORT_CONVERSION_48000_TO_08000=n +CONFIG_ASRC_SUPPORT_CONVERSION_48000_TO_11025=n +CONFIG_ASRC_SUPPORT_CONVERSION_48000_TO_12000=n +CONFIG_ASRC_SUPPORT_CONVERSION_48000_TO_16000=n +CONFIG_ASRC_SUPPORT_CONVERSION_48000_TO_22050=n +CONFIG_ASRC_SUPPORT_CONVERSION_48000_TO_24000=n +CONFIG_ASRC_SUPPORT_CONVERSION_48000_TO_32000=n +CONFIG_ASRC_SUPPORT_CONVERSION_48000_TO_44100=n +CONFIG_CORE_COUNT=1 +CONFIG_FORMAT_CONVERT_HIFI3=n diff --git a/app/boards/imx8mp_evk_mimx8ml8_m7_ddr.conf b/app/boards/imx8mp_evk_mimx8ml8_m7_ddr.conf new file mode 100644 index 000000000000..290f27a2b933 --- /dev/null +++ b/app/boards/imx8mp_evk_mimx8ml8_m7_ddr.conf @@ -0,0 +1,15 @@ +CONFIG_IMX8M_CM7=y +CONFIG_ZEPHYR_NATIVE_DRIVERS=y +CONFIG_CACHE_MANAGEMENT=y +CONFIG_NOCACHE_MEMORY=y +CONFIG_DYNAMIC_INTERRUPTS=y +CONFIG_SHARED_INTERRUPTS=y +CONFIG_CLOCK_CONTROL_FIXED_RATE_CLOCK=y +CONFIG_ROMSTART_RELOCATION_ROM=y +CONFIG_DMA_NXP_SOF_HOST_DMA_ALIGN=32 +CONFIG_DMA=y +CONFIG_DMA_NXP_SDMA=y +CONFIG_DAI_NXP_SAI=y +CONFIG_SAI_HAS_MCLK_CONFIG_OPTION=y +CONFIG_COMP_ASRC=n + diff --git a/app/boards/imx8mp_evk_mimx8ml8_m7_ddr.overlay b/app/boards/imx8mp_evk_mimx8ml8_m7_ddr.overlay new file mode 100644 index 000000000000..3f712e3ad00c --- /dev/null +++ b/app/boards/imx8mp_evk_mimx8ml8_m7_ddr.overlay @@ -0,0 +1,27 @@ +/* + * Copyright 2026 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/ { + host_dma: dma { + compatible = "nxp,sof-host-dma"; + dma-channels = <32>; + #dma-cells = <0>; + }; +}; + +&sdma3 { + status = "okay"; +}; + +&sai3 { + pinctrl-0 = <&sai3_default>; + pinctrl-names = "default"; + rx-fifo-watermark = <64>; + tx-fifo-watermark = <64>; + fifo-depth = <96>; + rx-sync-mode = <1>; + status = "okay"; +}; diff --git a/app/boards/imx95_evk_mimx9596_m7_ddr.overlay b/app/boards/imx95_evk_mimx9596_m7_ddr.overlay index 730ff6aa91ea..f1d2cce00781 100644 --- a/app/boards/imx95_evk_mimx9596_m7_ddr.overlay +++ b/app/boards/imx95_evk_mimx9596_m7_ddr.overlay @@ -5,6 +5,11 @@ */ / { + /* this'll select Cortex-M SysTick as the system timer */ + chosen { + /delete-property/ zephyr,system-timer; + }; + host_dma: dma { compatible = "nxp,sof-host-dma"; dma-channels = <32>; diff --git a/app/boards/intel_adsp/Kconfig.defconfig b/app/boards/intel_adsp/Kconfig.defconfig index a2ba2db15db6..f705960eee1a 100644 --- a/app/boards/intel_adsp/Kconfig.defconfig +++ b/app/boards/intel_adsp/Kconfig.defconfig @@ -123,6 +123,9 @@ config INTEL_ADSP_TIMER config MM_DRV default y +config UAOL + default y if ACE + # # Zephyr / power settings # ---------------------------------------- @@ -162,5 +165,21 @@ config LOG_FUNC_NAME_PREFIX_INF config LOG_FUNC_NAME_PREFIX_DBG default y +config LOG_CORE_ID_PREFIX + default y + config LOG_TIMESTAMP_64BIT default y + +# Zephyr / debugging +# ---------------------------------------- + +config ZTEST + default SOF_BOOT_TEST_SUPPORTED && SOF_BOOT_TEST_ALLOWED + +# Zephyr / debug slot manager +# ---------------------------------------- + +config INTEL_ADSP_DEBUG_SLOT_MANAGER + default y + diff --git a/app/boards/intel_adsp_ace15_mtpm.conf b/app/boards/intel_adsp_ace15_mtpm.conf index 4ebf4498375d..4f124eee5285 100644 --- a/app/boards/intel_adsp_ace15_mtpm.conf +++ b/app/boards/intel_adsp_ace15_mtpm.conf @@ -15,6 +15,7 @@ CONFIG_COMP_MFCC=y CONFIG_COMP_MULTIBAND_DRC=y CONFIG_FORMAT_CONVERT_HIFI3=n CONFIG_SAMPLE_KEYPHRASE=y +CONFIG_COMP_STFT_PROCESS=y # SOF / audio modules / mocks # This mock is part of official sof-bin releases because the CI that @@ -34,6 +35,7 @@ CONFIG_SOF_TELEMETRY_IO_PERFORMANCE_MEASUREMENTS=y CONFIG_SOF_TELEMETRY_PERFORMANCE_MEASUREMENTS=y CONFIG_ZEPHYR_TWB_SCHEDULER=y CONFIG_COLD_STORE_EXECUTE_DRAM=y +CONFIG_SOF_BOOT_TEST_SUPPORTED=n # SOF / loadable modules CONFIG_INTEL_MODULES=y @@ -69,7 +71,7 @@ CONFIG_DMA_DW_LLI_POOL_SIZE=50 CONFIG_DMA_INTEL_ADSP_GPDMA=y CONFIG_MEMORY_WIN_2_SIZE=12288 CONFIG_MM_DRV_INTEL_ADSP_TLB_REMAP_UNUSED_RAM=y -CONFIG_MM_DRV_INTEL_VIRTUAL_REGION_COUNT=2 +CONFIG_MM_DRV_INTEL_VIRTUAL_REGION_COUNT=3 CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC=38400000 CONFIG_SYS_CLOCK_TICKS_PER_SEC=12000 @@ -77,10 +79,10 @@ CONFIG_SYS_CLOCK_TICKS_PER_SEC=12000 CONFIG_ADSP_IDLE_CLOCK_GATING=y CONFIG_ADSP_IMR_CONTEXT_SAVE=n CONFIG_PM_PREWAKEUP_CONV_MODE_CEIL=y +CONFIG_PM_DEVICE_RUNTIME_ASYNC=n # Zephyr / logging CONFIG_LOG_BACKEND_ADSP=n CONFIG_LOG_BACKEND_SOF_PROBE=n -CONFIG_LOG_OUTPUT_FORMAT_LINUX_TIMESTAMP=y CONFIG_WINSTREAM_CONSOLE=n CONFIG_LOG_FLUSH_SLEEP_US=5000 diff --git a/app/boards/intel_adsp_ace20_lnl.conf b/app/boards/intel_adsp_ace20_lnl.conf index bc716dbf293d..2aef09fb9a4a 100644 --- a/app/boards/intel_adsp_ace20_lnl.conf +++ b/app/boards/intel_adsp_ace20_lnl.conf @@ -11,6 +11,7 @@ CONFIG_COMP_TESTER=m CONFIG_COMP_SRC_IPC4_FULL_MATRIX=y CONFIG_FORMAT_CONVERT_HIFI3=n CONFIG_SAMPLE_KEYPHRASE=y +CONFIG_COMP_STFT_PROCESS=y # SOF / infrastructure CONFIG_AMS=y @@ -48,7 +49,7 @@ CONFIG_DAI_INIT_PRIORITY=70 CONFIG_DMA_INTEL_ADSP_GPDMA=n CONFIG_MEMORY_WIN_2_SIZE=12288 CONFIG_MM_DRV_INTEL_ADSP_TLB_REMAP_UNUSED_RAM=y -CONFIG_MM_DRV_INTEL_VIRTUAL_REGION_COUNT=2 +CONFIG_MM_DRV_INTEL_VIRTUAL_REGION_COUNT=3 CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC=38400000 CONFIG_SYS_CLOCK_TICKS_PER_SEC=12000 @@ -56,9 +57,9 @@ CONFIG_SYS_CLOCK_TICKS_PER_SEC=12000 CONFIG_ADSP_IDLE_CLOCK_GATING=y CONFIG_ADSP_IMR_CONTEXT_SAVE=y CONFIG_PM_PREWAKEUP_CONV_MODE_CEIL=y +CONFIG_PM_DEVICE_RUNTIME_ASYNC=n # Zephyr / logging CONFIG_LOG_BACKEND_ADSP=n -CONFIG_LOG_OUTPUT_FORMAT_LINUX_TIMESTAMP=y CONFIG_WINSTREAM_CONSOLE=n CONFIG_LOG_FLUSH_SLEEP_US=5000 diff --git a/app/boards/intel_adsp_ace30_ptl.conf b/app/boards/intel_adsp_ace30_ptl.conf index c8305fa33b35..a79ca6e67764 100644 --- a/app/boards/intel_adsp_ace30_ptl.conf +++ b/app/boards/intel_adsp_ace30_ptl.conf @@ -14,6 +14,7 @@ CONFIG_FORMAT_CONVERT_HIFI3=n # tests it can't use extra CONFIGs. See #9410, #8722 and #9386 CONFIG_COMP_GOOGLE_RTC_AUDIO_PROCESSING=m CONFIG_GOOGLE_RTC_AUDIO_PROCESSING_MOCK=y +CONFIG_COMP_STFT_PROCESS=y # SOF / infrastructure CONFIG_KCPS_DYNAMIC_CLOCK_CONTROL=n @@ -52,15 +53,25 @@ CONFIG_DMA_INTEL_ADSP_GPDMA=n CONFIG_DMA_DW_LLI_POOL_SIZE=50 CONFIG_MEMORY_WIN_2_SIZE=12288 CONFIG_MM_DRV_INTEL_ADSP_TLB_REMAP_UNUSED_RAM=y -CONFIG_MM_DRV_INTEL_VIRTUAL_REGION_COUNT=2 +CONFIG_MM_DRV_INTEL_VIRTUAL_REGION_COUNT=3 +CONFIG_XTENSA_MMU_NUM_L2_TABLES=128 CONFIG_SYS_CLOCK_TICKS_PER_SEC=12000 # Zephyr / power settings CONFIG_ADSP_IMR_CONTEXT_SAVE=y CONFIG_PM_PREWAKEUP_CONV_MODE_CEIL=y +CONFIG_PM_DEVICE_RUNTIME_ASYNC=n # Zephyr / logging CONFIG_LOG_BACKEND_ADSP=n CONFIG_LOG_FLUSH_SLEEP_US=5000 -CONFIG_LOG_OUTPUT_FORMAT_LINUX_TIMESTAMP=y CONFIG_WINSTREAM_CONSOLE=n + +# Userspace options +CONFIG_USERSPACE=y +CONFIG_DYNAMIC_THREAD=y +CONFIG_DYNAMIC_THREAD_ALLOC=y +CONFIG_DYNAMIC_THREAD_PREFER_ALLOC=y +CONFIG_SOF_STACK_SIZE=8192 +CONFIG_SOF_USERSPACE_PROXY=y +CONFIG_MAX_THREAD_BYTES=3 diff --git a/app/boards/intel_adsp_ace30_wcl.conf b/app/boards/intel_adsp_ace30_wcl.conf index faa20dd97a9b..999a3c309d41 100644 --- a/app/boards/intel_adsp_ace30_wcl.conf +++ b/app/boards/intel_adsp_ace30_wcl.conf @@ -9,10 +9,20 @@ CONFIG_COMP_TESTER=m CONFIG_COMP_SRC_IPC4_FULL_MATRIX=y CONFIG_FORMAT_CONVERT_HIFI3=n +# SOF / audio modules / mocks +# This mock is part of official sof-bin releases because the CI that +# tests it can't use extra CONFIGs. See #9410, #8722 and #9386 +CONFIG_COMP_GOOGLE_RTC_AUDIO_PROCESSING=m +CONFIG_GOOGLE_RTC_AUDIO_PROCESSING_MOCK=y +CONFIG_COMP_STFT_PROCESS=y + # SOF / infrastructure CONFIG_KCPS_DYNAMIC_CLOCK_CONTROL=n CONFIG_PROBE=y CONFIG_PROBE_DMA_MAX=2 +CONFIG_SOF_TELEMETRY=y +CONFIG_SOF_TELEMETRY_IO_PERFORMANCE_MEASUREMENTS=y +CONFIG_SOF_TELEMETRY_PERFORMANCE_MEASUREMENTS=y CONFIG_COLD_STORE_EXECUTE_DRAM=y # SOF / loadable modules @@ -38,16 +48,17 @@ CONFIG_DAI_DMIC_HW_IOCLK=38400000 CONFIG_DAI_DMIC_HAS_OWNERSHIP=n CONFIG_DAI_DMIC_HAS_MULTIPLE_LINE_SYNC=y CONFIG_DMA_INTEL_ADSP_GPDMA=n +CONFIG_MEMORY_WIN_2_SIZE=12288 CONFIG_MM_DRV_INTEL_ADSP_TLB_REMAP_UNUSED_RAM=y -CONFIG_MM_DRV_INTEL_VIRTUAL_REGION_COUNT=2 +CONFIG_MM_DRV_INTEL_VIRTUAL_REGION_COUNT=3 CONFIG_SYS_CLOCK_TICKS_PER_SEC=12000 # Zephyr / power settings CONFIG_ADSP_IMR_CONTEXT_SAVE=y CONFIG_PM_PREWAKEUP_CONV_MODE_CEIL=y +CONFIG_PM_DEVICE_RUNTIME_ASYNC=n # Zephyr / logging CONFIG_LOG_BACKEND_ADSP=n CONFIG_LOG_FLUSH_SLEEP_US=5000 -CONFIG_LOG_OUTPUT_FORMAT_LINUX_TIMESTAMP=y CONFIG_WINSTREAM_CONSOLE=n diff --git a/app/boards/intel_adsp_ace40_nvl.conf b/app/boards/intel_adsp_ace40_nvl.conf index 55a6628959e1..becef1a65f10 100644 --- a/app/boards/intel_adsp_ace40_nvl.conf +++ b/app/boards/intel_adsp_ace40_nvl.conf @@ -15,10 +15,14 @@ CONFIG_FORMAT_CONVERT_HIFI3=n # SOF / infrastructure CONFIG_PROBE=y CONFIG_PROBE_DMA_MAX=2 +CONFIG_COLD_STORE_EXECUTE_DRAM=y # SOF / loadable modules CONFIG_INTEL_MODULES=y CONFIG_LIBRARY_MANAGER=y +CONFIG_LIBRARY_BASE_ADDRESS=0xa0688000 +CONFIG_LIBRARY_BUILD_LIB=y +CONFIG_LIBRARY_DEFAULT_MODULAR=y # SOF / logging CONFIG_TRACE=n @@ -26,6 +30,10 @@ CONFIG_SOF_LOG_LEVEL_INF=y # Zephyr / OS features CONFIG_HEAP_MEM_POOL_SIZE=8192 +CONFIG_LLEXT=y +CONFIG_LLEXT_STORAGE_WRITABLE=y +CONFIG_LLEXT_EXPERIMENTAL=y +CONFIG_MODULES=y # Zephyr / device drivers CONFIG_DAI_INIT_PRIORITY=70 @@ -34,6 +42,7 @@ CONFIG_DAI_DMIC_HAS_OWNERSHIP=n CONFIG_DAI_DMIC_HAS_MULTIPLE_LINE_SYNC=y CONFIG_DMA_INTEL_ADSP_GPDMA=n CONFIG_MM_DRV_INTEL_ADSP_TLB_REMAP_UNUSED_RAM=y +CONFIG_MM_DRV_INTEL_VIRTUAL_REGION_COUNT=3 CONFIG_SYS_CLOCK_TICKS_PER_SEC=12000 # Zephyr / power settings @@ -41,7 +50,13 @@ CONFIG_ADSP_IMR_CONTEXT_SAVE=y CONFIG_PM_POLICY_CUSTOM=y CONFIG_PM_PREWAKEUP_CONV_MODE_CEIL=y CONFIG_SRAM_RETENTION_MODE=n +CONFIG_PM_DEVICE_RUNTIME_ASYNC=n # Zephyr / logging CONFIG_LOG_BACKEND_ADSP=n CONFIG_WINSTREAM_CONSOLE=n +CONFIG_LOG_RUNTIME_FILTERING=y +CONFIG_LOG_RUNTIME_DEFAULT_LEVEL=1 + +# Zephyr / debug: temporary, until fixed +CONFIG_GDBSTUB=n diff --git a/app/boards/intel_adsp_ace40_nvls.conf b/app/boards/intel_adsp_ace40_nvls.conf index 55a6628959e1..becef1a65f10 100644 --- a/app/boards/intel_adsp_ace40_nvls.conf +++ b/app/boards/intel_adsp_ace40_nvls.conf @@ -15,10 +15,14 @@ CONFIG_FORMAT_CONVERT_HIFI3=n # SOF / infrastructure CONFIG_PROBE=y CONFIG_PROBE_DMA_MAX=2 +CONFIG_COLD_STORE_EXECUTE_DRAM=y # SOF / loadable modules CONFIG_INTEL_MODULES=y CONFIG_LIBRARY_MANAGER=y +CONFIG_LIBRARY_BASE_ADDRESS=0xa0688000 +CONFIG_LIBRARY_BUILD_LIB=y +CONFIG_LIBRARY_DEFAULT_MODULAR=y # SOF / logging CONFIG_TRACE=n @@ -26,6 +30,10 @@ CONFIG_SOF_LOG_LEVEL_INF=y # Zephyr / OS features CONFIG_HEAP_MEM_POOL_SIZE=8192 +CONFIG_LLEXT=y +CONFIG_LLEXT_STORAGE_WRITABLE=y +CONFIG_LLEXT_EXPERIMENTAL=y +CONFIG_MODULES=y # Zephyr / device drivers CONFIG_DAI_INIT_PRIORITY=70 @@ -34,6 +42,7 @@ CONFIG_DAI_DMIC_HAS_OWNERSHIP=n CONFIG_DAI_DMIC_HAS_MULTIPLE_LINE_SYNC=y CONFIG_DMA_INTEL_ADSP_GPDMA=n CONFIG_MM_DRV_INTEL_ADSP_TLB_REMAP_UNUSED_RAM=y +CONFIG_MM_DRV_INTEL_VIRTUAL_REGION_COUNT=3 CONFIG_SYS_CLOCK_TICKS_PER_SEC=12000 # Zephyr / power settings @@ -41,7 +50,13 @@ CONFIG_ADSP_IMR_CONTEXT_SAVE=y CONFIG_PM_POLICY_CUSTOM=y CONFIG_PM_PREWAKEUP_CONV_MODE_CEIL=y CONFIG_SRAM_RETENTION_MODE=n +CONFIG_PM_DEVICE_RUNTIME_ASYNC=n # Zephyr / logging CONFIG_LOG_BACKEND_ADSP=n CONFIG_WINSTREAM_CONSOLE=n +CONFIG_LOG_RUNTIME_FILTERING=y +CONFIG_LOG_RUNTIME_DEFAULT_LEVEL=1 + +# Zephyr / debug: temporary, until fixed +CONFIG_GDBSTUB=n diff --git a/app/boards/intel_adsp_cavs25.conf b/app/boards/intel_adsp_cavs25.conf index 0d39b072324c..7cd938ec7ff8 100644 --- a/app/boards/intel_adsp_cavs25.conf +++ b/app/boards/intel_adsp_cavs25.conf @@ -53,6 +53,5 @@ CONFIG_LOG_FUNC_NAME_PREFIX_ERR=y CONFIG_LOG_FUNC_NAME_PREFIX_WRN=y CONFIG_LOG_FUNC_NAME_PREFIX_INF=y CONFIG_LOG_FUNC_NAME_PREFIX_DBG=y -CONFIG_LOG_OUTPUT_FORMAT_LINUX_TIMESTAMP=y CONFIG_LOG_TIMESTAMP_64BIT=y CONFIG_WINSTREAM_CONSOLE=n diff --git a/app/boards/intel_adsp_cavs25_tgph.conf b/app/boards/intel_adsp_cavs25_tgph.conf index 2d6ac891cbd0..0c1d1dab1ad6 100644 --- a/app/boards/intel_adsp_cavs25_tgph.conf +++ b/app/boards/intel_adsp_cavs25_tgph.conf @@ -55,6 +55,5 @@ CONFIG_LOG_FUNC_NAME_PREFIX_ERR=y CONFIG_LOG_FUNC_NAME_PREFIX_WRN=y CONFIG_LOG_FUNC_NAME_PREFIX_INF=y CONFIG_LOG_FUNC_NAME_PREFIX_DBG=y -CONFIG_LOG_OUTPUT_FORMAT_LINUX_TIMESTAMP=y CONFIG_LOG_TIMESTAMP_64BIT=y CONFIG_WINSTREAM_CONSOLE=n diff --git a/app/boards/mt8365_mt8365_adsp.conf b/app/boards/mt8365_mt8365_adsp.conf new file mode 100644 index 000000000000..7c9d3647e8cc --- /dev/null +++ b/app/boards/mt8365_mt8365_adsp.conf @@ -0,0 +1,10 @@ +# Boilerplate. Because the "Platform" is a kconfig "choice" (of which +# "MTK" is an member), it can't be selected automatically from other +# kconfigs, nor expressed as a default. Don't put anything else here. +# Board-level config goes in Zephyr (and ideally in DTS). App-level +# config goes in prj.conf. +CONFIG_MTK=y + +# Override project default setting so 1 millisecond is exactly +# represented by an integral number of ticks. +CONFIG_SYS_CLOCK_TICKS_PER_SEC=13000 \ No newline at end of file diff --git a/app/boards/qemu_xtensa_dc233c.conf b/app/boards/qemu_xtensa_dc233c.conf new file mode 100644 index 000000000000..effed89802af --- /dev/null +++ b/app/boards/qemu_xtensa_dc233c.conf @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: BSD-3-Clause +CONFIG_IPC_MAJOR_4=y +CONFIG_ZTEST=y +CONFIG_MM_DRV=y +CONFIG_ZEPHYR_NATIVE_DRIVERS=y + +# Ensure the kernel can exit QEMU on shutdown/panic +CONFIG_REBOOT=y diff --git a/app/boards/qemu_xtensa_dc233c_mmu.conf b/app/boards/qemu_xtensa_dc233c_mmu.conf new file mode 100644 index 000000000000..8489a30197e6 --- /dev/null +++ b/app/boards/qemu_xtensa_dc233c_mmu.conf @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: BSD-3-Clause +CONFIG_IPC_MAJOR_4=y +CONFIG_USERSPACE=y +CONFIG_ZTEST=y +CONFIG_TEST_USERSPACE=y +CONFIG_MM_DRV=y +CONFIG_ZEPHYR_NATIVE_DRIVERS=y + +# Ensure the kernel can exit QEMU on shutdown/panic +CONFIG_REBOOT=y diff --git a/app/compr/README.txt b/app/compr/README.txt new file mode 100644 index 000000000000..a9c28957edbb --- /dev/null +++ b/app/compr/README.txt @@ -0,0 +1,37 @@ +Cadence Codec Configuration Overlays +===================================== + +This directory contains Kconfig overlay files for enabling Cadence codec support +in SOF firmware builds. + +Prerequisites +------------- +The Cadence codec libraries must be placed in the cadence_libs directory, one level +up from the SOF project directory: + sof/../cadence_libs/ + +Available Overlay Files +----------------------- +cadence.conf - Base Cadence codec module only (no individual codecs) +mp3.conf - MP3 decoder and encoder +aac.conf - AAC decoder +vorbis.conf - Vorbis decoder +all_codecs.conf - All supported codecs (MP3, AAC, Vorbis) + +Usage Examples +-------------- +Use with scripts/xtensa-build-zephyr.py via -o parameter: + +# Base module only (e.g., for Tiger Lake) +scripts/xtensa-build-zephyr.py tgl -o app/compr/cadence.conf + +# Single codec +scripts/xtensa-build-zephyr.py mtl -o app/compr/mp3.conf + +# Multiple codecs (use separate -o for each) +scripts/xtensa-build-zephyr.py mtl -o app/compr/mp3.conf -o app/compr/aac.conf + +# All codecs +scripts/xtensa-build-zephyr.py mtl -o app/compr/all_codecs.conf + +For more information about Cadence codecs, see the SOF documentation. diff --git a/app/compr/aac.conf b/app/compr/aac.conf new file mode 100644 index 000000000000..2adf1db86007 --- /dev/null +++ b/app/compr/aac.conf @@ -0,0 +1,4 @@ +# Cadence AAC decoder +CONFIG_CADENCE_CODEC=y +CONFIG_CADENCE_CODEC_AAC_DEC=y +CONFIG_CADENCE_CODEC_AAC_DEC_LIB="../cadence_libs/xa_aac_dec.a" diff --git a/app/compr/all_codecs.conf b/app/compr/all_codecs.conf new file mode 100644 index 000000000000..26b507f7f452 --- /dev/null +++ b/app/compr/all_codecs.conf @@ -0,0 +1,11 @@ +# All Cadence codecs (MP3, AAC, Vorbis) +CONFIG_CADENCE_CODEC=y +CONFIG_CADENCE_CODEC_MP3_DEC=y +CONFIG_CADENCE_CODEC_MP3_DEC_LIB="../cadence_libs/xa_mp3_dec.a" +CONFIG_CADENCE_CODEC_MP3_ENC=y +CONFIG_CADENCE_CODEC_MP3_ENC_LIB="../cadence_libs/xa_mp3_enc.a" +CONFIG_CADENCE_CODEC_AAC_DEC=y +CONFIG_CADENCE_CODEC_AAC_DEC_LIB="../cadence_libs/xa_aac_dec.a" +CONFIG_CADENCE_CODEC_VORBIS_DEC=y +CONFIG_CADENCE_CODEC_VORBIS_DEC_LIB="../cadence_libs/xa_vorbis_dec.a" + diff --git a/app/compr/cadence.conf b/app/compr/cadence.conf new file mode 100644 index 000000000000..9223140cab73 --- /dev/null +++ b/app/compr/cadence.conf @@ -0,0 +1,2 @@ +# Cadence codec module base support +CONFIG_CADENCE_CODEC=y diff --git a/app/compr/mp3.conf b/app/compr/mp3.conf new file mode 100644 index 000000000000..968f97295f57 --- /dev/null +++ b/app/compr/mp3.conf @@ -0,0 +1,6 @@ +# Cadence MP3 decoder and encoder +CONFIG_CADENCE_CODEC=y +CONFIG_CADENCE_CODEC_MP3_DEC=y +CONFIG_CADENCE_CODEC_MP3_DEC_LIB="../cadence_libs/xa_mp3_dec.a" +CONFIG_CADENCE_CODEC_MP3_ENC=y +CONFIG_CADENCE_CODEC_MP3_ENC_LIB="../cadence_libs/xa_mp3_enc.a" diff --git a/app/compr/vorbis.conf b/app/compr/vorbis.conf new file mode 100644 index 000000000000..064ac71b6616 --- /dev/null +++ b/app/compr/vorbis.conf @@ -0,0 +1,4 @@ +# Cadence Vorbis decoder +CONFIG_CADENCE_CODEC=y +CONFIG_CADENCE_CODEC_VORBIS_DEC=y +CONFIG_CADENCE_CODEC_VORBIS_DEC_LIB="../cadence_libs/xa_vorbis_dec.a" diff --git a/app/debug_overlay.conf b/app/debug_overlay.conf index 650b3c09f7f2..e99910ebfff1 100644 --- a/app/debug_overlay.conf +++ b/app/debug_overlay.conf @@ -1,9 +1,11 @@ CONFIG_DEBUG=y CONFIG_ASSERT=y -# Disabled until DSP panic #8621 is fixed -# CONFIG_ZTEST=y -# CONFIG_SOF_BOOT_TEST=y +CONFIG_ZTEST_NO_YIELD=n +CONFIG_ZTEST_SUMMARY=n +CONFIG_ZTEST_TEST_DELAY_MS=1 +CONFIG_SOF_BOOT_TEST_ALLOWED=y +CONFIG_TEST_EXTRA_STACK_SIZE=7168 CONFIG_DAI_VERBOSE_GLITCH_WARNINGS=y @@ -22,3 +24,10 @@ CONFIG_COLD_STORE_EXECUTE_DEBUG=y CONFIG_GDBSTUB=y CONFIG_GDBSTUB_ENTER_IMMEDIATELY=n + +# Logging options +# Set default runtime log level to INFO (3) to maintain expected behavior in SOF CI. +# This ensures firmware starts with INFO level logging, same as previous configuration. +# Testing with runtime filtering enabled ensures the same feature set is validated. +# Note: This setting has no effect if CONFIG_LOG_RUNTIME_FILTERING is disabled. +CONFIG_LOG_RUNTIME_DEFAULT_LEVEL=3 diff --git a/app/debug_stream_overlay.conf b/app/debug_stream_overlay.conf index 0195e5070b33..7edd6bf6709c 100644 --- a/app/debug_stream_overlay.conf +++ b/app/debug_stream_overlay.conf @@ -1,9 +1,24 @@ # Enable debug-stream protocol CONFIG_SOF_DEBUG_STREAM_SLOT=y +# Enable text message sending with ds_msg() +CONFIG_SOF_DEBUG_STREAM_TEXT_MSG=y # Add thread_info-client for debug stream CONFIG_SOF_DEBUG_STREAM_THREAD_INFO=y # Zephyr option for storing human readable thread names CONFIG_THREAD_NAME=y +# For Zephyr to compile with thread names on PTL we need to increase THREAD_BYTES +CONFIG_MAX_THREAD_BYTES=4 +# Shrink number of CPU sections +# As the number of supported cores shrink, the available circular +# buffer size increases. This means increased bandwidth. This is +# particularly useful, when debugging a problem on core 0. +#CONFIG_SOF_DEBUG_STREAM_SLOT_FORCE_MAX_CPUS=1 + +# Direct the assert prints to debug stream +# This option obeys CONFIG_EXCEPTION_DUMP_HOOK_ONLY. If it is selected +# the assert print is sent only to debug stream. Without it the assert +# prints are printed with vprintk too, +CONFIG_SOF_DEBUG_STREAM_TEXT_MSG_ASSERT_PRINT=y # Debug window slot configuration 1 # The CONFIG_SOF_TELEMETRY uses slot 2, but with performance and IO-performance @@ -12,9 +27,15 @@ CONFIG_MEMORY_WIN_2_SIZE=16384 CONFIG_SOF_TELEMETRY_PERFORMANCE_MEASUREMENTS=n CONFIG_SOF_TELEMETRY_IO_PERFORMANCE_MEASUREMENTS=n -# If we turn telemetry off all together, we can use slot 2. Slot 1 is used by mtrace +# If we turn telemetry off altogether, we can use slot 2. Slot 1 is used by mtrace #CONFIG_SOF_DEBUG_STREAM_SLOT_NUMBER=2 #CONFIG_SOF_TELEMETRY=n #CONFIG_SOF_TELEMETRY_PERFORMANCE_MEASUREMENTS=n #CONFIG_SOF_TELEMETRY_IO_PERFORMANCE_MEASUREMENTS=n +# Enable Zephyr exception printing hook; debug stream is sensitive to this option too +CONFIG_EXCEPTION_DUMP_HOOK=y +# Do not try to send exception prints through logs; this causes problems on PTL with mtrace +CONFIG_EXCEPTION_DUMP_HOOK_ONLY=y +# Print also backtrace through the exception hook +CONFIG_XTENSA_BACKTRACE_EXCEPTION_DUMP_HOOK=y diff --git a/app/os_linux_overlay.conf b/app/os_linux_overlay.conf index a1399d4ebe2c..18024b4e6106 100644 --- a/app/os_linux_overlay.conf +++ b/app/os_linux_overlay.conf @@ -3,6 +3,25 @@ # with SOF driver in Linux kernel # +# As a general rule, update of SOF firmware shall never +# break existing systems by depending on Linux kernel patches +# that have been upstreamed after commercial availability +# of a product. +CONFIG_SOF_OS_LINUX_COMPAT_PRIORITY=y + # SOF Linux driver does not require FW to retain its # state, so context save can be disabled CONFIG_ADSP_IMR_CONTEXT_SAVE=n +CONFIG_SOF_USERSPACE_PROXY=n + +# Logging options + +# Set default runtime log level to INFO (3). +# Note: This setting only has effect when CONFIG_LOG_RUNTIME_FILTERING is enabled. +# By default, the runtime level equals the compile-time level, but it is explicitly +# set here to INFO to ensure expected behavior if platform configurations set a +# lower default level (e.g., ERROR). +CONFIG_LOG_RUNTIME_DEFAULT_LEVEL=3 + +# Disable additional VMH buffers to use memory for virtual regions. +CONFIG_VIRTUAL_HEAP_EXTENDED=n diff --git a/app/overlays/mtl/dax_overlay.conf b/app/overlays/mtl/dax_overlay.conf new file mode 100644 index 000000000000..8bf245126b57 --- /dev/null +++ b/app/overlays/mtl/dax_overlay.conf @@ -0,0 +1,16 @@ +CONFIG_COMP_MODULE_ADAPTER=y +CONFIG_COMP_DOLBY_DAX_AUDIO_PROCESSING=y +CONFIG_COMP_DOLBY_DAX_AUDIO_PROCESSING_MOCK=n +CONFIG_KCPS_DYNAMIC_CLOCK_CONTROL=n +CONFIG_SOF_STACK_SIZE=8192 +CONFIG_IDC_TIMEOUT_US=50000 + +# LLEXT +CONFIG_LLEXT_HEAP_SIZE=32 +# Disabled due to issues encountered on the MTL platform. +# See https://github.com/thesofproject/sof/issues/10370 +CONFIG_LLEXT_EXPERIMENTAL=n + +# Log settings +CONFIG_LOG_BUFFER_SIZE=8192 +CONFIG_MTRACE_LOG_CACHE_BUF_SIZE=12288 diff --git a/app/overlays/ptl/ctc_overlay.conf b/app/overlays/ptl/ctc_overlay.conf new file mode 100644 index 000000000000..f609996f03c1 --- /dev/null +++ b/app/overlays/ptl/ctc_overlay.conf @@ -0,0 +1,2 @@ +CONFIG_COMP_MODULE_ADAPTER=y +CONFIG_COMP_GOOGLE_CTC_AUDIO_PROCESSING=y diff --git a/app/overlays/ptl/dax_overlay.conf b/app/overlays/ptl/dax_overlay.conf new file mode 100644 index 000000000000..7dcdf749dbe1 --- /dev/null +++ b/app/overlays/ptl/dax_overlay.conf @@ -0,0 +1,13 @@ +CONFIG_COMP_MODULE_ADAPTER=y +CONFIG_COMP_DOLBY_DAX_AUDIO_PROCESSING=y +CONFIG_COMP_DOLBY_DAX_AUDIO_PROCESSING_MOCK=n +CONFIG_KCPS_DYNAMIC_CLOCK_CONTROL=n +CONFIG_SOF_STACK_SIZE=8192 +CONFIG_IDC_TIMEOUT_US=50000 + +# LLEXT +CONFIG_LLEXT_HEAP_SIZE=32 + +# Log settings +CONFIG_LOG_BUFFER_SIZE=8192 +CONFIG_MTRACE_LOG_CACHE_BUF_SIZE=12288 diff --git a/app/overlays/ptl/dts_overlay.conf b/app/overlays/ptl/dts_overlay.conf new file mode 100644 index 000000000000..793559ad5de7 --- /dev/null +++ b/app/overlays/ptl/dts_overlay.conf @@ -0,0 +1,6 @@ +CONFIG_COMP_IIR=m +CONFIG_COMP_MODULE_ADAPTER=y +CONFIG_DTS_CODEC=y +CONFIG_LLEXT_HEAP_SIZE=64 +CONFIG_KCPS_DYNAMIC_CLOCK_CONTROL=n +CONFIG_SOF_STACK_SIZE=8192 diff --git a/app/overlays/ptl/userspace_overlay.conf b/app/overlays/ptl/userspace_overlay.conf index 331e2b4280d6..c484a2182807 100644 --- a/app/overlays/ptl/userspace_overlay.conf +++ b/app/overlays/ptl/userspace_overlay.conf @@ -11,3 +11,4 @@ CONFIG_DYNAMIC_THREAD_ALLOC=n # IADK module adapter store some buffers on stack, so we need to increase stack size CONFIG_DYNAMIC_THREAD_STACK_SIZE=8192 CONFIG_SOF_STACK_SIZE=8192 +CONFIG_SOF_USERSPACE_MOD_IPC_BY_DP_THREAD=y diff --git a/app/prj.conf b/app/prj.conf index e072f1df2fd2..5c22e19d6e0e 100644 --- a/app/prj.conf +++ b/app/prj.conf @@ -1,4 +1,3 @@ -CONFIG_SOF=y CONFIG_BUILD_OUTPUT_BIN=n # The additional stripped .elf files are deterministic. @@ -21,15 +20,16 @@ CONFIG_LOG=y CONFIG_LOG_PRINTK=y # Log processing is offloaded to a low-priority thread. CONFIG_LOG_MODE_DEFERRED=y -# Wake the low-priority log thread every 100ms and/or -# if more than 5 messages are queued by the frontend. -CONFIG_LOG_PROCESS_TRIGGER_THRESHOLD=5 -CONFIG_LOG_PROCESS_THREAD_SLEEP_MS=100 +# Wake the low-priority log thread every time new log +# messages are available +CONFIG_LOG_PROCESS_TRIGGER_THRESHOLD=1 # Frontend buffer must be large enough to cover all # typical bursts of log messages. CONFIG_LOG_BUFFER_SIZE=4096 # Use stack that is sufficient for SOF backends CONFIG_LOG_PROCESS_THREAD_STACK_SIZE=4096 +# Use Linux kernel style timestamp format +CONFIG_LOG_OUTPUT_FORMAT_LINUX_TIMESTAMP=y # Requires heap_info() be implemented, but no Zephyr wrapper CONFIG_DEBUG_MEMORY_USAGE_SCAN=n diff --git a/app/src/main.c b/app/src/main.c index 0f5c1f84fe0e..12cc3ffdc73a 100644 --- a/app/src/main.c +++ b/app/src/main.c @@ -5,10 +5,17 @@ */ #include - +#include #include + LOG_MODULE_REGISTER(main, LOG_LEVEL_DBG); +/* define qemu boot tests if any qemu target is defined, add targets to end */ +#if defined(CONFIG_BOARD_QEMU_XTENSA_DC233C) ||\ + defined(CONFIG_BOARD_QEMU_XTENSA_DC233C_MMU) +#define QEMU_BOOT_TESTS +#endif + /** * Should be included from sof/schedule/task.h * but triggers include chain issue @@ -50,11 +57,30 @@ static int sof_app_main(void) return 0; } +#if CONFIG_SOF_BOOT_TEST && defined(QEMU_BOOT_TESTS) +/* cleanly exit qemu so CI can continue and check test results */ +static inline void qemu_xtensa_exit(int status) +{ + register int syscall_id __asm__ ("a2") = 1; /* SYS_exit is 1 */ + register int exit_status __asm__ ("a3") = status; + + __asm__ __volatile__ ( + "simcall\n" + : + : "r" (syscall_id), "r" (exit_status) + : "memory" + ); +} +#endif + #if CONFIG_ZTEST void test_main(void) { sof_app_main(); - k_sleep(K_FOREVER); +#if CONFIG_SOF_BOOT_TEST && defined(QEMU_BOOT_TESTS) + sof_run_boot_tests(); + qemu_xtensa_exit(0); +#endif } #else int main(void) diff --git a/doc/sof.doxygen.in b/doc/sof.doxygen.in index bfbea17a7149..6379da500f11 100644 --- a/doc/sof.doxygen.in +++ b/doc/sof.doxygen.in @@ -40,3 +40,4 @@ TYPEDEF_HIDES_STRUCT = YES #FILTER_SOURCE_FILES = YES HTML_TIMESTAMP = NO +WARN_AS_ERROR = NO diff --git a/posix/include/rtos/alloc.h b/posix/include/rtos/alloc.h index 7927f17394e5..84751ef9f1f4 100644 --- a/posix/include/rtos/alloc.h +++ b/posix/include/rtos/alloc.h @@ -38,22 +38,24 @@ * the first two positions are reserved for SOF_BUF_ flags */ - /** \brief Indicates we should return DMA-able memory. */ + /** \brief Allocate DMA-able memory. */ #define SOF_MEM_FLAG_DMA BIT(2) -/** \brief Indicates that original content should not be copied by realloc. */ +/** \brief realloc() skips copying the original content. */ #define SOF_MEM_FLAG_NO_COPY BIT(3) -/** \brief Indicates that if we should return uncached address. */ +/** \brief Allocate uncached address. */ #define SOF_MEM_FLAG_COHERENT BIT(4) -/** \brief Indicates that if we should return L3 address. */ +/** \brief Allocate L3 address. */ #define SOF_MEM_FLAG_L3 BIT(5) -/** \brief Indicates that if we should return Low power memory address. */ +/** \brief Allocate Low power memory address. */ #define SOF_MEM_FLAG_LOW_POWER BIT(6) -/** \brief Indicates that if we should return kernel memory address. */ +/** \brief Allocate kernel memory address. */ #define SOF_MEM_FLAG_KERNEL BIT(7) -/** \brief Indicates that if we should return user memory address. */ +/** \brief Allocate user memory address. */ #define SOF_MEM_FLAG_USER BIT(8) -/** \brief Indicates that if we should return shared user memory address. */ +/** \brief Allocate shared user memory address. */ #define SOF_MEM_FLAG_USER_SHARED_BUFFER BIT(9) +/** \brief Use allocation method for large buffers. */ +#define SOF_MEM_FLAG_LARGE_BUFFER BIT(10) /** @} */ @@ -61,8 +63,15 @@ * Allocates memory block. * @param flags Flags, see SOF_MEM_FLAG_... * @param bytes Size in bytes. + * @param alignment Alignment in bytes. * @return Pointer to the allocated memory or NULL if failed. */ +void *rmalloc_align(uint32_t flags, size_t bytes, + uint32_t alignment); + +/** + * Similar to rmalloc_align(), but no alignment can be specified. + */ void *rmalloc(uint32_t flags, size_t bytes); /** @@ -88,29 +97,6 @@ static inline void *rballoc(uint32_t flags, size_t bytes) return rballoc_align(flags, bytes, PLATFORM_DCACHE_ALIGN); } -/** - * Changes size of the memory block allocated. - * @param ptr Address of the block to resize. - * @param flags Flags, see SOF_MEM_FLAG_... - * @param bytes New size in bytes. - * @param old_bytes Old size in bytes. - * @param alignment Alignment in bytes. - * @return Pointer to the resized memory of NULL if failed. - */ -void *rbrealloc_align(void *ptr, uint32_t flags, size_t bytes, - size_t old_bytes, uint32_t alignment); - -/** - * Similar to rballoc_align(), returns resized buffer aligned to - * PLATFORM_DCACHE_ALIGN. - */ -static inline void *rbrealloc(void *ptr, uint32_t flags, - size_t bytes, size_t old_bytes) -{ - return rbrealloc_align(ptr, flags, bytes, old_bytes, - PLATFORM_DCACHE_ALIGN); -} - /** * Frees the memory block. * @param ptr Pointer to the memory block. @@ -124,6 +110,16 @@ void rfree(void *ptr); */ void *rzalloc_core_sys(int core, size_t bytes); +struct k_heap; +static inline void k_heap_init(struct k_heap *heap, void *mem, size_t bytes) +{ +} +void *sof_heap_alloc(struct k_heap *heap, uint32_t flags, size_t bytes, + size_t alignment); +void sof_heap_free(struct k_heap *heap, void *addr); +struct k_heap *sof_sys_heap_get(void); +struct k_heap *sof_sys_user_heap_get(void); + /** * Calculates length of the null-terminated string. * @param s String. diff --git a/posix/include/rtos/kernel.h b/posix/include/rtos/kernel.h index 5921d86b0012..9fa3962a3589 100644 --- a/posix/include/rtos/kernel.h +++ b/posix/include/rtos/kernel.h @@ -10,6 +10,7 @@ #include +#include #include #ifdef __ZEPHYR__ @@ -28,6 +29,13 @@ typedef struct { #define Z_TIMEOUT_MS(t) ((k_timeout_t) { .ticks = clock_ms_to_ticks(PLATFORM_DEFAULT_CLOCK, t) }) +struct k_thread; +static inline bool thread_is_userspace(struct k_thread *thread) +{ + (void)thread; + return false; +} + static inline void k_sleep(k_timeout_t timeout) { wait_delay(timeout.ticks); @@ -43,4 +51,8 @@ static inline void k_usleep(int32_t us) wait_delay_us(us); } +struct k_heap { + int unused; +}; + #endif /* __POSIX_RTOS_KERNEL_H__ */ diff --git a/posix/include/rtos/mutex.h b/posix/include/rtos/mutex.h index 16c773f4cae4..19b360bdaea5 100644 --- a/posix/include/rtos/mutex.h +++ b/posix/include/rtos/mutex.h @@ -40,4 +40,26 @@ static inline int k_mutex_unlock(struct k_mutex *mutex) return 0; } +/* provide a no-op implementation for zephyr/sys/mutex.h */ + +struct sys_mutex { +}; + +#define SYS_MUTEX_DEFINE(name) \ + struct sys_mutex name + +static inline void sys_mutex_init(struct sys_mutex *mutex) +{ +} + +static inline int sys_mutex_lock(struct sys_mutex *mutex, k_timeout_t timeout) +{ + return 0; +} + +static inline int sys_mutex_unlock(struct sys_mutex *mutex) +{ + return 0; +} + #endif diff --git a/posix/include/rtos/sof.h b/posix/include/rtos/sof.h index 3cbb2e7bfefa..d6aa0fce66db 100644 --- a/posix/include/rtos/sof.h +++ b/posix/include/rtos/sof.h @@ -110,11 +110,6 @@ struct sof { struct ext_library *ext_library; #endif -#if CONFIG_IPC_MAJOR_4 - /* lock for fw_reg access */ - struct k_spinlock fw_reg_lock; -#endif - __aligned(PLATFORM_DCACHE_ALIGN) int alignment[0]; } __aligned(PLATFORM_DCACHE_ALIGN); diff --git a/posix/include/rtos/userspace_helper.h b/posix/include/rtos/userspace_helper.h index dda440a33ac4..09a52b7804d5 100644 --- a/posix/include/rtos/userspace_helper.h +++ b/posix/include/rtos/userspace_helper.h @@ -26,7 +26,7 @@ struct sys_heap; /** * Initialize private processing module heap. * @param N/A. - * @return pointer to the sys_heap structure. + * @return pointer to the k_heap structure. * * @note * Function used only when CONFIG_USERSPACE is set. @@ -34,69 +34,12 @@ struct sys_heap; * that should be isolated. The heap helps to accumulate all dynamic allocations in single memory * region which is then added to modules memory domain. */ -static inline struct sys_heap *module_driver_heap_init(void) +static inline struct k_heap *module_driver_heap_init(void) { return NULL; } - #endif -/** - * Allocates memory block from private module sys_heap if exists, otherwise call rballoc_align(). - * @param sys_heap - pointer to the sys_heap structure - * @param flags - Flags, see SOF_MEM_FLAG_... - * @param bytes - Size in bytes. - * @param alignment - Alignment in bytes. - * @return Pointer to the allocated memory or NULL if failed. - * - * @note When CONFIG_USERSPACE not set function calls rballoc_align() - */ -static inline void *module_driver_heap_aligned_alloc(struct sys_heap *mod_drv_heap, uint32_t flags, - size_t bytes, uint32_t align) -{ - return rballoc_align(flags, bytes, align); -} - -/** - * Allocates memory block from private module sys_heap if exists, otherwise call rmalloc. - * @param sys_heap - pointer to the sys_heap structure - * @param flags - Flags, see SOF_MEM_FLAG_... - * @param bytes - Size in bytes. - * @return - Pointer to the allocated memory or NULL if failed. - * - * * @note When CONFIG_USERSPACE not set function calls rmalloc() - */ -static inline void *module_driver_heap_rmalloc(struct sys_heap *mod_drv_heap, uint32_t flags, - size_t bytes) -{ - return rmalloc(flags, bytes); -} - -/** - * Similar to user_rmalloc(), guarantees that returned block is zeroed. - * - * @note When CONFIG_USERSPACE not set function calls rzalloc() - */ -static inline void *module_driver_heap_rzalloc(struct sys_heap *mod_drv_heap, uint32_t flags, - size_t bytes) -{ - return rzalloc(flags, bytes); -} - -/** - * Frees the memory block from private module sys_heap if exists. Otherwise call rfree. - * @param ptr Pointer to the memory block. - * - * @note User should take care to not free memory allocated from sys_heap - * with module_driver_heap set to NULL. It will cause exception. - * - * When CONFIG_USERSPACE not set function calls rfree() - */ -static inline void module_driver_heap_free(struct sys_heap *mod_drv_heap, void *mem) -{ - rfree(mem); -} - /** * Free private processing module heap. * @param sys_heap pointer to the sys_heap structure. @@ -105,7 +48,7 @@ static inline void module_driver_heap_free(struct sys_heap *mod_drv_heap, void * * Function used only when CONFIG_USERSPACE is set. * Frees private module heap. */ -static inline void module_driver_heap_remove(struct sys_heap *mod_drv_heap) +static inline void module_driver_heap_remove(struct k_heap *mod_drv_heap) { } #endif /* __RTOS_USERSPACE_HELPER_H__ */ diff --git a/posix/include/sof/lib/dma.h b/posix/include/sof/lib/dma.h index 960cfd469215..cb829dcaa21a 100644 --- a/posix/include/sof/lib/dma.h +++ b/posix/include/sof/lib/dma.h @@ -35,6 +35,7 @@ struct comp_buffer; struct comp_dev; +struct k_heap; /** \addtogroup sof_dma_drivers DMA Drivers * DMA Drivers API specification. @@ -511,13 +512,14 @@ static inline void dma_sg_init(struct dma_sg_elem_array *ea) ea->elems = NULL; } -int dma_sg_alloc(struct dma_sg_elem_array *ea, +int dma_sg_alloc(struct k_heap *heap, + struct dma_sg_elem_array *ea, uint32_t flags, 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); +void dma_sg_free(struct k_heap *heap, struct dma_sg_elem_array *ea); /** * \brief Get the total size of SG buffer diff --git a/scripts/README.md b/scripts/README.md new file mode 100644 index 000000000000..30441422899b --- /dev/null +++ b/scripts/README.md @@ -0,0 +1,167 @@ +# SOF Helper Scripts + +This folder contains a lot of useful scripts that can speed up development for routine tasks or simplify execution of complex tasks. + +## Build Scripts + +SOF has several build targets depending on whether you are building firmware, tooling, documentation or topologies. This directory has a helper for each. + +### Firmware Build (`xtensa-build-zephyr.py`) + +Firmware can either be built using west commands directly or by the `xtensa-build-zephyr.py` script. This script wraps up the west commands and can build using either the Zephyr SDK compiler or the Cadence xtensa compiler for xtensa targets. + +Please run the script with `--help` to see all options. + +E.g to build SOF for Intel Pantherlake: + +1) Enable the python virtual environment for west. This should be in your SOF workspace installation directory. Default is `~/work/sof` (only needs run once). + +```bash +source ~/work/sof/.venv/bin/activate +``` + +2) Now run the build script. *Note: most build errors are a result of ingredients being out of sync with the west manifest. Please run `west update` and rebuild before fixing/reporting build errors.* + +```bash +./scripts/xtensa-build-zephyr.py -p ptl +``` + +### Reproducible Output Builds (`test-repro-build.sh`) + +This script can be used to locally reproduce the exact build steps and environment of specific CI validation tests. + +```bash +./scripts/test-repro-build.sh --help +``` + +## Tools and Topologies + +Tooling and topology can be built together using one script. To build all topologies please run: + +```bash +./scripts/build-tools.sh +``` + +**Options for `build-tools.sh`:** + +* `-c` : Rebuild `ctl/` tool +* `-l` : Rebuild `logger/` tool +* `-p` : Rebuild `probes/` tool +* `-T` : Rebuild ALL `topology/` targets +* `-X` : Rebuild topology1 only +* `-Y` : Rebuild topology2 only +* `-t` : Rebuild test topologies +* `-A` : Clone and rebuild the local ALSA git version for `alsa-lib` and `alsa-utils` with latest non-distro features. +* `-C` : No build, only CMake re-configuration. Shows CMake targets. + +*Warning: building tools is incremental by default. To build from scratch delete the `tools/build_tools` directory or use `-C`.* + +### ALSA Specific Build (`build-alsa-tools.sh`) + +If you want to pull down and explicitly recompile only the associated ALSA libraries from their public `alsa-lib` GitHub upstream independently of SOF topologies: + +```bash +./scripts/build-alsa-tools.sh +``` + +## Testbench and Emulation + +Testbench is a host application that is used to run SOF processing modules on developers PC. This allows for module development using regular host based tooling. + +### Rebuilding the Testbench (`rebuild-testbench.sh`) + +This script cleans and rebuilds the host test application binary. Ensure you supply the correct target platform wrapper or fuzzing backend. + +**Usage Options:** + +* `-p ` : Build testbench binary for `xt-run` for selected platform (e.g. `-p tgl`). When omitted, performs a `BUILD_TYPE=native`, compile-only check. +* `-f` : Build testbench via a compiler provided by a fuzzer (default path: `.../AFL/afl-gcc`). +* `-j` : Number of parallel make jobs (defaults to `nproc`). + +### Running the Testbench (`host-testbench.sh`) + +Starts the testing sequences. This invokes specific components to ensure basic inputs process without segfaults. + +```bash +./scripts/host-testbench.sh +``` + +### QEMU Check (`qemu-check.sh`) + +Automated verifier for executing firmware builds under QEMU emulation. + +**Usage:** + +```bash +./scripts/qemu-check.sh [platform(s)] +``` + +* Supported platforms are: `imx8`, `imx8x`, `imx8m`. +* Runs all supported platforms by default if none are provided. + +## SDK Support + +There is some SDK support in this directory for speeding up or simplifying tasks with multiple steps. + +### New Modules (`sdk-create-module.py`) + +A new module can be created by running the SDK Create Module script. This python helper copies the SOF template audio module and renames all strings, Cmakefiles, and Kconfigs automatically. It also correctly registers a new DSP UUID and TOML entries. + +Please run: + +```bash +./scripts/sdk-create-module.py new_module_name +``` + +## Docker + +The docker container provided in `docker_build` sets up a build environment for building Sound Open Firmware. A working docker installation is needed to run the docker build container. + +*Note: In order to run docker as non sudo/root user please run:* + +```bash +sudo usermod -aG docker your-user-name +``` + +Then logout and login again. + +**Quick Start:** + +First, build the docker container. This step needs to be done initially and when the toolchain or ALSA dependencies are updated. + +```bash +cd scripts/docker_build +./docker-build.sh +``` + +After the container is built, it can be used to run the scripts. + +To build for tigerlake: + +```bash +./scripts/docker-run.sh ./scripts/xtensa-build-all.sh -l tgl +``` + +or (this command may prompt for a password during rimage installation inside the container) + +```bash +./scripts/docker-run.sh ./scripts/xtensa-build-all.sh tgl +``` + +To rebuild the topology and logger: + +```bash +./scripts/docker-run.sh ./scripts/build-tools.sh +``` + +An incremental `sof.git` build: + +```bash +./scripts/docker-run.sh make +``` + +Or enter a shell: + +```bash +./scripts/docker-run.sh bash +``` diff --git a/scripts/Readme.md b/scripts/Readme.md deleted file mode 100644 index df5c2587653c..000000000000 --- a/scripts/Readme.md +++ /dev/null @@ -1,107 +0,0 @@ -# SOF Helper Scripts - -This folder contains a lot of useful scripts that can speed up development for routine tasks or simplify execution of complex tasks. - -## Build Scripts - -SOF has several build targets depending on whether you are building firmware, tooling, documentation or topologies. This directory has a helper for each. - -### Firmware - -Firmware can either be built using west command directly or by the xtensa-build-zephyr.py script. This script wraps up the west commands and can build using either the Zephyr SDK compiler or the Cadence xtensa compiler for xtensa targets. - -Please run the script with --help to see all options. - -E.g to build SOF for Intel Pantherlake: - -1) Enable the python virtual environment for west. This should be in your SOF workspace installation direction. Default is ```~/work/sof``` (only needs run once). -```bash -source ~/work/sof/.venv/bin/activate -``` -2) Now run the build script. *Note: most build errors are a result of ingredients being out of sync with the west manifest. Please run ```west update``` and rebuild before fixing/reporting build errors.* -```bash -./scripts/xtensa-build-zephyr.py -p ptl -``` - -### Testbench - -Testbench is a host application that is used to run SOF processing modules on developers PC. This allows for module development using regular host based tooling. - -Please run -```bash -./rebuild-testbench.sh --help -``` -for full options. - -Testbench can be also be built for Cadence simulator targets. - -### Tools and Topologies - -Tooling and topology can be built together using one script. To build all topologies please run: - -```bash -./scripts/build-tools.sh -``` - -This script can build: -1) sof-ctl -2) sof-logger -3) probes -4) all topology 1 & 2 and test topologies. -5) Local ALSA git version for alsa-lib and alsa-utils that have features not yet in distro version of ALSA packages. - -## SDK Support - -There is some SDK support in this directory for speeding up or simplifying tasks with multiple steps. - -### New Modules - -A new module can be created by running the sdk-create-module script. This script will copy the template module and rename all strings, Cmakefiles, Kconfigs to match the new module. It will also create a UUID for the new module and a TOML manifest entry (for targets that need this). - -Please run -```bash -./sdk-create-module.py new_module_name -``` - -## Docker - -The docker container provided in docker_build sets up a build environment for -building Sound Open Firmware. A working docker installation is needed to run -the docker build container. - -Note: In order to run docker as non sudo/root user please run. -```bash -sudo usermod -aG docker your-user-name -``` -Then logout and login again. - -Quick Start: - -First, build the docker container. This step needs to be done initially and -when the toolchain or alsa dependencies are updated. -```bash -cd scripts/docker_build -./docker-build.sh -``` -After the container is built, it can be used to run the scripts. - -To build for tigerlake: -```bash -./scripts/docker-run.sh ./scripts/xtensa-build-all.sh -l tgl -``` -or (may need password test0000 for rimage install) -```bash -./scripts/docker-run.sh ./scripts/xtensa-build-all.sh tgl -``` -To rebuild the topology and logger: -```bash -./scripts/docker-run.sh ./scripts/build-tools.sh -``` -An incremental sof.git build: -```bash -./scripts/docker-run.sh make -``` -Or enter a shell: -```bash -./scripts/docker-run.sh bash -``` diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl deleted file mode 100755 index 86c25faec864..000000000000 --- a/scripts/checkpatch.pl +++ /dev/null @@ -1,7619 +0,0 @@ -#!/usr/bin/env perl -# SPDX-License-Identifier: GPL-2.0 -# -# (c) 2001, Dave Jones. (the file handling bit) -# (c) 2005, Joel Schopp (the ugly bit) -# (c) 2007,2008, Andy Whitcroft (new conditions, test suite) -# (c) 2008-2010 Andy Whitcroft -# (c) 2010-2018 Joe Perches - -use strict; -use warnings; -use POSIX; -use File::Basename; -use Cwd 'abs_path'; -use Term::ANSIColor qw(:constants); -use Encode qw(decode encode); - -my $P = $0; -my $D = dirname(abs_path($P)); - -my $V = '0.32'; - -use Getopt::Long qw(:config no_auto_abbrev); - -my $SOF = 1; # enables SOF-specific behaviour -my $quiet = 0; -my $verbose = 0; -my %verbose_messages = (); -my %verbose_emitted = (); -my $tree = 1; -my $chk_signoff = 1; -my $chk_patch = 1; -my $tst_only; -my $emacs = 0; -my $terse = 0; -my $showfile = 0; -my $file = 0; -my $git = 0; -my %git_commits = (); -my $check = 0; -my $check_orig = 0; -my $summary = 1; -my $mailback = 0; -my $summary_file = 0; -my $show_types = 0; -my $list_types = 0; -my $fix = 0; -my $fix_inplace = 0; -my $root; -my $gitroot = $ENV{'GIT_DIR'}; -$gitroot = ".git" if !defined($gitroot); -my %debug; -my %camelcase = (); -my %use_type = (); -my @use = (); -my %ignore_type = (); -my @ignore = (); -my $help = 0; -my $configuration_file = ".checkpatch.conf"; -my $max_line_length = 100; -my $ignore_perl_version = 0; -my $minimum_perl_version = 5.10.0; -my $min_conf_desc_length = 4; -my $spelling_file = "$D/spelling.txt"; -my $codespell = 0; -my $codespellfile = "/usr/share/codespell/dictionary.txt"; -my $user_codespellfile = ""; -my $conststructsfile = "$D/const_structs.checkpatch"; -my $docsfile = "$D/../Documentation/dev-tools/checkpatch.rst"; -my $typedefsfile; -my $color = "auto"; -my $allow_c99_comments = 1; # Can be overridden by --ignore C99_COMMENT_TOLERANCE -# git output parsing needs US English output, so first set backtick child process LANGUAGE -my $git_command ='export LANGUAGE=en_US.UTF-8; git'; -my $tabsize = 8; -my ${CONFIG_} = "CONFIG_"; - -sub help { - my ($exitcode) = @_; - - print << "EOM"; -Usage: $P [OPTION]... [FILE]... -Version: $V - -Options: - -q, --quiet quiet - -v, --verbose verbose mode - --no-tree run without a kernel tree - --no-signoff do not check for 'Signed-off-by' line - --patch treat FILE as patchfile (default) - --emacs emacs compile window format - --terse one line per report - --showfile emit diffed file position, not input file position - -g, --git treat FILE as a single commit or git revision range - single git commit with: - - ^ - ~n - multiple git commits with: - .. - ... - - - git merges are ignored - -f, --file treat FILE as regular source file - --subjective, --strict enable more subjective tests - --list-types list the possible message types - --types TYPE(,TYPE2...) show only these comma separated message types - --ignore TYPE(,TYPE2...) ignore various comma separated message types - --show-types show the specific message type in the output - --max-line-length=n set the maximum line length, (default $max_line_length) - if exceeded, warn on patches - requires --strict for use with --file - --min-conf-desc-length=n set the min description length, if shorter, warn - --tab-size=n set the number of spaces for tab (default $tabsize) - --root=PATH PATH to the kernel tree root - --no-summary suppress the per-file summary - --mailback only produce a report in case of warnings/errors - --summary-file include the filename in summary - --debug KEY=[0|1] turn on/off debugging of KEY, where KEY is one of - 'values', 'possible', 'type', and 'attr' (default - is all off) - --test-only=WORD report only warnings/errors containing WORD - literally - --fix EXPERIMENTAL - may create horrible results - If correctable single-line errors exist, create - ".EXPERIMENTAL-checkpatch-fixes" - with potential errors corrected to the preferred - checkpatch style - --fix-inplace EXPERIMENTAL - may create horrible results - Is the same as --fix, but overwrites the input - file. It's your fault if there's no backup or git - --ignore-perl-version override checking of perl version. expect - runtime errors. - --codespell Use the codespell dictionary for spelling/typos - (default:$codespellfile) - --codespellfile Use this codespell dictionary - --typedefsfile Read additional types from this file - --color[=WHEN] Use colors 'always', 'never', or only when output - is a terminal ('auto'). Default is 'auto'. - --kconfig-prefix=WORD use WORD as a prefix for Kconfig symbols (default - ${CONFIG_}) - -h, --help, --version display this help and exit - -When FILE is - read standard input. -EOM - - exit($exitcode); -} - -sub uniq { - my %seen; - return grep { !$seen{$_}++ } @_; -} - -sub list_types { - my ($exitcode) = @_; - - my $count = 0; - - local $/ = undef; - - open(my $script, '<', abs_path($P)) or - die "$P: Can't read '$P' $!\n"; - - my $text = <$script>; - close($script); - - my %types = (); - # Also catch when type or level is passed through a variable - while ($text =~ /(?:(\bCHK|\bWARN|\bERROR|&\{\$msg_level})\s*\(|\$msg_type\s*=)\s*"([^"]+)"/g) { - if (defined($1)) { - if (exists($types{$2})) { - $types{$2} .= ",$1" if ($types{$2} ne $1); - } else { - $types{$2} = $1; - } - } else { - $types{$2} = "UNDETERMINED"; - } - } - - print("#\tMessage type\n\n"); - if ($color) { - print(" ( Color coding: "); - print(RED . "ERROR" . RESET); - print(" | "); - print(YELLOW . "WARNING" . RESET); - print(" | "); - print(GREEN . "CHECK" . RESET); - print(" | "); - print("Multiple levels / Undetermined"); - print(" )\n\n"); - } - - foreach my $type (sort keys %types) { - my $orig_type = $type; - if ($color) { - my $level = $types{$type}; - if ($level eq "ERROR") { - $type = RED . $type . RESET; - } elsif ($level eq "WARN") { - $type = YELLOW . $type . RESET; - } elsif ($level eq "CHK") { - $type = GREEN . $type . RESET; - } - } - print(++$count . "\t" . $type . "\n"); - if ($verbose && exists($verbose_messages{$orig_type})) { - my $message = $verbose_messages{$orig_type}; - $message =~ s/\n/\n\t/g; - print("\t" . $message . "\n\n"); - } - } - - exit($exitcode); -} - -my $conf = which_conf($configuration_file); -if (-f $conf) { - my @conf_args; - open(my $conffile, '<', "$conf") - or warn "$P: Can't find a readable $configuration_file file $!\n"; - - while (<$conffile>) { - my $line = $_; - - $line =~ s/\s*\n?$//g; - $line =~ s/^\s*//g; - $line =~ s/\s+/ /g; - - next if ($line =~ m/^\s*#/); - next if ($line =~ m/^\s*$/); - - my @words = split(" ", $line); - foreach my $word (@words) { - last if ($word =~ m/^#/); - push (@conf_args, $word); - } - } - close($conffile); - unshift(@ARGV, @conf_args) if @conf_args; -} - -sub load_docs { - open(my $docs, '<', "$docsfile") - or warn "$P: Can't read the documentation file $docsfile $!\n"; - - my $type = ''; - my $desc = ''; - my $in_desc = 0; - - while (<$docs>) { - chomp; - my $line = $_; - $line =~ s/\s+$//; - - if ($line =~ /^\s*\*\*(.+)\*\*$/) { - if ($desc ne '') { - $verbose_messages{$type} = trim($desc); - } - $type = $1; - $desc = ''; - $in_desc = 1; - } elsif ($in_desc) { - if ($line =~ /^(?:\s{4,}|$)/) { - $line =~ s/^\s{4}//; - $desc .= $line; - $desc .= "\n"; - } else { - $verbose_messages{$type} = trim($desc); - $type = ''; - $desc = ''; - $in_desc = 0; - } - } - } - - if ($desc ne '') { - $verbose_messages{$type} = trim($desc); - } - close($docs); -} - -# Perl's Getopt::Long allows options to take optional arguments after a space. -# Prevent --color by itself from consuming other arguments -foreach (@ARGV) { - if ($_ eq "--color" || $_ eq "-color") { - $_ = "--color=$color"; - } -} - -GetOptions( - 'q|quiet+' => \$quiet, - 'v|verbose!' => \$verbose, - 'tree!' => \$tree, - 'signoff!' => \$chk_signoff, - 'patch!' => \$chk_patch, - 'emacs!' => \$emacs, - 'terse!' => \$terse, - 'showfile!' => \$showfile, - 'f|file!' => \$file, - 'g|git!' => \$git, - 'subjective!' => \$check, - 'strict!' => \$check, - 'ignore=s' => \@ignore, - 'types=s' => \@use, - 'show-types!' => \$show_types, - 'list-types!' => \$list_types, - 'max-line-length=i' => \$max_line_length, - 'min-conf-desc-length=i' => \$min_conf_desc_length, - 'tab-size=i' => \$tabsize, - 'root=s' => \$root, - 'summary!' => \$summary, - 'mailback!' => \$mailback, - 'summary-file!' => \$summary_file, - 'fix!' => \$fix, - 'fix-inplace!' => \$fix_inplace, - 'ignore-perl-version!' => \$ignore_perl_version, - 'debug=s' => \%debug, - 'test-only=s' => \$tst_only, - 'codespell!' => \$codespell, - 'codespellfile=s' => \$user_codespellfile, - 'typedefsfile=s' => \$typedefsfile, - 'color=s' => \$color, - 'no-color' => \$color, #keep old behaviors of -nocolor - 'nocolor' => \$color, #keep old behaviors of -nocolor - 'kconfig-prefix=s' => \${CONFIG_}, - 'h|help' => \$help, - 'version' => \$help -) or $help = 2; - -if ($user_codespellfile) { - # Use the user provided codespell file unconditionally - $codespellfile = $user_codespellfile; -} elsif (!(-f $codespellfile)) { - # If /usr/share/codespell/dictionary.txt is not present, try to find it - # under codespell's install directory: /data/dictionary.txt - if (($codespell || $help) && which("codespell") ne "" && which("python") ne "") { - my $python_codespell_dict = << "EOF"; - -import os.path as op -import codespell_lib -codespell_dir = op.dirname(codespell_lib.__file__) -codespell_file = op.join(codespell_dir, 'data', 'dictionary.txt') -print(codespell_file, end='') -EOF - - my $codespell_dict = `python -c "$python_codespell_dict" 2> /dev/null`; - $codespellfile = $codespell_dict if (-f $codespell_dict); - } -} - -# $help is 1 if either -h, --help or --version is passed as option - exitcode: 0 -# $help is 2 if invalid option is passed - exitcode: 1 -help($help - 1) if ($help); - -die "$P: --git cannot be used with --file or --fix\n" if ($git && ($file || $fix)); -die "$P: --verbose cannot be used with --terse\n" if ($verbose && $terse); - -if ($color =~ /^[01]$/) { - $color = !$color; -} elsif ($color =~ /^always$/i) { - $color = 1; -} elsif ($color =~ /^never$/i) { - $color = 0; -} elsif ($color =~ /^auto$/i) { - $color = (-t STDOUT); -} else { - die "$P: Invalid color mode: $color\n"; -} - -load_docs() if ($verbose); -list_types(0) if ($list_types); - -$fix = 1 if ($fix_inplace); -$check_orig = $check; - -my $exit = 0; - -my $perl_version_ok = 1; -if ($^V && $^V lt $minimum_perl_version) { - $perl_version_ok = 0; - printf "$P: requires at least perl version %vd\n", $minimum_perl_version; - exit(1) if (!$ignore_perl_version); -} - -#if no filenames are given, push '-' to read patch from stdin -if ($#ARGV < 0) { - push(@ARGV, '-'); -} - -# skip TAB size 1 to avoid additional checks on $tabsize - 1 -die "$P: Invalid TAB size: $tabsize\n" if ($tabsize < 2); - -sub hash_save_array_words { - my ($hashRef, $arrayRef) = @_; - - my @array = split(/,/, join(',', @$arrayRef)); - foreach my $word (@array) { - $word =~ s/\s*\n?$//g; - $word =~ s/^\s*//g; - $word =~ s/\s+/ /g; - $word =~ tr/[a-z]/[A-Z]/; - - next if ($word =~ m/^\s*#/); - next if ($word =~ m/^\s*$/); - - $hashRef->{$word}++; - } -} - -sub hash_show_words { - my ($hashRef, $prefix) = @_; - - if (keys %$hashRef) { - print "\nNOTE: $prefix message types:"; - foreach my $word (sort keys %$hashRef) { - print " $word"; - } - print "\n"; - } -} - -hash_save_array_words(\%ignore_type, \@ignore); -hash_save_array_words(\%use_type, \@use); - -my $dbg_values = 0; -my $dbg_possible = 0; -my $dbg_type = 0; -my $dbg_attr = 0; -for my $key (keys %debug) { - ## no critic - eval "\${dbg_$key} = '$debug{$key}';"; - die "$@" if ($@); -} - -my $rpt_cleaners = 0; - -if ($terse) { - $emacs = 1; - $quiet++; -} - -if ($tree) { - if (defined $root) { - if (!top_of_kernel_tree($root)) { - die "$P: $root: --root does not point at a valid tree\n"; - } - } else { - if (top_of_kernel_tree('.')) { - $root = '.'; - } elsif ($0 =~ m@(.*)/scripts/[^/]*$@ && - top_of_kernel_tree($1)) { - $root = $1; - } - } - - if (!defined $root) { - print "Must be run from the top-level dir. of a kernel tree\n"; - exit(2); - } -} - -my $emitted_corrupt = 0; - -our $Ident = qr{ - [A-Za-z_][A-Za-z\d_]* - (?:\s*\#\#\s*[A-Za-z_][A-Za-z\d_]*)* - }x; -our $Storage = qr{extern|static|asmlinkage}; -our $Sparse = qr{ - __sparse_cache| - __sparse_force| - __user| - __kernel| - __force| - __iomem| - __must_check| - __kprobes| - __ref| - __refconst| - __refdata| - __rcu| - __private - }x; -our $InitAttributePrefix = qr{__(?:mem|cpu|dev|net_|)}; -our $InitAttributeData = qr{$InitAttributePrefix(?:initdata\b)}; -our $InitAttributeConst = qr{$InitAttributePrefix(?:initconst\b)}; -our $InitAttributeInit = qr{$InitAttributePrefix(?:init\b)}; -our $InitAttribute = qr{$InitAttributeData|$InitAttributeConst|$InitAttributeInit}; - -# Notes to $Attribute: -# We need \b after 'init' otherwise 'initconst' will cause a false positive in a check -our $Attribute = qr{ - const| - volatile| - __percpu| - __nocast| - __safe| - __bitwise| - __packed__| - __packed2__| - __naked| - __maybe_unused| - __always_unused| - __noreturn| - __used| - __cold| - __pure| - __noclone| - __deprecated| - __read_mostly| - __ro_after_init| - __kprobes| - $InitAttribute| - ____cacheline_aligned| - ____cacheline_aligned_in_smp| - ____cacheline_internodealigned_in_smp| - __weak| - __alloc_size\s*\(\s*\d+\s*(?:,\s*\d+\s*)?\) - }x; -our $Modifier; -our $Inline = qr{inline|__always_inline|noinline|__inline|__inline__}; -our $Member = qr{->$Ident|\.$Ident|\[[^]]*\]}; -our $Lval = qr{$Ident(?:$Member)*}; - -our $Int_type = qr{(?i)llu|ull|ll|lu|ul|l|u}; -our $Binary = qr{(?i)0b[01]+$Int_type?}; -our $Hex = qr{(?i)0x[0-9a-f]+$Int_type?}; -our $Int = qr{[0-9]+$Int_type?}; -our $Octal = qr{0[0-7]+$Int_type?}; -our $String = qr{(?:\b[Lu])?"[X\t]*"}; -our $Float_hex = qr{(?i)0x[0-9a-f]+p-?[0-9]+[fl]?}; -our $Float_dec = qr{(?i)(?:[0-9]+\.[0-9]*|[0-9]*\.[0-9]+)(?:e-?[0-9]+)?[fl]?}; -our $Float_int = qr{(?i)[0-9]+e-?[0-9]+[fl]?}; -our $Float = qr{$Float_hex|$Float_dec|$Float_int}; -our $Constant = qr{$Float|$Binary|$Octal|$Hex|$Int}; -our $Assignment = qr{\*\=|/=|%=|\+=|-=|<<=|>>=|&=|\^=|\|=|=}; -our $Compare = qr{<=|>=|==|!=|<|(?}; -our $Arithmetic = qr{\+|-|\*|\/|%}; -our $Operators = qr{ - <=|>=|==|!=| - =>|->|<<|>>|<|>|!|~| - &&|\|\||,|\^|\+\+|--|&|\||$Arithmetic - }x; - -our $c90_Keywords = qr{do|for|while|if|else|return|goto|continue|switch|default|case|break}x; - -our $BasicType; -our $NonptrType; -our $NonptrTypeMisordered; -our $NonptrTypeWithAttr; -our $Type; -our $TypeMisordered; -our $Declare; -our $DeclareMisordered; - -our $NON_ASCII_UTF8 = qr{ - [\xC2-\xDF][\x80-\xBF] # non-overlong 2-byte - | \xE0[\xA0-\xBF][\x80-\xBF] # excluding overlongs - | [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} # straight 3-byte - | \xED[\x80-\x9F][\x80-\xBF] # excluding surrogates - | \xF0[\x90-\xBF][\x80-\xBF]{2} # planes 1-3 - | [\xF1-\xF3][\x80-\xBF]{3} # planes 4-15 - | \xF4[\x80-\x8F][\x80-\xBF]{2} # plane 16 -}x; - -our $UTF8 = qr{ - [\x09\x0A\x0D\x20-\x7E] # ASCII - | $NON_ASCII_UTF8 -}x; - -our $typeC99Typedefs = qr{(?:__)?(?:[us]_?)?int_?(?:8|16|32|64)_t}; -our $typeOtherOSTypedefs = qr{(?x: - u_(?:char|short|int|long) | # bsd - u(?:nchar|short|int|long) # sysv -)}; -our $typeKernelTypedefs = qr{(?x: - (?:__)?(?:u|s|be|le)(?:8|16|32|64)| - atomic_t -)}; -our $typeTypedefs = qr{(?x: - $typeC99Typedefs\b| - $typeOtherOSTypedefs\b| - $typeKernelTypedefs\b -)}; - -our $zero_initializer = qr{(?:(?:0[xX])?0+$Int_type?|NULL|false)\b}; - -our $logFunctions = qr{(?x: - printk(?:_ratelimited|_once|_deferred_once|_deferred|)| - (?:[a-z0-9]+_){1,2}(?:printk|emerg|alert|crit|err|warning|warn|notice|info|debug|dbg|vdbg|devel|cont|WARN)(?:_ratelimited|_once|)| - TP_printk| - WARN(?:_RATELIMIT|_ONCE|)| - panic| - MODULE_[A-Z_]+| - seq_vprintf|seq_printf|seq_puts| - trace[a-z_]+ -)}; - -our $allocFunctions = qr{(?x: - (?:(?:devm_)? - (?:kv|k|v)[czm]alloc(?:_array)?(?:_node)? | - kstrdup(?:_const)? | - kmemdup(?:_nul)?) | - (?:\w+)?alloc_skb(?:_ip_align)? | - # dev_alloc_skb/netdev_alloc_skb, et al - dma_alloc_coherent -)}; - -our $signature_tags = qr{(?xi: - Signed-off-by:| - Co-developed-by:| - Acked-by:| - Tested-by:| - Reviewed-by:| - Reported-by:| - Suggested-by:| - To:| - Cc: -)}; - -our $tracing_logging_tags = qr{(?xi: - [=-]*> | - <[=-]* | - \[ | - \] | - start | - called | - entered | - entry | - enter | - in | - inside | - here | - begin | - exit | - end | - done | - leave | - completed | - out | - return | - [\.\!:\s]* -)}; - -sub edit_distance_min { - my (@arr) = @_; - my $len = scalar @arr; - if ((scalar @arr) < 1) { - # if underflow, return - return; - } - my $min = $arr[0]; - for my $i (0 .. ($len-1)) { - if ($arr[$i] < $min) { - $min = $arr[$i]; - } - } - return $min; -} - -sub get_edit_distance { - my ($str1, $str2) = @_; - $str1 = lc($str1); - $str2 = lc($str2); - $str1 =~ s/-//g; - $str2 =~ s/-//g; - my $len1 = length($str1); - my $len2 = length($str2); - # two dimensional array storing minimum edit distance - my @distance; - for my $i (0 .. $len1) { - for my $j (0 .. $len2) { - if ($i == 0) { - $distance[$i][$j] = $j; - } elsif ($j == 0) { - $distance[$i][$j] = $i; - } elsif (substr($str1, $i-1, 1) eq substr($str2, $j-1, 1)) { - $distance[$i][$j] = $distance[$i - 1][$j - 1]; - } else { - my $dist1 = $distance[$i][$j - 1]; #insert distance - my $dist2 = $distance[$i - 1][$j]; # remove - my $dist3 = $distance[$i - 1][$j - 1]; #replace - $distance[$i][$j] = 1 + edit_distance_min($dist1, $dist2, $dist3); - } - } - } - return $distance[$len1][$len2]; -} - -sub find_standard_signature { - my ($sign_off) = @_; - my @standard_signature_tags = ( - 'Signed-off-by:', 'Co-developed-by:', 'Acked-by:', 'Tested-by:', - 'Reviewed-by:', 'Reported-by:', 'Suggested-by:' - ); - foreach my $signature (@standard_signature_tags) { - return $signature if (get_edit_distance($sign_off, $signature) <= 2); - } - - return ""; -} - -our @typeListMisordered = ( - qr{char\s+(?:un)?signed}, - qr{int\s+(?:(?:un)?signed\s+)?short\s}, - qr{int\s+short(?:\s+(?:un)?signed)}, - qr{short\s+int(?:\s+(?:un)?signed)}, - qr{(?:un)?signed\s+int\s+short}, - qr{short\s+(?:un)?signed}, - qr{long\s+int\s+(?:un)?signed}, - qr{int\s+long\s+(?:un)?signed}, - qr{long\s+(?:un)?signed\s+int}, - qr{int\s+(?:un)?signed\s+long}, - qr{int\s+(?:un)?signed}, - qr{int\s+long\s+long\s+(?:un)?signed}, - qr{long\s+long\s+int\s+(?:un)?signed}, - qr{long\s+long\s+(?:un)?signed\s+int}, - qr{long\s+long\s+(?:un)?signed}, - qr{long\s+(?:un)?signed}, -); - -our @typeList = ( - qr{void}, - qr{(?:(?:un)?signed\s+)?char}, - qr{(?:(?:un)?signed\s+)?short\s+int}, - qr{(?:(?:un)?signed\s+)?short}, - qr{(?:(?:un)?signed\s+)?int}, - qr{(?:(?:un)?signed\s+)?long\s+int}, - qr{(?:(?:un)?signed\s+)?long\s+long\s+int}, - qr{(?:(?:un)?signed\s+)?long\s+long}, - qr{(?:(?:un)?signed\s+)?long}, - qr{(?:un)?signed}, - qr{float}, - qr{double}, - qr{bool}, - qr{struct\s+$Ident}, - qr{union\s+$Ident}, - qr{enum\s+$Ident}, - qr{${Ident}_t}, - qr{${Ident}_handler}, - qr{${Ident}_handler_fn}, - @typeListMisordered, -); - -our $C90_int_types = qr{(?x: - long\s+long\s+int\s+(?:un)?signed| - long\s+long\s+(?:un)?signed\s+int| - long\s+long\s+(?:un)?signed| - (?:(?:un)?signed\s+)?long\s+long\s+int| - (?:(?:un)?signed\s+)?long\s+long| - int\s+long\s+long\s+(?:un)?signed| - int\s+(?:(?:un)?signed\s+)?long\s+long| - - long\s+int\s+(?:un)?signed| - long\s+(?:un)?signed\s+int| - long\s+(?:un)?signed| - (?:(?:un)?signed\s+)?long\s+int| - (?:(?:un)?signed\s+)?long| - int\s+long\s+(?:un)?signed| - int\s+(?:(?:un)?signed\s+)?long| - - int\s+(?:un)?signed| - (?:(?:un)?signed\s+)?int -)}; - -our @typeListFile = (); -our @typeListWithAttr = ( - @typeList, - qr{struct\s+$InitAttribute\s+$Ident}, - qr{union\s+$InitAttribute\s+$Ident}, -); - -our @modifierList = ( - qr{fastcall}, -); -our @modifierListFile = (); - -our @mode_permission_funcs = ( - ["module_param", 3], - ["module_param_(?:array|named|string)", 4], - ["module_param_array_named", 5], - ["debugfs_create_(?:file|u8|u16|u32|u64|x8|x16|x32|x64|size_t|atomic_t|bool|blob|regset32|u32_array)", 2], - ["proc_create(?:_data|)", 2], - ["(?:CLASS|DEVICE|SENSOR|SENSOR_DEVICE|IIO_DEVICE)_ATTR", 2], - ["IIO_DEV_ATTR_[A-Z_]+", 1], - ["SENSOR_(?:DEVICE_|)ATTR_2", 2], - ["SENSOR_TEMPLATE(?:_2|)", 3], - ["__ATTR", 2], -); - -my $word_pattern = '\b[A-Z]?[a-z]{2,}\b'; - -#Create a search pattern for all these functions to speed up a loop below -our $mode_perms_search = ""; -foreach my $entry (@mode_permission_funcs) { - $mode_perms_search .= '|' if ($mode_perms_search ne ""); - $mode_perms_search .= $entry->[0]; -} -$mode_perms_search = "(?:${mode_perms_search})"; - -our %deprecated_apis = ( - "synchronize_rcu_bh" => "synchronize_rcu", - "synchronize_rcu_bh_expedited" => "synchronize_rcu_expedited", - "call_rcu_bh" => "call_rcu", - "rcu_barrier_bh" => "rcu_barrier", - "synchronize_sched" => "synchronize_rcu", - "synchronize_sched_expedited" => "synchronize_rcu_expedited", - "call_rcu_sched" => "call_rcu", - "rcu_barrier_sched" => "rcu_barrier", - "get_state_synchronize_sched" => "get_state_synchronize_rcu", - "cond_synchronize_sched" => "cond_synchronize_rcu", -); - -#Create a search pattern for all these strings to speed up a loop below -our $deprecated_apis_search = ""; -foreach my $entry (keys %deprecated_apis) { - $deprecated_apis_search .= '|' if ($deprecated_apis_search ne ""); - $deprecated_apis_search .= $entry; -} -$deprecated_apis_search = "(?:${deprecated_apis_search})"; - -our $mode_perms_world_writable = qr{ - S_IWUGO | - S_IWOTH | - S_IRWXUGO | - S_IALLUGO | - 0[0-7][0-7][2367] -}x; - -our %mode_permission_string_types = ( - "S_IRWXU" => 0700, - "S_IRUSR" => 0400, - "S_IWUSR" => 0200, - "S_IXUSR" => 0100, - "S_IRWXG" => 0070, - "S_IRGRP" => 0040, - "S_IWGRP" => 0020, - "S_IXGRP" => 0010, - "S_IRWXO" => 0007, - "S_IROTH" => 0004, - "S_IWOTH" => 0002, - "S_IXOTH" => 0001, - "S_IRWXUGO" => 0777, - "S_IRUGO" => 0444, - "S_IWUGO" => 0222, - "S_IXUGO" => 0111, -); - -#Create a search pattern for all these strings to speed up a loop below -our $mode_perms_string_search = ""; -foreach my $entry (keys %mode_permission_string_types) { - $mode_perms_string_search .= '|' if ($mode_perms_string_search ne ""); - $mode_perms_string_search .= $entry; -} -our $single_mode_perms_string_search = "(?:${mode_perms_string_search})"; -our $multi_mode_perms_string_search = qr{ - ${single_mode_perms_string_search} - (?:\s*\|\s*${single_mode_perms_string_search})* -}x; - -sub perms_to_octal { - my ($string) = @_; - - return trim($string) if ($string =~ /^\s*0[0-7]{3,3}\s*$/); - - my $val = ""; - my $oval = ""; - my $to = 0; - my $curpos = 0; - my $lastpos = 0; - while ($string =~ /\b(($single_mode_perms_string_search)\b(?:\s*\|\s*)?\s*)/g) { - $curpos = pos($string); - my $match = $2; - my $omatch = $1; - last if ($lastpos > 0 && ($curpos - length($omatch) != $lastpos)); - $lastpos = $curpos; - $to |= $mode_permission_string_types{$match}; - $val .= '\s*\|\s*' if ($val ne ""); - $val .= $match; - $oval .= $omatch; - } - $oval =~ s/^\s*\|\s*//; - $oval =~ s/\s*\|\s*$//; - return sprintf("%04o", $to); -} - -our $allowed_asm_includes = qr{(?x: - irq| - memory| - time| - reboot -)}; -# memory.h: ARM has a custom one - -# Load common spelling mistakes and build regular expression list. -my $misspellings; -my %spelling_fix; - -if (open(my $spelling, '<', $spelling_file)) { - while (<$spelling>) { - my $line = $_; - - $line =~ s/\s*\n?$//g; - $line =~ s/^\s*//g; - - next if ($line =~ m/^\s*#/); - next if ($line =~ m/^\s*$/); - - my ($suspect, $fix) = split(/\|\|/, $line); - - $spelling_fix{$suspect} = $fix; - } - close($spelling); -} else { - warn "No typos will be found - file '$spelling_file': $!\n"; -} - -if ($codespell) { - if (open(my $spelling, '<', $codespellfile)) { - while (<$spelling>) { - my $line = $_; - - $line =~ s/\s*\n?$//g; - $line =~ s/^\s*//g; - - next if ($line =~ m/^\s*#/); - next if ($line =~ m/^\s*$/); - next if ($line =~ m/, disabled/i); - - $line =~ s/,.*$//; - - my ($suspect, $fix) = split(/->/, $line); - - $spelling_fix{$suspect} = $fix; - } - close($spelling); - } else { - warn "No codespell typos will be found - file '$codespellfile': $!\n"; - } -} - -$misspellings = join("|", sort keys %spelling_fix) if keys %spelling_fix; - -sub read_words { - my ($wordsRef, $file) = @_; - - if (open(my $words, '<', $file)) { - while (<$words>) { - my $line = $_; - - $line =~ s/\s*\n?$//g; - $line =~ s/^\s*//g; - - next if ($line =~ m/^\s*#/); - next if ($line =~ m/^\s*$/); - if ($line =~ /\s/) { - print("$file: '$line' invalid - ignored\n"); - next; - } - - $$wordsRef .= '|' if (defined $$wordsRef); - $$wordsRef .= $line; - } - close($file); - return 1; - } - - return 0; -} - -my $const_structs; -if (show_type("CONST_STRUCT")) { - read_words(\$const_structs, $conststructsfile) - or warn "No structs that should be const will be found - file '$conststructsfile': $!\n"; -} - -if (defined($typedefsfile)) { - my $typeOtherTypedefs; - read_words(\$typeOtherTypedefs, $typedefsfile) - or warn "No additional types will be considered - file '$typedefsfile': $!\n"; - $typeTypedefs .= '|' . $typeOtherTypedefs if (defined $typeOtherTypedefs); -} - -sub build_types { - my $mods = "(?x: \n" . join("|\n ", (@modifierList, @modifierListFile)) . "\n)"; - my $all = "(?x: \n" . join("|\n ", (@typeList, @typeListFile)) . "\n)"; - my $Misordered = "(?x: \n" . join("|\n ", @typeListMisordered) . "\n)"; - my $allWithAttr = "(?x: \n" . join("|\n ", @typeListWithAttr) . "\n)"; - $Modifier = qr{(?:$Attribute|$Sparse|$mods)}; - $BasicType = qr{ - (?:$typeTypedefs\b)| - (?:${all}\b) - }x; - $NonptrType = qr{ - (?:$Modifier\s+|const\s+)* - (?: - (?:typeof|__typeof__)\s*\([^\)]*\)| - (?:$typeTypedefs\b)| - (?:${all}\b) - ) - (?:\s+$Modifier|\s+const)* - }x; - $NonptrTypeMisordered = qr{ - (?:$Modifier\s+|const\s+)* - (?: - (?:${Misordered}\b) - ) - (?:\s+$Modifier|\s+const)* - }x; - $NonptrTypeWithAttr = qr{ - (?:$Modifier\s+|const\s+)* - (?: - (?:typeof|__typeof__)\s*\([^\)]*\)| - (?:$typeTypedefs\b)| - (?:${allWithAttr}\b) - ) - (?:\s+$Modifier|\s+const)* - }x; - $Type = qr{ - $NonptrType - (?:(?:\s|\*|\[\])+\s*const|(?:\s|\*\s*(?:const\s*)?|\[\])+|(?:\s*\[\s*\])+){0,4} - (?:\s+$Inline|\s+$Modifier)* - }x; - $TypeMisordered = qr{ - $NonptrTypeMisordered - (?:(?:\s|\*|\[\])+\s*const|(?:\s|\*\s*(?:const\s*)?|\[\])+|(?:\s*\[\s*\])+){0,4} - (?:\s+$Inline|\s+$Modifier)* - }x; - $Declare = qr{(?:$Storage\s+(?:$Inline\s+)?)?$Type}; - $DeclareMisordered = qr{(?:$Storage\s+(?:$Inline\s+)?)?$TypeMisordered}; -} -build_types(); - -our $Typecast = qr{\s*(\(\s*$NonptrType\s*\)){0,1}\s*}; - -# Using $balanced_parens, $LvalOrFunc, or $FuncArg -# requires at least perl version v5.10.0 -# Any use must be runtime checked with $^V - -our $balanced_parens = qr/(\((?:[^\(\)]++|(?-1))*\))/; -our $LvalOrFunc = qr{((?:[\&\*]\s*)?$Lval)\s*($balanced_parens{0,1})\s*}; -our $FuncArg = qr{$Typecast{0,1}($LvalOrFunc|$Constant|$String)}; - -our $declaration_macros = qr{(?x: - (?:$Storage\s+)?(?:[A-Z_][A-Z0-9]*_){0,2}(?:DEFINE|DECLARE)(?:_[A-Z0-9]+){1,6}\s*\(| - (?:$Storage\s+)?[HLP]?LIST_HEAD\s*\(| - (?:SKCIPHER_REQUEST|SHASH_DESC|AHASH_REQUEST)_ON_STACK\s*\( -)}; - -our %allow_repeated_words = ( - add => '', - added => '', - bad => '', - be => '', -); - -sub deparenthesize { - my ($string) = @_; - return "" if (!defined($string)); - - while ($string =~ /^\s*\(.*\)\s*$/) { - $string =~ s@^\s*\(\s*@@; - $string =~ s@\s*\)\s*$@@; - } - - $string =~ s@\s+@ @g; - - return $string; -} - -sub seed_camelcase_file { - my ($file) = @_; - - return if (!(-f $file)); - - local $/; - - open(my $include_file, '<', "$file") - or warn "$P: Can't read '$file' $!\n"; - my $text = <$include_file>; - close($include_file); - - my @lines = split('\n', $text); - - foreach my $line (@lines) { - next if ($line !~ /(?:[A-Z][a-z]|[a-z][A-Z])/); - if ($line =~ /^[ \t]*(?:#[ \t]*define|typedef\s+$Type)\s+(\w*(?:[A-Z][a-z]|[a-z][A-Z])\w*)/) { - $camelcase{$1} = 1; - } elsif ($line =~ /^\s*$Declare\s+(\w*(?:[A-Z][a-z]|[a-z][A-Z])\w*)\s*[\(\[,;]/) { - $camelcase{$1} = 1; - } elsif ($line =~ /^\s*(?:union|struct|enum)\s+(\w*(?:[A-Z][a-z]|[a-z][A-Z])\w*)\s*[;\{]/) { - $camelcase{$1} = 1; - } - } -} - -our %maintained_status = (); - -sub is_maintained_obsolete { - my ($filename) = @_; - - return 0 if (!$tree || !(-e "$root/scripts/get_maintainer.pl")); - - if (!exists($maintained_status{$filename})) { - $maintained_status{$filename} = `perl $root/scripts/get_maintainer.pl --status --nom --nol --nogit --nogit-fallback -f $filename 2>&1`; - } - - return $maintained_status{$filename} =~ /obsolete/i; -} - -sub is_SPDX_License_valid { - my ($license) = @_; - - return 1 if (!$tree || which("python3") eq "" || !(-x "$root/scripts/spdxcheck.py") || !(-e "$gitroot")); - - my $root_path = abs_path($root); - my $status = `cd "$root_path"; echo "$license" | scripts/spdxcheck.py -`; - return 0 if ($status ne ""); - return 1; -} - -my $camelcase_seeded = 0; -sub seed_camelcase_includes { - return if ($camelcase_seeded); - - my $files; - my $camelcase_cache = ""; - my @include_files = (); - - $camelcase_seeded = 1; - - if (-e "$gitroot") { - my $git_last_include_commit = `${git_command} log --no-merges --pretty=format:"%h%n" -1 -- include`; - chomp $git_last_include_commit; - $camelcase_cache = ".checkpatch-camelcase.git.$git_last_include_commit"; - } else { - my $last_mod_date = 0; - $files = `find $root/include -name "*.h"`; - @include_files = split('\n', $files); - foreach my $file (@include_files) { - my $date = POSIX::strftime("%Y%m%d%H%M", - localtime((stat $file)[9])); - $last_mod_date = $date if ($last_mod_date < $date); - } - $camelcase_cache = ".checkpatch-camelcase.date.$last_mod_date"; - } - - if ($camelcase_cache ne "" && -f $camelcase_cache) { - open(my $camelcase_file, '<', "$camelcase_cache") - or warn "$P: Can't read '$camelcase_cache' $!\n"; - while (<$camelcase_file>) { - chomp; - $camelcase{$_} = 1; - } - close($camelcase_file); - - return; - } - - if (-e "$gitroot") { - $files = `${git_command} ls-files "include/*.h"`; - @include_files = split('\n', $files); - } - - foreach my $file (@include_files) { - seed_camelcase_file($file); - } - - if ($camelcase_cache ne "") { - unlink glob ".checkpatch-camelcase.*"; - open(my $camelcase_file, '>', "$camelcase_cache") - or warn "$P: Can't write '$camelcase_cache' $!\n"; - foreach (sort { lc($a) cmp lc($b) } keys(%camelcase)) { - print $camelcase_file ("$_\n"); - } - close($camelcase_file); - } -} - -sub git_is_single_file { - my ($filename) = @_; - - return 0 if ((which("git") eq "") || !(-e "$gitroot")); - - my $output = `${git_command} ls-files -- $filename 2>/dev/null`; - my $count = $output =~ tr/\n//; - return $count eq 1 && $output =~ m{^${filename}$}; -} - -sub git_commit_info { - my ($commit, $id, $desc) = @_; - - return ($id, $desc) if ((which("git") eq "") || !(-e "$gitroot")); - - my $output = `${git_command} log --no-color --format='%H %s' -1 $commit 2>&1`; - $output =~ s/^\s*//gm; - my @lines = split("\n", $output); - - return ($id, $desc) if ($#lines < 0); - - if ($lines[0] =~ /^error: short SHA1 $commit is ambiguous/) { -# Maybe one day convert this block of bash into something that returns -# all matching commit ids, but it's very slow... -# -# echo "checking commits $1..." -# git rev-list --remotes | grep -i "^$1" | -# while read line ; do -# git log --format='%H %s' -1 $line | -# echo "commit $(cut -c 1-12,41-)" -# done - } elsif ($lines[0] =~ /^fatal: ambiguous argument '$commit': unknown revision or path not in the working tree\./ || - $lines[0] =~ /^fatal: bad object $commit/) { - $id = undef; - } else { - $id = substr($lines[0], 0, 12); - $desc = substr($lines[0], 41); - } - - return ($id, $desc); -} - -$chk_signoff = 0 if ($file); - -my @rawlines = (); -my @lines = (); -my @fixed = (); -my @fixed_inserted = (); -my @fixed_deleted = (); -my $fixlinenr = -1; - -# If input is git commits, extract all commits from the commit expressions. -# For example, HEAD-3 means we need check 'HEAD, HEAD~1, HEAD~2'. -die "$P: No git repository found\n" if ($git && !-e "$gitroot"); - -if ($git) { - my @commits = (); - foreach my $commit_expr (@ARGV) { - my $git_range; - if ($commit_expr =~ m/^(.*)-(\d+)$/) { - $git_range = "-$2 $1"; - } elsif ($commit_expr =~ m/\.\./) { - $git_range = "$commit_expr"; - } else { - $git_range = "-1 $commit_expr"; - } - my $lines = `${git_command} log --no-color --no-merges --pretty=format:'%H %s' $git_range`; - foreach my $line (split(/\n/, $lines)) { - $line =~ /^([0-9a-fA-F]{40,40}) (.*)$/; - next if (!defined($1) || !defined($2)); - my $sha1 = $1; - my $subject = $2; - unshift(@commits, $sha1); - $git_commits{$sha1} = $subject; - } - } - die "$P: no git commits after extraction!\n" if (@commits == 0); - @ARGV = @commits; -} - -my $vname; -$allow_c99_comments = !defined $ignore_type{"C99_COMMENT_TOLERANCE"}; -for my $filename (@ARGV) { - my $FILE; - my $is_git_file = git_is_single_file($filename); - my $oldfile = $file; - $file = 1 if ($is_git_file); - if ($git) { - open($FILE, '-|', "git format-patch -M --stdout -1 $filename") || - die "$P: $filename: git format-patch failed - $!\n"; - } elsif ($file) { - open($FILE, '-|', "diff -u /dev/null $filename") || - die "$P: $filename: diff failed - $!\n"; - } elsif ($filename eq '-') { - open($FILE, '<&STDIN'); - } else { - open($FILE, '<', "$filename") || - die "$P: $filename: open failed - $!\n"; - } - if ($filename eq '-') { - $vname = 'Your patch'; - } elsif ($git) { - $vname = "Commit " . substr($filename, 0, 12) . ' ("' . $git_commits{$filename} . '")'; - } else { - $vname = $filename; - } - while (<$FILE>) { - chomp; - push(@rawlines, $_); - $vname = qq("$1") if ($filename eq '-' && $_ =~ m/^Subject:\s+(.+)/i); - } - close($FILE); - - if ($#ARGV > 0 && $quiet == 0) { - print '-' x length($vname) . "\n"; - print "$vname\n"; - print '-' x length($vname) . "\n"; - } - - if (!process($filename)) { - $exit = 1; - } - @rawlines = (); - @lines = (); - @fixed = (); - @fixed_inserted = (); - @fixed_deleted = (); - $fixlinenr = -1; - @modifierListFile = (); - @typeListFile = (); - build_types(); - $file = $oldfile if ($is_git_file); -} - -if (!$quiet) { - hash_show_words(\%use_type, "Used"); - hash_show_words(\%ignore_type, "Ignored"); - - if (!$perl_version_ok) { - print << "EOM" - -NOTE: perl $^V is not modern enough to detect all possible issues. - An upgrade to at least perl $minimum_perl_version is suggested. -EOM - } - if ($exit) { - print << "EOM" - -NOTE: If any of the errors are false positives, please report - them to the maintainer, see CHECKPATCH in MAINTAINERS. -EOM - } -} - -exit($exit); - -sub top_of_kernel_tree { - my ($root) = @_; - - my @tree_check = ( - "COPYING", "CREDITS", "Kbuild", "MAINTAINERS", "Makefile", - "README", "Documentation", "arch", "include", "drivers", - "fs", "init", "ipc", "kernel", "lib", "scripts", - ); - - if ($SOF) { - @tree_check = ( - "LICENCE", "README.md", "tools", - "scripts", "doc", "src", "CODEOWNERS", - "CMakeLists.txt", - ); - } - - foreach my $check (@tree_check) { - if (! -e $root . '/' . $check) { - return 0; - } - } - return 1; -} - -sub parse_email { - my ($formatted_email) = @_; - - my $name = ""; - my $quoted = ""; - my $name_comment = ""; - my $address = ""; - my $comment = ""; - - if ($formatted_email =~ /^(.*)<(\S+\@\S+)>(.*)$/) { - $name = $1; - $address = $2; - $comment = $3 if defined $3; - } elsif ($formatted_email =~ /^\s*<(\S+\@\S+)>(.*)$/) { - $address = $1; - $comment = $2 if defined $2; - } elsif ($formatted_email =~ /(\S+\@\S+)(.*)$/) { - $address = $1; - $comment = $2 if defined $2; - $formatted_email =~ s/\Q$address\E.*$//; - $name = $formatted_email; - $name = trim($name); - $name =~ s/^\"|\"$//g; - # If there's a name left after stripping spaces and - # leading quotes, and the address doesn't have both - # leading and trailing angle brackets, the address - # is invalid. ie: - # "joe smith joe@smith.com" bad - # "joe smith ]+>$/) { - $name = ""; - $address = ""; - $comment = ""; - } - } - - # Extract comments from names excluding quoted parts - # "John D. (Doe)" - Do not extract - if ($name =~ s/\"(.+)\"//) { - $quoted = $1; - } - while ($name =~ s/\s*($balanced_parens)\s*/ /) { - $name_comment .= trim($1); - } - $name =~ s/^[ \"]+|[ \"]+$//g; - $name = trim("$quoted $name"); - - $address = trim($address); - $address =~ s/^\<|\>$//g; - $comment = trim($comment); - - if ($name =~ /[^\w \-]/i) { ##has "must quote" chars - $name =~ s/(?"; - } - $formatted_email .= "$comment"; - return $formatted_email; -} - -sub reformat_email { - my ($email) = @_; - - my ($email_name, $name_comment, $email_address, $comment) = parse_email($email); - return format_email($email_name, $name_comment, $email_address, $comment); -} - -sub same_email_addresses { - my ($email1, $email2) = @_; - - my ($email1_name, $name1_comment, $email1_address, $comment1) = parse_email($email1); - my ($email2_name, $name2_comment, $email2_address, $comment2) = parse_email($email2); - - return $email1_name eq $email2_name && - $email1_address eq $email2_address && - $name1_comment eq $name2_comment && - $comment1 eq $comment2; -} - -sub which { - my ($bin) = @_; - - foreach my $path (split(/:/, $ENV{PATH})) { - if (-e "$path/$bin") { - return "$path/$bin"; - } - } - - return ""; -} - -sub which_conf { - my ($conf) = @_; - - foreach my $path (split(/:/, ".:$ENV{HOME}:.scripts")) { - if (-e "$path/$conf") { - return "$path/$conf"; - } - } - - return ""; -} - -sub expand_tabs { - my ($str) = @_; - - my $res = ''; - my $n = 0; - for my $c (split(//, $str)) { - if ($c eq "\t") { - $res .= ' '; - $n++; - for (; ($n % $tabsize) != 0; $n++) { - $res .= ' '; - } - next; - } - $res .= $c; - $n++; - } - - return $res; -} -sub copy_spacing { - (my $res = shift) =~ tr/\t/ /c; - return $res; -} - -sub line_stats { - my ($line) = @_; - - # Drop the diff line leader and expand tabs - $line =~ s/^.//; - $line = expand_tabs($line); - - # Pick the indent from the front of the line. - my ($white) = ($line =~ /^(\s*)/); - - return (length($line), length($white)); -} - -my $sanitise_quote = ''; - -sub sanitise_line_reset { - my ($in_comment) = @_; - - if ($in_comment) { - $sanitise_quote = '*/'; - } else { - $sanitise_quote = ''; - } -} -sub sanitise_line { - my ($line) = @_; - - my $res = ''; - my $l = ''; - - my $qlen = 0; - my $off = 0; - my $c; - - # Always copy over the diff marker. - $res = substr($line, 0, 1); - - for ($off = 1; $off < length($line); $off++) { - $c = substr($line, $off, 1); - - # Comments we are whacking completely including the begin - # and end, all to $;. - if ($sanitise_quote eq '' && substr($line, $off, 2) eq '/*') { - $sanitise_quote = '*/'; - - substr($res, $off, 2, "$;$;"); - $off++; - next; - } - if ($sanitise_quote eq '*/' && substr($line, $off, 2) eq '*/') { - $sanitise_quote = ''; - substr($res, $off, 2, "$;$;"); - $off++; - next; - } - if ($sanitise_quote eq '' && substr($line, $off, 2) eq '//') { - $sanitise_quote = '//'; - - substr($res, $off, 2, $sanitise_quote); - $off++; - next; - } - - # A \ in a string means ignore the next character. - if (($sanitise_quote eq "'" || $sanitise_quote eq '"') && - $c eq "\\") { - substr($res, $off, 2, 'XX'); - $off++; - next; - } - # Regular quotes. - if ($c eq "'" || $c eq '"') { - if ($sanitise_quote eq '') { - $sanitise_quote = $c; - - substr($res, $off, 1, $c); - next; - } elsif ($sanitise_quote eq $c) { - $sanitise_quote = ''; - } - } - - #print "c<$c> SQ<$sanitise_quote>\n"; - if ($off != 0 && $sanitise_quote eq '*/' && $c ne "\t") { - substr($res, $off, 1, $;); - } elsif ($off != 0 && $sanitise_quote eq '//' && $c ne "\t") { - substr($res, $off, 1, $;); - } elsif ($off != 0 && $sanitise_quote && $c ne "\t") { - substr($res, $off, 1, 'X'); - } else { - substr($res, $off, 1, $c); - } - } - - if ($sanitise_quote eq '//') { - $sanitise_quote = ''; - } - - # The pathname on a #include may be surrounded by '<' and '>'. - if ($res =~ /^.\s*\#\s*include\s+\<(.*)\>/) { - my $clean = 'X' x length($1); - $res =~ s@\<.*\>@<$clean>@; - - # The whole of a #error is a string. - } elsif ($res =~ /^.\s*\#\s*(?:error|warning)\s+(.*)\b/) { - my $clean = 'X' x length($1); - $res =~ s@(\#\s*(?:error|warning)\s+).*@$1$clean@; - } - - if ($allow_c99_comments && $res =~ m@(//.*$)@) { - my $match = $1; - $res =~ s/\Q$match\E/"$;" x length($match)/e; - } - - return $res; -} - -sub get_quoted_string { - my ($line, $rawline) = @_; - - return "" if (!defined($line) || !defined($rawline)); - return "" if ($line !~ m/($String)/g); - return substr($rawline, $-[0], $+[0] - $-[0]); -} - -sub ctx_statement_block { - my ($linenr, $remain, $off) = @_; - my $line = $linenr - 1; - my $blk = ''; - my $soff = $off; - my $coff = $off - 1; - my $coff_set = 0; - - my $loff = 0; - - my $type = ''; - my $level = 0; - my @stack = (); - my $p; - my $c; - my $len = 0; - - my $remainder; - while (1) { - @stack = (['', 0]) if ($#stack == -1); - - #warn "CSB: blk<$blk> remain<$remain>\n"; - # If we are about to drop off the end, pull in more - # context. - if ($off >= $len) { - for (; $remain > 0; $line++) { - last if (!defined $lines[$line]); - next if ($lines[$line] =~ /^-/); - $remain--; - $loff = $len; - $blk .= $lines[$line] . "\n"; - $len = length($blk); - $line++; - last; - } - # Bail if there is no further context. - #warn "CSB: blk<$blk> off<$off> len<$len>\n"; - if ($off >= $len) { - last; - } - if ($level == 0 && substr($blk, $off) =~ /^.\s*#\s*define/) { - $level++; - $type = '#'; - } - } - $p = $c; - $c = substr($blk, $off, 1); - $remainder = substr($blk, $off); - - #warn "CSB: c<$c> type<$type> level<$level> remainder<$remainder> coff_set<$coff_set>\n"; - - # Handle nested #if/#else. - if ($remainder =~ /^#\s*(?:ifndef|ifdef|if)\s/) { - push(@stack, [ $type, $level ]); - } elsif ($remainder =~ /^#\s*(?:else|elif)\b/) { - ($type, $level) = @{$stack[$#stack - 1]}; - } elsif ($remainder =~ /^#\s*endif\b/) { - ($type, $level) = @{pop(@stack)}; - } - - # Statement ends at the ';' or a close '}' at the - # outermost level. - if ($level == 0 && $c eq ';') { - last; - } - - # An else is really a conditional as long as its not else if - if ($level == 0 && $coff_set == 0 && - (!defined($p) || $p =~ /(?:\s|\}|\+)/) && - $remainder =~ /^(else)(?:\s|{)/ && - $remainder !~ /^else\s+if\b/) { - $coff = $off + length($1) - 1; - $coff_set = 1; - #warn "CSB: mark coff<$coff> soff<$soff> 1<$1>\n"; - #warn "[" . substr($blk, $soff, $coff - $soff + 1) . "]\n"; - } - - if (($type eq '' || $type eq '(') && $c eq '(') { - $level++; - $type = '('; - } - if ($type eq '(' && $c eq ')') { - $level--; - $type = ($level != 0)? '(' : ''; - - if ($level == 0 && $coff < $soff) { - $coff = $off; - $coff_set = 1; - #warn "CSB: mark coff<$coff>\n"; - } - } - if (($type eq '' || $type eq '{') && $c eq '{') { - $level++; - $type = '{'; - } - if ($type eq '{' && $c eq '}') { - $level--; - $type = ($level != 0)? '{' : ''; - - if ($level == 0) { - if (substr($blk, $off + 1, 1) eq ';') { - $off++; - } - last; - } - } - # Preprocessor commands end at the newline unless escaped. - if ($type eq '#' && $c eq "\n" && $p ne "\\") { - $level--; - $type = ''; - $off++; - last; - } - $off++; - } - # We are truly at the end, so shuffle to the next line. - if ($off == $len) { - $loff = $len + 1; - $line++; - $remain--; - } - - my $statement = substr($blk, $soff, $off - $soff + 1); - my $condition = substr($blk, $soff, $coff - $soff + 1); - - #warn "STATEMENT<$statement>\n"; - #warn "CONDITION<$condition>\n"; - - #print "coff<$coff> soff<$off> loff<$loff>\n"; - - return ($statement, $condition, - $line, $remain + 1, $off - $loff + 1, $level); -} - -sub statement_lines { - my ($stmt) = @_; - - # Strip the diff line prefixes and rip blank lines at start and end. - $stmt =~ s/(^|\n)./$1/g; - $stmt =~ s/^\s*//; - $stmt =~ s/\s*$//; - - my @stmt_lines = ($stmt =~ /\n/g); - - return $#stmt_lines + 2; -} - -sub statement_rawlines { - my ($stmt) = @_; - - my @stmt_lines = ($stmt =~ /\n/g); - - return $#stmt_lines + 2; -} - -sub statement_block_size { - my ($stmt) = @_; - - $stmt =~ s/(^|\n)./$1/g; - $stmt =~ s/^\s*{//; - $stmt =~ s/}\s*$//; - $stmt =~ s/^\s*//; - $stmt =~ s/\s*$//; - - my @stmt_lines = ($stmt =~ /\n/g); - my @stmt_statements = ($stmt =~ /;/g); - - my $stmt_lines = $#stmt_lines + 2; - my $stmt_statements = $#stmt_statements + 1; - - if ($stmt_lines > $stmt_statements) { - return $stmt_lines; - } else { - return $stmt_statements; - } -} - -sub ctx_statement_full { - my ($linenr, $remain, $off) = @_; - my ($statement, $condition, $level); - - my (@chunks); - - # Grab the first conditional/block pair. - ($statement, $condition, $linenr, $remain, $off, $level) = - ctx_statement_block($linenr, $remain, $off); - #print "F: c<$condition> s<$statement> remain<$remain>\n"; - push(@chunks, [ $condition, $statement ]); - if (!($remain > 0 && $condition =~ /^\s*(?:\n[+-])?\s*(?:if|else|do)\b/s)) { - return ($level, $linenr, @chunks); - } - - # Pull in the following conditional/block pairs and see if they - # could continue the statement. - for (;;) { - ($statement, $condition, $linenr, $remain, $off, $level) = - ctx_statement_block($linenr, $remain, $off); - #print "C: c<$condition> s<$statement> remain<$remain>\n"; - last if (!($remain > 0 && $condition =~ /^(?:\s*\n[+-])*\s*(?:else|do)\b/s)); - #print "C: push\n"; - push(@chunks, [ $condition, $statement ]); - } - - return ($level, $linenr, @chunks); -} - -sub ctx_block_get { - my ($linenr, $remain, $outer, $open, $close, $off) = @_; - my $line; - my $start = $linenr - 1; - my $blk = ''; - my @o; - my @c; - my @res = (); - - my $level = 0; - my @stack = ($level); - for ($line = $start; $remain > 0; $line++) { - next if ($rawlines[$line] =~ /^-/); - $remain--; - - $blk .= $rawlines[$line]; - - # Handle nested #if/#else. - if ($lines[$line] =~ /^.\s*#\s*(?:ifndef|ifdef|if)\s/) { - push(@stack, $level); - } elsif ($lines[$line] =~ /^.\s*#\s*(?:else|elif)\b/) { - $level = $stack[$#stack - 1]; - } elsif ($lines[$line] =~ /^.\s*#\s*endif\b/) { - $level = pop(@stack); - } - - foreach my $c (split(//, $lines[$line])) { - ##print "C<$c>L<$level><$open$close>O<$off>\n"; - if ($off > 0) { - $off--; - next; - } - - if ($c eq $close && $level > 0) { - $level--; - last if ($level == 0); - } elsif ($c eq $open) { - $level++; - } - } - - if (!$outer || $level <= 1) { - push(@res, $rawlines[$line]); - } - - last if ($level == 0); - } - - return ($level, @res); -} -sub ctx_block_outer { - my ($linenr, $remain) = @_; - - my ($level, @r) = ctx_block_get($linenr, $remain, 1, '{', '}', 0); - return @r; -} -sub ctx_block { - my ($linenr, $remain) = @_; - - my ($level, @r) = ctx_block_get($linenr, $remain, 0, '{', '}', 0); - return @r; -} -sub ctx_statement { - my ($linenr, $remain, $off) = @_; - - my ($level, @r) = ctx_block_get($linenr, $remain, 0, '(', ')', $off); - return @r; -} -sub ctx_block_level { - my ($linenr, $remain) = @_; - - return ctx_block_get($linenr, $remain, 0, '{', '}', 0); -} -sub ctx_statement_level { - my ($linenr, $remain, $off) = @_; - - return ctx_block_get($linenr, $remain, 0, '(', ')', $off); -} - -sub ctx_locate_comment { - my ($first_line, $end_line) = @_; - - # If c99 comment on the current line, or the line before or after - my ($current_comment) = ($rawlines[$end_line - 1] =~ m@^\+.*(//.*$)@); - return $current_comment if (defined $current_comment); - ($current_comment) = ($rawlines[$end_line - 2] =~ m@^[\+ ].*(//.*$)@); - return $current_comment if (defined $current_comment); - ($current_comment) = ($rawlines[$end_line] =~ m@^[\+ ].*(//.*$)@); - return $current_comment if (defined $current_comment); - - # Catch a comment on the end of the line itself. - ($current_comment) = ($rawlines[$end_line - 1] =~ m@.*(/\*.*\*/)\s*(?:\\\s*)?$@); - return $current_comment if (defined $current_comment); - - # Look through the context and try and figure out if there is a - # comment. - my $in_comment = 0; - $current_comment = ''; - for (my $linenr = $first_line; $linenr < $end_line; $linenr++) { - my $line = $rawlines[$linenr - 1]; - #warn " $line\n"; - if ($linenr == $first_line and $line =~ m@^.\s*\*@) { - $in_comment = 1; - } - if ($line =~ m@/\*@) { - $in_comment = 1; - } - if (!$in_comment && $current_comment ne '') { - $current_comment = ''; - } - $current_comment .= $line . "\n" if ($in_comment); - if ($line =~ m@\*/@) { - $in_comment = 0; - } - } - - chomp($current_comment); - return($current_comment); -} -sub ctx_has_comment { - my ($first_line, $end_line) = @_; - my $cmt = ctx_locate_comment($first_line, $end_line); - - ##print "LINE: $rawlines[$end_line - 1 ]\n"; - ##print "CMMT: $cmt\n"; - - return ($cmt ne ''); -} - -sub raw_line { - my ($linenr, $cnt) = @_; - - my $offset = $linenr - 1; - $cnt++; - - my $line; - while ($cnt) { - $line = $rawlines[$offset++]; - next if (defined($line) && $line =~ /^-/); - $cnt--; - } - - return $line; -} - -sub get_stat_real { - my ($linenr, $lc) = @_; - - my $stat_real = raw_line($linenr, 0); - for (my $count = $linenr + 1; $count <= $lc; $count++) { - $stat_real = $stat_real . "\n" . raw_line($count, 0); - } - - return $stat_real; -} - -sub get_stat_here { - my ($linenr, $cnt, $here) = @_; - - my $herectx = $here . "\n"; - for (my $n = 0; $n < $cnt; $n++) { - $herectx .= raw_line($linenr, $n) . "\n"; - } - - return $herectx; -} - -sub cat_vet { - my ($vet) = @_; - my ($res, $coded); - - $res = ''; - while ($vet =~ /([^[:cntrl:]]*)([[:cntrl:]]|$)/g) { - $res .= $1; - if ($2 ne '') { - $coded = sprintf("^%c", unpack('C', $2) + 64); - $res .= $coded; - } - } - $res =~ s/$/\$/; - - return $res; -} - -my $av_preprocessor = 0; -my $av_pending; -my @av_paren_type; -my $av_pend_colon; - -sub annotate_reset { - $av_preprocessor = 0; - $av_pending = '_'; - @av_paren_type = ('E'); - $av_pend_colon = 'O'; -} - -sub annotate_values { - my ($stream, $type) = @_; - - my $res; - my $var = '_' x length($stream); - my $cur = $stream; - - print "$stream\n" if ($dbg_values > 1); - - while (length($cur)) { - @av_paren_type = ('E') if ($#av_paren_type < 0); - print " <" . join('', @av_paren_type) . - "> <$type> <$av_pending>" if ($dbg_values > 1); - if ($cur =~ /^(\s+)/o) { - print "WS($1)\n" if ($dbg_values > 1); - if ($1 =~ /\n/ && $av_preprocessor) { - $type = pop(@av_paren_type); - $av_preprocessor = 0; - } - - } elsif ($cur =~ /^(\(\s*$Type\s*)\)/ && $av_pending eq '_') { - print "CAST($1)\n" if ($dbg_values > 1); - push(@av_paren_type, $type); - $type = 'c'; - - } elsif ($cur =~ /^($Type)\s*(?:$Ident|,|\)|\(|\s*$)/) { - print "DECLARE($1)\n" if ($dbg_values > 1); - $type = 'T'; - - } elsif ($cur =~ /^($Modifier)\s*/) { - print "MODIFIER($1)\n" if ($dbg_values > 1); - $type = 'T'; - - } elsif ($cur =~ /^(\#\s*define\s*$Ident)(\(?)/o) { - print "DEFINE($1,$2)\n" if ($dbg_values > 1); - $av_preprocessor = 1; - push(@av_paren_type, $type); - if ($2 ne '') { - $av_pending = 'N'; - } - $type = 'E'; - - } elsif ($cur =~ /^(\#\s*(?:undef\s*$Ident|include\b))/o) { - print "UNDEF($1)\n" if ($dbg_values > 1); - $av_preprocessor = 1; - push(@av_paren_type, $type); - - } elsif ($cur =~ /^(\#\s*(?:ifdef|ifndef|if))/o) { - print "PRE_START($1)\n" if ($dbg_values > 1); - $av_preprocessor = 1; - - push(@av_paren_type, $type); - push(@av_paren_type, $type); - $type = 'E'; - - } elsif ($cur =~ /^(\#\s*(?:else|elif))/o) { - print "PRE_RESTART($1)\n" if ($dbg_values > 1); - $av_preprocessor = 1; - - push(@av_paren_type, $av_paren_type[$#av_paren_type]); - - $type = 'E'; - - } elsif ($cur =~ /^(\#\s*(?:endif))/o) { - print "PRE_END($1)\n" if ($dbg_values > 1); - - $av_preprocessor = 1; - - # Assume all arms of the conditional end as this - # one does, and continue as if the #endif was not here. - pop(@av_paren_type); - push(@av_paren_type, $type); - $type = 'E'; - - } elsif ($cur =~ /^(\\\n)/o) { - print "PRECONT($1)\n" if ($dbg_values > 1); - - } elsif ($cur =~ /^(__attribute__)\s*\(?/o) { - print "ATTR($1)\n" if ($dbg_values > 1); - $av_pending = $type; - $type = 'N'; - - } elsif ($cur =~ /^(sizeof)\s*(\()?/o) { - print "SIZEOF($1)\n" if ($dbg_values > 1); - if (defined $2) { - $av_pending = 'V'; - } - $type = 'N'; - - } elsif ($cur =~ /^(if|while|for)\b/o) { - print "COND($1)\n" if ($dbg_values > 1); - $av_pending = 'E'; - $type = 'N'; - - } elsif ($cur =~/^(case)/o) { - print "CASE($1)\n" if ($dbg_values > 1); - $av_pend_colon = 'C'; - $type = 'N'; - - } elsif ($cur =~/^(return|else|goto|typeof|__typeof__)\b/o) { - print "KEYWORD($1)\n" if ($dbg_values > 1); - $type = 'N'; - - } elsif ($cur =~ /^(\()/o) { - print "PAREN('$1')\n" if ($dbg_values > 1); - push(@av_paren_type, $av_pending); - $av_pending = '_'; - $type = 'N'; - - } elsif ($cur =~ /^(\))/o) { - my $new_type = pop(@av_paren_type); - if ($new_type ne '_') { - $type = $new_type; - print "PAREN('$1') -> $type\n" - if ($dbg_values > 1); - } else { - print "PAREN('$1')\n" if ($dbg_values > 1); - } - - } elsif ($cur =~ /^($Ident)\s*\(/o) { - print "FUNC($1)\n" if ($dbg_values > 1); - $type = 'V'; - $av_pending = 'V'; - - } elsif ($cur =~ /^($Ident\s*):(?:\s*\d+\s*(,|=|;))?/) { - if (defined $2 && $type eq 'C' || $type eq 'T') { - $av_pend_colon = 'B'; - } elsif ($type eq 'E') { - $av_pend_colon = 'L'; - } - print "IDENT_COLON($1,$type>$av_pend_colon)\n" if ($dbg_values > 1); - $type = 'V'; - - } elsif ($cur =~ /^($Ident|$Constant)/o) { - print "IDENT($1)\n" if ($dbg_values > 1); - $type = 'V'; - - } elsif ($cur =~ /^($Assignment)/o) { - print "ASSIGN($1)\n" if ($dbg_values > 1); - $type = 'N'; - - } elsif ($cur =~/^(;|{|})/) { - print "END($1)\n" if ($dbg_values > 1); - $type = 'E'; - $av_pend_colon = 'O'; - - } elsif ($cur =~/^(,)/) { - print "COMMA($1)\n" if ($dbg_values > 1); - $type = 'C'; - - } elsif ($cur =~ /^(\?)/o) { - print "QUESTION($1)\n" if ($dbg_values > 1); - $type = 'N'; - - } elsif ($cur =~ /^(:)/o) { - print "COLON($1,$av_pend_colon)\n" if ($dbg_values > 1); - - substr($var, length($res), 1, $av_pend_colon); - if ($av_pend_colon eq 'C' || $av_pend_colon eq 'L') { - $type = 'E'; - } else { - $type = 'N'; - } - $av_pend_colon = 'O'; - - } elsif ($cur =~ /^(\[)/o) { - print "CLOSE($1)\n" if ($dbg_values > 1); - $type = 'N'; - - } elsif ($cur =~ /^(-(?![->])|\+(?!\+)|\*|\&\&|\&)/o) { - my $variant; - - print "OPV($1)\n" if ($dbg_values > 1); - if ($type eq 'V') { - $variant = 'B'; - } else { - $variant = 'U'; - } - - substr($var, length($res), 1, $variant); - $type = 'N'; - - } elsif ($cur =~ /^($Operators)/o) { - print "OP($1)\n" if ($dbg_values > 1); - if ($1 ne '++' && $1 ne '--') { - $type = 'N'; - } - - } elsif ($cur =~ /(^.)/o) { - print "C($1)\n" if ($dbg_values > 1); - } - if (defined $1) { - $cur = substr($cur, length($1)); - $res .= $type x length($1); - } - } - - return ($res, $var); -} - -sub possible { - my ($possible, $line) = @_; - my $notPermitted = qr{(?: - ^(?: - $Modifier| - $Storage| - $Type| - DEFINE_\S+ - )$| - ^(?: - goto| - return| - case| - else| - asm|__asm__| - do| - \#| - \#\#| - )(?:\s|$)| - ^(?:typedef|struct|enum)\b - )}x; - warn "CHECK<$possible> ($line)\n" if ($dbg_possible > 2); - if ($possible !~ $notPermitted) { - # Check for modifiers. - $possible =~ s/\s*$Storage\s*//g; - $possible =~ s/\s*$Sparse\s*//g; - if ($possible =~ /^\s*$/) { - - } elsif ($possible =~ /\s/) { - $possible =~ s/\s*$Type\s*//g; - for my $modifier (split(' ', $possible)) { - if ($modifier !~ $notPermitted) { - warn "MODIFIER: $modifier ($possible) ($line)\n" if ($dbg_possible); - push(@modifierListFile, $modifier); - } - } - - } else { - warn "POSSIBLE: $possible ($line)\n" if ($dbg_possible); - push(@typeListFile, $possible); - } - build_types(); - } else { - warn "NOTPOSS: $possible ($line)\n" if ($dbg_possible > 1); - } -} - -my $prefix = ''; - -sub show_type { - my ($type) = @_; - - $type =~ tr/[a-z]/[A-Z]/; - - return defined $use_type{$type} if (scalar keys %use_type > 0); - - return !defined $ignore_type{$type}; -} - -sub report { - my ($level, $type, $msg) = @_; - - if (!show_type($type) || - (defined $tst_only && $msg !~ /\Q$tst_only\E/)) { - return 0; - } - my $output = ''; - if ($color) { - if ($level eq 'ERROR') { - $output .= RED; - } elsif ($level eq 'WARNING') { - $output .= YELLOW; - } else { - $output .= GREEN; - } - } - $output .= $prefix . $level . ':'; - if ($show_types) { - $output .= BLUE if ($color); - $output .= "$type:"; - } - $output .= RESET if ($color); - $output .= ' ' . $msg . "\n"; - - if ($showfile) { - my @lines = split("\n", $output, -1); - splice(@lines, 1, 1); - $output = join("\n", @lines); - } - - if ($terse) { - $output = (split('\n', $output))[0] . "\n"; - } - - if ($verbose && exists($verbose_messages{$type}) && - !exists($verbose_emitted{$type})) { - $output .= $verbose_messages{$type} . "\n\n"; - $verbose_emitted{$type} = 1; - } - - push(our @report, $output); - - return 1; -} - -sub report_dump { - our @report; -} - -sub fixup_current_range { - my ($lineRef, $offset, $length) = @_; - - if ($$lineRef =~ /^\@\@ -\d+,\d+ \+(\d+),(\d+) \@\@/) { - my $o = $1; - my $l = $2; - my $no = $o + $offset; - my $nl = $l + $length; - $$lineRef =~ s/\+$o,$l \@\@/\+$no,$nl \@\@/; - } -} - -sub fix_inserted_deleted_lines { - my ($linesRef, $insertedRef, $deletedRef) = @_; - - my $range_last_linenr = 0; - my $delta_offset = 0; - - my $old_linenr = 0; - my $new_linenr = 0; - - my $next_insert = 0; - my $next_delete = 0; - - my @lines = (); - - my $inserted = @{$insertedRef}[$next_insert++]; - my $deleted = @{$deletedRef}[$next_delete++]; - - foreach my $old_line (@{$linesRef}) { - my $save_line = 1; - my $line = $old_line; #don't modify the array - if ($line =~ /^(?:\+\+\+|\-\-\-)\s+\S+/) { #new filename - $delta_offset = 0; - } elsif ($line =~ /^\@\@ -\d+,\d+ \+\d+,\d+ \@\@/) { #new hunk - $range_last_linenr = $new_linenr; - fixup_current_range(\$line, $delta_offset, 0); - } - - while (defined($deleted) && ${$deleted}{'LINENR'} == $old_linenr) { - $deleted = @{$deletedRef}[$next_delete++]; - $save_line = 0; - fixup_current_range(\$lines[$range_last_linenr], $delta_offset--, -1); - } - - while (defined($inserted) && ${$inserted}{'LINENR'} == $old_linenr) { - push(@lines, ${$inserted}{'LINE'}); - $inserted = @{$insertedRef}[$next_insert++]; - $new_linenr++; - fixup_current_range(\$lines[$range_last_linenr], $delta_offset++, 1); - } - - if ($save_line) { - push(@lines, $line); - $new_linenr++; - } - - $old_linenr++; - } - - return @lines; -} - -sub fix_insert_line { - my ($linenr, $line) = @_; - - my $inserted = { - LINENR => $linenr, - LINE => $line, - }; - push(@fixed_inserted, $inserted); -} - -sub fix_delete_line { - my ($linenr, $line) = @_; - - my $deleted = { - LINENR => $linenr, - LINE => $line, - }; - - push(@fixed_deleted, $deleted); -} - -sub ERROR { - my ($type, $msg) = @_; - - if (report("ERROR", $type, $msg)) { - our $clean = 0; - our $cnt_error++; - return 1; - } - return 0; -} -sub WARN { - my ($type, $msg) = @_; - - if (report("WARNING", $type, $msg)) { - our $clean = 0; - our $cnt_warn++; - return 1; - } - return 0; -} -sub CHK { - my ($type, $msg) = @_; - - if ($check && report("CHECK", $type, $msg)) { - our $clean = 0; - our $cnt_chk++; - return 1; - } - return 0; -} - -sub check_absolute_file { - my ($absolute, $herecurr) = @_; - my $file = $absolute; - - ##print "absolute<$absolute>\n"; - - # See if any suffix of this path is a path within the tree. - while ($file =~ s@^[^/]*/@@) { - if (-f "$root/$file") { - ##print "file<$file>\n"; - last; - } - } - if (! -f _) { - return 0; - } - - # It is, so see if the prefix is acceptable. - my $prefix = $absolute; - substr($prefix, -length($file)) = ''; - - ##print "prefix<$prefix>\n"; - if ($prefix ne ".../") { - WARN("USE_RELATIVE_PATH", - "use relative pathname instead of absolute in changelog text\n" . $herecurr); - } -} - -sub trim { - my ($string) = @_; - - $string =~ s/^\s+|\s+$//g; - - return $string; -} - -sub ltrim { - my ($string) = @_; - - $string =~ s/^\s+//; - - return $string; -} - -sub rtrim { - my ($string) = @_; - - $string =~ s/\s+$//; - - return $string; -} - -sub string_find_replace { - my ($string, $find, $replace) = @_; - - $string =~ s/$find/$replace/g; - - return $string; -} - -sub tabify { - my ($leading) = @_; - - my $source_indent = $tabsize; - my $max_spaces_before_tab = $source_indent - 1; - my $spaces_to_tab = " " x $source_indent; - - #convert leading spaces to tabs - 1 while $leading =~ s@^([\t]*)$spaces_to_tab@$1\t@g; - #Remove spaces before a tab - 1 while $leading =~ s@^([\t]*)( {1,$max_spaces_before_tab})\t@$1\t@g; - - return "$leading"; -} - -sub pos_last_openparen { - my ($line) = @_; - - my $pos = 0; - - my $opens = $line =~ tr/\(/\(/; - my $closes = $line =~ tr/\)/\)/; - - my $last_openparen = 0; - - if (($opens == 0) || ($closes >= $opens)) { - return -1; - } - - my $len = length($line); - - for ($pos = 0; $pos < $len; $pos++) { - my $string = substr($line, $pos); - if ($string =~ /^($FuncArg|$balanced_parens)/) { - $pos += length($1) - 1; - } elsif (substr($line, $pos, 1) eq '(') { - $last_openparen = $pos; - } elsif (index($string, '(') == -1) { - last; - } - } - - return length(expand_tabs(substr($line, 0, $last_openparen))) + 1; -} - -sub get_raw_comment { - my ($line, $rawline) = @_; - my $comment = ''; - - for my $i (0 .. (length($line) - 1)) { - if (substr($line, $i, 1) eq "$;") { - $comment .= substr($rawline, $i, 1); - } - } - - return $comment; -} - -sub exclude_global_initialisers { - my ($realfile) = @_; - - # Do not check for BPF programs (tools/testing/selftests/bpf/progs/*.c, samples/bpf/*_kern.c, *.bpf.c). - return $realfile =~ m@^tools/testing/selftests/bpf/progs/.*\.c$@ || - $realfile =~ m@^samples/bpf/.*_kern\.c$@ || - $realfile =~ m@/bpf/.*\.bpf\.c$@; -} - -sub process { - my $filename = shift; - - my $linenr=0; - my $prevline=""; - my $prevrawline=""; - my $stashline=""; - my $stashrawline=""; - - my $length; - my $indent; - my $previndent=0; - my $stashindent=0; - - our $clean = 1; - my $signoff = 0; - my $author = ''; - my $authorsignoff = 0; - my $author_sob = ''; - my $is_patch = 0; - my $is_binding_patch = -1; - my $in_header_lines = $file ? 0 : 1; - my $in_commit_log = 0; #Scanning lines before patch - my $has_patch_separator = 0; #Found a --- line - my $has_commit_log = 0; #Encountered lines before patch - my $commit_log_lines = 0; #Number of commit log lines - my $commit_log_possible_stack_dump = 0; - my $commit_log_long_line = 0; - my $commit_log_has_diff = 0; - my $reported_maintainer_file = 0; - my $reported_abi_update = 0; - my $last_abi_file = ''; - my $non_utf8_charset = 0; - - my $last_git_commit_id_linenr = -1; - - my $last_blank_line = 0; - my $last_coalesced_string_linenr = -1; - - our @report = (); - our $cnt_lines = 0; - our $cnt_error = 0; - our $cnt_warn = 0; - our $cnt_chk = 0; - - # Trace the real file/line as we go. - my $realfile = ''; - my $realline = 0; - my $realcnt = 0; - my $here = ''; - my $context_function; #undef'd unless there's a known function - my $in_comment = 0; - my $comment_edge = 0; - my $first_line = 0; - my $p1_prefix = ''; - - my $prev_values = 'E'; - - # suppression flags - my %suppress_ifbraces; - my %suppress_whiletrailers; - my %suppress_export; - my $suppress_statement = 0; - - my %signatures = (); - - # Pre-scan the patch sanitizing the lines. - # Pre-scan the patch looking for any __setup documentation. - # - my @setup_docs = (); - my $setup_docs = 0; - - my $camelcase_file_seeded = 0; - - my $checklicenseline = 1; - - sanitise_line_reset(); - my $line; - foreach my $rawline (@rawlines) { - $linenr++; - $line = $rawline; - - push(@fixed, $rawline) if ($fix); - - if ($rawline=~/^\+\+\+\s+(\S+)/) { - $setup_docs = 0; - if ($1 =~ m@Documentation/admin-guide/kernel-parameters.txt$@) { - $setup_docs = 1; - } - #next; - } - if ($rawline =~ /^\@\@ -\d+(?:,\d+)? \+(\d+)(,(\d+))? \@\@/) { - $realline=$1-1; - if (defined $2) { - $realcnt=$3+1; - } else { - $realcnt=1+1; - } - $in_comment = 0; - - # Guestimate if this is a continuing comment. Run - # the context looking for a comment "edge". If this - # edge is a close comment then we must be in a comment - # at context start. - my $edge; - my $cnt = $realcnt; - for (my $ln = $linenr + 1; $cnt > 0; $ln++) { - next if (defined $rawlines[$ln - 1] && - $rawlines[$ln - 1] =~ /^-/); - $cnt--; - #print "RAW<$rawlines[$ln - 1]>\n"; - last if (!defined $rawlines[$ln - 1]); - if ($rawlines[$ln - 1] =~ m@(/\*|\*/)@ && - $rawlines[$ln - 1] !~ m@"[^"]*(?:/\*|\*/)[^"]*"@) { - ($edge) = $1; - last; - } - } - if (defined $edge && $edge eq '*/') { - $in_comment = 1; - } - - # Guestimate if this is a continuing comment. If this - # is the start of a diff block and this line starts - # ' *' then it is very likely a comment. - if (!defined $edge && - $rawlines[$linenr] =~ m@^.\s*(?:\*\*+| \*)(?:\s|$)@) - { - $in_comment = 1; - } - - ##print "COMMENT:$in_comment edge<$edge> $rawline\n"; - sanitise_line_reset($in_comment); - - } elsif ($realcnt && $rawline =~ /^(?:\+| |$)/) { - # Standardise the strings and chars within the input to - # simplify matching -- only bother with positive lines. - $line = sanitise_line($rawline); - } - - # Check if ABI is being updated. If so, there's probably no need to - # emit the "does ABI need updating?" message on file add/move/delete - if ($SOF && - ($line =~ /\+#define SOF_ABI_MAJOR*/ || - $line =~ /\+#define SOF_ABI_MINOR*/ || - $line =~ /\+#define SOF_ABI_PATCH*/)) { - $reported_abi_update = 1; - } - - push(@lines, $line); - - if ($realcnt > 1) { - $realcnt-- if ($line =~ /^(?:\+| |$)/); - } else { - $realcnt = 0; - } - - #print "==>$rawline\n"; - #print "-->$line\n"; - - if ($setup_docs && $line =~ /^\+/) { - push(@setup_docs, $line); - } - } - - $prefix = ''; - - $realcnt = 0; - $linenr = 0; - $fixlinenr = -1; - foreach my $line (@lines) { - $linenr++; - $fixlinenr++; - my $sline = $line; #copy of $line - $sline =~ s/$;/ /g; #with comments as spaces - - my $rawline = $rawlines[$linenr - 1]; - my $raw_comment = get_raw_comment($line, $rawline); - -# check if it's a mode change, rename or start of a patch - if (!$in_commit_log && - ($line =~ /^ mode change [0-7]+ => [0-7]+ \S+\s*$/ || - ($line =~ /^rename (?:from|to) \S+\s*$/ || - $line =~ /^diff --git a\/[\w\/\.\_\-]+ b\/\S+\s*$/))) { - $is_patch = 1; - } - -#extract the line range in the file after the patch is applied - if (!$in_commit_log && - $line =~ /^\@\@ -\d+(?:,\d+)? \+(\d+)(,(\d+))? \@\@(.*)/) { - my $context = $4; - $is_patch = 1; - $first_line = $linenr + 1; - $realline=$1-1; - if (defined $2) { - $realcnt=$3+1; - } else { - $realcnt=1+1; - } - annotate_reset(); - $prev_values = 'E'; - - %suppress_ifbraces = (); - %suppress_whiletrailers = (); - %suppress_export = (); - $suppress_statement = 0; - if ($context =~ /\b(\w+)\s*\(/) { - $context_function = $1; - } else { - undef $context_function; - } - next; - -# track the line number as we move through the hunk, note that -# new versions of GNU diff omit the leading space on completely -# blank context lines so we need to count that too. - } elsif ($line =~ /^( |\+|$)/) { - $realline++; - $realcnt-- if ($realcnt != 0); - - # Measure the line length and indent. - ($length, $indent) = line_stats($rawline); - - # Track the previous line. - ($prevline, $stashline) = ($stashline, $line); - ($previndent, $stashindent) = ($stashindent, $indent); - ($prevrawline, $stashrawline) = ($stashrawline, $rawline); - - #warn "line<$line>\n"; - - } elsif ($realcnt == 1) { - $realcnt--; - } - - my $hunk_line = ($realcnt != 0); - - $here = "#$linenr: " if (!$file); - $here = "#$realline: " if ($file); - - my $found_file = 0; - # extract the filename as it passes - if ($line =~ /^diff --git.*?(\S+)$/) { - $realfile = $1; - $realfile =~ s@^([^/]*)/@@ if (!$file); - $in_commit_log = 0; - $found_file = 1; - } elsif ($line =~ /^\+\+\+\s+(\S+)/) { - $realfile = $1; - $realfile =~ s@^([^/]*)/@@ if (!$file); - $in_commit_log = 0; - - $p1_prefix = $1; - if (!$file && $tree && $p1_prefix ne '' && - -e "$root/$p1_prefix") { - WARN("PATCH_PREFIX", - "patch prefix '$p1_prefix' exists, appears to be a -p0 patch\n"); - } - - if ($realfile =~ m@^include/asm/@) { - ERROR("MODIFIED_INCLUDE_ASM", - "do not modify files in include/asm, change architecture specific files in include/asm-\n" . "$here$rawline\n"); - } - $found_file = 1; - } - -#make up the handle for any error we report on this line - if ($showfile) { - $prefix = "$realfile:$realline: " - } elsif ($emacs) { - if ($file) { - $prefix = "$filename:$realline: "; - } else { - $prefix = "$filename:$linenr: "; - } - } - - if ($found_file) { - if (is_maintained_obsolete($realfile)) { - WARN("OBSOLETE", - "$realfile is marked as 'obsolete' in the MAINTAINERS hierarchy. No unnecessary modifications please.\n"); - } - if ($realfile =~ m@^(?:drivers/net/|net/|drivers/staging/)@) { - $check = 1; - } else { - $check = $check_orig; - } - $checklicenseline = 1; - - if ($realfile !~ /^MAINTAINERS/) { - my $last_binding_patch = $is_binding_patch; - - $is_binding_patch = () = $realfile =~ m@^(?:Documentation/devicetree/|include/dt-bindings/)@; - - if (($last_binding_patch != -1) && - ($last_binding_patch ^ $is_binding_patch)) { - WARN("DT_SPLIT_BINDING_PATCH", - "DT binding docs and includes should be a separate patch. See: Documentation/devicetree/bindings/submitting-patches.rst\n"); - } - } - - next; - } - - $here .= "FILE: $realfile:$realline:" if ($realcnt != 0); - - my $hereline = "$here\n$rawline\n"; - my $herecurr = "$here\n$rawline\n"; - my $hereprev = "$here\n$prevrawline\n$rawline\n"; - - $cnt_lines++ if ($realcnt != 0); - -# Verify the existence of a commit log if appropriate -# 2 is used because a $signature is counted in $commit_log_lines - if ($in_commit_log) { - if ($line !~ /^\s*$/) { - $commit_log_lines++; #could be a $signature - } - } elsif ($has_commit_log && $commit_log_lines < 2) { - WARN("COMMIT_MESSAGE", - "Missing commit description - Add an appropriate one\n"); - $commit_log_lines = 2; #warn only once - } - -# Check if the commit log has what seems like a diff which can confuse patch - if ($in_commit_log && !$commit_log_has_diff && - (($line =~ m@^\s+diff\b.*a/([\w/]+)@ && - $line =~ m@^\s+diff\b.*a/[\w/]+\s+b/$1\b@) || - $line =~ m@^\s*(?:\-\-\-\s+a/|\+\+\+\s+b/)@ || - $line =~ m/^\s*\@\@ \-\d+,\d+ \+\d+,\d+ \@\@/)) { - ERROR("DIFF_IN_COMMIT_MSG", - "Avoid using diff content in the commit message - patch(1) might not work\n" . $herecurr); - $commit_log_has_diff = 1; - } - -# Check for incorrect file permissions - if ($line =~ /^new (file )?mode.*[7531]\d{0,2}$/) { - my $permhere = $here . "FILE: $realfile\n"; - if ($realfile !~ m@scripts/@ && - $realfile !~ /\.(py|pl|awk|sh)$/) { - ERROR("EXECUTE_PERMISSIONS", - "do not set execute permissions for source files\n" . $permhere); - } - } - -# Check the patch for a From: - if (decode("MIME-Header", $line) =~ /^From:\s*(.*)/) { - $author = $1; - my $curline = $linenr; - while(defined($rawlines[$curline]) && ($rawlines[$curline++] =~ /^[ \t]\s*(.*)/)) { - $author .= $1; - } - $author = encode("utf8", $author) if ($line =~ /=\?utf-8\?/i); - $author =~ s/"//g; - $author = reformat_email($author); - } - -# Check the patch for a signoff: - if ($line =~ /^\s*signed-off-by:\s*(.*)/i) { - $signoff++; - $in_commit_log = 0; - if ($author ne '' && $authorsignoff != 1) { - if (same_email_addresses($1, $author)) { - $authorsignoff = 1; - } else { - my $ctx = $1; - my ($email_name, $email_comment, $email_address, $comment1) = parse_email($ctx); - my ($author_name, $author_comment, $author_address, $comment2) = parse_email($author); - - if (lc $email_address eq lc $author_address && $email_name eq $author_name) { - $author_sob = $ctx; - $authorsignoff = 2; - } elsif (lc $email_address eq lc $author_address) { - $author_sob = $ctx; - $authorsignoff = 3; - } elsif ($email_name eq $author_name) { - $author_sob = $ctx; - $authorsignoff = 4; - - my $address1 = $email_address; - my $address2 = $author_address; - - if ($address1 =~ /(\S+)\+\S+(\@.*)/) { - $address1 = "$1$2"; - } - if ($address2 =~ /(\S+)\+\S+(\@.*)/) { - $address2 = "$1$2"; - } - if ($address1 eq $address2) { - $authorsignoff = 5; - } - } - } - } - } - -# Check for patch separator - if ($line =~ /^---$/) { - $has_patch_separator = 1; - $in_commit_log = 0; - } - -# Check if MAINTAINERS is being updated. If so, there's probably no need to -# emit the "does MAINTAINERS need updating?" message on file add/move/delete - if ($line =~ /^\s*MAINTAINERS\s*\|/) { - $reported_maintainer_file = 1; - } - -# Check signature styles - if (!$in_header_lines && - $line =~ /^(\s*)([a-z0-9_-]+by:|$signature_tags)(\s*)(.*)/i) { - my $space_before = $1; - my $sign_off = $2; - my $space_after = $3; - my $email = $4; - my $ucfirst_sign_off = ucfirst(lc($sign_off)); - - if ($sign_off !~ /$signature_tags/) { - my $suggested_signature = find_standard_signature($sign_off); - if ($suggested_signature eq "") { - WARN("BAD_SIGN_OFF", - "Non-standard signature: $sign_off\n" . $herecurr); - } else { - if (WARN("BAD_SIGN_OFF", - "Non-standard signature: '$sign_off' - perhaps '$suggested_signature'?\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/$sign_off/$suggested_signature/; - } - } - } - if (defined $space_before && $space_before ne "") { - if (WARN("BAD_SIGN_OFF", - "Do not use whitespace before $ucfirst_sign_off\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] = - "$ucfirst_sign_off $email"; - } - } - if ($sign_off =~ /-by:$/i && $sign_off ne $ucfirst_sign_off) { - if (WARN("BAD_SIGN_OFF", - "'$ucfirst_sign_off' is the preferred signature form\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] = - "$ucfirst_sign_off $email"; - } - - } - if (!defined $space_after || $space_after ne " ") { - if (WARN("BAD_SIGN_OFF", - "Use a single space after $ucfirst_sign_off\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] = - "$ucfirst_sign_off $email"; - } - } - - my ($email_name, $name_comment, $email_address, $comment) = parse_email($email); - my $suggested_email = format_email(($email_name, $name_comment, $email_address, $comment)); - if ($suggested_email eq "") { - ERROR("BAD_SIGN_OFF", - "Unrecognized email address: '$email'\n" . $herecurr); - } else { - my $dequoted = $suggested_email; - $dequoted =~ s/^"//; - $dequoted =~ s/" 1) { - WARN("BAD_SIGN_OFF", - "Use a single name comment in email: '$email'\n" . $herecurr); - } - - - # stable@vger.kernel.org or stable@kernel.org shouldn't - # have an email name. In addition comments should strictly - # begin with a # - if ($email =~ /^.*stable\@(?:vger\.)?kernel\.org/i) { - if (($comment ne "" && $comment !~ /^#.+/) || - ($email_name ne "")) { - my $cur_name = $email_name; - my $new_comment = $comment; - $cur_name =~ s/[a-zA-Z\s\-\"]+//g; - - # Remove brackets enclosing comment text - # and # from start of comments to get comment text - $new_comment =~ s/^\((.*)\)$/$1/; - $new_comment =~ s/^\[(.*)\]$/$1/; - $new_comment =~ s/^[\s\#]+|\s+$//g; - - $new_comment = trim("$new_comment $cur_name") if ($cur_name ne $new_comment); - $new_comment = " # $new_comment" if ($new_comment ne ""); - my $new_email = "$email_address$new_comment"; - - if (WARN("BAD_STABLE_ADDRESS_STYLE", - "Invalid email format for stable: '$email', prefer '$new_email'\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/\Q$email\E/$new_email/; - } - } - } elsif ($comment ne "" && $comment !~ /^(?:#.+|\(.+\))$/) { - my $new_comment = $comment; - - # Extract comment text from within brackets or - # c89 style /*...*/ comments - $new_comment =~ s/^\[(.*)\]$/$1/; - $new_comment =~ s/^\/\*(.*)\*\/$/$1/; - - $new_comment = trim($new_comment); - $new_comment =~ s/^[^\w]$//; # Single lettered comment with non word character is usually a typo - $new_comment = "($new_comment)" if ($new_comment ne ""); - my $new_email = format_email($email_name, $name_comment, $email_address, $new_comment); - - if (WARN("BAD_SIGN_OFF", - "Unexpected content after email: '$email', should be: '$new_email'\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/\Q$email\E/$new_email/; - } - } - } - -# Check for duplicate signatures - my $sig_nospace = $line; - $sig_nospace =~ s/\s//g; - $sig_nospace = lc($sig_nospace); - if (defined $signatures{$sig_nospace}) { - WARN("BAD_SIGN_OFF", - "Duplicate signature\n" . $herecurr); - } else { - $signatures{$sig_nospace} = 1; - } - -# Check Co-developed-by: immediately followed by Signed-off-by: with same name and email - if ($sign_off =~ /^co-developed-by:$/i) { - if ($email eq $author) { - WARN("BAD_SIGN_OFF", - "Co-developed-by: should not be used to attribute nominal patch author '$author'\n" . "$here\n" . $rawline); - } - if (!defined $lines[$linenr]) { - WARN("BAD_SIGN_OFF", - "Co-developed-by: must be immediately followed by Signed-off-by:\n" . "$here\n" . $rawline); - } elsif ($rawlines[$linenr] !~ /^\s*signed-off-by:\s*(.*)/i) { - WARN("BAD_SIGN_OFF", - "Co-developed-by: must be immediately followed by Signed-off-by:\n" . "$here\n" . $rawline . "\n" .$rawlines[$linenr]); - } elsif ($1 ne $email) { - WARN("BAD_SIGN_OFF", - "Co-developed-by and Signed-off-by: name/email do not match \n" . "$here\n" . $rawline . "\n" .$rawlines[$linenr]); - } - } - } - -# Check email subject for common tools that don't need to be mentioned - if ($in_header_lines && - $line =~ /^Subject:.*\b(?:checkpatch|sparse|smatch)\b[^:]/i) { - WARN("EMAIL_SUBJECT", - "A patch subject line should describe the change not the tool that found it\n" . $herecurr); - } - -# Check for Gerrit Change-Ids not in any patch context - if ($realfile eq '' && !$has_patch_separator && $line =~ /^\s*change-id:/i) { - if (ERROR("GERRIT_CHANGE_ID", - "Remove Gerrit Change-Id's before submitting upstream\n" . $herecurr) && - $fix) { - fix_delete_line($fixlinenr, $rawline); - } - } - -# Check if the commit log is in a possible stack dump - if ($in_commit_log && !$commit_log_possible_stack_dump && - ($line =~ /^\s*(?:WARNING:|BUG:)/ || - $line =~ /^\s*\[\s*\d+\.\d{6,6}\s*\]/ || - # timestamp - $line =~ /^\s*\[\<[0-9a-fA-F]{8,}\>\]/) || - $line =~ /^(?:\s+\w+:\s+[0-9a-fA-F]+){3,3}/ || - $line =~ /^\s*\#\d+\s*\[[0-9a-fA-F]+\]\s*\w+ at [0-9a-fA-F]+/) { - # stack dump address styles - $commit_log_possible_stack_dump = 1; - } - -# Check for line lengths > 75 in commit log, warn once - if ($in_commit_log && !$commit_log_long_line && - length($line) > 75 && - !($line =~ /^\s*[a-zA-Z0-9_\/\.]+\s+\|\s+\d+/ || - # file delta changes - $line =~ /^\s*(?:[\w\.\-\+]*\/)++[\w\.\-\+]+:/ || - # filename then : - $line =~ /^\s*(?:Fixes:|Link:|$signature_tags)/i || - # A Fixes: or Link: line or signature tag line - $commit_log_possible_stack_dump)) { - WARN("COMMIT_LOG_LONG_LINE", - "Possible unwrapped commit description (prefer a maximum 75 chars per line)\n" . $herecurr); - $commit_log_long_line = 1; - } - -# Reset possible stack dump if a blank line is found - if ($in_commit_log && $commit_log_possible_stack_dump && - $line =~ /^\s*$/) { - $commit_log_possible_stack_dump = 0; - } - -# Check for lines starting with a # - if ($in_commit_log && $line =~ /^#/) { - if (WARN("COMMIT_COMMENT_SYMBOL", - "Commit log lines starting with '#' are dropped by git as comments\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/^/ /; - } - } - -# Check for git id commit length and improperly formed commit descriptions -# A correctly formed commit description is: -# commit ("Complete commit subject") -# with the commit subject '("' prefix and '")' suffix -# This is a fairly compilicated block as it tests for what appears to be -# bare SHA-1 hash with minimum length of 5. It also avoids several types of -# possible SHA-1 matches. -# A commit match can span multiple lines so this block attempts to find a -# complete typical commit on a maximum of 3 lines - if ($perl_version_ok && - $in_commit_log && !$commit_log_possible_stack_dump && - $line !~ /^\s*(?:Link|Patchwork|http|https|BugLink|base-commit):/i && - $line !~ /^This reverts commit [0-9a-f]{7,40}/ && - (($line =~ /\bcommit\s+[0-9a-f]{5,}\b/i || - ($line =~ /\bcommit\s*$/i && defined($rawlines[$linenr]) && $rawlines[$linenr] =~ /^\s*[0-9a-f]{5,}\b/i)) || - ($line =~ /(?:\s|^)[0-9a-f]{12,40}(?:[\s"'\(\[]|$)/i && - $line !~ /[\<\[][0-9a-f]{12,40}[\>\]]/i && - $line !~ /\bfixes:\s*[0-9a-f]{12,40}/i))) { - my $init_char = "c"; - my $orig_commit = ""; - my $short = 1; - my $long = 0; - my $case = 1; - my $space = 1; - my $id = '0123456789ab'; - my $orig_desc = "commit description"; - my $description = ""; - my $herectx = $herecurr; - my $has_parens = 0; - my $has_quotes = 0; - - my $input = $line; - if ($line =~ /(?:\bcommit\s+[0-9a-f]{5,}|\bcommit\s*$)/i) { - for (my $n = 0; $n < 2; $n++) { - if ($input =~ /\bcommit\s+[0-9a-f]{5,}\s*($balanced_parens)/i) { - $orig_desc = $1; - $has_parens = 1; - # Always strip leading/trailing parens then double quotes if existing - $orig_desc = substr($orig_desc, 1, -1); - if ($orig_desc =~ /^".*"$/) { - $orig_desc = substr($orig_desc, 1, -1); - $has_quotes = 1; - } - last; - } - last if ($#lines < $linenr + $n); - $input .= " " . trim($rawlines[$linenr + $n]); - $herectx .= "$rawlines[$linenr + $n]\n"; - } - $herectx = $herecurr if (!$has_parens); - } - - if ($input =~ /\b(c)ommit\s+([0-9a-f]{5,})\b/i) { - $init_char = $1; - $orig_commit = lc($2); - $short = 0 if ($input =~ /\bcommit\s+[0-9a-f]{12,40}/i); - $long = 1 if ($input =~ /\bcommit\s+[0-9a-f]{41,}/i); - $space = 0 if ($input =~ /\bcommit [0-9a-f]/i); - $case = 0 if ($input =~ /\b[Cc]ommit\s+[0-9a-f]{5,40}[^A-F]/); - } elsif ($input =~ /\b([0-9a-f]{12,40})\b/i) { - $orig_commit = lc($1); - } - - ($id, $description) = git_commit_info($orig_commit, - $id, $orig_desc); - - if (defined($id) && - ($short || $long || $space || $case || ($orig_desc ne $description) || !$has_quotes) && - $last_git_commit_id_linenr != $linenr - 1) { - ERROR("GIT_COMMIT_ID", - "Please use git commit description style 'commit <12+ chars of sha1> (\"\")' - ie: '${init_char}ommit $id (\"$description\")'\n" . $herectx); - } - #don't report the next line if this line ends in commit and the sha1 hash is the next line - $last_git_commit_id_linenr = $linenr if ($line =~ /\bcommit\s*$/i); - } - -# Check for added, moved or deleted files - if (!$SOF) { - if (!$reported_maintainer_file && !$in_commit_log && - ($line =~ /^(?:new|deleted) file mode\s*\d+\s*$/ || - $line =~ /^rename (?:from|to) [\w\/\.\-]+\s*$/ || - ($line =~ /\{\s*([\w\/\.\-]*)\s*\=\>\s*([\w\/\.\-]*)\s*\}/ && - (defined($1) || defined($2))))) { - $is_patch = 1; - $reported_maintainer_file = 1; - WARN("FILE_PATH_CHANGES", - "added, moved or deleted file(s), does MAINTAINERS need updating?\n" . $herecurr); - } - } - -# Check for adding new DT bindings not in schema format - if (!$in_commit_log && - ($line =~ /^new file mode\s*\d+\s*$/) && - ($realfile =~ m@^Documentation/devicetree/bindings/.*\.txt$@)) { - WARN("DT_SCHEMA_BINDING_PATCH", - "DT bindings should be in DT schema format. See: Documentation/devicetree/bindings/writing-schema.rst\n"); - } - -# Check for wrappage within a valid hunk of the file - if ($realcnt != 0 && $line !~ m{^(?:\+|-| |\\ No newline|$)}) { - ERROR("CORRUPTED_PATCH", - "patch seems to be corrupt (line wrapped?)\n" . - $herecurr) if (!$emitted_corrupt++); - } - -# UTF-8 regex found at http://www.w3.org/International/questions/qa-forms-utf-8.en.php - if (($realfile =~ /^$/ || $line =~ /^\+/) && - $rawline !~ m/^$UTF8*$/) { - my ($utf8_prefix) = ($rawline =~ /^($UTF8*)/); - - my $blank = copy_spacing($rawline); - my $ptr = substr($blank, 0, length($utf8_prefix)) . "^"; - my $hereptr = "$hereline$ptr\n"; - - CHK("INVALID_UTF8", - "Invalid UTF-8, patch and commit message should be encoded in UTF-8\n" . $hereptr); - } - -# Check if it's the start of a commit log -# (not a header line and we haven't seen the patch filename) - if ($in_header_lines && $realfile =~ /^$/ && - !($rawline =~ /^\s+(?:\S|$)/ || - $rawline =~ /^(?:commit\b|from\b|[\w-]+:)/i)) { - $in_header_lines = 0; - $in_commit_log = 1; - $has_commit_log = 1; - } - -# Check if there is UTF-8 in a commit log when a mail header has explicitly -# declined it, i.e defined some charset where it is missing. - if ($in_header_lines && - $rawline =~ /^Content-Type:.+charset="(.+)".*$/ && - $1 !~ /utf-8/i) { - $non_utf8_charset = 1; - } - - if ($in_commit_log && $non_utf8_charset && $realfile =~ /^$/ && - $rawline =~ /$NON_ASCII_UTF8/) { - WARN("UTF8_BEFORE_PATCH", - "8-bit UTF-8 used in possible commit log\n" . $herecurr); - } - -# Check for absolute kernel paths in commit message - if ($tree && $in_commit_log) { - while ($line =~ m{(?:^|\s)(/\S*)}g) { - my $file = $1; - - if ($file =~ m{^(.*?)(?::\d+)+:?$} && - check_absolute_file($1, $herecurr)) { - # - } else { - check_absolute_file($file, $herecurr); - } - } - } - -# Check for various typo / spelling mistakes - if (defined($misspellings) && - ($in_commit_log || $line =~ /^(?:\+|Subject:)/i)) { - while ($rawline =~ /(?:^|[^\w\-'`])($misspellings)(?:[^\w\-'`]|$)/gi) { - my $typo = $1; - my $blank = copy_spacing($rawline); - my $ptr = substr($blank, 0, $-[1]) . "^" x length($typo); - my $hereptr = "$hereline$ptr\n"; - my $typo_fix = $spelling_fix{lc($typo)}; - $typo_fix = ucfirst($typo_fix) if ($typo =~ /^[A-Z]/); - $typo_fix = uc($typo_fix) if ($typo =~ /^[A-Z]+$/); - my $msg_level = \&WARN; - $msg_level = \&CHK if ($file); - if (&{$msg_level}("TYPO_SPELLING", - "'$typo' may be misspelled - perhaps '$typo_fix'?\n" . $hereptr) && - $fix) { - $fixed[$fixlinenr] =~ s/(^|[^A-Za-z@])($typo)($|[^A-Za-z@])/$1$typo_fix$3/; - } - } - } - -# check for invalid commit id - if ($in_commit_log && $line =~ /(^fixes:|\bcommit)\s+([0-9a-f]{6,40})\b/i) { - my $id; - my $description; - ($id, $description) = git_commit_info($2, undef, undef); - if (!defined($id)) { - WARN("UNKNOWN_COMMIT_ID", - "Unknown commit id '$2', maybe rebased or not pulled?\n" . $herecurr); - } - } - -# check for repeated words separated by a single space -# avoid false positive from list command eg, '-rw-r--r-- 1 root root' - if (($rawline =~ /^\+/ || $in_commit_log) && - $rawline !~ /[bcCdDlMnpPs\?-][rwxsStT-]{9}/) { - pos($rawline) = 1 if (!$in_commit_log); - while ($rawline =~ /\b($word_pattern) (?=($word_pattern))/g) { - - my $first = $1; - my $second = $2; - my $start_pos = $-[1]; - my $end_pos = $+[2]; - if ($first =~ /(?:struct|union|enum)/) { - pos($rawline) += length($first) + length($second) + 1; - next; - } - - next if (lc($first) ne lc($second)); - next if ($first eq 'long'); - - # check for character before and after the word matches - my $start_char = ''; - my $end_char = ''; - $start_char = substr($rawline, $start_pos - 1, 1) if ($start_pos > ($in_commit_log ? 0 : 1)); - $end_char = substr($rawline, $end_pos, 1) if ($end_pos < length($rawline)); - - next if ($start_char =~ /^\S$/); - next if (index(" \t.,;?!", $end_char) == -1); - - # avoid repeating hex occurrences like 'ff ff fe 09 ...' - if ($first =~ /\b[0-9a-f]{2,}\b/i) { - next if (!exists($allow_repeated_words{lc($first)})); - } - - if (WARN("REPEATED_WORD", - "Possible repeated word: '$first'\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/\b$first $second\b/$first/; - } - } - - # if it's a repeated word on consecutive lines in a comment block - if ($prevline =~ /$;+\s*$/ && - $prevrawline =~ /($word_pattern)\s*$/) { - my $last_word = $1; - if ($rawline =~ /^\+\s*\*\s*$last_word /) { - if (WARN("REPEATED_WORD", - "Possible repeated word: '$last_word'\n" . $hereprev) && - $fix) { - $fixed[$fixlinenr] =~ s/(\+\s*\*\s*)$last_word /$1/; - } - } - } - } - -# ignore non-hunk lines and lines being removed - next if (!$hunk_line || $line =~ /^-/); - -#trailing whitespace - if ($line =~ /^\+.*\015/) { - my $herevet = "$here\n" . cat_vet($rawline) . "\n"; - if (ERROR("DOS_LINE_ENDINGS", - "DOS line endings\n" . $herevet) && - $fix) { - $fixed[$fixlinenr] =~ s/[\s\015]+$//; - } - } elsif ($rawline =~ /^\+.*\S\s+$/ || $rawline =~ /^\+\s+$/) { - my $herevet = "$here\n" . cat_vet($rawline) . "\n"; - if (ERROR("TRAILING_WHITESPACE", - "trailing whitespace\n" . $herevet) && - $fix) { - $fixed[$fixlinenr] =~ s/\s+$//; - } - - $rpt_cleaners = 1; - } - -# Check for FSF mailing addresses. - if ($rawline =~ /\bwrite to the Free/i || - $rawline =~ /\b675\s+Mass\s+Ave/i || - $rawline =~ /\b59\s+Temple\s+Pl/i || - $rawline =~ /\b51\s+Franklin\s+St/i) { - my $herevet = "$here\n" . cat_vet($rawline) . "\n"; - my $msg_level = \&ERROR; - $msg_level = \&CHK if ($file); - &{$msg_level}("FSF_MAILING_ADDRESS", - "Do not include the paragraph about writing to the Free Software Foundation's mailing address from the sample GPL notice. The FSF has changed addresses in the past, and may do so again. Linux already includes a copy of the GPL.\n" . $herevet) - } - -# check for Kconfig help text having a real description -# Only applies when adding the entry originally, after that we do not have -# sufficient context to determine whether it is indeed long enough. - if ($realfile =~ /Kconfig/ && - # 'choice' is usually the last thing on the line (though - # Kconfig supports named choices), so use a word boundary - # (\b) rather than a whitespace character (\s) - $line =~ /^\+\s*(?:config|menuconfig|choice)\b/) { - my $ln = $linenr; - my $needs_help = 0; - my $has_help = 0; - my $help_length = 0; - while (defined $lines[$ln]) { - my $f = $lines[$ln++]; - - next if ($f =~ /^-/); - last if ($f !~ /^[\+ ]/); # !patch context - - if ($f =~ /^\+\s*(?:bool|tristate|prompt)\s*["']/) { - $needs_help = 1; - next; - } - if ($f =~ /^\+\s*help\s*$/) { - $has_help = 1; - next; - } - - $f =~ s/^.//; # strip patch context [+ ] - $f =~ s/#.*//; # strip # directives - $f =~ s/^\s+//; # strip leading blanks - next if ($f =~ /^$/); # skip blank lines - - # At the end of this Kconfig block: - # This only checks context lines in the patch - # and so hopefully shouldn't trigger false - # positives, even though some of these are - # common words in help texts - if ($f =~ /^(?:config|menuconfig|choice|endchoice| - if|endif|menu|endmenu|source)\b/x) { - last; - } - $help_length++ if ($has_help); - } - if ($needs_help && - $help_length < $min_conf_desc_length) { - my $stat_real = get_stat_real($linenr, $ln - 1); - WARN("CONFIG_DESCRIPTION", - "please write a help paragraph that fully describes the config symbol\n" . "$here\n$stat_real\n"); - } - } - -# check MAINTAINERS entries - if ($realfile =~ /^MAINTAINERS$/) { -# check MAINTAINERS entries for the right form - if ($rawline =~ /^\+[A-Z]:/ && - $rawline !~ /^\+[A-Z]:\t\S/) { - if (WARN("MAINTAINERS_STYLE", - "MAINTAINERS entries use one tab after TYPE:\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/^(\+[A-Z]):\s*/$1:\t/; - } - } -# check MAINTAINERS entries for the right ordering too - my $preferred_order = 'MRLSWQBCPTFXNK'; - if ($rawline =~ /^\+[A-Z]:/ && - $prevrawline =~ /^[\+ ][A-Z]:/) { - $rawline =~ /^\+([A-Z]):\s*(.*)/; - my $cur = $1; - my $curval = $2; - $prevrawline =~ /^[\+ ]([A-Z]):\s*(.*)/; - my $prev = $1; - my $prevval = $2; - my $curindex = index($preferred_order, $cur); - my $previndex = index($preferred_order, $prev); - if ($curindex < 0) { - WARN("MAINTAINERS_STYLE", - "Unknown MAINTAINERS entry type: '$cur'\n" . $herecurr); - } else { - if ($previndex >= 0 && $curindex < $previndex) { - WARN("MAINTAINERS_STYLE", - "Misordered MAINTAINERS entry - list '$cur:' before '$prev:'\n" . $hereprev); - } elsif ((($prev eq 'F' && $cur eq 'F') || - ($prev eq 'X' && $cur eq 'X')) && - ($prevval cmp $curval) > 0) { - WARN("MAINTAINERS_STYLE", - "Misordered MAINTAINERS entry - list file patterns in alphabetic order\n" . $hereprev); - } - } - } - } - - if (($realfile =~ /Makefile.*/ || $realfile =~ /Kbuild.*/) && - ($line =~ /\+(EXTRA_[A-Z]+FLAGS).*/)) { - my $flag = $1; - my $replacement = { - 'EXTRA_AFLAGS' => 'asflags-y', - 'EXTRA_CFLAGS' => 'ccflags-y', - 'EXTRA_CPPFLAGS' => 'cppflags-y', - 'EXTRA_LDFLAGS' => 'ldflags-y', - }; - - WARN("DEPRECATED_VARIABLE", - "Use of $flag is deprecated, please use \`$replacement->{$flag} instead.\n" . $herecurr) if ($replacement->{$flag}); - } - -# check for DT compatible documentation - if (defined $root && - (($realfile =~ /\.dtsi?$/ && $line =~ /^\+\s*compatible\s*=\s*\"/) || - ($realfile =~ /\.[ch]$/ && $line =~ /^\+.*\.compatible\s*=\s*\"/))) { - - my @compats = $rawline =~ /\"([a-zA-Z0-9\-\,\.\+_]+)\"/g; - - my $dt_path = $root . "/Documentation/devicetree/bindings/"; - my $vp_file = $dt_path . "vendor-prefixes.yaml"; - - foreach my $compat (@compats) { - my $compat2 = $compat; - $compat2 =~ s/\,[a-zA-Z0-9]*\-/\,<\.\*>\-/; - my $compat3 = $compat; - $compat3 =~ s/\,([a-z]*)[0-9]*\-/\,$1<\.\*>\-/; - `grep -Erq "$compat|$compat2|$compat3" $dt_path`; - if ( $? >> 8 ) { - WARN("UNDOCUMENTED_DT_STRING", - "DT compatible string \"$compat\" appears un-documented -- check $dt_path\n" . $herecurr); - } - - next if $compat !~ /^([a-zA-Z0-9\-]+)\,/; - my $vendor = $1; - `grep -Eq "\\"\\^\Q$vendor\E,\\.\\*\\":" $vp_file`; - if ( $? >> 8 ) { - WARN("UNDOCUMENTED_DT_STRING", - "DT compatible string vendor \"$vendor\" appears un-documented -- check $vp_file\n" . $herecurr); - } - } - } - -# check for using SPDX license tag at beginning of files - if ($realline == $checklicenseline) { - if ($rawline =~ /^[ \+]\s*\#\!\s*\//) { - $checklicenseline = 2; - } elsif ($rawline =~ /^\+/) { - my $comment = ""; - if ($realfile =~ /\.(h|s|S)$/) { - $comment = '/*'; - } elsif ($realfile =~ /\.(c|dts|dtsi)$/) { - $comment = '//'; - } elsif (($checklicenseline == 2) || $realfile =~ /\.(sh|pl|py|awk|tc|yaml)$/) { - $comment = '#'; - } elsif ($realfile =~ /\.rst$/) { - $comment = '..'; - } - -# check SPDX comment style for .[chsS] files - if ($realfile =~ /\.[chsS]$/ && - $rawline =~ /SPDX-License-Identifier:/ && - $rawline !~ m@^\+\s*\Q$comment\E\s*@) { - WARN("SPDX_LICENSE_TAG", - "Improper SPDX comment style for '$realfile', please use '$comment' instead\n" . $herecurr); - } - - if ($comment !~ /^$/ && - $rawline !~ m@^\+\Q$comment\E SPDX-License-Identifier: @) { - WARN("SPDX_LICENSE_TAG", - "Missing or malformed SPDX-License-Identifier tag in line $checklicenseline\n" . $herecurr); - } elsif ($rawline =~ /(SPDX-License-Identifier: .*)/) { - my $spdx_license = $1; - if (!is_SPDX_License_valid($spdx_license)) { - WARN("SPDX_LICENSE_TAG", - "'$spdx_license' is not supported in LICENSES/...\n" . $herecurr); - } - if ($realfile =~ m@^Documentation/devicetree/bindings/@ && - not $spdx_license =~ /GPL-2\.0.*BSD-2-Clause/) { - my $msg_level = \&WARN; - $msg_level = \&CHK if ($file); - if (&{$msg_level}("SPDX_LICENSE_TAG", - - "DT binding documents should be licensed (GPL-2.0-only OR BSD-2-Clause)\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/SPDX-License-Identifier: .*/SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)/; - } - } - } - } - } - -# check for embedded filenames - if ($rawline =~ /^\+.*\Q$realfile\E/) { - WARN("EMBEDDED_FILENAME", - "It's generally not useful to have the filename in the file\n" . $herecurr); - } - -# check we are in a valid source file if not then ignore this hunk - next if ($realfile !~ /\.(h|c|s|S|sh|dtsi|dts)$/); - -# check for using SPDX-License-Identifier on the wrong line number - if ($realline != $checklicenseline && - $rawline =~ /\bSPDX-License-Identifier:/ && - substr($line, @-, @+ - @-) eq "$;" x (@+ - @-)) { - WARN("SPDX_LICENSE_TAG", - "Misplaced SPDX-License-Identifier tag - use line $checklicenseline instead\n" . $herecurr); - } - -# line length limit (with some exclusions) -# -# There are a few types of lines that may extend beyond $max_line_length: -# logging functions like pr_info that end in a string -# lines with a single string -# #defines that are a single string -# lines with an RFC3986 like URL -# -# There are 3 different line length message types: -# LONG_LINE_COMMENT a comment starts before but extends beyond $max_line_length -# LONG_LINE_STRING a string starts before but extends beyond $max_line_length -# LONG_LINE all other lines longer than $max_line_length -# -# if LONG_LINE is ignored, the other 2 types are also ignored -# - - if ($line =~ /^\+/ && $length > $max_line_length) { - my $msg_type = "LONG_LINE"; - - # Check the allowed long line types first - - # logging functions that end in a string that starts - # before $max_line_length - if ($line =~ /^\+\s*$logFunctions\s*\(\s*(?:(?:KERN_\S+\s*|[^"]*))?($String\s*(?:|,|\)\s*;)\s*)$/ && - length(expand_tabs(substr($line, 1, length($line) - length($1) - 1))) <= $max_line_length) { - $msg_type = ""; - - # lines with only strings (w/ possible termination) - # #defines with only strings - } elsif ($line =~ /^\+\s*$String\s*(?:\s*|,|\)\s*;)\s*$/ || - $line =~ /^\+\s*#\s*define\s+\w+\s+$String$/) { - $msg_type = ""; - - # More special cases - } elsif ($line =~ /^\+.*\bEFI_GUID\s*\(/ || - $line =~ /^\+\s*(?:\w+)?\s*DEFINE_PER_CPU/) { - $msg_type = ""; - - # URL ($rawline is used in case the URL is in a comment) - } elsif ($rawline =~ /^\+.*\b[a-z][\w\.\+\-]*:\/\/\S+/i) { - $msg_type = ""; - - # Otherwise set the alternate message types - - # a comment starts before $max_line_length - } elsif ($line =~ /($;[\s$;]*)$/ && - length(expand_tabs(substr($line, 1, length($line) - length($1) - 1))) <= $max_line_length) { - $msg_type = "LONG_LINE_COMMENT" - - # a quoted string starts before $max_line_length - } elsif ($sline =~ /\s*($String(?:\s*(?:\\|,\s*|\)\s*;\s*))?)$/ && - length(expand_tabs(substr($line, 1, length($line) - length($1) - 1))) <= $max_line_length) { - $msg_type = "LONG_LINE_STRING" - } - - if ($msg_type ne "" && - (show_type("LONG_LINE") || show_type($msg_type))) { - my $msg_level = \&WARN; - $msg_level = \&CHK if ($file); - &{$msg_level}($msg_type, - "line length of $length exceeds $max_line_length columns\n" . $herecurr); - } - } - -# check for adding lines without a newline. - if ($line =~ /^\+/ && defined $lines[$linenr] && $lines[$linenr] =~ /^\\ No newline at end of file/) { - if (WARN("MISSING_EOF_NEWLINE", - "adding a line without newline at end of file\n" . $herecurr) && - $fix) { - fix_delete_line($fixlinenr+1, "No newline at end of file"); - } - } - -# check for .L prefix local symbols in .S files - if ($realfile =~ /\.S$/ && - $line =~ /^\+\s*(?:[A-Z]+_)?SYM_[A-Z]+_(?:START|END)(?:_[A-Z_]+)?\s*\(\s*\.L/) { - WARN("AVOID_L_PREFIX", - "Avoid using '.L' prefixed local symbol names for denoting a range of code via 'SYM_*_START/END' annotations; see Documentation/asm-annotations.rst\n" . $herecurr); - } - -# check we are in a valid source file C or perl if not then ignore this hunk - next if ($realfile !~ /\.(h|c|pl|dtsi|dts)$/); - -# at the beginning of a line any tabs must come first and anything -# more than $tabsize must use tabs. - if ($rawline =~ /^\+\s* \t\s*\S/ || - $rawline =~ /^\+\s* \s*/) { - my $herevet = "$here\n" . cat_vet($rawline) . "\n"; - $rpt_cleaners = 1; - if (ERROR("CODE_INDENT", - "code indent should use tabs where possible\n" . $herevet) && - $fix) { - $fixed[$fixlinenr] =~ s/^\+([ \t]+)/"\+" . tabify($1)/e; - } - } - -# check for space before tabs. - if ($rawline =~ /^\+/ && $rawline =~ / \t/) { - my $herevet = "$here\n" . cat_vet($rawline) . "\n"; - if (WARN("SPACE_BEFORE_TAB", - "please, no space before tabs\n" . $herevet) && - $fix) { - while ($fixed[$fixlinenr] =~ - s/(^\+.*) {$tabsize,$tabsize}\t/$1\t\t/) {} - while ($fixed[$fixlinenr] =~ - s/(^\+.*) +\t/$1\t/) {} - } - } - -# check for assignments on the start of a line - if ($sline =~ /^\+\s+($Assignment)[^=]/) { - my $operator = $1; - if (CHK("ASSIGNMENT_CONTINUATIONS", - "Assignment operator '$1' should be on the previous line\n" . $hereprev) && - $fix && $prevrawline =~ /^\+/) { - # add assignment operator to the previous line, remove from current line - $fixed[$fixlinenr - 1] .= " $operator"; - $fixed[$fixlinenr] =~ s/\Q$operator\E\s*//; - } - } - -# check for && or || at the start of a line - if ($rawline =~ /^\+\s*(&&|\|\|)/) { - my $operator = $1; - if (CHK("LOGICAL_CONTINUATIONS", - "Logical continuations should be on the previous line\n" . $hereprev) && - $fix && $prevrawline =~ /^\+/) { - # insert logical operator at last non-comment, non-whitepsace char on previous line - $prevline =~ /[\s$;]*$/; - my $line_end = substr($prevrawline, $-[0]); - $fixed[$fixlinenr - 1] =~ s/\Q$line_end\E$/ $operator$line_end/; - $fixed[$fixlinenr] =~ s/\Q$operator\E\s*//; - } - } - -# check indentation starts on a tab stop - if ($perl_version_ok && - $sline =~ /^\+\t+( +)(?:$c90_Keywords\b|\{\s*$|\}\s*(?:else\b|while\b|\s*$)|$Declare\s*$Ident\s*[;=])/) { - my $indent = length($1); - if ($indent % $tabsize) { - if (WARN("TABSTOP", - "Statements should start on a tabstop\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s@(^\+\t+) +@$1 . "\t" x ($indent/$tabsize)@e; - } - } - } - -# check multi-line statement indentation matches previous line - if ($perl_version_ok && - $prevline =~ /^\+([ \t]*)((?:$c90_Keywords(?:\s+if)\s*)|(?:$Declare\s*)?(?:$Ident|\(\s*\*\s*$Ident\s*\))\s*|(?:\*\s*)*$Lval\s*=\s*$Ident\s*)\(.*(\&\&|\|\||,)\s*$/) { - $prevline =~ /^\+(\t*)(.*)$/; - my $oldindent = $1; - my $rest = $2; - - my $pos = pos_last_openparen($rest); - if ($pos >= 0) { - $line =~ /^(\+| )([ \t]*)/; - my $newindent = $2; - - my $goodtabindent = $oldindent . - "\t" x ($pos / $tabsize) . - " " x ($pos % $tabsize); - my $goodspaceindent = $oldindent . " " x $pos; - - if ($newindent ne $goodtabindent && - $newindent ne $goodspaceindent) { - - if (CHK("PARENTHESIS_ALIGNMENT", - "Alignment should match open parenthesis\n" . $hereprev) && - $fix && $line =~ /^\+/) { - $fixed[$fixlinenr] =~ - s/^\+[ \t]*/\+$goodtabindent/; - } - } - } - } - -# check for space after cast like "(int) foo" or "(struct foo) bar" -# avoid checking a few false positives: -# "sizeof(<type>)" or "__alignof__(<type>)" -# function pointer declarations like "(*foo)(int) = bar;" -# structure definitions like "(struct foo) { 0 };" -# multiline macros that define functions -# known attributes or the __attribute__ keyword - if ($line =~ /^\+(.*)\(\s*$Type\s*\)([ \t]++)((?![={]|\\$|$Attribute|__attribute__))/ && - (!defined($1) || $1 !~ /\b(?:sizeof|__alignof__)\s*$/)) { - if (CHK("SPACING", - "No space is necessary after a cast\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ - s/(\(\s*$Type\s*\))[ \t]+/$1/; - } - } - -# Block comment styles -# Networking with an initial /* - if ($realfile =~ m@^(drivers/net/|net/)@ && - $prevrawline =~ /^\+[ \t]*\/\*[ \t]*$/ && - $rawline =~ /^\+[ \t]*\*/ && - $realline > 3) { # Do not warn about the initial copyright comment block after SPDX-License-Identifier - WARN("NETWORKING_BLOCK_COMMENT_STYLE", - "networking block comments don't use an empty /* line, use /* Comment...\n" . $hereprev); - } - -# UAPI ABI version - if ($SOF && $realfile ne $last_abi_file && - $realfile =~ m@^(src/include/ipc/|src/include/kernel/|src/include/user/)@ && - $rawline =~ /^\+/ && - !$reported_abi_update) { - $last_abi_file = $realfile; - WARN("ABI update ??", - "Please update ABI in accordance with http://semver.org\n" . $hereprev); - } - -# Block comments use * on subsequent lines - if ($prevline =~ /$;[ \t]*$/ && #ends in comment - $prevrawline =~ /^\+.*?\/\*/ && #starting /* - $prevrawline !~ /\*\/[ \t]*$/ && #no trailing */ - $rawline =~ /^\+/ && #line is new - $rawline !~ /^\+[ \t]*\*/) { #no leading * - WARN("BLOCK_COMMENT_STYLE", - "Block comments use * on subsequent lines\n" . $hereprev); - } - -# Block comments use */ on trailing lines - if ($rawline !~ m@^\+[ \t]*\*/[ \t]*$@ && #trailing */ - $rawline !~ m@^\+.*/\*.*\*/[ \t]*$@ && #inline /*...*/ - $rawline !~ m@^\+.*\*{2,}/[ \t]*$@ && #trailing **/ - $rawline =~ m@^\+[ \t]*.+\*\/[ \t]*$@) { #non blank */ - WARN("BLOCK_COMMENT_STYLE", - "Block comments use a trailing */ on a separate line\n" . $herecurr); - } - -# Block comment * alignment - if ($prevline =~ /$;[ \t]*$/ && #ends in comment - $line =~ /^\+[ \t]*$;/ && #leading comment - $rawline =~ /^\+[ \t]*\*/ && #leading * - (($prevrawline =~ /^\+.*?\/\*/ && #leading /* - $prevrawline !~ /\*\/[ \t]*$/) || #no trailing */ - $prevrawline =~ /^\+[ \t]*\*/)) { #leading * - my $oldindent; - $prevrawline =~ m@^\+([ \t]*/?)\*@; - if (defined($1)) { - $oldindent = expand_tabs($1); - } else { - $prevrawline =~ m@^\+(.*/?)\*@; - $oldindent = expand_tabs($1); - } - $rawline =~ m@^\+([ \t]*)\*@; - my $newindent = $1; - $newindent = expand_tabs($newindent); - if (length($oldindent) ne length($newindent)) { - WARN("BLOCK_COMMENT_STYLE", - "Block comments should align the * on each line\n" . $hereprev); - } - } - -# check for missing blank lines after struct/union declarations -# with exceptions for various attributes and macros - if ($prevline =~ /^[\+ ]};?\s*$/ && - $line =~ /^\+/ && - !($line =~ /^\+\s*$/ || - $line =~ /^\+\s*EXPORT_SYMBOL/ || - $line =~ /^\+\s*MODULE_/i || - $line =~ /^\+\s*\#\s*(?:end|elif|else)/ || - $line =~ /^\+[a-z_]*init/ || - $line =~ /^\+\s*(?:static\s+)?[A-Z_]*ATTR/ || - $line =~ /^\+\s*DECLARE/ || - $line =~ /^\+\s*builtin_[\w_]*driver/ || - $line =~ /^\+\s*__setup/)) { - if (CHK("LINE_SPACING", - "Please use a blank line after function/struct/union/enum declarations\n" . $hereprev) && - $fix) { - fix_insert_line($fixlinenr, "\+"); - } - } - -# check for multiple consecutive blank lines - if ($prevline =~ /^[\+ ]\s*$/ && - $line =~ /^\+\s*$/ && - $last_blank_line != ($linenr - 1)) { - if (CHK("LINE_SPACING", - "Please don't use multiple blank lines\n" . $hereprev) && - $fix) { - fix_delete_line($fixlinenr, $rawline); - } - - $last_blank_line = $linenr; - } - -# check for missing blank lines after declarations -# (declarations must have the same indentation and not be at the start of line) - if (($prevline =~ /\+(\s+)\S/) && $sline =~ /^\+$1\S/) { - # use temporaries - my $sl = $sline; - my $pl = $prevline; - # remove $Attribute/$Sparse uses to simplify comparisons - $sl =~ s/\b(?:$Attribute|$Sparse)\b//g; - $pl =~ s/\b(?:$Attribute|$Sparse)\b//g; - if (($pl =~ /^\+\s+$Declare\s*$Ident\s*[=,;:\[]/ || - # function pointer declarations - $pl =~ /^\+\s+$Declare\s*\(\s*\*\s*$Ident\s*\)\s*[=,;:\[\(]/ || - # foo bar; where foo is some local typedef or #define - $pl =~ /^\+\s+$Ident(?:\s+|\s*\*\s*)$Ident\s*[=,;\[]/ || - # known declaration macros - $pl =~ /^\+\s+$declaration_macros/) && - # for "else if" which can look like "$Ident $Ident" - !($pl =~ /^\+\s+$c90_Keywords\b/ || - # other possible extensions of declaration lines - $pl =~ /(?:$Compare|$Assignment|$Operators)\s*$/ || - # not starting a section or a macro "\" extended line - $pl =~ /(?:\{\s*|\\)$/) && - # looks like a declaration - !($sl =~ /^\+\s+$Declare\s*$Ident\s*[=,;:\[]/ || - # function pointer declarations - $sl =~ /^\+\s+$Declare\s*\(\s*\*\s*$Ident\s*\)\s*[=,;:\[\(]/ || - # foo bar; where foo is some local typedef or #define - $sl =~ /^\+\s+$Ident(?:\s+|\s*\*\s*)$Ident\s*[=,;\[]/ || - # known declaration macros - $sl =~ /^\+\s+$declaration_macros/ || - # start of struct or union or enum - $sl =~ /^\+\s+(?:static\s+)?(?:const\s+)?(?:union|struct|enum|typedef)\b/ || - # start or end of block or continuation of declaration - $sl =~ /^\+\s+(?:$|[\{\}\.\#\"\?\:\(\[])/ || - # bitfield continuation - $sl =~ /^\+\s+$Ident\s*:\s*\d+\s*[,;]/ || - # other possible extensions of declaration lines - $sl =~ /^\+\s+\(?\s*(?:$Compare|$Assignment|$Operators)/)) { - if (WARN("LINE_SPACING", - "Missing a blank line after declarations\n" . $hereprev) && - $fix) { - fix_insert_line($fixlinenr, "\+"); - } - } - } - -# check for spaces at the beginning of a line. -# Exceptions: -# 1) within comments -# 2) indented preprocessor commands -# 3) hanging labels - if ($rawline =~ /^\+ / && $line !~ /^\+ *(?:$;|#|$Ident:)/) { - my $herevet = "$here\n" . cat_vet($rawline) . "\n"; - if (WARN("LEADING_SPACE", - "please, no spaces at the start of a line\n" . $herevet) && - $fix) { - $fixed[$fixlinenr] =~ s/^\+([ \t]+)/"\+" . tabify($1)/e; - } - } - -# check we are in a valid C source file if not then ignore this hunk - next if ($realfile !~ /\.(h|c)$/); - -# check for unusual line ending [ or ( - if ($line =~ /^\+.*([\[\(])\s*$/) { - CHK("OPEN_ENDED_LINE", - "Lines should not end with a '$1'\n" . $herecurr); - } - -# check if this appears to be the start function declaration, save the name - if ($sline =~ /^\+\{\s*$/ && - $prevline =~ /^\+(?:(?:(?:$Storage|$Inline)\s*)*\s*$Type\s*)?($Ident)\(/) { - $context_function = $1; - } - -# check if this appears to be the end of function declaration - if ($sline =~ /^\+\}\s*$/) { - undef $context_function; - } - -# check indentation of any line with a bare else -# (but not if it is a multiple line "if (foo) return bar; else return baz;") -# if the previous line is a break or return and is indented 1 tab more... - if ($sline =~ /^\+([\t]+)(?:}[ \t]*)?else(?:[ \t]*{)?\s*$/) { - my $tabs = length($1) + 1; - if ($prevline =~ /^\+\t{$tabs,$tabs}break\b/ || - ($prevline =~ /^\+\t{$tabs,$tabs}return\b/ && - defined $lines[$linenr] && - $lines[$linenr] !~ /^[ \+]\t{$tabs,$tabs}return/)) { - WARN("UNNECESSARY_ELSE", - "else is not generally useful after a break or return\n" . $hereprev); - } - } - -# check indentation of a line with a break; -# if the previous line is a goto, return or break -# and is indented the same # of tabs - if ($sline =~ /^\+([\t]+)break\s*;\s*$/) { - my $tabs = $1; - if ($prevline =~ /^\+$tabs(goto|return|break)\b/) { - if (WARN("UNNECESSARY_BREAK", - "break is not useful after a $1\n" . $hereprev) && - $fix) { - fix_delete_line($fixlinenr, $rawline); - } - } - } - -# check for RCS/CVS revision markers - if ($rawline =~ /^\+.*\$(Revision|Log|Id)(?:\$|)/) { - WARN("CVS_KEYWORD", - "CVS style keyword markers, these will _not_ be updated\n". $herecurr); - } - -# check for old HOTPLUG __dev<foo> section markings - if ($line =~ /\b(__dev(init|exit)(data|const|))\b/) { - WARN("HOTPLUG_SECTION", - "Using $1 is unnecessary\n" . $herecurr); - } - -# Check for potential 'bare' types - my ($stat, $cond, $line_nr_next, $remain_next, $off_next, - $realline_next); -#print "LINE<$line>\n"; - if ($linenr > $suppress_statement && - $realcnt && $sline =~ /.\s*\S/) { - ($stat, $cond, $line_nr_next, $remain_next, $off_next) = - ctx_statement_block($linenr, $realcnt, 0); - $stat =~ s/\n./\n /g; - $cond =~ s/\n./\n /g; - -#print "linenr<$linenr> <$stat>\n"; - # If this statement has no statement boundaries within - # it there is no point in retrying a statement scan - # until we hit end of it. - my $frag = $stat; $frag =~ s/;+\s*$//; - if ($frag !~ /(?:{|;)/) { -#print "skip<$line_nr_next>\n"; - $suppress_statement = $line_nr_next; - } - - # Find the real next line. - $realline_next = $line_nr_next; - if (defined $realline_next && - (!defined $lines[$realline_next - 1] || - substr($lines[$realline_next - 1], $off_next) =~ /^\s*$/)) { - $realline_next++; - } - - my $s = $stat; - $s =~ s/{.*$//s; - - # Ignore goto labels. - if ($s =~ /$Ident:\*$/s) { - - # Ignore functions being called - } elsif ($s =~ /^.\s*$Ident\s*\(/s) { - - } elsif ($s =~ /^.\s*else\b/s) { - - # declarations always start with types - } elsif ($prev_values eq 'E' && $s =~ /^.\s*(?:$Storage\s+)?(?:$Inline\s+)?(?:const\s+)?((?:\s*$Ident)+?)\b(?:\s+$Sparse)?\s*\**\s*(?:$Ident|\(\*[^\)]*\))(?:\s*$Modifier)?\s*(?:;|=|,|\()/s) { - my $type = $1; - $type =~ s/\s+/ /g; - possible($type, "A:" . $s); - - # definitions in global scope can only start with types - } elsif ($s =~ /^.(?:$Storage\s+)?(?:$Inline\s+)?(?:const\s+)?($Ident)\b\s*(?!:)/s) { - possible($1, "B:" . $s); - } - - # any (foo ... *) is a pointer cast, and foo is a type - while ($s =~ /\(($Ident)(?:\s+$Sparse)*[\s\*]+\s*\)/sg) { - possible($1, "C:" . $s); - } - - # Check for any sort of function declaration. - # int foo(something bar, other baz); - # void (*store_gdt)(x86_descr_ptr *); - if ($prev_values eq 'E' && $s =~ /^(.(?:typedef\s*)?(?:(?:$Storage|$Inline)\s*)*\s*$Type\s*(?:\b$Ident|\(\*\s*$Ident\))\s*)\(/s) { - my ($name_len) = length($1); - - my $ctx = $s; - substr($ctx, 0, $name_len + 1, ''); - $ctx =~ s/\)[^\)]*$//; - - for my $arg (split(/\s*,\s*/, $ctx)) { - if ($arg =~ /^(?:const\s+)?($Ident)(?:\s+$Sparse)*\s*\**\s*(:?\b$Ident)?$/s || $arg =~ /^($Ident)$/s) { - - possible($1, "D:" . $s); - } - } - } - - } - -# -# Checks which may be anchored in the context. -# - -# Check for switch () and associated case and default -# statements should be at the same indent. - if ($line=~/\bswitch\s*\(.*\)/) { - my $err = ''; - my $sep = ''; - my @ctx = ctx_block_outer($linenr, $realcnt); - shift(@ctx); - for my $ctx (@ctx) { - my ($clen, $cindent) = line_stats($ctx); - if ($ctx =~ /^\+\s*(case\s+|default:)/ && - $indent != $cindent) { - $err .= "$sep$ctx\n"; - $sep = ''; - } else { - $sep = "[...]\n"; - } - } - if ($err ne '') { - ERROR("SWITCH_CASE_INDENT_LEVEL", - "switch and case should be at the same indent\n$hereline$err"); - } - } - -# if/while/etc brace do not go on next line, unless defining a do while loop, -# or if that brace on the next line is for something else - if ($line =~ /(.*)\b((?:if|while|for|switch|(?:[a-z_]+|)for_each[a-z_]+)\s*\(|do\b|else\b)/ && $line !~ /^.\s*\#/) { - my $pre_ctx = "$1$2"; - - my ($level, @ctx) = ctx_statement_level($linenr, $realcnt, 0); - - if ($line =~ /^\+\t{6,}/) { - WARN("DEEP_INDENTATION", - "Too many leading tabs - consider code refactoring\n" . $herecurr); - } - - my $ctx_cnt = $realcnt - $#ctx - 1; - my $ctx = join("\n", @ctx); - - my $ctx_ln = $linenr; - my $ctx_skip = $realcnt; - - while ($ctx_skip > $ctx_cnt || ($ctx_skip == $ctx_cnt && - defined $lines[$ctx_ln - 1] && - $lines[$ctx_ln - 1] =~ /^-/)) { - ##print "SKIP<$ctx_skip> CNT<$ctx_cnt>\n"; - $ctx_skip-- if (!defined $lines[$ctx_ln - 1] || $lines[$ctx_ln - 1] !~ /^-/); - $ctx_ln++; - } - - #print "realcnt<$realcnt> ctx_cnt<$ctx_cnt>\n"; - #print "pre<$pre_ctx>\nline<$line>\nctx<$ctx>\nnext<$lines[$ctx_ln - 1]>\n"; - - if ($ctx !~ /{\s*/ && defined($lines[$ctx_ln - 1]) && $lines[$ctx_ln - 1] =~ /^\+\s*{/) { - ERROR("OPEN_BRACE", - "that open brace { should be on the previous line\n" . - "$here\n$ctx\n$rawlines[$ctx_ln - 1]\n"); - } - if ($level == 0 && $pre_ctx !~ /}\s*while\s*\($/ && - $ctx =~ /\)\s*\;\s*$/ && - defined $lines[$ctx_ln - 1]) - { - my ($nlength, $nindent) = line_stats($lines[$ctx_ln - 1]); - if ($nindent > $indent) { - WARN("TRAILING_SEMICOLON", - "trailing semicolon indicates no statements, indent implies otherwise\n" . - "$here\n$ctx\n$rawlines[$ctx_ln - 1]\n"); - } - } - } - -# Check relative indent for conditionals and blocks. - if ($line =~ /\b(?:(?:if|while|for|(?:[a-z_]+|)for_each[a-z_]+)\s*\(|(?:do|else)\b)/ && $line !~ /^.\s*#/ && $line !~ /\}\s*while\s*/) { - ($stat, $cond, $line_nr_next, $remain_next, $off_next) = - ctx_statement_block($linenr, $realcnt, 0) - if (!defined $stat); - my ($s, $c) = ($stat, $cond); - - substr($s, 0, length($c), ''); - - # remove inline comments - $s =~ s/$;/ /g; - $c =~ s/$;/ /g; - - # Find out how long the conditional actually is. - my @newlines = ($c =~ /\n/gs); - my $cond_lines = 1 + $#newlines; - - # Make sure we remove the line prefixes as we have - # none on the first line, and are going to readd them - # where necessary. - $s =~ s/\n./\n/gs; - while ($s =~ /\n\s+\\\n/) { - $cond_lines += $s =~ s/\n\s+\\\n/\n/g; - } - - # We want to check the first line inside the block - # starting at the end of the conditional, so remove: - # 1) any blank line termination - # 2) any opening brace { on end of the line - # 3) any do (...) { - my $continuation = 0; - my $check = 0; - $s =~ s/^.*\bdo\b//; - $s =~ s/^\s*{//; - if ($s =~ s/^\s*\\//) { - $continuation = 1; - } - if ($s =~ s/^\s*?\n//) { - $check = 1; - $cond_lines++; - } - - # Also ignore a loop construct at the end of a - # preprocessor statement. - if (($prevline =~ /^.\s*#\s*define\s/ || - $prevline =~ /\\\s*$/) && $continuation == 0) { - $check = 0; - } - - my $cond_ptr = -1; - $continuation = 0; - while ($cond_ptr != $cond_lines) { - $cond_ptr = $cond_lines; - - # If we see an #else/#elif then the code - # is not linear. - if ($s =~ /^\s*\#\s*(?:else|elif)/) { - $check = 0; - } - - # Ignore: - # 1) blank lines, they should be at 0, - # 2) preprocessor lines, and - # 3) labels. - if ($continuation || - $s =~ /^\s*?\n/ || - $s =~ /^\s*#\s*?/ || - $s =~ /^\s*$Ident\s*:/) { - $continuation = ($s =~ /^.*?\\\n/) ? 1 : 0; - if ($s =~ s/^.*?\n//) { - $cond_lines++; - } - } - } - - my (undef, $sindent) = line_stats("+" . $s); - my $stat_real = raw_line($linenr, $cond_lines); - - # Check if either of these lines are modified, else - # this is not this patch's fault. - if (!defined($stat_real) || - $stat !~ /^\+/ && $stat_real !~ /^\+/) { - $check = 0; - } - if (defined($stat_real) && $cond_lines > 1) { - $stat_real = "[...]\n$stat_real"; - } - - #print "line<$line> prevline<$prevline> indent<$indent> sindent<$sindent> check<$check> continuation<$continuation> s<$s> cond_lines<$cond_lines> stat_real<$stat_real> stat<$stat>\n"; - - if ($check && $s ne '' && - (($sindent % $tabsize) != 0 || - ($sindent < $indent) || - ($sindent == $indent && - ($s !~ /^\s*(?:\}|\{|else\b)/)) || - ($sindent > $indent + $tabsize))) { - WARN("SUSPECT_CODE_INDENT", - "suspect code indent for conditional statements ($indent, $sindent)\n" . $herecurr . "$stat_real\n"); - } - } - - # Track the 'values' across context and added lines. - my $opline = $line; $opline =~ s/^./ /; - my ($curr_values, $curr_vars) = - annotate_values($opline . "\n", $prev_values); - $curr_values = $prev_values . $curr_values; - if ($dbg_values) { - my $outline = $opline; $outline =~ s/\t/ /g; - print "$linenr > .$outline\n"; - print "$linenr > $curr_values\n"; - print "$linenr > $curr_vars\n"; - } - $prev_values = substr($curr_values, -1); - -#ignore lines not being added - next if ($line =~ /^[^\+]/); - -# check for self assignments used to avoid compiler warnings -# e.g.: int foo = foo, *bar = NULL; -# struct foo bar = *(&(bar)); - if ($line =~ /^\+\s*(?:$Declare)?([A-Za-z_][A-Za-z\d_]*)\s*=/) { - my $var = $1; - if ($line =~ /^\+\s*(?:$Declare)?$var\s*=\s*(?:$var|\*\s*\(?\s*&\s*\(?\s*$var\s*\)?\s*\)?)\s*[;,]/) { - WARN("SELF_ASSIGNMENT", - "Do not use self-assignments to avoid compiler warnings\n" . $herecurr); - } - } - -# check for dereferences that span multiple lines - if ($prevline =~ /^\+.*$Lval\s*(?:\.|->)\s*$/ && - $line =~ /^\+\s*(?!\#\s*(?!define\s+|if))\s*$Lval/) { - $prevline =~ /($Lval\s*(?:\.|->))\s*$/; - my $ref = $1; - $line =~ /^.\s*($Lval)/; - $ref .= $1; - $ref =~ s/\s//g; - WARN("MULTILINE_DEREFERENCE", - "Avoid multiple line dereference - prefer '$ref'\n" . $hereprev); - } - -# check for declarations of signed or unsigned without int - while ($line =~ m{\b($Declare)\s*(?!char\b|short\b|int\b|long\b)\s*($Ident)?\s*[=,;\[\)\(]}g) { - my $type = $1; - my $var = $2; - $var = "" if (!defined $var); - if ($type =~ /^(?:(?:$Storage|$Inline|$Attribute)\s+)*((?:un)?signed)((?:\s*\*)*)\s*$/) { - my $sign = $1; - my $pointer = $2; - - $pointer = "" if (!defined $pointer); - - if (WARN("UNSPECIFIED_INT", - "Prefer '" . trim($sign) . " int" . rtrim($pointer) . "' to bare use of '$sign" . rtrim($pointer) . "'\n" . $herecurr) && - $fix) { - my $decl = trim($sign) . " int "; - my $comp_pointer = $pointer; - $comp_pointer =~ s/\s//g; - $decl .= $comp_pointer; - $decl = rtrim($decl) if ($var eq ""); - $fixed[$fixlinenr] =~ s@\b$sign\s*\Q$pointer\E\s*$var\b@$decl$var@; - } - } - } - -# TEST: allow direct testing of the type matcher. - if ($dbg_type) { - if ($line =~ /^.\s*$Declare\s*$/) { - ERROR("TEST_TYPE", - "TEST: is type\n" . $herecurr); - } elsif ($dbg_type > 1 && $line =~ /^.+($Declare)/) { - ERROR("TEST_NOT_TYPE", - "TEST: is not type ($1 is)\n". $herecurr); - } - next; - } -# TEST: allow direct testing of the attribute matcher. - if ($dbg_attr) { - if ($line =~ /^.\s*$Modifier\s*$/) { - ERROR("TEST_ATTR", - "TEST: is attr\n" . $herecurr); - } elsif ($dbg_attr > 1 && $line =~ /^.+($Modifier)/) { - ERROR("TEST_NOT_ATTR", - "TEST: is not attr ($1 is)\n". $herecurr); - } - next; - } - -# check for initialisation to aggregates open brace on the next line - if ($line =~ /^.\s*{/ && - $prevline =~ /(?:^|[^=])=\s*$/) { - if (ERROR("OPEN_BRACE", - "that open brace { should be on the previous line\n" . $hereprev) && - $fix && $prevline =~ /^\+/ && $line =~ /^\+/) { - fix_delete_line($fixlinenr - 1, $prevrawline); - fix_delete_line($fixlinenr, $rawline); - my $fixedline = $prevrawline; - $fixedline =~ s/\s*=\s*$/ = {/; - fix_insert_line($fixlinenr, $fixedline); - $fixedline = $line; - $fixedline =~ s/^(.\s*)\{\s*/$1/; - fix_insert_line($fixlinenr, $fixedline); - } - } - -# -# Checks which are anchored on the added line. -# - -# check for malformed paths in #include statements (uses RAW line) - if ($rawline =~ m{^.\s*\#\s*include\s+[<"](.*)[">]}) { - my $path = $1; - if ($path =~ m{//}) { - ERROR("MALFORMED_INCLUDE", - "malformed #include filename\n" . $herecurr); - } - if ($path =~ "^uapi/" && $realfile =~ m@\binclude/uapi/@) { - ERROR("UAPI_INCLUDE", - "No #include in ...include/uapi/... should use a uapi/ path prefix\n" . $herecurr); - } - } - -# no C99 // comments - if ($line =~ m{//}) { - if (ERROR("C99_COMMENTS", - "do not use C99 // comments\n" . $herecurr) && - $fix) { - my $line = $fixed[$fixlinenr]; - if ($line =~ /\/\/(.*)$/) { - my $comment = trim($1); - $fixed[$fixlinenr] =~ s@\/\/(.*)$@/\* $comment \*/@; - } - } - } - # Remove C99 comments. - $line =~ s@//.*@@; - $opline =~ s@//.*@@; - -# EXPORT_SYMBOL should immediately follow the thing it is exporting, consider -# the whole statement. -#print "APW <$lines[$realline_next - 1]>\n"; - if (defined $realline_next && - exists $lines[$realline_next - 1] && - !defined $suppress_export{$realline_next} && - ($lines[$realline_next - 1] =~ /EXPORT_SYMBOL.*\((.*)\)/)) { - # Handle definitions which produce identifiers with - # a prefix: - # XXX(foo); - # EXPORT_SYMBOL(something_foo); - my $name = $1; - $name =~ s/^\s*($Ident).*/$1/; - if ($stat =~ /^(?:.\s*}\s*\n)?.([A-Z_]+)\s*\(\s*($Ident)/ && - $name =~ /^${Ident}_$2/) { -#print "FOO C name<$name>\n"; - $suppress_export{$realline_next} = 1; - - } elsif ($stat !~ /(?: - \n.}\s*$| - ^.DEFINE_$Ident\(\Q$name\E\)| - ^.DECLARE_$Ident\(\Q$name\E\)| - ^.LIST_HEAD\(\Q$name\E\)| - ^.(?:$Storage\s+)?$Type\s*\(\s*\*\s*\Q$name\E\s*\)\s*\(| - \b\Q$name\E(?:\s+$Attribute)*\s*(?:;|=|\[|\() - )/x) { -#print "FOO A<$lines[$realline_next - 1]> stat<$stat> name<$name>\n"; - $suppress_export{$realline_next} = 2; - } else { - $suppress_export{$realline_next} = 1; - } - } - if (!defined $suppress_export{$linenr} && - $prevline =~ /^.\s*$/ && - ($line =~ /EXPORT_SYMBOL.*\((.*)\)/)) { -#print "FOO B <$lines[$linenr - 1]>\n"; - $suppress_export{$linenr} = 2; - } - if (defined $suppress_export{$linenr} && - $suppress_export{$linenr} == 2) { - WARN("EXPORT_SYMBOL", - "EXPORT_SYMBOL(foo); should immediately follow its function/variable\n" . $herecurr); - } - -# check for global initialisers. - if ($line =~ /^\+$Type\s*$Ident(?:\s+$Modifier)*\s*=\s*($zero_initializer)\s*;/ && - !exclude_global_initialisers($realfile)) { - if (ERROR("GLOBAL_INITIALISERS", - "do not initialise globals to $1\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/(^.$Type\s*$Ident(?:\s+$Modifier)*)\s*=\s*$zero_initializer\s*;/$1;/; - } - } -# check for static initialisers. - if ($line =~ /^\+.*\bstatic\s.*=\s*($zero_initializer)\s*;/) { - if (ERROR("INITIALISED_STATIC", - "do not initialise statics to $1\n" . - $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/(\bstatic\s.*?)\s*=\s*$zero_initializer\s*;/$1;/; - } - } - -# check for misordered declarations of char/short/int/long with signed/unsigned - while ($sline =~ m{(\b$TypeMisordered\b)}g) { - my $tmp = trim($1); - WARN("MISORDERED_TYPE", - "type '$tmp' should be specified in [[un]signed] [short|int|long|long long] order\n" . $herecurr); - } - -# check for unnecessary <signed> int declarations of short/long/long long - while ($sline =~ m{\b($TypeMisordered(\s*\*)*|$C90_int_types)\b}g) { - my $type = trim($1); - next if ($type !~ /\bint\b/); - next if ($type !~ /\b(?:short|long\s+long|long)\b/); - my $new_type = $type; - $new_type =~ s/\b\s*int\s*\b/ /; - $new_type =~ s/\b\s*(?:un)?signed\b\s*/ /; - $new_type =~ s/^const\s+//; - $new_type = "unsigned $new_type" if ($type =~ /\bunsigned\b/); - $new_type = "const $new_type" if ($type =~ /^const\b/); - $new_type =~ s/\s+/ /g; - $new_type = trim($new_type); - if (WARN("UNNECESSARY_INT", - "Prefer '$new_type' over '$type' as the int is unnecessary\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/\b\Q$type\E\b/$new_type/; - } - } - -# check for static const char * arrays. - if ($line =~ /\bstatic\s+const\s+char\s*\*\s*(\w+)\s*\[\s*\]\s*=\s*/) { - WARN("STATIC_CONST_CHAR_ARRAY", - "static const char * array should probably be static const char * const\n" . - $herecurr); - } - -# check for initialized const char arrays that should be static const - if ($line =~ /^\+\s*const\s+(char|unsigned\s+char|_*u8|(?:[us]_)?int8_t)\s+\w+\s*\[\s*(?:\w+\s*)?\]\s*=\s*"/) { - if (WARN("STATIC_CONST_CHAR_ARRAY", - "const array should probably be static const\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/(^.\s*)const\b/${1}static const/; - } - } - -# check for static char foo[] = "bar" declarations. - if ($line =~ /\bstatic\s+char\s+(\w+)\s*\[\s*\]\s*=\s*"/) { - WARN("STATIC_CONST_CHAR_ARRAY", - "static char array declaration should probably be static const char\n" . - $herecurr); - } - -# check for const <foo> const where <foo> is not a pointer or array type - if ($sline =~ /\bconst\s+($BasicType)\s+const\b/) { - my $found = $1; - if ($sline =~ /\bconst\s+\Q$found\E\s+const\b\s*\*/) { - WARN("CONST_CONST", - "'const $found const *' should probably be 'const $found * const'\n" . $herecurr); - } elsif ($sline !~ /\bconst\s+\Q$found\E\s+const\s+\w+\s*\[/) { - WARN("CONST_CONST", - "'const $found const' should probably be 'const $found'\n" . $herecurr); - } - } - -# check for const static or static <non ptr type> const declarations -# prefer 'static const <foo>' over 'const static <foo>' and 'static <foo> const' - if ($sline =~ /^\+\s*const\s+static\s+($Type)\b/ || - $sline =~ /^\+\s*static\s+($BasicType)\s+const\b/) { - if (WARN("STATIC_CONST", - "Move const after static - use 'static const $1'\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/\bconst\s+static\b/static const/; - $fixed[$fixlinenr] =~ s/\bstatic\s+($BasicType)\s+const\b/static const $1/; - } - } - -# check for non-global char *foo[] = {"bar", ...} declarations. - if ($line =~ /^.\s+(?:static\s+|const\s+)?char\s+\*\s*\w+\s*\[\s*\]\s*=\s*\{/) { - WARN("STATIC_CONST_CHAR_ARRAY", - "char * array declaration might be better as static const\n" . - $herecurr); - } - -# check for sizeof(foo)/sizeof(foo[0]) that could be ARRAY_SIZE(foo) - if ($line =~ m@\bsizeof\s*\(\s*($Lval)\s*\)@) { - my $array = $1; - if ($line =~ m@\b(sizeof\s*\(\s*\Q$array\E\s*\)\s*/\s*sizeof\s*\(\s*\Q$array\E\s*\[\s*0\s*\]\s*\))@) { - my $array_div = $1; - if (WARN("ARRAY_SIZE", - "Prefer ARRAY_SIZE($array)\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/\Q$array_div\E/ARRAY_SIZE($array)/; - } - } - } - -# check for function declarations without arguments like "int foo()" - if ($line =~ /(\b$Type\s*$Ident)\s*\(\s*\)/) { - if (ERROR("FUNCTION_WITHOUT_ARGS", - "Bad function definition - $1() should probably be $1(void)\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/(\b($Type)\s+($Ident))\s*\(\s*\)/$2 $3(void)/; - } - } - -# check for new typedefs, only function parameters and sparse annotations -# make sense. - if ($line =~ /\btypedef\s/ && - $line !~ /\btypedef\s+$Type\s*\(\s*\*?$Ident\s*\)\s*\(/ && - $line !~ /\btypedef\s+$Type\s+$Ident\s*\(/ && - $line !~ /\b$typeTypedefs\b/ && - $line !~ /\b__bitwise\b/) { - WARN("NEW_TYPEDEFS", - "do not add new typedefs\n" . $herecurr); - } - -# * goes on variable not on type - # (char*[ const]) - while ($line =~ m{(\($NonptrType(\s*(?:$Modifier\b\s*|\*\s*)+)\))}g) { - #print "AA<$1>\n"; - my ($ident, $from, $to) = ($1, $2, $2); - - # Should start with a space. - $to =~ s/^(\S)/ $1/; - # Should not end with a space. - $to =~ s/\s+$//; - # '*'s should not have spaces between. - while ($to =~ s/\*\s+\*/\*\*/) { - } - -## print "1: from<$from> to<$to> ident<$ident>\n"; - if ($from ne $to) { - if (ERROR("POINTER_LOCATION", - "\"(foo$from)\" should be \"(foo$to)\"\n" . $herecurr) && - $fix) { - my $sub_from = $ident; - my $sub_to = $ident; - $sub_to =~ s/\Q$from\E/$to/; - $fixed[$fixlinenr] =~ - s@\Q$sub_from\E@$sub_to@; - } - } - } - while ($line =~ m{(\b$NonptrType(\s*(?:$Modifier\b\s*|\*\s*)+)($Ident))}g) { - #print "BB<$1>\n"; - my ($match, $from, $to, $ident) = ($1, $2, $2, $3); - - # Should start with a space. - $to =~ s/^(\S)/ $1/; - # Should not end with a space. - $to =~ s/\s+$//; - # '*'s should not have spaces between. - while ($to =~ s/\*\s+\*/\*\*/) { - } - # Modifiers should have spaces. - $to =~ s/(\b$Modifier$)/$1 /; - -## print "2: from<$from> to<$to> ident<$ident>\n"; - if ($from ne $to && $ident !~ /^$Modifier$/) { - if (ERROR("POINTER_LOCATION", - "\"foo${from}bar\" should be \"foo${to}bar\"\n" . $herecurr) && - $fix) { - - my $sub_from = $match; - my $sub_to = $match; - $sub_to =~ s/\Q$from\E/$to/; - $fixed[$fixlinenr] =~ - s@\Q$sub_from\E@$sub_to@; - } - } - } - -# avoid BUG() or BUG_ON() - if ($line =~ /\b(?:BUG|BUG_ON)\b/) { - my $msg_level = \&WARN; - $msg_level = \&CHK if ($file); - &{$msg_level}("AVOID_BUG", - "Avoid crashing the kernel - try using WARN_ON & recovery code rather than BUG() or BUG_ON()\n" . $herecurr); - } - -# avoid LINUX_VERSION_CODE - if ($line =~ /\bLINUX_VERSION_CODE\b/) { - WARN("LINUX_VERSION_CODE", - "LINUX_VERSION_CODE should be avoided, code should be for the version to which it is merged\n" . $herecurr); - } - - if(!$SOF) { - -# check for uses of printk_ratelimit - if ($line =~ /\bprintk_ratelimit\s*\(/) { - WARN("PRINTK_RATELIMITED", - "Prefer printk_ratelimited or pr_<level>_ratelimited to printk_ratelimit\n" . $herecurr); - } - -# printk should use KERN_* levels - if ($line =~ /\bprintk\s*\(\s*(?!KERN_[A-Z]+\b)/) { - WARN("PRINTK_WITHOUT_KERN_LEVEL", - "printk() should include KERN_<LEVEL> facility level\n" . $herecurr); - } - -# prefer variants of (subsystem|netdev|dev|pr)_<level> to printk(KERN_<LEVEL> - if ($line =~ /\b(printk(_once|_ratelimited)?)\s*\(\s*KERN_([A-Z]+)/) { - my $printk = $1; - my $modifier = $2; - my $orig = $3; - $modifier = "" if (!defined($modifier)); - my $level = lc($orig); - $level = "warn" if ($level eq "warning"); - my $level2 = $level; - $level2 = "dbg" if ($level eq "debug"); - $level .= $modifier; - $level2 .= $modifier; - WARN("PREFER_PR_LEVEL", - "Prefer [subsystem eg: netdev]_$level2([subsystem]dev, ... then dev_$level2(dev, ... then pr_$level(... to $printk(KERN_$orig ...\n" . $herecurr); - } - -# prefer dev_<level> to dev_printk(KERN_<LEVEL> - if ($line =~ /\bdev_printk\s*\(\s*KERN_([A-Z]+)/) { - my $orig = $1; - my $level = lc($orig); - $level = "warn" if ($level eq "warning"); - $level = "dbg" if ($level eq "debug"); - WARN("PREFER_DEV_LEVEL", - "Prefer dev_$level(... to dev_printk(KERN_$orig, ...\n" . $herecurr); - } - - } # !$SOF - -# trace_printk should not be used in production code. - if ($line =~ /\b(trace_printk|trace_puts|ftrace_vprintk)\s*\(/) { - WARN("TRACE_PRINTK", - "Do not use $1() in production code (this can be ignored if built only with a debug config option)\n" . $herecurr); - } - -# ENOSYS means "bad syscall nr" and nothing else. This will have a small -# number of false positives, but assembly files are not checked, so at -# least the arch entry code will not trigger this warning. - if ($line =~ /\bENOSYS\b/) { - WARN("ENOSYS", - "ENOSYS means 'invalid syscall nr' and nothing else\n" . $herecurr); - } - -# ENOTSUPP is not a standard error code and should be avoided in new patches. -# Folks usually mean EOPNOTSUPP (also called ENOTSUP), when they type ENOTSUPP. -# Similarly to ENOSYS warning a small number of false positives is expected. - if (!$file && $line =~ /\bENOTSUPP\b/) { - if (WARN("ENOTSUPP", - "ENOTSUPP is not a SUSV4 error code, prefer EOPNOTSUPP\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/\bENOTSUPP\b/EOPNOTSUPP/; - } - } - -# function brace can't be on same line, except for #defines of do while, -# or if closed on same line - if ($perl_version_ok && - $sline =~ /$Type\s*$Ident\s*$balanced_parens\s*\{/ && - $sline !~ /\#\s*define\b.*do\s*\{/ && - $sline !~ /}/) { - if (ERROR("OPEN_BRACE", - "open brace '{' following function definitions go on the next line\n" . $herecurr) && - $fix) { - fix_delete_line($fixlinenr, $rawline); - my $fixed_line = $rawline; - $fixed_line =~ /(^..*$Type\s*$Ident\(.*\)\s*)\{(.*)$/; - my $line1 = $1; - my $line2 = $2; - fix_insert_line($fixlinenr, ltrim($line1)); - fix_insert_line($fixlinenr, "\+{"); - if ($line2 !~ /^\s*$/) { - fix_insert_line($fixlinenr, "\+\t" . trim($line2)); - } - } - } - -# open braces for enum, union and struct go on the same line. - if ($line =~ /^.\s*{/ && - $prevline =~ /^.\s*(?:typedef\s+)?(enum|union|struct)(?:\s+$Ident)?\s*$/) { - if (ERROR("OPEN_BRACE", - "open brace '{' following $1 go on the same line\n" . $hereprev) && - $fix && $prevline =~ /^\+/ && $line =~ /^\+/) { - fix_delete_line($fixlinenr - 1, $prevrawline); - fix_delete_line($fixlinenr, $rawline); - my $fixedline = rtrim($prevrawline) . " {"; - fix_insert_line($fixlinenr, $fixedline); - $fixedline = $rawline; - $fixedline =~ s/^(.\s*)\{\s*/$1\t/; - if ($fixedline !~ /^\+\s*$/) { - fix_insert_line($fixlinenr, $fixedline); - } - } - } - -# missing space after union, struct or enum definition - if ($line =~ /^.\s*(?:typedef\s+)?(enum|union|struct)(?:\s+$Ident){1,2}[=\{]/) { - if (WARN("SPACING", - "missing space after $1 definition\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ - s/^(.\s*(?:typedef\s+)?(?:enum|union|struct)(?:\s+$Ident){1,2})([=\{])/$1 $2/; - } - } - -# Function pointer declarations -# check spacing between type, funcptr, and args -# canonical declaration is "type (*funcptr)(args...)" - if ($line =~ /^.\s*($Declare)\((\s*)\*(\s*)($Ident)(\s*)\)(\s*)\(/) { - my $declare = $1; - my $pre_pointer_space = $2; - my $post_pointer_space = $3; - my $funcname = $4; - my $post_funcname_space = $5; - my $pre_args_space = $6; - -# the $Declare variable will capture all spaces after the type -# so check it for a missing trailing missing space but pointer return types -# don't need a space so don't warn for those. - my $post_declare_space = ""; - if ($declare =~ /(\s+)$/) { - $post_declare_space = $1; - $declare = rtrim($declare); - } - if ($declare !~ /\*$/ && $post_declare_space =~ /^$/) { - WARN("SPACING", - "missing space after return type\n" . $herecurr); - $post_declare_space = " "; - } - -# unnecessary space "type (*funcptr)(args...)" -# This test is not currently implemented because these declarations are -# equivalent to -# int foo(int bar, ...) -# and this is form shouldn't/doesn't generate a checkpatch warning. -# -# elsif ($declare =~ /\s{2,}$/) { -# WARN("SPACING", -# "Multiple spaces after return type\n" . $herecurr); -# } - -# unnecessary space "type ( *funcptr)(args...)" - if (defined $pre_pointer_space && - $pre_pointer_space =~ /^\s/) { - WARN("SPACING", - "Unnecessary space after function pointer open parenthesis\n" . $herecurr); - } - -# unnecessary space "type (* funcptr)(args...)" - if (defined $post_pointer_space && - $post_pointer_space =~ /^\s/) { - WARN("SPACING", - "Unnecessary space before function pointer name\n" . $herecurr); - } - -# unnecessary space "type (*funcptr )(args...)" - if (defined $post_funcname_space && - $post_funcname_space =~ /^\s/) { - WARN("SPACING", - "Unnecessary space after function pointer name\n" . $herecurr); - } - -# unnecessary space "type (*funcptr) (args...)" - if (defined $pre_args_space && - $pre_args_space =~ /^\s/) { - WARN("SPACING", - "Unnecessary space before function pointer arguments\n" . $herecurr); - } - - if (show_type("SPACING") && $fix) { - $fixed[$fixlinenr] =~ - s/^(.\s*)$Declare\s*\(\s*\*\s*$Ident\s*\)\s*\(/$1 . $declare . $post_declare_space . '(*' . $funcname . ')('/ex; - } - } - -# check for spacing round square brackets; allowed: -# 1. with a type on the left -- int [] a; -# 2. at the beginning of a line for slice initialisers -- [0...10] = 5, -# 3. inside a curly brace -- = { [0...10] = 5 } - while ($line =~ /(.*?\s)\[/g) { - my ($where, $prefix) = ($-[1], $1); - if ($prefix !~ /$Type\s+$/ && - ($where != 0 || $prefix !~ /^.\s+$/) && - $prefix !~ /[{,:]\s+$/) { - if (ERROR("BRACKET_SPACE", - "space prohibited before open square bracket '['\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ - s/^(\+.*?)\s+\[/$1\[/; - } - } - } - -# check for spaces between functions and their parentheses. - while ($line =~ /($Ident)\s+\(/g) { - my $name = $1; - my $ctx_before = substr($line, 0, $-[1]); - my $ctx = "$ctx_before$name"; - - # Ignore those directives where spaces _are_ permitted. - if ($name =~ /^(?: - if|for|while|switch|return|case| - volatile|__volatile__| - __attribute__|format|__extension__| - asm|__asm__)$/x) - { - # cpp #define statements have non-optional spaces, ie - # if there is a space between the name and the open - # parenthesis it is simply not a parameter group. - } elsif ($ctx_before =~ /^.\s*\#\s*define\s*$/) { - - # cpp #elif statement condition may start with a ( - } elsif ($ctx =~ /^.\s*\#\s*elif\s*$/) { - - # If this whole things ends with a type its most - # likely a typedef for a function. - } elsif ($ctx =~ /$Type$/) { - - } else { - if (WARN("SPACING", - "space prohibited between function name and open parenthesis '('\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ - s/\b$name\s+\(/$name\(/; - } - } - } - -# Check operator spacing. - if (!($line=~/\#\s*include/)) { - my $fixed_line = ""; - my $line_fixed = 0; - - my $ops = qr{ - <<=|>>=|<=|>=|==|!=| - \+=|-=|\*=|\/=|%=|\^=|\|=|&=| - =>|->|<<|>>|<|>|=|!|~| - &&|\|\||,|\^|\+\+|--|&|\||\+|-|\*|\/|%| - \?:|\?|: - }x; - my @elements = split(/($ops|;)/, $opline); - -## print("element count: <" . $#elements . ">\n"); -## foreach my $el (@elements) { -## print("el: <$el>\n"); -## } - - my @fix_elements = (); - my $off = 0; - - foreach my $el (@elements) { - push(@fix_elements, substr($rawline, $off, length($el))); - $off += length($el); - } - - $off = 0; - - my $blank = copy_spacing($opline); - my $last_after = -1; - - for (my $n = 0; $n < $#elements; $n += 2) { - - my $good = $fix_elements[$n] . $fix_elements[$n + 1]; - -## print("n: <$n> good: <$good>\n"); - - $off += length($elements[$n]); - - # Pick up the preceding and succeeding characters. - my $ca = substr($opline, 0, $off); - my $cc = ''; - if (length($opline) >= ($off + length($elements[$n + 1]))) { - $cc = substr($opline, $off + length($elements[$n + 1])); - } - my $cb = "$ca$;$cc"; - - my $a = ''; - $a = 'V' if ($elements[$n] ne ''); - $a = 'W' if ($elements[$n] =~ /\s$/); - $a = 'C' if ($elements[$n] =~ /$;$/); - $a = 'B' if ($elements[$n] =~ /(\[|\()$/); - $a = 'O' if ($elements[$n] eq ''); - $a = 'E' if ($ca =~ /^\s*$/); - - my $op = $elements[$n + 1]; - - my $c = ''; - if (defined $elements[$n + 2]) { - $c = 'V' if ($elements[$n + 2] ne ''); - $c = 'W' if ($elements[$n + 2] =~ /^\s/); - $c = 'C' if ($elements[$n + 2] =~ /^$;/); - $c = 'B' if ($elements[$n + 2] =~ /^(\)|\]|;)/); - $c = 'O' if ($elements[$n + 2] eq ''); - $c = 'E' if ($elements[$n + 2] =~ /^\s*\\$/); - } else { - $c = 'E'; - } - - my $ctx = "${a}x${c}"; - - my $at = "(ctx:$ctx)"; - - my $ptr = substr($blank, 0, $off) . "^"; - my $hereptr = "$hereline$ptr\n"; - - # Pull out the value of this operator. - my $op_type = substr($curr_values, $off + 1, 1); - - # Get the full operator variant. - my $opv = $op . substr($curr_vars, $off, 1); - - # Ignore operators passed as parameters. - if ($op_type ne 'V' && - $ca =~ /\s$/ && $cc =~ /^\s*[,\)]/) { - -# # Ignore comments -# } elsif ($op =~ /^$;+$/) { - - # ; should have either the end of line or a space or \ after it - } elsif ($op eq ';') { - if ($ctx !~ /.x[WEBC]/ && - $cc !~ /^\\/ && $cc !~ /^;/) { - if (ERROR("SPACING", - "space required after that '$op' $at\n" . $hereptr)) { - $good = $fix_elements[$n] . trim($fix_elements[$n + 1]) . " "; - $line_fixed = 1; - } - } - - # // is a comment - } elsif ($op eq '//') { - - # : when part of a bitfield - } elsif ($opv eq ':B') { - # skip the bitfield test for now - - # No spaces for: - # -> - } elsif ($op eq '->') { - if ($ctx =~ /Wx.|.xW/) { - if (ERROR("SPACING", - "spaces prohibited around that '$op' $at\n" . $hereptr)) { - $good = rtrim($fix_elements[$n]) . trim($fix_elements[$n + 1]); - if (defined $fix_elements[$n + 2]) { - $fix_elements[$n + 2] =~ s/^\s+//; - } - $line_fixed = 1; - } - } - - # , must not have a space before and must have a space on the right. - } elsif ($op eq ',') { - my $rtrim_before = 0; - my $space_after = 0; - if ($ctx =~ /Wx./) { - if (ERROR("SPACING", - "space prohibited before that '$op' $at\n" . $hereptr)) { - $line_fixed = 1; - $rtrim_before = 1; - } - } - if ($ctx !~ /.x[WEC]/ && $cc !~ /^}/) { - if (ERROR("SPACING", - "space required after that '$op' $at\n" . $hereptr)) { - $line_fixed = 1; - $last_after = $n; - $space_after = 1; - } - } - if ($rtrim_before || $space_after) { - if ($rtrim_before) { - $good = rtrim($fix_elements[$n]) . trim($fix_elements[$n + 1]); - } else { - $good = $fix_elements[$n] . trim($fix_elements[$n + 1]); - } - if ($space_after) { - $good .= " "; - } - } - - # '*' as part of a type definition -- reported already. - } elsif ($opv eq '*_') { - #warn "'*' is part of type\n"; - - # unary operators should have a space before and - # none after. May be left adjacent to another - # unary operator, or a cast - } elsif ($op eq '!' || $op eq '~' || - $opv eq '*U' || $opv eq '-U' || - $opv eq '&U' || $opv eq '&&U') { - if ($ctx !~ /[WEBC]x./ && $ca !~ /(?:\)|!|~|\*|-|\&|\||\+\+|\-\-|\{)$/) { - if (ERROR("SPACING", - "space required before that '$op' $at\n" . $hereptr)) { - if ($n != $last_after + 2) { - $good = $fix_elements[$n] . " " . ltrim($fix_elements[$n + 1]); - $line_fixed = 1; - } - } - } - if ($op eq '*' && $cc =~/\s*$Modifier\b/) { - # A unary '*' may be const - - } elsif ($ctx =~ /.xW/) { - if (ERROR("SPACING", - "space prohibited after that '$op' $at\n" . $hereptr)) { - $good = $fix_elements[$n] . rtrim($fix_elements[$n + 1]); - if (defined $fix_elements[$n + 2]) { - $fix_elements[$n + 2] =~ s/^\s+//; - } - $line_fixed = 1; - } - } - - # unary ++ and unary -- are allowed no space on one side. - } elsif ($op eq '++' or $op eq '--') { - if ($ctx !~ /[WEOBC]x[^W]/ && $ctx !~ /[^W]x[WOBEC]/) { - if (ERROR("SPACING", - "space required one side of that '$op' $at\n" . $hereptr)) { - $good = $fix_elements[$n] . trim($fix_elements[$n + 1]) . " "; - $line_fixed = 1; - } - } - if ($ctx =~ /Wx[BE]/ || - ($ctx =~ /Wx./ && $cc =~ /^;/)) { - if (ERROR("SPACING", - "space prohibited before that '$op' $at\n" . $hereptr)) { - $good = rtrim($fix_elements[$n]) . trim($fix_elements[$n + 1]); - $line_fixed = 1; - } - } - if ($ctx =~ /ExW/) { - if (ERROR("SPACING", - "space prohibited after that '$op' $at\n" . $hereptr)) { - $good = $fix_elements[$n] . trim($fix_elements[$n + 1]); - if (defined $fix_elements[$n + 2]) { - $fix_elements[$n + 2] =~ s/^\s+//; - } - $line_fixed = 1; - } - } - - # << and >> may either have or not have spaces both sides - } elsif ($op eq '<<' or $op eq '>>' or - $op eq '&' or $op eq '^' or $op eq '|' or - $op eq '+' or $op eq '-' or - $op eq '*' or $op eq '/' or - $op eq '%') - { - if ($check) { - if (defined $fix_elements[$n + 2] && $ctx !~ /[EW]x[EW]/) { - if (CHK("SPACING", - "spaces preferred around that '$op' $at\n" . $hereptr)) { - $good = rtrim($fix_elements[$n]) . " " . trim($fix_elements[$n + 1]) . " "; - $fix_elements[$n + 2] =~ s/^\s+//; - $line_fixed = 1; - } - } elsif (!defined $fix_elements[$n + 2] && $ctx !~ /Wx[OE]/) { - if (CHK("SPACING", - "space preferred before that '$op' $at\n" . $hereptr)) { - $good = rtrim($fix_elements[$n]) . " " . trim($fix_elements[$n + 1]); - $line_fixed = 1; - } - } - } elsif ($ctx =~ /Wx[^WCE]|[^WCE]xW/) { - if (ERROR("SPACING", - "need consistent spacing around '$op' $at\n" . $hereptr)) { - $good = rtrim($fix_elements[$n]) . " " . trim($fix_elements[$n + 1]) . " "; - if (defined $fix_elements[$n + 2]) { - $fix_elements[$n + 2] =~ s/^\s+//; - } - $line_fixed = 1; - } - } - - # A colon needs no spaces before when it is - # terminating a case value or a label. - } elsif ($opv eq ':C' || $opv eq ':L') { - if ($ctx =~ /Wx./ and $realfile !~ m@.*\.lds\.h$@) { - if (ERROR("SPACING", - "space prohibited before that '$op' $at\n" . $hereptr)) { - $good = rtrim($fix_elements[$n]) . trim($fix_elements[$n + 1]); - $line_fixed = 1; - } - } - - # All the others need spaces both sides. - } elsif ($ctx !~ /[EWC]x[CWE]/) { - my $ok = 0; - - # Ignore email addresses <foo@bar> - if (($op eq '<' && - $cc =~ /^\S+\@\S+>/) || - ($op eq '>' && - $ca =~ /<\S+\@\S+$/)) - { - $ok = 1; - } - - # for asm volatile statements - # ignore a colon with another - # colon immediately before or after - if (($op eq ':') && - ($ca =~ /:$/ || $cc =~ /^:/)) { - $ok = 1; - } - - # messages are ERROR, but ?: are CHK - if ($ok == 0) { - my $msg_level = \&ERROR; - $msg_level = \&CHK if (($op eq '?:' || $op eq '?' || $op eq ':') && $ctx =~ /VxV/); - - if (&{$msg_level}("SPACING", - "spaces required around that '$op' $at\n" . $hereptr)) { - $good = rtrim($fix_elements[$n]) . " " . trim($fix_elements[$n + 1]) . " "; - if (defined $fix_elements[$n + 2]) { - $fix_elements[$n + 2] =~ s/^\s+//; - } - $line_fixed = 1; - } - } - } - $off += length($elements[$n + 1]); - -## print("n: <$n> GOOD: <$good>\n"); - - $fixed_line = $fixed_line . $good; - } - - if (($#elements % 2) == 0) { - $fixed_line = $fixed_line . $fix_elements[$#elements]; - } - - if ($fix && $line_fixed && $fixed_line ne $fixed[$fixlinenr]) { - $fixed[$fixlinenr] = $fixed_line; - } - - - } - -# check for whitespace before a non-naked semicolon - if ($line =~ /^\+.*\S\s+;\s*$/) { - if (WARN("SPACING", - "space prohibited before semicolon\n" . $herecurr) && - $fix) { - 1 while $fixed[$fixlinenr] =~ - s/^(\+.*\S)\s+;/$1;/; - } - } - -# check for multiple assignments - if ($line =~ /^.\s*$Lval\s*=\s*$Lval\s*=(?!=)/) { - CHK("MULTIPLE_ASSIGNMENTS", - "multiple assignments should be avoided\n" . $herecurr); - } - -## # check for multiple declarations, allowing for a function declaration -## # continuation. -## if ($line =~ /^.\s*$Type\s+$Ident(?:\s*=[^,{]*)?\s*,\s*$Ident.*/ && -## $line !~ /^.\s*$Type\s+$Ident(?:\s*=[^,{]*)?\s*,\s*$Type\s*$Ident.*/) { -## -## # Remove any bracketed sections to ensure we do not -## # falsely report the parameters of functions. -## my $ln = $line; -## while ($ln =~ s/\([^\(\)]*\)//g) { -## } -## if ($ln =~ /,/) { -## WARN("MULTIPLE_DECLARATION", -## "declaring multiple variables together should be avoided\n" . $herecurr); -## } -## } - -#need space before brace following if, while, etc - if (($line =~ /\(.*\)\{/ && $line !~ /\($Type\)\{/) || - $line =~ /\b(?:else|do)\{/) { - if (ERROR("SPACING", - "space required before the open brace '{'\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/^(\+.*(?:do|else|\)))\{/$1 {/; - } - } - -## # check for blank lines before declarations -## if ($line =~ /^.\t+$Type\s+$Ident(?:\s*=.*)?;/ && -## $prevrawline =~ /^.\s*$/) { -## WARN("SPACING", -## "No blank lines before declarations\n" . $hereprev); -## } -## - -# closing brace should have a space following it when it has anything -# on the line - if ($line =~ /}(?!(?:,|;|\)|\}))\S/) { - if (ERROR("SPACING", - "space required after that close brace '}'\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ - s/}((?!(?:,|;|\)))\S)/} $1/; - } - } - -# check spacing on square brackets - if ($line =~ /\[\s/ && $line !~ /\[\s*$/) { - if (ERROR("SPACING", - "space prohibited after that open square bracket '['\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ - s/\[\s+/\[/; - } - } - if ($line =~ /\s\]/) { - if (ERROR("SPACING", - "space prohibited before that close square bracket ']'\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ - s/\s+\]/\]/; - } - } - -# check spacing on parentheses - if ($line =~ /\(\s/ && $line !~ /\(\s*(?:\\)?$/ && - $line !~ /for\s*\(\s+;/) { - if (ERROR("SPACING", - "space prohibited after that open parenthesis '('\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ - s/\(\s+/\(/; - } - } - if ($line =~ /(\s+)\)/ && $line !~ /^.\s*\)/ && - $line !~ /for\s*\(.*;\s+\)/ && - $line !~ /:\s+\)/) { - if (ERROR("SPACING", - "space prohibited before that close parenthesis ')'\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ - s/\s+\)/\)/; - } - } - -# check unnecessary parentheses around addressof/dereference single $Lvals -# ie: &(foo->bar) should be &foo->bar and *(foo->bar) should be *foo->bar - - while ($line =~ /(?:[^&]&\s*|\*)\(\s*($Ident\s*(?:$Member\s*)+)\s*\)/g) { - my $var = $1; - if (CHK("UNNECESSARY_PARENTHESES", - "Unnecessary parentheses around $var\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/\(\s*\Q$var\E\s*\)/$var/; - } - } - -# check for unnecessary parentheses around function pointer uses -# ie: (foo->bar)(); should be foo->bar(); -# but not "if (foo->bar) (" to avoid some false positives - if ($line =~ /(\bif\s*|)(\(\s*$Ident\s*(?:$Member\s*)+\))[ \t]*\(/ && $1 !~ /^if/) { - my $var = $2; - if (CHK("UNNECESSARY_PARENTHESES", - "Unnecessary parentheses around function pointer $var\n" . $herecurr) && - $fix) { - my $var2 = deparenthesize($var); - $var2 =~ s/\s//g; - $fixed[$fixlinenr] =~ s/\Q$var\E/$var2/; - } - } - -# check for unnecessary parentheses around comparisons in if uses -# when !drivers/staging or command-line uses --strict - if (($realfile !~ m@^(?:drivers/staging/)@ || $check_orig) && - $perl_version_ok && defined($stat) && - $stat =~ /(^.\s*if\s*($balanced_parens))/) { - my $if_stat = $1; - my $test = substr($2, 1, -1); - my $herectx; - while ($test =~ /(?:^|[^\w\&\!\~])+\s*\(\s*([\&\!\~]?\s*$Lval\s*(?:$Compare\s*$FuncArg)?)\s*\)/g) { - my $match = $1; - # avoid parentheses around potential macro args - next if ($match =~ /^\s*\w+\s*$/); - if (!defined($herectx)) { - $herectx = $here . "\n"; - my $cnt = statement_rawlines($if_stat); - for (my $n = 0; $n < $cnt; $n++) { - my $rl = raw_line($linenr, $n); - $herectx .= $rl . "\n"; - last if $rl =~ /^[ \+].*\{/; - } - } - CHK("UNNECESSARY_PARENTHESES", - "Unnecessary parentheses around '$match'\n" . $herectx); - } - } - -# check that goto labels aren't indented (allow a single space indentation) -# and ignore bitfield definitions like foo:1 -# Strictly, labels can have whitespace after the identifier and before the : -# but this is not allowed here as many ?: uses would appear to be labels - if ($sline =~ /^.\s+[A-Za-z_][A-Za-z\d_]*:(?!\s*\d+)/ && - $sline !~ /^. [A-Za-z\d_][A-Za-z\d_]*:/ && - $sline !~ /^.\s+default:/) { - if (WARN("INDENTED_LABEL", - "labels should not be indented\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ - s/^(.)\s+/$1/; - } - } - -# check if a statement with a comma should be two statements like: -# foo = bar(), /* comma should be semicolon */ -# bar = baz(); - if (defined($stat) && - $stat =~ /^\+\s*(?:$Lval\s*$Assignment\s*)?$FuncArg\s*,\s*(?:$Lval\s*$Assignment\s*)?$FuncArg\s*;\s*$/) { - my $cnt = statement_rawlines($stat); - my $herectx = get_stat_here($linenr, $cnt, $here); - WARN("SUSPECT_COMMA_SEMICOLON", - "Possible comma where semicolon could be used\n" . $herectx); - } - -# return is not a function - if (defined($stat) && $stat =~ /^.\s*return(\s*)\(/s) { - my $spacing = $1; - if ($perl_version_ok && - $stat =~ /^.\s*return\s*($balanced_parens)\s*;\s*$/) { - my $value = $1; - $value = deparenthesize($value); - if ($value =~ m/^\s*$FuncArg\s*(?:\?|$)/) { - ERROR("RETURN_PARENTHESES", - "return is not a function, parentheses are not required\n" . $herecurr); - } - } elsif ($spacing !~ /\s+/) { - ERROR("SPACING", - "space required before the open parenthesis '('\n" . $herecurr); - } - } - -# unnecessary return in a void function -# at end-of-function, with the previous line a single leading tab, then return; -# and the line before that not a goto label target like "out:" - if ($sline =~ /^[ \+]}\s*$/ && - $prevline =~ /^\+\treturn\s*;\s*$/ && - $linenr >= 3 && - $lines[$linenr - 3] =~ /^[ +]/ && - $lines[$linenr - 3] !~ /^[ +]\s*$Ident\s*:/) { - WARN("RETURN_VOID", - "void function return statements are not generally useful\n" . $hereprev); - } - -# if statements using unnecessary parentheses - ie: if ((foo == bar)) - if ($perl_version_ok && - $line =~ /\bif\s*((?:\(\s*){2,})/) { - my $openparens = $1; - my $count = $openparens =~ tr@\(@\(@; - my $msg = ""; - if ($line =~ /\bif\s*(?:\(\s*){$count,$count}$LvalOrFunc\s*($Compare)\s*$LvalOrFunc(?:\s*\)){$count,$count}/) { - my $comp = $4; #Not $1 because of $LvalOrFunc - $msg = " - maybe == should be = ?" if ($comp eq "=="); - WARN("UNNECESSARY_PARENTHESES", - "Unnecessary parentheses$msg\n" . $herecurr); - } - } - -# comparisons with a constant or upper case identifier on the left -# avoid cases like "foo + BAR < baz" -# only fix matches surrounded by parentheses to avoid incorrect -# conversions like "FOO < baz() + 5" being "misfixed" to "baz() > FOO + 5" - if ($perl_version_ok && - $line =~ /^\+(.*)\b($Constant|[A-Z_][A-Z0-9_]*)\s*($Compare)\s*($LvalOrFunc)/) { - my $lead = $1; - my $const = $2; - my $comp = $3; - my $to = $4; - my $newcomp = $comp; - if ($lead !~ /(?:$Operators|\.)\s*$/ && - $to !~ /^(?:Constant|[A-Z_][A-Z0-9_]*)$/ && - WARN("CONSTANT_COMPARISON", - "Comparisons should place the constant on the right side of the test\n" . $herecurr) && - $fix) { - if ($comp eq "<") { - $newcomp = ">"; - } elsif ($comp eq "<=") { - $newcomp = ">="; - } elsif ($comp eq ">") { - $newcomp = "<"; - } elsif ($comp eq ">=") { - $newcomp = "<="; - } - $fixed[$fixlinenr] =~ s/\(\s*\Q$const\E\s*$Compare\s*\Q$to\E\s*\)/($to $newcomp $const)/; - } - } - -# Return of what appears to be an errno should normally be negative - if ($sline =~ /\breturn(?:\s*\(+\s*|\s+)(E[A-Z]+)(?:\s*\)+\s*|\s*)[;:,]/) { - my $name = $1; - if ($name ne 'EOF' && $name ne 'ERROR' && $name !~ /^EPOLL/) { - WARN("USE_NEGATIVE_ERRNO", - "return of an errno should typically be negative (ie: return -$1)\n" . $herecurr); - } - } - -# Need a space before open parenthesis after if, while etc - if ($line =~ /\b(if|while|for|switch)\(/) { - if (ERROR("SPACING", - "space required before the open parenthesis '('\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ - s/\b(if|while|for|switch)\(/$1 \(/; - } - } - -# Check for illegal assignment in if conditional -- and check for trailing -# statements after the conditional. - if ($line =~ /do\s*(?!{)/) { - ($stat, $cond, $line_nr_next, $remain_next, $off_next) = - ctx_statement_block($linenr, $realcnt, 0) - if (!defined $stat); - my ($stat_next) = ctx_statement_block($line_nr_next, - $remain_next, $off_next); - $stat_next =~ s/\n./\n /g; - ##print "stat<$stat> stat_next<$stat_next>\n"; - - if ($stat_next =~ /^\s*while\b/) { - # If the statement carries leading newlines, - # then count those as offsets. - my ($whitespace) = - ($stat_next =~ /^((?:\s*\n[+-])*\s*)/s); - my $offset = - statement_rawlines($whitespace) - 1; - - $suppress_whiletrailers{$line_nr_next + - $offset} = 1; - } - } - if (!defined $suppress_whiletrailers{$linenr} && - defined($stat) && defined($cond) && - $line =~ /\b(?:if|while|for)\s*\(/ && $line !~ /^.\s*#/) { - my ($s, $c) = ($stat, $cond); - - if ($c =~ /\bif\s*\(.*[^<>!=]=[^=].*/s) { - if (ERROR("ASSIGN_IN_IF", - "do not use assignment in if condition\n" . $herecurr) && - $fix && $perl_version_ok) { - if ($rawline =~ /^\+(\s+)if\s*\(\s*(\!)?\s*\(\s*(($Lval)\s*=\s*$LvalOrFunc)\s*\)\s*(?:($Compare)\s*($FuncArg))?\s*\)\s*(\{)?\s*$/) { - my $space = $1; - my $not = $2; - my $statement = $3; - my $assigned = $4; - my $test = $8; - my $against = $9; - my $brace = $15; - fix_delete_line($fixlinenr, $rawline); - fix_insert_line($fixlinenr, "$space$statement;"); - my $newline = "${space}if ("; - $newline .= '!' if defined($not); - $newline .= '(' if (defined $not && defined($test) && defined($against)); - $newline .= "$assigned"; - $newline .= " $test $against" if (defined($test) && defined($against)); - $newline .= ')' if (defined $not && defined($test) && defined($against)); - $newline .= ')'; - $newline .= " {" if (defined($brace)); - fix_insert_line($fixlinenr + 1, $newline); - } - } - } - - # Find out what is on the end of the line after the - # conditional. - substr($s, 0, length($c), ''); - $s =~ s/\n.*//g; - $s =~ s/$;//g; # Remove any comments - if (length($c) && $s !~ /^\s*{?\s*\\*\s*$/ && - $c !~ /}\s*while\s*/) - { - # Find out how long the conditional actually is. - my @newlines = ($c =~ /\n/gs); - my $cond_lines = 1 + $#newlines; - my $stat_real = ''; - - $stat_real = raw_line($linenr, $cond_lines) - . "\n" if ($cond_lines); - if (defined($stat_real) && $cond_lines > 1) { - $stat_real = "[...]\n$stat_real"; - } - - ERROR("TRAILING_STATEMENTS", - "trailing statements should be on next line\n" . $herecurr . $stat_real); - } - } - -# Check for bitwise tests written as boolean - if ($line =~ / - (?: - (?:\[|\(|\&\&|\|\|) - \s*0[xX][0-9]+\s* - (?:\&\&|\|\|) - | - (?:\&\&|\|\|) - \s*0[xX][0-9]+\s* - (?:\&\&|\|\||\)|\]) - )/x) - { - WARN("HEXADECIMAL_BOOLEAN_TEST", - "boolean test with hexadecimal, perhaps just 1 \& or \|?\n" . $herecurr); - } - -# if and else should not have general statements after it - if ($line =~ /^.\s*(?:}\s*)?else\b(.*)/) { - my $s = $1; - $s =~ s/$;//g; # Remove any comments - if ($s !~ /^\s*(?:\sif|(?:{|)\s*\\?\s*$)/) { - ERROR("TRAILING_STATEMENTS", - "trailing statements should be on next line\n" . $herecurr); - } - } -# if should not continue a brace - if ($line =~ /}\s*if\b/) { - ERROR("TRAILING_STATEMENTS", - "trailing statements should be on next line (or did you mean 'else if'?)\n" . - $herecurr); - } -# case and default should not have general statements after them - if ($line =~ /^.\s*(?:case\s*.*|default\s*):/g && - $line !~ /\G(?: - (?:\s*$;*)(?:\s*{)?(?:\s*$;*)(?:\s*\\)?\s*$| - \s*return\s+ - )/xg) - { - ERROR("TRAILING_STATEMENTS", - "trailing statements should be on next line\n" . $herecurr); - } - - # Check for }<nl>else {, these must be at the same - # indent level to be relevant to each other. - if ($prevline=~/}\s*$/ and $line=~/^.\s*else\s*/ && - $previndent == $indent) { - if (ERROR("ELSE_AFTER_BRACE", - "else should follow close brace '}'\n" . $hereprev) && - $fix && $prevline =~ /^\+/ && $line =~ /^\+/) { - fix_delete_line($fixlinenr - 1, $prevrawline); - fix_delete_line($fixlinenr, $rawline); - my $fixedline = $prevrawline; - $fixedline =~ s/}\s*$//; - if ($fixedline !~ /^\+\s*$/) { - fix_insert_line($fixlinenr, $fixedline); - } - $fixedline = $rawline; - $fixedline =~ s/^(.\s*)else/$1} else/; - fix_insert_line($fixlinenr, $fixedline); - } - } - - if ($prevline=~/}\s*$/ and $line=~/^.\s*while\s*/ && - $previndent == $indent) { - my ($s, $c) = ctx_statement_block($linenr, $realcnt, 0); - - # Find out what is on the end of the line after the - # conditional. - substr($s, 0, length($c), ''); - $s =~ s/\n.*//g; - - if ($s =~ /^\s*;/) { - if (ERROR("WHILE_AFTER_BRACE", - "while should follow close brace '}'\n" . $hereprev) && - $fix && $prevline =~ /^\+/ && $line =~ /^\+/) { - fix_delete_line($fixlinenr - 1, $prevrawline); - fix_delete_line($fixlinenr, $rawline); - my $fixedline = $prevrawline; - my $trailing = $rawline; - $trailing =~ s/^\+//; - $trailing = trim($trailing); - $fixedline =~ s/}\s*$/} $trailing/; - fix_insert_line($fixlinenr, $fixedline); - } - } - } - -#Specific variable tests - while ($line =~ m{($Constant|$Lval)}g) { - my $var = $1; - -#CamelCase - if ($var !~ /^$Constant$/ && - $var =~ /[A-Z][a-z]|[a-z][A-Z]/ && -#Ignore some autogenerated defines and enum values - $var !~ /^(?:[A-Z]+_){1,5}[A-Z]{1,3}[a-z]/ && -#Ignore Page<foo> variants - $var !~ /^(?:Clear|Set|TestClear|TestSet|)Page[A-Z]/ && -#Ignore SI style variants like nS, mV and dB -#(ie: max_uV, regulator_min_uA_show, RANGE_mA_VALUE) - $var !~ /^(?:[a-z0-9_]*|[A-Z0-9_]*)?_?[a-z][A-Z](?:_[a-z0-9_]+|_[A-Z0-9_]+)?$/ && -#Ignore some three character SI units explicitly, like MiB and KHz - $var !~ /^(?:[a-z_]*?)_?(?:[KMGT]iB|[KMGT]?Hz)(?:_[a-z_]+)?$/) { - while ($var =~ m{($Ident)}g) { - my $word = $1; - next if ($word !~ /[A-Z][a-z]|[a-z][A-Z]/); - if ($check) { - seed_camelcase_includes(); - if (!$file && !$camelcase_file_seeded) { - seed_camelcase_file($realfile); - $camelcase_file_seeded = 1; - } - } - if (!defined $camelcase{$word}) { - $camelcase{$word} = 1; - CHK("CAMELCASE", - "Avoid CamelCase: <$word>\n" . $herecurr); - } - } - } - } - -#no spaces allowed after \ in define - if ($line =~ /\#\s*define.*\\\s+$/) { - if (WARN("WHITESPACE_AFTER_LINE_CONTINUATION", - "Whitespace after \\ makes next lines useless\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/\s+$//; - } - } - -# warn if <asm/foo.h> is #included and <linux/foo.h> is available and includes -# itself <asm/foo.h> (uses RAW line) - if ($tree && $rawline =~ m{^.\s*\#\s*include\s*\<asm\/(.*)\.h\>}) { - my $file = "$1.h"; - my $checkfile = "include/linux/$file"; - if (-f "$root/$checkfile" && - $realfile ne $checkfile && - $1 !~ /$allowed_asm_includes/) - { - my $asminclude = `grep -Ec "#include\\s+<asm/$file>" $root/$checkfile`; - if ($asminclude > 0) { - if ($realfile =~ m{^arch/}) { - CHK("ARCH_INCLUDE_LINUX", - "Consider using #include <linux/$file> instead of <asm/$file>\n" . $herecurr); - } else { - WARN("INCLUDE_LINUX", - "Use #include <linux/$file> instead of <asm/$file>\n" . $herecurr); - } - } - } - } - -# multi-statement macros should be enclosed in a do while loop, grab the -# first statement and ensure its the whole macro if its not enclosed -# in a known good container - if ($realfile !~ m@/vmlinux.lds.h$@ && - $line =~ /^.\s*\#\s*define\s*$Ident(\()?/) { - my $ln = $linenr; - my $cnt = $realcnt; - my ($off, $dstat, $dcond, $rest); - my $ctx = ''; - my $has_flow_statement = 0; - my $has_arg_concat = 0; - ($dstat, $dcond, $ln, $cnt, $off) = - ctx_statement_block($linenr, $realcnt, 0); - $ctx = $dstat; - #print "dstat<$dstat> dcond<$dcond> cnt<$cnt> off<$off>\n"; - #print "LINE<$lines[$ln-1]> len<" . length($lines[$ln-1]) . "\n"; - - $has_flow_statement = 1 if ($ctx =~ /\b(goto|return)\b/); - $has_arg_concat = 1 if ($ctx =~ /\#\#/ && $ctx !~ /\#\#\s*(?:__VA_ARGS__|args)\b/); - - $dstat =~ s/^.\s*\#\s*define\s+$Ident(\([^\)]*\))?\s*//; - my $define_args = $1; - my $define_stmt = $dstat; - my @def_args = (); - - if (defined $define_args && $define_args ne "") { - $define_args = substr($define_args, 1, length($define_args) - 2); - $define_args =~ s/\s*//g; - $define_args =~ s/\\\+?//g; - @def_args = split(",", $define_args); - } - - $dstat =~ s/$;//g; - $dstat =~ s/\\\n.//g; - $dstat =~ s/^\s*//s; - $dstat =~ s/\s*$//s; - - # Flatten any parentheses and braces - while ($dstat =~ s/\([^\(\)]*\)/1u/ || - $dstat =~ s/\{[^\{\}]*\}/1u/ || - $dstat =~ s/.\[[^\[\]]*\]/1u/) - { - } - - # Flatten any obvious string concatenation. - while ($dstat =~ s/($String)\s*$Ident/$1/ || - $dstat =~ s/$Ident\s*($String)/$1/) - { - } - - # Make asm volatile uses seem like a generic function - $dstat =~ s/\b_*asm_*\s+_*volatile_*\b/asm_volatile/g; - - my $exceptions = qr{ - $Declare| - module_param_named| - MODULE_PARM_DESC| - DECLARE_PER_CPU| - DEFINE_PER_CPU| - __typeof__\(| - union| - struct| - \.$Ident\s*=\s*| - ^\"|\"$| - ^\[ - }x; - #print "REST<$rest> dstat<$dstat> ctx<$ctx>\n"; - - $ctx =~ s/\n*$//; - my $stmt_cnt = statement_rawlines($ctx); - my $herectx = get_stat_here($linenr, $stmt_cnt, $here); - - if ($dstat ne '' && - $dstat !~ /^(?:$Ident|-?$Constant),$/ && # 10, // foo(), - $dstat !~ /^(?:$Ident|-?$Constant);$/ && # foo(); - $dstat !~ /^[!~-]?(?:$Lval|$Constant)$/ && # 10 // foo() // !foo // ~foo // -foo // foo->bar // foo.bar->baz - $dstat !~ /^'X'$/ && $dstat !~ /^'XX'$/ && # character constants - $dstat !~ /$exceptions/ && - $dstat !~ /^\.$Ident\s*=/ && # .foo = - $dstat !~ /^(?:\#\s*$Ident|\#\s*$Constant)\s*$/ && # stringification #foo - $dstat !~ /^do\s*$Constant\s*while\s*$Constant;?$/ && # do {...} while (...); // do {...} while (...) - $dstat !~ /^while\s*$Constant\s*$Constant\s*$/ && # while (...) {...} - $dstat !~ /^for\s*$Constant$/ && # for (...) - $dstat !~ /^for\s*$Constant\s+(?:$Ident|-?$Constant)$/ && # for (...) bar() - $dstat !~ /^do\s*{/ && # do {... - $dstat !~ /^\(\{/ && # ({... - $ctx !~ /^.\s*#\s*define\s+TRACE_(?:SYSTEM|INCLUDE_FILE|INCLUDE_PATH)\b/) - { - if ($dstat =~ /^\s*if\b/) { - ERROR("MULTISTATEMENT_MACRO_USE_DO_WHILE", - "Macros starting with if should be enclosed by a do - while loop to avoid possible if/else logic defects\n" . "$herectx"); - } elsif ($dstat =~ /;/) { - ERROR("MULTISTATEMENT_MACRO_USE_DO_WHILE", - "Macros with multiple statements should be enclosed in a do - while loop\n" . "$herectx"); - } else { - ERROR("COMPLEX_MACRO", - "Macros with complex values should be enclosed in parentheses\n" . "$herectx"); - } - - } - - # Make $define_stmt single line, comment-free, etc - my @stmt_array = split('\n', $define_stmt); - my $first = 1; - $define_stmt = ""; - foreach my $l (@stmt_array) { - $l =~ s/\\$//; - if ($first) { - $define_stmt = $l; - $first = 0; - } elsif ($l =~ /^[\+ ]/) { - $define_stmt .= substr($l, 1); - } - } - $define_stmt =~ s/$;//g; - $define_stmt =~ s/\s+/ /g; - $define_stmt = trim($define_stmt); - -# check if any macro arguments are reused (ignore '...' and 'type') - foreach my $arg (@def_args) { - next if ($arg =~ /\.\.\./); - next if ($arg =~ /^type$/i); - my $tmp_stmt = $define_stmt; - $tmp_stmt =~ s/\b(__must_be_array|offsetof|sizeof|sizeof_field|__stringify|typeof|__typeof__|__builtin\w+|typecheck\s*\(\s*$Type\s*,|\#+)\s*\(*\s*$arg\s*\)*\b//g; - $tmp_stmt =~ s/\#+\s*$arg\b//g; - $tmp_stmt =~ s/\b$arg\s*\#\#//g; - my $use_cnt = () = $tmp_stmt =~ /\b$arg\b/g; - if ($use_cnt > 1) { - CHK("MACRO_ARG_REUSE", - "Macro argument reuse '$arg' - possible side-effects?\n" . "$herectx"); - } -# check if any macro arguments may have other precedence issues - if ($tmp_stmt =~ m/($Operators)?\s*\b$arg\b\s*($Operators)?/m && - ((defined($1) && $1 ne ',') || - (defined($2) && $2 ne ','))) { - CHK("MACRO_ARG_PRECEDENCE", - "Macro argument '$arg' may be better as '($arg)' to avoid precedence issues\n" . "$herectx"); - } - } - -# check for macros with flow control, but without ## concatenation -# ## concatenation is commonly a macro that defines a function so ignore those - if ($has_flow_statement && !$has_arg_concat) { - my $cnt = statement_rawlines($ctx); - my $herectx = get_stat_here($linenr, $cnt, $here); - - WARN("MACRO_WITH_FLOW_CONTROL", - "Macros with flow control statements should be avoided\n" . "$herectx"); - } - -# check for line continuations outside of #defines, preprocessor #, and asm - - } else { - if ($prevline !~ /^..*\\$/ && - $line !~ /^\+\s*\#.*\\$/ && # preprocessor - $line !~ /^\+.*\b(__asm__|asm)\b.*\\$/ && # asm - $line =~ /^\+.*\\$/) { - WARN("LINE_CONTINUATIONS", - "Avoid unnecessary line continuations\n" . $herecurr); - } - } - -# do {} while (0) macro tests: -# single-statement macros do not need to be enclosed in do while (0) loop, -# macro should not end with a semicolon - if ($perl_version_ok && - $realfile !~ m@/vmlinux.lds.h$@ && - $line =~ /^.\s*\#\s*define\s+$Ident(\()?/) { - my $ln = $linenr; - my $cnt = $realcnt; - my ($off, $dstat, $dcond, $rest); - my $ctx = ''; - ($dstat, $dcond, $ln, $cnt, $off) = - ctx_statement_block($linenr, $realcnt, 0); - $ctx = $dstat; - - $dstat =~ s/\\\n.//g; - $dstat =~ s/$;/ /g; - - if ($dstat =~ /^\+\s*#\s*define\s+$Ident\s*${balanced_parens}\s*do\s*{(.*)\s*}\s*while\s*\(\s*0\s*\)\s*([;\s]*)\s*$/) { - my $stmts = $2; - my $semis = $3; - - $ctx =~ s/\n*$//; - my $cnt = statement_rawlines($ctx); - my $herectx = get_stat_here($linenr, $cnt, $here); - - if (($stmts =~ tr/;/;/) == 1 && - $stmts !~ /^\s*(if|while|for|switch)\b/) { - WARN("SINGLE_STATEMENT_DO_WHILE_MACRO", - "Single statement macros should not use a do {} while (0) loop\n" . "$herectx"); - } - if (defined $semis && $semis ne "") { - WARN("DO_WHILE_MACRO_WITH_TRAILING_SEMICOLON", - "do {} while (0) macros should not be semicolon terminated\n" . "$herectx"); - } - } elsif ($dstat =~ /^\+\s*#\s*define\s+$Ident.*;\s*$/) { - $ctx =~ s/\n*$//; - my $cnt = statement_rawlines($ctx); - my $herectx = get_stat_here($linenr, $cnt, $here); - - WARN("TRAILING_SEMICOLON", - "macros should not use a trailing semicolon\n" . "$herectx"); - } - } - -# check for redundant bracing round if etc - if ($line =~ /(^.*)\bif\b/ && $1 !~ /else\s*$/) { - my ($level, $endln, @chunks) = - ctx_statement_full($linenr, $realcnt, 1); - #print "chunks<$#chunks> linenr<$linenr> endln<$endln> level<$level>\n"; - #print "APW: <<$chunks[1][0]>><<$chunks[1][1]>>\n"; - if ($#chunks > 0 && $level == 0) { - my @allowed = (); - my $allow = 0; - my $seen = 0; - my $herectx = $here . "\n"; - my $ln = $linenr - 1; - for my $chunk (@chunks) { - my ($cond, $block) = @{$chunk}; - - # If the condition carries leading newlines, then count those as offsets. - my ($whitespace) = ($cond =~ /^((?:\s*\n[+-])*\s*)/s); - my $offset = statement_rawlines($whitespace) - 1; - - $allowed[$allow] = 0; - #print "COND<$cond> whitespace<$whitespace> offset<$offset>\n"; - - # We have looked at and allowed this specific line. - $suppress_ifbraces{$ln + $offset} = 1; - - $herectx .= "$rawlines[$ln + $offset]\n[...]\n"; - $ln += statement_rawlines($block) - 1; - - substr($block, 0, length($cond), ''); - - $seen++ if ($block =~ /^\s*{/); - - #print "cond<$cond> block<$block> allowed<$allowed[$allow]>\n"; - if (statement_lines($cond) > 1) { - #print "APW: ALLOWED: cond<$cond>\n"; - $allowed[$allow] = 1; - } - if ($block =~/\b(?:if|for|while)\b/) { - #print "APW: ALLOWED: block<$block>\n"; - $allowed[$allow] = 1; - } - if (statement_block_size($block) > 1) { - #print "APW: ALLOWED: lines block<$block>\n"; - $allowed[$allow] = 1; - } - $allow++; - } - if ($seen) { - my $sum_allowed = 0; - foreach (@allowed) { - $sum_allowed += $_; - } - if ($sum_allowed == 0) { - WARN("BRACES", - "braces {} are not necessary for any arm of this statement\n" . $herectx); - } elsif ($sum_allowed != $allow && - $seen != $allow) { - CHK("BRACES", - "braces {} should be used on all arms of this statement\n" . $herectx); - } - } - } - } - if (!defined $suppress_ifbraces{$linenr - 1} && - $line =~ /\b(if|while|for|else)\b/) { - my $allowed = 0; - - # Check the pre-context. - if (substr($line, 0, $-[0]) =~ /(\}\s*)$/) { - #print "APW: ALLOWED: pre<$1>\n"; - $allowed = 1; - } - - my ($level, $endln, @chunks) = - ctx_statement_full($linenr, $realcnt, $-[0]); - - # Check the condition. - my ($cond, $block) = @{$chunks[0]}; - #print "CHECKING<$linenr> cond<$cond> block<$block>\n"; - if (defined $cond) { - substr($block, 0, length($cond), ''); - } - if (statement_lines($cond) > 1) { - #print "APW: ALLOWED: cond<$cond>\n"; - $allowed = 1; - } - if ($block =~/\b(?:if|for|while)\b/) { - #print "APW: ALLOWED: block<$block>\n"; - $allowed = 1; - } - if (statement_block_size($block) > 1) { - #print "APW: ALLOWED: lines block<$block>\n"; - $allowed = 1; - } - # Check the post-context. - if (defined $chunks[1]) { - my ($cond, $block) = @{$chunks[1]}; - if (defined $cond) { - substr($block, 0, length($cond), ''); - } - if ($block =~ /^\s*\{/) { - #print "APW: ALLOWED: chunk-1 block<$block>\n"; - $allowed = 1; - } - } - if ($level == 0 && $block =~ /^\s*\{/ && !$allowed) { - my $cnt = statement_rawlines($block); - my $herectx = get_stat_here($linenr, $cnt, $here); - - WARN("BRACES", - "braces {} are not necessary for single statement blocks\n" . $herectx); - } - } - -# check for single line unbalanced braces - if ($sline =~ /^.\s*\}\s*else\s*$/ || - $sline =~ /^.\s*else\s*\{\s*$/) { - CHK("BRACES", "Unbalanced braces around else statement\n" . $herecurr); - } - -# check for unnecessary blank lines around braces - if (($line =~ /^.\s*}\s*$/ && $prevrawline =~ /^.\s*$/)) { - if (CHK("BRACES", - "Blank lines aren't necessary before a close brace '}'\n" . $hereprev) && - $fix && $prevrawline =~ /^\+/) { - fix_delete_line($fixlinenr - 1, $prevrawline); - } - } - if (($rawline =~ /^.\s*$/ && $prevline =~ /^..*{\s*$/)) { - if (CHK("BRACES", - "Blank lines aren't necessary after an open brace '{'\n" . $hereprev) && - $fix) { - fix_delete_line($fixlinenr, $rawline); - } - } - -# no volatiles please - my $asm_volatile = qr{\b(__asm__|asm)\s+(__volatile__|volatile)\b}; - if ($line =~ /\bvolatile\b/ && $line !~ /$asm_volatile/) { - WARN("VOLATILE", - "Use of volatile is usually wrong: see Documentation/process/volatile-considered-harmful.rst\n" . $herecurr); - } - -# Check for user-visible strings broken across lines, which breaks the ability -# to grep for the string. Make exceptions when the previous string ends in a -# newline (multiple lines in one string constant) or '\t', '\r', ';', or '{' -# (common in inline assembly) or is a octal \123 or hexadecimal \xaf value - if ($line =~ /^\+\s*$String/ && - $prevline =~ /"\s*$/ && - $prevrawline !~ /(?:\\(?:[ntr]|[0-7]{1,3}|x[0-9a-fA-F]{1,2})|;\s*|\{\s*)"\s*$/) { - if (WARN("SPLIT_STRING", - "quoted string split across lines\n" . $hereprev) && - $fix && - $prevrawline =~ /^\+.*"\s*$/ && - $last_coalesced_string_linenr != $linenr - 1) { - my $extracted_string = get_quoted_string($line, $rawline); - my $comma_close = ""; - if ($rawline =~ /\Q$extracted_string\E(\s*\)\s*;\s*$|\s*,\s*)/) { - $comma_close = $1; - } - - fix_delete_line($fixlinenr - 1, $prevrawline); - fix_delete_line($fixlinenr, $rawline); - my $fixedline = $prevrawline; - $fixedline =~ s/"\s*$//; - $fixedline .= substr($extracted_string, 1) . trim($comma_close); - fix_insert_line($fixlinenr - 1, $fixedline); - $fixedline = $rawline; - $fixedline =~ s/\Q$extracted_string\E\Q$comma_close\E//; - if ($fixedline !~ /\+\s*$/) { - fix_insert_line($fixlinenr, $fixedline); - } - $last_coalesced_string_linenr = $linenr; - } - } - -# check for missing a space in a string concatenation - if ($prevrawline =~ /[^\\]\w"$/ && $rawline =~ /^\+[\t ]+"\w/) { - WARN('MISSING_SPACE', - "break quoted strings at a space character\n" . $hereprev); - } - -# check for an embedded function name in a string when the function is known -# This does not work very well for -f --file checking as it depends on patch -# context providing the function name or a single line form for in-file -# function declarations - if (!$SOF && - $line =~ /^\+.*$String/ && - defined($context_function) && - get_quoted_string($line, $rawline) =~ /\b$context_function\b/ && - length(get_quoted_string($line, $rawline)) != (length($context_function) + 2)) { - WARN("EMBEDDED_FUNCTION_NAME", - "Prefer using '\"%s...\", __func__' to using '$context_function', this function's name, in a string\n" . $herecurr); - } - -# check for unnecessary function tracing like uses -# This does not use $logFunctions because there are many instances like -# 'dprintk(FOO, "%s()\n", __func__);' which do not match $logFunctions - if ($rawline =~ /^\+.*\([^"]*"$tracing_logging_tags{0,3}%s(?:\s*\(\s*\)\s*)?$tracing_logging_tags{0,3}(?:\\n)?"\s*,\s*__func__\s*\)\s*;/) { - if (WARN("TRACING_LOGGING", - "Unnecessary ftrace-like logging - prefer using ftrace\n" . $herecurr) && - $fix) { - fix_delete_line($fixlinenr, $rawline); - } - } - -# check for spaces before a quoted newline - if ($rawline =~ /^.*\".*\s\\n/) { - if (WARN("QUOTED_WHITESPACE_BEFORE_NEWLINE", - "unnecessary whitespace before a quoted newline\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/^(\+.*\".*)\s+\\n/$1\\n/; - } - - } - -# concatenated string without spaces between elements - if ($line =~ /$String[A-Z_]/ || - ($line =~ /([A-Za-z0-9_]+)$String/ && $1 !~ /^[Lu]$/)) { - if (CHK("CONCATENATED_STRING", - "Concatenated strings should use spaces between elements\n" . $herecurr) && - $fix) { - while ($line =~ /($String)/g) { - my $extracted_string = substr($rawline, $-[0], $+[0] - $-[0]); - $fixed[$fixlinenr] =~ s/\Q$extracted_string\E([A-Za-z0-9_])/$extracted_string $1/; - $fixed[$fixlinenr] =~ s/([A-Za-z0-9_])\Q$extracted_string\E/$1 $extracted_string/; - } - } - } - -# uncoalesced string fragments - if ($line =~ /$String\s*[Lu]?"/) { - if (WARN("STRING_FRAGMENTS", - "Consecutive strings are generally better as a single string\n" . $herecurr) && - $fix) { - while ($line =~ /($String)(?=\s*")/g) { - my $extracted_string = substr($rawline, $-[0], $+[0] - $-[0]); - $fixed[$fixlinenr] =~ s/\Q$extracted_string\E\s*"/substr($extracted_string, 0, -1)/e; - } - } - } - -# check for non-standard and hex prefixed decimal printf formats - my $show_L = 1; #don't show the same defect twice - my $show_Z = 1; - while ($line =~ /(?:^|")([X\t]*)(?:"|$)/g) { - my $string = substr($rawline, $-[1], $+[1] - $-[1]); - $string =~ s/%%/__/g; - # check for %L - if ($show_L && $string =~ /%[\*\d\.\$]*L([diouxX])/) { - WARN("PRINTF_L", - "\%L$1 is non-standard C, use %ll$1\n" . $herecurr); - $show_L = 0; - } - # check for %Z - if ($show_Z && $string =~ /%[\*\d\.\$]*Z([diouxX])/) { - WARN("PRINTF_Z", - "%Z$1 is non-standard C, use %z$1\n" . $herecurr); - $show_Z = 0; - } - # check for 0x<decimal> - if ($string =~ /0x%[\*\d\.\$\Llzth]*[diou]/) { - ERROR("PRINTF_0XDECIMAL", - "Prefixing 0x with decimal output is defective\n" . $herecurr); - } - } - -# check for line continuations in quoted strings with odd counts of " - if ($rawline =~ /\\$/ && $sline =~ tr/"/"/ % 2) { - WARN("LINE_CONTINUATIONS", - "Avoid line continuations in quoted strings\n" . $herecurr); - } - -# warn about #if 0 - if ($line =~ /^.\s*\#\s*if\s+0\b/) { - WARN("IF_0", - "Consider removing the code enclosed by this #if 0 and its #endif\n" . $herecurr); - } - -# warn about #if 1 - if ($line =~ /^.\s*\#\s*if\s+1\b/) { - WARN("IF_1", - "Consider removing the #if 1 and its #endif\n" . $herecurr); - } - -# check for needless "if (<foo>) fn(<foo>)" uses - if ($prevline =~ /\bif\s*\(\s*($Lval)\s*\)/) { - my $tested = quotemeta($1); - my $expr = '\s*\(\s*' . $tested . '\s*\)\s*;'; - if ($line =~ /\b(kfree|usb_free_urb|debugfs_remove(?:_recursive)?|(?:kmem_cache|mempool|dma_pool)_destroy)$expr/) { - my $func = $1; - if (WARN('NEEDLESS_IF', - "$func(NULL) is safe and this check is probably not required\n" . $hereprev) && - $fix) { - my $do_fix = 1; - my $leading_tabs = ""; - my $new_leading_tabs = ""; - if ($lines[$linenr - 2] =~ /^\+(\t*)if\s*\(\s*$tested\s*\)\s*$/) { - $leading_tabs = $1; - } else { - $do_fix = 0; - } - if ($lines[$linenr - 1] =~ /^\+(\t+)$func\s*\(\s*$tested\s*\)\s*;\s*$/) { - $new_leading_tabs = $1; - if (length($leading_tabs) + 1 ne length($new_leading_tabs)) { - $do_fix = 0; - } - } else { - $do_fix = 0; - } - if ($do_fix) { - fix_delete_line($fixlinenr - 1, $prevrawline); - $fixed[$fixlinenr] =~ s/^\+$new_leading_tabs/\+$leading_tabs/; - } - } - } - } - -# check for unnecessary "Out of Memory" messages - if ($line =~ /^\+.*\b$logFunctions\s*\(/ && - $prevline =~ /^[ \+]\s*if\s*\(\s*(\!\s*|NULL\s*==\s*)?($Lval)(\s*==\s*NULL\s*)?\s*\)/ && - (defined $1 || defined $3) && - $linenr > 3) { - my $testval = $2; - my $testline = $lines[$linenr - 3]; - - my ($s, $c) = ctx_statement_block($linenr - 3, $realcnt, 0); -# print("line: <$line>\nprevline: <$prevline>\ns: <$s>\nc: <$c>\n\n\n"); - - if ($s =~ /(?:^|\n)[ \+]\s*(?:$Type\s*)?\Q$testval\E\s*=\s*(?:\([^\)]*\)\s*)?\s*$allocFunctions\s*\(/ && - $s !~ /\b__GFP_NOWARN\b/ ) { - WARN("OOM_MESSAGE", - "Possible unnecessary 'out of memory' message\n" . $hereprev); - } - } - -# check for logging functions with KERN_<LEVEL> - if ($line !~ /printk(?:_ratelimited|_once)?\s*\(/ && - $line =~ /\b$logFunctions\s*\(.*\b(KERN_[A-Z]+)\b/) { - my $level = $1; - if (WARN("UNNECESSARY_KERN_LEVEL", - "Possible unnecessary $level\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/\s*$level\s*//; - } - } - -# check for logging continuations - if ($line =~ /\bprintk\s*\(\s*KERN_CONT\b|\bpr_cont\s*\(/) { - WARN("LOGGING_CONTINUATION", - "Avoid logging continuation uses where feasible\n" . $herecurr); - } - -# check for unnecessary use of %h[xudi] and %hh[xudi] in logging functions - if (defined $stat && - $line =~ /\b$logFunctions\s*\(/ && - index($stat, '"') >= 0) { - my $lc = $stat =~ tr@\n@@; - $lc = $lc + $linenr; - my $stat_real = get_stat_real($linenr, $lc); - pos($stat_real) = index($stat_real, '"'); - while ($stat_real =~ /[^\"%]*(%[\#\d\.\*\-]*(h+)[idux])/g) { - my $pspec = $1; - my $h = $2; - my $lineoff = substr($stat_real, 0, $-[1]) =~ tr@\n@@; - if (WARN("UNNECESSARY_MODIFIER", - "Integer promotion: Using '$h' in '$pspec' is unnecessary\n" . "$here\n$stat_real\n") && - $fix && $fixed[$fixlinenr + $lineoff] =~ /^\+/) { - my $nspec = $pspec; - $nspec =~ s/h//g; - $fixed[$fixlinenr + $lineoff] =~ s/\Q$pspec\E/$nspec/; - } - } - } - -# check for mask then right shift without a parentheses - if ($perl_version_ok && - $line =~ /$LvalOrFunc\s*\&\s*($LvalOrFunc)\s*>>/ && - $4 !~ /^\&/) { # $LvalOrFunc may be &foo, ignore if so - WARN("MASK_THEN_SHIFT", - "Possible precedence defect with mask then right shift - may need parentheses\n" . $herecurr); - } - -# check for pointer comparisons to NULL - if ($perl_version_ok) { - while ($line =~ /\b$LvalOrFunc\s*(==|\!=)\s*NULL\b/g) { - my $val = $1; - my $equal = "!"; - $equal = "" if ($4 eq "!="); - if (CHK("COMPARISON_TO_NULL", - "Comparison to NULL could be written \"${equal}${val}\"\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/\b\Q$val\E\s*(?:==|\!=)\s*NULL\b/$equal$val/; - } - } - } - -# check for bad placement of section $InitAttribute (e.g.: __initdata) - if ($line =~ /(\b$InitAttribute\b)/) { - my $attr = $1; - if ($line =~ /^\+\s*static\s+(?:const\s+)?(?:$attr\s+)?($NonptrTypeWithAttr)\s+(?:$attr\s+)?($Ident(?:\[[^]]*\])?)\s*[=;]/) { - my $ptr = $1; - my $var = $2; - if ((($ptr =~ /\b(union|struct)\s+$attr\b/ && - ERROR("MISPLACED_INIT", - "$attr should be placed after $var\n" . $herecurr)) || - ($ptr !~ /\b(union|struct)\s+$attr\b/ && - WARN("MISPLACED_INIT", - "$attr should be placed after $var\n" . $herecurr))) && - $fix) { - $fixed[$fixlinenr] =~ s/(\bstatic\s+(?:const\s+)?)(?:$attr\s+)?($NonptrTypeWithAttr)\s+(?:$attr\s+)?($Ident(?:\[[^]]*\])?)\s*([=;])\s*/"$1" . trim(string_find_replace($2, "\\s*$attr\\s*", " ")) . " " . trim(string_find_replace($3, "\\s*$attr\\s*", "")) . " $attr" . ("$4" eq ";" ? ";" : " = ")/e; - } - } - } - -# check for $InitAttributeData (ie: __initdata) with const - if ($line =~ /\bconst\b/ && $line =~ /($InitAttributeData)/) { - my $attr = $1; - $attr =~ /($InitAttributePrefix)(.*)/; - my $attr_prefix = $1; - my $attr_type = $2; - if (ERROR("INIT_ATTRIBUTE", - "Use of const init definition must use ${attr_prefix}initconst\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ - s/$InitAttributeData/${attr_prefix}initconst/; - } - } - -# check for $InitAttributeConst (ie: __initconst) without const - if ($line !~ /\bconst\b/ && $line =~ /($InitAttributeConst)/) { - my $attr = $1; - if (ERROR("INIT_ATTRIBUTE", - "Use of $attr requires a separate use of const\n" . $herecurr) && - $fix) { - my $lead = $fixed[$fixlinenr] =~ - /(^\+\s*(?:static\s+))/; - $lead = rtrim($1); - $lead = "$lead " if ($lead !~ /^\+$/); - $lead = "${lead}const "; - $fixed[$fixlinenr] =~ s/(^\+\s*(?:static\s+))/$lead/; - } - } - -# check for __read_mostly with const non-pointer (should just be const) - if ($line =~ /\b__read_mostly\b/ && - $line =~ /($Type)\s*$Ident/ && $1 !~ /\*\s*$/ && $1 =~ /\bconst\b/) { - if (ERROR("CONST_READ_MOSTLY", - "Invalid use of __read_mostly with const type\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/\s+__read_mostly\b//; - } - } - -# don't use __constant_<foo> functions outside of include/uapi/ - if ($realfile !~ m@^include/uapi/@ && - $line =~ /(__constant_(?:htons|ntohs|[bl]e(?:16|32|64)_to_cpu|cpu_to_[bl]e(?:16|32|64)))\s*\(/) { - my $constant_func = $1; - my $func = $constant_func; - $func =~ s/^__constant_//; - if (WARN("CONSTANT_CONVERSION", - "$constant_func should be $func\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/\b$constant_func\b/$func/g; - } - } - -# prefer usleep_range over udelay - if ($line =~ /\budelay\s*\(\s*(\d+)\s*\)/) { - my $delay = $1; - # ignore udelay's < 10, however - if (! ($delay < 10) ) { - CHK("USLEEP_RANGE", - "usleep_range is preferred over udelay; see Documentation/timers/timers-howto.rst\n" . $herecurr); - } - if ($delay > 2000) { - WARN("LONG_UDELAY", - "long udelay - prefer mdelay; see arch/arm/include/asm/delay.h\n" . $herecurr); - } - } - -# warn about unexpectedly long msleep's - if ($line =~ /\bmsleep\s*\((\d+)\);/) { - if ($1 < 20) { - WARN("MSLEEP", - "msleep < 20ms can sleep for up to 20ms; see Documentation/timers/timers-howto.rst\n" . $herecurr); - } - } - -# check for comparisons of jiffies - if ($line =~ /\bjiffies\s*$Compare|$Compare\s*jiffies\b/) { - WARN("JIFFIES_COMPARISON", - "Comparing jiffies is almost always wrong; prefer time_after, time_before and friends\n" . $herecurr); - } - -# check for comparisons of get_jiffies_64() - if ($line =~ /\bget_jiffies_64\s*\(\s*\)\s*$Compare|$Compare\s*get_jiffies_64\s*\(\s*\)/) { - WARN("JIFFIES_COMPARISON", - "Comparing get_jiffies_64() is almost always wrong; prefer time_after64, time_before64 and friends\n" . $herecurr); - } - -# warn about #ifdefs in C files -# if ($line =~ /^.\s*\#\s*if(|n)def/ && ($realfile =~ /\.c$/)) { -# print "#ifdef in C files should be avoided\n"; -# print "$herecurr"; -# $clean = 0; -# } - -# warn about spacing in #ifdefs - if ($line =~ /^.\s*\#\s*(ifdef|ifndef|elif)\s\s+/) { - if (ERROR("SPACING", - "exactly one space required after that #$1\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ - s/^(.\s*\#\s*(ifdef|ifndef|elif))\s{2,}/$1 /; - } - - } - -# check for spinlock_t definitions without a comment. - if ($line =~ /^.\s*(struct\s+mutex|spinlock_t)\s+\S+;/ || - $line =~ /^.\s*(DEFINE_MUTEX)\s*\(/) { - my $which = $1; - if (!ctx_has_comment($first_line, $linenr)) { - CHK("UNCOMMENTED_DEFINITION", - "$1 definition without comment\n" . $herecurr); - } - } -# check for memory barriers without a comment. - - my $barriers = qr{ - mb| - rmb| - wmb - }x; - my $barrier_stems = qr{ - mb__before_atomic| - mb__after_atomic| - store_release| - load_acquire| - store_mb| - (?:$barriers) - }x; - my $all_barriers = qr{ - (?:$barriers)| - smp_(?:$barrier_stems)| - virt_(?:$barrier_stems) - }x; - - if ($line =~ /\b(?:$all_barriers)\s*\(/) { - if (!ctx_has_comment($first_line, $linenr)) { - WARN("MEMORY_BARRIER", - "memory barrier without comment\n" . $herecurr); - } - } - - my $underscore_smp_barriers = qr{__smp_(?:$barrier_stems)}x; - - if ($realfile !~ m@^include/asm-generic/@ && - $realfile !~ m@/barrier\.h$@ && - $line =~ m/\b(?:$underscore_smp_barriers)\s*\(/ && - $line !~ m/^.\s*\#\s*define\s+(?:$underscore_smp_barriers)\s*\(/) { - WARN("MEMORY_BARRIER", - "__smp memory barriers shouldn't be used outside barrier.h and asm-generic\n" . $herecurr); - } - -# check for waitqueue_active without a comment. - if ($line =~ /\bwaitqueue_active\s*\(/) { - if (!ctx_has_comment($first_line, $linenr)) { - WARN("WAITQUEUE_ACTIVE", - "waitqueue_active without comment\n" . $herecurr); - } - } - -# check for data_race without a comment. - if ($line =~ /\bdata_race\s*\(/) { - if (!ctx_has_comment($first_line, $linenr)) { - WARN("DATA_RACE", - "data_race without comment\n" . $herecurr); - } - } - -# check of hardware specific defines - if ($line =~ m@^.\s*\#\s*if.*\b(__i386__|__powerpc64__|__sun__|__s390x__)\b@ && $realfile !~ m@include/asm-@) { - CHK("ARCH_DEFINES", - "architecture specific defines should be avoided\n" . $herecurr); - } - -# check that the storage class is not after a type - if ($line =~ /\b($Type)\s+($Storage)\b/) { - WARN("STORAGE_CLASS", - "storage class '$2' should be located before type '$1'\n" . $herecurr); - } -# Check that the storage class is at the beginning of a declaration - if ($line =~ /\b$Storage\b/ && - $line !~ /^.\s*$Storage/ && - $line =~ /^.\s*(.+?)\$Storage\s/ && - $1 !~ /[\,\)]\s*$/) { - WARN("STORAGE_CLASS", - "storage class should be at the beginning of the declaration\n" . $herecurr); - } - -# check the location of the inline attribute, that it is between -# storage class and type. - if ($line =~ /\b$Type\s+$Inline\b/ || - $line =~ /\b$Inline\s+$Storage\b/) { - ERROR("INLINE_LOCATION", - "inline keyword should sit between storage class and type\n" . $herecurr); - } - -# Check for __inline__ and __inline, prefer inline - if ($realfile !~ m@\binclude/uapi/@ && - $line =~ /\b(__inline__|__inline)\b/) { - if (WARN("INLINE", - "plain inline is preferred over $1\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/\b(__inline__|__inline)\b/inline/; - - } - } - -# Check for compiler attributes - if ($realfile !~ m@\binclude/uapi/@ && - $rawline =~ /\b__attribute__\s*\(\s*($balanced_parens)\s*\)/) { - my $attr = $1; - $attr =~ s/\s*\(\s*(.*)\)\s*/$1/; - - my %attr_list = ( - "alias" => "__alias", - "aligned" => "__aligned", - "always_inline" => "__always_inline", - "assume_aligned" => "__assume_aligned", - "cold" => "__cold", - "const" => "__attribute_const__", - "copy" => "__copy", - "designated_init" => "__designated_init", - "externally_visible" => "__visible", - "format" => "printf|scanf", - "gnu_inline" => "__gnu_inline", - "malloc" => "__malloc", - "mode" => "__mode", - "no_caller_saved_registers" => "__no_caller_saved_registers", - "noclone" => "__noclone", - "noinline" => "noinline", - "nonstring" => "__nonstring", - "noreturn" => "__noreturn", - "packed" => "__packed", - "pure" => "__pure", - "section" => "__section", - "used" => "__used", - "weak" => "__weak" - ); - - while ($attr =~ /\s*(\w+)\s*(${balanced_parens})?/g) { - my $orig_attr = $1; - my $params = ''; - $params = $2 if defined($2); - my $curr_attr = $orig_attr; - $curr_attr =~ s/^[\s_]+|[\s_]+$//g; - if (exists($attr_list{$curr_attr})) { - my $new = $attr_list{$curr_attr}; - if ($curr_attr eq "format" && $params) { - $params =~ /^\s*\(\s*(\w+)\s*,\s*(.*)/; - $new = "__$1\($2"; - } else { - $new = "$new$params"; - } - if (WARN("PREFER_DEFINED_ATTRIBUTE_MACRO", - "Prefer $new over __attribute__(($orig_attr$params))\n" . $herecurr) && - $fix) { - my $remove = "\Q$orig_attr\E" . '\s*' . "\Q$params\E" . '(?:\s*,\s*)?'; - $fixed[$fixlinenr] =~ s/$remove//; - $fixed[$fixlinenr] =~ s/\b__attribute__/$new __attribute__/; - $fixed[$fixlinenr] =~ s/\}\Q$new\E/} $new/; - $fixed[$fixlinenr] =~ s/ __attribute__\s*\(\s*\(\s*\)\s*\)//; - } - } - } - - # Check for __attribute__ unused, prefer __always_unused or __maybe_unused - if ($attr =~ /^_*unused/) { - WARN("PREFER_DEFINED_ATTRIBUTE_MACRO", - "__always_unused or __maybe_unused is preferred over __attribute__((__unused__))\n" . $herecurr); - } - } - -# Check for __attribute__ weak, or __weak declarations (may have link issues) - if ($perl_version_ok && - $line =~ /(?:$Declare|$DeclareMisordered)\s*$Ident\s*$balanced_parens\s*(?:$Attribute)?\s*;/ && - ($line =~ /\b__attribute__\s*\(\s*\(.*\bweak\b/ || - $line =~ /\b__weak\b/)) { - ERROR("WEAK_DECLARATION", - "Using weak declarations can have unintended link defects\n" . $herecurr); - } - -# check for c99 types like uint8_t used outside of uapi/ and tools/ - if (!$SOF) { - if ($realfile !~ m@\binclude/uapi/@ && - $realfile !~ m@\btools/@ && - $line =~ /\b($Declare)\s*$Ident\s*[=;,\[]/) { - my $type = $1; - if ($type =~ /\b($typeC99Typedefs)\b/) { - $type = $1; - my $kernel_type = 'u'; - $kernel_type = 's' if ($type =~ /^_*[si]/); - $type =~ /(\d+)/; - $kernel_type .= $1; - if (CHK("PREFER_KERNEL_TYPES", - "Prefer kernel type '$kernel_type' over '$type'\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/\b$type\b/$kernel_type/; - } - } - } - } - -# check for cast of C90 native int or longer types constants - if ($line =~ /(\(\s*$C90_int_types\s*\)\s*)($Constant)\b/) { - my $cast = $1; - my $const = $2; - my $suffix = ""; - my $newconst = $const; - $newconst =~ s/${Int_type}$//; - $suffix .= 'U' if ($cast =~ /\bunsigned\b/); - if ($cast =~ /\blong\s+long\b/) { - $suffix .= 'LL'; - } elsif ($cast =~ /\blong\b/) { - $suffix .= 'L'; - } - if (WARN("TYPECAST_INT_CONSTANT", - "Unnecessary typecast of c90 int constant - '$cast$const' could be '$const$suffix'\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/\Q$cast\E$const\b/$newconst$suffix/; - } - } - -# check for sizeof(&) - if ($line =~ /\bsizeof\s*\(\s*\&/) { - WARN("SIZEOF_ADDRESS", - "sizeof(& should be avoided\n" . $herecurr); - } - -# check for sizeof without parenthesis - if ($line =~ /\bsizeof\s+((?:\*\s*|)$Lval|$Type(?:\s+$Lval|))/) { - if (WARN("SIZEOF_PARENTHESIS", - "sizeof $1 should be sizeof($1)\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/\bsizeof\s+((?:\*\s*|)$Lval|$Type(?:\s+$Lval|))/"sizeof(" . trim($1) . ")"/ex; - } - } - -# check for struct spinlock declarations - if ($line =~ /^.\s*\bstruct\s+spinlock\s+\w+\s*;/) { - WARN("USE_SPINLOCK_T", - "struct spinlock should be spinlock_t\n" . $herecurr); - } - -# check for seq_printf uses that could be seq_puts - if ($sline =~ /\bseq_printf\s*\(.*"\s*\)\s*;\s*$/) { - my $fmt = get_quoted_string($line, $rawline); - $fmt =~ s/%%//g; - if ($fmt !~ /%/) { - if (WARN("PREFER_SEQ_PUTS", - "Prefer seq_puts to seq_printf\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/\bseq_printf\b/seq_puts/; - } - } - } - -# check for memcpy uses that should be memcpy_s - if ($SOF && ($line =~ /memcpy\s*\(.*/)) { - my $fmt = get_quoted_string($line, $rawline); - $fmt =~ s/%%//g; - if ($fmt !~ /%/) { - if (WARN("PREFER_MEMCPY_S", - "Use safe version of memcpy - memcpy_s whenever possible\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/memcpy\b/memcpy_s/; - } - } - } - -# check for vsprintf extension %p<foo> misuses - if ($perl_version_ok && - defined $stat && - $stat =~ /^\+(?![^\{]*\{\s*).*\b(\w+)\s*\(.*$String\s*,/s && - $1 !~ /^_*volatile_*$/) { - my $stat_real; - - my $lc = $stat =~ tr@\n@@; - $lc = $lc + $linenr; - for (my $count = $linenr; $count <= $lc; $count++) { - my $specifier; - my $extension; - my $qualifier; - my $bad_specifier = ""; - my $fmt = get_quoted_string($lines[$count - 1], raw_line($count, 0)); - $fmt =~ s/%%//g; - - while ($fmt =~ /(\%[\*\d\.]*p(\w)(\w*))/g) { - $specifier = $1; - $extension = $2; - $qualifier = $3; - if ($extension !~ /[4SsBKRraEehMmIiUDdgVCbGNOxtf]/ || - ($extension eq "f" && - defined $qualifier && $qualifier !~ /^w/) || - ($extension eq "4" && - defined $qualifier && $qualifier !~ /^cc/)) { - $bad_specifier = $specifier; - last; - } - if ($extension eq "x" && !defined($stat_real)) { - if (!defined($stat_real)) { - $stat_real = get_stat_real($linenr, $lc); - } - WARN("VSPRINTF_SPECIFIER_PX", - "Using vsprintf specifier '\%px' potentially exposes the kernel memory layout, if you don't really need the address please consider using '\%p'.\n" . "$here\n$stat_real\n"); - } - } - if ($bad_specifier ne "") { - my $stat_real = get_stat_real($linenr, $lc); - my $ext_type = "Invalid"; - my $use = ""; - if ($bad_specifier =~ /p[Ff]/) { - $use = " - use %pS instead"; - $use =~ s/pS/ps/ if ($bad_specifier =~ /pf/); - } - - WARN("VSPRINTF_POINTER_EXTENSION", - "$ext_type vsprintf pointer extension '$bad_specifier'$use\n" . "$here\n$stat_real\n"); - } - } - } - -# Check for misused memsets - if ($perl_version_ok && - defined $stat && - $stat =~ /^\+(?:.*?)\bmemset\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\,\s*$FuncArg\s*\)/) { - - my $ms_addr = $2; - my $ms_val = $7; - my $ms_size = $12; - - if ($ms_size =~ /^(0x|)0$/i) { - ERROR("MEMSET", - "memset to 0's uses 0 as the 2nd argument, not the 3rd\n" . "$here\n$stat\n"); - } elsif ($ms_size =~ /^(0x|)1$/i) { - WARN("MEMSET", - "single byte memset is suspicious. Swapped 2nd/3rd argument?\n" . "$here\n$stat\n"); - } - } - -# Check for memcpy(foo, bar, ETH_ALEN) that could be ether_addr_copy(foo, bar) -# if ($perl_version_ok && -# defined $stat && -# $stat =~ /^\+(?:.*?)\bmemcpy\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\,\s*ETH_ALEN\s*\)/) { -# if (WARN("PREFER_ETHER_ADDR_COPY", -# "Prefer ether_addr_copy() over memcpy() if the Ethernet addresses are __aligned(2)\n" . "$here\n$stat\n") && -# $fix) { -# $fixed[$fixlinenr] =~ s/\bmemcpy\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\,\s*ETH_ALEN\s*\)/ether_addr_copy($2, $7)/; -# } -# } - -# Check for memcmp(foo, bar, ETH_ALEN) that could be ether_addr_equal*(foo, bar) -# if ($perl_version_ok && -# defined $stat && -# $stat =~ /^\+(?:.*?)\bmemcmp\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\,\s*ETH_ALEN\s*\)/) { -# WARN("PREFER_ETHER_ADDR_EQUAL", -# "Prefer ether_addr_equal() or ether_addr_equal_unaligned() over memcmp()\n" . "$here\n$stat\n") -# } - -# check for memset(foo, 0x0, ETH_ALEN) that could be eth_zero_addr -# check for memset(foo, 0xFF, ETH_ALEN) that could be eth_broadcast_addr -# if ($perl_version_ok && -# defined $stat && -# $stat =~ /^\+(?:.*?)\bmemset\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\,\s*ETH_ALEN\s*\)/) { -# -# my $ms_val = $7; -# -# if ($ms_val =~ /^(?:0x|)0+$/i) { -# if (WARN("PREFER_ETH_ZERO_ADDR", -# "Prefer eth_zero_addr over memset()\n" . "$here\n$stat\n") && -# $fix) { -# $fixed[$fixlinenr] =~ s/\bmemset\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*,\s*ETH_ALEN\s*\)/eth_zero_addr($2)/; -# } -# } elsif ($ms_val =~ /^(?:0xff|255)$/i) { -# if (WARN("PREFER_ETH_BROADCAST_ADDR", -# "Prefer eth_broadcast_addr() over memset()\n" . "$here\n$stat\n") && -# $fix) { -# $fixed[$fixlinenr] =~ s/\bmemset\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*,\s*ETH_ALEN\s*\)/eth_broadcast_addr($2)/; -# } -# } -# } - -# strlcpy uses that should likely be strscpy - if ($line =~ /\bstrlcpy\s*\(/) { - WARN("STRLCPY", - "Prefer strscpy over strlcpy - see: https://lore.kernel.org/r/CAHk-=wgfRnXz0W3D37d01q3JFkr_i_uTL=V6A6G1oUZcprmknw\@mail.gmail.com/\n" . $herecurr); - } - -# typecasts on min/max could be min_t/max_t - if ($perl_version_ok && - defined $stat && - $stat =~ /^\+(?:.*?)\b(min|max)\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\)/) { - if (defined $2 || defined $7) { - my $call = $1; - my $cast1 = deparenthesize($2); - my $arg1 = $3; - my $cast2 = deparenthesize($7); - my $arg2 = $8; - my $cast; - - if ($cast1 ne "" && $cast2 ne "" && $cast1 ne $cast2) { - $cast = "$cast1 or $cast2"; - } elsif ($cast1 ne "") { - $cast = $cast1; - } else { - $cast = $cast2; - } - WARN("MINMAX", - "$call() should probably be ${call}_t($cast, $arg1, $arg2)\n" . "$here\n$stat\n"); - } - } - -# check usleep_range arguments - if ($perl_version_ok && - defined $stat && - $stat =~ /^\+(?:.*?)\busleep_range\s*\(\s*($FuncArg)\s*,\s*($FuncArg)\s*\)/) { - my $min = $1; - my $max = $7; - if ($min eq $max) { - WARN("USLEEP_RANGE", - "usleep_range should not use min == max args; see Documentation/timers/timers-howto.rst\n" . "$here\n$stat\n"); - } elsif ($min =~ /^\d+$/ && $max =~ /^\d+$/ && - $min > $max) { - WARN("USLEEP_RANGE", - "usleep_range args reversed, use min then max; see Documentation/timers/timers-howto.rst\n" . "$here\n$stat\n"); - } - } - -# check for naked sscanf - if ($perl_version_ok && - defined $stat && - $line =~ /\bsscanf\b/ && - ($stat !~ /$Ident\s*=\s*sscanf\s*$balanced_parens/ && - $stat !~ /\bsscanf\s*$balanced_parens\s*(?:$Compare)/ && - $stat !~ /(?:$Compare)\s*\bsscanf\s*$balanced_parens/)) { - my $lc = $stat =~ tr@\n@@; - $lc = $lc + $linenr; - my $stat_real = get_stat_real($linenr, $lc); - WARN("NAKED_SSCANF", - "unchecked sscanf return value\n" . "$here\n$stat_real\n"); - } - -# check for simple sscanf that should be kstrto<foo> - if ($perl_version_ok && - defined $stat && - $line =~ /\bsscanf\b/) { - my $lc = $stat =~ tr@\n@@; - $lc = $lc + $linenr; - my $stat_real = get_stat_real($linenr, $lc); - if ($stat_real =~ /\bsscanf\b\s*\(\s*$FuncArg\s*,\s*("[^"]+")/) { - my $format = $6; - my $count = $format =~ tr@%@%@; - if ($count == 1 && - $format =~ /^"\%(?i:ll[udxi]|[udxi]ll|ll|[hl]h?[udxi]|[udxi][hl]h?|[hl]h?|[udxi])"$/) { - WARN("SSCANF_TO_KSTRTO", - "Prefer kstrto<type> to single variable sscanf\n" . "$here\n$stat_real\n"); - } - } - } - -# check for new externs in .h files. - if ($realfile =~ /\.h$/ && - $line =~ /^\+\s*(extern\s+)$Type\s*$Ident\s*\(/s) { - if (CHK("AVOID_EXTERNS", - "extern prototypes should be avoided in .h files\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/(.*)\bextern\b\s*(.*)/$1$2/; - } - } - -# check for new externs in .c files. - if ($realfile =~ /\.c$/ && defined $stat && - $stat =~ /^.\s*(?:extern\s+)?$Type\s+($Ident)(\s*)\(/s) - { - my $function_name = $1; - my $paren_space = $2; - - my $s = $stat; - if (defined $cond) { - substr($s, 0, length($cond), ''); - } - if ($s =~ /^\s*;/) - { - WARN("AVOID_EXTERNS", - "externs should be avoided in .c files\n" . $herecurr); - } - - if ($paren_space =~ /\n/) { - WARN("FUNCTION_ARGUMENTS", - "arguments for function declarations should follow identifier\n" . $herecurr); - } - - } elsif ($realfile =~ /\.c$/ && defined $stat && - $stat =~ /^.\s*extern\s+/) - { - WARN("AVOID_EXTERNS", - "externs should be avoided in .c files\n" . $herecurr); - } - -# check for function declarations that have arguments without identifier names - if (defined $stat && - $stat =~ /^.\s*(?:extern\s+)?$Type\s*(?:$Ident|\(\s*\*\s*$Ident\s*\))\s*\(\s*([^{]+)\s*\)\s*;/s && - $1 ne "void") { - my $args = trim($1); - while ($args =~ m/\s*($Type\s*(?:$Ident|\(\s*\*\s*$Ident?\s*\)\s*$balanced_parens)?)/g) { - my $arg = trim($1); - if ($arg =~ /^$Type$/ && $arg !~ /enum\s+$Ident$/) { - WARN("FUNCTION_ARGUMENTS", - "function definition argument '$arg' should also have an identifier name\n" . $herecurr); - } - } - } - -# check for function definitions - if ($perl_version_ok && - defined $stat && - $stat =~ /^.\s*(?:$Storage\s+)?$Type\s*($Ident)\s*$balanced_parens\s*{/s) { - $context_function = $1; - -# check for multiline function definition with misplaced open brace - my $ok = 0; - my $cnt = statement_rawlines($stat); - my $herectx = $here . "\n"; - for (my $n = 0; $n < $cnt; $n++) { - my $rl = raw_line($linenr, $n); - $herectx .= $rl . "\n"; - $ok = 1 if ($rl =~ /^[ \+]\{/); - $ok = 1 if ($rl =~ /\{/ && $n == 0); - last if $rl =~ /^[ \+].*\{/; - } - if (!$ok) { - ERROR("OPEN_BRACE", - "open brace '{' following function definitions go on the next line\n" . $herectx); - } - } - -# checks for new __setup's - if ($rawline =~ /\b__setup\("([^"]*)"/) { - my $name = $1; - - if (!grep(/$name/, @setup_docs)) { - CHK("UNDOCUMENTED_SETUP", - "__setup appears un-documented -- check Documentation/admin-guide/kernel-parameters.txt\n" . $herecurr); - } - } - -# check for pointless casting of alloc functions - if ($line =~ /\*\s*\)\s*$allocFunctions\b/) { - WARN("UNNECESSARY_CASTS", - "unnecessary cast may hide bugs, see http://c-faq.com/malloc/mallocnocast.html\n" . $herecurr); - } - -# alloc style -# p = alloc(sizeof(struct foo), ...) should be p = alloc(sizeof(*p), ...) - if ($perl_version_ok && - $line =~ /\b($Lval)\s*\=\s*(?:$balanced_parens)?\s*((?:kv|k|v)[mz]alloc(?:_node)?)\s*\(\s*(sizeof\s*\(\s*struct\s+$Lval\s*\))/) { - CHK("ALLOC_SIZEOF_STRUCT", - "Prefer $3(sizeof(*$1)...) over $3($4...)\n" . $herecurr); - } - -# check for k[mz]alloc with multiplies that could be kmalloc_array/kcalloc - if ($perl_version_ok && - defined $stat && - $stat =~ /^\+\s*($Lval)\s*\=\s*(?:$balanced_parens)?\s*(k[mz]alloc)\s*\(\s*($FuncArg)\s*\*\s*($FuncArg)\s*,/) { - my $oldfunc = $3; - my $a1 = $4; - my $a2 = $10; - my $newfunc = "kmalloc_array"; - $newfunc = "kcalloc" if ($oldfunc eq "kzalloc"); - my $r1 = $a1; - my $r2 = $a2; - if ($a1 =~ /^sizeof\s*\S/) { - $r1 = $a2; - $r2 = $a1; - } - if ($r1 !~ /^sizeof\b/ && $r2 =~ /^sizeof\s*\S/ && - !($r1 =~ /^$Constant$/ || $r1 =~ /^[A-Z_][A-Z0-9_]*$/)) { - my $cnt = statement_rawlines($stat); - my $herectx = get_stat_here($linenr, $cnt, $here); - - if (WARN("ALLOC_WITH_MULTIPLY", - "Prefer $newfunc over $oldfunc with multiply\n" . $herectx) && - $cnt == 1 && - $fix) { - $fixed[$fixlinenr] =~ s/\b($Lval)\s*\=\s*(?:$balanced_parens)?\s*(k[mz]alloc)\s*\(\s*($FuncArg)\s*\*\s*($FuncArg)/$1 . ' = ' . "$newfunc(" . trim($r1) . ', ' . trim($r2)/e; - } - } - } - -# check for krealloc arg reuse - if ($perl_version_ok && - $line =~ /\b($Lval)\s*\=\s*(?:$balanced_parens)?\s*krealloc\s*\(\s*($Lval)\s*,/ && - $1 eq $3) { - WARN("KREALLOC_ARG_REUSE", - "Reusing the krealloc arg is almost always a bug\n" . $herecurr); - } - -# check for alloc argument mismatch - if ($line =~ /\b((?:devm_)?(?:kcalloc|kmalloc_array))\s*\(\s*sizeof\b/) { - WARN("ALLOC_ARRAY_ARGS", - "$1 uses number as first arg, sizeof is generally wrong\n" . $herecurr); - } - -# check for multiple semicolons - if ($line =~ /;\s*;\s*$/) { - if (WARN("ONE_SEMICOLON", - "Statements terminations use 1 semicolon\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/(\s*;\s*){2,}$/;/g; - } - } - -# check for #defines like: 1 << <digit> that could be BIT(digit), it is not exported to uapi - if ($realfile !~ m@^include/uapi/@ && - $line =~ /#\s*define\s+\w+\s+\(?\s*1\s*([ulUL]*)\s*\<\<\s*(?:\d+|$Ident)\s*\)?/) { - my $ull = ""; - $ull = "_ULL" if (defined($1) && $1 =~ /ll/i); - if (CHK("BIT_MACRO", - "Prefer using the BIT$ull macro\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/\(?\s*1\s*[ulUL]*\s*<<\s*(\d+|$Ident)\s*\)?/BIT${ull}($1)/; - } - } - -# check for IS_ENABLED() without CONFIG_<FOO> ($rawline for comments too) - if ($rawline =~ /\bIS_ENABLED\s*\(\s*(\w+)\s*\)/ && $1 !~ /^${CONFIG_}/) { - WARN("IS_ENABLED_CONFIG", - "IS_ENABLED($1) is normally used as IS_ENABLED(${CONFIG_}$1)\n" . $herecurr); - } - -# check for #if defined CONFIG_<FOO> || defined CONFIG_<FOO>_MODULE - if ($line =~ /^\+\s*#\s*if\s+defined(?:\s*\(?\s*|\s+)(${CONFIG_}[A-Z_]+)\s*\)?\s*\|\|\s*defined(?:\s*\(?\s*|\s+)\1_MODULE\s*\)?\s*$/) { - my $config = $1; - if (WARN("PREFER_IS_ENABLED", - "Prefer IS_ENABLED(<FOO>) to ${CONFIG_}<FOO> || ${CONFIG_}<FOO>_MODULE\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] = "\+#if IS_ENABLED($config)"; - } - } - -# check for /* fallthrough */ like comment, prefer fallthrough; - my @fallthroughs = ( - 'fallthrough', - '@fallthrough@', - 'lint -fallthrough[ \t]*', - 'intentional(?:ly)?[ \t]*fall(?:(?:s | |-)[Tt]|t)hr(?:ough|u|ew)', - '(?:else,?\s*)?FALL(?:S | |-)?THR(?:OUGH|U|EW)[ \t.!]*(?:-[^\n\r]*)?', - 'Fall(?:(?:s | |-)[Tt]|t)hr(?:ough|u|ew)[ \t.!]*(?:-[^\n\r]*)?', - 'fall(?:s | |-)?thr(?:ough|u|ew)[ \t.!]*(?:-[^\n\r]*)?', - ); - if ($raw_comment ne '') { - foreach my $ft (@fallthroughs) { - if ($raw_comment =~ /$ft/) { - my $msg_level = \&WARN; - $msg_level = \&CHK if ($file); - &{$msg_level}("PREFER_FALLTHROUGH", - "Prefer 'fallthrough;' over fallthrough comment\n" . $herecurr); - last; - } - } - } - -# check for switch/default statements without a break; - if ($perl_version_ok && - defined $stat && - $stat =~ /^\+[$;\s]*(?:case[$;\s]+\w+[$;\s]*:[$;\s]*|)*[$;\s]*\bdefault[$;\s]*:[$;\s]*;/g) { - my $cnt = statement_rawlines($stat); - my $herectx = get_stat_here($linenr, $cnt, $here); - - WARN("DEFAULT_NO_BREAK", - "switch default: should use break\n" . $herectx); - } - -# check for gcc specific __FUNCTION__ - if ($line =~ /\b__FUNCTION__\b/) { - if (WARN("USE_FUNC", - "__func__ should be used instead of gcc specific __FUNCTION__\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/\b__FUNCTION__\b/__func__/g; - } - } - -# check for uses of __DATE__, __TIME__, __TIMESTAMP__ - while ($line =~ /\b(__(?:DATE|TIME|TIMESTAMP)__)\b/g) { - ERROR("DATE_TIME", - "Use of the '$1' macro makes the build non-deterministic\n" . $herecurr); - } - -# check for use of yield() - if ($line =~ /\byield\s*\(\s*\)/) { - WARN("YIELD", - "Using yield() is generally wrong. See yield() kernel-doc (sched/core.c)\n" . $herecurr); - } - -# check for comparisons against true and false - if ($line =~ /\+\s*(.*?)\b(true|false|$Lval)\s*(==|\!=)\s*(true|false|$Lval)\b(.*)$/i) { - my $lead = $1; - my $arg = $2; - my $test = $3; - my $otype = $4; - my $trail = $5; - my $op = "!"; - - ($arg, $otype) = ($otype, $arg) if ($arg =~ /^(?:true|false)$/i); - - my $type = lc($otype); - if ($type =~ /^(?:true|false)$/) { - if (("$test" eq "==" && "$type" eq "true") || - ("$test" eq "!=" && "$type" eq "false")) { - $op = ""; - } - - CHK("BOOL_COMPARISON", - "Using comparison to $otype is error prone\n" . $herecurr); - -## maybe suggesting a correct construct would better -## "Using comparison to $otype is error prone. Perhaps use '${lead}${op}${arg}${trail}'\n" . $herecurr); - - } - } - -# check for semaphores initialized locked - if ($line =~ /^.\s*sema_init.+,\W?0\W?\)/) { - WARN("CONSIDER_COMPLETION", - "consider using a completion\n" . $herecurr); - } - -# recommend kstrto* over simple_strto* and strict_strto* - if ($line =~ /\b((simple|strict)_(strto(l|ll|ul|ull)))\s*\(/) { - WARN("CONSIDER_KSTRTO", - "$1 is obsolete, use k$3 instead\n" . $herecurr); - } - -# check for __initcall(), use device_initcall() explicitly or more appropriate function please - if ($line =~ /^.\s*__initcall\s*\(/) { - WARN("USE_DEVICE_INITCALL", - "please use device_initcall() or more appropriate function instead of __initcall() (see include/linux/init.h)\n" . $herecurr); - } - -# check for spin_is_locked(), suggest lockdep instead - if ($line =~ /\bspin_is_locked\(/) { - WARN("USE_LOCKDEP", - "Where possible, use lockdep_assert_held instead of assertions based on spin_is_locked\n" . $herecurr); - } - -# check for deprecated apis - if ($line =~ /\b($deprecated_apis_search)\b\s*\(/) { - my $deprecated_api = $1; - my $new_api = $deprecated_apis{$deprecated_api}; - WARN("DEPRECATED_API", - "Deprecated use of '$deprecated_api', prefer '$new_api' instead\n" . $herecurr); - } - -# check for various structs that are normally const (ops, kgdb, device_tree) -# and avoid what seem like struct definitions 'struct foo {' - if (defined($const_structs) && - $line !~ /\bconst\b/ && - $line =~ /\bstruct\s+($const_structs)\b(?!\s*\{)/) { - WARN("CONST_STRUCT", - "struct $1 should normally be const\n" . $herecurr); - } - -# use of NR_CPUS is usually wrong -# ignore definitions of NR_CPUS and usage to define arrays as likely right -# ignore designated initializers using NR_CPUS - if ($line =~ /\bNR_CPUS\b/ && - $line !~ /^.\s*\s*#\s*if\b.*\bNR_CPUS\b/ && - $line !~ /^.\s*\s*#\s*define\b.*\bNR_CPUS\b/ && - $line !~ /^.\s*$Declare\s.*\[[^\]]*NR_CPUS[^\]]*\]/ && - $line !~ /\[[^\]]*\.\.\.[^\]]*NR_CPUS[^\]]*\]/ && - $line !~ /\[[^\]]*NR_CPUS[^\]]*\.\.\.[^\]]*\]/ && - $line !~ /^.\s*\.\w+\s*=\s*.*\bNR_CPUS\b/) - { - WARN("NR_CPUS", - "usage of NR_CPUS is often wrong - consider using cpu_possible(), num_possible_cpus(), for_each_possible_cpu(), etc\n" . $herecurr); - } - -# Use of __ARCH_HAS_<FOO> or ARCH_HAVE_<BAR> is wrong. - if ($line =~ /\+\s*#\s*define\s+((?:__)?ARCH_(?:HAS|HAVE)\w*)\b/) { - ERROR("DEFINE_ARCH_HAS", - "#define of '$1' is wrong - use Kconfig variables or standard guards instead\n" . $herecurr); - } - -# likely/unlikely comparisons similar to "(likely(foo) > 0)" - if ($perl_version_ok && - $line =~ /\b((?:un)?likely)\s*\(\s*$FuncArg\s*\)\s*$Compare/) { - WARN("LIKELY_MISUSE", - "Using $1 should generally have parentheses around the comparison\n" . $herecurr); - } - -# return sysfs_emit(foo, fmt, ...) fmt without newline - if ($line =~ /\breturn\s+sysfs_emit\s*\(\s*$FuncArg\s*,\s*($String)/ && - substr($rawline, $-[6], $+[6] - $-[6]) !~ /\\n"$/) { - my $offset = $+[6] - 1; - if (WARN("SYSFS_EMIT", - "return sysfs_emit(...) formats should include a terminating newline\n" . $herecurr) && - $fix) { - substr($fixed[$fixlinenr], $offset, 0) = '\\n'; - } - } - -# nested likely/unlikely calls - if ($line =~ /\b(?:(?:un)?likely)\s*\(\s*!?\s*(IS_ERR(?:_OR_NULL|_VALUE)?|WARN)/) { - WARN("LIKELY_MISUSE", - "nested (un)?likely() calls, $1 already uses unlikely() internally\n" . $herecurr); - } - -# whine mightly about in_atomic - if ($line =~ /\bin_atomic\s*\(/) { - if ($realfile =~ m@^drivers/@) { - ERROR("IN_ATOMIC", - "do not use in_atomic in drivers\n" . $herecurr); - } elsif ($realfile !~ m@^kernel/@) { - WARN("IN_ATOMIC", - "use of in_atomic() is incorrect outside core kernel code\n" . $herecurr); - } - } - -# check for lockdep_set_novalidate_class - if ($line =~ /^.\s*lockdep_set_novalidate_class\s*\(/ || - $line =~ /__lockdep_no_validate__\s*\)/ ) { - if ($realfile !~ m@^kernel/lockdep@ && - $realfile !~ m@^include/linux/lockdep@ && - $realfile !~ m@^drivers/base/core@) { - ERROR("LOCKDEP", - "lockdep_no_validate class is reserved for device->mutex.\n" . $herecurr); - } - } - - if ($line =~ /debugfs_create_\w+.*\b$mode_perms_world_writable\b/ || - $line =~ /DEVICE_ATTR.*\b$mode_perms_world_writable\b/) { - WARN("EXPORTED_WORLD_WRITABLE", - "Exporting world writable files is usually an error. Consider more restrictive permissions.\n" . $herecurr); - } - -# check for DEVICE_ATTR uses that could be DEVICE_ATTR_<FOO> -# and whether or not function naming is typical and if -# DEVICE_ATTR permissions uses are unusual too - if ($perl_version_ok && - defined $stat && - $stat =~ /\bDEVICE_ATTR\s*\(\s*(\w+)\s*,\s*\(?\s*(\s*(?:${multi_mode_perms_string_search}|0[0-7]{3,3})\s*)\s*\)?\s*,\s*(\w+)\s*,\s*(\w+)\s*\)/) { - my $var = $1; - my $perms = $2; - my $show = $3; - my $store = $4; - my $octal_perms = perms_to_octal($perms); - if ($show =~ /^${var}_show$/ && - $store =~ /^${var}_store$/ && - $octal_perms eq "0644") { - if (WARN("DEVICE_ATTR_RW", - "Use DEVICE_ATTR_RW\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/\bDEVICE_ATTR\s*\(\s*$var\s*,\s*\Q$perms\E\s*,\s*$show\s*,\s*$store\s*\)/DEVICE_ATTR_RW(${var})/; - } - } elsif ($show =~ /^${var}_show$/ && - $store =~ /^NULL$/ && - $octal_perms eq "0444") { - if (WARN("DEVICE_ATTR_RO", - "Use DEVICE_ATTR_RO\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/\bDEVICE_ATTR\s*\(\s*$var\s*,\s*\Q$perms\E\s*,\s*$show\s*,\s*NULL\s*\)/DEVICE_ATTR_RO(${var})/; - } - } elsif ($show =~ /^NULL$/ && - $store =~ /^${var}_store$/ && - $octal_perms eq "0200") { - if (WARN("DEVICE_ATTR_WO", - "Use DEVICE_ATTR_WO\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/\bDEVICE_ATTR\s*\(\s*$var\s*,\s*\Q$perms\E\s*,\s*NULL\s*,\s*$store\s*\)/DEVICE_ATTR_WO(${var})/; - } - } elsif ($octal_perms eq "0644" || - $octal_perms eq "0444" || - $octal_perms eq "0200") { - my $newshow = "$show"; - $newshow = "${var}_show" if ($show ne "NULL" && $show ne "${var}_show"); - my $newstore = $store; - $newstore = "${var}_store" if ($store ne "NULL" && $store ne "${var}_store"); - my $rename = ""; - if ($show ne $newshow) { - $rename .= " '$show' to '$newshow'"; - } - if ($store ne $newstore) { - $rename .= " '$store' to '$newstore'"; - } - WARN("DEVICE_ATTR_FUNCTIONS", - "Consider renaming function(s)$rename\n" . $herecurr); - } else { - WARN("DEVICE_ATTR_PERMS", - "DEVICE_ATTR unusual permissions '$perms' used\n" . $herecurr); - } - } - -# Mode permission misuses where it seems decimal should be octal -# This uses a shortcut match to avoid unnecessary uses of a slow foreach loop -# o Ignore module_param*(...) uses with a decimal 0 permission as that has a -# specific definition of not visible in sysfs. -# o Ignore proc_create*(...) uses with a decimal 0 permission as that means -# use the default permissions - if ($perl_version_ok && - defined $stat && - $line =~ /$mode_perms_search/) { - foreach my $entry (@mode_permission_funcs) { - my $func = $entry->[0]; - my $arg_pos = $entry->[1]; - - my $lc = $stat =~ tr@\n@@; - $lc = $lc + $linenr; - my $stat_real = get_stat_real($linenr, $lc); - - my $skip_args = ""; - if ($arg_pos > 1) { - $arg_pos--; - $skip_args = "(?:\\s*$FuncArg\\s*,\\s*){$arg_pos,$arg_pos}"; - } - my $test = "\\b$func\\s*\\(${skip_args}($FuncArg(?:\\|\\s*$FuncArg)*)\\s*[,\\)]"; - if ($stat =~ /$test/) { - my $val = $1; - $val = $6 if ($skip_args ne ""); - if (!($func =~ /^(?:module_param|proc_create)/ && $val eq "0") && - (($val =~ /^$Int$/ && $val !~ /^$Octal$/) || - ($val =~ /^$Octal$/ && length($val) ne 4))) { - ERROR("NON_OCTAL_PERMISSIONS", - "Use 4 digit octal (0777) not decimal permissions\n" . "$here\n" . $stat_real); - } - if ($val =~ /^$Octal$/ && (oct($val) & 02)) { - ERROR("EXPORTED_WORLD_WRITABLE", - "Exporting writable files is usually an error. Consider more restrictive permissions.\n" . "$here\n" . $stat_real); - } - } - } - } - -# check for uses of S_<PERMS> that could be octal for readability - while ($line =~ m{\b($multi_mode_perms_string_search)\b}g) { - my $oval = $1; - my $octal = perms_to_octal($oval); - if (WARN("SYMBOLIC_PERMS", - "Symbolic permissions '$oval' are not preferred. Consider using octal permissions '$octal'.\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/\Q$oval\E/$octal/; - } - } - -# validate content of MODULE_LICENSE against list from include/linux/module.h - if ($line =~ /\bMODULE_LICENSE\s*\(\s*($String)\s*\)/) { - my $extracted_string = get_quoted_string($line, $rawline); - my $valid_licenses = qr{ - GPL| - GPL\ v2| - GPL\ and\ additional\ rights| - Dual\ BSD/GPL| - Dual\ MIT/GPL| - Dual\ MPL/GPL| - Proprietary - }x; - if ($extracted_string !~ /^"(?:$valid_licenses)"$/x) { - WARN("MODULE_LICENSE", - "unknown module license " . $extracted_string . "\n" . $herecurr); - } - } - -# check for sysctl duplicate constants - if ($line =~ /\.extra[12]\s*=\s*&(zero|one|int_max)\b/) { - WARN("DUPLICATED_SYSCTL_CONST", - "duplicated sysctl range checking value '$1', consider using the shared one in include/linux/sysctl.h\n" . $herecurr); - } - } - - # If we have no input at all, then there is nothing to report on - # so just keep quiet. - if ($#rawlines == -1) { - exit(0); - } - - # In mailback mode only produce a report in the negative, for - # things that appear to be patches. - if ($mailback && ($clean == 1 || !$is_patch)) { - exit(0); - } - - # This is not a patch, and we are in 'no-patch' mode so - # just keep quiet. - if (!$chk_patch && !$is_patch) { - exit(0); - } - - if (!$is_patch && $filename !~ /cover-letter\.patch$/) { - ERROR("NOT_UNIFIED_DIFF", - "Does not appear to be a unified-diff format patch\n"); - } - if ($is_patch && $has_commit_log && $chk_signoff) { - if ($signoff == 0) { - ERROR("MISSING_SIGN_OFF", - "Missing Signed-off-by: line(s)\n"); - } elsif ($authorsignoff != 1) { - # authorsignoff values: - # 0 -> missing sign off - # 1 -> sign off identical - # 2 -> names and addresses match, comments mismatch - # 3 -> addresses match, names different - # 4 -> names match, addresses different - # 5 -> names match, addresses excluding subaddress details (refer RFC 5233) match - - my $sob_msg = "'From: $author' != 'Signed-off-by: $author_sob'"; - - if ($authorsignoff == 0) { - ERROR("NO_AUTHOR_SIGN_OFF", - "Missing Signed-off-by: line by nominal patch author '$author'\n"); - } elsif ($authorsignoff == 2) { - CHK("FROM_SIGN_OFF_MISMATCH", - "From:/Signed-off-by: email comments mismatch: $sob_msg\n"); - } elsif ($authorsignoff == 3) { - WARN("FROM_SIGN_OFF_MISMATCH", - "From:/Signed-off-by: email name mismatch: $sob_msg\n"); - } elsif ($authorsignoff == 4) { - WARN("FROM_SIGN_OFF_MISMATCH", - "From:/Signed-off-by: email address mismatch: $sob_msg\n"); - } elsif ($authorsignoff == 5) { - WARN("FROM_SIGN_OFF_MISMATCH", - "From:/Signed-off-by: email subaddress mismatch: $sob_msg\n"); - } - } - } - - print report_dump(); - if ($summary && !($clean == 1 && $quiet == 1)) { - print "$filename " if ($summary_file); - print "total: $cnt_error errors, $cnt_warn warnings, " . - (($check)? "$cnt_chk checks, " : "") . - "$cnt_lines lines checked\n"; - } - - if ($quiet == 0) { - # If there were any defects found and not already fixing them - if (!$clean and !$fix) { - print << "EOM" - -NOTE: For some of the reported defects, checkpatch may be able to - mechanically convert to the typical style using --fix or --fix-inplace. -EOM - } - # If there were whitespace errors which cleanpatch can fix - # then suggest that. - if ($rpt_cleaners) { - $rpt_cleaners = 0; - print << "EOM" - -NOTE: Whitespace errors detected. - You may wish to use scripts/cleanpatch or scripts/cleanfile -EOM - } - } - - if ($clean == 0 && $fix && - ("@rawlines" ne "@fixed" || - $#fixed_inserted >= 0 || $#fixed_deleted >= 0)) { - my $newfile = $filename; - $newfile .= ".EXPERIMENTAL-checkpatch-fixes" if (!$fix_inplace); - my $linecount = 0; - my $f; - - @fixed = fix_inserted_deleted_lines(\@fixed, \@fixed_inserted, \@fixed_deleted); - - open($f, '>', $newfile) - or die "$P: Can't open $newfile for write\n"; - foreach my $fixed_line (@fixed) { - $linecount++; - if ($file) { - if ($linecount > 3) { - $fixed_line =~ s/^\+//; - print $f $fixed_line . "\n"; - } - } else { - print $f $fixed_line . "\n"; - } - } - close($f); - - if (!$quiet) { - print << "EOM"; - -Wrote EXPERIMENTAL --fix correction(s) to '$newfile' - -Do _NOT_ trust the results written to this file. -Do _NOT_ submit these changes without inspecting them for correctness. - -This EXPERIMENTAL file is simply a convenience to help rewrite patches. -No warranties, expressed or implied... -EOM - } - } - - if ($quiet == 0) { - print "\n"; - if ($clean == 1) { - print "$vname has no obvious style problems and is ready for submission.\n"; - } else { - print "$vname has style problems, please review.\n"; - } - } - return $clean; -} diff --git a/scripts/const_structs.checkpatch b/scripts/const_structs.checkpatch deleted file mode 100644 index ab2bbb5402c0..000000000000 --- a/scripts/const_structs.checkpatch +++ /dev/null @@ -1,4 +0,0 @@ -comp_func_map -dma_ops -dai_driver -freq_table \ No newline at end of file diff --git a/scripts/docker_build/zephyr_lite/Dockerfile b/scripts/docker_build/zephyr_lite/Dockerfile index 0c52b1f668f4..c6d6e28159c8 100644 --- a/scripts/docker_build/zephyr_lite/Dockerfile +++ b/scripts/docker_build/zephyr_lite/Dockerfile @@ -2,46 +2,39 @@ # Copyright(c) 2025 Intel Corporation. All rights reserved. # Use zephyr-build as base image -FROM ghcr.io/zephyrproject-rtos/zephyr-build:v0.27.4 as base +FROM ghcr.io/zephyrproject-rtos/zephyr-build:v0.29.0 as base # Remove additional toolchains. # As this is not ideal solution there is a plan to build docker image without zephyr-build as the base -# and install only needeed toolchains in the future. -RUN cd /opt/toolchains/zephyr-sdk-0.17.0 && \ - sudo rm -rvf arc* \ - micro* \ - mips* \ - nios* \ - risc* \ - sparc* \ - x86* \ - xtensa-espressif* \ - xtensa-sample* \ - xtensa-dc233c* - -# Some of tests require python 3.12 - instll it from source -RUN cd /tmp && wget -q --show-progress --progress=bar:force:noscroll --no-check-certificate https://www.python.org/ftp/python/3.12.9/Python-3.12.9.tgz && \ - tar -xf Python-3.12.9.tgz && \ - cd Python-3.12.9 && \ - ./configure && \ - sudo make -j$(nproc) && \ - sudo make install && \ - sudo rm -rf /tmp/Python-3* - -# Reinstall python3.10 packages with python3.12 -RUN python3.10 -m pip freeze > /tmp/python3.10.pip.txt && \ - cat /tmp/python3.10.pip.txt | xargs -n 1 python3.12 -m pip install || true +# and install only needed toolchains in the future. +USER root + +RUN rm -rvf /opt/toolchains/zephyr-sdk-1.0.0/hosttools/sysroots && \ + rm -rvf /opt/fvps && \ + cd /opt/toolchains/zephyr-sdk-1.0.0/gnu && \ + rm -rvf arc* \ + micro* \ + mips* \ + nios* \ + risc* \ + sparc* \ + x86* \ + xtensa-espressif* \ + xtensa-sample* \ + xtensa-dc233c* # Use ubuntu24.04 as base for zephyr-lite FROM ubuntu:24.04 as zephyr-lite -# Copy needed files from base to zephyr-lite +# Copy only required files from base image to zephyr-lite # /opt for toolchains and sdk # /usr for binaries and libs # /home for libs installed in .local +# /etc/ssl for ssl certs for python packages COPY --from=base /opt /opt COPY --from=base /usr /usr COPY --from=base /home /home +COPY --from=base /etc/ssl /etc/ssl USER root @@ -49,10 +42,18 @@ USER root # Add user to dialout and sudo group RUN useradd -ms /bin/bash user && \ chown -R user:user /home/user && \ + chown -R user:user /opt/python && \ usermod -a -G dialout,sudo user USER user +# Install cmake and jsonschema in venv +RUN /opt/python/venv/bin/pip install 'cmake>=3.21' jsonschema + # Set zephyr env variables -ENV ZEPHYR_SDK_INSTALL_DIR=/opt/toolchains/zephyr-sdk-0.17.0 +ENV PATH="/opt/python/venv/bin/:$PATH" +ENV ZEPHYR_SDK_INSTALL_DIR=/opt/toolchains/zephyr-sdk-1.0.0 +ENV ZSDK_VERSION=1.0.0 ENV ZEPHYR_TOOLCHAIN_VARIANT=zephyr + +CMD ["/bin/bash", "-l"] diff --git a/scripts/fuzz.sh b/scripts/fuzz.sh index e29a0eed1623..b8b69b5fd1d2 100755 --- a/scripts/fuzz.sh +++ b/scripts/fuzz.sh @@ -189,7 +189,7 @@ setup() SOF_TOP=$(cd "$(dirname "$0")/.." && pwd) export SOF_TOP - export ZEPHYR_TOOLCHAIN_VARIANT=llvm + export ZEPHYR_TOOLCHAIN_VARIANT=host/llvm # Define ZEPHYR_BASE so this can be invoked even outside the west workspace. local WS_TOP diff --git a/scripts/host-testbench.sh b/scripts/host-testbench.sh index 2392525f1b69..4780b78f6de6 100755 --- a/scripts/host-testbench.sh +++ b/scripts/host-testbench.sh @@ -122,4 +122,16 @@ test_component asrc 32 32 48000 "$FullTest" # test with template component test_component template_comp 32 32 48000 "$FullTest" +# test with Dolby DAX with stub +test_component dolby-dax 32 32 48000 "$FullTest" + +# test with level_multiplier +test_component level_multiplier 32 32 48000 "$FullTest" + +# test with micsel +test_component micsel 32 32 48000 "$FullTest" + +# test with sound_dose +test_component sound_dose 32 32 48000 "$FullTest" + echo "All tests are done!" diff --git a/scripts/rebuild-testbench.sh b/scripts/rebuild-testbench.sh index fee09fd243ae..996d16f45a8c 100755 --- a/scripts/rebuild-testbench.sh +++ b/scripts/rebuild-testbench.sh @@ -97,7 +97,7 @@ export_xtensa_setup() cat <<EOFSETUP > "$export_script" export XTENSA_TOOLS_ROOT=$XTENSA_TOOLS_ROOT export XTENSA_CORE=$XTENSA_CORE -XTENSA_PATH=$tools_bin +export XTENSA_PATH=$tools_bin EOFSETUP } diff --git a/scripts/sdk-create-module.py b/scripts/sdk-create-module.py index b9193a57f942..bc80db16dfe9 100755 --- a/scripts/sdk-create-module.py +++ b/scripts/sdk-create-module.py @@ -362,9 +362,12 @@ def main(): print("--- SOF SDK New Module Creator ---") # Argument Validation --- - if len(sys.argv) != 2: + if len(sys.argv) == 2 and sys.argv[1] in ['-h', '--help']: + print("Usage: sdk-create-module.py <new_module_name>") + sys.exit(0) + elif len(sys.argv) != 2: print("\n[ERROR] Invalid number of arguments.") - print("Usage: sdk_create_module.py <new_module_name>") + print("Usage: sdk-create-module.py <new_module_name>") sys.exit(1) # Configuration --- paths are with respect to script dir diff --git a/scripts/set_xtensa_params.sh b/scripts/set_xtensa_params.sh index 92e4d7d384a6..a3932818edc1 100644 --- a/scripts/set_xtensa_params.sh +++ b/scripts/set_xtensa_params.sh @@ -66,6 +66,11 @@ case "$platform" in XTENSA_CORE="ace30_LX7HiFi4_PIF" TOOLCHAIN_VER="RI-2022.10-linux" ;; + nvl) + PLATFORM="$platform" + XTENSA_CORE="ace4px_HiFi5MMU_PIF_nlib" + TOOLCHAIN_VER="RI-2022.10-linux" + ;; # NXP imx8) @@ -145,6 +150,12 @@ case "$platform" in HOST="xtensa-mt8365-elf" TOOLCHAIN_VER="RG-2018.9-linux" ;; + qemu_xtensa | qemu_xtensa_mmu) + PLATFORM="$1" + XTENSA_CORE="" + HOST="xtensa-zephyr-elf" + TOOLCHAIN_VER="" + ;; *) >&2 printf 'Unknown xtensa platform=%s\n' "$platform" return 1 @@ -153,7 +164,7 @@ esac # Pre-zephyr "XTOS" build, testbench,... case "$platform" in - mtl|lnl|ptl|acp_7_0|mt8196) + mtl|lnl|ptl|acp_7_0|mt8196|nvl) SOF_CC_BASE='clang';; *) SOF_CC_BASE='xcc';; @@ -161,6 +172,8 @@ esac # For Zephyr unit tests case "$platform" in + qemu_xtensa | qemu_xtensa_mmu) + ZEPHYR_TOOLCHAIN_VARIANT='zephyr';; imx8*|mtl|lnl) ZEPHYR_TOOLCHAIN_VARIANT='xt-clang';; *) # The previous, main case/esac already caught invalid input. diff --git a/scripts/sof-crash-decode.py b/scripts/sof-crash-decode.py new file mode 100755 index 000000000000..8f76921431be --- /dev/null +++ b/scripts/sof-crash-decode.py @@ -0,0 +1,501 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(c) 2026 Intel Corporation. All rights reserved. +""" +decode_crash.py - Zephyr Xtensa Crash Dump Decoder + +Parses a Zephyr crash dump, extracts CPU registers/backtraces, and correlates them +to source code files and function names using the `objdump` output of the ELF file. + +Dependencies: + - python3 + - binutils for your target architecture (e.g. xtensa-zephyr-elf-objdump) + - Optional: `xclip`, `xsel`, or `wl-paste` (for --clipboard support on Linux) + +Usage Examples: + # 1. Provide the ELF and read crash from stdin + cat crash.txt | ./sof-crash-decode.py --elf zephyr.elf + + # 2. Automatically locate ELF/objdump from a Zephyr build directory, read crash from file + ./sof-crash-decode.py --build-dir build-qemu_xtensa/ --dump crash.txt + + # 3. Read directly from the system clipboard + ./sof-crash-decode.py --build-dir build-qemu_xtensa/ --clipboard + + # 4. Pipe a live trace to the decoder + tail -f log.txt | ./sof-crash-decode.py --build-dir build_dir/ + +""" + +import sys +import re +import argparse +import subprocess +import bisect +import os +import json +import shlex + +XTENSA_EXCCAUSE = { + 0: "No Error (or IllegalInstruction)", + 1: "Syscall", + 2: "InstructionFetchError", + 3: "LoadStoreError", + 4: "Level1Interrupt", + 5: "Alloca", + 6: "IntegerDivideByZero", + 8: "Privileged", + 9: "LoadStoreAlignment", + 12: "InstrPIFDataError", + 13: "LoadStorePIFDataError", + 14: "InstrPIFAddrError", + 15: "LoadStorePIFAddrError", + 16: "InstTLBMiss", + 17: "InstTLBMultiHit", + 18: "InstFetchPrivilege", + 20: "InstFetchProhibited", + 24: "LoadStoreTLBMiss", + 25: "LoadStoreTLBMultiHit", + 26: "LoadStorePrivilege", + 28: "LoadStoreProhibited", + 32: "Coprocessor0Disabled", + 33: "Coprocessor1Disabled", + 34: "Coprocessor2Disabled", + 35: "Coprocessor3Disabled", + 36: "Coprocessor4Disabled", + 37: "Coprocessor5Disabled", + 38: "Coprocessor6Disabled", + 39: "Coprocessor7Disabled", +} + +def parse_crash_log(content): + + registers = {} + backtraces = [] + + # Detect QEMU format + if re.search(r'\bPC=[0-9a-fA-F]+\b', content): + reg_pattern = re.compile(r'\b([A-Z0-9]+)=([0-9a-fA-F]+)\b') + for match in reg_pattern.finditer(content): + reg = match.group(1) + val = int(match.group(2), 16) + + if reg == 'EXCVADDR': + reg = 'VADDR' + elif re.match(r'^A\d{2}$', reg): + reg = f"A{int(reg[1:])}" + elif re.match(r'^AR\d{2}$', reg): + reg = f"AR{int(reg[2:])}" + + if re.match(r'^(PC|PR|SP|A\d+|AR\d+|EXCCAUSE|VADDR|LBEG|LEND|SAR|EPC\d+|EPS\d+|PS)$', reg): + registers[reg] = val + else: + # Standard format + # regex for registers: we want standalone pairs like PC 0x123 or A0 0x123 or EXCCAUSE 9 + reg_pattern = re.compile(r'\b([A-Z0-9]+)\s+(0x[0-9a-fA-F]+|\d+|(?:nil))\b') + for match in reg_pattern.finditer(content): + reg = match.group(1) + val_str = match.group(2) + if val_str == "(nil)": + val = 0 + elif val_str.startswith("0x"): + val = int(val_str, 16) + else: + val = int(val_str) + + # Keep only known registers or likely candidates + if re.match(r'^(PC|PR|SP|A\d+|AR\d+|EXCCAUSE|VADDR|LBEG|LEND|SAR|EPC\d+|EPS\d+|PS)$', reg): + registers[reg] = val + + # Backtrace parsing + bt_idx = content.find("Backtrace:") + if bt_idx != -1: + bt_line = content[bt_idx:content.find('\n', bt_idx)] + bt_pattern = re.compile(r'(0x[0-9a-fA-F]+):(0x[0-9a-fA-F]+)') + for match in bt_pattern.finditer(bt_line): + pc = int(match.group(1), 16) + sp = int(match.group(2), 16) + backtraces.append((pc, sp)) + + return registers, backtraces, content + +def get_objdump_output(elf_path, objdump_cmd): + print(f"Running {objdump_cmd} -d -S -l \"{elf_path}\" ...") + try: + # Check if objdump exists + result = subprocess.run([objdump_cmd, "-d", "-S", "-l", elf_path], + capture_output=True, text=True, check=True) + return result.stdout + except FileNotFoundError: + print(f"Error: {objdump_cmd} not found. Please provide the correct objdump command using --objdump.") + sys.exit(1) + except subprocess.CalledProcessError as e: + print(f"Error running objdump: {e}") + sys.exit(1) + +def parse_linker_cmd(filepath): + regions = [] + try: + with open(filepath, 'r') as f: + content = f.read() + m_block = re.search(r'MEMORY\s*\{(.*?)\}', content, re.DOTALL) + if m_block: + for line in m_block.group(1).splitlines(): + line = line.strip() + if not line or ':' not in line: continue + name, rest = line.split(':', 1) + name = name.strip() + m_org = re.search(r'org\s*=\s*(.*?),', rest) + m_len = re.search(r'len\s*=\s*(.*)', rest) + if m_org and m_len: + org_expr = m_org.group(1).strip() + len_expr = m_len.group(1).strip() + try: + org_val = eval(org_expr) + len_val = eval(len_expr) + # Ignore debug regions + if not (name.startswith('.debug') or name.startswith('.stab')): + regions.append({'name': name, 'start': org_val, 'end': org_val + len_val}) + except Exception: + pass + except Exception as e: + print(f"Warning: Failed to parse {filepath}: {e}") + return regions + +def parse_zephyr_stat(filepath): + sections = [] + try: + with open(filepath, 'r') as f: + for line in f: + m = re.match(r'^\s*\[\s*\d+\]\s+(\S+)\s+[A-Z0-9]+\s+([0-9a-fA-F]+)\s+[0-9a-fA-F]+\s+([0-9a-fA-F]+)', line) + if m: + name = m.group(1) + start = int(m.group(2), 16) + size = int(m.group(3), 16) + # Ignore debug sections + if size > 0 and not (name.startswith('.debug') or name.startswith('.stab')): + sections.append({'name': name, 'start': start, 'end': start + size}) + except Exception as e: + print(f"Warning: Failed to parse {filepath}: {e}") + return sections + +def parse_zephyr_dts(filepath): + regions = [] + try: + with open(filepath, 'r') as f: + lines = f.read().splitlines() + + current_node_path = [] + for line in lines: + line = line.strip() + # Simple node match: node_name: some_name@addr { + m_node = re.match(r'^(?:[a-zA-Z0-9_]+:\s*)?([a-zA-Z0-9_\-]+(?:@[0-9a-fA-Fx]+)?)\s*\{', line) + if m_node: + node_name = m_node.group(1) + current_node_path.append(node_name) + continue + + if line == "};": + if current_node_path: + current_node_path.pop() + continue + + # match reg = < ... > + m_reg = re.match(r'^reg\s*=\s*<\s*(.*?)\s*>;', line) + if m_reg and current_node_path: + reg_vals = m_reg.group(1).split() + if len(reg_vals) >= 2: + try: + addr = int(reg_vals[0], 16) if reg_vals[0].startswith('0x') else int(reg_vals[0]) + size = int(reg_vals[1], 16) if reg_vals[1].startswith('0x') else int(reg_vals[1]) + if size > 0: + node_name = current_node_path[-1] + regions.append({'name': node_name, 'start': addr, 'end': addr + size}) + except ValueError: + pass + except Exception as e: + print(f"Warning: Failed to parse {filepath}: {e}") + return regions + +def build_address_map(objdump_text): + current_func = "<unknown>" + current_context = [] + last_was_asm = False + address_map = {} + + func_re = re.compile(r'^([0-9a-fA-F]+)\s+<([^>]+)>:$') + asm_re = re.compile(r'^\s*([0-9a-fA-F]+):\s+(.*)$') + + for line in objdump_text.splitlines(): + line = line.rstrip() + if not line or line.startswith("Disassembly of section"): + continue + + m_func = func_re.match(line) + if m_func: + current_func = m_func.group(2) + current_context = [] + last_was_asm = False + continue + + m_asm = asm_re.match(line) + if m_asm: + addr = int(m_asm.group(1), 16) + address_map[addr] = { + 'func': current_func, + 'context': list(current_context), + 'asm': m_asm.group(2) + } + last_was_asm = True + continue + + if last_was_asm: + current_context = [] + last_was_asm = False + current_context.append(line) + + return address_map + +def find_closest_instruction(addr, address_map, sorted_addresses): + if addr in address_map: + return addr, address_map[addr] + + # Extract lower 29 bits for physical address mappings on Xtensa + physical = addr & 0x1FFFFFFF + if physical in address_map: + return physical, address_map[physical] + + idx = bisect.bisect_right(sorted_addresses, physical) + if idx > 0: + closest = sorted_addresses[idx-1] + # Return if within 16 bytes (typical small instruction offset) + if physical - closest < 16: + return closest, address_map[closest] + + return addr, None + +def decode_ps_bits(val): + intlevel = val & 0xF + excm = (val >> 4) & 1 + um = (val >> 5) & 1 + ring = (val >> 6) & 3 + owb = (val >> 8) & 0xF + callinc = (val >> 16) & 3 + woe = (val >> 18) & 1 + + flags = [] + flags.append(f"INTLEVEL:{intlevel}") + if excm: flags.append("EXCM") + flags.append(f"UM:{um}") + flags.append(f"RING:{ring}") + flags.append(f"OWB:{owb}") + flags.append(f"CALLINC:{callinc}") + flags.append(f"WOE:{woe}") + + return " | ".join(flags) + +def main(): + # Set default color explicitly at start + print("\x1b[0m", end='', flush=True) + + parser = argparse.ArgumentParser(description="Decode Xtensa/Zephyr crash dump using objdump.") + parser.add_argument("--elf", required=False, help="Path to the ELF file. Overridden if --build-dir is provided.") + parser.add_argument("--build-dir", required=False, help="Path to the Zephyr build directory.") + parser.add_argument("--dump", default="-", help="Path to the crash dump file. Default is '-' for stdin.") + parser.add_argument("--clipboard", action="store_true", help="Read crash dump from the clipboard instead of file/stdin.") + parser.add_argument("--objdump", default="xtensa-sof-zephyr-elf-objdump", help="Objdump command to use. e.g. xtensa-zephyr-elf-objdump") + args = parser.parse_args() + + objdump_cmd = args.objdump + + if args.build_dir: + # Resolve zephyr.elf + default_elf = os.path.join(args.build_dir, "zephyr", "zephyr.elf") + if os.path.isfile(default_elf): + args.elf = default_elf + + # Try to find objdump from compile_commands + cc_path = os.path.join(args.build_dir, "compile_commands.json") + if not os.path.isfile(cc_path): + cc_path = os.path.join(args.build_dir, "compile_commands.txt") + + if os.path.isfile(cc_path): + try: + with open(cc_path, 'r') as f: + cc_data = json.load(f) + if cc_data and len(cc_data) > 0 and 'command' in cc_data[0]: + # The command might contain arguments, we extract the first token + cmd_tokens = shlex.split(cc_data[0]['command']) + compiler_path = cmd_tokens[0] + # Replace gcc, g++, clang, etc. with objdump + if compiler_path.endswith('gcc') or compiler_path.endswith('g++') or compiler_path.endswith('cc'): + new_cmd = re.sub(r'(g?cc|g\+\+)$', 'objdump', compiler_path) + if os.path.isfile(new_cmd) and os.access(new_cmd, os.X_OK): + objdump_cmd = new_cmd + except Exception as e: + print(f"Warning: Failed to parse {cc_path} to deduce objdump: {e}") + + linker_regions = [] + stat_sections = [] + dts_regions = [] + if args.build_dir: + linker_cmd_path = os.path.join(args.build_dir, "zephyr", "linker.cmd") + zephyr_stat_path = os.path.join(args.build_dir, "zephyr", "zephyr.stat") + zephyr_dts_path = os.path.join(args.build_dir, "zephyr", "zephyr.dts") + + if os.path.isfile(linker_cmd_path): + linker_regions = parse_linker_cmd(linker_cmd_path) + if os.path.isfile(zephyr_stat_path): + stat_sections = parse_zephyr_stat(zephyr_stat_path) + if os.path.isfile(zephyr_dts_path): + dts_regions = parse_zephyr_dts(zephyr_dts_path) + + if not args.elf: + print("Error: --elf or --build-dir must be provided.") + sys.exit(1) + + if not os.path.isfile(args.elf): + print(f"Cannot find ELF file: {args.elf}") + sys.exit(1) + + if args.clipboard: + try: + dump_content = subprocess.check_output(['xclip', '-o', '-selection', 'clipboard'], text=True) + except (subprocess.CalledProcessError, FileNotFoundError): + try: + dump_content = subprocess.check_output(['xsel', '--clipboard', '--output'], text=True) + except (subprocess.CalledProcessError, FileNotFoundError): + try: + dump_content = subprocess.check_output(['wl-paste'], text=True) + except (subprocess.CalledProcessError, FileNotFoundError): + print("Error: Could not read from clipboard. Make sure xclip, xsel, or wl-paste is installed.") + sys.exit(1) + elif args.dump == "-": + dump_content = sys.stdin.read() + else: + if not os.path.isfile(args.dump): + print(f"Cannot find Dump file: {args.dump}") + sys.exit(1) + with open(args.dump, 'r') as f: + dump_content = f.read() + + registers, backtraces, raw_content = parse_crash_log(dump_content) + + print(f"Found {len(registers)} registers and {len(backtraces)} backtrace elements in crash dump.") + + print("Parsing objdump (this may take a few seconds)...") + + # Actually, many systems might use standard xtensa-zephyr-elf-objdump + # We can try to dynamically choose if the user just provided a prefix or left default + + # Try running the objdump to ensure it exists + import shutil + if not os.path.isfile(objdump_cmd) and not shutil.which(objdump_cmd) and "zephyr" in objdump_cmd: + # try without sof if user has a different one + alt_cmds = [ + "xtensa-zephyr-elf-objdump", + "xtensa-intel-elf-objdump", + "zephyr-sdk/xtensa-zephyr-elf-objdump", + "objdump" + ] + for alt in alt_cmds: + if shutil.which(alt): + print(f"Warning: {objdump_cmd} not found, falling back to {alt}") + objdump_cmd = alt + break + + objdump_text = get_objdump_output(args.elf, objdump_cmd) + address_map = build_address_map(objdump_text) + sorted_addresses = sorted(address_map.keys()) + + print("\n--- Summary ---") + print("PS Register Legend:") + print(" INTLEVEL : Interrupt Level EXCM : Exception Mode") + print(" UM : User Mode (1=User) RING : Privilege Ring") + print(" OWB : Old Window Base WOE : Window Overflow Enable") + print(" CALLINC : Call Increment") + print() + + def print_decoded(name, val): + if val == 0: + print(f"{name:5}: 0x00000000 -> (nil)") + return + + addr, info = find_closest_instruction(val, address_map, sorted_addresses) + if info: + print(f"{name:5}: 0x{val:08x} -> <{info['func']}>") + for ctx in info['context']: + ctx_strip = ctx.strip() + if re.match(r'^[^ \t:]+:\d+', ctx_strip): + print(f" \x1b[35m{ctx_strip}\x1b[0m") + else: + print(f" \x1b[93m{ctx_strip}\x1b[0m") + print(f" \x1b[93m{addr:08x}: {info['asm']}\x1b[0m") + print() + else: + dts_str = "" + for d in dts_regions: + if d['start'] <= val < d['end']: + dts_str = f", DT: {d['name']}" + break + region_str = "" + for r in linker_regions: + if r['start'] <= val < r['end']: + region_str = f", Region: {r['name']}" + break + sec_str = "" + for s in stat_sections: + if s['start'] <= val < s['end']: + sec_str = f", Section: {s['name']}" + break + + if dts_str or region_str or sec_str: + print(f"{name:5}: 0x{val:08x} -> <unknown{dts_str}{region_str}{sec_str}>") + else: + print(f"{name:5}: 0x{val:08x} -> <unknown/no code section>") + + # Prioritize specific registers + for reg in ['PC', 'EXCCAUSE', 'VADDR', 'SP', 'PS']: + if reg in registers: + if reg == 'EXCCAUSE': + cause_code = registers[reg] + cause_str = XTENSA_EXCCAUSE.get(cause_code, "Unknown/Unassigned") + print(f"EXCCAUSE: {cause_code} ({cause_str})") + elif reg == 'VADDR': + print(f"{reg:5}: 0x{registers[reg]:08x}") + elif reg == 'PS': + print(f"{reg:5}: 0x{registers[reg]:08x} -> [{decode_ps_bits(registers[reg])}]\n") + else: + print_decoded(reg, registers[reg]) + + for i in range(1, 8): + reg = f"EPC{i}" + if reg in registers: + print_decoded(reg, registers[reg]) + + print() + for i in range(2, 8): + reg = f"EPS{i}" + if reg in registers: + print(f"{reg:5}: 0x{registers[reg]:08x} -> [{decode_ps_bits(registers[reg])}]") + + print("\n--- Physical Windowed Registers (A) ---") + for i in range(16): + reg = f"A{i}" + if reg in registers: + print_decoded(reg, registers[reg]) + + print("\n--- Saved Stack Registers (AR) ---") + for i in range(64): + reg = f"AR{i}" + if reg in registers: + print_decoded(reg, registers[reg]) + + print("\n--- Backtrace Decode ---") + # Backtraces: + for i, (pc, sp) in enumerate(backtraces): + print(f"Frame {i}: SP = 0x{sp:08x}") + print_decoded("PC", pc) + +if __name__ == '__main__': + main() diff --git a/scripts/sof-post-commit-hook.sh b/scripts/sof-post-commit-hook.sh deleted file mode 100755 index 171ab58a2471..000000000000 --- a/scripts/sof-post-commit-hook.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/sh -# SPDX-License-Identifier: BSD-3-Clause -# Copyright(c) 2018 Intel Corporation. All rights reserved. - -set -e -# TODO: reduce duplication with scripts/sof-pre-commit-hook.sh -# and with .github/workflows/ with either some .conf file -# or some wrapper script -exec git show --format=email HEAD | - ./scripts/checkpatch.pl --no-tree --strict --codespell diff --git a/scripts/sof-pre-commit-hook.sh b/scripts/sof-pre-commit-hook.sh deleted file mode 100755 index cd754f6db661..000000000000 --- a/scripts/sof-pre-commit-hook.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash -# SPDX-License-Identifier: BSD-3-Clause -# Copyright(c) 2018 Intel Corporation. All rights reserved. - -exec git diff --cached | scripts/checkpatch.pl --no-tree --codespell --no-signoff -q - - diff --git a/scripts/sof-qemu-run.py b/scripts/sof-qemu-run.py new file mode 100755 index 000000000000..fc985e254d51 --- /dev/null +++ b/scripts/sof-qemu-run.py @@ -0,0 +1,192 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(c) 2026 Intel Corporation. All rights reserved. +""" +sof-qemu-run.py - Automated QEMU test runner and crash analyzer + +This script runs `west -v build -t run` for Zephyr, monitors the output, and +waits for 2 seconds after the last log event. If a Zephyr/QEMU crash occurs, +it decodes it using `sof-crash-decode.py`. If no crash occurred, it enters +the QEMU monitor (Ctrl-A c), issues `info registers`, and passes the output +to the crash decoder. +""" + +import sys +import pexpect +import subprocess +import argparse +import os + +import re + +# ANSI Color Codes +COLOR_RED = "\x1b[31;1m" +COLOR_YELLOW = "\x1b[33;1m" +COLOR_RESET = "\x1b[0m" + +def colorize_line(line): + """Colorize significant Zephyr log items.""" + if "<err>" in line or "[ERR]" in line or "Fatal fault" in line or "Oops" in line: + return COLOR_RED + line + COLOR_RESET + elif "<wrn>" in line or "[WRN]" in line: + return COLOR_YELLOW + line + COLOR_RESET + return line + +def check_for_crash(output): + """ + Check if the collected output indicates a crash. + Look for known Zephyr/Xtensa oops or exceptions, or QEMU crash register dumps. + """ + crash_keywords = [ + "Fatal fault", + "Oops", + "ZEPHYR FATAL ERROR", + "Exception", + "PC=", # QEMU PC output format + "EXCCAUSE=", + "Backtrace:" + ] + for keyword in crash_keywords: + if keyword in output: + return True + return False + +def run_sof_crash_decode(build_dir, dump_content): + """ + Invokes sof-crash-decode.py and feeds it the crash dump via stdin. + """ + # Find sof-crash-decode.py in the same directory as this script, or fallback to relative path + decoder_script = os.path.join(os.path.dirname(os.path.abspath(__file__)), "sof-crash-decode.py") + if not os.path.isfile(decoder_script): + decoder_script = "sof-crash-decode.py" + + print("\n====================================") + print("Running sof-crash-decode.py Analysis") + print("====================================\n") + + cmd = [sys.executable, decoder_script, "--build-dir", build_dir] + + try: + proc = subprocess.Popen(cmd, stdin=subprocess.PIPE) + proc.communicate(input=dump_content.encode('utf-8')) + except Exception as e: + print(f"Failed to run sof-crash-decode.py: {e}") + +def main(): + parser = argparse.ArgumentParser(description="Run QEMU via west and automatically decode crashes.") + parser.add_argument("--build-dir", default="build", help="Path to the build directory containing zephyr.elf, linker.cmd, etc. Defaults to 'build'.") + parser.add_argument("--log-file", default="qemu-run.log", help="Path to save the QEMU output log. Defaults to 'qemu-run.log'.") + args = parser.parse_args() + + # Make absolute path just in case + build_dir = os.path.abspath(args.build_dir) + + print(f"Starting QEMU test runner. Monitoring for crashes (Build Dir: {args.build_dir})...") + + # We will use pexpect to spawn the west command to get PTY features + import shutil + west_path = shutil.which("west") + if not west_path: + print("[sof-qemu-run] Error: 'west' command not found in PATH.") + print("Please ensure you have sourced the Zephyr environment (e.g., source zephyr-env.sh).") + sys.exit(1) + + child = pexpect.spawn(west_path, ["-v", "build", "-t", "run"], encoding='utf-8') + + # We will accumulate output to check for crashes + full_output = "" + + with open(args.log_file, "w") as log_file: + try: + # Loop reading output until EOF or a timeout occurs + qemu_started = False + while True: + try: + # Read character by character or line by line + # Pexpect's readline() doesn't consistently trigger timeout on idle + # We can use read_nonblocking and an explicit exceptTIMEOUT + index = child.expect([r'\r\n', pexpect.TIMEOUT, pexpect.EOF], timeout=2) + if index == 0: + line = child.before + '\n' + # Strip ANSI escape codes from output to write raw text to log file + clean_line = re.sub(r'\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])', '', line) + log_file.write(clean_line) + log_file.flush() + + colored_line = colorize_line(line) + sys.stdout.write(colored_line) + sys.stdout.flush() + + full_output += line + if not qemu_started and ("Booting Zephyr OS" in line or "To exit from QEMU" in line or "qemu-system-" in line): + qemu_started = True + elif index == 1: # TIMEOUT + if qemu_started or check_for_crash(full_output): + print("\n\n[sof-qemu-run] 2 seconds passed since last log event. Checking status...") + break + else: + # Still building or loading, continue waiting + pass + elif index == 2: # EOF + print("\n\n[sof-qemu-run] QEMU process terminated.") + break + + except pexpect.TIMEOUT: + if qemu_started or check_for_crash(full_output): + print("\n\n[sof-qemu-run] 2 seconds passed since last log event. Checking status...") + break + else: + # Still building or loading, continue waiting + pass + except pexpect.EOF: + print("\n\n[sof-qemu-run] QEMU process terminated.") + break + + except KeyboardInterrupt: + print("\n[sof-qemu-run] Interrupted by user.") + # Proceed with what we have + + crashed = check_for_crash(full_output) + + if crashed: + print("\n[sof-qemu-run] Detected crash signature in standard output!") + # Stop QEMU if it's still running + if child.isalive(): + child.sendline("\x01x") # Ctrl-A x to quit qemu + child.close(force=True) + + run_sof_crash_decode(build_dir, full_output) + else: + print("\n[sof-qemu-run] No crash detected. Interacting with QEMU Monitor to grab registers...") + + # We need to send Ctrl-A c to enter the monitor + if child.isalive(): + child.send("\x01c") # Ctrl-A c + try: + # Wait for (qemu) prompt + child.expect(r"\(qemu\)", timeout=5) + # Send "info registers" + child.sendline("info registers") + # Wait for the next prompt + child.expect(r"\(qemu\)", timeout=5) + + info_regs_output = child.before + print("\n[sof-qemu-run] Successfully extracted registers from QEMU monitor.\n") + + # Quit qemu safely + child.sendline("quit") + child.expect(pexpect.EOF, timeout=2) + child.close() + + # Run the decoder on the intercepted register output + run_sof_crash_decode(build_dir, info_regs_output) + except pexpect.TIMEOUT: + print("\n[sof-qemu-run] Timed out waiting for QEMU monitor. Is it running?") + child.close(force=True) + except pexpect.EOF: + print("\n[sof-qemu-run] QEMU terminated before we could run monitor commands.") + else: + print("\n[sof-qemu-run] Process is no longer alive, cannot extract registers.") + +if __name__ == "__main__": + main() diff --git a/scripts/sof-qemu-run.sh b/scripts/sof-qemu-run.sh new file mode 100755 index 000000000000..e1ece1dd5125 --- /dev/null +++ b/scripts/sof-qemu-run.sh @@ -0,0 +1,28 @@ +#!/bin/bash +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(c) 2026 Intel Corporation. All rights reserved. + +# Define the build directory from the first argument (or default) +BUILD_DIR="${1:-build}" + +# Find and source the zephyr environment script, typically via the sof-venv wrapper +# or directly if running in a known zephyrproject layout. +# We will use the existing helper sof-venv.sh to get the right environment. + +# Get the directory of this script +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +SOF_WORKSPACE="$(dirname "$(dirname "$SCRIPT_DIR")")" + +# Use the SOF workspace to locate the virtual environment +VENV_DIR="$SOF_WORKSPACE/.venv" +echo "Using SOF environment at $SOF_WORKSPACE" + +# start the virtual environment +source ${VENV_DIR}/bin/activate + +# Execute the QEMU runner from within the correct build directory +cd "${BUILD_DIR}" || exit 1 + +# Finally run the python script which will now correctly inherit 'west' from the sourced environment. +python3 "${SCRIPT_DIR}/sof-qemu-run.py" --build-dir "${BUILD_DIR}" + diff --git a/scripts/sof-testbench-build-profile.sh b/scripts/sof-testbench-build-profile.sh index 342f0710ff23..73a880a353b9 100755 --- a/scripts/sof-testbench-build-profile.sh +++ b/scripts/sof-testbench-build-profile.sh @@ -11,7 +11,9 @@ usage() { echo } -MODULES_S32="asrc dcblock drc drc_multiband eqfir eqiir gain src tdfb" +MODULES_S32_44K_48K="asrc src" +MODULES_S32="dcblock drc drc_multiband dolby-dax eqfir eqiir gain level_multiplier micsel \ + sound_dose stft_process_1536_240_ template_comp tdfb" MODULES_S24="aria" if [ -z "${SOF_WORKSPACE}" ]; then @@ -61,6 +63,13 @@ $HELPER -x -t development/sof-hda-benchmark-generic.tplg -n 1,2,3 \ -p "$PDIR/profile-$PLATFORM-benchmark.txt" > "$PDIR/log-$PLATFORM-benchmark.txt" # Profile modules +for mod in $MODULES_S32_44K_48K +do + echo "Profiling $mod ..." + $HELPER -r 44100 -x -m "$mod" \ + -p "$PDIR/profile-$PLATFORM-$mod.txt" > "$PDIR/log-$PLATFORM-$mod.txt" +done + for mod in $MODULES_S32 do echo "Profiling $mod ..." diff --git a/scripts/sof-testbench-helper.sh b/scripts/sof-testbench-helper.sh index 5de7d8d43a7b..ea7a6590ee67 100755 --- a/scripts/sof-testbench-helper.sh +++ b/scripts/sof-testbench-helper.sh @@ -15,7 +15,8 @@ usage() { echo " -n <pipelines>, default 1,2" echo " -o <output wav>, default none" echo " -p <profiling result text>, use with -x, default none" - echo " -r <rate>, default 48000" + echo " -r <rate>, input rate, default 48000" + echo " -R <rate>, output rate, default 48000" echo " -t <force topology>, default none, e.g. production/sof-hda-generic.tplg" echo " -v runs with valgrind, not available with -x" echo " -x runs testbench with xt-run simulator" @@ -55,7 +56,7 @@ PROFILE=false TPLG0= VALGRIND= -while getopts "b:c:hi:km:n:o:p:r:t:vx" opt; do +while getopts "b:c:hi:km:n:o:p:r:R:t:vx" opt; do case "${opt}" in b) BITS=${OPTARG} @@ -89,6 +90,8 @@ while getopts "b:c:hi:km:n:o:p:r:t:vx" opt; do ;; r) RATE_IN=${OPTARG} + ;; + R) RATE_OUT=${OPTARG} ;; t) @@ -147,10 +150,10 @@ if [[ "$XTRUN" == true ]]; then echo " input: $INFILE1, output: $OUTFILE1, trace: $TRACEFILE, profile: $PROFILETXT" source "$XTB4_SETUP" if [[ $PROFILE == true ]]; then - "$XTENSA_PATH"/xt-run --profile="$PROFILEOUT" "$XTB4" $OPTS 2> "$TRACEFILE" + "$XTENSA_PATH"/xt-run --mem_model --profile="$PROFILEOUT" "$XTB4" $OPTS 2> "$TRACEFILE" "$XTENSA_PATH"/xt-gprof "$XTB4" "$PROFILEOUT" > "$PROFILETXT" else - "$XTENSA_PATH"/xt-run "$XTB4" $OPTS 2> "$TRACEFILE" + "$XTENSA_PATH"/xt-run --mem_model "$XTB4" $OPTS 2> "$TRACEFILE" fi else if [ ! -x "$TB4" ]; then diff --git a/scripts/xtensa-build-zephyr.py b/scripts/xtensa-build-zephyr.py index 40931ba6623a..36c0a25aabb5 100755 --- a/scripts/xtensa-build-zephyr.py +++ b/scripts/xtensa-build-zephyr.py @@ -92,18 +92,6 @@ class PlatformConfig: # These cannot be built by everyone out of the box yet. # For instance: there's no open-source toolchain available for them yet. extra_platform_configs = { - "nvl" : PlatformConfig( - "intel", "intel_adsp/ace40/nvl", - f"RI-2022.10{xtensa_tools_version_postfix}", - "ace4px_HiFi5MMU_PIF_nlib", - ipc4 = True - ), - "nvl-s" : PlatformConfig( - "intel", "intel_adsp/ace40/nvls", - f"RI-2022.10{xtensa_tools_version_postfix}", - "ace4px_HiFi5MMU_PIF_nlib", - ipc4 = True - ), "wcl-sim" : PlatformConfig( "intel", "intel_adsp/ace30/wcl/sim", f"RI-2022.10{xtensa_tools_version_postfix}", @@ -123,6 +111,12 @@ class PlatformConfig: "rmb_LX7_HiFi5_PROD", RIMAGE_KEY = "key param ignored by acp_6_0" ), + "acp_7_0" : PlatformConfig( + "amd", "acp_7_0_adsp/acp_7_0", + f"RI-2023.11{xtensa_tools_version_postfix}", + "ACP_7_0_HiFi5_NNE_PROD", + RIMAGE_KEY = "key param ignored by acp_7_0" + ), # MediaTek platforms # (move to platform_configs_all on next Zephyr SDK release after 0.17.0) "mt8195" : PlatformConfig( @@ -145,6 +139,11 @@ class PlatformConfig: f"RJ-2024.3{xtensa_tools_version_postfix}", "HiFi5_MPU_lock_2023_11", ), + "mt8365" : PlatformConfig( + "mtk", "mt8365/mt8365/adsp", + f"RJ-2024.3{xtensa_tools_version_postfix}", + "hifi4_Aquila_E2_PROD", + ), } # These can all be built out of the box. --all builds all these. @@ -192,7 +191,18 @@ class PlatformConfig: "ace30_LX7HiFi4_PIF", ipc4 = True ), - + "nvl" : PlatformConfig( + "intel", "intel_adsp/ace40/nvl", + f"RI-2022.10{xtensa_tools_version_postfix}", + "ace4px_HiFi5MMU_PIF_nlib", + ipc4 = True + ), + "nvl-s" : PlatformConfig( + "intel", "intel_adsp/ace40/nvls", + f"RI-2022.10{xtensa_tools_version_postfix}", + "ace4px_HiFi5MMU_PIF_nlib", + ipc4 = True + ), # NXP platforms "imx8" : PlatformConfig( "imx", "imx8qm_mek/mimx8qm6/adsp", @@ -212,6 +222,10 @@ class PlatformConfig: "hifi4_mscale_v2_0_2_prod", RIMAGE_KEY = "key param ignored by imx8m" ), + "imx8m_cm7" : PlatformConfig( + "imx", "imx8mp_evk/mimx8ml8/m7/ddr", + "", "", "", "" + ), "imx8ulp" : PlatformConfig( "imx", "imx8ulp_evk/mimx8ud7/adsp", f"RI-2023.11{xtensa_tools_version_postfix}", @@ -222,6 +236,14 @@ class PlatformConfig: "imx", "imx95_evk/mimx9596/m7/ddr", "", "", "", "" ), + "qemu_xtensa" : PlatformConfig( + "zephyr", "qemu_xtensa/dc233c", + "", "", "zephyr" + ), + "qemu_xtensa_mmu" : PlatformConfig( + "zephyr", "qemu_xtensa/dc233c/mmu", + "", "", "zephyr" + ), } platform_configs = platform_configs_all.copy() @@ -688,9 +710,13 @@ def clean_staging(platform): def rimage_west_configuration(platform_dict, dest_dir): """Configure rimage in a new file `dest_dir/westconfig.ini`, starting from the workspace .west/config. - Returns a tuple (west ConfigFile, pathlib.Path to that new file). + Returns a tuple: (west.configuration.Configuration, pathlib.Path to that new file). """ + # This complex and painful saving and copying can be dropped and + # greatly simplified once any of these alternatives gets finally + # implemented in `west config`: + # https://github.com/zephyrproject-rtos/west/issues/429 or 849, 867,.. saved_local_var = os.environ.get('WEST_CONFIG_LOCAL') workspace_west_config_path = os.environ.get('WEST_CONFIG_LOCAL', str(west_top / ".west" / "config")) @@ -885,6 +911,7 @@ def build_platforms(): platform_build_dir_name = f"build-{platform}" PLAT_CONFIG = platform_dict["PLAT_CONFIG"] + build_cmd = ["west"] build_cmd += ["-v"] * args.verbose if args.menuconfig: @@ -936,6 +963,15 @@ def build_platforms(): platform_dict, STAGING_DIR / "sof-info" / platform ) + + # SOF marks ELF sections in the range [SOF_MODULE_DRAM_LINK_START, SOF_MODULE_DRAM_LINK_END] + # as "detached". On imx8m_cm7 that address range overlaps with the M7 ITCM, so rimage would + # incorrectly classify ITCM sections as detached. Pass -d to ignore detached classification + if platform == "imx8m_cm7": + existing = platform_wcfg.get("rimage.extra-args") or "" + platform_wcfg.set("rimage.extra-args", + shlex.join(shlex.split(existing) + ["-d"])) + platf_build_environ['WEST_CONFIG_LOCAL'] = str(wcfg_path) # Make sure the build logs don't leave anything hidden @@ -1135,8 +1171,9 @@ def install_platform(platform, sof_output_dir, platf_build_environ, platform_wco install_key_dir = install_key_dir / args.key_type_subdir os.makedirs(install_key_dir, exist_ok=True) - # looses file owner and group - file is commonly accessible - shutil.copy2(abs_build_dir / "zephyr.ri", install_key_dir / output_fwname) + # looses file owner and group - file is commonly accessible, dont install qemu. + if platform not in ("qemu_xtensa", "qemu_xtensa_mmu"): + shutil.copy2(abs_build_dir / "zephyr.ri", install_key_dir / output_fwname) if args.deployable_build and platform_configs[platform].ipc4: # IPC4 deployable builds are using separate directories per platforms @@ -1286,9 +1323,11 @@ def gzip_compress(fname, gzdst=None): # Don't run sof_ri_info and ignore silently .ri files that don't have one. RI_INFO_UNSUPPORTED = [] -RI_INFO_UNSUPPORTED += ['imx8', 'imx8x', 'imx8m', 'imx8ulp', 'imx95'] -RI_INFO_UNSUPPORTED += ['rn', 'acp_6_0'] -RI_INFO_UNSUPPORTED += ['mt8186', 'mt8188', 'mt8195', 'mt8196'] +RI_INFO_UNSUPPORTED += ['imx8', 'imx8x', 'imx8m', 'imx8m_cm7', 'imx8ulp', 'imx95'] +RI_INFO_UNSUPPORTED += ['rn', 'acp_6_0', 'acp_7_0'] +RI_INFO_UNSUPPORTED += ['mt8186', 'mt8188', 'mt8195', 'mt8196', 'mt8365'] +RI_INFO_UNSUPPORTED += ['qemu_xtensa', 'qemu_xtensa_mmu'] + # For temporary workarounds. Unlike _UNSUPPORTED above, the platforms below will print a warning. RI_INFO_FIXME = [ ] diff --git a/src/arch/host/Kconfig b/src/arch/host/Kconfig deleted file mode 100644 index de92f43a868c..000000000000 --- a/src/arch/host/Kconfig +++ /dev/null @@ -1,9 +0,0 @@ -# SPDX-License-Identifier: BSD-3-Clause - -# Host architecture configs - -config CORE_COUNT - int - default 1 - help - Number of used cores diff --git a/src/arch/host/configs/library_defconfig b/src/arch/host/configs/library_defconfig index 5c1ffe029cd6..28c486bec58d 100644 --- a/src/arch/host/configs/library_defconfig +++ b/src/arch/host/configs/library_defconfig @@ -2,6 +2,7 @@ CONFIG_COMP_ARIA=y CONFIG_COMP_ASRC=y CONFIG_COMP_CROSSOVER=y CONFIG_COMP_DCBLOCK=y +CONFIG_COMP_DOLBY_DAX_AUDIO_PROCESSING=y CONFIG_COMP_DRC=y CONFIG_COMP_FIR=y CONFIG_COMP_GOOGLE_RTC_AUDIO_PROCESSING=y @@ -18,8 +19,10 @@ CONFIG_COMP_SEL=y CONFIG_COMP_SOUND_DOSE=y CONFIG_COMP_SRC=y CONFIG_COMP_SRC_IPC4_FULL_MATRIX=y +CONFIG_COMP_STFT_PROCESS=y CONFIG_COMP_STUBS=y CONFIG_COMP_TDFB=y +CONFIG_COMP_TONE=y CONFIG_COMP_VOLUME=y CONFIG_COMP_VOLUME_LINEAR_RAMP=y CONFIG_COMP_VOLUME_WINDOWS_FADE=y diff --git a/src/arch/xtensa/configs/acp_7_0_defconfig b/src/arch/xtensa/configs/acp_7_0_defconfig index 9b7427f6e3aa..c5e3d4952294 100644 --- a/src/arch/xtensa/configs/acp_7_0_defconfig +++ b/src/arch/xtensa/configs/acp_7_0_defconfig @@ -30,7 +30,7 @@ CONFIG_COMP_ARIA=n CONFIG_COMP_BASEFW_IPC4=n CONFIG_COMP_UP_DOWN_MIXER=n CONFIG_COMP_TDFB=n -#CONFIG_COMP_MUX=n +CONFIG_COMP_MUX=n CONFIG_COMP_SEL=n CONFIG_COMP_MIXER=n -CONFIG_AMD_BINARY_BUILD=n +CONFIG_PROBE=n diff --git a/src/audio/CMakeLists.txt b/src/audio/CMakeLists.txt index d9de3c578d8e..29e602871af7 100644 --- a/src/audio/CMakeLists.txt +++ b/src/audio/CMakeLists.txt @@ -86,6 +86,9 @@ if(NOT CONFIG_COMP_MODULE_SHARED_LIBRARY_BUILD) if(CONFIG_COMP_SRC) add_subdirectory(src) endif() + if(CONFIG_COMP_STFT_PROCESS) + add_subdirectory(stft_process) + endif() if(CONFIG_COMP_TDFB) add_subdirectory(tdfb) endif() @@ -121,9 +124,7 @@ if(NOT CONFIG_COMP_MODULE_SHARED_LIBRARY_BUILD) add_subdirectory(mic_privacy_manager) endif() if(CONFIG_COMP_TONE) - add_local_sources(sof - tone.c - ) + add_subdirectory(tone) endif() if(CONFIG_ZEPHYR_NATIVE_DRIVERS) list(APPEND base_files host-zephyr.c) diff --git a/src/audio/Kconfig b/src/audio/Kconfig index 6bc1bd6ca012..1f7d362ffdc2 100644 --- a/src/audio/Kconfig +++ b/src/audio/Kconfig @@ -93,13 +93,6 @@ config COMP_STUBS Select to force all 3P blocks to link against stubs rather than their libraries. This should only be used in testing environments like fuzzers or CI. -config COMP_TONE - bool "Tone component" - select CORDIC_FIXED - help - Select for Tone component. - Warning: This component is deprecated and will be removed from SOF v2.8. - config COMP_KPB bool "KPB component" default y @@ -151,9 +144,11 @@ rsource "selector/Kconfig" rsource "smart_amp/Kconfig" rsource "sound_dose/Kconfig" rsource "src/Kconfig" +rsource "stft_process/Kconfig" rsource "tdfb/Kconfig" rsource "template/Kconfig" rsource "tensorflow/Kconfig" +rsource "tone/Kconfig" rsource "up_down_mixer/Kconfig" rsource "volume/Kconfig" # --- End Kconfig Sources (alphabetical order) --- diff --git a/src/audio/aria/README.md b/src/audio/aria/README.md new file mode 100644 index 000000000000..99dcc595ac6d --- /dev/null +++ b/src/audio/aria/README.md @@ -0,0 +1,23 @@ +# Aria Audio Component Architecture + +This directory contains the implementation of the Aria audio component. + +## Overview + +The Aria component implements an automatic regressive input amplifier that applies variable input gain, reducing gain for high-level signals to prevent clipping while introducing about 1 ms of processing latency. + +## Architecture Diagram + +```mermaid +graph LR + In[Input Data] --> Aria[Aria Core Processing] + Aria --> Out[Output Data] +``` + +## Configuration and Scripts + +- **Kconfig**: Enables the ARIA (Automatic Regressive Input Amplifier Module) component. It applies a variable gain (target: 0, 6, 12, 18 dB) that is reduced for high-level signals to prevent clipping, introducing a 1ms latency. Users can choose HIFI optimization levels (HIFI3, HIFI4, Max, Generic). +- **CMakeLists.txt**: Handles the build integration. Depends on `IPC_MAJOR_4`. If built modularly (`CONFIG_COMP_ARIA="m"`), it uses a loadable extension (`llext`); otherwise, it compiles `aria.c` and HIFI-specific files locally. +- **aria.toml**: Defines the topology parameters for the module (UUID, affinity, pin configuration) and an array of `mod_cfg` configurations defining processing constraints for various setups. +- **Topology (.conf)**: Derived from `tools/topology/topology2/include/components/aria.conf`, it defines the `aria` widget object for topology generation. It exposes attributes like `cpc` (cycles per chunk) and `is_pages`. It sets up an `extctl` byte control with a maximum payload of 4096 bytes and defaults to UUID `6d:16:f7:99:2c:37:ef:43:81:f6:22:00:7a:a1:5f:03` (type `effect`). +- **MATLAB Tuning (`tune/`)**: Contains `.m` scripts (e.g., `sof_aria_blobs.m`) which generate ALSA control binary blobs, text formats, and M4 configuration fragments used at topology creation or runtime to supply operational parameters like attenuation settings (e.g., `param_1.conf`, `param_2.conf`). diff --git a/src/audio/aria/aria.c b/src/audio/aria/aria.c index ef9ced95adde..dc265cfac240 100644 --- a/src/audio/aria/aria.c +++ b/src/audio/aria/aria.c @@ -9,7 +9,6 @@ #include <sof/audio/pipeline.h> #include <rtos/panic.h> #include <sof/ipc/msg.h> -#include <rtos/alloc.h> #include <rtos/cache.h> #include <rtos/init.h> #include <sof/lib/notifier.h> @@ -33,8 +32,6 @@ LOG_MODULE_REGISTER(aria, CONFIG_SOF_LOG_LEVEL); /* these ids aligns windows driver requirement to support windows driver */ SOF_DEFINE_REG_UUID(aria); -DECLARE_TR_CTX(aria_comp_tr, SOF_UUID(aria_uuid), LOG_LEVEL_INFO); - /** * \brief Aria gain index mapping table */ @@ -121,12 +118,12 @@ static int aria_init(struct processing_module *mod) size_t ibs, chc, sgs, sgc, req_mem, att; void *buf; - comp_info(dev, "aria_init()"); + comp_info(dev, "entry"); list_init(&dev->bsource_list); list_init(&dev->bsink_list); - cd = rzalloc(SOF_MEM_FLAG_USER, sizeof(*cd)); + cd = mod_zalloc(mod, sizeof(*cd)); if (!cd) { return -ENOMEM; } @@ -145,10 +142,10 @@ static int aria_init(struct processing_module *mod) } mod_data->private = cd; - buf = rballoc(SOF_MEM_FLAG_USER, req_mem); + buf = mod_balloc(mod, req_mem); if (!buf) { - rfree(cd); + mod_free(mod, cd); comp_err(dev, "allocation failed for size %d", req_mem); return -ENOMEM; } @@ -160,8 +157,8 @@ static int aria_free(struct processing_module *mod) { struct aria_data *cd = module_get_private_data(mod); - rfree(cd->data_addr); - rfree(cd); + mod_free(mod, cd->data_addr); + mod_free(mod, cd); return 0; } @@ -171,11 +168,6 @@ static void aria_set_stream_params(struct comp_buffer *buffer, const struct ipc4_audio_format *audio_fmt = &mod->priv.cfg.base_cfg.audio_fmt; ipc4_update_buffer_format(buffer, audio_fmt); -#if SOF_USE_HIFI(3, ARIA) || SOF_USE_HIFI(4, ARIA) - audio_stream_set_align(8, 1, &buffer->stream); -#elif SOF_USE_HIFI(5, ARIA) - audio_stream_set_align(16, 1, &buffer->stream); -#endif } static int aria_prepare(struct processing_module *mod, @@ -187,7 +179,7 @@ static int aria_prepare(struct processing_module *mod, struct comp_dev *dev = mod->dev; struct aria_data *cd = module_get_private_data(mod); - comp_info(dev, "aria_prepare()"); + comp_info(dev, "entry"); source = comp_dev_get_first_data_producer(dev); sink = comp_dev_get_first_data_consumer(dev); @@ -198,6 +190,7 @@ static int aria_prepare(struct processing_module *mod, aria_set_stream_params(source, mod); aria_set_stream_params(sink, mod); + audio_stream_set_align(SOF_FRAME_BYTE_ALIGN, SOF_FRAME_COUNT_ALIGN, &source->stream); if (audio_stream_get_valid_fmt(&source->stream) != SOF_IPC_FRAME_S24_4LE || audio_stream_get_valid_fmt(&sink->stream) != SOF_IPC_FRAME_S24_4LE) { @@ -228,7 +221,7 @@ static int aria_reset(struct processing_module *mod) struct aria_data *cd = module_get_private_data(mod); int idx; - comp_info(dev, "aria_reset()"); + comp_info(dev, "entry"); if (dev->state == COMP_STATE_ACTIVE) { comp_info(dev, "aria module is in active state. Ignore resetting"); @@ -255,7 +248,7 @@ static int aria_process(struct processing_module *mod, uint32_t copy_bytes; uint32_t frames = input_buffers[0].size; - comp_dbg(dev, "aria_copy()"); + comp_dbg(dev, "entry"); frames = MIN(frames, cd->smpl_group_cnt); @@ -282,7 +275,7 @@ static int aria_set_config(struct processing_module *mod, uint32_t param_id, struct aria_data *cd = module_get_private_data(mod); struct comp_dev *dev = mod->dev; - comp_info(dev, "aria_set_config()"); + comp_info(dev, "entry"); if (param_id == ARIA_SET_ATTENUATION) { if (fragment_size != sizeof(uint32_t)) { comp_err(dev, "Illegal fragment_size = %d", fragment_size); @@ -327,6 +320,7 @@ SOF_LLEXT_BUILDINFO; #else +DECLARE_TR_CTX(aria_comp_tr, SOF_UUID(aria_uuid), LOG_LEVEL_INFO); DECLARE_MODULE_ADAPTER(aria_interface, aria_uuid, aria_comp_tr); SOF_MODULE_INIT(aria, sys_comp_module_aria_interface_init); diff --git a/src/audio/asrc/README.md b/src/audio/asrc/README.md new file mode 100644 index 000000000000..0ed94f83f8dc --- /dev/null +++ b/src/audio/asrc/README.md @@ -0,0 +1,23 @@ +# Asynchronous Sample Rate Converter (ASRC) Architecture + +This directory contains the ASRC component implementation. + +## Overview + +The ASRC component converts the sample rate of an incoming audio stream to a different outgoing sample rate, independently of any synchronous clocks. This is commonly used when crossing clock domains. + +## Architecture Diagram + +```mermaid +graph LR + In[Input Buffer Rate A] --> ASRC[ASRC Core Engine] + ASRC --> Out[Output Buffer Rate B] + Clock[Target Clock Domain] -.-> ASRC +``` + +## Configuration and Scripts + +- **Kconfig**: Controls the ASRC component. ASRC tracks a slave DAI out of sync with firmware timers and uses less RAM than synchronous SRC. It also supports granular configuration of downsampling ratios (from 24kHz/48kHz to lower rates) to optimize memory footprint (about 1.5-2.1 kB per conversion ratio). +- **CMakeLists.txt**: Manages source inclusion (`asrc.c`, `asrc_farrow.c`, etc.). Handles modular builds (`llext`) and chooses IPC abstraction (`asrc_ipc3.c` vs `asrc_ipc4.c`) based on the active IPC major version. +- **asrc.toml**: Holds topology module entry parameters. Features pre-defined `mod_cfg` parameter arrays specifically tailored for `CONFIG_METEORLAKE`, `CONFIG_LUNARLAKE`, and `CONFIG_SOC_ACE30`/`40` SOC platforms. +- **Topology (.conf)**: Derived from `tools/topology/topology2/include/components/asrc.conf`, defining the `asrc` widget object. It introduces parameters like `rate_out` (target sample rate) and `operation_mode`. Defaults to UUID `2d:40:b4:66:68:b4:f2:42:81:a7:b3:71:21:86:3d:d4` and type `asrc`. diff --git a/src/audio/asrc/asrc.c b/src/audio/asrc/asrc.c index 281d2f82aaf7..e9c9e6e736d4 100644 --- a/src/audio/asrc/asrc.c +++ b/src/audio/asrc/asrc.c @@ -11,7 +11,6 @@ #include <sof/audio/ipc-config.h> #include <rtos/panic.h> #include <sof/ipc/msg.h> -#include <rtos/alloc.h> #include <rtos/init.h> #include <sof/lib/uuid.h> #include <sof/math/numbers.h> @@ -101,7 +100,7 @@ static void src_copy_s32(struct processing_module *mod, in_frames, &idx); if (ret) - comp_err(dev, "src_copy_s32(), error %d", ret); + comp_err(dev, "error %d", ret); buf = (int32_t *)cd->obuf[0]; n = out_frames * audio_stream_get_channels(sink); @@ -177,7 +176,7 @@ static void src_copy_s16(struct processing_module *mod, in_frames, &idx); if (ret) - comp_err(dev, "src_copy_s16(), error %d", ret); + comp_err(dev, "error %d", ret); buf = (int16_t *)cd->obuf[0]; n = out_frames * audio_stream_get_channels(sink); @@ -207,17 +206,17 @@ static int asrc_init(struct processing_module *mod) const ipc_asrc_cfg *ipc_asrc = (const ipc_asrc_cfg *)mod_data->cfg.init_data; struct comp_data *cd; - comp_info(dev, "asrc_init(), source_rate=%d, sink_rate=%d, asynchronous_mode=%d, operation_mode=%d", + comp_info(dev, "source_rate=%d, sink_rate=%d, asynchronous_mode=%d, operation_mode=%d", asrc_get_source_rate(ipc_asrc), asrc_get_sink_rate(ipc_asrc), asrc_get_asynchronous_mode(ipc_asrc), asrc_get_operation_mode(ipc_asrc)); /* validate init data - either SRC sink or source rate must be set */ if (asrc_get_source_rate(ipc_asrc) == 0 || asrc_get_sink_rate(ipc_asrc) == 0) { - comp_err(dev, "asrc_init(), sink or source rates are not set"); + comp_err(dev, "sink or source rates are not set"); return -EINVAL; } - cd = rzalloc(SOF_MEM_FLAG_USER, sizeof(*cd)); + cd = mod_zalloc(mod, sizeof(*cd)); if (!cd) return -ENOMEM; @@ -242,7 +241,7 @@ static int asrc_init(struct processing_module *mod) return 0; } -static int asrc_initialize_buffers(struct asrc_farrow *src_obj) +static int asrc_initialize_buffers(struct processing_module *mod, struct asrc_farrow *src_obj) { int32_t *buf_32; int16_t *buf_16; @@ -261,7 +260,7 @@ static int asrc_initialize_buffers(struct asrc_farrow *src_obj) buffer_size = src_obj->buffer_length * sizeof(int32_t); for (ch = 0; ch < src_obj->num_channels; ch++) { - buf_32 = rzalloc(SOF_MEM_FLAG_USER, buffer_size); + buf_32 = mod_zalloc(mod, buffer_size); if (!buf_32) return -ENOMEM; @@ -272,7 +271,7 @@ static int asrc_initialize_buffers(struct asrc_farrow *src_obj) buffer_size = src_obj->buffer_length * sizeof(int16_t); for (ch = 0; ch < src_obj->num_channels; ch++) { - buf_16 = rzalloc(SOF_MEM_FLAG_USER, buffer_size); + buf_16 = mod_zalloc(mod, buffer_size); if (!buf_16) return -ENOMEM; @@ -284,7 +283,7 @@ static int asrc_initialize_buffers(struct asrc_farrow *src_obj) return 0; } -static void asrc_release_buffers(struct asrc_farrow *src_obj) +static void asrc_release_buffers(struct processing_module *mod, struct asrc_farrow *src_obj) { int32_t *buf_32; int16_t *buf_16; @@ -299,7 +298,7 @@ static void asrc_release_buffers(struct asrc_farrow *src_obj) if (buf_32) { src_obj->ring_buffers32[ch] = NULL; - rfree(buf_32); + mod_free(mod, buf_32); } } else @@ -308,7 +307,7 @@ static void asrc_release_buffers(struct asrc_farrow *src_obj) if (buf_16) { src_obj->ring_buffers16[ch] = NULL; - rfree(buf_16); + mod_free(mod, buf_16); } } } @@ -318,13 +317,13 @@ static int asrc_free(struct processing_module *mod) struct comp_data *cd = module_get_private_data(mod); struct comp_dev *dev = mod->dev; - comp_dbg(dev, "asrc_free()"); + comp_dbg(dev, "entry"); - rfree(cd->buf); - asrc_release_buffers(cd->asrc_obj); - asrc_free_polyphase_filter(cd->asrc_obj); - rfree(cd->asrc_obj); - rfree(cd); + mod_free(mod, cd->buf); + asrc_release_buffers(mod, cd->asrc_obj); + asrc_free_polyphase_filter(mod, cd->asrc_obj); + mod_free(mod, cd->asrc_obj); + mod_free(mod, cd); return 0; } @@ -333,7 +332,7 @@ static int asrc_set_config(struct processing_module *mod, uint32_t config_id, const uint8_t *fragment, size_t fragment_size, uint8_t *response, size_t response_size) { - comp_err(mod->dev, "asrc_set_config()"); + comp_err(mod->dev, "entry"); return -EINVAL; } @@ -344,7 +343,7 @@ static int asrc_verify_params(struct processing_module *mod, struct comp_dev *dev = mod->dev; int ret; - comp_dbg(dev, "asrc_verify_params()"); + comp_dbg(dev, "entry"); /* check whether params->rate (received from driver) are equal * to asrc->source_rate (PLAYBACK) or asrc->sink_rate (CAPTURE) set @@ -387,7 +386,7 @@ static int asrc_params(struct processing_module *mod) struct comp_buffer *sourceb, *sinkb; int err; - comp_info(dev, "asrc_params()"); + comp_info(dev, "entry"); asrc_set_stream_params(cd, pcm_params); @@ -417,7 +416,7 @@ static int asrc_params(struct processing_module *mod) cd->sink_rate = audio_stream_get_rate(&sinkb->stream); if (!cd->sink_rate) { - comp_err(dev, "asrc_params(), zero sink rate"); + comp_err(dev, "zero sink rate"); return -EINVAL; } @@ -436,7 +435,7 @@ static int asrc_params(struct processing_module *mod) cd->sink_frames_max = cd->sink_frames + 10; cd->frames = MAX(cd->source_frames_max, cd->sink_frames_max); - comp_info(dev, "asrc_params(), source_rate=%u, sink_rate=%u, source_frames_max=%d, sink_frames_max=%d", + comp_info(dev, "source_rate=%u, sink_rate=%u, source_frames_max=%d, sink_frames_max=%d", cd->source_rate, cd->sink_rate, cd->source_frames_max, cd->sink_frames_max); @@ -507,7 +506,7 @@ static int asrc_trigger(struct processing_module *mod, int cmd) struct comp_dev *dev = mod->dev; int ret; - comp_info(dev, "asrc_trigger()"); + comp_info(dev, "entry"); /* Enable timestamping in pipeline DAI */ if (cmd == COMP_TRIGGER_START && cd->track_drift) { @@ -547,7 +546,7 @@ static int asrc_prepare(struct processing_module *mod, int ret; int i; - comp_info(dev, "asrc_prepare()"); + comp_info(dev, "entry"); ret = asrc_params(mod); if (ret < 0) @@ -581,12 +580,12 @@ static int asrc_prepare(struct processing_module *mod, /* validate */ if (!sink_period_bytes) { - comp_err(dev, "asrc_prepare(), sink_period_bytes = 0"); + comp_err(dev, "sink_period_bytes = 0"); ret = -EINVAL; goto err; } if (!source_period_bytes) { - comp_err(dev, "asrc_prepare(), source_period_bytes = 0"); + comp_err(dev, "source_period_bytes = 0"); ret = -EINVAL; goto err; } @@ -605,7 +604,7 @@ static int asrc_prepare(struct processing_module *mod, cd->asrc_func = src_copy_s32; break; default: - comp_err(dev, "asrc_prepare(), invalid frame format"); + comp_err(dev, "invalid frame format"); return -EINVAL; } @@ -614,11 +613,10 @@ static int asrc_prepare(struct processing_module *mod, cd->buf_size = (cd->source_frames_max + cd->sink_frames_max) * frame_bytes; - cd->buf = rzalloc(SOF_MEM_FLAG_USER, - cd->buf_size); + cd->buf = mod_zalloc(mod, cd->buf_size); if (!cd->buf) { cd->buf_size = 0; - comp_err(dev, "asrc_prepare(), allocation fail for size %d", + comp_err(dev, "allocation fail for size %d", cd->buf_size); ret = -ENOMEM; goto err; @@ -632,18 +630,17 @@ static int asrc_prepare(struct processing_module *mod, /* Get required size and allocate memory for ASRC */ sample_bits = sample_bytes * 8; - ret = asrc_get_required_size(dev, &cd->asrc_size, + ret = asrc_get_required_size(mod, &cd->asrc_size, audio_stream_get_channels(&sourceb->stream), sample_bits); if (ret) { - comp_err(dev, "asrc_prepare(), get_required_size_bytes failed"); + comp_err(dev, "get_required_size_bytes failed"); goto err_free_buf; } - cd->asrc_obj = rzalloc(SOF_MEM_FLAG_USER, - cd->asrc_size); + cd->asrc_obj = mod_zalloc(mod, cd->asrc_size); if (!cd->asrc_obj) { - comp_err(dev, "asrc_prepare(), allocation fail for size %d", + comp_err(dev, "allocation fail for size %d", cd->asrc_size); cd->asrc_size = 0; ret = -ENOMEM; @@ -659,7 +656,7 @@ static int asrc_prepare(struct processing_module *mod, fs_sec = cd->source_rate; } - ret = asrc_initialise(dev, cd->asrc_obj, audio_stream_get_channels(&sourceb->stream), + ret = asrc_initialise(mod, cd->asrc_obj, audio_stream_get_channels(&sourceb->stream), fs_prim, fs_sec, ASRC_IOF_INTERLEAVED, ASRC_IOF_INTERLEAVED, ASRC_BM_LINEAR, cd->frames, sample_bits, @@ -670,7 +667,7 @@ static int asrc_prepare(struct processing_module *mod, } /* Allocate ring buffers */ - ret = asrc_initialize_buffers(cd->asrc_obj); + ret = asrc_initialize_buffers(mod, cd->asrc_obj); /* check for errors */ if (ret) { @@ -688,7 +685,7 @@ static int asrc_prepare(struct processing_module *mod, cd->skew_min = cd->skew; cd->skew_max = cd->skew; - comp_info(dev, "asrc_prepare(), skew = %d", cd->skew); + comp_info(dev, "skew = %d", cd->skew); ret = asrc_update_drift(dev, cd->asrc_obj, cd->skew); if (ret) { comp_err(dev, "asrc_update_drift(), error %d", ret); @@ -698,12 +695,12 @@ static int asrc_prepare(struct processing_module *mod, return 0; err_free_asrc: - asrc_release_buffers(cd->asrc_obj); - rfree(cd->asrc_obj); + asrc_release_buffers(mod, cd->asrc_obj); + mod_free(mod, cd->asrc_obj); cd->asrc_obj = NULL; err_free_buf: - rfree(cd->buf); + mod_free(mod, cd->buf); cd->buf = NULL; err: @@ -759,7 +756,7 @@ static int asrc_control_loop(struct comp_dev *dev, struct comp_data *cd) /* Prevent divide by zero */ if (delta_sample == 0 || tsd.walclk_rate == 0) { - comp_err(dev, "asrc_control_loop(), DAI timestamp failed"); + comp_err(dev, "DAI timestamp failed"); return -EINVAL; } @@ -799,7 +796,7 @@ static int asrc_process(struct processing_module *mod, int frames_snk; int ret; - comp_dbg(dev, "asrc_process()"); + comp_dbg(dev, "entry"); ret = asrc_control_loop(dev, cd); if (ret) @@ -844,7 +841,7 @@ static int asrc_process(struct processing_module *mod, cd->asrc_func(mod, source_s, sink_s, &consumed, &produced); buffer_stream_writeback(sink, produced * audio_stream_frame_bytes(sink_s)); - comp_dbg(dev, "asrc_process(), consumed = %u, produced = %u", consumed, produced); + comp_dbg(dev, "consumed = %u, produced = %u", consumed, produced); output_buffers[0].size = produced * audio_stream_frame_bytes(sink_s); input_buffers[0].consumed = consumed * audio_stream_frame_bytes(source_s); @@ -858,17 +855,17 @@ static int asrc_reset(struct processing_module *mod) struct comp_dev *dev = mod->dev; struct comp_data *cd = module_get_private_data(mod); - comp_dbg(dev, "asrc_reset(), skew_min=%d, skew_max=%d", cd->skew_min, cd->skew_max); + comp_dbg(dev, "skew_min=%d, skew_max=%d", cd->skew_min, cd->skew_max); /* If any resources feasible to stop */ if (cd->track_drift) asrc_dai_stop_timestamp(cd); /* Free the allocations those were done in prepare() */ - asrc_release_buffers(cd->asrc_obj); - asrc_free_polyphase_filter(cd->asrc_obj); - rfree(cd->asrc_obj); - rfree(cd->buf); + asrc_release_buffers(mod, cd->asrc_obj); + asrc_free_polyphase_filter(mod, cd->asrc_obj); + mod_free(mod, cd->asrc_obj); + mod_free(mod, cd->buf); cd->asrc_obj = NULL; cd->buf = NULL; diff --git a/src/audio/asrc/asrc.toml b/src/audio/asrc/asrc.toml index 946842d13811..2e8f8eac837e 100644 --- a/src/audio/asrc/asrc.toml +++ b/src/audio/asrc/asrc.toml @@ -49,7 +49,7 @@ 12, 0, 0, 0, 20480, 30050000, 384, 192, 0, 30050, 0, 13, 0, 0, 0, 20480, 35152000, 384, 256, 0, 35152, 0, 14, 0, 0, 0, 20480, 81647000, 1536, 1440, 0, 81647, 0] -#elif CONFIG_SOC_INTEL_ACE30 || CONFIG_SOC_INTEL_ACE40 +#elif CONFIG_SOC_ACE30 || CONFIG_SOC_ACE40 mod_cfg = [0, 0, 0, 0, 20480, 29755000, 64, 192, 0, 29755, 0, 1, 0, 0, 0, 20480, 58017000, 64, 384, 0, 58017, 0, 2, 0, 0, 0, 20480, 103471000, 512, 1440, 0, 103471, 0, diff --git a/src/audio/asrc/asrc_farrow.c b/src/audio/asrc/asrc_farrow.c index 987a11408af0..dcd58fad22bf 100644 --- a/src/audio/asrc/asrc_farrow.c +++ b/src/audio/asrc/asrc_farrow.c @@ -11,9 +11,9 @@ #include <rtos/string.h> #include <sof/trace/trace.h> #include <sof/audio/format.h> -#include <sof/lib/fast-get.h> #include <sof/lib/memory.h> #include <user/trace.h> +#include <sof/audio/module_adapter/module/generic.h> #include "asrc_farrow.h" LOG_MODULE_DECLARE(asrc, CONFIG_SOF_LOG_LEVEL); @@ -243,7 +243,7 @@ static const struct asrc_filter_params c_filter_params[CR_NUM] = { * Initialise the pointers to the filters, set the number of filters * and their length */ -static enum asrc_error_code initialise_filter(struct comp_dev *dev, +static enum asrc_error_code initialise_filter(struct processing_module *mod, struct asrc_farrow *src_obj); /* @@ -268,27 +268,28 @@ static enum asrc_error_code initialise_filter(struct comp_dev *dev, * Pointers to each channels data. Buffers are allocated externally. */ -enum asrc_error_code asrc_get_required_size(struct comp_dev *dev, +enum asrc_error_code asrc_get_required_size(struct processing_module *mod, int *required_size, int num_channels, int bit_depth) { + struct comp_dev *dev = mod->dev; int size; /* check for parameter errors */ if (!required_size) { - comp_err(dev, "asrc_get_required_size(), invalid required_size"); + comp_err(dev, "invalid required_size"); return ASRC_EC_INVALID_POINTER; } if (num_channels < 1) { - comp_err(dev, "asrc_get_required_size(), invalid num_channels = %d", + comp_err(dev, "invalid num_channels = %d", num_channels); return ASRC_EC_INVALID_NUM_CHANNELS; } if (bit_depth != 16 && bit_depth != 32) { - comp_err(dev, "asrc_get_required_size_bytes(), invalid bit_depth = %d", + comp_err(dev, "invalid bit_depth = %d", bit_depth); return ASRC_EC_INVALID_BIT_DEPTH; } @@ -318,7 +319,7 @@ enum asrc_error_code asrc_get_required_size(struct comp_dev *dev, return ASRC_EC_OK; } -enum asrc_error_code asrc_initialise(struct comp_dev *dev, +enum asrc_error_code asrc_initialise(struct processing_module *mod, struct asrc_farrow *src_obj, int num_channels, int32_t fs_prim, @@ -331,38 +332,39 @@ enum asrc_error_code asrc_initialise(struct comp_dev *dev, enum asrc_control_mode control_mode, enum asrc_operation_mode operation_mode) { + struct comp_dev *dev = mod->dev; enum asrc_error_code error_code; /* check for parameter errors */ if (!src_obj) { - comp_err(dev, "asrc_initialise(), null src_obj"); + comp_err(dev, "null src_obj"); return ASRC_EC_INVALID_POINTER; } if (num_channels < 1) { - comp_err(dev, "asrc_initialise(), num_channels = %d", + comp_err(dev, "num_channels = %d", num_channels); return ASRC_EC_INVALID_NUM_CHANNELS; } if (fs_prim < 8000 || fs_prim > 192000) { - comp_err(dev, "asrc_initialise(), fs_prim = %d", fs_prim); + comp_err(dev, "fs_prim = %d", fs_prim); return ASRC_EC_INVALID_SAMPLE_RATE; } if (fs_sec < 8000 || fs_sec > 192000) { - comp_err(dev, "asrc_initialise(), fs_src = %d", fs_sec); + comp_err(dev, "fs_src = %d", fs_sec); return ASRC_EC_INVALID_SAMPLE_RATE; } if (buffer_length < 2) { - comp_err(dev, "asrc_initialise(), buffer_length = %d", + comp_err(dev, "buffer_length = %d", buffer_length); return ASRC_EC_INVALID_BUFFER_LENGTH; } if (bit_depth != 16 && bit_depth != 32) { - comp_err(dev, "asrc_initialise(), bit_depth = %d", + comp_err(dev, "bit_depth = %d", bit_depth); return ASRC_EC_INVALID_BIT_DEPTH; } @@ -395,7 +397,7 @@ enum asrc_error_code asrc_initialise(struct comp_dev *dev, /* check conversion ratios */ if (src_obj->fs_ratio == 0 || src_obj->fs_ratio_inv == 0) { - comp_err(dev, "asrc_initialise(), fail to calculate ratios"); + comp_err(dev, "fail to calculate ratios"); return ASRC_EC_INVALID_CONVERSION_RATIO; } @@ -410,11 +412,11 @@ enum asrc_error_code asrc_initialise(struct comp_dev *dev, * also sets the pointer to the corresponding * calc_impulse_response_nX function. */ - error_code = initialise_filter(dev, src_obj); + error_code = initialise_filter(mod, src_obj); /* check for errors */ if (error_code != ASRC_EC_OK) { - comp_err(dev, "asrc_initialise(), failed filter initialise"); + comp_err(dev, "failed filter initialise"); return error_code; } @@ -438,28 +440,30 @@ enum asrc_error_code asrc_initialise(struct comp_dev *dev, return ASRC_EC_OK; } -enum asrc_error_code asrc_set_fs_ratio(struct comp_dev *dev, +enum asrc_error_code asrc_set_fs_ratio(struct processing_module *mod, struct asrc_farrow *src_obj, int32_t fs_prim, int32_t fs_sec) { + struct comp_dev *dev = mod->dev; + /* Check for parameter errors */ if (!src_obj) { - comp_err(dev, "asrc_set_fs_ratio(), null src_obj"); + comp_err(dev, "null src_obj"); return ASRC_EC_INVALID_POINTER; } if (!src_obj->is_initialised) { - comp_err(dev, "asrc_set_fs_ratio(), not initialised"); + comp_err(dev, "not initialised"); return ASRC_EC_INIT_FAILED; } if (fs_prim < 8000 || fs_prim > 192000) { - comp_err(dev, "asrc_set_fs_ratio(), fs_prim = %d", fs_prim); + comp_err(dev, "fs_prim = %d", fs_prim); return ASRC_EC_INVALID_SAMPLE_RATE; } if (fs_sec < 8000 || fs_sec > 192000) { - comp_err(dev, "asrc_set_fs_ratio(), fs_sec = %d", fs_sec); + comp_err(dev, "fs_sec = %d", fs_sec); return ASRC_EC_INVALID_SAMPLE_RATE; } @@ -479,7 +483,7 @@ enum asrc_error_code asrc_set_fs_ratio(struct comp_dev *dev, /* check conversion ratios */ if (src_obj->fs_ratio == 0 || src_obj->fs_ratio_inv == 0) { - comp_err(dev, "asrc_set_fs_ratio(), failed to calculate ratios"); + comp_err(dev, "failed to calculate ratios"); return ASRC_EC_INVALID_CONVERSION_RATIO; } @@ -490,10 +494,10 @@ enum asrc_error_code asrc_set_fs_ratio(struct comp_dev *dev, /* See initialise_asrc(...) for further information * Update the filters accordingly */ - enum asrc_error_code error_code = initialise_filter(dev, src_obj); + enum asrc_error_code error_code = initialise_filter(mod, src_obj); /* check for errors */ if (error_code != ASRC_EC_OK) { - comp_err(dev, "asrc_set_fs_ratio(), failed filter initialise"); + comp_err(dev, "failed filter initialise"); return error_code; } @@ -520,12 +524,12 @@ enum asrc_error_code asrc_set_input_format(struct comp_dev *dev, { /* check for parameter errors */ if (!src_obj) { - comp_err(dev, "asrc_set_input_format(), null src_obj"); + comp_err(dev, "null src_obj"); return ASRC_EC_INVALID_POINTER; } if (!src_obj->is_initialised) { - comp_err(dev, "asrc_set_input_format(), not initialised"); + comp_err(dev, "not initialised"); return ASRC_EC_INIT_FAILED; } @@ -540,12 +544,12 @@ enum asrc_error_code asrc_set_output_format(struct comp_dev *dev, { /* check for parameter errors */ if (!src_obj) { - comp_err(dev, "asrc_set_output_format(), null src_obj"); + comp_err(dev, "null src_obj"); return ASRC_EC_INVALID_POINTER; } if (!src_obj->is_initialised) { - comp_err(dev, "asrc_set_output_format(), not initialised"); + comp_err(dev, "not initialised"); return ASRC_EC_INIT_FAILED; } @@ -554,28 +558,29 @@ enum asrc_error_code asrc_set_output_format(struct comp_dev *dev, return ASRC_EC_OK; } -static const int32_t *__get_polyphase_filter(const int32_t *filter, size_t size) +static const int32_t *__get_polyphase_filter(struct processing_module *mod, + const int32_t *filter, size_t size) { #if CONFIG_FAST_GET - return fast_get(filter, size); + return mod_fast_get(mod, filter, size); #else return filter; #endif } -#define get_polyphase_filter(f) __get_polyphase_filter(f, sizeof(f)) +#define get_polyphase_filter(m, f) __get_polyphase_filter(m, f, sizeof(f)) -static void put_polyphase_filter(const int32_t *filter) +static void put_polyphase_filter(struct processing_module *mod, const int32_t *filter) { #if CONFIG_FAST_GET - fast_put(filter); + mod_fast_put(mod, filter); #endif } -void asrc_free_polyphase_filter(struct asrc_farrow *src_obj) +void asrc_free_polyphase_filter(struct processing_module *mod, struct asrc_farrow *src_obj) { if (src_obj && src_obj->polyphase_filters) { - put_polyphase_filter(src_obj->polyphase_filters); + put_polyphase_filter(mod, src_obj->polyphase_filters); src_obj->polyphase_filters = NULL; } } @@ -583,9 +588,10 @@ void asrc_free_polyphase_filter(struct asrc_farrow *src_obj) /* * FILTER FUNCTIONS */ -static enum asrc_error_code initialise_filter(struct comp_dev *dev, +static enum asrc_error_code initialise_filter(struct processing_module *mod, struct asrc_farrow *src_obj) { + struct comp_dev *dev = mod->dev; int fs_in; int fs_out; @@ -606,11 +612,11 @@ static enum asrc_error_code initialise_filter(struct comp_dev *dev, /* Reset coefficients for possible exit with error. */ src_obj->filter_length = 0; src_obj->num_filters = 0; - asrc_free_polyphase_filter(src_obj); + asrc_free_polyphase_filter(mod, src_obj); if (fs_in == 0 || fs_out == 0) { /* Avoid possible divisions by zero. */ - comp_err(dev, "initialise_filter(), fs_in = %d, fs_out = %d", + comp_err(dev, "fs_in = %d, fs_out = %d", fs_in, fs_out); return ASRC_EC_INVALID_SAMPLE_RATE; } else if (fs_in == 48000 && fs_out >= 48000) { @@ -622,7 +628,7 @@ static enum asrc_error_code initialise_filter(struct comp_dev *dev, c_filter_params[CR_48000TO48000].filter_length; src_obj->num_filters = c_filter_params[CR_48000TO48000].num_filters; - src_obj->polyphase_filters = get_polyphase_filter(coeff48000to48000); + src_obj->polyphase_filters = get_polyphase_filter(mod, coeff48000to48000); } else if (fs_in <= fs_out) { /* All upsampling use cases can share the same set of * filter coefficients. @@ -631,7 +637,7 @@ static enum asrc_error_code initialise_filter(struct comp_dev *dev, c_filter_params[CR_44100TO48000].filter_length; src_obj->num_filters = c_filter_params[CR_44100TO48000].num_filters; - src_obj->polyphase_filters = get_polyphase_filter(coeff44100to48000); + src_obj->polyphase_filters = get_polyphase_filter(mod, coeff44100to48000); } else if (fs_in == 48000) { switch (fs_out) { #if (CONFIG_ASRC_SUPPORT_CONVERSION_48000_TO_08000) @@ -640,7 +646,7 @@ static enum asrc_error_code initialise_filter(struct comp_dev *dev, c_filter_params[CR_48000TO08000].filter_length; src_obj->num_filters = c_filter_params[CR_48000TO08000].num_filters; - src_obj->polyphase_filters = get_polyphase_filter(coeff48000to08000); + src_obj->polyphase_filters = get_polyphase_filter(mod, coeff48000to08000); break; #endif #if (CONFIG_ASRC_SUPPORT_CONVERSION_48000_TO_11025) @@ -649,7 +655,7 @@ static enum asrc_error_code initialise_filter(struct comp_dev *dev, c_filter_params[CR_48000TO11025].filter_length; src_obj->num_filters = c_filter_params[CR_48000TO11025].num_filters; - src_obj->polyphase_filters = get_polyphase_filter(coeff48000to11025); + src_obj->polyphase_filters = get_polyphase_filter(mod, coeff48000to11025); break; #endif #if (CONFIG_ASRC_SUPPORT_CONVERSION_48000_TO_12000) @@ -658,7 +664,7 @@ static enum asrc_error_code initialise_filter(struct comp_dev *dev, c_filter_params[CR_48000TO12000].filter_length; src_obj->num_filters = c_filter_params[CR_48000TO12000].num_filters; - src_obj->polyphase_filters = get_polyphase_filter(coeff48000to12000); + src_obj->polyphase_filters = get_polyphase_filter(mod, coeff48000to12000); break; #endif #if (CONFIG_ASRC_SUPPORT_CONVERSION_48000_TO_16000) @@ -667,7 +673,7 @@ static enum asrc_error_code initialise_filter(struct comp_dev *dev, c_filter_params[CR_48000TO16000].filter_length; src_obj->num_filters = c_filter_params[CR_48000TO16000].num_filters; - src_obj->polyphase_filters = get_polyphase_filter(coeff48000to16000); + src_obj->polyphase_filters = get_polyphase_filter(mod, coeff48000to16000); break; #endif #if (CONFIG_ASRC_SUPPORT_CONVERSION_48000_TO_22050) @@ -676,7 +682,7 @@ static enum asrc_error_code initialise_filter(struct comp_dev *dev, c_filter_params[CR_48000TO22050].filter_length; src_obj->num_filters = c_filter_params[CR_48000TO22050].num_filters; - src_obj->polyphase_filters = get_polyphase_filter(coeff48000to22050); + src_obj->polyphase_filters = get_polyphase_filter(mod, coeff48000to22050); break; #endif #if (CONFIG_ASRC_SUPPORT_CONVERSION_48000_TO_24000) @@ -685,7 +691,7 @@ static enum asrc_error_code initialise_filter(struct comp_dev *dev, c_filter_params[CR_48000TO24000].filter_length; src_obj->num_filters = c_filter_params[CR_48000TO24000].num_filters; - src_obj->polyphase_filters = get_polyphase_filter(coeff48000to24000); + src_obj->polyphase_filters = get_polyphase_filter(mod, coeff48000to24000); break; #endif #if (CONFIG_ASRC_SUPPORT_CONVERSION_48000_TO_32000) @@ -694,7 +700,7 @@ static enum asrc_error_code initialise_filter(struct comp_dev *dev, c_filter_params[CR_48000TO32000].filter_length; src_obj->num_filters = c_filter_params[CR_48000TO32000].num_filters; - src_obj->polyphase_filters = get_polyphase_filter(coeff48000to32000); + src_obj->polyphase_filters = get_polyphase_filter(mod, coeff48000to32000); break; #endif #if (CONFIG_ASRC_SUPPORT_CONVERSION_48000_TO_44100) @@ -703,11 +709,11 @@ static enum asrc_error_code initialise_filter(struct comp_dev *dev, c_filter_params[CR_48000TO44100].filter_length; src_obj->num_filters = c_filter_params[CR_48000TO44100].num_filters; - src_obj->polyphase_filters = get_polyphase_filter(coeff48000to44100); + src_obj->polyphase_filters = get_polyphase_filter(mod, coeff48000to44100); break; #endif default: - comp_err(dev, "initialise_filter(), fs_out = %d", + comp_err(dev, "fs_out = %d", fs_out); return ASRC_EC_INVALID_SAMPLE_RATE; } @@ -719,7 +725,7 @@ static enum asrc_error_code initialise_filter(struct comp_dev *dev, c_filter_params[CR_24000TO08000].filter_length; src_obj->num_filters = c_filter_params[CR_24000TO08000].num_filters; - src_obj->polyphase_filters = get_polyphase_filter(coeff24000to08000); + src_obj->polyphase_filters = get_polyphase_filter(mod, coeff24000to08000); break; #endif #if (CONFIG_ASRC_SUPPORT_CONVERSION_24000_TO_16000) @@ -728,23 +734,23 @@ static enum asrc_error_code initialise_filter(struct comp_dev *dev, c_filter_params[CR_24000TO16000].filter_length; src_obj->num_filters = c_filter_params[CR_24000TO16000].num_filters; - src_obj->polyphase_filters = get_polyphase_filter(coeff24000to16000); + src_obj->polyphase_filters = get_polyphase_filter(mod, coeff24000to16000); break; #endif default: - comp_err(dev, "initialise_filter(), fs_out = %d", + comp_err(dev, "fs_out = %d", fs_out); return ASRC_EC_INVALID_SAMPLE_RATE; } } else { /* Conversion ratio is not supported. */ - comp_err(dev, "initialise_filter(), fs_in = %d", fs_in); + comp_err(dev, "fs_in = %d", fs_in); return ASRC_EC_INVALID_SAMPLE_RATE; } /* Check that filter length does not exceed allocated */ if (src_obj->filter_length > ASRC_MAX_FILTER_LENGTH) { - comp_err(dev, "initialise_filter(), filter_length %d exceeds max", + comp_err(dev, "filter_length %d exceeds max", src_obj->filter_length); return ASRC_EC_INVALID_FILTER_LENGTH; } @@ -766,7 +772,7 @@ static enum asrc_error_code initialise_filter(struct comp_dev *dev, src_obj->calc_ir = &asrc_calc_impulse_response_n7; break; default: - comp_err(dev, "initialise_filter(), num_filters = %d", + comp_err(dev, "num_filters = %d", src_obj->num_filters); return ASRC_EC_INVALID_CONVERSION_RATIO; } @@ -783,12 +789,12 @@ enum asrc_error_code asrc_update_drift(struct comp_dev *dev, /* check for parameter errors */ if (!src_obj) { - comp_err(dev, "asrc_update_drift(), null src_obj"); + comp_err(dev, "null src_obj"); return ASRC_EC_INVALID_POINTER; } if (!src_obj->is_initialised) { - comp_err(dev, "asrc_update_drift(), not initialised"); + comp_err(dev, "not initialised"); return ASRC_EC_INIT_FAILED; } @@ -799,7 +805,7 @@ enum asrc_error_code asrc_update_drift(struct comp_dev *dev, /* Skew is Q2.30 */ if (clock_skew < SKEW_MIN || clock_skew > SKEW_MAX) { - comp_err(dev, "asrc_update_drift(), clock_skew = %d", + comp_err(dev, "clock_skew = %d", clock_skew); return ASRC_EC_INVALID_CLOCK_SKEW; } @@ -831,12 +837,12 @@ enum asrc_error_code asrc_update_fs_ratio(struct comp_dev *dev, /* Check input for errors */ if (!src_obj) { - comp_err(dev, "asrc_update_fs_ratio(), null src_obj"); + comp_err(dev, "null src_obj"); return ASRC_EC_INVALID_POINTER; } if (!src_obj->is_initialised) { - comp_err(dev, "asrc_update_fs_ratio(), not initialized"); + comp_err(dev, "not initialized"); return ASRC_EC_INIT_FAILED; } diff --git a/src/audio/asrc/asrc_farrow.h b/src/audio/asrc/asrc_farrow.h index 766ead172d58..48e564c2d1e1 100644 --- a/src/audio/asrc/asrc_farrow.h +++ b/src/audio/asrc/asrc_farrow.h @@ -231,7 +231,7 @@ struct asrc_farrow { * @param[in] bit_depth The wordlength that will be used for representing * the PCM samples, must be 16 or 32. */ -enum asrc_error_code asrc_get_required_size(struct comp_dev *dev, +enum asrc_error_code asrc_get_required_size(struct processing_module *mod, int *required_size, int num_channels, int bit_depth); @@ -268,7 +268,7 @@ enum asrc_error_code asrc_get_required_size(struct comp_dev *dev, * @param[in] operation_mode Choose 'push' or 'pull', depending on the mode * you want your ASRC to operate in. */ -enum asrc_error_code asrc_initialise(struct comp_dev *dev, +enum asrc_error_code asrc_initialise(struct processing_module *mod, struct asrc_farrow *src_obj, int num_channels, int32_t fs_prim, @@ -286,7 +286,7 @@ enum asrc_error_code asrc_initialise(struct comp_dev *dev, * * @param[in] src_obj Pointer to the ias_src_farrow. */ -void asrc_free_polyphase_filter(struct asrc_farrow *src_obj); +void asrc_free_polyphase_filter(struct processing_module *mod, struct asrc_farrow *src_obj); /* * @brief Process the sample rate converter for one frame; the frame @@ -591,7 +591,7 @@ enum asrc_error_code asrc_update_fs_ratio(struct comp_dev *dev, * @param[in] fs_prim Primary sampling rate. * @param[in] fs_sec Secondary sampling rate. */ -enum asrc_error_code asrc_set_fs_ratio(struct comp_dev *dev, +enum asrc_error_code asrc_set_fs_ratio(struct processing_module *mod, struct asrc_farrow *src_obj, int32_t fs_prim, int32_t fs_sec); diff --git a/src/audio/asrc/asrc_ipc3.c b/src/audio/asrc/asrc_ipc3.c index 30dfbecddc6b..ca917318d7e5 100644 --- a/src/audio/asrc/asrc_ipc3.c +++ b/src/audio/asrc/asrc_ipc3.c @@ -4,7 +4,6 @@ #include <sof/audio/module_adapter/module/generic.h> #include <sof/audio/ipc-config.h> -#include <rtos/alloc.h> #include <rtos/init.h> #include <sof/lib/uuid.h> #include <sof/trace/trace.h> diff --git a/src/audio/asrc/asrc_ipc4.c b/src/audio/asrc/asrc_ipc4.c index 3e5d8bf7818f..1047799f207b 100644 --- a/src/audio/asrc/asrc_ipc4.c +++ b/src/audio/asrc/asrc_ipc4.c @@ -4,7 +4,6 @@ #include <sof/audio/module_adapter/module/generic.h> #include <sof/audio/ipc-config.h> -#include <rtos/alloc.h> #include <rtos/init.h> #include <sof/lib/uuid.h> #include <sof/trace/trace.h> diff --git a/src/audio/asrc/llext/CMakeLists.txt b/src/audio/asrc/llext/CMakeLists.txt index 8ef1d2e26fa6..27d5889d166a 100644 --- a/src/audio/asrc/llext/CMakeLists.txt +++ b/src/audio/asrc/llext/CMakeLists.txt @@ -4,6 +4,7 @@ sof_llext_build("asrc" SOURCES ../asrc.c ../asrc_farrow_hifi3.c + ../asrc_farrow_hifi5.c ../asrc_farrow.c ../asrc_farrow_generic.c ../asrc_ipc4.c diff --git a/src/audio/audio_stream.c b/src/audio/audio_stream.c index c4c5f7b04acb..aa16617f066c 100644 --- a/src/audio/audio_stream.c +++ b/src/audio/audio_stream.c @@ -51,6 +51,6 @@ void audio_stream_init(struct audio_stream *audio_stream, void *buff_addr, uint3 audio_stream->addr = buff_addr; audio_stream->end_addr = (char *)audio_stream->addr + size; - audio_stream_set_align(1, 1, audio_stream); + audio_stream_set_align(SOF_FRAME_BYTE_ALIGN, SOF_FRAME_COUNT_ALIGN, audio_stream); audio_stream_reset(audio_stream); } diff --git a/src/audio/base_fw.c b/src/audio/base_fw.c index ed7f131d04e4..b86db469765a 100644 --- a/src/audio/base_fw.c +++ b/src/audio/base_fw.c @@ -11,7 +11,9 @@ #include <ipc4/base_fw_vendor.h> #include <ipc4/pipeline.h> #include <ipc4/logging.h> +#include <ipc4/notification.h> #include <ipc/topology.h> +#include <ipc/compress_params.h> #include <sof_versions.h> #include <sof/lib/cpu-clk-manager.h> #include <sof/lib/cpu.h> @@ -44,6 +46,90 @@ DECLARE_TR_CTX(basefw_comp_tr, SOF_UUID(basefw_uuid), LOG_LEVEL_INFO); static struct ipc4_system_time_info global_system_time_info; static uint64_t global_cycle_delta; +__cold static uint32_t get_host_buffer_size(void) +{ + struct sof_dma *dma_host; + uint32_t periods; + + assert_can_be_cold(); + + dma_host = sof_dma_get(SOF_DMA_DIR_HMEM_TO_LMEM, 0, SOF_DMA_DEV_HOST, + SOF_DMA_ACCESS_SHARED); + if (!dma_host) { + LOG_WRN("Failed to get host DMA channel"); + return 0; + } + + periods = dma_host->plat_data.period_count; + + sof_dma_put(dma_host); + + return periods; +} + +struct sof_ipc4_codec_info_data { + uint32_t count; + uint32_t items[32]; +} __packed __aligned(4); + +/** + * Encodes codec and direction information into a single 32-bit value. + * @param codec Codec type (bits 0-7) + * @param dir Stream direction (bits 8-11) + * @return Encoded 32-bit value + */ +#define SET_CODEC_INFO_ITEM(codec, dir) (((codec) & 0xff) | (((dir) & 0xf) << 8)) + +static void get_codec_info(struct sof_tlv **tuple) +{ + struct sof_ipc4_codec_info_data codec_info = { 0 }; + +#ifdef CONFIG_CADENCE_CODEC_AAC_DEC + codec_info.items[codec_info.count++] = + SET_CODEC_INFO_ITEM(SND_AUDIOCODEC_AAC, SOF_IPC_STREAM_PLAYBACK); +#endif +#ifdef CONFIG_CADENCE_CODEC_MP3_DEC + codec_info.items[codec_info.count++] = + SET_CODEC_INFO_ITEM(SND_AUDIOCODEC_MP3, SOF_IPC_STREAM_PLAYBACK); +#endif +#ifdef CONFIG_CADENCE_CODEC_MP3_ENC + codec_info.items[codec_info.count++] = + SET_CODEC_INFO_ITEM(SND_AUDIOCODEC_MP3, SOF_IPC_STREAM_CAPTURE); +#endif +#ifdef CONFIG_CADENCE_CODEC_VORBIS_DEC + codec_info.items[codec_info.count++] = + SET_CODEC_INFO_ITEM(SND_AUDIOCODEC_VORBIS, SOF_IPC_STREAM_PLAYBACK); +#endif + + if (!codec_info.count) + return; + + tlv_value_set(*tuple, IPC4_SOF_CODEC_INFO, sizeof(codec_info.count) + + sizeof(codec_info.items[0]) * codec_info.count, &codec_info); + + *tuple = tlv_next(*tuple); +} + +#define SOF_CONFIG_MEMBER_SIZE(struct_name) (sizeof(struct sof_tlv) + \ + sizeof(struct struct_name)) +#define SOF_CONFIG_SIZE_MAX (SOF_CONFIG_MEMBER_SIZE(sof_ipc4_codec_info_data)) + +static void base_fw_sof_config(struct sof_tlv **tuple) +{ + char sof_config_data[SOF_CONFIG_SIZE_MAX] = { 0 }; + struct sof_tlv *sof_config_tuple = (struct sof_tlv *)sof_config_data; + uint32_t sof_config_size; + + get_codec_info(&sof_config_tuple); + sof_config_size = (uint32_t)((char *)sof_config_tuple - sof_config_data); + if (sof_config_size == 0) + return; + + tlv_value_set(*tuple, IPC4_FW_SOF_INFO, sof_config_size, sof_config_data); + + *tuple = tlv_next(*tuple); +} + __cold static int basefw_config(uint32_t *data_offset, char *data) { uint16_t version[4] = {SOF_MAJOR, SOF_MINOR, SOF_MICRO, SOF_BUILD}; @@ -124,6 +210,13 @@ __cold static int basefw_config(uint32_t *data_offset, char *data) tuple = tlv_next(tuple); + tlv_value_uint32_set(tuple, IPC4_FW_MIN_HOST_BUFFER_PERIODS, + get_host_buffer_size()); + + tuple = tlv_next(tuple); + + base_fw_sof_config(&tuple); + /* add platform specific tuples */ basefw_vendor_fw_config(&plat_data_offset, (char *)tuple); @@ -230,6 +323,11 @@ __cold static int basefw_register_kcps(bool first_block, bool last_block, return IPC4_ERROR_INVALID_PARAM; #if CONFIG_KCPS_DYNAMIC_CLOCK_CONTROL + if (data_offset_or_size < sizeof(int32_t)) { + tr_err(&ipc_tr, "basefw_register_kcps: payload too small: %u", data_offset_or_size); + return IPC4_ERROR_INVALID_PARAM; + } + /* value of kcps to request on core 0. Can be negative */ if (core_kcps_adjust(0, *(int32_t *)data)) return IPC4_ERROR_INVALID_PARAM; @@ -260,6 +358,12 @@ __cold static int basefw_resource_allocation_request(bool first_block, bool last if (!(first_block && last_block)) return IPC4_ERROR_INVALID_PARAM; + if (data_offset_or_size < sizeof(struct ipc4_resource_request)) { + tr_err(&ipc_tr, "basefw_resource_allocation_request: payload too small: %u < %zu", + data_offset_or_size, sizeof(struct ipc4_resource_request)); + return IPC4_ERROR_INVALID_PARAM; + } + request = (struct ipc4_resource_request *)data; switch (request->ra_type) { @@ -318,7 +422,7 @@ __cold static int basefw_libraries_info_get(uint32_t *data_offset, char *data) desc = basefw_vendor_get_manifest(); } else { #if CONFIG_LIBRARY_MANAGER - desc = (struct sof_man_fw_desc *)lib_manager_get_library_manifest(lib_id); + desc = lib_manager_get_library_manifest(LIB_MANAGER_PACK_LIB_ID(lib_id)); #else desc = NULL; #endif @@ -327,7 +431,7 @@ __cold static int basefw_libraries_info_get(uint32_t *data_offset, char *data) if (!desc) continue; - libs_info->libraries[lib_id].id = lib_id; + libs_info->libraries[lib_counter].id = lib_id; memcpy_s(libs_info->libraries[lib_counter].name, SOF_MAN_FW_HDR_FW_NAME_LEN, desc->header.name, sizeof(desc->header.name)); libs_info->libraries[lib_counter].major_version = @@ -346,7 +450,7 @@ __cold static int basefw_libraries_info_get(uint32_t *data_offset, char *data) libs_info->library_count = lib_counter; *data_offset = - sizeof(libs_info) + libs_info->library_count * sizeof(libs_info->libraries[0]); + sizeof(*libs_info) + libs_info->library_count * sizeof(libs_info->libraries[0]); return IPC4_SUCCESS; } @@ -425,12 +529,17 @@ __cold static int basefw_pipeline_list_info_get(uint32_t *data_offset, char *dat return IPC4_SUCCESS; } -__cold int set_perf_meas_state(const char *data) +__cold int set_perf_meas_state(uint32_t data_size, const uint8_t *data) { assert_can_be_cold(); #ifdef CONFIG_SOF_TELEMETRY - enum ipc4_perf_measurements_state_set state = *data; + if (data_size < sizeof(*data)) { + tr_err(&ipc_tr, "set_perf_meas_state: payload too small: %u", data_size); + return IPC4_ERROR_INVALID_PARAM; + } + + uint8_t state = *data; switch (state) { case IPC4_PERF_MEASUREMENTS_DISABLED: @@ -531,12 +640,17 @@ __cold static int io_global_perf_data_get(uint32_t *data_off_size, char *data) #endif } -__cold static int io_perf_monitor_state_set(const char *data) +__cold static int io_perf_monitor_state_set(uint32_t data_size, const uint8_t *data) { assert_can_be_cold(); #ifdef CONFIG_SOF_TELEMETRY_IO_PERFORMANCE_MEASUREMENTS - return io_perf_monitor_set_state((enum ipc4_perf_measurements_state_set)*data); + if (data_size < sizeof(*data)) { + tr_err(&ipc_tr, "io_perf_monitor_state_set: payload too small: %u", data_size); + return IPC4_ERROR_INVALID_PARAM; + } + + return io_perf_monitor_set_state(*data); #else return IPC4_UNAVAILABLE; #endif @@ -604,6 +718,33 @@ __cold static int basefw_get_large_config(struct comp_dev *dev, uint32_t param_i data_offset, data); }; +__cold static int basefw_notification_mask_info(uint32_t data_size, const void *data) +{ + const struct ipc4_notification_mask_info *mask_info = data; + + assert_can_be_cold(); + + if (data_size < sizeof(struct ipc4_notification_mask_info)) { + tr_err(&ipc_tr, "basefw_notification_mask_info: payload too small: %u < %zu", + data_size, sizeof(struct ipc4_notification_mask_info)); + return IPC4_ERROR_INVALID_PARAM; + } + + ipc4_update_notification_mask(mask_info->ntfy_mask, mask_info->enabled_mask); + + return IPC4_SUCCESS; +} + +__cold static int basefw_astate_table(void) +{ + assert_can_be_cold(); + + /* Trivial handler possible due to an empty Astate Table requested in get_large_config */ + STATIC_ASSERT(IPC4_MAX_CLK_STATES == 0, IPC4_NON_ZERO_ASTATE_UNSUPPORTED); + + return IPC4_SUCCESS; +} + /** * Handles the DMA Control IPC message to initialize or modify DMA gateway configuration. * @@ -655,12 +796,16 @@ __cold static int basefw_set_large_config(struct comp_dev *dev, uint32_t param_i assert_can_be_cold(); switch (param_id) { + case IPC4_NOTIFICATION_MASK: + return basefw_notification_mask_info(data_offset, data); + case IPC4_ASTATE_TABLE: + return basefw_astate_table(); case IPC4_DMA_CONTROL: return basefw_dma_control(first_block, last_block, data_offset, data); case IPC4_PERF_MEASUREMENTS_STATE: - return set_perf_meas_state(data); + return set_perf_meas_state(data_offset, (const uint8_t *)data); case IPC4_IO_PERF_MEASUREMENTS_STATE: - return io_perf_monitor_state_set(data); + return io_perf_monitor_state_set(data_offset, (const uint8_t *)data); case IPC4_SYSTEM_TIME: return basefw_set_system_time(param_id, first_block, last_block, data_offset, data); diff --git a/src/audio/base_fw_intel.c b/src/audio/base_fw_intel.c index 7fa0d5c28705..510d31190a22 100644 --- a/src/audio/base_fw_intel.c +++ b/src/audio/base_fw_intel.c @@ -22,6 +22,11 @@ #include <zephyr/pm/device_runtime.h> #include <sof/lib/memory.h> +#include <sof/lib_manager.h> + +#if CONFIG_UAOL_INTEL_ADSP +#include <zephyr/drivers/uaol.h> +#endif #include <ipc4/base_fw.h> #include <ipc4/alh.h> @@ -37,6 +42,26 @@ struct ipc4_modules_info { struct sof_man_module modules[0]; } __packed __aligned(4); +/* Sanity check because a subtraction of those sizes is performed later on */ +STATIC_ASSERT(sizeof(struct ipc4_modules_info) < SOF_IPC_MSG_MAX_SIZE, + invalid_modules_info_struct_size); + +#if CONFIG_UAOL_INTEL_ADSP +struct ipc4_uaol_link_capabilities { + uint32_t input_streams_supported : 4; + uint32_t output_streams_supported : 4; + uint32_t bidirectional_streams_supported : 5; + uint32_t rsvd : 19; + uint32_t max_tx_fifo_size; + uint32_t max_rx_fifo_size; +} __packed __aligned(4); + +struct ipc4_uaol_capabilities { + uint32_t link_count; + struct ipc4_uaol_link_capabilities link_caps[]; +} __packed __aligned(4); +#endif /* CONFIG_UAOL_INTEL_ADSP */ + /* * TODO: default to value of ACE1.x platforms. This is defined * in multiple places in Zephyr, mm_drv_intel_adsp.h and @@ -67,7 +92,7 @@ __cold int basefw_vendor_fw_config(uint32_t *data_offset, char *data) tlv_value_uint32_set(tuple, IPC4_SLOW_CLOCK_FREQ_HZ_FW_CFG, IPC4_ALH_CAVS_1_8); tuple = tlv_next(tuple); - tlv_value_uint32_set(tuple, IPC4_UAOL_SUPPORT, 0); + tlv_value_uint32_set(tuple, IPC4_UAOL_SUPPORT, IS_ENABLED(CONFIG_UAOL_INTEL_ADSP)); tuple = tlv_next(tuple); tlv_value_uint32_set(tuple, IPC4_ALH_SUPPORT_LEVEL_FW_CFG, IPC4_ALH_CAVS_1_8); @@ -78,6 +103,61 @@ __cold int basefw_vendor_fw_config(uint32_t *data_offset, char *data) return 0; } +#if CONFIG_UAOL_INTEL_ADSP +#define DEV_AND_COMMA(node) DEVICE_DT_GET(node), +static const struct device *uaol_devs[] = { + DT_FOREACH_STATUS_OKAY(intel_adsp_uaol, DEV_AND_COMMA) +}; + +#if !CONFIG_SOF_OS_LINUX_COMPAT_PRIORITY +__cold static void tlv_value_set_uaol_caps(struct sof_tlv *tuple, uint32_t type) +{ + const size_t dev_count = ARRAY_SIZE(uaol_devs); + struct uaol_capabilities dev_cap; + struct ipc4_uaol_capabilities *caps = (struct ipc4_uaol_capabilities *)tuple->value; + size_t caps_size = offsetof(struct ipc4_uaol_capabilities, link_caps[dev_count]); + size_t i; + int ret; + + assert_can_be_cold(); + + memset(caps, 0, caps_size); + + caps->link_count = dev_count; + for (i = 0; i < dev_count; i++) { + ret = uaol_get_capabilities(uaol_devs[i], &dev_cap); + if (ret) + continue; + + caps->link_caps[i].input_streams_supported = dev_cap.input_streams; + caps->link_caps[i].output_streams_supported = dev_cap.output_streams; + caps->link_caps[i].bidirectional_streams_supported = dev_cap.bidirectional_streams; + caps->link_caps[i].max_tx_fifo_size = dev_cap.max_tx_fifo_size; + caps->link_caps[i].max_rx_fifo_size = dev_cap.max_rx_fifo_size; + } + + tlv_value_set(tuple, type, caps_size, caps); +} +#endif /* CONFIG_SOF_OS_LINUX_COMPAT_PRIORITY */ + +__cold static int uaol_stream_id_to_hda_link_stream_id(int uaol_stream_id) +{ + size_t dev_count = ARRAY_SIZE(uaol_devs); + size_t i; + + assert_can_be_cold(); + + for (i = 0; i < dev_count; i++) { + int hda_link_stream_id = uaol_get_mapped_hda_link_stream_id(uaol_devs[i], + uaol_stream_id); + if (hda_link_stream_id >= 0) + return hda_link_stream_id; + } + + return -1; +} +#endif /* CONFIG_UAOL_INTEL_ADSP */ + __cold int basefw_vendor_hw_config(uint32_t *data_offset, char *data) { struct sof_tlv *tuple = (struct sof_tlv *)data; @@ -103,7 +183,7 @@ __cold int basefw_vendor_hw_config(uint32_t *data_offset, char *data) tuple = tlv_next(tuple); tlv_value_uint32_set(tuple, IPC4_LP_EBB_COUNT_HW_CFG, PLATFORM_LPSRAM_EBB_COUNT); -#if defined(CONFIG_SOC_INTEL_ACE30) || defined(CONFIG_SOC_INTEL_ACE40) +#if defined(CONFIG_SOC_ACE30) || defined(CONFIG_SOC_ACE40) tuple = tlv_next(tuple); tlv_value_uint32_set(tuple, IPC4_I2S_CAPS_HW_CFG, I2S_VER_30_PTL); #endif @@ -120,6 +200,15 @@ __cold int basefw_vendor_hw_config(uint32_t *data_offset, char *data) tlv_value_set(tuple, IPC4_INTEL_MIC_PRIVACY_CAPS_HW_CFG, sizeof(priv_caps), &priv_caps); #endif + /* Linux 7.0 and older do not enable UAOL for any Intel + * hardware, so below capability check will lead to a DSP + * panic. In strict compatibility mode, bypass the capability + * check. */ +#if !defined(CONFIG_SOF_OS_LINUX_COMPAT_PRIORITY) && defined(CONFIG_UAOL_INTEL_ADSP) + tuple = tlv_next(tuple); + tlv_value_set_uaol_caps(tuple, IPC4_UAOL_CAPS_HW_CFG); +#endif + tuple = tlv_next(tuple); *data_offset = (int)((char *)tuple - data); @@ -137,22 +226,51 @@ __cold int basefw_vendor_modules_info_get(uint32_t *data_offset, char *data) { assert_can_be_cold(); - struct sof_man_fw_desc *desc = basefw_vendor_get_manifest(); + struct ipc4_modules_info *const module_info = (struct ipc4_modules_info *)data; + const struct sof_man_fw_desc *desc; + uint32_t curr_mod_cnt, curr_cpy_size, total_mod_cnt = 0; + uint32_t total_size_left = SOF_IPC_MSG_MAX_SIZE - sizeof(struct ipc4_modules_info); + int ret; + + for (int lib_id = 0; lib_id < LIB_MANAGER_MAX_LIBS; ++lib_id) { + if (lib_id == 0) { + desc = basefw_vendor_get_manifest(); + } else { +#if CONFIG_LIBRARY_MANAGER + desc = lib_manager_get_library_manifest(LIB_MANAGER_PACK_LIB_ID(lib_id)); +#else + desc = NULL; +#endif + } - if (!desc) - return IPC4_ERROR_INVALID_PARAM; + if (!desc) + continue; - struct ipc4_modules_info *const module_info = (struct ipc4_modules_info *)data; + curr_mod_cnt = desc->header.num_module_entries; + curr_cpy_size = sizeof(struct sof_man_module) * curr_mod_cnt; + + ret = memcpy_s(&module_info->modules[total_mod_cnt], total_size_left, + (char *)desc + SOF_MAN_MODULE_OFFSET(0), curr_cpy_size); + if (ret) { + tr_err(&basefw_comp_tr, "Couldn't copy module info for %d lib", lib_id); + return IPC4_OUT_OF_MEMORY; + } - module_info->modules_count = desc->header.num_module_entries; + /* replace structure id ("$AME" tag) with runtime info */ + for (uint32_t idx = 0; idx < curr_mod_cnt; ++idx) { + uint32_t mod_id = LIB_MANAGER_PACK_MODULE_ID(lib_id, idx); - for (int idx = 0; idx < module_info->modules_count; ++idx) { - struct sof_man_module *module_entry = - (struct sof_man_module *)((char *)desc + SOF_MAN_MODULE_OFFSET(idx)); - memcpy_s(&module_info->modules[idx], sizeof(module_info->modules[idx]), - module_entry, sizeof(struct sof_man_module)); + module_info->modules[total_mod_cnt + idx].runtime_info.module_id = mod_id; + /* TODO: set bit[0] for modules loaded into ADSP memory */ + module_info->modules[total_mod_cnt + idx].runtime_info.state_flags = 0x0; + } + + total_mod_cnt += curr_mod_cnt; + total_size_left -= curr_cpy_size; } + module_info->modules_count = total_mod_cnt; + *data_offset = sizeof(*module_info) + module_info->modules_count * sizeof(module_info->modules[0]); return IPC4_SUCCESS; @@ -352,17 +470,38 @@ __cold static int fw_config_set_force_l1_exit(const struct sof_tlv *tlv) __cold static int basefw_set_fw_config(bool first_block, bool last_block, uint32_t data_offset, const char *data) { + assert_can_be_cold(); + + /* Validate minimum TLV header (type + length fields) is present */ + if (data_offset < sizeof(struct sof_tlv)) { + tr_err(&basefw_comp_tr, "FW_CONFIG payload too small: %u < %zu", + data_offset, sizeof(struct sof_tlv)); + return IPC4_INVALID_CONFIG_DATA_LEN; + } + const struct sof_tlv *tlv = (const struct sof_tlv *)data; - assert_can_be_cold(); + /* Validate the TLV value payload fits within the reported buffer size */ + if (tlv->length > data_offset - sizeof(struct sof_tlv)) { + tr_err(&basefw_comp_tr, + "FW_CONFIG TLV value truncated: len %u exceeds payload %u", + tlv->length, data_offset); + return IPC4_INVALID_CONFIG_DATA_LEN; + } switch (tlv->type) { case IPC4_DMI_FORCE_L1_EXIT: + if (tlv->length < sizeof(uint32_t)) { + tr_err(&basefw_comp_tr, "DMI_FORCE_L1_EXIT value too small: %u", + tlv->length); + return IPC4_INVALID_CONFIG_DATA_LEN; + } return fw_config_set_force_l1_exit(tlv); default: break; } - tr_warn(&basefw_comp_tr, "returning success for Set FW_CONFIG without handling it"); + + tr_warn(&basefw_comp_tr, "Set FW_CONFIG: no handler for type %u", tlv->type); return 0; } @@ -384,9 +523,15 @@ static int basefw_mic_priv_state_changed(bool first_block, const char *data) { #if CONFIG_INTEL_ADSP_MIC_PRIVACY - tr_info(&basefw_comp_tr, "state changed to %d", *data); + if (data_offset_or_size < sizeof(uint8_t)) { + tr_err(&basefw_comp_tr, "mic_priv_state_changed: payload too small: %u", + data_offset_or_size); + return IPC4_ERROR_INVALID_PARAM; + } - uint32_t mic_disable_status = (uint32_t)(*data); + uint32_t mic_disable_status = (uint32_t)(uint8_t)(*data); + + tr_info(&basefw_comp_tr, "state changed to %u", mic_disable_status); struct mic_privacy_settings settings; mic_privacy_fill_settings(&settings, mic_disable_status); @@ -421,8 +566,9 @@ __cold int basefw_vendor_set_large_config(struct comp_dev *dev, uint32_t param_i __cold int basefw_vendor_dma_control(uint32_t node_id, const char *config_data, size_t data_size) { union ipc4_connector_node_id node = (union ipc4_connector_node_id)node_id; + int dai_index = node.f.v_index; int ret, result; - enum dai_type type; + enum sof_ipc_dai_type type; assert_can_be_cold(); @@ -442,13 +588,27 @@ __cold int basefw_vendor_dma_control(uint32_t node_id, const char *config_data, return IPC4_SUCCESS; case ipc4_i2s_link_output_class: case ipc4_i2s_link_input_class: - type = DAI_INTEL_SSP; + type = SOF_DAI_INTEL_SSP; break; + +#if CONFIG_UAOL_INTEL_ADSP + case ipc4_alh_uaol_stream_link_output_class: + case ipc4_alh_uaol_stream_link_input_class: + type = SOF_DAI_INTEL_UAOL; + dai_index = uaol_stream_id_to_hda_link_stream_id(node.f.v_index); + if (dai_index < 0) { + tr_err(&basefw_comp_tr, + "HDA link stream not found! UAOL node ID: 0x%x", node_id); + return IPC4_INVALID_RESOURCE_ID; + } + break; +#endif + default: return IPC4_INVALID_RESOURCE_ID; } - const struct device *dev = dai_get_device(type, node.f.v_index); + const struct device *dev = dai_get_device(type, dai_index); if (!dev) { tr_err(&basefw_comp_tr, diff --git a/src/audio/buffers/README.md b/src/audio/buffers/README.md new file mode 100644 index 000000000000..fcd4516dd4ab --- /dev/null +++ b/src/audio/buffers/README.md @@ -0,0 +1,20 @@ +# Audio Buffers Architecture + +This directory contains the core audio buffer management. + +## Overview + +Buffers connect the output of one component to the input of the next in an audio pipeline graph. They implement circular (ring) buffer semantics and handle cache coherency for DSP memory. + +## Architecture Diagram + +```mermaid +graph LR + CompA[Component A / Producer] -->|Write| Buf[Ring Buffer] + Buf -->|Read| CompB[Component B / Consumer] +``` + +## Configuration and Scripts + +- **CMakeLists.txt**: Includes the base core buffer source files (`audio_buffer.c`, `comp_buffer.c`). When the `CONFIG_PIPELINE_2_0` feature flag is enabled, it additionally compiles `ring_buffer.c`. +- **Topology (.conf)**: Derived from `tools/topology/topology2/include/components/buffer.conf`, defining the `buffer` widget object. Key parameters include `size` (automatically computed), `periods`, `channels`, and `caps` (capabilities like `dai`, `host`, `pass`, `comp`). Defaults to UUID `92:4c:54:42:92:8e:41:4e:b6:79:34:51:9f:1c:1d:28`. diff --git a/src/audio/buffers/audio_buffer.c b/src/audio/buffers/audio_buffer.c index 8ab0a711e7b1..1ecf8472dd65 100644 --- a/src/audio/buffers/audio_buffer.c +++ b/src/audio/buffers/audio_buffer.c @@ -10,6 +10,10 @@ #include <rtos/panic.h> #include <rtos/alloc.h> #include <ipc/stream.h> +#include <sof/audio/module_adapter/module/generic.h> +#include <module/ipc4/base-config.h> +#include <sof/audio/component.h> +#include <module/module/base.h> #include <sof/audio/audio_buffer.h> #include <sof/audio/sink_api.h> #include <sof/audio/source_api.h> @@ -182,12 +186,44 @@ int audio_buffer_source_set_alignment_constants(struct sof_source *source, return 0; } +uint32_t audio_buffer_sink_get_lft(struct sof_sink *sink) +{ + struct sof_audio_buffer *buffer = sof_audio_buffer_from_sink(sink); + /* get number of ms in the buffer */ + size_t bytes_per_sec = sink_get_frame_bytes(&buffer->_sink_api) * + sink_get_rate(&buffer->_sink_api); + size_t bytes_per_ms = bytes_per_sec / 1000; + + /* round up for frequencies like 44100 */ + if (bytes_per_ms * 1000 != bytes_per_sec) + bytes_per_ms++; + uint32_t us_in_buffer = + 1000 * source_get_data_available(&buffer->_source_api) / bytes_per_ms; + + return us_in_buffer; + + /* + * TODO, Currently there's no DP to DP connection + * >>> the code below is never accessible and won't work because of cache incoherence <<< + * + * to make DP to DP connection possible: + * + * 1) module data must be ALWAYS located in non cached memory alias, allowing + * cross core access to params like period (needed below) and calling + * module_get_deadline for the next module, regardless of cores the modules are + * running on + * 2) comp_buffer must be removed from all pipeline code, replaced with a generic abstract + * class audio_buffer - allowing using comp_buffer and ring_buffer without current + * "hybrid buffer" solution + */ +} + void audio_buffer_init(struct sof_audio_buffer *buffer, uint32_t buffer_type, bool is_shared, const struct source_ops *source_ops, const struct sink_ops *sink_ops, const struct audio_buffer_ops *audio_buffer_ops, struct sof_audio_stream_params *audio_stream_params) { - CORE_CHECK_STRUCT_INIT(&buffer, is_shared); + CORE_CHECK_STRUCT_INIT(buffer, is_shared); buffer->buffer_type = buffer_type; buffer->ops = audio_buffer_ops; assert(audio_buffer_ops->free); diff --git a/src/audio/buffers/comp_buffer.c b/src/audio/buffers/comp_buffer.c index b4f53e7cf7f1..cc38a7dfead7 100644 --- a/src/audio/buffers/comp_buffer.c +++ b/src/audio/buffers/comp_buffer.c @@ -16,8 +16,9 @@ #include <rtos/interrupt.h> #include <rtos/alloc.h> #include <rtos/cache.h> -#include <sof/lib/notifier.h> +#include <sof/lib/vregion.h> #include <sof/list.h> +#include <sof/schedule/dp_schedule.h> #include <rtos/spinlock.h> #include <rtos/symbol.h> #include <ipc/topology.h> @@ -146,20 +147,32 @@ static void comp_buffer_free(struct sof_audio_buffer *audio_buffer) struct comp_buffer *buffer = container_of(audio_buffer, struct comp_buffer, audio_buffer); - struct buffer_cb_free cb_data = { - .buffer = buffer, - }; + buf_dbg(buffer, "entry"); - buf_dbg(buffer, "buffer_free()"); - - notifier_event(buffer, NOTIFIER_ID_BUFFER_FREE, - NOTIFIER_TARGET_CORE_LOCAL, &cb_data, sizeof(cb_data)); +#if CONFIG_PROBE + if (buffer->probe_cb_free) + buffer->probe_cb_free(buffer->probe_cb_arg); +#endif - /* In case some listeners didn't unregister from buffer's callbacks */ - notifier_unregister_all(NULL, buffer); + struct mod_alloc_ctx *alloc = buffer->audio_buffer.alloc; +#ifdef CONFIG_SOF_USERSPACE_LL + assert(alloc); + if (alloc->vreg) + vregion_free(alloc->vreg, buffer->stream.addr); + else + sof_heap_free(alloc->heap, buffer->stream.addr); +#else rfree(buffer->stream.addr); - rfree(buffer); +#endif + + if (alloc && alloc->vreg) { + vregion_free(alloc->vreg, buffer); + if (!vregion_put(alloc->vreg)) + rfree(alloc); + } else { + sof_heap_free(alloc ? alloc->heap : NULL, buffer); + } } APP_TASK_DATA static const struct source_ops comp_buffer_source_ops = { @@ -178,6 +191,7 @@ APP_TASK_DATA static const struct sink_ops comp_buffer_sink_ops = { .audio_set_ipc_params = audio_buffer_sink_set_ipc_params, .on_audio_format_set = audio_buffer_sink_on_audio_format_set, .set_alignment_constants = audio_buffer_sink_set_alignment_constants, + .get_lft = audio_buffer_sink_get_lft, }; static const struct audio_buffer_ops audio_buffer_ops = { @@ -185,27 +199,34 @@ static const struct audio_buffer_ops audio_buffer_ops = { .reset = comp_buffer_reset, .audio_set_ipc_params = comp_buffer_set_ipc_params, .on_audio_format_set = comp_buffer_format_set, - .set_alignment_constants = comp_buffer_set_alignment_constants + .set_alignment_constants = comp_buffer_set_alignment_constants, }; -static struct comp_buffer *buffer_alloc_struct(void *stream_addr, size_t size, +static struct comp_buffer *buffer_alloc_struct(struct mod_alloc_ctx *alloc, + void *stream_addr, size_t size, uint32_t flags, bool is_shared) { struct comp_buffer *buffer; - tr_dbg(&buffer_tr, "buffer_alloc_struct()"); + tr_dbg(&buffer_tr, "entry"); /* allocate new buffer, but add coherent if shared with other cores */ if (is_shared) flags |= SOF_MEM_FLAG_COHERENT; - buffer = rzalloc(flags, sizeof(*buffer)); - + if (!alloc || !alloc->vreg) + buffer = sof_heap_alloc(alloc ? alloc->heap : NULL, flags, sizeof(*buffer), 0); + else if (is_shared) + buffer = vregion_alloc_coherent(alloc->vreg, VREGION_MEM_TYPE_INTERIM, sizeof(*buffer)); + else + buffer = vregion_alloc(alloc->vreg, VREGION_MEM_TYPE_INTERIM, sizeof(*buffer)); if (!buffer) { tr_err(&buffer_tr, "could not alloc structure"); return NULL; } + memset(buffer, 0, sizeof(*buffer)); + buffer->flags = flags; /* Force channels to 2 for init to prevent bad call to clz in buffer_init_stream */ buffer->stream.runtime_stream_params.channels = 2; @@ -220,6 +241,7 @@ static struct comp_buffer *buffer_alloc_struct(void *stream_addr, size_t size, audio_stream_set_underrun(&buffer->stream, !!(flags & SOF_BUF_UNDERRUN_PERMITTED)); audio_stream_set_overrun(&buffer->stream, !!(flags & SOF_BUF_OVERRUN_PERMITTED)); + buffer->audio_buffer.alloc = alloc; comp_buffer_reset_source_list(buffer); comp_buffer_reset_sink_list(buffer); @@ -227,13 +249,13 @@ static struct comp_buffer *buffer_alloc_struct(void *stream_addr, size_t size, return buffer; } -struct comp_buffer *buffer_alloc(size_t size, uint32_t flags, uint32_t align, - bool is_shared) +struct comp_buffer *buffer_alloc(struct mod_alloc_ctx *alloc, size_t size, uint32_t flags, + uint32_t align, bool is_shared) { struct comp_buffer *buffer; void *stream_addr; - tr_dbg(&buffer_tr, "buffer_alloc()"); + tr_dbg(&buffer_tr, "entry"); /* validate request */ if (size == 0) { @@ -241,23 +263,40 @@ struct comp_buffer *buffer_alloc(size_t size, uint32_t flags, uint32_t align, return NULL; } +#ifdef CONFIG_SOF_USERSPACE_LL + assert(alloc); + if (alloc->vreg) + stream_addr = vregion_alloc_align(alloc->vreg, VREGION_MEM_TYPE_INTERIM, size, align); + else + stream_addr = sof_heap_alloc(alloc->heap, flags, size, align); +#else stream_addr = rballoc_align(flags, size, align); +#endif if (!stream_addr) { tr_err(&buffer_tr, "could not alloc size = %zu bytes of flags = 0x%x", size, flags); return NULL; } - buffer = buffer_alloc_struct(stream_addr, size, flags, is_shared); + buffer = buffer_alloc_struct(alloc, stream_addr, size, flags, is_shared); if (!buffer) { tr_err(&buffer_tr, "could not alloc buffer structure"); +#ifdef CONFIG_SOF_USERSPACE_LL + assert(alloc); + if (alloc->vreg) + vregion_free(alloc->vreg, stream_addr); + else + sof_heap_free(alloc->heap, stream_addr); +#else rfree(stream_addr); +#endif } return buffer; } -struct comp_buffer *buffer_alloc_range(size_t preferred_size, size_t minimum_size, +struct comp_buffer *buffer_alloc_range(struct mod_alloc_ctx *alloc, size_t preferred_size, + size_t minimum_size, uint32_t flags, uint32_t align, bool is_shared) { struct comp_buffer *buffer; @@ -278,7 +317,15 @@ struct comp_buffer *buffer_alloc_range(size_t preferred_size, size_t minimum_siz preferred_size += minimum_size - preferred_size % minimum_size; for (size = preferred_size; size >= minimum_size; size -= minimum_size) { +#ifdef CONFIG_SOF_USERSPACE_LL + assert(alloc); + if (alloc->vreg) + stream_addr = vregion_alloc_align(alloc->vreg, VREGION_MEM_TYPE_INTERIM, size, align); + else + stream_addr = sof_heap_alloc(alloc->heap, flags, size, align); +#else stream_addr = rballoc_align(flags, size, align); +#endif if (stream_addr) break; } @@ -291,10 +338,18 @@ struct comp_buffer *buffer_alloc_range(size_t preferred_size, size_t minimum_siz return NULL; } - buffer = buffer_alloc_struct(stream_addr, size, flags, is_shared); + buffer = buffer_alloc_struct(alloc, stream_addr, size, flags, is_shared); if (!buffer) { tr_err(&buffer_tr, "could not alloc buffer structure"); +#ifdef CONFIG_SOF_USERSPACE_LL + assert(alloc); + if (alloc->vreg) + vregion_free(alloc->vreg, stream_addr); + else + sof_heap_free(alloc->heap, stream_addr); +#else rfree(stream_addr); +#endif } return buffer; @@ -302,7 +357,7 @@ struct comp_buffer *buffer_alloc_range(size_t preferred_size, size_t minimum_siz void buffer_zero(struct comp_buffer *buffer) { - buf_dbg(buffer, "stream_zero()"); + buf_dbg(buffer, "entry"); CORE_CHECK_STRUCT(&buffer->audio_buffer); bzero(audio_stream_get_addr(&buffer->stream), audio_stream_get_size(&buffer->stream)); @@ -315,6 +370,9 @@ void buffer_zero(struct comp_buffer *buffer) int buffer_set_size(struct comp_buffer *buffer, uint32_t size, uint32_t alignment) { void *new_ptr = NULL; +#ifdef CONFIG_SOF_USERSPACE_LL + struct mod_alloc_ctx *alloc = buffer->audio_buffer.alloc; +#endif CORE_CHECK_STRUCT(&buffer->audio_buffer); @@ -327,14 +385,16 @@ int buffer_set_size(struct comp_buffer *buffer, uint32_t size, uint32_t alignmen if (size == audio_stream_get_size(&buffer->stream)) return 0; - if (!alignment) - new_ptr = rbrealloc(audio_stream_get_addr(&buffer->stream), - buffer->flags | SOF_MEM_FLAG_NO_COPY, - size, audio_stream_get_size(&buffer->stream)); +#ifdef CONFIG_SOF_USERSPACE_LL + assert(alloc); + if (alloc->vreg) + new_ptr = vregion_alloc_align(alloc->vreg, VREGION_MEM_TYPE_INTERIM, size, alignment); else - new_ptr = rbrealloc_align(audio_stream_get_addr(&buffer->stream), - buffer->flags | SOF_MEM_FLAG_NO_COPY, size, - audio_stream_get_size(&buffer->stream), alignment); + new_ptr = sof_heap_alloc(alloc->heap, buffer->flags, size, alignment); +#else + new_ptr = rballoc_align(buffer->flags, size, alignment); +#endif + /* we couldn't allocate bigger chunk */ if (!new_ptr && size > audio_stream_get_size(&buffer->stream)) { buf_err(buffer, "resize can't alloc %u bytes of flags 0x%x", @@ -343,8 +403,18 @@ int buffer_set_size(struct comp_buffer *buffer, uint32_t size, uint32_t alignmen } /* use bigger chunk, else just use the old chunk but set smaller */ - if (new_ptr) - buffer->stream.addr = new_ptr; + if (new_ptr) { +#ifdef CONFIG_SOF_USERSPACE_LL + assert(alloc); + if (alloc->vreg) + vregion_free(alloc->vreg, audio_stream_get_addr(&buffer->stream)); + else + sof_heap_free(alloc->heap, audio_stream_get_addr(&buffer->stream)); +#else + rfree(audio_stream_get_addr(&buffer->stream)); +#endif + audio_stream_set_addr(&buffer->stream, new_ptr); + } buffer_init_stream(buffer, size); @@ -354,10 +424,12 @@ int buffer_set_size(struct comp_buffer *buffer, uint32_t size, uint32_t alignmen int buffer_set_size_range(struct comp_buffer *buffer, size_t preferred_size, size_t minimum_size, uint32_t alignment) { - void *ptr = audio_stream_get_addr(&buffer->stream); const size_t actual_size = audio_stream_get_size(&buffer->stream); void *new_ptr = NULL; size_t new_size; +#ifdef CONFIG_SOF_USERSPACE_LL + struct mod_alloc_ctx *alloc = buffer->audio_buffer.alloc; +#endif CORE_CHECK_STRUCT(&buffer->audio_buffer); @@ -375,22 +447,19 @@ int buffer_set_size_range(struct comp_buffer *buffer, size_t preferred_size, siz if (preferred_size == actual_size) return 0; - if (!alignment) { - for (new_size = preferred_size; new_size >= minimum_size; - new_size -= minimum_size) { - new_ptr = rbrealloc(ptr, buffer->flags | SOF_MEM_FLAG_NO_COPY, - new_size, actual_size); - if (new_ptr) - break; - } - } else { - for (new_size = preferred_size; new_size >= minimum_size; - new_size -= minimum_size) { - new_ptr = rbrealloc_align(ptr, buffer->flags | SOF_MEM_FLAG_NO_COPY, - new_size, actual_size, alignment); - if (new_ptr) - break; - } + for (new_size = preferred_size; new_size >= minimum_size; + new_size -= minimum_size) { +#ifdef CONFIG_SOF_USERSPACE_LL + assert(alloc); + if (alloc->vreg) + new_ptr = vregion_alloc_align(alloc->vreg, VREGION_MEM_TYPE_INTERIM, new_size, alignment); + else + new_ptr = sof_heap_alloc(alloc->heap, buffer->flags, new_size, alignment); +#else + new_ptr = rballoc_align(buffer->flags, new_size, alignment); +#endif + if (new_ptr) + break; } /* we couldn't allocate bigger chunk */ @@ -401,8 +470,18 @@ int buffer_set_size_range(struct comp_buffer *buffer, size_t preferred_size, siz } /* use bigger chunk, else just use the old chunk but set smaller */ - if (new_ptr) - buffer->stream.addr = new_ptr; + if (new_ptr) { +#ifdef CONFIG_SOF_USERSPACE_LL + assert(alloc); + if (alloc->vreg) + vregion_free(alloc->vreg, audio_stream_get_addr(&buffer->stream)); + else + sof_heap_free(alloc->heap, audio_stream_get_addr(&buffer->stream)); +#else + rfree(audio_stream_get_addr(&buffer->stream)); +#endif + audio_stream_set_addr(&buffer->stream, new_ptr); + } buffer_init_stream(buffer, new_size); @@ -418,7 +497,7 @@ int buffer_set_params(struct comp_buffer *buffer, CORE_CHECK_STRUCT(&buffer->audio_buffer); if (!params) { - buf_err(buffer, "buffer_set_params(): !params"); + buf_err(buffer, "!params"); return -EINVAL; } @@ -427,7 +506,7 @@ int buffer_set_params(struct comp_buffer *buffer, ret = audio_stream_set_params(&buffer->stream, params); if (ret < 0) { - buf_err(buffer, "buffer_set_params(): audio_stream_set_params failed"); + buf_err(buffer, "audio_stream_set_params failed"); return -EINVAL; } @@ -464,19 +543,13 @@ bool buffer_params_match(struct comp_buffer *buffer, void comp_update_buffer_produce(struct comp_buffer *buffer, uint32_t bytes) { - struct buffer_cb_transact cb_data = { - .buffer = buffer, - .transaction_amount = bytes, - .transaction_begin_address = audio_stream_get_wptr(&buffer->stream), - }; - /* return if no bytes */ if (!bytes) { #if CONFIG_SOF_LOG_DBG_BUFFER struct comp_dev *src_component = comp_buffer_get_source_component(buffer); struct comp_dev *sink_component = comp_buffer_get_sink_component(buffer); - buf_dbg(buffer, "comp_update_buffer_produce(), no bytes to produce, source->comp.id = %u, source->comp.type = %u, sink->comp.id = %u, sink->comp.type = %u", + buf_dbg(buffer, "no bytes to produce, source->comp.id = %u, source->comp.type = %u, sink->comp.id = %u, sink->comp.type = %u", src_component ? dev_comp_id(src_component) : (unsigned int)UINT32_MAX, src_component ? dev_comp_type(src_component) : (unsigned int)UINT32_MAX, sink_component ? dev_comp_id(sink_component) : (unsigned int)UINT32_MAX, @@ -485,17 +558,30 @@ void comp_update_buffer_produce(struct comp_buffer *buffer, uint32_t bytes) return; } +#if CONFIG_PROBE + void *produce_begin = audio_stream_get_wptr(&buffer->stream); +#endif + audio_stream_produce(&buffer->stream, bytes); - notifier_event(buffer, NOTIFIER_ID_BUFFER_PRODUCE, - NOTIFIER_TARGET_CORE_LOCAL, &cb_data, sizeof(cb_data)); +#if CONFIG_PROBE + if (buffer->probe_cb_produce) { + struct buffer_cb_transact cb_data = { + .buffer = buffer, + .transaction_amount = bytes, + .transaction_begin_address = produce_begin, + }; + + buffer->probe_cb_produce(buffer->probe_cb_arg, &cb_data); + } +#endif #if CONFIG_SOF_LOG_DBG_BUFFER - buf_dbg(buffer, "comp_update_buffer_produce(), ((buffer->avail << 16) | buffer->free) = %08x, ((buffer->id << 16) | buffer->size) = %08x", + buf_dbg(buffer, "((buffer->avail << 16) | buffer->free) = %08x, ((buffer->id << 16) | buffer->size) = %08x", (audio_stream_get_avail_bytes(&buffer->stream) << 16) | audio_stream_get_free_bytes(&buffer->stream), (buffer->id << 16) | audio_stream_get_size(&buffer->stream)); - buf_dbg(buffer, "comp_update_buffer_produce(), ((buffer->r_ptr - buffer->addr) << 16 | (buffer->w_ptr - buffer->addr)) = %08x", + buf_dbg(buffer, "((buffer->r_ptr - buffer->addr) << 16 | (buffer->w_ptr - buffer->addr)) = %08x", ((char *)audio_stream_get_rptr(&buffer->stream) - (char *)audio_stream_get_addr(&buffer->stream)) << 16 | ((char *)audio_stream_get_wptr(&buffer->stream) - @@ -505,12 +591,6 @@ void comp_update_buffer_produce(struct comp_buffer *buffer, uint32_t bytes) void comp_update_buffer_consume(struct comp_buffer *buffer, uint32_t bytes) { - struct buffer_cb_transact cb_data = { - .buffer = buffer, - .transaction_amount = bytes, - .transaction_begin_address = audio_stream_get_rptr(&buffer->stream), - }; - CORE_CHECK_STRUCT(&buffer->audio_buffer); /* return if no bytes */ @@ -519,7 +599,7 @@ void comp_update_buffer_consume(struct comp_buffer *buffer, uint32_t bytes) struct comp_dev *src_component = comp_buffer_get_source_component(buffer); struct comp_dev *sink_component = comp_buffer_get_sink_component(buffer); - buf_dbg(buffer, "comp_update_buffer_consume(), no bytes to consume, source->comp.id = %u, source->comp.type = %u, sink->comp.id = %u, sink->comp.type = %u", + buf_dbg(buffer, "no bytes to consume, source->comp.id = %u, source->comp.type = %u, sink->comp.id = %u, sink->comp.type = %u", src_component ? dev_comp_id(src_component) : (unsigned int)UINT32_MAX, src_component ? dev_comp_type(src_component) : (unsigned int)UINT32_MAX, sink_component ? dev_comp_id(sink_component) : (unsigned int)UINT32_MAX, @@ -530,11 +610,8 @@ void comp_update_buffer_consume(struct comp_buffer *buffer, uint32_t bytes) audio_stream_consume(&buffer->stream, bytes); - notifier_event(buffer, NOTIFIER_ID_BUFFER_CONSUME, - NOTIFIER_TARGET_CORE_LOCAL, &cb_data, sizeof(cb_data)); - #if CONFIG_SOF_LOG_DBG_BUFFER - buf_dbg(buffer, "comp_update_buffer_consume(), (buffer->avail << 16) | buffer->free = %08x, (buffer->id << 16) | buffer->size = %08x, (buffer->r_ptr - buffer->addr) << 16 | (buffer->w_ptr - buffer->addr)) = %08x", + buf_dbg(buffer, "(buffer->avail << 16) | buffer->free = %08x, (buffer->id << 16) | buffer->size = %08x, (buffer->r_ptr - buffer->addr) << 16 | (buffer->w_ptr - buffer->addr)) = %08x", (audio_stream_get_avail_bytes(&buffer->stream) << 16) | audio_stream_get_free_bytes(&buffer->stream), (buffer->id << 16) | audio_stream_get_size(&buffer->stream), @@ -553,7 +630,8 @@ static inline struct list_item *buffer_comp_list(struct comp_buffer *buffer, } /* - * Locking: must be called with interrupts disabled! Serialized IPCs protect us + * Locking: must be called with interrupts disabled (or sys_mutex held for + * userspace LL builds)! Serialized IPCs protect us * from racing attach / detach calls, but the scheduler can interrupt the IPC * thread and begin using the buffer for streaming. FIXME: this is still a * problem with different cores. @@ -566,7 +644,8 @@ void buffer_attach(struct comp_buffer *buffer, struct list_item *head, int dir) } /* - * Locking: must be called with interrupts disabled! See buffer_attach() above + * Locking: must be called with interrupts disabled (or sys_mutex held for + * userspace LL builds)! See buffer_attach() above * for details */ void buffer_detach(struct comp_buffer *buffer, struct list_item *head, int dir) diff --git a/src/audio/buffers/ring_buffer.c b/src/audio/buffers/ring_buffer.c index a71a27022e0a..fe67027df8db 100644 --- a/src/audio/buffers/ring_buffer.c +++ b/src/audio/buffers/ring_buffer.c @@ -6,7 +6,9 @@ #include <sof/common.h> #include <sof/trace/trace.h> #include <sof/lib/uuid.h> +#include <sof/lib/vregion.h> +#include <sof/audio/module_adapter/module/generic.h> #include <sof/audio/ring_buffer.h> #include <sof/audio/component.h> @@ -93,11 +95,17 @@ static void ring_buffer_free(struct sof_audio_buffer *audio_buffer) if (!audio_buffer) return; - struct ring_buffer *ring_buffer = - container_of(audio_buffer, struct ring_buffer, audio_buffer); + struct ring_buffer *ring_buffer = container_of(audio_buffer, + struct ring_buffer, audio_buffer); + struct mod_alloc_ctx *alloc = audio_buffer->alloc; - rfree((__sparse_force void *)ring_buffer->_data_buffer); - rfree(ring_buffer); + if (alloc->vreg) { + vregion_free(alloc->vreg, (__sparse_force void *)ring_buffer->_data_buffer); + vregion_free(alloc->vreg, ring_buffer); + } else { + sof_heap_free(alloc->heap, (__sparse_force void *)ring_buffer->_data_buffer); + sof_heap_free(alloc->heap, ring_buffer); + } } static void ring_buffer_reset(struct sof_audio_buffer *audio_buffer) @@ -273,11 +281,12 @@ static const struct sink_ops ring_buffer_sink_ops = { .audio_set_ipc_params = audio_buffer_sink_set_ipc_params, .on_audio_format_set = audio_buffer_sink_on_audio_format_set, .set_alignment_constants = audio_buffer_sink_set_alignment_constants, + .get_lft = audio_buffer_sink_get_lft, }; static const struct audio_buffer_ops audio_buffer_ops = { .free = ring_buffer_free, - .reset = ring_buffer_reset + .reset = ring_buffer_reset, }; struct ring_buffer *ring_buffer_create(struct comp_dev *dev, size_t min_available, @@ -285,14 +294,24 @@ struct ring_buffer *ring_buffer_create(struct comp_dev *dev, size_t min_availabl uint32_t id) { struct ring_buffer *ring_buffer; + struct mod_alloc_ctx *alloc = dev->mod->priv.resources.alloc; + struct k_heap *heap = alloc->heap; + struct vregion *vreg = alloc->vreg; int memory_flags = (is_shared ? SOF_MEM_FLAG_COHERENT : 0) | user_get_buffer_memory_region(dev->drv); /* allocate ring_buffer structure */ - ring_buffer = rzalloc(memory_flags, sizeof(*ring_buffer)); + if (!vreg) + ring_buffer = sof_heap_alloc(heap, memory_flags, sizeof(*ring_buffer), 0); + else if (is_shared) + ring_buffer = vregion_alloc_coherent(vreg, VREGION_MEM_TYPE_INTERIM, sizeof(*ring_buffer)); + else + ring_buffer = vregion_alloc(vreg, VREGION_MEM_TYPE_INTERIM, sizeof(*ring_buffer)); if (!ring_buffer) return NULL; + memset(ring_buffer, 0, sizeof(*ring_buffer)); + /* init base structure. The audio_stream_params is NULL because ring_buffer * is currently used as a secondary buffer for DP only * @@ -302,6 +321,8 @@ struct ring_buffer *ring_buffer_create(struct comp_dev *dev, size_t min_availabl audio_buffer_init(&ring_buffer->audio_buffer, BUFFER_TYPE_RING_BUFFER, is_shared, &ring_buffer_source_ops, &ring_buffer_sink_ops, &audio_buffer_ops, NULL); + ring_buffer->audio_buffer.alloc = alloc; + ring_buffer->audio_buffer.alloc->heap = heap; /* set obs/ibs in sink/source interfaces */ sink_set_min_free_space(audio_buffer_get_sink(&ring_buffer->audio_buffer), @@ -356,14 +377,23 @@ struct ring_buffer *ring_buffer_create(struct comp_dev *dev, size_t min_availabl ring_buffer->data_buffer_size = 3 * max_ibs_obs; /* allocate data buffer - always in cached memory alias */ - ring_buffer->data_buffer_size = - ALIGN_UP(ring_buffer->data_buffer_size, PLATFORM_DCACHE_ALIGN); - ring_buffer->_data_buffer = (__sparse_force __sparse_cache void *) - rballoc_align(memory_flags, ring_buffer->data_buffer_size, - PLATFORM_DCACHE_ALIGN); - if (!ring_buffer->_data_buffer) + ring_buffer->data_buffer_size = ALIGN_UP(ring_buffer->data_buffer_size, + PLATFORM_DCACHE_ALIGN); + + void *data_buf; + + if (vreg) + data_buf = vregion_alloc_align(vreg, VREGION_MEM_TYPE_INTERIM, ring_buffer->data_buffer_size, + PLATFORM_DCACHE_ALIGN); + else + data_buf = sof_heap_alloc(heap, user_get_buffer_memory_region(dev->drv), + ring_buffer->data_buffer_size, PLATFORM_DCACHE_ALIGN); + + if (!data_buf) goto err; + ring_buffer->_data_buffer = (__sparse_force __sparse_cache void *)data_buf; + tr_info(&ring_buffer_tr, "Ring buffer created, id: %u shared: %u min_available: %u min_free_space %u, size %u", id, ring_buffer_is_shared(ring_buffer), min_available, min_free_space, ring_buffer->data_buffer_size); @@ -372,6 +402,9 @@ struct ring_buffer *ring_buffer_create(struct comp_dev *dev, size_t min_availabl return ring_buffer; err: tr_err(&ring_buffer_tr, "Ring buffer creation failure"); - rfree(ring_buffer); + if (vreg) + vregion_free(vreg, ring_buffer); + else + sof_heap_free(heap, ring_buffer); return NULL; } diff --git a/src/audio/chain_dma.c b/src/audio/chain_dma.c index 975f2c6daaa1..4610845b5a42 100644 --- a/src/audio/chain_dma.c +++ b/src/audio/chain_dma.c @@ -26,7 +26,6 @@ #include <zephyr/pm/policy.h> #include <rtos/init.h> #if CONFIG_XRUN_NOTIFICATIONS_ENABLE -#include <sof/ipc/notification_pool.h> #include <ipc4/notification.h> #endif @@ -150,19 +149,8 @@ static int chain_get_dma_status(struct chain_dma_data *cd, struct dma_chan_data int ret = dma_get_status(chan->dma->z_dev, chan->index, stat); #if CONFIG_XRUN_NOTIFICATIONS_ENABLE if (ret == -EPIPE && !cd->xrun_notification_sent) { - struct ipc_msg *notify = ipc_notification_pool_get(IPC4_RESOURCE_EVENT_SIZE); - - if (notify) { - if (cd->stream_direction == SOF_IPC_STREAM_PLAYBACK) - gateway_underrun_notif_msg_init(notify, - cd->link_connector_node_id.dw); - else - gateway_overrun_notif_msg_init(notify, - cd->link_connector_node_id.dw); - - ipc_msg_send(notify, notify->tx_data, false); - cd->xrun_notification_sent = true; - } + cd->xrun_notification_sent = send_gateway_xrun_notif_msg + (cd->link_connector_node_id.dw, cd->stream_direction); } else if (!ret) { cd->xrun_notification_sent = false; } @@ -470,15 +458,16 @@ __cold static int chain_init(struct comp_dev *dev, void *addr, size_t length) channel = cd->host_connector_node_id.f.v_index; channel = dma_request_channel(cd->dma_host->z_dev, &channel); if (channel < 0) { - comp_err(dev, "dma_request_channel() failed"); - return -EINVAL; + comp_err(dev, "host dma_request_channel() failed for %u", + cd->host_connector_node_id.f.v_index); + return channel; } cd->chan_host = &cd->dma_host->chan[channel]; err = dma_config(cd->dma_host->z_dev, cd->chan_host->index, dma_cfg_host); if (err < 0) { - comp_err(dev, "dma_config() failed"); + comp_err(dev, "host dma_config() failed for %d", channel); goto error_host; } @@ -486,7 +475,9 @@ __cold static int chain_init(struct comp_dev *dev, void *addr, size_t length) channel = cd->link_connector_node_id.f.v_index; channel = dma_request_channel(cd->dma_link->z_dev, &channel); if (channel < 0) { - comp_err(dev, "dma_request_channel() failed"); + comp_err(dev, "link dma_request_channel() failed for %u", + cd->link_connector_node_id.f.v_index); + err = channel; goto error_host; } @@ -494,7 +485,7 @@ __cold static int chain_init(struct comp_dev *dev, void *addr, size_t length) err = dma_config(cd->dma_link->z_dev, cd->chan_link->index, dma_cfg_link); if (err < 0) { - comp_err(dev, "dma_config() failed"); + comp_err(dev, "link dma_config() failed for %d", channel); goto error_link; } return 0; @@ -591,7 +582,7 @@ __cold static int chain_task_init(struct comp_dev *dev, uint8_t host_dma_id, uin fifo_size = ALIGN_UP_INTERNAL(fifo_size, addr_align); /* allocate not shared buffer */ - cd->dma_buffer = buffer_alloc(fifo_size, SOF_MEM_FLAG_USER | SOF_MEM_FLAG_DMA, + cd->dma_buffer = buffer_alloc(NULL, fifo_size, SOF_MEM_FLAG_USER | SOF_MEM_FLAG_DMA, addr_align, BUFFER_USAGE_NOT_SHARED); if (!cd->dma_buffer) { @@ -671,7 +662,7 @@ __cold static struct comp_dev *chain_task_create(const struct comp_driver *drv, rfree(cd); error: - rfree(dev); + comp_free_device(dev); return NULL; } @@ -683,7 +674,7 @@ __cold static void chain_task_free(struct comp_dev *dev) chain_release(dev); rfree(cd); - rfree(dev); + comp_free_device(dev); } static const struct comp_driver comp_chain_dma = { diff --git a/src/audio/codec/CMakeLists.txt b/src/audio/codec/CMakeLists.txt index 5c4e2c485c21..5212a3631daf 100644 --- a/src/audio/codec/CMakeLists.txt +++ b/src/audio/codec/CMakeLists.txt @@ -7,7 +7,7 @@ if(zephyr) ### Zephyr ### add_subdirectory(dts/llext ${PROJECT_BINARY_DIR}/dts_llext) add_dependencies(app dts) - else(CONFIG_DTS_CODEC) + elseif(CONFIG_DTS_CODEC) zephyr_library_sources(dts/dts.c) if (CONFIG_DTS_CODEC_STUB) zephyr_library_sources(dts/dts_stub.c) diff --git a/src/audio/codec/README.md b/src/audio/codec/README.md new file mode 100644 index 000000000000..76a27e696d1a --- /dev/null +++ b/src/audio/codec/README.md @@ -0,0 +1,13 @@ +# Codec Abstraction Architecture + +This directory contains abstractions and adapters for various hardware and software audio codecs. + +## Overview + +Provides a unified interface to initialize, configure, and stream data to/from codec dependencies. + +## Configuration and Scripts + +- **Kconfig**: Specifically manages configurations for external codecs. For example, it defines options for the `DTS_CODEC`, including a testing/CI stub `DTS_CODEC_STUB` when `COMP_STUBS` is enabled. Use of the actual DTS codec requires a pre-compiled static library from Xperi. +- **CMakeLists.txt**: Specifies Zephyr build integration. For the DTS codec, it checks for modular builds (`llext`). If built statically, it either links the stub source or imports the pre-compiled `libdts-sof-interface-i32.a` library depending on configuration. +- **Topology (.conf)**: `tools/topology/topology2/include/components/dts.conf` defines the `dts` widget. It exposes `cpc` (cycles per chunk) and configures a byte control of size 2048 with `extctl` operations. Defaults to UUID `4f:c3:5f:d9:0f:37:c7:4a:bc:86:bf:dc:5b:e2:41:e6`. diff --git a/src/audio/codec/dts/dts.c b/src/audio/codec/dts/dts.c index 0bc590017004..cd1da3363517 100644 --- a/src/audio/codec/dts/dts.c +++ b/src/audio/codec/dts/dts.c @@ -8,11 +8,9 @@ #include <sof/audio/module_adapter/module/generic.h> #include "DtsSofInterface.h" - LOG_MODULE_REGISTER(dts, CONFIG_SOF_LOG_LEVEL); SOF_DEFINE_REG_UUID(dts); -DECLARE_TR_CTX(dts_tr, SOF_UUID(dts_uuid), LOG_LEVEL_INFO); #define MAX_EXPECTED_DTS_CONFIG_DATA_SIZE 8192 @@ -40,14 +38,14 @@ static void dts_effect_free_codec_memory(void *mod_void, void *pMem) struct processing_module *mod = mod_void; struct comp_dev *dev = mod->dev; - comp_dbg(dev, "dts_effect_free_codec_memory() start"); + comp_dbg(dev, "start"); int ret = mod_free(mod, pMem); if (ret) - comp_err(dev, "dts_effect_free_codec_memory() mod_free failed %d", ret); + comp_err(dev, "mod_free failed %d", ret); - comp_dbg(dev, "dts_effect_free_codec_memory() done"); + comp_dbg(dev, "done"); } static int dts_effect_convert_sof_interface_result(struct comp_dev *dev, @@ -82,7 +80,7 @@ static int dts_effect_populate_buffer_configuration(struct comp_dev *dev, DtsSofInterfaceBufferFormat buffer_format; unsigned int buffer_fmt, frame_fmt, rate, channels; - comp_dbg(dev, "dts_effect_populate_buffer_configuration() start"); + comp_dbg(dev, "start"); if (!source) return -EINVAL; @@ -127,7 +125,7 @@ static int dts_effect_populate_buffer_configuration(struct comp_dev *dev, buffer_config->numChannels = channels; buffer_config->periodInFrames = dev->frames; - comp_dbg(dev, "dts_effect_populate_buffer_configuration() done"); + comp_dbg(dev, "done"); return 0; } @@ -141,14 +139,14 @@ static int dts_codec_init(struct processing_module *mod) DtsSofInterfaceVersionInfo interface_version; DtsSofInterfaceVersionInfo sdk_version; - comp_dbg(dev, "dts_codec_init() start"); + comp_dbg(dev, "start"); dts_result = dtsSofInterfaceInit((DtsSofInterfaceInst **)&(codec->private), dts_effect_allocate_codec_memory, dts_effect_free_codec_memory, mod); ret = dts_effect_convert_sof_interface_result(dev, dts_result); if (ret) - comp_err(dev, "dts_codec_init() dtsSofInterfaceInit failed %d %d", ret, dts_result); + comp_err(dev, "dtsSofInterfaceInit failed %d %d", ret, dts_result); /* Obtain the current versions of DTS interface and SDK */ dts_result = dtsSofInterfaceGetVersion(&interface_version, &sdk_version); @@ -156,13 +154,13 @@ static int dts_codec_init(struct processing_module *mod) /* Not necessary to fail initialisation if only get version failed */ if (dts_result == DTS_SOF_INTERFACE_RESULT_SUCCESS) { comp_info(dev, - "dts_codec_init() DTS SOF Interface version %d.%d.%d.%d", + "DTS SOF Interface version %d.%d.%d.%d", interface_version.major, interface_version.minor, interface_version.patch, interface_version.build); comp_info(dev, - "dts_codec_init() DTS SDK version %d.%d.%d.%d", + "DTS SDK version %d.%d.%d.%d", sdk_version.major, sdk_version.minor, sdk_version.patch, @@ -170,9 +168,9 @@ static int dts_codec_init(struct processing_module *mod) } if (ret) - comp_err(dev, "dts_codec_init() failed %d %d", ret, dts_result); + comp_err(dev, "failed %d %d", ret, dts_result); - comp_dbg(dev, "dts_codec_init() done"); + comp_dbg(dev, "done"); return ret; } @@ -187,12 +185,12 @@ static int dts_codec_prepare(struct processing_module *mod, DtsSofInterfaceBufferConfiguration buffer_configuration; DtsSofInterfaceResult dts_result; - comp_dbg(dev, "dts_codec_prepare() start"); + comp_dbg(dev, "start"); ret = dts_effect_populate_buffer_configuration(dev, &buffer_configuration); if (ret) { comp_err(dev, - "dts_codec_prepare() dts_effect_populate_buffer_configuration failed %d", + "dts_effect_populate_buffer_configuration failed %d", ret); return ret; } @@ -207,9 +205,9 @@ static int dts_codec_prepare(struct processing_module *mod, ret = dts_effect_convert_sof_interface_result(dev, dts_result); if (ret) - comp_err(dev, "dts_codec_prepare() failed %d", ret); + comp_err(dev, "failed %d", ret); - comp_dbg(dev, "dts_codec_prepare() done"); + comp_dbg(dev, "done"); return ret; } @@ -221,7 +219,7 @@ static int dts_codec_init_process(struct processing_module *mod) struct module_data *codec = &mod->priv; DtsSofInterfaceResult dts_result; - comp_dbg(dev, "dts_codec_init_process() start"); + comp_dbg(dev, "start"); dts_result = dtsSofInterfaceInitProcess(codec->private); ret = dts_effect_convert_sof_interface_result(dev, dts_result); @@ -231,9 +229,9 @@ static int dts_codec_init_process(struct processing_module *mod) codec->mpd.init_done = 1; if (ret) - comp_err(dev, "dts_codec_init_process() failed %d %d", ret, dts_result); + comp_err(dev, "failed %d %d", ret, dts_result); - comp_dbg(dev, "dts_codec_init_process() done"); + comp_dbg(dev, "done"); return ret; } @@ -265,7 +263,7 @@ dts_codec_process(struct processing_module *mod, input_buffers[0].data, codec->mpd.in_buff_size); codec->mpd.avail = codec->mpd.in_buff_size; - comp_dbg(dev, "dts_codec_process() start"); + comp_dbg(dev, "start"); dts_result = dtsSofInterfaceProcess(codec->private, &bytes_processed); ret = dts_effect_convert_sof_interface_result(dev, dts_result); @@ -275,7 +273,7 @@ dts_codec_process(struct processing_module *mod, input_buffers[0].consumed = codec->mpd.consumed; if (ret) { - comp_err(dev, "dts_codec_process() failed %d %d", ret, dts_result); + comp_err(dev, "failed %d %d", ret, dts_result); return ret; } @@ -284,7 +282,7 @@ dts_codec_process(struct processing_module *mod, codec->mpd.produced); output_buffers[0].size = codec->mpd.produced; - comp_dbg(dev, "dts_codec_process() done"); + comp_dbg(dev, "done"); return ret; } @@ -304,17 +302,17 @@ static int dts_codec_apply_config(struct processing_module *mod) uint32_t param_number = 0; DtsSofInterfaceResult dts_result; - comp_dbg(dev, "dts_codec_apply_config() start"); + comp_dbg(dev, "start"); config = &codec->cfg; /* Check that config->data isn't invalid and has size greater than 0 */ config_header_size = sizeof(config->size) + sizeof(config->avail); if (config->size < config_header_size) { - comp_warn(dev, "dts_codec_apply_config() config->data is invalid"); + comp_warn(dev, "config->data is invalid"); return 0; } else if (config->size == config_header_size) { - comp_warn(dev, "dts_codec_apply_config() size of config->data is 0"); + comp_warn(dev, "size of config->data is 0"); return 0; } @@ -324,7 +322,7 @@ static int dts_codec_apply_config(struct processing_module *mod) /* Check that config->data is not greater than the max expected for DTS data */ if (config_data_size > MAX_EXPECTED_DTS_CONFIG_DATA_SIZE) { comp_err(dev, - "dts_codec_apply_config() size of config->data is larger than max for DTS data"); + "size of config->data is larger than max for DTS data"); return -EINVAL; } @@ -336,7 +334,7 @@ static int dts_codec_apply_config(struct processing_module *mod) /* If param->size is less than param_header_size, then this param is not valid */ if (param->size < param_header_size) { - comp_err(dev, "dts_codec_apply_config() param is invalid"); + comp_err(dev, "param is invalid"); return -EINVAL; } @@ -345,7 +343,7 @@ static int dts_codec_apply_config(struct processing_module *mod) /* Calculate size of param->data */ param_data_size = param->size - param_header_size; - comp_dbg(dev, "dts_codec_apply_config() id %d size %d", + comp_dbg(dev, "id %d size %d", param->id, param_data_size); if (param_data_size) { @@ -354,7 +352,7 @@ static int dts_codec_apply_config(struct processing_module *mod) ret = dts_effect_convert_sof_interface_result(dev, dts_result); if (ret) { comp_err(dev, - "dts_codec_apply_config() dtsSofInterfaceApplyConfig failed %d", + "dtsSofInterfaceApplyConfig failed %d", dts_result); return ret; } @@ -365,7 +363,7 @@ static int dts_codec_apply_config(struct processing_module *mod) i += param->size; } - comp_dbg(dev, "dts_codec_apply_config() done"); + comp_dbg(dev, "done"); return ret; } @@ -377,15 +375,15 @@ static int dts_codec_reset(struct processing_module *mod) struct module_data *codec = &mod->priv; DtsSofInterfaceResult dts_result; - comp_dbg(dev, "dts_codec_reset() start"); + comp_dbg(dev, "start"); dts_result = dtsSofInterfaceReset(codec->private); ret = dts_effect_convert_sof_interface_result(dev, dts_result); if (ret) - comp_err(dev, "dts_codec_reset() failed %d %d", ret, dts_result); + comp_err(dev, "failed %d %d", ret, dts_result); - comp_dbg(dev, "dts_codec_reset() done"); + comp_dbg(dev, "done"); return ret; } @@ -397,15 +395,15 @@ static int dts_codec_free(struct processing_module *mod) struct module_data *codec = &mod->priv; DtsSofInterfaceResult dts_result; - comp_dbg(dev, "dts_codec_free() start"); + comp_dbg(dev, "start"); dts_result = dtsSofInterfaceFree(codec->private); ret = dts_effect_convert_sof_interface_result(dev, dts_result); if (ret) - comp_err(dev, "dts_codec_free() failed %d %d", ret, dts_result); + comp_err(dev, "failed %d %d", ret, dts_result); - comp_dbg(dev, "dts_codec_free() done"); + comp_dbg(dev, "done"); return ret; } @@ -480,6 +478,7 @@ SOF_LLEXT_BUILDINFO; #else +DECLARE_TR_CTX(dts_tr, SOF_UUID(dts_uuid), LOG_LEVEL_INFO); DECLARE_MODULE_ADAPTER(dts_interface, dts_uuid, dts_tr); SOF_MODULE_INIT(dts, sys_comp_module_dts_interface_init); diff --git a/src/audio/codec/dts/dts.toml b/src/audio/codec/dts/dts.toml index 00d955a808f9..12b8a4104ad5 100644 --- a/src/audio/codec/dts/dts.toml +++ b/src/audio/codec/dts/dts.toml @@ -19,6 +19,6 @@ 1, 0, 0xfeef, 0xf, 0xf, 0x1ff] REM # mod_cfg [PAR_0 PAR_1 PAR_2 PAR_3 IS_BYTES CPS IBS OBS MOD_FLAGS CPC OBLS] - mod_cfg = [0, 0, 0, 0, 296, 5000000, 384, 384, 0, 5000, 0] + mod_cfg = [0, 0, 0, 0, 2048, 5000000, 384, 384, 0, 5000, 0] index = __COUNTER__ diff --git a/src/audio/codec/dts/llext/CMakeLists.txt b/src/audio/codec/dts/llext/CMakeLists.txt index f73dc4d3123b..da7043e10511 100644 --- a/src/audio/codec/dts/llext/CMakeLists.txt +++ b/src/audio/codec/dts/llext/CMakeLists.txt @@ -2,13 +2,21 @@ # SPDX-License-Identifier: Apache-2.0 if(CONFIG_DTS_CODEC_STUB) -sof_llext_build("dts" - SOURCES ../dts.c - ../dts_stub.c -) -target_include_directories(dts_llext_lib PRIVATE - "../../../../../third_party/include" -) + sof_llext_build("dts" + SOURCES ../dts.c + ../dts_stub.c + ) + target_include_directories(dts_llext_lib PRIVATE + "../../../../../third_party/include" + ) +elseif(CONFIG_DTS_CODEC STREQUAL "m") + sof_llext_build("dts" + SOURCES ../dts.c + INCLUDES "${sof_top_dir}/third_party/include" + LIBS_PATH "${sof_top_dir}/third_party/lib/" + LIBS dts-sof-interface-i32 m c gcc + ) + else() message(FATAL_ERROR "Add library linking support in src/audio/codec/dts/llext/CMakeLists.txt") endif() diff --git a/src/audio/component.c b/src/audio/component.c index 0964b0b62ebb..941be20f8534 100644 --- a/src/audio/component.c +++ b/src/audio/component.c @@ -53,7 +53,6 @@ int comp_register(struct comp_driver_info *drv) return 0; } -EXPORT_SYMBOL(comp_register); void comp_unregister(struct comp_driver_info *drv) { @@ -104,7 +103,7 @@ int comp_set_state(struct comp_dev *dev, int cmd) int requested_state = comp_get_requested_state(cmd); if (dev->state == requested_state) { - comp_info(dev, "comp_set_state(), state already set to %u", + comp_info(dev, "state already set to %u", dev->state); #ifdef CONFIG_IPC_MAJOR_4 return 0; @@ -517,7 +516,7 @@ static bool comp_check_eos(struct comp_dev *dev) * the EOS state. However, silence is generated to flush its internal * buffers, so pass this state to the output buffers. */ - comp_dbg(dev, "comp_check_eos() - EOS flush detected"); + comp_dbg(dev, "- EOS flush detected"); sink_state = AUDIOBUF_STATE_END_OF_STREAM_FLUSH; break; } else if (state == AUDIOBUF_STATE_END_OF_STREAM) { @@ -525,7 +524,7 @@ static bool comp_check_eos(struct comp_dev *dev) size_t min_avail = source_get_min_available(source); if (source_get_data_available(source) < min_avail) { - comp_dbg(dev, "comp_check_eos() - EOS detected"); + comp_dbg(dev, "- EOS detected"); if (dev->ipc_config.proc_domain == COMP_PROCESSING_DOMAIN_DP) { /* For DP modules, fill missing input data with silence to * allow it to process the remaining data. diff --git a/src/audio/copier/README.md b/src/audio/copier/README.md new file mode 100644 index 000000000000..f120f9cd7a86 --- /dev/null +++ b/src/audio/copier/README.md @@ -0,0 +1,22 @@ +# Copier Architecture + +This directory contains the Copier component. + +## Overview + +The Copier is a versatile component responsible for moving data smoothly between hardware endpoints (DAIs, Host APIs) and internal buffers. It may also apply simple format conversions (e.g., 16-bit to 32-bit). + +## Architecture Diagram + +```mermaid +graph LR + DMA[DMA Engine] <--> Copier[Copier Component] + Copier <--> Buf[Internal Buffer] +``` + +## Configuration and Scripts + +- **Kconfig**: Enables the `COMP_COPIER` component (depends on IPC 4). Also defines `COMP_DAI`, options to reverse DMA/DAI trigger stop ordering (`COMP_DAI_STOP_TRIGGER_ORDER_REVERSE`), DAI grouping, and an optional copier gain feature (`COPIER_GAIN`) for static gain, mute, or transition gain (fade-in/fade-out). +- **CMakeLists.txt**: Includes base copier implementations (HIFI, generic, host, DAI). If `CONFIG_IPC4_GATEWAY` is enabled, it adds `copier_ipcgtw.c`, and if `CONFIG_COPIER_GAIN` is selected, includes `copier_gain.c`. +- **copier.toml**: Contains topology configurations, describing UUID, pins, and complex `mod_cfg` tuples tailored per platform (`CONFIG_METEORLAKE`, `CONFIG_LUNARLAKE`, or `CONFIG_SOC_ACE30`/`40`). +- **Topology (.conf)**: `tools/topology/topology2/include/components/dai-copier.conf` (among others like `host-copier`, `module-copier`) define copier widget objects. They configure connection-specific attributes like `copier_type` (e.g., `HDA`, `SSP`, `DMIC`, `SAI`), `direction` (`playback` or `capture`), `node_type`, and `cpc`. `dai-copier` defaults to UUID `83:0c:a0:9b:12:CA:83:4a:94:3c:1f:a2:e8:2f:9d:da`. diff --git a/src/audio/copier/copier.c b/src/audio/copier/copier.c index 9aab0858a305..d2aefcb13be6 100644 --- a/src/audio/copier/copier.c +++ b/src/audio/copier/copier.c @@ -15,7 +15,6 @@ #include <sof/ipc/topology.h> #include <rtos/interrupt.h> #include <rtos/timer.h> -#include <rtos/alloc.h> #include <rtos/cache.h> #include <rtos/init.h> #include <sof/lib/memory.h> @@ -53,8 +52,6 @@ LOG_MODULE_REGISTER(copier, CONFIG_SOF_LOG_LEVEL); /* this id aligns windows driver requirement to support windows driver */ SOF_DEFINE_REG_UUID(copier); -DECLARE_TR_CTX(copier_comp_tr, SOF_UUID(copier_uuid), LOG_LEVEL_INFO); - #if CONFIG_INTEL_ADSP_MIC_PRIVACY static void mic_privacy_event(void *arg, enum notify_id type, void *data) { @@ -62,19 +59,19 @@ static void mic_privacy_event(void *arg, enum notify_id type, void *data) struct mic_privacy_settings *mic_privacy_settings = data; if (type == NOTIFIER_ID_MIC_PRIVACY_STATE_CHANGE) { - LOG_INF("mic_privacy_event, state1 = %d, state2 = %d ", + LOG_INF("state1 = %d, state2 = %d ", mic_privacy_settings->mic_privacy_state, mic_priv_data->mic_privacy_state); if (mic_privacy_settings->mic_privacy_state == MIC_PRIV_UNMUTED) { if (mic_priv_data->mic_privacy_state == MIC_PRIV_MUTED) { mic_priv_data->mic_privacy_state = MIC_PRIV_FADE_IN; - LOG_INF("mic_privacy_event switch to FADE_IN"); + LOG_INF("switch to FADE_IN"); } } else { /* In case when mute would be triggered before copier instantiation. */ if (mic_priv_data->mic_privacy_state != MIC_PRIV_MUTED) { mic_priv_data->mic_privacy_state = MIC_PRIV_FADE_OUT; - LOG_INF("mic_privacy_event switch to FADE_OUT"); + LOG_INF("switch to FADE_OUT"); } } mic_priv_data->max_ramp_time_in_ms = (mic_privacy_settings->max_ramp_time * 1000) / @@ -82,13 +79,12 @@ static void mic_privacy_event(void *arg, enum notify_id type, void *data) } } -static int mic_privacy_configure(struct comp_dev *dev, struct copier_data *cd) +static int mic_privacy_configure(struct processing_module *mod, struct copier_data *cd) { struct mic_privacy_data *mic_priv_data; int ret; - mic_priv_data = rzalloc(SOF_MEM_FLAG_USER, - sizeof(struct mic_privacy_data)); + mic_priv_data = mod_zalloc(mod, sizeof(struct mic_privacy_data)); if (!mic_priv_data) return -ENOMEM; @@ -100,10 +96,10 @@ static int mic_privacy_configure(struct comp_dev *dev, struct copier_data *cd) uint32_t zeroing_wait_time = (mic_privacy_get_dma_zeroing_wait_time() * 1000) / ADSP_RTC_FREQUENCY; - ret = copier_gain_set_params(dev, &mic_priv_data->mic_priv_gain_params, + ret = copier_gain_set_params(mod->dev, &mic_priv_data->mic_priv_gain_params, zeroing_wait_time, SOF_DAI_INTEL_NONE); if (ret != 0) { - rfree(mic_priv_data); + mod_free(mod, mic_priv_data); return ret; } @@ -111,92 +107,59 @@ static int mic_privacy_configure(struct comp_dev *dev, struct copier_data *cd) ret = notifier_register(cd->mic_priv, NULL, NOTIFIER_ID_MIC_PRIVACY_STATE_CHANGE, mic_privacy_event, 0); + if (ret != 0) - rfree(mic_priv_data); + mod_free(mod, mic_priv_data); return ret; } -static void mic_privacy_free(struct copier_data *cd) +static void mic_privacy_free(struct processing_module *mod) { + struct copier_data *cd = module_get_private_data(mod); + if (cd->gtw_type == ipc4_gtw_dmic) mic_privacy_enable_dmic_irq(false); notifier_unregister(cd->mic_priv, NULL, NOTIFIER_ID_MIC_PRIVACY_STATE_CHANGE); - rfree(cd->mic_priv); + mod_free(mod, cd->mic_priv); } #endif __cold static int copier_init(struct processing_module *mod) { union ipc4_connector_node_id node_id; - struct ipc_comp_dev *ipc_pipe; - struct ipc *ipc = ipc_get(); struct copier_data *cd; struct comp_dev *dev = mod->dev; struct module_data *md = &mod->priv; struct ipc4_copier_module_cfg *copier = (struct ipc4_copier_module_cfg *)md->cfg.init_data; - struct comp_ipc_config *config = &dev->ipc_config; - void *gtw_cfg = NULL; - size_t gtw_cfg_size; + size_t cfg_total_size = sizeof(*copier); + size_t gtw_cfg_var_size = 0; int i, ret = 0; assert_can_be_cold(); - cd = rzalloc(SOF_MEM_FLAG_USER, sizeof(*cd)); + if (copier->gtw_cfg.config_length > 1) { + /* one word already included in gateway_cfg struct hence subtraction */ + gtw_cfg_var_size += (copier->gtw_cfg.config_length - 1) << 2; + cfg_total_size += gtw_cfg_var_size; + } + + cd = mod_zalloc(mod, sizeof(*cd) + gtw_cfg_var_size); if (!cd) return -ENOMEM; md->private = cd; - /* - * Don't copy the config_data[] variable size array, we don't need to - * store it, it's only used during IPC processing, besides we haven't - * allocated space for it, so don't "fix" this! - */ - if (memcpy_s(&cd->config, sizeof(cd->config), copier, sizeof(*copier)) < 0) { - ret = -EINVAL; - goto error_cd; - } - - /* Allocate memory and store gateway_cfg in runtime. Gateway cfg has to - * be kept even after copier is created e.g. during SET_PIPELINE_STATE - * IPC when dai_config_dma_channel() is called second time and DMA - * config is used to assign dma_channel_id value. - */ - if (copier->gtw_cfg.config_length) { - gtw_cfg_size = copier->gtw_cfg.config_length << 2; - gtw_cfg = rmalloc(SOF_MEM_FLAG_USER, - gtw_cfg_size); - if (!gtw_cfg) { - ret = -ENOMEM; - goto error_cd; - } - ret = memcpy_s(gtw_cfg, gtw_cfg_size, &copier->gtw_cfg.config_data, - gtw_cfg_size); - if (ret) { - comp_err(dev, "Unable to copy gateway config from copier blob"); - goto error; - } - - cd->gtw_cfg = gtw_cfg; + if (memcpy_s(&cd->config, cfg_total_size, copier, cfg_total_size) < 0) { + ret = -EINVAL; + goto error; } for (i = 0; i < IPC4_COPIER_MODULE_OUTPUT_PINS_COUNT; i++) cd->out_fmt[i] = cd->config.out_fmt; - ipc_pipe = ipc_get_comp_by_ppl_id(ipc, COMP_TYPE_PIPELINE, - config->pipeline_id, - IPC_COMP_IGNORE_REMOTE); - if (!ipc_pipe) { - comp_err(dev, "pipeline %d is not existed", config->pipeline_id); - ret = -EPIPE; - goto error; - } - - dev->pipeline = ipc_pipe->pipeline; - node_id = copier->gtw_cfg.node_id; /* copier is linked to gateway */ if (node_id.dw != IPC4_INVALID_NODE_ID) { @@ -205,7 +168,7 @@ __cold static int copier_init(struct processing_module *mod) switch (node_id.f.dma_type) { case ipc4_hda_host_output_class: case ipc4_hda_host_input_class: - ret = copier_host_create(dev, cd, copier, ipc_pipe->pipeline); + ret = copier_host_create(mod, copier, dev->pipeline); if (ret < 0) { comp_err(dev, "unable to create host"); goto error; @@ -213,7 +176,7 @@ __cold static int copier_init(struct processing_module *mod) #if CONFIG_INTEL_ADSP_MIC_PRIVACY if (cd->direction == SOF_IPC_STREAM_CAPTURE && node_id.f.dma_type == ipc4_hda_host_output_class) { - ret = mic_privacy_configure(dev, cd); + ret = mic_privacy_configure(mod, cd); if (ret < 0) { comp_err(dev, "unable to configure mic privacy"); goto error; @@ -228,14 +191,16 @@ __cold static int copier_init(struct processing_module *mod) case ipc4_i2s_link_input_class: case ipc4_alh_link_output_class: case ipc4_alh_link_input_class: - ret = copier_dai_create(dev, cd, copier, ipc_pipe->pipeline); + case ipc4_alh_uaol_stream_link_output_class: + case ipc4_alh_uaol_stream_link_input_class: + ret = copier_dai_create(dev, cd, copier, dev->pipeline); if (ret < 0) { comp_err(dev, "unable to create dai"); goto error; } #if CONFIG_INTEL_ADSP_MIC_PRIVACY if (cd->direction == SOF_IPC_STREAM_CAPTURE) { - ret = mic_privacy_configure(dev, cd); + ret = mic_privacy_configure(mod, cd); if (ret < 0) { comp_err(dev, "unable to configure mic privacy"); goto error; @@ -246,7 +211,7 @@ __cold static int copier_init(struct processing_module *mod) #if CONFIG_IPC4_GATEWAY case ipc4_ipc_output_class: case ipc4_ipc_input_class: - ret = copier_ipcgtw_create(dev, cd, copier, ipc_pipe->pipeline); + ret = copier_ipcgtw_create(mod, copier, dev->pipeline); if (ret < 0) { comp_err(dev, "unable to create IPC gateway"); goto error; @@ -271,9 +236,7 @@ __cold static int copier_init(struct processing_module *mod) dev->state = COMP_STATE_READY; return 0; error: - rfree(gtw_cfg); -error_cd: - rfree(cd); + mod_free(mod, cd); return ret; } @@ -285,27 +248,25 @@ __cold static int copier_free(struct processing_module *mod) assert_can_be_cold(); #if CONFIG_INTEL_ADSP_MIC_PRIVACY - mic_privacy_free(cd); + mic_privacy_free(mod); #endif switch (dev->ipc_config.type) { case SOF_COMP_HOST: if (!cd->ipc_gtw) - copier_host_free(cd); + copier_host_free(mod); else /* handle gtw case */ - copier_ipcgtw_free(cd); + copier_ipcgtw_free(mod); break; case SOF_COMP_DAI: - copier_dai_free(cd); + copier_dai_free(mod); break; default: break; } - if (cd) - rfree(cd->gtw_cfg); - rfree(cd); + mod_free(mod, cd); return 0; } @@ -324,7 +285,7 @@ static int copier_prepare(struct processing_module *mod, if (ret < 0) return ret; - comp_info(dev, "copier_prepare()"); + comp_info(dev, "entry"); switch (dev->ipc_config.type) { case SOF_COMP_HOST: @@ -366,7 +327,7 @@ static int copier_reset(struct processing_module *mod) struct ipc4_pipeline_registers pipe_reg; struct comp_dev *dev = mod->dev; - comp_dbg(dev, "copier_reset()"); + comp_dbg(dev, "entry"); cd->input_total_data_processed = 0; cd->output_total_data_processed = 0; @@ -404,7 +365,7 @@ static int copier_comp_trigger(struct comp_dev *dev, int cmd) uint32_t latency; int ret; - comp_dbg(dev, "copier_comp_trigger()"); + comp_dbg(dev, "entry"); ret = comp_set_state(dev, cmd); if (ret < 0) @@ -540,7 +501,12 @@ static int do_conversion_copy(struct comp_dev *dev, comp_get_copy_limits(src, sink, processed_data); - i = IPC4_SINK_QUEUE_ID(buf_get_id(sink)); + /* + * Buffer ID is constructed as IPC4_COMP_ID(src_queue, dst_queue). + * From the buffer's perspective, copier's sink is the source, + * so we use IPC4_SRC_QUEUE_ID() to get the correct copier sink index. + */ + i = IPC4_SRC_QUEUE_ID(buf_get_id(sink)); if (i >= IPC4_COPIER_MODULE_OUTPUT_PINS_COUNT) return -EINVAL; buffer_stream_invalidate(src, processed_data->source_bytes); @@ -617,7 +583,12 @@ static int copier_module_copy(struct processing_module *mod, uint32_t source_samples; int sink_queue_id; - sink_queue_id = IPC4_SINK_QUEUE_ID(buf_get_id(sink_c)); + /* + * Buffer ID is constructed as IPC4_COMP_ID(src_queue, dst_queue). + * From the buffer's perspective, copier's sink is the source, + * so we use IPC4_SRC_QUEUE_ID() to get the correct copier sink index. + */ + sink_queue_id = IPC4_SRC_QUEUE_ID(buf_get_id(sink_c)); if (sink_queue_id >= IPC4_COPIER_MODULE_OUTPUT_PINS_COUNT) return -EINVAL; @@ -700,7 +671,7 @@ static int copier_process(struct processing_module *mod, struct copier_data *cd = module_get_private_data(mod); struct comp_dev *dev = mod->dev; - comp_dbg(dev, "copier_process()"); + comp_dbg(dev, "entry"); switch (dev->ipc_config.type) { case SOF_COMP_HOST: @@ -730,7 +701,7 @@ static int copier_params(struct processing_module *mod) struct comp_dev *dev = mod->dev; int i, ret = 0; - comp_dbg(dev, "copier_params()"); + comp_dbg(dev, "entry"); copier_update_params(cd, dev, params); @@ -1242,5 +1213,7 @@ static const struct module_interface copier_interface = { .endpoint_ops = &copier_endpoint_ops, }; +DECLARE_TR_CTX(copier_comp_tr, SOF_UUID(copier_uuid), LOG_LEVEL_INFO); + DECLARE_MODULE_ADAPTER(copier_interface, copier_uuid, copier_comp_tr); SOF_MODULE_INIT(copier, sys_comp_module_copier_interface_init); diff --git a/src/audio/copier/copier.h b/src/audio/copier/copier.h index 4a6091d99dca..4e29e18d33a6 100644 --- a/src/audio/copier/copier.h +++ b/src/audio/copier/copier.h @@ -241,13 +241,6 @@ struct ipc4_data_segment_enabled { } __attribute__((packed, aligned(4))); struct copier_data { - /* - * struct ipc4_copier_module_cfg actually has variable size, but we - * don't need the variable size array at the end, we won't be copying it - * from the IPC data. - */ - struct ipc4_copier_module_cfg config; - void *gtw_cfg; enum ipc4_gateway_type gtw_type; uint32_t endpoint_num; @@ -277,6 +270,8 @@ struct copier_data { #if CONFIG_INTEL_ADSP_MIC_PRIVACY struct mic_privacy_data *mic_priv; #endif + /* Has to be at the end due to variable size array */ + struct ipc4_copier_module_cfg config; }; int apply_attenuation(struct comp_dev *dev, struct copier_data *cd, diff --git a/src/audio/copier/copier.toml b/src/audio/copier/copier.toml index 3f3b0ac17899..acb2a5f8876f 100644 --- a/src/audio/copier/copier.toml +++ b/src/audio/copier/copier.toml @@ -92,7 +92,7 @@ 28, 0, 0, 0, 280, 6058000, 64, 64, 0, 6058, 0, 29, 0, 0, 0, 280, 6198000, 64, 64, 0, 6198, 0, 30, 0, 0, 0, 280, 6034000, 32, 32, 0, 6034, 0] -#elif CONFIG_SOC_INTEL_ACE30 || CONFIG_SOC_INTEL_ACE40 +#elif CONFIG_SOC_ACE30 || CONFIG_SOC_ACE40 mod_cfg = [0, 0, 0, 0, 280, 7915000, 768, 768, 0, 7915, 0, 1, 0, 0, 0, 280, 9487000, 768, 768, 0, 9487, 0, 2, 0, 0, 0, 280, 7363000, 384, 384, 0, 7363, 0, diff --git a/src/audio/copier/copier_dai.c b/src/audio/copier/copier_dai.c index 828ee259b79b..dfd2590c7108 100644 --- a/src/audio/copier/copier_dai.c +++ b/src/audio/copier/copier_dai.c @@ -204,9 +204,10 @@ __cold static int copier_dai_init(struct comp_dev *dev, return ret; } - dd = rzalloc(SOF_MEM_FLAG_USER | SOF_MEM_FLAG_COHERENT, sizeof(*dd)); + dd = mod_alloc_ext(mod, SOF_MEM_FLAG_USER | SOF_MEM_FLAG_COHERENT, sizeof(*dd), 0); if (!dd) return -ENOMEM; + memset(dd, 0, sizeof(*dd)); ret = dai_common_new(dd, dev, dai); if (ret < 0) @@ -223,12 +224,14 @@ __cold static int copier_dai_init(struct comp_dev *dev, /* Allocate gain data if selected for this dai type and set basic params */ if (dai->apply_gain) { - struct copier_gain_params *gain_data = rzalloc(SOF_MEM_FLAG_USER | SOF_MEM_FLAG_COHERENT, - sizeof(*gain_data)); + struct copier_gain_params *gain_data = + mod_alloc_ext(mod, SOF_MEM_FLAG_USER | SOF_MEM_FLAG_COHERENT, + sizeof(*gain_data), 0); if (!gain_data) { ret = -ENOMEM; goto e_zephyr_free; } + memset(gain_data, 0, sizeof(*gain_data)); cd->dd[index]->gain_data = gain_data; ret = copier_gain_set_params(dev, cd->dd[index]->gain_data, @@ -244,11 +247,11 @@ __cold static int copier_dai_init(struct comp_dev *dev, return 0; gain_free: - rfree(dd->gain_data); + mod_free(mod, dd->gain_data); e_zephyr_free: dai_common_free(dd); free_dd: - rfree(dd); + mod_free(mod, dd); return ret; } @@ -267,6 +270,8 @@ __cold int copier_dai_create(struct comp_dev *dev, struct copier_data *cd, struct ipc_config_dai dai; int dai_count; int i, ret; + uint8_t *gtw_cfg_data = (uint8_t *)cd->config.gtw_cfg.config_data; + size_t gtw_cfg_size = cd->config.gtw_cfg.config_length * 4; assert_can_be_cold(); @@ -280,6 +285,8 @@ __cold int copier_dai_create(struct comp_dev *dev, struct copier_data *cd, dai.is_config_blob = true; dai.sampling_frequency = copier->out_fmt.sampling_frequency; dai.feature_mask = copier->copier_feature_mask; + dai.gtw_fmt = (dai.direction == SOF_IPC_STREAM_PLAYBACK) ? + &cd->config.out_fmt : &cd->config.base.audio_fmt; switch (node_id.f.dma_type) { case ipc4_hda_link_output_class: @@ -293,13 +300,11 @@ __cold int copier_dai_create(struct comp_dev *dev, struct copier_data *cd, dai.type = SOF_DAI_INTEL_SSP; dai.is_config_blob = true; cd->gtw_type = ipc4_gtw_ssp; - ret = ipc4_find_dma_config(&dai, (uint8_t *)cd->gtw_cfg, - copier->gtw_cfg.config_length * 4); + ret = ipc4_find_dma_config(&dai, gtw_cfg_data, gtw_cfg_size); if (ret != 0) { comp_err(dev, "No ssp dma_config found in blob!"); return -EINVAL; } - dai.out_fmt = &copier->out_fmt; break; case ipc4_alh_link_output_class: case ipc4_alh_link_input_class: @@ -312,22 +317,31 @@ __cold int copier_dai_create(struct comp_dev *dev, struct copier_data *cd, dai.is_config_blob = true; cd->gtw_type = ipc4_gtw_alh; #endif /* ACE_VERSION > ACE_VERSION_1_5 */ - ret = copier_alh_assign_dai_index(dev, cd->gtw_cfg, node_id, - &dai, dai_index, &dai_count); + ret = copier_alh_assign_dai_index(dev, gtw_cfg_data, node_id, &dai, dai_index, + &dai_count); if (ret) return ret; break; + case ipc4_alh_uaol_stream_link_output_class: + case ipc4_alh_uaol_stream_link_input_class: + dai.type = SOF_DAI_INTEL_UAOL; + dai.is_config_blob = true; + cd->gtw_type = ipc4_gtw_alh; + ret = ipc4_find_dma_config(&dai, gtw_cfg_data, gtw_cfg_size); + if (ret != IPC4_SUCCESS) { + comp_err(dev, "No uaol dma_config found in blob!"); + return -EINVAL; + } + break; case ipc4_dmic_link_input_class: dai.type = SOF_DAI_INTEL_DMIC; dai.is_config_blob = true; cd->gtw_type = ipc4_gtw_dmic; - ret = ipc4_find_dma_config(&dai, (uint8_t *)cd->gtw_cfg, - copier->gtw_cfg.config_length * 4); + ret = ipc4_find_dma_config(&dai, gtw_cfg_data, gtw_cfg_size); if (ret != 0) { comp_err(dev, "No dmic dma_config found in blob!"); return -EINVAL; } - dai.out_fmt = &copier->out_fmt; #if CONFIG_COPIER_GAIN dai.apply_gain = true; #endif @@ -374,14 +388,16 @@ __cold int copier_dai_create(struct comp_dev *dev, struct copier_data *cd, return 0; } -__cold void copier_dai_free(struct copier_data *cd) +__cold void copier_dai_free(struct processing_module *mod) { + struct copier_data *cd = module_get_private_data(mod); + assert_can_be_cold(); for (int i = 0; i < cd->endpoint_num; i++) { dai_common_free(cd->dd[i]); - rfree(cd->dd[i]->gain_data); - rfree(cd->dd[i]); + mod_free(mod, cd->dd[i]->gain_data); + mod_free(mod, cd->dd[i]); } /* only dai have multi endpoint case */ if (cd->multi_endpoint_buffer) diff --git a/src/audio/copier/copier_gain.c b/src/audio/copier/copier_gain.c index 4c077665e7d0..fc9f6664add8 100644 --- a/src/audio/copier/copier_gain.c +++ b/src/audio/copier/copier_gain.c @@ -34,12 +34,8 @@ __cold int copier_gain_set_params(struct comp_dev *dev, struct copier_gain_param switch (dai_type) { case SOF_DAI_INTEL_DMIC: { - struct dmic_config_data *dmic_cfg = cd->gtw_cfg; - - if (!dmic_cfg) { - comp_err(dev, "No dmic config found"); - return -EINVAL; - } + struct dmic_config_data *dmic_cfg = + (void *)cd->config.gtw_cfg.config_data; union dmic_global_cfg *dmic_glb_cfg = &dmic_cfg->dmic_blob.global_cfg; diff --git a/src/audio/copier/copier_generic.c b/src/audio/copier/copier_generic.c index da1ca4dbf942..89805f25af0c 100644 --- a/src/audio/copier/copier_generic.c +++ b/src/audio/copier/copier_generic.c @@ -169,7 +169,7 @@ int copier_gain_input16(struct comp_buffer *buff, enum copier_gain_state state, /* Apply fade */ for (j = 0; j < nch; j++) { - dst += j; + dst_tmp = dst + j; /* Quadratic fade part in Q15 format*/ gain_env_sq = q_multsr_16x16(gain_env[j], gain_env[j], 15); @@ -180,8 +180,8 @@ int copier_gain_input16(struct comp_buffer *buff, enum copier_gain_state state, gain_env_sq, 15); for (i = 0; i < nmax; i += nch) - dst[i] = q_multsr_sat_16x16(dst[i], gain, - GAIN_Q10_INT_SHIFT); + dst_tmp[i] = q_multsr_sat_16x16(dst_tmp[i], gain, + GAIN_Q10_INT_SHIFT); } samples -= nmax; dst = audio_stream_wrap(&buff->stream, dst + nmax); @@ -260,7 +260,7 @@ int copier_gain_input32(struct comp_buffer *buff, enum copier_gain_state state, /* Apply fade */ for (j = 0; j < nch; j++) { - dst += j; + dst_tmp = dst + j; /* Quadratic fade part in Q15 format*/ gain_env_sq = q_multsr_16x16(gain_env[j], gain_env[j], 15); @@ -271,8 +271,8 @@ int copier_gain_input32(struct comp_buffer *buff, enum copier_gain_state state, gain_env_sq, 15); for (i = 0; i < nmax; i += nch) - dst[i] = q_multsr_sat_32x32(dst[i], gain, - GAIN_Q10_INT_SHIFT); + dst_tmp[i] = q_multsr_sat_32x32(dst_tmp[i], gain, + GAIN_Q10_INT_SHIFT); } samples -= nmax; dst = audio_stream_wrap(&buff->stream, dst + nmax); @@ -328,7 +328,7 @@ void copier_update_params(struct copier_data *cd, struct comp_dev *dev, /* update each sink format */ comp_dev_for_each_consumer(dev, sink) { int j; - j = IPC4_SINK_QUEUE_ID(buf_get_id(sink)); + j = IPC4_SRC_QUEUE_ID(buf_get_id(sink)); ipc4_update_buffer_format(sink, &cd->out_fmt[j]); } @@ -427,7 +427,7 @@ __cold int create_multi_endpoint_buffer(struct comp_dev *dev, ipc_buf.size = buf_size; ipc_buf.comp.pipeline_id = config->pipeline_id; ipc_buf.comp.core = config->core; - buffer = buffer_new(&ipc_buf, BUFFER_USAGE_NOT_SHARED); + buffer = buffer_new(NULL, &ipc_buf, BUFFER_USAGE_NOT_SHARED); if (!buffer) return -ENOMEM; @@ -566,6 +566,11 @@ pcm_converter_func get_converter_func(const struct ipc4_audio_format *in_fmt, if (out_valid == SOF_IPC_FRAME_S16_LE && out == SOF_IPC_FRAME_S32_LE) out = SOF_IPC_FRAME_S16_4LE; + if (in_valid == SOF_IPC_FRAME_S24_4LE && in == SOF_IPC_FRAME_S32_LE) + in = in_valid; + if (out_valid == SOF_IPC_FRAME_S24_4LE && out == SOF_IPC_FRAME_S32_LE) + out = out_valid; + return pcm_get_remap_function(in, out); } diff --git a/src/audio/copier/copier_host.c b/src/audio/copier/copier_host.c index 8315ad4535e2..fe17a49328b9 100644 --- a/src/audio/copier/copier_host.c +++ b/src/audio/copier/copier_host.c @@ -50,6 +50,7 @@ __cold static int add_to_fpi_sync_group(struct comp_dev *parent_dev, struct ipc4_copier_sync_group *sync_group) { struct fpi_sync_group *group = find_group_by_id(sync_group->group_id); + struct processing_module *mod = comp_mod(parent_dev); assert_can_be_cold(); @@ -62,11 +63,13 @@ __cold static int add_to_fpi_sync_group(struct comp_dev *parent_dev, group->ref_count++; } else { - group = rzalloc(SOF_MEM_FLAG_USER | SOF_MEM_FLAG_COHERENT, sizeof(*group)); + group = mod_alloc_ext(mod, SOF_MEM_FLAG_USER | SOF_MEM_FLAG_COHERENT, + sizeof(*group), 0); if (!group) { comp_err(parent_dev, "Failed to alloc memory for new group"); return -ENOMEM; } + memset(group, 0, sizeof(*group)); group->id = sync_group->group_id; group->period = sync_group->fpi_update_period_usec; @@ -81,9 +84,10 @@ __cold static int add_to_fpi_sync_group(struct comp_dev *parent_dev, return 0; } -__cold static void delete_from_fpi_sync_group(struct host_data *hd) +__cold static void delete_from_fpi_sync_group(struct processing_module *mod) { - struct fpi_sync_group *group = find_group_by_id(hd->group_id); + struct copier_data *cd = module_get_private_data(mod); + struct fpi_sync_group *group = find_group_by_id(cd->hd->group_id); assert_can_be_cold(); @@ -93,7 +97,7 @@ __cold static void delete_from_fpi_sync_group(struct host_data *hd) group->ref_count--; if (group->ref_count == 0) { list_item_del(&group->item); - rfree(group); + mod_free(mod, group); } } #endif @@ -131,11 +135,12 @@ __cold static int init_pipeline_reg(struct comp_dev *dev) * Sof host component can support this case so copier reuses host * component to support host gateway. */ -__cold int copier_host_create(struct comp_dev *dev, struct copier_data *cd, +__cold int copier_host_create(struct processing_module *mod, const struct ipc4_copier_module_cfg *copier_cfg, struct pipeline *pipeline) { - struct processing_module *mod = comp_mod(dev); + struct copier_data *cd = module_get_private_data(mod); + struct comp_dev *dev = mod->dev; struct comp_ipc_config *config = &dev->ipc_config; struct ipc_config_host ipc_host; struct host_data *hd; @@ -177,7 +182,7 @@ __cold int copier_host_create(struct comp_dev *dev, struct copier_data *cd, ipc_host.dma_buffer_size = copier_cfg->gtw_cfg.dma_buffer_size; ipc_host.feature_mask = copier_cfg->copier_feature_mask; - hd = rzalloc(SOF_MEM_FLAG_USER, sizeof(*hd)); + hd = mod_zalloc(mod, sizeof(*hd)); if (!hd) return -ENOMEM; @@ -205,14 +210,16 @@ __cold int copier_host_create(struct comp_dev *dev, struct copier_data *cd, if (value_ptr) { struct ipc4_copier_sync_group *sync_group; - if (value_size != sizeof(struct ipc4_copier_sync_group)) - return -EINVAL; + if (value_size != sizeof(struct ipc4_copier_sync_group)) { + ret = -EINVAL; + goto e_conv; + } sync_group = (struct ipc4_copier_sync_group *)((void *)value_ptr); ret = add_to_fpi_sync_group(dev, hd, sync_group); if (ret < 0) - return ret; + goto e_conv; } } #endif @@ -249,21 +256,23 @@ __cold int copier_host_create(struct comp_dev *dev, struct copier_data *cd, e_conv: host_common_free(hd); e_data: - rfree(hd); + mod_free(mod, hd); return ret; } -__cold void copier_host_free(struct copier_data *cd) +__cold void copier_host_free(struct processing_module *mod) { + struct copier_data *cd = module_get_private_data(mod); + assert_can_be_cold(); #if CONFIG_HOST_DMA_STREAM_SYNCHRONIZATION if (cd->hd->is_grouped) - delete_from_fpi_sync_group(cd->hd); + delete_from_fpi_sync_group(mod); #endif host_common_free(cd->hd); - rfree(cd->hd); + mod_free(mod, cd->hd); } /* This is called by DMA driver every time when DMA completes its current @@ -275,7 +284,7 @@ void copier_host_dma_cb(struct comp_dev *dev, size_t bytes) struct copier_data *cd = module_get_private_data(mod); int ret, frames; - comp_dbg(dev, "copier_host_dma_cb() %p", dev); + comp_dbg(dev, "%p", dev); /* update position */ host_common_update(cd->hd, dev, bytes); @@ -293,7 +302,7 @@ void copier_host_dma_cb(struct comp_dev *dev, size_t bytes) ret = apply_attenuation(dev, cd, cd->hd->local_buffer, frames); if (ret < 0) - comp_dbg(dev, "copier_host_dma_cb() apply attenuation failed! %d", ret); + comp_dbg(dev, "apply attenuation failed! %d", ret); buffer_stream_writeback(cd->hd->local_buffer, bytes); } diff --git a/src/audio/copier/copier_ipcgtw.c b/src/audio/copier/copier_ipcgtw.c index ea518b5e12f2..ed365c1e50d8 100644 --- a/src/audio/copier/copier_ipcgtw.c +++ b/src/audio/copier/copier_ipcgtw.c @@ -2,10 +2,10 @@ // // Copyright 2023 Intel Corporation. All rights reserved. +#include <sof/audio/module_adapter/module/generic.h> #include <sof/audio/component_ext.h> #include <sof/trace/trace.h> #include <sof/lib/memory.h> -#include <sof/lib/uuid.h> #include <sof/ut.h> #include <rtos/init.h> #include "copier.h" @@ -13,10 +13,6 @@ LOG_MODULE_REGISTER(ipcgtw, CONFIG_SOF_LOG_LEVEL); -SOF_DEFINE_REG_UUID(ipcgw); - -DECLARE_TR_CTX(ipcgtw_comp_tr, SOF_UUID(ipcgw_uuid), LOG_LEVEL_INFO); - /* List of existing IPC gateways */ static struct list_item ipcgtw_list_head = LIST_INIT(ipcgtw_list_head); @@ -207,10 +203,12 @@ void copier_ipcgtw_reset(struct comp_dev *dev) } } -__cold int copier_ipcgtw_create(struct comp_dev *dev, struct copier_data *cd, +__cold int copier_ipcgtw_create(struct processing_module *mod, const struct ipc4_copier_module_cfg *copier, struct pipeline *pipeline) { + struct copier_data *cd = module_get_private_data(mod); + struct comp_dev *dev = mod->dev; struct comp_ipc_config *config = &dev->ipc_config; struct ipcgtw_data *ipcgtw_data; const struct ipc4_copier_gateway_cfg *gtw_cfg; @@ -231,7 +229,7 @@ __cold int copier_ipcgtw_create(struct comp_dev *dev, struct copier_data *cd, config->type = SOF_COMP_HOST; cd->gtw_type = ipc4_gtw_host; - ipcgtw_data = rzalloc(SOF_MEM_FLAG_USER, sizeof(*ipcgtw_data)); + ipcgtw_data = mod_zalloc(mod, sizeof(*ipcgtw_data)); if (!ipcgtw_data) return -ENOMEM; @@ -273,14 +271,16 @@ __cold int copier_ipcgtw_create(struct comp_dev *dev, struct copier_data *cd, return 0; e_ipcgtw: - rfree(ipcgtw_data); + mod_free(mod, ipcgtw_data); return ret; } -__cold void copier_ipcgtw_free(struct copier_data *cd) +__cold void copier_ipcgtw_free(struct processing_module *mod) { + struct copier_data *cd = module_get_private_data(mod); + assert_can_be_cold(); list_item_del(&cd->ipcgtw_data->item); - rfree(cd->ipcgtw_data); + mod_free(mod, cd->ipcgtw_data); } diff --git a/src/audio/copier/dai_copier.h b/src/audio/copier/dai_copier.h index 0ec7f04ae6ac..481b06b4bc58 100644 --- a/src/audio/copier/dai_copier.h +++ b/src/audio/copier/dai_copier.h @@ -79,7 +79,7 @@ int copier_dai_create(struct comp_dev *dev, struct copier_data *cd, const struct ipc4_copier_module_cfg *copier, struct pipeline *pipeline); -void copier_dai_free(struct copier_data *cd); +void copier_dai_free(struct processing_module *mod); int copier_dai_prepare(struct comp_dev *dev, struct copier_data *cd); diff --git a/src/audio/copier/host_copier.h b/src/audio/copier/host_copier.h index e8f5fc58ca91..2bc8904e1258 100644 --- a/src/audio/copier/host_copier.h +++ b/src/audio/copier/host_copier.h @@ -22,6 +22,10 @@ #include <sof/lib/notifier.h> #include "copier.h" +#ifdef CONFIG_SOF_TELEMETRY_IO_PERFORMANCE_MEASUREMENTS +struct io_perf_data_item; +#endif + typedef void (*copy_callback_t)(struct comp_dev *dev, size_t bytes); struct host_data; @@ -50,10 +54,11 @@ struct host_data { /* local DMA config */ #if CONFIG_ZEPHYR_NATIVE_DRIVERS struct sof_dma *dma; + int chan_index; #else struct dma *dma; -#endif struct dma_chan_data *chan; +#endif struct dma_sg_config config; #ifdef __ZEPHYR__ struct dma_config z_config; @@ -112,6 +117,9 @@ struct host_data { uint64_t next_sync; uint64_t period_in_cycles; #endif +#ifdef CONFIG_SOF_TELEMETRY_IO_PERFORMANCE_MEASUREMENTS + struct io_perf_data_item *io_perf_host_byte_count; +#endif }; int host_common_new(struct host_data *hd, struct comp_dev *dev, @@ -129,10 +137,10 @@ static inline int host_common_copy(struct host_data *hd, struct comp_dev *dev, c } void host_common_update(struct host_data *hd, struct comp_dev *dev, uint32_t bytes); void host_common_one_shot(struct host_data *hd, uint32_t bytes); -int copier_host_create(struct comp_dev *dev, struct copier_data *cd, +int copier_host_create(struct processing_module *mod, const struct ipc4_copier_module_cfg *copier_cfg, struct pipeline *pipeline); -void copier_host_free(struct copier_data *cd); +void copier_host_free(struct processing_module *mod); int copier_host_params(struct copier_data *cd, struct comp_dev *dev, struct sof_ipc_stream_params *params); void copier_host_dma_cb(struct comp_dev *dev, size_t bytes); diff --git a/src/audio/copier/ipcgtw_copier.h b/src/audio/copier/ipcgtw_copier.h index ef42ede75ddd..a9ea82914d4e 100644 --- a/src/audio/copier/ipcgtw_copier.h +++ b/src/audio/copier/ipcgtw_copier.h @@ -95,17 +95,17 @@ struct ipc4_ipc_gateway_cmd_data_reply { int copier_ipcgtw_process(const struct ipc4_ipcgtw_cmd *cmd, void *reply_payload, uint32_t *reply_payload_size); -int copier_ipcgtw_create(struct comp_dev *dev, struct copier_data *cd, +int copier_ipcgtw_create(struct processing_module *mod, const struct ipc4_copier_module_cfg *copier, struct pipeline *pipeline); #if CONFIG_IPC4_GATEWAY -void copier_ipcgtw_free(struct copier_data *cd); +void copier_ipcgtw_free(struct processing_module *mod); int copier_ipcgtw_params(struct ipcgtw_data *ipcgtw_data, struct comp_dev *dev, struct sof_ipc_stream_params *params); void copier_ipcgtw_reset(struct comp_dev *dev); #else -static inline void copier_ipcgtw_free(struct copier_data *cd) {} +static inline void copier_ipcgtw_free(struct processing_module *mod) {} static inline void copier_ipcgtw_reset(struct comp_dev *dev) {} static inline int copier_ipcgtw_params(struct ipcgtw_data *ipcgtw_data, struct comp_dev *dev, struct sof_ipc_stream_params *params) diff --git a/src/audio/crossover/README.md b/src/audio/crossover/README.md new file mode 100644 index 000000000000..1e0e180178a9 --- /dev/null +++ b/src/audio/crossover/README.md @@ -0,0 +1,24 @@ +# Crossover Architecture + +This directory contains the Crossover filter component. + +## Overview + +The Crossover splits an audio signal into multiple frequency bands (e.g., Low, Mid, High) so they can be processed or routed independently (like to a woofer and a tweeter). + +## Architecture Diagram + +```mermaid +graph LR + In[Wideband Audio In] --> Cross[Crossover Split] + Cross --> OutLow[Low Frequency Out] + Cross --> OutHigh[High Frequency Out] +``` + +## Configuration and Scripts + +- **Kconfig**: Selects the Crossover Filter (`COMP_CROSSOVER`), which splits signals into driver-specific frequencies. Also automatically selects `COMP_BLOB` and `MATH_IIR_DF2T` math functions for its internal filters. +- **CMakeLists.txt**: Handles modular builds (`llext`). Selects `crossover.c` and generic implementations, and chooses the correct IPC file (`crossover_ipc3.c` or `crossover_ipc4.c`) based on the active IPC major version. +- **crossover.toml**: Topology parameters for the Crossover module. For IPC4, it sets `init_config = 1` so that `base_cfg_ext` is appended to the IPC payload, which is required up-front to identify output pin indices. Defines UUID, pins, and memory requirements. +- **Topology (.conf)**: `tools/topology/topology2/include/components/crossover.conf` defines the `crossover` widget object. It sets basic widget requirements and defaults to type `effect` with UUID `d1:9a:8c:94:6a:80:31:41:ad:6c:b2:bd:a9:e3:5a:9f`. +- **MATLAB Tuning (`tune/`)**: The `tune` directory holds MATLAB/Octave scripts (like `sof_example_crossover.m`) that convert mathematical frequency split parameters (e.g., 200 Hz, 1000 Hz, 3000 Hz cutoffs) into binary, ALSA, and `.conf` payloads suitable for 2-way, 3-way, or 4-way driver crossover configurations. diff --git a/src/audio/crossover/crossover.c b/src/audio/crossover/crossover.c index d1c0eefa4d7f..5c1dca7cc9aa 100644 --- a/src/audio/crossover/crossover.c +++ b/src/audio/crossover/crossover.c @@ -15,7 +15,6 @@ #include <sof/common.h> #include <rtos/panic.h> #include <sof/ipc/msg.h> -#include <rtos/alloc.h> #include <rtos/init.h> #include <sof/lib/uuid.h> #include <sof/math/iir_df1.h> @@ -41,18 +40,17 @@ LOG_MODULE_REGISTER(crossover, CONFIG_SOF_LOG_LEVEL); SOF_DEFINE_REG_UUID(crossover); -DECLARE_TR_CTX(crossover_tr, SOF_UUID(crossover_uuid), LOG_LEVEL_INFO); - /** * \brief Reset the state (coefficients and delay) of the crossover filter * across all channels */ -static void crossover_reset_state(struct comp_data *cd) +static void crossover_reset_state(struct processing_module *mod) { + struct comp_data *cd = module_get_private_data(mod); int i; for (i = 0; i < PLATFORM_MAX_CHANNELS; i++) - crossover_reset_state_ch(&cd->state[i]); + crossover_reset_state_ch(mod, &cd->state[i]); } /** @@ -73,7 +71,7 @@ int crossover_get_stream_index(struct processing_module *mod, if (assign_sink[i] == pipe_id) return i; - comp_err(mod->dev, "crossover_get_stream_index() error: couldn't find any assignment for connected pipeline %u", + comp_err(mod->dev, "error: couldn't find any assignment for connected pipeline %u", pipe_id); return -EINVAL; @@ -127,14 +125,14 @@ static int crossover_assign_sinks(struct processing_module *mod, */ if (i < 0) { comp_err(dev, - "crossover_assign_sinks(), could not find sink %d in config", + "could not find sink %d in config", sink_id); break; } if (assigned_obufs[i]) { comp_err(dev, - "crossover_assign_sinks(), multiple sinks with id %d are assigned", + "multiple sinks with id %d are assigned", sink_id); break; } @@ -156,7 +154,8 @@ static int crossover_assign_sinks(struct processing_module *mod, * high/low pass filter. * \param[out] lr4 initialized struct */ -static int crossover_init_coef_lr4(struct sof_eq_iir_biquad *coef, +static int crossover_init_coef_lr4(struct processing_module *mod, + struct sof_eq_iir_biquad *coef, struct iir_state_df1 *lr4) { int ret; @@ -169,8 +168,7 @@ static int crossover_init_coef_lr4(struct sof_eq_iir_biquad *coef, * in series due to identity. To maintain the structure of * iir_state_df1, it requires two copies of coefficients in a row. */ - lr4->coef = rzalloc(SOF_MEM_FLAG_USER, - sizeof(struct sof_eq_iir_biquad) * 2); + lr4->coef = mod_zalloc(mod, sizeof(struct sof_eq_iir_biquad) * 2); if (!lr4->coef) return -ENOMEM; @@ -189,8 +187,7 @@ static int crossover_init_coef_lr4(struct sof_eq_iir_biquad *coef, * delay[0..1] -> state for first biquad * delay[2..3] -> state for second biquad */ - lr4->delay = rzalloc(SOF_MEM_FLAG_USER, - sizeof(uint64_t) * CROSSOVER_NUM_DELAYS_LR4); + lr4->delay = mod_zalloc(mod, sizeof(uint64_t) * CROSSOVER_NUM_DELAYS_LR4); if (!lr4->delay) return -ENOMEM; @@ -203,7 +200,8 @@ static int crossover_init_coef_lr4(struct sof_eq_iir_biquad *coef, /** * \brief Initializes the crossover coefficients for one channel */ -int crossover_init_coef_ch(struct sof_eq_iir_biquad *coef, +int crossover_init_coef_ch(struct processing_module *mod, + struct sof_eq_iir_biquad *coef, struct crossover_state *ch_state, int32_t num_sinks) { @@ -214,12 +212,12 @@ int crossover_init_coef_ch(struct sof_eq_iir_biquad *coef, for (i = 0; i < num_lr4s; i++) { /* Get the low pass coefficients */ - err = crossover_init_coef_lr4(&coef[j], + err = crossover_init_coef_lr4(mod, &coef[j], &ch_state->lowpass[i]); if (err < 0) return -EINVAL; /* Get the high pass coefficients */ - err = crossover_init_coef_lr4(&coef[j + 1], + err = crossover_init_coef_lr4(mod, &coef[j + 1], &ch_state->highpass[i]); if (err < 0) return -EINVAL; @@ -243,29 +241,29 @@ static int crossover_init_coef(struct processing_module *mod, int nch) int ch, err; if (!config) { - comp_err(mod->dev, "crossover_init_coef(), no config is set"); + comp_err(mod->dev, "no config is set"); return -EINVAL; } /* Sanity checks */ if (nch > PLATFORM_MAX_CHANNELS) { - comp_err(mod->dev, "crossover_init_coef(), invalid channels count (%i)", nch); + comp_err(mod->dev, "invalid channels count (%i)", nch); return -EINVAL; } - comp_info(mod->dev, "crossover_init_coef(), initializing %i-way crossover", + comp_info(mod->dev, "initializing %i-way crossover", config->num_sinks); /* Collect the coef array and assign it to every channel */ crossover = config->coef; for (ch = 0; ch < nch; ch++) { - err = crossover_init_coef_ch(crossover, &cd->state[ch], + err = crossover_init_coef_ch(mod, crossover, &cd->state[ch], config->num_sinks); /* Free all previously allocated blocks in case of an error */ if (err < 0) { - comp_err(mod->dev, "crossover_init_coef(), could not assign coefficients to ch %d", + comp_err(mod->dev, "could not assign coefficients to ch %d", ch); - crossover_reset_state(cd); + crossover_reset_state(mod); return err; } } @@ -278,11 +276,10 @@ static int crossover_init_coef(struct processing_module *mod, int nch) */ static int crossover_setup(struct processing_module *mod, int nch) { - struct comp_data *cd = module_get_private_data(mod); int ret = 0; /* Reset any previous state */ - crossover_reset_state(cd); + crossover_reset_state(mod); /* Assign LR4 coefficients from config */ ret = crossover_init_coef(mod, nch); @@ -299,52 +296,36 @@ static int crossover_init(struct processing_module *mod) struct module_data *md = &mod->priv; struct comp_dev *dev = mod->dev; struct comp_data *cd; - const struct module_config *ipc_crossover = &md->cfg; - size_t bs = ipc_crossover->size; int ret; - comp_info(dev, "crossover_init()"); - - /* Check that the coefficients blob size is sane */ - if (bs > SOF_CROSSOVER_MAX_SIZE) { - comp_err(dev, "crossover_init(), blob size (%d) exceeds maximum allowed size (%i)", - bs, SOF_CROSSOVER_MAX_SIZE); - return -ENOMEM; - } + comp_info(dev, "entry"); - cd = rzalloc(SOF_MEM_FLAG_USER, sizeof(*cd)); + cd = mod_zalloc(mod, sizeof(*cd)); if (!cd) return -ENOMEM; md->private = cd; /* Handler for configuration data */ - cd->model_handler = comp_data_blob_handler_new(dev); + cd->model_handler = mod_data_blob_handler_new(mod); if (!cd->model_handler) { comp_err(dev, "comp_data_blob_handler_new() failed."); ret = -ENOMEM; goto cd_fail; } - /* Get configuration data and reset Crossover state */ - ret = comp_init_data_blob(cd->model_handler, bs, ipc_crossover->data); - if (ret < 0) { - comp_err(dev, "comp_init_data_blob() failed."); - goto cd_fail; - } - ret = crossover_output_pin_init(mod); if (ret < 0) { - comp_err(dev, "crossover_init_output_pins() failed."); + comp_err(dev, "failed."); goto cd_fail; } - crossover_reset_state(cd); + crossover_reset_state(mod); return 0; cd_fail: - comp_data_blob_handler_free(cd->model_handler); - rfree(cd); + mod_data_blob_handler_free(mod, cd->model_handler); + mod_free(mod, cd); return ret; } @@ -355,13 +336,13 @@ static int crossover_free(struct processing_module *mod) { struct comp_data *cd = module_get_private_data(mod); - comp_info(mod->dev, "crossover_free()"); + comp_info(mod->dev, "entry"); - comp_data_blob_handler_free(cd->model_handler); + mod_data_blob_handler_free(mod, cd->model_handler); - crossover_reset_state(cd); + crossover_reset_state(mod); - rfree(cd); + mod_free(mod, cd); return 0; } @@ -378,13 +359,13 @@ static int crossover_validate_config(struct processing_module *mod, int32_t num_assigned_sinks; if (size > SOF_CROSSOVER_MAX_SIZE || !size) { - comp_err(dev, "crossover_validate_config(), size %d is invalid", size); + comp_err(dev, "size %d is invalid", size); return -EINVAL; } if (config->num_sinks > SOF_CROSSOVER_MAX_STREAMS || config->num_sinks < 2) { - comp_err(dev, "crossover_validate_config(), invalid num_sinks %i, expected number between 2 and %i", + comp_err(dev, "invalid num_sinks %i, expected number between 2 and %i", config->num_sinks, SOF_CROSSOVER_MAX_STREAMS); return -EINVAL; } @@ -398,7 +379,7 @@ static int crossover_validate_config(struct processing_module *mod, * is different than what is configured. */ if (num_assigned_sinks != config->num_sinks) { - comp_err(dev, "crossover_validate_config(), number of assigned sinks %d, expected from config %d", + comp_err(dev, "number of assigned sinks %d, expected from config %d", num_assigned_sinks, config->num_sinks); return -EINVAL; } @@ -415,7 +396,7 @@ static int crossover_set_config(struct processing_module *mod, uint32_t config_i struct comp_data *cd = module_get_private_data(mod); int ret; - comp_info(mod->dev, "crossover_set_config()"); + comp_info(mod->dev, "entry"); ret = crossover_check_config(mod, fragment); if (ret < 0) @@ -433,7 +414,7 @@ static int crossover_get_config(struct processing_module *mod, struct sof_ipc_ctrl_data *cdata = (struct sof_ipc_ctrl_data *)fragment; int ret; - comp_info(mod->dev, "crossover_get_config()"); + comp_info(mod->dev, "entry"); ret = crossover_check_config(mod, fragment); if (ret < 0) @@ -470,14 +451,14 @@ static int crossover_process_audio_stream(struct processing_module *mod, int ret; int i; - comp_dbg(dev, "crossover_process_audio_stream()"); + comp_dbg(dev, "entry"); /* Check for changed configuration */ if (comp_is_new_data_blob_available(cd->model_handler)) { cd->config = comp_get_data_blob(cd->model_handler, NULL, NULL); ret = crossover_setup(mod, audio_stream_get_channels(source)); if (ret < 0) { - comp_err(dev, "crossover_process_audio_stream(), failed Crossover setup"); + comp_err(dev, "failed Crossover setup"); return ret; } } @@ -530,9 +511,10 @@ static int crossover_prepare(struct processing_module *mod, struct comp_data *cd = module_get_private_data(mod); struct comp_dev *dev = mod->dev; struct comp_buffer *source, *sink; + size_t data_size; int channels; - comp_info(dev, "crossover_prepare()"); + comp_info(dev, "entry"); source = comp_dev_get_first_data_producer(dev); if (!source) { @@ -558,45 +540,46 @@ static int crossover_prepare(struct processing_module *mod, } } - comp_info(dev, "crossover_prepare(), source_format=%d, sink_formats=%d, nch=%d", + comp_info(dev, "source_format=%d, sink_formats=%d, nch=%d", cd->source_format, cd->source_format, channels); - cd->config = comp_get_data_blob(cd->model_handler, NULL, NULL); + cd->config = comp_get_data_blob(cd->model_handler, &data_size, NULL); /* Initialize Crossover */ - if (cd->config && crossover_validate_config(mod, cd->config) < 0) { - /* If config is invalid then delete it */ - comp_err(dev, "crossover_prepare(), invalid binary config format"); - crossover_free_config(&cd->config); + if (cd->config && + (!data_size || crossover_validate_config(mod, cd->config) < 0)) { + /* If the configuration is invalid fail the prepare */ + comp_err(dev, "invalid binary config format"); + return -EINVAL; } if (cd->config) { int ret = crossover_setup(mod, channels); if (ret < 0) { - comp_err(dev, "crossover_prepare(), setup failed"); + comp_err(dev, "setup failed"); return ret; } cd->crossover_process = crossover_find_proc_func(cd->source_format); if (!cd->crossover_process) { - comp_err(dev, "crossover_prepare(), No processing function matching frame_fmt %i", + comp_err(dev, "No processing function matching frame_fmt %i", cd->source_format); return -EINVAL; } cd->crossover_split = crossover_find_split_func(cd->config->num_sinks); if (!cd->crossover_split) { - comp_err(dev, "crossover_prepare(), No split function matching num_sinks %i", + comp_err(dev, "No split function matching num_sinks %i", cd->config->num_sinks); return -EINVAL; } } else { - comp_info(dev, "crossover_prepare(), setting crossover to passthrough mode"); + comp_info(dev, "setting crossover to passthrough mode"); cd->crossover_process = crossover_find_proc_func_pass(cd->source_format); if (!cd->crossover_process) { - comp_err(dev, "crossover_prepare(), No passthrough function matching frame_fmt %i", + comp_err(dev, "No passthrough function matching frame_fmt %i", cd->source_format); return -EINVAL; } @@ -614,9 +597,9 @@ static int crossover_reset(struct processing_module *mod) { struct comp_data *cd = module_get_private_data(mod); - comp_info(mod->dev, "crossover_reset()"); + comp_info(mod->dev, "entry"); - crossover_reset_state(cd); + crossover_reset_state(mod); cd->crossover_process = NULL; cd->crossover_split = NULL; @@ -649,6 +632,7 @@ SOF_LLEXT_BUILDINFO; #else +DECLARE_TR_CTX(crossover_tr, SOF_UUID(crossover_uuid), LOG_LEVEL_INFO); DECLARE_MODULE_ADAPTER(crossover_interface, crossover_uuid, crossover_tr); SOF_MODULE_INIT(crossover, sys_comp_module_crossover_interface_init); diff --git a/src/audio/crossover/crossover_ipc3.c b/src/audio/crossover/crossover_ipc3.c index 07eb39ebca95..48f56ac3cced 100644 --- a/src/audio/crossover/crossover_ipc3.c +++ b/src/audio/crossover/crossover_ipc3.c @@ -48,13 +48,13 @@ int crossover_check_sink_assign(struct processing_module *mod, i = crossover_get_stream_index(mod, config, pipeline_id); if (i < 0) { - comp_warn(dev, "crossover_check_sink_assign(), could not assign sink %d", + comp_warn(dev, "could not assign sink %d", pipeline_id); break; } if (assigned_sinks[i]) { - comp_warn(dev, "crossover_check_sink_assign(), multiple sinks from pipeline %d are assigned", + comp_warn(dev, "multiple sinks from pipeline %d are assigned", pipeline_id); break; } diff --git a/src/audio/crossover/crossover_ipc4.c b/src/audio/crossover/crossover_ipc4.c index 981d34bafb7f..3e0beffeeeb1 100644 --- a/src/audio/crossover/crossover_ipc4.c +++ b/src/audio/crossover/crossover_ipc4.c @@ -81,13 +81,13 @@ int crossover_check_sink_assign(struct processing_module *mod, pin_index = cd->output_pin_index[j]; i = crossover_get_stream_index(mod, config, pin_index); if (i < 0) { - comp_warn(dev, "crossover_check_sink_assign(), could not assign sink %u", + comp_warn(dev, "could not assign sink %u", pin_index); break; } if (assigned_sinks[i]) { - comp_warn(dev, "crossover_check_sink_assign(), multiple sinks from pin %u are assigned", + comp_warn(dev, "multiple sinks from pin %u are assigned", pin_index); break; } @@ -113,7 +113,7 @@ void crossover_params(struct processing_module *mod) struct comp_buffer *sinkb, *sourceb; struct comp_dev *dev = mod->dev; - comp_dbg(dev, "crossover_params()"); + comp_dbg(dev, "entry"); ipc4_base_module_cfg_to_stream_params(&mod->priv.cfg.base_cfg, params); component_set_nearest_period_frames(dev, params->rate); diff --git a/src/audio/dai-legacy.c b/src/audio/dai-legacy.c index 59e2caed95b9..655fa94afb86 100644 --- a/src/audio/dai-legacy.c +++ b/src/audio/dai-legacy.c @@ -41,8 +41,6 @@ LOG_MODULE_REGISTER(dai_comp, CONFIG_SOF_LOG_LEVEL); SOF_DEFINE_REG_UUID(dai); -DECLARE_TR_CTX(dai_comp_tr, SOF_UUID(dai_uuid), LOG_LEVEL_INFO); - #if CONFIG_COMP_DAI_GROUP static int dai_comp_trigger_internal(struct dai_data *dd, struct comp_dev *dev, int cmd); @@ -62,7 +60,7 @@ int dai_assign_group(struct dai_data *dd, struct comp_dev *dev, uint32_t group_i { if (dd->group) { if (dd->group->group_id != group_id) { - comp_err(dev, "dai_assign_group(), DAI already in group %d, requested %d", + comp_err(dev, "DAI already in group %d, requested %d", dd->group->group_id, group_id); return -EINVAL; } @@ -73,12 +71,12 @@ int dai_assign_group(struct dai_data *dd, struct comp_dev *dev, uint32_t group_i dd->group = dai_group_get(group_id, DAI_CREAT); if (!dd->group) { - comp_err(dev, "dai_assign_group(), failed to assign group %d", + comp_err(dev, "failed to assign group %d", group_id); return -EINVAL; } - comp_dbg(dev, "dai_assign_group(), group %d num %d", + comp_dbg(dev, "group %d num %d", group_id, dd->group->num_dais); /* Register for the atomic trigger event */ @@ -97,7 +95,7 @@ static void dai_dma_cb(void *arg, enum notify_id type, void *data) uint32_t bytes = next->elem.size; int ret; - comp_dbg(dev, "dai_dma_cb()"); + comp_dbg(dev, "entry"); next->status = SOF_DMA_CB_STATUS_RELOAD; @@ -136,7 +134,7 @@ static void dai_dma_cb(void *arg, enum notify_id type, void *data) dd->local_buffer : dd->dma_buffer; sink_c = dev->direction == SOF_IPC_STREAM_PLAYBACK ? dd->dma_buffer : dd->local_buffer; - comp_err(dev, "dai_dma_cb() dma buffer copy failed, dir %d bytes %d avail %d free %d", + comp_err(dev, "dma buffer copy failed, dir %d bytes %d avail %d free %d", dev->direction, bytes, audio_stream_get_avail_samples(&source_c->stream) * audio_stream_frame_bytes(&source_c->stream), @@ -198,7 +196,7 @@ static struct comp_dev *dai_new(const struct comp_driver *drv, dd = rzalloc(SOF_MEM_FLAG_USER | SOF_MEM_FLAG_COHERENT, sizeof(*dd)); if (!dd) { - rfree(dev); + comp_free_device(dev); return NULL; } @@ -213,7 +211,7 @@ static struct comp_dev *dai_new(const struct comp_driver *drv, error: rfree(dd); - rfree(dev); + comp_free_device(dev); return NULL; } @@ -250,7 +248,7 @@ static void dai_free(struct comp_dev *dev) dai_common_free(dd); rfree(dd); - rfree(dev); + comp_free_device(dev); } int dai_common_get_hw_params(struct dai_data *dd, struct comp_dev *dev, @@ -293,7 +291,7 @@ static int dai_comp_hw_params(struct comp_dev *dev, struct dai_data *dd = comp_get_drvdata(dev); int ret; - comp_dbg(dev, "dai_comp_hw_params()"); + comp_dbg(dev, "entry"); /* configure hw dai stream params */ ret = dai_hw_params(dd->dai, params); @@ -371,7 +369,7 @@ static int dai_playback_params(struct comp_dev *dev, uint32_t period_bytes, config->is_scheduling_source = comp_is_scheduling_source(dev); config->period = dev->pipeline->period; - comp_info(dev, "dai_playback_params() dest_dev = %d stream_id = %d src_width = %d dest_width = %d", + comp_info(dev, "dest_dev = %d stream_id = %d src_width = %d dest_width = %d", config->dest_dev, dd->stream_id, config->src_width, config->dest_width); @@ -379,9 +377,9 @@ static int dai_playback_params(struct comp_dev *dev, uint32_t period_bytes, fifo = dai_get_fifo(dd->dai, dev->direction, dd->stream_id); - comp_info(dev, "dai_playback_params() fifo 0x%x", fifo); + comp_info(dev, "fifo 0x%x", fifo); - err = dma_sg_alloc(&config->elem_array, SOF_MEM_FLAG_USER, + err = dma_sg_alloc(NULL, &config->elem_array, SOF_MEM_FLAG_USER, config->direction, period_count, period_bytes, @@ -436,7 +434,7 @@ static int dai_capture_params(struct comp_dev *dev, uint32_t period_bytes, config->dest_width = config->src_width; } - comp_info(dev, "dai_capture_params() src_dev = %d stream_id = %d src_width = %d dest_width = %d", + comp_info(dev, "src_dev = %d stream_id = %d src_width = %d dest_width = %d", config->src_dev, dd->stream_id, config->src_width, config->dest_width); @@ -444,9 +442,9 @@ static int dai_capture_params(struct comp_dev *dev, uint32_t period_bytes, fifo = dai_get_fifo(dd->dai, dev->direction, dd->stream_id); - comp_info(dev, "dai_capture_params() fifo 0x%x", fifo); + comp_info(dev, "fifo 0x%x", fifo); - err = dma_sg_alloc(&config->elem_array, SOF_MEM_FLAG_USER, + err = dma_sg_alloc(NULL, &config->elem_array, SOF_MEM_FLAG_USER, config->direction, period_count, period_bytes, @@ -561,7 +559,8 @@ int dai_common_params(struct dai_data *dd, struct comp_dev *dev, return err; } } else { - dd->dma_buffer = buffer_alloc(buffer_size, SOF_MEM_FLAG_USER | SOF_MEM_FLAG_DMA, + dd->dma_buffer = buffer_alloc(NULL, buffer_size, + SOF_MEM_FLAG_USER | SOF_MEM_FLAG_DMA, addr_align, BUFFER_USAGE_NOT_SHARED); if (!dd->dma_buffer) { comp_err(dev, "failed to alloc dma buffer"); @@ -588,7 +587,7 @@ static int dai_params(struct comp_dev *dev, struct sof_ipc_stream_params *params { struct dai_data *dd = comp_get_drvdata(dev); - comp_dbg(dev, "dai_params()"); + comp_dbg(dev, "entry"); return dai_common_params(dd, dev, params); } @@ -615,7 +614,7 @@ int dai_common_config_prepare(struct dai_data *dd, struct comp_dev *dev) } channel = dai_config_dma_channel(dd, dev, dd->dai_spec_config); - comp_info(dev, "dai_common_config_prepare(), channel = %d", channel); + comp_info(dev, "channel = %d", channel); /* do nothing for asking for channel free, for compatibility. */ if (channel == DMA_CHAN_INVALID) { @@ -683,7 +682,7 @@ static int dai_prepare(struct comp_dev *dev) struct dai_data *dd = comp_get_drvdata(dev); int ret; - comp_info(dev, "dai_prepare()"); + comp_info(dev, "entry"); ret = dai_common_config_prepare(dd, dev); if (ret < 0) @@ -710,7 +709,7 @@ void dai_common_reset(struct dai_data *dd, struct comp_dev *dev) if (!dd->delayed_dma_stop) dai_dma_release(dd, dev); - dma_sg_free(&config->elem_array); + dma_sg_free(NULL, &config->elem_array); if (dd->dma_buffer) { buffer_free(dd->dma_buffer); @@ -726,7 +725,7 @@ static int dai_reset(struct comp_dev *dev) { struct dai_data *dd = comp_get_drvdata(dev); - comp_info(dev, "dai_reset()"); + comp_info(dev, "entry"); dai_common_reset(dd, dev); @@ -740,7 +739,7 @@ static int dai_comp_trigger_internal(struct dai_data *dd, struct comp_dev *dev, { int ret; - comp_dbg(dev, "dai_comp_trigger_internal(), command = %u", cmd); + comp_dbg(dev, "command = %u", cmd); ret = comp_set_state(dev, cmd); if (ret < 0) @@ -751,7 +750,7 @@ static int dai_comp_trigger_internal(struct dai_data *dd, struct comp_dev *dev, switch (cmd) { case COMP_TRIGGER_START: - comp_dbg(dev, "dai_comp_trigger_internal(), START"); + comp_dbg(dev, "START"); /* only start the DAI if we are not XRUN handling */ if (dd->xrun == 0) { @@ -794,12 +793,12 @@ static int dai_comp_trigger_internal(struct dai_data *dd, struct comp_dev *dev, platform_dai_wallclock(dev, &dd->wallclock); break; case COMP_TRIGGER_XRUN: - comp_info(dev, "dai_comp_trigger_internal(), XRUN"); + comp_info(dev, "XRUN"); dd->xrun = 1; COMPILER_FALLTHROUGH; case COMP_TRIGGER_STOP: - comp_dbg(dev, "dai_comp_trigger_internal(), STOP"); + comp_dbg(dev, "STOP"); /* * Some platforms cannot just simple disable * DMA channel during the transfer, @@ -817,7 +816,7 @@ static int dai_comp_trigger_internal(struct dai_data *dd, struct comp_dev *dev, #endif break; case COMP_TRIGGER_PAUSE: - comp_dbg(dev, "dai_comp_trigger_internal(), PAUSE"); + comp_dbg(dev, "PAUSE"); ret = dma_pause_legacy(dd->chan); dai_trigger(dd->dai, cmd, dev->direction); break; @@ -842,7 +841,7 @@ int dai_common_trigger(struct dai_data *dd, struct comp_dev *dev, int cmd) /* DAI not in a group, use normal trigger */ if (!group) { - comp_dbg(dev, "dai_common_trigger(), non-atomic trigger"); + comp_dbg(dev, "non-atomic trigger"); return dai_comp_trigger_internal(dd, dev, cmd); } @@ -852,13 +851,13 @@ int dai_common_trigger(struct dai_data *dd, struct comp_dev *dev, int cmd) /* First DAI to receive the trigger command, * prepare for atomic trigger */ - comp_dbg(dev, "dai_common_trigger(), begin atomic trigger for group %d", + comp_dbg(dev, "begin atomic trigger for group %d", group->group_id); group->trigger_cmd = cmd; group->trigger_counter = group->num_dais - 1; } else if (group->trigger_cmd != cmd) { /* Already processing a different trigger command */ - comp_err(dev, "dai_common_trigger(), already processing atomic trigger"); + comp_err(dev, "already processing atomic trigger"); ret = -EAGAIN; } else { /* Count down the number of remaining DAIs required @@ -866,7 +865,7 @@ int dai_common_trigger(struct dai_data *dd, struct comp_dev *dev, int cmd) * takes place */ group->trigger_counter--; - comp_dbg(dev, "dai_common_trigger(), trigger counter %d, group %d", + comp_dbg(dev, "trigger counter %d, group %d", group->trigger_counter, group->group_id); if (!group->trigger_counter) { @@ -951,7 +950,7 @@ int dai_common_copy(struct dai_data *dd, struct comp_dev *dev, pcm_converter_fun copy_bytes = samples * sampling; - comp_dbg(dev, "dai_common_copy(), dir: %d copy_bytes= 0x%x, frames= %d", + comp_dbg(dev, "dir: %d copy_bytes= 0x%x, frames= %d", dev->direction, copy_bytes, samples / audio_stream_get_channels(&dd->local_buffer->stream)); @@ -989,7 +988,7 @@ static int dai_copy(struct comp_dev *dev) { struct dai_data *dd = comp_get_drvdata(dev); - comp_dbg(dev, "dai_copy()"); + comp_dbg(dev, "entry"); /* * DAI devices will only ever have 1 sink, so no need to pass an array of PCM converter @@ -1050,7 +1049,7 @@ static int dai_ts_start(struct comp_dev *dev) { struct dai_data *dd = comp_get_drvdata(dev); - comp_dbg(dev, "dai_ts_start()"); + comp_dbg(dev, "entry"); return dai_common_ts_start(dd, dev); } @@ -1067,7 +1066,7 @@ static int dai_ts_stop(struct comp_dev *dev) { struct dai_data *dd = comp_get_drvdata(dev); - comp_dbg(dev, "dai_ts_stop()"); + comp_dbg(dev, "entry"); return dai_common_ts_stop(dd, dev); } @@ -1084,7 +1083,7 @@ static int dai_ts_get(struct comp_dev *dev, struct timestamp_data *tsd) { struct dai_data *dd = comp_get_drvdata(dev); - comp_dbg(dev, "dai_ts_get()"); + comp_dbg(dev, "entry"); return dai_common_ts_get(dd, dev, tsd); } @@ -1104,6 +1103,8 @@ static uint64_t dai_get_processed_data(struct comp_dev *dev, uint32_t stream_no, return ret; } +DECLARE_TR_CTX(dai_comp_tr, SOF_UUID(dai_uuid), LOG_LEVEL_INFO); + static const struct comp_driver comp_dai = { .type = SOF_COMP_DAI, .uid = SOF_RT_UUID(dai_uuid), diff --git a/src/audio/dai-zephyr.c b/src/audio/dai-zephyr.c index ef1abb48cc31..454132de150e 100644 --- a/src/audio/dai-zephyr.c +++ b/src/audio/dai-zephyr.c @@ -37,7 +37,6 @@ #include <stdint.h> #if CONFIG_XRUN_NOTIFICATIONS_ENABLE -#include <sof/ipc/notification_pool.h> #include <ipc4/notification.h> #endif @@ -64,8 +63,6 @@ LOG_MODULE_REGISTER(dai_comp, CONFIG_SOF_LOG_LEVEL); SOF_DEFINE_REG_UUID(dai); -DECLARE_TR_CTX(dai_comp_tr, SOF_UUID(dai_uuid), LOG_LEVEL_INFO); - #if CONFIG_COMP_DAI_GROUP static int dai_comp_trigger_internal(struct dai_data *dd, struct comp_dev *dev, int cmd); @@ -143,20 +140,21 @@ static int dai_trigger_op(struct dai *dai, int cmd, int direction) /* called from src/ipc/ipc3/handler.c and src/ipc/ipc4/dai.c */ __cold int dai_set_config(struct dai *dai, struct ipc_config_dai *common_config, - const void *spec_config) + const void *spec_config, size_t size) { const struct device *dev = dai->dev; const struct sof_ipc_dai_config *sof_cfg = spec_config; - struct dai_config cfg; + struct dai_config cfg = {0}; const void *cfg_params; + size_t dai_cfg_size = size; bool is_blob; assert_can_be_cold(); cfg.dai_index = common_config->dai_index; is_blob = common_config->is_config_blob; - cfg.format = sof_cfg->format; - cfg.options = sof_cfg->flags; + cfg.format = common_config->format; + cfg.options = is_blob ? 0 : sof_cfg->flags; cfg.rate = common_config->sampling_frequency; switch (common_config->type) { @@ -194,11 +192,34 @@ __cold int dai_set_config(struct dai *dai, struct ipc_config_dai *common_config, cfg.type = DAI_IMX_MICFIL; cfg_params = &sof_cfg->micfil; break; + case SOF_DAI_AMD_SDW: + cfg.type = DAI_AMD_SDW; + cfg_params = &sof_cfg->acpsdw; + break; + case SOF_DAI_INTEL_UAOL: + cfg.type = DAI_INTEL_UAOL; + cfg.channels = common_config->gtw_fmt->channels_count; + /* + * FIXME: The spec says HW expects container size here, not valid_bit_depth. + * However, tests fail if container size is used and work fine with + * valid_bit_depth. Needs investigation. Perhaps tests have a bug? + */ + cfg.word_size = common_config->gtw_fmt->valid_bit_depth; + cfg_params = spec_config; + dai_set_link_hda_config(&cfg.link_config, common_config, spec_config); + break; default: return -EINVAL; } - return dai_config_set(dev, &cfg, cfg_params); + if (!is_blob) { + if (size < SOF_DAI_CONFIG_HW_SPEC_OFFSET) + return -EINVAL; + + dai_cfg_size -= SOF_DAI_CONFIG_HW_SPEC_OFFSET; + } + + return dai_config_set(dev, &cfg, cfg_params, dai_cfg_size); } /* called from ipc/ipc3/dai.c */ @@ -263,7 +284,7 @@ dai_dma_cb(struct dai_data *dd, struct comp_dev *dev, uint32_t bytes, enum sof_dma_cb_status dma_status = SOF_DMA_CB_STATUS_RELOAD; int ret; - comp_dbg(dev, "dai_dma_cb()"); + comp_dbg(dev, "entry"); /* stop dma copy for pause/stop/xrun */ if (dev->state != COMP_STATE_ACTIVE || dd->xrun) { @@ -362,7 +383,7 @@ dai_dma_cb(struct dai_data *dd, struct comp_dev *dev, uint32_t bytes, sink_dev = comp_buffer_get_sink_component(sink); - j = IPC4_SINK_QUEUE_ID(buf_get_id(sink)); + j = IPC4_SRC_QUEUE_ID(buf_get_id(sink)); if (j >= IPC4_COPIER_MODULE_OUTPUT_PINS_COUNT) { comp_err(dev, "Sink queue ID: %d >= max output pin count: %d\n", @@ -408,7 +429,7 @@ dai_dma_cb(struct dai_data *dd, struct comp_dev *dev, uint32_t bytes, } #ifdef CONFIG_SOF_TELEMETRY_IO_PERFORMANCE_MEASUREMENTS /* Increment performance counters */ - io_perf_monitor_update_data(dd->io_perf_bytes_count, bytes); + io_perf_monitor_update_data(dd->io_perf_dai_byte_count, bytes); #endif return dma_status; @@ -422,7 +443,7 @@ dai_dma_multi_endpoint_cb(struct dai_data *dd, struct comp_dev *dev, uint32_t fr enum sof_dma_cb_status dma_status = SOF_DMA_CB_STATUS_RELOAD; uint32_t i, bytes; - comp_dbg(dev, "dai_dma_multi_endpoint_cb()"); + comp_dbg(dev, "entry"); /* stop dma copy for pause/stop/xrun */ if (dev->state != COMP_STATE_ACTIVE || dd->xrun) { @@ -495,6 +516,7 @@ __cold int dai_common_new(struct dai_data *dd, struct comp_dev *dev, dir = dai_cfg->direction == SOF_IPC_STREAM_PLAYBACK ? SOF_DMA_DIR_MEM_TO_DEV : SOF_DMA_DIR_DEV_TO_MEM; + dd->chan_index = -EINVAL; dd->dma = sof_dma_get(dir, dd->dai->dma_caps, dd->dai->dma_dev, SOF_DMA_ACCESS_SHARED); if (!dd->dma) { dai_put(dd->dai); @@ -506,7 +528,6 @@ __cold int dai_common_new(struct dai_data *dd, struct comp_dev *dev, dma_sg_init(&dd->config.elem_array); dd->xrun = 0; - dd->chan = NULL; /* I/O performance init, keep it last so the function does not reach this in case * of return on error, so that we do not waste a slot @@ -541,12 +562,12 @@ __cold int dai_common_new(struct dai_data *dd, struct comp_dev *dev, /* ignore perf meas init on case of other dai types */ if (perf_type != IO_PERF_INVALID_ID) { struct io_perf_data_item init_data = {perf_type, - cpu_get_id(), + dai_cfg->dai_index, perf_dir, IO_PERF_POWERED_UP_ENABLED, IO_PERF_D0IX_POWER_MODE, 0, 0, 0 }; - io_perf_monitor_init_data(&dd->io_perf_bytes_count, &init_data); + io_perf_monitor_init_data(&dd->io_perf_dai_byte_count, &init_data); } #endif @@ -591,7 +612,7 @@ __cold static struct comp_dev *dai_new(const struct comp_driver *drv, error: rfree(dd); e_data: - rfree(dev); + comp_free_device(dev); return NULL; } @@ -600,15 +621,15 @@ __cold void dai_common_free(struct dai_data *dd) assert_can_be_cold(); #ifdef CONFIG_SOF_TELEMETRY_IO_PERFORMANCE_MEASUREMENTS - io_perf_monitor_release_slot(dd->io_perf_bytes_count); + io_perf_monitor_release_slot(dd->io_perf_dai_byte_count); #endif if (dd->group) dai_group_put(dd->group); - if (dd->chan) { - dma_release_channel(dd->dma->z_dev, dd->chan->index); - dd->chan->dev_data = NULL; + if (dd->chan_index >= 0) { + sof_dma_release_channel(dd->dma, dd->chan_index); + dd->chan_index = -EINVAL; } sof_dma_put(dd->dma); @@ -632,7 +653,7 @@ __cold static void dai_free(struct comp_dev *dev) dai_common_free(dd); rfree(dd); - rfree(dev); + comp_free_device(dev); } int dai_common_get_hw_params(struct dai_data *dd, struct comp_dev *dev, @@ -641,7 +662,7 @@ int dai_common_get_hw_params(struct dai_data *dd, struct comp_dev *dev, struct dai_config cfg; int ret; - comp_dbg(dev, "dai_common_get_hw_params()"); + comp_dbg(dev, "entry"); ret = dai_config_get(dd->dai->dev, &cfg, dir); if (ret) @@ -683,7 +704,7 @@ static int dai_verify_params(struct dai_data *dd, struct comp_dev *dev, ret = dai_common_get_hw_params(dd, dev, &hw_params, params->direction); if (ret < 0) { - comp_err(dev, "dai_verify_params failed ret %d", ret); + comp_err(dev, "failed ret %d", ret); return ret; } @@ -798,7 +819,7 @@ static int dai_set_sg_config(struct dai_data *dd, struct comp_dev *dev, uint32_t comp_dbg(dev, "fifo 0x%x", fifo); - err = dma_get_attribute(dd->dma->z_dev, DMA_ATTR_MAX_BLOCK_COUNT, &max_block_count); + err = sof_dma_get_attribute(dd->dma, DMA_ATTR_MAX_BLOCK_COUNT, &max_block_count); if (err < 0) { comp_err(dev, "can't get max block count, err = %d", err); @@ -826,7 +847,7 @@ static int dai_set_sg_config(struct dai_data *dd, struct comp_dev *dev, uint32_t } while (--max_block_count > 0); } - err = dma_sg_alloc(&config->elem_array, SOF_MEM_FLAG_USER, + err = dma_sg_alloc(NULL, &config->elem_array, SOF_MEM_FLAG_USER, config->direction, period_count, period_bytes, @@ -850,9 +871,9 @@ static int dai_set_dma_config(struct dai_data *dd, struct comp_dev *dev) struct dma_block_config *prev = NULL; int i; - comp_dbg(dev, "dai_set_dma_config()"); + comp_dbg(dev, "entry"); - dma_cfg = rballoc(SOF_MEM_FLAG_USER | SOF_MEM_FLAG_COHERENT | SOF_MEM_FLAG_DMA, + dma_cfg = rmalloc(SOF_MEM_FLAG_USER | SOF_MEM_FLAG_COHERENT | SOF_MEM_FLAG_DMA, sizeof(struct dma_config)); if (!dma_cfg) { comp_err(dev, "dma_cfg allocation failed"); @@ -934,7 +955,7 @@ static int dai_set_dma_buffer(struct dai_data *dd, struct comp_dev *dev, uint32_t align; int err; - comp_dbg(dev, "dai_set_dma_buffer()"); + comp_dbg(dev, "entry"); if (dev->direction == SOF_IPC_STREAM_PLAYBACK) dd->local_buffer = comp_dev_get_first_data_producer(dev); @@ -954,14 +975,14 @@ static int dai_set_dma_buffer(struct dai_data *dd, struct comp_dev *dev, return -EINVAL; } - err = dma_get_attribute(dd->dma->z_dev, DMA_ATTR_BUFFER_ADDRESS_ALIGNMENT, &addr_align); + err = sof_dma_get_attribute(dd->dma, DMA_ATTR_BUFFER_ADDRESS_ALIGNMENT, &addr_align); if (err < 0) { comp_err(dev, "can't get dma buffer addr align, err = %d", err); return err; } - err = dma_get_attribute(dd->dma->z_dev, DMA_ATTR_BUFFER_SIZE_ALIGNMENT, &align); + err = sof_dma_get_attribute(dd->dma, DMA_ATTR_BUFFER_SIZE_ALIGNMENT, &align); if (err < 0 || !align) { comp_err(dev, "no valid dma align, err = %d, align = %u", err, align); @@ -1019,7 +1040,7 @@ static int dai_set_dma_buffer(struct dai_data *dd, struct comp_dev *dev, return err; } } else { - dd->dma_buffer = buffer_alloc_range(buffer_size_preferred, buffer_size, + dd->dma_buffer = buffer_alloc_range(NULL, buffer_size_preferred, buffer_size, SOF_MEM_FLAG_USER | SOF_MEM_FLAG_DMA, addr_align, BUFFER_USAGE_NOT_SHARED); if (!dd->dma_buffer) { @@ -1107,7 +1128,7 @@ int dai_common_params(struct dai_data *dd, struct comp_dev *dev, if (err < 0) { buffer_free(dd->dma_buffer); dd->dma_buffer = NULL; - dma_sg_free(&config->elem_array); + dma_sg_free(NULL, &config->elem_array); rfree(dd->z_config); dd->z_config = NULL; } @@ -1119,7 +1140,7 @@ static int dai_params(struct comp_dev *dev, struct sof_ipc_stream_params *params { struct dai_data *dd = comp_get_drvdata(dev); - comp_dbg(dev, "dai_params()"); + comp_dbg(dev, "entry"); return dai_common_params(dd, dev, params); } @@ -1139,9 +1160,9 @@ int dai_common_config_prepare(struct dai_data *dd, struct comp_dev *dev) return -EINVAL; } - if (dd->chan) { + if (dd->chan_index >= 0) { comp_info(dev, "dma channel index %d already configured", - dd->chan->index); + dd->chan_index); return 0; } @@ -1155,18 +1176,14 @@ int dai_common_config_prepare(struct dai_data *dd, struct comp_dev *dev) } /* get DMA channel */ - channel = dma_request_channel(dd->dma->z_dev, &channel); - if (channel < 0) { - comp_err(dev, "dma_request_channel() failed"); - dd->chan = NULL; - return -EIO; + dd->chan_index = sof_dma_request_channel(dd->dma, channel); + if (dd->chan_index < 0) { + comp_err(dev, "dma_request_channel() failed ch %d ret %d", channel, dd->chan_index); + return dd->chan_index; } - dd->chan = &dd->dma->chan[channel]; - dd->chan->dev_data = dd; - comp_dbg(dev, "new configured dma channel index %d", - dd->chan->index); + dd->chan_index); return 0; } @@ -1177,8 +1194,8 @@ int dai_common_prepare(struct dai_data *dd, struct comp_dev *dev) dd->total_data_processed = 0; - if (!dd->chan) { - comp_err(dev, "Missing dd->chan."); + if (dd->chan_index < 0) { + comp_err(dev, "Missing dd->chan_index."); comp_set_state(dev, COMP_TRIGGER_RESET); return -EINVAL; } @@ -1199,7 +1216,7 @@ int dai_common_prepare(struct dai_data *dd, struct comp_dev *dev) return 0; } - ret = dma_config(dd->chan->dma->z_dev, dd->chan->index, dd->z_config); + ret = sof_dma_config(dd->dma, dd->chan_index, dd->z_config); if (ret < 0) comp_set_state(dev, COMP_TRIGGER_RESET); @@ -1211,7 +1228,7 @@ static int dai_prepare(struct comp_dev *dev) struct dai_data *dd = comp_get_drvdata(dev); int ret; - comp_dbg(dev, "dai_prepare()"); + comp_dbg(dev, "entry"); ret = dai_common_config_prepare(dd, dev); if (ret < 0) @@ -1238,7 +1255,7 @@ void dai_common_reset(struct dai_data *dd, struct comp_dev *dev) if (!dd->delayed_dma_stop) dai_dma_release(dd, dev); - dma_sg_free(&config->elem_array); + dma_sg_free(NULL, &config->elem_array); if (dd->z_config) { rfree(dd->z_config->head_block); rfree(dd->z_config); @@ -1259,7 +1276,7 @@ static int dai_reset(struct comp_dev *dev) { struct dai_data *dd = comp_get_drvdata(dev); - comp_dbg(dev, "dai_reset()"); + comp_dbg(dev, "entry"); dai_common_reset(dd, dev); @@ -1286,7 +1303,7 @@ static int dai_comp_trigger_internal(struct dai_data *dd, struct comp_dev *dev, /* only start the DAI if we are not XRUN handling */ if (dd->xrun == 0) { - ret = dma_start(dd->chan->dma->z_dev, dd->chan->index); + ret = sof_dma_start(dd->dma, dd->chan_index); if (ret < 0) return ret; @@ -1324,16 +1341,16 @@ static int dai_comp_trigger_internal(struct dai_data *dd, struct comp_dev *dev, /* only start the DAI if we are not XRUN handling */ if (dd->xrun == 0) { /* recover valid start position */ - ret = dma_stop(dd->chan->dma->z_dev, dd->chan->index); + ret = sof_dma_stop(dd->dma, dd->chan_index); if (ret < 0) return ret; /* dma_config needed after stop */ - ret = dma_config(dd->chan->dma->z_dev, dd->chan->index, dd->z_config); + ret = sof_dma_config(dd->dma, dd->chan_index, dd->z_config); if (ret < 0) return ret; - ret = dma_start(dd->chan->dma->z_dev, dd->chan->index); + ret = sof_dma_start(dd->dma, dd->chan_index); if (ret < 0) return ret; @@ -1361,11 +1378,11 @@ static int dai_comp_trigger_internal(struct dai_data *dd, struct comp_dev *dev, * as soon as possible. */ #if CONFIG_COMP_DAI_STOP_TRIGGER_ORDER_REVERSE - ret = dma_stop(dd->chan->dma->z_dev, dd->chan->index); + ret = sof_dma_stop(dd->dma, dd->chan_index); dai_trigger_op(dd->dai, cmd, dev->direction); #else dai_trigger_op(dd->dai, cmd, dev->direction); - ret = dma_stop(dd->chan->dma->z_dev, dd->chan->index); + ret = sof_dma_stop(dd->dma, dd->chan_index); if (ret) { comp_warn(dev, "dma was stopped earlier"); ret = 0; @@ -1375,11 +1392,11 @@ static int dai_comp_trigger_internal(struct dai_data *dd, struct comp_dev *dev, case COMP_TRIGGER_PAUSE: comp_dbg(dev, "PAUSE"); #if CONFIG_COMP_DAI_STOP_TRIGGER_ORDER_REVERSE - ret = dma_suspend(dd->chan->dma->z_dev, dd->chan->index); + ret = sof_dma_suspend(dd->dma, dd->chan_index); dai_trigger_op(dd->dai, cmd, dev->direction); #else dai_trigger_op(dd->dai, cmd, dev->direction); - ret = dma_suspend(dd->chan->dma->z_dev, dd->chan->index); + ret = sof_dma_suspend(dd->dma, dd->chan_index); #endif break; case COMP_TRIGGER_PRE_START: @@ -1450,7 +1467,7 @@ int dai_common_trigger(struct dai_data *dd, struct comp_dev *dev, int cmd) irq_local_disable(irq_flags); notifier_event(group, NOTIFIER_ID_DAI_TRIGGER, - BIT(cpu_get_id()), NULL, 0); + NOTIFIER_TARGET_CORE_LOCAL, NULL, 0); irq_local_enable(irq_flags); /* return error of last trigger */ @@ -1468,25 +1485,20 @@ static int dai_comp_trigger(struct comp_dev *dev, int cmd) return dai_common_trigger(dd, dev, cmd); } -/* get status from dma and check for xrun */ +/** + * Get status from the DMA driver. + * + * After status call, a check for xrun condition is done and + * depending on configuration, a xrun report is optionally sent. + * See also xrun reporting done in dai_report_reload_xrun(). + */ static int dai_get_status(struct comp_dev *dev, struct dai_data *dd, struct dma_status *stat) { - int ret = dma_get_status(dd->chan->dma->z_dev, dd->chan->index, stat); + int ret = sof_dma_get_status(dd->dma, dd->chan_index, stat); #if CONFIG_XRUN_NOTIFICATIONS_ENABLE if (ret == -EPIPE && !dd->xrun_notification_sent) { - struct ipc_msg *notify = ipc_notification_pool_get(IPC4_RESOURCE_EVENT_SIZE); - - if (notify) { - if (dev->direction == SOF_IPC_STREAM_PLAYBACK) - copier_gateway_underrun_notif_msg_init(notify, - dev->pipeline->pipeline_id); - else - copier_gateway_overrun_notif_msg_init(notify, - dev->pipeline->pipeline_id); - - ipc_msg_send(notify, notify->tx_data, false); - dd->xrun_notification_sent = true; - } + dd->xrun_notification_sent = send_copier_gateway_xrun_notif_msg + (dev->pipeline->pipeline_id, dev->direction); } else if (!ret) { dd->xrun_notification_sent = false; } @@ -1494,8 +1506,13 @@ static int dai_get_status(struct comp_dev *dev, struct dai_data *dd, struct dma_ return ret; } -/* report xrun occurrence */ -static void dai_report_xrun(struct dai_data *dd, struct comp_dev *dev, uint32_t bytes) +/** + * Report xrun occurrence after DAI DMA driver reports + * an error for a reload attempt of 'bytes' of data. + * + * See also xrun detection done in dai_get_status(). + */ +static void dai_report_reload_xrun(struct dai_data *dd, struct comp_dev *dev, uint32_t bytes) { if (dev->direction == SOF_IPC_STREAM_PLAYBACK) { comp_err(dev, "underrun due to no data available"); @@ -1582,9 +1599,9 @@ int dai_zephyr_multi_endpoint_copy(struct dai_data **dd, struct comp_dev *dev, #endif for (i = 0; i < num_endpoints; i++) { - ret = dma_reload(dd[i]->chan->dma->z_dev, dd[i]->chan->index, 0, 0, 0); + ret = sof_dma_reload(dd[i]->dma, dd[i]->chan_index, 0); if (ret < 0) { - dai_report_xrun(dd[i], dev, 0); + dai_report_reload_xrun(dd[i], dev, 0); return ret; } } @@ -1608,12 +1625,12 @@ int dai_zephyr_multi_endpoint_copy(struct dai_data **dd, struct comp_dev *dev, status = dai_dma_multi_endpoint_cb(dd[i], dev, frames, multi_endpoint_buffer); if (status == SOF_DMA_CB_STATUS_END) - dma_stop(dd[i]->chan->dma->z_dev, dd[i]->chan->index); + sof_dma_stop(dd[i]->dma, dd[i]->chan_index); copy_bytes = frames * audio_stream_frame_bytes(&dd[i]->dma_buffer->stream); - ret = dma_reload(dd[i]->chan->dma->z_dev, dd[i]->chan->index, 0, 0, copy_bytes); + ret = sof_dma_reload(dd[i]->dma, dd[i]->chan_index, copy_bytes); if (ret < 0) { - dai_report_xrun(dd[i], dev, copy_bytes); + dai_report_reload_xrun(dd[i], dev, copy_bytes); return ret; } @@ -1800,7 +1817,7 @@ int dai_common_copy(struct dai_data *dd, struct comp_dev *dev, pcm_converter_fun comp_warn(dev, "nothing to copy, src_frames: %u, sink_frames: %u", src_frames, sink_frames); #endif - dma_reload(dd->chan->dma->z_dev, dd->chan->index, 0, 0, 0); + sof_dma_reload(dd->dma, dd->chan_index, 0); return 0; } @@ -1810,11 +1827,11 @@ int dai_common_copy(struct dai_data *dd, struct comp_dev *dev, pcm_converter_fun comp_warn(dev, "dai trigger copy failed"); if (dai_dma_cb(dd, dev, copy_bytes, converter) == SOF_DMA_CB_STATUS_END) - dma_stop(dd->chan->dma->z_dev, dd->chan->index); + sof_dma_stop(dd->dma, dd->chan_index); - ret = dma_reload(dd->chan->dma->z_dev, dd->chan->index, 0, 0, copy_bytes); + ret = sof_dma_reload(dd->dma, dd->chan_index, copy_bytes); if (ret < 0) { - dai_report_xrun(dd, dev, copy_bytes); + dai_report_reload_xrun(dd, dev, copy_bytes); return ret; } @@ -1850,7 +1867,7 @@ int dai_common_ts_config_op(struct dai_data *dd, struct comp_dev *dev) struct dai_ts_cfg *cfg = &dd->ts_config; comp_dbg(dev, "dai_ts_config()"); - if (!dd->chan) { + if (dd->chan_index < 0) { comp_err(dev, "No DMA channel information"); return -EINVAL; } @@ -1873,7 +1890,7 @@ int dai_common_ts_config_op(struct dai_data *dd, struct comp_dev *dev) cfg->direction = dai->direction; cfg->index = dd->dai->index; cfg->dma_id = dd->dma->plat_data.id; - cfg->dma_chan_index = dd->chan->index; + cfg->dma_chan_index = dd->chan_index; cfg->dma_chan_count = dd->dma->plat_data.channels; return dai_ts_config(dd->dai->dev, cfg); @@ -1984,6 +2001,8 @@ int dai_zephyr_unbind(struct dai_data *dd, struct comp_dev *dev, struct bind_inf } #endif /* CONFIG_IPC_MAJOR_4 */ +DECLARE_TR_CTX(dai_comp_tr, SOF_UUID(dai_uuid), LOG_LEVEL_INFO); + static const struct comp_driver comp_dai = { .type = SOF_COMP_DAI, .uid = SOF_RT_UUID(dai_uuid), diff --git a/src/audio/data_blob.c b/src/audio/data_blob.c index 42d612fb3104..399244106f95 100644 --- a/src/audio/data_blob.c +++ b/src/audio/data_blob.c @@ -110,7 +110,7 @@ bool comp_is_new_data_blob_available(struct comp_data_blob_handler { assert(blob_handler); - comp_dbg(blob_handler->dev, "comp_is_new_data_blob_available()"); + comp_dbg(blob_handler->dev, "entry"); /* New data blob is available when new data blob is allocated (data_new * is not NULL), and the component has received all required chunks of data @@ -185,25 +185,25 @@ int comp_data_blob_set(struct comp_data_blob_handler *blob_handler, #if CONFIG_IPC_MAJOR_3 if (cdata->cmd != SOF_CTRL_CMD_BINARY) { - comp_err(blob_handler->dev, "comp_data_blob_set_cmd(), illegal control command"); + comp_err(blob_handler->dev, "illegal control command"); return -EINVAL; } #endif - comp_dbg(blob_handler->dev, "comp_data_blob_set_cmd() pos = %d, fragment size = %zu", + comp_dbg(blob_handler->dev, "pos = %d, fragment size = %zu", pos, fragment_size); /* Check that there is no work-in-progress previous request */ if (blob_handler->data_new && (pos == MODULE_CFG_FRAGMENT_FIRST || pos == MODULE_CFG_FRAGMENT_SINGLE)) { - comp_err(blob_handler->dev, "comp_data_blob_set_cmd(), busy with previous request"); + comp_err(blob_handler->dev, "busy with previous request"); return -EBUSY; } /* In single blob mode the component can not be reconfigured if the component is active. */ if (blob_handler->single_blob && blob_handler->dev->state == COMP_STATE_ACTIVE) { - comp_err(blob_handler->dev, "comp_data_blob_set_cmd(), on the fly updates forbidden in single blob mode"); + comp_err(blob_handler->dev, "on the fly updates forbidden in single blob mode"); return -EBUSY; } @@ -441,13 +441,13 @@ int comp_data_blob_set_cmd(struct comp_data_blob_handler *blob_handler, assert(blob_handler); - comp_dbg(blob_handler->dev, "comp_data_blob_set_cmd() msg_index = %d, num_elems = %d, remaining = %d ", + comp_dbg(blob_handler->dev, "msg_index = %d, num_elems = %d, remaining = %d ", cdata->msg_index, cdata->num_elems, cdata->elems_remaining); /* Check that there is no work-in-progress previous request */ if (blob_handler->data_new && cdata->msg_index == 0) { - comp_err(blob_handler->dev, "comp_data_blob_set_cmd(), busy with previous request"); + comp_err(blob_handler->dev, "busy with previous request"); return -EBUSY; } @@ -456,7 +456,7 @@ int comp_data_blob_set_cmd(struct comp_data_blob_handler *blob_handler, */ if (blob_handler->single_blob && blob_handler->dev->state == COMP_STATE_ACTIVE) { - comp_err(blob_handler->dev, "comp_data_blob_set_cmd(), on the fly updates forbidden in single blob mode"); + comp_err(blob_handler->dev, "on the fly updates forbidden in single blob mode"); return -EBUSY; } @@ -581,7 +581,7 @@ int comp_data_blob_get_cmd(struct comp_data_blob_handler *blob_handler, return -EINVAL; } - comp_dbg(blob_handler->dev, "comp_data_blob_get_cmd() msg_index = %d, num_elems = %d, remaining = %d ", + comp_dbg(blob_handler->dev, "msg_index = %d, num_elems = %d, remaining = %d ", cdata->msg_index, cdata->num_elems, cdata->elems_remaining); @@ -590,7 +590,7 @@ int comp_data_blob_get_cmd(struct comp_data_blob_handler *blob_handler, /* reset data_pos variable in case of copying first element */ if (!cdata->msg_index) { blob_handler->data_pos = 0; - comp_dbg(blob_handler->dev, "comp_data_blob_get_cmd() model data_size = 0x%x", + comp_dbg(blob_handler->dev, "model data_size = 0x%x", blob_handler->data_size); } @@ -639,7 +639,7 @@ comp_data_blob_handler_new_ext(struct comp_dev *dev, bool single_blob, { struct comp_data_blob_handler *handler; - comp_dbg(dev, "comp_data_blob_handler_new_ext()"); + comp_dbg(dev, "entry"); handler = rzalloc(SOF_MEM_FLAG_USER, sizeof(struct comp_data_blob_handler)); diff --git a/src/audio/dcblock/README.md b/src/audio/dcblock/README.md new file mode 100644 index 000000000000..4cbae3402540 --- /dev/null +++ b/src/audio/dcblock/README.md @@ -0,0 +1,23 @@ +# DC Block Architecture + +This directory contains the DC Blocking filter. + +## Overview + +The DC Blocker removes the DC offset (0 Hz component) from an audio signal, avoiding speaker damage and maximizing dynamic range. + +## Architecture Diagram + +```mermaid +graph LR + In[Audio Input] --> IIR[High-pass IIR Filter] + IIR --> Out[DC-Free Audio Output] +``` + +## Configuration and Scripts + +- **Kconfig**: Enables the DC Blocking Filter component (`COMP_DCBLOCK`) which filters out DC offsets often from a microphone's output. Includes selectable HIFI optimization levels (Max, HIFI4, HIFI3, Generic). +- **CMakeLists.txt**: Manages local build sources including generic and HIFI optimized versions (`dcblock_generic.c`, `dcblock_hifi3.c`, etc.) and determines the valid IPC abstraction (`dcblock_ipc3.c` vs `dcblock_ipc4.c`). Supports loadable extension generation via `llext`. +- **dcblock.toml**: Topology parameters for the DCBlock module. Defines UUID, pins, and memory limits for IPC integration. +- **Topology (.conf)**: Derived from `tools/topology/topology2/include/components/dcblock.conf`, it defines the `dcblock` widget object for topology generation. It defaults to type `effect` with UUID `af:ef:09:b8:81:56:b1:42:9e:d6:04:bb:01:2d:d3:84` and enforces input/output pins configuration. +- **MATLAB Tuning (`tune/`)**: The included `.m` scripts (e.g., `sof_example_dcblock.m`) automatically determine the optimal R coefficients to achieve various high-pass cutoff frequencies (-3dB point) across supported sample rates (like 16kHz and 48kHz). These coefficients are then packed into configuration blobs. diff --git a/src/audio/dcblock/dcblock.c b/src/audio/dcblock/dcblock.c index d266f357554f..5f0935a2bc29 100644 --- a/src/audio/dcblock/dcblock.c +++ b/src/audio/dcblock/dcblock.c @@ -36,8 +36,6 @@ LOG_MODULE_REGISTER(dcblock, CONFIG_SOF_LOG_LEVEL); SOF_DEFINE_REG_UUID(dcblock); -DECLARE_TR_CTX(dcblock_tr, SOF_UUID(dcblock_uuid), LOG_LEVEL_INFO); - /** * \brief Sets the DC Blocking filter in pass through mode. * The frequency response of a DCB filter is: @@ -48,7 +46,7 @@ static void dcblock_set_passthrough(struct processing_module *mod) { struct comp_data *cd = module_get_private_data(mod); - comp_info(mod->dev, "dcblock_set_passthrough()"); + comp_info(mod->dev, "entry"); int i; for (i = 0; i < PLATFORM_MAX_CHANNELS; i++) @@ -82,12 +80,9 @@ static int dcblock_init(struct processing_module *mod) { struct module_data *md = &mod->priv; struct comp_dev *dev = mod->dev; - struct module_config *ipc_dcblock = &md->cfg; struct comp_data *cd; - size_t bs = ipc_dcblock->size; - int ret; - comp_info(dev, "dcblock_init()"); + comp_info(dev, "entry"); cd = rzalloc(SOF_MEM_FLAG_USER, sizeof(*cd)); if (!cd) @@ -100,24 +95,11 @@ static int dcblock_init(struct processing_module *mod) cd->model_handler = comp_data_blob_handler_new(dev); if (!cd->model_handler) { comp_err(dev, "comp_data_blob_handler_new() failed."); - ret = -ENOMEM; - goto err_cd; - } - - ret = comp_init_data_blob(cd->model_handler, bs, ipc_dcblock->data); - if (ret < 0) { - comp_err(dev, "comp_init_data_blob() failed with error: %d", ret); - goto err_model_cd; + rfree(cd); + return -ENOMEM; } return 0; - -err_model_cd: - comp_data_blob_handler_free(cd->model_handler); - -err_cd: - rfree(cd); - return ret; } /** @@ -128,7 +110,7 @@ static int dcblock_free(struct processing_module *mod) { struct comp_data *cd = module_get_private_data(mod); - comp_info(mod->dev, "dcblock_free()"); + comp_info(mod->dev, "entry"); comp_data_blob_handler_free(cd->model_handler); rfree(cd); return 0; @@ -172,7 +154,7 @@ static int dcblock_process(struct processing_module *mod, struct audio_stream *sink = output_buffers[0].data; uint32_t frames = input_buffers[0].size; - comp_dbg(mod->dev, "dcblock_process()"); + comp_dbg(mod->dev, "entry"); cd->dcblock_func(cd, source, sink, frames); @@ -192,8 +174,9 @@ static int dcblock_prepare(struct processing_module *mod, struct comp_data *cd = module_get_private_data(mod); struct comp_buffer *sourceb, *sinkb; struct comp_dev *dev = mod->dev; + size_t data_size; - comp_info(dev, "dcblock_prepare()"); + comp_info(dev, "entry"); /* DC Filter component will only ever have one source and sink buffer */ sourceb = comp_dev_get_first_data_producer(dev); @@ -214,15 +197,15 @@ static int dcblock_prepare(struct processing_module *mod, dcblock_init_state(cd); cd->dcblock_func = dcblock_find_func(cd->source_format); if (!cd->dcblock_func) { - comp_err(dev, "dcblock_prepare(), No processing function matching frames format"); + comp_err(dev, "No processing function matching frames format"); return -EINVAL; } - comp_info(mod->dev, "dcblock_prepare(), source_format=%d, sink_format=%d", + comp_info(mod->dev, "source_format=%d, sink_format=%d", cd->source_format, cd->sink_format); - cd->config = comp_get_data_blob(cd->model_handler, NULL, NULL); - if (cd->config) + cd->config = comp_get_data_blob(cd->model_handler, &data_size, NULL); + if (cd->config && data_size > 0) dcblock_copy_coefficients(mod); else dcblock_set_passthrough(mod); @@ -239,7 +222,7 @@ static int dcblock_reset(struct processing_module *mod) { struct comp_data *cd = module_get_private_data(mod); - comp_info(mod->dev, "dcblock_reset()"); + comp_info(mod->dev, "entry"); dcblock_init_state(cd); @@ -272,6 +255,7 @@ SOF_LLEXT_BUILDINFO; #else +DECLARE_TR_CTX(dcblock_tr, SOF_UUID(dcblock_uuid), LOG_LEVEL_INFO); DECLARE_MODULE_ADAPTER(dcblock_interface, dcblock_uuid, dcblock_tr); SOF_MODULE_INIT(dcblock, sys_comp_module_dcblock_interface_init); diff --git a/src/audio/dcblock/dcblock_ipc4.c b/src/audio/dcblock/dcblock_ipc4.c index d8030126a270..c375d11ec042 100644 --- a/src/audio/dcblock/dcblock_ipc4.c +++ b/src/audio/dcblock/dcblock_ipc4.c @@ -30,7 +30,7 @@ int dcblock_get_ipc_config(struct processing_module *mod, struct sof_ipc_ctrl_data *cdata = (struct sof_ipc_ctrl_data *)fragment; struct comp_data *cd = module_get_private_data(mod); - comp_info(mod->dev, "dcblock_get_ipc_config()"); + comp_info(mod->dev, "entry"); return comp_data_blob_get_cmd(cd->model_handler, cdata, fragment_size); } @@ -44,7 +44,7 @@ int dcblock_set_ipc_config(struct processing_module *mod, { struct comp_data *cd = module_get_private_data(mod); - comp_info(mod->dev, "dcblock_set_ipc_config()"); + comp_info(mod->dev, "entry"); return comp_data_blob_set(cd->model_handler, pos, data_offset_size, fragment, fragment_size); @@ -56,7 +56,7 @@ void dcblock_params(struct processing_module *mod) struct comp_buffer *sinkb, *sourceb; struct comp_dev *dev = mod->dev; - comp_dbg(dev, "dcblock_params()"); + comp_dbg(dev, "entry"); ipc4_base_module_cfg_to_stream_params(&mod->priv.cfg.base_cfg, params); component_set_nearest_period_frames(dev, params->rate); diff --git a/src/audio/drc/README.md b/src/audio/drc/README.md new file mode 100644 index 000000000000..a0a1eb8dd80a --- /dev/null +++ b/src/audio/drc/README.md @@ -0,0 +1,26 @@ +# Dynamic Range Compressor (DRC) Architecture + +This directory contains the DRC component. + +## Overview + +The Dynamic Range Compressor reduces the volume of loud sounds or amplifies quiet sounds by narrowing or "compressing" an audio signal's dynamic range. + +## Architecture Diagram + +```mermaid +graph TD + In[Audio Input] --> Det[Envelope Detector] + In --> Gain[Gain Element] + Det --> Calc[Gain Calculation] + Calc --> Gain + Gain --> Out[Audio Output] +``` + +## Configuration and Scripts + +- **Kconfig**: Enables the Dynamic Range Compressor component (`COMP_DRC`). It relies on various math features like `CORDIC_FIXED`, `MATH_LUT_SINE_FIXED`, and `MATH_EXP`. The maximum number of pre-delay frames is tunable via `DRC_MAX_PRE_DELAY_FRAMES` (defaults to 512). +- **CMakeLists.txt**: Manages local base sources and generic/HIFI specific files such as `drc_hifi4.c` and `drc_math_hifi3.c`. Adds logging capabilities if compiled in (`drc_log.c`). +- **drc.toml**: Topology parameters for the DRC module definition, exposing UUID and standard buffer sizes and processing capabilities. +- **Topology (.conf)**: `tools/topology/topology2/include/components/drc.conf` configures the `drc` widget object, providing switch controls by binding a mixer control to switch get/put handlers (`259`). Defaults to type `effect` with UUID `da:e4:6e:b3:6f:00:f9:47:a0:6d:fe:cb:e2:d8:b6:ce`. +- **MATLAB Tuning (`tune/`)**: Contains `.m` scripts (e.g., `sof_example_drc.m`) capable of tuning compressor parameters (threshold, knee, ratio, attack, release) and visualizing their gain reaction curves. The outputs are exported as `.conf` configurations, M4 macros, and ALSA `alsactl` payload blobs for preset instantiation defaults (e.g., speaker or DMIC presets). diff --git a/src/audio/drc/drc.c b/src/audio/drc/drc.c index 6219f54c8842..71d93496b766 100644 --- a/src/audio/drc/drc.c +++ b/src/audio/drc/drc.c @@ -20,7 +20,6 @@ #include <ipc/stream.h> #include <ipc/topology.h> #include <module/module/llext.h> -#include <rtos/alloc.h> #include <rtos/init.h> #include <rtos/panic.h> #include <rtos/string.h> @@ -43,11 +42,11 @@ extern const struct sof_uuid drc_uuid; extern struct tr_ctx drc_tr; /* Called from drc_setup() from drc_process(), so cannot be __cold */ -void drc_reset_state(struct drc_state *state) +void drc_reset_state(struct processing_module *mod, struct drc_state *state) { int i; - rfree(state->pre_delay_buffers[0]); + mod_free(mod, state->pre_delay_buffers[0]); for (i = 0; i < PLATFORM_MAX_CHANNELS; ++i) { state->pre_delay_buffers[i] = NULL; } @@ -67,7 +66,8 @@ void drc_reset_state(struct drc_state *state) state->max_attack_compression_diff_db = INT32_MIN; } -int drc_init_pre_delay_buffers(struct drc_state *state, +int drc_init_pre_delay_buffers(struct processing_module *mod, + struct drc_state *state, size_t sample_bytes, int channels) { @@ -76,7 +76,7 @@ int drc_init_pre_delay_buffers(struct drc_state *state, int i; /* Allocate pre-delay (lookahead) buffers */ - state->pre_delay_buffers[0] = rballoc(SOF_MEM_FLAG_USER, bytes_total); + state->pre_delay_buffers[0] = mod_balloc(mod, bytes_total); if (!state->pre_delay_buffers[0]) return -ENOMEM; @@ -121,16 +121,17 @@ int drc_set_pre_delay_time(struct drc_state *state, } /* Called from drc_process(), so cannot be __cold */ -static int drc_setup(struct drc_comp_data *cd, uint16_t channels, uint32_t rate) +static int drc_setup(struct processing_module *mod, uint16_t channels, uint32_t rate) { + struct drc_comp_data *cd = module_get_private_data(mod); uint32_t sample_bytes = get_sample_bytes(cd->source_format); int ret; /* Reset any previous state */ - drc_reset_state(&cd->state); + drc_reset_state(mod, &cd->state); /* Allocate pre-delay buffers */ - ret = drc_init_pre_delay_buffers(&cd->state, (size_t)sample_bytes, (int)channels); + ret = drc_init_pre_delay_buffers(mod, &cd->state, (size_t)sample_bytes, (int)channels); if (ret < 0) return ret; @@ -146,46 +147,28 @@ __cold static int drc_init(struct processing_module *mod) { struct module_data *md = &mod->priv; struct comp_dev *dev = mod->dev; - struct module_config *cfg = &md->cfg; struct drc_comp_data *cd; - size_t bs = cfg->size; int ret; assert_can_be_cold(); - comp_info(dev, "drc_init()"); - - /* Check first before proceeding with dev and cd that coefficients - * blob size is sane. - */ - if (bs > SOF_DRC_MAX_SIZE) { - comp_err(dev, "drc_init(), error: configuration blob size = %u > %d", - bs, SOF_DRC_MAX_SIZE); - return -EINVAL; - } + comp_info(dev, "entry"); - cd = rzalloc(SOF_MEM_FLAG_USER, sizeof(*cd)); + cd = mod_zalloc(mod, sizeof(*cd)); if (!cd) return -ENOMEM; md->private = cd; /* Handler for configuration data */ - cd->model_handler = comp_data_blob_handler_new(dev); + cd->model_handler = mod_data_blob_handler_new(mod); if (!cd->model_handler) { - comp_err(dev, "comp_data_blob_handler_new() failed."); + comp_err(dev, "mod_data_blob_handler_new() failed."); ret = -ENOMEM; goto cd_fail; } - /* Get configuration data and reset DRC state */ - ret = comp_init_data_blob(cd->model_handler, bs, cfg->data); - if (ret < 0) { - comp_err(dev, "comp_init_data_blob() failed."); - goto cd_fail; - } - - drc_reset_state(&cd->state); + drc_reset_state(mod, &cd->state); /* Initialize DRC to enabled. If defined by topology, a control may set * enabled to false before prepare() or during streaming with the switch @@ -196,8 +179,8 @@ __cold static int drc_init(struct processing_module *mod) return 0; cd_fail: - comp_data_blob_handler_free(cd->model_handler); - rfree(cd); + mod_data_blob_handler_free(mod, cd->model_handler); + mod_free(mod, cd); return ret; } @@ -207,8 +190,8 @@ __cold static int drc_free(struct processing_module *mod) assert_can_be_cold(); - comp_data_blob_handler_free(cd->model_handler); - rfree(cd); + mod_data_blob_handler_free(mod, cd->model_handler); + mod_free(mod, cd); return 0; } @@ -222,7 +205,7 @@ __cold static int drc_set_config(struct processing_module *mod, uint32_t param_i assert_can_be_cold(); - comp_dbg(dev, "drc_set_config()"); + comp_dbg(dev, "entry"); #if CONFIG_IPC_MAJOR_4 struct sof_ipc4_control_msg_payload *ctl = (struct sof_ipc4_control_msg_payload *)fragment; @@ -232,7 +215,7 @@ __cold static int drc_set_config(struct processing_module *mod, uint32_t param_i if (ctl->id == SOF_DRC_CTRL_INDEX_ENABLE_SWITCH && ctl->num_elems == SOF_DRC_NUM_ELEMS_ENABLE_SWITCH) { cd->enable_switch = ctl->chanv[0].value; - comp_info(dev, "drc_set_config(), enable_switch = %d", cd->enable_switch); + comp_info(dev, "enable_switch = %d", cd->enable_switch); } else { comp_err(dev, "Illegal switch control id = %d, num_elems = %d", ctl->id, ctl->num_elems); @@ -242,12 +225,12 @@ __cold static int drc_set_config(struct processing_module *mod, uint32_t param_i return 0; case SOF_IPC4_ENUM_CONTROL_PARAM_ID: - comp_err(dev, "drc_set_config(), illegal control."); + comp_err(dev, "illegal control."); return -EINVAL; } #endif - comp_info(dev, "drc_set_config(), bytes control"); + comp_info(dev, "bytes control"); return comp_data_blob_set(cd->model_handler, pos, data_offset_size, fragment, fragment_size); } @@ -261,7 +244,7 @@ __cold static int drc_get_config(struct processing_module *mod, assert_can_be_cold(); - comp_info(mod->dev, "drc_get_config()"); + comp_info(mod->dev, "entry"); return comp_data_blob_get_cmd(cd->model_handler, cdata, fragment_size); } @@ -279,12 +262,12 @@ static int drc_process(struct processing_module *mod, int frames = input_buffers[0].size; int ret; - comp_dbg(dev, "drc_process()"); + comp_dbg(dev, "entry"); /* Check for changed configuration */ if (comp_is_new_data_blob_available(cd->model_handler)) { cd->config = comp_get_data_blob(cd->model_handler, NULL, NULL); - ret = drc_setup(cd, audio_stream_get_channels(source), + ret = drc_setup(mod, audio_stream_get_channels(source), audio_stream_get_rate(source)); if (ret < 0) { comp_err(dev, "drc_copy(), failed DRC setup"); @@ -321,7 +304,7 @@ static void drc_params(struct processing_module *mod) struct comp_buffer *sinkb, *sourceb; struct comp_dev *dev = mod->dev; - comp_dbg(dev, "drc_params()"); + comp_dbg(dev, "entry"); ipc4_base_module_cfg_to_stream_params(&mod->priv.cfg.base_cfg, params); component_set_nearest_period_frames(dev, params->rate); @@ -343,11 +326,12 @@ static int drc_prepare(struct processing_module *mod, struct drc_comp_data *cd = module_get_private_data(mod); struct comp_buffer *sourceb, *sinkb; struct comp_dev *dev = mod->dev; + size_t data_size; int channels; int rate; int ret; - comp_info(dev, "drc_prepare()"); + comp_info(dev, "entry"); /* DRC component will only ever have 1 source and 1 sink buffer */ sourceb = comp_dev_get_first_data_producer(dev); @@ -367,18 +351,18 @@ static int drc_prepare(struct processing_module *mod, rate = audio_stream_get_rate(&sinkb->stream); /* Initialize DRC */ - comp_info(dev, "drc_prepare(), source_format=%d", cd->source_format); - cd->config = comp_get_data_blob(cd->model_handler, NULL, NULL); - if (cd->config) { - ret = drc_setup(cd, channels, rate); + comp_info(dev, "source_format=%d", cd->source_format); + cd->config = comp_get_data_blob(cd->model_handler, &data_size, NULL); + if (cd->config && data_size > 0) { + ret = drc_setup(mod, channels, rate); if (ret < 0) { - comp_err(dev, "drc_prepare() error: drc_setup failed."); + comp_err(dev, "error: drc_setup failed."); return ret; } cd->drc_func = drc_find_proc_func(cd->source_format); if (!cd->drc_func) { - comp_err(dev, "drc_prepare(), No proc func"); + comp_err(dev, "No proc func"); return -EINVAL; } @@ -395,7 +379,7 @@ static int drc_prepare(struct processing_module *mod, cd->drc_func = drc_default_pass; } - comp_info(dev, "drc_prepare(), DRC is configured."); + comp_info(dev, "DRC is configured."); return 0; } @@ -403,7 +387,7 @@ static int drc_reset(struct processing_module *mod) { struct drc_comp_data *cd = module_get_private_data(mod); - drc_reset_state(&cd->state); + drc_reset_state(mod, &cd->state); return 0; } diff --git a/src/audio/drc/drc_algorithm.h b/src/audio/drc/drc_algorithm.h index c0a942b09622..8d9d759eb3a8 100644 --- a/src/audio/drc/drc_algorithm.h +++ b/src/audio/drc/drc_algorithm.h @@ -14,10 +14,11 @@ #include "drc.h" /* drc reset function */ -void drc_reset_state(struct drc_state *state); +void drc_reset_state(struct processing_module *mod, struct drc_state *state); /* drc init functions */ -int drc_init_pre_delay_buffers(struct drc_state *state, +int drc_init_pre_delay_buffers(struct processing_module *mod, + struct drc_state *state, size_t sample_bytes, int channels); int drc_set_pre_delay_time(struct drc_state *state, diff --git a/src/audio/drc/drc_log.c b/src/audio/drc/drc_log.c index 63c88e65101a..d2e0f1efe7ea 100644 --- a/src/audio/drc/drc_log.c +++ b/src/audio/drc/drc_log.c @@ -10,6 +10,7 @@ SOF_DEFINE_REG_UUID(drc); LOG_MODULE_REGISTER(drc, CONFIG_SOF_LOG_LEVEL); DECLARE_TR_CTX(drc_tr, SOF_UUID(drc_uuid), LOG_LEVEL_INFO); -EXPORT_SYMBOL(drc_tr); -EXPORT_SYMBOL(drc_uuid); EXPORT_SYMBOL(log_const_drc); +#ifdef CONFIG_LOG_RUNTIME_FILTERING +EXPORT_SYMBOL(log_dynamic_drc); +#endif diff --git a/src/audio/eq_fir/README.md b/src/audio/eq_fir/README.md new file mode 100644 index 000000000000..24be526f6189 --- /dev/null +++ b/src/audio/eq_fir/README.md @@ -0,0 +1,24 @@ +# FIR Equalizer Architecture + +This directory contains the Finite Impulse Response (FIR) EQ component. + +## Overview + +FIR equalizers implement linear-phase (or minimal-phase) audio frequency shaping over a specified set of taps. + +## Architecture Diagram + +```mermaid +graph LR + In[Input Frame] --> FIR[FIR Filter Engine] + Coeffs[(Filter Coefficients)] --> FIR + FIR --> Out[Output Frame] +``` + +## Configuration and Scripts + +- **Kconfig**: Enables the FIR component (`COMP_FIR`), which automatically imports `MATH_FIR` and `COMP_BLOB`. Relies on compiler capabilities to leverage DSP MAC instructions for optimal performance. +- **CMakeLists.txt**: Integrates generic and architecture-specific (`eq_fir_hifi2ep.c`, `eq_fir_hifi3.c`) files into the build, picking the correct IPC wrapper (`ipc3` or `ipc4`), and supports `llext`. +- **eq_fir.toml**: Topology parameters for the EQFIR module, defining UUIDs, memory parameters (like 4096 bytes limits) and pin layouts. +- **Topology (.conf)**: Constrained by `tools/topology/topology2/include/components/eqfir.conf`, assigning it widget type `effect` and UUID `e7:0c:a9:43:a5:f3:df:41:ac:06:ba:98:65:1a:e6:a3`. +- **MATLAB Tuning (`tune/`)**: `sof_example_fir_eq.m` utilizes the Parks-McClellan (or related) algorithms to model loudness or mid-boost curves, iterating length variables to determine optimal FIR phase behaviors. It outputs quantized binary arrays containing the calculated taps necessary for topology population. diff --git a/src/audio/eq_fir/eq_fir.c b/src/audio/eq_fir/eq_fir.c index ed946157ed4c..793a2294c647 100644 --- a/src/audio/eq_fir/eq_fir.c +++ b/src/audio/eq_fir/eq_fir.c @@ -15,7 +15,6 @@ #include <sof/common.h> #include <rtos/panic.h> #include <sof/ipc/msg.h> -#include <rtos/alloc.h> #include <rtos/init.h> #include <sof/lib/uuid.h> #include <sof/list.h> @@ -41,8 +40,6 @@ LOG_MODULE_REGISTER(eq_fir, CONFIG_SOF_LOG_LEVEL); SOF_DEFINE_REG_UUID(eq_fir); -DECLARE_TR_CTX(eq_fir_tr, SOF_UUID(eq_fir_uuid), LOG_LEVEL_INFO); - /* Pass-through functions to replace FIR core while not configured for * response. */ @@ -58,15 +55,16 @@ static void eq_fir_passthrough(struct fir_state_32x16 fir[], audio_stream_copy(source, 0, sink, 0, frames * audio_stream_get_channels(source)); } -static void eq_fir_free_delaylines(struct comp_data *cd) +static void eq_fir_free_delaylines(struct processing_module *mod) { + struct comp_data *cd = module_get_private_data(mod); struct fir_state_32x16 *fir = cd->fir; int i = 0; /* Free the common buffer for all EQs and point then * each FIR channel delay line to NULL. */ - rfree(cd->fir_delay); + mod_free(mod, cd->fir_delay); cd->fir_delay = NULL; cd->fir_delay_size = 0; for (i = 0; i < PLATFORM_MAX_CHANNELS; i++) @@ -106,11 +104,11 @@ static int eq_fir_init_coef(struct comp_dev *dev, struct sof_eq_fir_config *conf if (nch > PLATFORM_MAX_CHANNELS || config->channels_in_config > PLATFORM_MAX_CHANNELS || !config->channels_in_config) { - comp_err(dev, "eq_fir_init_coef(), invalid channels count"); + comp_err(dev, "invalid channels count"); return -EINVAL; } if (config->number_of_responses > SOF_EQ_FIR_MAX_RESPONSES) { - comp_err(dev, "eq_fir_init_coef(), # of resp exceeds max"); + comp_err(dev, "# of resp exceeds max"); return -EINVAL; } @@ -145,14 +143,14 @@ static int eq_fir_init_coef(struct comp_dev *dev, struct sof_eq_fir_config *conf * next channel response. */ if (fir) { - comp_info(dev, "eq_fir_init_coef(), ch %d is set to bypass", i); + comp_info(dev, "ch %d is set to bypass", i); fir_reset(&fir[i]); } continue; } if (resp >= config->number_of_responses) { - comp_err(dev, "eq_fir_init_coef(), requested response %d exceeds what has been defined", + comp_err(dev, "requested response %d exceeds what has been defined", resp); return -EINVAL; } @@ -163,7 +161,7 @@ static int eq_fir_init_coef(struct comp_dev *dev, struct sof_eq_fir_config *conf if (s > 0) { size_sum += s; } else { - comp_info(dev, "eq_fir_init_coef(), FIR length %d is invalid", eq->length); + comp_info(dev, "FIR length %d is invalid", eq->length); return -EINVAL; } @@ -177,7 +175,7 @@ static int eq_fir_init_coef(struct comp_dev *dev, struct sof_eq_fir_config *conf if (fir) { fir_init_coef(&fir[i], eq); - comp_info(dev, "eq_fir_init_coef(), ch %d is set to response = %d", + comp_info(dev, "ch %d is set to response = %d", i, resp); } } @@ -198,12 +196,14 @@ static void eq_fir_init_delay(struct fir_state_32x16 *fir, } } -static int eq_fir_setup(struct comp_dev *dev, struct comp_data *cd, int nch) +static int eq_fir_setup(struct processing_module *mod, int nch) { + struct comp_data *cd = module_get_private_data(mod); + struct comp_dev *dev = mod->dev; int delay_size; /* Free existing FIR channels data if it was allocated */ - eq_fir_free_delaylines(cd); + eq_fir_free_delaylines(mod); /* Update number of channels */ cd->nch = nch; @@ -220,9 +220,9 @@ static int eq_fir_setup(struct comp_dev *dev, struct comp_data *cd, int nch) return 0; /* Allocate all FIR channels data in a big chunk and clear it */ - cd->fir_delay = rballoc(SOF_MEM_FLAG_USER, delay_size); + cd->fir_delay = mod_balloc(mod, delay_size); if (!cd->fir_delay) { - comp_err(dev, "eq_fir_setup(), delay allocation failed for size %d", delay_size); + comp_err(dev, "delay allocation failed for size %d", delay_size); return -ENOMEM; } @@ -247,24 +247,12 @@ static int eq_fir_init(struct processing_module *mod) { struct module_data *md = &mod->priv; struct comp_dev *dev = mod->dev; - struct module_config *cfg = &md->cfg; struct comp_data *cd = NULL; - size_t bs = cfg->size; int i; - int ret; - - comp_info(dev, "eq_fir_init()"); - /* Check first before proceeding with dev and cd that coefficients - * blob size is sane. - */ - if (bs > SOF_EQ_FIR_MAX_SIZE) { - comp_err(dev, "coefficients blob size = %zu > SOF_EQ_FIR_MAX_SIZE", - bs); - return -EINVAL; - } + comp_info(dev, "entry"); - cd = rzalloc(SOF_MEM_FLAG_USER, sizeof(*cd)); + cd = mod_zalloc(mod, sizeof(*cd)); if (!cd) return -ENOMEM; @@ -274,46 +262,31 @@ static int eq_fir_init(struct processing_module *mod) cd->nch = -1; /* component model data handler */ - cd->model_handler = comp_data_blob_handler_new(dev); + cd->model_handler = mod_data_blob_handler_new(mod); if (!cd->model_handler) { - comp_err(dev, "comp_data_blob_handler_new() failed."); - ret = -ENOMEM; - goto err; + comp_err(dev, "mod_data_blob_handler_new() failed."); + mod_free(mod, cd); + return -ENOMEM; } md->private = cd; - /* Allocate and make a copy of the coefficients blob and reset FIR. If - * the EQ is configured later in run-time the size is zero. - */ - ret = comp_init_data_blob(cd->model_handler, bs, cfg->init_data); - if (ret < 0) { - comp_err(dev, "comp_init_data_blob() failed."); - goto err_init; - } - for (i = 0; i < PLATFORM_MAX_CHANNELS; i++) fir_reset(&cd->fir[i]); return 0; - -err_init: - comp_data_blob_handler_free(cd->model_handler); -err: - rfree(cd); - return ret; } static int eq_fir_free(struct processing_module *mod) { struct comp_data *cd = module_get_private_data(mod); - comp_dbg(mod->dev, "eq_fir_free()"); + comp_dbg(mod->dev, "entry"); - eq_fir_free_delaylines(cd); - comp_data_blob_handler_free(cd->model_handler); + eq_fir_free_delaylines(mod); + mod_data_blob_handler_free(mod, cd->model_handler); - rfree(cd); + mod_free(mod, cd); return 0; } @@ -325,7 +298,7 @@ static int eq_fir_get_config(struct processing_module *mod, struct sof_ipc_ctrl_data *cdata = (struct sof_ipc_ctrl_data *)fragment; struct comp_data *cd = module_get_private_data(mod); - comp_info(mod->dev, "eq_fir_get_config()"); + comp_info(mod->dev, "entry"); return comp_data_blob_get_cmd(cd->model_handler, cdata, fragment_size); } @@ -337,7 +310,7 @@ static int eq_fir_set_config(struct processing_module *mod, uint32_t config_id, { struct comp_data *cd = module_get_private_data(mod); - comp_info(mod->dev, "eq_fir_set_config()"); + comp_info(mod->dev, "entry"); return comp_data_blob_set(cd->model_handler, pos, data_offset_size, fragment, fragment_size); @@ -355,23 +328,23 @@ static int eq_fir_process(struct processing_module *mod, uint32_t frame_count = input_buffers[0].size; int ret; - comp_dbg(mod->dev, "eq_fir_process()"); + comp_dbg(mod->dev, "entry"); /* Check for changed configuration */ if (comp_is_new_data_blob_available(cd->model_handler)) { cd->config = comp_get_data_blob(cd->model_handler, NULL, NULL); - ret = eq_fir_setup(mod->dev, cd, audio_stream_get_channels(source)); + ret = eq_fir_setup(mod, audio_stream_get_channels(source)); if (ret < 0) { - comp_err(mod->dev, "eq_fir_process(), failed FIR setup"); + comp_err(mod->dev, "failed FIR setup"); return ret; } else if (cd->fir_delay_size) { - comp_dbg(mod->dev, "eq_fir_process(), active"); + comp_dbg(mod->dev, "active"); ret = set_fir_func(mod, audio_stream_get_frm_fmt(source)); if (ret < 0) return ret; } else { cd->eq_fir_func = eq_fir_passthrough; - comp_dbg(mod->dev, "eq_fir_process(), pass-through"); + comp_dbg(mod->dev, "pass-through"); } } @@ -393,14 +366,12 @@ static int eq_fir_process(struct processing_module *mod, return 0; } -static void eq_fir_set_alignment(struct audio_stream *source, - struct audio_stream *sink) +static void eq_fir_set_alignment(struct audio_stream *source) { - const uint32_t byte_align = 1; + const uint32_t byte_align = SOF_FRAME_BYTE_ALIGN; const uint32_t frame_align_req = 2; /* Process multiples of 2 frames */ audio_stream_set_align(byte_align, frame_align_req, source); - audio_stream_set_align(byte_align, frame_align_req, sink); } static int eq_fir_prepare(struct processing_module *mod, @@ -413,8 +384,9 @@ static int eq_fir_prepare(struct processing_module *mod, int channels; enum sof_ipc_frame frame_fmt; int ret = 0; + size_t data_size; - comp_dbg(dev, "eq_fir_prepare()"); + comp_dbg(dev, "entry"); /* EQ component will only ever have 1 source and 1 sink buffer. */ sourceb = comp_dev_get_first_data_producer(dev); @@ -430,14 +402,14 @@ static int eq_fir_prepare(struct processing_module *mod, return ret; } - eq_fir_set_alignment(&sourceb->stream, &sinkb->stream); + eq_fir_set_alignment(&sourceb->stream); channels = audio_stream_get_channels(&sinkb->stream); frame_fmt = audio_stream_get_frm_fmt(&sourceb->stream); cd->eq_fir_func = eq_fir_passthrough; - cd->config = comp_get_data_blob(cd->model_handler, NULL, NULL); - if (cd->config) { - ret = eq_fir_setup(dev, cd, channels); + cd->config = comp_get_data_blob(cd->model_handler, &data_size, NULL); + if (cd->config && data_size > 0) { + ret = eq_fir_setup(mod, channels); if (ret < 0) comp_err(dev, "eq_fir_setup failed."); else if (cd->fir_delay_size) @@ -460,11 +432,11 @@ static int eq_fir_reset(struct processing_module *mod) int i; struct comp_data *cd = module_get_private_data(mod); - comp_dbg(mod->dev, "eq_fir_reset()"); + comp_dbg(mod->dev, "entry"); comp_data_blob_set_validator(cd->model_handler, NULL); - eq_fir_free_delaylines(cd); + eq_fir_free_delaylines(mod); cd->eq_fir_func = NULL; for (i = 0; i < PLATFORM_MAX_CHANNELS; i++) @@ -497,6 +469,7 @@ SOF_LLEXT_BUILDINFO; #else +DECLARE_TR_CTX(eq_fir_tr, SOF_UUID(eq_fir_uuid), LOG_LEVEL_INFO); DECLARE_MODULE_ADAPTER(eq_fir_interface, eq_fir_uuid, eq_fir_tr); SOF_MODULE_INIT(eq_fir, sys_comp_module_eq_fir_interface_init); diff --git a/src/audio/eq_fir/eq_fir_ipc3.c b/src/audio/eq_fir/eq_fir_ipc3.c index 5b5c7e48c675..ecfd873ffd7f 100644 --- a/src/audio/eq_fir/eq_fir_ipc3.c +++ b/src/audio/eq_fir/eq_fir_ipc3.c @@ -23,24 +23,24 @@ int set_fir_func(struct processing_module *mod, enum sof_ipc_frame fmt) switch (fmt) { #if CONFIG_FORMAT_S16LE case SOF_IPC_FRAME_S16_LE: - comp_dbg(mod->dev, "set_fir_func(), SOF_IPC_FRAME_S16_LE"); + comp_dbg(mod->dev, "SOF_IPC_FRAME_S16_LE"); set_s16_fir(cd); break; #endif /* CONFIG_FORMAT_S16LE */ #if CONFIG_FORMAT_S24LE case SOF_IPC_FRAME_S24_4LE: - comp_dbg(mod->dev, "set_fir_func(), SOF_IPC_FRAME_S24_4LE"); + comp_dbg(mod->dev, "SOF_IPC_FRAME_S24_4LE"); set_s24_fir(cd); break; #endif /* CONFIG_FORMAT_S24LE */ #if CONFIG_FORMAT_S32LE case SOF_IPC_FRAME_S32_LE: - comp_dbg(mod->dev, "set_fir_func(), SOF_IPC_FRAME_S32_LE"); + comp_dbg(mod->dev, "SOF_IPC_FRAME_S32_LE"); set_s32_fir(cd); break; #endif /* CONFIG_FORMAT_S32LE */ default: - comp_err(mod->dev, "set_fir_func(), invalid frame_fmt"); + comp_err(mod->dev, "invalid frame_fmt"); return -EINVAL; } return 0; diff --git a/src/audio/eq_fir/eq_fir_ipc4.c b/src/audio/eq_fir/eq_fir_ipc4.c index 74cda9752f6f..64ec2895e0dd 100644 --- a/src/audio/eq_fir/eq_fir_ipc4.c +++ b/src/audio/eq_fir/eq_fir_ipc4.c @@ -42,7 +42,7 @@ int set_fir_func(struct processing_module *mod, enum sof_ipc_frame fmt) break; #endif /* CONFIG_FORMAT_S32LE */ default: - comp_err(mod->dev, "set_fir_func(), invalid valid_bith_depth"); + comp_err(mod->dev, "invalid valid_bith_depth"); return -EINVAL; } return 0; @@ -54,7 +54,7 @@ int eq_fir_params(struct processing_module *mod) struct comp_buffer *sinkb, *sourceb; struct comp_dev *dev = mod->dev; - comp_dbg(dev, "eq_fir_params()"); + comp_dbg(dev, "entry"); ipc4_base_module_cfg_to_stream_params(&mod->priv.cfg.base_cfg, params); component_set_nearest_period_frames(dev, params->rate); diff --git a/src/audio/eq_iir/README.md b/src/audio/eq_iir/README.md new file mode 100644 index 000000000000..6af07ceae354 --- /dev/null +++ b/src/audio/eq_iir/README.md @@ -0,0 +1,24 @@ +# IIR Equalizer Architecture + +This directory contains the Infinite Impulse Response (IIR) EQ component. + +## Overview + +IIR equalizers provide frequency shaping (like parametric EQs, shelves, high/low passes) typically using arrays of biquad filters. + +## Architecture Diagram + +```mermaid +graph LR + In[Input Frame] --> IIR[Biquad Cascade] + Coeffs[(Biquad Coefficients)] --> IIR + IIR --> Out[Output Frame] +``` + +## Configuration and Scripts + +- **Kconfig**: Activates the IIR component (`COMP_IIR`), selecting `MATH_IIR_DF1` and depending on the module adapter. +- **CMakeLists.txt**: Compiles generic logic (`eq_iir_generic.c`) and IPC-specific files depending on the Zephyr IPC configuration. +- **eq_iir.toml**: Topology parameters tailored by platform. Defines custom `mod_cfg` arrays with varying constraints based on `CONFIG_METEORLAKE` versus `CONFIG_LUNARLAKE` and ACE SOCs. +- **Topology (.conf)**: Dictated by `tools/topology/topology2/include/components/eqiir.conf`, representing an `effect` widget object with UUID `e6:c0:50:51:f9:27:c8:4e:83:51:c7:05:b6:42:d1:2f`. +- **MATLAB Tuning (`tune/`)**: `sof_example_iir_eq.m` and associated scripts can design parametric biquad presets (e.g., loudness, bass boost, bandpass, flat). These scripts compute the IIR coefficients, calculate precise scaling values, quantize mathematically, and bundle the permutations into binaries and configuration fragments suitable for SOF IPC messages. diff --git a/src/audio/eq_iir/eq_iir.c b/src/audio/eq_iir/eq_iir.c index 4b70fe355c77..016c2c8caf82 100644 --- a/src/audio/eq_iir/eq_iir.c +++ b/src/audio/eq_iir/eq_iir.c @@ -17,7 +17,6 @@ #include <sof/common.h> #include <rtos/panic.h> #include <sof/ipc/msg.h> -#include <rtos/alloc.h> #include <rtos/init.h> #include <sof/lib/uuid.h> #include <sof/list.h> @@ -38,8 +37,6 @@ LOG_MODULE_REGISTER(eq_iir, CONFIG_SOF_LOG_LEVEL); SOF_DEFINE_REG_UUID(eq_iir); -DECLARE_TR_CTX(eq_iir_tr, SOF_UUID(eq_iir_uuid), LOG_LEVEL_INFO); - /* * End of EQ setup code. Next the standard component methods. */ @@ -47,60 +44,39 @@ static int eq_iir_init(struct processing_module *mod) { struct module_data *md = &mod->priv; struct comp_dev *dev = mod->dev; - struct module_config *cfg = &md->cfg; struct comp_data *cd; - size_t bs = cfg->size; - int i, ret; - - comp_info(dev, "eq_iir_init()"); + int i; - /* Check first before proceeding with dev and cd that coefficients blob size is sane */ - if (bs > SOF_EQ_IIR_MAX_SIZE) { - comp_err(dev, "eq_iir_init(), coefficients blob size %zu exceeds maximum", bs); - return -EINVAL; - } + comp_info(dev, "entry"); - cd = rzalloc(SOF_MEM_FLAG_USER, sizeof(*cd)); + cd = mod_zalloc(mod, sizeof(*cd)); if (!cd) return -ENOMEM; md->private = cd; /* component model data handler */ - cd->model_handler = comp_data_blob_handler_new(dev); + cd->model_handler = mod_data_blob_handler_new(mod); if (!cd->model_handler) { - comp_err(dev, "comp_data_blob_handler_new() failed."); - ret = -ENOMEM; - goto err; - } - - /* Allocate and make a copy of the coefficients blob and reset IIR. If - * the EQ is configured later in run-time the size is zero. - */ - ret = comp_init_data_blob(cd->model_handler, bs, cfg->data); - if (ret < 0) { - comp_err(dev, "comp_init_data_blob() failed with error: %d", ret); - comp_data_blob_handler_free(cd->model_handler); - goto err; + comp_err(dev, "mod_data_blob_handler_new() failed."); + mod_free(mod, cd); + return -ENOMEM; } for (i = 0; i < PLATFORM_MAX_CHANNELS; i++) iir_reset_df1(&cd->iir[i]); return 0; -err: - rfree(cd); - return ret; } static int eq_iir_free(struct processing_module *mod) { struct comp_data *cd = module_get_private_data(mod); - eq_iir_free_delaylines(cd); - comp_data_blob_handler_free(cd->model_handler); + eq_iir_free_delaylines(mod); + mod_data_blob_handler_free(mod, cd->model_handler); - rfree(cd); + mod_free(mod, cd); return 0; } @@ -113,7 +89,7 @@ static int eq_iir_set_config(struct processing_module *mod, uint32_t config_id, { struct comp_data *cd = module_get_private_data(mod); - comp_info(mod->dev, "eq_iir_set_config()"); + comp_info(mod->dev, "entry"); return comp_data_blob_set(cd->model_handler, pos, data_offset_size, fragment, fragment_size); @@ -126,7 +102,7 @@ static int eq_iir_get_config(struct processing_module *mod, struct sof_ipc_ctrl_data *cdata = (struct sof_ipc_ctrl_data *)fragment; struct comp_data *cd = module_get_private_data(mod); - comp_info(mod->dev, "eq_iir_get_config()"); + comp_info(mod->dev, "entry"); return comp_data_blob_get_cmd(cd->model_handler, cdata, fragment_size); } @@ -144,7 +120,7 @@ static int eq_iir_process(struct processing_module *mod, /* Check for changed configuration */ if (comp_is_new_data_blob_available(cd->model_handler)) { cd->config = comp_get_data_blob(cd->model_handler, NULL, NULL); - ret = eq_iir_new_blob(mod, cd, audio_stream_get_frm_fmt(source), + ret = eq_iir_new_blob(mod, audio_stream_get_frm_fmt(source), audio_stream_get_frm_fmt(sink), audio_stream_get_channels(source)); if (ret) @@ -166,7 +142,7 @@ static int eq_iir_process(struct processing_module *mod, static void eq_iir_set_alignment(struct audio_stream *source, struct audio_stream *sink) { - const uint32_t byte_align = 8; + const uint32_t byte_align = SOF_FRAME_BYTE_ALIGN; const uint32_t frame_align_req = 2; audio_stream_set_align(byte_align, frame_align_req, source); @@ -182,10 +158,11 @@ static int eq_iir_prepare(struct processing_module *mod, struct comp_dev *dev = mod->dev; enum sof_ipc_frame source_format; enum sof_ipc_frame sink_format; + size_t data_size; int channels; int ret = 0; - comp_dbg(dev, "eq_iir_prepare()"); + comp_dbg(dev, "entry"); /* EQ component will only ever have 1 source and 1 sink buffer */ sourceb = comp_dev_get_first_data_producer(dev); @@ -206,23 +183,23 @@ static int eq_iir_prepare(struct processing_module *mod, source_format = audio_stream_get_frm_fmt(&sourceb->stream); sink_format = audio_stream_get_frm_fmt(&sinkb->stream); - cd->config = comp_get_data_blob(cd->model_handler, NULL, NULL); + cd->config = comp_get_data_blob(cd->model_handler, &data_size, NULL); /* Initialize EQ */ - comp_info(dev, "eq_iir_prepare(), source_format=%d, sink_format=%d", + comp_info(dev, "source_format=%d, sink_format=%d", source_format, sink_format); eq_iir_set_passthrough_func(cd, source_format, sink_format); /* Initialize EQ */ - if (cd->config) { - ret = eq_iir_new_blob(mod, cd, source_format, sink_format, channels); + if (cd->config && data_size > 0) { + ret = eq_iir_new_blob(mod, source_format, sink_format, channels); if (ret) return ret; } if (!cd->eq_iir_func) { - comp_err(dev, "eq_iir_prepare(), No processing function found"); + comp_err(dev, "No processing function found"); ret = -EINVAL; } @@ -234,7 +211,7 @@ static int eq_iir_reset(struct processing_module *mod) struct comp_data *cd = module_get_private_data(mod); int i; - eq_iir_free_delaylines(cd); + eq_iir_free_delaylines(mod); cd->eq_iir_func = NULL; for (i = 0; i < PLATFORM_MAX_CHANNELS; i++) @@ -267,6 +244,7 @@ SOF_LLEXT_BUILDINFO; #else +DECLARE_TR_CTX(eq_iir_tr, SOF_UUID(eq_iir_uuid), LOG_LEVEL_INFO); DECLARE_MODULE_ADAPTER(eq_iir_interface, eq_iir_uuid, eq_iir_tr); SOF_MODULE_INIT(eq_iir, sys_comp_module_eq_iir_interface_init); diff --git a/src/audio/eq_iir/eq_iir.h b/src/audio/eq_iir/eq_iir.h index 7b6b6c247574..12d888d66594 100644 --- a/src/audio/eq_iir/eq_iir.h +++ b/src/audio/eq_iir/eq_iir.h @@ -56,9 +56,8 @@ void eq_iir_s24_default(struct processing_module *mod, struct input_stream_buffe void eq_iir_s32_default(struct processing_module *mod, struct input_stream_buffer *bsource, struct output_stream_buffer *bsink, uint32_t frames); -int eq_iir_new_blob(struct processing_module *mod, struct comp_data *cd, - enum sof_ipc_frame source_format, enum sof_ipc_frame sink_format, - int channels); +int eq_iir_new_blob(struct processing_module *mod, enum sof_ipc_frame source_format, + enum sof_ipc_frame sink_format, int channels); void eq_iir_set_passthrough_func(struct comp_data *cd, enum sof_ipc_frame source_format, @@ -71,5 +70,5 @@ void eq_iir_pass(struct processing_module *mod, struct input_stream_buffer *bsou int eq_iir_setup(struct processing_module *mod, int nch); -void eq_iir_free_delaylines(struct comp_data *cd); +void eq_iir_free_delaylines(struct processing_module *mod); #endif /* __SOF_AUDIO_EQ_IIR_EQ_IIR_H__ */ diff --git a/src/audio/eq_iir/eq_iir.toml b/src/audio/eq_iir/eq_iir.toml index b02eeb360395..a9933bffc9fb 100644 --- a/src/audio/eq_iir/eq_iir.toml +++ b/src/audio/eq_iir/eq_iir.toml @@ -23,8 +23,8 @@ mod_cfg = [0, 0, 0, 0, 4096, 1000000, 128, 128, 0, 1000, 0, 0, 0, 0, 0, 4096, 20663000, 768, 768, 0, 20663, 0, 0, 0, 0, 0, 4096, 11357000, 384, 384, 0, 11357, 0] -#elif defined(CONFIG_LUNARLAKE) || defined(CONFIG_SOC_INTEL_ACE30) || \ - defined(CONFIG_SOC_INTEL_ACE40) +#elif defined(CONFIG_LUNARLAKE) || defined(CONFIG_SOC_ACE30) || \ + defined(CONFIG_SOC_ACE40) mod_cfg = [0, 0, 0, 0, 4096, 1000000, 128, 128, 0, 0, 0] #endif diff --git a/src/audio/eq_iir/eq_iir_generic.c b/src/audio/eq_iir/eq_iir_generic.c index 4e7688b85004..fd3485a28eca 100644 --- a/src/audio/eq_iir/eq_iir_generic.c +++ b/src/audio/eq_iir/eq_iir_generic.c @@ -202,11 +202,11 @@ static int eq_iir_init_coef(struct processing_module *mod, int nch) if (nch > PLATFORM_MAX_CHANNELS || config->channels_in_config > PLATFORM_MAX_CHANNELS || !config->channels_in_config) { - comp_err(mod->dev, "eq_iir_init_coef(), invalid channels count"); + comp_err(mod->dev, "invalid channels count"); return -EINVAL; } if (config->number_of_responses > SOF_EQ_IIR_MAX_RESPONSES) { - comp_err(mod->dev, "eq_iir_init_coef(), # of resp exceeds max"); + comp_err(mod->dev, "# of resp exceeds max"); return -EINVAL; } @@ -241,13 +241,13 @@ static int eq_iir_init_coef(struct processing_module *mod, int nch) /* Initialize EQ channel to bypass and continue with * next channel response. */ - comp_info(mod->dev, "eq_iir_init_coef(), ch %d is set to bypass", i); + comp_info(mod->dev, "ch %d is set to bypass", i); iir_reset_df1(&iir[i]); continue; } if (resp >= config->number_of_responses) { - comp_err(mod->dev, "eq_iir_init_coef(), requested response %d exceeds defined", + comp_err(mod->dev, "requested response %d exceeds defined", resp); return -EINVAL; } @@ -258,13 +258,13 @@ static int eq_iir_init_coef(struct processing_module *mod, int nch) if (s > 0) { size_sum += s; } else { - comp_err(mod->dev, "eq_iir_init_coef(), sections count %d exceeds max", + comp_err(mod->dev, "sections count %d exceeds max", eq->num_sections); return -EINVAL; } iir_init_coef_df1(&iir[i], eq); - comp_info(mod->dev, "eq_iir_init_coef(), ch %d is set to response %d", i, resp); + comp_info(mod->dev, "ch %d is set to response %d", i, resp); } return size_sum; @@ -285,15 +285,16 @@ static void eq_iir_init_delay(struct iir_state_df1 *iir, } } -void eq_iir_free_delaylines(struct comp_data *cd) +void eq_iir_free_delaylines(struct processing_module *mod) { + struct comp_data *cd = module_get_private_data(mod); struct iir_state_df1 *iir = cd->iir; int i = 0; /* Free the common buffer for all EQs and point then * each IIR channel delay line to NULL. */ - rfree(cd->iir_delay); + mod_free(mod, cd->iir_delay); cd->iir_delay = NULL; cd->iir_delay_size = 0; for (i = 0; i < PLATFORM_MAX_CHANNELS; i++) @@ -315,7 +316,7 @@ int eq_iir_setup(struct processing_module *mod, int nch) int delay_size; /* Free existing IIR channels data if it was allocated */ - eq_iir_free_delaylines(cd); + eq_iir_free_delaylines(mod); /* Set coefficients for each channel EQ from coefficient blob */ delay_size = eq_iir_init_coef(mod, nch); @@ -329,10 +330,9 @@ int eq_iir_setup(struct processing_module *mod, int nch) return 0; /* Allocate all IIR channels data in a big chunk and clear it */ - cd->iir_delay = rzalloc(SOF_MEM_FLAG_USER, - delay_size); + cd->iir_delay = mod_zalloc(mod, delay_size); if (!cd->iir_delay) { - comp_err(mod->dev, "eq_iir_setup(), delay allocation fail"); + comp_err(mod->dev, "delay allocation fail"); return -ENOMEM; } diff --git a/src/audio/eq_iir/eq_iir_ipc3.c b/src/audio/eq_iir/eq_iir_ipc3.c index b8c5536f0d32..69b27d64c3e7 100644 --- a/src/audio/eq_iir/eq_iir_ipc3.c +++ b/src/audio/eq_iir/eq_iir_ipc3.c @@ -17,7 +17,6 @@ #include <sof/common.h> #include <rtos/panic.h> #include <sof/ipc/msg.h> -#include <rtos/alloc.h> #include <rtos/init.h> #include <sof/lib/uuid.h> #include <sof/list.h> @@ -270,7 +269,7 @@ static int eq_iir_verify_params(struct comp_dev *dev, uint32_t buffer_flag; int ret; - comp_dbg(dev, "eq_iir_verify_params()"); + comp_dbg(dev, "entry"); /* The caller has verified, that sink and source buffers are connected */ @@ -298,22 +297,22 @@ static int eq_iir_verify_params(struct comp_dev *dev, return 0; } -int eq_iir_new_blob(struct processing_module *mod, struct comp_data *cd, - enum sof_ipc_frame source_format, enum sof_ipc_frame sink_format, - int channels) +int eq_iir_new_blob(struct processing_module *mod, enum sof_ipc_frame source_format, + enum sof_ipc_frame sink_format, int channels) { + struct comp_data *cd = module_get_private_data(mod); int ret; ret = eq_iir_setup(mod, channels); if (ret < 0) { - comp_err(mod->dev, "eq_iir_new_blob(), failed IIR setup"); + comp_err(mod->dev, "failed IIR setup"); return ret; } else if (cd->iir_delay_size) { - comp_dbg(mod->dev, "eq_iir_new_blob(), active"); + comp_dbg(mod->dev, "active"); cd->eq_iir_func = eq_iir_find_func(source_format, sink_format, fm_configured, ARRAY_SIZE(fm_configured)); } else { - comp_dbg(mod->dev, "eq_iir_new_blob(), pass-through"); + comp_dbg(mod->dev, "pass-through"); cd->eq_iir_func = eq_iir_find_func(source_format, sink_format, fm_passthrough, ARRAY_SIZE(fm_passthrough)); } diff --git a/src/audio/eq_iir/eq_iir_ipc4.c b/src/audio/eq_iir/eq_iir_ipc4.c index ece765d3140b..c19d7f2650fa 100644 --- a/src/audio/eq_iir/eq_iir_ipc4.c +++ b/src/audio/eq_iir/eq_iir_ipc4.c @@ -17,7 +17,6 @@ #include <sof/common.h> #include <rtos/panic.h> #include <sof/ipc/msg.h> -#include <rtos/alloc.h> #include <rtos/init.h> #include <sof/lib/uuid.h> #include <sof/list.h> @@ -77,21 +76,21 @@ static eq_iir_func eq_iir_find_func(struct processing_module *mod) return NULL; } -int eq_iir_new_blob(struct processing_module *mod, struct comp_data *cd, - enum sof_ipc_frame source_format, enum sof_ipc_frame sink_format, - int channels) +int eq_iir_new_blob(struct processing_module *mod, enum sof_ipc_frame source_format, + enum sof_ipc_frame sink_format, int channels) { + struct comp_data *cd = module_get_private_data(mod); int ret; ret = eq_iir_setup(mod, channels); if (ret < 0) { - comp_err(mod->dev, "eq_iir_new_blob(), failed IIR setup"); + comp_err(mod->dev, "failed IIR setup"); return ret; } else if (cd->iir_delay_size) { - comp_dbg(mod->dev, "eq_iir_new_blob(), active"); + comp_dbg(mod->dev, "active"); cd->eq_iir_func = eq_iir_find_func(mod); } else { - comp_dbg(mod->dev, "eq_iir_new_blob(), pass-through"); + comp_dbg(mod->dev, "pass-through"); cd->eq_iir_func = eq_iir_pass; } @@ -107,7 +106,7 @@ static int eq_iir_params(struct processing_module *mod) enum sof_ipc_frame valid_fmt, frame_fmt; int i; - comp_dbg(dev, "eq_iir_params()"); + comp_dbg(dev, "entry"); comp_params = *params; comp_params.channels = mod->priv.cfg.base_cfg.audio_fmt.channels_count; comp_params.rate = mod->priv.cfg.base_cfg.audio_fmt.sampling_frequency; diff --git a/src/audio/eq_iir/tune/sof_example_iir_eq.m b/src/audio/eq_iir/tune/sof_example_iir_eq.m index b75bfa24c422..eef0f2751300 100644 --- a/src/audio/eq_iir/tune/sof_example_iir_eq.m +++ b/src/audio/eq_iir/tune/sof_example_iir_eq.m @@ -2,7 +2,7 @@ % SPDX-License-Identifier: BSD-3-Clause % -% Copyright (c) 2016-2020, Intel Corporation. All rights reserved. +% Copyright (c) 2016-2026, Intel Corporation. % % Author: Seppo Ingalsuo <seppo.ingalsuo@linux.intel.com> @@ -166,7 +166,7 @@ function sof_example_iir_eq() fs_list = [16e3 48e3]; fc_list = [20 30 40 50 100]; -g_list = [0 20 30 40]; +g_list = [0 16 20 30 40]; for i = 1:length(fs_list) for j = 1:length(fc_list); for k = 1:length(g_list); diff --git a/src/audio/eq_iir/tune/sof_example_lr4.m b/src/audio/eq_iir/tune/sof_example_lr4.m new file mode 100644 index 000000000000..d38111da6b98 --- /dev/null +++ b/src/audio/eq_iir/tune/sof_example_lr4.m @@ -0,0 +1,183 @@ +% sof_example_lr4 - Design 4th order Linkwitz–Riley filter bank +% +% This script is run without arguments. It creates IIR equalizer +% blobs for crossover filter bank for 2-way speaker and four +% channels stream. The exported configurations are Linkwitz-Riley +% 4th order with crossover frequency at 2 kHz. The filters are +% in order: +% - low, high, low, high +% - low, low, high, high +% - high, high, low, low +% + +% SPDX-License-Identifier: BSD-3-Clause +% +% Copyright (c) 2025, Intel Corporation. +% +% Author: Seppo Ingalsuo <seppo.ingalsuo@linux.intel.com> + +function sof_example_lr4() + +%% Common definitions +fs = 48e3; +fc = 2e3; +sof_tools = '../../../../tools'; +tpath = fullfile(sof_tools, 'topology/topology2/include/components/eqiir'); +cpath = fullfile(sof_tools, 'ctl/ipc4/eq_iir'); + +sof_eq_paths(1); + +%% -------------------------------------------------- +%% Example: Band-split 2ch to 4ch low and high bands +%% -------------------------------------------------- +design_name = sprintf('xover_lr4_%dhz_lhlh_%dkhz', fc, round(fs/1000)); +blob_fn = fullfile(cpath, [design_name '.bin']); +alsa_fn = fullfile(cpath, [design_name '.txt']); +tplg_fn = fullfile(tpath, [design_name '.conf']); +comment = 'LR4 filter bank coefficients'; +howto = 'cd src/audio/eq_iir/tune; octave sof_example_lr4.m'; + +% Design low-pass and high-pass filters +eq_lo = lo_band_iir(fs, fc); +eq_hi = hi_band_iir(fs, fc); + +% Quantize and pack filter coefficients plus shifts etc. +bq_lo = sof_eq_iir_blob_quant(eq_lo.p_z, eq_lo.p_p, eq_lo.p_k); +bq_hi = sof_eq_iir_blob_quant(eq_hi.p_z, eq_hi.p_p, eq_hi.p_k); + +% Build blob +channels_in_config = 4; % Setup max 4 channels EQ +assign_response = [0 1 0 1]; % Order: lo, hi, lo, hi +num_responses = 2; % Two responses: lo, hi +bm = sof_eq_iir_blob_merge(channels_in_config, ... + num_responses, ... + assign_response, ... + [bq_lo bq_hi]); + +% Pack and write file +sof_eq_pack_export(bm, blob_fn, alsa_fn, tplg_fn, comment, howto) + +%% -------------------------------------------------- +%% Example: Same but filters order is lo, lo, hi, hi +%% -------------------------------------------------- + +design_name = sprintf('xover_lr4_%dhz_llhh_%dkhz', fc, round(fs/1000)); +blob_fn = fullfile(cpath, [design_name '.bin']); +alsa_fn = fullfile(cpath, [design_name '.txt']); +tplg_fn = fullfile(tpath, [design_name '.conf']); + +assign_response = [0 0 1 1]; +num_responses = 2; +bm = sof_eq_iir_blob_merge(channels_in_config, ... + num_responses, ... + assign_response, ... + [bq_lo bq_hi]); + +% Pack and write file +sof_eq_pack_export(bm, blob_fn, alsa_fn, tplg_fn, comment, howto) + +%% -------------------------------------------------- +%% Example: Same but filters order is hi, hi, lo, lo +%% -------------------------------------------------- + +design_name = sprintf('xover_lr4_%dhz_hhll_%dkhz', fc, round(fs/1000)); +blob_fn = fullfile(cpath, [design_name '.bin']); +alsa_fn = fullfile(cpath, [design_name '.txt']); +tplg_fn = fullfile(tpath, [design_name '.conf']); + +assign_response = [1 1 0 0]; +num_responses = 2; +bm = sof_eq_iir_blob_merge(channels_in_config, ... + num_responses, ... + assign_response, ... + [bq_lo bq_hi]); + +% Pack and write file +sof_eq_pack_export(bm, blob_fn, alsa_fn, tplg_fn, comment, howto) + +%% ------------------------------------ +%% Done. +%% ------------------------------------ + +sof_eq_paths(0); +end + +%% ------------------- +%% EQ design functions +%% ------------------- + +function eq = lo_band_iir(fs, fc) + + +%% Get defaults for equalizer design +eq = sof_eq_defaults(); +eq.fs = fs; +eq.enable_iir = 1; +eq.iir_norm_type = 'peak'; +eq.iir_norm_offs_db = 0; + +% Parametric EQs are PEQ_HP1, PEQ_HP2, PEQ_LP1, PEQ_LP2, PEQ_LS1, +% PEQ_LS2, PEQ_HS1, PEQ_HS2 = 8, PEQ_PN2, PEQ_LP4, and PEQ_HP4. +% +% Parametric EQs take as second argument the cutoff frequency in Hz +% and as second argument a dB value (can use 0 for LP2). The +% Third argument is a Q-value (can use 0 for LP2). + +% Two 2nd order butterworth low-pass filters for 4th order Linkwitz–Riley +eq.peq = [ ... + eq.PEQ_LP2 fc 0 0 ; ... + eq.PEQ_LP2 fc 0 0 ; ... + ]; + +%% Design EQ +eq = sof_eq_compute(eq); + +%% Plot +sof_eq_plot(eq); + +end + +function eq = hi_band_iir(fs, fc) + + +%% Get defaults for equalizer design +eq = sof_eq_defaults(); +eq.fs = fs; +eq.enable_iir = 1; +eq.iir_norm_type = 'peak'; +eq.iir_norm_offs_db = 0; + +% Two 2nd order high-pass filters for 4th order Linkwitz–Riley +eq.peq = [ ... + eq.PEQ_HP2 fc 0 0 ; ... + eq.PEQ_HP2 fc 0 0 ; ... + ]; + +%% Design EQ +eq = sof_eq_compute(eq); + +%% Plot +sof_eq_plot(eq); + +end + + + +% Pack and write file common function for all exports +function sof_eq_pack_export(bm, bin_fn, ascii_fn, tplg_fn, note, howto) + +bp = sof_eq_iir_blob_pack(bm, 4); % IPC4 + +if ~isempty(bin_fn) + sof_ucm_blob_write(bin_fn, bp); +end + +if ~isempty(ascii_fn) + sof_alsactl_write(ascii_fn, bp); +end + +if ~isempty(tplg_fn) + sof_tplg2_write(tplg_fn, bp, 'IIR', note, howto); +end + +end diff --git a/src/audio/google/README.md b/src/audio/google/README.md new file mode 100644 index 000000000000..feb5ce615b0a --- /dev/null +++ b/src/audio/google/README.md @@ -0,0 +1,10 @@ +# Google Custom Components Architecture + +This directory houses components specific to Google integrations, such as specialized wake-word or hotword engines. + +## Configuration and Scripts + +- **Kconfig**: Exposes menus for Google components, including `COMP_GOOGLE_HOTWORD_DETECT` (engine for hotword detection), `COMP_GOOGLE_RTC_AUDIO_PROCESSING` (acoustic echo cancellation with tunable memory size, sample rates, max channels, and reference max channels), and `COMP_GOOGLE_CTC_AUDIO_PROCESSING` (crosstalk cancellation). It also exposes mock definitions for CI testing (`COMP_STUBS`). +- **CMakeLists.txt**: Conditionally links external static libraries (`libhifi3_google_hotword_dsp_api.a`, `google_rtc_audio_processing`, `google_ctc_audio_processing`) from `third_party` tools. Defines Zephyr and non-Zephyr build pipelines. +- **google_rtc_audio_processing.toml / google_ctc_audio_processing.toml**: Specify topology module configurations for `RTC_AEC` and `CTC` models respectively with pinning mappings (like DMIC pins and playback references). +- **Topology (.conf)**: Incorporates `tools/topology/topology2/include/components/ctc.conf` (defaults to UUID `bc:1b:0e:bf:6a:dc:fe:45:bc:90:25:54:cb:13:7a:b4`) and `google-rtc-aec.conf` (defaults to UUID `a6:a0:80:b7:9f:26:6f:46:b4:77:23:df:a0:5a:f7:58`) with strict audio format channel maps (e.g. 2ch vs 4ch mappings). diff --git a/src/audio/google/google_ctc_audio_processing.c b/src/audio/google/google_ctc_audio_processing.c index 4b01feea1efa..0ee37abd24d5 100644 --- a/src/audio/google/google_ctc_audio_processing.c +++ b/src/audio/google/google_ctc_audio_processing.c @@ -22,9 +22,6 @@ LOG_MODULE_REGISTER(google_ctc_audio_processing, CONFIG_SOF_LOG_LEVEL); SOF_DEFINE_REG_UUID(google_ctc_audio_processing); -DECLARE_TR_CTX(google_ctc_audio_processing_tr, SOF_UUID(google_ctc_audio_processing_uuid), - LOG_LEVEL_INFO); - // TODO(eddyhsu): Share these utils function with RTC. static inline float clamp_rescale(float max_val, float x) { @@ -243,13 +240,14 @@ static int ctc_free(struct processing_module *mod) { struct google_ctc_audio_processing_comp_data *cd = module_get_private_data(mod); - comp_info(mod->dev, "ctc_free()"); + comp_info(mod->dev, "entry"); if (cd) { - rfree(cd->input); - rfree(cd->output); + mod_free(mod, cd->input); + mod_free(mod, cd->output); GoogleCtcAudioProcessingFree(cd->state); - rfree(cd); + mod_data_blob_handler_free(mod, cd->tuning_handler); + mod_free(mod, cd); module_set_private_data(mod, NULL); } @@ -262,10 +260,10 @@ static int ctc_init(struct processing_module *mod) struct google_ctc_audio_processing_comp_data *cd; int buf_size; - comp_info(dev, "ctc_init()"); + comp_info(dev, "entry"); /* Create private component data */ - cd = rzalloc(SOF_MEM_FLAG_USER, sizeof(*cd)); + cd = mod_zalloc(mod, sizeof(*cd)); if (!cd) { comp_err(dev, "Failed to create component data"); ctc_free(mod); @@ -277,20 +275,20 @@ static int ctc_init(struct processing_module *mod) cd->chunk_frames = kChunkFrames; buf_size = cd->chunk_frames * sizeof(cd->input[0]) * kMaxChannels; - cd->input = rballoc(SOF_MEM_FLAG_USER, buf_size); + cd->input = mod_balloc(mod, buf_size); if (!cd->input) { comp_err(dev, "Failed to allocate input buffer"); ctc_free(mod); return -ENOMEM; } - cd->output = rballoc(SOF_MEM_FLAG_USER, buf_size); + cd->output = mod_balloc(mod, buf_size); if (!cd->output) { comp_err(dev, "Failed to allocate output buffer"); ctc_free(mod); return -ENOMEM; } - cd->tuning_handler = comp_data_blob_handler_new(dev); + cd->tuning_handler = mod_data_blob_handler_new(mod); if (!cd->tuning_handler) { comp_err(dev, "Failed to create tuning handler"); ctc_free(mod); @@ -312,7 +310,7 @@ static int google_ctc_audio_processing_reconfigure(struct processing_module *mod size_t size; int ret; - comp_dbg(dev, "google_ctc_audio_processing_reconfigure()"); + comp_dbg(dev, "entry"); config = comp_get_data_blob(cd->tuning_handler, &size, NULL); if (size == 0) { @@ -352,7 +350,7 @@ static int ctc_prepare(struct processing_module *mod, uint8_t *config; int config_size; - comp_info(mod->dev, "ctc_prepare()"); + comp_info(mod->dev, "entry"); source = comp_dev_get_first_data_producer(dev); if (!source) { @@ -377,13 +375,13 @@ static int ctc_prepare(struct processing_module *mod, break; #endif default: - comp_err(mod->dev, "ctc_prepare(), invalid frame_fmt"); + comp_err(mod->dev, "invalid frame_fmt"); return -EINVAL; } num_channels = audio_stream_get_channels(&source->stream); if (num_channels > kMaxChannels) { - comp_err(mod->dev, "ctc_prepare(), invalid number of channels"); + comp_err(mod->dev, "invalid number of channels"); return -EINVAL; } cd->next_avail_output_samples = cd->chunk_frames * num_channels; @@ -400,7 +398,7 @@ static int ctc_prepare(struct processing_module *mod, config, config_size); if (!cd->state) { - comp_err(mod->dev, "ctc_prepare(), failed to create CTC"); + comp_err(mod->dev, "failed to create CTC"); return -ENOMEM; } @@ -412,7 +410,7 @@ static int ctc_reset(struct processing_module *mod) struct google_ctc_audio_processing_comp_data *cd = module_get_private_data(mod); size_t buf_size = cd->chunk_frames * sizeof(cd->input[0]) * kMaxChannels; - comp_info(mod->dev, "ctc_reset()"); + comp_info(mod->dev, "entry"); GoogleCtcAudioProcessingFree(cd->state); cd->state = NULL; @@ -437,7 +435,7 @@ static int ctc_process(struct processing_module *mod, int ret; - comp_dbg(mod->dev, "ctc_process()"); + comp_dbg(mod->dev, "entry"); if (cd->reconfigure) { ret = google_ctc_audio_processing_reconfigure(mod); @@ -474,6 +472,8 @@ SOF_LLEXT_BUILDINFO; #else +DECLARE_TR_CTX(google_ctc_audio_processing_tr, SOF_UUID(google_ctc_audio_processing_uuid), + LOG_LEVEL_INFO); DECLARE_MODULE_ADAPTER(google_ctc_audio_processing_interface, google_ctc_audio_processing_uuid, google_ctc_audio_processing_tr); SOF_MODULE_INIT(google_ctc_audio_processing, diff --git a/src/audio/google/google_ctc_audio_processing_ipc3.c b/src/audio/google/google_ctc_audio_processing_ipc3.c index 96ce45cb72e2..8f6594203420 100644 --- a/src/audio/google/google_ctc_audio_processing_ipc3.c +++ b/src/audio/google/google_ctc_audio_processing_ipc3.c @@ -56,7 +56,7 @@ int ctc_set_config(struct processing_module *mod, uint32_t param_id, case SOF_CTRL_CMD_SWITCH: if (cdata->num_elems == 1) { cd->enabled = cdata->chanv[0].value; - comp_info(mod->dev, "ctc_set_config(), enabled = %d", + comp_info(mod->dev, "enabled = %d", cd->enabled); return 0; } diff --git a/src/audio/google/google_hotword_detect.c b/src/audio/google/google_hotword_detect.c index e625aba57d93..f7cf44f026fd 100644 --- a/src/audio/google/google_hotword_detect.c +++ b/src/audio/google/google_hotword_detect.c @@ -45,7 +45,6 @@ static const struct comp_driver ghd_driver; LOG_MODULE_REGISTER(google_hotword_detect, CONFIG_SOF_LOG_LEVEL); SOF_DEFINE_REG_UUID(google_hotword); -DECLARE_TR_CTX(ghd_tr, SOF_UUID(ghd_uuid), LOG_LEVEL_INFO); struct comp_data { struct comp_data_blob_handler *model_handler; @@ -63,7 +62,7 @@ static void notify_host(const struct comp_dev *dev) { struct comp_data *cd = comp_get_drvdata(dev); - comp_dbg(dev, "notify_host()"); + comp_dbg(dev, "entry"); ipc_msg_send(cd->msg, &cd->event, true); } @@ -72,7 +71,7 @@ static void notify_kpb(const struct comp_dev *dev) { struct comp_data *cd = comp_get_drvdata(dev); - comp_dbg(dev, "notify_kpb()"); + comp_dbg(dev, "entry"); cd->client_data.r_ptr = NULL; cd->client_data.sink = NULL; @@ -135,7 +134,7 @@ static struct comp_dev *ghd_create(const struct comp_driver *drv, ipc_msg_free(cd->msg); rfree(cd); fail: - rfree(dev); + comp_free_device(dev); return NULL; } @@ -143,12 +142,12 @@ static void ghd_free(struct comp_dev *dev) { struct comp_data *cd = comp_get_drvdata(dev); - comp_dbg(dev, "ghd_free()"); + comp_dbg(dev, "entry"); comp_data_blob_handler_free(cd->model_handler); ipc_msg_free(cd->msg); rfree(cd); - rfree(dev); + comp_free_device(dev); } static int ghd_params(struct comp_dev *dev, @@ -394,7 +393,7 @@ static int ghd_copy(struct comp_dev *dev) bytes = audio_stream_get_avail_bytes(stream); - comp_dbg(dev, "ghd_copy() avail_bytes %u", bytes); + comp_dbg(dev, "avail_bytes %u", bytes); comp_dbg(dev, "buffer begin/r_ptr/end [0x%x 0x%x 0x%x]", (uint32_t)audio_stream_get_addr(stream), (uint32_t)audio_stream_get_rptr(stream), @@ -425,7 +424,7 @@ static int ghd_reset(struct comp_dev *dev) { struct comp_data *cd = comp_get_drvdata(dev); - comp_dbg(dev, "ghd_reset()"); + comp_dbg(dev, "entry"); cd->detected = 0; cd->history_bytes = 0; @@ -438,7 +437,7 @@ static int ghd_prepare(struct comp_dev *dev) { int ret; - comp_dbg(dev, "ghd_prepare()"); + comp_dbg(dev, "entry"); ret = ghd_setup_model(dev); if (ret) @@ -447,6 +446,8 @@ static int ghd_prepare(struct comp_dev *dev) return comp_set_state(dev, COMP_TRIGGER_PREPARE); } +DECLARE_TR_CTX(ghd_tr, SOF_UUID(ghd_uuid), LOG_LEVEL_INFO); + static const struct comp_driver ghd_driver = { .type = SOF_COMP_KEYWORD_DETECT, .uid = SOF_RT_UUID(ghd_uuid), diff --git a/src/audio/google/google_rtc_audio_processing.c b/src/audio/google/google_rtc_audio_processing.c index 4e9a55f39516..7c50bca874c5 100644 --- a/src/audio/google/google_rtc_audio_processing.c +++ b/src/audio/google/google_rtc_audio_processing.c @@ -56,10 +56,6 @@ LOG_MODULE_REGISTER(google_rtc_audio_processing, CONFIG_SOF_LOG_LEVEL); SOF_DEFINE_REG_UUID(google_rtc_audio_processing); -DECLARE_TR_CTX(google_rtc_audio_processing_tr, SOF_UUID(google_rtc_audio_processing_uuid), - LOG_LEVEL_INFO); - - static __aligned(PLATFORM_DCACHE_ALIGN) uint8_t aec_mem_blob[CONFIG_COMP_GOOGLE_RTC_AUDIO_PROCESSING_MEMORY_BUFFER_SIZE_KB * 1024]; @@ -247,7 +243,7 @@ static int google_rtc_audio_processing_reconfigure(struct processing_module *mod size_t size; int ret; - comp_dbg(dev, "google_rtc_audio_processing_reconfigure()"); + comp_dbg(dev, "entry"); if (!comp_is_current_data_blob_valid(cd->tuning_handler) && !comp_is_new_data_blob_available(cd->tuning_handler)) { @@ -509,10 +505,10 @@ static int google_rtc_audio_processing_init(struct processing_module *mod) struct google_rtc_audio_processing_comp_data *cd; int ret, i; - comp_info(dev, "google_rtc_audio_processing_init()"); + comp_info(dev, "entry"); /* Create private component data */ - cd = rzalloc(SOF_MEM_FLAG_USER, sizeof(*cd)); + cd = mod_zalloc(mod, sizeof(*cd)); if (!cd) { ret = -ENOMEM; goto fail; @@ -520,7 +516,7 @@ static int google_rtc_audio_processing_init(struct processing_module *mod) md->private = cd; - cd->tuning_handler = comp_data_blob_handler_new(dev); + cd->tuning_handler = mod_data_blob_handler_new(mod); if (!cd->tuning_handler) { ret = -ENOMEM; goto fail; @@ -585,8 +581,8 @@ static int google_rtc_audio_processing_init(struct processing_module *mod) GoogleRtcAudioProcessingFree(cd->state); } GoogleRtcAudioProcessingDetachMemoryBuffer(); - comp_data_blob_handler_free(cd->tuning_handler); - rfree(cd); + mod_data_blob_handler_free(mod, cd->tuning_handler); + mod_free(mod, cd); } return ret; @@ -596,13 +592,13 @@ static int google_rtc_audio_processing_free(struct processing_module *mod) { struct google_rtc_audio_processing_comp_data *cd = module_get_private_data(mod); - comp_dbg(mod->dev, "google_rtc_audio_processing_free()"); + comp_dbg(mod->dev, "entry"); GoogleRtcAudioProcessingFree(cd->state); cd->state = NULL; GoogleRtcAudioProcessingDetachMemoryBuffer(); - comp_data_blob_handler_free(cd->tuning_handler); - rfree(cd); + mod_data_blob_handler_free(mod, cd->tuning_handler); + mod_free(mod, cd); return 0; } @@ -616,7 +612,7 @@ static int google_rtc_audio_processing_prepare(struct processing_module *mod, struct google_rtc_audio_processing_comp_data *cd = module_get_private_data(mod); int ret = 0; - comp_info(dev, "google_rtc_audio_processing_prepare()"); + comp_info(dev, "entry"); if (num_of_sources != 2 || num_of_sinks != 1) { comp_err(dev, "Invalid source/sink count"); @@ -766,7 +762,7 @@ static int trigger_handler(struct processing_module *mod, int cmd) static int google_rtc_audio_processing_reset(struct processing_module *mod) { - comp_dbg(mod->dev, "google_rtc_audio_processing_reset()"); + comp_dbg(mod->dev, "entry"); return 0; } @@ -864,6 +860,8 @@ SOF_LLEXT_BUILDINFO; #else +DECLARE_TR_CTX(google_rtc_audio_processing_tr, SOF_UUID(google_rtc_audio_processing_uuid), + LOG_LEVEL_INFO); DECLARE_MODULE_ADAPTER(google_rtc_audio_processing_interface, google_rtc_audio_processing_uuid, google_rtc_audio_processing_tr); SOF_MODULE_INIT(google_rtc_audio_processing, diff --git a/src/audio/google/google_rtc_audio_processing.toml b/src/audio/google/google_rtc_audio_processing.toml index 8650b50d8b38..931452d0a7fc 100644 --- a/src/audio/google/google_rtc_audio_processing.toml +++ b/src/audio/google/google_rtc_audio_processing.toml @@ -19,5 +19,7 @@ pin = [0, 0, 0x8, 0x2, 0x2, 0x1, 0, 0, 0x8, 0x2, 0x2, 0x4, 1, 0, 0x8, 0x2, 0x2, 0x1] + REM # mod_cfg [PAR_0 PAR_1 PAR_2 PAR_3 IS_BYTES CPS IBS OBS MOD_FLAGS CPC OBLS] + mod_cfg = [0, 0, 0, 0, 4096, 1000000, 128, 128, 0, 0, 0] index = __COUNTER__ diff --git a/src/audio/host-legacy.c b/src/audio/host-legacy.c index 75c896cede9d..3d62e271f518 100644 --- a/src/audio/host-legacy.c +++ b/src/audio/host-legacy.c @@ -25,6 +25,7 @@ #include <rtos/string.h> #include <sof/ut.h> #include <sof/trace/trace.h> +#include <sof/debug/telemetry/performance_monitor.h> #include <ipc/stream.h> #include <ipc/topology.h> #include <user/trace.h> @@ -40,8 +41,6 @@ LOG_MODULE_REGISTER(host, CONFIG_SOF_LOG_LEVEL); SOF_DEFINE_REG_UUID(host); -DECLARE_TR_CTX(host_tr, SOF_UUID(host_uuid), LOG_LEVEL_INFO); - static inline struct dma_sg_elem *next_buffer(struct hc_buf *hc) { if (!hc->elem_array.elems || !hc->elem_array.count) @@ -129,7 +128,7 @@ static int host_copy_one_shot(struct host_data *hd, struct comp_dev *dev, copy_c uint32_t split_value = 0; int ret = 0; - comp_dbg(dev, "host_copy_one_shot()"); + comp_dbg(dev, "entry"); copy_bytes = host_get_copy_bytes_one_shot(hd, dev); if (!copy_bytes) { @@ -197,7 +196,7 @@ static int host_copy_one_shot(struct host_data *hd, struct comp_dev *dev, copy_c uint32_t copy_bytes = 0; int ret = 0; - comp_dbg(dev, "host_copy_one_shot()"); + comp_dbg(dev, "entry"); copy_bytes = host_get_copy_bytes_one_shot(hd, dev); if (!copy_bytes) { @@ -242,7 +241,7 @@ void host_common_update(struct host_data *hd, struct comp_dev *dev, uint32_t byt /* assert dma_buffer_copy succeed */ if (ret < 0) - comp_err(dev, "host_common_update() dma buffer copy failed, dir %d bytes %d avail %d free %d", + comp_err(dev, "dma buffer copy failed, dir %d bytes %d avail %d free %d", dev->direction, bytes, audio_stream_get_avail_samples(&source->stream) * audio_stream_frame_bytes(&source->stream), @@ -252,6 +251,10 @@ void host_common_update(struct host_data *hd, struct comp_dev *dev, uint32_t byt if (ret < 0) return; +#ifdef CONFIG_SOF_TELEMETRY_IO_PERFORMANCE_MEASUREMENTS + io_perf_monitor_update_data(hd->io_perf_host_byte_count, bytes); +#endif + hd->total_data_processed += bytes; /* new local period, update host buffer position blks @@ -344,7 +347,7 @@ static void host_dma_cb(void *arg, enum notify_id type, void *data) struct host_data *hd = comp_get_drvdata(dev); uint32_t bytes = next->elem.size; - comp_dbg(dev, "host_dma_cb() %p", &comp_host); + comp_dbg(dev, "%p", &comp_host); /* update position */ host_common_update(hd, dev, bytes); @@ -411,7 +414,7 @@ static int host_copy_normal(struct host_data *hd, struct comp_dev *dev, copy_cal uint32_t flags = 0; int ret; - comp_dbg(dev, "host_copy_normal()"); + comp_dbg(dev, "entry"); if (hd->copy_type == COMP_COPY_BLOCKING) flags |= DMA_COPY_BLOCKING; @@ -442,7 +445,7 @@ static int create_local_elems(struct host_data *hd, struct comp_dev *dev, uint32 elem_array = &hd->local.elem_array; /* config buffer will be used as proxy */ - err = dma_sg_alloc(&hd->config.elem_array, SOF_MEM_FLAG_USER, + err = dma_sg_alloc(NULL, &hd->config.elem_array, SOF_MEM_FLAG_USER, dir, 1, 0, 0, 0); if (err < 0) { comp_err(dev, "dma_sg_alloc() failed"); @@ -452,7 +455,7 @@ static int create_local_elems(struct host_data *hd, struct comp_dev *dev, uint32 elem_array = &hd->config.elem_array; } - err = dma_sg_alloc(elem_array, SOF_MEM_FLAG_USER, dir, buffer_count, + err = dma_sg_alloc(NULL, elem_array, SOF_MEM_FLAG_USER, dir, buffer_count, buffer_bytes, (uintptr_t)(audio_stream_get_addr(&hd->dma_buffer->stream)), 0); if (err < 0) { @@ -515,7 +518,7 @@ static int host_trigger(struct comp_dev *dev, int cmd) struct host_data *hd = comp_get_drvdata(dev); int ret; - comp_dbg(dev, "host_trigger()"); + comp_dbg(dev, "entry"); ret = comp_set_state(dev, cmd); if (ret < 0) @@ -595,26 +598,34 @@ static struct comp_dev *host_new(const struct comp_driver *drv, e_dev: rfree(hd); e_data: - rfree(dev); + comp_free_device(dev); return NULL; } void host_common_free(struct host_data *hd) { +#ifdef CONFIG_SOF_TELEMETRY_IO_PERFORMANCE_MEASUREMENTS + /* Check for NULL just in case the params() step is omitted */ + if (hd->io_perf_host_byte_count) { + io_perf_monitor_release_slot(hd->io_perf_host_byte_count); + hd->io_perf_host_byte_count = NULL; + } +#endif + dma_put(hd->dma); ipc_msg_free(hd->msg); - dma_sg_free(&hd->config.elem_array); + dma_sg_free(NULL, &hd->config.elem_array); } static void host_free(struct comp_dev *dev) { struct host_data *hd = comp_get_drvdata(dev); - comp_dbg(dev, "host_free()"); + comp_dbg(dev, "entry"); host_common_free(hd); rfree(hd); - rfree(dev); + comp_free_device(dev); } static int host_elements_reset(struct host_data *hd, struct comp_dev *dev) @@ -655,7 +666,7 @@ static int host_verify_params(struct comp_dev *dev, { int ret; - comp_dbg(dev, "host_verify_params()"); + comp_dbg(dev, "entry"); ret = comp_verify_params(dev, 0, params); if (ret < 0) { @@ -764,7 +775,8 @@ int host_common_params(struct host_data *hd, struct comp_dev *dev, goto out; } } else { - hd->dma_buffer = buffer_alloc(buffer_size, SOF_MEM_FLAG_USER | SOF_MEM_FLAG_DMA, + hd->dma_buffer = buffer_alloc(NULL, buffer_size, + SOF_MEM_FLAG_USER | SOF_MEM_FLAG_DMA, addr_align, BUFFER_USAGE_NOT_SHARED); if (!hd->dma_buffer) { comp_err(dev, "failed to alloc dma buffer"); @@ -830,6 +842,25 @@ int host_common_params(struct host_data *hd, struct comp_dev *dev, pcm_get_conversion_function(audio_stream_get_frm_fmt(&hd->local_buffer->stream), audio_stream_get_frm_fmt(&hd->local_buffer->stream)); +#ifdef CONFIG_SOF_TELEMETRY_IO_PERFORMANCE_MEASUREMENTS + if (!hd->io_perf_host_byte_count) { + /* On the Host side, unlike the DAI side, the port direction values (INPUT/OUTPUT) + * match the stream direction enum values (CAPTURE/PLAYBACK), so we can directly + * use params->direction here. + */ + struct io_perf_data_item init_data = { + IO_PERF_HDA_ID, + hd->chan->index, + params->direction, + IO_PERF_POWERED_UP_ENABLED, + IO_PERF_D0IX_POWER_MODE, + 0, 0, 0 + }; + + io_perf_monitor_init_data(&hd->io_perf_host_byte_count, &init_data); + } +#endif + out: hd->cb_dev = dev; @@ -848,7 +879,7 @@ static int host_params(struct comp_dev *dev, struct host_data *hd = comp_get_drvdata(dev); int err; - comp_dbg(dev, "host_params()"); + comp_dbg(dev, "entry"); err = host_verify_params(dev, params); if (err < 0) { @@ -870,7 +901,7 @@ static int host_prepare(struct comp_dev *dev) struct host_data *hd = comp_get_drvdata(dev); int ret; - comp_dbg(dev, "host_prepare()"); + comp_dbg(dev, "entry"); ret = comp_set_state(dev, COMP_TRIGGER_PREPARE); if (ret < 0) @@ -906,9 +937,9 @@ void host_common_reset(struct host_data *hd, uint16_t state) } /* free all DMA elements */ - dma_sg_free(&hd->host.elem_array); - dma_sg_free(&hd->local.elem_array); - dma_sg_free(&hd->config.elem_array); + dma_sg_free(NULL, &hd->host.elem_array); + dma_sg_free(NULL, &hd->local.elem_array); + dma_sg_free(NULL, &hd->config.elem_array); /* It's safe that cleaning out `hd->config` after `dma_sg_free` for config.elem_array */ memset(&hd->config, 0, sizeof(hd->config)); @@ -933,7 +964,7 @@ static int host_reset(struct comp_dev *dev) { struct host_data *hd = comp_get_drvdata(dev); - comp_dbg(dev, "host_reset()"); + comp_dbg(dev, "entry"); host_common_reset(hd, dev->state); dev->state = COMP_STATE_READY; @@ -1004,6 +1035,8 @@ static uint64_t host_get_processed_data(struct comp_dev *dev, uint32_t stream_no return ret; } +DECLARE_TR_CTX(host_tr, SOF_UUID(host_uuid), LOG_LEVEL_INFO); + static const struct comp_driver comp_host = { .type = SOF_COMP_HOST, .uid = SOF_RT_UUID(host_uuid), diff --git a/src/audio/host-zephyr.c b/src/audio/host-zephyr.c index 6bf1fcf78217..59dd1f09b234 100644 --- a/src/audio/host-zephyr.c +++ b/src/audio/host-zephyr.c @@ -24,6 +24,7 @@ #include <rtos/string.h> #include <sof/ut.h> #include <sof/trace/trace.h> +#include <sof/debug/telemetry/performance_monitor.h> #include <ipc/stream.h> #include <ipc/topology.h> #include <user/trace.h> @@ -33,7 +34,6 @@ #include <stdint.h> #if CONFIG_XRUN_NOTIFICATIONS_ENABLE -#include <sof/ipc/notification_pool.h> #include <ipc4/notification.h> #endif @@ -46,8 +46,6 @@ LOG_MODULE_REGISTER(host_comp, CONFIG_SOF_LOG_LEVEL); SOF_DEFINE_REG_UUID(host); -DECLARE_TR_CTX(host_tr, SOF_UUID(host_uuid), LOG_LEVEL_INFO); - static inline struct dma_sg_elem *next_buffer(struct hc_buf *hc) { if (!hc->elem_array.elems || !hc->elem_array.count) @@ -86,7 +84,7 @@ static int host_dma_set_config_and_copy(struct host_data *hd, struct comp_dev *d local_elem->size = bytes; /* reconfigure transfer */ - ret = dma_config(hd->chan->dma->z_dev, hd->chan->index, &hd->z_config); + ret = sof_dma_config(hd->dma, hd->chan_index, &hd->z_config); if (ret < 0) { comp_err(dev, "dma_config() failed, ret = %d", ret); @@ -95,9 +93,9 @@ static int host_dma_set_config_and_copy(struct host_data *hd, struct comp_dev *d cb(dev, bytes); - ret = dma_reload(hd->chan->dma->z_dev, hd->chan->index, 0, 0, bytes); + ret = sof_dma_reload(hd->dma, hd->chan_index, bytes); if (ret < 0) { - comp_err(dev, "dma_copy() failed, ret = %d", + comp_err(dev, "dma_reload() failed, ret = %d", ret); return ret; } @@ -139,7 +137,7 @@ static int host_copy_one_shot(struct host_data *hd, struct comp_dev *dev, copy_c uint32_t split_value; int ret = 0; - comp_dbg(dev, "host_copy_one_shot()"); + comp_dbg(dev, "entry"); copy_bytes = host_get_copy_bytes_one_shot(hd); if (!copy_bytes) { @@ -209,7 +207,7 @@ static int host_copy_one_shot(struct host_data *hd, struct comp_dev *dev, copy_c struct dma_sg_elem *local_elem = hd->config.elem_array.elems; int ret = 0; - comp_dbg(dev, "host_copy_one_shot()"); + comp_dbg(dev, "entry"); copy_bytes = host_get_copy_bytes_one_shot(hd); if (!copy_bytes) { @@ -225,17 +223,17 @@ static int host_copy_one_shot(struct host_data *hd, struct comp_dev *dev, copy_c hd->z_config.head_block->block_size = local_elem->size; /* reconfigure transfer */ - ret = dma_config(hd->chan->dma->z_dev, hd->chan->index, &hd->z_config); + ret = sof_dma_config(hd->dma, hd->chan_index, &hd->z_config); if (ret < 0) { - comp_err(dev, "dma_config() failed, ret = %u", ret); + comp_err(dev, "dma_config() failed, ret = %d", ret); return ret; } cb(dev, copy_bytes); - ret = dma_reload(hd->chan->dma->z_dev, hd->chan->index, 0, 0, copy_bytes); + ret = sof_dma_reload(hd->dma, hd->chan_index, copy_bytes); if (ret < 0) - comp_err(dev, "dma_copy() failed, ret = %u", ret); + comp_err(dev, "dma_reload() failed, ret = %d", ret); return ret; } @@ -265,6 +263,10 @@ void host_common_update(struct host_data *hd, struct comp_dev *dev, uint32_t byt return; } +#ifdef CONFIG_SOF_TELEMETRY_IO_PERFORMANCE_MEASUREMENTS + io_perf_monitor_update_data(hd->io_perf_host_byte_count, bytes); +#endif + hd->total_data_processed += bytes; /* new local period, update host buffer position blks @@ -354,7 +356,7 @@ static void host_dma_cb(struct comp_dev *dev, size_t bytes) { struct host_data *hd = comp_get_drvdata(dev); - comp_cl_dbg(&comp_host, "host_dma_cb() %p", &comp_host); + comp_cl_dbg(&comp_host, "%p", &comp_host); /* update position */ host_common_update(hd, dev, bytes); @@ -367,22 +369,11 @@ static void host_dma_cb(struct comp_dev *dev, size_t bytes) /* get status from dma and check for xrun */ static int host_get_status(struct comp_dev *dev, struct host_data *hd, struct dma_status *stat) { - int ret = dma_get_status(hd->chan->dma->z_dev, hd->chan->index, stat); + int ret = sof_dma_get_status(hd->dma, hd->chan_index, stat); #if CONFIG_XRUN_NOTIFICATIONS_ENABLE if (ret == -EPIPE && !hd->xrun_notification_sent) { - struct ipc_msg *notify = ipc_notification_pool_get(IPC4_RESOURCE_EVENT_SIZE); - - if (notify) { - if (dev->direction == SOF_IPC_STREAM_PLAYBACK) - copier_gateway_underrun_notif_msg_init(notify, - dev->pipeline->pipeline_id); - else - copier_gateway_overrun_notif_msg_init(notify, - dev->pipeline->pipeline_id); - - ipc_msg_send(notify, notify->tx_data, false); - hd->xrun_notification_sent = true; - } + hd->xrun_notification_sent = send_copier_gateway_xrun_notif_msg + (dev->pipeline->pipeline_id, dev->direction); } else if (!ret) { hd->xrun_notification_sent = false; } @@ -408,7 +399,7 @@ static inline bool host_handle_eos(struct host_data *hd, struct comp_dev *dev, */ if (state != AUDIOBUF_STATE_END_OF_STREAM) { audio_buffer_set_eos(buffer); - comp_info(dev, "host_handle_eos() - EOS detected"); + comp_info(dev, "- EOS detected"); } return true; } @@ -437,7 +428,7 @@ static uint32_t host_get_copy_bytes_normal(struct host_data *hd, struct comp_dev /* get data sizes from DMA */ ret = host_get_status(dev, hd, &dma_stat); if (ret < 0) { - comp_err(dev, "dma_get_status() failed, ret = %u", + comp_err(dev, "dma_get_status() failed, ret = %d", ret); /* return 0 copy_bytes in case of error to skip DMA copy */ return 0; @@ -480,27 +471,30 @@ static uint32_t host_get_copy_bytes_normal(struct host_data *hd, struct comp_dev * in order to avoid high load spike * if FAST_MODE is enabled, then one period limitation is omitted */ - if (!(hd->ipc_host.feature_mask & BIT(IPC4_COPIER_FAST_MODE))) - dma_copy_bytes = MIN(hd->period_bytes, dma_copy_bytes); + if (!(hd->ipc_host.feature_mask & BIT(IPC4_COPIER_FAST_MODE))) { + const uint64_t now = k_uptime_get(); + const uint64_t delta = now - hd->nobytes_last_logged; + const bool reset_skipped = delta > SOF_MIN_NO_BYTES_INTERVAL_MS; - const uint64_t now = k_uptime_get(); - const uint64_t delta = now - hd->nobytes_last_logged; - const bool reset_skipped = delta > SOF_MIN_NO_BYTES_INTERVAL_MS; - - if (hd->n_skipped > 1 && (dma_copy_bytes || reset_skipped)) { - comp_warn(dev, "Skipped %u no-bytes events in last %llu ms, bytes %u", - hd->n_skipped - 1, delta, dma_copy_bytes); - hd->n_skipped = 0; - } + dma_copy_bytes = MIN(hd->period_bytes, dma_copy_bytes); - if (!dma_copy_bytes) { - if (!hd->n_skipped || reset_skipped) { - hd->nobytes_last_logged = now; + if (hd->n_skipped > 1 && (dma_copy_bytes || reset_skipped)) { + comp_warn(dev, + "Skipped %u no-bytes events in last %llu ms, bytes %u", + hd->n_skipped - 1, delta, dma_copy_bytes); hd->n_skipped = 0; - comp_warn(dev, "no bytes to copy, available samples: %u, free_samples: %u", - avail_samples, free_samples); } - hd->n_skipped++; + + if (!dma_copy_bytes) { + if (!hd->n_skipped || reset_skipped) { + hd->nobytes_last_logged = now; + hd->n_skipped = 0; + comp_warn(dev, + "no bytes to copy, available samples: %u, free_samples: %u", + avail_samples, free_samples); + } + hd->n_skipped++; + } } /* dma_copy_bytes should be aligned to minimum possible chunk of @@ -556,16 +550,16 @@ static int host_copy_normal(struct host_data *hd, struct comp_dev *dev, copy_cal 0; int ret = 0; - comp_dbg(dev, "host_copy_normal()"); + comp_dbg(dev, "entry"); copy_bytes = host_get_copy_bytes_normal(hd, dev); if (!copy_bytes) { if (hd->partial_size != 0) { if (stream_sync(hd, dev)) { - ret = dma_reload(hd->chan->dma->z_dev, hd->chan->index, 0, 0, - hd->partial_size); + ret = sof_dma_reload(hd->dma, hd->chan_index, + hd->partial_size); if (ret < 0) - comp_err(dev, "dma_reload() failed, ret = %u", ret); + comp_err(dev, "dma_reload() failed, ret = %d", ret); hd->partial_size = 0; } @@ -589,10 +583,10 @@ static int host_copy_normal(struct host_data *hd, struct comp_dev *dev, copy_cal hd->dma_buffer_size - hd->partial_size <= (2 + threshold) * hd->period_bytes) { if (stream_sync(hd, dev)) { - ret = dma_reload(hd->chan->dma->z_dev, hd->chan->index, 0, 0, - hd->partial_size); + ret = sof_dma_reload(hd->dma, hd->chan_index, + hd->partial_size); if (ret < 0) - comp_err(dev, "dma_reload() failed, ret = %u", ret); + comp_err(dev, "dma_reload() failed, ret = %d", ret); hd->partial_size = 0; } @@ -616,7 +610,7 @@ static int create_local_elems(struct host_data *hd, struct comp_dev *dev, elem_array = &hd->local.elem_array; /* config buffer will be used as proxy */ - err = dma_sg_alloc(&hd->config.elem_array, SOF_MEM_FLAG_USER, + err = dma_sg_alloc(NULL, &hd->config.elem_array, SOF_MEM_FLAG_USER, dir, 1, 0, 0, 0); if (err < 0) { comp_err(dev, "dma_sg_alloc() failed"); @@ -626,7 +620,7 @@ static int create_local_elems(struct host_data *hd, struct comp_dev *dev, elem_array = &hd->config.elem_array; } - err = dma_sg_alloc(elem_array, SOF_MEM_FLAG_USER, dir, buffer_count, + err = dma_sg_alloc(NULL, elem_array, SOF_MEM_FLAG_USER, dir, buffer_count, buffer_bytes, (uintptr_t)audio_stream_get_addr(&hd->dma_buffer->stream), 0); if (err < 0) { @@ -657,7 +651,7 @@ int host_common_trigger(struct host_data *hd, struct comp_dev *dev, int cmd) if (cmd != COMP_TRIGGER_START && hd->copy_type == COMP_COPY_ONE_SHOT) return ret; - if (!hd->chan) { + if (hd->chan_index < 0) { comp_err(dev, "no dma channel configured"); return -EINVAL; } @@ -665,14 +659,14 @@ int host_common_trigger(struct host_data *hd, struct comp_dev *dev, int cmd) switch (cmd) { case COMP_TRIGGER_START: hd->partial_size = 0; - ret = dma_start(hd->chan->dma->z_dev, hd->chan->index); + ret = sof_dma_start(hd->dma, hd->chan_index); if (ret < 0) - comp_err(dev, "dma_start() failed, ret = %u", + comp_err(dev, "dma_start() failed, ret = %d", ret); break; case COMP_TRIGGER_STOP: case COMP_TRIGGER_XRUN: - ret = dma_stop(hd->chan->dma->z_dev, hd->chan->index); + ret = sof_dma_stop(hd->dma, hd->chan_index); if (ret < 0) comp_err(dev, "dma stop failed: %d", ret); @@ -689,7 +683,7 @@ static int host_trigger(struct comp_dev *dev, int cmd) struct host_data *hd = comp_get_drvdata(dev); int ret; - comp_dbg(dev, "host_trigger()"); + comp_dbg(dev, "entry"); ret = comp_set_state(dev, cmd); if (ret < 0) @@ -732,7 +726,7 @@ __cold int host_common_new(struct host_data *hd, struct comp_dev *dev, sof_dma_put(hd->dma); return -ENOMEM; } - hd->chan = NULL; + hd->chan_index = -EINVAL; hd->copy_type = COMP_COPY_NORMAL; return 0; @@ -774,7 +768,7 @@ __cold static struct comp_dev *host_new(const struct comp_driver *drv, e_dev: rfree(hd); e_data: - rfree(dev); + comp_free_device(dev); return NULL; } @@ -782,10 +776,25 @@ __cold void host_common_free(struct host_data *hd) { assert_can_be_cold(); +#ifdef CONFIG_SOF_TELEMETRY_IO_PERFORMANCE_MEASUREMENTS + /* Check for NULL just in case the params() step is omitted */ + if (hd->io_perf_host_byte_count) { + io_perf_monitor_release_slot(hd->io_perf_host_byte_count); + hd->io_perf_host_byte_count = NULL; + } +#endif + + /* release DMA channel if not already done by reset */ + if (hd->chan_index >= 0) { + sof_dma_stop(hd->dma, hd->chan_index); + sof_dma_release_channel(hd->dma, hd->chan_index); + hd->chan_index = -EINVAL; + } + sof_dma_put(hd->dma); ipc_msg_free(hd->msg); - dma_sg_free(&hd->config.elem_array); + dma_sg_free(NULL, &hd->config.elem_array); } __cold static void host_free(struct comp_dev *dev) @@ -794,10 +803,10 @@ __cold static void host_free(struct comp_dev *dev) assert_can_be_cold(); - comp_dbg(dev, "host_free()"); + comp_dbg(dev, "entry"); host_common_free(hd); rfree(hd); - rfree(dev); + comp_free_device(dev); } static int host_elements_reset(struct host_data *hd, int direction) @@ -838,7 +847,7 @@ static int host_verify_params(struct comp_dev *dev, { int ret; - comp_dbg(dev, "host_verify_params()"); + comp_dbg(dev, "entry"); ret = comp_verify_params(dev, 0, params); if (ret < 0) { @@ -863,7 +872,7 @@ int host_common_params(struct host_data *hd, struct comp_dev *dev, uint32_t buffer_size_preferred; uint32_t addr_align; uint32_t align; - int i, channel, err; + int i, err; bool is_scheduling_source = dev == dev->pipeline->sched_comp; uint32_t round_up_size; @@ -875,8 +884,8 @@ int host_common_params(struct host_data *hd, struct comp_dev *dev, hd->cont_update_posn = params->cont_update_posn; /* retrieve DMA buffer address alignment */ - err = dma_get_attribute(hd->dma->z_dev, DMA_ATTR_BUFFER_ADDRESS_ALIGNMENT, - &addr_align); + err = sof_dma_get_attribute(hd->dma, DMA_ATTR_BUFFER_ADDRESS_ALIGNMENT, + &addr_align); if (err < 0) { comp_err(dev, "could not get dma buffer address alignment, err = %d", err); @@ -884,7 +893,7 @@ int host_common_params(struct host_data *hd, struct comp_dev *dev, } /* retrieve DMA buffer size alignment */ - err = dma_get_attribute(hd->dma->z_dev, DMA_ATTR_BUFFER_SIZE_ALIGNMENT, &align); + err = sof_dma_get_attribute(hd->dma, DMA_ATTR_BUFFER_SIZE_ALIGNMENT, &align); if (err < 0 || !align) { comp_err(dev, "could not get valid dma buffer alignment, err = %d, align = %u", err, align); @@ -954,7 +963,7 @@ int host_common_params(struct host_data *hd, struct comp_dev *dev, } } else { /* allocate not shared buffer */ - hd->dma_buffer = buffer_alloc_range(buffer_size_preferred, buffer_size, + hd->dma_buffer = buffer_alloc_range(NULL, buffer_size_preferred, buffer_size, SOF_MEM_FLAG_USER | SOF_MEM_FLAG_DMA, addr_align, BUFFER_USAGE_NOT_SHARED); if (!hd->dma_buffer) { @@ -999,22 +1008,16 @@ int host_common_params(struct host_data *hd, struct comp_dev *dev, /* get DMA channel from DMAC * note: stream_tag is ignored by dw-dma */ - channel = dma_request_channel(hd->dma->z_dev, &hda_chan); - if (channel < 0) { + hd->chan_index = sof_dma_request_channel(hd->dma, hda_chan); + if (hd->chan_index < 0) { comp_err(dev, "requested channel %d is busy", hda_chan); return -ENODEV; } - hd->chan = &hd->dma->chan[channel]; uint32_t buffer_addr = 0; uint32_t buffer_bytes = 0; uint32_t addr; - hd->chan->direction = config->direction; - hd->chan->desc_count = config->elem_array.count; - hd->chan->is_scheduling_source = config->is_scheduling_source; - hd->chan->period = config->period; - memset(dma_cfg, 0, sizeof(*dma_cfg)); dma_block_cfg = rzalloc(SOF_MEM_FLAG_USER, @@ -1061,14 +1064,14 @@ int host_common_params(struct host_data *hd, struct comp_dev *dev, break; } - err = dma_config(hd->chan->dma->z_dev, hd->chan->index, dma_cfg); + err = sof_dma_config(hd->dma, hd->chan_index, dma_cfg); if (err < 0) { comp_err(dev, "dma_config() failed"); goto err_free_block_cfg; } - err = dma_get_attribute(hd->dma->z_dev, DMA_ATTR_COPY_ALIGNMENT, - &hd->dma_copy_align); + err = sof_dma_get_attribute(hd->dma, DMA_ATTR_COPY_ALIGNMENT, + &hd->dma_copy_align); if (err < 0) { comp_err(dev, "dma_get_attribute() failed"); @@ -1085,14 +1088,32 @@ int host_common_params(struct host_data *hd, struct comp_dev *dev, hd->copy = hd->copy_type == COMP_COPY_ONE_SHOT ? host_copy_one_shot : host_copy_normal; +#ifdef CONFIG_SOF_TELEMETRY_IO_PERFORMANCE_MEASUREMENTS + if (!hd->io_perf_host_byte_count) { + /* On the Host side, unlike the DAI side, the port direction values (INPUT/OUTPUT) + * match the stream direction enum values (CAPTURE/PLAYBACK), so we can directly + * use params->direction here. + */ + struct io_perf_data_item init_data = { + IO_PERF_HDA_ID, + hd->chan_index, + params->direction, + IO_PERF_POWERED_UP_ENABLED, + IO_PERF_D0IX_POWER_MODE, + 0, 0, 0 + }; + io_perf_monitor_init_data(&hd->io_perf_host_byte_count, &init_data); + } +#endif + return 0; err_free_block_cfg: dma_cfg->head_block = NULL; rfree(dma_block_cfg); err_release_channel: - dma_release_channel(hd->dma->z_dev, hd->chan->index); - hd->chan = NULL; + sof_dma_release_channel(hd->dma, hd->chan_index); + hd->chan_index = -EINVAL; return err; } @@ -1103,7 +1124,7 @@ static int host_params(struct comp_dev *dev, struct host_data *hd = comp_get_drvdata(dev); int err; - comp_dbg(dev, "host_params()"); + comp_dbg(dev, "entry"); err = host_verify_params(dev, params); if (err < 0) { @@ -1125,7 +1146,7 @@ static int host_prepare(struct comp_dev *dev) struct host_data *hd = comp_get_drvdata(dev); int ret; - comp_dbg(dev, "host_prepare()"); + comp_dbg(dev, "entry"); ret = comp_set_state(dev, COMP_TRIGGER_PREPARE); if (ret < 0) @@ -1150,16 +1171,16 @@ static int host_position(struct comp_dev *dev, void host_common_reset(struct host_data *hd, uint16_t state) { - if (hd->chan) { - dma_stop(hd->chan->dma->z_dev, hd->chan->index); - dma_release_channel(hd->dma->z_dev, hd->chan->index); - hd->chan = NULL; + if (hd->chan_index >= 0) { + sof_dma_stop(hd->dma, hd->chan_index); + sof_dma_release_channel(hd->dma, hd->chan_index); + hd->chan_index = -EINVAL; } /* free all DMA elements */ - dma_sg_free(&hd->host.elem_array); - dma_sg_free(&hd->local.elem_array); - dma_sg_free(&hd->config.elem_array); + dma_sg_free(NULL, &hd->host.elem_array); + dma_sg_free(NULL, &hd->local.elem_array); + dma_sg_free(NULL, &hd->config.elem_array); /* free DMA buffer */ if (hd->dma_buffer) { @@ -1185,7 +1206,7 @@ static int host_reset(struct comp_dev *dev) { struct host_data *hd = comp_get_drvdata(dev); - comp_dbg(dev, "host_reset()"); + comp_dbg(dev, "entry"); host_common_reset(hd, dev->state); dev->state = COMP_STATE_READY; @@ -1260,6 +1281,8 @@ static uint64_t host_get_processed_data(struct comp_dev *dev, uint32_t stream_no return ret; } +DECLARE_TR_CTX(host_tr, SOF_UUID(host_uuid), LOG_LEVEL_INFO); + static const struct comp_driver comp_host = { .type = SOF_COMP_HOST, .uid = SOF_RT_UUID(host_uuid), diff --git a/src/audio/igo_nr/README.md b/src/audio/igo_nr/README.md new file mode 100644 index 000000000000..3ab01a6a9cbc --- /dev/null +++ b/src/audio/igo_nr/README.md @@ -0,0 +1,14 @@ +# Intelligo Noise Reduction (IGO NR) Architecture + +This directory contains integration for the Intelligo Noise Reduction algorithms. + +## Overview + +IGO NR is an advanced noise suppression engine targeting microphone input paths to improve speech intelligibility. + +## Configuration and Scripts + +- **Kconfig**: Enables the Intelligo non-speech noise reduction component (`COMP_IGO_NR`), which utilizes a proprietary binary `libigonr.a`. Also provides a `COMP_IGO_NR_STUB` option for testing/CI environments that links a stub library instead of the proprietary binary. +- **CMakeLists.txt**: Handles the build integration. It either links the stub (`igo_nr_stub.c`) or the actual binary (`libigonr.a`) based on the Kconfig selection. Supports Zephyr integration module loading and standard external library compilation (`llext`). +- **igo_nr.toml**: Defines the topology parameters for the module (UUID, module_type, pin configuration, and processing constraints configured under `mod_cfg`). +- **Topology (.conf)**: Derived from `tools/topology/topology2/include/components/igo_nr.conf`, it defines the `igo_nr` widget object for topology generation. Included with mixer controls binding get/put operations (`259`) and defaults to UUID `bc:e2:6a:69:77:28:eb:11:ad:c1:02:42:ac:12:00:02` (type `effect`). diff --git a/src/audio/igo_nr/igo_nr.c b/src/audio/igo_nr/igo_nr.c index fd6d900eaf82..e398af7d964b 100644 --- a/src/audio/igo_nr/igo_nr.c +++ b/src/audio/igo_nr/igo_nr.c @@ -12,7 +12,6 @@ #include <user/trace.h> #include <sof/common.h> #include <rtos/panic.h> -#include <rtos/alloc.h> #include <rtos/init.h> #include <sof/lib/uuid.h> #include <sof/list.h> @@ -45,8 +44,6 @@ LOG_MODULE_REGISTER(igo_nr, CONFIG_SOF_LOG_LEVEL); SOF_DEFINE_REG_UUID(igo_nr); -DECLARE_TR_CTX(igo_nr_tr, SOF_UUID(igo_nr_uuid), LOG_LEVEL_INFO); - static void igo_nr_lib_process(struct comp_data *cd) { /* Pass through the active channel if @@ -380,24 +377,24 @@ static inline int32_t set_capture_func(struct processing_module *mod, struct sof switch (source_get_frm_fmt(source)) { #if CONFIG_FORMAT_S16LE case SOF_IPC_FRAME_S16_LE: - comp_info(dev, "set_capture_func(), SOF_IPC_FRAME_S16_LE"); + comp_info(dev, "SOF_IPC_FRAME_S16_LE"); cd->igo_nr_func = igo_nr_capture_s16; break; #endif #if CONFIG_FORMAT_S24LE case SOF_IPC_FRAME_S24_4LE: - comp_info(dev, "set_capture_func(), SOF_IPC_FRAME_S24_4LE"); + comp_info(dev, "SOF_IPC_FRAME_S24_4LE"); cd->igo_nr_func = igo_nr_capture_s24; break; #endif #if CONFIG_FORMAT_S32LE case SOF_IPC_FRAME_S32_LE: - comp_info(dev, "set_capture_func(), SOF_IPC_FRAME_S32_LE"); + comp_info(dev, "SOF_IPC_FRAME_S32_LE"); cd->igo_nr_func = igo_nr_capture_s32; break; #endif default: - comp_err(dev, "set_capture_func(), invalid frame_fmt"); + comp_err(dev, "invalid frame_fmt"); return -EINVAL; } return 0; @@ -412,16 +409,16 @@ static int igo_nr_init(struct processing_module *mod) size_t bs = cfg->size; int32_t ret; - comp_info(dev, "igo_nr_init()"); + comp_info(dev, "entry"); /* Check first that configuration blob size is sane */ if (bs > SOF_IGO_NR_MAX_SIZE) { - comp_err(dev, "igo_nr_init() error: configuration blob size = %u > %d", + comp_err(dev, "error: configuration blob size = %u > %d", bs, SOF_IGO_NR_MAX_SIZE); return -EINVAL; } - cd = rzalloc(SOF_MEM_FLAG_USER, sizeof(*cd)); + cd = mod_zalloc(mod, sizeof(*cd)); if (!cd) return -ENOMEM; @@ -433,18 +430,18 @@ static int igo_nr_init(struct processing_module *mod) goto cd_fail; } - cd->p_handle = rballoc(SOF_MEM_FLAG_USER, cd->igo_lib_info.handle_size); + cd->p_handle = mod_balloc(mod, cd->igo_lib_info.handle_size); if (!cd->p_handle) { - comp_err(dev, "igo_handle memory rballoc error for size %d", + comp_err(dev, "igo_handle memory mod_balloc error for size %d", cd->igo_lib_info.handle_size); ret = -ENOMEM; goto cd_fail; } /* Handler for configuration data */ - cd->model_handler = comp_data_blob_handler_new(dev); + cd->model_handler = mod_data_blob_handler_new(mod); if (!cd->model_handler) { - comp_err(dev, "comp_data_blob_handler_new() failed."); + comp_err(dev, "mod_data_blob_handler_new() failed."); ret = -ENOMEM; goto cd_fail2; } @@ -463,13 +460,13 @@ static int igo_nr_init(struct processing_module *mod) return 0; cd_fail3: - comp_data_blob_handler_free(cd->model_handler); + mod_data_blob_handler_free(mod, cd->model_handler); cd_fail2: - rfree(cd->p_handle); + mod_free(mod, cd->p_handle); cd_fail: - rfree(cd); + mod_free(mod, cd); return ret; } @@ -477,12 +474,12 @@ static int igo_nr_free(struct processing_module *mod) { struct comp_data *cd = module_get_private_data(mod); - comp_info(mod->dev, "igo_nr_free()"); + comp_info(mod->dev, "entry"); - comp_data_blob_handler_free(cd->model_handler); + mod_data_blob_handler_free(mod, cd->model_handler); - rfree(cd->p_handle); - rfree(cd); + mod_free(mod, cd->p_handle); + mod_free(mod, cd); return 0; } @@ -493,30 +490,30 @@ static int32_t igo_nr_check_params(struct processing_module *mod, struct sof_sou struct comp_data *cd = module_get_private_data(mod); struct comp_dev *dev = mod->dev; - comp_info(dev, "igo_nr_check_params()"); + comp_info(dev, "entry"); /* set source/sink_frames/rate */ cd->source_rate = source_get_rate(source); cd->sink_rate = sink_get_rate(sink); if (source_get_channels(source) != sink_get_channels(sink)) { - comp_err(dev, "igo_nr_check_params(), mismatch source/sink stream channels"); + comp_err(dev, "mismatch source/sink stream channels"); cd->invalid_param = true; } if (!cd->sink_rate) { - comp_err(dev, "igo_nr_check_params(), zero sink rate"); + comp_err(dev, "zero sink rate"); return -EINVAL; } /* The igo_nr supports sample rate 48000 only. */ switch (cd->source_rate) { case 48000: - comp_info(dev, "igo_nr_check_params(), sample rate = 48000"); + comp_info(dev, "sample rate = 48000"); cd->invalid_param = false; break; default: - comp_err(dev, "igo_nr_check_params(), invalid sample rate"); + comp_err(dev, "invalid sample rate"); cd->invalid_param = true; } @@ -529,10 +526,10 @@ static int32_t igo_nr_check_config_validity(struct comp_dev *dev, struct sof_igo_nr_config *p_config = comp_get_data_blob(cd->model_handler, NULL, NULL); if (!p_config) { - comp_err(dev, "igo_nr_check_config_validity() error: invalid cd->model_handler"); + comp_err(dev, "error: invalid cd->model_handler"); return -EINVAL; } else if (p_config->active_channel_idx >= SOF_IPC_MAX_CHANNELS) { - comp_err(dev, "igo_nr_check_config_validity() error: invalid active_channel_idxs"); + comp_err(dev, "error: invalid active_channel_idxs"); return -EINVAL; } else { return 0; @@ -566,13 +563,13 @@ static int igo_nr_get_config(struct processing_module *mod, switch (cdata->cmd) { case SOF_CTRL_CMD_BINARY: - comp_info(dev, "igo_nr_get_config(), SOF_CTRL_CMD_BINARY"); + comp_info(dev, "SOF_CTRL_CMD_BINARY"); return comp_data_blob_get_cmd(cd->model_handler, cdata, fragment_size); case SOF_CTRL_CMD_SWITCH: for (j = 0; j < cdata->num_elems; j++) { cdata->chanv[j].channel = j; cdata->chanv[j].value = cd->process_enable[j]; - comp_info(dev, "igo_nr_get_config(), channel = %u, value = %u", + comp_info(dev, "channel = %u, value = %u", cdata->chanv[j].channel, cdata->chanv[j].value); } @@ -629,7 +626,7 @@ static int igo_nr_set_config(struct processing_module *mod, uint32_t param_id, struct comp_dev *dev = mod->dev; int ret; - comp_info(dev, "igo_nr_set_config()"); + comp_info(dev, "entry"); #if CONFIG_IPC_MAJOR_3 struct sof_ipc_ctrl_data *cdata = (struct sof_ipc_ctrl_data *)fragment; @@ -660,7 +657,7 @@ static int igo_nr_set_config(struct processing_module *mod, uint32_t param_id, comp_info(dev, "igo_nr_cmd_set_config(), SOF_IPC4_SWITCH_CONTROL_PARAM_ID"); return igo_nr_set_chan(mod, ctl); case SOF_IPC4_ENUM_CONTROL_PARAM_ID: - comp_err(dev, "igo_nr_set_config(), illegal control."); + comp_err(dev, "illegal control."); return -EINVAL; } @@ -721,7 +718,7 @@ static void igo_nr_set_igo_params(struct processing_module *mod) struct sof_igo_nr_config *p_config = comp_get_data_blob(cd->model_handler, NULL, NULL); struct comp_dev *dev = mod->dev; - comp_info(dev, "igo_nr_set_igo_params()"); + comp_info(dev, "entry"); igo_nr_check_config_validity(dev, cd); if (p_config) { @@ -816,7 +813,7 @@ static int32_t igo_nr_prepare(struct processing_module *mod, struct sof_sink *sink = sinks[0]; int ret; - comp_dbg(dev, "igo_nr_prepare()"); + comp_dbg(dev, "entry"); if (!source || !sink) { comp_err(dev, "no source or sink"); @@ -835,8 +832,7 @@ static int32_t igo_nr_prepare(struct processing_module *mod, if (ret) return ret; - source_set_alignment_constants(source, 1, IGO_FRAME_SIZE); - sink_set_alignment_constants(sink, 1, IGO_FRAME_SIZE); + source_set_alignment_constants(source, SOF_FRAME_BYTE_ALIGN, IGO_FRAME_SIZE); igo_nr_set_igo_params(mod); @@ -867,7 +863,7 @@ static int igo_nr_reset(struct processing_module *mod) { struct comp_data *cd = module_get_private_data(mod); - comp_info(mod->dev, "igo_nr_reset()"); + comp_info(mod->dev, "entry"); cd->igo_nr_func = NULL; @@ -902,6 +898,7 @@ SOF_LLEXT_BUILDINFO; #else +DECLARE_TR_CTX(igo_nr_tr, SOF_UUID(igo_nr_uuid), LOG_LEVEL_INFO); DECLARE_MODULE_ADAPTER(igo_nr_interface, igo_nr_uuid, igo_nr_tr); SOF_MODULE_INIT(igo_nr, sys_comp_module_igo_nr_interface_init); diff --git a/src/audio/kpb.c b/src/audio/kpb.c index e8a8bb104818..9dca05630da1 100644 --- a/src/audio/kpb.c +++ b/src/audio/kpb.c @@ -70,8 +70,6 @@ SOF_DEFINE_REG_UUID(kpb); #define KPB_UUID kpb_uuid #endif -DECLARE_TR_CTX(kpb_tr, SOF_UUID(KPB_UUID), LOG_LEVEL_INFO); - SOF_DEFINE_REG_UUID(kpb_task); /* KPB private data, runtime data */ @@ -191,7 +189,7 @@ static void kpb_ams_kpd_notification(const struct ams_message_payload *const ams struct kpb_client *cli_data = (struct kpb_client *)ams_message_payload->message; struct comp_dev *dev = ctx; - comp_dbg(dev, "kpb_ams_kpd_notification()"); + comp_dbg(dev, "entry"); kpb_init_draining(dev, cli_data); } @@ -287,7 +285,7 @@ static void kpb_set_params(struct comp_dev *dev, struct comp_data *kpb = comp_get_drvdata(dev); enum sof_ipc_frame frame_fmt, valid_fmt; - comp_dbg(dev, "kpb_set_params()"); + comp_dbg(dev, "entry"); memset_s(params, sizeof(*params), 0, sizeof(*params)); params->channels = kpb->ipc4_cfg.base_cfg.audio_fmt.channels_count; @@ -347,7 +345,7 @@ static int kpb_bind(struct comp_dev *dev, struct bind_info *bind_data) int buf_id; int ret = 0; - comp_dbg(dev, "kpb_bind()"); + comp_dbg(dev, "entry"); bu = bind_data->ipc4_data; buf_id = IPC4_COMP_ID(bu->extension.r.src_queue, bu->extension.r.dst_queue); @@ -393,7 +391,7 @@ static int kpb_unbind(struct comp_dev *dev, struct bind_info *unbind_data) struct ipc4_module_bind_unbind *bu; int buf_id; - comp_dbg(dev, "kpb_unbind()"); + comp_dbg(dev, "entry"); bu = unbind_data->ipc4_data; buf_id = IPC4_COMP_ID(bu->extension.r.src_queue, bu->extension.r.dst_queue); @@ -503,7 +501,7 @@ static struct comp_dev *kpb_new(const struct comp_driver *drv, kpb = rzalloc(SOF_MEM_FLAG_USER, sizeof(*kpb)); if (!kpb) { - rfree(dev); + comp_free_device(dev); return NULL; } @@ -511,7 +509,7 @@ static struct comp_dev *kpb_new(const struct comp_driver *drv, ret = kpb_set_verify_ipc_params(dev, ipc_process); if (ret) { - rfree(dev); + comp_free_device(dev); return NULL; } @@ -546,7 +544,7 @@ static struct comp_dev *kpb_new(const struct comp_driver *drv, /* retrieve params from the base config for IPC4 */ ret = kpb_params(dev, ¶ms); if (ret < 0) { - rfree(dev); + comp_free_device(dev); return NULL; } #endif @@ -576,7 +574,7 @@ static size_t kpb_allocate_history_buffer(struct comp_data *kpb, int i = 0; size_t allocated_size = 0; - comp_cl_info(&comp_kpb, "kpb_allocate_history_buffer()"); + comp_cl_info(&comp_kpb, "entry"); /* Initialize history buffer */ kpb->hd.c_hb = rzalloc(SOF_MEM_FLAG_USER, @@ -667,7 +665,7 @@ static void kpb_free_history_buffer(struct history_buffer *buff) struct history_buffer *_buff; struct history_buffer *first_buff = buff; - comp_cl_info(&comp_kpb, "kpb_free_history_buffer()"); + comp_cl_info(&comp_kpb, "entry"); if (!buff) return; @@ -694,7 +692,7 @@ static void kpb_free(struct comp_dev *dev) { struct comp_data *kpb = comp_get_drvdata(dev); - comp_info(dev, "kpb_free()"); + comp_info(dev, "entry"); #if CONFIG_AMS /* Unregister KPB as AMS consumer */ @@ -722,7 +720,7 @@ static void kpb_free(struct comp_dev *dev) /* Free KPB */ rfree(kpb); - rfree(dev); + comp_free_device(dev); } /** @@ -733,7 +731,7 @@ static void kpb_free(struct comp_dev *dev) */ static int kpb_trigger(struct comp_dev *dev, int cmd) { - comp_info(dev, "kpb_trigger()"); + comp_info(dev, "entry"); return comp_set_state(dev, cmd); } @@ -743,7 +741,7 @@ static int kpb_verify_params(struct comp_dev *dev, { int ret; - comp_dbg(dev, "kpb_verify_params()"); + comp_dbg(dev, "entry"); ret = comp_verify_params(dev, 0, params); if (ret < 0) { @@ -806,7 +804,7 @@ static int kpb_prepare(struct comp_dev *dev) int i; size_t hb_size_req = KPB_MAX_BUFFER_SIZE(kpb->config.sampling_width, kpb->config.channels); - comp_dbg(dev, "kpb_prepare()"); + comp_dbg(dev, "entry"); /* retrieve the params from the base_cfg and update the source/sink buffer params */ kpb_set_params(dev, ¶ms); @@ -1211,7 +1209,7 @@ static int kpb_copy(struct comp_dev *dev) uint32_t avail_bytes; uint32_t channels = kpb->config.channels; - comp_dbg(dev, "kpb_copy()"); + comp_dbg(dev, "entry"); if (list_is_empty(&dev->bsource_list)) { comp_err(dev, "no source."); @@ -1411,7 +1409,7 @@ static int kpb_buffer_data(struct comp_dev *dev, enum kpb_state state_preserved = kpb->state; size_t sample_width = kpb->config.sampling_width; - comp_dbg(dev, "kpb_buffer_data()"); + comp_dbg(dev, "entry"); /* We are allowed to buffer data in internal history buffer * only in KPB_STATE_RUN, KPB_STATE_DRAINING or KPB_STATE_INIT_DRAINING @@ -1566,7 +1564,7 @@ static int kpb_register_client(struct comp_data *kpb, struct kpb_client *cli) { int ret = 0; - comp_cl_info(&comp_kpb, "kpb_register_client()"); + comp_cl_info(&comp_kpb, "entry"); if (!cli) { comp_cl_err(&comp_kpb, "no client data"); @@ -1720,10 +1718,10 @@ static void kpb_init_draining(struct comp_dev *dev, struct kpb_client *cli) /* Unlimited draining */ drain_interval = 0; period_bytes_limit = 0; - comp_info(dev, "kpb_init_draining: unlimited draining speed selected."); + comp_info(dev, "unlimited draining speed selected."); } - comp_info(dev, "kpb_init_draining(), schedule draining task"); + comp_info(dev, "schedule draining task"); /* Add one-time draining task into the scheduler. */ kpb->draining_task_data.sink = kpb->host_sink; @@ -1848,7 +1846,7 @@ static enum task_state kpb_draining_task(void *arg) k_sched_lock(); #endif - comp_cl_dbg(&comp_kpb, "kpb_draining_task()"); + comp_cl_dbg(&comp_kpb, "entry"); /* Have we received reset request? */ if (kpb->state == KPB_STATE_RESETTING) { @@ -1968,10 +1966,10 @@ static enum task_state kpb_draining_task(void *arg) draining_time_ms = k_cyc_to_ms_near64(draining_time_end - draining_data->draining_time_start); if (draining_time_ms <= UINT_MAX) - comp_cl_info(&comp_kpb, "KPB: kpb_draining_task(), done. %zu drained in %u ms", + comp_cl_info(&comp_kpb, "KPB: done. %zu drained in %u ms", draining_data->drained, (unsigned int)draining_time_ms); else - comp_cl_info(&comp_kpb, "KPB: kpb_draining_task(), done. %zu drained in > %u ms", + comp_cl_info(&comp_kpb, "KPB: done. %zu drained in > %u ms", draining_data->drained, UINT_MAX); /* Restore original EDF thread priority */ @@ -2212,7 +2210,7 @@ static void kpb_clear_history_buffer(struct history_buffer *buff) void *start_addr; size_t size; - comp_cl_info(&comp_kpb, "kpb_clear_history_buffer()"); + comp_cl_info(&comp_kpb, "entry"); do { start_addr = buff->start_addr; @@ -2363,7 +2361,7 @@ static void kpb_reset_history_buffer(struct history_buffer *buff) { struct history_buffer *first_buff = buff; - comp_cl_info(&comp_kpb, "kpb_reset_history_buffer()"); + comp_cl_info(&comp_kpb, "entry"); if (!buff) return; @@ -2655,7 +2653,7 @@ static int kpb_set_large_config(struct comp_dev *dev, uint32_t param_id, /* We can use extended param id for both extended and standard param id */ union ipc4_extended_param_id extended_param_id; - comp_info(dev, "kpb_set_large_config()"); + comp_info(dev, "entry"); extended_param_id.full = param_id; @@ -2679,6 +2677,8 @@ static int kpb_set_large_config(struct comp_dev *dev, uint32_t param_id, } } +DECLARE_TR_CTX(kpb_tr, SOF_UUID(KPB_UUID), LOG_LEVEL_INFO); + static const struct comp_driver comp_kpb = { .type = SOF_COMP_KPB, .uid = SOF_RT_UUID(KPB_UUID), diff --git a/src/audio/level_multiplier/README.md b/src/audio/level_multiplier/README.md new file mode 100644 index 000000000000..278b895d099c --- /dev/null +++ b/src/audio/level_multiplier/README.md @@ -0,0 +1,14 @@ +# Level Multiplier Architecture + +This directory contains the Level Multiplier component. + +## Overview + +Applies simple digital gain/attenuation (multiplication) to an audio signal stream. + +## Configuration and Scripts + +- **Kconfig**: Enables the Level multiplier component (`COMP_LEVEL_MULTIPLIER`). This applies a configured fixed gain to an audio stream, typically to increase capture sensitivity of voice applications compared to media capture. +- **CMakeLists.txt**: Manages local base sources and optimized implementations (`level_multiplier-generic.c`, `level_multiplier-hifi3.c`, `level_multiplier-hifi5.c`). Includes the IPC version 4 wrapper (`level_multiplier-ipc4.c`) when `CONFIG_IPC_MAJOR_4` is present, and supports `llext`. +- **level_multiplier.toml**: Holds topology module entry parameters including UUIDs, standard pin layouts, and `mod_cfg` limits. +- **Topology (.conf)**: `tools/topology/topology2/include/components/level_multiplier.conf` configures the `level_multiplier` widget object. It specifies `num_input_pins` and `num_output_pins` mapping and defaults to type `effect` with UUID `56:74:39:30:61:46:44:46:97:e5:39:a9:e5:ab:17:78`. diff --git a/src/audio/level_multiplier/level_multiplier.c b/src/audio/level_multiplier/level_multiplier.c index 68edff07a96e..4fca482ced72 100644 --- a/src/audio/level_multiplier/level_multiplier.c +++ b/src/audio/level_multiplier/level_multiplier.c @@ -17,11 +17,6 @@ SOF_DEFINE_REG_UUID(level_multiplier); /* Creates logging data for the component */ LOG_MODULE_REGISTER(level_multiplier, CONFIG_SOF_LOG_LEVEL); -/* Creates the component trace. Traces show in trace console the component - * info, warning, and error messages. - */ -DECLARE_TR_CTX(level_multiplier_tr, SOF_UUID(level_multiplier_uuid), LOG_LEVEL_INFO); - /** * level_multiplier_init() - Initialize the level_multiplier component. * @mod: Pointer to module data. @@ -38,7 +33,7 @@ __cold static int level_multiplier_init(struct processing_module *mod) struct comp_dev *dev = mod->dev; struct level_multiplier_comp_data *cd; - comp_info(dev, "level_multiplier_init()"); + comp_info(dev, "entry"); cd = mod_alloc(mod, sizeof(*cd)); if (!cd) @@ -75,7 +70,7 @@ static int level_multiplier_process(struct processing_module *mod, int frames = source_get_data_frames_available(source); int sink_frames = sink_get_free_frames(sink); - comp_dbg(dev, "level_multiplier_process()"); + comp_dbg(dev, "entry"); frames = MIN(frames, sink_frames); frames = MIN(frames, dev->frames); @@ -111,7 +106,7 @@ static int level_multiplier_prepare(struct processing_module *mod, struct comp_dev *dev = mod->dev; enum sof_ipc_frame source_format; - comp_dbg(dev, "level_multiplier_prepare()"); + comp_dbg(dev, "entry"); /* The processing example in this component supports one input and one * output. Generally there can be more. @@ -147,7 +142,7 @@ static int level_multiplier_reset(struct processing_module *mod) { struct level_multiplier_comp_data *cd = module_get_private_data(mod); - comp_dbg(mod->dev, "level_multiplier_reset()"); + comp_dbg(mod->dev, "entry"); memset(cd, 0, sizeof(*cd)); cd->gain = LEVEL_MULTIPLIER_GAIN_ONE; @@ -171,7 +166,7 @@ __cold static int level_multiplier_free(struct processing_module *mod) assert_can_be_cold(); - comp_dbg(mod->dev, "level_multiplier_free()"); + comp_dbg(mod->dev, "entry"); mod_free(mod, cd); return 0; } @@ -205,6 +200,7 @@ SOF_LLEXT_BUILDINFO; #else +DECLARE_TR_CTX(level_multiplier_tr, SOF_UUID(level_multiplier_uuid), LOG_LEVEL_INFO); DECLARE_MODULE_ADAPTER(level_multiplier_interface, level_multiplier_uuid, level_multiplier_tr); SOF_MODULE_INIT(level_multiplier, sys_comp_module_level_multiplier_interface_init); diff --git a/src/audio/mfcc/Kconfig b/src/audio/mfcc/Kconfig index 678331896b5f..f56cadb40de2 100644 --- a/src/audio/mfcc/Kconfig +++ b/src/audio/mfcc/Kconfig @@ -4,7 +4,8 @@ config COMP_MFCC tristate "MFCC component" depends on COMP_MODULE_ADAPTER select CORDIC_FIXED - select MATH_16BIT_MEL_FILTERBANK + select MATH_32BIT_FFT + select MATH_32BIT_MEL_FILTERBANK select MATH_AUDITORY select MATH_DCT select MATH_DECIBELS diff --git a/src/audio/mfcc/README.md b/src/audio/mfcc/README.md new file mode 100644 index 000000000000..31f9c331e545 --- /dev/null +++ b/src/audio/mfcc/README.md @@ -0,0 +1,25 @@ +# MFCC Feature Extraction Architecture + +This directory contains the Mel-Frequency Cepstral Coefficients (MFCC) feature extractor. + +## Overview + +MFCC extracts audio features commonly used as inputs for machine learning models, such as wake-word detection or speech recognition. + +## Architecture Diagram + +```mermaid +graph LR + In[Audio Frame] --> Win[Windowing] + Win --> FFT[FFT] + FFT --> Mel[Mel Filterbank] + Mel --> DCT[DCT] + DCT --> Out[MFCC Output Features] +``` + +## Configuration and Scripts + +- **Kconfig**: Enables the MFCC component (`COMP_MFCC`) which selects necessary math libraries (`MATH_FFT`, `MATH_DCT`, `MATH_16BIT_MEL_FILTERBANK`, etc.). Depends on `COMP_MODULE_ADAPTER`. +- **CMakeLists.txt**: Compiles generic, common, and HIFI implementations (`mfcc_hifi3.c`, `mfcc_hifi4.c`). Provides support for Zephyr loadable extensions (`llext`). +- **mfcc.toml**: Specifies the topology configuration for the MFCC module (UUID, affinity, memory parameters, and pin formats). +- **Topology (.conf)**: Derived from `tools/topology/topology2/include/components/mfcc.conf`, configuring a `mfcc` widget object of type `effect` with UUID `73:a7:10:db:a4:1a:ea:4c:a2:1f:2d:57:a5:c9:82:eb`. diff --git a/src/audio/mfcc/mfcc.c b/src/audio/mfcc/mfcc.c index da1fa9f3414e..ea09d919009b 100644 --- a/src/audio/mfcc/mfcc.c +++ b/src/audio/mfcc/mfcc.c @@ -36,17 +36,15 @@ LOG_MODULE_REGISTER(mfcc, CONFIG_SOF_LOG_LEVEL); SOF_DEFINE_REG_UUID(mfcc); -DECLARE_TR_CTX(mfcc_tr, SOF_UUID(mfcc_uuid), LOG_LEVEL_INFO); - __cold_rodata const struct mfcc_func_map mfcc_fm[] = { #if CONFIG_FORMAT_S16LE - {SOF_IPC_FRAME_S16_LE, mfcc_s16_default}, + {SOF_IPC_FRAME_S16_LE, mfcc_s16_default}, #endif /* CONFIG_FORMAT_S16LE */ #if CONFIG_FORMAT_S24LE - {SOF_IPC_FRAME_S24_4LE, NULL}, + {SOF_IPC_FRAME_S24_4LE, mfcc_s24_default}, #endif /* CONFIG_FORMAT_S24LE */ #if CONFIG_FORMAT_S32LE - {SOF_IPC_FRAME_S32_LE, NULL}, + {SOF_IPC_FRAME_S32_LE, mfcc_s32_default}, #endif /* CONFIG_FORMAT_S32LE */ }; @@ -74,58 +72,34 @@ static int mfcc_init(struct processing_module *mod) { struct module_data *md = &mod->priv; struct comp_dev *dev = mod->dev; - struct module_config *cfg = &md->cfg; struct mfcc_comp_data *cd = NULL; - size_t bs = cfg->size; - int ret; - - comp_info(dev, "mfcc_init()"); - /* Check first that configuration blob size is sane */ - if (bs > SOF_MFCC_CONFIG_MAX_SIZE) { - comp_err(dev, "mfcc_init() error: configuration blob size %zu exceeds %d", - bs, SOF_MFCC_CONFIG_MAX_SIZE); - return -EINVAL; - } + comp_info(dev, "entry"); - cd = rzalloc(SOF_MEM_FLAG_USER, sizeof(*cd)); + cd = mod_zalloc(mod, sizeof(*cd)); if (!cd) return -ENOMEM; /* Handler for configuration data */ md->private = cd; - cd->model_handler = comp_data_blob_handler_new(dev); + cd->model_handler = mod_data_blob_handler_new(mod); if (!cd->model_handler) { comp_err(dev, "comp_data_blob_handler_new() failed."); - ret = -ENOMEM; - goto err; - } - - /* Get configuration data */ - ret = comp_init_data_blob(cd->model_handler, bs, cfg->init_data); - if (ret < 0) { - comp_err(mod->dev, "comp_init_data_blob() failed."); - goto err_init; + mod_free(mod, cd); + return -ENOMEM; } return 0; - -err_init: - comp_data_blob_handler_free(cd->model_handler); - -err: - rfree(cd); - return ret; } static int mfcc_free(struct processing_module *mod) { struct mfcc_comp_data *cd = module_get_private_data(mod); - comp_info(mod->dev, "mfcc_free()"); - comp_data_blob_handler_free(cd->model_handler); - mfcc_free_buffers(cd); - rfree(cd); + comp_info(mod->dev, "entry"); + mod_data_blob_handler_free(mod, cd->model_handler); + mfcc_free_buffers(mod); + mod_free(mod, cd); return 0; } @@ -136,7 +110,7 @@ static int mfcc_get_config(struct processing_module *mod, struct sof_ipc_ctrl_data *cdata = (struct sof_ipc_ctrl_data *)fragment; struct mfcc_comp_data *cd = module_get_private_data(mod); - comp_info(mod->dev, "mfcc_get_config()"); + comp_info(mod->dev, "entry"); return comp_data_blob_get_cmd(cd->model_handler, cdata, fragment_size); } @@ -148,7 +122,7 @@ static int mfcc_set_config(struct processing_module *mod, uint32_t config_id, { struct mfcc_comp_data *cd = module_get_private_data(mod); - comp_info(mod->dev, "mfcc_set_config()"); + comp_info(mod->dev, "entry"); return comp_data_blob_set(cd->model_handler, pos, data_offset_size, fragment, fragment_size); @@ -163,7 +137,7 @@ static int mfcc_process(struct processing_module *mod, struct audio_stream *sink = output_buffers->data; int frames = input_buffers->size; - comp_dbg(mod->dev, "mfcc_process(), start"); + comp_dbg(mod->dev, "start"); frames = MIN(frames, cd->max_frames); cd->mfcc_func(mod, input_buffers, output_buffers, frames); @@ -171,7 +145,7 @@ static int mfcc_process(struct processing_module *mod, /* TODO: use module_update_buffer_position() from #6194 */ input_buffers->consumed += audio_stream_frame_bytes(source) * frames; output_buffers->size += audio_stream_frame_bytes(sink) * frames; - comp_dbg(mod->dev, "mfcc_process(), done"); + comp_dbg(mod->dev, "done"); return 0; } @@ -185,10 +159,10 @@ static int mfcc_prepare(struct processing_module *mod, struct comp_dev *dev = mod->dev; enum sof_ipc_frame source_format; enum sof_ipc_frame sink_format; - uint32_t sink_period_bytes; + size_t data_size; int ret; - comp_info(dev, "mfcc_prepare()"); + comp_info(dev, "entry"); /* MFCC component will only ever have 1 source and 1 sink buffer */ sourceb = comp_dev_get_first_data_producer(dev); @@ -203,31 +177,23 @@ static int mfcc_prepare(struct processing_module *mod, /* get sink data format and period bytes */ sink_format = audio_stream_get_frm_fmt(&sinkb->stream); - sink_period_bytes = audio_stream_period_bytes(&sinkb->stream, dev->frames); - comp_info(dev, "mfcc_prepare(), source_format = %d, sink_format = %d", - source_format, sink_format); - if (audio_stream_get_size(&sinkb->stream) < sink_period_bytes) { - comp_err(dev, "sink buffer size %d is insufficient < %d", - audio_stream_get_size(&sinkb->stream), sink_period_bytes); - ret = -ENOMEM; - goto err; - } + comp_info(dev, "source_format = %d, sink_format = %d", source_format, sink_format); - cd->config = comp_get_data_blob(cd->model_handler, NULL, NULL); + cd->config = comp_get_data_blob(cd->model_handler, &data_size, NULL); /* Initialize MFCC, max_frames is set to dev->frames + 4 */ - if (cd->config) { + if (cd->config && data_size > 0) { ret = mfcc_setup(mod, dev->frames + 4, audio_stream_get_rate(&sourceb->stream), audio_stream_get_channels(&sourceb->stream)); if (ret < 0) { - comp_err(dev, "mfcc_prepare(), setup failed."); + comp_err(dev, "setup failed."); goto err; } } cd->mfcc_func = mfcc_find_func(source_format, sink_format, mfcc_fm, ARRAY_SIZE(mfcc_fm)); if (!cd->mfcc_func) { - comp_err(dev, "mfcc_prepare(), No proc func"); + comp_err(dev, "No proc func"); ret = -EINVAL; goto err; } @@ -243,7 +209,7 @@ static int mfcc_reset(struct processing_module *mod) { struct mfcc_comp_data *cd = module_get_private_data(mod); - comp_info(mod->dev, "mfcc_reset()"); + comp_info(mod->dev, "entry"); /* Reset to similar state as init() */ cd->mfcc_func = NULL; @@ -274,6 +240,7 @@ SOF_LLEXT_BUILDINFO; #else +DECLARE_TR_CTX(mfcc_tr, SOF_UUID(mfcc_uuid), LOG_LEVEL_INFO); DECLARE_MODULE_ADAPTER(mfcc_interface, mfcc_uuid, mfcc_tr); SOF_MODULE_INIT(mfcc, sys_comp_module_mfcc_interface_init); diff --git a/src/audio/mfcc/mfcc_common.c b/src/audio/mfcc/mfcc_common.c index 63ab84943669..1079864e9259 100644 --- a/src/audio/mfcc/mfcc_common.c +++ b/src/audio/mfcc/mfcc_common.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: BSD-3-Clause // -// Copyright(c) 2023 Intel Corporation. All rights reserved. +// Copyright(c) 2023-2026 Intel Corporation. // // Author: Andrula Song <andrula.song@intel.com> @@ -8,7 +8,9 @@ #include <sof/audio/component.h> #include <sof/audio/audio_stream.h> +#include <sof/audio/format.h> #include <sof/math/auditory.h> +#include <sof/math/fft.h> #include <sof/math/matrix.h> #include <sof/math/sqrt.h> #include <sof/math/trig.h> @@ -20,37 +22,32 @@ #include <stdint.h> LOG_MODULE_REGISTER(mfcc_common, CONFIG_SOF_LOG_LEVEL); -/* MFCC with 16 bit FFT benefits from data normalize, for 32 bits there's no - * significant impact. The amount of left shifts for FFT input is limited to - * 10 that equals about 60 dB boost. The boost is compensated in Mel energy - * calculation. - */ -#if MFCC_FFT_BITS == 16 -#define MFCC_NORMALIZE_FFT -#else -#undef MFCC_NORMALIZE_FFT -#endif -#define MFCC_NORMALIZE_MAX_SHIFT 10 /* * The main processing function for MFCC */ -static int mfcc_stft_process(const struct comp_dev *dev, struct mfcc_state *state) +static int mfcc_stft_process(const struct comp_dev *dev, struct mfcc_comp_data *cd) { + struct sof_mfcc_config *config = cd->config; + struct mfcc_state *state = &cd->state; struct mfcc_buffer *buf = &state->buf; struct mfcc_fft *fft = &state->fft; int mel_scale_shift; int input_shift; - int i; + int j; int m; int cc_count = 0; + int64_t s; + int32_t mel_value; + int32_t peak; + int32_t clamp_value; /* Phase 1, wait until whole fft_size is filled with valid data. This way * first output cepstral coefficients originate from streamed data and not * from buffers with zero data. */ - comp_dbg(dev, "mfcc_stft_process(), avail = %d", buf->s_avail); + comp_dbg(dev, "avail = %d", buf->s_avail); if (state->waiting_fill) { if (buf->s_avail < fft->fft_size) return 0; @@ -66,9 +63,9 @@ static int mfcc_stft_process(const struct comp_dev *dev, struct mfcc_state *stat state->prev_samples_valid = true; } - /* Check if enough samples in buffer for FFT hop */ + /* Check if enough samples in buffer for one FFT hop */ m = buf->s_avail / fft->fft_hop_size; - for (i = 0; i < m; i++) { + if (m > 0) { /* Clear FFT input buffer because it has been used as scratch */ bzero(fft->fft_buf, fft->fft_buffer_size); @@ -79,12 +76,7 @@ static int mfcc_stft_process(const struct comp_dev *dev, struct mfcc_state *stat /* TODO: use_energy & raw_energy */ -#ifdef MFCC_NORMALIZE_FFT - /* Find block scale left shift for FFT input */ - input_shift = mfcc_normalize_fft_buffer(state); -#else input_shift = 0; -#endif /* Window function */ mfcc_apply_window(state, input_shift); @@ -97,49 +89,177 @@ static int mfcc_stft_process(const struct comp_dev *dev, struct mfcc_state *stat bzero(fft->fft_out, fft->fft_buffer_size); /* Compute FFT */ -#if MFCC_FFT_BITS == 16 - fft_execute_16(fft->fft_plan, false); -#else fft_execute_32(fft->fft_plan, false); -#endif - /* Convert powerspectrum to Mel band logarithmic spectrum */ - mat_init_16b(state->mel_spectra, 1, state->dct.num_in, 7); /* Q8.7 */ + /* Initialize 16-bit Mel log spectrum buffer in Q9.7. The Mel values + * are converted from Q9.23 to Q9.7 for DCT matrix multiplication. + */ + mat_init_16b(state->mel_spectra, 1, state->dct.num_in, 7); /* Q9.7 */ /* Compensate FFT lib scaling to Mel log values, e.g. for 512 long FFT * the fft_plan->len is 9. The scaling is 1/512. Subtract from input_shift it * to add the missing "gain". */ mel_scale_shift = input_shift - fft->fft_plan->len; -#if MFCC_FFT_BITS == 16 - psy_apply_mel_filterbank_16(&state->melfb, fft->fft_out, state->power_spectra, - state->mel_spectra->data, mel_scale_shift); -#else psy_apply_mel_filterbank_32(&state->melfb, fft->fft_out, state->power_spectra, - state->mel_spectra->data, mel_scale_shift); -#endif + state->mel_log_32, mel_scale_shift); + + if (state->mel_only) { + /* In Mel-only mode output Mel log spectra directly */ + cc_count += state->dct.num_in; + + /* Find peak mel value and track state->mmax in Q9.23 */ + if (config->dynamic_mmax) { + peak = state->mel_log_32[0]; + for (j = 1; j < state->dct.num_in; j++) { + if (state->mel_log_32[j] > peak) + peak = state->mel_log_32[j]; + } + + /* Jump to peak immediately if higher, decay otherwise */ + if (peak > state->mmax) { + state->mmax = peak; + } else { + /* Q9.23 * Q1.15, result Q9.23. The coefficient is small + * so no need for saturation. + */ + s = (int64_t)peak - state->mmax; + state->mmax += + Q_MULTSR_32X32(s, config->mmax_coef, 23, 15, 23); + } + } + + /* Clamp Mel values lower than mmax - top_db, add offset, and scale. + * Config top_db and mel_offset are Q9.7, shift to Q9.23. + */ + clamp_value = state->mmax - ((int32_t)config->top_db << 16); + for (j = 0; j < state->dct.num_in; j++) { + mel_value = state->mel_log_32[j]; + if (mel_value < clamp_value) + mel_value = clamp_value; - /* Multiply Mel spectra with DCT matrix to get cepstral coefficients */ - mat_init_16b(state->cepstral_coef, 1, state->dct.num_out, 7); /* Q8.7 */ - mat_multiply(state->mel_spectra, state->dct.matrix, state->cepstral_coef); + /* Q9.23 * Q4.12, result Q9.23 */ + s = (int64_t)mel_value + ((int32_t)config->mel_offset << 16); + state->mel_log_32[j] = + sat_int32(Q_MULTSR_32X32(s, config->mel_scale, 23, 12, 23)); + } - /* Apply cepstral lifter */ - if (state->lifter.cepstral_lifter != 0) - mat_multiply_elementwise(state->cepstral_coef, state->lifter.matrix, - state->cepstral_coef); + /* Store Q9.7 version in mel_spectra for s16 output mode */ + for (j = 0; j < state->dct.num_in; j++) + state->mel_spectra->data[j] = + sat_int16(state->mel_log_32[j] >> 16); - cc_count += state->dct.num_out; + /* Enable this to check mmax decay */ + comp_dbg(dev, "state->mmax = %d", state->mmax); + } else { + /* Convert Q9.23 to Q9.7 for 16-bit DCT */ + for (j = 0; j < state->dct.num_in; j++) + state->mel_spectra->data[j] = + sat_int16(state->mel_log_32[j] >> 16); - /* Output to sink buffer */ + /* Multiply Mel spectra with DCT matrix to get cepstral coefficients */ + mat_init_16b(state->cepstral_coef, 1, state->dct.num_out, 7); /* Q9.7 */ + mat_multiply(state->mel_spectra, state->dct.matrix, state->cepstral_coef); + + /* Apply cepstral lifter */ + if (state->lifter.cepstral_lifter != 0) { + mat_multiply_elementwise(state->cepstral_coef, state->lifter.matrix, + state->cepstral_coef); + } + + cc_count += state->dct.num_out; + } } - /* TODO: This version handles only one FFT run per copy(). How to pass multiple - * cepstral coefficients sets return is an open. - */ return cc_count; } +void mfcc_fill_fft_buffer(struct mfcc_state *state) +{ + struct mfcc_buffer *buf = &state->buf; + struct mfcc_fft *fft = &state->fft; + int32_t *d = &fft->fft_buf[fft->fft_fill_start_idx].real; + const int fft_elem_inc = sizeof(fft->fft_buf[0]) / sizeof(int32_t); + int16_t *prev = state->prev_data; + int16_t *prev_end = prev + state->prev_data_size; + int16_t *r = buf->r_ptr; + int copied; + int nmax; + int n; + int j; + + /* Copy overlapped samples from state buffer. The fft_buf has been + * cleared by caller so imaginary part remains zero. + */ + while (prev < prev_end) { + *d = *prev++; + d += fft_elem_inc; + } + + /* Copy hop size of new data from circular buffer */ + for (copied = 0; copied < fft->fft_hop_size; copied += n) { + nmax = fft->fft_hop_size - copied; + n = mfcc_buffer_samples_without_wrap(buf, r); + n = MIN(n, nmax); + for (j = 0; j < n; j++) { + *d = *r++; + d += fft_elem_inc; + } + r = mfcc_buffer_wrap(buf, r); + } + + buf->s_avail -= copied; + buf->s_free += copied; + buf->r_ptr = r; + + /* Copy for next time data back to overlap buffer */ + d = (int32_t *)&fft->fft_buf[fft->fft_fill_start_idx + fft->fft_hop_size].real; + prev = state->prev_data; + while (prev < prev_end) { + *prev++ = *d; + d += fft_elem_inc; + } +} + #if CONFIG_FORMAT_S16LE +static int16_t *mfcc_sink_copy_zero_s16(const struct audio_stream *sink, int16_t *w_ptr, + int samples) +{ + int copied; + int nmax; + int n; + + for (copied = 0; copied < samples; copied += n) { + nmax = samples - copied; + n = audio_stream_samples_without_wrap_s16(sink, w_ptr); + n = MIN(n, nmax); + memset(w_ptr, 0, n * sizeof(int16_t)); + w_ptr = audio_stream_wrap(sink, w_ptr + n); + } + + return w_ptr; +} + +static int16_t *mfcc_sink_copy_data_s16(const struct audio_stream *sink, int16_t *w_ptr, + int samples, int16_t *r_ptr) +{ + int copied; + int nmax; + int n; + + for (copied = 0; copied < samples; copied += n) { + nmax = samples - copied; + n = audio_stream_samples_without_wrap_s16(sink, w_ptr); + n = MIN(n, nmax); + /* Not using memcpy_s() due to speed need */ + memcpy(w_ptr, r_ptr, n * sizeof(int16_t)); + w_ptr = audio_stream_wrap(sink, w_ptr + n); + r_ptr += n; + } + + return w_ptr; +} + void mfcc_s16_default(struct processing_module *mod, struct input_stream_buffer *bsource, struct output_stream_buffer *bsink, int frames) { @@ -149,35 +269,243 @@ void mfcc_s16_default(struct processing_module *mod, struct input_stream_buffer struct mfcc_buffer *buf = &cd->state.buf; uint32_t magic = MFCC_MAGIC; int16_t *w_ptr = audio_stream_get_wptr(sink); - // int num_magic = sizeof(magic) / sizeof(int16_t); const int num_magic = 2; int num_ceps; - int zero_samples; + int sink_samples; + int to_copy; /* Get samples from source buffer */ mfcc_source_copy_s16(bsource, buf, &state->emph, frames, state->source_channel); - /* Run STFT and processing after FFT: Mel auditory filter and DCT. The sink - * buffer is updated during STDF processing. - */ - num_ceps = mfcc_stft_process(mod->dev, state); + /* Run STFT and processing after FFT: Mel auditory filter and DCT. */ + num_ceps = mfcc_stft_process(mod->dev, cd); - /* Done, copy data to sink. This works only if the period has room for magic (2) - * plus num_ceps int16_t samples. TODO: split ceps over multiple periods. - */ - zero_samples = frames * audio_stream_get_channels(sink); + /* If new output produced, set up pointer into scratch data and mark magic pending */ if (num_ceps > 0) { - zero_samples -= num_ceps + num_magic; + if (state->mel_only) + state->out_data_ptr = state->mel_spectra->data; + else + state->out_data_ptr = state->cepstral_coef->data; + + state->out_remain = num_ceps; + state->magic_pending = true; + } + + /* Write to sink, limited by period size */ + sink_samples = frames * audio_stream_get_channels(sink); + + /* Write magic word first if pending */ + if (state->magic_pending && sink_samples >= num_magic) { w_ptr = mfcc_sink_copy_data_s16(sink, w_ptr, num_magic, (int16_t *)&magic); - w_ptr = mfcc_sink_copy_data_s16(sink, w_ptr, num_ceps, state->cepstral_coef->data); + sink_samples -= num_magic; + state->magic_pending = false; + } + + /* Write cepstral/mel data from scratch buffer */ + to_copy = MIN(state->out_remain, sink_samples); + if (to_copy > 0) { + w_ptr = mfcc_sink_copy_data_s16(sink, w_ptr, to_copy, state->out_data_ptr); + state->out_data_ptr += to_copy; + state->out_remain -= to_copy; + sink_samples -= to_copy; } - w_ptr = mfcc_sink_copy_zero_s16(sink, w_ptr, zero_samples); + /* Zero-fill remaining sink samples */ + w_ptr = mfcc_sink_copy_zero_s16(sink, w_ptr, sink_samples); } #endif /* CONFIG_FORMAT_S16LE */ +#if CONFIG_FORMAT_S24LE || CONFIG_FORMAT_S32LE +static int32_t *mfcc_sink_copy_zero_s32(const struct audio_stream *sink, int32_t *w_ptr, + int samples) +{ + int copied; + int nmax; + int n; + + for (copied = 0; copied < samples; copied += n) { + nmax = samples - copied; + n = audio_stream_samples_without_wrap_s32(sink, w_ptr); + n = MIN(n, nmax); + memset(w_ptr, 0, n * sizeof(int32_t)); + w_ptr = audio_stream_wrap(sink, w_ptr + n); + } + + return w_ptr; +} + +static int32_t *mfcc_sink_copy_data_s32(const struct audio_stream *sink, int32_t *w_ptr, + int samples, int32_t *r_ptr) +{ + int copied; + int nmax; + int n; + + for (copied = 0; copied < samples; copied += n) { + nmax = samples - copied; + n = audio_stream_samples_without_wrap_s32(sink, w_ptr); + n = MIN(n, nmax); + /* Not using memcpy_s() due to speed need */ + memcpy(w_ptr, r_ptr, n * sizeof(int32_t)); + w_ptr = audio_stream_wrap(sink, w_ptr + n); + r_ptr += n; + } + + return w_ptr; +} +#endif /* CONFIG_FORMAT_S24LE || CONFIG_FORMAT_S32LE */ + #if CONFIG_FORMAT_S24LE +void mfcc_s24_default(struct processing_module *mod, struct input_stream_buffer *bsource, + struct output_stream_buffer *bsink, int frames) +{ + struct audio_stream *sink = bsink->data; + struct mfcc_comp_data *cd = module_get_private_data(mod); + struct mfcc_state *state = &cd->state; + struct mfcc_buffer *buf = &cd->state.buf; + uint32_t magic = MFCC_MAGIC; + int32_t *w_ptr = audio_stream_get_wptr(sink); + const int num_magic = 1; /* one int32_t word for magic */ + int num_ceps; + int sink_samples; + int remain_s32; + int to_copy; + int k; + + /* Get samples from source buffer */ + mfcc_source_copy_s24(bsource, buf, &state->emph, frames, state->source_channel); + + /* Run STFT and processing after FFT */ + num_ceps = mfcc_stft_process(mod->dev, cd); + + /* If new output produced, set up pointer into scratch data */ + if (num_ceps > 0) { + if (state->mel_only) { + /* Convert mel_log_32 from Q9.23 to Q9.15 in-place */ + for (k = 0; k < num_ceps; k++) + state->mel_log_32[k] >>= 8; + + state->out_data_ptr_32 = state->mel_log_32; + } else { + state->out_data_ptr = state->cepstral_coef->data; + } + + state->out_remain = num_ceps; + state->magic_pending = true; + } + + /* Write to sink, limited by period size */ + sink_samples = frames * audio_stream_get_channels(sink); + + /* Write magic word first if pending */ + if (state->magic_pending && sink_samples >= num_magic) { + w_ptr = mfcc_sink_copy_data_s32(sink, w_ptr, num_magic, (int32_t *)&magic); + sink_samples -= num_magic; + state->magic_pending = false; + } + + if (state->mel_only) { + /* Write 32-bit mel data Q9.15, one value per int32_t */ + to_copy = MIN(state->out_remain, sink_samples); + if (to_copy > 0) { + w_ptr = mfcc_sink_copy_data_s32(sink, w_ptr, to_copy, + state->out_data_ptr_32); + state->out_data_ptr_32 += to_copy; + state->out_remain -= to_copy; + sink_samples -= to_copy; + } + } else { + /* Write cepstral data packed as int32_t from scratch buffer */ + remain_s32 = (state->out_remain + 1) / 2; + to_copy = MIN(remain_s32, sink_samples); + if (to_copy > 0) { + w_ptr = mfcc_sink_copy_data_s32(sink, w_ptr, to_copy, + (int32_t *)state->out_data_ptr); + state->out_data_ptr += to_copy * 2; + state->out_remain -= to_copy * 2; + if (state->out_remain < 0) + state->out_remain = 0; + + sink_samples -= to_copy; + } + } + + /* Zero-fill remaining sink samples */ + w_ptr = mfcc_sink_copy_zero_s32(sink, w_ptr, sink_samples); +} #endif /* CONFIG_FORMAT_S24LE */ #if CONFIG_FORMAT_S32LE +void mfcc_s32_default(struct processing_module *mod, struct input_stream_buffer *bsource, + struct output_stream_buffer *bsink, int frames) +{ + struct audio_stream *sink = bsink->data; + struct mfcc_comp_data *cd = module_get_private_data(mod); + struct mfcc_state *state = &cd->state; + struct mfcc_buffer *buf = &cd->state.buf; + uint32_t magic = MFCC_MAGIC; + int32_t *w_ptr = audio_stream_get_wptr(sink); + const int num_magic = 1; /* one int32_t word for magic */ + int num_ceps; + int sink_samples; + int remain_s32; + int to_copy; + + /* Get samples from source buffer */ + mfcc_source_copy_s32(bsource, buf, &state->emph, frames, state->source_channel); + + /* Run STFT and processing after FFT */ + num_ceps = mfcc_stft_process(mod->dev, cd); + + /* If new output produced, set up pointer into scratch data */ + if (num_ceps > 0) { + if (state->mel_only) { + state->out_data_ptr_32 = state->mel_log_32; + } else { + state->out_data_ptr = state->cepstral_coef->data; + } + + state->out_remain = num_ceps; + state->magic_pending = true; + } + + /* Write to sink, limited by period size */ + sink_samples = frames * audio_stream_get_channels(sink); + + /* Write magic word first if pending */ + if (state->magic_pending && sink_samples >= num_magic) { + w_ptr = mfcc_sink_copy_data_s32(sink, w_ptr, num_magic, (int32_t *)&magic); + sink_samples -= num_magic; + state->magic_pending = false; + } + + if (state->mel_only) { + /* Write 32-bit mel data Q9.23, one value per int32_t */ + to_copy = MIN(state->out_remain, sink_samples); + if (to_copy > 0) { + w_ptr = mfcc_sink_copy_data_s32(sink, w_ptr, to_copy, + state->out_data_ptr_32); + state->out_data_ptr_32 += to_copy; + state->out_remain -= to_copy; + sink_samples -= to_copy; + } + } else { + /* Write cepstral data packed as int32_t from scratch buffer */ + remain_s32 = (state->out_remain + 1) / 2; + to_copy = MIN(remain_s32, sink_samples); + if (to_copy > 0) { + w_ptr = mfcc_sink_copy_data_s32(sink, w_ptr, to_copy, + (int32_t *)state->out_data_ptr); + state->out_data_ptr += to_copy * 2; + state->out_remain -= to_copy * 2; + if (state->out_remain < 0) + state->out_remain = 0; + + sink_samples -= to_copy; + } + } + + /* Zero-fill remaining sink samples */ + w_ptr = mfcc_sink_copy_zero_s32(sink, w_ptr, sink_samples); +} #endif /* CONFIG_FORMAT_S32LE */ diff --git a/src/audio/mfcc/mfcc_generic.c b/src/audio/mfcc/mfcc_generic.c index 9384b16e0cee..73ac49272ed4 100644 --- a/src/audio/mfcc/mfcc_generic.c +++ b/src/audio/mfcc/mfcc_generic.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: BSD-3-Clause // -// Copyright(c) 2022 Intel Corporation. All rights reserved. +// Copyright(c) 2022-2026 Intel Corporation. // // Author: Seppo Ingalsuo <seppo.ingalsuo@linux.intel.com> @@ -10,6 +10,8 @@ #include <sof/audio/component.h> #include <sof/audio/audio_stream.h> #include <sof/math/auditory.h> +#include <sof/math/icomplex16.h> +#include <sof/math/icomplex32.h> #include <sof/math/matrix.h> #include <sof/math/sqrt.h> #include <sof/math/trig.h> @@ -24,6 +26,45 @@ * MFCC algorithm code */ +void mfcc_fill_prev_samples(struct mfcc_buffer *buf, int16_t *prev_data, + int prev_data_length) +{ + /* Fill prev_data from input buffer */ + int16_t *r = buf->r_ptr; + int16_t *p = prev_data; + int copied; + int nmax; + int n; + + for (copied = 0; copied < prev_data_length; copied += n) { + nmax = prev_data_length - copied; + n = mfcc_buffer_samples_without_wrap(buf, r); + n = MIN(n, nmax); + memcpy(p, r, sizeof(int16_t) * n); /* Not using memcpy_s() due to speed need */ + p += n; + r += n; + r = mfcc_buffer_wrap(buf, r); + } + + buf->s_avail -= copied; + buf->s_free += copied; + buf->r_ptr = r; +} + +void mfcc_apply_window(struct mfcc_state *state, int input_shift) +{ + struct mfcc_fft *fft = &state->fft; + int j; + int i = fft->fft_fill_start_idx; + + /* TODO: Use proper multiply and saturate function to make sure no overflows */ + int s = input_shift + 1; /* To convert 16 -> 32 with Q1.15 x Q1.15 -> Q30 -> Q31 */ + + for (j = 0; j < fft->fft_size; j++) + fft->fft_buf[i + j].real = (fft->fft_buf[i + j].real * state->window[j]) << s; +} + +#if CONFIG_FORMAT_S16LE void mfcc_source_copy_s16(struct input_stream_buffer *bsource, struct mfcc_buffer *buf, struct mfcc_pre_emph *emph, int frames, int source_channel) { @@ -70,170 +111,114 @@ void mfcc_source_copy_s16(struct input_stream_buffer *bsource, struct mfcc_buffe buf->s_free -= copied; buf->w_ptr = w; } +#endif /* CONFIG_FORMAT_S16LE */ -void mfcc_fill_prev_samples(struct mfcc_buffer *buf, int16_t *prev_data, - int prev_data_length) -{ - /* Fill prev_data from input buffer */ - int16_t *r = buf->r_ptr; - int16_t *p = prev_data; - int copied; - int nmax; - int n; - - for (copied = 0; copied < prev_data_length; copied += n) { - nmax = prev_data_length - copied; - n = mfcc_buffer_samples_without_wrap(buf, r); - n = MIN(n, nmax); - memcpy(p, r, sizeof(int16_t) * n); /* Not using memcpy_s() due to speed need */ - p += n; - r += n; - r = mfcc_buffer_wrap(buf, r); - } - - buf->s_avail -= copied; - buf->s_free += copied; - buf->r_ptr = r; -} +#if CONFIG_FORMAT_S24LE -void mfcc_fill_fft_buffer(struct mfcc_state *state) +void mfcc_source_copy_s24(struct input_stream_buffer *bsource, struct mfcc_buffer *buf, + struct mfcc_pre_emph *emph, int frames, int source_channel) { - struct mfcc_buffer *buf = &state->buf; - struct mfcc_fft *fft = &state->fft; - int16_t *r = buf->r_ptr; + struct audio_stream *source = bsource->data; + int32_t tmp, s; + int32_t *x0; + int32_t *x = audio_stream_get_rptr(source); + int16_t *w = buf->w_ptr; int copied; int nmax; - int idx = fft->fft_fill_start_idx; - int j; + int n1; + int n2; int n; + int i; + int num_channels = audio_stream_get_channels(source); - /* Copy overlapped samples from state buffer. Imaginary part of input - * remains zero. + /* Copy from source to pre-buffer for FFT. + * The pre-emphasis filter is done in this step. + * S24_4LE data is in 32-bit container, shift left by 8 to Q1.31, + * then convert to Q1.15 with rounding. */ - for (j = 0; j < state->prev_data_size; j++) - fft->fft_buf[idx + j].real = state->prev_data[j]; - - /* Copy hop size of new data from circular buffer */ - idx += state->prev_data_size; - for (copied = 0; copied < fft->fft_hop_size; copied += n) { - nmax = fft->fft_hop_size - copied; - n = mfcc_buffer_samples_without_wrap(buf, r); + for (copied = 0; copied < frames; copied += n) { + nmax = frames - copied; + n1 = audio_stream_frames_without_wrap(source, x); + n2 = mfcc_buffer_samples_without_wrap(buf, w); + n = MIN(n1, n2); n = MIN(n, nmax); - for (j = 0; j < n; j++) { - fft->fft_buf[idx].real = *r; - r++; - idx++; + x0 = x + source_channel; + for (i = 0; i < n; i++) { + if (emph->enable) { + /* Convert to Q1.31, ignore highest byte */ + s = (int32_t)((uint32_t)*x0 << 8); + /* Q1.15 x Q1.15 -> Q2.30 */ + tmp = (int32_t)emph->delay * emph->coef + Q_SHIFT(s, 31, 30); + *w = sat_int16(Q_SHIFT_RND(tmp, 30, 15)); + emph->delay = sat_int16(Q_SHIFT_RND(s, 31, 15)); + } else { + /* Convert to Q1.31, ignore highest byte */ + s = (int32_t)((uint32_t)*x0 << 8); + *w = sat_int16(Q_SHIFT_RND(s, 31, 15)); + } + x0 += num_channels; + w++; } - r = mfcc_buffer_wrap(buf, r); - } - - buf->s_avail -= copied; - buf->s_free += copied; - buf->r_ptr = r; - - /* Copy for next time data back to overlap buffer */ - idx = fft->fft_fill_start_idx + fft->fft_hop_size; - for (j = 0; j < state->prev_data_size; j++) - state->prev_data[j] = fft->fft_buf[idx + j].real; -} -#ifdef MFCC_NORMALIZE_FFT -int mfcc_normalize_fft_buffer(struct mfcc_state *state) -{ - struct mfcc_fft *fft = &state->fft; - int32_t absx; - int32_t smax = 0; - int32_t x; - int shift; - int j; - int i = fft->fft_fill_start_idx; - - for (j = 0; j < fft->fft_size; j++) { - x = fft->fft_buf[i + j].real; - absx = (x < 0) ? -x : x; - if (smax < absx) - smax = absx; + x = audio_stream_wrap(source, x + n * audio_stream_get_channels(source)); + w = mfcc_buffer_wrap(buf, w); } - - shift = norm_int32(smax << 15) - 1; /* 16 bit data */ - shift = MAX(shift, 0); - shift = MIN(shift, MFCC_NORMALIZE_MAX_SHIFT); - return shift; + buf->s_avail += copied; + buf->s_free -= copied; + buf->w_ptr = w; } -#endif -void mfcc_apply_window(struct mfcc_state *state, int input_shift) -{ - struct mfcc_fft *fft = &state->fft; - int j; - int i = fft->fft_fill_start_idx; +#endif /* CONFIG_FORMAT_S24LE */ -#if MFCC_FFT_BITS == 16 - /* TODO: Use proper multiply and saturate function to make sure no overflows */ - int32_t x; - int s = 14 - input_shift; /* Q1.15 x Q1.15 -> Q30 -> Q15, shift by 15 - 1 for round */ +#if CONFIG_FORMAT_S32LE - for (j = 0; j < fft->fft_size; j++) { - x = (int32_t)fft->fft_buf[i + j].real * state->window[j]; - fft->fft_buf[i + j].real = ((x >> s) + 1) >> 1; - } -#else - /* TODO: Use proper multiply and saturate function to make sure no overflows */ - int s = input_shift + 1; /* To convert 16 -> 32 with Q1.15 x Q1.15 -> Q30 -> Q31 */ - - for (j = 0; j < fft->fft_size; j++) - fft->fft_buf[i + j].real = (fft->fft_buf[i + j].real * state->window[j]) << s; -#endif -} - -#if CONFIG_FORMAT_S16LE - -int16_t *mfcc_sink_copy_zero_s16(const struct audio_stream *sink, - int16_t *w_ptr, int samples) +void mfcc_source_copy_s32(struct input_stream_buffer *bsource, struct mfcc_buffer *buf, + struct mfcc_pre_emph *emph, int frames, int source_channel) { + struct audio_stream *source = bsource->data; + int32_t s; + int32_t *x0; + int32_t *x = audio_stream_get_rptr(source); + int16_t *w = buf->w_ptr; int copied; int nmax; - int i; + int n1; + int n2; int n; - - for (copied = 0; copied < samples; copied += n) { - nmax = samples - copied; - n = audio_stream_samples_without_wrap_s16(sink, w_ptr); - n = MIN(n, nmax); - for (i = 0; i < n; i++) { - *w_ptr = 0; - w_ptr++; - } - - w_ptr = audio_stream_wrap(sink, w_ptr); - } - - return w_ptr; -} - -int16_t *mfcc_sink_copy_data_s16(const struct audio_stream *sink, int16_t *w_ptr, - int samples, int16_t *r_ptr) -{ - int copied; - int nmax; int i; - int n; + int num_channels = audio_stream_get_channels(source); - for (copied = 0; copied < samples; copied += n) { - nmax = samples - copied; - n = audio_stream_samples_without_wrap_s16(sink, w_ptr); + /* Copy from source to pre-buffer for FFT. + * The pre-emphasis filter is done in this step. + * S32 data is in 32-bit container, shift right by 16 to get 16-bit. + */ + for (copied = 0; copied < frames; copied += n) { + nmax = frames - copied; + n1 = audio_stream_frames_without_wrap(source, x); + n2 = mfcc_buffer_samples_without_wrap(buf, w); + n = MIN(n1, n2); n = MIN(n, nmax); + x0 = x + source_channel; for (i = 0; i < n; i++) { - *w_ptr = *r_ptr; - r_ptr++; - w_ptr++; + if (emph->enable) { + /* Q1.15 x Q1.15 -> Q2.30 */ + s = (int32_t)emph->delay * emph->coef + Q_SHIFT(*x0, 31, 30); + *w = sat_int16(Q_SHIFT_RND(s, 30, 15)); + emph->delay = sat_int16(Q_SHIFT_RND(*x0, 31, 15)); + } else { + *w = sat_int16(Q_SHIFT_RND(*x0, 31, 15)); + } + x0 += num_channels; + w++; } - w_ptr = audio_stream_wrap(sink, w_ptr); + x = audio_stream_wrap(source, x + n * audio_stream_get_channels(source)); + w = mfcc_buffer_wrap(buf, w); } - - return w_ptr; + buf->s_avail += copied; + buf->s_free -= copied; + buf->w_ptr = w; } +#endif /* CONFIG_FORMAT_S32LE */ -#endif /* CONFIG_FORMAT_S16LE */ -#endif +#endif /* MFCC_GENERIC */ diff --git a/src/audio/mfcc/mfcc_hifi3.c b/src/audio/mfcc/mfcc_hifi3.c index 76f0a79da668..80c384ad6c64 100644 --- a/src/audio/mfcc/mfcc_hifi3.c +++ b/src/audio/mfcc/mfcc_hifi3.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: BSD-3-Clause // -// Copyright(c) 2023 Intel Corporation. All rights reserved. +// Copyright(c) 2023-2026 Intel Corporation. // // Author: Andrula Song <andrula.song@intel.com> @@ -11,6 +11,8 @@ #include <sof/audio/component.h> #include <sof/audio/audio_stream.h> #include <sof/math/auditory.h> +#include <sof/math/icomplex16.h> +#include <sof/math/icomplex32.h> #include <sof/math/matrix.h> #include <sof/math/sqrt.h> #include <sof/math/trig.h> @@ -33,6 +35,7 @@ static inline void set_circular_buf0(const void *start, const void *end) * MFCC algorithm code */ +#if CONFIG_FORMAT_S16LE void mfcc_source_copy_s16(struct input_stream_buffer *bsource, struct mfcc_buffer *buf, struct mfcc_pre_emph *emph, int frames, int source_channel) { @@ -90,6 +93,7 @@ void mfcc_source_copy_s16(struct input_stream_buffer *bsource, struct mfcc_buffe buf->s_free -= copied; buf->w_ptr = (int16_t *)out; } +#endif /* CONFIG_FORMAT_S16LE */ void mfcc_fill_prev_samples(struct mfcc_buffer *buf, int16_t *prev_data, int prev_data_length) @@ -124,72 +128,6 @@ void mfcc_fill_prev_samples(struct mfcc_buffer *buf, int16_t *prev_data, buf->r_ptr = (void *)in; /* int16_t pointer but direct cast is not possible */ } -void mfcc_fill_fft_buffer(struct mfcc_state *state) -{ - struct mfcc_buffer *buf = &state->buf; - struct mfcc_fft *fft = &state->fft; - int idx = fft->fft_fill_start_idx; - ae_int16 *out = (ae_int16 *)&fft->fft_buf[idx].real; - ae_int16 *in = (ae_int16 *)state->prev_data; - ae_int16x4 sample; - const int buf_inc = sizeof(ae_int16); - const int fft_inc = sizeof(fft->fft_buf[0]); - int j; - - /* Copy overlapped samples from state buffer. Imaginary part of input - * remains zero. - */ - for (j = 0; j < state->prev_data_size; j++) { - AE_L16_XP(sample, in, buf_inc); - AE_S16_0_XP(sample, out, fft_inc); - } - - /* Copy hop size of new data from circular buffer */ - idx += state->prev_data_size; - in = (ae_int16 *)buf->r_ptr; - out = (ae_int16 *)&fft->fft_buf[idx].real; - set_circular_buf0(buf->addr, buf->end_addr); - for (j = 0; j < fft->fft_hop_size; j++) { - AE_L16_XC(sample, in, buf_inc); - AE_S16_0_XP(sample, out, fft_inc); - } - - buf->s_avail -= fft->fft_hop_size; - buf->s_free += fft->fft_hop_size; - buf->r_ptr = (int16_t *)in; - - /* Copy for next time data back to overlap buffer */ - idx = fft->fft_fill_start_idx + fft->fft_hop_size; - in = (ae_int16 *)&fft->fft_buf[idx].real; - out = (ae_int16 *)state->prev_data; - for (j = 0; j < state->prev_data_size; j++) { - AE_L16_XP(sample, in, fft_inc); - AE_S16_0_XP(sample, out, buf_inc); - } -} - -#ifdef MFCC_NORMALIZE_FFT -int mfcc_normalize_fft_buffer(struct mfcc_state *state) -{ - struct mfcc_fft *fft = &state->fft; - ae_p16s *in = (ae_p16s *)&fft->fft_buf[fft->fft_fill_start_idx].real; - ae_int32x2 sample; - ae_int32x2 max = AE_ZERO32(); - const int fft_inc = sizeof(fft->fft_buf[0]); - int shift; - int j; - - for (j = 0; j < fft->fft_size; j++) { - /* load 16-bit data to middle of 32-bit container*/ - AE_L16M_XU(sample, in, fft_inc); - max = AE_MAXABS32S(max, sample); - } - shift = AE_NSAZ32_L(max) - 8;/* 16 bit data */ - shift = MAX(shift, 0); - shift = MIN(shift, MFCC_NORMALIZE_MAX_SHIFT); - return shift; -} -#endif void mfcc_apply_window(struct mfcc_state *state, int input_shift) { struct mfcc_fft *fft = &state->fft; @@ -200,92 +138,143 @@ void mfcc_apply_window(struct mfcc_state *state, int input_shift) ae_int16x4 win; int j; -#if MFCC_FFT_BITS == 16 - ae_int16 *fft_in = (ae_int16 *)&fft->fft_buf[fft->fft_fill_start_idx].real; - ae_int16x4 sample; - - for (j = 0; j < fft->fft_size; j++) { - AE_L16_IP(sample, fft_in, 0); - AE_L16_XP(win, win_in, win_inc); - temp = AE_MULF16SS_00(sample, win); - temp = AE_SLAA32S(temp, input_shift); - sample = AE_ROUND16X4F32SASYM(temp, temp); - AE_S16_0_XP(sample, fft_in, fft_inc); - } -#else ae_int32 *fft_in = (ae_int32 *)&fft->fft_buf[fft->fft_fill_start_idx].real; ae_int32x2 sample; for (j = 0; j < fft->fft_size; j++) { AE_L32_IP(sample, fft_in, 0); AE_L16_XP(win, win_in, win_inc); - temp = AE_MULFP32X16X2RS_H(sample, win); + /* Data is 16-bit in 32-bit container, shift to Q1.31 for fractional multiply */ + sample = AE_SLAI32S(sample, 16); temp = AE_MULFP32X16X2RS_L(sample, win); temp = AE_SLAA32S(temp, input_shift); AE_S32_L_XP(temp, fft_in, fft_inc); } -#endif } -#if CONFIG_FORMAT_S16LE - -int16_t *mfcc_sink_copy_zero_s16(const struct audio_stream *sink, - int16_t *w_ptr, int samples) +#if CONFIG_FORMAT_S24LE +void mfcc_source_copy_s24(struct input_stream_buffer *bsource, struct mfcc_buffer *buf, + struct mfcc_pre_emph *emph, int frames, int source_channel) { + struct audio_stream *source = bsource->data; + int copied; + int nmax; + int n; int i; - int n = samples >> 2; - int m = samples & 0x03; - ae_int16x4 *out = (ae_int16x4 *)w_ptr; - const int inc = sizeof(ae_int16); - ae_valign outu = AE_ZALIGN64(); - ae_int16x4 zero = AE_ZERO16(); - - set_circular_buf0(sink->addr, sink->end_addr); - - for (i = 0; i < n; i++) - AE_SA16X4_IC(zero, outu, out); + int num_channels = audio_stream_get_channels(source); + ae_int32 *in; + ae_int32 *x = (ae_int32 *)audio_stream_get_rptr(source); + ae_int16 *out = (ae_int16 *)buf->w_ptr; + ae_int32x2 sample32; + ae_int16x4 sample; + ae_int32x2 temp; + ae_int16x4 coef = emph->coef; + ae_int16x4 delay; + const int in_inc = sizeof(ae_int32) * num_channels; - AE_SA64POS_FP(outu, out); - /* process the left samples that less than 4 - * one by one to avoid memory access overrun - */ - for (i = 0; i < m ; i++) - AE_S16_0_XC(zero, (ae_int16 *)out, inc); + for (copied = 0; copied < frames; copied += n) { + nmax = frames - copied; + n = audio_stream_frames_without_wrap(source, x); + n = MIN(n, nmax); + nmax = mfcc_buffer_samples_without_wrap(buf, (int16_t *)out); + n = MIN(n, nmax); + in = x + source_channel; + if (emph->enable) { + delay = emph->delay; + for (i = 0; i < n; i++) { + AE_L32_XP(sample32, in, in_inc); + /* Shift left by 8 to sign-extend to Q1.31 */ + sample32 = AE_SLAI32(sample32, 8); + /* Then shift right by 16 to get 16-bit */ + sample32 = AE_SRAI32(sample32, 16); + sample = AE_SAT16X4(sample32, sample32); + /* Q1.15 -> Q1.31 */ + temp = AE_CVT32X2F16_10(sample); + AE_MULAF16SS_00(temp, delay, coef); + delay = sample; + sample = AE_ROUND16X4F32SSYM(temp, temp); + AE_S16_0_IP(sample, out, 2); + } + emph->delay = delay; + } else { + for (i = 0; i < n; i++) { + AE_L32_XP(sample32, in, in_inc); + /* Shift left by 8 to sign-extend to Q1.31 */ + sample32 = AE_SLAI32(sample32, 8); + /* Then shift right by 16 to get 16-bit */ + sample32 = AE_SRAI32(sample32, 16); + sample = AE_SAT16X4(sample32, sample32); + AE_S16_0_IP(sample, out, 2); + } + } - return (int16_t *)out; + x = audio_stream_wrap(source, x + n * num_channels); + out = (ae_int16 *)mfcc_buffer_wrap(buf, (int16_t *)out); + } + buf->s_avail += copied; + buf->s_free -= copied; + buf->w_ptr = (int16_t *)out; } +#endif /* CONFIG_FORMAT_S24LE */ -int16_t *mfcc_sink_copy_data_s16(const struct audio_stream *sink, int16_t *w_ptr, - int samples, int16_t *r_ptr) +#if CONFIG_FORMAT_S32LE +void mfcc_source_copy_s32(struct input_stream_buffer *bsource, struct mfcc_buffer *buf, + struct mfcc_pre_emph *emph, int frames, int source_channel) { + struct audio_stream *source = bsource->data; + int copied; + int nmax; + int n; int i; - int n = samples >> 2; - int m = samples & 0x03; - ae_int16x4 *out = (ae_int16x4 *)w_ptr; - ae_int16x4 *in = (ae_int16x4 *)r_ptr; - ae_valign outu = AE_ZALIGN64(); - ae_valign inu = AE_ZALIGN64(); - const int inc = sizeof(ae_int16); - ae_int16x4 in_sample; + int num_channels = audio_stream_get_channels(source); + ae_int32 *in; + ae_int32 *x = (ae_int32 *)audio_stream_get_rptr(source); + ae_int16 *out = (ae_int16 *)buf->w_ptr; + ae_int32x2 sample32; + ae_int16x4 sample; + ae_int32x2 temp; + ae_int16x4 coef = emph->coef; + ae_int16x4 delay; + const int in_inc = sizeof(ae_int32) * num_channels; - set_circular_buf0(sink->addr, sink->end_addr); + for (copied = 0; copied < frames; copied += n) { + nmax = frames - copied; + n = audio_stream_frames_without_wrap(source, x); + n = MIN(n, nmax); + nmax = mfcc_buffer_samples_without_wrap(buf, (int16_t *)out); + n = MIN(n, nmax); + in = x + source_channel; + if (emph->enable) { + delay = emph->delay; + for (i = 0; i < n; i++) { + AE_L32_XP(sample32, in, in_inc); + /* S32: shift right by 16 to get 16-bit */ + sample32 = AE_SRAI32(sample32, 16); + sample = AE_SAT16X4(sample32, sample32); + /* Q1.15 -> Q1.31 */ + temp = AE_CVT32X2F16_10(sample); + AE_MULAF16SS_00(temp, delay, coef); + delay = sample; + sample = AE_ROUND16X4F32SSYM(temp, temp); + AE_S16_0_IP(sample, out, 2); + } + emph->delay = delay; + } else { + for (i = 0; i < n; i++) { + AE_L32_XP(sample32, in, in_inc); + sample32 = AE_SRAI32(sample32, 16); + sample = AE_SAT16X4(sample32, sample32); + AE_S16_0_IP(sample, out, 2); + } + } - inu = AE_LA64_PP(in); - for (i = 0; i < n; i++) { - AE_LA16X4_IP(in_sample, inu, in); - AE_SA16X4_IC(in_sample, outu, out); - } - AE_SA64POS_FP(outu, out); - /* process the left samples that less than 4 - * one by one to avoid memory access overrun - */ - for (i = 0; i < m ; i++) { - AE_L16_XP(in_sample, (ae_int16 *)in, inc); - AE_S16_0_XC(in_sample, (ae_int16 *)out, inc); + x = audio_stream_wrap(source, x + n * num_channels); + out = (ae_int16 *)mfcc_buffer_wrap(buf, (int16_t *)out); } - - return (int16_t *)out; + buf->s_avail += copied; + buf->s_free -= copied; + buf->w_ptr = (int16_t *)out; } +#endif /* CONFIG_FORMAT_S32LE */ -#endif /* CONFIG_FORMAT_S16LE */ -#endif +#endif /* MFCC_HIFI3 */ diff --git a/src/audio/mfcc/mfcc_hifi4.c b/src/audio/mfcc/mfcc_hifi4.c index d033dfe36124..63986870793b 100644 --- a/src/audio/mfcc/mfcc_hifi4.c +++ b/src/audio/mfcc/mfcc_hifi4.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: BSD-3-Clause // -// Copyright(c) 2023 Intel Corporation. All rights reserved. +// Copyright(c) 2023-2026 Intel Corporation. // // Author: Andrula Song <andrula.song@intel.com> @@ -11,6 +11,8 @@ #include <sof/audio/component.h> #include <sof/audio/audio_stream.h> #include <sof/math/auditory.h> +#include <sof/math/icomplex16.h> +#include <sof/math/icomplex32.h> #include <sof/math/matrix.h> #include <sof/math/sqrt.h> #include <sof/math/trig.h> @@ -39,6 +41,8 @@ static inline void set_circular_buf1(const void *start, const void *end) /* * MFCC algorithm code */ + +#if CONFIG_FORMAT_S16LE void mfcc_source_copy_s16(struct input_stream_buffer *bsource, struct mfcc_buffer *buf, struct mfcc_pre_emph *emph, int frames, int source_channel) { @@ -85,6 +89,7 @@ void mfcc_source_copy_s16(struct input_stream_buffer *bsource, struct mfcc_buffe buf->s_free -= frames; buf->w_ptr = (int16_t *)out; } +#endif /* CONFIG_FORMAT_S16LE */ void mfcc_fill_prev_samples(struct mfcc_buffer *buf, int16_t *prev_data, int prev_data_length) @@ -119,73 +124,6 @@ void mfcc_fill_prev_samples(struct mfcc_buffer *buf, int16_t *prev_data, buf->r_ptr = (int16_t *)in; } -void mfcc_fill_fft_buffer(struct mfcc_state *state) -{ - struct mfcc_buffer *buf = &state->buf; - struct mfcc_fft *fft = &state->fft; - int idx = fft->fft_fill_start_idx; - ae_int16 *out = (ae_int16 *)&fft->fft_buf[idx].real; - ae_int16 *in = (ae_int16 *)state->prev_data; - ae_int16x4 sample; - const int buf_inc = sizeof(ae_int16); - const int fft_inc = sizeof(fft->fft_buf[0]); - int j; - - /* Copy overlapped samples from state buffer. Imaginary part of input - * remains zero. - */ - for (j = 0; j < state->prev_data_size; j++) { - AE_L16_XP(sample, in, buf_inc); - AE_S16_0_XP(sample, out, fft_inc); - } - - /* Copy hop size of new data from circular buffer */ - idx += state->prev_data_size; - in = (ae_int16 *)buf->r_ptr; - out = (ae_int16 *)&fft->fft_buf[idx].real; - set_circular_buf0(buf->addr, buf->end_addr); - for (j = 0; j < fft->fft_hop_size; j++) { - AE_L16_XC(sample, in, buf_inc); - AE_S16_0_XP(sample, out, fft_inc); - } - - buf->s_avail -= fft->fft_hop_size; - buf->s_free += fft->fft_hop_size; - buf->r_ptr = (int16_t *)in; - - /* Copy for next time data back to overlap buffer */ - idx = fft->fft_fill_start_idx + fft->fft_hop_size; - in = (ae_int16 *)&fft->fft_buf[idx].real; - out = (ae_int16 *)state->prev_data; - for (j = 0; j < state->prev_data_size; j++) { - AE_L16_XP(sample, in, fft_inc); - AE_S16_0_XP(sample, out, buf_inc); - } -} - -#ifdef MFCC_NORMALIZE_FFT -int mfcc_normalize_fft_buffer(struct mfcc_state *state) -{ - struct mfcc_fft *fft = &state->fft; - ae_p16s *in = (ae_p16s *)&fft->fft_buf[fft->fft_fill_start_idx].real; - ae_int32x2 sample; - ae_int32x2 max = AE_ZERO32(); - const int fft_inc = sizeof(fft->fft_buf[0]); - int shift; - int j; - - for (j = 0; j < fft->fft_size; j++) { - /* load 16-bit data to middle of 32-bit container*/ - AE_L16M_XU(sample, in, fft_inc); - max = AE_MAXABS32S(max, sample); - } - shift = AE_NSAZ32_L(max) - 8;/* 16 bit data */ - shift = MAX(shift, 0); - shift = MIN(shift, MFCC_NORMALIZE_MAX_SHIFT); - return shift; -} -#endif - void mfcc_apply_window(struct mfcc_state *state, int input_shift) { struct mfcc_fft *fft = &state->fft; @@ -196,92 +134,125 @@ void mfcc_apply_window(struct mfcc_state *state, int input_shift) ae_int16x4 win; int j; -#if MFCC_FFT_BITS == 16 - ae_int16 *fft_in = (ae_int16 *)&fft->fft_buf[fft->fft_fill_start_idx].real; - ae_int16x4 sample; - - for (j = 0; j < fft->fft_size; j++) { - AE_L16_IP(sample, fft_in, 0); - AE_L16_XP(win, win_in, win_inc); - temp = AE_MULF16SS_00(sample, win); - temp = AE_SLAA32S(temp, input_shift); - sample = AE_ROUND16X4F32SASYM(temp, temp); - AE_S16_0_XP(sample, fft_in, fft_inc); - } -#else ae_int32 *fft_in = (ae_int32 *)&fft->fft_buf[fft->fft_fill_start_idx].real; ae_int32x2 sample; for (j = 0; j < fft->fft_size; j++) { AE_L32_IP(sample, fft_in, 0); AE_L16_XP(win, win_in, win_inc); - temp = AE_MULFP32X16X2RS_H(sample, win); + /* Data is 16-bit in 32-bit container, shift to Q1.31 for fractional multiply */ + sample = AE_SLAI32S(sample, 16); temp = AE_MULFP32X16X2RS_L(sample, win); temp = AE_SLAA32S(temp, input_shift); AE_S32_L_XP(temp, fft_in, fft_inc); } -#endif } -#if CONFIG_FORMAT_S16LE - -int16_t *mfcc_sink_copy_zero_s16(const struct audio_stream *sink, - int16_t *w_ptr, int samples) +#if CONFIG_FORMAT_S24LE +void mfcc_source_copy_s24(struct input_stream_buffer *bsource, struct mfcc_buffer *buf, + struct mfcc_pre_emph *emph, int frames, int source_channel) { + struct audio_stream *source = bsource->data; + int num_channels = audio_stream_get_channels(source); + ae_int32 *in = (ae_int32 *)source->r_ptr + source_channel; + ae_int16 *out = (ae_int16 *)buf->w_ptr; + ae_int32x2 sample32; + ae_int16x4 sample; + ae_int32x2 temp; + ae_int16x4 coef; + ae_int16x4 delay; + const int in_inc = sizeof(ae_int32) * num_channels; + const int out_inc = sizeof(ae_int16); int i; - int n = samples >> 2; - int m = samples & 0x03; - ae_int16x4 *out = (ae_int16x4 *)w_ptr; - const int inc = sizeof(ae_int16); - ae_valign outu = AE_ZALIGN64(); - ae_int16x4 zero = AE_ZERO16(); - set_circular_buf0(sink->addr, sink->end_addr); - - for (i = 0; i < n; i++) - AE_SA16X4_IC(zero, outu, out); + set_circular_buf1(buf->addr, buf->end_addr); + set_circular_buf0(source->addr, source->end_addr); - AE_SA64POS_FP(outu, out); - /* process the left samples that less than 4 - * one by one to avoid memory access overrun - */ - for (i = 0; i < m ; i++) - AE_S16_0_XC(zero, (ae_int16 *)out, inc); + if (emph->enable) { + delay = emph->delay; + coef = emph->coef; + for (i = 0; i < frames; i++) { + AE_L32_XC(sample32, in, in_inc); + /* Shift left by 8 to sign-extend to Q1.31 */ + sample32 = AE_SLAI32(sample32, 8); + /* Then shift right by 16 to get 16-bit */ + sample32 = AE_SRAI32(sample32, 16); + sample = AE_SAT16X4(sample32, sample32); + /* Q1.15 -> Q1.31 */ + temp = AE_CVT32X2F16_10(sample); + AE_MULAF16SS_00(temp, delay, coef); + delay = sample; + sample = AE_ROUND16X4F32SSYM(temp, temp); + AE_S16_0_XC1(sample, out, out_inc); + } + emph->delay = delay; + } else { + for (i = 0; i < frames; i++) { + AE_L32_XC(sample32, in, in_inc); + /* Shift left by 8 to sign-extend to Q1.31 */ + sample32 = AE_SLAI32(sample32, 8); + /* Then shift right by 16 to get 16-bit */ + sample32 = AE_SRAI32(sample32, 16); + sample = AE_SAT16X4(sample32, sample32); + AE_S16_0_XC1(sample, out, out_inc); + } + } - return (int16_t *)out; + buf->s_avail += frames; + buf->s_free -= frames; + buf->w_ptr = (int16_t *)out; } +#endif /* CONFIG_FORMAT_S24LE */ -int16_t *mfcc_sink_copy_data_s16(const struct audio_stream *sink, int16_t *w_ptr, - int samples, int16_t *r_ptr) +#if CONFIG_FORMAT_S32LE +void mfcc_source_copy_s32(struct input_stream_buffer *bsource, struct mfcc_buffer *buf, + struct mfcc_pre_emph *emph, int frames, int source_channel) { + struct audio_stream *source = bsource->data; + int num_channels = audio_stream_get_channels(source); + ae_int32 *in = (ae_int32 *)source->r_ptr + source_channel; + ae_int16 *out = (ae_int16 *)buf->w_ptr; + ae_int32x2 sample32; + ae_int16x4 sample; + ae_int32x2 temp; + ae_int16x4 coef; + ae_int16x4 delay; + const int in_inc = sizeof(ae_int32) * num_channels; + const int out_inc = sizeof(ae_int16); int i; - int n = samples >> 2; - int m = samples & 0x03; - ae_int16x4 *out = (ae_int16x4 *)w_ptr; - ae_int16x4 *in = (ae_int16x4 *)r_ptr; - ae_valign outu = AE_ZALIGN64(); - ae_valign inu = AE_ZALIGN64(); - const int inc = sizeof(ae_int16); - ae_int16x4 in_sample; - set_circular_buf0(sink->addr, sink->end_addr); + set_circular_buf1(buf->addr, buf->end_addr); + set_circular_buf0(source->addr, source->end_addr); - inu = AE_LA64_PP(in); - for (i = 0; i < n; i++) { - AE_LA16X4_IP(in_sample, inu, in); - AE_SA16X4_IC(in_sample, outu, out); - } - AE_SA64POS_FP(outu, out); - /* process the left samples that less than 4 - * one by one to avoid memory access overrun - */ - for (i = 0; i < m ; i++) { - AE_L16_XP(in_sample, (ae_int16 *)in, inc); - AE_S16_0_XC(in_sample, (ae_int16 *)out, inc); + if (emph->enable) { + delay = emph->delay; + coef = emph->coef; + for (i = 0; i < frames; i++) { + AE_L32_XC(sample32, in, in_inc); + /* S32: shift right by 16 to get 16-bit */ + sample32 = AE_SRAI32(sample32, 16); + sample = AE_SAT16X4(sample32, sample32); + /* Q1.15 -> Q1.31 */ + temp = AE_CVT32X2F16_10(sample); + AE_MULAF16SS_00(temp, delay, coef); + delay = sample; + sample = AE_ROUND16X4F32SSYM(temp, temp); + AE_S16_0_XC1(sample, out, out_inc); + } + emph->delay = delay; + } else { + for (i = 0; i < frames; i++) { + AE_L32_XC(sample32, in, in_inc); + sample32 = AE_SRAI32(sample32, 16); + sample = AE_SAT16X4(sample32, sample32); + AE_S16_0_XC1(sample, out, out_inc); + } } - return (int16_t *)out; + buf->s_avail += frames; + buf->s_free -= frames; + buf->w_ptr = (int16_t *)out; } +#endif /* CONFIG_FORMAT_S32LE */ -#endif /* CONFIG_FORMAT_S16LE */ -#endif +#endif /* MFCC_HIFI4 */ diff --git a/src/audio/mfcc/mfcc_setup.c b/src/audio/mfcc/mfcc_setup.c index 802ea058bb6f..1cad4b2b984e 100644 --- a/src/audio/mfcc/mfcc_setup.c +++ b/src/audio/mfcc/mfcc_setup.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: BSD-3-Clause // -// Copyright(c) 2022 Intel Corporation. All rights reserved. +// Copyright(c) 2022-2026 Intel Corporation. // // Author: Seppo Ingalsuo <seppo.ingalsuo@linux.intel.com> @@ -8,6 +8,8 @@ #include <sof/audio/component.h> #include <sof/audio/audio_stream.h> #include <sof/math/auditory.h> +#include <sof/math/icomplex16.h> +#include <sof/math/icomplex32.h> #include <sof/math/trig.h> #include <sof/math/window.h> #include <sof/trace/trace.h> @@ -48,10 +50,12 @@ static int mfcc_get_window(struct mfcc_state *state, enum sof_mfcc_fft_window_ty case MFCC_HAMMING_WINDOW: win_hamming_16b(state->window, fft->fft_size); return 0; + case MFCC_HANN_WINDOW: + win_hann_16b(state->window, fft->fft_size); + return 0; case MFCC_POVEY_WINDOW: win_povey_16b(state->window, fft->fft_size); return 0; - default: return -EINVAL; } @@ -65,7 +69,7 @@ static int mfcc_get_window(struct mfcc_state *state, enum sof_mfcc_fft_window_ty * coef[i] = 1.0 + 0.5 * lifter * sin(pi * i / lifter), i = 0 to num_ceps-1 */ -static int mfcc_get_cepstral_lifter(struct mfcc_cepstral_lifter *cl) +static int mfcc_get_cepstral_lifter(struct processing_module *mod, struct mfcc_cepstral_lifter *cl) { int32_t inv_cepstral_lifter; int32_t val; @@ -75,7 +79,7 @@ static int mfcc_get_cepstral_lifter(struct mfcc_cepstral_lifter *cl) if (cl->num_ceps > DCT_MATRIX_SIZE_MAX) return -EINVAL; - cl->matrix = mat_matrix_alloc_16b(1, cl->num_ceps, 9); /* Use Q7.9 */ + cl->matrix = mod_mat_matrix_alloc_16b(mod, 1, cl->num_ceps, 9); /* Use Q7.9 */ if (!cl->matrix) return -ENOMEM; @@ -108,7 +112,7 @@ int mfcc_setup(struct processing_module *mod, int max_frames, int sample_rate, i struct dct_plan_16 *dct = &state->dct; int ret; - comp_dbg(dev, "mfcc_setup()"); + comp_dbg(dev, "entry"); /* Check size */ if (config->size != sizeof(struct sof_mfcc_config)) { @@ -137,10 +141,9 @@ int mfcc_setup(struct processing_module *mod, int max_frames, int sample_rate, i return -EINVAL; } - comp_info(dev, "mfcc_setup(), source_channel = %d, stream_channels = %d", - config->channel, channels); - if (config->channel >= channels) { - comp_err(dev, "Illegal channel"); + if (config->channel >= channels || (config->channel < 0 && channels != 1)) { + comp_err(dev, "Illegal source_channel %d for stream channels %d", config->channel, + channels); return -EINVAL; } @@ -149,6 +152,7 @@ int mfcc_setup(struct processing_module *mod, int max_frames, int sample_rate, i else state->source_channel = config->channel; + state->mmax = (int32_t)config->mmax_init << 16; /* Q9.7 -> Q9.23 */ state->emph.enable = config->preemphasis_coefficient > 0; state->emph.coef = -config->preemphasis_coefficient; /* Negate config parameter */ fft->fft_size = config->frame_length; @@ -156,7 +160,7 @@ int mfcc_setup(struct processing_module *mod, int max_frames, int sample_rate, i fft->fft_hop_size = config->frame_shift; fft->half_fft_size = (fft->fft_padded_size >> 1) + 1; - comp_info(dev, "mfcc_setup(), emphasis = %d, fft_size = %d, fft_padded_size = %d, fft_hop_size = %d", + comp_info(dev, "emphasis = %d, fft_size = %d, fft_padded_size = %d, fft_hop_size = %d", config->preemphasis_coefficient, fft->fft_size, fft->fft_padded_size, fft->fft_hop_size); @@ -168,11 +172,10 @@ int mfcc_setup(struct processing_module *mod, int max_frames, int sample_rate, i state->sample_buffers_size = sizeof(int16_t) * (state->buffer_size + state->prev_data_size + fft->fft_size); - comp_info(dev, "mfcc_setup(), buffer_size = %d, prev_size = %d", + comp_info(dev, "buffer_size = %d, prev_size = %d", state->buffer_size, state->prev_data_size); - state->buffers = rzalloc(SOF_MEM_FLAG_USER, - state->sample_buffers_size); + state->buffers = mod_zalloc(mod, state->sample_buffers_size); if (!state->buffers) { comp_err(dev, "Failed buffer allocate"); ret = -ENOMEM; @@ -184,19 +187,15 @@ int mfcc_setup(struct processing_module *mod, int max_frames, int sample_rate, i state->window = state->prev_data + state->prev_data_size; /* Allocate buffers for FFT input and output data */ -#if MFCC_FFT_BITS == 16 - fft->fft_buffer_size = fft->fft_padded_size * sizeof(struct icomplex16); -#else fft->fft_buffer_size = fft->fft_padded_size * sizeof(struct icomplex32); -#endif - fft->fft_buf = rzalloc(SOF_MEM_FLAG_USER, fft->fft_buffer_size); + fft->fft_buf = mod_zalloc(mod, fft->fft_buffer_size); if (!fft->fft_buf) { comp_err(dev, "Failed FFT buffer allocate"); ret = -ENOMEM; goto free_buffers; } - fft->fft_out = rzalloc(SOF_MEM_FLAG_USER, fft->fft_buffer_size); + fft->fft_out = mod_zalloc(mod, fft->fft_buffer_size); if (!fft->fft_out) { comp_err(dev, "Failed FFT output allocate"); ret = -ENOMEM; @@ -206,24 +205,24 @@ int mfcc_setup(struct processing_module *mod, int max_frames, int sample_rate, i fft->fft_fill_start_idx = 0; /* From config pad_type */ /* Setup FFT */ - fft->fft_plan = fft_plan_new(fft->fft_buf, fft->fft_out, fft->fft_padded_size, - MFCC_FFT_BITS); + fft->fft_plan = mod_fft_plan_new(mod, fft->fft_buf, fft->fft_out, fft->fft_padded_size, + MFCC_FFT_BITS); if (!fft->fft_plan) { comp_err(dev, "Failed FFT init"); ret = -EINVAL; goto free_fft_out; } - comp_info(dev, "mfcc_setup(), window = %d, num_mel_bins = %d, num_ceps = %d, norm = %d", + comp_info(dev, "window = %d, num_mel_bins = %d, num_ceps = %d, norm = %d", config->window, config->num_mel_bins, config->num_ceps, config->norm); - comp_info(dev, "mfcc_setup(), low_freq = %d, high_freq = %d", + comp_info(dev, "low_freq = %d, high_freq = %d", state->low_freq, state->high_freq); /* Setup window */ ret = mfcc_get_window(state, config->window); if (ret < 0) { comp_err(dev, "Failed Window function"); - goto free_fft_out; + goto free_fft_plan; } /* Setup Mel auditory filterbank. FFT input and output buffers are used @@ -242,42 +241,57 @@ int mfcc_setup(struct processing_module *mod, int max_frames, int sample_rate, i fb->scratch_data2 = (int16_t *)fft->fft_out; fb->scratch_length1 = fft->fft_buffer_size / sizeof(int16_t); fb->scratch_length2 = fft->fft_buffer_size / sizeof(int16_t); - ret = psy_get_mel_filterbank(fb); + ret = mod_psy_get_mel_filterbank(mod, fb); if (ret < 0) { comp_err(dev, "Failed Mel filterbank"); - goto free_fft_out; - } - - /* Setup DCT */ - dct->num_in = config->num_mel_bins; - dct->num_out = config->num_ceps; - dct->type = (enum dct_type)config->dct; - dct->ortho = true; - ret = dct_initialize_16(dct); - if (ret < 0) { - comp_err(dev, "Failed DCT init"); - goto free_melfb_data; + goto free_fft_plan; } - state->lifter.num_ceps = config->num_ceps; - state->lifter.cepstral_lifter = config->cepstral_lifter; /* Q7.9 max 64.0*/ - ret = mfcc_get_cepstral_lifter(&state->lifter); - if (ret < 0) { - comp_err(dev, "Failed cepstral lifter"); - goto free_dct_matrix; + /* Setup DCT and cepstral lifter only when num_ceps > 0. + * When num_ceps is zero, skip DCT/lifter and output Mel + * log spectra directly. + */ + if (config->num_ceps > 0) { + dct->num_in = config->num_mel_bins; + dct->num_out = config->num_ceps; + dct->type = (enum dct_type)config->dct; + dct->ortho = true; + ret = mod_dct_initialize_16(mod, dct); + if (ret < 0) { + comp_err(dev, "Failed DCT init"); + goto free_melfb_data; + } + + state->lifter.num_ceps = config->num_ceps; + state->lifter.cepstral_lifter = config->cepstral_lifter; /* Q7.9 max 64.0*/ + ret = mfcc_get_cepstral_lifter(mod, &state->lifter); + if (ret < 0) { + comp_err(dev, "Failed cepstral lifter"); + goto free_dct_matrix; + } + + state->mel_only = false; + } else { + comp_info(dev, "num_ceps is 0, Mel log spectra output mode"); + dct->num_in = config->num_mel_bins; + dct->num_out = 0; + dct->matrix = NULL; + state->lifter.matrix = NULL; + state->mel_only = true; } /* Scratch overlay during runtime * - * +--------------------------------------------------------+ - * | 1. fft_buf[], 16 bits,size x 4, e.g. 512 -> 2048 bytes | - * +-------------------------------------+------------------+ - * | 3. power_spectra[], | - * | 32 bits, e.g. x257 -> 1028 bytes | - * +-------------------------------------+ + * +------------------------------------------------------------+ + * | 1. fft_buf[], 32 bits, size x 8, e.g. 512 -> 4096 bytes | + * +-------------------------------------+----------------------+ + * | 3. power_spectra[], | 6. mel_log_32[], | + * | 32 bits, e.g. x257 -> 1028 bytes | 32 bits, e.g. x80 | + * | | 320 bytes | + * +-------------------------------------+----------------------+ * * +---------------------------------------------------------------------------------+ - * | 2. fft_out[], 16 bits,size x 4, e.g. 512 -> 2048 bytes | + * | 2. fft_out[], 32 bits, size x 8, e.g. 512 -> 4096 bytes | * +----------------------------------+----------------------------------+-----------+ * | 4. mel_spectra[], | 5. cepstral_coef[], | * | 16 bits, e.g. x23 -> 46 bytes | 16 bits, e.g. 13x -> 26 bytes | @@ -287,43 +301,92 @@ int mfcc_setup(struct processing_module *mod, int max_frames, int sample_rate, i /* Use FFT buffer as scratch for later computed data */ state->power_spectra = (int32_t *)&fft->fft_buf[0]; + state->mel_log_32 = &state->power_spectra[fft->half_fft_size]; + + /* Check that mel_log_32 fits in the remaining fft_buf scratch space */ + int mel_log_32_space = (int)(fft->fft_buffer_size / sizeof(int32_t)) - fft->half_fft_size; + + if (config->num_mel_bins > mel_log_32_space) { + comp_err(dev, "num_mel_bins %d exceeds mel_log_32 scratch space %d", + config->num_mel_bins, mel_log_32_space); + ret = -EINVAL; + goto free_lifter; + } + state->mel_spectra = (struct mat_matrix_16b *)&fft->fft_out[0]; - state->cepstral_coef = (struct mat_matrix_16b *) - &state->mel_spectra->data[state->dct.num_in]; + if (!state->mel_only) { + state->cepstral_coef = + (struct mat_matrix_16b *)&state->mel_spectra->data[state->dct.num_in]; + } else { + state->cepstral_coef = NULL; + } + + /* Allocate output buffer for multi-period output. Size allows for + * current output data plus leftover from previous period. + */ + int max_out_per_hop = state->mel_only ? dct->num_in : dct->num_out; + + /* Check that output data can be drained within the periods spanned by one + * FFT hop. Each hop consumes fft_hop_size input samples and produces + * max_out_per_hop + 2 (magic) int16_t output values. The sink provides at + * least fft_hop_size * channels int16_t samples per hop (worst case s16). + * If output exceeds this, data accumulates and will eventually overflow. + */ + int out_per_hop = max_out_per_hop + 2; + int sink_per_hop = fft->fft_hop_size * channels; + + if (out_per_hop > sink_per_hop) { + comp_err(dev, "Output %d int16 per hop exceeds sink capacity %d (hop %d x ch %d)", + out_per_hop, sink_per_hop, fft->fft_hop_size, channels); + ret = -EINVAL; + goto free_lifter; + } /* Set initial state for STFT */ state->waiting_fill = true; state->prev_samples_valid = false; + state->magic_pending = false; + state->out_data_ptr = NULL; + state->out_data_ptr_32 = NULL; + state->out_remain = 0; - comp_dbg(dev, "mfcc_setup(), done"); + comp_dbg(dev, "done"); return 0; +free_lifter: + mod_free(mod, state->lifter.matrix); + free_dct_matrix: - rfree(state->dct.matrix); + mod_free(mod, state->dct.matrix); free_melfb_data: - rfree(fb->data); + mod_free(mod, fb->data); + +free_fft_plan: + mod_fft_plan_free(mod, fft->fft_plan); free_fft_out: - rfree(fft->fft_out); + mod_free(mod, fft->fft_out); free_fft_buf: - rfree(fft->fft_buf); + mod_free(mod, fft->fft_buf); free_buffers: - rfree(state->buffers); + mod_free(mod, state->buffers); exit: return ret; } -void mfcc_free_buffers(struct mfcc_comp_data *cd) +void mfcc_free_buffers(struct processing_module *mod) { - fft_plan_free(cd->state.fft.fft_plan); - rfree(cd->state.fft.fft_buf); - rfree(cd->state.fft.fft_out); - rfree(cd->state.buffers); - rfree(cd->state.melfb.data); - rfree(cd->state.dct.matrix); - rfree(cd->state.lifter.matrix); + struct mfcc_comp_data *cd = module_get_private_data(mod); + + mod_fft_plan_free(mod, cd->state.fft.fft_plan); + mod_free(mod, cd->state.fft.fft_buf); + mod_free(mod, cd->state.fft.fft_out); + mod_free(mod, cd->state.buffers); + mod_free(mod, cd->state.melfb.data); + mod_free(mod, cd->state.dct.matrix); + mod_free(mod, cd->state.lifter.matrix); } diff --git a/src/audio/mfcc/tune/README.txt b/src/audio/mfcc/tune/README.txt new file mode 100644 index 000000000000..a0c3189e81a3 --- /dev/null +++ b/src/audio/mfcc/tune/README.txt @@ -0,0 +1,52 @@ +This directory contains a tool to create configuration blob for SOF +MFCC component. It's simply run in Matlab or Octave with command +"setup_mfcc". The MFCC configuration parameters can be edited from the +script. + +The configuration can be test run with testbench. First the test topologies +need to be created with "scripts/build-tools.sh -t". Next the testbench +is build with "scripts/rebuild-testbench.sh". + +Once the previous steps are done, a sample wav file can be processed +with script run_mfcc.sh. The script converts the input to raw 16 kHz +stereo format and runs the testbench for S16, S24, and S32 bit depths, +producing both cepstral coefficient (MFCC) and Mel spectrogram outputs. + +./run_mfcc.sh /usr/share/sounds/alsa/Front_Center.wav + +Output files from host testbench: + mfcc_s16.raw, mfcc_s24.raw, mfcc_s32.raw - cepstral coefficients + mel_s16.raw, mel_s24.raw, mel_s32.raw - Mel spectrogram + +If the XTENSA_PATH environment variable is set, the script also runs +the Xtensa build of the testbench (via xt-run) and produces additional +output files prefixed with "xt_": + xt_mfcc_s16.raw, xt_mfcc_s24.raw, xt_mfcc_s32.raw + xt_mel_s16.raw, xt_mel_s24.raw, xt_mel_s32.raw + +All output files can be decoded and plotted at once in Matlab or Octave +with the decode_all.m script: + +decode_all + +This calls decode_ceps for each MFCC file (13 cepstral coefficients) and +decode_mel for each Mel file (80 Mel bins), plotting spectrograms for all +files that exist including the Xtensa variants. + +Individual files can also be decoded manually: + +[ceps, t, n] = decode_ceps('mfcc_s16.raw', 13); + +In the above it's known from configuration script that MFCC was set up to +output 13 cepstral coefficients from each FFT -> Mel -> DCT -> Cepstral +coefficients computation run. + +The 80 bands Mel output can be visualized with command: + +[mel, t, n] = decode_mel('mel_s16.raw', 80); + +Other kind of signals have quite big visual difference in audio features. Try +e.g. other sound files found in computer. + +./run_mfcc.sh /usr/share/sounds/gnome/default/alerts/bark.ogg +./run_mfcc.sh /usr/share/sounds/gnome/default/alerts/sonar.ogg diff --git a/src/audio/mfcc/tune/decode_all.m b/src/audio/mfcc/tune/decode_all.m new file mode 100644 index 000000000000..d5b60289b4cf --- /dev/null +++ b/src/audio/mfcc/tune/decode_all.m @@ -0,0 +1,39 @@ +% decode_all.m - Decode all MFCC and Mel raw output files from run_mfcc.sh +% +% SPDX-License-Identifier: BSD-3-Clause +% Copyright(c) 2026 Intel Corporation. + +num_ceps = 13; +num_mel = 80; + +% MFCC cepstral output files +ceps_files = {'mfcc_s16.raw', 'mfcc_s24.raw', 'mfcc_s32.raw'}; + +% Mel output files with corresponding format +mel_files = {'mel_s16.raw', 'mel_s24.raw', 'mel_s32.raw'}; +mel_fmts = {'s16', 's24', 's32'}; + +% Xtensa prefixed variants +xt_ceps_files = {'xt_mfcc_s16.raw', 'xt_mfcc_s24.raw', 'xt_mfcc_s32.raw'}; +xt_mel_files = {'xt_mel_s16.raw', 'xt_mel_s24.raw', 'xt_mel_s32.raw'}; + +all_ceps_files = [ceps_files, xt_ceps_files]; +all_mel_files = [mel_files, xt_mel_files]; +all_mel_fmts = [mel_fmts, mel_fmts]; + +for i = 1:length(all_ceps_files) + fn = all_ceps_files{i}; + if exist(fn, 'file') + fprintf('Decoding MFCC ceps: %s\n', fn); + [ceps, t, n] = decode_ceps(fn, num_ceps); + end +end + +for i = 1:length(all_mel_files) + fn = all_mel_files{i}; + fmt = all_mel_fmts{i}; + if exist(fn, 'file') + fprintf('Decoding Mel: %s\n', fn); + [mel, t, n] = decode_mel(fn, num_mel, fmt); + end +end diff --git a/tools/tune/mfcc/decode_ceps.m b/src/audio/mfcc/tune/decode_ceps.m similarity index 100% rename from tools/tune/mfcc/decode_ceps.m rename to src/audio/mfcc/tune/decode_ceps.m diff --git a/src/audio/mfcc/tune/decode_mel.m b/src/audio/mfcc/tune/decode_mel.m new file mode 100644 index 000000000000..f6a723aa2040 --- /dev/null +++ b/src/audio/mfcc/tune/decode_mel.m @@ -0,0 +1,145 @@ +% [mel, t, n] = decode_mel(fn, num_mel, fmt, num_channels) +% +% Input +% fn - File with Mel data in .raw or .wav format +% num_mel - number of Mel coefficients per frame +% fmt - format of the Mel data ('s16', 's24', 's32') +% num_channels - needed for .raw format, omit for .wav +% +% Outputs +% mel - Mel coefficients +% t - time vector for plotting +% n - mel 1..num_mel vector for plotting + +% SPDX-License-Identifier: BSD-3-Clause +% Copyright(c) 2026 Intel Corporation. + +function [mel, t, n] = decode_mel(fn, num_mel, fmt, num_channels) + +if nargin < 3 + fmt = 's16'; +end +if nargin < 4 + num_channels = 1; +end + +% MFCC stream +fs = 16e3; + +switch fmt + case 's16' + qformat = 7; + magic = [25443 28006]; % ASCII 'mfcc' as two int16 + num_magic = 2; + case 's24' + qformat = 15; + magic = int32(1835426659); % 0x6D666363 as int32 + num_magic = 1; + case 's32' + qformat = 23; + magic = int32(1835426659); % 0x6D666363 as int32 + num_magic = 1; + otherwise + error("Use 's16', 's24', or 's32' as format."); +end + +% Load output data +[data, num_channels] = get_file(fn, num_channels, fmt); + +if strcmp(fmt, 's16') + idx1 = find(data == magic(1)); + idx = []; + for i = 1:length(idx1) + next_word = idx1(i) + 1; + if next_word <= length(data) + if data(next_word) == magic(2) + idx = [idx idx1(i)]; + end + end + end +else + idx = find(data == magic); +end + +if isempty(idx) + error('No magic value markers found from stream'); +end + +period_mel = idx(2)-idx(1); +num_frames = length(idx); + +% Last frame can be incomplete due to span over multiple periods +last = idx(end) + num_mel - 1; +if (last > length(data)) + num_frames = num_frames - 1; +end + +t_mel = period_mel / num_channels / fs; +t = (0:num_frames -1) * t_mel; +n = 1:num_mel; + +mel = zeros(num_mel, num_frames); +for i = 1:num_frames + i1 = idx(i) + num_magic; + i2 = i1 + num_mel - 1; + mel(:,i) = double(data(i1:i2)) / 2^qformat; +end + +figure; +imagesc(t, n, mel); +axis xy; +colormap(jet); +colorbar; +tstr = sprintf('SOF MFCC Mel coefficients (%s)', fn); +title(tstr, 'Interpreter', 'None'); +xlabel('Time (s)'); +ylabel('Mel coef #'); + +end + +function [data, num_channels] = get_file(fn, num_channels, fmt) + +[~, ~, ext] = fileparts(fn); + +switch fmt + case 's16' + read_fmt = 'int16'; + case {'s24', 's32'} + read_fmt = 'int32'; + otherwise + error("Use 's16', 's24', or 's32' as format."); +end + +switch lower(ext) + case '.raw' + fh = fopen(fn, 'r'); + data = fread(fh, read_fmt); + fclose(fh); + case '.wav' + tmp = audioread(fn, 'native'); + t = whos('tmp'); + switch fmt + case 's16' + if ~strcmp(t.class, 'int16') + error('Expected 16-bit wav for s16 format'); + end + case {'s24', 's32'} + if ~strcmp(t.class, 'int32') + error('Expected 32-bit wav for %s format', fmt); + end + end + s = size(tmp); + num_channels = s(2); + if num_channels > 1 + data = zeros(prod(s), 1, t.class); + for i = 1:num_channels + data(i:num_channels:end) = tmp(:, i); + end + else + data = tmp; + end + otherwise + error('Unknown audio format'); +end + +end diff --git a/src/audio/mfcc/tune/run_mfcc.sh b/src/audio/mfcc/tune/run_mfcc.sh new file mode 100755 index 000000000000..e3c309fbc03e --- /dev/null +++ b/src/audio/mfcc/tune/run_mfcc.sh @@ -0,0 +1,55 @@ +#!/bin/sh +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(c) 2022-2026 Intel Corporation. All rights reserved. + +set -e + +RAW_INPUT_S16=in_s16.raw +RAW_INPUT_S24=in_s24.raw +RAW_INPUT_S32=in_s32.raw + +VALGRIND="valgrind --leak-check=full" +#VALGRIND="" +TESTBENCH=$SOF_WORKSPACE/sof/tools/testbench/build_testbench/install/bin/sof-testbench4 +TESTBENCH_RUN="$VALGRIND $TESTBENCH" + +convert_input() { + sox -R --encoding signed-integer "$1" -L -r 16000 -c 2 -b 16 "$RAW_INPUT_S16" + sox -R --no-dither --encoding signed-integer -L -r 16000 -c 2 -b 16 \ + "$RAW_INPUT_S16" -b 32 "$RAW_INPUT_S32" + sox -R --no-dither --encoding signed-integer -L -r 16000 -c 2 -b 16 \ + "$RAW_INPUT_S16" -b 32 "$RAW_INPUT_S24" vol 0.003906250000 +} + +run_testbench() { + local tplg_base="$1" + local out_s16="$2" + local out_s24="$3" + local out_s32="$4" + local label="$5" + local tplg_s16="${SOF_WORKSPACE}/sof/tools/build_tools/topology/topology2/development/${tplg_base}16.tplg" + local tplg_s24="${SOF_WORKSPACE}/sof/tools/build_tools/topology/topology2/development/${tplg_base}24.tplg" + local tplg_s32="${SOF_WORKSPACE}/sof/tools/build_tools/topology/topology2/development/${tplg_base}32.tplg" + + $TESTBENCH_RUN -r 16000 -c 2 -b S16_LE -p 3,4 -t "$tplg_s16" -i "$RAW_INPUT_S16" -o "$out_s16" + $TESTBENCH_RUN -r 16000 -c 2 -b S24_LE -p 3,4 -t "$tplg_s24" -i "$RAW_INPUT_S24" -o "$out_s24" + $TESTBENCH_RUN -r 16000 -c 2 -b S32_LE -p 3,4 -t "$tplg_s32" -i "$RAW_INPUT_S32" -o "$out_s32" + + echo ---------------------------------------------------------------------------------- + echo "The ${label} data was output to file ${out_s16}, ${out_s24}, ${out_s32}" + echo ---------------------------------------------------------------------------------- +} + +main() { + convert_input "$1" + run_testbench "sof-hda-benchmark-mfcc" mfcc_s16.raw mfcc_s24.raw mfcc_s32.raw "MFCC" + run_testbench "sof-hda-benchmark-mfccmel" mel_s16.raw mel_s24.raw mel_s32.raw "MFCC Mel" + + if [ -n "$XTENSA_PATH" ]; then + TESTBENCH_RUN="$XTENSA_PATH/xt-run $SOF_WORKSPACE/sof/tools/testbench/build_xt_testbench/sof-testbench4" + run_testbench "sof-hda-benchmark-mfcc" xt_mfcc_s16.raw xt_mfcc_s24.raw xt_mfcc_s32.raw "Xtensa MFCC" + run_testbench "sof-hda-benchmark-mfccmel" xt_mel_s16.raw xt_mel_s24.raw xt_mel_s32.raw "Xtensa MFCC Mel" + fi +} + +main "$@" diff --git a/tools/tune/mfcc/setup_mfcc.m b/src/audio/mfcc/tune/setup_mfcc.m similarity index 60% rename from tools/tune/mfcc/setup_mfcc.m rename to src/audio/mfcc/tune/setup_mfcc.m index 9b27703e98bd..bd2b3f11e60b 100644 --- a/tools/tune/mfcc/setup_mfcc.m +++ b/src/audio/mfcc/tune/setup_mfcc.m @@ -1,22 +1,36 @@ -% setup_mfcc(cfg) +% setup_mfcc() % -% Input -% cfg - optional MFCC configuration parameters struct, see -% below from code -% -% Create binary configuration blob for MFCC component. The hex data -% is written to tools/topology/topology1/m4/mfcc/mfcc_config.m4 +% Create binary configuration blobs for the MFCC component. +% The hex data is written to files in directory +% tools/topology/topology2/include/components/mfcc. % SPDX-License-Identifier: BSD-3-Clause % -% Copyright (c) 2018-2020, Intel Corporation. All rights reserved. +% Copyright (c) 2018-2026, Intel Corporation. + +function setup_mfcc() + + gen_cfg.tplg_ver = 2; + gen_cfg.ipc_ver = 4; + gen_cfg.tools_path = '../../../../tools/'; + gen_cfg.mfcc_conf_path = [gen_cfg.tools_path 'topology/topology2/include/components/mfcc/']; + + % Default blob + setup = get_mfcc_default_config(); + setup.tplg_fn = 'default.conf'; + export_mfcc_setup(gen_cfg, setup); -function setup_mfcc(cfg) + % Blob for mel spectrogram data + setup = get_mel_spectrogram_config(); + setup.tplg_fn = 'mel80.conf'; + export_mfcc_setup(gen_cfg, setup); -if nargin < 1 +end + +function cfg = get_mfcc_default_config() cfg.blackman_coef = 0.42; cfg.cepstral_lifter = 22.0; - cfg.channel = -1; % -1 expect mono, 0 left, 1 right ... + cfg.channel = 0; % -1 expect mono, 0 left, 1 right ... cfg.dither = 0.0; % no support cfg.energy_floor = 1.0; cfg.frame_length = 25.0; % ms @@ -43,24 +57,54 @@ function setup_mfcc(cfg) cfg.mel_log = 'log'; % Set to 'db' for librosa, set to 'log10' for matlab cfg.pmin = 5e-10; % Set to 1e-10 for librosa cfg.top_db = 200; % Set to 80 for librosa + cfg.mel_offset = 0; % For mel_only mode, no impact with num_ceps > 0 + cfg.mel_scale = 0; % same + cfg.mmax_init = 0; % same + cfg.mmax_coef = 0; % same + cfg.dynamic_mmax = false; % same end -cfg.tplg_fn = '../../topology/topology1/m4/mfcc/mfcc_config.m4'; -cfg.tplg_ver = 1; -cfg.ipc_ver = 3; -export_mfcc_setup(cfg); - -cfg.tplg_fn = '../../topology/topology2/include/components/mfcc/default.conf'; -cfg.tplg_ver = 2; -cfg.ipc_ver = 4; -export_mfcc_setup(cfg); - +function cfg = get_mel_spectrogram_config() + cfg.blackman_coef = 0; + cfg.cepstral_lifter = 0; + cfg.channel = 0; + cfg.dither = 0; + cfg.energy_floor = 1.0; + cfg.frame_length = 25.0; % 400 samples at 16 kHz + cfg.frame_shift = 10.0; % 160 samples at 16 kHz + cfg.high_freq = 8000; + cfg.htk_compat = false; + cfg.low_freq = 0; + cfg.num_ceps = 0; % Mel-only mode, no DCT + cfg.min_duration = 0; + cfg.norm = 'slaney'; + cfg.num_mel_bins = 80; + cfg.preemphasis_coefficient = 0; + cfg.raw_energy = false; + cfg.remove_dc_offset = false; + cfg.round_to_power_of_two = true; + cfg.sample_frequency = 16000; + cfg.snip_edges = true; + cfg.subtract_mean = false; + cfg.use_energy = false; + cfg.vtln_high = 0; + cfg.vtln_low = 0; + cfg.vtln_warp = 1.0; + cfg.window_type = 'hann'; + cfg.mel_log = 'log10'; + cfg.pmin = 1e-10; + cfg.top_db = 8; % applied for log10, would be 80 dB clamp for decibels as 10*log10() + cfg.mel_offset = 4.0; % For whisper like Mel scale and normalize + cfg.mel_scale = 0.25; % For whisper like Mel scale and normalize + cfg.mmax_init = 0; % Initial value max Mel value, data clamp is mmax - top_db + cfg.mmax_coef = 0; % Dynamic max Mel value decay coefficient (zero lock to found max) + cfg.dynamic_mmax = true; end -function export_mfcc_setup(cfg) +function export_mfcc_setup(gen_cfg, cfg) %% Use blob tool from EQ -addpath('../common'); +addpath([gen_cfg.tools_path 'tune/common']); %% Blob size, size plus reserved(8) + current parameters nbytes_data = 104; @@ -70,7 +114,7 @@ function export_mfcc_setup(cfg) sh16 = [0 -8]; %% Get ABI information -[abi_bytes, nbytes_abi] = sof_get_abi(nbytes_data, cfg.ipc_ver); +[abi_bytes, nbytes_abi] = sof_get_abi(nbytes_data, gen_cfg.ipc_ver); %% Initialize correct size uint8 array nbytes = nbytes_abi + nbytes_data; @@ -83,14 +127,21 @@ function export_mfcc_setup(cfg) %% Apply default MFCC configuration, first struct header and reserved, then data [b8, j] = add_w32b(nbytes_data, b8, j); -for i = 1:8 + +v = q_convert(cfg.mel_offset, 7); [b8, j] = add_w16b(v, b8, j); +v = q_convert(cfg.mel_scale, 12); [b8, j] = add_w16b(v, b8, j); +v = q_convert(cfg.mmax_init, 7); [b8, j] = add_w16b(v, b8, j); +v = q_convert(cfg.mmax_coef, 15); [b8, j] = add_w16b(v, b8, j); + +% Reserved +for i = 1:6 [b8, j] = add_w32b(0, b8, j); end v = q_convert(cfg.sample_frequency, 0); [b8, j] = add_w32b(v, b8, j); v = q_convert(cfg.pmin, 31); [b8, j] = add_w32b(v, b8, j); -v = 0; [b8, j] = add_w32b(v, b8, j); % enum mel_log -v = 0; [b8, j] = add_w32b(v, b8, j); % enum norm +v = get_mel_log_value(cfg.mel_log); [b8, j] = add_w32b(v, b8, j); % enum mel_log +v = get_norm_value(cfg.norm); [b8, j] = add_w32b(v, b8, j); % enum norm v = 0; [b8, j] = add_w32b(v, b8, j); % enum pad v = get_window(cfg); [b8, j] = add_w32b(v, b8, j); % enum window v = 1; [b8, j] = add_w32b(v, b8, j); % enum dct type @@ -116,22 +167,24 @@ function export_mfcc_setup(cfg) v = cfg.snip_edges; [b8, j] = add_w8b(v, b8, j); % bool v = cfg.subtract_mean; [b8, j] = add_w8b(v, b8, j); % bool v = cfg.use_energy; [b8, j] = add_w8b(v, b8, j); % bool +v = cfg.dynamic_mmax; [b8, j] = add_w8b(v, b8, j); % bool %% Export -switch cfg.tplg_ver +tplg_fn = [gen_cfg.mfcc_conf_path cfg.tplg_fn]; +switch gen_cfg.tplg_ver case 1 - sof_tplg_write(cfg.tplg_fn, b8, "DEF_MFCC_PRIV", ... + sof_tplg_write(tplg_fn, b8, "DEF_MFCC_PRIV", ... "Exported with script setup_mfcc.m", ... - "cd tools/tune/mfcc; octave setup_mfcc.m"); + "cd src/audio/mfcc/tune; octave setup_mfcc.m"); case 2 - sof_tplg2_write(cfg.tplg_fn, b8, "mfcc_config", ... + sof_tplg2_write(tplg_fn, b8, "mfcc_config", ... "Exported MFCC configuration", ... - "cd tools/tune/mfcc; octave setup_mfcc.m"); + "cd src/audio/mfcc/tune; octave setup_mfcc.m"); otherwise - error("Illegal cfg.tplg_ver, use 1 for topology v1 or 2 topology v2."); + error("Illegal tplg_ver, use 1 for topology v1 or 2 topology v2."); end -rmpath('../common'); +rmpath([gen_cfg.tools_path 'tune/common']); end @@ -154,6 +207,30 @@ function export_mfcc_setup(cfg) end end +function n = get_mel_log_value(mel_log) + switch lower(mel_log) + case 'log' + n = 0; + case 'log10' + n = 1; + case 'db' + n = 2; + otherwise + error('Unknown mel_log type'); + end +end + +function n = get_norm_value(norm) + switch lower(norm) + case 'none' + n = 0; + case 'slaney' + n = 1; + otherwise + error('Unknown norm type'); + end +end + function bytes = w8b(word) bytes = uint8(zeros(1,1)); bytes(1) = bitand(word, 255); diff --git a/src/audio/mic_privacy_manager/README.md b/src/audio/mic_privacy_manager/README.md new file mode 100644 index 000000000000..8d85f43e1a2b --- /dev/null +++ b/src/audio/mic_privacy_manager/README.md @@ -0,0 +1,11 @@ +# Microphone Privacy Manager Architecture + +This directory contains the Mic Privacy Manager. + +## Overview + +Provides a secure mechanism to optionally mute the microphone paths deeply inside the DSP, ensuring user privacy regardless of host OS behavior. + +## Configuration and Scripts + +- **CMakeLists.txt**: Simply builds the Intel-specific privacy implementation (`mic_privacy_manager_intel.c`). Currently, there is no generic or separate module wrapper defined within the scope of this directory. diff --git a/src/audio/mixer/README.md b/src/audio/mixer/README.md new file mode 100644 index 000000000000..eef844d0313c --- /dev/null +++ b/src/audio/mixer/README.md @@ -0,0 +1,22 @@ +# Mixer Architecture + +This directory contains the Mixer component. + +## Overview + +The Mixer adds together multiple input audio streams into a single output stream, applying required scaling or saturation logic. + +## Architecture Diagram + +```mermaid +graph TD + In1[Source 1] --> Mix[Mixer Core] + In2[Source 2] --> Mix + InN[Source N] --> Mix + Mix --> Out[Mixed Output] +``` + +## Configuration and Scripts + +- **Kconfig**: Enables the Mixer component (`COMP_MIXER`), which inherently depends on the older IPC framework `IPC_MAJOR_3`. +- **CMakeLists.txt**: Manages local base sources and optimized implementations (`mixer_generic.c`, `mixer_hifi3.c`) alongside the core `mixer.c`. diff --git a/src/audio/mixer/mixer.c b/src/audio/mixer/mixer.c index 703e87ecee43..26023bb6a570 100644 --- a/src/audio/mixer/mixer.c +++ b/src/audio/mixer/mixer.c @@ -14,7 +14,6 @@ #include <sof/common.h> #include <rtos/panic.h> #include <sof/ipc/msg.h> -#include <rtos/alloc.h> #include <rtos/init.h> #include <sof/lib/uuid.h> #include <sof/list.h> @@ -36,18 +35,15 @@ LOG_MODULE_REGISTER(mixer, CONFIG_SOF_LOG_LEVEL); SOF_DEFINE_REG_UUID(mixer); -DECLARE_TR_CTX(mixer_tr, SOF_UUID(mixer_uuid), LOG_LEVEL_INFO); - - static int mixer_init(struct processing_module *mod) { struct module_data *mod_data = &mod->priv; struct comp_dev *dev = mod->dev; struct mixer_data *md; - comp_dbg(dev, "mixer_init()"); + comp_dbg(dev, "entry"); - md = rzalloc(SOF_MEM_FLAG_USER, sizeof(*md)); + md = mod_zalloc(mod, sizeof(*md)); if (!md) return -ENOMEM; @@ -64,9 +60,9 @@ static int mixer_free(struct processing_module *mod) struct mixer_data *md = module_get_private_data(mod); struct comp_dev *dev = mod->dev; - comp_dbg(dev, "mixer_free()"); + comp_dbg(dev, "entry"); - rfree(md); + mod_free(mod, md); return 0; } @@ -89,7 +85,7 @@ static int mixer_process(struct processing_module *mod, uint32_t sink_bytes; int active_input_buffers = 0; - comp_dbg(dev, "mixer_process() %d", num_input_buffers); + comp_dbg(dev, "%d", num_input_buffers); /* too many sources ? */ if (num_input_buffers >= PLATFORM_MAX_STREAMS) @@ -129,7 +125,7 @@ static int mixer_process(struct processing_module *mod, sink_bytes = frames * audio_stream_frame_bytes(mod->output_buffers[0].data); - comp_dbg(dev, "mixer_process(), source_bytes = 0x%x, sink_bytes = 0x%x", + comp_dbg(dev, "source_bytes = 0x%x, sink_bytes = 0x%x", source_bytes, sink_bytes); /* mix streams */ @@ -164,7 +160,7 @@ static int mixer_reset(struct processing_module *mod) struct comp_dev *dev = mod->dev; int dir = dev->pipeline->source_comp->direction; - comp_dbg(dev, "mixer_reset()"); + comp_dbg(dev, "entry"); if (dir == SOF_IPC_STREAM_PLAYBACK) { struct comp_buffer *source; @@ -191,18 +187,18 @@ static int mixer_reset(struct processing_module *mod) /* init and calculate the aligned setting for available frames and free frames retrieve*/ static inline void mixer_set_frame_alignment(struct audio_stream *source) { -#if XCHAL_HAVE_HIFI3 || XCHAL_HAVE_HIFI4 /* Xtensa intrinsics ask for 8-byte aligned. 5.1 format SSE audio - * requires 16-byte aligned. + * requires 16-byte aligned. Note: The SOF_FRAME_BYTE_ALIGN is the + * same value 16 with HiFi5. */ - const uint32_t byte_align = audio_stream_get_channels(source) == 6 ? 16 : 8; + const uint32_t byte_align = audio_stream_get_channels(source) == 6 ? + MIXER_HIFI_FRAME_BYTE_ALIGN_6CH : SOF_FRAME_BYTE_ALIGN; - /*There is no limit for frame number, so set it as 1*/ - const uint32_t frame_align_req = 1; + /* There is no limit for frame number, so set it as default (1). */ + const uint32_t frame_align_req = SOF_FRAME_COUNT_ALIGN; audio_stream_set_align(byte_align, frame_align_req, source); -#endif } static int mixer_prepare(struct processing_module *mod, @@ -220,7 +216,10 @@ static int mixer_prepare(struct processing_module *mod, } md->mix_func = mixer_get_processing_function(dev, sink); - mixer_set_frame_alignment(&sink->stream); + + /* No need to set sink align constraints, set constraints for each + * source next. The sink align will follow to common source alignment. + */ /* check each mixer source state */ struct comp_buffer *source; @@ -257,5 +256,6 @@ static const struct module_interface mixer_interface = { .free = mixer_free, }; +DECLARE_TR_CTX(mixer_tr, SOF_UUID(mixer_uuid), LOG_LEVEL_INFO); DECLARE_MODULE_ADAPTER(mixer_interface, mixer_uuid, mixer_tr); SOF_MODULE_INIT(mixer, sys_comp_module_mixer_interface_init); diff --git a/src/audio/mixer/mixer.h b/src/audio/mixer/mixer.h index 118aaf1c914f..875ddcbb26fe 100644 --- a/src/audio/mixer/mixer.h +++ b/src/audio/mixer/mixer.h @@ -32,6 +32,9 @@ void sys_comp_module_mixer_interface_init(void); #define MIXER_MAX_SOURCES 2 +/* Xtensa HiFi optimized version needs this for 5.1ch */ +#define MIXER_HIFI_FRAME_BYTE_ALIGN_6CH 16 + /* mixer component private data */ struct mixer_data { void (*mix_func)(struct comp_dev *dev, struct audio_stream *sink, diff --git a/src/audio/mixin_mixout/README.md b/src/audio/mixin_mixout/README.md new file mode 100644 index 000000000000..a73b39e7655b --- /dev/null +++ b/src/audio/mixin_mixout/README.md @@ -0,0 +1,14 @@ +# Mix-In / Mix-Out Architecture + +This directory provides specialized Mix-in and Mix-out instances. + +## Overview + +These components typically act as routing endpoints to inject or extract specific streams in/out of an ongoing audio mix. + +## Configuration and Scripts + +- **Kconfig**: Compiles the Mixin_mixout component (`COMP_MIXIN_MIXOUT`), which depends on the modern `IPC_MAJOR_4`. Allows choosing SIMD optimization logic explicitly. +- **CMakeLists.txt**: Integrates generic, HIFI3, and HIFI5 specialized processing source files. Fully supports modular execution via `llext`. +- **mixin_mixout.toml**: Extensive configuration separating `MIXIN` and `MIXOUT` instances. Configures UUIDs, domain types, and highly localized `mod_cfg` arrays adapted for `CONFIG_METEORLAKE`, `CONFIG_LUNARLAKE`, and ACE SOCs. +- **Topology (.conf)**: Uses `tools/topology/topology2/include/components/mixin.conf` (type `mixer` with `mix_type` `"mix_in"`, defaulting to UUID `b2:6e:65:39:71:3b:49:40:8d:3f:f9:2c:d5:c4:3c:09`, supporting 3 output pins) and `mixout.conf` (type `mixer` with `mix_type` `"mix_out"`, defaulting to UUID `5a:50:56:3c:d7:24:8f:41:bd:dc:c1:f5:a3:ac:2a:e0`, mapping to 8 input pins). Both force a 32-bit depth processing format. diff --git a/src/audio/mixin_mixout/mixin_mixout.c b/src/audio/mixin_mixout/mixin_mixout.c index ec473baf6e58..81dfb20e143e 100644 --- a/src/audio/mixin_mixout/mixin_mixout.c +++ b/src/audio/mixin_mixout/mixin_mixout.c @@ -12,8 +12,6 @@ #include <sof/compiler_attributes.h> #include <rtos/panic.h> #include <sof/ipc/msg.h> -#include <sof/ipc/notification_pool.h> -#include <rtos/alloc.h> #include <rtos/init.h> #include <sof/lib/uuid.h> #include <sof/list.h> @@ -36,11 +34,9 @@ LOG_MODULE_REGISTER(mixin_mixout, CONFIG_SOF_LOG_LEVEL); /* mixin 39656eb2-3b71-4049-8d3f-f92cd5c43c09 */ SOF_DEFINE_REG_UUID(mixin); -DECLARE_TR_CTX(mixin_tr, SOF_UUID(mixin_uuid), LOG_LEVEL_INFO); /* mixout 3c56505a-24d7-418f-bddc-c1f5a3ac2ae0 */ SOF_DEFINE_REG_UUID(mixout); -DECLARE_TR_CTX(mixout_tr, SOF_UUID(mixout_uuid), LOG_LEVEL_INFO); #define MIXIN_MAX_SINKS IPC4_MIXIN_MODULE_MAX_OUTPUT_QUEUES #define MIXOUT_MAX_SOURCES IPC4_MIXOUT_MODULE_MAX_INPUT_QUEUES @@ -141,7 +137,7 @@ static int mixin_init(struct processing_module *mod) comp_dbg(dev, "entry"); - md = rzalloc(SOF_MEM_FLAG_USER, sizeof(*md)); + md = mod_zalloc(mod, sizeof(*md)); if (!md) return -ENOMEM; @@ -168,7 +164,7 @@ static int mixout_init(struct processing_module *mod) comp_dbg(dev, "entry"); - mo_data = rzalloc(SOF_MEM_FLAG_USER, sizeof(*mo_data)); + mo_data = mod_zalloc(mod, sizeof(*mo_data)); if (!mo_data) return -ENOMEM; @@ -184,14 +180,14 @@ static int mixin_free(struct processing_module *mod) { struct mixin_data *md = module_get_private_data(mod); - rfree(md); + mod_free(mod, md); return 0; } static int mixout_free(struct processing_module *mod) { - rfree(module_get_private_data(mod)); + mod_free(mod, module_get_private_data(mod)); return 0; } @@ -256,8 +252,6 @@ static void mixin_check_notify_underrun(struct comp_dev *dev, struct mixin_data const bool eos_detected = state == AUDIOBUF_STATE_END_OF_STREAM_FLUSH || state == AUDIOBUF_STATE_END_OF_STREAM; - struct ipc_msg *notify; - mixin_data->last_reported_underrun++; if (!source_avail || eos_detected) { @@ -276,13 +270,8 @@ static void mixin_check_notify_underrun(struct comp_dev *dev, struct mixin_data (eos_detected && mixin_data->eos_delay_periods == 0)) { mixin_data->last_reported_underrun = 0; - notify = ipc_notification_pool_get(IPC4_RESOURCE_EVENT_SIZE); - if (!notify) - return; - - mixer_underrun_notif_msg_init(notify, dev->ipc_config.id, eos_detected, + send_mixer_underrun_notif_msg(dev->ipc_config.id, eos_detected, source_avail, sinks_free); - ipc_msg_send(notify, notify->tx_data, false); } } } @@ -718,7 +707,9 @@ static int mixin_prepare(struct processing_module *mod, int ret; comp_info(dev, "entry"); +#if CONFIG_XRUN_NOTIFICATIONS_ENABLE md->eos_delay_configured = false; +#endif ret = mixin_params(mod); if (ret < 0) @@ -1051,9 +1042,11 @@ SOF_LLEXT_BUILDINFO; #else +DECLARE_TR_CTX(mixin_tr, SOF_UUID(mixin_uuid), LOG_LEVEL_INFO); DECLARE_MODULE_ADAPTER(mixin_interface, mixin_uuid, mixin_tr); SOF_MODULE_INIT(mixin, sys_comp_module_mixin_interface_init); +DECLARE_TR_CTX(mixout_tr, SOF_UUID(mixout_uuid), LOG_LEVEL_INFO); DECLARE_MODULE_ADAPTER(mixout_interface, mixout_uuid, mixout_tr); SOF_MODULE_INIT(mixout, sys_comp_module_mixout_interface_init); diff --git a/src/audio/mixin_mixout/mixin_mixout.toml b/src/audio/mixin_mixout/mixin_mixout.toml index 812505b19fa8..16edc0567aa3 100644 --- a/src/audio/mixin_mixout/mixin_mixout.toml +++ b/src/audio/mixin_mixout/mixin_mixout.toml @@ -32,7 +32,7 @@ 2, 0, 0, 0, 296, 2448000, 512, 512, 0, 2448, 0, 3, 0, 0, 0, 296, 2160000, 128, 128, 0, 2160, 0, 4, 0, 0, 0, 296, 3268000, 1536, 1536, 0, 3268, 0] -#elif CONFIG_SOC_INTEL_ACE30 || CONFIG_SOC_INTEL_ACE40 +#elif CONFIG_SOC_ACE30 || CONFIG_SOC_ACE40 mod_cfg = [0, 0, 0, 0, 296, 5091000, 384, 384, 0, 5091, 0, 1, 0, 0, 0, 296, 5111000, 384, 384, 0, 5111, 0, 2, 0, 0, 0, 296, 5195000, 512, 512, 0, 5195, 0, @@ -77,7 +77,7 @@ 2, 0, 0, 0, 520, 7631000, 512, 512, 0, 0, 0, 3, 0, 0, 0, 520, 1953000, 128, 128, 0, 0, 0, 4, 0, 0, 0, 520, 2301000, 1536, 1536, 0, 0, 0] -#elif CONFIG_SOC_INTEL_ACE30 || CONFIG_SOC_INTEL_ACE40 +#elif CONFIG_SOC_ACE30 || CONFIG_SOC_ACE40 mod_cfg = [0, 0, 0, 0, 520, 3999000, 384, 384, 0, 3999, 0, 1, 0, 0, 0, 520, 3999000, 384, 384, 0, 3999, 0, 2, 0, 0, 0, 520, 4055000, 512, 512, 0, 4055, 0, diff --git a/src/audio/module_adapter/CMakeLists.txt b/src/audio/module_adapter/CMakeLists.txt index 0ad91176bfc5..c4b4a43e99e5 100644 --- a/src/audio/module_adapter/CMakeLists.txt +++ b/src/audio/module_adapter/CMakeLists.txt @@ -10,13 +10,21 @@ is_zephyr(zephyr) if(zephyr) ### Zephyr ### # modules and codecs in alphabetical order - - zephyr_library_sources_ifdef(CONFIG_CADENCE_CODEC module/cadence.c) +if (CONFIG_IPC_MAJOR_3) + zephyr_library_sources_ifdef(CONFIG_CADENCE_CODEC module/cadence.c module/cadence_ipc3.c) +elseif (CONFIG_IPC_MAJOR_4) + zephyr_include_directories(${sof_top_dir}/src/include/sof/audio/cadence) + zephyr_library_sources_ifdef(CONFIG_CADENCE_CODEC module/cadence.c module/cadence_ipc4.c) +endif() if (CONFIG_CADENCE_CODEC_AAC_DEC) zephyr_library_import(xa_aac_dec ${CONFIG_CADENCE_CODEC_AAC_DEC_LIB}) endif() + if (CONFIG_CADENCE_CODEC_VORBIS_DEC) + zephyr_library_import(xa_vorbis_dec ${CONFIG_CADENCE_CODEC_VORBIS_DEC_LIB}) + endif() + if (CONFIG_CADENCE_CODEC_MP3_DEC) zephyr_library_import(xa_mp3_dec ${CONFIG_CADENCE_CODEC_MP3_DEC_LIB}) endif() @@ -25,6 +33,29 @@ if(zephyr) ### Zephyr ### zephyr_library_import(xa_mp3_enc ${CONFIG_CADENCE_CODEC_MP3_ENC_LIB}) endif() + if (CONFIG_COMP_DOLBY_DAX_AUDIO_PROCESSING) + if(CONFIG_COMP_DOLBY_DAX_AUDIO_PROCESSING STREQUAL "m" AND DEFINED CONFIG_LLEXT) + add_subdirectory(module/dolby/llext + ${PROJECT_BINARY_DIR}/dolby_dax_audio_processing_llext) + add_dependencies(app dolby_dax_audio_processing) + else() + zephyr_library_sources( + module/dolby/dax.c + module/dolby/dax_instance_manager.c + ) + if (CONFIG_COMP_DOLBY_DAX_AUDIO_PROCESSING_MOCK) + zephyr_library_sources( + module/dolby/dax_mock.c + ) + else() + target_link_libraries(SOF INTERFACE m) + target_link_libraries(SOF INTERFACE c) + zephyr_library_import(dax_effect + ${sof_top_dir}/third_party/lib/libdax.a) + endif() + endif() + endif() + zephyr_include_directories_ifdef(CONFIG_LIBRARY_MANAGER ${SOF_SRC_PATH}/include/sof/audio/module_adapter/iadk/ ${SOF_SRC_PATH}/include/sof/audio/module_adapter/library/ @@ -42,6 +73,11 @@ if(zephyr) ### Zephyr ### library/native_system_service.c ) + zephyr_library_sources_ifdef(CONFIG_SOF_USERSPACE_PROXY + library/userspace_proxy.c + library/userspace_proxy_user.c + ) + zephyr_library_sources_ifdef(CONFIG_PASSTHROUGH_CODEC module/passthrough.c ) @@ -112,6 +148,20 @@ if(NOT CONFIG_COMP_MODULE_SHARED_LIBRARY_BUILD) endif() + if(CONFIG_COMP_DOLBY_DAX_AUDIO_PROCESSING) + target_include_directories(sof PRIVATE ${PROJECT_SOURCE_DIR}/third_party/include) + add_local_sources(sof module/dolby/dax.c) + add_local_sources(sof module/dolby/dax_instance_manager.c) + if (CONFIG_COMP_DOLBY_DAX_AUDIO_PROCESSING_MOCK) + add_local_sources(sof module/dolby/dax_mock.c) + else() + target_link_libraries(sof PRIVATE m) + target_link_libraries(sof PRIVATE c) + sof_add_static_library(dax_effect + ${PROJECT_SOURCE_DIR}/third_party/lib/libdax.a) + endif() + endif() + if(CONFIG_PASSTHROUGH_CODEC) add_local_sources(sof module/passthrough.c) endif() diff --git a/src/audio/module_adapter/Kconfig b/src/audio/module_adapter/Kconfig index 7ed6b859cd64..a9f88dd1f2ad 100644 --- a/src/audio/module_adapter/Kconfig +++ b/src/audio/module_adapter/Kconfig @@ -175,6 +175,22 @@ if CADENCE_CODEC endif # Cadence + config COMP_DOLBY_DAX_AUDIO_PROCESSING + tristate "Dolby DAX audio processing component" + help + Select to include Dolby DAX component. Dolby DAX component implements DAX API. + API definition together with pre-compiled library is shared by Dolby. + If library is not provided, COMP_DOLBY_DAX_AUDIO_PROCESSING_MOCK must be true, + then the input will be copied to the output. + + config COMP_DOLBY_DAX_AUDIO_PROCESSING_MOCK + bool "Dolby DAX audio processing component mock" + default y if COMP_STUBS + depends on COMP_DOLBY_DAX_AUDIO_PROCESSING + help + Mock DAX audio processing. It allows for compilation check and basic audio + flow checking. + config PASSTHROUGH_CODEC bool "Passthrough codec" help diff --git a/src/audio/module_adapter/README.md b/src/audio/module_adapter/README.md new file mode 100644 index 000000000000..c642c46096bd --- /dev/null +++ b/src/audio/module_adapter/README.md @@ -0,0 +1,115 @@ +# Module Adapter Architecture + +This directory contains the Module Adapter. + +## Overview + +The Module Adapter is a crucial piece of the IPC4 pipeline, allowing the core SOF graph mechanism to interact with 3rd party processing modules (like Windows APOs or customized vendor DSP engines) through a generic wrapper format. + +## Creation and Teardown Flow + +The Module Adapter wraps an external DSP processing component and translates standard SOF pipeline calls (like `comp_new`, `comp_free`, `comp_trigger`) into an interface the external module natively understands (`ops->init`, `ops->free`, `ops->reset`, etc.). + +```mermaid +sequenceDiagram + participant Pipe as Pipeline + participant ModAd as Module Adapter (module_adapter_new) + participant Mod as Processing Module + participant Ext as External DSP Module (e.g., Windows APO) + + Pipe->>ModAd: comp_new() + activate ModAd + ModAd->>ModAd: Allocates comp_dev & struct processing_module + ModAd->>ModAd: module_adapter_dp_heap_new() (If DP domain) + ModAd->>ModAd: pipeline_comp_dp_task_init() (Spins up Zephyr thread if DP) + ModAd->>Mod: module_init() + Mod->>Ext: ops->init(mod) + Ext-->>ModAd: Init OK + ModAd-->>Pipe: Component READY + deactivate ModAd + + Note over Pipe,Ext: ... Audio Streaming ... + + Pipe->>ModAd: comp_free() + activate ModAd + ModAd->>Mod: module_free() + Mod->>Ext: ops->free(mod) + ModAd->>ModAd: schedule_task_free() (terminates thread) + ModAd->>ModAd: mod_free_all() (cleans up objpool and heap) + ModAd-->>Pipe: Component Destroyed + deactivate ModAd +``` + +## Configuration and Binding Flow + +Connection establishes buffer relationships (`comp_buffer`) connecting the component sources and sinks to the rest of the generic pipeline graph. + +```mermaid +sequenceDiagram + participant Pipe as Pipeline + participant ModAd as Module Adapter + participant Mod as Processing Module + + Pipe->>ModAd: comp_bind() / comp_unbind() + activate ModAd + ModAd->>Mod: module_bind() / module_unbind() + ModAd->>ModAd: module_update_source_buffer_params() + ModAd-->>Pipe: Bind successfully mapped + deactivate ModAd + + Pipe->>ModAd: comp_prepare() + activate ModAd + ModAd->>ModAd: Calculate chunks (period bytes, deep_buff_bytes) + ModAd->>Mod: module_prepare() + Mod-->>ModAd: External DSP prepared internal states + ModAd-->>Pipe: Prepared (Buffer formats synced) + deactivate ModAd +``` + +## Processing Flow (DP and LL Execution) + +To unify components operating directly in interrupt boundaries (DMA Low Latency (LL)) with heavy computational blocks executing asynchronously in Zephyr RTOS threads (Data Processing (DP)), the wrapper intercepts `comp_copy()`. + +### Low Latency (LL) Execution + +For LL modules, processing is fully synchronous within the pipeline tick: + +1. `comp_copy()` invokes `module_adapter_copy()`. +2. Data is fetched directly into `module_process_legacy()` or `module_process_sink_src()`. +3. `ops->process()` consumes upstream arrays and produces downstream arrays. + +### Data Processing (DP) Execution + +For DP modules, a discrete Zephyr thread manages execution asynchronously: + +```mermaid +sequenceDiagram + participant Sched as LL Scheduler + participant ModAd as Module Adapter (comp_copy) + participant DP as DP Thread (dp_task_run) + participant Mod as Processing Module (Ops) + + Sched->>ModAd: comp_copy() + activate ModAd + ModAd->>ModAd: Checks source data & sink free space + ModAd->>DP: Wakeup background thread (module_adapter_process_dp) + ModAd-->>Sched: Returns (No computation done yet) + deactivate ModAd + + activate DP + DP->>Mod: module_process_sink_src() + Mod->>Mod: ops->process(sources, sinks) + Mod-->>DP: Computation Complete + DP->>Sched: Signals data is ready + deactivate DP +``` + +## Error Handling and Memory Sandboxing + +* **Sandboxing (`mod_balloc_align`, `z_impl_mod_fast_get`, `z_impl_mod_free`)**: Since third-party DSP code is treated as semi-untrusted in memory lifetimes, module allocations grab slices from a dedicated component `dp_heap_user` heap instead of the global system heap (`mod_heap_info`). The wrapper automatically prunes leaked objects (`mod_free_all(mod)`) during teardown by keeping an `objpool` of all resource containers. +* **Reset Propagation**: Re-initializations via `COMP_TRIGGER_STOP` map down to `module_reset()` clearing the runtime state of the nested DSP module back to `MODULE_INITIALIZED`. + +## Configuration and Scripts + +* **Kconfig**: Highly customizable environment for "Processing modules" (`COMP_MODULE_ADAPTER`). Provides options for memory allocations, CADENCE codecs (AAC, BSAC, DAB, DRM, MP3, SBC, VORBIS, and their associated libraries), Dolby DAX Audio processing hooks (with stub support), Waves MaxxEffect codec support, and Intel module loaders. +* **CMakeLists.txt**: Handles the sprawling linkage process for the enabled processing modules. Links the core IPC abstraction layers (`module_adapter_ipc3.c` vs `module_adapter_ipc4.c`), external static libraries directly (e.g., `libdax.a`, `libMaxxChrome.a`, arbitrary CADENCE libraries), and includes custom Zephyr build options for `IADK` and `LIBRARY_MANAGER` systems. diff --git a/src/audio/module_adapter/iadk/README.md b/src/audio/module_adapter/iadk/README.md new file mode 100644 index 000000000000..d04ea1924bd8 --- /dev/null +++ b/src/audio/module_adapter/iadk/README.md @@ -0,0 +1,85 @@ +# Intel Audio Development Kit (`module_adapter/iadk`) + +The `iadk` directory provides the Module Adapter implementation for external 3rd-party audio algorithms developed using the **Intel Audio Development Kit (IADK)**. + +Unlike the native SOF `module_interface` API (written primarily in C), the IADK modules are object-oriented C++ classes that derive from `intel_adsp::ProcessingModuleInterface`. The SOF `IadkModuleAdapter` acts as a C++ to C "glue layer" that wraps these IADK methods so they can be natively plugged into the SOF `module_adapter` pipeline without modification to the module's pre-compiled binary. + +## Architecture and Class Hierarchy + +The system defines an `IadkModuleAdapter` class which internally holds an instance of the 3rd-party `ProcessingModuleInterface`. + +```mermaid +classDiagram + class SOF_ModuleAdapter { + +init + +process + +bind + } + + class iadk_wrapper { + iadk_wrapper_process + iadk_wrapper_init + } + + class IadkModuleAdapter { + -processing_module_ + +IadkModuleAdapter_Init + +IadkModuleAdapter_Process + +IadkModuleAdapter_SetConfiguration + } + + class ProcessingModuleInterface { + +Init + +Process + +SetConfiguration + +Reset + } + + class IADK_3rdParty_Algorithm { + +Init + +Process + } + + SOF_ModuleAdapter --> iadk_wrapper : C Function Pointers + iadk_wrapper --> IadkModuleAdapter : Instantiates and Wraps + IadkModuleAdapter --> ProcessingModuleInterface : Polymorphic Interface + ProcessingModuleInterface <|-- IADK_3rdParty_Algorithm : Inherits +``` + +## System Agent and Instantiation Flow + +Because the actual module resides in an external binary, it requires a "System Agent" to correctly instantiate the C++ objects during the component's `init` phase. + +1. The OS host driver sends an IPC `INIT_INSTANCE` command for the module. +2. The `system_agent_start()` function intercepts this, invokes the dynamic module's `create_instance` entry point (which invokes a `ModuleFactory`). +3. The `SystemAgent` deduces the pin count (interfaces) and initial pipeline configurations using `ModuleInitialSettingsConcrete`. +4. The factory allocates the concrete algorithm and checks it back into SOF through `SystemAgent::CheckIn`. + +```mermaid +sequenceDiagram + participant IPC + participant SA + participant Fac + participant Mod + + IPC->>SA: Trigger Mod Creation + SA->>Fac: CI invokes create_instance + Fac->>Fac: Deduce BaseModuleCfgExt + Fac->>Mod: operator new instantiate + + Fac->>SA: SystemAgent CheckIn Module + SA->>Adp: Create new IadkModuleAdapter Module + + SA-->>IPC: Return CPP adapter to C Pipeline +``` + +## Data Buffer Translation + +A significant task of `IadkModuleAdapter_Process` is converting SOF's underlying buffer formats to IADK's `InputStreamBuffer` and `OutputStreamBuffer` structures. + +Instead of letting the module directly touch the SOF `comp_buffer` (which could change with SOF version updates), the adapter uses the abstraction APIs (`source_get_data` / `sink_get_buffer`) and wraps them: + +1. Request raw continuous memory pointers from `source_get_data()`. +2. Construct an `intel_adsp::InputStreamBuffer` pointing to that continuous memory chunk. +3. Call the IADK `processing_module_.Process()`. +4. Release precisely the amount of consumed data using `source_release_data()`. diff --git a/src/audio/module_adapter/iadk/system_agent.cpp b/src/audio/module_adapter/iadk/system_agent.cpp index 860d5b1b279b..caed8cf65860 100644 --- a/src/audio/module_adapter/iadk/system_agent.cpp +++ b/src/audio/module_adapter/iadk/system_agent.cpp @@ -13,6 +13,7 @@ #include <stddef.h> #include <stdint.h> #include <rtos/string.h> +#include <rtos/userspace_helper.h> #include <utilities/array.h> #include <module/iadk/adsp_error_code.h> #include <system_service.h> @@ -21,6 +22,7 @@ #include <iadk_module_adapter.h> #include <system_agent.h> #include <sof/audio/module_adapter/library/native_system_service.h> +#include <sof/audio/module_adapter/library/native_system_agent.h> using namespace intel_adsp; using namespace intel_adsp::system; @@ -37,7 +39,7 @@ namespace system { /* Structure storing handles to system service operations */ -const AdspSystemService SystemAgent::system_service_ = { +const APP_TASK_DATA AdspSystemService SystemAgent::system_service_ = { native_system_service_log_message, native_system_service_safe_memcpy, native_system_service_safe_memmove, @@ -124,16 +126,17 @@ int SystemAgent::CheckIn(ProcessingModuleFactoryInterface& module_factory, typedef int (*create_instance_f)(uint32_t module_id, uint32_t instance_id, uint32_t core_id, void *mod_cfg, void *parent_ppl, void **mod_ptr); -int system_agent_start(uintptr_t entry_point, uint32_t module_id, uint32_t instance_id, - uint32_t core_id, uint32_t log_handle, void* mod_cfg, +int system_agent_start(const struct system_agent_params *params, const void **adapter) { uint32_t ret; - SystemAgent system_agent(module_id, instance_id, core_id, log_handle); + SystemAgent system_agent(params->module_id, params->instance_id, params->core_id, + params->log_handle); void* system_agent_p = reinterpret_cast<void*>(&system_agent); - create_instance_f ci = (create_instance_f)(entry_point); - ret = ci(module_id, instance_id, core_id, mod_cfg, NULL, &system_agent_p); + create_instance_f ci = (create_instance_f)(params->entry_point); + ret = ci(params->module_id, params->instance_id, params->core_id, params->mod_cfg, NULL, + &system_agent_p); IadkModuleAdapter* module_adapter = reinterpret_cast<IadkModuleAdapter*>(system_agent_p); *adapter = module_adapter; diff --git a/src/audio/module_adapter/library/README.md b/src/audio/module_adapter/library/README.md new file mode 100644 index 000000000000..3dd9aee14f7c --- /dev/null +++ b/src/audio/module_adapter/library/README.md @@ -0,0 +1,80 @@ +# Loadable Library & Userspace Proxy (`module_adapter/library`) + +The `library` directory within the module adapter manages the lifecycle and execution isolation for dynamically loaded algorithms, often referred to as "LLEXT modules" or "Userspace Modules". + +It acts as a secure intermediary layer between the native SOF/Zephyr kernel execution mode (supervisor mode) and the 3rd-party module running in an isolated user-mode context. By relying on Zephyr's Userspace mechanisms (`k_mem_domain`, `K_USER` threads), a faulting or misbehaving loadable extension cannot crash the entire firmware. + +## Architecture & Userspace Sandbox + +The Userspace Proxy architecture involves: + +- **`struct userspace_context`**: This encapsulates the memory domain (`k_mem_domain`) mapped exclusively for this module (code, rodata, BSS, and private heap memory). +- **`user_work_item`**: A Zephyr `k_work_user` mechanism. IPC configuration commands and data processing calls are packaged into a work item and executed safely inside the memory boundaries via `userspace_proxy_worker_handler`. + +```mermaid +graph TD + subgraph SOF Supervisor Domain + P["Pipeline DP Scheduler"] + MADP["Module Adapter Context"] + PROXY["Userspace Proxy (userspace_proxy_invoke)"] + end + + subgraph Zephyr Userspace Domain + SYSAGENT["LLEXT System Agent"] + HANDLER["Worker Handler (userspace_proxy_handle_request)"] + MOD["External Loadable Module (struct module_interface)"] + end + + P -->|Triggers process| MADP + MADP -->|Invokes Proxy| PROXY + + PROXY -->|Packages params & k_work| HANDLER + + HANDLER -->|Safe Call Context| MOD + SYSAGENT -.->|Bootstraps| MOD + + style SOF Supervisor Domain fill:#1d2951,stroke:#333 + style Zephyr Userspace Domain fill:#2e0b1a,stroke:#333 +``` + +## State Transitions & IPC Handling + +IPC messages arriving from the host (e.g. `SET_CONF`, `GET_CONF`, `MODULE_INIT`, `MODULE_BIND`) first hit the Module Adapter running in Supervisor Mode. The adapter checks its configuration and invokes the Userspace Proxy. The proxy performs the following context switch: + +```mermaid +sequenceDiagram + participant IPC as SOF IPC Task + participant MA as Module Adapter + participant Proxy as Userspace Proxy + participant Worker as Zephyr k_work_user Thread + participant UserMod as LLEXT Module + + IPC->>MA: IPC SET_CONF config_id + MA->>Proxy: userspace_proxy_set_configuration + + note over Proxy: 1. Package proxy params + note over Proxy: 2. Add Mailbox memory to `k_mem_domain` + note over Proxy: 3. Post `k_work_user` / event + + Proxy->>Worker: Context Switch -> User Mode + + Worker->>UserMod: module_interface set_configuration + + note over UserMod: Parse config payload safely + + UserMod-->>Worker: Return status + Worker-->>Proxy: Signal Task Done + + note over Proxy: Remove Mailbox memory from domain + + Proxy-->>MA: Return execution + MA-->>IPC: Send Reply/Status to Host +``` + +### Memory Domain Adjustments + +A crucial aspect of the userspace proxy is dynamic memory permission elevation: + +- Normally, the userspace module can only access its own `heap`, `data`, and `bss` partitions. +- When an IPC like `GET_CONF` requests large IPC buffers, the proxy temporarily adds the hardware `MAILBOX_HOSTBOX` into the module's `k_mem_domain` using `k_mem_domain_add_partition()`. +- Once the userspace thread returns, that hardware window is immediately removed from the memory domain to minimize the vulnerability window. diff --git a/src/audio/module_adapter/library/native_system_agent.c b/src/audio/module_adapter/library/native_system_agent.c index 9a93af6f3565..b93405b8bd27 100644 --- a/src/audio/module_adapter/library/native_system_agent.c +++ b/src/audio/module_adapter/library/native_system_agent.c @@ -19,21 +19,20 @@ typedef void* (*native_create_instance_f)(void *mod_cfg, void *parent_ppl, struct native_system_agent native_sys_agent; -int native_system_agent_start(uintptr_t entry_point, uint32_t module_id, uint32_t instance_id, - uint32_t core_id, uint32_t log_handle, void *mod_cfg, +int native_system_agent_start(const struct system_agent_params *params, const void **iface) { - native_sys_agent.module_id = module_id; - native_sys_agent.instance_id = instance_id; - native_sys_agent.core_id = core_id; - native_sys_agent.log_handle = log_handle; + native_sys_agent.module_id = params->module_id; + native_sys_agent.instance_id = params->instance_id; + native_sys_agent.core_id = params->core_id; + native_sys_agent.log_handle = params->log_handle; const void *ret; void *system_agent_p = &native_sys_agent; - native_create_instance_f ci = (native_create_instance_f)entry_point; + native_create_instance_f ci = (native_create_instance_f)params->entry_point; - ret = ci(mod_cfg, NULL, &system_agent_p); + ret = ci(params->mod_cfg, NULL, &system_agent_p); if (!ret) return -EINVAL; diff --git a/src/audio/module_adapter/library/native_system_service.c b/src/audio/module_adapter/library/native_system_service.c index 5126fa5ff12b..1c4688e885d0 100644 --- a/src/audio/module_adapter/library/native_system_service.c +++ b/src/audio/module_adapter/library/native_system_service.c @@ -19,6 +19,7 @@ #include <native_system_service.h> #include <sof/lib_manager.h> #include <module/module/logger.h> +#include <rtos/userspace_helper.h> #define RSIZE_MAX 0x7FFFFFFF @@ -162,7 +163,7 @@ AdspErrorCode native_system_service_get_interface(enum interface_id id, return ADSP_NO_ERROR; } -const struct native_system_service native_system_service = { +const APP_TASK_DATA struct native_system_service native_system_service = { .basic = { .log_message = native_system_service_log_message, .safe_memcpy = native_system_service_safe_memcpy, diff --git a/src/audio/module_adapter/library/userspace_proxy.c b/src/audio/module_adapter/library/userspace_proxy.c new file mode 100644 index 000000000000..4154bad3781d --- /dev/null +++ b/src/audio/module_adapter/library/userspace_proxy.c @@ -0,0 +1,907 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// Copyright(c) 2023 Intel Corporation. All rights reserved. +// +// Author: Jaroslaw Stelter <jaroslaw.stelter@intel.com> +// Author: Adrian Warecki <adrian.warecki@intel.com> + +/** + * \file audio/module_adapter/library/userspace_proxy.c + * \brief Userspace proxy. Acts as an intermediary between SOF and a userspace module. + * \brief Responsible for preparing the memory domain required for userspace execution + * \brief and forwarding API calls. The proxy invokes corresponding module methods + * \brief in userspace context. Enables execution of any module implementing module_interface + * \brief as a userspace module. + * \authors Adrian Warecki + */ + +#include <sof/common.h> +#include <rtos/alloc.h> +#include <rtos/cache.h> +#include <sof/lib/memory.h> +#include <rtos/string.h> +#include <errno.h> +#include <stdbool.h> +#include <stddef.h> +#include <stdint.h> + +#include <sof/lib_manager.h> +#include <sof/audio/component.h> +#include <sof/schedule/dp_schedule.h> +#include <rtos/userspace_helper.h> +#include <utilities/array.h> +#include <zephyr/sys/sem.h> +#include <sof/audio/module_adapter/module/generic.h> + +#include <sof/audio/module_adapter/library/userspace_proxy.h> +#include <sof/audio/module_adapter/library/userspace_proxy_user.h> +#include <rimage/sof/user/manifest.h> + + /* Assume that all the code runs in supervisor mode and don't make system calls. */ +#define __ZEPHYR_SUPERVISOR__ + +LOG_MODULE_REGISTER(userspace_proxy, CONFIG_SOF_LOG_LEVEL); + +/* 6f6b6f4b-6f73-7466-20e1e62b9779f003 */ +SOF_DEFINE_REG_UUID(userspace_proxy); + +DECLARE_TR_CTX(userspace_proxy_tr, SOF_UUID(userspace_proxy_uuid), LOG_LEVEL_INFO); + +static const struct module_interface userspace_proxy_interface; + +#if IS_ENABLED(CONFIG_SOF_USERSPACE_MOD_IPC_BY_DP_THREAD) +#include <sof/audio/module_adapter/iadk/system_agent.h> +#include <sof/schedule/dp_schedule.h> + +static inline int user_worker_get(void) +{ + return 0; +} + +static inline void user_worker_put(void) { } + +struct k_work_user *userspace_proxy_register_ipc_handler(struct processing_module *mod, + struct k_event *event) +{ + struct userspace_context * const user_ctx = mod->user_ctx; + + if (user_ctx) { + tr_dbg(&userspace_proxy_tr, "Set DP event %p for module %p", + (void *)event, (void *)mod); + assert(user_ctx->work_item); + + user_ctx->dp_event = event; + user_ctx->work_item->event = event; + + return &user_ctx->work_item->work_item; + } + + return NULL; +} +#else +/* IPC requests targeting userspace modules are handled through a user work queue. + * Each userspace module provides its own work item that carries the IPC request parameters. + * The worker thread is switched into the module's memory domain and receives the work item. + * It invokes the appropriate module function in userspace context and writes the operation + * result back into the work item. + * + * There is only a single work queue, which is shared by all userspace modules. It is created + * dynamically when needed. Because SOF uses a single dedicated thread for handling IPC, there + * is no need to perform any additional serialization when accessing the worker. + */ +struct user_worker { + k_tid_t thread_id; /* ipc worker thread ID */ + uint32_t reference_count; /* module reference count */ + void *stack_ptr; /* pointer to worker stack */ + struct k_work_user_q work_queue; + struct k_event event; +}; + +static struct user_worker worker; + +static int user_worker_get(void) +{ + if (worker.reference_count) { + worker.reference_count++; + return 0; + } + + worker.stack_ptr = user_stack_allocate(CONFIG_SOF_USERSPACE_PROXY_WORKER_STACK_SIZE, + K_USER); + if (!worker.stack_ptr) { + tr_err(&userspace_proxy_tr, "Userspace worker stack allocation failed."); + return -ENOMEM; + } + + k_event_init(&worker.event); + k_work_user_queue_start(&worker.work_queue, worker.stack_ptr, + CONFIG_SOF_USERSPACE_PROXY_WORKER_STACK_SIZE, 0, NULL); + + worker.thread_id = k_work_user_queue_thread_get(&worker.work_queue); + + k_thread_access_grant(worker.thread_id, &worker.event); + + worker.reference_count++; + return 0; +} + +static void user_worker_put(void) +{ + /* Module removed so decrement counter */ + worker.reference_count--; + + /* Free worker resources if no more active user space modules */ + if (worker.reference_count == 0) { + k_thread_abort(worker.thread_id); + user_stack_free(worker.stack_ptr); + } +} +#endif + +static int user_work_item_init(struct userspace_context *user_ctx, struct k_heap *user_heap) +{ + struct user_work_item *work_item = NULL; + int ret; + + ret = user_worker_get(); + if (ret) + return ret; + + /* We have only a single userspace IPC worker. It handles requests for all userspace + * modules, which may run on different cores. Because the worker processes work items + * coming from any core, the work item must be allocated in coherent memory. + */ + work_item = sof_heap_alloc(user_heap, SOF_MEM_FLAG_COHERENT, sizeof(*work_item), 0); + if (!work_item) { + user_worker_put(); + return -ENOMEM; + } + + k_work_user_init(&work_item->work_item, userspace_proxy_worker_handler); + +#if !IS_ENABLED(CONFIG_SOF_USERSPACE_MOD_IPC_BY_DP_THREAD) + work_item->event = &worker.event; +#endif + work_item->params.context = user_ctx; + user_ctx->work_item = work_item; + + return 0; +} + +static void user_work_item_free(struct userspace_context *user_ctx, struct k_heap *user_heap) +{ + sof_heap_free(user_heap, user_ctx->work_item); + user_worker_put(); +} + +static inline struct module_params *user_work_get_params(struct userspace_context *user_ctx) +{ + return &user_ctx->work_item->params; +} + +BUILD_ASSERT(IS_ALIGNED(MAILBOX_HOSTBOX_BASE, CONFIG_MMU_PAGE_SIZE), + "MAILBOX_HOSTBOX_BASE is not page aligned"); + +BUILD_ASSERT(IS_ALIGNED(MAILBOX_HOSTBOX_SIZE, CONFIG_MMU_PAGE_SIZE), + "MAILBOX_HOSTBOX_SIZE is not page aligned"); + +static int userspace_proxy_invoke(struct userspace_context *user_ctx, uint32_t cmd, + bool ipc_payload_access) +{ +#if IS_ENABLED(CONFIG_SOF_USERSPACE_MOD_IPC_BY_DP_THREAD) + struct k_event * const event = user_ctx->dp_event; +#else + struct k_event * const event = &worker.event; +#endif + struct module_params *params = user_work_get_params(user_ctx); + const uintptr_t ipc_req_buf = (uintptr_t)MAILBOX_HOSTBOX_BASE; + struct k_mem_partition ipc_part = { + .start = ipc_req_buf, + .size = MAILBOX_HOSTBOX_SIZE, + .attr = user_get_partition_attr(ipc_req_buf) | K_MEM_PARTITION_P_RO_U_RO, + }; + int ret = 0, ret2; + + params->cmd = cmd; + + if (ipc_payload_access) { + ret = k_mem_domain_add_partition(user_ctx->comp_dom, &ipc_part); + if (ret < 0) { + tr_err(&userspace_proxy_tr, "Add mailbox to domain error: %d", ret); + return ret; + } + } + +#if !IS_ENABLED(CONFIG_SOF_USERSPACE_MOD_IPC_BY_DP_THREAD) + /* Switch worker thread to module memory domain */ + ret = k_mem_domain_add_thread(user_ctx->comp_dom, worker.thread_id); + if (ret < 0) { + tr_err(&userspace_proxy_tr, "Failed to switch memory domain, error: %d", ret); + goto done; + } + +#ifdef CONFIG_SCHED_CPU_MASK + /* Pin worker thread to the same core as the module */ + ret = k_thread_cpu_pin(worker.thread_id, cpu_get_id()); + if (ret < 0) { + tr_err(&userspace_proxy_tr, "Failed to pin cpu, error: %d", ret); + goto done; + } +#endif + + ret = k_work_user_submit_to_queue(&worker.work_queue, &user_ctx->work_item->work_item); + if (ret < 0) { + tr_err(&userspace_proxy_tr, "Submit to queue error: %d", ret); + goto done; + } +#else + assert(event); + k_event_post(event, DP_TASK_EVENT_IPC); +#endif + + /* Timeout value is aligned with the ipc_wait_for_compound_msg function */ + if (!k_event_wait_safe(event, DP_TASK_EVENT_IPC_DONE, false, + Z_TIMEOUT_US(250 * 20))) { + tr_err(&userspace_proxy_tr, "IPC processing timedout."); + ret = -ETIMEDOUT; + } + +done: + if (ipc_payload_access) { + ret2 = k_mem_domain_remove_partition(user_ctx->comp_dom, &ipc_part); + if (ret2 < 0) { + tr_err(&userspace_proxy_tr, "Mailbox remove from domain error: %d", ret); + + if (!ret) + ret = ret2; + } + } + + return ret; +} + +extern struct k_mem_partition common_partition; + +static int userspace_proxy_memory_init(struct userspace_context *user_ctx, + const struct comp_driver *drv) +{ + /* Add module private heap to memory partitions */ + struct k_mem_partition heap_part = { .attr = K_MEM_PARTITION_P_RW_U_RW }; + struct sys_heap *heap = &drv->user_heap->heap; + + k_mem_region_align(&heap_part.start, &heap_part.size, + POINTER_TO_UINT(heap->init_mem), + heap->init_bytes, CONFIG_MM_DRV_PAGE_SIZE); + + tr_dbg(&userspace_proxy_tr, "Heap partition %#lx + %zx, attr = %u", + heap_part.start, heap_part.size, heap_part.attr); + +#if !defined(CONFIG_XTENSA_MMU_DOUBLE_MAP) && defined(CONFIG_SOF_ZEPHYR_HEAP_CACHED) +#define HEAP_PART_CACHED + /* Add cached module private heap to memory partitions */ + struct k_mem_partition heap_cached_part = { + .attr = K_MEM_PARTITION_P_RW_U_RW | XTENSA_MMU_CACHED_WB + }; + + k_mem_region_align(&heap_cached_part.start, &heap_cached_part.size, + POINTER_TO_UINT(sys_cache_cached_ptr_get(heap->init_mem)), + heap->init_bytes, CONFIG_MM_DRV_PAGE_SIZE); + + tr_dbg(&userspace_proxy_tr, "Cached heap partition %#lx + %zx, attr = %u", + heap_cached_part.start, heap_cached_part.size, heap_cached_part.attr); +#endif + + struct k_mem_partition *parts_ptr[] = { + /* The common partition contains sof components accessible to the userspace module. + * These include ops structures marked with APP_TASK_DATA. + */ + &common_partition, +#ifdef HEAP_PART_CACHED + &heap_cached_part, +#endif + &heap_part + }; + + tr_dbg(&userspace_proxy_tr, "Common partition %#lx + %zx, attr = %u", + common_partition.start, common_partition.size, common_partition.attr); + + return k_mem_domain_init(user_ctx->comp_dom, ARRAY_SIZE(parts_ptr), parts_ptr); +} + +static int userspace_proxy_add_sections(struct userspace_context *user_ctx, uint32_t instance_id, + const struct sof_man_module *const mod) +{ + struct k_mem_partition mem_partition; + void *va_base; + int idx, ret; + + for (idx = 0; idx < ARRAY_SIZE(mod->segment); ++idx) { + if (!mod->segment[idx].flags.r.load) + continue; + + if (mod->segment[idx].flags.r.code) + mem_partition.attr = K_MEM_PARTITION_P_RX_U_RX; + else if (!mod->segment[idx].flags.r.readonly) + mem_partition.attr = K_MEM_PARTITION_P_RW_U_RW; + else + mem_partition.attr = K_MEM_PARTITION_P_RO_U_RO; + + mem_partition.start = mod->segment[idx].v_base_addr; + mem_partition.size = mod->segment[idx].flags.r.length * CONFIG_MM_DRV_PAGE_SIZE; + mem_partition.attr |= user_get_partition_attr(mem_partition.start); + + ret = k_mem_domain_add_partition(user_ctx->comp_dom, &mem_partition); + + tr_dbg(&userspace_proxy_tr, "Add mod partition %#lx + %zx, attr = %u, ret = %d", + mem_partition.start, mem_partition.size, mem_partition.attr, ret); + + if (ret < 0) + return ret; + } + + lib_manager_get_instance_bss_address(instance_id, mod, &va_base, &mem_partition.size); + mem_partition.start = POINTER_TO_UINT(va_base); + mem_partition.attr = user_get_partition_attr(mem_partition.start) | + K_MEM_PARTITION_P_RW_U_RW; + ret = k_mem_domain_add_partition(user_ctx->comp_dom, &mem_partition); + + tr_dbg(&userspace_proxy_tr, "Add bss partition %#lx + %zx, attr = %u, ret = %d", + mem_partition.start, mem_partition.size, mem_partition.attr, ret); + + return ret; +} + +static int userspace_proxy_start_agent(struct userspace_context *user_ctx, + system_agent_start_fn start_fn, + const struct system_agent_params *agent_params, + const void **agent_interface) +{ + const byte_array_t * const mod_cfg = (byte_array_t *)agent_params->mod_cfg; + struct module_params *params = user_work_get_params(user_ctx); + + params->ext.agent.start_fn = start_fn; + + /* Start the system agent, if provided. */ + if (start_fn) { + params->ext.agent.params = *agent_params; + params->ext.agent.params.mod_cfg = ¶ms->ext.agent.mod_cfg; + params->ext.agent.mod_cfg = *mod_cfg; + + /* In case of processing modules ipc in the DP thread, the agent will be started in + * the init function. At this point the DP thread does not exist yet. + */ +#if !IS_ENABLED(CONFIG_SOF_USERSPACE_MOD_IPC_BY_DP_THREAD) + int ret = userspace_proxy_invoke(user_ctx, USER_PROXY_MOD_CMD_AGENT_START, true); + + if (ret) + return ret; + + *agent_interface = params->ext.agent.out_interface; + return params->status; +#endif + } + return 0; +} + +int userspace_proxy_create(struct userspace_context **user_ctx, const struct comp_driver *drv, + const struct sof_man_module *manifest, system_agent_start_fn start_fn, + const struct system_agent_params *agent_params, + const void **agent_interface, const struct module_interface **ops) +{ + struct userspace_context *context; + struct k_mem_domain *domain; + int ret; + + tr_dbg(&userspace_proxy_tr, "userspace create"); + + context = k_heap_alloc(drv->user_heap, sizeof(struct userspace_context), K_FOREVER); + if (!context) + return -ENOMEM; + + context->dp_event = NULL; + + /* Allocate memory domain struct */ + domain = rzalloc(SOF_MEM_FLAG_KERNEL, sizeof(*domain)); + if (!domain) { + ret = -ENOMEM; + goto error; + } + context->comp_dom = domain; + + ret = userspace_proxy_memory_init(context, drv); + if (ret) + goto error_dom; + + ret = userspace_proxy_add_sections(context, agent_params->instance_id, manifest); + if (ret) + goto error_dom; + + ret = user_work_item_init(context, drv->user_heap); + if (ret) + goto error_dom; + + ret = userspace_proxy_start_agent(context, start_fn, agent_params, agent_interface); + if (ret) { + tr_err(&userspace_proxy_tr, "System agent failed with error %d.", ret); + goto error_work_item; + } + + *user_ctx = context; + + /* Store a pointer to the module's interface. For the LMDK modules, the agent places a + * pointer to the module interface at the address specified by agent_interface. Since this + * points to ops, the assignment of the module interface used by this proxy must occur + * after the agent has been started. For other module types, the ops parameter points to a + * valid module interface. + */ + context->interface = *ops; + + /* All calls to the module interface must pass through the proxy. Set up our own interface. + */ + *ops = &userspace_proxy_interface; + + return 0; + +error_work_item: + user_work_item_free(context, drv->user_heap); +error_dom: + rfree(domain); +error: + k_heap_free(drv->user_heap, context); + return ret; +} + +void userspace_proxy_destroy(const struct comp_driver *drv, struct userspace_context *user_ctx) +{ + tr_dbg(&userspace_proxy_tr, "userspace proxy destroy"); + user_work_item_free(user_ctx, drv->user_heap); + rfree(user_ctx->comp_dom); + k_heap_free(drv->user_heap, user_ctx); +} + +/** + * Copy parameters to user worker accessible space. + * Queue module init() operation and return its result. + * Module init() code is performed in user workqueue. + * + * @param mod - pointer to processing module structure. + * @return 0 for success, error otherwise. + */ +static int userspace_proxy_init(struct processing_module *mod) +{ + struct module_params *params = user_work_get_params(mod->user_ctx); + int ret; + + comp_dbg(mod->dev, "start"); + +#if IS_ENABLED(CONFIG_SOF_USERSPACE_MOD_IPC_BY_DP_THREAD) + /* Start the system agent, if provided. Params is already filled by + * the userspace_proxy_start_agent function. + */ + if (params->ext.agent.start_fn) { + ret = userspace_proxy_invoke(mod->user_ctx, USER_PROXY_MOD_CMD_AGENT_START, true); + if (ret) + return ret; + + if (params->ext.agent.start_fn == system_agent_start) + module_set_private_data(mod, (void *)params->ext.agent.out_interface); + else + mod->user_ctx->interface = params->ext.agent.out_interface; + } +#endif + + params->mod = mod; + ret = userspace_proxy_invoke(mod->user_ctx, USER_PROXY_MOD_CMD_INIT, true); + if (ret) + return ret; + + /* Return status from module code operation. */ + return params->status; +} + +/** + * Copy parameters to user worker accessible space. + * Queue module prepare() operation and return its result. + * Module prepare() code is performed in user workqueue. + * + * @return 0 for success, error otherwise. + */ +static int userspace_proxy_prepare(struct processing_module *mod, + struct sof_source **sources, int num_of_sources, + struct sof_sink **sinks, int num_of_sinks) +{ + struct module_params *params = user_work_get_params(mod->user_ctx); + int ret; + + comp_dbg(mod->dev, "start"); + + if (!mod->user_ctx->interface->prepare) + return 0; + + params->ext.proc.sources = sources; + params->ext.proc.num_of_sources = num_of_sources; + params->ext.proc.sinks = sinks; + params->ext.proc.num_of_sinks = num_of_sinks; + + ret = userspace_proxy_invoke(mod->user_ctx, USER_PROXY_MOD_CMD_PREPARE, false); + if (ret) + return ret; + + /* Return status from module code operation. */ + return params->status; +} + +/** + * Forward processing request to the module's process() implementation. + * + * It is invoked by the DP thread running in userspace, so no + * additional queuing or context switching is performed here. + * + * @param mod Pointer to the processing module instance. + * @param sources Array of input sources for the module. + * @param num_of_sources Number of input sources. + * @param sinks Array of output sinks for the module. + * @param num_of_sinks Number of output sinks. + * + * @return 0 on success, negative error code on failure. + */ +static int userspace_proxy_process(struct processing_module *mod, struct sof_source **sources, + int num_of_sources, struct sof_sink **sinks, int num_of_sinks) +{ + return mod->user_ctx->interface->process(mod, sources, num_of_sources, sinks, num_of_sinks); +} + +/** + * Copy parameters to user worker accessible space. + * Queue module reset() operation and return its result. + * Module reset() code is performed in user workqueue. + * + * @param mod - pointer to processing module structure. + * @return 0 for success, error otherwise. + */ +static int userspace_proxy_reset(struct processing_module *mod) +{ + struct module_params *params = user_work_get_params(mod->user_ctx); + int ret; + + if (!mod->user_ctx->interface->reset) + return 0; + + ret = userspace_proxy_invoke(mod->user_ctx, USER_PROXY_MOD_CMD_RESET, false); + if (ret) + return ret; + + /* Return status from module code operation. */ + return params->status; +} + +/** + * Copy parameters to user worker accessible space. + * Queue module free() operation and return its result. + * Module free() code is performed in user workqueue. + * + * @param mod - pointer to processing module structure. + * @return 0 for success, error otherwise. + */ +static int userspace_proxy_free(struct processing_module *mod) +{ + struct module_params *params = user_work_get_params(mod->user_ctx); + int ret = 0; + + comp_dbg(mod->dev, "start"); + + if (mod->user_ctx->interface->free) { + ret = userspace_proxy_invoke(mod->user_ctx, USER_PROXY_MOD_CMD_FREE, false); + if (ret) + return ret; + ret = params->status; + } + + /* Destroy workqueue if this was last active userspace module */ + userspace_proxy_destroy(mod->dev->drv, mod->user_ctx); + mod->user_ctx = NULL; + + /* Return status from module code operation. */ + return ret; +} + +/** + * Copy parameters to user worker accessible space. + * Queue module set_configuration() operation and return its result. + * Module set_configuration() code is performed in user workqueue. + * + * @param[in] mod - struct processing_module pointer + * @param[in] config_id - Configuration ID + * @param[in] pos - position of the fragment in the large message + * @param[in] data_offset_size: size of the whole configuration if it is the first fragment or the + * only fragment. Otherwise, it is the offset of the fragment in the whole + * configuration. + * @param[in] fragment: configuration fragment buffer + * @param[in] fragment_size: size of @fragment + * @params[in] response: optional response buffer to fill + * @params[in] response_size: size of @response + * + * @return 0 for success, error otherwise. + */ +static int userspace_proxy_set_configuration(struct processing_module *mod, uint32_t config_id, + enum module_cfg_fragment_position pos, + uint32_t data_offset_size, const uint8_t *fragment, + size_t fragment_size, uint8_t *response, + size_t response_size) +{ + struct module_params *params = user_work_get_params(mod->user_ctx); + int ret; + + comp_dbg(mod->dev, "start"); + + if (!mod->user_ctx->interface->set_configuration) + return 0; + + params->ext.set_conf.config_id = config_id; + params->ext.set_conf.pos = pos; + params->ext.set_conf.data_off_size = data_offset_size; + params->ext.set_conf.fragment = fragment; + params->ext.set_conf.fragment_size = fragment_size; + params->ext.set_conf.response = response; + params->ext.set_conf.response_size = response_size; + + ret = userspace_proxy_invoke(mod->user_ctx, USER_PROXY_MOD_CMD_SET_CONF, true); + if (ret) + return ret; + + /* Return status from module code operation. */ + return params->status; +} + +/** + * Copy parameters to user worker accessible space. + * Queue module get_configuration() operation and return its result. + * Module get_configuration() code is performed in user workqueue. + * + * @param[in] mod - struct processing_module pointer + * @param[in] config_id - Configuration ID + * @param[in] data_offset_size: size of the whole configuration if it is the first fragment or the + * only fragment. Otherwise, it is the offset of the fragment in the whole + * configuration. + * @param[in] fragment: configuration fragment buffer + * @param[in] fragment_size: size of @fragment + * + * @return 0 for success, error otherwise. + */ +static int userspace_proxy_get_configuration(struct processing_module *mod, uint32_t config_id, + uint32_t *data_offset_size, uint8_t *fragment, + size_t fragment_size) +{ + struct module_params *params = user_work_get_params(mod->user_ctx); + struct k_mem_domain *domain = mod->user_ctx->comp_dom; + const uintptr_t ipc_resp_buf = POINTER_TO_UINT(ipc_get()->comp_data); + + /* Memory partition exposing the IPC response buffer. This buffer is allocated + * by the IPC driver and contains the payload of IPC replies sent to the host. + */ + struct k_mem_partition ipc_resp_part = { + .start = ipc_resp_buf, + .size = SOF_IPC_MSG_MAX_SIZE, + .attr = user_get_partition_attr(ipc_resp_buf) | K_MEM_PARTITION_P_RW_U_RW, + }; + int ret; + + comp_dbg(mod->dev, "start"); + + if (!mod->user_ctx->interface->get_configuration) + return -EIO; + + params->ext.get_conf.config_id = config_id; + params->ext.get_conf.data_off_size = data_offset_size; + params->ext.get_conf.fragment = fragment; + params->ext.get_conf.fragment_size = fragment_size; + + ret = k_mem_domain_add_partition(domain, &ipc_resp_part); + if (ret < 0) { + comp_err(mod->dev, "add response buffer to domain error: %d", ret); + return ret; + } + + ret = userspace_proxy_invoke(mod->user_ctx, USER_PROXY_MOD_CMD_GET_CONF, true); + + k_mem_domain_remove_partition(domain, &ipc_resp_part); + + /* Return status from module code operation. */ + return ret ? ret : params->status; +} + +/** + * Copy parameters to user worker accessible space. + * Queue module set_processing_mode() operation and return its result. + * Module set_processing_mode() code is performed in user workqueue. + * + * @param mod - pointer to processing module structure. + * @param mode - processing mode to be set. + * @return 0 for success, error otherwise. + */ +static int userspace_proxy_set_processing_mode(struct processing_module *mod, + enum module_processing_mode mode) +{ + struct module_params *params = user_work_get_params(mod->user_ctx); + int ret; + + comp_dbg(mod->dev, "start"); + + if (!mod->user_ctx->interface->set_processing_mode) + return 0; + + params->ext.proc_mode.mode = mode; + ret = userspace_proxy_invoke(mod->user_ctx, USER_PROXY_MOD_CMD_SET_PROCMOD, false); + if (ret) + return ret; + + /* Return status from module code operation. */ + return params->status; +} + +/** + * Copy parameters to user worker accessible space. + * Queue module get_processing_mode() operation and return its result. + * Module get_processing_mode() code is performed in user workqueue. + * + * @param mod - pointer to processing module structure. + * @return processing mode. + */ +static +enum module_processing_mode userspace_proxy_get_processing_mode(struct processing_module *mod) +{ + struct module_params *params = user_work_get_params(mod->user_ctx); + int ret; + + comp_dbg(mod->dev, "start"); + + if (!mod->user_ctx->interface->get_processing_mode) + return -EIO; + + ret = userspace_proxy_invoke(mod->user_ctx, USER_PROXY_MOD_CMD_GET_PROCMOD, false); + if (ret) + return ret; + + /* Return status from module code operation. */ + return params->ext.proc_mode.mode; +} + +/** + * Copy parameters to user worker accessible space. + * Queue module is_ready_to_process() operation and return its result. + * Module is_ready_to_process() code is performed in user workqueue. + * + * @param mod - pointer to processing module structure. + * @return true if the module is ready to process + */ +static bool userspace_proxy_is_ready_to_process(struct processing_module *mod, + struct sof_source **sources, + int num_of_sources, + struct sof_sink **sinks, + int num_of_sinks) +{ + struct module_params *params = user_work_get_params(mod->user_ctx); + int ret; + + comp_dbg(mod->dev, "start"); + + if (!mod->user_ctx->interface->is_ready_to_process) + return generic_module_is_ready_to_process(mod, sources, num_of_sources, sinks, + num_of_sinks); + + params->ext.proc.sources = sources; + params->ext.proc.num_of_sources = num_of_sources; + params->ext.proc.sinks = sinks; + params->ext.proc.num_of_sinks = num_of_sinks; + + ret = userspace_proxy_invoke(mod->user_ctx, USER_PROXY_MOD_CMD_PROC_READY, false); + if (ret) + return generic_module_is_ready_to_process(mod, sources, num_of_sources, sinks, + num_of_sinks); + + /* Return status from module code operation. */ + return params->status; +} + +/** + * Copy parameters to user worker accessible space. + * Queue module bind() operation and return its result. + * Module bind() code is performed in user workqueue. + * + * @param mod - pointer to processing module structure. + * @param bind_data - pointer to bind_info structure. + * @return 0 for success, error otherwise. + */ +static int userspace_proxy_bind(struct processing_module *mod, struct bind_info *bind_data) +{ + struct module_params *params = user_work_get_params(mod->user_ctx); + int ret; + + comp_dbg(mod->dev, "start"); + + if (!mod->user_ctx->interface->bind) + return 0; + + params->ext.bind_data = bind_data; + ret = userspace_proxy_invoke(mod->user_ctx, USER_PROXY_MOD_CMD_BIND, false); + if (ret) + return ret; + + /* Return status from module code operation. */ + return params->status; +} + +/** + * Copy parameters to user worker accessible space. + * Queue module unbind() operation and return its result. + * Module unbind() code is performed in user workqueue. + * + * @param mod - pointer to processing module structure. + * @param unbind_data - pointer to bind_info structure. + * @return 0 for success, error otherwise. + */ +static int userspace_proxy_unbind(struct processing_module *mod, struct bind_info *unbind_data) +{ + struct module_params *params = user_work_get_params(mod->user_ctx); + int ret; + + comp_dbg(mod->dev, "start"); + + if (!mod->user_ctx->interface->unbind) + return 0; + + params->ext.bind_data = unbind_data; + ret = userspace_proxy_invoke(mod->user_ctx, USER_PROXY_MOD_CMD_UNBIND, false); + if (ret) + return ret; + + /* Return status from module code operation. */ + return params->status; +} + +/** + * Copy parameters to user worker accessible space. + * Queue module trigger() operation and return its result. + * Module trigger() code is performed in user workqueue. + * + * @param mod - pointer to processing module structure. + * @return 0 for success, error otherwise. + */ +static int userspace_proxy_trigger(struct processing_module *mod, int cmd) +{ + struct module_params *params = user_work_get_params(mod->user_ctx); + int ret = 0; + + comp_dbg(mod->dev, "start"); + + if (mod->user_ctx->interface->trigger) { + params->ext.trigger_data = cmd; + ret = userspace_proxy_invoke(mod->user_ctx, USER_PROXY_MOD_CMD_TRIGGER, false); + if (ret) + return ret; + ret = params->status; + } + + if (!ret) + ret = module_adapter_set_state(mod, mod->dev, cmd); + + /* Return status from module code operation. */ + return ret; +} + +/* Userspace Proxy Module API */ +APP_TASK_DATA static const struct module_interface userspace_proxy_interface = { + .init = userspace_proxy_init, + .is_ready_to_process = userspace_proxy_is_ready_to_process, + .prepare = userspace_proxy_prepare, + .process = userspace_proxy_process, + .set_configuration = userspace_proxy_set_configuration, + .get_configuration = userspace_proxy_get_configuration, + .set_processing_mode = userspace_proxy_set_processing_mode, + .get_processing_mode = userspace_proxy_get_processing_mode, + .reset = userspace_proxy_reset, + .free = userspace_proxy_free, + .bind = userspace_proxy_bind, + .unbind = userspace_proxy_unbind, + .trigger = userspace_proxy_trigger, +}; diff --git a/src/audio/module_adapter/library/userspace_proxy_user.c b/src/audio/module_adapter/library/userspace_proxy_user.c new file mode 100644 index 000000000000..105c9841318f --- /dev/null +++ b/src/audio/module_adapter/library/userspace_proxy_user.c @@ -0,0 +1,119 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// Copyright(c) 2025 Intel Corporation. All rights reserved. +// +// Author: Adrian Warecki <adrian.warecki@intel.com> + +/** + * \file audio/module_adapter/library/userspace_proxy_user.c + * \brief Userspace proxy functions executed only in userspace context. + * \authors Adrian Warecki + */ + +/* Assume that all the code runs in user mode and unconditionally makes system calls. */ +#define __ZEPHYR_USER__ + +#include <sof/common.h> +#include <errno.h> +#include <stddef.h> +#include <stdint.h> + +#include <sof/audio/component.h> +#include <sof/schedule/dp_schedule.h> +#include <sof/audio/module_adapter/module/generic.h> + +#include <sof/audio/module_adapter/library/userspace_proxy.h> +#include <sof/audio/module_adapter/library/userspace_proxy_user.h> + +void userspace_proxy_handle_request(struct processing_module *mod, struct module_params *params) +{ + const struct module_interface *ops = params->context->interface; + + switch (params->cmd) { + case USER_PROXY_MOD_CMD_AGENT_START: + params->status = params->ext.agent.start_fn(¶ms->ext.agent.params, + ¶ms->ext.agent.out_interface); + break; + + case USER_PROXY_MOD_CMD_INIT: + params->status = ops->init(params->mod); + break; + + case USER_PROXY_MOD_CMD_PREPARE: + params->status = ops->prepare(params->mod, params->ext.proc.sources, + params->ext.proc.num_of_sources, + params->ext.proc.sinks, + params->ext.proc.num_of_sinks); + break; + + case USER_PROXY_MOD_CMD_PROC_READY: + params->status = ops->is_ready_to_process(params->mod, + params->ext.proc.sources, + params->ext.proc.num_of_sources, + params->ext.proc.sinks, + params->ext.proc.num_of_sinks); + break; + + case USER_PROXY_MOD_CMD_BIND: + params->status = ops->bind(params->mod, params->ext.bind_data); + break; + + case USER_PROXY_MOD_CMD_UNBIND: + params->status = ops->unbind(params->mod, params->ext.bind_data); + break; + + case USER_PROXY_MOD_CMD_RESET: + params->status = ops->reset(params->mod); + break; + + case USER_PROXY_MOD_CMD_FREE: + params->status = ops->free(params->mod); + break; + + case USER_PROXY_MOD_CMD_SET_CONF: + params->status = ops->set_configuration(params->mod, + params->ext.set_conf.config_id, + params->ext.set_conf.pos, + params->ext.set_conf.data_off_size, + params->ext.set_conf.fragment, + params->ext.set_conf.fragment_size, + params->ext.set_conf.response, + params->ext.set_conf.response_size); + break; + + case USER_PROXY_MOD_CMD_GET_CONF: + params->status = ops->get_configuration(params->mod, + params->ext.get_conf.config_id, + params->ext.get_conf.data_off_size, + params->ext.get_conf.fragment, + params->ext.get_conf.fragment_size); + break; + + case USER_PROXY_MOD_CMD_SET_PROCMOD: + params->status = ops->set_processing_mode(params->mod, + params->ext.proc_mode.mode); + break; + + case USER_PROXY_MOD_CMD_GET_PROCMOD: + params->ext.proc_mode.mode = ops->get_processing_mode(params->mod); + break; + + case USER_PROXY_MOD_CMD_TRIGGER: + params->status = ops->trigger(params->mod, params->ext.trigger_data); + break; + + default: + params->status = -EINVAL; + break; + } +} + +void userspace_proxy_worker_handler(struct k_work_user *work_item) +{ + struct user_work_item *user_work_item = CONTAINER_OF(work_item, struct user_work_item, + work_item); + struct module_params *params = &user_work_item->params; + + userspace_proxy_handle_request(params->mod, params); + k_event_post(user_work_item->event, DP_TASK_EVENT_IPC_DONE); +} diff --git a/src/audio/module_adapter/module/README.md b/src/audio/module_adapter/module/README.md new file mode 100644 index 000000000000..4586f060f37d --- /dev/null +++ b/src/audio/module_adapter/module/README.md @@ -0,0 +1,88 @@ +# Module Implementing Interfaces (`module_adapter/module`) + +The `module` directory contains the concrete `module_interface` implementations that bridge the core SOF Component API with various audio processing algorithms. These adapters translate the generic initialization, parameter configuration, and process loop calls into the specific operational sequences required by different module frameworks. + +The directory primarily houses three distinct module adapters: + +1. **Generic Core Adapter (`generic.c`)** +2. **Modules (IADK/Shim) Adapter (`modules.c`)** +3. **Cadence DSP Codec Adapter (`cadence.c`, `cadence_ipc4.c`)** + +## 1. Generic Core Adapter (`generic.c`) + +The Generic adapter provides the default runtime container for typical native SOF processing modules (e.g., volume, eq, src). + +It implements the full `struct module_interface` contract: + +* **Memory Management**: It intercepts memory allocation mappings using `mod_balloc_align` and tracks memory requests in a module-specific resource pool (`module_resource`). When the module goes out of scope, the framework garbage-collects any leaked allocations automatically via `mod_free_all()`. +* **Configuration Handling**: Manages large blob configuration messages across multiple IPC fragments (`module_set_configuration`). It allocates memory for `runtime_params` until the blob is fully assembled, then triggers the underlying algorithm with the completed struct. +* **State Machine Enforcement**: It wraps `process_audio_stream` and `process_raw_data` calls to verify the module is in either `MODULE_IDLE` or `MODULE_PROCESSING` states before execution. + +## 2. Modules (IADK Shim) Adapter (`modules.c`) + +The `modules.c` base is an extension adapter designed specifically to run Intel Audio Development Kit (IADK) 3rd party algorithms. + +Unlike the generic modules, the IADK modules are object-oriented C++ architectures linked into a separate library (`module_adapter/iadk`). This file acts as the primary C entry point wrapper. + +* It utilizes the `iadk_wrapper_*` C-bridge functions to invoke methods on the C++ `intel_adsp::ProcessingModuleInterface` classes. +* It exposes the standard `DECLARE_MODULE_ADAPTER(processing_module_adapter_interface)` that is bound to the SOF pipeline. + +## 3. Cadence Codec Adapter (`cadence.c`) + +This is a highly specialized adapter used for integrating Xtensa Audio (XA) codecs from Cadence (e.g., MP3, AAC, Vorbis, SBC, DAB). + +The `cadence.c` implementation maps the standard SOF pipeline controls into the Cadence memory buffer management and synchronous execution models. + +```mermaid +graph TD + subgraph SOF Initialization + INIT["cadence_codec_init"] + CONFIG["cadence_configure_codec_params"] + RESOLVE["cadence_codec_resolve_api"] + end + + subgraph Cadence Library Init XA API + MEMTABS["cadence_codec_init_memory_tables"] + GETSIZE["XA_API_CMD_GET_MEM_INFO_SIZE"] + SETPTR["XA_API_CMD_SET_MEM_PTR"] + end + + subgraph Data Processing Loop + PROC["cadence_codec_process_data"] + FILL["XA_API_CMD_SET_INPUT_BYTES"] + EXEC["XA_API_CMD_EXECUTE"] + GETOUT["XA_API_CMD_GET_OUTPUT_BYTES"] + end + + INIT --> RESOLVE + RESOLVE --> CONFIG + CONFIG --> MEMTABS + + MEMTABS -. Iterates Memory Types .-> GETSIZE + GETSIZE -. Assigns Buffers .-> SETPTR + + SETPTR ==>|Pipeline Trigger| PROC + + PROC --> FILL + FILL --> EXEC + EXEC --> GETOUT +``` + +### Cadence Memory Tables + +Unlike standard modules that directly read from a `sof_source` API, Cadence codecs require their memory isolated exclusively into exact predefined chunks categorized by "type". `cadence_codec_init_memory_tables` iterates through the codec's hardware definition to construct these memory areas: + +* `XA_MEMTYPE_INPUT` +* `XA_MEMTYPE_OUTPUT` +* `XA_MEMTYPE_SCRATCH` +* `XA_MEMTYPE_PERSIST` + +The SOF adapter allocates tracking structures via `mod_alloc_align` for each of these mandatory regions prior to audio playback. + +### Execution Wrapper + +During `cadence_codec_process()`, the adapter: + +1. Performs `source_get_data` and mechanically copies audio bytes into the isolated `XA_MEMTYPE_INPUT` buffer. +2. Invokes the Xtensa Audio codec API (`XA_API_CMD_EXECUTE`). +3. Reads the produced byte count and copies them back out from `XA_MEMTYPE_OUTPUT` into the `sof_sink`. diff --git a/src/audio/module_adapter/module/cadence.c b/src/audio/module_adapter/module/cadence.c index 05717eab6932..78a9c68b8afa 100644 --- a/src/audio/module_adapter/module/cadence.c +++ b/src/audio/module_adapter/module/cadence.c @@ -1,15 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause // -// Copyright(c) 2020 Intel Corporation. All rights reserved. +// Copyright(c) 2020 - 2026 Intel Corporation. All rights reserved. // -// Author: Marcin Rajwa <marcin.rajwa@linux.intel.com> - -/* - * \file cadence.c - * \brief Cadence Codec API - * \author Marcin Rajwa <marcin.rajwa@linux.intel.com> - * - */ #include <sof/audio/module_adapter/module/generic.h> #include <sof/audio/module_adapter/module/cadence.h> @@ -18,29 +10,10 @@ LOG_MODULE_REGISTER(cadence_codec, CONFIG_SOF_LOG_LEVEL); -SOF_DEFINE_REG_UUID(cadence_codec); - -DECLARE_TR_CTX(cadence_codec_tr, SOF_UUID(cadence_codec_uuid), LOG_LEVEL_INFO); - -enum cadence_api_id { - CADENCE_CODEC_WRAPPER_ID = 0x01, - CADENCE_CODEC_AAC_DEC_ID = 0x02, - CADENCE_CODEC_BSAC_DEC_ID = 0x03, - CADENCE_CODEC_DAB_DEC_ID = 0x04, - CADENCE_CODEC_DRM_DEC_ID = 0x05, - CADENCE_CODEC_MP3_DEC_ID = 0x06, - CADENCE_CODEC_SBC_DEC_ID = 0x07, - CADENCE_CODEC_VORBIS_DEC_ID = 0x08, - CADENCE_CODEC_SRC_PP_ID = 0x09, - CADENCE_CODEC_MP3_ENC_ID = 0x0A, -}; - -#define DEFAULT_CODEC_ID CADENCE_CODEC_WRAPPER_ID - /*****************************************************************************/ /* Cadence API functions array */ /*****************************************************************************/ -static struct cadence_api cadence_api_table[] = { +struct cadence_api cadence_api_table[] = { #ifdef CONFIG_CADENCE_CODEC_WRAPPER { .id = CADENCE_CODEC_WRAPPER_ID, @@ -103,52 +76,15 @@ static struct cadence_api cadence_api_table[] = { #endif }; -#if CONFIG_IPC_MAJOR_4 -static int cadence_codec_resolve_api(struct processing_module *mod) -{ - struct comp_dev *dev = mod->dev; - struct cadence_codec_data *cd = module_get_private_data(mod); - uint32_t api_id = CODEC_GET_API_ID(DEFAULT_CODEC_ID); - uint32_t n_apis = ARRAY_SIZE(cadence_api_table); - struct module_data *codec = &mod->priv; - struct module_param *param; - int i; - xa_codec_func_t *api = NULL; - - /* For ipc4 protocol codec parameters has to be retrieved from configuration */ - if (!codec->cfg.data) { - comp_err(dev, "could not find cadence config"); - return -EINVAL; - } - param = codec->cfg.data; - api_id = param->id >> 16; - - /* Find and assign API function */ - for (i = 0; i < n_apis; i++) { - if (cadence_api_table[i].id == api_id) { - api = cadence_api_table[i].api; - break; - } - } - - /* Verify API assignment */ - if (!api) { - comp_err(dev, "could not find API function for id %x", - api_id); - return -EINVAL; - } - cd->api = api; - cd->api_id = api_id; - - return 0; -} -#elif CONFIG_IPC_MAJOR_3 -static int cadence_code_get_api_id(uint32_t compress_id) +static int cadence_codec_get_api_id(uint32_t compress_id, uint32_t direction) { /* convert compress id to SOF cadence SOF id */ switch (compress_id) { case SND_AUDIOCODEC_MP3: - return CADENCE_CODEC_MP3_DEC_ID; + if (direction == SOF_IPC_STREAM_PLAYBACK) + return CADENCE_CODEC_MP3_DEC_ID; + + return CADENCE_CODEC_MP3_ENC_ID; case SND_AUDIOCODEC_AAC: return CADENCE_CODEC_AAC_DEC_ID; case SND_AUDIOCODEC_VORBIS: @@ -158,314 +94,21 @@ static int cadence_code_get_api_id(uint32_t compress_id) } } -static int cadence_codec_resolve_api(struct processing_module *mod) +void cadence_codec_free_memory_tables(struct processing_module *mod) { - int ret; - struct snd_codec codec_params; - struct comp_dev *dev = mod->dev; struct cadence_codec_data *cd = module_get_private_data(mod); - uint32_t api_id = CODEC_GET_API_ID(DEFAULT_CODEC_ID); - uint32_t n_apis = ARRAY_SIZE(cadence_api_table); int i; - xa_codec_func_t *api = NULL; - - if (mod->stream_params->ext_data_length) { - ret = memcpy_s(&codec_params, mod->stream_params->ext_data_length, - (uint8_t *)mod->stream_params + sizeof(*mod->stream_params), - mod->stream_params->ext_data_length); - if (ret < 0) - return ret; - - ret = cadence_code_get_api_id(codec_params.id); - if (ret < 0) - return ret; - - api_id = ret; - } - /* Find and assign API function */ - for (i = 0; i < n_apis; i++) { - if (cadence_api_table[i].id == api_id) { - api = cadence_api_table[i].api; - break; - } - } + if (cd->mem_to_be_freed) + for (i = 0; i < cd->mem_to_be_freed_len; i++) + mod_free(mod, cd->mem_to_be_freed[i]); - /* Verify API assignment */ - if (!api) { - comp_err(dev, "could not find API function for id %x", - api_id); - return -EINVAL; - } - cd->api = api; - cd->api_id = api_id; - - return 0; -} -#else -#error Unknown IPC major version -#endif - -static int cadence_codec_post_init(struct processing_module *mod) -{ - int ret; - struct comp_dev *dev = mod->dev; - struct cadence_codec_data *cd = module_get_private_data(mod); - uint32_t obj_size; - - comp_dbg(dev, "cadence_codec_post_init() start"); - - ret = cadence_codec_resolve_api(mod); - if (ret < 0) - return ret; - - /* Obtain codec name */ - API_CALL(cd, XA_API_CMD_GET_LIB_ID_STRINGS, - XA_CMD_TYPE_LIB_NAME, cd->name, ret); - if (ret != LIB_NO_ERROR) { - comp_err(dev, "cadence_codec_init() error %x: failed to get lib name", - ret); - return ret; - } - /* Get codec object size */ - API_CALL(cd, XA_API_CMD_GET_API_SIZE, 0, &obj_size, ret); - if (ret != LIB_NO_ERROR) { - comp_err(dev, "cadence_codec_init() error %x: failed to get lib object size", - ret); - return ret; - } - /* Allocate space for codec object */ - cd->self = rballoc(SOF_MEM_FLAG_USER, obj_size); - if (!cd->self) { - comp_err(dev, "failed to allocate space for lib object"); - return -ENOMEM; - } - - comp_dbg(dev, "allocated %d bytes for lib object", obj_size); - - /* Set all params to their default values */ - API_CALL(cd, XA_API_CMD_INIT, XA_CMD_TYPE_INIT_API_PRE_CONFIG_PARAMS, - NULL, ret); - if (ret != LIB_NO_ERROR) { - rfree(cd->self); - return ret; - } - - comp_dbg(dev, "cadence_codec_post_init() done"); - - return 0; + mod_free(mod, cd->mem_to_be_freed); + cd->mem_to_be_freed = NULL; + cd->mem_to_be_freed_len = 0; } -#if CONFIG_IPC_MAJOR_4 -static int cadence_codec_init(struct processing_module *mod) -{ - const struct ipc4_cadence_module_cfg *cfg; - struct module_data *codec = &mod->priv; - struct cadence_codec_data *cd; - struct module_config *setup_cfg; - struct comp_dev *dev = mod->dev; - int ret; - - comp_dbg(dev, "cadence_codec_init() start"); - - cd = rzalloc(SOF_MEM_FLAG_USER, sizeof(struct cadence_codec_data)); - if (!cd) { - comp_err(dev, "failed to allocate memory for cadence codec data"); - return -ENOMEM; - } - - codec->private = cd; - codec->mpd.init_done = 0; - - /* copy the setup config only for the first init */ - if (codec->state == MODULE_DISABLED && codec->cfg.avail) { - setup_cfg = &cd->setup_cfg; - - cfg = (const struct ipc4_cadence_module_cfg *)codec->cfg.init_data; - - /* allocate memory for set up config */ - setup_cfg->data = rmalloc(SOF_MEM_FLAG_USER, - cfg->param_size); - if (!setup_cfg->data) { - comp_err(dev, "failed to alloc setup config"); - ret = -ENOMEM; - goto free; - } - - /* allocate memory for runtime set up config */ - codec->cfg.data = rmalloc(SOF_MEM_FLAG_USER, - cfg->param_size); - if (!codec->cfg.data) { - comp_err(dev, "failed to alloc runtime setup config"); - ret = -ENOMEM; - goto free_cfg; - } - - codec->cfg.size = cfg->param_size; - ret = memcpy_s(codec->cfg.data, codec->cfg.size, - cfg->param, cfg->param_size); - if (ret) { - comp_err(dev, "failed to init runtime config %d", - ret); - goto free_cfg2; - } - codec->cfg.avail = true; - - setup_cfg->size = cfg->param_size; - ret = memcpy_s(setup_cfg->data, setup_cfg->size, - cfg->param, cfg->param_size); - if (ret) { - comp_err(dev, "failed to copy setup config %d", ret); - goto free_cfg2; - } - setup_cfg->avail = true; - } - - comp_dbg(dev, "cadence_codec_init() done"); - - return 0; - -free_cfg2: - rfree(codec->cfg.data); -free_cfg: - rfree(setup_cfg->data); -free: - rfree(cd); - return ret; -} - -#elif CONFIG_IPC_MAJOR_3 -static int cadence_codec_init(struct processing_module *mod) -{ - struct module_data *codec = &mod->priv; - struct cadence_codec_data *cd; - struct comp_dev *dev = mod->dev; - struct module_config *setup_cfg; - int ret; - - comp_dbg(dev, "cadence_codec_init() start"); - - cd = rzalloc(SOF_MEM_FLAG_USER, sizeof(struct cadence_codec_data)); - if (!cd) { - comp_err(dev, "failed to allocate memory for cadence codec data"); - return -ENOMEM; - } - - codec->private = cd; - codec->mpd.init_done = 0; - - /* copy the setup config only for the first init */ - if (codec->state == MODULE_DISABLED && codec->cfg.avail) { - setup_cfg = &cd->setup_cfg; - - /* allocate memory for set up config */ - setup_cfg->data = rmalloc(SOF_MEM_FLAG_USER, - codec->cfg.size); - if (!setup_cfg->data) { - comp_err(dev, "failed to alloc setup config"); - ret = -ENOMEM; - goto free; - } - - /* copy the setup config */ - setup_cfg->size = codec->cfg.size; - ret = memcpy_s(setup_cfg->data, setup_cfg->size, - codec->cfg.init_data, setup_cfg->size); - if (ret) { - comp_err(dev, "failed to copy setup config %d", ret); - goto free_cfg; - } - setup_cfg->avail = true; - } - - comp_dbg(dev, "cadence_codec_init() done"); - - return 0; - -free_cfg: - rfree(setup_cfg->data); -free: - rfree(cd); - return ret; -} - -#else -#error Unknown IPC major version -#endif - -static int cadence_codec_apply_config(struct processing_module *mod) -{ - int ret = 0; - int size; - uint16_t param_id; - uint16_t codec_id; - struct module_config *cfg; - void *data; - struct module_param *param; - struct comp_dev *dev = mod->dev; - struct module_data *codec = &mod->priv; - struct cadence_codec_data *cd = codec->private; - - comp_dbg(dev, "cadence_codec_apply_config() start"); - - cfg = &codec->cfg; - - /* use setup config if no runtime config available. This will be true during reset */ - if (!cfg->avail) - cfg = &cd->setup_cfg; - - data = cfg->data; - size = cfg->size; - - if (!cfg->avail || !size) { - comp_err(dev, "cadence_codec_apply_config() error: no config available"); - return -EIO; - } - - /* Read parameters stored in `data` - it may keep plenty of - * parameters. The `size` variable is equal to param->size * count, - * where count is number of parameters stored in `data`. - */ - while (size > 0) { - param = data; - comp_dbg(dev, "cadence_codec_apply_config() applying param %d value %d", - param->id, param->data[0]); - - param_id = param->id & 0xFF; - codec_id = param->id >> 16; - - /* if the parameter is not for current codec skip it! */ - if (codec_id && codec_id != cd->api_id) { - /* Obtain next parameter */ - data = (char *)data + param->size; - size -= param->size; - continue; - } - - /* Set read parameter */ - API_CALL(cd, XA_API_CMD_SET_CONFIG_PARAM, param_id, - param->data, ret); - if (ret != LIB_NO_ERROR) { - if (LIB_IS_FATAL_ERROR(ret)) { - comp_err(dev, "failed to apply parameter: %d value: %d error: %#x", - param->id, *(int32_t *)param->data, ret); - - return ret; - } - comp_warn(dev, "applied parameter %d value %d with return code: %#x", - param->id, *(int32_t *)param->data, ret); - } - /* Obtain next parameter, it starts right after the preceding one */ - data = (char *)data + param->size; - size -= param->size; - } - - comp_dbg(dev, "cadence_codec_apply_config() done"); - - return 0; -} - -static int init_memory_tables(struct processing_module *mod) +int cadence_codec_init_memory_tables(struct processing_module *mod) { int ret, no_mem_tables, i, mem_type, mem_size, mem_alignment; void *ptr, *scratch, *persistent; @@ -480,7 +123,7 @@ static int init_memory_tables(struct processing_module *mod) API_CALL(cd, XA_API_CMD_INIT, XA_CMD_TYPE_INIT_API_POST_CONFIG_PARAMS, NULL, ret); if (ret != LIB_NO_ERROR) { - comp_err(dev, "init_memory_tables() error %x: failed to calculate memory blocks size", + comp_err(dev, "error %x: failed to calculate memory blocks size", ret); return ret; } @@ -488,17 +131,22 @@ static int init_memory_tables(struct processing_module *mod) /* Get number of memory tables */ API_CALL(cd, XA_API_CMD_GET_N_MEMTABS, 0, &no_mem_tables, ret); if (ret != LIB_NO_ERROR) { - comp_err(dev, "init_memory_tables() error %x: failed to get number of memory tables", + comp_err(dev, "error %x: failed to get number of memory tables", ret); return ret; } + cd->mem_to_be_freed = mod_zalloc(mod, no_mem_tables * sizeof(*cd->mem_to_be_freed)); + if (!cd->mem_to_be_freed) + return -ENOMEM; + cd->mem_to_be_freed_len = no_mem_tables; + /* Initialize each memory table */ for (i = 0; i < no_mem_tables; i++) { /* Get type of memory - it specifies how the memory will be used */ API_CALL(cd, XA_API_CMD_GET_MEM_INFO_TYPE, i, &mem_type, ret); if (ret != LIB_NO_ERROR) { - comp_err(dev, "init_memory_tables() error %x: failed to get mem. type info of id %d out of %d", + comp_err(dev, "error %x: failed to get mem. type info of id %d out of %d", ret, i, no_mem_tables); goto err; } @@ -507,29 +155,30 @@ static int init_memory_tables(struct processing_module *mod) */ API_CALL(cd, XA_API_CMD_GET_MEM_INFO_SIZE, i, &mem_size, ret); if (ret != LIB_NO_ERROR) { - comp_err(dev, "init_memory_tables() error %x: failed to get mem. size for mem. type %d", + comp_err(dev, "error %x: failed to get mem. size for mem. type %d", ret, mem_type); goto err; } /* Get alignment constrains */ API_CALL(cd, XA_API_CMD_GET_MEM_INFO_ALIGNMENT, i, &mem_alignment, ret); if (ret != LIB_NO_ERROR) { - comp_err(dev, "init_memory_tables() error %x: failed to get mem. alignment of mem. type %d", + comp_err(dev, "error %x: failed to get mem. alignment of mem. type %d", ret, mem_type); goto err; } /* Allocate memory for this type, taking alignment into account */ ptr = mod_alloc_align(mod, mem_size, mem_alignment); if (!ptr) { - comp_err(dev, "init_memory_tables() error %x: failed to allocate memory for %d", + comp_err(dev, "error %x: failed to allocate memory for %d", ret, mem_type); ret = -EINVAL; goto err; } + cd->mem_to_be_freed[i] = ptr; /* Finally, provide this memory for codec */ API_CALL(cd, XA_API_CMD_SET_MEM_PTR, i, ptr, ret); if (ret != LIB_NO_ERROR) { - comp_err(dev, "init_memory_tables() error %x: failed to set memory pointer for %d", + comp_err(dev, "error %x: failed to set memory pointer for %d", ret, mem_type); goto err; } @@ -550,35 +199,34 @@ static int init_memory_tables(struct processing_module *mod) codec->mpd.out_buff_size = mem_size; break; default: - comp_err(dev, "init_memory_tables() error %x: unrecognized memory type!", + comp_err(dev, "error %x: unrecognized memory type!", mem_type); ret = -EINVAL; goto err; } - comp_dbg(dev, "init_memory_tables: allocated memory of %d bytes and alignment %d for mem. type %d", + comp_dbg(dev, "allocated memory of %d bytes and alignment %d for mem. type %d", mem_size, mem_alignment, mem_type); } return 0; err: - if (scratch) - mod_free(mod, scratch); - if (persistent) - mod_free(mod, persistent); - if (codec->mpd.in_buff) - mod_free(mod, codec->mpd.in_buff); - if (codec->mpd.out_buff) - mod_free(mod, codec->mpd.out_buff); + cadence_codec_free_memory_tables(mod); + return ret; } -static int cadence_codec_get_samples(struct processing_module *mod) +size_t cadence_api_table_size(void) +{ + return ARRAY_SIZE(cadence_api_table); +} + +int cadence_codec_get_samples(struct processing_module *mod) { struct cadence_codec_data *cd = module_get_private_data(mod); struct comp_dev *dev = mod->dev; - comp_dbg(dev, "cadence_codec_get_samples() start"); + comp_dbg(dev, "start"); switch (cd->api_id) { case CADENCE_CODEC_WRAPPER_ID: @@ -595,35 +243,26 @@ static int cadence_codec_get_samples(struct processing_module *mod) return 0; } -static int cadence_codec_deep_buff_allowed(struct processing_module *mod) -{ - struct cadence_codec_data *cd = module_get_private_data(mod); - - switch (cd->api_id) { - case CADENCE_CODEC_MP3_ENC_ID: - return 0; - default: - return 1; - } -} - -static int cadence_codec_init_process(struct processing_module *mod) +int cadence_codec_init_process(struct processing_module *mod) { int ret; struct module_data *codec = &mod->priv; struct cadence_codec_data *cd = codec->private; struct comp_dev *dev = mod->dev; + codec->mpd.eos_reached = false; + codec->mpd.eos_notification_sent = false; + API_CALL(cd, XA_API_CMD_SET_INPUT_BYTES, 0, &codec->mpd.avail, ret); if (ret != LIB_NO_ERROR) { - comp_err(dev, "cadence_codec_init_process() error %x: failed to set size of input data", + comp_err(dev, "error %x: failed to set size of input data", ret); return ret; } API_CALL(cd, XA_API_CMD_INIT, XA_CMD_TYPE_INIT_PROCESS, NULL, ret); if (LIB_IS_FATAL_ERROR(ret)) { - comp_err(dev, "cadence_codec_init_process() error %x: failed to initialize codec", + comp_err(dev, "error %x: failed to initialize codec", ret); return ret; } else if (ret != LIB_NO_ERROR) { @@ -636,21 +275,21 @@ static int cadence_codec_init_process(struct processing_module *mod) * a non-fatal error and let the init process continue. Next * chunk will contain the useful data. */ - comp_warn(dev, "cadence_codec_init_process() returned non-fatal error: 0x%x", + comp_warn(dev, "returned non-fatal error: 0x%x", ret); } API_CALL(cd, XA_API_CMD_INIT, XA_CMD_TYPE_INIT_DONE_QUERY, &codec->mpd.init_done, ret); if (ret != LIB_NO_ERROR) { - comp_err(dev, "cadence_codec_init_process() error %x: failed to get lib init status", + comp_err(dev, "error %x: failed to get lib init status", ret); return ret; } API_CALL(cd, XA_API_CMD_GET_CURIDX_INPUT_BUF, 0, &codec->mpd.consumed, ret); if (ret != LIB_NO_ERROR) { - comp_err(dev, "cadence_codec_init_process() error %x: could not get consumed bytes", + comp_err(dev, "error %x: could not get consumed bytes", ret); return ret; } @@ -658,262 +297,279 @@ static int cadence_codec_init_process(struct processing_module *mod) return 0; } -static int cadence_codec_prepare(struct processing_module *mod, - struct sof_source **sources, int num_of_sources, - struct sof_sink **sinks, int num_of_sinks) +int cadence_codec_free(struct processing_module *mod) { - int ret = 0, mem_tabs_size; - struct comp_dev *dev = mod->dev; - struct module_data *codec = &mod->priv; - struct cadence_codec_data *cd = codec->private; + struct cadence_codec_data *cd = module_get_private_data(mod); + + mod_free(mod, cd->setup_cfg.data); - comp_dbg(dev, "cadence_codec_prepare() start"); + cadence_codec_free_memory_tables(mod); + mod_free(mod, cd->mem_tabs); + + mod_free(mod, cd->self); + mod_free(mod, cd); + return 0; +} + +int cadence_codec_set_configuration(struct processing_module *mod, uint32_t config_id, + enum module_cfg_fragment_position pos, + uint32_t data_offset_size, const uint8_t *fragment, + size_t fragment_size, uint8_t *response, size_t response_size) +{ + struct module_data *md = &mod->priv; + struct comp_dev *dev = mod->dev; + int ret; - ret = cadence_codec_post_init(mod); - if (ret) + ret = module_set_configuration(mod, config_id, pos, data_offset_size, fragment, + fragment_size, response, response_size); + if (ret < 0) return ret; + /* return if more fragments are expected or if the module is not prepared */ + if ((pos != MODULE_CFG_FRAGMENT_LAST && pos != MODULE_CFG_FRAGMENT_SINGLE) || + md->state < MODULE_IDLE) + return 0; + + /* whole configuration received, apply it now */ ret = cadence_codec_apply_config(mod); if (ret) { - comp_err(dev, "cadence_codec_prepare() error %x: failed to apply config", - ret); - return ret; - } - - /* Allocate memory for the codec */ - API_CALL(cd, XA_API_CMD_GET_MEMTABS_SIZE, 0, &mem_tabs_size, ret); - if (ret != LIB_NO_ERROR) { - comp_err(dev, "cadence_codec_prepare() error %x: failed to get memtabs size", - ret); + comp_err(dev, "runtime config apply failed with error %x: ", ret); return ret; } - cd->mem_tabs = mod_alloc(mod, mem_tabs_size); - if (!cd->mem_tabs) { - comp_err(dev, "cadence_codec_prepare() error: failed to allocate space for memtabs"); - return -ENOMEM; - } - - comp_dbg(dev, "allocated %d bytes for memtabs", mem_tabs_size); - - API_CALL(cd, XA_API_CMD_SET_MEMTABS_PTR, 0, cd->mem_tabs, ret); - if (ret != LIB_NO_ERROR) { - comp_err(dev, "cadence_codec_prepare() error %x: failed to set memtabs", - ret); - goto free; - } + comp_dbg(dev, "config applied"); - ret = init_memory_tables(mod); - if (ret != LIB_NO_ERROR) { - comp_err(dev, "cadence_codec_prepare() error %x: failed to init memory tables", - ret); - goto free; - } - /* Check init done status. Note, it may happen that init_done flag will return - * false value, this is normal since some codec variants needs input in order to - * fully finish initialization. That's why at codec_adapter_copy() we call - * codec_init_process() base on result obtained below. - */ -#ifdef CONFIG_CADENCE_CODEC_WRAPPER - /* TODO: remove the "#ifdef CONFIG_CADENCE_CODEC_WRAPPER" once cadence fixes the bug - * in the init/prepare sequence. Basically below API_CALL shall return 1 for - * PCM streams and 0 for compress ones. As it turns out currently it returns 1 - * in both cases so in turn compress stream won't finish its prepare during first copy - * in codec_adapter_copy(). - */ - API_CALL(cd, XA_API_CMD_INIT, XA_CMD_TYPE_INIT_DONE_QUERY, - &codec->mpd.init_done, ret); - if (ret != LIB_NO_ERROR) { - comp_err(dev, "cadence_codec_init_process() error %x: failed to get lib init status", - ret); - return ret; - } -#endif - comp_dbg(dev, "cadence_codec_prepare() done"); return 0; -free: - mod_free(mod, cd->mem_tabs); - return ret; } -static int -cadence_codec_process(struct processing_module *mod, - struct input_stream_buffer *input_buffers, int num_input_buffers, - struct output_stream_buffer *output_buffers, int num_output_buffers) +int cadence_codec_apply_params(struct processing_module *mod, int size, void *data) { - struct comp_buffer *local_buff; - struct comp_dev *dev = mod->dev; struct module_data *codec = &mod->priv; + struct comp_dev *dev = mod->dev; struct cadence_codec_data *cd = codec->private; - int free_bytes, output_bytes = cadence_codec_get_samples(mod) * - mod->stream_params->sample_container_bytes * - mod->stream_params->channels; - uint32_t remaining = input_buffers[0].size; + struct module_param *param; + uint16_t param_id; + uint16_t codec_id; int ret; - if (!cadence_codec_deep_buff_allowed(mod)) { - mod->deep_buff_bytes = 0; - } + /* Read parameters stored in `data` - it may keep plenty of + * parameters. The `size` variable is equal to param->size * count, + * where count is number of parameters stored in `data`. + */ + while (size > 0) { + param = data; + comp_dbg(dev, "cadence_codec_apply_config() applying param %d value %d", + param->id, param->data[0]); - /* Proceed only if we have enough data to fill the module buffer completely */ - if (input_buffers[0].size < codec->mpd.in_buff_size) { - comp_dbg(dev, "not enough data to process"); - return -ENODATA; - } + param_id = param->id & 0xFF; + codec_id = param->id >> 16; - if (!codec->mpd.init_done) { - memcpy_s(codec->mpd.in_buff, codec->mpd.in_buff_size, input_buffers[0].data, - codec->mpd.in_buff_size); - codec->mpd.avail = codec->mpd.in_buff_size; + /* if the parameter is not for current codec skip it! */ + if (codec_id && codec_id != cd->api_id) { + /* Obtain next parameter */ + data = (char *)data + param->size; + size -= param->size; + continue; + } - ret = cadence_codec_init_process(mod); - if (ret) - return ret; + /* Set read parameter */ + API_CALL(cd, XA_API_CMD_SET_CONFIG_PARAM, param_id, + param->data, ret); + if (ret != LIB_NO_ERROR) { + if (LIB_IS_FATAL_ERROR(ret)) { + comp_err(dev, "failed to apply parameter: %d value: %d error: %#x", + param->id, *(int32_t *)param->data, ret); - remaining -= codec->mpd.consumed; - input_buffers[0].consumed = codec->mpd.consumed; + return ret; + } + comp_warn(dev, "applied parameter %d value %d with return code: %#x", + param->id, *(int32_t *)param->data, ret); + } + /* Obtain next parameter, it starts right after the preceding one */ + data = (char *)data + param->size; + size -= param->size; } - /* do not proceed with processing if not enough free space left in the local buffer */ - local_buff = list_first_item(&mod->raw_data_buffers_list, struct comp_buffer, buffers_list); - free_bytes = audio_stream_get_free(&local_buff->stream); - if (free_bytes < output_bytes) - return -ENOSPC; - - /* Proceed only if we have enough data to fill the module buffer completely */ - if (remaining < codec->mpd.in_buff_size) - return -ENODATA; - - memcpy_s(codec->mpd.in_buff, codec->mpd.in_buff_size, - (uint8_t *)input_buffers[0].data + input_buffers[0].consumed, - codec->mpd.in_buff_size); - codec->mpd.avail = codec->mpd.in_buff_size; + return 0; +} - comp_dbg(dev, "cadence_codec_process() start"); +int cadence_init_codec_object(struct processing_module *mod) +{ + int ret; + struct comp_dev *dev = mod->dev; + struct cadence_codec_data *cd = module_get_private_data(mod); + uint32_t obj_size; - API_CALL(cd, XA_API_CMD_SET_INPUT_BYTES, 0, &codec->mpd.avail, ret); - if (ret != LIB_NO_ERROR) { - comp_err(dev, "cadence_codec_process() error %x: failed to set size of input data", - ret); + ret = cadence_codec_resolve_api(mod); + if (ret < 0) return ret; - } - API_CALL(cd, XA_API_CMD_EXECUTE, XA_CMD_TYPE_DO_EXECUTE, NULL, ret); + /* Obtain codec name */ + API_CALL(cd, XA_API_CMD_GET_LIB_ID_STRINGS, + XA_CMD_TYPE_LIB_NAME, cd->name, ret); if (ret != LIB_NO_ERROR) { - if (LIB_IS_FATAL_ERROR(ret)) { - comp_err(dev, "cadence_codec_process() error %x: processing failed", - ret); - return ret; - } - comp_warn(dev, "cadence_codec_process() nonfatal error %x", ret); + comp_err(dev, "failed to get lib name error: %x: ", ret); + return ret; } - - API_CALL(cd, XA_API_CMD_GET_OUTPUT_BYTES, 0, &codec->mpd.produced, ret); + /* Get codec object size */ + API_CALL(cd, XA_API_CMD_GET_API_SIZE, 0, &obj_size, ret); if (ret != LIB_NO_ERROR) { - comp_err(dev, "cadence_codec_process() error %x: could not get produced bytes", - ret); + comp_err(dev, "failed to get lib object size error %x:", ret); return ret; } + /* Allocate space for codec object */ + cd->self = mod_balloc(mod, obj_size); + if (!cd->self) { + comp_err(dev, "failed to allocate space for lib object"); + return -ENOMEM; + } - API_CALL(cd, XA_API_CMD_GET_CURIDX_INPUT_BUF, 0, &codec->mpd.consumed, ret); + comp_dbg(dev, "allocated %d bytes for lib object", obj_size); + + /* Set all params to their default values */ + API_CALL(cd, XA_API_CMD_INIT, XA_CMD_TYPE_INIT_API_PRE_CONFIG_PARAMS, + NULL, ret); if (ret != LIB_NO_ERROR) { - comp_err(dev, "cadence_codec_process() error %x: could not get consumed bytes", - ret); + mod_free(mod, cd->self); return ret; } - /* update consumed with the number of samples consumed during init */ - input_buffers[0].consumed += codec->mpd.consumed; - codec->mpd.consumed = input_buffers[0].consumed; - - /* copy the produced samples into the output buffer */ - memcpy_s(output_buffers[0].data, codec->mpd.produced, codec->mpd.out_buff, - codec->mpd.produced); - output_buffers[0].size = codec->mpd.produced; - - comp_dbg(dev, "cadence_codec_process() done"); - return 0; } -static int cadence_codec_reset(struct processing_module *mod) +int cadence_codec_resolve_api_with_id(struct processing_module *mod, uint32_t codec_id, + uint32_t direction) { - struct module_data *codec = &mod->priv; - struct cadence_codec_data *cd = codec->private; - int ret; - - /* - * Current CADENCE API doesn't support reset of codec's runtime parameters. - * So, free all memory associated with runtime params. These will be reallocated during - * prepare. - */ - mod_free_all(mod); + struct cadence_codec_data *cd = module_get_private_data(mod); + struct comp_dev *dev = mod->dev; + uint32_t api_id; + uint32_t n_apis = cadence_api_table_size(); + xa_codec_func_t *api = NULL; + int i; - /* reset to default params */ - API_CALL(cd, XA_API_CMD_INIT, XA_CMD_TYPE_INIT_API_PRE_CONFIG_PARAMS, NULL, ret); - if (ret != LIB_NO_ERROR) - return ret; + api_id = cadence_codec_get_api_id(codec_id, direction); + if (api_id < 0) + return api_id; - codec->mpd.init_done = 0; + /* Find and assign API function */ + for (i = 0; i < n_apis; i++) { + if (cadence_api_table[i].id == api_id) { + api = cadence_api_table[i].api; + break; + } + } - rfree(cd->self); - cd->self = NULL; + /* Verify API assignment */ + if (!api) { + comp_err(dev, "could not find API function for id %x", + api_id); + return -EINVAL; + } + cd->api = api; + cd->api_id = api_id; - return ret; + return 0; } -static int cadence_codec_free(struct processing_module *mod) +static void cadence_store_api_error_code(struct comp_dev *dev, + XA_ERRORCODE *api_error_code, + int ret, const char *cmd_name) { - struct cadence_codec_data *cd = module_get_private_data(mod); + if (!api_error_code) + return; - rfree(cd->setup_cfg.data); - mod_free_all(mod); - rfree(cd->self); - rfree(cd); - return 0; + if (*api_error_code && *api_error_code != ret) + comp_dbg(dev, "overwriting api error code at %s: old %#x new %#x", + cmd_name, *api_error_code, ret); + + *api_error_code = ret; } -static int -cadence_codec_set_configuration(struct processing_module *mod, uint32_t config_id, - enum module_cfg_fragment_position pos, uint32_t data_offset_size, - const uint8_t *fragment, size_t fragment_size, uint8_t *response, - size_t response_size) +int cadence_codec_process_data(struct processing_module *mod, + XA_ERRORCODE *api_error_code) { - struct module_data *md = &mod->priv; + struct cadence_codec_data *cd = module_get_private_data(mod); + struct module_data *codec = &mod->priv; struct comp_dev *dev = mod->dev; + uint32_t done = 0; int ret; - ret = module_set_configuration(mod, config_id, pos, data_offset_size, fragment, - fragment_size, response, response_size); - if (ret < 0) - return ret; + if (api_error_code) + *api_error_code = 0; + + if (codec->mpd.eos_reached) { + codec->mpd.produced = 0; + codec->mpd.consumed = 0; - /* return if more fragments are expected or if the module is not prepared */ - if ((pos != MODULE_CFG_FRAGMENT_LAST && pos != MODULE_CFG_FRAGMENT_SINGLE) || - md->state < MODULE_IDLE) return 0; + } - /* whole configuration received, apply it now */ - ret = cadence_codec_apply_config(mod); - if (ret) { - comp_err(dev, "error %x: runtime config apply failed", + if (dev->pipeline->expect_eos) { + /* Signal that the stream is expected to end anytime soon */ + API_CALL(cd, XA_API_CMD_INPUT_OVER, 0, NULL, ret); + if (ret != LIB_NO_ERROR) { + if (api_error_code) + *api_error_code = ret; + + if (LIB_IS_FATAL_ERROR(ret)) { + comp_err(dev, "input_over failed with error: %x", ret); + return ret; + } + } + } + + API_CALL(cd, XA_API_CMD_SET_INPUT_BYTES, 0, &codec->mpd.avail, ret); + if (ret != LIB_NO_ERROR) { + comp_err(dev, "failed to set size of input data with error: %x:", ret); + return ret; + } + + API_CALL(cd, XA_API_CMD_EXECUTE, XA_CMD_TYPE_DO_EXECUTE, NULL, ret); + if (ret != LIB_NO_ERROR) { + cadence_store_api_error_code(dev, api_error_code, ret, + "XA_API_CMD_EXECUTE_DO_EXECUTE"); + + if (LIB_IS_FATAL_ERROR(ret)) { + comp_err(dev, "processing failed with error: %x", ret); + return ret; + } + } + + API_CALL(cd, XA_API_CMD_EXECUTE, XA_CMD_TYPE_DONE_QUERY, &done, ret); + if (ret != LIB_NO_ERROR) { + cadence_store_api_error_code(dev, api_error_code, ret, + "XA_API_CMD_EXECUTE_DONE_QUERY"); + + if (LIB_IS_FATAL_ERROR(ret)) { + comp_err(dev, "done query failed with error: %x", ret); + return ret; + } + } + + API_CALL(cd, XA_API_CMD_GET_OUTPUT_BYTES, 0, &codec->mpd.produced, ret); + if (ret != LIB_NO_ERROR) { + comp_err(dev, "could not get produced bytes, error %x:", ret); return ret; } - comp_dbg(dev, "config applied"); + API_CALL(cd, XA_API_CMD_GET_CURIDX_INPUT_BUF, 0, &codec->mpd.consumed, ret); + if (ret != LIB_NO_ERROR) { + comp_err(dev, "could not get consumed bytes, error: %x", ret); + return ret; + } - return 0; -} + if (dev->pipeline->expect_eos) { + /* + * AAC decoder cannot signal DONE, check if it stopped + * producing data when EOS is expected + */ + if (cd->api_id == CADENCE_CODEC_AAC_DEC_ID && !codec->mpd.produced) + done = true; -static const struct module_interface cadence_codec_interface = { - .init = cadence_codec_init, - .prepare = cadence_codec_prepare, - .process_raw_data = cadence_codec_process, - .set_configuration = cadence_codec_set_configuration, - .reset = cadence_codec_reset, - .free = cadence_codec_free -}; + if (done) + codec->mpd.eos_reached = true; + } -DECLARE_MODULE_ADAPTER(cadence_codec_interface, cadence_codec_uuid, cadence_codec_tr); -SOF_MODULE_INIT(cadence_codec, sys_comp_module_cadence_codec_interface_init); + return 0; +} diff --git a/src/audio/module_adapter/module/cadence_ipc3.c b/src/audio/module_adapter/module/cadence_ipc3.c new file mode 100644 index 000000000000..d1711e79f800 --- /dev/null +++ b/src/audio/module_adapter/module/cadence_ipc3.c @@ -0,0 +1,317 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// Copyright(c) 2020 Intel Corporation. All rights reserved. +// +// Author: Marcin Rajwa <marcin.rajwa@linux.intel.com> + +/* + * \file cadence.c + * \brief Cadence Codec API + * \author Marcin Rajwa <marcin.rajwa@linux.intel.com> + * + */ + +#include <ipc/compress_params.h> +#include <sof/audio/module_adapter/module/generic.h> +#include <sof/audio/module_adapter/module/cadence.h> +#include <rtos/init.h> + +SOF_DEFINE_REG_UUID(cadence_codec); +LOG_MODULE_DECLARE(cadence_codec, CONFIG_SOF_LOG_LEVEL); +DECLARE_TR_CTX(cadence_codec_tr, SOF_UUID(cadence_codec_uuid), LOG_LEVEL_INFO); + +int cadence_codec_resolve_api(struct processing_module *mod) +{ + int ret; + struct snd_codec codec_params; + uint32_t codec_id = DEFAULT_CODEC_ID; + + if (mod->stream_params->ext_data_length) { + ret = memcpy_s(&codec_params, mod->stream_params->ext_data_length, + (uint8_t *)mod->stream_params + sizeof(*mod->stream_params), + mod->stream_params->ext_data_length); + if (ret < 0) + return ret; + + codec_id = codec_params.id; + } + + /* IPC3 only supports playback */ + return cadence_codec_resolve_api_with_id(mod, codec_id, mod->stream_params->direction); +} + +static int cadence_codec_init(struct processing_module *mod) +{ + struct module_data *codec = &mod->priv; + struct cadence_codec_data *cd; + struct comp_dev *dev = mod->dev; + struct module_config *setup_cfg; + int ret; + + comp_dbg(dev, "cadence_codec_init() start"); + + cd = mod_zalloc(mod, sizeof(struct cadence_codec_data)); + if (!cd) { + comp_err(dev, "failed to allocate memory for cadence codec data"); + return -ENOMEM; + } + + codec->private = cd; + codec->mpd.init_done = 0; + + /* copy the setup config only for the first init */ + if (codec->state == MODULE_DISABLED && codec->cfg.avail) { + setup_cfg = &cd->setup_cfg; + + /* allocate memory for set up config */ + setup_cfg->data = mod_alloc(mod, codec->cfg.size); + if (!setup_cfg->data) { + comp_err(dev, "failed to alloc setup config"); + ret = -ENOMEM; + goto free; + } + + /* copy the setup config */ + setup_cfg->size = codec->cfg.size; + ret = memcpy_s(setup_cfg->data, setup_cfg->size, + codec->cfg.init_data, setup_cfg->size); + if (ret) { + comp_err(dev, "failed to copy setup config %d", ret); + goto free_cfg; + } + setup_cfg->avail = true; + } + + comp_dbg(dev, "cadence_codec_init() done"); + + return 0; + +free_cfg: + mod_free(mod, setup_cfg->data); +free: + mod_free(mod, cd); + return ret; +} + +int cadence_codec_apply_config(struct processing_module *mod) +{ + int size; + struct module_config *cfg; + void *data; + struct comp_dev *dev = mod->dev; + struct module_data *codec = &mod->priv; + struct cadence_codec_data *cd = codec->private; + + comp_dbg(dev, "cadence_codec_apply_config() start"); + + cfg = &codec->cfg; + + /* use setup config if no runtime config available. This will be true during reset */ + if (!cfg->avail) + cfg = &cd->setup_cfg; + + data = cfg->data; + size = cfg->size; + + if (!cfg->avail || !size) { + comp_err(dev, "cadence_codec_apply_config() error: no config available"); + return -EIO; + } + + return cadence_codec_apply_params(mod, size, data); +} + +static int cadence_codec_deep_buff_allowed(struct processing_module *mod) +{ + struct cadence_codec_data *cd = module_get_private_data(mod); + + switch (cd->api_id) { + case CADENCE_CODEC_MP3_ENC_ID: + return 0; + default: + return 1; + } +} + +static int cadence_codec_prepare(struct processing_module *mod, + struct sof_source **sources, int num_of_sources, + struct sof_sink **sinks, int num_of_sinks) +{ + int ret = 0, mem_tabs_size; + struct comp_dev *dev = mod->dev; + struct module_data *codec = &mod->priv; + struct cadence_codec_data *cd = codec->private; + + comp_dbg(dev, "cadence_codec_prepare() start"); + + ret = cadence_init_codec_object(mod); + if (ret) + return ret; + + ret = cadence_codec_apply_config(mod); + if (ret) { + comp_err(dev, "cadence_codec_prepare() error %x: failed to apply config", + ret); + return ret; + } + + /* Allocate memory for the codec */ + API_CALL(cd, XA_API_CMD_GET_MEMTABS_SIZE, 0, &mem_tabs_size, ret); + if (ret != LIB_NO_ERROR) { + comp_err(dev, "cadence_codec_prepare() error %x: failed to get memtabs size", + ret); + return ret; + } + + cd->mem_tabs = mod_alloc(mod, mem_tabs_size); + if (!cd->mem_tabs) { + comp_err(dev, "cadence_codec_prepare() error: failed to allocate space for memtabs"); + return -ENOMEM; + } + + comp_dbg(dev, "allocated %d bytes for memtabs", mem_tabs_size); + + API_CALL(cd, XA_API_CMD_SET_MEMTABS_PTR, 0, cd->mem_tabs, ret); + if (ret != LIB_NO_ERROR) { + comp_err(dev, "cadence_codec_prepare() error %x: failed to set memtabs", + ret); + goto free; + } + + ret = cadence_codec_init_memory_tables(mod); + if (ret != LIB_NO_ERROR) { + comp_err(dev, "cadence_codec_prepare() error %x: failed to init memory tables", + ret); + goto free; + } + /* Check init done status. Note, it may happen that init_done flag will return + * false value, this is normal since some codec variants needs input in order to + * fully finish initialization. That's why at codec_adapter_copy() we call + * codec_init_process() base on result obtained below. + */ +#ifdef CONFIG_CADENCE_CODEC_WRAPPER + /* TODO: remove the "#ifdef CONFIG_CADENCE_CODEC_WRAPPER" once cadence fixes the bug + * in the init/prepare sequence. Basically below API_CALL shall return 1 for + * PCM streams and 0 for compress ones. As it turns out currently it returns 1 + * in both cases so in turn compress stream won't finish its prepare during first copy + * in codec_adapter_copy(). + */ + API_CALL(cd, XA_API_CMD_INIT, XA_CMD_TYPE_INIT_DONE_QUERY, + &codec->mpd.init_done, ret); + if (ret != LIB_NO_ERROR) { + comp_err(dev, "cadence_codec_init_process() error %x: failed to get lib init status", + ret); + return ret; + } +#endif + comp_dbg(dev, "cadence_codec_prepare() done"); + return 0; +free: + mod_free(mod, cd->mem_tabs); + return ret; +} + +static int +cadence_codec_process(struct processing_module *mod, + struct input_stream_buffer *input_buffers, int num_input_buffers, + struct output_stream_buffer *output_buffers, int num_output_buffers) +{ + struct comp_buffer *local_buff; + struct comp_dev *dev = mod->dev; + struct module_data *codec = &mod->priv; + int free_bytes, output_bytes = cadence_codec_get_samples(mod) * + mod->stream_params->sample_container_bytes * + mod->stream_params->channels; + uint32_t remaining = input_buffers[0].size; + int ret; + + if (!cadence_codec_deep_buff_allowed(mod)) + mod->deep_buff_bytes = 0; + + /* Proceed only if we have enough data to fill the module buffer completely */ + if (input_buffers[0].size < codec->mpd.in_buff_size) { + comp_dbg(dev, "not enough data to process"); + return -ENODATA; + } + + if (!codec->mpd.init_done) { + memcpy_s(codec->mpd.in_buff, codec->mpd.in_buff_size, input_buffers[0].data, + codec->mpd.in_buff_size); + codec->mpd.avail = codec->mpd.in_buff_size; + + ret = cadence_codec_init_process(mod); + if (ret) + return ret; + + remaining -= codec->mpd.consumed; + input_buffers[0].consumed = codec->mpd.consumed; + } + + /* do not proceed with processing if not enough free space left in the local buffer */ + local_buff = list_first_item(&mod->raw_data_buffers_list, struct comp_buffer, buffers_list); + free_bytes = audio_stream_get_free(&local_buff->stream); + if (free_bytes < output_bytes) + return -ENOSPC; + + /* Proceed only if we have enough data to fill the module buffer completely */ + if (remaining < codec->mpd.in_buff_size) + return -ENODATA; + + memcpy_s(codec->mpd.in_buff, codec->mpd.in_buff_size, + (uint8_t *)input_buffers[0].data + input_buffers[0].consumed, + codec->mpd.in_buff_size); + codec->mpd.avail = codec->mpd.in_buff_size; + + comp_dbg(dev, "cadence_codec_process() start"); + + ret = cadence_codec_process_data(mod, NULL); + if (ret) + return ret; + + /* update consumed with the number of samples consumed during init */ + input_buffers[0].consumed += codec->mpd.consumed; + codec->mpd.consumed = input_buffers[0].consumed; + + /* copy the produced samples into the output buffer */ + memcpy_s(output_buffers[0].data, codec->mpd.produced, codec->mpd.out_buff, + codec->mpd.produced); + output_buffers[0].size = codec->mpd.produced; + + comp_dbg(dev, "cadence_codec_process() done"); + + return 0; +} + +static int cadence_codec_reset(struct processing_module *mod) +{ + struct module_data *codec = &mod->priv; + struct cadence_codec_data *cd = codec->private; + int ret; + + cadence_codec_free_memory_tables(mod); + mod_free(mod, cd->mem_tabs); + + /* reset to default params */ + API_CALL(cd, XA_API_CMD_INIT, XA_CMD_TYPE_INIT_API_PRE_CONFIG_PARAMS, NULL, ret); + if (ret != LIB_NO_ERROR) + return ret; + + codec->mpd.init_done = 0; + + mod_free(mod, cd->self); + cd->self = NULL; + + return ret; +} + +static const struct module_interface cadence_codec_interface = { + .init = cadence_codec_init, + .prepare = cadence_codec_prepare, + .process_raw_data = cadence_codec_process, + .set_configuration = cadence_codec_set_configuration, + .reset = cadence_codec_reset, + .free = cadence_codec_free +}; + +DECLARE_MODULE_ADAPTER(cadence_codec_interface, cadence_codec_uuid, cadence_codec_tr); +SOF_MODULE_INIT(cadence_codec, sys_comp_module_cadence_codec_interface_init); diff --git a/src/audio/module_adapter/module/cadence_ipc4.c b/src/audio/module_adapter/module/cadence_ipc4.c new file mode 100644 index 000000000000..9621d8ab0f1d --- /dev/null +++ b/src/audio/module_adapter/module/cadence_ipc4.c @@ -0,0 +1,582 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// Copyright(c) 2025-2026 Intel Corporation. All rights reserved. +// + +#include <sof/audio/module_adapter/module/generic.h> +#include <sof/audio/module_adapter/module/cadence.h> +#include <sof/audio/cadence/mp3_dec/xa_mp3_dec_api.h> +#include <sof/audio/cadence/mp3_enc/xa_mp3_enc_api.h> +#include <sof/audio/cadence/aac_dec/xa_aac_dec_api.h> +#include <sof/ipc/msg.h> +#include <sof/schedule/ll_schedule_domain.h> +#include <ipc/compress_params.h> +#include <ipc4/notification.h> +#include <rtos/init.h> + +SOF_DEFINE_REG_UUID(cadence_codec); +LOG_MODULE_DECLARE(cadence_codec, CONFIG_SOF_LOG_LEVEL); +DECLARE_TR_CTX(cadence_codec_tr, SOF_UUID(cadence_codec_uuid), LOG_LEVEL_INFO); + +int cadence_codec_resolve_api(struct processing_module *mod) +{ + struct cadence_codec_data *cd = module_get_private_data(mod); + struct module_config *setup_cfg = &cd->setup_cfg; + struct snd_codec *codec_params; + uint32_t codec_id = DEFAULT_CODEC_ID; + + /* update codec_id if setup_cfg is available */ + if (setup_cfg->avail) { + codec_params = (struct snd_codec *)cd->setup_cfg.data; + codec_id = codec_params->id; + } + + return cadence_codec_resolve_api_with_id(mod, codec_id, cd->direction); +} + +static int cadence_configure_mp3_dec_params(struct processing_module *mod) +{ + struct cadence_codec_data *cd = module_get_private_data(mod); + struct comp_dev *dev = mod->dev; + int word_size; + int ret; + + /* Cadence module only supports 16bit or 24 bits for word size */ + switch (cd->base_cfg.audio_fmt.depth) { + case IPC4_DEPTH_16BIT: + word_size = 16; + break; + case IPC4_DEPTH_24BIT: + case IPC4_DEPTH_32BIT: + word_size = 24; + break; + default: + comp_err(dev, "Unsupported bit depth: %d", cd->base_cfg.audio_fmt.depth); + return -EINVAL; + } + + API_CALL(cd, XA_API_CMD_SET_CONFIG_PARAM, XA_MP3DEC_CONFIG_PARAM_PCM_WDSZ, + (void *)&word_size, ret); + if (ret != LIB_NO_ERROR) { + if (LIB_IS_FATAL_ERROR(ret)) { + comp_err(dev, "failed to apply config param word size: error: %#x", ret); + return ret; + } + comp_warn(dev, "applied param word size return code: %#x", ret); + } + + return 0; +} + +static int cadence_configure_mp3_enc_params(struct processing_module *mod) +{ + struct cadence_codec_data *cd = module_get_private_data(mod); + struct comp_dev *dev = mod->dev; + int word_size = 16; + int ret; + + /* + * Cadence encoder only supports 16-bit word size. Make sure the topology is set up + * correctly + */ + switch (cd->base_cfg.audio_fmt.depth) { + case IPC4_DEPTH_24BIT: + case IPC4_DEPTH_32BIT: + comp_err(dev, "Unsupported bit depth: %d for MP3 encoder", + cd->base_cfg.audio_fmt.depth); + return -EINVAL; + default: + break; + } + + API_CALL(cd, XA_API_CMD_SET_CONFIG_PARAM, XA_MP3ENC_CONFIG_PARAM_PCM_WDSZ, + (void *)&word_size, ret); + if (ret != LIB_NO_ERROR) { + if (LIB_IS_FATAL_ERROR(ret)) { + comp_err(dev, "failed to apply config param word size: error: %#x", ret); + return ret; + } + comp_warn(dev, "applied param word size return code: %#x", ret); + } + + int num_channels = cd->base_cfg.audio_fmt.channels_count; + + API_CALL(cd, XA_API_CMD_SET_CONFIG_PARAM, XA_MP3ENC_CONFIG_PARAM_NUM_CHANNELS, + (void *)&num_channels, ret); + if (ret != LIB_NO_ERROR) { + if (LIB_IS_FATAL_ERROR(ret)) { + comp_err(dev, "failed to apply config num_channels: error: %#x", ret); + return ret; + } + } + + int sampling_freq = cd->base_cfg.audio_fmt.sampling_frequency; + + API_CALL(cd, XA_API_CMD_SET_CONFIG_PARAM, XA_MP3ENC_CONFIG_PARAM_SAMP_FREQ, + (void *)&sampling_freq, ret); + if (ret != LIB_NO_ERROR) { + if (LIB_IS_FATAL_ERROR(ret)) { + comp_err(dev, "failed to apply config sampling_frequency: error: %#x", ret); + return ret; + } + } + + int bitrate = CADENCE_MP3_ENCODER_DEFAULT_BITRATE; + + API_CALL(cd, XA_API_CMD_SET_CONFIG_PARAM, XA_MP3ENC_CONFIG_PARAM_BITRATE, + (void *)&bitrate, ret); + if (ret != LIB_NO_ERROR) { + if (LIB_IS_FATAL_ERROR(ret)) { + comp_err(dev, "failed to apply config bitrate: error: %#x", ret); + return ret; + } + } + + return 0; +} + +static int cadence_configure_aac_dec_params(struct processing_module *mod) +{ + struct cadence_codec_data *cd = module_get_private_data(mod); + struct module_config *setup_cfg = &cd->setup_cfg; + struct comp_dev *dev = mod->dev; + struct snd_codec *codec_params; + int bitstream_format = XA_AACDEC_EBITSTREAM_TYPE_AAC_ADTS; + int word_size; + int ret; + + /* check bitstream format. Only MPEG-4 ADTS supported for now */ + if (setup_cfg->avail) { + codec_params = (struct snd_codec *)cd->setup_cfg.data; + if (codec_params->format != SND_AUDIOSTREAMFORMAT_MP4ADTS) { + comp_err(dev, "Unsupported AAC format: %d", codec_params->format); + return -EINVAL; + } + } else { + comp_err(dev, "No setup config available for AAC decoder"); + return -EINVAL; + } + + /* AAC decoder module only supports 16bit or 24 bits for word size */ + switch (cd->base_cfg.audio_fmt.depth) { + case IPC4_DEPTH_16BIT: + word_size = 16; + break; + case IPC4_DEPTH_24BIT: + case IPC4_DEPTH_32BIT: + word_size = 24; + break; + default: + comp_err(dev, "Unsupported bit depth: %d", cd->base_cfg.audio_fmt.depth); + return -EINVAL; + } + + API_CALL(cd, XA_API_CMD_SET_CONFIG_PARAM, XA_AACDEC_CONFIG_PARAM_PCM_WDSZ, + (void *)&word_size, ret); + if (ret != LIB_NO_ERROR) { + if (LIB_IS_FATAL_ERROR(ret)) { + comp_err(dev, "failed to apply config param word size: error: %#x", ret); + return ret; + } + comp_warn(dev, "applied param word size return code: %#x", ret); + } + + API_CALL(cd, XA_API_CMD_SET_CONFIG_PARAM, XA_AACDEC_CONFIG_PARAM_EXTERNALBSFORMAT, + (void *)&bitstream_format, ret); + if (ret != LIB_NO_ERROR) { + if (LIB_IS_FATAL_ERROR(ret)) { + comp_err(dev, "failed to apply config param bitstream format: error: %#x", + ret); + return ret; + } + comp_warn(dev, "applied param bitstream format return code: %#x", ret); + } + + return 0; +} + +static int cadence_configure_codec_params(struct processing_module *mod) +{ + struct cadence_codec_data *cd = module_get_private_data(mod); + + switch (cd->api_id) { + case CADENCE_CODEC_MP3_DEC_ID: + return cadence_configure_mp3_dec_params(mod); + case CADENCE_CODEC_MP3_ENC_ID: + return cadence_configure_mp3_enc_params(mod); + case CADENCE_CODEC_AAC_DEC_ID: + return cadence_configure_aac_dec_params(mod); + case CADENCE_CODEC_VORBIS_DEC_ID: + /* No configuration needed for Vorbis */ + return 0; + default: + break; + } + + comp_err(mod->dev, "Unsupported codec API ID: %u", cd->api_id); + return -EINVAL; +} + +static int cadence_codec_init(struct processing_module *mod) +{ + struct module_data *codec = &mod->priv; + struct module_config *cfg = &codec->cfg; + struct module_ext_init_data *ext_data = cfg->ext_data; + struct module_config *setup_cfg = NULL; + struct cadence_codec_data *cd; + struct comp_dev *dev = mod->dev; + int mem_tabs_size; + int ret; + + comp_dbg(dev, "cadence_codec_init() start"); + + cd = mod_zalloc(mod, sizeof(struct cadence_codec_data)); + if (!cd) { + comp_err(dev, "failed to allocate memory for cadence codec data"); + return -ENOMEM; + } + + codec->private = cd; + memcpy_s(&cd->base_cfg, sizeof(cd->base_cfg), &cfg->base_cfg, sizeof(cd->base_cfg)); + + codec->mpd.init_done = 0; + + /* copy the setup config only for the first init */ + if (codec->state == MODULE_DISABLED && ext_data->module_data_size > 0) { + int size = ext_data->module_data_size; + uint8_t *init_bytes; + + setup_cfg = &cd->setup_cfg; + + /* allocate memory for set up config (codec params) */ + setup_cfg->data = mod_alloc(mod, size); + if (!setup_cfg->data) { + comp_err(dev, "failed to alloc setup config"); + ret = -ENOMEM; + goto free_cd; + } + + setup_cfg->size = size; + ret = memcpy_s(setup_cfg->data, size, ext_data->module_data, size); + if (ret) { + comp_err(dev, "failed to copy setup config %d", ret); + goto free_cfg; + } + setup_cfg->avail = true; + codec->cfg.avail = false; + + /* direction follows the codec params in init data */ + init_bytes = (uint8_t *)ext_data->module_data; + cd->direction = *(uint32_t *)(init_bytes + sizeof(struct snd_codec)); + + comp_info(dev, "codec direction set to %u", cd->direction); + } + + ret = cadence_init_codec_object(mod); + if (ret) + goto free_cfg; + + ret = cadence_configure_codec_params(mod); + if (ret) + goto free_cfg; + + /* Allocate memory for the codec */ + API_CALL(cd, XA_API_CMD_GET_MEMTABS_SIZE, 0, &mem_tabs_size, ret); + if (ret != LIB_NO_ERROR) { + comp_err(dev, "error %x: failed to get memtabs size", ret); + goto free_cfg; + } + + cd->mem_tabs = mod_alloc(mod, mem_tabs_size); + if (!cd->mem_tabs) { + comp_err(dev, "failed to allocate space for memtabs"); + goto free_cfg; + } + + comp_dbg(dev, "allocated %d bytes for memtabs", mem_tabs_size); + + API_CALL(cd, XA_API_CMD_SET_MEMTABS_PTR, 0, cd->mem_tabs, ret); + if (ret != LIB_NO_ERROR) { + comp_err(dev, "error %x: failed to set memtabs", ret); + goto free; + } + + ret = cadence_codec_init_memory_tables(mod); + if (ret != LIB_NO_ERROR) { + comp_err(dev, "error %x: failed to init memory tables", ret); + goto free; + } + + comp_dbg(dev, "cadence_codec_init() done"); + + return 0; +free: + mod_free(mod, cd->mem_tabs); +free_cfg: + if (setup_cfg) + mod_free(mod, setup_cfg->data); +free_cd: + mod_free(mod, cd); + + return ret; +} + +int cadence_codec_apply_config(struct processing_module *mod) +{ + int size; + struct module_config *cfg; + void *data; + struct comp_dev *dev = mod->dev; + struct module_data *codec = &mod->priv; + + cfg = &codec->cfg; + + /* this will be true during prepare if there's no config available after init */ + if (!cfg->avail) + return 0; + + data = cfg->data; + size = cfg->size; + + if (!size) { + comp_err(dev, "error: no data available in config to apply"); + return -EIO; + } + + return cadence_codec_apply_params(mod, size, data); +} + +static int cadence_codec_prepare(struct processing_module *mod, + struct sof_source **sources, int num_of_sources, + struct sof_sink **sinks, int num_of_sinks) +{ + int ret = 0; + struct comp_dev *dev = mod->dev; + struct module_data *codec = &mod->priv; + + comp_dbg(dev, "cadence_codec_prepare() start"); + + ret = cadence_codec_apply_config(mod); + if (ret) { + comp_err(dev, "failed to apply config error %x:", ret); + return ret; + } + + /* Check init done status. Note, it may happen that init_done flag will return + * false value, this is normal since some codec variants needs input in order to + * fully finish initialization. That's why at codec_adapter_copy() we call + * codec_init_process() base on result obtained below. + */ +#ifdef CONFIG_CADENCE_CODEC_WRAPPER + /* TODO: remove the "#ifdef CONFIG_CADENCE_CODEC_WRAPPER" once cadence fixes the bug + * in the init/prepare sequence. Basically below API_CALL shall return 1 for + * PCM streams and 0 for compress ones. As it turns out currently it returns 1 + * in both cases so in turn compress stream won't finish its prepare during first copy + * in codec_adapter_copy(). + */ + API_CALL(cd, XA_API_CMD_INIT, XA_CMD_TYPE_INIT_DONE_QUERY, + &codec->mpd.init_done, ret); + if (ret != LIB_NO_ERROR) { + comp_err(dev, "failed to get lib init status error %x:", ret); + return ret; + } +#endif + + /* set the period based on the minimum required input data size */ + dev->period = 1000000ULL * codec->mpd.in_buff_size / + (source_get_frame_bytes(sources[0]) * source_get_rate(sources[0])); + comp_dbg(dev, "period set to %u usec", dev->period); + + /* align down period to LL cycle time */ + dev->period /= LL_TIMER_PERIOD_US; + dev->period *= LL_TIMER_PERIOD_US; + + comp_dbg(dev, "cadence_codec_prepare() done"); + return 0; +} + +static void cadence_copy_data_from_buffer(void *dest, const void *buffer_ptr, size_t bytes_to_copy, + size_t buffer_size, uint8_t const *buffer_start) +{ + size_t bytes_to_end = (size_t)((uint8_t *)buffer_start + + buffer_size - (uint8_t *)buffer_ptr); + + if (bytes_to_end >= bytes_to_copy) { + /* No wrap, copy directly */ + memcpy_s(dest, bytes_to_copy, buffer_ptr, bytes_to_copy); + return; + } + + /* Wrap occurs, copy in two parts */ + memcpy_s(dest, bytes_to_end, buffer_ptr, bytes_to_end); + memcpy_s((uint8_t *)dest + bytes_to_end, bytes_to_copy - bytes_to_end, + buffer_start, bytes_to_copy - bytes_to_end); +} + +static int cadence_codec_process(struct processing_module *mod, struct sof_source **sources, + int num_of_sources, struct sof_sink **sinks, int num_of_sinks) +{ + struct comp_dev *dev = mod->dev; + struct module_data *codec = &mod->priv; + size_t in_size = source_get_data_available(sources[0]); + size_t out_space = sink_get_free_size(sinks[0]); + uint8_t const *source_buffer_start; + int consumed_during_init = 0; + uint32_t remaining = in_size; + const void *src_ptr; + size_t src_bytes; + int ret; + + if (!codec->mpd.init_done) { + /* Acquire data from the source buffer */ + ret = source_get_data(sources[0], codec->mpd.in_buff_size, &src_ptr, + (const void **)&source_buffer_start, &src_bytes); + if (ret) { + comp_err(dev, "cannot get data from source buffer"); + return ret; + } + + cadence_copy_data_from_buffer(codec->mpd.in_buff, src_ptr, codec->mpd.in_buff_size, + src_bytes, source_buffer_start); + + codec->mpd.avail = codec->mpd.in_buff_size; + ret = cadence_codec_init_process(mod); + if (ret) + return ret; + + remaining -= codec->mpd.consumed; + source_release_data(sources[0], codec->mpd.consumed); + consumed_during_init = codec->mpd.consumed; + } + + codec->mpd.consumed = 0; + + /* Proceed only if we have enough data to fill the module buffer completely */ + if (remaining < codec->mpd.in_buff_size) + return -ENODATA; + + /* Acquire data from the source buffer */ + ret = source_get_data(sources[0], codec->mpd.in_buff_size, &src_ptr, + (const void **)&source_buffer_start, &src_bytes); + + cadence_copy_data_from_buffer(codec->mpd.in_buff, src_ptr, codec->mpd.in_buff_size, + src_bytes, source_buffer_start); + codec->mpd.avail = codec->mpd.in_buff_size; + + comp_dbg(dev, "cadence_codec_process() start"); + + ret = cadence_codec_process_data(mod, NULL); + if (ret) { + source_release_data(sources[0], 0); + return ret; + } + + if (codec->mpd.eos_reached && !codec->mpd.eos_notification_sent) { + struct ipc_msg msg_proto; + struct comp_ipc_config *ipc_config = &dev->ipc_config; + union ipc4_notification_header *primary = + (union ipc4_notification_header *)&msg_proto.header; + struct sof_ipc4_notify_module_data *msg_module_data; + struct ipc_msg *msg; + + memset_s(&msg_proto, sizeof(msg_proto), 0, sizeof(msg_proto)); + primary->r.notif_type = SOF_IPC4_MODULE_NOTIFICATION; + primary->r.type = SOF_IPC4_GLB_NOTIFICATION; + primary->r.rsp = SOF_IPC4_MESSAGE_DIR_MSG_REQUEST; + primary->r.msg_tgt = SOF_IPC4_MESSAGE_TARGET_FW_GEN_MSG; + msg = ipc_msg_w_ext_init(msg_proto.header, msg_proto.extension, + sizeof(*msg_module_data)); + if (msg) { + msg_module_data = (struct sof_ipc4_notify_module_data *)msg->tx_data; + msg_module_data->instance_id = IPC4_INST_ID(ipc_config->id); + msg_module_data->module_id = IPC4_MOD_ID(ipc_config->id); + msg_module_data->event_id = SOF_IPC4_NOTIFY_MODULE_EVENTID_COMPR_MAGIC_VAL; + msg_module_data->event_data_size = 0; + + ipc_msg_send(msg, NULL, false); + codec->mpd.eos_notification_sent = true; + } + + /* Set EOS for the sink as we are not going to produce more data */ + audio_buffer_set_eos(sof_audio_buffer_from_sink(sinks[0])); + } + + /* do not proceed if not enough free space left */ + if (out_space < codec->mpd.produced) { + source_release_data(sources[0], 0); + return -ENOSPC; + } + + void *sink_ptr; + size_t sink_bytes; + uint8_t const *sink_buffer_start; + + ret = sink_get_buffer(sinks[0], codec->mpd.produced, &sink_ptr, + (void **)&sink_buffer_start, &sink_bytes); + if (ret) { + comp_err(dev, "cannot get sink buffer"); + return ret; + } + + /* Copy the produced samples into the output buffer */ + size_t bytes_to_end = (size_t)((uint8_t *)sink_buffer_start + + sink_bytes - (uint8_t *)sink_ptr); + + if (bytes_to_end >= codec->mpd.produced) { + /* No wrap, copy directly */ + memcpy_s(sink_ptr, codec->mpd.produced, codec->mpd.out_buff, + codec->mpd.produced); + } else { + /* Wrap occurs, copy in two parts */ + memcpy_s(sink_ptr, bytes_to_end, codec->mpd.out_buff, bytes_to_end); + memcpy_s((uint8_t *)sink_buffer_start, codec->mpd.produced - bytes_to_end, + (uint8_t *)codec->mpd.out_buff + bytes_to_end, + codec->mpd.produced - bytes_to_end); + } + + source_release_data(sources[0], codec->mpd.consumed); + sink_commit_buffer(sinks[0], codec->mpd.produced); + + /* reset produced and consumed */ + codec->mpd.consumed = 0; + codec->mpd.produced = 0; + + comp_dbg(dev, "cadence_codec_process() done"); + + return 0; +} + +static int cadence_codec_reset(struct processing_module *mod) +{ + struct module_data *codec = &mod->priv; + + codec->mpd.init_done = 0; + + return 0; +} + +static bool cadence_is_ready_to_process(struct processing_module *mod, + struct sof_source **sources, int num_of_sources, + struct sof_sink **sinks, int num_of_sinks) +{ + struct module_data *codec = &mod->priv; + + if (source_get_data_available(sources[0]) < codec->mpd.in_buff_size || + sink_get_free_size(sinks[0]) < codec->mpd.out_buff_size) + return false; + + return true; +} + +static const struct module_interface cadence_codec_interface = { + .init = cadence_codec_init, + .prepare = cadence_codec_prepare, + .process = cadence_codec_process, + .set_configuration = cadence_codec_set_configuration, + .reset = cadence_codec_reset, + .free = cadence_codec_free, + .is_ready_to_process = cadence_is_ready_to_process, +}; + +DECLARE_MODULE_ADAPTER(cadence_codec_interface, cadence_codec_uuid, cadence_codec_tr); +SOF_MODULE_INIT(cadence_codec, sys_comp_module_cadence_codec_interface_init); diff --git a/src/audio/module_adapter/module/dolby/dax.c b/src/audio/module_adapter/module/dolby/dax.c new file mode 100644 index 000000000000..984bd2c11680 --- /dev/null +++ b/src/audio/module_adapter/module/dolby/dax.c @@ -0,0 +1,976 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY THIS LICENSE +// +// Copyright(c) 2025 Dolby Laboratories. All rights reserved. +// +// Author: Jun Lai <jun.lai@dolby.com> +// + +#include <rtos/atomic.h> +#include <rtos/init.h> +#include <sof/audio/data_blob.h> +#include <sof/audio/module_adapter/module/generic.h> +#include <sof/compiler_attributes.h> + +#include "dax.h" + +LOG_MODULE_REGISTER(dolby_dax_audio_processing, CONFIG_SOF_LOG_LEVEL); +SOF_DEFINE_REG_UUID(dolby_dax_audio_processing); + +#define MAX_PARAMS_STR_BUFFER_SIZE 1536 +#define DAX_ENABLE_MASK 0x1 +#define DAX_PROFILE_MASK 0x2 +#define DAX_DEVICE_MASK 0x4 +#define DAX_CP_MASK 0x8 +#define DAX_VOLUME_MASK 0x10 +#define DAX_CTC_MASK 0x20 +#define DAX_PROCESSING_MASK 0x10000 +#define DAX_RESET_MASK 0x20000 +#define DAX_FREE_MASK 0x40000 + +#define DAX_SWITCH_ENABLE_CONTROL_ID 0 +#define DAX_SWITCH_CP_CONTROL_ID 1 +#define DAX_SWITCH_CTC_CONTROL_ID 2 +#define DAX_ENUM_PROFILE_CONTROL_ID 0 +#define DAX_ENUM_DEVICE_CONTROL_ID 1 + +enum dax_flag_opt_mode { + DAX_FLAG_READ = 0, + DAX_FLAG_SET, + DAX_FLAG_CLEAR, + DAX_FLAG_READ_AND_CLEAR, +}; + +static int32_t flag_process(struct dax_adapter_data *adapter_data, + uint32_t flag, + enum dax_flag_opt_mode opt_mode) +{ +#ifdef __ZEPHYR__ + int32_t bit = ffs(flag) - 1; + + switch (opt_mode) { + case DAX_FLAG_READ: + return atomic_test_bit(&adapter_data->proc_flags, bit); + case DAX_FLAG_SET: + atomic_set_bit(&adapter_data->proc_flags, bit); + break; + case DAX_FLAG_CLEAR: + atomic_clear_bit(&adapter_data->proc_flags, bit); + break; + case DAX_FLAG_READ_AND_CLEAR: + return atomic_test_and_clear_bit(&adapter_data->proc_flags, bit); + default: + break; + } +#else + /* Non-Zephyr builds run single-threaded (no DP mode), there is no synchronous problem */ + int32_t old_flags = atomic_read(&adapter_data->proc_flags); + + switch (opt_mode) { + case DAX_FLAG_READ: + return (old_flags & flag) != 0; + case DAX_FLAG_SET: + atomic_set(&adapter_data->proc_flags, old_flags | flag); + break; + case DAX_FLAG_CLEAR: + atomic_set(&adapter_data->proc_flags, old_flags & ~flag); + break; + case DAX_FLAG_READ_AND_CLEAR: + atomic_set(&adapter_data->proc_flags, old_flags & ~flag); + return (old_flags & flag) != 0; + default: + break; + } +#endif + return 0; +} + +static int itostr(int num, char *str) +{ + int index = 0, digit_count = 0; + int temp; + + if (num < 0) { + str[0] = '-'; + index = 1; + num = -num; + } + + if (num == 0) { + str[index] = '0'; + str[index + 1] = '\0'; + return index + 1; + } + + temp = num; + while (temp > 0) { + temp /= 10; + digit_count++; + } + + temp = index + digit_count - 1; + while (num > 0) { + str[temp] = (num % 10) + '0'; + num /= 10; + temp--; + } + + str[index + digit_count] = '\0'; + return index + digit_count; +} + +static const char *get_params_str(const void *val, uint32_t val_sz) +{ + static char params_str[MAX_PARAMS_STR_BUFFER_SIZE + 16]; + const int32_t *param_val = (const int32_t *)val; + const uint32_t param_sz = val_sz >> 2; + uint32_t offset = 0; + + for (uint32_t i = 0; i < param_sz && offset < MAX_PARAMS_STR_BUFFER_SIZE; i++) { + offset += itostr(param_val[i], params_str + offset); + params_str[offset] = ','; + offset++; + params_str[offset] = '\0'; + } + return ¶ms_str[0]; +} + +static int sof_to_dax_frame_fmt(enum sof_ipc_frame sof_frame_fmt) +{ + switch (sof_frame_fmt) { + case SOF_IPC_FRAME_S16_LE: + return DAX_FMT_SHORT_16; + case SOF_IPC_FRAME_S32_LE: + return DAX_FMT_INT; + case SOF_IPC_FRAME_FLOAT: + return DAX_FMT_FLOAT; + default: + return DAX_FMT_UNSUPPORTED; + } +} + +static int sof_to_dax_sample_rate(uint32_t rate) +{ + switch (rate) { + case 48000: + return rate; + default: + return DAX_RATE_UNSUPPORTED; + } +} + +static int sof_to_dax_channels(uint32_t channels) +{ + switch (channels) { + case 2: + case 6 /* 5.1 */: + case 8 /* 7.1 */: + return channels; + default: + return DAX_CHANNLES_UNSUPPORTED; + } +} + +static int sof_to_dax_buffer_layout(enum sof_ipc_buffer_format sof_buf_fmt) +{ + switch (sof_buf_fmt) { + case SOF_IPC_BUFFER_INTERLEAVED: + return DAX_BUFFER_LAYOUT_INTERLEAVED; + case SOF_IPC_BUFFER_NONINTERLEAVED: + return DAX_BUFFER_LAYOUT_NONINTERLEAVED; + default: + return DAX_BUFFER_LAYOUT_UNSUPPORTED; + } +} + +void dax_buffer_release(struct processing_module *mod, struct dax_buffer *dax_buff) +{ + if (dax_buff->addr) { + mod_free(mod, dax_buff->addr); + dax_buff->addr = NULL; + } + dax_buff->size = 0; + dax_buff->avail = 0; + dax_buff->free = 0; +} + +int dax_buffer_alloc(struct processing_module *mod, + struct dax_buffer *dax_buff, uint32_t bytes) +{ + dax_buffer_release(mod, dax_buff); + dax_buff->addr = mod_balloc(mod, bytes); + if (!dax_buff->addr) + dax_buff->addr = mod_zalloc(mod, bytes); + if (!dax_buff->addr) + return -ENOMEM; + + dax_buff->size = bytes; + dax_buff->avail = 0; + dax_buff->free = bytes; + return 0; +} + +/* After reading from buffer */ +static void dax_buffer_consume(struct dax_buffer *dax_buff, uint32_t bytes) +{ + uint8_t *buf = (uint8_t *)dax_buff->addr; + uint32_t copy_bytes; + + bytes = MIN(bytes, dax_buff->avail); + copy_bytes = dax_buff->avail - bytes; + for (int i = 0; i < copy_bytes; i++) + buf[i] = buf[bytes + i]; + dax_buff->avail = copy_bytes; + dax_buff->free = dax_buff->size - dax_buff->avail; +} + +/* After writing to buffer */ +static void dax_buffer_produce(struct dax_buffer *dax_buff, uint32_t bytes) +{ + dax_buff->avail += bytes; + dax_buff->avail = MIN(dax_buff->avail, dax_buff->size); + dax_buff->free = dax_buff->size - dax_buff->avail; +} + +static bool is_enabled(struct processing_module *mod) +{ + struct dax_adapter_data *adapter_data = module_get_private_data(mod); + struct sof_dax *dax_ctx = &adapter_data->dax_ctx; + + return dax_ctx->enable && dax_ctx->p_dax; +} + +static int set_tuning_file(struct processing_module *mod, void *value, uint32_t size) +{ + int ret = 0; + struct comp_dev *dev = mod->dev; + struct dax_adapter_data *adapter_data = module_get_private_data(mod); + struct sof_dax *dax_ctx = &adapter_data->dax_ctx; + + if (dax_buffer_alloc(mod, &dax_ctx->tuning_file_buffer, size) != 0) { + comp_err(dev, "allocate %u bytes failed for tuning file", size); + ret = -ENOMEM; + } else { + memcpy_s(dax_ctx->tuning_file_buffer.addr, + dax_ctx->tuning_file_buffer.free, + value, + size); + } + + comp_info(dev, "allocated: tuning %u, ret %d", dax_ctx->tuning_file_buffer.size, ret); + return ret; +} + +static int set_enable(struct processing_module *mod, int32_t enable) +{ + int ret; + struct dax_adapter_data *adapter_data = module_get_private_data(mod); + struct sof_dax *dax_ctx = &adapter_data->dax_ctx; + + ret = dax_set_enable(enable, dax_ctx); + comp_info(mod->dev, "set dax enable %d, ret %d", enable, ret); + return ret; +} + +static int set_volume(struct processing_module *mod, int32_t abs_volume) +{ + int ret; + struct dax_adapter_data *adapter_data = module_get_private_data(mod); + struct sof_dax *dax_ctx = &adapter_data->dax_ctx; + + ret = dax_set_volume(abs_volume, dax_ctx); + comp_info(mod->dev, "set volume %d, ret %d", abs_volume, ret); + return ret; +} + +static int set_device(struct processing_module *mod, int32_t out_device) +{ + int ret; + struct dax_adapter_data *adapter_data = module_get_private_data(mod); + struct sof_dax *dax_ctx = &adapter_data->dax_ctx; + + ret = dax_set_device(out_device, dax_ctx); + comp_info(mod->dev, "set device %d, ret %d", out_device, ret); + return ret; +} + +static int set_crosstalk_cancellation_enable(struct processing_module *mod, int32_t enable) +{ + int ret; + struct dax_adapter_data *adapter_data = module_get_private_data(mod); + struct sof_dax *dax_ctx = &adapter_data->dax_ctx; + + ret = dax_set_ctc_enable(enable, dax_ctx); + comp_info(mod->dev, "set ctc enable %d, ret %d", enable, ret); + return ret; +} + +static int update_params_from_buffer(struct processing_module *mod, void *params, uint32_t size); + +static int set_profile(struct processing_module *mod, int32_t profile_id) +{ + int ret = -EINVAL; + struct comp_dev *dev = mod->dev; + struct dax_adapter_data *adapter_data = module_get_private_data(mod); + struct sof_dax *dax_ctx = &adapter_data->dax_ctx; + uint32_t params_sz = 0; + void *params; + + params = dax_find_params(DAX_PARAM_ID_PROFILE, profile_id, ¶ms_sz, dax_ctx); + if (params) + ret = update_params_from_buffer(mod, params, params_sz); + comp_info(dev, "switched to profile %d, ret %d", profile_id, ret); + return ret; +} + +static int set_tuning_device(struct processing_module *mod, int32_t tuning_device) +{ + int ret = -EINVAL; + struct comp_dev *dev = mod->dev; + struct dax_adapter_data *adapter_data = module_get_private_data(mod); + struct sof_dax *dax_ctx = &adapter_data->dax_ctx; + uint32_t params_sz = 0; + void *params; + + params = dax_find_params(DAX_PARAM_ID_TUNING_DEVICE, tuning_device, ¶ms_sz, dax_ctx); + if (params) + ret = update_params_from_buffer(mod, params, params_sz); + comp_info(dev, "switched to tuning device %d, ret %d", tuning_device, ret); + return ret; +} + +static int set_content_processing_enable(struct processing_module *mod, int32_t enable) +{ + int ret = -EINVAL; + struct comp_dev *dev = mod->dev; + struct dax_adapter_data *adapter_data = module_get_private_data(mod); + struct sof_dax *dax_ctx = &adapter_data->dax_ctx; + uint32_t params_sz = 0; + void *params; + + params = dax_find_params(DAX_PARAM_ID_CP_ENABLE, enable, ¶ms_sz, dax_ctx); + if (params) + ret = update_params_from_buffer(mod, params, params_sz); + comp_info(dev, "set content processing enable %d, ret %d", enable, ret); + return ret; +} + +static int dax_set_param_wrapper(struct processing_module *mod, + uint32_t id, void *value, uint32_t size) +{ + int ret = 0; + struct comp_dev *dev = mod->dev; + struct dax_adapter_data *adapter_data = module_get_private_data(mod); + struct sof_dax *dax_ctx = &adapter_data->dax_ctx; + int32_t tmp_val; + + switch (id) { + case DAX_PARAM_ID_TUNING_FILE: + set_tuning_file(mod, value, size); + break; + case DAX_PARAM_ID_ENABLE: + tmp_val = *((int32_t *)value); + tmp_val = !!tmp_val; + if (dax_ctx->enable != tmp_val) { + dax_ctx->enable = tmp_val; + flag_process(adapter_data, DAX_ENABLE_MASK, DAX_FLAG_SET); + } + break; + case DAX_PARAM_ID_ABSOLUTE_VOLUME: + dax_ctx->volume = *((int32_t *)value); + flag_process(adapter_data, DAX_VOLUME_MASK, DAX_FLAG_SET); + break; + case DAX_PARAM_ID_OUT_DEVICE: + tmp_val = *((int32_t *)value); + if (dax_ctx->out_device != tmp_val) { + dax_ctx->out_device = tmp_val; + flag_process(adapter_data, DAX_DEVICE_MASK, DAX_FLAG_SET); + } + break; + case DAX_PARAM_ID_PROFILE: + tmp_val = *((int32_t *)value); + if (dax_ctx->profile != tmp_val) { + dax_ctx->profile = tmp_val; + flag_process(adapter_data, DAX_PROFILE_MASK, DAX_FLAG_SET); + } + break; + case DAX_PARAM_ID_CP_ENABLE: + tmp_val = *((int32_t *)value); + tmp_val = !!tmp_val; + if (dax_ctx->content_processing_enable != tmp_val) { + dax_ctx->content_processing_enable = tmp_val; + flag_process(adapter_data, DAX_CP_MASK, DAX_FLAG_SET); + } + break; + case DAX_PARAM_ID_CTC_ENABLE: + tmp_val = *((int32_t *)value); + tmp_val = !!tmp_val; + if (dax_ctx->ctc_enable != tmp_val) { + dax_ctx->ctc_enable = tmp_val; + flag_process(adapter_data, DAX_CTC_MASK, DAX_FLAG_SET); + } + break; + case DAX_PARAM_ID_ENDPOINT: + if (dax_ctx->endpoint == *((int32_t *)value)) { + ret = update_params_from_buffer(mod, (uint8_t *)value + 4, size - 4); + comp_info(dev, "switched to endpoint %d, ret %d", dax_ctx->endpoint, ret); + } + break; + default: + ret = dax_set_param(id, (void *)(value), size, dax_ctx); + comp_info(dev, "dax_set_param: ret %d, id %#x, size %u, value %s", + ret, id, size >> 2, get_params_str(value, size)); + break; + } + + return ret; +} + +static int update_params_from_buffer(struct processing_module *mod, void *data, uint32_t data_size) +{ + struct comp_dev *dev = mod->dev; + struct module_param *param; + void *pos = data; + const uint32_t param_header_size = 8; + uint32_t param_data_size; + + for (uint32_t i = 0; i < data_size;) { + param = (struct module_param *)(pos); + if (param->size < param_header_size || + param->size > data_size - i || + (param->size & 0x03) != 0) { + comp_err(dev, "invalid param %#x, param size %u, pos %u", + param->id, param->size, i); + return -EINVAL; + } + + if (param->size > param_header_size) { + param_data_size = param->size - param_header_size; + dax_set_param_wrapper(mod, param->id, (void *)(param->data), + param_data_size); + } + + pos = (void *)((uint8_t *)(pos) + param->size); + i += param->size; + } + + return 0; +} + +static void check_and_update_settings(struct processing_module *mod) +{ + int ret; + struct dax_adapter_data *adapter_data = module_get_private_data(mod); + struct sof_dax *dax_ctx = &adapter_data->dax_ctx; + + /* ret equals to 0 mean a new creation or destruction of dax instance */ + ret = dax_check_and_update_instance(mod); + if (ret == 0) { + if (is_enabled(mod) /* A new creation */) { + /* set DAX_ENABLE_MASK bit to trigger the fully update of kcontrol values */ + flag_process(adapter_data, DAX_ENABLE_MASK, DAX_FLAG_SET); + } else if (!dax_ctx->p_dax /* A new destruction */) { + set_enable(mod, 0); + comp_info(mod->dev, "falling back to pass-through mode."); + } + } + + if (flag_process(adapter_data, DAX_ENABLE_MASK, DAX_FLAG_READ_AND_CLEAR)) { + set_enable(mod, dax_ctx->enable); + if (is_enabled(mod)) { + flag_process(adapter_data, DAX_DEVICE_MASK, DAX_FLAG_SET); + flag_process(adapter_data, DAX_VOLUME_MASK, DAX_FLAG_SET); + } + return; + } + + if (!is_enabled(mod)) + return; + + if (flag_process(adapter_data, DAX_DEVICE_MASK, DAX_FLAG_READ_AND_CLEAR)) { + set_device(mod, dax_ctx->out_device); + set_tuning_device(mod, dax_ctx->tuning_device); + flag_process(adapter_data, DAX_PROFILE_MASK, DAX_FLAG_SET); + return; + } + if (flag_process(adapter_data, DAX_CTC_MASK, DAX_FLAG_READ_AND_CLEAR)) { + set_crosstalk_cancellation_enable(mod, dax_ctx->ctc_enable); + flag_process(adapter_data, DAX_PROFILE_MASK, DAX_FLAG_SET); + return; + } + if (flag_process(adapter_data, DAX_PROFILE_MASK, DAX_FLAG_READ_AND_CLEAR)) { + set_profile(mod, dax_ctx->profile); + if (!dax_ctx->content_processing_enable) + flag_process(adapter_data, DAX_CP_MASK, DAX_FLAG_SET); + return; + } + if (flag_process(adapter_data, DAX_CP_MASK, DAX_FLAG_READ_AND_CLEAR)) { + set_content_processing_enable(mod, dax_ctx->content_processing_enable); + return; + } + if (flag_process(adapter_data, DAX_VOLUME_MASK, DAX_FLAG_READ_AND_CLEAR)) + set_volume(mod, dax_ctx->volume); +} + +static int sof_dax_reset(struct processing_module *mod) +{ + struct dax_adapter_data *adapter_data = module_get_private_data(mod); + struct sof_dax *dax_ctx; + + /* dax instance will be established on prepare(), and destroyed on reset() */ + if (adapter_data) { + dax_ctx = &adapter_data->dax_ctx; + if (flag_process(adapter_data, DAX_PROCESSING_MASK, DAX_FLAG_READ)) { + flag_process(adapter_data, DAX_RESET_MASK, DAX_FLAG_SET); + } else { + dax_unregister_user(mod); + dax_buffer_release(mod, &dax_ctx->input_buffer); + dax_buffer_release(mod, &dax_ctx->output_buffer); + } + } + + return 0; +} + +static int sof_dax_free(struct processing_module *mod) +{ + struct dax_adapter_data *adapter_data = module_get_private_data(mod); + struct sof_dax *dax_ctx; + + if (adapter_data) { + dax_ctx = &adapter_data->dax_ctx; + if (flag_process(adapter_data, DAX_PROCESSING_MASK, DAX_FLAG_READ)) { + flag_process(adapter_data, DAX_FREE_MASK, DAX_FLAG_SET); + } else { + sof_dax_reset(mod); + dax_buffer_release(mod, &dax_ctx->tuning_file_buffer); + mod_data_blob_handler_free(mod, dax_ctx->blob_handler); + dax_ctx->blob_handler = NULL; + mod_free(mod, adapter_data); + module_set_private_data(mod, NULL); + } + } + return 0; +} + +static void check_and_update_state(struct processing_module *mod) +{ + struct dax_adapter_data *adapter_data = module_get_private_data(mod); + + if (!adapter_data) + return; + + if (flag_process(adapter_data, DAX_FREE_MASK, DAX_FLAG_READ_AND_CLEAR)) + sof_dax_free(mod); + else if (flag_process(adapter_data, DAX_RESET_MASK, DAX_FLAG_READ_AND_CLEAR)) + sof_dax_reset(mod); +} + +static int sof_dax_init(struct processing_module *mod) +{ + struct comp_dev *dev = mod->dev; + struct module_data *md = &mod->priv; + struct dax_adapter_data *adapter_data; + struct sof_dax *dax_ctx; + + md->private = mod_zalloc(mod, sizeof(struct dax_adapter_data)); + if (!md->private) { + comp_err(dev, "failed to allocate %u bytes for initialization", + sizeof(struct sof_dax)); + return -ENOMEM; + } + + adapter_data = module_get_private_data(mod); + adapter_data->comp_id = dev->ipc_config.id; + adapter_data->priority = DAX_USER_PRIORITY_DEFAULT; + dax_ctx = &adapter_data->dax_ctx; + dax_ctx->enable = 0; + dax_ctx->profile = 0; + dax_ctx->out_device = 0; + dax_ctx->ctc_enable = 1; + dax_ctx->content_processing_enable = 1; + dax_ctx->volume = 1 << 23; + dax_ctx->update_flags = 0; + + dax_ctx->blob_handler = mod_data_blob_handler_new(mod); + if (!dax_ctx->blob_handler) { + comp_err(dev, "create blob handler failed"); + mod_free(mod, adapter_data); + module_set_private_data(mod, NULL); + return -ENOMEM; + } + + dax_instance_manager_init(); + + return 0; +} + +static int check_media_format(struct processing_module *mod) +{ + int ret = 0; + struct comp_dev *dev = mod->dev; + struct comp_buffer *source = comp_dev_get_first_data_producer(dev); + struct comp_buffer *sink = comp_dev_get_first_data_consumer(dev); + const struct audio_stream *src_stream = &source->stream; + const struct audio_stream *sink_stream = &sink->stream; + struct dax_adapter_data *adapter_data = module_get_private_data(mod); + struct sof_dax *dax_ctx = &adapter_data->dax_ctx; + + if (audio_stream_get_frm_fmt(src_stream) != audio_stream_get_frm_fmt(sink_stream) || + sof_to_dax_frame_fmt(audio_stream_get_frm_fmt(src_stream)) == DAX_FMT_UNSUPPORTED) { + comp_err(dev, "unsupported format, source %d, sink %d", + audio_stream_get_frm_fmt(src_stream), + audio_stream_get_frm_fmt(sink_stream)); + ret = -EINVAL; + } + + if (audio_stream_get_rate(src_stream) != audio_stream_get_rate(sink_stream) || + sof_to_dax_sample_rate(audio_stream_get_rate(src_stream)) == DAX_RATE_UNSUPPORTED) { + comp_err(dev, "unsupported sample rate, source %d, sink %d", + audio_stream_get_rate(src_stream), audio_stream_get_rate(sink_stream)); + ret = -EINVAL; + } + + if (audio_stream_get_channels(sink_stream) != 2 || + sof_to_dax_channels(audio_stream_get_channels(src_stream)) == + DAX_CHANNLES_UNSUPPORTED) { + comp_err(dev, "unsupported number of channels, source %d, sink %d", + audio_stream_get_channels(src_stream), + audio_stream_get_channels(sink_stream)); + ret = -EINVAL; + } + + if (audio_stream_get_buffer_fmt(src_stream) != audio_stream_get_buffer_fmt(sink_stream) || + sof_to_dax_buffer_layout(audio_stream_get_buffer_fmt(src_stream)) == + DAX_BUFFER_LAYOUT_UNSUPPORTED) { + comp_err(dev, "unsupported buffer layout %d", + audio_stream_get_buffer_fmt(src_stream)); + ret = -EINVAL; + } + + if (ret != 0) + return ret; + + dax_ctx->input_media_format.data_format = + sof_to_dax_frame_fmt(audio_stream_get_frm_fmt(src_stream)); + dax_ctx->input_media_format.sampling_rate = + sof_to_dax_sample_rate(audio_stream_get_rate(src_stream)); + dax_ctx->input_media_format.num_channels = + sof_to_dax_channels(audio_stream_get_channels(src_stream)); + dax_ctx->input_media_format.layout = + sof_to_dax_buffer_layout(audio_stream_get_buffer_fmt(src_stream)); + dax_ctx->input_media_format.bytes_per_sample = audio_stream_sample_bytes(src_stream); + + dax_ctx->output_media_format.data_format = + sof_to_dax_frame_fmt(audio_stream_get_frm_fmt(sink_stream)); + dax_ctx->output_media_format.sampling_rate = + sof_to_dax_sample_rate(audio_stream_get_rate(sink_stream)); + dax_ctx->output_media_format.num_channels = + sof_to_dax_channels(audio_stream_get_channels(sink_stream)); + dax_ctx->output_media_format.layout = + sof_to_dax_buffer_layout(audio_stream_get_buffer_fmt(sink_stream)); + dax_ctx->output_media_format.bytes_per_sample = audio_stream_sample_bytes(sink_stream); + + comp_info(dev, "format %d, sample rate %d, channels %d, data format %d", + dax_ctx->input_media_format.data_format, + dax_ctx->input_media_format.sampling_rate, + dax_ctx->input_media_format.num_channels, + dax_ctx->input_media_format.data_format); + return 0; +} + +static int sof_dax_prepare(struct processing_module *mod, struct sof_source **sources, + int num_of_sources, struct sof_sink **sinks, int num_of_sinks) +{ + int ret; + struct comp_dev *dev = mod->dev; + struct dax_adapter_data *adapter_data = module_get_private_data(mod); + struct sof_dax *dax_ctx = &adapter_data->dax_ctx; + uint32_t ibs, obs; + + if (num_of_sources != 1 || num_of_sinks != 1) { + comp_err(dev, "unsupported number of buffers, in %d, out %d", + num_of_sources, num_of_sinks); + return -EINVAL; + } + + ret = check_media_format(mod); + if (ret != 0) + return ret; + + dax_ctx->sof_period_bytes = dev->frames * + dax_ctx->output_media_format.num_channels * + dax_ctx->output_media_format.bytes_per_sample; + dax_ctx->period_bytes = dax_query_period_frames(dax_ctx) * + dax_ctx->output_media_format.num_channels * + dax_ctx->output_media_format.bytes_per_sample; + dax_ctx->period_us = 1000000 * dax_ctx->period_bytes / + (dax_ctx->output_media_format.bytes_per_sample * + dax_ctx->output_media_format.num_channels * + dax_ctx->output_media_format.sampling_rate); + + ibs = (dax_query_period_frames(dax_ctx) + dev->frames) * + dax_ctx->input_media_format.num_channels * + dax_ctx->input_media_format.bytes_per_sample; + obs = dax_ctx->period_bytes + dax_ctx->sof_period_bytes; + if (dax_buffer_alloc(mod, &dax_ctx->input_buffer, ibs) != 0) { + comp_err(dev, "allocate %u bytes failed for input", ibs); + ret = -ENOMEM; + goto err; + } + if (dax_buffer_alloc(mod, &dax_ctx->output_buffer, obs) != 0) { + comp_err(dev, "allocate %u bytes failed for output", obs); + ret = -ENOMEM; + goto err; + } + memset(dax_ctx->output_buffer.addr, 0, dax_ctx->output_buffer.size); + dax_buffer_produce(&dax_ctx->output_buffer, dax_ctx->output_buffer.size); + comp_info(dev, "allocated: ibs %u, obs %u", ibs, obs); + + dax_register_user(mod); + dax_check_and_update_instance(mod); + + return 0; + +err: + dax_buffer_release(mod, &dax_ctx->input_buffer); + dax_buffer_release(mod, &dax_ctx->output_buffer); + return ret; +} + +static int sof_dax_process(struct processing_module *mod, struct sof_source **sources, + int num_of_sources, struct sof_sink **sinks, int num_of_sinks) +{ + struct dax_adapter_data *adapter_data = module_get_private_data(mod); + struct sof_dax *dax_ctx = &adapter_data->dax_ctx; + struct sof_source *source = sources[0]; + struct sof_sink *sink = sinks[0]; + uint8_t *buf, *bufstart, *bufend, *dax_buf; + size_t bufsz; + struct dax_buffer *dax_input_buffer = &dax_ctx->input_buffer; + struct dax_buffer *dax_output_buffer = &dax_ctx->output_buffer; + uint32_t consumed_bytes, processed_bytes, produced_bytes; + + flag_process(adapter_data, DAX_PROCESSING_MASK, DAX_FLAG_SET); + + if (!adapter_data) { + comp_err(mod->dev, "invalid adapter data"); + return -EINVAL; + } + + /* source stream -> internal input buffer */ + consumed_bytes = MIN(source_get_data_available(source), dax_input_buffer->free); + source_get_data(source, consumed_bytes, (void *)&buf, (void *)&bufstart, &bufsz); + bufend = &bufstart[bufsz]; + dax_buf = (uint8_t *)(dax_input_buffer->addr); + cir_buf_copy(buf, bufstart, bufend, + dax_buf + dax_input_buffer->avail, + dax_buf, + dax_buf + dax_input_buffer->size, + consumed_bytes); + dax_buffer_produce(dax_input_buffer, consumed_bytes); + source_release_data(source, consumed_bytes); + + check_and_update_settings(mod); + + /* internal input buffer -> internal output buffer */ + processed_bytes = dax_process(dax_ctx); + dax_buffer_consume(dax_input_buffer, processed_bytes); + dax_buffer_produce(dax_output_buffer, processed_bytes); + + /* internal output buffer -> sink stream */ + produced_bytes = MIN(dax_output_buffer->avail, sink_get_free_size(sink)); + if (produced_bytes > 0) { + sink_get_buffer(sink, produced_bytes, (void *)&buf, (void *)&bufstart, &bufsz); + bufend = &bufstart[bufsz]; + dax_buf = (uint8_t *)(dax_output_buffer->addr); + cir_buf_copy(dax_buf, dax_buf, dax_buf + dax_output_buffer->size, + buf, bufstart, bufend, produced_bytes); + dax_buffer_consume(dax_output_buffer, produced_bytes); + sink_commit_buffer(sink, produced_bytes); + } + flag_process(adapter_data, DAX_PROCESSING_MASK, DAX_FLAG_CLEAR); + check_and_update_state(mod); + + return 0; +} + +static int sof_dax_set_configuration(struct processing_module *mod, uint32_t config_id, + enum module_cfg_fragment_position pos, + uint32_t data_offset_size, const uint8_t *fragment, + size_t fragment_size, + uint8_t *response, size_t response_size) +{ + int ret; + struct comp_dev *dev = mod->dev; + struct dax_adapter_data *adapter_data = module_get_private_data(mod); + struct sof_dax *dax_ctx = &adapter_data->dax_ctx; + int32_t dax_param_id = 0; + int32_t val; + + if (fragment_size == 0) + return 0; + +#if CONFIG_IPC_MAJOR_4 + const struct sof_ipc4_control_msg_payload *ctl = NULL; + + switch (config_id) { + case 0: /* IPC4_VOLUME */ + /* ipc4_peak_volume_config::target_volume */ + val = ((const int32_t *)fragment)[1]; + val = sat_int32(Q_SHIFT_RND((int64_t)val, 31, 23)); + dax_param_id = DAX_PARAM_ID_ABSOLUTE_VOLUME; + break; + case SOF_IPC4_SWITCH_CONTROL_PARAM_ID: + ctl = (const struct sof_ipc4_control_msg_payload *)fragment; + if (ctl->num_elems != 1) + return -EINVAL; + + val = ctl->chanv[0].value; + switch (ctl->id) { + case DAX_SWITCH_ENABLE_CONTROL_ID: + dax_param_id = DAX_PARAM_ID_ENABLE; + break; + case DAX_SWITCH_CP_CONTROL_ID: + dax_param_id = DAX_PARAM_ID_CP_ENABLE; + break; + case DAX_SWITCH_CTC_CONTROL_ID: + dax_param_id = DAX_PARAM_ID_CTC_ENABLE; + break; + default: + comp_err(dev, "unknown switch control %d", ctl->id); + return -EINVAL; + } + break; + case SOF_IPC4_ENUM_CONTROL_PARAM_ID: + ctl = (const struct sof_ipc4_control_msg_payload *)fragment; + if (ctl->num_elems != 1) + return -EINVAL; + + val = ctl->chanv[0].value; + switch (ctl->id) { + case DAX_ENUM_PROFILE_CONTROL_ID: + dax_param_id = DAX_PARAM_ID_PROFILE; + break; + case DAX_ENUM_DEVICE_CONTROL_ID: + dax_param_id = DAX_PARAM_ID_OUT_DEVICE; + break; + default: + comp_err(dev, "unknown enum control %d", ctl->id); + return -EINVAL; + } + break; + default: + break; + } +#else + struct sof_ipc_ctrl_data *ctl = (struct sof_ipc_ctrl_data *)fragment; + + switch (ctl->cmd) { + case SOF_CTRL_CMD_VOLUME: + val = ctl->chanv[0].value; + dax_param_id = DAX_PARAM_ID_ABSOLUTE_VOLUME; + break; + case SOF_CTRL_CMD_SWITCH: + if (ctl->num_elems != 1) + return -EINVAL; + + val = ctl->chanv[0].value; + switch (ctl->index) { + case DAX_SWITCH_ENABLE_CONTROL_ID: + dax_param_id = DAX_PARAM_ID_ENABLE; + break; + case DAX_SWITCH_CP_CONTROL_ID: + dax_param_id = DAX_PARAM_ID_CP_ENABLE; + break; + case DAX_SWITCH_CTC_CONTROL_ID: + dax_param_id = DAX_PARAM_ID_CTC_ENABLE; + break; + default: + comp_err(dev, "unknown switch control %d", ctl->index); + return -EINVAL; + } + break; + case SOF_CTRL_CMD_ENUM: + if (ctl->num_elems != 1) + return -EINVAL; + + val = ctl->chanv[0].value; + switch (ctl->index) { + case DAX_ENUM_PROFILE_CONTROL_ID: + dax_param_id = DAX_PARAM_ID_PROFILE; + break; + case DAX_ENUM_DEVICE_CONTROL_ID: + dax_param_id = DAX_PARAM_ID_OUT_DEVICE; + break; + default: + comp_err(dev, "unknown enum control %d", ctl->index); + return -EINVAL; + } + break; + default: + break; + } +#endif + + if (dax_param_id == 0) { + ret = comp_data_blob_set(dax_ctx->blob_handler, pos, + data_offset_size, fragment, fragment_size); + if (ret == 0 && (pos == MODULE_CFG_FRAGMENT_LAST || + pos == MODULE_CFG_FRAGMENT_SINGLE)) { + void *data = NULL; + size_t data_size = 0; + + data = comp_get_data_blob(dax_ctx->blob_handler, &data_size, NULL); + if (data && data_size > 0) + update_params_from_buffer(mod, data, data_size); + } + } else { + ret = dax_set_param_wrapper(mod, dax_param_id, &val, sizeof(val)); + } + return ret; +} + +static const struct module_interface dolby_dax_audio_processing_interface = { + .init = sof_dax_init, + .prepare = sof_dax_prepare, + .process = sof_dax_process, + .set_configuration = sof_dax_set_configuration, + .reset = sof_dax_reset, + .free = sof_dax_free, +}; + +#if CONFIG_COMP_DOLBY_DAX_AUDIO_PROCESSING_MODULE +/* modular: llext dynamic link */ + +#include <module/module/api_ver.h> +#include <module/module/llext.h> +#include <rimage/sof/user/manifest.h> + +static const struct sof_man_module_manifest main_manifest __section(".module") __used = { + .module = { + .name = "DAX", + .uuid = SOF_REG_UUID(dolby_dax_audio_processing), + .entry_point = (uint32_t)(&dolby_dax_audio_processing_interface), + .instance_max_count = DAX_MAX_INSTANCE, + .type = { + .load_type = SOF_MAN_MOD_TYPE_LLEXT, + .domain_dp = 1, + }, + .affinity_mask = 7, + } +}; + +SOF_LLEXT_BUILDINFO; + +#else + +DECLARE_TR_CTX(dolby_dax_audio_processing_tr, SOF_UUID(dolby_dax_audio_processing_uuid), + LOG_LEVEL_INFO); +DECLARE_MODULE_ADAPTER(dolby_dax_audio_processing_interface, dolby_dax_audio_processing_uuid, + dolby_dax_audio_processing_tr); +SOF_MODULE_INIT(dolby_dax_audio_processing, + sys_comp_module_dolby_dax_audio_processing_interface_init); + +#endif diff --git a/src/audio/module_adapter/module/dolby/dax.h b/src/audio/module_adapter/module/dolby/dax.h new file mode 100644 index 000000000000..da0419fa3766 --- /dev/null +++ b/src/audio/module_adapter/module/dolby/dax.h @@ -0,0 +1,78 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY THIS LICENSE + * + * Copyright(c) 2025 Dolby Laboratories. All rights reserved. + * + * Author: Jun Lai <jun.lai@dolby.com> + */ + +#include <sof/audio/module_adapter/module/generic.h> +#include <dax_inf.h> + +#define DAX_USER_ID_INVALID 0 +#define DAX_MAX_INSTANCE 2 + +enum dax_user_priority { + DAX_USER_PRIORITY_DEFAULT = 0, + DAX_USER_PRIORITY_P0 = 0, + DAX_USER_PRIORITY_P1 = 1, /* Highest priority */ +}; + +struct dax_adapter_data { + struct sof_dax dax_ctx; + atomic_t proc_flags; + uint32_t comp_id; + int32_t priority; +}; + +/** + * @brief Release memory used by a DAX buffer. + * + * @param[in] mod Pointer to the processing module. + * @param[in,out] dax_buff Pointer to the DAX buffer to release. + */ +void dax_buffer_release(struct processing_module *mod, struct dax_buffer *dax_buff); + +/** + * @brief Allocate memory for a DAX buffer. + * + * @param[in] mod Pointer to the processing module. + * @param[in,out] dax_buff Pointer to the DAX buffer to allocate. + * @param[in] bytes Number of bytes to allocate. + * + * @return 0 on success, negative error code on failure. + */ +int dax_buffer_alloc(struct processing_module *mod, struct dax_buffer *dax_buff, uint32_t bytes); + +/** + * @brief Initialize global DAX instance manager state. + */ +void dax_instance_manager_init(void); + +/** + * @brief Register the current module as a DAX instance user. + * + * @param[in] mod Pointer to the processing module. + * + * @return 0 on success, negative error code on failure. + */ +int dax_register_user(struct processing_module *mod); + +/** + * @brief Unregister the current module from DAX instance user management. + * + * @param[in] mod Pointer to the processing module. + * + * @return 0 on success, negative error code on failure. + */ +int dax_unregister_user(struct processing_module *mod); + +/** + * @brief Reconcile DAX instance allocation based on user priority. + * + * @param[in] mod Pointer to the processing module. + * + * @return 0 on success, negative error code on failure. + */ +int dax_check_and_update_instance(struct processing_module *mod); diff --git a/src/audio/module_adapter/module/dolby/dax.toml b/src/audio/module_adapter/module/dolby/dax.toml new file mode 100644 index 000000000000..e57f29c8c792 --- /dev/null +++ b/src/audio/module_adapter/module/dolby/dax.toml @@ -0,0 +1,25 @@ +#ifndef LOAD_TYPE +#define LOAD_TYPE "0" +#endif + + REM # DAX module config + [[module.entry]] + name = "DAX" + uuid = "40F66C8B-5AA5-4345-8919-53EC431AAA98" + affinity_mask = "0x7" + instance_count = "2" + domain_types = "1" + load_type = LOAD_TYPE + module_type = "9" + auto_start = "0" + sched_caps = [1, 0x00008000] + + REM # see struct fw_pin_description + REM # pin = [dir, type, sample rate, size, container, channel-cfg] + pin = [0, 0, 0x400, 0x8, 0x8, 0x4404] + + REM # see struct sof_man_mod_config + REM # mod_cfg [PAR_0 PAR_1 PAR_2 PAR_3 IS_BYTES CPS IBS OBS MOD_FLAGS CPC OBLS] + mod_cfg = [0, 0, 0, 0, 4096, 135000000, 1024, 1024, 0, 675000, 0] + + index = __COUNTER__ diff --git a/src/audio/module_adapter/module/dolby/dax_instance_manager.c b/src/audio/module_adapter/module/dolby/dax_instance_manager.c new file mode 100644 index 000000000000..36ef4f052757 --- /dev/null +++ b/src/audio/module_adapter/module/dolby/dax_instance_manager.c @@ -0,0 +1,297 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY THIS LICENSE +// +// Copyright(c) 2026 Dolby Laboratories. All rights reserved. +// +// Author: Jun Lai <jun.lai@dolby.com> +// + +#include <stdbool.h> + +#include <rtos/spinlock.h> +#include <sof/audio/module_adapter/module/generic.h> +#include <sof/compiler_attributes.h> + +#include "dax.h" + +LOG_MODULE_DECLARE(dolby_dax_audio_processing, CONFIG_SOF_LOG_LEVEL); + +/* A DAX user refers to a DAX component, i.e., the processing_module + * object. When a DAX user intends to process audio data, it first needs + * to create a DAX instance. However, when multiple DAX users coexist + * in the system simultaneously, each DAX user will request its own DAX instance. + * Under limited memory resources, users that make later requests may fail to + * allocate the required memory. To address this, each DAX user is assigned a + * priority level, ensuring that higher-priority users are always granted + * instance resources first, regardless of the order in which the requests + * were made. + * + * The specific priority value will be automatically assigned in the dax_register_user + * function based on the configuration of the DAX component. + */ +struct dax_user { + uint32_t id; /* Component ID */ + enum dax_user_priority priority; + bool allocated; /* Whether the instance memory has been allocated */ +}; + +struct dax_instance_manager { + struct dax_user users[DAX_MAX_INSTANCE]; + + /* The maximum count of DAX instances that can be allocated simultaneously, + * determined by the system memory resource + */ + int32_t available_inst_cnt; + + struct k_spinlock lock; + bool initialized; +}; + +static struct dax_instance_manager inst_mgr; + +static void update_alloc_state_l(struct processing_module *mod) +{ + struct dax_adapter_data *adapter_data = module_get_private_data(mod); + struct sof_dax *dax_ctx = &adapter_data->dax_ctx; + struct dax_user *user = NULL; + + for (int i = 0; i < DAX_MAX_INSTANCE; i++) { + if (inst_mgr.users[i].id == adapter_data->comp_id) { + user = &inst_mgr.users[i]; + break; + } + } + + if (!user) + return; + + if (dax_ctx->p_dax && dax_ctx->persist_buffer.addr && dax_ctx->scratch_buffer.addr) + user->allocated = true; + else + user->allocated = false; +} + +static int destroy_instance(struct processing_module *mod) +{ + struct dax_adapter_data *adapter_data = module_get_private_data(mod); + struct sof_dax *dax_ctx = &adapter_data->dax_ctx; + + if (!dax_ctx->p_dax) + return -EFAULT; + + /* free internal dax instance data and set dax_ctx->p_dax to NULL */ + dax_free(dax_ctx); + dax_buffer_release(mod, &dax_ctx->persist_buffer); + dax_buffer_release(mod, &dax_ctx->scratch_buffer); + comp_info(mod->dev, "freed instance"); + + return 0; +} + +static int establish_instance(struct processing_module *mod) +{ + int ret; + struct comp_dev *dev = mod->dev; + struct dax_adapter_data *adapter_data = module_get_private_data(mod); + struct sof_dax *dax_ctx = &adapter_data->dax_ctx; + uint32_t persist_sz; + uint32_t scratch_sz; + + if (dax_ctx->p_dax && dax_ctx->persist_buffer.addr && dax_ctx->scratch_buffer.addr) + return -EEXIST; + + if (dax_ctx->persist_buffer.addr || dax_ctx->scratch_buffer.addr) + destroy_instance(mod); + + persist_sz = dax_query_persist_memory(dax_ctx); + if (dax_buffer_alloc(mod, &dax_ctx->persist_buffer, persist_sz) != 0) { + comp_err(dev, "allocate %u bytes failed for persist", persist_sz); + ret = -ENOMEM; + goto err; + } + scratch_sz = dax_query_scratch_memory(dax_ctx); + if (dax_buffer_alloc(mod, &dax_ctx->scratch_buffer, scratch_sz) != 0) { + comp_err(dev, "allocate %u bytes failed for scratch", scratch_sz); + ret = -ENOMEM; + goto err; + } + ret = dax_init(dax_ctx); + if (ret != 0) { + comp_err(dev, "dax instance initialization failed, ret %d", ret); + goto err; + } + + comp_info(dev, "allocated: persist %u, scratch %u. version: %s", + persist_sz, scratch_sz, dax_get_version()); + return 0; + +err: + destroy_instance(mod); + return ret; +} + +static bool check_priority_l(struct processing_module *mod) +{ + struct dax_adapter_data *adapter_data = module_get_private_data(mod); + struct dax_user *user = NULL; + int32_t allocated_cnt = 0; + + for (int i = 0; i < DAX_MAX_INSTANCE; i++) { + if (inst_mgr.users[i].id == adapter_data->comp_id) { + user = &inst_mgr.users[i]; + break; + } + } + + /* If the current module (user) is not existing in user manager, + * instance memory allocation is not allowed. + */ + if (!user) + return false; + + for (int i = 0; i < DAX_MAX_INSTANCE; i++) { + if (inst_mgr.users[i].priority > user->priority && !inst_mgr.users[i].allocated) + return false; + if (inst_mgr.users[i].allocated) + allocated_cnt++; + } + + /* Resource is exhausted */ + if (!user->allocated && allocated_cnt >= inst_mgr.available_inst_cnt) + return false; + + return true; +} + +void dax_instance_manager_init(void) +{ + if (!inst_mgr.initialized) { + k_spinlock_init(&inst_mgr.lock); + /* Assume memory source is sufficient for maximum instances at the beginning */ + inst_mgr.available_inst_cnt = DAX_MAX_INSTANCE; + inst_mgr.initialized = true; + } +} + +int dax_register_user(struct processing_module *mod) +{ + struct dax_adapter_data *adapter_data = module_get_private_data(mod); + struct sof_dax *dax_ctx = &adapter_data->dax_ctx; + struct dax_user *user = NULL; + k_spinlock_key_t key; + + key = k_spin_lock(&inst_mgr.lock); + for (int i = 0; i < DAX_MAX_INSTANCE; i++) { + if (!user && inst_mgr.users[i].id == DAX_USER_ID_INVALID) + user = &inst_mgr.users[i]; + + if (inst_mgr.users[i].id == adapter_data->comp_id) { + k_spin_unlock(&inst_mgr.lock, key); + return -EEXIST; + } + } + + if (!user) { + k_spin_unlock(&inst_mgr.lock, key); + return -ENOSPC; + } + + user->id = adapter_data->comp_id; + if (dax_ctx->out_device == DAX_DEVICE_SPEAKER) { + /* If the current component's output device is a speaker, it is assigned + * the highest priority. + */ + user->priority = DAX_USER_PRIORITY_P1; + } else if (dax_ctx->out_device == DAX_DEVICE_HEADPHONE) { + user->priority = DAX_USER_PRIORITY_P0; + } else { + user->priority = DAX_USER_PRIORITY_DEFAULT; + } + adapter_data->priority = user->priority; + + if (dax_ctx->p_dax && dax_ctx->persist_buffer.addr && dax_ctx->scratch_buffer.addr) + user->allocated = true; + else + user->allocated = false; + + k_spin_unlock(&inst_mgr.lock, key); + + comp_info(mod->dev, "added user %#x, priority %d", + adapter_data->comp_id, adapter_data->priority); + return 0; +} + +int dax_unregister_user(struct processing_module *mod) +{ + struct dax_adapter_data *adapter_data = module_get_private_data(mod); + struct dax_user *user = NULL; + k_spinlock_key_t key; + + key = k_spin_lock(&inst_mgr.lock); + for (int i = 0; i < DAX_MAX_INSTANCE; i++) { + if (inst_mgr.users[i].id == adapter_data->comp_id) { + user = &inst_mgr.users[i]; + break; + } + } + + if (!user) { + k_spin_unlock(&inst_mgr.lock, key); + return -EINVAL; + } + k_spin_unlock(&inst_mgr.lock, key); + + destroy_instance(mod); + adapter_data->priority = DAX_USER_PRIORITY_DEFAULT; + + key = k_spin_lock(&inst_mgr.lock); + user->id = DAX_USER_ID_INVALID; + user->priority = DAX_USER_PRIORITY_DEFAULT; + user->allocated = false; + k_spin_unlock(&inst_mgr.lock, key); + + comp_info(mod->dev, "removed user %#x", adapter_data->comp_id); + return 0; +} + +int dax_check_and_update_instance(struct processing_module *mod) +{ + k_spinlock_key_t key; + bool has_priority; + int ret = 0; + + key = k_spin_lock(&inst_mgr.lock); + /* Check whether any user that has higher priority is waiting for allocating instance + * memory (allocated == false), if is, current user should release its instance memory + * to make sure the higher priority user can allocate memory successfully. + */ + has_priority = check_priority_l(mod); + k_spin_unlock(&inst_mgr.lock, key); + + /* This section of logic do not need lock protection because + * it does not involve access to any shared resources. `has_priority` + * may be stale when using in if condition but it is OK, `update_alloc_state_l` + * will synchronize instance state which will be used in the next check phase. + */ + if (has_priority) + ret = establish_instance(mod); + else + ret = destroy_instance(mod); + + key = k_spin_lock(&inst_mgr.lock); + update_alloc_state_l(mod); + + /* Auto detect and update the available memory count */ + if (ret == -ENOMEM) { + inst_mgr.available_inst_cnt = 0; + for (int i = 0; i < DAX_MAX_INSTANCE; i++) { + if (inst_mgr.users[i].id != DAX_USER_ID_INVALID && + inst_mgr.users[i].allocated) { + inst_mgr.available_inst_cnt++; + } + } + } + k_spin_unlock(&inst_mgr.lock, key); + return ret; +} diff --git a/src/audio/module_adapter/module/dolby/dax_mock.c b/src/audio/module_adapter/module/dolby/dax_mock.c new file mode 100644 index 000000000000..c8889699a7e8 --- /dev/null +++ b/src/audio/module_adapter/module/dolby/dax_mock.c @@ -0,0 +1,93 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY THIS LICENSE +// +// Copyright(c) 2025 Dolby Laboratories. All rights reserved. +// +// Author: Jun Lai <jun.lai@dolby.com> +// + +#include <dax_inf.h> +#include <rtos/string.h> + +#define PLACEHOLDER_BUF_SZ 8 + +uint32_t dax_query_persist_memory(struct sof_dax *dax_ctx) +{ + return PLACEHOLDER_BUF_SZ; +} + +uint32_t dax_query_scratch_memory(struct sof_dax *dax_ctx) +{ + return PLACEHOLDER_BUF_SZ; +} + +uint32_t dax_query_period_frames(struct sof_dax *dax_ctx) +{ + return 256; +} + +int dax_free(struct sof_dax *dax_ctx) +{ + return 0; +} + +int dax_init(struct sof_dax *dax_ctx) +{ + return 0; +} + +int dax_process(struct sof_dax *dax_ctx) +{ + uint32_t peroid_bytes = dax_query_period_frames(dax_ctx) * + dax_ctx->input_media_format.num_channels * + dax_ctx->input_media_format.bytes_per_sample; + + if (dax_ctx->input_buffer.avail < peroid_bytes || + dax_ctx->output_buffer.free < peroid_bytes) { + return 0; + } + memcpy_s((uint8_t *)dax_ctx->output_buffer.addr + dax_ctx->output_buffer.avail, + dax_ctx->output_buffer.free, + dax_ctx->input_buffer.addr, + peroid_bytes); + return peroid_bytes; +} + +int dax_set_param(uint32_t id, const void *val, uint32_t val_sz, struct sof_dax *dax_ctx) +{ + return 0; +} + +int dax_set_enable(int32_t enable, struct sof_dax *dax_ctx) +{ + return 0; +} + +int dax_set_volume(int32_t pregain, struct sof_dax *dax_ctx) +{ + return 0; +} + +int dax_set_device(int32_t out_device, struct sof_dax *dax_ctx) +{ + return 0; +} + +int dax_set_ctc_enable(int32_t enable, struct sof_dax *dax_ctx) +{ + return 0; +} + +const char *dax_get_version(void) +{ + return ""; +} + +void *dax_find_params(uint32_t query_id, + int32_t query_val, + uint32_t *query_sz, + struct sof_dax *dax_ctx) +{ + return NULL; +} diff --git a/src/audio/module_adapter/module/dolby/llext-wrap.c b/src/audio/module_adapter/module/dolby/llext-wrap.c new file mode 100644 index 000000000000..30c7be24e22b --- /dev/null +++ b/src/audio/module_adapter/module/dolby/llext-wrap.c @@ -0,0 +1,75 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// Copyright(c) 2025 Intel Corporation. All rights reserved. + +#include <stdint.h> +#include <errno.h> +#include <assert.h> +#include <rtos/symbol.h> + +/* + * Stubs that are needed for linkage of some applications or libraries + * that come from porting userspace code. Anyone porting should + * make sure that any code does not depend on working copies of these + * reentrant functions. We will fail for any caller. + */ + +struct stat; +struct _reent; + +size_t _read_r(struct _reent *ptr, int fd, char *buf, size_t cnt) +{ + errno = -ENOTSUP; + return -ENOTSUP; +} + +size_t _write_r(struct _reent *ptr, int fd, char *buf, size_t cnt) +{ + errno = -ENOTSUP; + return -ENOTSUP; +} + +void *_sbrk_r(struct _reent *ptr, ptrdiff_t incr) +{ + errno = -ENOTSUP; + return NULL; +} + +int _lseek_r(struct _reent *ptr, int fd, int pos, int whence) +{ + errno = -ENOTSUP; + return -ENOTSUP; +} + +int _kill_r(struct _reent *ptr, int pid, int sig) +{ + errno = -ENOTSUP; + return -ENOTSUP; +} + +int _getpid_r(struct _reent *ptr) +{ + errno = -ENOTSUP; + return -ENOTSUP; +} + +int _fstat_r(struct _reent *ptr, int fd, struct stat *pstat) +{ + errno = -ENOTSUP; + return -ENOTSUP; +} + +int _close_r(struct _reent *ptr, int fd) +{ + errno = -ENOTSUP; + return -ENOTSUP; +} + +void _exit(int status) +{ + assert(0); + while (1) { + /* spin forever */ + } + /* NOTREACHED */ +} diff --git a/src/audio/module_adapter/module/dolby/llext/CMakeLists.txt b/src/audio/module_adapter/module/dolby/llext/CMakeLists.txt new file mode 100644 index 000000000000..377373ddc5b5 --- /dev/null +++ b/src/audio/module_adapter/module/dolby/llext/CMakeLists.txt @@ -0,0 +1,18 @@ +# Copyright(c) 2025 Dolby Laboratories. +# SPDX-License-Identifier: Apache-2.0 + +if(CONFIG_COMP_DOLBY_DAX_AUDIO_PROCESSING_MOCK) + sof_llext_build("dolby_dax_audio_processing" + SOURCES ../dax.c + ../dax_instance_manager.c + ../dax_mock.c + INCLUDES ${sof_top_dir}/third_party/include + ) +else() + sof_llext_build("dolby_dax_audio_processing" + SOURCES ../dax.c ../dax_instance_manager.c ../llext-wrap.c + INCLUDES ${sof_top_dir}/third_party/include + LIBS_PATH ${sof_top_dir}/third_party/lib/ + LIBS dax m c gcc + ) +endif() diff --git a/src/audio/module_adapter/module/dolby/llext/llext.toml.h b/src/audio/module_adapter/module/dolby/llext/llext.toml.h new file mode 100644 index 000000000000..74f92b85fb81 --- /dev/null +++ b/src/audio/module_adapter/module/dolby/llext/llext.toml.h @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * + * Copyright(c) 2025 Dolby Laboratories. All rights reserved. + */ +#include <tools/rimage/config/platform.toml> +#define LOAD_TYPE "2" +#include "../dax.toml" + +[module] +count = __COUNTER__ diff --git a/src/audio/module_adapter/module/generic.c b/src/audio/module_adapter/module/generic.c index f95299f952d8..2989bceb160b 100644 --- a/src/audio/module_adapter/module/generic.c +++ b/src/audio/module_adapter/module/generic.c @@ -12,15 +12,25 @@ */ #include <rtos/symbol.h> - +#include <sof/compiler_attributes.h> +#include <sof/objpool.h> #include <sof/audio/module_adapter/module/generic.h> #include <sof/audio/data_blob.h> #include <sof/lib/fast-get.h> +#include <sof/lib/vregion.h> +#include <sof/schedule/dp_schedule.h> +#if CONFIG_IPC_MAJOR_4 +#include <ipc4/header.h> +#include <ipc4/module.h> +#include <ipc4/pipeline.h> +#endif /* The __ZEPHYR__ condition is to keep cmocka tests working */ #if CONFIG_MODULE_MEMORY_API_DEBUG && defined(__ZEPHYR__) -#define MEM_API_CHECK_THREAD(res) __ASSERT((res)->rsrc_mngr == k_current_get(), \ - "Module memory API operation from wrong thread") +#define MEM_API_CHECK_THREAD(res) do { \ + if ((res)->rsrc_mngr != k_current_get()) \ + LOG_WRN("mngr %p != cur %p", (res)->rsrc_mngr, k_current_get()); \ +} while (0) #else #define MEM_API_CHECK_THREAD(res) #endif @@ -71,10 +81,21 @@ int module_load_config(struct comp_dev *dev, const void *cfg, size_t size) return ret; } +void mod_resource_init(struct processing_module *mod) +{ + struct module_resources *res = &mod->priv.resources; + + /* Init memory list */ + list_init(&res->objpool.list); + res->objpool.heap = res->alloc->heap; + res->objpool.vreg = res->alloc->vreg; + res->heap_usage = 0; + res->heap_high_water_mark = 0; +} + int module_init(struct processing_module *mod) { int ret; - struct module_data *md = &mod->priv; struct comp_dev *dev = mod->dev; const struct module_interface *const interface = dev->drv->adapter_ops; @@ -99,82 +120,77 @@ int module_init(struct processing_module *mod) return -EIO; } - /* Init memory list */ - list_init(&md->resources.res_list); - list_init(&md->resources.free_cont_list); - list_init(&md->resources.cont_chunk_list); - md->resources.heap_usage = 0; - md->resources.heap_high_water_mark = 0; #if CONFIG_MODULE_MEMORY_API_DEBUG && defined(__ZEPHYR__) - md->resources.rsrc_mngr = k_current_get(); + mod->priv.resources.rsrc_mngr = k_current_get(); #endif /* Now we can proceed with module specific initialization */ - ret = interface->init(mod); +#if CONFIG_SOF_USERSPACE_APPLICATION + if (mod->dev->ipc_config.proc_domain == COMP_PROCESSING_DOMAIN_DP) + ret = scheduler_dp_thread_ipc(mod, SOF_IPC4_MOD_INIT_INSTANCE, NULL); + else +#endif + ret = interface->init(mod); + if (ret) { comp_err(dev, "error %d: module specific init failed", ret); + mod_free_all(mod); return ret; } comp_dbg(dev, "done"); #if CONFIG_IPC_MAJOR_3 - md->state = MODULE_INITIALIZED; + mod->priv.state = MODULE_INITIALIZED; #endif return 0; } -struct container_chunk { - struct list_item chunk_list; - struct module_resource containers[CONFIG_MODULE_MEMORY_API_CONTAINER_CHUNK_SIZE]; -}; - static struct module_resource *container_get(struct processing_module *mod) { - struct module_resources *res = &mod->priv.resources; - struct module_resource *container; - - if (list_is_empty(&res->free_cont_list)) { - struct container_chunk *chunk = rzalloc(SOF_MEM_FLAG_USER, sizeof(*chunk)); - int i; - - if (!chunk) { - comp_err(mod->dev, "allocating more containers failed"); - return NULL; - } - - list_item_append(&chunk->chunk_list, &res->cont_chunk_list); - for (i = 0; i < ARRAY_SIZE(chunk->containers); i++) - list_item_append(&chunk->containers[i].list, &res->free_cont_list); - } - - container = list_first_item(&res->free_cont_list, struct module_resource, list); - list_item_del(&container->list); - return container; + return objpool_alloc(&mod->priv.resources.objpool, sizeof(struct module_resource), 0); } static void container_put(struct processing_module *mod, struct module_resource *container) +{ + objpool_free(&mod->priv.resources.objpool, container); +} + +#if CONFIG_USERSPACE +void mod_heap_info(struct processing_module *mod, size_t *size, uintptr_t *start) { struct module_resources *res = &mod->priv.resources; - list_item_append(&container->list, &res->free_cont_list); + if (res->alloc->vreg) { + vregion_mem_info(res->alloc->vreg, size, start); + } else if (res->alloc->heap) { + if (size) + *size = res->alloc->heap->heap.init_bytes; + + if (start) + *start = (uintptr_t)res->alloc->heap->heap.init_mem; + } } +#endif /** - * Allocates aligned memory block for module. - * @param mod Pointer to the module this memory block is allocatd for. + * Allocates aligned buffer memory block for module. + * @param mod Pointer to the module this memory block is allocated for. * @param bytes Size in bytes. * @param alignment Alignment in bytes. * @return Pointer to the allocated memory or NULL if failed. * - * The allocated memory is automatically freed when the module is unloaded. + * The allocated memory is automatically freed when the module is + * unloaded. The back-end, rballoc(), always aligns the memory to + * PLATFORM_DCACHE_ALIGN at the minimum. */ -void *mod_alloc_align(struct processing_module *mod, uint32_t size, uint32_t alignment) +void *mod_balloc_align(struct processing_module *mod, size_t size, size_t alignment) { - struct module_resource *container = container_get(mod); struct module_resources *res = &mod->priv.resources; - void *ptr; + struct module_resource *container; MEM_API_CHECK_THREAD(res); + + container = container_get(mod); if (!container) return NULL; @@ -184,14 +200,13 @@ void *mod_alloc_align(struct processing_module *mod, uint32_t size, uint32_t ali return NULL; } - /* Allocate memory for module */ - if (alignment) - ptr = rballoc_align(SOF_MEM_FLAG_USER, size, alignment); - else - ptr = rballoc(SOF_MEM_FLAG_USER, size); + /* Allocate buffer memory for module */ + void *ptr = sof_heap_alloc(res->alloc->heap, SOF_MEM_FLAG_USER | SOF_MEM_FLAG_LARGE_BUFFER, + size, alignment); if (!ptr) { - comp_err(mod->dev, "failed to allocate memory."); + comp_err(mod->dev, "Failed to alloc %zu bytes %zu alignment for comp %#x.", + size, alignment, dev_comp_id(mod->dev)); container_put(mod, container); return NULL; } @@ -199,7 +214,6 @@ void *mod_alloc_align(struct processing_module *mod, uint32_t size, uint32_t ali container->ptr = ptr; container->size = size; container->type = MOD_RES_HEAP; - list_item_prepend(&container->list, &res->res_list); res->heap_usage += size; if (res->heap_usage > res->heap_high_water_mark) @@ -207,41 +221,66 @@ void *mod_alloc_align(struct processing_module *mod, uint32_t size, uint32_t ali return ptr; } -EXPORT_SYMBOL(mod_alloc_align); +EXPORT_SYMBOL(mod_balloc_align); /** - * Allocates memory block for module. - * @param mod Pointer to module this memory block is allocated for. - * @param bytes Size in bytes. + * Allocates aligned memory block with flags for module. + * @param mod Pointer to the module this memory block is allocated for. + * @param flags Allocator flags. + * @param bytes Size in bytes. + * @param alignment Alignment in bytes. * @return Pointer to the allocated memory or NULL if failed. * - * Like mod_alloc_align() but the alignment can not be specified. However, - * rballoc() will always aligns the memory to PLATFORM_DCACHE_ALIGN. + * The allocated memory is automatically freed when the module is unloaded. */ -void *mod_alloc(struct processing_module *mod, uint32_t size) +void *z_impl_mod_alloc_ext(struct processing_module *mod, uint32_t flags, size_t size, + size_t alignment) { - return mod_alloc_align(mod, size, 0); -} -EXPORT_SYMBOL(mod_alloc); + struct module_resources *res = &mod->priv.resources; + struct module_resource *container; -/** - * Allocates memory block for module and initializes it to zero. - * @param mod Pointer to module this memory block is allocated for. - * @param bytes Size in bytes. - * @return Pointer to the allocated memory or NULL if failed. - * - * Like mod_alloc() but the allocated memory is initialized to zero. - */ -void *mod_zalloc(struct processing_module *mod, uint32_t size) -{ - void *ret = mod_alloc(mod, size); + MEM_API_CHECK_THREAD(res); - if (ret) - memset(ret, 0, size); + container = container_get(mod); + if (!container) + return NULL; - return ret; + if (!size) { + comp_err(mod->dev, "requested allocation of 0 bytes."); + container_put(mod, container); + return NULL; + } + + /* Allocate memory for module */ + void *ptr; + + if (!res->alloc->vreg) + ptr = sof_heap_alloc(res->alloc->heap, flags, size, alignment); + else if (flags & SOF_MEM_FLAG_COHERENT) + ptr = vregion_alloc_coherent_align(res->alloc->vreg, VREGION_MEM_TYPE_INTERIM, + size, alignment); + else + ptr = vregion_alloc_align(res->alloc->vreg, VREGION_MEM_TYPE_INTERIM, + size, alignment); + + if (!ptr) { + comp_err(mod->dev, "Failed to alloc %zu bytes %zu alignment for comp %#x.", + size, alignment, dev_comp_id(mod->dev)); + container_put(mod, container); + return NULL; + } + /* Store reference to allocated memory */ + container->ptr = ptr; + container->size = size; + container->type = MOD_RES_HEAP; + + res->heap_usage += size; + if (res->heap_usage > res->heap_high_water_mark) + res->heap_high_water_mark = res->heap_usage; + + return ptr; } -EXPORT_SYMBOL(mod_zalloc); +EXPORT_SYMBOL(z_impl_mod_alloc_ext); /** * Creates a blob handler and releases it when the module is unloaded @@ -251,14 +290,15 @@ EXPORT_SYMBOL(mod_zalloc); * Like comp_data_blob_handler_new() but the handler is automatically freed. */ #if CONFIG_COMP_BLOB -struct comp_data_blob_handler * -mod_data_blob_handler_new(struct processing_module *mod) +struct comp_data_blob_handler *mod_data_blob_handler_new(struct processing_module *mod) { - struct module_resources *res = &mod->priv.resources; - struct module_resource *container = container_get(mod); + struct module_resources * __maybe_unused res = &mod->priv.resources; struct comp_data_blob_handler *bhp; + struct module_resource *container; MEM_API_CHECK_THREAD(res); + + container = container_get(mod); if (!container) return NULL; @@ -271,7 +311,6 @@ mod_data_blob_handler_new(struct processing_module *mod) container->bhp = bhp; container->size = 0; container->type = MOD_RES_BLOB_HANDLER; - list_item_prepend(&container->list, &res->res_list); return bhp; } @@ -286,17 +325,20 @@ EXPORT_SYMBOL(mod_data_blob_handler_new); * Like fast_get() but the handler is automatically freed. */ #if CONFIG_FAST_GET -const void *mod_fast_get(struct processing_module *mod, const void * const dram_ptr, size_t size) +const void *z_impl_mod_fast_get(struct processing_module *mod, const void * const dram_ptr, + size_t size) { struct module_resources *res = &mod->priv.resources; - struct module_resource *container = container_get(mod); + struct module_resource *container; const void *ptr; MEM_API_CHECK_THREAD(res); + + container = container_get(mod); if (!container) return NULL; - ptr = fast_get(dram_ptr, size); + ptr = fast_get(res->alloc, dram_ptr, size); if (!ptr) { container_put(mod, container); return NULL; @@ -305,20 +347,25 @@ const void *mod_fast_get(struct processing_module *mod, const void * const dram_ container->sram_ptr = ptr; container->size = 0; container->type = MOD_RES_FAST_GET; - list_item_prepend(&container->list, &res->res_list); return ptr; } -EXPORT_SYMBOL(mod_fast_get); +EXPORT_SYMBOL(z_impl_mod_fast_get); #endif static int free_contents(struct processing_module *mod, struct module_resource *container) { struct module_resources *res = &mod->priv.resources; +#if CONFIG_FAST_GET + struct k_mem_domain *mdom; +#endif switch (container->type) { case MOD_RES_HEAP: - rfree(container->ptr); + if (res->alloc->vreg) + vregion_free(res->alloc->vreg, container->ptr); + else + sof_heap_free(res->alloc->heap, container->ptr); res->heap_usage -= container->size; return 0; #if CONFIG_COMP_BLOB @@ -328,7 +375,12 @@ static int free_contents(struct processing_module *mod, struct module_resource * #endif #if CONFIG_FAST_GET case MOD_RES_FAST_GET: - fast_put(container->sram_ptr); +#if CONFIG_USERSPACE + mdom = mod->mdom; +#else + mdom = NULL; +#endif + fast_put(res->alloc, mdom, container->sram_ptr); return 0; #endif default: @@ -337,38 +389,104 @@ static int free_contents(struct processing_module *mod, struct module_resource * return -EINVAL; } +struct mod_res_cb_arg { + struct processing_module *mod; + const void *ptr; +}; + +static bool mod_res_free(void *data, void *arg) +{ + struct mod_res_cb_arg *cb_arg = arg; + struct module_resource *container = data; + + if (cb_arg->ptr && container->ptr != cb_arg->ptr) + return false; + + int ret = free_contents(cb_arg->mod, container); + + if (ret < 0) + comp_err(cb_arg->mod->dev, "Cannot free allocation %p", cb_arg->ptr); + + container_put(cb_arg->mod, container); + + /* cb_arg->ptr == NULL means, that we're freeing all. Continue iterating */ + return !!cb_arg->ptr; +} + /** * Frees the memory block removes it from module's book keeping. * @param mod Pointer to module this memory block was allocated for. * @param ptr Pointer to the memory block. */ -int mod_free(struct processing_module *mod, const void *ptr) +int z_impl_mod_free(struct processing_module *mod, const void *ptr) { struct module_resources *res = &mod->priv.resources; - struct module_resource *container; - struct list_item *res_list; MEM_API_CHECK_THREAD(res); if (!ptr) return 0; - /* Find which container keeps this memory */ - list_for_item(res_list, &res->res_list) { - container = container_of(res_list, struct module_resource, list); - if (container->ptr == ptr) { - int ret = free_contents(mod, container); + /* Find which container holds this memory */ + struct mod_res_cb_arg cb_arg = {mod, ptr}; + int ret = objpool_iterate(&res->objpool, mod_res_free, &cb_arg); - list_item_del(&container->list); - container_put(mod, container); - return ret; - } - } + if (ret < 0) + comp_err(mod->dev, "error: could not find memory pointed by %p", ptr); - comp_err(mod->dev, "error: could not find memory pointed by %p", ptr); + return ret; +} +EXPORT_SYMBOL(z_impl_mod_free); - return -EINVAL; +#ifdef CONFIG_USERSPACE +#include <zephyr/internal/syscall_handler.h> + +#if CONFIG_FAST_GET +const void *z_vrfy_mod_fast_get(struct processing_module *mod, const void * const dram_ptr, + size_t size) +{ + size_t h_size = 0; + uintptr_t h_start; + + K_OOPS(K_SYSCALL_MEMORY_WRITE(mod, sizeof(*mod))); + mod_heap_info(mod, &h_size, &h_start); + if (h_size) + K_OOPS(K_SYSCALL_MEMORY_WRITE(h_start, h_size)); + K_OOPS(K_SYSCALL_MEMORY_READ(dram_ptr, size)); + + return z_impl_mod_fast_get(mod, dram_ptr, size); +} +#include <zephyr/syscalls/mod_fast_get_mrsh.c> +#endif + +void *z_vrfy_mod_alloc_ext(struct processing_module *mod, uint32_t flags, size_t size, + size_t alignment) +{ + size_t h_size = 0; + uintptr_t h_start; + + K_OOPS(K_SYSCALL_MEMORY_WRITE(mod, sizeof(*mod))); + mod_heap_info(mod, &h_size, &h_start); + if (h_size) + K_OOPS(K_SYSCALL_MEMORY_WRITE(h_start, h_size)); + + return z_impl_mod_alloc_ext(mod, flags, size, alignment); } -EXPORT_SYMBOL(mod_free); +#include <zephyr/syscalls/mod_alloc_ext_mrsh.c> + +int z_vrfy_mod_free(struct processing_module *mod, const void *ptr) +{ + size_t h_size = 0; + uintptr_t h_start; + + K_OOPS(K_SYSCALL_MEMORY_WRITE(mod, sizeof(*mod))); + mod_heap_info(mod, &h_size, &h_start); + if (h_size) + K_OOPS(K_SYSCALL_MEMORY_WRITE(h_start, h_size)); + + return z_impl_mod_free(mod, ptr); +} +#include <zephyr/syscalls/mod_free_mrsh.c> +#endif #if CONFIG_COMP_BLOB void mod_data_blob_handler_free(struct processing_module *mod, struct comp_data_blob_handler *dbh) @@ -403,7 +521,24 @@ int module_prepare(struct processing_module *mod, return -EPERM; #endif if (ops->prepare) { - int ret = ops->prepare(mod, sources, num_of_sources, sinks, num_of_sinks); + int ret; + +#if CONFIG_SOF_USERSPACE_APPLICATION + if (dev->ipc_config.proc_domain == COMP_PROCESSING_DOMAIN_DP) { + const union scheduler_dp_thread_ipc_param param = { + .pipeline_state = { + .trigger_cmd = COMP_TRIGGER_PREPARE, + .state = SOF_IPC4_PIPELINE_STATE_RUNNING, + .n_sources = num_of_sources, + .sources = sources, + .n_sinks = num_of_sinks, + .sinks = sinks, + }, + }; + ret = scheduler_dp_thread_ipc(mod, SOF_IPC4_GLB_SET_PIPELINE_STATE, ¶m); + } else +#endif + ret = ops->prepare(mod, sources, num_of_sources, sinks, num_of_sinks); if (ret) { comp_err(dev, "error %d: module specific prepare failed", ret); @@ -522,11 +657,24 @@ int module_reset(struct processing_module *mod) if (md->state < MODULE_IDLE) return 0; #endif + /* cancel task if DP task*/ - if (mod->dev->ipc_config.proc_domain == COMP_PROCESSING_DOMAIN_DP && mod->dev->task) + if (mod->dev->ipc_config.proc_domain == COMP_PROCESSING_DOMAIN_DP && mod->dev->task && + !IS_ENABLED(CONFIG_SOF_USERSPACE_APPLICATION) && + !IS_ENABLED(CONFIG_SOF_USERSPACE_MOD_IPC_BY_DP_THREAD)) schedule_task_cancel(mod->dev->task); + if (ops->reset) { - ret = ops->reset(mod); +#if CONFIG_SOF_USERSPACE_APPLICATION + if (mod->dev->ipc_config.proc_domain == COMP_PROCESSING_DOMAIN_DP) { + const union scheduler_dp_thread_ipc_param param = { + .pipeline_state.trigger_cmd = COMP_TRIGGER_STOP, + }; + ret = scheduler_dp_thread_ipc(mod, SOF_IPC4_GLB_SET_PIPELINE_STATE, ¶m); + } else +#endif + ret = ops->reset(mod); + if (ret) { if (ret != PPL_STATUS_PATH_STOP) comp_err(mod->dev, @@ -559,28 +707,18 @@ int module_reset(struct processing_module *mod) void mod_free_all(struct processing_module *mod) { struct module_resources *res = &mod->priv.resources; - struct list_item *list; - struct list_item *_list; MEM_API_CHECK_THREAD(res); - /* Find which container keeps this memory */ - list_for_item_safe(list, _list, &res->res_list) { - struct module_resource *container = - container_of(list, struct module_resource, list); - free_contents(mod, container); - list_item_del(&container->list); - } + /* Free all contents found in used containers */ + struct mod_res_cb_arg cb_arg = {mod, NULL}; - list_for_item_safe(list, _list, &res->cont_chunk_list) { - struct container_chunk *chunk = - container_of(list, struct container_chunk, chunk_list); + objpool_iterate(&res->objpool, mod_res_free, &cb_arg); + objpool_prune(&res->objpool); - list_item_del(&chunk->chunk_list); - rfree(chunk); - } + /* Make sure resource lists and accounting are reset */ + mod_resource_init(mod); } -EXPORT_SYMBOL(mod_free_all); int module_free(struct processing_module *mod) { @@ -588,7 +726,8 @@ int module_free(struct processing_module *mod) struct module_data *md = &mod->priv; int ret = 0; - if (ops->free) { + if (ops->free && (mod->dev->ipc_config.proc_domain != COMP_PROCESSING_DOMAIN_DP || + !IS_ENABLED(CONFIG_SOF_USERSPACE_APPLICATION))) { ret = ops->free(mod); if (ret) comp_warn(mod->dev, "error: %d", ret); @@ -733,8 +872,17 @@ int module_bind(struct processing_module *mod, struct bind_info *bind_data) if (ret) return ret; - if (ops->bind) - ret = ops->bind(mod, bind_data); + if (ops->bind) { +#if CONFIG_SOF_USERSPACE_APPLICATION + if (mod->dev->ipc_config.proc_domain == COMP_PROCESSING_DOMAIN_DP) { + const union scheduler_dp_thread_ipc_param param = { + .bind_data = bind_data, + }; + ret = scheduler_dp_thread_ipc(mod, SOF_IPC4_MOD_BIND, ¶m); + } else +#endif + ret = ops->bind(mod, bind_data); + } return ret; } @@ -757,8 +905,17 @@ int module_unbind(struct processing_module *mod, struct bind_info *unbind_data) if (ret) return ret; - if (ops->unbind) - ret = ops->unbind(mod, unbind_data); + if (ops->unbind) { +#if CONFIG_SOF_USERSPACE_APPLICATION + if (mod->dev->ipc_config.proc_domain == COMP_PROCESSING_DOMAIN_DP) { + const union scheduler_dp_thread_ipc_param param = { + .bind_data = unbind_data, + }; + ret = scheduler_dp_thread_ipc(mod, SOF_IPC4_MOD_UNBIND, ¶m); + } else +#endif + ret = ops->unbind(mod, unbind_data); + } return ret; } @@ -774,3 +931,26 @@ void module_update_buffer_position(struct input_stream_buffer *input_buffers, output_buffers->size += audio_stream_frame_bytes(sink) * frames; } EXPORT_SYMBOL(module_update_buffer_position); + +uint32_t module_get_deadline(struct processing_module *mod) +{ + uint32_t deadline; + + /* LL modules have no deadline - it is always "now" */ + if (mod->dev->ipc_config.proc_domain == COMP_PROCESSING_DOMAIN_LL) + return 0; + + /* startup condition - set deadline to "unknown" */ + if (mod->dp_startup_delay) + return UINT32_MAX / 2; + + deadline = UINT32_MAX; + /* calculate the shortest LFT for all sinks */ + for (size_t i = 0; i < mod->num_of_sinks; i++) { + uint32_t sink_lft = sink_get_last_feeding_time(mod->sinks[i]); + + deadline = MIN(deadline, sink_lft); + } + + return deadline; +} diff --git a/src/audio/module_adapter/module/modules.c b/src/audio/module_adapter/module/modules.c index 3bb34c449478..589a25e33887 100644 --- a/src/audio/module_adapter/module/modules.c +++ b/src/audio/module_adapter/module/modules.c @@ -7,6 +7,7 @@ #include <sof/audio/module_adapter/module/generic.h> #include <sof/audio/module_adapter/module/modules.h> +#include <rtos/userspace_helper.h> #include <utilities/array.h> #include <iadk_module_adapter.h> #include <sof/lib_manager.h> @@ -42,7 +43,6 @@ LOG_MODULE_REGISTER(sof_modules, CONFIG_SOF_LOG_LEVEL); SOF_DEFINE_REG_UUID(modules); -DECLARE_TR_CTX(intel_codec_tr, SOF_UUID(modules_uuid), LOG_LEVEL_INFO); /** * \brief modules_init. @@ -201,7 +201,7 @@ static int modules_reset(struct processing_module *mod) } /* Processing Module Adapter API*/ -const struct module_interface processing_module_adapter_interface = { +APP_TASK_DATA const struct module_interface processing_module_adapter_interface = { .init = modules_init, .prepare = modules_prepare, .process = modules_process, diff --git a/src/audio/module_adapter/module/passthrough.c b/src/audio/module_adapter/module/passthrough.c index 484e1319ae3a..4ad42277cbe3 100644 --- a/src/audio/module_adapter/module/passthrough.c +++ b/src/audio/module_adapter/module/passthrough.c @@ -12,7 +12,6 @@ LOG_MODULE_REGISTER(passthrough, CONFIG_SOF_LOG_LEVEL); SOF_DEFINE_REG_UUID(passthrough); -DECLARE_TR_CTX(passthrough_tr, SOF_UUID(passthrough_uuid), LOG_LEVEL_INFO); static int passthrough_codec_init(struct processing_module *mod) { @@ -127,5 +126,6 @@ static const struct module_interface passthrough_interface = { .free = passthrough_codec_free }; +DECLARE_TR_CTX(passthrough_tr, SOF_UUID(passthrough_uuid), LOG_LEVEL_INFO); DECLARE_MODULE_ADAPTER(passthrough_interface, passthrough_uuid, passthrough_tr); SOF_MODULE_INIT(passthrough, sys_comp_module_passthrough_interface_init); diff --git a/src/audio/module_adapter/module/waves/waves.c b/src/audio/module_adapter/module/waves/waves.c index 624b53f5f75c..6616527d2308 100644 --- a/src/audio/module_adapter/module/waves/waves.c +++ b/src/audio/module_adapter/module/waves/waves.c @@ -20,7 +20,6 @@ SOF_DEFINE_REG_UUID(waves); -DECLARE_TR_CTX(waves_tr, SOF_UUID(waves_uuid), LOG_LEVEL_INFO); LOG_MODULE_REGISTER(waves, CONFIG_SOF_LOG_LEVEL); struct waves_codec_data { @@ -191,11 +190,11 @@ static int waves_effect_allocate(struct processing_module *mod) struct waves_codec_data *waves_codec = codec->private; MaxxStatus_t status; - comp_dbg(dev, "waves_effect_allocate() start"); + comp_dbg(dev, "start"); status = MaxxEffect_GetEffectSize(&waves_codec->effect_size); if (status) { - comp_err(dev, "waves_effect_allocate() MaxxEffect_GetEffectSize returned %d", + comp_err(dev, "MaxxEffect_GetEffectSize returned %d", status); return -EINVAL; } @@ -204,12 +203,12 @@ static int waves_effect_allocate(struct processing_module *mod) waves_codec->effect_size, 16); if (!waves_codec->effect) { - comp_err(dev, "waves_effect_allocate() failed to allocate %d bytes for effect", + comp_err(dev, "failed to allocate %d bytes for effect", waves_codec->effect_size); return -ENOMEM; } - comp_dbg(dev, "waves_effect_allocate() allocated %d bytes for effect", + comp_dbg(dev, "allocated %d bytes for effect", waves_codec->effect_size); return 0; @@ -224,7 +223,7 @@ static int waves_effect_check(struct comp_dev *dev) const struct audio_stream *snk_fmt = &sink->stream; /* Init sink & source buffers */ - comp_dbg(dev, "waves_effect_check() start"); + comp_dbg(dev, "start"); if (!source || !sink) { comp_err(dev, "no source or sink buffer"); @@ -235,55 +234,55 @@ static int waves_effect_check(struct comp_dev *dev) /* resampling not supported */ if (audio_stream_get_rate(src_fmt) != audio_stream_get_rate(snk_fmt)) { - comp_err(dev, "waves_effect_check() source %d sink %d rate mismatch", + comp_err(dev, "source %d sink %d rate mismatch", audio_stream_get_rate(src_fmt), audio_stream_get_rate(snk_fmt)); return -EINVAL; } /* upmix/downmix not supported */ if (audio_stream_get_channels(src_fmt) != audio_stream_get_channels(snk_fmt)) { - comp_err(dev, "waves_effect_check() source %d sink %d channels mismatch", + comp_err(dev, "source %d sink %d channels mismatch", audio_stream_get_channels(src_fmt), audio_stream_get_channels(snk_fmt)); return -EINVAL; } /* different frame format not supported */ if (audio_stream_get_frm_fmt(src_fmt) != audio_stream_get_frm_fmt(snk_fmt)) { - comp_err(dev, "waves_effect_check() source %d sink %d sample format mismatch", + comp_err(dev, "source %d sink %d sample format mismatch", audio_stream_get_frm_fmt(src_fmt), audio_stream_get_frm_fmt(snk_fmt)); return -EINVAL; } /* different interleaving is not supported */ if (audio_stream_get_buffer_fmt(src_fmt) != audio_stream_get_buffer_fmt(snk_fmt)) { - comp_err(dev, "waves_effect_check() source %d sink %d buffer format mismatch", + comp_err(dev, "source %d sink %d buffer format mismatch", audio_stream_get_buffer_fmt(src_fmt), audio_stream_get_buffer_fmt(snk_fmt)); return -EINVAL; } if (!format_is_supported(audio_stream_get_frm_fmt(src_fmt))) { - comp_err(dev, "waves_effect_check() float samples not supported"); + comp_err(dev, "float samples not supported"); return -EINVAL; } if (!layout_is_supported(audio_stream_get_buffer_fmt(src_fmt))) { - comp_err(dev, "waves_effect_check() non interleaved format not supported"); + comp_err(dev, "non interleaved format not supported"); return -EINVAL; } if (!rate_is_supported(audio_stream_get_rate(src_fmt))) { - comp_err(dev, "waves_effect_check() rate %d not supported", + comp_err(dev, "rate %d not supported", audio_stream_get_rate(src_fmt)); return -EINVAL; } if (audio_stream_get_channels(src_fmt) != 2) { - comp_err(dev, "waves_effect_check() channels %d not supported", + comp_err(dev, "channels %d not supported", audio_stream_get_channels(src_fmt)); return -EINVAL; } - comp_dbg(dev, "waves_effect_check() done"); + comp_dbg(dev, "done"); return 0; } @@ -302,25 +301,25 @@ static int waves_effect_init(struct processing_module *mod) MaxxStreamFormat_t *i_formats[NUM_IO_STREAMS] = { &waves_codec->i_format }; MaxxStreamFormat_t *o_formats[NUM_IO_STREAMS] = { &waves_codec->o_format }; - comp_dbg(dev, "waves_effect_init() start"); + comp_dbg(dev, "start"); sample_format = format_convert_sof_to_me(audio_stream_get_frm_fmt(src_fmt)); if (sample_format < 0) { - comp_err(dev, "waves_effect_init() sof sample format %d not supported", + comp_err(dev, "sof sample format %d not supported", audio_stream_get_frm_fmt(src_fmt)); return -EINVAL; } buffer_format = layout_convert_sof_to_me(audio_stream_get_buffer_fmt(src_fmt)); if (buffer_format < 0) { - comp_err(dev, "waves_effect_init() sof buffer format %d not supported", + comp_err(dev, "sof buffer format %d not supported", audio_stream_get_buffer_fmt(src_fmt)); return -EINVAL; } sample_bytes = sample_format_convert_to_bytes(sample_format); if (sample_bytes < 0) { - comp_err(dev, "waves_effect_init() sample_format %d not supported", + comp_err(dev, "sample_format %d not supported", sample_format); return -EINVAL; } @@ -346,22 +345,22 @@ static int waves_effect_init(struct processing_module *mod) // trace allows printing only up-to 4 words at a time // logging all the information in two calls - comp_info(dev, "waves_effect_init() rate %d, channels %d", waves_codec->i_format.sampleRate, + comp_info(dev, "rate %d, channels %d", waves_codec->i_format.sampleRate, waves_codec->i_format.numChannels); - comp_info(dev, "waves_effect_init() format %d, layout %d, frame %d", + comp_info(dev, "format %d, layout %d, frame %d", waves_codec->i_format.samplesFormat, waves_codec->i_format.samplesLayout, waves_codec->buffer_samples); status = MaxxEffect_Initialize(waves_codec->effect, i_formats, 1, o_formats, 1); if (status) { - comp_err(dev, "waves_effect_init() MaxxEffect_Initialize returned %d", status); + comp_err(dev, "MaxxEffect_Initialize returned %d", status); return -EINVAL; } waves_codec->initialized = true; - comp_dbg(dev, "waves_effect_init() done"); + comp_dbg(dev, "done"); return 0; } @@ -374,11 +373,11 @@ static int waves_effect_buffers(struct processing_module *mod) int ret; void *i_buffer = NULL, *o_buffer = NULL; - comp_dbg(dev, "waves_effect_buffers() start"); + comp_dbg(dev, "start"); i_buffer = mod_alloc_align(mod, waves_codec->buffer_bytes, 16); if (!i_buffer) { - comp_err(dev, "waves_effect_buffers() failed to allocate %d bytes for i_buffer", + comp_err(dev, "failed to allocate %d bytes for i_buffer", waves_codec->buffer_bytes); ret = -ENOMEM; goto err; @@ -386,7 +385,7 @@ static int waves_effect_buffers(struct processing_module *mod) o_buffer = mod_alloc_align(mod, waves_codec->buffer_bytes, 16); if (!o_buffer) { - comp_err(dev, "waves_effect_buffers() failed to allocate %d bytes for o_buffer", + comp_err(dev, "failed to allocate %d bytes for o_buffer", waves_codec->buffer_bytes); ret = -ENOMEM; goto err; @@ -399,10 +398,10 @@ static int waves_effect_buffers(struct processing_module *mod) codec->mpd.out_buff = waves_codec->o_buffer; codec->mpd.out_buff_size = waves_codec->buffer_bytes; - comp_dbg(dev, "waves_effect_buffers() in_buff_size %d, out_buff_size %d", + comp_dbg(dev, "in_buff_size %d, out_buff_size %d", codec->mpd.in_buff_size, codec->mpd.out_buff_size); - comp_dbg(dev, "waves_effect_buffers() done"); + comp_dbg(dev, "done"); return 0; err: @@ -423,12 +422,12 @@ static int waves_effect_revision(struct processing_module *mod) uint32_t revision_len; MaxxStatus_t status; - comp_info(dev, "waves_effect_revision() start"); + comp_info(dev, "start"); status = MaxxEffect_Revision_Get(waves_codec->effect, &revision, &revision_len); if (status) { - comp_err(dev, "waves_effect_revision() MaxxEffect_Revision_Get returned %d", + comp_err(dev, "MaxxEffect_Revision_Get returned %d", status); return -EINVAL; } @@ -458,7 +457,7 @@ static int waves_effect_revision(struct processing_module *mod) } #endif - comp_info(dev, "waves_effect_revision() done"); + comp_info(dev, "done"); return 0; } @@ -470,11 +469,11 @@ static int waves_effect_save_config_blob_to_cache(struct processing_module *mod, struct module_data *codec = &mod->priv; struct waves_codec_data *waves_codec = codec->private; - comp_info(dev, "waves_effect_save_config_blob_to_cache() start"); + comp_info(dev, "start"); /* release old cached config blob*/ if (waves_codec->config_blob && size != waves_codec->config_blob_size) { - comp_info(dev, "waves_effect_save_config_blob_to_cache() release blob"); + comp_info(dev, "release blob"); mod_free(mod, waves_codec->config_blob); waves_codec->config_blob = NULL; waves_codec->config_blob_size = 0; @@ -484,7 +483,7 @@ static int waves_effect_save_config_blob_to_cache(struct processing_module *mod, waves_codec->config_blob = mod_alloc_align(mod, size, 16); if (!waves_codec->config_blob) { comp_err(dev, - "waves_effect_save_config_blob_to_cache() failed to allocate %d bytes for config blob", + "failed to allocate %d bytes for config blob", size); return -ENOMEM; } @@ -503,7 +502,7 @@ static int waves_effect_save_config_blob_to_cache(struct processing_module *mod, return ret; } - comp_dbg(dev, "waves_effect_save_config_blob_to_cache() done"); + comp_dbg(dev, "done"); return 0; } @@ -517,13 +516,13 @@ static int waves_effect_message(struct processing_module *mod, void *data, uint3 uint32_t response_size = 0; if (waves_codec->initialized) { - comp_info(dev, "waves_effect_message() start data %p size %d", data, size); + comp_info(dev, "start data %p size %d", data, size); status = MaxxEffect_Message(waves_codec->effect, data, size, waves_codec->response, &response_size); if (status) { - comp_err(dev, "waves_effect_message() MaxxEffect_Message returned %d", + comp_err(dev, "MaxxEffect_Message returned %d", status); return -EINVAL; } @@ -553,7 +552,7 @@ static int waves_effect_apply_config_blob_from_cache(struct processing_module *m struct module_data *codec = &mod->priv; struct waves_codec_data *waves_codec = codec->private; - comp_info(dev, "waves_effect_apply_config_blob_from_cache()"); + comp_info(dev, "entry"); if (waves_codec->config_blob) { return waves_effect_message(mod, waves_codec->config_blob, @@ -584,24 +583,24 @@ static int waves_effect_apply_config(struct processing_module *mod) uint32_t param_number = 0; int ret = 0; - comp_info(dev, "waves_effect_apply_config() start"); + comp_info(dev, "start"); cfg = &codec->cfg; - comp_info(dev, "waves_effect_apply_config() config %p, size %d, avail %d", + comp_info(dev, "config %p, size %d, avail %d", cfg->data, cfg->size, cfg->avail); if (!cfg->data) { ret = waves_effect_apply_config_blob_from_cache(mod); if (ret) { - comp_err(dev, "waves_effect_apply_config() error %x: apply cache fail", + comp_err(dev, "error %x: apply cache fail", ret); return ret; } } if (cfg->size > MAX_CONFIG_SIZE_BYTES) { - comp_err(dev, "waves_effect_apply_config() provided config is too big, size %d", + comp_err(dev, "provided config is too big, size %d", cfg->size); return -EINVAL; } @@ -616,24 +615,24 @@ static int waves_effect_apply_config(struct processing_module *mod) param = (struct module_param *)((char *)cfg->data + index); param_data_size = param->size - sizeof(param->size) - sizeof(param->id); - comp_info(dev, "waves_effect_apply_config() param num %d id %d size %d", + comp_info(dev, "param num %d id %d size %d", param_number, param->id, param->size); if ((param->size <= header_size) || (param->size > MAX_CONFIG_SIZE_BYTES)) { - comp_err(dev, "waves_effect_apply_config() invalid module_param size: %d", + comp_err(dev, "invalid module_param size: %d", param->size); return -EINVAL; } if ((index + param->size) > cfg->size) { - comp_err(dev, "waves_effect_apply_config() module_param size: %d exceeds cfg buffer size: %d", + comp_err(dev, "module_param size: %d exceeds cfg buffer size: %d", param->size, cfg->size); return -EINVAL; } switch (param->id) { case PARAM_NOP: - comp_info(dev, "waves_effect_apply_config() NOP"); + comp_info(dev, "NOP"); break; case PARAM_MESSAGE: ret = waves_effect_handle_param_message(mod, param->data, param_data_size); @@ -650,11 +649,11 @@ static int waves_effect_apply_config(struct processing_module *mod) } if (ret) { - comp_err(dev, "waves_effect_apply_config() failed %d", ret); + comp_err(dev, "failed %d", ret); return ret; } - comp_dbg(dev, "waves_effect_apply_config() done"); + comp_dbg(dev, "done"); return 0; } @@ -666,11 +665,11 @@ static int waves_codec_init(struct processing_module *mod) int ret = 0; void *response = NULL; - comp_dbg(dev, "waves_codec_init() start"); + comp_dbg(dev, "start"); waves_codec = mod_alloc_align(mod, sizeof(struct waves_codec_data), 16); if (!waves_codec) { - comp_err(dev, "waves_codec_init() failed to allocate %d bytes for waves_codec_data", + comp_err(dev, "failed to allocate %d bytes for waves_codec_data", sizeof(struct waves_codec_data)); ret = -ENOMEM; } else { @@ -682,7 +681,7 @@ static int waves_codec_init(struct processing_module *mod) } if (ret) { - comp_err(dev, "waves_codec_init() failed %d", ret); + comp_err(dev, "failed %d", ret); return ret; } @@ -690,20 +689,20 @@ static int waves_codec_init(struct processing_module *mod) &waves_codec->response_max_bytes); if (ret) { - comp_err(dev, "waves_codec_init() MaxxEffect_GetMessageMaxSize returned %d", ret); + comp_err(dev, "MaxxEffect_GetMessageMaxSize returned %d", ret); return -EINVAL; } response = mod_alloc_align(mod, waves_codec->response_max_bytes, 16); if (!response) { - comp_err(dev, "waves_codec_init() failed to allocate %d bytes for response", + comp_err(dev, "failed to allocate %d bytes for response", waves_codec->response_max_bytes); return -ENOMEM; } waves_codec->response = response; waves_codec->initialized = false; - comp_dbg(dev, "waves_codec_init() done"); + comp_dbg(dev, "done"); return ret; } @@ -714,7 +713,7 @@ static int waves_codec_prepare(struct processing_module *mod, struct comp_dev *dev = mod->dev; int ret; - comp_dbg(dev, "waves_codec_prepare() start"); + comp_dbg(dev, "start"); ret = waves_effect_check(dev); if (ret) @@ -732,11 +731,11 @@ static int waves_codec_prepare(struct processing_module *mod, if (ret) goto error; - comp_dbg(dev, "waves_codec_prepare() done"); + comp_dbg(dev, "done"); return 0; error: - comp_err(dev, "waves_codec_prepare() failed %d", ret); + comp_err(dev, "failed %d", ret); return ret; } @@ -745,7 +744,7 @@ static int waves_codec_init_process(struct processing_module *mod) struct module_data *codec = &mod->priv; struct comp_dev *dev = mod->dev; - comp_dbg(dev, "waves_codec_init_process()"); + comp_dbg(dev, "entry"); codec->mpd.produced = 0; codec->mpd.consumed = 0; @@ -777,7 +776,7 @@ waves_codec_process(struct processing_module *mod, input_buffers[0].data, codec->mpd.in_buff_size); codec->mpd.avail = codec->mpd.in_buff_size; - comp_dbg(dev, "waves_codec_process() start"); + comp_dbg(dev, "start"); MaxxStream_t *i_streams[NUM_IO_STREAMS] = { &waves_codec->i_stream }; MaxxStream_t *o_streams[NUM_IO_STREAMS] = { &waves_codec->o_stream }; @@ -789,7 +788,7 @@ waves_codec_process(struct processing_module *mod, * on the other hand there is available/produced counters in mpd, check them anyways */ if (codec->mpd.avail != waves_codec->buffer_bytes) { - comp_warn(dev, "waves_codec_process() input buffer %d is not full %d", + comp_warn(dev, "input buffer %d is not full %d", codec->mpd.avail, waves_codec->buffer_bytes); num_input_samples = codec->mpd.avail / (waves_codec->sample_size_in_bytes * waves_codec->i_format.numChannels); @@ -807,7 +806,7 @@ waves_codec_process(struct processing_module *mod, status = MaxxEffect_Process(waves_codec->effect, i_streams, o_streams); if (status) { - comp_err(dev, "waves_codec_process() MaxxEffect_Process returned %d", status); + comp_err(dev, "MaxxEffect_Process returned %d", status); ret = -EINVAL; } else { codec->mpd.produced = waves_codec->o_stream.numAvailableSamples * @@ -822,9 +821,9 @@ waves_codec_process(struct processing_module *mod, } if (ret) - comp_err(dev, "waves_codec_process() failed %d", ret); + comp_err(dev, "failed %d", ret); - comp_dbg(dev, "waves_codec_process() done"); + comp_dbg(dev, "done"); return ret; } @@ -836,16 +835,16 @@ static int waves_codec_reset(struct processing_module *mod) struct module_data *codec = &mod->priv; struct waves_codec_data *waves_codec = codec->private; - comp_info(dev, "waves_codec_reset() start"); + comp_info(dev, "start"); status = MaxxEffect_Reset(waves_codec->effect); if (status) { - comp_err(dev, "waves_codec_reset() MaxxEffect_Reset returned %d", status); + comp_err(dev, "MaxxEffect_Reset returned %d", status); ret = -EINVAL; } if (ret) - comp_err(dev, "waves_codec_reset() failed %d", ret); + comp_err(dev, "failed %d", ret); if (codec->mpd.in_buff) mod_free(mod, codec->mpd.in_buff); @@ -854,13 +853,13 @@ static int waves_codec_reset(struct processing_module *mod) mod_free(mod, codec->mpd.out_buff); waves_codec->initialized = false; - comp_dbg(dev, "waves_codec_reset() done"); + comp_dbg(dev, "done"); return ret; } static int waves_codec_free(struct processing_module *mod) { - comp_dbg(mod->dev, "waves_codec_free()"); + comp_dbg(mod->dev, "entry"); return 0; } @@ -920,6 +919,7 @@ SOF_LLEXT_BUILDINFO; #else +DECLARE_TR_CTX(waves_tr, SOF_UUID(waves_uuid), LOG_LEVEL_INFO); DECLARE_MODULE_ADAPTER(waves_interface, waves_uuid, waves_tr); SOF_MODULE_INIT(waves, sys_comp_module_waves_interface_init); diff --git a/src/audio/module_adapter/module_adapter.c b/src/audio/module_adapter/module_adapter.c index 77777727efba..adcb5ae829e0 100644 --- a/src/audio/module_adapter/module_adapter.c +++ b/src/audio/module_adapter/module_adapter.c @@ -18,12 +18,19 @@ #include <sof/audio/source_api.h> #include <sof/audio/audio_buffer.h> #include <sof/audio/pipeline.h> +#include <sof/lib/vregion.h> +#include <sof/schedule/dp_schedule.h> #include <sof/schedule/ll_schedule_domain.h> #include <sof/common.h> #include <sof/platform.h> #include <sof/ut.h> +#if CONFIG_IPC_MAJOR_4 +#include <ipc4/base_fw.h> +#include <ipc4/header.h> +#include <ipc4/module.h> +#endif #include <rtos/interrupt.h> -#include <rtos/symbol.h> +#include <rtos/kernel.h> #include <limits.h> #include <stdint.h> @@ -41,14 +48,155 @@ LOG_MODULE_REGISTER(module_adapter, CONFIG_SOF_LOG_LEVEL); struct comp_dev *module_adapter_new(const struct comp_driver *drv, const struct comp_ipc_config *config, const void *spec) { - return module_adapter_new_ext(drv, config, spec, NULL); + return module_adapter_new_ext(drv, config, spec, NULL, NULL); +} + +#if CONFIG_MM_DRV +#define PAGE_SZ CONFIG_MM_DRV_PAGE_SIZE +#else +#include <sof/platform.h> +#define PAGE_SZ HOST_PAGE_SIZE +#endif + +static struct vregion *module_adapter_dp_heap_new(const struct comp_ipc_config *config, + size_t *heap_size) +{ + /* src-lite with 8 channels has been seen allocating 14k in one go */ + /* FIXME: the size will be derived from configuration */ + const size_t buf_size = 28 * 1024; + + /* + * A 1-to-1 replacement of the original heap implementation would be to + * have "lifetime size" equal to 0. But (1) this is invalid for + * vregion_create() and (2) we gradually move objects, that are simple + * to move to the lifetime buffer. Make it 4k for the beginning. + */ + return vregion_create(4096, buf_size - 4096); +} + +static struct processing_module *module_adapter_mem_alloc(const struct comp_driver *drv, + const struct comp_ipc_config *config) +{ + struct k_heap *mod_heap; + struct vregion *mod_vreg; + struct processing_module *mod; + struct comp_dev *dev; + /* + * For DP shared modules the struct processing_module object must be + * accessible from all cores. Unfortunately at this point there's no + * information of components the module will be bound to. So we need to + * allocate shared memory for each DP module. + * To be removed when pipeline 2.0 is ready. + */ + uint32_t flags = config->proc_domain == COMP_PROCESSING_DOMAIN_DP ? + SOF_MEM_FLAG_USER | SOF_MEM_FLAG_COHERENT : SOF_MEM_FLAG_USER; + size_t heap_size; + + if (config->proc_domain == COMP_PROCESSING_DOMAIN_DP && IS_ENABLED(CONFIG_USERSPACE) && + !IS_ENABLED(CONFIG_SOF_USERSPACE_USE_DRIVER_HEAP)) { + mod_vreg = module_adapter_dp_heap_new(config, &heap_size); + if (!mod_vreg) { + comp_cl_err(drv, "Failed to allocate DP module heap / vregion"); + return NULL; + } + mod_heap = NULL; + } else { + mod_heap = drv->user_heap; + heap_size = 0; + mod_vreg = NULL; + } + + if (!mod_vreg) + mod = sof_heap_alloc(mod_heap, flags, sizeof(*mod), 0); + else if (flags & SOF_MEM_FLAG_COHERENT) + mod = vregion_alloc_coherent(mod_vreg, VREGION_MEM_TYPE_LIFETIME, sizeof(*mod)); + else + mod = vregion_alloc(mod_vreg, VREGION_MEM_TYPE_LIFETIME, sizeof(*mod)); + + if (!mod) { + comp_cl_err(drv, "failed to allocate memory for module"); + goto emod; + } + + struct mod_alloc_ctx *alloc = rmalloc(flags, sizeof(*alloc)); + + if (!alloc) + goto ealloc; + + memset(mod, 0, sizeof(*mod)); + alloc->heap = mod_heap; + alloc->vreg = mod_vreg; + mod->priv.resources.alloc = alloc; + mod_resource_init(mod); + + /* + * Would be difficult to optimize the allocation to use cache. Only if + * the whole currently active topology is running on the primary core, + * then it can be cached. Effectively it can be only cached in + * single-core configurations. + */ + if (mod_vreg) + dev = vregion_alloc_coherent(mod_vreg, VREGION_MEM_TYPE_LIFETIME, sizeof(*dev)); + else + dev = sof_heap_alloc(mod_heap, SOF_MEM_FLAG_COHERENT, sizeof(*dev), 0); + + if (!dev) { + comp_cl_err(drv, "failed to allocate memory for comp_dev"); + goto edev; + } + + memset(dev, 0, sizeof(*dev)); + comp_init(drv, dev, sizeof(*dev)); + dev->ipc_config = *config; + mod->dev = dev; + dev->mod = mod; + + return mod; + +edev: + rfree(alloc); +ealloc: + if (mod_vreg) + vregion_free(mod_vreg, mod); + else + sof_heap_free(mod_heap, mod); +emod: + vregion_put(mod_vreg); + + return NULL; +} + +static void module_adapter_mem_free(struct processing_module *mod) +{ + struct mod_alloc_ctx *alloc = mod->priv.resources.alloc; + struct k_heap *mod_heap = alloc->heap; + + /* + * In principle it shouldn't even be needed to free individual objects + * on the module heap since we're freeing the heap itself too + */ +#if CONFIG_IPC_MAJOR_4 + sof_heap_free(mod_heap, mod->priv.cfg.input_pins); +#endif + if (alloc->vreg) { + struct vregion *mod_vreg = alloc->vreg; + + vregion_free(mod_vreg, mod->dev); + vregion_free(mod_vreg, mod); + if (!vregion_put(mod_vreg)) + rfree(alloc); + } else { + sof_heap_free(mod_heap, mod->dev); + sof_heap_free(mod_heap, mod); + rfree(alloc); + } } /* * \brief Create a module adapter component. * \param[in] drv - component driver pointer. * \param[in] config - component ipc descriptor pointer. - * \param[in] spec - passdowned data from driver. + * \param[in] const_spec - passdowned data from driver. * \param[in] mod_priv - Pointer to private data for processing module. * * \return: a pointer to newly created module adapter component on success. NULL on error. @@ -57,54 +205,68 @@ struct comp_dev *module_adapter_new(const struct comp_driver *drv, * the create method. */ struct comp_dev *module_adapter_new_ext(const struct comp_driver *drv, - const struct comp_ipc_config *config, const void *spec, - void *mod_priv) + const struct comp_ipc_config *config, + const void *const_spec, void *mod_priv, + struct userspace_context *user_ctx) { int ret; - struct comp_dev *dev; - struct processing_module *mod; struct module_config *dst; const struct module_interface *const interface = drv->adapter_ops; - + struct ipc_config_process spec = + *((const struct ipc_config_process *) const_spec); +#if CONFIG_IPC_MAJOR_4 + struct module_ext_init_data ext_data = { 0 }; +#endif comp_cl_dbg(drv, "start"); if (!config) { - comp_cl_err(drv, "wrong input params! drv = %zx config = %zx", - (size_t)drv, (size_t)config); + comp_cl_err(drv, "NULL config! drv = %p", drv); return NULL; } - - dev = comp_alloc(drv, sizeof(*dev)); - if (!dev) { - comp_cl_err(drv, "failed to allocate memory for comp_dev"); - return NULL; +#if CONFIG_IPC_MAJOR_4 + if (config->ipc_extended_init) { + ret = module_ext_init_decode(drv, &ext_data, &spec); + if (ret != 0) + return NULL; } - dev->ipc_config = *config; - - /* allocate module information. - * for DP shared modules this struct must be accessible from all cores - * Unfortunately at this point there's no information of components the module - * will be bound to. So we need to allocate shared memory for each DP module - * To be removed when pipeline 2.0 is ready - */ - int flags = config->proc_domain == COMP_PROCESSING_DOMAIN_DP ? - SOF_MEM_FLAG_USER | SOF_MEM_FLAG_COHERENT : SOF_MEM_FLAG_USER; +#endif - mod = module_driver_heap_rzalloc(drv->user_heap, flags, sizeof(*mod)); - if (!mod) { - comp_err(dev, "failed to allocate memory for module"); - goto err; - } + struct processing_module *mod = module_adapter_mem_alloc(drv, config); - dst = &mod->priv.cfg; + if (!mod) + return NULL; module_set_private_data(mod, mod_priv); - mod->dev = dev; - dev->mod = mod; - list_init(&mod->raw_data_buffers_list); +#if CONFIG_USERSPACE + mod->user_ctx = user_ctx; +#endif /* CONFIG_USERSPACE */ - ret = module_adapter_init_data(dev, dst, config, spec); + struct comp_dev *dev = mod->dev; + +#if CONFIG_ZEPHYR_DP_SCHEDULER + /* create a task for DP processing */ + if (config->proc_domain == COMP_PROCESSING_DOMAIN_DP) { + /* All data allocated, create a thread */ + ret = pipeline_comp_dp_task_init(dev); + if (ret) { + comp_cl_err(drv, "DP task creation failed with error %d.", ret); + goto err; + } + } +#endif /* CONFIG_ZEPHYR_DP_SCHEDULER */ + + dst = &mod->priv.cfg; + /* + * NOTE: dst->ext_data points to stack variable and contains + * pointers to IPC payload mailbox, so its only valid in + * functions that called from this function. This why + * the pointer is set NULL before this function exits. + */ +#if CONFIG_IPC_MAJOR_4 + dst->ext_data = &ext_data; +#endif + ret = module_adapter_init_data(dev, dst, config, &spec); if (ret) { comp_err(dev, "%d: module init data failed", ret); @@ -125,6 +287,22 @@ struct comp_dev *module_adapter_new_ext(const struct comp_driver *drv, else goto err; +#if CONFIG_IPC_MAJOR_4 + struct ipc_comp_dev *ipc_pipe; + struct ipc *ipc = ipc_get(); + + /* set the pipeline pointer if ipc_pipe is valid */ + ipc_pipe = ipc_get_comp_by_ppl_id(ipc, COMP_TYPE_PIPELINE, config->pipeline_id, + IPC_COMP_IGNORE_REMOTE); + if (ipc_pipe) { + dev->pipeline = ipc_pipe->pipeline; + + /* LL modules have the same period as the pipeline */ + if (dev->ipc_config.proc_domain == COMP_PROCESSING_DOMAIN_LL) + dev->period = ipc_pipe->pipeline->period; + } +#endif + /* Init processing module */ ret = module_init(mod); if (ret) { @@ -133,12 +311,6 @@ struct comp_dev *module_adapter_new_ext(const struct comp_driver *drv, goto err; } -#if CONFIG_ZEPHYR_DP_SCHEDULER - /* create a task for DP processing */ - if (config->proc_domain == COMP_PROCESSING_DOMAIN_DP) - pipeline_comp_dp_task_init(dev); -#endif /* CONFIG_ZEPHYR_DP_SCHEDULER */ - module_adapter_reset_data(dst); dev->state = COMP_STATE_READY; @@ -155,20 +327,25 @@ struct comp_dev *module_adapter_new_ext(const struct comp_driver *drv, comp_err(dev, "%d: module params failed", ret); goto err; } + + /* set component period frames */ + component_set_nearest_period_frames(dev, params.rate); #endif +#if CONFIG_IPC_MAJOR_4 + dst->ext_data = NULL; +#endif comp_dbg(dev, "done"); return dev; + err: -#if CONFIG_IPC_MAJOR_4 - if (mod) - rfree(mod->priv.cfg.input_pins); +#if CONFIG_ZEPHYR_DP_SCHEDULER + if (dev->task) + schedule_task_free(dev->task); #endif - rfree(mod); - rfree(dev); + module_adapter_mem_free(mod); return NULL; } -EXPORT_SYMBOL(module_adapter_new); #if CONFIG_ZEPHYR_DP_SCHEDULER static void module_adapter_calculate_dp_period(struct comp_dev *dev) @@ -215,11 +392,7 @@ int module_adapter_prepare(struct comp_dev *dev) comp_dbg(dev, "start"); #if CONFIG_IPC_MAJOR_4 - /* - * if the stream_params are valid, just update the sink/source buffer params. If not, - * retrieve the params from the basecfg, allocate stream_params and then update the - * sink/source buffer params. - */ + /* allocate stream_params and retrieve the params from the basecfg if needed */ if (!mod->stream_params) { struct sof_ipc_stream_params params; @@ -228,12 +401,6 @@ int module_adapter_prepare(struct comp_dev *dev) comp_err(dev, "module_adapter_new() %d: module params failed", ret); return ret; } - } else { - ret = comp_verify_params(dev, mod->verify_params_flags, mod->stream_params); - if (ret < 0) { - comp_err(dev, "comp_verify_params() failed."); - return ret; - } } #endif /* Prepare module */ @@ -301,10 +468,15 @@ int module_adapter_prepare(struct comp_dev *dev) mod->deep_buff_bytes = 0; - /* Get period_bytes first on prepare(). At this point it is guaranteed that the stream - * parameter from sink buffer is settled, and still prior to all references to period_bytes. + /* Get period_bytes first on prepare(). At this point the stream parameter from sink buffer + * shall be settled, provided that the pipeline is built correctly (bind IPC received). + * Hence check for NULL. */ sink = comp_dev_get_first_data_consumer(dev); + if (!sink) { + comp_err(dev, "no sink present on period size calculation"); + return -EINVAL; + } mod->period_bytes = audio_stream_period_bytes(&sink->stream, dev->frames); comp_dbg(dev, "got period_bytes = %u", mod->period_bytes); @@ -449,7 +621,8 @@ int module_adapter_prepare(struct comp_dev *dev) if (list_is_empty(&mod->raw_data_buffers_list)) { for (i = 0; i < mod->num_of_sinks; i++) { /* allocate not shared buffer */ - struct comp_buffer *buffer = buffer_alloc(buff_size, memory_flags, + struct comp_buffer *buffer = buffer_alloc(md->resources.alloc, + buff_size, memory_flags, PLATFORM_DCACHE_ALIGN, BUFFER_USAGE_NOT_SHARED); uint32_t flags; @@ -460,6 +633,8 @@ int module_adapter_prepare(struct comp_dev *dev) goto free; } + vregion_get(md->resources.alloc->vreg); + irq_local_disable(flags); list_item_prepend(&buffer->buffers_list, &mod->raw_data_buffers_list); irq_local_enable(flags); @@ -515,7 +690,6 @@ int module_adapter_prepare(struct comp_dev *dev) mod->input_buffers = NULL; return ret; } -EXPORT_SYMBOL(module_adapter_prepare); int module_adapter_params(struct comp_dev *dev, struct sof_ipc_stream_params *params) { @@ -524,18 +698,18 @@ int module_adapter_params(struct comp_dev *dev, struct sof_ipc_stream_params *pa module_adapter_set_params(mod, params); +#if CONFIG_IPC_MAJOR_3 ret = comp_verify_params(dev, mod->verify_params_flags, params); if (ret < 0) { comp_err(dev, "comp_verify_params() failed."); return ret; } +#endif /* allocate stream_params each time */ - if (mod->stream_params) - rfree(mod->stream_params); + mod_free(mod, mod->stream_params); - mod->stream_params = rzalloc(SOF_MEM_FLAG_USER, - sizeof(*mod->stream_params) + params->ext_data_length); + mod->stream_params = mod_alloc(mod, sizeof(*mod->stream_params) + params->ext_data_length); if (!mod->stream_params) return -ENOMEM; @@ -555,7 +729,6 @@ int module_adapter_params(struct comp_dev *dev, struct sof_ipc_stream_params *pa return 0; } -EXPORT_SYMBOL(module_adapter_params); /* * Function to copy from source buffer to the module buffer @@ -1170,7 +1343,6 @@ int module_adapter_copy(struct comp_dev *dev) comp_err(dev, "unknown processing_data_type"); return -EINVAL; } -EXPORT_SYMBOL(module_adapter_copy); int module_adapter_trigger(struct comp_dev *dev, int cmd) { @@ -1191,12 +1363,23 @@ int module_adapter_trigger(struct comp_dev *dev, int cmd) dev->state = COMP_STATE_ACTIVE; return PPL_STATUS_PATH_STOP; } - if (interface->trigger) + + if (interface->trigger) { +#if CONFIG_SOF_USERSPACE_APPLICATION + if (dev->ipc_config.proc_domain == COMP_PROCESSING_DOMAIN_DP) { + /* Process DP module's trigger */ + const union scheduler_dp_thread_ipc_param param = { + .pipeline_state.trigger_cmd = cmd, + }; + return scheduler_dp_thread_ipc(mod, SOF_IPC4_GLB_SET_PIPELINE_STATE, + ¶m); + } +#endif return interface->trigger(mod, cmd); + } return module_adapter_set_state(mod, dev, cmd); } -EXPORT_SYMBOL(module_adapter_trigger); int module_adapter_reset(struct comp_dev *dev) { @@ -1237,14 +1420,13 @@ int module_adapter_reset(struct comp_dev *dev) buffer_zero(buffer); } - rfree(mod->stream_params); + mod_free(mod, mod->stream_params); mod->stream_params = NULL; comp_dbg(dev, "done"); return comp_set_state(dev, COMP_TRIGGER_RESET); } -EXPORT_SYMBOL(module_adapter_reset); void module_adapter_free(struct comp_dev *dev) { @@ -1254,10 +1436,24 @@ void module_adapter_free(struct comp_dev *dev) comp_dbg(dev, "start"); +#if CONFIG_SOF_USERSPACE_APPLICATION + if (dev->task) + /* + * Run DP module's .free() method in its thread context. + * Unlike with other IPCs we first run module's .free() in + * thread context, then cancel the thread, and then execute + * final clean up + */ + scheduler_dp_thread_ipc(mod, SOF_IPC4_MOD_DELETE_INSTANCE, NULL); +#endif + ret = module_free(mod); if (ret) comp_err(dev, "failed with error: %d", ret); + if (dev->task) + schedule_task_free(dev->task); + list_for_item_safe(blist, _blist, &mod->raw_data_buffers_list) { struct comp_buffer *buffer = container_of(blist, struct comp_buffer, buffers_list); @@ -1269,17 +1465,11 @@ void module_adapter_free(struct comp_dev *dev) buffer_free(buffer); } + mod_free(mod, mod->stream_params); mod_free_all(mod); -#if CONFIG_IPC_MAJOR_4 - rfree(mod->priv.cfg.input_pins); -#endif - - rfree(mod->stream_params); - rfree(mod); - rfree(dev); + module_adapter_mem_free(mod); } -EXPORT_SYMBOL(module_adapter_free); size_t module_adapter_heap_usage(struct processing_module *mod, size_t *hwm) { @@ -1290,7 +1480,6 @@ size_t module_adapter_heap_usage(struct processing_module *mod, size_t *hwm) return res->heap_usage; } -EXPORT_SYMBOL(module_adapter_heap_usage); /* * \brief Get DAI hw params @@ -1313,7 +1502,6 @@ int module_adapter_get_hw_params(struct comp_dev *dev, struct sof_ipc_stream_par return -EOPNOTSUPP; } -EXPORT_SYMBOL(module_adapter_get_hw_params); /* * \brief Get stream position @@ -1334,7 +1522,6 @@ int module_adapter_position(struct comp_dev *dev, struct sof_ipc_stream_posn *po return -EOPNOTSUPP; } -EXPORT_SYMBOL(module_adapter_position); /* * \brief DAI timestamp configure @@ -1354,7 +1541,6 @@ int module_adapter_ts_config_op(struct comp_dev *dev) return -EOPNOTSUPP; } -EXPORT_SYMBOL(module_adapter_ts_config_op); /* * \brief DAI timestamp start @@ -1374,7 +1560,6 @@ int module_adapter_ts_start_op(struct comp_dev *dev) return -EOPNOTSUPP; } -EXPORT_SYMBOL(module_adapter_ts_start_op); /* * \brief DAI timestamp stop @@ -1394,7 +1579,6 @@ int module_adapter_ts_stop_op(struct comp_dev *dev) return -EOPNOTSUPP; } -EXPORT_SYMBOL(module_adapter_ts_stop_op); /* * \brief Get DAI timestamp @@ -1419,4 +1603,3 @@ int module_adapter_ts_get_op(struct comp_dev *dev, struct timestamp_data *tsd) return -EOPNOTSUPP; } -EXPORT_SYMBOL(module_adapter_ts_get_op); diff --git a/src/audio/module_adapter/module_adapter_ipc3.c b/src/audio/module_adapter/module_adapter_ipc3.c index 30aa8492c25d..74c8f02afa2f 100644 --- a/src/audio/module_adapter/module_adapter_ipc3.c +++ b/src/audio/module_adapter/module_adapter_ipc3.c @@ -91,7 +91,7 @@ int module_adapter_init_data(struct comp_dev *dev, break; } default: - comp_err(dev, "module_adapter_init_data() unsupported comp type %d", config->type); + comp_err(dev, "unsupported comp type %d", config->type); return -EINVAL; } @@ -99,7 +99,7 @@ int module_adapter_init_data(struct comp_dev *dev, if (size) { ret = module_load_config(dev, data, size); if (ret < 0) { - comp_err(dev, "module_adapter_init_data() error %d: config loading has failed.", + comp_err(dev, "error %d: config loading has failed.", ret); return ret; } @@ -270,7 +270,7 @@ int module_adapter_cmd(struct comp_dev *dev, int cmd, void *data, int max_data_s const struct module_interface *const interface = mod->dev->drv->adapter_ops; int ret = 0; - comp_dbg(dev, "module_adapter_cmd() %d start", cmd); + comp_dbg(dev, "%d start", cmd); switch (cmd) { case COMP_CMD_SET_DATA: @@ -307,12 +307,12 @@ int module_adapter_cmd(struct comp_dev *dev, int cmd, void *data, int max_data_s ret = interface->get_configuration(mod, 0, 0, (uint8_t *)cdata, 0); break; default: - comp_err(dev, "module_adapter_cmd() error: unknown command"); + comp_err(dev, "error: unknown command"); ret = -EINVAL; break; } - comp_dbg(dev, "module_adapter_cmd() done"); + comp_dbg(dev, "done"); return ret; } diff --git a/src/audio/module_adapter/module_adapter_ipc4.c b/src/audio/module_adapter/module_adapter_ipc4.c index 2657e7e2a561..492a5d6534ce 100644 --- a/src/audio/module_adapter/module_adapter_ipc4.c +++ b/src/audio/module_adapter/module_adapter_ipc4.c @@ -16,6 +16,7 @@ #include <sof/audio/module_adapter/module/generic.h> #include <sof/audio/pipeline.h> #include <sof/common.h> +#include <sof/lib/mailbox.h> #include <sof/platform.h> #include <sof/ut.h> #include <rtos/interrupt.h> @@ -25,20 +26,25 @@ LOG_MODULE_DECLARE(module_adapter, CONFIG_SOF_LOG_LEVEL); -static const struct ipc4_base_module_extended_cfg * -module_ext_init_decode(struct comp_dev *dev, struct module_config *dst, - const unsigned char *data, size_t *size) +int module_ext_init_decode(const struct comp_driver *drv, struct module_ext_init_data *ext_data, + struct ipc_config_process *spec) { - const struct ipc4_module_init_ext_init *ext_init = - (const struct ipc4_module_init_ext_init *)data; - bool last_object = !ext_init->data_obj_array; + const struct ipc4_module_init_ext_init *ext_init; const struct ipc4_module_init_ext_object *obj; + bool last_object; + size_t consumed; - if (*size < sizeof(ext_init)) { - comp_err(dev, "Size too small for ext init %u < %u", - *size, sizeof(ext_init)); - return NULL; + assert(drv->type == SOF_COMP_MODULE_ADAPTER); + + /* Validate size before dereferencing ext_init pointer */ + if (spec->size < sizeof(*ext_init)) { + comp_cl_err(drv, "Size too small for ext init %u < %zu", + spec->size, sizeof(*ext_init)); + return -EINVAL; } + + ext_init = (const struct ipc4_module_init_ext_init *)spec->data; + last_object = !ext_init->data_obj_array; /* TODO: Handle ext_init->gna_used and ext_init->rtos_domain here */ /* Get the first obj struct right after ext_init struct */ obj = (const struct ipc4_module_init_ext_object *)(ext_init + 1); @@ -46,18 +52,18 @@ module_ext_init_decode(struct comp_dev *dev, struct module_config *dst, const struct ipc4_module_init_ext_object *next_obj; /* Check if there is space for the object header */ - if ((unsigned char *)(obj + 1) - data > *size) { - comp_err(dev, "ext init obj overflow, %u > %u", - (unsigned char *)(obj + 1) - data, *size); - return NULL; + if ((unsigned char *)(obj + 1) - spec->data > spec->size) { + comp_cl_err(drv, "ext init obj overflow, %u > %u", + (unsigned char *)(obj + 1) - spec->data, spec->size); + return -EINVAL; } /* Calculate would be next object position and check if current object fits */ next_obj = (const struct ipc4_module_init_ext_object *) (((uint32_t *) (obj + 1)) + obj->object_words); - if ((unsigned char *)next_obj - data > *size) { - comp_err(dev, "ext init object array overflow, %u > %u", - (unsigned char *)obj - data, *size); - return NULL; + if ((unsigned char *)next_obj - spec->data > spec->size) { + comp_cl_err(drv, "ext init object array overflow, %u > %u", + (unsigned char *)obj - spec->data, spec->size); + return -EINVAL; } switch (obj->object_id) { case IPC4_MOD_INIT_DATA_ID_DP_DATA: @@ -67,20 +73,34 @@ module_ext_init_decode(struct comp_dev *dev, struct module_config *dst, (const struct ipc4_module_init_ext_obj_dp_data *)(obj + 1); if (obj->object_words * sizeof(uint32_t) < sizeof(*dp_data)) { - comp_err(dev, "dp_data object too small %u < %u", - obj->object_words * sizeof(uint32_t), sizeof(*dp_data)); - return NULL; + comp_cl_warn(drv, "dp_data object too small %zu < %zu", + obj->object_words * sizeof(uint32_t), + sizeof(*dp_data)); + break; } - dst->domain_id = dp_data->domain_id; - dst->stack_bytes = dp_data->stack_bytes; - dst->heap_bytes = dp_data->heap_bytes; - comp_info(dev, "init_ext_obj_dp_data domain %u stack %u heap %u", - dp_data->domain_id, dp_data->stack_bytes, dp_data->heap_bytes); + ext_data->dp_data = dp_data; + comp_cl_info(drv, + "init_ext_obj_dp_data domain %u stack %u interim %u lifetime %u shared %u", + dp_data->domain_id, dp_data->stack_bytes, + dp_data->interim_heap_bytes, dp_data->lifetime_heap_bytes, + dp_data->shared_bytes); + break; + } + case IPC4_MOD_INIT_DATA_ID_MODULE_DATA: + { + /* + * set the module init_data. Modules must copy/save this in their init + * callbacks if they need this to be persistent + */ + ext_data->module_data = (const void *)(obj + 1); + ext_data->module_data_size = obj->object_words * sizeof(uint32_t); + comp_cl_info(drv, "module init data size %u bytes", + ext_data->module_data_size); break; } default: - comp_info(dev, "Unknown ext init object id %u of %u words", - obj->object_id, obj->object_words); + comp_cl_info(drv, "Unknown ext init object id %u of %u words", + obj->object_id, obj->object_words); } /* Read the last object flag from obj header */ last_object = obj->last_object; @@ -88,11 +108,20 @@ module_ext_init_decode(struct comp_dev *dev, struct module_config *dst, obj = next_obj; } - /* Remove decoded ext_init payload from the size */ - *size -= (unsigned char *) obj - data; + /* + * Remove decoded ext_init payload from spec. + * consumed <= spec->size is guaranteed here: + * - no-loop path: consumed == sizeof(*ext_init) <= spec->size (validated above) + * - loop path: in-loop bounds checks (obj/next_obj vs spec->size) ensure + * obj never advances past spec->data + spec->size + */ + consumed = (unsigned char *)obj - spec->data; + assert(consumed <= spec->size); + + spec->size -= consumed; + spec->data = (const unsigned char *)obj; - /* return remaining payload */ - return (const struct ipc4_base_module_extended_cfg *)obj; + return 0; } /* @@ -114,15 +143,14 @@ int module_adapter_init_data(struct comp_dev *dev, size_t cfgsz = args->size; assert(dev->drv->type == SOF_COMP_MODULE_ADAPTER); - if (config->ipc_extended_init) - cfg = module_ext_init_decode(dev, dst, args->data, &cfgsz); - else - cfg = (const struct ipc4_base_module_extended_cfg *)args->data; + cfg = (const struct ipc4_base_module_extended_cfg *)args->data; if (cfg == NULL) return -EINVAL; - if (cfgsz < sizeof(cfg->base_cfg)) + if (cfgsz > MAILBOX_HOSTBOX_SIZE || cfgsz < sizeof(cfg->base_cfg)) { + comp_err(dev, "invalid config size %zu", cfgsz); return -EINVAL; + } dst->base_cfg = cfg->base_cfg; dst->size = cfgsz; @@ -136,9 +164,9 @@ int module_adapter_init_data(struct comp_dev *dev, if (cfgsz == (sizeof(*cfg) + pinsz)) { dst->nb_input_pins = n_in; dst->nb_output_pins = n_out; - dst->input_pins = module_driver_heap_rmalloc(dev->drv->user_heap, - SOF_MEM_FLAG_USER | - SOF_MEM_FLAG_COHERENT, pinsz); + dst->input_pins = sof_heap_alloc(dev->mod->priv.resources.alloc->heap, + SOF_MEM_FLAG_USER | SOF_MEM_FLAG_COHERENT, + pinsz, 0); if (!dst->input_pins) return -ENOMEM; @@ -148,8 +176,12 @@ int module_adapter_init_data(struct comp_dev *dev, } } - dst->init_data = cfg; /* legacy API */ - dst->avail = true; + /* Assume legacy API if module data was not found in ext_init payload */ + if (!config->ipc_extended_init || !dst->ext_data->module_data) { + dst->init_data = cfg; /* legacy API */ + dst->avail = true; + } + return 0; } @@ -212,7 +244,6 @@ int module_set_large_config(struct comp_dev *dev, uint32_t param_id, bool first_ NULL, 0); return 0; } -EXPORT_SYMBOL(module_set_large_config); int module_get_large_config(struct comp_dev *dev, uint32_t param_id, bool first_block, bool last_block, uint32_t *data_offset_size, char *data) @@ -244,7 +275,6 @@ int module_get_large_config(struct comp_dev *dev, uint32_t param_id, bool first_ */ return -EIO; } -EXPORT_SYMBOL(module_get_large_config); int module_adapter_get_attribute(struct comp_dev *dev, uint32_t type, void *value) { @@ -266,7 +296,6 @@ int module_adapter_get_attribute(struct comp_dev *dev, uint32_t type, void *valu return 0; } -EXPORT_SYMBOL(module_adapter_get_attribute); int module_adapter_set_attribute(struct comp_dev *dev, uint32_t type, void *value) { @@ -284,7 +313,6 @@ int module_adapter_set_attribute(struct comp_dev *dev, uint32_t type, void *valu return 0; } -EXPORT_SYMBOL(module_adapter_set_attribute); static bool module_adapter_multi_sink_source_prepare(struct comp_dev *dev) { @@ -320,11 +348,47 @@ static bool module_adapter_multi_sink_source_prepare(struct comp_dev *dev) return false; } +static int module_update_source_buffer_params(struct processing_module *mod, + struct bind_info *bind_data) +{ + struct module_config *dst = &mod->priv.cfg; + struct sof_ipc_stream_params params; + struct comp_buffer *buffer; + int dst_queue_id = bind_data->ipc4_data->extension.r.dst_queue; + + /* only update buffer params for sink components */ + if (bind_data->bind_type != COMP_BIND_TYPE_SOURCE) + return 0; + + comp_dev_for_each_producer(mod->dev, buffer) { + if (IPC4_SINK_QUEUE_ID(buffer->stream.runtime_stream_params.id) != dst_queue_id) + continue; + + /* use base_cfg params for pin 0 or if base config extn is missing */ + if (!dst_queue_id || dst_queue_id >= dst->nb_input_pins) { + buffer_set_params(buffer, mod->stream_params, BUFFER_UPDATE_FORCE); + return 0; + } + + /* otherwise use the respective input pin audio format */ + ipc4_audio_format_to_stream_params(&dst->input_pins[dst_queue_id].audio_fmt, + ¶ms); + buffer_set_params(buffer, ¶ms, BUFFER_UPDATE_FORCE); + return 0; + } + + return 0; +} + int module_adapter_bind(struct comp_dev *dev, struct bind_info *bind_data) { struct processing_module *mod = comp_mod(dev); int ret; + ret = module_update_source_buffer_params(mod, bind_data); + if (ret < 0) + return ret; + ret = module_bind(mod, bind_data); if (ret < 0) return ret; @@ -333,7 +397,6 @@ int module_adapter_bind(struct comp_dev *dev, struct bind_info *bind_data) return 0; } -EXPORT_SYMBOL(module_adapter_bind); int module_adapter_unbind(struct comp_dev *dev, struct bind_info *unbind_data) { @@ -348,7 +411,6 @@ int module_adapter_unbind(struct comp_dev *dev, struct bind_info *unbind_data) return 0; } -EXPORT_SYMBOL(module_adapter_unbind); uint64_t module_adapter_get_total_data_processed(struct comp_dev *dev, uint32_t stream_no, bool input) @@ -364,7 +426,6 @@ uint64_t module_adapter_get_total_data_processed(struct comp_dev *dev, else return mod->total_data_consumed; } -EXPORT_SYMBOL(module_adapter_get_total_data_processed); int module_adapter_sink_src_prepare(struct comp_dev *dev) { diff --git a/src/audio/multiband_drc/README.md b/src/audio/multiband_drc/README.md new file mode 100644 index 000000000000..2a86e93ddbb6 --- /dev/null +++ b/src/audio/multiband_drc/README.md @@ -0,0 +1,27 @@ +# Multi-Band DRC Architecture + +This directory contains the Multi-Band Dynamic Range Compressor. + +## Overview + +Splits the audio into several discrete frequency bands (e.g., bass, mids, treble) and applies a separate instance of DRC to each band before remixing them. + +## Architecture Diagram + +```mermaid +graph LR + In[Audio Input] --> Splitter[Band Splitter] + Splitter --> DRC1[DRC Band 1] + Splitter --> DRC2[DRC Band 2] + DRC1 --> Sum[Mixer] + DRC2 --> Sum + Sum --> Out[Audio Output] +``` + +## Configuration and Scripts + +- **Kconfig**: Enables the Multiband Dynamic Range Compressor component (`COMP_MULTIBAND_DRC`). Has explicit dependencies on standard equalizers and filters: `COMP_IIR && COMP_CROSSOVER && COMP_DRC`. +- **CMakeLists.txt**: Compiles `multiband_drc.c` and generic versions, wrapping with IPC specifics (`multiband_drc_ipc3.c` or `multiband_drc_ipc4.c`). Supports Zephyr loadable extensions (`llext`). +- **multiband_drc.toml**: Defines module topology constraints and mapping (UUID `UUIDREG_STR_MULTIBAND_DRC`, module type 9). +- **Topology (.conf)**: Derived from `tools/topology/topology2/include/components/multiband_drc.conf`, configuring the `multiband_drc` widget object of type `effect` (UUID `56:22:9f:0d:4f:8e:b3:47:84:48:23:9a:33:4f:11:91`). Utilizes an internal switch control for `fc`. +- **MATLAB Tuning (`tune/`)**: Features `sof_example_multiband_drc.m` to generate the complex configuration structures necessary to bind multiple EQs, crossovers, and compressors together. The scripts output the aggregated `.m4`, binary `.bin`, and ALSA `.txt` blobs which define parameter blocks for each individual sub-system active within the multiband processor. diff --git a/src/audio/multiband_drc/multiband_drc.c b/src/audio/multiband_drc/multiband_drc.c index cca2850cfe12..84a079134ea1 100644 --- a/src/audio/multiband_drc/multiband_drc.c +++ b/src/audio/multiband_drc/multiband_drc.c @@ -39,31 +39,31 @@ LOG_MODULE_REGISTER(multiband_drc, CONFIG_SOF_LOG_LEVEL); SOF_DEFINE_REG_UUID(multiband_drc); -DECLARE_TR_CTX(multiband_drc_tr, SOF_UUID(multiband_drc_uuid), LOG_LEVEL_INFO); - /* Called from multiband_drc_setup() from multiband_drc_process(), so cannot be __cold */ -static void multiband_drc_reset_state(struct multiband_drc_state *state) +static void multiband_drc_reset_state(struct processing_module *mod, + struct multiband_drc_state *state) { int i; /* Reset emphasis eq-iir state */ for (i = 0; i < PLATFORM_MAX_CHANNELS; i++) - multiband_drc_iir_reset_state_ch(&state->emphasis[i]); + multiband_drc_iir_reset_state_ch(mod, &state->emphasis[i]); /* Reset crossover state */ for (i = 0; i < PLATFORM_MAX_CHANNELS; i++) - crossover_reset_state_ch(&state->crossover[i]); + crossover_reset_state_ch(mod, &state->crossover[i]); /* Reset drc kernel state */ for (i = 0; i < SOF_MULTIBAND_DRC_MAX_BANDS; i++) - drc_reset_state(&state->drc[i]); + drc_reset_state(mod, &state->drc[i]); /* Reset deemphasis eq-iir state */ for (i = 0; i < PLATFORM_MAX_CHANNELS; i++) - multiband_drc_iir_reset_state_ch(&state->deemphasis[i]); + multiband_drc_iir_reset_state_ch(mod, &state->deemphasis[i]); } -static int multiband_drc_eq_init_coef_ch(struct sof_eq_iir_biquad *coef, +static int multiband_drc_eq_init_coef_ch(struct processing_module *mod, + struct sof_eq_iir_biquad *coef, struct iir_state_df1 *eq) { int ret; @@ -72,8 +72,7 @@ static int multiband_drc_eq_init_coef_ch(struct sof_eq_iir_biquad *coef, if (SOF_EMP_DEEMP_BIQUADS != SOF_IIR_DF1_4TH_NUM_BIQUADS) return -EINVAL; - eq->coef = rzalloc(SOF_MEM_FLAG_USER, - sizeof(struct sof_eq_iir_biquad) * SOF_EMP_DEEMP_BIQUADS); + eq->coef = mod_zalloc(mod, sizeof(struct sof_eq_iir_biquad) * SOF_EMP_DEEMP_BIQUADS); if (!eq->coef) return -ENOMEM; @@ -86,8 +85,7 @@ static int multiband_drc_eq_init_coef_ch(struct sof_eq_iir_biquad *coef, * delay[0..1] -> state for first biquad * delay[2..3] -> state for second biquad */ - eq->delay = rzalloc(SOF_MEM_FLAG_USER, - sizeof(uint64_t) * CROSSOVER_NUM_DELAYS_LR4); + eq->delay = mod_zalloc(mod, sizeof(uint64_t) * CROSSOVER_NUM_DELAYS_LR4); if (!eq->delay) return -ENOMEM; @@ -110,7 +108,7 @@ static int multiband_drc_init_coef(struct processing_module *mod, int16_t nch, u int i, ch, ret, num_bands; if (!config) { - comp_err(dev, "multiband_drc_init_coef(), no config is set"); + comp_err(dev, "no config is set"); return -EINVAL; } @@ -119,22 +117,22 @@ static int multiband_drc_init_coef(struct processing_module *mod, int16_t nch, u /* Sanity checks */ if (nch > PLATFORM_MAX_CHANNELS) { comp_err(dev, - "multiband_drc_init_coef(), invalid channels count(%i)", nch); + "invalid channels count(%i)", nch); return -EINVAL; } if (config->num_bands > SOF_MULTIBAND_DRC_MAX_BANDS) { - comp_err(dev, "multiband_drc_init_coef(), invalid bands count(%i)", + comp_err(dev, "invalid bands count(%i)", config->num_bands); return -EINVAL; } - comp_info(dev, "multiband_drc_init_coef(), initializing %i-way crossover", + comp_info(dev, "initializing %i-way crossover", config->num_bands); /* Crossover: determine the split function */ cd->crossover_split = crossover_find_split_func(config->num_bands); if (!cd->crossover_split) { - comp_err(dev, "multiband_drc_init_coef(), No crossover_split for band count(%i)", + comp_err(dev, "No crossover_split for band count(%i)", config->num_bands); return -EINVAL; } @@ -142,39 +140,39 @@ static int multiband_drc_init_coef(struct processing_module *mod, int16_t nch, u /* Crossover: collect the coef array and assign it to every channel */ crossover = config->crossover_coef; for (ch = 0; ch < nch; ch++) { - ret = crossover_init_coef_ch(crossover, &state->crossover[ch], + ret = crossover_init_coef_ch(mod, crossover, &state->crossover[ch], config->num_bands); /* Free all previously allocated blocks in case of an error */ if (ret < 0) { comp_err(dev, - "multiband_drc_init_coef(), could not assign coeffs to ch %d", ch); + "could not assign coeffs to ch %d", ch); goto err; } } - comp_info(dev, "multiband_drc_init_coef(), initializing emphasis_eq"); + comp_info(dev, "initializing emphasis_eq"); /* Emphasis: collect the coef array and assign it to every channel */ emphasis = config->emp_coef; for (ch = 0; ch < nch; ch++) { - ret = multiband_drc_eq_init_coef_ch(emphasis, &state->emphasis[ch]); + ret = multiband_drc_eq_init_coef_ch(mod, emphasis, &state->emphasis[ch]); /* Free all previously allocated blocks in case of an error */ if (ret < 0) { - comp_err(dev, "multiband_drc_init_coef(), could not assign coeffs to ch %d", + comp_err(dev, "could not assign coeffs to ch %d", ch); goto err; } } - comp_info(dev, "multiband_drc_init_coef(), initializing deemphasis_eq"); + comp_info(dev, "initializing deemphasis_eq"); /* Deemphasis: collect the coef array and assign it to every channel */ deemphasis = config->deemp_coef; for (ch = 0; ch < nch; ch++) { - ret = multiband_drc_eq_init_coef_ch(deemphasis, &state->deemphasis[ch]); + ret = multiband_drc_eq_init_coef_ch(mod, deemphasis, &state->deemphasis[ch]); /* Free all previously allocated blocks in case of an error */ if (ret < 0) { - comp_err(dev, "multiband_drc_init_coef(), could not assign coeffs to ch %d", + comp_err(dev, "could not assign coeffs to ch %d", ch); goto err; } @@ -182,19 +180,20 @@ static int multiband_drc_init_coef(struct processing_module *mod, int16_t nch, u /* Allocate all DRC pre-delay buffers and set delay time with band number */ for (i = 0; i < num_bands; i++) { - comp_info(dev, "multiband_drc_init_coef(), initializing drc band %d", i); + comp_info(dev, "initializing drc band %d", i); - ret = drc_init_pre_delay_buffers(&state->drc[i], (size_t)sample_bytes, (int)nch); + ret = drc_init_pre_delay_buffers(mod, &state->drc[i], + (size_t)sample_bytes, (int)nch); if (ret < 0) { comp_err(dev, - "multiband_drc_init_coef(), could not init pre delay buffers"); + "could not init pre delay buffers"); goto err; } ret = drc_set_pre_delay_time(&state->drc[i], cd->config->drc_coef[i].pre_delay_time, rate); if (ret < 0) { - comp_err(dev, "multiband_drc_init_coef(), could not set pre delay time"); + comp_err(dev, "could not set pre delay time"); goto err; } } @@ -202,7 +201,7 @@ static int multiband_drc_init_coef(struct processing_module *mod, int16_t nch, u return 0; err: - multiband_drc_reset_state(state); + multiband_drc_reset_state(mod, state); return ret; } @@ -213,7 +212,7 @@ static int multiband_drc_setup(struct processing_module *mod, int16_t channels, struct multiband_drc_comp_data *cd = module_get_private_data(mod); /* Reset any previous state */ - multiband_drc_reset_state(&cd->state); + multiband_drc_reset_state(mod, &cd->state); /* Setup Crossover, Emphasis EQ, Deemphasis EQ, and DRC */ return multiband_drc_init_coef(mod, channels, rate); @@ -227,23 +226,11 @@ static int multiband_drc_init(struct processing_module *mod) { struct module_data *md = &mod->priv; struct comp_dev *dev = mod->dev; - struct module_config *cfg = &md->cfg; struct multiband_drc_comp_data *cd; - size_t bs = cfg->size; - int ret; - comp_info(dev, "multiband_drc_init()"); - - /* Check first before proceeding with dev and cd that coefficients - * blob size is sane. - */ - if (bs > SOF_MULTIBAND_DRC_MAX_BLOB_SIZE) { - comp_err(dev, "multiband_drc_init(), error: configuration blob size = %u > %d", - bs, SOF_MULTIBAND_DRC_MAX_BLOB_SIZE); - return -EINVAL; - } + comp_info(dev, "entry"); - cd = rzalloc(SOF_MEM_FLAG_USER, sizeof(*cd)); + cd = mod_zalloc(mod, sizeof(*cd)); if (!cd) return -ENOMEM; @@ -258,27 +245,16 @@ static int multiband_drc_init(struct processing_module *mod) multiband_drc_process_enable(&cd->process_enabled); /* Handler for configuration data */ - cd->model_handler = comp_data_blob_handler_new(dev); + cd->model_handler = mod_data_blob_handler_new(mod); if (!cd->model_handler) { comp_err(dev, "comp_data_blob_handler_new() failed."); - ret = -ENOMEM; - goto cd_fail; + mod_free(mod, cd); + return -ENOMEM; } - /* Get configuration data and reset DRC state */ - ret = comp_init_data_blob(cd->model_handler, bs, cfg->data); - if (ret < 0) { - comp_err(dev, "comp_init_data_blob() failed."); - goto cd_fail; - } - multiband_drc_reset_state(&cd->state); + multiband_drc_reset_state(mod, &cd->state); return 0; - -cd_fail: - comp_data_blob_handler_free(cd->model_handler); - rfree(cd); - return ret; } __cold static int multiband_drc_free(struct processing_module *mod) @@ -287,11 +263,11 @@ __cold static int multiband_drc_free(struct processing_module *mod) assert_can_be_cold(); - comp_info(mod->dev, "multiband_drc_free()"); + comp_info(mod->dev, "entry"); - comp_data_blob_handler_free(cd->model_handler); + mod_data_blob_handler_free(mod, cd->model_handler); - rfree(cd); + mod_free(mod, cd); return 0; } @@ -305,7 +281,7 @@ __cold static int multiband_drc_set_config(struct processing_module *mod, uint32 assert_can_be_cold(); - comp_dbg(dev, "multiband_drc_set_config()"); + comp_dbg(dev, "entry"); return multiband_drc_set_ipc_config(mod, param_id, fragment, pos, data_offset_size, fragment_size); @@ -319,7 +295,7 @@ __cold static int multiband_drc_get_config(struct processing_module *mod, assert_can_be_cold(); - comp_dbg(mod->dev, "multiband_drc_get_config()"); + comp_dbg(mod->dev, "entry"); return multiband_drc_get_ipc_config(mod, cdata, fragment_size); } @@ -336,7 +312,7 @@ static int multiband_drc_process(struct processing_module *mod, int frames = input_buffers[0].size; int ret; - comp_dbg(dev, "multiband_drc_process()"); + comp_dbg(dev, "entry"); /* Check for changed configuration */ if (comp_is_new_data_blob_available(cd->model_handler)) { @@ -344,7 +320,7 @@ static int multiband_drc_process(struct processing_module *mod, ret = multiband_drc_setup(mod, (int16_t)audio_stream_get_channels(sink), audio_stream_get_rate(sink)); if (ret < 0) { - comp_err(dev, "multiband_drc_process(), failed DRC setup"); + comp_err(dev, "failed DRC setup"); return ret; } } @@ -366,11 +342,12 @@ static int multiband_drc_prepare(struct processing_module *mod, struct multiband_drc_comp_data *cd = module_get_private_data(mod); struct comp_dev *dev = mod->dev; struct comp_buffer *sourceb; + size_t data_size; int channels; int rate; int ret = 0; - comp_info(dev, "multiband_drc_prepare()"); + comp_info(dev, "entry"); ret = multiband_drc_params(mod); if (ret < 0) @@ -389,20 +366,20 @@ static int multiband_drc_prepare(struct processing_module *mod, rate = audio_stream_get_rate(&sourceb->stream); /* Initialize DRC */ - comp_dbg(dev, "multiband_drc_prepare(), source_format=%d, sink_format=%d", + comp_dbg(dev, "source_format=%d, sink_format=%d", cd->source_format, cd->source_format); - cd->config = comp_get_data_blob(cd->model_handler, NULL, NULL); - if (cd->config) { + cd->config = comp_get_data_blob(cd->model_handler, &data_size, NULL); + if (cd->config && data_size > 0) { ret = multiband_drc_setup(mod, channels, rate); if (ret < 0) { - comp_err(dev, "multiband_drc_prepare() error: multiband_drc_setup failed."); + comp_err(dev, "error: multiband_drc_setup failed."); return ret; } } cd->multiband_drc_func = multiband_drc_find_proc_func(cd->source_format); if (!cd->multiband_drc_func) { - comp_err(dev, "multiband_drc_prepare(), No proc func"); + comp_err(dev, "No proc func"); return -EINVAL; } @@ -413,9 +390,9 @@ static int multiband_drc_reset(struct processing_module *mod) { struct multiband_drc_comp_data *cd = module_get_private_data(mod); - comp_info(mod->dev, "multiband_drc_reset()"); + comp_info(mod->dev, "entry"); - multiband_drc_reset_state(&cd->state); + multiband_drc_reset_state(mod, &cd->state); cd->source_format = 0; cd->multiband_drc_func = NULL; @@ -449,6 +426,7 @@ SOF_LLEXT_BUILDINFO; #else +DECLARE_TR_CTX(multiband_drc_tr, SOF_UUID(multiband_drc_uuid), LOG_LEVEL_INFO); DECLARE_MODULE_ADAPTER(multiband_drc_interface, multiband_drc_uuid, multiband_drc_tr); SOF_MODULE_INIT(multiband_drc, sys_comp_module_multiband_drc_interface_init); diff --git a/src/audio/multiband_drc/multiband_drc.h b/src/audio/multiband_drc/multiband_drc.h index 20f939877209..6a99fda55cef 100644 --- a/src/audio/multiband_drc/multiband_drc.h +++ b/src/audio/multiband_drc/multiband_drc.h @@ -89,10 +89,11 @@ static inline multiband_drc_func multiband_drc_find_proc_func_pass(enum sof_ipc_ return NULL; } -static inline void multiband_drc_iir_reset_state_ch(struct iir_state_df1 *iir) +static inline void multiband_drc_iir_reset_state_ch(struct processing_module *mod, + struct iir_state_df1 *iir) { - rfree(iir->coef); - rfree(iir->delay); + mod_free(mod, iir->coef); + mod_free(mod, iir->delay); iir->coef = NULL; iir->delay = NULL; diff --git a/src/audio/multiband_drc/multiband_drc_ipc3.c b/src/audio/multiband_drc/multiband_drc_ipc3.c index fdc9de9e5ba9..04aa4fc33e40 100644 --- a/src/audio/multiband_drc/multiband_drc_ipc3.c +++ b/src/audio/multiband_drc/multiband_drc_ipc3.c @@ -30,10 +30,10 @@ static int multiband_drc_cmd_set_value(struct processing_module *mod, switch (cdata->cmd) { case SOF_CTRL_CMD_SWITCH: - comp_dbg(dev, "multiband_drc_multiband_drc_cmd_set_value(), SOF_CTRL_CMD_SWITCH"); + comp_dbg(dev, "multiband_drc_SOF_CTRL_CMD_SWITCH"); if (cdata->num_elems == 1) { cd->process_enabled = cdata->chanv[0].value; - comp_info(dev, "multiband_drc_cmd_set_value(), process_enabled = %d", + comp_info(dev, "process_enabled = %d", cd->process_enabled); return 0; } @@ -68,13 +68,13 @@ static int multiband_drc_cmd_get_value(struct processing_module *mod, switch (cdata->cmd) { case SOF_CTRL_CMD_SWITCH: - comp_dbg(dev, "multiband_drc_cmd_get_value(), SOF_CTRL_CMD_SWITCH"); + comp_dbg(dev, "SOF_CTRL_CMD_SWITCH"); for (j = 0; j < cdata->num_elems; j++) cdata->chanv[j].value = cd->process_enabled; if (cdata->num_elems == 1) return 0; - comp_warn(dev, "multiband_drc_cmd_get_value() warn: num_elems should be 1, got %d", + comp_warn(dev, "warn: num_elems should be 1, got %d", cdata->num_elems); return 0; } @@ -91,7 +91,7 @@ int multiband_drc_get_ipc_config(struct processing_module *mod, struct sof_ipc_c if (cdata->cmd != SOF_CTRL_CMD_BINARY) return multiband_drc_cmd_get_value(mod, cdata); - comp_dbg(mod->dev, "multiband_drc_get_ipc_config(), SOF_CTRL_CMD_BINARY"); + comp_dbg(mod->dev, "SOF_CTRL_CMD_BINARY"); return comp_data_blob_get_cmd(cd->model_handler, cdata, fragment_size); } diff --git a/src/audio/multiband_drc/multiband_drc_ipc4.c b/src/audio/multiband_drc/multiband_drc_ipc4.c index 03d7e6c5a074..ac2a7a7426aa 100644 --- a/src/audio/multiband_drc/multiband_drc_ipc4.c +++ b/src/audio/multiband_drc/multiband_drc_ipc4.c @@ -54,11 +54,11 @@ __cold int multiband_drc_set_ipc_config(struct processing_module *mod, uint32_t return 0; case SOF_IPC4_ENUM_CONTROL_PARAM_ID: - comp_err(dev, "multiband_drc_set_ipc_config(), illegal control."); + comp_err(dev, "illegal control."); return -EINVAL; } - comp_dbg(mod->dev, "multiband_drc_set_ipc_config(), SOF_CTRL_CMD_BINARY"); + comp_dbg(mod->dev, "SOF_CTRL_CMD_BINARY"); return comp_data_blob_set(cd->model_handler, pos, data_offset_size, fragment, fragment_size); } @@ -68,7 +68,7 @@ __cold int multiband_drc_get_ipc_config(struct processing_module *mod, { struct multiband_drc_comp_data *cd = module_get_private_data(mod); - comp_dbg(mod->dev, "multiband_drc_get_ipc_config(), SOF_CTRL_CMD_BINARY"); + comp_dbg(mod->dev, "SOF_CTRL_CMD_BINARY"); return comp_data_blob_get_cmd(cd->model_handler, cdata, fragment_size); } @@ -82,7 +82,7 @@ int multiband_drc_params(struct processing_module *mod) enum sof_ipc_frame valid_fmt, frame_fmt; int i; - comp_dbg(dev, "multiband_drc_params()"); + comp_dbg(dev, "entry"); comp_params = *params; comp_params.channels = mod->priv.cfg.base_cfg.audio_fmt.channels_count; diff --git a/src/audio/mux/README.md b/src/audio/mux/README.md new file mode 100644 index 000000000000..97a5a3905b5e --- /dev/null +++ b/src/audio/mux/README.md @@ -0,0 +1,23 @@ +# Multiplexer/Demultiplexer Architecture + +This directory contains the Mux/Demux component. + +## Overview + +Selects between multiple audio paths, routing N inputs to 1 output, or 1 input to N outputs. + +## Architecture Diagram + +```mermaid +graph LR + In1[Path A] --> Mux[Path Selector] + In2[Path B] --> Mux + Mux --> Out[Selected Path] +``` + +## Configuration and Scripts + +- **Kconfig**: Enables the MUX component (`COMP_MUX`), relying on the standard `COMP_MODULE_ADAPTER`. +- **CMakeLists.txt**: Manages `mux.c` and generic code paths alongside IPC abstraction interfaces (`mux_ipc3.c`, `mux_ipc4.c`). Supports `llext` modular integration. +- **mux.toml**: Topology settings defining UUID `UUIDREG_STR_MUX4` and accommodating up to 15 concurrent instances with multiple 10-channel I/O pins. +- **Topology (.conf)**: Found at `tools/topology/topology2/include/components/muxdemux.conf`, which declares a `muxdemux` widget object. Provides flexible process typing such as `DEMUX` with type `effect` (UUID `68:68:b2:c4:30:14:0e:47:a0:89:15:d1:c7:7f:85:1a`). diff --git a/src/audio/mux/mux.c b/src/audio/mux/mux.c index bbcf1b544e90..965faaf8037e 100644 --- a/src/audio/mux/mux.c +++ b/src/audio/mux/mux.c @@ -11,7 +11,6 @@ #include <sof/audio/ipc-config.h> #include <sof/common.h> #include <sof/ipc/msg.h> -#include <rtos/alloc.h> #include <rtos/init.h> #include <sof/lib/uuid.h> #include <sof/list.h> @@ -94,14 +93,13 @@ static int mux_demux_common_init(struct processing_module *mod, enum sof_comp_ty return -EINVAL; } - cd = rzalloc(SOF_MEM_FLAG_USER, - sizeof(*cd) + MUX_BLOB_STREAMS_SIZE); + cd = mod_zalloc(mod, sizeof(*cd) + MUX_BLOB_STREAMS_SIZE); if (!cd) return -ENOMEM; - cd->model_handler = comp_data_blob_handler_new(dev); + cd->model_handler = mod_data_blob_handler_new(mod); if (!cd->model_handler) { - comp_err(dev, "comp_data_blob_handler_new() failed."); + comp_err(dev, "mod_data_blob_handler_new() failed."); ret = -ENOMEM; goto err; } @@ -109,7 +107,7 @@ static int mux_demux_common_init(struct processing_module *mod, enum sof_comp_ty module_data->private = cd; ret = comp_init_data_blob(cd->model_handler, cfg->size, cfg->init_data); if (ret < 0) { - comp_err(dev, "comp_init_data_blob() failed."); + comp_err(dev, "module data blob initialization failed."); goto err_init; } @@ -119,10 +117,10 @@ static int mux_demux_common_init(struct processing_module *mod, enum sof_comp_ty return 0; err_init: - comp_data_blob_handler_free(cd->model_handler); + mod_data_blob_handler_free(mod, cd->model_handler); err: - rfree(cd); + mod_free(mod, cd); return ret; } @@ -137,10 +135,10 @@ static int mux_free(struct processing_module *mod) { struct comp_data *cd = module_get_private_data(mod); - comp_dbg(mod->dev, "mux_free()"); + comp_dbg(mod->dev, "entry"); - comp_data_blob_handler_free(cd->model_handler); - rfree(cd); + mod_data_blob_handler_free(mod, cd->model_handler); + mod_free(mod, cd); return 0; } @@ -240,7 +238,7 @@ static int demux_process(struct processing_module *mod, int source_bytes; int i; - comp_dbg(dev, "demux_process()"); + comp_dbg(dev, "entry"); /* align sink streams with their respective configurations */ comp_dev_for_each_consumer(dev, sink) { @@ -314,10 +312,9 @@ static int mux_process(struct processing_module *mod, const struct audio_stream *sources_stream[MUX_MAX_STREAMS] = { NULL }; int frames = 0; int sink_bytes; - int source_bytes; int i, j; - comp_dbg(dev, "mux_process()"); + comp_dbg(dev, "entry"); /* align source streams with their respective configurations */ j = 0; @@ -343,18 +340,18 @@ static int mux_process(struct processing_module *mod, if (num_input_buffers == 0) return 0; - source_bytes = frames * audio_stream_frame_bytes(mod->input_buffers[0].data); sink_bytes = frames * audio_stream_frame_bytes(mod->output_buffers[0].data); mux_prepare_active_look_up(cd, output_buffers[0].data, &sources_stream[0]); /* produce output */ cd->mux(dev, output_buffers[0].data, &sources_stream[0], frames, &cd->active_lookup); - /* Update consumed and produced */ + /* Update consumed per source using each source's own frame size */ j = 0; comp_dev_for_each_producer(dev, source) { if (comp_buffer_get_source_state(source) == dev->state) - mod->input_buffers[j].consumed = source_bytes; + mod->input_buffers[j].consumed = + frames * audio_stream_frame_bytes(mod->input_buffers[j].data); j++; } mod->output_buffers[0].size = sink_bytes; @@ -368,7 +365,7 @@ static int mux_reset(struct processing_module *mod) struct comp_dev *dev = mod->dev; int dir = dev->pipeline->source_comp->direction; - comp_dbg(dev, "mux_reset()"); + comp_dbg(dev, "entry"); if (dir == SOF_IPC_STREAM_PLAYBACK) { comp_dev_for_each_producer(dev, source) { @@ -392,19 +389,9 @@ static int mux_prepare(struct processing_module *mod, { struct comp_dev *dev = mod->dev; struct comp_data *cd = module_get_private_data(mod); - struct sof_mux_config *config; - size_t blob_size; int ret; - comp_dbg(dev, "mux_prepare()"); - - config = comp_get_data_blob(cd->model_handler, &blob_size, NULL); - if (blob_size > MUX_BLOB_MAX_SIZE) { - comp_err(dev, "illegal blob size %zu", blob_size); - return -EINVAL; - } - - memcpy_s(&cd->config, MUX_BLOB_MAX_SIZE, config, blob_size); + comp_dbg(dev, "entry"); ret = mux_params(mod); if (ret < 0) @@ -431,7 +418,7 @@ static int mux_get_config(struct processing_module *mod, struct sof_ipc_ctrl_data *cdata = (struct sof_ipc_ctrl_data *)fragment; struct comp_data *cd = module_get_private_data(mod); - comp_dbg(mod->dev, "mux_get_config()"); + comp_dbg(mod->dev, "entry"); return comp_data_blob_get_cmd(cd->model_handler, cdata, fragment_size); } @@ -443,7 +430,7 @@ static int mux_set_config(struct processing_module *mod, uint32_t config_id, { struct comp_data *cd = module_get_private_data(mod); - comp_dbg(mod->dev, "mux_set_config()"); + comp_dbg(mod->dev, "entry"); return comp_data_blob_set(cd->model_handler, pos, data_offset_size, fragment, fragment_size); diff --git a/src/audio/mux/mux.h b/src/audio/mux/mux.h index 526d70ecb870..0fb11d8300b5 100644 --- a/src/audio/mux/mux.h +++ b/src/audio/mux/mux.h @@ -213,7 +213,11 @@ void sys_comp_module_demux_interface_init(void); #endif /* UNIT_TEST */ #define MUX_BLOB_STREAMS_SIZE (MUX_MAX_STREAMS * sizeof(struct mux_stream_data)) +#ifdef CONFIG_IPC_MAJOR_4 +#define MUX_BLOB_MAX_SIZE (sizeof(struct mux_data)) +#else #define MUX_BLOB_MAX_SIZE (sizeof(struct sof_mux_config) + MUX_BLOB_STREAMS_SIZE) +#endif extern const struct sof_uuid demux_uuid; extern struct tr_ctx mux_tr; diff --git a/src/audio/mux/mux_generic.c b/src/audio/mux/mux_generic.c index 991b79097fec..20d21afdffb1 100644 --- a/src/audio/mux/mux_generic.c +++ b/src/audio/mux/mux_generic.c @@ -171,7 +171,7 @@ static void demux_s16le(struct comp_dev *dev, struct audio_stream *sink, uint32_t elem; uint32_t frames_without_wrap; - comp_dbg(dev, "demux_s16le()"); + comp_dbg(dev, "entry"); if (!lookup || !lookup->num_elems) return; @@ -224,7 +224,7 @@ static void mux_s16le(struct comp_dev *dev, struct audio_stream *sink, uint32_t elem; uint32_t frames_without_wrap; - comp_dbg(dev, "mux_s16le()"); + comp_dbg(dev, "entry"); if (!lookup || !lookup->num_elems) return; @@ -371,7 +371,7 @@ static void demux_s32le(struct comp_dev *dev, struct audio_stream *sink, uint32_t elem; uint32_t frames_without_wrap; - comp_dbg(dev, "demux_s32le"); + comp_dbg(dev, "entry"); if (!lookup || !lookup->num_elems) return; @@ -424,7 +424,7 @@ static void mux_s32le(struct comp_dev *dev, struct audio_stream *sink, uint32_t elem; uint32_t frames_without_wrap; - comp_dbg(dev, "mux_s32le()"); + comp_dbg(dev, "entry"); if (!lookup || !lookup->num_elems) return; diff --git a/src/audio/mux/mux_ipc3.c b/src/audio/mux/mux_ipc3.c index 4a39dd277012..fb00030b5814 100644 --- a/src/audio/mux/mux_ipc3.c +++ b/src/audio/mux/mux_ipc3.c @@ -9,6 +9,7 @@ #include <sof/audio/module_adapter/module/generic.h> #include <sof/audio/component.h> +#include <sof/audio/data_blob.h> #include <module/module/base.h> #include <sof/trace/trace.h> #include <rtos/string_macro.h> @@ -38,7 +39,7 @@ static int mux_set_values(struct processing_module *mod) unsigned int i; unsigned int j; - comp_dbg(dev, "mux_set_values()"); + comp_dbg(dev, "entry"); /* check if number of streams configured doesn't exceed maximum */ if (cfg->num_streams > MUX_MAX_STREAMS) { @@ -98,6 +99,18 @@ static int mux_set_values(struct processing_module *mod) int mux_params(struct processing_module *mod) { + struct comp_data *cd = module_get_private_data(mod); + struct sof_mux_config *config; + size_t blob_size; + + config = comp_get_data_blob(cd->model_handler, &blob_size, NULL); + if (blob_size > MUX_BLOB_MAX_SIZE) { + comp_err(mod->dev, "illegal blob size %zu", blob_size); + return -EINVAL; + } + + memcpy_s(&cd->config, MUX_BLOB_MAX_SIZE, config, blob_size); + return mux_set_values(mod); } #endif /* CONFIG_COMP_MUX */ diff --git a/src/audio/mux/mux_ipc4.c b/src/audio/mux/mux_ipc4.c index 9d844ac15efa..dc6877059f57 100644 --- a/src/audio/mux/mux_ipc4.c +++ b/src/audio/mux/mux_ipc4.c @@ -11,6 +11,7 @@ #include <sof/audio/audio_stream.h> #include <sof/audio/component.h> #include <sof/audio/buffer.h> +#include <sof/audio/data_blob.h> #include <sof/trace/trace.h> #include <sof/lib/uuid.h> #include <sof/list.h> @@ -31,7 +32,7 @@ SOF_DEFINE_REG_UUID(demux); DECLARE_TR_CTX(demux_tr, SOF_UUID(demux_uuid), LOG_LEVEL_INFO); -static int build_config(struct processing_module *mod) +static int build_config(struct processing_module *mod, struct mux_data *cfg) { struct comp_dev *dev = mod->dev; struct comp_data *cd = module_get_private_data(mod); @@ -45,12 +46,12 @@ static int build_config(struct processing_module *mod) memset(cd->config.streams[i].mask, 0, sizeof(cd->config.streams[i].mask)); /* Setting masks for streams */ - for (i = 0; i < cd->md.base_cfg.audio_fmt.channels_count; i++) { + for (i = 0; i < cfg->base_cfg.audio_fmt.channels_count; i++) { cd->config.streams[0].mask[i] = mask; mask <<= 1; } - for (i = 0; i < cd->md.reference_format.channels_count; i++) { + for (i = 0; i < cfg->reference_format.channels_count; i++) { cd->config.streams[1].mask[i] = mask; mask <<= 1; } @@ -67,7 +68,7 @@ static int build_config(struct processing_module *mod) * set up param then verify param. BTW for IPC3 path, the param is sent by * host driver. */ -static void set_mux_params(struct processing_module *mod) +static void set_mux_params(struct processing_module *mod, struct mux_data *cfg) { struct sof_ipc_stream_params *params = mod->stream_params; struct comp_data *cd = module_get_private_data(mod); @@ -76,12 +77,12 @@ static void set_mux_params(struct processing_module *mod) int j; params->direction = dev->direction; - params->channels = cd->md.base_cfg.audio_fmt.channels_count; - params->rate = cd->md.base_cfg.audio_fmt.sampling_frequency; - params->sample_container_bytes = cd->md.base_cfg.audio_fmt.depth / 8; - params->sample_valid_bytes = cd->md.base_cfg.audio_fmt.valid_bit_depth / 8; - params->buffer_fmt = cd->md.base_cfg.audio_fmt.interleaving_style; - params->buffer.size = cd->md.base_cfg.ibs; + params->channels = cfg->base_cfg.audio_fmt.channels_count; + params->rate = cfg->base_cfg.audio_fmt.sampling_frequency; + params->sample_container_bytes = cfg->base_cfg.audio_fmt.depth / 8; + params->sample_valid_bytes = cfg->base_cfg.audio_fmt.valid_bit_depth / 8; + params->buffer_fmt = cfg->base_cfg.audio_fmt.interleaving_style; + params->buffer.size = cfg->base_cfg.ibs; params->no_stream_position = 1; /* There are two input pins and one output pin in the mux. @@ -95,7 +96,7 @@ static void set_mux_params(struct processing_module *mod) sink = comp_dev_get_first_data_consumer(dev); if (!audio_buffer_hw_params_configured(&sink->audio_buffer)) { - ipc4_update_buffer_format(sink, &cd->md.output_format); + ipc4_update_buffer_format(sink, &cfg->output_format); params->frame_fmt = audio_stream_get_frm_fmt(&sink->stream); } } @@ -105,12 +106,12 @@ static void set_mux_params(struct processing_module *mod) struct ipc4_audio_format *audio_fmt; comp_dev_for_each_producer(dev, source) { - j = buf_get_id(source); + j = IPC4_SINK_QUEUE_ID(buf_get_id(source)); cd->config.streams[j].pipeline_id = buffer_pipeline_id(source); if (j == BASE_CFG_QUEUED_ID) - audio_fmt = &cd->md.base_cfg.audio_fmt; + audio_fmt = &cfg->base_cfg.audio_fmt; else - audio_fmt = &cd->md.reference_format; + audio_fmt = &cfg->reference_format; ipc4_update_buffer_format(source, audio_fmt); } @@ -121,13 +122,22 @@ static void set_mux_params(struct processing_module *mod) int mux_params(struct processing_module *mod) { + struct comp_data *cd = module_get_private_data(mod); + struct mux_data *cfg; + size_t blob_size; int ret; - ret = build_config(mod); + cfg = comp_get_data_blob(cd->model_handler, &blob_size, NULL); + if (!cfg || blob_size > MUX_BLOB_MAX_SIZE) { + comp_err(mod->dev, "illegal blob size %zu", blob_size); + return -EINVAL; + } + + ret = build_config(mod, cfg); if (ret < 0) return ret; - set_mux_params(mod); + set_mux_params(mod, cfg); return ret; } diff --git a/src/audio/nxp/README.md b/src/audio/nxp/README.md new file mode 100644 index 000000000000..b8a59e0256aa --- /dev/null +++ b/src/audio/nxp/README.md @@ -0,0 +1,10 @@ +# NXP Specific Components Architecture + +## Overview + +This directory contains DSP components tailored for NXP hardware acceleration or special features. + +## Configuration and Scripts + +- **Kconfig**: Manages the proprietary NXP Essential Audio Processing blocks (`COMP_NXP_EAP`), aiming to enhance tonal and spatial audio perception. Also supports testing through a stub mechanism (`COMP_NXP_EAP_STUB`). +- **CMakeLists.txt**: Configures external NXP SDK directories and intelligently links the `eap.c` wrapper either against the stub logic or the authentic `libEAP16_3_0_13_FP1_RT600.a` static library blob. diff --git a/src/audio/nxp/eap.c b/src/audio/nxp/eap.c index 4e6e2c5eb535..3e596a5cf85d 100644 --- a/src/audio/nxp/eap.c +++ b/src/audio/nxp/eap.c @@ -5,7 +5,6 @@ // Author: Daniel Baluta <daniel.baluta@nxp.com> #include <rtos/panic.h> -#include <rtos/alloc.h> #include <rtos/cache.h> #include <rtos/init.h> #include <rtos/string.h> @@ -93,9 +92,9 @@ static int nxp_eap_init(struct processing_module *mod) tr_info(mod->dev, "NXP EAP library, platform: %s version:%s", info.pPlatform, info.pVersionNumber); - eap = rballoc(SOF_MEM_FLAG_USER, sizeof(*eap)); + eap = mod_alloc(mod, sizeof(*eap)); if (!eap) { - comp_err(dev, "nxp_eap_init() failed to allocate module private data"); + comp_err(dev, "failed to allocate module private data"); return -ENOMEM; } @@ -105,8 +104,8 @@ static int nxp_eap_init(struct processing_module *mod) lvm_ret = LVM_GetMemoryTable(LVM_NULL, &eap->mem_tab, &eap->inst_params); if (lvm_ret != LVM_SUCCESS) { - comp_err(dev, "nxp_eap_init() failed to get memory table %d", lvm_ret); - rfree(eap); + comp_err(dev, "failed to get memory table %d", lvm_ret); + mod_free(mod, eap); return -EINVAL; } @@ -115,10 +114,9 @@ static int nxp_eap_init(struct processing_module *mod) eap->mem_tab.Region[i].pBaseAddress = NULL; for (int i = 0; i < LVM_NR_MEMORY_REGIONS; i++) { - eap->mem_tab.Region[i].pBaseAddress = rballoc(SOF_MEM_FLAG_USER, - eap->mem_tab.Region[i].Size); + eap->mem_tab.Region[i].pBaseAddress = mod_balloc(mod, eap->mem_tab.Region[i].Size); if (!eap->mem_tab.Region[i].pBaseAddress) { - comp_err(dev, "nxp_eap_init() failed to allocate memory for region %d", i); + comp_err(dev, "failed to allocate memory for region %d", i); ret = -ENOMEM; goto free_mem; } @@ -126,7 +124,7 @@ static int nxp_eap_init(struct processing_module *mod) lvm_ret = LVM_GetInstanceHandle(&eap->instance, &eap->mem_tab, &eap->inst_params); if (lvm_ret != LVM_SUCCESS) { - comp_err(dev, "nxp_eap_init() failed to get instance handle err: %d", lvm_ret); + comp_err(dev, "failed to get instance handle err: %d", lvm_ret); ret = -EINVAL; goto free_mem; } @@ -139,11 +137,11 @@ static int nxp_eap_init(struct processing_module *mod) free_mem: for (int i = 0; i < LVM_NR_MEMORY_REGIONS; i++) { if (eap->mem_tab.Region[i].pBaseAddress) { - rfree(eap->mem_tab.Region[i].pBaseAddress); + mod_free(mod, eap->mem_tab.Region[i].pBaseAddress); eap->mem_tab.Region[i].pBaseAddress = NULL; } } - rfree(eap); + mod_free(mod, eap); return ret; } @@ -152,16 +150,15 @@ static int nxp_eap_free(struct processing_module *mod) struct comp_dev *dev = mod->dev; struct nxp_eap_data *eap = module_get_private_data(mod); - comp_dbg(dev, "nxp_eap_free()"); + comp_dbg(dev, "entry"); for (int i = 0; i < LVM_NR_MEMORY_REGIONS; i++) { if (eap->mem_tab.Region[i].pBaseAddress) { - rfree(eap->mem_tab.Region[i].pBaseAddress); + mod_free(mod, eap->mem_tab.Region[i].pBaseAddress); eap->mem_tab.Region[i].pBaseAddress = NULL; } } - - rfree(eap); + mod_free(mod, eap); return 0; } @@ -176,7 +173,7 @@ static int nxp_eap_prepare(struct processing_module *mod, struct comp_buffer *source = comp_dev_get_first_data_producer(dev); const struct audio_stream *stream; - comp_dbg(dev, "nxp_eap_prepare()"); + comp_dbg(dev, "entry"); stream = &source->stream; eap->sample_rate = audio_stream_get_rate(stream); @@ -189,13 +186,13 @@ static int nxp_eap_prepare(struct processing_module *mod, */ eap->buffer_bytes = NXP_EAP_DEFAULT_MAX_BLOCK_SIZE; - md->mpd.in_buff = rballoc_align(SOF_MEM_FLAG_USER, eap->buffer_bytes, 32); + md->mpd.in_buff = mod_balloc_align(mod, eap->buffer_bytes, 32); if (!md->mpd.in_buff) return -ENOMEM; - md->mpd.out_buff = rballoc_align(SOF_MEM_FLAG_USER, eap->buffer_bytes, 32); + md->mpd.out_buff = mod_balloc_align(mod, eap->buffer_bytes, 32); if (!md->mpd.out_buff) { - rfree(md->mpd.in_buff); + mod_free(mod, md->mpd.in_buff); return -ENOMEM; } @@ -210,16 +207,16 @@ static int nxp_eap_reset(struct processing_module *mod) struct comp_dev *dev = mod->dev; struct module_data *md = &mod->priv; - comp_dbg(dev, "nxp_eap_reset"); + comp_dbg(dev, "entry"); if (md->mpd.in_buff) { - rfree(md->mpd.in_buff); + mod_free(mod, md->mpd.in_buff); md->mpd.in_buff = NULL; md->mpd.in_buff_size = 0; } if (md->mpd.out_buff) { - rfree(md->mpd.out_buff); + mod_free(mod, md->mpd.out_buff); md->mpd.out_buff = NULL; md->mpd.out_buff_size = 0; } @@ -237,7 +234,7 @@ static int nxp_eap_process(struct processing_module *mod, LVM_INT16 *buffer_table[2]; LVM_ReturnStatus_en ret; - comp_dbg(dev, "nxp_eap_process()"); + comp_dbg(dev, "entry"); /* we need to input buffer to be completely full to be able to process it */ if (input_buffers[0].size < eap->mpd.in_buff_size) @@ -257,7 +254,7 @@ static int nxp_eap_process(struct processing_module *mod, (LVM_INT16 **)buffer_table, eap->mpd.avail / eap_data->frame_bytes, eap_data->audio_time_ms); if (ret != LVM_SUCCESS) { - comp_err(dev, "nxp_eap_process() failed with error %d", ret); + comp_err(dev, "failed with error %d", ret); return -EIO; } @@ -283,7 +280,7 @@ static int nxp_eap_cmd_set_value(struct processing_module *mod, struct sof_ipc_c index = cdata->chanv[0].value; if (index >= ARRAY_SIZE(nxp_eap_effect_presets)) { - comp_info(dev, "nxp_eap_cmd_set_value() invalid index (%d), config not changed", + comp_info(dev, "invalid index (%d), config not changed", index); } else { memcpy(&eap->ctrl_params, nxp_eap_effect_presets[index].params, @@ -307,7 +304,7 @@ static int nxp_eap_set_config(struct processing_module *mod, uint32_t param_id, struct comp_dev *dev = mod->dev; struct sof_ipc_ctrl_data *cdata = (struct sof_ipc_ctrl_data *)fragment; - comp_dbg(dev, "nxp_eap_set_config()"); + comp_dbg(dev, "entry"); if (cdata->cmd != SOF_CTRL_CMD_BINARY) return nxp_eap_cmd_set_value(mod, cdata); @@ -322,7 +319,7 @@ static int nxp_eap_get_config(struct processing_module *mod, { struct comp_dev *dev = mod->dev; - comp_dbg(dev, "nxp_eap_get_config()"); + comp_dbg(dev, "entry"); return 0; } diff --git a/src/audio/pcm_converter/README.md b/src/audio/pcm_converter/README.md new file mode 100644 index 000000000000..d9fd8152383a --- /dev/null +++ b/src/audio/pcm_converter/README.md @@ -0,0 +1,11 @@ +# PCM Format Converter Architecture + +This directory provides PCM format conversion. + +## Overview + +Handles conversions like 16-bit to 24-bit, interleaved to non-interleaved, and other basic PCM layout translations. + +## Configuration and Scripts + +- **CMakeLists.txt**: Organizes standard and highly performant HIFI implementations (`pcm_converter.c`, `pcm_converter_generic.c`, `pcm_converter_hifi3.c`). Includes optional support for `pcm_remap.c` dependent on the `CONFIG_PCM_REMAPPING_CONVERTERS` flag. diff --git a/src/audio/pipeline/README.md b/src/audio/pipeline/README.md new file mode 100644 index 000000000000..4e530bdc1463 --- /dev/null +++ b/src/audio/pipeline/README.md @@ -0,0 +1,245 @@ +# Pipeline Engine Architecture + +This directory contains the core graph/pipeline logic. + +## Overview + +The Pipeline engine is the heart of the SOF processing architecture. It links `components` together using `buffers`, and provides scheduling execution entities (tasks) to repeatedly trigger these component graphs. + +## Architecture Diagram + +```mermaid +graph TD + Host[Host Topology] -.-> Manager[Pipeline Manager] + Manager --> Buf1[Buffer] + Manager --> CompA[Component] + Manager --> Sched[Task Scheduler] + Sched --> CompA +``` + +## State Machine + +The pipeline progresses through various states (`COMP_STATE_INIT`, `COMP_STATE_READY`, `COMP_STATE_ACTIVE`, etc.) largely directed by `comp_trigger()` commands cascaded down the graph. + +```mermaid +stateDiagram-v2 + [*] --> INIT : pipeline_new() + INIT --> READY : pipeline_complete() + READY --> PRE_ACTIVE : COMP_TRIGGER_PRE_START + PRE_ACTIVE --> ACTIVE : COMP_TRIGGER_START + ACTIVE --> PAUSED : COMP_TRIGGER_PAUSE + PAUSED --> ACTIVE : COMP_TRIGGER_RELEASE / START + ACTIVE --> SUSPEND : COMP_TRIGGER_SUSPEND + SUSPEND --> ACTIVE : COMP_TRIGGER_RESUME + ACTIVE --> PRE_ACTIVE : COMP_TRIGGER_PRE_RELEASE + ACTIVE --> READY : COMP_TRIGGER_STOP + ACTIVE --> XRUN_PAUSED : COMP_TRIGGER_XRUN + XRUN_PAUSED --> READY : pipeline_xrun_recover() +``` + +## Processing Flow (LL and DP Modes) + +Execution within the SOF pipeline is divided between two primary timing domains depending on the component's `proc_domain` property. + +1. **Low Latency (LL) Domain:** + * **Driven By:** DMA interrupts or precise timers. + * **Execution:** A single cooperative scheduler task (`pipeline_task`) iterates over the entire connected graph. + * **Process:** The pipeline scheduler invokes `pipeline_copy()` which calls `comp_copy()` on the source or sink, and then recursively relies on `pipeline_for_each_comp` to pull or push data through the graph synchronously within that single timeslice. + +2. **Data Processing (DP) Domain:** + * **Driven By:** A Zephyr-based discrete RTOS thread (`CONFIG_ZEPHYR_DP_SCHEDULER`). + * **Execution:** Modules that require extensive computation (especially those leveraging the `module_adapter`) are spun off into their own isolated threads using `pipeline_comp_dp_task_init()`. + * **Process:** Instead of synchronous execution alongside the DMA, they consume and produce data (`module_process_sink_src`) with their own stack (`TASK_DP_STACK_SIZE`), relying on inter-component buffers and thread synchronization to pass chunks back to the LL domain when ready. + +```mermaid +sequenceDiagram + participant DMA + participant Sched as LL Scheduler (pipeline_task) + participant Source as Source Component (e.g., Host Interface) + participant Buffer as Internal Buffer + participant Filter as Filter Component (e.g., EQ) + participant Sink as Sink Component (e.g., DAI) + + DMA->>Sched: Interrupt / Timer Tick + activate Sched + Sched->>Source: pipeline_copy() -> comp_copy() + Source-->>Buffer: Copies PCM Data to Buffer + Sched->>Filter: pipeline_for_each_comp() -> comp_copy() + Filter->>Buffer: Reads PCM Data + Filter-->>Buffer: Writes Processed Data + Sched->>Sink: pipeline_for_each_comp() -> comp_copy() + Sink->>Buffer: Reads Processed Data + Sink-->>Sink: Transmits to Audio Hardware + deactivate Sched +``` + +### Mixed LL and DP Execution + +```mermaid +sequenceDiagram + participant DMA + participant Sched as LL Scheduler (pipeline_task) + participant DPThread as DP Thread (dp_task_run) + participant CompLL as LL Component + participant CompDP as DP Component + + DMA->>Sched: Interrupt / Timer Tick + activate Sched + Sched->>CompLL: pipeline_copy() -> comp_copy() + CompLL-->>Sched: Data Produced into Buffer + Sched->>DPThread: Wakeup (Buffer Data Available) + deactivate Sched + + activate DPThread + DPThread->>CompDP: module_process_sink_src() + CompDP-->>DPThread: Computations finished + DPThread->>Sched: Notification (Data Ready) + deactivate DPThread +``` + +## Control and Configuration Flows + +The lifecycle of a pipeline graph involves several discrete initialization and connection steps orchestrated by IPC topology commands before streaming begins. + +1. **Creation (`pipeline_new`)**: Instantiates the `struct pipeline` object, associates the memory heap, and grabs a mailbox offset for IPC position tracking. +2. **Connection (`pipeline_connect` / `pipeline_disconnect`)**: Establishes the directional edges of the graph by attaching a `comp_buffer` between a source and sink `comp_dev`. It updates the component's internal buffer lists. +3. **Completion (`pipeline_complete`)**: Validates the graph structure. It recursively walks the entire chain from source to sink (`pipeline_for_each_comp`), verifying consistency (e.g., ensuring components aren't part of mismatched pipelines unless properly handled), and transitions the pipeline state to `COMP_STATE_READY`. +4. **Parameter Propagation (`pipeline_params`, `pipeline_prepare`)**: Triggered prior to streaming, `pipeline_prepare()` walks the graph to finalize PCM formats, period sizes, and hardware configurations (like iterating over the audio buffers to `audio_buffer_reset_params`). +5. **Teardown (`pipeline_free`)**: When a stream is closed, after all `comp_dev` objects internal to the pipeline are halted and detached, `pipeline_free` cleans up the `pipe_task` scheduler footprint, IPC messages, and unlinks memory allocations freeing the `struct pipeline` entirely. + +### Creation and Teardown Flow + +```mermaid +sequenceDiagram + participant Host + participant Sched as LL Scheduler + participant Pipe as Pipeline + participant Source as Source Component + + Host->>Pipe: pipeline_new() + activate Pipe + Pipe-->>Pipe: Allocates struct pipeline & gets Mailbox Offset + + Host->>Source: comp_new() (Creates Components) + + Host->>Pipe: pipeline_connect() + Pipe-->>Pipe: Links comp_buffers internally + + Host->>Pipe: pipeline_complete() + Pipe->>Source: pipeline_for_each_comp() + Source-->>Pipe: Graph validates + Pipe-->>Host: Status: COMP_STATE_READY + + Note over Host,Pipe: ... Active Audio Streaming ... + + Host->>Pipe: pipeline_free() + Pipe->>Sched: schedule_task_free(pipe_task) + Pipe-->>Pipe: sof_heap_free() (Frees tracking structs) + Pipe-->>Host: Pipeline Extinguished + deactivate Pipe +``` + +### Triggering Flow + +Triggering is fundamentally responsible for transitioning graph states (`COMP_STATE_ACTIVE`, `COMP_STATE_PAUSED`, etc). A trigger (like `COMP_TRIGGER_START` or `COMP_TRIGGER_STOP`) commands an underlying state change and pushes the `pipeline_task` into the `schedule_task` queue. + +```mermaid +sequenceDiagram + participant Host + participant PPL as Pipeline (pipeline_trigger) + participant Sched as LL Scheduler (pipeline_task) + participant Source as Source Component + participant Sink as Sink Component + + Host->>PPL: pipeline_trigger(COMP_TRIGGER_START) + activate PPL + PPL->>Source: pipeline_for_each_comp(COMP_TRIGGER_START) + Source->>Sink: comp_trigger(COMP_TRIGGER_START) + Sink-->>PPL: State -> COMP_STATE_ACTIVE + + PPL->>Sched: pipeline_schedule_copy() + Sched-->>Sched: Adds `pipe_task` to active scheduler + PPL-->>Host: Trigger successful + deactivate PPL + + Note over PPL,Sched: Pipeline repeatedly scheduled via DMA or Timers + + Host->>PPL: pipeline_trigger(COMP_TRIGGER_STOP) + activate PPL + PPL->>Sched: pipeline_schedule_cancel() + Sched-->>Sched: Removes `pipe_task` from active scheduler + PPL->>Source: pipeline_for_each_comp(COMP_TRIGGER_STOP) + Source->>Sink: comp_trigger(COMP_TRIGGER_STOP) + Sink-->>PPL: State -> COMP_STATE_PAUSED / READY + PPL-->>Host: Trigger successful + deactivate PPL +``` + +```mermaid +sequenceDiagram + participant Host + participant Pipe as Pipeline (pipeline_complete) + participant Source as Source Component + participant Sink as Sink Component + participant Buf as Buffer + + Host->>Pipe: ipc_pipeline_complete() + activate Pipe + Pipe->>Source: pipeline_for_each_comp(PPL_DIR_DOWNSTREAM) + Source->>Buf: buffer_set_comp() + Buf->>Sink: comp_is_single_pipeline() + Sink-->>Pipe: Pipeline graph linked + Pipe-->>Host: Status: COMP_STATE_READY + deactivate Pipe + + Host->>Pipe: ipc_comp_prepare() + activate Pipe + Pipe->>Source: pipeline_prepare() + Source->>Buf: audio_buffer_reset_params() + Buf->>Sink: comp_prepare() + Sink-->>Pipe: Prepared formats + Pipe-->>Host: Prepared + deactivate Pipe +``` + +## Error Handling (XRUNs) + +Overruns (host writes too fast/firmware reads too slow) and underruns (host reads too fast/firmware writes too slow) are tracked continuously. + +1. **Detection**: Components directly hooked to interfaces (like a host IPC component or a hardware DAI) monitor their `comp_copy` status. If they detect starvation or overflow, they trigger an XRUN event. +2. **Propagation (`pipeline_xrun`)**: The pipeline immediately invokes a broadcast `pipeline_trigger(..., COMP_TRIGGER_XRUN)` forcing all internal components to drop to a halted `XRUN_PAUSED` state. In older IPC3 topologies, it additionally signals the host via mailbox offsets (`ipc_build_stream_posn`). +3. **Recovery (`pipeline_xrun_handle_trigger` / `pipeline_xrun_recover`)**: By default, the `pipeline_task` scheduler will intercept the `xrun_bytes` flag. Unless `NO_XRUN_RECOVERY` is defined, the firmware attempts self-healing: + * It resets the pipeline downstream of the source (`pipeline_reset`). + * It prepares it again (`pipeline_prepare`). + * It issues an internal `COMP_TRIGGER_START` to restart data flow automatically without host intervention. + +```mermaid +sequenceDiagram + participant DAI as Hardware Interface + participant Comp as Connected Component + participant PPL as Pipeline (pipeline_xrun) + participant Sched as LL Scheduler (pipeline_task) + participant Host as Host Interface + + DAI-->>Comp: Overflow/Underrun Detected + activate Comp + Comp->>PPL: pipeline_xrun() + PPL->>Comp: pipeline_trigger(COMP_TRIGGER_XRUN) + PPL-->>Host: Mailbox XRUN position (IPC3) + deactivate Comp + + Note over PPL,Sched: Pipeline halts (XRUN_PAUSED) + + Sched->>PPL: intercepts xrun_bytes > 0 + activate Sched + Sched->>PPL: pipeline_xrun_recover() + PPL->>Comp: pipeline_reset() + PPL->>Comp: pipeline_prepare() + PPL->>Comp: pipeline_trigger(COMP_TRIGGER_START) + PPL-->>Sched: Recovered (xrun_bytes = 0) + deactivate Sched +``` + +## Configuration and Scripts + +* **CMakeLists.txt**: Straightforward build configuration integrating the fundamental internal execution blocks of the SOF graph: `pipeline-graph.c`, `pipeline-stream.c`, `pipeline-params.c`, `pipeline-xrun.c`, and `pipeline-schedule.c`. diff --git a/src/audio/pipeline/pipeline-graph.c b/src/audio/pipeline/pipeline-graph.c index 678b8095289f..47d5d0127fd0 100644 --- a/src/audio/pipeline/pipeline-graph.c +++ b/src/audio/pipeline/pipeline-graph.c @@ -11,6 +11,7 @@ #include <sof/ipc/msg.h> #include <rtos/interrupt.h> #include <rtos/symbol.h> +#include <rtos/alloc.h> #include <sof/lib/mm_heap.h> #include <sof/lib/uuid.h> #include <sof/compiler_attributes.h> @@ -108,7 +109,8 @@ void pipeline_posn_init(struct sof *sof) } /* create new pipeline - returns pipeline id or negative error */ -struct pipeline *pipeline_new(uint32_t pipeline_id, uint32_t priority, uint32_t comp_id) +struct pipeline *pipeline_new(struct k_heap *heap, uint32_t pipeline_id, uint32_t priority, + uint32_t comp_id, struct create_pipeline_params *pparams) { struct sof_ipc_stream_posn posn; struct pipeline *p; @@ -121,13 +123,16 @@ struct pipeline *pipeline_new(uint32_t pipeline_id, uint32_t priority, uint32_t heap_trace_all(0); /* allocate new pipeline */ - p = rzalloc(SOF_MEM_FLAG_USER, sizeof(*p)); + p = sof_heap_alloc(heap, SOF_MEM_FLAG_USER, sizeof(*p), 0); if (!p) { pipe_cl_err("Out of Memory"); return NULL; } + memset(p, 0, sizeof(*p)); + /* init pipeline */ + p->heap = heap; p->comp_id = comp_id; p->priority = priority; p->pipeline_id = pipeline_id; @@ -160,7 +165,7 @@ struct pipeline *pipeline_new(uint32_t pipeline_id, uint32_t priority, uint32_t return p; free: - rfree(p); + sof_heap_free(heap, p); return NULL; } @@ -173,24 +178,40 @@ static void buffer_set_comp(struct comp_buffer *buffer, struct comp_dev *comp, comp_buffer_set_sink_component(buffer, comp); } +#ifdef CONFIG_SOF_USERSPACE_LL +#define PPL_LOCK_DECLARE +#define PPL_LOCK() do { \ + int ret = sys_mutex_lock(&comp->list_mutex, K_FOREVER); \ + assert(ret == 0); \ + } while (0) +#define PPL_UNLOCK() do { \ + int ret = sys_mutex_unlock(&comp->list_mutex); \ + assert(ret == 0); \ + } while (0) +#else +#define PPL_LOCK_DECLARE uint32_t flags +#define PPL_LOCK() irq_local_disable(flags) +#define PPL_UNLOCK() irq_local_enable(flags) +#endif + int pipeline_connect(struct comp_dev *comp, struct comp_buffer *buffer, int dir) { struct list_item *comp_list; - uint32_t flags; + PPL_LOCK_DECLARE; if (dir == PPL_CONN_DIR_COMP_TO_BUFFER) comp_info(comp, "connect buffer %d as sink", buf_get_id(buffer)); else comp_info(comp, "connect buffer %d as source", buf_get_id(buffer)); - irq_local_disable(flags); + PPL_LOCK(); comp_list = comp_buffer_list(comp, dir); buffer_attach(buffer, comp_list, dir); buffer_set_comp(buffer, comp, dir); - irq_local_enable(flags); + PPL_UNLOCK(); return 0; } @@ -198,26 +219,26 @@ int pipeline_connect(struct comp_dev *comp, struct comp_buffer *buffer, void pipeline_disconnect(struct comp_dev *comp, struct comp_buffer *buffer, int dir) { struct list_item *comp_list; - uint32_t flags; + PPL_LOCK_DECLARE; if (dir == PPL_CONN_DIR_COMP_TO_BUFFER) comp_dbg(comp, "disconnect buffer %d as sink", buf_get_id(buffer)); else comp_dbg(comp, "disconnect buffer %d as source", buf_get_id(buffer)); - irq_local_disable(flags); + PPL_LOCK(); comp_list = comp_buffer_list(comp, dir); buffer_detach(buffer, comp_list, dir); buffer_set_comp(buffer, NULL, dir); - irq_local_enable(flags); + PPL_UNLOCK(); } /* pipelines must be inactive */ int pipeline_free(struct pipeline *p) { - pipe_dbg(p, "pipeline_free()"); + pipe_dbg(p, "entry"); /* * pipeline_free should always be called only after all the widgets in the pipeline have @@ -229,7 +250,7 @@ int pipeline_free(struct pipeline *p) #if !CONFIG_LIBRARY || UNIT_TEST schedule_task_free(p->pipe_task); #endif - rfree(p->pipe_task); + sof_heap_free(p->heap, p->pipe_task); } ipc_msg_free(p->msg); @@ -237,7 +258,7 @@ int pipeline_free(struct pipeline *p) pipeline_posn_offset_put(p->posn_offset); /* now free the pipeline */ - rfree(p); + sof_heap_free(p->heap, p); /* show heap status */ heap_trace_all(0); @@ -251,11 +272,11 @@ static int pipeline_comp_complete(struct comp_dev *current, { struct pipeline_data *ppl_data = ctx->comp_data; - pipe_dbg(ppl_data->p, "pipeline_comp_complete(), current->comp.id = 0x%x, dir = %u", + pipe_dbg(ppl_data->p, "current->comp.id = 0x%x, dir = %u", dev_comp_id(current), dir); if (!comp_is_single_pipeline(current, ppl_data->start)) { - pipe_dbg(ppl_data->p, "pipeline_comp_complete(), current is from another pipeline"); + pipe_dbg(ppl_data->p, "current is from another pipeline"); return 0; } @@ -326,7 +347,7 @@ static int pipeline_comp_reset(struct comp_dev *current, int is_single_ppl; int err; - pipe_dbg(p_current, "pipeline_comp_reset(), current->comp.id = 0x%x, dir = %u", + pipe_dbg(p_current, "current->comp.id = 0x%x, dir = %u", dev_comp_id(current), dir); if (!p->source_comp) { diff --git a/src/audio/pipeline/pipeline-params.c b/src/audio/pipeline/pipeline-params.c index f2aa30b978c1..16273e0897d4 100644 --- a/src/audio/pipeline/pipeline-params.c +++ b/src/audio/pipeline/pipeline-params.c @@ -31,7 +31,7 @@ static int pipeline_comp_params_neg(struct comp_dev *current, struct pipeline_data *ppl_data = ctx->comp_data; int err = 0; - pipe_dbg(current->pipeline, "pipeline_comp_params_neg(), current->comp.id = 0x%x, dir = %u", + pipe_dbg(current->pipeline, "current->comp.id = 0x%x, dir = %u", dev_comp_id(current), dir); /* check if 'current' is already configured */ @@ -81,7 +81,7 @@ static int pipeline_comp_params(struct comp_dev *current, int stream_direction = ppl_data->params->params.direction; int err; - pipe_dbg(current->pipeline, "pipeline_comp_params(), current->comp.id = 0x%x, dir = %u", + pipe_dbg(current->pipeline, "current->comp.id = 0x%x, dir = %u", dev_comp_id(current), dir); /* Don't propagate to pipelines in the opposite direction */ @@ -131,7 +131,7 @@ static int pipeline_comp_hw_params(struct comp_dev *current, struct pipeline_data *ppl_data = ctx->comp_data; int ret; - pipe_dbg(current->pipeline, "pipeline_comp_hw_params(), current->comp.id = 0x%x, dir = %u", + pipe_dbg(current->pipeline, "current->comp.id = 0x%x, dir = %u", dev_comp_id(current), dir); ret = pipeline_for_each_comp(current, ctx, dir); @@ -262,7 +262,7 @@ static int pipeline_comp_prepare(struct comp_dev *current, struct pipeline_data *ppl_data = ctx->comp_data; int err; - pipe_dbg(current->pipeline, "pipeline_comp_prepare(), current->comp.id = 0x%x, dir = %u", + pipe_dbg(current->pipeline, "current->comp.id = 0x%x, dir = %u", dev_comp_id(current), dir); if (!comp_is_single_pipeline(current, ppl_data->start)) { diff --git a/src/audio/pipeline/pipeline-schedule.c b/src/audio/pipeline/pipeline-schedule.c index a13e95982ccc..45fd1eed639c 100644 --- a/src/audio/pipeline/pipeline-schedule.c +++ b/src/audio/pipeline/pipeline-schedule.c @@ -8,6 +8,7 @@ #include <sof/audio/buffer.h> #include <sof/audio/component_ext.h> #include <sof/audio/pipeline.h> +#include <rtos/alloc.h> #include <rtos/interrupt.h> #include <sof/lib/agent.h> #include <sof/list.h> @@ -167,7 +168,7 @@ static enum task_state pipeline_task(void *arg) struct pipeline *p = arg; int err; - pipe_dbg(p, "pipeline_task()"); + pipe_dbg(p, "entry"); /* are we in xrun ? */ if (p->xrun_bytes) { @@ -232,7 +233,7 @@ static enum task_state pipeline_task(void *arg) } } - pipe_dbg(p, "pipeline_task() sched"); + pipe_dbg(p, "sched"); return SOF_TASK_STATE_RESCHEDULE; } @@ -241,15 +242,17 @@ static struct task *pipeline_task_init(struct pipeline *p, uint32_t type) { struct pipeline_task *task = NULL; - task = rzalloc(SOF_MEM_FLAG_USER, - sizeof(*task)); + task = sof_heap_alloc(p->heap, SOF_MEM_FLAG_USER, + sizeof(*task), 0); if (!task) return NULL; + memset(task, 0, sizeof(*task)); + if (schedule_task_init_ll(&task->task, SOF_UUID(pipe_task_uuid), type, p->priority, pipeline_task, p, p->core, 0) < 0) { - rfree(task); + sof_heap_free(p->heap, task); return NULL; } @@ -383,7 +386,6 @@ static enum task_state dp_task_run(void *data) int pipeline_comp_dp_task_init(struct comp_dev *comp) { - int ret; /* DP tasks are guaranteed to have a module_adapter */ struct processing_module *mod = comp_mod(comp); struct task_ops ops = { @@ -392,22 +394,17 @@ int pipeline_comp_dp_task_init(struct comp_dev *comp) .complete = NULL }; - if (!comp->task) { - ret = scheduler_dp_task_init(&comp->task, - SOF_UUID(dp_task_uuid), - &ops, - mod, - comp->ipc_config.core, - TASK_DP_STACK_SIZE, -#if CONFIG_USERSPACE - mod->user_ctx ? K_USER : -#endif /* CONFIG_USERSPACE */ - 0); - if (ret < 0) - return ret; - } + if (comp->task) + return 0; - return 0; +#if CONFIG_SOF_USERSPACE_PROXY + unsigned int flags = mod->user_ctx ? K_USER : 0; +#else + unsigned int flags = IS_ENABLED(CONFIG_USERSPACE) ? K_USER : 0; +#endif + + return scheduler_dp_task_init(&comp->task, SOF_UUID(dp_task_uuid), &ops, mod, + comp->ipc_config.core, TASK_DP_STACK_SIZE, flags); } #endif /* CONFIG_ZEPHYR_DP_SCHEDULER */ diff --git a/src/audio/pipeline/pipeline-stream.c b/src/audio/pipeline/pipeline-stream.c index 6ababd12d838..8bfdfc182912 100644 --- a/src/audio/pipeline/pipeline-stream.c +++ b/src/audio/pipeline/pipeline-stream.c @@ -21,7 +21,6 @@ #include <rtos/kernel.h> #include <sof/audio/module_adapter/module/generic.h> #include <sof/lib/cpu-clk-manager.h> -#include <sof/ipc/notification_pool.h> #ifdef CONFIG_IPC_MAJOR_4 #include <ipc4/notification.h> @@ -96,14 +95,7 @@ pipeline_should_report_enodata_on_trigger(struct comp_dev *rsrc, void pipeline_comp_copy_error_notify(const struct comp_dev *component, int err) { #ifdef CONFIG_IPC_MAJOR_4 - struct ipc_msg *notify; - - notify = ipc_notification_pool_get(IPC4_RESOURCE_EVENT_SIZE); - if (!notify) - return; - - process_data_error_notif_msg_init(notify, component->ipc_config.id, err); - ipc_msg_send(notify, notify->tx_data, false); + send_process_data_error_notif_msg(component->ipc_config.id, err); #endif } @@ -115,17 +107,17 @@ static int pipeline_comp_copy(struct comp_dev *current, bool is_single_ppl = comp_is_single_pipeline(current, ppl_data->start); int err; - pipe_dbg(current->pipeline, "pipeline_comp_copy(), current->comp.id = %u, dir = %u", + pipe_dbg(current->pipeline, "current->comp.id = %u, dir = %u", dev_comp_id(current), dir); if (!is_single_ppl) { pipe_dbg(current->pipeline, - "pipeline_comp_copy(), current is from another pipeline and can't be scheduled together"); + "current is from another pipeline and can't be scheduled together"); return 0; } if (!comp_is_active(current)) { - pipe_dbg(current->pipeline, "pipeline_comp_copy(), current is not active"); + pipe_dbg(current->pipeline, "current is not active"); return 0; } @@ -239,7 +231,7 @@ static int pipeline_comp_list(struct comp_dev *current, */ if (!is_single_ppl && (!is_same_sched || IPC4_MOD_ID(current->ipc_config.id))) { pipe_dbg(current->pipeline, - "pipeline_comp_list(), current is from another pipeline"); + "current is from another pipeline"); return 0; } @@ -473,7 +465,7 @@ static int pipeline_comp_trigger(struct comp_dev *current, int err; pipe_dbg(current->pipeline, - "pipeline_comp_trigger(), current->comp.id = %u, dir = %u", + "current->comp.id = %u, dir = %u", dev_comp_id(current), dir); switch (ppl_data->cmd) { @@ -520,7 +512,7 @@ static int pipeline_comp_trigger(struct comp_dev *current, */ if (!is_single_ppl && (!is_same_sched || IS_ENABLED(CONFIG_IPC_MAJOR_4))) { pipe_dbg(current->pipeline, - "pipeline_comp_trigger(), current is from another pipeline"); + "current is from another pipeline"); if (pipeline_should_report_enodata_on_trigger(current, ctx, dir)) return -ENODATA; diff --git a/src/audio/pipeline/pipeline-xrun.c b/src/audio/pipeline/pipeline-xrun.c index 3b727074ab97..c535ddbb84a1 100644 --- a/src/audio/pipeline/pipeline-xrun.c +++ b/src/audio/pipeline/pipeline-xrun.c @@ -29,6 +29,8 @@ LOG_MODULE_DECLARE(pipe, CONFIG_SOF_LOG_LEVEL); */ #define NO_XRUN_RECOVERY 1 +#if CONFIG_IPC_MAJOR_3 + /* This function always returns success */ static int pipeline_comp_xrun(struct comp_dev *current, struct comp_buffer *calling_buf, @@ -49,6 +51,8 @@ static int pipeline_comp_xrun(struct comp_dev *current, return pipeline_for_each_comp(current, ctx, dir); } +#endif /* CONFIG_IPC_MAJOR_3 */ + #if NO_XRUN_RECOVERY /* recover the pipeline from a XRUN condition */ int pipeline_xrun_recover(struct pipeline *p) @@ -63,7 +67,7 @@ int pipeline_xrun_recover(struct pipeline *p) { int ret; - pipe_err(p, "pipeline_xrun_recover()"); + pipe_err(p, "entry"); /* prepare the pipeline */ ret = pipeline_prepare(p, p->source_comp); @@ -139,6 +143,7 @@ int pipeline_xrun_handle_trigger(struct pipeline *p, int cmd) void pipeline_xrun(struct pipeline *p, struct comp_dev *dev, int32_t bytes) { +#if CONFIG_IPC_MAJOR_3 struct pipeline_data data; struct pipeline_walk_context walk_ctx = { .comp_func = pipeline_comp_xrun, @@ -146,6 +151,7 @@ void pipeline_xrun(struct pipeline *p, struct comp_dev *dev, .skip_incomplete = true, }; struct sof_ipc_stream_posn posn; +#endif int ret; /* don't flood host */ @@ -162,6 +168,12 @@ void pipeline_xrun(struct pipeline *p, struct comp_dev *dev, pipe_err(p, "Pipelines notification about XRUN failed, ret = %d", ret); + /* + * The IPC position info reporting via window2 is only + * used for IPC3 and e.g. in IPC4 this is conflicting + * with the debug window usages (logging, debug, ..) + */ +#if CONFIG_IPC_MAJOR_3 memset(&posn, 0, sizeof(posn)); ipc_build_stream_posn(&posn, SOF_IPC_STREAM_TRIG_XRUN, dev_comp_id(dev)); @@ -172,4 +184,5 @@ void pipeline_xrun(struct pipeline *p, struct comp_dev *dev, data.p = p; walk_ctx.comp_func(dev, NULL, &walk_ctx, dev->direction); +#endif } diff --git a/src/audio/rtnr/README.md b/src/audio/rtnr/README.md new file mode 100644 index 000000000000..2dd6d718f15f --- /dev/null +++ b/src/audio/rtnr/README.md @@ -0,0 +1,13 @@ +# Real-Time Noise Reduction (RTNR) Architecture + +This directory houses the RTNR component. + +## Overview + +Reduces steady-state or transient background noise from an input signal path in real time. + +## Configuration and Scripts + +- **Kconfig**: Dictates the build behavior for the Realtek noise reduction and suppression components (`COMP_RTNR`). The feature depends heavily on specific, proprietary Realtek libraries (`libSOF_RTK_MA_API.a`, etc.). Configures stubs (`COMP_RTNR_STUB`) to circumvent library unavailability during testing and CI logic runs. +- **CMakeLists.txt**: Injects `rtnr.c` directly into Zephyr builds and gracefully falls back to Zephyr external libraries handling. Offers thorough `llext` generation and cleanly wraps stub testing modules `rtnr_stub.c`. +- **rtnr.toml**: Defines topology properties for the loadable RTNR logic (UUID binding, generic pinning setups). diff --git a/src/audio/rtnr/rtnr.c b/src/audio/rtnr/rtnr.c index 541f985753a1..45ff62a60f8d 100644 --- a/src/audio/rtnr/rtnr.c +++ b/src/audio/rtnr/rtnr.c @@ -74,27 +74,27 @@ void rtnr_printf(int a, int b, int c, int d, int e) { switch (a) { case 0xa: - tr_info(&rtnr_tr, "rtnr_printf 1st=%08x, 2nd=%08x, 3rd=%08x, 4st=%08x", + tr_info(&rtnr_tr, "1st=%08x, 2nd=%08x, 3rd=%08x, 4st=%08x", b, c, d, e); break; case 0xb: - tr_info(&rtnr_tr, "rtnr_printf 1st=%08x, 2nd=%08x, 3rd=%08x, 4st=%08x", + tr_info(&rtnr_tr, "1st=%08x, 2nd=%08x, 3rd=%08x, 4st=%08x", b, c, d, e); break; case 0xc: - tr_warn(&rtnr_tr, "rtnr_printf 1st=%08x, 2nd=%08x, 3rd=%08x, 4st=%08x", + tr_warn(&rtnr_tr, "1st=%08x, 2nd=%08x, 3rd=%08x, 4st=%08x", b, c, d, e); break; case 0xd: - tr_dbg(&rtnr_tr, "rtnr_printf 1st=%08x, 2nd=%08x, 3rd=%08x, 4st=%08x", + tr_dbg(&rtnr_tr, "1st=%08x, 2nd=%08x, 3rd=%08x, 4st=%08x", b, c, d, e); break; case 0xe: - tr_err(&rtnr_tr, "rtnr_printf 1st=%08x, 2nd=%08x, 3rd=%08x, 4st=%08x", + tr_err(&rtnr_tr, "1st=%08x, 2nd=%08x, 3rd=%08x, 4st=%08x", b, c, d, e); break; @@ -197,7 +197,7 @@ static inline void rtnr_set_process_sample_rate(struct processing_module *mod, u { struct comp_data *cd = module_get_private_data(mod); - comp_dbg(mod->dev, "rtnr_set_process_sample_rate()"); + comp_dbg(mod->dev, "entry"); cd->process_sample_rate = sample_rate; } @@ -206,12 +206,12 @@ static int32_t rtnr_check_config_validity(struct processing_module *mod) struct comp_data *cd = module_get_private_data(mod); struct comp_dev *dev = mod->dev; - comp_dbg(dev, "rtnr_check_config_validity() sample_rate:%d enabled: %d", + comp_dbg(dev, "sample_rate:%d enabled: %d", cd->config.params.sample_rate, cd->config.params.enabled); if ((cd->config.params.sample_rate != 48000) && (cd->config.params.sample_rate != 16000)) { - comp_err(dev, "rtnr_check_config_validity() invalid sample_rate:%d", + comp_err(dev, "invalid sample_rate:%d", cd->config.params.sample_rate); return -EINVAL; } @@ -241,7 +241,7 @@ static int rtnr_init(struct processing_module *mod) return -EINVAL; } - cd = rzalloc(SOF_MEM_FLAG_USER, sizeof(*cd)); + cd = mod_zalloc(mod, sizeof(*cd)); if (!cd) return -ENOMEM; @@ -250,9 +250,9 @@ static int rtnr_init(struct processing_module *mod) cd->process_enable = true; /* Handler for component data */ - cd->model_handler = comp_data_blob_handler_new(dev); + cd->model_handler = mod_data_blob_handler_new(mod); if (!cd->model_handler) { - comp_err(dev, "comp_data_blob_handler_new() failed."); + comp_err(dev, "mod_data_blob_handler_new() failed."); ret = -ENOMEM; goto cd_fail; } @@ -285,8 +285,8 @@ static int rtnr_init(struct processing_module *mod) return 0; cd_fail: - comp_data_blob_handler_free(cd->model_handler); - rfree(cd); + mod_data_blob_handler_free(mod, cd->model_handler); + mod_free(mod, cd); return ret; } @@ -294,13 +294,13 @@ static int rtnr_free(struct processing_module *mod) { struct comp_data *cd = module_get_private_data(mod); - comp_info(mod->dev, "rtnr_free()"); + comp_info(mod->dev, "entry"); - comp_data_blob_handler_free(cd->model_handler); + mod_data_blob_handler_free(mod, cd->model_handler); RTKMA_API_Context_Free(cd->rtk_agl); - rfree(cd); + mod_free(mod, cd); return 0; } @@ -312,7 +312,7 @@ static int rtnr_check_params(struct processing_module *mod, struct audio_stream struct comp_data *cd = module_get_private_data(mod); bool channels_valid; - comp_info(dev, "rtnr_check_params()"); + comp_info(dev, "entry"); /* set source/sink_frames/rate */ cd->source_rate = audio_stream_get_rate(source); @@ -439,7 +439,7 @@ static int rtnr_get_config(struct processing_module *mod, struct comp_dev *dev = mod->dev; #if CONFIG_IPC_MAJOR_4 - comp_err(dev, "rtnr_get_config(), Not supported, should not happen"); + comp_err(dev, "Not supported, should not happen"); return -EINVAL; #elif CONFIG_IPC_MAJOR_3 @@ -447,7 +447,7 @@ static int rtnr_get_config(struct processing_module *mod, struct sof_ipc_ctrl_data *cdata = (struct sof_ipc_ctrl_data *)fragment; int j; - comp_dbg(dev, "rtnr_get_config()"); + comp_dbg(dev, "entry"); switch (cdata->cmd) { case SOF_CTRL_CMD_BINARY: @@ -478,7 +478,7 @@ static int rtnr_reconfigure(struct processing_module *mod) uint8_t *config; size_t size; - comp_dbg(dev, "rtnr_reconfigure()"); + comp_dbg(dev, "entry"); if (!comp_is_current_data_blob_valid(cd->model_handler) && !comp_is_new_data_blob_available(cd->model_handler)) { @@ -494,7 +494,7 @@ static int rtnr_reconfigure(struct processing_module *mod) } config = comp_get_data_blob(cd->model_handler, &size, NULL); - comp_dbg(dev, "rtnr_reconfigure() size: %d", size); + comp_dbg(dev, "size: %d", size); if (size == 0) { /* No data to be handled */ @@ -562,7 +562,7 @@ static int32_t rtnr_set_value(struct processing_module *mod, void *ctl_data) for (j = 0; j < cdata->num_elems; j++) { val |= cdata->chanv[j].value; - comp_dbg(dev, "rtnr_set_value(), value = %u", val); + comp_dbg(dev, "value = %u", val); } if (val) { @@ -614,7 +614,7 @@ static int rtnr_set_config(struct processing_module *mod, uint32_t param_id, * concurrently which is the case when there is no preemption. */ if (comp_is_new_data_blob_available(cd->model_handler)) { - comp_dbg(dev, "rtnr_set_config(), new data blob available"); + comp_dbg(dev, "new data blob available"); comp_get_data_blob(cd->model_handler, NULL, NULL); cd->reconfigure = true; } @@ -629,7 +629,7 @@ static int rtnr_set_config(struct processing_module *mod, uint32_t param_id, return rtnr_set_value(mod, cdata); } - comp_err(dev, "rtnr_set_config() error: invalid command %d", cdata->cmd); + comp_err(dev, "error: invalid command %d", cdata->cmd); return -EINVAL; #elif CONFIG_IPC_MAJOR_4 @@ -637,10 +637,10 @@ static int rtnr_set_config(struct processing_module *mod, uint32_t param_id, switch (param_id) { case SOF_IPC4_SWITCH_CONTROL_PARAM_ID: - comp_dbg(dev, "rtnr_set_config(), SOF_IPC4_SWITCH_CONTROL_PARAM_ID"); + comp_dbg(dev, "SOF_IPC4_SWITCH_CONTROL_PARAM_ID"); return rtnr_set_value(mod, ctl); case SOF_RTNR_CONFIG: - comp_dbg(dev, "rtnr_set_config(), SOF_RTNR_CONFIG"); + comp_dbg(dev, "SOF_RTNR_CONFIG"); if (dev->state < COMP_STATE_READY) { comp_err(dev, "driver in init!"); return -EBUSY; @@ -648,7 +648,7 @@ static int rtnr_set_config(struct processing_module *mod, uint32_t param_id, return rtnr_set_config_bytes(mod, fragment, fragment_size); case SOF_RTNR_DATA: - comp_dbg(dev, "rtnr_set_config(), SOF_RTNR_DATA"); + comp_dbg(dev, "SOF_RTNR_DATA"); if (dev->state < COMP_STATE_READY) { comp_err(dev, "driver in init!"); return -EBUSY; @@ -675,7 +675,7 @@ static int rtnr_set_config(struct processing_module *mod, uint32_t param_id, return 0; } - comp_err(dev, "rtnr_set_config(), error: invalid param_id = %d", param_id); + comp_err(dev, "error: invalid param_id = %d", param_id); return -EINVAL; #endif } @@ -799,7 +799,7 @@ static int rtnr_prepare(struct processing_module *mod, struct comp_buffer *sourceb, *sinkb; int ret; - comp_dbg(dev, "rtnr_prepare()"); + comp_dbg(dev, "entry"); sinkb = comp_dev_get_first_data_consumer(dev); sourceb = comp_dev_get_first_data_producer(dev); @@ -829,7 +829,7 @@ static int rtnr_prepare(struct processing_module *mod, goto err; /* Check source and sink PCM format and get processing function */ - comp_info(dev, "rtnr_prepare(), sink_format=%d", cd->sink_format); + comp_info(dev, "sink_format=%d", cd->sink_format); cd->rtnr_func = rtnr_find_func(cd->sink_format); if (!cd->rtnr_func) { comp_err(dev, "No suitable processing function found."); @@ -853,7 +853,7 @@ static int rtnr_reset(struct processing_module *mod) { struct comp_data *cd = module_get_private_data(mod); - comp_info(mod->dev, "rtnr_reset()"); + comp_info(mod->dev, "entry"); cd->sink_format = 0; cd->rtnr_func = NULL; diff --git a/src/audio/selector/README.md b/src/audio/selector/README.md new file mode 100644 index 000000000000..54f09e2ba876 --- /dev/null +++ b/src/audio/selector/README.md @@ -0,0 +1,14 @@ +# Channel Selector Architecture + +This directory contains the Channel Selector component. + +## Overview + +Given a multi-channel stream (e.g., 8-channel microphone array), the channel selector component isolates specific channels (e.g., channels 1 and 2) to pass forward while dropping the rest. + +## Configuration and Scripts + +- **Kconfig**: Enables the selector component (`COMP_SEL`). +- **CMakeLists.txt**: Compiles `selector.c` and `selector_generic.c`, and natively supports Zephyr environment modules (`llext`). +- **selector.toml**: Includes topology parameters under `MICSEL`, using UUID `UUIDREG_STR_SELECTOR4` and limits up to 8 instances, with distinct `mod_cfg` limits depending on chipset architectures (Meteor Lake, Lunar Lake, ACE). +- **Topology (.conf)**: Derived from `tools/topology/topology2/include/components/micsel.conf`, which defines a `micsel` widget of type `effect` (UUID `c1:92:fe:32:17:1e:c2:4f:97:58:c7:f3:54:2e:98:0a`). diff --git a/src/audio/selector/selector.c b/src/audio/selector/selector.c index 201ed9fa6567..6afc92d8bdfc 100644 --- a/src/audio/selector/selector.c +++ b/src/audio/selector/selector.c @@ -37,13 +37,16 @@ #include <stddef.h> #include <stdint.h> +#if CONFIG_IPC_MAJOR_4 +#define SEL_MAX_CONFIG_BLOB_SIZE (SEL_MAX_NUM_CONFIGS * sizeof(struct ipc4_selector_coeffs_config)) +#endif + LOG_MODULE_REGISTER(selector, CONFIG_SOF_LOG_LEVEL); #if CONFIG_IPC_MAJOR_3 static const struct comp_driver comp_selector; SOF_DEFINE_REG_UUID(selector); -DECLARE_TR_CTX(selector_tr, SOF_UUID(selector_uuid), LOG_LEVEL_INFO); static int selector_verify_params(struct comp_dev *dev, struct sof_ipc_stream_params *params) @@ -53,7 +56,7 @@ static int selector_verify_params(struct comp_dev *dev, uint32_t in_channels; uint32_t out_channels; - comp_dbg(dev, "selector_verify_params()"); + comp_dbg(dev, "entry"); sinkb = comp_dev_get_first_data_consumer(dev); @@ -164,7 +167,7 @@ static struct comp_dev *selector_new(const struct comp_driver *drv, cd = rzalloc(SOF_MEM_FLAG_USER, sizeof(*cd)); if (!cd) { - rfree(dev); + comp_free_device(dev); return NULL; } @@ -173,7 +176,7 @@ static struct comp_dev *selector_new(const struct comp_driver *drv, ret = memcpy_s(&cd->config, sizeof(cd->config), ipc_process->data, bs); if (ret) { rfree(cd); - rfree(dev); + comp_free_device(dev); return NULL; } @@ -189,10 +192,10 @@ static void selector_free(struct comp_dev *dev) { struct comp_data *cd = comp_get_drvdata(dev); - comp_dbg(dev, "selector_free()"); + comp_dbg(dev, "entry"); rfree(cd); - rfree(dev); + comp_free_device(dev); } /** @@ -207,7 +210,7 @@ static int selector_params(struct comp_dev *dev, { int err; - comp_dbg(dev, "selector_params()"); + comp_dbg(dev, "entry"); err = selector_verify_params(dev, params); if (err < 0) { @@ -233,7 +236,7 @@ static int selector_ctrl_set_data(struct comp_dev *dev, switch (cdata->cmd) { case SOF_CTRL_CMD_BINARY: - comp_dbg(dev, "selector_ctrl_set_data(), SOF_CTRL_CMD_BINARY"); + comp_dbg(dev, "SOF_CTRL_CMD_BINARY"); cfg = (struct sof_sel_config *) ASSUME_ALIGNED(&cdata->data->data, 4); @@ -269,7 +272,7 @@ static int selector_ctrl_get_data(struct comp_dev *dev, switch (cdata->cmd) { case SOF_CTRL_CMD_BINARY: - comp_dbg(dev, "selector_ctrl_get_data(), SOF_CTRL_CMD_BINARY"); + comp_dbg(dev, "SOF_CTRL_CMD_BINARY"); if (size < sizeof(cd->config)) return -EINVAL; @@ -306,7 +309,7 @@ static int selector_cmd(struct comp_dev *dev, int cmd, void *data, struct sof_ipc_ctrl_data *cdata = ASSUME_ALIGNED(data, 4); int ret = 0; - comp_dbg(dev, "selector_cmd()"); + comp_dbg(dev, "entry"); switch (cmd) { case COMP_CMD_SET_DATA: @@ -316,10 +319,10 @@ static int selector_cmd(struct comp_dev *dev, int cmd, void *data, ret = selector_ctrl_get_data(dev, cdata, max_data_size); break; case COMP_CMD_SET_VALUE: - comp_dbg(dev, "selector_cmd(), COMP_CMD_SET_VALUE"); + comp_dbg(dev, "COMP_CMD_SET_VALUE"); break; case COMP_CMD_GET_VALUE: - comp_dbg(dev, "selector_cmd(), COMP_CMD_GET_VALUE"); + comp_dbg(dev, "COMP_CMD_GET_VALUE"); break; default: comp_err(dev, "invalid command"); @@ -341,7 +344,7 @@ static int selector_trigger(struct comp_dev *dev, int cmd) enum sof_comp_type type; int ret; - comp_dbg(dev, "selector_trigger()"); + comp_dbg(dev, "entry"); sourceb = comp_dev_get_first_data_producer(dev); if (!sourceb) { @@ -375,7 +378,7 @@ static int selector_copy(struct comp_dev *dev) uint32_t source_bytes; uint32_t sink_bytes; - comp_dbg(dev, "selector_copy()"); + comp_dbg(dev, "entry"); /* selector component will have 1 source and 1 sink buffer */ source = comp_dev_get_first_data_producer(dev); @@ -388,7 +391,7 @@ static int selector_copy(struct comp_dev *dev) source_bytes = frames * audio_stream_frame_bytes(&source->stream); sink_bytes = frames * audio_stream_frame_bytes(&sink->stream); - comp_dbg(dev, "selector_copy(), source_bytes = 0x%x, sink_bytes = 0x%x", + comp_dbg(dev, "source_bytes = 0x%x, sink_bytes = 0x%x", source_bytes, sink_bytes); /* copy selected channels from in to out */ @@ -415,7 +418,7 @@ static int selector_prepare(struct comp_dev *dev) size_t sink_size; int ret; - comp_dbg(dev, "selector_prepare()"); + comp_dbg(dev, "entry"); ret = comp_set_state(dev, COMP_TRIGGER_PREPARE); if (ret < 0) @@ -499,7 +502,7 @@ static int selector_reset(struct comp_dev *dev) int ret; struct comp_data *cd = comp_get_drvdata(dev); - comp_dbg(dev, "selector_reset()"); + comp_dbg(dev, "entry"); cd->source_period_bytes = 0; cd->sink_period_bytes = 0; @@ -510,6 +513,8 @@ static int selector_reset(struct comp_dev *dev) return ret; } +DECLARE_TR_CTX(selector_tr, SOF_UUID(selector_uuid), LOG_LEVEL_INFO); + /** \brief Selector component definition. */ static const struct comp_driver comp_selector = { .type = SOF_COMP_SELECTOR, @@ -544,7 +549,6 @@ SOF_MODULE_INIT(selector, sys_comp_selector_init); #else SOF_DEFINE_REG_UUID(selector4); -DECLARE_TR_CTX(selector_tr, SOF_UUID(selector4_uuid), LOG_LEVEL_INFO); static void build_config(struct comp_data *cd, struct module_config *cfg) { @@ -574,7 +578,7 @@ static void build_config(struct comp_data *cd, struct module_config *cfg) /* Build default coefficient array (unity Q10 on diagonal, i.e. pass-through mode) */ memset(&cd->coeffs_config, 0, sizeof(cd->coeffs_config)); for (i = 0; i < MIN(SEL_SOURCE_CHANNELS_MAX, SEL_SINK_CHANNELS_MAX); i++) - cd->coeffs_config.coeffs[i][i] = 1 << 10; + cd->coeffs_config.coeffs[i][i] = SEL_COEF_ONE_Q10; } static int selector_init(struct processing_module *mod) @@ -589,7 +593,7 @@ static int selector_init(struct processing_module *mod) size_t bs[2]; int ret; - comp_dbg(mod->dev, "selector_init()"); + comp_dbg(mod->dev, "entry"); init_cfg_ext = cfg->init_data; init_cfg_out_fmt = cfg->init_data; @@ -613,7 +617,7 @@ static int selector_init(struct processing_module *mod) return -EINVAL; } - cd = rzalloc(SOF_MEM_FLAG_USER, sizeof(*cd)); + cd = mod_zalloc(mod, sizeof(*cd)); if (!cd) return -ENOMEM; @@ -692,7 +696,7 @@ static int selector_verify_params(struct processing_module *mod, uint32_t in_channels = cd->config.in_channels_count; uint32_t out_channels = cd->config.out_channels_count; - comp_dbg(dev, "selector_verify_params()"); + comp_dbg(dev, "entry"); /* verify input channels */ if (in_channels == 0 || in_channels > SEL_SOURCE_CHANNELS_MAX) { @@ -731,9 +735,10 @@ static int selector_free(struct processing_module *mod) { struct comp_data *cd = module_get_private_data(mod); - comp_dbg(mod->dev, "selector_free()"); + comp_dbg(mod->dev, "entry"); - rfree(cd); + mod_free(mod, cd->multi_coeffs_config); + mod_free(mod, cd); return 0; } @@ -750,7 +755,7 @@ static int selector_params(struct processing_module *mod) struct sof_ipc_stream_params *params = mod->stream_params; int err; - comp_dbg(mod->dev, "selector_params()"); + comp_dbg(mod->dev, "entry"); set_selector_params(mod, params); @@ -769,13 +774,51 @@ static int selector_set_config(struct processing_module *mod, uint32_t config_id size_t response_size) { struct comp_data *cd = module_get_private_data(mod); + struct comp_dev *dev = mod->dev; + int n; if (config_id == IPC4_SELECTOR_COEFFS_CONFIG_ID) { - if (data_offset_size != sizeof(cd->coeffs_config)) + if (data_offset_size > SEL_MAX_CONFIG_BLOB_SIZE || + fragment_size < data_offset_size || + pos != MODULE_CFG_FRAGMENT_SINGLE) { + comp_err(dev, "Failure with data_offset_size %u fragment_size %u pos %u", + data_offset_size, (uint32_t)fragment_size, (uint32_t)pos); return -EINVAL; + } - memcpy_s(&cd->coeffs_config, sizeof(cd->coeffs_config), fragment, data_offset_size); - return 0; + /* The size must be N times the coefficient vectors size of one channels + * up/down mix profile. + */ + n = data_offset_size / sizeof(struct ipc4_selector_coeffs_config); + if (n < 1 || data_offset_size != n * sizeof(struct ipc4_selector_coeffs_config)) { + comp_err(dev, "Invalid configuration size."); + return -EINVAL; + } + + cd->num_configs = n; + if (cd->multi_coeffs_config && cd->multi_coeffs_config_size < data_offset_size) { + /* Configuration exist but the allocation is too small to write over. */ + mod_free(mod, cd->multi_coeffs_config); + cd->multi_coeffs_config = NULL; + } + + if (!cd->multi_coeffs_config) { + cd->multi_coeffs_config_size = data_offset_size; + cd->multi_coeffs_config = mod_alloc(mod, cd->multi_coeffs_config_size); + if (!cd->multi_coeffs_config) { + comp_err(dev, "Failed to allocate configuration blob."); + return -ENOMEM; + } + } + + /* Copy the configuration and notify for need to re-configure. If for + * some reason the memcpy_s() would return an error (it can't because + * of the previous checks), the partial or incorrect blob would remain + * allocated but be freed later in module error handling and ending. + */ + cd->new_config = true; + return memcpy_s(cd->multi_coeffs_config, cd->multi_coeffs_config_size, + fragment, data_offset_size); } return -EINVAL; @@ -788,6 +831,112 @@ static int selector_get_config(struct processing_module *mod, uint32_t config_id return 0; } +/** + * \brief Loop the array of mix coefficients sets and find a set with matching channels + * in and out count. + * \param[in] cd Selector component data. + * \param[in] source_channels Number of channels in source. + * \param[in] sink_channels Number of channels in sink. + * + * \return Pointer to the matching ipc4_selector_coeffs_config if found, or NULL if + * no matching configuration exists. + */ +static struct ipc4_selector_coeffs_config *selector_config_array_search(struct comp_data *cd, + int source_channels, + int sink_channels) +{ + struct ipc4_selector_coeffs_config *found = NULL; + int i; + + for (i = 0; i < cd->num_configs; i++) { + if (cd->multi_coeffs_config[i].source_channels_count == source_channels && + cd->multi_coeffs_config[i].sink_channels_count == sink_channels) { + found = &cd->multi_coeffs_config[i]; + break; + } + } + + return found; +} + +/** + * \brief Get mix coefficients set from configuration blob with multiple coefficients sets. + * Also activate more efficient pass-through copy mode if the coefficients indicate 1:1 + * copy from source to sink. + * \param[in,out] mod Selector base module device. + * \param[in] source_channels Number of channels in source. + * \param[in] sink_channels Number of channels in sink. + * + * \return Error code. + */ +static int selector_find_coefficients(struct processing_module *mod) +{ + struct comp_data *cd = module_get_private_data(mod); + struct comp_dev *dev = mod->dev; + struct ipc4_selector_coeffs_config *config; + uint32_t source_channels = cd->config.in_channels_count; + uint32_t sink_channels = cd->config.out_channels_count; + int16_t coef; + int ret, i, j; + + /* In set_config() the blob is copied to cd->multi_coeffs_config. A legacy blob contains a + * single set of mix coefficients without channels information. A new blob with multiple + * configurations has the source and sink channels count information. If there has been no + * set_config(), then the cd->coeffs_config has been initialized in set_selector_params() + * to mix with coefficients SEL_COEF_ONE_Q10 for matching input and output channels. + */ + if (cd->multi_coeffs_config) { + if (cd->num_configs > 1) { + config = selector_config_array_search(cd, source_channels, sink_channels); + /* If not found, check if pass-through mix is defined for the max + * channels count (8). + */ + if (!config && source_channels == sink_channels) + config = selector_config_array_search(cd, SEL_SOURCE_CHANNELS_MAX, + SEL_SINK_CHANNELS_MAX); + + if (!config) { + comp_err(dev, "No mix coefficients found for %u to %u channels.", + source_channels, sink_channels); + return -EINVAL; + } + } else { + config = cd->multi_coeffs_config; + } + + ret = memcpy_s(&cd->coeffs_config, sizeof(struct ipc4_selector_coeffs_config), + config, sizeof(*config)); + if (ret) + return ret; + } + + /* The pass-through copy function can be used if coefficients are a unit matrix for + * 1:1 stream copy. + */ + if (source_channels == sink_channels) { + cd->passthrough = true; + for (i = 0; i < sink_channels; i++) { + for (j = 0; j < source_channels; j++) { + coef = cd->coeffs_config.coeffs[i][j]; + if ((i == j && coef != SEL_COEF_ONE_Q10) || (i != j && coef != 0)) { + cd->passthrough = false; + break; + } + } + } + } else { + cd->passthrough = false; + } + + if (cd->passthrough) + comp_info(dev, "Passthrough mode."); + else + comp_info(dev, "Using coefficients for %u to %u channels.", + source_channels, sink_channels); + + return 0; +} + /** * \brief Copies and processes stream data. * \param[in,out] mod Selector base module device. @@ -799,10 +948,28 @@ static int selector_process(struct processing_module *mod, struct output_stream_buffer *output_buffers, int num_output_buffers) { + struct audio_stream *source; + struct audio_stream *sink; struct comp_data *cd = module_get_private_data(mod); uint32_t avail_frames = input_buffers[0].size; + int ret; + + comp_dbg(mod->dev, "entry"); - comp_dbg(mod->dev, "selector_process()"); + if (cd->new_config) { + cd->new_config = false; + ret = selector_find_coefficients(mod); + if (ret) + return ret; + } + + if (cd->passthrough) { + source = input_buffers->data; + sink = output_buffers->data; + audio_stream_copy(source, 0, sink, 0, avail_frames * cd->config.in_channels_count); + module_update_buffer_position(input_buffers, output_buffers, avail_frames); + return 0; + } if (avail_frames) /* copy selected channels from in to out */ @@ -827,7 +994,7 @@ static int selector_prepare(struct processing_module *mod, size_t sink_size; int ret; - comp_dbg(dev, "selector_prepare()"); + comp_dbg(dev, "entry"); ret = selector_params(mod); if (ret < 0) @@ -840,8 +1007,7 @@ static int selector_prepare(struct processing_module *mod, sourceb = comp_dev_get_first_data_producer(dev); sinkb = comp_dev_get_first_data_consumer(dev); - audio_stream_set_align(4, 1, &sourceb->stream); - audio_stream_set_align(4, 1, &sinkb->stream); + audio_stream_set_align(SOF_FRAME_BYTE_ALIGN, SOF_FRAME_COUNT_ALIGN, &sourceb->stream); /* get source data format and period bytes */ cd->source_format = audio_stream_get_frm_fmt(&sourceb->stream); @@ -850,17 +1016,7 @@ static int selector_prepare(struct processing_module *mod, /* get sink data format and period bytes */ cd->sink_format = audio_stream_get_frm_fmt(&sinkb->stream); cd->sink_period_bytes = audio_stream_period_bytes(&sinkb->stream, dev->frames); - - /* There is an assumption that sink component will report out - * proper number of channels [1] for selector to actually - * reduce channel count between source and sink - */ - comp_info(dev, "source sink channel = %u %u", - audio_stream_get_channels(&sourceb->stream), - audio_stream_get_channels(&sinkb->stream)); - sink_size = audio_stream_get_size(&sinkb->stream); - md->mpd.in_buff_size = cd->source_period_bytes; md->mpd.out_buff_size = cd->sink_period_bytes; @@ -891,6 +1047,11 @@ static int selector_prepare(struct processing_module *mod, return -EINVAL; } + if (cd->new_config) { + cd->new_config = false; + return selector_find_coefficients(mod); + } + return 0; } @@ -903,12 +1064,14 @@ static int selector_reset(struct processing_module *mod) { struct comp_data *cd = module_get_private_data(mod); - comp_dbg(mod->dev, "selector_reset()"); + comp_dbg(mod->dev, "entry"); cd->source_period_bytes = 0; cd->sink_period_bytes = 0; cd->sel_func = NULL; - + cd->num_configs = 0; + cd->passthrough = false; + cd->new_config = false; return 0; } @@ -937,6 +1100,7 @@ SOF_LLEXT_BUILDINFO; #else +DECLARE_TR_CTX(selector_tr, SOF_UUID(selector4_uuid), LOG_LEVEL_INFO); DECLARE_MODULE_ADAPTER(selector_interface, selector4_uuid, selector_tr); SOF_MODULE_INIT(selector, sys_comp_module_selector_interface_init); diff --git a/src/audio/selector/selector.toml b/src/audio/selector/selector.toml index 69256573033f..3c984a00fd45 100644 --- a/src/audio/selector/selector.toml +++ b/src/audio/selector/selector.toml @@ -39,7 +39,7 @@ 13, 0, 0, 0, 216, 8818000, 384, 1152, 0, 8818, 0, 14, 0, 0, 0, 216, 12274000, 768, 1152, 0, 12274, 0, 15, 0, 0, 0, 216, 19186000, 1536, 1152, 0, 19186, 0] -#elif CONFIG_SOC_INTEL_ACE30 || CONFIG_SOC_INTEL_ACE40 +#elif CONFIG_SOC_ACE30 || CONFIG_SOC_ACE40 mod_cfg = [0, 0, 0, 0, 216, 2952000, 384, 192, 0, 2952, 0, 1, 0, 0, 0, 216, 4720000, 384, 384, 0, 4720, 0, 2, 0, 0, 0, 216, 5705000, 576, 384, 0, 5705, 0, diff --git a/src/audio/selector/tune/sof_selector_blobs.m b/src/audio/selector/tune/sof_selector_blobs.m index 10ea1879b74a..1b370d4030de 100644 --- a/src/audio/selector/tune/sof_selector_blobs.m +++ b/src/audio/selector/tune/sof_selector_blobs.m @@ -1,47 +1,69 @@ -% Export configuration blobs for Selector +% sof_selector_blobs - export configuration blobs for Selector +% +% This script is run without arguments. It exports a number of +% configuration blobs for selector/micsel component. The first +% category of blobs are for upmix and downmix between mono to +% 7.1 channel audio formats. +% +% The second category is for duplicating stereo to four channels +% for 2-way speaker crossover filter. +% % SPDX-License-Identifier: BSD-3-Clause % -% Copyright (c) 2025, Intel Corporation. +% Copyright (c) 2025-2026, Intel Corporation. function sof_selector_blobs() % See ITU-R BS.775-4 for mix coefficient values sof_selector_paths(true); + % Values of enum ipc4_channel_config + IPC4_CHANNEL_CONFIG_MONO = 0; + IPC4_CHANNEL_CONFIG_STEREO = 1; + IPC4_CHANNEL_CONFIG_QUATRO = 5; + IPC4_CHANNEL_CONFIG_5_POINT_1 = 8; + IPC4_CHANNEL_CONFIG_7_POINT_1 = 12; + % Matrix for 1:1 pass-through - sel.rsvd0 = 0; - sel.rsvd1 = 0; + sel.ch_count = [8 8]; % Number of channels + sel.ch_config = [IPC4_CHANNEL_CONFIG_7_POINT_1 IPC4_CHANNEL_CONFIG_7_POINT_1]; sel.coeffs = diag(ones(8, 1)); - write_blob(sel, "passthrough"); + passthrough_pack8 = write_blob(sel, "passthrough"); % Stereo to mono downmix + sel.ch_count = [2 1]; + sel.ch_config = [IPC4_CHANNEL_CONFIG_STEREO IPC4_CHANNEL_CONFIG_MONO]; sel.coeffs = zeros(8,8); sel.coeffs(1, 1) = 0.7071; sel.coeffs(1, 2) = 0.7071; - write_blob(sel, "downmix_stereo_to_mono"); + stereo_to_mono_pack8 = write_blob(sel, "downmix_stereo_to_mono"); % 5.1 to stereo downmix + sel.ch_count = [6 2]; + sel.ch_config = [IPC4_CHANNEL_CONFIG_5_POINT_1 IPC4_CHANNEL_CONFIG_STEREO]; fl = 1; fr = 2; fc = 3; lfe = 4; sl = 5; sr = 6; m = zeros(8,8); m(1, fl) = 1.0000; m(1, fr) = 0.0000; m(1, fc) = 0.7071; m(1, sl) = 0.7071; m(1, sr) = 0.0000; m(2, fl) = 0.0000; m(2, fr) = 1.0000; m(2, fc) = 0.7071; m(2, sl) = 0.0000; m(2, sr) = 0.7071; sel.coeffs = m; - write_blob(sel, "downmix_51_to_stereo"); sel.coeffs(1, lfe) = 10^(+4/20); % +10 dB, attenuate by -6 dB to left sel.coeffs(2, lfe) = 10^(+4/20); % +10 dB, attenuate by -6 dB to right - write_blob(sel, "downmix_51_to_stereo_with_lfe"); + sixch_to_stereo_pack8 = write_blob(sel, "downmix_51_to_stereo_with_lfe"); % 5.1 to mono downmix + sel.ch_count = [6 1]; + sel.ch_config = [IPC4_CHANNEL_CONFIG_5_POINT_1 IPC4_CHANNEL_CONFIG_MONO]; fl = 1; fr = 2; fc = 3; lfe = 4; sl = 5; sr = 6; m = zeros(8,8); m(1, fl) = 0.7071; m(1, fr) = 0.7071; m(1, fc) = 1.0000; m(1, sl) = 0.5000; m(1, sr) = 0.5000; sel.coeffs = m; - write_blob(sel, "downmix_51_to_mono"); sel.coeffs(1, lfe) = 10^(+10/20); - write_blob(sel, "downmix_51_to_mono_with_lfe"); + sixch_to_mono_pack8 = write_blob(sel, "downmix_51_to_mono_with_lfe"); % 7.1 to 5.1 downmix + sel.ch_count = [8 6]; + sel.ch_config = [IPC4_CHANNEL_CONFIG_7_POINT_1 IPC4_CHANNEL_CONFIG_5_POINT_1]; fl8 = 1; fr8 = 2; fc8 = 3; lfe8 = 4; bl8 = 5; br8 = 6; sl8 = 7; sr8 = 8; fl6 = 1; fr6 = 2; fc6 = 3; lfe6 = 4; sl6 = 5; sr6 = 6; m = zeros(8,8); @@ -54,78 +76,157 @@ function sof_selector_blobs() m(sr6, br8) = 1; m(lfe6, lfe8) = 1; sel.coeffs = m; - write_blob(sel, "downmix_71_to_51"); + eightch_to_sixch_pack8 = write_blob(sel, "downmix_71_to_51"); - % 7.1 to 5.1 downmix + % 7.1 to stereo downmix + sel.ch_count = [8 2]; + sel.ch_config = [IPC4_CHANNEL_CONFIG_7_POINT_1 IPC4_CHANNEL_CONFIG_STEREO]; fl = 1; fr = 2; fc = 3; lfe = 4; bl = 5; br = 6; sl = 7; sr = 8; m = zeros(8,8); m(1, fl) = 1.0000; m(1, fr) = 0.0000; m(1, fc) = 0.7071; m(1, sl) = 0.7071; m(1, sr) = 0.0000; m(1, bl) = 0.7071; m(1, br) = 0.0000; m(2, fl) = 0.0000; m(2, fr) = 1.0000; m(2, fc) = 0.7071; m(2, sl) = 0.0000; m(2, sr) = 0.7071; m(2, bl) = 0.0000; m(2, br) = 0.7071; sel.coeffs = m; - write_blob(sel, "downmix_71_to_stereo"); sel.coeffs(1, lfe) = 10^(+4/20); % +10 dB, attenuate by -6 dB to left sel.coeffs(2, lfe) = 10^(+4/20); % +10 dB, attenuate by -6 dB to right - write_blob(sel, "downmix_71_to_stereo_with_lfe"); + eightch_to_stereo_pack8 = write_blob(sel, "downmix_71_to_stereo_with_lfe"); % 7.1 to mono downmix + sel.ch_count = [8 1]; + sel.ch_config = [IPC4_CHANNEL_CONFIG_7_POINT_1 IPC4_CHANNEL_CONFIG_MONO]; fl = 1; fc = 3; fr = 2; sr = 8; br = 6; bl = 5; sl = 7; lfe = 4; m = zeros(8,8); m(1, fl) = 0.7071; m(1, fr) = 0.7071; m(1, fc) = 1.0000; m(1, sl) = 0.5000; m(1, sr) = 0.5000; m(1, bl) = 0.5000; m(1, br) = 0.5000; sel.coeffs = m; - write_blob(sel, "downmix_71_to_mono"); m(1, lfe) = 10^(+19/20); % +10 dB - write_blob(sel, "downmix_71_to_mono_with_lfe"); + eightch_to_mono_pack8 = write_blob(sel, "downmix_71_to_mono_with_lfe"); % mono to stereo upmix + sel.ch_count = [1 2]; + sel.ch_config = [IPC4_CHANNEL_CONFIG_MONO IPC4_CHANNEL_CONFIG_STEREO]; sel.coeffs = zeros(8,8); sel.coeffs(1, 1) = 10^(-3/20); sel.coeffs(2, 1) = 10^(-3/20); - write_blob(sel, "upmix_mono_to_stereo"); + mono_to_stereo_pack8 = write_blob(sel, "upmix_mono_to_stereo"); % mono to 5.1 / 7.1 upmix - fc = 3 + sel.ch_count = [1 6]; + sel.ch_config = [IPC4_CHANNEL_CONFIG_MONO IPC4_CHANNEL_CONFIG_5_POINT_1]; + fc = 3; sel.coeffs = zeros(8,8); sel.coeffs(fc, 1) = 1; - write_blob(sel, "upmix_mono_to_51"); - write_blob(sel, "upmix_mono_to_71"); + mono_to_sixch_pack8 = write_blob(sel, "upmix_mono_to_51"); + sel.ch_count = [1 8]; + sel.ch_config = [IPC4_CHANNEL_CONFIG_MONO IPC4_CHANNEL_CONFIG_7_POINT_1]; + mono_to_eightch_pack8 = write_blob(sel, "upmix_mono_to_71"); % stereo to 5.1 / 7.1 upmix + sel.ch_count = [2 6]; + sel.ch_config = [IPC4_CHANNEL_CONFIG_STEREO IPC4_CHANNEL_CONFIG_5_POINT_1]; fl = 1; fr = 2; sel.coeffs = zeros(8,8); sel.coeffs(fl, 1) = 1; sel.coeffs(fr, 2) = 1; - write_blob(sel, "upmix_stereo_to_51"); - write_blob(sel, "upmix_stereo_to_71"); + stereo_to_sixch_pack8 = write_blob(sel, "upmix_stereo_to_51"); + sel.ch_count = [2 8]; + sel.ch_config = [IPC4_CHANNEL_CONFIG_STEREO IPC4_CHANNEL_CONFIG_7_POINT_1]; + stereo_to_eightch_pack8 = write_blob(sel, "upmix_stereo_to_71"); + + % For blob format with multiple up/down-mix profiles, intended + % for playback decoder offload conversions. + multi_pack8 = [passthrough_pack8 mono_to_stereo_pack8 sixch_to_stereo_pack8 eightch_to_stereo_pack8]; + write_8bit_packed(multi_pack8, 'stereo_endpoint_playback_updownmix'); + + % Stereo to L,L,R,R + sel.ch_count = [2 4]; + sel.ch_config = [IPC4_CHANNEL_CONFIG_STEREO IPC4_CHANNEL_CONFIG_QUATRO]; + sel.coeffs = [ 1 0 0 0 0 0 0 0 ; ... + 1 0 0 0 0 0 0 0 ; ... + 0 1 0 0 0 0 0 0 ; ... + 0 1 0 0 0 0 0 0 ; ... + 0 0 0 0 0 0 0 0 ; ... + 0 0 0 0 0 0 0 0 ; ... + 0 0 0 0 0 0 0 0 ; ... + 0 0 0 0 0 0 0 0 ]; + write_blob(sel, "xover_selector_lr_to_llrr"); + + % Stereo to R,R,L,L + sel.ch_count = [2 4]; + sel.ch_config = [IPC4_CHANNEL_CONFIG_STEREO IPC4_CHANNEL_CONFIG_QUATRO]; + sel.coeffs = [ 0 1 0 0 0 0 0 0 ; ... + 0 1 0 0 0 0 0 0 ; ... + 1 0 0 0 0 0 0 0 ; ... + 1 0 0 0 0 0 0 0 ; ... + 0 0 0 0 0 0 0 0 ; ... + 0 0 0 0 0 0 0 0 ; ... + 0 0 0 0 0 0 0 0 ; ... + 0 0 0 0 0 0 0 0 ]; + write_blob(sel, "xover_selector_lr_to_rrll"); + + % Stereo to L,R,L,R + sel.ch_count = [2 4]; + sel.ch_config = [IPC4_CHANNEL_CONFIG_STEREO IPC4_CHANNEL_CONFIG_QUATRO]; + sel.coeffs = [ 1 0 0 0 0 0 0 0 ; ... + 0 1 0 0 0 0 0 0 ; ... + 1 0 0 0 0 0 0 0 ; ... + 0 1 0 0 0 0 0 0 ; ... + 0 0 0 0 0 0 0 0 ; ... + 0 0 0 0 0 0 0 0 ; ... + 0 0 0 0 0 0 0 0 ; ... + 0 0 0 0 0 0 0 0 ]; + write_blob(sel, "xover_selector_lr_to_lrlr"); sof_selector_paths(false); end -function write_blob(sel, blobname) +function pack8 = write_blob(sel, blobname) + pack8 = pack_selector_config(sel); + pack8_copy = pack8; + pack8_copy(1:4) = uint8([0 0 0 0]); + write_8bit_packed(pack8_copy, blobname); +end + +function write_8bit_packed(pack8, blobname) + blob8 = sof_selector_build_blob(pack8); str_config = "selector_config"; str_exported = "Exported with script sof_selector_blobs.m"; - str_howto = "cd tools/tune/selector; octave sof_selector_blobs.m" + str_howto = "cd tools/tune/selector; octave sof_selector_blobs.m"; sof_tools = '../../../../tools'; sof_tplg = fullfile(sof_tools, 'topology'); sof_tplg_selector = fullfile(sof_tplg, 'topology2/include/components/micsel'); + tplg2_fn = sprintf("%s/%s.conf", sof_tplg_selector, blobname); + sof_check_create_dir(tplg2_fn); + sof_tplg2_write(tplg2_fn, blob8, str_config, str_exported, str_howto); +end - sel +function pack8 = pack_selector_config(sel) - sum_coefs = sum(sel.coeffs, 2)' - max_sum_coef = max(sum_coefs) + sum_coefs = sum(sel.coeffs, 2)'; + max_sum_coef = max(sum_coefs); if max_sum_coef > 1 scale = 1 / max_sum_coef; else scale = 1; end - scale sel.coeffs = scale .* sel.coeffs'; + coeffs_vec = reshape(sel.coeffs, 1, []); % convert to row vector + coeffs_q10 = int16(round(coeffs_vec .* 1024)); % Q6.10 + pack8 = uint8(zeros(1, 2 * length(coeffs_q10) + 4)); - blob8 = sof_selector_build_blob(sel); - tplg2_fn = sprintf("%s/%s.conf", sof_tplg_selector, blobname); - sof_check_create_dir(tplg2_fn); - sof_tplg2_write(tplg2_fn, blob8, str_config, str_exported, str_howto); -end + % header + j = 1; + pack8(j:j + 1) = uint8(sel.ch_count); + j = j + 2; + pack8(j:j + 1) = uint8(sel.ch_config); + j = j + 2; + + % coeffs matrix + for i = 1:length(coeffs_q10) + pack8(j:j+1) = int16_to_byte(coeffs_q10(i)); + j = j + 2; + end + + end function sof_selector_paths(enable) @@ -137,15 +238,11 @@ function sof_selector_paths(enable) end end -function blob8 = sof_selector_build_blob(sel) +function blob8 = sof_selector_build_blob(pack8) - s = size(sel.coeffs); blob_type = 0; blob_param_id = 0; % IPC4_SELECTOR_COEFFS_CONFIG_ID - data_length = s(1) * s(2) + 2; - data_size = 2 * data_length; % int16_t matrix - coeffs_vec = reshape(sel.coeffs, 1, []); % convert to row vector - coeffs_q10 = int16(round(coeffs_vec .* 1024)); % Q6.10 + data_size = length(pack8); ipc_ver = 4; [abi_bytes, abi_size] = sof_get_abi(data_size, ipc_ver, blob_type, blob_param_id); blob_size = data_size + abi_size; @@ -153,17 +250,7 @@ function sof_selector_paths(enable) blob8(1:abi_size) = abi_bytes; j = abi_size + 1; - % header - blob8(j:j+1) = int16_to_byte(int16(sel.rsvd0)); - j = j + 2; - blob8(j:j+1) = int16_to_byte(int16(sel.rsvd1)); - j = j + 2; - - % coeffs matrix - for i = 1:(s(1) * s(2)) - blob8(j:j+1) = int16_to_byte(coeffs_q10(i)); - j = j + 2; - end + blob8(j:j+data_size-1) = pack8; end function bytes = int16_to_byte(word) diff --git a/src/audio/smart_amp/Kconfig b/src/audio/smart_amp/Kconfig index 34c537d4db16..51564f99eebc 100644 --- a/src/audio/smart_amp/Kconfig +++ b/src/audio/smart_amp/Kconfig @@ -2,7 +2,6 @@ config COMP_SMART_AMP bool "Smart Amplifier component" - select COMP_BLOB help Select for Smart Amp component. This component protect the speaker from overheating and excursion violation. This consists of two parts diff --git a/src/audio/smart_amp/README.md b/src/audio/smart_amp/README.md new file mode 100644 index 000000000000..848d7abd7b15 --- /dev/null +++ b/src/audio/smart_amp/README.md @@ -0,0 +1,23 @@ +# Smart Amplifier Architecture + +This directory contains the Smart Amp Component. + +## Overview + +Uses predictive speaker models and real-time Current/Voltage (I/V) feedback to push speakers louder without causing thermal damage or physical over-excursion. + +## Architecture Diagram + +```mermaid +graph LR + In[Audio Input] --> Proc[Protection Algorithm] + FB[I/V Sense Input] --> Proc + Model[(Speaker Parameters)] --> Proc + Proc --> Out[Safe Audio Output] +``` + +## Configuration and Scripts + +- **Kconfig**: Enables the Smart Amplifier component (`COMP_SMART_AMP`). Features selectable architectures depending on third-party supply (defaults to `PASSTHRU_AMP`, or optionally `MAXIM_DSM` utilizing `libdsm.a`). Includes stubs for CI (`MAXIM_DSM_STUB`). +- **CMakeLists.txt**: Manages standard smart amp implementations (`smart_amp.c`, `smart_amp_generic.c`) and vendor-specific paths (`smart_amp_maxim_dsm.c`, `smart_amp_passthru.c`). Uses `zephyr_library_import` to link the Maxim SDK libraries. +- **Topology (.conf)**: Configured in `tools/topology/topology2/include/components/smart_amp.conf`, declaring a `smart_amp` effect component with UUID `1e:96:7a:16:e4:8a:ea:11:89:f1:00:0c:29:ce:16:35` (matching the test module) and hardcoding default configuration payloads into ALSA controls (`tlv_read`/`extctl`). diff --git a/src/audio/smart_amp/smart_amp.c b/src/audio/smart_amp/smart_amp.c index f7ed81d25b8a..f82e3d2f7360 100644 --- a/src/audio/smart_amp/smart_amp.c +++ b/src/audio/smart_amp/smart_amp.c @@ -11,6 +11,7 @@ #include <sys/types.h> #include <rtos/init.h> +#include <sof/audio/module_adapter/module/generic.h> #include <sof/trace/trace.h> #include <sof/ipc/msg.h> #include <sof/ut.h> @@ -18,8 +19,6 @@ #include <sof/audio/ipc-config.h> #include <sof/audio/smart_amp/smart_amp.h> -static const struct comp_driver comp_smart_amp; - /* NOTE: this code builds itself with one of two distinct UUIDs * depending on configuration! */ @@ -29,25 +28,23 @@ SOF_DEFINE_REG_UUID(maxim_dsm); #else /* Passthrough */ SOF_DEFINE_REG_UUID(passthru_smart_amp); -#define UUID_SYM passthru_smart_amp +#define UUID_SYM passthru_smart_amp_uuid #endif -DECLARE_TR_CTX(smart_amp_comp_tr, SOF_UUID(UUID_SYM), - LOG_LEVEL_INFO); + +LOG_MODULE_REGISTER(smart_amp, CONFIG_SOF_LOG_LEVEL); /* Amp configuration & model calibration data for tuning/debug */ #define SOF_SMART_AMP_CONFIG 0 #define SOF_SMART_AMP_MODEL 1 struct smart_amp_data { + struct processing_module *mod; struct sof_smart_amp_config config; - struct comp_data_blob_handler *model_handler; struct comp_buffer *source_buf; /**< stream source buffer */ struct comp_buffer *feedback_buf; /**< feedback source buffer */ struct comp_buffer *sink_buf; /**< sink buffer */ - struct ipc_config_process ipc_config; - smart_amp_src_func ff_get_frame; /**< function to get stream source */ smart_amp_src_func fb_get_frame; /**< function to get feedback source */ smart_amp_sink_func ff_set_frame; /**< function to set sink */ @@ -69,33 +66,34 @@ struct smart_amp_data { static inline void smart_amp_free_mod_memories(struct smart_amp_data *sad) { /* sof -> mod feed-forward data re-mapping and format conversion */ - rfree(sad->ff_mod.buf.data); + mod_free(sad->mod, sad->ff_mod.buf.data); sad->ff_mod.buf.data = NULL; /* sof -> mod feedback data re-mapping and format conversion */ - rfree(sad->fb_mod.buf.data); + mod_free(sad->mod, sad->fb_mod.buf.data); sad->fb_mod.buf.data = NULL; /* mod -> sof processed data format conversion */ - rfree(sad->out_mod.buf.data); + mod_free(sad->mod, sad->out_mod.buf.data); sad->out_mod.buf.data = NULL; /* mem block for mod private data usage */ - rfree(sad->mod_mems[MOD_MEMBLK_PRIVATE].data); + mod_free(sad->mod, sad->mod_mems[MOD_MEMBLK_PRIVATE].data); sad->mod_mems[MOD_MEMBLK_PRIVATE].data = NULL; /* mem block for mod audio frame data usage */ - rfree(sad->mod_mems[MOD_MEMBLK_FRAME].data); + mod_free(sad->mod, sad->mod_mems[MOD_MEMBLK_FRAME].data); sad->mod_mems[MOD_MEMBLK_FRAME].data = NULL; /* mem block for mod parameter blob usage */ - rfree(sad->mod_mems[MOD_MEMBLK_PARAM].data); + mod_free(sad->mod, sad->mod_mems[MOD_MEMBLK_PARAM].data); sad->mod_mems[MOD_MEMBLK_PARAM].data = NULL; /* inner model data struct */ - rfree(sad->mod_data); + mod_free(sad->mod, sad->mod_data); sad->mod_data = NULL; } -static inline int smart_amp_buf_alloc(struct smart_amp_buf *buf, size_t size) +static inline int smart_amp_buf_alloc(struct processing_module *mod, + struct smart_amp_buf *buf, size_t size) { - buf->data = rballoc(SOF_MEM_FLAG_USER, size); + buf->data = mod_alloc(mod, size); if (!buf->data) return -ENOMEM; buf->size = size; @@ -105,12 +103,12 @@ static inline int smart_amp_buf_alloc(struct smart_amp_buf *buf, size_t size) static ssize_t smart_amp_alloc_mod_memblk(struct smart_amp_data *sad, enum smart_amp_mod_memblk blk) { - struct smart_amp_mod_data_base *mod = sad->mod_data; + struct smart_amp_mod_data_base *smod = sad->mod_data; int ret; size_t size; /* query the required size from inner model. */ - ret = mod->mod_ops->query_memblk_size(mod, blk); + ret = smod->mod_ops->query_memblk_size(smod, blk); if (ret < 0) goto error; @@ -120,12 +118,12 @@ static ssize_t smart_amp_alloc_mod_memblk(struct smart_amp_data *sad, /* allocate the memory block when returned size > 0. */ size = ret; - ret = smart_amp_buf_alloc(&sad->mod_mems[blk], size); + ret = smart_amp_buf_alloc(sad->mod, &sad->mod_mems[blk], size); if (ret < 0) goto error; /* provide the memory block information to inner model. */ - ret = mod->mod_ops->set_memblk(mod, blk, &sad->mod_mems[blk]); + ret = smod->mod_ops->set_memblk(smod, blk, &sad->mod_mems[blk]); if (ret < 0) goto error; @@ -145,21 +143,21 @@ static int smart_amp_alloc_data_buffers(struct comp_dev *dev, /* sof -> mod feed-forward data re-mapping and format conversion */ size = SMART_AMP_FF_BUF_DB_SZ * sizeof(int32_t); - ret = smart_amp_buf_alloc(&sad->ff_mod.buf, size); + ret = smart_amp_buf_alloc(sad->mod, &sad->ff_mod.buf, size); if (ret < 0) goto error; total_size = size; /* sof -> mod feedback data re-mapping and format conversion */ size = SMART_AMP_FB_BUF_DB_SZ * sizeof(int32_t); - ret = smart_amp_buf_alloc(&sad->fb_mod.buf, size); + ret = smart_amp_buf_alloc(sad->mod, &sad->fb_mod.buf, size); if (ret < 0) goto error; total_size += size; /* mod -> sof processed data format conversion */ size = SMART_AMP_FF_BUF_DB_SZ * sizeof(int32_t); - ret = smart_amp_buf_alloc(&sad->out_mod.buf, size); + ret = smart_amp_buf_alloc(sad->mod, &sad->out_mod.buf, size); if (ret < 0) goto error; total_size += size; @@ -172,46 +170,35 @@ static int smart_amp_alloc_data_buffers(struct comp_dev *dev, return ret; } -static struct comp_dev *smart_amp_new(const struct comp_driver *drv, - const struct comp_ipc_config *config, - const void *spec) +static int smart_amp_init(struct processing_module *mod) { - struct comp_dev *dev; - const struct ipc_config_process *ipc_sa = spec; + struct comp_dev *dev = mod->dev; + struct module_config *mcfg = &mod->priv.cfg; struct smart_amp_data *sad; struct sof_smart_amp_config *cfg; size_t bs; int ret; - dev = comp_alloc(drv, sizeof(*dev)); - if (!dev) - return NULL; - - dev->ipc_config = *config; - - sad = rzalloc(SOF_MEM_FLAG_USER, sizeof(*sad)); - if (!sad) { - rfree(dev); - return NULL; - } - - comp_set_drvdata(dev, sad); - sad->ipc_config = *ipc_sa; + /* Allocate smart_amp_data using module API */ + sad = mod_zalloc(mod, sizeof(*sad)); + if (!sad) + return -ENOMEM; - cfg = (struct sof_smart_amp_config *)ipc_sa->data; - bs = ipc_sa->size; + mod->priv.private = sad; + sad->mod = mod; - if (bs > 0 && bs < sizeof(struct sof_smart_amp_config)) { - comp_err(dev, "failed to apply config"); - goto error; + /* Copy config blob if present */ + if (mcfg->avail && mcfg->init_data && mcfg->size >= sizeof(struct sof_smart_amp_config)) { + cfg = (struct sof_smart_amp_config *)mcfg->init_data; + bs = mcfg->size; + memcpy_s(&sad->config, sizeof(struct sof_smart_amp_config), cfg, bs); } - memcpy_s(&sad->config, sizeof(struct sof_smart_amp_config), cfg, bs); - /* allocate inner model data struct */ sad->mod_data = mod_data_create(dev); if (!sad->mod_data) { - comp_err(dev, "failed to allocate nner model data"); + comp_err(dev, "failed to allocate inner model data"); + ret = -ENOMEM; goto error; } @@ -253,22 +240,19 @@ static struct comp_dev *smart_amp_new(const struct comp_driver *drv, } comp_dbg(dev, "used mod config buffer %d bytes", ret); - dev->state = COMP_STATE_READY; - - return dev; - + return 0; error: smart_amp_free_mod_memories(sad); - rfree(sad); - rfree(dev); - return NULL; + mod_free(mod, sad); + return ret; } -static int smart_amp_set_config(struct comp_dev *dev, +static int smart_amp_set_config(struct processing_module *mod, struct sof_ipc_ctrl_data *cdata) { - struct smart_amp_data *sad = comp_get_drvdata(dev); + struct smart_amp_data *sad = module_get_private_data(mod); struct sof_smart_amp_config *cfg; + struct comp_dev *dev = mod->dev; size_t bs; /* Copy new config, find size from header */ @@ -276,7 +260,7 @@ static int smart_amp_set_config(struct comp_dev *dev, ASSUME_ALIGNED(&cdata->data->data, sizeof(uint32_t)); bs = cfg->size; - comp_dbg(dev, "smart_amp_set_config(), actual blob size = %zu, expected blob size = %zu", + comp_dbg(dev, "actual blob size = %zu, expected blob size = %zu", bs, sizeof(struct sof_smart_amp_config)); if (bs != sizeof(struct sof_smart_amp_config)) { @@ -291,17 +275,18 @@ static int smart_amp_set_config(struct comp_dev *dev, return 0; } -static int smart_amp_get_config(struct comp_dev *dev, +static int smart_amp_get_config(struct processing_module *mod, struct sof_ipc_ctrl_data *cdata, int size) { - struct smart_amp_data *sad = comp_get_drvdata(dev); + struct smart_amp_data *sad = module_get_private_data(mod); + struct comp_dev *dev = mod->dev; size_t bs; int ret = 0; /* Copy back to user space */ bs = sad->config.size; - comp_dbg(dev, "smart_amp_get_config(), actual blob size = %zu, expected blob size = %zu", + comp_dbg(dev, "actual blob size = %zu, expected blob size = %zu", bs, sizeof(struct sof_smart_amp_config)); if (bs == 0 || bs > size) @@ -316,24 +301,24 @@ static int smart_amp_get_config(struct comp_dev *dev, return ret; } -#if CONFIG_IPC_MAJOR_3 -static int smart_amp_ctrl_get_bin_data(struct comp_dev *dev, +static int smart_amp_ctrl_get_bin_data(struct processing_module *mod, struct sof_ipc_ctrl_data *cdata, int size) { - struct smart_amp_data *sad = comp_get_drvdata(dev); - struct smart_amp_mod_data_base *mod; + struct smart_amp_data *sad = module_get_private_data(mod); + struct smart_amp_mod_data_base *smod; + struct comp_dev *dev = mod->dev; int ret = 0; assert(sad); - mod = sad->mod_data; + smod = sad->mod_data; switch (cdata->data->type) { case SOF_SMART_AMP_CONFIG: - ret = smart_amp_get_config(dev, cdata, size); + ret = smart_amp_get_config(mod, cdata, size); break; case SOF_SMART_AMP_MODEL: - ret = mod->mod_ops->get_config(mod, cdata, size); + ret = smod->mod_ops->get_config(smod, cdata, size); if (ret < 0) { comp_err(dev, "failed to read inner model!"); return ret; @@ -347,34 +332,38 @@ static int smart_amp_ctrl_get_bin_data(struct comp_dev *dev, return ret; } -static int smart_amp_ctrl_get_data(struct comp_dev *dev, - struct sof_ipc_ctrl_data *cdata, int size) +static int smart_amp_get_configuration(struct processing_module *mod, + uint32_t config_id, uint32_t *data_offset_size, + uint8_t *fragment, size_t fragment_size) { - int ret = 0; + struct sof_ipc_ctrl_data *cdata = (struct sof_ipc_ctrl_data *)fragment; + struct comp_dev *dev = mod->dev; - comp_dbg(dev, "smart_amp_ctrl_get_data() size: %d", size); + comp_dbg(dev, "config_id %u size: %zu", config_id, fragment_size); +#if CONFIG_IPC_MAJOR_3 switch (cdata->cmd) { case SOF_CTRL_CMD_BINARY: - ret = smart_amp_ctrl_get_bin_data(dev, cdata, size); - break; + return smart_amp_ctrl_get_bin_data(mod, cdata, fragment_size); default: comp_err(dev, "invalid cdata->cmd"); - return -EINVAL; } - - return ret; +#elif CONFIG_IPC_MAJOR_4 + return smart_amp_ctrl_get_bin_data(mod, cdata, fragment_size); +#endif + return -EINVAL; } -static int smart_amp_ctrl_set_bin_data(struct comp_dev *dev, +static int smart_amp_ctrl_set_bin_data(struct processing_module *mod, struct sof_ipc_ctrl_data *cdata) { - struct smart_amp_data *sad = comp_get_drvdata(dev); - struct smart_amp_mod_data_base *mod; + struct smart_amp_data *sad = module_get_private_data(mod); + struct smart_amp_mod_data_base *smod; + struct comp_dev *dev = mod->dev; int ret = 0; assert(sad); - mod = sad->mod_data; + smod = sad->mod_data; if (dev->state < COMP_STATE_READY) { comp_err(dev, "driver in init!"); @@ -383,10 +372,10 @@ static int smart_amp_ctrl_set_bin_data(struct comp_dev *dev, switch (cdata->data->type) { case SOF_SMART_AMP_CONFIG: - ret = smart_amp_set_config(dev, cdata); + ret = smart_amp_set_config(mod, cdata); break; case SOF_SMART_AMP_MODEL: - ret = mod->mod_ops->set_config(mod, cdata); + ret = smod->mod_ops->set_config(smod, cdata); if (ret < 0) { comp_err(dev, "failed to write inner model!"); return ret; @@ -400,9 +389,10 @@ static int smart_amp_ctrl_set_bin_data(struct comp_dev *dev, return ret; } -static int smart_amp_ctrl_set_data(struct comp_dev *dev, +static int smart_amp_ctrl_set_data(struct processing_module *mod, struct sof_ipc_ctrl_data *cdata) { + struct comp_dev *dev = mod->dev; int ret = 0; /* Check version from ABI header */ @@ -413,8 +403,8 @@ static int smart_amp_ctrl_set_data(struct comp_dev *dev, switch (cdata->cmd) { case SOF_CTRL_CMD_BINARY: - comp_dbg(dev, "smart_amp_ctrl_set_data(), SOF_CTRL_CMD_BINARY"); - ret = smart_amp_ctrl_set_bin_data(dev, cdata); + comp_dbg(dev, "SOF_CTRL_CMD_BINARY"); + ret = smart_amp_ctrl_set_bin_data(mod, cdata); break; default: comp_err(dev, "invalid cdata->cmd"); @@ -425,77 +415,74 @@ static int smart_amp_ctrl_set_data(struct comp_dev *dev, return ret; } -/* used to pass standard and bespoke commands (with data) to component */ -static int smart_amp_cmd(struct comp_dev *dev, int cmd, void *data, - int max_data_size) +static int smart_amp_set_configuration(struct processing_module *mod, + uint32_t config_id, + enum module_cfg_fragment_position pos, + uint32_t data_offset_size, + const uint8_t *fragment, size_t fragment_size, + uint8_t *response, size_t response_size) { - struct sof_ipc_ctrl_data *cdata = ASSUME_ALIGNED(data, 4); + struct sof_ipc_ctrl_data *cdata = (struct sof_ipc_ctrl_data *)fragment; + struct comp_dev *dev = mod->dev; - comp_dbg(dev, "cmd: %d", cmd); + comp_info(dev, "config_id %u size %zu", config_id, response_size); - switch (cmd) { - case COMP_CMD_SET_DATA: - return smart_amp_ctrl_set_data(dev, cdata); - case COMP_CMD_GET_DATA: - return smart_amp_ctrl_get_data(dev, cdata, max_data_size); +#if CONFIG_IPC_MAJOR_3 + switch (cdata->cmd) { + case SOF_CTRL_CMD_BINARY: + comp_info(dev, "SOF_CTRL_CMD_BINARY"); + return smart_amp_ctrl_set_data(mod, cdata); default: - return -EINVAL; + comp_err(dev, "unknown cmd %d", cdata->cmd); } -} +#elif CONFIG_IPC_MAJOR_4 + return smart_amp_ctrl_set_data(mod, cdata); #endif + return -EINVAL; +} -static void smart_amp_free(struct comp_dev *dev) +static int smart_amp_free(struct processing_module *mod) { - struct smart_amp_data *sad = comp_get_drvdata(dev); + struct smart_amp_data *sad = module_get_private_data(mod); + struct comp_dev *dev = mod->dev; - comp_dbg(dev, "smart_amp_free()"); + comp_dbg(dev, "entry"); + if (!sad) + return 0; smart_amp_free_mod_memories(sad); - rfree(sad); - sad = NULL; - rfree(dev); - dev = NULL; -} - -static int smart_amp_verify_params(struct comp_dev *dev, - struct sof_ipc_stream_params *params) -{ - int ret; - - comp_dbg(dev, "smart_amp_verify_params()"); - - ret = comp_verify_params(dev, BUFF_PARAMS_CHANNELS, params); - if (ret < 0) { - comp_err(dev, "volume_verify_params() error: comp_verify_params() failed."); - return ret; - } + mod_free(mod, sad); + mod->priv.private = NULL; return 0; } -static int smart_amp_params(struct comp_dev *dev, - struct sof_ipc_stream_params *params) +#if CONFIG_IPC_MAJOR_4 +static void smart_amp_ipc4_params(struct processing_module *mod) { - int err; + struct sof_ipc_stream_params *params = mod->stream_params; + struct comp_buffer *sinkb, *sourceb; + struct comp_dev *dev = mod->dev; - comp_dbg(dev, "smart_amp_params()"); + ipc4_base_module_cfg_to_stream_params(&mod->priv.cfg.base_cfg, params); + component_set_nearest_period_frames(dev, params->rate); - err = smart_amp_verify_params(dev, params); - if (err < 0) { - comp_err(dev, "pcm params verification failed."); - return -EINVAL; - } + sourceb = list_first_item(&dev->bsource_list, struct comp_buffer, sink_list); + ipc4_update_buffer_format(sourceb, &mod->priv.cfg.base_cfg.audio_fmt); - return 0; + sinkb = list_first_item(&dev->bsink_list, struct comp_buffer, source_list); + ipc4_update_buffer_format(sinkb, &mod->priv.cfg.base_cfg.audio_fmt); } +#endif /* CONFIG_IPC_MAJOR_4 */ -static int smart_amp_trigger(struct comp_dev *dev, int cmd) +static int smart_amp_trigger(struct processing_module *mod, int cmd) { - struct smart_amp_data *sad = comp_get_drvdata(dev); + struct smart_amp_data *sad = module_get_private_data(mod); + struct comp_dev *dev = mod->dev; int ret = 0; - comp_dbg(dev, "smart_amp_trigger(), command = %u", cmd); + comp_dbg(dev, "command = %u", cmd); ret = comp_set_state(dev, cmd); @@ -516,13 +503,14 @@ static int smart_amp_trigger(struct comp_dev *dev, int cmd) return ret; } -static int smart_amp_ff_process(struct comp_dev *dev, +static int smart_amp_ff_process(struct processing_module *mod, const struct audio_stream *source, const struct audio_stream *sink, uint32_t frames, const int8_t *chan_map) { - struct smart_amp_data *sad = comp_get_drvdata(dev); - struct smart_amp_mod_data_base *mod = sad->mod_data; + struct smart_amp_data *sad = module_get_private_data(mod); + struct smart_amp_mod_data_base *smod = sad->mod_data; + struct comp_dev *dev = mod->dev; int ret; sad->ff_mod.consumed = 0; @@ -541,7 +529,7 @@ static int smart_amp_ff_process(struct comp_dev *dev, sad->ff_get_frame(&sad->ff_mod, frames, source, chan_map); - ret = mod->mod_ops->ff_proc(mod, frames, &sad->ff_mod, &sad->out_mod); + ret = smod->mod_ops->ff_proc(smod, frames, &sad->ff_mod, &sad->out_mod); if (ret) { comp_err(dev, "feed forward inner model process error"); return ret; @@ -552,12 +540,13 @@ static int smart_amp_ff_process(struct comp_dev *dev, return 0; } -static int smart_amp_fb_process(struct comp_dev *dev, +static int smart_amp_fb_process(struct processing_module *mod, const struct audio_stream *source, uint32_t frames, const int8_t *chan_map) { - struct smart_amp_data *sad = comp_get_drvdata(dev); - struct smart_amp_mod_data_base *mod = sad->mod_data; + struct smart_amp_data *sad = module_get_private_data(mod); + struct smart_amp_mod_data_base *smod = sad->mod_data; + struct comp_dev *dev = mod->dev; int ret; sad->fb_mod.consumed = 0; @@ -575,7 +564,7 @@ static int smart_amp_fb_process(struct comp_dev *dev, sad->fb_get_frame(&sad->fb_mod, frames, source, chan_map); - ret = mod->mod_ops->fb_proc(mod, frames, &sad->fb_mod); + ret = smod->mod_ops->fb_proc(smod, frames, &sad->fb_mod); if (ret) { comp_err(dev, "feedback inner model process error"); return ret; @@ -584,11 +573,14 @@ static int smart_amp_fb_process(struct comp_dev *dev, return 0; } -static int smart_amp_copy(struct comp_dev *dev) +static int smart_amp_process(struct processing_module *mod, + struct sof_source **sources, int num_of_sources, + struct sof_sink **sinks, int num_of_sinks) { - struct smart_amp_data *sad = comp_get_drvdata(dev); + struct smart_amp_data *sad = module_get_private_data(mod); + struct comp_dev *dev = mod->dev; struct comp_buffer *source_buf = sad->source_buf; - struct comp_buffer *sink_buf = sad->sink_buf; + struct comp_buffer *sink_buf = comp_buffer_get_from_sink(sinks[0]); uint32_t avail_passthrough_frames; uint32_t avail_feedback_frames; uint32_t avail_frames; @@ -596,7 +588,7 @@ static int smart_amp_copy(struct comp_dev *dev) uint32_t sink_bytes; uint32_t feedback_bytes; - comp_dbg(dev, "smart_amp_copy()"); + comp_dbg(dev, "%d sources %d sinks", num_of_sources, num_of_sinks); /* available bytes and samples calculation */ avail_passthrough_frames = audio_stream_avail_frames(&source_buf->stream, @@ -623,7 +615,7 @@ static int smart_amp_copy(struct comp_dev *dev) /* perform buffer writeback after source_buf process */ buffer_stream_invalidate(feedback_buf, feedback_bytes); - smart_amp_fb_process(dev, &feedback_buf->stream, + smart_amp_fb_process(mod, &feedback_buf->stream, avail_feedback_frames, sad->config.feedback_ch_map); @@ -642,7 +634,7 @@ static int smart_amp_copy(struct comp_dev *dev) source_bytes = avail_frames * audio_stream_frame_bytes(&source_buf->stream); buffer_stream_invalidate(source_buf, source_bytes); - smart_amp_ff_process(dev, &source_buf->stream, &sink_buf->stream, + smart_amp_ff_process(mod, &source_buf->stream, &sink_buf->stream, avail_frames, sad->config.source_ch_map); comp_dbg(dev, "processing %u feed forward frames (consumed: %u, produced: %u)", @@ -660,26 +652,20 @@ static int smart_amp_copy(struct comp_dev *dev) return 0; } -static int smart_amp_reset(struct comp_dev *dev) +static int smart_amp_reset(struct processing_module *mod) { - struct smart_amp_data *sad = comp_get_drvdata(dev); - struct smart_amp_mod_data_base *mod = sad->mod_data; - int ret; + struct smart_amp_data *sad = module_get_private_data(mod); + struct smart_amp_mod_data_base *smod = sad->mod_data; + struct comp_dev *dev = mod->dev; - comp_dbg(dev, "smart_amp_reset()"); + comp_dbg(dev, "entry"); sad->ff_get_frame = NULL; sad->fb_get_frame = NULL; sad->ff_set_frame = NULL; /* reset inner model */ - ret = mod->mod_ops->reset(mod); - if (ret) - return ret; - - comp_set_state(dev, COMP_TRIGGER_RESET); - - return 0; + return smod->mod_ops->reset(smod); } /* supported formats: {SOF_IPC_FRAME_S16_LE, SOF_IPC_FRAME_S24_4LE, SOF_IPC_FRAME_S32_LE} @@ -690,17 +676,18 @@ static inline bool is_supported_fmt(uint16_t fmt) return fmt <= SOF_IPC_FRAME_S32_LE; } -static int smart_amp_resolve_mod_fmt(struct comp_dev *dev, uint32_t least_req_depth) +static int smart_amp_resolve_mod_fmt(struct processing_module *mod, uint32_t least_req_depth) { - struct smart_amp_data *sad = comp_get_drvdata(dev); - struct smart_amp_mod_data_base *mod = sad->mod_data; + struct smart_amp_data *sad = module_get_private_data(mod); + struct smart_amp_mod_data_base *smod = sad->mod_data; + struct comp_dev *dev = mod->dev; const uint16_t *mod_fmts; int num_mod_fmts; int ret; int i; /* get supported formats from mod */ - ret = mod->mod_ops->get_supported_fmts(mod, &mod_fmts, &num_mod_fmts); + ret = smod->mod_ops->get_supported_fmts(smod, &mod_fmts, &num_mod_fmts); if (ret) { comp_err(dev, "failed to get supported formats"); return ret; @@ -712,7 +699,7 @@ static int smart_amp_resolve_mod_fmt(struct comp_dev *dev, uint32_t least_req_de /* set frame format to inner model */ comp_dbg(dev, "set mod format to %u", mod_fmts[i]); - ret = mod->mod_ops->set_fmt(mod, mod_fmts[i]); + ret = smod->mod_ops->set_fmt(smod, mod_fmts[i]); if (ret) { comp_err(dev, "failed setting format %u", @@ -728,32 +715,36 @@ static int smart_amp_resolve_mod_fmt(struct comp_dev *dev, uint32_t least_req_de return -EINVAL; } -static int smart_amp_prepare(struct comp_dev *dev) +static int smart_amp_prepare(struct processing_module *mod, + struct sof_source **sources, int num_of_sources, + struct sof_sink **sinks, int num_of_sinks) { - struct smart_amp_data *sad = comp_get_drvdata(dev); + struct smart_amp_data *sad = module_get_private_data(mod); + struct comp_dev *dev = mod->dev; uint16_t ff_src_fmt, fb_src_fmt, resolved_mod_fmt; uint32_t least_req_depth; - int ret; - - comp_dbg(dev, "smart_amp_prepare()"); - - ret = comp_set_state(dev, COMP_TRIGGER_PREPARE); - if (ret < 0) - return ret; + int ret, i; - /* searching for stream and feedback source buffers */ - struct comp_buffer *source_buffer; + comp_dbg(dev, "%d sources %d sinks", num_of_sources, num_of_sinks); +#if CONFIG_IPC_MAJOR_4 + smart_amp_ipc4_params(mod); +#endif - comp_dev_for_each_producer(dev, source_buffer) { - if (comp_buffer_get_source_component(source_buffer)->ipc_config.type - == SOF_COMP_DEMUX) - sad->feedback_buf = source_buffer; + /* In module API, state is managed by the framework, so no comp_set_state needed */ + for (i = 0; i < num_of_sources; i++) { + /* NOTE: This should not work in module based environment: + * sources[i]->bound_module->dev->ipc_config.type == SOF_COMP_DEMUX + * So let's check which one of the sources is from a capture stream. + * The code is not tested and may not work. + */ + if (sources[i]->bound_module->dev->direction == SOF_IPC_STREAM_CAPTURE) + sad->feedback_buf = comp_buffer_get_from_source(sources[i]); else - sad->source_buf = source_buffer; + sad->source_buf = comp_buffer_get_from_source(sources[i]); } /* sink buffer */ - sad->sink_buf = comp_dev_get_first_data_consumer(dev); + sad->sink_buf = comp_buffer_get_from_sink(sinks[0]); if (!sad->sink_buf) { comp_err(dev, "no sink buffer"); return -ENOTCONN; @@ -778,14 +769,13 @@ static int smart_amp_prepare(struct comp_dev *dev) fb_src_fmt = audio_stream_get_frm_fmt(&sad->feedback_buf->stream); sad->fb_mod.channels = MIN(SMART_AMP_FB_MAX_CH_NUM, audio_stream_get_channels(&sad->feedback_buf->stream)); - least_req_depth = MAX(least_req_depth, get_sample_bitdepth(fb_src_fmt)); } /* resolve the frame format for inner model. The return value will be the applied format * or the negative error code. */ - ret = smart_amp_resolve_mod_fmt(dev, least_req_depth); + ret = smart_amp_resolve_mod_fmt(mod, least_req_depth); if (ret < 0) return ret; @@ -809,36 +799,21 @@ static int smart_amp_prepare(struct comp_dev *dev) comp_dbg(dev, "fb mod buffer channels:%u fmt_conv:%u -> %u", sad->fb_mod.channels, fb_src_fmt, sad->fb_mod.frame_fmt); } + return 0; } -static const struct comp_driver comp_smart_amp = { - .type = SOF_COMP_SMART_AMP, - .uid = SOF_RT_UUID(UUID_SYM), - .tctx = &smart_amp_comp_tr, - .ops = { - .create = smart_amp_new, - .free = smart_amp_free, - .params = smart_amp_params, - .prepare = smart_amp_prepare, -#if CONFIG_IPC_MAJOR_3 - .cmd = smart_amp_cmd, -#endif - .trigger = smart_amp_trigger, - .copy = smart_amp_copy, - .reset = smart_amp_reset, - }, -}; - -static SHARED_DATA struct comp_driver_info comp_smart_amp_info = { - .drv = &comp_smart_amp, +static struct module_interface smart_amp_interface = { + .init = smart_amp_init, + .prepare = smart_amp_prepare, + .process = smart_amp_process, + .set_configuration = smart_amp_set_configuration, + .get_configuration = smart_amp_get_configuration, + .reset = smart_amp_reset, + .free = smart_amp_free, + .trigger = smart_amp_trigger, }; -UT_STATIC void sys_comp_smart_amp_init(void) -{ - comp_register(platform_shared_get(&comp_smart_amp_info, - sizeof(comp_smart_amp_info))); -} - -DECLARE_MODULE(sys_comp_smart_amp_init); -SOF_MODULE_INIT(smart_amp, sys_comp_smart_amp_init); +DECLARE_TR_CTX(smart_amp_comp_tr, SOF_UUID(UUID_SYM), LOG_LEVEL_INFO); +DECLARE_MODULE_ADAPTER(smart_amp_interface, UUID_SYM, smart_amp_comp_tr); +SOF_MODULE_INIT(smart_amp, sys_comp_module_smart_amp_interface_init); diff --git a/src/audio/smart_amp/smart_amp_maxim_dsm.c b/src/audio/smart_amp/smart_amp_maxim_dsm.c index 3360ff5c3b86..b9289861aa8d 100644 --- a/src/audio/smart_amp/smart_amp_maxim_dsm.c +++ b/src/audio/smart_amp/smart_amp_maxim_dsm.c @@ -20,6 +20,8 @@ #include <sof/audio/smart_amp/smart_amp.h> #include "dsm_api_public.h" +LOG_MODULE_DECLARE(smart_amp, CONFIG_SOF_LOG_LEVEL); + /* Maxim DSM(Dynamic Speaker Management) process buffer size */ #define DSM_FRM_SZ 48 #define DSM_FF_BUF_SZ (DSM_FRM_SZ * SMART_AMP_FF_MAX_CH_NUM) @@ -319,7 +321,7 @@ static int maxim_dsm_get_param(struct smart_amp_mod_struct_t *hspk, * required size */ if (bs > size) { - comp_err(dev, "[DSM] maxim_dsm_get_param(): invalid size %d", bs); + comp_err(dev, "[DSM] invalid size %d", bs); return -EINVAL; } @@ -392,7 +394,7 @@ static int maxim_dsm_set_param(struct smart_amp_mod_struct_t *hspk, */ retcode = dsm_api_set_params(hspk->dsmhandle, 1, value); if (retcode != DSM_API_OK) { - comp_err(dev, "[DSM] maxim_dsm_set_param() write failure. (id:%x, ret:%x)", + comp_err(dev, "[DSM] write failure. (id:%x, ret:%x)", id, retcode); return -EINVAL; } @@ -420,7 +422,7 @@ static int maxim_dsm_restore_param(struct smart_amp_mod_struct_t *hspk) retcode = dsm_api_set_params(hspk->dsmhandle, 1, value); if (retcode != DSM_API_OK) { - comp_err(dev, "[DSM] maxim_dsm_restore_param() write failure. (id:%x, ret:%x)", + comp_err(dev, "[DSM] write failure. (id:%x, ret:%x)", value[DSM_SET_ID_IDX], retcode); return -EINVAL; } diff --git a/src/audio/smart_amp/smart_amp_passthru.c b/src/audio/smart_amp/smart_amp_passthru.c index d2e408a6ccd4..53084345d6d1 100644 --- a/src/audio/smart_amp/smart_amp_passthru.c +++ b/src/audio/smart_amp/smart_amp_passthru.c @@ -10,12 +10,13 @@ #include <sof/trace/trace.h> #include <user/trace.h> -#include <sof/bit.h> #include <sof/common.h> #include <stdint.h> #include <stdlib.h> #include <sof/audio/smart_amp/smart_amp.h> +LOG_MODULE_DECLARE(smart_amp, CONFIG_SOF_LOG_LEVEL); + /* self-declared inner model data struct */ struct passthru_mod_data { struct smart_amp_mod_data_base base; diff --git a/src/audio/sound_dose/README.md b/src/audio/sound_dose/README.md index 2a25ad19d1ba..0436da7b886d 100644 --- a/src/audio/sound_dose/README.md +++ b/src/audio/sound_dose/README.md @@ -1,75 +1,14 @@ -Sound Dose +# Sound Dose Architecture -The purpose of this component is to calculate with DSP offload the -headphone audio playback MEL values (momentary sound exposure level). -The calculated MEL values are notified to user space to be available -every one second. The user space should respond to notification with -bytes control get to get the data. The MEL values are used to -calculate the CSD value (cumulative sound dose). Low enough CSD value -ensures the music listening is safe the user's hearing. +This directory contains the Sound Dose component. -The calculation of MEL values and CSD values is defined in EN 50332-3 -stadard. The implementation in the Android OS is described in -https://source.android.com/docs/core/audio/sound-dose . +## Overview -The SOF Sound Dose component should be placed in topology as last -component before playback dai-copier. +Monitors the acoustic energy output to headphones over time, ensuring it complies with regulations governing maximum cumulative sound exposure (to protect user hearing). -Currently it can be tested with next test topologies: -- sof-hda-benchmark-sound_dose32.tplg -- sof-mtl-sdw-benchmark-sound_dose32-sdw0.tplg -- sof-mtl-sdw-benchmark-sound_dose32-simplejack.tplg +## Configuration and Scripts -E.g. in the sdw topologies the controls for setting it up can be -seen with command: - -$ amixer -c0 controls | grep "Jack Out Sound Dose" -numid=33,iface=MIXER,name='Jack Out Sound Dose data bytes' -numid=32,iface=MIXER,name='Jack Out Sound Dose gain bytes' -numid=30,iface=MIXER,name='Jack Out Sound Dose setup bytes' -numid=31,iface=MIXER,name='Jack Out Sound Dose volume bytes' - -The above topologies program the setup bytes for acoustical -sensitivity of acoustical 100 dBSPL for 0 dBFS digital level. -The gain is set to 0 dB and volume to 0 dB. - -To test the basics copy a compatible test topology over the -normal topology file. Or alternative use module options -tplg_filename and tplg_path for module snd_sof. - -Get kcontrol_events from https://github.com/ujfalusi/kcontrol_events -and build and install it. - -Start playback of some music or test signal. Note that there is no SOF -volume control component in the test topology, so it plays out at max -volume. - -Start in other console kcontrol_events. The control 'Jack Out Sound -Dose data bytes' should get updates every one second. The structures -are defined in src/include/user/audio_feature.h and -src/include/user/sound_dose.h. - -The next steps require build of blobs for sof-ctl. The blobs can be -rebuilt with command - -cd src/audio/sound_dose/tune; octave --quiet --no-window-system sof_sound_dose_blobs.m - -To simulate effect of initial sensitivity setup, try command - -sof-ctl -c name='Jack Out Sound Dose setup bytes' -s <path>/sound_dose/setup_sens_0db.txt -sof-ctl -c name='Jack Out Sound Dose setup bytes' -s <path>/sound_dose/setup_sens_100db.txt - -To simulate adjusting codec volume control down 10 dB and up again 10 dB - -sof-ctl -c name='Jack Out Sound Dose volume bytes' -s ctl4/sound_dose/setup_vol_-10db.txt -sof-ctl -c name='Jack Out Sound Dose volume bytes' -s ctl4/sound_dose/setup_vol_0db.txt - -To force user's listening level 10 dB down after observing too large dose per the MSD value - -sof-ctl -c name='Jack Out Sound Dose gain bytes' -s ctl4/sound_dose/setup_gain_-10db.txt - -To restore user's listening level to non-attenuated - -sof-ctl -c name='Jack Out Sound Dose gain bytes' -s ctl4/sound_dose/setup_gain_0db.txt - -The above commands have impact to data shown by kcontrol_events. +- **Kconfig**: Dictates the Sound Dose component (`COMP_SOUND_DOSE`) with prerequisites on `IPC_MAJOR_4` and `COMP_MODULE_ADAPTER`, plus mathematical modules (`MATH_EXP`, `MATH_IIR`). +- **CMakeLists.txt**: Compiles generic operations (`sound_dose-generic.c`, `sound_dose.c`) alongside their IPC wrappers (`sound_dose-ipc4.c`), offering full `llext` enablement. +- **sound_dose.toml**: Contains topology descriptors, declaring UUID `UUIDREG_STR_SOUND_DOSE` and standard memory allocation bounds. +- **Topology (.conf)**: Extracted from `tools/topology/topology2/include/components/sound_dose.conf`, configuring a `sound_dose` widget of type `effect` (UUID `7c:9d:3f:a4:75:ea:d5:44:94:2d:96:79:91:a3:38:09`). diff --git a/src/audio/sound_dose/sound_dose.c b/src/audio/sound_dose/sound_dose.c index dcbac5c643c7..c28b55baee4d 100644 --- a/src/audio/sound_dose/sound_dose.c +++ b/src/audio/sound_dose/sound_dose.c @@ -27,11 +27,6 @@ SOF_DEFINE_REG_UUID(sound_dose); /* Creates logging data for the component */ LOG_MODULE_REGISTER(sound_dose, CONFIG_SOF_LOG_LEVEL); -/* Creates the component trace. Traces show in trace console the component - * info, warning, and error messages. - */ -DECLARE_TR_CTX(sound_dose_tr, SOF_UUID(sound_dose_uuid), LOG_LEVEL_INFO); - void sound_dose_report_mel(const struct processing_module *mod) { struct sound_dose_comp_data *cd = module_get_private_data(mod); @@ -223,7 +218,7 @@ static int sound_dose_process(struct processing_module *mod, int frames = source_get_data_frames_available(source); int sink_frames = sink_get_free_frames(sink); - comp_dbg(dev, "sound_dose_process()"); + comp_dbg(dev, "entry"); if (cd->gain_update) { /* Convert dB * 100 to Q8.24 */ @@ -270,7 +265,7 @@ static int sound_dose_prepare(struct processing_module *mod, struct comp_dev *dev = mod->dev; enum sof_ipc_frame source_format; - comp_dbg(dev, "sound_dose_prepare()"); + comp_dbg(dev, "entry"); /* The processing example in this component supports one input and one * output. Generally there can be more. @@ -307,7 +302,7 @@ static int sound_dose_reset(struct processing_module *mod) { struct sound_dose_comp_data *cd = module_get_private_data(mod); - comp_dbg(mod->dev, "sound_dose_reset()"); + comp_dbg(mod->dev, "entry"); sound_dose_setup_init(cd); return 0; @@ -329,13 +324,10 @@ __cold static int sound_dose_free(struct processing_module *mod) struct sound_dose_comp_data *cd = module_get_private_data(mod); assert_can_be_cold(); - comp_dbg(mod->dev, "sound_dose_free()"); + comp_dbg(mod->dev, "entry"); sound_dose_filters_free(cd); - if (cd->msg) { - rfree(cd->msg->tx_data); - rfree(cd->msg); - } + ipc_msg_free(cd->msg); rfree(cd->abi); rfree(cd); return 0; @@ -369,6 +361,7 @@ SOF_LLEXT_BUILDINFO; #else +DECLARE_TR_CTX(sound_dose_tr, SOF_UUID(sound_dose_uuid), LOG_LEVEL_INFO); DECLARE_MODULE_ADAPTER(sound_dose_interface, sound_dose_uuid, sound_dose_tr); SOF_MODULE_INIT(sound_dose, sys_comp_module_sound_dose_interface_init); diff --git a/src/audio/src/README.md b/src/audio/src/README.md new file mode 100644 index 000000000000..0cb434a67b81 --- /dev/null +++ b/src/audio/src/README.md @@ -0,0 +1,23 @@ +# Synchronous Sample Rate Converter (SRC) Architecture + +This directory contains the Synchronous SRC implementation. + +## Overview + +Converts sample rates by an exact, locked integer or fractional ratio (e.g., 48 kHz to 44.1 kHz) using a polyphase filter bank. + +## Architecture Diagram + +```mermaid +graph LR + In[48kHz Stream] --> SRC[Polyphase FIR Interpolator] + SRC --> Out[44.1kHz Stream] +``` + +## Configuration and Scripts + +- **Kconfig**: Extensive options for `COMP_SRC`, determining conversion quality targets and memory consumption limitations: standard (`COMP_SRC_STD`), small (`COMP_SRC_SMALL`), and tiny (`COMP_SRC_TINY`) filter sets. Alternatively targets IPC4 full conversions (`COMP_SRC_IPC4_FULL_MATRIX`), and supports lite conversions (`COMP_SRC_LITE`). +- **CMakeLists.txt**: Wraps numerous internal source implementations spanning multiple SIMD platforms (`src_hifi2ep.c`, `src_hifi3.c`, `src_hifi4.c`, `src_hifi5.c`) and connects the required IPC APIs. Allows Zephyr out-of-tree loader configurations (`llext`). +- **src.toml**: Declares massive topology limits under `mod_cfg` per chipset to enforce correct processing behavior of varied rate conversions internally, specifying `UUIDREG_STR_SRC4` and `UUIDREG_STR_SRC_LITE`. +- **Topology (.conf)**: Instantiated via `tools/topology/topology2/include/components/src.conf`, utilizing `rate_in` and `rate_out` attributes to specify integer transformations. Associates as a `src` widget type with UUID `8d:b2:1b:e6:9a:14:1f:4c:b7:09:46:82:3e:f5:f5:ae`. +- **MATLAB Tuning (`tune/`)**: Houses heavily comprehensive scripts like `sof_src_generate.m` which compute optimal polyphase filter bank coefficients for a massive grid of supported input-to-output fractional sample rate conversions. The scripts export these fixed-point (or float) arrays as C header data structs (`.h` code outputs) allowing the DSP to pull static transition parameters depending on memory (`std`, `small`, `tiny`, `lite`) profiles. diff --git a/src/audio/src/llext/CMakeLists.txt b/src/audio/src/llext/CMakeLists.txt index dfb9782ee941..7cd52be725c7 100644 --- a/src/audio/src/llext/CMakeLists.txt +++ b/src/audio/src/llext/CMakeLists.txt @@ -7,6 +7,7 @@ sof_llext_build("src" ../src_generic.c ../src_hifi3.c ../src_hifi4.c + ../src_hifi5.c ../src.c ../src_common.c ../src_ipc4.c @@ -19,6 +20,7 @@ sof_llext_build("src" ../src_generic.c ../src_hifi3.c ../src_hifi4.c + ../src_hifi5.c ../src.c ../src_common.c ../src_ipc4.c diff --git a/src/audio/src/src.c b/src/audio/src/src.c index 3b1eca197b6a..9870ad911ca8 100644 --- a/src/audio/src/src.c +++ b/src/audio/src/src.c @@ -78,8 +78,6 @@ static const struct module_interface src_interface = { .prepare = src_prepare, .process = src_process, .is_ready_to_process = src_is_ready_to_process, - .set_configuration = src_set_config, - .get_configuration = src_get_config, .reset = src_reset, .free = src_free, }; diff --git a/src/audio/src/src.toml b/src/audio/src/src.toml index 4cf472b27641..ab496c5b8a80 100644 --- a/src/audio/src/src.toml +++ b/src/audio/src/src.toml @@ -72,7 +72,7 @@ 23, 0, 0, 0, 12832, 21852000, 180, 256, 0, 21852, 0, 24, 0, 0, 0, 12832, 12629000, 256, 512, 0, 12629, 0, 25, 0, 0, 0, 12832, 13996000, 128, 256, 0, 13996, 0] -#elif CONFIG_SOC_INTEL_ACE30 || CONFIG_SOC_INTEL_ACE40 +#elif CONFIG_SOC_ACE30 || CONFIG_SOC_ACE40 mod_cfg = [0, 0, 0, 0, 12832, 30633000, 128, 512, 0, 30633, 0, 1, 0, 0, 0, 12832, 28143000, 64, 256, 0, 28143, 0, 2, 0, 0, 0, 12832, 33513000, 96, 512, 0, 33513, 0, diff --git a/src/audio/src/src_common.c b/src/audio/src/src_common.c index 5d25413193a8..3a9c7dac280f 100644 --- a/src/audio/src/src_common.c +++ b/src/audio/src/src_common.c @@ -384,13 +384,12 @@ int src_copy_sxx(struct comp_data *cd, struct sof_source *source, return -ENOTSUP; } -void src_set_alignment(struct sof_source *source, struct sof_sink *sink) +void src_set_alignment(struct sof_source *source) { - const uint32_t byte_align = 1; - const uint32_t frame_align_req = 1; + const uint32_t byte_align = SOF_FRAME_BYTE_ALIGN; + const uint32_t frame_align_req = SOF_FRAME_COUNT_ALIGN; source_set_alignment_constants(source, byte_align, frame_align_req); - sink_set_alignment_constants(sink, byte_align, frame_align_req); } static int src_verify_params(struct processing_module *mod) @@ -537,7 +536,7 @@ int src_params_general(struct processing_module *mod, if (!cd->delay_lines) { comp_err(dev, "failed to alloc cd->delay_lines, delay_lines_size = %zu", delay_lines_size); - return -EINVAL; + return -ENOMEM; } /* Clear all delay lines here */ @@ -669,24 +668,6 @@ int src_process(struct processing_module *mod, return cd->src_func(cd, sources[0], sinks[0]); } -__cold int src_set_config(struct processing_module *mod, uint32_t config_id, - enum module_cfg_fragment_position pos, uint32_t data_offset_size, - const uint8_t *fragment, size_t fragment_size, uint8_t *response, - size_t response_size) -{ - assert_can_be_cold(); - - return -EINVAL; -} - -__cold int src_get_config(struct processing_module *mod, uint32_t config_id, - uint32_t *data_offset_size, uint8_t *fragment, size_t fragment_size) -{ - assert_can_be_cold(); - - return -EINVAL; -} - int src_reset(struct processing_module *mod) { struct comp_data *cd = module_get_private_data(mod); @@ -701,9 +682,23 @@ int src_reset(struct processing_module *mod) __cold int src_free(struct processing_module *mod) { + struct comp_data *cd = module_get_private_data(mod); + assert_can_be_cold(); comp_info(mod->dev, "entry"); +#if CONFIG_FAST_GET + struct src_param *a = &cd->param; + + if (a->stage1) + mod_fast_put(mod, a->stage1->coefs); + if (a->stage2) + mod_fast_put(mod, a->stage2->coefs); +#endif + + mod_free(mod, cd->delay_lines); + mod_free(mod, cd); + return 0; } diff --git a/src/audio/src/src_common.h b/src/audio/src/src_common.h index 6a8028662ffd..98f29a263131 100644 --- a/src/audio/src/src_common.h +++ b/src/audio/src/src_common.h @@ -143,7 +143,7 @@ int32_t src_input_rates(void); int32_t src_output_rates(void); -void src_set_alignment(struct sof_source *source, struct sof_sink *sink); +void src_set_alignment(struct sof_source *source); struct comp_data { #if CONFIG_IPC_MAJOR_4 @@ -242,12 +242,6 @@ int src_process(struct processing_module *mod, struct sof_source **sources, int num_of_sources, struct sof_sink **sinks, int num_of_sinks); -int src_set_config(struct processing_module *mod, uint32_t config_id, - enum module_cfg_fragment_position pos, uint32_t data_offset_size, - const uint8_t *fragment, size_t fragment_size, uint8_t *response, - size_t response_size); -int src_get_config(struct processing_module *mod, uint32_t config_id, - uint32_t *data_offset_size, uint8_t *fragment, size_t fragment_size); int src_free(struct processing_module *mod); int src_reset(struct processing_module *mod); extern struct tr_ctx src_tr; diff --git a/src/audio/src/src_ipc3.c b/src/audio/src/src_ipc3.c index b161155193fc..541efcdb24be 100644 --- a/src/audio/src/src_ipc3.c +++ b/src/audio/src/src_ipc3.c @@ -105,7 +105,7 @@ int src_prepare_general(struct processing_module *mod, enum sof_ipc_frame sink_format; /* set align requirements */ - src_set_alignment(source, sink); + src_set_alignment(source); /* get source/sink data format */ source_format = source_get_frm_fmt(source); diff --git a/src/audio/src/src_ipc4.c b/src/audio/src/src_ipc4.c index 1240a86bd48e..91f286347a5f 100644 --- a/src/audio/src/src_ipc4.c +++ b/src/audio/src/src_ipc4.c @@ -151,7 +151,7 @@ int src_prepare_general(struct processing_module *mod, int ret = 0; /* set align requirements */ - src_set_alignment(source, sink); + src_set_alignment(source); switch (cd->ipc_config.base.audio_fmt.valid_bit_depth) { #if CONFIG_FORMAT_S16LE diff --git a/src/audio/src/src_lite.c b/src/audio/src/src_lite.c index cefe60367bfd..9d5593ff34ca 100644 --- a/src/audio/src/src_lite.c +++ b/src/audio/src/src_lite.c @@ -61,8 +61,6 @@ const struct module_interface src_lite_interface = { .prepare = src_lite_prepare, .process = src_process, .is_ready_to_process = src_is_ready_to_process, - .set_configuration = src_set_config, - .get_configuration = src_get_config, .reset = src_reset, .free = src_free, }; diff --git a/src/audio/stft_process/CMakeLists.txt b/src/audio/stft_process/CMakeLists.txt new file mode 100644 index 000000000000..e3a13e17bb93 --- /dev/null +++ b/src/audio/stft_process/CMakeLists.txt @@ -0,0 +1,16 @@ +# SPDX-License-Identifier: BSD-3-Clause + +if(CONFIG_COMP_STFT_PROCESS STREQUAL "m" AND DEFINED CONFIG_LLEXT) + add_subdirectory(llext ${PROJECT_BINARY_DIR}/stft_process_llext) + add_dependencies(app stft_process) +else() + add_local_sources(sof stft_process.c) + add_local_sources(sof stft_process_setup.c) + add_local_sources(sof stft_process_common.c) + add_local_sources(sof stft_process-generic.c) + add_local_sources(sof stft_process-hifi3.c) + + if(CONFIG_IPC_MAJOR_4) + add_local_sources(sof stft_process-ipc4.c) + endif() +endif() diff --git a/src/audio/stft_process/Kconfig b/src/audio/stft_process/Kconfig new file mode 100644 index 000000000000..426d272477a9 --- /dev/null +++ b/src/audio/stft_process/Kconfig @@ -0,0 +1,32 @@ +# SPDX-License-Identifier: BSD-3-Clause + +config COMP_STFT_PROCESS + tristate "STFT processing component" + default n + select MATH_FFT + select MATH_32BIT_FFT + select MATH_FFT_MULTI + help + Select for stft_process component. STFT acronym means + short term Fourier transform. It converts audio + to multiple FFTs with selected FFT size, hop, and + window function. Possible signal processing can be + done in it in frequency domain for FFTs that is + efficient for more complex signal processing techniques. + The component converts then the frequency domain + version of signal back to normal PCM audio stream + with inverse STFT. + +if COMP_STFT_PROCESS + +rsource "Kconfig.simd" + +config STFT_PROCESS_MAGNITUDE_PHASE + bool "Convert FFTs to polar magnitude and phase" + default n + help + Select for processing in polar magnitude and phase + domain. Such complex values format is common for + frequency domain signal processing. + +endif # COMP_STFT_PROCESS diff --git a/src/audio/stft_process/Kconfig.simd b/src/audio/stft_process/Kconfig.simd new file mode 100644 index 000000000000..0b981df583fa --- /dev/null +++ b/src/audio/stft_process/Kconfig.simd @@ -0,0 +1,31 @@ +# SPDX-License-Identifier: BSD-3-Clause + +comment "STFT Process optimization level select" + +choice "COMP_STFT_PROCESS_SIMD_LEVEL_SELECT" + prompt "Choose which SIMD level is used for the STFT Process module" + depends on COMP_STFT_PROCESS + default COMP_STFT_PROCESS_HIFI_MAX + + config COMP_STFT_PROCESS_HIFI_MAX + prompt "SIMD will be selected by toolchain pre-defined header" + bool + help + When this is selected, the optimization level will be + determined by the toolchain pre-defined macros in the + core isa header file. + + config COMP_STFT_PROCESS_HIFI_3 + prompt "Choose HIFI3 intrinsic optimized STFT Process module" + bool + help + This option is used to build HIFI3 intrinsic optimized + STFT Process code. + + config COMP_STFT_PROCESS_HIFI_NONE + prompt "Choose generic C STFT Process module, no HIFI SIMD involved" + bool + help + This option is used to build STFT Process + with generic C code. +endchoice diff --git a/src/audio/stft_process/README.md b/src/audio/stft_process/README.md new file mode 100644 index 000000000000..074b2c0cf331 --- /dev/null +++ b/src/audio/stft_process/README.md @@ -0,0 +1,14 @@ +# STFT Process Architecture + +This directory provides Short-Time Fourier Transform (STFT) utilities. + +## Overview + +Converts time-domain audio slices into the frequency domain using FFTs, runs frequency-domain processing over those bins, and applies the inverse-STFT (Overlap-Add) back to the time-domain. + +## Configuration and Scripts + +- **Kconfig**: Manages the STFT processing component (`COMP_STFT_PROCESS`) and requires multi-channel 32-bit FFT math libraries (`MATH_FFT`, `MATH_32BIT_FFT`, `MATH_FFT_MULTI`). Also optionally supports converting pure FFTs into polar magnitude and phase domains (`STFT_PROCESS_MAGNITUDE_PHASE`). +- **CMakeLists.txt**: Compiles generic STFT operations (`stft_process.c`, `stft_process_common.c`, `stft_process-generic.c`) and IPC4 wrappers (`stft_process-ipc4.c`), fully supporting the `llext` format. +- **stft_process.toml**: Defines the default `STFTPROC` module layout with UUID `UUIDREG_STR_STFT_PROCESS`. +- **Topology (.conf)**: Instantiated from `tools/topology/topology2/include/components/stft_process.conf`, creating a `stft_process` widget type `effect` (UUID `a6:6e:11:0d:50:91:de:46:98:b8:b2:b3:a7:91:da:29`). diff --git a/src/audio/stft_process/llext/CMakeLists.txt b/src/audio/stft_process/llext/CMakeLists.txt new file mode 100644 index 000000000000..133330c4a69d --- /dev/null +++ b/src/audio/stft_process/llext/CMakeLists.txt @@ -0,0 +1,11 @@ +# Copyright (c) 2025 Intel Corporation. +# SPDX-License-Identifier: Apache-2.0 + +sof_llext_build("stft_process" + SOURCES ../stft_process.c + ../stft_process_setup.c + ../stft_process_common.c + ../stft_process-generic.c + ../stft_process-ipc4.c + LIB openmodules +) diff --git a/src/audio/stft_process/llext/llext.toml.h b/src/audio/stft_process/llext/llext.toml.h new file mode 100644 index 000000000000..e3988e4bb4ed --- /dev/null +++ b/src/audio/stft_process/llext/llext.toml.h @@ -0,0 +1,6 @@ +#include <tools/rimage/config/platform.toml> +#define LOAD_TYPE "2" +#include "../stft_process.toml" + +[module] +count = __COUNTER__ diff --git a/src/audio/stft_process/stft_process-generic.c b/src/audio/stft_process/stft_process-generic.c new file mode 100644 index 000000000000..5241c372261f --- /dev/null +++ b/src/audio/stft_process/stft_process-generic.c @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// Copyright(c) 2025-2026 Intel Corporation. + +#include <sof/audio/format.h> +#include <sof/common.h> +#include <assert.h> +#include <stdint.h> +#include "stft_process.h" + +#if SOF_USE_HIFI(NONE, COMP_STFT_PROCESS) +void stft_process_overlap_add_ifft_buffer(struct stft_process_state *state, int ch) +{ + struct stft_process_buffer *obuf = &state->obuf[ch]; + struct stft_process_fft *fft = &state->fft; + int32_t *w = obuf->w_ptr; + int32_t sample; + int i; + int n; + int samples_remain = fft->fft_size; + int idx = 0; + + while (samples_remain) { + n = stft_process_buffer_samples_without_wrap(obuf, w); + n = MIN(samples_remain, n); + + /* Abort if n is zero to avoid infinite loop. The assert can + * trigger only with incorrect usage of this function. + */ + assert(n); + for (i = 0; i < n; i++) { + sample = Q_MULTSR_32X32((int64_t)state->gain_comp, fft->fft_buf[idx].real, + 31, 31, 31); + *w = sat_int32((int64_t)*w + sample); + w++; + idx++; + } + w = stft_process_buffer_wrap(obuf, w); + samples_remain -= n; + } + + w = obuf->w_ptr + fft->fft_hop_size; + obuf->w_ptr = stft_process_buffer_wrap(obuf, w); + obuf->s_avail += fft->fft_hop_size; + obuf->s_free -= fft->fft_hop_size; +} + +void stft_process_apply_window(struct stft_process_state *state) +{ + struct stft_process_fft *fft = &state->fft; + int j; + + /* Multiply Q1.31 by Q1.15 gives Q2.46, shift right by 15 to get Q2.31, no saturate need */ + for (j = 0; j < fft->fft_size; j++) + fft->fft_buf[j].real = + sat_int32(Q_MULTSR_32X32((int64_t)fft->fft_buf[j].real, + state->window[j], 31, 31, 31)); +} +#endif /* SOF_USE_HIFI(NONE, COMP_STFT_PROCESS) */ diff --git a/src/audio/stft_process/stft_process-hifi3.c b/src/audio/stft_process/stft_process-hifi3.c new file mode 100644 index 000000000000..6cf7c3dc7e85 --- /dev/null +++ b/src/audio/stft_process/stft_process-hifi3.c @@ -0,0 +1,145 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// Copyright(c) 2025-2026 Intel Corporation. + +/** + * \file + * \brief HiFi3 SIMD-optimized helpers for the STFT processing component. + * + * This compilation unit provides HiFi3 intrinsic versions of selected + * hot-path helpers. It is guarded by SOF_USE_MIN_HIFI(3, COMP_STFT_PROCESS) + * so only one of the generic or hifi implementations is active. + */ + +#include <sof/audio/format.h> +#include <sof/common.h> +#include <assert.h> +#include <stdint.h> +#include "stft_process.h" + +#if SOF_USE_MIN_HIFI(3, COMP_STFT_PROCESS) + +#include <xtensa/tie/xt_hifi3.h> + +/** + * stft_process_apply_window() - Multiply FFT buffer by the analysis window. + * @state: STFT processing state that contains the FFT buffer and window. + * + * The real part of each icomplex32 sample in the FFT buffer is multiplied + * by the corresponding Q1.31 window coefficient. + */ +void stft_process_apply_window(struct stft_process_state *state) +{ + struct stft_process_fft *fft = &state->fft; + ae_int32 *buf; + const ae_int32x2 *win; + ae_f32x2 data01, data23; + ae_f32x2 win01, win23; + ae_int32x2 d0, d1; + int fft_size = fft->fft_size; + int j; + int n4; + + /* + * buf points to {real, imag} pairs (struct icomplex32). + * win points to scalar Q1.31 window coefficients. + * + * We load each complex pair, multiply only the real part by the + * window value, then store the pair back with the updated real. + * The imaginary part is left untouched. + * + * Stride for buf is sizeof(ae_int32x2) = 8 bytes per complex sample. + * Stride for win is sizeof(ae_int32) = 4 bytes per scalar window value. + */ + buf = (ae_int32 *)fft->fft_buf; + win = (const ae_int32x2 *)state->window; + + assert(!(fft_size & 3)); + + /* Main loop: process 4 samples per iteration */ + n4 = fft_size >> 2; + for (j = 0; j < n4; j++) { + /* Load four FFT real part values, combine into fft_data, + * buf[0] goes to data01 low, buf[1] goes to data01 high. + */ + d0 = AE_L32_I(buf, 0 * sizeof(ae_int32x2)); + d1 = AE_L32_I(buf, 1 * sizeof(ae_int32x2)); + data01 = AE_SEL32_HH(d0, d1); + d0 = AE_L32_I(buf, 2 * sizeof(ae_int32x2)); + d1 = AE_L32_I(buf, 3 * sizeof(ae_int32x2)); + data23 = AE_SEL32_HH(d0, d1); + + /* Load four window coefficients, + * win[0] goes to win01 low, win[1] goes to win01 high + */ + AE_L32X2_IP(win01, win, sizeof(ae_int32x2)); + AE_L32X2_IP(win23, win, sizeof(ae_int32x2)); + + /* Multiply with window function */ + data01 = AE_MULFP32X2RS(data01, win01); + data23 = AE_MULFP32X2RS(data23, win23); + + /* Store back the updated real parts */ + AE_S32_L_IP(AE_SEL32_LH(data01, data01), buf, sizeof(ae_int32x2)); + AE_S32_L_IP(data01, buf, sizeof(ae_int32x2)); + AE_S32_L_IP(AE_SEL32_LH(data23, data23), buf, sizeof(ae_int32x2)); + AE_S32_L_IP(data23, buf, sizeof(ae_int32x2)); + } +} + +/** + * stft_process_overlap_add_ifft_buffer() - Overlap-add IFFT output to circular output buffer. + * @state: STFT processing state. + * @ch: Channel index. + * + * Each IFFT output sample is multiplied by gain_comp (Q1.31 x Q1.31) and + * added with saturation to the existing content of the circular output + * buffer. HiFi3 AE_MULF32S_HH handles the multiply and + * AE_ADD32S provides the saturating accumulation. + * + * Note: obuf must be even number of samples and 64-bit aligned. + */ +void stft_process_overlap_add_ifft_buffer(struct stft_process_state *state, int ch) +{ + struct stft_process_buffer *obuf = &state->obuf[ch]; + struct stft_process_fft *fft = &state->fft; + ae_f32x2 gain = AE_MOVDA32(state->gain_comp); + ae_f32x2 buffer_data; + ae_f32x2 fft_data; + ae_f32x2 d0, d1; + ae_f32x2 *w = (ae_f32x2 *)obuf->w_ptr; + ae_f32 *fft_p = (ae_f32 *)fft->fft_buf; + int samples_remain = fft->fft_size; + int i, n; + + while (samples_remain) { + n = stft_process_buffer_samples_without_wrap(obuf, (int32_t *)w); + + /* The samples count must be even and not zero, the latter to avoid infinite + * loop. The assert can trigger only with incorrect usage of this function. + */ + assert(n && !(n & 1)); + n = MIN(samples_remain, n) >> 1; + for (i = 0; i < n; i++) { + /* Load two FFT real part values, combine into fft_data */ + AE_L32_IP(d0, fft_p, sizeof(ae_f32x2)); + AE_L32_IP(d1, fft_p, sizeof(ae_f32x2)); + fft_data = AE_SEL32_HH(d0, d1); + + /* Load buffer data, multiply fft_data with gain and accumulate, and + * store to output buffer. + */ + buffer_data = AE_L32X2_I(w, 0); + AE_MULAFP32X2RS(buffer_data, fft_data, gain); + AE_S32X2_IP(buffer_data, w, sizeof(ae_f32x2)); + } + w = (ae_f32x2 *)stft_process_buffer_wrap(obuf, (int32_t *)w); + samples_remain -= n << 1; + } + + obuf->w_ptr = stft_process_buffer_wrap(obuf, obuf->w_ptr + fft->fft_hop_size); + obuf->s_avail += fft->fft_hop_size; + obuf->s_free -= fft->fft_hop_size; +} + +#endif /* SOF_USE_MIN_HIFI(3, COMP_STFT_PROCESS) */ diff --git a/src/audio/stft_process/stft_process-ipc4.c b/src/audio/stft_process/stft_process-ipc4.c new file mode 100644 index 000000000000..ac6d9b5e56b5 --- /dev/null +++ b/src/audio/stft_process/stft_process-ipc4.c @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// Copyright(c) 2025 Intel Corporation. + +#include <sof/audio/module_adapter/module/generic.h> +#include <sof/audio/component.h> +#include "stft_process.h" + +LOG_MODULE_DECLARE(stft_process, CONFIG_SOF_LOG_LEVEL); + +/* IPC4 controls handler */ +__cold int stft_process_set_config(struct processing_module *mod, uint32_t param_id, + enum module_cfg_fragment_position pos, uint32_t data_offset_size, + const uint8_t *fragment, size_t fragment_size, uint8_t *response, + size_t response_size) +{ + struct stft_comp_data *cd = module_get_private_data(mod); + struct comp_dev *dev = mod->dev; + + assert_can_be_cold(); + + switch (param_id) { + case SOF_IPC4_SWITCH_CONTROL_PARAM_ID: + case SOF_IPC4_ENUM_CONTROL_PARAM_ID: + comp_err(dev, "Illegal control param_id %d.", param_id); + return -EINVAL; + } + + if (fragment_size != sizeof(struct sof_stft_process_config)) { + comp_err(dev, "Illegal fragment size %d, expect %d.", fragment_size, + sizeof(struct sof_stft_process_config)); + return -EINVAL; + } + + if (!cd->config) { + cd->config = mod_alloc(mod, sizeof(struct sof_stft_process_config)); + if (!cd->config) { + comp_err(dev, "Failed to allocate configuration."); + return -ENOMEM; + } + } + + memcpy_s(cd->config, sizeof(struct sof_stft_process_config), fragment, fragment_size); + return 0; +} + +/* Not used in IPC4 systems, if IPC4 only component, omit .get_configuration set */ +__cold int stft_process_get_config(struct processing_module *mod, uint32_t config_id, + uint32_t *data_offset_size, uint8_t *fragment, + size_t fragment_size) +{ + assert_can_be_cold(); + return 0; +} diff --git a/src/audio/stft_process/stft_process.c b/src/audio/stft_process/stft_process.c new file mode 100644 index 000000000000..5fb96a267f15 --- /dev/null +++ b/src/audio/stft_process/stft_process.c @@ -0,0 +1,254 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// Copyright(c) 2025 Intel Corporation. + +#include <sof/audio/module_adapter/module/generic.h> +#include <sof/audio/sink_source_utils.h> +#include <sof/audio/sink_api.h> +#include <sof/audio/source_api.h> +#include <rtos/init.h> +#include "stft_process.h" + +/* UUID identifies the components. Use e.g. command uuidgen from package + * uuid-runtime, add it to uuid-registry.txt in SOF top level. + */ +SOF_DEFINE_REG_UUID(stft_process); + +/* Creates logging data for the component */ +LOG_MODULE_REGISTER(stft_process, CONFIG_SOF_LOG_LEVEL); + +#if STFT_DEBUG +FILE *stft_debug_fft_in_fh; +FILE *stft_debug_fft_out_fh; +FILE *stft_debug_ifft_out_fh; +#endif + +/** + * stft_process_init() - Initialize the stft_process component. + * @mod: Pointer to module data. + * + * This function is called when the instance is created. The + * macro __cold informs that the code that is non-critical + * is loaded to slower but large DRAM. + * + * Return: Zero if success, otherwise error code. + */ +__cold static int stft_process_init(struct processing_module *mod) +{ + struct module_data *md = &mod->priv; + struct comp_dev *dev = mod->dev; + struct stft_comp_data *cd; + + assert_can_be_cold(); + + comp_info(dev, "entry"); + + cd = mod_alloc(mod, sizeof(*cd)); + if (!cd) + return -ENOMEM; + + md->private = cd; + memset(cd, 0, sizeof(*cd)); + +#if STFT_DEBUG + stft_debug_fft_in_fh = fopen("stft_debug_fft_in.txt", "w"); + if (!stft_debug_fft_in_fh) { + fprintf(stderr, "Debug file open failed.\n"); + return -EINVAL; + } + + stft_debug_fft_out_fh = fopen("stft_debug_fft_out.txt", "w"); + if (!stft_debug_fft_out_fh) { + fprintf(stderr, "Debug file open failed.\n"); + return -EINVAL; + } + + stft_debug_ifft_out_fh = fopen("stft_debug_ifft_out.txt", "w"); + if (!stft_debug_ifft_out_fh) { + fprintf(stderr, "Debug file open failed.\n"); + fclose(stft_debug_fft_out_fh); + return -EINVAL; + } +#endif + + return 0; +} + +/** + * stft_process_process() - The audio data processing function. + * @mod: Pointer to module data. + * @sources: Pointer to audio samples data sources array. + * @num_of_sources: Number of sources in the array. + * @sinks: Pointer to audio samples data sinks array. + * @num_of_sinks: Number of sinks in the array. + * + * This is the processing function that is called for scheduled + * pipelines. The processing is controlled by the enable switch. + * + * Return: Zero if success, otherwise error code. + */ +static int stft_process_process(struct processing_module *mod, + struct sof_source **sources, + int num_of_sources, + struct sof_sink **sinks, + int num_of_sinks) +{ + struct stft_comp_data *cd = module_get_private_data(mod); + struct sof_source *source = sources[0]; /* One input in this example */ + struct sof_sink *sink = sinks[0]; /* One output in this example */ + int frames = source_get_data_frames_available(source); + int sink_frames = sink_get_free_frames(sink); + int ret; + + frames = MIN(frames, sink_frames); + + /* Process the data with the channels swap example function. */ + ret = cd->stft_process_func(mod, source, sink, frames); + + return ret; +} + +/** + * stft_process_prepare() - Prepare the component for processing. + * @mod: Pointer to module data. + * @sources: Pointer to audio samples data sources array. + * @num_of_sources: Number of sources in the array. + * @sinks: Pointer to audio samples data sinks array. + * @num_of_sinks: Number of sinks in the array. + * + * Function prepare is called just before the pipeline is started. In + * this case the audio format parameters are for better code performance + * saved to component data to avoid to find out them in process. The + * processing function pointer is set to process the current audio format. + * + * Return: Value zero if success, otherwise error code. + */ +static int stft_process_prepare(struct processing_module *mod, + struct sof_source **sources, int num_of_sources, + struct sof_sink **sinks, int num_of_sinks) +{ + struct stft_comp_data *cd = module_get_private_data(mod); + struct comp_dev *dev = mod->dev; + enum sof_ipc_frame source_format; + int ret; + + comp_dbg(dev, "prepare"); + + /* The processing example in this component supports one input and one + * output. Generally there can be more. + */ + if (num_of_sources != 1 || num_of_sinks != 1) { + comp_err(dev, "Only one source and one sink is supported."); + return -EINVAL; + } + + /* Initialize STFT, max_frames is set to dev->frames + 4 */ + if (!cd->config) { + comp_err(dev, "Can't prepare without bytes control configuration."); + return -EINVAL; + } + + /* get source data format */ + cd->max_frames = dev->frames + 2; + cd->frame_bytes = source_get_frame_bytes(sources[0]); + cd->channels = source_get_channels(sources[0]); + source_format = source_get_frm_fmt(sources[0]); + + ret = stft_process_setup(mod, cd->max_frames, source_get_rate(sources[0]), + source_get_channels(sources[0])); + if (ret < 0) { + comp_err(dev, "setup failed."); + return ret; + } + + cd->stft_process_func = stft_process_find_proc_func(source_format); + if (!cd->stft_process_func) { + comp_err(dev, "No processing function found for format %d.", + source_format); + return -EINVAL; + } + + return 0; +} + +/** + * stft_process_reset() - Reset the component. + * @mod: Pointer to module data. + * + * The component reset is called when pipeline is stopped. The reset + * should return the component to same state as init. + * + * Return: Value zero, always success. + */ +static int stft_process_reset(struct processing_module *mod) +{ + struct stft_comp_data *cd = module_get_private_data(mod); + + comp_dbg(mod->dev, "reset"); + + stft_process_free_buffers(mod); + memset(cd, 0, sizeof(*cd)); + return 0; +} + +/** + * stft_process_free() - Free dynamic allocations. + * @mod: Pointer to module data. + * + * Component free is called when the pipelines are deleted. All + * dynamic allocations need to be freed here. The macro __cold + * instructs the build to locate this performance wise non-critical + * function to large and slower DRAM. + * + * Return: Value zero, always success. + */ +__cold static int stft_process_free(struct processing_module *mod) +{ + struct stft_comp_data *cd = module_get_private_data(mod); + + assert_can_be_cold(); + + comp_dbg(mod->dev, "free"); + mod_free(mod, cd); + +#if STFT_DEBUG + fclose(stft_debug_fft_in_fh); + fclose(stft_debug_fft_out_fh); + fclose(stft_debug_ifft_out_fh); +#endif + return 0; +} + +/* This defines the module operations */ +static const struct module_interface stft_process_interface = { + .init = stft_process_init, + .prepare = stft_process_prepare, + .process = stft_process_process, + .set_configuration = stft_process_set_config, + .get_configuration = stft_process_get_config, + .reset = stft_process_reset, + .free = stft_process_free +}; + +/* This controls build of the module. If COMP_MODULE is selected in kconfig + * this is build as dynamically loadable module. + */ +#if CONFIG_COMP_STFT_PROCESS_MODULE + +#include <module/module/api_ver.h> +#include <module/module/llext.h> +#include <rimage/sof/user/manifest.h> + +static const struct sof_man_module_manifest mod_manifest __section(".module") __used = + SOF_LLEXT_MODULE_MANIFEST("STFT_PROCESS", &stft_process_interface, 1, + SOF_REG_UUID(stft_process), 40); + +SOF_LLEXT_BUILDINFO; + +#else + +DECLARE_TR_CTX(stft_process_tr, SOF_UUID(stft_process_uuid), LOG_LEVEL_INFO); +DECLARE_MODULE_ADAPTER(stft_process_interface, stft_process_uuid, stft_process_tr); +SOF_MODULE_INIT(stft_process, sys_comp_module_stft_process_interface_init); + +#endif diff --git a/src/audio/stft_process/stft_process.h b/src/audio/stft_process/stft_process.h new file mode 100644 index 000000000000..558023ba55bc --- /dev/null +++ b/src/audio/stft_process/stft_process.h @@ -0,0 +1,339 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * + * Copyright(c) 2025 Intel Corporation. + * + */ +#ifndef __SOF_AUDIO_STFT_PROCESS_H__ +#define __SOF_AUDIO_STFT_PROCESS_H__ + +#include <sof/audio/module_adapter/module/generic.h> +#include <sof/math/auditory.h> +#include <sof/math/dct.h> +#include <sof/math/fft.h> + +#include <stdbool.h> +#include <stdint.h> + +#if CONFIG_LIBRARY +#define STFT_DEBUG 0 /* Keep zero, produces large files with fprintf() */ +#else +#define STFT_DEBUG 0 +#endif + +#define SOF_STFT_PROCESS_CONFIG_MAX_SIZE 256 /* Max size for configuration data in bytes */ + +enum sof_stft_process_fft_pad_type { + STFT_PAD_END = 0, + STFT_PAD_CENTER = 1, + STFT_PAD_START = 2, +}; + +enum sof_stft_process_fft_window_type { + STFT_RECTANGULAR_WINDOW = 0, + STFT_BLACKMAN_WINDOW = 1, + STFT_HAMMING_WINDOW = 2, + STFT_HANN_WINDOW = 3, + STFT_POVEY_WINDOW = 4, +}; + +struct sof_stft_process_config { + uint32_t size; /**< Size of this struct in bytes */ + uint32_t reserved[8]; + int32_t sample_frequency; /**< Hz. e.g. 16000 */ + int32_t window_gain_comp; /**< Q1.31 gain for IFFT */ + int32_t reserved_32; + int16_t channel; /**< -1 expect mono, 0 left, 1 right, ... */ + int16_t frame_length; /**< samples, e.g. 400 for 25 ms @ 16 kHz*/ + int16_t frame_shift; /**< samples, e.g. 160 for 10 ms @ 16 kHz */ + int16_t reserved_16; + enum sof_stft_process_fft_pad_type pad; /**< Use PAD_END, PAD_CENTER, PAD_START */ + enum sof_stft_process_fft_window_type window; /**< Use RECTANGULAR_WINDOW, etc. */ +} __attribute__((packed)); + +struct stft_process_buffer { + int32_t *addr; + int32_t *end_addr; + int32_t *r_ptr; + int32_t *w_ptr; + int s_avail; /**< samples count */ + int s_free; /**< samples count */ + int s_length; /**< length in samples for wrap */ +}; + +struct stft_process_fft { + struct icomplex32 *fft_buf; /**< fft_padded_size */ + struct icomplex32 *fft_out; /**< fft_padded_size */ + struct ipolar32 *fft_polar; + struct fft_multi_plan *fft_plan; + struct fft_multi_plan *ifft_plan; + int fft_size; + int fft_padded_size; + int fft_hop_size; + int fft_buf_size; + int half_fft_size; + size_t fft_buffer_size; /**< bytes */ +}; + +struct stft_process_state { + struct stft_process_buffer ibuf[PLATFORM_MAX_CHANNELS]; /**< Buffer for input data */ + struct stft_process_buffer obuf[PLATFORM_MAX_CHANNELS]; /**< Buffer for output data */ + struct stft_process_fft fft; /**< FFT related */ + int32_t *prev_data[PLATFORM_MAX_CHANNELS]; /**< prev_data_size */ + int32_t gain_comp; /**< Gain to compensate window gain */ + int32_t *buffers; + int32_t *window; /**< fft_size */ + int source_channel; + int prev_data_size; + int sample_rate; + bool waiting_fill; /**< booleans */ + bool prev_samples_valid; +}; + +/** + * struct stft_process_func - Function call pointer for process function + * @mod: Pointer to module data. + * @source: Source for PCM samples data. + * @sink: Sink for PCM samples data. + * @frames: Number of audio data frames to process. + */ +typedef int (*stft_process_func)(const struct processing_module *mod, + struct sof_source *source, + struct sof_sink *sink, + uint32_t frames); + +/** + * struct stft_comp_data + * @stft_process_func: Pointer to used processing function. + * @channels_order[]: Vector with desired sink channels order. + * @source_format: Source samples format. + * @frame_bytes: Number of bytes in an audio frame. + * @channels: Channels count. + * @enable: Control processing on/off, on - reorder channels + */ +struct stft_comp_data { + stft_process_func stft_process_func; /**< processing function */ + struct stft_process_state state; + struct sof_stft_process_config *config; + size_t frame_bytes; + int source_channel; + int max_frames; + int channels; + bool fft_done; +}; + +static inline int stft_process_buffer_samples_without_wrap(struct stft_process_buffer *buffer, + int32_t *ptr) +{ + return buffer->end_addr - ptr; +} + +static inline int32_t *stft_process_buffer_wrap(struct stft_process_buffer *buffer, int32_t *ptr) +{ + if (ptr >= buffer->end_addr) + ptr -= buffer->s_length; + + return ptr; +} + +/** + * struct stft_process_proc_fnmap - processing functions for frame formats + * @frame_fmt: Current frame format + * @stft_process_proc_func: Function pointer for the suitable processing function + */ +struct stft_process_proc_fnmap { + enum sof_ipc_frame frame_fmt; + stft_process_func stft_process_function; +}; + +/** + * stft_process_find_proc_func() - Find suitable processing function. + * @src_fmt: Enum value for PCM format. + * + * This function finds the suitable processing function to use for + * the used PCM format. If not found, return NULL. + * + * Return: Pointer to processing function for the requested PCM format. + */ +stft_process_func stft_process_find_proc_func(enum sof_ipc_frame src_fmt); + +#if CONFIG_IPC_MAJOR_4 +/** + * stft_process_set_config() - Handle controls set + * @mod: Pointer to module data. + * @param_id: Id to know control type, used to know ALSA control type. + * @pos: Position of the fragment in the large message. + * @data_offset_size: Size of the whole configuration if it is the first or only + * fragment. Otherwise it is offset of the fragment. + * @fragment: Message payload data. + * @fragment_size: Size of this fragment. + * @response_size: Size of response. + * + * This function handles the real-time controls. The ALSA controls have the + * param_id set to indicate the control type. The control ID, from topology, + * is used to separate the controls instances of same type. In control payload + * the num_elems defines to how many channels the control is applied to. + * + * Return: Zero if success, otherwise error code. + */ +int stft_process_set_config(struct processing_module *mod, uint32_t param_id, + enum module_cfg_fragment_position pos, uint32_t data_offset_size, + const uint8_t *fragment, size_t fragment_size, + uint8_t *response, size_t response_size); +/** + * stft_process_set_config() - Handle controls get + * @mod: Pointer to module data. + * @config_id: Configuration ID. + * @data_offset_size: Size of the whole configuration if it is the first or only + * fragment. Otherwise it is offset of the fragment. + * @fragment: Message payload data. + * @fragment_size: Size of this fragment. + * + * This function is used for controls get. + * + * Return: Zero if success, otherwise error code. + */ +int stft_process_get_config(struct processing_module *mod, uint32_t config_id, + uint32_t *data_offset_size, uint8_t *fragment, size_t fragment_size); +#else +static inline int stft_process_set_config(struct processing_module *mod, uint32_t param_id, + enum module_cfg_fragment_position pos, + uint32_t data_offset_size, + const uint8_t *fragment, size_t fragment_size, + uint8_t *response, size_t response_size) +{ + return 0; +} + +static inline int stft_process_get_config(struct processing_module *mod, uint32_t config_id, + uint32_t *data_offset_size, uint8_t *fragment, + size_t fragment_size) +{ + return 0; +} +#endif + +/** + * stft_process_setup() - Initialize STFT processing state and allocate buffers. + * @mod: Pointer to processing module. + * @max_frames: Maximum number of frames per processing call. + * @rate: Audio sample rate in Hz. + * @channels: Number of audio channels. + * + * Configures FFT parameters, allocates aligned sample and FFT buffers, + * sets up window function, and creates FFT/IFFT plans based on the + * component configuration. + * + * Return: Zero on success, otherwise a negative error code. + */ +int stft_process_setup(struct processing_module *mod, int max_frames, int rate, int channels); + +/** + * stft_process_source_s16() - Copy S16_LE source data to STFT internal buffers. + * @cd: STFT component data. + * @source: Source for PCM samples data. + * @frames: Number of audio data frames to process. + * + * De-interleaves S16_LE audio frames from the source circular buffer + * into per-channel internal circular buffers. Each 16-bit sample is + * converted to Q1.31 format by left-shifting 16 bits. + * + * Return: Zero on success, otherwise an error code. + */ +int stft_process_source_s16(struct stft_comp_data *cd, struct sof_source *source, int frames); + +/** + * stft_process_sink_s16() - Copy STFT internal buffers to S16_LE sink. + * @cd: STFT component data. + * @sink: Sink for PCM samples data. + * @frames: Number of audio data frames to produce. + * + * Interleaves per-channel STFT output buffers into the sink circular + * buffer in S16_LE format. Q1.31 samples are converted to Q1.15 with + * rounding and saturation. Output buffer samples are cleared after + * reading to prepare for the next overlap-add cycle. + * + * Return: Zero on success, otherwise an error code. + */ +int stft_process_sink_s16(struct stft_comp_data *cd, struct sof_sink *sink, int frames); + +/** + * stft_process_source_s32() - Copy S32_LE source data to STFT internal buffers. + * @cd: STFT component data. + * @source: Source for PCM samples data. + * @frames: Number of audio data frames to process. + * + * De-interleaves S32_LE audio frames from the source circular buffer + * into per-channel internal circular buffers. + * + * Return: Zero on success, otherwise an error code. + */ +int stft_process_source_s32(struct stft_comp_data *cd, struct sof_source *source, int frames); + +/** + * stft_process_sink_s32() - Copy STFT internal buffers to S32_LE sink. + * @cd: STFT component data. + * @sink: Sink for PCM samples data. + * @frames: Number of audio data frames to produce. + * + * Interleaves per-channel STFT output buffers into the sink circular + * buffer in S32_LE format. The output buffer samples are cleared after + * reading to prepare for the next overlap-add cycle. + * + * Return: Zero on success, otherwise an error code. + */ +int stft_process_sink_s32(struct stft_comp_data *cd, struct sof_sink *sink, int frames); + +/** + * stft_process_free_buffers() - Free all STFT processing buffers. + * @mod: Pointer to processing module. + * + * Releases sample buffers, FFT buffers, and FFT/IFFT plans allocated + * during stft_process_setup(). + */ +void stft_process_free_buffers(struct processing_module *mod); + +/** + * stft_process_fill_prev_samples() - Save overlap samples for next STFT frame. + * @buf: Circular buffer to read overlap samples from. + * @prev_data: Destination array for the overlap data. + * @prev_data_length: Number of samples to copy. + * + * Copies prev_data_length samples from the circular buffer into the + * linear prev_data array, handling wrap-around as needed. + */ +void stft_process_fill_prev_samples(struct stft_process_buffer *buf, int32_t *prev_data, + int prev_data_length); + +/** + * stft_process_fill_fft_buffer() - Assemble FFT input from overlap and new data. + * @state: STFT processing state. + * @ch: Channel index. + * + * Constructs the FFT input buffer by concatenating the previous overlap + * samples and one hop of new samples from the input circular buffer. + * Imaginary parts are set to zero. The overlap buffer is updated with + * data for the next frame. + */ +void stft_process_fill_fft_buffer(struct stft_process_state *state, int ch); + +/** + * stft_process_apply_window() - Multiply FFT buffer by the analysis window. + * @state: STFT processing state that contains the FFT buffer and window. + * + * The real part of each complex sample in the FFT buffer is multiplied + * by the corresponding Q1.31 window coefficient. + */ +void stft_process_apply_window(struct stft_process_state *state); + +/** + * stft_process_overlap_add_ifft_buffer() - Overlap-add IFFT output to circular output buffer. + * @state: STFT processing state. + * @ch: Channel index. + * + * Each IFFT output sample is multiplied by the gain compensation value + * and added with saturation to the existing content of the circular + * output buffer. + */ +void stft_process_overlap_add_ifft_buffer(struct stft_process_state *state, int ch); + +#endif // __SOF_AUDIO_STFT_PROCESS_H__ diff --git a/src/audio/stft_process/stft_process.toml b/src/audio/stft_process/stft_process.toml new file mode 100644 index 000000000000..5e0f3b805c7d --- /dev/null +++ b/src/audio/stft_process/stft_process.toml @@ -0,0 +1,21 @@ +#ifndef LOAD_TYPE +#define LOAD_TYPE "0" +#endif + + REM # Template component module config + [[module.entry]] + name = "STFTPROC" + uuid = UUIDREG_STR_STFT_PROCESS + affinity_mask = "0x1" + instance_count = "40" + domain_types = "0" + load_type = LOAD_TYPE + module_type = "9" + auto_start = "0" + sched_caps = [1, 0x00008000] + REM # pin = [dir, type, sample rate, size, container, channel-cfg] + pin = [0, 0, 0xfeef, 0xf, 0xf, 0x45ff, 1, 0, 0xfeef, 0xf, 0xf, 0x1ff] + REM # mod_cfg [PAR_0 PAR_1 PAR_2 PAR_3 IS_BYTES CPS IBS OBS MOD_FLAGS CPC OBLS] + mod_cfg = [0, 0, 0, 0, 4096, 1000000, 128, 128, 0, 0, 0] + + index = __COUNTER__ diff --git a/src/audio/stft_process/stft_process_common.c b/src/audio/stft_process/stft_process_common.c new file mode 100644 index 000000000000..6ab3199082de --- /dev/null +++ b/src/audio/stft_process/stft_process_common.c @@ -0,0 +1,623 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// Copyright(c) 2025 Intel Corporation. + +#include <sof/audio/component.h> +#include <sof/audio/audio_stream.h> +#include <sof/audio/format.h> +#include <sof/audio/sink_api.h> +#include <sof/audio/sink_source_utils.h> +#include <sof/audio/source_api.h> +#include <sof/math/auditory.h> +#include <sof/math/icomplex32.h> +#include <sof/math/matrix.h> +#include <sof/math/sqrt.h> +#include <sof/math/trig.h> +#include <sof/math/window.h> +#include <sof/trace/trace.h> + +#include "stft_process.h" + +#include <errno.h> +#include <stddef.h> +#include <stdint.h> +#include <string.h> + +#if STFT_DEBUG +extern FILE *stft_debug_fft_in_fh; +extern FILE *stft_debug_fft_out_fh; +extern FILE *stft_debug_ifft_out_fh; + +static void debug_print_to_file_real(FILE *fh, struct icomplex32 *c, int n) +{ + for (int i = 0; i < n; i++) + fprintf(fh, "%d\n", c[i].real); +} + +static void debug_print_to_file_complex(FILE *fh, struct icomplex32 *c, int n) +{ + for (int i = 0; i < n; i++) + fprintf(fh, "%d %d\n", c[i].real, c[i].imag); +} +#endif + +#if CONFIG_FORMAT_S32LE +int stft_process_source_s32(struct stft_comp_data *cd, struct sof_source *source, int frames) +{ + struct stft_process_state *state = &cd->state; + struct stft_process_buffer *ibuf; + int32_t const *x, *x_start, *x_end; + int x_size; + int bytes = frames * cd->frame_bytes; + int frames_left = frames; + int ret; + int n1; + int n2; + int channels = cd->channels; + int n; + int i; + int j; + + /* Get pointer to source data in circular buffer */ + ret = source_get_data_s32(source, bytes, &x, &x_start, &x_size); + if (ret) + return ret; + + /* Set helper pointers to buffer end for wrap check. Then loop until all + * samples are processed. + */ + x_end = x_start + x_size; + + while (frames_left) { + /* Find out samples to process before first wrap or end of data. */ + ibuf = &state->ibuf[0]; + n1 = (x_end - x) / cd->channels; + n2 = stft_process_buffer_samples_without_wrap(ibuf, ibuf->w_ptr); + n = MIN(n1, n2); + n = MIN(n, frames_left); + for (i = 0; i < n; i++) { + for (j = 0; j < channels; j++) { + ibuf = &state->ibuf[j]; + *ibuf->w_ptr++ = *x++; + } + } + + /* One of the buffers needs a wrap (or end of data), so check for wrap */ + for (j = 0; j < channels; j++) { + ibuf = &state->ibuf[j]; + ibuf->w_ptr = stft_process_buffer_wrap(ibuf, ibuf->w_ptr); + } + + if (x >= x_end) + x -= x_size; + + /* Update processed samples count for next loop iteration. */ + frames_left -= n; + } + + /* Update the source for bytes consumed. Return success. */ + source_release_data(source, bytes); + for (j = 0; j < channels; j++) { + ibuf = &state->ibuf[j]; + ibuf->s_avail += frames; + ibuf->s_free -= frames; + } + + return 0; +} + +int stft_process_sink_s32(struct stft_comp_data *cd, struct sof_sink *sink, int frames) +{ + struct stft_process_state *state = &cd->state; + struct stft_process_buffer *obuf; + int32_t *y, *y_start, *y_end; + int frames_remain = frames; + int channels = cd->channels; + int bytes = frames * cd->frame_bytes; + int y_size; + int ret; + int ch, n1, n, i; + + /* Get pointer to sink data in circular buffer */ + ret = sink_get_buffer_s32(sink, bytes, &y, &y_start, &y_size); + if (ret) + return ret; + + /* Set helper pointers to buffer end for wrap check. Then loop until all + * samples are processed. + */ + y_end = y_start + y_size; + while (frames_remain) { + /* Find out samples to process before first wrap or end of data. */ + obuf = &state->obuf[0]; + n1 = (y_end - y) / cd->channels; + n = stft_process_buffer_samples_without_wrap(obuf, obuf->r_ptr); + n = MIN(n1, n); + n = MIN(n, frames_remain); + + for (i = 0; i < n; i++) { + for (ch = 0; ch < channels; ch++) { + obuf = &state->obuf[ch]; + *y++ = *obuf->r_ptr; + *obuf->r_ptr++ = 0; /* clear overlap add mix */ + } + } + + /* One of the buffers needs a wrap (or end of data), so check for wrap */ + for (ch = 0; ch < cd->channels; ch++) { + obuf = &state->obuf[ch]; + obuf->r_ptr = stft_process_buffer_wrap(obuf, obuf->r_ptr); + } + + if (y >= y_end) + y -= y_size; + + /* Update processed samples count for next loop iteration. */ + frames_remain -= n; + } + + /* Update the sink for bytes produced. Return success. */ + sink_commit_buffer(sink, bytes); + for (ch = 0; ch < channels; ch++) { + obuf = &state->obuf[ch]; + obuf->s_avail -= frames; + obuf->s_free += frames; + } + + return 0; +} +#endif /* CONFIG_FORMAT_S32LE */ + +#if CONFIG_FORMAT_S16LE +int stft_process_source_s16(struct stft_comp_data *cd, struct sof_source *source, int frames) +{ + struct stft_process_state *state = &cd->state; + struct stft_process_buffer *ibuf; + int16_t const *x, *x_start, *x_end; + int16_t in; + int x_size; + int channels = cd->channels; + int bytes = frames * cd->frame_bytes; + int frames_left = frames; + int ret; + int n1; + int n2; + int n; + int i; + int j; + + ret = source_get_data_s16(source, bytes, &x, &x_start, &x_size); + if (ret) + return ret; + + x_end = x_start + x_size; + + while (frames_left) { + ibuf = &state->ibuf[0]; + n1 = (x_end - x) / cd->channels; + n2 = stft_process_buffer_samples_without_wrap(ibuf, ibuf->w_ptr); + n = MIN(n1, n2); + n = MIN(n, frames_left); + for (i = 0; i < n; i++) { + for (j = 0; j < channels; j++) { + ibuf = &state->ibuf[j]; + in = *x++; + *ibuf->w_ptr++ = (int32_t)in << 16; + } + } + + for (j = 0; j < channels; j++) { + ibuf = &state->ibuf[j]; + ibuf->w_ptr = stft_process_buffer_wrap(ibuf, ibuf->w_ptr); + } + + if (x >= x_end) + x -= x_size; + + frames_left -= n; + } + + source_release_data(source, bytes); + for (j = 0; j < channels; j++) { + ibuf = &state->ibuf[j]; + ibuf->s_avail += frames; + ibuf->s_free -= frames; + } + return 0; +} + +int stft_process_sink_s16(struct stft_comp_data *cd, struct sof_sink *sink, int frames) +{ + struct stft_process_state *state = &cd->state; + struct stft_process_buffer *obuf; + int16_t *y, *y_start, *y_end; + int frames_remain = frames; + int channels = cd->channels; + int bytes = frames * cd->frame_bytes; + int y_size; + int ret; + int ch, n1, n, i; + + ret = sink_get_buffer_s16(sink, bytes, &y, &y_start, &y_size); + if (ret) + return ret; + + y_end = y_start + y_size; + while (frames_remain) { + obuf = &state->obuf[0]; + n1 = (y_end - y) / cd->channels; + n = stft_process_buffer_samples_without_wrap(obuf, obuf->r_ptr); + n = MIN(n1, n); + n = MIN(n, frames_remain); + + for (i = 0; i < n; i++) { + for (ch = 0; ch < channels; ch++) { + obuf = &state->obuf[ch]; + *y++ = sat_int16(Q_SHIFT_RND(*obuf->r_ptr, 31, 15)); + *obuf->r_ptr++ = 0; /* clear overlap add mix */ + } + } + + for (ch = 0; ch < channels; ch++) { + obuf = &state->obuf[ch]; + obuf->r_ptr = stft_process_buffer_wrap(obuf, obuf->r_ptr); + } + + if (y >= y_end) + y -= y_size; + + frames_remain -= n; + } + + sink_commit_buffer(sink, bytes); + for (ch = 0; ch < channels; ch++) { + obuf = &state->obuf[ch]; + obuf->s_avail -= frames; + obuf->s_free += frames; + } + + return 0; +} +#endif /* CONFIG_FORMAT_S16LE */ + +void stft_process_fill_prev_samples(struct stft_process_buffer *buf, int32_t *prev_data, + int prev_data_length) +{ + int32_t *r = buf->r_ptr; + int32_t *p = prev_data; + int copied; + int nmax; + int n; + + for (copied = 0; copied < prev_data_length; copied += n) { + nmax = prev_data_length - copied; + n = stft_process_buffer_samples_without_wrap(buf, r); + n = MIN(n, nmax); + memcpy(p, r, sizeof(int32_t) * n); + p += n; + r += n; + r = stft_process_buffer_wrap(buf, r); + } + + buf->s_avail -= copied; + buf->s_free += copied; + buf->r_ptr = r; +} + +void stft_process_fill_fft_buffer(struct stft_process_state *state, int ch) +{ + struct stft_process_buffer *ibuf = &state->ibuf[ch]; + struct stft_process_fft *fft = &state->fft; + int32_t *prev_data = state->prev_data[ch]; + int32_t *r = ibuf->r_ptr; + int copied; + int nmax; + int idx; + int j; + int n; + + /* Copy overlapped samples from state buffer. Imaginary part of input + * remains zero. + */ + for (j = 0; j < state->prev_data_size; j++) { + fft->fft_buf[j].real = prev_data[j]; + fft->fft_buf[j].imag = 0; + } + + /* Copy hop size of new data from circular buffer */ + idx = state->prev_data_size; + for (copied = 0; copied < fft->fft_hop_size; copied += n) { + nmax = fft->fft_hop_size - copied; + n = stft_process_buffer_samples_without_wrap(ibuf, r); + n = MIN(n, nmax); + for (j = 0; j < n; j++) { + fft->fft_buf[idx].real = *r++; + fft->fft_buf[idx].imag = 0; + idx++; + } + r = stft_process_buffer_wrap(ibuf, r); + } + + ibuf->s_avail -= copied; + ibuf->s_free += copied; + ibuf->r_ptr = r; + + /* Copy for next time data back to overlap buffer */ + idx = fft->fft_hop_size; + for (j = 0; j < state->prev_data_size; j++) + prev_data[j] = fft->fft_buf[idx + j].real; +} + +LOG_MODULE_REGISTER(stft_process_common, CONFIG_SOF_LOG_LEVEL); + +/* + * The main processing function for STFT_PROCESS + */ + +static int stft_prepare_fft(struct stft_process_state *state, int channel) +{ + struct stft_process_buffer *ibuf = &state->ibuf[channel]; + struct stft_process_fft *fft = &state->fft; + + /* Wait for FFT hop size of new data */ + if (ibuf->s_avail < fft->fft_hop_size) + return 0; + + return 1; +} + +static void stft_do_fft(struct stft_process_state *state, int ch) +{ + struct stft_process_fft *fft = &state->fft; + + /* Copy data to FFT input buffer from overlap buffer and from new samples buffer */ + stft_process_fill_fft_buffer(state, ch); + + /* Window function */ + stft_process_apply_window(state); + +#if STFT_DEBUG + debug_print_to_file_real(stft_debug_fft_in_fh, fft->fft_buf, fft->fft_size); +#endif + + /* Compute FFT. A full scale s16 sine input with 2^N samples period in low + * part of s32 real part and zero imaginary part gives to output about 0.5 + * full scale 32 bit output to real and imaginary. The scaling is same for + * all FFT sizes. + */ + fft_multi_execute_32(fft->fft_plan, false); + +#if STFT_DEBUG + debug_print_to_file_complex(stft_debug_fft_out_fh, fft->fft_out, fft->fft_size); +#endif +} + +static void stft_do_ifft(struct stft_process_state *state, int ch) +{ + struct stft_process_fft *fft = &state->fft; + + /* Compute IFFT */ + fft_multi_execute_32(fft->ifft_plan, true); + +#if STFT_DEBUG + debug_print_to_file_complex(stft_debug_ifft_out_fh, fft->fft_buf, fft->fft_size); +#endif + + /* Window function */ + stft_process_apply_window(state); + + /* Copy to output buffer */ + stft_process_overlap_add_ifft_buffer(state, ch); +} + +#if CONFIG_STFT_PROCESS_MAGNITUDE_PHASE +static void stft_convert_to_polar(struct stft_process_fft *fft) +{ + int i; + + for (i = 0; i < fft->half_fft_size; i++) + sofm_icomplex32_to_polar(&fft->fft_out[i], &fft->fft_polar[i]); +} + +static void stft_convert_to_complex(struct stft_process_fft *fft) +{ + int i; + + for (i = 0; i < fft->half_fft_size; i++) + sofm_ipolar32_to_complex(&fft->fft_polar[i], &fft->fft_out[i]); +} + +static void stft_apply_fft_symmetry(struct stft_process_fft *fft) +{ + int i, j, k; + + j = 2 * fft->half_fft_size - 2; + for (i = fft->half_fft_size; i < fft->fft_size; i++) { + k = j - i; + fft->fft_out[i].real = fft->fft_out[k].real; + fft->fft_out[i].imag = -fft->fft_out[k].imag; + } +} +#endif + +static void stft_do_fft_ifft(const struct processing_module *mod) +{ + struct stft_comp_data *cd = module_get_private_data(mod); + struct stft_process_state *state = &cd->state; + int num_fft; + int ch; + + for (ch = 0; ch < cd->channels; ch++) { + num_fft = stft_prepare_fft(state, ch); + + if (num_fft) { + stft_do_fft(state, ch); + +#if CONFIG_STFT_PROCESS_MAGNITUDE_PHASE + /* Convert half-FFT to polar and back, and fix upper part */ + stft_convert_to_polar(&state->fft); + stft_convert_to_complex(&state->fft); + stft_apply_fft_symmetry(&state->fft); +#endif + + stft_do_ifft(state, ch); + cd->fft_done = true; + } + } +} + +#if CONFIG_FORMAT_S32LE +static int stft_process_output_zeros_s32(struct stft_comp_data *cd, struct sof_sink *sink, + int frames) +{ + int32_t *y, *y_start, *y_end; + int samples = frames * cd->channels; + size_t bytes = samples * sizeof(int32_t); + int samples_without_wrap; + int y_size; + int ret; + + /* Get pointer to sink data in circular buffer, buffer start and size. */ + ret = sink_get_buffer_s32(sink, bytes, &y, &y_start, &y_size); + if (ret) + return ret; + + /* Set helper pointers to buffer end for wrap check. Then loop until all + * samples are processed. + */ + y_end = y_start + y_size; + while (samples) { + /* Find out samples to process before first wrap or end of data. */ + samples_without_wrap = y_end - y; + samples_without_wrap = MIN(samples_without_wrap, samples); + memset(y, 0, samples_without_wrap * sizeof(int32_t)); + y += samples_without_wrap; + + /* Check for wrap */ + if (y >= y_end) + y -= y_size; + + /* Update processed samples count for next loop iteration. */ + samples -= samples_without_wrap; + } + + /* Update the source and sink for bytes consumed and produced. Return success. */ + sink_commit_buffer(sink, bytes); + return 0; +} + +static int stft_process_s32(const struct processing_module *mod, struct sof_source *source, + struct sof_sink *sink, uint32_t frames) +{ + struct stft_comp_data *cd = module_get_private_data(mod); + + /* Get samples from source buffer */ + stft_process_source_s32(cd, source, frames); + + /* Do STFT, processing and inverse STFT */ + stft_do_fft_ifft(mod); + + /* Get samples from source buffer */ + if (cd->fft_done) + stft_process_sink_s32(cd, sink, frames); + else + stft_process_output_zeros_s32(cd, sink, frames); + + return 0; +} +#endif /* CONFIG_FORMAT_S32LE */ + +#if CONFIG_FORMAT_S16LE +static int stft_process_output_zeros_s16(struct stft_comp_data *cd, struct sof_sink *sink, + int frames) +{ + int16_t *y, *y_start, *y_end; + int samples = frames * cd->channels; + size_t bytes = samples * sizeof(int16_t); + int samples_without_wrap; + int y_size; + int ret; + + /* Get pointer to sink data in circular buffer, buffer start and size. */ + ret = sink_get_buffer_s16(sink, bytes, &y, &y_start, &y_size); + if (ret) + return ret; + + /* Set helper pointers to buffer end for wrap check. Then loop until all + * samples are processed. + */ + y_end = y_start + y_size; + while (samples) { + /* Find out samples to process before first wrap or end of data. */ + samples_without_wrap = y_end - y; + samples_without_wrap = MIN(samples_without_wrap, samples); + memset(y, 0, samples_without_wrap * sizeof(int16_t)); + y += samples_without_wrap; + + /* Check for wrap */ + if (y >= y_end) + y -= y_size; + + /* Update processed samples count for next loop iteration. */ + samples -= samples_without_wrap; + } + + /* Update the source and sink for bytes consumed and produced. Return success. */ + sink_commit_buffer(sink, bytes); + return 0; +} + +static int stft_process_s16(const struct processing_module *mod, struct sof_source *source, + struct sof_sink *sink, uint32_t frames) +{ + struct stft_comp_data *cd = module_get_private_data(mod); + + /* Get samples from source buffer */ + stft_process_source_s16(cd, source, frames); + + /* Do STFT, processing and inverse STFT */ + stft_do_fft_ifft(mod); + + /* Get samples from source buffer */ + if (cd->fft_done) + stft_process_sink_s16(cd, sink, frames); + else + stft_process_output_zeros_s16(cd, sink, frames); + + return 0; +} +#endif /* CONFIG_FORMAT_S16LE */ + +#if CONFIG_FORMAT_S24LE +#endif /* CONFIG_FORMAT_S24LE */ + +/* This struct array defines the used processing functions for + * the PCM formats + */ +const struct stft_process_proc_fnmap stft_process_functions[] = { +#if CONFIG_FORMAT_S16LE + { SOF_IPC_FRAME_S16_LE, stft_process_s16 }, + { SOF_IPC_FRAME_S32_LE, stft_process_s32 }, +#endif +}; + +/** + * stft_process_find_proc_func() - Find suitable processing function. + * @src_fmt: Enum value for PCM format. + * + * This function finds the suitable processing function to use for + * the used PCM format. If not found, return NULL. + * + * Return: Pointer to processing function for the requested PCM format. + */ +stft_process_func stft_process_find_proc_func(enum sof_ipc_frame src_fmt) +{ + int i; + + /* Find suitable processing function from map */ + for (i = 0; i < ARRAY_SIZE(stft_process_functions); i++) + if (src_fmt == stft_process_functions[i].frame_fmt) + return stft_process_functions[i].stft_process_function; + + return NULL; +} diff --git a/src/audio/stft_process/stft_process_setup.c b/src/audio/stft_process/stft_process_setup.c new file mode 100644 index 000000000000..377d5feeabc8 --- /dev/null +++ b/src/audio/stft_process/stft_process_setup.c @@ -0,0 +1,247 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// Copyright(c) 2025 Intel Corporation. + +#include <sof/audio/component.h> +#include <sof/audio/audio_stream.h> +#include <sof/math/auditory.h> +#include <sof/math/trig.h> +#include <sof/math/window.h> +#include <sof/trace/trace.h> +#include "stft_process.h" + +#include <errno.h> +#include <stddef.h> +#include <stdint.h> + +/* Definitions for cepstral lifter */ +#define PI_Q23 Q_CONVERT_FLOAT(3.1415926536, 23) +#define TWO_PI_Q23 Q_CONVERT_FLOAT(6.2831853072, 23) +#define ONE_Q9 Q_CONVERT_FLOAT(1, 9) + +#define STFT_MAX_ALLOC_SIZE 65536 + +LOG_MODULE_REGISTER(stft_process_setup, CONFIG_SOF_LOG_LEVEL); + +static void stft_process_init_buffer(struct stft_process_buffer *buf, int32_t *base, int size) +{ + buf->addr = base; + buf->end_addr = base + size; + buf->r_ptr = base; + buf->w_ptr = base; + buf->s_free = size; + buf->s_avail = 0; + buf->s_length = size; +} + +static int stft_process_get_window(struct stft_process_state *state, + enum sof_stft_process_fft_window_type name) +{ + struct stft_process_fft *fft = &state->fft; + + switch (name) { + case STFT_RECTANGULAR_WINDOW: + win_rectangular_32b(state->window, fft->fft_size); + return 0; + case STFT_BLACKMAN_WINDOW: + win_blackman_32b(state->window, fft->fft_size, WIN_BLACKMAN_A0_Q31); + return 0; + case STFT_HAMMING_WINDOW: + win_hamming_32b(state->window, fft->fft_size); + return 0; + case STFT_HANN_WINDOW: + win_hann_32b(state->window, fft->fft_size); + return 0; + + default: + return -EINVAL; + } +} + +/* TODO stft_process setup needs to use the config blob, not hard coded parameters. + * Also this is a too long function. Split to STFT, Mel filter, etc. parts. + */ +int stft_process_setup(struct processing_module *mod, int max_frames, + int sample_rate, int channels) +{ + struct stft_comp_data *cd = module_get_private_data(mod); + struct comp_dev *dev = mod->dev; + struct sof_stft_process_config *config = cd->config; + struct stft_process_state *state = &cd->state; + struct stft_process_fft *fft = &state->fft; + size_t sample_buffers_size; + size_t ibuf_size; + size_t obuf_size; + size_t prev_size; + int32_t *addr; + int ret; + int i; + + comp_dbg(dev, "entry"); + + /* Check size */ + if (config->size != sizeof(struct sof_stft_process_config)) { + comp_err(dev, "Illegal configuration size %d.", config->size); + return -EINVAL; + } + + if (config->sample_frequency != sample_rate) { + comp_err(dev, "Config sample_frequency does not match stream"); + return -EINVAL; + } + + /* max_frames needs to be even for buffer size allocation for Xtensa HiFi SIMD. */ + max_frames = ALIGN_UP(max_frames, 2); + cd->max_frames = max_frames; + state->sample_rate = sample_rate; + + comp_info(dev, "source_channel = %d, stream_channels = %d", + config->channel, channels); + if (config->channel >= channels) { + comp_err(dev, "Illegal channel"); + return -EINVAL; + } + + if (config->channel < 0) + state->source_channel = 0; + else + state->source_channel = config->channel; + + fft->fft_size = config->frame_length; + fft->fft_padded_size = fft->fft_size; /* Same */ + fft->fft_hop_size = config->frame_shift; + fft->half_fft_size = (fft->fft_padded_size >> 1) + 1; + + /* FFT size needs to be a multiple of 4 for Xtensa HiFi SIMD, + * and FFT hop size needs to be a multiple of 2. Check also + * for otherwise sane values. + */ + if (fft->fft_size <= 0 || fft->fft_hop_size <= 0 || + fft->fft_hop_size > fft->fft_size || + (fft->fft_size & 3) || (fft->fft_hop_size & 1)) { + comp_err(dev, "FFT size %d or hop size %d are invalid.", + fft->fft_size, fft->fft_hop_size); + return -EINVAL; + } + + comp_info(dev, "fft_size = %d, fft_hop_size = %d, window = %d", + fft->fft_size, fft->fft_hop_size, config->window); + + /* Calculated parameters */ + state->prev_data_size = fft->fft_size - fft->fft_hop_size; + ibuf_size = fft->fft_hop_size + max_frames; + obuf_size = fft->fft_size + max_frames; + prev_size = state->prev_data_size; + + /* Allocate buffer input samples, overlap buffer, window */ + sample_buffers_size = sizeof(int32_t) * + (cd->channels * (ibuf_size + obuf_size + prev_size) + fft->fft_size); + + if (sample_buffers_size > STFT_MAX_ALLOC_SIZE) { + comp_err(dev, "Illegal allocation size"); + return -ENOMEM; + } + + state->buffers = mod_balloc_align(mod, sample_buffers_size, 2 * sizeof(int32_t)); + if (!state->buffers) { + comp_err(dev, "Failed buffer allocate"); + ret = -ENOMEM; + goto exit; + } + + bzero(state->buffers, sample_buffers_size); + addr = state->buffers; + for (i = 0; i < cd->channels; i++) { + stft_process_init_buffer(&state->ibuf[i], addr, ibuf_size); + addr += ibuf_size; + stft_process_init_buffer(&state->obuf[i], addr, obuf_size); + addr += obuf_size; + state->prev_data[i] = addr; + addr += prev_size; + } + state->window = addr; + + /* Allocate buffers for FFT input and output data */ + fft->fft_buffer_size = fft->fft_padded_size * sizeof(struct icomplex32); + fft->fft_buf = mod_balloc_align(mod, fft->fft_buffer_size, sizeof(struct icomplex32)); + if (!fft->fft_buf) { + comp_err(dev, "Failed FFT buffer allocate"); + ret = -ENOMEM; + goto free_buffers; + } + + fft->fft_out = mod_balloc_align(mod, fft->fft_buffer_size, sizeof(struct icomplex32)); + if (!fft->fft_out) { + comp_err(dev, "Failed FFT output allocate"); + ret = -ENOMEM; + goto free_fft_buf; + } + + /* Share the fft_out buffer for polar format */ + fft->fft_polar = (struct ipolar32 *)fft->fft_out; + + /* Setup FFT */ + fft->fft_plan = mod_fft_multi_plan_new(mod, fft->fft_buf, fft->fft_out, + fft->fft_padded_size, 32); + if (!fft->fft_plan) { + comp_err(dev, "Failed FFT init"); + ret = -EINVAL; + goto free_fft_out; + } + + fft->ifft_plan = mod_fft_multi_plan_new(mod, fft->fft_out, fft->fft_buf, + fft->fft_padded_size, 32); + if (!fft->ifft_plan) { + comp_err(dev, "Failed IFFT init"); + ret = -EINVAL; + goto free_ifft_out; + } + + /* Setup window */ + ret = stft_process_get_window(state, config->window); + if (ret < 0) { + comp_err(dev, "Failed Window function"); + goto free_window_out; + } + + /* Need to compensate the window function gain */ + state->gain_comp = config->window_gain_comp; + + /* Set initial state for STFT */ + state->waiting_fill = true; + state->prev_samples_valid = false; + + comp_dbg(dev, "done"); + return 0; + +free_window_out: + mod_free(mod, fft->ifft_plan); + +free_ifft_out: + mod_free(mod, fft->fft_plan); + +free_fft_out: + mod_free(mod, fft->fft_out); + +free_fft_buf: + mod_free(mod, fft->fft_buf); + +free_buffers: + mod_free(mod, state->buffers); + +exit: + return ret; +} + +void stft_process_free_buffers(struct processing_module *mod) +{ + struct stft_comp_data *cd = module_get_private_data(mod); + struct stft_process_state *state = &cd->state; + struct stft_process_fft *fft = &state->fft; + + mod_fft_multi_plan_free(mod, fft->ifft_plan); + mod_fft_multi_plan_free(mod, fft->fft_plan); + mod_free(mod, cd->state.fft.fft_buf); + mod_free(mod, cd->state.fft.fft_out); + mod_free(mod, cd->state.buffers); +} diff --git a/src/audio/stft_process/tune/setup_stft_process.m b/src/audio/stft_process/tune/setup_stft_process.m new file mode 100644 index 000000000000..4018a1d7aeb7 --- /dev/null +++ b/src/audio/stft_process/tune/setup_stft_process.m @@ -0,0 +1,185 @@ +% setup_stft_process() +% +% Create binary configuration blob for STFT_PROCESS component. The hex data +% is written to tools/topology/topology1/m4/stft_process/stft_process_config.m4 + +% SPDX-License-Identifier: BSD-3-Clause +% +% Copyright (c) 2025, Intel Corporation. + +function setup_stft_process(cfg) + + cfg.tools_path = '../../../../tools/'; + cfg.tplg_path = [cfg.tools_path 'topology/topology2/include/components/stft_process/']; + cfg.common_path = [cfg.tools_path 'tune/common']; + cfg.tplg_ver = 2; + cfg.ipc_ver = 4; + cfg.channel = 0; + cfg.sample_frequency = 48000; + + cfg.frame_length = 192; % 4 ms + cfg.frame_shift = 48; % 1 ms + cfg.window_type = 'hann'; + cfg.tplg_fn = 'hann_192_48.conf'; + export_stft_process_setup(cfg); + + cfg.frame_length = 512; % 10.7 ms + cfg.frame_shift = 128; % 2.7 ms + cfg.window_type = 'hann'; + cfg.tplg_fn = 'hann_512_128.conf'; + export_stft_process_setup(cfg); + + cfg.frame_length = 768; % 16 ms + cfg.frame_shift = 120; % 2.5 ms + cfg.window_type = 'hann'; + cfg.tplg_fn = 'hann_768_120.conf'; + export_stft_process_setup(cfg); + + cfg.frame_length = 1024; % 21.3 ms + cfg.frame_shift = 256; % 5.3 ms + cfg.window_type = 'hann'; + cfg.tplg_fn = 'hann_1024_256.conf'; + export_stft_process_setup(cfg); + + cfg.frame_length = 1536; % 32 ms + cfg.frame_shift = 240; % 5 ms + cfg.window_type = 'hann'; + cfg.tplg_fn = 'hann_1536_240.conf'; + export_stft_process_setup(cfg); + +end + +function export_stft_process_setup(cfg) + + % Use blob tool from EQ + addpath(cfg.common_path); + + % Blob size, size plus reserved(8) + current parameters + nbytes_data = 64; + + % Get ABI information + [abi_bytes, nbytes_abi] = sof_get_abi(nbytes_data, cfg.ipc_ver); + + % Initialize correct size uint8 array + nbytes = nbytes_abi + nbytes_data; + b8 = uint8(zeros(1,nbytes)); + + % Insert ABI header + fprintf(1, 'STFT_PROCESS blob size is %d, ABI header is %d, data is %d\n',nbytes, nbytes_abi, nbytes_data); + b8(1:nbytes_abi) = abi_bytes; + j = nbytes_abi + 1; + + % Apply default STFT_PROCESS configuration, first struct header and reserved, then data + [b8, j] = add_w32b(nbytes_data, b8, j); + for i = 1:8 + [b8, j] = add_w32b(0, b8, j); + end + + fft_length = cfg.frame_length; + fft_hop = cfg.frame_shift; + [window_idx, window_gain_comp] = get_window(cfg, fft_length, fft_hop); + + v = q_convert(cfg.sample_frequency, 0); [b8, j] = add_w32b(v, b8, j); + v = q_convert(window_gain_comp, 31); [b8, j] = add_w32b(v, b8, j); + v = 0; [b8, j] = add_w32b(v, b8, j); % reserved + v = cfg.channel; [b8, j] = add_w16b(v, b8, j); + v = fft_length; [b8, j] = add_w16b(v, b8, j); + v = fft_hop; [b8, j] = add_w16b(v, b8, j); + v = 0; [b8, j] = add_w16b(v, b8, j); % reserved + v = 0; [b8, j] = add_w32b(v, b8, j); % enum pad + v = window_idx; [b8, j] = add_w32b(v, b8, j); % enum window + + % Export + switch cfg.tplg_ver + case 2 + sof_tplg2_write([cfg.tplg_path cfg.tplg_fn], b8, "stft_process_config", ... + "Exported STFT_PROCESS configuration", ... + "cd tools/tune/stft_process; octave setup_stft_process.m"); + otherwise + error("Illegal cfg.tplg_ver, use 2 topology v2."); + end + + rmpath(cfg.common_path); + +end + +%% Helper functions + +function [idx, iwg] = get_window(cfg, len, hop) + switch lower(cfg.window_type) + case 'rectangular' + win = boxcar(len); + idx = 0; + case 'blackman' + win = blackman(len); + idx = 1; + case 'hamming' + win = hamming(len); + idx = 2; + case 'hann' + win = hann(len); + idx = 3; + case 'povey' + idx = 4; + n = 0:(len-1); + win = ((1 - cos(2 * pi * n / len)) / 2).^0.85; + otherwise + error('Unknown window type'); + end + iwg = hop / sum(win.^2); +end + +function bytes = w8b(word) + bytes = uint8(zeros(1,1)); + bytes(1) = bitand(word, 255); +end + +function bytes = w16b(word) + sh = [0 -8]; + bytes = uint8(zeros(1,2)); + bytes(1) = bitand(bitshift(word, sh(1)), 255); + bytes(2) = bitand(bitshift(word, sh(2)), 255); +end + +function bytes = w32b(word) + sh = [0 -8 -16 -24]; + bytes = uint8(zeros(1,4)); + bytes(1) = bitand(bitshift(word, sh(1)), 255); + bytes(2) = bitand(bitshift(word, sh(2)), 255); + bytes(3) = bitand(bitshift(word, sh(3)), 255); + bytes(4) = bitand(bitshift(word, sh(4)), 255); +end + +function n = q_convert(val, q) + n = round(val * 2^q); +end + +function [blob8, j] = add_w8b(v, blob8, j) + if j > length(blob8) + error('Blob size is not sufficient'); + end + blob8(j) = w8b(v); + j = j + 1; +end + +function [blob8, j] = add_w16b(v, blob8, j) + if j + 1 > length(blob8) + error('Blob size is not sufficient'); + end + if v < 0 + v = 2^16 + v; + end + blob8(j : j + 1) = w16b(v); + j = j + 2; +end + +function [blob8, j] = add_w32b(v, blob8, j) + if j + 3 > length(blob8) + error('Blob size is not sufficient'); + end + if v < 0 + v = 2^32 + v; + end + blob8(j : j + 3) = w32b(v); + j = j + 4; +end diff --git a/src/audio/tdfb/README.md b/src/audio/tdfb/README.md new file mode 100644 index 000000000000..cf3bc13dbe2d --- /dev/null +++ b/src/audio/tdfb/README.md @@ -0,0 +1,15 @@ +# Time Domain Filter Bank Architecture + +This directory contains the TDFB component. + +## Overview + +Generally used for operations like beamforming or highly directional microphone array processing in the time domain. + +## Configuration and Scripts + +- **Kconfig**: Manages the Time Domain Fixed Beamformer component (`COMP_TDFB`), requiring generic `COMP_MODULE_ADAPTER` integration along with high-precision math packages (`MATH_FIR`, `MATH_IIR_DF1`, `SQRT_FIXED`, `CORDIC_FIXED`). +- **CMakeLists.txt**: Builds the standard source set (`tdfb.c`, `tdfb_generic.c`, `tdfb_hifiep.c`, `tdfb_hifi3.c`, `tdfb_direction.c`) handling generic and HIFI implementations alongside IPC abstractions (`tdfb_ipc3.c`, `tdfb_ipc4.c`). Inherits out-of-tree loader (`llext`) generation. +- **tdfb.toml**: Generates ALSA properties mapping the component to `UUIDREG_STR_TDFB`. +- **Topology (.conf)**: Instantiated from `tools/topology/topology2/include/components/tdfb.conf`, creating a `tdfb` widget type `effect` (UUID `49:17:51:dd:fa:d9:5c:45:b3:a7:13:58:56:93:f1:af`). Provides multiple internal mixer controls spanning phase/direction toggles. +- **MATLAB Tuning (`tune/`)**: `sof_example_circular_array.m` and similar array topology scripts define spatial setups (e.g., circular, linear, L-shape, rectangular). They take microphone layout coordinates, compute beamforming angles (azimuth/elevation) and delays, and pack them into binary configuration blobs allowing microphones to "steer" their listening direction interactively. diff --git a/src/audio/tdfb/tdfb.c b/src/audio/tdfb/tdfb.c index 9fc04820ff75..5df4563c3558 100644 --- a/src/audio/tdfb/tdfb.c +++ b/src/audio/tdfb/tdfb.c @@ -25,7 +25,6 @@ #include <ipc/control.h> #include <ipc/stream.h> #include <ipc/topology.h> -#include <rtos/alloc.h> #include <rtos/init.h> #include <rtos/panic.h> #include <rtos/string.h> @@ -44,8 +43,6 @@ LOG_MODULE_REGISTER(tdfb, CONFIG_SOF_LOG_LEVEL); SOF_DEFINE_REG_UUID(tdfb); -DECLARE_TR_CTX(tdfb_tr, SOF_UUID(tdfb_uuid), LOG_LEVEL_INFO); - static inline int set_func(struct processing_module *mod, enum sof_ipc_frame fmt) { struct tdfb_comp_data *cd = module_get_private_data(mod); @@ -53,24 +50,24 @@ static inline int set_func(struct processing_module *mod, enum sof_ipc_frame fmt switch (fmt) { #if CONFIG_FORMAT_S16LE case SOF_IPC_FRAME_S16_LE: - comp_dbg(mod->dev, "set_func(), SOF_IPC_FRAME_S16_LE"); + comp_dbg(mod->dev, "SOF_IPC_FRAME_S16_LE"); cd->tdfb_func = tdfb_fir_s16; break; #endif /* CONFIG_FORMAT_S16LE */ #if CONFIG_FORMAT_S24LE case SOF_IPC_FRAME_S24_4LE: - comp_dbg(mod->dev, "set_func(), SOF_IPC_FRAME_S24_4LE"); + comp_dbg(mod->dev, "SOF_IPC_FRAME_S24_4LE"); cd->tdfb_func = tdfb_fir_s24; break; #endif /* CONFIG_FORMAT_S24LE */ #if CONFIG_FORMAT_S32LE case SOF_IPC_FRAME_S32_LE: - comp_dbg(mod->dev, "set_func(), SOF_IPC_FRAME_S32_LE"); + comp_dbg(mod->dev, "SOF_IPC_FRAME_S32_LE"); cd->tdfb_func = tdfb_fir_s32; break; #endif /* CONFIG_FORMAT_S32LE */ default: - comp_err(mod->dev, "set_func(), invalid frame_fmt"); + comp_err(mod->dev, "invalid frame_fmt"); return -EINVAL; } return 0; @@ -234,24 +231,24 @@ static inline int set_pass_func(struct processing_module *mod, enum sof_ipc_fram switch (fmt) { #if CONFIG_FORMAT_S16LE case SOF_IPC_FRAME_S16_LE: - comp_dbg(mod->dev, "set_pass_func(), SOF_IPC_FRAME_S16_LE"); + comp_dbg(mod->dev, "SOF_IPC_FRAME_S16_LE"); cd->tdfb_func = tdfb_pass_s16; break; #endif /* CONFIG_FORMAT_S16LE */ #if CONFIG_FORMAT_S24LE case SOF_IPC_FRAME_S24_4LE: - comp_dbg(mod->dev, "set_pass_func(), SOF_IPC_FRAME_S24_4LE"); + comp_dbg(mod->dev, "SOF_IPC_FRAME_S24_4LE"); cd->tdfb_func = tdfb_pass_s24; break; #endif /* CONFIG_FORMAT_S24LE */ #if CONFIG_FORMAT_S32LE case SOF_IPC_FRAME_S32_LE: - comp_dbg(mod->dev, "set_pass_func(), SOF_IPC_FRAME_S32_LE"); + comp_dbg(mod->dev, "SOF_IPC_FRAME_S32_LE"); cd->tdfb_func = tdfb_pass_s32; break; #endif /* CONFIG_FORMAT_S32LE */ default: - comp_err(mod->dev, "set_pass_func(), invalid frame_fmt"); + comp_err(mod->dev, "invalid frame_fmt"); return -EINVAL; } return 0; @@ -261,15 +258,16 @@ static inline int set_pass_func(struct processing_module *mod, enum sof_ipc_fram * Control code functions next. The processing is in fir_ C modules. */ -static void tdfb_free_delaylines(struct tdfb_comp_data *cd) +static void tdfb_free_delaylines(struct processing_module *mod) { + struct tdfb_comp_data *cd = module_get_private_data(mod); struct fir_state_32x16 *fir = cd->fir; int i = 0; /* Free the common buffer for all EQs and point then * each FIR channel delay line to NULL. */ - rfree(cd->fir_delay); + mod_free(mod, cd->fir_delay); cd->fir_delay = NULL; cd->fir_delay_size = 0; for (i = 0; i < PLATFORM_MAX_CHANNELS; i++) @@ -327,37 +325,37 @@ static int tdfb_init_coef(struct processing_module *mod, int source_nch, /* Sanity checks */ if (config->num_output_channels > PLATFORM_MAX_CHANNELS || !config->num_output_channels) { - comp_err(dev, "tdfb_init_coef(), invalid num_output_channels %d", + comp_err(dev, "invalid num_output_channels %d", config->num_output_channels); return -EINVAL; } if (config->num_output_channels != sink_nch) { - comp_err(dev, "tdfb_init_coef(), stream output channels count %d does not match configuration %d", + comp_err(dev, "stream output channels count %d does not match configuration %d", sink_nch, config->num_output_channels); return -EINVAL; } if (config->num_filters > SOF_TDFB_FIR_MAX_COUNT) { - comp_err(dev, "tdfb_init_coef(), invalid num_filters %d", + comp_err(dev, "invalid num_filters %d", config->num_filters); return -EINVAL; } if (config->num_angles > SOF_TDFB_MAX_ANGLES) { - comp_err(dev, "tdfb_init_coef(), invalid num_angles %d", + comp_err(dev, "invalid num_angles %d", config->num_angles); return -EINVAL; } if (config->beam_off_defined > 1) { - comp_err(dev, "tdfb_init_coef(), invalid beam_off_defined %d", + comp_err(dev, "invalid beam_off_defined %d", config->beam_off_defined); return -EINVAL; } if (config->num_mic_locations > SOF_TDFB_MAX_MICROPHONES) { - comp_err(dev, "tdfb_init_coef(), invalid num_mic_locations %d", + comp_err(dev, "invalid num_mic_locations %d", config->num_mic_locations); return -EINVAL; } @@ -367,7 +365,7 @@ static int tdfb_init_coef(struct processing_module *mod, int source_nch, * A most basic blob has num_angles equals 1. Mic locations data is optional. */ if (config->num_angles == 0 && config->num_mic_locations == 0) { - comp_err(dev, "tdfb_init_coef(), ABI version less than 3.19.1 is not supported."); + comp_err(dev, "ABI version less than 3.19.1 is not supported."); return -EINVAL; } @@ -395,7 +393,7 @@ static int tdfb_init_coef(struct processing_module *mod, int source_nch, (&cd->filter_angles[config->num_angles]); if ((uint8_t *)&cd->mic_locations[config->num_mic_locations] != (uint8_t *)config + config->size) { - comp_err(dev, "tdfb_init_coef(), invalid config size"); + comp_err(dev, "invalid config size"); return -EINVAL; } @@ -414,14 +412,14 @@ static int tdfb_init_coef(struct processing_module *mod, int source_nch, idx = cd->filter_angles[min_delta_idx].filter_index; if (cd->beam_on) { - comp_info(dev, "tdfb_init_coef(), angle request %d, found %d, idx %d", + comp_info(dev, "angle request %d, found %d, idx %d", target_az, cd->filter_angles[min_delta_idx].azimuth, idx); } else if (config->beam_off_defined) { cd->output_channel_mix = output_channel_mix_beam_off; idx = config->num_filters * config->num_angles; - comp_info(dev, "tdfb_init_coef(), configure beam off"); + comp_info(dev, "configure beam off"); } else { - comp_info(dev, "tdfb_init_coef(), beam off is not defined, using filter %d, idx %d", + comp_info(dev, "beam off is not defined, using filter %d, idx %d", cd->filter_angles[min_delta_idx].azimuth, idx); } @@ -436,7 +434,7 @@ static int tdfb_init_coef(struct processing_module *mod, int source_nch, if (s > 0) { size_sum += s; } else { - comp_err(dev, "tdfb_init_coef(), FIR length %d is invalid", + comp_err(dev, "FIR length %d is invalid", coef_data->length); return -EINVAL; } @@ -459,7 +457,7 @@ static int tdfb_init_coef(struct processing_module *mod, int source_nch, * used for filters input. */ if (max_ch + 1 > source_nch) { - comp_err(dev, "tdfb_init_coef(), stream input channels count %d is not sufficient for configuration %d", + comp_err(dev, "stream input channels count %d is not sufficient for configuration %d", source_nch, max_ch + 1); return -EINVAL; } @@ -511,12 +509,12 @@ static int tdfb_setup(struct processing_module *mod, int source_nch, int sink_nc if (delay_size > cd->fir_delay_size) { /* Free existing FIR channels data if it was allocated */ - tdfb_free_delaylines(cd); + tdfb_free_delaylines(mod); /* Allocate all FIR channels data in a big chunk and clear it */ - cd->fir_delay = rballoc(SOF_MEM_FLAG_USER, delay_size); + cd->fir_delay = mod_balloc(mod, delay_size); if (!cd->fir_delay) { - comp_err(mod->dev, "tdfb_setup(), delay allocation failed for size %d", + comp_err(mod->dev, "delay allocation failed for size %d", delay_size); return -ENOMEM; } @@ -539,22 +537,13 @@ static int tdfb_init(struct processing_module *mod) { struct module_data *md = &mod->priv; struct comp_dev *dev = mod->dev; - struct module_config *cfg = &md->cfg; struct tdfb_comp_data *cd; - size_t bs = cfg->size; int ret; int i; - comp_info(dev, "tdfb_init()"); - - /* Check first that configuration blob size is sane */ - if (bs > SOF_TDFB_MAX_SIZE) { - comp_err(dev, "tdfb_init() error: configuration blob size = %zu > %d", - bs, SOF_TDFB_MAX_SIZE); - return -EINVAL; - } + comp_info(dev, "entry"); - cd = rzalloc(SOF_MEM_FLAG_USER, sizeof(*cd)); + cd = mod_zalloc(mod, sizeof(*cd)); if (!cd) return -ENOMEM; @@ -574,20 +563,13 @@ static int tdfb_init(struct processing_module *mod) goto err_free_cd; /* Handler for configuration data */ - cd->model_handler = comp_data_blob_handler_new(dev); + cd->model_handler = mod_data_blob_handler_new(mod); if (!cd->model_handler) { - comp_err(dev, "comp_data_blob_handler_new() failed."); + comp_err(dev, "mod_data_blob_handler_new() failed."); ret = -ENOMEM; goto err; } - /* Get configuration data and reset FIR filters */ - ret = comp_init_data_blob(cd->model_handler, bs, cfg->data); - if (ret < 0) { - comp_err(dev, "comp_init_data_blob() failed."); - goto err; - } - for (i = 0; i < PLATFORM_MAX_CHANNELS; i++) fir_reset(&cd->fir[i]); @@ -599,11 +581,12 @@ static int tdfb_init(struct processing_module *mod) err: /* These are null if not used for IPC version */ - rfree(cd->ctrl_data); + mod_free(mod, cd->ctrl_data); ipc_msg_free(cd->msg); + mod_data_blob_handler_free(mod, cd->model_handler); err_free_cd: - rfree(cd); + mod_free(mod, cd); return ret; } @@ -612,14 +595,14 @@ static int tdfb_free(struct processing_module *mod) { struct tdfb_comp_data *cd = module_get_private_data(mod); - comp_dbg(mod->dev, "tdfb_free()"); + comp_dbg(mod->dev, "entry"); ipc_msg_free(cd->msg); - tdfb_free_delaylines(cd); - comp_data_blob_handler_free(cd->model_handler); - tdfb_direction_free(cd); - rfree(cd->ctrl_data); - rfree(cd); + tdfb_free_delaylines(mod); + mod_data_blob_handler_free(mod, cd->model_handler); + tdfb_direction_free(mod); + mod_free(mod, cd->ctrl_data); + mod_free(mod, cd); return 0; } @@ -654,7 +637,7 @@ static int tdfb_process(struct processing_module *mod, int frame_count = input_buffers[0].size; int ret; - comp_dbg(dev, "tdfb_process()"); + comp_dbg(dev, "entry"); /* Check for changed configuration */ if (comp_is_new_data_blob_available(cd->model_handler)) { @@ -663,7 +646,7 @@ static int tdfb_process(struct processing_module *mod, audio_stream_get_channels(sink), audio_stream_get_frm_fmt(source)); if (ret < 0) { - comp_err(dev, "tdfb_process(), failed FIR setup"); + comp_err(dev, "failed FIR setup"); return ret; } } @@ -675,7 +658,7 @@ static int tdfb_process(struct processing_module *mod, audio_stream_get_channels(sink), audio_stream_get_frm_fmt(source)); if (ret < 0) { - comp_err(dev, "tdfb_process(), failed FIR setup"); + comp_err(dev, "failed FIR setup"); return ret; } } @@ -706,14 +689,12 @@ static int tdfb_process(struct processing_module *mod, return 0; } -static void tdfb_set_alignment(struct audio_stream *source, - struct audio_stream *sink) +static void tdfb_set_alignment(struct audio_stream *source) { - const uint32_t byte_align = 1; + const uint32_t byte_align = SOF_FRAME_BYTE_ALIGN; const uint32_t frame_align_req = 2; /* Process multiples of 2 frames */ audio_stream_set_align(byte_align, frame_align_req, source); - audio_stream_set_align(byte_align, frame_align_req, sink); } static int tdfb_prepare(struct processing_module *mod, @@ -724,12 +705,13 @@ static int tdfb_prepare(struct processing_module *mod, struct comp_buffer *sourceb, *sinkb; struct comp_dev *dev = mod->dev; enum sof_ipc_frame frame_fmt; + size_t data_size; int source_channels; int sink_channels; int rate; int ret; - comp_info(dev, "tdfb_prepare()"); + comp_info(dev, "entry"); /* Find source and sink buffers */ sourceb = comp_dev_get_first_data_producer(dev); @@ -745,7 +727,7 @@ static int tdfb_prepare(struct processing_module *mod, return ret; } - tdfb_set_alignment(&sourceb->stream, &sinkb->stream); + tdfb_set_alignment(&sourceb->stream); frame_fmt = audio_stream_get_frm_fmt(&sourceb->stream); source_channels = audio_stream_get_channels(&sourceb->stream); @@ -753,15 +735,16 @@ static int tdfb_prepare(struct processing_module *mod, rate = audio_stream_get_rate(&sourceb->stream); /* Initialize filter */ - cd->config = comp_get_data_blob(cd->model_handler, NULL, NULL); - if (!cd->config) { + cd->config = comp_get_data_blob(cd->model_handler, &data_size, NULL); + if (!cd->config || !data_size) { + comp_err(dev, "Missing a configuration blob."); ret = -EINVAL; goto out; } ret = tdfb_setup(mod, source_channels, sink_channels, frame_fmt); if (ret < 0) { - comp_err(dev, "tdfb_prepare() error: tdfb_setup failed."); + comp_err(dev, "error: tdfb_setup failed."); goto out; } @@ -780,7 +763,7 @@ static int tdfb_prepare(struct processing_module *mod, comp_dbg(dev, "dev_frames = %d, max_frames = %d", dev->frames, cd->max_frames); /* Initialize tracking */ - ret = tdfb_direction_init(cd, rate, source_channels); + ret = tdfb_direction_init(mod, rate, source_channels); if (!ret) { comp_info(dev, "max_lag = %d, xcorr_size = %zu", cd->direction.max_lag, cd->direction.d_size); @@ -801,9 +784,9 @@ static int tdfb_reset(struct processing_module *mod) struct tdfb_comp_data *cd = module_get_private_data(mod); int i; - comp_dbg(mod->dev, "tdfb_reset()"); + comp_dbg(mod->dev, "entry"); - tdfb_free_delaylines(cd); + tdfb_free_delaylines(mod); cd->tdfb_func = NULL; for (i = 0; i < PLATFORM_MAX_CHANNELS; i++) @@ -840,6 +823,7 @@ SOF_LLEXT_BUILDINFO; #else +DECLARE_TR_CTX(tdfb_tr, SOF_UUID(tdfb_uuid), LOG_LEVEL_INFO); DECLARE_MODULE_ADAPTER(tdfb_interface, tdfb_uuid, tdfb_tr); SOF_MODULE_INIT(tdfb, sys_comp_module_tdfb_interface_init); diff --git a/src/audio/tdfb/tdfb_comp.h b/src/audio/tdfb/tdfb_comp.h index e2d5075e06b5..ef33f28fa612 100644 --- a/src/audio/tdfb/tdfb_comp.h +++ b/src/audio/tdfb/tdfb_comp.h @@ -118,10 +118,10 @@ void tdfb_fir_s32(struct tdfb_comp_data *cd, struct output_stream_buffer *bsink, int frames); #endif -int tdfb_direction_init(struct tdfb_comp_data *cd, int32_t fs, int channels); +int tdfb_direction_init(struct processing_module *mod, int32_t fs, int channels); void tdfb_direction_copy_emphasis(struct tdfb_comp_data *cd, int channels, int *channel, int32_t x); void tdfb_direction_estimate(struct tdfb_comp_data *cd, int frames, int channels); -void tdfb_direction_free(struct tdfb_comp_data *cd); +void tdfb_direction_free(struct processing_module *mod); static inline void tdfb_cinc_s16(int16_t **ptr, int16_t *end, size_t size) { diff --git a/src/audio/tdfb/tdfb_direction.c b/src/audio/tdfb/tdfb_direction.c index 640fe4eaa06e..71b544003ee7 100644 --- a/src/audio/tdfb/tdfb_direction.c +++ b/src/audio/tdfb/tdfb_direction.c @@ -8,7 +8,6 @@ #include "tdfb_comp.h" #include <ipc/topology.h> -#include <rtos/alloc.h> #include <sof/math/iir_df1.h> #include <sof/math/trig.h> #include <sof/math/sqrt.h> @@ -109,7 +108,7 @@ static inline int16_t tdfb_mic_distance_sqrt(int32_t x) xs = Q_SHIFT_RND(x, 24, 12); xs = MIN(xs, UINT16_MAX); - return sqrt_int16((uint16_t)xs); + return sofm_sqrt_int16((uint16_t)xs); } static int16_t max_mic_distance(struct tdfb_comp_data *cd) @@ -176,8 +175,9 @@ static bool line_array_mode_check(struct tdfb_comp_data *cd) return true; } -int tdfb_direction_init(struct tdfb_comp_data *cd, int32_t fs, int ch_count) +int tdfb_direction_init(struct processing_module *mod, int32_t fs, int ch_count) { + struct tdfb_comp_data *cd = module_get_private_data(mod); struct sof_eq_iir_header *filt; int32_t *delay; int32_t d_max; @@ -200,7 +200,7 @@ int tdfb_direction_init(struct tdfb_comp_data *cd, int32_t fs, int ch_count) /* Allocate delay lines for IIR filters and initialize them */ size = ch_count * iir_delay_size_df1(filt); - delay = rzalloc(SOF_MEM_FLAG_USER, size); + delay = mod_zalloc(mod, size); if (!delay) return -ENOMEM; @@ -225,7 +225,7 @@ int tdfb_direction_init(struct tdfb_comp_data *cd, int32_t fs, int ch_count) cd->direction.max_lag = Q_MULTSR_32X32((int64_t)fs, t_max, 0, 15, 0) + 1; n = (cd->max_frames + (2 * cd->direction.max_lag + 1)) * ch_count; cd->direction.d_size = n * sizeof(int16_t); - cd->direction.d = rzalloc(SOF_MEM_FLAG_USER, cd->direction.d_size); + cd->direction.d = mod_zalloc(mod, cd->direction.d_size); if (!cd->direction.d) goto err_free_iir; @@ -238,7 +238,7 @@ int tdfb_direction_init(struct tdfb_comp_data *cd, int32_t fs, int ch_count) /* xcorr result is temporary but too large for stack so it is allocated here */ cd->direction.r_size = (2 * cd->direction.max_lag + 1) * sizeof(int32_t); - cd->direction.r = rzalloc(SOF_MEM_FLAG_USER, cd->direction.r_size); + cd->direction.r = mod_zalloc(mod, cd->direction.r_size); if (!cd->direction.r) goto err_free_all; @@ -251,20 +251,22 @@ int tdfb_direction_init(struct tdfb_comp_data *cd, int32_t fs, int ch_count) return 0; err_free_all: - rfree(cd->direction.d); + mod_free(mod, cd->direction.d); cd->direction.d = NULL; err_free_iir: - rfree(cd->direction.df1_delay); + mod_free(mod, cd->direction.df1_delay); cd->direction.df1_delay = NULL; return -ENOMEM; } -void tdfb_direction_free(struct tdfb_comp_data *cd) +void tdfb_direction_free(struct processing_module *mod) { - rfree(cd->direction.df1_delay); - rfree(cd->direction.d); - rfree(cd->direction.r); + struct tdfb_comp_data *cd = module_get_private_data(mod); + + mod_free(mod, cd->direction.df1_delay); + mod_free(mod, cd->direction.d); + mod_free(mod, cd->direction.r); } /* Measure level of one channel */ diff --git a/src/audio/tdfb/tdfb_ipc3.c b/src/audio/tdfb/tdfb_ipc3.c index 52662a141e56..544acbe9a41f 100644 --- a/src/audio/tdfb/tdfb_ipc3.c +++ b/src/audio/tdfb/tdfb_ipc3.c @@ -30,7 +30,7 @@ static int init_get_ctl_ipc(struct processing_module *mod) struct tdfb_comp_data *cd = module_get_private_data(mod); int comp_id = dev_comp_id(mod->dev); - cd->ctrl_data = rzalloc(SOF_MEM_FLAG_USER, TDFB_GET_CTRL_DATA_SIZE); + cd->ctrl_data = mod_zalloc(mod, TDFB_GET_CTRL_DATA_SIZE); if (!cd->ctrl_data) return -ENOMEM; @@ -114,16 +114,16 @@ static int tdfb_cmd_get_value(struct processing_module *mod, struct sof_ipc_ctrl switch (cdata->cmd) { case SOF_CTRL_CMD_ENUM: - comp_dbg(mod->dev, "tdfb_cmd_get_value(), SOF_CTRL_CMD_ENUM index=%d", + comp_dbg(mod->dev, "SOF_CTRL_CMD_ENUM index=%d", cdata->index); return tdfb_cmd_enum_get(cdata, cd); case SOF_CTRL_CMD_SWITCH: - comp_dbg(mod->dev, "tdfb_cmd_get_value(), SOF_CTRL_CMD_SWITCH index=%d", + comp_dbg(mod->dev, "SOF_CTRL_CMD_SWITCH index=%d", cdata->index); return tdfb_cmd_switch_get(cdata, cd); } - comp_err(mod->dev, "tdfb_cmd_get_value() error: invalid cdata->cmd"); + comp_err(mod->dev, "error: invalid cdata->cmd"); return -EINVAL; } @@ -137,7 +137,7 @@ int tdfb_get_ipc_config(struct processing_module *mod, if (cdata->cmd != SOF_CTRL_CMD_BINARY) return tdfb_cmd_get_value(mod, cdata); - comp_dbg(mod->dev, "tdfb_get_ipc_config(), binary"); + comp_dbg(mod->dev, "binary"); return comp_data_blob_get_cmd(cd->model_handler, cdata, fragment_size); } @@ -190,16 +190,16 @@ static int tdfb_cmd_set_value(struct processing_module *mod, struct sof_ipc_ctrl switch (cdata->cmd) { case SOF_CTRL_CMD_ENUM: - comp_dbg(mod->dev, "tdfb_cmd_set_value(), SOF_CTRL_CMD_ENUM index=%d", + comp_dbg(mod->dev, "SOF_CTRL_CMD_ENUM index=%d", cdata->index); return tdfb_cmd_enum_set(cdata, cd); case SOF_CTRL_CMD_SWITCH: - comp_dbg(mod->dev, "tdfb_cmd_set_value(), SOF_CTRL_CMD_SWITCH index=%d", + comp_dbg(mod->dev, "SOF_CTRL_CMD_SWITCH index=%d", cdata->index); return tdfb_cmd_switch_set(cdata, cd); } - comp_err(mod->dev, "tdfb_cmd_set_value() error: invalid cdata->cmd"); + comp_err(mod->dev, "error: invalid cdata->cmd"); return -EINVAL; } @@ -215,7 +215,7 @@ int tdfb_set_ipc_config(struct processing_module *mod, uint32_t param_id, if (cdata->cmd != SOF_CTRL_CMD_BINARY) { ret = tdfb_cmd_set_value(mod, cdata); } else { - comp_info(mod->dev, "tdfb_set_ipc_config(), binary"); + comp_info(mod->dev, "binary"); ret = comp_data_blob_set(cd->model_handler, pos, data_offset_size, fragment, fragment_size); } diff --git a/src/audio/tdfb/tdfb_ipc4.c b/src/audio/tdfb/tdfb_ipc4.c index dd8ddec714c2..2f7e7e865214 100644 --- a/src/audio/tdfb/tdfb_ipc4.c +++ b/src/audio/tdfb/tdfb_ipc4.c @@ -109,7 +109,7 @@ int tdfb_get_ipc_config(struct processing_module *mod, uint32_t param_id, uint32_t *data_offset_size, uint8_t *fragment, size_t fragment_size) { - comp_err(mod->dev, "tdfb_get_ipc_config, Not supported, should not happen"); + comp_err(mod->dev, "Not supported, should not happen"); return -EINVAL; } @@ -175,7 +175,7 @@ int tdfb_set_ipc_config(struct processing_module *mod, uint32_t param_id, ctl->id, ctl->num_elems); return tdfb_cmd_enum_set(ctl, cd); default: - comp_info(mod->dev, "tdfb_set_ipc_config(), binary"); + comp_info(mod->dev, "binary"); return comp_data_blob_set(cd->model_handler, pos, data_offset_size, fragment, fragment_size); } diff --git a/src/audio/template/README.md b/src/audio/template/README.md new file mode 100644 index 000000000000..93e48c80f2cc --- /dev/null +++ b/src/audio/template/README.md @@ -0,0 +1,14 @@ +# Template Component Architecture + +This directory contains template code. + +## Overview + +Provides a reference or stub implementation that developers can copy and modify to easily construct new custom audio components. + +## Configuration and Scripts + +- **Kconfig**: Exposes the `COMP_TEMPLATE` toggle to build the baseline framework plugin (internally swaps or reverses channels). +- **CMakeLists.txt**: Compiles generic operations (`template.c`, `template-generic.c`) and connects via either IPC major version. Generates a Zephyr `llext` by default. +- **template.toml**: Includes default setup arguments and UUID bindings (`UUIDREG_STR_TEMPLATE`). +- **Topology (.conf)**: Defined in `tools/topology/topology2/include/components/template_comp.conf`, registering the `template_comp` widget of type `effect` (UUID `af:e1:2d:a6:64:59:2e:4e:b1:67:7f:dc:97:27:9a:29`). diff --git a/src/audio/template/template-ipc3.c b/src/audio/template/template-ipc3.c index 30398b841bcf..ad35389341f6 100644 --- a/src/audio/template/template-ipc3.c +++ b/src/audio/template/template-ipc3.c @@ -25,7 +25,7 @@ __cold int template_set_config(struct processing_module *mod, uint32_t param_id, assert_can_be_cold(); - comp_dbg(dev, "template_set_config()"); + comp_dbg(dev, "entry"); switch (cdata->cmd) { case SOF_CTRL_CMD_SWITCH: @@ -65,7 +65,7 @@ __cold int template_get_config(struct processing_module *mod, assert_can_be_cold(); - comp_info(dev, "template_get_config()"); + comp_info(dev, "entry"); switch (cdata->cmd) { case SOF_CTRL_CMD_SWITCH: diff --git a/src/audio/template/template.c b/src/audio/template/template.c index 20911df27bb2..1bef6668b75e 100644 --- a/src/audio/template/template.c +++ b/src/audio/template/template.c @@ -17,11 +17,6 @@ SOF_DEFINE_REG_UUID(template); /* Creates logging data for the component */ LOG_MODULE_REGISTER(template, CONFIG_SOF_LOG_LEVEL); -/* Creates the compont trace. Traces show in trace console the component - * info, warning, and error messages. - */ -DECLARE_TR_CTX(template_tr, SOF_UUID(template_uuid), LOG_LEVEL_INFO); - /** * template_init() - Initialize the template component. * @mod: Pointer to module data. @@ -38,9 +33,9 @@ __cold static int template_init(struct processing_module *mod) struct comp_dev *dev = mod->dev; struct template_comp_data *cd; - comp_info(dev, "template_init()"); + comp_info(dev, "entry"); - cd = rzalloc(SOF_MEM_FLAG_USER, sizeof(*cd)); + cd = mod_zalloc(mod, sizeof(*cd)); if (!cd) return -ENOMEM; @@ -74,7 +69,7 @@ static int template_process(struct processing_module *mod, int frames = source_get_data_frames_available(source); int sink_frames = sink_get_free_frames(sink); - comp_dbg(dev, "template_process()"); + comp_dbg(dev, "entry"); frames = MIN(frames, sink_frames); if (cd->enable) @@ -110,7 +105,7 @@ static int template_prepare(struct processing_module *mod, enum sof_ipc_frame source_format; int i; - comp_dbg(dev, "template_prepare()"); + comp_dbg(dev, "entry"); /* The processing example in this component supports one input and one * output. Generally there can be more. @@ -150,7 +145,7 @@ static int template_reset(struct processing_module *mod) { struct template_comp_data *cd = module_get_private_data(mod); - comp_dbg(mod->dev, "template_reset()"); + comp_dbg(mod->dev, "entry"); memset(cd, 0, sizeof(*cd)); return 0; } @@ -172,8 +167,8 @@ __cold static int template_free(struct processing_module *mod) assert_can_be_cold(); - comp_dbg(mod->dev, "template_free()"); - rfree(cd); + comp_dbg(mod->dev, "entry"); + mod_free(mod, cd); return 0; } @@ -205,6 +200,8 @@ SOF_LLEXT_BUILDINFO; #else +/* Only used for the module adapter trace context, soon to be deprecated */ +DECLARE_TR_CTX(template_tr, SOF_UUID(template_uuid), LOG_LEVEL_INFO); DECLARE_MODULE_ADAPTER(template_interface, template_uuid, template_tr); SOF_MODULE_INIT(template, sys_comp_module_template_interface_init); diff --git a/src/audio/tensorflow/README.md b/src/audio/tensorflow/README.md index 07c9089afb4c..a8e3f59ba3ce 100644 --- a/src/audio/tensorflow/README.md +++ b/src/audio/tensorflow/README.md @@ -1,34 +1,22 @@ -# SOF Tensorflow Micro Integration +# TensorFlow Lite Micro (TFLM) Architecture -The TFLM module integrates tensorflow micro into SOF as an audio classifiction and in the future an audio processing module. This module is currently still work in progress, and needs a few features to be completed before its ready for production. +This directory acts as the bridge for running ML models. -## Building +## Overview -Please run ```./scripts/tensorflow-clone.sh``` before building as this will clone all dependencies required to build tensor flow micro with optimized xtensa kernels. +Integrates TensorFlow Lite for Microcontrollers into the SOF audio pipeline. Evaluates pre-trained neural network topologies inline with the audio stream for tasks like wake-word, noise cancellation, or sound classification. -Please select the following in Kconfig -``` -CONFIG_CPP=y -CONFIG_STD_CPP17=y -CONFIG_SOF_STAGING=y -``` - -To build as built-in, select ```CONFIG_COMP_TENSORFLOW=y``` in Kconfig and build. - -To build as a llext module, select ```CONFIG_COMP_TENSORFLOW=m``` in Kconfig and the build. - -## Running +## Architecture Diagram -The TFLM SOF module needs features created by the MFCC module as its input. This step is still in progress. The following pipeline will be used - -``` Mic --> MFCC --> TFLM --> Classification ``` - -## TODO - -1) Create MFCC pipeline and align with TFLM micro_speech audio feature extraction configuration. - -2) Support compressed output PCM with Classification results. +```mermaid +graph LR + Feat[Audio Features] --> TFLM[TFLM Inference Engine] + SubGraph[FlatBuffer Model] -.-> TFLM + TFLM --> Out[Inference Labels/Scores] +``` -3) Load tflite models via binary kcontrol. +## Configuration and Scripts -4) Support audio processing via TFLM module. \ No newline at end of file +- **Kconfig**: Enforces requirements for C++17 support and core framework staging logic (`COMP_TENSORFLOW`). +- **CMakeLists.txt**: An intricate build specification linking the Tensilica neural network library block computations (`nn_hifi_lib`) and the TensorFlow Lite micro core engine (`tflm_lib`). Also hooks the `tflm-classify.c` SOF adapter via compiler flags explicitly enforcing memory, precision, and XTENSA optimizations. +- **tflmcly.toml**: Topology definition for the specific TFLM Classifier implementation binding the engine against the UUID `UUIDREG_STR_TFLMCLY`. diff --git a/src/audio/tensorflow/tflm-classify.c b/src/audio/tensorflow/tflm-classify.c index 29b4b6a1af74..827f6711bc41 100644 --- a/src/audio/tensorflow/tflm-classify.c +++ b/src/audio/tensorflow/tflm-classify.c @@ -18,7 +18,6 @@ #include <ipc/stream.h> #include <ipc/topology.h> #include <module/module/llext.h> -#include <rtos/alloc.h> #include <rtos/init.h> #include <rtos/panic.h> #include <rtos/string.h> @@ -36,8 +35,6 @@ SOF_DEFINE_REG_UUID(tflmcly); LOG_MODULE_REGISTER(tflmcly, CONFIG_SOF_LOG_LEVEL); -DECLARE_TR_CTX(tflm_tr, SOF_UUID(tflmcly_uuid), LOG_LEVEL_INFO); -EXPORT_SYMBOL(tflm_tr); EXPORT_SYMBOL(tflmcly_uuid); EXPORT_SYMBOL(log_const_tflmcly); @@ -59,27 +56,27 @@ __cold static int tflm_init(struct processing_module *mod) assert_can_be_cold(); - comp_info(dev, "tflm_init()"); + comp_info(dev, "entry"); - cd = rzalloc(SOF_MEM_FLAG_USER, sizeof(*cd)); + cd = mod_zalloc(mod, sizeof(*cd)); if (!cd) return -ENOMEM; md->private = cd; /* Handler for configuration data */ - cd->model_handler = comp_data_blob_handler_new(dev); + cd->model_handler = mod_data_blob_handler_new(mod); if (!cd->model_handler) { - comp_err(dev, "comp_data_blob_handler_new() failed."); + comp_err(dev, "mod_data_blob_handler_new() failed."); ret = -ENOMEM; - goto cd_fail; + goto fail; } /* Get configuration data and reset DRC state */ ret = comp_init_data_blob(cd->model_handler, bs, cfg->data); if (ret < 0) { comp_err(dev, "comp_init_data_blob() failed."); - goto cd_fail; + goto fail; } /* hard coded atm */ @@ -89,20 +86,22 @@ __cold static int tflm_init(struct processing_module *mod) ret = TF_SetModel(&cd->tfc, NULL); if (!ret) { comp_err(dev, "failed to set model"); - return ret; + goto fail; } /* initialise ops */ ret = TF_InitOps(&cd->tfc); if (!ret) { comp_err(dev, "failed to init ops"); - return ret; + goto fail; } return ret; -cd_fail: - rfree(cd); +fail: + /* Passing NULL pointer to free functions is Ok */ + mod_data_blob_handler_free(mod, cd->model_handler); + mod_free(mod, cd); return ret; } @@ -112,8 +111,8 @@ __cold static int tflm_free(struct processing_module *mod) assert_can_be_cold(); - comp_data_blob_handler_free(cd->model_handler); - rfree(cd); + mod_data_blob_handler_free(mod, cd->model_handler); + mod_free(mod, cd); return 0; } @@ -128,11 +127,11 @@ __cold static int tflm_set_config(struct processing_module *mod, uint32_t param_ assert_can_be_cold(); - comp_dbg(dev, "tflm_set_config()"); + comp_dbg(dev, "entry"); struct sof_ipc4_control_msg_payload *ctl = (struct sof_ipc4_control_msg_payload *)fragment; - comp_info(dev, "tflm_set_config(), bytes control"); + comp_info(dev, "bytes control"); ret = comp_data_blob_set(cd->model_handler, pos, data_offset_size, fragment, fragment_size); @@ -187,7 +186,7 @@ static int tflm_process(struct processing_module *mod, int features = input_buffers[0].size; int ret; - comp_dbg(dev, "tflm_process()"); + comp_dbg(dev, "entry"); /* Window size is TFLM_FEATURE_ELEM_COUNT and we increment * by TFLM_FEATURE_SIZE until buffer empty. @@ -234,6 +233,7 @@ static const struct module_interface tflmcly_interface = { .free = tflm_free }; +DECLARE_TR_CTX(tflm_tr, SOF_UUID(tflmcly_uuid), LOG_LEVEL_INFO); DECLARE_MODULE_ADAPTER(tflmcly_interface, tflmcly_uuid, tflm_tr); SOF_MODULE_INIT(tflmcly, sys_comp_module_tflmcly_interface_init); diff --git a/src/audio/tone.c b/src/audio/tone.c deleted file mode 100644 index 39c2ad820581..000000000000 --- a/src/audio/tone.c +++ /dev/null @@ -1,743 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -// -// Copyright(c) 2016 Intel Corporation. All rights reserved. -// -// Author: Seppo Ingalsuo <seppo.ingalsuo@linux.intel.com> -// Liam Girdwood <liam.r.girdwood@linux.intel.com> -// Keyon Jie <yang.jie@linux.intel.com> - -#include <sof/audio/buffer.h> -#include <sof/audio/component.h> -#include <sof/audio/format.h> -#include <sof/audio/pipeline.h> -#include <sof/audio/ipc-config.h> -#include <sof/common.h> -#include <rtos/panic.h> -#include <sof/ipc/msg.h> -#include <rtos/alloc.h> -#include <rtos/init.h> -#include <sof/lib/memory.h> /* for SHARED_DATA */ -#include <sof/lib/uuid.h> -#include <sof/list.h> -#include <sof/math/trig.h> -#include <sof/platform.h> -#include <rtos/string.h> -#include <sof/trace/trace.h> -#include <sof/ut.h> -#include <ipc/control.h> -#include <ipc/stream.h> -#include <ipc/topology.h> -#include <user/tone.h> -#include <user/trace.h> -#include <errno.h> -#include <stdbool.h> -#include <stddef.h> -#include <stdint.h> - -/* Convert float frequency in Hz to Q16.16 fractional format */ -#define TONE_FREQ(f) Q_CONVERT_FLOAT(f, 16) - -/* Convert float gain to Q1.31 fractional format */ -#define TONE_GAIN(v) Q_CONVERT_FLOAT(v, 31) - -/* Set default tone amplitude and frequency */ -#define TONE_AMPLITUDE_DEFAULT TONE_GAIN(0.1) /* -20 dB */ -#define TONE_FREQUENCY_DEFAULT TONE_FREQ(997.0) -#define TONE_NUM_FS 13 /* Table size for 8-192 kHz range */ - -static const struct comp_driver comp_tone; - -LOG_MODULE_REGISTER(tone, CONFIG_SOF_LOG_LEVEL); - -SOF_DEFINE_REG_UUID(tone); - -DECLARE_TR_CTX(tone_tr, SOF_UUID(tone_uuid), LOG_LEVEL_INFO); - -/* 2*pi/Fs lookup tables in Q1.31 for each Fs */ -static const int32_t tone_fs_list[TONE_NUM_FS] = { - 8000, 11025, 16000, 22050, 24000, 32000, 44100, 48000, - 64000, 88200, 96000, 176400, 192000 -}; - -static const int32_t tone_pi2_div_fs[TONE_NUM_FS] = { - 1686630, 1223858, 843315, 611929, 562210, 421657, 305965, - 281105, 210829, 152982, 140552, 76491, 70276 -}; - -/* tone component private data */ - -struct tone_state { - int mute; - int32_t a; /* Current amplitude Q1.31 */ - int32_t a_target; /* Target amplitude Q1.31 */ - int32_t ampl_coef; /* Amplitude multiplier Q2.30 */ - int32_t c; /* Coefficient 2*pi/Fs Q1.31 */ - int32_t f; /* Frequency Q16.16 */ - int32_t freq_coef; /* Frequency multiplier Q2.30 */ - int32_t fs; /* Sample rate in Hertz Q32.0 */ - int32_t ramp_step; /* Amplitude ramp step Q1.31 */ - int32_t w; /* Angle radians Q4.28 */ - int32_t w_step; /* Angle step Q4.28 */ - uint32_t block_count; - uint32_t repeat_count; - uint32_t repeats; /* Number of repeats for tone (sweep steps) */ - uint32_t sample_count; - uint32_t samples_in_block; /* Samples in 125 us block */ - uint32_t tone_length; /* Active length in 125 us blocks */ - uint32_t tone_period; /* Active + idle time in 125 us blocks */ -}; - -struct comp_data { - uint32_t period_bytes; - uint32_t channels; - uint32_t frame_bytes; - uint32_t rate; - struct tone_state sg[PLATFORM_MAX_CHANNELS]; - void (*tone_func)(struct comp_dev *dev, struct audio_stream *sink, - uint32_t frames); -}; - -static int32_t tonegen(struct tone_state *sg); -static void tonegen_control(struct tone_state *sg); -static void tonegen_update_f(struct tone_state *sg, int32_t f); - -/* - * Tone generator algorithm code - */ - -static inline void tone_circ_inc_wrap(int32_t **ptr, int32_t *end, size_t size) -{ - if (*ptr >= end) - *ptr = (int32_t *)((size_t)*ptr - size); -} - -static void tone_s32_default(struct comp_dev *dev, struct audio_stream *sink, - uint32_t frames) -{ - struct comp_data *cd = comp_get_drvdata(dev); - int32_t *dest = audio_stream_get_wptr(sink); - int i; - int n; - int n_wrap_dest; - int n_min; - int nch = cd->channels; - - n = frames * nch; - while (n > 0) { - n_wrap_dest = (int32_t *)audio_stream_get_end_addr(sink) - dest; - n_min = (n < n_wrap_dest) ? n : n_wrap_dest; - /* Process until wrap or completed n */ - while (n_min > 0) { - n -= nch; - n_min -= nch; - for (i = 0; i < nch; i++) { - tonegen_control(&cd->sg[i]); - *dest = tonegen(&cd->sg[i]); - dest++; - } - } - tone_circ_inc_wrap(&dest, audio_stream_get_end_addr(sink), - audio_stream_get_size(sink)); - } -} - -static int32_t tonegen(struct tone_state *sg) -{ - int64_t sine; - int64_t w; - /* sg->w is angle in Q4.28 radians format, sin() returns Q1.31 */ - /* sg->a is amplitude as Q1.31 */ - sine = - q_mults_32x32(sin_fixed_32b(sg->w), sg->a, - Q_SHIFT_BITS_64(31, 31, 31)); - - /* Next point */ - w = (int64_t)sg->w + sg->w_step; - sg->w = (w > PI_MUL2_Q4_28) - ? (int32_t)(w - PI_MUL2_Q4_28) : (int32_t)w; - - if (sg->mute) - return 0; - else - return (int32_t)sine; /* Q1.31 no saturation need */ -} - -static void tonegen_control(struct tone_state *sg) -{ - int64_t a; - int64_t p; - - /* Count samples, 125 us blocks */ - sg->sample_count++; - if (sg->sample_count < sg->samples_in_block) - return; - - sg->sample_count = 0; - if (sg->block_count < INT32_MAX) - sg->block_count++; - - /* Fade-in ramp during tone */ - if (sg->block_count < sg->tone_length) { - if (sg->a == 0) - sg->w = 0; /* Reset phase to have less clicky ramp */ - - if (sg->a > sg->a_target) { - a = (int64_t)sg->a - sg->ramp_step; - if (a < sg->a_target) - a = sg->a_target; - - } else { - a = (int64_t)sg->a + sg->ramp_step; - if (a > sg->a_target) - a = sg->a_target; - } - sg->a = (int32_t)a; - } - - /* Fade-out ramp after tone*/ - if (sg->block_count > sg->tone_length) { - a = (int64_t)sg->a - sg->ramp_step; - if (a < 0) - a = 0; - - sg->a = (int32_t)a; - } - - /* New repeated tone, update for frequency or amplitude sweep */ - if ((sg->block_count > sg->tone_period) && - (sg->repeat_count + 1 < sg->repeats)) { - sg->block_count = 0; - if (sg->ampl_coef > 0) { - sg->a_target = - sat_int32(q_multsr_32x32(sg->a_target, - sg->ampl_coef, Q_SHIFT_BITS_64(31, 30, 31))); - sg->a = (sg->ramp_step > sg->a_target) - ? sg->a_target : sg->ramp_step; - } - if (sg->freq_coef > 0) { - /* f is Q16.16, freq_coef is Q2.30 */ - p = q_multsr_32x32(sg->f, sg->freq_coef, - Q_SHIFT_BITS_64(16, 30, 16)); - tonegen_update_f(sg, (int32_t)p); /* No saturation */ - } - sg->repeat_count++; - } -} - -/* Set sine amplitude */ -static inline void tonegen_set_a(struct tone_state *sg, int32_t a) -{ - sg->a_target = a; -} - -/* Repeated number of beeps */ -static void tonegen_set_repeats(struct tone_state *sg, uint32_t r) -{ - sg->repeats = r; -} - -/* The next functions support zero as shortcut for defaults to get - * make a nicer API without need to remember the neutral steady - * non-swept tone settings. - */ - -/* Multiplication factor for frequency as Q2.30 for logarithmic change */ -static void tonegen_set_freq_mult(struct tone_state *sg, int32_t fm) -{ - sg->freq_coef = (fm > 0) ? fm : ONE_Q2_30; /* Set freq mult to 1.0 */ -} - -/* Multiplication factor for amplitude as Q2.30 for logarithmic change */ -static void tonegen_set_ampl_mult(struct tone_state *sg, int32_t am) -{ - sg->ampl_coef = (am > 0) ? am : ONE_Q2_30; /* Set ampl mult to 1.0 */ -} - -/* Tone length in samples, this is the active length of tone */ -static void tonegen_set_length(struct tone_state *sg, uint32_t tl) -{ - sg->tone_length = (tl > 0) ? tl : INT32_MAX; /* Count rate 125 us */ -} - -/* Tone period in samples, this is the length including the pause after beep */ -static void tonegen_set_period(struct tone_state *sg, uint32_t tp) -{ - sg->tone_period = (tp > 0) ? tp : INT32_MAX; /* Count rate 125 us */ -} - -/* Tone ramp parameters: - * step - Value that is added or subtracted to amplitude. A zero or negative - * number disables the ramp and amplitude is immediately modified to - * final value. - */ - -static inline void tonegen_set_linramp(struct tone_state *sg, int32_t step) -{ - sg->ramp_step = (step > 0) ? step : INT32_MAX; -} - -static inline int32_t tonegen_get_f(struct tone_state *sg) -{ - return sg->f; -} - -static inline int32_t tonegen_get_a(struct tone_state *sg) -{ - return sg->a_target; -} - -static inline void tonegen_mute(struct tone_state *sg) -{ - sg->mute = 1; -} - -static inline void tonegen_unmute(struct tone_state *sg) -{ - sg->mute = 0; -} - -static void tonegen_update_f(struct tone_state *sg, int32_t f) -{ - int64_t w_tmp; - int64_t f_max; - - /* Calculate Fs/2, fs is Q32.0, f is Q16.16 */ - f_max = Q_SHIFT_LEFT((int64_t)sg->fs, 0, 16 - 1); - f_max = (f_max > INT32_MAX) ? INT32_MAX : f_max; - sg->f = (f > f_max) ? f_max : f; - /* Q16 x Q31 -> Q28 */ - w_tmp = q_multsr_32x32(sg->f, sg->c, Q_SHIFT_BITS_64(16, 31, 28)); - w_tmp = (w_tmp > PI_Q4_28) ? PI_Q4_28 : w_tmp; /* Limit to pi Q4.28 */ - sg->w_step = (int32_t)w_tmp; -} - -static void tonegen_reset(struct tone_state *sg) -{ - sg->mute = 1; - sg->a = 0; - sg->a_target = TONE_AMPLITUDE_DEFAULT; - sg->c = 0; - sg->f = TONE_FREQUENCY_DEFAULT; - sg->w = 0; - sg->w_step = 0; - - sg->block_count = 0; - sg->repeat_count = 0; - sg->repeats = 0; - sg->sample_count = 0; - sg->samples_in_block = 0; - - /* Continuous tone */ - sg->freq_coef = ONE_Q2_30; /* Set freq multiplier to 1.0 */ - sg->ampl_coef = ONE_Q2_30; /* Set ampl multiplier to 1.0 */ - sg->tone_length = INT32_MAX; - sg->tone_period = INT32_MAX; - sg->ramp_step = ONE_Q1_31; /* Set lin ramp modification to max */ -} - -static int tonegen_init(struct tone_state *sg, int32_t fs, int32_t f, int32_t a) -{ - int idx; - int i; - - sg->a_target = a; - sg->a = (sg->ramp_step > sg->a_target) ? sg->a_target : sg->ramp_step; - - idx = -1; - sg->mute = 1; - sg->fs = 0; - - /* Find index of current sample rate and then get from lookup table the - * corresponding 2*pi/Fs value. - */ - for (i = 0; i < TONE_NUM_FS; i++) { - if (fs == tone_fs_list[i]) - idx = i; - } - - if (idx < 0) { - sg->w_step = 0; - return -EINVAL; - } - - sg->fs = fs; - sg->c = tone_pi2_div_fs[idx]; /* Store 2*pi/Fs */ - sg->mute = 0; - tonegen_update_f(sg, f); - - /* 125us as Q1.31 is 268435, calculate fs * 125e-6 in Q31.0 */ - sg->samples_in_block = - (int32_t) q_multsr_32x32(fs, 268435, Q_SHIFT_BITS_64(0, 31, 0)); - - return 0; -} - -/* - * End of algorithm code. Next the standard component methods. - */ - -static struct comp_dev *tone_new(const struct comp_driver *drv, - const struct comp_ipc_config *config, - const void *spec) -{ - struct comp_dev *dev; - const struct ipc_config_tone *ipc_tone = spec; - struct comp_data *cd; - int i; - - comp_cl_info(&comp_tone, "tone_new()"); - - dev = comp_alloc(drv, sizeof(*dev)); - if (!dev) - return NULL; - dev->ipc_config = *config; - - cd = rzalloc(SOF_MEM_FLAG_USER, sizeof(*cd)); - if (!cd) { - rfree(dev); - return NULL; - } - - comp_set_drvdata(dev, cd); - cd->tone_func = tone_s32_default; - - cd->rate = ipc_tone->sample_rate; - - /* Reset tone generator and set channels volumes to default */ - for (i = 0; i < PLATFORM_MAX_CHANNELS; i++) - tonegen_reset(&cd->sg[i]); - - dev->state = COMP_STATE_READY; - return dev; -} - -static void tone_free(struct comp_dev *dev) -{ - struct tone_data *td = comp_get_drvdata(dev); - - comp_info(dev, "tone_free()"); - - rfree(td); - rfree(dev); -} - -/* set component audio stream parameters */ -static int tone_params(struct comp_dev *dev, - struct sof_ipc_stream_params *params) -{ - struct comp_data *cd = comp_get_drvdata(dev); - struct comp_buffer *sourceb, *sinkb; - - sourceb = comp_dev_get_first_data_producer(dev); - sinkb = comp_dev_get_first_data_consumer(dev); - if (!sourceb || !sinkb) { - comp_err(dev, "no source or sink buffer"); - return -ENOTCONN; - } - - comp_info(dev, "tone_params(), config->frame_fmt = %u", - dev->ipc_config.frame_fmt); - - /* Tone supports only S32_LE PCM format atm */ - if (dev->ipc_config.frame_fmt != SOF_IPC_FRAME_S32_LE) - return -EINVAL; - - audio_stream_set_frm_fmt(&sourceb->stream, dev->ipc_config.frame_fmt); - audio_stream_set_frm_fmt(&sinkb->stream, dev->ipc_config.frame_fmt); - - /* calculate period size based on config */ - cd->period_bytes = dev->frames * - audio_stream_frame_bytes(&sourceb->stream); - - return 0; -} - -#if CONFIG_IPC_MAJOR_3 -static int tone_cmd_get_value(struct comp_dev *dev, - struct sof_ipc_ctrl_data *cdata, int max_size) -{ - struct comp_data *cd = comp_get_drvdata(dev); - int j; - - comp_info(dev, "tone_cmd_get_value()"); - - if (cdata->type != SOF_CTRL_TYPE_VALUE_CHAN_GET) { - comp_err(dev, "wrong cdata->type: %u", - cdata->type); - return -EINVAL; - } - - if (cdata->cmd == SOF_CTRL_CMD_SWITCH) { - for (j = 0; j < cdata->num_elems; j++) { - cdata->chanv[j].channel = j; - cdata->chanv[j].value = !cd->sg[j].mute; - comp_info(dev, "tone_cmd_get_value(), j = %u, cd->sg[j].mute = %u", - j, cd->sg[j].mute); - } - } - return 0; -} - -static int tone_cmd_set_value(struct comp_dev *dev, - struct sof_ipc_ctrl_data *cdata) -{ - struct comp_data *cd = comp_get_drvdata(dev); - int j; - uint32_t ch; - bool val; - - if (cdata->type != SOF_CTRL_TYPE_VALUE_CHAN_SET) { - comp_err(dev, "wrong cdata->type: %u", - cdata->type); - return -EINVAL; - } - - if (cdata->cmd == SOF_CTRL_CMD_SWITCH) { - comp_info(dev, "tone_cmd_set_value(), SOF_CTRL_CMD_SWITCH"); - for (j = 0; j < cdata->num_elems; j++) { - ch = cdata->chanv[j].channel; - val = cdata->chanv[j].value; - comp_info(dev, "tone_cmd_set_value(), SOF_CTRL_CMD_SWITCH, ch = %u, val = %u", - ch, val); - if (ch >= PLATFORM_MAX_CHANNELS) { - comp_err(dev, "ch >= PLATFORM_MAX_CHANNELS"); - return -EINVAL; - } - - if (val) - tonegen_unmute(&cd->sg[ch]); - else - tonegen_mute(&cd->sg[ch]); - } - } else { - comp_err(dev, "invalid cdata->cmd"); - return -EINVAL; - } - - return 0; -} - -static int tone_cmd_set_data(struct comp_dev *dev, - struct sof_ipc_ctrl_data *cdata) -{ - struct comp_data *cd = comp_get_drvdata(dev); - struct sof_ipc_ctrl_value_comp *compv; - int i; - uint32_t ch; - uint32_t val; - - comp_info(dev, "tone_cmd_set_data()"); - - if (cdata->type != SOF_CTRL_TYPE_VALUE_COMP_SET) { - comp_err(dev, "wrong cdata->type: %u", - cdata->type); - return -EINVAL; - } - - switch (cdata->cmd) { - case SOF_CTRL_CMD_ENUM: - comp_info(dev, "tone_cmd_set_data(), SOF_CTRL_CMD_ENUM, cdata->index = %u", - cdata->index); - compv = (struct sof_ipc_ctrl_value_comp *)ASSUME_ALIGNED(&cdata->data->data, 4); - - for (i = 0; i < (int)cdata->num_elems; i++) { - ch = compv[i].index; - val = compv[i].svalue; - comp_info(dev, "tone_cmd_set_data(), SOF_CTRL_CMD_ENUM, ch = %u, val = %u", - ch, val); - switch (cdata->index) { - case SOF_TONE_IDX_FREQUENCY: - comp_info(dev, "tone_cmd_set_data(), SOF_TONE_IDX_FREQUENCY"); - tonegen_update_f(&cd->sg[ch], val); - break; - case SOF_TONE_IDX_AMPLITUDE: - comp_info(dev, "tone_cmd_set_data(), SOF_TONE_IDX_AMPLITUDE"); - tonegen_set_a(&cd->sg[ch], val); - break; - case SOF_TONE_IDX_FREQ_MULT: - comp_info(dev, "tone_cmd_set_data(), SOF_TONE_IDX_FREQ_MULT"); - tonegen_set_freq_mult(&cd->sg[ch], val); - break; - case SOF_TONE_IDX_AMPL_MULT: - comp_info(dev, "tone_cmd_set_data(), SOF_TONE_IDX_AMPL_MULT"); - tonegen_set_ampl_mult(&cd->sg[ch], val); - break; - case SOF_TONE_IDX_LENGTH: - comp_info(dev, "tone_cmd_set_data(), SOF_TONE_IDX_LENGTH"); - tonegen_set_length(&cd->sg[ch], val); - break; - case SOF_TONE_IDX_PERIOD: - comp_info(dev, "tone_cmd_set_data(), SOF_TONE_IDX_PERIOD"); - tonegen_set_period(&cd->sg[ch], val); - break; - case SOF_TONE_IDX_REPEATS: - comp_info(dev, "tone_cmd_set_data(), SOF_TONE_IDX_REPEATS"); - tonegen_set_repeats(&cd->sg[ch], val); - break; - case SOF_TONE_IDX_LIN_RAMP_STEP: - comp_info(dev, "tone_cmd_set_data(), SOF_TONE_IDX_LIN_RAMP_STEP"); - tonegen_set_linramp(&cd->sg[ch], val); - break; - default: - comp_err(dev, "invalid cdata->index"); - return -EINVAL; - } - } - break; - default: - comp_err(dev, "invalid cdata->cmd"); - return -EINVAL; - } - - return 0; -} - -/* used to pass standard and bespoke commands (with data) to component */ -static int tone_cmd(struct comp_dev *dev, int cmd, void *data, - int max_data_size) -{ - struct sof_ipc_ctrl_data *cdata = ASSUME_ALIGNED(data, 4); - int ret = 0; - - comp_info(dev, "tone_cmd()"); - - switch (cmd) { - case COMP_CMD_SET_DATA: - ret = tone_cmd_set_data(dev, cdata); - break; - case COMP_CMD_SET_VALUE: - ret = tone_cmd_set_value(dev, cdata); - break; - case COMP_CMD_GET_VALUE: - ret = tone_cmd_get_value(dev, cdata, max_data_size); - break; - } - - return ret; -} -#endif - -static int tone_trigger(struct comp_dev *dev, int cmd) -{ - comp_info(dev, "tone_trigger()"); - - return comp_set_state(dev, cmd); -} - -/* copy and process stream data from source to sink buffers */ -static int tone_copy(struct comp_dev *dev) -{ - struct comp_buffer *sink; - struct comp_data *cd = comp_get_drvdata(dev); - uint32_t free; - int ret = 0; - - comp_dbg(dev, "tone_copy()"); - - /* tone component sink buffer */ - sink = comp_dev_get_first_data_consumer(dev); - free = audio_stream_get_free_bytes(&sink->stream); - - /* Test that sink has enough free frames. Then run once to maintain - * low latency and steady load for tones. - */ - if (free >= cd->period_bytes) { - /* create tone */ - cd->tone_func(dev, &sink->stream, dev->frames); - buffer_stream_writeback(sink, cd->period_bytes); - - /* calc new free and available */ - comp_update_buffer_produce(sink, cd->period_bytes); - - ret = dev->frames; - } - - return ret; -} - -static int tone_prepare(struct comp_dev *dev) -{ - struct comp_data *cd = comp_get_drvdata(dev); - struct comp_buffer *sourceb; - int32_t f; - int32_t a; - int ret; - int i; - - comp_info(dev, "tone_prepare()"); - - ret = comp_set_state(dev, COMP_TRIGGER_PREPARE); - if (ret < 0) - return ret; - - if (ret == COMP_STATUS_STATE_ALREADY_SET) - return PPL_STATUS_PATH_STOP; - - sourceb = comp_dev_get_first_data_producer(dev); - if (!sourceb) { - comp_err(dev, "no source buffer"); - return -ENOTCONN; - } - - cd->channels = audio_stream_get_channels(&sourceb->stream); - comp_info(dev, "tone_prepare(), cd->channels = %u, cd->rate = %u", - cd->channels, cd->rate); - - for (i = 0; i < cd->channels; i++) { - f = tonegen_get_f(&cd->sg[i]); - a = tonegen_get_a(&cd->sg[i]); - if (tonegen_init(&cd->sg[i], cd->rate, f, a) < 0) { - comp_set_state(dev, COMP_TRIGGER_RESET); - return -EINVAL; - } - } - - return 0; -} - -static int tone_reset(struct comp_dev *dev) -{ - struct comp_data *cd = comp_get_drvdata(dev); - int i; - - comp_info(dev, "tone_reset()"); - - /* Initialize with the defaults */ - for (i = 0; i < PLATFORM_MAX_CHANNELS; i++) - tonegen_reset(&cd->sg[i]); - - comp_set_state(dev, COMP_TRIGGER_RESET); - - return 0; -} - -static const struct comp_driver comp_tone = { - .type = SOF_COMP_TONE, - .uid = SOF_RT_UUID(tone_uuid), - .tctx = &tone_tr, - .ops = { - .create = tone_new, - .free = tone_free, - .params = tone_params, -#if CONFIG_IPC_MAJOR_3 - .cmd = tone_cmd, -#endif - .trigger = tone_trigger, - .copy = tone_copy, - .prepare = tone_prepare, - .reset = tone_reset, - }, -}; - -static SHARED_DATA struct comp_driver_info comp_tone_info = { - .drv = &comp_tone, -}; - -UT_STATIC void sys_comp_tone_init(void) -{ - comp_register(platform_shared_get(&comp_tone_info, - sizeof(comp_tone_info))); -} - -DECLARE_MODULE(sys_comp_tone_init); -SOF_MODULE_INIT(tone, sys_comp_tone_init); diff --git a/src/audio/tone/CMakeLists.txt b/src/audio/tone/CMakeLists.txt new file mode 100644 index 000000000000..58f22ec6c59e --- /dev/null +++ b/src/audio/tone/CMakeLists.txt @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: BSD-3-Clause + +if(CONFIG_COMP_TONE STREQUAL "m" AND DEFINED CONFIG_LLEXT) + add_subdirectory(llext ${PROJECT_BINARY_DIR}/tone_llext) + add_dependencies(app tone) +else() + if(CONFIG_IPC_MAJOR_3) + add_local_sources(sof tone.c tone-ipc3.c) + elseif(CONFIG_IPC_MAJOR_4) + add_local_sources(sof tone.c tone-ipc4.c) + endif() +endif() diff --git a/src/audio/tone/Kconfig b/src/audio/tone/Kconfig new file mode 100644 index 000000000000..8cb6f7302497 --- /dev/null +++ b/src/audio/tone/Kconfig @@ -0,0 +1,16 @@ +# SPDX-License-Identifier: BSD-3-Clause + +config COMP_TONE + tristate "Tone component" + default m if LIBRARY_DEFAULT_MODULAR + default y + depends on COMP_MODULE_ADAPTER + depends on IPC_MAJOR_4 + select CORDIC_FIXED + help + Select for Tone component. This component is used to generate + audio tones (sine waves) at specified frequencies when the Tone + mode is enabled. The two other modes it supports are silence where + it generates 0s and passthrough where the input is passed to the + output. Warning: This component is untested with IPC_MAJOR_3 and + is unavailable. diff --git a/src/audio/tone/README.md b/src/audio/tone/README.md new file mode 100644 index 000000000000..e2435c2e2b3a --- /dev/null +++ b/src/audio/tone/README.md @@ -0,0 +1,13 @@ +# Tone Generator Architecture + +This directory contains the Tone component. + +## Overview + +Synthesizes simple sine-waves or beep sequences, generally used for internal testing, diagnostics, or system alert sounds. + +## Configuration and Scripts + +- **Kconfig**: Controls compilation inclusion via `COMP_TONE` acting securely on top of the generic `COMP_MODULE_ADAPTER` and `IPC_MAJOR_4`. Depends on the `CORDIC_FIXED` library for sine approximations. +- **CMakeLists.txt**: Links `tone.c` to respective system-level pipelines (`tone-ipc3.c` or `tone-ipc4.c`) and allows execution seamlessly as a zephyr `llext`. +- **tone.toml**: Describes module structure mappings assigning `UUIDREG_STR_TONE` up to multi-instance capacities. diff --git a/src/audio/tone/llext/CMakeLists.txt b/src/audio/tone/llext/CMakeLists.txt new file mode 100644 index 000000000000..39b0894a013a --- /dev/null +++ b/src/audio/tone/llext/CMakeLists.txt @@ -0,0 +1,7 @@ +# Copyright (c) 2025 Intel Corporation. +# SPDX-License-Identifier: Apache-2.0 + +sof_llext_build("tone" + SOURCES ../tone.c ../tone-ipc4.c + LIB openmodules +) diff --git a/src/audio/tone/llext/llext.toml.h b/src/audio/tone/llext/llext.toml.h new file mode 100644 index 000000000000..20f5940c3555 --- /dev/null +++ b/src/audio/tone/llext/llext.toml.h @@ -0,0 +1,6 @@ +#include <tools/rimage/config/platform.toml> +#define LOAD_TYPE "2" +#include "../tone.toml" + +[module] +count = __COUNTER__ diff --git a/src/audio/tone/tone-ipc3.c b/src/audio/tone/tone-ipc3.c new file mode 100644 index 000000000000..b2f153696c20 --- /dev/null +++ b/src/audio/tone/tone-ipc3.c @@ -0,0 +1,409 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// Copyright(c) 2016 Intel Corporation. All rights reserved. +// +// Author: Seppo Ingalsuo <seppo.ingalsuo@linux.intel.com> +// Liam Girdwood <liam.r.girdwood@linux.intel.com> +// Keyon Jie <yang.jie@linux.intel.com> + +#include <sof/audio/buffer.h> +#include <sof/audio/component.h> +#include <sof/audio/format.h> +#include <sof/audio/pipeline.h> +#include <sof/audio/ipc-config.h> +#include <sof/common.h> +#include <rtos/panic.h> +#include <sof/ipc/msg.h> +#include <rtos/alloc.h> +#include <rtos/init.h> +#include <sof/lib/memory.h> /* for SHARED_DATA */ +#include <sof/lib/uuid.h> +#include <sof/list.h> +#include <sof/math/trig.h> +#include <sof/platform.h> +#include <rtos/string.h> +#include <sof/trace/trace.h> +#include <sof/ut.h> +#include <ipc/control.h> +#include <ipc/stream.h> +#include <ipc/topology.h> +#include <user/tone.h> +#include <user/trace.h> +#include <errno.h> +#include <stdbool.h> +#include <stddef.h> +#include <stdint.h> +#include "tone.h" + +SOF_DEFINE_REG_UUID(tone); +LOG_MODULE_DECLARE(tone, CONFIG_SOF_LOG_LEVEL); + +static const struct comp_driver comp_tone; + +static struct comp_dev *tone_new(const struct comp_driver *drv, + const struct comp_ipc_config *config, + const void *spec) +{ + struct comp_dev *dev; + const struct ipc_config_tone *ipc_tone = spec; + struct comp_data *cd; + int i; + + comp_cl_info(&comp_tone, "tone_new()"); + + dev = comp_alloc(drv, sizeof(*dev)); + if (!dev) + return NULL; + dev->ipc_config = *config; + + cd = rzalloc(SOF_MEM_FLAG_USER, sizeof(*cd)); + if (!cd) { + comp_free_device(dev); + return NULL; + } + + comp_set_drvdata(dev, cd); + cd->tone_func = tone_s32_default; + + cd->rate = ipc_tone->sample_rate; + + /* Reset tone generator and set channels volumes to default */ + for (i = 0; i < PLATFORM_MAX_CHANNELS; i++) + tonegen_reset(&cd->sg[i]); + + dev->state = COMP_STATE_READY; + return dev; +} + +static void tone_free(struct comp_dev *dev) +{ + struct tone_data *td = comp_get_drvdata(dev); + + comp_info(dev, "entry"); + + rfree(td); + comp_free_device(dev); +} + +/* set component audio stream parameters */ +static int tone_params(struct comp_dev *dev, + struct sof_ipc_stream_params *params) +{ + struct comp_data *cd = comp_get_drvdata(dev); + struct comp_buffer *sourceb, *sinkb; + + sourceb = comp_dev_get_first_data_producer(dev); + sinkb = comp_dev_get_first_data_consumer(dev); + if (!sourceb || !sinkb) { + comp_err(dev, "no source or sink buffer"); + return -ENOTCONN; + } + + comp_info(dev, "config->frame_fmt = %u", + dev->ipc_config.frame_fmt); + + /* Tone supports only S32_LE PCM format atm */ + if (dev->ipc_config.frame_fmt != SOF_IPC_FRAME_S32_LE) + return -EINVAL; + + audio_stream_set_frm_fmt(&sourceb->stream, dev->ipc_config.frame_fmt); + audio_stream_set_frm_fmt(&sinkb->stream, dev->ipc_config.frame_fmt); + + /* calculate period size based on config */ + cd->period_bytes = dev->frames * + audio_stream_frame_bytes(&sourceb->stream); + + return 0; +} + +#if CONFIG_IPC_MAJOR_3 +static int tone_cmd_get_value(struct comp_dev *dev, + struct sof_ipc_ctrl_data *cdata, int max_size) +{ + struct comp_data *cd = comp_get_drvdata(dev); + int j; + + comp_info(dev, "entry"); + + if (cdata->type != SOF_CTRL_TYPE_VALUE_CHAN_GET) { + comp_err(dev, "wrong cdata->type: %u", + cdata->type); + return -EINVAL; + } + + if (cdata->cmd == SOF_CTRL_CMD_SWITCH) { + for (j = 0; j < cdata->num_elems; j++) { + cdata->chanv[j].channel = j; + cdata->chanv[j].value = !cd->sg[j].mute; + comp_info(dev, "j = %u, cd->sg[j].mute = %u", + j, cd->sg[j].mute); + } + } + return 0; +} + +static int tone_cmd_set_value(struct comp_dev *dev, + struct sof_ipc_ctrl_data *cdata) +{ + struct comp_data *cd = comp_get_drvdata(dev); + int j; + uint32_t ch; + bool val; + + if (cdata->type != SOF_CTRL_TYPE_VALUE_CHAN_SET) { + comp_err(dev, "wrong cdata->type: %u", + cdata->type); + return -EINVAL; + } + + if (cdata->cmd == SOF_CTRL_CMD_SWITCH) { + comp_info(dev, "SOF_CTRL_CMD_SWITCH"); + for (j = 0; j < cdata->num_elems; j++) { + ch = cdata->chanv[j].channel; + val = cdata->chanv[j].value; + comp_info(dev, "SOF_CTRL_CMD_SWITCH, ch = %u, val = %u", + ch, val); + if (ch >= PLATFORM_MAX_CHANNELS) { + comp_err(dev, "ch >= PLATFORM_MAX_CHANNELS"); + return -EINVAL; + } + + if (val) + tonegen_unmute(&cd->sg[ch]); + else + tonegen_mute(&cd->sg[ch]); + } + } else { + comp_err(dev, "invalid cdata->cmd"); + return -EINVAL; + } + + return 0; +} + +static int tone_cmd_set_data(struct comp_dev *dev, + struct sof_ipc_ctrl_data *cdata) +{ + struct comp_data *cd = comp_get_drvdata(dev); + struct sof_ipc_ctrl_value_comp *compv; + int i; + uint32_t ch; + uint32_t val; + + comp_info(dev, "entry"); + + if (cdata->type != SOF_CTRL_TYPE_VALUE_COMP_SET) { + comp_err(dev, "wrong cdata->type: %u", + cdata->type); + return -EINVAL; + } + + switch (cdata->cmd) { + case SOF_CTRL_CMD_ENUM: + comp_info(dev, "SOF_CTRL_CMD_ENUM, cdata->index = %u", + cdata->index); + compv = (struct sof_ipc_ctrl_value_comp *)ASSUME_ALIGNED(&cdata->data->data, 4); + + for (i = 0; i < (int)cdata->num_elems; i++) { + ch = compv[i].index; + val = compv[i].svalue; + comp_info(dev, "SOF_CTRL_CMD_ENUM, ch = %u, val = %u", + ch, val); + switch (cdata->index) { + case SOF_TONE_IDX_FREQUENCY: + comp_info(dev, "SOF_TONE_IDX_FREQUENCY"); + tonegen_update_f(&cd->sg[ch], val); + break; + case SOF_TONE_IDX_AMPLITUDE: + comp_info(dev, "SOF_TONE_IDX_AMPLITUDE"); + tonegen_set_a(&cd->sg[ch], val); + break; + case SOF_TONE_IDX_FREQ_MULT: + comp_info(dev, "SOF_TONE_IDX_FREQ_MULT"); + tonegen_set_freq_mult(&cd->sg[ch], val); + break; + case SOF_TONE_IDX_AMPL_MULT: + comp_info(dev, "SOF_TONE_IDX_AMPL_MULT"); + tonegen_set_ampl_mult(&cd->sg[ch], val); + break; + case SOF_TONE_IDX_LENGTH: + comp_info(dev, "SOF_TONE_IDX_LENGTH"); + tonegen_set_length(&cd->sg[ch], val); + break; + case SOF_TONE_IDX_PERIOD: + comp_info(dev, "SOF_TONE_IDX_PERIOD"); + tonegen_set_period(&cd->sg[ch], val); + break; + case SOF_TONE_IDX_REPEATS: + comp_info(dev, "SOF_TONE_IDX_REPEATS"); + tonegen_set_repeats(&cd->sg[ch], val); + break; + case SOF_TONE_IDX_LIN_RAMP_STEP: + comp_info(dev, "SOF_TONE_IDX_LIN_RAMP_STEP"); + tonegen_set_linramp(&cd->sg[ch], val); + break; + default: + comp_err(dev, "invalid cdata->index"); + return -EINVAL; + } + } + break; + default: + comp_err(dev, "invalid cdata->cmd"); + return -EINVAL; + } + + return 0; +} + +/* used to pass standard and bespoke commands (with data) to component */ +static int tone_cmd(struct comp_dev *dev, int cmd, void *data, + int max_data_size) +{ + struct sof_ipc_ctrl_data *cdata = ASSUME_ALIGNED(data, 4); + int ret = 0; + + comp_info(dev, "entry"); + + switch (cmd) { + case COMP_CMD_SET_DATA: + ret = tone_cmd_set_data(dev, cdata); + break; + case COMP_CMD_SET_VALUE: + ret = tone_cmd_set_value(dev, cdata); + break; + case COMP_CMD_GET_VALUE: + ret = tone_cmd_get_value(dev, cdata, max_data_size); + break; + } + + return ret; +} +#endif + +static int tone_trigger(struct comp_dev *dev, int cmd) +{ + comp_info(dev, "entry"); + + return comp_set_state(dev, cmd); +} + +/* copy and process stream data from source to sink buffers */ +static int tone_copy(struct comp_dev *dev) +{ + struct comp_buffer *sink; + struct comp_data *cd = comp_get_drvdata(dev); + uint32_t free; + int ret = 0; + + comp_dbg(dev, "entry"); + + /* tone component sink buffer */ + sink = comp_dev_get_first_data_consumer(dev); + free = audio_stream_get_free_bytes(&sink->stream); + + /* Test that sink has enough free frames. Then run once to maintain + * low latency and steady load for tones. + */ + if (free >= cd->period_bytes) { + /* create tone */ + cd->tone_func(dev, &sink->stream, dev->frames); + buffer_stream_writeback(sink, cd->period_bytes); + + /* calc new free and available */ + comp_update_buffer_produce(sink, cd->period_bytes); + + ret = dev->frames; + } + + return ret; +} + +static int tone_prepare(struct comp_dev *dev) +{ + struct comp_data *cd = comp_get_drvdata(dev); + struct comp_buffer *sourceb; + int32_t f; + int32_t a; + int ret; + int i; + + comp_info(dev, "entry"); + + ret = comp_set_state(dev, COMP_TRIGGER_PREPARE); + if (ret < 0) + return ret; + + if (ret == COMP_STATUS_STATE_ALREADY_SET) + return PPL_STATUS_PATH_STOP; + + sourceb = comp_dev_get_first_data_producer(dev); + if (!sourceb) { + comp_err(dev, "no source buffer"); + return -ENOTCONN; + } + + cd->channels = audio_stream_get_channels(&sourceb->stream); + comp_info(dev, "cd->channels = %u, cd->rate = %u", + cd->channels, cd->rate); + + for (i = 0; i < cd->channels; i++) { + f = tonegen_get_f(&cd->sg[i]); + a = tonegen_get_a(&cd->sg[i]); + if (tonegen_init(&cd->sg[i], cd->rate, f, a) < 0) { + comp_set_state(dev, COMP_TRIGGER_RESET); + return -EINVAL; + } + } + + return 0; +} + +static int tone_reset(struct comp_dev *dev) +{ + struct comp_data *cd = comp_get_drvdata(dev); + int i; + + comp_info(dev, "entry"); + + /* Initialize with the defaults */ + for (i = 0; i < PLATFORM_MAX_CHANNELS; i++) + tonegen_reset(&cd->sg[i]); + + comp_set_state(dev, COMP_TRIGGER_RESET); + + return 0; +} + +DECLARE_TR_CTX(tone_tr, SOF_UUID(tone_uuid), LOG_LEVEL_INFO); + +static const struct comp_driver comp_tone = { + .type = SOF_COMP_TONE, + .uid = SOF_RT_UUID(tone_uuid), + .tctx = &tone_tr, + .ops = { + .create = tone_new, + .free = tone_free, + .params = tone_params, +#if CONFIG_IPC_MAJOR_3 + .cmd = tone_cmd, +#endif + .trigger = tone_trigger, + .copy = tone_copy, + .prepare = tone_prepare, + .reset = tone_reset, + }, +}; + +static SHARED_DATA struct comp_driver_info comp_tone_info = { + .drv = &comp_tone, +}; + +UT_STATIC void sys_comp_tone_init(void) +{ + comp_register(platform_shared_get(&comp_tone_info, + sizeof(comp_tone_info))); +} + +DECLARE_MODULE(sys_comp_tone_init); +SOF_MODULE_INIT(tone, sys_comp_tone_init); diff --git a/src/audio/tone/tone-ipc4.c b/src/audio/tone/tone-ipc4.c new file mode 100644 index 000000000000..30d99058a3d6 --- /dev/null +++ b/src/audio/tone/tone-ipc4.c @@ -0,0 +1,229 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// Copyright(c) 2016 Intel Corporation. All rights reserved. +// +// Author: Seppo Ingalsuo <seppo.ingalsuo@linux.intel.com> +// Liam Girdwood <liam.r.girdwood@linux.intel.com> +// Keyon Jie <yang.jie@linux.intel.com> + +#include <sof/audio/buffer.h> +#include <sof/audio/component.h> +#include <sof/audio/format.h> +#include <sof/audio/module_adapter/module/generic.h> +#include <sof/audio/pipeline.h> +#include <sof/audio/ipc-config.h> +#include <sof/common.h> +#include <rtos/panic.h> +#include <sof/ipc/msg.h> +#include <rtos/alloc.h> +#include <rtos/init.h> +#include <sof/lib/memory.h> /* for SHARED_DATA */ +#include <sof/lib/uuid.h> +#include <sof/list.h> +#include <sof/math/trig.h> +#include <sof/platform.h> +#include <rtos/string.h> +#include <sof/trace/trace.h> +#include <sof/ut.h> +#include <ipc/control.h> +#include <ipc/stream.h> +#include <ipc/topology.h> +#include <user/tone.h> +#include <user/trace.h> +#include <errno.h> +#include <stdbool.h> +#include <stddef.h> +#include <stdint.h> +#include <ipc4/base-config.h> +#include <sof/audio/sink_api.h> +#include "tone.h" + +SOF_DEFINE_REG_UUID(tone); +LOG_MODULE_DECLARE(tone, CONFIG_SOF_LOG_LEVEL); + +static int tone_init(struct processing_module *mod) +{ + struct module_data *mod_data = &mod->priv; + struct module_config *mod_config = &mod->priv.cfg; + struct comp_dev *dev = mod->dev; + struct comp_data *cd; + int i; + + cd = mod_alloc(mod, sizeof(*cd)); + if (!cd) + return -ENOMEM; + + mod_data->private = cd; + + /* Tnoe only supports 32-bit format */ + cd->tone_func = tone_s32_default; + + /* + * set direction for the comp. In the case of the tone generator being used for + * echo reference, the number of input pins will be non-zero + */ + if (mod_config->nb_input_pins > 0) { + dev->direction = SOF_IPC_STREAM_CAPTURE; + cd->mode = TONE_MODE_SILENCE; + + } else { + dev->direction = SOF_IPC_STREAM_PLAYBACK; + cd->mode = TONE_MODE_TONEGEN; + } + + dev->direction_set = true; + + /* Reset tone generator and set channels volumes to default */ + for (i = 0; i < PLATFORM_MAX_CHANNELS; i++) + tonegen_reset(&cd->sg[i]); + + return 0; +} + +static int tone_free(struct processing_module *mod) +{ + struct comp_data *cd = module_get_private_data(mod); + + comp_info(mod->dev, "entry"); + + mod_free(mod, cd); + return 0; +} + +/* set component audio stream parameters */ +static int tone_params(struct processing_module *mod) +{ + struct comp_data *cd = module_get_private_data(mod); + struct comp_dev *dev = mod->dev; + struct comp_buffer *sinkb; + enum sof_ipc_frame frame_fmt, valid_fmt; + + sinkb = comp_dev_get_first_data_consumer(dev); + if (!sinkb) { + comp_err(dev, "no sink buffer found for tone component"); + return -ENOTCONN; + } + + audio_stream_fmt_conversion(mod->priv.cfg.base_cfg.audio_fmt.depth, + mod->priv.cfg.base_cfg.audio_fmt.valid_bit_depth, + &frame_fmt, &valid_fmt, + mod->priv.cfg.base_cfg.audio_fmt.s_type); + cd->rate = mod->priv.cfg.base_cfg.audio_fmt.sampling_frequency; + + /* Tone supports only S32_LE PCM format atm */ + if (frame_fmt != SOF_IPC_FRAME_S32_LE) { + comp_err(dev, "unsupported frame_fmt = %u", frame_fmt); + return -EINVAL; + } + + return 0; +} + +/* copy and process stream data from source to sink buffers */ +static int tone_process(struct processing_module *mod, + struct sof_source **sources, int num_of_sources, + struct sof_sink **sinks, int num_of_sinks) +{ + struct comp_data *cd = module_get_private_data(mod); + + if (num_of_sources > 0) + return cd->tone_func(mod, sinks[0], sources[0]); + + return cd->tone_func(mod, sinks[0], NULL); +} + +static int tone_prepare(struct processing_module *mod, struct sof_source **sources, + int num_of_sources, struct sof_sink **sinks, int num_of_sinks) +{ + struct comp_data *cd = module_get_private_data(mod); + int32_t f; + int32_t a; + int ret; + int i; + + ret = tone_params(mod); + if (ret < 0) + return ret; + + cd->channels = mod->priv.cfg.base_cfg.audio_fmt.channels_count; + + for (i = 0; i < cd->channels; i++) { + f = tonegen_get_f(&cd->sg[i]); + a = tonegen_get_a(&cd->sg[i]); + if (tonegen_init(&cd->sg[i], cd->rate, f, a) < 0) + return -EINVAL; + } + + return 0; +} + +static int tone_reset(struct processing_module *mod) +{ + struct comp_data *cd = module_get_private_data(mod); + int i; + + /* Initialize with the defaults */ + for (i = 0; i < PLATFORM_MAX_CHANNELS; i++) + tonegen_reset(&cd->sg[i]); + + return 0; +} + +static int tone_bind(struct processing_module *mod, struct bind_info *bind_data) +{ + struct comp_data *cd = module_get_private_data(mod); + + /* nothing to do when tone is not the sink */ + if (bind_data->bind_type != COMP_BIND_TYPE_SOURCE) + return 0; + + /* set passthrough mode when tone generator is bound as a sink */ + cd->mode = TONE_MODE_PASSTHROUGH; + + return 0; +} + +static int tone_unbind(struct processing_module *mod, struct bind_info *unbind_data) +{ + struct comp_data *cd = module_get_private_data(mod); + + /* nothing to do when tone is not the sink */ + if (unbind_data->bind_type != COMP_BIND_TYPE_SOURCE) + return 0; + + /* set silence mode when tone generator is unbound from a source module */ + cd->mode = TONE_MODE_SILENCE; + + return 0; +} + +static const struct module_interface tone_interface = { + .init = tone_init, + .prepare = tone_prepare, + .process = tone_process, + .reset = tone_reset, + .free = tone_free, + .bind = tone_bind, + .unbind = tone_unbind, +}; + +#if CONFIG_COMP_TONE_MODULE +/* modular: llext dynamic link */ + +#include <module/module/api_ver.h> +#include <module/module/llext.h> +#include <rimage/sof/user/manifest.h> + +static const struct sof_man_module_manifest mod_manifest[] __section(".module") __used = { + SOF_LLEXT_MODULE_MANIFEST("TONE", &tone_interface, 1, SOF_REG_UUID(tone), 30), +}; + +SOF_LLEXT_BUILDINFO; + +#else + +DECLARE_TR_CTX(tone_tr, SOF_UUID(tone_uuid), LOG_LEVEL_INFO); +DECLARE_MODULE_ADAPTER(tone_interface, tone_uuid, tone_tr); +SOF_MODULE_INIT(tone, sys_comp_module_tone_interface_init); + +#endif diff --git a/src/audio/tone/tone.c b/src/audio/tone/tone.c new file mode 100644 index 000000000000..975e4ee06bc8 --- /dev/null +++ b/src/audio/tone/tone.c @@ -0,0 +1,355 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// Copyright(c) 2016 Intel Corporation. All rights reserved. +// +// Author: Seppo Ingalsuo <seppo.ingalsuo@linux.intel.com> +// Liam Girdwood <liam.r.girdwood@linux.intel.com> +// Keyon Jie <yang.jie@linux.intel.com> + +#include <sof/audio/buffer.h> +#include <sof/audio/component.h> +#include <sof/audio/format.h> +#include <sof/audio/module_adapter/module/generic.h> +#include <sof/audio/pipeline.h> +#include <sof/audio/ipc-config.h> +#include <sof/common.h> +#include <rtos/panic.h> +#include <sof/ipc/msg.h> +#include <rtos/alloc.h> +#include <rtos/init.h> +#include <sof/lib/memory.h> /* for SHARED_DATA */ +#include <sof/lib/uuid.h> +#include <sof/list.h> +#include <sof/math/trig.h> +#include <sof/platform.h> +#include <rtos/string.h> +#include <sof/trace/trace.h> +#include <sof/ut.h> +#include <ipc/control.h> +#include <ipc/stream.h> +#include <ipc/topology.h> +#include <user/tone.h> +#include <user/trace.h> +#include <errno.h> +#include <stdbool.h> +#include <stddef.h> +#include <stdint.h> +#include <ipc4/base-config.h> +#include <sof/audio/sink_api.h> +#include "tone.h" + +LOG_MODULE_REGISTER(tone, CONFIG_SOF_LOG_LEVEL); + +static int32_t tonegen(struct tone_state *sg) +{ + int64_t sine; + int64_t w; + /* sg->w is angle in Q4.28 radians format, sin() returns Q1.31 */ + /* sg->a is amplitude as Q1.31 */ + sine = + q_mults_32x32(sin_fixed_32b(sg->w), sg->a, + Q_SHIFT_BITS_64(31, 31, 31)); + + /* Next point */ + w = (int64_t)sg->w + sg->w_step; + sg->w = (w > PI_MUL2_Q4_28) + ? (int32_t)(w - PI_MUL2_Q4_28) : (int32_t)w; + + if (sg->mute) + return 0; + else + return (int32_t)sine; /* Q1.31 no saturation need */ +} + +static void tonegen_control(struct tone_state *sg) +{ + int64_t a; + int64_t p; + + /* Count samples, 125 us blocks */ + sg->sample_count++; + if (sg->sample_count < sg->samples_in_block) + return; + + sg->sample_count = 0; + if (sg->block_count < INT32_MAX) + sg->block_count++; + + /* Fade-in ramp during tone */ + if (sg->block_count < sg->tone_length) { + if (sg->a == 0) + sg->w = 0; /* Reset phase to have less clicky ramp */ + + if (sg->a > sg->a_target) { + a = (int64_t)sg->a - sg->ramp_step; + if (a < sg->a_target) + a = sg->a_target; + + } else { + a = (int64_t)sg->a + sg->ramp_step; + if (a > sg->a_target) + a = sg->a_target; + } + sg->a = (int32_t)a; + } + + /* Fade-out ramp after tone*/ + if (sg->block_count > sg->tone_length) { + a = (int64_t)sg->a - sg->ramp_step; + if (a < 0) + a = 0; + + sg->a = (int32_t)a; + } + + /* New repeated tone, update for frequency or amplitude sweep */ + if (sg->block_count > sg->tone_period && + (sg->repeat_count + 1 < sg->repeats)) { + sg->block_count = 0; + if (sg->ampl_coef > 0) { + sg->a_target = + sat_int32(q_multsr_32x32(sg->a_target, + sg->ampl_coef, + Q_SHIFT_BITS_64(31, 30, 31))); + sg->a = (sg->ramp_step > sg->a_target) + ? sg->a_target : sg->ramp_step; + } + if (sg->freq_coef > 0) { + /* f is Q16.16, freq_coef is Q2.30 */ + p = q_multsr_32x32(sg->f, sg->freq_coef, + Q_SHIFT_BITS_64(16, 30, 16)); + tonegen_update_f(sg, (int32_t)p); /* No saturation */ + } + sg->repeat_count++; + } +} + +static int tone_s32_passthrough(struct processing_module *mod, struct sof_sink *sink, + struct sof_source *source) +{ + struct comp_data *cd = module_get_private_data(mod); + size_t output_frame_bytes, output_frames; + size_t input_frame_bytes, input_frames; + int32_t *output_pos, *output_start, output_cirbuf_size; + int32_t const *input_pos, *input_start, *input_end; + int32_t *output_end, input_cirbuf_size; + uint32_t frames, bytes; + int nch = cd->channels; + int n; + int ret; + + /* tone generator only ever has 1 sink */ + output_frames = sink_get_free_frames(sink); + output_frame_bytes = sink_get_frame_bytes(sink); + output_frames = mod->period_bytes / output_frame_bytes; + + ret = sink_get_buffer_s32(sink, output_frames * output_frame_bytes, + &output_pos, &output_start, &output_cirbuf_size); + if (ret) { + comp_err(mod->dev, "sink_get_buffer_s32() failed"); + return -ENODATA; + } + + input_frames = source_get_data_frames_available(source); + input_frame_bytes = source_get_frame_bytes(source); + + ret = source_get_data_s32(source, input_frames * input_frame_bytes, + &input_pos, &input_start, &input_cirbuf_size); + if (ret) { + comp_err(mod->dev, "source_get_data_s32() failed"); + return -ENODATA; + } + input_end = input_start + input_cirbuf_size; + + frames = MIN(output_frames, input_frames); + + if (frames * output_frame_bytes >= mod->period_bytes) + frames = mod->period_bytes / output_frame_bytes; + bytes = frames * output_frame_bytes; + + output_end = output_start + output_cirbuf_size; + + n = frames * nch; + + while (n > 0) { + int n_wrap_source, n_wrap_dest, n_min; + int i; + + n_wrap_dest = output_end - output_pos; + n_wrap_source = input_end - input_pos; + + /* Process until source/dest wrap or completed n */ + n_min = (n < n_wrap_dest) ? n : n_wrap_dest; + n_min = (n_min < n_wrap_source) ? n_min : n_wrap_source; + while (n_min > 0) { + n -= nch; + n_min -= nch; + for (i = 0; i < nch; i++) { + *output_pos = *input_pos; + output_pos++; + input_pos++; + } + } + + /* Wrap destination/source buffer */ + if (output_pos >= output_end) + output_pos = output_start; + if (input_pos >= input_end) + input_pos = input_start; + } + + ret = sink_commit_buffer(sink, bytes); + if (ret) + return ret; + + return source_release_data(source, bytes); +} + +/* + * Tone generator algorithm code + */ +int tone_s32_default(struct processing_module *mod, struct sof_sink *sink, + struct sof_source *source) +{ + struct comp_data *cd = module_get_private_data(mod); + size_t output_frame_bytes, output_frames; + int32_t *output_pos, *output_start, output_cirbuf_size; + int32_t *output_end; + uint32_t frames, bytes; + int nch = cd->channels; + int i; + int n; + int n_wrap_dest; + int n_min; + int ret; + + if (cd->mode == TONE_MODE_PASSTHROUGH) + return tone_s32_passthrough(mod, sink, source); + + /* tone generator only ever has 1 sink */ + output_frames = sink_get_free_frames(sink); + output_frame_bytes = sink_get_frame_bytes(sink); + output_frames = mod->period_bytes / output_frame_bytes; + + ret = sink_get_buffer_s32(sink, output_frames * output_frame_bytes, + &output_pos, &output_start, &output_cirbuf_size); + if (ret) + return -ENODATA; + + frames = output_frames; + + if (frames * output_frame_bytes >= mod->period_bytes) + frames = mod->period_bytes / output_frame_bytes; + bytes = frames * output_frame_bytes; + + output_end = output_start + output_cirbuf_size; + + n = frames * nch; + if (!source) { + while (n > 0) { + n_wrap_dest = output_end - output_pos; + + /* Process until wrap or completed n */ + n_min = (n < n_wrap_dest) ? n : n_wrap_dest; + while (n_min > 0) { + n -= nch; + n_min -= nch; + for (i = 0; i < nch; i++) { + switch (cd->mode) { + case TONE_MODE_TONEGEN: + tonegen_control(&cd->sg[i]); + *output_pos = tonegen(&cd->sg[i]); + break; + case TONE_MODE_SILENCE: + *output_pos = 0; + break; + default: + break; + } + output_pos++; + } + } + + /* Wrap destination buffer */ + output_pos = output_start; + } + } + + return sink_commit_buffer(sink, bytes); +} + +void tonegen_update_f(struct tone_state *sg, int32_t f) +{ + int64_t w_tmp; + int64_t f_max; + + /* Calculate Fs/2, fs is Q32.0, f is Q16.16 */ + f_max = Q_SHIFT_LEFT((int64_t)sg->fs, 0, 16 - 1); + f_max = (f_max > INT32_MAX) ? INT32_MAX : f_max; + sg->f = (f > f_max) ? f_max : f; + /* Q16 x Q31 -> Q28 */ + w_tmp = q_multsr_32x32(sg->f, sg->c, Q_SHIFT_BITS_64(16, 31, 28)); + w_tmp = (w_tmp > PI_Q4_28) ? PI_Q4_28 : w_tmp; /* Limit to pi Q4.28 */ + sg->w_step = (int32_t)w_tmp; +} + +void tonegen_reset(struct tone_state *sg) +{ + sg->mute = 1; + sg->a = 0; + sg->a_target = TONE_AMPLITUDE_DEFAULT; + sg->c = 0; + sg->f = TONE_FREQUENCY_DEFAULT; + sg->w = 0; + sg->w_step = 0; + + sg->block_count = 0; + sg->repeat_count = 0; + sg->repeats = 0; + sg->sample_count = 0; + sg->samples_in_block = 0; + + /* Continuous tone */ + sg->freq_coef = ONE_Q2_30; /* Set freq multiplier to 1.0 */ + sg->ampl_coef = ONE_Q2_30; /* Set ampl multiplier to 1.0 */ + sg->tone_length = INT32_MAX; + sg->tone_period = INT32_MAX; + sg->ramp_step = ONE_Q1_31; /* Set lin ramp modification to max */ +} + +int tonegen_init(struct tone_state *sg, int32_t fs, int32_t f, int32_t a) +{ + int idx; + int i; + + sg->a_target = a; + sg->a = (sg->ramp_step > sg->a_target) ? sg->a_target : sg->ramp_step; + + idx = -1; + sg->mute = 1; + sg->fs = 0; + + /* Find index of current sample rate and then get from lookup table the + * corresponding 2*pi/Fs value. + */ + for (i = 0; i < TONE_NUM_FS; i++) { + if (fs == tone_fs_list[i]) + idx = i; + } + + if (idx < 0) { + sg->w_step = 0; + return -EINVAL; + } + + sg->fs = fs; + sg->c = tone_pi2_div_fs[idx]; /* Store 2*pi/Fs */ + sg->mute = 0; + tonegen_update_f(sg, f); + + /* 125us as Q1.31 is 268435, calculate fs * 125e-6 in Q31.0 */ + sg->samples_in_block = + (int32_t)q_multsr_32x32(fs, 268435, Q_SHIFT_BITS_64(0, 31, 0)); + + return 0; +} diff --git a/src/audio/tone/tone.h b/src/audio/tone/tone.h new file mode 100644 index 000000000000..4183543e0beb --- /dev/null +++ b/src/audio/tone/tone.h @@ -0,0 +1,172 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * + * Copyright(c) 2016 Intel Corporation. + * + */ + +#include <sof/audio/buffer.h> +#include <sof/audio/component.h> +#include <sof/audio/format.h> +#include <sof/audio/pipeline.h> +#include <sof/audio/ipc-config.h> +#include <sof/common.h> +#include <rtos/panic.h> +#include <sof/ipc/msg.h> +#include <rtos/alloc.h> +#include <rtos/init.h> +#include <sof/lib/memory.h> /* for SHARED_DATA */ +#include <sof/lib/uuid.h> +#include <sof/list.h> +#include <sof/math/trig.h> +#include <sof/platform.h> +#include <rtos/string.h> +#include <sof/trace/trace.h> +#include <sof/ut.h> +#include <ipc/control.h> +#include <ipc/stream.h> +#include <ipc/topology.h> +#include <user/tone.h> +#include <user/trace.h> +#include <errno.h> +#include <stdbool.h> +#include <stddef.h> +#include <stdint.h> + +/* Convert float frequency in Hz to Q16.16 fractional format */ +#define TONE_FREQ(f) Q_CONVERT_FLOAT(f, 16) + +/* Convert float gain to Q1.31 fractional format */ +#define TONE_GAIN(v) Q_CONVERT_FLOAT(v, 31) + +/* Set default tone amplitude and frequency */ +#define TONE_AMPLITUDE_DEFAULT TONE_GAIN(0.1) /* -20 dB */ +#define TONE_FREQUENCY_DEFAULT TONE_FREQ(997.0) +#define TONE_NUM_FS 13 /* Table size for 8-192 kHz range */ + +#define TONE_MODE_TONEGEN 0 +#define TONE_MODE_PASSTHROUGH 1 +#define TONE_MODE_SILENCE 2 + +extern struct tr_ctx tone_tr; +extern const struct sof_uuid tone; + +/* 2*pi/Fs lookup tables in Q1.31 for each Fs */ +static const int32_t tone_fs_list[TONE_NUM_FS] = { + 8000, 11025, 16000, 22050, 24000, 32000, 44100, 48000, + 64000, 88200, 96000, 176400, 192000 +}; + +static const int32_t tone_pi2_div_fs[TONE_NUM_FS] = { + 1686630, 1223858, 843315, 611929, 562210, 421657, 305965, + 281105, 210829, 152982, 140552, 76491, 70276 +}; + +/* tone component private data */ + +struct tone_state { + int mute; + int32_t a; /* Current amplitude Q1.31 */ + int32_t a_target; /* Target amplitude Q1.31 */ + int32_t ampl_coef; /* Amplitude multiplier Q2.30 */ + int32_t c; /* Coefficient 2*pi/Fs Q1.31 */ + int32_t f; /* Frequency Q16.16 */ + int32_t freq_coef; /* Frequency multiplier Q2.30 */ + int32_t fs; /* Sample rate in Hertz Q32.0 */ + int32_t ramp_step; /* Amplitude ramp step Q1.31 */ + int32_t w; /* Angle radians Q4.28 */ + int32_t w_step; /* Angle step Q4.28 */ + uint32_t block_count; + uint32_t repeat_count; + uint32_t repeats; /* Number of repeats for tone (sweep steps) */ + uint32_t sample_count; + uint32_t samples_in_block; /* Samples in 125 us block */ + uint32_t tone_length; /* Active length in 125 us blocks */ + uint32_t tone_period; /* Active + idle time in 125 us blocks */ +}; + +struct comp_data { + uint32_t channels; + uint32_t rate; + struct tone_state sg[PLATFORM_MAX_CHANNELS]; + int (*tone_func)(struct processing_module *mod, struct sof_sink *sink, + struct sof_source *source); + int mode; +}; + +void tonegen_reset(struct tone_state *sg); +int tonegen_init(struct tone_state *sg, int32_t fs, int32_t f, int32_t a); +void tonegen_update_f(struct tone_state *sg, int32_t f); +int tone_s32_default(struct processing_module *mod, struct sof_sink *sink, + struct sof_source *source); + +/* Set sine amplitude */ +static inline void tonegen_set_a(struct tone_state *sg, int32_t a) +{ + sg->a_target = a; +} + +/* Repeated number of beeps */ +static inline void tonegen_set_repeats(struct tone_state *sg, uint32_t r) +{ + sg->repeats = r; +} + +/* The next functions support zero as shortcut for defaults to get + * make a nicer API without need to remember the neutral steady + * non-swept tone settings. + */ + +/* Multiplication factor for frequency as Q2.30 for logarithmic change */ +static inline void tonegen_set_freq_mult(struct tone_state *sg, int32_t fm) +{ + sg->freq_coef = (fm > 0) ? fm : ONE_Q2_30; /* Set freq mult to 1.0 */ +} + +/* Multiplication factor for amplitude as Q2.30 for logarithmic change */ +static inline void tonegen_set_ampl_mult(struct tone_state *sg, int32_t am) +{ + sg->ampl_coef = (am > 0) ? am : ONE_Q2_30; /* Set ampl mult to 1.0 */ +} + +/* Tone length in samples, this is the active length of tone */ +static inline void tonegen_set_length(struct tone_state *sg, uint32_t tl) +{ + sg->tone_length = (tl > 0) ? tl : INT32_MAX; /* Count rate 125 us */ +} + +/* Tone period in samples, this is the length including the pause after beep */ +static inline void tonegen_set_period(struct tone_state *sg, uint32_t tp) +{ + sg->tone_period = (tp > 0) ? tp : INT32_MAX; /* Count rate 125 us */ +} + +/* Tone ramp parameters: + * step - Value that is added or subtracted to amplitude. A zero or negative + * number disables the ramp and amplitude is immediately modified to + * final value. + */ + +static inline void tonegen_set_linramp(struct tone_state *sg, int32_t step) +{ + sg->ramp_step = (step > 0) ? step : INT32_MAX; +} + +static inline int32_t tonegen_get_f(struct tone_state *sg) +{ + return sg->f; +} + +static inline int32_t tonegen_get_a(struct tone_state *sg) +{ + return sg->a_target; +} + +static inline void tonegen_mute(struct tone_state *sg) +{ + sg->mute = 1; +} + +static inline void tonegen_unmute(struct tone_state *sg) +{ + sg->mute = 0; +} diff --git a/src/audio/tone/tone.toml b/src/audio/tone/tone.toml new file mode 100644 index 000000000000..02d6e5dca96b --- /dev/null +++ b/src/audio/tone/tone.toml @@ -0,0 +1,22 @@ +#ifndef LOAD_TYPE +#define LOAD_TYPE "0" +#endif + + REM # Template component module config + [[module.entry]] + name = "TONE" + uuid = UUIDREG_STR_TONE + affinity_mask = "0x1" + instance_count = "40" + domain_types = "0" + load_type = LOAD_TYPE + init_config = "1" + module_type = "9" + auto_start = "0" + sched_caps = [1, 0x00008000] + REM # pin = [dir, type, sample rate, size, container, channel-cfg] + pin = [0, 0, 0xfeef, 0xf, 0xf, 0x45ff, 1, 0, 0xfeef, 0xf, 0xf, 0x1ff] + REM # mod_cfg [PAR_0 PAR_1 PAR_2 PAR_3 IS_BYTES CPS IBS OBS MOD_FLAGS CPC OBLS] + mod_cfg = [0, 0, 0, 0, 4096, 1000000, 128, 128, 0, 0, 0] + + index = __COUNTER__ diff --git a/src/audio/up_down_mixer/README.md b/src/audio/up_down_mixer/README.md new file mode 100644 index 000000000000..723d5cc609fc --- /dev/null +++ b/src/audio/up_down_mixer/README.md @@ -0,0 +1,13 @@ +# Up/Down Mixer Architecture + +This directory contains the Up/Down channel mixer. + +## Overview + +Converts the spatial format of audio (e.g., Stereo up to 5.1 surround, or 5.1 mixed down to Stereo) natively. + +## Configuration and Scripts + +- **Kconfig**: Enabled via `COMP_UP_DOWN_MIXER`, specifically tuned for `IPC_MAJOR_4`. Supports heavy format transformations up to 7.1 endpoints. +- **CMakeLists.txt**: Implements the base `up_down_mixer.c` component along with highly-optimized architecture primitives (`up_down_mixer_hifi3.c`). +- **up_down_mixer.toml**: Robust configuration map controlling `UPDWMIX` deployment against variable system arrays depending on Meteor Lake, Lunar Lake, or ACE platform parameters (UUID binding `UUIDREG_STR_UP_DOWN_MIXER`). diff --git a/src/audio/up_down_mixer/up_down_mixer.c b/src/audio/up_down_mixer/up_down_mixer.c index 3cc8308ad62d..a5843026710a 100644 --- a/src/audio/up_down_mixer/up_down_mixer.c +++ b/src/audio/up_down_mixer/up_down_mixer.c @@ -11,7 +11,6 @@ #include <sof/audio/pipeline.h> #include <rtos/panic.h> #include <sof/ipc/msg.h> -#include <rtos/alloc.h> #include <rtos/cache.h> #include <rtos/init.h> #include <sof/lib/notifier.h> @@ -33,9 +32,6 @@ LOG_MODULE_REGISTER(up_down_mixer, CONFIG_SOF_LOG_LEVEL); /* these ids aligns windows driver requirement to support windows driver */ SOF_DEFINE_REG_UUID(up_down_mixer); -DECLARE_TR_CTX(up_down_mixer_comp_tr, SOF_UUID(up_down_mixer_uuid), - LOG_LEVEL_INFO); - int32_t custom_coeffs[UP_DOWN_MIX_COEFFS_LENGTH]; static int set_downmix_coefficients(struct processing_module *mod, @@ -326,9 +322,9 @@ static int up_down_mixer_free(struct processing_module *mod) { struct up_down_mixer_data *cd = module_get_private_data(mod); - rfree(cd->buf_in); - rfree(cd->buf_out); - rfree(cd); + mod_free(mod, cd->buf_in); + mod_free(mod, cd->buf_out); + mod_free(mod, cd); return 0; } @@ -342,7 +338,7 @@ static int up_down_mixer_init(struct processing_module *mod) struct up_down_mixer_data *cd; int ret; - cd = rzalloc(SOF_MEM_FLAG_USER, sizeof(*cd)); + cd = mod_zalloc(mod, sizeof(*cd)); if (!cd) { comp_free(dev); return -ENOMEM; @@ -350,8 +346,8 @@ static int up_down_mixer_init(struct processing_module *mod) mod_data->private = cd; - cd->buf_in = rballoc(SOF_MEM_FLAG_USER, mod->priv.cfg.base_cfg.ibs); - cd->buf_out = rballoc(SOF_MEM_FLAG_USER, mod->priv.cfg.base_cfg.obs); + cd->buf_in = mod_balloc(mod, mod->priv.cfg.base_cfg.ibs); + cd->buf_out = mod_balloc(mod, mod->priv.cfg.base_cfg.obs); if (!cd->buf_in || !cd->buf_out) { ret = -ENOMEM; goto err; @@ -408,7 +404,7 @@ up_down_mixer_process(struct processing_module *mod, const uint8_t *input0_pos, *input0_start; uint8_t *output_pos, *output_start; - comp_dbg(dev, "up_down_mixer_process()"); + comp_dbg(dev, "entry"); output_frames = sink_get_free_frames(output_buffers[0]); input_frames = source_get_data_frames_available(input_buffers[0]); @@ -448,5 +444,6 @@ static const struct module_interface up_down_mixer_interface = { .free = up_down_mixer_free }; +DECLARE_TR_CTX(up_down_mixer_comp_tr, SOF_UUID(up_down_mixer_uuid), LOG_LEVEL_INFO); DECLARE_MODULE_ADAPTER(up_down_mixer_interface, up_down_mixer_uuid, up_down_mixer_comp_tr); SOF_MODULE_INIT(up_down_mixer, sys_comp_module_up_down_mixer_interface_init); diff --git a/src/audio/up_down_mixer/up_down_mixer.toml b/src/audio/up_down_mixer/up_down_mixer.toml index f233e44fa6aa..713cff7a87d8 100644 --- a/src/audio/up_down_mixer/up_down_mixer.toml +++ b/src/audio/up_down_mixer/up_down_mixer.toml @@ -65,7 +65,7 @@ 16, 0, 0, 0, 216, 4596000, 768, 1152, 0, 4596, 0, 17, 0, 0, 0, 216, 5572000, 1536, 1152, 0, 5572, 0, 18, 0, 0, 0, 216, 4896000, 384, 1536, 0, 4896, 0] -#elif CONFIG_SOC_INTEL_ACE30 || CONFIG_SOC_INTEL_ACE40 +#elif CONFIG_SOC_ACE30 || CONFIG_SOC_ACE40 mod_cfg = [0, 0, 0, 0, 216, 4841000, 384, 192, 0, 4841, 0, 1, 0, 0, 0, 216, 4355000, 384, 384, 0, 4355, 0, 2, 0, 0, 0, 216, 5079000, 576, 384, 0, 5079, 0, diff --git a/src/audio/volume/README.md b/src/audio/volume/README.md new file mode 100644 index 000000000000..c044d73fd11d --- /dev/null +++ b/src/audio/volume/README.md @@ -0,0 +1,24 @@ +# Volume Component Architecture + +This directory contains the Volume control component. + +## Overview + +Applies amplitude modifications to the stream, supporting smooth ramping and muting interfaces configurable by the host. + +## Architecture Diagram + +```mermaid +graph LR + In[Audio Input] --> Mult[Multiplier] + IPC[IPC Control] -.-> Ramp[Ramp Generator] + Ramp --> Mult + Mult --> Out[Audio Output] +``` + +## Configuration and Scripts + +- **Kconfig**: Extensive tuning for the standard volume framework (`COMP_VOLUME`), selecting underlying algorithms (e.g., `COMP_VOLUME_WINDOWS_FADE`, `COMP_VOLUME_LINEAR_RAMP`). Manages sub-settings like querying host telemetry (`COMP_PEAK_VOL`, `PEAK_METER_UPDATE_PERIOD_CHOICE`). +- **CMakeLists.txt**: Extremely versatile linkage resolving general pathways (`volume_generic.c`), custom optimized builds across HIFI pipelines (`volume_hifi3.c`, `volume_hifi4.c`, `volume_hifi5.c`), and dual-path execution logic (`*_with_peakvol.c`). +- **volume.toml**: Registers independent modules `PEAKVOL` (`UUIDREG_STR_VOLUME4`) and `GAIN` (`UUIDREG_STR_GAIN`), outlining explicit memory mapping parameters via platform-dependent constraints. +- **Topology (.conf)**: Derived from `tools/topology/topology2/include/components/volume.conf`, configuring a standard `pga` widget (UUID `7e:67:7e:b7:f4:5f:88:41:af:14:fb:a8:bd:bf:86:82`). Outlines parameters for ramp steps interpolation methods and binds specific ALSA interface elements. diff --git a/src/audio/volume/llext/CMakeLists.txt b/src/audio/volume/llext/CMakeLists.txt index ca33117f0a67..edaeec449450 100644 --- a/src/audio/volume/llext/CMakeLists.txt +++ b/src/audio/volume/llext/CMakeLists.txt @@ -5,9 +5,11 @@ sof_llext_build("volume" SOURCES ../volume_generic.c ../volume_hifi3.c ../volume_hifi4.c + ../volume_hifi5.c ../volume_generic_with_peakvol.c ../volume_hifi3_with_peakvol.c ../volume_hifi4_with_peakvol.c + ../volume_hifi5_with_peakvol.c ../volume.c ../volume_ipc4.c LIB openmodules diff --git a/src/audio/volume/volume.c b/src/audio/volume/volume.c index 9f8aff265226..a6cff8a09083 100644 --- a/src/audio/volume/volume.c +++ b/src/audio/volume/volume.c @@ -24,7 +24,6 @@ #include <sof/common.h> #include <rtos/panic.h> #include <sof/ipc/msg.h> -#include <rtos/alloc.h> #include <rtos/init.h> #include <sof/lib/cpu.h> #include <sof/lib/uuid.h> @@ -490,14 +489,14 @@ int volume_set_chan(struct processing_module *mod, int chan, */ if (v < cd->vol_min) { /* No need to fail, just trace the event. */ - comp_warn(mod->dev, "volume_set_chan: Limited request %d to min. %d", + comp_warn(mod->dev, "Limited request %d to min. %d", v, cd->vol_min); v = cd->vol_min; } if (v > cd->vol_max) { /* No need to fail, just trace the event. */ - comp_warn(mod->dev, "volume_set_chan: Limited request %d to max. %d", + comp_warn(mod->dev, "Limited request %d to max. %d", v, cd->vol_max); v = cd->vol_max; } @@ -557,11 +556,12 @@ static int volume_process(struct processing_module *mod, struct output_stream_buffer *output_buffers, int num_output_buffers) { struct vol_data *cd = module_get_private_data(mod); + struct audio_stream *source = input_buffers[0].data; uint32_t avail_frames = input_buffers[0].size; uint32_t frames; int64_t prev_sum = 0; - comp_dbg(mod->dev, "volume_process()"); + comp_dbg(mod->dev, "entry"); while (avail_frames) { #if CONFIG_COMP_PEAK_VOL @@ -572,12 +572,27 @@ static int volume_process(struct processing_module *mod, frames = avail_frames; } else if (cd->ramp_type == SOF_VOLUME_LINEAR_ZC) { /* with ZC ramping look for next ZC offset */ - frames = cd->zc_get(input_buffers[0].data, cd->vol_ramp_frames, &prev_sum); + frames = cd->zc_get(source, cd->vol_ramp_frames, &prev_sum); + /* Align frames count to audio stream constraints. If it rounds to zero + * round it up to smallest nonzero aligned frames count. + */ + frames = audio_stream_align_frames_round_nearest(source, frames); + if (!frames) + frames = audio_stream_align_frames_round_up(source, 1); } else { - /* without ZC process max ramp chunk */ - frames = cd->vol_ramp_frames; + /* During volume ramp align the number of frames used in this + * gain step. Align up since with low rates this would typically + * become zero. + */ + frames = audio_stream_align_frames_round_up(source, cd->vol_ramp_frames); } + /* Cancel the gain step for ZC or smaller ramp step if it exceeds + * the available frames. + */ + if (frames > avail_frames) + frames = avail_frames; + if (!cd->ramp_finished) { volume_ramp(mod); cd->vol_ramp_elapsed_frames += frames; @@ -624,30 +639,37 @@ static vol_zc_func vol_get_zc_function(struct comp_dev *dev, /** * \brief Set volume frames alignment limit. * \param[in,out] source Structure pointer of source. - * \param[in,out] sink Structure pointer of sink. */ -static void volume_set_alignment(struct audio_stream *source, - struct audio_stream *sink) +static void volume_set_alignment(struct audio_stream *source) { - /* Both source and sink buffer in HiFi5 processing version, - * xtensa intrinsics ask for 16-byte aligned. + const int channels = audio_stream_get_channels(source); + + /* The source buffer in HiFi5 processing version needs 16 bytes alignment. The + * macro SOF_FRAME_BYTE_ALIGN is set in common.h to the requirement align. + * The VOLUME_HIFI3_HIFI4_FRAME_BYTE_ALIGN_6CH also has the value 16 and the + * same align for 5.1 channels works for HiFi5 too. * - * Both source and sink buffer in HiFi3 or HiFi4 processing version, - * xtensa intrinsics ask for 8-byte aligned. 5.1 format SSE audio - * requires 16-byte aligned. + * In HiFi4 processing version the source align requirement is 8 bytes. While + * the 5.1 format SSE audio requires 16 bytes alignment. + */ + const uint32_t byte_align = + (channels == 6) ? VOLUME_HIFI3_HIFI4_FRAME_BYTE_ALIGN_6CH : SOF_FRAME_BYTE_ALIGN; + + /* On HiFi5 the number of samples to process must be multiple of four for s24/s32 + * and multiple of eight for s16. For HiFi4 and HiFi3 the number of samples + * need to be multiple of two for s32 and multiple of four for s16. + * E.g. for s32 format for channels counts of 1, 2, 4, 6, ... the + * frame align need to be 4, 2, 1, 2, ... */ -#if SOF_USE_HIFI(3, VOLUME) || SOF_USE_HIFI(4, VOLUME) - const uint32_t byte_align = audio_stream_get_channels(source) == 6 ? - VOLUME_HIFI3_HIFI4_FRAME_BYTE_ALIGN_6CH : SOF_FRAME_BYTE_ALIGN; +#if SOF_USE_HIFI(5, VOLUME) + const int n = (audio_stream_get_valid_fmt(source) == SOF_IPC_FRAME_S16_LE) ? 8 : 4; #else - const uint32_t byte_align = SOF_FRAME_BYTE_ALIGN; + const int n = (audio_stream_get_valid_fmt(source) == SOF_IPC_FRAME_S16_LE) ? 4 : 2; #endif - /*There is no limit for frame number, so both source and sink set it to be 1*/ - const uint32_t frame_align_req = 1; + const uint32_t frame_align_req = (uint32_t)(n / gcd(n, channels)); audio_stream_set_align(byte_align, frame_align_req, source); - audio_stream_set_align(byte_align, frame_align_req, sink); } /** @@ -671,7 +693,7 @@ static int volume_prepare(struct processing_module *mod, int ret; int i; - comp_dbg(dev, "volume_prepare()"); + comp_dbg(dev, "entry"); /* volume component will only ever have 1 sink and source buffer */ sinkb = comp_dev_get_first_data_consumer(dev); @@ -683,7 +705,7 @@ static int volume_prepare(struct processing_module *mod, ret = volume_peak_prepare(cd, mod); - volume_set_alignment(&sourceb->stream, &sinkb->stream); + volume_set_alignment(&sourceb->stream); /* get sink period bytes */ sink_period_bytes = audio_stream_period_bytes(&sinkb->stream, @@ -761,7 +783,7 @@ static int volume_reset(struct processing_module *mod) { struct vol_data *cd = module_get_private_data(mod); - comp_dbg(mod->dev, "volume_reset()"); + comp_dbg(mod->dev, "entry"); volume_reset_state(cd); return 0; } @@ -774,11 +796,11 @@ static int volume_free(struct processing_module *mod) { struct vol_data *cd = module_get_private_data(mod); - comp_dbg(mod->dev, "volume_free()"); + comp_dbg(mod->dev, "entry"); - volume_peak_free(cd); - rfree(cd->vol); - rfree(cd); + volume_peak_free(mod); + mod_free(mod, cd->vol); + mod_free(mod, cd); return 0; } diff --git a/src/audio/volume/volume.h b/src/audio/volume/volume.h index 7e7c41ea00d7..9d1d08a280fe 100644 --- a/src/audio/volume/volume.h +++ b/src/audio/volume/volume.h @@ -300,7 +300,7 @@ void sys_comp_module_volume_interface_init(void); /* source_or_sink, true means source, false means sink */ void set_volume_process(struct vol_data *cd, struct comp_dev *dev, bool source_or_sink); -void volume_peak_free(struct vol_data *cd); +void volume_peak_free(struct processing_module *mod); int volume_peak_prepare(struct vol_data *cd, struct processing_module *mod); diff --git a/src/audio/volume/volume.toml b/src/audio/volume/volume.toml index fed4defb37de..0c30281def81 100644 --- a/src/audio/volume/volume.toml +++ b/src/audio/volume/volume.toml @@ -31,7 +31,7 @@ 2, 0, 0, 0, 480, 6846000, 720, 720, 0, 6846, 0, 3, 0, 0, 0, 480, 7212000, 768, 768, 0, 7212, 0, 4, 0, 0, 0, 480, 9532000, 1536, 1536, 0, 9532, 0] -#elif CONFIG_SOC_INTEL_ACE30 || CONFIG_SOC_INTEL_ACE40 +#elif CONFIG_SOC_ACE30 || CONFIG_SOC_ACE40 mod_cfg = [0, 0, 0, 0, 480, 6993000, 384, 384, 0, 6993, 0, 1, 0, 0, 0, 480, 6385000, 192, 192, 0, 6385, 0, 2, 0, 0, 0, 480, 10887000, 720, 720, 0, 10887, 0, @@ -71,7 +71,7 @@ 2, 0, 0, 0, 416, 7882000, 512, 512, 0, 7882, 0, 3, 0, 0, 0, 416, 5170000, 128, 128, 0, 5170, 0, 4, 0, 0, 0, 416, 5908000, 768, 768, 0, 0, 0] -#elif CONFIG_SOC_INTEL_ACE30 || CONFIG_SOC_INTEL_ACE40 +#elif CONFIG_SOC_ACE30 || CONFIG_SOC_ACE40 mod_cfg = [0, 0, 0, 0, 416, 11865000, 1536, 1536, 0, 11865, 0, 1, 0, 0, 0, 416, 7797000, 384, 384, 1, 7797, 1, 2, 0, 0, 0, 416, 12083000, 512, 512, 2, 12083, 2, diff --git a/src/audio/volume/volume_ipc3.c b/src/audio/volume/volume_ipc3.c index e83ebf25cff1..99a9a9c57ce1 100644 --- a/src/audio/volume/volume_ipc3.c +++ b/src/audio/volume/volume_ipc3.c @@ -14,7 +14,6 @@ #include <sof/common.h> #include <rtos/panic.h> #include <sof/ipc/msg.h> -#include <rtos/alloc.h> #include <rtos/init.h> #include <sof/lib/cpu.h> #include <sof/lib/uuid.h> @@ -81,7 +80,7 @@ int volume_init(struct processing_module *mod) return -EINVAL; } - cd = rzalloc(SOF_MEM_FLAG_USER, sizeof(struct vol_data)); + cd = mod_zalloc(mod, sizeof(struct vol_data)); if (!cd) return -ENOMEM; @@ -89,9 +88,9 @@ int volume_init(struct processing_module *mod) * malloc memory to store current volume 4 times to ensure the address * is 8-byte aligned for multi-way xtensa intrinsic operations. */ - cd->vol = rmalloc(SOF_MEM_FLAG_USER, vol_size); + cd->vol = mod_alloc_align(mod, vol_size, SOF_FRAME_BYTE_ALIGN); if (!cd->vol) { - rfree(cd); + mod_free(mod, cd); comp_err(dev, "Failed to allocate %zu", vol_size); return -ENOMEM; } @@ -158,8 +157,8 @@ int volume_init(struct processing_module *mod) break; default: comp_err(dev, "invalid ramp type %d", vol->ramp); - rfree(cd); - rfree(cd->vol); + mod_free(mod, cd); + mod_free(mod, cd->vol); return -EINVAL; } @@ -168,7 +167,7 @@ int volume_init(struct processing_module *mod) return 0; } -void volume_peak_free(struct vol_data *cd) +void volume_peak_free(struct processing_module *mod) { } @@ -185,7 +184,7 @@ int volume_set_config(struct processing_module *mod, uint32_t config_id, int j; int ret = 0; - comp_dbg(dev, "volume_set_config()"); + comp_dbg(dev, "entry"); /* validate */ if (cdata->num_elems == 0 || cdata->num_elems > SOF_IPC_MAX_CHANNELS) { @@ -195,16 +194,16 @@ int volume_set_config(struct processing_module *mod, uint32_t config_id, switch (cdata->cmd) { case SOF_CTRL_CMD_VOLUME: - comp_dbg(dev, "volume_set_config(), SOF_CTRL_CMD_VOLUME, cdata->comp_id = %u", + comp_dbg(dev, "SOF_CTRL_CMD_VOLUME, cdata->comp_id = %u", cdata->comp_id); for (j = 0; j < cdata->num_elems; j++) { ch = cdata->chanv[j].channel; val = cdata->chanv[j].value; - comp_dbg(dev, "volume_set_config(), channel = %d, value = %u", + comp_dbg(dev, "channel = %d, value = %u", ch, val); if (ch >= SOF_IPC_MAX_CHANNELS) { - comp_err(dev, "volume_set_config(), illegal channel = %d", + comp_err(dev, "illegal channel = %d", ch); return -EINVAL; } @@ -223,15 +222,15 @@ int volume_set_config(struct processing_module *mod, uint32_t config_id, break; case SOF_CTRL_CMD_SWITCH: - comp_dbg(dev, "volume_set_config(), SOF_CTRL_CMD_SWITCH, cdata->comp_id = %u", + comp_dbg(dev, "SOF_CTRL_CMD_SWITCH, cdata->comp_id = %u", cdata->comp_id); for (j = 0; j < cdata->num_elems; j++) { ch = cdata->chanv[j].channel; val = cdata->chanv[j].value; - comp_dbg(dev, "volume_set_config(), channel = %d, value = %u", + comp_dbg(dev, "channel = %d, value = %u", ch, val); if (ch >= SOF_IPC_MAX_CHANNELS) { - comp_err(dev, "volume_set_config(), illegal channel = %d", + comp_err(dev, "illegal channel = %d", ch); return -EINVAL; } @@ -262,7 +261,7 @@ int volume_get_config(struct processing_module *mod, struct comp_dev *dev = mod->dev; int j; - comp_dbg(dev, "volume_get_config()"); + comp_dbg(dev, "entry"); /* validate */ if (cdata->num_elems == 0 || cdata->num_elems > SOF_IPC_MAX_CHANNELS) { @@ -276,7 +275,7 @@ int volume_get_config(struct processing_module *mod, for (j = 0; j < cdata->num_elems; j++) { cdata->chanv[j].channel = j; cdata->chanv[j].value = cd->tvolume[j]; - comp_dbg(dev, "volume_get_config(), channel = %u, value = %u", + comp_dbg(dev, "channel = %u, value = %u", cdata->chanv[j].channel, cdata->chanv[j].value); } @@ -285,7 +284,7 @@ int volume_get_config(struct processing_module *mod, for (j = 0; j < cdata->num_elems; j++) { cdata->chanv[j].channel = j; cdata->chanv[j].value = !cd->muted[j]; - comp_dbg(dev, "volume_get_config(), channel = %u, value = %u", + comp_dbg(dev, "channel = %u, value = %u", cdata->chanv[j].channel, cdata->chanv[j].value); } diff --git a/src/audio/volume/volume_ipc4.c b/src/audio/volume/volume_ipc4.c index 889008657e8e..57a5f427903e 100644 --- a/src/audio/volume/volume_ipc4.c +++ b/src/audio/volume/volume_ipc4.c @@ -14,7 +14,6 @@ #include <sof/common.h> #include <rtos/panic.h> #include <sof/ipc/msg.h> -#include <rtos/alloc.h> #include <rtos/init.h> #include <sof/lib/cpu.h> #include <sof/lib/uuid.h> @@ -128,7 +127,7 @@ int volume_init(struct processing_module *mod) return -EINVAL; } - cd = rzalloc(SOF_MEM_FLAG_USER, sizeof(struct vol_data)); + cd = mod_zalloc(mod, sizeof(struct vol_data)); if (!cd) return -ENOMEM; @@ -136,9 +135,9 @@ int volume_init(struct processing_module *mod) * malloc memory to store current volume 4 times to ensure the address * is 8-byte aligned for multi-way xtensa intrinsic operations. */ - cd->vol = rmalloc(SOF_MEM_FLAG_USER, vol_size); + cd->vol = mod_alloc_align(mod, vol_size, SOF_FRAME_BYTE_ALIGN); if (!cd->vol) { - rfree(cd); + mod_free(mod, cd); comp_err(dev, "Failed to allocate %d", vol_size); return -ENOMEM; } @@ -146,10 +145,10 @@ int volume_init(struct processing_module *mod) /* malloc memory to store temp peak volume 4 times to ensure the address * is 8-byte aligned for multi-way xtensa intrinsic operations. */ - cd->peak_vol = rzalloc(SOF_MEM_FLAG_USER, vol_size); + cd->peak_vol = mod_zalloc(mod, vol_size); if (!cd->peak_vol) { - rfree(cd->vol); - rfree(cd); + mod_free(mod, cd->vol); + mod_free(mod, cd); comp_err(dev, "Failed to allocate %d for peak_vol", vol_size); return -ENOMEM; } @@ -189,14 +188,15 @@ int volume_init(struct processing_module *mod) return 0; } -void volume_peak_free(struct vol_data *cd) +void volume_peak_free(struct processing_module *mod) { + struct vol_data *cd = module_get_private_data(mod); struct ipc4_peak_volume_regs regs; /* clear mailbox */ memset_s(®s, sizeof(regs), 0, sizeof(regs)); mailbox_sw_regs_write(cd->mailbox_offset, ®s, sizeof(regs)); - rfree(cd->peak_vol); + mod_free(mod, cd->peak_vol); } static int volume_set_volume(struct processing_module *mod, const uint8_t *data, int data_size) @@ -351,7 +351,7 @@ int volume_set_config(struct processing_module *mod, uint32_t config_id, struct comp_dev *dev = mod->dev; int ret; - comp_dbg(dev, "volume_set_config()"); + comp_dbg(dev, "entry"); ret = module_set_configuration(mod, config_id, pos, data_offset_size, fragment, fragment_size, response, response_size); @@ -413,7 +413,7 @@ static int volume_params(struct processing_module *mod) struct comp_buffer *sinkb, *sourceb; struct comp_dev *dev = mod->dev; - comp_dbg(dev, "volume_params()"); + comp_dbg(dev, "entry"); ipc4_base_module_cfg_to_stream_params(&mod->priv.cfg.base_cfg, params); diff --git a/src/debug/debug_stream/CMakeLists.txt b/src/debug/debug_stream/CMakeLists.txt index 4d353128a819..36de0d24a46d 100644 --- a/src/debug/debug_stream/CMakeLists.txt +++ b/src/debug/debug_stream/CMakeLists.txt @@ -3,3 +3,5 @@ add_local_sources_ifdef(CONFIG_SOF_DEBUG_STREAM_SLOT sof debug_stream_slot.c) add_local_sources_ifdef(CONFIG_SOF_DEBUG_STREAM_THREAD_INFO sof debug_stream_thread_info.c) + +add_local_sources_ifdef(CONFIG_SOF_DEBUG_STREAM_TEXT_MSG sof debug_stream_text_msg.c) diff --git a/src/debug/debug_stream/Kconfig b/src/debug/debug_stream/Kconfig index 3a5f3d1f49cf..6ac6280c611c 100644 --- a/src/debug/debug_stream/Kconfig +++ b/src/debug/debug_stream/Kconfig @@ -7,7 +7,8 @@ config SOF_DEBUG_STREAM_SLOT information from SOF system that are streamed from SOF DSP to the host side for decoding and presentation. This option enables transferring the records from DSP to host over a - debug window slot. + debug window slot. To extract the debugstream see + tools/debug_stream/debug_stream.py. if SOF_DEBUG_STREAM_SLOT @@ -44,4 +45,43 @@ config SOF_DEBUG_STREAM_THREAD_INFO_INTERVAL Decides how often thread info runs and checks execution cycle statistics and stack usage. +config SOF_DEBUG_STREAM_THREAD_INFO_TOTAL_CPU_LOAD_TO_LOG + bool "Print summarized total CPU load to log subsystem" + depends on SOF_DEBUG_STREAM_THREAD_INFO + default y + help + In addition to printing thread statistics to debug stream, + print the total CPU load (sum of all threads) to + the logging system. + +config SOF_DEBUG_STREAM_SLOT_FORCE_MAX_CPUS + int "Number of cpu sections slot is divided to" + default CORE_COUNT + range 1 MP_MAX_NUM_CPUS + help + In some situations a high number of cpu sections shrinks the + circular buffer size so much that it limit debugging. With + this option its possible to use fewer sections. The downside + is that the cpus above the number of sections can not send + any debug stream messages. + +config SOF_DEBUG_STREAM_TEXT_MSG + bool "Enable text message sending through Debug-Stream" + help + Enable debug message sending over debug stream. To use this + feature one only needs to enable debug stream with this + config option and print the debug messages with + ds_msg(). See include/user/debug_stream_text_msg.h for + prototype. + +config SOF_DEBUG_STREAM_TEXT_MSG_ASSERT_PRINT + bool "Enable assert print sending through Debug-Stream" + depends on EXCEPTION_DUMP_HOOK && (ASSERT || ASSERT_VERBOSE) + select SOF_DEBUG_STREAM_TEXT_MSG + help + Enable assert print sending over debug stream as text + message. This feature is also sensitive to Zephyr option + CONFIG_EXCEPTION_DUMP_HOOK_ONLY. If that is set then the + asserts are not printed through printk interface. + endif diff --git a/src/debug/debug_stream/README.md b/src/debug/debug_stream/README.md new file mode 100644 index 000000000000..592edef8a719 --- /dev/null +++ b/src/debug/debug_stream/README.md @@ -0,0 +1,61 @@ +# SOF Debug Stream + +The `debug_stream` framework is an abstract logging and live-data streaming mechanism allowing the DSP to asynchronously push structured or freeform diagnostic records immediately out to the host. + +## Feature Overview + +Unlike standard tracing (`mtrace`), which requires buffering and complex host parsing logic often tied directly to pipeline topologies or ALSA interfaces, the `debug_stream` bypasses the audio framework entirely. It utilizes the dedicated IPC Memory Windows (specifically the debug slot) to write data. + +The stream is particularly useful for reporting: + +1. **Thread Information:** Real-time data from Zephyr OS threads (like CPU runtime, context switch frequencies, or stack high-water marks). +2. **Text Messages (`ds_msg`):** Lightweight string prints that bypass the standard heavily-formatted logger. + +## How to Enable + +These features are disabled by default to save firmware footprint. You can enable them via Kconfig: + +* `CONFIG_SOF_DEBUG_STREAM_SLOT=y` : Master switch. Reserves exactly one Memory Window 4k block (default Slot 3) mapping to host space. +* `CONFIG_SOF_DEBUG_STREAM_THREAD_INFO=y` : Activates the Zephyr thread statistics compiler integration (`INIT_STACKS`, `THREAD_MONITOR`). +* `CONFIG_SOF_DEBUG_STREAM_TEXT_MSG=y` : Allows calling `ds_msg("...", ...)` scattered throughout DSP C code to emit plain strings. + +## Architecture + +The architecture revolves around a "Slot" abstraction where data is copied sequentially over a ringbuffer into the ADSP debug window slot used for the debug stream (mapped over PCIe/SPI for the Host to read non-destructively). + +```mermaid +graph TD + subgraph SOF Firmware + SysEvent["System Event / OS Timer"] --> |Triggers| DSThread["Thread Info Collector"] + DevCode["Developer Code"] --> |"ds_msg()"| Text["Text Subsystem"] + + DSThread --> Formatter[DS Formatter] + Text --> Formatter + + Formatter --> Slot[Memory Window Slot 3] + end + + subgraph Host System + PyTool[tools/debug_stream/debug_stream.py] + Slot -.->|PCIe DMA / IPC Memory| PyTool + PyTool --> |Stdout| User[Developer Terminal] + end +``` + +## Usage Example + +If you enable `CONFIG_SOF_DEBUG_STREAM_TEXT_MSG=y`, developers can insert rapid debug markers without setting up topology traces: + +```c +#include <user/debug_stream_text_msg.h> + +void my_function() { + ds_msg("Reached tricky initialization state! Value: %d", some_val); +} +``` + +On the host machine, you extract this continuous output stream by running the provided SOF tooling: + +```bash +python3 tools/debug_stream/debug_stream.py +``` diff --git a/src/debug/debug_stream/debug_stream_slot.c b/src/debug/debug_stream/debug_stream_slot.c index dbfde134b438..82816a6788f3 100644 --- a/src/debug/debug_stream/debug_stream_slot.c +++ b/src/debug/debug_stream/debug_stream_slot.c @@ -10,21 +10,37 @@ #include <rtos/string.h> #include <user/debug_stream.h> #include <user/debug_stream_slot.h> +#include <zephyr/kernel.h> LOG_MODULE_REGISTER(debug_stream_slot); struct cpu_mutex { - struct k_mutex m; + struct k_spinlock l; } __aligned(CONFIG_DCACHE_LINE_SIZE); /* CPU specific mutexes for each circular buffer */ -static struct cpu_mutex cpu_mutex[CONFIG_MP_MAX_NUM_CPUS]; +static struct cpu_mutex cpu_mutex[CONFIG_SOF_DEBUG_STREAM_SLOT_FORCE_MAX_CPUS]; +#ifdef CONFIG_INTEL_ADSP_DEBUG_SLOT_MANAGER +static struct debug_stream_slot_hdr *dbg_stream_slot; +#else static const int debug_stream_slot = CONFIG_SOF_DEBUG_STREAM_SLOT_NUMBER; +#endif static struct debug_stream_slot_hdr *debug_stream_get_slot(void) { +#ifdef CONFIG_INTEL_ADSP_DEBUG_SLOT_MANAGER + if (!dbg_stream_slot) { + struct adsp_dw_desc slot_desc = { .type = ADSP_DW_SLOT_DEBUG_STREAM, }; + + dbg_stream_slot = (struct debug_stream_slot_hdr *) + adsp_dw_request_slot(&slot_desc, NULL); + } + + return dbg_stream_slot; +#else return (struct debug_stream_slot_hdr *)ADSP_DW->slots[debug_stream_slot]; +#endif } static @@ -38,6 +54,12 @@ debug_stream_get_circular_buffer(struct debug_stream_section_descriptor *desc, u return NULL; } + if (core >= CONFIG_SOF_DEBUG_STREAM_SLOT_FORCE_MAX_CPUS) { + LOG_DBG("No section for cpu %u >= %u ", core, + CONFIG_SOF_DEBUG_STREAM_SLOT_FORCE_MAX_CPUS); + return NULL; + } + *desc = hdr->section_desc[core]; LOG_DBG("Section %u (desc %u %u %u)", core, desc->core_id, desc->buf_words, desc->offset); @@ -51,6 +73,7 @@ int debug_stream_slot_send_record(struct debug_stream_record *rec) debug_stream_get_circular_buffer(&desc, arch_proc_id()); uint32_t record_size = rec->size_words; uint32_t record_start, buf_remain; + k_spinlock_key_t key; LOG_DBG("Sending record %u id %u len %u", rec->seqno, rec->id, rec->size_words); @@ -62,7 +85,7 @@ int debug_stream_slot_send_record(struct debug_stream_record *rec) desc.buf_words, desc.core_id, desc.buf_words, desc.offset); return -ENOMEM; } - k_mutex_lock(&cpu_mutex[arch_proc_id()].m, K_FOREVER); + key = k_spin_lock(&cpu_mutex[arch_proc_id()].l); rec->seqno = buf->next_seqno++; rec->size_words = record_size + 1; /* +1 for size at the end of record */ @@ -90,7 +113,7 @@ int debug_stream_slot_send_record(struct debug_stream_record *rec) buf->data[buf->w_ptr] = record_size + 1; buf->w_ptr = (buf->w_ptr + 1) % desc.buf_words; - k_mutex_unlock(&cpu_mutex[arch_proc_id()].m); + k_spin_unlock(&cpu_mutex[arch_proc_id()].l, key); LOG_DBG("Record %u id %u len %u sent", rec->seqno, rec->id, record_size); return 0; @@ -99,56 +122,52 @@ int debug_stream_slot_send_record(struct debug_stream_record *rec) static int debug_stream_slot_init(void) { struct debug_stream_slot_hdr *hdr = debug_stream_get_slot(); - size_t hdr_size = ALIGN_UP(offsetof(struct debug_stream_slot_hdr, - section_desc[CONFIG_MP_MAX_NUM_CPUS]), - CONFIG_DCACHE_LINE_SIZE); + size_t hdr_size = ALIGN_UP( + offsetof(struct debug_stream_slot_hdr, + section_desc[CONFIG_SOF_DEBUG_STREAM_SLOT_FORCE_MAX_CPUS]), + CONFIG_DCACHE_LINE_SIZE); size_t section_area_size = ADSP_DW_SLOT_SIZE - hdr_size; size_t section_size = ALIGN_DOWN(section_area_size / - CONFIG_MP_MAX_NUM_CPUS, + CONFIG_SOF_DEBUG_STREAM_SLOT_FORCE_MAX_CPUS, CONFIG_DCACHE_LINE_SIZE); size_t offset = hdr_size; int i; LOG_INF("%u sections of %u bytes, hdr %u, section area %u", - CONFIG_MP_MAX_NUM_CPUS, section_size, hdr_size, + CONFIG_SOF_DEBUG_STREAM_SLOT_FORCE_MAX_CPUS, section_size, hdr_size, section_area_size); +#ifdef CONFIG_INTEL_ADSP_DEBUG_SLOT_MANAGER + if (!hdr) + return -ENOMEM; +#else if (ADSP_DW->descs[debug_stream_slot].type != 0) LOG_WRN("Slot %d was not free: %u", debug_stream_slot, ADSP_DW->descs[debug_stream_slot].type); ADSP_DW->descs[debug_stream_slot].type = ADSP_DW_SLOT_DEBUG_STREAM; +#endif hdr->hdr.magic = DEBUG_STREAM_IDENTIFIER; hdr->hdr.hdr_size = hdr_size; - hdr->total_size = hdr_size + CONFIG_MP_MAX_NUM_CPUS * section_size; - hdr->num_sections = CONFIG_MP_MAX_NUM_CPUS; - for (i = 0; i < CONFIG_MP_MAX_NUM_CPUS; i++) { + hdr->total_size = hdr_size + CONFIG_SOF_DEBUG_STREAM_SLOT_FORCE_MAX_CPUS * section_size; + hdr->num_sections = CONFIG_SOF_DEBUG_STREAM_SLOT_FORCE_MAX_CPUS; + for (i = 0; i < CONFIG_SOF_DEBUG_STREAM_SLOT_FORCE_MAX_CPUS; i++) { hdr->section_desc[i].core_id = i; hdr->section_desc[i].buf_words = - (section_size - offsetof(struct debug_stream_circular_buf, data[0]))/ + (section_size - offsetof(struct debug_stream_circular_buf, data[0])) / sizeof(uint32_t); hdr->section_desc[i].offset = offset; - LOG_INF("sections %u, size %u, offset %u", - i, section_size, offset); + LOG_DBG("sections %u, size %u, offset %u", i, section_size, offset); offset += section_size; } - for (i = 0; i < CONFIG_MP_MAX_NUM_CPUS; i++) { + for (i = 0; i < CONFIG_SOF_DEBUG_STREAM_SLOT_FORCE_MAX_CPUS; i++) { struct debug_stream_section_descriptor desc = { 0 }; - struct debug_stream_circular_buf *buf = - debug_stream_get_circular_buffer(&desc, i); + struct debug_stream_circular_buf *buf = debug_stream_get_circular_buffer(&desc, i); buf->next_seqno = 0; buf->w_ptr = 0; - k_mutex_init(&cpu_mutex[i].m); - /* The core specific mutexes are now .bss which is uncached so the - * following line is commented out. However, since the mutexes are - * core specific there should be nothing preventing from having them - * in cached memory. - * - * sys_cache_data_flush_range(&cpu_mutex[i], sizeof(cpu_mutex[i])); - */ } - LOG_INF("Debug stream slot initialized"); + LOG_DBG("Debug stream slot initialized"); return 0; } diff --git a/src/debug/debug_stream/debug_stream_text_msg.c b/src/debug/debug_stream/debug_stream_text_msg.c new file mode 100644 index 000000000000..97db0fd29330 --- /dev/null +++ b/src/debug/debug_stream/debug_stream_text_msg.c @@ -0,0 +1,183 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// Copyright(c) 2024 Intel Corporation. + +#include <sys/types.h> +#include <stdarg.h> +#include <stdio.h> +#include <soc.h> +#include <adsp_debug_window.h> +#include <sof/common.h> +#include <zephyr/logging/log.h> +#include <zephyr/arch/exception.h> + +#include <user/debug_stream_text_msg.h> + +LOG_MODULE_REGISTER(debug_stream_text_msg); + +void ds_vamsg(const char *format, va_list args) +{ + struct { + struct debug_stream_text_msg msg; + char text[128]; + } __packed buf = { 0 }; + ssize_t len; + + len = vsnprintf(buf.text, sizeof(buf.text), format, args); + + if (len < 0) + return; + len = MIN(len, sizeof(buf.text)); + + buf.msg.hdr.id = DEBUG_STREAM_RECORD_ID_TEXT_MSG; + buf.msg.hdr.size_words = SOF_DIV_ROUND_UP(sizeof(buf.msg) + len, + sizeof(buf.msg.hdr.data[0])); + debug_stream_slot_send_record(&buf.msg.hdr); +} + +void ds_msg(const char *format, ...) +{ + va_list args; + + va_start(args, format); + ds_vamsg(format, args); + va_end(args); +} + +#if defined(CONFIG_EXCEPTION_DUMP_HOOK) +/* The debug stream debug window slot is 4k, and when it is split + * between the cores and the header/other overhead is removed, with 5 + * cores the size is 768 bytes. The dump record must be smaller than + * that to get through to the host side. + * + * Also, because of the limited circular buffer size, we should only + * send one exception record. On some situations the exceptions happen + * in bursts, and sending more than one record in short time makes the + * host-side decoder lose track of things. + */ + +/* Per-CPU state for exception dump and assert_print(). Static data is + * currently placed in .bss and its ATM uncached so the ds_cpu table + * elements do not need to be cache aligned, but if this changes we + * need __aligned(CONFIG_DCACHE_LINE_SIZE) here. + */ +static struct ds_cpu_state { + struct { + struct debug_stream_text_msg msg; + char text[640]; + } __packed buf; + int reports_sent; + size_t pos; +} ds_cpu[CONFIG_SOF_DEBUG_STREAM_SLOT_FORCE_MAX_CPUS]; + +static void ds_exception_drain(bool flush) +{ + unsigned int cpu = arch_proc_id(); + struct ds_cpu_state *cs = &ds_cpu[cpu]; + + if (flush) { + cs->pos = 0; + return; + } + + if (cs->pos == 0) + return; + + if (cs->reports_sent > 0) + return; + + cs->buf.msg.hdr.id = DEBUG_STREAM_RECORD_ID_TEXT_MSG; + cs->buf.msg.hdr.size_words = + SOF_DIV_ROUND_UP(sizeof(cs->buf.msg) + cs->pos, + sizeof(cs->buf.msg.hdr.data[0])); + + /* Make sure the possible up to 3 extra bytes at end of msg are '\0' */ + memset(cs->buf.text + cs->pos, 0, + cs->buf.msg.hdr.size_words * sizeof(cs->buf.msg.hdr.data[0]) - cs->pos); + debug_stream_slot_send_record(&cs->buf.msg.hdr); + cs->reports_sent = 1; + cs->pos = 0; +} + +static void ds_exception_dump(const char *format, va_list args) +{ + ssize_t len; + size_t avail; + size_t written; + unsigned int cpu = arch_proc_id(); + struct ds_cpu_state *cs = &ds_cpu[cpu]; + + if (cs->reports_sent > 0) + return; + + avail = sizeof(cs->buf.text) - cs->pos; + if (avail == 0) { + ds_exception_drain(false); + return; + } + + if (format[0] == '\0') + return; + + /* Skip useless " ** " prefix to save bytes */ + if (strlen(format) >= 4 && + format[0] == ' ' && format[1] == '*' && format[2] == '*' && format[3] == ' ') + format += 4; + + len = vsnprintf(cs->buf.text + cs->pos, avail, format, args); + if (len < 0) { + cs->pos = 0; + return; + } + + if (len == 0) + return; + + if ((size_t)len >= avail) + written = avail - 1; + else + written = (size_t)len; + + cs->pos += written; + + if (cs->pos >= sizeof(cs->buf.text)) + ds_exception_drain(false); +} + +static int init_exception_dump_hook(void) +{ + arch_exception_set_dump_hook(ds_exception_dump, ds_exception_drain); + LOG_DBG("exception_dump_hook set"); + return 0; +} + +SYS_INIT(init_exception_dump_hook, APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT); + +#if defined(CONFIG_SOF_DEBUG_STREAM_TEXT_MSG_ASSERT_PRINT) +void assert_print(const char *fmt, ...) +{ + va_list ap; + + /* Do not print assert after exception has been dumped */ + if (ds_cpu[arch_proc_id()].reports_sent > 0) + return; + + va_start(ap, fmt); +#if !defined(CONFIG_EXCEPTION_DUMP_HOOK_ONLY) + { + va_list ap2; + + va_copy(ap2, ap); +#endif + ds_vamsg(fmt, ap); +#if !defined(CONFIG_EXCEPTION_DUMP_HOOK_ONLY) + vprintk(fmt, ap2); + va_end(ap2); + } +#endif + ds_vamsg(fmt, ap); + va_end(ap); +} +EXPORT_SYMBOL(assert_print); +#endif +#endif diff --git a/src/debug/debug_stream/debug_stream_thread_info.c b/src/debug/debug_stream/debug_stream_thread_info.c index 4b47df638158..5c0d1bd7ffb1 100644 --- a/src/debug/debug_stream/debug_stream_thread_info.c +++ b/src/debug/debug_stream/debug_stream_thread_info.c @@ -22,6 +22,9 @@ LOG_MODULE_REGISTER(thread_info); /* Data structure to store the cycle counter values from the previous * round. The numbers are used to calculate what the load was on this * round. + * Static data is currently placed in .bss and its ATM uncached so the + * ds_cpu table elements do not need to be cache aligned, but if this + * changes we need __aligned(CONFIG_DCACHE_LINE_SIZE) here. */ static struct previous_counters { /* Cached data from previous round */ uint64_t active; /* All execution cycles */ @@ -30,7 +33,7 @@ static struct previous_counters { /* Cached data from previous round */ void *tid; /* thread ID (the thread struct ptr) */ uint64_t cycles; /* cycle counter value */ } threads[THREAD_INFO_MAX_THREADS]; /* The max amount of threads we follow */ -} __aligned(CONFIG_DCACHE_LINE_SIZE) previous[CONFIG_MP_MAX_NUM_CPUS]; +} previous[CONFIG_SOF_DEBUG_STREAM_SLOT_FORCE_MAX_CPUS]; #endif /* @@ -225,6 +228,13 @@ static void thread_info_cb(const struct k_thread *cthread, void *user_data) tinfo->name, tinfo->stack_usage * 100U / 255, tinfo->cpu_usage * 100U / 255); + /* .is_idle depends on CONFIG_SMP */ +#if defined(CONFIG_SOF_DEBUG_STREAM_THREAD_INFO_TOTAL_CPU_LOAD_TO_LOG) && defined(CONFIG_SMP) + if (thread->base.is_idle) + LOG_INF("core %u utilization %u%%", ud->core, + 100U - tinfo->cpu_usage * 100U / 255); +#endif + ud->thread_count++; } @@ -334,9 +344,10 @@ static void thread_info_run(void *cnum, void *a, void *b) } #define THREAD_STACK_SIZE (2048) -static K_THREAD_STACK_ARRAY_DEFINE(info_thread_stacks, CONFIG_MP_MAX_NUM_CPUS, +static K_THREAD_STACK_ARRAY_DEFINE(info_thread_stacks, + CONFIG_SOF_DEBUG_STREAM_SLOT_FORCE_MAX_CPUS, THREAD_STACK_SIZE); -static struct k_thread info_thread[CONFIG_MP_MAX_NUM_CPUS]; +static struct k_thread info_thread[CONFIG_SOF_DEBUG_STREAM_SLOT_FORCE_MAX_CPUS]; static int thread_info_start(void) { @@ -356,12 +367,14 @@ static int thread_info_start(void) LOG_ERR("k_thread_create() failed for core %u", i); continue; } +#ifdef CONFIG_SCHED_CPU_MASK ret = k_thread_cpu_pin(tid, i); if (ret < 0) { LOG_ERR("Pinning thread to code core %u", i); k_thread_abort(tid); continue; } +#endif snprintf(name, sizeof(name), "%u thread info", i); ret = k_thread_name_set(tid, name); if (ret < 0) diff --git a/src/debug/gdb/README.md b/src/debug/gdb/README.md new file mode 100644 index 000000000000..33b6a31915c1 --- /dev/null +++ b/src/debug/gdb/README.md @@ -0,0 +1,65 @@ +# GDB Remote Debugging Stub + +The Sound Open Firmware (SOF) project carries a GNU Debugger (GDB) stub directly integrated with the framework's exception handlers. This translates commands sent by a GDB client (running on the host Linux machine) into architecture-specific logic. + +## Feature Overview + +Instead of completely relying on complex JTAG setups, developers can use this stub to dynamically introspect panic states, stack traces, and variable states during firmware execution, particularly inside isolated SoC DSP cores. + +When the firmware faults or hits a defined breakpoint, the exception vector routes control into this stub. It then waits for GDB Remote Protocol packet streams (ASCII formatted over the SOF mailbox/shared memory window). The Host reads these mailbox slots and pushes/pulls responses to its active GNU Debugger session. + +## Architecture + +Data moves between the Host GDB environment, the physical mailboxes bounding the DSP domain, the DSP firmware's built-in stub, and the active exception state. + +```mermaid +sequenceDiagram + participant GdbSession as Host GDB Client + participant IPC as SOF Driver / ALSA + participant Stub as Firmware GDB Stub (gdb_parser) + participant HW as DSP Context Registers + + HW-->>Stub: Hard Fault / Breakpoint Hit + activate Stub + + Note over Stub: Stores fault context (sregs/aregs) + + Stub-->>IPC: Write Mailbox: Breakpoint Notification + + GdbSession->>IPC: Send Packet (e.g., $g#67 to Read Regs) + IPC->>Stub: Passes 'g' string + + Stub->>HW: Reads requested register values + HW-->>Stub: Values + + Stub->>Stub: mem_to_hex() formatting + Stub-->>GdbSession: Returns hex payload via Mailbox + + GdbSession->>IPC: Send Packet ($c#63 to Continue) + IPC->>Stub: Process 'c' + Stub->>HW: Restores Context, Retains Execution + deactivate Stub +``` + +## How to Enable + +A basic GDB debugging configuration is exposed via Kconfig and must be explicitly bound: + +* `CONFIG_GDB_DEBUG`: Needs to be toggled `=y` to compile `src/debug/gdb/gdb.c` into the main application. + +Additionally, the overarching architecture requires the corresponding Exception vectors to be rewritten. In Zephyr OS based builds (which currently drive native architectures), fatal exception handling must be configured to pass register dumps recursively to `gdb_handle_exception()`. + +## Usage and Protocols + +The protocol adheres precisely to the standard GDB remote serial specification. Each string packet expects the format: + +`$<packet-data>#<check-sum>` + +Supported Command Handlers inside the Stub: + +* `g` (Read all registers) / `G` (Write all registers) +* `m` (Read memory) / `M` (Write memory) +* `p` (Read specific register) / `P` (Write specific register) +* `v` (Query architecture/support details like `vCont`) +* `c` / `s` (Continue execution / Single-step) +* `z` / `Z` (Insert/Remove breakpoints) diff --git a/src/debug/telemetry/Kconfig b/src/debug/telemetry/Kconfig index 6e6a55d18f3c..f380a90e1808 100644 --- a/src/debug/telemetry/Kconfig +++ b/src/debug/telemetry/Kconfig @@ -3,6 +3,7 @@ config SOF_TELEMETRY bool "enable telemetry" default n + depends on !SOF_USERSPACE_LL help Enables telemetry. Enables performance measurements and debug utilities that use memory window 2 (debug window) as interface. Measurements include diff --git a/src/debug/telemetry/README.md b/src/debug/telemetry/README.md new file mode 100644 index 000000000000..19664ad2be8c --- /dev/null +++ b/src/debug/telemetry/README.md @@ -0,0 +1,48 @@ +# Telemetry and Performance Measurements + +The SOF Telemetry subsystem is a suite of built-in diagnostics measuring code execution efficiencies, cycle overheads, and hardware I/O throughput. + +## Feature Overview + +Latency and real-time execution bounds are critical in DSP firmware. The telemetry feature provides mechanisms to monitor these bounds accurately without intrusive breakpoints or slowing down the pipeline too aggressively. + +Capabilities include: + +1. **Component Performance Tracking**: For every instantiated component in the graph, it measures the pure execution time bounds (min/max/average) of that component's `comp_copy()` routines. +2. **I/O Throughput Tracking**: Measures hardware bus speeds or message handling by counting bytes, state changes, or tokens across distinct interfaces: IPC, DMIC, I2S, HD/A, I2C, SPI, etc. +3. **Zephyr Systick Measurement**: Specifically tracks the overall scheduler overhead bounding RTOS ticks. + +Measurements are batched into a ringbuffer locally, then synced across mapped ADSP memory windows into user space, limiting the impact on the active instruction cache. + +## Architecture + +The architecture bridges the component layer (like pure IPC or Audio Component wrappers) directly into independent statistics accumulators. + +```mermaid +graph TD + subgraph DSP Environment + Comp[Audio Component X] --> |"comp_copy() Execution"| Telemetry[perf_measure_execute] + HW[I2S / DMIC HW Driver] --> |"State Change Count"| Telemetry + + Telemetry --> RingBuffer[Statistics Ringbuffer] + RingBuffer --> Sync["Memory Window 3 (perf_data_sync)"] + end + + subgraph Host Userspace + Dev[sof-logger / IPC Tooling] --> |Reads/Queries| Sync + end +``` + +## How to Enable + +Telemetry depends strictly on NOT being built inside a host-userspace environment simulator (`depends on !SOF_USERSPACE_LL`). Ensure your target is a physical or emulated DSP target. + +Settings to configure in `Kconfig`: + +* `CONFIG_SOF_TELEMETRY=y` : Enable the overarching telemetry interfaces, giving you systick and basic task metrics over Memory Window 2 interfaces. +* `CONFIG_SOF_TELEMETRY_PERFORMANCE_MEASUREMENTS=y` : Adds granular tracking to audio components (creating the explicit `telemetry.c` ringbuffer maps via Memory Window 3 slots). Be aware that only a specific configured amount (`PERFORMANCE_DATA_ENTRIES_COUNT`) can be actively tracked due to RAM constraints. +* `CONFIG_SOF_TELEMETRY_IO_PERFORMANCE_MEASUREMENTS=y` : Instructs hardware and communication buses to start pumping data into the metrics collector. + +## Extracting Data + +You can fetch these metrics via `sof-logger` or standard IPC interrogation tools that support polling the corresponding debug window slots mapped for your particular platform's `ADSP_MW`. diff --git a/src/debug/telemetry/performance_monitor.c b/src/debug/telemetry/performance_monitor.c index dc5e80c7c253..b7aedc5ffa7c 100644 --- a/src/debug/telemetry/performance_monitor.c +++ b/src/debug/telemetry/performance_monitor.c @@ -128,7 +128,7 @@ static bool perf_bitmap_is_bit_clear(struct perf_bitmap * const bitmap, size_t b struct perf_data_item_comp *perf_data_getnext(void) { - int idx; + size_t idx; int ret = perf_bitmap_alloc(&performance_data_bitmap, &idx); if (ret < 0) @@ -138,8 +138,10 @@ struct perf_data_item_comp *perf_data_getnext(void) * ,and always set bit on bitmap alloc. */ ret = perf_bitmap_setbit(&performance_data_bitmap, idx); - if (ret < 0) + if (ret < 0) { + perf_bitmap_free(&performance_data_bitmap, idx); return NULL; + } return &perf_data[idx]; } @@ -211,10 +213,17 @@ int get_performance_data(struct global_perf_data * const global_perf_data) size_t slots_count; size_t slot_idx = 0; +#ifdef CONFIG_INTEL_ADSP_DEBUG_SLOT_MANAGER + struct system_tick_info *systick_info = telemetry_get_systick_info_ptr(); + + if (!systick_info) + return 0; +#else struct telemetry_wnd_data *wnd_data = (struct telemetry_wnd_data *)ADSP_DW->slots[SOF_DW_TELEMETRY_SLOT]; struct system_tick_info *systick_info = (struct system_tick_info *)wnd_data->system_tick_info; +#endif /* Fill one performance record with performance stats per core */ for (int core_id = 0; core_id < CONFIG_MAX_CORE_COUNT; ++core_id) { @@ -337,7 +346,7 @@ int enable_performance_counters(void) desc = basefw_vendor_get_manifest(); } else { #if CONFIG_LIBRARY_MANAGER - desc = (struct sof_man_fw_desc *)lib_manager_get_library_manifest(lib_id); + desc = lib_manager_get_library_manifest(LIB_MANAGER_PACK_LIB_ID(lib_id)); #else desc = NULL; #endif @@ -369,10 +378,17 @@ int reset_performance_counters(void) if (perf_measurements_state == IPC4_PERF_MEASUREMENTS_DISABLED) return -EINVAL; +#ifdef CONFIG_INTEL_ADSP_DEBUG_SLOT_MANAGER + struct system_tick_info *systick_info = telemetry_get_systick_info_ptr(); + + if (!systick_info) + return 0; +#else struct telemetry_wnd_data *wnd_data = (struct telemetry_wnd_data *)ADSP_DW->slots[SOF_DW_TELEMETRY_SLOT]; struct system_tick_info *systick_info = (struct system_tick_info *)wnd_data->system_tick_info; +#endif for (int core_id = 0; core_id < CONFIG_MAX_CORE_COUNT; ++core_id) { if (!(cpu_enabled_cores() & BIT(core_id))) @@ -431,24 +447,30 @@ int io_perf_monitor_init(void) static struct io_perf_data_item *io_perf_monitor_get_next_slot(struct io_perf_monitor_ctx *self) { - int idx; + size_t idx; int ret; k_spinlock_key_t key = k_spin_lock(&self->lock); ret = perf_bitmap_alloc(&self->io_performance_data_bitmap, &idx); if (ret < 0) - return NULL; + goto out_unlock; /* ref. FW did not set the bits, but here we do it to not have to use * isFree() check that the bitarray does not provide yet. Instead we will use isClear * ,and always set bit on bitmap alloc. */ ret = perf_bitmap_setbit(&self->io_performance_data_bitmap, idx); - if (ret < 0) - return NULL; + if (ret < 0) { + perf_bitmap_free(&self->io_performance_data_bitmap, idx); + goto out_unlock; + } k_spin_unlock(&self->lock, key); return &self->io_perf_data[idx]; + +out_unlock: + k_spin_unlock(&self->lock, key); + return NULL; } int io_perf_monitor_release_slot(struct io_perf_data_item *item) diff --git a/src/debug/telemetry/telemetry.c b/src/debug/telemetry/telemetry.c index 3cbd28774543..1e4c522dfee0 100644 --- a/src/debug/telemetry/telemetry.c +++ b/src/debug/telemetry/telemetry.c @@ -27,6 +27,10 @@ LOG_MODULE_DECLARE(ipc, CONFIG_SOF_LOG_LEVEL); /* Systic variables, one set per core */ static int telemetry_systick_counter[CONFIG_MAX_CORE_COUNT]; +#ifdef CONFIG_INTEL_ADSP_DEBUG_SLOT_MANAGER +static struct telemetry_wnd_data *wnd_data; +#endif + #ifdef CONFIG_SOF_TELEMETRY_PERFORMANCE_MEASUREMENTS static int telemetry_prev_ccount[CONFIG_MAX_CORE_COUNT]; static int telemetry_perf_period_sum[CONFIG_MAX_CORE_COUNT]; @@ -68,19 +72,45 @@ static size_t telemetry_perf_queue_avg(struct telemetry_perf_queue *q) } #endif +#ifdef CONFIG_INTEL_ADSP_DEBUG_SLOT_MANAGER +struct system_tick_info *telemetry_get_systick_info_ptr(void) +{ + if (!wnd_data) + return NULL; + + return (struct system_tick_info *)wnd_data->system_tick_info; +} +#endif + int telemetry_init(void) { /* systick_init */ +#ifdef CONFIG_INTEL_ADSP_DEBUG_SLOT_MANAGER + struct adsp_dw_desc slot_desc = { .type = ADSP_DW_SLOT_TELEMETRY, }; + struct system_tick_info *systick_info; + + if (wnd_data) + return 0; + + wnd_data = (struct telemetry_wnd_data *)adsp_dw_request_slot(&slot_desc, + NULL); + if (!wnd_data) + return -ENOMEM; + + systick_info = (struct system_tick_info *)wnd_data->system_tick_info; +#else uint8_t slot_num = SOF_DW_TELEMETRY_SLOT; volatile struct adsp_debug_window *window = ADSP_DW; struct telemetry_wnd_data *wnd_data = (struct telemetry_wnd_data *)ADSP_DW->slots[slot_num]; struct system_tick_info *systick_info = (struct system_tick_info *)wnd_data->system_tick_info; - tr_info(&ipc_tr, "Telemetry enabled. May affect performance"); - window->descs[slot_num].type = ADSP_DW_SLOT_TELEMETRY; window->descs[slot_num].resource_id = 0; +#endif + + tr_info(&ipc_tr, "Telemetry enabled. May affect performance"); + wnd_data->separator_1 = 0x0000C0DE; /* Zero values per core */ @@ -101,13 +131,19 @@ int telemetry_init(void) void telemetry_update(uint32_t begin_stamp, uint32_t current_stamp) { int prid = cpu_get_id(); +#ifdef CONFIG_INTEL_ADSP_DEBUG_SLOT_MANAGER + struct system_tick_info *systick_info = telemetry_get_systick_info_ptr(); - ++telemetry_systick_counter[prid]; - + if (!systick_info) + return; +#else struct telemetry_wnd_data *wnd_data = (struct telemetry_wnd_data *)ADSP_DW->slots[SOF_DW_TELEMETRY_SLOT]; struct system_tick_info *systick_info = (struct system_tick_info *)wnd_data->system_tick_info; +#endif + + ++telemetry_systick_counter[prid]; systick_info[prid].count = telemetry_systick_counter[prid]; systick_info[prid].last_time_elapsed = current_stamp - begin_stamp; diff --git a/src/debug/tester/CMakeLists.txt b/src/debug/tester/CMakeLists.txt index e207a0547f6d..85bf026b5ed3 100644 --- a/src/debug/tester/CMakeLists.txt +++ b/src/debug/tester/CMakeLists.txt @@ -14,7 +14,7 @@ if(zephyr) ### Zephyr ### add_subdirectory(llext ${PROJECT_BINARY_DIR}/tester_llext) add_dependencies(app tester) - elseif(CONFIG_COMP_TESTER) + elseif(CONFIG_COMP_TESTER STREQUAL "y") zephyr_library_sources(${base_files}) diff --git a/src/debug/tester/README.md b/src/debug/tester/README.md new file mode 100644 index 000000000000..bd558fd80a4d --- /dev/null +++ b/src/debug/tester/README.md @@ -0,0 +1,54 @@ +# Tester Component + +The `tester` directory implements a pseudo-component inside the SOF architecture. Instead of processing audio like an EQ or Mixer, it acts as a test execution harness designed specifically for Continuous Integration (CI) and automated system verifications. + +## Feature Overview + +Continuous integration often requires validating low-level system interactions that cannot be reached or easily triggered via standard ALSA driver behaviors. To overcome this, the `tester` component can be instantiated via Topologies or explicit IPC commands. Once bound, the host can send configuration blobs triggering embedded testing subroutines compiled directly into the firmware. + +Currently, it supports functionalities like: + +* **Dummy Operations**: Echoing parameters to verify full-stack serialization roundtrips. +* **DRAM Verification**: Stress-testing memory allocation arrays within the `LLB`/`L2` domains to detect caching bugs or out-of-bounds pointer crashes (`tester_simple_dram_test`). + +## Architecture + +The architecture functions identical to an IPC Version 4 standard module, containing `comp_driver`, `comp_dev`, `comp_buffer` wrappers and implementing `tester_params()` handlers. + +```mermaid +graph TD + subgraph Host Automation + Testbench[CI Testbench / ALSA Tool] + Testbench -->> |Topology Payload: CMD_TEST| IPC + end + + subgraph SOF Firmware + IPC[IPC4 Handler] -->> |ipc4_set_module_params| Tester[Tester Component] + + Tester -->> |Switch on Test Type| Dummy[tester_dummy_test.c] + Tester -->> |Switch on Test Type| DRAM[tester_simple_dram_test.c] + + Dummy -->> IPC + DRAM -->> |Runs Data Verifications| ExtMemory[SoC DRAM / Caches] + DRAM -->> IPC + end +``` + +## How to Enable + +Because the tester artificially exposes internal memory bounds limits and allows arbitrary firmware injection tests, it is locked out of standard release payloads. + +To compile it into your build, you must append: + +* `CONFIG_COMP_TESTER=y` + +**Constraints:** +It strictly `depends on IPC_MAJOR_4`. You cannot instantiate this tester under the legacy IPC3 streaming mechanisms. + +## Creating New Tests + +Developers diagnosing tricky hardware bugs can extend the tester by: + +1. Adding a new case to the test selection switch in `tester.c:tester_init()` (and, if needed, extending runtime handling in `tester_set_configuration()`). +2. Defining a new `test_runner()` C file implementing their data patterns. +3. Adding the target configurations in `tester.toml` to automatically sync with the module offset calculators (`llext_offset_calc`). diff --git a/src/debug/tester/tester.c b/src/debug/tester/tester.c index 869b7d9b2a7a..161b90a7d3f3 100644 --- a/src/debug/tester/tester.c +++ b/src/debug/tester/tester.c @@ -40,8 +40,6 @@ LOG_MODULE_REGISTER(tester, CONFIG_SOF_LOG_LEVEL); SOF_DEFINE_REG_UUID(tester); -DECLARE_TR_CTX(tester_tr, SOF_UUID(tester_uuid), LOG_LEVEL_INFO); - struct tester_init_config { struct ipc4_base_module_cfg ipc4_cfg; int32_t test_type; @@ -250,6 +248,7 @@ SOF_LLEXT_BUILDINFO; #else +DECLARE_TR_CTX(tester_tr, SOF_UUID(tester_uuid), LOG_LEVEL_INFO); DECLARE_MODULE_ADAPTER(tester_interface, tester_uuid, tester_tr); SOF_MODULE_INIT(tester, sys_comp_module_tester_interface_init); diff --git a/src/debug/tester/tester.toml b/src/debug/tester/tester.toml index a8c233166608..586bf2c3046b 100644 --- a/src/debug/tester/tester.toml +++ b/src/debug/tester/tester.toml @@ -45,8 +45,8 @@ 23, 0, 0, 0, 12832, 27696000, 180, 256, 0, 27696, 0, 24, 0, 0, 0, 12832, 18368000, 256, 512, 0, 18368, 0, 25, 0, 0, 0, 12832, 15204000, 128, 256, 0, 15204, 0] -#elif defined(CONFIG_LUNARLAKE) || defined(CONFIG_SOC_INTEL_ACE30) || \ - defined(CONFIG_SOC_INTEL_ACE40) +#elif defined(CONFIG_LUNARLAKE) || defined(CONFIG_SOC_ACE30) || \ + defined(CONFIG_SOC_ACE40) mod_cfg = [0, 0, 0, 0, 12832, 1365500, 0, 0, 0, 1365, 0, 1, 0, 0, 0, 12832, 2302300, 0, 0, 0, 2302, 0, 2, 0, 0, 0, 12832, 3218200, 0, 0, 0, 3218, 0, diff --git a/src/drivers/amd/common/acp_bt_dai.c b/src/drivers/amd/common/acp_bt_dai.c index 0cb751cbbfe8..9f5a79ce15f8 100644 --- a/src/drivers/amd/common/acp_bt_dai.c +++ b/src/drivers/amd/common/acp_bt_dai.c @@ -1,9 +1,10 @@ // SPDX-License-Identifier: BSD-3-Clause // -//Copyright(c) 2023 AMD. All rights reserved. +//Copyright(c) 2023, 2026 AMD. All rights reserved. // //Author: Basavaraj Hiregoudar <basavaraj.hiregoudar@amd.com> // SaiSurya, Ch <saisurya.chakkaveeravenkatanaga@amd.com> +// Sivasubramanian <sravisar@amd.com> #include <sof/audio/component.h> #include <sof/drivers/acp_dai_dma.h> @@ -79,8 +80,8 @@ const struct dai_driver acp_btdai_driver = { .type = SOF_DAI_AMD_BT, .uid = SOF_UUID(btdai_uuid), .tctx = &btdai_tr, - .dma_dev = DMA_DEV_BT, - .dma_caps = DMA_CAP_BT, + .dma_dev = SOF_DMA_DEV_BT, + .dma_caps = SOF_DMA_CAP_BT, .ops = { .trigger = btdai_trigger, .set_config = btdai_set_config, diff --git a/src/drivers/amd/common/acp_dmic_dai.c b/src/drivers/amd/common/acp_dmic_dai.c index f0c411141f03..18851843f005 100644 --- a/src/drivers/amd/common/acp_dmic_dai.c +++ b/src/drivers/amd/common/acp_dmic_dai.c @@ -1,9 +1,10 @@ // SPDX-License-Identifier: BSD-3-Clause // -//Copyright(c) 2023 AMD. All rights reserved. +//Copyright(c) 2023, 2026 AMD. All rights reserved. // // Author: Basavaraj Hiregoudar <basavaraj.hiregoudar@amd.com> // SaiSurya, Ch <saisurya.chakkaveeravenkatanaga@amd.com> +// Sivasubramanian <sravisar@amd.com> #include <sof/audio/component.h> #include <sof/drivers/acp_dai_dma.h> @@ -157,8 +158,8 @@ const struct dai_driver acp_dmic_dai_driver = { .type = SOF_DAI_AMD_DMIC, .uid = SOF_UUID(acp_dmic_dai_uuid), .tctx = &acp_dmic_dai_tr, - .dma_dev = DMA_DEV_DMIC, - .dma_caps = DMA_CAP_DMIC, + .dma_dev = SOF_DMA_DEV_DMIC, + .dma_caps = SOF_DMA_CAP_DMIC, .ops = { .trigger = acp_dmic_dai_trigger, .set_config = acp_dmic_dai_set_config, diff --git a/src/drivers/amd/common/ipc.c b/src/drivers/amd/common/ipc.c index 36ae6a7d17fb..fbb0ebdb1492 100644 --- a/src/drivers/amd/common/ipc.c +++ b/src/drivers/amd/common/ipc.c @@ -1,13 +1,12 @@ // SPDX-License-Identifier: BSD-3-Clause // -//Copyright(c) 2023 AMD. All rights reserved. +//Copyright(c) 2023, 2026 AMD. All rights reserved. // //Author: Basavaraj Hiregoudar <basavaraj.hiregoudar@amd.com> // SaiSurya, Ch <saisurya.chakkaveeravenkatanaga@amd.com> +// Sivasubramanian <sravisar@amd.com> #include <rtos/panic.h> -#include <platform/chip_offset_byte.h> -#include <platform/chip_registers.h> #include <rtos/interrupt.h> #include <sof/ipc/driver.h> #include <sof/ipc/msg.h> @@ -91,7 +90,7 @@ int platform_ipc_init(struct ipc *ipc) /* schedule */ schedule_task_init_edf(&ipc->ipc_task, SOF_UUID(ipc_task_amd_uuid), &ipc_task_ops, ipc, 0, 0); - arch_interrupt_clear(IRQ_NUM_EXT_LEVEL3); + interrupt_disable(IRQ_NUM_EXT_LEVEL3, ipc); interrupt_register(IRQ_NUM_EXT_LEVEL3, amd_irq_handler, ipc); /* Enabling software interuppts */ interrupt_enable(IRQ_NUM_EXT_LEVEL3, ipc); diff --git a/src/drivers/amd/rembrandt/acp_hs_dai.c b/src/drivers/amd/rembrandt/acp_hs_dai.c index 780454f52c87..bc1579d3f4d8 100644 --- a/src/drivers/amd/rembrandt/acp_hs_dai.c +++ b/src/drivers/amd/rembrandt/acp_hs_dai.c @@ -1,9 +1,10 @@ // SPDX-License-Identifier: BSD-3-Clause // -//Copyright(c) 2022 AMD. All rights reserved. +//Copyright(c) 2022, 2026 AMD. All rights reserved. // //Author: Basavaraj Hiregoudar <basavaraj.hiregoudar@amd.com> // Bala Kishore <balakishore.pati@amd.com> +// Sivasubramanian <sravisar@amd.com> #include <sof/audio/component.h> #include <sof/drivers/acp_dai_dma.h> @@ -60,7 +61,7 @@ static inline int hsdai_set_config(struct dai *dai, struct ipc_config_dai *commo i2stdm_mstrclkgen.bits.i2stdm_bclk_div_val = 0x20; break; default: - dai_err(dai, "hsdai_set_config unsupported slots"); + dai_err(dai, "unsupported slots"); return -EINVAL; } break; @@ -70,7 +71,7 @@ static inline int hsdai_set_config(struct dai *dai, struct ipc_config_dai *commo i2stdm_mstrclkgen.bits.i2stdm_bclk_div_val = 0x80; break; default: - dai_err(dai, "hsdai_set_config invalid format"); + dai_err(dai, "invalid format"); return -EINVAL; } @@ -109,7 +110,7 @@ static inline int hsdai_set_config(struct dai *dai, struct ipc_config_dai *commo io_reg_write((PU_REGISTER_BASE + ACP_HSTDM_IRER), hs_irer.u32all); break; default: - dai_err(dai, "hsdai_set_config invalid format"); + dai_err(dai, "invalid format"); return -EINVAL; } return 0; @@ -193,8 +194,8 @@ const struct dai_driver acp_hsdai_driver = { .type = SOF_DAI_AMD_HS, .uid = SOF_UUID(hsdai_uuid), .tctx = &hsdai_tr, - .dma_dev = DMA_DEV_SP, - .dma_caps = DMA_CAP_SP, + .dma_dev = SOF_DMA_DEV_SP, + .dma_caps = SOF_DMA_CAP_SP, .ops = { .trigger = hsdai_trigger, .set_config = hsdai_set_config, diff --git a/src/drivers/amd/rembrandt/acp_sp_dai.c b/src/drivers/amd/rembrandt/acp_sp_dai.c index 800fff08efcd..1dc5facc1533 100644 --- a/src/drivers/amd/rembrandt/acp_sp_dai.c +++ b/src/drivers/amd/rembrandt/acp_sp_dai.c @@ -1,9 +1,10 @@ // SPDX-License-Identifier: BSD-3-Clause // -//Copyright(c) 2022 AMD. All rights reserved. +//Copyright(c) 2022, 2026 AMD. All rights reserved. // //Author: Basavaraj Hiregoudar <basavaraj.hiregoudar@amd.com> // Bala Kishore <balakishore.pati@amd.com> +// Sivasubramanian <sravisar@amd.com> #include <sof/audio/component.h> #include <sof/drivers/acp_dai_dma.h> @@ -79,8 +80,8 @@ const struct dai_driver acp_spdai_driver = { .type = SOF_DAI_AMD_SP, .uid = SOF_UUID(spdai_uuid), .tctx = &spdai_tr, - .dma_dev = DMA_DEV_SP, - .dma_caps = DMA_CAP_SP, + .dma_dev = SOF_DMA_DEV_SP, + .dma_caps = SOF_DMA_CAP_SP, .ops = { .trigger = spdai_trigger, .set_config = spdai_set_config, diff --git a/src/drivers/amd/rembrandt/acp_sw_audio_dai.c b/src/drivers/amd/rembrandt/acp_sw_audio_dai.c index 1b31cffd59d8..bcee323a2303 100644 --- a/src/drivers/amd/rembrandt/acp_sw_audio_dai.c +++ b/src/drivers/amd/rembrandt/acp_sw_audio_dai.c @@ -1,9 +1,10 @@ // SPDX-License-Identifier: BSD-3-Clause // -//Copyright(c) 2023 AMD. All rights reserved. +//Copyright(c) 2023, 2026 AMD. All rights reserved. // //Author: Basavaraj Hiregoudar <basavaraj.hiregoudar@amd.com> // Maruthi Machani <maruthi.machani@amd.com> +// Sivasubramanian <sravisar@amd.com> #include <sof/drivers/acp_dai_dma.h> #include <sof/lib/uuid.h> @@ -51,7 +52,7 @@ static int swaudiodai_remove(struct dai *dai) { struct acp_pdata *acp = dai_get_drvdata(dai); - dai_info(dai, "swaudiodai_remove"); + dai_info(dai, "entry"); rfree(acp); dai_set_drvdata(dai, NULL); @@ -95,7 +96,7 @@ static int swaudiodai_get_hw_params(struct dai *dai, } const struct dai_driver acp_swaudiodai_driver = { - .type = SOF_DAI_AMD_SW_AUDIO, + .type = SOF_DAI_AMD_SDW, .uid = SOF_UUID(swaudiodai_uuid), .tctx = &swaudiodai_tr, .dma_dev = DMA_DEV_SW, diff --git a/src/drivers/amd/rembrandt/ipc.c b/src/drivers/amd/rembrandt/ipc.c index d32c80c34e3b..ffc13987bb13 100644 --- a/src/drivers/amd/rembrandt/ipc.c +++ b/src/drivers/amd/rembrandt/ipc.c @@ -1,13 +1,11 @@ // SPDX-License-Identifier: BSD-3-Clause // -//Copyright(c) 2022 AMD. All rights reserved. +//Copyright(c) 2022, 2026 AMD. All rights reserved. // //Author: Basavaraj Hiregoudar <basavaraj.hiregoudar@amd.com> // Bala Kishore <balakishore.pati@amd.com> - +// Sivasubramanian <sravisar@amd.com> #include <rtos/panic.h> -#include <platform/chip_offset_byte.h> -#include <platform/chip_registers.h> #include <rtos/interrupt.h> #include <sof/ipc/driver.h> #include <sof/ipc/msg.h> @@ -32,9 +30,39 @@ #include <platform/platform.h> #include <platform/ipc.h> +#define HOST_TO_DSP_INTR 1 +#define INTERRUPT_DISABLE 0 +LOG_MODULE_REGISTER(ipc1, CONFIG_SOF_LOG_LEVEL); volatile acp_scratch_mem_config_t *pscratch_mem_cfg = (volatile acp_scratch_mem_config_t *) (PU_SCRATCH_REG_BASE + SCRATCH_REG_OFFSET); +#ifdef CONFIG_ZEPHYR_NATIVE_DRIVERS +/* Clear the Acknowledge ( status) for the host to DSP interrupt */ +void acp_ack_intr_from_host(void) +{ + /* acknowledge the host interrupt */ + acp_dsp_sw_intr_stat_t sw_intr_stat; + + sw_intr_stat.u32all = 0; + sw_intr_stat.bits.host_to_dsp0_intr1_stat = INTERRUPT_ENABLE; + io_reg_write((PU_REGISTER_BASE + ACP_DSP_SW_INTR_STAT), sw_intr_stat.u32all); +} + +/* This function triggers a host interrupt from ACP DSP */ +void acp_dsp_to_host_intr_trig(void) +{ + acp_sw_intr_trig_t sw_intr_trig; + + /* Read the Software Interrupt controller register and update */ + sw_intr_trig = (acp_sw_intr_trig_t)io_reg_read(PU_REGISTER_BASE + + ACP_SW_INTR_TRIG); + /* Configures the trigger bit in ACP_DSP_SW_INTR_TRIG register */ + sw_intr_trig.bits.trig_dsp0_to_host_intr = INTERRUPT_ENABLE; + /* Write the Software Interrupt controller register */ + io_reg_write((PU_REGISTER_BASE + ACP_SW_INTR_TRIG), sw_intr_trig.u32all); +} +#endif /* CONFIG_ZEPHYR_NATIVE_DRIVERS */ + void amd_irq_handler(void *arg) { struct ipc *ipc = arg; diff --git a/src/drivers/amd/renoir/acp_sp_dai.c b/src/drivers/amd/renoir/acp_sp_dai.c index 0078b4d3a9d5..fbff87105ced 100644 --- a/src/drivers/amd/renoir/acp_sp_dai.c +++ b/src/drivers/amd/renoir/acp_sp_dai.c @@ -110,7 +110,7 @@ static int spdai_get_fifo(struct dai *dai, int direction, int stream_id) case DAI_DIR_CAPTURE: return dai_fifo(dai, direction); default: - dai_err(dai, "spdai_get_fifo(): Invalid direction"); + dai_err(dai, "Invalid direction"); return -EINVAL; } } diff --git a/src/drivers/amd/vangogh/acp_hs_dma.c b/src/drivers/amd/vangogh/acp_hs_dma.c index 361f74170932..b15a7f5e51b3 100644 --- a/src/drivers/amd/vangogh/acp_hs_dma.c +++ b/src/drivers/amd/vangogh/acp_hs_dma.c @@ -279,7 +279,7 @@ static int acp_dai_hs_dma_copy(struct dma_chan_data *channel, int bytes, .channel = channel, .elem.size = bytes, }; - tr_info(&acp_hs_tr, "acp_dai_hs_dma_copy "); + tr_info(&acp_hs_tr, "entry"); notifier_event(channel, NOTIFIER_ID_DMA_COPY, NOTIFIER_TARGET_CORE_LOCAL, &next, sizeof(next)); return 0; diff --git a/src/drivers/dw/dma.c b/src/drivers/dw/dma.c index 97ac6ddabbad..102cbbe0d7ba 100644 --- a/src/drivers/dw/dma.c +++ b/src/drivers/dw/dma.c @@ -254,7 +254,7 @@ static void dw_dma_channel_put(struct dma_chan_data *channel) { k_spinlock_key_t key; - tr_info(&dwdma_tr, "dw_dma_channel_put(): dma %d channel %d put", + tr_info(&dwdma_tr, "dma %d channel %d put", channel->dma->plat_data.id, channel->index); key = k_spin_lock(&channel->dma->lock); @@ -273,7 +273,7 @@ static int dw_dma_start(struct dma_chan_data *channel) uint32_t words_per_tfr = 0; #endif - tr_dbg(&dwdma_tr, "dw_dma_start(): dma %d channel %d start", + tr_dbg(&dwdma_tr, "dma %d channel %d start", channel->dma->plat_data.id, channel->index); irq_local_disable(flags); @@ -282,7 +282,7 @@ static int dw_dma_start(struct dma_chan_data *channel) if ((channel->status != COMP_STATE_PREPARE && channel->status != COMP_STATE_PAUSED) || (dma_reg_read(dma, DW_DMA_CHAN_EN) & DW_CHAN(channel->index))) { - tr_err(&dwdma_tr, "dw_dma_start(): dma %d channel %d not ready ena 0x%x status 0x%x", + tr_err(&dwdma_tr, "dma %d channel %d not ready ena 0x%x status 0x%x", dma->plat_data.id, channel->index, dma_reg_read(dma, DW_DMA_CHAN_EN), channel->status); @@ -292,7 +292,7 @@ static int dw_dma_start(struct dma_chan_data *channel) /* is valid stream */ if (!dw_chan->lli) { - tr_err(&dwdma_tr, "dw_dma_start(): dma %d channel %d invalid stream", + tr_err(&dwdma_tr, "dma %d channel %d invalid stream", dma->plat_data.id, channel->index); ret = -EINVAL; goto out; @@ -348,7 +348,7 @@ static int dw_dma_release(struct dma_chan_data *channel) struct dw_dma_chan_data *dw_chan = dma_chan_get_data(channel); uint32_t flags; - tr_info(&dwdma_tr, "dw_dma_release(): dma %d channel %d release", + tr_info(&dwdma_tr, "dma %d channel %d release", channel->dma->plat_data.id, channel->index); irq_local_disable(flags); @@ -370,7 +370,7 @@ static int dw_dma_pause(struct dma_chan_data *channel) struct dma *dma = channel->dma; uint32_t flags; - tr_info(&dwdma_tr, "dw_dma_pause(): dma %d channel %d pause", + tr_info(&dwdma_tr, "dma %d channel %d pause", channel->dma->plat_data.id, channel->index); irq_local_disable(flags); @@ -405,7 +405,7 @@ static int dw_dma_stop(struct dma_chan_data *channel) int i; #endif - tr_info(&dwdma_tr, "dw_dma_stop(): dma %d channel %d stop", + tr_info(&dwdma_tr, "dma %d channel %d stop", dma->plat_data.id, channel->index); irq_local_disable(flags); @@ -428,7 +428,7 @@ static int dw_dma_stop(struct dma_chan_data *channel) DW_CFGL_FIFO_EMPTY, DW_DMA_TIMEOUT); if (ret < 0) - tr_err(&dwdma_tr, "dw_dma_stop(): dma %d channel %d timeout", + tr_err(&dwdma_tr, "dma %d channel %d timeout", dma->plat_data.id, channel->index); #endif @@ -438,7 +438,7 @@ static int dw_dma_stop(struct dma_chan_data *channel) ret = poll_for_register_delay(dma_base(dma) + DW_DMA_CHAN_EN, DW_CHAN(channel->index), 0, DW_DMA_TIMEOUT); if (ret < 0) { - tr_err(&dwdma_tr, "dw_dma_stop(): dma %d channel %d disable timeout", + tr_err(&dwdma_tr, "dma %d channel %d disable timeout", dma->plat_data.id, channel->index); return -ETIMEDOUT; } @@ -499,7 +499,7 @@ static int dw_dma_set_config(struct dma_chan_data *channel, int ret = 0; int i; - tr_dbg(&dwdma_tr, "dw_dma_set_config(): dma %d channel %d config", + tr_dbg(&dwdma_tr, "dma %d channel %d config", channel->dma->plat_data.id, channel->index); irq_local_disable(flags); @@ -514,7 +514,7 @@ static int dw_dma_set_config(struct dma_chan_data *channel, dw_chan->cfg_hi = DW_CFG_HIGH_DEF; if (!config->elem_array.count) { - tr_err(&dwdma_tr, "dw_dma_set_config(): dma %d channel %d no elems", + tr_err(&dwdma_tr, "dma %d channel %d no elems", channel->dma->plat_data.id, channel->index); ret = -EINVAL; goto out; @@ -522,7 +522,7 @@ static int dw_dma_set_config(struct dma_chan_data *channel, if (config->irq_disabled && config->elem_array.count < DW_DMA_CFG_NO_IRQ_MIN_ELEMS) { - tr_err(&dwdma_tr, "dw_dma_set_config(): dma %d channel %d not enough elems for config with irq disabled %d", + tr_err(&dwdma_tr, "dma %d channel %d not enough elems for config with irq disabled %d", channel->dma->plat_data.id, channel->index, config->elem_array.count); ret = -EINVAL; @@ -548,7 +548,7 @@ static int dw_dma_set_config(struct dma_chan_data *channel, dw_chan->lli = rmalloc(SOF_MEM_FLAG_KERNEL | SOF_MEM_FLAG_COHERENT | SOF_MEM_FLAG_DMA, sizeof(struct dw_lli) * channel->desc_count); if (!dw_chan->lli) { - tr_err(&dwdma_tr, "dw_dma_set_config(): dma %d channel %d lli alloc failed", + tr_err(&dwdma_tr, "dma %d channel %d lli alloc failed", channel->dma->plat_data.id, channel->index); ret = -ENOMEM; @@ -600,7 +600,7 @@ static int dw_dma_set_config(struct dma_chan_data *channel, lli_desc->ctrl_lo |= DW_CTLL_SRC_WIDTH(2); break; default: - tr_err(&dwdma_tr, "dw_dma_set_config(): dma %d channel %d invalid src width %d", + tr_err(&dwdma_tr, "dma %d channel %d invalid src width %d", channel->dma->plat_data.id, channel->index, config->src_width); ret = -EINVAL; @@ -628,7 +628,7 @@ static int dw_dma_set_config(struct dma_chan_data *channel, lli_desc->ctrl_lo |= DW_CTLL_DST_WIDTH(2); break; default: - tr_err(&dwdma_tr, "dw_dma_set_config(): dma %d channel %d invalid dest width %d", + tr_err(&dwdma_tr, "dma %d channel %d invalid dest width %d", channel->dma->plat_data.id, channel->index, config->dest_width); ret = -EINVAL; @@ -706,7 +706,7 @@ static int dw_dma_set_config(struct dma_chan_data *channel, DW_CFGH_DST(config->dest_dev); break; default: - tr_err(&dwdma_tr, "dw_dma_set_config(): dma %d channel %d invalid direction %d", + tr_err(&dwdma_tr, "dma %d channel %d invalid direction %d", channel->dma->plat_data.id, channel->index, config->direction); ret = -EINVAL; @@ -717,7 +717,7 @@ static int dw_dma_set_config(struct dma_chan_data *channel, lli_desc->dar = sg_elem->dest; if (sg_elem->size > DW_CTLH_BLOCK_TS_MASK) { - tr_err(&dwdma_tr, "dw_dma_set_config(): dma %d channel %d block size too big %d", + tr_err(&dwdma_tr, "dma %d channel %d block size too big %d", channel->dma->plat_data.id, channel->index, sg_elem->size); ret = -EINVAL; @@ -839,7 +839,7 @@ static int dw_dma_copy(struct dma_chan_data *channel, int bytes, }; k_spinlock_key_t key; - tr_dbg(&dwdma_tr, "dw_dma_copy(): dma %d channel %d copy", + tr_dbg(&dwdma_tr, "dma %d channel %d copy", channel->dma->plat_data.id, channel->index); notifier_event(channel, NOTIFIER_ID_DMA_COPY, @@ -859,7 +859,7 @@ static int dw_dma_copy(struct dma_chan_data *channel, int bytes, DW_CHAN(channel->index), 0, DW_DMA_TIMEOUT); if (ret < 0) { - tr_dbg(&dwdma_tr, "dw_dma_copy(): poll_for_register_delay timeout"); + tr_dbg(&dwdma_tr, "poll_for_register_delay timeout"); return ret; } } @@ -888,7 +888,7 @@ static int dw_dma_setup(struct dma *dma) break; if (!i) { - tr_err(&dwdma_tr, "dw_dma_setup(): dma %d setup failed", + tr_err(&dwdma_tr, "dma %d setup failed", dma->plat_data.id); return -EIO; } @@ -940,7 +940,7 @@ static int dw_dma_probe(struct dma *dma) sizeof(struct dma_chan_data) * dma->plat_data.channels); if (!dma->chan) { - tr_err(&dwdma_tr, "dw_dma_probe(): dma %d allocaction of channels failed", + tr_err(&dwdma_tr, "dma %d allocation of channels failed", dma->plat_data.id); goto out; } @@ -961,7 +961,7 @@ static int dw_dma_probe(struct dma *dma) sizeof(*dw_chan)); if (!dw_chan) { - tr_err(&dwdma_tr, "dw_dma_probe(): dma %d allocaction of channel %d private data failed", + tr_err(&dwdma_tr, "dma %d allocation of channel %d private data failed", dma->plat_data.id, i); goto out; } @@ -989,7 +989,7 @@ static int dw_dma_remove(struct dma *dma) { int i; - tr_dbg(&dwdma_tr, "dw_dma_remove(): dma %d remove", dma->plat_data.id); + tr_dbg(&dwdma_tr, "dma %d remove", dma->plat_data.id); pm_runtime_put_sync(DW_DMAC_CLK, dma->plat_data.id); @@ -1024,7 +1024,7 @@ static int dw_dma_avail_data_size(struct dma_chan_data *channel) if (delta) size = dw_chan->ptr_data.buffer_bytes; else - tr_info(&dwdma_tr, "dw_dma_avail_data_size() size is 0!"); + tr_info(&dwdma_tr, "size is 0!"); } tr_dbg(&dwdma_tr, "DAR %x reader 0x%x free 0x%x avail 0x%x", write_ptr, @@ -1055,7 +1055,7 @@ static int dw_dma_free_data_size(struct dma_chan_data *channel) if (delta) size = dw_chan->ptr_data.buffer_bytes; else - tr_info(&dwdma_tr, "dw_dma_free_data_size() size is 0!"); + tr_info(&dwdma_tr, "size is 0!"); } tr_dbg(&dwdma_tr, "SAR %x writer 0x%x free 0x%x avail 0x%x", read_ptr, @@ -1071,7 +1071,7 @@ static int dw_dma_get_data_size(struct dma_chan_data *channel, k_spinlock_key_t key; int ret = 0; - tr_dbg(&dwdma_tr, "dw_dma_get_data_size(): dma %d channel %d get data size", + tr_dbg(&dwdma_tr, "dma %d channel %d get data size", channel->dma->plat_data.id, channel->index); key = k_spin_lock(&channel->dma->lock); @@ -1090,7 +1090,7 @@ static int dw_dma_get_data_size(struct dma_chan_data *channel, #if CONFIG_DMA_HW_LLI if (!(dma_reg_read(channel->dma, DW_DMA_CHAN_EN) & DW_CHAN(channel->index))) { - tr_err(&dwdma_tr, "dw_dma_get_data_size(): xrun detected"); + tr_err(&dwdma_tr, "xrun detected"); return -ENODATA; } #endif diff --git a/src/drivers/imx/edma.c b/src/drivers/imx/edma.c index f8e3fb1dcf7c..b21c58bcb02c 100644 --- a/src/drivers/imx/edma.c +++ b/src/drivers/imx/edma.c @@ -391,7 +391,7 @@ static int edma_set_config(struct dma_chan_data *channel, doff = config->dest_width; break; default: - tr_err(&edma_tr, "edma_set_config() unsupported config direction"); + tr_err(&edma_tr, "unsupported config direction"); return -EINVAL; } @@ -541,7 +541,7 @@ static int edma_get_data_size(struct dma_chan_data *channel, *avail = ABS(capture_data_size) / 2; break; default: - tr_err(&edma_tr, "edma_get_data_size() unsupported direction %d", + tr_err(&edma_tr, "unsupported direction %d", channel->direction); return -EINVAL; } diff --git a/src/drivers/imx/esai.c b/src/drivers/imx/esai.c index 4c62009e56bc..60af9bef9e5a 100644 --- a/src/drivers/imx/esai.c +++ b/src/drivers/imx/esai.c @@ -367,7 +367,7 @@ static int esai_remove(struct dai *dai) { struct esai_pdata *pdata = dai_get_drvdata(dai); - dai_info(dai, "esai_remove()"); + dai_info(dai, "entry"); rfree(pdata); dai_set_drvdata(dai, NULL); @@ -391,7 +391,7 @@ static int esai_get_fifo(struct dai *dai, int direction, int stream_id) case DAI_DIR_CAPTURE: return dai_fifo(dai, direction); /* stream_id is unused */ default: - dai_err(dai, "esai_get_fifo(): Invalid direction"); + dai_err(dai, "Invalid direction"); return -EINVAL; } } @@ -403,7 +403,7 @@ static int esai_get_fifo_depth(struct dai *dai, int direction) case DAI_DIR_CAPTURE: return dai->plat_data.fifo[direction].depth; default: - dai_err(dai, "esai_get_fifo_depth(): Invalid direction"); + dai_err(dai, "Invalid direction"); return -EINVAL; } } @@ -420,7 +420,7 @@ static int esai_get_hw_params(struct dai *dai, params->buffer_fmt = 0; params->frame_fmt = SOF_IPC_FRAME_S24_4LE; - dai_info(dai, "esai_get_hw_params(): params->rate = %d, fsync_rate = %d", + dai_info(dai, "params->rate = %d, fsync_rate = %d", params->rate, esai->params.fsync_rate); return 0; diff --git a/src/drivers/imx/interrupt-irqsteer.c b/src/drivers/imx/interrupt-irqsteer.c index da45b4f109ba..e42dce923bc9 100644 --- a/src/drivers/imx/interrupt-irqsteer.c +++ b/src/drivers/imx/interrupt-irqsteer.c @@ -348,7 +348,7 @@ static inline void irq_handler(void *data, uint32_t line_index) if (!--tries) { tries = IRQ_MAX_TRIES; - tr_err(&irq_i_tr, "irq_handler(): IRQ storm, status 0x%08x%08x", + tr_err(&irq_i_tr, "IRQ storm, status 0x%08x%08x", (uint32_t)(status >> 32), (uint32_t)status); } } diff --git a/src/drivers/imx/micfil.c b/src/drivers/imx/micfil.c index 5cbf5c3e292b..0946ca165c57 100644 --- a/src/drivers/imx/micfil.c +++ b/src/drivers/imx/micfil.c @@ -50,7 +50,7 @@ static int micfil_get_hw_params(struct dai *dai, { struct micfil_pdata *micfil = dai_get_drvdata(dai); - dai_info(dai, "micfil_get_hw_params()"); + dai_info(dai, "entry"); params->rate = micfil->params.pdm_rate; params->channels = micfil->params.pdm_ch; @@ -184,7 +184,7 @@ static int micfil_set_config(struct dai *dai, struct ipc_config_dai *common_conf micfil->params = config->micfil; - dai_info(dai, "micfil_set_config() dai_idx %d channels %d sampling_freq %d", + dai_info(dai, "dai_idx %d channels %d sampling_freq %d", common_config->dai_index, micfil->params.pdm_ch, micfil->params.pdm_rate); /* disable the module */ @@ -232,7 +232,7 @@ static int micfil_get_fifo_depth(struct dai *dai, int direction) static void micfil_start(struct dai *dai) { - dai_info(dai, "micfil_start()"); + dai_info(dai, "entry"); micfil_reset(dai); @@ -252,7 +252,7 @@ static void micfil_start(struct dai *dai) static void micfil_stop(struct dai *dai) { - dai_info(dai, "micfil_stop()"); + dai_info(dai, "entry"); /* Disable the module */ dai_update_bits(dai, REG_MICFIL_CTRL1, MICFIL_CTRL1_PDMIEN, 0); @@ -263,7 +263,7 @@ static void micfil_stop(struct dai *dai) static int micfil_trigger(struct dai *dai, int cmd, int direction) { - dai_info(dai, "micfil_trigger() cmd %d dir %d", cmd, direction); + dai_info(dai, "cmd %d dir %d", cmd, direction); switch (cmd) { case COMP_TRIGGER_START: @@ -288,7 +288,7 @@ static int micfil_probe(struct dai *dai) { struct micfil_pdata *micfil; - dai_info(dai, "micfil_probe()"); + dai_info(dai, "entry"); micfil = rzalloc(SOF_MEM_FLAG_KERNEL | SOF_MEM_FLAG_COHERENT, sizeof(*micfil)); if (!micfil) { @@ -307,7 +307,7 @@ static int micfil_remove(struct dai *dai) { struct micfil_pdata *micfil = dai_get_drvdata(dai); - dai_info(dai, "micfil_remove()"); + dai_info(dai, "entry"); rfree(micfil); dai_set_drvdata(dai, NULL); diff --git a/src/drivers/imx/sai.c b/src/drivers/imx/sai.c index eeb4555d3143..1d748c44a6e0 100644 --- a/src/drivers/imx/sai.c +++ b/src/drivers/imx/sai.c @@ -29,7 +29,7 @@ DECLARE_TR_CTX(sai_tr, SOF_UUID(sai_uuid), LOG_LEVEL_INFO); static void sai_start(struct dai *dai, int direction) { - dai_info(dai, "SAI: sai_start"); + dai_info(dai, "SAI: entry"); struct sai_pdata *sai = dai_get_drvdata(dai); int chan_idx = 0; @@ -139,7 +139,7 @@ static void sai_start(struct dai *dai, int direction) static void sai_release(struct dai *dai, int direction) { - dai_info(dai, "SAI: sai_release"); + dai_info(dai, "SAI: entry"); int chan_idx = 0; #ifdef CONFIG_IMX8ULP @@ -171,7 +171,7 @@ static void sai_release(struct dai *dai, int direction) static void sai_stop(struct dai *dai, int direction) { - dai_info(dai, "SAI: sai_stop"); + dai_info(dai, "SAI: entry"); uint32_t xcsr = 0U; int ret = 0; @@ -222,7 +222,7 @@ static void sai_stop(struct dai *dai, int direction) static inline int sai_set_config(struct dai *dai, struct ipc_config_dai *common_config, const void *spec_config) { - dai_info(dai, "SAI: sai_set_config"); + dai_info(dai, "SAI: entry"); const struct sof_ipc_dai_config *config = spec_config; uint32_t val_cr2 = 0, val_cr4 = 0, val_cr5 = 0; uint32_t mask_cr2 = 0, mask_cr4 = 0, mask_cr5 = 0; @@ -438,7 +438,7 @@ static inline int sai_set_config(struct dai *dai, struct ipc_config_dai *common_ static int sai_trigger(struct dai *dai, int cmd, int direction) { - dai_info(dai, "SAI: sai_trigger"); + dai_info(dai, "SAI: entry"); switch (cmd) { case COMP_TRIGGER_START: @@ -465,12 +465,12 @@ static int sai_probe(struct dai *dai) { struct sai_pdata *sai; - dai_info(dai, "SAI: sai_probe"); + dai_info(dai, "SAI: entry"); /* allocate private data */ sai = rzalloc(SOF_MEM_FLAG_KERNEL | SOF_MEM_FLAG_COHERENT, sizeof(*sai)); if (!sai) { - dai_err(dai, "sai_probe(): alloc failed"); + dai_err(dai, "alloc failed"); return -ENOMEM; } dai_set_drvdata(dai, sai); @@ -504,7 +504,7 @@ static int sai_remove(struct dai *dai) { struct sai_pdata *sai = dai_get_drvdata(dai); - dai_info(dai, "sai_remove()"); + dai_info(dai, "entry"); rfree(sai); dai_set_drvdata(dai, NULL); @@ -524,7 +524,7 @@ static int sai_get_fifo(struct dai *dai, int direction, int stream_id) case DAI_DIR_CAPTURE: return dai_fifo(dai, direction); /* stream_id is unused */ default: - dai_err(dai, "sai_get_fifo(): Invalid direction"); + dai_err(dai, "Invalid direction"); return -EINVAL; } } @@ -536,7 +536,7 @@ static int sai_get_fifo_depth(struct dai *dai, int direction) case DAI_DIR_CAPTURE: return dai->plat_data.fifo[direction].depth; default: - dai_err(dai, "esai_get_fifo_depth(): Invalid direction"); + dai_err(dai, "Invalid direction"); return -EINVAL; } } diff --git a/src/drivers/imx/sdma.c b/src/drivers/imx/sdma.c index 3f47b34101b5..a613f98b40c5 100644 --- a/src/drivers/imx/sdma.c +++ b/src/drivers/imx/sdma.c @@ -85,8 +85,7 @@ struct sdma_pdata { static void sdma_set_overrides(struct dma_chan_data *channel, bool event_override, bool host_override) { - tr_dbg(&sdma_tr, "sdma_set_overrides(%d, %d)", event_override, - host_override); + tr_dbg(&sdma_tr, "event %d, host %d", event_override, host_override); dma_reg_update_bits(channel->dma, SDMA_EVTOVR, BIT(channel->index), event_override ? BIT(channel->index) : 0); dma_reg_update_bits(channel->dma, SDMA_HOSTOVR, BIT(channel->index), @@ -110,7 +109,7 @@ static int sdma_run_c0(struct dma *dma, uint8_t cmd, uint32_t buf_addr, struct sdma_chan *c0data = dma_chan_get_data(c0); int ret; - tr_dbg(&sdma_tr, "sdma_run_c0 cmd %d buf_addr 0x%08x sdma_addr 0x%04x count %d", + tr_dbg(&sdma_tr, "cmd %d buf_addr 0x%08x sdma_addr 0x%04x count %d", cmd, buf_addr, sdma_addr, count); c0data->desc[0].config = SDMA_BD_CMD(cmd) | SDMA_BD_COUNT(count) @@ -150,7 +149,7 @@ static int sdma_run_c0(struct dma *dma, uint8_t cmd, uint32_t buf_addr, dma_reg_update_bits(dma, SDMA_CONFIG, SDMA_CONFIG_CSM_MSK, SDMA_CONFIG_CSM_DYN); - tr_dbg(&sdma_tr, "sdma_run_c0 done, ret = %d", ret); + tr_dbg(&sdma_tr, "done, ret = %d", ret); return ret; } @@ -161,7 +160,7 @@ static int sdma_register_init(struct dma *dma) struct sdma_pdata *pdata = dma_get_drvdata(dma); int i; - tr_dbg(&sdma_tr, "sdma_register_init"); + tr_dbg(&sdma_tr, "entry"); dma_reg_write(dma, SDMA_RESET, 1); /* Wait for 10us */ ret = poll_for_register_delay(dma_base(dma) + SDMA_RESET, 1, 0, 1000); @@ -216,7 +215,7 @@ static void sdma_init_c0(struct dma *dma) struct sdma_pdata *sdma_pdata = dma_get_drvdata(dma); struct sdma_chan *pdata = &sdma_pdata->chan_pdata[0]; - tr_dbg(&sdma_tr, "sdma_init_c0"); + tr_dbg(&sdma_tr, "entry"); c0->status = COMP_STATE_READY; /* Reset channel 0 private data */ @@ -233,14 +232,14 @@ static int sdma_boot(struct dma *dma) { int ret; - tr_dbg(&sdma_tr, "sdma_boot"); + tr_dbg(&sdma_tr, "entry"); ret = sdma_register_init(dma); if (ret < 0) return ret; sdma_init_c0(dma); - tr_dbg(&sdma_tr, "sdma_boot done"); + tr_dbg(&sdma_tr, "done"); return 0; } @@ -251,7 +250,7 @@ static int sdma_upload_context(struct dma_chan_data *chan) /* Ensure context is ready for upload */ dcache_writeback_region(pdata->ctx, sizeof(*pdata->ctx)); - tr_dbg(&sdma_tr, "sdma_upload_context for channel %d", chan->index); + tr_dbg(&sdma_tr, "for channel %d", chan->index); /* Last parameters are unneeded for this command and are ignored; * set to 0. @@ -382,7 +381,7 @@ static int sdma_remove(struct dma *dma) return 0; } - tr_dbg(&sdma_tr, "sdma_remove"); + tr_dbg(&sdma_tr, "entry"); /* Prevent all channels except channel 0 from running */ dma_reg_write(dma, SDMA_HOSTOVR, 1); @@ -442,7 +441,7 @@ static void sdma_enable_event(struct dma_chan_data *channel, int eventnum) { struct sdma_chan *pdata = dma_chan_get_data(channel); - tr_dbg(&sdma_tr, "sdma_enable_event(%d, %d)", channel->index, eventnum); + tr_dbg(&sdma_tr, "channel %d, event %d", channel->index, eventnum); if (eventnum < 0 || eventnum > SDMA_HWEVENTS_COUNT) return; /* No change if request is invalid */ @@ -460,7 +459,7 @@ static void sdma_enable_event(struct dma_chan_data *channel, int eventnum) static void sdma_disable_event(struct dma_chan_data *channel, int eventnum) { - tr_dbg(&sdma_tr, "sdma_disable_event(%d, %d)", channel->index, eventnum); + tr_dbg(&sdma_tr, "channel %d, event %d", channel->index, eventnum); if (eventnum < 0 || eventnum > SDMA_HWEVENTS_COUNT) return; /* No change if request is invalid */ @@ -475,7 +474,7 @@ static void sdma_channel_put(struct dma_chan_data *channel) if (channel->status == COMP_STATE_INIT) return; /* Channel was already free */ - tr_dbg(&sdma_tr, "sdma_channel_put(%d)", channel->index); + tr_dbg(&sdma_tr, "channel %d", channel->index); dma_interrupt_legacy(channel, DMA_IRQ_CLEAR); sdma_disable_event(channel, pdata->hw_event); @@ -485,7 +484,7 @@ static void sdma_channel_put(struct dma_chan_data *channel) static int sdma_start(struct dma_chan_data *channel) { - tr_dbg(&sdma_tr, "sdma_start(%d)", channel->index); + tr_dbg(&sdma_tr, "channel %d", channel->index); if (channel->status != COMP_STATE_PREPARE && channel->status != COMP_STATE_PAUSED) @@ -507,7 +506,7 @@ static int sdma_stop(struct dma_chan_data *channel) channel->status = COMP_STATE_READY; - tr_dbg(&sdma_tr, "sdma_stop(%d)", channel->index); + tr_dbg(&sdma_tr, "channel %d", channel->index); sdma_disable_channel(channel->dma, channel->index); @@ -553,7 +552,7 @@ static int sdma_copy(struct dma_chan_data *channel, int bytes, uint32_t flags) }; int idx; - tr_dbg(&sdma_tr, "sdma_copy"); + tr_dbg(&sdma_tr, "entry"); idx = (pdata->next_bd + 1) % 2; pdata->next_bd = idx; @@ -581,7 +580,7 @@ static int sdma_status(struct dma_chan_data *channel, struct sdma_chan *pdata = dma_chan_get_data(channel); struct sdma_bd *bd; - tr_dbg(&sdma_tr, "sdma_status"); + tr_dbg(&sdma_tr, "entry"); if (channel->status == COMP_STATE_INIT) return -EINVAL; status->state = channel->status; @@ -689,13 +688,13 @@ static int sdma_read_config(struct dma_chan_data *channel, for (i = 0; i < config->elem_array.count; i++) { if (config->direction == SOF_DMA_DIR_MEM_TO_DEV && pdata->fifo_paddr != config->elem_array.elems[i].dest) { - tr_err(&sdma_tr, "sdma_read_config: FIFO changes address!"); + tr_err(&sdma_tr, "FIFO changes address!"); return -EINVAL; } if (config->direction == SOF_DMA_DIR_DEV_TO_MEM && pdata->fifo_paddr != config->elem_array.elems[i].src) { - tr_err(&sdma_tr, "sdma_read_config: FIFO changes address!"); + tr_err(&sdma_tr, "FIFO changes address!"); return -EINVAL; } @@ -868,7 +867,7 @@ static int sdma_set_config(struct dma_chan_data *channel, struct sdma_chan *pdata = dma_chan_get_data(channel); int ret; - tr_dbg(&sdma_tr, "sdma_set_config channel %d", channel->index); + tr_dbg(&sdma_tr, "channel %d", channel->index); ret = sdma_read_config(channel, config); if (ret < 0) @@ -925,7 +924,7 @@ static int sdma_interrupt(struct dma_chan_data *channel, enum dma_irq_cmd cmd) */ return 0; default: - tr_err(&sdma_tr, "sdma_interrupt unknown cmd %d", cmd); + tr_err(&sdma_tr, "unknown cmd %d", cmd); return -EINVAL; } } @@ -966,7 +965,7 @@ static int sdma_get_data_size(struct dma_chan_data *channel, uint32_t *avail, uint32_t result_data = 0; int i; - tr_dbg(&sdma_tr, "sdma_get_data_size(%d)", channel->index); + tr_dbg(&sdma_tr, "channel %d", channel->index); if (channel->index == 0) { /* Channel 0 shouldn't have this called anyway */ tr_err(&sdma_tr, "Please do not call get_data_size on SDMA channel 0!"); @@ -992,7 +991,7 @@ static int sdma_get_data_size(struct dma_chan_data *channel, uint32_t *avail, *avail = result_data; break; default: - tr_err(&sdma_tr, "sdma_get_data_size channel invalid direction"); + tr_err(&sdma_tr, "channel invalid direction"); return -EINVAL; } return 0; diff --git a/src/drivers/mediatek/afe/afe-drv.c b/src/drivers/mediatek/afe/afe-drv.c index bdf5bc1f5f07..5f09359117e6 100644 --- a/src/drivers/mediatek/afe/afe-drv.c +++ b/src/drivers/mediatek/afe/afe-drv.c @@ -251,7 +251,7 @@ int afe_dai_set_config(struct mtk_base_afe *afe, int id, unsigned int channel, u if (id >= afe->dais_size) return -EINVAL; - tr_info(&afedrv_tr, "afe_dai_set_config, id:%d\n", id); + tr_info(&afedrv_tr, "id:%d\n", id); dai = &afe->dais[id]; dai->channel = channel; @@ -271,10 +271,10 @@ int afe_dai_get_config(struct mtk_base_afe *afe, int id, unsigned int *channel, /* TODO 1. if need use dai->id to search target dai */ /* TODO 1. if need a status to control the dai status */ - tr_info(&afedrv_tr, "afe_dai_get_config, id:%d\n", id); + tr_info(&afedrv_tr, "id:%d\n", id); if (id >= afe->dais_size || id < 0) { - tr_err(&afedrv_tr, "afe_dai_get_config , invalid id:%d\n", id); + tr_err(&afedrv_tr, ", invalid id:%d\n", id); return -EINVAL; } dai = &afe->dais[id]; diff --git a/src/drivers/mediatek/afe/afe-memif.c b/src/drivers/mediatek/afe/afe-memif.c index 233eaec63748..1e57f5cf0289 100644 --- a/src/drivers/mediatek/afe/afe-memif.c +++ b/src/drivers/mediatek/afe/afe-memif.c @@ -192,7 +192,7 @@ static int memif_copy(struct dma_chan_data *channel, int bytes, uint32_t flags) memif->wptr = (memif->wptr + bytes) % memif->dma_size; else memif->rptr = (memif->rptr + bytes) % memif->dma_size; - tr_dbg(&memif_tr, "memif_copy: wptr:%u, rptr:%u", memif->wptr, memif->rptr); + tr_dbg(&memif_tr, "wptr:%u, rptr:%u", memif->wptr, memif->rptr); notifier_event(channel, NOTIFIER_ID_DMA_COPY, NOTIFIER_TARGET_CORE_LOCAL, &next, sizeof(next)); @@ -241,7 +241,7 @@ static int memif_set_config(struct dma_chan_data *channel, struct dma_sg_config channel->direction = config->direction; direction = afe_memif_get_direction(memif->afe, memif->memif_id); - tr_info(&memif_tr, "memif_set_config, direction:%d, afe_dir:%d", config->direction, + tr_info(&memif_tr, "direction:%d, afe_dir:%d", config->direction, direction); switch (config->direction) { @@ -263,7 +263,7 @@ static int memif_set_config(struct dma_chan_data *channel, struct dma_sg_config tr_dbg(&memif_tr, "capture: dai_id:%d, dma_addr:%u\n", dai_id, dma_addr); break; default: - tr_err(&memif_tr, "afe_memif_set_config() unsupported config direction"); + tr_err(&memif_tr, "afe_unsupported config direction"); return -EINVAL; } diff --git a/src/idc/idc.c b/src/idc/idc.c index 466f84eee63f..bafa44299073 100644 --- a/src/idc/idc.c +++ b/src/idc/idc.c @@ -184,20 +184,21 @@ static int idc_prepare(uint32_t comp_id) /* we're running LL on different core, so allocate our own task */ if (!dev->task && dev->ipc_config.proc_domain == COMP_PROCESSING_DOMAIN_LL) { /* allocate task for shared component */ - dev->task = module_driver_heap_rzalloc(dev->drv->user_heap, SOF_MEM_FLAG_USER, - sizeof(*dev->task)); + dev->task = sof_heap_alloc(dev->drv->user_heap, SOF_MEM_FLAG_USER, + sizeof(*dev->task), 0); if (!dev->task) { ret = -ENOMEM; goto out; } + memset(dev->task, 0, sizeof(*dev->task)); ret = schedule_task_init_ll(dev->task, SOF_UUID(idc_task_uuid), SOF_SCHEDULE_LL_TIMER, dev->priority, comp_task, dev, dev->ipc_config.core, 0); if (ret < 0) { - module_driver_heap_free(dev->drv->user_heap, dev->task); + sof_heap_free(dev->drv->user_heap, dev->task); goto out; } } @@ -221,32 +222,12 @@ static int idc_trigger(uint32_t comp_id) struct idc *idc = *idc_get(); struct idc_payload *payload = idc_payload_get(idc, cpu_get_id()); uint32_t cmd = *(uint32_t *)payload; - int ret; ipc_dev = ipc_get_comp_by_id(ipc, comp_id); if (!ipc_dev) return -ENODEV; - ret = comp_trigger(ipc_dev->cd, cmd); - if (ret < 0) - goto out; - - /* schedule or cancel task */ - switch (cmd) { - case COMP_TRIGGER_START: - case COMP_TRIGGER_RELEASE: - schedule_task(ipc_dev->cd->task, 0, ipc_dev->cd->period); - break; - case COMP_TRIGGER_XRUN: - case COMP_TRIGGER_PAUSE: - case COMP_TRIGGER_STOP: - schedule_task_cancel(ipc_dev->cd->task); - break; - } - -out: - - return ret; + return comp_trigger(ipc_dev->cd, cmd); } /** diff --git a/src/idc/zephyr_idc.c b/src/idc/zephyr_idc.c index afd30628a5d3..8ffc770eb6ee 100644 --- a/src/idc/zephyr_idc.c +++ b/src/idc/zephyr_idc.c @@ -115,23 +115,38 @@ static void idc_handler(struct k_p4wq_work *work) /* * Used for *target* CPUs, since the initiator (usually core 0) can launch - * several IDC messages at once + * several IDC messages at once. Also we need 2 work items per target core, + * because the p4wq thread might just have returned from the work handler, but + * hasn't released the work buffer yet (hasn't set thread pointer to NULL). + * Then submitting the same work item again can result in an assertion failure. */ -static struct zephyr_idc_msg idc_work[CONFIG_CORE_COUNT]; +static struct zephyr_idc_msg idc_work[CONFIG_CORE_COUNT * 2]; +/* Protect the above array */ +static K_MUTEX_DEFINE(idc_mutex); int idc_send_msg(struct idc_msg *msg, uint32_t mode) { struct idc *idc = *idc_get(); struct idc_payload *payload = idc_payload_get(idc, msg->core); unsigned int target_cpu = msg->core; - struct zephyr_idc_msg *zmsg = idc_work + target_cpu; + struct zephyr_idc_msg *zmsg = idc_work + target_cpu * 2; struct idc_msg *msg_cp = &zmsg->msg; struct k_p4wq_work *work = &zmsg->work; int ret; int idc_send_memcpy_err __unused; + k_mutex_lock(&idc_mutex, K_FOREVER); + + if (unlikely(work->thread)) { + /* See comment above the idc_work[] array. */ + zmsg++; + work = &zmsg->work; + msg_cp = &zmsg->msg; + } + idc_send_memcpy_err = memcpy_s(msg_cp, sizeof(*msg_cp), msg, sizeof(*msg)); assert(!idc_send_memcpy_err); + /* Same priority as the IPC thread which is an EDF task and under Zephyr */ work->priority = CONFIG_EDF_THREAD_PRIORITY; work->deadline = 0; @@ -158,6 +173,8 @@ int idc_send_msg(struct idc_msg *msg, uint32_t mode) k_p4wq_submit(q_zephyr_idc + target_cpu, work); + k_mutex_unlock(&idc_mutex); + #ifdef CONFIG_SOF_TELEMETRY_IO_PERFORMANCE_MEASUREMENTS /* Increment performance counters */ io_perf_monitor_update_data(idc->io_perf_out_msg_count, 1); @@ -181,10 +198,19 @@ int idc_send_msg(struct idc_msg *msg, uint32_t mode) void idc_init_thread(void) { + char thread_name[] = "idc_p4wq0"; int cpu = cpu_get_id(); k_p4wq_enable_static_thread(q_zephyr_idc + cpu, _p4threads_q_zephyr_idc + cpu, BIT(cpu)); + + thread_name[sizeof(thread_name) - 2] = '0' + cpu; + k_thread_name_set(_p4threads_q_zephyr_idc + cpu, thread_name); + /* + * Assign SOF system heap to the IDC thread. Otherwise by default it + * uses the Zephyr heap for DP stack allocation + */ + k_thread_heap_assign(_p4threads_q_zephyr_idc + cpu, sof_sys_heap_get()); } #endif /* CONFIG_MULTICORE */ diff --git a/src/include/Readme.md b/src/include/README.md similarity index 100% rename from src/include/Readme.md rename to src/include/README.md diff --git a/src/include/ipc/dai.h b/src/include/ipc/dai.h index b5b29316f9e6..dfc2a3e9ffb0 100644 --- a/src/include/ipc/dai.h +++ b/src/include/ipc/dai.h @@ -21,6 +21,7 @@ #include <ipc/dai-mediatek.h> #include <ipc/dai-amd.h> #include <ipc/header.h> +#include <stddef.h> #include <stdint.h> /* @@ -94,9 +95,12 @@ enum sof_ipc_dai_type { SOF_DAI_AMD_SP_VIRTUAL, /**<Amd SP VIRTUAL */ SOF_DAI_AMD_HS_VIRTUAL, /**<Amd HS VIRTUAL */ SOF_DAI_IMX_MICFIL, /**< i.MX MICFIL */ - SOF_DAI_AMD_SW_AUDIO /**<Amd SW AUDIO */ + SOF_DAI_AMD_SDW, /**< Amd SDW */ + SOF_DAI_INTEL_UAOL, /**< Intel UAOL */ }; +#define SOF_DAI_CONFIG_HW_SPEC_OFFSET offsetof(struct sof_ipc_dai_config, ssp) + /* general purpose DAI configuration */ struct sof_ipc_dai_config { struct sof_ipc_cmd_hdr hdr; diff --git a/src/include/ipc4/alh.h b/src/include/ipc4/alh.h index c15578514b2a..7691b9fe0a11 100644 --- a/src/include/ipc4/alh.h +++ b/src/include/ipc4/alh.h @@ -37,7 +37,7 @@ #endif #if defined(CONFIG_SOC_SERIES_INTEL_ADSP_CAVS) || \ - defined(CONFIG_SOC_INTEL_ACE15_MTPM) + defined(CONFIG_SOC_ACE15_MTPM) #define IPC4_DAI_NUM_ALH_BI_DIR_LINKS 16 #define IPC4_DAI_NUM_ALH_BI_DIR_LINKS_GROUP 4 #else diff --git a/src/include/ipc4/base-config.h b/src/include/ipc4/base-config.h index b95ecbd3e1f9..0c89d534637a 100644 --- a/src/include/ipc4/base-config.h +++ b/src/include/ipc4/base-config.h @@ -30,6 +30,8 @@ struct sof_ipc_stream_params; void ipc4_base_module_cfg_to_stream_params(const struct ipc4_base_module_cfg *base_cfg, struct sof_ipc_stream_params *params); +void ipc4_audio_format_to_stream_params(const struct ipc4_audio_format *audio_fmt, + struct sof_ipc_stream_params *params); struct comp_buffer; void ipc4_update_buffer_format(struct comp_buffer *buf_c, const struct ipc4_audio_format *fmt); diff --git a/src/include/ipc4/base_fw.h b/src/include/ipc4/base_fw.h index 25601b1209f0..289ab76830e3 100644 --- a/src/include/ipc4/base_fw.h +++ b/src/include/ipc4/base_fw.h @@ -17,10 +17,11 @@ #ifndef __SOF_IPC4_BASE_FW_H__ #define __SOF_IPC4_BASE_FW_H__ -/* Three clk src states :low power XTAL, low power ring - * and high power ring oscillator +/* SOF FW performs autonomous management of clock sources. + * Set MAX_CLK_STATES to 0 in order to propagate this information through IPC and thus + * prevent reception of unsupported clock configuration. */ -#define IPC4_MAX_CLK_STATES 3 +#define IPC4_MAX_CLK_STATES 0 /* Max src queue count count supported by ipc4 */ #define IPC4_MAX_SRC_QUEUE 8 @@ -373,12 +374,30 @@ enum ipc4_fw_config_params { IPC4_DMI_FORCE_L1_EXIT = 28, /* FW context save on D3 entry */ IPC4_FW_CONTEXT_SAVE = 29, + /* Minimum size of host buffer in ms */ + IPC4_FW_MIN_HOST_BUFFER_PERIODS = 33, + /* decoder/encoder codec information */ + IPC4_FW_SOF_INFO = 35, /* Total number of FW config parameters */ IPC4_FW_CFG_PARAMS_COUNT, /* Max config parameter id */ IPC4_MAX_FW_CFG_PARAM = IPC4_FW_CFG_PARAMS_COUNT - 1, }; +/* + * tuple based array for SOF specific information under IPC4_FW_SOF_INFO + * tuple of fw_config + */ +enum ipc4_fw_sof_info_params { + /* decoder/encoder codec information */ + IPC4_SOF_CODEC_INFO = 0, + + /* Total number of SOF config parameters */ + IPC4_SOF_CFG_PARAMS_COUNT, + /* Max config parameter id */ + IPC4_MAX_SOF_CFG_PARAM = IPC4_SOF_CFG_PARAMS_COUNT - 1, +}; + enum ipc4_hw_config_params { /* Version of cAVS implemented by FW (from ROMInfo) */ IPC4_CAVS_VER_HW_CFG = 0, diff --git a/src/include/ipc4/gateway.h b/src/include/ipc4/gateway.h index f78c21caf979..9398bb44be0e 100644 --- a/src/include/ipc4/gateway.h +++ b/src/include/ipc4/gateway.h @@ -162,8 +162,8 @@ struct ipc4_ipc_gateway_config_blob { uint32_t buffer_size; /**< Flags */ - union flags { - struct bits { + union { + struct { /**< Activates high threshold notification */ /*! * Indicates whether notification should be sent to the host diff --git a/src/include/ipc4/handler.h b/src/include/ipc4/handler.h new file mode 100644 index 000000000000..b25cb98e9427 --- /dev/null +++ b/src/include/ipc4/handler.h @@ -0,0 +1,54 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * + * Copyright(c) 2026 Intel Corporation. + */ + +#ifndef __SOF_IPC4_HANDLER_H__ +#define __SOF_IPC4_HANDLER_H__ + +struct ipc4_message_request; + +/** + * \brief Processes IPC4 userspace module message. + * @param[in] ipc4 IPC4 message request. + * @param[in] reply IPC message reply structure. + * @return IPC4_SUCCESS on success, error code otherwise. + */ +int ipc4_user_process_module_message(struct ipc4_message_request *ipc4, struct ipc_msg *reply); + +/** + * \brief Processes IPC4 userspace global message. + * @param[in] ipc4 IPC4 message request. + * @param[in] reply IPC message reply structure. + * @return IPC4_SUCCESS on success, error code otherwise. + */ +int ipc4_user_process_glb_message(struct ipc4_message_request *ipc4, struct ipc_msg *reply); + +/** + * \brief Increment the IPC compound message pre-start counter. + * @param[in] msg_id IPC message ID. + */ +void ipc_compound_pre_start(int msg_id); + +/** + * \brief Decrement the IPC compound message pre-start counter on return value status. + * @param[in] msg_id IPC message ID. + * @param[in] ret Return value of the IPC command. + * @param[in] delayed True if the reply is delayed. + */ +void ipc_compound_post_start(uint32_t msg_id, int ret, bool delayed); + +/** + * \brief Complete the IPC compound message. + * @param[in] msg_id IPC message ID. + * @param[in] error Error code of the IPC command. + */ +void ipc_compound_msg_done(uint32_t msg_id, int error); + +/** + * \brief Wait for the IPC compound message to complete. + * @return 0 on success, error code otherwise on timeout. + */ +int ipc_wait_for_compound_msg(void); + +#endif /* __SOF_IPC4_HANDLER_H__ */ diff --git a/src/include/ipc4/header.h b/src/include/ipc4/header.h index 643ded46050a..6a6eb59d02e5 100644 --- a/src/include/ipc4/header.h +++ b/src/include/ipc4/header.h @@ -177,6 +177,7 @@ struct ipc4_message_reply { #define SOF_IPC4_ENUM_CONTROL_PARAM_ID 201 #define SOF_IPC4_BYTES_CONTROL_PARAM_ID 202 #define SOF_IPC4_NOTIFY_MODULE_EVENTID_ALSA_MAGIC_VAL ((uint32_t)(0xA15A << 16)) +#define SOF_IPC4_NOTIFY_MODULE_EVENTID_COMPR_MAGIC_VAL ((uint32_t)(0xC0C0 << 16)) /** * struct sof_ipc4_ctrl_value_chan: generic channel mapped value data diff --git a/src/include/ipc4/module.h b/src/include/ipc4/module.h index a73ede0b0bd5..dfe0f02e628e 100644 --- a/src/include/ipc4/module.h +++ b/src/include/ipc4/module.h @@ -77,7 +77,8 @@ struct ipc4_vendor_error { enum ipc4_mod_init_data_glb_id { IPC4_MOD_INIT_DATA_ID_INVALID = 0, IPC4_MOD_INIT_DATA_ID_DP_DATA = 1, - IPC4_MOD_INIT_DATA_ID_MAX = IPC4_MOD_INIT_DATA_ID_DP_DATA, + IPC4_MOD_INIT_DATA_ID_MODULE_DATA = 2, + IPC4_MOD_INIT_DATA_ID_MAX = IPC4_MOD_INIT_DATA_ID_MODULE_DATA, }; /* data object for vendor bespoke data with ABI growth and backwards compat */ @@ -90,9 +91,11 @@ struct ipc4_module_init_ext_object { /* Ext init array data object for Data Processing module memory requirements */ struct ipc4_module_init_ext_obj_dp_data { - uint32_t domain_id; /* userspace domain ID */ - uint32_t stack_bytes; /* required stack size in bytes, 0 means default size */ - uint32_t heap_bytes; /* required heap size in bytes, 0 means default size */ + uint32_t domain_id; /* userspace domain ID */ + uint32_t stack_bytes; /* required stack size in bytes */ + uint32_t interim_heap_bytes; /* required interim heap size in bytes */ + uint32_t lifetime_heap_bytes; /* required lifetime heap size in bytes */ + uint32_t shared_bytes; /* required shared memory size in bytes */ } __attribute__((packed, aligned(4))); /* diff --git a/src/include/ipc4/notification.h b/src/include/ipc4/notification.h index 207dbfb7139c..614a926d16ad 100644 --- a/src/include/ipc4/notification.h +++ b/src/include/ipc4/notification.h @@ -88,7 +88,7 @@ enum sof_ipc4_resource_event_type { /* SNDW debug notification e.g. external VAD detected */ SOF_IPC4_SNDW_DEBUG_INFO = 18, /* Invalid type */ - SOF_IPC4_INVALID_RESORUCE_EVENT_TYPE = 19, + SOF_IPC4_INVALID_RESOURCE_EVENT_TYPE = 19, }; /* Resource Type - source of the event */ @@ -288,15 +288,13 @@ struct ipc4_resource_event_data_notification { #define IPC4_RESOURCE_EVENT_SIZE sizeof(struct ipc4_resource_event_data_notification) -void process_data_error_notif_msg_init(struct ipc_msg *msg, uint32_t resource_id, - uint32_t error_code); +void send_process_data_error_notif_msg(uint32_t resource_id, uint32_t error_code); -void copier_gateway_underrun_notif_msg_init(struct ipc_msg *msg, uint32_t pipeline_id); -void copier_gateway_overrun_notif_msg_init(struct ipc_msg *msg, uint32_t pipeline_id); -void gateway_underrun_notif_msg_init(struct ipc_msg *msg, uint32_t resource_id); -void gateway_overrun_notif_msg_init(struct ipc_msg *msg, uint32_t resource_id); +bool send_copier_gateway_xrun_notif_msg(uint32_t pipeline_id, enum sof_ipc_stream_direction dir); +bool send_gateway_xrun_notif_msg(uint32_t resource_id, enum sof_ipc_stream_direction dir); -void mixer_underrun_notif_msg_init(struct ipc_msg *msg, uint32_t resource_id, uint32_t eos_flag, - uint32_t data_mixed, uint32_t expected_data_mixed); +void send_mixer_underrun_notif_msg(uint32_t resource_id, uint32_t eos_flag, uint32_t data_mixed, + uint32_t expected_data_mixed); +void ipc4_update_notification_mask(uint32_t ntfy_mask, uint32_t enabled_mask); #endif /* __IPC4_NOTIFICATION_H__ */ diff --git a/src/include/ipc4/pipeline.h b/src/include/ipc4/pipeline.h index 219c36a89a3d..198918cf8577 100644 --- a/src/include/ipc4/pipeline.h +++ b/src/include/ipc4/pipeline.h @@ -69,6 +69,41 @@ enum ipc4_pipeline_state { SOF_IPC4_PIPELINE_STATE_SAVED }; +/* IDs for all pipeline ext data types in struct ipc4_pipeline_init_ext_object */ +enum ipc4_pipeline_ext_obj_id { + IPC4_GLB_PIPE_EXT_OBJ_ID_INVALID = 0, + IPC4_GLB_PIPE_EXT_OBJ_ID_MEM_DATA = 1, + IPC4_GLB_PIPE_EXT_OBJ_ID_MAX = IPC4_GLB_PIPE_EXT_OBJ_ID_MEM_DATA, +}; + +/* data object for vendor bespoke data with ABI growth and backwards compat */ +struct ipc4_pipeline_ext_object { + uint32_t last_object : 1; /* object is last in array if 1 else object follows. */ + uint32_t object_id : 15; /* unique ID for this object or globally */ + uint32_t object_words : 16; /* size in dwords (excluding this structure) */ +} __packed __aligned(4); +/* the object data will be placed in memory here and will have size "object_words" */ + +/* Ext array data object for pipeline instance's memory requirements */ +struct ipc4_pipeline_ext_obj_mem_data { + uint32_t domain_id; /* userspace domain ID */ + uint32_t stack_bytes; /* required stack size in bytes */ + uint32_t interim_heap_bytes; /* required interim heap size in bytes */ + uint32_t lifetime_heap_bytes; /* required lifetime heap size in bytes */ + uint32_t shared_bytes; /* required shared memory in bytes */ +} __packed __aligned(4); + +/* + * Host Driver sends this message to create a new pipeline instance. + */ +struct ipc4_pipeline_ext_payload { + uint32_t payload_words : 24; /* size in dwords (excluding this structure) */ + uint32_t data_obj_array : 1; /* struct ipc4_pipeline_ext_object data */ + uint32_t rsvd0 : 7; + uint32_t rsvd1; + uint32_t rsvd2; +} __packed __aligned(4); + /*! * lp - indicates whether the pipeline should be kept on running in low power * mode. On BXT the driver should set this flag to 1 for WoV pipeline. @@ -106,7 +141,8 @@ struct ipc4_pipeline_create { uint32_t rsvd1 : 3; uint32_t attributes : 16; uint32_t core_id : 4; - uint32_t rsvd2 : 6; + uint32_t rsvd2 : 5; + uint32_t payload : 1; uint32_t _reserved_2 : 2; } r; } extension; diff --git a/src/include/module/audio/sink_api.h b/src/include/module/audio/sink_api.h index 527fd1dc11c6..920087d6f8b3 100644 --- a/src/include/module/audio/sink_api.h +++ b/src/include/module/audio/sink_api.h @@ -77,6 +77,12 @@ struct sink_ops { */ int (*commit_buffer)(struct sof_sink *sink, size_t commit_size); + /** + * get latest feeding time for this sink, result is a number of microseconds since "NOW" + * where "now" means a start of last LL cycle, as described in zephyr_dp_schedule.c + */ + uint32_t (*get_lft)(struct sof_sink *sink); + /** * OPTIONAL: Notification to the sink implementation about changes in audio format * @@ -348,4 +354,9 @@ static inline struct processing_module *sink_get_bound_module(struct sof_sink *s return sink->bound_module; } +static inline uint32_t sink_get_last_feeding_time(struct sof_sink *sink) +{ + return sink->ops->get_lft(sink); +} + #endif /* __MODULE_AUDIO_SINK_API_H__ */ diff --git a/src/include/module/crossover/crossover_common.h b/src/include/module/crossover/crossover_common.h index 28c6e8aa7775..d238ff7c1008 100644 --- a/src/include/module/crossover/crossover_common.h +++ b/src/include/module/crossover/crossover_common.h @@ -8,6 +8,7 @@ #ifndef __SOF_CROSSOVER_COMMON_H__ #define __SOF_CROSSOVER_COMMON_H__ +#include <sof/audio/module_adapter/module/generic.h> #include <sof/math/iir_df1.h> #include <user/eq.h> @@ -39,17 +40,19 @@ typedef void (*crossover_split)(int32_t in, int32_t out[], extern const crossover_split crossover_split_fnmap[]; /* crossover init function */ -int crossover_init_coef_ch(struct sof_eq_iir_biquad *coef, +int crossover_init_coef_ch(struct processing_module *mod, + struct sof_eq_iir_biquad *coef, struct crossover_state *ch_state, int32_t num_sinks); /** * \brief Reset the state of an LR4 filter. */ -static inline void crossover_reset_state_lr4(struct iir_state_df1 *lr4) +static inline void crossover_reset_state_lr4(struct processing_module *mod, + struct iir_state_df1 *lr4) { - rfree(lr4->coef); - rfree(lr4->delay); + mod_free(mod, lr4->coef); + mod_free(mod, lr4->delay); lr4->coef = NULL; lr4->delay = NULL; @@ -59,13 +62,14 @@ static inline void crossover_reset_state_lr4(struct iir_state_df1 *lr4) * \brief Reset the state (coefficients and delay) of the crossover filter * of a single channel. */ -static inline void crossover_reset_state_ch(struct crossover_state *ch_state) +static inline void crossover_reset_state_ch(struct processing_module *mod, + struct crossover_state *ch_state) { int i; for (i = 0; i < CROSSOVER_MAX_LR4; i++) { - crossover_reset_state_lr4(&ch_state->lowpass[i]); - crossover_reset_state_lr4(&ch_state->highpass[i]); + crossover_reset_state_lr4(mod, &ch_state->lowpass[i]); + crossover_reset_state_lr4(mod, &ch_state->highpass[i]); } } diff --git a/src/include/module/module/base.h b/src/include/module/module/base.h index ed116681afd2..657673099d60 100644 --- a/src/include/module/module/base.h +++ b/src/include/module/module/base.h @@ -16,9 +16,27 @@ #include "interface.h" #include "../ipc4/base-config.h" +#ifdef SOF_MODULE_API_PRIVATE +#include <sof/list.h> +#endif + #define module_get_private_data(mod) ((mod)->priv.private) #define module_set_private_data(mod, data) ((mod)->priv.private = data) +/** + * \struct module_ext_init_data + * \brief Container for found ext init object pointers + * This struct contains pointers that point to IPC payload directly. The + * module should store what it needs in its init() callback as the data + * is not valid after that. + */ +struct ipc4_module_init_ext_obj_dp_data; +struct module_ext_init_data { + const struct ipc4_module_init_ext_obj_dp_data *dp_data; + const void *module_data; + size_t module_data_size; +}; + /** * \struct module_config * \brief Module config container, used for both config types. @@ -34,9 +52,7 @@ struct module_config { uint8_t nb_output_pins; struct ipc4_input_pin_format *input_pins; struct ipc4_output_pin_format *output_pins; - uint32_t domain_id; /* userspace domain ID */ - uint32_t stack_bytes; /* stack size in bytes, 0 means default value */ - uint32_t heap_bytes; /* max heap size in bytes, 0 means default value */ + struct module_ext_init_data *ext_data; /**< IPC payload pointers, NULL after init() */ #endif }; @@ -71,6 +87,7 @@ enum module_processing_type { }; struct userspace_context; +struct k_mem_domain; /* * A pointer to this structure is passed to module API functions (from struct module_interface). @@ -184,6 +201,7 @@ struct processing_module { enum module_processing_type proc_type; #if CONFIG_USERSPACE struct userspace_context *user_ctx; + struct k_mem_domain *mdom; #endif /* CONFIG_USERSPACE */ #endif /* SOF_MODULE_PRIVATE */ }; diff --git a/src/include/sof/audio/audio_buffer.h b/src/include/sof/audio/audio_buffer.h index 5156971d515f..95317e5dc964 100644 --- a/src/include/sof/audio/audio_buffer.h +++ b/src/include/sof/audio/audio_buffer.h @@ -60,6 +60,7 @@ struct audio_buffer_ops { const uint32_t frame_align_req); }; +struct mod_alloc_ctx; /* base class for all buffers, all buffers must inherit from it */ struct sof_audio_buffer { CORE_CHECK_STRUCT_FIELD; @@ -110,6 +111,8 @@ struct sof_audio_buffer { * should not be in struct sof_audio_buffer at all, kept for pipeline2.0 transition */ bool walking; /**< indicates if the buffer is being walked */ + + struct mod_alloc_ctx *alloc; }; #if CONFIG_PIPELINE_2_0 @@ -322,6 +325,14 @@ void audio_buffer_reset(struct sof_audio_buffer *buffer) { if (buffer->ops->reset) buffer->ops->reset(buffer); + +#if CONFIG_PIPELINE_2_0 + if (buffer->secondary_buffer_sink && buffer->secondary_buffer_sink->ops->reset) + buffer->secondary_buffer_sink->ops->reset(buffer->secondary_buffer_sink); + + if (buffer->secondary_buffer_source && buffer->secondary_buffer_source->ops->reset) + buffer->secondary_buffer_source->ops->reset(buffer->secondary_buffer_source); +#endif } /* Audio-buffer wrappers for the source-sink API */ @@ -337,5 +348,6 @@ int audio_buffer_sink_on_audio_format_set(struct sof_sink *sink); int audio_buffer_sink_set_alignment_constants(struct sof_sink *sink, const uint32_t byte_align, const uint32_t frame_align_req); +uint32_t audio_buffer_sink_get_lft(struct sof_sink *sink); #endif /* __SOF_AUDIO_BUFFER__ */ diff --git a/src/include/sof/audio/audio_stream.h b/src/include/sof/audio/audio_stream.h index 9703ec9cadea..dd81c157b8d8 100644 --- a/src/include/sof/audio/audio_stream.h +++ b/src/include/sof/audio/audio_stream.h @@ -591,9 +591,55 @@ audio_stream_avail_frames(const struct audio_stream *source, } /** - * Computes maximum number of frames aligned that can be copied from - * source buffer to sink buffer, verifying number of available source - * frames vs. free space available in sink. + * Rounds down a frame count to meet the alignment constraint of the stream. + * @param stream Audio stream with alignment requirements set. + * @param frames Frame count to round down. + * @return Largest aligned frame count less than or equal to frames. + */ +static inline uint32_t audio_stream_align_frames_round_down(const struct audio_stream *stream, + uint32_t frames) +{ + uint16_t align = stream->runtime_stream_params.align_frame_cnt; + + return ROUND_DOWN(frames, align); +} + +/** + * Rounds up a frame count to meet the alignment constraint of the stream. + * @param stream Audio stream with alignment requirements set. + * @param frames Frame count to round up. + * @return Smallest aligned frame count greater than or equal to frames. + */ +static inline uint32_t audio_stream_align_frames_round_up(const struct audio_stream *stream, + uint32_t frames) +{ + uint16_t align = stream->runtime_stream_params.align_frame_cnt; + uint32_t aligned_frames = ROUND_DOWN(frames, align); + + if (aligned_frames < frames) + aligned_frames += align; + + return aligned_frames; +} + +/** + * Rounds to nearest a frame count to meet the alignment constraint of the stream. + * @param stream Audio stream with alignment requirements set. + * @param frames Frame count to round to nearest. + * @return Aligned frame count. + */ +static inline uint32_t audio_stream_align_frames_round_nearest(const struct audio_stream *stream, + uint32_t frames) +{ + uint16_t align = stream->runtime_stream_params.align_frame_cnt; + + return ROUND_DOWN(frames + (align >> 1), align); +} + +/** + * Computes maximum number of frames aligned with source align criteria + * that can be copied from source buffer to sink buffer, verifying number + * of available source frames vs. free space available in sink. * @param source Buffer of source. * @param sink Buffer of sink. * @return Number of frames. @@ -602,14 +648,11 @@ static inline uint32_t audio_stream_avail_frames_aligned(const struct audio_stream *source, const struct audio_stream *sink) { - uint32_t src_frames = (audio_stream_get_avail_bytes(source) >> - source->runtime_stream_params.align_shift_idx) * - source->runtime_stream_params.align_frame_cnt; - uint32_t sink_frames = (audio_stream_get_free_bytes(sink) >> - sink->runtime_stream_params.align_shift_idx) * - sink->runtime_stream_params.align_frame_cnt; + uint32_t src_frames = audio_stream_get_avail_frames(source); + uint32_t sink_frames = audio_stream_get_free_frames(sink); + uint32_t n = MIN(src_frames, sink_frames); - return MIN(src_frames, sink_frames); + return audio_stream_align_frames_round_down(source, n); } /** diff --git a/src/include/sof/audio/buffer.h b/src/include/sof/audio/buffer.h index 768db978bb02..6e6b8a9caef8 100644 --- a/src/include/sof/audio/buffer.h +++ b/src/include/sof/audio/buffer.h @@ -16,7 +16,6 @@ #include <rtos/panic.h> #include <rtos/alloc.h> #include <rtos/cache.h> -#include <sof/lib/uuid.h> #include <sof/list.h> #include <sof/coherent.h> #include <sof/math/numbers.h> @@ -33,6 +32,7 @@ #include <stdint.h> struct comp_dev; +struct buffer_cb_transact; /** \name Trace macros * @{ @@ -148,6 +148,15 @@ struct comp_buffer { /* list of buffers, to be used i.e. in raw data processing mode*/ struct list_item buffers_list; + +#if CONFIG_PROBE + /** probe produce callback, called on buffer produce */ + void (*probe_cb_produce)(void *arg, struct buffer_cb_transact *cb_data); + /** probe free callback, called on buffer free */ + void (*probe_cb_free)(void *arg); + /** opaque argument passed to probe callbacks */ + void *probe_cb_arg; +#endif }; /* @@ -188,17 +197,13 @@ static inline void comp_buffer_reset_sink_list(struct comp_buffer *buffer) list_init(&buffer->sink_list); } -/* Only to be used for synchronous same-core notifications! */ +/* Used as parameter for probe produce callback */ struct buffer_cb_transact { struct comp_buffer *buffer; uint32_t transaction_amount; void *transaction_begin_address; }; -struct buffer_cb_free { - struct comp_buffer *buffer; -}; - #define buffer_from_list(ptr, dir) \ ((dir) == PPL_DIR_DOWNSTREAM ? \ container_of(ptr, struct comp_buffer, source_list) : \ @@ -211,12 +216,16 @@ struct buffer_cb_free { buffer->cb_type = type; \ } while (0) +struct mod_alloc_ctx; + /* pipeline buffer creation and destruction */ -struct comp_buffer *buffer_alloc(size_t size, uint32_t flags, uint32_t align, - bool is_shared); -struct comp_buffer *buffer_alloc_range(size_t preferred_size, size_t minimum_size, +struct comp_buffer *buffer_alloc(struct mod_alloc_ctx *alloc, size_t size, uint32_t flags, + uint32_t align, bool is_shared); +struct comp_buffer *buffer_alloc_range(struct mod_alloc_ctx *alloc, size_t preferred_size, + size_t minimum_size, uint32_t flags, uint32_t align, bool is_shared); -struct comp_buffer *buffer_new(const struct sof_ipc_buffer *desc, bool is_shared); +struct comp_buffer *buffer_new(struct mod_alloc_ctx *alloc, const struct sof_ipc_buffer *desc, + bool is_shared); int buffer_set_size(struct comp_buffer *buffer, uint32_t size, uint32_t alignment); int buffer_set_size_range(struct comp_buffer *buffer, size_t preferred_size, size_t minimum_size, diff --git a/src/include/sof/audio/cadence/mp3_enc/xa_mp3_enc_api.h b/src/include/sof/audio/cadence/mp3_enc/xa_mp3_enc_api.h new file mode 100644 index 000000000000..aac961e63cf9 --- /dev/null +++ b/src/include/sof/audio/cadence/mp3_enc/xa_mp3_enc_api.h @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2022-2025 Cadence Design Systems, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + + + +#ifndef __XA_MP3ENC_CONFIG_PARAMS_H__ +#define __XA_MP3ENC_CONFIG_PARAMS_H__ + +/* mp3_enc-specific configuration parameters */ +enum xa_config_param_mp3_enc { + XA_MP3ENC_CONFIG_PARAM_PCM_WDSZ = 0, + XA_MP3ENC_CONFIG_PARAM_SAMP_FREQ = 1, + XA_MP3ENC_CONFIG_PARAM_NUM_CHANNELS = 2, + XA_MP3ENC_CONFIG_PARAM_BITRATE = 3 +#ifdef ENABLE_CUT_OFF_FREQ_CONFIG + , XA_MP3ENC_CONFIG_FATAL_FRAC_BANDWIDTH = 4 +#endif // ENABLE_CUT_OFF_FREQ_CONFIG +}; + +/* commands */ +#include "xa_apicmd_standards.h" + +/* mp3_enc-specific commands */ +/* (none) */ + +/* mp3_enc-specific command types */ +/* (none) */ + +/* error codes */ +#include "xa_error_standards.h" + +#define XA_CODEC_MP3_ENC 2 + +/* mp3_enc-specific error_codes */ +/*****************************************************************************/ +/* Class 0: API Errors */ +/*****************************************************************************/ +/* Nonfatal Errors */ +/* (none) */ +/* Fatal Errors */ +/* (none) */ + +/*****************************************************************************/ +/* Class 1: Configuration Errors */ +/*****************************************************************************/ +/* Nonfatal Errors */ +enum xa_error_nonfatal_config_mp3_enc { + XA_MP3ENC_CONFIG_NONFATAL_INVALID_BITRATE = XA_ERROR_CODE(xa_severity_nonfatal, xa_class_config, XA_CODEC_MP3_ENC, 0) +}; + +/* Fatal Errors */ +enum xa_error_fatal_config_mp3_enc { + XA_MP3ENC_CONFIG_FATAL_SAMP_FREQ = XA_ERROR_CODE(xa_severity_fatal, xa_class_config, XA_CODEC_MP3_ENC, 0), + XA_MP3ENC_CONFIG_FATAL_NUM_CHANNELS = XA_ERROR_CODE(xa_severity_fatal, xa_class_config, XA_CODEC_MP3_ENC, 1), + XA_MP3ENC_CONFIG_FATAL_PCM_WDSZ = XA_ERROR_CODE(xa_severity_fatal, xa_class_config, XA_CODEC_MP3_ENC, 2) +#ifdef ENABLE_CUT_OFF_FREQ_CONFIG + , XA_MP3ENC_CONFIG_PARAM_FRAC_BANDWIDTH = XA_ERROR_CODE(xa_severity_fatal, xa_class_config, XA_CODEC_MP3_ENC, 3) +#endif // ENABLE_CUT_OFF_FREQ_CONFIG +}; +/* (none) */ + +#include "xa_type_def.h" + +#if defined(__cplusplus) +extern "C" { +#endif /* __cplusplus */ +xa_codec_func_t xa_mp3_enc; +#if defined(__cplusplus) +} +#endif /* __cplusplus */ +#endif /* __XA_MP3ENC_CONFIG_PARAMS_H__ */ diff --git a/src/include/sof/audio/coefficients/fft/twiddle_3072_32.h b/src/include/sof/audio/coefficients/fft/twiddle_3072_32.h new file mode 100644 index 000000000000..3024c7b49cff --- /dev/null +++ b/src/include/sof/audio/coefficients/fft/twiddle_3072_32.h @@ -0,0 +1,4120 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * + * Copyright(c) 2025 Intel Corporation. All rights reserved. + * + */ + +/* Twiddle factors in Q1.31 format */ + +#ifndef __INCLUDE_TWIDDLE_3072_32_H__ +#define __INCLUDE_TWIDDLE_3072_32_H__ + +#include <stdint.h> + +#define FFT_MULTI_TWIDDLE_SIZE 2048 + +/* in Q1.31, generated from cos(i * 2 * pi / FFT_SIZE_MAX) */ +const int32_t multi_twiddle_real_32[FFT_MULTI_TWIDDLE_SIZE] = { + 2147483647, + 2147479156, + 2147465681, + 2147443222, + 2147411780, + 2147371355, + 2147321946, + 2147263555, + 2147196181, + 2147119825, + 2147034487, + 2146940167, + 2146836866, + 2146724584, + 2146603322, + 2146473080, + 2146333858, + 2146185658, + 2146028480, + 2145862324, + 2145687192, + 2145503083, + 2145310000, + 2145107942, + 2144896910, + 2144676905, + 2144447929, + 2144209982, + 2143963065, + 2143707180, + 2143442326, + 2143168506, + 2142885721, + 2142593971, + 2142293258, + 2141983583, + 2141664948, + 2141337354, + 2141000801, + 2140655293, + 2140300829, + 2139937412, + 2139565043, + 2139183723, + 2138793455, + 2138394240, + 2137986079, + 2137568974, + 2137142927, + 2136707940, + 2136264015, + 2135811153, + 2135349356, + 2134878626, + 2134398966, + 2133910377, + 2133412861, + 2132906420, + 2132391057, + 2131866773, + 2131333572, + 2130791454, + 2130240422, + 2129680480, + 2129111628, + 2128533869, + 2127947206, + 2127351642, + 2126747178, + 2126133817, + 2125511562, + 2124880416, + 2124240380, + 2123591458, + 2122933653, + 2122266967, + 2121591402, + 2120906963, + 2120213651, + 2119511470, + 2118800422, + 2118080511, + 2117351739, + 2116614110, + 2115867626, + 2115112291, + 2114348108, + 2113575080, + 2112793210, + 2112002502, + 2111202959, + 2110394584, + 2109577380, + 2108751352, + 2107916502, + 2107072834, + 2106220352, + 2105359059, + 2104488958, + 2103610054, + 2102722350, + 2101825849, + 2100920556, + 2100006474, + 2099083608, + 2098151960, + 2097211535, + 2096262337, + 2095304370, + 2094337637, + 2093362143, + 2092377892, + 2091384888, + 2090383135, + 2089372638, + 2088353400, + 2087325426, + 2086288720, + 2085243286, + 2084189130, + 2083126254, + 2082054665, + 2080974365, + 2079885360, + 2078787655, + 2077681253, + 2076566160, + 2075442379, + 2074309917, + 2073168777, + 2072018965, + 2070860485, + 2069693342, + 2068517540, + 2067333086, + 2066139983, + 2064938237, + 2063727853, + 2062508835, + 2061281190, + 2060044922, + 2058800036, + 2057546537, + 2056284431, + 2055013723, + 2053734418, + 2052446522, + 2051150040, + 2049844978, + 2048531340, + 2047209133, + 2045878362, + 2044539032, + 2043191150, + 2041834720, + 2040469748, + 2039096241, + 2037714204, + 2036323642, + 2034924562, + 2033516969, + 2032100869, + 2030676269, + 2029243173, + 2027801589, + 2026351522, + 2024892978, + 2023425963, + 2021950484, + 2020466546, + 2018974156, + 2017473321, + 2015964045, + 2014446336, + 2012920201, + 2011385644, + 2009842674, + 2008291295, + 2006731516, + 2005163342, + 2003586779, + 2002001835, + 2000408516, + 1998806829, + 1997196780, + 1995578377, + 1993951625, + 1992316532, + 1990673105, + 1989021350, + 1987361274, + 1985692885, + 1984016189, + 1982331193, + 1980637905, + 1978936331, + 1977226479, + 1975508355, + 1973781967, + 1972047323, + 1970304428, + 1968553292, + 1966793920, + 1965026321, + 1963250501, + 1961466469, + 1959674231, + 1957873796, + 1956065170, + 1954248361, + 1952423377, + 1950590226, + 1948748914, + 1946899451, + 1945041843, + 1943176098, + 1941302225, + 1939420231, + 1937530123, + 1935631910, + 1933725600, + 1931811201, + 1929888720, + 1927958166, + 1926019547, + 1924072871, + 1922118145, + 1920155379, + 1918184581, + 1916205758, + 1914218919, + 1912224073, + 1910221227, + 1908210390, + 1906191570, + 1904164776, + 1902130017, + 1900087301, + 1898036636, + 1895978031, + 1893911494, + 1891837035, + 1889754661, + 1887664383, + 1885566207, + 1883460144, + 1881346202, + 1879224389, + 1877094716, + 1874957189, + 1872811820, + 1870658615, + 1868497586, + 1866328740, + 1864152086, + 1861967634, + 1859775393, + 1857575372, + 1855367581, + 1853152028, + 1850928722, + 1848697674, + 1846458892, + 1844212386, + 1841958164, + 1839696238, + 1837426615, + 1835149306, + 1832864320, + 1830571667, + 1828271356, + 1825963397, + 1823647799, + 1821324572, + 1818993726, + 1816655271, + 1814309216, + 1811955572, + 1809594347, + 1807225553, + 1804849198, + 1802465294, + 1800073849, + 1797674873, + 1795268378, + 1792854372, + 1790432867, + 1788003871, + 1785567396, + 1783123452, + 1780672048, + 1778213194, + 1775746903, + 1773273182, + 1770792044, + 1768303498, + 1765807555, + 1763304224, + 1760793518, + 1758275445, + 1755750017, + 1753217244, + 1750677137, + 1748129707, + 1745574963, + 1743012918, + 1740443581, + 1737866963, + 1735283075, + 1732691928, + 1730093532, + 1727487899, + 1724875040, + 1722254965, + 1719627685, + 1716993211, + 1714351555, + 1711702727, + 1709046739, + 1706383601, + 1703713325, + 1701035922, + 1698351403, + 1695659779, + 1692961062, + 1690255263, + 1687542393, + 1684822463, + 1682095486, + 1679361471, + 1676620432, + 1673872378, + 1671117323, + 1668355276, + 1665586251, + 1662810258, + 1660027308, + 1657237415, + 1654440588, + 1651636841, + 1648826185, + 1646008631, + 1643184191, + 1640352877, + 1637514702, + 1634669676, + 1631817811, + 1628959121, + 1626093616, + 1623221309, + 1620342211, + 1617456335, + 1614563692, + 1611664296, + 1608758157, + 1605845289, + 1602925703, + 1599999411, + 1597066426, + 1594126760, + 1591180426, + 1588227435, + 1585267800, + 1582301533, + 1579328647, + 1576349155, + 1573363068, + 1570370399, + 1567371161, + 1564365367, + 1561353028, + 1558334157, + 1555308768, + 1552276872, + 1549238483, + 1546193612, + 1543142274, + 1540084480, + 1537020244, + 1533949577, + 1530872494, + 1527789007, + 1524699129, + 1521602872, + 1518500250, + 1515391276, + 1512275962, + 1509154322, + 1506026369, + 1502892116, + 1499751576, + 1496604762, + 1493451687, + 1490292364, + 1487126808, + 1483955030, + 1480777044, + 1477592864, + 1474402503, + 1471205974, + 1468003290, + 1464794466, + 1461579514, + 1458358447, + 1455131280, + 1451898025, + 1448658697, + 1445413309, + 1442161874, + 1438904406, + 1435640919, + 1432371426, + 1429095941, + 1425814478, + 1422527051, + 1419233672, + 1415934356, + 1412629117, + 1409317969, + 1406000925, + 1402678000, + 1399349206, + 1396014559, + 1392674072, + 1389327759, + 1385975633, + 1382617710, + 1379254004, + 1375884527, + 1372509294, + 1369128320, + 1365741619, + 1362349204, + 1358951090, + 1355547292, + 1352137822, + 1348722696, + 1345301929, + 1341875533, + 1338443524, + 1335005916, + 1331562723, + 1328113960, + 1324659641, + 1321199781, + 1317734393, + 1314263493, + 1310787095, + 1307305214, + 1303817864, + 1300325060, + 1296826816, + 1293323147, + 1289814068, + 1286299593, + 1282779738, + 1279254516, + 1275723942, + 1272188032, + 1268646800, + 1265100260, + 1261548429, + 1257991320, + 1254428948, + 1250861329, + 1247288478, + 1243710408, + 1240127136, + 1236538675, + 1232945043, + 1229346252, + 1225742318, + 1222133257, + 1218519084, + 1214899813, + 1211275460, + 1207646039, + 1204011567, + 1200372058, + 1196727527, + 1193077991, + 1189423463, + 1185763960, + 1182099496, + 1178430087, + 1174755748, + 1171076495, + 1167392344, + 1163703308, + 1160009405, + 1156310649, + 1152607055, + 1148898640, + 1145185419, + 1141467408, + 1137744621, + 1134017074, + 1130284784, + 1126547765, + 1122806034, + 1119059606, + 1115308496, + 1111552721, + 1107792296, + 1104027237, + 1100257559, + 1096483278, + 1092704411, + 1088920972, + 1085132978, + 1081340445, + 1077543388, + 1073741824, + 1069935768, + 1066125236, + 1062310244, + 1058490808, + 1054666944, + 1050838668, + 1047005996, + 1043168945, + 1039327529, + 1035481766, + 1031631671, + 1027777260, + 1023918550, + 1020055556, + 1016188296, + 1012316784, + 1008441038, + 1004561072, + 1000676905, + 996788551, + 992896028, + 988999351, + 985098537, + 981193602, + 977284562, + 973371434, + 969454234, + 965532978, + 961607684, + 957678367, + 953745043, + 949807730, + 945866443, + 941921200, + 937972016, + 934018909, + 930061894, + 926100989, + 922136209, + 918167572, + 914195094, + 910218791, + 906238681, + 902254780, + 898267104, + 894275671, + 890280497, + 886281598, + 882278992, + 878272695, + 874262724, + 870249095, + 866231826, + 862210934, + 858186435, + 854158345, + 850126682, + 846091463, + 842052705, + 838010424, + 833964638, + 829915362, + 825862615, + 821806413, + 817746774, + 813683713, + 809617249, + 805547397, + 801474176, + 797397602, + 793317693, + 789234464, + 785147934, + 781058120, + 776965038, + 772868706, + 768769141, + 764666360, + 760560380, + 756451218, + 752338892, + 748223418, + 744104815, + 739983099, + 735858287, + 731730397, + 727599446, + 723465451, + 719328430, + 715188400, + 711045377, + 706899381, + 702750427, + 698598533, + 694443717, + 690285996, + 686125387, + 681961908, + 677795576, + 673626408, + 669454423, + 665279637, + 661102068, + 656921734, + 652738651, + 648552838, + 644364312, + 640173090, + 635979190, + 631782630, + 627583426, + 623381598, + 619177161, + 614970135, + 610760536, + 606548381, + 602333690, + 598116479, + 593896765, + 589674567, + 585449903, + 581222789, + 576993244, + 572761285, + 568526931, + 564290197, + 560051104, + 555809667, + 551565905, + 547319836, + 543071478, + 538820847, + 534567963, + 530312842, + 526055503, + 521795963, + 517534240, + 513270353, + 509004318, + 504736154, + 500465878, + 496193509, + 491919064, + 487642562, + 483364019, + 479083454, + 474800886, + 470516330, + 466229807, + 461941333, + 457650927, + 453358607, + 449064389, + 444768294, + 440470337, + 436170538, + 431868915, + 427565485, + 423260266, + 418953276, + 414644534, + 410334058, + 406021865, + 401707973, + 397392401, + 393075166, + 388756287, + 384435782, + 380113669, + 375789965, + 371464690, + 367137861, + 362809495, + 358479612, + 354148230, + 349815365, + 345481038, + 341145265, + 336808065, + 332469456, + 328129457, + 323788084, + 319445358, + 315101295, + 310755913, + 306409232, + 302061269, + 297712042, + 293361570, + 289009871, + 284656963, + 280302863, + 275947592, + 271591166, + 267233603, + 262874923, + 258515144, + 254154282, + 249792358, + 245429388, + 241065392, + 236700388, + 232334393, + 227967426, + 223599506, + 219230650, + 214860878, + 210490206, + 206118654, + 201746240, + 197372981, + 192998897, + 188624006, + 184248325, + 179871874, + 175494670, + 171116733, + 166738079, + 162358728, + 157978697, + 153598006, + 149216672, + 144834714, + 140452151, + 136068999, + 131685278, + 127301007, + 122916203, + 118530885, + 114145071, + 109758779, + 105372028, + 100984837, + 96597223, + 92209205, + 87820801, + 83432030, + 79042909, + 74653459, + 70263695, + 65873638, + 61483306, + 57092716, + 52701887, + 48310838, + 43919586, + 39528151, + 35136551, + 30744804, + 26352928, + 21960942, + 17568864, + 13176712, + 8784505, + 4392262, + 0, + -4392262, + -8784505, + -13176712, + -17568864, + -21960942, + -26352928, + -30744804, + -35136551, + -39528151, + -43919586, + -48310838, + -52701887, + -57092716, + -61483306, + -65873638, + -70263695, + -74653459, + -79042909, + -83432030, + -87820801, + -92209205, + -96597223, + -100984837, + -105372028, + -109758779, + -114145071, + -118530885, + -122916203, + -127301007, + -131685278, + -136068999, + -140452151, + -144834714, + -149216672, + -153598006, + -157978697, + -162358728, + -166738079, + -171116733, + -175494670, + -179871874, + -184248325, + -188624006, + -192998897, + -197372981, + -201746240, + -206118654, + -210490206, + -214860878, + -219230650, + -223599506, + -227967426, + -232334393, + -236700388, + -241065392, + -245429388, + -249792358, + -254154282, + -258515144, + -262874923, + -267233603, + -271591166, + -275947592, + -280302863, + -284656963, + -289009871, + -293361570, + -297712042, + -302061269, + -306409232, + -310755913, + -315101295, + -319445358, + -323788084, + -328129457, + -332469456, + -336808065, + -341145265, + -345481038, + -349815365, + -354148230, + -358479612, + -362809495, + -367137861, + -371464690, + -375789965, + -380113669, + -384435782, + -388756287, + -393075166, + -397392401, + -401707973, + -406021865, + -410334058, + -414644534, + -418953276, + -423260266, + -427565485, + -431868915, + -436170538, + -440470337, + -444768294, + -449064389, + -453358607, + -457650927, + -461941333, + -466229807, + -470516330, + -474800886, + -479083454, + -483364019, + -487642562, + -491919064, + -496193509, + -500465878, + -504736154, + -509004318, + -513270353, + -517534240, + -521795963, + -526055503, + -530312842, + -534567963, + -538820847, + -543071478, + -547319836, + -551565905, + -555809667, + -560051104, + -564290197, + -568526931, + -572761285, + -576993244, + -581222789, + -585449903, + -589674567, + -593896765, + -598116479, + -602333690, + -606548381, + -610760536, + -614970135, + -619177161, + -623381598, + -627583426, + -631782630, + -635979190, + -640173090, + -644364312, + -648552838, + -652738651, + -656921734, + -661102068, + -665279637, + -669454423, + -673626408, + -677795576, + -681961908, + -686125387, + -690285996, + -694443717, + -698598533, + -702750427, + -706899381, + -711045377, + -715188400, + -719328430, + -723465451, + -727599446, + -731730397, + -735858287, + -739983099, + -744104815, + -748223418, + -752338892, + -756451218, + -760560380, + -764666360, + -768769141, + -772868706, + -776965038, + -781058120, + -785147934, + -789234464, + -793317693, + -797397602, + -801474176, + -805547397, + -809617249, + -813683713, + -817746774, + -821806413, + -825862615, + -829915362, + -833964638, + -838010424, + -842052705, + -846091463, + -850126682, + -854158345, + -858186435, + -862210934, + -866231826, + -870249095, + -874262724, + -878272695, + -882278992, + -886281598, + -890280497, + -894275671, + -898267104, + -902254780, + -906238681, + -910218791, + -914195094, + -918167572, + -922136209, + -926100989, + -930061894, + -934018909, + -937972016, + -941921200, + -945866443, + -949807730, + -953745043, + -957678367, + -961607684, + -965532978, + -969454234, + -973371434, + -977284562, + -981193602, + -985098537, + -988999351, + -992896028, + -996788551, + -1000676905, + -1004561072, + -1008441038, + -1012316784, + -1016188296, + -1020055556, + -1023918550, + -1027777260, + -1031631671, + -1035481766, + -1039327529, + -1043168945, + -1047005996, + -1050838668, + -1054666944, + -1058490808, + -1062310244, + -1066125236, + -1069935768, + -1073741824, + -1077543388, + -1081340445, + -1085132978, + -1088920972, + -1092704411, + -1096483278, + -1100257559, + -1104027237, + -1107792296, + -1111552721, + -1115308496, + -1119059606, + -1122806034, + -1126547765, + -1130284784, + -1134017074, + -1137744621, + -1141467408, + -1145185419, + -1148898640, + -1152607055, + -1156310649, + -1160009405, + -1163703308, + -1167392344, + -1171076495, + -1174755748, + -1178430087, + -1182099496, + -1185763960, + -1189423463, + -1193077991, + -1196727527, + -1200372058, + -1204011567, + -1207646039, + -1211275460, + -1214899813, + -1218519084, + -1222133257, + -1225742318, + -1229346252, + -1232945043, + -1236538675, + -1240127136, + -1243710408, + -1247288478, + -1250861329, + -1254428948, + -1257991320, + -1261548429, + -1265100260, + -1268646800, + -1272188032, + -1275723942, + -1279254516, + -1282779738, + -1286299593, + -1289814068, + -1293323147, + -1296826816, + -1300325060, + -1303817864, + -1307305214, + -1310787095, + -1314263493, + -1317734393, + -1321199781, + -1324659641, + -1328113960, + -1331562723, + -1335005916, + -1338443524, + -1341875533, + -1345301929, + -1348722696, + -1352137822, + -1355547292, + -1358951090, + -1362349204, + -1365741619, + -1369128320, + -1372509294, + -1375884527, + -1379254004, + -1382617710, + -1385975633, + -1389327759, + -1392674072, + -1396014559, + -1399349206, + -1402678000, + -1406000925, + -1409317969, + -1412629117, + -1415934356, + -1419233672, + -1422527051, + -1425814478, + -1429095941, + -1432371426, + -1435640919, + -1438904406, + -1442161874, + -1445413309, + -1448658697, + -1451898025, + -1455131280, + -1458358447, + -1461579514, + -1464794466, + -1468003290, + -1471205974, + -1474402503, + -1477592864, + -1480777044, + -1483955030, + -1487126808, + -1490292364, + -1493451687, + -1496604762, + -1499751576, + -1502892116, + -1506026369, + -1509154322, + -1512275962, + -1515391276, + -1518500250, + -1521602872, + -1524699129, + -1527789007, + -1530872494, + -1533949577, + -1537020244, + -1540084480, + -1543142274, + -1546193612, + -1549238483, + -1552276872, + -1555308768, + -1558334157, + -1561353028, + -1564365367, + -1567371161, + -1570370399, + -1573363068, + -1576349155, + -1579328647, + -1582301533, + -1585267800, + -1588227435, + -1591180426, + -1594126760, + -1597066426, + -1599999411, + -1602925703, + -1605845289, + -1608758157, + -1611664296, + -1614563692, + -1617456335, + -1620342211, + -1623221309, + -1626093616, + -1628959121, + -1631817811, + -1634669676, + -1637514702, + -1640352877, + -1643184191, + -1646008631, + -1648826185, + -1651636841, + -1654440588, + -1657237415, + -1660027308, + -1662810258, + -1665586251, + -1668355276, + -1671117323, + -1673872378, + -1676620432, + -1679361471, + -1682095486, + -1684822463, + -1687542393, + -1690255263, + -1692961062, + -1695659779, + -1698351403, + -1701035922, + -1703713325, + -1706383601, + -1709046739, + -1711702727, + -1714351555, + -1716993211, + -1719627685, + -1722254965, + -1724875040, + -1727487899, + -1730093532, + -1732691928, + -1735283075, + -1737866963, + -1740443581, + -1743012918, + -1745574963, + -1748129707, + -1750677137, + -1753217244, + -1755750017, + -1758275445, + -1760793518, + -1763304224, + -1765807555, + -1768303498, + -1770792044, + -1773273182, + -1775746903, + -1778213194, + -1780672048, + -1783123452, + -1785567396, + -1788003871, + -1790432867, + -1792854372, + -1795268378, + -1797674873, + -1800073849, + -1802465294, + -1804849198, + -1807225553, + -1809594347, + -1811955572, + -1814309216, + -1816655271, + -1818993726, + -1821324572, + -1823647799, + -1825963397, + -1828271356, + -1830571667, + -1832864320, + -1835149306, + -1837426615, + -1839696238, + -1841958164, + -1844212386, + -1846458892, + -1848697674, + -1850928722, + -1853152028, + -1855367581, + -1857575372, + -1859775393, + -1861967634, + -1864152086, + -1866328740, + -1868497586, + -1870658615, + -1872811820, + -1874957189, + -1877094716, + -1879224389, + -1881346202, + -1883460144, + -1885566207, + -1887664383, + -1889754661, + -1891837035, + -1893911494, + -1895978031, + -1898036636, + -1900087301, + -1902130017, + -1904164776, + -1906191570, + -1908210390, + -1910221227, + -1912224073, + -1914218919, + -1916205758, + -1918184581, + -1920155379, + -1922118145, + -1924072871, + -1926019547, + -1927958166, + -1929888720, + -1931811201, + -1933725600, + -1935631910, + -1937530123, + -1939420231, + -1941302225, + -1943176098, + -1945041843, + -1946899451, + -1948748914, + -1950590226, + -1952423377, + -1954248361, + -1956065170, + -1957873796, + -1959674231, + -1961466469, + -1963250501, + -1965026321, + -1966793920, + -1968553292, + -1970304428, + -1972047323, + -1973781967, + -1975508355, + -1977226479, + -1978936331, + -1980637905, + -1982331193, + -1984016189, + -1985692885, + -1987361274, + -1989021350, + -1990673105, + -1992316532, + -1993951625, + -1995578377, + -1997196780, + -1998806829, + -2000408516, + -2002001835, + -2003586779, + -2005163342, + -2006731516, + -2008291295, + -2009842674, + -2011385644, + -2012920201, + -2014446336, + -2015964045, + -2017473321, + -2018974156, + -2020466546, + -2021950484, + -2023425963, + -2024892978, + -2026351522, + -2027801589, + -2029243173, + -2030676269, + -2032100869, + -2033516969, + -2034924562, + -2036323642, + -2037714204, + -2039096241, + -2040469748, + -2041834720, + -2043191150, + -2044539032, + -2045878362, + -2047209133, + -2048531340, + -2049844978, + -2051150040, + -2052446522, + -2053734418, + -2055013723, + -2056284431, + -2057546537, + -2058800036, + -2060044922, + -2061281190, + -2062508835, + -2063727853, + -2064938237, + -2066139983, + -2067333086, + -2068517540, + -2069693342, + -2070860485, + -2072018965, + -2073168777, + -2074309917, + -2075442379, + -2076566160, + -2077681253, + -2078787655, + -2079885360, + -2080974365, + -2082054665, + -2083126254, + -2084189130, + -2085243286, + -2086288720, + -2087325426, + -2088353400, + -2089372638, + -2090383135, + -2091384888, + -2092377892, + -2093362143, + -2094337637, + -2095304370, + -2096262337, + -2097211535, + -2098151960, + -2099083608, + -2100006474, + -2100920556, + -2101825849, + -2102722350, + -2103610054, + -2104488958, + -2105359059, + -2106220352, + -2107072834, + -2107916502, + -2108751352, + -2109577380, + -2110394584, + -2111202959, + -2112002502, + -2112793210, + -2113575080, + -2114348108, + -2115112291, + -2115867626, + -2116614110, + -2117351739, + -2118080511, + -2118800422, + -2119511470, + -2120213651, + -2120906963, + -2121591402, + -2122266967, + -2122933653, + -2123591458, + -2124240380, + -2124880416, + -2125511562, + -2126133817, + -2126747178, + -2127351642, + -2127947206, + -2128533869, + -2129111628, + -2129680480, + -2130240422, + -2130791454, + -2131333572, + -2131866773, + -2132391057, + -2132906420, + -2133412861, + -2133910377, + -2134398966, + -2134878626, + -2135349356, + -2135811153, + -2136264015, + -2136707940, + -2137142927, + -2137568974, + -2137986079, + -2138394240, + -2138793455, + -2139183723, + -2139565043, + -2139937412, + -2140300829, + -2140655293, + -2141000801, + -2141337354, + -2141664948, + -2141983583, + -2142293258, + -2142593971, + -2142885721, + -2143168506, + -2143442326, + -2143707180, + -2143963065, + -2144209982, + -2144447929, + -2144676905, + -2144896910, + -2145107942, + -2145310000, + -2145503083, + -2145687192, + -2145862324, + -2146028480, + -2146185658, + -2146333858, + -2146473080, + -2146603322, + -2146724584, + -2146836866, + -2146940167, + -2147034487, + -2147119825, + -2147196181, + -2147263555, + -2147321946, + -2147371355, + -2147411780, + -2147443222, + -2147465681, + -2147479156, + -2147483647, + -2147479156, + -2147465681, + -2147443222, + -2147411780, + -2147371355, + -2147321946, + -2147263555, + -2147196181, + -2147119825, + -2147034487, + -2146940167, + -2146836866, + -2146724584, + -2146603322, + -2146473080, + -2146333858, + -2146185658, + -2146028480, + -2145862324, + -2145687192, + -2145503083, + -2145310000, + -2145107942, + -2144896910, + -2144676905, + -2144447929, + -2144209982, + -2143963065, + -2143707180, + -2143442326, + -2143168506, + -2142885721, + -2142593971, + -2142293258, + -2141983583, + -2141664948, + -2141337354, + -2141000801, + -2140655293, + -2140300829, + -2139937412, + -2139565043, + -2139183723, + -2138793455, + -2138394240, + -2137986079, + -2137568974, + -2137142927, + -2136707940, + -2136264015, + -2135811153, + -2135349356, + -2134878626, + -2134398966, + -2133910377, + -2133412861, + -2132906420, + -2132391057, + -2131866773, + -2131333572, + -2130791454, + -2130240422, + -2129680480, + -2129111628, + -2128533869, + -2127947206, + -2127351642, + -2126747178, + -2126133817, + -2125511562, + -2124880416, + -2124240380, + -2123591458, + -2122933653, + -2122266967, + -2121591402, + -2120906963, + -2120213651, + -2119511470, + -2118800422, + -2118080511, + -2117351739, + -2116614110, + -2115867626, + -2115112291, + -2114348108, + -2113575080, + -2112793210, + -2112002502, + -2111202959, + -2110394584, + -2109577380, + -2108751352, + -2107916502, + -2107072834, + -2106220352, + -2105359059, + -2104488958, + -2103610054, + -2102722350, + -2101825849, + -2100920556, + -2100006474, + -2099083608, + -2098151960, + -2097211535, + -2096262337, + -2095304370, + -2094337637, + -2093362143, + -2092377892, + -2091384888, + -2090383135, + -2089372638, + -2088353400, + -2087325426, + -2086288720, + -2085243286, + -2084189130, + -2083126254, + -2082054665, + -2080974365, + -2079885360, + -2078787655, + -2077681253, + -2076566160, + -2075442379, + -2074309917, + -2073168777, + -2072018965, + -2070860485, + -2069693342, + -2068517540, + -2067333086, + -2066139983, + -2064938237, + -2063727853, + -2062508835, + -2061281190, + -2060044922, + -2058800036, + -2057546537, + -2056284431, + -2055013723, + -2053734418, + -2052446522, + -2051150040, + -2049844978, + -2048531340, + -2047209133, + -2045878362, + -2044539032, + -2043191150, + -2041834720, + -2040469748, + -2039096241, + -2037714204, + -2036323642, + -2034924562, + -2033516969, + -2032100869, + -2030676269, + -2029243173, + -2027801589, + -2026351522, + -2024892978, + -2023425963, + -2021950484, + -2020466546, + -2018974156, + -2017473321, + -2015964045, + -2014446336, + -2012920201, + -2011385644, + -2009842674, + -2008291295, + -2006731516, + -2005163342, + -2003586779, + -2002001835, + -2000408516, + -1998806829, + -1997196780, + -1995578377, + -1993951625, + -1992316532, + -1990673105, + -1989021350, + -1987361274, + -1985692885, + -1984016189, + -1982331193, + -1980637905, + -1978936331, + -1977226479, + -1975508355, + -1973781967, + -1972047323, + -1970304428, + -1968553292, + -1966793920, + -1965026321, + -1963250501, + -1961466469, + -1959674231, + -1957873796, + -1956065170, + -1954248361, + -1952423377, + -1950590226, + -1948748914, + -1946899451, + -1945041843, + -1943176098, + -1941302225, + -1939420231, + -1937530123, + -1935631910, + -1933725600, + -1931811201, + -1929888720, + -1927958166, + -1926019547, + -1924072871, + -1922118145, + -1920155379, + -1918184581, + -1916205758, + -1914218919, + -1912224073, + -1910221227, + -1908210390, + -1906191570, + -1904164776, + -1902130017, + -1900087301, + -1898036636, + -1895978031, + -1893911494, + -1891837035, + -1889754661, + -1887664383, + -1885566207, + -1883460144, + -1881346202, + -1879224389, + -1877094716, + -1874957189, + -1872811820, + -1870658615, + -1868497586, + -1866328740, + -1864152086, + -1861967634, + -1859775393, + -1857575372, + -1855367581, + -1853152028, + -1850928722, + -1848697674, + -1846458892, + -1844212386, + -1841958164, + -1839696238, + -1837426615, + -1835149306, + -1832864320, + -1830571667, + -1828271356, + -1825963397, + -1823647799, + -1821324572, + -1818993726, + -1816655271, + -1814309216, + -1811955572, + -1809594347, + -1807225553, + -1804849198, + -1802465294, + -1800073849, + -1797674873, + -1795268378, + -1792854372, + -1790432867, + -1788003871, + -1785567396, + -1783123452, + -1780672048, + -1778213194, + -1775746903, + -1773273182, + -1770792044, + -1768303498, + -1765807555, + -1763304224, + -1760793518, + -1758275445, + -1755750017, + -1753217244, + -1750677137, + -1748129707, + -1745574963, + -1743012918, + -1740443581, + -1737866963, + -1735283075, + -1732691928, + -1730093532, + -1727487899, + -1724875040, + -1722254965, + -1719627685, + -1716993211, + -1714351555, + -1711702727, + -1709046739, + -1706383601, + -1703713325, + -1701035922, + -1698351403, + -1695659779, + -1692961062, + -1690255263, + -1687542393, + -1684822463, + -1682095486, + -1679361471, + -1676620432, + -1673872378, + -1671117323, + -1668355276, + -1665586251, + -1662810258, + -1660027308, + -1657237415, + -1654440588, + -1651636841, + -1648826185, + -1646008631, + -1643184191, + -1640352877, + -1637514702, + -1634669676, + -1631817811, + -1628959121, + -1626093616, + -1623221309, + -1620342211, + -1617456335, + -1614563692, + -1611664296, + -1608758157, + -1605845289, + -1602925703, + -1599999411, + -1597066426, + -1594126760, + -1591180426, + -1588227435, + -1585267800, + -1582301533, + -1579328647, + -1576349155, + -1573363068, + -1570370399, + -1567371161, + -1564365367, + -1561353028, + -1558334157, + -1555308768, + -1552276872, + -1549238483, + -1546193612, + -1543142274, + -1540084480, + -1537020244, + -1533949577, + -1530872494, + -1527789007, + -1524699129, + -1521602872, + -1518500250, + -1515391276, + -1512275962, + -1509154322, + -1506026369, + -1502892116, + -1499751576, + -1496604762, + -1493451687, + -1490292364, + -1487126808, + -1483955030, + -1480777044, + -1477592864, + -1474402503, + -1471205974, + -1468003290, + -1464794466, + -1461579514, + -1458358447, + -1455131280, + -1451898025, + -1448658697, + -1445413309, + -1442161874, + -1438904406, + -1435640919, + -1432371426, + -1429095941, + -1425814478, + -1422527051, + -1419233672, + -1415934356, + -1412629117, + -1409317969, + -1406000925, + -1402678000, + -1399349206, + -1396014559, + -1392674072, + -1389327759, + -1385975633, + -1382617710, + -1379254004, + -1375884527, + -1372509294, + -1369128320, + -1365741619, + -1362349204, + -1358951090, + -1355547292, + -1352137822, + -1348722696, + -1345301929, + -1341875533, + -1338443524, + -1335005916, + -1331562723, + -1328113960, + -1324659641, + -1321199781, + -1317734393, + -1314263493, + -1310787095, + -1307305214, + -1303817864, + -1300325060, + -1296826816, + -1293323147, + -1289814068, + -1286299593, + -1282779738, + -1279254516, + -1275723942, + -1272188032, + -1268646800, + -1265100260, + -1261548429, + -1257991320, + -1254428948, + -1250861329, + -1247288478, + -1243710408, + -1240127136, + -1236538675, + -1232945043, + -1229346252, + -1225742318, + -1222133257, + -1218519084, + -1214899813, + -1211275460, + -1207646039, + -1204011567, + -1200372058, + -1196727527, + -1193077991, + -1189423463, + -1185763960, + -1182099496, + -1178430087, + -1174755748, + -1171076495, + -1167392344, + -1163703308, + -1160009405, + -1156310649, + -1152607055, + -1148898640, + -1145185419, + -1141467408, + -1137744621, + -1134017074, + -1130284784, + -1126547765, + -1122806034, + -1119059606, + -1115308496, + -1111552721, + -1107792296, + -1104027237, + -1100257559, + -1096483278, + -1092704411, + -1088920972, + -1085132978, + -1081340445, + -1077543388, +}; + +/* in Q1.31, generated from sin(i * 2 * pi / FFT_SIZE_MAX) */ +const int32_t multi_twiddle_imag_32[FFT_MULTI_TWIDDLE_SIZE] = { + 0, + -4392262, + -8784505, + -13176712, + -17568864, + -21960942, + -26352928, + -30744804, + -35136551, + -39528151, + -43919586, + -48310838, + -52701887, + -57092716, + -61483306, + -65873638, + -70263695, + -74653459, + -79042909, + -83432030, + -87820801, + -92209205, + -96597223, + -100984837, + -105372028, + -109758779, + -114145071, + -118530885, + -122916203, + -127301007, + -131685278, + -136068999, + -140452151, + -144834714, + -149216672, + -153598006, + -157978697, + -162358728, + -166738079, + -171116733, + -175494670, + -179871874, + -184248325, + -188624006, + -192998897, + -197372981, + -201746240, + -206118654, + -210490206, + -214860878, + -219230650, + -223599506, + -227967426, + -232334393, + -236700388, + -241065392, + -245429388, + -249792358, + -254154282, + -258515144, + -262874923, + -267233603, + -271591166, + -275947592, + -280302863, + -284656963, + -289009871, + -293361570, + -297712042, + -302061269, + -306409232, + -310755913, + -315101295, + -319445358, + -323788084, + -328129457, + -332469456, + -336808065, + -341145265, + -345481038, + -349815365, + -354148230, + -358479612, + -362809495, + -367137861, + -371464690, + -375789965, + -380113669, + -384435782, + -388756287, + -393075166, + -397392401, + -401707973, + -406021865, + -410334058, + -414644534, + -418953276, + -423260266, + -427565485, + -431868915, + -436170538, + -440470337, + -444768294, + -449064389, + -453358607, + -457650927, + -461941333, + -466229807, + -470516330, + -474800886, + -479083454, + -483364019, + -487642562, + -491919064, + -496193509, + -500465878, + -504736154, + -509004318, + -513270353, + -517534240, + -521795963, + -526055503, + -530312842, + -534567963, + -538820847, + -543071478, + -547319836, + -551565905, + -555809667, + -560051104, + -564290197, + -568526931, + -572761285, + -576993244, + -581222789, + -585449903, + -589674567, + -593896765, + -598116479, + -602333690, + -606548381, + -610760536, + -614970135, + -619177161, + -623381598, + -627583426, + -631782630, + -635979190, + -640173090, + -644364312, + -648552838, + -652738651, + -656921734, + -661102068, + -665279637, + -669454423, + -673626408, + -677795576, + -681961908, + -686125387, + -690285996, + -694443717, + -698598533, + -702750427, + -706899381, + -711045377, + -715188400, + -719328430, + -723465451, + -727599446, + -731730397, + -735858287, + -739983099, + -744104815, + -748223418, + -752338892, + -756451218, + -760560380, + -764666360, + -768769141, + -772868706, + -776965038, + -781058120, + -785147934, + -789234464, + -793317693, + -797397602, + -801474176, + -805547397, + -809617249, + -813683713, + -817746774, + -821806413, + -825862615, + -829915362, + -833964638, + -838010424, + -842052705, + -846091463, + -850126682, + -854158345, + -858186435, + -862210934, + -866231826, + -870249095, + -874262724, + -878272695, + -882278992, + -886281598, + -890280497, + -894275671, + -898267104, + -902254780, + -906238681, + -910218791, + -914195094, + -918167572, + -922136209, + -926100989, + -930061894, + -934018909, + -937972016, + -941921200, + -945866443, + -949807730, + -953745043, + -957678367, + -961607684, + -965532978, + -969454234, + -973371434, + -977284562, + -981193602, + -985098537, + -988999351, + -992896028, + -996788551, + -1000676905, + -1004561072, + -1008441038, + -1012316784, + -1016188296, + -1020055556, + -1023918550, + -1027777260, + -1031631671, + -1035481766, + -1039327529, + -1043168945, + -1047005996, + -1050838668, + -1054666944, + -1058490808, + -1062310244, + -1066125236, + -1069935768, + -1073741824, + -1077543388, + -1081340445, + -1085132978, + -1088920972, + -1092704411, + -1096483278, + -1100257559, + -1104027237, + -1107792296, + -1111552721, + -1115308496, + -1119059606, + -1122806034, + -1126547765, + -1130284784, + -1134017074, + -1137744621, + -1141467408, + -1145185419, + -1148898640, + -1152607055, + -1156310649, + -1160009405, + -1163703308, + -1167392344, + -1171076495, + -1174755748, + -1178430087, + -1182099496, + -1185763960, + -1189423463, + -1193077991, + -1196727527, + -1200372058, + -1204011567, + -1207646039, + -1211275460, + -1214899813, + -1218519084, + -1222133257, + -1225742318, + -1229346252, + -1232945043, + -1236538675, + -1240127136, + -1243710408, + -1247288478, + -1250861329, + -1254428948, + -1257991320, + -1261548429, + -1265100260, + -1268646800, + -1272188032, + -1275723942, + -1279254516, + -1282779738, + -1286299593, + -1289814068, + -1293323147, + -1296826816, + -1300325060, + -1303817864, + -1307305214, + -1310787095, + -1314263493, + -1317734393, + -1321199781, + -1324659641, + -1328113960, + -1331562723, + -1335005916, + -1338443524, + -1341875533, + -1345301929, + -1348722696, + -1352137822, + -1355547292, + -1358951090, + -1362349204, + -1365741619, + -1369128320, + -1372509294, + -1375884527, + -1379254004, + -1382617710, + -1385975633, + -1389327759, + -1392674072, + -1396014559, + -1399349206, + -1402678000, + -1406000925, + -1409317969, + -1412629117, + -1415934356, + -1419233672, + -1422527051, + -1425814478, + -1429095941, + -1432371426, + -1435640919, + -1438904406, + -1442161874, + -1445413309, + -1448658697, + -1451898025, + -1455131280, + -1458358447, + -1461579514, + -1464794466, + -1468003290, + -1471205974, + -1474402503, + -1477592864, + -1480777044, + -1483955030, + -1487126808, + -1490292364, + -1493451687, + -1496604762, + -1499751576, + -1502892116, + -1506026369, + -1509154322, + -1512275962, + -1515391276, + -1518500250, + -1521602872, + -1524699129, + -1527789007, + -1530872494, + -1533949577, + -1537020244, + -1540084480, + -1543142274, + -1546193612, + -1549238483, + -1552276872, + -1555308768, + -1558334157, + -1561353028, + -1564365367, + -1567371161, + -1570370399, + -1573363068, + -1576349155, + -1579328647, + -1582301533, + -1585267800, + -1588227435, + -1591180426, + -1594126760, + -1597066426, + -1599999411, + -1602925703, + -1605845289, + -1608758157, + -1611664296, + -1614563692, + -1617456335, + -1620342211, + -1623221309, + -1626093616, + -1628959121, + -1631817811, + -1634669676, + -1637514702, + -1640352877, + -1643184191, + -1646008631, + -1648826185, + -1651636841, + -1654440588, + -1657237415, + -1660027308, + -1662810258, + -1665586251, + -1668355276, + -1671117323, + -1673872378, + -1676620432, + -1679361471, + -1682095486, + -1684822463, + -1687542393, + -1690255263, + -1692961062, + -1695659779, + -1698351403, + -1701035922, + -1703713325, + -1706383601, + -1709046739, + -1711702727, + -1714351555, + -1716993211, + -1719627685, + -1722254965, + -1724875040, + -1727487899, + -1730093532, + -1732691928, + -1735283075, + -1737866963, + -1740443581, + -1743012918, + -1745574963, + -1748129707, + -1750677137, + -1753217244, + -1755750017, + -1758275445, + -1760793518, + -1763304224, + -1765807555, + -1768303498, + -1770792044, + -1773273182, + -1775746903, + -1778213194, + -1780672048, + -1783123452, + -1785567396, + -1788003871, + -1790432867, + -1792854372, + -1795268378, + -1797674873, + -1800073849, + -1802465294, + -1804849198, + -1807225553, + -1809594347, + -1811955572, + -1814309216, + -1816655271, + -1818993726, + -1821324572, + -1823647799, + -1825963397, + -1828271356, + -1830571667, + -1832864320, + -1835149306, + -1837426615, + -1839696238, + -1841958164, + -1844212386, + -1846458892, + -1848697674, + -1850928722, + -1853152028, + -1855367581, + -1857575372, + -1859775393, + -1861967634, + -1864152086, + -1866328740, + -1868497586, + -1870658615, + -1872811820, + -1874957189, + -1877094716, + -1879224389, + -1881346202, + -1883460144, + -1885566207, + -1887664383, + -1889754661, + -1891837035, + -1893911494, + -1895978031, + -1898036636, + -1900087301, + -1902130017, + -1904164776, + -1906191570, + -1908210390, + -1910221227, + -1912224073, + -1914218919, + -1916205758, + -1918184581, + -1920155379, + -1922118145, + -1924072871, + -1926019547, + -1927958166, + -1929888720, + -1931811201, + -1933725600, + -1935631910, + -1937530123, + -1939420231, + -1941302225, + -1943176098, + -1945041843, + -1946899451, + -1948748914, + -1950590226, + -1952423377, + -1954248361, + -1956065170, + -1957873796, + -1959674231, + -1961466469, + -1963250501, + -1965026321, + -1966793920, + -1968553292, + -1970304428, + -1972047323, + -1973781967, + -1975508355, + -1977226479, + -1978936331, + -1980637905, + -1982331193, + -1984016189, + -1985692885, + -1987361274, + -1989021350, + -1990673105, + -1992316532, + -1993951625, + -1995578377, + -1997196780, + -1998806829, + -2000408516, + -2002001835, + -2003586779, + -2005163342, + -2006731516, + -2008291295, + -2009842674, + -2011385644, + -2012920201, + -2014446336, + -2015964045, + -2017473321, + -2018974156, + -2020466546, + -2021950484, + -2023425963, + -2024892978, + -2026351522, + -2027801589, + -2029243173, + -2030676269, + -2032100869, + -2033516969, + -2034924562, + -2036323642, + -2037714204, + -2039096241, + -2040469748, + -2041834720, + -2043191150, + -2044539032, + -2045878362, + -2047209133, + -2048531340, + -2049844978, + -2051150040, + -2052446522, + -2053734418, + -2055013723, + -2056284431, + -2057546537, + -2058800036, + -2060044922, + -2061281190, + -2062508835, + -2063727853, + -2064938237, + -2066139983, + -2067333086, + -2068517540, + -2069693342, + -2070860485, + -2072018965, + -2073168777, + -2074309917, + -2075442379, + -2076566160, + -2077681253, + -2078787655, + -2079885360, + -2080974365, + -2082054665, + -2083126254, + -2084189130, + -2085243286, + -2086288720, + -2087325426, + -2088353400, + -2089372638, + -2090383135, + -2091384888, + -2092377892, + -2093362143, + -2094337637, + -2095304370, + -2096262337, + -2097211535, + -2098151960, + -2099083608, + -2100006474, + -2100920556, + -2101825849, + -2102722350, + -2103610054, + -2104488958, + -2105359059, + -2106220352, + -2107072834, + -2107916502, + -2108751352, + -2109577380, + -2110394584, + -2111202959, + -2112002502, + -2112793210, + -2113575080, + -2114348108, + -2115112291, + -2115867626, + -2116614110, + -2117351739, + -2118080511, + -2118800422, + -2119511470, + -2120213651, + -2120906963, + -2121591402, + -2122266967, + -2122933653, + -2123591458, + -2124240380, + -2124880416, + -2125511562, + -2126133817, + -2126747178, + -2127351642, + -2127947206, + -2128533869, + -2129111628, + -2129680480, + -2130240422, + -2130791454, + -2131333572, + -2131866773, + -2132391057, + -2132906420, + -2133412861, + -2133910377, + -2134398966, + -2134878626, + -2135349356, + -2135811153, + -2136264015, + -2136707940, + -2137142927, + -2137568974, + -2137986079, + -2138394240, + -2138793455, + -2139183723, + -2139565043, + -2139937412, + -2140300829, + -2140655293, + -2141000801, + -2141337354, + -2141664948, + -2141983583, + -2142293258, + -2142593971, + -2142885721, + -2143168506, + -2143442326, + -2143707180, + -2143963065, + -2144209982, + -2144447929, + -2144676905, + -2144896910, + -2145107942, + -2145310000, + -2145503083, + -2145687192, + -2145862324, + -2146028480, + -2146185658, + -2146333858, + -2146473080, + -2146603322, + -2146724584, + -2146836866, + -2146940167, + -2147034487, + -2147119825, + -2147196181, + -2147263555, + -2147321946, + -2147371355, + -2147411780, + -2147443222, + -2147465681, + -2147479156, + -2147483647, + -2147479156, + -2147465681, + -2147443222, + -2147411780, + -2147371355, + -2147321946, + -2147263555, + -2147196181, + -2147119825, + -2147034487, + -2146940167, + -2146836866, + -2146724584, + -2146603322, + -2146473080, + -2146333858, + -2146185658, + -2146028480, + -2145862324, + -2145687192, + -2145503083, + -2145310000, + -2145107942, + -2144896910, + -2144676905, + -2144447929, + -2144209982, + -2143963065, + -2143707180, + -2143442326, + -2143168506, + -2142885721, + -2142593971, + -2142293258, + -2141983583, + -2141664948, + -2141337354, + -2141000801, + -2140655293, + -2140300829, + -2139937412, + -2139565043, + -2139183723, + -2138793455, + -2138394240, + -2137986079, + -2137568974, + -2137142927, + -2136707940, + -2136264015, + -2135811153, + -2135349356, + -2134878626, + -2134398966, + -2133910377, + -2133412861, + -2132906420, + -2132391057, + -2131866773, + -2131333572, + -2130791454, + -2130240422, + -2129680480, + -2129111628, + -2128533869, + -2127947206, + -2127351642, + -2126747178, + -2126133817, + -2125511562, + -2124880416, + -2124240380, + -2123591458, + -2122933653, + -2122266967, + -2121591402, + -2120906963, + -2120213651, + -2119511470, + -2118800422, + -2118080511, + -2117351739, + -2116614110, + -2115867626, + -2115112291, + -2114348108, + -2113575080, + -2112793210, + -2112002502, + -2111202959, + -2110394584, + -2109577380, + -2108751352, + -2107916502, + -2107072834, + -2106220352, + -2105359059, + -2104488958, + -2103610054, + -2102722350, + -2101825849, + -2100920556, + -2100006474, + -2099083608, + -2098151960, + -2097211535, + -2096262337, + -2095304370, + -2094337637, + -2093362143, + -2092377892, + -2091384888, + -2090383135, + -2089372638, + -2088353400, + -2087325426, + -2086288720, + -2085243286, + -2084189130, + -2083126254, + -2082054665, + -2080974365, + -2079885360, + -2078787655, + -2077681253, + -2076566160, + -2075442379, + -2074309917, + -2073168777, + -2072018965, + -2070860485, + -2069693342, + -2068517540, + -2067333086, + -2066139983, + -2064938237, + -2063727853, + -2062508835, + -2061281190, + -2060044922, + -2058800036, + -2057546537, + -2056284431, + -2055013723, + -2053734418, + -2052446522, + -2051150040, + -2049844978, + -2048531340, + -2047209133, + -2045878362, + -2044539032, + -2043191150, + -2041834720, + -2040469748, + -2039096241, + -2037714204, + -2036323642, + -2034924562, + -2033516969, + -2032100869, + -2030676269, + -2029243173, + -2027801589, + -2026351522, + -2024892978, + -2023425963, + -2021950484, + -2020466546, + -2018974156, + -2017473321, + -2015964045, + -2014446336, + -2012920201, + -2011385644, + -2009842674, + -2008291295, + -2006731516, + -2005163342, + -2003586779, + -2002001835, + -2000408516, + -1998806829, + -1997196780, + -1995578377, + -1993951625, + -1992316532, + -1990673105, + -1989021350, + -1987361274, + -1985692885, + -1984016189, + -1982331193, + -1980637905, + -1978936331, + -1977226479, + -1975508355, + -1973781967, + -1972047323, + -1970304428, + -1968553292, + -1966793920, + -1965026321, + -1963250501, + -1961466469, + -1959674231, + -1957873796, + -1956065170, + -1954248361, + -1952423377, + -1950590226, + -1948748914, + -1946899451, + -1945041843, + -1943176098, + -1941302225, + -1939420231, + -1937530123, + -1935631910, + -1933725600, + -1931811201, + -1929888720, + -1927958166, + -1926019547, + -1924072871, + -1922118145, + -1920155379, + -1918184581, + -1916205758, + -1914218919, + -1912224073, + -1910221227, + -1908210390, + -1906191570, + -1904164776, + -1902130017, + -1900087301, + -1898036636, + -1895978031, + -1893911494, + -1891837035, + -1889754661, + -1887664383, + -1885566207, + -1883460144, + -1881346202, + -1879224389, + -1877094716, + -1874957189, + -1872811820, + -1870658615, + -1868497586, + -1866328740, + -1864152086, + -1861967634, + -1859775393, + -1857575372, + -1855367581, + -1853152028, + -1850928722, + -1848697674, + -1846458892, + -1844212386, + -1841958164, + -1839696238, + -1837426615, + -1835149306, + -1832864320, + -1830571667, + -1828271356, + -1825963397, + -1823647799, + -1821324572, + -1818993726, + -1816655271, + -1814309216, + -1811955572, + -1809594347, + -1807225553, + -1804849198, + -1802465294, + -1800073849, + -1797674873, + -1795268378, + -1792854372, + -1790432867, + -1788003871, + -1785567396, + -1783123452, + -1780672048, + -1778213194, + -1775746903, + -1773273182, + -1770792044, + -1768303498, + -1765807555, + -1763304224, + -1760793518, + -1758275445, + -1755750017, + -1753217244, + -1750677137, + -1748129707, + -1745574963, + -1743012918, + -1740443581, + -1737866963, + -1735283075, + -1732691928, + -1730093532, + -1727487899, + -1724875040, + -1722254965, + -1719627685, + -1716993211, + -1714351555, + -1711702727, + -1709046739, + -1706383601, + -1703713325, + -1701035922, + -1698351403, + -1695659779, + -1692961062, + -1690255263, + -1687542393, + -1684822463, + -1682095486, + -1679361471, + -1676620432, + -1673872378, + -1671117323, + -1668355276, + -1665586251, + -1662810258, + -1660027308, + -1657237415, + -1654440588, + -1651636841, + -1648826185, + -1646008631, + -1643184191, + -1640352877, + -1637514702, + -1634669676, + -1631817811, + -1628959121, + -1626093616, + -1623221309, + -1620342211, + -1617456335, + -1614563692, + -1611664296, + -1608758157, + -1605845289, + -1602925703, + -1599999411, + -1597066426, + -1594126760, + -1591180426, + -1588227435, + -1585267800, + -1582301533, + -1579328647, + -1576349155, + -1573363068, + -1570370399, + -1567371161, + -1564365367, + -1561353028, + -1558334157, + -1555308768, + -1552276872, + -1549238483, + -1546193612, + -1543142274, + -1540084480, + -1537020244, + -1533949577, + -1530872494, + -1527789007, + -1524699129, + -1521602872, + -1518500250, + -1515391276, + -1512275962, + -1509154322, + -1506026369, + -1502892116, + -1499751576, + -1496604762, + -1493451687, + -1490292364, + -1487126808, + -1483955030, + -1480777044, + -1477592864, + -1474402503, + -1471205974, + -1468003290, + -1464794466, + -1461579514, + -1458358447, + -1455131280, + -1451898025, + -1448658697, + -1445413309, + -1442161874, + -1438904406, + -1435640919, + -1432371426, + -1429095941, + -1425814478, + -1422527051, + -1419233672, + -1415934356, + -1412629117, + -1409317969, + -1406000925, + -1402678000, + -1399349206, + -1396014559, + -1392674072, + -1389327759, + -1385975633, + -1382617710, + -1379254004, + -1375884527, + -1372509294, + -1369128320, + -1365741619, + -1362349204, + -1358951090, + -1355547292, + -1352137822, + -1348722696, + -1345301929, + -1341875533, + -1338443524, + -1335005916, + -1331562723, + -1328113960, + -1324659641, + -1321199781, + -1317734393, + -1314263493, + -1310787095, + -1307305214, + -1303817864, + -1300325060, + -1296826816, + -1293323147, + -1289814068, + -1286299593, + -1282779738, + -1279254516, + -1275723942, + -1272188032, + -1268646800, + -1265100260, + -1261548429, + -1257991320, + -1254428948, + -1250861329, + -1247288478, + -1243710408, + -1240127136, + -1236538675, + -1232945043, + -1229346252, + -1225742318, + -1222133257, + -1218519084, + -1214899813, + -1211275460, + -1207646039, + -1204011567, + -1200372058, + -1196727527, + -1193077991, + -1189423463, + -1185763960, + -1182099496, + -1178430087, + -1174755748, + -1171076495, + -1167392344, + -1163703308, + -1160009405, + -1156310649, + -1152607055, + -1148898640, + -1145185419, + -1141467408, + -1137744621, + -1134017074, + -1130284784, + -1126547765, + -1122806034, + -1119059606, + -1115308496, + -1111552721, + -1107792296, + -1104027237, + -1100257559, + -1096483278, + -1092704411, + -1088920972, + -1085132978, + -1081340445, + -1077543388, + -1073741824, + -1069935768, + -1066125236, + -1062310244, + -1058490808, + -1054666944, + -1050838668, + -1047005996, + -1043168945, + -1039327529, + -1035481766, + -1031631671, + -1027777260, + -1023918550, + -1020055556, + -1016188296, + -1012316784, + -1008441038, + -1004561072, + -1000676905, + -996788551, + -992896028, + -988999351, + -985098537, + -981193602, + -977284562, + -973371434, + -969454234, + -965532978, + -961607684, + -957678367, + -953745043, + -949807730, + -945866443, + -941921200, + -937972016, + -934018909, + -930061894, + -926100989, + -922136209, + -918167572, + -914195094, + -910218791, + -906238681, + -902254780, + -898267104, + -894275671, + -890280497, + -886281598, + -882278992, + -878272695, + -874262724, + -870249095, + -866231826, + -862210934, + -858186435, + -854158345, + -850126682, + -846091463, + -842052705, + -838010424, + -833964638, + -829915362, + -825862615, + -821806413, + -817746774, + -813683713, + -809617249, + -805547397, + -801474176, + -797397602, + -793317693, + -789234464, + -785147934, + -781058120, + -776965038, + -772868706, + -768769141, + -764666360, + -760560380, + -756451218, + -752338892, + -748223418, + -744104815, + -739983099, + -735858287, + -731730397, + -727599446, + -723465451, + -719328430, + -715188400, + -711045377, + -706899381, + -702750427, + -698598533, + -694443717, + -690285996, + -686125387, + -681961908, + -677795576, + -673626408, + -669454423, + -665279637, + -661102068, + -656921734, + -652738651, + -648552838, + -644364312, + -640173090, + -635979190, + -631782630, + -627583426, + -623381598, + -619177161, + -614970135, + -610760536, + -606548381, + -602333690, + -598116479, + -593896765, + -589674567, + -585449903, + -581222789, + -576993244, + -572761285, + -568526931, + -564290197, + -560051104, + -555809667, + -551565905, + -547319836, + -543071478, + -538820847, + -534567963, + -530312842, + -526055503, + -521795963, + -517534240, + -513270353, + -509004318, + -504736154, + -500465878, + -496193509, + -491919064, + -487642562, + -483364019, + -479083454, + -474800886, + -470516330, + -466229807, + -461941333, + -457650927, + -453358607, + -449064389, + -444768294, + -440470337, + -436170538, + -431868915, + -427565485, + -423260266, + -418953276, + -414644534, + -410334058, + -406021865, + -401707973, + -397392401, + -393075166, + -388756287, + -384435782, + -380113669, + -375789965, + -371464690, + -367137861, + -362809495, + -358479612, + -354148230, + -349815365, + -345481038, + -341145265, + -336808065, + -332469456, + -328129457, + -323788084, + -319445358, + -315101295, + -310755913, + -306409232, + -302061269, + -297712042, + -293361570, + -289009871, + -284656963, + -280302863, + -275947592, + -271591166, + -267233603, + -262874923, + -258515144, + -254154282, + -249792358, + -245429388, + -241065392, + -236700388, + -232334393, + -227967426, + -223599506, + -219230650, + -214860878, + -210490206, + -206118654, + -201746240, + -197372981, + -192998897, + -188624006, + -184248325, + -179871874, + -175494670, + -171116733, + -166738079, + -162358728, + -157978697, + -153598006, + -149216672, + -144834714, + -140452151, + -136068999, + -131685278, + -127301007, + -122916203, + -118530885, + -114145071, + -109758779, + -105372028, + -100984837, + -96597223, + -92209205, + -87820801, + -83432030, + -79042909, + -74653459, + -70263695, + -65873638, + -61483306, + -57092716, + -52701887, + -48310838, + -43919586, + -39528151, + -35136551, + -30744804, + -26352928, + -21960942, + -17568864, + -13176712, + -8784505, + -4392262, + 0, + 4392262, + 8784505, + 13176712, + 17568864, + 21960942, + 26352928, + 30744804, + 35136551, + 39528151, + 43919586, + 48310838, + 52701887, + 57092716, + 61483306, + 65873638, + 70263695, + 74653459, + 79042909, + 83432030, + 87820801, + 92209205, + 96597223, + 100984837, + 105372028, + 109758779, + 114145071, + 118530885, + 122916203, + 127301007, + 131685278, + 136068999, + 140452151, + 144834714, + 149216672, + 153598006, + 157978697, + 162358728, + 166738079, + 171116733, + 175494670, + 179871874, + 184248325, + 188624006, + 192998897, + 197372981, + 201746240, + 206118654, + 210490206, + 214860878, + 219230650, + 223599506, + 227967426, + 232334393, + 236700388, + 241065392, + 245429388, + 249792358, + 254154282, + 258515144, + 262874923, + 267233603, + 271591166, + 275947592, + 280302863, + 284656963, + 289009871, + 293361570, + 297712042, + 302061269, + 306409232, + 310755913, + 315101295, + 319445358, + 323788084, + 328129457, + 332469456, + 336808065, + 341145265, + 345481038, + 349815365, + 354148230, + 358479612, + 362809495, + 367137861, + 371464690, + 375789965, + 380113669, + 384435782, + 388756287, + 393075166, + 397392401, + 401707973, + 406021865, + 410334058, + 414644534, + 418953276, + 423260266, + 427565485, + 431868915, + 436170538, + 440470337, + 444768294, + 449064389, + 453358607, + 457650927, + 461941333, + 466229807, + 470516330, + 474800886, + 479083454, + 483364019, + 487642562, + 491919064, + 496193509, + 500465878, + 504736154, + 509004318, + 513270353, + 517534240, + 521795963, + 526055503, + 530312842, + 534567963, + 538820847, + 543071478, + 547319836, + 551565905, + 555809667, + 560051104, + 564290197, + 568526931, + 572761285, + 576993244, + 581222789, + 585449903, + 589674567, + 593896765, + 598116479, + 602333690, + 606548381, + 610760536, + 614970135, + 619177161, + 623381598, + 627583426, + 631782630, + 635979190, + 640173090, + 644364312, + 648552838, + 652738651, + 656921734, + 661102068, + 665279637, + 669454423, + 673626408, + 677795576, + 681961908, + 686125387, + 690285996, + 694443717, + 698598533, + 702750427, + 706899381, + 711045377, + 715188400, + 719328430, + 723465451, + 727599446, + 731730397, + 735858287, + 739983099, + 744104815, + 748223418, + 752338892, + 756451218, + 760560380, + 764666360, + 768769141, + 772868706, + 776965038, + 781058120, + 785147934, + 789234464, + 793317693, + 797397602, + 801474176, + 805547397, + 809617249, + 813683713, + 817746774, + 821806413, + 825862615, + 829915362, + 833964638, + 838010424, + 842052705, + 846091463, + 850126682, + 854158345, + 858186435, + 862210934, + 866231826, + 870249095, + 874262724, + 878272695, + 882278992, + 886281598, + 890280497, + 894275671, + 898267104, + 902254780, + 906238681, + 910218791, + 914195094, + 918167572, + 922136209, + 926100989, + 930061894, + 934018909, + 937972016, + 941921200, + 945866443, + 949807730, + 953745043, + 957678367, + 961607684, + 965532978, + 969454234, + 973371434, + 977284562, + 981193602, + 985098537, + 988999351, + 992896028, + 996788551, + 1000676905, + 1004561072, + 1008441038, + 1012316784, + 1016188296, + 1020055556, + 1023918550, + 1027777260, + 1031631671, + 1035481766, + 1039327529, + 1043168945, + 1047005996, + 1050838668, + 1054666944, + 1058490808, + 1062310244, + 1066125236, + 1069935768, + 1073741824, + 1077543388, + 1081340445, + 1085132978, + 1088920972, + 1092704411, + 1096483278, + 1100257559, + 1104027237, + 1107792296, + 1111552721, + 1115308496, + 1119059606, + 1122806034, + 1126547765, + 1130284784, + 1134017074, + 1137744621, + 1141467408, + 1145185419, + 1148898640, + 1152607055, + 1156310649, + 1160009405, + 1163703308, + 1167392344, + 1171076495, + 1174755748, + 1178430087, + 1182099496, + 1185763960, + 1189423463, + 1193077991, + 1196727527, + 1200372058, + 1204011567, + 1207646039, + 1211275460, + 1214899813, + 1218519084, + 1222133257, + 1225742318, + 1229346252, + 1232945043, + 1236538675, + 1240127136, + 1243710408, + 1247288478, + 1250861329, + 1254428948, + 1257991320, + 1261548429, + 1265100260, + 1268646800, + 1272188032, + 1275723942, + 1279254516, + 1282779738, + 1286299593, + 1289814068, + 1293323147, + 1296826816, + 1300325060, + 1303817864, + 1307305214, + 1310787095, + 1314263493, + 1317734393, + 1321199781, + 1324659641, + 1328113960, + 1331562723, + 1335005916, + 1338443524, + 1341875533, + 1345301929, + 1348722696, + 1352137822, + 1355547292, + 1358951090, + 1362349204, + 1365741619, + 1369128320, + 1372509294, + 1375884527, + 1379254004, + 1382617710, + 1385975633, + 1389327759, + 1392674072, + 1396014559, + 1399349206, + 1402678000, + 1406000925, + 1409317969, + 1412629117, + 1415934356, + 1419233672, + 1422527051, + 1425814478, + 1429095941, + 1432371426, + 1435640919, + 1438904406, + 1442161874, + 1445413309, + 1448658697, + 1451898025, + 1455131280, + 1458358447, + 1461579514, + 1464794466, + 1468003290, + 1471205974, + 1474402503, + 1477592864, + 1480777044, + 1483955030, + 1487126808, + 1490292364, + 1493451687, + 1496604762, + 1499751576, + 1502892116, + 1506026369, + 1509154322, + 1512275962, + 1515391276, + 1518500250, + 1521602872, + 1524699129, + 1527789007, + 1530872494, + 1533949577, + 1537020244, + 1540084480, + 1543142274, + 1546193612, + 1549238483, + 1552276872, + 1555308768, + 1558334157, + 1561353028, + 1564365367, + 1567371161, + 1570370399, + 1573363068, + 1576349155, + 1579328647, + 1582301533, + 1585267800, + 1588227435, + 1591180426, + 1594126760, + 1597066426, + 1599999411, + 1602925703, + 1605845289, + 1608758157, + 1611664296, + 1614563692, + 1617456335, + 1620342211, + 1623221309, + 1626093616, + 1628959121, + 1631817811, + 1634669676, + 1637514702, + 1640352877, + 1643184191, + 1646008631, + 1648826185, + 1651636841, + 1654440588, + 1657237415, + 1660027308, + 1662810258, + 1665586251, + 1668355276, + 1671117323, + 1673872378, + 1676620432, + 1679361471, + 1682095486, + 1684822463, + 1687542393, + 1690255263, + 1692961062, + 1695659779, + 1698351403, + 1701035922, + 1703713325, + 1706383601, + 1709046739, + 1711702727, + 1714351555, + 1716993211, + 1719627685, + 1722254965, + 1724875040, + 1727487899, + 1730093532, + 1732691928, + 1735283075, + 1737866963, + 1740443581, + 1743012918, + 1745574963, + 1748129707, + 1750677137, + 1753217244, + 1755750017, + 1758275445, + 1760793518, + 1763304224, + 1765807555, + 1768303498, + 1770792044, + 1773273182, + 1775746903, + 1778213194, + 1780672048, + 1783123452, + 1785567396, + 1788003871, + 1790432867, + 1792854372, + 1795268378, + 1797674873, + 1800073849, + 1802465294, + 1804849198, + 1807225553, + 1809594347, + 1811955572, + 1814309216, + 1816655271, + 1818993726, + 1821324572, + 1823647799, + 1825963397, + 1828271356, + 1830571667, + 1832864320, + 1835149306, + 1837426615, + 1839696238, + 1841958164, + 1844212386, + 1846458892, + 1848697674, + 1850928722, + 1853152028, + 1855367581, + 1857575372, +}; + +#endif diff --git a/src/include/sof/audio/component.h b/src/include/sof/audio/component.h index 61486e0e2a36..7fc9feb53736 100644 --- a/src/include/sof/audio/component.h +++ b/src/include/sof/audio/component.h @@ -21,6 +21,7 @@ #include <sof/audio/pipeline.h> #include <sof/debug/telemetry/telemetry.h> #include <rtos/idc.h> +#include <rtos/mutex.h> #include <rtos/userspace_helper.h> #include <sof/lib/dai.h> #include <sof/schedule/schedule.h> @@ -578,6 +579,13 @@ struct comp_ops { uint64_t (*get_total_data_processed)(struct comp_dev *dev, uint32_t stream_no, bool input); }; +struct k_heap; +struct vregion; +struct mod_alloc_ctx { + struct k_heap *heap; + struct vregion *vreg; +}; + /** * Audio component base driver "class" * - used by all other component types. @@ -591,7 +599,7 @@ struct comp_driver { * Intended to replace the ops field. * Currently used by module_adapter. */ - struct sys_heap *user_heap; /**< Userspace heap */ + struct k_heap *user_heap; /**< Userspace heap */ }; /** \brief Holds constant pointer to component driver */ @@ -679,6 +687,10 @@ struct comp_dev { struct list_item bsource_list; /**< list of source buffers */ struct list_item bsink_list; /**< list of sink buffers */ +#ifdef CONFIG_SOF_USERSPACE_LL + struct sys_mutex list_mutex; /**< protect lists of source/sinks */ +#endif + /* performance data*/ struct comp_perf_data perf_data; /* Input Buffer Size for pin 0, add array for other pins if needed */ @@ -849,6 +861,27 @@ static inline enum sof_comp_type dev_comp_type(const struct comp_dev *dev) return dev->ipc_config.type; } +/** + * Initialize common part of a component device + * @param drv Parent component driver. + * @param dev Device. + * @param bytes Size of the component device in bytes. + */ +static inline void comp_init(const struct comp_driver *drv, + struct comp_dev *dev, size_t bytes) +{ + dev->size = bytes; + dev->drv = drv; + dev->state = COMP_STATE_INIT; + list_init(&dev->bsink_list); + list_init(&dev->bsource_list); +#ifdef CONFIG_SOF_USERSPACE_LL + sys_mutex_init(&dev->list_mutex); +#endif + memcpy_s(&dev->tctx, sizeof(dev->tctx), + trace_comp_drv_get_tr_ctx(dev->drv), sizeof(struct tr_ctx)); +} + /** * Allocates memory for the component device and initializes common part. * @param drv Parent component driver. @@ -857,27 +890,36 @@ static inline enum sof_comp_type dev_comp_type(const struct comp_dev *dev) */ static inline struct comp_dev *comp_alloc(const struct comp_driver *drv, size_t bytes) { - struct comp_dev *dev = NULL; - /* * Use uncached address everywhere to access components to rule out * multi-core failures. TODO: verify if cached alias may be used in some cases */ - dev = module_driver_heap_rzalloc(drv->user_heap, SOF_MEM_FLAG_USER | SOF_MEM_FLAG_COHERENT, - bytes); + struct comp_dev *dev = sof_heap_alloc(drv->user_heap, + SOF_MEM_FLAG_USER | SOF_MEM_FLAG_COHERENT, + bytes, 0); + if (!dev) return NULL; - dev->size = bytes; - dev->drv = drv; - dev->state = COMP_STATE_INIT; - list_init(&dev->bsink_list); - list_init(&dev->bsource_list); - memcpy_s(&dev->tctx, sizeof(struct tr_ctx), - trace_comp_drv_get_tr_ctx(dev->drv), sizeof(struct tr_ctx)); + + memset(dev, 0, sizeof(*dev)); + comp_init(drv, dev, bytes); return dev; } +/** + * Frees memory allocated for component device. + * + * This is a counterpart to comp_alloc() and not to be confused with + * comp_free(). + * + * @param dev Pointer to the component device. + */ +static inline void comp_free_device(struct comp_dev *dev) +{ + sof_heap_free(dev->drv->user_heap, dev); +} + /** * \brief Module adapter associated with a component * @param dev Component device @@ -919,6 +961,7 @@ void sys_comp_module_copier_interface_init(void); void sys_comp_module_crossover_interface_init(void); void sys_comp_module_dcblock_interface_init(void); void sys_comp_module_demux_interface_init(void); +void sys_comp_module_dolby_dax_audio_processing_interface_init(void); void sys_comp_module_drc_interface_init(void); void sys_comp_module_dts_interface_init(void); void sys_comp_module_eq_fir_interface_init(void); @@ -940,7 +983,9 @@ void sys_comp_module_selector_interface_init(void); void sys_comp_module_sound_dose_interface_init(void); void sys_comp_module_src_interface_init(void); void sys_comp_module_src_lite_interface_init(void); +void sys_comp_module_stft_process_interface_init(void); void sys_comp_module_tdfb_interface_init(void); +void sys_comp_module_tone_interface_init(void); void sys_comp_module_template_interface_init(void); void sys_comp_module_tester_interface_init(void); void sys_comp_module_volume_interface_init(void); @@ -1200,7 +1245,7 @@ bool comp_update_performance_data(struct comp_dev *dev, uint32_t cycles_used); static inline int user_get_buffer_memory_region(const struct comp_driver *drv) { -#if CONFIG_USERSPACE +#if CONFIG_SOF_USERSPACE_USE_DRIVER_HEAP return drv->user_heap ? SOF_MEM_FLAG_USER_SHARED_BUFFER : SOF_MEM_FLAG_USER; #else return SOF_MEM_FLAG_USER; diff --git a/src/include/sof/audio/component_ext.h b/src/include/sof/audio/component_ext.h index 3762fcde7791..d2bbf87a7764 100644 --- a/src/include/sof/audio/component_ext.h +++ b/src/include/sof/audio/component_ext.h @@ -44,17 +44,15 @@ struct comp_dev *comp_new_ipc4(struct ipc4_module_init_instance *module_init); /** See comp_ops::free */ static inline void comp_free(struct comp_dev *dev) { - assert(dev->drv->ops.free); - - /* free task if shared component or DP task*/ - if ((dev->is_shared || dev->ipc_config.proc_domain == COMP_PROCESSING_DOMAIN_DP) && - dev->task) { - schedule_task_free(dev->task); - module_driver_heap_free(dev->drv->user_heap, dev->task); - dev->task = NULL; - } + const struct comp_driver *drv = dev->drv; + + assert(drv->ops.free); - dev->drv->ops.free(dev); + /* + * In DP case this will run in DP thread context, so the task can only + * be freed after this. + */ + drv->ops.free(dev); } /** @@ -152,19 +150,20 @@ static inline int comp_trigger_local(struct comp_dev *dev, int cmd) int ret; ret = dev->drv->ops.trigger(dev, cmd); + if (ret) + return ret; /* start a thread in case of shared component or DP scheduling */ if (dev->task) { /* schedule or cancel task */ switch (cmd) { case COMP_TRIGGER_START: - case COMP_TRIGGER_RELEASE: - schedule_task(dev->task, 0, dev->period); + ret = schedule_task(dev->task, 0, dev->period); break; + case COMP_TRIGGER_RELEASE: case COMP_TRIGGER_XRUN: case COMP_TRIGGER_PAUSE: case COMP_TRIGGER_STOP: - schedule_task_cancel(dev->task); break; } } diff --git a/src/include/sof/audio/format.h b/src/include/sof/audio/format.h index 8174e53a4163..a468278531f3 100644 --- a/src/include/sof/audio/format.h +++ b/src/include/sof/audio/format.h @@ -70,6 +70,9 @@ /* Convert fractional Qnx.ny number x to float */ #define Q_CONVERT_QTOF(x, ny) ((float)(x) / ((int64_t)1 << (ny))) +/* Convert fractional Qnx.ny number x to double */ +#define Q_CONVERT_QTOD(x, ny) ((double)(x) / ((int64_t)1 << (ny))) + /* A more clever macro for Q-shifts */ #define Q_SHIFT(x, src_q, dst_q) ((x) >> ((src_q) - (dst_q))) #define Q_SHIFT_RND(x, src_q, dst_q) \ diff --git a/src/include/sof/audio/ipc-config.h b/src/include/sof/audio/ipc-config.h index 49dbb9eac217..1eb6b9cca555 100644 --- a/src/include/sof/audio/ipc-config.h +++ b/src/include/sof/audio/ipc-config.h @@ -77,7 +77,7 @@ struct ipc_config_dai { */ /**< DMA configs - required for ACE 2.0 and newer */ struct ipc_dma_config *host_dma_config[GTW_DMA_DEVICE_MAX_COUNT]; - const struct ipc4_audio_format *out_fmt;/**< audio format for output pin 0 - required + const struct ipc4_audio_format *gtw_fmt;/**< audio format for gateway DMA data - required * for ACE 2.0 and newer */ /* Gain feature flag */ diff --git a/src/include/sof/audio/kpb.h b/src/include/sof/audio/kpb.h index fce40f1638a1..14c7b33a38e9 100644 --- a/src/include/sof/audio/kpb.h +++ b/src/include/sof/audio/kpb.h @@ -42,7 +42,7 @@ struct comp_buffer; #define KPB_MAX_BUFFER_SIZE(sw, channels_number) ((KPB_SAMPLNG_FREQUENCY / 1000) * \ (KPB_SAMPLE_CONTAINER_SIZE(sw) / 8) * KPB_MAX_BUFF_TIME * \ (channels_number)) -#define KPB_MAX_NO_OF_CLIENTS 2 +#define KPB_MAX_NO_OF_CLIENTS 4 #define KPB_MAX_SINK_CNT (1 + KPB_MAX_NO_OF_CLIENTS) #define KPB_NO_OF_HISTORY_BUFFERS 2 /**< no of internal buffers */ #define KPB_ALLOCATION_STEP 0x100 diff --git a/src/include/sof/audio/mfcc/mfcc_comp.h b/src/include/sof/audio/mfcc/mfcc_comp.h index bbc01030e157..025eef116752 100644 --- a/src/include/sof/audio/mfcc/mfcc_comp.h +++ b/src/include/sof/audio/mfcc/mfcc_comp.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: BSD-3-Clause * - * Copyright(c) 2022 Intel Corporation. All rights reserved. + * Copyright(c) 2022-2026 Intel Corporation. * * Author: Seppo Ingalsuo <seppo.ingalsuo@linux.intel.com> */ @@ -18,7 +18,7 @@ /* __XCC__ is both for xt_xcc and xt_clang */ #if defined(__XCC__) # include <xtensa/config/core-isa.h> -# if XCHAL_HAVE_HIFI4 +# if XCHAL_HAVE_HIFI4 || XCHAL_HAVE_HIFI5 # define MFCC_HIFI4 # elif XCHAL_HAVE_HIFI3 # define MFCC_HIFI3 @@ -30,25 +30,7 @@ #endif #define MFCC_MAGIC 0x6d666363 /* ASCII for "mfcc" */ - -/* Set to 16 for lower RAM and MCPS with slightly lower quality. Set to 32 for best - * quality but higher MCPS and RAM. The MFCC input is currently 16 bits. With this option - * set to 32 the FFT and Mel filterbank are computed with better 32 bit precision. There - * is also need to enable 32 bit FFT from Kconfig if set. - */ -#define MFCC_FFT_BITS 16 - -/* MFCC with 16 bit FFT benefits from data normalize, for 32 bits there's no - * significant impact. The amount of left shifts for FFT input is limited to - * 10 that equals about 60 dB boost. The boost is compensated in Mel energy - * calculation. - */ -#if MFCC_FFT_BITS == 16 -#define MFCC_NORMALIZE_FFT -#else -#undef MFCC_NORMALIZE_FFT -#endif -#define MFCC_NORMALIZE_MAX_SHIFT 10 +#define MFCC_FFT_BITS 32 /** \brief Type definition for processing function select return value. */ typedef void (*mfcc_func)(struct processing_module *mod, @@ -79,15 +61,8 @@ struct mfcc_pre_emph { }; struct mfcc_fft { -#if MFCC_FFT_BITS == 16 - struct icomplex16 *fft_buf; /**< fft_padded_size */ - struct icomplex16 *fft_out; /**< fft_padded_size */ -#elif MFCC_FFT_BITS == 32 struct icomplex32 *fft_buf; /**< fft_padded_size */ struct icomplex32 *fft_out; /**< fft_padded_size */ -#else -#error "MFCC_FFT_BITS needs to be 16 or 32" -#endif struct fft_plan *fft_plan; int fft_fill_start_idx; /**< Set to 0 for pad left, etc. */ int fft_size; @@ -114,6 +89,8 @@ struct mfcc_state { struct mat_matrix_16b *mel_spectra; /**< Pointer to scratch */ struct mat_matrix_16b *cepstral_coef; /**< Pointer to scratch */ int32_t *power_spectra; /**< Pointer to scratch */ + int32_t *mel_log_32; /**< Pointer to scratch for 32-bit Mel output Q9.23 */ + int32_t mmax; /**< Maximum Mel value in Q9.23 */ int16_t buf_avail; int16_t *buffers; int16_t *prev_data; /**< prev_data_size */ @@ -125,9 +102,14 @@ struct mfcc_state { int low_freq; int high_freq; int sample_rate; + bool mel_only; /**< When true, output Mel spectra instead of cepstral coefficients */ bool waiting_fill; /**< booleans */ bool prev_samples_valid; + bool magic_pending; /**< True when magic word not yet written for current output */ size_t sample_buffers_size; /**< bytes */ + int16_t *out_data_ptr; /**< Read pointer into scratch data for multi-period output */ + int32_t *out_data_ptr_32; /**< Read pointer for 32-bit mel-only output */ + int out_remain; /**< Remaining int16_t samples to write to sink from scratch */ }; /* MFCC component private data */ @@ -154,37 +136,42 @@ static inline int16_t *mfcc_buffer_wrap(struct mfcc_buffer *buffer, int16_t *ptr int mfcc_setup(struct processing_module *mod, int max_frames, int rate, int channels); -void mfcc_free_buffers(struct mfcc_comp_data *cd); - -void mfcc_s16_default(struct processing_module *mod, struct input_stream_buffer *bsource, - struct output_stream_buffer *bsink, int frames); - -void mfcc_source_copy_s16(struct input_stream_buffer *bsource, struct mfcc_buffer *buf, - struct mfcc_pre_emph *emph, int frames, int source_channel); +void mfcc_free_buffers(struct processing_module *mod); void mfcc_fill_prev_samples(struct mfcc_buffer *buf, int16_t *prev_data, int prev_data_length); void mfcc_fill_fft_buffer(struct mfcc_state *state); -#ifdef MFCC_NORMALIZE_FFT -int mfcc_normalize_fft_buffer(struct mfcc_state *state); -#endif - void mfcc_apply_window(struct mfcc_state *state, int input_shift); #if CONFIG_FORMAT_S16LE -int16_t *mfcc_sink_copy_zero_s16(const struct audio_stream *sink, - int16_t *w_ptr, int samples); - -int16_t *mfcc_sink_copy_data_s16(const struct audio_stream *sink, int16_t *w_ptr, - int samples, int16_t *r_ptr); +void mfcc_source_copy_s16(struct input_stream_buffer *bsource, struct mfcc_buffer *buf, + struct mfcc_pre_emph *emph, int frames, int source_channel); void mfcc_s16_default(struct processing_module *mod, struct input_stream_buffer *bsource, struct output_stream_buffer *bsink, int frames); #endif +#if CONFIG_FORMAT_S24LE + +void mfcc_source_copy_s24(struct input_stream_buffer *bsource, struct mfcc_buffer *buf, + struct mfcc_pre_emph *emph, int frames, int source_channel); + +void mfcc_s24_default(struct processing_module *mod, struct input_stream_buffer *bsource, + struct output_stream_buffer *bsink, int frames); +#endif + +#if CONFIG_FORMAT_S32LE + +void mfcc_source_copy_s32(struct input_stream_buffer *bsource, struct mfcc_buffer *buf, + struct mfcc_pre_emph *emph, int frames, int source_channel); + +void mfcc_s32_default(struct processing_module *mod, struct input_stream_buffer *bsource, + struct output_stream_buffer *bsink, int frames); +#endif + #ifdef UNIT_TEST void sys_comp_module_mfcc_interface_init(void); #endif diff --git a/src/include/sof/audio/module_adapter/iadk/system_agent.h b/src/include/sof/audio/module_adapter/iadk/system_agent.h index a0470167c796..e7aab98c748c 100644 --- a/src/include/sof/audio/module_adapter/iadk/system_agent.h +++ b/src/include/sof/audio/module_adapter/iadk/system_agent.h @@ -107,8 +107,9 @@ extern "C" { * method (the variant with 7 parameters) via a parameter that initially contained the address to * the agent system. The system_agent_start function returns it in the variable adapter. */ -int system_agent_start(uintptr_t entry_point, uint32_t module_id, uint32_t instance_id, - uint32_t core_id, uint32_t log_handle, void *mod_cfg, const void **adapter); +struct system_agent_params; + +int system_agent_start(const struct system_agent_params *params, const void **adapter); #ifdef __cplusplus } #endif diff --git a/src/include/sof/audio/module_adapter/library/native_system_agent.h b/src/include/sof/audio/module_adapter/library/native_system_agent.h index 657bc6ac643d..70c0f6b1aad1 100644 --- a/src/include/sof/audio/module_adapter/library/native_system_agent.h +++ b/src/include/sof/audio/module_adapter/library/native_system_agent.h @@ -11,9 +11,17 @@ #include <sof/audio/module_adapter/module/module_interface.h> #include <native_system_service.h> -typedef int (*system_agent_start_fn)(uintptr_t entry_point, uint32_t module_id, - uint32_t instance_id, uint32_t core_id, uint32_t log_handle, - void *mod_cfg, const void **adapter); +struct system_agent_params { + uintptr_t entry_point; /* The module entry point function address. */ + uint32_t module_id; /* The identifier for the module. */ + uint32_t instance_id; /* The instance identifier of the module. */ + uint32_t core_id; /* Core on which the module will run. */ + uint32_t log_handle; /* The handle for logging purposes. */ + void *mod_cfg; /* Pointer to the module configuration data. */ +}; + +typedef int (*system_agent_start_fn)(const struct system_agent_params *params, + const void **adapter); struct native_system_agent { struct system_service system_service; @@ -29,18 +37,12 @@ struct native_system_agent { * * This function initializes and starts the native system agent with the provided parameters. * - * @param[in] entry_point - The module entry point function address. - * @param[in] module_id - The identifier for the module. - * @param[in] instance_id - The instance identifier of the module. - * @param[in] core_id - Core on which the module will run. - * @param[in] log_handle - The handle for logging purposes. - * @param[in] mod_cfg - Pointer to the module configuration data. + * @param[in] params - Pointer to the system agent parameter structure * @param[out] iface - Pointer to the module interface. * * @return Returns 0 on success or an error code on failure. */ -int native_system_agent_start(uintptr_t entry_point, uint32_t module_id, uint32_t instance_id, - uint32_t core_id, uint32_t log_handle, void *mod_cfg, +int native_system_agent_start(const struct system_agent_params *params, const void **iface); #endif /* __NATIVE_SYSTEM_AGENT_H__ */ diff --git a/src/include/sof/audio/module_adapter/library/userspace_proxy.h b/src/include/sof/audio/module_adapter/library/userspace_proxy.h index 52381ac7c175..482590caf67f 100644 --- a/src/include/sof/audio/module_adapter/library/userspace_proxy.h +++ b/src/include/sof/audio/module_adapter/library/userspace_proxy.h @@ -14,12 +14,71 @@ #include <stdbool.h> #include <zephyr/kernel.h> +#include <zephyr/app_memory/app_memdomain.h> +#include <utilities/array.h> + +#include <native_system_agent.h> +#include <module/module/interface.h> +#include <sof/audio/module_adapter/library/userspace_proxy_user.h> + +struct module_interface; +struct comp_driver; +struct sof_man_module; +struct system_agent_params; /* Processing module structure fields needed for user mode */ struct userspace_context { struct k_mem_domain *comp_dom; /* Module specific memory domain */ + const struct module_interface *interface; /* Userspace module interface */ + struct user_work_item *work_item; /* work item for user worker thread */ + struct k_event *dp_event; /* DP thread event */ }; - #endif /* CONFIG_USERSPACE */ +#if CONFIG_SOF_USERSPACE_PROXY +/** + * Creates userspace module proxy + * + * @param user_ctx - pointer to pointer of userspace module context + * @param drv - pointer to component driver + * @param manifest - pointer to module manifest + * @param start_fn - pointer to system agent start function + * @param agent_params - pointer to system_agent_params + * @param agent_interface - pointer to variable to store module interface created by agent + * @param ops - Pointer to a variable that will hold the address of the module interface + * structure. The function stores a pointer to its own userspace proxy + * interface structure in this variable. + * + * @return 0 for success, error otherwise. + */ +int userspace_proxy_create(struct userspace_context **user_ctx, const struct comp_driver *drv, + const struct sof_man_module *manifest, system_agent_start_fn start_fn, + const struct system_agent_params *agent_params, + const void **agent_interface, const struct module_interface **ops); + +/** + * Destroy userspace module proxy + * + * @param drv - pointer to component driver + * @param user_ctx - pointer to userspace module context + */ +void userspace_proxy_destroy(const struct comp_driver *drv, struct userspace_context *user_ctx); + +#if IS_ENABLED(CONFIG_SOF_USERSPACE_MOD_IPC_BY_DP_THREAD) +/** + * Register a k_event object used to notify the DP thread about a pending userspace module IPC + * request to process. + * + * @param mod Pointer to the processing module. + * @param event Pointer to the event to signal incoming IPC. + * + * @return Pointer to a k_work_user work item for userspace modules, or NULL for non-userspace + * modules. + */ +struct k_work_user *userspace_proxy_register_ipc_handler(struct processing_module *mod, + struct k_event *event); +#endif + +#endif /* CONFIG_SOF_USERSPACE_PROXY */ + #endif /* __SOF_AUDIO_USERSPACE_PROXY_H__ */ diff --git a/src/include/sof/audio/module_adapter/library/userspace_proxy_user.h b/src/include/sof/audio/module_adapter/library/userspace_proxy_user.h new file mode 100644 index 000000000000..e571705cec17 --- /dev/null +++ b/src/include/sof/audio/module_adapter/library/userspace_proxy_user.h @@ -0,0 +1,92 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * + * Copyright(c) 2025 Intel Corporation. All rights reserved. + * + * Author: Adrian Warecki <adrian.warecki@intel.com> + */ + +#ifndef __SOF_AUDIO_USERSPACE_PROXY_USER_H__ +#define __SOF_AUDIO_USERSPACE_PROXY_USER_H__ + +#if CONFIG_SOF_USERSPACE_PROXY +struct module_agent_params { + system_agent_start_fn start_fn; + struct system_agent_params params; + byte_array_t mod_cfg; + const void *out_interface; +}; + +struct module_large_cfg_set_params { + uint32_t config_id; + enum module_cfg_fragment_position pos; + uint32_t data_off_size; + const uint8_t *fragment; + size_t fragment_size; + uint8_t *response; + size_t response_size; +}; + +struct module_large_cfg_get_params { + uint32_t config_id; + uint32_t *data_off_size; + uint8_t *fragment; + size_t fragment_size; +}; + +struct module_processing_mode_params { + enum module_processing_mode mode; +}; + +struct module_process_params { + struct sof_source **sources; + int num_of_sources; + struct sof_sink **sinks; + int num_of_sinks; +}; + +enum userspace_proxy_cmd { + USER_PROXY_MOD_CMD_AGENT_START, + USER_PROXY_MOD_CMD_INIT, + USER_PROXY_MOD_CMD_PREPARE, + USER_PROXY_MOD_CMD_PROC_READY, + USER_PROXY_MOD_CMD_SET_PROCMOD, + USER_PROXY_MOD_CMD_GET_PROCMOD, + USER_PROXY_MOD_CMD_SET_CONF, + USER_PROXY_MOD_CMD_GET_CONF, + USER_PROXY_MOD_CMD_BIND, + USER_PROXY_MOD_CMD_UNBIND, + USER_PROXY_MOD_CMD_RESET, + USER_PROXY_MOD_CMD_FREE, + USER_PROXY_MOD_CMD_TRIGGER +}; + +struct module_params { + enum userspace_proxy_cmd cmd; + int status; + struct processing_module *mod; + struct userspace_context *context; + /* The field used in the union depends on the value of cmd */ + union { + struct module_agent_params agent; + struct module_large_cfg_set_params set_conf; + struct module_large_cfg_get_params get_conf; + struct module_processing_mode_params proc_mode; + struct module_process_params proc; + struct bind_info *bind_data; + int trigger_data; + } ext; +}; + +struct user_work_item { + struct k_work_user work_item; /* ipc worker workitem */ + struct k_event *event; /* ipc worker done event */ + struct module_params params; +}; + +void userspace_proxy_handle_request(struct processing_module *mod, struct module_params *params); + +void userspace_proxy_worker_handler(struct k_work_user *work_item); + +#endif /* CONFIG_SOF_USERSPACE_PROXY */ + +#endif /* __SOF_AUDIO_USERSPACE_PROXY_USER_H__ */ diff --git a/src/include/sof/audio/module_adapter/module/cadence.h b/src/include/sof/audio/module_adapter/module/cadence.h index a4ad3d9482f8..8ba658749c05 100644 --- a/src/include/sof/audio/module_adapter/module/cadence.h +++ b/src/include/sof/audio/module_adapter/module/cadence.h @@ -17,6 +17,7 @@ #define LIB_NO_ERROR XA_NO_ERROR #define LIB_IS_FATAL_ERROR(e) ((e) & XA_FATAL_ERROR) #define CODEC_GET_API_ID(id) ((id) & 0xFF) +#define CADENCE_MP3_ENCODER_DEFAULT_BITRATE 320 /*****************************************************************************/ /* Cadence API functions */ @@ -32,6 +33,8 @@ extern xa_codec_func_t xa_sbc_dec; extern xa_codec_func_t xa_vorbis_dec; extern xa_codec_func_t xa_src_pp; +#define DEFAULT_CODEC_ID CADENCE_CODEC_WRAPPER_ID + #define API_CALL(cd, cmd, sub_cmd, value, ret) \ do { \ ret = (cd)->api((cd)->self, \ @@ -49,14 +52,33 @@ struct cadence_api { }; struct cadence_codec_data { +#if CONFIG_IPC_MAJOR_4 + struct ipc4_base_module_cfg base_cfg; + uint32_t direction; +#endif char name[LIB_NAME_MAX_LEN]; void *self; xa_codec_func_t *api; void *mem_tabs; + size_t mem_to_be_freed_len; + void **mem_to_be_freed; uint32_t api_id; struct module_config setup_cfg; }; +enum cadence_api_id { + CADENCE_CODEC_WRAPPER_ID = 0x01, + CADENCE_CODEC_AAC_DEC_ID = 0x02, + CADENCE_CODEC_BSAC_DEC_ID = 0x03, + CADENCE_CODEC_DAB_DEC_ID = 0x04, + CADENCE_CODEC_DRM_DEC_ID = 0x05, + CADENCE_CODEC_MP3_DEC_ID = 0x06, + CADENCE_CODEC_SBC_DEC_ID = 0x07, + CADENCE_CODEC_VORBIS_DEC_ID = 0x08, + CADENCE_CODEC_SRC_PP_ID = 0x09, + CADENCE_CODEC_MP3_ENC_ID = 0x0A, +}; + #if CONFIG_IPC_MAJOR_4 struct ipc4_cadence_module_cfg { struct ipc4_base_module_cfg base_cfg; @@ -65,4 +87,25 @@ struct ipc4_cadence_module_cfg { } __packed __aligned(4); #endif +extern struct cadence_api cadence_api_table[]; + +int cadence_codec_set_configuration(struct processing_module *mod, uint32_t config_id, + enum module_cfg_fragment_position pos, + uint32_t data_offset_size, const uint8_t *fragment, + size_t fragment_size, uint8_t *response, size_t response_size); +int cadence_codec_resolve_api_with_id(struct processing_module *mod, uint32_t codec_id, + uint32_t direction); +int cadence_codec_apply_params(struct processing_module *mod, int size, void *data); +int cadence_codec_process_data(struct processing_module *mod, + XA_ERRORCODE *api_error_code); +int cadence_codec_apply_config(struct processing_module *mod); +void cadence_codec_free_memory_tables(struct processing_module *mod); +int cadence_codec_init_memory_tables(struct processing_module *mod); +int cadence_codec_get_samples(struct processing_module *mod); +int cadence_codec_init_process(struct processing_module *mod); +int cadence_init_codec_object(struct processing_module *mod); +int cadence_codec_resolve_api(struct processing_module *mod); +int cadence_codec_free(struct processing_module *mod); +size_t cadence_api_table_size(void); + #endif /* __SOF_AUDIO_CADENCE_CODEC__ */ diff --git a/src/include/sof/audio/module_adapter/module/generic.h b/src/include/sof/audio/module_adapter/module/generic.h index 97e5d9b35520..91bdce96b1c7 100644 --- a/src/include/sof/audio/module_adapter/module/generic.h +++ b/src/include/sof/audio/module_adapter/module/generic.h @@ -13,8 +13,9 @@ #ifndef __SOF_AUDIO_MODULE_GENERIC__ #define __SOF_AUDIO_MODULE_GENERIC__ -#include <sof/audio/component.h> +#include <sof/objpool.h> #include <sof/ut.h> +#include <sof/audio/component.h> #include <sof/audio/sink_api.h> #include <sof/audio/source_api.h> #include "module_interface.h" @@ -23,6 +24,7 @@ #if CONFIG_MODULE_MEMORY_API_DEBUG && defined(__ZEPHYR__) #include <zephyr/kernel/thread.h> #endif +#include <sof/compiler_attributes.h> /* * helpers to determine processing type @@ -127,11 +129,10 @@ struct module_param { * when the module unloads. */ struct module_resources { - struct list_item res_list; /**< Allocad resource containers */ - struct list_item free_cont_list; /**< Unused memory containers */ - struct list_item cont_chunk_list; /**< Memory container chunks */ + struct objpool_head objpool; size_t heap_usage; size_t heap_high_water_mark; + struct mod_alloc_ctx *alloc; #if CONFIG_MODULE_MEMORY_API_DEBUG && defined(__ZEPHYR__) k_tid_t rsrc_mngr; #endif @@ -170,6 +171,8 @@ struct module_processing_data { uint32_t produced; /**< Specifies how much data the module produced in its last task.*/ uint32_t consumed; /**< Specified how much data the module consumed in its last task */ uint32_t init_done; /**< Specifies if the module initialization is finished */ + bool eos_reached; /**< End of stream processing is reached */ + bool eos_notification_sent; /**< EOS notification is sent to host */ void *in_buff; /**< A pointer to module input buffer. */ void *out_buff; /**< A pointer to module output buffer. */ }; @@ -188,16 +191,68 @@ struct module_processing_data { /*****************************************************************************/ int module_load_config(struct comp_dev *dev, const void *cfg, size_t size); int module_init(struct processing_module *mod); -void *mod_alloc_align(struct processing_module *mod, uint32_t size, uint32_t alignment); -void *mod_alloc(struct processing_module *mod, uint32_t size); -void *mod_zalloc(struct processing_module *mod, uint32_t size); -int mod_free(struct processing_module *mod, const void *ptr); +void *mod_balloc_align(struct processing_module *mod, size_t size, size_t alignment); +void mod_resource_init(struct processing_module *mod); +void mod_heap_info(struct processing_module *mod, size_t *size, uintptr_t *start); +#if defined(__ZEPHYR__) && defined(CONFIG_SOF_FULL_ZEPHYR_APPLICATION) +__syscall void *mod_alloc_ext(struct processing_module *mod, uint32_t flags, size_t size, + size_t alignment); +__syscall int mod_free(struct processing_module *mod, const void *ptr); +#else +void *z_impl_mod_alloc_ext(struct processing_module *mod, uint32_t flags, size_t size, + size_t alignment); +int z_impl_mod_free(struct processing_module *mod, const void *ptr); +#define mod_alloc_ext z_impl_mod_alloc_ext +#define mod_free z_impl_mod_free +#endif + +/** + * Allocates aligned memory block for module. + * @param mod Pointer to the module this memory block is allocated for. + * @param size Size in bytes. + * @param alignment Alignment in bytes. + * @return Pointer to the allocated memory or NULL if failed. + * + * The allocated memory is automatically freed when the module is unloaded. + */ +static inline void *mod_alloc_align(struct processing_module *mod, size_t size, size_t alignment) +{ + return mod_alloc_ext(mod, SOF_MEM_FLAG_USER, size, alignment); +} + +static inline void *mod_balloc(struct processing_module *mod, size_t size) +{ + return mod_balloc_align(mod, size, 0); +} + +static inline void *mod_alloc(struct processing_module *mod, size_t size) +{ + return mod_alloc_align(mod, size, 0); +} + +static inline void *mod_zalloc(struct processing_module *mod, size_t size) +{ + void *ret = mod_alloc(mod, size); + + if (ret) + memset(ret, 0, size); + + return ret; +} + #if CONFIG_COMP_BLOB struct comp_data_blob_handler *mod_data_blob_handler_new(struct processing_module *mod); void mod_data_blob_handler_free(struct processing_module *mod, struct comp_data_blob_handler *dbh); #endif #if CONFIG_FAST_GET -const void *mod_fast_get(struct processing_module *mod, const void * const dram_ptr, size_t size); +#if defined(__ZEPHYR__) && defined(CONFIG_SOF_FULL_ZEPHYR_APPLICATION) +__syscall const void *mod_fast_get(struct processing_module *mod, const void * const dram_ptr, + size_t size); +#else +const void *z_impl_mod_fast_get(struct processing_module *mod, const void * const dram_ptr, + size_t size); +#define mod_fast_get z_impl_mod_fast_get +#endif void mod_fast_put(struct processing_module *mod, const void *sram_ptr); #endif void mod_free_all(struct processing_module *mod); @@ -266,9 +321,10 @@ int module_unbind(struct processing_module *mod, struct bind_info *unbind_data); struct comp_dev *module_adapter_new(const struct comp_driver *drv, const struct comp_ipc_config *config, const void *spec); +struct userspace_context; struct comp_dev *module_adapter_new_ext(const struct comp_driver *drv, const struct comp_ipc_config *config, const void *spec, - void *mod_priv); + void *mod_priv, struct userspace_context *user_ctx); int module_adapter_prepare(struct comp_dev *dev); int module_adapter_params(struct comp_dev *dev, struct sof_ipc_stream_params *params); int module_adapter_copy(struct comp_dev *dev); @@ -376,7 +432,18 @@ int module_adapter_ts_get_op(struct comp_dev *dev, struct timestamp_data *tsd); void module_update_buffer_position(struct input_stream_buffer *input_buffers, struct output_stream_buffer *output_buffers, uint32_t frames); - +struct module_ext_init_data; +#if CONFIG_IPC_MAJOR_4 +int module_ext_init_decode(const struct comp_driver *drv, struct module_ext_init_data *ext_data, + struct ipc_config_process *spec); +#else +static inline +int module_ext_init_decode(const struct comp_driver *drv, struct module_ext_init_data *ext_data, + struct ipc_config_process *spec) +{ + return 0; +} +#endif int module_adapter_init_data(struct comp_dev *dev, struct module_config *dst, const struct comp_ipc_config *config, @@ -388,4 +455,33 @@ void module_adapter_set_params(struct processing_module *mod, struct sof_ipc_str int module_adapter_set_state(struct processing_module *mod, struct comp_dev *dev, int cmd); int module_adapter_sink_src_prepare(struct comp_dev *dev); + +/** + * Get a deadline based on current LFT reported by all sinks + * it returns a value >= UINT32_MAX / 2 in case the deadline cannot be calculated: + * - if a module is in a dealayed start + * - if there's no sink - i.e. DP module is a pure data consumer (like key phrare detector) + * + * @return a deadline the module must finish processing since NOW [in us] + */ +uint32_t module_get_deadline(struct processing_module *mod); + +/** + * Get a Longest Processing Time estimation for the module, in us + * It is either + * - a value taken from the manifest or estimated from module period (TODO) + * - or a value taken from IPC call (TODO) + * - a worst case - module period + * note that module period may be calculated reasonably late, i.e. in prepare() method + */ +static inline uint32_t module_get_lpt(struct processing_module *mod) +{ + /* return worst case of LPT - a module period. See zephyr_dp_schedule.c for description */ + return mod->dev->period; +} + +#if defined(__ZEPHYR__) && defined(CONFIG_SOF_FULL_ZEPHYR_APPLICATION) +#include <zephyr/syscalls/generic.h> +#endif + #endif /* __SOF_AUDIO_MODULE_GENERIC__ */ diff --git a/src/include/sof/audio/pipeline.h b/src/include/sof/audio/pipeline.h index 5221d330e0f1..913a569c208c 100644 --- a/src/include/sof/audio/pipeline.h +++ b/src/include/sof/audio/pipeline.h @@ -25,6 +25,7 @@ struct comp_buffer; struct comp_dev; struct ipc; struct ipc_msg; +struct k_heap; /* * Pipeline status to stop execution of current path, but to keep the @@ -52,6 +53,7 @@ struct ipc_msg; * Audio pipeline. */ struct pipeline { + struct k_heap *heap; /**< heap used for allocating this pipeline */ uint32_t comp_id; /**< component id for pipeline */ uint32_t pipeline_id; /**< pipeline id */ uint32_t sched_id; /**< Scheduling component id */ @@ -134,6 +136,13 @@ struct pipeline_task { struct comp_dev *sched_comp; /**< pipeline scheduling component */ }; +struct ipc4_pipeline_ext_obj_mem_data; + +/** \brief For storing IPC payload data. */ +struct create_pipeline_params { + const struct ipc4_pipeline_ext_obj_mem_data *mem_data; +}; + #define pipeline_task_get(t) container_of(t, struct pipeline_task, task) /* @@ -145,12 +154,15 @@ struct pipeline_task { /** * \brief Creates a new pipeline. + * \param[in] heap Heap to allocate the pipeline on, or NULL for default. * \param[in] pipeline_id Pipeline ID number. * \param[in] priority Pipeline scheduling priority. * \param[in] comp_id Pipeline component ID number. + * \param[in] pparams Pipeline parameters from IPC payload, maybe NULL. * \return New pipeline pointer or NULL. */ -struct pipeline *pipeline_new(uint32_t pipeline_id, uint32_t priority, uint32_t comp_id); +struct pipeline *pipeline_new(struct k_heap *heap, uint32_t pipeline_id, uint32_t priority, + uint32_t comp_id, struct create_pipeline_params *pparams); /** * \brief Free's a pipeline. @@ -257,7 +269,7 @@ int pipeline_params(struct pipeline *p, struct comp_dev *cd, struct sof_ipc_pcm_params *params); /** - * \brief Creates a new pipeline. + * \brief Prepares pipeline for processing. * \param[in] p pipeline. * \param[in,out] cd Pipeline component device. * \return 0 on success. @@ -357,9 +369,9 @@ int pipeline_comp_ll_task_init(struct pipeline *p); int pipeline_comp_dp_task_init(struct comp_dev *comp); /** - * \brief Free's a pipeline. + * \brief Schedule a pipeline copy to run after a delay * \param[in] p pipeline. - * \param[in] start Pipelien start time in microseconds. + * \param[in] start Pipeline start time in microseconds. */ void pipeline_schedule_copy(struct pipeline *p, uint64_t start); diff --git a/src/include/sof/audio/selector.h b/src/include/sof/audio/selector.h index cd4aa926c7f4..e977a47af9a9 100644 --- a/src/include/sof/audio/selector.h +++ b/src/include/sof/audio/selector.h @@ -27,6 +27,9 @@ struct comp_buffer; struct comp_dev; +/** \brief Default mix gain. */ +#define SEL_COEF_ONE_Q10 1024 /* int16(1 * 2^10) */ + #if CONFIG_IPC_MAJOR_3 /** \brief Supported channel count on input. */ #define SEL_SOURCE_2CH 2 @@ -43,6 +46,9 @@ struct comp_dev; /** \brief Maximum supported channel count on output. */ #define SEL_SINK_CHANNELS_MAX 8 +/** \brief Maximum number of configurations in the blob received with set_config() */ +#define SEL_MAX_NUM_CONFIGS 8 + #define SEL_NUM_IN_PIN_FMTS 1 #define SEL_NUM_OUT_PIN_FMTS 1 @@ -60,8 +66,10 @@ enum ipc4_selector_config_id { /** \brief IPC4 mixing coefficients configuration. */ struct ipc4_selector_coeffs_config { - uint16_t rsvd0; /**< Unused field, keeps the structure aligned with common layout */ - uint16_t rsvd1; /**< Unused field, keeps the structure aligned with common layout */ + uint8_t source_channels_count; /**< Used when multiple profiles are packed into one blob. */ + uint8_t sink_channels_count; /**< Used when multiple profiles are packed into one blob. */ + uint8_t source_channel_config; /**< Used when multiple profiles are packed into one blob. */ + uint8_t sink_channel_config; /**< Used when multiple profiles are packed into one blob. */ /** Mixing coefficients in Q10 fixed point format */ int16_t coeffs[SEL_SINK_CHANNELS_MAX][SEL_SOURCE_CHANNELS_MAX]; @@ -109,6 +117,8 @@ struct comp_data { #if CONFIG_IPC_MAJOR_4 struct sof_selector_ipc4_config sel_ipc4_cfg; struct ipc4_selector_coeffs_config coeffs_config; + struct ipc4_selector_coeffs_config *multi_coeffs_config; + size_t multi_coeffs_config_size; #endif uint32_t source_period_bytes; /**< source number of period bytes */ @@ -117,6 +127,9 @@ struct comp_data { enum sof_ipc_frame sink_format; /**< sink frame format */ struct sof_sel_config config; /**< component configuration data */ sel_func sel_func; /**< channel selector processing function */ + int num_configs; /**< Number of coefficients sets in configuration blob. */ + bool passthrough; /**< Use a passthrough copy function when no up/down mix. */ + bool new_config; /**< True if new configuration has been received */ }; /** \brief Selector processing functions map. */ diff --git a/src/include/sof/boot_test.h b/src/include/sof/boot_test.h index 965eec06a6a9..1af6e7f2d8e0 100644 --- a/src/include/sof/boot_test.h +++ b/src/include/sof/boot_test.h @@ -6,8 +6,14 @@ #ifndef __SOF_BOOT_TEST_H__ #define __SOF_BOOT_TEST_H__ +#if !CONFIG_LIBRARY +#include <zephyr/logging/log.h> +#else +#define LOG_ERR(...) do {} while (0) +#endif #include <stdbool.h> +#if CONFIG_SOF_BOOT_TEST #define TEST_RUN_ONCE(fn, ...) do { \ static bool once; \ if (!once) { \ @@ -15,6 +21,9 @@ fn(__VA_ARGS__); \ } \ } while (0) +#else +#define TEST_RUN_ONCE(fn, ...) do {} while (0) +#endif #define TEST_CHECK_RET(ret, testname) do { \ if ((ret) < 0) { \ @@ -25,4 +34,6 @@ } \ } while (0) +void sof_run_boot_tests(void); + #endif diff --git a/src/include/sof/common.h b/src/include/sof/common.h index 4bb279b039aa..d7aa9a7989cb 100644 --- a/src/include/sof/common.h +++ b/src/include/sof/common.h @@ -223,6 +223,9 @@ # define SOF_FRAME_BYTE_ALIGN 4 #endif +/* Default frame-count alignment is 1 */ +#define SOF_FRAME_COUNT_ALIGN 1 + #ifndef __GLIBC_USE #define __GLIBC_USE(x) 0 #endif diff --git a/src/include/sof/debug/telemetry/telemetry.h b/src/include/sof/debug/telemetry/telemetry.h index 4f8a869a9dd6..6c2d5fdf8cb7 100644 --- a/src/include/sof/debug/telemetry/telemetry.h +++ b/src/include/sof/debug/telemetry/telemetry.h @@ -11,8 +11,11 @@ #include <zephyr/timing/timing.h> #endif +#ifndef CONFIG_INTEL_ADSP_DEBUG_SLOT_MANAGER /* Slot in memory window 2 (Debug Window) to be used as telemetry slot */ #define SOF_DW_TELEMETRY_SLOT 1 +#endif + /* Memory of average algorithm of performance queue */ #define SOF_AVG_PERF_MEAS_DEPTH 64 /* Number of runs taken to calculate average (algorithm resolution) */ @@ -87,6 +90,9 @@ struct telemetry_perf_queue { }; void telemetry_update(uint32_t begin_ccount, uint32_t current_ccount); +#ifdef CONFIG_INTEL_ADSP_DEBUG_SLOT_MANAGER +struct system_tick_info *telemetry_get_systick_info_ptr(void); +#endif #ifdef CONFIG_TIMING_FUNCTIONS #define telemetry_timestamp timing_counter_get diff --git a/src/include/sof/ipc/notification_pool.h b/src/include/sof/ipc/notification_pool.h index 0e41a6effcb9..835399307ae2 100644 --- a/src/include/sof/ipc/notification_pool.h +++ b/src/include/sof/ipc/notification_pool.h @@ -23,4 +23,15 @@ */ struct ipc_msg *ipc_notification_pool_get(size_t size); +#if CONFIG_LIBRARY +/** + * @brief Frees all IPC notification messages in the pool. + * + * This function frees all notification messages currently held in + * the pool free list and resets the pool depth counter. It is + * required only in library (testbench) build. + */ +void ipc_notification_pool_free(void); +#endif /* CONFIG_LIBRARY */ + #endif /* __SOF_IPC_NOTIFICATION_POOL_H__ */ diff --git a/src/include/sof/lib/dai-legacy.h b/src/include/sof/lib/dai-legacy.h index 44142412334b..c1b7c0a28653 100644 --- a/src/include/sof/lib/dai-legacy.h +++ b/src/include/sof/lib/dai-legacy.h @@ -419,7 +419,7 @@ void dai_put(struct dai *dai); * \brief Digital Audio interface formatting */ static inline int dai_set_config(struct dai *dai, struct ipc_config_dai *config, - const void *spec_config) + const void *spec_config, size_t size) { return dai->drv->ops.set_config(dai, config, spec_config); } diff --git a/src/include/sof/lib/dai-zephyr.h b/src/include/sof/lib/dai-zephyr.h index cff5fffb4228..d25474c4816d 100644 --- a/src/include/sof/lib/dai-zephyr.h +++ b/src/include/sof/lib/dai-zephyr.h @@ -28,6 +28,7 @@ #include <sof/ipc/topology.h> #include <sof/audio/pcm_converter.h> #include <sof/audio/ipc-config.h> +#include <ipc/dai.h> #include <errno.h> #include <stddef.h> #include <stdint.h> @@ -116,7 +117,7 @@ typedef int (*channel_copy_func)(const struct audio_stream *src, unsigned int sr */ struct dai_data { /* local DMA config */ - struct dma_chan_data *chan; + int chan_index; uint32_t stream_id; struct dma_sg_config config; struct dma_config *z_config; @@ -163,7 +164,7 @@ struct dai_data { #endif #ifdef CONFIG_SOF_TELEMETRY_IO_PERFORMANCE_MEASUREMENTS /* io performance measurement */ - struct io_perf_data_item *io_perf_bytes_count; + struct io_perf_data_item *io_perf_dai_byte_count; #endif /* Copier gain params */ struct copier_gain_params *gain_data; @@ -237,7 +238,8 @@ void dai_put(struct dai *dai); /** * \brief Digital Audio interface formatting */ -int dai_set_config(struct dai *dai, struct ipc_config_dai *config, const void *spec_config); +int dai_set_config(struct dai *dai, struct ipc_config_dai *config, + const void *spec_config, size_t size); /** * \brief Get Digital Audio interface DMA Handshake @@ -304,7 +306,14 @@ void dai_release_llp_slot(struct dai_data *dd); /** * \brief Retrieve a pointer to the Zephyr device structure for a DAI of a given type and index. */ -const struct device *dai_get_device(uint32_t type, uint32_t index); +const struct device *dai_get_device(enum sof_ipc_dai_type type, uint32_t index); + +/** + * \brief Retrieve the list of all DAI devices. + * \param count Pointer to store the number of devices in the list. + * \return Pointer to the array of device pointers. + */ +const struct device **dai_get_device_list(size_t *count); /** @}*/ #endif /* __SOF_LIB_DAI_ZEPHYR_H__ */ diff --git a/src/include/sof/lib/fast-get.h b/src/include/sof/lib/fast-get.h index ffbac19cf1b8..05f098a752cc 100644 --- a/src/include/sof/lib/fast-get.h +++ b/src/include/sof/lib/fast-get.h @@ -10,7 +10,31 @@ #include <stddef.h> -const void *fast_get(const void * const dram_ptr, size_t size); -void fast_put(const void *sram_ptr); +struct k_mem_domain; +struct mod_alloc_ctx; + +/* + * When built for SOF, fast_get() and fast_put() are only needed when DRAM + * storage and execution is enabled (CONFIG_COLD_STORE_EXECUTE_DRAM=y), but not + * when building LLEXT extensions (!defined(LL_EXTENSION_BUILD)), using Zephyr + * SDK (CONFIG_LLEXT_TYPE_ELF_RELOCATABLE=n while + * CONFIG_LLEXT_TYPE_ELF_SHAREDLIB=y). + * For unit-testing full versions of fast_get() and fast_put() are checked by + * test/ztest/unit/fast-get/ and test/cmocka/src/lib/fast-get/ + */ +#if (CONFIG_COLD_STORE_EXECUTE_DRAM && \ + (CONFIG_LLEXT_TYPE_ELF_RELOCATABLE || !defined(LL_EXTENSION_BUILD))) || \ + !CONFIG_SOF_FULL_ZEPHYR_APPLICATION +const void *fast_get(struct mod_alloc_ctx *alloc, const void * const dram_ptr, size_t size); +void fast_put(struct mod_alloc_ctx *alloc, struct k_mem_domain *mdom, const void *sram_ptr); +#else +static inline const void *fast_get(struct mod_alloc_ctx *alloc, const void * const dram_ptr, + size_t size) +{ + return dram_ptr; +} +static inline void fast_put(struct mod_alloc_ctx *alloc, struct k_mem_domain *mdom, + const void *sram_ptr) {} +#endif #endif /* __SOF_LIB_FAST_GET_H__ */ diff --git a/src/include/sof/lib/mailbox.h b/src/include/sof/lib/mailbox.h index 06e2659a5847..ae667f0d0c4c 100644 --- a/src/include/sof/lib/mailbox.h +++ b/src/include/sof/lib/mailbox.h @@ -100,6 +100,14 @@ void mailbox_hostbox_read(void *dest, size_t dest_size, assert(!host_read_err); } +#if CONFIG_IPC_MAJOR_4 +static inline +void mailbox_stream_write(size_t offset, const void *src, size_t bytes) +{ + /* in IPC4, the stream mailbox must not be used */ + assert(false); +} +#else static inline void mailbox_stream_write(size_t offset, const void *src, size_t bytes) { @@ -110,5 +118,6 @@ void mailbox_stream_write(size_t offset, const void *src, size_t bytes) dcache_writeback_region((__sparse_force void __sparse_cache *)(MAILBOX_STREAM_BASE + offset), bytes); } +#endif #endif /* __SOF_LIB_MAILBOX_H__ */ diff --git a/src/include/sof/lib/notifier.h b/src/include/sof/lib/notifier.h index 060906655cb8..87ca2cd40265 100644 --- a/src/include/sof/lib/notifier.h +++ b/src/include/sof/lib/notifier.h @@ -27,9 +27,6 @@ enum notify_id { NOTIFIER_ID_SSP_FREQ, /* struct clock_notify_data * */ NOTIFIER_ID_KPB_CLIENT_EVT, /* struct kpb_event_data * */ NOTIFIER_ID_DMA_DOMAIN_CHANGE, /* struct dma_chan_data * */ - NOTIFIER_ID_BUFFER_PRODUCE, /* struct buffer_cb_transact* */ - NOTIFIER_ID_BUFFER_CONSUME, /* struct buffer_cb_transact* */ - NOTIFIER_ID_BUFFER_FREE, /* struct buffer_cb_free* */ NOTIFIER_ID_DMA_COPY, /* struct dma_cb_data* */ NOTIFIER_ID_LL_POST_RUN, /* NULL */ NOTIFIER_ID_DMA_IRQ, /* struct dma_chan_data * */ diff --git a/src/include/sof/lib/vregion.h b/src/include/sof/lib/vregion.h new file mode 100644 index 000000000000..612443f5bc48 --- /dev/null +++ b/src/include/sof/lib/vregion.h @@ -0,0 +1,186 @@ +// SPDX-License-Identifier: BSD-3-Clause +// Copyright(c) 2025 Intel Corporation. + +/* Pre Allocated Contiguous Virtual Region */ +#ifndef __SOF_LIB_VREGION_H__ +#define __SOF_LIB_VREGION_H__ + +#include <stddef.h> + +#ifdef __cplusplus +extern "C" { +#endif + +struct vregion; + +/** + * @brief Memory types for virtual region allocations. + * Used to specify the type of memory allocation within a virtual region. + * + * @note + * - interim: allocation that can be freed i.e. get/set large config, kcontrols. + * - lifetime: allocation that cannot be freed i.e. init data, pipeline data. + */ +enum vregion_mem_type { + VREGION_MEM_TYPE_INTERIM, /* interim allocation that can be freed */ + VREGION_MEM_TYPE_LIFETIME, /* lifetime allocation */ +}; + +#if CONFIG_SOF_VREGIONS + +/** + * @brief Create a new virtual region instance. + * + * Create a new virtual region instance with specified static and dynamic partitions. + * Total size is the sum of static and dynamic sizes. + * + * @param[in] lifetime_size Size of the virtual region lifetime partition. + * @param[in] interim_size Size of the virtual region interim partition. + * @return struct vregion* Pointer to the new virtual region instance, or NULL on failure. + */ +struct vregion *vregion_create(size_t lifetime_size, size_t interim_size); + +/** + * @brief Increment virtual region's user count. + * + * The creator of the virtual region is its first user, for any additional users + * increment the region's use-count. + * + * @param[in] vr Pointer to the virtual region instance to release. + * @return struct vregion* Pointer to the virtual region instance. + */ +struct vregion *vregion_get(struct vregion *vr); + +/** + * @brief Decrement virtual region's user count or destroy it. + * + * Decrement virtual region's user count, when it reaches 0 free all associated + * resources. + * + * @param[in] vr Pointer to the virtual region instance to release. + * @return struct vregion* Pointer to the virtual region instance or NULL if it has been destroyed. + */ +struct vregion *vregion_put(struct vregion *vr); + +/** + * @brief Allocate memory from the specified virtual region. + * + * @param[in] vr Pointer to the virtual region instance. + * @param[in] type Type of memory to allocate (lifetime or interim). + * @param[in] size Size of memory to allocate in bytes. + * @return void* Pointer to the allocated memory, or NULL on failure. + */ +void *vregion_alloc(struct vregion *vr, enum vregion_mem_type type, size_t size); + +/** + * @brief like vregion_alloc() but allocates coherent memory + */ +void *vregion_alloc_coherent(struct vregion *vr, enum vregion_mem_type type, size_t size); + +/** + * @brief Allocate aligned memory from the specified virtual region. + * + * Allocate aligned memory from the specified virtual region based on the memory type. + * + * @param[in] vr Pointer to the virtual region instance. + * @param[in] type Type of memory to allocate (lifetime or interim). + * @param[in] size Size of memory to allocate in bytes. + * @param[in] alignment Alignment of memory to allocate in bytes. + * @return void* Pointer to the allocated memory, or NULL on failure. + */ +void *vregion_alloc_align(struct vregion *vr, enum vregion_mem_type type, + size_t size, size_t alignment); + +/** + * @brief like vregion_alloc_align() but allocates coherent memory + */ +void *vregion_alloc_coherent_align(struct vregion *vr, enum vregion_mem_type type, + size_t size, size_t alignment); + +/** + * @brief Free memory allocated from the specified virtual region. + * + * Free memory previously allocated from the specified virtual region. + * + * @param[in] vr Pointer to the virtual region instance. + * @param[in] ptr Pointer to the memory to free. + */ +void vregion_free(struct vregion *vr, void *ptr); + +/** + * @brief Log virtual region memory usage. + * + * @param[in] vr Pointer to the virtual region instance. + */ +void vregion_info(struct vregion *vr); + +/** + * @brief Get virtual region memory start and size. + * + * @param[in] vr Pointer to the virtual region instance. + * @param[in] size Pointer to size + * @param[in] start Pointer to start + */ +void vregion_mem_info(struct vregion *vr, size_t *size, uintptr_t *start); + +#else /* CONFIG_SOF_VREGIONS */ + +#include <rtos/alloc.h> + +struct vregion { + unsigned int use_count; +}; + +static inline struct vregion *vregion_create(size_t lifetime_size, size_t interim_size) +{ + struct vregion *vr = rmalloc(0, sizeof(*vr)); + + vr->use_count = 1; + return vr; +} +static inline struct vregion *vregion_get(struct vregion *vr) +{ + if (vr) + vr->use_count++; + return vr; +} +static inline struct vregion *vregion_put(struct vregion *vr) +{ + if (vr && !--vr->use_count) + rfree(vr); + return vr; +} +static inline void *vregion_alloc(struct vregion *vr, enum vregion_mem_type type, size_t size) +{ + return NULL; +} +static inline void *vregion_alloc_coherent(struct vregion *vr, enum vregion_mem_type type, + size_t size) +{ + return NULL; +} +static inline void *vregion_alloc_align(struct vregion *vr, enum vregion_mem_type type, + size_t size, size_t alignment) +{ + return NULL; +} +static inline void *vregion_alloc_coherent_align(struct vregion *vr, enum vregion_mem_type type, + size_t size, size_t alignment) +{ + return NULL; +} +static inline void vregion_free(struct vregion *vr, void *ptr) {} +static inline void vregion_info(struct vregion *vr) {} +static inline void vregion_mem_info(struct vregion *vr, size_t *size, uintptr_t *start) +{ + if (size) + *size = 0; +} + +#endif /* CONFIG_SOF_VREGIONS */ + +#ifdef __cplusplus +} +#endif + +#endif /* __SOF_LIB_VREGION_H__ */ diff --git a/src/include/sof/lib_manager.h b/src/include/sof/lib_manager.h index ef32b5d75e87..29c226eb61a7 100644 --- a/src/include/sof/lib_manager.h +++ b/src/include/sof/lib_manager.h @@ -77,6 +77,9 @@ #define LIB_MANAGER_GET_LIB_ID(module_id) (((module_id) & 0xF000) >> LIB_MANAGER_LIB_ID_SHIFT) #define LIB_MANAGER_GET_MODULE_INDEX(module_id) ((module_id) & 0xFFF) +#define LIB_MANAGER_PACK_MODULE_ID(lib_index, module_index) (((module_index) & 0xFFF) | \ + (((lib_index) << LIB_MANAGER_LIB_ID_SHIFT) & 0xF000)) +#define LIB_MANAGER_PACK_LIB_ID(lib_index) LIB_MANAGER_PACK_MODULE_ID(lib_index, 0x0) #ifdef CONFIG_LIBRARY_MANAGER struct ipc_lib_msg { @@ -91,6 +94,8 @@ enum { LIB_MANAGER_DATA, LIB_MANAGER_RODATA, LIB_MANAGER_BSS, + LIB_MANAGER_COLD, + LIB_MANAGER_COLDRODATA, LIB_MANAGER_N_SEGMENTS, }; @@ -198,15 +203,6 @@ void lib_manager_get_instance_bss_address(uint32_t instance_id, const struct sof_man_module *mod, void __sparse_cache **va_addr, size_t *size); -/* - * \brief Free module - * - * param[in] component_id - component id coming from ipc config. This function reguires valid - * lib_id and module_id fields of component id. - * - * Function is responsible to free module resources in HP memory. - */ -int lib_manager_free_module(const uint32_t component_id); /* * \brief Load library * diff --git a/src/include/sof/llext_manager.h b/src/include/sof/llext_manager.h index 44928c5a124f..525fa50b1506 100644 --- a/src/include/sof/llext_manager.h +++ b/src/include/sof/llext_manager.h @@ -15,6 +15,7 @@ struct comp_dev; struct comp_driver; struct comp_ipc_config; +struct k_mem_domain; static inline bool module_is_llext(const struct sof_man_module *mod) { @@ -29,12 +30,16 @@ int llext_manager_free_module(const uint32_t component_id); int llext_manager_add_library(uint32_t module_id); +int llext_manager_add_domain(const uint32_t component_id, struct k_mem_domain *domain); +int llext_manager_rm_domain(const uint32_t component_id, struct k_mem_domain *domain); + bool comp_is_llext(struct comp_dev *comp); #else #define module_is_llext(mod) false #define llext_manager_allocate_module(ipc_config, ipc_specific_config) 0 #define llext_manager_free_module(component_id) 0 #define llext_manager_add_library(module_id) 0 +#define llext_manager_add_domain(component_id, domain) 0 #define comp_is_llext(comp) false #endif diff --git a/src/include/sof/math/auditory.h b/src/include/sof/math/auditory.h index eef073092e22..bd707dc5079a 100644 --- a/src/include/sof/math/auditory.h +++ b/src/include/sof/math/auditory.h @@ -76,7 +76,9 @@ int16_t psy_mel_to_hz(int16_t mel); * filter coefficients. * \return Zero when success, otherwise error code. */ -int psy_get_mel_filterbank(struct psy_mel_filterbank *mel_fb); +struct processing_module; +int mod_psy_get_mel_filterbank(struct processing_module *mod, struct psy_mel_filterbank *mel_fb); +int mod_psy_free_mel_filterbank(struct processing_module *mod, struct psy_mel_filterbank *mel_fb); /** * \brief Convert linear complex spectra from FFT into Mel band energies in desired @@ -101,11 +103,11 @@ void psy_apply_mel_filterbank_16(struct psy_mel_filterbank *mel_fb, struct icomp * \param[in] fft_out Array of complex numbers from FFT in Q1.31 format. * \param[out] power_spectra Array of linear power spectra, needed scratch are that is half + 1 * side of fft_out. The data can be discarded after if no use. - * \param[out] mel_log Array of Q9.7 log/log10/10log10 format Mel band energies. + * \param[out] mel_log Array of Q9.23 log/log10/10log10 format Mel band energies. * \param[in] bitshift A shift left scale that has been possibly applied to FFT. This will * be subtracted from the log or decibels notation. */ void psy_apply_mel_filterbank_32(struct psy_mel_filterbank *mel_fb, struct icomplex32 *fft_out, - int32_t *power_spectra, int16_t *mel_log, int bitshift); + int32_t *power_spectra, int32_t *mel_log, int bitshift); #endif /* __SOF_MATH_AUDITORY_H__ */ diff --git a/src/include/sof/math/dct.h b/src/include/sof/math/dct.h index bb3aca81b202..7c754f448c21 100644 --- a/src/include/sof/math/dct.h +++ b/src/include/sof/math/dct.h @@ -29,6 +29,8 @@ struct dct_plan_16 { bool ortho; }; -int dct_initialize_16(struct dct_plan_16 *dct); +int mod_dct_initialize_16(struct processing_module *mod, struct dct_plan_16 *dct); + +int mod_dct_free_16(struct processing_module *mod, struct dct_plan_16 *dct); #endif /* __SOF_MATH_DCT_H__ */ diff --git a/src/include/sof/math/fft.h b/src/include/sof/math/fft.h index 504465401cad..df06baf47c81 100644 --- a/src/include/sof/math/fft.h +++ b/src/include/sof/math/fft.h @@ -9,7 +9,9 @@ #ifndef __SOF_FFT_H__ #define __SOF_FFT_H__ +#include <sof/audio/module_adapter/module/generic.h> #include <sof/audio/format.h> +#include <sof/math/icomplex32.h> #include <sof/common.h> #include <stdbool.h> #include <stdint.h> @@ -25,21 +27,9 @@ #endif -#define FFT_SIZE_MAX 1024 - -struct icomplex32 { - int32_t real; - int32_t imag; -}; - -/* Note: the add of packed attribute to icmplex16 would significantly increase - * processing time of fft_execute_16() so it is not done. The optimized versions of - * FFT for HiFi will need a different packed data structure vs. generic C. - */ -struct icomplex16 { - int16_t real; - int16_t imag; -}; +#define FFT_SIZE_MIN 1 +#define FFT_SIZE_MAX 1024 +#define FFT_MULTI_COUNT_MAX 3 struct fft_plan { uint32_t size; /* fft size */ @@ -51,10 +41,69 @@ struct fft_plan { struct icomplex16 *outb16; /* pointer to output integer complex buffer */ }; +struct fft_multi_plan { + struct fft_plan *fft_plan[FFT_MULTI_COUNT_MAX]; + struct icomplex32 *tmp_i32[FFT_MULTI_COUNT_MAX]; /* pointer to input buffer */ + struct icomplex32 *tmp_o32[FFT_MULTI_COUNT_MAX]; /* pointer to output buffer */ + struct icomplex32 *inb32; /* pointer to input integer complex buffer */ + struct icomplex32 *outb32; /* pointer to output integer complex buffer */ + uint16_t *bit_reverse_idx; + uint32_t total_size; + uint32_t fft_size; + int num_ffts; +}; + /* interfaces of the library */ -struct fft_plan *fft_plan_new(void *inb, void *outb, uint32_t size, int bits); +struct fft_plan *mod_fft_plan_new(struct processing_module *mod, void *inb, + void *outb, uint32_t size, int bits); void fft_execute_16(struct fft_plan *plan, bool ifft); void fft_execute_32(struct fft_plan *plan, bool ifft); -void fft_plan_free(struct fft_plan *plan16); +void mod_fft_plan_free(struct processing_module *mod, struct fft_plan *plan); + +/** + * mod_fft_multi_plan_new() - Prepare FFT for 2^N size and some other FFT sizes + * @param mod: Pointer to module + * @param inb: Buffer to use for complex input data + * @param outb: Buffer to use for complex output data + * @param size: Size of FFT as number of bins + * @param bits: World length of FFT. Currently only 32 is supported. + * @return Pointer to allocated FFT plan + * + * This function does the preparations to calculate FFT. If the size is power of two + * the operation is similar to mod_fft_plan_new(). Some other FFT sizes like 1536 is + * supported by allocated multiple FFT plans and by wrapping all needed for similar + * usage as power of two size FFT. + */ + +struct fft_multi_plan *mod_fft_multi_plan_new(struct processing_module *mod, void *inb, + void *outb, uint32_t size, int bits); +/** + * fft_multi_execute_32() - Calculate Fast Fourier Transform (FFT) for 2^size and other + * @param plan: Pointer to FFT plan created with mod_fft_multi_plan_new() + * @param ifft: Value 0 calculates FFT, value 1 calculates IFFT + * + * This function calculates the FFT with the buffers defined with mod_fft_multi_plan_new(). + */ +void fft_multi_execute_32(struct fft_multi_plan *plan, bool ifft); + +/** + * mod_fft_multi_plan_free() - Free the FFT plan + * @param mod: Pointer to module + * @param plan: Pointe to FFT plan + * + * This function frees the allocations done internally by the function mod_fft_multi_plan_new(). + * The input and output buffers need to be freed separately. + */ +void mod_fft_multi_plan_free(struct processing_module *mod, struct fft_multi_plan *plan); + +/** + * dft3_32() - Discrete Fourier Transform (DFT) for size 3. + * @param input: Pointer to complex values input array, Q1.31. + * @param output: Pointer to complex values output array, Q1.31, scaled down by 1/3. + * + * This function is useful for calculating some non power of two FFTs. E.g. the + * FFT for size 1536 is done with three 512 size FFTs and one 3 size DFT. + */ +void dft3_32(struct icomplex32 *input, struct icomplex32 *output); #endif /* __SOF_FFT_H__ */ diff --git a/src/include/sof/math/icomplex16.h b/src/include/sof/math/icomplex16.h new file mode 100644 index 000000000000..a2ade94660ba --- /dev/null +++ b/src/include/sof/math/icomplex16.h @@ -0,0 +1,84 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// Copyright(c) 2020-2026 Intel Corporation. +// +// Author: Amery Song <chao.song@intel.com> +// Keyon Jie <yang.jie@linux.intel.com> + +#include <sof/audio/format.h> +#include <sof/common.h> +#include <stdint.h> + +#ifndef __SOF_ICOMPLEX16_H__ +#define __SOF_ICOMPLEX16_H__ + +/* Note: the add of packed attribute to icmplex16 would significantly increase + * processing time of fft_execute_16() so it is not done. The optimized versions of + * FFT for HiFi will need a different packed data structure vs. generic C. + * + * TODO: Use with care with other than 16-bit FFT internals. Access with intrinsics + * will requires packed and aligned data. Currently there is no such usage in SOF. + */ + +/** + * struct icomplex16 - Storage for a normal complex number. + * @param real The real part in Q1.15 fractional format. + * @param imag The imaginary part in Q1.15 fractional format. + */ +struct icomplex16 { + int16_t real; + int16_t imag; +}; + +/* + * Helpers for 16 bit FFT calculation + */ +static inline void icomplex16_add(const struct icomplex16 *in1, const struct icomplex16 *in2, + struct icomplex16 *out) +{ + out->real = in1->real + in2->real; + out->imag = in1->imag + in2->imag; +} + +static inline void icomplex16_sub(const struct icomplex16 *in1, const struct icomplex16 *in2, + struct icomplex16 *out) +{ + out->real = in1->real - in2->real; + out->imag = in1->imag - in2->imag; +} + +static inline void icomplex16_mul(const struct icomplex16 *in1, const struct icomplex16 *in2, + struct icomplex16 *out) +{ + int32_t real = (int32_t)in1->real * in2->real - (int32_t)in1->imag * in2->imag; + int32_t imag = (int32_t)in1->real * in2->imag + (int32_t)in1->imag * in2->real; + + out->real = Q_SHIFT_RND(real, 30, 15); + out->imag = Q_SHIFT_RND(imag, 30, 15); +} + +/* complex conjugate */ +static inline void icomplex16_conj(struct icomplex16 *comp) +{ + comp->imag = sat_int16(-((int32_t)comp->imag)); +} + +/* shift a complex n bits, n > 0: left shift, n < 0: right shift */ +static inline void icomplex16_shift(const struct icomplex16 *input, int16_t n, + struct icomplex16 *output) +{ + int n1, n2; + + if (n >= 0) { + /* need saturation handling */ + output->real = sat_int16((int32_t)input->real << n); + output->imag = sat_int16((int32_t)input->imag << n); + } else { + n1 = -n; + n2 = 1 << (n1 - 1); + output->real = sat_int16(((int32_t)input->real + n2) >> n1); + output->imag = sat_int16(((int32_t)input->imag + n2) >> n1); + } +} + +#endif /* __SOF_ICOMPLEX16_H__ */ diff --git a/src/include/sof/math/icomplex32.h b/src/include/sof/math/icomplex32.h new file mode 100644 index 000000000000..29d22daadab8 --- /dev/null +++ b/src/include/sof/math/icomplex32.h @@ -0,0 +1,113 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// Copyright(c) 2020-2026 Intel Corporation. +// +// Author: Amery Song <chao.song@intel.com> +// Keyon Jie <yang.jie@linux.intel.com> + +#include <sof/audio/format.h> +#include <sof/math/exp_fcn.h> +#include <sof/math/log.h> +#include <sof/math/trig.h> +#include <sof/common.h> +#include <stdint.h> + +#ifndef __SOF_ICOMPLEX32_H__ +#define __SOF_ICOMPLEX32_H__ + +/** + * struct icomplex32 - Storage for a normal complex number. + * @param real The real part in Q1.31 fractional format. + * @param imag The imaginary part in Q1.31 fractional format. + */ +struct icomplex32 { + int32_t real; + int32_t imag; +}; + +/** + * struct ipolar32 - Storage for complex number in polar format. + * @param magnitude The length of vector in Q2.30 format. + * @param angle The phase angle of the vector -pi to +pi in Q3.29 format. + */ +struct ipolar32 { + int32_t magnitude; + int32_t angle; +}; + +/* + * These helpers are optimized for FFT calculation only. + * e.g. _add/sub() assume the output won't be saturate so no check needed, + * and _mul() assumes Q1.31 * Q1.31 so the output will be shifted to be Q1.31. + */ + +static inline void icomplex32_add(const struct icomplex32 *in1, const struct icomplex32 *in2, + struct icomplex32 *out) +{ + out->real = in1->real + in2->real; + out->imag = in1->imag + in2->imag; +} + +static inline void icomplex32_adds(const struct icomplex32 *in1, const struct icomplex32 *in2, + struct icomplex32 *out) +{ + out->real = sat_int32((int64_t)in1->real + in2->real); + out->imag = sat_int32((int64_t)in1->imag + in2->imag); +} + +static inline void icomplex32_sub(const struct icomplex32 *in1, const struct icomplex32 *in2, + struct icomplex32 *out) +{ + out->real = in1->real - in2->real; + out->imag = in1->imag - in2->imag; +} + +static inline void icomplex32_mul(const struct icomplex32 *in1, const struct icomplex32 *in2, + struct icomplex32 *out) +{ + out->real = ((int64_t)in1->real * in2->real - (int64_t)in1->imag * in2->imag) >> 31; + out->imag = ((int64_t)in1->real * in2->imag + (int64_t)in1->imag * in2->real) >> 31; +} + +/* complex conjugate */ +static inline void icomplex32_conj(struct icomplex32 *comp) +{ + comp->imag = SATP_INT32((int64_t)-1 * comp->imag); +} + +/* shift a complex n bits, n > 0: left shift, n < 0: right shift */ +static inline void icomplex32_shift(const struct icomplex32 *input, int32_t n, + struct icomplex32 *output) +{ + if (n > 0) { + /* need saturation handling */ + output->real = SATP_INT32(SATM_INT32((int64_t)input->real << n)); + output->imag = SATP_INT32(SATM_INT32((int64_t)input->imag << n)); + } else { + output->real = input->real >> -n; + output->imag = input->imag >> -n; + } +} + +/** + * sofm_icomplex32_to_polar() - Convert (re, im) complex number to polar. + * @param complex Pointer to input complex number in Q1.31 format. + * @param polar Pointer to output complex number in Q2.30 format for + * magnitude and Q3.29 for phase angle. + * + * The function can be used to convert data in-place with same address for + * input and output. It can be useful to save scratch memory. + */ +void sofm_icomplex32_to_polar(struct icomplex32 *complex, struct ipolar32 *polar); + +/** + * sofm_ipolar32_to_complex() - Convert complex number from polar to normal (re, im) format. + * @param polar Pointer to input complex number in polar format. + * @param complex Pointer to output complex number in normal format in Q1.31. + * + * This function can be used to convert data in-place with same address for input + * and output. It can be useful to save scratch memory. + */ +void sofm_ipolar32_to_complex(struct ipolar32 *polar, struct icomplex32 *complex); + +#endif /* __SOF_ICOMPLEX32_H__ */ diff --git a/src/include/sof/math/matrix.h b/src/include/sof/math/matrix.h index 74899371038a..f48f1ca4960d 100644 --- a/src/include/sof/math/matrix.h +++ b/src/include/sof/math/matrix.h @@ -10,6 +10,7 @@ #ifndef __SOF_MATH_MATRIX_H__ #define __SOF_MATH_MATRIX_H__ +#include <sof/audio/module_adapter/module/generic.h> #include <rtos/alloc.h> #include <ipc/topology.h> #include <stdint.h> @@ -44,6 +45,20 @@ static inline struct mat_matrix_16b *mat_matrix_alloc_16b(int16_t rows, int16_t return mat; } +static inline struct mat_matrix_16b *mod_mat_matrix_alloc_16b(struct processing_module *mod, + int16_t rows, int16_t columns, + int16_t fractions) +{ + struct mat_matrix_16b *mat; + const int mat_size = sizeof(int16_t) * rows * columns + sizeof(struct mat_matrix_16b); + + mat = mod_zalloc(mod, mat_size); + if (mat) + mat_init_16b(mat, rows, columns, fractions); + + return mat; +} + static inline void mat_copy_from_linear_16b(struct mat_matrix_16b *mat, const int16_t *lin_data) { size_t bytes = sizeof(int16_t) * mat->rows * mat->columns; diff --git a/src/include/sof/math/numbers.h b/src/include/sof/math/numbers.h index 0cd9314f4859..80d4eba1a639 100644 --- a/src/include/sof/math/numbers.h +++ b/src/include/sof/math/numbers.h @@ -36,7 +36,11 @@ __a > 0 ? 1 : 0; \ }) +/* Zephyr added gcd() in 2025/Nov to sys/util.h */ +#ifndef gcd +#define USE_SOF_GCD 1 int gcd(int a, int b); /* Calculate greatest common divisor for a and b */ +#endif /* This is a divide function that returns ceil of the quotient. * E.g. ceil_divide(9, 3) returns 3, ceil_divide(10, 3) returns 4. diff --git a/src/include/sof/math/sqrt.h b/src/include/sof/math/sqrt.h index 1cdb82afe9a0..3d8fd0df72fe 100644 --- a/src/include/sof/math/sqrt.h +++ b/src/include/sof/math/sqrt.h @@ -1,15 +1,32 @@ /* SPDX-License-Identifier: BSD-3-Clause * - * Copyright(c) 2021 Intel Corporation. All rights reserved. + * Copyright(c) 2021-2026 Intel Corporation. * * Author: Shriram Shastry <malladi.sastry@linux.intel.com> * */ -#ifndef __SOF_MATH__SQRTLOOKUP__H -#define __SOF_MATH__SQRTLOOKUP__H +#ifndef __SOF_MATH_SQRT_H__ +#define __SOF_MATH_SQRT_H__ #include <stdint.h> -uint16_t sqrt_int16(uint16_t u); -#endif +/** + * sofm_sqrt_int16() - Calculate 16-bit fractional square root function. + * @param u Input value in Q4.12 format, from 0 to 16.0. + * @return Calculated square root of n in Q4.12 format, from 0 to 4.0. + */ +uint16_t sofm_sqrt_int16(uint16_t u); + +/** + * sofm_sqrt_int32() - Calculate 32-bit fractional square root function. + * @param n Input value in Q2.30 format, from 0 to 2.0. + * @return Calculated square root of n in Q2.30 format. + * + * The input range of square root function is matched with Q1.31 + * complex numbers range where the magnitude squared can be to 2.0. + * The function returns zero for non-positive input values. + */ +int32_t sofm_sqrt_int32(int32_t n); + +#endif /* __SOF_MATH_SQRT_H__ */ diff --git a/src/include/sof/math/trig.h b/src/include/sof/math/trig.h index 0c0b2d23a388..67428cac494a 100644 --- a/src/include/sof/math/trig.h +++ b/src/include/sof/math/trig.h @@ -13,14 +13,17 @@ #include <stdint.h> -#define PI_DIV2_Q4_28 421657428 -#define PI_DIV2_Q3_29 843314856 -#define PI_Q4_28 843314857 -#define PI_MUL2_Q4_28 1686629713 -#define CORDIC_31B_TABLE_SIZE 31 -#define CORDIC_15B_TABLE_SIZE 15 -#define CORDIC_30B_ITABLE_SIZE 30 -#define CORDIC_16B_ITABLE_SIZE 16 +#define PI_Q4_28 843314857 /* int32(pi * 2^28) */ +#define PI_MUL2_Q4_28 1686629713 /* int32(2 * pi * 2^28) */ +#define PI_DIV2_Q3_29 843314857 /* int32(pi / 2 * 2^29) */ +#define PI_Q3_29 1686629713 /* int32(pi * 2^29) */ + +#define CORDIC_31B_TABLE_SIZE 31 +#define CORDIC_15B_TABLE_SIZE 15 +#define CORDIC_30B_ITABLE_SIZE 30 +#define CORDIC_16B_ITABLE_SIZE 16 +#define CORDIC_31B_ITERATIONS (CORDIC_31B_TABLE_SIZE - 1) +#define CORDIC_16B_ITERATIONS (CORDIC_16B_ITABLE_SIZE - 1) typedef enum { EN_32B_CORDIC_SINE, @@ -36,13 +39,49 @@ struct cordic_cmpx { int32_t im; }; +/** + * cordic_approx() - CORDIC-based approximation of sine and cosine + * @param th_rad_fxp Input angle in radian Q4.28 format. + * @param a_idx Used LUT size. + * @param sign Output pointer to sine/cosine sign. + * @param b_yn Output pointer to sine value in Q2.30 format. + * @param xn Output pointer to cosine value in Q2.30 format. + * @param th_cdc_fxp Output pointer to the residual angle in Q2.30 format. + */ void cordic_approx(int32_t th_rad_fxp, int32_t a_idx, int32_t *sign, int32_t *b_yn, int32_t *xn, int32_t *th_cdc_fxp); -int32_t is_scalar_cordic_acos(int32_t realvalue, int16_t numiters); -int32_t is_scalar_cordic_asin(int32_t realvalue, int16_t numiters); + +/** + * is_scalar_cordic_acos() - CORDIC-based approximation for inverse cosine + * @param realvalue Input cosine value in Q2.30 format. + * @param numiters_minus_one Number of iterations minus one. + * @return Inverse cosine angle in Q3.29 format. + */ +int32_t is_scalar_cordic_acos(int32_t realvalue, int numiters_minus_one); + +/** + * is_scalar_cordic_asin() - CORDIC-based approximation for inverse sine + * @param realvalue Input sine value in Q2.30 format. + * @param numiters_minus_one Number of iterations minus one. + * @return Inverse sine angle in Q2.30 format. + */ +int32_t is_scalar_cordic_asin(int32_t realvalue, int numiters_minus_one); + +/** + * cmpx_cexp() - CORDIC-based approximation of complex exponential e^(j*THETA) + * @param sign Sine sign + * @param b_yn Sine value in Q2.30 format + * @param xn Cosine value in Q2.30 format + * @param type CORDIC type + * @param cexp Output pointer to complex result in struct cordic_cmpx + */ void cmpx_cexp(int32_t sign, int32_t b_yn, int32_t xn, cordic_cfg type, struct cordic_cmpx *cexp); -/* Input is Q4.28, output is Q1.31 */ + /** + * sin_fixed_32b() - Sine function using CORDIC algorithm + * @param th_rad_fxp Input angle in radian Q4.28 format. + * @return Sine value in Q1.31 format. + * * Compute fixed point cordicsine with table lookup and interpolation * The cordic sine algorithm converges, when the angle is in the range * [-pi/2, pi/2).If an angle is outside of this range, then a multiple of @@ -71,6 +110,10 @@ static inline int32_t sin_fixed_32b(int32_t th_rad_fxp) } /** + * cos_fixed_32b() - Cosine function using CORDIC algorithm + * @param th_rad_fxp Input angle in radian Q4.28 format. + * @return Cosine value in Q1.31 format. + * * Compute fixed point cordicsine with table lookup and interpolation * The cordic cosine algorithm converges, when the angle is in the range * [-pi/2, pi/2).If an angle is outside of this range, then a multiple of @@ -98,8 +141,11 @@ static inline int32_t cos_fixed_32b(int32_t th_rad_fxp) return sat_int32(Q_SHIFT_LEFT((int64_t)th_cdc_fxp, 30, 31)); } -/* Input is Q4.28, output is Q1.15 */ /** + * sin_fixed_16b() - Sine function using CORDIC algorithm + * @param th_rad_fxp Input angle in radian Q4.28 format. + * @return Sine value in Q1.15 format + * * Compute fixed point cordic sine with table lookup and interpolation * The cordic sine algorithm converges, when the angle is in the range * [-pi/2, pi/2).If an angle is outside of this range, then a multiple of @@ -129,6 +175,10 @@ static inline int16_t sin_fixed_16b(int32_t th_rad_fxp) } /** + * cos_fixed_16b() - Cosine function using CORDIC algorithm + * @param th_rad_fxp Input angle in radian Q4.28 format. + * @return Cosine value in Q1.15 format. + * * Compute fixed point cordic cosine with table lookup and interpolation * The cordic cos algorithm converges, when the angle is in the range * [-pi/2, pi/2).If an angle is outside of this range, then a multiple of @@ -158,7 +208,10 @@ static inline int16_t cos_fixed_16b(int32_t th_rad_fxp) } /** - * CORDIC-based approximation of complex exponential e^(j*THETA). + * cmpx_exp_32b() - CORDIC-based approximation of complex exponential e^(j*THETA). + * @param th_rad_fxp Input angle in radian Q4.28 format. + * @param cexp Output pointer to complex result in struct cordic_cmpx in Q2.30 format. + * * computes COS(THETA) + j*SIN(THETA) using CORDIC algorithm * approximation and returns the complex result. * THETA values must be in the range [-2*pi, 2*pi). The cordic @@ -190,7 +243,10 @@ static inline void cmpx_exp_32b(int32_t th_rad_fxp, struct cordic_cmpx *cexp) } /** - * CORDIC-based approximation of complex exponential e^(j*THETA). + * cmpx_exp_16b() - CORDIC-based approximation of complex exponential e^(j*THETA). + * @param th_rad_fxp Input angle in radian Q4.28 format. + * @param cexp Output pointer to complex result in struct cordic_cmpx in Q1.15 format. + * * computes COS(THETA) + j*SIN(THETA) using CORDIC algorithm * approximation and returns the complex result. * THETA values must be in the range [-2*pi, 2*pi). The cordic @@ -223,7 +279,10 @@ static inline void cmpx_exp_16b(int32_t th_rad_fxp, struct cordic_cmpx *cexp) } /** - * CORDIC-based approximation of inverse sine + * asin_fixed_32b() - CORDIC-based approximation of inverse sine + * @param cdc_asin_th Input value in Q2.30 format. + * @return Inverse sine angle in Q2.30 format. + * * inverse sine of cdc_asin_theta based on a CORDIC approximation. * asin(cdc_asin_th) inverse sine angle values in radian produces using the DCORDIC * (Double CORDIC) algorithm. @@ -238,17 +297,18 @@ static inline int32_t asin_fixed_32b(int32_t cdc_asin_th) int32_t th_asin_fxp; if (cdc_asin_th >= 0) - th_asin_fxp = is_scalar_cordic_asin(cdc_asin_th, - CORDIC_31B_TABLE_SIZE); + th_asin_fxp = is_scalar_cordic_asin(cdc_asin_th, CORDIC_31B_ITERATIONS); else - th_asin_fxp = -is_scalar_cordic_asin(-cdc_asin_th, - CORDIC_31B_TABLE_SIZE); + th_asin_fxp = -is_scalar_cordic_asin(-cdc_asin_th, CORDIC_31B_ITERATIONS); return th_asin_fxp; /* Q2.30 */ } /** - * CORDIC-based approximation of inverse cosine + * acos_fixed_32b() - CORDIC-based approximation of inverse cosine + * @param cdc_acos_th Input value in Q2.30 format. + * @return Inverse cosine angle in Q3.29 format. + * * inverse cosine of cdc_acos_theta based on a CORDIC approximation * acos(cdc_acos_th) inverse cosine angle values in radian produces using the DCORDIC * (Double CORDIC) algorithm. @@ -262,18 +322,18 @@ static inline int32_t acos_fixed_32b(int32_t cdc_acos_th) int32_t th_acos_fxp; if (cdc_acos_th >= 0) - th_acos_fxp = is_scalar_cordic_acos(cdc_acos_th, - CORDIC_31B_TABLE_SIZE); + th_acos_fxp = is_scalar_cordic_acos(cdc_acos_th, CORDIC_31B_ITERATIONS); else - th_acos_fxp = - PI_MUL2_Q4_28 - is_scalar_cordic_acos(-cdc_acos_th, - CORDIC_31B_TABLE_SIZE); + th_acos_fxp = PI_Q3_29 - is_scalar_cordic_acos(-cdc_acos_th, CORDIC_31B_ITERATIONS); return th_acos_fxp; /* Q3.29 */ } /** - * CORDIC-based approximation of inverse sine + * asin_fixed_16b() - CORDIC-based approximation of inverse sine + * @param cdc_asin_th Input value in Q2.30 format. + * @return Inverse sine angle in Q2.14 format. + * * inverse sine of cdc_asin_theta based on a CORDIC approximation. * asin(cdc_asin_th) inverse sine angle values in radian produces using the DCORDIC * (Double CORDIC) algorithm. @@ -289,17 +349,18 @@ static inline int16_t asin_fixed_16b(int32_t cdc_asin_th) int32_t th_asin_fxp; if (cdc_asin_th >= 0) - th_asin_fxp = is_scalar_cordic_asin(cdc_asin_th, - CORDIC_16B_ITABLE_SIZE); + th_asin_fxp = is_scalar_cordic_asin(cdc_asin_th, CORDIC_16B_ITERATIONS); else - th_asin_fxp = -is_scalar_cordic_asin(-cdc_asin_th, - CORDIC_16B_ITABLE_SIZE); + th_asin_fxp = -is_scalar_cordic_asin(-cdc_asin_th, CORDIC_16B_ITERATIONS); /*convert Q2.30 to Q2.14 format*/ return sat_int16(Q_SHIFT_RND(th_asin_fxp, 30, 14)); } /** - * CORDIC-based approximation of inverse cosine + * acos_fixed_16b() - CORDIC-based approximation of inverse cosine + * @param cdc_acos_th Input value in Q2.30 format. + * @return Inverse cosine angle in Q3.13 format. + * * inverse cosine of cdc_acos_theta based on a CORDIC approximation * acos(cdc_acos_th) inverse cosine angle values in radian produces using the DCORDIC * (Double CORDIC) algorithm. @@ -314,15 +375,26 @@ static inline int16_t acos_fixed_16b(int32_t cdc_acos_th) int32_t th_acos_fxp; if (cdc_acos_th >= 0) - th_acos_fxp = is_scalar_cordic_acos(cdc_acos_th, - CORDIC_16B_ITABLE_SIZE); + th_acos_fxp = is_scalar_cordic_acos(cdc_acos_th, CORDIC_16B_ITERATIONS); else - th_acos_fxp = - PI_MUL2_Q4_28 - is_scalar_cordic_acos(-cdc_acos_th, - CORDIC_16B_ITABLE_SIZE); + th_acos_fxp = PI_Q3_29 - is_scalar_cordic_acos(-cdc_acos_th, CORDIC_16B_ITERATIONS); /*convert Q3.29 to Q3.13 format*/ return sat_int16(Q_SHIFT_RND(th_acos_fxp, 29, 13)); } +/** + * sofm_atan2_32b() - Four-quadrant arctangent using degree-9 Remez minimax polynomial + * @param y Imaginary component (sine) in Q1.31 format. + * @param x Real component (cosine) in Q1.31 format. + * @return Angle in Q3.29 radians, range [-pi, +pi]. + * + * Uses the Horner-form polynomial: + * atan(z) = z * (C0 + z^2 * (C1 + z^2 * (C2 + z^2 * (C3 + z^2 * C4)))) + * + * with Remez minimax coefficients on [0, 1]. + * Maximum error ~0.001 degrees (1.94e-5 radians). + */ +int32_t sofm_atan2_32b(int32_t y, int32_t x); + #endif /* __SOF_MATH_TRIG_H__ */ diff --git a/src/include/sof/math/window.h b/src/include/sof/math/window.h index bd04317b96ee..d72d00b3bd87 100644 --- a/src/include/sof/math/window.h +++ b/src/include/sof/math/window.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: BSD-3-Clause * - * Copyright(c) 2022 Intel Corporation. All rights reserved. + * Copyright(c) 2022-2025 Intel Corporation. * * Author: Seppo Ingalsuo <seppo.ingalsuo@linux.intel.com> */ @@ -13,40 +13,85 @@ #include <sof/audio/format.h> #include <stdint.h> -#define WIN_BLACKMAN_A0 Q_CONVERT_FLOAT(7938.0 / 18608.0, 15) /* For "exact" blackman */ +#define WIN_BLACKMAN_A0_Q15 Q_CONVERT_FLOAT(7938.0 / 18608.0, 15) /* For "exact" blackman 16bit */ +#define WIN_BLACKMAN_A0_Q31 Q_CONVERT_FLOAT(7938.0 / 18608.0, 31) /* For "exact" blackman 32bit */ /** - * \brief Return rectangular window, simply values of one - * \param[in,out] win Output vector with coefficients - * \param[in] length Length of coefficients vector + * Returns a rectangular window with 16 bits, simply a values of ones vector + * @param win Pointer to output vector with Q1.15 coefficients + * @param length Length of coefficients vector */ void win_rectangular_16b(int16_t win[], int length); /** - * \brief Calculate Blackman window function, reference + * Returns a rectangular window with 32 bits, simply a values of ones vector + * @param win Pointer to output vector with Q1.31 coefficients + * @param length Length of coefficients vector + */ +void win_rectangular_32b(int32_t win[], int length); + +/** + * Calculates a Blackman window function with 16 bits, reference * https://en.wikipedia.org/wiki/Window_function#Blackman_window * - * \param[in,out] win Output vector with coefficients - * \param[in] length Length of coefficients vector - * \param[in] a0 Parameter for window shape, use e.g. 0.42 as Q1.15 + * @param win Pointer to output vector with Q1.15 coefficients + * @param length Length of coefficients vector + * @param a0 Parameter for window shape, use e.g. 0.42 as Q1.15 */ void win_blackman_16b(int16_t win[], int length, int16_t a0); /** - * \brief Calculate Hamming window function, reference + * Calculates a Blackman window function with 32 bits, reference + * https://en.wikipedia.org/wiki/Window_function#Blackman_window + * + * @param win Pointer to output vector with Q1.31 coefficients + * @param length Length of coefficients vector + * @param a0 Parameter for window shape, use e.g. 0.42 as Q1.31 + */ +void win_blackman_32b(int32_t win[], int length, int32_t a0); + +/** + * Calculates a Hann window function with 16 bits, reference * https://en.wikipedia.org/wiki/Window_function#Hann_and_Hamming_windows * - * \param[in,out] win Output vector with coefficients - * \param[in] length Length of coefficients vector + * @param win Pointer to output vector with Q1.15 coefficients + * @param length Length of coefficients vector + */ +void win_hann_16b(int16_t win[], int length); + +/** + * Calculates a Hann window function with 32 bits, reference + * https://en.wikipedia.org/wiki/Window_function#Hann_and_Hamming_windows + * + * @param win Pointer to output vector with Q1.31 coefficients + * @param length Length of coefficients vector + */ +void win_hann_32b(int32_t win[], int length); + +/** + * Calculates a Hamming window function with 16 bits, reference + * https://en.wikipedia.org/wiki/Window_function#Hann_and_Hamming_windows + * + * @param win Pointer to output vector with Q1.15 coefficients + * @param length Length of coefficients vector */ void win_hamming_16b(int16_t win[], int length); /** - * \brief Calculate Povey window function. It's a window function - * from Pytorch. + * Calculates a Hamming window function with 32 bits, reference + * https://en.wikipedia.org/wiki/Window_function#Hann_and_Hamming_windows + * + * @param win Pointer to output vector with Q1.31 coefficients + * @param length Length of coefficients vector + */ +void win_hamming_32b(int32_t win[], int length); + +/** + * Calculates a Povey window function with 16 bits. It's a window function + * used in Pytorch and Kaldi. * - * \param[in,out] win Output vector with coefficients - * \param[in] length Length of coefficients vector + * @param win Output vector with coefficients + * @param length Length of coefficients vector */ void win_povey_16b(int16_t win[], int length); diff --git a/src/include/sof/objpool.h b/src/include/sof/objpool.h new file mode 100644 index 000000000000..0821fec8786b --- /dev/null +++ b/src/include/sof/objpool.h @@ -0,0 +1,82 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * + * Copyright(c) 2025 Intel Corporation. + */ + +#ifndef __ZEPHYR_OBJPOOL_H__ +#define __ZEPHYR_OBJPOOL_H__ + +#include <sof/list.h> + +#include <stdbool.h> +#include <stddef.h> +#include <stdint.h> + +struct vregion; +struct k_heap; +struct objpool_head { + struct list_item list; + struct vregion *vreg; + struct k_heap *heap; + uint32_t flags; +}; + +/** + * Allocate memory tracked as part of an object pool. + * + * @param head Pointer to the object pool head. + * @param size Size in bytes of memory blocks to allocate. + * @param flags Memory allocation flags. + * + * @return a pointer to the allocated memory on success, NULL on failure. + * + * Allocate a memory block of @a size bytes. @a size is used upon the first + * invocation to allocate memory on the heap, all consequent allocations with + * the same @a head must use the same @a size value. First allocation with an + * empty @a head allocates 2 blocks. After both blocks are taken and a third one + * is requested, the next call allocates 4 blocks, then 8, 16 and 32. After that + * 32 blocks are allocated every time. Note, that by design allocated blocks are + * never freed. See more below. + * TODO: @a flags are currently only used when allocating new object sets. + * Should add a check that they're consistent with already allocated objects. + */ +void *objpool_alloc(struct objpool_head *head, size_t size, uint32_t flags); + +/** + * Return a block to the object pool + * + * @param head Pointer to the object pool head. + * @param data Pointer to the object to return (can be NULL) + * + * @return 0 on success or a negative error code. + * + * Return a block to the object pool. Memory is never freed by design, unused + * blocks are kept in the object pool for future re-use. + */ +int objpool_free(struct objpool_head *head, void *data); + +/** + * Free all of the object pool memory + * + * @param head Pointer to the object pool head. + */ +void objpool_prune(struct objpool_head *head); + +/* returns true to stop */ +typedef bool (*objpool_iterate_cb)(void *data, void *arg); + +/** + * Iterate over object pool entries until stopped + * + * @param head Pointer to the object pool head. + * @param cb Callback function + * @param arg Callback function argument + * + * @return 0 on success or a negative error code. + * + * Call the callback function for each entry in the pool, until it returns true. + * If the callback never returns true, return an error. + */ +int objpool_iterate(struct objpool_head *head, objpool_iterate_cb cb, void *arg); + +#endif diff --git a/src/include/sof/schedule/dp_schedule.h b/src/include/sof/schedule/dp_schedule.h index 360d7f47c1de..2267d676fb8a 100644 --- a/src/include/sof/schedule/dp_schedule.h +++ b/src/include/sof/schedule/dp_schedule.h @@ -13,6 +13,8 @@ #include <user/trace.h> #include <stdint.h> #include <ipc4/base_fw.h> +#include <ipc4/module.h> +#include <ipc4/pipeline.h> struct processing_module; @@ -86,4 +88,47 @@ int scheduler_dp_task_init(struct task **task, void scheduler_get_task_info_dp(struct scheduler_props *scheduler_props, uint32_t *data_off_size); +enum { + DP_TASK_EVENT_PROCESS = BIT(0), /* Need to process data */ + DP_TASK_EVENT_CANCEL = BIT(1), /* Thread cancellation */ + DP_TASK_EVENT_IPC = BIT(2), /* IPC message */ + DP_TASK_EVENT_IPC_DONE = BIT(3), /* IPC processing has completed. */ +}; + +struct bind_info; +struct sof_source; +struct sof_sink; +/* + * Keeps the scheduler_dp_thread_ipc() flow simple - just one call that does all + * the IPC-message specific parameter packing internally. This is slightly + * suboptimal because IPC parameters first have to be collected in this + * structure and then packed in DP-accessible memory inside + * scheduler_dp_thread_ipc(). This could be split into two levels, by adding + * IPC-specific functions like ipc_flatten_pipeline_state() and similar, but + * that would add multiple functions to the API. + */ +union scheduler_dp_thread_ipc_param { + struct bind_info *bind_data; + struct { + unsigned int trigger_cmd; + enum ipc4_pipeline_state state; + int n_sources; + struct sof_source **sources; + int n_sinks; + struct sof_sink **sinks; + } pipeline_state; +}; + +#if CONFIG_ZEPHYR_DP_SCHEDULER +int scheduler_dp_thread_ipc(struct processing_module *pmod, unsigned int cmd, + const union scheduler_dp_thread_ipc_param *param); +#else +static inline int scheduler_dp_thread_ipc(struct processing_module *pmod, + unsigned int cmd, + const union scheduler_dp_thread_ipc_param *param) +{ + return 0; +} +#endif + #endif /* __SOF_SCHEDULE_DP_SCHEDULE_H__ */ diff --git a/src/include/sof/schedule/ll_schedule_domain.h b/src/include/sof/schedule/ll_schedule_domain.h index 5fc7691e3b13..dc28e43c7461 100644 --- a/src/include/sof/schedule/ll_schedule_domain.h +++ b/src/include/sof/schedule/ll_schedule_domain.h @@ -66,7 +66,11 @@ struct ll_schedule_domain_ops { struct ll_schedule_domain { uint64_t next_tick; /**< ticks just set for next run */ uint64_t new_target_tick; /**< for the next set, used during the reschedule stage */ - struct k_spinlock lock; /**< standard lock */ +#ifdef CONFIG_SOF_USERSPACE_LL + struct k_mutex *lock; /**< standard lock */ +#else + struct k_spinlock lock; /**< standard lock */ +#endif atomic_t total_num_tasks; /**< total number of registered tasks */ atomic_t enabled_cores; /**< number of enabled cores */ uint32_t ticks_per_ms; /**< number of clock ticks per ms */ @@ -93,13 +97,26 @@ static inline struct ll_schedule_domain *dma_domain_get(void) return sof_get()->platform_dma_domain; } +#ifdef CONFIG_SOF_USERSPACE_LL +struct task *zephyr_ll_task_alloc(void); +struct k_heap *zephyr_ll_user_heap(void); +void zephyr_ll_user_resources_init(void); +#endif /* CONFIG_SOF_USERSPACE_LL */ + static inline struct ll_schedule_domain *domain_init (int type, int clk, bool synchronous, const struct ll_schedule_domain_ops *ops) { struct ll_schedule_domain *domain; +#ifdef CONFIG_SOF_USERSPACE_LL + domain = sof_heap_alloc(zephyr_ll_user_heap(), SOF_MEM_FLAG_USER | SOF_MEM_FLAG_COHERENT, + sizeof(*domain), sizeof(void *)); + if (domain) + memset(domain, 0, sizeof(*domain)); +#else domain = rzalloc(SOF_MEM_FLAG_KERNEL | SOF_MEM_FLAG_COHERENT, sizeof(*domain)); +#endif if (!domain) return NULL; domain->type = type; @@ -116,7 +133,17 @@ static inline struct ll_schedule_domain *domain_init domain->next_tick = UINT64_MAX; domain->new_target_tick = UINT64_MAX; +#ifdef CONFIG_SOF_USERSPACE_LL + /* Allocate mutex dynamically for userspace access */ + domain->lock = k_object_alloc(K_OBJ_MUTEX); + if (!domain->lock) { + sof_heap_free(zephyr_ll_user_heap(), domain); + return NULL; + } + k_mutex_init(domain->lock); +#else k_spinlock_init(&domain->lock); +#endif atomic_init(&domain->total_num_tasks, 0); atomic_init(&domain->enabled_cores, 0); @@ -246,7 +273,11 @@ struct ll_schedule_domain *zephyr_dma_domain_init(struct dma *dma_array, struct ll_schedule_domain *zephyr_ll_domain(void); struct ll_schedule_domain *zephyr_domain_init(int clk); #define timer_domain_init(timer, clk) zephyr_domain_init(clk) -#endif +#ifdef CONFIG_SOF_USERSPACE_LL +struct k_thread *zephyr_domain_thread_tid(struct ll_schedule_domain *domain); +struct k_mem_domain *zephyr_ll_mem_domain(void); +#endif /* CONFIG_SOF_USERSPACE_LL */ +#endif /* __ZEPHYR__ */ struct ll_schedule_domain *dma_multi_chan_domain_init(struct dma *dma_array, uint32_t num_dma, int clk, diff --git a/src/include/sof/tlv.h b/src/include/sof/tlv.h index 24155707dfd4..442c03a5fc69 100644 --- a/src/include/sof/tlv.h +++ b/src/include/sof/tlv.h @@ -91,7 +91,7 @@ static inline void tlv_value_get(const void *data, const struct sof_tlv *tlv = (const struct sof_tlv *)data; const uint32_t end_addr = (uint32_t)data + size; - while ((uint32_t)tlv < end_addr) { + while (tlv && (uint32_t)tlv < end_addr) { if (tlv->type == type) { *value = (void *)tlv->value; *length = tlv->length; diff --git a/src/include/sof/trace/trace.h b/src/include/sof/trace/trace.h index ce8a12ed83c2..fc3aa324f847 100644 --- a/src/include/sof/trace/trace.h +++ b/src/include/sof/trace/trace.h @@ -31,6 +31,9 @@ #endif #include <sof/common.h> +#if CONFIG_ZEPHYR_LOG || CONFIG_LIBRARY || CONFIG_ARCH_POSIX_LIBFUZZER +#include <sof/lib/uuid.h> +#endif #include <sof/trace/preproc.h> #include <sof/trace/trace-boot.h> @@ -128,6 +131,7 @@ static inline void mtrace_printf(int log_level, const char *format_str, ...) /** * Trace context. */ +struct sof_uuid_entry; struct tr_ctx { const struct sof_uuid_entry *uuid_p; /**< UUID pointer, use SOF_UUID() to init */ uint32_t level; /**< Default log level */ diff --git a/src/include/user/debug_stream.h b/src/include/user/debug_stream.h index 60f731622478..b6d8c08cf43a 100644 --- a/src/include/user/debug_stream.h +++ b/src/include/user/debug_stream.h @@ -48,5 +48,6 @@ struct debug_stream_record { /* Debug Stream record identifiers */ #define DEBUG_STREAM_RECORD_ID_UNINITIALIZED 0 /* invalid record marker */ #define DEBUG_STREAM_RECORD_ID_THREAD_INFO 1 /* Thread info record */ +#define DEBUG_STREAM_RECORD_ID_TEXT_MSG 2 /* Text message */ #endif /* __SOC_DEBUG_STREAM_H__ */ diff --git a/src/include/user/debug_stream_text_msg.h b/src/include/user/debug_stream_text_msg.h new file mode 100644 index 000000000000..debfaad7042e --- /dev/null +++ b/src/include/user/debug_stream_text_msg.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * + * Copyright(c) 2024 Intel Corporation. + */ + +#ifndef __SOC_DEBUG_STREAM_TEXT_MSG_H__ +#define __SOC_DEBUG_STREAM_TEXT_MSG_H__ + +#include <user/debug_stream_slot.h> +#include <stdarg.h> + +/* + * Debug Stream text message. + */ +struct debug_stream_text_msg { + struct debug_stream_record hdr; + char msg[]; +} __packed; + +/* + * To send debug messages over debug stream. Enable + * CONFIG_SOF_DEBUG_STREAM_TEXT_MSG to enable this function. + */ +void ds_msg(const char *format, ...); +void ds_vamsg(const char *format, va_list ap); + +#endif /* __SOC_DEBUG_STREAM_TEXT_MSG_H__ */ diff --git a/src/include/user/mfcc.h b/src/include/user/mfcc.h index 7a5b7fcca98e..8a0defcd9883 100644 --- a/src/include/user/mfcc.h +++ b/src/include/user/mfcc.h @@ -50,7 +50,11 @@ enum sof_mfcc_dct_type { */ struct sof_mfcc_config { uint32_t size; /**< Size of this struct in bytes */ - uint32_t reserved[8]; + int16_t mel_offset; /**< Q8.7 default 0, use 4.0 for Whisper */ + int16_t mel_scale; /**< Q4.12 default 1.0, use 0.25 for Whisper */ + int16_t mmax_init; /**< Q8.7 default 0, with dynamic_mmax false, can sim. Whisper mmax */ + int16_t mmax_coef; /**< Q1.15 decay coefficient for dynamic mmax, a small value for slow */ + uint32_t reserved[6]; int32_t sample_frequency; /**< Hz. e.g. 16000 */ int32_t pmin; /**< Q1.31 linear power, limit minimum Mel energy, e.g. 1e-9 */ enum sof_mfcc_mel_log_type mel_log; /**< Use MEL_LOG_IS_LOG, LOG10 or DB*/ @@ -69,7 +73,7 @@ struct sof_mfcc_config { int16_t num_ceps; /**< Number of cepstral coefficients, e.g. 13 */ int16_t num_mel_bins; /**< Number of internal Mel bands, e.g. 23 */ int16_t preemphasis_coefficient; /**< Q1.15, e.g. 0.97, or 0 for disable */ - int16_t top_db; /**< Q8.7 dB, limit Mel energies to this value e.g. 200 */ + int16_t top_db; /**< Q8.7 dB, limit min. Mel energies to chunk max - top_dB, e.g. 80 */ int16_t vtln_high; /**< Reserved, no support */ int16_t vtln_low; /**< Reserved, no support */ int16_t vtln_warp; /**< Reserved, no support */ @@ -80,7 +84,7 @@ struct sof_mfcc_config { bool snip_edges; /**< Must be true (1) */ bool subtract_mean; /**< Must be false (0) */ bool use_energy; /**< Must be false (0) */ - bool reserved_bool1; + bool dynamic_mmax; /**< Track max Mel value for clamp with top_db value */ bool reserved_bool2; bool reserved_bool3; } __attribute__((packed)); diff --git a/src/init/README.md b/src/init/README.md new file mode 100644 index 000000000000..1df29a7b6223 --- /dev/null +++ b/src/init/README.md @@ -0,0 +1,156 @@ +# DSP Initialization (`src/init`) + +The `src/init` directory contains the generic digital signal processor (DSP) initialization code and firmware metadata structures for Sound Open Firmware (SOF). It acts as the bridge between the underlying RTOS (Zephyr) boot phase and the SOF-specific task scheduling and processing pipelines. + +## Architecture and Boot Flow + +The firmware initialization architecture relies on the Zephyr RTOS boot sequence. Zephyr handles the very early hardware setup and kernel initialization before invoking SOF-specific routines via Zephyr's `SYS_INIT` macros. + +### Primary Core Boot Sequence + +The primary entry point for SOF system initialization is `sof_init()`, registered to run at Zephyr's `POST_KERNEL` initialization level. This ensures basic OS primitives (like memory allocators and threads) are available before SOF starts. + +`sof_init()` delegates to `primary_core_init()`, which executes the following sequence: + +```mermaid +sequenceDiagram + participant Zephyr as Zephyr RTOS + participant sof_init as sof_init (src/init.c) + participant primary_core as primary_core_init + participant trace as trace_init + participant notify as init_system_notify + participant pm as pm_runtime_init + participant platform as platform_init + participant ams as ams_init + participant mbox as mailbox (IPC4) + participant task as task_main_start + + Zephyr->>sof_init: SYS_INIT (POST_KERNEL) + sof_init->>primary_core: Initialize Primary Core Context + + primary_core->>trace: Setup DMA Tracing & Logging + primary_core->>notify: Setup System Notifiers + primary_core->>pm: Initialize Runtime Power Management + + primary_core->>platform: Platform-Specific HW Config + note right of platform: e.g., interrupts, IPC windows (Intel, i.MX) + + primary_core->>ams: Init Asynchronous Messaging Service + + primary_core->>mbox: Set Firmware Registers ABI Version + + primary_core->>task: Start SOF Main Processing Task Loop +``` + +1. **Context Initialization**: Sets up the global `struct sof` context. +2. **Logging and Tracing**: Initializes Zephyr's logging timestamps and SOF's DMA trace infrastructure (`trace_init()`), printing the firmware version and ABI banner. +3. **System Subsystems**: + - Initializes the system notifier (`init_system_notify()`) for inter-component messaging. + - Sets up runtime power management (`pm_runtime_init()`). +4. **Platform-Specific Initialization**: Calls `platform_init()` to allow the specific hardware platform (e.g., Intel cAVS, i.MX) to configure its hardware IPs, interrupts, and IPC mailbox memory windows. +5. **Architectural Handoff**: For IPC4, it sets the Firmware Registers ABI version in the mailbox. It may also unpack boot manifests if configured. +6. **Task Scheduler**: Finally, it calls `task_main_start()` to kick off the main SOF processing task loop. + +### Secondary Core Boot Sequence + +For multi-core DSP platforms, secondary cores execute `secondary_core_init()`: + +```mermaid +sequenceDiagram + participant ZephyrN as Zephyr RTOS (Core N) + participant sci as secondary_core_init + participant check as check_restore + participant notify as init_system_notify + participant ll as scheduler_init_ll + participant dp as scheduler_dp_init + participant idc as idc_init + + ZephyrN->>sci: Core Wake/Boot + sci->>check: Cold boot or power restore? + + alt is Power Restore (e.g. D0ix wake) + sci->>sci: secondary_core_restore()<br>(Skip full init) + else is Cold Boot + sci->>notify: Setup Local Core Notifiers + + sci->>ll: Init Low-Latency (LL) Timer Domain + sci->>ll: Init LL DMA Domain (if applicable) + sci->>dp: Init Data Processing (DP) Scheduler + + sci->>idc: Init Inter-Domain Communication (IDC) + end +``` + +1. **Power State Checking**: It checks if the core is cold booting or resuming from a low-power retention/restore state (e.g., D0ix) via `check_restore()`. In the restore case, it restores state without re-allocating core structures; in the cold-boot case, it follows the full initialization path described below. +2. **Local Subsystem Setup**: Sets up system notifiers for the local core. +3. **Scheduler Setup**: Initializes the Low-Latency (LL) and Data Processing (DP) schedulers on the secondary core. +4. **Inter-Core Communication**: Initializes the Inter-Domain Communication (IDC) mechanism (`idc_init()`), allowing cross-core messaging. + +### Firmware Extended Manifest (`ext_manifest.c`) + +This directory also provides the extended manifest implementation. The manifest consists of structured metadata embedded into the `.fw_metadata` section of the firmware binary. + +```mermaid +classDiagram + direction LR + + class fw_metadata { + <<Section>> + +ext_man_fw_ver + +ext_man_cc_ver + +ext_man_probe + +ext_man_dbg_info + +ext_man_config + } + + class ext_man_elem_header { + +uint32_t type + +uint32_t elem_size + } + + class ext_man_fw_version { + +ext_man_elem_header hdr + +sof_ipc_fw_version version + +uint32_t flags + } + + class ext_man_cc_version { + +ext_man_elem_header hdr + +sof_ipc_cc_version cc_version + } + + class ext_man_probe_support { + +ext_man_elem_header hdr + +sof_ipc_probe_support probe + } + + class ext_man_dbg_abi { + +ext_man_elem_header hdr + +sof_ipc_user_abi_version dbg_abi + } + + class ext_man_config_data { + +ext_man_elem_header hdr + +config_elem elems[] + } + + fw_metadata *-- ext_man_fw_version + fw_metadata *-- ext_man_cc_version + fw_metadata *-- ext_man_probe_support + fw_metadata *-- ext_man_dbg_abi + fw_metadata *-- ext_man_config_data + + ext_man_fw_version *-- ext_man_elem_header + ext_man_cc_version *-- ext_man_elem_header + ext_man_probe_support *-- ext_man_elem_header + ext_man_dbg_abi *-- ext_man_elem_header + ext_man_config_data *-- ext_man_elem_header +``` + +When the host OS (e.g., Linux SOF driver) parses the firmware binary before loading it, it reads these manifest structures to discover firmware capabilities dynamically. The manifest includes: + +- **Firmware Version**: Major, minor, micro, tag, and build hashes (`ext_man_fw_ver`). +- **Compiler Version**: Details of the toolchain used to compile the firmware (`ext_man_cc_ver`). +- **Probe Info**: Extraction probe configurations and limits (`ext_man_probe`). +- **Debug ABI**: Supported debugger ABI versions (`ext_man_dbg_info`). +- **Configuration Dictionary**: Compile-time enabled features and sizing parameters (e.g., `SOF_IPC_MSG_MAX_SIZE`). diff --git a/src/init/init.c b/src/init/init.c index ddb4c604b4a6..e8d374c810ad 100644 --- a/src/init/init.c +++ b/src/init/init.c @@ -154,6 +154,8 @@ __cold int secondary_core_init(struct sof *sof) trace_point(TRACE_BOOT_PLATFORM); + LOG_INF("init done"); + return err; } @@ -218,6 +220,10 @@ __cold static int primary_core_init(int argc, char *argv[], struct sof *sof) io_perf_monitor_init(); #endif +#if CONFIG_SOF_USERSPACE_LL + zephyr_ll_user_resources_init(); +#endif + /* init the platform */ if (platform_init(sof) < 0) sof_panic(SOF_IPC_PANIC_PLATFORM); @@ -232,8 +238,6 @@ __cold static int primary_core_init(int argc, char *argv[], struct sof *sof) size_t ipc4_abi_ver_offset = offsetof(struct ipc4_fw_registers, abi_ver); mailbox_sw_reg_write(ipc4_abi_ver_offset, IPC4_FW_REGS_ABI_VER); - - k_spinlock_init(&sof->fw_reg_lock); #endif trace_point(TRACE_BOOT_PLATFORM); diff --git a/src/ipc/Kconfig b/src/ipc/Kconfig index 84e186b6bb08..3cde2ddeabfe 100644 --- a/src/ipc/Kconfig +++ b/src/ipc/Kconfig @@ -14,6 +14,7 @@ config IPC_MAJOR_3 config IPC_MAJOR_4 bool "IPC Major Version 4" + depends on ZEPHYR_NATIVE_DRIVERS || LIBRARY help This is an IPC version used by certain middleware on some IOT Intel devices. Not for general use. diff --git a/src/ipc/README.md b/src/ipc/README.md new file mode 100644 index 000000000000..7389e6ae4a54 --- /dev/null +++ b/src/ipc/README.md @@ -0,0 +1,98 @@ +# Inter-Processor Communication (IPC) Core Architecture + +This directory contains the common foundation for all Inter-Processor Communication (IPC) within the Sound Open Firmware (SOF) project. It bridges the gap between hardware mailbox interrupts and the version-specific (IPC3/IPC4) message handlers. + +## Overview + +The Core IPC layer is completely agnostic to the specific structure or content of the messages (whether they are IPC3 stream commands or IPC4 pipeline messages). Its primary responsibilities are: + +1. **Message State Management**: Tracking if a message is being processed, queued, or completed. +2. **Interrupt Bridging**: Routing incoming platform interrupts into the Zephyr or SOF thread-domain scheduler. +3. **Queueing**: Safe traversal and delayed processing capabilities via `k_work` items or SOF scheduler tasks. +4. **Platform Acknowledgment**: Signaling the hardware mailbox layers to confirm receipt or signal completion out entirely. + +## Architecture Diagram + +The basic routing of any IPC message moves from a hardware interrupt, through the platform driver, into the core IPC handlers, and ultimately up to version-specific handlers. + +```mermaid +graph TD + Platform[Platform / Mailbox HW] -->|IRQ| CoreIPC[Core IPC Framework] + + subgraph CoreIPC [src/ipc/ipc-common.c] + Queue[Msg Queue / Worker Task] + Dispatcher[IPC Message Dispatcher] + PM[Power Management Wait/Wake] + + Queue --> Dispatcher + Dispatcher --> PM + end + + Dispatcher -->|Version Specific Parsing| IPC3[IPC3 Handler] + Dispatcher -->|Version Specific Parsing| IPC4[IPC4 Handler] + + IPC3 -.-> CoreIPC + IPC4 -.-> CoreIPC + CoreIPC -.->|Ack| Platform +``` + +## Processing Flow + +When the host writes a command to the IPC mailbox and triggers an interrupt, the hardware-specific driver (`src/platform/...`) catches the IRQ and eventually calls down into the IPC framework. + +Different RTOS environments (Zephyr vs. bare metal SOF native) handle the thread handoff differently. In Zephyr, this leverages the `k_work` queues heavily for `ipc_work_handler`. + +### Receiving Messages (Host -> DSP) + +```mermaid +sequenceDiagram + participant Host + participant Platform as Platform Mailbox (IRQ) + participant CoreIPC as Core IPC Worker + participant Handler as Version-Specific Handler (IPC3/4) + + Host->>Platform: Writes Mailbox, Triggers Interrupt + activate Platform + Platform->>CoreIPC: ipc_schedule_process() + deactivate Platform + + Note over CoreIPC: Worker thread wakes up + + activate CoreIPC + CoreIPC->>Platform: ipc_platform_wait_ack() (Optional blocking) + CoreIPC->>Handler: version_specific_command_handler() + + Handler-->>CoreIPC: Command Processed (Status Header) + CoreIPC->>Platform: ipc_complete_cmd() + Platform-->>Host: Signals Completion Mailbox / IRQ + deactivate CoreIPC +``` + +### Sending Messages (DSP -> Host) + +Firmware-initiated messages (like notifications for position updates, traces, or XRUNs) rely on a queue if the hardware is busy. + +```mermaid +sequenceDiagram + participant DSP as DSP Component (e.g. Pipeline Tracker) + participant Queue as IPC Message Queue + participant Platform as Platform Mailbox + + DSP->>Queue: ipc_msg_send() / ipc_msg_send_direct() + activate Queue + Queue-->>Queue: Add to Tx list (if BUSY) + Queue->>Platform: Copy payload to mailbox and send + + alt If host is ready + Platform-->>Queue: Success + Queue->>Platform: Triggers IRQ to Host + else If host requires delayed ACKs + Queue-->>DSP: Queued pending prior completion + end + deactivate Queue +``` + +## Global IPC Objects and Helpers + +* `ipc_comp_dev`: Wrapper structure linking generic devices (`comp_dev`) specifically to their IPC pipeline and endpoint identifiers. +* `ipc_get_comp_dev` / `ipc_get_ppl_comp`: Lookup assistants utilizing the central graph tracking to find specific components either directly by component ID or by traversing the pipeline graph starting from a given `pipeline_id` and direction (upstream/downstream). diff --git a/src/ipc/ipc-common.c b/src/ipc/ipc-common.c index 2368ea9163fd..d0d248c9ec77 100644 --- a/src/ipc/ipc-common.c +++ b/src/ipc/ipc-common.c @@ -55,9 +55,11 @@ int ipc_process_on_core(uint32_t core, bool blocking) return -EACCES; } +#if CONFIG_IPC_MAJOR_3 /* The other core will write there its response */ dcache_invalidate_region((__sparse_force void __sparse_cache *)MAILBOX_HOSTBOX_BASE, ((struct sof_ipc_cmd_hdr *)ipc->comp_data)->size); +#endif /* * If the primary core is waiting for secondary cores to complete, it @@ -203,7 +205,7 @@ __cold void ipc_msg_send_direct(struct ipc_msg *msg, void *data) key = k_spin_lock(&ipc->lock); /* copy mailbox data to message if not already copied */ - if (msg->tx_size > 0 && msg->tx_size <= SOF_IPC_MSG_MAX_SIZE && + if (data && msg->tx_size > 0 && msg->tx_size <= SOF_IPC_MSG_MAX_SIZE && msg->tx_data != data) { ret = memcpy_s(msg->tx_data, msg->tx_size, data, msg->tx_size); assert(!ret); @@ -223,7 +225,7 @@ void ipc_msg_send(struct ipc_msg *msg, void *data, bool high_priority) key = k_spin_lock(&ipc->lock); /* copy mailbox data to message if not already copied */ - if ((msg->tx_size > 0 && msg->tx_size <= SOF_IPC_MSG_MAX_SIZE) && + if (data && (msg->tx_size > 0 && msg->tx_size <= SOF_IPC_MSG_MAX_SIZE) && msg->tx_data != data) { ret = memcpy_s(msg->tx_data, msg->tx_size, data, msg->tx_size); assert(!ret); @@ -292,6 +294,11 @@ __cold int ipc_init(struct sof *sof) tr_dbg(&ipc_tr, "entry"); +#if CONFIG_SOF_BOOT_TEST_STANDALONE + LOG_INF("SOF_BOOT_TEST_STANDALONE, disabling IPC."); + return 0; +#endif + /* init ipc data */ sof->ipc = rzalloc(SOF_MEM_FLAG_USER | SOF_MEM_FLAG_COHERENT, sizeof(*sof->ipc)); if (!sof->ipc) { @@ -330,7 +337,9 @@ __cold int ipc_init(struct sof *sof) k_thread_suspend(thread); +#ifdef CONFIG_SCHED_CPU_MASK k_thread_cpu_pin(thread, PLATFORM_PRIMARY_CORE_ID); +#endif k_thread_name_set(thread, "ipc_send_wq"); k_thread_resume(thread); diff --git a/src/ipc/ipc-helper.c b/src/ipc/ipc-helper.c index 0b34915ea26f..ad7b3771a16b 100644 --- a/src/ipc/ipc-helper.c +++ b/src/ipc/ipc-helper.c @@ -50,7 +50,8 @@ __cold static bool valid_ipc_buffer_desc(const struct sof_ipc_buffer *desc) } /* create a new component in the pipeline */ -__cold struct comp_buffer *buffer_new(const struct sof_ipc_buffer *desc, bool is_shared) +__cold struct comp_buffer *buffer_new(struct mod_alloc_ctx *alloc, + const struct sof_ipc_buffer *desc, bool is_shared) { struct comp_buffer *buffer; uint32_t flags = desc->flags; @@ -78,7 +79,7 @@ __cold struct comp_buffer *buffer_new(const struct sof_ipc_buffer *desc, bool is desc->caps, flags); /* allocate buffer */ - buffer = buffer_alloc(desc->size, flags, PLATFORM_DCACHE_ALIGN, + buffer = buffer_alloc(alloc, desc->size, flags, PLATFORM_DCACHE_ALIGN, is_shared); if (buffer) { buffer->stream.runtime_stream_params.id = desc->comp.id; @@ -234,7 +235,7 @@ int ipc_pipeline_complete(struct ipc *ipc, uint32_t comp_id) /* check whether pipeline exists */ ipc_pipe = ipc_get_pipeline_by_id(ipc, comp_id); if (!ipc_pipe) { - tr_err(&ipc_tr, "ipc: ipc_pipeline_complete looking for pipe component id 0x%x failed", + tr_err(&ipc_tr, "ipc: looking for pipe component id 0x%x failed", comp_id); return -EINVAL; } @@ -248,14 +249,14 @@ int ipc_pipeline_complete(struct ipc *ipc, uint32_t comp_id) /* get pipeline source component */ ipc_ppl_source = ipc_get_ppl_src_comp(ipc, p->pipeline_id); if (!ipc_ppl_source) { - tr_err(&ipc_tr, "ipc: ipc_pipeline_complete looking for pipeline source failed"); + tr_err(&ipc_tr, "ipc: looking for pipeline source failed"); return -EINVAL; } /* get pipeline sink component */ ipc_ppl_sink = ipc_get_ppl_sink_comp(ipc, p->pipeline_id); if (!ipc_ppl_sink) { - tr_err(&ipc_tr, "ipc: ipc_pipeline_complete looking for pipeline sink failed"); + tr_err(&ipc_tr, "ipc: looking for pipeline sink failed"); return -EINVAL; } diff --git a/src/ipc/ipc-zephyr.c b/src/ipc/ipc-zephyr.c index 460089414cf7..66ada4ddaa05 100644 --- a/src/ipc/ipc-zephyr.c +++ b/src/ipc/ipc-zephyr.c @@ -12,8 +12,12 @@ #include <autoconf.h> #include <zephyr/kernel.h> - +#include <zephyr/ipc/ipc_service.h> +#ifdef CONFIG_INTEL_ADSP_IPC +#include <zephyr/ipc/backends/intel_adsp_host_ipc.h> #include <intel_adsp_ipc.h> +#endif + #include <sof/ipc/common.h> #include <sof/ipc/schedule.h> @@ -58,25 +62,32 @@ LOG_MODULE_DECLARE(ipc, CONFIG_SOF_LOG_LEVEL); * When IPC message is read fills ipc_cmd_hdr. */ static uint32_t g_last_data, g_last_ext_data; +static struct ipc_ept sof_ipc_ept; +static struct ipc_ept_cfg sof_ipc_ept_cfg; + +BUILD_ASSERT(sizeof(struct ipc_cmd_hdr) == sizeof(uint32_t) * 2, + "ipc_cmd_hdr must be exactly two 32-bit words"); /** - * @brief cAVS IPC Message Handler Callback function. + * @brief SOF IPC receive callback for Zephyr IPC service. * - * See @ref (*intel_adsp_ipc_handler_t) for function signature description. - * @return false so BUSY on the other side will not be cleared immediately but - * will remain set until message would have been processed by scheduled task, i.e. - * until ipc_platform_complete_cmd() call. + * This callback is invoked by the Zephyr IPC service backend when a compact 2-word IPC message + * arrives from the host. It stores the raw header words in g_last_data/g_last_ext_data and + * schedules the SOF IPC task to process the command via ipc_platform_do_cmd(). */ -static bool message_handler(const struct device *dev, void *arg, uint32_t data, uint32_t ext_data) +static void sof_ipc_receive_cb(const void *data, size_t len, void *priv) { - struct ipc *ipc = (struct ipc *)arg; - + struct ipc *ipc = (struct ipc *)priv; + const uint32_t *msg = data; k_spinlock_key_t key; + __ASSERT(len == sizeof(uint32_t) * 2, "Unexpected IPC message length: %zu", len); + __ASSERT(data, "IPC data pointer is NULL"); + key = k_spin_lock(&ipc->lock); - g_last_data = data; - g_last_ext_data = ext_data; + g_last_data = msg[0]; + g_last_ext_data = msg[1]; #if CONFIG_DEBUG_IPC_COUNTERS increment_ipc_received_counter(); @@ -84,8 +95,6 @@ static bool message_handler(const struct device *dev, void *arg, uint32_t data, ipc_schedule_process(ipc); k_spin_unlock(&ipc->lock, key); - - return false; } #ifdef CONFIG_PM_DEVICE @@ -159,9 +168,6 @@ static int ipc_device_resume_handler(const struct device *dev, void *arg) ipc->task_mask = 0; ipc->pm_prepare_D3 = false; - /* attach handlers */ - intel_adsp_ipc_set_message_handler(INTEL_ADSP_IPC_HOST_DEV, message_handler, ipc); - /* schedule task */ #if CONFIG_TWB_IPC_TASK scheduler_twb_task_init(&ipc->ipc_task, SOF_UUID(zipc_task_uuid), @@ -254,7 +260,10 @@ enum task_state ipc_platform_do_cmd(struct ipc *ipc) void ipc_platform_complete_cmd(struct ipc *ipc) { ARG_UNUSED(ipc); - intel_adsp_ipc_complete(INTEL_ADSP_IPC_HOST_DEV); + int ret = ipc_service_release_rx_buffer(&sof_ipc_ept, NULL); + + if (ret < 0) + tr_warn(&ipc_tr, "ipc_service_release_rx_buffer() failed: %d", ret); #if CONFIG_DEBUG_IPC_COUNTERS increment_ipc_processed_counter(); @@ -263,30 +272,34 @@ void ipc_platform_complete_cmd(struct ipc *ipc) int ipc_platform_send_msg(const struct ipc_msg *msg) { - if (!intel_adsp_ipc_is_complete(INTEL_ADSP_IPC_HOST_DEV)) + if (ipc_service_get_tx_buffer_size(&sof_ipc_ept) == 0) return -EBUSY; /* prepare the message and copy to mailbox */ struct ipc_cmd_hdr *hdr = ipc_prepare_to_send(msg); - return intel_adsp_ipc_send_message(INTEL_ADSP_IPC_HOST_DEV, hdr->pri, hdr->ext); + return ipc_service_send(&sof_ipc_ept, hdr, sizeof(*hdr)); } void ipc_platform_send_msg_direct(const struct ipc_msg *msg) { /* prepare the message and copy to mailbox */ struct ipc_cmd_hdr *hdr = ipc_prepare_to_send(msg); + int ret = ipc_service_send_critical(&sof_ipc_ept, hdr, sizeof(*hdr)); - intel_adsp_ipc_send_message_emergency(INTEL_ADSP_IPC_HOST_DEV, hdr->pri, hdr->ext); + if (ret < 0) + tr_err(&ipc_tr, "ipc_service_send_critical() failed: %d", ret); } int ipc_platform_poll_is_host_ready(void) { - return intel_adsp_ipc_is_complete(INTEL_ADSP_IPC_HOST_DEV); + return ipc_service_get_tx_buffer_size(&sof_ipc_ept) > 0; } int platform_ipc_init(struct ipc *ipc) { + int ret; + ipc_set_drvdata(ipc, NULL); /* schedule task */ @@ -300,9 +313,22 @@ int platform_ipc_init(struct ipc *ipc) #endif /* configure interrupt - work is done internally by Zephyr API */ - /* attach handlers */ - intel_adsp_ipc_set_message_handler(INTEL_ADSP_IPC_HOST_DEV, message_handler, ipc); -#ifdef CONFIG_PM + sof_ipc_ept_cfg.name = "sof_ipc"; + sof_ipc_ept_cfg.prio = 0; + sof_ipc_ept_cfg.cb.received = sof_ipc_receive_cb; + sof_ipc_ept_cfg.priv = ipc; + + /* + * TODO: INTEL_ADSP_IPC_HOST_DEV is currently hardcoded for Intel ADSP platform. + * This needs to be made generic/configurable when additional vendor IPC backends + * become available (e.g., via devicetree, Kconfig, or platform-specific device selection). + */ + ret = ipc_service_register_endpoint(INTEL_ADSP_IPC_HOST_DEV, + &sof_ipc_ept, &sof_ipc_ept_cfg); + if (ret < 0) + return ret; + +#if defined(CONFIG_PM) && defined(CONFIG_INTEL_ADSP_IPC) intel_adsp_ipc_set_suspend_handler(INTEL_ADSP_IPC_HOST_DEV, ipc_device_suspend_handler, ipc); intel_adsp_ipc_set_resume_handler(INTEL_ADSP_IPC_HOST_DEV, @@ -312,6 +338,7 @@ int platform_ipc_init(struct ipc *ipc) return 0; } +#ifdef CONFIG_INTEL_ADSP_IPC static bool ipc_wait_complete(const struct device *dev, void *arg) { k_sem_give(arg); @@ -331,3 +358,4 @@ void ipc_platform_wait_ack(struct ipc *ipc) intel_adsp_ipc_set_done_handler(INTEL_ADSP_IPC_HOST_DEV, NULL, NULL); } +#endif /* CONFIG_INTEL_ADSP_IPC */ diff --git a/src/ipc/ipc3/README.md b/src/ipc/ipc3/README.md new file mode 100644 index 000000000000..8f3f7c68a24d --- /dev/null +++ b/src/ipc/ipc3/README.md @@ -0,0 +1,113 @@ +# IPC3 Architecture + +This directory houses the Version 3 Inter-Processor Communication handling components. IPC3 is the older, legacy framework structure used extensively across initial Sound Open Firmware releases before the transition to IPC4 compound pipeline commands. + +## Overview + +The IPC3 architecture treats streaming, DAI configurations, and pipeline management as distinct scalar events. Messages arrive containing a specific `sof_ipc_cmd_hdr` denoting the "Global Message Type" (e.g., Stream, DAI, Trace, PM) and the targeted command within that type. + +## Command Structure and Routing + +Every message received is placed into an Rx buffer and initially routed to `ipc_cmd()`. Based on the `cmd` inside the `sof_ipc_cmd_hdr`, it delegates to one of the handler subsystems: + +* `ipc_glb_stream_message`: Stream/Pipeline configuration and states +* `ipc_glb_dai_message`: DAI parameters and formats +* `ipc_glb_pm_message`: Power Management operations + +```mermaid +graph TD + Mailbox[IPC Mailbox Interrupt] --> Valid[mailbox_validate] + Valid --> Disp[IPC Core Dispatcher] + + Disp -->|Global Type 1| StreamMsg[ipc_glb_stream_message] + Disp -->|Global Type 2| DAIMsg[ipc_glb_dai_message] + Disp -->|Global Type 3| PMMsg[ipc_glb_pm_message] + Disp -->|Global Type ...| TraceMsg[ipc_glb_trace_message] + + subgraph Stream Commands + StreamMsg --> StreamAlloc[ipc_stream_pcm_params] + StreamMsg --> StreamTrig[ipc_stream_trigger] + StreamMsg --> StreamFree[ipc_stream_pcm_free] + StreamMsg --> StreamPos[ipc_stream_position] + end + + subgraph DAI Commands + DAIMsg --> DAIConf[ipc_msg_dai_config] + end + + subgraph PM Commands + PMMsg --> PMCore[ipc_pm_core_enable] + PMMsg --> PMContext[ipc_pm_context_save / restore] + end +``` + +## Processing Flows + +### Stream Triggering (`ipc_stream_trigger`) + +Triggering is strictly hierarchical via IPC3. It expects pipelines built and components fully parsed prior to active streaming commands. + +1. **Validation**: The IPC fetches the host component ID. +2. **Device Lookup**: It searches the components list (`ipc_get_comp_dev`) for the PCM device matching the pipeline. +3. **Execution**: If valid, the pipeline graph is crawled recursively and its state altered via `pipeline_trigger`. + +```mermaid +sequenceDiagram + participant Host + participant IPC3 as IPC3 Handler (ipc_stream_trigger) + participant Pipe as Pipeline Framework + participant Comp as Connected Component + + Host->>IPC3: Send SOF_IPC_STREAM_TRIG_START + activate IPC3 + IPC3->>IPC3: ipc_get_comp_dev(stream_id) + IPC3->>Pipe: pipeline_trigger(COMP_TRIGGER_START) + activate Pipe + Pipe->>Comp: pipeline_for_each_comp(COMP_TRIGGER_START) + Comp-->>Pipe: Success (Component ACTIVE) + Pipe-->>IPC3: Return Status + deactivate Pipe + + alt If Success + IPC3-->>Host: Acknowledge Success Header + else If Error + IPC3-->>Host: Acknowledge Error Header (EINVAL / EIO) + end + deactivate IPC3 +``` + +### DAI Configuration (`ipc_msg_dai_config`) + +DAI (Digital Audio Interface) configuration involves setting up physical I2S, ALH, SSP, or HDA parameters. + +1. **Format Unpacking**: Converts the `sof_ipc_dai_config` payload sent from the ALSA driver into an internal DSP structure `ipc_config_dai`. +2. **Device Selection**: Identifies the exact DAI interface and finds its tracking device ID via `dai_get`. +3. **Hardware Config**: Applies the unpacked settings directly to the hardware via the specific DAI driver's `set_config` function. + +```mermaid +sequenceDiagram + participant Host + participant IPC3 as IPC3 Handler (ipc_msg_dai_config) + participant DAIDev as DAI Framework (dai_get) + participant HWDriver as HW Specific Driver (e.g. SSP) + + Host->>IPC3: Send SOF_IPC_DAI_CONFIG (e.g., SSP1, I2S Format) + activate IPC3 + + IPC3->>IPC3: build_dai_config() + IPC3->>DAIDev: dai_get(type, index) + DAIDev-->>IPC3: pointer to dai instance + + IPC3->>HWDriver: dai_set_config() + activate HWDriver + HWDriver-->>HWDriver: configures registers + HWDriver-->>IPC3: hardware configured + deactivate HWDriver + + IPC3-->>Host: Acknowledged Setting + deactivate IPC3 +``` + +## Mailbox and Validation (`mailbox_validate`) + +All commands passing through this layer enforce rigid payload boundaries. `mailbox_validate()` reads the first word directly from the mailbox memory, identifying the command type before parsing parameters out of shared RAM to prevent host/DSP mismatches from cascading. diff --git a/src/ipc/ipc3/dai.c b/src/ipc/ipc3/dai.c index 870459e3a352..d208e0a884b9 100644 --- a/src/ipc/ipc3/dai.c +++ b/src/ipc/ipc3/dai.c @@ -93,9 +93,14 @@ int dai_config_dma_channel(struct dai_data *dd, struct comp_dev *dev, const void break; case SOF_DAI_AMD_HS: case SOF_DAI_AMD_HS_VIRTUAL: - case SOF_DAI_AMD_SW_AUDIO: + case SOF_DAI_AMD_SDW: channel = dai_get_handshake(dd->dai, dai->direction, dd->stream_id); +#if defined(CONFIG_SOC_ACP_7_0) + if (channel >= 64 && channel < 128) { + channel = channel - 64; + } +#endif break; case SOF_DAI_MEDIATEK_AFE: handshake = dai_get_handshake(dd->dai, dai->direction, @@ -188,8 +193,32 @@ int ipc_dai_data_config(struct dai_data *dd, struct comp_dev *dev) break; case SOF_DAI_AMD_HS: case SOF_DAI_AMD_HS_VIRTUAL: - case SOF_DAI_AMD_SW_AUDIO: - dev->ipc_config.frame_fmt = SOF_IPC_FRAME_S16_LE; + case SOF_DAI_AMD_SDW: +#if defined(CONFIG_AMD) && !defined(CONFIG_SOC_ACP_6_0) + { + struct acp_dma_dev_data *dev_data = dd->dma->z_dev->data; + struct sdw_pin_data *pin_data; + + /* Allocate memory only if not already allocated */ + if (!dev_data->dai_index_ptr) { + pin_data = rzalloc(SOF_MEM_FLAG_USER | SOF_MEM_FLAG_COHERENT, + sizeof(*pin_data)); + dev_data->dai_index_ptr = pin_data; + } else { + pin_data = dev_data->dai_index_ptr; + } + pin_data->pin_num = dd->dai->index; + pin_data->pin_dir = dai->direction; +#ifdef CONFIG_ZEPHYR_NATIVE_DRIVERS + pin_data->dma_channel = dd->chan_index >= 0 ? dd->chan_index : 0xFFFF; +#else + pin_data->dma_channel = dd->chan ? dd->chan->index : 0xFFFF; +#endif + pin_data->index = 0xFFFF; + pin_data->instance = 0xFFFF; + dev_data->dai_index_ptr = pin_data; + } +#endif break; case SOF_DAI_MEDIATEK_AFE: break; @@ -215,7 +244,7 @@ int ipc_comp_dai_config(struct ipc *ipc, struct ipc_config_dai *common_config, int ret = -ENODEV; int i; - tr_info(&ipc_tr, "ipc_comp_dai_config() dai type = %d index = %d", + tr_info(&ipc_tr, "dai type = %d index = %d", config->type, config->dai_index); /* for each component */ @@ -286,17 +315,23 @@ void dai_dma_release(struct dai_data *dd, struct comp_dev *dev) } /* put the allocated DMA channel first */ +#ifdef CONFIG_ZEPHYR_NATIVE_DRIVERS + if (dd->chan_index >= 0) { + /* remove callback */ + notifier_unregister(dev, &dd->dma->chan[dd->chan_index], + NOTIFIER_ID_DMA_COPY); + dma_release_channel(dd->dma->z_dev, dd->chan_index); + dd->chan_index = -EINVAL; + } +#else if (dd->chan) { /* remove callback */ notifier_unregister(dev, dd->chan, NOTIFIER_ID_DMA_COPY); -#if CONFIG_ZEPHYR_NATIVE_DRIVERS - dma_release_channel(dd->chan->dma->z_dev, dd->chan->index); -#else dma_channel_put_legacy(dd->chan); -#endif dd->chan->dev_data = NULL; dd->chan = NULL; } +#endif } int dai_config(struct dai_data *dd, struct comp_dev *dev, struct ipc_config_dai *common_config, @@ -310,7 +345,7 @@ int dai_config(struct dai_data *dd, struct comp_dev *dev, struct ipc_config_dai dd->ipc_config.type != config->type) return 0; - comp_info(dev, "dai_config() dai type = %d index = %d dd %p", + comp_info(dev, "dai type = %d index = %d dd %p", config->type, config->dai_index, dd); /* cannot configure DAI while active */ @@ -327,20 +362,33 @@ int dai_config(struct dai_data *dd, struct comp_dev *dev, struct ipc_config_dai if (SOF_DAI_QUIRK_IS_SET(config->flags, SOF_DAI_CONFIG_FLAGS_2_STEP_STOP)) dd->delayed_dma_stop = true; +#ifdef CONFIG_ZEPHYR_NATIVE_DRIVERS + if (dd->chan_index >= 0) { + comp_info(dev, "Configured. dma channel index %d, ignore...", + dd->chan_index); + return 0; + } +#else if (dd->chan) { comp_info(dev, "Configured. dma channel index %d, ignore...", dd->chan->index); return 0; } +#endif break; case SOF_DAI_CONFIG_FLAGS_HW_FREE: +#ifdef CONFIG_ZEPHYR_NATIVE_DRIVERS + if (dd->chan_index < 0) + return 0; +#else if (!dd->chan) return 0; +#endif /* stop DMA and reset config for two-step stop DMA */ if (dd->delayed_dma_stop) { -#if CONFIG_ZEPHYR_NATIVE_DRIVERS - ret = dma_stop(dd->chan->dma->z_dev, dd->chan->index); +#ifdef CONFIG_ZEPHYR_NATIVE_DRIVERS + ret = dma_stop(dd->dma->z_dev, dd->chan_index); #else ret = dma_stop_delayed_legacy(dd->chan); #endif @@ -352,11 +400,13 @@ int dai_config(struct dai_data *dd, struct comp_dev *dev, struct ipc_config_dai return 0; case SOF_DAI_CONFIG_FLAGS_PAUSE: - if (!dd->chan) +#ifdef CONFIG_ZEPHYR_NATIVE_DRIVERS + if (dd->chan_index < 0) return 0; -#if CONFIG_ZEPHYR_NATIVE_DRIVERS - return dma_stop(dd->chan->dma->z_dev, dd->chan->index); + return dma_stop(dd->dma->z_dev, dd->chan_index); #else + if (!dd->chan) + return 0; return dma_stop_delayed_legacy(dd->chan); #endif default: @@ -379,7 +429,7 @@ int dai_config(struct dai_data *dd, struct comp_dev *dev, struct ipc_config_dai dd->dai_spec_config = rzalloc(SOF_MEM_FLAG_USER | SOF_MEM_FLAG_COHERENT, sizeof(struct sof_ipc_dai_config)); if (!dd->dai_spec_config) { - comp_err(dev, "No memory for dai_config."); + comp_err(dev, "No memory"); return -ENOMEM; } } diff --git a/src/ipc/ipc3/handler.c b/src/ipc/ipc3/handler.c index 9f865960f1bf..5bd5293c61b7 100644 --- a/src/ipc/ipc3/handler.c +++ b/src/ipc/ipc3/handler.c @@ -579,7 +579,7 @@ static int ipc_dai_config_set(struct sof_ipc_dai_config *config, } /* configure DAI */ - ret = dai_set_config(dai, config_dai, config); + ret = dai_set_config(dai, config_dai, config, sizeof(*config)); dai_put(dai); /* free ref immediately */ if (ret < 0) { ipc_cmd_err(&ipc_tr, "ipc: dai %d,%d config failed %d", config->type, @@ -829,7 +829,7 @@ static int ipc_dma_trace_config(uint32_t header) if (!dmat) { mtrace_printf(LOG_LEVEL_ERROR, - "ipc_dma_trace_config failed: dmat not initialized"); + "failed: dmat not initialized"); return -ENOMEM; } @@ -878,7 +878,7 @@ static int ipc_dma_trace_config(uint32_t header) error: #if CONFIG_HOST_PTABLE - dma_sg_free(&elem_array); + dma_sg_free(NULL, &elem_array); processing_error: #endif diff --git a/src/ipc/ipc3/helper.c b/src/ipc/ipc3/helper.c index 658ceaa26784..4d87f042dd1d 100644 --- a/src/ipc/ipc3/helper.c +++ b/src/ipc/ipc3/helper.c @@ -395,8 +395,8 @@ int ipc_pipeline_new(struct ipc *ipc, ipc_pipe_new *_pipe_desc) } /* create the pipeline */ - pipe = pipeline_new(pipe_desc->pipeline_id, pipe_desc->priority, - pipe_desc->comp_id); + pipe = pipeline_new(NULL, pipe_desc->pipeline_id, pipe_desc->priority, + pipe_desc->comp_id, NULL); if (!pipe) { tr_err(&ipc_tr, "pipeline_new() failed"); return -ENOMEM; @@ -485,7 +485,7 @@ int ipc_buffer_new(struct ipc *ipc, const struct sof_ipc_buffer *desc) } /* register buffer with pipeline */ - buffer = buffer_new(desc, BUFFER_USAGE_NOT_SHARED); + buffer = buffer_new(NULL, desc, BUFFER_USAGE_NOT_SHARED); if (!buffer) { tr_err(&ipc_tr, "buffer_new() failed"); return -ENOMEM; diff --git a/src/ipc/ipc3/host-page-table.c b/src/ipc/ipc3/host-page-table.c index 7a3da31caa82..997ee6683352 100644 --- a/src/ipc/ipc3/host-page-table.c +++ b/src/ipc/ipc3/host-page-table.c @@ -239,6 +239,6 @@ int ipc_process_host_buffer(struct ipc *ipc, return 0; error: - dma_sg_free(elem_array); + dma_sg_free(NULL, elem_array); return err; } diff --git a/src/ipc/ipc4/CMakeLists.txt b/src/ipc/ipc4/CMakeLists.txt index 02c56130ed11..360a4e988650 100644 --- a/src/ipc/ipc4/CMakeLists.txt +++ b/src/ipc/ipc4/CMakeLists.txt @@ -2,13 +2,19 @@ # Files common to Zephyr and XTOS add_local_sources(sof - dai.c - handler.c + handler-user.c + handler-kernel.c helper.c logging.c notification.c ) +# The DAI interface is not implemented in library builds and +# code depends on Zephyr DAI driver definitions to be available. +if(NOT CONFIG_LIBRARY) + add_local_sources(sof dai.c) +endif() + is_zephyr(zephyr) if(zephyr) ### Zephyr ### diff --git a/src/ipc/ipc4/README.md b/src/ipc/ipc4/README.md new file mode 100644 index 000000000000..1b40195085b9 --- /dev/null +++ b/src/ipc/ipc4/README.md @@ -0,0 +1,104 @@ +# IPC4 Architecture + +This directory holds the handlers and topology parsing logic for Inter-Processor Communication Version 4. IPC4 introduces a significantly denser, compound-command structure heavily based around the concept of "pipelines" and dynamic "modules" rather than static DSP stream roles. + +## Overview + +Unlike older iterations (IPC3) which trigger single components via scalar commands, IPC4 uses compound structures. A single host interrupt might contain batch operations like building an entire processing chain, setting module parameters sequentially, and triggering a start across multiple interconnected blocks simultaneously. + +## Message Handling and Dispatch + +IPC4 messages are received via the generic IPC handler entry point `ipc_cmd()`. `ipc_cmd()` determines the IPC target and dispatches the message appropriately. Global messages are dispatched to `ipc4_process_glb_message()`, while module-specific messages are routed directly to `ipc4_process_module_message()`. + +```mermaid +graph TD + Mailbox[IPC Mailbox Interrupt] --> CoreIPC[ipc_cmd] + + CoreIPC --> TypeSel{Decode IPC Message Target} + TypeSel -->|FW_GEN_MSG| Glb[ipc4_process_glb_message] + TypeSel -->|MODULE_MSG| Mod[ipc4_process_module_message] + + subgraph Global Handler + Glb --> NewPipe[ipc4_new_pipeline] + Glb --> DelPipe[ipc4_delete_pipeline] + Glb --> MemMap[ipc4_process_chain_dma] + Glb --> SetPipe[ipc4_set_pipeline_state] + end + + subgraph Module Handler + Mod --> InitMod[ipc4_init_module_instance] + Mod --> SetMod[ipc4_set_module_params] + Mod --> GetMod[ipc4_get_module_params] + Mod --> Bind[ipc4_bind] + Mod --> Unbind[ipc4_unbind] + end +``` + +## Processing Flows + +### Pipeline State Management (`ipc4_set_pipeline_state`) + +The core driver of graph execution in IPC4 is `ipc4_set_pipeline_state()`. This accepts a multi-stage request (e.g., `START`, `PAUSE`, `RESET`) and coordinates triggering the internal pipelines. + +1. **State Translation**: It maps the incoming IPC4 state request to an internal SOF state (e.g., `IPC4_PIPELINE_STATE_RUNNING` -> `COMP_TRIGGER_START`). +2. **Graph Traversal**: It fetches the pipeline object associated with the command and begins preparing it (`ipc4_pipeline_prepare`). +3. **Trigger Execution**: It executes `ipc4_pipeline_trigger()`, recursively changing states across the internal graphs and alerting either the LL scheduler or DP threads. + +```mermaid +sequenceDiagram + participant Host + participant IPC4Set as ipc4_set_pipeline_state + participant PPLPrep as ipc4_pipeline_prepare + participant PPLTrig as ipc4_pipeline_trigger + participant Comp as Graph Components + + Host->>IPC4Set: IPC4_PIPELINE_STATE_RUNNING + activate IPC4Set + + IPC4Set->>PPLPrep: Maps to COMP_TRIGGER_START + PPLPrep->>Comp: Applies PCM params & formatting + Comp-->>PPLPrep: Components ready + + IPC4Set->>PPLTrig: execute trigger + PPLTrig->>Comp: pipeline_trigger(COMP_TRIGGER_START) + Comp-->>PPLTrig: Success + + IPC4Set-->>Host: Reply: ipc4_send_reply() + deactivate IPC4Set +``` + +### Module Instantiation and Binding (`ipc4_bind`) + +In IPC4, modules (components) are bound together dynamically rather than constructed statically by the firmware at boot time. + +1. **Instantiation**: `ipc4_init_module_instance()` allocates the module via the DSP heap arrays based on UUIDs. +2. **Binding**: `ipc4_bind()` takes two module IDs and dynamically connects their sink and source pins using intermediate `comp_buffer` objects. + +```mermaid +sequenceDiagram + participant Host + participant IPC4Bind as ipc4_bind + participant SrcMod as Source Module + participant SinkMod as Sink Module + participant Buff as Connection Buffer + + Host->>IPC4Bind: Bind Src(ID) -> Sink(ID) + activate IPC4Bind + + IPC4Bind->>SrcMod: Locate by ID + IPC4Bind->>SinkMod: Locate by ID + + IPC4Bind->>Buff: buffer_new() (Create Intermediate Storage) + + IPC4Bind->>SrcMod: Bind source pin to Buff (via comp_bind/comp_buffer_connect) + IPC4Bind->>SinkMod: Bind sink pin to Buff (via comp_bind/comp_buffer_connect) + + IPC4Bind-->>Host: Reply: Linked + deactivate IPC4Bind +``` + +## Compound Messages (`ipc_wait_for_compound_msg`) + +To accelerate initialization, IPC4 enables Compound commands. A host can send multiple IPC messages chained back-to-back using a single mailbox trigger flag before waiting for ACKs. + +`ipc_compound_pre_start` and `ipc_compound_post_start` manage this batch execution safely without overflowing the Zephyr work queues or breaking hardware configurations during intermediate states. diff --git a/src/ipc/ipc4/dai.c b/src/ipc/ipc4/dai.c index 94fbd1823945..9724e7578c99 100644 --- a/src/ipc/ipc4/dai.c +++ b/src/ipc/ipc4/dai.c @@ -11,6 +11,7 @@ #include <sof/common.h> #include <rtos/idc.h> #include <rtos/alloc.h> +#include <rtos/mutex.h> #include <sof/lib/dai.h> #include <sof/lib/memory.h> #include <sof/lib/notifier.h> @@ -33,12 +34,15 @@ LOG_MODULE_DECLARE(ipc, CONFIG_SOF_LOG_LEVEL); +/* Protects IPC4 LLP reading-slot firmware registers used by DAI code. */ +static SYS_MUTEX_DEFINE(llp_reading_slots_lock); + void dai_set_link_hda_config(uint16_t *link_config, struct ipc_config_dai *common_config, const void *spec_config) { #if ACE_VERSION > ACE_VERSION_1_5 - const struct ipc4_audio_format *out_fmt = common_config->out_fmt; + const struct ipc4_audio_format *gtw_fmt = common_config->gtw_fmt; union hdalink_cfg link_cfg; switch (common_config->type) { @@ -49,17 +53,23 @@ void dai_set_link_hda_config(uint16_t *link_config, break; case SOF_DAI_INTEL_DMIC: link_cfg.full = 0; - if (out_fmt->depth == IPC4_DEPTH_16BIT) { + if (gtw_fmt->depth == IPC4_DEPTH_16BIT) { /* 16bit dmic packs two 16bit samples into single 32bit word * fw needs to adjust channel count to match final sample * group size */ - link_cfg.part.hchan = (out_fmt->channels_count - 1) / 2; + link_cfg.part.hchan = (gtw_fmt->channels_count - 1) / 2; } else { - link_cfg.part.hchan = out_fmt->channels_count - 1; + link_cfg.part.hchan = gtw_fmt->channels_count - 1; } link_cfg.part.stream = common_config->host_dma_config[0]->stream_id; break; + case SOF_DAI_INTEL_UAOL: + link_cfg.full = 0; + link_cfg.part.hchan = gtw_fmt->channels_count - 1; + link_cfg.part.dir = common_config->direction; + link_cfg.part.stream = common_config->host_dma_config[0]->stream_id; + break; default: /* other types of DAIs not need link_config */ return; @@ -91,13 +101,9 @@ int dai_config_dma_channel(struct dai_data *dd, struct comp_dev *dev, const void struct processing_module *mod = comp_mod(dev); struct copier_data *cd = module_get_private_data(mod); - if (!cd->gtw_cfg) { - comp_err(dev, "No gateway config found!"); - return SOF_DMA_CHAN_INVALID; - } - channel = SOF_DMA_CHAN_INVALID; - const struct sof_alh_configuration_blob *alh_blob = cd->gtw_cfg; + const struct sof_alh_configuration_blob *alh_blob = + (void *)cd->config.gtw_cfg.config_data; for (int i = 0; i < alh_blob->alh_cfg.count; i++) { if (dai->host_dma_config[i]->stream_id == dai->dai_index) { @@ -116,6 +122,13 @@ int dai_config_dma_channel(struct dai_data *dd, struct comp_dev *dev, const void */ channel = 0; break; +#if ACE_VERSION > ACE_VERSION_1_5 + case SOF_DAI_INTEL_UAOL: + channel = 0; + if (dai->host_dma_config[0]->pre_allocated_by_host) + channel = dai->host_dma_config[0]->dma_channel_id; + break; +#endif default: /* other types of DAIs not handled for now */ comp_err(dev, "Unknown dai type %d", dai->type); @@ -130,9 +143,7 @@ int ipc_dai_data_config(struct dai_data *dd, struct comp_dev *dev) { struct ipc_config_dai *dai = &dd->ipc_config; struct ipc4_copier_module_cfg *copier_cfg = dd->dai_spec_config; -#ifdef CONFIG_ZEPHYR_NATIVE_DRIVERS struct dai *dai_p = dd->dai; -#endif if (!dai) { comp_err(dev, "no dai!\n"); @@ -161,12 +172,7 @@ int ipc_dai_data_config(struct dai_data *dd, struct comp_dev *dev) case SOF_DAI_INTEL_HDA: break; case SOF_DAI_INTEL_ALH: -#ifdef CONFIG_ZEPHYR_NATIVE_DRIVERS dd->stream_id = dai_get_stream_id(dai_p, dai->direction); -#else - /* only native Zephyr driver supported */ - return -EINVAL; -#endif /* SDW HW FIFO always requires 32bit MSB aligned sample data for * all formats, such as 8/16/24/32 bits. */ @@ -177,6 +183,8 @@ int ipc_dai_data_config(struct dai_data *dd, struct comp_dev *dev) comp_dbg(dev, "dai_data_config() SOF_DAI_INTEL_ALH dev->ipc_config.frame_fmt: %d, stream_id: %d", dev->ipc_config.frame_fmt, dd->stream_id); + break; + case SOF_DAI_INTEL_UAOL: break; default: /* other types of DAIs not handled for now */ @@ -199,6 +207,8 @@ int ipc_comp_dai_config(struct ipc *ipc, struct ipc_config_dai *common_config, void dai_dma_release(struct dai_data *dd, struct comp_dev *dev) { + int ret; + /* cannot configure DAI while active */ if (dev->state == COMP_STATE_ACTIVE) { comp_info(dev, "Component is in active state. Ignore resetting"); @@ -206,19 +216,19 @@ void dai_dma_release(struct dai_data *dd, struct comp_dev *dev) } /* put the allocated DMA channel first */ - if (dd->chan) { + if (dd->chan_index >= 0) { struct ipc4_llp_reading_slot slot; if (dd->slot_info.node_id) { - k_spinlock_key_t key; - /* reset llp position to 0 in memory window for reset state. */ memset_s(&slot, sizeof(slot), 0, sizeof(slot)); slot.node_id = dd->slot_info.node_id; - key = k_spin_lock(&sof_get()->fw_reg_lock); + ret = sys_mutex_lock(&llp_reading_slots_lock, K_FOREVER); + assert(!ret); mailbox_sw_regs_write(dd->slot_info.reg_offset, &slot, sizeof(slot)); - k_spin_unlock(&sof_get()->fw_reg_lock, key); + ret = sys_mutex_unlock(&llp_reading_slots_lock); + assert(!ret); } /* The stop sequnece of host driver is first pause and then reset @@ -226,24 +236,18 @@ void dai_dma_release(struct dai_data *dd, struct comp_dev *dev) * pause to stop. * TODO: refine power management when stream is paused */ -#if CONFIG_ZEPHYR_NATIVE_DRIVERS /* if reset is after pause dma has already been stopped */ - dma_stop(dd->chan->dma->z_dev, dd->chan->index); + dma_stop(dd->dma->z_dev, dd->chan_index); - dma_release_channel(dd->chan->dma->z_dev, dd->chan->index); -#else - dma_stop_legacy(dd->chan); - dma_channel_put_legacy(dd->chan); -#endif - dd->chan->dev_data = NULL; - dd->chan = NULL; + dma_release_channel(dd->dma->z_dev, dd->chan_index); + dd->chan_index = -EINVAL; } } void dai_release_llp_slot(struct dai_data *dd) { struct ipc4_llp_reading_slot slot; - k_spinlock_key_t key; + int ret; if (!dd->slot_info.node_id) return; @@ -251,9 +255,11 @@ void dai_release_llp_slot(struct dai_data *dd) memset_s(&slot, sizeof(slot), 0, sizeof(slot)); /* clear node id for released llp slot */ - key = k_spin_lock(&sof_get()->fw_reg_lock); + ret = sys_mutex_lock(&llp_reading_slots_lock, K_FOREVER); + assert(!ret); mailbox_sw_regs_write(dd->slot_info.reg_offset, &slot, sizeof(slot)); - k_spin_unlock(&sof_get()->fw_reg_lock, key); + ret = sys_mutex_unlock(&llp_reading_slots_lock); + assert(!ret); dd->slot_info.reg_offset = 0; dd->slot_info.node_id = 0; @@ -263,9 +269,9 @@ static int dai_get_unused_llp_slot(struct comp_dev *dev, union ipc4_connector_node_id *node) { struct ipc4_llp_reading_slot slot; - k_spinlock_key_t key; uint32_t max_slot; uint32_t offset; + int ret; int i; /* sdw with multiple gateways uses sndw_reading_slots */ @@ -277,7 +283,8 @@ static int dai_get_unused_llp_slot(struct comp_dev *dev, max_slot = IPC4_MAX_LLP_GPDMA_READING_SLOTS; } - key = k_spin_lock(&sof_get()->fw_reg_lock); + ret = sys_mutex_lock(&llp_reading_slots_lock, K_FOREVER); + assert(!ret); /* find unused llp slot offset with node_id of zero */ for (i = 0; i < max_slot; i++, offset += sizeof(slot)) { @@ -290,7 +297,8 @@ static int dai_get_unused_llp_slot(struct comp_dev *dev, if (i >= max_slot) { comp_err(dev, "can't find free slot"); - k_spin_unlock(&sof_get()->fw_reg_lock, key); + ret = sys_mutex_unlock(&llp_reading_slots_lock); + assert(!ret); return -EINVAL; } @@ -298,7 +306,8 @@ static int dai_get_unused_llp_slot(struct comp_dev *dev, slot.node_id = node->dw & IPC4_NODE_ID_MASK; mailbox_sw_regs_write(offset, &slot, sizeof(slot)); - k_spin_unlock(&sof_get()->fw_reg_lock, key); + ret = sys_mutex_unlock(&llp_reading_slots_lock); + assert(!ret); return offset; } @@ -346,7 +355,7 @@ __cold int dai_config(struct dai_data *dd, struct comp_dev *dev, dd->ipc_config.type != common_config->type) return 0; - comp_info(dev, "dai_config() dai type = %d index = %d dd %p", + comp_info(dev, "dai type = %d index = %d dd %p", common_config->type, common_config->dai_index, dd); /* cannot configure DAI while active */ @@ -355,9 +364,9 @@ __cold int dai_config(struct dai_data *dd, struct comp_dev *dev, return 0; } - if (dd->chan) { + if (dd->chan_index >= 0) { comp_info(dev, "Configured. dma channel index %d, ignore...", - dd->chan->index); + dd->chan_index); return 0; } @@ -380,7 +389,7 @@ __cold int dai_config(struct dai_data *dd, struct comp_dev *dev, size = sizeof(*copier_cfg); dd->dai_spec_config = rzalloc(SOF_MEM_FLAG_USER, size); if (!dd->dai_spec_config) { - comp_err(dev, "No memory for dai_config size %d", size); + comp_err(dev, "No memory for size %d", size); return -ENOMEM; } @@ -396,10 +405,13 @@ __cold int dai_config(struct dai_data *dd, struct comp_dev *dev, if (ret < 0) return ret; - return dai_set_config(dd->dai, common_config, copier_cfg->gtw_cfg.config_data); + /* gtw_cfg.config_length is in words */ + size = copier_cfg->gtw_cfg.config_length << 2; + + return dai_set_config(dd->dai, common_config, + copier_cfg->gtw_cfg.config_data, size); } -#if CONFIG_ZEPHYR_NATIVE_DRIVERS int dai_common_position(struct dai_data *dd, struct comp_dev *dev, struct sof_ipc_stream_posn *posn) { @@ -412,7 +424,7 @@ int dai_common_position(struct dai_data *dd, struct comp_dev *dev, platform_dai_wallclock(dev, &dd->wallclock); posn->wallclock = dd->wallclock; - ret = dma_get_status(dd->dma->z_dev, dd->chan->index, &status); + ret = dma_get_status(dd->dma->z_dev, dd->chan_index, &status); if (ret < 0) return ret; @@ -437,7 +449,7 @@ void dai_dma_position_update(struct dai_data *dd, struct comp_dev *dev) if (!dd->slot_info.node_id) return; - ret = dma_get_status(dd->dma->z_dev, dd->chan->index, &status); + ret = dma_get_status(dd->dma->z_dev, dd->chan_index, &status); if (ret < 0) return; @@ -451,51 +463,3 @@ void dai_dma_position_update(struct dai_data *dd, struct comp_dev *dev) mailbox_sw_regs_write(dd->slot_info.reg_offset, &slot, sizeof(slot)); } -#else -int dai_common_position(struct dai_data *dd, struct comp_dev *dev, - struct sof_ipc_stream_posn *posn) -{ - struct dma_chan_status status; - - /* total processed bytes count */ - posn->dai_posn = dd->total_data_processed; - - platform_dai_wallclock(dev, &dd->wallclock); - posn->wallclock = dd->wallclock; - - status.ipc_posn_data = &posn->comp_posn; - dma_status_legacy(dd->chan, &status, dev->direction); - - return 0; -} - -int dai_position(struct comp_dev *dev, struct sof_ipc_stream_posn *posn) -{ - struct dai_data *dd = comp_get_drvdata(dev); - - return dai_common_position(dd, dev, posn); -} - -void dai_dma_position_update(struct dai_data *dd, struct comp_dev *dev) -{ - struct ipc4_llp_reading_slot slot; - struct dma_chan_status status; - uint32_t llp_data[2]; - - if (!dd->slot_info.node_id) - return; - - status.ipc_posn_data = llp_data; - dma_status_legacy(dd->chan, &status, dev->direction); - - platform_dai_wallclock(dev, &dd->wallclock); - - slot.node_id = dd->slot_info.node_id; - slot.reading.llp_l = llp_data[0]; - slot.reading.llp_u = llp_data[1]; - slot.reading.wclk_l = (uint32_t)dd->wallclock; - slot.reading.wclk_u = (uint32_t)(dd->wallclock >> 32); - - mailbox_sw_regs_write(dd->slot_info.reg_offset, &slot, sizeof(slot)); -} -#endif diff --git a/src/ipc/ipc4/handler-kernel.c b/src/ipc/ipc4/handler-kernel.c new file mode 100644 index 000000000000..ae75ebc87303 --- /dev/null +++ b/src/ipc/ipc4/handler-kernel.c @@ -0,0 +1,666 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// Copyright(c) 2021 Intel Corporation. All rights reserved. +// +// Author: Liam Girdwood <liam.r.girdwood@linux.intel.com> +// Author: Rander Wang <rander.wang@linux.intel.com> +/* + * IPC (InterProcessor Communication) provides a method of two way + * communication between the host processor and the DSP. The IPC used here + * utilises a shared mailbox and door bell between the host and DSP. + * + */ + +#include <sof/audio/buffer.h> +#include <sof/audio/component_ext.h> +#include <sof/audio/pipeline.h> +#include <sof/boot_test.h> +#include <sof/common.h> +#include <sof/ipc/topology.h> +#include <sof/ipc/common.h> +#include <sof/ipc/msg.h> +#include <sof/ipc/driver.h> +#include <sof/lib/mailbox.h> +#include <sof/lib/memory.h> +#include <sof/lib/pm_runtime.h> +#include <sof/llext_manager.h> +#include <sof/math/numbers.h> +#include <sof/tlv.h> +#include <sof/trace/trace.h> +#include <ipc4/error_status.h> +#include <ipc/header.h> +#include <ipc4/module.h> +#include <ipc4/pipeline.h> +#include <ipc4/notification.h> +#include <ipc4/handler.h> +#include <ipc/trace.h> +#include <user/trace.h> + +#include <rtos/atomic.h> +#include <rtos/kernel.h> +#include <rtos/string.h> +#include <sof/lib_manager.h> + +#if CONFIG_SOF_BOOT_TEST +/* CONFIG_SOF_BOOT_TEST depends on Zephyr */ +#include <zephyr/ztest.h> +#endif + +#include <errno.h> +#include <stdbool.h> +#include <stddef.h> +#include <stdint.h> + +#include "../audio/copier/ipcgtw_copier.h" + +/* Command format errors during fuzzing are reported for virtually all + * commands, and the resulting flood of logging becomes a severe + * performance penalty (i.e. we get a lot less fuzzing done per CPU + * cycle). + */ +#ifdef CONFIG_ARCH_POSIX_LIBFUZZER +#define ipc_cmd_err(...) +#else +#define ipc_cmd_err(...) tr_err(__VA_ARGS__) +#endif + +LOG_MODULE_DECLARE(ipc, CONFIG_SOF_LOG_LEVEL); + +struct ipc4_msg_data { + struct ipc_cmd_hdr msg_in; /* local copy of current message from host header */ + struct ipc_cmd_hdr msg_out; /* local copy of current message to host header */ + atomic_t delayed_reply; + uint32_t delayed_error; +}; + +static struct ipc4_msg_data msg_data; + +/* fw sends a fw ipc message to send the status of the last host ipc message */ +static struct ipc_msg msg_reply = {0, 0, 0, 0, LIST_INIT(msg_reply.list)}; + +static struct ipc_msg msg_notify = {0, 0, 0, 0, LIST_INIT(msg_notify.list)}; + +#if CONFIG_LIBRARY +static inline struct ipc4_message_request *ipc4_get_message_request(void) +{ + struct ipc *ipc = ipc_get(); + + return (struct ipc4_message_request *)ipc->comp_data; +} + +static inline void ipc4_send_reply(struct ipc4_message_reply *reply) +{ + struct ipc *ipc = ipc_get(); + int ret; + + /* copy the extension from the message reply */ + reply->extension.dat = msg_reply.extension; + ret = memcpy_s(ipc->comp_data, sizeof(*reply), reply, sizeof(*reply)); + assert(!ret); +} +#else +static inline struct ipc4_message_request *ipc4_get_message_request(void) +{ + /* ignoring _hdr as it does not contain valid data in IPC4/IDC case */ + return ipc_from_hdr(&msg_data.msg_in); +} + +static inline void ipc4_send_reply(struct ipc4_message_reply *reply) +{ + struct ipc *ipc = ipc_get(); + char *data = ipc->comp_data; + + ipc_msg_send(&msg_reply, data, true); +} + +#endif + +__cold static bool is_any_ppl_active(void) +{ + struct ipc_comp_dev *icd; + struct list_item *clist; + + assert_can_be_cold(); + + list_for_item(clist, &ipc_get()->comp_list) { + icd = container_of(clist, struct ipc_comp_dev, list); + if (icd->type != COMP_TYPE_PIPELINE) + continue; + + if (icd->pipeline->status == COMP_STATE_ACTIVE) + return true; + } + + return false; +} + +void ipc_compound_pre_start(int msg_id) +{ + /* ipc thread will wait for all scheduled tasks to be complete + * Use a reference count to check status of these tasks. + */ + atomic_add(&msg_data.delayed_reply, 1); +} + +void ipc_compound_post_start(uint32_t msg_id, int ret, bool delayed) +{ + if (ret) { + ipc_cmd_err(&ipc_tr, "failed to process msg %d status %d", msg_id, ret); + atomic_set(&msg_data.delayed_reply, 0); + return; + } + + /* decrease counter if it is not scheduled by another thread */ + if (!delayed) + atomic_sub(&msg_data.delayed_reply, 1); +} + +void ipc_compound_msg_done(uint32_t msg_id, int error) +{ + if (!atomic_read(&msg_data.delayed_reply)) { + ipc_cmd_err(&ipc_tr, "unexpected delayed reply"); + return; + } + + atomic_sub(&msg_data.delayed_reply, 1); + + /* error reported in delayed pipeline task */ + if (error < 0) { + if (msg_id == SOF_IPC4_GLB_SET_PIPELINE_STATE) + msg_data.delayed_error = IPC4_PIPELINE_STATE_NOT_SET; + } +} + +#if CONFIG_LIBRARY +/* There is no parallel execution in testbench for scheduler and pipelines, so the result would + * be always IPC4_FAILURE. Therefore the compound messages handling is simplified. The pipeline + * triggers will require an explicit scheduler call to get the components to desired state. + */ +int ipc_wait_for_compound_msg(void) +{ + atomic_set(&msg_data.delayed_reply, 0); + return IPC4_SUCCESS; +} +#else +int ipc_wait_for_compound_msg(void) +{ + int try_count = 30; + + while (atomic_read(&msg_data.delayed_reply)) { + k_sleep(Z_TIMEOUT_US(250)); + + if (!try_count--) { + atomic_set(&msg_data.delayed_reply, 0); + ipc_cmd_err(&ipc_tr, "ipc4: failed to wait schedule thread"); + return IPC4_FAILURE; + } + } + + return IPC4_SUCCESS; +} +#endif + +#if CONFIG_LIBRARY_MANAGER +__cold static int ipc4_load_library(struct ipc4_message_request *ipc4) +{ + struct ipc4_module_load_library library; + int ret; + + assert_can_be_cold(); + + library.header.dat = ipc4->primary.dat; + + ret = lib_manager_load_library(library.header.r.dma_id, library.header.r.lib_id, + ipc4->primary.r.type); + if (ret != 0) + return (ret == -EINVAL) ? IPC4_ERROR_INVALID_PARAM : IPC4_FAILURE; + + return IPC4_SUCCESS; +} +#endif + +static int ipc4_process_glb_message(struct ipc4_message_request *ipc4) +{ + uint32_t type; + int ret; + + type = ipc4->primary.r.type; + + switch (type) { + + /* Loads library (using Code Load or HD/A Host Output DMA) */ +#ifdef CONFIG_LIBRARY_MANAGER + case SOF_IPC4_GLB_LOAD_LIBRARY: + ret = ipc4_load_library(ipc4); + break; + case SOF_IPC4_GLB_LOAD_LIBRARY_PREPARE: + ret = ipc4_load_library(ipc4); + break; +#endif + /* not a kernel level IPC message */ + default: + /* try and handle as a user IPC message */ + ret = ipc4_user_process_glb_message(ipc4, &msg_reply); + break; + } + + return ret; +} + +/* disable power gating on core 0 */ +__cold static int ipc4_module_process_d0ix(struct ipc4_message_request *ipc4) +{ + struct ipc4_module_set_d0ix d0ix; + uint32_t module_id, instance_id; + + assert_can_be_cold(); + + int ret = memcpy_s(&d0ix, sizeof(d0ix), ipc4, sizeof(*ipc4)); + + if (ret < 0) + return IPC4_FAILURE; + + module_id = d0ix.primary.r.module_id; + instance_id = d0ix.primary.r.instance_id; + + tr_dbg(&ipc_tr, "%x : %x", module_id, instance_id); + + /* only module 0 can be used to set d0ix state */ + if (d0ix.primary.r.module_id || d0ix.primary.r.instance_id) { + ipc_cmd_err(&ipc_tr, "invalid resource id %x : %x", module_id, instance_id); + return IPC4_INVALID_RESOURCE_ID; + } + + if (d0ix.extension.r.prevent_power_gating) + pm_runtime_disable(PM_RUNTIME_DSP, PLATFORM_PRIMARY_CORE_ID); + else + pm_runtime_enable(PM_RUNTIME_DSP, PLATFORM_PRIMARY_CORE_ID); + + return 0; +} + +/* enable/disable cores according to the state mask */ +__cold static int ipc4_module_process_dx(struct ipc4_message_request *ipc4) +{ + struct ipc4_module_set_dx dx; + struct ipc4_dx_state_info dx_info; + uint32_t module_id, instance_id; + uint32_t core_id; + + assert_can_be_cold(); + + int ret = memcpy_s(&dx, sizeof(dx), ipc4, sizeof(*ipc4)); + + if (ret < 0) + return IPC4_FAILURE; + + module_id = dx.primary.r.module_id; + instance_id = dx.primary.r.instance_id; + + /* only module 0 can be used to set dx state */ + if (module_id || instance_id) { + ipc_cmd_err(&ipc_tr, "invalid resource id %x : %x", module_id, instance_id); + return IPC4_INVALID_RESOURCE_ID; + } + + dcache_invalidate_region((__sparse_force void __sparse_cache *)MAILBOX_HOSTBOX_BASE, + sizeof(dx_info)); + ret = memcpy_s(&dx_info, sizeof(dx_info), + (const void *)MAILBOX_HOSTBOX_BASE, sizeof(dx_info)); + if (ret < 0) + return IPC4_FAILURE; + + /* check if core enable mask is valid */ + if (dx_info.core_mask > MASK(CONFIG_CORE_COUNT - 1, 0)) { + ipc_cmd_err(&ipc_tr, "CONFIG_CORE_COUNT: %d < core enable mask: %d", + CONFIG_CORE_COUNT, dx_info.core_mask); + return IPC4_ERROR_INVALID_PARAM; + } + + /* check primary core first */ + if ((dx_info.core_mask & BIT(PLATFORM_PRIMARY_CORE_ID)) && + (dx_info.dx_mask & BIT(PLATFORM_PRIMARY_CORE_ID))) { + /* core0 can't be activated more, it's already active since we got here */ + ipc_cmd_err(&ipc_tr, "Core0 is already active"); + return IPC4_BAD_STATE; + } + + /* Activate/deactivate requested cores */ + for (core_id = 1; core_id < CONFIG_CORE_COUNT; core_id++) { + if ((dx_info.core_mask & BIT(core_id)) == 0) + continue; + + if (dx_info.dx_mask & BIT(core_id)) { + ret = cpu_enable_core(core_id); + if (ret != 0) { + ipc_cmd_err(&ipc_tr, "failed to enable core %d", core_id); + return IPC4_FAILURE; + } + } else { + cpu_disable_core(core_id); + if (cpu_is_core_enabled(core_id)) { + ipc_cmd_err(&ipc_tr, "failed to disable core %d", core_id); + return IPC4_FAILURE; + } + } + } + + /* Deactivating primary core if requested. */ + if (dx_info.core_mask & BIT(PLATFORM_PRIMARY_CORE_ID)) { + if (cpu_enabled_cores() & ~BIT(PLATFORM_PRIMARY_CORE_ID)) { + ipc_cmd_err(&ipc_tr, "secondary cores 0x%x still active", + cpu_enabled_cores()); + return IPC4_BUSY; + } + + if (is_any_ppl_active()) { + ipc_cmd_err(&ipc_tr, "some pipelines are still active"); + return IPC4_BUSY; + } + +#if !CONFIG_ADSP_IMR_CONTEXT_SAVE + ret = llext_manager_store_to_dram(); + if (ret < 0) + ipc_cmd_err(&ipc_tr, "Error %d saving LLEXT context. Resume might fail.", + ret); + +#if CONFIG_L3_HEAP + l3_heap_save(); +#endif +#endif + +#if defined(CONFIG_PM) + ipc_get()->task_mask |= IPC_TASK_POWERDOWN; +#endif + /* do platform specific suspending */ + platform_context_save(sof_get()); + +#if !defined(CONFIG_LIBRARY) && !defined(CONFIG_ZEPHYR_NATIVE_DRIVERS) + arch_irq_lock(); + platform_timer_stop(timer_get()); +#endif + ipc_get()->pm_prepare_D3 = 1; + } + + return IPC4_SUCCESS; +} + +__cold static int ipc4_process_module_message(struct ipc4_message_request *ipc4) +{ + uint32_t type; + int ret; + + assert_can_be_cold(); + + type = ipc4->primary.r.type; + + switch (type) { + case SOF_IPC4_MOD_SET_D0IX: + ret = ipc4_module_process_d0ix(ipc4); + break; + case SOF_IPC4_MOD_SET_DX: + ret = ipc4_module_process_dx(ipc4); + break; + default: + /* try and handle as a user IPC message */ + ret = ipc4_user_process_module_message(ipc4, &msg_reply); + break; + } + + return ret; +} + +__cold struct ipc_cmd_hdr *mailbox_validate(void) +{ + struct ipc_cmd_hdr *hdr = ipc_get()->comp_data; + + assert_can_be_cold(); + + return hdr; +} + +struct ipc_cmd_hdr *ipc_compact_read_msg(void) +{ + int words; + + words = ipc_platform_compact_read_msg(&msg_data.msg_in, 2); + if (!words) + return mailbox_validate(); + + return &msg_data.msg_in; +} + +struct ipc_cmd_hdr *ipc_prepare_to_send(const struct ipc_msg *msg) +{ + msg_data.msg_out.pri = msg->header; + msg_data.msg_out.ext = msg->extension; + + if (msg->tx_size) + mailbox_dspbox_write(0, (uint32_t *)msg->tx_data, msg->tx_size); + + return &msg_data.msg_out; +} + +__cold void ipc_boot_complete_msg(struct ipc_cmd_hdr *header, uint32_t data) +{ + assert_can_be_cold(); + + header->pri = SOF_IPC4_FW_READY; + header->ext = 0; +} + +#if defined(CONFIG_PM_DEVICE) && defined(CONFIG_INTEL_ADSP_IPC) +__cold void ipc_send_failed_power_transition_response(void) +{ + struct ipc4_message_request *request = ipc_from_hdr(&msg_data.msg_in); + struct ipc4_message_reply response; + + assert_can_be_cold(); + + response.primary.r.status = IPC4_POWER_TRANSITION_FAILED; + response.primary.r.rsp = SOF_IPC4_MESSAGE_DIR_MSG_REPLY; + response.primary.r.msg_tgt = request->primary.r.msg_tgt; + response.primary.r.type = request->primary.r.type; + + msg_reply.header = response.primary.dat; + list_init(&msg_reply.list); + + ipc_msg_send_direct(&msg_reply, NULL); +} +#endif /* defined(CONFIG_PM_DEVICE) && defined(CONFIG_INTEL_ADSP_IPC) */ + +__cold void ipc_send_panic_notification(void) +{ + assert_can_be_cold(); + + msg_notify.header = SOF_IPC4_NOTIF_HEADER(SOF_IPC4_EXCEPTION_CAUGHT); + msg_notify.extension = cpu_get_id(); + msg_notify.tx_size = 0; + msg_notify.tx_data = NULL; + list_init(&msg_notify.list); + + ipc_msg_send_direct(&msg_notify, NULL); +} + +#ifdef CONFIG_LOG_BACKEND_ADSP_MTRACE + +static bool is_notification_queued(struct ipc_msg *msg) +{ + struct ipc *ipc = ipc_get(); + k_spinlock_key_t key; + bool queued = false; + + key = k_spin_lock(&ipc->lock); + if (!list_is_empty(&msg->list)) + queued = true; + k_spin_unlock(&ipc->lock, key); + + return queued; +} + +/* Called from ipc_send_buffer_status_notify(), which is currently "hot" */ +void ipc_send_buffer_status_notify(void) +{ + /* a single msg_notify object is used */ + if (is_notification_queued(&msg_notify)) + return; + + msg_notify.header = SOF_IPC4_NOTIF_HEADER(SOF_IPC4_NOTIFY_LOG_BUFFER_STATUS); + msg_notify.extension = 0; + msg_notify.tx_size = 0; + + tr_dbg(&ipc_tr, "tx-notify\t: %#x|%#x", msg_notify.header, msg_notify.extension); + + ipc_msg_send(&msg_notify, NULL, true); +} +#endif + +void ipc_msg_reply(struct sof_ipc_reply *reply) +{ + struct ipc4_message_request in; + + in.primary.dat = msg_data.msg_in.pri; + ipc_compound_msg_done(in.primary.r.type, reply->error); +} + +void ipc_cmd(struct ipc_cmd_hdr *_hdr) +{ + struct ipc4_message_request *in = ipc4_get_message_request(); + enum ipc4_message_target target; +#ifdef CONFIG_DEBUG_IPC_TIMINGS + struct ipc4_message_request req; + uint64_t tstamp; +#endif + int err; + +#ifdef CONFIG_DEBUG_IPC_TIMINGS + req = *in; + tstamp = sof_cycle_get_64(); +#else + if (cpu_is_primary(cpu_get_id())) + tr_info(&ipc_tr, "rx\t: %#x|%#x", in->primary.dat, in->extension.dat); +#endif + /* no process on scheduled thread */ + atomic_set(&msg_data.delayed_reply, 0); + msg_data.delayed_error = 0; + msg_reply.tx_data = NULL; + msg_reply.tx_size = 0; + msg_reply.header = in->primary.dat; + msg_reply.extension = in->extension.dat; + + target = in->primary.r.msg_tgt; + + switch (target) { + case SOF_IPC4_MESSAGE_TARGET_FW_GEN_MSG: + err = ipc4_process_glb_message(in); + if (err) + ipc_cmd_err(&ipc_tr, "ipc4: FW_GEN_MSG failed with err %d", err); + break; + case SOF_IPC4_MESSAGE_TARGET_MODULE_MSG: + err = ipc4_process_module_message(in); + if (err) + ipc_cmd_err(&ipc_tr, "ipc4: MODULE_MSG failed with err %d", err); + break; + default: + /* should not reach here as we only have 2 message types */ + ipc_cmd_err(&ipc_tr, "ipc4: invalid target %d", target); + err = IPC4_UNKNOWN_MESSAGE_TYPE; + } + + /* FW sends an ipc message to host if request bit is clear */ + if (in->primary.r.rsp == SOF_IPC4_MESSAGE_DIR_MSG_REQUEST) { + struct ipc *ipc = ipc_get(); + struct ipc4_message_reply reply = {{0}}; + + /* Process flow and time stamp for IPC4 msg processed on secondary core : + * core 0 (primary core) core x (secondary core) + * # IPC msg thread #IPC delayed worker #core x idc thread + * ipc_task_ops.run() + * ipc_do_cmd() + * msg_reply.header = in->primary.dat + * ipc4_process_on_core(x) + * mask |= SECONDARY_CORE + * idc_send_message() + * Case 1: + * // Ipc msg processed by secondary core idc_ipc() + * if ((mask & SECONDARY_CORE)) ipc_cmd() + * return; ipc_msg_send() + * mask &= ~SECONDARY_CORE + * + * ipc_platform_send_msg + * ---------------------------------------------------------------------------- + * Case 2: + * idc_ipc() + * ipc_cmd() + * //Prepare reply msg + * msg_reply.header = + * reply.primary.dat; + * ipc_msg_send() + * mask &= ~SECONDARY_CORE + * + * if ((mask & IPC_TASK_SECONDARY_CORE)) + * return; + * // Ipc reply msg was prepared, so return + * if (msg_reply.header != in->primary.dat) + * return; + * ipc_platform_send_msg + * ---------------------------------------------------------------------------- + * Case 3: + * idc_ipc() + * ipc_cmd() + * //Prepare reply msg + * msg_reply.header = + * reply.primary.dat; + * ipc_msg_send() + * mask &= ~SECONDARY_CORE + * + * ipc_platform_send_msg + * + * if ((mask & IPC_TASK_SECONDARY_CORE)) + * return; + * // Ipc reply msg was prepared, so return + * if (msg_reply.header != in->primary.dat) + * return; + */ + + /* Reply prepared by secondary core */ + if ((ipc->task_mask & IPC_TASK_SECONDARY_CORE) && cpu_is_primary(cpu_get_id())) + return; + /* Reply has been prepared by secondary core */ + if (msg_reply.header != in->primary.dat) + return; + + /* Do not send reply for SET_DX if we are going to enter D3 + * The reply is going to be sent as part of the power down + * sequence + */ + if (ipc->task_mask & IPC_TASK_POWERDOWN) + return; + + if (ipc_wait_for_compound_msg() != 0) { + ipc_cmd_err(&ipc_tr, "ipc4: failed to send delayed reply"); + err = IPC4_FAILURE; + } + + /* copy contents of message received */ + reply.primary.r.rsp = SOF_IPC4_MESSAGE_DIR_MSG_REPLY; + reply.primary.r.msg_tgt = in->primary.r.msg_tgt; + reply.primary.r.type = in->primary.r.type; + if (msg_data.delayed_error) + reply.primary.r.status = msg_data.delayed_error; + else + reply.primary.r.status = err; + + msg_reply.header = reply.primary.dat; + +#ifdef CONFIG_DEBUG_IPC_TIMINGS + tr_info(&ipc_tr, "tx-reply\t: %#x|%#x to %#x|%#x in %llu us", msg_reply.header, + msg_reply.extension, req.primary.dat, req.extension.dat, + k_cyc_to_us_near64(sof_cycle_get_64() - tstamp)); +#else + tr_dbg(&ipc_tr, "tx-reply\t: %#x|%#x", msg_reply.header, + msg_reply.extension); +#endif + ipc4_send_reply(&reply); + } +} diff --git a/src/ipc/ipc4/handler.c b/src/ipc/ipc4/handler-user.c similarity index 66% rename from src/ipc/ipc4/handler.c rename to src/ipc/ipc4/handler-user.c index 3eea8f623081..2bfa31babce0 100644 --- a/src/ipc/ipc4/handler.c +++ b/src/ipc/ipc4/handler-user.c @@ -14,7 +14,6 @@ #include <sof/audio/buffer.h> #include <sof/audio/component_ext.h> #include <sof/audio/pipeline.h> -#include <sof/boot_test.h> #include <sof/common.h> #include <sof/ipc/topology.h> #include <sof/ipc/common.h> @@ -32,6 +31,7 @@ #include <ipc4/module.h> #include <ipc4/pipeline.h> #include <ipc4/notification.h> +#include <ipc4/handler.h> #include <ipc/trace.h> #include <user/trace.h> @@ -39,11 +39,6 @@ #include <rtos/kernel.h> #include <sof/lib_manager.h> -#if CONFIG_SOF_BOOT_TEST -/* CONFIG_SOF_BOOT_TEST depends on Zephyr */ -#include <zephyr/ztest.h> -#endif - #include <errno.h> #include <stdbool.h> #include <stddef.h> @@ -64,36 +59,11 @@ LOG_MODULE_DECLARE(ipc, CONFIG_SOF_LOG_LEVEL); -struct ipc4_msg_data { - struct ipc_cmd_hdr msg_in; /* local copy of current message from host header */ - struct ipc_cmd_hdr msg_out; /* local copy of current message to host header */ - atomic_t delayed_reply; - uint32_t delayed_error; -}; - -static struct ipc4_msg_data msg_data; - +/* Userspace message context, copied in/out by kernel IPC thread. */ /* fw sends a fw ipc message to send the status of the last host ipc message */ -static struct ipc_msg msg_reply = {0, 0, 0, 0, LIST_INIT(msg_reply.list)}; - -static struct ipc_msg msg_notify = {0, 0, 0, 0, LIST_INIT(msg_notify.list)}; +static struct ipc_msg *msg_reply; #if CONFIG_LIBRARY -static inline struct ipc4_message_request *ipc4_get_message_request(void) -{ - struct ipc *ipc = ipc_get(); - - return (struct ipc4_message_request *)ipc->comp_data; -} - -static inline void ipc4_send_reply(struct ipc4_message_reply *reply) -{ - struct ipc *ipc = ipc_get(); - - /* copy the extension from the message reply */ - reply->extension.dat = msg_reply.extension; - memcpy((char *)ipc->comp_data, reply, sizeof(*reply)); -} static inline const struct ipc4_pipeline_set_state_data *ipc4_get_pipeline_data(void) { @@ -105,19 +75,6 @@ static inline const struct ipc4_pipeline_set_state_data *ipc4_get_pipeline_data( return ppl_data; } #else -static inline struct ipc4_message_request *ipc4_get_message_request(void) -{ - /* ignoring _hdr as it does not contain valid data in IPC4/IDC case */ - return ipc_from_hdr(&msg_data.msg_in); -} - -static inline void ipc4_send_reply(struct ipc4_message_reply *reply) -{ - struct ipc *ipc = ipc_get(); - char *data = ipc->comp_data; - - ipc_msg_send(&msg_reply, data, true); -} static inline const struct ipc4_pipeline_set_state_data *ipc4_get_pipeline_data(void) { @@ -186,25 +143,6 @@ static int ipc4_pcm_params(struct ipc_comp_dev *pcm_dev) return err; } -__cold static bool is_any_ppl_active(void) -{ - struct ipc_comp_dev *icd; - struct list_item *clist; - - assert_can_be_cold(); - - list_for_item(clist, &ipc_get()->comp_list) { - icd = container_of(clist, struct ipc_comp_dev, list); - if (icd->type != COMP_TYPE_PIPELINE) - continue; - - if (icd->pipeline->status == COMP_STATE_ACTIVE) - return true; - } - - return false; -} - static struct ipc_comp_dev *pipeline_get_host_dev(struct ipc_comp_dev *ppl_icd) { struct ipc_comp_dev *host_dev; @@ -474,72 +412,6 @@ int ipc4_pipeline_trigger(struct ipc_comp_dev *ppl_icd, uint32_t cmd, bool *dela return ret; } -static void ipc_compound_pre_start(int msg_id) -{ - /* ipc thread will wait for all scheduled tasks to be complete - * Use a reference count to check status of these tasks. - */ - atomic_add(&msg_data.delayed_reply, 1); -} - -static void ipc_compound_post_start(uint32_t msg_id, int ret, bool delayed) -{ - if (ret) { - ipc_cmd_err(&ipc_tr, "failed to process msg %d status %d", msg_id, ret); - atomic_set(&msg_data.delayed_reply, 0); - return; - } - - /* decrease counter if it is not scheduled by another thread */ - if (!delayed) - atomic_sub(&msg_data.delayed_reply, 1); -} - -static void ipc_compound_msg_done(uint32_t msg_id, int error) -{ - if (!atomic_read(&msg_data.delayed_reply)) { - ipc_cmd_err(&ipc_tr, "unexpected delayed reply"); - return; - } - - atomic_sub(&msg_data.delayed_reply, 1); - - /* error reported in delayed pipeline task */ - if (error < 0) { - if (msg_id == SOF_IPC4_GLB_SET_PIPELINE_STATE) - msg_data.delayed_error = IPC4_PIPELINE_STATE_NOT_SET; - } -} - -#if CONFIG_LIBRARY -/* There is no parallel execution in testbench for scheduler and pipelines, so the result would - * be always IPC4_FAILURE. Therefore the compound messages handling is simplified. The pipeline - * triggers will require an explicit scheduler call to get the components to desired state. - */ -static int ipc_wait_for_compound_msg(void) -{ - atomic_set(&msg_data.delayed_reply, 0); - return IPC4_SUCCESS; -} -#else -static int ipc_wait_for_compound_msg(void) -{ - int try_count = 30; - - while (atomic_read(&msg_data.delayed_reply)) { - k_sleep(Z_TIMEOUT_US(250)); - - if (!try_count--) { - atomic_set(&msg_data.delayed_reply, 0); - ipc_cmd_err(&ipc_tr, "ipc4: failed to wait schedule thread"); - return IPC4_FAILURE; - } - } - - return IPC4_SUCCESS; -} -#endif - __cold const struct ipc4_pipeline_set_state_data *ipc4_get_pipeline_data_wrapper(void) { assert_can_be_cold(); @@ -676,25 +548,6 @@ static int ipc4_set_pipeline_state(struct ipc4_message_request *ipc4) return ret; } -#if CONFIG_LIBRARY_MANAGER -__cold static int ipc4_load_library(struct ipc4_message_request *ipc4) -{ - struct ipc4_module_load_library library; - int ret; - - assert_can_be_cold(); - - library.header.dat = ipc4->primary.dat; - - ret = lib_manager_load_library(library.header.r.dma_id, library.header.r.lib_id, - ipc4->primary.r.type); - if (ret != 0) - return (ret == -EINVAL) ? IPC4_ERROR_INVALID_PARAM : IPC4_FAILURE; - - return IPC4_SUCCESS; -} -#endif - __cold static int ipc4_process_chain_dma(struct ipc4_message_request *ipc4) { assert_can_be_cold(); @@ -764,11 +617,11 @@ __cold static int ipc4_process_ipcgtw_cmd(struct ipc4_message_request *ipc4) err = copier_ipcgtw_process((const struct ipc4_ipcgtw_cmd *)ipc4, ipc->comp_data, &reply_size); /* reply size is returned in header extension dword */ - msg_reply.extension = reply_size; + msg_reply->extension = reply_size; if (reply_size > 0) { - msg_reply.tx_data = ipc->comp_data; - msg_reply.tx_size = reply_size; + msg_reply->tx_data = ipc->comp_data; + msg_reply->tx_size = reply_size; } return err < 0 ? IPC4_FAILURE : IPC4_SUCCESS; @@ -788,12 +641,14 @@ static int ipc_glb_gdb_debug(struct ipc4_message_request *ipc4) #endif } -static int ipc4_process_glb_message(struct ipc4_message_request *ipc4) +int ipc4_user_process_glb_message(struct ipc4_message_request *ipc4, + struct ipc_msg *reply) { uint32_t type; int ret; type = ipc4->primary.r.type; + msg_reply = reply; switch (type) { case SOF_IPC4_GLB_BOOT_CONFIG: @@ -828,15 +683,6 @@ static int ipc4_process_glb_message(struct ipc4_message_request *ipc4) ret = IPC4_UNAVAILABLE; break; - /* Loads library (using Code Load or HD/A Host Output DMA) */ -#ifdef CONFIG_LIBRARY_MANAGER - case SOF_IPC4_GLB_LOAD_LIBRARY: - ret = ipc4_load_library(ipc4); - break; - case SOF_IPC4_GLB_LOAD_LIBRARY_PREPARE: - ret = ipc4_load_library(ipc4); - break; -#endif case SOF_IPC4_GLB_INTERNAL_MESSAGE: ipc_cmd_err(&ipc_tr, "not implemented ipc message type %d", type); ret = IPC4_UNAVAILABLE; @@ -887,7 +733,7 @@ __cold static int ipc4_init_module_instance(struct ipc4_message_request *ipc4) return IPC4_FAILURE; tr_dbg(&ipc_tr, - "ipc4_init_module_instance %x : %x", + "%x : %x", (uint32_t)module_init.primary.r.module_id, (uint32_t)module_init.primary.r.instance_id); @@ -918,7 +764,7 @@ __cold static int ipc4_bind_module_instance(struct ipc4_message_request *ipc4) if (ret < 0) return IPC4_FAILURE; - tr_dbg(&ipc_tr, "ipc4_bind_module_instance %x : %x with %x : %x", + tr_dbg(&ipc_tr, "%x : %x with %x : %x", (uint32_t)bu.primary.r.module_id, (uint32_t)bu.primary.r.instance_id, (uint32_t)bu.extension.r.dst_module_id, (uint32_t)bu.extension.r.dst_instance_id); @@ -937,7 +783,7 @@ __cold static int ipc4_unbind_module_instance(struct ipc4_message_request *ipc4) if (ret < 0) return IPC4_FAILURE; - tr_dbg(&ipc_tr, "ipc4_unbind_module_instance %x : %x with %x : %x", + tr_dbg(&ipc_tr, "%x : %x with %x : %x", (uint32_t)bu.primary.r.module_id, (uint32_t)bu.primary.r.instance_id, (uint32_t)bu.extension.r.dst_module_id, (uint32_t)bu.extension.r.dst_instance_id); @@ -952,7 +798,7 @@ static int ipc4_set_get_config_module_instance(struct ipc4_message_request *ipc4 struct comp_dev *dev = NULL; int ret; - tr_dbg(&ipc_tr, "ipc4_set_get_config_module_instance %x : %x, set %d", + tr_dbg(&ipc_tr, "%x : %x, set %d", (uint32_t)config->primary.r.module_id, (uint32_t)config->primary.r.instance_id, !!set); @@ -983,7 +829,7 @@ static int ipc4_set_get_config_module_instance(struct ipc4_message_request *ipc4 ret = function(dev, COMP_ATTR_IPC4_CONFIG, &config->extension.dat); if (ret < 0) { - ipc_cmd_err(&ipc_tr, "ipc4_set_get_config_module_instance %x : %x failed %d, set %u, param %x", + ipc_cmd_err(&ipc_tr, "%x : %x failed %d, set %u, param %x", (uint32_t)config->primary.r.module_id, (uint32_t)config->primary.r.instance_id, ret, !!set, (uint32_t)config->extension.dat); @@ -991,7 +837,7 @@ static int ipc4_set_get_config_module_instance(struct ipc4_message_request *ipc4 } if (!set) - msg_reply.extension = config->extension.dat; + msg_reply->extension = config->extension.dat; return ret; } @@ -1026,7 +872,7 @@ __cold static void ipc4_prepare_for_kcontrol_get(struct comp_dev *dev, uint8_t p } } -__cold static int ipc4_get_vendor_config_module_instance(struct comp_dev *dev, +__cold static int ipc4_get_vendor_config_module_instance(struct comp_dev *dev, const struct comp_driver *drv, bool init_block, bool final_block, @@ -1137,7 +983,7 @@ __cold static int ipc4_get_large_config_module_instance(struct ipc4_message_requ if (ret < 0) return IPC4_FAILURE; - tr_dbg(&ipc_tr, "ipc4_get_large_config_module_instance %x : %x", + tr_dbg(&ipc_tr, "%x : %x", (uint32_t)config.primary.r.module_id, (uint32_t)config.primary.r.instance_id); /* get component dev for non-basefw since there is no @@ -1213,9 +1059,9 @@ __cold static int ipc4_get_large_config_module_instance(struct ipc4_message_requ if (ret) return ret; - msg_reply.extension = reply.extension.dat; - msg_reply.tx_size = data_offset; - msg_reply.tx_data = data; + msg_reply->extension = reply.extension.dat; + msg_reply->tx_size = data_offset; + msg_reply->tx_data = data; return ret; } @@ -1239,7 +1085,7 @@ __cold static int ipc4_set_vendor_config_module_instance(struct comp_dev *dev, * (4 bytes type | 4 bytes length=0 | no value) * we do not handle such case */ - if (data_off_size < sizeof(struct sof_tlv)) + if (data_off_size < sizeof(struct sof_tlv) || data_off_size > MAILBOX_HOSTBOX_SIZE) return IPC4_INVALID_CONFIG_DATA_STRUCT; /* ===Iterate over payload=== @@ -1251,10 +1097,21 @@ __cold static int ipc4_set_vendor_config_module_instance(struct comp_dev *dev, const uint8_t *end_offset = (const uint8_t *)data + data_off_size; while ((const uint8_t *)tlv < end_offset) { + size_t remaining = (size_t)(end_offset - (const uint8_t *)tlv); + /* check for invalid length */ if (!tlv->length) return IPC4_INVALID_CONFIG_DATA_LEN; + /* Validate TLV header + value fits within remaining + * payload to prevent OOB access and pointer wraparound + * on 32-bit arithmetic (CWE-190). Split into two checks + * to avoid overflow in the size_t addition itself. + */ + if (remaining < sizeof(struct sof_tlv) || + tlv->length > remaining - sizeof(struct sof_tlv)) + return IPC4_INVALID_REQUEST; + ret = drv->ops.set_large_config(dev, tlv->type, init_block, final_block, tlv->length, tlv->value); if (ret < 0) { @@ -1262,7 +1119,7 @@ __cold static int ipc4_set_vendor_config_module_instance(struct comp_dev *dev, (uint32_t)module_id, (uint32_t)instance_id); return IPC4_INVALID_RESOURCE_ID; } - /* Move pointer to the end of this tlv */ + /* Move pointer to the end of this tlv (aligned) */ tlv = (struct sof_tlv *)((const uint8_t *)tlv + sizeof(struct sof_tlv) + ALIGN_UP(tlv->length, 4)); } @@ -1299,7 +1156,7 @@ __cold static int ipc4_set_large_config_module_instance(struct ipc4_message_requ dcache_invalidate_region((__sparse_force void __sparse_cache *)MAILBOX_HOSTBOX_BASE, config.extension.r.data_off_size); - tr_dbg(&ipc_tr, "ipc4_set_large_config_module_instance %x : %x", + tr_dbg(&ipc_tr, "%x : %x", (uint32_t)config.primary.r.module_id, (uint32_t)config.primary.r.instance_id); if (config.primary.r.module_id) { @@ -1368,7 +1225,7 @@ __cold static int ipc4_delete_module_instance(struct ipc4_message_request *ipc4) if (ret < 0) return IPC4_FAILURE; - tr_dbg(&ipc_tr, "ipc4_delete_module_instance %x : %x", (uint32_t)module.primary.r.module_id, + tr_dbg(&ipc_tr, "%x : %x", (uint32_t)module.primary.r.module_id, (uint32_t)module.primary.r.instance_id); comp_id = IPC4_COMP_ID(module.primary.r.module_id, module.primary.r.instance_id); @@ -1383,145 +1240,8 @@ __cold static int ipc4_delete_module_instance(struct ipc4_message_request *ipc4) return ret; } -/* disable power gating on core 0 */ -__cold static int ipc4_module_process_d0ix(struct ipc4_message_request *ipc4) -{ - struct ipc4_module_set_d0ix d0ix; - uint32_t module_id, instance_id; - - assert_can_be_cold(); - - int ret = memcpy_s(&d0ix, sizeof(d0ix), ipc4, sizeof(*ipc4)); - - if (ret < 0) - return IPC4_FAILURE; - - module_id = d0ix.primary.r.module_id; - instance_id = d0ix.primary.r.instance_id; - - tr_dbg(&ipc_tr, "ipc4_module_process_d0ix %x : %x", module_id, instance_id); - - /* only module 0 can be used to set d0ix state */ - if (d0ix.primary.r.module_id || d0ix.primary.r.instance_id) { - ipc_cmd_err(&ipc_tr, "invalid resource id %x : %x", module_id, instance_id); - return IPC4_INVALID_RESOURCE_ID; - } - - if (d0ix.extension.r.prevent_power_gating) - pm_runtime_disable(PM_RUNTIME_DSP, PLATFORM_PRIMARY_CORE_ID); - else - pm_runtime_enable(PM_RUNTIME_DSP, PLATFORM_PRIMARY_CORE_ID); - - return 0; -} - -/* enable/disable cores according to the state mask */ -__cold static int ipc4_module_process_dx(struct ipc4_message_request *ipc4) -{ - struct ipc4_module_set_dx dx; - struct ipc4_dx_state_info dx_info; - uint32_t module_id, instance_id; - uint32_t core_id; - - assert_can_be_cold(); - - int ret = memcpy_s(&dx, sizeof(dx), ipc4, sizeof(*ipc4)); - - if (ret < 0) - return IPC4_FAILURE; - - module_id = dx.primary.r.module_id; - instance_id = dx.primary.r.instance_id; - - /* only module 0 can be used to set dx state */ - if (module_id || instance_id) { - ipc_cmd_err(&ipc_tr, "invalid resource id %x : %x", module_id, instance_id); - return IPC4_INVALID_RESOURCE_ID; - } - - dcache_invalidate_region((__sparse_force void __sparse_cache *)MAILBOX_HOSTBOX_BASE, - sizeof(dx_info)); - ret = memcpy_s(&dx_info, sizeof(dx_info), - (const void *)MAILBOX_HOSTBOX_BASE, sizeof(dx_info)); - if (ret < 0) - return IPC4_FAILURE; - - /* check if core enable mask is valid */ - if (dx_info.core_mask > MASK(CONFIG_CORE_COUNT - 1, 0)) { - ipc_cmd_err(&ipc_tr, "ipc4_module_process_dx: CONFIG_CORE_COUNT: %d < core enable mask: %d", - CONFIG_CORE_COUNT, dx_info.core_mask); - return IPC4_ERROR_INVALID_PARAM; - } - - /* check primary core first */ - if ((dx_info.core_mask & BIT(PLATFORM_PRIMARY_CORE_ID)) && - (dx_info.dx_mask & BIT(PLATFORM_PRIMARY_CORE_ID))) { - /* core0 can't be activated more, it's already active since we got here */ - ipc_cmd_err(&ipc_tr, "Core0 is already active"); - return IPC4_BAD_STATE; - } - - /* Activate/deactivate requested cores */ - for (core_id = 1; core_id < CONFIG_CORE_COUNT; core_id++) { - if ((dx_info.core_mask & BIT(core_id)) == 0) - continue; - - if (dx_info.dx_mask & BIT(core_id)) { - ret = cpu_enable_core(core_id); - if (ret != 0) { - ipc_cmd_err(&ipc_tr, "failed to enable core %d", core_id); - return IPC4_FAILURE; - } - } else { - cpu_disable_core(core_id); - if (cpu_is_core_enabled(core_id)) { - ipc_cmd_err(&ipc_tr, "failed to disable core %d", core_id); - return IPC4_FAILURE; - } - } - } - - /* Deactivating primary core if requested. */ - if (dx_info.core_mask & BIT(PLATFORM_PRIMARY_CORE_ID)) { - if (cpu_enabled_cores() & ~BIT(PLATFORM_PRIMARY_CORE_ID)) { - ipc_cmd_err(&ipc_tr, "secondary cores 0x%x still active", - cpu_enabled_cores()); - return IPC4_BUSY; - } - - if (is_any_ppl_active()) { - ipc_cmd_err(&ipc_tr, "some pipelines are still active"); - return IPC4_BUSY; - } - -#if !CONFIG_ADSP_IMR_CONTEXT_SAVE - ret = llext_manager_store_to_dram(); - if (ret < 0) - ipc_cmd_err(&ipc_tr, "Error %d saving LLEXT context. Resume might fail.", - ret); - -#if CONFIG_L3_HEAP - l3_heap_save(); -#endif -#endif - -#if defined(CONFIG_PM) - ipc_get()->task_mask |= IPC_TASK_POWERDOWN; -#endif - /* do platform specific suspending */ - platform_context_save(sof_get()); - -#if !defined(CONFIG_LIBRARY) && !defined(CONFIG_ZEPHYR_NATIVE_DRIVERS) - arch_irq_lock(); - platform_timer_stop(timer_get()); -#endif - ipc_get()->pm_prepare_D3 = 1; - } - - return IPC4_SUCCESS; -} - -__cold static int ipc4_process_module_message(struct ipc4_message_request *ipc4) +__cold int ipc4_user_process_module_message(struct ipc4_message_request *ipc4, + struct ipc_msg *reply) { uint32_t type; int ret; @@ -1529,6 +1249,7 @@ __cold static int ipc4_process_module_message(struct ipc4_message_request *ipc4) assert_can_be_cold(); type = ipc4->primary.r.type; + msg_reply = reply; switch (type) { case SOF_IPC4_MOD_INIT_INSTANCE: @@ -1555,12 +1276,6 @@ __cold static int ipc4_process_module_message(struct ipc4_message_request *ipc4) case SOF_IPC4_MOD_DELETE_INSTANCE: ret = ipc4_delete_module_instance(ipc4); break; - case SOF_IPC4_MOD_SET_D0IX: - ret = ipc4_module_process_d0ix(ipc4); - break; - case SOF_IPC4_MOD_SET_DX: - ret = ipc4_module_process_dx(ipc4); - break; case SOF_IPC4_MOD_ENTER_MODULE_RESTORE: case SOF_IPC4_MOD_EXIT_MODULE_RESTORE: ret = IPC4_UNAVAILABLE; @@ -1572,258 +1287,3 @@ __cold static int ipc4_process_module_message(struct ipc4_message_request *ipc4) return ret; } - -__cold struct ipc_cmd_hdr *mailbox_validate(void) -{ - struct ipc_cmd_hdr *hdr = ipc_get()->comp_data; - - assert_can_be_cold(); - - return hdr; -} - -struct ipc_cmd_hdr *ipc_compact_read_msg(void) -{ - int words; - - words = ipc_platform_compact_read_msg(&msg_data.msg_in, 2); - if (!words) - return mailbox_validate(); - - return &msg_data.msg_in; -} - -struct ipc_cmd_hdr *ipc_prepare_to_send(const struct ipc_msg *msg) -{ - msg_data.msg_out.pri = msg->header; - msg_data.msg_out.ext = msg->extension; - - if (msg->tx_size) - mailbox_dspbox_write(0, (uint32_t *)msg->tx_data, msg->tx_size); - - return &msg_data.msg_out; -} - -__cold void ipc_boot_complete_msg(struct ipc_cmd_hdr *header, uint32_t data) -{ - assert_can_be_cold(); - - header->pri = SOF_IPC4_FW_READY; - header->ext = 0; -} - -#if defined(CONFIG_PM_DEVICE) && defined(CONFIG_INTEL_ADSP_IPC) -__cold void ipc_send_failed_power_transition_response(void) -{ - struct ipc4_message_request *request = ipc_from_hdr(&msg_data.msg_in); - struct ipc4_message_reply response; - - assert_can_be_cold(); - - response.primary.r.status = IPC4_POWER_TRANSITION_FAILED; - response.primary.r.rsp = SOF_IPC4_MESSAGE_DIR_MSG_REPLY; - response.primary.r.msg_tgt = request->primary.r.msg_tgt; - response.primary.r.type = request->primary.r.type; - - msg_reply.header = response.primary.dat; - list_init(&msg_reply.list); - - ipc_msg_send_direct(&msg_reply, NULL); -} -#endif /* defined(CONFIG_PM_DEVICE) && defined(CONFIG_INTEL_ADSP_IPC) */ - -__cold void ipc_send_panic_notification(void) -{ - assert_can_be_cold(); - - msg_notify.header = SOF_IPC4_NOTIF_HEADER(SOF_IPC4_EXCEPTION_CAUGHT); - msg_notify.extension = cpu_get_id(); - msg_notify.tx_size = 0; - msg_notify.tx_data = NULL; - list_init(&msg_notify.list); - - ipc_msg_send_direct(&msg_notify, NULL); -} - -#ifdef CONFIG_LOG_BACKEND_ADSP_MTRACE - -static bool is_notification_queued(struct ipc_msg *msg) -{ - struct ipc *ipc = ipc_get(); - k_spinlock_key_t key; - bool queued = false; - - key = k_spin_lock(&ipc->lock); - if (!list_is_empty(&msg->list)) - queued = true; - k_spin_unlock(&ipc->lock, key); - - return queued; -} - -/* Called from ipc_send_buffer_status_notify(), which is currently "hot" */ -void ipc_send_buffer_status_notify(void) -{ - /* a single msg_notify object is used */ - if (is_notification_queued(&msg_notify)) - return; - - msg_notify.header = SOF_IPC4_NOTIF_HEADER(SOF_IPC4_NOTIFY_LOG_BUFFER_STATUS); - msg_notify.extension = 0; - msg_notify.tx_size = 0; - - tr_dbg(&ipc_tr, "tx-notify\t: %#x|%#x", msg_notify.header, msg_notify.extension); - - ipc_msg_send(&msg_notify, NULL, true); -} -#endif - -void ipc_msg_reply(struct sof_ipc_reply *reply) -{ - struct ipc4_message_request in; - - in.primary.dat = msg_data.msg_in.pri; - ipc_compound_msg_done(in.primary.r.type, reply->error); -} - -void ipc_cmd(struct ipc_cmd_hdr *_hdr) -{ - struct ipc4_message_request *in = ipc4_get_message_request(); - enum ipc4_message_target target; -#ifdef CONFIG_DEBUG_IPC_TIMINGS - struct ipc4_message_request req; - uint64_t tstamp; -#endif - int err; - -#ifdef CONFIG_DEBUG_IPC_TIMINGS - req = *in; - tstamp = sof_cycle_get_64(); -#else - if (cpu_is_primary(cpu_get_id())) - tr_info(&ipc_tr, "rx\t: %#x|%#x", in->primary.dat, in->extension.dat); -#endif - /* no process on scheduled thread */ - atomic_set(&msg_data.delayed_reply, 0); - msg_data.delayed_error = 0; - msg_reply.tx_data = NULL; - msg_reply.tx_size = 0; - msg_reply.header = in->primary.dat; - msg_reply.extension = in->extension.dat; - - target = in->primary.r.msg_tgt; - - switch (target) { - case SOF_IPC4_MESSAGE_TARGET_FW_GEN_MSG: - err = ipc4_process_glb_message(in); - if (err) - ipc_cmd_err(&ipc_tr, "ipc4: FW_GEN_MSG failed with err %d", err); - break; - case SOF_IPC4_MESSAGE_TARGET_MODULE_MSG: - err = ipc4_process_module_message(in); - if (err) - ipc_cmd_err(&ipc_tr, "ipc4: MODULE_MSG failed with err %d", err); - break; - default: - /* should not reach here as we only have 2 message types */ - ipc_cmd_err(&ipc_tr, "ipc4: invalid target %d", target); - err = IPC4_UNKNOWN_MESSAGE_TYPE; - } - - /* FW sends an ipc message to host if request bit is clear */ - if (in->primary.r.rsp == SOF_IPC4_MESSAGE_DIR_MSG_REQUEST) { - struct ipc *ipc = ipc_get(); - struct ipc4_message_reply reply = {{0}}; - - /* Process flow and time stamp for IPC4 msg processed on secondary core : - * core 0 (primary core) core x (secondary core) - * # IPC msg thread #IPC delayed worker #core x idc thread - * ipc_task_ops.run() - * ipc_do_cmd() - * msg_reply.header = in->primary.dat - * ipc4_process_on_core(x) - * mask |= SECONDARY_CORE - * idc_send_message() - * Case 1: - * // Ipc msg processed by secondary core idc_ipc() - * if ((mask & SECONDARY_CORE)) ipc_cmd() - * return; ipc_msg_send() - * mask &= ~SECONDARY_CORE - * - * ipc_platform_send_msg - * ---------------------------------------------------------------------------- - * Case 2: - * idc_ipc() - * ipc_cmd() - * //Prepare reply msg - * msg_reply.header = - * reply.primary.dat; - * ipc_msg_send() - * mask &= ~SECONDARY_CORE - * - * if ((mask & IPC_TASK_SECONDARY_CORE)) - * return; - * // Ipc reply msg was prepared, so return - * if (msg_reply.header != in->primary.dat) - * return; - * ipc_platform_send_msg - * ---------------------------------------------------------------------------- - * Case 3: - * idc_ipc() - * ipc_cmd() - * //Prepare reply msg - * msg_reply.header = - * reply.primary.dat; - * ipc_msg_send() - * mask &= ~SECONDARY_CORE - * - * ipc_platform_send_msg - * - * if ((mask & IPC_TASK_SECONDARY_CORE)) - * return; - * // Ipc reply msg was prepared, so return - * if (msg_reply.header != in->primary.dat) - * return; - */ - - /* Reply prepared by secondary core */ - if ((ipc->task_mask & IPC_TASK_SECONDARY_CORE) && cpu_is_primary(cpu_get_id())) - return; - /* Reply has been prepared by secondary core */ - if (msg_reply.header != in->primary.dat) - return; - - /* Do not send reply for SET_DX if we are going to enter D3 - * The reply is going to be sent as part of the power down - * sequence - */ - if (ipc->task_mask & IPC_TASK_POWERDOWN) - return; - - if (ipc_wait_for_compound_msg() != 0) { - ipc_cmd_err(&ipc_tr, "ipc4: failed to send delayed reply"); - err = IPC4_FAILURE; - } - - /* copy contents of message received */ - reply.primary.r.rsp = SOF_IPC4_MESSAGE_DIR_MSG_REPLY; - reply.primary.r.msg_tgt = in->primary.r.msg_tgt; - reply.primary.r.type = in->primary.r.type; - if (msg_data.delayed_error) - reply.primary.r.status = msg_data.delayed_error; - else - reply.primary.r.status = err; - - msg_reply.header = reply.primary.dat; - -#ifdef CONFIG_DEBUG_IPC_TIMINGS - tr_info(&ipc_tr, "tx-reply\t: %#x|%#x to %#x|%#x in %llu us", msg_reply.header, - msg_reply.extension, req.primary.dat, req.extension.dat, - k_cyc_to_us_near64(sof_cycle_get_64() - tstamp)); -#else - tr_dbg(&ipc_tr, "tx-reply\t: %#x|%#x", msg_reply.header, - msg_reply.extension); -#endif - ipc4_send_reply(&reply); - } -} diff --git a/src/ipc/ipc4/helper.c b/src/ipc/ipc4/helper.c index 511917ceb768..892e704eb2e4 100644 --- a/src/ipc/ipc4/helper.c +++ b/src/ipc/ipc4/helper.c @@ -21,8 +21,10 @@ #include <sof/ipc/msg.h> #include <sof/lib/mailbox.h> #include <sof/lib/memory.h> +#include <sof/lib/vregion.h> #include <sof/list.h> #include <sof/platform.h> +#include <sof/schedule/dp_schedule.h> #include <sof/schedule/ll_schedule_domain.h> #include <rtos/symbol.h> #include <rtos/wait.h> @@ -138,12 +140,19 @@ __cold struct comp_dev *comp_new_ipc4(struct ipc4_module_init_instance *module_i ipc_config.core = module_init->extension.r.core_id; ipc_config.ipc_config_size = module_init->extension.r.param_block_size * sizeof(uint32_t); ipc_config.ipc_extended_init = module_init->extension.r.extended_init; - - dcache_invalidate_region((__sparse_force void __sparse_cache *)MAILBOX_HOSTBOX_BASE, - MAILBOX_HOSTBOX_SIZE); - + if (ipc_config.ipc_config_size > MAILBOX_HOSTBOX_SIZE) { + tr_err(&ipc_tr, "IPC payload size %u too big for the message window", + ipc_config.ipc_config_size); + return NULL; + } +#ifdef CONFIG_DCACHE_LINE_SIZE + if (!IS_ENABLED(CONFIG_LIBRARY)) + sys_cache_data_invd_range((__sparse_force void __sparse_cache *) + MAILBOX_HOSTBOX_BASE, + ALIGN_UP(ipc_config.ipc_config_size, + CONFIG_DCACHE_LINE_SIZE)); +#endif data = ipc4_get_comp_new_data(); - #if CONFIG_LIBRARY ipc_config.ipc_config_size -= sizeof(struct sof_uuid); drv = ipc4_library_get_comp_drv(data + ipc_config.ipc_config_size); @@ -229,7 +238,105 @@ struct ipc_comp_dev *ipc_get_comp_by_ppl_id(struct ipc *ipc, uint16_t type, return NULL; } -__cold static int ipc4_create_pipeline(struct ipc4_pipeline_create *pipe_desc) +/* + * This function currently only decodes the payload and prints out + * data it finds, but it does not store it anywhere. + */ +__cold static int ipc4_create_pipeline_payload_decode(char *data, + struct create_pipeline_params *pparams) +{ + const struct ipc4_pipeline_ext_payload *hdr = + (struct ipc4_pipeline_ext_payload *)data; + const struct ipc4_pipeline_ext_object *obj; +#ifdef CONFIG_DCACHE_LINE_SIZE + size_t cache_line_size = CONFIG_DCACHE_LINE_SIZE; + size_t hdr_cache_size = ALIGN_UP(sizeof(*hdr), cache_line_size); +#endif + bool last_object; + size_t size; + +#ifdef CONFIG_DCACHE_LINE_SIZE + if (!IS_ENABLED(CONFIG_LIBRARY)) + sys_cache_data_invd_range((__sparse_force void __sparse_cache *)data, + hdr_cache_size); +#endif + size = hdr->payload_words * sizeof(uint32_t); + last_object = !hdr->data_obj_array; + + if (size < sizeof(*hdr)) { + tr_err(&ipc_tr, "Payload size too small: %u : %#x", hdr->payload_words, + *((uint32_t *)hdr)); + return -EINVAL; + } + if (size > MAILBOX_HOSTBOX_SIZE) { + tr_err(&ipc_tr, "Payload size too large: %u : %#x", hdr->payload_words, + *((uint32_t *)hdr)); + } + tr_info(&ipc_tr, "payload size %u array %u: %#x", hdr->payload_words, hdr->data_obj_array, + *((uint32_t *)hdr)); + +#ifdef CONFIG_DCACHE_LINE_SIZE + if (!IS_ENABLED(CONFIG_LIBRARY) && ALIGN_UP(size, cache_line_size) > hdr_cache_size) + sys_cache_data_invd_range((__sparse_force void __sparse_cache *) + ((char *)data + hdr_cache_size), + ALIGN_UP(size, cache_line_size) - hdr_cache_size); +#endif + + obj = (const struct ipc4_pipeline_ext_object *)(hdr + 1); + while (!last_object) { + const struct ipc4_pipeline_ext_object *next_obj; + + /* Check if there is space for the object header */ + if ((char *)(obj + 1) - data > size) { + tr_err(&ipc_tr, "obj header overflow, %u > %zu", + (char *)(obj + 1) - data, size); + return -EINVAL; + } + + /* Calculate would be next object position and check if current object fits */ + next_obj = (const struct ipc4_pipeline_ext_object *) + (((const uint32_t *)(obj + 1)) + obj->object_words); + if ((char *)next_obj - data > size) { + tr_err(&ipc_tr, "object size overflow, %u > %zu", + (char *)next_obj - data, size); + return -EINVAL; + } + + switch (obj->object_id) { + case IPC4_GLB_PIPE_EXT_OBJ_ID_MEM_DATA: + { + /* Get mem_data struct that follows the obj struct */ + const struct ipc4_pipeline_ext_obj_mem_data *mem_data = + (const struct ipc4_pipeline_ext_obj_mem_data *)(obj + 1); + + if (obj->object_words * sizeof(uint32_t) < sizeof(*mem_data)) { + tr_err(&ipc_tr, "mem_data object does not fit %zu < %zu", + obj->object_words * sizeof(uint32_t), sizeof(*mem_data)); + break; + } + pparams->mem_data = mem_data; + tr_info(&ipc_tr, + "init_ext_obj_mem_data domain %u stack %u interim %u lifetime %u shared %u", + mem_data->domain_id, mem_data->stack_bytes, + mem_data->interim_heap_bytes, mem_data->lifetime_heap_bytes, + mem_data->shared_bytes); + break; + } + default: + tr_warn(&ipc_tr, "Unknown ext init object id %u of %u words", + obj->object_id, obj->object_words); + } + /* Read the last object flag from obj header */ + last_object = obj->last_object; + /* Move to next object */ + obj = next_obj; + } + + return 0; +} + +__cold static int ipc4_create_pipeline(struct ipc4_pipeline_create *pipe_desc, + struct create_pipeline_params *pparams) { struct ipc_comp_dev *ipc_pipe; struct pipeline *pipe; @@ -246,7 +353,8 @@ __cold static int ipc4_create_pipeline(struct ipc4_pipeline_create *pipe_desc) } /* create the pipeline */ - pipe = pipeline_new(pipe_desc->primary.r.instance_id, pipe_desc->primary.r.ppl_priority, 0); + pipe = pipeline_new(NULL, pipe_desc->primary.r.instance_id, + pipe_desc->primary.r.ppl_priority, 0, pparams); if (!pipe) { tr_err(&ipc_tr, "ipc: pipeline_new() failed"); return IPC4_OUT_OF_MEMORY; @@ -280,11 +388,29 @@ __cold static int ipc4_create_pipeline(struct ipc4_pipeline_create *pipe_desc) return IPC4_SUCCESS; } +#if CONFIG_LIBRARY +static inline char *ipc4_get_pipe_create_data(void) +{ + struct ipc *ipc = ipc_get(); + char *data = (char *)ipc->comp_data + sizeof(struct ipc4_pipeline_create); + + return data; +} +#else +__cold static inline char *ipc4_get_pipe_create_data(void) +{ + assert_can_be_cold(); + + return (char *)MAILBOX_HOSTBOX_BASE; +} +#endif + /* Only called from ipc4_new_pipeline(), which is __cold */ __cold int ipc_pipeline_new(struct ipc *ipc, ipc_pipe_new *_pipe_desc) { struct ipc4_pipeline_create *pipe_desc = ipc_from_pipe_new(_pipe_desc); - + struct create_pipeline_params pparams = { 0 }; + bool valid_pparams = false; assert_can_be_cold(); tr_dbg(&ipc_tr, "ipc: pipeline id = %u", (uint32_t)pipe_desc->primary.r.instance_id); @@ -293,7 +419,18 @@ __cold int ipc_pipeline_new(struct ipc *ipc, ipc_pipe_new *_pipe_desc) if (!cpu_is_me(pipe_desc->extension.r.core_id)) return ipc4_process_on_core(pipe_desc->extension.r.core_id, false); - return ipc4_create_pipeline(pipe_desc); + if (pipe_desc->extension.r.payload) { + char *data; + int ret; + + data = ipc4_get_pipe_create_data(); + + ret = ipc4_create_pipeline_payload_decode(data, &pparams); + if (ret == 0) + valid_pparams = true; + } + + return ipc4_create_pipeline(pipe_desc, valid_pparams ? &pparams : NULL); } __cold static inline int ipc_comp_free_remote(struct comp_dev *dev) @@ -321,6 +458,7 @@ __cold static int ipc_pipeline_module_free(uint32_t pipeline_id) /* free sink buffer allocated by current component in bind function */ comp_dev_for_each_consumer_safe(icd->cd, buffer, safe) { + pipeline_disconnect(icd->cd, buffer, PPL_CONN_DIR_COMP_TO_BUFFER); struct comp_dev *sink = comp_buffer_get_sink_component(buffer); /* free the buffer only when the sink module has also been disconnected */ @@ -390,8 +528,8 @@ __cold int ipc_pipeline_free(struct ipc *ipc, uint32_t comp_id) } __cold static struct comp_buffer *ipc4_create_buffer(struct comp_dev *src, bool is_shared, - uint32_t buf_size, uint32_t src_queue, - uint32_t dst_queue) + uint32_t buf_size, uint32_t src_queue, + uint32_t dst_queue, struct mod_alloc_ctx *alloc) { struct sof_ipc_buffer ipc_buf; @@ -402,7 +540,7 @@ __cold static struct comp_buffer *ipc4_create_buffer(struct comp_dev *src, bool ipc_buf.comp.id = IPC4_COMP_ID(src_queue, dst_queue); ipc_buf.comp.pipeline_id = src->ipc_config.pipeline_id; ipc_buf.comp.core = cpu_get_id(); - return buffer_new(&ipc_buf, is_shared); + return buffer_new(alloc, &ipc_buf, is_shared); } #if CONFIG_CROSS_CORE_STREAM @@ -503,6 +641,8 @@ __cold int ipc_comp_connect(struct ipc *ipc, ipc_pipe_comp_connect *_connect) return IPC4_INVALID_RESOURCE_ID; } + struct mod_alloc_ctx *alloc; + #if CONFIG_ZEPHYR_DP_SCHEDULER if (source->ipc_config.proc_domain == COMP_PROCESSING_DOMAIN_DP && sink->ipc_config.proc_domain == COMP_PROCESSING_DOMAIN_DP) { @@ -510,6 +650,19 @@ __cold int ipc_comp_connect(struct ipc *ipc, ipc_pipe_comp_connect *_connect) src_id, sink_id); return IPC4_INVALID_REQUEST; } + + struct comp_dev *dp; + + if (sink->ipc_config.proc_domain == COMP_PROCESSING_DOMAIN_DP) + dp = sink; + else if (source->ipc_config.proc_domain == COMP_PROCESSING_DOMAIN_DP) + dp = source; + else + dp = NULL; + + alloc = dp && dp->mod ? dp->mod->priv.resources.alloc : NULL; +#else + alloc = NULL; #endif /* CONFIG_ZEPHYR_DP_SCHEDULER */ bool cross_core_bind = source->ipc_config.core != sink->ipc_config.core; @@ -579,12 +732,17 @@ __cold int ipc_comp_connect(struct ipc *ipc, ipc_pipe_comp_connect *_connect) buf_size = ibs * 2; buffer = ipc4_create_buffer(source, cross_core_bind, buf_size, bu->extension.r.src_queue, - bu->extension.r.dst_queue); + bu->extension.r.dst_queue, alloc); if (!buffer) { - tr_err(&ipc_tr, "failed to allocate buffer to bind %d to %d", src_id, sink_id); + tr_err(&ipc_tr, "failed to allocate buffer to bind %#x to %#x", src_id, sink_id); return IPC4_OUT_OF_MEMORY; } +#if CONFIG_ZEPHYR_DP_SCHEDULER + if (alloc) + vregion_get(alloc->vreg); +#endif + /* * set min_free_space and min_available in sink/src api of created buffer. * buffer is connected like: @@ -603,20 +761,30 @@ __cold int ipc_comp_connect(struct ipc *ipc, ipc_pipe_comp_connect *_connect) if (sink->ipc_config.proc_domain == COMP_PROCESSING_DOMAIN_DP || source->ipc_config.proc_domain == COMP_PROCESSING_DOMAIN_DP) { - bool dp_on_source = source->ipc_config.proc_domain == COMP_PROCESSING_DOMAIN_DP; - struct sof_source *src = audio_buffer_get_source(&buffer->audio_buffer); - struct sof_sink *snk = audio_buffer_get_sink(&buffer->audio_buffer); - - ring_buffer = ring_buffer_create(dp_on_source ? source : sink, - source_get_min_available(src), - sink_get_min_free_space(snk), + struct processing_module *srcmod = comp_mod(source); + struct module_data *src_module_data = &srcmod->priv; + struct processing_module *dstmod = comp_mod(sink); + struct module_data *dst_module_data = &dstmod->priv; + + /* + * Handle cases where the size of the ring buffer depends on the + * in_buff_size/out_buff_size advertised by the module. E.g. in the case of the + * compressed decoder module, the in/out_buff_size is determined during module + * init based on the decoder implementation. Also note that the size passed here + * is only for the ring buffer. The size of intermediate buffer created above is + * unchanged. + */ + ring_buffer = ring_buffer_create(dp, MAX(ibs, dst_module_data->mpd.in_buff_size), + MAX(obs, src_module_data->mpd.out_buff_size), audio_buffer_is_shared(&buffer->audio_buffer), buf_get_id(buffer)); - if (!ring_buffer) - goto free; + if (!ring_buffer) { + buffer_free(buffer); + return IPC4_OUT_OF_MEMORY; + } /* data destination module needs to use ring_buffer */ - audio_buffer_attach_secondary_buffer(&buffer->audio_buffer, dp_on_source, + audio_buffer_attach_secondary_buffer(&buffer->audio_buffer, dp == source, &ring_buffer->audio_buffer); } @@ -646,14 +814,14 @@ __cold int ipc_comp_connect(struct ipc *ipc, ipc_pipe_comp_connect *_connect) ret = comp_buffer_connect(source, source->ipc_config.core, buffer, PPL_CONN_DIR_COMP_TO_BUFFER); if (ret < 0) { - tr_err(&ipc_tr, "failed to connect src %d to internal buffer", src_id); + tr_err(&ipc_tr, "failed to connect src %#x to internal buffer", src_id); goto free; } ret = comp_buffer_connect(sink, sink->ipc_config.core, buffer, PPL_CONN_DIR_BUFFER_TO_COMP); if (ret < 0) { - tr_err(&ipc_tr, "failed to connect internal buffer to sink %d", sink_id); + tr_err(&ipc_tr, "failed to connect internal buffer to sink %#x", sink_id); goto e_sink_connect; } @@ -956,9 +1124,8 @@ int ipc4_process_on_core(uint32_t core, bool blocking) return IPC4_SUCCESS; } -__cold static const struct comp_driver *ipc4_get_drv(const void *uuid) +__cold static const struct comp_driver *ipc4_search_for_drv(const void *uuid) { - const struct sof_uuid *const __maybe_unused sof_uuid = (const struct sof_uuid *)uuid; struct comp_driver_list *drivers = comp_drivers_get(); struct list_item *clist; const struct comp_driver *drv = NULL; @@ -979,21 +1146,32 @@ __cold static const struct comp_driver *ipc4_get_drv(const void *uuid) info->drv->type, info->drv->tctx->uuid_p); drv = info->drv; - goto out; + break; } } - tr_warn(&comp_tr, - "the provided UUID (%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x) can't be found!", - sof_uuid->a, sof_uuid->b, sof_uuid->c, sof_uuid->d[0], sof_uuid->d[1], - sof_uuid->d[2], sof_uuid->d[3], sof_uuid->d[4], sof_uuid->d[5], sof_uuid->d[6], - sof_uuid->d[7]); - -out: irq_local_enable(flags); return drv; } +__cold static const struct comp_driver *ipc4_get_drv(const void *uuid) +{ + const struct sof_uuid *const __maybe_unused sof_uuid = (const struct sof_uuid *)uuid; + const struct comp_driver *drv; + + assert_can_be_cold(); + + drv = ipc4_search_for_drv(uuid); + if (!drv) + tr_err(&comp_tr, + "the provided UUID (%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x) can't be found!", + sof_uuid->a, sof_uuid->b, sof_uuid->c, sof_uuid->d[0], sof_uuid->d[1], + sof_uuid->d[2], sof_uuid->d[3], sof_uuid->d[4], sof_uuid->d[5], + sof_uuid->d[6], sof_uuid->d[7]); + + return drv; +} + /* * Called from * - ipc4_get_large_config_module_instance() @@ -1004,7 +1182,6 @@ __cold static const struct comp_driver *ipc4_get_drv(const void *uuid) __cold const struct comp_driver *ipc4_get_comp_drv(uint32_t module_id) { const struct sof_man_fw_desc *desc = NULL; - const struct comp_driver *drv; const struct sof_man_module *mod; uint32_t entry_index; @@ -1043,7 +1220,7 @@ __cold const struct comp_driver *ipc4_get_comp_drv(uint32_t module_id) mod = lib_manager_get_module_manifest(module_id); if (!mod) { - tr_err(&comp_tr, "Error: Couldn't find loadable module with id %d.", + tr_err(&comp_tr, "Error: Couldn't find loadable module with id %#x.", module_id); return NULL; } @@ -1053,18 +1230,18 @@ __cold const struct comp_driver *ipc4_get_comp_drv(uint32_t module_id) return NULL; #endif } - /* Check already registered components */ - drv = ipc4_get_drv(&mod->uuid); #if CONFIG_LIBRARY_MANAGER - if (!drv) { - /* New module not registered yet. */ - lib_manager_register_module(module_id); - drv = ipc4_get_drv(&mod->uuid); - } -#endif + /* Check already registered components */ + const struct comp_driver *drv = ipc4_search_for_drv(&mod->uuid); - return drv; + if (drv) + return drv; + + /* New module not registered yet. */ + lib_manager_register_module(module_id); +#endif + return ipc4_get_drv(&mod->uuid); } struct comp_dev *ipc4_get_comp_dev(uint32_t comp_id) @@ -1094,7 +1271,6 @@ __cold static int ipc4_add_comp_dev(struct comp_dev *dev) sizeof(struct ipc_comp_dev)); if (!icd) { tr_err(&ipc_tr, "alloc failed"); - rfree(icd); return IPC4_OUT_OF_MEMORY; } @@ -1103,7 +1279,7 @@ __cold static int ipc4_add_comp_dev(struct comp_dev *dev) icd->core = dev->ipc_config.core; icd->id = dev->ipc_config.id; - tr_dbg(&ipc_tr, "ipc4_add_comp_dev add comp 0x%x", icd->id); + tr_dbg(&ipc_tr, "add comp 0x%x", icd->id); /* add new component to the list */ list_item_append(&icd->list, &ipc->comp_list); @@ -1130,7 +1306,7 @@ int ipc4_find_dma_config_multiple(struct ipc_config_dai *dai, uint8_t *data_buff struct ipc_dma_config *dma_cfg; struct sof_tlv *tlvs; - for (tlvs = (struct sof_tlv *)data_buffer; (uint32_t)tlvs < end_addr; + for (tlvs = (struct sof_tlv *)data_buffer; tlvs && (uint32_t)tlvs < end_addr; tlvs = tlv_next(tlvs)) { dma_cfg = tlv_value_ptr_get(tlvs, GTW_DMA_CONFIG_ID); if (!dma_cfg) @@ -1152,28 +1328,36 @@ int ipc4_find_dma_config_multiple(struct ipc_config_dai *dai, uint8_t *data_buff return IPC4_INVALID_REQUEST; } -void ipc4_base_module_cfg_to_stream_params(const struct ipc4_base_module_cfg *base_cfg, - struct sof_ipc_stream_params *params) +void ipc4_audio_format_to_stream_params(const struct ipc4_audio_format *audio_fmt, + struct sof_ipc_stream_params *params) { enum sof_ipc_frame frame_fmt, valid_fmt; int i; memset(params, 0, sizeof(struct sof_ipc_stream_params)); - params->channels = base_cfg->audio_fmt.channels_count; - params->rate = base_cfg->audio_fmt.sampling_frequency; - params->sample_container_bytes = base_cfg->audio_fmt.depth / 8; - params->sample_valid_bytes = base_cfg->audio_fmt.valid_bit_depth / 8; - params->buffer_fmt = base_cfg->audio_fmt.interleaving_style; - params->buffer.size = base_cfg->obs * 2; - - audio_stream_fmt_conversion(base_cfg->audio_fmt.depth, - base_cfg->audio_fmt.valid_bit_depth, + params->channels = audio_fmt->channels_count; + params->rate = audio_fmt->sampling_frequency; + params->sample_container_bytes = audio_fmt->depth / 8; + params->sample_valid_bytes = audio_fmt->valid_bit_depth / 8; + params->buffer_fmt = audio_fmt->interleaving_style; + + audio_stream_fmt_conversion(audio_fmt->depth, + audio_fmt->valid_bit_depth, &frame_fmt, &valid_fmt, - base_cfg->audio_fmt.s_type); + audio_fmt->s_type); params->frame_fmt = frame_fmt; for (i = 0; i < SOF_IPC_MAX_CHANNELS; i++) - params->chmap[i] = (base_cfg->audio_fmt.ch_map >> i * 4) & 0xf; + params->chmap[i] = (audio_fmt->ch_map >> i * 4) & 0xf; +} + +void ipc4_base_module_cfg_to_stream_params(const struct ipc4_base_module_cfg *base_cfg, + struct sof_ipc_stream_params *params) +{ + memset(params, 0, sizeof(struct sof_ipc_stream_params)); + params->buffer.size = base_cfg->obs * 2; + + ipc4_audio_format_to_stream_params(&base_cfg->audio_fmt, params); } EXPORT_SYMBOL(ipc4_base_module_cfg_to_stream_params); diff --git a/src/ipc/ipc4/logging.c b/src/ipc/ipc4/logging.c index 2e0f6fd5088e..b5361ff5944e 100644 --- a/src/ipc/ipc4/logging.c +++ b/src/ipc/ipc4/logging.c @@ -9,6 +9,7 @@ #endif #include <stdint.h> +#include <sof/boot_test.h> #include <sof/lib/uuid.h> #include <sof/ipc/common.h> #include <ipc4/base_fw.h> @@ -60,7 +61,7 @@ static uint32_t mtrace_aging_timer = IPC4_MTRACE_NOTIFY_AGING_TIMER_MS; #define MTRACE_IPC_CORE PLATFORM_PRIMARY_CORE_ID -static struct k_mutex log_mutex; +static K_MUTEX_DEFINE(log_mutex); static struct k_work_delayable log_work; static void mtrace_log_hook_unlocked(size_t written, size_t space_left) @@ -139,12 +140,53 @@ int ipc4_logging_enable_logs(bool first_block, log_state = (const struct ipc4_log_state_info *)data; if (log_state->enable) { - adsp_mtrace_log_init(mtrace_log_hook); - - k_mutex_init(&log_mutex); - k_work_init_delayable(&log_work, log_work_handler); + uint32_t log_level = LOG_LEVEL_NONE; /* Default if no bits set */ + uint32_t mask = log_state->logs_mask[0]; + + /* TODO: Improve mask handling for better code maintainability + * The logs_mask bits should be defined using proper macros or a struct + * to improve readability and maintainability. Current hardcoded bit + * positions are sufficient for now but should be refactored in the future. + * Possible improvements: + * - Define IPC4_LOG_MASK_* macros for each bit position + * - Create a struct with bitfields for each priority level + * - Add proper documentation in IPC4 specification headers + */ + + /* Determine log level from mask bits 0-4 (priority levels) + * bit 0: critical & error -> LOG_LEVEL_ERR + * bit 1: high & warning -> LOG_LEVEL_WRN + * bit 2: medium -> LOG_LEVEL_INF + * bit 3: low & info -> LOG_LEVEL_INF + * bit 4: verbose & debug -> LOG_LEVEL_DBG + * Check highest bit set to determine maximum log level + */ + if (mask & BIT(4)) + log_level = LOG_LEVEL_DBG; + else if (mask & (BIT(3) | BIT(2))) + log_level = LOG_LEVEL_INF; + else if (mask & BIT(1)) + log_level = LOG_LEVEL_WRN; + else if (mask & BIT(0)) + log_level = LOG_LEVEL_ERR; - log_backend_enable(log_backend, mtrace_log_hook, CONFIG_SOF_LOG_LEVEL); + adsp_mtrace_log_init(mtrace_log_hook); + /* Initialize work queue if not already initialized */ + if (!log_work.work.handler) + k_work_init_delayable(&log_work, log_work_handler); + + /* Enable backend with determined log level + * + * Note: If CONFIG_LOG_RUNTIME_FILTERING is not enabled, the log_level + * parameter has no effect - all logs are filtered at compile-time only. + * + * Note: Setting log_level to LOG_LEVEL_NONE will result in no logs being + * output, as all runtime filters will be set to NONE. This behavior will + * be useful in the future when per-source filtering can be specified via + * IPC, allowing selective enabling of specific log sources while keeping + * others disabled. + */ + log_backend_enable(log_backend, mtrace_log_hook, log_level); mtrace_aging_timer = log_state->aging_timer_period; if (mtrace_aging_timer < IPC4_MTRACE_AGING_TIMER_MIN_MS) { @@ -152,10 +194,20 @@ int ipc4_logging_enable_logs(bool first_block, LOG_WRN("Too small aging timer value, limiting to %u\n", mtrace_aging_timer); } + + /* Logs enabled, this is the best place to run boot-tests */ + TEST_RUN_ONCE(sof_run_boot_tests); } else { - k_work_flush_delayable(&log_work, &ipc4_log_work_sync); + /* Flush work queue if initialized */ + if (log_work.work.handler) { + k_work_flush_delayable(&log_work, &ipc4_log_work_sync); + log_work.work.handler = NULL; + } + adsp_mtrace_log_init(NULL); - log_backend_disable(log_backend); + /* Disable backend if currently active */ + if (log_backend_is_active(log_backend)) + log_backend_disable(log_backend); } return 0; diff --git a/src/ipc/ipc4/notification.c b/src/ipc/ipc4/notification.c index f2a15b42f926..9fd566ee1f93 100644 --- a/src/ipc/ipc4/notification.c +++ b/src/ipc/ipc4/notification.c @@ -10,12 +10,46 @@ #include <sof/ipc/msg.h> #include <stdbool.h> #include <ipc4/notification.h> +#include <sof/ipc/notification_pool.h> #include <rtos/symbol.h> -static void resource_notif_header_init(struct ipc_msg *msg) +static uint32_t notification_mask = 0xFFFFFFFF; + +static bool is_notif_filtered_out(uint32_t event_type) +{ + uint32_t notif_idx; + + switch (event_type) { + case SOF_IPC4_GATEWAY_UNDERRUN_DETECTED: + notif_idx = IPC4_UNDERRUN_AT_GATEWAY_NOTIFICATION_MASK_IDX; + break; + case SOF_IPC4_MIXER_UNDERRUN_DETECTED: + notif_idx = IPC4_UNDERRUN_AT_MIXER_NOTIFICATION_MASK_IDX; + break; + case SOF_IPC4_GATEWAY_OVERRUN_DETECTED: + notif_idx = IPC4_OVERRUN_AT_GATEWAY_NOTIFICATION_MASK_IDX; + break; + default: + return false; + } + + return (notification_mask & BIT(notif_idx)) == 0; +} + +static bool send_resource_notif(uint32_t resource_id, uint32_t event_type, uint32_t resource_type, + void *data, uint32_t data_size) { - struct ipc4_resource_event_data_notification *notif_data = msg->tx_data; + struct ipc_msg *msg; + + if (is_notif_filtered_out(event_type)) + return true; //silently ignore + + msg = ipc_notification_pool_get(IPC4_RESOURCE_EVENT_SIZE); + if (!msg) + return false; + + struct ipc4_resource_event_data_notification *notif = msg->tx_data; union ipc4_notification_header header; header.r.notif_type = SOF_IPC4_NOTIFY_RESOURCE_EVENT; @@ -23,72 +57,72 @@ static void resource_notif_header_init(struct ipc_msg *msg) header.r.rsp = SOF_IPC4_MESSAGE_DIR_MSG_REQUEST; header.r.msg_tgt = SOF_IPC4_MESSAGE_TARGET_FW_GEN_MSG; msg->header = header.dat; - memset(¬if_data->event_data, 0, sizeof(notif_data->event_data)); + + notif->resource_id = resource_id; + notif->event_type = event_type; + notif->resource_type = resource_type; + memset(¬if->event_data, 0, sizeof(notif->event_data)); + if (data && data_size) { + int ret = memcpy_s(¬if->event_data, sizeof(notif->event_data), data, data_size); + + if (ret) { + /* Most likely a simple coding mistake: + * Using a data type that is not included in the event_data union. + * Return true to prevent an infinite re-try loop in non-debug builds + */ + assert(false); + return true; + } + } + + ipc_msg_send(msg, msg->tx_data, false); + + return true; } -void copier_gateway_underrun_notif_msg_init(struct ipc_msg *msg, uint32_t pipeline_id) +static enum sof_ipc4_resource_event_type dir_to_xrun_event(enum sof_ipc_stream_direction dir) { - struct ipc4_resource_event_data_notification *notif_data = msg->tx_data; - - resource_notif_header_init(msg); - notif_data->resource_id = pipeline_id; - notif_data->event_type = SOF_IPC4_GATEWAY_UNDERRUN_DETECTED; - notif_data->resource_type = SOF_IPC4_PIPELINE; + return (dir == SOF_IPC_STREAM_PLAYBACK) ? SOF_IPC4_GATEWAY_UNDERRUN_DETECTED : + SOF_IPC4_GATEWAY_OVERRUN_DETECTED; } -void gateway_underrun_notif_msg_init(struct ipc_msg *msg, uint32_t resource_id) +void ipc4_update_notification_mask(uint32_t ntfy_mask, uint32_t enabled_mask) { - struct ipc4_resource_event_data_notification *notif_data = msg->tx_data; - - resource_notif_header_init(msg); - notif_data->resource_id = resource_id; - notif_data->event_type = SOF_IPC4_GATEWAY_UNDERRUN_DETECTED; - notif_data->resource_type = SOF_IPC4_GATEWAY; + notification_mask &= enabled_mask | (~ntfy_mask); + notification_mask |= enabled_mask & ntfy_mask; } -void copier_gateway_overrun_notif_msg_init(struct ipc_msg *msg, uint32_t pipeline_id) +bool send_copier_gateway_xrun_notif_msg(uint32_t pipeline_id, enum sof_ipc_stream_direction dir) { - struct ipc4_resource_event_data_notification *notif_data = msg->tx_data; - - resource_notif_header_init(msg); - notif_data->resource_id = pipeline_id; - notif_data->event_type = SOF_IPC4_GATEWAY_OVERRUN_DETECTED; - notif_data->resource_type = SOF_IPC4_PIPELINE; + return send_resource_notif(pipeline_id, dir_to_xrun_event(dir), SOF_IPC4_PIPELINE, NULL, + 0); } -void gateway_overrun_notif_msg_init(struct ipc_msg *msg, uint32_t resource_id) +bool send_gateway_xrun_notif_msg(uint32_t resource_id, enum sof_ipc_stream_direction dir) { - struct ipc4_resource_event_data_notification *notif_data = msg->tx_data; - - resource_notif_header_init(msg); - notif_data->resource_id = resource_id; - notif_data->event_type = SOF_IPC4_GATEWAY_OVERRUN_DETECTED; - notif_data->resource_type = SOF_IPC4_GATEWAY; + return send_resource_notif(resource_id, dir_to_xrun_event(dir), SOF_IPC4_GATEWAY, NULL, 0); } -void mixer_underrun_notif_msg_init(struct ipc_msg *msg, uint32_t resource_id, uint32_t eos_flag, - uint32_t data_mixed, uint32_t expected_data_mixed) +void send_mixer_underrun_notif_msg(uint32_t resource_id, uint32_t eos_flag, uint32_t data_mixed, + uint32_t expected_data_mixed) { - struct ipc4_resource_event_data_notification *notif_data = msg->tx_data; - - resource_notif_header_init(msg); - notif_data->resource_id = resource_id; - notif_data->event_type = SOF_IPC4_MIXER_UNDERRUN_DETECTED; - notif_data->resource_type = SOF_IPC4_PIPELINE; - notif_data->event_data.mixer_underrun.eos_flag = eos_flag; - notif_data->event_data.mixer_underrun.data_mixed = data_mixed; - notif_data->event_data.mixer_underrun.expected_data_mixed = expected_data_mixed; + struct ipc4_mixer_underrun_event_data mixer_underrun_data; + + mixer_underrun_data.eos_flag = eos_flag; + mixer_underrun_data.data_mixed = data_mixed; + mixer_underrun_data.expected_data_mixed = expected_data_mixed; + + send_resource_notif(resource_id, SOF_IPC4_MIXER_UNDERRUN_DETECTED, SOF_IPC4_PIPELINE, + &mixer_underrun_data, sizeof(mixer_underrun_data)); } -EXPORT_SYMBOL(mixer_underrun_notif_msg_init); +EXPORT_SYMBOL(send_mixer_underrun_notif_msg); -void process_data_error_notif_msg_init(struct ipc_msg *msg, uint32_t resource_id, - uint32_t error_code) +void send_process_data_error_notif_msg(uint32_t resource_id, uint32_t error_code) { - struct ipc4_resource_event_data_notification *notif_data = msg->tx_data; + struct ipc4_process_data_error_event_data error_data; + + error_data.error_code = error_code; - resource_notif_header_init(msg); - notif_data->resource_id = resource_id; - notif_data->event_type = SOF_IPC4_PROCESS_DATA_ERROR; - notif_data->resource_type = SOF_IPC4_MODULE_INSTANCE; - notif_data->event_data.process_data_error.error_code = error_code; + send_resource_notif(resource_id, SOF_IPC4_PROCESS_DATA_ERROR, SOF_IPC4_MODULE_INSTANCE, + &error_data, sizeof(error_data)); } diff --git a/src/ipc/notification_pool.c b/src/ipc/notification_pool.c index 1627213f7829..5894fc499b99 100644 --- a/src/ipc/notification_pool.c +++ b/src/ipc/notification_pool.c @@ -100,4 +100,22 @@ struct ipc_msg *ipc_notification_pool_get(size_t size) item->msg.tx_size = size; return &item->msg; } -EXPORT_SYMBOL(ipc_notification_pool_get); + +#if CONFIG_LIBRARY +void ipc_notification_pool_free(void) +{ + struct ipc_notif_pool_item *item; + k_spinlock_key_t key; + + key = k_spin_lock(&pool_free_list_lock); + while (!list_is_empty(&pool_free_list)) { + item = list_first_item(&pool_free_list, struct ipc_notif_pool_item, msg.list); + list_item_del(&item->msg.list); + --pool_depth; + k_spin_unlock(&pool_free_list_lock, key); + rfree(item); + key = k_spin_lock(&pool_free_list_lock); + } + k_spin_unlock(&pool_free_list_lock, key); +} +#endif /* CONFIG_LIBRARY */ diff --git a/src/lib/CMakeLists.txt b/src/lib/CMakeLists.txt index f7a37d50cd72..707753635f69 100644 --- a/src/lib/CMakeLists.txt +++ b/src/lib/CMakeLists.txt @@ -1,6 +1,6 @@ # SPDX-License-Identifier: BSD-3-Clause -set(common_files notifier.c dma.c dai.c) +set(common_files notifier.c dma.c dai.c objpool.c) if(CONFIG_LIBRARY) add_local_sources(sof diff --git a/src/lib/agent.c b/src/lib/agent.c index 1d96bc54bb7e..1030e88dd7f2 100644 --- a/src/lib/agent.c +++ b/src/lib/agent.c @@ -81,9 +81,9 @@ static enum task_state validate(void *data) /* warning timeout */ if (delta > sa->warn_timeout) { if (delta > UINT_MAX) - tr_warn(&sa_tr, "validate(), ll drift detected, delta > %u", UINT_MAX); + tr_warn(&sa_tr, "ll drift detected, delta > %u", UINT_MAX); else - tr_warn(&sa_tr, "validate(), ll drift detected, delta = %u", + tr_warn(&sa_tr, "ll drift detected, delta = %u", (unsigned int)delta); } @@ -98,9 +98,9 @@ void sa_init(struct sof *sof, uint64_t timeout) uint64_t ticks; if (timeout > UINT_MAX) - tr_warn(&sa_tr, "sa_init(), timeout > %u", UINT_MAX); + tr_warn(&sa_tr, "timeout > %u", UINT_MAX); else - tr_info(&sa_tr, "sa_init(), timeout = %u", (unsigned int)timeout); + tr_info(&sa_tr, "timeout = %u", (unsigned int)timeout); sof->sa = rzalloc(SOF_MEM_FLAG_USER | SOF_MEM_FLAG_COHERENT, sizeof(*sof->sa)); @@ -117,10 +117,10 @@ void sa_init(struct sof *sof, uint64_t timeout) if (ticks > UINT_MAX || sof->sa->warn_timeout > UINT_MAX || sof->sa->panic_timeout > UINT_MAX) tr_info(&sa_tr, - "sa_init(), some of the values are > %u", UINT_MAX); + "some of the values are > %u", UINT_MAX); else tr_info(&sa_tr, - "sa_init(), ticks = %u, sof->sa->warn_timeout = %u, sof->sa->panic_timeout = %u", + "ticks = %u, sof->sa->warn_timeout = %u, sof->sa->panic_timeout = %u", (unsigned int)ticks, (unsigned int)sof->sa->warn_timeout, (unsigned int)sof->sa->panic_timeout); diff --git a/src/lib/alloc.c b/src/lib/alloc.c index 7d07677c9a65..aa7edeea95fd 100644 --- a/src/lib/alloc.c +++ b/src/lib/alloc.c @@ -38,18 +38,6 @@ void rfree(void *ptr) free(ptr); } -void *rbrealloc_align(void *ptr, uint32_t flags, size_t bytes, - size_t old_bytes, uint32_t alignment) -{ - void *newptr = aligned_alloc(alignment, bytes); - - if (!newptr) - return NULL; - memcpy(newptr, ptr, bytes > old_bytes ? old_bytes : bytes); - free(ptr); - return newptr; -} - /* TODO: all mm_pm_...() routines to be implemented for IMR storage */ uint32_t mm_pm_context_size(void) { @@ -71,3 +59,14 @@ int heap_info(int index, struct mm_info *out) return 0; } #endif + +void *sof_heap_alloc(struct k_heap *heap, uint32_t flags, size_t bytes, + size_t alignment) +{ + return malloc(bytes); +} + +void sof_heap_free(struct k_heap *heap, void *addr) +{ + free(addr); +} diff --git a/src/lib/ams.c b/src/lib/ams.c index 766c3bd96213..31bca13ce833 100644 --- a/src/lib/ams.c +++ b/src/lib/ams.c @@ -495,7 +495,7 @@ static int ams_process_slot(struct async_message_service *ams, uint32_t slot) instance_id = shared_c->slots[slot].instance_id; ams_release(shared_c); - tr_info(&ams_tr, "ams_process_slot slot %d msg %d from 0x%08x", + tr_info(&ams_tr, "slot %d msg %d from 0x%08x", slot, msg.message_type_id, msg.producer_module_id << 16 | msg.producer_instance_id); diff --git a/src/lib/dai.c b/src/lib/dai.c index c4daf2b06918..bd7c02edcb2d 100644 --- a/src/lib/dai.c +++ b/src/lib/dai.c @@ -185,8 +185,22 @@ const struct device *zephyr_dev[] = { #if CONFIG_DAI_NXP_MICFIL DT_FOREACH_STATUS_OKAY(nxp_dai_micfil, GET_DEVICE_LIST) #endif +#if CONFIG_DAI_AMD_SDW + DT_FOREACH_STATUS_OKAY(amd_acp_sdw_dai, GET_DEVICE_LIST) +#endif +#if CONFIG_DAI_INTEL_UAOL + DT_FOREACH_STATUS_OKAY(intel_uaol_dai, GET_DEVICE_LIST) +#endif }; +const struct device **dai_get_device_list(size_t *count) +{ + __ASSERT_NO_MSG(count); + *count = ARRAY_SIZE(zephyr_dev); + + return zephyr_dev; +} + /* convert sof_ipc_dai_type to Zephyr dai_type */ static int sof_dai_type_to_zephyr(uint32_t type) { @@ -199,6 +213,8 @@ static int sof_dai_type_to_zephyr(uint32_t type) return DAI_INTEL_HDA; case SOF_DAI_INTEL_ALH: return DAI_INTEL_ALH; + case SOF_DAI_INTEL_UAOL: + return DAI_INTEL_UAOL; case SOF_DAI_IMX_SAI: return DAI_IMX_SAI; case SOF_DAI_IMX_ESAI: @@ -216,14 +232,14 @@ static int sof_dai_type_to_zephyr(uint32_t type) case SOF_DAI_AMD_HS: case SOF_DAI_AMD_SP_VIRTUAL: case SOF_DAI_AMD_HS_VIRTUAL: - case SOF_DAI_AMD_SW_AUDIO: - return -ENOTSUP; + case SOF_DAI_AMD_SDW: + return DAI_AMD_SDW; default: return -EINVAL; } } -const struct device *dai_get_device(uint32_t type, uint32_t index) +const struct device *dai_get_device(enum sof_ipc_dai_type type, uint32_t index) { struct dai_config cfg; int z_type; @@ -277,9 +293,14 @@ static void dai_set_device_params(struct dai *d) #endif break; case SOF_DAI_INTEL_HDA: + case SOF_DAI_INTEL_UAOL: d->dma_dev = SOF_DMA_DEV_HDA; d->dma_caps = SOF_DMA_CAP_HDA; break; + case SOF_DAI_AMD_SDW: + d->dma_dev = SOF_DMA_DEV_SW; + d->dma_caps = SOF_DMA_CAP_SW; + break; default: break; } @@ -325,7 +346,7 @@ void dai_put(struct dai *dai) ret = dai_remove(dai->dev); if (ret < 0) { - tr_err(&dai_tr, "dai_put_zephyr: index %d failed ret = %d", + tr_err(&dai_tr, "index %d failed ret = %d", dai->index, ret); } @@ -391,11 +412,11 @@ void dai_put(struct dai *dai) if (--dai->sref == 0) { ret = dai_remove(dai); if (ret < 0) { - tr_err(&dai_tr, "dai_put: type %d index %d dai_remove() failed ret = %d", + tr_err(&dai_tr, "type %d index %d dai_remove() failed ret = %d", dai->drv->type, dai->index, ret); } } - tr_info(&dai_tr, "dai_put type %d index %d new sref %d", + tr_info(&dai_tr, "type %d index %d new sref %d", dai->drv->type, dai->index, dai->sref); k_spin_unlock(&dai->lock, key); } diff --git a/src/lib/dma.c b/src/lib/dma.c index d24f730276b1..70ac5c975058 100644 --- a/src/lib/dma.c +++ b/src/lib/dma.c @@ -17,7 +17,6 @@ #include <sof/lib/memory.h> #include <sof/lib/uuid.h> #include <rtos/spinlock.h> -#include <rtos/symbol.h> #include <sof/trace/trace.h> #include <ipc/topology.h> #include <user/trace.h> @@ -36,7 +35,7 @@ DECLARE_TR_CTX(dma_tr, SOF_UUID(dma_uuid), LOG_LEVEL_INFO); #if CONFIG_ZEPHYR_NATIVE_DRIVERS static int dma_init(struct sof_dma *dma); -struct sof_dma *sof_dma_get(uint32_t dir, uint32_t cap, uint32_t dev, uint32_t flags) +struct sof_dma *z_impl_sof_dma_get(uint32_t dir, uint32_t cap, uint32_t dev, uint32_t flags) { const struct dma_info *info = dma_info_get(); int users, ret = 0; @@ -129,7 +128,7 @@ struct sof_dma *sof_dma_get(uint32_t dir, uint32_t cap, uint32_t dev, uint32_t f return !ret ? dmin : NULL; } -void sof_dma_put(struct sof_dma *dma) +void z_impl_sof_dma_put(struct sof_dma *dma) { k_spinlock_key_t key; @@ -168,8 +167,6 @@ static int dma_init(struct sof_dma *dma) return 0; } -EXPORT_SYMBOL(sof_dma_get); -EXPORT_SYMBOL(sof_dma_put); #else struct dma *dma_get(uint32_t dir, uint32_t cap, uint32_t dev, uint32_t flags) { @@ -278,19 +275,18 @@ void dma_put(struct dma *dma) if (--dma->sref == 0) { ret = dma_remove_legacy(dma); if (ret < 0) { - tr_err(&dma_tr, "dma_put(): dma_remove() failed id = %d, ret = %d", + tr_err(&dma_tr, "dma_remove() failed id = %d, ret = %d", dma->plat_data.id, ret); } } - tr_info(&dma_tr, "dma_put(), dma = %p, sref = %d", + tr_info(&dma_tr, "dma = %p, sref = %d", dma, dma->sref); k_spin_unlock(&dma->lock, key); } -EXPORT_SYMBOL(dma_get); -EXPORT_SYMBOL(dma_put); #endif -int dma_sg_alloc(struct dma_sg_elem_array *elem_array, +int dma_sg_alloc(struct k_heap *heap, + struct dma_sg_elem_array *elem_array, uint32_t flags, uint32_t direction, uint32_t buffer_count, uint32_t buffer_bytes, @@ -298,11 +294,13 @@ int dma_sg_alloc(struct dma_sg_elem_array *elem_array, { int i; - elem_array->elems = rzalloc(SOF_MEM_FLAG_USER, - sizeof(struct dma_sg_elem) * buffer_count); + elem_array->elems = sof_heap_alloc(heap, SOF_MEM_FLAG_USER, + sizeof(struct dma_sg_elem) * buffer_count, 0); if (!elem_array->elems) return -ENOMEM; + memset(elem_array->elems, 0, sizeof(struct dma_sg_elem) * buffer_count); + for (i = 0; i < buffer_count; i++) { elem_array->elems[i].size = buffer_bytes; // TODO: may count offsets once @@ -324,12 +322,11 @@ int dma_sg_alloc(struct dma_sg_elem_array *elem_array, return 0; } -void dma_sg_free(struct dma_sg_elem_array *elem_array) +void dma_sg_free(struct k_heap *heap, struct dma_sg_elem_array *elem_array) { - rfree(elem_array->elems); + sof_heap_free(heap, elem_array->elems); dma_sg_init(elem_array); } -EXPORT_SYMBOL(dma_sg_free); int dma_buffer_copy_from(struct comp_buffer *source, struct comp_buffer *sink, diff --git a/src/lib/notifier.c b/src/lib/notifier.c index 8ec3d02f8a82..e398417a6fd1 100644 --- a/src/lib/notifier.c +++ b/src/lib/notifier.c @@ -63,7 +63,7 @@ int notifier_register(void *receiver, void *caller, enum notify_id type, sizeof(*handle)); if (!handle) { - tr_err(&nt_tr, "notifier_register(): callback handle allocation failed."); + tr_err(&nt_tr, "callback handle allocation failed."); ret = -ENOMEM; goto out; } @@ -197,7 +197,7 @@ void init_system_notify(struct sof *sof) *notify = rzalloc(SOF_MEM_FLAG_USER | SOF_MEM_FLAG_COHERENT, sizeof(**notify)); if (!*notify) { - tr_err(&nt_tr, "init_system_notify(): allocation failed"); + tr_err(&nt_tr, "allocation failed"); sof_panic(SOF_IPC_PANIC_IPC); } diff --git a/src/lib/objpool.c b/src/lib/objpool.c new file mode 100644 index 000000000000..6925e6f7070a --- /dev/null +++ b/src/lib/objpool.c @@ -0,0 +1,191 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// Copyright(c) 2025 Intel Corporation. + +#include <rtos/alloc.h> +#include <rtos/bit.h> +#include <sof/objpool.h> +#include <sof/common.h> +#include <sof/list.h> +#include <sof/lib/vregion.h> + +#include <errno.h> +#include <limits.h> +#include <stdbool.h> +#include <stddef.h> +#include <stdint.h> + +struct objpool { + struct list_item list; + unsigned int n; + uint32_t mask; + size_t size; + uint8_t data[]; +}; + +#define OBJPOOL_BITS (sizeof(((struct objpool *)0)->mask) * 8) + +static int objpool_add(struct objpool_head *head, unsigned int n, size_t size, uint32_t flags) +{ + if (n > OBJPOOL_BITS) + return -ENOMEM; + + if (!is_power_of_2(n)) + return -EINVAL; + + size_t aligned_size = n * ALIGN_UP(size, sizeof(int)); + + if (!head->heap) + head->heap = sof_sys_heap_get(); + + struct objpool *pobjpool; + + if (!head->vreg) + pobjpool = sof_heap_alloc(head->heap, flags, + aligned_size + sizeof(*pobjpool), 0); + else if (flags & SOF_MEM_FLAG_COHERENT) + pobjpool = vregion_alloc_coherent(head->vreg, VREGION_MEM_TYPE_INTERIM, + aligned_size + sizeof(*pobjpool)); + else + pobjpool = vregion_alloc(head->vreg, VREGION_MEM_TYPE_INTERIM, + aligned_size + sizeof(*pobjpool)); + + if (!pobjpool) + return -ENOMEM; + + /* Initialize with 0 to give caller a chance to identify new allocations */ + memset(pobjpool->data, 0, aligned_size); + + pobjpool->n = n; + /* clear bit means free */ + pobjpool->mask = 0; + pobjpool->size = size; + + list_item_append(&pobjpool->list, &head->list); + + return 0; +} + +void *objpool_alloc(struct objpool_head *head, size_t size, uint32_t flags) +{ + size_t aligned_size = ALIGN_UP(size, sizeof(int)); + struct list_item *list; + struct objpool *pobjpool; + + /* Make sure size * 32 still fits in OBJPOOL_BITS bits */ + if (!size || aligned_size > (UINT_MAX >> 5) - sizeof(*pobjpool)) + return NULL; + + if (!list_is_empty(&head->list) && head->flags != flags) + /* List isn't empty, and flags don't match */ + return NULL; + + list_for_item(list, &head->list) { + pobjpool = container_of(list, struct objpool, list); + + uint32_t free_mask = MASK(pobjpool->n - 1, 0) & ~pobjpool->mask; + + /* sanity check */ + if (size != pobjpool->size) + return NULL; + + if (!free_mask) + continue; + + /* Find first free - guaranteed valid now */ + unsigned int bit = ffs(free_mask) - 1; + + pobjpool->mask |= BIT(bit); + + return pobjpool->data + aligned_size * bit; + } + + /* no free elements found */ + unsigned int new_n; + + if (list_is_empty(&head->list)) { + head->flags = flags; + new_n = 2; + } else { + /* Check the last one */ + pobjpool = container_of(head->list.prev, struct objpool, list); + + if (pobjpool->n == OBJPOOL_BITS) + new_n = OBJPOOL_BITS; + else + new_n = pobjpool->n << 1; + } + + if (objpool_add(head, new_n, size, flags) < 0) + return NULL; + + /* Return the first element of the new objpool, which is now the last one in the list */ + pobjpool = container_of(head->list.prev, struct objpool, list); + pobjpool->mask = 1; + + return pobjpool->data; +} + +int objpool_free(struct objpool_head *head, void *data) +{ + struct list_item *list; + struct objpool *pobjpool; + + if (!data) + return 0; + + list_for_item(list, &head->list) { + pobjpool = container_of(list, struct objpool, list); + + size_t aligned_size = ALIGN_UP(pobjpool->size, sizeof(int)); + + if ((uint8_t *)data >= pobjpool->data && + (uint8_t *)data < pobjpool->data + aligned_size * pobjpool->n) { + unsigned int n = ((uint8_t *)data - pobjpool->data) / aligned_size; + + if ((uint8_t *)data != pobjpool->data + n * aligned_size) + return -EINVAL; + + pobjpool->mask &= ~BIT(n); + + return 0; + } + } + + return -EINVAL; +} + +void objpool_prune(struct objpool_head *head) +{ + struct list_item *next, *tmp; + + list_for_item_safe(next, tmp, &head->list) { + struct objpool *pool = container_of(next, struct objpool, list); + + list_item_del(next); + if (head->vreg) + vregion_free(head->vreg, pool); + else + sof_heap_free(head->heap, pool); + } +} + +int objpool_iterate(struct objpool_head *head, objpool_iterate_cb cb, void *arg) +{ + struct list_item *list; + + list_for_item(list, &head->list) { + struct objpool *pobjpool = container_of(list, struct objpool, list); + size_t aligned_size = ALIGN_UP(pobjpool->size, sizeof(int)); + unsigned int bit; + + for (uint32_t mask = pobjpool->mask; mask; mask &= ~BIT(bit)) { + bit = ffs(mask) - 1; + + if (cb(pobjpool->data + bit * aligned_size, arg)) + return 0; + } + } + + return -ENOENT; +} diff --git a/src/library_manager/lib_manager.c b/src/library_manager/lib_manager.c index d45df1493f5e..b81ec6bbe738 100644 --- a/src/library_manager/lib_manager.c +++ b/src/library_manager/lib_manager.c @@ -3,7 +3,8 @@ // Copyright(c) 2022 Intel Corporation. All rights reserved. // // Author: Jaroslaw Stelter <jaroslaw.stelter@intel.com> -// Pawel Dobrowolski<pawelx.dobrowolski@intel.com> +// Pawel Dobrowolski <pawelx.dobrowolski@intel.com> +// Adrian Warecki <adrian.warecki@intel.com> /* * Dynamic module loading functions. @@ -18,11 +19,13 @@ #include <rtos/clk.h> #include <rtos/sof.h> #include <rtos/spinlock.h> +#include <rtos/userspace_helper.h> #include <sof/lib/cpu-clk-manager.h> #include <sof/lib_manager.h> #include <sof/llext_manager.h> #include <sof/audio/module_adapter/module/generic.h> #include <sof/audio/module_adapter/module/modules.h> +#include <sof/audio/module_adapter/library/userspace_proxy.h> #include <utilities/array.h> #include <system_agent.h> #include <native_system_agent.h> @@ -373,7 +376,15 @@ static uintptr_t lib_manager_allocate_module(const struct sof_man_module *mod, return 0; } -int lib_manager_free_module(const uint32_t component_id) +/* + * \brief Free module + * + * param[in] component_id - component id coming from ipc config. This function reguires valid + * lib_id and module_id fields of component id. + * + * Function is responsible to free module resources in HP memory. + */ +static int lib_manager_free_module(const uint32_t component_id) { const struct sof_man_module *mod; const uint32_t module_id = IPC4_MOD_ID(component_id); @@ -412,14 +423,14 @@ int lib_manager_free_module(const uint32_t component_id) #define PAGE_SZ 4096 /* equals to MAN_PAGE_SIZE used by rimage */ -uintptr_t lib_manager_allocate_module(const struct comp_ipc_config *ipc_config, - const void *ipc_specific_config, const void **buildinfo) +static uintptr_t lib_manager_allocate_module(const struct comp_ipc_config *ipc_config, + const void *ipc_specific_config, const void **buildinfo) { tr_err(&lib_manager_tr, "Dynamic module allocation is not supported"); return 0; } -int lib_manager_free_module(const uint32_t component_id) +static int lib_manager_free_module(const uint32_t component_id) { /* Since we cannot allocate the freeing is not considered to be an error */ tr_warn(&lib_manager_tr, "Dynamic module freeing is not supported"); @@ -452,7 +463,7 @@ static void lib_manager_update_sof_ctx(void *base_addr, uint32_t lib_id) struct lib_manager_mod_ctx *ctx = rzalloc(SOF_MEM_FLAG_KERNEL | SOF_MEM_FLAG_COHERENT, sizeof(*ctx)); if (!ctx) { - tr_err(&lib_manager_tr, "lib_manager_update_sof_ctx(): allocation failed"); + tr_err(&lib_manager_tr, "allocation failed"); sof_panic(SOF_IPC_PANIC_IPC); } @@ -487,23 +498,27 @@ const struct sof_man_module *lib_manager_get_module_manifest(const uint32_t modu * \brief Starts system agent and returns module interface into agent_interface variable. * * \param[in] drv - Pointer to the component driver structure. - * \param[in] component_id - Identifier of the component. + * \param[in] config - Pointer to component ipc config structure * \param[in] args - Pointer to components' ipc configuration arguments. * \param[in] module_entry_point - Entry point address of the module. * \param[in] agent - Function pointer to the system agent start function. * \param[out] agent_interface - Pointer to store the module interface returned by the system agent. + * \param[out] userspace - Pointer to store the userspace module proxy context. + * \param[out] ops - Pointer to store pointer to the module interface structure. * * \return Error code returned by the system agent, 0 on success. */ -static int lib_manager_start_agent(const struct comp_driver *drv, const uint32_t component_id, +static int lib_manager_start_agent(const struct comp_driver *drv, + const struct comp_ipc_config *config, + const struct sof_man_module *mod_manifest, const struct ipc_config_process *args, const uintptr_t module_entry_point, const system_agent_start_fn agent, - const void **agent_interface) + const void **agent_interface, + struct userspace_context **userspace, + const struct module_interface **ops) { - const uint32_t module_id = IPC4_MOD_ID(component_id); - const uint32_t instance_id = IPC4_INST_ID(component_id); - const uint32_t log_handle = (uint32_t)drv->tctx; + struct system_agent_params agent_params; byte_array_t mod_cfg; int ret; @@ -511,8 +526,25 @@ static int lib_manager_start_agent(const struct comp_driver *drv, const uint32_t /* Intel modules expects DW size here */ mod_cfg.size = args->size >> 2; - ret = agent(module_entry_point, module_id, instance_id, 0, log_handle, &mod_cfg, - agent_interface); + agent_params.entry_point = module_entry_point; + agent_params.module_id = IPC4_MOD_ID(config->id); + agent_params.instance_id = IPC4_INST_ID(config->id); + agent_params.core_id = config->core; + agent_params.log_handle = (uint32_t)drv->tctx; + agent_params.mod_cfg = &mod_cfg; + +#if CONFIG_SOF_USERSPACE_PROXY + /* If drv->user_heap is allocated, it means the module is userspace. */ + if (drv->user_heap) { + ret = userspace_proxy_create(userspace, drv, mod_manifest, agent, &agent_params, + agent_interface, ops); + if (ret) + tr_err(&lib_manager_tr, "userspace_proxy_create failed! %d", ret); + return ret; + } +#endif /* CONFIG_SOF_USERSPACE_PROXY */ + + ret = agent(&agent_params, agent_interface); if (ret) tr_err(&lib_manager_tr, "System agent start failed %d!", ret); @@ -578,9 +610,10 @@ static struct comp_dev *lib_manager_module_create(const struct comp_driver *drv, const void *spec) { const struct sof_man_fw_desc *const desc = lib_manager_get_library_manifest(config->id); - const struct ipc_config_process *args = (struct ipc_config_process *)spec; + const struct ipc_config_process *args = (const struct ipc_config_process *)spec; const uint32_t entry_index = LIB_MANAGER_GET_MODULE_INDEX(config->id); - const struct module_interface *ops = NULL; + struct userspace_context *userspace = NULL; + const struct module_interface *ops; const struct sof_man_module *mod; system_agent_start_fn agent; void *adapter_priv = NULL; @@ -588,6 +621,13 @@ static struct comp_dev *lib_manager_module_create(const struct comp_driver *drv, struct comp_dev *dev; int ret; +#ifdef CONFIG_SOF_USERSPACE_PROXY + if (drv->user_heap && config->proc_domain != COMP_PROCESSING_DOMAIN_DP) { + tr_err(&lib_manager_tr, "Userspace supports only DP modules."); + return NULL; + } +#endif + tr_dbg(&lib_manager_tr, "start"); if (!desc) { tr_err(&lib_manager_tr, "Error: Couldn't find loadable module with id %u.", @@ -619,35 +659,39 @@ static struct comp_dev *lib_manager_module_create(const struct comp_driver *drv, agent = &native_system_agent_start; agent_iface = (const void **)&ops; break; +#if CONFIG_INTEL_MODULES case MOD_TYPE_IADK: agent = &system_agent_start; - /* processing_module_adapter_interface is already assigned to drv->adapter_ops by - * the lib_manager_prepare_module_adapter function. - */ + ops = &processing_module_adapter_interface; agent_iface = (const void **)&adapter_priv; break; +#endif case MOD_TYPE_INVALID: goto err; } /* At this point module resources are allocated and it is moved to L2 memory. */ if (agent) { - ret = lib_manager_start_agent(drv, config->id, args, module_entry_point, agent, - agent_iface); + ret = lib_manager_start_agent(drv, config, mod, args, module_entry_point, agent, + agent_iface, &userspace, &ops); if (ret) goto err; } - if (ops && comp_set_adapter_ops(drv, ops) < 0) + if (comp_set_adapter_ops(drv, ops) < 0) goto err; - dev = module_adapter_new_ext(drv, config, spec, adapter_priv); + dev = module_adapter_new_ext(drv, config, spec, adapter_priv, userspace); if (!dev) goto err; return dev; err: +#if CONFIG_SOF_USERSPACE_PROXY + if (userspace) + userspace_proxy_destroy(drv, userspace); +#endif /* CONFIG_SOF_USERSPACE_PROXY */ lib_manager_free_module(config->id); return NULL; } @@ -694,18 +738,16 @@ static void lib_manager_prepare_module_adapter(struct comp_driver *drv, const st drv->ops.dai_ts_start = module_adapter_ts_start_op; drv->ops.dai_ts_stop = module_adapter_ts_stop_op; drv->ops.dai_ts_get = module_adapter_ts_get_op; -#if CONFIG_INTEL_MODULES - drv->adapter_ops = &processing_module_adapter_interface; -#endif } int lib_manager_register_module(const uint32_t component_id) { - struct comp_driver_info *new_drv_info; - struct comp_driver *drv = NULL; const struct sof_man_module *mod = lib_manager_get_module_manifest(component_id); const struct sof_uuid *uid = (struct sof_uuid *)&mod->uuid; - int ret; + struct comp_driver_info *new_drv_info; + struct k_heap *drv_heap = NULL; + struct comp_driver *drv = NULL; + int ret = -ENOMEM; if (!mod) { tr_err(&lib_manager_tr, "Error: Couldn't find loadable module with id %u.", @@ -722,14 +764,26 @@ int lib_manager_register_module(const uint32_t component_id) return -ENOMEM; } - drv = rzalloc(SOF_MEM_FLAG_KERNEL | SOF_MEM_FLAG_COHERENT, - sizeof(struct comp_driver)); +#if CONFIG_SOF_USERSPACE_USE_DRIVER_HEAP + if (mod->type.user_mode) { + drv_heap = module_driver_heap_init(); + if (!drv_heap) { + tr_err(&lib_manager_tr, "failed to allocate driver heap!"); + goto cleanup; + } + } +#endif /* CONFIG_SOF_USERSPACE_USE_DRIVER_HEAP */ + + drv = sof_heap_alloc(drv_heap, SOF_MEM_FLAG_KERNEL | SOF_MEM_FLAG_COHERENT, + sizeof(struct comp_driver), 0); if (!drv) { tr_err(&lib_manager_tr, "failed to allocate comp_driver"); - ret = -ENOMEM; goto cleanup; } + memset(drv, 0, sizeof(*drv)); + drv->user_heap = drv_heap; + lib_manager_prepare_module_adapter(drv, uid); /* Fill the new_drv_info structure with already known parameters */ @@ -741,8 +795,9 @@ int lib_manager_register_module(const uint32_t component_id) cleanup: if (ret < 0) { - rfree(drv); rfree(new_drv_info); + sof_heap_free(drv_heap, drv); + module_driver_heap_remove(drv_heap); } return ret; @@ -937,6 +992,9 @@ static int lib_manager_store_library(struct lib_manager_dma_ext *dma_ext, return ret; } + /* Writeback entire library to ensure it's visible to other cores */ + dcache_writeback_region((__sparse_force void *)library_base_address, preload_size); + #if CONFIG_LIBRARY_AUTH_SUPPORT /* AUTH_PHASE_LAST - do final library authentication checks */ ret = lib_manager_auth_proc((__sparse_force void *)library_base_address, diff --git a/src/library_manager/llext_manager.c b/src/library_manager/llext_manager.c index 9c032319f82d..a370a5f1c7f7 100644 --- a/src/library_manager/llext_manager.c +++ b/src/library_manager/llext_manager.c @@ -25,6 +25,7 @@ #include <sof/audio/module_adapter/module/modules.h> #include <zephyr/cache.h> +#include <zephyr/app_memory/mem_domain.h> #include <zephyr/drivers/mm/system_mm.h> #include <zephyr/llext/buf_loader.h> #include <zephyr/llext/loader.h> @@ -124,7 +125,7 @@ static int llext_manager_load_data_from_storage(const struct sys_mm_drv_region * int ret = llext_get_section_info(ldr, ext, i, &shdr, &s_region, &s_offset); if (ret < 0) { - tr_err(lib_manager_tr, "no section info: %d", ret); + tr_err(&lib_manager_tr, "no section info: %d", ret); continue; } @@ -226,9 +227,12 @@ static int llext_manager_load_module(struct lib_manager_module *mctx) (uintptr_t)bss_addr >= (uintptr_t)va_base_data + data_size)) { size_t bss_align = MIN(PAGE_SZ, BIT(__builtin_ctz((uintptr_t)bss_addr))); - if ((uintptr_t)bss_addr + bss_size == (uintptr_t)va_base_data && - !((uintptr_t)bss_addr & (PAGE_SZ - 1))) { - /* .bss directly in front of writable data and properly aligned, prepend */ + if ((!data_size || (uintptr_t)bss_addr + bss_size == (uintptr_t)va_base_data) && + bss_align >= PAGE_SZ) { + /* + * .bss is properly aligned and either there's no writable data, + * or .bss is directly in front of writable data, prepend .bss + */ va_base_data = bss_addr; data_size += bss_size; } else if ((uintptr_t)bss_addr == (uintptr_t)va_base_data + @@ -250,11 +254,15 @@ static int llext_manager_load_module(struct lib_manager_module *mctx) const struct sys_mm_drv_region *virtual_memory_regions = sys_mm_drv_query_memory_regions(); const struct sys_mm_drv_region *virtual_region; + if (!virtual_memory_regions) + return -EFAULT; + SYS_MM_DRV_MEMORY_REGION_FOREACH(virtual_memory_regions, virtual_region) { if (virtual_region->attr == VIRTUAL_REGION_LLEXT_LIBRARIES_ATTR) break; } - if (!virtual_region || !virtual_region->size) + + if (!virtual_region->size) return -EFAULT; /* Copy Code */ @@ -286,7 +294,8 @@ static int llext_manager_load_module(struct lib_manager_module *mctx) return 0; e_rodata: - llext_manager_align_unmap(va_base_rodata, rodata_size); + if (rodata_size) + llext_manager_align_unmap(va_base_rodata, rodata_size); e_text: llext_manager_align_unmap(va_base_text, text_size); @@ -311,6 +320,8 @@ static int llext_manager_unload_module(struct lib_manager_module *mctx) /* Writable data (.data, .bss, etc.) */ void __sparse_cache *va_base_data = (void __sparse_cache *) mctx->segment[LIB_MANAGER_DATA].addr; + void __sparse_cache *va_base_bss = (void __sparse_cache *) + mctx->segment[LIB_MANAGER_BSS].addr; size_t data_size = mctx->segment[LIB_MANAGER_DATA].size + mctx->segment[LIB_MANAGER_BSS].size; int err = 0, ret; @@ -321,6 +332,12 @@ static int llext_manager_unload_module(struct lib_manager_module *mctx) if (ret < 0) err = ret; + /* Mimic the logic from load_module where the .bss address is used for mapping + * in case of e.g. lack of writable .data section + */ + if (!va_base_data) + va_base_data = va_base_bss; + llext_manager_unmap_detached_sections(ldr, ext, LLEXT_MEM_DATA, va_base_data, data_size); ret = llext_manager_align_unmap(va_base_data, data_size); @@ -716,6 +733,249 @@ uintptr_t llext_manager_allocate_module(const struct comp_ipc_config *ipc_config return mod_manifest->module.entry_point; } +#ifdef CONFIG_USERSPACE +static int llext_manager_add_partition(struct k_mem_domain *domain, + uintptr_t addr, size_t size, + k_mem_partition_attr_t attr) +{ + size_t pre_pad_size = addr & (PAGE_SZ - 1); + struct k_mem_partition part = { + .start = addr - pre_pad_size, + .size = ALIGN_UP(pre_pad_size + size, PAGE_SZ), + .attr = attr, + }; + + tr_dbg(&lib_manager_tr, "add %#zx @ %lx partition", part.size, part.start); + return k_mem_domain_add_partition(domain, &part); +} + +static int llext_manager_rm_partition(struct k_mem_domain *domain, + uintptr_t addr, size_t size, + k_mem_partition_attr_t attr) +{ + size_t pre_pad_size = addr & (PAGE_SZ - 1); + struct k_mem_partition part = { + .start = addr - pre_pad_size, + .size = ALIGN_UP(pre_pad_size + size, PAGE_SZ), + .attr = attr, + }; + + tr_dbg(&lib_manager_tr, "remove %#zx @ %lx partition", part.size, part.start); + return k_mem_domain_remove_partition(domain, &part); +} + +int llext_manager_add_domain(const uint32_t component_id, struct k_mem_domain *domain) +{ + const uint32_t module_id = IPC4_MOD_ID(component_id); + struct lib_manager_mod_ctx *ctx = lib_manager_get_mod_ctx(module_id); + const uint32_t entry_index = LIB_MANAGER_GET_MODULE_INDEX(module_id); + const unsigned int mod_idx = llext_manager_mod_find(ctx, entry_index); + struct lib_manager_module *mctx = ctx->mod + mod_idx; + const struct llext *ext = mctx->llext; + const struct llext_loader *ldr = &mctx->ebl->loader; + + /* Executable code (.text) */ + uintptr_t va_base_text = mctx->segment[LIB_MANAGER_TEXT].addr; + size_t text_size = mctx->segment[LIB_MANAGER_TEXT].size; + + /* Read-only data (.rodata and others) */ + uintptr_t va_base_rodata = mctx->segment[LIB_MANAGER_RODATA].addr; + size_t rodata_size = mctx->segment[LIB_MANAGER_RODATA].size; + + /* Writable data (.data, .bss and others) */ + uintptr_t va_base_data = mctx->segment[LIB_MANAGER_DATA].addr; + size_t data_size = mctx->segment[LIB_MANAGER_DATA].size; + + int ret = llext_manager_add_partition(domain, va_base_text, text_size, + K_MEM_PARTITION_P_RX_U_RX | XTENSA_MMU_CACHED_WB); + + if (ret < 0) + return ret; + + if (rodata_size) { + ret = llext_manager_add_partition(domain, va_base_rodata, rodata_size, + K_MEM_PARTITION_P_RO_U_RO | XTENSA_MMU_CACHED_WB); + if (ret < 0) + goto e_text; + } + + if (data_size) { + ret = llext_manager_add_partition(domain, va_base_data, data_size, + K_MEM_PARTITION_P_RW_U_RW | XTENSA_MMU_CACHED_WB); + if (ret < 0) + goto e_rodata; + } + + elf_shdr_t shdr_cold, shdr_coldrodata; + bool rodata = false, text = false; + const void *rodata_addr = NULL, *text_addr = NULL; + size_t text_offset = 0, rodata_offset = 0; + + shdr_cold.sh_size = 0; + shdr_coldrodata.sh_size = 0; + + ret = llext_get_section_header(ldr, ext, ".cold", &shdr_cold); + if (ret < 0) + tr_warn(&lib_manager_tr, "couldn't get .cold header"); + else + llext_get_region_info(ldr, ext, LLEXT_MEM_TEXT, NULL, &text_addr, NULL); + + ret = llext_get_section_header(ldr, ext, ".coldrodata", &shdr_coldrodata); + if (ret < 0) + tr_warn(&lib_manager_tr, "couldn't get .coldrodata header"); + else + llext_get_region_info(ldr, ext, LLEXT_MEM_RODATA, NULL, &rodata_addr, NULL); + + for (unsigned int i = 0; i < llext_section_count(ext) && (!rodata || !text); i++) { + const elf_shdr_t *shdr; + enum llext_mem s_region = LLEXT_MEM_COUNT; + size_t s_offset = 0; + + ret = llext_get_section_info(ldr, ext, i, &shdr, &s_region, &s_offset); + if (ret < 0) + continue; + + switch (s_region) { + case LLEXT_MEM_TEXT: + if (shdr_cold.sh_size && + shdr->sh_name == shdr_cold.sh_name && + shdr->sh_offset == shdr_cold.sh_offset && !text) { + text = true; + text_offset = s_offset; + } + break; + case LLEXT_MEM_RODATA: + if (shdr_coldrodata.sh_size && + shdr->sh_name == shdr_coldrodata.sh_name && + shdr->sh_offset == shdr_coldrodata.sh_offset && !rodata) { + rodata = true; + rodata_offset = s_offset; + } + break; + default: + break; + } + } + + if (text) { + tr_dbg(&lib_manager_tr, ".cold %#x @ %#lx", + shdr_cold.sh_size, (uintptr_t)text_addr + text_offset); + ret = llext_manager_add_partition(domain, (uintptr_t)text_addr + text_offset, + shdr_cold.sh_size, + K_MEM_PARTITION_P_RX_U_RX | XTENSA_MMU_CACHED_WB); + if (ret < 0) + goto e_data; + mctx->segment[LIB_MANAGER_COLD].addr = (uintptr_t)text_addr + text_offset; + mctx->segment[LIB_MANAGER_COLD].size = shdr_cold.sh_size; + } + + if (rodata) { + tr_dbg(&lib_manager_tr, ".coldrodata %#x @ %#lx", + shdr_coldrodata.sh_size, (uintptr_t)rodata_addr + rodata_offset); + ret = llext_manager_add_partition(domain, (uintptr_t)rodata_addr + rodata_offset, + shdr_coldrodata.sh_size, + K_MEM_PARTITION_P_RO_U_RO | XTENSA_MMU_CACHED_WB); + if (ret < 0) + goto e_cold; + mctx->segment[LIB_MANAGER_COLDRODATA].addr = (uintptr_t)rodata_addr + rodata_offset; + mctx->segment[LIB_MANAGER_COLDRODATA].size = shdr_coldrodata.sh_size; + } + + return 0; + +e_cold: + llext_manager_rm_partition(domain, (uintptr_t)text_addr + text_offset, shdr_cold.sh_size, + K_MEM_PARTITION_P_RX_U_RX | XTENSA_MMU_CACHED_WB); + mctx->segment[LIB_MANAGER_COLD].addr = 0; + mctx->segment[LIB_MANAGER_COLD].size = 0; +e_data: + llext_manager_rm_partition(domain, va_base_data, data_size, + K_MEM_PARTITION_P_RW_U_RW | XTENSA_MMU_CACHED_WB); +e_rodata: + llext_manager_rm_partition(domain, va_base_rodata, rodata_size, + K_MEM_PARTITION_P_RO_U_RO | XTENSA_MMU_CACHED_WB); +e_text: + llext_manager_rm_partition(domain, va_base_text, text_size, + K_MEM_PARTITION_P_RX_U_RX | XTENSA_MMU_CACHED_WB); + return ret; +} + +int llext_manager_rm_domain(const uint32_t component_id, struct k_mem_domain *domain) +{ + const uint32_t module_id = IPC4_MOD_ID(component_id); + struct lib_manager_mod_ctx *ctx = lib_manager_get_mod_ctx(module_id); + const uint32_t entry_index = LIB_MANAGER_GET_MODULE_INDEX(module_id); + const unsigned int mod_idx = llext_manager_mod_find(ctx, entry_index); + struct lib_manager_module *mctx = ctx->mod + mod_idx; + + /* Executable code (.text) */ + uintptr_t va_base_text = mctx->segment[LIB_MANAGER_TEXT].addr; + size_t text_size = mctx->segment[LIB_MANAGER_TEXT].size; + + /* Read-only data (.rodata and others) */ + uintptr_t va_base_rodata = mctx->segment[LIB_MANAGER_RODATA].addr; + size_t rodata_size = mctx->segment[LIB_MANAGER_RODATA].size; + + /* Writable data (.data, .bss and others) */ + uintptr_t va_base_data = mctx->segment[LIB_MANAGER_DATA].addr; + size_t data_size = mctx->segment[LIB_MANAGER_DATA].size; + + int err, ret = llext_manager_rm_partition(domain, va_base_text, text_size, + K_MEM_PARTITION_P_RX_U_RX | XTENSA_MMU_CACHED_WB); + + if (ret < 0) + tr_err(&lib_manager_tr, "failed to remove .text memory partition: %d", ret); + + if (rodata_size) { + err = llext_manager_rm_partition(domain, va_base_rodata, rodata_size, + K_MEM_PARTITION_P_RO_U_RO | XTENSA_MMU_CACHED_WB); + if (err < 0) { + tr_err(&lib_manager_tr, "failed to remove .rodata memory partition: %d", + err); + if (!ret) + ret = err; + } + } + + if (data_size) { + err = llext_manager_rm_partition(domain, va_base_data, data_size, + K_MEM_PARTITION_P_RW_U_RW | XTENSA_MMU_CACHED_WB); + if (err < 0) { + tr_err(&lib_manager_tr, "failed to remove .data memory partition: %d", err); + if (!ret) + ret = err; + } + } + + if (mctx->segment[LIB_MANAGER_COLD].addr) { + err = llext_manager_rm_partition(domain, + mctx->segment[LIB_MANAGER_COLD].addr, + mctx->segment[LIB_MANAGER_COLD].size, + K_MEM_PARTITION_P_RX_U_RX | XTENSA_MMU_CACHED_WB); + if (err < 0) { + tr_err(&lib_manager_tr, "failed to remove .cold memory partition: %d", err); + if (!ret) + ret = err; + } + } + + if (mctx->segment[LIB_MANAGER_COLDRODATA].addr) { + err = llext_manager_rm_partition(domain, + mctx->segment[LIB_MANAGER_COLDRODATA].addr, + mctx->segment[LIB_MANAGER_COLDRODATA].size, + K_MEM_PARTITION_P_RO_U_RO | XTENSA_MMU_CACHED_WB); + if (err < 0) { + tr_err(&lib_manager_tr, + "failed to remove .coldrodata memory partition: %d", err); + if (!ret) + ret = err; + } + } + + return ret; +} +#endif + int llext_manager_free_module(const uint32_t component_id) { const uint32_t module_id = IPC4_MOD_ID(component_id); diff --git a/src/library_manager/llext_manager_dram.c b/src/library_manager/llext_manager_dram.c index 9c134a3fcd81..25c00f47b4e1 100644 --- a/src/library_manager/llext_manager_dram.c +++ b/src/library_manager/llext_manager_dram.c @@ -23,6 +23,7 @@ struct lib_manager_dram_storage { struct llext_elf_sect_map *sect; struct llext_symbol *sym; unsigned int n_llext; + unsigned int n_mod; }; /* @@ -147,6 +148,8 @@ int llext_manager_store_to_dram(void) } lib_manager_dram.n_llext = n_llext; + lib_manager_dram.n_mod = n_mod; + /* Make sure, that the data is actually in the DRAM, not just in data cache */ dcache_writeback_region((__sparse_force void __sparse_cache *)&lib_manager_dram, sizeof(lib_manager_dram)); @@ -162,26 +165,33 @@ int llext_manager_restore_from_dram(void) struct ext_library *_ext_lib = ext_lib_get(); unsigned int i, j, k, n_mod, n_llext, n_sect, n_sym; + struct llext_loader **ldr; + struct llext **llext; - if (!lib_manager_dram.n_llext || !lib_manager_dram.ctx) { + if (!lib_manager_dram.n_mod || !lib_manager_dram.ctx) { tr_dbg(&lib_manager_tr, "No modules saved"); dcache_writeback_region((__sparse_force void __sparse_cache *)&lib_manager_dram, sizeof(lib_manager_dram)); return 0; } - /* arrays of pointers for llext_restore() */ - void **ptr_array = rmalloc(SOF_MEM_FLAG_KERNEL, - sizeof(*ptr_array) * lib_manager_dram.n_llext * 2); - - if (!ptr_array) - return -ENOMEM; - - struct llext_loader **ldr = (struct llext_loader **)ptr_array; - struct llext **llext = (struct llext **)(ptr_array + lib_manager_dram.n_llext); - *_ext_lib = lib_manager_dram.ext_lib; + if (lib_manager_dram.n_llext) { + /* arrays of pointers for llext_restore() */ + void **ptr_array = rmalloc(SOF_MEM_FLAG_KERNEL, + sizeof(*ptr_array) * lib_manager_dram.n_llext * 2); + + if (!ptr_array) + return -ENOMEM; + + ldr = (struct llext_loader **)ptr_array; + llext = (struct llext **)(ptr_array + lib_manager_dram.n_llext); + } else { + ldr = NULL; + llext = NULL; + } + /* The external loop walks all the libraries */ for (i = 0, j = 0, n_mod = 0, n_llext = 0, n_sect = 0, n_sym = 0; i < ARRAY_SIZE(_ext_lib->desc); i++) { @@ -262,26 +272,28 @@ int llext_manager_restore_from_dram(void) _ext_lib->desc[i] = ctx; } - /* Let Zephyr restore extensions and its own internal bookkeeping */ - int ret = llext_restore(llext, ldr, lib_manager_dram.n_llext); + if (lib_manager_dram.n_llext) { + /* Let Zephyr restore extensions and its own internal bookkeeping */ + int ret = llext_restore(llext, ldr, lib_manager_dram.n_llext); - if (ret < 0) { - tr_err(&lib_manager_tr, "Zephyr failed to restore: %d", ret); - goto nomem; - } + if (ret < 0) { + tr_err(&lib_manager_tr, "Zephyr failed to restore: %d", ret); + goto nomem; + } - /* Rewrite to correct LLEXT pointers, created by Zephyr */ - for (i = 0, n_llext = 0; i < ARRAY_SIZE(_ext_lib->desc); i++) { - struct lib_manager_mod_ctx *ctx = _ext_lib->desc[i]; + /* Rewrite to correct LLEXT pointers, created by Zephyr */ + for (i = 0, n_llext = 0; i < ARRAY_SIZE(_ext_lib->desc); i++) { + struct lib_manager_mod_ctx *ctx = _ext_lib->desc[i]; - if (!ctx) - continue; + if (!ctx) + continue; - struct lib_manager_module *mod = ctx->mod; + struct lib_manager_module *mod = ctx->mod; - for (k = 0; k < ctx->n_mod; k++) { - if (mod[k].llext) - mod[k].llext = llext[n_llext++]; + for (k = 0; k < ctx->n_mod; k++) { + if (mod[k].llext) + mod[k].llext = llext[n_llext++]; + } } } diff --git a/src/math/CMakeLists.txt b/src/math/CMakeLists.txt index 6afd2c342fe0..a52263006295 100644 --- a/src/math/CMakeLists.txt +++ b/src/math/CMakeLists.txt @@ -11,12 +11,16 @@ if(CONFIG_CORDIC_FIXED) list(APPEND base_files trig.c) endif() +if(CONFIG_MATH_ATAN2) + list(APPEND base_files atan2.c) +endif() + if(CONFIG_MATH_LUT_SINE_FIXED) list(APPEND base_files lut_trig.c) endif() if(CONFIG_SQRT_FIXED) - list(APPEND base_files sqrt_int16.c) + list(APPEND base_files sqrt_int16.c sqrt_int32.c) endif() if(CONFIG_MATH_EXP) @@ -91,6 +95,10 @@ if(CONFIG_MATH_MU_LAW_CODEC) list(APPEND base_files mu_law.c) endif() +if(CONFIG_MATH_COMPLEX) + list(APPEND base_files complex.c) +endif() + is_zephyr(zephyr) if(zephyr) ### Zephyr ### diff --git a/src/math/Kconfig b/src/math/Kconfig index ebab5dd076de..1feaab8f03d1 100644 --- a/src/math/Kconfig +++ b/src/math/Kconfig @@ -9,6 +9,16 @@ config CORDIC_FIXED Select this to enable sin(), cos(), asin(), acos(), and cexp() functions as 16 bit and 32 bit versions. +config MATH_ATAN2 + bool "Four-quadrant arctangent function" + default n + help + Select this to enable sofm_atan2_32b(y, x) function. It computes + the four-quadrant arctangent using a polynomial approximation. + Input arguments y and x are Q1.31 format and the output angle + is Q3.29 format with range [-pi, pi]. The maximum approximation + error is ~0.001 degrees. + config MATH_LUT_SINE_FIXED bool "Lookup table based sine function" default n @@ -95,13 +105,33 @@ config MATH_DECIBELS Select this to enable db2lin_fixed() and exp_fixed() functions. +config MATH_COMPLEX + bool "Operations for complex numbers" + default n + select CORDIC_FIXED + select MATH_ATAN2 + select SQRT_FIXED + help + Select this to enable functions for complex numbers + arithmetic such as conversions to/from polar format. + config MATH_FFT bool "FFT library" default n + select MATH_COMPLEX help Enable Fast Fourier Transform library, this should not be selected directly, please select it from other audio components where need it. +config MATH_FFT_MULTI + bool "FFT library for some non-power-of-two sizes" + depends on MATH_FFT + default n + help + Enable Fast Fourier Transform library for e.g. sizes 1536 and 3072, + this should not be selected directly, please select it from other + audio components where need it. + menu "Supported FFT word lengths" visible if MATH_FFT diff --git a/src/math/atan2.c b/src/math/atan2.c new file mode 100644 index 000000000000..652f1d5c9cc7 --- /dev/null +++ b/src/math/atan2.c @@ -0,0 +1,114 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// Copyright(c) 2026 Intel Corporation. +// +// Author: Seppo Ingalsuo <seppo.ingalsuo@linux.intel.com> + +/** + * \file math/atan2.c + * \brief Four-quadrant arctangent function using polynomial approximation + */ + +#include <rtos/symbol.h> +#include <sof/audio/format.h> +#include <sof/math/trig.h> +#include <stdint.h> + +/* + * Degree-9 Remez minimax polynomial for atan(z) in first octant, z in [0, 1]: + * + * atan(z) = z * (C0 + z^2 * (C1 + z^2 * (C2 + z^2 * (C3 + z^2 * C4)))) + * + * Coefficients are in Q3.29 format, computed via Remez exchange algorithm + * to minimize the maximum approximation error over [0, 1]. + * Maximum approximation error is ~0.0011 degrees (1.94e-5 radians). + */ +#define SOFM_ATAN2_C0 536890772 /* Q3.29, +1.000036992319 */ +#define SOFM_ATAN2_C1 -178298711 /* Q3.29, -0.332107229582 */ +#define SOFM_ATAN2_C2 99794340 /* Q3.29, +0.185881443393 */ +#define SOFM_ATAN2_C3 -49499604 /* Q3.29, -0.092200197922 */ +#define SOFM_ATAN2_C4 12781063 /* Q3.29, +0.023806584771 */ + +/** + * sofm_atan2_32b() - Compute four-quadrant arctangent of y/x + * @param y Imaginary component in Q1.31 format + * @param x Real component in Q1.31 format + * @return Angle in Q3.29 radians, range [-pi, +pi] + * + * Uses degree-9 Remez minimax polynomial for the core atan(z) computation + * where z = min(|y|,|x|) / max(|y|,|x|) is reduced to [0, 1] range. + * Octant and quadrant corrections extend the result to full [-pi, +pi]. + */ +int32_t sofm_atan2_32b(int32_t y, int32_t x) +{ + int32_t abs_x; + int32_t abs_y; + int32_t num; + int32_t den; + int32_t z; + int32_t z2; + int32_t acc; + int32_t angle; + int swap; + + /* Return zero for origin */ + if (x == 0 && y == 0) + return 0; + + /* Take absolute values, handle INT32_MIN overflow */ + abs_x = (x > 0) ? x : ((x == INT32_MIN) ? INT32_MAX : -x); + abs_y = (y > 0) ? y : ((y == INT32_MIN) ? INT32_MAX : -y); + + /* Reduce to first octant: z = min/max ensures z in [0, 1] */ + if (abs_y <= abs_x) { + num = abs_y; + den = abs_x; + swap = 0; + } else { + num = abs_x; + den = abs_y; + swap = 1; + } + + /* z = num / den in Q1.31, den is always > 0 here */ + z = sat_int32(((int64_t)num << 31) / den); + + /* + * Horner evaluation of degree-9 Remez minimax polynomial: + * atan(z) = z * (C0 + z^2 * (C1 + z^2 * (C2 + z^2 * (C3 + z^2 * C4)))) + * + * z is Q1.31, coefficients are Q3.29. + * Multiplications: Q1.31 * Q3.29 = Q4.60, >> 31 -> Q3.29. + */ + + /* z^2: Q1.31 * Q1.31 = Q2.62, >> 31 -> Q1.31 */ + z2 = (int32_t)(((int64_t)z * z) >> 31); + + /* Innermost: C3 + z^2 * C4 */ + acc = (int32_t)(((int64_t)z2 * SOFM_ATAN2_C4) >> 31) + SOFM_ATAN2_C3; + + /* C2 + z^2 * (C3 + z^2 * C4) */ + acc = (int32_t)(((int64_t)z2 * acc) >> 31) + SOFM_ATAN2_C2; + + /* C1 + z^2 * (C2 + z^2 * (C3 + z^2 * C4)) */ + acc = (int32_t)(((int64_t)z2 * acc) >> 31) + SOFM_ATAN2_C1; + + /* C0 + z^2 * (C1 + z^2 * (C2 + z^2 * (C3 + z^2 * C4))) */ + acc = (int32_t)(((int64_t)z2 * acc) >> 31) + SOFM_ATAN2_C0; + + /* angle = z * acc: Q1.31 * Q3.29 = Q4.60, >> 31 -> Q3.29 */ + angle = (int32_t)(((int64_t)z * acc) >> 31); + + /* If |y| > |x|, use identity: atan(y/x) = pi/2 - atan(x/y) */ + if (swap) + angle = PI_DIV2_Q3_29 - angle; + + /* Map to correct quadrant based on signs of x and y */ + if (x < 0) + angle = (y >= 0) ? PI_Q3_29 - angle : -(PI_Q3_29 - angle); + else if (y < 0) + angle = -angle; + + return angle; +} +EXPORT_SYMBOL(sofm_atan2_32b); diff --git a/src/math/auditory/auditory.c b/src/math/auditory/auditory.c index dbfe47b1699c..57641c46aa5a 100644 --- a/src/math/auditory/auditory.c +++ b/src/math/auditory/auditory.c @@ -4,6 +4,7 @@ // // Author: Seppo Ingalsuo <seppo.ingalsuo@linux.intel.com> +#include <sof/audio/module_adapter/module/generic.h> #include <sof/audio/format.h> #include <rtos/alloc.h> #include <sof/math/auditory.h> @@ -85,7 +86,7 @@ int16_t psy_mel_to_hz(int16_t mel) return hz; } -int psy_get_mel_filterbank(struct psy_mel_filterbank *fb) +int mod_psy_get_mel_filterbank(struct processing_module *mod, struct psy_mel_filterbank *fb) { int32_t up_slope; int32_t down_slope; @@ -200,8 +201,7 @@ int psy_get_mel_filterbank(struct psy_mel_filterbank *fb) } fb->data_length = &fb->scratch_data2[base_idx] - &fb->scratch_data2[0]; - fb->data = rzalloc(SOF_MEM_FLAG_USER, - sizeof(int16_t) * fb->data_length); + fb->data = mod_zalloc(mod, sizeof(int16_t) * fb->data_length); if (!fb->data) return -ENOMEM; @@ -210,3 +210,8 @@ int psy_get_mel_filterbank(struct psy_mel_filterbank *fb) fb->scratch_data2, sizeof(int16_t) * fb->data_length); return 0; } + +int mod_psy_free_mel_filterbank(struct processing_module *mod, struct psy_mel_filterbank *mel_fb) +{ + return mod_free(mod, mel_fb->data); +} diff --git a/src/math/auditory/mel_filterbank_16.c b/src/math/auditory/mel_filterbank_16.c index 2ed62c319d38..f5a18f052b77 100644 --- a/src/math/auditory/mel_filterbank_16.c +++ b/src/math/auditory/mel_filterbank_16.c @@ -6,7 +6,7 @@ #include <sof/audio/format.h> #include <sof/math/auditory.h> -#include <sof/math/fft.h> +#include <sof/math/icomplex16.h> #include <sof/math/log.h> #include <sof/math/numbers.h> #include <stdint.h> diff --git a/src/math/auditory/mel_filterbank_32.c b/src/math/auditory/mel_filterbank_32.c index 69b781817870..414ddf482f93 100644 --- a/src/math/auditory/mel_filterbank_32.c +++ b/src/math/auditory/mel_filterbank_32.c @@ -6,13 +6,13 @@ #include <sof/audio/format.h> #include <sof/math/auditory.h> -#include <sof/math/fft.h> +#include <sof/math/icomplex32.h> #include <sof/math/log.h> #include <sof/math/numbers.h> #include <stdint.h> void psy_apply_mel_filterbank_32(struct psy_mel_filterbank *fb, struct icomplex32 *fft_out, - int32_t *power_spectra, int16_t *mel_log, int bitshift) + int32_t *power_spectra, int32_t *mel_log, int bitshift) { int64_t pmax; int64_t p; @@ -79,8 +79,8 @@ void psy_apply_mel_filterbank_32(struct psy_mel_filterbank *fb, struct icomplex3 */ log -= ((int32_t)lshift + 2 * bitshift) << 16; - /* Scale for desired log */ - log = Q_MULTSR_32X32((int64_t)log, fb->log_mult, 16, 29, 7); - mel_log[i] = sat_int16(log); /* Q8.7 */ + /* Scale for desired log, output as Q9.23 */ + log = Q_MULTSR_32X32((int64_t)log, fb->log_mult, 16, 29, 23); + mel_log[i] = log; /* Q9.23 */ } } diff --git a/src/math/complex.c b/src/math/complex.c new file mode 100644 index 000000000000..ac2a59e91498 --- /dev/null +++ b/src/math/complex.c @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// Copyright(c) 2026 Intel Corporation. +// +// Author: Seppo Ingalsuo <seppo.ingalsuo@linux.intel.com> +// + +#include <rtos/symbol.h> +#include <sof/audio/format.h> +#include <sof/math/icomplex32.h> +#include <sof/math/sqrt.h> +#include <sof/math/trig.h> +#include <stdint.h> + +/* sofm_icomplex32_to_polar() - Convert (re, im) complex number to polar. */ +void sofm_icomplex32_to_polar(struct icomplex32 *complex, struct ipolar32 *polar) +{ + struct icomplex32 c = *complex; + int64_t squares_sum; + int32_t sqrt_arg; + + /* Calculate square of magnitudes Q1.31, result is Q2.62 */ + squares_sum = (int64_t)c.real * c.real + (int64_t)c.imag * c.imag; + + /* Square root */ + sqrt_arg = Q_SHIFT_RND(squares_sum, 62, 30); + polar->magnitude = sofm_sqrt_int32(sqrt_arg); /* Q2.30 */ + + /* Angle */ + polar->angle = sofm_atan2_32b(c.imag, c.real); /* Q3.29 */ +} +EXPORT_SYMBOL(sofm_icomplex32_to_polar); + +/* sofm_ipolar32_to_complex() - Convert complex number from polar to normal (re, im) format. */ +void sofm_ipolar32_to_complex(struct ipolar32 *polar, struct icomplex32 *complex) +{ + struct cordic_cmpx cexp; + int32_t phase; + int32_t magnitude; + + /* The conversion can happen in-place, so load copies of the values first */ + magnitude = polar->magnitude; + phase = Q_SHIFT_RND(polar->angle, 29, 28); /* Q3.29 to Q2.28 */ + cmpx_exp_32b(phase, &cexp); /* Q2.30 */ + complex->real = sat_int32(Q_MULTSR_32X32((int64_t)magnitude, cexp.re, 30, 30, 31)); + complex->imag = sat_int32(Q_MULTSR_32X32((int64_t)magnitude, cexp.im, 30, 30, 31)); +} +EXPORT_SYMBOL(sofm_ipolar32_to_complex); diff --git a/src/math/dct.c b/src/math/dct.c index 15d23b67c7f9..e4bbe5623dd8 100644 --- a/src/math/dct.c +++ b/src/math/dct.c @@ -4,6 +4,7 @@ // // Author: Seppo Ingalsuo <seppo.ingalsuo@linux.intel.com> +#include <sof/audio/module_adapter/module/generic.h> #include <sof/audio/format.h> #include <sof/math/matrix.h> #include <sof/math/dct.h> @@ -31,7 +32,7 @@ * multiply with the returned matrix. * \param[in,out] dct In input provide DCT type and size, in output the DCT matrix */ -int dct_initialize_16(struct dct_plan_16 *dct) +int mod_dct_initialize_16(struct processing_module *mod, struct dct_plan_16 *dct) { int16_t dct_val; int32_t arg; @@ -51,13 +52,13 @@ int dct_initialize_16(struct dct_plan_16 *dct) if (dct->num_in > DCT_MATRIX_SIZE_MAX || dct->num_out > DCT_MATRIX_SIZE_MAX) return -EINVAL; - dct->matrix = mat_matrix_alloc_16b(dct->num_in, dct->num_out, 15); + dct->matrix = mod_mat_matrix_alloc_16b(mod, dct->num_in, dct->num_out, 15); if (!dct->matrix) return -ENOMEM; c1 = PI_Q29 / dct->num_in; arg = Q_SHIFT_RND(TWO_Q29 / dct->num_in, 29, 12); - c2 = sqrt_int16(arg); /* Q4.12 */ + c2 = sofm_sqrt_int16(arg); /* Q4.12 */ for (n = 0; n < dct->num_in; n++) { for (k = 0; k < dct->num_out; k++) { /* Note: Current int16_t nk works up to DCT_MATRIX_SIZE_MAX = 91 */ @@ -77,3 +78,8 @@ int dct_initialize_16(struct dct_plan_16 *dct) return 0; } + +int mod_dct_free_16(struct processing_module *mod, struct dct_plan_16 *dct) +{ + return mod_free(mod, dct->matrix); +} diff --git a/src/math/fft/CMakeLists.txt b/src/math/fft/CMakeLists.txt index 82b8aee77b4d..c605886c335e 100644 --- a/src/math/fft/CMakeLists.txt +++ b/src/math/fft/CMakeLists.txt @@ -10,6 +10,10 @@ if(CONFIG_MATH_32BIT_FFT) list(APPEND base_files fft_32.c fft_32_hifi3.c) endif() +if(CONFIG_MATH_FFT_MULTI) + list(APPEND base_files fft_multi.c fft_multi_generic.c fft_multi_hifi3.c) +endif() + is_zephyr(zephyr) if(zephyr) ### Zephyr ### diff --git a/src/math/fft/fft_16.c b/src/math/fft/fft_16.c index 7af6c1eb2768..7a8ffad26054 100644 --- a/src/math/fft/fft_16.c +++ b/src/math/fft/fft_16.c @@ -1,68 +1,19 @@ // SPDX-License-Identifier: BSD-3-Clause // -// Copyright(c) 2020 Intel Corporation. All rights reserved. +// Copyright(c) 2020-2026 Intel Corporation. // // Author: Amery Song <chao.song@intel.com> // Keyon Jie <yang.jie@linux.intel.com> #include <sof/audio/format.h> -#include <sof/common.h> #include <sof/math/fft.h> +#include <sof/math/icomplex16.h> +#include <sof/common.h> +#include <stdint.h> #ifdef FFT_GENERIC #include <sof/audio/coefficients/fft/twiddle_16.h> -/* - * Helpers for 16 bit FFT calculation - */ -static inline void icomplex16_add(const struct icomplex16 *in1, const struct icomplex16 *in2, - struct icomplex16 *out) -{ - out->real = in1->real + in2->real; - out->imag = in1->imag + in2->imag; -} - -static inline void icomplex16_sub(const struct icomplex16 *in1, const struct icomplex16 *in2, - struct icomplex16 *out) -{ - out->real = in1->real - in2->real; - out->imag = in1->imag - in2->imag; -} - -static inline void icomplex16_mul(const struct icomplex16 *in1, const struct icomplex16 *in2, - struct icomplex16 *out) -{ - int32_t real = (int32_t)in1->real * in2->real - (int32_t)in1->imag * in2->imag; - int32_t imag = (int32_t)in1->real * in2->imag + (int32_t)in1->imag * in2->real; - - out->real = Q_SHIFT_RND(real, 30, 15); - out->imag = Q_SHIFT_RND(imag, 30, 15); -} - -/* complex conjugate */ -static inline void icomplex16_conj(struct icomplex16 *comp) -{ - comp->imag = sat_int16(-((int32_t)comp->imag)); -} - -/* shift a complex n bits, n > 0: left shift, n < 0: right shift */ -static inline void icomplex16_shift(const struct icomplex16 *input, int16_t n, - struct icomplex16 *output) -{ - int n1, n2; - - if (n >= 0) { - /* need saturation handling */ - output->real = sat_int16((int32_t)input->real << n); - output->imag = sat_int16((int32_t)input->imag << n); - } else { - n1 = -n; - n2 = 1 << (n1 - 1); - output->real = sat_int16(((int32_t)input->real + n2) >> n1); - output->imag = sat_int16(((int32_t)input->imag + n2) >> n1); - } -} - /** * \brief Execute the 16-bits Fast Fourier Transform (FFT) or Inverse FFT (IFFT) * For the configured fft_pan. diff --git a/src/math/fft/fft_16_hifi3.c b/src/math/fft/fft_16_hifi3.c index 54e9cebc4a23..56d5bc177789 100644 --- a/src/math/fft/fft_16_hifi3.c +++ b/src/math/fft/fft_16_hifi3.c @@ -7,6 +7,8 @@ #include <sof/audio/format.h> #include <sof/common.h> #include <sof/math/fft.h> +#include <sof/math/icomplex16.h> + #ifdef FFT_HIFI3 #include <sof/audio/coefficients/fft/twiddle_16.h> @@ -33,12 +35,14 @@ void fft_execute_16(struct fft_plan *plan, bool ifft) ae_valign outu = AE_ZALIGN64(); int depth, top, bottom, index; int i, j, k, m, n; - int size = plan->size; - int len = plan->len; + int size, len; if (!plan || !plan->bit_reverse_idx) return; + size = plan->size; + len = plan->len; + outb = plan->outb16; if (!plan->inb16 || !outb) return; diff --git a/src/math/fft/fft_32.c b/src/math/fft/fft_32.c index 3806f37675b8..a6f8b2aa1ab3 100644 --- a/src/math/fft/fft_32.c +++ b/src/math/fft/fft_32.c @@ -6,60 +6,14 @@ // Keyon Jie <yang.jie@linux.intel.com> #include <sof/audio/format.h> -#include <sof/common.h> -#include <rtos/alloc.h> #include <sof/math/fft.h> +#include <sof/math/icomplex32.h> +#include <sof/common.h> +#include <stdint.h> #ifdef FFT_GENERIC #include <sof/audio/coefficients/fft/twiddle_32.h> -/* - * These helpers are optimized for FFT calculation only. - * e.g. _add/sub() assume the output won't be saturate so no check needed, - * and _mul() assumes Q1.31 * Q1.31 so the output will be shifted to be Q1.31. - */ - -static inline void icomplex32_add(const struct icomplex32 *in1, const struct icomplex32 *in2, - struct icomplex32 *out) -{ - out->real = in1->real + in2->real; - out->imag = in1->imag + in2->imag; -} - -static inline void icomplex32_sub(const struct icomplex32 *in1, const struct icomplex32 *in2, - struct icomplex32 *out) -{ - out->real = in1->real - in2->real; - out->imag = in1->imag - in2->imag; -} - -static inline void icomplex32_mul(const struct icomplex32 *in1, const struct icomplex32 *in2, - struct icomplex32 *out) -{ - out->real = ((int64_t)in1->real * in2->real - (int64_t)in1->imag * in2->imag) >> 31; - out->imag = ((int64_t)in1->real * in2->imag + (int64_t)in1->imag * in2->real) >> 31; -} - -/* complex conjugate */ -static inline void icomplex32_conj(struct icomplex32 *comp) -{ - comp->imag = SATP_INT32((int64_t)-1 * comp->imag); -} - -/* shift a complex n bits, n > 0: left shift, n < 0: right shift */ -static inline void icomplex32_shift(const struct icomplex32 *input, int32_t n, - struct icomplex32 *output) -{ - if (n > 0) { - /* need saturation handling */ - output->real = SATP_INT32(SATM_INT32((int64_t)input->real << n)); - output->imag = SATP_INT32(SATM_INT32((int64_t)input->imag << n)); - } else { - output->real = input->real >> -n; - output->imag = input->imag >> -n; - } -} - /** * \brief Execute the 32-bits Fast Fourier Transform (FFT) or Inverse FFT (IFFT) * For the configured fft_pan. @@ -97,7 +51,7 @@ void fft_execute_32(struct fft_plan *plan, bool ifft) } /* step 1: re-arrange input in bit reverse order, and shrink the level to avoid overflow */ - for (i = 1; i < plan->size; ++i) + for (i = 0; i < plan->size; ++i) icomplex32_shift(&inb[i], -(plan->len), &outb[plan->bit_reverse_idx[i]]); /* step 2: loop to do FFT transform in smaller size */ @@ -133,9 +87,11 @@ void fft_execute_32(struct fft_plan *plan, bool ifft) * for Q1.31 format. Instead, we need to multiply N to compensate * the shrink we did in the FFT transform. */ - for (i = 0; i < plan->size; i++) + for (i = 0; i < plan->size; i++) { + icomplex32_conj(&outb[i]); icomplex32_shift(&outb[i], plan->len, &outb[i]); + } } } -#endif +#endif /* FFT_GENERIC */ diff --git a/src/math/fft/fft_32_hifi3.c b/src/math/fft/fft_32_hifi3.c index 91cd18b6a3ed..1d00dacd1b7f 100644 --- a/src/math/fft/fft_32_hifi3.c +++ b/src/math/fft/fft_32_hifi3.c @@ -15,23 +15,21 @@ void fft_execute_32(struct fft_plan *plan, bool ifft) { - struct icomplex32 tmp1; - ae_int32x2 *inx; - ae_int32x2 *out; - ae_int32x2 *outtop; - ae_int32x2 *outbottom; - ae_int32x2 *outx; + ae_int64 res, res1; ae_int32x2 sample; ae_int32x2 sample1; ae_int32x2 sample2; - ae_int64 res, res1; - int depth, top, bottom, index; - int i, j, k, m, n; - ae_int32 *in; - ae_valign inu = AE_ZALIGN64(); - ae_valign outu = AE_ZALIGN64(); - int size = plan->size; - int len = plan->len; + ae_int32x2 tw; + ae_int32x2 *inx; + ae_int32x2 *outx; + ae_int32x2 *top_ptr; + ae_int32x2 *bot_ptr; + uint16_t *idx; + const int32_t *tw_r; + const int32_t *tw_i; + int depth, i; + int j, k, m, n; + int size, len; if (!plan || !plan->bit_reverse_idx) return; @@ -39,64 +37,104 @@ void fft_execute_32(struct fft_plan *plan, bool ifft) if (!plan->inb32 || !plan->outb32) return; - inx = (ae_int32x2 *)plan->inb32 + 1; + inx = (ae_int32x2 *)plan->inb32; outx = (ae_int32x2 *)plan->outb32; + idx = &plan->bit_reverse_idx[0]; + size = plan->size; + len = plan->len; - /* convert to complex conjugate for ifft */ + /* step 1: re-arrange input in bit reverse order, and shrink the level to avoid overflow */ if (ifft) { - in = (ae_int32 *)&plan->inb32->imag; - for (i = 0; i < size; i++) { - AE_L32_IP(sample, in, 0); - sample = AE_NEG32S(sample); - AE_S32_L_IP(sample, in, sizeof(struct icomplex32)); + /* convert to complex conjugate for ifft */ + for (i = 0; i < size; ++i) { + AE_L32X2_IP(sample, inx, sizeof(ae_int32x2)); + sample = AE_SRAA32S(sample, len); + sample1 = AE_NEG32S(sample); + sample = AE_SEL32_HL(sample, sample1); + AE_S32X2_X(sample, outx, idx[i] * sizeof(ae_int32x2)); + } + } else { + for (i = 0; i < size; ++i) { + AE_L32X2_IP(sample, inx, sizeof(ae_int32x2)); + sample = AE_SRAA32S(sample, len); + AE_S32X2_X(sample, outx, idx[i] * sizeof(ae_int32x2)); } } - /* step 1: re-arrange input in bit reverse order, and shrink the level to avoid overflow */ - inu = AE_LA64_PP(inx); - for (i = 1; i < size; ++i) { - AE_LA32X2_IP(sample, inu, inx); - sample = AE_SRAA32S(sample, len); - out = &outx[plan->bit_reverse_idx[i]]; - AE_SA32X2_IP(sample, outu, out); + /* + * Step 2a: First FFT stage (depth=1, m=2, n=1). + * All butterflies use twiddle factor W^0 = 1+0j, + * so the complex multiply is skipped entirely. + */ + top_ptr = outx; + bot_ptr = outx + 1; + for (k = 0; k < size; k += 2) { + sample1 = AE_L32X2_I(top_ptr, 0); + sample2 = AE_L32X2_I(bot_ptr, 0); + sample = AE_ADD32S(sample1, sample2); + AE_S32X2_I(sample, top_ptr, 0); + sample = AE_SUB32S(sample1, sample2); + AE_S32X2_I(sample, bot_ptr, 0); + top_ptr += 2; + bot_ptr += 2; } - AE_SA64POS_FP(outu, out); - /* step 2: loop to do FFT transform in smaller size */ - for (depth = 1; depth <= len; ++depth) { + /* Step 2b: Remaining FFT stages (depth >= 2) */ + for (depth = 2; depth <= len; ++depth) { m = 1 << depth; n = m >> 1; i = FFT_SIZE_MAX >> depth; + top_ptr = outx; + bot_ptr = outx + n; + /* doing FFT transforms in size m */ for (k = 0; k < size; k += m) { - /* doing one FFT transform for size m */ - for (j = 0; j < n; ++j) { - index = i * j; - top = k + j; - bottom = top + n; - tmp1.real = twiddle_real_32[index]; - tmp1.imag = twiddle_imag_32[index]; - inx = (ae_int32x2 *)&tmp1; - AE_LA32X2_IP(sample1, inu, inx); + /* + * j=0: twiddle factor W^0 = 1+0j, + * butterfly without complex multiply. + */ + sample1 = AE_L32X2_I(top_ptr, 0); + sample = AE_L32X2_I(bot_ptr, 0); + sample2 = AE_ADD32S(sample1, sample); + AE_S32X2_I(sample2, top_ptr, 0); + sample2 = AE_SUB32S(sample1, sample); + AE_S32X2_I(sample2, bot_ptr, 0); + top_ptr++; + bot_ptr++; + + /* j=1..n-1: full butterfly with twiddle multiply */ + tw_r = &twiddle_real_32[i]; + tw_i = &twiddle_imag_32[i]; + for (j = 1; j < n; ++j) { + /* load and combine twiddle factor {real, imag} into tw */ + tw = AE_MOVDA32X2(tw_r[0], tw_i[0]); + /* calculate the accumulator: twiddle * bottom */ - sample2 = outx[bottom]; - res = AE_MULF32S_HH(sample1, sample2); - AE_MULSF32S_LL(res, sample1, sample2); - res1 = AE_MULF32S_HL(sample1, sample2); - AE_MULAF32S_LH(res1, sample1, sample2); + sample2 = AE_L32X2_I(bot_ptr, 0); + res = AE_MULF32S_HH(tw, sample2); + AE_MULSF32S_LL(res, tw, sample2); + res1 = AE_MULF32S_HL(tw, sample2); + AE_MULAF32S_LH(res1, tw, sample2); sample = AE_ROUND32X2F64SSYM(res, res1); + sample1 = AE_L32X2_I(top_ptr, 0); - sample1 = outx[top]; /* calculate the top output: top = top + accumulate */ sample2 = AE_ADD32S(sample1, sample); - outtop = outx + top; - AE_SA32X2_IP(sample2, outu, outtop); + AE_S32X2_I(sample2, top_ptr, 0); + /* calculate the bottom output: bottom = top - accumulate */ sample2 = AE_SUB32S(sample1, sample); - outbottom = outx + bottom; - AE_SA32X2_IP(sample2, outu, outbottom); + AE_S32X2_I(sample2, bot_ptr, 0); + + top_ptr++; + bot_ptr++; + tw_r += i; + tw_i += i; } + /* advance pointers past current group's bottom half */ + top_ptr += n; + bot_ptr += n; } } @@ -105,16 +143,17 @@ void fft_execute_32(struct fft_plan *plan, bool ifft) /* * no need to divide N as it is already done in the input side * for Q1.31 format. Instead, we need to multiply N to compensate - * the shrink we did in the FFT transform. + * the shrink we did in the FFT transform. Also make complex + * conjugate by negating the imaginary part. */ inx = outx; - inu = AE_LA64_PP(inx); for (i = 0; i < size; ++i) { - AE_LA32X2_IP(sample, inu, inx); + AE_L32X2_IP(sample, inx, sizeof(ae_int32x2)); sample = AE_SLAA32S(sample, len); - AE_SA32X2_IP(sample, outu, outx); + sample1 = AE_NEG32S(sample); + sample = AE_SEL32_HL(sample, sample1); + AE_S32X2_IP(sample, outx, sizeof(ae_int32x2)); } - AE_SA64POS_FP(outu, outx); } } #endif diff --git a/src/math/fft/fft_common.c b/src/math/fft/fft_common.c index a2afd09cf127..5ce47acd025a 100644 --- a/src/math/fft/fft_common.c +++ b/src/math/fft/fft_common.c @@ -1,29 +1,46 @@ // SPDX-License-Identifier: BSD-3-Clause // -// Copyright(c) 2020 Intel Corporation. All rights reserved. +// Copyright(c) 2020-2025 Intel Corporation. // // Author: Amery Song <chao.song@intel.com> // Keyon Jie <yang.jie@linux.intel.com> +#include <sof/audio/module_adapter/module/generic.h> #include <sof/audio/buffer.h> #include <sof/audio/format.h> +#include <sof/trace/trace.h> +#include <sof/lib/uuid.h> #include <sof/common.h> #include <rtos/alloc.h> #include <sof/math/fft.h> +#include "fft_common.h" -struct fft_plan *fft_plan_new(void *inb, void *outb, uint32_t size, int bits) +LOG_MODULE_REGISTER(math_fft, CONFIG_SOF_LOG_LEVEL); +SOF_DEFINE_REG_UUID(math_fft); +DECLARE_TR_CTX(math_fft_tr, SOF_UUID(math_fft_uuid), LOG_LEVEL_INFO); + +struct fft_plan *fft_plan_common_new(struct processing_module *mod, void *inb, + void *outb, uint32_t size, int bits) { struct fft_plan *plan; int lim = 1; int len = 0; - int i; - if (!inb || !outb) + if (!inb || !outb) { + comp_cl_err(mod->dev, "NULL input/output buffers."); return NULL; + } - plan = rzalloc(SOF_MEM_FLAG_USER, sizeof(struct fft_plan)); - if (!plan) + if (!is_power_of_2(size)) { + comp_cl_err(mod->dev, "The FFT size must be a power of two."); return NULL; + } + + plan = mod_zalloc(mod, sizeof(struct fft_plan)); + if (!plan) { + comp_cl_err(mod->dev, "Failed to allocate FFT plan."); + return NULL; + } switch (bits) { case 16: @@ -35,7 +52,7 @@ struct fft_plan *fft_plan_new(void *inb, void *outb, uint32_t size, int bits) plan->outb32 = outb; break; default: - rfree(plan); + comp_cl_err(mod->dev, "Invalid word length."); return NULL; } @@ -47,27 +64,50 @@ struct fft_plan *fft_plan_new(void *inb, void *outb, uint32_t size, int bits) plan->size = lim; plan->len = len; + return plan; +} - plan->bit_reverse_idx = rzalloc(SOF_MEM_FLAG_USER, - plan->size * sizeof(uint16_t)); - if (!plan->bit_reverse_idx) { - rfree(plan); +void fft_plan_init_bit_reverse(uint16_t *bit_reverse_idx, int size, int len) +{ + int i; + + /* Set up the bit reverse index. The array will contain the value of + * the index with the bits order reversed. Index can be skipped. + */ + for (i = 1; i < size; ++i) + bit_reverse_idx[i] = (bit_reverse_idx[i >> 1] >> 1) | ((i & 1) << (len - 1)); +} + +struct fft_plan *mod_fft_plan_new(struct processing_module *mod, void *inb, + void *outb, uint32_t size, int bits) +{ + struct fft_plan *plan; + + if (size > FFT_SIZE_MAX || size < FFT_SIZE_MIN) { + comp_cl_err(mod->dev, "Invalid FFT size %d", size); return NULL; } - /* set up the bit reverse index */ - for (i = 1; i < plan->size; ++i) - plan->bit_reverse_idx[i] = (plan->bit_reverse_idx[i >> 1] >> 1) | - ((i & 1) << (len - 1)); + plan = fft_plan_common_new(mod, inb, outb, size, bits); + if (!plan) + return NULL; + + plan->bit_reverse_idx = mod_zalloc(mod, plan->size * sizeof(uint16_t)); + if (!plan->bit_reverse_idx) { + comp_cl_err(mod->dev, "Failed to allocate bit reverse table."); + mod_free(mod, plan); + return NULL; + } + fft_plan_init_bit_reverse(plan->bit_reverse_idx, plan->size, plan->len); return plan; } -void fft_plan_free(struct fft_plan *plan) +void mod_fft_plan_free(struct processing_module *mod, struct fft_plan *plan) { if (!plan) return; - rfree(plan->bit_reverse_idx); - rfree(plan); + mod_free(mod, plan->bit_reverse_idx); + mod_free(mod, plan); } diff --git a/src/math/fft/fft_common.h b/src/math/fft/fft_common.h new file mode 100644 index 000000000000..9ea9998073ca --- /dev/null +++ b/src/math/fft/fft_common.h @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// Copyright(c) 2025 Intel Corporation. All rights reserved. +// +// Author: Seppo Ingalsuo <seppo.ingalsuo@linux.intel.com> + +/** + * fft_plan_common_new() - Common FFT prepare function + * @param mod: Pointer to module + * @param inb: Buffer to use for complex input data + * @param outb: Buffer to use for complex output data + * @param size: Size of FFT as number of bins + * @param bits: World length of FFT. Currently only 32 is supported. + * @return Pointer to FFT plan + */ +struct fft_plan *fft_plan_common_new(struct processing_module *mod, void *inb, + void *outb, uint32_t size, int bits); + +/** + * fft_plan_init_bit_reverse - Configures a bit reversal lookup vector + * @param bit_reverse_idx: Pointer to array to store bit reverse lookup + * @param size: Size of FFT + * @param len: Power of two value equals FFT size + */ +void fft_plan_init_bit_reverse(uint16_t *bit_reverse_idx, int size, int len); diff --git a/src/math/fft/fft_multi.c b/src/math/fft/fft_multi.c new file mode 100644 index 000000000000..b16decca012d --- /dev/null +++ b/src/math/fft/fft_multi.c @@ -0,0 +1,140 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// Copyright(c) 2025 Intel Corporation. +// +// Author: Seppo Ingalsuo <seppo.ingalsuo@linux.intel.com> + +#include <sof/audio/coefficients/fft/twiddle_3072_32.h> +#include <sof/audio/module_adapter/module/generic.h> +#include <sof/audio/format.h> +#include <sof/math/icomplex32.h> +#include <sof/trace/trace.h> +#include <sof/lib/uuid.h> +#include <sof/common.h> +#include <rtos/alloc.h> +#include <sof/math/fft.h> +#include "fft_common.h" + +LOG_MODULE_REGISTER(math_fft_multi, CONFIG_SOF_LOG_LEVEL); +SOF_DEFINE_REG_UUID(math_fft_multi); +DECLARE_TR_CTX(math_fft_multi_tr, SOF_UUID(math_fft_multi_uuid), LOG_LEVEL_INFO); + +struct fft_multi_plan *mod_fft_multi_plan_new(struct processing_module *mod, void *inb, + void *outb, uint32_t size, int bits) +{ + struct fft_multi_plan *plan; + size_t tmp_size; + const int size_div3 = size / 3; + int i; + + if (!inb || !outb) { + comp_cl_err(mod->dev, "Null buffers"); + return NULL; + } + + if (size < FFT_SIZE_MIN) { + comp_cl_err(mod->dev, "Invalid FFT size %d", size); + return NULL; + } + + plan = mod_zalloc(mod, sizeof(struct fft_multi_plan)); + if (!plan) + return NULL; + + if (is_power_of_2(size)) { + plan->num_ffts = 1; + } else if (size_div3 * 3 == size) { + plan->num_ffts = 3; + } else { + comp_cl_err(mod->dev, "Not supported FFT size %d", size); + goto err; + } + + /* Allocate common bit reverse table for all FFT plans */ + plan->total_size = size; + plan->fft_size = size / plan->num_ffts; + if (plan->fft_size > FFT_SIZE_MAX) { + comp_cl_err(mod->dev, "Requested size %d FFT is too large", size); + goto err; + } + + plan->bit_reverse_idx = mod_zalloc(mod, plan->fft_size * sizeof(uint16_t)); + if (!plan->bit_reverse_idx) { + comp_cl_err(mod->dev, "Failed to allocate FFT plan"); + goto err; + } + + switch (bits) { + case 32: + plan->inb32 = inb; + plan->outb32 = outb; + + if (plan->num_ffts > 1) { + /* Allocate input/output buffers for FFTs */ + tmp_size = 2 * plan->num_ffts * plan->fft_size * sizeof(struct icomplex32); + plan->tmp_i32[0] = mod_balloc(mod, tmp_size); + if (!plan->tmp_i32[0]) { + comp_cl_err(mod->dev, "Failed to allocate FFT buffers"); + goto err_free_bit_reverse; + } + + /* Set up buffers */ + plan->tmp_o32[0] = plan->tmp_i32[0] + plan->fft_size; + for (i = 1; i < plan->num_ffts; i++) { + plan->tmp_i32[i] = plan->tmp_o32[i - 1] + plan->fft_size; + plan->tmp_o32[i] = plan->tmp_i32[i] + plan->fft_size; + } + } else { + plan->tmp_i32[0] = inb; + plan->tmp_o32[0] = outb; + } + + for (i = 0; i < plan->num_ffts; i++) { + plan->fft_plan[i] = fft_plan_common_new(mod, + plan->tmp_i32[i], + plan->tmp_o32[i], + plan->fft_size, 32); + if (!plan->fft_plan[i]) + goto err_free_buffer; + + plan->fft_plan[i]->bit_reverse_idx = plan->bit_reverse_idx; + } + break; + default: + comp_cl_err(mod->dev, "Not supported word length %d", bits); + goto err; + } + + /* Set up common bit index reverse table */ + fft_plan_init_bit_reverse(plan->bit_reverse_idx, plan->fft_plan[0]->size, + plan->fft_plan[0]->len); + return plan; + +err_free_buffer: + mod_free(mod, plan->tmp_i32[0]); + +err_free_bit_reverse: + mod_free(mod, plan->bit_reverse_idx); + +err: + mod_free(mod, plan); + return NULL; +} + +void mod_fft_multi_plan_free(struct processing_module *mod, struct fft_multi_plan *plan) +{ + int i; + + if (!plan) + return; + + for (i = 0; i < plan->num_ffts; i++) + mod_free(mod, plan->fft_plan[i]); + + /* If single FFT, the internal buffers were not allocated. */ + if (plan->num_ffts > 1) + mod_free(mod, plan->tmp_i32[0]); + + mod_free(mod, plan->bit_reverse_idx); + mod_free(mod, plan); +} diff --git a/src/math/fft/fft_multi_generic.c b/src/math/fft/fft_multi_generic.c new file mode 100644 index 000000000000..52ec1e8172c0 --- /dev/null +++ b/src/math/fft/fft_multi_generic.c @@ -0,0 +1,181 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// Copyright(c) 2025-2026 Intel Corporation. +// +// Author: Seppo Ingalsuo <seppo.ingalsuo@linux.intel.com> + +#include <sof/audio/format.h> +#include <sof/math/icomplex32.h> +#include <sof/common.h> +#include <sof/math/fft.h> +#include <string.h> + +#ifdef DEBUG_DUMP_TO_FILE +#include <stdio.h> +#endif + +/* Twiddle factor tables defined in fft_multi.c via twiddle_3072_32.h */ +#define FFT_MULTI_TWIDDLE_SIZE 2048 +extern const int32_t multi_twiddle_real_32[]; +extern const int32_t multi_twiddle_imag_32[]; + +/* Constants for size 3 DFT */ +#define DFT3_COEFR -1073741824 /* int32(-0.5 * 2^31) */ +#define DFT3_COEFI 1859775393 /* int32(sqrt(3) / 2 * 2^31) */ +#define DFT3_SCALE 715827883 /* int32(1/3*2^31) */ + +#ifdef FFT_GENERIC + +void dft3_32(struct icomplex32 *x_in, struct icomplex32 *y) +{ + const struct icomplex32 c0 = {DFT3_COEFR, -DFT3_COEFI}; + const struct icomplex32 c1 = {DFT3_COEFR, DFT3_COEFI}; + struct icomplex32 x[3]; + struct icomplex32 p1, p2, sum; + int i; + + for (i = 0; i < 3; i++) { + x[i].real = Q_MULTSR_32X32((int64_t)x_in[i].real, DFT3_SCALE, 31, 31, 31); + x[i].imag = Q_MULTSR_32X32((int64_t)x_in[i].imag, DFT3_SCALE, 31, 31, 31); + } + + /* + * | 1 1 1 | + * c = | 1 c0 c1 | , x = [ x0 x1 x2 ] + * | 1 c1 c0 | + * + * y(0) = c(0,0) * x(0) + c(1,0) * x(1) + c(2,0) * x(2) + * y(1) = c(0,1) * x(0) + c(1,1) * x(1) + c(2,1) * x(2) + * y(2) = c(0,2) * x(0) + c(1,2) * x(1) + c(2,2) * x(2) + */ + + /* y(0) = 1 * x(0) + 1 * x(1) + 1 * x(2) */ + icomplex32_adds(&x[0], &x[1], &sum); + icomplex32_adds(&x[2], &sum, &y[0]); + + /* y(1) = 1 * x(0) + c0 * x(1) + c1 * x(2) */ + icomplex32_mul(&c0, &x[1], &p1); + icomplex32_mul(&c1, &x[2], &p2); + icomplex32_adds(&p1, &p2, &sum); + icomplex32_adds(&x[0], &sum, &y[1]); + + /* y(2) = 1 * x(0) + c1 * x(1) + c0 * x(2) */ + icomplex32_mul(&c1, &x[1], &p1); + icomplex32_mul(&c0, &x[2], &p2); + icomplex32_adds(&p1, &p2, &sum); + icomplex32_adds(&x[0], &sum, &y[2]); +} + +void fft_multi_execute_32(struct fft_multi_plan *plan, bool ifft) +{ + struct icomplex32 x[FFT_MULTI_COUNT_MAX]; + struct icomplex32 y[FFT_MULTI_COUNT_MAX]; + struct icomplex32 t, c; + int i, j, k, m; + + /* Handle 2^N FFT */ + if (plan->num_ffts == 1) { + memset(plan->outb32, 0, plan->fft_size * sizeof(struct icomplex32)); + fft_execute_32(plan->fft_plan[0], ifft); + return; + } + +#ifdef DEBUG_DUMP_TO_FILE + FILE *fh1 = fopen("debug_fft_multi_int1.txt", "w"); + FILE *fh2 = fopen("debug_fft_multi_int2.txt", "w"); + FILE *fh3 = fopen("debug_fft_multi_twiddle.txt", "w"); + FILE *fh4 = fopen("debug_fft_multi_dft_out.txt", "w"); +#endif + + /* convert to complex conjugate for IFFT */ + if (ifft) { + for (i = 0; i < plan->total_size; i++) + icomplex32_conj(&plan->inb32[i]); + } + + /* Copy input buffers */ + k = 0; + for (i = 0; i < plan->fft_size; i++) + for (j = 0; j < plan->num_ffts; j++) + plan->tmp_i32[j][i] = plan->inb32[k++]; + + /* Clear output buffers and call individual FFTs*/ + for (j = 0; j < plan->num_ffts; j++) { + memset(&plan->tmp_o32[j][0], 0, plan->fft_size * sizeof(struct icomplex32)); + fft_execute_32(plan->fft_plan[j], 0); + } + +#ifdef DEBUG_DUMP_TO_FILE + for (j = 0; j < plan->num_ffts; j++) + for (i = 0; i < plan->fft_size; i++) + fprintf(fh1, "%d %d\n", plan->tmp_o32[j][i].real, plan->tmp_o32[j][i].imag); +#endif + + /* Multiply with twiddle factors */ + m = FFT_MULTI_TWIDDLE_SIZE / 2 / plan->fft_size; + for (j = 1; j < plan->num_ffts; j++) { + for (i = 0; i < plan->fft_size; i++) { + c = plan->tmp_o32[j][i]; + k = j * i * m; + t.real = multi_twiddle_real_32[k]; + t.imag = multi_twiddle_imag_32[k]; + // fprintf(fh3, "%d %d\n", t.real, t.imag); + icomplex32_mul(&t, &c, &plan->tmp_o32[j][i]); + } + } + +#ifdef DEBUG_DUMP_TO_FILE + for (j = 0; j < plan->num_ffts; j++) + for (i = 0; i < plan->fft_size; i++) + fprintf(fh2, "%d %d\n", plan->tmp_o32[j][i].real, plan->tmp_o32[j][i].imag); +#endif + + /* DFT of size 3 */ + j = plan->fft_size; + k = 2 * plan->fft_size; + for (i = 0; i < plan->fft_size; i++) { + x[0] = plan->tmp_o32[0][i]; + x[1] = plan->tmp_o32[1][i]; + x[2] = plan->tmp_o32[2][i]; + dft3_32(x, y); + plan->outb32[i] = y[0]; + plan->outb32[i + j] = y[1]; + plan->outb32[i + k] = y[2]; + } + +#ifdef DEBUG_DUMP_TO_FILE + for (i = 0; i < plan->total_size; i++) + fprintf(fh4, "%d %d\n", plan->outb32[i].real, plan->outb32[i].imag); +#endif + + /* shift back for IFFT */ + + /* TODO: Check if time shift method for IFFT is more efficient or more accurate + * tmp = 1 / N * fft(X); + * x = tmp([1 N:-1:2]) + */ + if (ifft) { + /* + * no need to divide N as it is already done in the input side + * for Q1.31 format. Instead, we need to multiply N to compensate + * the shrink we did in the FFT transform. + */ + for (i = 0; i < plan->total_size; i++) { + /* Need to negate imag part to match reference */ + plan->outb32[i].imag = -plan->outb32[i].imag; + icomplex32_shift(&plan->outb32[i], plan->fft_plan[0]->len, + &plan->outb32[i]); + plan->outb32[i].real = sat_int32((int64_t)plan->outb32[i].real * 3); + plan->outb32[i].imag = sat_int32((int64_t)plan->outb32[i].imag * 3); + } + } + +#ifdef DEBUG_DUMP_TO_FILE + fclose(fh1); + fclose(fh2); + fclose(fh3); + fclose(fh4); +#endif +} + +#endif /* FFT_GENERIC */ diff --git a/src/math/fft/fft_multi_hifi3.c b/src/math/fft/fft_multi_hifi3.c new file mode 100644 index 000000000000..8f0c25e65f8c --- /dev/null +++ b/src/math/fft/fft_multi_hifi3.c @@ -0,0 +1,256 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// Copyright(c) 2025-2026 Intel Corporation. +// +// Author: Seppo Ingalsuo <seppo.ingalsuo@linux.intel.com> + +/** + * @file fft_multi_hifi3.c + * @brief HiFi3 optimized multi-radix FFT functions. + * + * This file provides HiFi3 optimized implementations of dft3_32() and + * fft_multi_execute_32() using Xtensa HiFi3 intrinsics. These replace + * the generic versions when HiFi3 or HiFi4 hardware is available. + * + * Key optimizations over the generic versions: + * - Packed {real, imag} processing using ae_int32x2 registers + * - 64-bit MAC accumulation for fused complex multiply-add + * - Saturating 32-bit arithmetic via AE_ADD32S/AE_SLAA32S + * - Vectorized conjugate, shift, and scale in the IFFT path + */ + +#include <sof/audio/module_adapter/module/generic.h> +#include <sof/audio/format.h> +#include <sof/math/icomplex32.h> +#include <sof/common.h> +#include <sof/math/fft.h> +#include <string.h> + +/* Twiddle factor tables defined in fft_multi.c via twiddle_3072_32.h */ +#define FFT_MULTI_TWIDDLE_SIZE 2048 +extern const int32_t multi_twiddle_real_32[]; +extern const int32_t multi_twiddle_imag_32[]; + +#ifdef FFT_HIFI3 + +#include <xtensa/tie/xt_hifi3.h> + +/** @brief Q1.31 constant -0.5 */ +#define DFT3_COEFR -1073741824 +/** @brief Q1.31 constant sqrt(3)/2 */ +#define DFT3_COEFI 1859775393 +/** @brief Q1.31 constant 1/3 */ +#define DFT3_SCALE 715827883 + +/** + * dft3_32() - Compute 3-point DFT of Q1.31 complex data (HiFi3). + * @param x_in Pointer to 3 input complex samples in Q1.31. + * @param y Pointer to 3 output complex samples in Q1.31. + * + * Computes the DFT matrix-vector product: + * + * | 1 1 1 | + * Y = | 1 c0 c1 | * X / 3 + * | 1 c1 c0 | + * + * where c0 = exp(-j*2*pi/3) and c1 = exp(+j*2*pi/3). + * Input is prescaled by 1/3 to prevent overflow. + * + * The two complex multiplies for each output bin are fused + * into a single 64-bit accumulator pair, avoiding intermediate + * rounding and saving instructions. + */ +void dft3_32(struct icomplex32 *x_in, struct icomplex32 *y) +{ + ae_int32x2 *p_in = (ae_int32x2 *)x_in; + ae_int32x2 *p_out = (ae_int32x2 *)y; + ae_int32x2 x0, x1, x2; + ae_int32x2 c0, c1; + ae_int32x2 scale; + ae_int32x2 sum, result; + ae_int64 re, im; + + /* + * Set up DFT3 twiddle factors as packed {H=real, L=imag}. + * c0 = {-0.5, -sqrt(3)/2} + * c1 = {-0.5, +sqrt(3)/2} + */ + scale = AE_MOVDA32(DFT3_COEFR); + c0 = AE_SEL32_LH(scale, AE_MOVDA32(-DFT3_COEFI)); + c1 = AE_SEL32_LH(scale, AE_MOVDA32(DFT3_COEFI)); + + /* Scale factor 1/3, broadcast to both H and L */ + scale = AE_MOVDA32(DFT3_SCALE); + + /* Load input samples as packed {real, imag} */ + x0 = AE_L32X2_I(p_in, 0); + x1 = AE_L32X2_I(p_in, 1 * sizeof(ae_int32x2)); + x2 = AE_L32X2_I(p_in, 2 * sizeof(ae_int32x2)); + + /* Scale all inputs by 1/3 to prevent overflow */ + x0 = AE_MULFP32X2RS(x0, scale); + x1 = AE_MULFP32X2RS(x1, scale); + x2 = AE_MULFP32X2RS(x2, scale); + + /* y[0] = x[0] + x[1] + x[2] */ + sum = AE_ADD32S(x0, x1); + AE_S32X2_I(AE_ADD32S(sum, x2), p_out, 0); + + /* + * y[1] = x[0] + c0 * x[1] + c1 * x[2] + * + * Fuse two complex multiplies and their sum into a single + * 64-bit accumulator pair to avoid intermediate rounding. + * + * Real part: c0.re*x1.re - c0.im*x1.im + c1.re*x2.re - c1.im*x2.im + * Imag part: c0.re*x1.im + c0.im*x1.re + c1.re*x2.im + c1.im*x2.re + */ + re = AE_MULF32S_HH(c0, x1); /* c0.re * x1.re */ + AE_MULSF32S_LL(re, c0, x1); /* -= c0.im * x1.im */ + AE_MULAF32S_HH(re, c1, x2); /* += c1.re * x2.re */ + AE_MULSF32S_LL(re, c1, x2); /* -= c1.im * x2.im */ + + im = AE_MULF32S_HL(c0, x1); /* c0.re * x1.im */ + AE_MULAF32S_LH(im, c0, x1); /* += c0.im * x1.re */ + AE_MULAF32S_HL(im, c1, x2); /* += c1.re * x2.im */ + AE_MULAF32S_LH(im, c1, x2); /* += c1.im * x2.re */ + + result = AE_ROUND32X2F64SSYM(re, im); + AE_S32X2_I(AE_ADD32S(x0, result), p_out, sizeof(ae_int32x2)); + + /* + * y[2] = x[0] + c1 * x[1] + c0 * x[2] + * + * Same structure as y[1] but with swapped coefficients. + */ + re = AE_MULF32S_HH(c1, x1); /* c1.re * x1.re */ + AE_MULSF32S_LL(re, c1, x1); /* -= c1.im * x1.im */ + AE_MULAF32S_HH(re, c0, x2); /* += c0.re * x2.re */ + AE_MULSF32S_LL(re, c0, x2); /* -= c0.im * x2.im */ + + im = AE_MULF32S_HL(c1, x1); /* c1.re * x1.im */ + AE_MULAF32S_LH(im, c1, x1); /* += c1.im * x1.re */ + AE_MULAF32S_HL(im, c0, x2); /* += c0.re * x2.im */ + AE_MULAF32S_LH(im, c0, x2); /* += c0.im * x2.re */ + + result = AE_ROUND32X2F64SSYM(re, im); + AE_S32X2_I(AE_ADD32S(x0, result), p_out, 2 * sizeof(ae_int32x2)); +} + +/** + * fft_multi_execute_32() - Execute multi-radix FFT/IFFT (HiFi3). + * @param plan Pointer to multi-FFT plan. + * @param ifft False for FFT, true for IFFT. + * + * Performs a composite FFT of size N = num_ffts * fft_size (e.g. 3 * 512 = 1536). + * For power-of-two sizes the call is forwarded to fft_execute_32(). + * + * HiFi3 optimizations vs. the generic path: + * - IFFT conjugate uses packed AE_SEL32_HL / AE_NEG32S + * - Twiddle multiply uses fused 64-bit MAC (no intermediate rounding) + * - IFFT shift-back combines negate, shift, and *3 scale in HiFi3 ops + */ +void fft_multi_execute_32(struct fft_multi_plan *plan, bool ifft) +{ + struct icomplex32 x[FFT_MULTI_COUNT_MAX]; + struct icomplex32 y[FFT_MULTI_COUNT_MAX]; + ae_int32x2 *p_src; + ae_int32x2 *p_dst; + ae_int32x2 sample, sample_neg, tw, data; + ae_int64 re, im; + int i, j, k, m; + + /* Handle 2^N FFT */ + if (plan->num_ffts == 1) { + memset(plan->outb32, 0, plan->fft_size * sizeof(struct icomplex32)); + fft_execute_32(plan->fft_plan[0], ifft); + return; + } + + /* Convert to complex conjugate for IFFT */ + if (ifft) { + p_src = (ae_int32x2 *)plan->inb32; + for (i = 0; i < plan->total_size; i++) { + AE_L32X2_IP(sample, p_src, 0); + sample_neg = AE_NEG32S(sample); + sample = AE_SEL32_HL(sample, sample_neg); + AE_S32X2_IP(sample, p_src, sizeof(ae_int32x2)); + } + } + + /* Copy input buffers (interleaved -> per-FFT) */ + k = 0; + for (i = 0; i < plan->fft_size; i++) + for (j = 0; j < plan->num_ffts; j++) + plan->tmp_i32[j][i] = plan->inb32[k++]; + + /* Clear output buffers and call individual FFTs */ + for (j = 0; j < plan->num_ffts; j++) { + memset(&plan->tmp_o32[j][0], 0, plan->fft_size * sizeof(struct icomplex32)); + fft_execute_32(plan->fft_plan[j], 0); + } + + /* Multiply with twiddle factors using HiFi3 complex multiply */ + m = FFT_MULTI_TWIDDLE_SIZE / 2 / plan->fft_size; + for (j = 1; j < plan->num_ffts; j++) { + p_dst = (ae_int32x2 *)plan->tmp_o32[j]; + for (i = 0; i < plan->fft_size; i++) { + k = j * i * m; + /* + * Build twiddle as packed {H=real, L=imag}. + * Use AE_SEL32_LH to pack two scalar loads. + */ + tw = AE_SEL32_LH(AE_MOVDA32(multi_twiddle_real_32[k]), + AE_MOVDA32(multi_twiddle_imag_32[k])); + + data = AE_L32X2_I(p_dst, 0); + + /* Complex multiply: tw * data */ + re = AE_MULF32S_HH(tw, data); + AE_MULSF32S_LL(re, tw, data); + im = AE_MULF32S_HL(tw, data); + AE_MULAF32S_LH(im, tw, data); + + AE_S32X2_IP(AE_ROUND32X2F64SSYM(re, im), + p_dst, sizeof(ae_int32x2)); + } + } + + /* DFT of size 3 */ + j = plan->fft_size; + k = 2 * plan->fft_size; + for (i = 0; i < plan->fft_size; i++) { + x[0] = plan->tmp_o32[0][i]; + x[1] = plan->tmp_o32[1][i]; + x[2] = plan->tmp_o32[2][i]; + dft3_32(x, y); + plan->outb32[i] = y[0]; + plan->outb32[i + j] = y[1]; + plan->outb32[i + k] = y[2]; + } + + /* Shift back for IFFT */ + if (ifft) { + int len = plan->fft_plan[0]->len; + + p_dst = (ae_int32x2 *)plan->outb32; + for (i = 0; i < plan->total_size; i++) { + AE_L32X2_IP(sample, p_dst, 0); + + /* Negate imag part to match reference */ + sample_neg = AE_NEG32S(sample); + sample = AE_SEL32_HL(sample, sample_neg); + + /* Shift left by FFT length to compensate shrink */ + sample = AE_SLAA32S(sample, len); + + /* Integer multiply by 3 (num_ffts) with saturation */ + data = AE_ADD32S(sample, sample); + sample = AE_ADD32S(data, sample); + + AE_S32X2_IP(sample, p_dst, sizeof(ae_int32x2)); + } + } +} + +#endif /* FFT_HIFI3 */ diff --git a/src/math/fft/tune/README.md b/src/math/fft/tune/README.md index d0ff5781890e..e753716a8952 100644 --- a/src/math/fft/tune/README.md +++ b/src/math/fft/tune/README.md @@ -7,3 +7,9 @@ octave -q --eval "sof_export_twiddle(32, 'twiddle_32.h', 1024);" octave -q --eval "sof_export_twiddle(16, 'twiddle_16.h', 1024);" cp twiddle_32.h ../../../include/sof/audio/coefficients/fft/ cp twiddle_16.h ../../../include/sof/audio/coefficients/fft/ + +To generate the twiddle factors for the non-power-of-two FFT implementation for max +size 3072 run these shell commands: + +octave -q --eval "sof_export_twiddle(32, 'twiddle_3072_32.h', 2048, 3072, 'FFT_MULTI_TWIDDLE_SIZE', 'multi_twiddle');" +cp twiddle_3072_32.h ../../../include/sof/audio/coefficients/fft/ diff --git a/src/math/fft/tune/sof_export_twiddle.m b/src/math/fft/tune/sof_export_twiddle.m index 48bf069e391f..9e6b1489c8b5 100644 --- a/src/math/fft/tune/sof_export_twiddle.m +++ b/src/math/fft/tune/sof_export_twiddle.m @@ -1,15 +1,18 @@ -% sof_export_twiddle(bits, fn, fft_size_max) +% sof_export_twiddle(bits, fn, fft_size_max, denom, str_size_max, str_var) % % Input % bits - Number of bits for data, 16 or 32 % fn - File name, defaults to twiddle.h % fft_size_max - Number of twiddle factors, defaults to 1024 if omitted +% denom - divide index * 2 * pi by denom instead of fft_size_max, same if omitted +% str_size_max - macro name for values array size, FFT_SIZE_MAX if omitted +% str_var - variable name prefix, twiddle if omitted % SPDX-License-Identifier: BSD-3-Clause % % Copyright (c) 2022, Intel Corporation. All rights reserved. -function sof_export_twiddle(bits, fn, fft_size_max) +function sof_export_twiddle(bits, fn, fft_size_max, denom, str_size_max, str_var) if nargin < 2 fn = 'twiddle.h'; @@ -19,6 +22,18 @@ function sof_export_twiddle(bits, fn, fft_size_max) fft_size_max = 1024; end +if nargin < 4 + denom = fft_size_max; +end + +if nargin < 5 + str_size_max = 'FFT_SIZE_MAX'; +end + +if nargin < 6 + str_var = 'twiddle'; +end + switch bits case 16, qx = 1; @@ -34,8 +49,8 @@ function sof_export_twiddle(bits, fn, fft_size_max) hcaps = upper(hname); i = 0:(fft_size_max - 1); -twiddle_real = cos(i * 2 * pi / fft_size_max); -twiddle_imag = -sin(i * 2 * pi / fft_size_max); +twiddle_real = cos(i * 2 * pi / denom); +twiddle_imag = -sin(i * 2 * pi / denom); year = datestr(now(), 'yyyy'); fh = fopen(fn, 'w'); @@ -48,12 +63,14 @@ function sof_export_twiddle(bits, fn, fft_size_max) fprintf(fh, '#ifndef __INCLUDE_%s_H__\n', hcaps); fprintf(fh, '#define __INCLUDE_%s_H__\n\n', hcaps); fprintf(fh, '#include <stdint.h>\n\n'); -fprintf(fh, '#define FFT_SIZE_MAX %d\n\n', fft_size_max); +fprintf(fh, '#define %s\t%d\n\n', str_size_max, fft_size_max); fprintf(fh, '/* in Q1.%d, generated from cos(i * 2 * pi / FFT_SIZE_MAX) */\n', qy); -c_export_int(fh, 'twiddle_real', 'FFT_SIZE_MAX', twiddle_real, qx, qy); +str_real = sprintf('%s_real', str_var); +c_export_int(fh, str_real, str_size_max, twiddle_real, qx, qy); fprintf(fh, '/* in Q1.%d, generated from sin(i * 2 * pi / FFT_SIZE_MAX) */\n', qy); -c_export_int(fh, 'twiddle_imag', 'FFT_SIZE_MAX', twiddle_imag, qx, qy); +str_imag = sprintf('%s_imag', str_var); +c_export_int(fh, str_imag, str_size_max, twiddle_imag, qx, qy); fprintf(fh, '#endif\n'); fclose(fh); diff --git a/src/math/numbers.c b/src/math/numbers.c index b4d6ade10421..df4f822c749a 100644 --- a/src/math/numbers.c +++ b/src/math/numbers.c @@ -15,6 +15,9 @@ #include <rtos/symbol.h> #include <stdint.h> +/* see numbers.h */ +#ifdef USE_SOF_GCD + /* This function returns the greatest common divisor of two numbers * If both parameters are 0, gcd(0, 0) returns 0 * If first parameters is 0 or second parameter is 0, gcd(0, b) returns b @@ -74,6 +77,7 @@ int gcd(int a, int b) return a << k; } EXPORT_SYMBOL(gcd); +#endif /* USE_SOF_GCD */ #if CONFIG_NUMBERS_VECTOR_FIND diff --git a/src/math/power.c b/src/math/power.c index fd120271d160..ff9e5f911d5f 100644 --- a/src/math/power.c +++ b/src/math/power.c @@ -10,6 +10,7 @@ #include <sof/audio/format.h> #include <sof/trace/trace.h> #include <sof/math/power.h> + #include <sof/lib/uuid.h> #include <ipc/trace.h> #include <user/trace.h> diff --git a/src/math/sqrt_int16.c b/src/math/sqrt_int16.c index 9068717d638d..81772fc90c3c 100644 --- a/src/math/sqrt_int16.c +++ b/src/math/sqrt_int16.c @@ -29,7 +29,7 @@ * Arguments : uint16_t u * Return Type : int32_t */ -uint16_t sqrt_int16(uint16_t u) +uint16_t sofm_sqrt_int16(uint16_t u) { static const int32_t iv1[193] = { 46341, 46702, 47059, 47415, 47767, 48117, 48465, 48809, 49152, 49492, 49830, 50166, @@ -146,4 +146,4 @@ uint16_t sqrt_int16(uint16_t u) return y; } -EXPORT_SYMBOL(sqrt_int16); +EXPORT_SYMBOL(sofm_sqrt_int16); diff --git a/src/math/sqrt_int32.c b/src/math/sqrt_int32.c new file mode 100644 index 000000000000..6d412b33f33a --- /dev/null +++ b/src/math/sqrt_int32.c @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// Copyright(c) 2026 Intel Corporation. +// +// Author: Seppo Ingalsuo <seppo.ingalsuo@linux.intel.com> +// + +#include <sof/math/sqrt.h> +#include <rtos/symbol.h> +#include <stdint.h> + +/* Lookup table for square root for initial value in iteration, + * created with Octave commands: + * + * arg=((1:64) * 2^25) / 2^30; lut = int32(sqrt(arg) * 2^30); + * fmt=['static const int32_t sqrt_int32_lut[] = {' repmat(' %d,',1, numel(lut)-1) ' %d};\n']; + * fprintf(fmt, lut) + */ +static const int32_t sqrt_int32_lut[] = { + 189812531, 268435456, 328764948, 379625062, 424433723, 464943848, 502196753, + 536870912, 569437594, 600239927, 629536947, 657529896, 684378814, 710213460, + 735140772, 759250125, 782617115, 805306368, 827373642, 848867446, 869830292, + 890299688, 910308921, 929887697, 949062656, 967857801, 986294844, 1004393507, + 1022171763, 1039646051, 1056831447, 1073741824, 1090389977, 1106787739, 1122946079, + 1138875187, 1154584553, 1170083026, 1185378878, 1200479854, 1215393219, 1230125796, + 1244684005, 1259073893, 1273301169, 1287371222, 1301289153, 1315059792, 1328687719, + 1342177280, 1355532607, 1368757628, 1381856086, 1394831545, 1407687407, 1420426919, + 1433053185, 1445569171, 1457977717, 1470281545, 1482483261, 1494585366, 1506590260, + 1518500250 +}; + +/* sofm_sqrt_int32() - Calculate 32-bit fractional square root function. */ +int32_t sofm_sqrt_int32(int32_t n) +{ + uint64_t n_shifted; + uint32_t x; + int shift; + + if (n < 1) + return 0; + + /* Scale input argument with 2^n, where n is even. + * Scale calculated sqrt() with 2^(-n/2). + */ + shift = (__builtin_clz(n) - 1) & ~1; /* Make even by clearing LSB */ + n = n << shift; + shift >>= 1; /* Divide by 2 for sqrt shift compensation */ + + /* For Q2.30 divide */ + n_shifted = (uint64_t)n << 30; + + /* Get initial guess from LUT, idx = 0 .. 63 */ + x = sqrt_int32_lut[n >> 25]; + + /* Iterate x(n+1) = 1/2 * (x(n) + N / x(n)) + * N is argument for square root + * x(n) is initial guess. Do two iterations. + */ + x = (uint32_t)(((n_shifted / x + x) + 1) >> 1); + x = (uint32_t)(((n_shifted / x + x) + 1) >> 1); + + return (int32_t)(x >> shift); +} +EXPORT_SYMBOL(sofm_sqrt_int32); diff --git a/src/math/trig.c b/src/math/trig.c index 32613d393034..1c1e221a5887 100644 --- a/src/math/trig.c +++ b/src/math/trig.c @@ -13,18 +13,13 @@ #include <sof/math/cordic.h> #include <stdint.h> -/* Use a local definition to avoid adding a dependency on <math.h> */ -#define _M_PI 3.14159265358979323846 /* pi */ +#define CORDIC_SINE_COS_LUT_Q29 652032874 /* deg = 69.586061, int32(1.214505869895220 * 2^29) */ + +#define CORDIC_SINCOS_PIOVERTWO_Q28 421657428 /* int32(pi / 2 * 2^28) */ +#define CORDIC_SINCOS_PI_Q28 843314857 /* int32(pi * 2^28) */ +#define CORDIC_SINCOS_TWOPI_Q28 1686629713 /* int32(2 * pi * 2^28) */ +#define CORDIC_SINCOS_ONEANDHALFPI_Q28 1264972285 /* int32(1.5 * pi * 2^28) */ -/* 652032874 , deg = 69.586061*/ -const int32_t cordic_sine_cos_lut_q29fl = Q_CONVERT_FLOAT(1.214505869895220, 29); -/* 1686629713, deg = 90.000000 */ -const int32_t cordic_sine_cos_piovertwo_q30fl = Q_CONVERT_FLOAT(_M_PI / 2, 30); -/* 421657428 , deg = 90.000000 */ -const int32_t cord_sincos_piovertwo_q28fl = Q_CONVERT_FLOAT(_M_PI / 2, 28); -/* 843314857, deg = 90.000000 */ -const int32_t cord_sincos_piovertwo_q29fl = Q_CONVERT_FLOAT(_M_PI / 2, 29); -/* arc trignometry constant*/ /** * CORDIC-based approximation of sine and cosine * \+----------+----------------------------------------+--------------------+-------------------+ @@ -36,20 +31,25 @@ const int32_t cord_sincos_piovertwo_q29fl = Q_CONVERT_FLOAT(_M_PI / 2, 29); * \|1686629713| Q_CONVERT_FLOAT(1.5707963267341256, 30)| 89.9999999965181| 1.57079632673413 | * \+----------+----------------------------------------+--------------------+-------------------+ */ -/* 379625062, deg = 81.0284683480568475 or round(1.4142135605216026*2^28) */ -const int32_t cord_arcsincos_q28fl = Q_CONVERT_FLOAT(1.4142135605216026 / 2, 28); -/* 1073741824, deg = 57.2957795130823229 or round(1*2^30)*/ -const int32_t cord_arcsincos_q30fl = Q_CONVERT_FLOAT(1.0000000000000000, 30); + +#define CORDIC_ARCSINCOS_SQRT2_DIV4_Q30 379625062 /* int32(sqrt(2) / 4 * 2^30) */ +#define CORDIC_ARCSINCOS_ONE_Q30 1073741824 /* int32(1 * 2^30) */ + /** * CORDIC-based approximation of sine, cosine and complex exponential */ void cordic_approx(int32_t th_rad_fxp, int32_t a_idx, int32_t *sign, int32_t *b_yn, int32_t *xn, int32_t *th_cdc_fxp) { + int32_t direction; + int32_t abs_th; int32_t b_idx; - int32_t xtmp; - int32_t ytmp; - *sign = 1; + int32_t xn_local = CORDIC_SINE_COS_LUT_Q29; + int32_t yn_local = 0; + int32_t xtmp = CORDIC_SINE_COS_LUT_Q29; + int32_t ytmp = 0; + int shift; + /* Addition or subtraction by a multiple of pi/2 is done in the data type * of the input. When the fraction length is 29, then the quantization error * introduced by the addition or subtraction of pi/2 is done with 29 bits of @@ -58,57 +58,46 @@ void cordic_approx(int32_t th_rad_fxp, int32_t a_idx, int32_t *sign, int32_t *b_ * without overflow.Increase of fractionLength makes the addition or * subtraction of a multiple of pi/2 more precise */ - if (th_rad_fxp > cord_sincos_piovertwo_q28fl) { - if ((th_rad_fxp - cord_sincos_piovertwo_q29fl) <= cord_sincos_piovertwo_q28fl) { - th_rad_fxp -= cord_sincos_piovertwo_q29fl; - *sign = -1; - } else { - th_rad_fxp -= cordic_sine_cos_piovertwo_q30fl; - } - } else if (th_rad_fxp < -cord_sincos_piovertwo_q28fl) { - if ((th_rad_fxp + cord_sincos_piovertwo_q29fl) >= -cord_sincos_piovertwo_q28fl) { - th_rad_fxp += cord_sincos_piovertwo_q29fl; - *sign = -1; + abs_th = (th_rad_fxp >= 0) ? th_rad_fxp : -th_rad_fxp; + direction = (th_rad_fxp >= 0) ? 1 : -1; + *sign = 1; + if (abs_th > CORDIC_SINCOS_PIOVERTWO_Q28) { + if (abs_th <= CORDIC_SINCOS_ONEANDHALFPI_Q28) { + th_rad_fxp -= direction * CORDIC_SINCOS_PI_Q28; + *sign = -1; } else { - th_rad_fxp += cordic_sine_cos_piovertwo_q30fl; + th_rad_fxp -= direction * CORDIC_SINCOS_TWOPI_Q28; } } th_rad_fxp <<= 2; - *b_yn = 0; - *xn = cordic_sine_cos_lut_q29fl; - xtmp = cordic_sine_cos_lut_q29fl; - ytmp = 0; /* Calculate the correct coefficient values from rotation angle. * Find difference between the coefficients from the lookup table * and those from the calculation */ for (b_idx = 0; b_idx < a_idx; b_idx++) { - if (th_rad_fxp < 0) { - th_rad_fxp += cordic_lookup[b_idx]; - *xn += ytmp; - *b_yn -= xtmp; - } else { - th_rad_fxp -= cordic_lookup[b_idx]; - *xn -= ytmp; - *b_yn += xtmp; - } - xtmp = *xn >> (b_idx + 1); - ytmp = *b_yn >> (b_idx + 1); + direction = (th_rad_fxp >= 0) ? 1 : -1; + shift = b_idx + 1; + th_rad_fxp -= direction * cordic_lookup[b_idx]; + xn_local -= direction * ytmp; + yn_local += direction * xtmp; + xtmp = xn_local >> shift; + ytmp = yn_local >> shift; } - /* Q2.30 format -sine, cosine*/ + + /* Write back results once */ + *xn = xn_local; + *b_yn = yn_local; *th_cdc_fxp = th_rad_fxp; } EXPORT_SYMBOL(cordic_approx); /** * CORDIC-based approximation for inverse cosine - * Arguments : int32_t cosvalue - * int16_t numiters - * Return Type : int32_t + * cosvalue is Q2.30, return value is angle in Q3.29 format */ -int32_t is_scalar_cordic_acos(int32_t cosvalue, int16_t numiters) +int32_t is_scalar_cordic_acos(int32_t cosvalue, int numiters) { int32_t xdshift; int32_t ydshift; @@ -118,25 +107,22 @@ int32_t is_scalar_cordic_acos(int32_t cosvalue, int16_t numiters) int32_t y = 0; int32_t z = 0; int32_t sign; - int32_t b_i; - int i; + int b_i; int j; - int k; /* Initialize the variables for the cordic iteration * angles less than pi/4, we initialize (x,y) along the x-axis. * angles greater than or equal to pi/4, we initialize (x,y) * along the y-axis. This improves the accuracy of the algorithm * near the edge of the domain of convergence + * + * Note: not pi/4 but sqrt(2)/4 is used as the threshold */ - if ((cosvalue >> 1) < cord_arcsincos_q28fl) { - x = 0; - y = cord_arcsincos_q30fl; + if (cosvalue < CORDIC_ARCSINCOS_SQRT2_DIV4_Q30) { + y = CORDIC_ARCSINCOS_ONE_Q30; z = PI_DIV2_Q3_29; } else { - x = cord_arcsincos_q30fl; - y = 0; - z = 0; + x = CORDIC_ARCSINCOS_ONE_Q30; } /* DCORDIC(Double CORDIC) algorithm */ @@ -144,20 +130,14 @@ int32_t is_scalar_cordic_acos(int32_t cosvalue, int16_t numiters) /* CORDIC method,where the iteration step value changes EVERY time, i.e. on */ /* each iteration, in the double iteration method, the iteration step value */ /* is repeated twice and changes only through one iteration */ - i = numiters - 1; - for (b_i = 0; b_i < i; b_i++) { + for (b_i = 0; b_i < numiters; b_i++) { j = (b_i + 1) << 1; if (j >= 31) j = 31; - if (b_i < 31) - k = b_i; - else - k = 31; - - xshift = x >> k; + xshift = x >> b_i; + yshift = y >> b_i; xdshift = x >> j; - yshift = y >> k; ydshift = y >> j; /* Do nothing if x currently equals the target value. Allowed for * double rotations algorithms, as it is equivalent to rotating by @@ -184,11 +164,9 @@ int32_t is_scalar_cordic_acos(int32_t cosvalue, int16_t numiters) /** * CORDIC-based approximation for inverse sine - * Arguments : int32_t sinvalue - * int16_t numiters - * Return Type : int32_t + * sinvalue is Q2.30, return value is angle in Q2.30 format */ -int32_t is_scalar_cordic_asin(int32_t sinvalue, int16_t numiters) +int32_t is_scalar_cordic_asin(int32_t sinvalue, int numiters) { int32_t xdshift; int32_t ydshift; @@ -198,25 +176,22 @@ int32_t is_scalar_cordic_asin(int32_t sinvalue, int16_t numiters) int32_t y = 0; int32_t z = 0; int32_t sign; - int32_t b_i; - int i; + int b_i; int j; - int k; /* Initialize the variables for the cordic iteration * angles less than pi/4, we initialize (x,y) along the x-axis. * angles greater than or equal to pi/4, we initialize (x,y) * along the y-axis. This improves the accuracy of the algorithm * near the edge of the domain of convergence + * + * Note: Instead of pi/4, sqrt(2)/4 is used as the threshold */ - if ((sinvalue >> 1) > cord_arcsincos_q28fl) { - x = 0; - y = cord_arcsincos_q30fl; + if (sinvalue > CORDIC_ARCSINCOS_SQRT2_DIV4_Q30) { + y = CORDIC_ARCSINCOS_ONE_Q30; z = PI_DIV2_Q3_29; } else { - x = cord_arcsincos_q30fl; - y = 0; - z = 0; + x = CORDIC_ARCSINCOS_ONE_Q30; } /* DCORDIC(Double CORDIC) algorithm */ @@ -224,21 +199,15 @@ int32_t is_scalar_cordic_asin(int32_t sinvalue, int16_t numiters) /* CORDIC method,where the iteration step value changes EVERY time, i.e. on */ /* each iteration, in the double iteration method, the iteration step value */ /* is repeated twice and changes only through one iteration */ - i = numiters - 1; - for (b_i = 0; b_i < i; b_i++) { + for (b_i = 0; b_i < numiters; b_i++) { j = (b_i + 1) << 1; if (j >= 31) j = 31; - if (b_i < 31) - k = b_i; - else - k = 31; - - xshift = x >> k; - xdshift = x >> j; - yshift = y >> k; + xshift = x >> b_i; + yshift = y >> b_i; ydshift = y >> j; + xdshift = x >> j; /* Do nothing if x currently equals the target value. Allowed for * double rotations algorithms, as it is equivalent to rotating by * the same angle in opposite directions sequentially. Accounts for @@ -263,13 +232,9 @@ int32_t is_scalar_cordic_asin(int32_t sinvalue, int16_t numiters) } /** - * approximated complex result - * Arguments : int32_t sign - * int32_t b_yn - * int32_t xn - * enum type - * struct cordic_cmpx - * Return Type : none + * cmpx_cexp() - CORDIC-based approximation of complex exponential e^(j*THETA) + * + * The sine and cosine values are in Q2.30 format from cordic_approx()function. */ void cmpx_cexp(int32_t sign, int32_t b_yn, int32_t xn, cordic_cfg type, struct cordic_cmpx *cexp) { diff --git a/src/math/window.c b/src/math/window.c index 4795112ffc0f..39739ef24be3 100644 --- a/src/math/window.c +++ b/src/math/window.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: BSD-3-Clause // -// Copyright(c) 2022 Intel Corporation. All rights reserved. +// Copyright(c) 2022-2025 Intel Corporation. // // Author: Seppo Ingalsuo <seppo.ingalsuo@linux.intel.com> @@ -16,6 +16,7 @@ #define WIN_ONE_Q31 INT32_MAX #define WIN_05_Q31 Q_CONVERT_FLOAT(0.5, 31) +#define WIN_PI_Q28 Q_CONVERT_FLOAT(3.1415926536, 28) #define WIN_TWO_PI_Q28 Q_CONVERT_FLOAT(6.2831853072, 28) #define WIN_085_Q31 Q_CONVERT_FLOAT(0.85, 31) @@ -29,12 +30,10 @@ /* Common approximations to match e.g. Octave */ #define WIN_HAMMING_A0_Q30 Q_CONVERT_FLOAT(0.54, 30) #define WIN_HAMMING_A1_Q30 Q_CONVERT_FLOAT(0.46, 30) +#define WIN_HAMMING_A0_Q31 Q_CONVERT_FLOAT(0.54, 31) +#define WIN_HAMMING_A1_Q31 Q_CONVERT_FLOAT(0.46, 31) -/** - * \brief Return rectangular window, simply values of one - * \param[in,out] win Output vector with coefficients - * \param[in] length Length of coefficients vector - */ +/* Rectangular window */ void win_rectangular_16b(int16_t *win, int length) { int i; @@ -43,14 +42,15 @@ void win_rectangular_16b(int16_t *win, int length) win[i] = WIN_ONE_Q15; } -/** - * \brief Calculate Blackman window function, reference - * https://en.wikipedia.org/wiki/Window_function#Blackman_window +void win_rectangular_32b(int32_t *win, int length) +{ + int i; - * \param[in,out] win Output vector with coefficients - * \param[in] length Length of coefficients vector - * \param[in] a0 Parameter for window shape, use e.g. 0.42 as Q1.15 - */ + for (i = 0; i < length; i++) + win[i] = WIN_ONE_Q31; +} + +/* Blackman window */ void win_blackman_16b(int16_t win[], int length, int16_t a0) { const int32_t a1 = Q_CONVERT_FLOAT(0.5, 31); @@ -77,6 +77,64 @@ void win_blackman_16b(int16_t win[], int length, int16_t a0) } } +void win_blackman_32b(int32_t win[], int length, int32_t a0) +{ + const int32_t a1 = Q_CONVERT_FLOAT(0.5, 31); + int64_t val; + int32_t inv_length; + int32_t a; + int16_t alpha; + int32_t a2; + int32_t c1; + int32_t c2; + int n; + + alpha = WIN_ONE_Q31 - 2 * a0; /* Q1.31 */ + a2 = alpha << 15; /* Divided by 2 in Q1.31 */ + a = WIN_TWO_PI_Q28 / (length - 1); /* Q4.28 */ + inv_length = WIN_ONE_Q31 / length; + + for (n = 0; n < length; n++) { + c1 = cos_fixed_32b(a * n); + c2 = cos_fixed_32b(2 * n * Q_MULTSR_32X32((int64_t)a, inv_length, 28, 31, 28)); + val = a0 - Q_MULTSR_32X32((int64_t)a1, c1, 31, 31, 31) + + Q_MULTSR_32X32((int64_t)a2, c2, 31, 31, 31); + win[n] = sat_int32(val); + } +} + +/* Hann window */ +void win_hann_16b(int16_t win[], int length) +{ + int32_t val; + int32_t a; + int n; + + a = WIN_PI_Q28 / (length - 1); /* Q4.28 */ + for (n = 0; n < length; n++) { + /* Calculate sin(a * n)^2 */ + val = sin_fixed_32b(a * n); /* Q4.28 -> Q1.31 */ + val = Q_MULTSR_32X32((int64_t)val, val, 31, 31, 15); /* Q1.15 */ + win[n] = sat_int16(val); + } +} + +void win_hann_32b(int32_t win[], int length) +{ + int64_t val; + int32_t a; + int n; + + a = WIN_PI_Q28 / (length - 1); /* Q4.28 */ + for (n = 0; n < length; n++) { + /* Calculate sin(a * n)^2 */ + val = sin_fixed_32b(a * n); /* Q4.28 -> Q1.31 */ + val = Q_MULTSR_32X32((int64_t)val, val, 31, 31, 31); /* Q1.31 */ + win[n] = sat_int32(val); + } +} + +/* Hamming window */ void win_hamming_16b(int16_t win[], int length) { int32_t val; @@ -95,6 +153,23 @@ void win_hamming_16b(int16_t win[], int length) } } +void win_hamming_32b(int32_t win[], int length) +{ + int64_t val; + int32_t a; + int n; + + a = WIN_TWO_PI_Q28 / (length - 1); /* Q4.28 */ + for (n = 0; n < length; n++) { + /* Calculate 0.54 - 0.46 * cos(a * n) */ + val = cos_fixed_32b(a * n); /* Q4.28 -> Q1.31 */ + val = Q_MULTSR_32X32((int64_t)val, WIN_HAMMING_A1_Q31, 31, 31, 31); /* Q1.31 */ + val = WIN_HAMMING_A0_Q31 - val; + win[n] = sat_int32(val); + } +} + +/* Povey window */ void win_povey_16b(int16_t win[], int length) { int32_t cos_an; diff --git a/src/module/README.md b/src/module/README.md new file mode 100644 index 000000000000..3d79c948bdd8 --- /dev/null +++ b/src/module/README.md @@ -0,0 +1,111 @@ +# Audio Processing Modules (`src/module`) + +The `src/module` directory and the `src/include/module` headers define the Sound Open Firmware (SOF) modern Audio Processing Module API. This architecture abstracts the underlying OS and pipeline scheduler implementations from the actual audio signal processing logic, allowing modules to be written once and deployed either as statically linked core components or as dynamically loadable Zephyr EXT (LLEXT) modules. + +## Architecture Overview + +The SOF module architecture is built around three core concepts: + +1. **`struct module_interface`**: A standardized set of operations (`init`, `prepare`, `process`, `reset`, `free`, `set_configuration`) that every audio processing module implements. +2. **`struct processing_module`**: The runtime instantiated state of a module. It holds metadata, configuration objects, memory allocations (`module_resources`), and references to interconnected streams. +3. **Module Adapter (`src/audio/module_adapter`)**: The system glue layer. It masquerades as a legacy pipeline `comp_dev` to the SOF schedulers, but acts as a sandbox container for the `processing_module`. It intercepts IPC commands, manages the module's state machine, manages inputs/outputs, and safely calls the `module_interface` operations. + +```mermaid +graph TD + subgraph SOF Core Pipeline Scheduler + P[Pipeline Scheduler <br> LL/DP Domains] + end + + subgraph Module Adapter System Layer + MA[Module Adapter `comp_dev`] + IPC[IPC Command Dispatch] + MEM[Memory Resource Manager] + end + + subgraph Standardized Module Framework API + INF[`module_interface` Ops] + SRC[Source API <br> `source_get_data`] + SNK[Sink API <br> `sink_get_buffer`] + end + + subgraph Custom Audio Modules + VOL[Volume] + EQ[EQ FIR/IIR] + CUSTOM[Loadable 3rd Party <br> Zephyr LLEXT] + end + + P <-->|Execute| MA + IPC -->|Config/Triggers| MA + + MA -->|Invoke| INF + MA -->|Manage| MEM + + INF --> VOL + INF --> EQ + INF --> CUSTOM + + VOL -->|Read| SRC + VOL -->|Write| SNK + EQ -->|Read| SRC + EQ -->|Write| SNK +``` + +## Module State Machine + +Every processing module is strictly governed by a uniform runtime state machine managed by the `module_adapter`. Modules must adhere to the transitions defined by `enum module_state`: + +1. `MODULE_DISABLED`: The module is loaded but uninitialized, or has been freed. No memory is allocated. +2. `MODULE_INITIALIZED`: After a successful `.init()` call. The module parses its IPC configuration and allocates necessary local resources (delay lines, coefficient tables). +3. `MODULE_IDLE`: After a successful `.prepare()` call. Audio stream formats are fully negotiated and agreed upon (Stream params, channels, rate). +4. `MODULE_PROCESSING`: When the pipeline triggers a `START` command. The `.process()` callback is actively handling buffers. + +```mermaid +stateDiagram-v2 + [*] --> MODULE_DISABLED: Module Created + + MODULE_DISABLED --> MODULE_INITIALIZED: .init() / IPC NEW + MODULE_INITIALIZED --> MODULE_DISABLED: .free() / IPC FREE + + MODULE_INITIALIZED --> MODULE_IDLE: .prepare() / Pipeline Setup + MODULE_IDLE --> MODULE_INITIALIZED: .reset() / Pipeline Reset + + MODULE_IDLE --> MODULE_PROCESSING: .trigger(START) / IPC START + MODULE_PROCESSING --> MODULE_IDLE: .trigger(STOP/PAUSE) / IPC STOP +``` + +## Data Flows and Buffer Management + +Modules do not directly manipulate underlying DMA, ALSA, or Zephyr `comp_buffer` pointers. Instead, they interact via the decoupled **Source and Sink APIs**. This allows the adapter to seamlessly feed data from varying topological sources without changing module code. + +The flow operates primarily in a "get -> manipulate -> commit/release" pattern: + +```mermaid +sequenceDiagram + participant Adapter as Module Adapter + participant Mod as Processing Module (.process) + participant Src as Source API (Input) + participant Snk as Sink API (Output) + + Adapter->>Mod: Process Trigger (sources[], sinks[]) + + Mod->>Src: source_get_data(req_size) + Src-->>Mod: Provides read_ptr, available_bytes + + Mod->>Snk: sink_get_buffer(req_size) + Snk-->>Mod: Provides write_ptr, free_bytes + + note over Mod: Execute DSP Algorithm <br> (Read from read_ptr -> Write to write_ptr) + + Mod->>Src: source_release_data(consumed_bytes) + Mod->>Snk: sink_commit_buffer(produced_bytes) + + Mod-->>Adapter: Return Status +``` + +### Source API (`src/module/audio/source_api.c`) +- modules request data by calling `source_get_data_s16` or `s32`. This establishes an active read lock. +- Once done, the module must call `source_release_data()` releasing only the frames actually consumed. + +### Sink API (`src/module/audio/sink_api.c`) +- modules request destination buffers by calling `sink_get_buffer_s16` or `s32`. +- After processing into the provided memory array, the module marks the memory as valid by calling `sink_commit_buffer()` for the exact number of frames successfully written. diff --git a/src/platform/CMakeLists.txt b/src/platform/CMakeLists.txt index 1d0d7596cf3b..e43c21e1ac0d 100644 --- a/src/platform/CMakeLists.txt +++ b/src/platform/CMakeLists.txt @@ -11,6 +11,8 @@ elseif(CONFIG_IMX8 OR CONFIG_IMX8X) add_subdirectory(imx8) elseif(CONFIG_IMX8M) add_subdirectory(imx8m) +elseif(CONFIG_IMX8M_CM7) + add_subdirectory(imx8m_cm7) elseif(CONFIG_IMX8ULP) add_subdirectory(imx8ulp) elseif(CONFIG_AMD) @@ -25,4 +27,6 @@ elseif(CONFIG_MT8196) add_subdirectory(mt8196) elseif(CONFIG_MT8365) add_subdirectory(mt8365) +elseif(PLATFORM STREQUAL "qemu_xtensa") + add_subdirectory(qemu_xtensa) endif() diff --git a/src/platform/Kconfig b/src/platform/Kconfig index ebbe0540b7d4..2de2c5ed00da 100644 --- a/src/platform/Kconfig +++ b/src/platform/Kconfig @@ -145,6 +145,14 @@ config IMX8M help Select if your target platform is imx8m-compatible +config IMX8M_CM7 + bool "Build for NXP i.MX8MP CM7 core" + select BUILD_OUTPUT_BIN + select HOST_PTABLE + select IMX + help + Select if your target platform is imx8mp-compatible with cm7 core. + config IMX8ULP bool "Build for NXP i.MX8ULP" select XT_HAVE_RESET_VECTOR_ROM @@ -239,7 +247,7 @@ config ACP_7_0 select XT_WAITI_DELAY select XTENSA_EXCLUSIVE select AMD - select SCHEDULE_DMA_MULTI_CHANNEL + select AMD_BINARY_BUILD help Select if your target platform is acp_7_0-compatible @@ -332,21 +340,30 @@ config MT8196 endchoice -config MAX_CORE_COUNT +# +# For non-Zephyr builds like testbench, cmocka and SOF ALSA plugin, +# set core count separately. +# +if !ZEPHYR_SOF_MODULE + +config MP_MAX_NUM_CPUS int - default 5 if LUNARLAKE || PANTHERLAKE - default 4 if TIGERLAKE || NOVALAKE - default 3 if METEORLAKE - default 3 if WILDCATLAKE default 1 help Maximum number of cores per configuration +endif # !ZEPHYR_SOF_MODULE + +config MAX_CORE_COUNT + int + default MP_MAX_NUM_CPUS + help + Maximum number of cores per configuration + config CORE_COUNT int "Number of cores" - default MP_MAX_NUM_CPUS if KERNEL_BIN_NAME = "zephyr" - default MAX_CORE_COUNT - range 1 MAX_CORE_COUNT + default MP_MAX_NUM_CPUS + range 1 MP_MAX_NUM_CPUS help Number of used cores Lowering available core count could result in lower power consumption @@ -519,7 +536,9 @@ config RIMAGE_SIGNING_SCHEMA default "imx8" if IMX8 default "imx8x" if IMX8X default "imx8m" if IMX8M + default "imx8m_cm7" if IMX8M_CM7 default "imx8ulp" if IMX8ULP + default "imx95" if IMX95 default "rn" if RENOIR default "rmb" if REMBRANDT default "vangogh" if VANGOGH diff --git a/src/platform/amd/acp_6_3/lib/clk.c b/src/platform/amd/acp_6_3/lib/clk.c index 5a735d4241fa..f060aafc22c6 100644 --- a/src/platform/amd/acp_6_3/lib/clk.c +++ b/src/platform/amd/acp_6_3/lib/clk.c @@ -191,7 +191,7 @@ void acp_change_clock_notify(uint32_t clock_freq) acp_6_3_get_boot_ref_clock(&boot_ref_clk); - tr_info(&acp_clk_tr, "acp_change_clock_notify clock_freq : %d clock_type : %d", + tr_info(&acp_clk_tr, "clock_freq : %d clock_type : %d", clock_freq, clock_type); fraction_val = (float)(clock_freq / (float)1000000.0f); @@ -212,7 +212,7 @@ void acp_change_clock_notify(uint32_t clock_freq) bypass_cntl.bitfields.CLK1_BYPASS_DIV = 0xF; } else { did = (float)(boot_ref_clk / (float)fraction_val); - tr_info(&acp_clk_tr, "acp_change_clock_notify CLK Divider : %d boot_ref_clk : %d\n", + tr_info(&acp_clk_tr, "CLK Divider : %d boot_ref_clk : %d\n", (uint32_t)(did * 100), (uint32_t)boot_ref_clk); if (did > 62.0f) { @@ -251,7 +251,7 @@ void acp_change_clock_notify(uint32_t clock_freq) do { dfs_status.u32all = acp_reg_read_via_smn(CLK5_CLK1_DFS_STATUS, sizeof(int)); - tr_info(&acp_clk_tr, "acp_change_clock_notify ACLK1 CLK1_DIVIDER : %d dfsstatus %d ", + tr_info(&acp_clk_tr, "ACLK1 CLK1_DIVIDER : %d dfsstatus %d ", dfs_cntl.u32all, dfs_status.u32all); } while (dfs_status.bitfields.CLK1_DFS_DIV_REQ_IDLE == 0); updated_clk = acp_reg_read_via_smn(CLK5_CLK1_CURRENT_CNT, sizeof(int)); @@ -268,7 +268,7 @@ void acp_change_clock_notify(uint32_t clock_freq) dfs_cntl.u32all = acp_reg_read_via_smn(CLK5_CLK1_DFS_CNTL, sizeof(int)); - tr_info(&acp_clk_tr, "acp_change_clock_notify ACLK2 CLK1_DIVIDER:%d dfsstatus %d ", + tr_info(&acp_clk_tr, "ACLK2 CLK1_DIVIDER:%d dfsstatus %d ", dfs_cntl.u32all, dfs_status.u32all); } while (dfs_status.bitfields.CLK1_DFS_DIV_REQ_IDLE == 0); } @@ -281,7 +281,7 @@ void acp_change_clock_notify(uint32_t clock_freq) do { dfs_status.u32all = acp_reg_read_via_smn(CLK5_CLK0_DFS_STATUS, sizeof(int)); - tr_info(&acp_clk_tr, "acp_change_clock_notify SCLK CLK1_DIVIDER: %d", + tr_info(&acp_clk_tr, "SCLK CLK1_DIVIDER: %d", dfs_cntl.u32all); } while (dfs_status.bitfields.CLK1_DFS_DIV_REQ_IDLE == 0); diff --git a/src/platform/amd/acp_7_0/include/arch/xtensa/config/tie-asm.h b/src/platform/amd/acp_7_0/include/arch/xtensa/config/tie-asm.h index 86df964b4628..5e31df65fca0 100644 --- a/src/platform/amd/acp_7_0/include/arch/xtensa/config/tie-asm.h +++ b/src/platform/amd/acp_7_0/include/arch/xtensa/config/tie-asm.h @@ -182,7 +182,7 @@ .ifeq (XTHAL_SAS_TIE | XTHAL_SAS_NOCC | XTHAL_SAS_CALR) & ~(\select) xchal_sa_align \ptr, 0, 0, 16, 16 ae_s64.i aed0, \ptr, .Lxchal_ofs_+56 - addi.a \ptr, \ptr, 64 + addi \ptr, \ptr, 64 ae_s64.i aed1, \ptr, .Lxchal_ofs_+0 ae_s64.i aed2, \ptr, .Lxchal_ofs_+8 ae_s64.i aed3, \ptr, .Lxchal_ofs_+16 @@ -191,7 +191,7 @@ ae_s64.i aed6, \ptr, .Lxchal_ofs_+40 ae_s64.i aed7, \ptr, .Lxchal_ofs_+48 ae_s64.i aed8, \ptr, .Lxchal_ofs_+56 - addi.a \ptr, \ptr, 64 + addi \ptr, \ptr, 64 ae_s64.i aed9, \ptr, .Lxchal_ofs_+0 ae_s64.i aed10, \ptr, .Lxchal_ofs_+8 ae_s64.i aed11, \ptr, .Lxchal_ofs_+16 @@ -200,7 +200,7 @@ ae_s64.i aed14, \ptr, .Lxchal_ofs_+40 ae_s64.i aed15, \ptr, .Lxchal_ofs_+48 ae_s64.i aed16, \ptr, .Lxchal_ofs_+56 - addi.a \ptr, \ptr, 64 + addi \ptr, \ptr, 64 ae_s64.i aed17, \ptr, .Lxchal_ofs_+0 ae_s64.i aed18, \ptr, .Lxchal_ofs_+8 ae_s64.i aed19, \ptr, .Lxchal_ofs_+16 @@ -209,7 +209,7 @@ ae_s64.i aed22, \ptr, .Lxchal_ofs_+40 ae_s64.i aed23, \ptr, .Lxchal_ofs_+48 ae_s64.i aed24, \ptr, .Lxchal_ofs_+56 - addi.a \ptr, \ptr, 64 + addi \ptr, \ptr, 64 ae_s64.i aed25, \ptr, .Lxchal_ofs_+0 ae_s64.i aed26, \ptr, .Lxchal_ofs_+8 ae_s64.i aed27, \ptr, .Lxchal_ofs_+16 @@ -225,12 +225,12 @@ s8i \at1, \ptr, .Lxchal_ofs_+58 ae_movae \at1, aep3 s8i \at1, \ptr, .Lxchal_ofs_+59 - addi.a \ptr, \ptr, 64 + addi \ptr, \ptr, 64 ae_salign128.i u0, \ptr, .Lxchal_ofs_+0 ae_salign128.i u1, \ptr, .Lxchal_ofs_+16 ae_salign128.i u2, \ptr, .Lxchal_ofs_+32 ae_salign128.i u3, \ptr, .Lxchal_ofs_+48 - addi.a \ptr, \ptr, -320 + addi \ptr, \ptr, -320 ae_movdrzbvc aed0 // ureg AE_ZBIASV8C ae_s64.i aed0, \ptr, .Lxchal_ofs_+0 + 0 ae_movvfcrfsr aed0 // ureg FCR_FSR @@ -302,7 +302,7 @@ l32i \at1, \ptr, .Lxchal_ofs_+52 wur.ae_cend2 \at1 // ureg 251 ae_l64.i aed0, \ptr, .Lxchal_ofs_+56 - addi.a \ptr, \ptr, 64 + addi \ptr, \ptr, 64 ae_l64.i aed1, \ptr, .Lxchal_ofs_+0 ae_l64.i aed2, \ptr, .Lxchal_ofs_+8 ae_l64.i aed3, \ptr, .Lxchal_ofs_+16 @@ -311,7 +311,7 @@ ae_l64.i aed6, \ptr, .Lxchal_ofs_+40 ae_l64.i aed7, \ptr, .Lxchal_ofs_+48 ae_l64.i aed8, \ptr, .Lxchal_ofs_+56 - addi.a \ptr, \ptr, 64 + addi \ptr, \ptr, 64 ae_l64.i aed9, \ptr, .Lxchal_ofs_+0 ae_l64.i aed10, \ptr, .Lxchal_ofs_+8 ae_l64.i aed11, \ptr, .Lxchal_ofs_+16 @@ -320,7 +320,7 @@ ae_l64.i aed14, \ptr, .Lxchal_ofs_+40 ae_l64.i aed15, \ptr, .Lxchal_ofs_+48 ae_l64.i aed16, \ptr, .Lxchal_ofs_+56 - addi.a \ptr, \ptr, 64 + addi \ptr, \ptr, 64 ae_l64.i aed17, \ptr, .Lxchal_ofs_+0 ae_l64.i aed18, \ptr, .Lxchal_ofs_+8 ae_l64.i aed19, \ptr, .Lxchal_ofs_+16 @@ -329,7 +329,7 @@ ae_l64.i aed22, \ptr, .Lxchal_ofs_+40 ae_l64.i aed23, \ptr, .Lxchal_ofs_+48 ae_l64.i aed24, \ptr, .Lxchal_ofs_+56 - addi.a \ptr, \ptr, 64 + addi \ptr, \ptr, 64 ae_l64.i aed25, \ptr, .Lxchal_ofs_+0 ae_l64.i aed26, \ptr, .Lxchal_ofs_+8 ae_l64.i aed27, \ptr, .Lxchal_ofs_+16 @@ -337,7 +337,7 @@ ae_l64.i aed29, \ptr, .Lxchal_ofs_+32 ae_l64.i aed30, \ptr, .Lxchal_ofs_+40 ae_l64.i aed31, \ptr, .Lxchal_ofs_+48 - addi.a \ptr, \ptr, 56 + addi \ptr, \ptr, 56 l8ui \at1, \ptr, .Lxchal_ofs_+0 ae_movea aep0, \at1 l8ui \at1, \ptr, .Lxchal_ofs_+1 @@ -346,7 +346,7 @@ ae_movea aep2, \at1 l8ui \at1, \ptr, .Lxchal_ofs_+3 ae_movea aep3, \at1 - addi.a \ptr, \ptr, 8 + addi \ptr, \ptr, 8 ae_lalign128.i u0, \ptr, .Lxchal_ofs_+0 ae_lalign128.i u1, \ptr, .Lxchal_ofs_+16 ae_lalign128.i u2, \ptr, .Lxchal_ofs_+32 diff --git a/src/platform/amd/acp_7_0/include/arch/xtensa/config/tie.h b/src/platform/amd/acp_7_0/include/arch/xtensa/config/tie.h index 4c451a6af2c1..cd55d1d3028c 100644 --- a/src/platform/amd/acp_7_0/include/arch/xtensa/config/tie.h +++ b/src/platform/amd/acp_7_0/include/arch/xtensa/config/tie.h @@ -32,6 +32,10 @@ #ifndef XTENSA_CORE_TIE_H #define XTENSA_CORE_TIE_H +#ifndef UINT32_C +#define UINT32_C(x) x +#endif + /* parasoft-begin-suppress ALL "This file not MISRA checked." */ #define XCHAL_CP_NUM UINT32_C(1) /* number of coprocessors */ diff --git a/src/platform/amd/acp_7_0/include/arch/xtensa/idma.h b/src/platform/amd/acp_7_0/include/arch/xtensa/idma.h new file mode 100644 index 000000000000..5ba0e82d3c10 --- /dev/null +++ b/src/platform/amd/acp_7_0/include/arch/xtensa/idma.h @@ -0,0 +1,3775 @@ +/* + * Copyright (c) 2018-2023 Cadence Design Systems. ALL RIGHTS RESERVED. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef IDMA_H__ +#define IDMA_H__ + +#include <xtensa/hal.h> +#include <xtensa/xtensa-types.h> +#include <xtensa/config/core-isa.h> +#if XCHAL_HAVE_EXTERN_REGS +#include <xtensa/tie/xt_externalregisters.h> +#endif + + +#ifndef XCHAL_IDMA_ADDR_WIDTH +# define XCHAL_IDMA_ADDR_WIDTH 32 +#endif + +/* If HAL doesn't define # of channels macro, need to set to 1 */ +#ifndef XCHAL_IDMA_NUM_CHANNELS +# define XCHAL_IDMA_NUM_CHANNELS 1 +#endif + +/* User enabled multichannel mode - use LIBIDMA_USE_MULTICHANNEL */ +#if (defined IDMA_USE_MULTICHANNEL) +# define LIBIDMA_USE_MULTICHANNEL 1 +#else +# define LIBIDMA_USE_MULTICHANNEL 0 +#endif + +/* Use multichannels only if user-enabled */ +#if (LIBIDMA_USE_MULTICHANNEL > 0) +# define LIBIDMA_USE_MULTICHANNEL_API 1 +#else +# define LIBIDMA_USE_MULTICHANNEL_API 0 +#endif + +/* Enable large (64-byte) descriptor API if the hardware supports it. */ +#if (XCHAL_IDMA_DESC_SIZE == 64) +# define IDMA_USE_64B_DESC 1 +# define IDMA_HAVE_LARGE_DESC 1 +# define IDMA_HAVE_3D 1 +#else +# define IDMA_USE_64B_DESC 0 +# define IDMA_HAVE_LARGE_DESC 0 +# define IDMA_HAVE_3D 0 +#endif + +/* Enable wide address API if the hardware supports it. */ +#if (XCHAL_IDMA_ADDR_WIDTH > 32) && (XCHAL_IDMA_ADDR_WIDTH <= 64) +# define IDMA_USE_WIDE_API 1 +# define IDMA_HAVE_WIDE_API 1 +#else +# define IDMA_USE_WIDE_API 0 +# define IDMA_HAVE_WIDE_API 0 +#endif + +#if (IDMA_USE_64B_DESC == 0) && (IDMA_USE_WIDE_API > 0) +/* #error "IDMA_USE_WIDE_API requires a config that supports 64-byte descriptors" */ +#endif + +/* This is INTERNAL. Need to be here as headers use it */ +#define PSO_SAVE_SIZE_PER_CHANNEL 6 +#define IDMA_PSO_SAVE_SIZE PSO_SAVE_SIZE_PER_CHANNEL*XCHAL_IDMA_NUM_CHANNELS + + +// For the old 2-channel implementation, ch0 and ch1 are at different +// base addresses. For the newer implementation, all the channels can +// be accessed at either of the two base addresses. We choose to use +// the first (device1) base address. +#if (XCHAL_IDMA_NUM_CHANNELS == 2) && (IDMA_USE_64B_DESC == 0) +#if XCHAL_HAVE_XEA2 || XCHAL_HAVE_XEA3 +#if defined(IDMA_USERMODE) +# define IDMAREG_BASE_CH0 UINT32_C(0x00910000) +# define IDMAREG_BASE_CH1 UINT32_C(0x00930400) +#else +# define IDMAREG_BASE_CH0 UINT32_C(0x00110000) +# define IDMAREG_BASE_CH1 UINT32_C(0x00130400) +#endif +# define IDMAREG_BASE(n) (((n) == 0U) ? (IDMAREG_BASE_CH0) : (IDMAREG_BASE_CH1)) +#else +# define IDMAREG_BASE_CH0 IDMA_CHAN_ADDR(0) +# define IDMAREG_BASE_CH1 IDMA_CHAN_ADDR(1) +# define IDMAREG_BASE(n) IDMA_CHAN_ADDR(n) +#endif +#else +#if defined(IDMA_USERMODE) +# define IDMAREG_BASE_SDEV1 UINT32_C(0x00910000) +#else +# define IDMAREG_BASE_SDEV1 UINT32_C(0x00110000) +#endif +# define IDMAREG_BASE(n) (IDMAREG_BASE_SDEV1 + ((UINT32_C(0x400)) * (n))) +#endif + +/* iDMA registers offsets */ +#define IDMA_REG_SETTINGS 0x00 +#define IDMA_REG_TIMEOUT 0x04 +#define IDMA_REG_DESC_START 0x08 +#define IDMA_REG_NUM_DESC 0x0C +#define IDMA_REG_DESC_INC 0x10 +#define IDMA_REG_CONTROL 0x14 +#define IDMA_REG_USERPRIV 0x18 +#define IDMA_REG_STATUS 0x40 +#define IDMA_REG_CURR_DESC 0x44 +#define IDMA_REG_DESC_TYPE 0x48 +#define IDMA_REG_SRC_ADDR 0x4C +#define IDMA_REG_DST_ADDR 0x50 + +// PSO status, after core save/restore +// Value at IDMA_PSO_STATUS_OFFSET from at XtosSaveState.idmaregs +// will indicate if idma HW was idle prior to save, so save was +// skipped, or there was forced iDMA HW disable, or save was done. +#define IDMA_PSO_STATUS_OFFSET 5 +#define IDMA_PSO_STATUS_SAVED 0x1 +#define IDMA_PSO_STATUS_DISABLED 0x2 +#define IDMA_PSO_STATUS_IDLE 0x3 + +#if !defined(_ASMLANGUAGE) && !defined(__ASSEMBLER__) + +#include <stdint.h> +#include <stddef.h> +#include <time.h> + +/* Typedef for internal use (required by idma_os.h) */ +typedef struct idma_buf_struct idma_buf_t; + +/* libidma internal buffer ptr, used in Fixed-Buffer mode */ +extern idma_buf_t * g_idma_buf_ptr[XCHAL_IDMA_NUM_CHANNELS]; + +#if defined(IDMA_USERMODE) +#undef IDMA_USE_INTR /* parasoft-suppress MISRA2012-RULE-20_5-4 "Not defined by library" */ +#define IDMA_USE_INTR 0 +#define IDMA_USE_XTOS 1 +#define IDMA_APP_USE_XTOS 1 +#endif + +/** + * Use inline XTOS interface if application defines IDMA_APP_USE_XTOS or + * library build defines IDMA_USE_XTOS. Else use libidma OS interface. + * Both are defined in idma_os.h. + */ +#include "idma_os.h" /* parasoft-suppress MISRA2012-RULE-20_1-4 "This has to follow idma_buf_t" */ + + +/** + * Use interrupts in APIs defined in the header. Default is to use interrupts. + * Overhead of enabling/disabling interrupts to guard shared structs + */ +#if !defined(IDMA_USE_INTR) +#define IDMA_USE_INTR 1 +#endif + +/******************************************************/ +/**** Initialization flags, types, defines ****/ +/******************************************************/ + +/** + * iDMAlib and iDMA HW operation flags, for idma_init() 1st arg. + */ +#define IDMA_OCD_HALT_ON 0x001 /* Enable iDMA halt on OCD interrupt. */ + +/** +* Type of descriptor to add. NOTE: value reflects multiple of 16B. +*/ +typedef enum { + IDMA_1D_DESC = 1, + IDMA_2D_DESC = 2, + IDMA_64B_DESC = 4 +} idma_type_t; + +/** + * Maximum allowed PIF request block size. + */ +typedef enum { + MAX_BLOCK_2 = 0, + MAX_BLOCK_4 = 1, + MAX_BLOCK_8 = 2, + MAX_BLOCK_16 = 3 +} idma_max_block_t; + +/** + * TICK_CYCLES_N: iDMA HW internal timer ticks every N cycles. + */ +typedef enum { +TICK_CYCLES_1 = 0, /* internal timer ticks every 1 cycle */ +TICK_CYCLES_2 = 1, /* internal timer ticks every 2 cycles */ +TICK_CYCLES_4 = 2, +TICK_CYCLES_8 = 3, +TICK_CYCLES_16 = 4, +TICK_CYCLES_32 = 5, +TICK_CYCLES_64 = 6, +TICK_CYCLES_128 = 7 +} idma_ticks_cyc_t; + +/** + * "Descriptor Control Flags" for any type of descriptor + * This sets the idma descriptor control field - see HW description. + */ +#define DESC_IDMA_NOPRIV_SRC 0x00200 /* Non-privileged source memory access */ +#define DESC_IDMA_NOPRIV_DST 0x00800 /* Non-privileged dest. memory access */ +#define DESC_IDMA_PRIOR_H 0x08000 /* QoS high */ +#define DESC_IDMA_PRIOR_L 0x00000 /* QoS low */ +#define DESC_IDMA_TRIG_WAIT 0x20000000 /* wait for external trigger to start */ +#define DESC_IDMA_TRIG_OUT 0x40000000 /* send trigger out on desc. completion */ +#define DESC_NOTIFY_W_INT 0x80000000 /* trigger interrupt on completion */ +//* "Descriptor Control Flags" for new 64-Byte descriptors only. +#define DESC64_IDMA_OVERLAP 0x00000008 /* Overlap Bit (bit 3) */ +#define DESC64_IDMA_LONG_ADDR 0x00000400 /* LA – Long Address (bit 10) */ + +// "Descriptor Control Flags" AXI attribute bits for 64-byte descriptors. +// These go into bits 27:24 of the descriptor control word. +#define DESC64_IDMA_AXI_ATTRIBUTE(val) (((val) & 0xF) << 24) + +/** + * iDMA API return values + * For most of the iDMAlib API calls + */ +typedef enum { + IDMA_ERR_NO_BUF = -40, /* No valid ring buffer */ + IDMA_ERR_BAD_DESC = -20, /* Descriptor not correct */ + IDMA_ERR_BAD_CHAN, /* Invalid channel number */ + IDMA_ERR_NOT_INIT, /* iDMAlib and HW not initialized */ + IDMA_ERR_TASK_NOT_INIT, /* Cannot scheduled uninitialized task */ + IDMA_ERR_BAD_TASK, /* Task not correct */ + IDMA_ERR_BUSY, /* iDMA busy when not expected */ + IDMA_ERR_IN_SPEC_MODE, /* iDMAlib in unexpected mode */ + IDMA_ERR_NOT_SPEC_MODE, /* iDMAlib in unexpected mode */ + IDMA_ERR_TASK_EMPTY, /* No descs in the task/buffer */ + IDMA_ERR_TASK_OUTSTAND_NEG, /* Number of outstanding descs is a negative value */ + IDMA_ERR_TASK_IN_ERROR, /* Task in error */ + IDMA_ERR_BUFFER_IN_ERROR, /* Buffer in error */ + IDMA_ERR_NO_NEXT_TASK, /* Next task to process is missing */ + IDMA_ERR_BUF_OVFL, /* Attempt to schedule too many descriptors */ + IDMA_ERR_HW_ERROR, /* HW error detected */ + IDMA_ERR_BAD_INIT, /* Bad idma_init args */ + IDMA_ERR_UNSUP, /* Not supported in current configuration/mode */ + IDMA_OK = 0, /* No error */ + IDMA_CANT_SLEEP = 1, /* Cannot sleep (no pending descriptors) */ +} idma_status_t; + +/** + * iDMA task status API return values + * NOTE: Valid only after task is scheduled + * + * N (>=0) - Number of outstanding scheduled descriptors. + * 0 (IDMA_TASK_DONE) - Whole task has completed the execution. + * IDMA_TASK_ERROR - iDMA HW error happened due to a descriptor executed + * from this task. Error details are available (idma_hw_error_t). + * IDMA_TASK_ABORTED - Task forcefully aborted. + */ +typedef enum { + IDMA_TASK_ABORTED = -3, + IDMA_TASK_ERROR = -2, + IDMA_TASK_NOINIT = -1, + IDMA_TASK_DONE = 0, + IDMA_TASK_EMPTY = 0 +} task_status_t; + +/** + * iDMA HW error details API return values + * This corresponds to the Error Codes field + * of the HW Status register. + */ +#if XCHAL_HAVE_XEA2 +#define IDMA_ERR_FETCH_ADDR UINT32_C(0x2000) +#define IDMA_ERR_FETCH_DATA UINT32_C(0x1000) +#define IDMA_ERR_READ_ADDR UINT32_C(0x0800) +#define IDMA_ERR_READ_DATA UINT32_C(0x0400) +#define IDMA_ERR_WRITE_ADDR UINT32_C(0x0200) +#define IDMA_ERR_WRITE_DATA UINT32_C(0x0100) +#define IDMA_ERR_REG_TIMEOUT UINT32_C(0x0080) +#define IDMA_ERR_TRIG_OVFL UINT32_C(0x0040) +#define IDMA_ERR_DESC_OVFL UINT32_C(0x0020) +#define IDMA_ERR_DESC_UNKNW UINT32_C(0x0010) +#define IDMA_ERR_DESC_UNSUP_DIR UINT32_C(0x0008) +#define IDMA_ERR_DESC_BAD_PARAMS UINT32_C(0x0004) +#define IDMA_ERR_DESC_NULL_ADDR UINT32_C(0x0002) +#define IDMA_ERR_DESC_PRIVILEGE UINT32_C(0x0001) +#define IDMA_NO_ERR UINT32_C(0x0000) +#else +#define IDMA_ERR_FETCH_ADDR UINT32_C(0x80000) +#define IDMA_ERR_FETCH_DATA UINT32_C(0x40000) +#define IDMA_ERR_READ_ADDR UINT32_C(0x20000) +#define IDMA_ERR_READ_DATA UINT32_C(0x10000) +#define IDMA_ERR_WRITE_ADDR UINT32_C(0x08000) +#define IDMA_ERR_WRITE_DATA UINT32_C(0x04000) +#define IDMA_ERR_REG_TIMEOUT UINT32_C(0x02000) +#define IDMA_ERR_TRIG_OVFL UINT32_C(0x01000) +#define IDMA_ERR_DESC_OVFL UINT32_C(0x00800) +#define IDMA_ERR_DESC_UNKNW UINT32_C(0x00400) +#define IDMA_ERR_DESC_UNSUP_DIR UINT32_C(0x00200) +#define IDMA_ERR_DESC_BAD_PARAMS UINT32_C(0x00100) +#define IDMA_ERR_DESC_NULL_ADDR UINT32_C(0x00080) +#define IDMA_ERR_DESC_PRIVILEGE UINT32_C(0x00040) +#define IDMA_ERR_DESC_MMUMPU UINT32_C(0x00020) +#define IDMA_ERR_BIT_VECT UINT32_C(0x00010) +#define IDMA_ERR_VECT_OTHER UINT32_C(0x00008) +#define IDMA_ERR_VECT_DATA UINT32_C(0x00004) +#define IDMA_ERR_SRC_MMUMPU UINT32_C(0x00002) +#define IDMA_ERR_DST_MMUMPU UINT32_C(0x00001) +#define IDMA_NO_ERR UINT32_C(0x00000) +#endif + +typedef uint32_t idma_hw_error_t; + +/* Status reg fields */ +#define IDMA_STATE_IDLE UINT32_C(0x0) //idma done with all descriptors and disabled +#define IDMA_STATE_STANDBY UINT32_C(0x1) //transient stat +#define IDMA_STATE_BUSY UINT32_C(0x2) //idma busy copying +#define IDMA_STATE_DONE UINT32_C(0x3) //idma done with all descriptors but enabled +#define IDMA_STATE_HALT UINT32_C(0x4) //idma disabled during copy operation +#define IDMA_STATE_ERROR UINT32_C(0x5) //idma in error +#define IDMA_STATE_MASK UINT32_C(0x7) + +typedef uint32_t idma_state_t; + +# ifdef IDMA_DEBUG +# define INTERNAL_FUNC static +# else +# define INTERNAL_FUNC static inline +# endif + +/* No inlining for debug libs */ +#ifdef IDMA_LIB_BUILD +# define IDMA_API static inline +#else +# define IDMA_API INTERNAL_FUNC +#endif + +#define ALWAYS_INLINE __attribute__((always_inline)) static inline + +#ifndef IDMA_EXTERN +# define IDMA_EXTERN +#endif + + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * iDMA error details structure - available from different API calls + */ +typedef struct idma_error_details_struct { + idma_hw_error_t err_type; /* ErrorCodes field of the Status reg */ + uint32_t currDesc; /* Descriptor causing error */ + uint32_t srcAddr; /* PIF source address causing error */ + uint32_t dstAddr; /* PIF dest. address causing error */ +} idma_error_details_t; + +/* callback typedef */ +typedef void (*idma_callback_fn)( void* arg); +/* error callback typedef */ +typedef void (*idma_err_callback_fn)( const idma_error_details_t* error); +/* log handler typedef */ +typedef void (*idma_log_h)( const char* xlog); + +/* Typedef for external API use */ +typedef struct idma_buffer_struct { + char buffer[4]; +} idma_buffer_t; + +/* allocate space for n descriptors of type idma_type_t. */ +#define IDMA_BUFFER_SIZE(n,type) \ + ( ((n)* (((type) == IDMA_1D_DESC) ? IDMA_1D_DESC_SIZE : (((type) == IDMA_2D_DESC) ? IDMA_2D_DESC_SIZE : IDMA_64_DESC_SIZE))) + \ + sizeof(struct idma_buf_struct)) + +/* Define a buffer of containing n descriptors of type n */ +#define IDMA_BUFFER_DEFINE(name, n, type) \ + IDMA_DRAM idma_buffer_t name[IDMA_BUFFER_SIZE((n), (type))/sizeof(idma_buffer_t)] + +/* Use these macros when allocating copy buffers to avoid sharing cache lines */ +#define IDMA_DCACHE_ALIGN (XCHAL_DCACHE_LINESIZE << XCHAL_DCACHE_LINES_PER_TAG_LOG2) + +/* Align to dcache line */ +#define ALIGNDCACHE __attribute__ ((aligned(IDMA_DCACHE_ALIGN))) + +/* Buffer size will be rounded to the cache alignment so it occupies the whole cache line */ +#define IDMA_SIZE(N) (((N) + IDMA_DCACHE_ALIGN-1) & -IDMA_DCACHE_ALIGN) + +/* These macros are empty if no interrupts. Otherwise, they map to OS provided + * functions that are defined in idma-xtos.h or idma-os.h. The XTOS versions + * will get inlined for performance. + */ +#if defined XCHAL_HAVE_INTERRUPTS && (IDMA_USE_INTR > 0) +# define DECLARE_PS() uint32_t ps +# define IDMA_ENABLE_INTS() idma_enable_interrupts(ps) +# define IDMA_DISABLE_INTS() ps = idma_disable_interrupts() +#else //no interrupts +# define DECLARE_PS() do {} while (0) +# define IDMA_ENABLE_INTS() do {} while (0) +# define IDMA_DISABLE_INTS() do {} while (0) +#endif + +/*************************************************/ +/** Forward declarations for internal stuff **/ +/*************************************************/ +typedef struct idma_desc_struct idma_desc_t; +typedef struct idma_2d_desc_struct idma_2d_desc_t; +typedef struct idma_desc64_struct idma_desc64_t; + +/************************************************/ +/**** MISC HELPER FUNCTIONS ****/ +/************************************************/ + +ALWAYS_INLINE void * +cvt_uint32_to_voidp(uint32_t val) +{ + return (void *) val; // parasoft-suppress MISRA2012-RULE-11_6-2 "Type conversion necessary." +} + +ALWAYS_INLINE uint32_t +cvt_voidp_to_uint32(void * val) // parasoft-suppress MISRA2012-RULE-8_13_a-4 "Cannot use const because of type conversion." +{ + return (uint32_t) val; // parasoft-suppress MISRA2012-RULE-11_6-2 "Type conversion necessary." +} + +ALWAYS_INLINE void * +cvt_uint32p_to_voidp(uint32_t * val) // parasoft-suppress MISRA2012-RULE-8_13_a-4 "Cannot use const because of type conversion." +{ + return (void *) val; +} + +ALWAYS_INLINE uint32_t * +cvt_voidp_to_uint32p(void * val) // parasoft-suppress MISRA2012-RULE-8_13_a-4 "Cannot use const because of type conversion." +{ + return (uint32_t *) val; // parasoft-suppress MISRA2012-RULE-11_5-4 "Type conversion necessary." +} + +ALWAYS_INLINE void * +cvt_int32_to_voidp(int32_t val) +{ + return (void *) val; // parasoft-suppress MISRA2012-RULE-11_6-2 "Type conversion necessary." +} + +ALWAYS_INLINE int32_t +cvt_voidp_to_int32(void * val) // parasoft-suppress MISRA2012-RULE-8_13_a-4 "Cannot use const because of type conversion." +{ + return (int32_t) val; // parasoft-suppress MISRA2012-RULE-11_6-2 "Type conversion necessary." +} + +ALWAYS_INLINE idma_desc64_t * +cvt_desc_to_desc64(idma_desc_t * val) // parasoft-suppress MISRA2012-RULE-8_13_a-4 "Cannot use const because of type conversion." +{ + return (idma_desc64_t *) val; // parasoft-suppress MISRA2012-RULE-11_3-2 MISRA2012-RULE-11_2-2 "Type conversion necessary." +} + +/************************************************/ +/**** API ****/ +/************************************************/ + +/* NOTE NOTE NOTE: + * Have to define LIBIDMA_USE_MULTICHANNEL if want to use the multichannel API. + * The new API basically adds the channel argument to the most of the old functions. + * See below, #else part, for the old API. Also, each function descriptions mentions + * if an argument is not present in the old API. + */ +#if (LIBIDMA_USE_MULTICHANNEL_API > 0) + +/** + * @name Wait for all descriptors to finish, by polling only the HW + * @param ch Selected iDMA HW channel. + * NOTE: Argument present only if LIBIDMA_USE_MULTICHANNEL_API defined. + * @retval None + */ +IDMA_API void +idma_hw_wait_all(int32_t ch); + +/** + * @name Schedule a number of descriptors, by accessing HW only. + * @param ch Selected iDMA HW channel. + * NOTE: Argument present only if LIBIDMA_USE_MULTICHANNEL_API defined. + * @param count Number of descriptors to schedule. + * @retval None + */ +IDMA_API void +idma_hw_schedule(int32_t ch, uint32_t count); + +/** + * @name Return number of outstanding descriptor, as indicated by HW + * @param ch Selected iDMA HW channel. + * NOTE: Argument present only if LIBIDMA_USE_MULTICHANNEL_API defined. + * @retval number of outstanding descriptor + */ +IDMA_API uint32_t +idma_hw_num_outstanding(int32_t ch); + +/** + * @name iDMAlib and iDMA HW initialization + * @brief Does iDMA HW soft reset, sets iDMAlib parameters and sets + * iDMA HW registers (Settings/Timeout) with given parameters. + * @param ch Selected. iDMA HW channel. + * NOTE: Argument present only if LIBIDMA_USE_MULTICHANNEL_API defined. + * @param flags iDMAlib initialization flags, above. + * @param block_sz idma_max_block_t above. + * @param pif_req Maximum outstanding PIF requests (1-64). + * @param ticks_per_cyc Ticks per cycle. See iDMA hardware details. + * @param timeout_ticks Timeout ticks. See iDMA hardware details. + * @param err_cb_func Register global error handler. Called on idma HW + * error detection for which intr is always enabled. + * @retval IDMA_OK Successful + * @retval !IDMA_OK Error type + */ +IDMA_API idma_status_t +idma_init( int32_t ch, + uint32_t flags, + idma_max_block_t block_sz, + uint32_t pif_req, + idma_ticks_cyc_t ticks_per_cyc, + uint32_t timeout_ticks, + idma_err_callback_fn err_cb_func); + +/** + * @name Enable fast-wake mode for all channels. + * @brief Enables fast wakeup from WAITI for all channels. All channels + * must be not busy and not in error state. + * @retval IDMA_OK Successful + * @retval IDMA_ERR_BUSY At least one channel was busy or in error + * @retval IDMA_ERR_UNSUP Not supported in this configuration + */ +idma_status_t +idma_fast_wake_enable(void); + +/** + * @name Disable fast-wake mode for all channels. + * @brief Disables fast wakeup from WAITI for all channels. All channels + * must be not busy and not in error state. + * @retval IDMA_OK Successful + * @retval IDMA_ERR_BUSY At least one channel was busy or in error + * @retval IDMA_ERR_UNSUP Not supported in this configuration + */ +idma_status_t +idma_fast_wake_disable(void); + +/** + * @name Check the state of idma HW. + * @param ch Selected iDMA HW channel. + * NOTE: Argument present only if LIBIDMA_USE_MULTICHANNEL_API defined. + */ +IDMA_API idma_state_t +idma_get_state(int32_t ch); + +/** + * @name Add log messages handler. + * @brief Log messages from iDMAlib are sent to this handler. + * NOTE: In debug library only ! + * @param xlog Function to call on iDMAlib log call + */ +void +idma_log_handler(idma_log_h xlog); + +/** + * @name Sleep/block until current iDMA activity completes. + * @brief Wait in low-power mode until iDMA activity completes. Return immediately + * if iDMA is in error or there are no outstanding descriptors. + * @param ch Selected iDMA HW channel. + * NOTE: Argument present only if LIBIDMA_USE_MULTICHANNEL_API defined. + * @retval IDMA_OK Normal return. + * @retval IDMA_CANT_SLEEP Cannot sleep (no pending descriptors). + * @retval (all other values) Error code. + */ +IDMA_API idma_status_t +idma_sleep(int32_t ch); + +/** + * @name Get the error details. + * @brief When task or fixed buffer status indicate error, use this + * function to obtain the error details. + * @param ch Selected iDMA HW channel. + * NOTE: Argument present only if LIBIDMA_USE_MULTICHANNEL_API defined. + * @param task Pointer to iDMA task + * @retval Pointer to the structure containing error details. + * NOTE: Pointer is valid only if idma task/buffer is in error. + */ +IDMA_API idma_error_details_t* idma_error_details(int32_t ch); +IDMA_API idma_error_details_t* idma_buffer_error_details(int32_t ch); + +/** + * @name Disable iDMA HW when not in use. + * @param ch Selected iDMA HW channel. + * NOTE: Argument present only if LIBIDMA_USE_MULTICHANNEL_API defined. + * @brief Needs idma_init to resume. + */ +IDMA_API idma_status_t +idma_stop(int32_t ch); + +/** + * @name Pause iDMA HW. + * @param ch Selected iDMA HW channel. + * NOTE: Argument present only if LIBIDMA_USE_MULTICHANNEL_API defined. + * @brief Can simply resume after + */ +IDMA_API void +idma_pause(int32_t ch); + +/** + * @name Resume iDMA HW. + * @param ch Selected iDMA HW channel. + * NOTE: Argument present only if LIBIDMA_USE_MULTICHANNEL_API defined. + * @brief Can simply pause before + */ +IDMA_API void +idma_resume(int32_t ch); + +/** + * @name Add 1D desc to the next location in the buffer/task + * @brief Descriptors are added in order. Used in both Task and + * and the fixed-buffer mode. + * NOTE: Number of added descriptors needs to match the + * size of the task (an argument to idma_init_task()). + * @param dst... iDMA 1D transfer parameters + * @param flags Descriptor options (Control field). + * See "Descriptor Control Flags". + * @retval IDMA_OK Successful. + * @retval !IDMA_OK Error type. + */ +IDMA_API idma_status_t +idma_add_desc( idma_buffer_t *bufh, + void *dst, + void *src, + size_t size, + uint32_t flags); + +/** + * @name Add 2D desc to the next location in the buffer/task + * @brief Descriptors are added in order. Used in both Task and + * and the fixed-buffer mode. + * NOTE: Number of added descriptors needs to match the + * size of the task (an argument to idma_init_task()). + * @param dst... iDMA 1D transfer parameters + * @param flags Descriptor options (Control field). + * See "Descriptor Control Flags". + * @retval IDMA_OK Successful. + * @retval !IDMA_OK Error type. + */ +IDMA_API idma_status_t +idma_add_2d_desc( idma_buffer_t *bufh, + void *dst, + void *src, + size_t row_sz, + uint32_t flags, + uint32_t nrows, + uint32_t src_pitch, + uint32_t dst_pitch); + +/************************************************/ +/**** Task-mode API ****/ +/************************************************/ + +/** + * @name Initialize a task (a separate buffer/array of descriptors). + * @brief Needs to be called before adding descriptors to a task. + * @param ch Selected iDMA HW channel. + * NOTE: Argument present only if LIBIDMA_USE_MULTICHANNEL_API defined. + * @param task Pointer to the memory used for iDMA task. + * @param type 1D or 2D. + * @param num Number of descriptors to be added. NOTE: must match + * the actual number of added descs using _add_ calls. + * Also, IDMA_BUFFER_DEFINE must allocate sufficent memory. + * @param cb_func Function to call on each descriptor completion. + * (if enabled by descriptor). + * @param cb_data Callback data. + * @retval IDMA_OK Successful. + * @retval !IDMA_OK Error type + */ +IDMA_API idma_status_t +idma_init_task (int32_t ch, + idma_buffer_t *taskh, + idma_type_t type, + int32_t ndescs, + idma_callback_fn cb_func, + void *cb_data); + +/** + * @name Get the task execution status. + * @brief Reads the number of outstanding descriptors from the + * scheduled task. When no iDMA interrupts, must be used + * together with idma_process_tasks() as something must + * trigger iDMAlib internal task completion processing. + * @param task Pointer to iDMA task + * @retval < 0 Error (task_status_t) + * @retval >= 0 Number of outstanding descriptors in the task. + */ +IDMA_API int32_t +idma_task_status(idma_buffer_t *taskh); + +/** + * @name Create and schedule 1D copy request. + * @brief Convenience functions that combine buffer initialization, + * adding one descriptor to it and scheduling the task. + * @param ch Selected iDMA HW channel. + * NOTE: Argument present only if LIBIDMA_USE_MULTICHANNEL_API defined. + * @param task Pointer to iDMA buffer + * @param cb_func Function to call on each descriptor completion + * (if enabled by descriptor) + * @param cb_data Callback data. + * @retval IDMA_OK Successful. + * @retval !IDMA_OK Error type + */ +IDMA_API idma_status_t +idma_copy_task(int32_t ch, + idma_buffer_t *taskh, + void *dst, + void *src, + size_t size, + uint32_t flags, + void *cb_data, + idma_callback_fn cb_func); + +/** + * @name Create and schedule 2D copy request. + * @brief Convenience functions that combine buffer initialization, + * adding one descriptor to it and scheduling the task. + * @param ch Selected iDMA HW channel. + * NOTE: Argument present only if LIBIDMA_USE_MULTICHANNEL_API defined. + * @param task Pointer to iDMA buffer + * @param cb_func Function to call on each descriptor completion + * (if enabled by descriptor) + * @param cb_data Callback data. + * @retval IDMA_OK Successful. + * @retval !IDMA_OK Error type + */ +IDMA_API idma_status_t +idma_copy_2d_task(int32_t ch, + idma_buffer_t *taskh, + void *dst, + void *src, + size_t row_sz, + uint32_t flags, + uint32_t nrows, + uint32_t src_pitch, + uint32_t dst_pitch, + void *cb_data, + idma_callback_fn cb_func); + +/** + * @name Schedule a task for execution. + * @brief Schedule a task(separate buffer/array ofdescriptor) + * for execution in the task mode. If IDMA is busy, + * last desc from the last scheduled buffer is linked + * with the 1st desc from this task using a JUMP command. + * If IDMA is not busy iDMA HW starts execution from this task. + * NOTE: TASK MODE ONLY. + * @param buf Pointer to iDMA task to be scheduled. + * @retval IDMA_OK Successful. + * @retval !IDMA_OK Error type. + */ +idma_status_t +idma_schedule_task(idma_buffer_t *taskh); + +/** + * @name Trigger iDMAlib internal processing of completed tasks. + * @brief Needed when no interrupts are available/setup. + * NOTE: Function will also invoke completion callback, + * if set with idma_init_task() function. + * @param ch Selected iDMA HW channel. + * NOTE: Argument present only if LIBIDMA_USE_MULTICHANNEL_API defined. + * @retval IDMA_OK Successful. + * @retval !IDMA_OK Error type (when iDMA is in error). + */ +IDMA_API idma_status_t +idma_process_tasks(int32_t ch); + +/** + * @name Abort all tasks. + * @brief Reset iDMA HW and set status of all "outstanding" tasks to + * aborted state. Note: "Outstanding" tasks can also include + * tasks that actually completed (their descriptors are all executed). + * To ensure all the completed tasks are not seen as "outstanding", + * enable iDMA Done interrupt for at least the last descriptor in + * a task, or call idma_process_tasks() before calling this function. + * @param ch Selected iDMA HW channel. + * NOTE: Argument present only if LIBIDMA_USE_MULTICHANNEL_API defined. + * @retval IDMA_OK Successful. + * @retval !IDMA_OK Error type + */ +IDMA_API idma_status_t +idma_abort_tasks(int32_t ch); + +/************************************************/ +/**** Fixed-Buffer Mode API ****/ +/************************************************/ + +/** + * @name Initialize buffer (array of descs). Also enables the fixed-buffer + * mode which prevents using the buffer as a separate task. + * @brief Needs to be called after a buffer is created (e.g. using the + * IDMA_DEFINE_BUFFER) and before adding descriptors to it. + * @param ch Selected iDMA HW channel. + * NOTE: Argument present only if LIBIDMA_USE_MULTICHANNEL_API defined. + * @param buffer Pointer to the memory used for iDMA buffer + * @param type 1D or 2D + * @param ndescs Number of descriptors. In the TASK mode, the number + * of descs added to the task need to much this number. + * @param cb_func Function to call on each descriptor completion. + * (if enabled by each descriptor settings) + * @param cb_data Callback data + * @retval IDMA_OK Successful. + * @retval !IDMA_OK Error type + */ +IDMA_API idma_status_t +idma_init_loop (int32_t ch, + idma_buffer_t *bufh, + idma_type_t type, + int32_t ndescs, + void *cb_data, + idma_callback_fn cb_func); + +/** + * @name Schedule consecutive descriptors for execution. + * @brief Schedule a number of next in line descriptors, with wrap-around + * NOTE: INCOMPATIBLE with idma_copy_desc/idma_copy_2d_desc + * @param ch Selected iDMA HW channel. + * NOTE: Argument present only if LIBIDMA_USE_MULTICHANNEL_API defined. + * @param count Number of descriptors to schedule. + * NOTE: Cannot be larger than the number of descriptors + * in the buffer initialized using idma_init_loop(). + * @retval < 0 Error code, from idma_status_t. + * @retval >= 0 Unique index of the scheduled descriptor in the range from 0 + * to 0x7fffffff. Starts with value of 1 at iDMAlib initialization + * time. It's expected the number of outstanding descriptors in + * the system is always less then 0x7fffffff. + * E.g. if the circular buffer contains 8 descs and retval=17, + * the 2nd descriptor in the buffer is the last scheduled one and + * by, the iDMAlib has scheduled 17 descriptors. + * NOTE: This unique index is to be used in idma_desc_done() only. + */ +IDMA_API int32_t +idma_schedule_desc(int32_t ch, + uint32_t count); + +/** + * @name Schedule consecutive descriptors for execution w/o ability to change descs. + * @brief Schedule a number of next in line descriptors, with wrap-around. + * The function is to be used when all the descriptors are already added + * so they are just scheduled or possibly updated. This is because the + * the function doesn't keep the track of the place where the next descriptor + * is to be added. + * NOTE: INCOMPATIBLE with idma_copy_desc/idma_copy_2d_desc and idma_update_desc_* + * @param ch Selected iDMA HW channel. + * NOTE: Argument present only if LIBIDMA_USE_MULTICHANNEL_API defined. + * @param count Number of descriptors to schedule. + * @retval < 0 Error code, from idma_status_t. + * @retval >= 0 See idma_schedule_desc() + */ +IDMA_API int32_t +idma_schedule_desc_fast(int32_t ch, + uint32_t count); + +/** + * @name Add and schedule a 1D descriptor. + * @brief Descriptor added in order and scheduled in order. + * NOTE: INCOMPATIBLE with idma_add_desc/idma_add_2d_desc/idma_schedule_desc + * @param ch Selected iDMA HW channel. + * NOTE: Argument present only if LIBIDMA_USE_MULTICHANNEL_API defined. + * @param dst iDMA transfer destination + * @param src iDMA transfer source + * @param size iDMA transfer size + * @param flags Descriptor options (Control field). See "Descriptor Control Flags". + * @retval < 0 Error code, from idma_status_t. + * @retval >= 0 See idma_schedule_desc() + */ +IDMA_API int32_t +idma_copy_desc(int32_t ch, + void *dst, + void *src, + size_t size, + uint32_t flags); + +/** + * @name Add and schedule a 2D descriptor. + * @brief Descriptor added in order and scheduled in order. + * NOTE: INCOMPATIBLE with idma_add_desc/idma_add_2d_desc/idma_schedule_desc + * @param ch Selected iDMA HW channel. + * NOTE: Argument present only if LIBIDMA_USE_MULTICHANNEL_API defined. + * @param dst iDMA transfer destination + * @param src iDMA transfer source + * @param size iDMA transfer size + * @param flags Descriptor options (Control field). + * See "Descriptor Control Flags". + * @param nrows Number of rows to transfer + * @param src_pitch iDMA transfer source pitch + * @param dst_pitch iDMA transfer destination pitch + * @retval < 0 Error code, from idma_status_t. + * @retval >= 0 See idma_schedule_desc() + */ +IDMA_API int32_t +idma_copy_2d_desc(int32_t ch, + void *dst, + void *src, + size_t size, + uint32_t flags, + uint32_t nrows, + uint32_t src_pitch, + uint32_t dst_pitch); + +/** + * @name Check if a descriptor is done, using unique desc ID. + * @brief Unique ID is made available when descriptor was scheduled. + * The function does the internal processing so it can be + * called in an empty loop if want to wait for desc to complete. + * @param ch Selected iDMA HW channel. + * NOTE: Argument present only if LIBIDMA_USE_MULTICHANNEL_API defined. + * @param index Unique ID of the descriptor. + * @retval 1 Desc is done. + * @retval 0 Desc is NOT done. + * @retval -1 Error. + */ +IDMA_API int32_t +idma_desc_done(int32_t ch, + int32_t index); + + +/** + * @name Update destination field of the "next" descriptor + * @brief Update next to be schedule descriptor (means the next + * call to idma_desc_schedule() will schedule the desc. + * updated in this call. + * ONLY IN FIXED-BUFFER MODE. + * @param ch Selected iDMA HW channel. + * NOTE: Argument present only if LIBIDMA_USE_MULTICHANNEL_API defined. + * @param dst New destination address + * @retval IDMA_OK Successful + * @retval !IDMA_OK Error type + */ +IDMA_API idma_status_t +idma_update_desc_dst(int32_t ch, + void *dst); + +/** + * @name Update source field of the "next" descriptor. + * @brief Update next to be schedule descriptor (means the next + * call to idma_desc_schedule() will schedule the desc. + * updated in this call. + * ONLY IN FIXED-BUFFER MODE. + * @param ch Selected iDMA HW channel. + * NOTE: Argument present only if LIBIDMA_USE_MULTICHANNEL_API defined. + * @param src New source address + * @retval IDMA_OK Successful + * @retval !IDMA_OK Error type + */ +IDMA_API idma_status_t +idma_update_desc_src(int32_t ch, + void *src); + +/** + * @name Update size field of the "next" descriptor. + * @brief Update next to be schedule descriptor (means the next + * call to idma_desc_schedule() will schedule the desc. + * updated in this call. + * ONLY IN FIXED-BUFFER MODE. + * @param ch Selected iDMA HW channel. + * NOTE: Argument present only if LIBIDMA_USE_MULTICHANNEL_API defined. + * @param size New copy request size + * @retval IDMA_OK Successful + * @retval !IDMA_OK Error type + */ +IDMA_API idma_status_t +idma_update_desc_size(int32_t ch, + uint32_t size); + +/** + * @name Get buffer status w/ internal processing of completed descs. + * @brief Check the number of outstanding descriptors. Will also + * trigger internal processing making this call needed when no + * interrupts are available/setup. + * ONLY IN FIXED-BUFFER MODE. + * @brief Returns error or the number of outstanding descs + * @param ch Selected iDMA HW channel. + * NOTE: Argument present only if LIBIDMA_USE_MULTICHANNEL_API defined. + * @retval >=0 num. of outstanding descriptors + * @retval <0 Error type + */ +IDMA_API int32_t +idma_buffer_status(int32_t ch); + +/** + * @name Check if the IDMA HW is in error. If yes the function also + * sets the error details, sets the buffer status to indicate + * error and returns the error code. +* @param ch Selected iDMA HW channel. + * NOTE: Argument present only if LIBIDMA_USE_MULTICHANNEL_API defined. + * @retval IDMA_NO_ERR No Error. + * @retval !IDMA_NO_ERR Error type + */ +IDMA_API idma_hw_error_t +idma_buffer_check_errors(int32_t ch); + +/** + * These two functions operate exactly like idma_schedule_desc() and + * idma_schedule_desc_fast(), and in addition they also return the + * current clock value (which normally is the CCOUNT register value) + * in the location pointed to by the "ptime" argument. + */ +IDMA_API int32_t +idma_schedule_desc_clock(int32_t ch, + uint32_t count, + uint32_t *ptime); + +IDMA_API int32_t +idma_schedule_desc_fast_clock(int32_t ch, + uint32_t count, + uint32_t *ptime); + +#else // OLD API - no channels + +IDMA_API void idma_hw_wait_all(void); +IDMA_API void idma_hw_schedule(uint32_t count); +IDMA_API uint32_t idma_hw_num_outstanding(void); + +void idma_log_handler(idma_log_h xlog); + +idma_status_t idma_fast_wake_enable(void); +idma_status_t idma_fast_wake_disable(void); +IDMA_API idma_status_t idma_init(uint32_t flags, idma_max_block_t block_sz, uint32_t pif_req, idma_ticks_cyc_t ticks_per_cyc, uint32_t timeout_ticks, idma_err_callback_fn err_cb_func); +IDMA_API idma_status_t idma_add_desc(idma_buffer_t *bufh, void *dst, void *src, size_t size, uint32_t flags); +IDMA_API idma_status_t idma_add_2d_desc(idma_buffer_t *bufh, void *dst, void *src, size_t row_sz, uint32_t flags, uint32_t nrows, uint32_t src_pitch, uint32_t dst_pitch); +IDMA_API idma_status_t idma_init_loop (idma_buffer_t *bufh, idma_type_t type, int32_t ndescs, void *cb_data, idma_callback_fn cb_func); +IDMA_API idma_status_t idma_init_task(idma_buffer_t *taskh, idma_type_t type, int32_t ndescs, idma_callback_fn cb_func, void *cb_data); +IDMA_API idma_status_t idma_copy_task(idma_buffer_t *taskh, void *dst, void *src, size_t size, uint32_t flags, void *cb_data, idma_callback_fn cb_func); +IDMA_API idma_status_t idma_copy_2d_task(idma_buffer_t *taskh, void *dst, void *src, size_t row_sz, uint32_t flags, uint32_t nrows, uint32_t src_pitch, uint32_t dst_pitch, void *cb_data, idma_callback_fn cb_func); +IDMA_API idma_status_t idma_process_tasks(void); +IDMA_API idma_status_t idma_abort_tasks(void); +IDMA_API idma_status_t idma_stop(void); +IDMA_API idma_status_t idma_update_desc_dst(void *dst); +IDMA_API idma_status_t idma_update_desc_src(void *src); +IDMA_API idma_status_t idma_update_desc_size(uint32_t size); +idma_status_t idma_schedule_task( idma_buffer_t *taskh); + +IDMA_API idma_state_t idma_get_state(void); + +IDMA_API void idma_pause(void); +IDMA_API void idma_resume(void); + +IDMA_API int32_t idma_schedule_desc(uint32_t count); +IDMA_API int32_t idma_schedule_desc_fast(uint32_t count); +IDMA_API int32_t idma_copy_desc(void *dst, void *src, size_t size, uint32_t flags); +IDMA_API int32_t idma_copy_2d_desc(void *dst, void *src, size_t size, uint32_t flags, uint32_t nrows, uint32_t src_pitch, uint32_t dst_pitch); +IDMA_API int32_t idma_desc_done(int32_t index); +IDMA_API int32_t idma_buffer_status(void); +IDMA_API int32_t idma_task_status(idma_buffer_t *taskh); +IDMA_API idma_status_t idma_sleep(void); + +IDMA_API idma_hw_error_t idma_buffer_check_errors(void); + +IDMA_API idma_error_details_t* idma_error_details(void); +IDMA_API idma_error_details_t* idma_buffer_error_details(void); + +IDMA_API int32_t idma_schedule_desc_clock(uint32_t count, uint32_t *ptime); +IDMA_API int32_t idma_schedule_desc_fast_clock(uint32_t count, uint32_t *ptime); + +#endif // not LIBIDMA_USE_MULTICHANNEL_API + +/* For compatibility with macros that were used before */ +#define IDMA_HW_NUM_OUTSTANDING idma_hw_num_outstanding +#define IDMA_HW_SCHEDULE idma_hw_schedule +#define IDMA_HW_WAIT_ALL idma_hw_wait_all + +#if (IDMA_USE_64B_DESC > 0) + +/** + * @name Add 1D descriptor in 64B format to the next location in the buffer/task + * NOTE: This functions uses 32-bit src/dst addresses; it'll fetch + * one word behind the src/dst pointers. + */ +IDMA_API idma_status_t +idma_add_desc64( idma_buffer_t *bufh, + void *dst, + void *src, + size_t size, + uint32_t flags); + +/** + * @name Add 2D descriptor in 64B format to the next location in the buffer/task + * NOTE: This functions uses 32-bit src/dst addresses; it'll fetch + * one word behind the src/dst pointers. + */ +IDMA_API idma_status_t +idma_add_2d_desc64( idma_buffer_t *bufh, + void *dst, + void *src, + size_t row_sz, + uint32_t flags, + uint32_t nrows, + uint32_t src_pitch, + uint32_t dst_pitch); + +#if (XCHAL_IDMA_HAVE_2DPRED > 0) +/** + * @name Add predicated 2D descriptor in 64B format to the next location in the buffer/task + * NOTE: This functions uses 32-bit src/dst addresses; it'll fetch + * one word behind the src/dst pointers. + */ +IDMA_API idma_status_t +idma_add_2d_pred_desc64( idma_buffer_t *bufh, + void *dst, + void *src, + size_t row_sz, + uint32_t flags, + void* pred_mask, + uint32_t nrows, + uint32_t src_pitch, + uint32_t dst_pitch); +#endif + +/** + * @name Add 3D descriptor in 64B format to the next location in the buffer/task + * NOTE: This functions uses 32-bit src/dst addresses; it'll fetch + * one word behind the src/dst pointers. + */ +IDMA_API idma_status_t +idma_add_3d_desc64( idma_buffer_t *bufh, + void *dst, + void *src, + uint32_t flags, + size_t row_sz, + uint32_t nrows, + uint32_t ntiles, + uint32_t src_row_pitch, + uint32_t dst_row_pitch, + uint32_t src_tile_pitch, + uint32_t dst_tile_pitch); + +/** + * @name Create & schedule 1D copy request in 64B format + * @brief See idma_copy_task for details + * NOTE: This functions uses 32-bit src/dst addresses; it'll fetch + * one word behind the src/dst pointers. + */ +IDMA_API idma_status_t +idma_copy_task64(int32_t ch, + idma_buffer_t *taskh, + void *dst, + void *src, + size_t size, + uint32_t flags, + void *cb_data, + idma_callback_fn cb_func); + +/** + * @name Create & schedule 2D copy request in 64B format + * @brief See idma_copy_2d_task for details. + * NOTE: This functions uses 32-bit src/dst addresses; it'll fetch + * one word behind the src/dst pointers. + */ +IDMA_API idma_status_t +idma_copy_2d_task64(int32_t ch, + idma_buffer_t *taskh, + void *dst, + void *src, + size_t row_sz, + uint32_t flags, + uint32_t nrows, + uint32_t src_pitch, + uint32_t dst_pitch, + void *cb_data, + idma_callback_fn cb_func); + +#if (XCHAL_IDMA_HAVE_2DPRED > 0) +/** + * @name Create & schedule predicated 2D copy request in 64B format + * @brief See idma_copy_2d_task for details. + * NOTE: This functions uses 32-bit src/dst addresses; it'll fetch + * one word behind the src/dst pointers. + */ +IDMA_API idma_status_t +idma_copy_2d_pred_task64(int32_t ch, + idma_buffer_t *taskh, + void *dst, + void *src, + size_t row_sz, + uint32_t flags, + void* pred_mask, + uint32_t nrows, + uint32_t src_pitch, + uint32_t dst_pitch, + void *cb_data, + idma_callback_fn cb_func); +#endif + +/** + * @name Add and schedule a 1D descriptor in 64B format. + * @brief See idma_copy_desc for details. + * NOTE: This functions uses 32-bit src/dst addresses; it'll fetch + * one word behind the src/dst pointers. + */ +IDMA_API int32_t +idma_copy_desc64(int32_t ch, + void *dst, + void *src, + size_t size, + uint32_t flags); + +/** + * @name Add and schedule a 2D descriptor in 64B format. + * @brief See idma_copy_2d_desc for details. + * NOTE: This functions uses 32-bit src/dst addresses; it'll fetch + * one word behind the src/dst pointers. + */ +IDMA_API int32_t +idma_copy_2d_desc64(int32_t ch, + void *dst, + void *src, + size_t size, + uint32_t flags, + uint32_t nrows, + uint32_t src_pitch, + uint32_t dst_pitch); + +#if (XCHAL_IDMA_HAVE_2DPRED > 0) +/** + * @name Add and schedule a predicated 2D descriptor in 64B format. + * @brief See idma_copy_2d_desc for details. + * NOTE: This functions uses 32-bit src/dst addresses; it'll fetch + * one word behind the src/dst pointers. + */ +IDMA_API int32_t +idma_copy_2d_pred_desc64(int32_t ch, + void *dst, + void *src, + size_t size, + uint32_t flags, + void* pred_mask, + uint32_t nrows, + uint32_t src_pitch, + uint32_t dst_pitch); +#endif + +/** + * @name Add and schedule a 3D descriptor in 64B format. + * @brief See idma_copy_2d_desc for details. + * NOTE: This functions uses 32-bit src/dst addresses; it'll fetch + * one word behind the src/dst pointers. + */ +IDMA_API int32_t +idma_copy_3d_desc64(int32_t ch, + void *dst, + void *src, + uint32_t flags, + size_t row_sz, + uint32_t nrows, + uint32_t ntiles, + uint32_t src_row_pitch, + uint32_t dst_row_pitch, + uint32_t src_tile_pitch, + uint32_t dst_tile_pitch); + +#if (IDMA_USE_WIDE_API > 0) + +/** + * @name Add 1D descriptor in 64B format to the next location in the buffer/task. + * @brief See idma_add_2d_desc for details on arguments. + * NOTE: This functions uses 64-bit src/dst addresses; it'll fetch + * two words behind the src/dst pointers. + */ +IDMA_API idma_status_t +idma_add_desc64_wide( idma_buffer_t *bufh, + void *dst, + void *src, + size_t size, + uint32_t flags); + +/** + * @name Add 2D descriptor in 64B format to the next location in the buffer/task + * @brief See idma_add_2d_desc for details on arguments. + * NOTE: This functions uses 64-bit src/dst addresses; it'll fetch + * two words behind the src/dst pointers. + */ +IDMA_API idma_status_t +idma_add_2d_desc64_wide( idma_buffer_t *bufh, + void *dst, + void *src, + size_t row_sz, + uint32_t flags, + uint32_t nrows, + uint32_t src_pitch, + uint32_t dst_pitch); + +#if (XCHAL_IDMA_HAVE_2DPRED > 0) +/** + * @name Add predicated 2D descriptor in 64B format to the next location in the buffer/task. + * @brief See idma_add_2d_desc for details on arguments. + * NOTE: This functions uses 64-bit src/dst addresses; it'll fetch + * two words behind the src/dst pointers. + */ +IDMA_API idma_status_t +idma_add_2d_pred_desc64_wide( idma_buffer_t *bufh, + void *dst, + void *src, + size_t row_sz, + uint32_t flags, + void* pred_mask, + uint32_t nrows, + uint32_t src_pitch, + uint32_t dst_pitch); +#endif + +/** + * @name Add 3D descriptor in 64B format to the next location in the buffer/task. + * @brief See idma_add_3d_desc64 for details on arguments. + * NOTE: This functions uses 64-bit src/dst addresses; it'll fetch + * two words behind the src/dst pointers. + */ +IDMA_API idma_status_t +idma_add_3d_desc64_wide( idma_buffer_t *bufh, + void *dst, + void *src, + uint32_t flags, + size_t row_sz, + uint32_t nrows, + uint32_t ntiles, + uint32_t src_row_pitch, + uint32_t dst_row_pitch, + uint32_t src_tile_pitch, + uint32_t dst_tile_pitch); + +/** + * @name Create & schedule 1D copy request in 64B format. + * @brief See idma_copy_task for details + * NOTE: This functions uses 64-bit src/dst addresses; it'll fetch + * two words behind the src/dst pointers. + */ +IDMA_API idma_status_t +idma_copy_task64_wide(int32_t ch, + idma_buffer_t *taskh, + void *dst, + void *src, + size_t size, + uint32_t flags, + void *cb_data, + idma_callback_fn cb_func); + +/** + * @name Create & schedule 2D copy request in 64B format. + * @brief See idma_copy_task for details + * NOTE: This functions uses 64-bit src/dst addresses; it'll fetch + * two words behind the src/dst pointers. + */ +IDMA_API idma_status_t +idma_copy_2d_task64_wide(int32_t ch, + idma_buffer_t *taskh, + void *dst, + void *src, + size_t row_sz, + uint32_t flags, + uint32_t nrows, + uint32_t src_pitch, + uint32_t dst_pitch, + void *cb_data, + idma_callback_fn cb_func); + +#if (XCHAL_IDMA_HAVE_2DPRED > 0) +/** + * @name Create & schedule predicated 2D copy request in 64B format. + * @brief See idma_copy_task for details + * NOTE: This functions uses 64-bit src/dst addresses; it'll fetch + * two words behind the src/dst pointers. + */ +IDMA_API idma_status_t +idma_copy_2d_pred_task64_wide(int32_t ch, + idma_buffer_t *taskh, + void *dst, + void *src, + size_t row_sz, + uint32_t flags, + void* pred_mask, + uint32_t nrows, + uint32_t src_pitch, + uint32_t dst_pitch, + void *cb_data, + idma_callback_fn cb_func); +#endif + +/** + * @name Add and schedule a 1D descriptor in 64B format. + * @brief See idma_copy_desc for details. + * NOTE: This functions uses 64-bit src/dst addresses; it'll fetch + * two words behind the src/dst pointers. + */ +IDMA_API int32_t +idma_copy_desc64_wide(int32_t ch, + void *dst, + void *src, + size_t size, + uint32_t flags); + +/** + * @name Add and schedule a 2D descriptor in 64B format. + * @brief See idma_copy_2d_desc for details. + * NOTE: This functions uses 64-bit src/dst addresses; it'll fetch + * two word behind the src/dst pointers. + */ +IDMA_API int32_t +idma_copy_2d_desc64_wide(int32_t ch, + void *dst, + void *src, + size_t size, + uint32_t flags, + uint32_t nrows, + uint32_t src_pitch, + uint32_t dst_pitch); + +#if (XCHAL_IDMA_HAVE_2DPRED > 0) +/** + * @name Add and schedule a predicated 2D descriptor in 64B format. + * @brief See idma_copy_desc for details. + * NOTE: This functions uses 64-bit src/dst addresses; it'll fetch + * two words behind the src/dst pointers. + */ +IDMA_API int32_t +idma_copy_2d_pred_desc64_wide(int32_t ch, + void *dst, + void *src, + size_t size, + uint32_t flags, + void* pred_mask, + uint32_t nrows, + uint32_t src_pitch, + uint32_t dst_pitch); +#endif + +/** + * @name Add and schedule a 3D descriptor in 64B format. + * @brief See idma_copy_desc for details. + * NOTE: This functions uses 64-bit src/dst addresses; it'll fetch + * two words behind the src/dst pointers. + */ +IDMA_API int32_t +idma_copy_3d_desc64_wide(int32_t ch, + void *dst, + void *src, + uint32_t flags, + size_t row_sz, + uint32_t nrows, + uint32_t ntiles, + uint32_t src_row_pitch, + uint32_t dst_row_pitch, + uint32_t src_tile_pitch, + uint32_t dst_tile_pitch); + +#endif +#endif + + + + + +/*************************************************/ +/**** Internal Stuff ****/ +/**** DO NOT ACCESS OR RELY ON THE CODE BELOW ****/ +/*************************************************/ + +#ifdef IDMA_USE_WIDE_ADDRESS_COMPILE + +# define SRC_WIDE_ADDR_SHIFT UINT32_C(16) +# define DST_WIDE_ADDR_SHIFT UINT32_C(20) +# define IDMA_WIDE_ADDR_MASK UINT32_C(0x0000000F) + +idma_status_t +idma_copy_task_wide_i(int32_t ch, + idma_buffer_t * taskh, + void * dst, + void * src, + size_t size, + uint32_t flags, + void * cb_data, + idma_callback_fn cb_func); + +idma_status_t +idma_copy_2d_task_wide_i(int32_t ch, + idma_buffer_t * taskh, + void * dst, + void * src, + size_t row_sz, + uint32_t flags, + uint32_t nrows, + uint32_t src_pitch, + uint32_t dst_pitch, + void * cb_data, + idma_callback_fn cb_func); + +#endif // IDMA_USE_WIDE_ADDRESS_COMPILE + +#define IDMA_CHANNEL_0 0 +#define IDMA_CHANNEL_1 1 +#define IDMA_CHANNEL_2 2 +#define IDMA_CHANNEL_3 3 +#define IDMA_CHANNEL_4 4 +#define IDMA_CHANNEL_5 5 +#define IDMA_CHANNEL_6 6 +#define IDMA_CHANNEL_7 7 + + +#if (LIBIDMA_USE_MULTICHANNEL_API > 0) +# define IDMA_CH_PTR ch +# define IDMA_CHAN_FUNC_ARG int32_t ch, +# define IDMA_CHAN_FUNC_ARG_SINGLE int32_t ch +#else +# define IDMA_CH_PTR IDMA_CHANNEL_0 +# define IDMA_CHAN_FUNC_ARG +# define IDMA_CHAN_FUNC_ARG_SINGLE void +#endif + +#if XCHAL_HAVE_XEA2 || XCHAL_HAVE_XEA3 +# define IDMA_REG_ADDR(c,r) (IDMAREG_BASE((uint32_t)(c)) + (uint32_t)(r)) +# define IDMA_RER XT_RER +# define IDMA_WER XT_WER +#else +# define IDMA_RER(r) xthal_mmio_ld32((r)) +# define IDMA_WER(v,r) xthal_mmio_st32((r),(v)) +#endif + +#if XCHAL_HAVE_XEA5 +# define IDMA_DONE_INT(ch) ((uint32_t)XCHAL_IDMA_CH0_DONE_INTERRUPT + ((ch) * UINT32_C(2))) +# define IDMA_ERR_INT(ch) ((uint32_t)XCHAL_IDMA_CH0_ERR_INTERRUPT + ((ch) * UINT32_C(2))) +#else +# define IDMA_DONE_INT(ch) ((uint32_t)XCHAL_IDMA_CH0_DONE_INTERRUPT + (ch)) +# define IDMA_ERR_INT(ch) ((uint32_t)XCHAL_IDMA_CH0_ERR_INTERRUPT + (ch)) +#endif + + +#if defined (IDMA_USERMODE) +#define IDMA_1D_DESC_CODE UINT32_C(0xA03) +#define IDMA_2D_DESC_CODE UINT32_C(0xA07) +#define IDMA_JMP_DESC_CODE UINT32_C(0xA00) +#define IDMA_64B_DESC_CODE UINT32_C(0xA01) +#define IDMA_ZVC_DESC_CODE UINT32_C(0xA05) +#else +#define IDMA_1D_DESC_CODE UINT32_C(3) +#define IDMA_2D_DESC_CODE UINT32_C(7) +#define IDMA_JMP_DESC_CODE UINT32_C(0) +#define IDMA_64B_DESC_CODE UINT32_C(1) +#define IDMA_ZVC_DESC_CODE UINT32_C(5) +#endif + +// Applicable to 64-byte and zvc descriptors only +#define IDMA_64B_SUBTYPE_MASK UINT32_C(0x00000007) +#define IDMA_64B_SUBTYPE_SHIFT 4 +#define IDMA_64B_1D_TYPE UINT32_C(0) +#define IDMA_64B_2D_TYPE UINT32_C(1) +#define IDMA_64B_2D_COMPRESS_TYPE UINT32_C(2) +#define IDMA_64B_3D_TYPE UINT32_C(4) +#define IDMA_64B_2D_FBC_TYPE UINT32_C(6) +#define IDMA_ZVC_1D_TYPE UINT32_C(5) +#define IDMA_ZVC_2D_TYPE UINT32_C(7) +#define IDMA_64B_LONG_ADDR_CTRL_BIT_MASK UINT32_C(0x00000400) +#define SET_CONTROL_SUBTYPE(a) (((a) & IDMA_64B_SUBTYPE_MASK) << IDMA_64B_SUBTYPE_SHIFT) + +// Inline selection for "wrapper" functions +#define WRAPPER_FUNC static __attribute__((always_inline)) + +#define IDMA_1D_DESC_SIZE 16 +#define IDMA_2D_DESC_SIZE 32 +#define IDMA_64_DESC_SIZE 64 + +// Data placement in local memory. First, let the user select which dataram to +// use. If the user has defined neither IDMA_USE_DRAM0 or IDMA_USE_DRAM1, then +// - if both datarams are present, choose the one at the lower address +// - if only one is present, it must be dataram0 + +#if defined (IDMA_USE_DRAM_BSS) +# define IDMA_SECTION "bss" +#else +# define IDMA_SECTION "data" +#endif + +#if defined (IDMA_USE_DRAM1) +# define IDMA_DRAM __attribute__ ((section(".dram1."IDMA_SECTION))) +#elif defined (IDMA_USE_DRAM0) +# define IDMA_DRAM __attribute__ ((section(".dram0."IDMA_SECTION))) +#else +# if (XCHAL_NUM_DATARAM > 1) +# if XCHAL_DATARAM1_VADDR > XCHAL_DATARAM0_VADDR +# define IDMA_DRAM __attribute__ ((section(".dram0."IDMA_SECTION))) +# else +# define IDMA_DRAM __attribute__ ((section(".dram1."IDMA_SECTION))) +# endif +# else +# define IDMA_DRAM __attribute__ ((section(".dram0."IDMA_SECTION))) +# endif +#endif + +#ifdef IDMA_DEBUG +# define IDMA_CONTROL_STRUCT_SIZE_ 52 +#else +# define IDMA_CONTROL_STRUCT_SIZE_ 48 +#endif + +# ifdef IDMA_DEBUG +# define IDMA_ASSERT(expr) +void idma_print(int32_t ch, const char* fmt, ...); +# define XLOG(ch, ...) do { idma_print((ch), __VA_ARGS__);} while (0) // parasoft-suppress MISRA2012-RULE-20_7 "used only in non-FuSa code" +# else +# define IDMA_ASSERT(expr) +# define XLOG(ch, ...) (void)(ch); do {} while (0) +# endif + + +/* 1D descriptor structure */ +struct idma_desc_struct { + uint32_t control; + uint32_t src; + uint32_t dst; + uint32_t size; +}; + +/* 2D descriptor structure */ +struct idma_2d_desc_struct { + uint32_t control; + uint32_t src; + uint32_t dst; + uint32_t size; + uint32_t src_pitch; + uint32_t dst_pitch; + uint32_t nrows; + uint32_t word8; +}; + +/* 64B descriptor structure */ +struct idma_desc64_struct { + uint32_t control; + uint32_t src; + uint32_t dst; + uint32_t size; + uint32_t src_pitch; + uint32_t dst_pitch; + uint32_t nrows; + uint32_t word8; + int32_t src_tile_pitch; + int32_t dst_tile_pitch; + uint32_t ntiles; + uint32_t pred_mask; + uint32_t word13; + uint32_t word14; + uint32_t ext_src; + uint32_t ext_dst; +}; + +/* Real buffer struct - NOT TO BE USED BY APPLICATION */ + +struct idma_buf_struct { + idma_desc_t * next_desc; // points past the last scheduled desc. Used by functions + // needing to update the desc that is just to be scheduled. + idma_desc_t * next_add_desc; // points past the last added desc. Used by functions that + // populate a new task, or populate a buffer where later + // schedule call will schedule them all at once. + + int32_t num_descs; // total num of descs, assigned on init. In fixed buffer mode + // it sets the size of the circular buffer by setting JUMP + // command between the last desc and the 1st one. In TASK mode + // it is used to tell the lib how many descs to schedule. + idma_desc_t * last_desc; // points past the last desc. Assigned to sped-up wrap-arround + // calculation, not to use num_descs and multiplication. + + int32_t type; // descs type, assigned on init. + + idma_buf_t * next_task; // TASK: points to next task, assigned on schedule + int32_t cur_desc_i; // FIXED-BUFFER: tracks next desc to execute. + int32_t status; // TASK: # of remaining descs after schedule. + // BOTH modes: error type, if in error. + idma_callback_fn cb_func; // Callback on completion + void * cb_data; // Completion callback argument + int32_t ch; + void * thread_id; // (OS-specific) ID of owning thread + int16_t sleeping; // Nonzero when thread is sleeping (blocked) + int16_t pending; // Nonzero when buffer is queued + int32_t pending_desc_cnt; + + idma_desc_t desc __attribute__ ((aligned(16))); +}; + + +/* Inline functions for iDMA register read/write */ +ALWAYS_INLINE uint32_t +READ_IDMA_REG(int32_t ch, int32_t reg) +{ + return IDMA_RER(IDMA_REG_ADDR(ch,reg)); +} + +ALWAYS_INLINE void +WRITE_IDMA_REG(int32_t ch, int32_t reg, uint32_t val) +{ + IDMA_WER(val, IDMA_REG_ADDR(ch,reg)); +} + +ALWAYS_INLINE void +set_desc_addr_fields(idma_desc_t * desc, + void * dst, + void * src) +{ +#ifdef IDMA_USE_WIDE_ADDRESS_COMPILE + uint32_t * src36 = cvt_voidp_to_uint32p(src); + uint32_t * dst36 = cvt_voidp_to_uint32p(dst); + uint32_t wide_addr_mask = IDMA_WIDE_ADDR_MASK; + + desc->src = src36[0]; + desc->dst = dst36[0]; + desc->control = + (desc->control & ~(wide_addr_mask << SRC_WIDE_ADDR_SHIFT)) | ((src36[1] & wide_addr_mask) << SRC_WIDE_ADDR_SHIFT); + desc->control = + (desc->control & ~(wide_addr_mask << DST_WIDE_ADDR_SHIFT)) | ((dst36[1] & wide_addr_mask) << DST_WIDE_ADDR_SHIFT); +#else + desc->src = cvt_voidp_to_uint32(src); + desc->dst = cvt_voidp_to_uint32(dst); +#endif +} + +ALWAYS_INLINE void +set_1d_fields( int32_t ch, + idma_desc_t* desc, + void *dst, + void *src, + size_t size) +{ + set_desc_addr_fields(desc, dst, src); + desc->size = size; +#ifdef IDMA_USE_WIDE_ADDRESS_COMPILE +# ifdef IDMA_DEBUG + { + uint32_t src64 = (desc->control >> SRC_WIDE_ADDR_SHIFT) & IDMA_WIDE_ADDR_MASK; + uint32_t dst64 = (desc->control >> DST_WIDE_ADDR_SHIFT) & IDMA_WIDE_ADDR_MASK; + XLOG(ch, "Add 1D desc wide @ %p, %d-bytes(%x-%p -> %x-%p), control:0x%x\n", + desc, desc->size, src64, desc->src, dst64, desc->dst, desc->control); + } +# endif +#else + XLOG(ch, "Add 1D desc @ %p, %d-bytes(%p -> %p), control:0x%x\n", desc, desc->size, desc->src, desc->dst, desc->control); +#endif + (void)(ch); +} + +ALWAYS_INLINE void +set_2d_fields(int32_t ch, + idma_desc_t* desc, + void *dst, + void *src, + size_t size, + uint32_t nrows, + uint32_t src_pitch, + uint32_t dst_pitch) +{ + idma_2d_desc_t* desc2d = (idma_2d_desc_t*) desc; // parasoft-suppress MISRA2012-RULE-11_3 "conversion checked" + set_desc_addr_fields(desc, dst, src); + desc2d->size = size; + desc2d->nrows = nrows; + desc2d->src_pitch = src_pitch; + desc2d->dst_pitch = dst_pitch; +#ifdef IDMA_USE_WIDE_ADDRESS_COMPILE +# ifdef IDMA_DEBUG + { + uint32_t src64 = (desc->control >> SRC_WIDE_ADDR_SHIFT) & IDMA_WIDE_ADDR_MASK; + uint32_t dst64 = (desc->control >> DST_WIDE_ADDR_SHIFT) & IDMA_WIDE_ADDR_MASK; + XLOG(ch, "Add 2D desc wide @ %p, %d-bytes (%x-%p -> %x-%p)\n", + desc2d, desc2d->size, src64, desc->src, dst64, desc->dst); + XLOG(ch, " (#rows:%d, src_pitch:%d, dst_pitch:%d, control:0x%x)\n", + desc2d->nrows, desc2d->src_pitch, desc2d->dst_pitch, desc2d->control); + + } +# endif +#else + XLOG(ch, "Add 2D desc @ %p, %d-bytes (%p -> %p)\n", desc2d, desc2d->size, desc2d->src, desc2d->dst); + XLOG(ch, " (#rows:%d, src_pitch:%d, dst_pitch:%d, control:0x%x)\n", + desc2d->nrows, desc2d->src_pitch, desc2d->dst_pitch, desc2d->control); +#endif + (void)(ch); +} + +#if (IDMA_USE_64B_DESC > 0) + +INTERNAL_FUNC void +set_desc64_short_addr(idma_desc64_t* desc, void *dst, void *src) // parasoft-suppress MISRA2012-RULE-8_13_a-4 "Cannot use const, backward compatibility and dst is modified" +{ + desc->src = (cvt_voidp_to_uint32p(src))[0]; + desc->dst = (cvt_voidp_to_uint32p(dst))[0]; +} + + +INTERNAL_FUNC void +set_desc64_2d_pitch(idma_desc64_t* desc, uint32_t src_pitch, uint32_t dst_pitch) +{ + desc->src_pitch = src_pitch; + desc->dst_pitch = dst_pitch; +} + +INTERNAL_FUNC void +set_desc64_3d_pitch(idma_desc64_t* desc, uint32_t src_pitch, uint32_t dst_pitch, uint32_t src_tile_pitch, uint32_t dst_tile_pitch) +{ + set_desc64_2d_pitch(desc, src_pitch, dst_pitch); + desc->src_tile_pitch = (int32_t) src_tile_pitch; + desc->dst_tile_pitch = (int32_t) dst_tile_pitch; +} + +INTERNAL_FUNC void +set_1d_desc64_fields(int32_t ch, + idma_desc_t* desch, + void *dst, + void *src, + size_t size) +{ + idma_desc64_t* desc = cvt_desc_to_desc64(desch); + + set_desc64_short_addr(desc, dst, src); + desc->size = size; + XLOG(ch, "Add 1D desc64 @ %p, %d-bytes(%lx -> %lx), control:0x%x\n", desc, desc->size, desc->src, desc->dst, desc->control); +} + +INTERNAL_FUNC void +set_2d_desc64_fields(int32_t ch, + idma_desc_t* desch, + void *dst, + void *src, + size_t size, + uint32_t nrows, + uint32_t src_pitch, + uint32_t dst_pitch) +{ + idma_desc64_t* desc = cvt_desc_to_desc64(desch); + + set_desc64_short_addr(desc, dst, src); + set_desc64_2d_pitch(desc, src_pitch, dst_pitch); + desc->size = size; + desc->nrows = nrows; + + XLOG(ch, "Add 2D desc @ %p, %d-bytes (%x -> %x)\n", desc, desc->size, desc->src, desc->dst); + XLOG(ch, " (#rows:%d, src_pitch:%d, dst_pitch:%d, control:0x%x)\n", desc->nrows, desc->src_pitch, desc->dst_pitch, desc->control); +} + +INTERNAL_FUNC void +set_3d_desc64_fields(int32_t ch, + idma_desc_t* desch, + void *dst, + void *src, + size_t row_sz, + uint32_t nrows, + uint32_t ntiles, + uint32_t src_pitch, + uint32_t dst_pitch, + uint32_t src_tile_pitch, + uint32_t dst_tile_pitch) +{ + idma_desc64_t* desc = cvt_desc_to_desc64(desch); + + set_desc64_short_addr(desc, dst, src); + set_desc64_3d_pitch(desc, src_pitch, dst_pitch, src_tile_pitch, dst_tile_pitch); + + desc->size = row_sz; + desc->nrows = nrows; + desc->ntiles = ntiles; + + XLOG(ch, "Add 3D desc @ %p, %d-bytes (%x -> %x)\n", desc, desc->size, desc->src, desc->dst); + XLOG(ch, " (#row_sz/rows/tiles:%d/%d/%d, row_pitch:%d/%d, tile_pitch:%d/%d, control:0x%x)\n", + desc->size,desc->nrows, desc->ntiles, desc->src_pitch, desc->dst_pitch, src_tile_pitch, dst_tile_pitch, desc->control); +} + +INTERNAL_FUNC void +set_desc64_ctrl(idma_desc_t* desch, uint32_t flags, uint32_t code) +{ + idma_desc64_t* desc = cvt_desc_to_desc64(desch); + desc->control = (flags | code); +} + +#if (IDMA_USE_WIDE_API > 0) + +INTERNAL_FUNC void +set_desc64_long_addr(idma_desc64_t* desc, void *dst, void *src) +{ + set_desc64_short_addr(desc, dst, src); + desc->ext_src = (cvt_voidp_to_uint32p(src))[1]; + desc->ext_dst = (cvt_voidp_to_uint32p(dst))[1]; +} + +INTERNAL_FUNC void +set_1d_desc64_fields_wide(int32_t ch, + idma_desc_t* desch, + void *dst, + void *src, + size_t size) +{ + idma_desc64_t* desc = cvt_desc_to_desc64(desch); + + set_desc64_long_addr(desc, dst, src); + desc->size = size; + XLOG(ch, "Add 1D desc64 wide @ %p, %d-bytes(%x-%x -> %x-%x), control:0x%x\n", desc, desc->size, desc->ext_src, desc->src, desc->ext_dst, desc->dst, desc->control); +} + +INTERNAL_FUNC void +set_2d_desc64_fields_wide(int32_t ch, + idma_desc_t* desch, + void *dst, + void *src, + size_t size, + uint32_t nrows, + uint32_t src_pitch, + uint32_t dst_pitch) +{ + idma_desc64_t* desc = cvt_desc_to_desc64(desch); + + set_desc64_long_addr(desc, dst, src); + set_desc64_2d_pitch(desc, src_pitch, dst_pitch); + desc->size = size; + desc->nrows = nrows; + + XLOG(ch, "Add 2D desc wide @ %p, %d-bytes (%x-%x -> %x-%x)\n", desc, desc->size, desc->ext_src, desc->src, desc->ext_dst, desc->dst); + XLOG(ch, " (#rows:%d, src_pitch:%d, dst_pitch:%d, control:0x%x)\n", desc->nrows, desc->src_pitch, desc->dst_pitch, desc->control); +} + +INTERNAL_FUNC void +set_3d_desc64_fields_wide(int32_t ch, + idma_desc_t* desch, + void *dst, + void *src, + size_t row_sz, + uint32_t nrows, + uint32_t ntiles, + uint32_t src_pitch, + uint32_t dst_pitch, + uint32_t src_tile_pitch, + uint32_t dst_tile_pitch) +{ + idma_desc64_t* desc = cvt_desc_to_desc64(desch); + + set_desc64_long_addr(desc, dst, src); + set_desc64_3d_pitch(desc, src_pitch, dst_pitch, src_tile_pitch, dst_tile_pitch); + desc->size = row_sz; + desc->nrows = nrows; + desc->ntiles = ntiles; + + XLOG(ch, "Add 3D desc wide @ %p, %d-bytes (%x-%x -> %x-%x)\n", desc, desc->size, desc->ext_src, desc->src, desc->ext_dst, desc->dst); + XLOG(ch, " (#row_sz/rows/tiles:%d/%d/%d, row_pitch:%d/%d, tile_pitch:%d/%d, control:0x%x)\n", + desc->size,desc->nrows, desc->ntiles, desc->src_pitch, desc->dst_pitch, src_tile_pitch, dst_tile_pitch, desc->control); +} + +#endif +#endif + +INTERNAL_FUNC void +set_desc_ctrl(idma_desc_t* desc, uint32_t flags, uint32_t code) +{ + desc->control = (flags | code); +} + +INTERNAL_FUNC void +update_next_add_ptr(idma_buf_t* buf) +{ + idma_desc_t* next = &buf->next_add_desc[buf->type]; + + if (next >= buf->last_desc) { + next = &buf->desc; + } + buf->next_add_desc = next; +} + +#if defined(IDMA_USE_XTOS) || defined(IDMA_APP_USE_XTOS) +INTERNAL_FUNC void +update_next_desc(int32_t ch, uint32_t count) +{ + idma_buf_t* buf; + idma_desc_t* next; + + buf = idma_chan_buf_get(ch); + if (buf != NULL) { + next = &buf->next_desc[count*(uint32_t)buf->type]; // parasoft-suppress MISRA2012-DIR-4_1_b "Skip check for performance" + + if (next >= buf->last_desc) { + next = &buf->desc + (next - buf->last_desc); // parasoft-suppress MISRA2012-RULE-18_4-4 "Pointer arithmetic cleaner than casting" + } + buf->next_desc = next; + } +} +#endif + +INTERNAL_FUNC void +hw_schedule(int32_t ch, uint32_t count) +{ + extern uint32_t g_idma_ctrl; + +#if XCHAL_HAVE_XEA2 || XCHAL_HAVE_XEA3 + XT_MEMW(); +#endif + WRITE_IDMA_REG(ch, IDMA_REG_CONTROL, 0x1U | g_idma_ctrl); + WRITE_IDMA_REG(ch, IDMA_REG_DESC_INC, count); +} + +INTERNAL_FUNC uint32_t +hw_num_outstanding(int32_t ch) +{ + return READ_IDMA_REG(ch, IDMA_REG_NUM_DESC); +} + +INTERNAL_FUNC int32_t +schedule_desc_fast(int32_t ch, uint32_t count) +{ + idma_buf_t* buf = idma_chan_buf_get(ch); + if (buf != NULL) { + buf->cur_desc_i += (int32_t)count; + buf->cur_desc_i &= INT32_C(0x7fffffff); + + XLOG(ch, "Schedule %d desc from index:%d\n", count, buf->cur_desc_i); + hw_schedule(ch, count); + return buf->cur_desc_i; + } + + return (int32_t) IDMA_ERR_BAD_CHAN; +} + +#if defined(IDMA_USE_XTOS) || defined(IDMA_APP_USE_XTOS) +INTERNAL_FUNC int32_t +schedule_desc(int32_t ch, uint32_t count) +{ + update_next_desc(ch, count); + return schedule_desc_fast(ch, count); +} +#else +int32_t schedule_thread_buffer(int32_t ch, uint32_t count); + +INTERNAL_FUNC int32_t +schedule_desc(int32_t ch, uint32_t count) +{ + return schedule_thread_buffer(ch, count); +} +#endif + +WRAPPER_FUNC idma_buf_t * +convert_buffer_to_buf(idma_buffer_t * buffer) // parasoft-suppress MISRA2012-RULE-8_13_a-4 "Pointer is converted, cannot use const here" +{ + return (idma_buf_t *) buffer; // parasoft-suppress MISRA2012-RULE-11_3-2 "Conversion to internal data type OK" +} + + +idma_status_t idma_sleep_i(int32_t ch); +int32_t idma_buffer_status_i(int32_t ch); +idma_state_t idma_get_state_i(int32_t ch); +idma_status_t idma_init_i(int32_t ch, uint32_t flags, idma_max_block_t block_sz, uint32_t pif_req, idma_ticks_cyc_t ticks_per_cyc, uint32_t timeout_ticks, idma_err_callback_fn err_cb_func); +void idma_stop_i(int32_t ch); +void idma_pause_i(int32_t ch); +void idma_enable_i(int32_t ch); +void idma_resume_i(int32_t ch); +idma_status_t idma_process_tasks_i(int32_t ch); +idma_status_t idma_abort_tasks_i(int32_t ch); +idma_status_t idma_init_task_i (int32_t ch, idma_buffer_t *taskh, idma_type_t type, int32_t ndescs, idma_callback_fn cb_func, void *cb_data); +idma_status_t idma_copy_task_i(int32_t ch, idma_buffer_t *taskh, void *dst, void *src, size_t size, uint32_t flags, void *cb_data, idma_callback_fn cb_func); +idma_status_t idma_copy_task64_i(int32_t ch, idma_buffer_t *taskh, void *dst, void *src, size_t size, uint32_t flags, void *cb_data, idma_callback_fn cb_func); +idma_status_t idma_copy_task64_wide_i(int32_t ch, idma_buffer_t *taskh, void *dst, void *src, size_t size, uint32_t flags, void *cb_data, idma_callback_fn cb_func); +idma_status_t idma_copy_2d_task64_i(int32_t ch, idma_buffer_t *taskh, void *dst, void *src, size_t row_sz, uint32_t flags, uint32_t nrows, uint32_t src_pitch, uint32_t dst_pitch, void *cb_data, idma_callback_fn cb_func); +idma_status_t idma_copy_2d_task64_wide_i(int32_t ch, idma_buffer_t *taskh, void *dst, void *src, size_t row_sz, uint32_t flags, uint32_t nrows, uint32_t src_pitch, uint32_t dst_pitch, void *cb_data, idma_callback_fn cb_func); +idma_status_t idma_copy_2d_pred_task64_i(int32_t ch, idma_buffer_t *taskh, void *dst, void *src, size_t row_sz, uint32_t flags, void* pred_mask, uint32_t nrows, uint32_t src_pitch, uint32_t dst_pitch, void *cb_data, idma_callback_fn cb_func); +idma_status_t idma_copy_2d_pred_task64_wide_i(int32_t ch, idma_buffer_t *taskh, void *dst, void *src, size_t row_sz, uint32_t flags, void* pred_mask, uint32_t nrows, uint32_t src_pitch, uint32_t dst_pitch, void *cb_data, idma_callback_fn cb_func); +idma_status_t idma_copy_3d_task64_i( int32_t ch, idma_buffer_t *taskh, void *dst, void *src, uint32_t flags, size_t row_sz, uint32_t nrows, uint32_t ntiles, uint32_t src_pitch, uint32_t dst_pitch, uint32_t src_tile_pitch, uint32_t dst_tile_pitch, void *cb_data, idma_callback_fn cb_func); +idma_status_t idma_copy_3d_task64_wide_i( int32_t ch, idma_buffer_t *taskh, void *dst, void *src, uint32_t flags, size_t row_sz, uint32_t nrows, uint32_t ntiles, uint32_t src_pitch, uint32_t dst_pitch, uint32_t src_tile_pitch, uint32_t dst_tile_pitch, void *cb_data, idma_callback_fn cb_func); +idma_status_t idma_copy_2d_task_i(int32_t ch, idma_buffer_t *taskh, void *dst, void *src, size_t row_sz, uint32_t flags, uint32_t nrows, uint32_t src_pitch, uint32_t dst_pitch, void *cb_data, idma_callback_fn cb_func); +idma_status_t idma_init_loop_i (int32_t ch, idma_buffer_t* bufh, idma_type_t type, int32_t ndescs, void* cb_data, idma_callback_fn cb_func); +idma_hw_error_t idma_buffer_check_errors_i(int32_t ch); +idma_error_details_t* idma_error_details_i(int32_t ch); + + +/************************************************/ +/**** API Implementation ****/ +/************************************************/ + +IDMA_API void +idma_hw_wait_all(IDMA_CHAN_FUNC_ARG_SINGLE) +{ + uint32_t state; + do { + state = READ_IDMA_REG(IDMA_CH_PTR, IDMA_REG_STATUS) & IDMA_STATE_MASK; + if ((state == (uint32_t)IDMA_STATE_DONE) || (state == (uint32_t)IDMA_STATE_IDLE) || (state == (uint32_t)IDMA_STATE_ERROR)) { + break; + } + } while (1); +} + +IDMA_API void +idma_hw_schedule(IDMA_CHAN_FUNC_ARG + uint32_t count) +{ +#if defined(IDMA_USE_XTOS) || defined(IDMA_APP_USE_XTOS) + hw_schedule(IDMA_CH_PTR, count); +#else +# if (LIBIDMA_USE_MULTICHANNEL_API > 0) + (void) idma_schedule_desc(ch, count); +# else + (void) idma_schedule_desc(count); +# endif +#endif +} + +IDMA_API uint32_t +idma_hw_num_outstanding(IDMA_CHAN_FUNC_ARG_SINGLE) +{ + return hw_num_outstanding(IDMA_CH_PTR); +} + +IDMA_API idma_state_t +idma_get_state(IDMA_CHAN_FUNC_ARG_SINGLE) +{ + return idma_get_state_i(IDMA_CH_PTR); +} + +IDMA_API idma_status_t +idma_init( IDMA_CHAN_FUNC_ARG + uint32_t flags, + idma_max_block_t block_sz, + uint32_t pif_req, + idma_ticks_cyc_t ticks_per_cyc, + uint32_t timeout_ticks, + idma_err_callback_fn err_cb_func) +{ + return idma_init_i(IDMA_CH_PTR, flags, block_sz, pif_req, ticks_per_cyc, timeout_ticks, err_cb_func); +} + +IDMA_API idma_status_t +idma_stop(IDMA_CHAN_FUNC_ARG_SINGLE) +{ + idma_stop_i(IDMA_CH_PTR); + return IDMA_OK; +} + +IDMA_API void +idma_pause(IDMA_CHAN_FUNC_ARG_SINGLE) +{ + idma_pause_i(IDMA_CH_PTR); +} + +IDMA_API void +idma_resume(IDMA_CHAN_FUNC_ARG_SINGLE) +{ + idma_resume_i(IDMA_CH_PTR); +} + +IDMA_API idma_status_t +idma_sleep(IDMA_CHAN_FUNC_ARG_SINGLE) +{ +#if defined(IDMA_USERMODE) + return IDMA_CANT_SLEEP; +#else + return idma_sleep_i(IDMA_CH_PTR); +#endif +} + +IDMA_API idma_status_t +idma_init_loop (IDMA_CHAN_FUNC_ARG + idma_buffer_t *bufh, + idma_type_t type, + int32_t ndescs, + void *cb_data, + idma_callback_fn cb_func) +{ + return idma_init_loop_i(IDMA_CH_PTR, bufh, type, ndescs, cb_data, cb_func); +} + +IDMA_API int32_t +idma_buffer_status(IDMA_CHAN_FUNC_ARG_SINGLE) { + return idma_buffer_status_i(IDMA_CH_PTR); +} + +IDMA_API idma_status_t +idma_add_desc( idma_buffer_t *bufh, + void *dst, + void *src, + size_t size, + uint32_t flags) +{ + idma_buf_t *buf; + DECLARE_PS(); + IDMA_DISABLE_INTS(); + buf = convert_buffer_to_buf(bufh); + + set_desc_ctrl(buf->next_add_desc, flags, IDMA_1D_DESC_CODE); + set_1d_fields(buf->ch, buf->next_add_desc, dst, src, size); + + update_next_add_ptr(buf); + IDMA_ENABLE_INTS(); + return IDMA_OK; +} + + +static inline idma_status_t +idma_add_2d_desc( idma_buffer_t *bufh, void *dst, void *src, size_t row_sz, uint32_t flags, uint32_t nrows, uint32_t src_pitch, uint32_t dst_pitch) +{ + idma_buf_t *buf; + DECLARE_PS(); + + IDMA_DISABLE_INTS(); + + buf = convert_buffer_to_buf(bufh); + + set_desc_ctrl(buf->next_add_desc, flags, IDMA_2D_DESC_CODE); + set_2d_fields(buf->ch, buf->next_add_desc, dst, src, row_sz, nrows, src_pitch, dst_pitch); + + update_next_add_ptr(buf); + + IDMA_ENABLE_INTS(); + return IDMA_OK; +} + +IDMA_API int32_t +idma_copy_desc(IDMA_CHAN_FUNC_ARG + void *dst, + void *src, + size_t size, + uint32_t flags) +{ + int32_t ret; + idma_buf_t* buf; + DECLARE_PS(); + + IDMA_DISABLE_INTS(); + + buf = idma_chan_buf_get(IDMA_CH_PTR); + + set_desc_ctrl(buf->next_desc, flags, IDMA_1D_DESC_CODE); + set_1d_fields(IDMA_CH_PTR, buf->next_desc, dst, src, size); + + ret = schedule_desc(IDMA_CH_PTR, 1U); + IDMA_ENABLE_INTS(); + + return ret; +} + +IDMA_API int32_t +idma_copy_2d_desc(IDMA_CHAN_FUNC_ARG void *dst, void *src, size_t size, uint32_t flags, uint32_t nrows, uint32_t src_pitch, uint32_t dst_pitch) +{ + idma_buf_t* buf; + DECLARE_PS(); + int32_t ret = 0; + + IDMA_DISABLE_INTS(); + + buf = idma_chan_buf_get(IDMA_CH_PTR); + + set_desc_ctrl(buf->next_desc, flags, IDMA_2D_DESC_CODE); + set_2d_fields (IDMA_CH_PTR, buf->next_desc, dst, src, size, nrows, src_pitch, dst_pitch); + + ret = schedule_desc(IDMA_CH_PTR, 1U); + IDMA_ENABLE_INTS(); + return ret; +} + +IDMA_API int32_t +idma_schedule_desc(IDMA_CHAN_FUNC_ARG + uint32_t count) +{ + int32_t ret; + DECLARE_PS(); + + IDMA_DISABLE_INTS(); + + ret = schedule_desc(IDMA_CH_PTR, count); + IDMA_ENABLE_INTS(); + return ret; +} + +IDMA_API int32_t +idma_schedule_desc_fast(IDMA_CHAN_FUNC_ARG + uint32_t count) +{ + int32_t ret; + DECLARE_PS(); + + IDMA_DISABLE_INTS(); +#if defined(IDMA_USE_XTOS) || defined(IDMA_APP_USE_XTOS) + ret = schedule_desc_fast(IDMA_CH_PTR, count); +#else + ret = schedule_desc(IDMA_CH_PTR, count); +#endif + IDMA_ENABLE_INTS(); + return ret; +} + +IDMA_API int32_t +idma_desc_done(IDMA_CHAN_FUNC_ARG + int32_t index) +{ + idma_buf_t* buf; + int32_t diff ; + DECLARE_PS(); + int32_t ret = 0; + + IDMA_DISABLE_INTS(); + + buf = idma_chan_buf_get(IDMA_CH_PTR); + + if (buf == NULL) { + ret = -1; + } + else if (buf != g_idma_buf_ptr[IDMA_CH_PTR]) { // parasoft-suppress MISRA2012-RULE-14_3_zc--1 "Not a constant expression in all configs" +#if defined(IDMA_USE_XTOS) || defined(IDMA_APP_USE_XTOS) + ret = -1; +#else + /* There are 3 cases to handle when the buffer is not the active buffer: + * - buffer is queued but not yet started, + * - buffer has completed successfully, or + * - buffer has encountered an error. + */ + ret = (buf->pending > 0) ? 0 : (buf->status == 0) ? 1 : -1; +#endif + } + else { + /* Descriptor is done if it's index is less then or equal the difference between + the last scheduled descriptor, and the number of outstanding descriptors. + We take the diff between the last scheduled descriptor and the index. + This diff is (cur_desc_i - index) or 0x7fffffff - (index - cur_desc_i) + depending on which value is larger. + */ + diff = (buf->cur_desc_i - index) & INT32_C(0x7fffffff); + if ((int32_t)hw_num_outstanding(IDMA_CH_PTR) <= diff) { + ret = 1; + } + + XLOG(IDMA_CH_PTR, "Descriptor @%u: %s (remaining: %d, current:%d) \n", + index, ((ret != 0) ? "DONE" : "PENDING"), hw_num_outstanding(IDMA_CH_PTR), buf->cur_desc_i); + } + + IDMA_ENABLE_INTS(); + return ret; +} + +ALWAYS_INLINE idma_status_t +idma_update_desc_dst(IDMA_CHAN_FUNC_ARG + void *dst) +{ + idma_desc_t *desc; + idma_buf_t *buf; +#ifdef IDMA_USE_WIDE_ADDRESS_COMPILE + uint32_t * dstw = cvt_voidp_to_uint32p(dst); +#endif + DECLARE_PS(); + + IDMA_DISABLE_INTS(); + + buf = idma_chan_buf_get(IDMA_CH_PTR); + desc = buf->next_desc; +#ifdef IDMA_USE_WIDE_ADDRESS_COMPILE + desc->dst = dstw[0]; + { + uint32_t wide_addr_mask = IDMA_WIDE_ADDR_MASK; + desc->control = + (desc->control & ~(wide_addr_mask << DST_WIDE_ADDR_SHIFT)) | ((dstw[1] & wide_addr_mask) << DST_WIDE_ADDR_SHIFT); + } + XLOG(IDMA_CH_PTR, "Change dst field for descriptor @ %p to: %p-%p\n", + desc, cvt_uint32_to_voidp(dstw[1]), cvt_uint32_to_voidp(dstw[0])); +#else + desc->dst = cvt_voidp_to_uint32(dst); + XLOG(IDMA_CH_PTR, "Change dst field for descriptor @ %p to: %p\n", + desc, desc->dst); +#endif + IDMA_ENABLE_INTS(); + return IDMA_OK; +} + +ALWAYS_INLINE idma_status_t +idma_update_desc_src(IDMA_CHAN_FUNC_ARG + void *src) +{ + idma_desc_t *desc; + idma_buf_t *buf; +#ifdef IDMA_USE_WIDE_ADDRESS_COMPILE + uint32_t * srcw = cvt_voidp_to_uint32p(src); +#endif + + DECLARE_PS(); + + IDMA_DISABLE_INTS(); + + buf = idma_chan_buf_get(IDMA_CH_PTR); + desc = buf->next_desc; +#ifdef IDMA_USE_WIDE_ADDRESS_COMPILE + desc->src = srcw[0]; + { + uint32_t wide_addr_mask = IDMA_WIDE_ADDR_MASK; + desc->control = + (desc->control & ~(wide_addr_mask << SRC_WIDE_ADDR_SHIFT)) | ((srcw[1] & wide_addr_mask) << SRC_WIDE_ADDR_SHIFT); + } + XLOG(IDMA_CH_PTR, "Change src field for descriptor @ %p to: %p-%p\n", + desc, cvt_uint32_to_voidp(srcw[1]), cvt_uint32_to_voidp(srcw[0])); +#else + desc->src = cvt_voidp_to_uint32(src); + XLOG(IDMA_CH_PTR, "Change src field for descriptor @ %p to: %p\n", + desc, desc->src); +#endif + IDMA_ENABLE_INTS(); + return IDMA_OK; +} + +IDMA_API idma_status_t +idma_update_desc_size(IDMA_CHAN_FUNC_ARG + uint32_t size) +{ + idma_desc_t *desc; + idma_buf_t *buf; + DECLARE_PS(); + + IDMA_DISABLE_INTS(); + + buf = idma_chan_buf_get(IDMA_CH_PTR); + desc = buf->next_desc; + desc->size = size; + + XLOG(IDMA_CH_PTR, "Change size field for descriptor @ %p to: %d\n", desc, desc->size); + IDMA_ENABLE_INTS(); + return IDMA_OK; +} + +#if (IDMA_USE_64B_DESC > 0) + +IDMA_API idma_status_t +idma_update_desc64_dst(int32_t ch, void *dst) // parasoft-suppress MISRA2012-RULE-8_13_a-4 "Cannot use const, dst modified" +{ + idma_desc64_t *desc; + idma_buf_t *buf; + idma_status_t ret; + DECLARE_PS(); + + IDMA_DISABLE_INTS(); + + buf = idma_chan_buf_get(ch); + if (buf != NULL) { + desc = cvt_desc_to_desc64(buf->next_desc); + desc->dst = (cvt_voidp_to_uint32p(dst))[0]; + XLOG(ch, "Change dst field for descriptor @ %p to: %p\n", desc, desc->dst); + ret = IDMA_OK; + } + else { + ret = IDMA_ERR_NO_BUF; + } + + IDMA_ENABLE_INTS(); + return ret; +} + +IDMA_API idma_status_t +idma_update_desc64_src(int32_t ch, void *src) // parasoft-suppress MISRA2012-RULE-8_13_a-4 "Cannot use const (backward compatibility)" +{ + idma_desc64_t *desc; + idma_buf_t *buf; + idma_status_t ret; + DECLARE_PS(); + + IDMA_DISABLE_INTS(); + + buf = idma_chan_buf_get(ch); + if (buf != NULL) { + desc = cvt_desc_to_desc64(buf->next_desc); + desc->src = (cvt_voidp_to_uint32p(src))[0]; + + XLOG(ch, "Change src field for descriptor @ %p to: %p\n", desc, desc->src); + ret = IDMA_OK; + } + else { + ret = IDMA_ERR_NO_BUF; + } + + IDMA_ENABLE_INTS(); + return ret; +} + +IDMA_API idma_status_t +idma_update_desc64_size(int32_t ch, uint32_t size) +{ + idma_desc64_t *desc; + idma_buf_t *buf; + idma_status_t ret; + DECLARE_PS(); + + IDMA_DISABLE_INTS(); + + buf = idma_chan_buf_get(ch); + if (buf != NULL) { + desc = cvt_desc_to_desc64(buf->next_desc); + desc->size = size; + + XLOG(ch, "Change size field for descriptor @ %p to: %d\n", desc, desc->size); + ret = IDMA_OK; + } + else { + ret = IDMA_ERR_NO_BUF; + } + + IDMA_ENABLE_INTS(); + return ret; +} + +#if (IDMA_USE_WIDE_API > 0) + +IDMA_API idma_status_t +idma_update_desc64_dst_wide(int32_t ch, void *dst) // parasoft-suppress MISRA2012-RULE-8_13_a-4 "Cannot use const (backward compatibility)" +{ + idma_desc64_t *desc; + idma_buf_t *buf; + idma_status_t ret; + + DECLARE_PS(); + IDMA_DISABLE_INTS(); + + buf = idma_chan_buf_get(ch); + if (buf != NULL) { + desc = cvt_desc_to_desc64(buf->next_desc); + desc->dst = (cvt_voidp_to_uint32p(dst))[0]; + desc->ext_dst = (cvt_voidp_to_uint32p(dst))[1]; + XLOG(ch, "Change dst field for descriptor @ %p to: %p\n", desc, desc->dst); + ret = IDMA_OK; + } + else { + ret = IDMA_ERR_NO_BUF; + } + + IDMA_ENABLE_INTS(); + return ret; +} + +IDMA_API idma_status_t +idma_update_desc64_src_wide(int32_t ch, void *src) // parasoft-suppress MISRA2012-RULE-8_13_a-4 "Cannot use const (backward compatibility)" +{ + idma_desc64_t *desc; + idma_buf_t *buf; + idma_status_t ret; + + DECLARE_PS(); + + IDMA_DISABLE_INTS(); + + buf = idma_chan_buf_get(ch); + if (buf != NULL) { + desc = cvt_desc_to_desc64(buf->next_desc); + desc->src = (cvt_voidp_to_uint32p(src))[0]; + desc->ext_src = (cvt_voidp_to_uint32p(src))[1]; + XLOG(ch, "Change src field for descriptor @ %p to: %p\n", desc, desc->src); + ret = IDMA_OK; + } + else { + ret = IDMA_ERR_NO_BUF; + } + + IDMA_ENABLE_INTS(); + return ret; +} + +#endif +#endif + +IDMA_API idma_status_t +idma_init_task (IDMA_CHAN_FUNC_ARG + idma_buffer_t *taskh, + idma_type_t type, + int32_t ndescs, + idma_callback_fn cb_func, + void *cb_data) +{ + return idma_init_task_i(IDMA_CH_PTR, taskh, type, ndescs, cb_func, cb_data); +} + +ALWAYS_INLINE idma_status_t +idma_copy_task(IDMA_CHAN_FUNC_ARG + idma_buffer_t *taskh, + void *dst, + void *src, + size_t size, + uint32_t flags, + void *cb_data, + idma_callback_fn cb_func) +{ +#ifdef IDMA_USE_WIDE_ADDRESS_COMPILE + return idma_copy_task_wide_i(IDMA_CH_PTR, taskh, dst, src, size, flags, cb_data, cb_func); +#else + return idma_copy_task_i(IDMA_CH_PTR, taskh, dst, src, size, flags, cb_data, cb_func); +#endif +} + +ALWAYS_INLINE idma_status_t +idma_copy_2d_task(IDMA_CHAN_FUNC_ARG + idma_buffer_t *taskh, + void *dst, + void *src, + size_t row_sz, + uint32_t flags, + uint32_t nrows, + uint32_t src_pitch, + uint32_t dst_pitch, + void *cb_data, + idma_callback_fn cb_func) +{ +#ifdef IDMA_USE_WIDE_ADDRESS_COMPILE + return idma_copy_2d_task_wide_i(IDMA_CH_PTR, taskh, dst, src, row_sz, flags, nrows, src_pitch, dst_pitch, cb_data, cb_func); +#else + return idma_copy_2d_task_i(IDMA_CH_PTR, taskh, dst, src, row_sz, flags, nrows, src_pitch, dst_pitch, cb_data, cb_func); +#endif +} + +#if (IDMA_USE_64B_DESC > 0) + +#if (XCHAL_IDMA_HAVE_2DPRED > 0) +IDMA_API idma_status_t +idma_copy_2d_pred_task64(int32_t ch, + idma_buffer_t *taskh, + void *dst, + void *src, + size_t row_sz, + uint32_t flags, + void* pred_mask, + uint32_t nrows, + uint32_t src_pitch, + uint32_t dst_pitch, + void *cb_data, + idma_callback_fn cb_func) +{ + return idma_copy_2d_pred_task64_i(ch, taskh, dst, src, row_sz, flags, pred_mask, nrows, src_pitch, dst_pitch, cb_data, cb_func); +} +#endif + +IDMA_API idma_status_t +idma_copy_task64(int32_t ch, + idma_buffer_t *taskh, + void *dst, + void *src, + size_t size, + uint32_t flags, + void *cb_data, + idma_callback_fn cb_func) +{ + return idma_copy_task64_i(ch, taskh, dst, src, size, flags, cb_data, cb_func); +} + +IDMA_API idma_status_t +idma_copy_2d_task64(int32_t ch, + idma_buffer_t *taskh, + void *dst, + void *src, + size_t row_sz, + uint32_t flags, + uint32_t nrows, + uint32_t src_pitch, + uint32_t dst_pitch, + void *cb_data, + idma_callback_fn cb_func) +{ + return idma_copy_2d_task64_i(ch, taskh, dst, src, row_sz, flags, nrows, src_pitch, dst_pitch, cb_data, cb_func); +} + +IDMA_API idma_status_t +idma_copy_3d_task64(int32_t ch, + idma_buffer_t *taskh, + void *dst, + void *src, + uint32_t flags, + size_t row_sz, + uint32_t nrows, + uint32_t ntiles, + uint32_t src_pitch, + uint32_t dst_pitch, + uint32_t src_tile_pitch, + uint32_t dst_tile_pitch, + void *cb_data, + idma_callback_fn cb_func) +{ + return idma_copy_3d_task64_i(ch, taskh, dst, src, flags, row_sz, nrows, ntiles, src_pitch, dst_pitch, src_tile_pitch, dst_tile_pitch, cb_data, cb_func); +} + +IDMA_API idma_status_t +idma_add_desc64( idma_buffer_t *bufh, + void *dst, + void *src, + size_t size, + uint32_t flags) +{ + idma_buf_t *buf; + DECLARE_PS(); + IDMA_DISABLE_INTS(); + buf = convert_buffer_to_buf(bufh); + + set_desc64_ctrl(buf->next_add_desc, flags, IDMA_64B_DESC_CODE | SET_CONTROL_SUBTYPE(IDMA_64B_1D_TYPE)); + set_1d_desc64_fields(buf->ch, buf->next_add_desc, dst, src, size); + update_next_add_ptr(buf); + + IDMA_ENABLE_INTS(); + return IDMA_OK; +} + +static inline idma_status_t +idma_add_2d_desc64( idma_buffer_t *bufh, + void *dst, + void *src, + size_t row_sz, + uint32_t flags, + uint32_t nrows, + uint32_t src_pitch, + uint32_t dst_pitch) +{ + idma_buf_t *buf; + DECLARE_PS(); + + IDMA_DISABLE_INTS(); + + buf = convert_buffer_to_buf(bufh); + + set_desc64_ctrl(buf->next_add_desc, flags, IDMA_64B_DESC_CODE | SET_CONTROL_SUBTYPE(IDMA_64B_2D_TYPE)); + set_2d_desc64_fields(buf->ch, buf->next_add_desc, dst, src, row_sz, nrows, src_pitch, dst_pitch); + + update_next_add_ptr(buf); + + IDMA_ENABLE_INTS(); + return IDMA_OK; +} + +#if (XCHAL_IDMA_HAVE_2DPRED > 0) +static inline idma_status_t +idma_add_2d_pred_desc64( idma_buffer_t *bufh, + void *dst, + void *src, + size_t row_sz, + uint32_t flags, + void* pred_mask, + uint32_t nrows, + uint32_t src_pitch, + uint32_t dst_pitch) +{ + idma_buf_t *buf; + DECLARE_PS(); + + IDMA_DISABLE_INTS(); + + buf = convert_buffer_to_buf(bufh); + + set_desc64_ctrl(buf->next_add_desc, flags, IDMA_64B_DESC_CODE | SET_CONTROL_SUBTYPE(IDMA_64B_2D_COMPRESS_TYPE)); + set_2d_desc64_fields(buf->ch, buf->next_add_desc, dst, src, row_sz, nrows, src_pitch, dst_pitch); + (cvt_desc_to_desc64(buf->next_add_desc))->pred_mask = cvt_voidp_to_uint32(pred_mask); + + update_next_add_ptr(buf); + + IDMA_ENABLE_INTS(); + return IDMA_OK; +} +#endif + +IDMA_API idma_status_t +idma_add_3d_desc64( idma_buffer_t *bufh, + void *dst, + void *src, + uint32_t flags, + size_t row_sz, + uint32_t nrows, + uint32_t ntiles, + uint32_t src_row_pitch, + uint32_t dst_row_pitch, + uint32_t src_tile_pitch, + uint32_t dst_tile_pitch) +{ + idma_buf_t *buf; + DECLARE_PS(); + + IDMA_DISABLE_INTS(); + + buf = convert_buffer_to_buf(bufh); + set_desc64_ctrl(buf->next_add_desc, flags, IDMA_64B_DESC_CODE | SET_CONTROL_SUBTYPE(IDMA_64B_3D_TYPE)); + set_3d_desc64_fields(buf->ch, buf->next_add_desc, dst, src, row_sz, nrows, ntiles, src_row_pitch, dst_row_pitch, src_tile_pitch, dst_tile_pitch); + + update_next_add_ptr(buf); + + IDMA_ENABLE_INTS(); + return IDMA_OK; +} + +IDMA_API int32_t +idma_copy_desc64(int32_t ch, void *dst, void *src, size_t size, uint32_t flags) +{ + int32_t ret; + idma_buf_t* buf; + DECLARE_PS(); + + IDMA_DISABLE_INTS(); + + buf = idma_chan_buf_get(ch); + if (buf != NULL) { + set_desc64_ctrl(buf->next_desc, flags, IDMA_64B_DESC_CODE | SET_CONTROL_SUBTYPE(IDMA_64B_1D_TYPE)); + set_1d_desc64_fields(ch, buf->next_desc, dst, src, size); + ret = schedule_desc(ch, 1U); + } + else { + ret = (int32_t) IDMA_ERR_NO_BUF; + } + + IDMA_ENABLE_INTS(); + return ret; +} + +IDMA_API int32_t +idma_copy_2d_desc64(int32_t ch, + void *dst, + void *src, + size_t size, + uint32_t flags, + uint32_t nrows, + uint32_t src_pitch, + uint32_t dst_pitch) +{ + idma_buf_t* buf; + DECLARE_PS(); + int32_t ret; + + IDMA_DISABLE_INTS(); + + buf = idma_chan_buf_get(ch); + if (buf != NULL) { + set_desc64_ctrl(buf->next_desc, flags, IDMA_64B_DESC_CODE | SET_CONTROL_SUBTYPE(IDMA_64B_2D_TYPE)); + set_2d_desc64_fields(ch, buf->next_desc, dst, src, size, nrows, src_pitch, dst_pitch); + + ret = schedule_desc(ch, 1U); + } + else { + ret = (int32_t) IDMA_ERR_NO_BUF; + } + + IDMA_ENABLE_INTS(); + return ret; +} + +#if (XCHAL_IDMA_HAVE_2DPRED > 0) +IDMA_API int32_t +idma_copy_2d_pred_desc64(int32_t ch, void *dst, void *src, size_t size, uint32_t flags, void* pred_mask, uint32_t nrows, uint32_t src_pitch, uint32_t dst_pitch) +{ + idma_buf_t* buf; + DECLARE_PS(); + int32_t ret; + + IDMA_DISABLE_INTS(); + + buf = idma_chan_buf_get(ch); + if (buf != NULL) { + set_desc64_ctrl(buf->next_desc, flags, IDMA_64B_DESC_CODE | SET_CONTROL_SUBTYPE(IDMA_64B_2D_COMPRESS_TYPE)); + set_2d_desc64_fields(ch, buf->next_desc, dst, src, size, nrows, src_pitch, dst_pitch); + (cvt_desc_to_desc64(buf->next_desc))->pred_mask = cvt_voidp_to_uint32(pred_mask); + + ret = schedule_desc(ch, 1U); + } + else { + ret = (int32_t) IDMA_ERR_NO_BUF; + } + + IDMA_ENABLE_INTS(); + return ret; +} +#endif + + +IDMA_API int32_t +idma_copy_3d_desc64(int32_t ch, + void *dst, + void *src, + uint32_t flags, + size_t row_sz, + uint32_t nrows, + uint32_t ntiles, + uint32_t src_row_pitch, + uint32_t dst_row_pitch, + uint32_t src_tile_pitch, + uint32_t dst_tile_pitch) +{ + idma_buf_t* buf; + DECLARE_PS(); + int32_t ret; + + IDMA_DISABLE_INTS(); + + buf = idma_chan_buf_get(ch); + if (buf != NULL) { + set_desc64_ctrl(buf->next_desc, flags, IDMA_64B_DESC_CODE | SET_CONTROL_SUBTYPE(IDMA_64B_3D_TYPE)); + set_3d_desc64_fields(ch, buf->next_desc, dst, src, row_sz, nrows, ntiles, src_row_pitch, dst_row_pitch, src_tile_pitch, dst_tile_pitch); + + ret = schedule_desc(ch, 1U); + } + else { + ret = (int32_t) IDMA_ERR_NO_BUF; + } + + IDMA_ENABLE_INTS(); + return ret; +} + +#if (IDMA_USE_WIDE_API > 0) + +IDMA_API idma_status_t +idma_copy_task64_wide(int32_t ch, + idma_buffer_t *taskh, + void *dst, + void *src, + size_t size, + uint32_t flags, + void *cb_data, + idma_callback_fn cb_func) +{ + return idma_copy_task64_wide_i(ch, taskh, dst, src, size, flags, cb_data, cb_func); +} + +IDMA_API idma_status_t +idma_copy_2d_task64_wide(int32_t ch, + idma_buffer_t *taskh, + void *dst, + void *src, + size_t row_sz, + uint32_t flags, + uint32_t nrows, + uint32_t src_pitch, + uint32_t dst_pitch, + void *cb_data, + idma_callback_fn cb_func) +{ + return idma_copy_2d_task64_wide_i(ch, taskh, dst, src, row_sz, flags, nrows, src_pitch, dst_pitch, cb_data, cb_func); +} + +IDMA_API idma_status_t +idma_copy_3d_task64_wide(int32_t ch, + idma_buffer_t *taskh, + void *dst, + void *src, + uint32_t flags, + size_t row_sz, + uint32_t nrows, + uint32_t ntiles, + uint32_t src_pitch, + uint32_t dst_pitch, + uint32_t src_tile_pitch, + uint32_t dst_tile_pitch, + void *cb_data, + idma_callback_fn cb_func) +{ + return idma_copy_3d_task64_wide_i(ch, taskh, dst, src, flags, row_sz, nrows, ntiles, src_pitch, dst_pitch, src_tile_pitch, dst_tile_pitch, cb_data, cb_func); +} + +IDMA_API idma_status_t +idma_add_desc64_wide( idma_buffer_t *bufh, + void *dst, + void *src, + size_t size, + uint32_t flags) +{ + idma_buf_t *buf; + DECLARE_PS(); + IDMA_DISABLE_INTS(); + buf = convert_buffer_to_buf(bufh); + + set_desc64_ctrl(buf->next_add_desc, flags, IDMA_64B_DESC_CODE | IDMA_64B_LONG_ADDR_CTRL_BIT_MASK | SET_CONTROL_SUBTYPE(IDMA_64B_1D_TYPE)); + set_1d_desc64_fields_wide(buf->ch, buf->next_add_desc, dst, src, size); + update_next_add_ptr(buf); + + IDMA_ENABLE_INTS(); + return IDMA_OK; +} + +IDMA_API int32_t +idma_copy_desc64_wide(int32_t ch, void *dst, void *src, size_t size, uint32_t flags) +{ + int32_t ret; + idma_buf_t* buf; + DECLARE_PS(); + + IDMA_DISABLE_INTS(); + + buf = idma_chan_buf_get(ch); + if (buf != NULL) { + set_desc64_ctrl(buf->next_desc, flags, IDMA_64B_DESC_CODE | IDMA_64B_LONG_ADDR_CTRL_BIT_MASK | SET_CONTROL_SUBTYPE(IDMA_64B_1D_TYPE)); + set_1d_desc64_fields_wide(ch, buf->next_desc, dst, src, size); + ret = schedule_desc(ch, 1U); + } + else { + ret = (int32_t) IDMA_ERR_NO_BUF; + } + + IDMA_ENABLE_INTS(); + return ret; +} + +#if (XCHAL_IDMA_HAVE_2DPRED > 0) +IDMA_API idma_status_t +idma_copy_2d_pred_task64_wide(int32_t ch, + idma_buffer_t *taskh, + void *dst, + void *src, + size_t row_sz, + uint32_t flags, + void* pred_mask, + uint32_t nrows, + uint32_t src_pitch, + uint32_t dst_pitch, + void *cb_data, + idma_callback_fn cb_func) +{ + return idma_copy_2d_pred_task64_wide_i(ch, taskh, dst, src, row_sz, flags, pred_mask, nrows, src_pitch, dst_pitch, cb_data, cb_func); +} + + +static inline idma_status_t +idma_add_2d_pred_desc64_wide( idma_buffer_t *bufh, void *dst, void *src, size_t row_sz, uint32_t flags, void* pred_mask, uint32_t nrows, uint32_t src_pitch, uint32_t dst_pitch) +{ + idma_buf_t *buf; + DECLARE_PS(); + + IDMA_DISABLE_INTS(); + + buf = convert_buffer_to_buf(bufh); + + set_desc64_ctrl(buf->next_add_desc, flags, IDMA_64B_DESC_CODE | IDMA_64B_LONG_ADDR_CTRL_BIT_MASK | SET_CONTROL_SUBTYPE(IDMA_64B_2D_COMPRESS_TYPE)); + set_2d_desc64_fields_wide(buf->ch, buf->next_add_desc, dst, src, row_sz, nrows, src_pitch, dst_pitch); + (cvt_desc_to_desc64(buf->next_add_desc))->pred_mask = cvt_voidp_to_uint32(pred_mask); + + update_next_add_ptr(buf); + + IDMA_ENABLE_INTS(); + return IDMA_OK; +} +#endif + +static inline idma_status_t +idma_add_2d_desc64_wide( idma_buffer_t *bufh, void *dst, void *src, size_t row_sz, uint32_t flags, uint32_t nrows, uint32_t src_pitch, uint32_t dst_pitch) +{ + idma_buf_t *buf; + DECLARE_PS(); + + IDMA_DISABLE_INTS(); + buf = convert_buffer_to_buf(bufh); + + set_desc64_ctrl(buf->next_add_desc, flags, IDMA_64B_DESC_CODE | IDMA_64B_LONG_ADDR_CTRL_BIT_MASK | SET_CONTROL_SUBTYPE(IDMA_64B_2D_TYPE)); + set_2d_desc64_fields_wide(buf->ch, buf->next_add_desc, dst, src, row_sz, nrows, src_pitch, dst_pitch); + update_next_add_ptr(buf); + + IDMA_ENABLE_INTS(); + return IDMA_OK; +} + +IDMA_API idma_status_t +idma_add_3d_desc64_wide( idma_buffer_t *bufh, + void *dst, + void *src, + uint32_t flags, + size_t row_sz, + uint32_t nrows, + uint32_t ntiles, + uint32_t src_row_pitch, + uint32_t dst_row_pitch, + uint32_t src_tile_pitch, + uint32_t dst_tile_pitch) +{ + idma_buf_t *buf; + DECLARE_PS(); + + IDMA_DISABLE_INTS(); + + buf = convert_buffer_to_buf(bufh); + set_desc64_ctrl(buf->next_add_desc, flags, IDMA_64B_DESC_CODE | IDMA_64B_LONG_ADDR_CTRL_BIT_MASK | SET_CONTROL_SUBTYPE(IDMA_64B_3D_TYPE)); + set_3d_desc64_fields_wide(buf->ch, buf->next_add_desc, dst, src, row_sz, nrows, ntiles, src_row_pitch, dst_row_pitch, src_tile_pitch, dst_tile_pitch); + update_next_add_ptr(buf); + + IDMA_ENABLE_INTS(); + return IDMA_OK; +} + +IDMA_API int32_t +idma_copy_2d_desc64_wide(int32_t ch, void *dst, void *src, size_t size, uint32_t flags, uint32_t nrows, uint32_t src_pitch, uint32_t dst_pitch) +{ + idma_buf_t* buf; + DECLARE_PS(); + int32_t ret = 0; + + IDMA_DISABLE_INTS(); + + buf = idma_chan_buf_get(ch); + if (buf != NULL) { + set_desc64_ctrl(buf->next_desc, flags, IDMA_64B_DESC_CODE | IDMA_64B_LONG_ADDR_CTRL_BIT_MASK | SET_CONTROL_SUBTYPE(IDMA_64B_2D_TYPE)); + set_2d_desc64_fields_wide(ch, buf->next_desc, dst, src, size, nrows, src_pitch, dst_pitch); + ret = schedule_desc(ch, 1U); + } + else { + ret = (int32_t) IDMA_ERR_NO_BUF; + } + + IDMA_ENABLE_INTS(); + return ret; +} + +#if (XCHAL_IDMA_HAVE_2DPRED > 0) +IDMA_API int32_t +idma_copy_2d_pred_desc64_wide(int32_t ch, void *dst, void *src, size_t size, uint32_t flags, void* pred_mask, uint32_t nrows, uint32_t src_pitch, uint32_t dst_pitch) +{ + idma_buf_t* buf; + DECLARE_PS(); + int32_t ret = 0; + + IDMA_DISABLE_INTS(); + + buf = idma_chan_buf_get(ch); + if (buf != NULL) { + set_desc64_ctrl(buf->next_desc, flags, IDMA_64B_DESC_CODE | IDMA_64B_LONG_ADDR_CTRL_BIT_MASK | SET_CONTROL_SUBTYPE(IDMA_64B_2D_COMPRESS_TYPE)); + set_2d_desc64_fields_wide(ch, buf->next_desc, dst, src, size, nrows, src_pitch, dst_pitch); + (cvt_desc_to_desc64(buf->next_desc))->pred_mask = cvt_voidp_to_uint32(pred_mask); + ret = schedule_desc(ch, 1U); + } + else { + ret = (int32_t) IDMA_ERR_NO_BUF; + } + + IDMA_ENABLE_INTS(); + return ret; +} +#endif + +IDMA_API int32_t +idma_copy_3d_desc64_wide(int32_t ch, + void *dst, + void *src, + uint32_t flags, + size_t row_sz, + uint32_t nrows, + uint32_t ntiles, + uint32_t src_row_pitch, + uint32_t dst_row_pitch, + uint32_t src_tile_pitch, + uint32_t dst_tile_pitch) +{ + idma_buf_t* buf; + DECLARE_PS(); + int32_t ret = 0; + + IDMA_DISABLE_INTS(); + + buf = idma_chan_buf_get(ch); + if (buf != NULL) { + set_desc64_ctrl(buf->next_desc, flags, IDMA_64B_DESC_CODE | IDMA_64B_LONG_ADDR_CTRL_BIT_MASK | SET_CONTROL_SUBTYPE(IDMA_64B_3D_TYPE)); + set_3d_desc64_fields_wide(ch, buf->next_desc, dst, src, row_sz, nrows, ntiles, src_row_pitch, dst_row_pitch, src_tile_pitch, dst_tile_pitch); + ret = schedule_desc(ch, 1U); + } + else { + ret = (int32_t) IDMA_ERR_NO_BUF; + } + + IDMA_ENABLE_INTS(); + return ret; +} +#endif +#endif //64B + + +IDMA_API idma_status_t +idma_process_tasks(IDMA_CHAN_FUNC_ARG_SINGLE) +{ + return idma_process_tasks_i(IDMA_CH_PTR); +} + +IDMA_API idma_error_details_t* +idma_error_details(IDMA_CHAN_FUNC_ARG_SINGLE) +{ + return idma_error_details_i(IDMA_CH_PTR); +} + +IDMA_API idma_error_details_t* +idma_buffer_error_details(IDMA_CHAN_FUNC_ARG_SINGLE) +{ + return idma_error_details_i(IDMA_CH_PTR); +} + +IDMA_API idma_hw_error_t +idma_buffer_check_errors(IDMA_CHAN_FUNC_ARG_SINGLE) +{ + return idma_buffer_check_errors_i(IDMA_CH_PTR); +} + +IDMA_API idma_status_t +idma_abort_tasks(IDMA_CHAN_FUNC_ARG_SINGLE) +{ + return idma_abort_tasks_i(IDMA_CH_PTR); +} + + +IDMA_API int32_t +idma_task_status(idma_buffer_t *taskh) +{ + idma_buf_t* task; + task = convert_buffer_to_buf(taskh); + + XLOG(task->ch, "Task %p status '%s' (%d)\n", task, + (task->status == (int32_t)IDMA_TASK_ERROR) ? "Error" : + (task->status == (int32_t)IDMA_TASK_DONE) ? "Done" : + (task->status > (int32_t)IDMA_TASK_DONE) ? "Pending" : "UNKNOWN" , task->status); + + return (task->status); +} + + +IDMA_API int32_t +idma_schedule_desc_clock(IDMA_CHAN_FUNC_ARG + uint32_t count, + uint32_t *ptime) +{ + int32_t ret; + DECLARE_PS(); + + IDMA_DISABLE_INTS(); + ret = schedule_desc(IDMA_CH_PTR, count); + *ptime = (uint32_t) clock(); // parasoft-suppress MISRA2012-RULE-21_10-2 "This function is used for profiling only." + IDMA_ENABLE_INTS(); + return ret; +} + +IDMA_API int32_t +idma_schedule_desc_fast_clock(IDMA_CHAN_FUNC_ARG + uint32_t count, + uint32_t *ptime) +{ + int32_t ret; + DECLARE_PS(); + + IDMA_DISABLE_INTS(); + ret = schedule_desc_fast(IDMA_CH_PTR, count); + *ptime = (uint32_t) clock(); // parasoft-suppress MISRA2012-RULE-21_10-2 "This function is used for profiling only." + IDMA_ENABLE_INTS(); + return ret; +} + + +#if (XCHAL_IDMA_HAVE_FBC > 0) + +typedef enum { + IDMA_FBC_SF_1_1 = 0, + IDMA_FBC_SF_1_2 = 1, + IDMA_FBC_SF_3_8 = 2, +} idma_fbc_sf_t; + +typedef enum { + IDMA_FBC_CTW_32B = 0, + IDMA_FBC_CTW_64B = 1, + IDMA_FBC_CTW_128B = 2, + IDMA_FBC_CTW_256B = 3, + IDMA_FBC_CTW_512B = 4, +} idma_fbc_ctw_t; + +typedef enum { + IDMA_FBC_CTH_2 = 0, + IDMA_FBC_CTH_4 = 1, + IDMA_FBC_CTH_8 = 2, + IDMA_FBC_CTH_16 = 3, + IDMA_FBC_CTH_32 = 4, +} idma_fbc_cth_t; + + +idma_status_t +idma_copy_fbc_task64_i(int32_t ch, + idma_buffer_t *taskh, + void *dst, + void *src, + void *baseaddr, + uint32_t flags, + size_t row_sz, + uint32_t nrows, + uint32_t src_pitch, + uint32_t dst_pitch, + idma_fbc_sf_t sf, + idma_fbc_ctw_t ctw, + idma_fbc_cth_t cth, + uint16_t custom, + void *cb_data, + idma_callback_fn cb_func); + +idma_status_t +idma_copy_fbc_task64_wide_i(int32_t ch, + idma_buffer_t *taskh, + void *dst, + void *src, + void *baseaddr, + uint32_t flags, + size_t row_sz, + uint32_t nrows, + uint32_t src_pitch, + uint32_t dst_pitch, + idma_fbc_sf_t sf, + idma_fbc_ctw_t ctw, + idma_fbc_cth_t cth, + uint16_t custom, + void *cb_data, + idma_callback_fn cb_func); + + +INTERNAL_FUNC void +set_fbc_desc64_fields(idma_desc_t *desch, + uint32_t baseaddr_lo, + uint32_t baseaddr_hi, + idma_fbc_sf_t sf, + idma_fbc_ctw_t ctw, + idma_fbc_cth_t cth, + uint16_t custom) +{ + idma_desc64_t *desc = cvt_desc_to_desc64(desch); + + desc->word13 = baseaddr_lo; + desc->word14 = ((uint32_t)(custom >> 4) << 20) | + ((uint32_t)sf << 18) | + ((uint32_t)ctw << 15) | + ((uint32_t)cth << 12) | + ((custom & 0xF) << 8) | + (baseaddr_hi & 0xFF); +} + + +IDMA_API idma_status_t +idma_add_fbc_desc64( idma_buffer_t *bufh, + void *dst, + void *src, + void *baseaddr, + uint32_t flags, + size_t row_sz, + uint32_t nrows, + uint32_t src_pitch, + uint32_t dst_pitch, + idma_fbc_sf_t sf, + idma_fbc_ctw_t ctw, + idma_fbc_cth_t cth, + uint16_t custom) +{ + idma_buf_t *buf; + DECLARE_PS(); + + IDMA_DISABLE_INTS(); + + buf = convert_buffer_to_buf(bufh); + + set_desc64_ctrl(buf->next_add_desc, + flags, + IDMA_64B_DESC_CODE | SET_CONTROL_SUBTYPE(IDMA_64B_2D_FBC_TYPE)); + + set_2d_desc64_fields(buf->ch, + buf->next_add_desc, + dst, + src, + row_sz, + nrows, + src_pitch, + dst_pitch); + + set_fbc_desc64_fields(buf->next_add_desc, + cvt_voidp_to_uint32(baseaddr), + 0U, + sf, + ctw, + cth, + custom); + + update_next_add_ptr(buf); + IDMA_ENABLE_INTS(); + + return IDMA_OK; +} + +IDMA_API int32_t +idma_copy_fbc_desc64(int32_t ch, + void *dst, + void *src, + void *baseaddr, + uint32_t flags, + size_t row_sz, + uint32_t nrows, + uint32_t src_pitch, + uint32_t dst_pitch, + idma_fbc_sf_t sf, + idma_fbc_ctw_t ctw, + idma_fbc_cth_t cth, + uint16_t custom) +{ + idma_buf_t *buf; + int32_t ret; + DECLARE_PS(); + + IDMA_DISABLE_INTS(); + + buf = idma_chan_buf_get(ch); + if (buf != NULL) { + set_desc64_ctrl(buf->next_desc, + flags, + IDMA_64B_DESC_CODE | SET_CONTROL_SUBTYPE(IDMA_64B_2D_FBC_TYPE)); + + set_2d_desc64_fields(ch, + buf->next_desc, + dst, + src, + row_sz, + nrows, + src_pitch, + dst_pitch); + + set_fbc_desc64_fields(buf->next_desc, + cvt_voidp_to_uint32(baseaddr), + 0U, + sf, + ctw, + cth, + custom); + + ret = schedule_desc(ch, 1U); + } + else { + ret = (int32_t) IDMA_ERR_NO_BUF; + } + + IDMA_ENABLE_INTS(); + return ret; +} + +IDMA_API idma_status_t +idma_copy_fbc_task64(int32_t ch, + idma_buffer_t *taskh, + void *dst, + void *src, + void *baseaddr, + uint32_t flags, + size_t row_sz, + uint32_t nrows, + uint32_t src_pitch, + uint32_t dst_pitch, + idma_fbc_sf_t sf, + idma_fbc_ctw_t ctw, + idma_fbc_cth_t cth, + uint16_t custom, + void *cb_data, + idma_callback_fn cb_func) +{ + return idma_copy_fbc_task64_i(ch, taskh, dst, src, baseaddr, flags, + row_sz, nrows, src_pitch, dst_pitch, sf, ctw, cth, + custom, cb_data, cb_func); +} + +#if (IDMA_USE_WIDE_API > 0) + +IDMA_API idma_status_t +idma_add_fbc_desc64_wide(idma_buffer_t *bufh, + void *dst, + void *src, + void *baseaddr, + uint32_t flags, + size_t row_sz, + uint32_t nrows, + uint32_t src_pitch, + uint32_t dst_pitch, + idma_fbc_sf_t sf, + idma_fbc_ctw_t ctw, + idma_fbc_cth_t cth, + uint16_t custom) +{ + idma_buf_t *buf; + DECLARE_PS(); + + IDMA_DISABLE_INTS(); + + buf = convert_buffer_to_buf(bufh); + + set_desc64_ctrl(buf->next_add_desc, + flags, + IDMA_64B_DESC_CODE | IDMA_64B_LONG_ADDR_CTRL_BIT_MASK | + SET_CONTROL_SUBTYPE(IDMA_64B_2D_FBC_TYPE)); + + set_2d_desc64_fields_wide(buf->ch, + buf->next_add_desc, + dst, + src, + row_sz, + nrows, + src_pitch, + dst_pitch); + + set_fbc_desc64_fields(buf->next_add_desc, + (cvt_voidp_to_uint32p(baseaddr))[0], + (cvt_voidp_to_uint32p(baseaddr))[1], + sf, + ctw, + cth, + custom); + + update_next_add_ptr(buf); + IDMA_ENABLE_INTS(); + + return IDMA_OK; +} + +IDMA_API int32_t +idma_copy_fbc_desc64_wide(int32_t ch, + void *dst, + void *src, + void *baseaddr, + uint32_t flags, + size_t row_sz, + uint32_t nrows, + uint32_t src_pitch, + uint32_t dst_pitch, + idma_fbc_sf_t sf, + idma_fbc_ctw_t ctw, + idma_fbc_cth_t cth, + uint16_t custom) +{ + idma_buf_t* buf; + int32_t ret; + DECLARE_PS(); + + IDMA_DISABLE_INTS(); + + buf = idma_chan_buf_get(ch); + if (buf != NULL) { + set_desc64_ctrl(buf->next_desc, + flags, + IDMA_64B_DESC_CODE | IDMA_64B_LONG_ADDR_CTRL_BIT_MASK | + SET_CONTROL_SUBTYPE(IDMA_64B_2D_FBC_TYPE)); + + set_2d_desc64_fields_wide(ch, + buf->next_desc, + dst, + src, + row_sz, + nrows, + src_pitch, + dst_pitch); + + set_fbc_desc64_fields(buf->next_desc, + (cvt_voidp_to_uint32p(baseaddr))[0], + (cvt_voidp_to_uint32p(baseaddr))[1], + sf, + ctw, + cth, + custom); + + ret = schedule_desc(ch, 1U); + } + else { + ret = (int32_t) IDMA_ERR_NO_BUF; + } + + IDMA_ENABLE_INTS(); + return ret; +} + +IDMA_API idma_status_t +idma_copy_fbc_task64_wide(int32_t ch, + idma_buffer_t *taskh, + void *dst, + void *src, + void *baseaddr, + uint32_t flags, + size_t row_sz, + uint32_t nrows, + uint32_t src_pitch, + uint32_t dst_pitch, + idma_fbc_sf_t sf, + idma_fbc_ctw_t ctw, + idma_fbc_cth_t cth, + uint16_t custom, + void *cb_data, + idma_callback_fn cb_func) +{ + return idma_copy_fbc_task64_wide_i(ch, taskh, dst, src, baseaddr, flags, + row_sz, nrows, src_pitch, dst_pitch, + sf, ctw, cth, custom, cb_data, cb_func); +} + +#endif // IDMA_USE_WIDE_API + +#endif // XCHAL_IDMA_HAVE_FBC + + +#if (XCHAL_IDMA_HAVE_ZVC > 0) + +idma_status_t +idma_copy_zvc_task_i(int32_t ch, + idma_buffer_t *taskh, + void *dst, + void *src, + size_t size, + void *table_addr, + uint32_t flags, + void *cb_data, + idma_callback_fn cb_func); + +idma_status_t +idma_copy_zvc_task64_wide_i(int32_t ch, + idma_buffer_t *taskh, + void *dst, + void *src, + size_t size, + void *table_addr, + uint32_t flags, + void *cb_data, + idma_callback_fn cb_func); + +INTERNAL_FUNC void +set_zvc_desc_fields(idma_desc_t *desch, + void *table_addr) +{ + idma_2d_desc_t *desc2d = (idma_2d_desc_t *) desch; + + desc2d->word8 = cvt_voidp_to_uint32(table_addr); +} + +IDMA_API idma_status_t +idma_add_zvc_desc(idma_buffer_t *bufh, + void *dst, + void *src, + size_t size, + void *table_addr, + uint32_t flags) +{ + idma_buf_t *buf; + DECLARE_PS(); + + IDMA_DISABLE_INTS(); + buf = convert_buffer_to_buf(bufh); + + set_desc_ctrl(buf->next_add_desc, + flags, + IDMA_ZVC_DESC_CODE | SET_CONTROL_SUBTYPE(IDMA_ZVC_1D_TYPE)); + set_2d_fields(buf->ch, buf->next_add_desc, dst, src, size, 0, 0, 0); + set_zvc_desc_fields(buf->next_add_desc, table_addr); + + update_next_add_ptr(buf); + IDMA_ENABLE_INTS(); + + return IDMA_OK; +} + +IDMA_API int32_t +idma_copy_zvc_desc(int32_t ch, + void *dst, + void *src, + size_t size, + void *table_addr, + uint32_t flags) +{ + idma_buf_t* buf; + int32_t ret; + DECLARE_PS(); + + IDMA_DISABLE_INTS(); + + buf = idma_chan_buf_get(ch); + if (buf != NULL) { + set_desc_ctrl(buf->next_desc, + flags, + IDMA_ZVC_DESC_CODE | SET_CONTROL_SUBTYPE(IDMA_ZVC_1D_TYPE)); + set_2d_fields(ch, buf->next_desc, dst, src, size, 0, 0, 0); + set_zvc_desc_fields(buf->next_desc, table_addr); + + ret = schedule_desc(ch, 1U); + } + else { + ret = IDMA_ERR_NO_BUF; + } + + IDMA_ENABLE_INTS(); + return ret; +} + +ALWAYS_INLINE idma_status_t +idma_copy_zvc_task(int32_t ch, + idma_buffer_t *taskh, + void *dst, + void *src, + size_t size, + void *table_addr, + uint32_t flags, + void *cb_data, + idma_callback_fn cb_func) +{ + return idma_copy_zvc_task_i(ch, taskh, dst, src, size, table_addr, + flags, cb_data, cb_func); +} + + +#if (IDMA_USE_WIDE_API > 0) + +IDMA_API idma_status_t +idma_add_zvc_desc64_wide(idma_buffer_t *bufh, + void *dst, + void *src, + size_t size, + void *table_addr, + uint32_t flags) +{ + idma_buf_t *buf; + DECLARE_PS(); + + IDMA_DISABLE_INTS(); + buf = convert_buffer_to_buf(bufh); + + set_desc64_ctrl(buf->next_add_desc, + flags, + IDMA_64B_DESC_CODE | IDMA_64B_LONG_ADDR_CTRL_BIT_MASK | + SET_CONTROL_SUBTYPE(IDMA_ZVC_1D_TYPE)); + set_2d_desc64_fields_wide(buf->ch, buf->next_add_desc, dst, src, size, 0, 0, 0); + set_zvc_desc_fields(buf->next_add_desc, table_addr); + + update_next_add_ptr(buf); + IDMA_ENABLE_INTS(); + + return IDMA_OK; +} + +IDMA_API int32_t +idma_copy_zvc_desc64_wide(int32_t ch, + void *dst, + void *src, + size_t size, + void *table_addr, + uint32_t flags) +{ + idma_buf_t* buf; + int32_t ret; + DECLARE_PS(); + + IDMA_DISABLE_INTS(); + + buf = idma_chan_buf_get(ch); + if (buf != NULL) { + set_desc_ctrl(buf->next_desc, + flags, + IDMA_64B_DESC_CODE | IDMA_64B_LONG_ADDR_CTRL_BIT_MASK | + SET_CONTROL_SUBTYPE(IDMA_ZVC_1D_TYPE)); + set_2d_desc64_fields_wide(ch, buf->next_desc, dst, src, size, 0, 0, 0); + set_zvc_desc_fields(buf->next_desc, table_addr); + + ret = schedule_desc(ch, 1U); + } + else { + ret = IDMA_ERR_NO_BUF; + } + + IDMA_ENABLE_INTS(); + return ret; +} + +ALWAYS_INLINE idma_status_t +idma_copy_zvc_task64_wide(int32_t ch, + idma_buffer_t *taskh, + void *dst, + void *src, + size_t size, + void *table_addr, + uint32_t flags, + void *cb_data, + idma_callback_fn cb_func) +{ + return idma_copy_zvc_task64_wide_i(ch, taskh, dst, src, size, table_addr, + flags, cb_data, cb_func); +} + +#endif // IDMA_USE_WIDE_API + +#endif // XCHAL_IDMA_HAVE_ZVC + + +#ifdef __cplusplus +} +#endif + +#endif + +#endif /* __IDMA_H__ */ diff --git a/src/platform/amd/acp_7_0/include/arch/xtensa/tie/xt_externalregisters.h b/src/platform/amd/acp_7_0/include/arch/xtensa/tie/xt_externalregisters.h new file mode 100644 index 000000000000..0fbb7e17e800 --- /dev/null +++ b/src/platform/amd/acp_7_0/include/arch/xtensa/tie/xt_externalregisters.h @@ -0,0 +1,77 @@ +// Customer ID=18056; Build=0xa6a6b; Copyright (c) 2017-2019 Cadence Design Systems, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +/* Definitions for the xt_externalregisters TIE package */ + +/* Do not modify. This is automatically generated.*/ + +/* parasoft-begin-suppress ALL "This file not MISRA checked." */ + +#ifndef _XTENSA_xt_externalregisters_HEADER +#define _XTENSA_xt_externalregisters_HEADER + +#ifdef __XTENSA__ +#ifdef __XCC__ + +#ifndef _ASMLANGUAGE +#ifndef _NOCLANGUAGE +#ifndef __ASSEMBLER__ + +#include <xtensa/tie/xt_core.h> + +/* + * The following prototypes describe intrinsic functions + * corresponding to TIE instructions. Some TIE instructions + * may produce multiple results (designated as "out" operands + * in the iclass section) or may have operands used as both + * inputs and outputs (designated as "inout"). However, the C + * and C++ languages do not provide syntax that can express + * the in/out/inout constraints of TIE intrinsics. + * Nevertheless, the compiler understands these constraints + * and will check that the intrinsic functions are used + * correctly. To improve the readability of these prototypes, + * the "out" and "inout" parameters are marked accordingly + * with comments. + */ + +extern unsigned _TIE_xt_externalregisters_RER(unsigned ars /*in*/); +extern unsigned _TIE_xt_externalregisters_RSR_ERACCESS(void); +extern void _TIE_xt_externalregisters_WER(unsigned art /*in*/, unsigned ars /*in*/); +extern void _TIE_xt_externalregisters_WSR_ERACCESS(unsigned art /*in*/); +extern void _TIE_xt_externalregisters_XSR_ERACCESS(unsigned art /*inout*/); + +#endif /*__ASSEMBLER__*/ +#endif /*_NOCLANGUAGE*/ +#endif /*_ASMLANGUAGE*/ + +#define XT_RER _TIE_xt_externalregisters_RER +#define XT_RSR_ERACCESS _TIE_xt_externalregisters_RSR_ERACCESS +#define XT_WER _TIE_xt_externalregisters_WER +#define XT_WSR_ERACCESS _TIE_xt_externalregisters_WSR_ERACCESS +#define XT_XSR_ERACCESS _TIE_xt_externalregisters_XSR_ERACCESS + +#endif /* __XCC__ */ + +#endif /* __XTENSA__ */ + +#endif /* !_XTENSA_xt_externalregisters_HEADER */ + +/* parasoft-end-suppress ALL "This file not MISRA checked." */ diff --git a/src/platform/amd/acp_7_0/include/arch/xtensa/xtensa-types.h b/src/platform/amd/acp_7_0/include/arch/xtensa/xtensa-types.h new file mode 100644 index 000000000000..4a0d2cd90404 --- /dev/null +++ b/src/platform/amd/acp_7_0/include/arch/xtensa/xtensa-types.h @@ -0,0 +1,65 @@ +/* + * xtensa-types.h -- General type definitions and macros. + */ + +/* + * Copyright (c) 2002-2018 Cadence Design Systems, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef XTENSA_TYPES_H +#define XTENSA_TYPES_H + +/* parasoft-begin-suppress MISRA2012-RULE-20_7 "Cannot parenthesize macro args here" */ +#if defined (_ASMLANGUAGE) || defined (__ASSEMBLER__) + +/* Redefine stdint.h macros for assembler. */ +/* parasoft-begin-suppress MISRA2012-RULE-21_1_c "It is necessary to redefine these to enable assembly code to compile." */ +#define INT8_C(x) x +#define UINT8_C(x) x +#define INT16_C(x) x +#define UINT16_C(x) x +#define INT32_C(x) x +#define UINT32_C(x) x +#define INT64_C(x) x +#define UINT64_C(x) x +/* parasoft-end-suppress MISRA2012-RULE-21_1_c "It is necessary to redefine these to enable assembly code to compile." */ + +#else + +#include <stdint.h> + +/* Adapt inline spec for older and newer versions of C standard. */ +#if (__STDC_VERSION__ >= 199901L) || defined (__cplusplus) +#define XT_INLINE __attribute__((always_inline)) static inline +#else +#define XT_INLINE __attribute__((always_inline)) static __inline__ +#endif + +/* This macro is used to mark unused function parameters and suppress warnings + from compiler / MISRA checker. + */ +#define UNUSED(x) ((void)(x)) + +#endif +/* parasoft-end-suppress MISRA2012-RULE-20_7 "Cannot parenthesize macro args here" */ + +#endif // XTENSA_TYPES_H diff --git a/src/platform/amd/acp_7_0/include/platform/chip_offset_byte.h b/src/platform/amd/acp_7_0/include/platform/chip_offset_byte.h deleted file mode 100644 index 7139cdb7e65d..000000000000 --- a/src/platform/amd/acp_7_0/include/platform/chip_offset_byte.h +++ /dev/null @@ -1,270 +0,0 @@ -/* SPDX-License-Identifier: BSD-3-Clause - * - * Copyright(c) 2024 AMD.All rights reserved. - * - * Author: SaiSurya, Ch <saisurya.chakkaveeravenkatanaga@amd.com> - */ - -#ifndef _ACP_7_0_OFFSET_HEADER -#define _ACP_7_0_OFFSET_HEADER - -#define PU_REGISTER_BASE (0x9FD00000 - 0x01240000) -#define PU_SCRATCH_REG_BASE (0x9FF00000 - 0x01250000) - -/* Registers from ACP_DMA block */ -#define ACP_DMA_CNTL_0 0x1240000 -#define ACP_DMA_DSCR_STRT_IDX_0 0x1240028 -#define ACP_DMA_DSCR_CNT_0 0x1240050 -#define ACP_DMA_PRIO_0 0x1240078 -#define ACP_DMA_DESC_BASE_ADDR 0x1240118 -#define ACP_DMA_DESC_MAX_NUM_DSCR 0x124011C -#define ACP_DMA_CH_STS 0x1240120 -#define ACP_DSP0_NONCACHE_OFFSET0 0x1240400 -#define ACP_DSP0_NONCACHE_SIZE0 0x1240404 -#define ACP_DSP0_NONCACHE_OFFSET1 0x1240408 -#define ACP_DSP0_NONCACHE_SIZE1 0x124040C -#define ACP_XNNE_IDLE_STATE 0x1240550 - -/* Registers from ACP_MISC block */ -#define ACP_INTR_URGENCY_TIMER 0x124101C -#define ACP_SYSHUB_DMA_URGENCY_TIMER 0x1240838 -#define ACP_OCD_HALT_ON_RST 0x124100C -#define ACP_CLKMUX_SEL 0x124102C -#define ACP_I2S_196MHZ_CLK_SEL 0x124103C -#define ACP_DSP0_INTR_CNTL 0x1241800 -#define ACP_DSP0_INTR_STAT 0x1241804 -#define ACP_DSP_SW_INTR_CNTL 0x1241808 -#define ACP_DSP_SW_INTR_STAT 0x124180C -#define ACP_SW_INTR_TRIG 0x1241810 -#define DSP_INTERRUPT_ROUTING_CTRL_0 0x1241814 -#define DSP_INTERRUPT_ROUTING_CTRL_1 0x1241818 -#define ACP_DSP_FW_STATUS 0x1241850 -#define ACP_FUTURE_REG_ACLK_0 0x1241854 -#define ACP_AXI2DAGB_SEM_0 0x1241874 -#define ACP_DSP0_INTR_CNTL1 0x1241920 -#define ACP_DSP0_INTR_STAT1 0x1241924 -#define ACP_SRBM_CLIENT_BASE_ADDR 0x12419EC -#define ACP_SRBM_CLIENT_RDDATA 0x12419F0 -#define ACP_SRBM_CYCLE_STS 0x12419F4 -#define ACP_SRBM_CLIENT_CONFIG 0x12419F8 - -/* Registers from ACP_P1_MISC block */ -#define ACP_EXTERNAL_INTR_ENB 0x1241A00 - -/* Registers from ACP_AUDIO_BUFFERS block */ -#define ACP_AUDIO_RX_RINGBUFADDR 0x1242000 -#define ACP_AUDIO_RX_RINGBUFSIZE 0x1242004 -#define ACP_AUDIO_RX_LINKPOSITIONCNTR 0x1242008 -#define ACP_AUDIO_RX_FIFOADDR 0x124200C -#define ACP_AUDIO_RX_FIFOSIZE 0x1242010 -#define ACP_AUDIO_RX_DMA_SIZE 0x1242014 -#define ACP_AUDIO_RX_LINEARPOSITIONCNTR_HIGH 0x1242018 -#define ACP_AUDIO_RX_LINEARPOSITIONCNTR_LOW 0x124201C -#define ACP_AUDIO_RX_INTR_WATERMARK_SIZE 0x1242020 -#define ACP_AUDIO_TX_RINGBUFADDR 0x1242024 -#define ACP_AUDIO_TX_RINGBUFSIZE 0x1242028 -#define ACP_AUDIO_TX_LINKPOSITIONCNTR 0x124202C -#define ACP_AUDIO_TX_FIFOADDR 0x1242030 -#define ACP_AUDIO_TX_FIFOSIZE 0x1242034 -#define ACP_AUDIO_TX_DMA_SIZE 0x1242038 -#define ACP_AUDIO_TX_LINEARPOSITIONCNTR_HIGH 0x124203C -#define ACP_AUDIO_TX_LINEARPOSITIONCNTR_LOW 0x1242040 -#define ACP_AUDIO_TX_INTR_WATERMARK_SIZE 0x1242044 - -#define ACP_BT_RX_RINGBUFADDR 0x1242048 -#define ACP_BT_RX_RINGBUFSIZE 0x124204C -#define ACP_BT_RX_FIFOADDR 0x1242054 -#define ACP_BT_RX_FIFOSIZE 0x1242058 -#define ACP_BT_RX_DMA_SIZE 0x124205C -#define ACP_BT_RX_LINEARPOSITIONCNTR_HIGH 0x1242060 -#define ACP_BT_RX_LINEARPOSITIONCNTR_LOW 0x1242064 -#define ACP_BT_RX_INTR_WATERMARK_SIZE 0x1242068 -#define ACP_BT_TX_RINGBUFADDR 0x124206C -#define ACP_BT_TX_RINGBUFSIZE 0x1242070 -#define ACP_BT_TX_FIFOADDR 0x1242078 -#define ACP_BT_TX_FIFOSIZE 0x124207C -#define ACP_BT_TX_DMA_SIZE 0x1242080 -#define ACP_BT_TX_LINEARPOSITIONCNTR_HIGH 0x1242084 -#define ACP_BT_TX_LINEARPOSITIONCNTR_LOW 0x1242088 -#define ACP_BT_TX_INTR_WATERMARK_SIZE 0x124208C - -#define ACP_HS_RX_RINGBUFADDR 0x1242090 -#define ACP_HS_RX_RINGBUFSIZE 0x1242094 -#define ACP_HS_RX_FIFOADDR 0x124209C -#define ACP_HS_RX_FIFOSIZE 0x12420A0 -#define ACP_HS_RX_DMA_SIZE 0x12420A4 -#define ACP_HS_RX_LINEARPOSITIONCNTR_HIGH 0x12420A8 -#define ACP_HS_RX_LINEARPOSITIONCNTR_LOW 0x12420AC -#define ACP_HS_RX_INTR_WATERMARK_SIZE 0x12420B0 -#define ACP_HS_TX_RINGBUFADDR 0x12420B4 -#define ACP_HS_TX_RINGBUFSIZE 0x12420B8 -#define ACP_HS_TX_FIFOADDR 0x12420C0 -#define ACP_HS_TX_FIFOSIZE 0x12420C4 -#define ACP_HS_TX_DMA_SIZE 0x12420C8 -#define ACP_HS_TX_LINEARPOSITIONCNTR_HIGH 0x12420CC -#define ACP_HS_TX_LINEARPOSITIONCNTR_LOW 0x12420D0 -#define ACP_HS_TX_INTR_WATERMARK_SIZE 0x12420D4 - -/* Registers from ACP_I2S_TDM block */ -#define ACP_I2STDM_IER 0x1242400 -#define ACP_I2STDM_IRER 0x1242404 -#define ACP_I2STDM_RXFRMT 0x1242408 -#define ACP_I2STDM_ITER 0x124240C -#define ACP_I2STDM_TXFRMT 0x1242410 -#define ACP_I2STDM0_MSTRCLKGEN 0x1242414 -#define ACP_I2STDM2_MSTRCLKGEN 0x124241C - -/* Registers from ACP_BT_TDM block */ -#define ACP_BTTDM_IER 0x1242800 -#define ACP_BTTDM_IRER 0x1242804 -#define ACP_BTTDM_ITER 0x124280C -#define ACP_HSTDM_IER 0x1242814 -#define ACP_HSTDM_IRER 0x1242818 -#define ACP_HSTDM_RXFRMT 0x124281C -#define ACP_HSTDM_ITER 0x1242820 -#define ACP_HSTDM_TXFRMT 0x1242824 - -/* Registers from ACP_WOV block */ -#define ACP_WOV_PDM_ENABLE 0x1242C04 -#define ACP_WOV_PDM_DMA_ENABLE 0x1242C08 -#define ACP_WOV_RX_RINGBUFADDR 0x1242C0C -#define ACP_WOV_RX_RINGBUFSIZE 0x1242C10 -#define ACP_WOV_RX_INTR_WATERMARK_SIZE 0x1242C20 -#define ACP_WOV_PDM_FIFO_FLUSH 0x1242C24 -#define ACP_WOV_PDM_NO_OF_CHANNELS 0x1242C28 -#define ACP_WOV_PDM_DECIMATION_FACTOR 0x1242C2C -#define ACP_WOV_MISC_CTRL 0x1242C5C -#define ACP_WOV_CLK_CTRL 0x1242C60 - -#define ACP_SW_EN 0x1243000 -#define ACP_SW_EN_STATUS 0x1243004 -#define ACP_SW_AUDIO_TX_EN 0x1243010 -#define ACP_SW_AUDIO_TX_EN_STATUS 0x1243014 -#define ACP_SW_BT_TX_EN 0x1243050 -#define ACP_SW_BT_TX_EN_STATUS 0x1243054 -#define ACP_SW_HS_TX_EN 0x124306C -#define ACP_SW_HS_TX_EN_STATUS 0x1243070 -#define ACP_SW_AUDIO_RX_EN 0x1243088 -#define ACP_SW_AUDIO_RX_EN_STATUS 0x124308C -#define ACP_SW_BT_RX_EN 0x1243128 -#define ACP_SW_BT_RX_EN_STATUS 0x124312C -#define ACP_SW_HS_RX_EN 0x1243144 -#define ACP_SW_HS_RX_EN_STATUS 0x1243148 - -/* Registers from ACP_P1_AUDIO_BUFFERS block */ -#define ACP_P1_I2S_RX_RINGBUFADDR 0x1243A00 -#define ACP_P1_I2S_RX_RINGBUFSIZE 0x1243A04 -#define ACP_P1_I2S_RX_FIFOADDR 0x1243A0C -#define ACP_P1_I2S_RX_FIFOSIZE 0x1243A10 -#define ACP_P1_I2S_RX_DMA_SIZE 0x1243A14 -#define ACP_P1_I2S_RX_LINEARPOSITIONCNTR_HIGH 0x1243A18 -#define ACP_P1_I2S_RX_LINEARPOSITIONCNTR_LOW 0x1243A1C -#define ACP_P1_I2S_RX_INTR_WATERMARK_SIZE 0x1243A20 -#define ACP_P1_I2S_TX_RINGBUFADDR 0x1243A24 -#define ACP_P1_I2S_TX_RINGBUFSIZE 0x1243A28 -#define ACP_P1_I2S_TX_FIFOADDR 0x1243A30 -#define ACP_P1_I2S_TX_FIFOSIZE 0x1243A34 -#define ACP_P1_I2S_TX_DMA_SIZE 0x1243A38 -#define ACP_P1_I2S_TX_LINEARPOSITIONCNTR_HIGH 0x1243A3C -#define ACP_P1_I2S_TX_LINEARPOSITIONCNTR_LOW 0x1243A40 -#define ACP_P1_I2S_TX_INTR_WATERMARK_SIZE 0x1243A44 - -#define ACP_P1_AUDIO_RX_RINGBUFADDR 0x1243A00 -#define ACP_P1_AUDIO_RX_RINGBUFSIZE 0x1243A04 -#define ACP_P1_AUDIO_RX_FIFOADDR 0x1243A0C -#define ACP_P1_AUDIO_RX_FIFOSIZE 0x1243A10 -#define ACP_P1_AUDIO_RX_DMA_SIZE 0x1243A14 -#define ACP_P1_AUDIO_RX_LINEARPOSITIONCNTR_HIGH 0x1243A18 -#define ACP_P1_AUDIO_RX_LINEARPOSITIONCNTR_LOW 0x1243A1C -#define ACP_P1_AUDIO_RX_INTR_WATERMARK_SIZE 0x1243A20 -#define ACP_P1_AUDIO_TX_RINGBUFADDR 0x1243A24 -#define ACP_P1_AUDIO_TX_RINGBUFSIZE 0x1243A28 -#define ACP_P1_AUDIO_TX_FIFOADDR 0x1243A30 -#define ACP_P1_AUDIO_TX_FIFOSIZE 0x1243A34 -#define ACP_P1_AUDIO_TX_DMA_SIZE 0x1243A38 -#define ACP_P1_AUDIO_TX_LINEARPOSITIONCNTR_HIGH 0x1243A3C -#define ACP_P1_AUDIO_TX_LINEARPOSITIONCNTR_LOW 0x1243A40 -#define ACP_P1_AUDIO_TX_INTR_WATERMARK_SIZE 0x1243A44 - -#define ACP_P1_BT_RX_RINGBUFADDR 0x1243A48 -#define ACP_P1_BT_RX_RINGBUFSIZE 0x1243A4C -#define ACP_P1_BT_RX_FIFOADDR 0x1243A54 -#define ACP_P1_BT_RX_FIFOSIZE 0x1243A58 -#define ACP_P1_BT_RX_DMA_SIZE 0x1243A5C -#define ACP_P1_BT_RX_LINEARPOSITIONCNTR_HIGH 0x1243A60 -#define ACP_P1_BT_RX_LINEARPOSITIONCNTR_LOW 0x1243A64 -#define ACP_P1_BT_RX_INTR_WATERMARK_SIZE 0x1243A68 -#define ACP_P1_BT_TX_RINGBUFADDR 0x1243A6C -#define ACP_P1_BT_TX_RINGBUFSIZE 0x1243A70 -#define ACP_P1_BT_TX_FIFOADDR 0x1243A78 -#define ACP_P1_BT_TX_FIFOSIZE 0x1243A7C -#define ACP_P1_BT_TX_DMA_SIZE 0x1243A80 -#define ACP_P1_BT_TX_LINEARPOSITIONCNTR_HIGH 0x1243A84 -#define ACP_P1_BT_TX_LINEARPOSITIONCNTR_LOW 0x1243A88 -#define ACP_P1_BT_TX_INTR_WATERMARK_SIZE 0x1243A8C -#define ACP_P1_HS_RX_RINGBUFADDR 0x1243A90 -#define ACP_P1_HS_RX_RINGBUFSIZE 0x1243A94 -#define ACP_P1_HS_RX_FIFOADDR 0x1243A9C -#define ACP_P1_HS_RX_FIFOSIZE 0x1243AA0 -#define ACP_P1_HS_RX_DMA_SIZE 0x1243AA4 -#define ACP_P1_HS_RX_LINEARPOSITIONCNTR_HIGH 0x1243AA8 -#define ACP_P1_HS_RX_LINEARPOSITIONCNTR_LOW 0x1243AAC -#define ACP_P1_HS_RX_INTR_WATERMARK_SIZE 0x1243AB0 -#define ACP_P1_HS_TX_RINGBUFADDR 0x1243AB4 -#define ACP_P1_HS_TX_RINGBUFSIZE 0x1243AB8 -#define ACP_P1_HS_TX_FIFOADDR 0x1243AC0 -#define ACP_P1_HS_TX_FIFOSIZE 0x1243AC4 -#define ACP_P1_HS_TX_DMA_SIZE 0x1243AC8 -#define ACP_P1_HS_TX_LINEARPOSITIONCNTR_HIGH 0x1243ACC -#define ACP_P1_HS_TX_LINEARPOSITIONCNTR_LOW 0x1243AD0 -#define ACP_P1_HS_TX_INTR_WATERMARK_SIZE 0x1243AD4 - -#define ACP_P1_SW_AUDIO_TX_EN 0x1243C10 -#define ACP_P1_SW_AUDIO_TX_EN_STATUS 0x1243C14 -#define ACP_P1_SW_AUDIO_RX_EN 0x1243C88 -#define ACP_P1_SW_AUDIO_RX_EN_STATUS 0x1243C8C - -#define ACP_P1_SW_BT_TX_EN 0x1243C50 -#define ACP_P1_SW_BT_TX_EN_STATUS 0x1243C54 -#define ACP_P1_SW_BT_RX_EN 0x1243D28 -#define ACP_P1_SW_BT_RX_EN_STATUS 0x1243D2C - -#define ACP_P1_SW_HEADSET_TX_EN 0x1243C6C -#define ACP_P1_SW_HEADSET_TX_EN_STATUS 0x1243C70 -#define ACP_P1_SW_HEADSET_RX_EN 0x1243D44 -#define ACP_P1_SW_HEADSET_RX_EN_STATUS 0x1243D48 - -#define MP1_SMN_C2PMSG_69 0x58A14 -#define MP1_SMN_C2PMSG_85 0x58A54 -#define MP1_SMN_C2PMSG_93 0x58A74 - -#define CLK7_ROOTREFCLK_MUX_1 0x6C0C8 -#define CLK7_CLK_PLL_REFCLK_RATE_STARTUP 0x6C0D0 -#define CLK7_CLK_PLL_REQ 0x6C0DC -#define CLK7_CLK1_DFS_CNTL 0x6C1B0 -#define CLK7_CLK1_CURRENT_CNT 0x6C378 -#define CLK7_CLK0_DFS_CNTL 0x6C1A4 -#define CLK7_CLK0_CURRENT_CNT 0x6C374 -#define CLK7_CLK0_BYPASS_CNTL 0x6C210 -#define CLK7_CLK1_BYPASS_CNTL 0x6C234 -#define CLK7_CLK0_DFS_STATUS 0x6C1AC -#define CLK7_CLK1_DFS_STATUS 0x6C1B8 -#define CLK7_SPLL_FIELD_2 0x6C114 -#define CLK7_CLK2_CURRENT_CNT 0x6C37C -#define CLK7_CLK2_BYPASS_CNTL 0x6C258 -#define CLK7_CLK2_DFS_STATUS 0x6C1C4 -#define CLK7_CLK_PLL_PWR_REQ 0x6C2F0 -#define CLK7_CLK_DFSBYPASS_CONTROL 0x6C2F8 -#define CLK7_CLK_FSM_STATUS 0x6C304 -#define CLK7_SPLL_FUSE_1 0x6C0F8 -#define CLK7_SPLL_FUSE_2 0x6C0FC -#define CLK7_SPLL_FIELD_7 0x6C128 -#define CLK7_SPLL_FIELD_9 0x6C130 -#define CLK7_SPLL_FIELD_6nm 0x6C138 -#define CLK7_SPLL_FIELD_4 0x6C11C -#define CLK7_SPLL_FIELD_5nm_BUS_CTRL 0x6C140 -#define CLK7_SPLL_FIELD_5nm_BUS_WDATA 0x6C144 -#define CLK7_SPLL_FIELD_5nm_BUS_STATUS 0x6C148 -#define CLK7_CLK_PLL_RESET_STOP_TIMER 0x6C180 - -#endif diff --git a/src/platform/amd/acp_7_0/include/platform/chip_registers.h b/src/platform/amd/acp_7_0/include/platform/chip_registers.h deleted file mode 100644 index 99ff883e6dd5..000000000000 --- a/src/platform/amd/acp_7_0/include/platform/chip_registers.h +++ /dev/null @@ -1,1061 +0,0 @@ -/* SPDX-License-Identifier: BSD-3-Clause - * - * Copyright(c) 2024 AMD.All rights reserved. - * - * Author: SaiSurya, Ch <saisurya.chakkaveeravenkatanaga@amd.com> - */ -#if !defined(_ACP_7_0_REG_HEADER) -#define _ACP_7_0_REG_HEADER - -typedef union acp_dma_cntl_0 { - struct { - unsigned int dmachrst:1; - unsigned int dmachrun:1; - unsigned int dmachiocen:1; - unsigned int :29; - } bits; - unsigned int u32all; -} acp_dma_cntl_0_t; - -typedef union acp_dma_ch_sts { - struct { - unsigned int dmachrunsts:8; - unsigned int :24; - } bits; - unsigned int u32all; -} acp_dma_ch_sts_t; - -typedef union acp_external_intr_enb { - struct { - unsigned int acpextintrenb:1; - unsigned int :31; - } bits; - unsigned int u32all; -} acp_external_intr_enb_t; - -typedef union acp_dsp0_intr_cntl { - struct { - unsigned int dmaiocmask:8; - unsigned int :8; - unsigned int wov_dma_intr_mask:1; - unsigned int :6; - unsigned int audio_buffer_int_mask:6; - unsigned int :3; - } bits; - unsigned int u32all; -} acp_dsp0_intr_cntl_t; - -typedef union acp_dsp0_intr_stat { - struct { - unsigned int dmaiocstat:8; - unsigned int :8; - unsigned int wov_dma_stat:1; - unsigned int :6; - unsigned int audio_buffer_int_stat:6; - unsigned int :3; - } bits; - unsigned int u32all; -} acp_dsp0_intr_stat_t; - -typedef union acp_dsp0_intr_cntl1 { - struct { - unsigned int acp_fusion_dsp_ext_timer1_timeoutmask :1; - unsigned int fusion_dsp_watchdog_timeoutmask :1; - unsigned int soundwire_mask :1; - unsigned int audio_buffer_int_mask :6; - unsigned int :23; - } bits; - unsigned int u32all; -} acp_dsp0_intr_cntl1_t; - -typedef union acp_dsp0_intr_stat1 { - struct { - unsigned int acp_fusion_dsp_timer1_timeoutstat :1; - unsigned int fusion_dsp_watchdog_timeoutstat :1; - unsigned int soundwire_stat :1; - unsigned int audio_buffer_int_stat :6; - unsigned int :23; - } bits; - unsigned int u32all; -} acp_dsp0_intr_stat1_t; - -typedef union acp_dsp_sw_intr_cntl { - struct { - unsigned int :2; - unsigned int dsp0_to_host_intr_mask:1; - unsigned int :29; - } bits; - unsigned int u32all; -} acp_dsp_sw_intr_cntl_t; - -typedef union acp_dsp_sw_intr_stat { - struct { - unsigned int host_to_dsp0_intr1_stat:1; - unsigned int host_to_dsp0_intr2_stat:1; - unsigned int dsp0_to_host_intr_stat:1; - unsigned int host_to_dsp0_intr3_stat:1; - unsigned int :28; - } bits; - unsigned int u32all; -} acp_dsp_sw_intr_stat_t; - -typedef union acp_sw_intr_trig { - struct { - unsigned int trig_host_to_dsp0_intr1:1; - unsigned int :1; - unsigned int trig_dsp0_to_host_intr:1; - unsigned int :29; - } bits; - unsigned int u32all; -} acp_sw_intr_trig_t; - -typedef union dsp_interrupt_routing_ctrl_0 { - struct { - unsigned int dma_intr_level:3; - unsigned int :18; - unsigned int watchdog_intr_level:3; - unsigned int az_sw_i2s_intr_level:3; - unsigned int sha_intr_level:3; - unsigned int :2; - } bits; - unsigned int u32all; -} dsp_interrupt_routing_ctrl_0_t; - -typedef union dsp_interrupt_routing_ctrl_1 { - struct { - unsigned int host_to_dsp_intr1_level:3; - unsigned int host_to_dsp_intr2_level:3; - unsigned int src_intr_level:3; - unsigned int mailbox_intr_level:3; - unsigned int error_intr_level:3; - unsigned int wov_intr_level:3; - unsigned int fusion_timer1_intr_level:3; - unsigned int fusion_watchdog_intr_level:3; - unsigned int p1_sw_i2s_intr_level:3; - unsigned int :5; - } bits; - unsigned int u32all; -} dsp_interrupt_routing_ctrl_1_t; - -typedef union acp_i2s_rx_ringbufaddr { - struct { - unsigned int i2s_rx_ringbufaddr:27; - unsigned int :5; - } bits; - unsigned int u32all; -} acp_i2s_rx_ringbufaddr_t; - -typedef union acp_i2s_rx_ringbufsize { - struct { - unsigned int i2s_rx_ringbufsize:26; - unsigned int :6; - } bits; - unsigned int u32all; -} acp_i2s_rx_ringbufsize_t; - -typedef union acp_i2s_rx_linkpositioncntr { - struct { - unsigned int i2s_rx_linkpositioncntr:26; - unsigned int :6; - } bits; - unsigned int u32all; -} acp_i2s_rx_linkpositioncntr_t; - -typedef union acp_i2s_rx_fifoaddr { - struct { - unsigned int i2s_rx_fifoaddr:27; - unsigned int :5; - } bits; - unsigned int u32all; -} acp_i2s_rx_fifoaddr_t; - -typedef union acp_i2s_rx_fifosize { - struct { - unsigned int i2s_rx_fifosize:13; - unsigned int :19; - } bits; - unsigned int u32all; -} acp_i2s_rx_fifosize_t; - -typedef union acp_i2s_rx_dma_size { - struct { - unsigned int i2s_rx_dma_size:13; - unsigned int :19; - } bits; - unsigned int u32all; -} acp_i2s_rx_dma_size_t; - -typedef union acp_i2s_rx_linearpositioncntr_high { - struct { - unsigned int i2s_rx_linearpositioncntr_high:32; - } bits; - unsigned int u32all; -} acp_i2s_rx_linearpositioncntr_high_t; - -typedef union acp_i2s_rx_linearpositioncntr_low { - struct { - unsigned int i2s_rx_linearpositioncntr_low:32; - } bits; - unsigned int u32all; -} acp_i2s_rx_linearpositioncntr_low_t; - -typedef union acp_i2s_rx_watermark_size { - struct { - unsigned int i2s_rx_intr_watermark_size:26; - unsigned int :6; - } bits; - unsigned int u32all; -} acp_i2s_rx_intr_watermark_size_t; - -typedef union acp_i2s_tx_ringbufaddr { - struct { - unsigned int i2s_tx_ringbufaddr:27; - unsigned int :5; - } bits; - unsigned int u32all; -} acp_i2s_tx_ringbufaddr_t; - -typedef union acp_i2s_tx_ringbufsize { - struct { - unsigned int i2s_tx_ringbufsize:26; - unsigned int :6; - } bits; - unsigned int u32all; -} acp_i2s_tx_ringbufsize_t; - -typedef union acp_i2s_tx_linkpositioncntr { - struct { - unsigned int i2s_tx_linkpositioncntr:26; - unsigned int :6; - } bits; - unsigned int u32all; -} acp_i2s_tx_linkpositioncntr_t; - -typedef union acp_i2s_tx_fifoaddr { - struct { - unsigned int i2s_tx_fifoaddr:27; - unsigned int :5; - } bits; - unsigned int u32all; -} acp_i2s_tx_fifoaddr_t; - -typedef union acp_i2s_tx_fifosize { - struct { - unsigned int i2s_tx_fifosize:13; - unsigned int :19; - } bits; - unsigned int u32all; -} acp_i2s_tx_fifosize_t; - -typedef union acp_i2s_tx_dma_size { - struct { - unsigned int i2s_tx_dma_size:13; - unsigned int :19; - } bits; - unsigned int u32all; -} acp_i2s_tx_dma_size_t; - -typedef union acp_i2s_tx_linearpositioncntr_high { - struct { - unsigned int i2s_tx_linearpositioncntr_high:32; - } bits; - unsigned int u32all; -} acp_i2s_tx_linearpositioncntr_hight_t; - -typedef union acp_i2s_tx_linearpositioncntr_low { - struct { - unsigned int i2s_tx_linearpositioncntr_low:32; - } bits; - unsigned int u32all; -} acp_i2s_tx_linearpositioncntr_low_t; - -typedef union acp_i2s_tx_intr_watermark_size { - struct { - unsigned int i2s_tx_intr_watermark_size:26; - unsigned int :6; - } bits; - unsigned int u32all; -} acp_i2s_tx_intr_watermark_size_t; - -typedef union acp_bt_rx_ringbufaddr { - struct { - unsigned int bt_rx_ringbufaddr:27; - unsigned int :5; - } bits; - unsigned int u32all; -} acp_bt_rx_ringbufaddr_t; - -typedef union acp_bt_rx_ringbufsize { - struct { - unsigned int bt_rx_ringbufsize:26; - unsigned int :6; - } bits; - unsigned int u32all; -} acp_bt_rx_ringbufsize_t; - -typedef union acp_bt_rx_linkpositioncntr { - struct { - unsigned int bt_rx_linkpositioncntr:26; - unsigned int :6; - } bits; - unsigned int u32all; -} acp_bt_rx_linkpositioncntr_t; - -typedef union acp_bt_rx_fifoaddr { - struct { - unsigned int bt_rx_fifoaddr:27; - unsigned int :5; - } bits; - unsigned int u32all; -} acp_bt_rx_fifoaddr_t; - -typedef union acp_bt_rx_fifosize { - struct { - unsigned int bt_rx_fifosize:13; - unsigned int :19; - } bits; - unsigned int u32all; -} acp_bt_rx_fifosize_t; - -typedef union acp_bt_rx_dma_size { - struct { - unsigned int bt_rx_dma_size:13; - unsigned int :19; - } bits; - unsigned int u32all; -} acp_bt_rx_dma_size_t; - -typedef union acp_bt_rx_linearpositioncntr_high { - struct { - unsigned int bt_rx_linearpositioncntr_high:32; - } bits; - unsigned int u32all; -} acp_bt_rx_linearpositioncntr_high_t; - -typedef union acp_bt_rx_linearpositioncntr_low { - struct { - unsigned int bt_rx_linearpositioncntr_low:32; - } bits; - unsigned int u32all; -} acp_bt_rx_linearpositioncntr_low_t; - -typedef union acp_bt_rx_intr_watermark_size { - struct { - unsigned int bt_rx_intr_watermark_size:26; - unsigned int :6; - } bits; - unsigned int u32all; -} acp_bt_rx_intr_watermark_size_t; - -typedef union acp_bt_tx_ringbufaddr { - struct { - unsigned int bt_tx_ringbufaddr:27; - unsigned int :5; - } bits; - unsigned int u32all; -} acp_bt_tx_ringbufaddr_t; - -typedef union acp_bt_tx_ringbufsize { - struct { - unsigned int bt_tx_ringbufsize:26; - unsigned int :6; - } bits; - unsigned int u32all; -} acp_bt_tx_ringbufsize_t; - -typedef union acp_bt_tx_linkpositiontcntr { - struct { - unsigned int bt_tx_linkpositioncntr:26; - unsigned int :6; - } bits; - unsigned int u32all; -} acp_bt_tx_linkpositiontcntr_t; - -typedef union acp_bt_tx_fifoaddr { - struct { - unsigned int bt_tx_fifoaddr:27; - unsigned int :5; - } bits; - unsigned int u32all; -} acp_bt_tx_fifoaddr_t; - -typedef union acp_bt_tx_fifosize { - struct { - unsigned int bt_tx_fifosize:13; - unsigned int :19; - } bits; - unsigned int u32all; -} acp_bt_tx_fifosize_t; - -typedef union acp_bt_tx_dmasize { - struct { - unsigned int bt_tx_dma_size:13; - unsigned int :19; - } bits; - unsigned int u32all; -} acp_bt_tx_dmasize_t; - -typedef union acp_bt_tx_linearpositioncntr_high { - struct { - unsigned int bt_tx_linearpositioncntr_high:32; - } bits; - unsigned int u32all; -} acp_bt_tx_linearpositioncntr_high_t; - -typedef union acp_bt_tx_linearpositioncntr_low { - struct { - unsigned int bt_tx_linearpositioncntr_low:32; - } bits; - unsigned int u32all; -} acp_bt_tx_linearpositioncntr_low_t; - -typedef union acp_bt_tx_intr_watermark_size { - struct { - unsigned int bt_tx_intr_watermark_size:26; - unsigned int :6; - } bits; - unsigned int u32all; -} acp_bt_tx_intr_watermark_size_t; - -typedef union acp_i2stdm_ier { - struct { - unsigned int i2stdm_ien:1; - unsigned int :31; - } bits; - unsigned int u32all; -} acp_i2stdm_ier_t; - -typedef union acp_i2stdm_irer { - struct { - unsigned int i2stdm_rx_en:1; - unsigned int i2stdm_rx_protocol_mode:1; - unsigned int i2stdm_rx_data_path_mode:1; - unsigned int i2stdm_rx_samplen:3; - unsigned int i2stdm_rx_status:1; - unsigned int :25; - } bits; - unsigned int u32all; -} acp_i2stdm_irer_t; - -typedef union acp_i2stdm_iter { - struct { - unsigned int i2stdm_txen:1; - unsigned int i2stdm_tx_protocol_mode:1; - unsigned int i2stdm_tx_data_path_mode:1; - unsigned int i2stdm_tx_samp_len:3; - unsigned int i2stdm_tx_status:1; - unsigned int :25; - } bits; - unsigned int u32all; -} acp_i2stdm_iter_t; - -typedef union acp_bttdm_ier { - struct { - unsigned int bttdm_ien:1; - unsigned int :31; - } bits; - unsigned int u32all; -} acp_bttdm_ier_t; - -typedef union acp_bttdm_irer { - struct { - unsigned int bttdm_rx_en:1; - unsigned int bttdm_rx_protocol_mode:1; - unsigned int bttdm_rx_data_path_mode:1; - unsigned int bttdm_rx_samplen:3; - unsigned int bttdm_rx_status:1; - unsigned int :25; - } bits; - unsigned int u32all; -} acp_bttdm_irer_t; - -typedef union acp_bttdm_iter { - struct { - unsigned int bttdm_txen :1; - unsigned int bttdm_tx_protocol_mode :1; - unsigned int bttdm_tx_data_path_mode :1; - unsigned int bttdm_tx_samp_len :3; - unsigned int bttdm_tx_status :1; - unsigned int :25; - } bits; - unsigned int u32all; -} acp_bttdm_iter_t; - -typedef union acp_wov_pdm_dma_enable { - struct { - unsigned int pdm_dma_en :1; - unsigned int pdm_dma_en_status :1; - unsigned int :30; - } bits; -unsigned int u32all; -} acp_wov_pdm_dma_enable_t; - -typedef union acp_wov_rx_ringbufaddr { - struct { - unsigned int rx_ringbufaddr :27; - unsigned int :5; - } bits; - unsigned int u32all; -} acp_wov_rx_ringbufaddr_t; - -typedef union acp_wov_rx_ringbufsize { - struct { - unsigned int rx_ringbufsize :26; - unsigned int :6; - } bits; - unsigned int u32all; -} acp_wov_rx_ringbufsize_t; - -typedef union acp_wov_rx_intr_watermark_size { - struct { - unsigned int rx_intr_watermark_size :26; - unsigned int :6; - } bits; - unsigned int u32all; -} acp_wov_rx_intr_watermark_size_t; - -typedef union acp_wov_pdm_no_of_channels { - struct { - unsigned int pdm_no_of_channels :2; - unsigned int :30; - } bits; - unsigned int u32all; -} acp_wov_pdm_no_of_channels_t; - -typedef union acp_wov_pdm_decimation_factor { - struct { - unsigned int pdm_decimation_factor :2; - unsigned int :30; - } bits; - unsigned int u32all; -} acp_wov_pdm_decimation_factor_t; - -typedef union acp_wov_misc_ctrl { - struct { - unsigned int :3; - unsigned int pcm_data_shift_ctrl :2; - unsigned int :27; - } bits; - unsigned int u32all; -} acp_wov_misc_ctrl_t; - -typedef union acp_wov_clk_ctrl { - struct { - unsigned int brm_clk_ctrl :4; - unsigned int pdm_vad_clkdiv :2; - unsigned int :26; - } bits; - unsigned int u32all; -} acp_wov_clk_ctrl_t; - -typedef union acp_srbm_cycle_sts { - struct { - unsigned int srbm_clients_sts :1; - unsigned int :7; - } bits; - unsigned int u32all; -} acp_srbm_cycle_sts_t; - -typedef union acp_hs_rx_ringbufaddr { - struct { - unsigned int hs_rx_ringbufaddr :32; - } bits; - unsigned int u32all; -} acp_hs_rx_ringbufaddr_t; - -typedef union acp_hs_rx_ringbufsize { - struct { - unsigned int hs_rx_ringbufsize :26; - unsigned int :6; - } bits; - unsigned int u32all; -} acp_hs_rx_ringbufsize_t; - -typedef union acp_hs_rx_linkpositioncntr { - struct { - unsigned int hs_rx_linkpositioncntr :26; - unsigned int :6; - } bits; - unsigned int u32all; -} acp_hs_rx_linkpositioncntr_t; - -typedef union acp_hs_rx_fifoaddr { - struct { - unsigned int hs_rx_fifoaddr :27; - unsigned int :5; - } bits; - unsigned int u32all; -} acp_hs_rx_fifoaddr_t; - -typedef union acp_hs_rx_fifosize { - struct { - unsigned int hs_rx_fifosize :13; - unsigned int :19; - } bits; - unsigned int u32all; -} acp_hs_rx_fifosize_t; - -typedef union acp_hs_rx_dma_size { - struct { - unsigned int hs_rx_dma_size :13; - unsigned int :19; - } bits; - unsigned int u32all; -} acp_hs_rx_dma_size_t; - -typedef union acp_hs_rx_linearpositioncntr_high { - struct { - unsigned int hs_rx_linearpositioncntr_high :32; - } bits; - unsigned int u32all; -} acp_hs_rx_linearpositioncntr_high_t; - -typedef union acp_hs_rx_linearpositioncntr_low { - struct { - unsigned int hs_rx_linearpositioncntr_low :32; - } bits; - unsigned int u32all; -} acp_hs_rx_linearpositioncntr_low_t; - -typedef union acp_hs_rx_intr_watermark_size { - struct { - unsigned int hs_rx_intr_watermark_size :26; - unsigned int :6; - } bits; - unsigned int u32all; -} acp_hs_rx_intr_watermark_size_t; - -typedef union acp_hs_tx_ringbufaddr { - struct { - unsigned int hs_tx_ringbufaddr :32; - } bits; - unsigned int u32all; -} acp_hs_tx_ringbufaddr_t; - -typedef union acp_hs_tx_ringbufsize { - struct { - unsigned int hs_tx_ringbufsize :26; - unsigned int :6; - } bits; - unsigned int u32all; -} acp_hs_tx_ringbufsize_t; - -typedef union acp_hs_tx_linkpositioncntr { - struct { - unsigned int hs_tx_linkpositioncntr :26; - unsigned int :6; - } bits; - unsigned int u32all; -} acp_hs_tx_linkpositioncntr_t; - -typedef union acp_hs_tx_fifoaddr { - struct { - unsigned int hs_tx_fifoaddr :27; - unsigned int :5; - } bits; - unsigned int u32all; -} acp_hs_tx_fifoaddr_t; - -typedef union acp_hs_tx_fifosize { - struct { - unsigned int hs_tx_fifosize :13; - unsigned int :19; - } bits; - unsigned int u32all; -} acp_hs_tx_fifosize_t; - -typedef union acp_hs_tx_dma_size { - struct { - unsigned int hs_tx_dma_size :13; - unsigned int :19; - } bits; - unsigned int u32all; -} acp_hs_tx_dma_size_t; - -typedef union acp_hs_tx_linearpositioncntr_high { - struct { - unsigned int hs_tx_linearpositioncntr_high :32; - } bits; - unsigned int u32all; -} acp_hs_tx_linearpositioncntr_high_t; - -typedef union acp_hs_tx_linearpositioncntr_low { - struct { - unsigned int hs_tx_linearpositioncntr_low :32; - } bits; - unsigned int u32all; -} acp_hs_tx_linearpositioncntr_low_t; - -typedef union acp_hs_tx_intr_watermark_size { - struct { - unsigned int hs_tx_intr_watermark_size :26; - unsigned int :6; - } bits; - unsigned int u32all; -} acp_hs_tx_intr_watermark_size_t; - -typedef union acp_i2stdm_rxfrmt { - struct { - unsigned int i2stdm_frame_len :9; - unsigned int :6; - unsigned int i2stdm_num_slots :3; - unsigned int i2stdm_slot_len :5; - unsigned int :9; - } bits; - unsigned int u32all; -} acp_i2stdm_rxfrmt_t; - -typedef union acp_i2stdm_txfrmt { - struct { - unsigned int i2stdm_frame_len :9; - unsigned int :6; - unsigned int i2stdm_num_slots :3; - unsigned int i2stdm_slot_len :5; - unsigned int :9; - } bits; - unsigned int u32all; -} acp_i2stdm_txfrmt_t; - -typedef union acp_hstdm_ier { - struct { - unsigned int hstdm_ien :1; - unsigned int :31; - } bits; - unsigned int u32all; -} acp_hstdm_ier_t; - -typedef union acp_hstdm_irer { - struct { - unsigned int hstdm_rx_en :1; - unsigned int hstdm_rx_protocol_mode :1; - unsigned int hstdm_rx_data_path_mode :1; - unsigned int hstdm_rx_samplen :3; - unsigned int hstdm_rx_status :1; - unsigned int :25; - } bits; - unsigned int u32all; -} acp_hstdm_irer_t; - -typedef union acp_hstdm_rxfrmt { - struct { - unsigned int hstdm_frame_len :9; - unsigned int :6; - unsigned int hstdm_num_slots :3; - unsigned int hstdm_slot_len :5; - unsigned int :9; - } bits; - unsigned int u32all; -} acp_hstdm_rxfrmt_t; - -typedef union acp_hstdm_iter { - struct { - unsigned int hstdm_txen :1; - unsigned int hstdm_tx_protocol_mode :1; - unsigned int hstdm_tx_data_path_mode :1; - unsigned int hstdm_tx_samp_len :3; - unsigned int hstdm_tx_status :1; - unsigned int :25; - } bits; - unsigned int u32all; -} acp_hstdm_iter_t; - -typedef union acp_hstdm_txfrmt { - struct { - unsigned int hstdm_frame_len :9; - unsigned int :6; - unsigned int hstdm_num_slots :3; - unsigned int hstdm_slot_len :5; - unsigned int :9; - } bits; - unsigned int u32all; -} acp_hstdm_txfrmt_t; - -typedef union acp_clkmux_sel { - struct { - unsigned int acp_clkmux_sel : 3; - unsigned int : 13; - unsigned int acp_clkmux_div_value : 16; - } bits; - unsigned int u32all; -} acp_clkmux_sel_t; - -typedef union acp_i2stdm_mstrclkgen { - struct { - unsigned int i2stdm_master_mode : 1; - unsigned int i2stdm_format_mode : 1; - unsigned int i2stdm_lrclk_div_val : 11; - unsigned int i2stdm_bclk_div_val : 11; - unsigned int : 8; - } bits; - unsigned int u32all; -} acp_i2stdm_mstrclkgen_t; - -typedef union clk7_clk1_dfs_cntl_u { - struct { - unsigned int CLK1_DIVIDER : 7; - unsigned int : 25; - } bitfields, bits; - uint32_t u32all; - int32_t i32all; - float f32all; -} clk7_clk1_dfs_cntl_u_t; - -typedef union clk7_clk1_dfs_status_u { - struct { - unsigned int : 16; - unsigned int CLK1_DFS_DIV_REQ_IDLE : 1; - unsigned int : 2; - unsigned int RO_CLK1_DFS_STATE_IDLE : 1; - unsigned int CLK1_CURRENT_DFS_DID : 7; - unsigned int : 5; - } bitfields, bits; - uint32_t u32all; - int32_t i32all; - float f32all; -} clk7_clk1_dfs_status_u_t; - -typedef union clk7_clk1_bypass_cntl_u { - struct { - unsigned int CLK1_BYPASS_SEL : 3; - unsigned int : 13; - unsigned int CLK1_BYPASS_DIV : 4; - unsigned int : 12; - } bitfields, bits; - uint32_t u32all; - int32_t i32all; - float f32all; -} clk7_clk1_bypass_cntl_u_t; - -typedef union clk7_clk_fsm_status_u { - struct { - unsigned int AUTOLAUCH_FSM_FULL_SPEED_IDLE : 1; - unsigned int : 3; - unsigned int AUTOLAUCH_FSM_BYPASS_IDLE : 1; - unsigned int : 3; - unsigned int RO_FSM_PLL_STATUS_STARTED : 1; - unsigned int : 3; - unsigned int RO_FSM_PLL_STATUS_STOPPED : 1; - unsigned int : 3; - unsigned int RO_EARLY_FSM_DONE : 1; - unsigned int : 3; - unsigned int RO_DFS_GAP_ACTIVE : 1; - unsigned int : 3; - unsigned int RO_DID_FSM_IDLE : 1; - unsigned int : 7; - } bitfields, bits; - uint32_t u32all; - int32_t i32all; - float f32all; -} clk7_clk_fsm_status_t; - -typedef union clk7_clk_pll_req_u { - struct { - unsigned int fbmult_int : 9; - unsigned int : 3; - unsigned int pllspinediv : 4; - unsigned int fbmult_frac : 16; - } bitfields, bits; - uint32_t u32all; - int32_t i32all; - float f32all; -} clk7_clk_pll_req_u_t; - -typedef union clk7_clk_pll_refclk_startup { - struct { - unsigned int main_pll_ref_clk_rate_startup : 8; - unsigned int main_pll_cfg_4_startup : 8; - unsigned int main_pll_ref_clk_div_startup : 2; - unsigned int main_pll_cfg_3_startup : 10; - unsigned int : 1; - unsigned int main_pll_refclk_src_mux0_startup : 1; - unsigned int main_pll_refclk_src_mux1_startup : 1; - unsigned int : 1; - } bitfields, bits; - uint32_t u32all; - int32_t i32all; - float f32all; -} clk7_clk_pll_refclk_startup_t; - -typedef union clk7_spll_field_2 { - struct{ - unsigned int : 3; - unsigned int spll_fbdiv_mask_en : 1; - unsigned int spll_fracn_en : 1; - unsigned int spll_freq_jump_en : 1; - unsigned int : 25; - } bitfields, bits; - uint32_t u32all; - int32_t i32all; - float f32all; -} clk7_spll_field_2_t; - -typedef union clk7_clk_dfsbypass_cntl { - struct { - unsigned int enter_dfs_bypass_0 : 1; - unsigned int enter_dfs_bypass_1 : 1; - unsigned int : 14; - unsigned int exit_dfs_bypass_0 : 1; - unsigned int exit_dfs_bypass_1 : 1; - unsigned int : 14; - } bitfields, bits; - uint32_t u32all; - int32_t i32all; - float f32all; -} clk7_clk_dfsbypass_cntl_t; - -typedef union clk7_clk_pll_pwr_req { - struct { - unsigned int PLL_AUTO_START_REQ : 1; - unsigned int : 3; - unsigned int PLL_AUTO_STOP_REQ : 1; - unsigned int : 3; - unsigned int PLL_AUTO_STOP_NOCLK_REQ : 1; - unsigned int : 3; - unsigned int PLL_AUTO_STOP_REFBYPCLK_REQ : 1; - unsigned int : 3; - unsigned int PLL_FORCE_RESET_HIGH : 1; - unsigned int : 15; - } bitfields, bits; - uint32_t u32all; - int32_t i32all; - float f32all; -} clk7_clk_pll_pwr_req_t; - -typedef union clk7_spll_fuse_1 { - struct { - unsigned int : 8; - unsigned int spll_gp_coarse_exp : 4; - unsigned int spll_gp_coarse_mant : 4; - unsigned int : 4; - unsigned int spll_gi_coarse_exp : 4; - unsigned int : 1; - unsigned int spll_gi_coarse_mant : 2; - unsigned int : 5; - } bitfields, bits; - uint32_t u32all; - int32_t i32all; - float f32all; -} clk7_spll_fuse_1_t; - -typedef union clk7_spll_fuse_2 { - struct { - unsigned int spll_tdc_resolution : 8; - unsigned int spll_freq_offset_exp : 4; - unsigned int spll_freq_offset_mant : 5; - unsigned int : 15; - } bitfields, bits; - uint32_t u32all; - int32_t i32all; - float f32all; -} clk7_spll_fuse_2_t; - -typedef union clk7_spll_field_9 { - struct { - unsigned int : 16; - unsigned int spll_dpll_cfg_3 : 10; - unsigned int spll_fll_mode : 1; - unsigned int : 5; - } bitfields, bits; - uint32_t u32all; - int32_t i32all; - float f32all; -} clk7_spll_field_9_t; - -typedef union clk7_spll_field_6nm { - struct { - unsigned int spll_dpll_cfg_4 : 8; - unsigned int spll_reg_tim_exp : 3; - unsigned int spll_reg_tim_mant : 1; - unsigned int spll_ref_tim_exp : 3; - unsigned int spll_ref_tim_mant : 1; - unsigned int spll_vco_pre_div : 2; - unsigned int : 14; - } bitfields, bits; - uint32_t u32all; - int32_t i32all; - float f32all; -} clk7_spll_field_6nm_t; - -typedef union clk7_spll_field_7 { - struct { - unsigned int : 7; - unsigned int spll_pllout_sel : 1; - unsigned int spll_pllout_req : 1; - unsigned int spll_pllout_state : 2; - unsigned int spll_postdiv_ovrd : 4; - unsigned int spll_postdiv_pllout_ovrd : 4; - unsigned int spll_postdiv_sync_enable : 1; - unsigned int : 1; - unsigned int spll_pwr_state : 2; - unsigned int : 1; - unsigned int spll_refclk_rate : 8; - } bitfields, bits; - uint32_t u32all; - int32_t i32all; - float f32all; -} clk7_spll_field_7_t; - -typedef union clk7_spll_field_4 { - struct { - unsigned int spll_fcw0_frac_ovrd : 16; - unsigned int pll_out_sel : 1; - unsigned int : 3; - unsigned int pll_pwr_dn_state : 2; - unsigned int : 2; - unsigned int spll_refclk_div : 2; - unsigned int : 6; - } bitfields, bits; - uint32_t u32all; - int32_t i32all; - float f32all; -} clk7_spll_field_4_t; - -typedef union clk7_spll_field_5nm_bus_ctrl { - struct { - unsigned int bus_spll_async_mode :1; - unsigned int bus_spll_apb_mode :1; - unsigned int bus_spll_addr :8; - unsigned int bus_spll_byte_en :4; - unsigned int bus_spll_rdtr :1; - unsigned int bus_spll_resetb :1; - unsigned int bus_spll_sel :1; - unsigned int bus_spll_wrtr :1; - unsigned int :14; - } bitfields, bits; - uint32_t u32all; - int32_t i32all; - float f32all; -} clk7_spll_field_5nm_bus_ctrl_t; - -typedef union clk7_spll_field_5nm_bus_wdata { - struct { - unsigned int bus_spll_wr_data; - } bitfields, bits; - uint32_t u32all; - int32_t i32all; - float f32all; -} clk7_spll_field_5nm_bus_wdata_t; - -typedef union clk7_rootrefclk_mux_1 { - struct { - unsigned int ROOTREFCLK_MUX_1 : 1; - unsigned int reserved : 31; - } bitfields, bits; - uint32_t u32all; - int32_t i32all; - float f32all; -} clk7_rootrefclk_mux_1_t; - -typedef union clk7_spll_field_5nm_bus_status { - struct { - unsigned int spll_bus_error :1; - unsigned int spll_bus_rd_valid :1; - unsigned int spll_bus_wr_ack :1; - unsigned int :29; - } bitfields, bits; - uint32_t u32all; - int32_t i32all; - float f32all; -} clk7_spll_field_5nm_bus_status_t; - -#endif diff --git a/src/platform/amd/acp_7_0/include/platform/lib/memory.h b/src/platform/amd/acp_7_0/include/platform/lib/memory.h index abf87a21e360..de506811721d 100644 --- a/src/platform/amd/acp_7_0/include/platform/lib/memory.h +++ b/src/platform/amd/acp_7_0/include/platform/lib/memory.h @@ -1,8 +1,10 @@ /* SPDX-License-Identifier: BSD-3-Clause * - * Copyright(c) 2024 AMD.All rights reserved. + * Copyright(c) 2024, 2026 AMD.All rights reserved. * * Author: SaiSurya, Ch <saisurya.chakkaveeravenkatanaga@amd.com> + * Basavaraj Hiregoudar <basavaraj.hiregoudar@amd.com> + * Sivasubramanian <sravisar@amd.com> */ #ifdef __SOF_LIB_MEMORY_H__ @@ -10,7 +12,9 @@ #define __PLATFORM_LIB_MEMORY_H__ #include <rtos/cache.h> -#include <platform/chip_offset_byte.h> + +#define PU_REGISTER_BASE (0x9FD00000 - 0x01240000) +#define PU_SCRATCH_REG_BASE (0x9FF00000 - 0x01250000) /* data cache line alignment */ #define PLATFORM_DCACHE_ALIGN 128 @@ -182,7 +186,7 @@ static inline void *platform_rfree_prepare(void *ptr) return ptr; } #endif - +#define host_to_local(addr) addr #endif /* __PLATFORM_LIB_MEMORY_H__ */ #else diff --git a/src/platform/amd/acp_7_0/include/platform/platform.h b/src/platform/amd/acp_7_0/include/platform/platform.h index 3e927ace6687..5c2e7db0549e 100644 --- a/src/platform/amd/acp_7_0/include/platform/platform.h +++ b/src/platform/amd/acp_7_0/include/platform/platform.h @@ -1,8 +1,10 @@ /* SPDX-License-Identifier: BSD-3-Clause * - * Copyright(c) 2024 AMD.All rights reserved. + * Copyright(c) 2024, 2026 AMD.All rights reserved. * - * Author: SaiSurya, Ch <saisurya.chakkaveeravenkatanaga@amd.com> + * Author: SaiSurya, Ch <saisurya.chakkaveeravenkatanaga@amd.com> + * Basavaraj Hiregoudar <basavaraj.hiregoudar@amd.com> + * Sivasubramanian <sravisar@amd.com> */ #ifdef __SOF_PLATFORM_H__ @@ -17,9 +19,9 @@ #include <stddef.h> #include <stdint.h> #include <platform/fw_scratch_mem.h> -#include <platform/chip_registers.h> +#include <platform/platform_misc.h> -struct ll_schedule_domain; +#define INTERRUPT_ENABLE 1 struct timer; #define PLATFORM_DEFAULT_CLOCK CLK_CPU(0) diff --git a/src/platform/amd/acp_7_0/include/platform/platform_misc.h b/src/platform/amd/acp_7_0/include/platform/platform_misc.h new file mode 100644 index 000000000000..2338a91317e0 --- /dev/null +++ b/src/platform/amd/acp_7_0/include/platform/platform_misc.h @@ -0,0 +1,445 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * + * Copyright(c) 2026 AMD. All rights reserved. + * + * + */ + +//Author: Basavaraj Hiregoudar <basavaraj.hiregoudar@amd.com> +// Sivasubramanian <sravisar@amd.com> + +#define ACP_SW_INTR_TRIG 0x1241810 +#define ACP_DSP0_INTR_CNTL 0x1241800 +#define ACP_DSP0_INTR_STAT 0x1241804 +#define ACP_DSP_SW_INTR_CNTL 0x1241808 +#define ACP_DSP_SW_INTR_STAT 0x124180C +#define ACP_AXI2DAGB_SEM_0 0x1241874 +#define ACP_SRBM_CLIENT_BASE_ADDR 0x12419EC +#define ACP_SRBM_CLIENT_RDDATA 0x12419F0 +#define ACP_SRBM_CYCLE_STS 0x12419F4 +#define ACP_SRBM_CLIENT_CONFIG 0x12419F8 +#define MP1_SMN_C2PMSG_69 0x58A14 +#define MP1_SMN_C2PMSG_85 0x58A54 +#define MP1_SMN_C2PMSG_93 0x58A74 +#define CLK7_ROOTREFCLK_MUX_1 0x6C0C8 +#define CLK7_CLK_PLL_REFCLK_RATE_STARTUP 0x6C0D0 +#define CLK7_CLK_PLL_REQ 0x6C0DC +#define CLK7_CLK1_DFS_CNTL 0x6C1B0 +#define CLK7_CLK1_CURRENT_CNT 0x6C378 +#define CLK7_CLK0_DFS_CNTL 0x6C1A4 +#define CLK7_CLK0_CURRENT_CNT 0x6C374 +#define CLK7_CLK0_BYPASS_CNTL 0x6C210 +#define CLK7_CLK1_BYPASS_CNTL 0x6C234 +#define CLK7_CLK0_DFS_STATUS 0x6C1AC +#define CLK7_CLK1_DFS_STATUS 0x6C1B8 +#define CLK7_SPLL_FIELD_2 0x6C114 +#define CLK7_CLK2_CURRENT_CNT 0x6C37C +#define CLK7_CLK2_BYPASS_CNTL 0x6C258 +#define CLK7_CLK2_DFS_STATUS 0x6C1C4 +#define CLK7_CLK_PLL_PWR_REQ 0x6C2F0 +#define CLK7_CLK_DFSBYPASS_CONTROL 0x6C2F8 +#define CLK7_CLK_FSM_STATUS 0x6C304 +#define CLK7_SPLL_FUSE_1 0x6C0F8 +#define CLK7_SPLL_FUSE_2 0x6C0FC +#define CLK7_SPLL_FIELD_7 0x6C128 +#define CLK7_SPLL_FIELD_9 0x6C130 +#define CLK7_SPLL_FIELD_6nm 0x6C138 +#define CLK7_SPLL_FIELD_4 0x6C11C +#define CLK7_SPLL_FIELD_5nm_BUS_CTRL 0x6C140 +#define CLK7_SPLL_FIELD_5nm_BUS_WDATA 0x6C144 +#define CLK7_SPLL_FIELD_5nm_BUS_STATUS 0x6C148 +#define CLK7_CLK_PLL_RESET_STOP_TIMER 0x6C180 + +typedef union acp_srbm_cycle_sts { + struct { + unsigned int srbm_clients_sts :1; + unsigned int :7; + } bits; + unsigned int u32all; +} acp_srbm_cycle_sts_t; + +typedef union acp_clkmux_sel { + struct { + unsigned int acp_clkmux_sel : 3; + unsigned int : 13; + unsigned int acp_clkmux_div_value : 16; + } bits; + unsigned int u32all; +} acp_clkmux_sel_t; + +typedef union clk7_clk1_dfs_cntl_u { + struct { + unsigned int CLK1_DIVIDER : 7; + unsigned int : 25; + } bitfields, bits; + uint32_t u32all; + int32_t i32all; + float f32all; +} clk7_clk1_dfs_cntl_u_t; + +typedef union clk7_clk1_dfs_status_u { + struct { + unsigned int : 16; + unsigned int CLK1_DFS_DIV_REQ_IDLE : 1; + unsigned int : 2; + unsigned int RO_CLK1_DFS_STATE_IDLE : 1; + unsigned int CLK1_CURRENT_DFS_DID : 7; + unsigned int : 5; + } bitfields, bits; + uint32_t u32all; + int32_t i32all; + float f32all; +} clk7_clk1_dfs_status_u_t; + +typedef union clk7_clk1_bypass_cntl_u { + struct { + unsigned int CLK1_BYPASS_SEL : 3; + unsigned int : 13; + unsigned int CLK1_BYPASS_DIV : 4; + unsigned int : 12; + } bitfields, bits; + uint32_t u32all; + int32_t i32all; + float f32all; +} clk7_clk1_bypass_cntl_u_t; + +typedef union clk7_clk_fsm_status_u { + struct { + unsigned int AUTOLAUCH_FSM_FULL_SPEED_IDLE : 1; + unsigned int : 3; + unsigned int AUTOLAUCH_FSM_BYPASS_IDLE : 1; + unsigned int : 3; + unsigned int RO_FSM_PLL_STATUS_STARTED : 1; + unsigned int : 3; + unsigned int RO_FSM_PLL_STATUS_STOPPED : 1; + unsigned int : 3; + unsigned int RO_EARLY_FSM_DONE : 1; + unsigned int : 3; + unsigned int RO_DFS_GAP_ACTIVE : 1; + unsigned int : 3; + unsigned int RO_DID_FSM_IDLE : 1; + unsigned int : 7; + } bitfields, bits; + uint32_t u32all; + int32_t i32all; + float f32all; +} clk7_clk_fsm_status_t; + +typedef union clk7_clk_pll_req_u { + struct { + unsigned int fbmult_int : 9; + unsigned int : 3; + unsigned int pllspinediv : 4; + unsigned int fbmult_frac : 16; + } bitfields, bits; + uint32_t u32all; + int32_t i32all; + float f32all; +} clk7_clk_pll_req_u_t; + +typedef union clk7_clk_pll_refclk_startup { + struct { + unsigned int main_pll_ref_clk_rate_startup : 8; + unsigned int main_pll_cfg_4_startup : 8; + unsigned int main_pll_ref_clk_div_startup : 2; + unsigned int main_pll_cfg_3_startup : 10; + unsigned int : 1; + unsigned int main_pll_refclk_src_mux0_startup : 1; + unsigned int main_pll_refclk_src_mux1_startup : 1; + unsigned int : 1; + } bitfields, bits; + uint32_t u32all; + int32_t i32all; + float f32all; +} clk7_clk_pll_refclk_startup_t; + +typedef union clk7_spll_field_2 { + struct{ + unsigned int : 3; + unsigned int spll_fbdiv_mask_en : 1; + unsigned int spll_fracn_en : 1; + unsigned int spll_freq_jump_en : 1; + unsigned int : 25; + } bitfields, bits; + uint32_t u32all; + int32_t i32all; + float f32all; +} clk7_spll_field_2_t; + +typedef union clk7_clk_dfsbypass_cntl { + struct { + unsigned int enter_dfs_bypass_0 : 1; + unsigned int enter_dfs_bypass_1 : 1; + unsigned int : 14; + unsigned int exit_dfs_bypass_0 : 1; + unsigned int exit_dfs_bypass_1 : 1; + unsigned int : 14; + } bitfields, bits; + uint32_t u32all; + int32_t i32all; + float f32all; +} clk7_clk_dfsbypass_cntl_t; + +typedef union clk7_clk_pll_pwr_req { + struct { + unsigned int PLL_AUTO_START_REQ : 1; + unsigned int : 3; + unsigned int PLL_AUTO_STOP_REQ : 1; + unsigned int : 3; + unsigned int PLL_AUTO_STOP_NOCLK_REQ : 1; + unsigned int : 3; + unsigned int PLL_AUTO_STOP_REFBYPCLK_REQ : 1; + unsigned int : 3; + unsigned int PLL_FORCE_RESET_HIGH : 1; + unsigned int : 15; + } bitfields, bits; + uint32_t u32all; + int32_t i32all; + float f32all; +} clk7_clk_pll_pwr_req_t; + +typedef union clk7_spll_fuse_1 { + struct { + unsigned int : 8; + unsigned int spll_gp_coarse_exp : 4; + unsigned int spll_gp_coarse_mant : 4; + unsigned int : 4; + unsigned int spll_gi_coarse_exp : 4; + unsigned int : 1; + unsigned int spll_gi_coarse_mant : 2; + unsigned int : 5; + } bitfields, bits; + uint32_t u32all; + int32_t i32all; + float f32all; +} clk7_spll_fuse_1_t; + +typedef union clk7_spll_fuse_2 { + struct { + unsigned int spll_tdc_resolution : 8; + unsigned int spll_freq_offset_exp : 4; + unsigned int spll_freq_offset_mant : 5; + unsigned int : 15; + } bitfields, bits; + uint32_t u32all; + int32_t i32all; + float f32all; +} clk7_spll_fuse_2_t; + +typedef union clk7_spll_field_9 { + struct { + unsigned int : 16; + unsigned int spll_dpll_cfg_3 : 10; + unsigned int spll_fll_mode : 1; + unsigned int : 5; + } bitfields, bits; + uint32_t u32all; + int32_t i32all; + float f32all; +} clk7_spll_field_9_t; + +typedef union clk7_spll_field_6nm { + struct { + unsigned int spll_dpll_cfg_4 : 8; + unsigned int spll_reg_tim_exp : 3; + unsigned int spll_reg_tim_mant : 1; + unsigned int spll_ref_tim_exp : 3; + unsigned int spll_ref_tim_mant : 1; + unsigned int spll_vco_pre_div : 2; + unsigned int : 14; + } bitfields, bits; + uint32_t u32all; + int32_t i32all; + float f32all; +} clk7_spll_field_6nm_t; + +typedef union clk7_spll_field_7 { + struct { + unsigned int : 7; + unsigned int spll_pllout_sel : 1; + unsigned int spll_pllout_req : 1; + unsigned int spll_pllout_state : 2; + unsigned int spll_postdiv_ovrd : 4; + unsigned int spll_postdiv_pllout_ovrd : 4; + unsigned int spll_postdiv_sync_enable : 1; + unsigned int : 1; + unsigned int spll_pwr_state : 2; + unsigned int : 1; + unsigned int spll_refclk_rate : 8; + } bitfields, bits; + uint32_t u32all; + int32_t i32all; + float f32all; +} clk7_spll_field_7_t; + +typedef union clk7_spll_field_4 { + struct { + unsigned int spll_fcw0_frac_ovrd : 16; + unsigned int pll_out_sel : 1; + unsigned int : 3; + unsigned int pll_pwr_dn_state : 2; + unsigned int : 2; + unsigned int spll_refclk_div : 2; + unsigned int : 6; + } bitfields, bits; + uint32_t u32all; + int32_t i32all; + float f32all; +} clk7_spll_field_4_t; + +typedef union clk7_spll_field_5nm_bus_ctrl { + struct { + unsigned int bus_spll_async_mode :1; + unsigned int bus_spll_apb_mode :1; + unsigned int bus_spll_addr :8; + unsigned int bus_spll_byte_en :4; + unsigned int bus_spll_rdtr :1; + unsigned int bus_spll_resetb :1; + unsigned int bus_spll_sel :1; + unsigned int bus_spll_wrtr :1; + unsigned int :14; + } bitfields, bits; + uint32_t u32all; + int32_t i32all; + float f32all; +} clk7_spll_field_5nm_bus_ctrl_t; + +typedef union clk7_spll_field_5nm_bus_wdata { + struct { + unsigned int bus_spll_wr_data; + } bitfields, bits; + uint32_t u32all; + int32_t i32all; + float f32all; +} clk7_spll_field_5nm_bus_wdata_t; + +typedef union clk7_rootrefclk_mux_1 { + struct { + unsigned int ROOTREFCLK_MUX_1 : 1; + unsigned int reserved : 31; + } bitfields, bits; + uint32_t u32all; + int32_t i32all; + float f32all; +} clk7_rootrefclk_mux_1_t; + +typedef union clk7_spll_field_5nm_bus_status { + struct { + unsigned int spll_bus_error :1; + unsigned int spll_bus_rd_valid :1; + unsigned int spll_bus_wr_ack :1; + unsigned int :29; + } bitfields, bits; + uint32_t u32all; + int32_t i32all; + float f32all; +} clk7_spll_field_5nm_bus_status_t; + +typedef union acp_dsp0_sdw_intr_cntl { + struct { + unsigned int soundwire_mask :17; + unsigned int :15; + } bits; + unsigned int u32all; +} acp_dsp0_sdw_intr_cntl_t; + +typedef union acp_dsp_sw_intr_stat { + struct { + unsigned int host_to_dsp0_intr1_stat:1; + unsigned int host_to_dsp0_intr2_stat:1; + unsigned int dsp0_to_host_intr_stat:1; + unsigned int host_to_dsp0_intr3_stat:1; + unsigned int :28; + } bits; + unsigned int u32all; +} acp_dsp_sw_intr_stat_t; + +typedef union acp_sw_intr_trig { + struct { + unsigned int trig_host_to_dsp0_intr1:1; + unsigned int :1; + unsigned int trig_dsp0_to_host_intr:1; + unsigned int :29; + } bits; + unsigned int u32all; +} acp_sw_intr_trig_t; + +typedef void (*dma_callback_t)(const struct device *dev, void *user_data, + uint32_t channel, int status); + +#define ACP_DMA_CHAN_COUNT 12 +struct acp_dma_ptr_data { + /* base address of dma buffer */ + uint32_t base; + /* size of dma buffer */ + uint32_t size; + /* write pointer of dma buffer */ + uint32_t wr_ptr; + /* read pointer of dma buffer */ + uint32_t rd_ptr; + /* read size of dma buffer */ + uint32_t rd_size; + /* write size of dma buffer */ + uint32_t wr_size; + /* system memory size defined for the stream */ + uint32_t sys_buff_size; + /* virtual system memory offset for system memory buffer */ + uint32_t phy_off; + /* probe_channel id */ + uint32_t probe_channel; +}; + +enum acp_dma_state { + ACP_DMA_READY, + ACP_DMA_PREPARED, + ACP_DMA_SUSPENDED, + ACP_DMA_ACTIVE, +}; + +struct acp_dma_chan_data { + uint32_t direction; + enum acp_dma_state state; + struct acp_dma_ptr_data ptr_data; /* pointer data */ + dma_callback_t dma_tfrcallback; + void *priv_data;//unused +}; + +struct dma_context1 { + /** magic code to identify the context */ + int32_t magic; + /** number of dma channels */ + int dma_channels; + /** atomic holding bit flags for each channel to mark as used/unused */ + atomic_t *atomic; +}; + +struct sdw_pin_data { + uint32_t pin_num; + uint32_t pin_dir; + uint32_t dma_channel; + uint32_t index; + uint32_t instance; +}; + +struct tdm_context { + uint32_t tdm_instance; + uint32_t pin_dir; + uint32_t dma_channel; + uint32_t index; +}; + +struct dmic_context { + uint32_t dmic_instance; + uint32_t pin_dir; + uint32_t dma_channel; + uint32_t index; +}; + +struct acp_dma_dev_data { + struct dma_context1 dma_ctx; + struct acp_dma_chan_data chan_data[ACP_DMA_CHAN_COUNT]; + + ATOMIC_DEFINE(atomic, ACP_DMA_CHAN_COUNT); + struct dma_config *dma_config; + void *dai_index_ptr; +}; diff --git a/src/platform/amd/acp_7_0/lib/clk.c b/src/platform/amd/acp_7_0/lib/clk.c index 89fa787db1c3..da2b069682d0 100644 --- a/src/platform/amd/acp_7_0/lib/clk.c +++ b/src/platform/amd/acp_7_0/lib/clk.c @@ -1,8 +1,10 @@ // SPDX-License-Identifier: BSD-3-Clause // -// Copyright(c) 2024 AMD.All rights reserved. +// Copyright(c) 2024, 2026 AMD.All rights reserved. // // Author: SaiSurya, Ch <saisurya.chakkaveeravenkatanaga@amd.com> +// Basavaraj Hiregoudar <basavaraj.hiregoudar@amd.com> +// Sivasubramanian <sravisar@amd.com> // #include <sof/common.h> @@ -36,8 +38,8 @@ #include <stddef.h> #include <stdint.h> #include <platform/fw_scratch_mem.h> -#include <platform/chip_registers.h> +LOG_MODULE_REGISTER(acp_clk, CONFIG_SOF_LOG_LEVEL); SOF_DEFINE_REG_UUID(acp_clk); DECLARE_TR_CTX(acp_clk_tr, SOF_UUID(acp_clk_uuid), LOG_LEVEL_INFO); @@ -183,7 +185,7 @@ void acp_change_clock_notify(uint32_t clock_freq) acp_7_0_get_boot_ref_clock(&boot_ref_clk); - tr_info(&acp_clk_tr, "acp_change_clock_notify clock_freq : %d clock_type : %d", + tr_info(&acp_clk_tr, "clock_freq : %d clock_type : %d", clock_freq, clock_type); fraction_val = (float)(clock_freq / 1000000.0f); @@ -204,7 +206,7 @@ void acp_change_clock_notify(uint32_t clock_freq) bypass_cntl.bitfields.CLK1_BYPASS_DIV = 0xF; } else { did = boot_ref_clk / fraction_val; - tr_info(&acp_clk_tr, "acp_change_clock_notify CLK Divider : %d boot_ref_clk : %d\n", + tr_info(&acp_clk_tr, "CLK Divider : %d boot_ref_clk : %d\n", (uint32_t)(did * 100), (uint32_t)boot_ref_clk); if (did > 62.0f) { @@ -241,7 +243,7 @@ void acp_change_clock_notify(uint32_t clock_freq) do { dfs_status.u32all = acp_reg_read_via_smn(CLK7_CLK1_DFS_STATUS, sizeof(int)); - tr_info(&acp_clk_tr, "acp_change_clock_notify ACLK1 CLK1_DIVIDER : %d dfsstatus %d ", + tr_info(&acp_clk_tr, "ACLK1 CLK1_DIVIDER : %d dfsstatus %d ", dfs_cntl.u32all, dfs_status.u32all); } while (dfs_status.bitfields.CLK1_DFS_DIV_REQ_IDLE == 0); updated_clk = acp_reg_read_via_smn(CLK7_CLK1_CURRENT_CNT, sizeof(int)); @@ -258,7 +260,7 @@ void acp_change_clock_notify(uint32_t clock_freq) dfs_cntl.u32all = acp_reg_read_via_smn(CLK7_CLK1_DFS_CNTL, sizeof(int)); - tr_info(&acp_clk_tr, "acp_change_clock_notify ACLK2 CLK1_DIVIDER:%d dfsstatus %d ", + tr_info(&acp_clk_tr, "ACLK2 CLK1_DIVIDER:%d dfsstatus %d ", dfs_cntl.u32all, dfs_status.u32all); } while (dfs_status.bitfields.CLK1_DFS_DIV_REQ_IDLE == 0); } @@ -272,7 +274,7 @@ void acp_change_clock_notify(uint32_t clock_freq) do { dfs_status.u32all = acp_reg_read_via_smn(CLK7_CLK0_DFS_STATUS, sizeof(int)); - tr_info(&acp_clk_tr, "acp_change_clock_notify SCLK CLK1_DIVIDER: %d", + tr_info(&acp_clk_tr, "SCLK CLK1_DIVIDER: %d", dfs_cntl.u32all); } while (dfs_status.bitfields.CLK1_DFS_DIV_REQ_IDLE == 0); diff --git a/src/platform/amd/acp_7_0/lib/dai.c b/src/platform/amd/acp_7_0/lib/dai.c index 1879c041e24c..87c3f8f62a53 100644 --- a/src/platform/amd/acp_7_0/lib/dai.c +++ b/src/platform/amd/acp_7_0/lib/dai.c @@ -1,8 +1,10 @@ // SPDX-License-Identifier: BSD-3-Clause // -//Copyright(c) 2024 AMD. All rights reserved. +//Copyright(c) 2024, 2026 AMD. All rights reserved. // //Author: SaiSurya, Ch <saisurya.chakkaveeravenkatanaga@amd.com> +// Basavaraj Hiregoudar <basavaraj.hiregoudar@amd.com> +// Sivasubramanian <sravisar@amd.com> #include <sof/common.h> #include <sof/drivers/acp_dai_dma.h> @@ -299,7 +301,7 @@ const struct dai_type_info dti[] = { }, #endif { - .type = SOF_DAI_AMD_SW_AUDIO, + .type = SOF_DAI_AMD_SDW, .dai_array = swaudiodai, .num_dais = ARRAY_SIZE(swaudiodai) }, diff --git a/src/platform/amd/acp_7_0/platform.c b/src/platform/amd/acp_7_0/platform.c index 0111edd8a191..72162bfd68f4 100644 --- a/src/platform/amd/acp_7_0/platform.c +++ b/src/platform/amd/acp_7_0/platform.c @@ -1,8 +1,10 @@ // SPDX-License-Identifier: BSD-3-Clause // -//Copyright(c) 2024 AMD. All rights reserved. +//Copyright(c) 2024, 2026 AMD. All rights reserved. // //Author: SaiSurya, Ch <saisurya.chakkaveeravenkatanaga@amd.com> +// Basavaraj Hiregoudar <basavaraj.hiregoudar@amd.com> +// Sivasubramanian <sravisar@amd.com> #include <sof/compiler_info.h> #include <sof/debug/debug.h> @@ -33,7 +35,12 @@ #include <sof_versions.h> #include <errno.h> #include <stdint.h> -#include <platform/chip_offset_byte.h> +#include <zephyr/logging/log.h> + +LOG_MODULE_REGISTER(platform_file, CONFIG_SOF_LOG_LEVEL); + +#define INTERRUPT_DISABLE 0 +extern void acp_dsp_to_host_intr_trig(void); struct sof; static const struct sof_ipc_fw_ready ready @@ -48,11 +55,14 @@ static const struct sof_ipc_fw_ready ready .micro = SOF_MICRO, .minor = SOF_MINOR, .major = SOF_MAJOR, -#ifdef DEBUG_BUILD - /* only added in debug for reproducibility in releases */ - .build = SOF_BUILD, +#if BLD_COUNTERS + .build = SOF_BUILD, /* See version-build-counter.cmake */ .date = __DATE__, .time = __TIME__, +#else + .build = -1, + .date = "dtermin.\0", + .time = "fwready.\0", #endif .tag = SOF_TAG, .abi_version = SOF_ABI_VERSION, @@ -126,62 +136,34 @@ const struct ext_man_windows xsram_window }, }; -static SHARED_DATA struct timer timer = { - .id = TIMER0, - .irq = IRQ_NUM_TIMER0, -}; - int platform_init(struct sof *sof) { int ret; - sof->platform_timer = &timer; - sof->cpu_timers = &timer; /* to view system memory */ - interrupt_init(sof); platform_interrupt_init(); platform_clock_init(sof); scheduler_init_edf(); /* init low latency domains and schedulers */ /* CONFIG_SYSTICK_PERIOD set as PLATFORM_DEFAULT_CLOCK */ - sof->platform_timer_domain = - timer_domain_init(sof->platform_timer, PLATFORM_DEFAULT_CLOCK); - scheduler_init_ll(sof->platform_timer_domain); - platform_timer_start(sof->platform_timer); + sof->platform_timer_domain = zephyr_domain_init(PLATFORM_DEFAULT_CLOCK); + zephyr_ll_scheduler_init(sof->platform_timer_domain); + /*CONFIG_SYSTICK_PERIOD hardcoded as 200000*/ sa_init(sof, 200000); clock_set_freq(CLK_CPU(cpu_get_id()), CLK_MAX_CPU_HZ); /* init DMA */ - ret = acp_dma_init(sof); + ret = dmac_init(sof); if (ret < 0) return -ENODEV; - /* Init DMA platform domain */ - sof->platform_dma_domain = - dma_multi_chan_domain_init(&sof->dma_info->dma_array[0], - sizeof(sof->dma_info->dma_array), - PLATFORM_DEFAULT_CLOCK, true); - sof->platform_dma_domain->full_sync = true; - scheduler_init_ll(sof->platform_dma_domain); + /* initialize the host IPC mechanisms */ ipc_init(sof); /* initialize the DAI mechanisms */ ret = dai_init(sof); if (ret < 0) return -ENODEV; -#if CONFIG_TRACE - /* Initialize DMA for Trace*/ - trace_point(TRACE_BOOT_PLATFORM_DMA_TRACE); - sof->dmat->config.elem_array.elems = - rzalloc(SOF_MEM_FLAG_KERNEL, - sizeof(struct dma_sg_elem) * 1); - sof->dmat->config.elem_array.count = 1; - sof->dmat->config.elem_array.elems->dest = 0x03800000; - sof->dmat->config.elem_array.elems->size = 65536; - sof->dmat->config.scatter = 0; - dma_trace_init_complete(sof->dmat); -#endif - /* show heap status */ - heap_trace_all(1); + return 0; } @@ -209,8 +191,3 @@ int platform_context_save(struct sof *sof) { return 0; } - -void platform_wait_for_interrupt(int level) -{ - arch_wait_for_interrupt(level); -} diff --git a/src/platform/amd/common/include/platform/ipc.h b/src/platform/amd/common/include/platform/ipc.h index 26c392776662..a715adde1254 100644 --- a/src/platform/amd/common/include/platform/ipc.h +++ b/src/platform/amd/common/include/platform/ipc.h @@ -1,9 +1,10 @@ /* SPDX-License-Identifier: BSD-3-Clause * - * Copyright(c) 2023 AMD. All rights reserved. + * Copyright(c) 2023, 2026 AMD. All rights reserved. * *Author: Basavaraj Hiregoudar <basavaraj.hiregoudar@amd.com> * SaiSurya, Ch <saisurya.chakkaveeravenkatanaga@amd.com> + * Sivasubramanian <sravisar@amd.com> */ #ifndef _COMMON_IPC_HEADER #define _COMMON_IPC_HEADER @@ -11,6 +12,7 @@ #include <stdint.h> #include <platform/platform.h> +#define IRQ_NUM_EXT_LEVEL3 3 uint32_t sof_ipc_host_status(void); uint32_t sof_ipc_host_ack_flag(void); void sof_ipc_host_ack_clear(void); diff --git a/src/platform/amd/rembrandt/include/arch/xtensa/config/tie-asm.h b/src/platform/amd/rembrandt/include/arch/xtensa/config/tie-asm.h index 9ec7b495afd9..9ac7ec55993d 100644 --- a/src/platform/amd/rembrandt/include/arch/xtensa/config/tie-asm.h +++ b/src/platform/amd/rembrandt/include/arch/xtensa/config/tie-asm.h @@ -166,7 +166,7 @@ .ifeq (XTHAL_SAS_TIE | XTHAL_SAS_NOCC | XTHAL_SAS_CALR) & ~(\select) xchal_sa_align \ptr, 0, 0, 16, 16 ae_s64.i aed0, \ptr, .Lxchal_ofs_+56 - addi.a \ptr, \ptr, 64 + addi \ptr, \ptr, 64 ae_s64.i aed1, \ptr, .Lxchal_ofs_+0 ae_s64.i aed2, \ptr, .Lxchal_ofs_+8 ae_s64.i aed3, \ptr, .Lxchal_ofs_+16 @@ -175,7 +175,7 @@ ae_s64.i aed6, \ptr, .Lxchal_ofs_+40 ae_s64.i aed7, \ptr, .Lxchal_ofs_+48 ae_s64.i aed8, \ptr, .Lxchal_ofs_+56 - addi.a \ptr, \ptr, 64 + addi \ptr, \ptr, 64 ae_s64.i aed9, \ptr, .Lxchal_ofs_+0 ae_s64.i aed10, \ptr, .Lxchal_ofs_+8 ae_s64.i aed11, \ptr, .Lxchal_ofs_+16 @@ -184,7 +184,7 @@ ae_s64.i aed14, \ptr, .Lxchal_ofs_+40 ae_s64.i aed15, \ptr, .Lxchal_ofs_+48 ae_s64.i aed16, \ptr, .Lxchal_ofs_+56 - addi.a \ptr, \ptr, 64 + addi \ptr, \ptr, 64 ae_s64.i aed17, \ptr, .Lxchal_ofs_+0 ae_s64.i aed18, \ptr, .Lxchal_ofs_+8 ae_s64.i aed19, \ptr, .Lxchal_ofs_+16 @@ -193,7 +193,7 @@ ae_s64.i aed22, \ptr, .Lxchal_ofs_+40 ae_s64.i aed23, \ptr, .Lxchal_ofs_+48 ae_s64.i aed24, \ptr, .Lxchal_ofs_+56 - addi.a \ptr, \ptr, 64 + addi \ptr, \ptr, 64 ae_s64.i aed25, \ptr, .Lxchal_ofs_+0 ae_s64.i aed26, \ptr, .Lxchal_ofs_+8 ae_s64.i aed27, \ptr, .Lxchal_ofs_+16 @@ -209,12 +209,12 @@ s8i \at1, \ptr, .Lxchal_ofs_+58 ae_movae \at1, aep3 s8i \at1, \ptr, .Lxchal_ofs_+59 - addi.a \ptr, \ptr, 64 + addi \ptr, \ptr, 64 ae_salign128.i u0, \ptr, .Lxchal_ofs_+0 ae_salign128.i u1, \ptr, .Lxchal_ofs_+16 ae_salign128.i u2, \ptr, .Lxchal_ofs_+32 ae_salign128.i u3, \ptr, .Lxchal_ofs_+48 - addi.a \ptr, \ptr, -320 + addi \ptr, \ptr, -320 ae_movdrzbvc aed0 // ureg AE_ZBIASV8C ae_s64.i aed0, \ptr, .Lxchal_ofs_+0 + 0 ae_movvfcrfsr aed0 // ureg FCR_FSR @@ -286,7 +286,7 @@ l32i \at1, \ptr, .Lxchal_ofs_+52 wur.ae_cend2 \at1 // ureg 251 ae_l64.i aed0, \ptr, .Lxchal_ofs_+56 - addi.a \ptr, \ptr, 64 + addi \ptr, \ptr, 64 ae_l64.i aed1, \ptr, .Lxchal_ofs_+0 ae_l64.i aed2, \ptr, .Lxchal_ofs_+8 ae_l64.i aed3, \ptr, .Lxchal_ofs_+16 @@ -295,7 +295,7 @@ ae_l64.i aed6, \ptr, .Lxchal_ofs_+40 ae_l64.i aed7, \ptr, .Lxchal_ofs_+48 ae_l64.i aed8, \ptr, .Lxchal_ofs_+56 - addi.a \ptr, \ptr, 64 + addi \ptr, \ptr, 64 ae_l64.i aed9, \ptr, .Lxchal_ofs_+0 ae_l64.i aed10, \ptr, .Lxchal_ofs_+8 ae_l64.i aed11, \ptr, .Lxchal_ofs_+16 @@ -304,7 +304,7 @@ ae_l64.i aed14, \ptr, .Lxchal_ofs_+40 ae_l64.i aed15, \ptr, .Lxchal_ofs_+48 ae_l64.i aed16, \ptr, .Lxchal_ofs_+56 - addi.a \ptr, \ptr, 64 + addi \ptr, \ptr, 64 ae_l64.i aed17, \ptr, .Lxchal_ofs_+0 ae_l64.i aed18, \ptr, .Lxchal_ofs_+8 ae_l64.i aed19, \ptr, .Lxchal_ofs_+16 @@ -313,7 +313,7 @@ ae_l64.i aed22, \ptr, .Lxchal_ofs_+40 ae_l64.i aed23, \ptr, .Lxchal_ofs_+48 ae_l64.i aed24, \ptr, .Lxchal_ofs_+56 - addi.a \ptr, \ptr, 64 + addi \ptr, \ptr, 64 ae_l64.i aed25, \ptr, .Lxchal_ofs_+0 ae_l64.i aed26, \ptr, .Lxchal_ofs_+8 ae_l64.i aed27, \ptr, .Lxchal_ofs_+16 @@ -321,7 +321,7 @@ ae_l64.i aed29, \ptr, .Lxchal_ofs_+32 ae_l64.i aed30, \ptr, .Lxchal_ofs_+40 ae_l64.i aed31, \ptr, .Lxchal_ofs_+48 - addi.a \ptr, \ptr, 56 + addi \ptr, \ptr, 56 l8ui \at1, \ptr, .Lxchal_ofs_+0 ae_movea aep0, \at1 l8ui \at1, \ptr, .Lxchal_ofs_+1 @@ -330,7 +330,7 @@ ae_movea aep2, \at1 l8ui \at1, \ptr, .Lxchal_ofs_+3 ae_movea aep3, \at1 - addi.a \ptr, \ptr, 8 + addi \ptr, \ptr, 8 ae_lalign128.i u0, \ptr, .Lxchal_ofs_+0 ae_lalign128.i u1, \ptr, .Lxchal_ofs_+16 ae_lalign128.i u2, \ptr, .Lxchal_ofs_+32 diff --git a/src/platform/amd/rembrandt/lib/dma.c b/src/platform/amd/rembrandt/lib/dma.c index d739e77bad1b..66ddea8effe8 100644 --- a/src/platform/amd/rembrandt/lib/dma.c +++ b/src/platform/amd/rembrandt/lib/dma.c @@ -1,9 +1,10 @@ // SPDX-License-Identifier: BSD-3-Clause // -//Copyright(c) 2022 AMD. All rights reserved. +//Copyright(c) 2022, 2026 AMD. All rights reserved. // //Author: Basavaraj Hiregoudar <basavaraj.hiregoudar@amd.com> // Bala Kishore <balakishore.pati@amd.com> +// Sivasubramanian <sravisar@amd.com> #include <sof/common.h> #include <platform/fw_scratch_mem.h> @@ -41,8 +42,8 @@ SHARED_DATA struct dma dma[PLATFORM_NUM_DMACS] = { .plat_data = { .id = DMA_ID_DAI_DMIC, .dir = DMA_DIR_DEV_TO_MEM, - .devs = DMA_DEV_DMIC, - .caps = DMA_CAP_DMIC, + .devs = SOF_DMA_DEV_DMIC, + .caps = SOF_DMA_CAP_DMIC, .base = DMA0_BASE, .chan_size = DMA0_SIZE, .channels = 8, @@ -54,8 +55,8 @@ SHARED_DATA struct dma dma[PLATFORM_NUM_DMACS] = { .plat_data = { .id = DMA_ID_DAI_HS, .dir = DMA_DIR_DEV_TO_MEM | DMA_DIR_MEM_TO_DEV, - .devs = DMA_DEV_SP, - .caps = DMA_CAP_SP, + .devs = SOF_DMA_DEV_SP, + .caps = SOF_DMA_CAP_SP, .base = DMA0_BASE, .chan_size = DMA0_SIZE, .channels = 8, diff --git a/src/platform/imx8m_cm7/include/platform/lib/clk.h b/src/platform/imx8m_cm7/include/platform/lib/clk.h new file mode 100644 index 000000000000..379aade87252 --- /dev/null +++ b/src/platform/imx8m_cm7/include/platform/lib/clk.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * + * Copyright 2026 NXP + */ + +#ifdef __SOF_LIB_CLK_H__ + +#ifndef __PLATFORM_LIB_CLK_H__ +#define __PLATFORM_LIB_CLK_H__ + +#define CLK_MAX_CPU_HZ 800000000 +#define CPU_DEFAULT_IDX 0 +#define NUM_CPU_FREQ 1 +#define NUM_CLOCKS 1 + +struct sof; + +void platform_clock_init(struct sof *sof); + +#endif /* __PLATFORM_LIB_CLK_H__ */ + +#else + +#error "This file shouldn't be included from outside of sof/lib/clk.h" + +#endif /* __SOF_LIB_CLK_H__ */ diff --git a/src/platform/imx8m_cm7/include/platform/lib/dma.h b/src/platform/imx8m_cm7/include/platform/lib/dma.h new file mode 100644 index 000000000000..ad55e7f7fb89 --- /dev/null +++ b/src/platform/imx8m_cm7/include/platform/lib/dma.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * + * Copyright 2026 NXP + */ + +#ifdef __SOF_LIB_DMA_H__ + +#ifndef __PLATFORM_LIB_DMA_H__ +#define __PLATFORM_LIB_DMA_H__ + +/* TODO: remove me whenever possible */ + +#endif /* __PLATFORM_LIB_DMA_H__ */ + +#else + +#error "This file shouldn't be included from outside of sof/lib/dma.h" + +#endif /* __SOF_LIB_DMA_H__ */ diff --git a/src/platform/imx8m_cm7/include/platform/lib/mailbox.h b/src/platform/imx8m_cm7/include/platform/lib/mailbox.h new file mode 100644 index 000000000000..6099d36806f9 --- /dev/null +++ b/src/platform/imx8m_cm7/include/platform/lib/mailbox.h @@ -0,0 +1,49 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * + * Copyright 2026 NXP + */ + +#ifdef __SOF_LIB_MAILBOX_H__ + +#ifndef __PLATFORM_LIB_MAILBOX_H__ +#define __PLATFORM_LIB_MAILBOX_H__ + +/* The i.MX8MP CM7 mailbox region is organized like this: + * + * +---------------+-------------------------+ + * | Region name | Base address | Size | + * +---------------+---------------+---------+ + * | Outbox region | 0x82000000 | 0x1000 | + * +---------------+---------------+---------+ + * | Inbox region | 0x82001000 | 0x1000 | + * +---------------+---------------+---------+ + * | Stream region | 0x82002000 | 0x1000 | + * +---------------+---------------+---------+ + * + * IMPORTANT: all regions should be 32-byte aligned. + * This is required because cache maintenance might + * be performed on them. + */ + +/* outbox */ +#define MAILBOX_DSPBOX_SIZE 0x1000 +#define MAILBOX_DSPBOX_BASE 0x82000000 +#define MAILBOX_DSPBOX_OFFSET 0 + +/* inbox */ +#define MAILBOX_HOSTBOX_SIZE 0x1000 +#define MAILBOX_HOSTBOX_BASE 0x82001000 +#define MAILBOX_HOSTBOX_OFFSET (MAILBOX_DSPBOX_OFFSET + MAILBOX_DSPBOX_SIZE) + +/* stream */ +#define MAILBOX_STREAM_SIZE 0x1000 +#define MAILBOX_STREAM_BASE 0x82002000 +#define MAILBOX_STREAM_OFFSET (MAILBOX_HOSTBOX_OFFSET + MAILBOX_HOSTBOX_SIZE) + +#endif /* __PLATFORM_LIB_MAILBOX_H__ */ + +#else + +#error "This file shouldn't be included from outside of sof/lib/mailbox.h" + +#endif /* __SOF_LIB_MAILBOX_H__ */ diff --git a/src/platform/imx8m_cm7/include/platform/lib/memory.h b/src/platform/imx8m_cm7/include/platform/lib/memory.h new file mode 100644 index 000000000000..413e77f142e1 --- /dev/null +++ b/src/platform/imx8m_cm7/include/platform/lib/memory.h @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * + * Copyright 2026 NXP + */ + +#ifdef __SOF_LIB_MEMORY_H__ + +#ifndef __PLATFORM_LIB_MEMORY_H__ +#define __PLATFORM_LIB_MEMORY_H__ + +#include <rtos/cache.h> + +#define PLATFORM_DCACHE_ALIGN DCACHE_LINE_SIZE + +#define SHARED_DATA + +#define uncache_to_cache(address) address +#define cache_to_uncache(address) address +#define cache_to_uncache_init(address) address +#define is_uncached(address) 0 + +/* no address translation required */ +#define host_to_local(addr) (addr) +#define local_to_host(addr) (addr) + +#define HEAPMEM_SIZE 0x00010000 + +/* WAKEUP domain MU1 side B */ +#define MU_BASE 0x30AB0000UL + +static inline void *platform_shared_get(void *ptr, int bytes) +{ + return ptr; +} + +#endif /* __PLATFORM_LIB_MEMORY_H__ */ + +#else + +#error "This file shouldn't be included from outside of sof/lib/memory.h" + +#endif /* __SOF_LIB_MEMORY_H__*/ diff --git a/src/platform/imx8m_cm7/include/platform/platform.h b/src/platform/imx8m_cm7/include/platform/platform.h new file mode 100644 index 000000000000..7d65624accda --- /dev/null +++ b/src/platform/imx8m_cm7/include/platform/platform.h @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * + * Copyright 2026 NXP + */ + +#ifdef __SOF_PLATFORM_H__ + +#ifndef __PLATFORM_PLATFORM_H__ +#define __PLATFORM_PLATFORM_H__ + +/* refers to M7 core clock - one core, one clock */ +#define PLATFORM_DEFAULT_CLOCK 0 + +#define HOST_PAGE_SIZE 4096 + +#define PLATFORM_PAGE_TABLE_SIZE 256 + +/* TODO: generous (SOF is usually used with 2 channels at most on i.MX + * platforms) and (potentially) not true. Can be adjusted later on if + * need be. + */ +#define PLATFORM_MAX_CHANNELS 4 +/* TODO: same as PLATFORM_MAX_CHANNELS */ +#define PLATFORM_MAX_STREAMS 5 + +/* WAKEUP domain MU7 side B */ +#define PLATFORM_IPC_INTERRUPT 97 + +#endif /* __PLATFORM_PLATFORM_H__ */ + +#else + +#error "This file shouldn't be included from outside of sof/platform.h" + +#endif /* __SOF_PLATFORM_H__ */ diff --git a/src/platform/imx8m_cm7/include/platform/trace/trace.h b/src/platform/imx8m_cm7/include/platform/trace/trace.h new file mode 100644 index 000000000000..3209ad9dc0ae --- /dev/null +++ b/src/platform/imx8m_cm7/include/platform/trace/trace.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * + * Copyright 2026 NXP + */ + +#ifdef __SOF_TRACE_TRACE_H__ + +#ifndef __PLATFORM_TRACE_TRACE_H__ +#define __PLATFORM_TRACE_TRACE_H__ + +/* TODO: remove me whenever possible */ + +#endif /* __PLATFORM_TRACE_TRACE_H__ */ + +#else + +#error "This file shouldn't be included from outside of sof/trace/trace.h" + +#endif /* __SOF_TRACE_TRACE_H__ */ diff --git a/src/platform/imx8m_cm7/lib/clk.c b/src/platform/imx8m_cm7/lib/clk.c new file mode 100644 index 000000000000..9c2bcd70f63f --- /dev/null +++ b/src/platform/imx8m_cm7/lib/clk.c @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright 2026 NXP + */ + +#include <rtos/clk.h> +#include <sof/lib/notifier.h> + +static const struct freq_table platform_cpu_freq[] = { + { + .freq = CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC, + .ticks_per_msec = CONFIG_SYS_CLOCK_TICKS_PER_SEC / 1000, + }, +}; + +static struct clock_info platform_clocks_info[NUM_CLOCKS]; + +void platform_clock_init(struct sof *sof) +{ + int i; + + sof->clocks = platform_clocks_info; + + for (i = 0; i < CONFIG_CORE_COUNT; i++) { + sof->clocks[i] = (struct clock_info) { + .freqs_num = NUM_CPU_FREQ, + .freqs = platform_cpu_freq, + .default_freq_idx = CPU_DEFAULT_IDX, + .current_freq_idx = CPU_DEFAULT_IDX, + .notification_id = NOTIFIER_ID_CPU_FREQ, + .notification_mask = NOTIFIER_TARGET_CORE_MASK(i), + .set_freq = NULL, + }; + } +} diff --git a/src/platform/imx8m_cm7/linker/data-sections.ld b/src/platform/imx8m_cm7/linker/data-sections.ld new file mode 100644 index 000000000000..3df5d5526577 --- /dev/null +++ b/src/platform/imx8m_cm7/linker/data-sections.ld @@ -0,0 +1,5 @@ +SECTION_PROLOGUE(.fw_metadata,,) +{ + KEEP (*(*.fw_metadata)) + . = ALIGN(16); +} GROUP_DATA_LINK_IN(RAMABLE_REGION, ROMABLE_REGION) diff --git a/src/platform/imx8m_cm7/platform.c b/src/platform/imx8m_cm7/platform.c new file mode 100644 index 000000000000..2da512f3063d --- /dev/null +++ b/src/platform/imx8m_cm7/platform.c @@ -0,0 +1,111 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright 2026 NXP + */ + +#include <rtos/sof.h> +#include <rtos/clk.h> +#include <sof/platform.h> +#include <sof/schedule/edf_schedule.h> +#include <sof/schedule/ll_schedule_domain.h> +#include <sof/schedule/ll_schedule.h> +#include <sof/lib/dma.h> +#include <sof/lib/dai.h> +#include <sof/debug/debug.h> +#include <sof_versions.h> +#include <kernel/abi.h> +#include <kernel/ext_manifest.h> +#include <sof/drivers/mu.h> + +static const struct sof_ipc_fw_ready ready = { + .hdr = { + .cmd = SOF_IPC_FW_READY, + .size = sizeof(struct sof_ipc_fw_ready), + }, + .version = { + .hdr.size = sizeof(struct sof_ipc_fw_version), + .micro = SOF_MICRO, + .minor = SOF_MINOR, + .major = SOF_MAJOR, +#ifdef DEBUG_BUILD + .build = SOF_BUILD, + .date = __DATE__, + .time = __TIME__, +#endif + .tag = SOF_TAG, + .abi_version = SOF_ABI_VERSION, + .src_hash = SOF_SRC_HASH, + }, + .flags = DEBUG_SET_FW_READY_FLAGS, +}; + +const struct ext_man_windows windows + __aligned(EXT_MAN_ALIGN) __section(".fw_metadata") __unused = { + .hdr = { + .type = EXT_MAN_ELEM_WINDOW, + .elem_size = ALIGN_UP_COMPILE(sizeof(struct ext_man_windows), EXT_MAN_ALIGN), + }, + .window = { + .ext_hdr = { + .hdr.cmd = SOF_IPC_FW_READY, + .hdr.size = sizeof(struct sof_ipc_window), + .type = SOF_IPC_EXT_WINDOW, + }, + .num_windows = 3, + .window = { + { + .type = SOF_IPC_REGION_DOWNBOX, + .size = MAILBOX_HOSTBOX_SIZE, + .offset = MAILBOX_HOSTBOX_OFFSET, + }, + { + .type = SOF_IPC_REGION_UPBOX, + .size = MAILBOX_DSPBOX_SIZE, + .offset = MAILBOX_DSPBOX_OFFSET, + }, + { + .type = SOF_IPC_REGION_STREAM, + .size = MAILBOX_STREAM_SIZE, + .offset = MAILBOX_STREAM_OFFSET, + }, + }, + }, +}; + +int platform_boot_complete(uint32_t boot_message) +{ + mailbox_dspbox_write(0, &ready, sizeof(ready)); + + imx_mu_xcr_rmw(IMX_MU_VERSION, IMX_MU_GCR, + IMX_MU_xCR_GIRn(IMX_MU_VERSION, 1), 0); + + return 0; +} + +int platform_context_save(struct sof *sof) +{ + /* nothing to be done here */ + return 0; +} + +int platform_init(struct sof *sof) +{ + int ret; + + platform_clock_init(sof); + + scheduler_init_edf(); + + sof->platform_timer_domain = zephyr_domain_init(PLATFORM_DEFAULT_CLOCK); + zephyr_ll_scheduler_init(sof->platform_timer_domain); + + ret = dmac_init(sof); + if (ret < 0) + return ret; + + ipc_init(sof); + + dai_init(sof); + + return 0; +} diff --git a/src/platform/intel/ace/include/ace/lib/memory.h b/src/platform/intel/ace/include/ace/lib/memory.h index f021b18b00c9..357397b2affa 100644 --- a/src/platform/intel/ace/include/ace/lib/memory.h +++ b/src/platform/intel/ace/include/ace/lib/memory.h @@ -31,7 +31,7 @@ #define uncache_to_cache(address) sys_cache_cached_ptr_get(address) #define cache_to_uncache(address) sys_cache_uncached_ptr_get(address) -#define is_uncached(address) sys_cache_is_ptr_cached(address) +#define is_uncached(address) (!sys_cache_is_ptr_cached(address)) /** * \brief Returns pointer to the memory shared by multiple cores. diff --git a/src/platform/intel/cavs/platform.c b/src/platform/intel/cavs/platform.c index 366424bae1dd..8a1a7c59c3b5 100644 --- a/src/platform/intel/cavs/platform.c +++ b/src/platform/intel/cavs/platform.c @@ -107,9 +107,7 @@ int platform_boot_complete(uint32_t boot_message) return 0; } -static struct pm_notifier pm_state_notifier = { - .state_exit = cpu_notify_state_exit, -}; +static struct pm_notifier pm_state_notifier; /* Runs on the primary core only */ int platform_init(struct sof *sof) @@ -138,6 +136,7 @@ int platform_init(struct sof *sof) return ret; /* register power states exit notifiers */ + pm_state_notifier.state_exit = cpu_notify_state_exit; pm_notifier_register(&pm_state_notifier); /* initialize the host IPC mechanisms */ diff --git a/src/platform/library/lib/alloc.c b/src/platform/library/lib/alloc.c index ec7667b351be..953267ab5265 100644 --- a/src/platform/library/lib/alloc.c +++ b/src/platform/library/lib/alloc.c @@ -16,6 +16,11 @@ /* testbench mem alloc definition */ +void *rmalloc_align(uint32_t flags, size_t bytes, uint32_t alignment) +{ + return malloc(bytes); +} + void *rmalloc(uint32_t flags, size_t bytes) { return malloc(bytes); @@ -37,10 +42,15 @@ void *rballoc_align(uint32_t flags, size_t bytes, return malloc(bytes); } -void *rbrealloc_align(void *ptr, uint32_t flags, size_t bytes, - size_t old_bytes, uint32_t alignment) +void *sof_heap_alloc(struct k_heap *heap, uint32_t flags, size_t bytes, + size_t alignment) +{ + return malloc(bytes); +} + +void sof_heap_free(struct k_heap *heap, void *addr) { - return realloc(ptr, bytes); + free(addr); } void heap_trace(struct mm_heap *heap, int size) @@ -54,3 +64,13 @@ void heap_trace_all(int force) { heap_trace(NULL, 0); } + +struct k_heap *sof_sys_heap_get(void) +{ + return NULL; +} + +struct k_heap *sof_sys_user_heap_get(void) +{ + return NULL; +} diff --git a/src/platform/mt8186/lib/clk.c b/src/platform/mt8186/lib/clk.c index 555ab83c2a4f..033580a280bf 100644 --- a/src/platform/mt8186/lib/clk.c +++ b/src/platform/mt8186/lib/clk.c @@ -36,7 +36,7 @@ static SHARED_DATA struct clock_info platform_clocks_info[NUM_CLOCKS]; static void clk_dsppll_enable(uint32_t value) { - tr_dbg(&clkdrv_tr, "clk_dsppll_enable: %d\n", value); + tr_dbg(&clkdrv_tr, "%d\n", value); switch (value) { case ADSP_CLK_PLL_300M: @@ -60,7 +60,7 @@ static void clk_dsppll_enable(uint32_t value) static void clk_dsppll_disable(void) { - tr_dbg(&clkdrv_tr, "clk_dsppll_disable\n"); + tr_dbg(&clkdrv_tr, "entry"); io_reg_update_bits(MTK_ADSPPLL_CON0, MTK_PLL_BASE_EN, 0); wait_delay_us(1); diff --git a/src/platform/mt8188/lib/clk.c b/src/platform/mt8188/lib/clk.c index d11b9fe2317b..f39872fca845 100644 --- a/src/platform/mt8188/lib/clk.c +++ b/src/platform/mt8188/lib/clk.c @@ -36,7 +36,7 @@ static SHARED_DATA struct clock_info platform_clocks_info[NUM_CLOCKS]; static void clk_dsppll_enable(uint32_t value) { - tr_dbg(&clkdrv_tr, "clk_dsppll_enable %d\n", value); + tr_dbg(&clkdrv_tr, "%d\n", value); switch (value) { case ADSP_CLK_PLL_400M: @@ -60,7 +60,7 @@ static void clk_dsppll_enable(uint32_t value) static void clk_dsppll_disable(void) { - tr_dbg(&clkdrv_tr, "clk_dsppll_disable\n"); + tr_dbg(&clkdrv_tr, "entry"); io_reg_update_bits(MTK_ADSPPLL_CON0, MTK_PLL_EN, 0); wait_delay_us(1); diff --git a/src/platform/mt8195/lib/clk.c b/src/platform/mt8195/lib/clk.c index 3f69c5e2d8f1..1514b9d9307d 100644 --- a/src/platform/mt8195/lib/clk.c +++ b/src/platform/mt8195/lib/clk.c @@ -77,7 +77,7 @@ static inline int dsp_clk_value_convert(int value) static void clk_dsppll_enable(void) { - tr_dbg(&clkdrv_tr, "clk_dsppll_enable\n"); + tr_dbg(&clkdrv_tr, "entry"); io_reg_update_bits(AUDIODSP_CK_CG, 0x1 << RG_AUDIODSP_SW_CG, 0x0); clk_setl(DSPPLL_CON4, PLL_PWR_ON); @@ -91,7 +91,7 @@ static void clk_dsppll_enable(void) static void clk_dsppll_disable(void) { - tr_dbg(&clkdrv_tr, "clk_dsppll_disable\n"); + tr_dbg(&clkdrv_tr, "entry"); clk_clrl(DSPPLL_CON0, PLL_EN); wait_delay_us(1); @@ -144,7 +144,7 @@ static int clock_platform_set_cpu_freq(int clock, int freq_idx) if (adsp_clock == adsp_clk_req) return 0; - tr_info(&clkdrv_tr, "clock_platform_set_cpu_freq %d\n", adsp_clk_req); + tr_info(&clkdrv_tr, "%d\n", adsp_clk_req); /* convert res manager value to driver map */ clk_mux = dsp_clk_value_convert(freq_idx); diff --git a/src/platform/mt8365/lib/clk.c b/src/platform/mt8365/lib/clk.c index 80f95ea30708..8101d4fd0f2c 100644 --- a/src/platform/mt8365/lib/clk.c +++ b/src/platform/mt8365/lib/clk.c @@ -79,7 +79,7 @@ static inline int dsp_clk_value_convert(int value) static void clk_dsppll_enable(void) { - tr_dbg(&clkdrv_tr, "clk_dsppll_enable\n"); + tr_dbg(&clkdrv_tr, "entry"); clk_setl(DSPPLL_CON3, PLL_PWR_ON); wait_delay_us(1); @@ -93,7 +93,7 @@ static void clk_dsppll_enable(void) static void clk_dsppll_disable(void) { - tr_dbg(&clkdrv_tr, "clk_dsppll_disable\n"); + tr_dbg(&clkdrv_tr, "entry"); clk_clrl(DSPPLL_CON0, PLL_BASE_EN); wait_delay_us(1); diff --git a/src/platform/mtk/dai.c b/src/platform/mtk/dai.c index cb6ebc144624..17fbc665dfbe 100644 --- a/src/platform/mtk/dai.c +++ b/src/platform/mtk/dai.c @@ -22,6 +22,9 @@ #elif defined(CONFIG_SOC_MT8196) #define MTK_AFE_BASE 0x1a110000 #define SRAM_CPU_START 0x1a210000 +#elif defined(CONFIG_SOC_MT8365) +#define MTK_AFE_BASE 0x11220000 +#define SRAM_CPU_START 0x1e000000 #else #error Unrecognized device #endif @@ -133,7 +136,7 @@ static void cfg_convert(const struct afe_cfg *src, struct mtk_base_memif_data *d .base = DT_PROP(n, base), \ .end = DT_PROP(n, end), \ .cur = DT_PROP(n, cur), \ - .fs = DT_PROP(n, fs), \ + COND_PROP(n, fs) \ .hd = DT_PROP(n, hd), \ .enable = DT_PROP(n, enable), \ COND_PROP(n, mono) \ @@ -269,6 +272,20 @@ static unsigned int mtk_afe_fs_timing(unsigned int rate) { 192000, 14 }, { 352800, 7 }, { 384000, 3 }, +#elif defined(CONFIG_SOC_MT8365) + { 8000, 0 }, + { 11025, 1 }, + { 12000, 2 }, + { 16000, 4 }, + { 22050, 5 }, + { 24000, 6 }, + { 32000, 8 }, + { 44100, 9 }, + { 48000, 10 }, + { 88200, 11 }, + { 96000, 12 }, + { 176400, 13 }, + { 192000, 14 }, #else { 8000, 0 }, { 11025, 1 }, diff --git a/src/platform/mtk/include/platform/lib/memory.h b/src/platform/mtk/include/platform/lib/memory.h index a84809bf2d48..26806ab394ba 100644 --- a/src/platform/mtk/include/platform/lib/memory.h +++ b/src/platform/mtk/include/platform/lib/memory.h @@ -42,7 +42,7 @@ static inline void *platform_shared_get(void *ptr, int bytes) * validation that the kernel driver interprets the manifest * correctly. Right now we're using the historical addresses. */ -#ifdef CONFIG_SOC_MT8195 +#if defined(CONFIG_SOC_MT8195) || defined(CONFIG_SOC_MT8365) #define MTK_IPC_BASE (DT_REG_ADDR(DT_NODELABEL(dram0)) + 0x800000) #else #define MTK_IPC_BASE (DT_REG_ADDR(DT_NODELABEL(dram0)) + 0x500000) diff --git a/src/platform/mtk/platform.c b/src/platform/mtk/platform.c index 1592c020f7a2..c189af1f0a16 100644 --- a/src/platform/mtk/platform.c +++ b/src/platform/mtk/platform.c @@ -24,12 +24,25 @@ void mtk_dai_init(struct sof *sof); +#ifndef CONFIG_SOC_MT8365 #define MBOX0 DEVICE_DT_GET(DT_INST(0, mediatek_mbox)) #define MBOX1 DEVICE_DT_GET(DT_INST(1, mediatek_mbox)) +#else +#define IPI DEVICE_DT_GET(DT_INST(0, mediatek_ipi)) + +#define MAILBOX_DEBUG_BASE MTK_IPC_WIN_BASE(DEBUG) + +#define SRAM_REG_OP_CPU2DSP 0x8 +#define SRAM_REG_OP_DSP2CPU 0xC + +#define ADSP_IPI_OP_REQ 0x1 +#define ADSP_IPI_OP_RSP 0x2 +#endif /* Use the same UUID as in "ipc-zephyr.c", which is actually an Intel driver */ SOF_DEFINE_REG_UUID(zipc_task); +#ifndef CONFIG_SOC_MT8365 static void mbox_cmd_fn(const struct device *mbox, void *arg) { /* We're in ISR context. This unblocks the IPC task thread, @@ -38,6 +51,7 @@ static void mbox_cmd_fn(const struct device *mbox, void *arg) */ ipc_schedule_process(ipc_get()); } +#endif enum task_state ipc_platform_do_cmd(struct ipc *ipc) { @@ -54,13 +68,23 @@ enum task_state ipc_platform_do_cmd(struct ipc *ipc) void ipc_platform_complete_cmd(struct ipc *ipc) { +#ifndef CONFIG_SOC_MT8365 mtk_adsp_mbox_signal(MBOX0, 1); +#else + *(uint32_t *)(MAILBOX_DEBUG_BASE + SRAM_REG_OP_DSP2CPU) = ADSP_IPI_OP_RSP; + mtk_adsp_ipi_signal(IPI, 1); +#endif } static void mtk_ipc_send(const void *msg, size_t sz) { mailbox_dspbox_write(0, msg, sz); +#ifndef CONFIG_SOC_MT8365 mtk_adsp_mbox_signal(MBOX1, 0); +#else + *(uint32_t *)(MAILBOX_DEBUG_BASE + SRAM_REG_OP_DSP2CPU) = ADSP_IPI_OP_REQ; + mtk_adsp_ipi_signal(IPI, 1); +#endif } int ipc_platform_send_msg(const struct ipc_msg *msg) @@ -75,11 +99,36 @@ int ipc_platform_send_msg(const struct ipc_msg *msg) return 0; } +#ifndef CONFIG_SOC_MT8365 static void mbox_reply_fn(const struct device *mbox, void *arg) { ipc_get()->is_notification_pending = false; } +#else + +static void ipi_handler_fn(const struct device *ipi, void *arg) +{ + uint32_t op; + + op = *(uint32_t *)(MAILBOX_DEBUG_BASE + SRAM_REG_OP_CPU2DSP); + + switch (op) { + case ADSP_IPI_OP_REQ: + /* new message from host */ + ipc_schedule_process(ipc_get()); + break; + case ADSP_IPI_OP_RSP: + /* reply message(done) from host */ + ipc_get()->is_notification_pending = false; + break; + default: + /* do nothing */ + break; + } +} +#endif + /* "Host Page Table" support. The platform is responsible for * providing a buffer into which the IPC layer reads a DMA "page * table" from the host. This isn't really a page table, it's a @@ -114,8 +163,12 @@ int platform_ipc_init(struct ipc *ipc) schedule_task_init_edf(&ipc->ipc_task, SOF_UUID(zipc_task_uuid), &ipc_task_ops, ipc, 0, 0); +#ifndef CONFIG_SOC_MT8365 mtk_adsp_mbox_set_handler(MBOX0, 0, mbox_cmd_fn, NULL); mtk_adsp_mbox_set_handler(MBOX1, 1, mbox_reply_fn, NULL); +#else + mtk_adsp_ipi_set_handler(IPI, 0, ipi_handler_fn, NULL); +#endif return 0; } diff --git a/src/platform/novalake/include/platform/lib/memory.h b/src/platform/novalake/include/platform/lib/memory.h index 19ade62dfb2b..666c4fc9eb89 100644 --- a/src/platform/novalake/include/platform/lib/memory.h +++ b/src/platform/novalake/include/platform/lib/memory.h @@ -56,6 +56,12 @@ */ #define HEAPMEM_SIZE CONFIG_SOF_ZEPHYR_HEAP_SIZE +#if CONFIG_COLD_STORE_EXECUTE_DRAM && \ + (CONFIG_LLEXT_TYPE_ELF_RELOCATABLE || !defined(LL_EXTENSION_BUILD)) +#define __cold __section(".cold") +#define __cold_rodata __section(".coldrodata") +#endif + #endif /* __PLATFORM_LIB_MEMORY_H__ */ #else diff --git a/src/platform/qemu_xtensa/CMakeLists.txt b/src/platform/qemu_xtensa/CMakeLists.txt new file mode 100644 index 000000000000..8688947cf0c6 --- /dev/null +++ b/src/platform/qemu_xtensa/CMakeLists.txt @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: BSD-3-Clause + +add_local_sources(sof platform.c) diff --git a/src/platform/qemu_xtensa/include/platform/lib/clk.h b/src/platform/qemu_xtensa/include/platform/lib/clk.h new file mode 100644 index 000000000000..c9f05cdf405b --- /dev/null +++ b/src/platform/qemu_xtensa/include/platform/lib/clk.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * + * Copyright(c) 2026 Intel Corporation. + */ + +#ifndef __PLATFORM_LIB_CLK_H__ +#define __PLATFORM_LIB_CLK_H__ + +/* Dummy clk header for qemu_xtensa */ +#define CLK_MAX_CPU_HZ 10000000 +#define CPU_LOWEST_FREQ_IDX 0 + +#endif /* __PLATFORM_LIB_CLK_H__ */ diff --git a/src/platform/qemu_xtensa/include/platform/lib/dai.h b/src/platform/qemu_xtensa/include/platform/lib/dai.h new file mode 100644 index 000000000000..418c383789a8 --- /dev/null +++ b/src/platform/qemu_xtensa/include/platform/lib/dai.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * + * Copyright(c) 2026 Intel Corporation. + */ + +#ifndef __PLATFORM_LIB_DAI_H__ +#define __PLATFORM_LIB_DAI_H__ + +/* Dummy dai header for qemu_xtensa */ + +#endif /* __PLATFORM_LIB_DAI_H__ */ diff --git a/src/platform/qemu_xtensa/include/platform/lib/dma.h b/src/platform/qemu_xtensa/include/platform/lib/dma.h new file mode 100644 index 000000000000..4c4068b99392 --- /dev/null +++ b/src/platform/qemu_xtensa/include/platform/lib/dma.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * + * Copyright(c) 2026 Intel Corporation. + */ + +#ifndef __PLATFORM_LIB_DMA_H__ +#define __PLATFORM_LIB_DMA_H__ + +/* Dummy dma header for qemu_xtensa */ +struct dma; + +struct sof_dma { + const struct device *z_dev; +}; + +#endif /* __PLATFORM_LIB_DMA_H__ */ diff --git a/src/platform/qemu_xtensa/include/platform/lib/mailbox.h b/src/platform/qemu_xtensa/include/platform/lib/mailbox.h new file mode 100644 index 000000000000..47c97744fe6d --- /dev/null +++ b/src/platform/qemu_xtensa/include/platform/lib/mailbox.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * + * Copyright(c) 2026 Intel Corporation. + */ + +#ifndef __PLATFORM_LIB_MAILBOX_H__ +#define __PLATFORM_LIB_MAILBOX_H__ + +/* Dummy mailbox header for qemu_xtensa */ +#define MAILBOX_HOSTBOX_BASE 0x10000000 +#define MAILBOX_HOSTBOX_SIZE 0x1000 +#define MAILBOX_DSPBOX_BASE 0x10005000 +#define MAILBOX_DSPBOX_SIZE 0x1000 +#define MAILBOX_STREAM_BASE 0x10001000 +#define MAILBOX_STREAM_SIZE 0x1000 +#define MAILBOX_TRACE_BASE 0x10002000 +#define MAILBOX_TRACE_SIZE 0x1000 +#define MAILBOX_EXCEPTION_BASE 0x10003000 +#define MAILBOX_EXCEPTION_SIZE 0x1000 +#define MAILBOX_DEBUG_BASE 0x10004000 +#define MAILBOX_DEBUG_SIZE 0x1000 +#define MAILBOX_SW_REG_BASE 0x10005000 +#define MAILBOX_SW_REG_SIZE 0x1000 + +#include <stddef.h> +#include <stdint.h> + +static inline void mailbox_sw_regs_write(size_t offset, const void *src, size_t bytes) {} +static inline void mailbox_sw_reg_write(size_t offset, uint32_t val) {} +static inline void mailbox_sw_reg_write64(size_t offset, uint64_t val) {} +static inline uint32_t mailbox_sw_reg_read(size_t offset) { return 0; } +static inline uint64_t mailbox_sw_reg_read64(size_t offset) { return 0; } + +#endif /* __PLATFORM_LIB_MAILBOX_H__ */ diff --git a/src/platform/qemu_xtensa/include/platform/lib/memory.h b/src/platform/qemu_xtensa/include/platform/lib/memory.h new file mode 100644 index 000000000000..d0843904f563 --- /dev/null +++ b/src/platform/qemu_xtensa/include/platform/lib/memory.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * + * Copyright(c) 2026 Intel Corporation. + */ + +#ifndef __PLATFORM_LIB_MEMORY_H__ +#define __PLATFORM_LIB_MEMORY_H__ + +/* Dummy memory header for qemu_xtensa */ + +static inline void *platform_shared_get(void *ptr, int bytes) +{ + return ptr; +} + +#define PLATFORM_DCACHE_ALIGN sizeof(void *) +#define HOST_PAGE_SIZE 4096 +#define SHARED_DATA + +#endif /* __PLATFORM_LIB_MEMORY_H__ */ diff --git a/src/platform/qemu_xtensa/include/platform/platform.h b/src/platform/qemu_xtensa/include/platform/platform.h new file mode 100644 index 000000000000..5f89152251b4 --- /dev/null +++ b/src/platform/qemu_xtensa/include/platform/platform.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * + * Copyright(c) 2026 Intel Corporation. + */ + +#ifndef __PLATFORM_PLATFORM_H__ +#define __PLATFORM_PLATFORM_H__ + +/* Dummy platform header for qemu_xtensa */ +#define PLATFORM_CORE_COUNT 1 +#define PLATFORM_MAX_CHANNELS 8 +#define PLATFORM_MAX_STREAMS 8 + +#define HW_CFG_VERSION 0x010000 +#define DMA_TRACE_LOCAL_SIZE HOST_PAGE_SIZE + +struct ipc_msg; +static inline void ipc_platform_send_msg_direct(const struct ipc_msg *msg) {} + +#endif /* __PLATFORM_PLATFORM_H__ */ diff --git a/src/platform/qemu_xtensa/include/platform/trace/trace.h b/src/platform/qemu_xtensa/include/platform/trace/trace.h new file mode 100644 index 000000000000..65499099cd52 --- /dev/null +++ b/src/platform/qemu_xtensa/include/platform/trace/trace.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * + * Copyright(c) 2026 Intel Corporation. + */ + +#ifndef __PLATFORM_TRACE_TRACE_H__ +#define __PLATFORM_TRACE_TRACE_H__ + +/* Dummy trace header for qemu_xtensa */ +#define PLATFORM_TRACE_DICT_FRONT 0 + +#endif /* __PLATFORM_TRACE_TRACE_H__ */ diff --git a/src/platform/qemu_xtensa/platform.c b/src/platform/qemu_xtensa/platform.c new file mode 100644 index 000000000000..f39ba5c1a639 --- /dev/null +++ b/src/platform/qemu_xtensa/platform.c @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// Copyright(c) 2026 Intel Corporation. +// +#include <sof/lib/mailbox.h> +#include <sof/ipc/common.h> +#include <rtos/sof.h> + +void ipc_platform_complete_cmd(struct ipc *ipc) +{ +} + +int platform_boot_complete(uint32_t boot_message) +{ + return 0; +} + +int platform_init(struct sof *sof) +{ + return 0; +} diff --git a/src/probe/probe.c b/src/probe/probe.c index 339935993ef1..19f62bf81a14 100644 --- a/src/probe/probe.c +++ b/src/probe/probe.c @@ -13,7 +13,6 @@ #include <rtos/alloc.h> #include <rtos/init.h> #include <sof/lib/dma.h> -#include <sof/lib/notifier.h> #include <sof/lib/uuid.h> #include <sof/ipc/topology.h> #include <sof/ipc/driver.h> @@ -80,11 +79,11 @@ struct probe_dma_ext { * Probe main struct */ struct probe_pdata { + struct task dmap_work; /**< probe task */ struct probe_dma_ext ext_dma; /**< extraction DMA */ struct probe_dma_ext inject_dma[CONFIG_PROBE_DMA_MAX]; /**< injection DMA */ struct probe_point probe_points[CONFIG_PROBE_POINTS_MAX]; /**< probe points */ struct probe_data_packet header; /**< data packet header */ - struct task dmap_work; /**< probe task */ }; /** @@ -102,7 +101,7 @@ static int probe_dma_buffer_init(struct probe_dma_buf *buffer, uint32_t size, size, align); if (!buffer->addr) { - tr_err(&pr_tr, "probe_dma_buffer_init(): alloc failed"); + tr_err(&pr_tr, "alloc failed"); return -ENOMEM; } @@ -145,7 +144,7 @@ static int probe_dma_init(struct probe_dma_ext *dma, uint32_t direction) dma->dc.dmac = dma_get(direction, 0, SOF_DMA_DEV_HOST, SOF_DMA_ACCESS_SHARED); if (!dma->dc.dmac) { - tr_err(&pr_tr, "probe_dma_init(): dma->dc.dmac = NULL"); + tr_err(&pr_tr, "dma->dc.dmac = NULL"); return -ENODEV; } dma->dc.dmac->priv_data = &dma->dc.dmac->chan->index; @@ -176,7 +175,7 @@ static int probe_dma_init(struct probe_dma_ext *dma, uint32_t direction) dma->config.dest_width = sizeof(uint32_t); dma->config.cyclic = 0; - err = dma_sg_alloc(&dma->config.elem_array, SOF_MEM_FLAG_USER, + err = dma_sg_alloc(NULL, &dma->config.elem_array, SOF_MEM_FLAG_USER, dma->config.direction, elem_num, elem_size, elem_addr, 0); if (err < 0) return err; @@ -201,7 +200,7 @@ static int probe_dma_init(struct probe_dma_ext *dma, uint32_t direction) dma->dc.dmac = sof_dma_get(direction, 0, SOF_DMA_DEV_HOST, SOF_DMA_ACCESS_SHARED); if (!dma->dc.dmac) { - tr_err(&pr_tr, "probe_dma_init(): dma->dc.dmac = NULL"); + tr_err(&pr_tr, "dma->dc.dmac = NULL"); return -ENODEV; } @@ -213,7 +212,7 @@ static int probe_dma_init(struct probe_dma_ext *dma, uint32_t direction) channel = dma_request_channel(dma->dc.dmac->z_dev, &channel); if (channel < 0) { - tr_err(&pr_tr, "probe_dma_init(): dma_request_channel() failed"); + tr_err(&pr_tr, "dma_request_channel() failed"); return -EINVAL; } dma->dc.chan = &dma->dc.dmac->chan[channel]; @@ -255,14 +254,14 @@ static int probe_dma_init(struct probe_dma_ext *dma, uint32_t direction) static int probe_dma_deinit(struct probe_dma_ext *dma) { int err = 0; - dma_sg_free(&dma->config.elem_array); + dma_sg_free(NULL, &dma->config.elem_array); #if CONFIG_ZEPHYR_NATIVE_DRIVERS err = dma_stop(dma->dc.dmac->z_dev, dma->dc.chan->index); #else err = dma_stop_legacy(dma->dc.chan); #endif if (err < 0) { - tr_err(&pr_tr, "probe_dma_deinit(): dma_stop() failed"); + tr_err(&pr_tr, "dma_stop() failed"); return err; } #if CONFIG_ZEPHYR_NATIVE_DRIVERS @@ -303,7 +302,7 @@ static enum task_state probe_task(void *data) ©_align); #endif if (err < 0) { - tr_err(&pr_tr, "probe_task(): dma_get_attribute failed."); + tr_err(&pr_tr, "dma_get_attribute failed."); return SOF_TASK_STATE_COMPLETED; } @@ -325,7 +324,7 @@ static enum task_state probe_task(void *data) return SOF_TASK_STATE_RESCHEDULE; if (err < 0) { - tr_err(&pr_tr, "probe_task(): dma_copy_to_host() failed."); + tr_err(&pr_tr, "dma_copy_to_host() failed."); return err; } @@ -357,7 +356,7 @@ static void probe_auto_enable_logs(uint32_t stream_tag) ret = probe_point_add(1, &log_point); if (ret) - tr_err(&pr_tr, "probe_auto_enable_logs() failed"); + tr_err(&pr_tr, "failed"); } #endif @@ -367,10 +366,10 @@ int probe_init(const struct probe_dma *probe_dma) uint32_t i; int err; - tr_dbg(&pr_tr, "probe_init()"); + tr_dbg(&pr_tr, "entry"); if (_probe) { - tr_err(&pr_tr, "probe_init(): Probes already initialized."); + tr_err(&pr_tr, "Probes already initialized."); return -EINVAL; } @@ -378,13 +377,13 @@ int probe_init(const struct probe_dma *probe_dma) sof_get()->probe = rzalloc(SOF_MEM_FLAG_USER, sizeof(*_probe)); if (!sof_get()->probe) { - tr_err(&pr_tr, "probe_init(): Alloc failed."); + tr_err(&pr_tr, "Alloc failed."); return -ENOMEM; } _probe = probe_get(); if (!_probe) { - tr_err(&pr_tr, "probe_init(): Alloc failed."); + tr_err(&pr_tr, "Alloc failed."); return -ENOMEM; } @@ -406,7 +405,7 @@ int probe_init(const struct probe_dma *probe_dma) err = probe_dma_init(&_probe->ext_dma, SOF_DMA_DIR_LMEM_TO_HMEM); if (err < 0) { - tr_err(&pr_tr, "probe_init(): probe_dma_init() failed"); + tr_err(&pr_tr, "probe_dma_init() failed"); _probe->ext_dma.stream_tag = PROBE_DMA_INVALID; return err; } @@ -416,7 +415,7 @@ int probe_init(const struct probe_dma *probe_dma) err = dma_start_legacy(_probe->ext_dma.dc.chan); #endif if (err < 0) { - tr_err(&pr_tr, "probe_init(): failed to start extraction dma"); + tr_err(&pr_tr, "failed to start extraction dma"); return -EBUSY; } @@ -444,10 +443,10 @@ int probe_deinit(void) uint32_t i; int err; - tr_dbg(&pr_tr, "probe_deinit()"); + tr_dbg(&pr_tr, "entry"); if (!_probe) { - tr_err(&pr_tr, "probe_deinit(): Not initialized."); + tr_err(&pr_tr, "Not initialized."); return -EINVAL; } @@ -455,7 +454,7 @@ int probe_deinit(void) /* check for attached injection probe DMAs */ for (i = 0; i < CONFIG_PROBE_DMA_MAX; i++) { if (_probe->inject_dma[i].stream_tag != PROBE_DMA_INVALID) { - tr_err(&pr_tr, "probe_deinit(): Cannot deinitialize with injection DMAs attached."); + tr_err(&pr_tr, "Cannot deinitialize with injection DMAs attached."); return -EINVAL; } } @@ -463,13 +462,13 @@ int probe_deinit(void) /* check for connected probe points */ for (i = 0; i < CONFIG_PROBE_POINTS_MAX; i++) { if (_probe->probe_points[i].stream_tag != PROBE_POINT_INVALID) { - tr_err(&pr_tr, "probe_deinit(): Cannot deinitialize with probe points connected."); + tr_err(&pr_tr, "Cannot deinitialize with probe points connected."); return -EINVAL; } } if (_probe->ext_dma.stream_tag != PROBE_DMA_INVALID) { - tr_dbg(&pr_tr, "probe_deinit() Freeing task and extraction DMA."); + tr_dbg(&pr_tr, "Freeing task and extraction DMA."); schedule_task_free(&_probe->dmap_work); err = probe_dma_deinit(&_probe->ext_dma); if (err < 0) @@ -491,10 +490,10 @@ int probe_dma_add(uint32_t count, const struct probe_dma *probe_dma) uint32_t first_free; int err; - tr_dbg(&pr_tr, "probe_dma_add() count = %u", count); + tr_dbg(&pr_tr, "count = %u", count); if (!_probe) { - tr_err(&pr_tr, "probe_dma_add(): Not initialized."); + tr_err(&pr_tr, "Not initialized."); return -EINVAL; } @@ -520,14 +519,14 @@ int probe_dma_add(uint32_t count, const struct probe_dma *probe_dma) } if (stream_tag == probe_dma[i].stream_tag) { - tr_err(&pr_tr, "probe_dma_add(): Probe DMA %u already attached.", + tr_err(&pr_tr, "Probe DMA %u already attached.", stream_tag); return -EINVAL; } } if (first_free == CONFIG_PROBE_DMA_MAX) { - tr_err(&pr_tr, "probe_dma_add(): Exceeded maximum number of DMAs attached = " + tr_err(&pr_tr, "Exceeded maximum number of DMAs attached = " STRINGIFY(CONFIG_PROBE_DMA_MAX)); return -EINVAL; } @@ -540,7 +539,7 @@ int probe_dma_add(uint32_t count, const struct probe_dma *probe_dma) err = probe_dma_init(&_probe->inject_dma[first_free], SOF_DMA_DIR_HMEM_TO_LMEM); if (err < 0) { - tr_err(&pr_tr, "probe_dma_add(): probe_dma_init() failed"); + tr_err(&pr_tr, "probe_dma_init() failed"); _probe->inject_dma[first_free].stream_tag = PROBE_DMA_INVALID; return err; @@ -575,10 +574,10 @@ int probe_dma_remove(uint32_t count, const uint32_t *stream_tag) uint32_t j; int err; - tr_dbg(&pr_tr, "probe_dma_remove() count = %u", count); + tr_dbg(&pr_tr, "count = %u", count); if (!_probe) { - tr_err(&pr_tr, "probe_dma_remove(): Not initialized."); + tr_err(&pr_tr, "Not initialized."); return -EINVAL; } @@ -633,7 +632,7 @@ static int copy_to_pbuffer(struct probe_dma_buf *pbuf, void *data, /* copy data to probe buffer */ if (memcpy_s((void *)pbuf->w_ptr, pbuf->end_addr - pbuf->w_ptr, data, head)) { - tr_err(&pr_tr, "copy_to_pbuffer(): memcpy_s() failed"); + tr_err(&pr_tr, "memcpy_s() failed"); return -EINVAL; } dcache_writeback_region((__sparse_force void __sparse_cache *)pbuf->w_ptr, head); @@ -643,7 +642,7 @@ static int copy_to_pbuffer(struct probe_dma_buf *pbuf, void *data, pbuf->w_ptr = pbuf->addr; if (memcpy_s((void *)pbuf->w_ptr, (char *)pbuf->end_addr - (char *)pbuf->w_ptr, (char *)data + head, tail)) { - tr_err(&pr_tr, "copy_to_pbuffer(): memcpy_s() failed"); + tr_err(&pr_tr, "memcpy_s() failed"); return -EINVAL; } dcache_writeback_region((__sparse_force void __sparse_cache *)pbuf->w_ptr, tail); @@ -690,7 +689,7 @@ static int copy_from_pbuffer(struct probe_dma_buf *pbuf, void *data, /* data from DMA so invalidate it */ dcache_invalidate_region((__sparse_force void __sparse_cache *)pbuf->r_ptr, head); if (memcpy_s(data, bytes, (void *)pbuf->r_ptr, head)) { - tr_err(&pr_tr, "copy_from_pbuffer(): memcpy_s() failed"); + tr_err(&pr_tr, "memcpy_s() failed"); return -EINVAL; } @@ -700,7 +699,7 @@ static int copy_from_pbuffer(struct probe_dma_buf *pbuf, void *data, pbuf->r_ptr = pbuf->addr; dcache_invalidate_region((__sparse_force void __sparse_cache *)pbuf->r_ptr, tail); if (memcpy_s((char *)data + head, tail, (void *)pbuf->r_ptr, tail)) { - tr_err(&pr_tr, "copy_from_pbuffer(): memcpy_s() failed"); + tr_err(&pr_tr, "memcpy_s() failed"); return -EINVAL; } pbuf->r_ptr = pbuf->r_ptr + tail; @@ -789,7 +788,7 @@ static uint32_t probe_gen_format(uint32_t frame_fmt, uint32_t rate, float_fmt = 1; break; default: - tr_err(&pr_tr, "probe_gen_format(): Invalid frame format specified = 0x%08x", + tr_err(&pr_tr, "Invalid frame format specified = 0x%08x", frame_fmt); return 0; } @@ -902,14 +901,12 @@ static ssize_t probe_logging_hook(uint8_t *buffer, size_t length) * Extraction probe: generate format, header and copy data to probe buffer. * Injection probe: find corresponding DMA, check avail data, copy data, * update pointers and request more data from host if needed. - * \param[in] arg pointer (not used). - * \param[in] type of notify. - * \param[in] data pointer. + * \param[in] arg pointer to buffer_id. + * \param[in] cb_data pointer to buffer callback transaction data. */ -static void probe_cb_produce(void *arg, enum notify_id type, void *data) +static void probe_cb_produce(void *arg, struct buffer_cb_transact *cb_data) { struct probe_pdata *_probe = probe_get(); - struct buffer_cb_transact *cb_data = data; struct comp_buffer *buffer = cb_data->buffer; struct probe_dma_ext *dma; uint32_t buffer_id; @@ -921,7 +918,7 @@ static void probe_cb_produce(void *arg, enum notify_id type, void *data) uint32_t format; uint64_t checksum; - buffer_id = *(int *)arg; + buffer_id = *(uint32_t *)arg; /* search for probe point connected to this buffer */ for (i = 0; i < CONFIG_PROBE_POINTS_MAX; i++) @@ -929,7 +926,7 @@ static void probe_cb_produce(void *arg, enum notify_id type, void *data) break; if (i == CONFIG_PROBE_POINTS_MAX) { - tr_err(&pr_tr, "probe_cb_produce(): probe not found for buffer id: %d", + tr_err(&pr_tr, "probe not found for buffer id: %d", buffer_id); return; } @@ -986,7 +983,7 @@ static void probe_cb_produce(void *arg, enum notify_id type, void *data) } } if (j == CONFIG_PROBE_DMA_MAX) { - tr_err(&pr_tr, "probe_cb_produce(): dma not found"); + tr_err(&pr_tr, "dma not found"); return; } dma = &_probe->inject_dma[j]; @@ -1003,7 +1000,7 @@ static void probe_cb_produce(void *arg, enum notify_id type, void *data) &free_bytes); #endif if (ret < 0) { - tr_err(&pr_tr, "probe_cb_produce(): dma_get_data_size() failed, ret = %u", + tr_err(&pr_tr, "dma_get_data_size() failed, ret = %u", ret); goto err; } @@ -1063,25 +1060,23 @@ static void probe_cb_produce(void *arg, enum notify_id type, void *data) } return; err: - tr_err(&pr_tr, "probe_cb_produce(): failed to generate probe data"); + tr_err(&pr_tr, "failed to generate probe data"); } /** * \brief Callback for buffer free, it will remove probe point. - * \param[in] arg pointer (not used). - * \param[in] type of notify. - * \param[in] data pointer. + * \param[in] arg pointer to buffer_id. */ -static void probe_cb_free(void *arg, enum notify_id type, void *data) +static void probe_cb_free(void *arg) { - uint32_t buffer_id = *(int *)arg; + uint32_t buffer_id = *(uint32_t *)arg; int ret; - tr_dbg(&pr_tr, "probe_cb_free() buffer_id = %u", buffer_id); + tr_dbg(&pr_tr, "buffer_id = %u", buffer_id); ret = probe_point_remove(1, &buffer_id); if (ret < 0) - tr_err(&pr_tr, "probe_cb_free(): probe_point_remove() failed"); + tr_err(&pr_tr, "probe_point_remove() failed"); } static bool probe_purpose_needs_ext_dma(uint32_t purpose) @@ -1102,7 +1097,7 @@ static struct comp_buffer *ipc4_get_buffer(struct ipc_comp_dev *dev, probe_point switch (probe_point.fields.type) { case PROBE_TYPE_INPUT: comp_dev_for_each_producer(dev->cd, buf) { - queue_id = IPC4_SRC_QUEUE_ID(buf_get_id(buf)); + queue_id = IPC4_SINK_QUEUE_ID(buf_get_id(buf)); if (queue_id == probe_point.fields.index) return buf; @@ -1110,7 +1105,7 @@ static struct comp_buffer *ipc4_get_buffer(struct ipc_comp_dev *dev, probe_point break; case PROBE_TYPE_OUTPUT: comp_dev_for_each_consumer(dev->cd, buf) { - queue_id = IPC4_SINK_QUEUE_ID(buf_get_id(buf)); + queue_id = IPC4_SRC_QUEUE_ID(buf_get_id(buf)); if (queue_id == probe_point.fields.index) return buf; @@ -1155,10 +1150,10 @@ int probe_point_add(uint32_t count, const struct probe_point *probe) #if CONFIG_IPC_MAJOR_4 struct comp_buffer *buf = NULL; #endif - tr_dbg(&pr_tr, "probe_point_add() count = %u", count); + tr_dbg(&pr_tr, "count = %u", count); if (!_probe) { - tr_err(&pr_tr, "probe_point_add(): Not initialized."); + tr_err(&pr_tr, "Not initialized."); return -EINVAL; } @@ -1173,7 +1168,7 @@ int probe_point_add(uint32_t count, const struct probe_point *probe) probe[i].stream_tag); if (!verify_purpose(probe[i].purpose)) { - tr_err(&pr_tr, "probe_point_add() error: invalid purpose %d", + tr_err(&pr_tr, "error: invalid purpose %d", probe[i].purpose); return -EINVAL; @@ -1181,7 +1176,7 @@ int probe_point_add(uint32_t count, const struct probe_point *probe) if (_probe->ext_dma.stream_tag == PROBE_DMA_INVALID && probe_purpose_needs_ext_dma(probe[i].purpose)) { - tr_err(&pr_tr, "probe_point_add(): extraction DMA not enabled."); + tr_err(&pr_tr, "extraction DMA not enabled."); return -EINVAL; } @@ -1197,7 +1192,7 @@ int probe_point_add(uint32_t count, const struct probe_point *probe) #endif /* check if buffer exists */ if (!dev) { - tr_err(&pr_tr, "probe_point_add(): No device with ID %u found.", + tr_err(&pr_tr, "No device with ID %u found.", buf_id->full_id); return -EINVAL; @@ -1205,14 +1200,14 @@ int probe_point_add(uint32_t count, const struct probe_point *probe) #if CONFIG_IPC_MAJOR_4 buf = ipc4_get_buffer(dev, *buf_id); if (!buf) { - tr_err(&pr_tr, "probe_point_add(): buffer %u not found.", + tr_err(&pr_tr, "buffer %u not found.", buf_id->full_id); return -EINVAL; } #else if (dev->type != COMP_TYPE_BUFFER) { - tr_err(&pr_tr, "probe_point_add(): Device ID %u is not a buffer.", + tr_err(&pr_tr, "Device ID %u is not a buffer.", buf_id->full_id); return -EINVAL; @@ -1236,7 +1231,7 @@ int probe_point_add(uint32_t count, const struct probe_point *probe) if (buffer_id == buf_id->full_id) { if (_probe->probe_points[j].purpose == probe[i].purpose) { - tr_err(&pr_tr, "probe_point_add(): Probe already attached to buffer %u with purpose %u", + tr_err(&pr_tr, "Probe already attached to buffer %u with purpose %u", buffer_id, probe[i].purpose); @@ -1246,7 +1241,7 @@ int probe_point_add(uint32_t count, const struct probe_point *probe) } if (first_free == CONFIG_PROBE_POINTS_MAX) { - tr_err(&pr_tr, "probe_point_add(): Maximum number of probe points connected aleady: " + tr_err(&pr_tr, "Maximum number of probe points connected aleady: " STRINGIFY(CONFIG_PROBE_POINTS_MAX)); return -EINVAL; @@ -1267,7 +1262,7 @@ int probe_point_add(uint32_t count, const struct probe_point *probe) } if (!dma_found) { - tr_err(&pr_tr, "probe_point_add(): No DMA with stream tag %u found for injection.", + tr_err(&pr_tr, "No DMA with stream tag %u found for injection.", probe[i].stream_tag); return -EINVAL; @@ -1278,7 +1273,7 @@ int probe_point_add(uint32_t count, const struct probe_point *probe) #else if (dma_start_legacy(_probe->inject_dma[j].dc.chan) < 0) { #endif - tr_err(&pr_tr, "probe_point_add(): failed to start dma"); + tr_err(&pr_tr, "failed to start dma"); return -EBUSY; } @@ -1293,7 +1288,7 @@ int probe_point_add(uint32_t count, const struct probe_point *probe) } if (j == CONFIG_PROBE_POINTS_MAX) { - tr_dbg(&pr_tr, "probe_point_add(): start probe task"); + tr_dbg(&pr_tr, "start probe task"); schedule_task(&_probe->dmap_work, 1000, 1000); } /* ignore probe stream tag for extraction probes */ @@ -1315,16 +1310,13 @@ int probe_point_add(uint32_t count, const struct probe_point *probe) probe_point_id_t *new_buf_id = &_probe->probe_points[first_free].buffer_id; #if CONFIG_IPC_MAJOR_4 - notifier_register(&new_buf_id->full_id, buf, NOTIFIER_ID_BUFFER_PRODUCE, - &probe_cb_produce, 0); - notifier_register(&new_buf_id->full_id, buf, NOTIFIER_ID_BUFFER_FREE, - &probe_cb_free, 0); + struct comp_buffer *probe_buf = buf; #else - notifier_register(&new_buf_id->full_id, dev->cb, NOTIFIER_ID_BUFFER_PRODUCE, - &probe_cb_produce, 0); - notifier_register(&new_buf_id->full_id, dev->cb, NOTIFIER_ID_BUFFER_FREE, - &probe_cb_free, 0); + struct comp_buffer *probe_buf = (struct comp_buffer *)dev->cb; #endif + probe_buf->probe_cb_produce = probe_cb_produce; + probe_buf->probe_cb_free = probe_cb_free; + probe_buf->probe_cb_arg = &new_buf_id->full_id; } } @@ -1338,10 +1330,10 @@ int probe_dma_info(struct sof_ipc_probe_info_params *data, uint32_t max_size) uint32_t i = 0; uint32_t j = 0; - tr_dbg(&pr_tr, "probe_dma_info()"); + tr_dbg(&pr_tr, "entry"); if (!_probe) { - tr_err(&pr_tr, "probe_dma_info(): Not initialized."); + tr_err(&pr_tr, "Not initialized."); return -EINVAL; } @@ -1376,10 +1368,10 @@ int probe_point_info(struct sof_ipc_probe_info_params *data, uint32_t max_size) uint32_t i = 0; uint32_t j = 0; - tr_dbg(&pr_tr, "probe_point_info()"); + tr_dbg(&pr_tr, "entry"); if (!_probe) { - tr_err(&pr_tr, "probe_point_info(): Not initialized."); + tr_err(&pr_tr, "Not initialized."); return -EINVAL; } @@ -1418,10 +1410,10 @@ int probe_point_remove(uint32_t count, const uint32_t *buffer_id) struct comp_buffer *buf; #endif - tr_dbg(&pr_tr, "probe_point_remove() count = %u", count); + tr_dbg(&pr_tr, "count = %u", count); if (!_probe) { - tr_err(&pr_tr, "probe_point_remove(): Not initialized."); + tr_err(&pr_tr, "Not initialized."); return -EINVAL; } /* remove each requested probe point */ @@ -1444,19 +1436,20 @@ int probe_point_remove(uint32_t count, const uint32_t *buffer_id) if (dev) { buf = ipc4_get_buffer(dev, *buf_id); if (buf) { - notifier_unregister(NULL, buf, - NOTIFIER_ID_BUFFER_PRODUCE); - notifier_unregister(NULL, buf, - NOTIFIER_ID_BUFFER_FREE); + buf->probe_cb_produce = NULL; + buf->probe_cb_free = NULL; + buf->probe_cb_arg = NULL; } } #else dev = ipc_get_comp_by_id(ipc_get(), buffer_id[i]); if (dev) { - notifier_unregister(&buf_id->full_id, dev->cb, - NOTIFIER_ID_BUFFER_PRODUCE); - notifier_unregister(&buf_id->full_id, dev->cb, - NOTIFIER_ID_BUFFER_FREE); + struct comp_buffer *probe_buf = + (struct comp_buffer *)dev->cb; + + probe_buf->probe_cb_produce = NULL; + probe_buf->probe_cb_free = NULL; + probe_buf->probe_cb_arg = NULL; } #endif _probe->probe_points[j].stream_tag = @@ -1470,7 +1463,7 @@ int probe_point_remove(uint32_t count, const uint32_t *buffer_id) break; } if (j == CONFIG_PROBE_POINTS_MAX) { - tr_dbg(&pr_tr, "probe_point_remove(): cancel probe task"); + tr_dbg(&pr_tr, "cancel probe task"); schedule_task_cancel(&_probe->dmap_work); } @@ -1485,7 +1478,7 @@ static int probe_mod_init(struct processing_module *mod) const struct ipc4_probe_module_cfg *probe_cfg = mod_data->cfg.init_data; int ret; - comp_info(dev, "probe_mod_init()"); + comp_info(dev, "entry"); ret = probe_init(&probe_cfg->gtw_cfg); if (ret < 0) @@ -1498,7 +1491,7 @@ static int probe_free(struct processing_module *mod) { struct comp_dev *dev = mod->dev; - comp_info(dev, "probe_free()"); + comp_info(dev, "entry"); probe_deinit(); @@ -1515,7 +1508,7 @@ static int probe_set_config(struct processing_module *mod, uint32_t param_id, { struct comp_dev *dev = mod->dev; - comp_info(dev, "probe_set_config()"); + comp_info(dev, "entry"); switch (param_id) { case IPC4_PROBE_MODULE_PROBE_POINTS_ADD: @@ -1581,14 +1574,14 @@ static int probe_get_available_points(struct processing_module *mod, id.fields.type = PROBE_TYPE_INPUT; comp_dev_for_each_producer(icd->cd, buf) { - id.fields.index = IPC4_SRC_QUEUE_ID(buf_get_id(buf)); + id.fields.index = IPC4_SINK_QUEUE_ID(buf_get_id(buf)); if (probe_add_point_info_params(info, id, i, max_size)) return 0; i++; } id.fields.type = PROBE_TYPE_OUTPUT; comp_dev_for_each_consumer(icd->cd, buf) { - id.fields.index = IPC4_SINK_QUEUE_ID(buf_get_id(buf)); + id.fields.index = IPC4_SRC_QUEUE_ID(buf_get_id(buf)); if (probe_add_point_info_params(info, id, i, max_size)) return 0; i++; @@ -1640,7 +1633,7 @@ static int probe_dummy_process(struct processing_module *mod, { struct comp_dev *dev = mod->dev; - comp_warn(dev, "probe_dummy_process() called"); + comp_warn(dev, "called"); return 0; } diff --git a/src/samples/audio/detect_test.c b/src/samples/audio/detect_test.c index 1b9082a9ab8c..35ed6ecd2011 100644 --- a/src/samples/audio/detect_test.c +++ b/src/samples/audio/detect_test.c @@ -139,7 +139,7 @@ static void notify_host(const struct comp_dev *dev) { struct comp_data *cd = comp_get_drvdata(dev); - comp_info(dev, "notify_host()"); + comp_info(dev, "entry"); #if CONFIG_IPC_MAJOR_4 ipc_msg_send(cd->msg, NULL, true); @@ -177,7 +177,7 @@ static void notify_kpb(const struct comp_dev *dev) { struct comp_data *cd = comp_get_drvdata(dev); - comp_info(dev, "notify_kpb(), preamble: %u", cd->detect_preamble); + comp_info(dev, "preamble: %u", cd->detect_preamble); cd->client_data.r_ptr = NULL; cd->client_data.sink = NULL; @@ -282,7 +282,7 @@ static int test_keyword_get_threshold(struct comp_dev *dev, int sample_width) return ACTIVATION_DEFAULT_THRESHOLD_S32; #endif /* CONFIG_FORMAT_S32LE */ default: - comp_err(dev, "test_keyword_get_threshold(), unsupported sample width: %d", + comp_err(dev, "unsupported sample width: %d", sample_width); return -EINVAL; } @@ -311,7 +311,7 @@ static int test_keyword_apply_config(struct comp_dev *dev, if (!cd->config.activation_threshold) { ret = test_keyword_get_threshold(dev, sample_width); if (ret < 0) { - comp_err(dev, "test_keyword_apply_config(): unsupported sample width %u", + comp_err(dev, "unsupported sample width %u", sample_width); return ret; } @@ -332,7 +332,7 @@ static void test_keyword_set_params(struct comp_dev *dev, struct comp_data *cd = comp_get_drvdata(dev); enum sof_ipc_frame valid_fmt, frame_fmt; - comp_info(dev, "test_keyword_set_params()"); + comp_info(dev, "entry"); memset(params, 0, sizeof(*params)); params->channels = cd->base_cfg.audio_fmt.channels_count; @@ -361,11 +361,11 @@ static int test_keyword_set_config(struct comp_dev *dev, const char *data, cfg = (const struct sof_detect_test_config *)data; cfg_size = data_size; - comp_info(dev, "test_keyword_set_config(): config size = %u", + comp_info(dev, "config size = %u", cfg_size); if (cfg_size != sizeof(struct sof_detect_test_config)) { - comp_err(dev, "test_keyword_set_config(): invalid config size"); + comp_err(dev, "invalid config size"); return -EINVAL; } @@ -379,12 +379,12 @@ static int test_keyword_get_config(struct comp_dev *dev, char *data, size_t cfg_size; int ret; - comp_info(dev, "test_keyword_get_config()"); + comp_info(dev, "entry"); cfg_size = sizeof(struct sof_detect_test_config); if (cfg_size > *data_size) { - comp_err(dev, "test_keyword_get_config(): wrong config size: %d", + comp_err(dev, "wrong config size: %d", *data_size); return -EINVAL; } @@ -405,7 +405,7 @@ static int test_keyword_set_large_config(struct comp_dev *dev, uint32_t data_offset, const char *data) { - comp_dbg(dev, "test_keyword_set_large_config()"); + comp_dbg(dev, "entry"); struct comp_data *cd = comp_get_drvdata(dev); switch (param_id) { @@ -429,7 +429,7 @@ static int test_keyword_get_large_config(struct comp_dev *dev, uint32_t *data_offset, char *data) { - comp_dbg(dev, "test_keyword_get_large_config()"); + comp_dbg(dev, "entry"); switch (param_id) { case IPC4_DETECT_TEST_GET_CONFIG: @@ -492,10 +492,10 @@ static int test_keyword_set_config(struct comp_dev *dev, cfg = (struct sof_detect_test_config *)cdata->data->data; bs = cfg->size; - comp_info(dev, "test_keyword_set_config(), blob size = %zu", bs); + comp_info(dev, "blob size = %zu", bs); if (bs != sizeof(struct sof_detect_test_config)) { - comp_err(dev, "test_keyword_set_config(): invalid blob size"); + comp_err(dev, "invalid blob size"); return -EINVAL; } @@ -573,7 +573,7 @@ static int test_keyword_get_config(struct comp_dev *dev, size_t bs; int ret = 0; - comp_info(dev, "test_keyword_get_config()"); + comp_info(dev, "entry"); /* Copy back to user space */ bs = cd->config.size; @@ -606,7 +606,7 @@ static int test_keyword_ctrl_get_bin_data(struct comp_dev *dev, ret = comp_data_blob_get_cmd(cd->model_handler, cdata, size); break; default: - comp_err(dev, "test_keyword_ctrl_get_bin_data(): unknown binary data type"); + comp_err(dev, "unknown binary data type"); break; } @@ -618,14 +618,14 @@ static int test_keyword_ctrl_get_data(struct comp_dev *dev, { int ret = 0; - comp_info(dev, "test_keyword_ctrl_get_data() size: %d", size); + comp_info(dev, "size: %d", size); switch (cdata->cmd) { case SOF_CTRL_CMD_BINARY: ret = test_keyword_ctrl_get_bin_data(dev, cdata, size); break; default: - comp_err(dev, "test_keyword_ctrl_get_data(): invalid cdata->cmd"); + comp_err(dev, "invalid cdata->cmd"); return -EINVAL; } @@ -638,7 +638,7 @@ static int test_keyword_cmd(struct comp_dev *dev, int cmd, void *data, { struct sof_ipc_ctrl_data *cdata = ASSUME_ALIGNED(data, 4); - comp_info(dev, "test_keyword_cmd()"); + comp_info(dev, "entry"); switch (cmd) { case COMP_CMD_SET_DATA: @@ -766,7 +766,7 @@ static struct comp_dev *test_keyword_new(const struct comp_driver *drv, comp_data_blob_handler_free(cd->model_handler); rfree(cd); fail: - rfree(dev); + comp_free_device(dev); return NULL; } @@ -774,7 +774,7 @@ static void test_keyword_free(struct comp_dev *dev) { struct comp_data *cd = comp_get_drvdata(dev); - comp_info(dev, "test_keyword_free()"); + comp_info(dev, "entry"); #if CONFIG_AMS int ret; @@ -782,13 +782,13 @@ static void test_keyword_free(struct comp_dev *dev) /* Unregister KD as AMS producer */ ret = ams_helper_unregister_producer(dev, cd->kpd_uuid_id); if (ret) - comp_err(dev, "test_keyword_free(): unregister ams error %d", ret); + comp_err(dev, "unregister ams error %d", ret); #endif ipc_msg_free(cd->msg); comp_data_blob_handler_free(cd->model_handler); rfree(cd); - rfree(dev); + comp_free_device(dev); } static int test_keyword_verify_params(struct comp_dev *dev, @@ -796,11 +796,11 @@ static int test_keyword_verify_params(struct comp_dev *dev, { int ret; - comp_dbg(dev, "test_keyword_verify_params()"); + comp_dbg(dev, "entry"); ret = comp_verify_params(dev, 0, params); if (ret < 0) { - comp_err(dev, "test_keyword_verify_params(): verification failed!"); + comp_err(dev, "verification failed!"); return ret; } @@ -821,7 +821,7 @@ static int test_keyword_params(struct comp_dev *dev, err = test_keyword_verify_params(dev, params); if (err < 0) { - comp_err(dev, "test_keyword_params(): pcm params verification failed."); + comp_err(dev, "pcm params verification failed."); return err; } @@ -834,7 +834,7 @@ static int test_keyword_params(struct comp_dev *dev, if (!cd->config.activation_threshold) { err = test_keyword_get_threshold(dev, params->sample_valid_bytes * 8); if (err < 0) { - comp_err(dev, "test_keyword_params(): unsupported sample width %u", + comp_err(dev, "unsupported sample width %u", params->sample_valid_bytes * 8); return err; } @@ -850,12 +850,12 @@ static int test_keyword_params(struct comp_dev *dev, rate = audio_stream_get_rate(&sourceb->stream); if (channels != 1) { - comp_err(dev, "test_keyword_params(): only single-channel supported"); + comp_err(dev, "only single-channel supported"); return -EINVAL; } if (!detector_is_sample_width_supported(frame_fmt)) { - comp_err(dev, "test_keyword_params(): only 16-bit format supported"); + comp_err(dev, "only 16-bit format supported"); return -EINVAL; } @@ -880,7 +880,7 @@ static int test_keyword_trigger(struct comp_dev *dev, int cmd) int ret; struct comp_data *cd = comp_get_drvdata(dev); - comp_info(dev, "test_keyword_trigger()"); + comp_info(dev, "entry"); ret = comp_set_state(dev, cmd); if (ret) @@ -903,7 +903,7 @@ static int test_keyword_copy(struct comp_dev *dev) struct comp_buffer *source; uint32_t frames; - comp_dbg(dev, "test_keyword_copy()"); + comp_dbg(dev, "entry"); /* keyword components will only ever have 1 source */ source = comp_dev_get_first_data_producer(dev); @@ -927,7 +927,7 @@ static int test_keyword_reset(struct comp_dev *dev) { struct comp_data *cd = comp_get_drvdata(dev); - comp_info(dev, "test_keyword_reset()"); + comp_info(dev, "entry"); cd->activation = 0; cd->detect_preamble = 0; @@ -950,11 +950,11 @@ static int test_keyword_prepare(struct comp_dev *dev) sample_width = cd->config.sample_width; #endif /* CONFIG_IPC_MAJOR_4 */ - comp_info(dev, "test_keyword_prepare()"); + comp_info(dev, "entry"); ret = test_keyword_params(dev, ¶ms); if (ret < 0) { - comp_err(dev, "test_keyword_prepare(): params config failed."); + comp_err(dev, "params config failed."); return ret; } @@ -969,7 +969,7 @@ static int test_keyword_prepare(struct comp_dev *dev) ret = test_keyword_get_threshold(dev, valid_bits); if (ret < 0) { - comp_err(dev, "test_keyword_prepare(): unsupported sample width %u", + comp_err(dev, "unsupported sample width %u", valid_bits); return ret; } diff --git a/src/samples/audio/kwd_nn_detect_test.c b/src/samples/audio/kwd_nn_detect_test.c index ef71a441580b..5d1f054bb040 100644 --- a/src/samples/audio/kwd_nn_detect_test.c +++ b/src/samples/audio/kwd_nn_detect_test.c @@ -84,18 +84,18 @@ void kwd_nn_detect_test(struct comp_dev *dev, result = kwd_nn_detect_postprocess(confidences); time_stop = sof_cycle_get_64(); comp_dbg(dev, - "KWD: kwd_nn_detect_test_copy() inference done in %u ms", + "KWD: inference done in %u ms", (unsigned int)k_cyc_to_ms_near64(time_stop - time_start)); switch (result) { case KWD_NN_YES_KEYWORD: case KWD_NN_NO_KEYWORD: if (result == KWD_NN_NO_KEYWORD) comp_info(dev, - "kwd_nn_detect_test_copy(): keyword NO detected confidence %d", + "keyword NO detected confidence %d", confidences[3]); else comp_info(dev, - "kwd_nn_detect_test_copy(): keyword YES detected confidences %d", + "keyword YES detected confidences %d", confidences[2]); /* The algorithm shall use cd->drain_req * to specify its draining size request. diff --git a/src/samples/audio/smart_amp_test.toml b/src/samples/audio/smart_amp_test.toml index f0f4fa9339dc..1576ed449bf6 100644 --- a/src/samples/audio/smart_amp_test.toml +++ b/src/samples/audio/smart_amp_test.toml @@ -23,8 +23,8 @@ REM # mod_cfg [PAR_0 PAR_1 PAR_2 PAR_3 IS_BYTES CPS IBS OBS MOD_FLAGS CPC OBLS] #ifdef CONFIG_METEORLAKE mod_cfg = [0, 0, 0, 0, 296, 5000000, 384, 384, 0, 5000, 0] -#elif defined(CONFIG_LUNARLAKE) || defined(CONFIG_SOC_INTEL_ACE30) || \ - defined(CONFIG_SOC_INTEL_ACE40) +#elif defined(CONFIG_LUNARLAKE) || defined(CONFIG_SOC_ACE30) || \ + defined(CONFIG_SOC_ACE40) mod_cfg = [0, 0, 0, 0, 4096, 1000000, 128, 128, 0, 0, 0] #endif diff --git a/src/samples/audio/smart_amp_test_ipc3.c b/src/samples/audio/smart_amp_test_ipc3.c index 610e38f6d753..3e69bb5f308e 100644 --- a/src/samples/audio/smart_amp_test_ipc3.c +++ b/src/samples/audio/smart_amp_test_ipc3.c @@ -88,7 +88,7 @@ static struct comp_dev *smart_amp_new(const struct comp_driver *drv, comp_data_blob_handler_free(sad->model_handler); rfree(sad); fail: - rfree(dev); + comp_free_device(dev); return NULL; } @@ -110,11 +110,11 @@ static int smart_amp_set_config(struct comp_dev *dev, ASSUME_ALIGNED(&cdata->data->data, sizeof(uint32_t)); bs = cfg->size; - comp_dbg(dev, "smart_amp_set_config(), actual blob size = %zu, expected blob size = %zu", + comp_dbg(dev, "actual blob size = %zu, expected blob size = %zu", bs, sizeof(struct sof_smart_amp_config)); if (bs != sizeof(struct sof_smart_amp_config)) { - comp_err(dev, "smart_amp_set_config(): invalid blob size, actual blob size = %zu, expected blob size = %zu", + comp_err(dev, "invalid blob size, actual blob size = %zu, expected blob size = %zu", bs, sizeof(struct sof_smart_amp_config)); return -EINVAL; } @@ -164,7 +164,7 @@ static int smart_amp_ctrl_get_bin_data(struct comp_dev *dev, case SOF_SMART_AMP_MODEL: return comp_data_blob_get_cmd(sad->model_handler, cdata, size); default: - comp_warn(dev, "smart_amp_ctrl_get_bin_data(): unknown binary data type"); + comp_warn(dev, "unknown binary data type"); break; } @@ -174,13 +174,13 @@ static int smart_amp_ctrl_get_bin_data(struct comp_dev *dev, static int smart_amp_ctrl_get_data(struct comp_dev *dev, struct sof_ipc_ctrl_data *cdata, int size) { - comp_info(dev, "smart_amp_ctrl_get_data() size: %d", size); + comp_info(dev, "size: %d", size); switch (cdata->cmd) { case SOF_CTRL_CMD_BINARY: return smart_amp_ctrl_get_bin_data(dev, cdata, size); default: - comp_err(dev, "smart_amp_ctrl_get_data(): invalid cdata->cmd"); + comp_err(dev, "invalid cdata->cmd"); return -EINVAL; } } @@ -193,7 +193,7 @@ static int smart_amp_ctrl_set_bin_data(struct comp_dev *dev, assert(sad); if (dev->state < COMP_STATE_READY) { - comp_err(dev, "smart_amp_ctrl_set_bin_data(): driver in init!"); + comp_err(dev, "driver in init!"); return -EBUSY; } @@ -203,7 +203,7 @@ static int smart_amp_ctrl_set_bin_data(struct comp_dev *dev, case SOF_SMART_AMP_MODEL: return comp_data_blob_set_cmd(sad->model_handler, cdata); default: - comp_warn(dev, "smart_amp_ctrl_set_bin_data(): unknown binary data type"); + comp_warn(dev, "unknown binary data type"); break; } @@ -215,19 +215,19 @@ static int smart_amp_ctrl_set_data(struct comp_dev *dev, { /* Check version from ABI header */ if (SOF_ABI_VERSION_INCOMPATIBLE(SOF_ABI_VERSION, cdata->data->abi)) { - comp_err(dev, "smart_amp_ctrl_set_data(): invalid version"); + comp_err(dev, "invalid version"); return -EINVAL; } switch (cdata->cmd) { case SOF_CTRL_CMD_ENUM: - comp_info(dev, "smart_amp_ctrl_set_data(), SOF_CTRL_CMD_ENUM"); + comp_info(dev, "SOF_CTRL_CMD_ENUM"); break; case SOF_CTRL_CMD_BINARY: - comp_info(dev, "smart_amp_ctrl_set_data(), SOF_CTRL_CMD_BINARY"); + comp_info(dev, "SOF_CTRL_CMD_BINARY"); return smart_amp_ctrl_set_bin_data(dev, cdata); default: - comp_err(dev, "smart_amp_ctrl_set_data(): invalid cdata->cmd"); + comp_err(dev, "invalid cdata->cmd"); return -EINVAL; } @@ -240,7 +240,7 @@ static int smart_amp_cmd(struct comp_dev *dev, int cmd, void *data, { struct sof_ipc_ctrl_data *cdata = ASSUME_ALIGNED(data, 4); - comp_info(dev, "smart_amp_cmd(): cmd: %d", cmd); + comp_info(dev, "cmd: %d", cmd); switch (cmd) { case COMP_CMD_SET_DATA: @@ -256,12 +256,12 @@ static void smart_amp_free(struct comp_dev *dev) { struct smart_amp_data *sad = comp_get_drvdata(dev); - comp_info(dev, "smart_amp_free()"); + comp_info(dev, "entry"); comp_data_blob_handler_free(sad->model_handler); rfree(sad); - rfree(dev); + comp_free_device(dev); } static int smart_amp_verify_params(struct comp_dev *dev, @@ -269,11 +269,11 @@ static int smart_amp_verify_params(struct comp_dev *dev, { int ret; - comp_info(dev, "smart_amp_verify_params()"); + comp_info(dev, "entry"); ret = comp_verify_params(dev, BUFF_PARAMS_CHANNELS, params); if (ret < 0) { - comp_err(dev, "smart_amp_verify_params() error: comp_verify_params() failed."); + comp_err(dev, "error: comp_verify_params() failed."); return ret; } @@ -285,13 +285,13 @@ static int smart_amp_params(struct comp_dev *dev, { int err; - comp_info(dev, "smart_amp_params()"); + comp_info(dev, "entry"); smart_amp_set_params(dev, params); err = smart_amp_verify_params(dev, params); if (err < 0) { - comp_err(dev, "smart_amp_params(): pcm params verification failed."); + comp_err(dev, "pcm params verification failed."); return err; } @@ -303,7 +303,7 @@ static int smart_amp_trigger(struct comp_dev *dev, int cmd) struct smart_amp_data *sad = comp_get_drvdata(dev); int ret = 0; - comp_info(dev, "smart_amp_trigger(), command = %u", cmd); + comp_info(dev, "command = %u", cmd); ret = comp_set_state(dev, cmd); @@ -340,7 +340,7 @@ static int smart_amp_process_s16(struct comp_dev *dev, int i; int j; - comp_dbg(dev, "smart_amp_process_s16()"); + comp_dbg(dev, "entry"); for (i = 0; i < frames; i++) { for (j = 0 ; j < sad->out_channels; j++) { @@ -372,7 +372,7 @@ static int smart_amp_process_s32(struct comp_dev *dev, int i; int j; - comp_dbg(dev, "smart_amp_process_s32()"); + comp_dbg(dev, "entry"); for (i = 0; i < frames; i++) { for (j = 0 ; j < sad->out_channels; j++) { @@ -419,7 +419,7 @@ static int smart_amp_copy(struct comp_dev *dev) uint32_t sink_bytes; uint32_t feedback_bytes; - comp_dbg(dev, "smart_amp_copy()"); + comp_dbg(dev, "entry"); /* available bytes and samples calculation */ avail_passthrough_frames = @@ -440,7 +440,7 @@ static int smart_amp_copy(struct comp_dev *dev) feedback_bytes = avail_frames * audio_stream_frame_bytes(&buf->stream); - comp_dbg(dev, "smart_amp_copy(): processing %d feedback frames (avail_passthrough_frames: %d)", + comp_dbg(dev, "processing %d feedback frames (avail_passthrough_frames: %d)", avail_frames, avail_passthrough_frames); /* perform buffer writeback after source_buf process */ @@ -476,7 +476,7 @@ static int smart_amp_copy(struct comp_dev *dev) static int smart_amp_reset(struct comp_dev *dev) { - comp_info(dev, "smart_amp_reset()"); + comp_info(dev, "entry"); comp_set_state(dev, COMP_TRIGGER_RESET); @@ -489,7 +489,7 @@ static int smart_amp_prepare(struct comp_dev *dev) struct comp_buffer *source_buffer; int ret; - comp_info(dev, "smart_amp_prepare()"); + comp_info(dev, "entry"); ret = comp_set_state(dev, COMP_TRIGGER_PREPARE); if (ret < 0) @@ -527,7 +527,7 @@ static int smart_amp_prepare(struct comp_dev *dev) sad->process = get_smart_amp_process(dev, sad->source_buf); if (!sad->process) { - comp_err(dev, "smart_amp_prepare(): get_smart_amp_process failed"); + comp_err(dev, "get_smart_amp_process failed"); ret = -EINVAL; } return ret; diff --git a/src/samples/audio/smart_amp_test_ipc4.c b/src/samples/audio/smart_amp_test_ipc4.c index 52e92699e669..89d5861fed71 100644 --- a/src/samples/audio/smart_amp_test_ipc4.c +++ b/src/samples/audio/smart_amp_test_ipc4.c @@ -62,14 +62,14 @@ static int smart_amp_init(struct processing_module *mod) comp_dbg(dev, "entry"); - sad = rzalloc(SOF_MEM_FLAG_USER, sizeof(*sad)); + sad = mod_zalloc(mod, sizeof(*sad)); if (!sad) return -ENOMEM; mod_data->private = sad; /* component model data handler */ - sad->model_handler = comp_data_blob_handler_new(dev); + sad->model_handler = mod_data_blob_handler_new(mod); if (!sad->model_handler) { ret = -ENOMEM; goto sad_fail; @@ -93,8 +93,8 @@ static int smart_amp_init(struct processing_module *mod) return 0; sad_fail: - comp_data_blob_handler_free(sad->model_handler); - rfree(sad); + mod_data_blob_handler_free(mod, sad->model_handler); + mod_free(mod, sad); return ret; } @@ -107,7 +107,7 @@ static int smart_amp_set_config(struct processing_module *mod, uint32_t config_i struct comp_dev *dev = mod->dev; struct smart_amp_data *sad = module_get_private_data(mod); - comp_dbg(dev, "smart_amp_set_config()"); + comp_dbg(dev, "entry"); switch (config_id) { case SMART_AMP_SET_MODEL: @@ -115,11 +115,11 @@ static int smart_amp_set_config(struct processing_module *mod, uint32_t config_i data_offset_size, fragment, fragment_size); case SMART_AMP_SET_CONFIG: if (fragment_size != sizeof(sad->config)) { - comp_err(dev, "smart_amp_set_config(): invalid config size %u, expect %u", + comp_err(dev, "invalid config size %u, expect %u", fragment_size, sizeof(struct sof_smart_amp_config)); return -EINVAL; } - comp_dbg(dev, "smart_amp_set_config(): config size = %u", fragment_size); + comp_dbg(dev, "config size = %u", fragment_size); memcpy_s(&sad->config, sizeof(sad->config), fragment, fragment_size); return 0; default: @@ -135,13 +135,13 @@ static inline int smart_amp_get_config(struct processing_module *mod, struct comp_dev *dev = mod->dev; int ret; - comp_dbg(dev, "smart_amp_get_config()"); + comp_dbg(dev, "entry"); switch (config_id) { case SMART_AMP_GET_CONFIG: ret = memcpy_s(fragment, fragment_size, &sad->config, sizeof(sad->config)); if (ret) { - comp_err(dev, "smart_amp_get_config(): wrong config size %d", + comp_err(dev, "wrong config size %d", fragment_size); return ret; } @@ -157,9 +157,9 @@ static int smart_amp_free(struct processing_module *mod) struct smart_amp_data *sad = module_get_private_data(mod); struct comp_dev *dev = mod->dev; - comp_dbg(dev, "smart_amp_free()"); - comp_data_blob_handler_free(sad->model_handler); - rfree(sad); + comp_dbg(dev, "entry"); + mod_data_blob_handler_free(mod, sad->model_handler); + mod_free(mod, sad); return 0; } diff --git a/src/schedule/CMakeLists.txt b/src/schedule/CMakeLists.txt index afa1cdc95324..622b75e46888 100644 --- a/src/schedule/CMakeLists.txt +++ b/src/schedule/CMakeLists.txt @@ -29,7 +29,7 @@ if (CONFIG_SOC_ACP_6_0) ) else() zephyr_library_sources( - ll_schedule.c + ll_schedule_xtos.c ) endif() else() @@ -38,12 +38,24 @@ else() ) endif() -zephyr_library_sources_ifdef(CONFIG_ZEPHYR_DP_SCHEDULER - zephyr_dp_schedule.c -) +if (CONFIG_SOF_USERSPACE_APPLICATION) + zephyr_library_sources_ifdef(CONFIG_ZEPHYR_DP_SCHEDULER + zephyr_dp_schedule.c + zephyr_dp_schedule_application.c + ) +else() + zephyr_library_sources_ifdef(CONFIG_ZEPHYR_DP_SCHEDULER + zephyr_dp_schedule.c + zephyr_dp_schedule_thread.c + ) +endif() zephyr_library_sources_ifdef(CONFIG_ZEPHYR_TWB_SCHEDULER zephyr_twb_schedule.c ) +zephyr_library_sources_ifdef(CONFIG_SOF_USERSPACE_LL + zephyr_ll_user.c +) + endif() diff --git a/src/schedule/Kconfig b/src/schedule/Kconfig index 99ca2861f650..f1383e70103f 100644 --- a/src/schedule/Kconfig +++ b/src/schedule/Kconfig @@ -15,6 +15,7 @@ config SCHEDULE_DMA_MULTI_CHANNEL config SCHEDULE_LL_STATS_LOG bool "Log low-latency scheduler statistics" default y + depends on !SOF_USERSPACE_LL help Log statistics from low-latency scheduler. This is a low overhead mechanism to gather average and worst-case execution times of diff --git a/src/schedule/README.md b/src/schedule/README.md new file mode 100644 index 000000000000..493ab092decc --- /dev/null +++ b/src/schedule/README.md @@ -0,0 +1,168 @@ +# SOF Scheduling Architecture + +This directory (`src/schedule`) contains the Sound Open Firmware (SOF) scheduling infrastructure, deeply integrated with the underlying Zephyr RTOS. SOF utilizes a multi-tiered scheduling approach to cater to different real-time constraints, ranging from hard real-time, low-latency requirements to more relaxed, compute-intensive data processing tasks. + +## Overview of Schedulers + +SOF categorizes tasks and assigns them to specialized schedulers: + +1. **LL (Low Latency) Scheduler**: For tasks that require strict, predictable, and lowest possible latency bound to hardware events (Timers, DMA interrupts). +2. **DP (Data Processing) Scheduler**: For compute-intensive components that process large chunks of data and operate on deadlines rather than strict cycles. +3. **TWB (Thread With Budget) Scheduler**: For tasks that are allotted a specific execution "budget" per scheduler tick, expressed in Zephyr time-slice ticks (e.g. derived from `ZEPHYR_TWB_BUDGET_MAX` in OS ticks), which the runtime then uses to limit and account for CPU cycles to prevent starvation. + +Below is a high-level component interaction architecture of the SOF scheduling domains on top of Zephyr. + +```mermaid +graph TD + subgraph Zephyr RTOS + Timer[Hardware Timer] + DMA[DMA Controller] + Threads[Zephyr Threads] + end + + subgraph Generic Scheduler API + API[schedule.c API] + end + + subgraph LL Scheduler Domain + LL[zephyr_ll.c] + LLDomain[zephyr_domain.c] + DMADomain[zephyr_dma_domain.c] + end + + subgraph DP Scheduler Domain + DP[zephyr_dp_schedule.c] + DPThread[zephyr_dp_schedule_thread.c] + end + + subgraph TWB Scheduler Domain + TWB[zephyr_twb_schedule.c] + end + + API --> LL + API --> DP + API --> TWB + + Timer -.->|Interrupt| LLDomain + DMA -.->|Interrupt| DMADomain + + LLDomain --> |Wakeup| LL + DMADomain --> |Wakeup| LL + LL -->|Runs tasks| Threads + + LL -->|NOTIFIER_ID_LL_POST_RUN| DP + DP -->|Recalculate Deadlines| DPThread + DPThread -->|Update Thread Deadlines| Threads + + LL -->|LL Tick Source| TWB + TWB -->|Update Time Slices| Threads +``` + +--- + +## 1. LL (Low Latency) Scheduler + +The LL scheduler (`zephyr_ll.c`) is designed for extreme low-latency processing. It bypasses complex generic Zephyr scheduling for its internal tasks to minimize overhead, executing a list of registered SOF tasks in a strict priority order. + +### Architecture + +- **Domain Threads**: The LL scheduler runs within a dedicated high-priority Zephyr thread (`ll_thread0`, etc.) pinned to each core (`zephyr_domain.c`). +- **Triggers**: It is woken up by a hardware timer (e.g., a 1ms tick) or directly by hardware DMA interrupts (`zephyr_dma_domain.c`). +- **Execution**: Once woken up, it locks the domain, iterates through all scheduled tasks in priority order, moves them to a temporary list, and calls their `.run()` functions. +- **Post-Run**: After all tasks execute, it triggers a `NOTIFIER_ID_LL_POST_RUN` event. This event cascades to wake up other dependent schedulers like DP and TWB. Event not run on LL userspace configuration. + +### Task State Diagram + +```mermaid +stateDiagram-v2 + [*] --> INIT: task_init + INIT --> QUEUED: schedule_task + QUEUED --> RUNNING: zephyr_ll_run (Timer/DMA Tick) + RUNNING --> RUNNING: return RESCHEDULE + RUNNING --> FREE: return COMPLETED + RUNNING --> CANCEL: task_cancel + + QUEUED --> CANCEL: task_cancel + CANCEL --> FREE: task_free + + FREE --> [*] +``` + +*(Note: State transitions handle Zephyr SMP locking to ensure a task is safely dequeued before state shifts)* + +--- + +## 2. DP (Data Processing) Scheduler + +The DP scheduler (`zephyr_dp_schedule.c`) manages asynchronous, compute-heavy tasks that process data when enough input is available and sufficient output space is free. It effectively relies on Zephyr's EDF (Earliest Deadline First) or standard preemptive scheduling capabilities. + +### Architecture + +- **Separate Threads**: Unlike LL which multiplexes tasks inside a single thread, **each DP task is assigned its own Zephyr thread**. +- **Wakeup Mechanism**: DP scheduling is evaluated at the end of each LL tick (`scheduler_dp_recalculate()`). +- **Readiness**: It checks if a component has sufficient data across its sinks and sources. If so, it transitions to `RUNNING` and signals the individual DP thread via a Zephyr Event object. +- **Deadlines**: Once ready, the DP thread computes its deadline absolute timestamp (`module_get_deadline()`) and calls `k_thread_absolute_deadline_set()`, submitting to the Zephyr kernel's EDF scheduler. + +### Task State Diagram + +```mermaid +stateDiagram-v2 + [*] --> INIT: task_init + INIT --> QUEUED: schedule_task + + note right of QUEUED + Wait for LL POST RUN event + to evaluate Readiness. + end note + + QUEUED --> RUNNING: resources ready (set priority/deadline) + RUNNING --> QUEUED: return RESCHEDULE (processed chunk) + RUNNING --> COMPLETED: return COMPLETED + RUNNING --> CANCEL: task_cancel + + QUEUED --> CANCEL: task_cancel + COMPLETED --> FREE: task_free + CANCEL --> FREE: task_free + + FREE --> [*] +``` + +--- + +## 3. TWB (Thread With Budget) Scheduler + +The TWB scheduler (`zephyr_twb_schedule.c`) provides execution budget limits for specific tasks to prevent them from starving the CPU. This is useful for intensive workloads that shouldn't disrupt the overall systemic low-latency chain. + +### Architecture + +- **Separate Threads**: Similar to DP, each TWB task executes in its own Zephyr thread. +- **Time Slicing**: When scheduled, the thread's execution budget is configured in OS ticks via `k_thread_time_slice_set()`. This tick-based budget is internally converted to hardware cycles for accounting against the CPU cycles actually consumed. +- **Budget Exhaustion**: If the thread consumes its budget (as measured in hardware cycles derived from the tick budget) before completing its work for the tick, a callback (`scheduler_twb_task_cb()`) is invoked by the Zephyr kernel. This callback immediately drops the thread's priority to a background level (`CONFIG_TWB_THREAD_LOW_PRIORITY`), preventing starvation of other threads. +- **Replenishment**: On the next LL tick (`scheduler_twb_ll_tick()`), the consumed hardware cycles are reset, and the thread's original priority and time slice are restored, granting it a fresh tick-based budget. + +### Task State Diagram + +```mermaid +stateDiagram-v2 + [*] --> INIT: task_init + INIT --> RUNNING: schedule_task (Thread Created) + + state RUNNING { + [*] --> HighPriority: Budget replenished + HighPriority --> LowPriority: Budget Exhausted (Callback) + LowPriority --> HighPriority: Next LL Tick + } + + RUNNING --> QUEUED: return RESCHEDULE + QUEUED --> RUNNING: Next LL Tick (Restore Priority) + + RUNNING --> CANCEL: task_cancel + QUEUED --> CANCEL: task_cancel + + RUNNING --> COMPLETED: return COMPLETED + + CANCEL --> FREE: task_free + COMPLETED --> FREE: task_free + + FREE --> [*] +``` diff --git a/src/schedule/ll_schedule.c b/src/schedule/ll_schedule_xtos.c similarity index 100% rename from src/schedule/ll_schedule.c rename to src/schedule/ll_schedule_xtos.c diff --git a/src/schedule/zephyr_dma_domain.c b/src/schedule/zephyr_dma_domain.c index 84165d363229..9bb76660ab29 100644 --- a/src/schedule/zephyr_dma_domain.c +++ b/src/schedule/zephyr_dma_domain.c @@ -461,8 +461,10 @@ static int zephyr_dma_domain_register(struct ll_schedule_domain *domain, 0, K_FOREVER); +#ifdef CONFIG_SCHED_CPU_MASK k_thread_cpu_mask_clear(thread); k_thread_cpu_mask_enable(thread, core); +#endif k_thread_name_set(thread, thread_name); k_thread_start(thread); diff --git a/src/schedule/zephyr_domain.c b/src/schedule/zephyr_domain.c index 6e03158f2bb9..ebf5aea8d4d2 100644 --- a/src/schedule/zephyr_domain.c +++ b/src/schedule/zephyr_domain.c @@ -7,6 +7,7 @@ #include <rtos/timer.h> #include <rtos/alloc.h> #include <rtos/symbol.h> +#include <rtos/userspace_helper.h> #include <sof/lib/cpu.h> #include <sof/lib/memory.h> #include <sof/lib/watchdog.h> @@ -38,17 +39,28 @@ LOG_MODULE_DECLARE(ll_schedule, CONFIG_SOF_LOG_LEVEL); #define ZEPHYR_LL_STACK_SIZE 8192 +#if CONFIG_SOF_USERSPACE_LL +K_THREAD_STACK_ARRAY_DEFINE(ll_sched_stack, CONFIG_CORE_COUNT, ZEPHYR_LL_STACK_SIZE); +#else K_KERNEL_STACK_ARRAY_DEFINE(ll_sched_stack, CONFIG_CORE_COUNT, ZEPHYR_LL_STACK_SIZE); +#endif struct zephyr_domain_thread { - struct k_thread ll_thread; - struct k_sem sem; + struct k_thread *ll_thread; + struct k_sem *sem; +#ifndef CONFIG_SOF_USERSPACE_LL + struct k_thread ll_thread_obj; + struct k_sem sem_obj; +#endif void (*handler)(void *arg); void *arg; }; struct zephyr_domain { - struct k_timer timer; + struct k_timer *timer; +#ifndef CONFIG_SOF_USERSPACE_LL + struct k_timer timer_obj; +#endif struct zephyr_domain_thread domain_thread[CONFIG_CORE_COUNT]; struct ll_schedule_domain *ll_domain; #if CONFIG_CROSS_CORE_STREAM @@ -77,16 +89,18 @@ static inline void stats_report(unsigned int runs, int core, unsigned int cycles static void zephyr_domain_thread_fn(void *p1, void *p2, void *p3) { struct zephyr_domain *zephyr_domain = p1; - int core = cpu_get_id(); + int core = POINTER_TO_INT(p2); struct zephyr_domain_thread *dt = zephyr_domain->domain_thread + core; #ifdef CONFIG_SCHEDULE_LL_STATS_LOG unsigned int runs = 0, overruns = 0, cycles_sum = 0, cycles_max = 0; unsigned int cycles0, cycles1, diff, timer_fired; #endif + tr_dbg(&ll_tr, "ll core %u thread starting", core); + for (;;) { /* immediately go to sleep, waiting to be woken up by the timer */ - k_sem_take(&dt->sem, K_FOREVER); + k_sem_take(dt->sem, K_FOREVER); #ifdef CONFIG_SCHEDULE_LL_STATS_LOG cycles0 = k_cycle_get_32(); @@ -120,7 +134,7 @@ static void zephyr_domain_thread_fn(void *p1, void *p2, void *p3) /* This handles wrapping correctly too */ diff = cycles1 - cycles0; - timer_fired = k_timer_status_get(&zephyr_domain->timer); + timer_fired = k_timer_status_get(zephyr_domain->timer); if (timer_fired > 1) overruns++; @@ -163,10 +177,13 @@ static void zephyr_domain_timer_fn(struct k_timer *timer) struct zephyr_domain_thread *dt = zephyr_domain->domain_thread + core; if (dt->handler) - k_sem_give(&dt->sem); + k_sem_give(dt->sem); } } +/* The normal kernel-space implementation for register/unregister */ +#ifndef CONFIG_SOF_USERSPACE_LL + static int zephyr_domain_register(struct ll_schedule_domain *domain, struct task *task, void (*handler)(void *arg), void *arg) @@ -188,31 +205,34 @@ static int zephyr_domain_register(struct ll_schedule_domain *domain, dt->arg = arg; /* 10 is rather random, we better not accumulate 10 missed timer interrupts */ - k_sem_init(&dt->sem, 0, 10); + k_sem_init(dt->sem, 0, 10); thread_name[sizeof(thread_name) - 2] = '0' + core; - thread = k_thread_create(&dt->ll_thread, - ll_sched_stack[core], - ZEPHYR_LL_STACK_SIZE, - zephyr_domain_thread_fn, zephyr_domain, NULL, NULL, - CONFIG_LL_THREAD_PRIORITY, 0, K_FOREVER); + /* not allocated dynamically when LL in kernel space */ + dt->ll_thread = &dt->ll_thread_obj; + + thread = k_thread_create(dt->ll_thread, ll_sched_stack[core], ZEPHYR_LL_STACK_SIZE, + zephyr_domain_thread_fn, zephyr_domain, INT_TO_POINTER(core), + NULL, CONFIG_LL_THREAD_PRIORITY, 0, K_FOREVER); +#ifdef CONFIG_SCHED_CPU_MASK k_thread_cpu_mask_clear(thread); k_thread_cpu_mask_enable(thread, core); +#endif k_thread_name_set(thread, thread_name); k_thread_start(thread); key = k_spin_lock(&domain->lock); - if (!k_timer_user_data_get(&zephyr_domain->timer)) { + if (!k_timer_user_data_get(zephyr_domain->timer)) { k_timeout_t start = {0}; - k_timer_init(&zephyr_domain->timer, zephyr_domain_timer_fn, NULL); - k_timer_user_data_set(&zephyr_domain->timer, zephyr_domain); + k_timer_init(zephyr_domain->timer, zephyr_domain_timer_fn, NULL); + k_timer_user_data_set(zephyr_domain->timer, zephyr_domain); - k_timer_start(&zephyr_domain->timer, start, K_USEC(LL_TIMER_PERIOD_US)); + k_timer_start(zephyr_domain->timer, start, K_USEC(LL_TIMER_PERIOD_US)); /* Enable the watchdog */ watchdog_enable(core); @@ -220,7 +240,7 @@ static int zephyr_domain_register(struct ll_schedule_domain *domain, k_spin_unlock(&domain->lock, key); - tr_info(&ll_tr, "zephyr_domain_register domain->type %d domain->clk %d domain->ticks_per_ms %d period %d", + tr_info(&ll_tr, "domain->type %d domain->clk %d domain->ticks_per_ms %d period %d", domain->type, domain->clk, domain->ticks_per_ms, (uint32_t)LL_TIMER_PERIOD_US); return 0; @@ -245,26 +265,162 @@ static int zephyr_domain_unregister(struct ll_schedule_domain *domain, /* Disable the watchdog */ watchdog_disable(core); - k_timer_stop(&zephyr_domain->timer); - k_timer_user_data_set(&zephyr_domain->timer, NULL); + k_timer_stop(zephyr_domain->timer); + k_timer_user_data_set(zephyr_domain->timer, NULL); } zephyr_domain->domain_thread[core].handler = NULL; k_spin_unlock(&domain->lock, key); - tr_info(&ll_tr, "zephyr_domain_unregister domain->type %d domain->clk %d", + tr_info(&ll_tr, "domain->type %d domain->clk %d", domain->type, domain->clk); /* * If running in the context of the domain thread, k_thread_abort() will * not return */ - k_thread_abort(&zephyr_domain->domain_thread[core].ll_thread); + k_thread_abort(zephyr_domain->domain_thread[core].ll_thread); + + tr_dbg(&ll_tr, "exit"); return 0; } +#else /* CONFIG_SOF_USERSPACE_LL */ + +/* User-space implementation for register/unregister */ + +static int zephyr_domain_register_user(struct ll_schedule_domain *domain, + struct task *task, + void (*handler)(void *arg), void *arg) +{ + struct zephyr_domain *zephyr_domain = ll_sch_domain_get_pdata(domain); + int core = cpu_get_id(); + struct zephyr_domain_thread *dt = zephyr_domain->domain_thread + core; + char thread_name[] = "ll_thread0"; + k_tid_t thread; + + tr_dbg(&ll_tr, "entry"); + + /* domain work only needs registered once on each core */ + if (dt->handler) + return 0; + + __ASSERT_NO_MSG(task->core == core); + + dt->handler = handler; + dt->arg = arg; + + /* 10 is rather random, we better not accumulate 10 missed timer interrupts */ + k_sem_init(dt->sem, 0, 10); + + thread_name[sizeof(thread_name) - 2] = '0' + core; + + if (!dt->ll_thread) { + /* Allocate thread structure dynamically */ + dt->ll_thread = k_object_alloc(K_OBJ_THREAD); + if (!dt->ll_thread) { + tr_err(&ll_tr, "Failed to allocate thread object for core %d", core); + dt->handler = NULL; + dt->arg = NULL; + return -ENOMEM; + } + + thread = k_thread_create(dt->ll_thread, ll_sched_stack[core], ZEPHYR_LL_STACK_SIZE, + zephyr_domain_thread_fn, zephyr_domain, + INT_TO_POINTER(core), NULL, CONFIG_LL_THREAD_PRIORITY, + K_USER, K_FOREVER); + +#ifdef CONFIG_SCHED_CPU_MASK + k_thread_cpu_mask_clear(thread); + k_thread_cpu_mask_enable(thread, core); +#endif + k_thread_name_set(thread, thread_name); + + k_mem_domain_add_thread(zephyr_ll_mem_domain(), thread); + k_thread_access_grant(thread, dt->sem, domain->lock, zephyr_domain->timer); + user_grant_dai_access_all(thread); + user_grant_dma_access_all(thread); + tr_dbg(&ll_tr, "granted LL access to thread %p (core %d)", thread, core); + + k_thread_start(thread); + } + + k_mutex_lock(domain->lock, K_FOREVER); + if (!k_timer_user_data_get(zephyr_domain->timer)) { + k_timeout_t start = {0}; + + k_timer_init(zephyr_domain->timer, zephyr_domain_timer_fn, NULL); + k_timer_user_data_set(zephyr_domain->timer, zephyr_domain); + + k_timer_start(zephyr_domain->timer, start, K_USEC(LL_TIMER_PERIOD_US)); + + /* Enable the watchdog */ + watchdog_enable(core); + } + + k_mutex_unlock(domain->lock); + + tr_info(&ll_tr, "domain->type %d domain->clk %d domain->ticks_per_ms %d period %d", + domain->type, domain->clk, domain->ticks_per_ms, (uint32_t)LL_TIMER_PERIOD_US); + + return 0; +} + +static int zephyr_domain_unregister_user(struct ll_schedule_domain *domain, + struct task *task, uint32_t num_tasks) +{ + struct zephyr_domain *zephyr_domain = ll_sch_domain_get_pdata(domain); + int core = task->core; + + tr_dbg(&ll_tr, "entry"); + + /* tasks still registered on this core */ + if (num_tasks) + return 0; + + k_mutex_lock(domain->lock, K_FOREVER); + + if (!atomic_read(&domain->total_num_tasks)) { + /* Disable the watchdog */ + watchdog_disable(core); + + k_timer_stop(zephyr_domain->timer); + k_timer_user_data_set(zephyr_domain->timer, NULL); + } + + zephyr_domain->domain_thread[core].handler = NULL; + + k_mutex_unlock(domain->lock); + + tr_info(&ll_tr, "domain->type %d domain->clk %d", + domain->type, domain->clk); + + /* Thread not removed here, only the timer is stopped. + * Thread object cleanup would require k_thread_abort() which cannot + * be safely called from this context. The thread remains allocated + * but dormant until next registration or system shutdown. + */ + + tr_dbg(&ll_tr, "exit"); + + return 0; +} + +struct k_thread *zephyr_domain_thread_tid(struct ll_schedule_domain *domain) +{ + struct zephyr_domain *zephyr_domain = ll_sch_domain_get_pdata(domain); + int core = cpu_get_id(); + struct zephyr_domain_thread *dt = zephyr_domain->domain_thread + core; + + tr_dbg(&ll_tr, "entry"); + + return dt->ll_thread; +} + +#endif /* CONFIG_SOF_USERSPACE_LL */ + #if CONFIG_CROSS_CORE_STREAM static void zephyr_domain_block(struct ll_schedule_domain *domain) { @@ -290,9 +446,14 @@ static void zephyr_domain_unblock(struct ll_schedule_domain *domain) } #endif -static const struct ll_schedule_domain_ops zephyr_domain_ops = { +APP_TASK_DATA static const struct ll_schedule_domain_ops zephyr_domain_ops = { +#ifdef CONFIG_SOF_USERSPACE_LL + .domain_register = zephyr_domain_register_user, + .domain_unregister = zephyr_domain_unregister_user, +#else .domain_register = zephyr_domain_register, .domain_unregister = zephyr_domain_unregister, +#endif #if CONFIG_CROSS_CORE_STREAM .domain_block = zephyr_domain_block, .domain_unblock = zephyr_domain_unblock, @@ -303,6 +464,8 @@ struct ll_schedule_domain *zephyr_domain_init(int clk) { struct ll_schedule_domain *domain; struct zephyr_domain *zephyr_domain; + struct zephyr_domain_thread *dt; + int core; domain = domain_init(SOF_SCHEDULE_LL_TIMER, clk, false, &zephyr_domain_ops); @@ -311,16 +474,37 @@ struct ll_schedule_domain *zephyr_domain_init(int clk) return NULL; } +#if CONFIG_SOF_USERSPACE_LL + zephyr_domain = sof_heap_alloc(zephyr_ll_user_heap(), + SOF_MEM_FLAG_USER | SOF_MEM_FLAG_COHERENT, + sizeof(*zephyr_domain), sizeof(void *)); + if (zephyr_domain) + memset(zephyr_domain, 0, sizeof(*zephyr_domain)); +#else zephyr_domain = rzalloc(SOF_MEM_FLAG_KERNEL | SOF_MEM_FLAG_COHERENT, sizeof(*zephyr_domain)); +#endif if (!zephyr_domain) { tr_err(&ll_tr, "domain allocation failed"); - rfree(domain); + k_panic(); return NULL; } zephyr_domain->ll_domain = domain; +#if CONFIG_SOF_USERSPACE_LL + /* Allocate timer dynamically for userspace access */ + zephyr_domain->timer = k_object_alloc(K_OBJ_TIMER); + if (!zephyr_domain->timer) { + tr_err(&ll_tr, "timer allocation failed"); + k_panic(); + return NULL; + } +#else + /* not allocated dynamically when LL in kernel space */ + zephyr_domain->timer = &zephyr_domain->timer_obj; +#endif + #if CONFIG_CROSS_CORE_STREAM atomic_set(&zephyr_domain->block, 0); k_mutex_init(&zephyr_domain->block_mutex); @@ -329,6 +513,20 @@ struct ll_schedule_domain *zephyr_domain_init(int clk) ll_sch_domain_set_pdata(domain, zephyr_domain); + for (core = 0; core < CONFIG_CORE_COUNT; core++) { + dt = zephyr_domain->domain_thread + core; +#ifdef CONFIG_SOF_USERSPACE_LL + dt->sem = k_object_alloc(K_OBJ_SEM); + if (!dt->sem) { + tr_err(&ll_tr, "Failed to allocate semaphore for core %d", core); + k_panic(); + } +#else + /* not allocated dynamically when LL in kernel space */ + dt->sem = &dt->sem_obj; +#endif + } + return domain; } @@ -342,6 +540,6 @@ bool ll_sch_is_current(void) struct zephyr_domain_thread *dt = zephyr_domain->domain_thread + cpu_get_id(); - return k_current_get() == &dt->ll_thread; + return k_current_get() == dt->ll_thread; } EXPORT_SYMBOL(ll_sch_is_current); diff --git a/src/schedule/zephyr_dp_schedule.c b/src/schedule/zephyr_dp_schedule.c index c4f3aaedeb37..fdb10c7201af 100644 --- a/src/schedule/zephyr_dp_schedule.c +++ b/src/schedule/zephyr_dp_schedule.c @@ -7,6 +7,7 @@ #include <sof/audio/component.h> #include <sof/audio/module_adapter/module/generic.h> +#include <sof/llext_manager.h> #include <rtos/task.h> #include <rtos/userspace_helper.h> #include <stdint.h> @@ -23,6 +24,8 @@ #include <sof/lib/notifier.h> #include <ipc4/base_fw.h> +#include "zephyr_dp_schedule.h" + #include <zephyr/kernel/thread.h> LOG_MODULE_REGISTER(dp_schedule, CONFIG_SOF_LOG_LEVEL); @@ -30,29 +33,6 @@ SOF_DEFINE_REG_UUID(dp_sched); DECLARE_TR_CTX(dp_tr, SOF_UUID(dp_sched_uuid), LOG_LEVEL_INFO); -struct scheduler_dp_data { - struct list_item tasks; /* list of active dp tasks */ - struct task ll_tick_src; /* LL task - source of DP tick */ -}; - -struct task_dp_pdata { - k_tid_t thread_id; /* zephyr thread ID */ - struct k_thread *thread; /* pointer to the kernels' thread object */ - struct k_thread thread_struct; /* thread object for kernel threads */ - uint32_t deadline_clock_ticks; /* dp module deadline in Zephyr ticks */ - k_thread_stack_t __sparse_cache *p_stack; /* pointer to thread stack */ - size_t stack_size; /* size of the stack in bytes */ - struct k_sem *sem; /* pointer to semaphore for task scheduling */ - struct k_sem sem_struct; /* semaphore for task scheduling for kernel threads */ - struct processing_module *mod; /* the module to be scheduled */ - uint32_t ll_cycles_to_start; /* current number of LL cycles till delayed start */ -}; - -#ifdef CONFIG_USERSPACE -/* Single CPU-wide lock - * The irq_lock is not available for USERSPACE (non-privileged) threads. - * Therefore semaphore is used to control critical section. - */ #define DP_LOCK_INIT(i, _) Z_SEM_INITIALIZER(dp_lock[i], 1, 1) #define DP_LOCK_INIT_LIST LISTIFY(CONFIG_MP_MAX_NUM_CPUS, DP_LOCK_INIT, (,)) @@ -63,43 +43,26 @@ struct task_dp_pdata { static STRUCT_SECTION_ITERABLE_ARRAY(k_sem, dp_lock, CONFIG_MP_MAX_NUM_CPUS) = { DP_LOCK_INIT_LIST }; -static inline unsigned int scheduler_dp_lock(uint16_t core) +/* Each per-core instance of DP scheduler has separate structures; hence, locks are per-core. + * + * TODO: consider using cpu_get_id() instead of supplying core as a parameter. + */ +unsigned int scheduler_dp_lock(uint16_t core) { k_sem_take(&dp_lock[core], K_FOREVER); return core; } -static inline void scheduler_dp_unlock(unsigned int key) +void scheduler_dp_unlock(unsigned int key) { k_sem_give(&dp_lock[key]); } -static inline void scheduler_dp_grant(k_tid_t thread_id, uint16_t core) +void scheduler_dp_grant(k_tid_t thread_id, uint16_t core) { k_thread_access_grant(thread_id, &dp_lock[core]); } -#else /* CONFIG_USERSPACE */ - -static inline void scheduler_dp_grant(k_tid_t thread_id, uint16_t core) -{ -} - -/* Single CPU-wide lock - * as each per-core instance if dp-scheduler has separate structures, it is enough to - * use irq_lock instead of cross-core spinlocks - */ -static inline unsigned int scheduler_dp_lock(uint16_t core) -{ - return irq_lock(); -} - -static inline void scheduler_dp_unlock(unsigned int key) -{ - irq_unlock(key); -} -#endif - /* dummy LL task - to start LL on secondary cores */ static enum task_state scheduler_dp_ll_tick_dummy(void *data) { @@ -258,73 +221,51 @@ static enum task_state scheduler_dp_ll_tick_dummy(void *data) * Now - pipeline is in stable state, CPU used almost in 100% (it would be 100% if DP3 * needed 1.2ms for processing - but the example would be too complicated) */ + void scheduler_dp_ll_tick(void *receiver_data, enum notify_id event_type, void *caller_data) { (void)receiver_data; (void)event_type; (void)caller_data; - struct list_item *tlist; - struct task *curr_task; - struct task_dp_pdata *pdata; unsigned int lock_key; struct scheduler_dp_data *dp_sch = scheduler_get_data(SOF_SCHEDULE_DP); + /* remember current timestamp as "NOW" */ + dp_sch->last_ll_tick_timestamp = k_cycle_get_32(); + lock_key = scheduler_dp_lock(cpu_get_id()); - list_for_item(tlist, &dp_sch->tasks) { - curr_task = container_of(tlist, struct task, list); - pdata = curr_task->priv_data; - struct processing_module *mod = pdata->mod; - - /* decrease number of LL ticks/cycles left till the module reaches its deadline */ - if (pdata->ll_cycles_to_start) { - pdata->ll_cycles_to_start--; - if (!pdata->ll_cycles_to_start) - /* deadline reached, clear startup delay flag. - * see dp_startup_delay comment for details - */ - mod->dp_startup_delay = false; - } - - if (curr_task->state == SOF_TASK_STATE_QUEUED) { - bool mod_ready; - - mod_ready = module_is_ready_to_process(mod, mod->sources, - mod->num_of_sources, - mod->sinks, - mod->num_of_sinks); - if (mod_ready) { - /* set a deadline for given num of ticks, starting now */ - k_thread_deadline_set(pdata->thread_id, - pdata->deadline_clock_ticks); - - /* trigger the task */ - curr_task->state = SOF_TASK_STATE_RUNNING; - k_sem_give(pdata->sem); - } - } - } + scheduler_dp_recalculate(dp_sch, event_type == NOTIFIER_ID_LL_POST_RUN); scheduler_dp_unlock(lock_key); } +#if CONFIG_SOF_USERSPACE_APPLICATION static int scheduler_dp_task_cancel(void *data, struct task *task) +{ + /* Should never be called */ + k_panic(); + return -EOPNOTSUPP; +} +#endif + +static int scheduler_dp_task_stop(void *data, struct task *task) { unsigned int lock_key; struct scheduler_dp_data *dp_sch = (struct scheduler_dp_data *)data; struct task_dp_pdata *pdata = task->priv_data; - /* this is asyn cancel - mark the task as canceled and remove it from scheduling */ lock_key = scheduler_dp_lock(cpu_get_id()); task->state = SOF_TASK_STATE_CANCEL; list_item_del(&task->list); - /* if there're no more DP task, stop LL tick source */ + /* if there're no more DP task, stop LL tick source */ if (list_is_empty(&dp_sch->tasks)) schedule_task_cancel(&dp_sch->ll_tick_src); - /* if the task is waiting on a semaphore - let it run and self-terminate */ - k_sem_give(pdata->sem); + /* if the task is waiting - let it run and self-terminate */ + k_event_set(pdata->event, DP_TASK_EVENT_CANCEL); + scheduler_dp_unlock(lock_key); /* wait till the task has finished, if there was any task created */ @@ -339,7 +280,7 @@ static int scheduler_dp_task_free(void *data, struct task *task) struct task_dp_pdata *pdata = task->priv_data; int ret; - scheduler_dp_task_cancel(data, task); + scheduler_dp_task_stop(data, task); /* the thread should be terminated at this moment, * abort is safe and will ensure no use after free @@ -349,79 +290,13 @@ static int scheduler_dp_task_free(void *data, struct task *task) pdata->thread_id = NULL; } -#ifdef CONFIG_USERSPACE - if (pdata->sem != &pdata->sem_struct) - k_object_free(pdata->sem); - if (pdata->thread != &pdata->thread_struct) - k_object_free(pdata->thread); -#endif - /* free task stack */ - ret = user_stack_free((__sparse_force void *)pdata->p_stack); + ret = user_stack_free(pdata->p_stack); pdata->p_stack = NULL; - /* all other memory has been allocated as a single malloc, will be freed later by caller */ - return ret; -} - -/* Thread function called in component context, on target core */ -static void dp_thread_fn(void *p1, void *p2, void *p3) -{ - struct task *task = p1; - (void)p2; - (void)p3; - struct task_dp_pdata *task_pdata = task->priv_data; - unsigned int lock_key; - enum task_state state; - bool task_stop; - - do { - /* - * the thread is started immediately after creation, it will stop on semaphore - * Semaphore will be released once the task is ready to process - */ - k_sem_take(task_pdata->sem, K_FOREVER); - - if (task->state == SOF_TASK_STATE_RUNNING) - state = task_run(task); - else - state = task->state; /* to avoid undefined variable warning */ - - lock_key = scheduler_dp_lock(task->core); - /* - * check if task is still running, may have been canceled by external call - * if not, set the state returned by run procedure - */ - if (task->state == SOF_TASK_STATE_RUNNING) { - task->state = state; - switch (state) { - case SOF_TASK_STATE_RESCHEDULE: - /* mark to reschedule, schedule time is already calculated */ - task->state = SOF_TASK_STATE_QUEUED; - break; - - case SOF_TASK_STATE_CANCEL: - case SOF_TASK_STATE_COMPLETED: - /* remove from scheduling */ - list_item_del(&task->list); - break; - - default: - /* illegal state, serious defect, won't happen */ - k_panic(); - } - } - - /* if true exit the while loop, terminate the thread */ - task_stop = task->state == SOF_TASK_STATE_COMPLETED || - task->state == SOF_TASK_STATE_CANCEL; - - scheduler_dp_unlock(lock_key); - } while (!task_stop); + scheduler_dp_internal_free(task); - /* call task_complete */ - if (task->state == SOF_TASK_STATE_COMPLETED) - task_complete(task); + return ret; } static int scheduler_dp_task_shedule(void *data, struct task *task, uint64_t start, @@ -430,11 +305,15 @@ static int scheduler_dp_task_shedule(void *data, struct task *task, uint64_t sta struct scheduler_dp_data *dp_sch = (struct scheduler_dp_data *)data; struct task_dp_pdata *pdata = task->priv_data; unsigned int lock_key; - uint64_t deadline_clock_ticks; - int ret; lock_key = scheduler_dp_lock(cpu_get_id()); + if (task_is_active(task)) { + scheduler_dp_unlock(lock_key); + tr_dbg(&dp_tr, "DP task already active"); + return 0; + } + if (task->state != SOF_TASK_STATE_INIT && task->state != SOF_TASK_STATE_CANCEL && task->state != SOF_TASK_STATE_COMPLETED) { @@ -442,39 +321,6 @@ static int scheduler_dp_task_shedule(void *data, struct task *task, uint64_t sta return -EINVAL; } - /* create a zephyr thread for the task */ - pdata->thread_id = k_thread_create(pdata->thread, (__sparse_force void *)pdata->p_stack, - pdata->stack_size, dp_thread_fn, task, NULL, NULL, - CONFIG_DP_THREAD_PRIORITY, task->flags, K_FOREVER); - if (!pdata->thread_id) { - tr_err(&dp_tr, "DP thread creation failed"); - scheduler_dp_unlock(lock_key); - return -ECHILD; - } - - k_thread_access_grant(pdata->thread_id, pdata->sem); - scheduler_dp_grant(pdata->thread_id, cpu_get_id()); - /* pin the thread to specific core */ - ret = k_thread_cpu_pin(pdata->thread_id, task->core); - if (ret < 0) { - tr_err(&dp_tr, "zephyr task pin to core failed"); - goto err; - } - -#ifdef CONFIG_USERSPACE - if (task->flags & K_USER) { - ret = user_memory_init_shared(pdata->thread_id, pdata->mod); - if (ret < 0) { - tr_err(&dp_tr, "user_memory_init_shared() failed"); - goto err; - } - } -#endif /* CONFIG_USERSPACE */ - - /* start the thread, it should immediately stop at a semaphore, so clean it */ - k_sem_init(pdata->sem, 0, 1); - k_thread_start(pdata->thread_id); - /* if there's no DP tasks scheduled yet, run ll tick source task */ if (list_is_empty(&dp_sch->tasks)) schedule_task(&dp_sch->ll_tick_src, 0, 0); @@ -483,30 +329,20 @@ static int scheduler_dp_task_shedule(void *data, struct task *task, uint64_t sta task->state = SOF_TASK_STATE_QUEUED; list_item_prepend(&task->list, &dp_sch->tasks); - deadline_clock_ticks = period * CONFIG_SYS_CLOCK_TICKS_PER_SEC; - /* period/deadline is in us - convert to seconds in next step - * or it always will be zero because of integer calculation - */ - deadline_clock_ticks /= 1000000; - - pdata->deadline_clock_ticks = deadline_clock_ticks; - pdata->ll_cycles_to_start = period / LL_TIMER_PERIOD_US; pdata->mod->dp_startup_delay = true; scheduler_dp_unlock(lock_key); tr_dbg(&dp_tr, "DP task scheduled with period %u [us]", (uint32_t)period); return 0; - -err: - /* cleanup - unlock and free all allocated resources */ - scheduler_dp_unlock(lock_key); - k_thread_abort(pdata->thread_id); - return ret; } static struct scheduler_ops schedule_dp_ops = { .schedule_task = scheduler_dp_task_shedule, +#if CONFIG_SOF_USERSPACE_APPLICATION .schedule_task_cancel = scheduler_dp_task_cancel, +#else + .schedule_task_cancel = scheduler_dp_task_stop, +#endif .schedule_task_free = scheduler_dp_task_free, }; @@ -537,107 +373,6 @@ int scheduler_dp_init(void) return 0; } -int scheduler_dp_task_init(struct task **task, - const struct sof_uuid_entry *uid, - const struct task_ops *ops, - struct processing_module *mod, - uint16_t core, - size_t stack_size, - uint32_t options) -{ - void __sparse_cache *p_stack = NULL; - struct sys_heap *const user_heap = mod->dev->drv->user_heap; - - /* memory allocation helper structure */ - struct { - struct task task; - struct task_dp_pdata pdata; - } *task_memory; - - int ret; - - /* must be called on the same core the task will be binded to */ - assert(cpu_get_id() == core); - - /* - * allocate memory - * to avoid multiple malloc operations allocate all required memory as a single structure - * and return pointer to task_memory->task - * As the structure contains zephyr kernel specific data, it must be located in - * shared, non cached memory - */ - task_memory = module_driver_heap_rzalloc(user_heap, SOF_MEM_FLAG_USER | - SOF_MEM_FLAG_COHERENT, sizeof(*task_memory)); - if (!task_memory) { - tr_err(&dp_tr, "memory alloc failed"); - return -ENOMEM; - } - - /* allocate stack - must be aligned and cached so a separate alloc */ - p_stack = user_stack_allocate(stack_size, options); - if (!p_stack) { - tr_err(&dp_tr, "stack alloc failed"); - ret = -ENOMEM; - goto err; - } - - /* internal SOF task init */ - ret = schedule_task_init(&task_memory->task, uid, SOF_SCHEDULE_DP, 0, ops->run, - mod, core, options); - if (ret < 0) { - tr_err(&dp_tr, "schedule_task_init failed"); - goto err; - } - - /* Point to ksem semaphore for kernel threads synchronization */ - /* It will be overwritten for K_USER threads to dynamic ones. */ - task_memory->pdata.sem = &task_memory->pdata.sem_struct; - task_memory->pdata.thread = &task_memory->pdata.thread_struct; - -#ifdef CONFIG_USERSPACE - if (options & K_USER) { - task_memory->pdata.sem = k_object_alloc(K_OBJ_SEM); - if (!task_memory->pdata.sem) { - tr_err(&dp_tr, "Semaphore object allocation failed"); - ret = -ENOMEM; - goto err; - } - - task_memory->pdata.thread = k_object_alloc(K_OBJ_THREAD); - if (!task_memory->pdata.thread) { - tr_err(&dp_tr, "Thread object allocation failed"); - ret = -ENOMEM; - goto err; - } - } -#endif /* CONFIG_USERSPACE */ - - /* initialize other task structures */ - task_memory->task.ops.complete = ops->complete; - task_memory->task.ops.get_deadline = ops->get_deadline; - task_memory->task.state = SOF_TASK_STATE_INIT; - task_memory->task.core = core; - - /* success, fill the structures */ - task_memory->task.priv_data = &task_memory->pdata; - task_memory->pdata.p_stack = p_stack; - task_memory->pdata.stack_size = stack_size; - task_memory->pdata.mod = mod; - *task = &task_memory->task; - - return 0; -err: - /* cleanup - free all allocated resources */ - if (user_stack_free((__sparse_force void *)p_stack)) - tr_err(&dp_tr, "user_stack_free failed!"); - - /* k_object_free looks for a pointer in the list, any invalid value can be passed */ - k_object_free(task_memory->pdata.sem); - k_object_free(task_memory->pdata.thread); - module_driver_heap_free(user_heap, task_memory); - return ret; -} - void scheduler_get_task_info_dp(struct scheduler_props *scheduler_props, uint32_t *data_off_size) { unsigned int lock_key; diff --git a/src/schedule/zephyr_dp_schedule.h b/src/schedule/zephyr_dp_schedule.h new file mode 100644 index 000000000000..c4f37fc812f2 --- /dev/null +++ b/src/schedule/zephyr_dp_schedule.h @@ -0,0 +1,63 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * Copyright(c) 2023 Intel Corporation. All rights reserved. + * + * Author: Marcin Szkudlinski + */ + +#include <rtos/task.h> +#include <sof/audio/module_adapter/module/generic.h> +#include <sof/list.h> +#include <sof/compiler_attributes.h> + +#include <zephyr/app_memory/mem_domain.h> + +#include <stdbool.h> +#include <stdint.h> + +struct scheduler_dp_data { + struct list_item tasks; /* list of active dp tasks */ + struct task ll_tick_src; /* LL task - source of DP tick */ + uint32_t last_ll_tick_timestamp;/* a timestamp as k_cycle_get_32 of last LL tick, + * "NOW" for DP deadline calculation + */ +}; + +enum sof_dp_part_type { + SOF_DP_PART_HEAP, + SOF_DP_PART_HEAP_CACHE, + SOF_DP_PART_CFG, + SOF_DP_PART_CFG_CACHE, + SOF_DP_PART_TYPE_COUNT, +}; + +struct ipc4_flat; + +struct task_dp_pdata { + k_tid_t thread_id; /* zephyr thread ID */ + struct k_thread *thread; /* pointer to the kernels' thread object */ + struct k_thread thread_struct; /* thread object for kernel threads */ + uint32_t deadline_clock_ticks; /* dp module deadline in Zephyr ticks */ + k_thread_stack_t *p_stack; /* pointer to thread stack */ + struct processing_module *mod; /* the module to be scheduled */ + uint32_t ll_cycles_to_start; /* current number of LL cycles till delayed start */ +#if CONFIG_SOF_USERSPACE_APPLICATION + struct ipc4_flat *flat; + struct k_mem_partition mpart[SOF_DP_PART_TYPE_COUNT]; +#endif + struct k_event *event; /* pointer to event for task scheduling */ + struct k_event event_struct; /* event for task scheduling for kernel threads */ +#if IS_ENABLED(CONFIG_SOF_USERSPACE_MOD_IPC_BY_DP_THREAD) + struct k_work_user *ipc_work_item; /* work item for IPC handling */ +#endif +}; + +void scheduler_dp_recalculate(struct scheduler_dp_data *dp_sch, bool is_ll_post_run); +void dp_thread_fn(void *p1, void *p2, void *p3); +unsigned int scheduler_dp_lock(uint16_t core); +void scheduler_dp_unlock(unsigned int key); +void scheduler_dp_grant(k_tid_t thread_id, uint16_t core); +int scheduler_dp_task_init(struct task **task, const struct sof_uuid_entry *uid, + const struct task_ops *ops, struct processing_module *mod, + uint16_t core, size_t stack_size, uint32_t options); +void scheduler_dp_internal_free(struct task *task); diff --git a/src/schedule/zephyr_dp_schedule_application.c b/src/schedule/zephyr_dp_schedule_application.c new file mode 100644 index 000000000000..06f2d7b46b12 --- /dev/null +++ b/src/schedule/zephyr_dp_schedule_application.c @@ -0,0 +1,600 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright(c) 2025 Intel Corporation. All rights reserved. + * + * Author: Marcin Szkudlinski + */ + +#include <rtos/task.h> + +#include <sof/audio/module_adapter/module/generic.h> +#include <sof/common.h> +#include <sof/list.h> +#include <sof/llext_manager.h> +#include <sof/objpool.h> +#include <sof/schedule/dp_schedule.h> +#include <sof/schedule/ll_schedule_domain.h> + +#include <zephyr/app_memory/mem_domain.h> +#include <zephyr/kernel.h> +#include <zephyr/logging/log.h> +#include <zephyr/sys/slist.h> + +#include <stdbool.h> +#include <stdint.h> + +#include "zephyr_dp_schedule.h" + +LOG_MODULE_DECLARE(dp_schedule, CONFIG_SOF_LOG_LEVEL); +extern struct tr_ctx dp_tr; + +static struct objpool_head dp_mdom_head = {.list = LIST_INIT(dp_mdom_head.list)}; + +/* Synchronization semaphore for the scheduler thread to wait for DP startup */ +#define DP_SYNC_INIT(i, _) Z_SEM_INITIALIZER(dp_sync[i], 0, 1) +#define DP_SYNC_INIT_LIST LISTIFY(CONFIG_CORE_COUNT, DP_SYNC_INIT, (,)) +static STRUCT_SECTION_ITERABLE_ARRAY(k_sem, dp_sync, CONFIG_CORE_COUNT) = { DP_SYNC_INIT_LIST }; + +struct ipc4_flat { + unsigned int cmd; + int ret; + union { + struct { + struct ipc4_module_bind_unbind bu; + enum bind_type type; + } bind; + struct { + unsigned int trigger_cmd; + enum ipc4_pipeline_state state; + int n_sources; + int n_sinks; + struct sof_source *source[CONFIG_MODULE_MAX_CONNECTIONS]; + struct sof_sink *sink[CONFIG_MODULE_MAX_CONNECTIONS]; + } pipeline_state; + }; +}; + +/* Pack IPC input data */ +static int ipc_thread_flatten(unsigned int cmd, const union scheduler_dp_thread_ipc_param *param, + struct ipc4_flat *flat) +{ + flat->cmd = cmd; + + /* + * FIXME: SOF_IPC4_MOD_* and SOF_IPC4_GLB_* aren't fully orthogonal, but + * so far none of the used ones overlap + */ + switch (cmd) { + case SOF_IPC4_MOD_BIND: + case SOF_IPC4_MOD_UNBIND: + flat->bind.bu = *param->bind_data->ipc4_data; + flat->bind.type = param->bind_data->bind_type; + break; + case SOF_IPC4_GLB_SET_PIPELINE_STATE: + flat->pipeline_state.trigger_cmd = param->pipeline_state.trigger_cmd; + switch (param->pipeline_state.trigger_cmd) { + case COMP_TRIGGER_STOP: + break; + case COMP_TRIGGER_PREPARE: + if (param->pipeline_state.n_sources > CONFIG_MODULE_MAX_CONNECTIONS || + param->pipeline_state.n_sinks > CONFIG_MODULE_MAX_CONNECTIONS) + return -ENOMEM; + + flat->pipeline_state.state = param->pipeline_state.state; + flat->pipeline_state.n_sources = param->pipeline_state.n_sources; + flat->pipeline_state.n_sinks = param->pipeline_state.n_sinks; + /* Up to 2 * CONFIG_MODULE_MAX_CONNECTIONS */ + memcpy(flat->pipeline_state.source, param->pipeline_state.sources, + flat->pipeline_state.n_sources * + sizeof(flat->pipeline_state.source[0])); + memcpy(flat->pipeline_state.sink, param->pipeline_state.sinks, + flat->pipeline_state.n_sinks * + sizeof(flat->pipeline_state.sink[0])); + } + } + + return 0; +} + +/* Unpack IPC data and execute a callback */ +static void ipc_thread_unflatten_run(struct processing_module *pmod, struct ipc4_flat *flat) +{ + const struct module_interface *const ops = pmod->dev->drv->adapter_ops; + + switch (flat->cmd) { + case SOF_IPC4_MOD_BIND: + if (ops->bind) { + struct bind_info bind_data = { + .ipc4_data = &flat->bind.bu, + .bind_type = flat->bind.type, + }; + + flat->ret = ops->bind(pmod, &bind_data); + } else { + flat->ret = 0; + } + break; + case SOF_IPC4_MOD_UNBIND: + if (ops->unbind) { + struct bind_info bind_data = { + .ipc4_data = &flat->bind.bu, + .bind_type = flat->bind.type, + }; + + flat->ret = ops->unbind(pmod, &bind_data); + } else { + flat->ret = 0; + } + break; + case SOF_IPC4_MOD_DELETE_INSTANCE: + flat->ret = ops->free(pmod); + break; + case SOF_IPC4_MOD_INIT_INSTANCE: + flat->ret = ops->init(pmod); + break; + case SOF_IPC4_GLB_SET_PIPELINE_STATE: + switch (flat->pipeline_state.trigger_cmd) { + case COMP_TRIGGER_STOP: + flat->ret = ops->reset(pmod); + break; + case COMP_TRIGGER_PREPARE: + flat->ret = ops->prepare(pmod, + flat->pipeline_state.source, + flat->pipeline_state.n_sources, + flat->pipeline_state.sink, + flat->pipeline_state.n_sinks); + } + } +} + +#define DP_THREAD_IPC_TIMEOUT K_MSEC(100) + +/* Signal an IPC and wait for processing completion */ +int scheduler_dp_thread_ipc(struct processing_module *pmod, unsigned int cmd, + const union scheduler_dp_thread_ipc_param *param) +{ + struct task_dp_pdata *pdata = pmod->dev->task->priv_data; + int ret; + + if (!pmod) { + tr_err(&dp_tr, "no thread module"); + return -EINVAL; + } + + if (cmd == SOF_IPC4_MOD_INIT_INSTANCE) { + /* Wait for the DP thread to start */ + ret = k_sem_take(&dp_sync[pmod->dev->task->core], DP_THREAD_IPC_TIMEOUT); + if (ret < 0) { + tr_err(&dp_tr, "Failed waiting for DP thread to start: %d", ret); + return ret; + } + } + + unsigned int lock_key = scheduler_dp_lock(pmod->dev->task->core); + + /* IPCs are serialised */ + pdata->flat->ret = -ENOSYS; + + ret = ipc_thread_flatten(cmd, param, pdata->flat); + if (!ret) + k_event_post(pdata->event, DP_TASK_EVENT_IPC); + + scheduler_dp_unlock(lock_key); + + if (!ret) { + /* Wait for completion */ + ret = k_sem_take(&dp_sync[cpu_get_id()], DP_THREAD_IPC_TIMEOUT); + if (ret < 0) + tr_err(&dp_tr, "Failed waiting for DP thread: %d", ret); + else + ret = pdata->flat->ret; + } + + return ret; +} + +/* Go through all DP tasks and recalculate their readiness and deadlines + * NOT REENTRANT, called with scheduler_dp_lock() held + */ +void scheduler_dp_recalculate(struct scheduler_dp_data *dp_sch, bool is_ll_post_run) +{ + struct list_item *tlist; + struct task *curr_task; + struct task_dp_pdata *pdata; + + list_for_item(tlist, &dp_sch->tasks) { + curr_task = container_of(tlist, struct task, list); + pdata = curr_task->priv_data; + struct processing_module *mod = pdata->mod; + bool trigger_task = false; + + /* decrease number of LL ticks/cycles left till the module reaches its deadline */ + if (mod->dp_startup_delay && is_ll_post_run && pdata->ll_cycles_to_start) { + pdata->ll_cycles_to_start--; + if (!pdata->ll_cycles_to_start) + /* delayed start complete, clear startup delay flag. + * see dp_startup_delay comment for details + */ + mod->dp_startup_delay = false; + } + + if (curr_task->state == SOF_TASK_STATE_QUEUED && + mod->dev->state >= COMP_STATE_ACTIVE) { + /* trigger the task */ + curr_task->state = SOF_TASK_STATE_RUNNING; + trigger_task = true; + k_event_post(pdata->event, DP_TASK_EVENT_PROCESS); + } + + if (curr_task->state == SOF_TASK_STATE_RUNNING) { + /* (re) calculate deadline for all running tasks */ + /* get module deadline in us */ + uint32_t deadline = module_get_deadline(mod); + + /* if a deadline cannot be calculated, use a fixed value relative to its + * first start + */ + if (deadline >= UINT32_MAX / 2 && trigger_task) + deadline = module_get_lpt(mod); + + if (deadline < UINT32_MAX) { + /* round down to 1ms */ + deadline = deadline / 1000; + + /* calculate number of ticks */ + deadline = deadline * (CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC / 1000); + + /* add to "NOW", overflows are OK */ + deadline = dp_sch->last_ll_tick_timestamp + deadline; + + /* set in Zephyr. Note that it may be in past, it does not matter, + * Zephyr still will schedule the thread with earlier deadline + * first + */ + k_thread_absolute_deadline_set(pdata->thread_id, deadline); + } + } + } +} + +/* Thread function called in component context, on target core */ +void dp_thread_fn(void *p1, void *p2, void *p3) +{ + struct task *task = p1; + struct task_dp_pdata *task_pdata = task->priv_data; + struct processing_module *pmod = task_pdata->mod; + unsigned int lock_key; + enum task_state state; + bool task_stop; + + ARG_UNUSED(p2); + ARG_UNUSED(p3); + + /* The IPC thread is waiting for the thread to be started, it can proceed now. */ + k_sem_give(&dp_sync[task->core]); + comp_info(pmod->dev, "userspace thread started"); + + do { + uint32_t mask = k_event_wait_safe(task_pdata->event, + DP_TASK_EVENT_PROCESS | DP_TASK_EVENT_CANCEL | + DP_TASK_EVENT_IPC, false, K_FOREVER); + + if (mask & DP_TASK_EVENT_IPC) { + /* handle IPC */ + tr_dbg(&dp_tr, "got IPC wake up for %p state %d", pmod, task->state); + ipc_thread_unflatten_run(pmod, task_pdata->flat); + k_sem_give(&dp_sync[task->core]); + } + + if (mask & DP_TASK_EVENT_PROCESS) { + bool ready; + + if (task->state == SOF_TASK_STATE_RUNNING) { + ready = module_is_ready_to_process(pmod, pmod->sources, + pmod->num_of_sources, + pmod->sinks, pmod->num_of_sinks); + } else { + state = task->state; /* to avoid undefined variable warning */ + ready = false; + } + + if (ready) { + if (pmod->dp_startup_delay && !task_pdata->ll_cycles_to_start) { + /* first time run - use delayed start */ + task_pdata->ll_cycles_to_start = + module_get_lpt(pmod) / LL_TIMER_PERIOD_US; + + /* in case LPT < LL cycle - delay at least cycle */ + if (!task_pdata->ll_cycles_to_start) + task_pdata->ll_cycles_to_start = 1; + } + + state = task_run(task); + } + + lock_key = scheduler_dp_lock(task->core); + /* + * check if task is still running, may have been canceled by external call + * if not, set the state returned by run procedure + */ + if (ready && task->state == SOF_TASK_STATE_RUNNING) { + task->state = state; + switch (state) { + case SOF_TASK_STATE_RESCHEDULE: + /* mark to reschedule, schedule time is already calculated */ + task->state = SOF_TASK_STATE_QUEUED; + break; + + case SOF_TASK_STATE_CANCEL: + case SOF_TASK_STATE_COMPLETED: + /* task already removed from scheduling */ + break; + + default: + /* illegal state, serious defect, won't happen */ + k_oops(); + } + } else { + task->state = SOF_TASK_STATE_QUEUED; + } + } else { + lock_key = scheduler_dp_lock(task->core); + } + + /* if true exit the while loop, terminate the thread */ + task_stop = task->state == SOF_TASK_STATE_COMPLETED || + task->state == SOF_TASK_STATE_CANCEL; + + scheduler_dp_unlock(lock_key); + } while (!task_stop); + + /* call task_complete */ + if (task->state == SOF_TASK_STATE_COMPLETED) + task_complete(task); +} + +/* + * Safe to call with partial successful initialisation, + * k_mem_domain_remove_partition() then just returns -ENOENT + */ +static void scheduler_dp_domain_free(struct task_dp_pdata *pdata) +{ + struct processing_module *pmod = pdata->mod; + struct k_mem_domain *mdom = pmod->mdom; + + llext_manager_rm_domain(pmod->dev->ipc_config.id, mdom); + + k_mem_domain_remove_partition(mdom, pdata->mpart + SOF_DP_PART_HEAP); + k_mem_domain_remove_partition(mdom, pdata->mpart + SOF_DP_PART_HEAP_CACHE); + k_mem_domain_remove_partition(mdom, pdata->mpart + SOF_DP_PART_CFG); + k_mem_domain_remove_partition(mdom, pdata->mpart + SOF_DP_PART_CFG_CACHE); + + if (mdom->num_partitions) { + /* Violation: all partitions should be freed by now */ + unsigned int n = mdom->num_partitions; + + LOG_WRN("%u domain partition(s) not freed, force-freeing them all", n); + + for (unsigned int i = 0; i < arch_mem_domain_max_partitions_get() && n; i++) { + struct k_mem_partition *dpart = &mdom->partitions[i]; + + if (dpart->size) { + k_mem_domain_remove_partition(mdom, dpart); + n--; + } + } + } + + /* All partitions removed, the domain can be freed now */ + pmod->mdom = NULL; + k_mem_domain_deinit(mdom); + objpool_free(&dp_mdom_head, mdom); +} + +/* memory allocation helper structure */ +struct scheduler_dp_task_memory { + struct task task; + struct task_dp_pdata pdata; + struct comp_driver drv; + struct ipc4_flat flat; +}; + +void scheduler_dp_internal_free(struct task *task) +{ + struct task_dp_pdata *pdata = task->priv_data; + + k_object_free(pdata->event); + k_object_free(pdata->thread); + scheduler_dp_domain_free(pdata); + + mod_free(pdata->mod, container_of(task, struct scheduler_dp_task_memory, task)); +} + +/* Called only in IPC context */ +int scheduler_dp_task_init(struct task **task, const struct sof_uuid_entry *uid, + const struct task_ops *ops, struct processing_module *mod, + uint16_t core, size_t stack_size, uint32_t options) +{ + k_thread_stack_t *p_stack; + struct scheduler_dp_task_memory *task_memory; + + int ret; + + /* must be called on the same core the task will be bound to */ + assert(cpu_get_id() == core); + + /* + * allocate memory + * to avoid multiple malloc operations allocate all required memory as a single structure + * and return pointer to task_memory->task + * As the structure contains zephyr kernel specific data, it must be located in + * shared, non cached memory + */ + task_memory = mod_alloc_ext(mod, SOF_MEM_FLAG_USER | SOF_MEM_FLAG_COHERENT, + sizeof(*task_memory), 0); + if (!task_memory) { + tr_err(&dp_tr, "memory alloc failed"); + return -ENOMEM; + } + + memset(task_memory, 0, sizeof(*task_memory)); + + task_memory->drv = *mod->dev->drv; + mod->dev->drv = &task_memory->drv; + + /* allocate stack - must be aligned and cached so a separate alloc */ + p_stack = user_stack_allocate(stack_size, options); + if (!p_stack) { + tr_err(&dp_tr, "stack alloc failed"); + ret = -ENOMEM; + goto e_tmem; + } + + struct task *ptask = &task_memory->task; + + /* internal SOF task init */ + ret = schedule_task_init(ptask, uid, SOF_SCHEDULE_DP, 0, ops->run, mod, core, options); + if (ret < 0) { + tr_err(&dp_tr, "schedule_task_init failed"); + goto e_stack; + } + + struct task_dp_pdata *pdata = &task_memory->pdata; + + pdata->flat = &task_memory->flat; + + pdata->event = k_object_alloc(K_OBJ_EVENT); + if (!pdata->event) { + tr_err(&dp_tr, "Event object allocation failed"); + ret = -ENOMEM; + goto e_stack; + } + + pdata->thread = k_object_alloc(K_OBJ_THREAD); + if (!pdata->thread) { + tr_err(&dp_tr, "Thread object allocation failed"); + ret = -ENOMEM; + goto e_kobj; + } + memset(&pdata->thread->arch, 0, sizeof(pdata->thread->arch)); + + /* success, fill the structures */ + pdata->p_stack = p_stack; + pdata->mod = mod; + + /* initialize other task structures */ + ptask->ops.complete = ops->complete; + ptask->ops.get_deadline = ops->get_deadline; + ptask->priv_data = pdata; + list_init(&ptask->list); + *task = ptask; + + /* create a zephyr thread for the task */ + pdata->thread_id = k_thread_create(pdata->thread, p_stack, + stack_size, dp_thread_fn, ptask, NULL, NULL, + CONFIG_DP_THREAD_PRIORITY, ptask->flags, K_FOREVER); + +#ifdef CONFIG_SCHED_CPU_MASK + /* pin the thread to specific core */ + ret = k_thread_cpu_pin(pdata->thread_id, core); + if (ret < 0) { + tr_err(&dp_tr, "zephyr task pin to core failed"); + goto e_thread; + } +#endif + + k_thread_access_grant(pdata->thread_id, pdata->event, &dp_sync[core]); + scheduler_dp_grant(pdata->thread_id, core); + + unsigned int pidx; + size_t size; + uintptr_t start; + bool on_pool = false; + struct k_mem_domain *mdom = objpool_alloc(&dp_mdom_head, sizeof(*mdom), + SOF_MEM_FLAG_COHERENT); + + if (!mdom) { + tr_err(&dp_tr, "objpool allocation failed"); + ret = -ENOMEM; + goto e_thread; + } + + on_pool = true; + + mod->mdom = mdom; + + if (!mdom->arch.ptables) { + ret = k_mem_domain_init(mdom, 0, NULL); + if (ret < 0) + goto e_thread; + } + + /* Module heap partition */ + mod_heap_info(mod, &size, &start); + pdata->mpart[SOF_DP_PART_HEAP] = (struct k_mem_partition){ + .start = (uintptr_t)sys_cache_uncached_ptr_get((void *)start), + .size = size, + .attr = K_MEM_PARTITION_P_RW_U_RW, + }; + pdata->mpart[SOF_DP_PART_HEAP_CACHE] = (struct k_mem_partition){ + .start = start, + .size = size, + .attr = K_MEM_PARTITION_P_RW_U_RW | XTENSA_MMU_CACHED_WB, + }; + /* Host mailbox partition for additional IPC parameters: read-only */ + pdata->mpart[SOF_DP_PART_CFG] = (struct k_mem_partition){ + .start = (uintptr_t)sys_cache_uncached_ptr_get((void *)MAILBOX_HOSTBOX_BASE), + .size = 4096, + .attr = K_MEM_PARTITION_P_RO_U_RO, + }; + pdata->mpart[SOF_DP_PART_CFG_CACHE] = (struct k_mem_partition){ + .start = (uintptr_t)MAILBOX_HOSTBOX_BASE, + .size = 4096, + .attr = K_MEM_PARTITION_P_RO_U_RO | XTENSA_MMU_CACHED_WB, + }; + + for (pidx = 0; pidx < SOF_DP_PART_TYPE_COUNT; pidx++) { + ret = k_mem_domain_add_partition(mdom, pdata->mpart + pidx); + if (ret < 0) + goto e_dom; + } + + ret = llext_manager_add_domain(mod->dev->ipc_config.id, mdom); + if (ret < 0) { + tr_err(&dp_tr, "failed to add LLEXT to domain %d", ret); + goto e_dom; + } + + /* + * Keep this call last, able to fail, otherwise domain will be removed + * before its thread + */ + ret = k_mem_domain_add_thread(mdom, pdata->thread_id); + if (ret < 0) { + tr_err(&dp_tr, "failed to add thread to domain %d", ret); + goto e_dom; + } + + /* start the thread, it should immediately stop at the semaphore */ + k_event_init(pdata->event); + k_thread_start(pdata->thread_id); + + return 0; + +e_dom: + scheduler_dp_domain_free(pdata); + on_pool = false; +e_thread: + if (on_pool) + objpool_free(&dp_mdom_head, mdom); + k_thread_abort(pdata->thread_id); +e_kobj: + /* k_object_free looks for a pointer in the list, any invalid value can be passed */ + k_object_free(pdata->thread); + k_object_free(pdata->event); +e_stack: + user_stack_free(p_stack); +e_tmem: + mod_free(mod, task_memory); + return ret; +} diff --git a/src/schedule/zephyr_dp_schedule_thread.c b/src/schedule/zephyr_dp_schedule_thread.c new file mode 100644 index 000000000000..7fbb3b6a5c21 --- /dev/null +++ b/src/schedule/zephyr_dp_schedule_thread.c @@ -0,0 +1,351 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright(c) 2025 Intel Corporation. All rights reserved. + * + * Author: Marcin Szkudlinski + * Adrian Warecki + */ + +#include <rtos/task.h> + +#include <sof/audio/module_adapter/module/generic.h> +#include <sof/audio/module_adapter/library/userspace_proxy.h> +#include <sof/audio/module_adapter/library/userspace_proxy_user.h> +#include <sof/common.h> +#include <sof/list.h> +#include <sof/schedule/ll_schedule_domain.h> +#include <sof/schedule/dp_schedule.h> +#include <sof/trace/trace.h> + +#include <zephyr/kernel.h> + +#include <stdbool.h> +#include <stdint.h> + +#include "zephyr_dp_schedule.h" + +LOG_MODULE_DECLARE(dp_schedule, CONFIG_SOF_LOG_LEVEL); +extern struct tr_ctx dp_tr; + +/* Go through all DP tasks and recalculate their readiness and deadlines + * NOT REENTRANT, should be called with scheduler_dp_lock() + */ +void scheduler_dp_recalculate(struct scheduler_dp_data *dp_sch, bool is_ll_post_run) +{ + struct list_item *tlist; + struct task *curr_task; + struct task_dp_pdata *pdata; + + list_for_item(tlist, &dp_sch->tasks) { + curr_task = container_of(tlist, struct task, list); + pdata = curr_task->priv_data; + struct processing_module *mod = pdata->mod; + bool trigger_task = false; + + /* decrease number of LL ticks/cycles left till the module reaches its deadline */ + if (mod->dp_startup_delay && is_ll_post_run && pdata->ll_cycles_to_start) { + pdata->ll_cycles_to_start--; + if (!pdata->ll_cycles_to_start) + /* delayed start complete, clear startup delay flag. + * see dp_startup_delay comment for details + */ + mod->dp_startup_delay = false; + } + + if (curr_task->state == SOF_TASK_STATE_QUEUED) { + bool mod_ready; + + mod_ready = module_is_ready_to_process(mod, mod->sources, + mod->num_of_sources, + mod->sinks, + mod->num_of_sinks); + if (mod_ready) { + /* trigger the task */ + curr_task->state = SOF_TASK_STATE_RUNNING; + if (mod->dp_startup_delay && !pdata->ll_cycles_to_start) { + /* first time run - use delayed start */ + pdata->ll_cycles_to_start = + module_get_lpt(pdata->mod) / LL_TIMER_PERIOD_US; + + /* in case LPT < LL cycle - delay at least cycle */ + if (!pdata->ll_cycles_to_start) + pdata->ll_cycles_to_start = 1; + } + trigger_task = true; + k_event_post(pdata->event, DP_TASK_EVENT_PROCESS); + } + } + if (curr_task->state == SOF_TASK_STATE_RUNNING) { + /* (re) calculate deadline for all running tasks */ + /* get module deadline in us*/ + uint32_t deadline = module_get_deadline(mod); + + /* if a deadline cannot be calculated, use a fixed value relative to its + * first start + */ + if (deadline >= UINT32_MAX / 2 && trigger_task) + deadline = module_get_lpt(mod); + + if (deadline < UINT32_MAX) { + /* round down to 1ms */ + deadline = deadline / 1000; + + /* calculate number of ticks */ + deadline = deadline * (CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC / 1000); + + /* add to "NOW", overflows are OK */ + deadline = dp_sch->last_ll_tick_timestamp + deadline; + + /* set in Zephyr. Note that it may be in past, it does not matter, + * Zephyr still will schedule the thread with earlier deadline + * first + */ + k_thread_absolute_deadline_set(pdata->thread_id, deadline); + } + } + } +} + +/* Thread function called in component context, on target core */ +void dp_thread_fn(void *p1, void *p2, void *p3) +{ + struct task *task = p1; + (void)p2; + (void)p3; + struct task_dp_pdata *task_pdata = task->priv_data; + struct scheduler_dp_data *dp_sch = NULL; + unsigned int lock_key; + enum task_state state; + bool task_stop = false; + uint32_t event; + + if (!(task->flags & K_USER)) + dp_sch = scheduler_get_data(SOF_SCHEDULE_DP); + + do { + /* + * the thread is started immediately after creation, it will stop on event. + * Event will be signalled once the task is ready to process. + */ + event = k_event_wait_safe(task_pdata->event, DP_TASK_EVENT_PROCESS | + DP_TASK_EVENT_CANCEL | DP_TASK_EVENT_IPC, false, + K_FOREVER); + +#if IS_ENABLED(CONFIG_SOF_USERSPACE_MOD_IPC_BY_DP_THREAD) + if (event & DP_TASK_EVENT_IPC) { + assert(task_pdata->ipc_work_item); + userspace_proxy_worker_handler(task_pdata->ipc_work_item); + } +#endif + + if (event & DP_TASK_EVENT_PROCESS) { + state = task->state; /* to avoid undefined variable warning */ + if (task->state == SOF_TASK_STATE_RUNNING && event & DP_TASK_EVENT_PROCESS) + state = task_run(task); + + lock_key = scheduler_dp_lock(task->core); + /* + * check if task is still running, may have been canceled by external call + * if not, set the state returned by run procedure + */ + if (task->state == SOF_TASK_STATE_RUNNING) { + task->state = state; + switch (state) { + case SOF_TASK_STATE_RESCHEDULE: + /* mark to reschedule, schedule time is already calculated + */ + task->state = SOF_TASK_STATE_QUEUED; + break; + + case SOF_TASK_STATE_CANCEL: + case SOF_TASK_STATE_COMPLETED: + /* remove from scheduling */ + list_item_del(&task->list); + break; + + default: + /* illegal state, serious defect, won't happen */ + k_panic(); + } + } + + /* if true exit the while loop, terminate the thread */ + task_stop = task->state == SOF_TASK_STATE_COMPLETED || + task->state == SOF_TASK_STATE_CANCEL; + /* recalculate all DP tasks readiness and deadlines + * TODO: it should be for all tasks, for all cores + * currently its limited to current core only + */ + if (dp_sch) + scheduler_dp_recalculate(dp_sch, false); + + scheduler_dp_unlock(lock_key); + } + + if (event & DP_TASK_EVENT_CANCEL) + task_stop = true; + } while (!task_stop); + + /* call task_complete */ + if (task->state == SOF_TASK_STATE_COMPLETED) + task_complete(task); +} + +int scheduler_dp_task_init(struct task **task, + const struct sof_uuid_entry *uid, + const struct task_ops *ops, + struct processing_module *mod, + uint16_t core, + size_t stack_size, + uint32_t options) +{ + void __sparse_cache *p_stack = NULL; + struct k_heap *const user_heap = mod->dev->drv->user_heap; + + /* memory allocation helper structure */ + struct { + struct task task; /* keep first, used for freeing below */ + struct task_dp_pdata pdata; + } *task_memory; + + int ret; + + /* must be called on the same core the task will be binded to */ + assert(cpu_get_id() == core); + + /* + * allocate memory + * to avoid multiple malloc operations allocate all required memory as a single structure + * and return pointer to task_memory->task + * As the structure contains zephyr kernel specific data, it must be located in + * shared, non cached memory + */ + task_memory = sof_heap_alloc(user_heap, SOF_MEM_FLAG_USER | SOF_MEM_FLAG_COHERENT, + sizeof(*task_memory), 0); + if (!task_memory) { + tr_err(&dp_tr, "memory alloc failed"); + return -ENOMEM; + } + + memset(task_memory, 0, sizeof(*task_memory)); + /* allocate stack - must be aligned and cached so a separate alloc */ + p_stack = user_stack_allocate(stack_size, options); + if (!p_stack) { + tr_err(&dp_tr, "stack alloc failed"); + ret = -ENOMEM; + goto err; + } + + /* internal SOF task init */ + ret = schedule_task_init(&task_memory->task, uid, SOF_SCHEDULE_DP, 0, ops->run, + mod, core, options); + if (ret < 0) { + tr_err(&dp_tr, "schedule_task_init failed"); + goto err; + } + + struct task_dp_pdata *pdata = &task_memory->pdata; + + /* Point to event_struct event for kernel threads synchronization */ + /* It will be overwritten for K_USER threads to dynamic ones. */ + pdata->event = &pdata->event_struct; + pdata->thread = &pdata->thread_struct; + +#ifdef CONFIG_USERSPACE + if (options & K_USER) { + pdata->event = k_object_alloc(K_OBJ_EVENT); + if (!pdata->event) { + tr_err(&dp_tr, "Event object allocation failed"); + ret = -ENOMEM; + goto err; + } + + pdata->thread = k_object_alloc(K_OBJ_THREAD); + if (!pdata->thread) { + tr_err(&dp_tr, "Thread object allocation failed"); + ret = -ENOMEM; + goto err; + } + } +#endif /* CONFIG_USERSPACE */ + + /* initialize other task structures */ + task_memory->task.ops.complete = ops->complete; + task_memory->task.ops.get_deadline = ops->get_deadline; + task_memory->task.state = SOF_TASK_STATE_INIT; + task_memory->task.core = core; + task_memory->task.priv_data = pdata; + k_event_init(pdata->event); + + /* success, fill the structures */ + pdata->p_stack = p_stack; + pdata->mod = mod; + + /* create a zephyr thread for the task */ + pdata->thread_id = k_thread_create(pdata->thread, (__sparse_force void *)p_stack, + stack_size, dp_thread_fn, &task_memory->task, NULL, NULL, + CONFIG_DP_THREAD_PRIORITY, task_memory->task.flags, + K_FOREVER); + +#ifdef CONFIG_SCHED_CPU_MASK + /* pin the thread to specific core */ + ret = k_thread_cpu_pin(pdata->thread_id, core); + if (ret < 0) { + tr_err(&dp_tr, "zephyr task pin to core failed"); + goto e_thread; + } +#endif + +#ifdef CONFIG_USERSPACE + if (options & K_USER) { + k_thread_access_grant(pdata->thread_id, pdata->event); + scheduler_dp_grant(pdata->thread_id, core); + + ret = k_mem_domain_add_thread(pdata->mod->user_ctx->comp_dom, pdata->thread_id); + if (ret < 0) { + tr_err(&dp_tr, "k_mem_domain_add_thread() failed %d", ret); + goto e_thread; + } + +#if IS_ENABLED(CONFIG_SOF_USERSPACE_MOD_IPC_BY_DP_THREAD) + pdata->ipc_work_item = userspace_proxy_register_ipc_handler(mod, pdata->event); + assert(pdata->ipc_work_item); +#endif + } +#endif /* CONFIG_USERSPACE */ + + /* start the thread, it should immediately stop at an event */ + k_thread_start(pdata->thread_id); + + /* success, fill output parameter */ + *task = &task_memory->task; + return 0; + +e_thread: + k_thread_abort(pdata->thread_id); +err: + /* cleanup - free all allocated resources */ + if (user_stack_free((__sparse_force void *)p_stack)) + tr_err(&dp_tr, "user_stack_free failed!"); + + /* k_object_free looks for a pointer in the list, any invalid value can be passed */ + k_object_free(task_memory->pdata.event); + k_object_free(task_memory->pdata.thread); + sof_heap_free(user_heap, task_memory); + return ret; +} + +void scheduler_dp_internal_free(struct task *task) +{ + struct task_dp_pdata *pdata = task->priv_data; + +#ifdef CONFIG_USERSPACE + if (pdata->event != &pdata->event_struct) + k_object_free(pdata->event); + if (pdata->thread != &pdata->thread_struct) + k_object_free(pdata->thread); +#endif + + /* task is the first member in task_memory above */ + sof_heap_free(pdata->mod->dev->drv->user_heap, task); +} diff --git a/src/schedule/zephyr_ll.c b/src/schedule/zephyr_ll.c index 851fd9a96a98..575a82d91dda 100644 --- a/src/schedule/zephyr_ll.c +++ b/src/schedule/zephyr_ll.c @@ -30,6 +30,10 @@ struct zephyr_ll { unsigned int n_tasks; /* task counter */ struct ll_schedule_domain *ll_domain; /* scheduling domain */ unsigned int core; /* core ID of this instance */ +#if CONFIG_SOF_USERSPACE_LL + struct k_mutex *lock; /* mutex for userspace */ +#endif + struct k_heap *heap; }; /* per-task scheduler data */ @@ -41,17 +45,26 @@ struct zephyr_ll_pdata { static void zephyr_ll_lock(struct zephyr_ll *sch, uint32_t *flags) { +#if CONFIG_SOF_USERSPACE_LL + k_mutex_lock(sch->lock, K_FOREVER); +#else irq_local_disable(*flags); +#endif } static void zephyr_ll_unlock(struct zephyr_ll *sch, uint32_t *flags) { +#if CONFIG_SOF_USERSPACE_LL + k_mutex_unlock(sch->lock); +#else irq_local_enable(*flags); +#endif } static void zephyr_ll_assert_core(const struct zephyr_ll *sch) { - assert(CONFIG_CORE_COUNT == 1 || sch->core == cpu_get_id()); + assert(CONFIG_CORE_COUNT == 1 || IS_ENABLED(CONFIG_SOF_USERSPACE_LL) || + sch->core == cpu_get_id()); } /* Locking: caller should hold the domain lock */ @@ -176,6 +189,8 @@ static void zephyr_ll_run(void *data) struct list_item *list, *tmp, task_head = LIST_INIT(task_head); uint32_t flags; + tr_dbg(&ll_tr, "entry"); + zephyr_ll_lock(sch, &flags); /* @@ -216,7 +231,7 @@ static void zephyr_ll_run(void *data) if (state != SOF_TASK_STATE_COMPLETED && state != SOF_TASK_STATE_RESCHEDULE) { tr_err(&ll_tr, - "zephyr_ll_run: invalid return state %u", + "invalid return state %u", state); state = SOF_TASK_STATE_RESCHEDULE; } @@ -248,8 +263,11 @@ static void zephyr_ll_run(void *data) zephyr_ll_unlock(sch, &flags); +#ifndef CONFIG_SOF_USERSPACE_LL + /* TODO: to be replaced with direct function calls */ notifier_event(sch, NOTIFIER_ID_LL_POST_RUN, NOTIFIER_TARGET_CORE_LOCAL, NULL, 0); +#endif } static void schedule_ll_callback(void *data) @@ -345,6 +363,15 @@ static int zephyr_ll_task_schedule_common(struct zephyr_ll *sch, struct task *ta tr_err(&ll_tr, "cannot register domain %d", ret); +#if CONFIG_SOF_USERSPACE_LL + k_thread_access_grant(zephyr_domain_thread_tid(sch->ll_domain), sch->lock); + + tr_dbg(&ll_tr, "granting access to lock %p for thread %p", sch->lock, + zephyr_domain_thread_tid(sch->ll_domain)); + tr_dbg(&ll_tr, "granting access to domain lock %p for thread %p", &sch->ll_domain->lock, + zephyr_domain_thread_tid(sch->ll_domain)); +#endif + return 0; } @@ -432,7 +459,7 @@ static int zephyr_ll_task_free(void *data, struct task *task) /* Protect against racing with schedule_task() */ zephyr_ll_lock(sch, &flags); task->priv_data = NULL; - rfree(pdata); + sof_heap_free(sch->heap, pdata); zephyr_ll_unlock(sch, &flags); return 0; @@ -493,14 +520,26 @@ static const struct scheduler_ops zephyr_ll_ops = { .scheduler_free = zephyr_ll_scheduler_free, }; +#if CONFIG_SOF_USERSPACE_LL +struct task *zephyr_ll_task_alloc(void) +{ + return sof_heap_alloc(zephyr_ll_user_heap(), SOF_MEM_FLAG_USER, + sizeof(struct task), sizeof(void *)); +} +#endif /* CONFIG_SOF_USERSPACE_LL */ + int zephyr_ll_task_init(struct task *task, const struct sof_uuid_entry *uid, uint16_t type, uint16_t priority, enum task_state (*run)(void *data), void *data, uint16_t core, uint32_t flags) { struct zephyr_ll_pdata *pdata; + struct k_heap *heap = sof_sys_heap_get(); + int alloc_flags = SOF_MEM_FLAG_KERNEL | SOF_MEM_FLAG_COHERENT; int ret; + tr_dbg(&ll_tr, "ll-scheduler task %p init", data); + if (task->priv_data) return -EEXIST; @@ -509,13 +548,18 @@ int zephyr_ll_task_init(struct task *task, if (ret < 0) return ret; - pdata = rzalloc(SOF_MEM_FLAG_KERNEL | SOF_MEM_FLAG_COHERENT, - sizeof(*pdata)); +#if CONFIG_SOF_USERSPACE_LL + heap = zephyr_ll_user_heap(); + alloc_flags = SOF_MEM_FLAG_USER | SOF_MEM_FLAG_COHERENT; +#endif + pdata = sof_heap_alloc(heap, alloc_flags, sizeof(*pdata), 0); if (!pdata) { tr_err(&ll_tr, "alloc failed"); return -ENOMEM; } + memset(pdata, 0, sizeof(*pdata)); + k_sem_init(&pdata->sem, 0, 1); task->priv_data = pdata; @@ -529,17 +573,43 @@ EXPORT_SYMBOL(zephyr_ll_task_init); int zephyr_ll_scheduler_init(struct ll_schedule_domain *domain) { struct zephyr_ll *sch; + int core = cpu_get_id(); + struct k_heap *heap = sof_sys_heap_get(); + int flags = SOF_MEM_FLAG_KERNEL | SOF_MEM_FLAG_COHERENT; + +#if CONFIG_SOF_USERSPACE_LL + heap = zephyr_ll_user_heap(); + flags = SOF_MEM_FLAG_USER; +#endif + tr_dbg(&ll_tr, "init on core %d", core); /* initialize per-core scheduler private data */ - sch = rzalloc(SOF_MEM_FLAG_KERNEL, sizeof(*sch)); + sch = sof_heap_alloc(heap, flags, sizeof(*sch), 0); if (!sch) { tr_err(&ll_tr, "allocation failed"); return -ENOMEM; } + + memset(sch, 0, sizeof(*sch)); + list_init(&sch->tasks); sch->ll_domain = domain; - sch->core = cpu_get_id(); + sch->core = core; sch->n_tasks = 0; + sch->heap = heap; + +#if CONFIG_SOF_USERSPACE_LL + /* Allocate mutex dynamically for userspace access */ + sch->lock = k_object_alloc(K_OBJ_MUTEX); + if (!sch->lock) { + tr_err(&ll_tr, "mutex allocation failed"); + sof_heap_free(sch->heap, sch); + return -ENOMEM; + } + k_mutex_init(sch->lock); + + tr_dbg(&ll_tr, "ll-scheduler init done, sch %p sch->lock %p", sch, sch->lock); +#endif scheduler_init(domain->type, &zephyr_ll_ops, sch); diff --git a/src/schedule/zephyr_ll_user.c b/src/schedule/zephyr_ll_user.c new file mode 100644 index 000000000000..aa33807b4aa3 --- /dev/null +++ b/src/schedule/zephyr_ll_user.c @@ -0,0 +1,85 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// Copyright(c) 2026 Intel Corporation. + +#include <zephyr/cache.h> +#include <zephyr/kernel.h> +#include <zephyr/logging/log.h> + +#include <rtos/userspace_helper.h> + +#include <sof/schedule/ll_schedule_domain.h> + +LOG_MODULE_DECLARE(ll_schedule, CONFIG_SOF_LOG_LEVEL); + +/** + * Memory resources for userspace LL scheduler + * + * This structure encapsulates the memory management resources required for the + * low-latency (LL) scheduler in userspace mode. It provides memory isolation + * and heap management for LL scheduler threads. + */ +struct zephyr_ll_mem_resources { + struct k_mem_domain mem_domain; /**< Memory domain for LL thread isolation */ + struct k_heap *heap; /**< Heap allocator for LL scheduler memory */ +}; + +static struct zephyr_ll_mem_resources ll_mem_resources; + +static struct k_heap *zephyr_ll_heap_init(void) +{ + struct k_heap *heap = module_driver_heap_init(); + struct k_mem_partition mem_partition; + int ret; + + /* + * TODO: the size of LL heap should be independently configurable and + * not tied to CONFIG_SOF_ZEPHYR_USERSPACE_MODULE_HEAP_SIZE + */ + + if (!heap) { + tr_err(&ll_tr, "heap alloc fail"); + k_panic(); + } + + /* Create memory partition for sch_data array */ + mem_partition.start = (uintptr_t)sys_cache_cached_ptr_get(heap->heap.init_mem); + mem_partition.size = heap->heap.init_bytes; + mem_partition.attr = K_MEM_PARTITION_P_RW_U_RW | XTENSA_MMU_CACHED_WB; + + ret = k_mem_domain_add_partition(&ll_mem_resources.mem_domain, &mem_partition); + tr_dbg(&ll_tr, "init ll heap %p, size %u (cached), ret %d", + (void *)mem_partition.start, heap->heap.init_bytes, ret); + if (ret) + k_panic(); + + mem_partition.start = (uintptr_t)sys_cache_uncached_ptr_get(heap->heap.init_mem); + mem_partition.attr = K_MEM_PARTITION_P_RW_U_RW; + ret = k_mem_domain_add_partition(&ll_mem_resources.mem_domain, &mem_partition); + tr_dbg(&ll_tr, "init ll heap %p, size %u (uncached), ret %d", + (void *)mem_partition.start, heap->heap.init_bytes, ret); + if (ret) + k_panic(); + + return heap; +} + +void zephyr_ll_user_resources_init(void) +{ + k_mem_domain_init(&ll_mem_resources.mem_domain, 0, NULL); + + ll_mem_resources.heap = zephyr_ll_heap_init(); + + /* attach common partition to LL domain */ + user_memory_attach_common_partition(zephyr_ll_mem_domain()); +} + +struct k_heap *zephyr_ll_user_heap(void) +{ + return ll_mem_resources.heap; +} + +struct k_mem_domain *zephyr_ll_mem_domain(void) +{ + return &ll_mem_resources.mem_domain; +} diff --git a/src/schedule/zephyr_twb_schedule.c b/src/schedule/zephyr_twb_schedule.c index 8ba28595cfa3..aee61360d697 100644 --- a/src/schedule/zephyr_twb_schedule.c +++ b/src/schedule/zephyr_twb_schedule.c @@ -441,6 +441,7 @@ int scheduler_twb_task_init(struct task **task, goto err; } +#ifdef CONFIG_SCHED_CPU_MASK /* pin the thread to specific core */ ret = k_thread_cpu_pin(thread_id, core); if (ret < 0) { @@ -448,6 +449,7 @@ int scheduler_twb_task_init(struct task **task, tr_err(&twb_tr, "zephyr task pin to core %d failed", core); goto err; } +#endif /* set the thread name */ if (name) { diff --git a/src/trace/dma-trace.c b/src/trace/dma-trace.c index 8779d71582ab..554204ac5c70 100644 --- a/src/trace/dma-trace.c +++ b/src/trace/dma-trace.c @@ -96,7 +96,7 @@ static enum task_state trace_work(void *data) size = dma_copy_to_host(&d->dc, config, d->posn.host_offset, buffer->r_ptr, size); if (size < 0) { - tr_err(&dt_tr, "trace_work(): dma_copy_to_host() failed"); + tr_err(&dt_tr, "dma_copy_to_host() failed"); goto out; } @@ -152,7 +152,7 @@ int dma_trace_init_early(struct sof *sof) sof->dmat = rzalloc(SOF_MEM_FLAG_USER | SOF_MEM_FLAG_COHERENT, sizeof(*sof->dmat)); if (!sof->dmat) { mtrace_printf(LOG_LEVEL_ERROR, - "dma_trace_init_early(): alloc failed"); + "alloc failed"); return -ENOMEM; } @@ -171,7 +171,7 @@ int dma_trace_init_early(struct sof *sof) err: mtrace_printf(LOG_LEVEL_ERROR, - "dma_trace_init_early() failed: %d", ret); + "failed: %d", ret); rfree(sof->dmat); sof->dmat = NULL; @@ -184,11 +184,11 @@ int dma_trace_init_complete(struct dma_trace_data *d) { int ret = 0; - tr_info(&dt_tr, "dma_trace_init_complete()"); + tr_info(&dt_tr, "entry"); if (!d) { mtrace_printf(LOG_LEVEL_ERROR, - "dma_trace_init_complete(): failed, no dma_trace_data"); + "failed, no dma_trace_data"); return -ENOMEM; } @@ -196,7 +196,7 @@ int dma_trace_init_complete(struct dma_trace_data *d) ret = dma_copy_new(&d->dc); if (ret < 0) { mtrace_printf(LOG_LEVEL_ERROR, - "dma_trace_init_complete(): dma_copy_new() failed: %d", ret); + "dma_copy_new() failed: %d", ret); goto out; } #if CONFIG_ZEPHYR_NATIVE_DRIVERS @@ -208,7 +208,7 @@ int dma_trace_init_complete(struct dma_trace_data *d) #endif if (ret < 0) { mtrace_printf(LOG_LEVEL_ERROR, - "dma_trace_init_complete(): dma_get_attribute() failed: %d", ret); + "dma_get_attribute() failed: %d", ret); goto out; } @@ -268,7 +268,7 @@ static int dma_trace_buffer_init(struct dma_trace_data *d) if (!d || !d->dc.dmac) { mtrace_printf(LOG_LEVEL_ERROR, - "dma_trace_buffer_init() failed, no DMAC! d=%p", d); + "failed, no DMAC! d=%p", d); return -ENODEV; } #if CONFIG_ZEPHYR_NATIVE_DRIVERS @@ -285,7 +285,7 @@ static int dma_trace_buffer_init(struct dma_trace_data *d) buf = rballoc_align(SOF_MEM_FLAG_USER | SOF_MEM_FLAG_DMA, DMA_TRACE_LOCAL_SIZE, addr_align); if (!buf) { - mtrace_printf(LOG_LEVEL_ERROR, "dma_trace_buffer_init(): alloc failed"); + mtrace_printf(LOG_LEVEL_ERROR, "alloc failed"); return -ENOMEM; } @@ -378,7 +378,7 @@ int dma_trace_enable(struct dma_trace_data *d) /* validate DMA context */ if (!d->dc.dmac || !d->dc.chan) { - tr_err_atomic(&dt_tr, "dma_trace_enable(): not valid"); + tr_err_atomic(&dt_tr, "not valid"); err = -ENODEV; goto out; } @@ -407,7 +407,7 @@ void dma_trace_disable(struct dma_trace_data *d) #if (CONFIG_HOST_PTABLE) /* Free up the host SG if it is set */ if (d->host_size) { - dma_sg_free(&d->config.elem_array); + dma_sg_free(NULL, &d->config.elem_array); d->host_size = 0; } #endif @@ -544,7 +544,7 @@ static void dtrace_add_event(const char *e, uint32_t length) trace_data->dropped_entries; trace_data->dropped_entries = 0; mtrace_printf(LOG_LEVEL_WARNING, - "dtrace_add_event(): number of dropped logs = %u", + "number of dropped logs = %u", tmp_dropped_entries); } } diff --git a/src/trace/trace.c b/src/trace/trace.c index f73474404e2b..bc17c848a902 100644 --- a/src/trace/trace.c +++ b/src/trace/trace.c @@ -500,7 +500,7 @@ void trace_init(struct sof *sof) { sof->trace = rzalloc(SOF_MEM_FLAG_USER | SOF_MEM_FLAG_COHERENT, sizeof(*sof->trace)); if (!sof->trace) { - mtrace_printf(LOG_LEVEL_ERROR, "trace_init(): allocation failed"); + mtrace_printf(LOG_LEVEL_ERROR, "allocation failed"); sof_panic(SOF_IPC_PANIC_IPC); } diff --git a/test/cmocka/src/CMakeLists.txt b/test/cmocka/src/CMakeLists.txt index c69f777bf575..2c244aea9d0d 100644 --- a/test/cmocka/src/CMakeLists.txt +++ b/test/cmocka/src/CMakeLists.txt @@ -2,5 +2,4 @@ add_subdirectory(audio) add_subdirectory(lib) -add_subdirectory(list) add_subdirectory(math) diff --git a/test/cmocka/src/audio/buffer/buffer_copy.c b/test/cmocka/src/audio/buffer/buffer_copy.c index ca2c6d6bacc5..f4e958d7fe19 100644 --- a/test/cmocka/src/audio/buffer/buffer_copy.c +++ b/test/cmocka/src/audio/buffer/buffer_copy.c @@ -29,8 +29,8 @@ static void test_audio_buffer_copy_underrun(void **state) .size = 256 }; - struct comp_buffer *src = buffer_new(&test_buf_desc, BUFFER_USAGE_NOT_SHARED); - struct comp_buffer *snk = buffer_new(&test_buf_desc, BUFFER_USAGE_NOT_SHARED); + struct comp_buffer *src = buffer_new(NULL, &test_buf_desc, BUFFER_USAGE_NOT_SHARED); + struct comp_buffer *snk = buffer_new(NULL, &test_buf_desc, BUFFER_USAGE_NOT_SHARED); assert_non_null(src); assert_non_null(snk); @@ -56,8 +56,8 @@ static void test_audio_buffer_copy_overrun(void **state) .size = 256 }; - struct comp_buffer *src = buffer_new(&test_buf_desc, BUFFER_USAGE_NOT_SHARED); - struct comp_buffer *snk = buffer_new(&test_buf_desc, BUFFER_USAGE_NOT_SHARED); + struct comp_buffer *src = buffer_new(NULL, &test_buf_desc, BUFFER_USAGE_NOT_SHARED); + struct comp_buffer *snk = buffer_new(NULL, &test_buf_desc, BUFFER_USAGE_NOT_SHARED); assert_non_null(src); assert_non_null(snk); @@ -85,8 +85,8 @@ static void test_audio_buffer_copy_success(void **state) .size = 256 }; - struct comp_buffer *src = buffer_new(&test_buf_desc, BUFFER_USAGE_NOT_SHARED); - struct comp_buffer *snk = buffer_new(&test_buf_desc, BUFFER_USAGE_NOT_SHARED); + struct comp_buffer *src = buffer_new(NULL, &test_buf_desc, BUFFER_USAGE_NOT_SHARED); + struct comp_buffer *snk = buffer_new(NULL, &test_buf_desc, BUFFER_USAGE_NOT_SHARED); assert_non_null(src); assert_non_null(snk); @@ -111,8 +111,8 @@ static void test_audio_buffer_copy_fit_space_constraint(void **state) .size = 256 }; - struct comp_buffer *src = buffer_new(&test_buf_desc, BUFFER_USAGE_NOT_SHARED); - struct comp_buffer *snk = buffer_new(&test_buf_desc, BUFFER_USAGE_NOT_SHARED); + struct comp_buffer *src = buffer_new(NULL, &test_buf_desc, BUFFER_USAGE_NOT_SHARED); + struct comp_buffer *snk = buffer_new(NULL, &test_buf_desc, BUFFER_USAGE_NOT_SHARED); assert_non_null(src); assert_non_null(snk); @@ -139,8 +139,8 @@ static void test_audio_buffer_copy_fit_no_space_constraint(void **state) .size = 256 }; - struct comp_buffer *src = buffer_new(&test_buf_desc, BUFFER_USAGE_NOT_SHARED); - struct comp_buffer *snk = buffer_new(&test_buf_desc, BUFFER_USAGE_NOT_SHARED); + struct comp_buffer *src = buffer_new(NULL, &test_buf_desc, BUFFER_USAGE_NOT_SHARED); + struct comp_buffer *snk = buffer_new(NULL, &test_buf_desc, BUFFER_USAGE_NOT_SHARED); assert_non_null(src); assert_non_null(snk); diff --git a/test/cmocka/src/audio/buffer/buffer_new.c b/test/cmocka/src/audio/buffer/buffer_new.c index b1e11fc1de50..1561c94ec675 100644 --- a/test/cmocka/src/audio/buffer/buffer_new.c +++ b/test/cmocka/src/audio/buffer/buffer_new.c @@ -27,7 +27,7 @@ static void test_audio_buffer_new(void **state) .size = 256 }; - struct comp_buffer *buf = buffer_new(&test_buf_desc, BUFFER_USAGE_NOT_SHARED); + struct comp_buffer *buf = buffer_new(NULL, &test_buf_desc, BUFFER_USAGE_NOT_SHARED); assert_non_null(buf); assert_int_equal(audio_stream_get_avail_bytes(&buf->stream), 0); diff --git a/test/cmocka/src/audio/buffer/buffer_wrap.c b/test/cmocka/src/audio/buffer/buffer_wrap.c index 675875fe527f..a1fc37383274 100644 --- a/test/cmocka/src/audio/buffer/buffer_wrap.c +++ b/test/cmocka/src/audio/buffer/buffer_wrap.c @@ -27,7 +27,7 @@ static void test_audio_buffer_write_fill_10_bytes_and_write_5(void **state) .size = 10 }; - struct comp_buffer *buf = buffer_new(&test_buf_desc, BUFFER_USAGE_NOT_SHARED); + struct comp_buffer *buf = buffer_new(NULL, &test_buf_desc, BUFFER_USAGE_NOT_SHARED); assert_non_null(buf); assert_int_equal(audio_stream_get_avail_bytes(&buf->stream), 0); diff --git a/test/cmocka/src/audio/buffer/buffer_write.c b/test/cmocka/src/audio/buffer/buffer_write.c index f6b9216cd3ef..3fd5504560d8 100644 --- a/test/cmocka/src/audio/buffer/buffer_write.c +++ b/test/cmocka/src/audio/buffer/buffer_write.c @@ -28,7 +28,7 @@ static void test_audio_buffer_write_10_bytes_out_of_256_and_read_back .size = 256 }; - struct comp_buffer *buf = buffer_new(&test_buf_desc, BUFFER_USAGE_NOT_SHARED); + struct comp_buffer *buf = buffer_new(NULL, &test_buf_desc, BUFFER_USAGE_NOT_SHARED); assert_non_null(buf); assert_int_equal(audio_stream_get_avail_bytes(&buf->stream), 0); @@ -63,7 +63,7 @@ static void test_audio_buffer_fill_10_bytes(void **state) .size = 10 }; - struct comp_buffer *buf = buffer_new(&test_buf_desc, BUFFER_USAGE_NOT_SHARED); + struct comp_buffer *buf = buffer_new(NULL, &test_buf_desc, BUFFER_USAGE_NOT_SHARED); assert_non_null(buf); assert_int_equal(audio_stream_get_avail_bytes(&buf->stream), 0); diff --git a/test/cmocka/src/audio/eq_fir/CMakeLists.txt b/test/cmocka/src/audio/eq_fir/CMakeLists.txt index 305a6846966c..226128b220d2 100644 --- a/test/cmocka/src/audio/eq_fir/CMakeLists.txt +++ b/test/cmocka/src/audio/eq_fir/CMakeLists.txt @@ -37,6 +37,7 @@ add_library(audio_for_eq_fir STATIC ${PROJECT_SOURCE_DIR}/src/ipc/ipc3/helper.c ${PROJECT_SOURCE_DIR}/src/ipc/ipc-common.c ${PROJECT_SOURCE_DIR}/src/ipc/ipc-helper.c + ${PROJECT_SOURCE_DIR}/src/lib/objpool.c ${PROJECT_SOURCE_DIR}/test/cmocka/src/notifier_mocks.c ${PROJECT_SOURCE_DIR}/src/audio/pipeline/pipeline-graph.c ${PROJECT_SOURCE_DIR}/src/audio/pipeline/pipeline-params.c diff --git a/test/cmocka/src/audio/eq_fir/eq_fir_process.c b/test/cmocka/src/audio/eq_fir/eq_fir_process.c index a7087fa7c470..5e3b1dd5b156 100644 --- a/test/cmocka/src/audio/eq_fir/eq_fir_process.c +++ b/test/cmocka/src/audio/eq_fir/eq_fir_process.c @@ -11,6 +11,7 @@ #include <sof/audio/component_ext.h> #include <eq_fir/eq_fir.h> #include <sof/audio/module_adapter/module/generic.h> +#include <ipc/control.h> #include "../../util.h" #include "../../../include/cmocka_chirp_2ch.h" @@ -69,23 +70,48 @@ static int setup_group(void **state) static struct sof_ipc_comp_process *create_eq_fir_comp_ipc(struct test_data *td) { struct sof_ipc_comp_process *ipc; - struct sof_eq_fir_config *eq; size_t ipc_size = sizeof(struct sof_ipc_comp_process); - struct sof_abi_hdr *blob = (struct sof_abi_hdr *)fir_coef_2ch; const struct sof_uuid uuid = SOF_REG_UUID(eq_fir); - ipc = calloc(1, ipc_size + blob->size + SOF_UUID_SIZE); + ipc = calloc(1, ipc_size + SOF_UUID_SIZE); memcpy_s(ipc + 1, SOF_UUID_SIZE, &uuid, SOF_UUID_SIZE); - eq = (struct sof_eq_fir_config *)((char *)(ipc + 1) + SOF_UUID_SIZE); ipc->comp.hdr.size = ipc_size + SOF_UUID_SIZE; ipc->comp.type = SOF_COMP_MODULE_ADAPTER; ipc->config.hdr.size = sizeof(struct sof_ipc_comp_config); - ipc->size = blob->size; + ipc->size = 0; ipc->comp.ext_data_length = SOF_UUID_SIZE; - memcpy_s(eq, blob->size, blob->data, blob->size); return ipc; } +static int eq_fir_send_config(struct processing_module *mod) +{ + const struct module_interface *const ops = mod->dev->drv->adapter_ops; + struct sof_abi_hdr *blob = (struct sof_abi_hdr *)fir_coef_2ch; + size_t cdata_size = sizeof(struct sof_ipc_ctrl_data) + + sizeof(struct sof_abi_hdr) + blob->size; + struct sof_ipc_ctrl_data *cdata; + int ret; + + cdata = calloc(1, cdata_size); + if (!cdata) + return -ENOMEM; + + cdata->cmd = SOF_CTRL_CMD_BINARY; + cdata->num_elems = blob->size; + cdata->data[0].magic = blob->magic; + cdata->data[0].type = blob->type; + cdata->data[0].size = blob->size; + cdata->data[0].abi = blob->abi; + memcpy_s(cdata->data[0].data, blob->size, blob->data, blob->size); + + ret = ops->set_configuration(mod, 0, MODULE_CFG_FRAGMENT_SINGLE, + blob->size, (const uint8_t *)cdata, + blob->size, NULL, 0); + + free(cdata); + return ret; +} + static void prepare_sink(struct test_data *td, struct processing_module *mod) { struct test_parameters *parameters = td->params; @@ -156,6 +182,10 @@ static int setup(void **state) dev->frames = params->frames; mod = comp_mod(dev); + ret = eq_fir_send_config(mod); + if (ret) + return ret; + prepare_sink(td, mod); prepare_source(td, mod); diff --git a/test/cmocka/src/audio/eq_iir/CMakeLists.txt b/test/cmocka/src/audio/eq_iir/CMakeLists.txt index aa704a1af92b..b5ff8770eec2 100644 --- a/test/cmocka/src/audio/eq_iir/CMakeLists.txt +++ b/test/cmocka/src/audio/eq_iir/CMakeLists.txt @@ -40,6 +40,7 @@ add_library(audio_for_eq_iir STATIC ${PROJECT_SOURCE_DIR}/src/ipc/ipc3/helper.c ${PROJECT_SOURCE_DIR}/src/ipc/ipc-common.c ${PROJECT_SOURCE_DIR}/src/ipc/ipc-helper.c + ${PROJECT_SOURCE_DIR}/src/lib/objpool.c ${PROJECT_SOURCE_DIR}/test/cmocka/src/notifier_mocks.c ${PROJECT_SOURCE_DIR}/src/audio/pipeline/pipeline-graph.c ${PROJECT_SOURCE_DIR}/src/audio/pipeline/pipeline-params.c diff --git a/test/cmocka/src/audio/eq_iir/eq_iir_process.c b/test/cmocka/src/audio/eq_iir/eq_iir_process.c index 9df4d17af864..756a4606c68b 100644 --- a/test/cmocka/src/audio/eq_iir/eq_iir_process.c +++ b/test/cmocka/src/audio/eq_iir/eq_iir_process.c @@ -13,6 +13,7 @@ #include <sof/audio/component_ext.h> #include <eq_iir/eq_iir.h> #include <sof/audio/module_adapter/module/generic.h> +#include <ipc/control.h> #include "../../util.h" #include "../../../include/cmocka_chirp_2ch.h" @@ -68,23 +69,48 @@ static int setup_group(void **state) static struct sof_ipc_comp_process *create_eq_iir_comp_ipc(struct test_data *td) { struct sof_ipc_comp_process *ipc; - struct sof_eq_iir_config *eq; size_t ipc_size = sizeof(struct sof_ipc_comp_process); - struct sof_abi_hdr *blob = (struct sof_abi_hdr *)iir_coef_2ch; const struct sof_uuid uuid = SOF_REG_UUID(eq_iir); - ipc = calloc(1, ipc_size + blob->size + SOF_UUID_SIZE); + ipc = calloc(1, ipc_size + SOF_UUID_SIZE); memcpy_s(ipc + 1, SOF_UUID_SIZE, &uuid, SOF_UUID_SIZE); - eq = (struct sof_eq_iir_config *)((char *)(ipc + 1) + SOF_UUID_SIZE); ipc->comp.hdr.size = ipc_size + SOF_UUID_SIZE; ipc->comp.type = SOF_COMP_MODULE_ADAPTER; ipc->config.hdr.size = sizeof(struct sof_ipc_comp_config); - ipc->size = blob->size; + ipc->size = 0; ipc->comp.ext_data_length = SOF_UUID_SIZE; - memcpy_s(eq, blob->size, blob->data, blob->size); return ipc; } +static int eq_iir_send_config(struct processing_module *mod) +{ + const struct module_interface *const ops = mod->dev->drv->adapter_ops; + struct sof_abi_hdr *blob = (struct sof_abi_hdr *)iir_coef_2ch; + size_t cdata_size = sizeof(struct sof_ipc_ctrl_data) + sizeof(struct sof_abi_hdr) + + blob->size; + struct sof_ipc_ctrl_data *cdata; + int ret; + + cdata = calloc(1, cdata_size); + if (!cdata) + return -ENOMEM; + + cdata->cmd = SOF_CTRL_CMD_BINARY; + cdata->num_elems = blob->size; + cdata->data[0].magic = blob->magic; + cdata->data[0].type = blob->type; + cdata->data[0].size = blob->size; + cdata->data[0].abi = blob->abi; + memcpy_s(cdata->data[0].data, blob->size, blob->data, blob->size); + + ret = ops->set_configuration(mod, 0, MODULE_CFG_FRAGMENT_SINGLE, + blob->size, (const uint8_t *)cdata, + blob->size, NULL, 0); + + free(cdata); + return ret; +} + static void prepare_sink(struct test_data *td, struct processing_module *mod) { struct test_parameters *parameters = td->params; @@ -155,6 +181,10 @@ static int setup(void **state) dev->frames = params->frames; mod = comp_mod(dev); + ret = eq_iir_send_config(mod); + if (ret) + return ret; + prepare_sink(td, mod); prepare_source(td, mod); diff --git a/test/cmocka/src/audio/mixer/CMakeLists.txt b/test/cmocka/src/audio/mixer/CMakeLists.txt index ea8cad0bd79e..c0dbb8a0a4fc 100644 --- a/test/cmocka/src/audio/mixer/CMakeLists.txt +++ b/test/cmocka/src/audio/mixer/CMakeLists.txt @@ -10,6 +10,7 @@ cmocka_test(mixer ${PROJECT_SOURCE_DIR}/src/ipc/ipc3/helper.c ${PROJECT_SOURCE_DIR}/src/ipc/ipc-common.c ${PROJECT_SOURCE_DIR}/src/ipc/ipc-helper.c + ${PROJECT_SOURCE_DIR}/src/lib/objpool.c ${PROJECT_SOURCE_DIR}/src/audio/module_adapter/module_adapter.c ${PROJECT_SOURCE_DIR}/src/audio/module_adapter/module_adapter_ipc3.c ${PROJECT_SOURCE_DIR}/src/audio/module_adapter/module/generic.c diff --git a/test/cmocka/src/audio/module_adapter_test.c b/test/cmocka/src/audio/module_adapter_test.c index 29140e4581e9..81ec6cadc1b7 100644 --- a/test/cmocka/src/audio/module_adapter_test.c +++ b/test/cmocka/src/audio/module_adapter_test.c @@ -77,7 +77,7 @@ void module_adapter_test_free(struct processing_module_test_data *test_data) } for (i = 0; i < test_data->num_sources; i++) { - free_test_sink(test_data->sources[i]); + free_test_source(test_data->sources[i]); test_free(test_data->input_buffers[i]); } diff --git a/test/cmocka/src/audio/mux/CMakeLists.txt b/test/cmocka/src/audio/mux/CMakeLists.txt index 67b10f77270d..a4a72613fd6b 100644 --- a/test/cmocka/src/audio/mux/CMakeLists.txt +++ b/test/cmocka/src/audio/mux/CMakeLists.txt @@ -25,6 +25,7 @@ add_library( ${PROJECT_SOURCE_DIR}/src/math/numbers.c ${PROJECT_SOURCE_DIR}/src/ipc/ipc3/helper.c ${PROJECT_SOURCE_DIR}/src/ipc/ipc-helper.c + ${PROJECT_SOURCE_DIR}/src/lib/objpool.c ${PROJECT_SOURCE_DIR}/test/cmocka/src/notifier_mocks.c ${PROJECT_SOURCE_DIR}/src/audio/module_adapter/module_adapter.c ${PROJECT_SOURCE_DIR}/src/audio/module_adapter/module_adapter_ipc3.c diff --git a/test/cmocka/src/audio/mux/demux_copy.c b/test/cmocka/src/audio/mux/demux_copy.c index 4934ab6dbdaa..8e331446bbf4 100644 --- a/test/cmocka/src/audio/mux/demux_copy.c +++ b/test/cmocka/src/audio/mux/demux_copy.c @@ -188,6 +188,9 @@ static int teardown_test_case(void **state) struct test_data *td = *((struct test_data **)state); int i; + rfree(td->mod->input_buffers); + rfree(td->mod->output_buffers); + free_test_source(td->source); for (i = 0; i < MUX_MAX_STREAMS; ++i) @@ -339,8 +342,10 @@ int main(void) cmocka_set_message_output(CM_OUTPUT_TAP); ret = cmocka_run_group_tests(tests, setup_group, NULL); - for (ti = 0; ti < ARRAY_SIZE(valid_formats) * ARRAY_SIZE(masks); ti++) + for (ti = 0; ti < ARRAY_SIZE(valid_formats) * ARRAY_SIZE(masks); ti++) { free(tests[ti].initial_state); + free((void *)tests[ti].name); + } return ret; } diff --git a/test/cmocka/src/audio/mux/mux_copy.c b/test/cmocka/src/audio/mux/mux_copy.c index 97b7be7b9076..66b21b0df27c 100644 --- a/test/cmocka/src/audio/mux/mux_copy.c +++ b/test/cmocka/src/audio/mux/mux_copy.c @@ -211,6 +211,9 @@ static int teardown_test_case(void **state) struct test_data *td = *((struct test_data **)state); int i; + rfree(td->mod->input_buffers); + rfree(td->mod->output_buffers); + for (i = 0; i < MUX_MAX_STREAMS; ++i) free_test_source(td->sources[i]); @@ -357,8 +360,10 @@ int main(void) cmocka_set_message_output(CM_OUTPUT_TAP); ret = cmocka_run_group_tests(tests, setup_group, NULL); - for (ti = 0; ti < ARRAY_SIZE(valid_formats) * ARRAY_SIZE(masks); ti++) + for (ti = 0; ti < ARRAY_SIZE(valid_formats) * ARRAY_SIZE(masks); ti++) { free(tests[ti].initial_state); + free((void *)tests[ti].name); + } return ret; } diff --git a/test/cmocka/src/audio/pipeline/pipeline_connect_upstream.c b/test/cmocka/src/audio/pipeline/pipeline_connect_upstream.c index dea28ce65401..e911f0238162 100644 --- a/test/cmocka/src/audio/pipeline/pipeline_connect_upstream.c +++ b/test/cmocka/src/audio/pipeline/pipeline_connect_upstream.c @@ -24,6 +24,7 @@ static int setup(void **state) static int teardown(void **state) { + free_standard_connect_objects(*state); free(*state); return 0; } diff --git a/test/cmocka/src/audio/pipeline/pipeline_connection_mocks.c b/test/cmocka/src/audio/pipeline/pipeline_connection_mocks.c index 35899b14b297..de05f8e3b284 100644 --- a/test/cmocka/src/audio/pipeline/pipeline_connection_mocks.c +++ b/test/cmocka/src/audio/pipeline/pipeline_connection_mocks.c @@ -30,7 +30,7 @@ struct pipeline_connect_data *get_standard_connect_objects(void) struct pipeline_connect_data *pipeline_connect_data = calloc (sizeof(struct pipeline_connect_data), 1); - struct pipeline *pipe = calloc(sizeof(struct pipeline), 1); + struct pipeline *pipe = &pipeline_connect_data->p; pipe->frames_per_sched = 5; pipe->pipeline_id = PIPELINE_ID_SAME; @@ -83,7 +83,14 @@ struct pipeline_connect_data *get_standard_connect_objects(void) comp_buffer_reset_source_list(buffer_2); pipeline_connect_data->b2 = buffer_2; - pipeline_connect_data->p = *pipe; - return pipeline_connect_data; } + +void free_standard_connect_objects(struct pipeline_connect_data *data) +{ + free(data->p.pipe_task); + free(data->p.sched_comp); + free(data->second); + free(data->b1); + free(data->b2); +} diff --git a/test/cmocka/src/audio/pipeline/pipeline_connection_mocks.h b/test/cmocka/src/audio/pipeline/pipeline_connection_mocks.h index 6a8c0fce1a12..60eedabbd43b 100644 --- a/test/cmocka/src/audio/pipeline/pipeline_connection_mocks.h +++ b/test/cmocka/src/audio/pipeline/pipeline_connection_mocks.h @@ -32,6 +32,7 @@ struct pipeline_connect_data { }; struct pipeline_connect_data *get_standard_connect_objects(void); +void free_standard_connect_objects(struct pipeline_connect_data *data); void cleanup_test_data(struct pipeline_connect_data *data); diff --git a/test/cmocka/src/audio/pipeline/pipeline_free.c b/test/cmocka/src/audio/pipeline/pipeline_free.c index d4d441012a29..9878f4f5e42a 100644 --- a/test/cmocka/src/audio/pipeline/pipeline_free.c +++ b/test/cmocka/src/audio/pipeline/pipeline_free.c @@ -23,7 +23,7 @@ #endif /* mock free() - dont free as we inspect contents */ -void rfree(void *ptr) +void sof_heap_free(struct k_heap *heap, void *addr) { } @@ -36,6 +36,7 @@ static int setup(void **state) static int teardown(void **state) { + free_standard_connect_objects(*state); free(*state); return 0; } diff --git a/test/cmocka/src/audio/pipeline/pipeline_new.c b/test/cmocka/src/audio/pipeline/pipeline_new.c index dda5d1a5c020..a68afe49e4be 100644 --- a/test/cmocka/src/audio/pipeline/pipeline_new.c +++ b/test/cmocka/src/audio/pipeline/pipeline_new.c @@ -49,12 +49,18 @@ static void test_audio_pipeline_pipeline_new_creation(void **state) struct pipeline_new_setup_data *test_data = *state; /*Testing component*/ - struct pipeline *result = pipeline_new(test_data->pipe_id, + struct pipeline *result = pipeline_new(NULL, + test_data->pipe_id, test_data->priority, - test_data->comp_id); + test_data->comp_id, + NULL); /*Pipeline should have been created so pointer can't be null*/ assert_non_null(result); + + rfree(result->msg->tx_data); + rfree(result->msg); + rfree(result); } int main(void) diff --git a/test/cmocka/src/audio/volume/CMakeLists.txt b/test/cmocka/src/audio/volume/CMakeLists.txt index d89927578222..0385441e5878 100644 --- a/test/cmocka/src/audio/volume/CMakeLists.txt +++ b/test/cmocka/src/audio/volume/CMakeLists.txt @@ -34,6 +34,7 @@ add_library(audio_for_volume STATIC ${PROJECT_SOURCE_DIR}/src/ipc/ipc3/helper.c ${PROJECT_SOURCE_DIR}/src/ipc/ipc-common.c ${PROJECT_SOURCE_DIR}/src/ipc/ipc-helper.c + ${PROJECT_SOURCE_DIR}/src/lib/objpool.c ${PROJECT_SOURCE_DIR}/test/cmocka/src/notifier_mocks.c ${PROJECT_SOURCE_DIR}/src/audio/pipeline/pipeline-graph.c ${PROJECT_SOURCE_DIR}/src/audio/pipeline/pipeline-params.c diff --git a/test/cmocka/src/audio/volume/volume_process.c b/test/cmocka/src/audio/volume/volume_process.c index 10afdd1276a3..389a4e75a503 100644 --- a/test/cmocka/src/audio/volume/volume_process.c +++ b/test/cmocka/src/audio/volume/volume_process.c @@ -315,7 +315,7 @@ int main(void) struct vol_test_parameters *parameters; uint32_t volume_values[] = {VOL_MAX, VOL_ZERO_DB, VOL_MINUS_80DB}; int num_tests = ARRAY_SIZE(test_parameters) * ARRAY_SIZE(volume_values); - int i, j; + int i, j, ret; parameters = test_calloc(num_tests, sizeof(struct vol_test_parameters)); for (i = 0; i < ARRAY_SIZE(test_parameters); i++) { @@ -338,5 +338,9 @@ int main(void) cmocka_set_message_output(CM_OUTPUT_TAP); - return cmocka_run_group_tests(tests, NULL, NULL); + ret = cmocka_run_group_tests(tests, NULL, NULL); + + test_free(parameters); + + return ret; } diff --git a/test/cmocka/src/common_mocks.c b/test/cmocka/src/common_mocks.c index cb116c5f8908..16fee8f2ff48 100644 --- a/test/cmocka/src/common_mocks.c +++ b/test/cmocka/src/common_mocks.c @@ -18,6 +18,7 @@ #include <user/trace.h> #include <rtos/spinlock.h> #include <sof/audio/component_ext.h> +#include <sof/audio/module_adapter/module/generic.h> #include <rtos/clk.h> #include <sof/lib/notifier.h> #include <rtos/wait.h> @@ -58,13 +59,19 @@ void WEAK *rzalloc(uint32_t flags, return calloc(bytes, 1); } -void WEAK *rbrealloc_align(void *ptr, uint32_t flags, - size_t bytes, size_t old_bytes, uint32_t alignment) +void WEAK *rmalloc_align(uint32_t flags, size_t bytes, uint32_t alignment) { (void)flags; - (void)old_bytes; + (void)alignment; - return realloc(ptr, bytes); + return malloc(bytes); +} + +void WEAK *rmalloc(uint32_t flags, size_t bytes) +{ + (void)flags; + + return malloc(bytes); } void WEAK rfree(void *ptr) @@ -72,6 +79,63 @@ void WEAK rfree(void *ptr) free(ptr); } +void WEAK *mod_balloc_align(struct processing_module *mod, size_t size, size_t alignment) +{ + void *ret; + (void)mod; + (void)alignment; + + ret = malloc(size); + + assert(ret); + + return ret; +} + +void WEAK *mod_alloc_ext(struct processing_module *mod, uint32_t flags, size_t size, + size_t alignment) +{ + void *ret; + (void)mod; + (void)flags; + (void)alignment; + + ret = malloc(size); + + assert(ret); + + return ret; +} + +int WEAK mod_free(struct processing_module *mod, const void *ptr) +{ + (void)mod; + free((void *)ptr); + return 0; +} + +struct k_heap * WEAK sof_sys_heap_get(void) +{ + return NULL; +} + +void WEAK *sof_heap_alloc(struct k_heap *heap, uint32_t flags, size_t bytes, + size_t alignment) +{ + (void)heap; + (void)flags; + (void)alignment; + + return malloc(bytes); +} + +void WEAK sof_heap_free(struct k_heap *heap, void *addr) +{ + (void)heap; + + free(addr); +} + int WEAK memcpy_s(void *dest, size_t dest_size, const void *src, size_t count) { diff --git a/test/cmocka/src/lib/fast-get/CMakeLists.txt b/test/cmocka/src/lib/fast-get/CMakeLists.txt index 640f821cf4f8..8449320f4e6b 100644 --- a/test/cmocka/src/lib/fast-get/CMakeLists.txt +++ b/test/cmocka/src/lib/fast-get/CMakeLists.txt @@ -2,6 +2,7 @@ cmocka_test(fast-get-tests fast-get-tests.c + ${PROJECT_SOURCE_DIR}/src/lib/objpool.c ${PROJECT_SOURCE_DIR}/zephyr/lib/fast-get.c ${PROJECT_SOURCE_DIR}/src/lib/alloc.c ${PROJECT_SOURCE_DIR}/src/platform/library/lib/memory.c diff --git a/test/cmocka/src/lib/fast-get/fast-get-tests.c b/test/cmocka/src/lib/fast-get/fast-get-tests.c index b7041abd827b..bd4a6fedfea3 100644 --- a/test/cmocka/src/lib/fast-get/fast-get-tests.c +++ b/test/cmocka/src/lib/fast-get/fast-get-tests.c @@ -72,12 +72,12 @@ static void test_simple_fast_get_put(void **state) (void)state; /* unused */ - ret = fast_get(testdata[0], sizeof(testdata[0])); + ret = fast_get(NULL, testdata[0], sizeof(testdata[0])); assert(ret); assert(!memcmp(ret, testdata[0], sizeof(testdata[0]))); - fast_put(ret); + fast_put(NULL, NULL, ret); } static void test_fast_get_size_missmatch_test(void **state) @@ -86,15 +86,15 @@ static void test_fast_get_size_missmatch_test(void **state) (void)state; /* unused */ - ret[0] = fast_get(testdata[0], sizeof(testdata[0])); + ret[0] = fast_get(NULL, testdata[0], sizeof(testdata[0])); assert(ret[0]); assert(!memcmp(ret[0], testdata[0], sizeof(testdata[0]))); - ret[1] = fast_get(testdata[0], sizeof(testdata[0]) + 1); + ret[1] = fast_get(NULL, testdata[0], sizeof(testdata[0]) + 1); assert(!ret[1]); - fast_put(ret); + fast_put(NULL, NULL, ret); } static void test_over_32_fast_gets_and_puts(void **state) @@ -105,13 +105,13 @@ static void test_over_32_fast_gets_and_puts(void **state) (void)state; /* unused */ for (i = 0; i < ARRAY_SIZE(copy); i++) - copy[i] = fast_get(testdata[i], sizeof(testdata[0])); + copy[i] = fast_get(NULL, testdata[i], sizeof(testdata[0])); for (i = 0; i < ARRAY_SIZE(copy); i++) assert(!memcmp(copy[i], testdata[i], sizeof(testdata[0]))); for (i = 0; i < ARRAY_SIZE(copy); i++) - fast_put(copy[i]); + fast_put(NULL, NULL, copy[i]); } static void test_fast_get_refcounting(void **state) @@ -121,10 +121,10 @@ static void test_fast_get_refcounting(void **state) (void)state; /* unused */ for (i = 0; i < ARRAY_SIZE(copy[0]); i++) - copy[0][i] = fast_get(testdata[i], sizeof(testdata[0])); + copy[0][i] = fast_get(NULL, testdata[i], sizeof(testdata[0])); for (i = 0; i < ARRAY_SIZE(copy[0]); i++) - copy[1][i] = fast_get(testdata[i], sizeof(testdata[0])); + copy[1][i] = fast_get(NULL, testdata[i], sizeof(testdata[0])); for (i = 0; i < ARRAY_SIZE(copy[0]); i++) assert(copy[0][i] == copy[1][i]); @@ -133,13 +133,13 @@ static void test_fast_get_refcounting(void **state) assert(!memcmp(copy[0][i], testdata[i], sizeof(testdata[0]))); for (i = 0; i < ARRAY_SIZE(copy[0]); i++) - fast_put(copy[0][i]); + fast_put(NULL, NULL, copy[0][i]); for (i = 0; i < ARRAY_SIZE(copy[0]); i++) assert(!memcmp(copy[1][i], testdata[i], sizeof(testdata[0]))); for (i = 0; i < ARRAY_SIZE(copy[0]); i++) - fast_put(copy[1][i]); + fast_put(NULL, NULL, copy[1][i]); } int main(void) diff --git a/test/cmocka/src/list/CMakeLists.txt b/test/cmocka/src/list/CMakeLists.txt deleted file mode 100644 index 5b8c5699512e..000000000000 --- a/test/cmocka/src/list/CMakeLists.txt +++ /dev/null @@ -1,29 +0,0 @@ -# SPDX-License-Identifier: BSD-3-Clause - -cmocka_test(list_init - list_init.c -) - -cmocka_test(list_is_empty - list_is_empty.c -) - -cmocka_test(list_item_append - list_item_append.c -) - -cmocka_test(list_item_del - list_item_del.c -) - -cmocka_test(list_item_is_last - list_item_is_last.c -) - -cmocka_test(list_item_prepend - list_item_prepend.c -) - -cmocka_test(list_item - list_item.c -) diff --git a/test/cmocka/src/list/list_init.c b/test/cmocka/src/list/list_init.c deleted file mode 100644 index 08ac2a9f0c4f..000000000000 --- a/test/cmocka/src/list/list_init.c +++ /dev/null @@ -1,47 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -// -// Copyright(c) 2018 Intel Corporation. All rights reserved. -// -// Author: Janusz Jankowski <janusz.jankowski@linux.intel.com> - -#include <sof/list.h> - -#include <stdarg.h> -#include <stddef.h> -#include <setjmp.h> -#include <stdint.h> -#include <cmocka.h> - -static void test_list_list_init_prev_equal_to_root(void **state) -{ - (void) state; /* unused */ - - struct list_item list = {.prev = NULL, .next = NULL}; - - list_init(&list); - - assert_ptr_equal(&list, list.prev); -} - -static void test_list_list_init_next_equal_to_root(void **state) -{ - (void) state; /* unused */ - - struct list_item list = {.prev = NULL, .next = NULL}; - - list_init(&list); - - assert_ptr_equal(&list, list.next); -} - -int main(void) -{ - const struct CMUnitTest tests[] = { - cmocka_unit_test(test_list_list_init_prev_equal_to_root), - cmocka_unit_test(test_list_list_init_next_equal_to_root), - }; - - cmocka_set_message_output(CM_OUTPUT_TAP); - - return cmocka_run_group_tests(tests, NULL, NULL); -} diff --git a/test/cmocka/src/list/list_is_empty.c b/test/cmocka/src/list/list_is_empty.c deleted file mode 100644 index 300967dd80ec..000000000000 --- a/test/cmocka/src/list/list_is_empty.c +++ /dev/null @@ -1,51 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -// -// Copyright(c) 2018 Intel Corporation. All rights reserved. -// -// Author: Janusz Jankowski <janusz.jankowski@linux.intel.com> - -#include <sof/list.h> - -#include <stdlib.h> -#include <stdarg.h> -#include <stddef.h> -#include <setjmp.h> -#include <stdint.h> -#include <cmocka.h> - -static void test_list_list_is_empty_when_empty_then_true(void **state) -{ - (void) state; /* unused */ - - struct list_item list; - - list_init(&list); - - assert_true(list_is_empty(&list)); -} - -static void test_list_list_is_empty_when_not_empty_then_false(void **state) -{ - (void) state; /* unused */ - - struct list_item list, item; - - list_init(&list); - list_init(&item); - - list_item_append(&item, &list); - - assert_false(list_is_empty(&list)); -} - -int main(void) -{ - const struct CMUnitTest tests[] = { - cmocka_unit_test(test_list_list_is_empty_when_empty_then_true), - cmocka_unit_test(test_list_list_is_empty_when_not_empty_then_false), - }; - - cmocka_set_message_output(CM_OUTPUT_TAP); - - return cmocka_run_group_tests(tests, NULL, NULL); -} diff --git a/test/cmocka/src/list/list_item.c b/test/cmocka/src/list/list_item.c deleted file mode 100644 index d8d0b45ab2a8..000000000000 --- a/test/cmocka/src/list/list_item.c +++ /dev/null @@ -1,45 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -// -// Copyright(c) 2018 Intel Corporation. All rights reserved. -// -// Author: Janusz Jankowski <janusz.jankowski@linux.intel.com> - -#include <sof/list.h> - -#include <stdlib.h> -#include <stdarg.h> -#include <stddef.h> -#include <setjmp.h> -#include <stdint.h> -#include <cmocka.h> - -struct test_list_container { - void *field1; - struct list_item list; - void *field2; -}; - -static void test_list_list_item_when_valid_offset_then_ptr_equal(void **state) -{ - (void) state; /* unused */ - - struct test_list_container container; - - list_init(&(container.list)); - - struct test_list_container *result_container = list_item( - &(container.list), struct test_list_container, list); - - assert_ptr_equal(result_container, &container); -} - -int main(void) -{ - const struct CMUnitTest tests[] = { - cmocka_unit_test(test_list_list_item_when_valid_offset_then_ptr_equal), - }; - - cmocka_set_message_output(CM_OUTPUT_TAP); - - return cmocka_run_group_tests(tests, NULL, NULL); -} diff --git a/test/cmocka/src/list/list_item_append.c b/test/cmocka/src/list/list_item_append.c deleted file mode 100644 index ff4ba2f7f68c..000000000000 --- a/test/cmocka/src/list/list_item_append.c +++ /dev/null @@ -1,123 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -// -// Copyright(c) 2018 Intel Corporation. All rights reserved. -// -// Author: Janusz Jankowski <janusz.jankowski@linux.intel.com> - -#include <sof/list.h> - -#include <stdlib.h> -#include <stdarg.h> -#include <stddef.h> -#include <setjmp.h> -#include <stdint.h> -#include <cmocka.h> - -struct test_data { - struct list_item *head; - struct list_item *tail_minus_1; - struct list_item *tail; -}; - -static int setup(void **state) -{ - struct test_data *data = malloc(sizeof(struct test_data)); - - if (!data) - return -1; - - data->head = malloc(sizeof(struct list_item)); - data->tail_minus_1 = malloc(sizeof(struct list_item)); - data->tail = malloc(sizeof(struct list_item)); - - if (!data->head || !data->tail_minus_1 - || !data->tail) { - free(data->head); - free(data->tail_minus_1); - free(data->tail); - - free(data); - - return -1; - } - - list_init(data->head); - list_init(data->tail_minus_1); - list_init(data->tail); - - list_item_append(data->tail_minus_1, data->head); - list_item_append(data->tail, data->head); - - *state = data; - return 0; -} - -static int teardown(void **state) -{ - struct test_data *data = *state; - - free(data->head); - free(data->tail_minus_1); - free(data->tail); - - free(data); - return 0; -} - -static void test_list_list_item_append_head_prev_is_tail(void **state) -{ - struct test_data *data = *state; - - assert_ptr_equal(data->head->prev, data->tail); -} - -static void test_list_list_item_append_head_next_is_tail_minus_1(void **state) -{ - struct test_data *data = *state; - - assert_ptr_equal(data->head->next, data->tail_minus_1); -} - -static void test_list_list_item_append_tail_minus_1_prev_is_head(void **state) -{ - struct test_data *data = *state; - - assert_ptr_equal(data->tail_minus_1->prev, data->head); -} - -static void test_list_list_item_append_tail_minus_1_next_is_tail(void **state) -{ - struct test_data *data = *state; - - assert_ptr_equal(data->tail_minus_1->next, data->tail); -} - -static void test_list_list_item_append_tail_prev_is_tail_minus_1(void **state) -{ - struct test_data *data = *state; - - assert_ptr_equal(data->tail->prev, data->tail_minus_1); -} - -static void test_list_list_item_append_tail_next_is_head(void **state) -{ - struct test_data *data = *state; - - assert_ptr_equal(data->tail->next, data->head); -} - -int main(void) -{ - const struct CMUnitTest tests[] = { - cmocka_unit_test(test_list_list_item_append_head_prev_is_tail), - cmocka_unit_test(test_list_list_item_append_head_next_is_tail_minus_1), - cmocka_unit_test(test_list_list_item_append_tail_minus_1_prev_is_head), - cmocka_unit_test(test_list_list_item_append_tail_minus_1_next_is_tail), - cmocka_unit_test(test_list_list_item_append_tail_prev_is_tail_minus_1), - cmocka_unit_test(test_list_list_item_append_tail_next_is_head), - }; - - cmocka_set_message_output(CM_OUTPUT_TAP); - - return cmocka_run_group_tests(tests, setup, teardown); -} diff --git a/test/cmocka/src/list/list_item_del.c b/test/cmocka/src/list/list_item_del.c deleted file mode 100644 index ad6bb81a1237..000000000000 --- a/test/cmocka/src/list/list_item_del.c +++ /dev/null @@ -1,197 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -// -// Copyright(c) 2018 Intel Corporation. All rights reserved. -// -// Author: Janusz Jankowski <janusz.jankowski@linux.intel.com> - -#include <sof/list.h> - -#include <stdlib.h> -#include <stdarg.h> -#include <stddef.h> -#include <setjmp.h> -#include <stdint.h> -#include <cmocka.h> - -struct test_data { - struct list_item *head; - struct list_item *tail_minus_1; - struct list_item *tail; -}; - -static int setup(void **state) -{ - struct test_data *data = malloc(sizeof(struct test_data)); - - if (!data) - return -1; - - data->head = malloc(sizeof(struct list_item)); - data->tail_minus_1 = malloc(sizeof(struct list_item)); - data->tail = malloc(sizeof(struct list_item)); - - if (!data->head || !data->tail_minus_1 - || !data->tail) { - free(data->head); - free(data->tail_minus_1); - free(data->tail); - - free(data); - - return -1; - } - - list_init(data->head); - list_init(data->tail_minus_1); - list_init(data->tail); - - list_item_append(data->tail_minus_1, data->head); - list_item_append(data->tail, data->head); - - *state = data; - return 0; -} - -static int teardown(void **state) -{ - struct test_data *data = *state; - - free(data->head); - free(data->tail_minus_1); - free(data->tail); - - free(data); - return 0; -} - -static void test_list_list_item_del_when_delete_head_then_tail_minus_1_prev_is_tail(void **state) -{ - struct test_data *data = *state; - - list_item_del(data->head); - - assert_ptr_equal(data->tail_minus_1->prev, data->tail); -} - -static void test_list_list_item_del_when_delete_head_then_tail_minus_1_next_is_tail(void **state) -{ - struct test_data *data = *state; - - list_item_del(data->head); - - assert_ptr_equal(data->tail_minus_1->next, data->tail); -} - -static void test_list_list_item_del_when_delete_head_then_tail_prev_is_tail_minus_1(void **state) -{ - struct test_data *data = *state; - - list_item_del(data->head); - - assert_ptr_equal(data->tail->prev, data->tail_minus_1); -} - -static void test_list_list_item_del_when_delete_head_then_tail_next_is_tail_minus_1(void **state) -{ - struct test_data *data = *state; - - list_item_del(data->head); - - assert_ptr_equal(data->tail->next, data->tail_minus_1); -} - -static void test_list_list_item_del_when_delete_tail_minus_1_then_head_prev_is_tail(void **state) -{ - struct test_data *data = *state; - - list_item_del(data->tail_minus_1); - - assert_ptr_equal(data->head->prev, data->tail); -} - -static void test_list_list_item_del_when_delete_tail_minus_1_then_head_next_is_tail(void **state) -{ - struct test_data *data = *state; - - list_item_del(data->tail_minus_1); - - assert_ptr_equal(data->head->next, data->tail); -} - -static void test_list_list_item_del_when_delete_tail_minus_1_then_tail_prev_is_head(void **state) -{ - struct test_data *data = *state; - - list_item_del(data->tail_minus_1); - - assert_ptr_equal(data->tail->prev, data->head); -} - -static void test_list_list_item_del_when_delete_tail_minus_1_then_tail_next_is_head(void **state) -{ - struct test_data *data = *state; - - list_item_del(data->tail_minus_1); - - assert_ptr_equal(data->tail->next, data->head); -} - -static void test_list_list_item_del_when_delete_tail_then_head_prev_is_tail_minus_1(void **state) -{ - struct test_data *data = *state; - - list_item_del(data->tail); - - assert_ptr_equal(data->head->prev, data->tail_minus_1); -} - -static void test_list_list_item_del_when_delete_tail_then_head_next_is_tail_minus_1(void **state) -{ - struct test_data *data = *state; - - list_item_del(data->tail); - - assert_ptr_equal(data->head->next, data->tail_minus_1); -} - -static void test_list_list_item_del_when_delete_tail_then_tail_minus_1_prev_is_head(void **state) -{ - struct test_data *data = *state; - - list_item_del(data->tail); - - assert_ptr_equal(data->tail_minus_1->prev, data->head); -} - -static void test_list_list_item_del_when_delete_tail_then_tail_minus_1_next_is_head(void **state) -{ - struct test_data *data = *state; - - list_item_del(data->tail); - - assert_ptr_equal(data->tail_minus_1->next, data->head); -} - -int main(void) -{ - const struct CMUnitTest tests[] = { - cmocka_unit_test_setup_teardown(test_list_list_item_del_when_delete_head_then_tail_minus_1_prev_is_tail, setup, teardown), - cmocka_unit_test_setup_teardown(test_list_list_item_del_when_delete_head_then_tail_minus_1_next_is_tail, setup, teardown), - cmocka_unit_test_setup_teardown(test_list_list_item_del_when_delete_head_then_tail_prev_is_tail_minus_1, setup, teardown), - cmocka_unit_test_setup_teardown(test_list_list_item_del_when_delete_head_then_tail_next_is_tail_minus_1, setup, teardown), - - cmocka_unit_test_setup_teardown(test_list_list_item_del_when_delete_tail_minus_1_then_head_prev_is_tail, setup, teardown), - cmocka_unit_test_setup_teardown(test_list_list_item_del_when_delete_tail_minus_1_then_head_next_is_tail, setup, teardown), - cmocka_unit_test_setup_teardown(test_list_list_item_del_when_delete_tail_minus_1_then_tail_prev_is_head, setup, teardown), - cmocka_unit_test_setup_teardown(test_list_list_item_del_when_delete_tail_minus_1_then_tail_next_is_head, setup, teardown), - - cmocka_unit_test_setup_teardown(test_list_list_item_del_when_delete_tail_then_head_prev_is_tail_minus_1, setup, teardown), - cmocka_unit_test_setup_teardown(test_list_list_item_del_when_delete_tail_then_head_next_is_tail_minus_1, setup, teardown), - cmocka_unit_test_setup_teardown(test_list_list_item_del_when_delete_tail_then_tail_minus_1_prev_is_head, setup, teardown), - cmocka_unit_test_setup_teardown(test_list_list_item_del_when_delete_tail_then_tail_minus_1_next_is_head, setup, teardown), - }; - - cmocka_set_message_output(CM_OUTPUT_TAP); - - return cmocka_run_group_tests(tests, NULL, NULL); -} diff --git a/test/cmocka/src/list/list_item_is_last.c b/test/cmocka/src/list/list_item_is_last.c deleted file mode 100644 index ffd1abee50bd..000000000000 --- a/test/cmocka/src/list/list_item_is_last.c +++ /dev/null @@ -1,110 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -// -// Copyright(c) 2018 Intel Corporation. All rights reserved. -// -// Author: Janusz Jankowski <janusz.jankowski@linux.intel.com> - -#include <sof/list.h> - -#include <stdlib.h> -#include <stdarg.h> -#include <stddef.h> -#include <setjmp.h> -#include <stdint.h> -#include <cmocka.h> - -struct test_data { - struct list_item *head; - struct list_item *tail_minus_1; - struct list_item *tail; -}; - -static int setup(void **state) -{ - struct test_data *data = malloc(sizeof(struct test_data)); - - if (!data) - return -1; - - data->head = malloc(sizeof(struct list_item)); - data->tail_minus_1 = malloc(sizeof(struct list_item)); - data->tail = malloc(sizeof(struct list_item)); - - if (!data->head || !data->tail_minus_1 - || !data->tail) { - free(data->head); - free(data->tail_minus_1); - free(data->tail); - - free(data); - - return -1; - } - - list_init(data->head); - list_init(data->tail_minus_1); - list_init(data->tail); - - list_item_append(data->tail_minus_1, data->head); - list_item_append(data->tail, data->head); - - *state = data; - return 0; -} - -static int teardown(void **state) -{ - struct test_data *data = *state; - - free(data->head); - free(data->tail_minus_1); - free(data->tail); - - free(data); - return 0; -} - -static void test_list_list_item_is_last_when_head_then_false(void **state) -{ - struct test_data *data = *state; - - assert_false(list_item_is_last(data->head, data->head)); -} - -static void test_list_list_item_is_last_when_tail_minus_1_then_false(void **state) -{ - struct test_data *data = *state; - - assert_false(list_item_is_last(data->tail_minus_1, data->head)); -} - -static void test_list_list_item_is_last_when_tail_then_true(void **state) -{ - struct test_data *data = *state; - - assert_true(list_item_is_last(data->tail, data->head)); -} - -static void test_list_list_item_is_last_when_not_in_list_then_false(void **state) -{ - struct list_item other_list; - struct test_data *data = *state; - - list_init(&other_list); - - assert_false(list_item_is_last(&other_list, data->head)); -} - -int main(void) -{ - const struct CMUnitTest tests[] = { - cmocka_unit_test(test_list_list_item_is_last_when_head_then_false), - cmocka_unit_test(test_list_list_item_is_last_when_tail_minus_1_then_false), - cmocka_unit_test(test_list_list_item_is_last_when_tail_then_true), - cmocka_unit_test(test_list_list_item_is_last_when_not_in_list_then_false), - }; - - cmocka_set_message_output(CM_OUTPUT_TAP); - - return cmocka_run_group_tests(tests, setup, teardown); -} diff --git a/test/cmocka/src/list/list_item_prepend.c b/test/cmocka/src/list/list_item_prepend.c deleted file mode 100644 index e86fa97f8a5e..000000000000 --- a/test/cmocka/src/list/list_item_prepend.c +++ /dev/null @@ -1,123 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -// -// Copyright(c) 2018 Intel Corporation. All rights reserved. -// -// Author: Janusz Jankowski <janusz.jankowski@linux.intel.com> - -#include <sof/list.h> - -#include <stdlib.h> -#include <stdarg.h> -#include <stddef.h> -#include <setjmp.h> -#include <stdint.h> -#include <cmocka.h> - -struct test_data { - struct list_item *head; - struct list_item *tail_minus_1; - struct list_item *tail; -}; - -static int setup(void **state) -{ - struct test_data *data = malloc(sizeof(struct test_data)); - - if (!data) - return -1; - - data->head = malloc(sizeof(struct list_item)); - data->tail_minus_1 = malloc(sizeof(struct list_item)); - data->tail = malloc(sizeof(struct list_item)); - - if (!data->head || !data->tail_minus_1 - || !data->tail) { - free(data->head); - free(data->tail_minus_1); - free(data->tail); - - free(data); - - return -1; - } - - list_init(data->head); - list_init(data->tail_minus_1); - list_init(data->tail); - - list_item_prepend(data->tail, data->head); - list_item_prepend(data->tail_minus_1, data->head); - - *state = data; - return 0; -} - -static int teardown(void **state) -{ - struct test_data *data = *state; - - free(data->head); - free(data->tail_minus_1); - free(data->tail); - - free(data); - return 0; -} - -static void test_list_list_item_prepend_head_prev_is_tail(void **state) -{ - struct test_data *data = *state; - - assert_ptr_equal(data->head->prev, data->tail); -} - -static void test_list_list_item_prepend_head_next_is_tail_minus_1(void **state) -{ - struct test_data *data = *state; - - assert_ptr_equal(data->head->next, data->tail_minus_1); -} - -static void test_list_list_item_prepend_tail_minus_1_prev_is_head(void **state) -{ - struct test_data *data = *state; - - assert_ptr_equal(data->tail_minus_1->prev, data->head); -} - -static void test_list_list_item_prepend_tail_minus_1_next_is_tail(void **state) -{ - struct test_data *data = *state; - - assert_ptr_equal(data->tail_minus_1->next, data->tail); -} - -static void test_list_list_item_prepend_tail_prev_is_tail_minus_1(void **state) -{ - struct test_data *data = *state; - - assert_ptr_equal(data->tail->prev, data->tail_minus_1); -} - -static void test_list_list_item_prepend_tail_next_is_head(void **state) -{ - struct test_data *data = *state; - - assert_ptr_equal(data->tail->next, data->head); -} - -int main(void) -{ - const struct CMUnitTest tests[] = { - cmocka_unit_test(test_list_list_item_prepend_head_prev_is_tail), - cmocka_unit_test(test_list_list_item_prepend_head_next_is_tail_minus_1), - cmocka_unit_test(test_list_list_item_prepend_tail_minus_1_prev_is_head), - cmocka_unit_test(test_list_list_item_prepend_tail_minus_1_next_is_tail), - cmocka_unit_test(test_list_list_item_prepend_tail_prev_is_tail_minus_1), - cmocka_unit_test(test_list_list_item_prepend_tail_next_is_head), - }; - - cmocka_set_message_output(CM_OUTPUT_TAP); - - return cmocka_run_group_tests(tests, setup, teardown); -} diff --git a/test/cmocka/src/math/CMakeLists.txt b/test/cmocka/src/math/CMakeLists.txt index 07e5c495a382..83180953eb55 100644 --- a/test/cmocka/src/math/CMakeLists.txt +++ b/test/cmocka/src/math/CMakeLists.txt @@ -1,7 +1,5 @@ # SPDX-License-Identifier: BSD-3-Clause -add_subdirectory(numbers) -add_subdirectory(trig) add_subdirectory(arithmetic) add_subdirectory(fft) add_subdirectory(window) diff --git a/test/cmocka/src/math/arithmetic/square_root.c b/test/cmocka/src/math/arithmetic/square_root.c index f6c03c9c42cf..6a31588c1235 100644 --- a/test/cmocka/src/math/arithmetic/square_root.c +++ b/test/cmocka/src/math/arithmetic/square_root.c @@ -139,7 +139,7 @@ static void test_math_arithmetic_sqrt_fixed(void **state) memcpy_s((void *)&u[0], sizeof(u), (void *)&uv[0], 252U * sizeof(uint32_t)); for (i = 0; i < ARRAY_SIZE(sqrt_ref_table); i++) { - y = Q_CONVERT_QTOF(sqrt_int16(u[i]), 12); + y = Q_CONVERT_QTOF(sofm_sqrt_int16(u[i]), 12); diff = fabs(sqrt_ref_table[i] - y); if (diff > CMP_TOLERANCE) { diff --git a/test/cmocka/src/math/auditory/auditory.c b/test/cmocka/src/math/auditory/auditory.c index 1e3e903fb5fb..ff222e52fadd 100644 --- a/test/cmocka/src/math/auditory/auditory.c +++ b/test/cmocka/src/math/auditory/auditory.c @@ -14,7 +14,10 @@ #include <string.h> #include <cmocka.h> #include <math.h> +#include <sof/audio/module_adapter/module/generic.h> #include <sof/math/auditory.h> +#include <sof/math/icomplex16.h> +#include <sof/math/icomplex32.h> #include <sof/math/fft.h> #include "ref_hz_to_mel.h" #include "ref_mel_filterbank_16_test1.h" @@ -38,6 +41,8 @@ #undef DEBUGFILES /* Change this to #define to get output data files for debugging */ +struct processing_module dummy; + static void filterbank_16_test(const int16_t *fft_real, const int16_t *fft_imag, const int16_t *ref_mel_log, int num_fft_bins, int num_mel_bins, int norm_slaney, @@ -87,7 +92,7 @@ static void filterbank_16_test(const int16_t *fft_real, const int16_t *fft_imag, fb.scratch_data2 = (int16_t *)fft_out; fb.scratch_length1 = fft_size / sizeof(int16_t); fb.scratch_length2 = fft_size / sizeof(int16_t); - ret = psy_get_mel_filterbank(&fb); + ret = mod_psy_get_mel_filterbank(&dummy, &fb); if (ret < 0) { fprintf(stderr, "Failed Mel filterbank\n"); goto err_get_filterbank; @@ -128,6 +133,10 @@ static void filterbank_16_test(const int16_t *fft_real, const int16_t *fft_imag, assert_true(error_rms < MEL_FB16_MAX_ERROR_RMS); assert_true(delta_max < MEL_FB16_MAX_ERROR_ABS); + free(mel_log); + free(fft_buf); + free(fft_out); + mod_psy_free_mel_filterbank(&dummy, &fb); return; err_get_filterbank: @@ -154,7 +163,8 @@ static void filterbank_32_test(const int32_t *fft_real, const int32_t *fft_imag, float error_rms; float delta_max = 0; int32_t *power_spectra; - int16_t *mel_log; + int32_t *mel_log; + int16_t mel_log_16; int i; const int half_fft = num_fft_bins / 2 + 1; const int fft_size = num_fft_bins * sizeof(struct icomplex32); @@ -172,7 +182,7 @@ static void filterbank_32_test(const int32_t *fft_real, const int32_t *fft_imag, goto err_out_alloc; } - mel_log = malloc(MEL_FILTERBANK_32_TEST1_NUM_MEL_BINS * sizeof(int16_t)); + mel_log = malloc(num_mel_bins * sizeof(int32_t)); if (!mel_log) { fprintf(stderr, "Failed to allocate output vector\n"); goto err_mel_alloc; @@ -190,7 +200,7 @@ static void filterbank_32_test(const int32_t *fft_real, const int32_t *fft_imag, fb.scratch_data2 = (int16_t *)fft_out; fb.scratch_length1 = fft_size / sizeof(int16_t); fb.scratch_length2 = fft_size / sizeof(int16_t); - ret = psy_get_mel_filterbank(&fb); + ret = mod_psy_get_mel_filterbank(&dummy, &fb); if (ret < 0) { fprintf(stderr, "Failed Mel filterbank\n"); goto err_get_filterbank; @@ -206,9 +216,10 @@ static void filterbank_32_test(const int32_t *fft_real, const int32_t *fft_imag, power_spectra = (int32_t *)&fft_buf[0]; psy_apply_mel_filterbank_32(&fb, fft_out, power_spectra, mel_log, shift); - /* Check */ + /* Check: convert Q9.23 output to Q9.7 for comparison with reference */ for (i = 0; i < num_mel_bins; i++) { - delta = (float)ref_mel_log[i] - (float)mel_log[i]; + mel_log_16 = (int16_t)(mel_log[i] >> 16); + delta = (float)ref_mel_log[i] - (float)mel_log_16; sum_squares += delta * delta; if (delta > delta_max) delta_max = delta; @@ -224,13 +235,17 @@ static void filterbank_32_test(const int32_t *fft_real, const int32_t *fft_imag, FILE *fh = fopen("mel_filterbank_32.txt", "w"); for (i = 0; i < num_mel_bins; i++) - fprintf(fh, "%d %d\n", ref_mel_log[i], mel_log[i]); + fprintf(fh, "%d %d\n", ref_mel_log[i], (int16_t)(mel_log[i] >> 16)); fclose(fh); #endif assert_true(error_rms < MEL_FB32_MAX_ERROR_RMS); assert_true(delta_max < MEL_FB32_MAX_ERROR_ABS); + free(mel_log); + free(fft_buf); + free(fft_out); + mod_psy_free_mel_filterbank(&dummy, &fb); return; err_get_filterbank: diff --git a/test/cmocka/src/math/dct/dct.c b/test/cmocka/src/math/dct/dct.c index 4cfe38ef803b..ea35d170e66e 100644 --- a/test/cmocka/src/math/dct/dct.c +++ b/test/cmocka/src/math/dct/dct.c @@ -22,6 +22,8 @@ #define MATRIX_MULT_16_MAX_ERROR_ABS 2.5 #define MATRIX_MULT_16_MAX_ERROR_RMS 1.1 +struct processing_module dummy; + static void dct_matrix_16_test(const int16_t *ref, int num_in, int num_out, enum dct_type type, bool ortho) { @@ -41,7 +43,7 @@ static void dct_matrix_16_test(const int16_t *ref, int num_in, int num_out, dct.num_out = num_out; dct.type = type; dct.ortho = ortho; - ret = dct_initialize_16(&dct); + ret = mod_dct_initialize_16(&dummy, &dct); if (ret) { fprintf(stderr, "Failed to initialize DCT.\n"); exit(EXIT_FAILURE); @@ -69,6 +71,8 @@ static void dct_matrix_16_test(const int16_t *ref, int num_in, int num_out, assert_true(error_rms < MATRIX_MULT_16_MAX_ERROR_RMS); assert_true(delta_max < MATRIX_MULT_16_MAX_ERROR_ABS); + + mod_dct_free_16(&dummy, &dct); } static void test_dct_matrix_16_test1(void **state) diff --git a/test/cmocka/src/math/fft/CMakeLists.txt b/test/cmocka/src/math/fft/CMakeLists.txt index c3d9e47e6ea5..97ac2ee8438f 100644 --- a/test/cmocka/src/math/fft/CMakeLists.txt +++ b/test/cmocka/src/math/fft/CMakeLists.txt @@ -28,3 +28,25 @@ cmocka_test(fft ${PROJECT_SOURCE_DIR}/src/audio/component.c ${PROJECT_SOURCE_DIR}/src/math/numbers.c ) + +cmocka_test(dft3 + dft3.c + ${PROJECT_SOURCE_DIR}/src/math/fft/fft_multi.c + ${PROJECT_SOURCE_DIR}/src/math/fft/fft_multi_generic.c + ${PROJECT_SOURCE_DIR}/src/math/fft/fft_multi_hifi3.c + ${PROJECT_SOURCE_DIR}/src/math/fft/fft_32.c + ${PROJECT_SOURCE_DIR}/src/math/fft/fft_common.c + ${PROJECT_SOURCE_DIR}/test/cmocka/src/notifier_mocks.c + ${PROJECT_SOURCE_DIR}/test/cmocka/src/common_mocks.c +) + +cmocka_test(fft_multi + fft_multi.c + ${PROJECT_SOURCE_DIR}/src/math/fft/fft_multi.c + ${PROJECT_SOURCE_DIR}/src/math/fft/fft_multi_generic.c + ${PROJECT_SOURCE_DIR}/src/math/fft/fft_multi_hifi3.c + ${PROJECT_SOURCE_DIR}/src/math/fft/fft_common.c + ${PROJECT_SOURCE_DIR}/src/math/fft/fft_32.c + ${PROJECT_SOURCE_DIR}/test/cmocka/src/notifier_mocks.c + ${PROJECT_SOURCE_DIR}/test/cmocka/src/common_mocks.c +) diff --git a/test/cmocka/src/math/fft/dft3.c b/test/cmocka/src/math/fft/dft3.c new file mode 100644 index 000000000000..a787d8329993 --- /dev/null +++ b/test/cmocka/src/math/fft/dft3.c @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// Copyright(c) 2025 Intel Corporation. All rights reserved. +// +// Author: Seppo Ingalsuo <seppo.ingalsuo@linux.intel.com> + +#include <sof/audio/format.h> +#include <sof/math/fft.h> +#include "ref_dft3_32.h" + +#include <errno.h> +#include <stdlib.h> +#include <stdio.h> +#include <stdint.h> +#include <stdarg.h> +#include <stddef.h> +#include <setjmp.h> +#include <string.h> +#include <cmocka.h> +#include <math.h> + +#define SOFM_DFT3_MAX_ERROR_ABS 3.1 +#define SOFM_DFT3_MAX_ERROR_RMS 1.1 +#define DFT_SIZE 3 + +static void dft3_32_test(const int32_t *in_real, const int32_t *in_imag, + const int32_t *ref_real, const int32_t *ref_imag, int num_tests) + +{ + struct icomplex32 x[DFT_SIZE]; + struct icomplex32 y[DFT_SIZE]; + double delta; + double error_rms; + double delta_max = 0; + double sum_squares = 0; + const int32_t *p_in_real = in_real; + const int32_t *p_in_imag = in_imag; + const int32_t *p_ref_real = ref_real; + const int32_t *p_ref_imag = ref_imag; + int i, j; + + for (i = 0; i < num_tests; i++) { + for (j = 0; j < DFT_SIZE; j++) { + x[j].real = *p_in_real++; + x[j].imag = *p_in_imag++; + } + + dft3_32(x, y); + + for (j = 0; j < DFT_SIZE; j++) { + delta = (double)*p_ref_real - (double)y[j].real; + sum_squares += delta * delta; + if (delta > delta_max) + delta_max = delta; + else if (-delta > delta_max) + delta_max = -delta; + + delta = (double)*p_ref_imag - (double)y[j].imag; + sum_squares += delta * delta; + if (delta > delta_max) + delta_max = delta; + else if (-delta > delta_max) + delta_max = -delta; + + p_ref_real++; + p_ref_imag++; + } + } + + error_rms = sqrt(sum_squares / (double)(2 * DFT_SIZE * num_tests)); + printf("Max absolute error = %5.2f (max %5.2f), error RMS = %5.2f (max %5.2f)\n", + delta_max, SOFM_DFT3_MAX_ERROR_ABS, error_rms, SOFM_DFT3_MAX_ERROR_RMS); + + assert_true(error_rms < SOFM_DFT3_MAX_ERROR_RMS); + assert_true(delta_max < SOFM_DFT3_MAX_ERROR_ABS); +} + +static void dft3_32_test_1(void **state) +{ + (void)state; + + dft3_32_test(input_data_real_q31, input_data_imag_q31, + ref_data_real_q31, ref_data_imag_q31, + REF_SOFM_DFT3_NUM_TESTS); +} + +int main(void) +{ + const struct CMUnitTest tests[] = { + cmocka_unit_test(dft3_32_test_1), + }; + + cmocka_set_message_output(CM_OUTPUT_TAP); + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/test/cmocka/src/math/fft/fft.c b/test/cmocka/src/math/fft/fft.c index 34f59cc656fb..dc3dc8e70524 100644 --- a/test/cmocka/src/math/fft/fft.c +++ b/test/cmocka/src/math/fft/fft.c @@ -13,9 +13,12 @@ #include <cmocka.h> #include <stdbool.h> +#include <sof/audio/module_adapter/module/generic.h> #include <sof/audio/buffer.h> #include <sof/audio/component.h> #include <sof/audio/format.h> +#include <sof/math/icomplex16.h> +#include <sof/math/icomplex32.h> #include <sof/math/fft.h> #include "input.h" @@ -39,6 +42,8 @@ #define MIN_SNR_512 125.0 #define MIN_SNR_1024 119.0 +struct processing_module dummy; + /** * \brief Doing Fast Fourier Transform (FFT) for mono real input buffers. * \param[in] src - pointer to input buffer. @@ -68,7 +73,7 @@ static void fft_real(struct comp_buffer *src, struct comp_buffer *dst, uint32_t if (!outb) goto err_outb; - plan = fft_plan_new(inb, outb, size, 32); + plan = mod_fft_plan_new(&dummy, inb, outb, size, 32); if (!plan) goto err_plan; @@ -85,7 +90,7 @@ static void fft_real(struct comp_buffer *src, struct comp_buffer *dst, uint32_t *((int32_t *)dst->stream.addr + 2 * i + 1) = outb[i].imag; } - fft_plan_free(plan); + mod_fft_plan_free(&dummy, plan); err_plan: rfree(outb); @@ -123,7 +128,7 @@ static void ifft_complex(struct comp_buffer *src, struct comp_buffer *dst, uint3 if (!outb) goto err_outb; - plan = fft_plan_new(inb, outb, size, 32); + plan = mod_fft_plan_new(&dummy, inb, outb, size, 32); if (!plan) goto err_plan; @@ -140,7 +145,7 @@ static void ifft_complex(struct comp_buffer *src, struct comp_buffer *dst, uint3 *((int32_t *)dst->stream.addr + 2 * i + 1) = outb[i].imag; } - fft_plan_free(plan); + mod_fft_plan_free(&dummy, plan); err_plan: rfree(outb); @@ -181,7 +186,7 @@ static void fft_real_2(struct comp_buffer *src, struct comp_buffer *dst1, if (!outb) goto err_outb; - plan = fft_plan_new(inb, outb, size, 32); + plan = mod_fft_plan_new(&dummy, inb, outb, size, 32); if (!plan) goto err_plan; @@ -210,7 +215,7 @@ static void fft_real_2(struct comp_buffer *src, struct comp_buffer *dst1, (outb[size - i].real - outb[i].real) / 2; } - fft_plan_free(plan); + mod_fft_plan_free(&dummy, plan); err_plan: rfree(outb); @@ -265,8 +270,8 @@ static void test_math_fft_256(void **state) struct sof_ipc_buffer test_buf_desc = { .size = 256 * 2 * sizeof(int32_t), }; - struct comp_buffer *source = buffer_new(&test_buf_desc, BUFFER_USAGE_NOT_SHARED); - struct comp_buffer *sink = buffer_new(&test_buf_desc, BUFFER_USAGE_NOT_SHARED); + struct comp_buffer *source = buffer_new(NULL, &test_buf_desc, BUFFER_USAGE_NOT_SHARED); + struct comp_buffer *sink = buffer_new(NULL, &test_buf_desc, BUFFER_USAGE_NOT_SHARED); struct icomplex32 *out = (struct icomplex32 *)sink->stream.addr; int32_t *in = (int32_t *)source->stream.addr; int fft_size = 256; @@ -300,6 +305,9 @@ static void test_math_fft_256(void **state) snr = 10 * log10(signal / noise); printf("%s: SNR %5.2f dB\n", __func__, snr); assert_int_equal(snr < MIN_SNR_256, 0); + + buffer_free(source); + buffer_free(sink); } static void test_math_fft_512(void **state) @@ -307,8 +315,8 @@ static void test_math_fft_512(void **state) struct sof_ipc_buffer test_buf_desc = { .size = 512 * 2 * sizeof(int32_t), }; - struct comp_buffer *source = buffer_new(&test_buf_desc, BUFFER_USAGE_NOT_SHARED); - struct comp_buffer *sink = buffer_new(&test_buf_desc, BUFFER_USAGE_NOT_SHARED); + struct comp_buffer *source = buffer_new(NULL, &test_buf_desc, BUFFER_USAGE_NOT_SHARED); + struct comp_buffer *sink = buffer_new(NULL, &test_buf_desc, BUFFER_USAGE_NOT_SHARED); struct icomplex32 *out = (struct icomplex32 *)sink->stream.addr; int32_t *in = (int32_t *)source->stream.addr; int fft_size = 512; @@ -342,6 +350,9 @@ static void test_math_fft_512(void **state) snr = 10 * log10(signal / noise); printf("%s: SNR %5.2f dB\n", __func__, snr); assert_int_equal(snr < MIN_SNR_512, 0); + + buffer_free(source); + buffer_free(sink); } static void test_math_fft_1024(void **state) @@ -349,8 +360,8 @@ static void test_math_fft_1024(void **state) struct sof_ipc_buffer test_buf_desc = { .size = 1024 * 2 * sizeof(int32_t), }; - struct comp_buffer *source = buffer_new(&test_buf_desc, BUFFER_USAGE_NOT_SHARED); - struct comp_buffer *sink = buffer_new(&test_buf_desc, BUFFER_USAGE_NOT_SHARED); + struct comp_buffer *source = buffer_new(NULL, &test_buf_desc, BUFFER_USAGE_NOT_SHARED); + struct comp_buffer *sink = buffer_new(NULL, &test_buf_desc, BUFFER_USAGE_NOT_SHARED); struct icomplex32 *out = (struct icomplex32 *)sink->stream.addr; int32_t *in = (int32_t *)source->stream.addr; int fft_size = 1024; @@ -384,6 +395,9 @@ static void test_math_fft_1024(void **state) snr = 10 * log10(signal / noise); printf("%s: SNR %5.2f dB\n", __func__, snr); assert_int_equal(snr < MIN_SNR_1024, 0); + + buffer_free(source); + buffer_free(sink); } static void test_math_fft_1024_ifft(void **state) @@ -391,9 +405,9 @@ static void test_math_fft_1024_ifft(void **state) struct sof_ipc_buffer test_buf_desc = { .size = 1024 * 4 * 2, }; - struct comp_buffer *source = buffer_new(&test_buf_desc, BUFFER_USAGE_NOT_SHARED); - struct comp_buffer *intm = buffer_new(&test_buf_desc, BUFFER_USAGE_NOT_SHARED); - struct comp_buffer *sink = buffer_new(&test_buf_desc, BUFFER_USAGE_NOT_SHARED); + struct comp_buffer *source = buffer_new(NULL, &test_buf_desc, BUFFER_USAGE_NOT_SHARED); + struct comp_buffer *intm = buffer_new(NULL, &test_buf_desc, BUFFER_USAGE_NOT_SHARED); + struct comp_buffer *sink = buffer_new(NULL, &test_buf_desc, BUFFER_USAGE_NOT_SHARED); struct icomplex32 *out = (struct icomplex32 *)sink->stream.addr; float db; int64_t signal = 0; @@ -425,6 +439,10 @@ static void test_math_fft_1024_ifft(void **state) db = 10 * log10((float)signal / noise); printf("%s: SNR: %6.2f dB\n", __func__, db); assert_int_equal(db < FFT_DB_TH, 0); + + buffer_free(source); + buffer_free(intm); + buffer_free(sink); } static void test_math_fft_512_2ch(void **state) @@ -432,9 +450,9 @@ static void test_math_fft_512_2ch(void **state) struct sof_ipc_buffer test_buf_desc = { .size = 512 * 4 * 2, }; - struct comp_buffer *source = buffer_new(&test_buf_desc, BUFFER_USAGE_NOT_SHARED); - struct comp_buffer *sink1 = buffer_new(&test_buf_desc, BUFFER_USAGE_NOT_SHARED); - struct comp_buffer *sink2 = buffer_new(&test_buf_desc, BUFFER_USAGE_NOT_SHARED); + struct comp_buffer *source = buffer_new(NULL, &test_buf_desc, BUFFER_USAGE_NOT_SHARED); + struct comp_buffer *sink1 = buffer_new(NULL, &test_buf_desc, BUFFER_USAGE_NOT_SHARED); + struct comp_buffer *sink2 = buffer_new(NULL, &test_buf_desc, BUFFER_USAGE_NOT_SHARED); struct icomplex32 *out1 = (struct icomplex32 *)sink1->stream.addr; struct icomplex32 *out2 = (struct icomplex32 *)sink2->stream.addr; uint32_t fft_size = 512; @@ -467,6 +485,10 @@ static void test_math_fft_512_2ch(void **state) /* the peak should be in range i +/-1 */ assert_in_range(r, i - 1, i + 1); + + buffer_free(source); + buffer_free(sink1); + buffer_free(sink2); } /** @@ -498,7 +520,7 @@ static void fft_real_16(struct comp_buffer *src, struct comp_buffer *dst, uint32 if (!outb) goto err_outb; - plan = fft_plan_new(inb, outb, size, 16); + plan = mod_fft_plan_new(&dummy, inb, outb, size, 16); if (!plan) goto err_plan; @@ -515,7 +537,7 @@ static void fft_real_16(struct comp_buffer *src, struct comp_buffer *dst, uint32 *((int16_t *)dst->stream.addr + 2 * i + 1) = outb[i].imag; } - fft_plan_free(plan); + mod_fft_plan_free(&dummy, plan); err_plan: rfree(outb); @@ -553,7 +575,7 @@ static void ifft_complex_16(struct comp_buffer *src, struct comp_buffer *dst, ui if (!outb) goto err_outb; - plan = fft_plan_new(inb, outb, size, 16); + plan = mod_fft_plan_new(&dummy, inb, outb, size, 16); if (!plan) goto err_plan; @@ -570,7 +592,7 @@ static void ifft_complex_16(struct comp_buffer *src, struct comp_buffer *dst, ui *((int16_t *)dst->stream.addr + 2 * i + 1) = outb[i].imag; } - fft_plan_free(plan); + mod_fft_plan_free(&dummy, plan); err_plan: rfree(outb); @@ -625,8 +647,8 @@ static void test_math_fft_256_16(void **state) struct sof_ipc_buffer test_buf_desc = { .size = 256 * 2 * sizeof(int16_t), }; - struct comp_buffer *source = buffer_new(&test_buf_desc, BUFFER_USAGE_NOT_SHARED); - struct comp_buffer *sink = buffer_new(&test_buf_desc, BUFFER_USAGE_NOT_SHARED); + struct comp_buffer *source = buffer_new(NULL, &test_buf_desc, BUFFER_USAGE_NOT_SHARED); + struct comp_buffer *sink = buffer_new(NULL, &test_buf_desc, BUFFER_USAGE_NOT_SHARED); struct icomplex16 *out = (struct icomplex16 *)sink->stream.addr; int16_t *in = (int16_t *)source->stream.addr; int fft_size = 256; @@ -660,6 +682,9 @@ static void test_math_fft_256_16(void **state) snr = 10 * log10(signal / noise); printf("%s: SNR %5.2f dB\n", __func__, snr); assert_int_equal(snr < MIN_SNR_256_16, 0); + + buffer_free(source); + buffer_free(sink); } static void test_math_fft_512_16(void **state) @@ -667,8 +692,8 @@ static void test_math_fft_512_16(void **state) struct sof_ipc_buffer test_buf_desc = { .size = 512 * 2 * sizeof(int16_t), }; - struct comp_buffer *source = buffer_new(&test_buf_desc, BUFFER_USAGE_NOT_SHARED); - struct comp_buffer *sink = buffer_new(&test_buf_desc, BUFFER_USAGE_NOT_SHARED); + struct comp_buffer *source = buffer_new(NULL, &test_buf_desc, BUFFER_USAGE_NOT_SHARED); + struct comp_buffer *sink = buffer_new(NULL, &test_buf_desc, BUFFER_USAGE_NOT_SHARED); struct icomplex16 *out = (struct icomplex16 *)sink->stream.addr; int16_t *in = (int16_t *)source->stream.addr; int fft_size = 512; @@ -702,6 +727,9 @@ static void test_math_fft_512_16(void **state) snr = 10 * log10(signal / noise); printf("%s: SNR %5.2f dB\n", __func__, snr); assert_int_equal(snr < MIN_SNR_512_16, 0); + + buffer_free(source); + buffer_free(sink); } static void test_math_fft_1024_16(void **state) @@ -709,8 +737,8 @@ static void test_math_fft_1024_16(void **state) struct sof_ipc_buffer test_buf_desc = { .size = 1024 * 2 * sizeof(int16_t), }; - struct comp_buffer *source = buffer_new(&test_buf_desc, BUFFER_USAGE_NOT_SHARED); - struct comp_buffer *sink = buffer_new(&test_buf_desc, BUFFER_USAGE_NOT_SHARED); + struct comp_buffer *source = buffer_new(NULL, &test_buf_desc, BUFFER_USAGE_NOT_SHARED); + struct comp_buffer *sink = buffer_new(NULL, &test_buf_desc, BUFFER_USAGE_NOT_SHARED); struct icomplex16 *out = (struct icomplex16 *)sink->stream.addr; int16_t *in = (int16_t *)source->stream.addr; int fft_size = 1024; @@ -744,6 +772,9 @@ static void test_math_fft_1024_16(void **state) snr = 10 * log10(signal / noise); printf("%s: SNR %5.2f dB\n", __func__, snr); assert_int_equal(snr < MIN_SNR_1024_16, 0); + + buffer_free(source); + buffer_free(sink); } static void test_math_fft_1024_ifft_16(void **state) @@ -751,9 +782,9 @@ static void test_math_fft_1024_ifft_16(void **state) struct sof_ipc_buffer test_buf_desc = { .size = 1024 * 2 * sizeof(int16_t), }; - struct comp_buffer *source = buffer_new(&test_buf_desc, BUFFER_USAGE_NOT_SHARED); - struct comp_buffer *intm = buffer_new(&test_buf_desc, BUFFER_USAGE_NOT_SHARED); - struct comp_buffer *sink = buffer_new(&test_buf_desc, BUFFER_USAGE_NOT_SHARED); + struct comp_buffer *source = buffer_new(NULL, &test_buf_desc, BUFFER_USAGE_NOT_SHARED); + struct comp_buffer *intm = buffer_new(NULL, &test_buf_desc, BUFFER_USAGE_NOT_SHARED); + struct comp_buffer *sink = buffer_new(NULL, &test_buf_desc, BUFFER_USAGE_NOT_SHARED); struct icomplex16 *out = (struct icomplex16 *)sink->stream.addr; float db; int64_t signal = 0; @@ -784,6 +815,10 @@ static void test_math_fft_1024_ifft_16(void **state) db = 10 * log10((float)signal / noise); printf("%s: SNR: %6.2f dB\n", __func__, db); assert_int_equal(db < FFT_DB_TH_16, 0); + + buffer_free(source); + buffer_free(intm); + buffer_free(sink); } int main(void) diff --git a/test/cmocka/src/math/fft/fft_multi.c b/test/cmocka/src/math/fft/fft_multi.c new file mode 100644 index 000000000000..bf5bd49efb08 --- /dev/null +++ b/test/cmocka/src/math/fft/fft_multi.c @@ -0,0 +1,193 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// Copyright(c) 2025 Intel Corporation. All rights reserved. +// +// Author: Seppo Ingalsuo <seppo.ingalsuo@linux.intel.com> + +#include <sof/audio/format.h> +#include <sof/math/icomplex16.h> +#include <sof/math/icomplex32.h> +#include <sof/math/fft.h> +#include "ref_fft_multi_96_32.h" +#include "ref_fft_multi_512_32.h" +#include "ref_fft_multi_768_32.h" +#include "ref_fft_multi_1024_32.h" +#include "ref_fft_multi_1536_32.h" +#include "ref_fft_multi_3072_32.h" + +#include "ref_ifft_multi_24_32.h" +#include "ref_ifft_multi_256_32.h" +#include "ref_ifft_multi_1024_32.h" +#include "ref_ifft_multi_1536_32.h" +#include "ref_ifft_multi_3072_32.h" + +#include <errno.h> +#include <stdlib.h> +#include <stdio.h> +#include <stdint.h> +#include <stdarg.h> +#include <stddef.h> +#include <setjmp.h> +#include <string.h> +#include <cmocka.h> +#include <math.h> + +#define FFT_MAX_ERROR_ABS 1050.0 /* about -126 dB */ +#define FFT_MAX_ERROR_RMS 35.0 /* about -156 dB */ +#define IFFT_MAX_ERROR_ABS 2400000.0 /* about -59 dB */ +#define IFFT_MAX_ERROR_RMS 44000.0 /* about -94 dB */ + +struct processing_module dummy; + +static void fft_multi_32_test(const int32_t *in_real, const int32_t *in_imag, + const int32_t *ref_real, const int32_t *ref_imag, + int num_bins, int num_tests, double max_error_abs, + double max_error_rms, bool do_ifft) +{ + struct icomplex32 *x; + struct icomplex32 *y; + struct fft_multi_plan *plan; + double delta; + double error_rms; + double delta_max = 0; + double sum_squares = 0; + const int32_t *p_in_real = in_real; + const int32_t *p_in_imag = in_imag; + const int32_t *p_ref_real = ref_real; + const int32_t *p_ref_imag = ref_imag; + int i, j; + FILE *fh1, *fh2; + + x = malloc(num_bins * sizeof(struct icomplex32)); + if (!x) { + fprintf(stderr, "Failed to allocate input data buffer.\n"); + assert_true(false); + } + + y = malloc(num_bins * sizeof(struct icomplex32)); + if (!y) { + fprintf(stderr, "Failed to allocate output data buffer.\n"); + assert_true(false); + } + + plan = mod_fft_multi_plan_new(&dummy, x, y, num_bins, 32); + if (!plan) { + fprintf(stderr, "Failed to allocate FFT plan.\n"); + assert_true(false); + } + + fh1 = fopen("debug_fft_multi_in.txt", "w"); + fh2 = fopen("debug_fft_multi_out.txt", "w"); + + for (i = 0; i < num_tests; i++) { + for (j = 0; j < num_bins; j++) { + x[j].real = *p_in_real++; + x[j].imag = *p_in_imag++; + fprintf(fh1, "%d %d\n", x[j].real, x[j].imag); + } + + fft_multi_execute_32(plan, do_ifft); + + for (j = 0; j < num_bins; j++) { + fprintf(fh2, "%d %d %d %d\n", + y[j].real, y[j].imag, *p_ref_real, *p_ref_imag); + delta = (double)*p_ref_real - (double)y[j].real; + sum_squares += delta * delta; + if (delta > delta_max) + delta_max = delta; + else if (-delta > delta_max) + delta_max = -delta; + + delta = (double)*p_ref_imag - (double)y[j].imag; + sum_squares += delta * delta; + if (delta > delta_max) + delta_max = delta; + else if (-delta > delta_max) + delta_max = -delta; + + p_ref_real++; + p_ref_imag++; + } + + } + + mod_fft_multi_plan_free(&dummy, plan); + free(y); + free(x); + fclose(fh1); fclose(fh2); + + error_rms = sqrt(sum_squares / (double)(2 * num_bins * num_tests)); + printf("Max absolute error = %5.2f (limit %5.2f), error RMS = %5.2f (limit %5.2f)\n", + delta_max, max_error_abs, error_rms, max_error_rms); + + assert_true(error_rms < max_error_rms); + assert_true(delta_max < max_error_abs); +} + +static void fft_multi_32_test_1(void **state) +{ + (void)state; + + /* Test FFT */ + fft_multi_32_test(fft_in_real_96_q31, fft_in_imag_96_q31, + fft_ref_real_96_q31, fft_ref_imag_96_q31, + 96, REF_SOFM_FFT_MULTI_96_NUM_TESTS, + FFT_MAX_ERROR_ABS, FFT_MAX_ERROR_RMS, false); + fft_multi_32_test(fft_in_real_512_q31, fft_in_imag_512_q31, + fft_ref_real_512_q31, fft_ref_imag_512_q31, + 512, REF_SOFM_FFT_MULTI_512_NUM_TESTS, + FFT_MAX_ERROR_ABS, FFT_MAX_ERROR_RMS, false); + fft_multi_32_test(fft_in_real_768_q31, fft_in_imag_768_q31, + fft_ref_real_768_q31, fft_ref_imag_768_q31, + 768, REF_SOFM_FFT_MULTI_768_NUM_TESTS, + FFT_MAX_ERROR_ABS, FFT_MAX_ERROR_RMS, false); + fft_multi_32_test(fft_in_real_1024_q31, fft_in_imag_1024_q31, + fft_ref_real_1024_q31, fft_ref_imag_1024_q31, + 1024, REF_SOFM_FFT_MULTI_1024_NUM_TESTS, + FFT_MAX_ERROR_ABS, FFT_MAX_ERROR_RMS, false); + fft_multi_32_test(fft_in_real_1536_q31, fft_in_imag_1536_q31, + fft_ref_real_1536_q31, fft_ref_imag_1536_q31, + 1536, REF_SOFM_FFT_MULTI_1536_NUM_TESTS, + FFT_MAX_ERROR_ABS, FFT_MAX_ERROR_RMS, false); + fft_multi_32_test(fft_in_real_3072_q31, fft_in_imag_3072_q31, + fft_ref_real_3072_q31, fft_ref_imag_3072_q31, + 3072, REF_SOFM_FFT_MULTI_3072_NUM_TESTS, + FFT_MAX_ERROR_ABS, FFT_MAX_ERROR_RMS, false); + fft_multi_32_test(fft_in_real_3072_q31, fft_in_imag_3072_q31, + fft_ref_real_3072_q31, fft_ref_imag_3072_q31, + 3072, REF_SOFM_FFT_MULTI_3072_NUM_TESTS, + FFT_MAX_ERROR_ABS, FFT_MAX_ERROR_RMS, false); + + /* Test IFFT */ + fft_multi_32_test(ifft_in_real_24_q31, ifft_in_imag_24_q31, + ifft_ref_real_24_q31, ifft_ref_imag_24_q31, + 24, REF_SOFM_IFFT_MULTI_24_NUM_TESTS, + IFFT_MAX_ERROR_ABS, IFFT_MAX_ERROR_RMS, true); + fft_multi_32_test(ifft_in_real_256_q31, ifft_in_imag_256_q31, + ifft_ref_real_256_q31, ifft_ref_imag_256_q31, + 256, REF_SOFM_IFFT_MULTI_256_NUM_TESTS, + IFFT_MAX_ERROR_ABS, IFFT_MAX_ERROR_RMS, true); + fft_multi_32_test(ifft_in_real_1024_q31, ifft_in_imag_1024_q31, + ifft_ref_real_1024_q31, ifft_ref_imag_1024_q31, + 1024, REF_SOFM_IFFT_MULTI_1024_NUM_TESTS, + IFFT_MAX_ERROR_ABS, IFFT_MAX_ERROR_RMS, true); + fft_multi_32_test(ifft_in_real_1536_q31, ifft_in_imag_1536_q31, + ifft_ref_real_1536_q31, ifft_ref_imag_1536_q31, + 1536, REF_SOFM_IFFT_MULTI_1536_NUM_TESTS, + IFFT_MAX_ERROR_ABS, IFFT_MAX_ERROR_RMS, true); + fft_multi_32_test(ifft_in_real_3072_q31, ifft_in_imag_3072_q31, + ifft_ref_real_3072_q31, ifft_ref_imag_3072_q31, + 3072, REF_SOFM_IFFT_MULTI_3072_NUM_TESTS, + IFFT_MAX_ERROR_ABS, IFFT_MAX_ERROR_RMS, true); +} + +int main(void) +{ + const struct CMUnitTest tests[] = { + cmocka_unit_test(fft_multi_32_test_1), + }; + + cmocka_set_message_output(CM_OUTPUT_TAP); + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/test/cmocka/src/math/fft/ref_dft3.m b/test/cmocka/src/math/fft/ref_dft3.m new file mode 100644 index 000000000000..c3f55d91b3e3 --- /dev/null +++ b/test/cmocka/src/math/fft/ref_dft3.m @@ -0,0 +1,59 @@ +% ref_dft3 - Generate C header files for DFT3 function unit tests + +% SPDX-License-Identifier: BSD-3-Clause +% +% Copyright(c) 2025 Intel Corporation. All rights reserved. + +function ref_dft3() + + path(path(), '../../../m'); + opt.describe = export_get_git_describe(); + + N = 3; + num_tests = 100; + scale_q31 = 2^31; + opt.bits = 32; + min_int32 = int32(-2^31); + max_int32 = int32(2^31 - 1); + + % Random values + input_data_real_q31 = int32((2 * rand(N, num_tests) - 1) * scale_q31); + input_data_imag_q31 = int32((2 * rand(N, num_tests) - 1) * scale_q31); + + % Apply max and min values to first two tests + input_data_real_q31(:,1) = [max_int32 max_int32 max_int32]; + input_data_imag_q31(:,1) = [max_int32 max_int32 max_int32]; + input_data_real_q31(:,2) = [min_int32 min_int32 min_int32]; + input_data_imag_q31(:,2) = [min_int32 min_int32 min_int32]; + + % Convert to float for reference DFT + input_data_real_f = double(input_data_real_q31) / scale_q31; + input_data_imag_f = double(input_data_imag_q31) / scale_q31; + input_data_f = complex(input_data_real_f, input_data_imag_f); + + ref_data_f = zeros(N, num_tests); + for i = 1:num_tests + ref_data_f(:,i) = fft(1/N * input_data_f(:,i)); + end + + input_data_vec_f = reshape(input_data_f, N * num_tests, 1); + input_data_real_q31 = int32(real(input_data_vec_f) * scale_q31); + input_data_imag_q31 = int32(imag(input_data_vec_f) * scale_q31); + + ref_data_vec_f = reshape(ref_data_f, N * num_tests, 1); + ref_data_real_q31 = int32(real(ref_data_vec_f) * scale_q31); + ref_data_imag_q31 = int32(imag(ref_data_vec_f) * scale_q31); + + header_fn = sprintf('ref_dft3_32.h'); + fh = export_headerfile_open(header_fn); + comment = sprintf('Created %s with script ref_dft3.m %s', ... + datestr(now, 0), opt.describe); + export_comment(fh, comment); + export_ndefine(fh, 'REF_SOFM_DFT3_NUM_TESTS', num_tests); + export_vector(fh, opt.bits, 'input_data_real_q31', input_data_real_q31); + export_vector(fh, opt.bits, 'input_data_imag_q31', input_data_imag_q31); + export_vector(fh, opt.bits, 'ref_data_real_q31', ref_data_real_q31); + export_vector(fh, opt.bits, 'ref_data_imag_q31', ref_data_imag_q31); + fclose(fh); + fprintf(1, 'Exported %s.\n', header_fn); +end diff --git a/test/cmocka/src/math/fft/ref_dft3_32.h b/test/cmocka/src/math/fft/ref_dft3_32.h new file mode 100644 index 000000000000..dc29c737e552 --- /dev/null +++ b/test/cmocka/src/math/fft/ref_dft3_32.h @@ -0,0 +1,220 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * + * Copyright(c) 2025 Intel Corporation. + */ + +/* Created 18-Nov-2025 10:04:16 with script ref_dft3.m v1.9-rc1-6866-g7082edfc2-dirty */ + +#define REF_SOFM_DFT3_NUM_TESTS 100 + +static const int32_t input_data_real_q31[300] = { + 2147483647, 2147483647, 2147483647, -2147483648, -2147483648, -2147483648, + 1162219834, 23816319, -504635601, -893753638, 2096234199, 1926797429, + -1498879994, -1344377511, -1565629944, 470700317, 1534215118, 250204116, + -1513962524, 328839298, -891150595, 1037032885, 2053083918, 214745934, + -1132393293, -684253779, 1290308737, 1492556115, 1767631432, -1476198666, + -1980706125, -152724048, 1304919915, -333340349, -945811108, 555573004, + 508704347, -91940743, -1172534471, 808237431, -864171862, -1701987975, + 667143603, 466065800, -680587634, -1210890266, -1609079348, 376693582, + -1989200673, -866348502, -1518045121, -810592170, -2014026366, 308045234, + -893653199, -71890004, 1798867597, -1165288212, -195853850, 727803849, + -827203566, -763401435, -555032730, 2049556353, -1733329582, 1857973403, + -969720479, 474304971, -118122188, -1414917124, 755553858, 512336983, + -412624469, -654030354, -673412294, -480554119, 2141467418, 982286252, + -333493549, -1792914214, -751632737, 1453631035, -567352454, -1121160722, + 1835418889, 1469373494, 1260799356, 1916321534, 1046738218, -482282675, + -1777733492, 1417780825, -1905923885, 1720942739, -1698447345, 1524731616, + -940329093, 474359626, 869280780, -1587738164, 718005895, -1275483558, + 1311357743, -1127231201, 1170568884, 351336134, 912933315, 929235364, + -1766156248, -751021719, -1575099846, -500683117, 758133192, -1000251215, + -460549792, -872077348, 1190236123, -2094039372, -97472255, 1766680277, + 1682916204, 1878420776, -1937130520, -1305433548, -2023614346, -961620965, + -1144745178, 349170968, -990207654, 2066179435, -409481891, -1640628527, + -1748672103, 1509049194, 2136386712, 749103155, 1869393346, -473447757, + 188132091, 471958830, 565514557, 24038287, 250957039, 555942787, + -1417341343, 1310588913, -1980667608, 1978973460, -1886552564, -931043047, + 240691726, 366351147, 856435514, -2100179945, 1322815981, -1993386310, + -1907183049, 1018981874, -1982669624, -392347504, 283140809, -701932511, + 827970927, 1087003102, 1539141719, 575668370, -284136687, -166908907, + 1086613743, -1578737459, 1755161659, -363056680, -972520979, 1833561918, + -691836837, 2043720743, -142353127, 1166774227, 991149245, -1806872349, + -1973652264, 1188981112, 1911685701, -968798985, 2077908949, -166982863, + 1326781679, -1986923452, -1067800528, 1854266938, 1333307796, -241964941, + -267584968, 886003675, -805272619, 1479927684, 159424368, 139369932, + -313191797, -791964593, 123256824, 1761190835, 267191061, -1066245064, + 2059356024, 983929284, -988243656, -2128354307, 1321996812, 279274571, + -958912371, 115946572, 1492522503, -1102123606, -1958076794, -1473281696, + -511493535, -162111044, -1652434912, 515681118, 1328854757, 1615582785, + 838101882, -981669826, -85699769, 826563860, -1054635732, 276608854, + -285946678, 1081455066, 1239400031, 344174895, -152045930, -1779441010, + -1443541302, -626187735, -7031089, -314268939, 575767023, -18068620, + 857747866, -1026238975, 1741588511, -825216518, -657754491, -1800153864, + 902250246, 1007864270, 1984210739, 1594306126, -365683058, -306799624, + 728125587, -1705315469, -445468326, 1860359390, 1561088340, 996962395, + 636780028, 1069385362, 49149330, -1167618105, -2051885175, 1699344213, + -450653472, 28073512, 1407413309, -1332191163, 1302508825, 589871272, + -769422448, -1945782013, -1273023036, -2144421096, -981418969, 1126299923, + -1473462655, -1468616147, 1514904515, 1873597062, 977229454, -190860952, + -1450912250, -1515509653, -1706549902, -1798566387, -1488199231, 465976855, + 1507852161, 1334015424, -1930050220, -839537711, -519449681, 1802561273, + -1686609113, -988247120, -2018183481, -1422312120, -524629977, 777771469, +}; + +static const int32_t input_data_imag_q31[300] = { + 2147483647, 2147483647, 2147483647, -2147483648, -2147483648, -2147483648, + -447071638, -710569404, -521054814, -1525984216, 1485866355, -1887906218, + 694991588, -1423316381, -526983997, 1416498079, -9059322, -1894876350, + 529729352, -1667720604, -1765426014, 1622805049, 1403298661, -56966777, + 1372247161, -989865104, 226982867, 1200666007, -1172962658, -327728731, + -1770194241, 2063801732, -1222401122, -1437089259, 7163116, -1667845557, + 699401620, -767508329, 1458191154, 2102543710, -386800992, 984597551, + -1002742709, -1568629571, -502851595, -51793840, -709663670, 1748043151, + 1096459944, 1109269442, 1991276284, 1630095127, -412134472, -1039994124, + -769839143, -2071887810, -1473927075, 143017106, 792574136, 1214806759, + -1707309285, -1546417474, 1644851668, 1369929054, -1504595449, -504861820, + 941716193, 1798841734, 1004503064, -548152154, 1433124598, 75564731, + -837059818, -1634446222, 1096996669, 760853936, 158476524, 1288109053, + -545735405, -1072909043, 1216016240, 1728999756, 1301464866, 1708661027, + 424731640, -179953738, 1829344056, -325009476, 1191455307, 1243675521, + 206426657, -785131901, 513197679, -1441381381, -1879255156, -626702268, + 1196431670, -484279907, 2027499180, 947791638, -1004082133, 1126341198, + -762028151, -776494670, -1745568389, -1969907879, 894288459, 106735650, + -272461269, -337916005, -1243196237, -395332730, 1275277656, -1472977807, + -938816844, 1834539458, 1332936891, 878083108, -45073545, -1185953597, + -717818068, -30126320, 1482293565, -1904955333, -932393894, -2110646291, + -525568699, -1934721075, -841727840, 199552072, 402812734, 545621043, + 2041424330, 636650031, 262616779, 2027512409, -1473662400, -2131440924, + 86104846, 858180945, -1162684732, -1853469689, -1511760166, -2140871371, + -16978023, 1565504329, 1807701041, 838301612, -432147051, 1812146715, + 358112650, -1258170956, 688314303, -938717727, 1479050461, -808626808, + -1801201683, -567689961, 409371332, -1131239726, -219900330, 1662293964, + -1591338732, -1898978738, -1394863666, -1673116723, -1385225905, -506268616, + 1968110246, -124595511, 1787454593, 579998873, -556710560, 1912851650, + -99737218, -2012489847, 248120965, 283176403, 1573201156, -759852424, + -1562343755, 1375885975, -1557867556, 135286679, 920363352, 1029195101, + 1429284623, 1861442578, 1690850333, -2039742677, -452522368, 94467724, + 603284320, 370872395, -1416940658, 1020409603, -181807657, -1585188066, + -131317730, 1841147202, 751515590, 1429723577, -1685642094, 1194135059, + 782914220, 1896122751, 1008900429, 937134356, 54677423, -1294082603, + -1394821557, -1413641053, -1995302037, 1412674184, -2090278821, -818075169, + 412258694, -646997248, -1371284669, -943407015, -277753547, -1009224290, + -714869328, 759608215, 1616359360, -754310245, 1178040373, -1748780178, + -283459899, 863462232, 844805157, 302181273, -1799234263, 299774378, + 902067000, 2019733716, -779964295, -1476250288, 174210071, 1848240816, + -417614345, -1449712719, -232573863, -1033791826, -1734572491, 2059952961, + 164824306, -1192574723, -794465893, -2100496663, 1966082659, 662383049, + -1724169177, 1397272869, 443158715, -1466228413, -1048441604, -721618311, + 607147167, 405920890, 1890695467, 1700845925, -861572606, -193465319, + -699440151, -1445722888, -1602829412, 2051117803, -720439151, -1947113131, + -390310420, -1650048864, 841983379, 476615869, -1467229630, 43603884, + -1704704811, -1363183779, 1599254898, 174995960, 1261823464, -2010724063, + -1013060202, 983314876, -1374232138, 2105244869, -1751771177, -2066157313, + -200819569, 997385318, 1618215010, -2057992268, 1973711259, -518966102, + 1974013954, -1632849536, -1429309305, -817476773, -724148800, -1290709464, +}; + +static const int32_t ref_data_real_q31[300] = { + 2147483647, 0, 0, -2147483648, 0, 0, + 227133517, 412835009, 522251308, 1043092663, 5501101, -1942347402, + -1469629150, -273374294, 244123449, 751706517, 403885384, -684891584, + -692091274, -382730503, -439140748, 1101620912, 389248308, -453836336, + -175446112, -829747342, -127199839, 594662960, 204948560, 692944595, + -276170086, 96377032, -1800913071, -241192818, 437459588, -529607120, + -251923622, -262190113, 1022818082, -585974135, 301217124, 1092994442, + 150873923, -49528761, 565798441, -814425344, -907711308, 511246386, + -1457864765, -520281398, -11054510, -838857767, 195380268, -167114671, + 277774798, -758330394, -413097603, -211112738, -598975796, -355199678, + -715212577, -977235544, 865244555, 724733391, 373813241, 951009721, + -204512565, -153298134, -611909779, -49008761, -291060404, -1074847959, + -580022372, -704800693, 872198596, 881066517, -1006907140, -354713496, + -959346833, -347829172, 973682456, -78294047, 648415134, 883509948, + 1521863913, -423256823, 736811799, 826925692, 529623244, 559772598, + -755292184, -886016120, -136425188, 515742337, 241019328, 964181075, + 134437104, -1262471265, 187705067, -715071942, -1051333353, 178667131, + 451565142, 709643787, 150148814, 731168271, 37430845, -417262982, + -1364092604, 60300071, -462363715, -247600380, 666811647, -919894384, + -47463672, -61742871, -351343248, -141610450, -646870758, -1305558164, + 541402153, 134159011, 1007355039, -1430222953, 402526872, -277737467, + -595260621, -590262248, 40777691, 5356339, 989186340, 1071636756, + 632254601, -1082489253, -1298437451, 715016248, 206927757, -172840850, + 408535159, 473172137, -693575205, 276979371, 55138220, -308079304, + -695806679, -430683500, -290851163, -279540717, 481385284, 1777128893, + 487826129, -685469096, 438334693, -923583425, 72097283, -1248693804, + -956956933, -757166358, -193059758, -270379735, -604326575, 482358807, + 1151371916, -307225981, -16175008, 41540925, 13330609, 520796836, + 421012648, -219160773, 884761869, 165994753, -977426920, 448375487, + 403176926, -1200089012, 105075249, 117017041, 1198373149, -148615963, + 375671516, -327760195, -2021563586, 314042367, -672837696, -610003656, + -575980767, 1000626962, 902135484, 981869931, 278296065, 594100942, + -62284637, 413447008, -618747339, 592907328, 848631206, 38389150, + -327299855, 321603581, -307495523, 320712277, -111080778, 1551559336, + 685013884, 943290093, 431052047, -175694308, -586976517, -1365683482, + 216518901, -419804573, -755626699, -1511160699, -162735014, 571772107, + -775346497, 341010250, -77157288, 1153372887, -107688469, -530003300, + -76422571, 209939474, 704584979, 16178994, 1250092749, -439707883, + 678302806, -476738909, -487510576, -529104015, -169292147, 1042571057, + -692253375, 432559237, -1183847163, 81143155, -680957097, 285545004, + 524365801, -184666690, 518048756, -1094374958, -960805926, 1229964365, + 1298108418, -312853206, -83004966, 307274481, 1019861483, 267170162, + -474219403, 876601527, 325743463, 1472803375, 99432249, 288123766, + 585104907, -402779940, 454455062, -506719689, -523315169, -137583247, + 328277783, -344112881, -434818374, 186729645, -405350128, -1113570680, + -1329409166, -439394384, 999381102, -666513381, -1175093926, -302813790, + -475724762, -1354051330, 356313437, 886655188, 1438174035, -451232161, + -1557657268, 733937711, -627192692, -940262921, -338396273, -519907193, + 303939122, 422738425, 781174615, 147857960, 225876137, -1213271808, + -1564346571, -119888274, -2374267, -389723543, -352742313, -679846265, +}; + +static const int32_t ref_data_imag_q31[300] = { + 2147483647, 0, 0, -2147483648, 0, 0, + -559565285, -96304105, 208797753, -642674693, -490566944, -392742579, + -418436263, 492843850, 620584002, -162479198, 418826590, 1160150687, + -967805755, 396586807, 1100948300, 989712311, -214136096, 847228834, + 203121641, 1154569860, 14555660, -100008461, -286075856, 1586750324, + -309597877, -309512615, -1151083749, -1032590567, 231162914, -635661607, + 463361482, -193920471, 429960609, 900113423, 359358464, 843071823, + -1024741292, -320011043, 342009626, 328861880, 382915408, -763571128, + 1399001890, -339399582, 36857636, 59322177, 1455710807, 115062143, + -1438551343, 874397302, -205685102, 716799334, -20254103, -553528124, + -536291697, -525357930, -645659658, -213176072, 1828272435, -245167310, + 1248353664, -324337725, 17700255, 320179058, -504376270, -363954942, + -458169790, -195040098, -183849930, 735813171, -322106397, 347147162, + -134209403, 94829069, -506355072, 1579708550, -85225073, 234516279, + 691373986, -193531340, -73111006, 703373784, -955581942, -72801318, + -21835855, -845339649, 1073602161, -1315779602, 867650731, -993252510, + 913216981, 255611262, 27603427, 356683568, -279916801, 871024871, + -1094697070, 829652208, -496983289, -322961257, -818767315, -828179307, + -617857837, -65192580, 410589148, -197677627, -606429407, 408774304, + 742886502, -245513054, -1436190292, -117648011, 1036000043, -40268923, + 244783059, -1582755347, 620154220, -1649331839, 178759335, -434382829, + -1100672538, -99093384, 674197223, 382661950, -446956360, 263846482, + 980230380, 711693717, 349500233, -525863638, 600368053, 1953007994, + -72799647, 106459459, 52445034, -1835367075, 78990495, -97093109, + 1118742449, -1517964155, 382243683, 739433759, 325265765, -226397912, + -70581334, 355822163, 72871822, -89431358, -1381948327, 532661958, + -653173437, -1440516273, 292488027, 103717969, -901845021, -333112674, + -1628393712, 149048666, -111993686, -1188203748, -208615742, -276297233, + 1210323109, 1341307345, -583520208, 645379988, 777355801, -842736915, + -621368700, -370249428, 891880910, 365508378, -848885248, 766553273, + -581441779, -281824144, -699077833, 694948377, -927875295, 368213597, + 1660525845, 149707323, -380948545, -799265774, -1074980521, -165496382, + -147594648, -112789928, 863668896, -248862040, 628846604, 640425039, + 820448354, -211681376, -740084708, 312738847, 173562512, 943422218, + 1229312467, -792516412, 346118166, -100756941, 217937665, 819953632, + -1601254882, 500599905, -294166579, -498559935, 1095565350, 815668770, + -535341074, 43580441, 904019327, -743461617, -17201447, -182743951, + 553699416, -375640095, -892928649, -441683350, 227983763, -540610658, + 474935830, -333603080, -424792649, -399092871, -119151422, 820425566, + 713945474, 272795891, -84674365, 182066866, -1000584161, -657732993, + -699966976, 940179287, -657826657, -236137119, -728609646, -69045061, + -607405437, 667961820, 104267923, 175989682, -1121244989, -1155241356, + 38754136, -517775113, -1245148200, -1078762776, -356581952, -30883685, + 967921175, -474903778, 114129770, 215269333, 1825674944, -340098353, + -1249330817, 673126435, -123235769, -205478160, 922577240, 1334018723, + -399458635, 198782896, -189634681, -315669959, 1004588949, -212303121, + -489544564, 253688105, -1468848352, -191301546, -154049902, 520347408, + -467992488, -327682427, -217385287, -570894540, 1902191749, 773947660, + 804926920, -1445127833, 439381345, -201082370, -258148124, -1598761774, + -362714962, 871047441, 1465681476, -944111679, 439288366, -312653460, +}; diff --git a/test/cmocka/src/math/fft/ref_fft_multi.m b/test/cmocka/src/math/fft/ref_fft_multi.m new file mode 100644 index 000000000000..57b1218f64dc --- /dev/null +++ b/test/cmocka/src/math/fft/ref_fft_multi.m @@ -0,0 +1,138 @@ +% ref_sofm_dft3 - Generate C header files for DFT3 function unit tests + +% SPDX-License-Identifier: BSD-3-Clause +% +% Copyright(c) 2025 Intel Corporation. All rights reserved. + +function ref_fft_multi() + + rand('twister', 0); % Set seed to produce same test vectors every time + path(path(), '../../../m'); + opt.describe = export_get_git_describe(); + opt.bits = 32; + opt.fs = 48e3; + opt.ifft = 0; + opt.sine = 1; + opt.rand = 0; + opt.dc = 0; + opt.num_tests = 1; + + N = 96; + make_fft_multi_test_vectors(opt, N); + + N = 512; + make_fft_multi_test_vectors(opt, N); + + N = 768; + make_fft_multi_test_vectors(opt, N); + + N = 1024; + make_fft_multi_test_vectors(opt, N); + + N = 1536; + make_fft_multi_test_vectors(opt, N); + + N = 3072; + make_fft_multi_test_vectors(opt, N); + + opt.ifft = 1; + opt.dc = 0; + opt.sine = 1; + opt.rand = 0; + + N = 24; + make_fft_multi_test_vectors(opt, N); + + N = 256; + make_fft_multi_test_vectors(opt, N); + + N = 1024; + make_fft_multi_test_vectors(opt, N); + + N = 1536; + make_fft_multi_test_vectors(opt, N); + + N = 3072; + make_fft_multi_test_vectors(opt, N); + +end + +function make_fft_multi_test_vectors(opt, N) + + scale_q = 2^(opt.bits - 1); + min_int = int32(-scale_q); + max_int = int32(scale_q - 1); + n = 1; + + input_data_real_q = int32(zeros(N, opt.num_tests)); + input_data_imag_q = int32(zeros(N, opt.num_tests)); + + if opt.dc + input_data_real_q(:,n) = int32(ones(N, 1) * scale_q / N); + n = n + 1; + end + if opt.rand + input_data_real_q(:,n) = int32(2 * (rand(N, 1) - 1) * scale_q * 0.1); + input_data_imag_q(:,n) = int32(2 * (rand(N, 1) - 1) * scale_q * 0.1); + n = n + 1; + end + if opt.sine + ft = 997; + t = (0:(N - 1))'/opt.fs; + x = 10^(-1 / 20) * sin(2 * pi * ft * t) .* kaiser(N, 20); + dither = scale_q / 2^19 * (rand(N, 1) + rand(N, 1) - 1); + input_data_real_q(:,n) = int32(x * scale_q + dither); + if opt.ifft + tmp_fft = fft(double(input_data_real_q(:,n)) / scale_q) / N; + input_data_real_q(:,n) = int32(real(tmp_fft) * scale_q); + input_data_imag_q(:,n) = int32(imag(tmp_fft) * scale_q); + end + n = n + 1; + end + + if opt.ifft + N_half = N/2 + 1; + for i = 1:opt.num_tests + input_data_real_q(N_half + 1:end) = input_data_real_q(N_half - 1:-1:2); + input_data_imag_q(N_half + 1:end) = -input_data_imag_q(N_half - 1:-1:2); + end + end + + input_data_real_f = double(input_data_real_q) / scale_q; + input_data_imag_f = double(input_data_imag_q) / scale_q; + input_data_f = complex(input_data_real_f, input_data_imag_f); + + ref_data_f = zeros(N, opt.num_tests); + for i = 1:opt.num_tests + if opt.ifft + ref_data_f(:,i) = ifft(input_data_f(:,i)) * N; + test_type = 'ifft'; + else + ref_data_f(:,i) = fft(input_data_f(:,i)) / N; + test_type = 'fft'; + end + end + + input_data_vec_f = reshape(input_data_f, N * opt.num_tests, 1); + input_data_real_q = int32(real(input_data_vec_f) * scale_q); + input_data_imag_q = int32(imag(input_data_vec_f) * scale_q); + + ref_data_vec_f = reshape(ref_data_f, N * opt.num_tests, 1); + ref_data_real_q = int32(real(ref_data_vec_f) * scale_q); + ref_data_imag_q = int32(imag(ref_data_vec_f) * scale_q); + + header_fn = sprintf('ref_%s_multi_%d_%d.h', test_type, N, opt.bits); + fh = export_headerfile_open(header_fn); + comment = sprintf('Created %s with script ref_fft_multi.m %s', ... + datestr(now, 0), opt.describe); + export_comment(fh, comment); + dstr = sprintf('REF_SOFM_%s_MULTI_%d_NUM_TESTS', upper(test_type), N); + export_ndefine(fh, dstr, opt.num_tests); + qbits = opt.bits-1; + vstr = sprintf('%s_in_real_%d_q%d', test_type, N, qbits); export_vector(fh, opt.bits, vstr, input_data_real_q); + vstr = sprintf('%s_in_imag_%d_q%d', test_type, N, qbits); export_vector(fh, opt.bits, vstr, input_data_imag_q); + vstr = sprintf('%s_ref_real_%d_q%d', test_type, N, qbits); export_vector(fh, opt.bits, vstr, ref_data_real_q); + vstr = sprintf('%s_ref_imag_%d_q%d', test_type, N, qbits); export_vector(fh, opt.bits, vstr, ref_data_imag_q); + fclose(fh); + fprintf(1, 'Exported %s.\n', header_fn); +end diff --git a/test/cmocka/src/math/fft/ref_fft_multi_1024_32.h b/test/cmocka/src/math/fft/ref_fft_multi_1024_32.h new file mode 100644 index 000000000000..e9482fd88f05 --- /dev/null +++ b/test/cmocka/src/math/fft/ref_fft_multi_1024_32.h @@ -0,0 +1,704 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * + * Copyright(c) 2025 Intel Corporation. + */ + +/* Created 20-Nov-2025 16:11:52 with script ref_fft_multi.m v1.9-rc1-6882-ge0b90b605-dirty */ + +#define REF_SOFM_FFT_MULTI_1024_NUM_TESTS 1 + +static const int32_t fft_in_real_1024_q31[1024] = { + -368, 399, 1137, -1510, 917, -915, + 1477, 4004, 26, -670, 19, -245, + 2071, 2623, -2126, 2311, 2688, 609, + -1461, 4166, -310, 1858, -248, -2221, + 2398, -796, -698, -2996, -2571, -2483, + -6926, -3796, -6581, -7667, -9040, -7126, + -9738, -14377, -11690, -11331, -14016, -14971, + -16347, -13087, -10581, -9389, -7947, -3776, + -2708, 3498, 9037, 13136, 16328, 23190, + 32849, 35886, 48147, 54704, 58656, 61210, + 72193, 75724, 79356, 83168, 79591, 79627, + 74772, 71013, 60544, 48856, 38174, 19894, + 7444, -19623, -39518, -64353, -90252, -120797, + -153590, -180846, -212338, -240701, -268837, -292589, + -315071, -330749, -341626, -350148, -349717, -341020, + -321991, -296385, -259725, -214521, -161010, -95406, + -24910, 56696, 146420, 244250, 343559, 452730, + 558328, 663250, 762184, 865981, 953691, 1033614, + 1102113, 1146873, 1184623, 1193892, 1178554, 1142859, + 1077203, 986336, 862134, 713246, 531955, 325661, + 92277, -163732, -444123, -741602, -1049074, -1363826, + -1673315, -1986171, -2284459, -2565083, -2814370, -3040133, + -3213891, -3347594, -3418856, -3434223, -3384851, -3262396, + -3066903, -2788032, -2437241, -2010678, -1507173, -933398, + -292175, 401186, 1147352, 1924628, 2732596, 3549354, + 4364626, 5154297, 5908401, 6604560, 7227639, 7762620, + 8187446, 8485952, 8644128, 8647068, 8487648, 8152221, + 7637666, 6938503, 6053636, 4993121, 3757798, 2362454, + 824586, -836731, -2601505, -4437764, -6320658, -8213153, + -10084097, -11885518, -13592349, -15153379, -16539412, -17701707, + -18614256, -19232046, -19532284, -19487537, -19071832, -18272835, + -17080871, -15486581, -13507411, -11145107, -8429888, -5384383, + -2046831, 1539831, 5322377, 9232369, 13214694, 17192520, + 21092853, 24835370, 28351421, 31545946, 34345883, 36680788, + 38470656, 39655664, 40177693, 39987331, 39049848, 37341196, + 34842396, 31562278, 27514832, 22737753, 17268732, 11184374, + 4553185, -2521582, -9942347, -17586988, -25318003, -33004376, + -40502110, -47661459, -54324353, -60352266, -65595404, -69912251, + -73185900, -75289166, -76129593, -75628186, -73725851, -70384308, + -65592931, -59374400, -51767812, -42836205, -32691229, -21454188, + -9277580, 3661148, 17155201, 30992883, 44929675, 58721466, + 72101400, 84812804, 96588363, 107172798, 116308292, 123771871, + 129349707, 132844569, 134111243, 133017754, 129482950, 123461622, + 114953182, 103996453, 90695125, 75177530, 57638338, 38301433, + 17446062, -4619599, -27538997, -50938883, -74412221, -97544926, + -119890697, -141021854, -160509246, -177915765, -192864953, -204975605, + -213910563, -219386073, -221174163, -219080029, -213000947, -202885489, + -188762284, -170723570, -148948541, -123686136, -95259386, -64055427, + -30526184, 4796392, 41356982, 78546374, 115716100, 152196811, + 187309768, 220376926, 250730546, 277732060, 300774553, 319307850, + 332839493, 340957166, 343321132, 339690110, 329923997, 313993109, + 291964388, 264026987, 230483360, 191744445, 148326011, 100855269, + 50040078, -3320582, -58361692, -114153996, -169724988, -224082501, + -276218832, -325139925, -369868955, -409475994, -443108525, -469982022, + -489407140, -500819525, -503770287, -497957407, -483219505, -459561117, + -427126685, -386245337, -337398907, -281210112, -218478327, -150122739, + -77193519, -857934, 77633280, 156947583, 235700971, 312486893, + 385900471, 454546040, 517078779, 572234396, 618846940, 655866323, + 682385018, 697661309, 701140616, 692455705, 671454559, 638191695, + 592953243, 536228161, 468740763, 391410722, 305371394, 211913742, + 112517115, 8779394, -97571576, -204721044, -310812944, -413944482, + -512242592, -603866176, -687055198, -760155313, -821656150, -870226938, + -904719195, -924232004, -928094286, -915902067, -887526961, -843128897, + -783147032, -708310751, -619625914, -518372450, -406053401, -284426943, + -155438894, -21198654, 116042557, 253946179, 390103902, 522103112, + 647549938, 764131222, 869639373, 962024696, 1039435602, 1100238386, + 1143082131, 1166889988, 1170916785, 1154742462, 1118300936, 1061877010, + 986124238, 892031533, 780943280, 654515630, 514699010, 363722438, + 204041308, 38296251, -130713333, -300089725, -466886857, -628155818, + -781011029, -922654557, -1050458538, -1161990550, -1255074187, -1327830448, + -1378699528, -1406489333, -1410401993, -1390048875, -1345442734, -1277046744, + -1185735381, -1072791871, -939913349, -789148010, -622904148, -443859970, + -254989459, -59453972, 139440960, 338268373, 533579021, 721939184, + 899993480, 1064547333, 1212580705, 1341356167, 1448429738, 1531710189, + 1589513254, 1620570159, 1624075071, 1599693216, 1547576100, 1468358349, + 1363160695, 1233557983, 1081585799, 909666978, 720606772, 517535787, + 303843415, 83155845, -140774037, -364089673, -582915806, -793434722, + -991929280, -1174879622, -1339004843, -1481331242, -1599239695, -1690521084, + -1753414617, -1786655673, -1789471827, -1761622946, -1703404590, -1615638103, + -1499681710, -1357368825, -1191014889, -1003370614, -797569666, -577065119, + -345612065, -107143552, 134247659, 374406599, 609174673, 834479923, + 1046391045, 1241200833, 1415476892, 1566138455, 1690498403, 1786340377, + 1851905391, 1885980406, 1887879581, 1857497280, 1795259750, 1702182967, + 1579787973, 1430138425, 1255747801, 1059596968, 845018463, 615693435, + 375556538, 128723997, -120541681, -367952127, -609245853, -840251626, + -1056983758, -1255713119, -1433002929, -1585806827, -1711487302, -1807896047, + -1873374729, -1906818169, -1907682853, -1875971972, -1812263992, -1717703065, + -1593954268, -1443193039, -1268056578, -1071600342, -857256745, -628746553, + -390033866, -145248896, 101367834, 345584281, 583192434, 810121403, + 1022514754, 1216749114, 1389559178, 1538036051, 1659720826, 1752625201, + 1815263564, 1846687542, 1846482241, 1814804568, 1752335291, 1660298657, + 1540415690, 1394895914, 1226369062, 1037859231, 832714547, 614553921, + 387208246, 154635241, -79138710, -310077686, -534233497, -747807910, + -947188843, -1129059176, -1290398152, -1428596523, -1541438634, -1627173606, + -1684538320, -1712767041, -1711595046, -1681286807, -1622597001, -1536771335, + -1425516195, -1290963285, -1135617456, -962344519, -774276536, -574780100, + -367390670, -155754790, 56472073, 265619298, 468138053, 660610367, + 839838555, 1002881515, 1147110243, 1270246183, 1370408966, 1446132923, + 1496387070, 1520594854, 1518642268, 1490871859, 1438060822, 1361413472, + 1262533951, 1143393613, 1006287680, 853798274, 688736004, 514105396, + 333012732, 148667398, -35733530, -217014467, -392110758, -558100965, + -712261129, -852105187, -975443549, -1080391668, -1165419174, -1229358286, + -1271414669, -1291210466, -1288731328, -1264359862, -1218860857, -1153341530, + -1069241701, -968310686, -852532664, -724155275, -585580585, -439357512, + -288125820, -134567565, 18641167, 168876530, 313606578, 450450547, + 577187923, 691823560, 792614885, 878071523, 947009493, 998553637, + 1032138861, 1047522337, 1044786400, 1024325396, 986827656, 933271381, + 864889514, 783149214, 689722654, 586443194, 475288458, 358321978, + 237687607, 115515445, -6047806, -124934122, -239156594, -346852125, + -446302911, -535987147, -614574458, -680951101, -734251225, -773853212, + -799381512, -810715674, -807980350, -791554564, -762030650, -720222509, + -667136883, -603952781, -532002178, -452725279, -367667954, -278430473, + -186653775, -93972100, -2008492, 87674367, 173587352, 254349650, + 328707091, 395532579, 453881657, 502968672, 542187532, 571122852, + 589545497, 597423743, 594906342, 582312760, 560132475, 529012045, + 489738788, 443207538, 390429237, 332480939, 270507669, 205698013, + 139248854, 72343880, 6161787, -58185019, -119638793, -177222667, + -230069141, -277392475, -318549855, -353017704, -380401245, -400449224, + -413034731, -418173539, -416007937, -406810636, -390947543, -368915371, + -341281976, -308709781, -271917570, -231677874, -188800752, -144107774, + -98436708, -52602612, -7411234, 36384414, 78073041, 117002200, + 152593715, 184344809, 211842274, 234752359, 252842032, 265962120, + 274065260, 277185824, 275449286, 269054677, 258286311, 243480550, + 225049447, 203437341, 179141946, 152679159, 124587493, 95420206, + 65717940, 36017421, 6836042, -21343530, -48068436, -72924828, + -95564270, -115673579, -133003852, -147361340, -158615881, -166696411, + -171587602, -173332374, -172021708, -167813897, -160892709, -151487639, + -139867533, -126332160, -111186743, -94771864, -77414398, -59468599, + -41264688, -23136882, -5393390, 11673173, 27787932, 42720560, + 56258677, 68224110, 78480457, 86924579, 93484306, 98135239, + 100882999, 101760369, 100841495, 98224064, 94030202, 88408482, + 81519998, 73548785, 64683174, 55119877, 45056490, 34697694, + 24240625, 13872761, 3766924, -5911143, -15005474, -23390680, + -30955991, -37604248, -43269353, -47895977, -51454249, -53937007, + -55362525, -55747797, -55145509, -53618025, -51236984, -48085430, + -44268219, -39886204, -35034027, -29836702, -24398794, -18829886, + -13230267, -7714345, -2360118, 2740293, 7512401, 11880497, + 15801966, 19227412, 22118471, 24467824, 26244407, 27468824, + 28135353, 28271925, 27906481, 27074587, 25818645, 24179863, + 22210583, 19974799, 17518427, 14905291, 12182096, 9415316, + 6650447, 3941996, 1322253, -1147115, -3452698, -5548001, + -7414029, -9030592, -10383149, -11471664, -12284872, -12825237, + -13105151, -13137584, -12935370, -12509734, -11898201, -11110368, + -10182305, -9134505, -7994237, -6789150, -5547559, -4288857, + -3040785, -1826870, -664203, 428580, 1435486, 2347513, + 3156311, 3844950, 4416372, 4868942, 5201790, 5414675, + 5520660, 5510424, 5404147, 5214941, 4938663, 4597616, + 4199190, 3756301, 3279832, 2777704, 2263902, 1749437, + 1241556, 754804, 295430, -138960, -532715, -883294, + -1191253, -1456374, -1673086, -1837247, -1953436, -2026864, + -2056034, -2045306, -1996661, -1918087, -1810768, -1676714, + -1527078, -1358080, -1181368, -994156, -808485, -625825, + -444558, -269900, -109532, 35633, 171250, 289167, + 391357, 477326, 547758, 597162, 631158, 651246, + 658747, 652056, 630488, 604961, 565338, 524089, + 468544, 414088, 361691, 302709, 245522, 189197, + 133521, 86065, 35459, -10093, -45678, -78284, + -107849, -127244, -147945, -159990, -166806, -172400, + -172766, -166698, -161371, -155608, -142927, -131151, + -117442, -103171, -87695, -73917, -59685, -43890, + -32345, -17413, -8851, 1262, 10615, 13688, + 22228, 26986, 33290, 32113, 31330, 36857, + 35941, 33027, 31131, 28558, 25825, 25129, + 20114, 20537, 15259, 10616, 7286, 6079, + 2197, 1142, 1303, 2290, -964, -3359, + -1473, -2972, -5153, -4402, -4875, -2764, + -2954, -2435, -6289, -2906, -1971, -1469, + -1069, -1732, -1718, -2575, -802, -2749, + 882, 1954, 1617, -292, -6, 2813, + 1640, -2041, -979, -122, -997, 1850, + 717, 1785, 1849, 1967, +}; + +static const int32_t fft_in_imag_1024_q31[1024] = { + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, +}; + +static const int32_t fft_ref_real_1024_q31[1024] = { + 28, -16, 40, -36, -11, 23, + -2, 7, 65, 28, -27, 3, + 62, -17, 36, 122, -55154, 1535502, + -13543813, 57130836, -135763827, 195823639, -176199334, 98119761, + -32279433, 5635597, -409816, 6072, -21, -83, + -21, 67, -30, -1, 1, 3, + 42, -5, 77, 67, -11, -9, + 24, -27, -78, -11, 18, -61, + -28, 16, -7, 46, -11, 10, + -8, -47, 95, 12, -3, -22, + -8, -46, 24, 59, -6, 32, + 26, 18, 35, 8, 52, -23, + -67, -17, -26, -3, -10, -47, + 32, 35, -9, 38, 14, 30, + 36, 2, -7, 63, 32, -84, + 30, 19, -9, 6, 43, 11, + -2, -5, -47, -43, 4, 8, + -22, -6, -30, -63, -43, -14, + 20, 2, 86, -5, 39, -58, + 37, -42, -4, 27, 0, -16, + -30, 20, -67, -31, 40, -74, + 57, -62, -8, -6, 23, 13, + 3, 21, -51, 10, 64, -25, + 11, -3, 12, 38, -22, 18, + -5, 0, 41, -14, 30, -16, + -11, 11, -18, 36, -29, -54, + 20, -14, -6, -28, 21, 15, + 78, -17, 48, -23, -13, 16, + 27, -22, -12, -2, -85, -4, + -23, -43, 64, 6, 1, 31, + 35, 19, 23, -1, 22, 54, + 18, 6, -25, -42, 2, 41, + -18, -40, -43, 31, -26, 37, + -34, 13, 7, -16, 9, 72, + 38, 2, 44, -54, -28, -56, + 1, -58, 51, 59, 84, -48, + 39, -80, 6, 21, -6, 21, + -25, -24, -54, -67, -1, 11, + 4, -80, 3, -12, -24, -59, + -3, 57, -9, -36, -24, -28, + -31, -19, -40, -24, 4, -9, + 5, -64, -16, 25, 19, 53, + 2, -60, 48, -25, 55, -27, + 61, 17, 43, 36, -15, 6, + -98, -38, -4, 63, -14, 57, + 7, 19, -6, 12, -18, 51, + -48, 39, 4, -63, -3, -1, + -59, -15, 16, 61, -8, 12, + 5, 29, -38, 65, 29, -10, + -25, -19, 22, 17, -53, 12, + -29, 13, 7, -8, -27, 3, + 62, -4, -29, -35, 9, 40, + -69, 9, -23, 18, 21, -92, + -42, 20, -15, 5, 26, 20, + 32, 59, -17, 25, -3, 57, + -40, 9, 8, 90, -40, 16, + -119, -32, 26, -35, 18, 21, + -22, 45, 17, 29, -24, -14, + -44, -29, -59, -6, 17, -7, + -33, -37, 14, -53, 16, -17, + -59, -20, -24, -19, -58, -21, + 11, -12, 10, -13, 22, -18, + 4, -30, 53, -46, -6, -51, + 45, -21, 46, -1, 14, -22, + -33, -25, -28, 30, 1, 45, + 78, 6, -26, -6, 17, -27, + 52, -9, 20, -40, -3, 16, + 36, -21, -60, 2, 16, -56, + 13, 0, 20, -3, 46, -8, + -65, -2, -26, -27, 38, -41, + 32, -11, 6, 12, 55, -22, + 23, 47, 56, 25, -4, -27, + -14, -26, 17, -49, -5, 49, + -16, 29, -42, 51, -14, 56, + 18, -62, -31, -10, -38, -22, + -56, -60, -39, 42, 9, 90, + 29, 79, 27, 2, -27, -60, + -20, 7, 26, -6, 86, -94, + 0, -31, 7, -22, 46, 44, + 22, -64, 25, 47, -49, -24, + 62, 5, 7, -59, 40, -3, + -27, 16, 7, -32, -20, -41, + 84, -36, 21, -81, 96, 48, + 58, 16, -48, -16, -72, 29, + 36, 21, -30, -21, -55, 68, + -20, -40, -24, -40, -20, 68, + -55, -21, -30, 21, 36, 29, + -72, -16, -48, 16, 58, 48, + 96, -81, 21, -36, 84, -41, + -20, -32, 7, 16, -27, -3, + 40, -59, 7, 5, 62, -24, + -49, 47, 25, -64, 22, 44, + 46, -22, 7, -31, 0, -94, + 86, -6, 26, 7, -20, -60, + -27, 2, 27, 79, 29, 90, + 9, 42, -39, -60, -56, -22, + -38, -10, -31, -62, 18, 56, + -14, 51, -42, 29, -16, 49, + -5, -49, 17, -26, -14, -27, + -4, 25, 56, 47, 23, -22, + 55, 12, 6, -11, 32, -41, + 38, -27, -26, -2, -65, -8, + 46, -3, 20, 0, 13, -56, + 16, 2, -60, -21, 36, 16, + -3, -40, 20, -9, 52, -27, + 17, -6, -26, 6, 78, 45, + 1, 30, -28, -25, -33, -22, + 14, -1, 46, -21, 45, -51, + -6, -46, 53, -30, 4, -18, + 22, -13, 10, -12, 11, -21, + -58, -19, -24, -20, -59, -17, + 16, -53, 14, -37, -33, -7, + 17, -6, -59, -29, -44, -14, + -24, 29, 17, 45, -22, 21, + 18, -35, 26, -32, -119, 16, + -40, 90, 8, 9, -40, 57, + -3, 25, -17, 59, 32, 20, + 26, 5, -15, 20, -42, -92, + 21, 18, -23, 9, -69, 40, + 9, -35, -29, -4, 62, 3, + -27, -8, 7, 13, -29, 12, + -53, 17, 22, -19, -25, -10, + 29, 65, -38, 29, 5, 12, + -8, 61, 16, -15, -59, -1, + -3, -63, 4, 39, -48, 51, + -18, 12, -6, 19, 7, 57, + -14, 63, -4, -38, -98, 6, + -15, 36, 43, 17, 61, -27, + 55, -25, 48, -60, 2, 53, + 19, 25, -16, -64, 5, -9, + 4, -24, -40, -19, -31, -28, + -24, -36, -9, 57, -3, -59, + -24, -12, 3, -80, 4, 11, + -1, -67, -54, -24, -25, 21, + -6, 21, 6, -80, 39, -48, + 84, 59, 51, -58, 1, -56, + -28, -54, 44, 2, 38, 72, + 9, -16, 7, 13, -34, 37, + -26, 31, -43, -40, -18, 41, + 2, -42, -25, 6, 18, 54, + 22, -1, 23, 19, 35, 31, + 1, 6, 64, -43, -23, -4, + -85, -2, -12, -22, 27, 16, + -13, -23, 48, -17, 78, 15, + 21, -28, -6, -14, 20, -54, + -29, 36, -18, 11, -11, -16, + 30, -14, 41, 0, -5, 18, + -22, 38, 12, -3, 11, -25, + 64, 10, -51, 21, 3, 13, + 23, -6, -8, -62, 57, -74, + 40, -31, -67, 20, -30, -16, + 0, 27, -4, -42, 37, -58, + 39, -5, 86, 2, 20, -14, + -43, -63, -30, -6, -22, 8, + 4, -43, -47, -5, -2, 11, + 43, 6, -9, 19, 30, -84, + 32, 63, -7, 2, 36, 30, + 14, 38, -9, 35, 32, -47, + -10, -3, -26, -17, -67, -23, + 52, 8, 35, 18, 26, 32, + -6, 59, 24, -46, -8, -22, + -3, 12, 95, -47, -8, 10, + -11, 46, -7, 16, -28, -61, + 18, -11, -78, -27, 24, -9, + -11, 67, 77, -5, 42, 3, + 1, -1, -30, 67, -21, -83, + -21, 6072, -409816, 5635597, -32279433, 98119761, + -176199334, 195823639, -135763827, 57130836, -13543813, 1535502, + -55154, 122, 36, -17, 62, 3, + -27, 28, 65, 7, -2, 23, + -11, -36, 40, -16, +}; + +static const int32_t fft_ref_imag_1024_q31[1024] = { + 0, -7, -54, 77, 13, 24, + 39, -30, 30, -28, -25, 2, + 30, -15, 23, -34, 50485, -1395758, + 12235464, -51294594, 121144577, -173660896, 155294491, -85945123, + 28099645, -4875526, 352336, -5061, -5, 10, + -18, 14, -31, -55, 65, -37, + 34, -52, -5, 56, -13, -45, + -15, -15, 11, 44, -21, 3, + 55, 54, 61, -36, -27, -47, + -44, 19, -26, -13, 12, -45, + 35, 51, -67, -9, 11, -54, + 28, 92, 15, -32, -46, 10, + -8, -15, -6, 7, 14, 68, + -19, -68, 20, 27, -51, 92, + 4, -11, 39, -15, 36, -38, + 6, -8, 11, 45, 27, 13, + 24, 48, -2, 20, 6, 3, + -9, 4, 86, 10, 11, 8, + -45, 80, 28, -23, 7, 11, + 6, -50, -66, 34, 102, 95, + 8, 36, 2, -72, -22, 55, + -21, 24, -5, 1, 35, -3, + -68, -20, -2, -7, 49, -65, + -34, -15, 19, 108, 55, 9, + 16, -24, 2, -18, -18, 37, + 67, 16, 15, -11, -7, -23, + 25, -8, 8, -20, 10, 41, + 32, -9, -81, -12, -29, 47, + 5, -31, -17, -67, -17, -32, + 16, -47, 12, -30, 18, -23, + -20, 14, 21, 27, 2, -99, + -28, 22, -11, 8, -15, -44, + -8, -2, 1, -46, 61, -16, + 30, 18, 14, 8, 29, 16, + -69, 29, -43, -33, -2, 23, + 53, -71, -51, -10, -1, -29, + 33, -7, -43, -25, 47, -49, + 44, 17, -16, 55, 53, 48, + 32, 60, -15, -15, 34, -31, + -34, 36, -11, 22, 18, 7, + 49, 48, -19, -70, 50, -15, + -91, 75, 4, -14, 36, 14, + -1, 16, 2, -3, -49, 29, + 44, 64, -74, 11, 42, 11, + -12, -64, -76, -11, 2, -35, + 22, -48, -39, -31, -33, -18, + -33, 34, 8, 66, 43, 8, + -2, 39, 29, -16, 3, 28, + 8, -50, -54, 41, 14, -34, + -2, -33, 23, -8, 21, -29, + 13, 62, 9, -21, -30, 31, + 56, 44, 14, 20, 12, -8, + -2, -7, -1, 40, 30, -1, + 22, -43, 88, 68, 36, -51, + -75, 21, -61, -40, -12, -8, + -57, 5, -1, 28, -41, -17, + 22, 34, -63, -27, 24, 57, + -86, -4, -16, -53, 15, 26, + 14, -27, -23, -48, 16, -28, + -6, -37, 38, -39, -2, 32, + -56, 40, 15, -26, -73, -21, + 28, -62, 4, 76, 23, 49, + -1, -40, 51, -29, 4, 49, + 5, -3, -36, 41, 27, -16, + -11, -47, -12, 4, 2, 32, + -79, 67, -24, 45, 23, -15, + 66, -7, 60, -76, -17, -45, + -20, 17, -3, -13, 2, 9, + 19, 16, 6, -29, 23, -15, + 98, 3, 11, 22, 38, -10, + 2, -12, 20, 32, 30, 34, + 54, 47, -25, -15, -75, 38, + -10, 49, 28, -81, -1, 21, + 6, 39, -68, -31, -11, -45, + -19, 10, -27, -11, 29, -26, + 39, 3, -53, 17, 111, 58, + -3, -27, -26, -16, 57, 19, + 5, 34, 17, 68, -22, 31, + 28, 8, 36, 10, -52, 43, + 4, 35, -34, 2, -8, 40, + 33, -68, -35, 32, 4, 38, + -27, -24, 30, -15, 1, -35, + -19, -33, 43, 9, 15, -30, + -38, -10, -4, -48, 17, -12, + 20, -20, 40, -60, 27, 12, + 47, -13, 0, 13, -47, -12, + -27, 60, -40, 20, -20, 12, + -17, 48, 4, 10, 38, 30, + -15, -9, -43, 33, 19, 35, + -1, 15, -30, 24, 27, -38, + -4, -32, 35, 68, -33, -40, + 8, -2, 34, -35, -4, -43, + 52, -10, -36, -8, -28, -31, + 22, -68, -17, -34, -5, -19, + -57, 16, 26, 27, 3, -58, + -111, -17, 53, -3, -39, 26, + -29, 11, 27, -10, 19, 45, + 11, 31, 68, -39, -6, -21, + 1, 81, -28, -49, 10, -38, + 75, 15, 25, -47, -54, -34, + -30, -32, -20, 12, -2, 10, + -38, -22, -11, -3, -98, 15, + -23, 29, -6, -16, -19, -9, + -2, 13, 3, -17, 20, 45, + 17, 76, -60, 7, -66, 15, + -23, -45, 24, -67, 79, -32, + -2, -4, 12, 47, 11, 16, + -27, -41, 36, 3, -5, -49, + -4, 29, -51, 40, 1, -49, + -23, -76, -4, 62, -28, 21, + 73, 26, -15, -40, 56, -32, + 2, 39, -38, 37, 6, 28, + -16, 48, 23, 27, -14, -26, + -15, 53, 16, 4, 86, -57, + -24, 27, 63, -34, -22, 17, + 41, -28, 1, -5, 57, 8, + 12, 40, 61, -21, 75, 51, + -36, -68, -88, 43, -22, 1, + -30, -40, 1, 7, 2, 8, + -12, -20, -14, -44, -56, -31, + 30, 21, -9, -62, -13, 29, + -21, 8, -23, 33, 2, 34, + -14, -41, 54, 50, -8, -28, + -3, 16, -29, -39, 2, -8, + -43, -66, -8, -34, 33, 18, + 33, 31, 39, 48, -22, 35, + -2, 11, 76, 64, 12, -11, + -42, -11, 74, -64, -44, -29, + 49, 3, -2, -16, 1, -14, + -36, 14, -4, -75, 91, 15, + -50, 70, 19, -48, -49, -7, + -18, -22, 11, -36, 34, 31, + -34, 15, 15, -60, -32, -48, + -53, -55, 16, -17, -44, 49, + -47, 25, 43, 7, -33, 29, + 1, 10, 51, 71, -53, -23, + 2, 33, 43, -29, 69, -16, + -29, -8, -14, -18, -30, 16, + -61, 46, -1, 2, 8, 44, + 15, -8, 11, -22, 28, 99, + -2, -27, -21, -14, 20, 23, + -18, 30, -12, 47, -16, 32, + 17, 67, 17, 31, -5, -47, + 29, 12, 81, 9, -32, -41, + -10, 20, -8, 8, -25, 23, + 7, 11, -15, -16, -67, -37, + 18, 18, -2, 24, -16, -9, + -55, -108, -19, 15, 34, 65, + -49, 7, 2, 20, 68, 3, + -35, -1, 5, -24, 21, -55, + 22, 72, -2, -36, -8, -95, + -102, -34, 66, 50, -6, -11, + -7, 23, -28, -80, 45, -8, + -11, -10, -86, -4, 9, -3, + -6, -20, 2, -48, -24, -13, + -27, -45, -11, 8, -6, 38, + -36, 15, -39, 11, -4, -92, + 51, -27, -20, 68, 19, -68, + -14, -7, 6, 15, 8, -10, + 46, 32, -15, -92, -28, 54, + -11, 9, 67, -51, -35, 45, + -12, 13, 26, -19, 44, 47, + 27, 36, -61, -54, -55, -3, + 21, -44, -11, 15, 15, 45, + 13, -56, 5, 52, -34, 37, + -65, 55, 31, -14, 18, -10, + 5, 5061, -352336, 4875526, -28099645, 85945123, + -155294491, 173660896, -121144577, 51294594, -12235464, 1395758, + -50485, 34, -23, 15, -30, -2, + 25, 28, -30, 30, -39, -24, + -13, -77, 54, 7, +}; diff --git a/test/cmocka/src/math/fft/ref_fft_multi_1536_32.h b/test/cmocka/src/math/fft/ref_fft_multi_1536_32.h new file mode 100644 index 000000000000..3b80ddea426a --- /dev/null +++ b/test/cmocka/src/math/fft/ref_fft_multi_1536_32.h @@ -0,0 +1,1044 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * + * Copyright(c) 2025 Intel Corporation. + */ + +/* Created 20-Nov-2025 16:11:52 with script ref_fft_multi.m v1.9-rc1-6882-ge0b90b605-dirty */ + +#define REF_SOFM_FFT_MULTI_1536_NUM_TESTS 1 + +static const int32_t fft_in_real_1536_q31[1536] = { + -1307, -574, -1210, -1522, -1899, 1071, + 855, 2163, 1521, 2259, 1260, -2999, + 433, 1384, -1287, -2171, 1479, 1583, + -1436, 1011, 4189, 2711, -946, -195, + -2543, 1178, -1736, 772, 1522, -171, + -1454, -1564, -1958, -4470, -3253, -3927, + -4950, -5304, -4795, -3937, -4683, -5511, + -5491, -2036, -46, -2309, -4636, 183, + 1165, 889, 2769, 2573, 4187, 7141, + 10550, 8534, 13292, 12890, 13854, 14933, + 14481, 13572, 19470, 17619, 19996, 15501, + 14335, 15941, 10852, 9125, 9930, 6717, + 3435, -122, -7217, -14931, -15699, -19050, + -28226, -29840, -39221, -38444, -45330, -48565, + -53919, -51940, -56165, -59291, -56469, -54002, + -50482, -46240, -39727, -31555, -24931, -13667, + -4067, 7601, 19601, 36606, 49720, 66264, + 82262, 93798, 109492, 123549, 138328, 146587, + 153740, 163017, 163203, 163630, 160765, 157379, + 146736, 132520, 113001, 93878, 69705, 39592, + 12865, -21580, -56672, -95272, -131691, -174549, + -213209, -248628, -283486, -320449, -346051, -370771, + -392071, -407380, -415127, -415461, -403141, -387183, + -367199, -327447, -286189, -235079, -175686, -108377, + -31956, 45401, 128250, 217821, 312206, 403171, + 493176, 576281, 661962, 734196, 802037, 857666, + 899782, 931826, 947158, 943468, 922552, 883848, + 826153, 747513, 653458, 535042, 402552, 250199, + 86048, -86449, -277634, -465706, -659677, -859066, + -1050155, -1237367, -1413054, -1568939, -1705530, -1823177, + -1913070, -1972205, -1997013, -1988742, -1939559, -1853061, + -1727260, -1564661, -1362612, -1120399, -846100, -538521, + -204309, 151603, 528919, 922290, 1315125, 1706894, + 2090470, 2458389, 2799972, 3109863, 3382597, 3609505, + 3778017, 3891544, 3933694, 3912353, 3813931, 3643198, + 3393850, 3071142, 2677376, 2209600, 1677941, 1084818, + 444967, -243363, -962452, -1701393, -2442824, -3187052, + -3905129, -4595717, -5232588, -5810315, -6311599, -6728312, + -7036669, -7235070, -7315570, -7261861, -7076599, -6757898, + -6294514, -5696302, -4963545, -4110784, -3133421, -2057740, + -890886, 352251, 1643641, 2971293, 4311667, 5637297, + 6922700, 8146290, 9279509, 10298347, 11182888, 11906619, + 12445720, 12792796, 12920677, 12821426, 12487235, 11917145, + 11098463, 10047806, 8770879, 7274529, 5582694, 3712279, + 1694100, -446673, -2678418, -4957743, -7246818, -9512063, + -11705023, -13778294, -15700344, -17427663, -18912276, -20124761, + -21027320, -21598948, -21801191, -21628479, -21056400, -20085908, + -18714238, -16955159, -14813766, -12321078, -9504162, -6399966, + -3054231, 482872, 4154302, 7905278, 11666569, 15378054, + 18958123, 22345534, 25474375, 28277167, 30683302, 32642235, + 34096393, 35003550, 35319949, 35025915, 34092432, 32521600, + 30307717, 27471468, 24039684, 20045508, 15546072, 10598856, + 5269966, -350481, -6180026, -12114692, -18059248, -23906672, + -29549875, -34871747, -39782448, -44161036, -47925834, -50975328, + -53235322, -54634944, -55118776, -54647517, -53185173, -50739616, + -47302385, -42904835, -37598374, -31437470, -24504539, -16886940, + -8713803, -96592, 8822281, 17893277, 26962568, 35868287, + 44446216, 52535664, 59974374, 66609968, 72289304, 76896286, + 80294207, 82394351, 83111692, 82389627, 80191979, 76513564, + 71358551, 64784668, 56848569, 47656194, 37330198, 26009511, + 13864615, 1086171, -12123873, -25537363, -38932152, -52068869, + -64704797, -76601691, -87525849, -97258037, -105582362, -112312248, + -117278944, -120341163, -121382694, -120326971, -117128187, -111776250, + -104303150, -94772849, -83295810, -70008000, -55097092, -38780459, + -21296115, -2915789, 16048887, 35294650, 54490243, 73286361, + 91346168, 108333722, 123911975, 137767211, 149615571, 159177154, + 166229885, 170578636, 172063686, 170577539, 166068796, 158531146, + 148010324, 134608279, 118482912, 99845751, 78943021, 56093794, + 31643166, 5972435, -20501213, -47329173, -74051124, -100196883, + -125294427, -148870026, -170477108, -189679210, -206075759, -219304043, + -229055107, -235069748, -237136842, -235122812, -228956016, -218642090, + -204251958, -185931479, -163914657, -138473803, -109988389, -78864741, + -45593192, -10699983, 25253171, 61650957, 97869663, 133278383, + 167232254, 199106861, 228282653, 254196068, 276312161, 294146665, + 307294652, 315410249, 318234645, 315590297, 307394443, 293664335, + 274509339, 250133892, 220842123, 187039901, 149209960, 107922433, + 63810361, 17587760, -29985506, -78117921, -125968455, -172708192, + -217493625, -259497359, -297927725, -332031196, -361123362, -384588415, + -401886494, -412588709, -416366237, -413004376, -402403545, -384595162, + -359740975, -328120781, -290142483, -246340640, -197348989, -143910091, + -86867351, -27143103, 34285708, 96376993, 158069138, 218280055, + 275929446, 329963897, 379373709, 423197160, 460572512, 490705168, + 512948401, 526747189, 531695512, 527540747, 514177226, 491657698, + 460188457, 420160343, 372096220, 316680397, 254738394, 187222293, + 115197014, 39828687, -37626355, -115870108, -193554390, -269321025, + -341820450, -409733063, -471798151, -526831940, -573748345, -611594031, + -639544543, -656949500, -663308649, -658325717, -641880896, -614068050, + -575168915, -525665290, -466238167, -397738580, -321208417, -237834108, + -148957438, -56007249, 39465483, 135841047, 231468773, 324681365, + 413821307, 497283182, 573521157, 641095651, 698712130, 745197774, + 779567711, 801049388, 809057254, 803237808, 783489679, 749925411, + 702911733, 643063728, 571208755, 488423234, 395954144, 295275675, + 187993978, 75865718, -39236308, -155364925, -270531450, -382717564, + -489957676, -590312404, -681949986, -763163139, -832392865, -888277334, + -929664966, -955628349, -965507939, -958906225, -935713724, -896099293, + -840526616, -769729759, -684734549, -586814614, -477494414, -358504690, + -231768214, -99384696, 36447617, 173415519, 309178420, 441371753, + 567666025, 685811530, 793656966, 889216942, 970688561, 1036488980, + 1085290370, 1116036006, 1127980589, 1120686956, 1094053945, 1048302464, + 983994405, 902020613, 803589058, 690200491, 563631899, 425926519, + 279325343, 126250979, -30724886, -188948326, -345694846, -498252679, + -643944275, -780184780, -904516055, -1014670896, -1108601825, -1184506299, + -1240890909, -1276581824, -1290734322, -1282895863, -1252960854, -1201218312, + -1128343320, -1035375507, -923712972, -795088635, -651552657, -495428590, + -329277266, -155868968, 21884630, 200961971, 378296024, 550818202, + 715513308, 869463796, 1009937937, 1134384860, 1240514038, 1326341350, + 1390206244, 1430809681, 1447252889, 1439042709, 1406104903, 1348787986, + 1267878591, 1164563795, 1040427150, 897443541, 737906293, 564423186, + 379869427, 187329491, -9955718, -208626909, -405282321, -596523018, + -779023314, -949576746, -1105159690, -1242989351, -1360560433, -1455708690, + -1526631531, -1571933920, -1590652703, -1582289863, -1546784045, -1484564044, + -1396513095, -1283969795, -1148697739, -992870821, -819029807, -630048695, + -429062628, -219454929, -4764302, 211344971, 425177867, 633047097, + 831343127, 1016615527, 1185591113, 1335287179, 1463017725, 1566460527, + 1643703210, 1693272282, 1714173828, 1705879519, 1668383535, 1602171121, + 1508225260, 1388013119, 1243466543, 1076941063, 891185548, 689291733, + 474640802, 250860397, 21738447, -208811879, -436851181, -658444086, + -869778675, -1067171636, -1247182880, -1406650050, -1542754248, -1653063285, + -1735584343, -1788792418, -1811656971, -1803674070, -1764855225, -1695756123, + -1597458692, -1471529123, -1320042483, -1145495192, -950811122, -739251472, + -514387884, -280038811, -40182539, 201082127, 439625474, 671360194, + 892281969, 1098593058, 1286704310, 1453353848, 1595632929, 1711032396, + 1797519314, 1853535499, 1878057863, 1870596072, 1831203976, 1760492969, + 1659601101, 1530204850, 1374462215, 1194990025, 994815702, 777344707, + 546257874, 305494569, 59165380, -188527365, -433343031, -671080193, + -897665887, -1109212583, -1302074592, -1472935050, -1618847731, -1737293175, + -1826220505, -1884077847, -1909862809, -1903102016, -1863908084, -1792915406, + -1691338340, -1560892199, -1403810772, -1222762623, -1020844224, -801519269, + -568521705, -325842895, -77639026, 171850260, 418354109, 657652397, + 885658006, 1098483323, 1292482492, 1464354264, 1611173802, 1730447918, + 1820147456, 1878774327, 1905345015, 1899436591, 1861181614, 1791269880, + 1690931416, 1561918192, 1406474208, 1227285856, 1027453891, 810423456, + 579930851, 339935975, 94561541, -151995804, -395519520, -631846220, + -856955512, -1067023060, -1258484983, -1428107906, -1573047808, -1690881207, + -1779650546, -1837917712, -1864752691, -1859784222, -1823172826, -1755627369, + -1658388705, -1533199617, -1382280092, -1208279946, -1014241857, -803543612, + -579832950, -346972564, -108975262, 130084072, 366112543, 595091308, + 813135881, 1016560268, 1201935283, 1366175566, 1506546319, 1620740341, + 1706921440, 1763716202, 1790274256, 1786276435, 1751905466, 1687886894, + 1595436847, 1476262351, 1332506505, 1166743251, 981898453, 781224629, + 568212339, 346559921, 120100320, -107291665, -331716427, -549356988, + -756537606, -949779759, -1125851662, -1281839136, -1415194341, -1523750226, + -1605799171, -1660093788, -1685848027, -1682799447, -1651157876, -1591635572, + -1505409467, -1394110995, -1259788475, -1104878691, -932150993, -744662067, + -545708117, -338757382, -127388767, 84763881, 294067482, 496979529, + 690070737, 870117589, 1034133585, 1179439215, 1303678090, 1404887501, + 1481492441, 1532367900, 1556837967, 1554669622, 1526102357, 1471816788, + 1392929409, 1290979584, 1167883699, 1025898547, 867595552, 695801640, + 513558015, 324055197, 130580019, -63531828, -254957837, -440470852, + -616936670, -781439126, -931257387, -1063973979, -1177469886, -1269969396, + -1340089214, -1386823818, -1409580111, -1408192311, -1382898809, -1334344278, + -1263571363, -1172000225, -1061372669, -933760932, -791499808, -637157868, + -473474442, -303337763, -129705392, 44420335, 216069365, 382339648, + 540447254, 687781491, 821934079, 940760571, 1042387135, 1125252799, + 1188147208, 1230201006, 1250924236, 1250187783, 1228227566, 1185647619, + 1123398884, 1042759518, 945299898, 832870179, 707547821, 571626092, + 427527543, 277810041, 125085057, -28010600, -178853506, -324906192, + -463733638, -593048340, -710768042, -815013616, -904172591, -976908539, + -1032173782, -1069239365, -1087698933, -1087470671, -1068778489, -1032179008, + -978515150, -908919940, -824775378, -727706611, -619535673, -502243121, + -377946135, -248861178, -117246162, 14621362, 144491857, 270172298, + 389581420, 500764993, 601939427, 691515419, 768128879, 830643528, + 878195038, 910177424, 926258719, 926396266, 910805964, 879977667, + 834650965, 775805529, 704649184, 622560430, 531100567, 431974140, + 326968596, 217974337, 106897439, -4331905, -113811517, -219713043, + -320271090, -413863122, -498997138, -574345705, -638786489, -691378723, + -731416804, -758409004, -772102118, -772476516, -759734475, -734294552, + -696804594, -648094824, -589172646, -521212233, -445517032, -363502236, + -276673232, -186591246, -94842458, -3022754, 87303105, 174620285, + 257495934, 334583680, 404667701, 466682288, 519703658, 562979891, + 595946616, 618221526, 629610881, 630106942, 619907159, 599363749, + 569010853, 529543975, 481805921, 426750078, 365456250, 299075520, + 228840090, 156015660, 81893049, 7755186, -65128851, -135537978, + -202321503, -264404090, -320822061, -370714643, -413359634, -448173767, + -474696651, -492653799, -501893571, -502433588, -494432932, -478193313, + -454161674, -422891283, -385068437, -341460613, -292938082, -240419450, + -184880948, -127332930, -68805082, -10308903, 47162165, 102642618, + 155226699, 204081975, 248446611, 287663437, 321164632, 348507085, + 369349845, 383472440, 390780777, 391301244, 385159596, 372608714, + 354002348, 329792757, 300514176, 266772038, 229250923, 188660171, + 145771607, 101362438, 56230870, 11158037, -33089190, -75767489, + -116186499, -153708843, -187764622, -217840328, -243526598, -264475454, + -280442868, -291277471, -296907101, -297359175, -292750056, -283273727, + -269212330, -250907995, -228786222, -203299441, -174980678, -144370836, + -112054125, -78619334, -44674297, -10796779, 22429621, 54448874, + 84741856, 112850180, 138327885, 160816025, 180007793, 195653700, + 207572067, 215662380, 219880160, 220249673, 216865158, 209879430, + 199507426, 186012659, 169709832, 150948749, 130113527, 107611242, + 83879355, 59352318, 34467719, 9665911, -14642108, -38040824, + -60157016, -80654418, -99219264, -115589797, -129545820, -140913863, + -149571739, -155444700, -158505797, -158792334, -156363817, -151338055, + -143887461, -134194341, -122498799, -109046102, -94123508, -78025128, + -61059747, -43545883, -25796815, -8129513, 9163546, 25792880, + 41498590, 56033059, 69187641, 80767050, 90630249, 98658995, + 104762234, 108902079, 111059757, 111263586, 109562557, 106046746, + 100833173, 94061670, 85895528, 76519048, 66130807, 54934736, + 43153781, 31007416, 18709409, 6487602, -5465933, -16946274, + -27770029, -37775509, -46814982, -54771683, -61533641, -67030347, + -71205626, -74023788, -75495830, -75627969, -74470032, -72077681, + -68529180, -63937739, -58408135, -52062371, -45042063, -37490140, + -29555197, -21385211, -13122625, -4921524, 3085357, 10759723, + 17992582, 24666389, 30683196, 35971842, 40458997, 44099420, + 46855760, 48721212, 49685704, 49765688, 48994777, 47411331, + 45075651, 42054087, 38424158, 34268812, 29672938, 24740687, + 19571693, 14248378, 8884824, 3562881, -1621239, -6586647, + -11253090, -15555599, -19428913, -22821406, -25700820, -28024865, + -29783485, -30965398, -31574032, -31615670, -31114544, -30104441, + -28614173, -26690187, -24393033, -21759693, -18857738, -15751354, + -12492477, -9153261, -5783717, -2459634, 778698, 3875964, + 6776702, 9449232, 11845786, 13948370, 15720788, 17149101, + 18231430, 18949799, 19315305, 19335514, 19020327, 18395826, + 17478205, 16296941, 14893212, 13283979, 11522416, 9639711, + 7663000, 5644053, 3617443, 1615060, -332168, -2184038, + -3918720, -5508362, -6936545, -8183325, -9232830, -10074356, + -10703608, -11126458, -11331077, -11339686, -11148104, -10770979, + -10228871, -9536383, -8710109, -7770585, -6742184, -5643349, + -4500541, -3328458, -2159579, -1004392, 110110, 1173423, + 2159647, 3069460, 3876884, 4586498, 5177693, 5646960, + 5999906, 6233787, 6347370, 6346598, 6231484, 6021809, + 5711125, 5320225, 4855551, 4327752, 3755975, 3148939, + 2514295, 1870100, 1224054, 591479, -19842, -595926, + -1136695, -1623514, -2058527, -2441755, -2759699, -3009118, + -3195895, -3315318, -3372391, -3367899, -3304971, -3188264, + -3019838, -2809759, -2565867, -2288243, -1979646, -1665042, + -1328619, -993474, -657631, -327993, -16430, 284913, + 562503, 810232, 1034553, 1226932, 1381838, 1513025, + 1599447, 1662854, 1686316, 1682092, 1651127, 1587146, + 1503496, 1397927, 1269542, 1131369, 982502, 824625, + 660881, 493354, 326771, 168908, 16066, -125971, + -261297, -378974, -483268, -576163, -647622, -708224, + -748436, -777452, -785947, -779909, -768879, -735235, + -693141, -644311, -585366, -522056, -451398, -378281, + -301901, -228634, -154426, -81830, -10134, 47735, + 111082, 163294, 207447, 247956, 282325, 307949, + 324908, 333180, 335597, 335296, 325667, 311552, + 293582, 271780, 245119, 219071, 189774, 158232, + 126724, 93564, 64134, 34822, 6228, -19584, + -41721, -63726, -82340, -98638, -109326, -117337, + -126201, -127991, -132091, -130369, -124920, -119589, + -111608, -103992, -92468, -82123, -67954, -61380, + -46424, -35967, -21111, -11739, -5029, 5679, + 13330, 22276, 26343, 35218, 34648, 39078, + 41053, 41092, 46268, 44252, 41586, 40964, + 33470, 36946, 29670, 27351, 23384, 20595, + 14255, 8620, 6966, 7402, 2342, -3982, + -3662, -6609, -4254, -9826, -11984, -13311, + -11772, -11570, -11066, -9660, -9629, -8766, + -9862, -6981, -8621, -6396, -5888, -4708, + -1464, -3442, -1834, -1918, -3426, 1074, + 2306, 544, 72, 3496, 3495, -1310, + 1331, 2847, 5980, 2827, 3263, 1499, + 2773, 1854, 1693, 3452, 2877, -1854, + 724, -2026, -2173, -141, -640, 1167, + 2129, -1066, 1932, -806, -690, -1960, + -506, -1528, -1399, -333, -2033, -851, + -1057, -321, -3212, 2295, -24, -2239, +}; + +static const int32_t fft_in_imag_1536_q31[1536] = { + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, +}; + +static const int32_t fft_ref_real_1536_q31[1536] = { + 36, 4, -4, -16, 8, 47, + -34, 30, -82, -15, 32, -63, + -77, -44, 12, -13, 1, -15, + -7, -13, 58, -10, 19, -16, + -13, 92, -848, 92278, -1546050, 10045551, + -33641357, 65657099, -78882771, 59164075, -27128397, 7128059, + -930416, 42760, -189, 12, -18, 9, + -9, -43, -37, 25, -6, 36, + -13, -15, -64, 43, -7, 3, + 16, -4, 46, 43, 15, -30, + 46, -7, 15, -9, 8, -14, + 29, -1, -53, 49, -19, -15, + -29, -11, -10, -29, -25, -11, + -36, 36, -29, -6, 28, 8, + 49, 23, -32, -27, -28, -25, + 14, 28, -6, 8, -39, 10, + 21, -4, -22, 52, 22, -11, + -27, 17, -1, 10, 30, -40, + -5, 5, 0, -18, 28, -29, + 30, 27, -1, -16, 42, -51, + -84, -14, -24, -26, -4, -4, + -36, -57, -39, 22, -4, -36, + 1, -53, 52, -17, -45, -6, + -22, 20, -19, 21, -12, -3, + -18, 38, -43, -24, -32, 48, + 47, 17, -25, -4, 22, 9, + -25, 15, -7, -22, -33, 20, + 19, 37, 36, 42, 39, -20, + 28, 25, -5, -5, 20, 42, + 30, -15, 1, -19, 33, -48, + -4, 24, -38, -18, -30, 44, + 14, 42, 3, 44, -85, 25, + 33, 27, -16, -7, 14, -48, + 35, -12, 1, 18, -37, -82, + 4, 24, -52, -57, 21, -2, + -37, -51, 23, 35, 60, 2, + 19, 32, -23, 3, 54, 7, + -49, 26, -37, 39, 48, 19, + 29, -3, 5, -22, 6, 26, + -5, -11, 13, -16, -38, 38, + -23, 55, -4, 30, -12, 59, + -26, -25, 27, -21, 4, -18, + -24, -17, -41, -56, 39, 22, + -1, -6, -38, -18, -3, 25, + -66, 52, 3, 29, -17, -2, + -36, -23, -8, 20, 21, -41, + -22, -15, 31, -6, 11, 26, + -1, 36, -29, -4, -11, 12, + 47, -8, -5, 0, 35, 8, + -13, -3, -47, -15, 14, -38, + 40, 34, -55, -38, -51, -17, + -13, 9, -33, -10, 9, 56, + 12, 4, 12, -1, -11, 3, + 55, 31, -5, 36, -14, -26, + -4, 10, -12, 8, 5, -70, + 37, -31, -55, -17, -26, -45, + -5, -13, -1, 27, 42, 31, + -5, -39, -57, -22, 14, 9, + 4, -20, -29, 31, -45, 16, + 4, 38, 27, -39, 5, 23, + 36, 39, 36, 51, -3, -4, + -19, -44, -9, -53, -14, 46, + -46, 5, 4, -24, 13, -21, + 2, 40, -26, 31, 21, 41, + -13, 9, -39, -33, -4, -22, + 10, 18, 0, 16, 42, 20, + -8, 12, -18, 11, 8, -30, + -22, 35, -29, 8, 47, -29, + -36, 2, -10, -57, -21, 23, + 9, -91, -49, 6, 14, 3, + 50, -59, -56, -3, 4, -4, + -71, -10, -55, 13, 28, -37, + -6, -50, 17, 4, 52, 7, + -53, 19, 33, -19, 12, -8, + -52, 30, -8, -5, 53, -3, + -13, 10, 2, -25, -14, -43, + 35, -22, 5, 68, -8, -8, + 3, 46, -2, -16, -5, 40, + -14, -28, 6, 65, -22, -5, + -9, -13, -27, 11, 16, -39, + 28, 24, 54, 37, -31, 39, + -15, 68, -3, -42, 8, 40, + 11, -3, -47, 33, -19, 24, + -6, -6, -9, -61, 55, 9, + 24, 11, -35, 19, -1, 5, + 63, -10, -24, -6, -8, -12, + 20, 17, 0, 31, -34, -3, + 32, 66, -38, 0, 7, 32, + 11, 0, 2, -8, -22, -46, + 11, -3, -25, -36, -2, 41, + -19, -17, -29, 30, -1, 70, + 25, 16, -25, -15, 12, 18, + -13, 20, 13, -44, -42, -21, + 4, -39, 32, 21, 60, -11, + -31, 10, -9, 1, 19, -45, + -4, -16, -21, 26, 35, 11, + 18, 38, 1, 6, 51, 13, + -70, 7, 55, 35, -10, -13, + -14, -56, -12, 26, 18, 51, + -24, 4, -35, 16, -1, -45, + 40, 48, 50, 15, 1, -19, + 1, -21, 67, -46, -20, -8, + -7, -2, -24, 16, -39, -1, + 26, 72, 3, -16, -3, 14, + 28, 9, -40, 34, -3, 18, + -8, -41, -51, 45, 28, -37, + 4, 24, 21, -38, 90, -29, + 9, 9, 47, -10, 35, -1, + -30, -11, 11, 38, -24, 10, + -40, 43, -3, -52, 23, 3, + -9, 14, 1, 20, 44, 13, + -35, -9, 36, -39, 37, 4, + 20, -18, 42, 39, -19, 20, + 26, -3, 38, -11, 20, -2, + 29, -60, -22, -23, 37, -30, + 44, -23, 7, -30, 39, -14, + -18, -26, 13, 23, -40, -45, + 3, -2, -14, 35, -10, -33, + 3, -40, -42, -38, 3, -5, + 18, -44, -36, 24, 40, 33, + 37, 18, -13, 19, -5, -13, + -7, -8, -1, -41, -18, -14, + -46, -49, 22, 8, -1, -14, + 0, 40, -15, 65, -35, -29, + 14, 25, -19, 13, -27, -12, + -37, -42, 4, -14, -58, 0, + 62, -16, 26, 19, -29, 23, + -17, 24, -61, 30, 24, 9, + -52, 9, 24, 30, -61, 24, + -17, 23, -29, 19, 26, -16, + 62, 0, -58, -14, 4, -42, + -37, -12, -27, 13, -19, 25, + 14, -29, -35, 65, -15, 40, + 0, -14, -1, 8, 22, -49, + -46, -14, -18, -41, -1, -8, + -7, -13, -5, 19, -13, 18, + 37, 33, 40, 24, -36, -44, + 18, -5, 3, -38, -42, -40, + 3, -33, -10, 35, -14, -2, + 3, -45, -40, 23, 13, -26, + -18, -14, 39, -30, 7, -23, + 44, -30, 37, -23, -22, -60, + 29, -2, 20, -11, 38, -3, + 26, 20, -19, 39, 42, -18, + 20, 4, 37, -39, 36, -9, + -35, 13, 44, 20, 1, 14, + -9, 3, 23, -52, -3, 43, + -40, 10, -24, 38, 11, -11, + -30, -1, 35, -10, 47, 9, + 9, -29, 90, -38, 21, 24, + 4, -37, 28, 45, -51, -41, + -8, 18, -3, 34, -40, 9, + 28, 14, -3, -16, 3, 72, + 26, -1, -39, 16, -24, -2, + -7, -8, -20, -46, 67, -21, + 1, -19, 1, 15, 50, 48, + 40, -45, -1, 16, -35, 4, + -24, 51, 18, 26, -12, -56, + -14, -13, -10, 35, 55, 7, + -70, 13, 51, 6, 1, 38, + 18, 11, 35, 26, -21, -16, + -4, -45, 19, 1, -9, 10, + -31, -11, 60, 21, 32, -39, + 4, -21, -42, -44, 13, 20, + -13, 18, 12, -15, -25, 16, + 25, 70, -1, 30, -29, -17, + -19, 41, -2, -36, -25, -3, + 11, -46, -22, -8, 2, 0, + 11, 32, 7, 0, -38, 66, + 32, -3, -34, 31, 0, 17, + 20, -12, -8, -6, -24, -10, + 63, 5, -1, 19, -35, 11, + 24, 9, 55, -61, -9, -6, + -6, 24, -19, 33, -47, -3, + 11, 40, 8, -42, -3, 68, + -15, 39, -31, 37, 54, 24, + 28, -39, 16, 11, -27, -13, + -9, -5, -22, 65, 6, -28, + -14, 40, -5, -16, -2, 46, + 3, -8, -8, 68, 5, -22, + 35, -43, -14, -25, 2, 10, + -13, -3, 53, -5, -8, 30, + -52, -8, 12, -19, 33, 19, + -53, 7, 52, 4, 17, -50, + -6, -37, 28, 13, -55, -10, + -71, -4, 4, -3, -56, -59, + 50, 3, 14, 6, -49, -91, + 9, 23, -21, -57, -10, 2, + -36, -29, 47, 8, -29, 35, + -22, -30, 8, 11, -18, 12, + -8, 20, 42, 16, 0, 18, + 10, -22, -4, -33, -39, 9, + -13, 41, 21, 31, -26, 40, + 2, -21, 13, -24, 4, 5, + -46, 46, -14, -53, -9, -44, + -19, -4, -3, 51, 36, 39, + 36, 23, 5, -39, 27, 38, + 4, 16, -45, 31, -29, -20, + 4, 9, 14, -22, -57, -39, + -5, 31, 42, 27, -1, -13, + -5, -45, -26, -17, -55, -31, + 37, -70, 5, 8, -12, 10, + -4, -26, -14, 36, -5, 31, + 55, 3, -11, -1, 12, 4, + 12, 56, 9, -10, -33, 9, + -13, -17, -51, -38, -55, 34, + 40, -38, 14, -15, -47, -3, + -13, 8, 35, 0, -5, -8, + 47, 12, -11, -4, -29, 36, + -1, 26, 11, -6, 31, -15, + -22, -41, 21, 20, -8, -23, + -36, -2, -17, 29, 3, 52, + -66, 25, -3, -18, -38, -6, + -1, 22, 39, -56, -41, -17, + -24, -18, 4, -21, 27, -25, + -26, 59, -12, 30, -4, 55, + -23, 38, -38, -16, 13, -11, + -5, 26, 6, -22, 5, -3, + 29, 19, 48, 39, -37, 26, + -49, 7, 54, 3, -23, 32, + 19, 2, 60, 35, 23, -51, + -37, -2, 21, -57, -52, 24, + 4, -82, -37, 18, 1, -12, + 35, -48, 14, -7, -16, 27, + 33, 25, -85, 44, 3, 42, + 14, 44, -30, -18, -38, 24, + -4, -48, 33, -19, 1, -15, + 30, 42, 20, -5, -5, 25, + 28, -20, 39, 42, 36, 37, + 19, 20, -33, -22, -7, 15, + -25, 9, 22, -4, -25, 17, + 47, 48, -32, -24, -43, 38, + -18, -3, -12, 21, -19, 20, + -22, -6, -45, -17, 52, -53, + 1, -36, -4, 22, -39, -57, + -36, -4, -4, -26, -24, -14, + -84, -51, 42, -16, -1, 27, + 30, -29, 28, -18, 0, 5, + -5, -40, 30, 10, -1, 17, + -27, -11, 22, 52, -22, -4, + 21, 10, -39, 8, -6, 28, + 14, -25, -28, -27, -32, 23, + 49, 8, 28, -6, -29, 36, + -36, -11, -25, -29, -10, -11, + -29, -15, -19, 49, -53, -1, + 29, -14, 8, -9, 15, -7, + 46, -30, 15, 43, 46, -4, + 16, 3, -7, 43, -64, -15, + -13, 36, -6, 25, -37, -43, + -9, 9, -18, 12, -189, 42760, + -930416, 7128059, -27128397, 59164075, -78882771, 65657099, + -33641357, 10045551, -1546050, 92278, -848, 92, + -13, -16, 19, -10, 58, -13, + -7, -15, 1, -13, 12, -44, + -77, -63, 32, -15, -82, 30, + -34, 47, 8, -16, -4, 4, +}; + +static const int32_t fft_ref_imag_1536_q31[1536] = { + 0, -51, -47, -1, -37, 43, + 28, -75, -11, -5, 22, -19, + 34, 22, -2, 47, 5, -17, + -59, 34, 2, -3, -6, 15, + -23, -10, -2528, 286363, -4833320, 31628670, + -106676515, 209692355, -253750691, 191701755, -88543033, 23436068, + -3081512, 142772, -625, 11, 6, -13, + 4, -7, -42, 19, -6, -5, + 11, -28, 39, -47, 29, 60, + 39, -3, -48, 25, -47, 5, + -27, 14, -37, 42, 54, 24, + -11, -60, -22, -14, 51, -36, + 53, -48, -50, -5, -52, -23, + -18, -23, -16, -22, 23, 34, + 33, -6, -11, -62, -45, 11, + 19, -2, -39, -18, 20, 4, + 2, 24, 24, 13, -9, -2, + -4, 32, -2, 10, 15, 6, + -29, -7, 45, -29, 18, 26, + 1, -19, -20, 0, 32, 16, + -1, 32, 72, -48, -27, -4, + 69, 22, -7, 6, 22, 56, + -14, 32, -34, -39, 31, -18, + 30, 19, -4, 71, 10, 28, + -23, 22, 13, 12, -2, 21, + 38, -33, 1, -37, -32, 19, + 13, 23, -43, 10, 7, -28, + 65, 25, 4, -24, 13, 17, + 0, -23, 14, 13, -18, -16, + 21, -47, 19, -20, 15, 1, + -50, -2, -21, -33, 9, 44, + 25, 28, 20, -31, 13, 4, + -4, 83, -1, 46, 10, 16, + -14, 17, 16, 15, 32, 24, + -11, -44, 45, -68, 36, -32, + 66, 23, 12, 42, -12, -6, + 50, -68, -14, -46, -36, -9, + 71, -41, -28, -33, 5, -61, + -10, 54, 0, 40, 0, 17, + 3, 4, 6, -20, 6, -7, + 1, -17, 3, -26, -29, 23, + -4, 17, -15, 8, 6, 8, + -23, -20, -3, 38, 13, -32, + 18, -3, -45, -47, -44, -26, + -29, 23, 28, -3, 58, -40, + 53, -9, 5, 35, 8, -1, + -25, 36, -6, -56, -1, 35, + -3, -7, -23, -12, 31, -51, + 5, 77, -38, -42, 49, -1, + -23, -3, 8, 0, 2, 11, + 27, -35, 8, 58, -46, -27, + 14, -10, -15, -45, -16, 2, + 20, 8, 10, 9, 29, -30, + -15, 68, -8, 16, -63, -23, + -5, -37, 7, -31, -25, 44, + -17, -34, 14, 10, 7, 16, + 18, 26, 44, 12, -13, 4, + 25, 6, 29, -47, 24, 8, + -14, 6, 21, -39, -2, -73, + -11, -7, -7, -14, -34, 11, + 74, 43, 17, -28, 18, -49, + 27, 3, -7, -13, 39, 20, + -39, 9, -10, 38, -15, -14, + 3, -43, 12, -31, 18, 44, + -24, -22, -52, -26, 15, -20, + -52, -28, 10, -18, -13, -17, + -16, -77, 16, -59, -102, 15, + 31, -75, -24, 42, 9, 19, + -3, 10, 4, 4, -25, 33, + 18, 22, 2, 34, -32, -18, + -1, 12, -3, -12, -6, 20, + -63, 3, -4, 18, 46, 40, + -15, -30, -4, 26, -31, -1, + -15, 19, -49, -11, 20, 1, + 2, 37, 54, 13, -2, -7, + -19, 9, -5, 4, 101, -42, + -45, 17, 14, -35, -56, 30, + -19, 86, -10, -46, -50, -7, + -22, -6, 0, -15, -6, 9, + -46, 54, -14, -7, 2, 27, + -54, 74, 29, 58, -39, 2, + -78, -8, -65, 47, 17, -40, + -15, 6, 3, 13, 7, -26, + -65, -26, -37, 18, -34, -10, + 11, -44, 3, 4, -28, -16, + 8, -88, -3, 8, 27, 18, + -9, 60, 30, 45, -53, -1, + -11, -1, 31, -4, 18, -5, + -13, -5, 9, -40, 22, 6, + -20, 23, 6, 32, -13, 25, + -5, 25, 12, 29, 8, -5, + -53, 45, 7, -24, -3, -12, + 3, 31, -1, -43, -9, -47, + -14, -38, -34, 57, 22, 45, + -20, -5, -30, -18, -17, -2, + 43, -28, 2, 59, -80, -5, + -16, -51, 39, -21, -21, 24, + 47, -29, 15, -22, 71, 77, + -34, 38, -9, -24, 23, -2, + 17, 33, -23, 52, 2, -25, + -55, -2, -34, 64, 59, 1, + 17, 11, 17, 11, -41, -15, + 13, 9, 32, 21, 17, 30, + -32, -49, 10, 11, 31, -1, + -48, 41, 28, -42, -47, 6, + -6, 2, -71, -44, -27, 51, + -3, 38, -66, -36, 34, -41, + 43, 38, 15, 32, -29, 11, + 3, -20, 23, 20, -54, -10, + 6, 7, -4, -12, 26, -14, + 22, 45, -5, 64, 22, -6, + -41, -30, -14, -8, -100, -26, + 28, 57, -5, -9, 42, -20, + 39, 9, 18, 14, 52, 6, + 2, 7, -93, -29, -15, -20, + 25, 18, 2, -20, -7, 15, + 67, -29, -20, -43, 3, 38, + 16, -32, 20, 43, -2, 41, + 16, -3, -8, -21, 1, -12, + 25, 1, 63, 12, -43, -2, + -23, 8, -7, -14, 24, 24, + 32, -19, 33, 13, -9, 1, + 24, 37, -24, 9, -46, -19, + 38, 71, 32, -26, 5, -66, + -5, 8, -5, 13, -43, -24, + -24, -26, 5, -28, 26, 0, + -20, -20, 2, -14, 60, -8, + -57, -34, 13, 12, 14, -1, + 0, 1, -14, -12, -13, 34, + 57, 8, -60, 14, -2, 20, + 20, 0, -26, 28, -5, 26, + 24, 24, 43, -13, 5, -8, + 5, 66, -5, 26, -32, -71, + -38, 19, 46, -9, 24, -37, + -24, -1, 9, -13, -33, 19, + -32, -24, -24, 14, 7, -8, + 23, 2, 43, -12, -63, -1, + -25, 12, -1, 21, 8, 3, + -16, -41, 2, -43, -20, 32, + -16, -38, -3, 43, 20, 29, + -67, -15, 7, 20, -2, -18, + -25, 20, 15, 29, 93, -7, + -2, -6, -52, -14, -18, -9, + -39, 20, -42, 9, 5, -57, + -28, 26, 100, 8, 14, 30, + 41, 6, -22, -64, 5, -45, + -22, 14, -26, 12, 4, -7, + -6, 10, 54, -20, -23, 20, + -3, -11, 29, -32, -15, -38, + -43, 41, -34, 36, 66, -38, + 3, -51, 27, 44, 71, -2, + 6, -6, 47, 42, -28, -41, + 48, 1, -31, -11, -10, 49, + 32, -30, -17, -21, -32, -9, + -13, 15, 41, -11, -17, -11, + -17, -1, -59, -64, 34, 2, + 55, 25, -2, -52, 23, -33, + -17, 2, -23, 24, 9, -38, + 34, -77, -71, 22, -15, 29, + -47, -24, 21, 21, -39, 51, + 16, 5, 80, -59, -2, 28, + -43, 2, 17, 18, 30, 5, + 20, -45, -22, -57, 34, 38, + 14, 47, 9, 43, 1, -31, + -3, 12, 3, 24, -7, -45, + 53, 5, -8, -29, -12, -25, + 5, -25, 13, -32, -6, -23, + 20, -6, -22, 40, -9, 5, + 13, 5, -18, 4, -31, 1, + 11, 1, 53, -45, -30, -60, + 9, -18, -27, -8, 3, 88, + -8, 16, 28, -4, -3, 44, + -11, 10, 34, -18, 37, 26, + 65, 26, -7, -13, -3, -6, + 15, 40, -17, -47, 65, 8, + 78, -2, 39, -58, -29, -74, + 54, -27, -2, 7, 14, -54, + 46, -9, 6, 15, 0, 6, + 22, 7, 50, 46, 10, -86, + 19, -30, 56, 35, -14, -17, + 45, 42, -101, -4, 5, -9, + 19, 7, 2, -13, -54, -37, + -2, -1, -20, 11, 49, -19, + 15, 1, 31, -26, 4, 30, + 15, -40, -46, -18, 4, -3, + 63, -20, 6, 12, 3, -12, + 1, 18, 32, -34, -2, -22, + -18, -33, 25, -4, -4, -10, + 3, -19, -9, -42, 24, 75, + -31, -15, 102, 59, -16, 77, + 16, 17, 13, 18, -10, 28, + 52, 20, -15, 26, 52, 22, + 24, -44, -18, 31, -12, 43, + -3, 14, 15, -38, 10, -9, + 39, -20, -39, 13, 7, -3, + -27, 49, -18, 28, -17, -43, + -74, -11, 34, 14, 7, 7, + 11, 73, 2, 39, -21, -6, + 14, -8, -24, 47, -29, -6, + -25, -4, 13, -12, -44, -26, + -18, -16, -7, -10, -14, 34, + 17, -44, 25, 31, -7, 37, + 5, 23, 63, -16, 8, -68, + 15, 30, -29, -9, -10, -8, + -20, -2, 16, 45, 15, 10, + -14, 27, 46, -58, -8, 35, + -27, -11, -2, 0, -8, 3, + 23, 1, -49, 42, 38, -77, + -5, 51, -31, 12, 23, 7, + 3, -35, 1, 56, 6, -36, + 25, 1, -8, -35, -5, 9, + -53, 40, -58, 3, -28, -23, + 29, 26, 44, 47, 45, 3, + -18, 32, -13, -38, 3, 20, + 23, -8, -6, -8, 15, -17, + 4, -23, 29, 26, -3, 17, + -1, 7, -6, 20, -6, -4, + -3, -17, 0, -40, 0, -54, + 10, 61, -5, 33, 28, 41, + -71, 9, 36, 46, 14, 68, + -50, 6, 12, -42, -12, -23, + -66, 32, -36, 68, -45, 44, + 11, -24, -32, -15, -16, -17, + 14, -16, -10, -46, 1, -83, + 4, -4, -13, 31, -20, -28, + -25, -44, -9, 33, 21, 2, + 50, -1, -15, 20, -19, 47, + -21, 16, 18, -13, -14, 23, + 0, -17, -13, 24, -4, -25, + -65, 28, -7, -10, 43, -23, + -13, -19, 32, 37, -1, 33, + -38, -21, 2, -12, -13, -22, + 23, -28, -10, -71, 4, -19, + -30, 18, -31, 39, 34, -32, + 14, -56, -22, -6, 7, -22, + -69, 4, 27, 48, -72, -32, + 1, -16, -32, 0, 20, 19, + -1, -26, -18, 29, -45, 7, + 29, -6, -15, -10, 2, -32, + 4, 2, 9, -13, -24, -24, + -2, -4, -20, 18, 39, 2, + -19, -11, 45, 62, 11, 6, + -33, -34, -23, 22, 16, 23, + 18, 23, 52, 5, 50, 48, + -53, 36, -51, 14, 22, 60, + 11, -24, -54, -42, 37, -14, + 27, -5, 47, -25, 48, 3, + -39, -60, -29, 47, -39, 28, + -11, 5, 6, -19, 42, 7, + -4, 13, -6, -11, 625, -142772, + 3081512, -23436068, 88543033, -191701755, 253750691, -209692355, + 106676515, -31628670, 4833320, -286363, 2528, 10, + 23, -15, 6, 3, -2, -34, + 59, 17, -5, -47, 2, -22, + -34, 19, -22, 5, 11, 75, + -28, -43, 37, 1, 47, 51, +}; diff --git a/test/cmocka/src/math/fft/ref_fft_multi_3072_32.h b/test/cmocka/src/math/fft/ref_fft_multi_3072_32.h new file mode 100644 index 000000000000..25e70400ccf6 --- /dev/null +++ b/test/cmocka/src/math/fft/ref_fft_multi_3072_32.h @@ -0,0 +1,2068 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * + * Copyright(c) 2025 Intel Corporation. + */ + +/* Created 20-Nov-2025 16:11:52 with script ref_fft_multi.m v1.9-rc1-6882-ge0b90b605-dirty */ + +#define REF_SOFM_FFT_MULTI_3072_NUM_TESTS 1 + +static const int32_t fft_in_real_3072_q31[3072] = { + 372, -3573, -1008, 217, -780, 436, + -1549, -852, -2495, 1920, -990, 2031, + -1837, -1265, 107, -2253, 2173, -458, + 3792, -194, 423, 799, 701, 1111, + -2804, -608, 1177, -1518, 168, 1765, + 705, -254, -3040, 1816, -2288, -285, + -1613, -1936, 688, -3208, -4065, -349, + -966, -1253, -1, 817, 111, -3848, + 205, 1017, -2528, 2877, 174, -1019, + 2896, 307, 1621, 520, 3704, 931, + 823, 2510, 3310, 1172, 4812, 931, + 1606, 2074, 2256, -1087, 2452, 758, + 589, 2385, 1960, -2318, -2127, -4529, + -4734, 105, -4991, -7191, -4860, -5616, + -2791, -5424, -4297, -5662, -8319, -6945, + -5017, -2177, -4699, -1601, 604, -43, + 1878, 3462, 1152, 1164, 2308, 5171, + 8093, 6337, 9436, 9868, 12643, 11458, + 11732, 12804, 8785, 10229, 11049, 12522, + 10680, 5304, 6307, 3106, 6523, 1343, + 258, -3209, -4227, -5295, -7664, -10933, + -9853, -13973, -18784, -16492, -20604, -22894, + -19663, -21716, -19330, -19486, -20742, -19535, + -16630, -15494, -14231, -10701, -8950, -5354, + -2341, 157, 6564, 11704, 18055, 14585, + 21646, 28327, 29665, 32553, 35738, 35431, + 38650, 39954, 38001, 43701, 38676, 35777, + 37697, 30835, 28715, 23077, 16120, 10116, + 1842, -2231, -12878, -19231, -23871, -33268, + -39644, -46730, -52379, -58782, -59834, -67902, + -68580, -70729, -72311, -70437, -71116, -65100, + -62389, -52520, -45857, -39326, -26646, -17904, + -5812, 7728, 15285, 31013, 43597, 56440, + 68135, 76812, 89464, 101782, 109968, 113335, + 121569, 117890, 122090, 118167, 117262, 109348, + 104021, 95523, 81365, 63317, 50309, 32668, + 13461, -7822, -28259, -51658, -72664, -91775, + -115036, -131884, -148209, -161626, -176665, -186642, + -198422, -201251, -204468, -199154, -194769, -184574, + -170630, -155814, -133647, -111067, -82874, -53815, + -23251, 8690, 45733, 77325, 109576, 147709, + 178113, 208648, 234279, 261877, 284629, 296896, + 312298, 317591, 320982, 318739, 305377, 291686, + 270019, 245901, 215298, 175604, 137830, 90294, + 41554, -10071, -62629, -117838, -169306, -223248, + -270854, -323501, -368270, -403786, -438021, -462004, + -483065, -495690, -501584, -494599, -479802, -453999, + -421113, -384132, -333536, -274847, -214043, -141020, + -68871, 9322, 93150, 174657, 257553, 338309, + 414683, 487699, 555110, 613887, 662617, 705407, + 734778, 750029, 756904, 750785, 728812, 693681, + 641368, 582598, 505227, 422816, 325847, 224093, + 111558, -5393, -128174, -253153, -371497, -497447, + -611885, -720876, -818284, -911629, -982314, -1042069, + -1089267, -1113410, -1121889, -1111498, -1080988, -1028361, + -955932, -867286, -759764, -631297, -494221, -341335, + -174492, -1266, 175797, 353877, 535388, 712596, + 885107, 1043835, 1190169, 1319079, 1429815, 1519748, + 1579473, 1619092, 1633179, 1618264, 1574576, 1499577, + 1397206, 1267140, 1106626, 928445, 727130, 507505, + 271402, 21420, -236596, -495465, -757620, -1006830, + -1249141, -1479683, -1690633, -1879331, -2034859, -2165655, + -2256619, -2315157, -2332427, -2310891, -2246487, -2143264, + -1999884, -1816213, -1596280, -1342502, -1052822, -742495, + -407978, -57316, 304579, 672838, 1038655, 1398459, + 1742918, 2065247, 2365675, 2631021, 2853345, 3036470, + 3167820, 3249898, 3281470, 3252625, 3164310, 3019310, + 2822626, 2564728, 2259683, 1900120, 1508700, 1070684, + 603408, 110951, -392666, -903614, -1414802, -1912902, + -2392206, -2845130, -3258570, -3627149, -3940690, -4191313, + -4381803, -4499619, -4541815, -4504007, -4388011, -4192289, + -3919064, -3569292, -3147477, -2658923, -2113464, -1518547, + -874826, -209524, 487728, 1191134, 1892304, 2573893, + 3234254, 3855470, 4423871, 4927377, 5359814, 5713992, + 5972621, 6140505, 6199387, 6153667, 5998277, 5738563, + 5368890, 4896998, 4329184, 3671418, 2930085, 2121158, + 1254388, 347574, -591983, -1544910, -2490043, -3420032, + -4315360, -5152121, -5921191, -6611696, -7199706, -7679390, + -8034751, -8257121, -8346135, -8290828, -8093048, -7744783, + -7254418, -6630540, -5870181, -4992728, -4006965, -2925017, + -1770290, -553992, 701532, 1972732, 3243885, 4485736, + 5676566, 6798847, 7830355, 8753293, 9542274, 10185538, + 10668092, 10977869, 11100656, 11039240, 10778861, 10326862, + 9684417, 8861605, 7863637, 6708489, 5407284, 3980889, + 2454492, 851943, -806049, -2488656, -4160522, -5801884, + -7381535, -8865301, -10234725, -11457830, -12501687, -13359571, + -14004580, -14421804, -14594932, -14515706, -14190718, -13607659, + -12780027, -11711622, -10410644, -8902247, -7209085, -5351692, + -3360190, -1266353, 892180, 3085982, 5277651, 7423286, + 9487880, 11431794, 13220058, 14818486, 16199378, 17322667, + 18173106, 18731015, 18968208, 18891463, 18480248, 17738440, + 16674383, 15296941, 13630301, 11690888, 9508065, 7111137, + 4541597, 1838855, -953433, -3788691, -6620413, -9392119, + -12060485, -14576464, -16894758, -18969314, -20757213, -22224208, + -23337383, -24068281, -24396675, -24314555, -23807617, -22876611, + -21528574, -19784004, -17663705, -15191458, -12403961, -9347173, + -6060577, -2608229, 959950, 4586727, 8204034, 11755744, + 15172504, 18397037, 21372442, 24033132, 26334534, 28221481, + 29661590, 30621139, 31064526, 30984600, 30361616, 29206481, + 27520780, 25328643, 22655207, 19532705, 16011679, 12149179, + 7998202, 3629080, -886601, -5477122, -10060474, -14562086, + -18899133, -22989928, -26765395, -30151720, -33079322, -35496765, + -37343077, -38580863, -39174876, -39103667, -38354970, -36935358, + -34846029, -32114319, -28772721, -24876871, -20476549, -15641187, + -10440364, -4966127, 700841, 6459485, 12215659, 17866676, + 23313028, 28458228, 33210198, 37478907, 41172064, 44232678, + 46576561, 48163284, 48945603, 48900048, 48012912, 46278450, + 43710721, 40344152, 36215687, 31392482, 25936258, 19939304, + 13485840, 6683808, -355573, -7517128, -14679416, -21710106, + -28499406, -34910051, -40834645, -46165701, -50790443, -54626140, + -57579532, -59598013, -60619069, -60618781, -59567039, -57473025, + -54351412, -50233342, -45186383, -39261858, -32564132, -25185248, + -17241901, -8866923, -191879, 8633665, 17460936, 26143204, + 34521609, 42447110, 49780242, 56379237, 62120194, 66881659, + 70575603, 73119678, 74443740, 74499636, 73282198, 70781179, + 67012890, 62026148, 55887344, 48687496, 40526263, 31529575, + 21836507, 11612109, 1012091, -9779981, -20582181, -31204035, + -41466004, -51182619, -60176524, -68284491, -75345078, -81225915, + -85799183, -88971434, -90663991, -90821930, -89419903, -86450743, + -81949463, -75956415, -68564796, -59875217, -50011014, -39129949, + -27400942, -15013360, -2168850, 10917793, 24023847, 36925822, + 49394121, 61211286, 72157333, 82036329, 90656560, 97852309, + 103475793, 107411172, 109549870, 109844745, 108245148, 104766008, + 99424144, 92289308, 83453912, 73051237, 61227750, 48165694, + 34079160, 19189890, 3741027, -12005929, -27789733, -43333372, + -58369825, -72626480, -85852717, -97799740, -108240568, -116984834, + -123841326, -128673829, -131365348, -131830251, -130042187, -125987529, + -119708342, -111271729, -100800507, -88439340, -74371460, -58820447, + -42024813, -24265428, -5823474, 12987814, 31851444, 50439832, + 68434255, 85515447, 101367399, 115712690, 128275627, 138809813, + 147111488, 153005021, 156352343, 157056501, 155072568, 150396562, + 143063101, 133170013, 120845814, 106268769, 89657765, 71271888, + 51399185, 30366381, 8513394, -13791446, -36175759, -58247278, + -79630509, -99940520, -118815593, -135911795, -150904974, -163518047, + -173494300, -180628604, -184757144, -185767143, -183596719, -178244443, + -169749533, -158227572, -143833336, -126769181, -107293920, -85709208, + -62364441, -37631290, -11919266, 14347827, 40716579, 66739386, + 91969993, 115952914, 138266858, 158500447, 176282916, 191272406, + 203177017, 211746908, 216794498, 218182573, 215846442, 209774794, + 200015892, 186692556, 169994049, 150152492, 127483522, 102323384, + 75084856, 46201855, 16153098, -14559068, -45419047, -75892600, + -105455486, -133589527, -159788255, -183572055, -204512930, -222207105, + -236312548, -246537938, -252661829, -254527285, -252044844, -245204000, + -234070892, -218777349, -199540150, -176640610, -150422459, -121292087, + -89721942, -56221556, -21340285, 14334216, 50205554, 85654350, + 120069924, 152846317, 183399211, 211180614, 235673463, 256424814, + 273019784, 285138036, 292514936, 294953678, 292364878, 284724191, + 272110317, 254679055, 232666741, 206401006, 176282044, 142782930, + 106435316, 67827972, 27606004, -13567407, -54989891, -95960432, + -135762579, -173703133, -209109185, -241342374, -269808493, -293980021, + -313386899, -327646179, -336453636, -339592042, -336942587, -328480069, + -314289983, -294549465, -269535128, -239608257, -205233219, -166948378, + -125366856, -81167440, -35076509, 12137668, 59676374, 106721077, + 152465978, 196107885, 236879838, 274040630, 306911433, 334892873, + 357441507, 374108431, 384553642, 388516901, 385863592, 376571435, + 360716345, 338509581, 310254184, 276376144, 237395341, 193917688, + 146647470, 96356984, 43868781, -9933526, -64145444, -117835189, + -170084338, -219972544, -266627410, -309204586, -346936289, -379122173, + -405155218, -424514827, -436810151, -441744679, -439162883, -429030505, + -411439631, -386617413, -354911037, -316798931, -272863878, -223795489, + -170394207, -113517117, -54112101, 6829649, 68271983, 129181249, + 188492354, 245177826, 298242279, 346730695, 389776661, 426578154, + 456438915, 478787617, 493156906, 499218782, 496797130, 485840086, + 466453824, 438883546, 403526720, 360909157, 311693565, 256651431, + 196672699, 132738813, 65908364, -2703538, -71935942, -140613095, + -207543775, -271573069, -331567032, -386461954, -435270224, -477095415, + -511150801, -536776982, -553457499, -560821589, -558658317, -546905800, + -525675660, -495251147, -456064122, -408695891, -353884464, -292507426, + -225550806, -154103971, -79358011, -2561945, 74990922, 151974456, + 227067781, 298963701, 366402727, 428187197, 483209074, 530460127, + 569065103, 598275256, 617513188, 626357580, 624559602, 612058838, + 588971043, 555592552, 512412748, 460076790, 399404508, 331355368, + 257033083, 177659157, 94542334, 9080697, -77291728, -163095921, + -246863323, -327129624, -402505892, -471643480, -533307653, -586384181, + -629887100, -662983451, -685030545, -695536926, -694237467, -681049278, + -656096022, -619710506, -572410016, -514922467, -448143694, -373128295, + -291109814, -203423941, -111523060, -16946439, 78702051, 173797563, + 266709385, 355818638, 439585563, 516517252, 585239763, 644525256, + 693261354, 730544741, 755634274, 768002388, 767333019, 753544351, + 726750519, 687313547, 635808270, 573021365, 499930935, 417715763, + 327699155, 231373047, 130330697, 26255704, -79077473, -183883762, + -286364208, -384749243, -477322091, -562450927, -638620614, -704458584, + -758759964, -800505747, -828885262, -843307676, -843428140, -829125298, + -800541387, -758052984, -702291149, -634095686, -554553702, -464934785, + -366695229, -261454617, -150966943, -37076378, 78289104, 193169085, + 305591559, 413616449, 515365690, 609043518, 692999321, 765716588, + 825874492, 872350263, 904253049, 920935675, 921993322, 907300938, + 876995158, 831492911, 771451746, 697804425, 611711609, 514553983, + 407924116, 293581677, 173416032, 49451086, -76220456, -201456944, + -324120836, -442096257, -553320447, -655855947, -747885861, -827768799, + -894049560, -945498889, -981150629, -1000271847, -1002432788, -987484122, + -955566735, -907106665, -842823854, -763720833, -671044190, -566294105, + -451177423, -327601471, -197612754, -63402956, 72760120, 208568511, + 341689943, 469838167, 590782890, 702408508, 802758269, 890034724, + 962657597, 1019303575, 1058896759, 1080652859, 1084088210, 1069029598, + 1035618264, 984312724, 915867520, 831363489, 732141019, 619797883, + 496179013, 363327875, 223462244, 78928184, -67831872, -214319471, + -358035612, -496500520, -627323350, -748214481, -857046183, -951889165, + -1031049603, -1093062609, -1136780159, -1161344249, -1166220616, -1151212846, + -1116462912, -1062447094, -989978191, -900185174, -794510265, -674662535, + -542611316, -400546431, -250829785, -95990652, 61366328, 218558108, + 372901738, 521744570, 662502854, 792737450, 910165194, 1012696160, + 1098503418, 1166031146, 1214025412, 1241556119, 1248038839, 1233252312, + 1197338095, 1140793802, 1064472412, 969574944, 857621750, 730435938, + 590107881, 438968868, 279545499, 114515179, -53332932, -221139123, + -386045481, -545210951, -695893623, -835474544, -961505113, -1071770327, + -1164307836, -1237438055, -1289820122, -1320452961, -1328709383, -1314332855, + -1277452999, -1218591232, -1138644358, -1038871318, -920887278, -786611045, + -638256294, -478293717, -309395514, -134403655, 43716046, 221948477, + 397235398, 566578363, 727061393, 875885710, 1010467343, 1128436049, + 1227704280, 1306480900, 1363339414, 1397194675, 1407372472, 1393581163, + 1355953850, 1295026075, 1211729278, 1107379980, 983679487, 842642384, + 686602223, 518156042, 340129235, 155518548, -32551931, -220890139, + -406278195, -585537160, -755583489, -913469206, -1056447718, -1182017615, + -1287952094, -1372373602, -1433740315, -1470904340, -1483138314, -1470125229, + -1431990546, -1369269446, -1282934208, -1174373193, -1045338593, -897949621, + -734659284, -558177505, -371469791, -177689875, 19897308, 217914039, + 413000387, 601804247, 781081356, 947739871, 1098873275, 1231848458, + 1344327873, 1434310884, 1500178253, 1540716776, 1555129431, 1543082456, + 1504676705, 1440471424, 1351471197, 1239098180, 1105185347, 951941327, + 781912112, 597936823, 403109189, 200711702, -5827201, -212996516, + -417263748, -615131936, -803202303, -978233490, -1137186723, -1277303294, + -1396118945, -1491537327, -1561856292, -1605781912, -1622476857, -1611576361, + -1573161776, -1507803264, -1416525319, -1300803759, -1162529626, -1003997711, + -827837307, -637004368, -434709124, -224359341, -9529003, 206138691, + 418964803, 625310311, 821624595, 1004540605, 1170892708, 1317790091, + 1442669662, 1543328211, 1617988707, 1665290217, 1684351248, 1674761398, + 1636600581, 1570429050, 1477314812, 1358748196, 1216694357, 1053500806, + 871896317, 674924682, 465908519, 248377622, 26015600, -197400277, + -418050818, -632173339, -836090410, -1026306638, -1199532255, -1352774350, + -1483362532, -1589011002, -1667857340, -1718486144, -1739962335, -1731840810, + -1694189001, -1627581900, -1533073579, -1412215425, -1266999164, -1099845212, + -913551939, -711244528, -496342110, -272477568, -43447743, 186853426, + 414497132, 635604535, 846382113, 1043217377, 1222716978, 1381788493, + 1517666640, 1627985742, 1710809548, 1764671488, 1788582982, 1782076477, + 1745200236, 1678510858, 1583099752, 1460517021, 1312810748, 1142441329, + 952273264, 745504463, 525629176, 296376863, 61629633, -174618632, + -408342794, -635547128, -852353972, -1055045659, -1240135324, -1404435561, + -1545115420, -1659730825, -1746277289, -1803232403, -1829574223, -1824802377, + -1788944079, -1722556598, -1626725028, -1503023090, -1353527120, -1180736052, + -987563716, -777264704, -553397694, -319761871, -80318116, 160854501, + 399645250, 631985939, 853909923, 1061611331, 1251535092, 1420412831, + 1565341387, 1683811150, 1773776008, 1833654782, 1862391453, 1859458840, + 1824851861, 1759129786, 1663371340, 1539171366, 1388613977, 1214226218, + 1018956327, 806101706, 579277790, 342327686, 99281607, -145729280, + -388524088, -624973189, -851026538, -1062835803, -1256769433, -1429499244, + -1578069680, -1699911201, -1792938289, -1855528096, -1886587771, -1885558778, + -1852429664, -1787734857, -1692550025, -1568466400, -1417586538, -1242447341, + -1046019000, -831624085, -602913005, -363765229, -118251153, 129457667, + 375134811, 614592317, 843748087, 1058698271, 1255758464, 1431568802, + 1583117023, 1707808398, 1803500889, 1868550498, 1901833106, 1902758600, + 1871299790, 1807979837, 1713850848, 1590514279, 1440046976, 1265017072, + 1068378419, 853480525, 623981978, 383772466, 136960898, -112268288, + -359659896, -601003283, -832179172, -1049261809, -1248533101, -1426603183, + -1580432180, -1707395953, -1805328349, -1872549473, -1907910206, -1910814775, + -1881202917, -1819574231, -1726982026, -1604991549, -1455688417, -1281609582, + -1085722448, -871357291, -642173873, -402075571, -155147588, 94400620, + 342321129, 584394446, 816488006, 1034659652, 1235193836, 1414674308, + 1570050564, 1698682278, 1798381561, 1867461880, 1904745928, 1909621460, + 1882002289, 1822370756, 1731752675, 1611705013, 1464279579, 1291998030, + 1097801079, 885007491, 657247751, 418407719, 172550487, -76118746, + -323372208, -565006313, -796894006, -1015107018, -1215927812, -1395948753, + -1552119395, -1681790895, -1782774050, -1853366552, -1892385206, -1899187777, + -1873679839, -1816317016, -1728099817, -1610555232, -1465702231, -1296030798, + -1104443064, -894224213, -668970217, -432524377, -188922860, 57680630, + 303083765, 543111971, 773679620, 990878213, 1191015166, 1370695312, + 1526895364, 1656961023, 1758718736, 1830458745, 1870990611, 1879657894, + 1856353863, 1801500924, 1716067064, 1601550638, 1459926812, 1293641207, + 1105551152, 898884149, 677182586, 444237696, 204031857, -39343627, + -281736298, -519024737, -747172550, -962310633, -1160791510, -1339271328, + -1494719175, -1624541086, -1726550849, -1799056437, -1840866215, -1851310975, + -1830256497, -1778117519, -1695817922, -1584815664, -1447032134, -1284862167, + -1101102449, -898911555, -681766646, -453381107, -217659996, 21366015, + 259627334, 493072531, 717731912, 929796840, 1125686578, 1302092995, + 1456042960, 1584965877, 1686703884, 1759582038, 1802411351, 1814517128, + 1795746957, 1746490037, 1667630110, 1560571974, 1427195337, 1269816814, + 1091157415, 894309346, 682655783, 459821541, 229631191, -3993204, + -237059049, -465606052, -685758770, -893780837, -1086166235, -1259671231, + -1411385009, -1538774911, -1639722862, -1712573317, -1756151511, -1769778723, + -1753291016, -1707044018, -1631880689, -1529156784, -1400686108, -1248703573, + -1075859349, -885148396, -679846603, -463496048, -239788111, -12560535, + 214318802, 436987939, 651666136, 854732389, 1042749336, 1212562328, + 1361331574, 1486573736, 1586227826, 1658651760, 1702702242, 1717695626, + 1703457825, 1660303465, 1589051490, 1490989763, 1367859974, 1221822045, + 1055420110, 871556095, 673397091, 464356944, 248014359, 28080829, + -191693218, -407572184, -615900079, -813149545, -995996567, -1161375920, + -1306531882, -1429044334, -1526907572, -1598523621, -1642761569, -1658943247, + -1646894744, -1606892066, -1539710329, -1446580159, -1329162799, -1189527590, + -1030127747, -853735167, -663407611, -462418808, -254229501, -42399820, + 169456638, 377724875, 578889840, 769552187, 946493762, 1106754813, + 1247677619, 1366914015, 1462520817, 1532951923, 1577094138, 1594286405, + 1584336159, 1547503054, 1484508014, 1396513358, 1285105867, 1152266026, + 1000323211, 831943670, 650031253, 457742924, 258379751, 55360097, + -147846433, -347791305, -541082791, -724454616, -894833732, -1049361614, + -1185484687, -1300948925, -1393867554, -1462752654, -1506521824, -1524533384, + -1516574802, -1482884630, -1424136518, -1341432851, -1236268049, -1110524433, + -966418198, -806474656, -633479500, -450427103, -260466143, -66854834, + 127098863, 318089721, 502893013, 678382955, 841619455, 989876004, + 1120697339, 1231931272, 1321770079, 1388772538, 1431906568, 1450534876, + 1444442644, 1413845056, 1359367791, 1282042512, 1183277772, 1064854075, + 928867553, 777701643, 614003268, 440609784, 260505083, 76790900, + -107401371, -288931568, -464730053, -631836635, -787442491, -928965186, + -1054051161, -1160657065, -1247066123, -1311885917, -1354123223, -1373168202, + -1368802284, -1341216760, -1290982804, -1219066595, -1126792942, -1015826842, + -888151193, -746007399, -591889851, -428465269, -258572080, -85121742, + 88920688, 260585671, 426981992, 585294900, 732878617, 867277717, + 986273316, 1087923620, 1170580702, 1232948145, 1274057596, 1293321664, + 1290530992, 1265838308, 1219781089, 1153257231, 1067495900, 964056608, + 844792140, 711809082, 567448255, 414212554, 254760785, 91832000, + -71785113, -233304073, -389994059, -539215391, -678473322, -805453139, + -918071312, -1014479841, -1093139146, -1152809828, -1192574868, -1211869926, + -1210493120, -1188564867, -1146588764, -1085378215, -1006085479, -910161450, + -799328092, -675557523, -541026496, -398078579, -249185492, -96927040, + 56100459, 207288881, 354080272, 494009592, 624733114, 744082407, + 850101449, 941069115, 1015518946, 1072290089, 1110517423, 1129670265, + 1129533115, 1110223700, 1072184544, 1016169922, 943250152, 854763597, + 752304762, 637705517, 512980046, 380314890, 242012700, 100457755, + -41929112, -182717458, -319523659, -450054681, -572124060, -683719153, + -783001820, -868369330, -938460454, -992171022, -1028698784, -1047540077, + -1048477478, -1031607491, -997345635, -946379405, -879674816, -798478560, + -704259631, -598699131, -483675841, -361200006, -233396979, -102482690, + 29303374, 159718607, 286551799, 407675562, 521064808, 624853955, + 717339406, 797022340, 862641000, 913174146, 947865519, 966232390, + 968081056, 953484359, 922809892, 876682824, 816002928, 741894612, + 655708808, 559003777, 453484070, 341012181, 223536838, 103102585, + -18231207, -138395994, -255358187, -367155417, -471922910, -567935035, + -653617482, -727595050, -788689017, -835957241, -868699728, -886469086, + -889064437, -876548172, -849255973, -807751705, -752850349, -685578717, + -607173529, -519046712, -422764499, -320025539, -212631771, -102431653, + 8679050, 118808590, 226088922, 328724742, 424998537, 513330924, + 592282938, 660578068, 717148585, 761116085, 791826700, 808868239, + 812060341, 801441718, 777312861, 740179100, 690781536, 630052357, + 559110226, 479237115, 391866261, 298532311, 200875179, 100588108, + -610935, -100984350, -198848126, -292555400, -380546365, -461371121, + -533714162, -596419791, -648495691, -689153805, -717786679, -734004557, + -737643044, -728735072, -707539194, -674510633, -630315927, -575794155, + -511962755, -439967668, -361115320, -276796807, -188485656, -97721711, + -6063325, 84929152, 173707386, 258789883, 338754416, 412300822, + 478218897, 535461035, 583136381, 620504801, 647030016, 662347093, + 666298302, 658915851, 640421042, 611217389, 571909299, 523236434, + 466118540, 401593676, 330829977, 255074713, 175659977, 93973455, + 11414720, -70601436, -150685191, -227503101, -299773173, -366307069, + -426034100, -477991736, -521372745, -555521252, -579940444, -594295661, + -598442639, -592398963, -576367295, -550703332, -515942267, -472744367, + -421927480, -364422131, -301274182, -233603649, -162596392, -89494303, + -15558951, 57945069, 129779304, 198738559, 263672984, 323523850, + 377322092, 424207594, 463456672, 494471834, 516803451, 530158177, + 534393266, 529522630, 515724338, 493304606, 462743275, 424626320, + 379680922, 328730698, 272703820, 212598528, 149477147, 84438014, + 18611869, -46890812, -110948255, -172487905, -230496546, -284018556, + -332192437, -374254634, -409548650, -437541760, -457837031, -470164722, + -474394574, -470538855, -458739378, -439284058, -412572405, -379141514, + -339620640, -294741156, -245331348, -192260455, -136472440, -78951410, + -20681431, 37338334, 94122301, 148721185, 200232600, 247810779, + 290690901, 328188337, 359735133, 384848064, 403172538, 414463652, + 418610740, 415613591, 405605607, 388825120, 365626134, 336479343, + 301938434, 262645580, 219323579, 172743491, 123741607, 73166108, + 21901695, -29183784, -79217342, -127361553, -172827260, -214863245, + -252797135, -286027063, -314046252, -336431187, -352861785, -363131190, + -367130392, -364860806, -356427781, -342050458, -322027327, -296771219, + -266766470, -232577223, -194827396, -154198752, -111414668, -67220706, + -22389838, 22307679, 66124103, 108321476, 148198106, 185107523, + 218456075, 247717969, 272443667, 292266337, 306907078, 316173250, + 319972907, 318303699, 311259083, 299016458, 281848796, 260101048, + 234203589, 204633082, 171949718, 136725579, 99604730, 61231692, + 22273999, -16596878, -54722051, -91473029, -126228497, -158429158, + -187562194, -213164905, -234846275, -252286330, -265238700, -273536116, + -277094400, -275914375, -270072015, -259723698, -245095661, -226492133, + -204280400, -178876569, -150746899, -120409997, -88411434, -55305328, + -21667576, 11918194, 44883235, 76677980, 106780616, 134691029, + 159972434, 182226557, 201111853, 216348627, 227724715, 235098889, + 238395584, 237611210, 232806177, 224118037, 211737832, 195926515, + 176998172, 155309580, 131268918, 105312714, 77901800, 49526741, + 20677629, -8152943, -36463737, -63797045, -89689774, -113724824, + -135517472, -154731255, -171066319, -184289182, -194215635, -200718014, + -203729844, -203249948, -199333372, -192088472, -181686292, -168339828, + -152317100, -133929642, -113514481, -91450898, -68133602, -43973447, + -19397877, 5181202, 29338391, 52668207, 74790435, 95343031, + 114004002, 130473256, 144509701, 155902177, 164494452, 170183422, + 172913199, 172664116, 169497118, 163505317, 154823158, 143635522, + 130168501, 114678360, 97469233, 78842181, 59141556, 38714750, + 17918761, -2890580, -23352359, -43134629, -61902442, -79359178, + -95219793, -109241655, -121213199, -130959126, -138341127, -143279885, + -145714907, -145650458, -143112905, -138187337, -130992820, -121679590, + -110437544, -97489588, -83076189, -67465699, -50939873, -33792500, + -16322159, 1168621, 18385872, 35036145, 50842116, 65557638, + 78945236, 90795089, 100928151, 109206567, 115506792, 119754384, + 121907623, 121965051, 119953463, 115936809, 110018616, 102324285, + 93009785, 82258499, 70282778, 57293087, 43527865, 29239498, + 14664699, 70861, -14299001, -28213778, -41434394, -53740906, + -64954354, -74898839, -83413372, -90382889, -95713352, -99339509, + -101224772, -101365811, -99785822, -96538276, -91706578, -85398224, + -77737953, -68883543, -59001121, -48272419, -36897641, -25072795, + -13015236, -928046, 10981346, 22514261, 33483859, 43710738, + 53031141, 61298934, 68403208, 74226528, 78703859, 81766293, + 83402166, 83593718, 82365139, 79763031, 75844981, 70710156, + 64464541, 57223854, 49132714, 40341451, 31011508, 21310849, + 11407368, 1476260, -8314581, -17806980, -26836240, -35256930, + -42939854, -49774817, -55645608, -60473208, -64195712, -66770589, + -68169750, -68386355, -67439994, -65369396, -62224639, -58081970, + -53024988, -47151459, -40577074, -33432655, -25843582, -17943272, + -9879653, -1786795, 6204057, 13943017, 21315103, 28198305, + 34483247, 40076675, 44890339, 48859763, 51930920, 54068789, + 55255655, 55479959, 54760676, 53131015, 50625910, 47308295, + 43244014, 38521353, 33230411, 27468337, 21346443, 14969441, + 8451097, 1911594, -4546380, -10811070, -16777372, -22352502, + -27448560, -31988943, -35903458, -39138144, -41648387, -43410944, + -44400727, -44621343, -44081728, -42808165, -40830393, -38198108, + -34962490, -31195794, -26971588, -22366339, -17469059, -12364939, + -7146798, -1904260, 3270688, 8291813, 13081852, 17556488, + 21651104, 25302659, 28458917, 31067904, 33104793, 34537546, + 35361975, 35568405, 35171243, 34183205, 32633364, 30558842, + 28008752, 25031682, 21689735, 18045101, 14159915, 10112948, + 5974900, 1811564, -2298588, -6289153, -10097187, -13660268, + -16919025, -19831526, -22344434, -24432003, -26066684, -27225918, + -27897027, -28086260, -27792985, -27036877, -25834713, -24221524, + -22225205, -19892344, -17274057, -14413387, -11365046, -8183432, + -4931257, -1661870, 1574892, 4716073, 7715537, 10517058, + 13094848, 15384977, 17377333, 19030984, 20328238, 21250991, + 21798830, 21964593, 21748393, 21175692, 20254116, 19005897, + 17462773, 15654865, 13621029, 11397319, 9026624, 6554408, + 4021974, 1473276, -1045124, -3494283, -5828872, -8019481, + -10025125, -11819321, -13375815, -14668543, -15689994, -16418956, + -16857296, -16995023, -16846583, -16415125, -15716033, -14759735, + -13579195, -12195213, -10628145, -8917589, -7096262, -5190286, + -3236944, -1276058, 664754, 2555441, 4356032, 6042109, + 7595282, 8978157, 10184344, 11185233, 11977764, 12548965, + 12893033, 13012020, 12907487, 12584782, 12058266, 11340230, + 10442126, 9390046, 8200237, 6899404, 5512572, 4061687, + 2576181, 1082059, -399648, -1840723, -3216302, -4503358, + -5687931, -6747557, -7663720, -8435769, -9042960, -9483154, + -9751663, -9850907, -9778461, -9539916, -9147874, -8613425, + -7936567, -7146604, -6254913, -5277446, -4230376, -3136291, + -2020988, -890678, 223543, 1308679, 2340192, 3311287, + 4208466, 5007097, 5699297, 6284284, 6744247, 7081498, + 7287208, 7366198, 7318562, 7141633, 6855926, 6457825, + 5963094, 5371511, 4712336, 3985065, 3203226, 2395092, + 1563689, 724529, -106180, -913239, -1684731, -2407744, + -3072538, -3667103, -4189285, -4623149, -4965217, -5220752, + -5374082, -5440210, -5406704, -5282249, -5070729, -4783594, + -4415459, -3989230, -3500736, -2970824, -2398253, -1803663, + -1193152, -574547, 34052, 628024, 1193352, 1723345, + 2216278, 2654349, 3031779, 3355709, 3608106, 3794901, + 3913643, 3960671, 3940274, 3855545, 3701699, 3492897, + 3230771, 2919037, 2567523, 2182907, 1769008, 1336204, + 894476, 450016, 4193, -419198, -833301, -1219488, + -1571324, -1888273, -2167536, -2401825, -2582554, -2722866, + -2806550, -2845009, -2831941, -2765462, -2661468, -2513744, + -2323485, -2105147, -1854412, -1580356, -1286629, -975301, + -656393, -339857, -25917, 279482, 571766, 848201, + 1100367, 1327317, 1524758, 1690162, 1819819, 1918854, + 1980841, 2008787, 1998777, 1955867, 1882817, 1780155, + 1648781, 1489634, 1319263, 1122357, 915235, 703731, + 481879, 255745, 35727, -179285, -387338, -576268, + -757789, -914450, -1054956, -1171327, -1261362, -1329783, + -1378263, -1393224, -1392519, -1361178, -1306723, -1240796, + -1149249, -1040253, -919112, -788188, -644100, -491012, + -342989, -186968, -35569, 111048, 254402, 388767, + 509286, 617211, 714449, 793277, 860190, 907067, + 935048, 948515, 946097, 928525, 896936, 846039, + 783146, 710400, 630008, 540362, 441121, 340845, + 239764, 133643, 30048, -71439, -166063, -251751, + -336514, -413690, -476442, -527614, -569975, -607659, + -628455, -633038, -634976, -620199, -598848, -564510, + -525244, -476398, -419249, -365706, -299017, -231986, + -163672, -92415, -26440, 38907, 105485, 163003, + 216292, 267479, 308792, 345665, 374432, 397776, + 404976, 414881, 412461, 405962, 388957, 369428, + 345535, 309636, 276555, 239076, 192795, 150978, + 104505, 65702, 20859, -25024, -65606, -102706, + -135673, -165912, -198790, -220192, -234479, -251306, + -260362, -266001, -259886, -260002, -248657, -234334, + -217503, -202297, -179055, -150768, -125468, -98832, + -72894, -42018, -16516, 10166, 39386, 62450, + 82544, 102115, 120459, 135598, 149235, 157042, + 158816, 163383, 162876, 159670, 151270, 148796, + 135741, 125439, 109131, 94537, 77595, 61876, + 45016, 28294, 13536, -6488, -22571, -33842, + -48832, -63638, -72140, -79993, -87569, -91387, + -95105, -99573, -95906, -97510, -90434, -89172, + -80757, -73767, -68179, -54591, -49098, -38081, + -30096, -17678, -7483, 5124, 12679, 18818, + 30341, 33042, 42717, 49133, 51057, 55150, + 58448, 56577, 55508, 55179, 54289, 49140, + 48421, 43652, 34986, 33365, 29958, 21943, + 13434, 9563, 6068, -2185, -7641, -11314, + -14491, -18527, -21375, -25536, -28757, -27068, + -31902, -32247, -31210, -32378, -28844, -28972, + -26917, -25630, -19653, -19191, -12051, -13582, + -4826, -5962, -4862, 33, 4819, 6114, + 9049, 8981, 14172, 14118, 15072, 15034, + 13362, 14617, 16282, 17865, 16105, 14323, + 10499, 14625, 9801, 12096, 5293, 8147, + 4763, 1429, -173, 295, 161, -2375, + -3393, -8030, -6053, -7764, -7193, -6627, + -9527, -5178, -9881, -4584, -6991, -9292, + -4680, -4957, -5201, -6648, -1473, -1584, + -1987, -2221, 2201, 2720, -1950, 3637, + 2274, 2021, -940, 2507, 2115, 4251, + 1166, 6089, 4628, 2775, 1047, 4879, + 3630, 4012, 4345, 3681, -266, 1657, + 2323, 829, 3660, 1100, -605, 876, + -165, -2762, 1204, -1477, 1170, 1144, + -1294, 1092, -473, -2287, 641, -3627, + 953, -4014, -3664, 1544, -1398, -3629, + 880, -264, 338, 1087, 1064, 578, + 1306, 2399, 218, -903, 3089, 1797, + 434, 3576, 2306, 1934, 3420, 2750, + 373, -2829, -1670, 851, 1698, 227, + 3297, -3177, -2034, 108, -559, 2858, + -1395, -1436, -3109, 2556, -1476, -899, + 232, -2557, -1521, -2150, 1048, -826, +}; + +static const int32_t fft_in_imag_3072_q31[3072] = { + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, +}; + +static const int32_t fft_ref_real_3072_q31[3072] = { + -45, 22, 19, -6, 19, -10, + 21, 10, -22, 11, -4, -26, + -5, -42, 5, 6, 9, -28, + -19, 5, 4, 13, -6, -24, + -4, -32, -14, -5, -21, -19, + -14, 5, 1, 12, 17, -4, + 17, 6, -31, -49, 6, -14, + -10, 43, -3, -29, -20, -4, + 1, -37, 37, -25, 21, -56, + -6, 6, -12, 10, -2790, 237344, + -3579960, 21781560, -69391869, 129763729, -149772245, 107836740, + -47262115, 11753740, -1421223, 57093, -178, -10, + 16, -20, 6, -4, -33, -18, + -22, -12, -20, -5, -33, -9, + 0, 2, -36, 15, 11, 36, + 45, 16, 26, -15, 15, -13, + -4, 17, 24, 17, -4, 0, + 13, -19, 17, -3, 20, 11, + -26, -11, 3, 0, -12, 26, + 20, 10, 42, -8, -16, -56, + 4, -8, 11, 8, 40, -9, + 5, -40, 20, -4, 19, -17, + 11, -9, -21, -16, 31, 31, + 0, 36, -14, 0, 2, -9, + -35, -37, -37, 37, -6, -2, + -35, 3, -19, -11, 11, -3, + -5, -15, -4, 34, 0, 5, + -27, 10, 28, -14, 26, -20, + -27, 2, -4, 32, 12, 13, + -4, -19, 25, 30, 0, 2, + 7, 21, -28, -10, -21, 10, + -4, -8, 8, -1, -8, 35, + -18, -8, -20, 5, -17, 3, + 2, 11, -22, -13, 3, 27, + 8, 34, 6, -17, 31, -17, + 31, -34, -8, 0, 9, 5, + -22, 2, 19, -19, 8, 5, + 16, -8, 26, 6, 4, 21, + 25, -7, 11, -34, -4, 17, + 11, -1, -20, -28, -6, -38, + -7, 17, 15, -11, 32, -1, + 8, 21, -8, 30, -55, -19, + -14, -10, 8, 8, -14, -21, + 15, 27, 5, -12, 9, -24, + -7, -3, -8, -20, -14, -7, + 6, -28, -3, 1, -6, -21, + -21, 2, -22, 44, -5, -25, + -34, 18, 19, 5, 9, 23, + 7, 6, -20, 22, 0, -13, + 51, 11, 2, -16, -6, 0, + 8, -6, 0, 18, 16, 22, + 10, -7, -54, -11, -18, -8, + 5, 27, 12, -17, -24, 13, + 11, 9, 2, -12, 23, 0, + 12, 31, -14, 12, 9, 24, + 2, -1, 32, -8, 11, -9, + 26, 6, -1, -3, 7, 57, + -20, 10, 8, 13, -24, 3, + -7, -29, 46, 30, 0, 22, + -2, -20, 39, 24, -15, -2, + -2, 7, -16, -50, 29, -7, + -22, 23, 19, -21, -29, -22, + 32, -4, 9, -14, -20, -17, + -12, 2, -19, -24, 10, 22, + 10, 14, -15, -3, -41, -20, + 3, 6, 20, 10, 8, -11, + -4, 10, 2, -19, -10, 41, + -8, -6, -36, -35, -11, 0, + -11, 1, -3, -16, 12, -14, + -28, 2, 6, -10, -14, -2, + 14, 10, -7, -13, 37, 17, + -1, 12, 9, -37, 34, -36, + 7, 8, 4, -13, 22, 24, + 19, 0, 24, 26, -33, -30, + 28, 19, -26, 20, -29, -12, + -2, -25, -26, 1, 3, 43, + -4, -12, -23, 4, -19, 21, + -34, -3, 55, -20, -29, -23, + 30, 51, 13, -15, 10, 9, + 17, -28, -23, -16, 64, -1, + 5, 10, -4, 29, 26, -7, + 18, -37, -24, 49, -18, 11, + -15, -29, 34, -2, -15, -17, + -6, 16, -15, 7, 29, -9, + 23, 18, 10, -10, 23, -33, + 28, -19, -1, 31, -13, 5, + -5, -47, -21, 29, 4, 13, + -7, -19, 2, 9, -31, 3, + 3, -3, 4, -1, -32, -34, + -11, -29, 11, -7, 44, 13, + -25, -12, -21, 6, 5, -6, + 71, -5, -4, 25, -4, -6, + -39, 16, -26, 9, -2, -14, + -3, -7, -5, -8, -21, -20, + -25, 9, 10, 2, -44, 2, + 16, 3, 31, 8, -16, -5, + 6, -7, 9, 7, 3, -15, + -13, 11, -44, 11, -4, 14, + 15, 6, -39, -2, -23, 9, + 13, -48, 19, 9, 7, -24, + 15, 43, -17, 40, 6, -26, + -36, 2, -30, 28, 8, -5, + -16, -20, 25, 48, -17, -6, + -45, 31, 7, 5, 27, 21, + 19, 36, -25, 18, 4, 3, + -20, -23, 8, -27, 27, -8, + -20, 23, -18, -13, -12, 13, + 2, 1, 15, 23, -10, 24, + -42, -46, 54, 18, -13, -11, + 5, 24, 12, 12, -19, 6, + -52, -10, 0, -2, -4, -10, + -14, 12, 12, -15, -17, 46, + 26, 20, 38, 16, -9, -12, + 0, -28, -8, -6, 4, -13, + 5, 7, 5, 0, 2, 10, + -15, 9, -2, 39, -13, 9, + 7, 11, -7, 26, 9, -11, + -20, -26, 42, 32, -22, -13, + 10, 39, 10, 6, -15, 25, + -17, -9, 14, -9, -5, -33, + 5, -23, -30, 0, 1, -6, + 3, 37, -15, 7, -4, 17, + 29, 14, 20, 11, -5, -14, + 35, 12, -13, -12, 8, -27, + 0, -8, -7, -8, 30, 23, + -41, 8, -28, 35, 53, 21, + 27, -17, -32, -5, -48, 4, + -8, 19, -2, -29, 17, 6, + -11, 2, -7, 0, 0, 1, + -34, -22, 22, -13, 10, 2, + -15, 22, 0, -47, 9, -1, + -42, -7, -10, 10, -21, -55, + 25, -27, -15, -21, -23, 10, + 2, -35, 20, -20, -3, 2, + -28, 9, 12, 40, 3, 25, + -9, -19, 15, 7, 8, -11, + 19, 10, -1, -15, -11, 32, + -32, -50, 29, -12, 13, 11, + -6, 30, 4, -26, 9, 3, + -4, 13, -32, -16, -9, 10, + 33, 26, 27, -23, -35, 10, + 15, -5, 53, 8, -25, -10, + -5, 7, 9, 11, -4, -34, + -7, 2, -5, 11, -13, -61, + -24, 18, -16, 13, 9, -45, + 10, 31, -9, 13, -3, 11, + 31, -30, 38, -14, -3, 18, + 26, 10, 10, 17, -16, -3, + -6, 37, -36, -17, -3, 30, + 14, 6, -31, -1, 8, -39, + 18, 20, 17, 22, -35, -12, + 0, -5, -6, -2, -37, 20, + -10, -16, -2, -5, 21, -8, + -5, 18, -12, -17, -5, 4, + -33, -8, -33, -23, -25, -14, + -15, 4, 53, -61, -6, 34, + -3, 11, -53, -7, -6, -11, + 28, 4, -16, -6, -13, -3, + 35, 3, -15, -16, 24, 16, + 25, 45, 5, -18, 2, 15, + 19, 42, 33, 17, 39, 8, + 3, 0, -12, -19, -9, 31, + 15, 40, 19, 8, 7, 5, + 14, -33, 8, -3, 16, -33, + -31, -13, -31, 37, -28, -19, + 6, 0, -46, 29, -22, -10, + -8, -2, -42, 29, -35, 10, + 40, 20, 8, 19, -13, 8, + 6, -2, 20, -15, 27, -42, + -11, 13, -1, 30, 31, 29, + 36, -27, 11, 36, 0, 3, + -9, 12, 1, -26, -8, 17, + -18, -9, -44, 23, -8, -36, + 2, 36, 41, 33, -28, -25, + 7, -8, 29, 43, 13, 19, + -7, 19, -19, -6, 34, -3, + 25, 40, 1, 3, -4, 5, + -16, -11, 36, 3, -17, -31, + 13, 3, -13, 3, -5, 6, + -6, -19, 1, -4, -7, 13, + -25, 26, -1, 26, -40, 0, + -1, -34, 21, 31, -22, 23, + 14, -19, 25, -7, 21, 31, + -6, 16, -16, 10, 28, 7, + -28, 7, -33, -6, 8, -31, + -16, -11, -3, -6, -7, 18, + -18, 24, 31, -24, 32, -43, + 9, -8, 12, 25, -21, 13, + -7, -5, -18, -48, -4, -33, + 15, 11, 7, 26, -10, -28, + -20, 8, 40, 15, 56, -35, + -42, 16, 3, -36, -25, 6, + 11, 24, -35, 10, -4, 5, + 17, 25, 73, -2, 72, -20, + -5, 19, 13, -8, -27, -18, + -11, 2, -15, 12, 16, -20, + -23, -12, -5, 47, 12, 9, + -14, 1, -10, 15, -4, -9, + -8, -5, -18, 28, -49, 18, + 35, 11, -8, 12, -10, -13, + 13, -4, -7, 4, 1, -13, + 13, 21, 28, 15, 19, -3, + 15, -8, 38, 31, -15, 21, + -8, -4, -10, 34, 3, -18, + -1, -20, -18, 16, -41, -3, + -10, -4, 15, -25, 2, -11, + -4, -14, 6, -14, -10, -17, + 10, -27, 51, -51, 30, 13, + -15, 4, -12, -55, -3, 17, + -7, -4, 0, -15, -28, -29, + -12, -29, 12, 6, -12, -46, + 9, 4, 9, -21, 10, 4, + 7, -9, -22, 4, -3, -8, + -15, 53, 26, 5, -19, -24, + 13, 16, -16, -26, 20, -35, + -26, 25, -15, 4, -23, 30, + -18, 11, -31, -2, -13, 24, + 3, 9, -1, 1, -1, -7, + 10, 29, -4, -1, 5, 12, + 5, 6, 3, 22, -18, 11, + -26, -4, -10, 10, -13, 7, + 13, 1, 36, -9, 35, 32, + 2, 7, -20, 19, -6, -18, + 15, -8, 33, 20, -9, -18, + 25, 29, 40, -23, -6, 34, + -21, 5, 21, -7, -2, 7, + -16, -3, 2, -18, 23, 9, + 62, 21, 32, 2, 6, -16, + 25, -9, -8, 31, 6, 37, + 14, 30, -36, -15, 4, 16, + 25, -13, 23, 26, 38, -5, + -27, 20, 27, -2, 9, -22, + 47, -3, 6, 42, 12, 17, + 8, -7, 47, 43, 2, 3, + -24, 9, -3, -11, 5, 42, + 13, 7, -23, -36, 22, 26, + -6, 18, -11, -15, -17, 18, + 2, 36, -40, -1, -18, -42, + 33, 56, 10, -1, 16, -2, + 6, -23, 16, 7, -35, -5, + -13, -11, -4, 10, 5, 5, + 33, 0, 8, 23, -19, 31, + -23, 2, -51, -30, -24, -28, + 13, -10, 8, -19, 14, -23, + -8, 3, -14, 17, -36, -21, + 12, 41, -5, 26, -4, -10, + 25, 40, -21, -4, 17, 8, + -22, -3, -38, 22, 5, 28, + 38, 0, -3, 1, 20, -11, + -3, -6, 28, -26, 9, -21, + 5, -14, -2, -42, -14, 23, + -21, -24, -13, 20, -19, -13, + -3, -7, 20, 13, -16, 18, + -14, -2, 14, 31, -1, -12, + -25, -2, 1, -9, -32, 37, + -5, -3, 30, -13, -7, 12, + 31, 12, -7, -13, 30, -3, + -5, 37, -32, -9, 1, -2, + -25, -12, -1, 31, 14, -2, + -14, 18, -16, 13, 20, -7, + -3, -13, -19, 20, -13, -24, + -21, 23, -14, -42, -2, -14, + 5, -21, 9, -26, 28, -6, + -3, -11, 20, 1, -3, 0, + 38, 28, 5, 22, -38, -3, + -22, 8, 17, -4, -21, 40, + 25, -10, -4, 26, -5, 41, + 12, -21, -36, 17, -14, 3, + -8, -23, 14, -19, 8, -10, + 13, -28, -24, -30, -51, 2, + -23, 31, -19, 23, 8, 0, + 33, 5, 5, 10, -4, -11, + -13, -5, -35, 7, 16, -23, + 6, -2, 16, -1, 10, 56, + 33, -42, -18, -1, -40, 36, + 2, 18, -17, -15, -11, 18, + -6, 26, 22, -36, -23, 7, + 13, 42, 5, -11, -3, 9, + -24, 3, 2, 43, 47, -7, + 8, 17, 12, 42, 6, -3, + 47, -22, 9, -2, 27, 20, + -27, -5, 38, 26, 23, -13, + 25, 16, 4, -15, -36, 30, + 14, 37, 6, 31, -8, -9, + 25, -16, 6, 2, 32, 21, + 62, 9, 23, -18, 2, -3, + -16, 7, -2, -7, 21, 5, + -21, 34, -6, -23, 40, 29, + 25, -18, -9, 20, 33, -8, + 15, -18, -6, 19, -20, 7, + 2, 32, 35, -9, 36, 1, + 13, 7, -13, 10, -10, -4, + -26, 11, -18, 22, 3, 6, + 5, 12, 5, -1, -4, 29, + 10, -7, -1, 1, -1, 9, + 3, 24, -13, -2, -31, 11, + -18, 30, -23, 4, -15, 25, + -26, -35, 20, -26, -16, 16, + 13, -24, -19, 5, 26, 53, + -15, -8, -3, 4, -22, -9, + 7, 4, 10, -21, 9, 4, + 9, -46, -12, 6, 12, -29, + -12, -29, -28, -15, 0, -4, + -7, 17, -3, -55, -12, 4, + -15, 13, 30, -51, 51, -27, + 10, -17, -10, -14, 6, -14, + -4, -11, 2, -25, 15, -4, + -10, -3, -41, 16, -18, -20, + -1, -18, 3, 34, -10, -4, + -8, 21, -15, 31, 38, -8, + 15, -3, 19, 15, 28, 21, + 13, -13, 1, 4, -7, -4, + 13, -13, -10, 12, -8, 11, + 35, 18, -49, 28, -18, -5, + -8, -9, -4, 15, -10, 1, + -14, 9, 12, 47, -5, -12, + -23, -20, 16, 12, -15, 2, + -11, -18, -27, -8, 13, 19, + -5, -20, 72, -2, 73, 25, + 17, 5, -4, 10, -35, 24, + 11, 6, -25, -36, 3, 16, + -42, -35, 56, 15, 40, 8, + -20, -28, -10, 26, 7, 11, + 15, -33, -4, -48, -18, -5, + -7, 13, -21, 25, 12, -8, + 9, -43, 32, -24, 31, 24, + -18, 18, -7, -6, -3, -11, + -16, -31, 8, -6, -33, 7, + -28, 7, 28, 10, -16, 16, + -6, 31, 21, -7, 25, -19, + 14, 23, -22, 31, 21, -34, + -1, 0, -40, 26, -1, 26, + -25, 13, -7, -4, 1, -19, + -6, 6, -5, 3, -13, 3, + 13, -31, -17, 3, 36, -11, + -16, 5, -4, 3, 1, 40, + 25, -3, 34, -6, -19, 19, + -7, 19, 13, 43, 29, -8, + 7, -25, -28, 33, 41, 36, + 2, -36, -8, 23, -44, -9, + -18, 17, -8, -26, 1, 12, + -9, 3, 0, 36, 11, -27, + 36, 29, 31, 30, -1, 13, + -11, -42, 27, -15, 20, -2, + 6, 8, -13, 19, 8, 20, + 40, 10, -35, 29, -42, -2, + -8, -10, -22, 29, -46, 0, + 6, -19, -28, 37, -31, -13, + -31, -33, 16, -3, 8, -33, + 14, 5, 7, 8, 19, 40, + 15, 31, -9, -19, -12, 0, + 3, 8, 39, 17, 33, 42, + 19, 15, 2, -18, 5, 45, + 25, 16, 24, -16, -15, 3, + 35, -3, -13, -6, -16, 4, + 28, -11, -6, -7, -53, 11, + -3, 34, -6, -61, 53, 4, + -15, -14, -25, -23, -33, -8, + -33, 4, -5, -17, -12, 18, + -5, -8, 21, -5, -2, -16, + -10, 20, -37, -2, -6, -5, + 0, -12, -35, 22, 17, 20, + 18, -39, 8, -1, -31, 6, + 14, 30, -3, -17, -36, 37, + -6, -3, -16, 17, 10, 10, + 26, 18, -3, -14, 38, -30, + 31, 11, -3, 13, -9, 31, + 10, -45, 9, 13, -16, 18, + -24, -61, -13, 11, -5, 2, + -7, -34, -4, 11, 9, 7, + -5, -10, -25, 8, 53, -5, + 15, 10, -35, -23, 27, 26, + 33, 10, -9, -16, -32, 13, + -4, 3, 9, -26, 4, 30, + -6, 11, 13, -12, 29, -50, + -32, 32, -11, -15, -1, 10, + 19, -11, 8, 7, 15, -19, + -9, 25, 3, 40, 12, 9, + -28, 2, -3, -20, 20, -35, + 2, 10, -23, -21, -15, -27, + 25, -55, -21, 10, -10, -7, + -42, -1, 9, -47, 0, 22, + -15, 2, 10, -13, 22, -22, + -34, 1, 0, 0, -7, 2, + -11, 6, 17, -29, -2, 19, + -8, 4, -48, -5, -32, -17, + 27, 21, 53, 35, -28, 8, + -41, 23, 30, -8, -7, -8, + 0, -27, 8, -12, -13, 12, + 35, -14, -5, 11, 20, 14, + 29, 17, -4, 7, -15, 37, + 3, -6, 1, 0, -30, -23, + 5, -33, -5, -9, 14, -9, + -17, 25, -15, 6, 10, 39, + 10, -13, -22, 32, 42, -26, + -20, -11, 9, 26, -7, 11, + 7, 9, -13, 39, -2, 9, + -15, 10, 2, 0, 5, 7, + 5, -13, 4, -6, -8, -28, + 0, -12, -9, 16, 38, 20, + 26, 46, -17, -15, 12, 12, + -14, -10, -4, -2, 0, -10, + -52, 6, -19, 12, 12, 24, + 5, -11, -13, 18, 54, -46, + -42, 24, -10, 23, 15, 1, + 2, 13, -12, -13, -18, 23, + -20, -8, 27, -27, 8, -23, + -20, 3, 4, 18, -25, 36, + 19, 21, 27, 5, 7, 31, + -45, -6, -17, 48, 25, -20, + -16, -5, 8, 28, -30, 2, + -36, -26, 6, 40, -17, 43, + 15, -24, 7, 9, 19, -48, + 13, 9, -23, -2, -39, 6, + 15, 14, -4, 11, -44, 11, + -13, -15, 3, 7, 9, -7, + 6, -5, -16, 8, 31, 3, + 16, 2, -44, 2, 10, 9, + -25, -20, -21, -8, -5, -7, + -3, -14, -2, 9, -26, 16, + -39, -6, -4, 25, -4, -5, + 71, -6, 5, 6, -21, -12, + -25, 13, 44, -7, 11, -29, + -11, -34, -32, -1, 4, -3, + 3, 3, -31, 9, 2, -19, + -7, 13, 4, 29, -21, -47, + -5, 5, -13, 31, -1, -19, + 28, -33, 23, -10, 10, 18, + 23, -9, 29, 7, -15, 16, + -6, -17, -15, -2, 34, -29, + -15, 11, -18, 49, -24, -37, + 18, -7, 26, 29, -4, 10, + 5, -1, 64, -16, -23, -28, + 17, 9, 10, -15, 13, 51, + 30, -23, -29, -20, 55, -3, + -34, 21, -19, 4, -23, -12, + -4, 43, 3, 1, -26, -25, + -2, -12, -29, 20, -26, 19, + 28, -30, -33, 26, 24, 0, + 19, 24, 22, -13, 4, 8, + 7, -36, 34, -37, 9, 12, + -1, 17, 37, -13, -7, 10, + 14, -2, -14, -10, 6, 2, + -28, -14, 12, -16, -3, 1, + -11, 0, -11, -35, -36, -6, + -8, 41, -10, -19, 2, 10, + -4, -11, 8, 10, 20, 6, + 3, -20, -41, -3, -15, 14, + 10, 22, 10, -24, -19, 2, + -12, -17, -20, -14, 9, -4, + 32, -22, -29, -21, 19, 23, + -22, -7, 29, -50, -16, 7, + -2, -2, -15, 24, 39, -20, + -2, 22, 0, 30, 46, -29, + -7, 3, -24, 13, 8, 10, + -20, 57, 7, -3, -1, 6, + 26, -9, 11, -8, 32, -1, + 2, 24, 9, 12, -14, 31, + 12, 0, 23, -12, 2, 9, + 11, 13, -24, -17, 12, 27, + 5, -8, -18, -11, -54, -7, + 10, 22, 16, 18, 0, -6, + 8, 0, -6, -16, 2, 11, + 51, -13, 0, 22, -20, 6, + 7, 23, 9, 5, 19, 18, + -34, -25, -5, 44, -22, 2, + -21, -21, -6, 1, -3, -28, + 6, -7, -14, -20, -8, -3, + -7, -24, 9, -12, 5, 27, + 15, -21, -14, 8, 8, -10, + -14, -19, -55, 30, -8, 21, + 8, -1, 32, -11, 15, 17, + -7, -38, -6, -28, -20, -1, + 11, 17, -4, -34, 11, -7, + 25, 21, 4, 6, 26, -8, + 16, 5, 8, -19, 19, 2, + -22, 5, 9, 0, -8, -34, + 31, -17, 31, -17, 6, 34, + 8, 27, 3, -13, -22, 11, + 2, 3, -17, 5, -20, -8, + -18, 35, -8, -1, 8, -8, + -4, 10, -21, -10, -28, 21, + 7, 2, 0, 30, 25, -19, + -4, 13, 12, 32, -4, 2, + -27, -20, 26, -14, 28, 10, + -27, 5, 0, 34, -4, -15, + -5, -3, 11, -11, -19, 3, + -35, -2, -6, 37, -37, -37, + -35, -9, 2, 0, -14, 36, + 0, 31, 31, -16, -21, -9, + 11, -17, 19, -4, 20, -40, + 5, -9, 40, 8, 11, -8, + 4, -56, -16, -8, 42, 10, + 20, 26, -12, 0, 3, -11, + -26, 11, 20, -3, 17, -19, + 13, 0, -4, 17, 24, 17, + -4, -13, 15, -15, 26, 16, + 45, 36, 11, 15, -36, 2, + 0, -9, -33, -5, -20, -12, + -22, -18, -33, -4, 6, -20, + 16, -10, -178, 57093, -1421223, 11753740, + -47262115, 107836740, -149772245, 129763729, -69391869, 21781560, + -3579960, 237344, -2790, 10, -12, 6, + -6, -56, 21, -25, 37, -37, + 1, -4, -20, -29, -3, 43, + -10, -14, 6, -49, -31, 6, + 17, -4, 17, 12, 1, 5, + -14, -19, -21, -5, -14, -32, + -4, -24, -6, 13, 4, 5, + -19, -28, 9, 6, 5, -42, + -5, -26, -4, 11, -22, 10, + 21, -10, 19, -6, 19, 22, +}; + +static const int32_t fft_ref_imag_3072_q31[3072] = { + 0, -1, -4, -15, -6, 19, + 1, 14, 47, -16, 15, 7, + 11, -9, -21, 4, 30, 43, + 16, 41, -11, 34, -26, 1, + 5, -11, -9, 45, -15, -18, + 18, -1, -6, 47, 25, -24, + -21, -56, 19, -1, 2, -12, + -15, 28, -26, 5, 15, 0, + 3, -17, 33, 0, -32, -16, + 10, 21, -16, -2, -3984, 340956, + -5154182, 31427830, -100341944, 188051672, -217523306, 156961255, + -68943044, 17183326, -2082337, 83868, -223, -19, + -32, -29, 1, 20, -16, 53, + 22, 26, -19, 7, -22, -18, + 4, 3, -20, 25, -23, 5, + -23, 17, -26, 23, -25, 7, + -19, -41, 14, -20, -1, 11, + 3, -12, -4, 11, 23, 8, + 15, 4, 2, 23, 26, 9, + 30, 31, -20, -17, -38, 39, + -23, 7, 10, 18, 17, 0, + 23, -15, 12, 25, -9, -28, + 34, -19, -23, 6, 11, 1, + -13, -3, 19, 23, -10, -19, + 25, 5, -3, -7, -22, 17, + 43, -32, 10, -10, -43, -34, + 25, 1, 7, -26, 4, 21, + -2, 5, 2, -15, -1, 19, + -19, -1, 37, -18, -44, 15, + -14, 1, 24, -11, -14, 6, + -20, -14, 16, -11, 31, 35, + 14, -4, 3, 17, -22, -21, + 24, -32, 26, 43, -9, -20, + -13, -21, 15, -25, 30, -11, + -14, -2, -28, -23, -21, 8, + 24, -9, -36, -29, 1, 6, + -6, 0, -11, -19, 22, 15, + 29, -28, -12, 18, 18, 4, + 10, -56, 7, -21, -32, 11, + -20, -18, -35, 19, -15, 4, + 42, -7, 43, 2, 9, -11, + 14, 9, -2, 25, 14, -4, + 0, 7, 19, 7, 21, 20, + -9, 20, 22, -22, -18, 21, + -19, 16, 0, 2, -8, -24, + 20, 13, -6, 15, -20, -19, + -35, -21, -1, 0, 22, 11, + 20, 14, -14, 41, 36, -2, + -4, 1, 35, 4, 19, -12, + 3, -5, -26, -26, -33, 12, + 13, -21, -20, 18, -23, -14, + -13, -23, -14, 20, -20, 12, + 16, 7, 7, 12, -9, 15, + -17, 20, 29, 12, -7, -11, + 17, -35, -18, 6, -3, 22, + -10, -12, -21, 21, 12, -4, + -28, 27, -8, -5, -8, 21, + -9, 16, 19, -13, -1, 0, + -40, 34, 1, -22, -7, 5, + 25, 15, -5, -38, 17, 35, + -11, 19, -8, -9, -19, 42, + 10, 10, -63, 3, -39, 18, + -7, 32, -2, 0, 7, 18, + 20, 11, -57, -22, -1, 18, + -13, 13, -11, 5, -32, 16, + -11, 40, 3, 8, -7, -62, + -5, 22, -16, -6, 15, 34, + 20, -55, 16, 36, 19, 30, + 8, 31, -2, 19, -46, -12, + -20, -7, -13, -15, -5, 6, + 9, -12, -6, 23, -7, 7, + -33, -23, -1, -17, -2, 0, + -1, 9, 11, 42, -3, -57, + -42, 3, -13, -30, 26, -13, + 26, -28, 17, -36, -32, 12, + 17, 8, -33, 27, 18, -13, + 3, 24, 3, 47, -8, 7, + 11, -17, 0, -12, -20, 12, + -15, -18, -6, -20, -6, 9, + 19, 20, -13, 4, 18, 3, + 11, -6, 37, 27, 18, -2, + -13, -8, 43, 33, -7, -11, + 7, 30, 3, 9, 17, 20, + -40, 25, 48, 8, 8, 29, + -23, 19, -13, -4, 30, -13, + 27, 18, 43, -1, 35, 28, + -38, -14, -32, 14, 14, -7, + 26, 4, -18, 37, -26, 5, + 18, 38, 6, 20, 15, 13, + 17, 27, 23, 21, 9, 7, + 37, 28, 35, -12, 11, -11, + -26, -5, 18, -56, 11, -31, + -20, 28, -21, 2, 35, -17, + -26, 30, -28, -4, 40, 6, + 19, -6, 11, 0, -1, 17, + 4, 0, -33, -4, 3, 17, + 18, 15, -15, 31, -14, -3, + -19, -8, 2, -50, 2, -5, + -12, 14, 18, -20, -2, 30, + -10, -50, -23, -7, -12, 8, + 6, -1, 39, 6, 0, -58, + 14, 20, 16, -14, -35, 0, + -19, 27, -11, -7, -19, 14, + 10, 0, 43, -18, 33, 5, + -10, 72, 4, -1, -7, -22, + -25, -24, 1, -8, 3, 1, + 45, 3, 22, 5, -12, -23, + -46, 11, 8, -19, 8, 39, + 4, 2, -31, 4, -8, 20, + 17, 33, -11, 4, -23, 8, + 10, -5, -4, 5, 25, -34, + -1, 18, -19, -8, -27, 8, + 5, -39, 48, -20, -13, 32, + 27, -31, 17, -21, 13, -9, + 11, 9, 33, -8, -26, 16, + -38, -1, -19, 6, 1, 2, + -2, 21, 42, -14, 27, 5, + -16, 22, -55, 18, -23, 74, + -14, 15, 2, 16, -8, -28, + -36, 27, 15, 1, 20, -6, + 15, 11, -56, 29, 10, 24, + 31, 13, -38, 24, -18, -16, + 16, 13, 4, -9, -6, 7, + 11, -1, -11, -28, -23, 24, + -4, -4, 22, 29, 10, -41, + -2, -24, -14, 13, -42, -20, + 26, 34, -5, -19, 15, 7, + -44, -15, -44, 21, -2, -37, + 25, 33, 8, 22, 30, -3, + 10, -13, 1, 13, 13, 26, + -26, 20, 37, 17, 22, 21, + 25, 1, 0, -17, -1, -5, + 6, -17, 5, -28, 29, 13, + 6, -32, -5, -30, 4, -3, + -13, -14, 35, 19, 31, 34, + 66, 11, -28, 31, 13, 2, + -6, 2, 52, -22, 5, -6, + 1, -21, 22, -46, -28, -10, + 42, 16, -26, 6, -21, -17, + -18, 5, 18, 11, -53, -3, + -9, 2, -3, 8, -7, 32, + 14, -26, 25, 5, 19, 1, + -3, 4, -10, 20, -12, -19, + -11, -35, 3, 11, -27, -10, + -15, -40, -10, 19, -1, -20, + -30, 2, 49, 21, 32, 16, + -43, -3, -34, -26, 4, -23, + 41, 6, 35, 25, 28, 20, + 3, 14, -12, -3, -14, -9, + 10, 26, 6, -42, 10, 21, + 18, 13, -23, 27, -5, -28, + 11, -2, -5, -19, 19, 12, + 32, -1, -12, -24, -11, -11, + -15, 8, 6, 38, 21, 29, + -17, -4, -40, 2, 11, -10, + 1, 10, -16, -12, -65, -5, + 23, 39, -40, -10, 23, -14, + 2, 38, -5, 3, 15, 19, + -1, 25, -20, -22, -24, -17, + 11, -9, -16, 4, 18, -10, + -5, -14, -24, -13, -20, 38, + -31, 17, -33, -15, -9, -25, + 9, -9, 8, -23, -5, 14, + 45, 10, -11, 9, -2, 2, + -17, -8, 11, -13, -24, -3, + 8, -4, -13, 22, 11, 28, + 4, 14, 28, 10, 16, -35, + 5, -8, -3, -24, 9, -22, + -32, 10, 24, -1, 27, 10, + 0, 13, -26, -3, 2, -13, + 24, 23, -20, -38, 13, 21, + -20, -11, -16, -24, -5, 4, + 21, 35, 9, 12, -2, 1, + 16, 15, -31, 47, 7, -3, + 18, -22, -7, 28, -7, -6, + 18, -6, -5, 6, -35, 11, + 18, 3, 9, 4, -4, 5, + -3, -26, 35, 22, 34, 42, + -24, 9, -24, 10, -23, 34, + -32, 30, -1, 32, 17, 13, + -26, 10, 0, -1, 23, 3, + -14, 59, -49, -35, 3, 1, + -17, -5, 0, 5, -5, 18, + 10, 4, -4, 18, -4, -19, + -33, -4, -5, 26, -9, -18, + 53, -30, 19, 23, 39, -9, + -6, 38, -10, 22, 13, -21, + 8, 1, 17, -21, 1, -18, + 34, 25, -4, -3, 32, 0, + -23, -2, -9, -13, 1, -5, + -2, 31, -29, -11, -16, 2, + -8, -29, -34, -44, -51, -28, + 7, -13, 9, -44, -3, 30, + -15, -33, 25, -26, 7, -21, + 40, 31, -5, 42, -18, -21, + 5, -17, 24, -7, 29, 5, + 17, 15, 3, 23, 19, -6, + -33, 12, -10, -31, 38, 6, + 26, 26, 6, 20, 18, 39, + 30, -6, -17, -13, 22, -49, + 10, -13, -16, 15, 12, 14, + -31, -17, -1, 17, 8, -32, + -28, -15, 9, -30, 23, -12, + 17, -22, -21, -60, -33, -16, + 13, -14, -7, 18, 20, -8, + -11, 3, -6, -35, -15, 27, + 24, 67, 10, -18, 8, -14, + 3, 11, -38, -1, -18, 26, + -21, 39, 3, -3, 34, -45, + 11, 0, 11, -2, -16, -7, + 34, -7, 22, 16, -22, -14, + 17, -15, -32, 9, 37, 5, + 27, -15, 24, 2, -9, -17, + -17, -11, -26, 8, 29, 9, + 21, -41, -3, 36, 8, 43, + 14, -6, 9, 5, -1, -3, + -38, -27, 9, 1, 13, 7, + -6, 54, 20, -18, 9, 0, + 10, 6, -8, 30, -17, -28, + -22, 7, 16, -42, 12, 14, + 29, -2, 21, 19, 0, -23, + 19, -16, -31, -3, -32, -45, + -52, -24, -27, 2, 15, -28, + -11, 24, -14, 9, -4, 6, + 3, 1, -22, 21, 5, 37, + 24, 33, 21, 14, -56, -20, + -12, -26, -20, -12, -42, -17, + -12, 0, 0, -7, -6, 14, + 37, -37, -6, -8, 18, -20, + 24, 12, 11, 14, 20, -24, + 6, -6, -1, -19, -7, 4, + 4, -11, -29, 6, -3, 18, + -37, -37, -26, -24, 17, -17, + -3, -21, 19, -2, 15, -5, + -14, 0, 6, 4, 9, -19, + 8, -13, 15, -14, -14, -28, + 19, -24, 40, 6, -10, -20, + -21, -38, -14, -11, 14, 12, + 31, 30, -1, 9, -5, -4, + -29, -20, 43, -7, -33, -10, + -31, 0, 9, 2, 15, 18, + -11, -11, -3, 12, 32, 31, + -9, -8, 19, -5, -2, -8, + -19, 7, 13, -10, -1, -4, + -38, -6, 6, -34, -14, -4, + -11, 6, 12, -18, 28, 73, + -21, 54, -16, 32, -17, 13, + -3, 5, -2, -5, -1, 22, + 8, 29, 15, -9, -26, -29, + 1, 28, -27, 15, 0, 26, + -28, 17, 7, -7, -4, 4, + -3, -22, -3, -25, -6, 10, + -26, -11, -19, -23, -14, 22, + 0, -10, 22, -5, -3, -11, + 27, -6, 27, -8, 19, -8, + -2, 12, 2, 53, 11, -46, + 0, 46, -11, -53, -2, -12, + 2, 8, -19, 8, -27, 6, + -27, 11, 3, 5, -22, 10, + 0, -22, 14, 23, 19, 11, + 26, -10, 6, 25, 3, 22, + 3, -4, 4, 7, -7, -17, + 28, -26, 0, -15, 27, -28, + -1, 29, 26, 9, -15, -29, + -8, -22, 1, 5, 2, -5, + 3, -13, 17, -32, 16, -54, + 21, -73, -28, 18, -12, -6, + 11, 4, 14, 34, -6, 6, + 38, 4, 1, 10, -13, -7, + 19, 8, 2, 5, -19, 8, + 9, -31, -32, -12, 3, 11, + 11, -18, -15, -2, -9, 0, + 31, 10, 33, 7, -43, 20, + 29, 4, 5, -9, 1, -30, + -31, -12, -14, 11, 14, 38, + 21, 20, 10, -6, -40, 24, + -19, 28, 14, 14, -15, 13, + -8, 19, -9, -4, -6, 0, + 14, 5, -15, 2, -19, 21, + 3, 17, -17, 24, 26, 37, + 37, -18, 3, -6, 29, 11, + -4, -4, 7, 19, 1, 6, + -6, 24, -20, -14, -11, -12, + -24, 20, -18, 8, 6, 37, + -37, -14, 6, 7, 0, 0, + 12, 17, 42, 12, 20, 26, + 12, 20, 56, -14, -21, -33, + -24, -37, -5, -21, 22, -1, + -3, -6, 4, -9, 14, -24, + 11, 28, -15, -2, 27, 24, + 52, 45, 32, 3, 31, 16, + -19, 23, 0, -19, -21, 2, + -29, -14, -12, 42, -16, -7, + 22, 28, 17, -30, 8, -6, + -10, 0, -9, 18, -20, -54, + 6, -7, -13, -1, -9, 27, + 38, 3, 1, -5, -9, 6, + -14, -43, -8, -36, 3, 41, + -21, -9, -29, -8, 26, 11, + 17, 17, 9, -2, -24, 15, + -27, -5, -37, -9, 32, 15, + -17, 14, 22, -16, -22, 7, + -34, 7, 16, 2, -11, 0, + -11, 45, -34, 3, -3, -39, + 21, -26, 18, 1, 38, -11, + -3, 14, -8, 18, -10, -67, + -24, -27, 15, 35, 6, -3, + 11, 8, -20, -18, 7, 14, + -13, 16, 33, 60, 21, 22, + -17, 12, -23, 30, -9, 15, + 28, 32, -8, -17, 1, 17, + 31, -14, -12, -15, 16, 13, + -10, 49, -22, 13, 17, 6, + -30, -39, -18, -20, -6, -26, + -26, -6, -38, 31, 10, -12, + 33, 6, -19, -23, -3, -15, + -17, -5, -29, 7, -24, 17, + -5, 21, 18, -42, 5, -31, + -40, 21, -7, 26, -25, 33, + 15, -30, 3, 44, -9, 13, + -7, 28, 51, 44, 34, 29, + 8, -2, 16, 11, 29, -31, + 2, 5, -1, 13, 9, 2, + 23, 0, -32, 3, 4, -25, + -34, 18, -1, 21, -17, -1, + -8, 21, -13, -22, 10, -38, + 6, 9, -39, -23, -19, 30, + -53, 18, 9, -26, 5, 4, + 33, 19, 4, -18, 4, -4, + -10, -18, 5, -5, 0, 5, + 17, -1, -3, 35, 49, -59, + 14, -3, -23, 1, 0, -10, + 26, -13, -17, -32, 1, -30, + 32, -34, 23, -10, 24, -9, + 24, -42, -34, -22, -35, 26, + 3, -5, 4, -4, -9, -3, + -18, -11, 35, -6, 5, 6, + -18, 6, 7, -28, 7, 22, + -18, 3, -7, -47, 31, -15, + -16, -1, 2, -12, -9, -35, + -21, -4, 5, 24, 16, 11, + 20, -21, -13, 38, 20, -23, + -24, 13, -2, 3, 26, -13, + 0, -10, -27, 1, -24, -10, + 32, 22, -9, 24, 3, 8, + -5, 35, -16, -10, -28, -14, + -4, -28, -11, -22, 13, 4, + -8, 3, 24, 13, -11, 8, + 17, -2, 2, -9, 11, -10, + -45, -14, 5, 23, -8, 9, + -9, 25, 9, 15, 33, -17, + 31, -38, 20, 13, 24, 14, + 5, 10, -18, -4, 16, 9, + -11, 17, 24, 22, 20, -25, + 1, -19, -15, -3, 5, -38, + -2, 14, -23, 10, 40, -39, + -23, 5, 65, 12, 16, -10, + -1, 10, -11, -2, 40, 4, + 17, -29, -21, -38, -6, -8, + 15, 11, 11, 24, 12, 1, + -32, -12, -19, 19, 5, 2, + -11, 28, 5, -27, 23, -13, + -18, -21, -10, 42, -6, -26, + -10, 9, 14, 3, 12, -14, + -3, -20, -28, -25, -35, -6, + -41, 23, -4, 26, 34, 3, + 43, -16, -32, -21, -49, -2, + 30, 20, 1, -19, 10, 40, + 15, 10, 27, -11, -3, 35, + 11, 19, 12, -20, 10, -4, + 3, -1, -19, -5, -25, 26, + -14, -32, 7, -8, 3, -2, + 9, 3, 53, -11, -18, -5, + 18, 17, 21, -6, 26, -16, + -42, 10, 28, 46, -22, 21, + -1, 6, -5, 22, -52, -2, + 6, -2, -13, -31, 28, -11, + -66, -34, -31, -19, -35, 14, + 13, 3, -4, 30, 5, 32, + -6, -13, -29, 28, -5, 17, + -6, 5, 1, 17, 0, -1, + -25, -21, -22, -17, -37, -20, + 26, -26, -13, -13, -1, 13, + -10, 3, -30, -22, -8, -33, + -25, 37, 2, -21, 44, 15, + 44, -7, -15, 19, 5, -34, + -26, 20, 42, -13, 14, 24, + 2, 41, -10, -29, -22, 4, + 4, -24, 23, 28, 11, 1, + -11, -7, 6, 9, -4, -13, + -16, 16, 18, -24, 38, -13, + -31, -24, -10, -29, 56, -11, + -15, 6, -20, -1, -15, -27, + 36, 28, 8, -16, -2, -15, + 14, -74, 23, -18, 55, -22, + 16, -5, -27, 14, -42, -21, + 2, -2, -1, -6, 19, 1, + 38, -16, 26, 8, -33, -9, + -11, 9, -13, 21, -17, 31, + -27, -32, 13, 20, -48, 39, + -5, -8, 27, 8, 19, -18, + 1, 34, -25, -5, 4, 5, + -10, -8, 23, -4, 11, -33, + -17, -20, 8, -4, 31, -2, + -4, -39, -8, 19, -8, -11, + 46, 23, 12, -5, -22, -3, + -45, -1, -3, 8, -1, 24, + 25, 22, 7, 1, -4, -72, + 10, -5, -33, 18, -43, 0, + -10, -14, 19, 7, 11, -27, + 19, 0, 35, 14, -16, -20, + -14, 58, 0, -6, -39, 1, + -6, -8, 12, 7, 23, 50, + 10, -30, 2, 20, -18, -14, + 12, 5, -2, 50, -2, 8, + 19, 3, 14, -31, 15, -15, + -18, -17, -3, 4, 33, 0, + -4, -17, 1, 0, -11, 6, + -19, -6, -40, 4, 28, -30, + 26, 17, -35, -2, 21, -28, + 20, 31, -11, 56, -18, 5, + 26, 11, -11, 12, -35, -28, + -37, -7, -9, -21, -23, -27, + -17, -13, -15, -20, -6, -38, + -18, -5, 26, -37, 18, -4, + -26, 7, -14, -14, 32, 14, + 38, -28, -35, 1, -43, -18, + -27, 13, -30, 4, 13, -19, + 23, -29, -8, -8, -48, -25, + 40, -20, -17, -9, -3, -30, + -7, 11, 7, -33, -43, 8, + 13, 2, -18, -27, -37, 6, + -11, -3, -18, -4, 13, -20, + -19, -9, 6, 20, 6, 18, + 15, -12, 20, 12, 0, 17, + -11, -7, 8, -47, -3, -24, + -3, 13, -18, -27, 33, -8, + -17, -12, 32, 36, -17, 28, + -26, 13, -26, 30, 13, -3, + 42, 57, 3, -42, -11, -9, + 1, 0, 2, 17, 1, 23, + 33, -7, 7, -23, 6, 12, + -9, -6, 5, 15, 13, 7, + 20, 12, 46, -19, 2, -31, + -8, -30, -19, -36, -16, 55, + -20, -34, -15, 6, 16, -22, + 5, 62, 7, -8, -3, -40, + 11, -16, 32, -5, 11, -13, + 13, -18, 1, 22, 57, -11, + -20, -18, -7, 0, 2, -32, + 7, -18, 39, -3, 63, -10, + -10, -42, 19, 9, 8, -19, + 11, -35, -17, 38, 5, -15, + -25, -5, 7, 22, -1, -34, + 40, 0, 1, 13, -19, -16, + 9, -21, 8, 5, 8, -27, + 28, 4, -12, -21, 21, 12, + 10, -22, 3, -6, 18, 35, + -17, 11, 7, -12, -29, -20, + 17, -15, 9, -12, -7, -7, + -16, -12, 20, -20, 14, 23, + 13, 14, 23, -18, 20, 21, + -13, -12, 33, 26, 26, 5, + -3, 12, -19, -4, -35, -1, + 4, 2, -36, -41, 14, -14, + -20, -11, -22, 0, 1, 21, + 35, 19, 20, -15, 6, -13, + -20, 24, 8, -2, 0, -16, + 19, -21, 18, 22, -22, -20, + 9, -20, -21, -7, -19, -7, + 0, 4, -14, -25, 2, -9, + -14, 11, -9, -2, -43, 7, + -42, -4, 15, -19, 35, 18, + 20, -11, 32, 21, -7, 56, + -10, -4, -18, -18, 12, 28, + -29, -15, -22, 19, 11, 0, + 6, -6, -1, 29, 36, 9, + -24, -8, 21, 23, 28, 2, + 14, 11, -30, 25, -15, 21, + 13, 20, 9, -43, -26, 32, + -24, 21, 22, -17, -3, 4, + -14, -35, -31, 11, -16, 14, + 20, -6, 14, 11, -24, -1, + 14, -15, 44, 18, -37, 1, + 19, -19, 1, 15, -2, -5, + 2, -21, -4, 26, -7, -1, + -25, 34, 43, 10, -10, 32, + -43, -17, 22, 7, 3, -5, + -25, 19, 10, -23, -19, 3, + 13, -1, -11, -6, 23, 19, + -34, 28, 9, -25, -12, 15, + -23, 0, -17, -18, -10, -7, + 23, -39, 38, 17, 20, -31, + -30, -9, -26, -23, -2, -4, + -15, -8, -23, -11, 4, 12, + -3, -11, 1, 20, -14, 41, + 19, -7, 25, -23, 26, -17, + 23, -5, 23, -25, 20, -3, + -4, 18, 22, -7, 19, -26, + -22, -53, 16, -20, -1, 29, + 32, 19, 223, -83868, 2082337, -17183326, + 68943044, -156961255, 217523306, -188051672, 100341944, -31427830, + 5154182, -340956, 3984, 2, 16, -21, + -10, 16, 32, 0, -33, 17, + -3, 0, -15, -5, 26, -28, + 15, 12, -2, 1, -19, 56, + 21, 24, -25, -47, 6, 1, + -18, 18, 15, -45, 9, 11, + -5, -1, 26, -34, 11, -41, + -16, -43, -30, -4, 21, 9, + -11, -7, -15, 16, -47, -14, + -1, -19, 6, 15, 4, 1, +}; diff --git a/test/cmocka/src/math/fft/ref_fft_multi_512_32.h b/test/cmocka/src/math/fft/ref_fft_multi_512_32.h new file mode 100644 index 000000000000..2f66b1943af0 --- /dev/null +++ b/test/cmocka/src/math/fft/ref_fft_multi_512_32.h @@ -0,0 +1,364 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * + * Copyright(c) 2025 Intel Corporation. + */ + +/* Created 20-Nov-2025 16:11:52 with script ref_fft_multi.m v1.9-rc1-6882-ge0b90b605-dirty */ + +#define REF_SOFM_FFT_MULTI_512_NUM_TESTS 1 + +static const int32_t fft_in_real_512_q31[512] = { + 2377, 340, -1196, 1231, 390, -220, + 1663, -1097, 2649, 2272, 2236, 4342, + 3392, 4062, 6525, 5862, 5260, 4298, + 5946, 6952, 9757, 6742, 5507, 6233, + 2746, -6220, -7167, -15262, -27034, -37541, + -48308, -63366, -79216, -99095, -114978, -136892, + -158310, -176123, -192474, -205758, -221510, -225664, + -228096, -217668, -204746, -176198, -137635, -82142, + -9371, 74606, 179015, 298476, 439502, 594979, + 765297, 950034, 1143794, 1342066, 1544951, 1741581, + 1929492, 2099900, 2239228, 2342857, 2403695, 2406952, + 2348372, 2215760, 1999224, 1685659, 1277296, 767477, + 145652, -576843, -1412213, -2348478, -3374059, -4478609, + -5650739, -6865398, -8098727, -9324264, -10504916, -11605125, + -12596267, -13427768, -14058251, -14447678, -14546148, -14324532, + -13731373, -12741029, -11318240, -9445464, -7106292, -4298630, + -1028803, 2685749, 6812826, 11306975, 16107663, 21145964, + 26323168, 31551295, 36709825, 41672460, 46306375, 50477948, + 54028871, 56821040, 58704348, 59538514, 59202251, 57564674, + 54532854, 50034062, 44008837, 36428878, 27312638, 16697669, + 4668381, -8656667, -23115247, -38512817, -54605837, -71114797, + -87734187, -104118684, -119911490, -134714976, -148144714, -159788322, + -169256899, -176160487, -180148043, -180882561, -178086768, -171520785, + -161024629, -146479642, -127877567, -105271227, -78804161, -48731098, + -15371603, 20843417, 59393829, 99676740, 141010892, 182645196, + 223765608, 263517948, 301016662, 335366499, 365667808, 391049190, + 410689064, 423816593, 429762391, 427948384, 417924213, 399366813, + 372118999, 336184162, 291744393, 239157693, 178963703, 111897394, + 38855262, -39076188, -120670524, -204555997, -289250536, -373165551, + -454647551, -532020754, -603591727, -667697336, -722744462, -767256517, + -799860865, -819376406, -824826855, -815445853, -790753739, -750514716, + -694807177, -624005830, -538798080, -440180087, -329446119, -208181786, + -78228178, 58322457, 199177086, 341868299, 483806041, 622316802, + 754703901, 878291108, 990474654, 1088787157, 1170940744, 1234884629, + 1278855173, 1301396967, 1301443489, 1278304462, 1231723405, 1161876364, + 1069381916, 955316252, 821188818, 668927730, 500846793, 319639136, + 128281798, -69969079, -271643589, -473138138, -670788021, -860891365, + -1039840037, -1204143102, -1350521372, -1475985731, -1577855538, -1653868143, + -1702201550, -1721507212, -1710987200, -1670357852, -1599913631, -1500498908, + -1373524895, -1220918243, -1045119827, -849030441, -635951651, -409549533, + -173768611, 67236847, 309172231, 547685783, 778474962, 997324032, + 1200229451, 1383452973, 1543599999, 1677687708, 1783202582, 1858150469, + 1901102793, 1911207547, 1888235269, 1832550711, 1745137332, 1627554622, + 1481924147, 1310889170, 1117549902, 905412773, 678329960, 440406809, + 195946023, -50663943, -295003900, -532722276, -759616239, -971714214, + -1165343086, -1337202219, -1484407839, -1604573685, -1695818260, -1756812449, + -1786819343, -1785656952, -1753727278, -1692003786, -1601974636, -1485654570, + -1345493052, -1184355718, -1005445656, -812247941, -608443862, -397860620, + -184370436, 28155650, 235957789, 435414812, 623142459, 796038302, + 951331454, 1086646118, 1200032277, 1289966608, 1355409649, 1395802572, + 1411048221, 1401526273, 1368065265, 1311904927, 1234670391, 1138345134, + 1025205425, 897763065, 758730599, 610957451, 457356034, 300870453, + 144392851, -9273089, -157452086, -297686686, -427726193, -545602364, + -649633283, -738464975, -811055534, -866715451, -905106645, -926196810, + -930306878, -918048808, -890310025, -848238628, -793193671, -726739273, + -650563089, -566482249, -476362216, -382121302, -285648791, -188804509, + -93364778, -998556, 86774250, 168572629, 243214304, 309689690, + 367216509, 415201133, 453269644, 481254962, 499186921, 507293573, + 505953135, 495734458, 477315340, 451506885, 419217631, 381418708, + 339130862, 293409409, 245305078, 195852674, 146051406, 96847653, + 49123725, 3659924, -38835925, -77768267, -112648940, -143097159, + -168840927, -189723596, -205688470, -216780356, -223132535, -224963023, + -222563624, -216291546, -206547705, -193779025, -178448674, -161042676, + -142059071, -121973485, -101257674, -80361164, -59693896, -39639548, + -20528876, -2649969, 13743698, 28468278, 41381077, 52378701, + 61415428, 68489302, 73635092, 76919839, 78440949, 78342766, + 76763449, 73871913, 69846273, 64879055, 59156607, 52859212, + 46172815, 39271624, 32308042, 25436123, 18778046, 12454362, + 6550180, 1153222, -3690186, -7931022, -11549357, -14541771, + -16905259, -18670872, -19854696, -20504812, -20665306, -20388254, + -19735543, -18756855, -17510711, -16058137, -14452422, -12750215, + -10996406, -9230387, -7506048, -5841514, -4269596, -2815062, + -1498386, -320570, 699683, 1563959, 2280010, 2842827, + 3263548, 3556194, 3723649, 3782673, 3754077, 3639669, + 3463676, 3234034, 2965500, 2669406, 2362184, 2043555, + 1730398, 1426011, 1139067, 870062, 626287, 406552, + 214758, 52290, -81400, -192447, -276653, -339805, + -380854, -406690, -418638, -414054, -398956, -376437, + -345489, -315906, -280747, -249434, -213100, -176507, + -144467, -112761, -87348, -65472, -45559, -29659, + -17812, -6285, 5913, 11034, 14331, 17585, + 21840, 18597, 16523, 19606, 14800, 12216, + 13421, 8687, 9253, 5262, 7307, 4478, + 87, 917, 3655, 2959, -1190, -1801, + -2753, -174, -122, -410, -480, -795, + -1249, 1770, +}; + +static const int32_t fft_in_imag_512_q31[512] = { + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, +}; + +static const int32_t fft_ref_real_512_q31[512] = { + -45, -91, 39, 13, 39, -12759, + 683966, -8435969, 44965977, -129224628, 221075980, -234823286, + 155575966, -62329239, 13936841, -1458185, 45393, -68, + -11, 94, 12, 28, -47, 61, + 89, -87, -33, 108, -49, -75, + -72, 76, -55, 53, 22, -71, + -24, 83, 42, -17, 71, 73, + 65, -2, 5, 63, -22, 0, + -22, 87, 31, -51, 42, -41, + -5, -51, 46, 32, -65, 92, + -3, 20, -36, -14, -131, -38, + 87, -49, 7, 6, 69, 9, + 26, -43, 11, -80, 145, -61, + 4, -4, -65, 76, 18, 25, + 118, -32, 8, 24, -76, 37, + -41, -23, 110, 126, -66, 55, + 12, -7, 119, -24, 29, -70, + 1, 42, -11, 118, 44, -5, + -28, -2, 33, -101, 36, -67, + 7, 19, -36, -15, 16, 16, + 30, 14, -41, -1, -6, -35, + -23, -50, 77, -11, 10, -28, + 102, 117, 41, 31, 39, 108, + 75, -68, -9, -6, 35, -69, + -52, 37, -81, 30, -76, -50, + 59, 0, 41, -24, 66, 61, + 6, -12, 28, 80, -68, 42, + 21, -62, 1, -103, -21, 7, + 46, 76, -48, 15, -76, 84, + 97, -35, 135, 18, 43, -20, + -22, 42, 54, 49, -48, -16, + -22, 18, -6, -76, 32, -80, + 25, 34, 8, 6, 9, 71, + -1, 97, -137, 52, -9, 0, + 2, 15, 19, -115, -40, 53, + 37, -22, -61, -72, -7, 4, + -41, 4, -44, -28, 16, -12, + -30, 17, 103, -24, 15, 41, + -25, -3, 15, -42, -73, 83, + -4, 49, 2, -96, 58, 85, + -11, -50, -24, -44, -75, -8, + 10, -23, 21, -26, 49, -50, + -40, 15, 87, -18, 58, -18, + 87, 15, -40, -50, 49, -26, + 21, -23, 10, -8, -75, -44, + -24, -50, -11, 85, 58, -96, + 2, 49, -4, 83, -73, -42, + 15, -3, -25, 41, 15, -24, + 103, 17, -30, -12, 16, -28, + -44, 4, -41, 4, -7, -72, + -61, -22, 37, 53, -40, -115, + 19, 15, 2, 0, -9, 52, + -137, 97, -1, 71, 9, 6, + 8, 34, 25, -80, 32, -76, + -6, 18, -22, -16, -48, 49, + 54, 42, -22, -20, 43, 18, + 135, -35, 97, 84, -76, 15, + -48, 76, 46, 7, -21, -103, + 1, -62, 21, 42, -68, 80, + 28, -12, 6, 61, 66, -24, + 41, 0, 59, -50, -76, 30, + -81, 37, -52, -69, 35, -6, + -9, -68, 75, 108, 39, 31, + 41, 117, 102, -28, 10, -11, + 77, -50, -23, -35, -6, -1, + -41, 14, 30, 16, 16, -15, + -36, 19, 7, -67, 36, -101, + 33, -2, -28, -5, 44, 118, + -11, 42, 1, -70, 29, -24, + 119, -7, 12, 55, -66, 126, + 110, -23, -41, 37, -76, 24, + 8, -32, 118, 25, 18, 76, + -65, -4, 4, -61, 145, -80, + 11, -43, 26, 9, 69, 6, + 7, -49, 87, -38, -131, -14, + -36, 20, -3, 92, -65, 32, + 46, -51, -5, -41, 42, -51, + 31, 87, -22, 0, -22, 63, + 5, -2, 65, 73, 71, -17, + 42, 83, -24, -71, 22, 53, + -55, 76, -72, -75, -49, 108, + -33, -87, 89, 61, -47, 28, + 12, 94, -11, -68, 45393, -1458185, + 13936841, -62329239, 155575966, -234823286, 221075980, -129224628, + 44965977, -8435969, 683966, -12759, 39, 13, + 39, -91, +}; + +static const int32_t fft_ref_imag_512_q31[512] = { + 0, 16, -60, 53, -58, -5240, + 284775, -3574190, 19378087, -56632327, 98507314, -106364728, + 71622799, -29159462, 6624575, -704027, 22181, -107, + 40, 52, 20, -56, -24, 12, + 16, -86, -125, 4, -12, 76, + -44, 24, 37, -11, 36, 32, + -10, 83, -45, 12, -22, 42, + -16, -11, 45, 41, 73, 29, + -59, 13, -10, -52, 9, -21, + -69, -51, -41, 13, -9, 102, + 4, 66, 62, -33, 113, -66, + -49, 36, -93, -52, -48, 49, + 56, 71, 1, 36, -56, -60, + -55, 36, -57, -130, 38, 51, + -69, 33, -49, -68, -10, 61, + 6, 29, 56, -41, -5, 18, + 65, 15, 71, 24, 27, -9, + 13, 19, 119, 52, -8, 26, + 60, -36, -5, 29, -25, -5, + -17, 3, 34, 21, -28, -92, + -21, -42, 28, 18, -95, 47, + 71, -4, -1, 47, -16, -27, + -25, 26, 16, 14, -92, 53, + -27, -14, 50, -23, 23, 87, + -45, 49, 156, -12, -49, 1, + 14, -22, -3, -33, -56, -39, + 24, -11, 74, -3, -36, -26, + -10, -5, 62, 48, -2, 58, + -111, 29, -20, 102, 104, -8, + -88, 41, 3, 24, 38, -40, + 79, -96, 11, 14, 23, -8, + 44, 26, -92, 14, 91, 9, + -74, -8, -40, 2, -99, -29, + -44, 10, -3, 49, -43, 11, + -66, -3, -1, 8, -2, 45, + 52, -39, 82, -32, -85, 55, + -59, -25, -58, -78, 34, 70, + -66, 104, -31, -5, 71, -56, + -25, -35, 37, 8, 18, 36, + 35, -15, 70, 86, 0, -54, + -65, -4, 45, 51, -101, 73, + 15, -27, 32, 32, 57, -22, + -3, -55, 46, -60, 0, 60, + -46, 55, 3, 22, -57, -32, + -32, 27, -15, -73, 101, -51, + -45, 4, 65, 54, 0, -86, + -70, 15, -35, -36, -18, -8, + -37, 35, 25, 56, -71, 5, + 31, -104, 66, -70, -34, 78, + 58, 25, 59, -55, 85, 32, + -82, 39, -52, -45, 2, -8, + 1, 3, 66, -11, 43, -49, + 3, -10, 44, 29, 99, -2, + 40, 8, 74, -9, -91, -14, + 92, -26, -44, 8, -23, -14, + -11, 96, -79, 40, -38, -24, + -3, -41, 88, 8, -104, -102, + 20, -29, 111, -58, 2, -48, + -62, 5, 10, 26, 36, 3, + -74, 11, -24, 39, 56, 33, + 3, 22, -14, -1, 49, 12, + -156, -49, 45, -87, -23, 23, + -50, 14, 27, -53, 92, -14, + -16, -26, 25, 27, 16, -47, + 1, 4, -71, -47, 95, -18, + -28, 42, 21, 92, 28, -21, + -34, -3, 17, 5, 25, -29, + 5, 36, -60, -26, 8, -52, + -119, -19, -13, 9, -27, -24, + -71, -15, -65, -18, 5, 41, + -56, -29, -6, -61, 10, 68, + 49, -33, 69, -51, -38, 130, + 57, -36, 55, 60, 56, -36, + -1, -71, -56, -49, 48, 52, + 93, -36, 49, 66, -113, 33, + -62, -66, -4, -102, 9, -13, + 41, 51, 69, 21, -9, 52, + 10, -13, 59, -29, -73, -41, + -45, 11, 16, -42, 22, -12, + 45, -83, 10, -32, -36, 11, + -37, -24, 44, -76, 12, -4, + 125, 86, -16, -12, 24, 56, + -20, -52, -40, 107, -22181, 704027, + -6624575, 29159462, -71622799, 106364728, -98507314, 56632327, + -19378087, 3574190, -284775, 5240, 58, -53, + 60, -16, +}; diff --git a/test/cmocka/src/math/fft/ref_fft_multi_768_32.h b/test/cmocka/src/math/fft/ref_fft_multi_768_32.h new file mode 100644 index 000000000000..e2addddba223 --- /dev/null +++ b/test/cmocka/src/math/fft/ref_fft_multi_768_32.h @@ -0,0 +1,532 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * + * Copyright(c) 2025 Intel Corporation. + */ + +/* Created 20-Nov-2025 16:11:52 with script ref_fft_multi.m v1.9-rc1-6882-ge0b90b605-dirty */ + +#define REF_SOFM_FFT_MULTI_768_NUM_TESTS 1 + +static const int32_t fft_in_real_768_q31[768] = { + -241, 3628, 1266, -2634, 3092, 232, + -640, -2125, -647, -471, 2119, 325, + 924, 1109, 2295, 142, 2343, 4363, + 2796, 852, 2382, 3248, 59, -164, + -472, -531, -1894, -3358, -6065, -6495, + -10815, -14044, -14091, -21234, -21055, -28456, + -27657, -28889, -31938, -37655, -37301, -39941, + -37891, -34378, -35403, -25561, -19647, -12509, + -606, 7543, 27322, 43905, 60616, 82045, + 106206, 131271, 155362, 177405, 206514, 230378, + 252852, 269899, 284703, 292311, 298400, 296951, + 287396, 266368, 239466, 199332, 153113, 89801, + 17743, -67554, -159603, -263913, -379403, -500228, + -624420, -747661, -878313, -1008009, -1123947, -1235504, + -1330305, -1415140, -1469543, -1503109, -1506498, -1472749, + -1404180, -1300722, -1150891, -953777, -717782, -430319, + -101128, 266708, 676619, 1119232, 1588541, 2080078, + 2582718, 3082395, 3581368, 4053897, 4495380, 4890271, + 5223260, 5483176, 5660980, 5732018, 5692000, 5528919, + 5231518, 4798517, 4214949, 3489048, 2614503, 1598896, + 445129, -831444, -2217322, -3689401, -5234758, -6824231, + -8425955, -10013777, -11546286, -12986974, -14303728, -15452171, + -16393147, -17098221, -17516838, -17627470, -17394452, -16795021, + -15807984, -14417914, -12622002, -10422907, -7825330, -4853484, + -1537434, 2089436, 5978600, 10075995, 14311269, 18609861, + 22892297, 27073727, 31067236, 34772158, 38090534, 40934898, + 43202133, 44813294, 45679346, 45731324, 44905126, 43149410, + 40438048, 36750358, 32085432, 26463085, 19925175, 12540171, + 4381317, -4438039, -13790945, -23543065, -33528565, -43564061, + -53465445, -63029646, -72045718, -80312846, -87610680, -93744955, + -98520554, -101753835, -103280411, -102966601, -100709277, -96415613, + -90045652, -81592422, -71092553, -58618157, -44282598, -28246494, + -10713843, 8066777, 27824234, 48231529, 68946383, 89591059, + 109773996, 129088677, 147128854, 163468860, 177716203, 189481150, + 198414424, 204188194, 206522572, 205187796, 200015913, 190891669, + 177791058, 160736722, 139852452, 115319077, 87417331, 56488140, + 22961781, -12682422, -49874998, -88011035, -126426119, -164419626, + -201273383, -236244280, -268603859, -297627612, -322626893, -342948091, + -358004727, -367277860, -370342769, -366852815, -356581652, -339422399, + -315383357, -284601000, -247362998, -204058953, -155232745, -101548024, + -43766850, 17205216, 80403869, 144751837, 209130532, 272363524, + 333253552, 390604777, 443225021, 489983715, 529796603, 561679037, + 584753366, 598271174, 601626791, 594389174, 576296927, 547288978, + 507501344, 457258651, 397112676, 327797845, 250251097, 165585699, + 75093904, -19793649, -117512265, -216400732, -314702204, -410646381, + -502420161, -588236711, -666381398, -735199970, -793172141, -838922144, + -871263019, -889212884, -892024318, -879199280, -850513416, -806023413, + -746080576, -671318723, -582662836, -481304796, -368721547, -246617566, + -116917087, 18270872, 156674321, 295907377, 433516862, 567002905, + 693899018, 811767777, 918300703, 1011331647, 1088889407, 1149227101, + 1190877755, 1212675673, 1213778899, 1193712929, 1152364866, 1090009355, + 1007296111, 905266409, 785326540, 649219884, 499038860, 337158886, + 166206194, -10961531, -191342499, -371803627, -549163002, -720223486, + -881850257, -1031031431, -1164911846, -1280868241, -1376550015, -1449939386, + -1499367541, -1523583693, -1521766543, -1493533215, -1438987495, -1358687436, + -1253679050, -1125438417, -975903567, -807401765, -622633116, -424638658, + -216716665, -2389257, 214658063, 430646136, 641770661, 844277440, + 1034504073, 1208988113, 1364494665, 1498093914, 1607219524, 1689717041, + 1743869703, 1768468592, 1762801849, 1726698298, 1660522023, 1565178648, + 1442100556, 1293212702, 1120923589, 928071098, 717883328, 493909272, + 259976392, 20109529, -221543570, -460775430, -693380588, -915279395, + -1122535646, -1311465176, -1478694764, -1621202263, -1736420964, -1822228493, + -1877035711, -1899782713, -1889981357, -1847719295, -1773655476, -1669011369, + -1535556633, -1375568847, -1191799200, -987421289, -765980565, -531317241, + -287522473, -38826774, 210438051, 455926599, 693378607, 918668739, + 1127898672, 1317450221, 1484066329, 1624902389, 1737561435, 1820169914, + 1871370256, 1890370085, 1876956318, 1831481264, 1754859942, 1648550469, + 1514537726, 1355269389, 1173649163, 972947096, 756747847, 528901760, + 293423150, 54461413, -183819750, -417285653, -641919290, -853887630, + -1049609077, -1225812585, -1379604726, -1508498680, -1610479734, -1684014303, + -1728095431, -1742222985, -1726455301, -1681364668, -1608026287, -1508012075, + -1383349677, -1236465382, -1070165680, -887555137, -691989929, -487024198, + -276323896, -63620915, 147368778, 353015035, 549823704, 734499919, + 904012813, 1055637525, 1186997279, 1296126973, 1381461577, 1441882934, + 1476731983, 1485810836, 1469378480, 1428137473, 1363206291, 1276124718, + 1168776500, 1043382186, 902429921, 748648315, 584934734, 414320933, + 239889131, 64740645, -108075141, -275595744, -435033737, -583772214, + -719463613, -840011127, -943642758, -1028922523, -1094765960, -1140450717, + -1165628503, -1170317758, -1154910358, -1120124363, -1067022676, -996950501, + -911527447, -812600545, -702233328, -582607456, -456037458, -324903799, + -191587971, -58477754, 72134075, 198026348, 317140968, 427596570, + 527697948, 615989474, 691267140, 752573081, 799243835, 830886626, + 847377376, 848875628, 835788248, 808780118, 768740386, 716762333, + 654109652, 582210084, 502593426, 416905102, 326822356, 234056696, + 140318147, 47264446, -43493550, -130454209, -212221899, -287551695, + -355341753, -414661156, -464781140, -505142479, -535375283, -555326382, + -564998467, -564605784, -554520170, -535277315, -507545850, -472137486, + -429952101, -381993665, -329320813, -273037272, -214275690, -154161305, + -93793908, -34261274, 23446178, 78376049, 129678588, 176604769, + 218510346, 254869838, 285274876, 309446449, 327216576, 338561446, + 343550216, 342374595, 335330281, 322798847, 305241328, 283205569, + 257276138, 228087926, 196306163, 162614199, 127698298, 92230529, + 56859824, 22218011, -11122306, -42635429, -71849428, -98358438, + -121834503, -141999690, -158667712, -171721547, -181107624, -186849406, + -189032211, -187801396, -183357131, -175951701, -165863098, -153423152, + -138980699, -122894992, -105545878, -87314275, -68566012, -49677861, + -30984026, -12814682, 4531458, 20798958, 35753250, 49203321, + 60992344, 71008612, 79174265, 85454460, 89847498, 92383097, + 93135114, 92195760, 89684369, 85748781, 80536599, 74229321, + 67008217, 59067898, 50589678, 41764861, 32780594, 23799570, + 14993219, 6509774, -1519081, -8981737, -15771145, -21811536, + -27053959, -31440950, -34961449, -37604251, -39392286, -40339728, + -40496531, -39917823, -38657779, -36799506, -34411495, -31574572, + -28386220, -24923452, -21270576, -17508795, -13717544, -9974397, + -6333739, -2866681, 384131, 3368107, 6055198, 8416649, + 10434463, 12091696, 13399827, 14352349, 14958192, 15241456, + 15221308, 14925295, 14374955, 13608579, 12654643, 11552968, + 10327766, 9020876, 7661805, 6278839, 4907365, 3564212, + 2278189, 1064063, -56710, -1068964, -1969870, -2748235, + -3400263, -3926939, -4334024, -4613344, -4777385, -4836237, + -4799867, -4673215, -4466130, -4199935, -3874971, -3511593, + -3118446, -2704701, -2280034, -1857999, -1445532, -1048462, + -672536, -324455, -6582, 276892, 520368, 728589, + 897649, 1032019, 1126293, 1192973, 1228285, 1231327, + 1206481, 1161397, 1102233, 1026682, 938355, 843020, + 739097, 631951, 530386, 428349, 326932, 240431, + 154170, 73476, 6017, -55869, -103578, -144953, + -178246, -202681, -221485, -230703, -235306, -226956, + -222383, -208635, -196477, -178217, -164594, -144650, + -125017, -105575, -87630, -67991, -51183, -39259, + -23108, -11134, 1244, 9731, 12825, 20491, + 23334, 24167, 26047, 27043, 28682, 26990, + 26946, 22875, 20894, 17822, 18266, 15434, + 12288, 11757, 9451, 4447, 6333, 2560, + 3081, 1489, 1911, 2215, -1287, 197, + 1072, -1622, -3627, -2827, -1107, -1453, + -1069, -4722, 633, 1036, -3478, -2766, + 990, -1236, 1683, -3486, 3291, -2011, +}; + +static const int32_t fft_in_imag_768_q31[768] = { + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, +}; + +static const int32_t fft_ref_real_768_q31[768] = { + 45, 20, -33, 47, 30, -4, + -56, -17, 21, -19, -368, 43718, + -759260, 5010913, -16911518, 33136889, -39906332, 30002948, + -13815282, 3661837, -487124, 23450, -78, 55, + 58, 54, -44, -50, 69, 7, + -8, 3, 9, 37, -38, 92, + 6, 75, 74, -35, 16, -23, + -30, 7, 60, -2, 33, -28, + 35, 5, -27, 48, -6, 36, + 74, 49, 3, -36, -82, -71, + -17, 44, 13, 9, 1, 0, + 15, 116, 24, 13, -22, -83, + -43, -23, 52, -7, -32, 26, + 77, 12, -9, 61, -27, 64, + 14, -20, 0, 28, 38, -32, + -48, 27, -35, -15, -25, -29, + -33, -64, 12, 25, -34, -17, + -26, 62, 57, 47, -53, 45, + 36, -75, -50, -100, -53, 1, + -23, 12, -27, 126, -25, 11, + -13, -23, -54, 30, -8, -4, + 74, 33, -26, -18, 45, -4, + -28, -84, 42, 19, 2, -15, + -27, 27, 42, -49, 68, -51, + 17, 77, -46, 58, 6, 15, + -72, 32, -8, 97, 1, 21, + 47, -67, -98, 1, 6, 16, + 13, 10, 65, 83, -7, 58, + 34, 29, 33, -47, 53, 8, + -33, 25, -34, 61, -22, -34, + 37, 45, 52, 32, 36, 32, + -29, -13, 53, 46, -110, -40, + -76, 32, -18, -32, -46, -5, + 8, -22, -73, -49, -23, -39, + 6, -27, -34, -56, -42, -34, + -24, -18, 48, -19, -12, 115, + -23, 45, -22, -81, -65, 13, + 42, 28, -15, 43, 9, -18, + -98, -21, 4, 21, 37, -35, + -14, -14, 20, -37, -62, 66, + 10, -1, -26, 1, 35, 2, + -25, -91, -9, 26, 23, -39, + 9, 5, -27, 21, -24, 21, + 12, -26, -71, -17, 26, -31, + -23, 1, 107, -94, -17, 12, + -30, -53, 62, 28, 0, 55, + -109, -29, -79, -80, -53, -30, + -61, -57, -47, 59, -44, -19, + 30, 48, -43, 42, -15, 11, + 1, -23, -38, -12, -47, -30, + -50, -3, -20, -20, 68, -42, + -1, -15, 16, -33, 4, -23, + -59, -24, -39, 5, -34, 67, + -40, 14, 105, 41, -40, 38, + -12, -34, 34, 16, -21, 68, + -42, -32, -32, -12, 8, -58, + 29, 2, 39, 76, 57, 35, + -26, -18, -70, -14, 20, -37, + 23, 18, -47, -72, 69, -24, + -6, 41, -1, 8, 25, -41, + -5, 58, 71, 72, -40, 50, + 32, -28, 55, 50, -52, -27, + 44, 38, -23, 18, 38, 9, + 53, -26, 45, 79, -17, 110, + 32, 110, -17, 79, 45, -26, + 53, 9, 38, 18, -23, 38, + 44, -27, -52, 50, 55, -28, + 32, 50, -40, 72, 71, 58, + -5, -41, 25, 8, -1, 41, + -6, -24, 69, -72, -47, 18, + 23, -37, 20, -14, -70, -18, + -26, 35, 57, 76, 39, 2, + 29, -58, 8, -12, -32, -32, + -42, 68, -21, 16, 34, -34, + -12, 38, -40, 41, 105, 14, + -40, 67, -34, 5, -39, -24, + -59, -23, 4, -33, 16, -15, + -1, -42, 68, -20, -20, -3, + -50, -30, -47, -12, -38, -23, + 1, 11, -15, 42, -43, 48, + 30, -19, -44, 59, -47, -57, + -61, -30, -53, -80, -79, -29, + -109, 55, 0, 28, 62, -53, + -30, 12, -17, -94, 107, 1, + -23, -31, 26, -17, -71, -26, + 12, 21, -24, 21, -27, 5, + 9, -39, 23, 26, -9, -91, + -25, 2, 35, 1, -26, -1, + 10, 66, -62, -37, 20, -14, + -14, -35, 37, 21, 4, -21, + -98, -18, 9, 43, -15, 28, + 42, 13, -65, -81, -22, 45, + -23, 115, -12, -19, 48, -18, + -24, -34, -42, -56, -34, -27, + 6, -39, -23, -49, -73, -22, + 8, -5, -46, -32, -18, 32, + -76, -40, -110, 46, 53, -13, + -29, 32, 36, 32, 52, 45, + 37, -34, -22, 61, -34, 25, + -33, 8, 53, -47, 33, 29, + 34, 58, -7, 83, 65, 10, + 13, 16, 6, 1, -98, -67, + 47, 21, 1, 97, -8, 32, + -72, 15, 6, 58, -46, 77, + 17, -51, 68, -49, 42, 27, + -27, -15, 2, 19, 42, -84, + -28, -4, 45, -18, -26, 33, + 74, -4, -8, 30, -54, -23, + -13, 11, -25, 126, -27, 12, + -23, 1, -53, -100, -50, -75, + 36, 45, -53, 47, 57, 62, + -26, -17, -34, 25, 12, -64, + -33, -29, -25, -15, -35, 27, + -48, -32, 38, 28, 0, -20, + 14, 64, -27, 61, -9, 12, + 77, 26, -32, -7, 52, -23, + -43, -83, -22, 13, 24, 116, + 15, 0, 1, 9, 13, 44, + -17, -71, -82, -36, 3, 49, + 74, 36, -6, 48, -27, 5, + 35, -28, 33, -2, 60, 7, + -30, -23, 16, -35, 74, 75, + 6, 92, -38, 37, 9, 3, + -8, 7, 69, -50, -44, 54, + 58, 55, -78, 23450, -487124, 3661837, + -13815282, 30002948, -39906332, 33136889, -16911518, 5010913, + -759260, 43718, -368, -19, 21, -17, + -56, -4, 30, 47, -33, 20, +}; + +static const int32_t fft_ref_imag_768_q31[768] = { + 0, -32, -5, 26, 5, 43, + -33, 33, 46, -24, -1903, 252986, + -4504301, 30493677, -105611719, 212501965, -262975509, 203317701, + -96347654, 26303114, -3606843, 179333, -945, -2, + -33, -11, -91, -18, -5, -11, + -4, 11, 43, -17, 26, 49, + 13, -31, -13, 4, 27, -2, + -1, 66, -18, -35, 34, -14, + 37, -68, 47, 2, -33, 36, + -10, 44, -5, -19, 8, -19, + -26, -44, 65, -29, -73, -29, + -22, -24, 11, -29, 13, -44, + -26, 44, 76, -44, -30, 26, + 3, -45, -18, 105, -113, 82, + -11, 57, -5, -54, -10, -36, + 0, 11, -10, 40, -33, -29, + 7, 34, -19, -11, -114, 57, + 18, 26, -81, -58, 9, -19, + 0, 92, 77, -34, -14, -76, + 35, -55, -9, 43, 7, 2, + 46, -51, 39, -57, 57, -96, + 9, 36, -6, 91, 12, -133, + -27, 38, 14, 52, 16, 12, + 16, -36, 43, -9, -23, 22, + 31, -5, -90, -25, -34, 61, + -49, 22, -56, 26, 39, -25, + -23, -69, -17, -49, 33, -10, + -86, 125, 10, -22, 62, -77, + -28, -96, -11, 68, 17, -26, + 69, 29, -16, -43, 29, 16, + 71, -13, -4, 26, 51, 21, + -43, -28, -30, -55, 26, 19, + 33, -7, -21, 9, -17, -38, + -41, -34, -14, 14, 6, -43, + -27, 70, 30, -53, -31, -11, + -24, -4, -27, 0, -31, -71, + -4, -17, 44, 29, 18, -32, + -11, -15, -12, -2, 2, -24, + -43, 9, -103, 54, -39, -80, + 41, -118, 17, -16, -35, -76, + 11, -34, -9, -58, -18, -20, + 54, 28, 23, -38, -47, -15, + 7, -104, 11, 59, 29, 37, + -29, -37, 16, 0, -4, 26, + 27, -40, 38, 58, -55, 2, + 42, 21, -51, 47, -5, -77, + -1, -29, 63, 14, 30, -35, + -73, 1, 13, 8, 65, -17, + -2, -21, -66, 87, -17, -134, + 9, -66, -11, 66, -27, -28, + 12, 31, -24, 0, -61, 14, + 58, 20, -46, -17, -14, -14, + -12, -47, 12, -63, 27, -21, + 33, -78, 1, 16, -25, -48, + -41, -96, 137, -59, 4, 18, + 43, 32, -33, -21, -13, 16, + 71, -39, -46, 33, 5, -7, + -11, -19, -13, -16, -59, 45, + -26, -24, -70, -39, 2, 12, + 70, 43, -58, 32, 34, -40, + 44, -32, -28, -26, 23, -7, + 64, -63, 24, 37, -17, 7, + -1, 0, -59, -16, 5, -77, + -16, 51, 19, 44, 16, 62, + 0, -62, -16, -44, -19, -51, + 16, 77, -5, 16, 59, 0, + 1, -7, 17, -37, -24, 63, + -64, 7, -23, 26, 28, 32, + -44, 40, -34, -32, 58, -43, + -70, -12, -2, 39, 70, 24, + 26, -45, 59, 16, 13, 19, + 11, 7, -5, -33, 46, 39, + -71, -16, 13, 21, 33, -32, + -43, -18, -4, 59, -137, 96, + 41, 48, 25, -16, -1, 78, + -33, 21, -27, 63, -12, 47, + 12, 14, 14, 17, 46, -20, + -58, -14, 61, 0, 24, -31, + -12, 28, 27, -66, 11, 66, + -9, 134, 17, -87, 66, 21, + 2, 17, -65, -8, -13, -1, + 73, 35, -30, -14, -63, 29, + 1, 77, 5, -47, 51, -21, + -42, -2, 55, -58, -38, 40, + -27, -26, 4, 0, -16, 37, + 29, -37, -29, -59, -11, 104, + -7, 15, 47, 38, -23, -28, + -54, 20, 18, 58, 9, 34, + -11, 76, 35, 16, -17, 118, + -41, 80, 39, -54, 103, -9, + 43, 24, -2, 2, 12, 15, + 11, 32, -18, -29, -44, 17, + 4, 71, 31, 0, 27, 4, + 24, 11, 31, 53, -30, -70, + 27, 43, -6, -14, 14, 34, + 41, 38, 17, -9, 21, 7, + -33, -19, -26, 55, 30, 28, + 43, -21, -51, -26, 4, 13, + -71, -16, -29, 43, 16, -29, + -69, 26, -17, -68, 11, 96, + 28, 77, -62, 22, -10, -125, + 86, 10, -33, 49, 17, 69, + 23, 25, -39, -26, 56, -22, + 49, -61, 34, 25, 90, 5, + -31, -22, 23, 9, -43, 36, + -16, -12, -16, -52, -14, -38, + 27, 133, -12, -91, 6, -36, + -9, 96, -57, 57, -39, 51, + -46, -2, -7, -43, 9, 55, + -35, 76, 14, 34, -77, -92, + 0, 19, -9, 58, 81, -26, + -18, -57, 114, 11, 19, -34, + -7, 29, 33, -40, 10, -11, + 0, 36, 10, 54, 5, -57, + 11, -82, 113, -105, 18, 45, + -3, -26, 30, 44, -76, -44, + 26, 44, -13, 29, -11, 24, + 22, 29, 73, 29, -65, 44, + 26, 19, -8, 19, 5, -44, + 10, -36, 33, -2, -47, 68, + -37, 14, -34, 35, 18, -66, + 1, 2, -27, -4, 13, 31, + -13, -49, -26, 17, -43, -11, + 4, 11, 5, 18, 91, 11, + 33, 2, 945, -179333, 3606843, -26303114, + 96347654, -203317701, 262975509, -212501965, 105611719, -30493677, + 4504301, -252986, 1903, 24, -46, -33, + 33, -43, -5, -26, 5, 32, +}; diff --git a/test/cmocka/src/math/fft/ref_fft_multi_96_32.h b/test/cmocka/src/math/fft/ref_fft_multi_96_32.h new file mode 100644 index 000000000000..48ed4c1e7ae1 --- /dev/null +++ b/test/cmocka/src/math/fft/ref_fft_multi_96_32.h @@ -0,0 +1,84 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * + * Copyright(c) 2025 Intel Corporation. + */ + +/* Created 20-Nov-2025 16:11:52 with script ref_fft_multi.m v1.9-rc1-6882-ge0b90b605-dirty */ + +#define REF_SOFM_FFT_MULTI_96_NUM_TESTS 1 + +static const int32_t fft_in_real_96_q31[96] = { + 2612, -585, 740, 1771, 10242, 28387, + 67673, 150357, 305252, 583434, 1049075, 1773977, + 2866391, 4421878, 6535194, 9264176, 12595638, 16410151, + 20420570, 24132226, 26792322, 27353105, 24457934, 16442487, + 1411524, -22682595, -57886075, -106071598, -168720146, -246662584, + -339873057, -447258396, -566496507, -693969722, -824813628, -953046420, + -1071834144, -1173876250, -1251869944, -1299031192, -1309635449, -1279509181, + -1206486132, -1090677103, -934610659, -743214358, -523566965, -284489188, + -36038274, 211200645, 446740932, 660913160, 845461777, 994029750, + 1102498010, 1169119233, 1194490190, 1181384510, 1134337335, 1059242451, + 962836599, 852157942, 734074206, 614854055, 499863929, 393334033, + 298286546, 216528461, 148768326, 94769100, 53566286, 23675203, + 3333228, -9343610, -16164205, -18786637, -18606500, -16758603, + -14096842, -11214929, -8500848, -6156283, -4266830, -2826418, + -1788630, -1079523, -614752, -331493, -166234, -76988, + -33171, -13273, -4796, -1383, 2439, 1203, +}; + +static const int32_t fft_in_imag_96_q31[96] = { + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, +}; + +static const int32_t fft_ref_real_96_q31[96] = { + -17402140, 14269492, -5584270, -2890773, 4789231, -2333890, + 486956, -35699, 330, 13, 164, 145, + 68, -18, -52, -121, 151, 206, + -21, -66, -30, 131, 0, 78, + -307, 162, -16, -184, 174, -39, + 140, -48, 32, -31, -199, 139, + -187, 137, -72, 272, 156, -11, + -74, 3, 49, 154, -109, 172, + 81, 172, -109, 154, 49, 3, + -74, -11, 156, 272, -72, 137, + -187, 139, -199, -31, 32, -48, + 140, -39, 174, -184, -16, 162, + -307, 78, 0, 131, -30, -66, + -21, 206, 151, -121, -52, -18, + 68, 145, 164, 13, 330, -35699, + 486956, -2333890, 4789231, -2890773, -5584270, 14269492, +}; + +static const int32_t fft_ref_imag_96_q31[96] = { + 0, 179914066, -259234323, 208477801, -102275624, 29288094, + -4318694, 244372, -1937, 98, -53, 13, + -18, 191, 43, 145, 120, 116, + -14, 54, 22, -22, -73, -99, + 113, 41, 26, -65, 99, -120, + -16, 69, -52, -74, 73, -70, + 85, -46, 106, -107, 55, -3, + 13, -45, -98, 68, 77, -85, + 0, 85, -77, -68, 98, 45, + -13, 3, -55, 107, -106, 46, + -85, 70, -73, 74, 52, -69, + 16, 120, -99, 65, -26, -41, + -113, 99, 73, 22, -22, -54, + 14, -116, -120, -145, -43, -191, + 18, -13, 53, -98, 1937, -244372, + 4318694, -29288094, 102275624, -208477801, 259234323, -179914066, +}; diff --git a/test/cmocka/src/math/fft/ref_ifft_multi_1024_32.h b/test/cmocka/src/math/fft/ref_ifft_multi_1024_32.h new file mode 100644 index 000000000000..f9d39f509c2e --- /dev/null +++ b/test/cmocka/src/math/fft/ref_ifft_multi_1024_32.h @@ -0,0 +1,704 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * + * Copyright(c) 2025 Intel Corporation. + */ + +/* Created 20-Nov-2025 16:11:52 with script ref_fft_multi.m v1.9-rc1-6882-ge0b90b605-dirty */ + +#define REF_SOFM_IFFT_MULTI_1024_NUM_TESTS 1 + +static const int32_t ifft_in_real_1024_q31[1024] = { + -25, -79, 39, 48, -13, -34, + -8, -8, -21, -23, 43, -46, + -62, -24, -39, 10, -55147, 1535442, + -13543809, 57130805, -135763817, 195823576, -176199328, 98119764, + -32279401, 5635577, -409793, 5971, 1, 4, + 86, 8, -39, -22, 43, 34, + 22, -65, -65, 9, 49, 17, + 54, 42, -41, 100, 11, -49, + 26, 19, -36, 4, -26, 25, + -30, 19, 27, -30, 78, -61, + -71, 7, 2, 33, 51, -3, + 29, 17, 7, 0, -28, 45, + 17, -31, -72, 44, 14, -9, + 38, 11, 35, 10, 68, 34, + -3, 6, 19, 16, 45, -35, + 48, 21, 28, 27, 18, 41, + 5, 35, -2, -5, -69, 14, + -6, 45, 22, 19, 55, 14, + 13, 40, 5, 4, 11, -30, + 59, 18, 94, 41, -92, 27, + -3, -2, 50, 35, -24, 27, + 44, -40, 35, -28, 49, -30, + -3, 11, -54, -28, 6, 45, + -21, 63, -10, -10, -8, -2, + 24, 10, 50, 4, 21, -29, + -10, 39, -61, 16, -28, -29, + -58, 37, 38, -34, 19, -72, + -30, 9, 45, -36, 32, 23, + 38, 59, -59, -23, -31, -60, + 23, -11, 12, -15, -32, 36, + 15, 48, -39, -33, 81, -58, + -2, -73, 50, -70, 19, 8, + 45, -18, -22, 40, -8, 23, + 18, 42, 18, 80, 38, -1, + 7, -4, 3, 23, 23, -59, + -4, 21, -45, -26, 49, -17, + 61, -16, -17, 32, -8, -13, + 32, 77, -72, 50, -6, 9, + 51, 21, -35, -95, -32, -6, + 26, 17, 4, -56, 0, -94, + 3, -5, 3, 7, -57, -44, + -29, -18, 29, -92, -25, -93, + 41, 4, 5, -38, -1, -64, + -81, -63, -21, -41, -51, 41, + -59, -1, 68, -14, 40, -62, + 47, 73, 9, 25, 57, -18, + 15, -25, -39, -49, 1, 11, + 37, -28, 42, -52, 45, 5, + 47, -40, -10, -8, 45, -1, + -28, -33, 63, -44, 38, 15, + 15, -48, 45, -51, -28, -87, + 7, 10, -63, 25, 29, 48, + -1, -36, 10, -44, 41, 33, + 9, 32, -15, -40, -86, 12, + -84, 34, 46, -12, -5, 54, + -19, -8, -46, -26, -4, -57, + -44, -11, -7, -19, 10, -30, + -1, 39, 9, -17, -22, 35, + -14, -7, 5, -41, 27, -2, + -21, 33, 47, 20, 66, 1, + 23, -54, -4, -34, 2, 33, + 56, -7, 26, 11, 20, -30, + -66, -30, -20, 18, 72, -24, + -37, -20, -70, -21, -48, 64, + -45, 15, -46, 19, -41, -8, + -38, 17, -14, -27, 5, -72, + 34, 36, 29, -5, -11, 24, + -10, 26, -16, 30, 22, -18, + 37, 31, -14, 42, -14, -17, + -10, 29, -14, -80, -2, 26, + -10, -21, -87, 20, 76, 1, + 28, -11, 81, 44, 18, -9, + 12, 53, 40, 14, 8, 18, + 4, -3, -19, -4, -15, -8, + 19, -18, -12, -34, 10, -29, + -56, -10, -18, -59, 45, 19, + 12, 25, -4, 12, 34, -27, + 0, 1, 29, -16, 14, 7, + -41, 15, 26, -21, 24, -30, + -9, -24, 26, 14, 76, 0, + 76, 4, -32, 91, -21, -31, + -6, 8, -45, 21, 69, 44, + -9, 0, -7, -27, -14, 14, + -3, -6, 44, -4, 7, -35, + -40, 32, -18, -17, -40, -21, + -17, 51, 31, 51, -17, -21, + -40, -17, -18, 32, -40, -35, + 7, -4, 44, -6, -3, 14, + -14, -27, -7, 0, -9, 44, + 69, 21, -45, 8, -6, -31, + -21, 91, -32, 4, 76, 0, + 76, 14, 26, -24, -9, -30, + 24, -21, 26, 15, -41, 7, + 14, -16, 29, 1, 0, -27, + 34, 12, -4, 25, 12, 19, + 45, -59, -18, -10, -56, -29, + 10, -34, -12, -18, 19, -8, + -15, -4, -19, -3, 4, 18, + 8, 14, 40, 53, 12, -9, + 18, 44, 81, -11, 28, 1, + 76, 20, -87, -21, -10, 26, + -2, -80, -14, 29, -10, -17, + -14, 42, -14, 31, 37, -18, + 22, 30, -16, 26, -10, 24, + -11, -5, 29, 36, 34, -72, + 5, -27, -14, 17, -38, -8, + -41, 19, -46, 15, -45, 64, + -48, -21, -70, -20, -37, -24, + 72, 18, -20, -30, -66, -30, + 20, 11, 26, -7, 56, 33, + 2, -34, -4, -54, 23, 1, + 66, 20, 47, 33, -21, -2, + 27, -41, 5, -7, -14, 35, + -22, -17, 9, 39, -1, -30, + 10, -19, -7, -11, -44, -57, + -4, -26, -46, -8, -19, 54, + -5, -12, 46, 34, -84, 12, + -86, -40, -15, 32, 9, 33, + 41, -44, 10, -36, -1, 48, + 29, 25, -63, 10, 7, -87, + -28, -51, 45, -48, 15, 15, + 38, -44, 63, -33, -28, -1, + 45, -8, -10, -40, 47, 5, + 45, -52, 42, -28, 37, 11, + 1, -49, -39, -25, 15, -18, + 57, 25, 9, 73, 47, -62, + 40, -14, 68, -1, -59, 41, + -51, -41, -21, -63, -81, -64, + -1, -38, 5, 4, 41, -93, + -25, -92, 29, -18, -29, -44, + -57, 7, 3, -5, 3, -94, + 0, -56, 4, 17, 26, -6, + -32, -95, -35, 21, 51, 9, + -6, 50, -72, 77, 32, -13, + -8, 32, -17, -16, 61, -17, + 49, -26, -45, 21, -4, -59, + 23, 23, 3, -4, 7, -1, + 38, 80, 18, 42, 18, 23, + -8, 40, -22, -18, 45, 8, + 19, -70, 50, -73, -2, -58, + 81, -33, -39, 48, 15, 36, + -32, -15, 12, -11, 23, -60, + -31, -23, -59, 59, 38, 23, + 32, -36, 45, 9, -30, -72, + 19, -34, 38, 37, -58, -29, + -28, 16, -61, 39, -10, -29, + 21, 4, 50, 10, 24, -2, + -8, -10, -10, 63, -21, 45, + 6, -28, -54, 11, -3, -30, + 49, -28, 35, -40, 44, 27, + -24, 35, 50, -2, -3, 27, + -92, 41, 94, 18, 59, -30, + 11, 4, 5, 40, 13, 14, + 55, 19, 22, 45, -6, 14, + -69, -5, -2, 35, 5, 41, + 18, 27, 28, 21, 48, -35, + 45, 16, 19, 6, -3, 34, + 68, 10, 35, 11, 38, -9, + 14, 44, -72, -31, 17, 45, + -28, 0, 7, 17, 29, -3, + 51, 33, 2, 7, -71, -61, + 78, -30, 27, 19, -30, 25, + -26, 4, -36, 19, 26, -49, + 11, 100, -41, 42, 54, 17, + 49, 9, -65, -65, 22, 34, + 43, -22, -39, 8, 86, 4, + 1, 5971, -409793, 5635577, -32279401, 98119764, + -176199328, 195823576, -135763817, 57130805, -13543809, 1535442, + -55147, 10, -39, -24, -62, -46, + 43, -23, -21, -8, -8, -34, + -13, 48, 39, -79, +}; + +static const int32_t ifft_in_imag_1024_q31[1024] = { + 0, -83, 39, 95, 15, 7, + -35, 3, -6, -13, 35, 57, + 28, 60, -67, -79, 50487, -1395762, + 12235510, -51294609, 121144602, -173660953, 155294502, -85945073, + 28099679, -4875536, 352396, -5166, 26, -2, + -73, -42, 38, 16, 22, -70, + 59, -3, -20, 45, 5, -16, + 10, 11, 20, -37, -43, -13, + 25, -25, 28, 39, -27, -7, + 46, -10, 59, 84, 14, -82, + 2, 13, 16, -54, 84, 0, + 50, -8, 13, 59, -28, -33, + 81, -5, 42, -22, -37, -7, + -40, -26, -32, -90, 2, 60, + 17, 38, 19, -58, 29, 17, + 38, 31, -21, -77, -3, 43, + -27, 23, 10, 20, -35, 3, + 13, 16, 22, 36, -42, 0, + 5, 4, -38, 51, -37, -83, + 10, -7, -9, 2, -83, -99, + 7, -10, -32, -40, 26, -8, + 16, 62, -15, 28, -21, 111, + 17, 8, 71, 64, -25, 24, + -18, -18, 36, -2, 2, 42, + -4, -32, 19, -76, 28, -83, + 57, -12, -26, -11, -3, -17, + 51, -36, 33, -66, -1, -28, + 16, -7, -62, 24, -47, -23, + -18, -22, 80, -15, -86, 46, + 59, 11, -11, -20, -23, 35, + -5, 16, 29, -15, -20, -53, + 99, 58, -10, 34, 28, -13, + 23, 10, -42, -61, 52, 31, + -5, 9, 0, -50, -56, -47, + 13, 1, -22, -1, -48, 8, + -2, 34, -26, 39, -54, -9, + -9, -30, 15, 6, 46, -36, + -55, 24, 15, 1, -33, 7, + 0, 23, -19, 35, -15, 43, + 83, -21, -32, -11, -44, -1, + 81, 16, -34, 14, -39, -11, + -65, -17, -9, 42, -10, -4, + 24, -34, -15, -9, -15, -10, + -26, 20, 28, 0, -5, -46, + 7, -71, -48, -35, 1, -4, + 120, -22, 4, 1, -37, -2, + -35, -30, 67, -55, -40, -3, + -104, -17, 24, -2, 22, 44, + -32, -34, 11, 58, 10, -21, + 25, 13, 86, 12, -70, -14, + -26, -40, 3, 28, 26, -36, + 12, 32, -15, 10, 52, 40, + -4, 59, 13, -32, -23, -21, + 2, -54, 28, -27, -12, 14, + 69, -74, 0, 42, -32, 16, + 24, -10, 32, -10, 19, -8, + -14, -3, 50, 6, -33, -56, + 40, 6, -39, -29, 18, 7, + 40, -35, -39, 96, -32, 71, + 63, -32, 1, 49, -75, 60, + -56, 7, -51, -9, -30, 11, + 4, -54, 24, 55, -1, 87, + 56, -55, -4, 27, -20, -92, + -29, 49, 15, -32, 9, -32, + 53, 16, 40, -20, 29, 19, + 31, 21, 37, 19, -40, 76, + 25, 49, -8, 10, 26, 61, + -29, 24, 59, -34, 71, -43, + 42, -16, -5, 4, 4, -56, + -18, -56, 9, 30, -26, 1, + -71, 21, 86, -3, -58, 7, + 80, -38, -10, -16, -2, 18, + -14, -3, 15, 27, -17, -67, + -15, 41, -13, 6, 13, 24, + -25, 39, -24, -12, 16, -14, + 23, 26, 10, 53, -49, -49, + -38, 6, 31, -30, -5, -16, + 16, -14, -31, 60, -42, -7, + 66, 52, 16, -12, -36, -9, + -60, -21, 63, -40, -47, -4, + -73, 16, 75, -46, -41, -12, + -19, -48, 5, -22, 19, 14, + 56, 42, -23, -11, 29, -17, + -68, -31, -19, -4, -46, 16, + 55, 23, 22, -15, -9, -7, + -31, -26, 0, 26, 31, 7, + 9, 15, -22, -23, -55, -16, + 46, 4, 19, 31, 68, 17, + -29, 11, 23, -42, -56, -14, + -19, 22, -5, 48, 19, 12, + 41, 46, -75, -16, 73, 4, + 47, 40, -63, 21, 60, 9, + 36, 12, -16, -52, -66, 7, + 42, -60, 31, 14, -16, 16, + 5, 30, -31, -6, 38, 49, + 49, -53, -10, -26, -23, 14, + -16, 12, 24, -39, 25, -24, + -13, -6, 13, -41, 15, 67, + 17, -27, -15, 3, 14, -18, + 2, 16, 10, 38, -80, -7, + 58, 3, -86, -21, 71, -1, + 26, -30, -9, 56, 18, 56, + -4, -4, 5, 16, -42, 43, + -71, 34, -59, -24, 29, -61, + -26, -10, 8, -49, -25, -76, + 40, -19, -37, -21, -31, -19, + -29, 20, -40, -16, -53, 32, + -9, 32, -15, -49, 29, 92, + 20, -27, 4, 55, -56, -87, + 1, -55, -24, 54, -4, -11, + 30, 9, 51, -7, 56, -60, + 75, -49, -1, 32, -63, -71, + 32, -96, 39, 35, -40, -7, + -18, 29, 39, -6, -40, 56, + 33, -6, -50, 3, 14, 8, + -19, 10, -32, 10, -24, -16, + 32, -42, 0, 74, -69, -14, + 12, 27, -28, 54, -2, 21, + 23, 32, -13, -59, 4, -40, + -52, -10, 15, -32, -12, 36, + -26, -28, -3, 40, 26, 14, + 70, -12, -86, -13, -25, 21, + -10, -58, -11, 34, 32, -44, + -22, 2, -24, 17, 104, 3, + 40, 55, -67, 30, 35, 2, + 37, -1, -4, 22, -120, 4, + -1, 35, 48, 71, -7, 46, + 5, 0, -28, -20, 26, 10, + 15, 9, 15, 34, -24, 4, + 10, -42, 9, 17, 65, 11, + 39, -14, 34, -16, -81, 1, + 44, 11, 32, 21, -83, -43, + 15, -35, 19, -23, 0, -7, + 33, -1, -15, -24, 55, 36, + -46, -6, -15, 30, 9, 9, + 54, -39, 26, -34, 2, -8, + 48, 1, 22, -1, -13, 47, + 56, 50, 0, -9, 5, -31, + -52, 61, 42, -10, -23, 13, + -28, -34, 10, -58, -99, 53, + 20, 15, -29, -16, 5, -35, + 23, 20, 11, -11, -59, -46, + 86, 15, -80, 22, 18, 23, + 47, -24, 62, 7, -16, 28, + 1, 66, -33, 36, -51, 17, + 3, 11, 26, 12, -57, 83, + -28, 76, -19, 32, 4, -42, + -2, 2, -36, 18, 18, -24, + 25, -64, -71, -8, -17, -111, + 21, -28, 15, -62, -16, 8, + -26, 40, 32, 10, -7, 99, + 83, -2, 9, 7, -10, 83, + 37, -51, 38, -4, -5, 0, + 42, -36, -22, -16, -13, -3, + 35, -20, -10, -23, 27, -43, + 3, 77, 21, -31, -38, -17, + -29, 58, -19, -38, -17, -60, + -2, 90, 32, 26, 40, 7, + 37, 22, -42, 5, -81, 33, + 28, -59, -13, 8, -50, 0, + -84, 54, -16, -13, -2, 82, + -14, -84, -59, 10, -46, 7, + 27, -39, -28, 25, -25, 13, + 43, 37, -20, -11, -10, 16, + -5, -45, 20, 3, -59, 70, + -22, -16, -38, 42, 73, 2, + -26, 5166, -352396, 4875536, -28099679, 85945073, + -155294502, 173660953, -121144602, 51294609, -12235510, 1395762, + -50487, 79, 67, -60, -28, -57, + -35, 13, 6, -3, 35, -7, + -15, -95, -39, 83, +}; + +static const int32_t ifft_ref_real_1024_q31[1024] = { + 326, 1146, 2786, -3072, -3316, -605, + -2901, -840, -2671, 664, 2057, 956, + -21, -662, 462, 3641, -2217, 610, + -1321, 238, 2943, 688, 1060, 846, + -3792, -1576, 2121, -1631, -211, -2712, + -2722, -3466, -8597, -7806, -10107, -8978, + -8707, -11131, -9732, -15323, -15254, -13081, + -12623, -10274, -13212, -5985, -4940, -4716, + -1004, 3307, 7423, 11870, 21908, 23159, + 29444, 38649, 46299, 51698, 58474, 61509, + 69988, 71541, 78183, 80345, 79248, 80016, + 77508, 69254, 64701, 54919, 38485, 22397, + 7619, -18671, -39738, -64092, -93406, -121566, + -151206, -181934, -213000, -241011, -269356, -295013, + -318042, -331352, -343862, -348224, -349487, -339135, + -319901, -295323, -258729, -214256, -161875, -98338, + -22320, 61463, 148883, 241596, 343430, 449953, + 553092, 660575, 765348, 863233, 956207, 1031827, + 1098053, 1152642, 1181959, 1190748, 1179948, 1146590, + 1076767, 984062, 860947, 715027, 533899, 321992, + 91170, -167866, -444979, -738889, -1046761, -1361827, + -1677156, -1987696, -2285378, -2563978, -2820773, -3038273, + -3217797, -3348348, -3421990, -3434697, -3385794, -3264787, + -3066741, -2788681, -2438638, -2011383, -1505748, -933663, + -291619, 399962, 1144061, 1925860, 2735503, 3549988, + 4362537, 5151016, 5907267, 6604092, 7230123, 7760787, + 8186022, 8488314, 8646197, 8646598, 8484987, 8152712, + 7635835, 6935136, 6051955, 4991930, 3755510, 2363819, + 826364, -834320, -2599066, -4440124, -6321571, -8216197, + -10077901, -11885570, -13588265, -15155182, -16535940, -17702619, + -18613150, -19234858, -19532980, -19487165, -19075195, -18273148, + -17078708, -15490893, -13506877, -11147624, -8430976, -5382361, + -2046368, 1542253, 5318886, 9227859, 13214381, 17188190, + 21089248, 24838424, 28350006, 31546237, 34346432, 36681320, + 38471965, 39655342, 40176918, 39988511, 39053022, 37340045, + 34844574, 31564156, 27518651, 22736319, 17272291, 11186028, + 4553529, -2522282, -9940434, -17585491, -25318423, -33006825, + -40506944, -47660215, -54327081, -60355027, -65599108, -69911020, + -73182968, -75287952, -76129493, -75629810, -73723931, -70383367, + -65595111, -59372093, -51768179, -42837441, -32692074, -21452167, + -9277743, 3657281, 17156928, 30990380, 44930749, 58723629, + 72104522, 84815781, 96588254, 107170396, 116310129, 123773406, + 129346015, 132845872, 134109268, 133017610, 129484530, 123462360, + 114949234, 103998137, 90691090, 75177605, 57635434, 38303041, + 17446134, -4620199, -27539988, -50941560, -74413943, -97543947, + -119891596, -141022922, -160505079, -177918821, -192864296, -204973947, + -213909668, -219387656, -221170208, -219080741, -212996462, -202883444, + -188761959, -170722219, -148945912, -123689848, -95255466, -64056433, + -30529263, 4794075, 41356921, 78547365, 115714160, 152194199, + 187309917, 220377731, 250731557, 277733009, 300773373, 319311844, + 332844739, 340960776, 343321097, 339692071, 329923185, 313994650, + 291963289, 264028543, 230483055, 191743888, 148332347, 100859462, + 50042902, -3319828, -58361358, -114155510, -169723973, -224083369, + -276218550, -325136386, -369863274, -409475668, -443106446, -469980132, + -489407054, -500821595, -503772467, -497957373, -483223160, -459560044, + -427130807, -386246818, -337395714, -281211712, -218478864, -150120760, + -77192341, -857360, 77633043, 156945573, 235703099, 312492496, + 385896495, 454541831, 517077914, 572237251, 618849440, 655864033, + 682381207, 697662138, 701140517, 692454437, 671455926, 638192497, + 592950953, 536228301, 468743311, 391413562, 305373081, 211917193, + 112515196, 8778457, -97574737, -204722093, -310812643, -413944496, + -512244821, -603865242, -687056465, -760154912, -821653865, -870222658, + -904720479, -924233005, -928095122, -915899426, -887528715, -843128402, + -783143163, -708310042, -619630001, -518370643, -406052045, -284427622, + -155436858, -21195452, 116047980, 253948213, 390107041, 522103241, + 647551921, 764128146, 869637593, 962029890, 1039437023, 1100241539, + 1143083583, 1166892820, 1170916692, 1154740779, 1118299769, 1061881018, + 986122536, 892029173, 780944200, 654513839, 514700066, 363723079, + 204041778, 38296711, -130712875, -300087519, -466886151, -628155008, + -781010506, -922657115, -1050458160, -1161992111, -1255074688, -1327830658, + -1378698781, -1406490452, -1410403952, -1390048744, -1345443160, -1277046282, + -1185733013, -1072792031, -939913170, -789152392, -622900774, -443861303, + -254992312, -59450927, 139443874, 338270624, 533579987, 721937713, + 899993623, 1064543298, 1212581744, 1341359152, 1448429579, 1531710980, + 1589512741, 1620569648, 1624074651, 1599696439, 1547574911, 1468356915, + 1363161501, 1233562136, 1081583751, 909664374, 720608445, 517535818, + 303845742, 83153937, -140773909, -364088220, -582915643, -793431776, + -991930383, -1174877083, -1339004161, -1481332999, -1599238488, -1690519489, + -1753417293, -1786653305, -1789473370, -1761623106, -1703406621, -1615638750, + -1499680365, -1357367601, -1191013457, -1003373006, -797566749, -577067164, + -345613976, -107144347, 134245982, 374403491, 609177711, 834477933, + 1046389454, 1241198812, 1415473145, 1566135863, 1690502316, 1786338339, + 1851906157, 1885975091, 1887878900, 1857497793, 1795262548, 1702180329, + 1579789356, 1430135802, 1255748305, 1059597591, 845017078, 615693842, + 375555802, 128728549, -120541875, -367952417, -609239266, -840247912, + -1056986532, -1255711898, -1433001718, -1585806912, -1711490644, -1807893880, + -1873372869, -1906821270, -1907681888, -1875968599, -1812268342, -1717704175, + -1593954721, -1443189071, -1268052547, -1071604372, -857256046, -628745296, + -390036133, -145250986, 101370068, 345578885, 583188397, 810124441, + 1022511584, 1216750332, 1389559157, 1538037301, 1659724323, 1752624907, + 1815261574, 1846685940, 1846484281, 1814806282, 1752336430, 1660299848, + 1540418887, 1394895267, 1226368506, 1037861164, 832714737, 614557244, + 387207300, 154634175, -79136531, -310076941, -534235098, -747807622, + -947193377, -1129055065, -1290399669, -1428593288, -1541436757, -1627178290, + -1684539978, -1712767863, -1711595900, -1681285479, -1622599120, -1536772665, + -1425517966, -1290963494, -1135616184, -962344542, -774274368, -574781562, + -367391560, -155748026, 56468987, 265617545, 468136455, 660612031, + 839839063, 1002881883, 1147108605, 1270245760, 1370406427, 1446131637, + 1496385859, 1520595225, 1518643671, 1490871745, 1438059084, 1361412752, + 1262534324, 1143392079, 1006288158, 853795822, 688738010, 514100456, + 333016242, 148666198, -35733786, -217015994, -392114684, -558100465, + -712257271, -852104103, -975444447, -1080394328, -1165418355, -1229357540, + -1271415577, -1291209572, -1288731220, -1264358312, -1218861398, -1153343168, + -1069242324, -968305532, -852531316, -724153592, -585580472, -439354359, + -288129099, -134568938, 18639437, 168876066, 313606234, 450448228, + 577184171, 691821653, 792615435, 878069759, 947011876, 998555008, + 1032137571, 1047524066, 1044787870, 1024323798, 986830822, 933272555, + 864888321, 783151052, 689717699, 586439370, 475283364, 358327327, + 237682213, 115515955, -6047289, -124936621, -239160418, -346849610, + -446305192, -535984604, -614569358, -680950643, -734254056, -773856112, + -799382239, -810716482, -807980300, -791555184, -762029195, -720221489, + -667137368, -603957468, -532003241, -452726882, -367667281, -278430831, + -186650850, -93967586, -2010105, 87670605, 173583395, 254348294, + 328705558, 395535680, 453880834, 502971400, 542184178, 571117360, + 589547079, 597424930, 594903224, 582311351, 560135788, 529013782, + 489740139, 443210310, 390428155, 332480838, 270509976, 205696632, + 139248261, 72346401, 6164976, -58182531, -119639865, -177226711, + -230066695, -277391885, -318549814, -353018304, -380398777, -400445720, + -413030907, -418170496, -416006843, -406808812, -390949602, -368911794, + -341285549, -308709477, -271916169, -231679042, -188803111, -144109017, + -98433010, -52605797, -7410253, 36387441, 78074158, 117003911, + 152593644, 184341862, 211838920, 234752163, 252840296, 265961426, + 274066892, 277186709, 275448124, 269055850, 258286481, 243481885, + 225049684, 203441735, 179143029, 152678153, 124588986, 95419225, + 65714533, 36014755, 6833670, -21342804, -48066571, -72929222, + -95564180, -115671966, -133002819, -147362366, -158618696, -166699102, + -171586889, -173327948, -172026559, -167814604, -160894287, -151489663, + -139871117, -126329356, -111186960, -94771411, -77414678, -59467657, + -41266314, -23138140, -5391564, 11671033, 27790902, 42722186, + 56256573, 68223974, 78479099, 86918806, 93483066, 98132973, + 100882481, 101761537, 100840788, 98224915, 94030256, 88408558, + 81520765, 73547285, 64681638, 55113243, 45056283, 34698342, + 24242606, 13871476, 3767927, -5908113, -15006947, -23389971, + -30957637, -37604085, -43265955, -47896929, -51453182, -53942389, + -55361586, -55748217, -55150039, -53619207, -51237913, -48086596, + -44270032, -39882417, -35035095, -29835697, -24398261, -18831626, + -13233531, -7709600, -2359060, 2741076, 7508090, 11883351, + 15802228, 19226824, 22123134, 24464432, 26249325, 27466691, + 28134399, 28273722, 27908768, 27072999, 25812506, 24173126, + 22210833, 19974234, 17521274, 14903269, 12184305, 9415050, + 6651964, 3939391, 1325486, -1151329, -3451045, -5548765, + -7411580, -9028634, -10385842, -11469809, -12283476, -12825585, + -13106189, -13133337, -12930778, -12512723, -11898955, -11114197, + -10181485, -9136535, -7996551, -6790865, -5546526, -4288121, + -3042708, -1831404, -666060, 426453, 1433672, 2346296, + 3151055, 3844528, 4418334, 4867341, 5201684, 5417090, + 5517138, 5510524, 5407103, 5212655, 4939564, 4596810, + 4201640, 3756418, 3276626, 2775443, 2263223, 1749530, + 1245083, 755616, 295067, -137266, -531108, -885778, + -1193909, -1455972, -1667698, -1836140, -1951262, -2025669, + -2058551, -2043942, -1998955, -1921219, -1810497, -1674966, + -1525317, -1355771, -1176953, -994191, -810831, -625797, + -442618, -273791, -112000, 39406, 171237, 287439, + 392918, 476625, 546027, 599266, 631420, 652492, + 661672, 651810, 635639, 603428, 568623, 520399, + 472127, 414285, 362057, 300075, 247514, 186936, + 134315, 82093, 36560, -9730, -45362, -81307, + -109537, -129710, -147427, -160759, -168481, -172912, + -172555, -167998, -163837, -153003, -142682, -133355, + -120121, -102416, -88146, -72605, -59067, -43018, + -30225, -20737, -6450, 2671, 9514, 15270, + 21544, 27571, 31312, 30051, 32554, 35022, + 29866, 33834, 34496, 30956, 25856, 24739, + 21090, 18605, 13885, 11395, 12074, 8208, + 6113, 1491, 2436, -1194, 755, -1141, + -1742, -2584, -3779, -2934, -5882, -4082, + -4822, -4378, -850, -2076, -212, 341, + -1712, -2779, -3499, 1149, -151, 92, + -3132, 333, 1147, -3681, -2353, 1598, + 1786, -536, -1080, -780, 238, -2557, + -1215, -478, 1247, 793, +}; + +static const int32_t ifft_ref_imag_1024_q31[1024] = { + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, +}; diff --git a/test/cmocka/src/math/fft/ref_ifft_multi_1536_32.h b/test/cmocka/src/math/fft/ref_ifft_multi_1536_32.h new file mode 100644 index 000000000000..1a1b0fc65f4a --- /dev/null +++ b/test/cmocka/src/math/fft/ref_ifft_multi_1536_32.h @@ -0,0 +1,1044 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * + * Copyright(c) 2025 Intel Corporation. + */ + +/* Created 20-Nov-2025 16:11:52 with script ref_fft_multi.m v1.9-rc1-6882-ge0b90b605-dirty */ + +#define REF_SOFM_IFFT_MULTI_1536_NUM_TESTS 1 + +static const int32_t ifft_in_real_1536_q31[1536] = { + 99, -4, 22, -33, 37, 83, + 29, 51, 3, 13, -14, -14, + -20, -21, 39, 31, -29, -6, + -26, -52, -4, -9, -9, -9, + 29, -25, -825, 92195, -1546020, 10045581, + -33641397, 65657100, -78882664, 59164046, -27128469, 7128081, + -930292, 42792, -213, 1, 4, 7, + 58, -24, 27, -19, -58, -34, + -1, -41, -4, -16, -22, -20, + -1, 35, 2, 8, 21, -59, + 28, -17, -35, -32, 56, 7, + -45, 10, 19, 26, 13, 7, + -17, 21, -20, 18, 19, -15, + -57, -18, 3, 33, 9, 11, + 9, -17, -5, 34, 23, 55, + -3, -10, 20, -2, 23, -8, + -6, -28, -21, -10, -15, 5, + 4, -21, -58, 16, 24, 45, + -15, -42, 10, -10, -46, -23, + 7, 13, 2, 17, -12, -35, + -31, 17, 30, -5, 1, 11, + 18, -5, 41, -13, -1, 14, + -9, -10, -16, -41, 12, 8, + 21, 13, 91, -53, -38, 19, + 11, 32, 36, 29, -19, 16, + -32, 22, 47, -35, -17, 6, + -1, -2, -34, 27, -12, -7, + -18, 16, -11, 28, 8, 31, + -72, 33, -32, 2, 27, -35, + -26, 2, -35, 9, 33, 1, + -34, -29, -35, 9, 12, -22, + -8, 0, 11, 21, -14, 3, + 41, 11, 27, 8, -14, 7, + -2, -18, 39, 26, -15, 7, + -6, 15, -17, -4, 19, 2, + -33, -11, -27, 0, 37, 45, + -8, 8, 38, 43, -16, -51, + -17, -35, -19, 32, 50, 9, + 2, -23, 36, 8, 34, 41, + -22, -40, 7, 9, -4, 23, + -35, 2, 0, 49, 66, 4, + -9, 18, 2, -25, -30, -21, + 51, -65, -15, -38, 72, 16, + -7, 27, -14, 2, 2, 18, + -45, -18, -17, -10, 17, -63, + -6, 1, -58, -16, -26, -8, + 9, 44, -9, 6, -19, 15, + -48, 28, -12, 51, -17, 42, + 6, -3, 12, -31, 19, -22, + 34, -9, 20, 38, -9, 0, + 40, -14, 31, 39, 74, -19, + -38, 19, 46, -2, -2, 27, + -24, -5, -34, 15, 8, 52, + 36, 53, 20, 31, -6, -4, + 21, -10, -6, 19, 7, -11, + -49, 32, -8, -16, 29, 9, + -6, -3, -2, 20, -7, -13, + 10, -16, -12, -81, -29, -34, + -42, 36, 4, 76, -12, -24, + 40, 39, 8, 9, 33, -8, + 34, 39, 16, 18, -30, -16, + 13, -22, 25, 21, 8, 28, + -17, -18, 2, -42, 40, -44, + 20, -53, -35, 30, 2, 25, + -58, -15, -32, 43, 25, 8, + 7, 6, -34, 27, 21, 56, + -65, 52, 27, -11, 28, 43, + -14, -11, 40, -29, 71, -69, + 28, 0, -11, -57, -33, 17, + 28, -21, -29, 0, -40, 1, + -28, -44, 31, 6, -16, -38, + 1, -41, -17, 46, -10, -21, + 30, 46, 57, -30, 37, -3, + -22, -55, -15, 20, -8, -4, + -29, 13, -11, 11, 3, 5, + -2, 51, -10, -29, -28, -5, + 33, -12, 5, -20, 3, -13, + -9, 7, -25, -38, 33, -13, + -46, -18, -7, -99, -32, -56, + 28, -1, 23, -17, -61, -36, + -14, -20, 17, -14, -11, -19, + 45, -10, 12, -12, -15, 18, + 32, 11, 6, -47, 0, -19, + 49, -23, 40, -36, 35, -19, + 9, -23, -7, -14, -45, 6, + 71, -8, 8, -25, -1, 14, + 39, 39, 61, -3, -42, 20, + 10, 27, -14, 49, -36, 6, + 21, 19, 15, 5, -12, -9, + 15, 6, -45, 19, -43, -4, + 9, 37, 37, 5, -29, 34, + -1, -24, -8, -18, -8, 39, + -19, -1, -12, 54, -36, -14, + 14, -8, 23, -59, -54, -13, + 39, 1, -12, -51, 18, 15, + -14, 5, 28, -15, -30, -5, + 7, 35, 21, -25, -40, -13, + -58, 36, 42, -16, 14, -28, + -50, -17, -10, -20, 2, 7, + -49, 38, -19, -2, -16, 8, + 9, 16, -12, 20, 1, 34, + 38, -9, 43, 31, 30, 0, + -55, -2, 69, 14, -23, 39, + -31, -20, 26, 46, 22, -24, + -1, 29, -24, 24, 6, 34, + -13, 21, 25, -34, 7, 9, + -47, -3, 46, -11, 27, -23, + -13, -21, -24, 5, 9, -49, + -7, 38, -13, 46, 2, 32, + 18, 38, 17, -47, 5, 4, + 8, 5, 34, -11, 26, 14, + 1, -7, 3, 45, -31, 2, + -13, -43, 58, 11, 14, -3, + -57, -28, -23, -5, 28, 42, + -1, 2, -1, 2, 25, -49, + -57, 18, -57, 18, 50, -32, + 26, -3, -23, -6, -60, -28, + 24, -9, 3, 28, 21, -46, + -22, -57, -21, 4, -63, -39, + -46, -51, 2, 20, -35, -47, + -14, 64, -75, 1, -8, 12, + 34, 13, 48, -10, 45, 23, + 12, 12, -6, -61, -45, 3, + 3, 45, -39, 12, 10, -49, + -14, 47, 7, -8, 31, -36, + -45, 9, 5, -2, -2, -8, + 24, -35, 6, -15, -8, 67, + 28, -5, -14, -9, -8, -24, + 131, -24, -8, -9, -14, -5, + 28, 67, -8, -15, 6, -35, + 24, -8, -2, -2, 5, 9, + -45, -36, 31, -8, 7, 47, + -14, -49, 10, 12, -39, 45, + 3, 3, -45, -61, -6, 12, + 12, 23, 45, -10, 48, 13, + 34, 12, -8, 1, -75, 64, + -14, -47, -35, 20, 2, -51, + -46, -39, -63, 4, -21, -57, + -22, -46, 21, 28, 3, -9, + 24, -28, -60, -6, -23, -3, + 26, -32, 50, 18, -57, 18, + -57, -49, 25, 2, -1, 2, + -1, 42, 28, -5, -23, -28, + -57, -3, 14, 11, 58, -43, + -13, 2, -31, 45, 3, -7, + 1, 14, 26, -11, 34, 5, + 8, 4, 5, -47, 17, 38, + 18, 32, 2, 46, -13, 38, + -7, -49, 9, 5, -24, -21, + -13, -23, 27, -11, 46, -3, + -47, 9, 7, -34, 25, 21, + -13, 34, 6, 24, -24, 29, + -1, -24, 22, 46, 26, -20, + -31, 39, -23, 14, 69, -2, + -55, 0, 30, 31, 43, -9, + 38, 34, 1, 20, -12, 16, + 9, 8, -16, -2, -19, 38, + -49, 7, 2, -20, -10, -17, + -50, -28, 14, -16, 42, 36, + -58, -13, -40, -25, 21, 35, + 7, -5, -30, -15, 28, 5, + -14, 15, 18, -51, -12, 1, + 39, -13, -54, -59, 23, -8, + 14, -14, -36, 54, -12, -1, + -19, 39, -8, -18, -8, -24, + -1, 34, -29, 5, 37, 37, + 9, -4, -43, 19, -45, 6, + 15, -9, -12, 5, 15, 19, + 21, 6, -36, 49, -14, 27, + 10, 20, -42, -3, 61, 39, + 39, 14, -1, -25, 8, -8, + 71, 6, -45, -14, -7, -23, + 9, -19, 35, -36, 40, -23, + 49, -19, 0, -47, 6, 11, + 32, 18, -15, -12, 12, -10, + 45, -19, -11, -14, 17, -20, + -14, -36, -61, -17, 23, -1, + 28, -56, -32, -99, -7, -18, + -46, -13, 33, -38, -25, 7, + -9, -13, 3, -20, 5, -12, + 33, -5, -28, -29, -10, 51, + -2, 5, 3, 11, -11, 13, + -29, -4, -8, 20, -15, -55, + -22, -3, 37, -30, 57, 46, + 30, -21, -10, 46, -17, -41, + 1, -38, -16, 6, 31, -44, + -28, 1, -40, 0, -29, -21, + 28, 17, -33, -57, -11, 0, + 28, -69, 71, -29, 40, -11, + -14, 43, 28, -11, 27, 52, + -65, 56, 21, 27, -34, 6, + 7, 8, 25, 43, -32, -15, + -58, 25, 2, 30, -35, -53, + 20, -44, 40, -42, 2, -18, + -17, 28, 8, 21, 25, -22, + 13, -16, -30, 18, 16, 39, + 34, -8, 33, 9, 8, 39, + 40, -24, -12, 76, 4, 36, + -42, -34, -29, -81, -12, -16, + 10, -13, -7, 20, -2, -3, + -6, 9, 29, -16, -8, 32, + -49, -11, 7, 19, -6, -10, + 21, -4, -6, 31, 20, 53, + 36, 52, 8, 15, -34, -5, + -24, 27, -2, -2, 46, 19, + -38, -19, 74, 39, 31, -14, + 40, 0, -9, 38, 20, -9, + 34, -22, 19, -31, 12, -3, + 6, 42, -17, 51, -12, 28, + -48, 15, -19, 6, -9, 44, + 9, -8, -26, -16, -58, 1, + -6, -63, 17, -10, -17, -18, + -45, 18, 2, 2, -14, 27, + -7, 16, 72, -38, -15, -65, + 51, -21, -30, -25, 2, 18, + -9, 4, 66, 49, 0, 2, + -35, 23, -4, 9, 7, -40, + -22, 41, 34, 8, 36, -23, + 2, 9, 50, 32, -19, -35, + -17, -51, -16, 43, 38, 8, + -8, 45, 37, 0, -27, -11, + -33, 2, 19, -4, -17, 15, + -6, 7, -15, 26, 39, -18, + -2, 7, -14, 8, 27, 11, + 41, 3, -14, 21, 11, 0, + -8, -22, 12, 9, -35, -29, + -34, 1, 33, 9, -35, 2, + -26, -35, 27, 2, -32, 33, + -72, 31, 8, 28, -11, 16, + -18, -7, -12, 27, -34, -2, + -1, 6, -17, -35, 47, 22, + -32, 16, -19, 29, 36, 32, + 11, 19, -38, -53, 91, 13, + 21, 8, 12, -41, -16, -10, + -9, 14, -1, -13, 41, -5, + 18, 11, 1, -5, 30, 17, + -31, -35, -12, 17, 2, 13, + 7, -23, -46, -10, 10, -42, + -15, 45, 24, 16, -58, -21, + 4, 5, -15, -10, -21, -28, + -6, -8, 23, -2, 20, -10, + -3, 55, 23, 34, -5, -17, + 9, 11, 9, 33, 3, -18, + -57, -15, 19, 18, -20, 21, + -17, 7, 13, 26, 19, 10, + -45, 7, 56, -32, -35, -17, + 28, -59, 21, 8, 2, 35, + -1, -20, -22, -16, -4, -41, + -1, -34, -58, -19, 27, -24, + 58, 7, 4, 1, -213, 42792, + -930292, 7128081, -27128469, 59164046, -78882664, 65657100, + -33641397, 10045581, -1546020, 92195, -825, -25, + 29, -9, -9, -9, -4, -52, + -26, -6, -29, 31, 39, -21, + -20, -14, -14, 13, 3, 51, + 29, 83, 37, -33, 22, -4, +}; + +static const int32_t ifft_in_imag_1536_q31[1536] = { + 0, 0, 18, -9, 28, 19, + 4, -28, -9, -34, -6, -1, + 0, 31, 9, -9, -65, -34, + 6, 1, -49, -34, -2, 18, + -20, -61, -2523, 286383, -4833354, 31628685, + -106676469, 209692391, -253750715, 191701771, -88543013, 23436058, + -3081460, 142801, -570, -45, -20, -15, + 0, -4, -25, 28, -24, -66, + -10, 11, 37, -2, 10, 5, + 64, -2, 10, -9, 25, 32, + 34, -76, -24, 40, 11, -61, + -57, 0, -18, -22, -30, 32, + -3, -26, 9, 28, 6, -25, + -29, 18, -31, -39, -59, -23, + -14, -71, 33, 9, -12, -61, + 44, -9, 8, 38, 65, 48, + 17, -22, 16, 15, 8, -12, + 36, 16, -55, -24, 13, -22, + -38, -58, 34, -28, 52, -24, + -12, 38, -6, -16, 5, 23, + -11, 21, -21, 11, 52, 0, + -1, 29, -39, 20, 40, 47, + 34, -16, 34, 23, -19, -39, + 14, -28, -10, 5, 23, 22, + 21, 8, -10, 18, -20, 33, + -16, 19, 22, -45, -13, -21, + 6, 23, -19, -18, -15, 15, + -5, -13, 26, -19, -33, 2, + 46, 7, -1, -10, 0, 50, + -4, -34, 23, -3, -36, -76, + -58, 28, 18, 16, 0, 7, + 6, 52, 9, 10, -14, 37, + 43, 28, -21, 37, 16, 4, + -28, -7, -64, -2, 3, -35, + -52, -25, -7, -30, -3, 46, + -24, 27, 64, 17, 2, -23, + 15, -6, -26, 33, 49, 7, + 9, -39, -38, 43, -33, -37, + 58, 16, 48, 23, 34, 24, + -15, -15, 21, 17, -3, -19, + 2, -15, 37, -9, 21, -18, + 26, 89, 39, 29, 1, -17, + 38, 25, -48, -27, -16, 21, + 20, -36, 16, -3, 16, -23, + -8, -34, 29, -62, 47, -39, + -2, 8, -23, -18, -20, 24, + -4, 29, 3, 14, -15, -13, + -36, 21, -15, -29, 15, -13, + -19, 21, -2, 53, -28, 11, + -12, -70, 48, -1, -11, -62, + -18, 7, -14, -2, 22, -40, + 6, -35, 35, 8, 5, -25, + -24, 69, -21, 26, 12, 53, + 21, 17, -3, -42, 39, -7, + 64, 23, -37, 30, -35, 37, + 23, 29, 20, 72, 0, 29, + 49, 38, -76, -39, -63, 33, + -3, -5, -8, 21, -15, -36, + 25, -34, -7, -1, 0, -74, + 15, 5, -9, 59, -12, 3, + -22, 48, -11, -36, -57, 4, + -5, 45, -38, 61, -17, 9, + 67, 11, 7, 22, -10, 13, + -10, 10, 11, -20, -9, 46, + -26, 12, 12, -54, 36, -8, + 7, -27, 24, 2, -26, 9, + -34, -15, 35, 59, 40, 18, + 19, 5, 20, 11, -41, -4, + -54, 50, -30, 19, -28, 3, + 0, 3, -12, -37, -75, 6, + -25, -9, -5, 33, 32, -40, + 38, 33, 21, -11, -24, -15, + 33, -16, -13, 30, -5, 31, + 44, 26, -33, -10, 10, 27, + -14, -2, -76, -6, -12, -29, + -20, -7, 1, 28, 13, -24, + 11, 64, -29, -10, -1, -10, + -30, -61, -10, -8, 23, 16, + -28, 68, -37, -49, -7, 73, + -39, 1, 53, 54, 44, 10, + 8, 14, -19, -6, -10, -6, + 42, -2, -15, 27, -6, -41, + 7, -27, 3, -43, -15, -31, + 15, 2, -17, 18, -37, -1, + -1, 5, 11, 11, -38, 33, + 17, -24, -53, 0, 30, -17, + 39, -20, -9, -6, 26, -18, + -17, 6, 10, -3, -75, -12, + -7, -27, 13, -25, -52, 41, + -19, -1, -78, 7, 23, -15, + 24, 16, -36, -15, -40, 8, + 20, 31, 8, 8, 48, -40, + 4, -29, -2, -1, -14, -56, + -37, -26, -41, 21, -17, -38, + -34, -1, 24, 33, -31, -14, + 8, -23, 6, -51, 28, 2, + 6, 4, 38, 4, -1, -39, + 17, -24, 0, 21, -32, -4, + -22, 6, 23, -67, 11, 31, + 36, 3, 27, 16, 24, -9, + 10, -9, -53, -45, -42, 17, + -24, -1, 15, -1, -8, -11, + -4, -59, 30, -47, -30, -24, + 27, -24, 62, 7, -60, 79, + -6, 2, 47, -20, -5, 41, + -8, 4, 43, -14, 32, -8, + -2, 11, -8, 12, -33, 5, + 24, -17, 8, -44, 37, 28, + -7, 24, -5, 42, -6, -42, + -8, 49, -30, 10, 61, -3, + 23, 48, 25, 45, -43, 4, + -7, -26, -23, -13, 18, -13, + -42, 52, 39, -60, -21, 23, + -71, -35, 9, 3, 1, -8, + 19, 0, -9, 8, -39, 1, + 8, 35, 2, 67, 1, -32, + -11, -16, 32, -16, -78, 14, + 54, -32, -16, 16, 2, -10, + 31, 1, 26, -20, -20, -25, + 1, -13, -16, -64, 0, 45, + -6, -29, 21, 55, 27, 55, + -10, -34, -22, 57, -1, 7, + 2, -6, 14, -9, -28, 13, + 1, 7, 7, 11, -45, -35, + -24, -46, 32, -27, 76, -27, + 4, -14, -7, -37, 24, -8, + -2, 58, 26, 34, -24, 23, + 29, 30, 28, -18, -5, 17, + 0, -17, 5, 18, -28, -30, + -29, -23, 24, -34, -26, -58, + 2, 8, -24, 37, 7, 14, + -4, 27, -76, 27, -32, 46, + 24, 35, 45, -11, -7, -7, + -1, -13, 28, 9, -14, 6, + -2, -7, 1, -57, 22, 34, + 10, -55, -27, -55, -21, 29, + 6, -45, 0, 64, 16, 13, + -1, 25, 20, 20, -26, -1, + -31, 10, -2, -16, 16, 32, + -54, -14, 78, 16, -32, 16, + 11, 32, -1, -67, -2, -35, + -8, -1, 39, -8, 9, 0, + -19, 8, -1, -3, -9, 35, + 71, -23, 21, 60, -39, -52, + 42, 13, -18, 13, 23, 26, + 7, -4, 43, -45, -25, -48, + -23, 3, -61, -10, 30, -49, + 8, 42, 6, -42, 5, -24, + 7, -28, -37, 44, -8, 17, + -24, -5, 33, -12, 8, -11, + 2, 8, -32, 14, -43, -4, + 8, -41, 5, 20, -47, -2, + 6, -79, 60, -7, -62, 24, + -27, 24, 30, 47, -30, 59, + 4, 11, 8, 1, -15, 1, + 24, -17, 42, 45, 53, 9, + -10, 9, -24, -16, -27, -3, + -36, -31, -11, 67, -23, -6, + 22, 4, 32, -21, 0, 24, + -17, 39, 1, -4, -38, -4, + -6, -2, -28, 51, -6, 23, + -8, 14, 31, -33, -24, 1, + 34, 38, 17, -21, 41, 26, + 37, 56, 14, 1, 2, 29, + -4, 40, -48, -8, -8, -31, + -20, -8, 40, 15, 36, -16, + -24, 15, -23, -7, 78, 1, + 19, -41, 52, 25, -13, 27, + 7, 12, 75, 3, -10, -6, + 17, 18, -26, 6, 9, 20, + -39, 17, -30, 0, 53, 24, + -17, -33, 38, -11, -11, -5, + 1, 1, 37, -18, 17, -2, + -15, 31, 15, 43, -3, 27, + -7, 41, 6, -27, 15, 2, + -42, 6, 10, 6, 19, -14, + -8, -10, -44, -54, -53, -1, + 39, -73, 7, 49, 37, -68, + 28, -16, -23, 8, 10, 61, + 30, 10, 1, 10, 29, -64, + -11, 24, -13, -28, -1, 7, + 20, 29, 12, 6, 76, 2, + 14, -27, -10, 10, 33, -26, + -44, -31, 5, -30, 13, 16, + -33, 15, 24, 11, -21, -33, + -38, 40, -32, -33, 5, 9, + 25, -6, 75, 37, 12, -3, + 0, -3, 28, -19, 30, -50, + 54, 4, 41, -11, -20, -5, + -19, -18, -40, -59, -35, 15, + 34, -9, 26, -2, -24, 27, + -7, 8, -36, 54, -12, -12, + 26, -46, 9, 20, -11, -10, + 10, -13, 10, -22, -7, -11, + -67, -9, 17, -61, 38, -45, + 5, -4, 57, 36, 11, -48, + 22, -3, 12, -59, 9, -5, + -15, 74, 0, 1, 7, 34, + -25, 36, 15, -21, 8, 5, + 3, -33, 63, 39, 76, -38, + -49, -29, 0, -72, -20, -29, + -23, -37, 35, -30, 37, -23, + -64, 7, -39, 42, 3, -17, + -21, -53, -12, -26, 21, -69, + 24, 25, -5, -8, -35, 35, + -6, 40, -22, 2, 14, -7, + 18, 62, 11, 1, -48, 70, + 12, -11, 28, -53, 2, -21, + 19, 13, -15, 29, 15, -21, + 36, 13, 15, -14, -3, -29, + 4, -24, 20, 18, 23, -8, + 2, 39, -47, 62, -29, 34, + 8, 23, -16, 3, -16, 36, + -20, -21, 16, 27, 48, -25, + -38, 17, -1, -29, -39, -89, + -26, 18, -21, 9, -37, 15, + -2, 19, 3, -17, -21, 15, + 15, -24, -34, -23, -48, -16, + -58, 37, 33, -43, 38, 39, + -9, -7, -49, -33, 26, 6, + -15, 23, -2, -17, -64, -27, + 24, -46, 3, 30, 7, 25, + 52, 35, -3, 2, 64, 7, + 28, -4, -16, -37, 21, -28, + -43, -37, 14, -10, -9, -52, + -6, -7, 0, -16, -18, -28, + 58, 76, 36, 3, -23, 34, + 4, -50, 0, 10, 1, -7, + -46, -2, 33, 19, -26, 13, + 5, -15, 15, 18, 19, -23, + -6, 21, 13, 45, -22, -19, + 16, -33, 20, -18, 10, -8, + -21, -22, -23, -5, 10, 28, + -14, 39, 19, -23, -34, 16, + -34, -47, -40, -20, 39, -29, + 1, 0, -52, -11, 21, -21, + 11, -23, -5, 16, 6, -38, + 12, 24, -52, 28, -34, 58, + 38, 22, -13, 24, 55, -16, + -36, 12, -8, -15, -16, 22, + -17, -48, -65, -38, -8, 9, + -44, 61, 12, -9, -33, 71, + 14, 23, 59, 39, 31, -18, + 29, 25, -6, -28, -9, 26, + 3, -32, 30, 22, 18, 0, + 57, 61, -11, -40, 24, 76, + -34, -32, -25, 9, -10, 2, + -64, -5, -10, 2, -37, -11, + 10, 66, 24, -28, 25, 4, + 0, 15, 20, 45, 570, -142801, + 3081460, -23436058, 88543013, -191701771, 253750715, -209692391, + 106676469, -31628685, 4833354, -286383, 2523, 61, + 20, -18, 2, 34, 49, -1, + -6, 34, 65, 9, -9, -31, + 0, 1, 6, 34, 9, 28, + -4, -19, -28, 9, -18, 0, +}; + +static const int32_t ifft_ref_real_1536_q31[1536] = { + -208, 1028, -2146, -280, 776, 1608, + 1081, 637, 842, 2274, 792, -115, + 4394, -1464, 1851, 967, -191, -578, + 3180, 1263, 2142, 3402, 586, 3266, + 1202, 1888, -1120, -375, 151, -3604, + -122, 1991, -236, -3107, -2506, -2626, + -4282, -4639, -172, -4654, -4533, -2999, + -15, -5065, -155, -4063, 1261, 721, + 3129, -671, 2662, 2723, 1061, 8426, + 7675, 7229, 12234, 10576, 10304, 13036, + 14434, 17152, 14472, 17043, 18274, 13826, + 13241, 11632, 10311, 8876, 7535, 5023, + 3673, -2123, -7309, -14532, -15667, -19573, + -23310, -31847, -36355, -41917, -44643, -49330, + -54444, -56048, -54924, -55007, -57774, -56197, + -52275, -46488, -40590, -33525, -26331, -14534, + -5315, 9452, 18671, 36735, 52659, 65412, + 79725, 95877, 109849, 123652, 137540, 145200, + 153930, 156815, 164349, 165740, 158411, 156119, + 147951, 133110, 115471, 91519, 70424, 42272, + 11340, -21575, -56827, -94279, -134881, -172055, + -213360, -250037, -284627, -319512, -347581, -372634, + -392288, -406000, -415059, -413205, -406127, -386463, + -366070, -328695, -289256, -232414, -177543, -111152, + -32078, 46657, 132162, 217378, 312255, 401003, + 492486, 578105, 657728, 735235, 800967, 858345, + 901514, 933447, 945826, 945383, 923194, 881957, + 823666, 744892, 650339, 532505, 401713, 249385, + 86826, -91622, -272974, -466517, -662726, -858114, + -1051320, -1236046, -1411686, -1569633, -1705463, -1822427, + -1914305, -1971737, -1997485, -1988663, -1938185, -1857534, + -1732525, -1564796, -1360688, -1122122, -845645, -539384, + -203558, 157006, 530053, 917864, 1312502, 1707075, + 2090197, 2459433, 2801281, 3110743, 3383779, 3607114, + 3778184, 3890401, 3933792, 3914304, 3817403, 3643098, + 3399418, 3073155, 2678505, 2209822, 1678372, 1084276, + 442509, -245337, -960479, -1702598, -2445594, -3185798, + -3906204, -4592220, -5233953, -5810889, -6310512, -6724566, + -7038143, -7235970, -7314842, -7265505, -7076677, -6754784, + -6296018, -5699434, -4969654, -4108742, -3136922, -2059649, + -890244, 353243, 1648727, 2977030, 4314244, 5640043, + 6926146, 8145887, 9277320, 10297821, 11185992, 11903031, + 12448852, 12792884, 12916543, 12821770, 12484853, 11912496, + 11102716, 10052315, 8769831, 7273682, 5584355, 3711104, + 1693775, -447201, -2677090, -4958879, -7247563, -9511744, + -11703465, -13782705, -15702704, -17424542, -18916260, -20127258, + -21032184, -21595619, -21803149, -21624235, -21057140, -20087223, + -18716527, -16953659, -14810811, -12321141, -9503518, -6400778, + -3055652, 480782, 4156075, 7903631, 11669689, 15374606, + 18958814, 22346114, 25474151, 28274296, 30683080, 32639661, + 34096110, 35002560, 35321394, 35025586, 34095720, 32521541, + 30304896, 27471731, 24037163, 20047123, 15547141, 10594808, + 5269683, -351499, -6179407, -12112749, -18058849, -23905328, + -29545744, -34872979, -39781852, -44166629, -47923957, -50978111, + -53236721, -54638099, -55119176, -54643963, -53185424, -50737369, + -47298744, -42907294, -37598036, -31434040, -24498741, -16887429, + -8713123, -94741, 8820257, 17893205, 26963034, 35871054, + 44447851, 52538069, 59975530, 66609377, 72294399, 76892212, + 80292014, 82392141, 83110260, 82389572, 80193927, 76508526, + 71360649, 64780127, 56849853, 47655824, 37328004, 26008147, + 13867342, 1085333, -12120465, -25536048, -38934836, -52068253, + -64705008, -76603397, -87527796, -97258179, -105585075, -112314562, + -117280713, -120338580, -121384153, -120330545, -117127521, -111779505, + -104304143, -94771825, -83291461, -70009055, -55094819, -38774930, + -21294148, -2914662, 16054170, 35299102, 54488431, 73287116, + 91347784, 108331536, 123909356, 137766473, 149616023, 159180965, + 166233868, 170575046, 172064616, 170576428, 166068025, 158531167, + 148009147, 134610545, 118483473, 99844653, 78947786, 56095457, + 31645852, 5973079, -20500963, -47330323, -74052822, -100200161, + -125296303, -148871677, -170474655, -189679049, -206074331, -219306189, + -229059310, -235068628, -237134168, -235123748, -228956546, -218641946, + -204252159, -185934134, -163910290, -138473312, -109988245, -78863628, + -45594336, -10693457, 25250196, 61651254, 97872112, 133279643, + 167232663, 199102751, 228284556, 254194790, 276309629, 294146180, + 307294012, 315412570, 318233653, 315585456, 307396754, 293662441, + 274507454, 250130988, 220841590, 187039402, 149213225, 107920491, + 63808737, 17587427, -29987869, -78118952, -125968080, -172711586, + -217494176, -259499772, -297926602, -332032449, -361127238, -384589906, + -401888592, -412591548, -416368239, -413003605, -402401970, -384597510, + -359738491, -328121730, -290141454, -246339982, -197346613, -143913090, + -86867154, -27145840, 34282217, 96378194, 158069896, 218280353, + 275928983, 329964614, 379369503, 423194148, 460570682, 490705819, + 512949051, 526745521, 531697773, 527540589, 514173891, 491656679, + 460188620, 420159841, 372096451, 316682269, 254737793, 187222598, + 115194822, 39831891, -37626003, -115868772, -193555103, -269324053, + -341819985, -409734768, -471798949, -526832218, -573747752, -611592070, + -639544497, -656948721, -663307112, -658324851, -641879757, -614068853, + -575169661, -525665973, -466233677, -397737653, -321211140, -237834773, + -148953290, -56002184, 39464289, 135838415, 231470672, 324683029, + 413824173, 497281235, 573519966, 641099500, 698711660, 745193260, + 779570707, 801047198, 809057956, 803239996, 783490048, 749925326, + 702910625, 643062665, 571208539, 488421585, 395954303, 295274829, + 187992943, 75867686, -39234688, -155364893, -270526625, -382721173, + -489954333, -590312450, -681948840, -763162502, -832395730, -888280790, + -929662923, -955628164, -965508614, -958905396, -935714163, -896097977, + -840525812, -769729565, -684734615, -586818083, -477494634, -358505054, + -231767635, -99384991, 36447908, 173418741, 309179351, 441372486, + 567665080, 685809850, 793657618, 889220299, 970691248, 1036487321, + 1085290926, 1116036982, 1127985532, 1120690429, 1094052234, 1048304735, + 983994827, 902020022, 803586229, 690197392, 563634712, 425927856, + 279326828, 126252690, -30727602, -188948106, -345698579, -498253663, + -643943557, -780184469, -904517779, -1014672187, -1108602474, -1184506618, + -1240893623, -1276580740, -1290736320, -1282893759, -1252957322, -1201218320, + -1128344799, -1035378921, -923712736, -795091118, -651549487, -495428782, + -329278199, -155867023, 21884624, 200964166, 378296390, 550817084, + 715509842, 869465952, 1009935821, 1134382373, 1240516904, 1326342221, + 1390206083, 1430813800, 1447256973, 1439042403, 1406102963, 1348789380, + 1267880510, 1164559892, 1040430447, 897443767, 737906646, 564425518, + 379875346, 187325754, -9959043, -208628068, -405280556, -596522969, + -779024043, -949576504, -1105159555, -1242986235, -1360557688, -1455707771, + -1526630125, -1571931512, -1590656579, -1582288902, -1546785379, -1484564418, + -1396514081, -1283966465, -1148699380, -992870834, -819030265, -630046527, + -429061869, -219452221, -4767624, 211344570, 425174230, 633047815, + 831345812, 1016614742, 1185594779, 1335289737, 1463018092, 1566456589, + 1643704895, 1693273650, 1714172081, 1705882228, 1668384051, 1602168866, + 1508224931, 1388011642, 1243467936, 1076940537, 891184470, 689290793, + 474641988, 250858324, 21737861, -208816087, -436846641, -658446865, + -869777852, -1067170436, -1247183257, -1406646888, -1542751972, -1653062659, + -1735582597, -1788791763, -1811654306, -1803668814, -1764855539, -1695759994, + -1597456049, -1471530418, -1320042339, -1145497633, -950809867, -739249865, + -514385056, -280041257, -40181383, 201080379, 439624918, 671355509, + 892286064, 1098594389, 1286706868, 1453352441, 1595628823, 1711035463, + 1797521019, 1853538365, 1878059813, 1870600384, 1831206309, 1760492154, + 1659602184, 1530203408, 1374457364, 1194986427, 994818107, 777345237, + 546255918, 305492794, 59165875, -188527458, -433341422, -671079309, + -897668893, -1109214643, -1302074023, -1472937031, -1618848646, -1737290618, + -1826217726, -1884079590, -1909863238, -1903105212, -1863905400, -1792913351, + -1691333260, -1560895247, -1403806747, -1222758514, -1020847804, -801517798, + -568519036, -325840915, -77637046, 171850151, 418353964, 657652106, + 885660159, 1098481666, 1292480155, 1464355516, 1611177051, 1730444441, + 1820150539, 1878772825, 1905343502, 1899434946, 1861181237, 1791267354, + 1690932265, 1561919190, 1406470627, 1227285157, 1027454790, 810422575, + 579933199, 339937735, 94560706, -151998519, -395519648, -631847447, + -856952704, -1067020943, -1258483185, -1428108906, -1573046811, -1690880320, + -1779653406, -1837919413, -1864754559, -1859783641, -1823171173, -1755625833, + -1658386915, -1533198396, -1382281189, -1208279192, -1014238945, -803541923, + -579834919, -346970773, -108971582, 130083337, 366114057, 595094166, + 813132897, 1016558501, 1201938275, 1366176039, 1506542457, 1620740077, + 1706919479, 1763717224, 1790275640, 1786276393, 1751908357, 1687891249, + 1595438012, 1476262486, 1332509163, 1166745659, 981900242, 781222554, + 568211774, 346560641, 120100574, -107289782, -331713541, -549356216, + -756540884, -949779137, -1125851227, -1281843132, -1415192803, -1523754996, + -1605802264, -1660092492, -1685848824, -1682801752, -1651163067, -1591637338, + -1505408041, -1394110256, -1259790097, -1104882900, -932150731, -744661919, + -545709413, -338757446, -127382761, 84765707, 294067250, 496976412, + 690070929, 870113211, 1034134977, 1179439235, 1303678232, 1404885195, + 1481490379, 1532369848, 1556838111, 1554667668, 1526100288, 1471814589, + 1392935146, 1290979951, 1167883446, 1025897621, 867598300, 695803013, + 513556483, 324055689, 130579822, -63530664, -254960711, -440468393, + -616936322, -781437175, -931258704, -1063977475, -1177469159, -1269973586, + -1340088669, -1386818867, -1409579450, -1408193505, -1382898111, -1334344314, + -1263572251, -1171997342, -1061377097, -933764183, -791500540, -637158044, + -473474842, -303339128, -129708618, 44418353, 216071053, 382335233, + 540448165, 687781229, 821938681, 940758749, 1042384718, 1125252993, + 1188147956, 1230202439, 1250926083, 1250188874, 1228226406, 1185647049, + 1123399468, 1042758074, 945299018, 832869534, 707550079, 571621816, + 427526974, 277809022, 125084990, -28007635, -178856734, -324907138, + -463734021, -593052973, -710764745, -815013568, -904171247, -976904534, + -1032170687, -1069237209, -1087700273, -1087470343, -1068778111, -1032176645, + -978516872, -908919323, -824774811, -727708909, -619535342, -502245966, + -377946914, -248858753, -117245647, 14624738, 144489450, 270173067, + 389580624, 500766017, 601940765, 691518395, 768126426, 830640815, + 878191105, 910178934, 926258016, 926396556, 910805951, 879973778, + 834645350, 775805740, 704647514, 622561565, 531103763, 431972981, + 326970096, 217973439, 106899986, -4330812, -113813213, -219710289, + -320272154, -413860814, -498998744, -574348156, -638786776, -691379857, + -731412862, -758411959, -772099815, -772476020, -759732747, -734296127, + -696804018, -648095467, -589173798, -521211860, -445513712, -363500819, + -276669977, -186588833, -94839242, -3023445, 87304541, 174622298, + 257494031, 334584378, 404667892, 466682945, 519706284, 562979312, + 595951179, 618222270, 629608809, 630112956, 619904735, 599360500, + 569008486, 529544706, 481803119, 426750006, 365455529, 299075876, + 228840843, 156016605, 81889836, 7756860, -65127282, -135536293, + -202317721, -264407579, -320823972, -370715300, -413363358, -448173476, + -474700396, -492652588, -501896551, -502436599, -494431331, -478193830, + -454159830, -422893027, -385065038, -341461778, -292933893, -240416823, + -184881223, -127335687, -68805911, -10307038, 47162104, 102643156, + 155228358, 204082912, 248449560, 287665317, 321164977, 348506386, + 369349387, 383472021, 390781163, 391299046, 385158588, 372607442, + 354005897, 329793737, 300515771, 266774740, 229249182, 188658014, + 145771955, 101363895, 56228463, 11154361, -33085315, -75765867, + -116185210, -153712088, -187762879, -217843178, -243525696, -264475607, + -280445626, -291273854, -296909542, -297359668, -292750374, -283274804, + -269211393, -250910393, -228781316, -203300855, -174980799, -144372364, + -112054201, -78621969, -44668588, -10793090, 22429460, 54446787, + 84748044, 112851032, 138327117, 160818678, 180008562, 195655717, + 207575091, 215660629, 219880235, 220245999, 216864942, 209880683, + 199509818, 186014197, 169708472, 150946259, 130109644, 107612785, + 83879303, 59350962, 34464106, 9662772, -14639065, -38039657, + -60160003, -80657250, -99220426, -115593466, -129546162, -140913942, + -149573644, -155442464, -158504321, -158789018, -156361959, -151342314, + -143885404, -134196704, -122497522, -109047150, -94117870, -78022254, + -61057968, -43547261, -25804349, -8129901, 9162869, 25798240, + 41501763, 56035957, 69186490, 80772311, 90633998, 98658424, + 104762522, 108903204, 111057934, 111259018, 109562688, 106046605, + 100834254, 94063360, 85898082, 76517844, 66128784, 54935713, + 43153877, 31006516, 18711362, 6483672, -5469064, -16945311, + -27770754, -37775892, -46818615, -54771112, -61533068, -67026503, + -71200739, -74026465, -75494146, -75627807, -74467061, -72073364, + -68533189, -63937179, -58405371, -52064462, -45039931, -37490040, + -29556752, -21379814, -13124254, -4921292, 3081325, 10761369, + 17992294, 24664939, 30680797, 35969684, 40457241, 44097381, + 46861649, 48722338, 49681754, 49764898, 48992385, 47410889, + 45073356, 42055772, 38424287, 34266273, 29673244, 24744847, + 19568708, 14249149, 8882570, 3560649, -1623890, -6587324, + -11253071, -15553237, -19428909, -22822812, -25697558, -28027913, + -29781633, -30967017, -31573078, -31616950, -31116986, -30105009, + -28612194, -26690950, -24392479, -21758782, -18856741, -15747532, + -12493053, -9151192, -5784785, -2455284, 778428, 3873794, + 6780421, 9450391, 11848188, 13945439, 15721307, 17153486, + 18229833, 18951292, 19317601, 19331829, 19021991, 18391908, + 17477080, 16298631, 14892301, 13286497, 11524410, 9636999, + 7666248, 5643968, 3619164, 1614643, -329494, -2182511, + -3917053, -5510740, -6935551, -8181585, -9230774, -10069505, + -10702745, -11124460, -11330344, -11338179, -11147183, -10771893, + -10229996, -9534288, -8708260, -7772003, -6742108, -5645520, + -4499511, -3330022, -2160619, -1007517, 105958, 1168081, + 2163850, 3069108, 3879602, 4582611, 5175824, 5650631, + 6001619, 6231347, 6348767, 6345832, 6232970, 6020724, + 5711035, 5319768, 4857301, 4330983, 3759052, 3147093, + 2517341, 1869971, 1228320, 593568, -13299, -596644, + -1133926, -1626587, -2061258, -2441661, -2757956, -3009281, + -3195090, -3317069, -3371358, -3369538, -3302704, -3189124, + -3018550, -2810982, -2566460, -2287369, -1982568, -1663786, + -1330037, -993438, -655731, -327480, -11833, 285255, + 559486, 814369, 1035555, 1225864, 1383761, 1511685, + 1599628, 1660692, 1688986, 1684490, 1647570, 1589017, + 1501582, 1394429, 1269741, 1134725, 981075, 823149, + 660159, 493844, 330114, 168814, 14527, -125758, + -257464, -379420, -486743, -577185, -649186, -707958, + -749120, -773688, -786833, -783576, -765253, -738249, + -693765, -642543, -586705, -522856, -450846, -376578, + -301263, -227397, -150470, -82949, -13294, 54367, + 108632, 162347, 208816, 250661, 281292, 304919, + 322777, 332948, 337096, 334473, 326032, 313228, + 295408, 269412, 246699, 215496, 190086, 157695, + 129658, 95562, 61975, 35544, 6711, -22281, + -42869, -59780, -80439, -98610, -110030, -117739, + -124576, -125753, -129948, -128983, -126581, -120301, + -111860, -100762, -94013, -80985, -73587, -61872, + -44448, -35456, -24861, -15228, -2995, 5562, + 13149, 23916, 29909, 35440, 38197, 42010, + 41917, 45141, 44609, 43511, 37540, 35788, + 34531, 31114, 29589, 26982, 24108, 17555, + 14847, 11406, 8428, 3218, 3294, -1756, + -1478, -3498, -8358, -10181, -11537, -11814, + -13888, -11146, -12862, -10178, -12001, -9587, + -8324, -8228, -7163, -2786, -4551, -4214, + -5084, -462, -2985, -2426, -1665, 3391, + 1562, -1694, 4507, 3775, 5534, 2691, + 2039, 5411, 4536, 2785, 1536, 1316, + 3155, 784, -961, 1172, 6, 2598, + 6, -1522, -831, 2692, 3324, -1300, + -1621, -647, -107, 3253, 1955, -1892, + 328, -2424, 701, -926, 497, -2227, + -2074, 2411, -2446, -1601, 854, 603, +}; + +static const int32_t ifft_ref_imag_1536_q31[1536] = { + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, +}; diff --git a/test/cmocka/src/math/fft/ref_ifft_multi_24_32.h b/test/cmocka/src/math/fft/ref_ifft_multi_24_32.h new file mode 100644 index 000000000000..8bcfdd9a5674 --- /dev/null +++ b/test/cmocka/src/math/fft/ref_ifft_multi_24_32.h @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * + * Copyright(c) 2025 Intel Corporation. + */ + +/* Created 20-Nov-2025 16:11:52 with script ref_fft_multi.m v1.9-rc1-6882-ge0b90b605-dirty */ + +#define REF_SOFM_IFFT_MULTI_24_NUM_TESTS 1 + +static const int32_t ifft_in_real_24_q31[24] = { + 482922326, -394845603, 213372960, -73385945, 14855847, -1509706, + 52714, 201, 43, 63, -129, -11, + 417, -11, -129, 63, 43, 201, + 52714, -1509706, 14855847, -73385945, 213372960, -394845603, +}; + +static const int32_t ifft_in_imag_24_q31[24] = { + 0, -45964234, 50459274, -26744935, 7515901, -1010620, + 45488, -203, -258, 455, -501, -248, + 0, 248, 501, -455, 258, 203, + -45488, 1010620, -7515901, 26744935, -50459274, 45964234, +}; + +static const int32_t ifft_ref_real_24_q31[24] = { + 3611, 3470, 104050, 1303039, 8697436, 38649256, + 126241459, 320754897, 656625195, 1107910524, 1563494828, 1861834284, + 1878967615, 1607563569, 1161729935, 703464423, 352140415, 142695560, + 45325407, 10719014, 1734754, 163218, 8211, 1653, +}; + +static const int32_t ifft_ref_imag_24_q31[24] = { + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, +}; diff --git a/test/cmocka/src/math/fft/ref_ifft_multi_256_32.h b/test/cmocka/src/math/fft/ref_ifft_multi_256_32.h new file mode 100644 index 000000000000..edc4d0e6bcf5 --- /dev/null +++ b/test/cmocka/src/math/fft/ref_ifft_multi_256_32.h @@ -0,0 +1,192 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * + * Copyright(c) 2025 Intel Corporation. + */ + +/* Created 20-Nov-2025 16:11:52 with script ref_fft_multi.m v1.9-rc1-6882-ge0b90b605-dirty */ + +#define REF_SOFM_IFFT_MULTI_256_NUM_TESTS 1 + +static const int32_t ifft_in_real_256_q31[256] = { + -104443, 1525281, -13889136, 60179033, -146702250, 217202408, + -201034199, 115601193, -39529650, 7259750, -570251, 9887, + -10, -25, -68, -107, 25, -62, + -37, -112, 27, -50, -122, 55, + 55, -9, -19, -74, 31, 1, + -21, 24, -93, -138, 2, 128, + 31, -108, -62, -21, 153, 60, + -104, 65, 61, 63, 82, 19, + -92, -12, -63, -10, -8, 72, + 6, -41, -35, 2, 172, 109, + 7, 87, -64, -88, 4, 133, + 19, -113, 0, 93, 82, 106, + 17, 44, 38, -76, -19, -96, + 25, 37, 52, -61, -129, 25, + -116, -18, -62, -88, -142, -17, + 88, 52, 70, 8, -36, -104, + 138, 103, 24, -9, 60, 17, + -86, 24, -150, 13, -84, 138, + -114, -4, 17, 65, 0, 9, + 22, 13, -110, -64, 50, 52, + -37, 79, -79, -85, -120, -34, + 76, -81, 60, -81, 76, -34, + -120, -85, -79, 79, -37, 52, + 50, -64, -110, 13, 22, 9, + 0, 65, 17, -4, -114, 138, + -84, 13, -150, 24, -86, 17, + 60, -9, 24, 103, 138, -104, + -36, 8, 70, 52, 88, -17, + -142, -88, -62, -18, -116, 25, + -129, -61, 52, 37, 25, -96, + -19, -76, 38, 44, 17, 106, + 82, 93, 0, -113, 19, 133, + 4, -88, -64, 87, 7, 109, + 172, 2, -35, -41, 6, 72, + -8, -10, -63, -12, -92, 19, + 82, 63, 61, 65, -104, 60, + 153, -21, -62, -108, 31, 128, + 2, -138, -93, 24, -21, 1, + 31, -74, -19, -9, 55, 55, + -122, -50, 27, -112, -37, -62, + 25, -107, -68, -25, -10, 9887, + -570251, 7259750, -39529650, 115601193, -201034199, 217202408, + -146702250, 60179033, -13889136, 1525281, +}; + +static const int32_t ifft_in_imag_256_q31[256] = { + 0, -1104526, 9802224, -41374276, 98231553, -141609308, + 127580058, -71388104, 23746028, -4240611, 323629, -5337, + 32, -60, -129, -15, -27, 102, + 58, -169, -92, 19, 75, 5, + -81, 131, -54, -36, -21, -15, + 118, -72, -77, 65, -42, 118, + -9, -18, 106, 80, -7, 88, + 41, 65, 32, -107, -22, -1, + 67, 45, 7, -109, 47, -25, + -53, -44, -33, -24, 2, 179, + -68, 105, 153, 16, -75, -42, + 11, -113, -44, -22, -63, -25, + 6, 2, 26, -87, 111, 106, + -53, -51, -36, -51, -27, 235, + -114, -20, -69, 10, -97, 83, + -57, -37, -69, -148, -34, 75, + 76, -38, 60, 29, 152, 37, + 141, 35, 52, -103, 96, -39, + -4, 38, -124, 18, 162, -48, + 52, -93, 94, -33, -1, 129, + 54, -27, 16, 64, 44, -43, + -45, 22, 0, -22, 45, 43, + -44, -64, -16, 27, -54, -129, + 1, 33, -94, 93, -52, 48, + -162, -18, 124, -38, 4, 39, + -96, 103, -52, -35, -141, -37, + -152, -29, -60, 38, -76, -75, + 34, 148, 69, 37, 57, -83, + 97, -10, 69, 20, 114, -235, + 27, 51, 36, 51, 53, -106, + -111, 87, -26, -2, -6, 25, + 63, 22, 44, 113, -11, 42, + 75, -16, -153, -105, 68, -179, + -2, 24, 33, 44, 53, 25, + -47, 109, -7, -45, -67, 1, + 22, 107, -32, -65, -41, -88, + 7, -80, -106, 18, 9, -118, + 42, -65, 77, 72, -118, 15, + 21, 36, 54, -131, 81, -5, + -75, -19, 92, 169, -58, -102, + 27, 15, 129, 60, -32, 5337, + -323629, 4240611, -23746028, 71388104, -127580058, 141609308, + -98231553, 41374276, -9802224, 1104526, +}; + +static const int32_t ifft_ref_real_256_q31[256] = { + -1569, -830, -1909, -1488, 3415, 3228, + 2188, 5284, 6862, 8740, 14431, 22319, + 31495, 39307, 50570, 64256, 80688, 97686, + 111687, 123695, 129388, 123769, 106202, 69953, + 5001, -90529, -229211, -419412, -662837, -975167, + -1362616, -1829929, -2378158, -3009308, -3713257, -4488081, + -5304582, -6149738, -6981956, -7751361, -8417790, -8914691, + -9158092, -9075448, -8582552, -7568550, -5957542, -3636565, + -521174, 3464467, 8388636, 14295537, 21192186, 29060893, + 37842039, 47439021, 57690475, 68385421, 79264186, 90000101, + 100216705, 109496356, 117359992, 123303230, 126792865, 127286147, + 124251244, 117173422, 105589837, 89107796, 67418137, 40323152, + 7767654, -30165152, -73203423, -120914317, -172649439, -227578442, + -284684073, -342740508, -400372402, -456051421, -508121756, -554862914, + -594502691, -625273094, -645469812, -653507615, -647957114, -627616785, + -591570790, -539212509, -470319052, -385064318, -284054609, -168344268, + -39430444, 100735569, 249803271, 405020394, 563321689, 721358446, + 875594019, 1022376304, 1158027755, 1278948253, 1381696559, 1463114882, + 1520394842, 1551181119, 1553662138, 1526604042, 1469444535, 1382288632, + 1265978107, 1122031888, 952680234, 760801123, 549852852, 323835818, + 87168988, -155419072, -398954843, -638404904, -868755989, -1085152640, + -1283040981, -1458244948, -1607111733, -1726568155, -1814231913, -1868441101, + -1888300566, -1873720889, -1825365598, -1744688034, -1633837210, -1495617661, + -1333406076, -1151061358, -952804289, -743116848, -526638885, -308020361, + -91818428, 117599868, 316184921, 500269979, 666664268, 812707094, + 936326727, 1036037706, 1110986476, 1160919766, 1186188110, 1187665485, + 1166746402, 1125255573, 1065389168, 989631979, 900676276, 801355102, + 694539705, 583089310, 469753347, 357133296, 247607391, 143289840, + 46010628, -42730106, -121763798, -190243820, -247669626, -293855572, + -328899891, -353164563, -367242673, -371922368, -368136646, -356940054, + -339446602, -316817655, -290210974, -260754391, -229500117, -197441719, + -165470302, -134343018, -104720510, -77121551, -51962267, -29512549, + -9954815, 6654740, 20324394, 31163846, 39337881, 45058213, + 48577520, 50167506, 50120504, 48722648, 46250707, 42973219, + 39131997, 34949970, 30607270, 26272771, 22078954, 18121182, + 14480497, 11206366, 8326540, 5845185, 3759796, 2057048, + 705551, -326115, -1083775, -1602381, -1917835, -2073023, + -2101305, -2038436, -1903566, -1728329, -1527768, -1319424, + -1111624, -920902, -739460, -586382, -452219, -337529, + -243069, -171574, -119157, -73001, -40498, -22362, + -7395, -1426, 8844, 10381, 5185, 10614, + 6251, 4908, 6476, 3273, 608, 2586, + -939, 517, -2100, 59, +}; + +static const int32_t ifft_ref_imag_256_q31[256] = { + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, +}; diff --git a/test/cmocka/src/math/fft/ref_ifft_multi_3072_32.h b/test/cmocka/src/math/fft/ref_ifft_multi_3072_32.h new file mode 100644 index 000000000000..9fc9c64978d3 --- /dev/null +++ b/test/cmocka/src/math/fft/ref_ifft_multi_3072_32.h @@ -0,0 +1,2068 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * + * Copyright(c) 2025 Intel Corporation. + */ + +/* Created 20-Nov-2025 16:11:53 with script ref_fft_multi.m v1.9-rc1-6882-ge0b90b605-dirty */ + +#define REF_SOFM_IFFT_MULTI_3072_NUM_TESTS 1 + +static const int32_t ifft_in_real_3072_q31[3072] = { + -59, -1, -55, 24, -14, 0, + -14, -29, -16, 30, -13, 24, + 23, -8, -29, -3, -6, -29, + -25, -14, -20, 1, -8, 2, + 7, -21, -15, 18, -56, 0, + -26, 43, -5, 7, 42, -26, + -13, -5, -24, -8, -11, -27, + 20, -6, -4, -1, 14, -21, + -23, 31, 4, -7, 21, 31, + 51, -20, -8, -5, -2784, 237345, + -3580001, 21781536, -69391859, 129763784, -149772281, 107836784, + -47262139, 11753785, -1421192, 57131, -155, -28, + -24, 6, -19, 20, -3, 7, + -19, -9, -52, 28, -31, 44, + -14, 8, -3, -37, 7, -25, + 13, 29, 2, 70, 13, 16, + -4, 40, -34, 7, -23, 26, + -20, 9, 21, 6, -31, -27, + -19, 12, 20, -15, 15, -3, + -34, 34, -24, 39, 62, 3, + -38, 21, -21, 39, 2, -4, + 8, 9, -25, -17, 35, 14, + -4, 13, 30, -13, 7, 18, + 22, -27, 8, -34, -15, 5, + 0, 18, -26, -22, 16, 6, + -19, -15, -12, -19, 11, 25, + -6, 6, -1, 40, 24, -17, + 0, -30, 2, -3, 15, 6, + 32, -2, -6, 8, -44, -28, + -11, -17, -16, 24, -4, 9, + 11, 22, -16, 9, 13, -10, + 11, -31, 10, -39, -9, 3, + 24, 7, -32, 31, -9, -22, + -17, 7, -17, 22, 8, -11, + 0, 6, 20, 22, -25, 18, + 1, 6, -21, -8, -1, -2, + 22, -1, -30, 13, -8, -21, + -21, -22, 15, -17, 40, -15, + -21, 7, -6, 9, 37, -3, + 13, -10, -52, 31, -8, 23, + 39, 14, -4, 5, -9, -38, + -9, -4, 10, 2, 42, -27, + 2, 28, -15, -16, -22, 17, + 0, -15, 14, -11, 4, -17, + -24, 4, 6, -15, -2, 40, + 7, 1, 22, -2, 0, -10, + -2, 31, -5, 20, -12, -6, + 0, 12, 1, -35, 7, -26, + 11, -47, 3, -34, 23, 1, + -8, 0, 11, 18, 26, -1, + -31, 23, 10, -17, -12, 7, + 5, -21, 19, 10, -14, 11, + 12, -10, 49, 0, 5, -16, + -9, 24, 11, 21, -29, -15, + -31, -21, -5, 46, -25, 1, + 62, -18, -32, -8, 4, 5, + -22, -27, 38, -15, 19, 3, + -22, 8, 16, -27, 18, -31, + -25, -22, 23, -34, 1, 39, + 1, -11, 2, 40, 19, 29, + -29, -3, 4, 33, -26, 1, + 15, 3, -8, 65, 29, 3, + -7, 26, -25, 10, -4, 14, + 40, -30, 1, 11, 5, 17, + 9, 8, -6, -13, 18, 27, + -1, -25, 15, 85, -20, 6, + -32, -29, 31, 13, -17, -4, + 9, -3, 8, 11, -11, -38, + -26, 7, -19, 13, 28, 32, + 20, 1, -8, 11, 13, 1, + 8, 20, 40, 2, -41, -7, + -13, 6, 0, 45, -8, 26, + 13, 9, 7, 31, -19, -28, + -18, -19, 10, 13, -5, -32, + 18, -26, -23, 19, -23, -3, + -7, -9, 4, 3, -12, 7, + 14, -14, 29, 22, 0, 63, + -6, 5, -24, -13, 7, 34, + -9, 9, 9, 21, 4, 7, + 3, 17, 31, -27, -38, -47, + 17, 12, -11, -14, 15, -10, + -3, 22, 11, 14, 2, -16, + 23, 5, -48, -18, -21, 38, + -23, 8, -7, -19, 31, 29, + -14, -7, 22, 6, 38, 1, + 22, 24, -9, -2, 20, -70, + 21, 20, -42, -1, -39, -7, + -7, 26, -57, -18, 28, 22, + -4, 11, -13, 12, -16, -5, + -9, -5, 9, 3, 4, -38, + -2, -2, -20, 4, -34, -21, + 15, -54, -8, -47, -17, 25, + -26, 20, 6, 12, -20, 21, + -10, -7, 32, -26, 16, -3, + -9, 1, -3, -3, 6, -9, + -11, 18, 0, -23, -17, 1, + 1, -13, -9, -1, 7, 8, + -16, -7, 19, 15, -38, 4, + 21, 20, 3, -1, 5, -2, + -7, 25, -66, 41, -9, -20, + -17, -22, 11, -16, 4, 17, + 6, 22, -20, 14, -4, -2, + 22, -5, 32, 16, 36, -1, + -6, 0, -20, 8, -2, 14, + -29, -2, 6, 14, -4, 9, + 23, -4, 4, -21, -11, -20, + -13, -22, -30, 14, -18, 39, + 27, -24, 24, -19, 24, 29, + 1, -29, 14, 22, -2, -4, + -34, -26, 7, 24, -22, 4, + 24, -19, -35, 11, -4, 10, + -12, -21, 22, 7, 14, 17, + 8, 11, 9, 23, 3, -15, + -6, 20, -11, -17, -28, 33, + -6, 2, -12, -19, -13, -18, + -7, 45, 18, -20, -1, 2, + 15, -1, 37, 9, -15, -12, + 23, 10, 0, 29, -23, 42, + -15, 7, -22, -9, 2, 6, + -30, -26, 56, -18, 2, -1, + -20, -25, 30, 25, -15, 29, + 20, -11, -25, 29, 4, -26, + 9, -8, -14, 29, 13, 4, + -29, 2, -5, -12, 3, 27, + -9, -13, 42, 10, -6, -18, + -6, -3, -26, -40, -8, -11, + 17, -3, 10, -23, -24, -8, + -30, -8, -6, 18, 30, -6, + 24, -1, 3, -1, -35, 9, + -10, -6, -20, -2, -32, 15, + -48, 14, 19, -15, -10, -22, + -19, -26, -59, 3, -3, 39, + 4, 1, 16, 38, -25, 17, + 10, 57, 28, 5, 31, 19, + 2, 18, 16, -26, -6, 28, + -54, -12, -12, -12, -20, 1, + 21, 22, -22, -4, -11, 26, + -1, 0, -2, -8, 13, 35, + 30, -28, -10, -3, -15, -45, + -4, 3, -6, 23, 42, 2, + 29, -35, 45, 0, 2, 6, + -15, 16, -5, 0, -11, 4, + 22, -20, 36, 37, -14, 10, + 6, 5, 19, 35, -3, 44, + -22, -13, -22, 5, 15, 14, + -33, 9, -23, -17, 13, -9, + -17, 9, -25, -14, -17, 31, + 3, -4, -27, -16, 25, 1, + 16, -34, 6, -21, -15, 16, + -25, 3, 23, 17, -4, 14, + 8, -10, 31, 9, 21, 5, + 6, 14, 14, 20, -25, -3, + 12, 2, 37, -25, -14, -1, + -17, -19, -14, 0, 14, 18, + 24, -42, 22, -5, -12, -13, + -21, -1, -31, 25, -17, -38, + -10, -21, -18, 33, 19, 20, + -16, -7, -27, -9, -10, -11, + 17, 9, 10, -33, 23, -23, + 13, 17, -8, 2, -19, -9, + 18, -4, 31, 11, -21, -2, + 2, -2, -11, -15, 12, -20, + 0, 11, -3, -22, -6, 6, + 12, 13, 19, -1, 16, 21, + 11, 1, 7, 9, -31, 29, + 6, -12, -27, -41, -21, 16, + 23, 17, -16, -52, 12, 63, + -6, -37, 26, -16, 52, 28, + -32, -12, -4, 10, -46, 12, + -17, -5, 72, 10, 43, -9, + -27, 43, 14, 26, -39, -16, + 13, -3, -23, -4, 11, 9, + -2, -22, 8, 26, -5, 13, + -18, -9, -18, 5, 29, -35, + 3, 23, 21, 17, -18, -40, + 43, -38, 18, -3, -7, 28, + -1, 27, -15, -31, -30, 2, + 9, -18, -3, -21, -10, -13, + -26, 5, -6, 18, 32, 41, + 11, -1, -4, 14, 22, -39, + 2, -20, -17, 22, -8, 2, + 20, 32, -1, -9, 3, 1, + -34, -8, -20, 3, -18, 12, + 13, -10, -26, -45, 15, -32, + -12, 0, -38, -2, 0, -6, + 10, 1, -17, -1, 17, 15, + -7, -29, 9, 17, -10, -6, + -33, -8, -11, -4, 37, 11, + 28, 9, 0, 3, 18, -24, + -36, -8, -9, 38, 16, 47, + -19, -9, -22, 24, 22, -22, + 10, 40, 2, -10, 1, 3, + -17, 2, -22, -7, 33, -17, + -9, 18, 37, 6, 21, -23, + 17, -30, -54, -9, 31, 13, + -7, -23, 30, 2, 47, 21, + 28, -34, 3, 32, -40, -7, + 27, 8, 33, -28, -14, 48, + -21, -57, 38, -14, -40, 1, + -9, -21, 3, 24, 6, 19, + 10, 1, 9, 12, -15, 3, + 9, 21, -2, 8, 6, -51, + 10, 16, -25, 15, 13, 18, + -8, 20, -24, -19, 10, 11, + -17, -15, -14, 47, 9, 14, + 20, -24, -10, 17, -4, 7, + -8, 9, -19, 5, 6, 27, + 7, 36, 26, -22, -4, -30, + -19, -3, -30, 33, 7, -31, + 23, 7, -34, 23, 31, -20, + 8, 33, 31, 8, -1, -1, + -39, 19, -22, -50, -27, -23, + -13, 18, -8, -5, -1, 8, + 25, 14, 7, -29, 21, 0, + 28, 35, 26, 18, 7, -1, + -18, -25, 33, -23, -5, -28, + -37, 28, -1, -13, 5, -18, + -11, -8, 1, -18, -4, 1, + 2, -20, 8, -23, -21, 31, + 20, 12, -4, -5, -4, -10, + 9, -41, -12, 32, 30, -5, + -30, 1, -23, -11, 22, -21, + 30, -14, -29, -15, 23, 0, + -6, 7, -15, -18, 26, -3, + 5, -3, -2, 0, 26, 36, + -17, -6, 14, 0, 3, -19, + 1, 23, 20, 6, 15, -8, + -33, -15, -26, -14, 10, -23, + -25, 11, 0, -18, -4, -2, + -3, -11, 21, -40, 15, 19, + -10, 21, 12, -44, -21, 16, + 22, 8, -18, -13, -12, -8, + 0, -52, -4, -9, -20, -3, + 16, 21, -16, -35, -23, 4, + 11, -18, 10, 3, -26, -34, + -24, -33, -6, -28, -21, 57, + 0, 22, 9, -31, 8, -9, + -4, 24, 22, 22, 19, -10, + 17, 38, 43, 10, 27, 21, + 7, 8, 3, -57, -9, -36, + 3, -13, 3, 18, 34, 23, + -22, -31, -8, -3, 24, 28, + -48, -24, 25, -13, 2, 9, + 6, -22, 4, -46, -30, 9, + -13, -4, 9, -24, -2, -21, + -20, 1, -36, 0, -23, -21, + -37, -21, 3, 17, 4, -34, + 5, -36, -13, -4, -25, -18, + 20, -40, -2, -26, -9, -7, + -4, -34, -16, -26, 22, -12, + -17, -24, -40, -3, -26, 37, + -14, 23, -8, 0, -35, 2, + 11, 5, -7, -11, 54, 9, + -25, 26, -59, 0, -5, 40, + -13, -6, -12, 43, 11, 9, + -25, -16, -47, -28, 6, -3, + -27, -3, 6, -28, -47, -16, + -25, 9, 11, 43, -12, -6, + -13, 40, -5, 0, -59, 26, + -25, 9, 54, -11, -7, 5, + 11, 2, -35, 0, -8, 23, + -14, 37, -26, -3, -40, -24, + -17, -12, 22, -26, -16, -34, + -4, -7, -9, -26, -2, -40, + 20, -18, -25, -4, -13, -36, + 5, -34, 4, 17, 3, -21, + -37, -21, -23, 0, -36, 1, + -20, -21, -2, -24, 9, -4, + -13, 9, -30, -46, 4, -22, + 6, 9, 2, -13, 25, -24, + -48, 28, 24, -3, -8, -31, + -22, 23, 34, 18, 3, -13, + 3, -36, -9, -57, 3, 8, + 7, 21, 27, 10, 43, 38, + 17, -10, 19, 22, 22, 24, + -4, -9, 8, -31, 9, 22, + 0, 57, -21, -28, -6, -33, + -24, -34, -26, 3, 10, -18, + 11, 4, -23, -35, -16, 21, + 16, -3, -20, -9, -4, -52, + 0, -8, -12, -13, -18, 8, + 22, 16, -21, -44, 12, 21, + -10, 19, 15, -40, 21, -11, + -3, -2, -4, -18, 0, 11, + -25, -23, 10, -14, -26, -15, + -33, -8, 15, 6, 20, 23, + 1, -19, 3, 0, 14, -6, + -17, 36, 26, 0, -2, -3, + 5, -3, 26, -18, -15, 7, + -6, 0, 23, -15, -29, -14, + 30, -21, 22, -11, -23, 1, + -30, -5, 30, 32, -12, -41, + 9, -10, -4, -5, -4, 12, + 20, 31, -21, -23, 8, -20, + 2, 1, -4, -18, 1, -8, + -11, -18, 5, -13, -1, 28, + -37, -28, -5, -23, 33, -25, + -18, -1, 7, 18, 26, 35, + 28, 0, 21, -29, 7, 14, + 25, 8, -1, -5, -8, 18, + -13, -23, -27, -50, -22, 19, + -39, -1, -1, 8, 31, 33, + 8, -20, 31, 23, -34, 7, + 23, -31, 7, 33, -30, -3, + -19, -30, -4, -22, 26, 36, + 7, 27, 6, 5, -19, 9, + -8, 7, -4, 17, -10, -24, + 20, 14, 9, 47, -14, -15, + -17, 11, 10, -19, -24, 20, + -8, 18, 13, 15, -25, 16, + 10, -51, 6, 8, -2, 21, + 9, 3, -15, 12, 9, 1, + 10, 19, 6, 24, 3, -21, + -9, 1, -40, -14, 38, -57, + -21, 48, -14, -28, 33, 8, + 27, -7, -40, 32, 3, -34, + 28, 21, 47, 2, 30, -23, + -7, 13, 31, -9, -54, -30, + 17, -23, 21, 6, 37, 18, + -9, -17, 33, -7, -22, 2, + -17, 3, 1, -10, 2, 40, + 10, -22, 22, 24, -22, -9, + -19, 47, 16, 38, -9, -8, + -36, -24, 18, 3, 0, 9, + 28, 11, 37, -4, -11, -8, + -33, -6, -10, 17, 9, -29, + -7, 15, 17, -1, -17, 1, + 10, -6, 0, -2, -38, 0, + -12, -32, 15, -45, -26, -10, + 13, 12, -18, 3, -20, -8, + -34, 1, 3, -9, -1, 32, + 20, 2, -8, 22, -17, -20, + 2, -39, 22, 14, -4, -1, + 11, 41, 32, 18, -6, 5, + -26, -13, -10, -21, -3, -18, + 9, 2, -30, -31, -15, 27, + -1, 28, -7, -3, 18, -38, + 43, -40, -18, 17, 21, 23, + 3, -35, 29, 5, -18, -9, + -18, 13, -5, 26, 8, -22, + -2, 9, 11, -4, -23, -3, + 13, -16, -39, 26, 14, 43, + -27, -9, 43, 10, 72, -5, + -17, 12, -46, 10, -4, -12, + -32, 28, 52, -16, 26, -37, + -6, 63, 12, -52, -16, 17, + 23, 16, -21, -41, -27, -12, + 6, 29, -31, 9, 7, 1, + 11, 21, 16, -1, 19, 13, + 12, 6, -6, -22, -3, 11, + 0, -20, 12, -15, -11, -2, + 2, -2, -21, 11, 31, -4, + 18, -9, -19, 2, -8, 17, + 13, -23, 23, -33, 10, 9, + 17, -11, -10, -9, -27, -7, + -16, 20, 19, 33, -18, -21, + -10, -38, -17, 25, -31, -1, + -21, -13, -12, -5, 22, -42, + 24, 18, 14, 0, -14, -19, + -17, -1, -14, -25, 37, 2, + 12, -3, -25, 20, 14, 14, + 6, 5, 21, 9, 31, -10, + 8, 14, -4, 17, 23, 3, + -25, 16, -15, -21, 6, -34, + 16, 1, 25, -16, -27, -4, + 3, 31, -17, -14, -25, 9, + -17, -9, 13, -17, -23, 9, + -33, 14, 15, 5, -22, -13, + -22, 44, -3, 35, 19, 5, + 6, 10, -14, 37, 36, -20, + 22, 4, -11, 0, -5, 16, + -15, 6, 2, 0, 45, -35, + 29, 2, 42, 23, -6, 3, + -4, -45, -15, -3, -10, -28, + 30, 35, 13, -8, -2, 0, + -1, 26, -11, -4, -22, 22, + 21, 1, -20, -12, -12, -12, + -54, 28, -6, -26, 16, 18, + 2, 19, 31, 5, 28, 57, + 10, 17, -25, 38, 16, 1, + 4, 39, -3, 3, -59, -26, + -19, -22, -10, -15, 19, 14, + -48, 15, -32, -2, -20, -6, + -10, 9, -35, -1, 3, -1, + 24, -6, 30, 18, -6, -8, + -30, -8, -24, -23, 10, -3, + 17, -11, -8, -40, -26, -3, + -6, -18, -6, 10, 42, -13, + -9, 27, 3, -12, -5, 2, + -29, 4, 13, 29, -14, -8, + 9, -26, 4, 29, -25, -11, + 20, 29, -15, 25, 30, -25, + -20, -1, 2, -18, 56, -26, + -30, 6, 2, -9, -22, 7, + -15, 42, -23, 29, 0, 10, + 23, -12, -15, 9, 37, -1, + 15, 2, -1, -20, 18, 45, + -7, -18, -13, -19, -12, 2, + -6, 33, -28, -17, -11, 20, + -6, -15, 3, 23, 9, 11, + 8, 17, 14, 7, 22, -21, + -12, 10, -4, 11, -35, -19, + 24, 4, -22, 24, 7, -26, + -34, -4, -2, 22, 14, -29, + 1, 29, 24, -19, 24, -24, + 27, 39, -18, 14, -30, -22, + -13, -20, -11, -21, 4, -4, + 23, 9, -4, 14, 6, -2, + -29, 14, -2, 8, -20, 0, + -6, -1, 36, 16, 32, -5, + 22, -2, -4, 14, -20, 22, + 6, 17, 4, -16, 11, -22, + -17, -20, -9, 41, -66, 25, + -7, -2, 5, -1, 3, 20, + 21, 4, -38, 15, 19, -7, + -16, 8, 7, -1, -9, -13, + 1, 1, -17, -23, 0, 18, + -11, -9, 6, -3, -3, 1, + -9, -3, 16, -26, 32, -7, + -10, 21, -20, 12, 6, 20, + -26, 25, -17, -47, -8, -54, + 15, -21, -34, 4, -20, -2, + -2, -38, 4, 3, 9, -5, + -9, -5, -16, 12, -13, 11, + -4, 22, 28, -18, -57, 26, + -7, -7, -39, -1, -42, 20, + 21, -70, 20, -2, -9, 24, + 22, 1, 38, 6, 22, -7, + -14, 29, 31, -19, -7, 8, + -23, 38, -21, -18, -48, 5, + 23, -16, 2, 14, 11, 22, + -3, -10, 15, -14, -11, 12, + 17, -47, -38, -27, 31, 17, + 3, 7, 4, 21, 9, 9, + -9, 34, 7, -13, -24, 5, + -6, 63, 0, 22, 29, -14, + 14, 7, -12, 3, 4, -9, + -7, -3, -23, 19, -23, -26, + 18, -32, -5, 13, 10, -19, + -18, -28, -19, 31, 7, 9, + 13, 26, -8, 45, 0, 6, + -13, -7, -41, 2, 40, 20, + 8, 1, 13, 11, -8, 1, + 20, 32, 28, 13, -19, 7, + -26, -38, -11, 11, 8, -3, + 9, -4, -17, 13, 31, -29, + -32, 6, -20, 85, 15, -25, + -1, 27, 18, -13, -6, 8, + 9, 17, 5, 11, 1, -30, + 40, 14, -4, 10, -25, 26, + -7, 3, 29, 65, -8, 3, + 15, 1, -26, 33, 4, -3, + -29, 29, 19, 40, 2, -11, + 1, 39, 1, -34, 23, -22, + -25, -31, 18, -27, 16, 8, + -22, 3, 19, -15, 38, -27, + -22, 5, 4, -8, -32, -18, + 62, 1, -25, 46, -5, -21, + -31, -15, -29, 21, 11, 24, + -9, -16, 5, 0, 49, -10, + 12, 11, -14, 10, 19, -21, + 5, 7, -12, -17, 10, 23, + -31, -1, 26, 18, 11, 0, + -8, 1, 23, -34, 3, -47, + 11, -26, 7, -35, 1, 12, + 0, -6, -12, 20, -5, 31, + -2, -10, 0, -2, 22, 1, + 7, 40, -2, -15, 6, 4, + -24, -17, 4, -11, 14, -15, + 0, 17, -22, -16, -15, 28, + 2, -27, 42, 2, 10, -4, + -9, -38, -9, 5, -4, 14, + 39, 23, -8, 31, -52, -10, + 13, -3, 37, 9, -6, 7, + -21, -15, 40, -17, 15, -22, + -21, -21, -8, 13, -30, -1, + 22, -2, -1, -8, -21, 6, + 1, 18, -25, 22, 20, 6, + 0, -11, 8, 22, -17, 7, + -17, -22, -9, 31, -32, 7, + 24, 3, -9, -39, 10, -31, + 11, -10, 13, 9, -16, 22, + 11, 9, -4, 24, -16, -17, + -11, -28, -44, 8, -6, -2, + 32, 6, 15, -3, 2, -30, + 0, -17, 24, 40, -1, 6, + -6, 25, 11, -19, -12, -15, + -19, 6, 16, -22, -26, 18, + 0, 5, -15, -34, 8, -27, + 22, 18, 7, -13, 30, 13, + -4, 14, 35, -17, -25, 9, + 8, -4, 2, 39, -21, 21, + -38, 3, 62, 39, -24, 34, + -34, -3, 15, -15, 20, 12, + -19, -27, -31, 6, 21, 9, + -20, 26, -23, 7, -34, 40, + -4, 16, 13, 70, 2, 29, + 13, -25, 7, -37, -3, 8, + -14, 44, -31, 28, -52, -9, + -19, 7, -3, 20, -19, 6, + -24, -28, -155, 57131, -1421192, 11753785, + -47262139, 107836784, -149772281, 129763784, -69391859, 21781536, + -3580001, 237345, -2784, -5, -8, -20, + 51, 31, 21, -7, 4, 31, + -23, -21, 14, -1, -4, -6, + 20, -27, -11, -8, -24, -5, + -13, -26, 42, 7, -5, 43, + -26, 0, -56, 18, -15, -21, + 7, 2, -8, 1, -20, -14, + -25, -29, -6, -3, -29, -8, + 23, 24, -13, 30, -16, -29, + -14, 0, -14, 24, -55, -1, +}; + +static const int32_t ifft_in_imag_3072_q31[3072] = { + 0, 10, -20, -15, 21, -8, + 63, -15, -9, -25, 3, -7, + -21, -6, -5, -35, 2, 43, + -21, 1, -6, 0, 13, 8, + -6, -7, 1, -1, 7, 3, + -10, 68, 17, 12, -28, -34, + 26, 4, -5, -17, -4, 11, + -21, -2, -21, -7, 13, -33, + -11, 10, 19, 6, -5, 16, + -7, 50, -5, 33, -4068, 340975, + -5154185, 31427845, -100341977, 188051721, -217523237, 156961250, + -68943059, 17183334, -2082308, 83903, -220, 19, + -10, -17, 24, 8, 30, -1, + -16, -1, -1, 7, -30, -32, + 4, 12, -10, 52, 41, 0, + -23, -1, 33, -13, -27, 33, + -23, -27, 2, -53, -7, 17, + -5, 54, 6, -1, 7, 11, + -8, 25, -10, 22, -9, 25, + 0, -26, -27, 3, -5, -19, + -24, -7, 29, -1, 15, 8, + 27, 33, 25, 16, -57, -14, + 13, 11, -22, 24, -22, 10, + -17, 8, 10, 43, 19, 15, + -19, -12, -13, -26, 2, 11, + 29, 29, -29, 24, 20, -31, + 16, 4, 5, -21, -16, -21, + -22, 5, -5, 22, -15, -2, + 29, -21, -3, -5, 9, 6, + 4, 6, 7, -3, -5, -27, + 31, 6, -2, 8, -36, -3, + 43, 2, -8, -16, -23, -44, + -17, -8, 33, -37, -8, 13, + -24, 3, -12, 35, -26, -13, + -22, -2, -5, 5, -23, 5, + -20, 2, -20, 21, 42, 17, + -19, 2, 31, -31, 10, 23, + 4, 20, -11, 9, 15, -5, + 13, -20, -45, 34, -6, -10, + -25, 5, -17, -14, 25, -27, + 9, 5, -7, 7, -24, 10, + -5, -10, -8, 3, -34, -43, + -10, 8, 23, 11, 36, 18, + -26, -11, -1, -3, -19, -4, + -31, -31, -15, 12, 15, 44, + 39, -13, 20, 45, -2, -4, + 7, 5, -7, 32, 9, -4, + -4, -1, -13, 22, 10, -35, + -1, 2, 31, 16, 36, 37, + -21, -14, -42, -34, 32, -26, + 9, 25, -9, 8, -18, -2, + 2, 13, 38, 24, -9, -17, + -4, -1, -10, 2, -1, 45, + 28, -25, 18, 10, -46, -19, + 30, 1, -46, -13, 10, -7, + -27, 22, 2, 23, -24, -13, + -22, -15, -30, 27, 16, 3, + -55, 29, 24, 5, 29, -3, + 1, 15, -18, -6, 22, 11, + 19, -8, 25, -12, 15, 14, + 31, -46, -10, 18, -38, -10, + 22, 16, -2, 8, 4, 37, + 22, 30, 42, 48, -30, -5, + 21, -15, -13, -13, -31, 27, + -4, -26, 2, -15, 33, -17, + -39, 7, -20, -1, 14, -19, + 21, 4, 22, -20, -18, 23, + -2, -9, 38, -23, 8, 35, + -9, -7, 3, 7, -5, -5, + 46, 20, 35, 21, -28, 17, + -33, 19, 22, -23, 30, 14, + 34, -12, -25, -19, -14, 21, + -20, 22, 4, 8, -9, 20, + 24, -36, 0, -24, -18, -4, + 6, 11, -4, -26, -22, -15, + -36, 16, -32, 23, -3, 14, + -51, -36, -5, 14, 8, 10, + 41, -12, -20, 25, 1, -24, + 16, 12, 43, 14, -17, 4, + -60, -28, -4, -41, 44, 6, + 1, -4, 6, -1, -1, -14, + -9, 3, -14, -38, 0, 14, + 1, 35, -9, -14, 22, 39, + -21, -29, 15, 32, 4, 23, + 19, 20, 1, -40, -29, 31, + 6, -33, -14, -15, 11, -8, + -13, -36, -35, -8, -7, 2, + 21, -17, -19, -2, 38, 19, + -5, 4, 15, -21, 51, 52, + 18, 1, 45, -7, -11, -22, + -7, 1, -49, -6, -12, 24, + -7, -46, -63, 1, -13, -15, + -25, 3, 27, 10, 9, -33, + -6, -36, 6, 1, -25, -7, + 13, -5, -4, 2, -5, 3, + -33, -36, -2, -26, -27, -16, + -6, -10, 37, -29, 18, 3, + -18, -4, -7, -1, -7, -4, + 3, 46, -12, -30, -7, -12, + -6, 5, -25, -9, 1, 12, + 31, 17, -7, 20, -36, 10, + 0, 13, 16, -16, 10, 17, + 23, -14, 10, -4, -17, -1, + -8, -8, -25, 24, 3, 7, + -16, 59, 2, 28, 25, -1, + 14, -3, -30, -27, -17, 25, + 46, -14, -22, 21, 18, -9, + 13, 23, -5, -23, -18, -5, + -35, 7, 12, -18, -17, 8, + 3, -47, 1, -16, -6, 38, + 38, 20, -3, 41, 71, 0, + 3, 31, 29, -9, 5, -20, + 23, 19, -30, 14, 30, 45, + 14, -29, -6, -9, -12, 14, + -11, 26, -6, -20, -26, 20, + -2, -5, 30, 1, 3, -2, + -36, 13, 5, -7, 30, 18, + -8, -16, 9, 24, 4, 8, + -18, -15, -7, 6, -44, 2, + 9, -18, -21, 3, 4, 1, + -13, -12, 18, -5, 7, -11, + -17, -26, 10, 12, -7, 24, + 7, -12, 39, 21, -16, 37, + -10, -12, -36, -9, -9, 17, + -29, -37, 6, -3, 28, 19, + -37, 17, -21, 7, 4, -5, + 22, 10, -2, -13, 26, -5, + -5, 6, -22, -2, 19, -5, + 15, 15, -2, 9, -15, 11, + -18, -5, -24, 21, 40, -33, + -9, -3, 40, -14, -15, 32, + 31, 13, -23, -17, 32, 10, + 14, 7, 18, -30, -13, -27, + -11, -27, -52, -15, -18, 15, + 42, -7, 17, 10, -1, -30, + -13, -21, 7, -3, 37, 19, + 8, -22, -6, -28, -7, 43, + 9, -24, -27, -33, -7, 27, + -21, 21, 28, -20, -44, 14, + 29, 6, -6, -4, 2, -18, + 13, -5, 14, 21, 15, -2, + 57, -2, 25, -4, 28, 20, + -10, -9, -29, 16, -43, 27, + 12, -9, -1, -21, 47, -45, + -15, -26, -1, 18, 22, -20, + 3, -22, 19, -55, 5, 19, + -4, -15, -11, 0, -14, -9, + -47, 15, 21, -17, -3, 3, + 6, 43, 14, 9, 10, -38, + -1, -22, 13, 6, 11, -17, + 52, -1, -7, 4, 12, -22, + 1, -13, -4, -30, -17, 7, + 6, 39, -5, -34, -15, 21, + -15, -10, -15, 6, -24, -23, + 0, -18, -23, -22, -44, 1, + -16, -3, 22, 3, -14, 4, + 18, -1, -6, 10, 3, 6, + 19, -1, -4, 11, 1, -9, + -3, 6, 22, -11, 6, 5, + -6, -9, -4, 0, -7, -43, + -29, 1, 2, 28, 9, 34, + 19, -2, 1, 7, -9, 16, + 9, 5, -2, 6, -10, 25, + -3, -28, -46, -8, 10, -10, + 0, 36, 1, 10, -2, 14, + 12, 28, -6, 22, -1, -2, + -18, -15, 15, 7, -2, -4, + 28, 17, -6, 5, 8, 22, + -12, -52, 8, 33, 7, 8, + -26, -19, -11, 8, 1, 18, + 12, -38, 25, 9, 22, -6, + 16, 9, 34, -33, 27, -22, + -6, -24, 0, -28, -5, 25, + -6, 36, -4, -38, 6, -14, + 1, -21, -17, 27, -73, -21, + 19, 20, 13, -13, -28, 21, + 2, 34, -2, -33, -8, 28, + 5, 18, 3, 16, -35, 7, + 14, 1, -20, -11, 33, -16, + 20, 15, -21, -21, 10, 9, + 3, -27, -10, -1, -2, 22, + 6, 34, -34, -25, 7, 37, + 8, -33, -16, 5, 5, 5, + 17, 5, -5, 3, 37, -18, + -9, 10, -20, 6, -5, 56, + 26, -11, -17, 5, 10, 7, + 5, -23, 51, 15, -24, -15, + -20, -24, -16, 23, -22, -5, + -18, 30, 23, -35, 2, 23, + 12, 20, -9, -3, 25, -4, + 0, 2, 47, -24, -10, 12, + 14, 22, -7, 18, -14, 10, + 20, -6, 9, 20, 0, -2, + -32, 22, -17, -12, 26, -39, + -2, -16, 29, 2, 16, 13, + 8, -14, 3, -15, 3, 3, + 2, -26, -46, 9, 14, -16, + -29, 36, 1, 49, 25, -45, + 23, 7, -1, 13, -3, 24, + -15, -23, -9, -13, -10, -7, + 28, -14, -14, 29, 1, -7, + -10, 5, 17, 4, 24, 27, + -4, 31, 8, 10, 22, -18, + 27, -12, -47, 24, 3, 8, + 8, -12, -7, -10, -50, -38, + 28, 11, 18, -6, 38, 22, + -1, 15, -15, 8, -27, 27, + -50, 10, -15, 20, 10, 1, + -15, -12, -13, 1, 6, -22, + 17, -3, 23, 13, -17, -12, + 20, -27, -23, -6, 0, -12, + 15, -23, 18, -3, -2, -37, + -32, 28, 20, 0, -33, 10, + -25, 3, -16, -6, 5, 5, + -3, -28, 5, 33, -11, -18, + 31, 34, -2, -2, -53, 15, + 12, -3, 9, 12, 11, 30, + -18, 25, 34, 3, 43, 15, + -18, 43, -5, -10, 2, -6, + 45, 19, 18, -15, -9, 26, + 3, -21, -25, -7, 17, 4, + -7, -27, 11, 5, 18, -40, + 28, 1, 0, -7, 38, -26, + 9, 15, 12, 3, 9, 15, + -34, 51, -1, -30, 4, 45, + 13, 9, 13, -15, 17, 25, + 22, -21, 30, 27, -52, -41, + 40, -37, -8, -44, 45, 26, + 1, 20, -5, 4, 13, 30, + 19, -7, 4, -6, -10, -8, + -19, -30, 4, -35, 4, 10, + -2, -6, 1, 50, -3, 2, + 14, 0, 10, 24, 3, 36, + -13, 4, -21, -15, -4, -9, + 18, -26, -4, 13, 11, -16, + 17, 1, -15, -4, 14, -14, + 4, -23, -2, 3, 12, -8, + 32, 10, -4, 6, 2, 11, + -31, 20, 7, -37, 7, 23, + -18, -30, -31, 20, -1, 10, + -18, 17, 31, -10, -12, -9, + -3, -16, -44, -16, -10, -6, + -21, -10, -13, 7, 6, -1, + -18, 10, -9, 23, -14, -31, + -21, -8, 32, 11, 35, 11, + -18, 1, -8, -29, 7, 6, + -16, -4, 9, 10, -4, 0, + -12, -7, 13, 9, -23, 7, + -18, -16, 23, 46, -39, 17, + -9, -18, 9, 11, 18, 7, + -18, 1, -20, 8, -1, -7, + 21, -2, 13, -23, -12, 8, + -28, 17, -12, -36, 33, 10, + 34, -8, -2, 44, -2, 11, + 19, -17, -11, 55, 26, 4, + -14, -41, 5, 3, 49, -44, + 0, 44, -49, -3, -5, 41, + 14, -4, -26, -55, 11, 17, + -19, -11, 2, -44, 2, 8, + -34, -10, -33, 36, 12, -17, + 28, -8, 12, 23, -13, 2, + -21, 7, 1, -8, 20, -1, + 18, -7, -18, -11, -9, 18, + 9, -17, 39, -46, -23, 16, + 18, -7, 23, -9, -13, 7, + 12, 0, 4, -10, -9, 4, + 16, -6, -7, 29, 8, -1, + 18, -11, -35, -11, -32, 8, + 21, 31, 14, -23, 9, -10, + 18, 1, -6, -7, 13, 10, + 21, 6, 10, 16, 44, 16, + 3, 9, 12, 10, -31, -17, + 18, -10, 1, -20, 31, 30, + 18, -23, -7, 37, -7, -20, + 31, -11, -2, -6, 4, -10, + -32, 8, -12, -3, 2, 23, + -4, 14, -14, 4, 15, -1, + -17, 16, -11, -13, 4, 26, + -18, 9, 4, 15, 21, -4, + 13, -36, -3, -24, -10, 0, + -14, -2, 3, -50, -1, 6, + 2, -10, -4, 35, -4, 30, + 19, 8, 10, 6, -4, 7, + -19, -30, -13, -4, 5, -20, + -1, -26, -45, 44, 8, 37, + -40, 41, 52, -27, -30, 21, + -22, -25, -17, 15, -13, -9, + -13, -45, -4, 30, 1, -51, + 34, -15, -9, -3, -12, -15, + -9, 26, -38, 7, 0, -1, + -28, 40, -18, -5, -11, 27, + 7, -4, -17, 7, 25, 21, + -3, -26, 9, 15, -18, -19, + -45, 6, -2, 10, 5, -43, + 18, -15, -43, -3, -34, -25, + 18, -30, -11, -12, -9, 3, + -12, -15, 53, 2, 2, -34, + -31, 18, 11, -33, -5, 28, + 3, -5, -5, 6, 16, -3, + 25, -10, 33, 0, -20, -28, + 32, 37, 2, 3, -18, 23, + -15, 12, 0, 6, 23, 27, + -20, 12, 17, -13, -23, 3, + -17, 22, -6, -1, 13, 12, + 15, -1, -10, -20, 15, -10, + 50, -27, 27, -8, 15, -15, + 1, -22, -38, 6, -18, -11, + -28, 38, 50, 10, 7, 12, + -8, -8, -3, -24, 47, 12, + -27, 18, -22, -10, -8, -31, + 4, -27, -24, -4, -17, -5, + 10, 7, -1, -29, 14, 14, + -28, 7, 10, 13, 9, 23, + 15, -24, 3, -13, 1, -7, + -23, 45, -25, -49, -1, -36, + 29, 16, -14, -9, 46, 26, + -2, -3, -3, 15, -3, 14, + -8, -13, -16, -2, -29, 16, + 2, 39, -26, 12, 17, -22, + 32, 2, 0, -20, -9, 6, + -20, -10, 14, -18, 7, -22, + -14, -12, 10, 24, -47, -2, + 0, 4, -25, 3, 9, -20, + -12, -23, -2, 35, -23, -30, + 18, 5, 22, -23, 16, 24, + 20, 15, 24, -15, -51, 23, + -5, -7, -10, -5, 17, 11, + -26, -56, 5, -6, 20, -10, + 9, 18, -37, -3, 5, -5, + -17, -5, -5, -5, 16, 33, + -8, -37, -7, 25, 34, -34, + -6, -22, 2, 1, 10, 27, + -3, -9, -10, 21, 21, -15, + -20, 16, -33, 11, 20, -1, + -14, -7, 35, -16, -3, -18, + -5, -28, 8, 33, 2, -34, + -2, -21, 28, 13, -13, -20, + -19, 21, 73, -27, 17, 21, + -1, 14, -6, 38, 4, -36, + 6, -25, 5, 28, 0, 24, + 6, 22, -27, 33, -34, -9, + -16, 6, -22, -9, -25, 38, + -12, -18, -1, -8, 11, 19, + 26, -8, -7, -33, -8, 52, + 12, -22, -8, -5, 6, -17, + -28, 4, 2, -7, -15, 15, + 18, 2, 1, -22, 6, -28, + -12, -14, 2, -10, -1, -36, + 0, 10, -10, 8, 46, 28, + 3, -25, 10, -6, 2, -5, + -9, -16, 9, -7, -1, 2, + -19, -34, -9, -28, -2, -1, + 29, 43, 7, 0, 4, 9, + 6, -5, -6, 11, -22, -6, + 3, 9, -1, -11, 4, 1, + -19, -6, -3, -10, 6, 1, + -18, -4, 14, -3, -22, 3, + 16, -1, 44, 22, 23, 18, + 0, 23, 24, -6, 15, 10, + 15, -21, 15, 34, 5, -39, + -6, -7, 17, 30, 4, 13, + -1, 22, -12, -4, 7, 1, + -52, 17, -11, -6, -13, 22, + 1, 38, -10, -9, -14, -43, + -6, -3, 3, 17, -21, -15, + 47, 9, 14, 0, 11, 15, + 4, -19, -5, 55, -19, 22, + -3, 20, -22, -18, 1, 26, + 15, 45, -47, 21, 1, 9, + -12, -27, 43, -16, 29, 9, + 10, -20, -28, 4, -25, 2, + -57, 2, -15, -21, -14, 5, + -13, 18, -2, 4, 6, -6, + -29, -14, 44, 20, -28, -21, + 21, -27, 7, 33, 27, 24, + -9, -43, 7, 28, 6, 22, + -8, -19, -37, 3, -7, 21, + 13, 30, 1, -10, -17, 7, + -42, -15, 18, 15, 52, 27, + 11, 27, 13, 30, -18, -7, + -14, -10, -32, 17, 23, -13, + -31, -32, 15, 14, -40, 3, + 9, 33, -40, -21, 24, 5, + 18, -11, 15, -9, 2, -15, + -15, 5, -19, 2, 22, -6, + 5, 5, -26, 13, 2, -10, + -22, 5, -4, -7, 21, -17, + 37, -19, -28, 3, -6, 37, + 29, -17, 9, 9, 36, 12, + 10, -37, 16, -21, -39, 12, + -7, -24, 7, -12, -10, 26, + 17, 11, -7, 5, -18, 12, + 13, -1, -4, -3, 21, 18, + -9, -2, 44, -6, 7, 15, + 18, -8, -4, -24, -9, 16, + 8, -18, -30, 7, -5, -13, + 36, 2, -3, -1, -30, 5, + 2, -20, 26, 20, 6, -26, + 11, -14, 12, 9, 6, 29, + -14, -45, -30, -14, 30, -19, + -23, 20, -5, 9, -29, -31, + -3, 0, -71, -41, 3, -20, + -38, -38, 6, 16, -1, 47, + -3, -8, 17, 18, -12, -7, + 35, 5, 18, 23, 5, -23, + -13, 9, -18, -21, 22, 14, + -46, -25, 17, 27, 30, 3, + -14, 1, -25, -28, -2, -59, + 16, -7, -3, -24, 25, 8, + 8, 1, 17, 4, -10, 14, + -23, -17, -10, 16, -16, -13, + 0, -10, 36, -20, 7, -17, + -31, -12, -1, 9, 25, -5, + 6, 12, 7, 30, 12, -46, + -3, 4, 7, 1, 7, 4, + 18, -3, -18, 29, -37, 10, + 6, 16, 27, 26, 2, 36, + 33, -3, 5, -2, 4, 5, + -13, 7, 25, -1, -6, 36, + 6, 33, -9, -10, -27, -3, + 25, 15, 13, -1, 63, 46, + 7, -24, 12, 6, 49, -1, + 7, 22, 11, 7, -45, -1, + -18, -52, -51, 21, -15, -4, + 5, -19, -38, 2, 19, 17, + -21, -2, 7, 8, 35, 36, + 13, 8, -11, 15, 14, 33, + -6, -31, 29, 40, -1, -20, + -19, -23, -4, -32, -15, 29, + 21, -39, -22, 14, 9, -35, + -1, -14, 0, 38, 14, -3, + 9, 14, 1, 1, -6, 4, + -1, -6, -44, 41, 4, 28, + 60, -4, 17, -14, -43, -12, + -16, 24, -1, -25, 20, 12, + -41, -10, -8, -14, 5, 36, + 51, -14, 3, -23, 32, -16, + 36, 15, 22, 26, 4, -11, + -6, 4, 18, 24, 0, 36, + -24, -20, 9, -8, -4, -22, + 20, -21, 14, 19, 25, 12, + -34, -14, -30, 23, -22, -19, + 33, -17, 28, -21, -35, -20, + -46, 5, 5, -7, -3, 7, + 9, -35, -8, 23, -38, 9, + 2, -23, 18, 20, -22, -4, + -21, 19, -14, 1, 20, -7, + 39, 17, -33, 15, -2, 26, + 4, -27, 31, 13, 13, 15, + -21, 5, 30, -48, -42, -30, + -22, -37, -4, -8, 2, -16, + -22, 10, 38, -18, 10, 46, + -31, -14, -15, 12, -25, 8, + -19, -11, -22, 6, 18, -15, + -1, 3, -29, -5, -24, -29, + 55, -3, -16, -27, 30, 15, + 22, 13, 24, -23, -2, -22, + 27, 7, -10, 13, 46, -1, + -30, 19, 46, -10, -18, 25, + -28, -45, 1, -2, 10, 1, + 4, 17, 9, -24, -38, -13, + -2, 2, 18, -8, 9, -25, + -9, 26, -32, 34, 42, 14, + 21, -37, -36, -16, -31, -2, + 1, 35, -10, -22, 13, 1, + 4, 4, -9, -32, 7, -5, + -7, 4, 2, -45, -20, 13, + -39, -44, -15, -12, 15, 31, + 31, 4, 19, 3, 1, 11, + 26, -18, -36, -11, -23, -8, + 10, 43, 34, -3, 8, 10, + 5, -10, 24, -7, 7, -5, + -9, 27, -25, 14, 17, -5, + 25, 10, 6, -34, 45, 20, + -13, 5, -15, -9, 11, -20, + -4, -23, -10, 31, -31, -2, + 19, -17, -42, -21, 20, -2, + 20, -5, 23, -5, 5, 2, + 22, 13, 26, -35, 12, -3, + 24, -13, 8, 37, -33, 8, + 17, 44, 23, 16, 8, -2, + -43, 3, 36, -8, 2, -6, + -31, 27, 5, 3, -7, -6, + -4, -6, -9, 5, 3, 21, + -29, 2, 15, -22, 5, -5, + 22, 21, 16, 21, -5, -4, + -16, 31, -20, -24, 29, -29, + -29, -11, -2, 26, 13, 12, + 19, -15, -19, -43, -10, -8, + 17, -10, 22, -24, 22, -11, + -13, 14, 57, -16, -25, -33, + -27, -8, -15, 1, -29, 7, + 24, 19, 5, -3, 27, 26, + 0, -25, 9, -22, 10, -25, + 8, -11, -7, 1, -6, -54, + 5, -17, 7, 53, -2, 27, + 23, -33, 27, 13, -33, 1, + 23, 0, -41, -52, 10, -12, + -4, 32, 30, -7, 1, 1, + 16, 1, -30, -8, -24, 17, + 10, -19, 220, -83903, 2082308, -17183334, + 68943059, -156961250, 217523237, -188051721, 100341977, -31427845, + 5154185, -340975, 4068, -33, 5, -50, + 7, -16, 5, -6, -19, -10, + 11, 33, -13, 7, 21, 2, + 21, -11, 4, 17, 5, -4, + -26, 34, 28, -12, -17, -68, + 10, -3, -7, 1, -1, 7, + 6, -8, -13, 0, 6, -1, + 21, -43, -2, 35, 5, 6, + 21, 7, -3, 25, 9, 15, + -63, 8, -21, 15, 20, -10, +}; + +static const int32_t ifft_ref_real_3072_q31[3072] = { + -1954, 1267, -1428, -189, -2328, -332, + -2139, 2088, 1120, -504, -2966, -2008, + -3174, 846, -914, 1141, -2101, -1347, + 3027, -1403, -1333, 362, 1668, -927, + 1181, -1244, -225, -358, 228, -2786, + 2981, -1883, 1268, -3378, 837, -534, + -1694, -1442, -1609, 233, 657, 23, + -1344, 662, -1927, -982, -3378, -381, + -779, 844, 2776, 2991, -1622, -1119, + 3870, 265, -548, 2619, 4851, -163, + 1724, 2204, 750, 902, 2561, 4782, + 3436, 2236, 3459, 607, -2115, 1150, + 684, 206, -433, -699, -4322, -665, + -1792, -1032, -2476, -4106, -4595, -7403, + -5622, -4960, -3025, -6094, -4879, -5786, + -6701, -500, -3185, -2761, -3410, -1888, + 2902, 3236, 1579, 1072, 6289, 5865, + 3737, 7878, 10582, 11262, 5716, 8054, + 10346, 11274, 12870, 8232, 10221, 8739, + 9892, 9425, 8387, 2936, 1194, 3767, + 3503, -909, -5905, -5029, -9175, -10882, + -13511, -12505, -18920, -17241, -21785, -18975, + -18165, -21109, -20486, -23023, -18012, -19889, + -16786, -16232, -16213, -12552, -11375, -5925, + -3013, -68, 6753, 11006, 10410, 17275, + 18657, 26177, 27511, 30865, 35720, 36178, + 40779, 42618, 39224, 37957, 35824, 38634, + 33522, 32862, 26097, 23825, 18366, 10697, + 2542, -3672, -8849, -17950, -25991, -35087, + -41498, -47572, -51102, -58015, -62956, -68385, + -70399, -70984, -75068, -68763, -72106, -66894, + -60619, -53553, -47109, -36855, -27916, -17365, + -9085, 4803, 17617, 31120, 43881, 55889, + 67189, 80156, 92141, 98698, 106521, 111873, + 119040, 120646, 121770, 119358, 115558, 112068, + 103408, 89951, 80567, 66268, 48447, 35386, + 11202, -5637, -31755, -52096, -69437, -90322, + -113541, -131335, -149125, -165772, -176375, -189568, + -193607, -198890, -199959, -201103, -193177, -183881, + -169659, -150640, -131600, -112287, -80475, -51938, + -22370, 9892, 41497, 76822, 111071, 143854, + 174132, 205505, 236130, 261930, 279611, 299838, + 309539, 319576, 321418, 317950, 307013, 292425, + 272196, 248900, 217268, 174794, 134153, 89526, + 39276, -11100, -64816, -117792, -169261, -224363, + -272702, -320621, -363995, -405013, -436618, -461801, + -484416, -498480, -498357, -493096, -476616, -455593, + -423250, -379896, -334722, -274426, -209798, -142911, + -68726, 10645, 91081, 174972, 259533, 339980, + 415872, 486041, 556745, 615719, 664094, 704463, + 734385, 750620, 754842, 747862, 725129, 692003, + 643234, 579508, 509962, 422076, 326650, 221229, + 109587, -7844, -129406, -252356, -374193, -495182, + -610853, -720845, -820495, -906599, -985383, -1044324, + -1087653, -1116727, -1119971, -1110995, -1079312, -1030261, + -955116, -868440, -755018, -634629, -491612, -340000, + -174008, -2545, 175459, 357679, 534472, 711584, + 880611, 1044734, 1187234, 1319178, 1429981, 1514941, + 1579514, 1621514, 1632752, 1615925, 1570986, 1497739, + 1396079, 1264505, 1111550, 930262, 727987, 506360, + 270114, 20615, -236580, -496428, -752595, -1006049, + -1248329, -1478414, -1689987, -1875964, -2034697, -2162770, + -2260013, -2312597, -2336506, -2314000, -2246916, -2147191, + -1999537, -1819161, -1598211, -1339436, -1053219, -741591, + -404819, -52105, 309008, 673402, 1040004, 1399854, + 1743725, 2065359, 2364137, 2627629, 2852233, 3037607, + 3171927, 3252277, 3278896, 3252821, 3166834, 3019401, + 2821154, 2565178, 2259733, 1902289, 1505911, 1071868, + 604387, 114568, -390538, -901979, -1414555, -1914667, + -2392421, -2844993, -3257702, -3627354, -3937513, -4197246, + -4382927, -4499670, -4541377, -4505222, -4388437, -4193254, + -3917142, -3572652, -3149370, -2664511, -2114061, -1520182, + -878438, -206493, 488632, 1187065, 1887697, 2573079, + 3234344, 3854568, 4423382, 4930050, 5363304, 5713011, + 5974276, 6138366, 6197414, 6153614, 6000228, 5736857, + 5367596, 4895708, 4327978, 3670697, 2930826, 2121844, + 1253041, 345643, -591645, -1542721, -2491418, -3421573, + -4314585, -5153975, -5923671, -6613787, -7199081, -7676067, + -8032673, -8261748, -8344694, -8289718, -8091735, -7743502, + -7254200, -6625856, -5868940, -4992277, -4003982, -2924878, + -1768018, -551110, 699523, 1970706, 3241390, 4481897, + 5675977, 6800856, 7836039, 8754662, 9546004, 10185660, + 10671856, 10976049, 11105194, 11035120, 10778861, 10326591, + 9683699, 8861631, 7867395, 6705779, 5405603, 3980232, + 2454384, 850530, -805109, -2486513, -4160748, -5805891, + -7382307, -8869688, -10233575, -11456331, -12505369, -13360134, + -14001774, -14417847, -14592276, -14519772, -14192650, -13609416, + -12782143, -11705217, -10409849, -8901823, -7209623, -5352473, + -3361810, -1266846, 895532, 3091918, 5277170, 7420525, + 9489624, 11432011, 13218921, 14821879, 16198902, 17320553, + 18176905, 18728063, 18968219, 18889211, 18474356, 17736672, + 16676488, 15301851, 13630777, 11691303, 9503862, 7109981, + 4543576, 1837027, -952943, -3787629, -6616674, -9389765, + -12060838, -14575923, -16896520, -18966998, -20757833, -22225876, + -23339580, -24064965, -24399452, -24313960, -23801953, -22874513, + -21529058, -19786854, -17661695, -15187331, -12403054, -9343646, + -6061675, -2608238, 960215, 4587200, 8203769, 11754426, + 15171453, 18398050, 21370217, 24031857, 26331810, 28224976, + 29662244, 30620056, 31063943, 30981748, 30362653, 29208928, + 27521429, 25329400, 22653382, 19531351, 16013837, 12153017, + 7998716, 3631447, -887872, -5478882, -10065002, -14563158, + -18898704, -22992346, -26766793, -30153622, -33084231, -35497471, + -37346046, -38578296, -39177363, -39107071, -38357663, -36931725, + -34843569, -32109203, -28776745, -24879474, -20480119, -15636363, + -10440654, -4962221, 700614, 6457439, 12213225, 17863443, + 23312919, 28460670, 33212090, 37476766, 41174535, 44229347, + 46576727, 48162205, 48947682, 48902130, 48012213, 46278804, + 43713794, 40343588, 36215821, 31390803, 25937800, 19941472, + 13486924, 6681309, -359302, -7516992, -14675702, -21708040, + -28497166, -34910721, -40834521, -46165672, -50790519, -54624928, + -57581677, -59595573, -60621295, -60618413, -59571237, -57474927, + -54349703, -50239530, -45184393, -39261870, -32559111, -25183391, + -17240783, -8864180, -195070, 8633884, 17462285, 26142836, + 34521640, 42450999, 49782997, 56376985, 62114562, 66884038, + 70577659, 73119273, 74442582, 74504938, 73280556, 70778132, + 67008877, 62027743, 55891005, 48684209, 40526777, 31524482, + 21837075, 11608676, 1013426, -9776897, -20582082, -31205608, + -41468126, -51178912, -60175566, -68283677, -75344704, -81222512, + -85802753, -88973444, -90664439, -90820751, -89418368, -86452620, + -81948797, -75958944, -68566242, -59877640, -50011282, -39132078, + -27402568, -15013504, -2168131, 10918713, 24020964, 36927546, + 49396361, 61209585, 72158752, 82038947, 90660079, 97855090, + 103473914, 107407932, 109551924, 109843422, 108249115, 104763010, + 99421497, 92289068, 83455070, 73050647, 61227870, 48169481, + 34081773, 19187156, 3739342, -12004598, -27791472, -43333230, + -58365766, -72626836, -85851549, -97795818, -108239741, -116981970, + -123840767, -128671350, -131359123, -131835146, -130042269, -125987174, + -119707501, -111272601, -100798435, -88436623, -74369612, -58823550, + -42030940, -24264150, -5826716, 12986473, 31848307, 50441973, + 68433889, 85512134, 101368634, 115713840, 128269407, 138806740, + 147108818, 153002353, 156352203, 157056990, 155072795, 150393409, + 143062822, 133169262, 120845178, 106267660, 89657885, 71273294, + 51399725, 30366288, 8514011, -13792699, -36174735, -58248196, + -79627543, -99936507, -118812766, -135906620, -150905421, -163518325, + -173495056, -180624235, -184755195, -185764212, -183594689, -178240626, + -169750794, -158228079, -143834201, -126764583, -107293151, -85713607, + -62363318, -37632655, -11918011, 14346364, 40717861, 66742307, + 91968909, 115955469, 138267475, 158501540, 176282366, 191273980, + 203176778, 211748630, 216793410, 218187466, 215847622, 209772219, + 200013871, 186692507, 169996463, 150155667, 127480051, 102325892, + 75082013, 46198998, 16151019, -14561432, -45418844, -75894680, + -105459536, -133586607, -159787579, -183572791, -204511701, -222205628, + -236312691, -246538905, -252660476, -254524141, -252042329, -245203169, + -234069735, -218776002, -199541362, -176642414, -150419791, -121291048, + -89722588, -56220440, -21344396, 14334015, 50206743, 85657746, + 120072210, 152844393, 183400551, 211180477, 235674009, 256422421, + 273023557, 285141522, 292508799, 294952686, 292362193, 284725036, + 272111181, 254677613, 232669346, 206404757, 176282500, 142783969, + 106437823, 67830506, 27604415, -13569950, -54991741, -95959498, + -135761919, -173705841, -209109551, -241337116, -269805490, -293980085, + -313391229, -327649859, -336454096, -339589981, -336941047, -328484687, + -314290618, -294554567, -269534232, -239604639, -205232447, -166945906, + -125368449, -81167635, -35075166, 12139805, 59675747, 106722917, + 152463819, 196110460, 236875290, 274037087, 306914105, 334892098, + 357442746, 374110440, 384551660, 388518382, 385864545, 376568060, + 360714253, 338512455, 310255664, 276379501, 237393220, 193922019, + 146648837, 96353228, 43872047, -9932907, -64141763, -117837085, + -170085645, -219975310, -266629410, -309200944, -346939392, -379119852, + -405153604, -424514398, -436807953, -441745719, -439164716, -429031904, + -411439319, -386617372, -354913267, -316797073, -272860523, -223800373, + -170393396, -113518291, -54112519, 6827960, 68275656, 129182673, + 188492573, 245180555, 298245210, 346732971, 389775867, 426573829, + 456438737, 478787157, 493153292, 499220637, 496795613, 485840102, + 466452793, 438881992, 403526130, 360909673, 311691302, 256650303, + 196670827, 132739707, 65907805, -2700984, -71937130, -140610726, + -207548644, -271569377, -331569987, -386464900, -435269685, -477092614, + -511151438, -536775753, -553458247, -560822864, -558660166, -546903818, + -525680344, -495253593, -456060540, -408696645, -353886403, -292506414, + -225548912, -154104305, -79357989, -2561325, 74990181, 151972303, + 227066091, 298962161, 366405218, 428188514, 483211372, 530459862, + 569062016, 598277372, 617510900, 626356585, 624560000, 612060937, + 588968499, 555591130, 512411217, 460077642, 399402792, 331356454, + 257035710, 177661517, 94544543, 9078357, -77292654, -163096706, + -246858480, -327129277, -402500612, -471640542, -533309915, -586386059, + -629889889, -662987333, -685024657, -695536650, -694236254, -681048472, + -656096749, -619709358, -572413134, -514919077, -448139426, -373131135, + -291109258, -203418830, -111520436, -16947282, 78700085, 173795497, + 266706134, 355821766, 439583391, 516516093, 585243211, 644522213, + 693263322, 730544442, 755633273, 768005415, 767338814, 753543170, + 726752740, 687313915, 635809216, 573019067, 499930312, 417713744, + 327700363, 231372022, 130329737, 26257105, -79076016, -183887358, + -286366416, -384750216, -477320353, -562449725, -638616288, -704458907, + -758758447, -800507282, -828883506, -843307951, -843426655, -829126268, + -800540682, -758051926, -702285573, -634098007, -554555308, -464934509, + -366696843, -261459008, -150968810, -37078553, 78287202, 193169274, + 305590106, 413617022, 515363932, 609041209, 692999791, 765716620, + 825874328, 872350899, 904255173, 920933942, 921991897, 907302345, + 876995606, 831491084, 771450963, 697802833, 611711188, 514553522, + 407924872, 293581426, 173412732, 49450008, -76218165, -201462064, + -324123571, -442093130, -553318187, -655856510, -747889886, -827768644, + -894047369, -945499331, -981148121, -1000271653, -1002435558, -987487658, + -955564675, -907105489, -842828582, -763719588, -671045492, -566295061, + -451176469, -327596047, -197610656, -63402519, 72763028, 208568268, + 341689279, 469837824, 590779804, 702411793, 802756107, 890031701, + 962659652, 1019302373, 1058894770, 1080652653, 1084089892, 1069029267, + 1035616997, 984310463, 915868643, 831368539, 732138036, 619797618, + 496178710, 363330656, 223463581, 78932936, -67831952, -214321056, + -358035511, -496501573, -627322734, -748210245, -857045845, -951889207, + -1031046097, -1093063589, -1136783070, -1161342438, -1166218018, -1151211372, + -1116459710, -1062446233, -989977050, -900187067, -794513962, -674664651, + -542612066, -400545709, -250835618, -95987277, 61364836, 218559073, + 372902420, 521744972, 662503466, 792739349, 910160054, 1012694129, + 1098502669, 1166028599, 1214023809, 1241553437, 1248036967, 1233254419, + 1197341171, 1140794021, 1064475666, 969571958, 857622760, 730435171, + 590108274, 438970794, 279545597, 114515489, -53334992, -221140870, + -386048025, -545211956, -695892850, -835473083, -961502732, -1071771032, + -1164306249, -1237436513, -1289821237, -1320457758, -1328710489, -1314334805, + -1277454667, -1218590303, -1138644363, -1038872942, -920883135, -786610219, + -638256404, -478293472, -309391820, -134402940, 43720522, 221947038, + 397237986, 566582584, 727057401, 875882937, 1010467192, 1128437030, + 1227705429, 1306486153, 1363337657, 1397194988, 1407374023, 1393581661, + 1355955920, 1295029129, 1211727393, 1107379233, 983675309, 842639668, + 686599942, 518157159, 340132910, 155517872, -32551047, -220888680, + -406281117, -585536670, -755581427, -913471547, -1056447125, -1182018238, + -1287955758, -1372371364, -1433739680, -1470903413, -1483138046, -1470128386, + -1431988068, -1369264203, -1282936733, -1174373434, -1045338297, -897950837, + -734656662, -558176986, -371475445, -177689527, 19892513, 217915644, + 412997957, 601805608, 781081716, 947733928, 1098870125, 1231847160, + 1344327293, 1434310121, 1500180677, 1540715667, 1555129526, 1543084250, + 1504675269, 1440471194, 1351469669, 1239099003, 1105185102, 951940796, + 781910283, 597941305, 403111530, 200712781, -5824762, -212994191, + -417261433, -615130588, -803202090, -978230008, -1137190856, -1277301705, + -1396119506, -1491538873, -1561855656, -1605779457, -1622481182, -1611573682, + -1573160731, -1507799779, -1416525935, -1300804137, -1162532566, -1003997496, + -827842473, -637007723, -434707419, -224363857, -9529147, 206141110, + 418961991, 625309440, 821625264, 1004543936, 1170892474, 1317789291, + 1442668348, 1543329576, 1617986419, 1665291150, 1684353298, 1674763544, + 1636596958, 1570432320, 1477313125, 1358749421, 1216693848, 1053501196, + 871900049, 674928292, 465909734, 248374414, 26011493, -197396416, + -418047432, -632168499, -836088206, -1026306899, -1199532237, -1352774708, + -1483362122, -1589013138, -1667859380, -1718486820, -1739959414, -1731839850, + -1694188625, -1627580629, -1533073593, -1412212813, -1266996403, -1099843876, + -913551827, -711245725, -496342808, -272475963, -43448807, 186852460, + 414501321, 635605431, 846385683, 1043217713, 1222718789, 1381788185, + 1517669181, 1627982917, 1710811456, 1764671245, 1788582254, 1782075582, + 1745195237, 1678510734, 1583094073, 1460515358, 1312808363, 1142439233, + 952272960, 745500972, 525630752, 296374628, 61627773, -174619248, + -408338841, -635548976, -852356373, -1055039545, -1240132943, -1404437242, + -1545116122, -1659725677, -1746274508, -1803234452, -1829578860, -1824800936, + -1788947477, -1722558810, -1626723784, -1503026676, -1353526848, -1180736873, + -987560877, -777264714, -553400924, -319762014, -80323054, 160850975, + 399648300, 631989745, 853911556, 1061611842, 1251535835, 1420413179, + 1565341882, 1683813544, 1773777276, 1833658423, 1862393559, 1859456468, + 1824850431, 1759130098, 1663373112, 1539169746, 1388612600, 1214226013, + 1018956148, 806102039, 579277584, 342326951, 99278560, -145730951, + -388522944, -624972370, -851028582, -1062836348, -1256765856, -1429496044, + -1578067280, -1699914608, -1792940108, -1855530759, -1886589380, -1885556536, + -1852427918, -1787733942, -1692547773, -1568470823, -1417588696, -1242446357, + -1046016840, -831625333, -602914593, -363764440, -118246447, 129457933, + 375133133, 614592525, 843753094, 1058698702, 1255759245, 1431571485, + 1583116729, 1707807432, 1803502380, 1868552397, 1901829488, 1902756563, + 1871297545, 1807976709, 1713849715, 1590510399, 1440048829, 1265014746, + 1068377633, 853481494, 623978067, 383777980, 136958703, -112263451, + -359658453, -601001951, -832182190, -1049261424, -1248533642, -1426603203, + -1580431454, -1707395036, -1805325977, -1872546489, -1907914848, -1910818076, + -1881207464, -1819578191, -1726983553, -1604994703, -1455689253, -1281612422, + -1085724274, -871362313, -642175923, -402075595, -155149216, 94400503, + 342322888, 584392163, 816484983, 1034660863, 1235192607, 1414675018, + 1570047568, 1698683993, 1798381542, 1867461297, 1904749062, 1909622838, + 1882001675, 1822371202, 1731754856, 1611708739, 1464279918, 1291998989, + 1097800233, 885005516, 657247259, 418403559, 172550621, -76116588, + -323374830, -565003771, -796896074, -1015106272, -1215927587, -1395951522, + -1552119510, -1681791215, -1782773966, -1853368131, -1892388366, -1899186924, + -1873682131, -1816320639, -1728100774, -1610554472, -1465704675, -1296028376, + -1104443620, -894227806, -668969691, -432525248, -188924505, 57679224, + 303082074, 543110898, 773680753, 990874742, 1191013320, 1370694105, + 1526894338, 1656961319, 1758722067, 1830459298, 1870994518, 1879662164, + 1856352631, 1801500505, 1716070729, 1601552060, 1459928482, 1293641204, + 1105547943, 898884127, 677184136, 444236544, 204030905, -39343464, + -281738664, -519025295, -747170062, -962310760, -1160794862, -1339268137, + -1494721598, -1624539903, -1726553534, -1799057804, -1840866292, -1851309234, + -1830258511, -1778116140, -1695821581, -1584818150, -1447033124, -1284862132, + -1101101431, -898908590, -681765683, -453380283, -217663348, 21366067, + 259626629, 493069110, 717729127, 929798100, 1125685812, 1302090661, + 1456045329, 1584967615, 1686708061, 1759581633, 1802411603, 1814515303, + 1795749950, 1746490361, 1667630949, 1560573113, 1427192494, 1269816696, + 1091161725, 894307502, 682653795, 459821740, 229630215, -3991130, + -237055717, -465609152, -685757243, -893783125, -1086168150, -1259671175, + -1411386186, -1538775234, -1639723774, -1712577604, -1756151022, -1769776508, + -1753292116, -1707041151, -1631881051, -1529159258, -1400681659, -1248706757, + -1075861775, -885147434, -679849203, -463492936, -239788150, -12560413, + 214318410, 436985739, 651670077, 854731189, 1042749355, 1212561343, + 1361329636, 1486571568, 1586224427, 1658655883, 1702702675, 1717693933, + 1703455417, 1660303044, 1589054484, 1490989879, 1367857293, 1221819477, + 1055425710, 871558841, 673398885, 464352943, 248016103, 28083145, + -191694062, -407575006, -615898089, -813149104, -995999398, -1161377159, + -1306529815, -1429044289, -1526909689, -1598526684, -1642758694, -1658946878, + -1646894078, -1606895102, -1539711606, -1446581982, -1329160129, -1189527998, + -1030131177, -853735906, -663406443, -462421125, -254228780, -42401115, + 169451502, 377727713, 578892455, 769550429, 946492399, 1106757984, + 1247674256, 1366915397, 1462519769, 1532950547, 1577092743, 1594289529, + 1584334455, 1547501541, 1484506909, 1396511141, 1285109652, 1152267535, + 1000325188, 831938878, 650030018, 457742667, 258382869, 55359579, + -147847679, -347788933, -541079146, -724454918, -894832674, -1049365889, + -1185484078, -1300945379, -1393867499, -1462752000, -1506525019, -1524534111, + -1516573574, -1482884349, -1424138055, -1341436489, -1236269034, -1110525776, + -966421500, -806481853, -633479129, -450426376, -260462875, -66851522, + 127097697, 318089971, 502889675, 678382853, 841616839, 989876013, + 1120699644, 1231929704, 1321767154, 1388773428, 1431909652, 1450532073, + 1444440429, 1413846257, 1359367356, 1282038972, 1183278349, 1064856624, + 928864074, 777700150, 614008311, 440608109, 260508254, 76791586, + -107397743, -288930379, -464730860, -631837850, -787440176, -928960132, + -1054050908, -1160662595, -1247065451, -1311883567, -1354123178, -1373165346, + -1368802620, -1341217886, -1290981329, -1219063811, -1126791045, -1015827089, + -888150371, -746008746, -591888502, -428466615, -258572737, -85123526, + 88914651, 260589405, 426977848, 585294445, 732875277, 867275801, + 986273147, 1087921573, 1170581642, 1232944255, 1274053918, 1293320897, + 1290533012, 1265841361, 1219785598, 1153259730, 1067496103, 964053892, + 844793457, 711808351, 567446758, 414212914, 254760236, 91832935, + -71784776, -233305320, -389993434, -539216020, -678471866, -805456788, + -918066920, -1014479691, -1093137391, -1152807458, -1192573206, -1211874921, + -1210490051, -1188568015, -1146583692, -1085373537, -1006089058, -910161528, + -799331029, -675561646, -541023592, -398076238, -249186890, -96927127, + 56101333, 207288357, 354082194, 494010022, 624734728, 744083114, + 850106912, 941064500, 1015518034, 1072287608, 1110518956, 1129673954, + 1129534807, 1110224999, 1072183624, 1016175030, 943253900, 854764740, + 752304975, 637704638, 512982470, 380314468, 242013120, 100454613, + -41930954, -182719906, -319522396, -450050709, -572124797, -683715081, + -783002819, -868369891, -938459121, -992172259, -1028703484, -1047537853, + -1048477454, -1031613185, -997345311, -946374992, -879676080, -798477569, + -704258873, -598700531, -483679417, -361196098, -233396476, -102482455, + 29308002, 159719299, 286554702, 407677229, 521069555, 624855049, + 717336577, 797022777, 862641869, 913174983, 947866087, 966234173, + 968080713, 953484175, 922811838, 876683918, 816003691, 741894095, + 655709798, 559004050, 453483583, 341011967, 223540020, 103099760, + -18233260, -138395689, -255355128, -367156398, -471920516, -567930301, + -653616510, -727591722, -788690875, -835956443, -868701664, -886467574, + -889061508, -876552359, -849257612, -807750695, -752853186, -685576819, + -607171911, -519042242, -422762779, -320026739, -212630774, -102428770, + 8682967, 118812009, 226087388, 328720449, 424999500, 513333030, + 592286422, 660577795, 717148715, 761115072, 791827347, 808872127, + 812061714, 801441574, 777310902, 740181390, 690785229, 630053428, + 559110814, 479238032, 391861521, 298534506, 200878658, 100590430, + -607287, -100989423, -198849315, -292555237, -380548523, -461371351, + -533713558, -596424172, -648498641, -689151697, -717782049, -734005256, + -737643908, -728733948, -707537411, -674510484, -630317318, -575800816, + -511961435, -439971506, -361118572, -276793246, -188485832, -97722445, + -6061637, 84927343, 173708384, 258790208, 338756360, 412298791, + 478220506, 535463826, 583135683, 620504631, 647031770, 662348437, + 666300618, 658915978, 640416389, 611220585, 571908297, 523234948, + 466117071, 401592604, 330825586, 255073642, 175660907, 93972958, + 11416894, -70596785, -150687862, -227501411, -299770818, -366304718, + -426033185, -477990740, -521373453, -555521652, -579935619, -594295837, + -598441014, -592403331, -576366581, -550704622, -515936356, -472738946, + -421926566, -364421956, -301273613, -233600973, -162593440, -89496920, + -15564081, 57945801, 129777276, 198735962, 263673192, 323522023, + 377324214, 424207890, 463459128, 494471178, 516806828, 530161704, + 534390205, 529525253, 515722595, 493310268, 462743131, 424629406, + 379680615, 328731908, 272704910, 212598522, 149476632, 84436692, + 18611043, -46890106, -110946726, -172490471, -230497005, -284018193, + -332193404, -374253518, -409544375, -437542950, -457836446, -470163396, + -474396436, -470538010, -458741499, -439285478, -412576250, -379140208, + -339618079, -294740527, -245328608, -192258120, -136474880, -78950323, + -20684043, 37337800, 94123822, 148719315, 200232263, 247809979, + 290690028, 328191762, 359737003, 384846037, 403167797, 414462578, + 418612328, 415616471, 405608074, 388824822, 365627908, 336478576, + 301938852, 262645553, 219323761, 172742000, 123743307, 73167648, + 21900080, -29183756, -79218352, -127364108, -172827967, -214863012, + -252796088, -286026643, -314042942, -336429977, -352864310, -363132718, + -367129059, -364863616, -356429040, -342048284, -322028244, -296774094, + -266768316, -232579633, -194825815, -154197159, -111415846, -67221548, + -22393396, 22309840, 66124542, 108316468, 148196551, 185105519, + 218455448, 247719513, 272444601, 292265988, 306909294, 316172902, + 319976012, 318306257, 311259813, 299016336, 281847408, 260104006, + 234201035, 204634435, 171943983, 136726029, 99607937, 61229245, + 22277383, -16592151, -54723400, -91468534, -126228236, -158430959, + -187562953, -213164354, -234846478, -252284778, -265235499, -273534395, + -277099121, -275918277, -270073506, -259721509, -245096765, -226492670, + -204280869, -178875510, -150747495, -120413922, -88411837, -55306424, + -21669900, 11917494, 44880837, 76678439, 106778103, 134689892, + 159972738, 182223956, 201108612, 216348810, 227726051, 235104451, + 238398771, 237610161, 232803839, 224116370, 211735335, 195925346, + 176994801, 155311385, 131268473, 105311188, 77899862, 49524551, + 20673231, -8152563, -36466660, -63795119, -89689631, -113724103, + -135516162, -154729334, -171063592, -184287496, -194214077, -200713055, + -203732664, -203253125, -199335834, -192088497, -181685639, -168336364, + -152320108, -133926606, -113516594, -91451969, -68135088, -43975393, + -19395029, 5179829, 29338263, 52667285, 74792697, 95346363, + 114000498, 130472557, 144507874, 155901496, 164498743, 170184919, + 172911765, 172668424, 169499571, 163505275, 154819428, 143630524, + 130166712, 114681138, 97470938, 78844567, 59142464, 38718237, + 17918794, -2890238, -23353533, -43135381, -61900242, -79354061, + -95218074, -109237355, -121211123, -130960785, -138345116, -143280745, + -145714419, -145646556, -143111878, -138189432, -130990829, -121678718, + -110439552, -97488855, -83077526, -67467936, -50938253, -33790970, + -16323372, 1170024, 18382855, 35032469, 50844610, 65556934, + 78945328, 90799046, 100930128, 109201569, 115507170, 119753539, + 121907093, 121964702, 119952242, 115936776, 110019754, 102325119, + 93008244, 82261389, 70284961, 57293131, 43529865, 29234651, + 14669782, 72779, -14296826, -28212097, -41430023, -53743152, + -64955789, -74894358, -83414010, -90383998, -95716201, -99336816, + -101228066, -101364256, -99783104, -96540755, -91706720, -85398404, + -77736086, -68883213, -58999854, -48272828, -36895247, -25073075, + -13017290, -929976, 10982581, 22515118, 33483816, 43708142, + 53031199, 61303563, 68404150, 74229401, 78705238, 81769841, + 83400870, 83592764, 82367919, 79764133, 75845584, 70713662, + 64463816, 57222981, 49132592, 40343881, 31010088, 21309507, + 11408987, 1476574, -8313780, -17805847, -26834606, -35257362, + -42940370, -49768901, -55642345, -60471386, -64198025, -66767863, + -68164088, -68386583, -67440978, -65373000, -62227670, -58079637, + -53020760, -47150844, -40578759, -33434938, -25844740, -17946248, + -9882139, -1783854, 6202175, 13941288, 21315238, 28196425, + 34483059, 40077359, 44886111, 48859936, 51928205, 54068948, + 55255999, 55479035, 54764175, 53128559, 50625402, 47303227, + 43241873, 38519687, 33230138, 27466783, 21346265, 14970247, + 8455675, 1911983, -4545824, -10812044, -16774603, -22355449, + -27449074, -31989201, -35898928, -39137158, -41646925, -43408685, + -44400739, -44620314, -44085374, -42807115, -40826602, -38193991, + -34964509, -31193892, -26972036, -22367527, -17466953, -12367218, + -7146262, -1904869, 3267821, 8291041, 13080466, 17561039, + 21651499, 25306067, 28460581, 31066869, 33106592, 34537018, + 35360504, 35569033, 35167687, 34177966, 32634704, 30560883, + 28009270, 25036520, 21689186, 18040253, 14161727, 10113724, + 5974160, 1812535, -2298571, -6293363, -10098147, -13659222, + -16921043, -19827946, -22345389, -24432435, -26066901, -27223379, + -27898523, -28085752, -27792084, -27035106, -25834265, -24220063, + -22225270, -19894969, -17274748, -14414396, -11365505, -8185495, + -4931060, -1659560, 1575182, 4715237, 7711549, 10523077, + 13090914, 15387506, 17376138, 19029662, 20325401, 21250905, + 21795069, 21961365, 21753118, 21177274, 20250113, 19008061, + 17460596, 15654912, 13623473, 11397730, 9025297, 6557050, + 4019253, 1474121, -1045141, -3489662, -5831327, -8019809, + -10026099, -11818538, -13371276, -14669502, -15687477, -16419232, + -16856866, -16994403, -16845308, -16412914, -15714736, -14760202, + -13578426, -12191116, -10629600, -8921647, -7092202, -5190448, + -3239217, -1275934, 663046, 2554298, 4356076, 6044040, + 7594214, 8980359, 10182478, 11188168, 11974521, 12546254, + 12895773, 13014200, 12908086, 12587362, 12058446, 11338623, + 10439693, 9387813, 8201196, 6899320, 5509481, 4064219, + 2576865, 1080047, -400703, -1839939, -3214850, -4504065, + -5687108, -6747572, -7663672, -8435009, -9043227, -9484985, + -9753642, -9849076, -9779353, -9541923, -9151368, -8610339, + -7937707, -7149141, -6254080, -5276073, -4231361, -3138920, + -2020649, -894056, 222733, 1305993, 2340866, 3311888, + 4207117, 5005958, 5703280, 6283490, 6745544, 7082468, + 7287202, 7363976, 7317850, 7144786, 6853114, 6459652, + 5959086, 5371726, 4711175, 3985262, 3204339, 2395324, + 1564483, 723119, -105777, -909733, -1683889, -2405419, + -3074924, -3671133, -4188416, -4623151, -4966669, -5217822, + -5376505, -5438699, -5406246, -5282262, -5074698, -4786421, + -4415698, -3989649, -3503458, -2971553, -2398392, -1804213, + -1193619, -576390, 32462, 628385, 1194781, 1727140, + 2213574, 2652369, 3033564, 3352595, 3609052, 3797641, + 3911246, 3962598, 3939600, 3852699, 3701594, 3492114, + 3230424, 2916926, 2563901, 2181899, 1765468, 1336348, + 897255, 451524, 10400, -423355, -833387, -1217639, + -1571274, -1889254, -2165240, -2398526, -2581778, -2722473, + -2806177, -2843650, -2829585, -2769524, -2663939, -2516691, + -2327935, -2105389, -1854132, -1579438, -1284757, -976868, + -663162, -345625, -29806, 277631, 573265, 848377, + 1098155, 1323542, 1523169, 1692667, 1820388, 1921574, + 1980041, 2009485, 2000458, 1958506, 1882804, 1781576, + 1650184, 1491210, 1315288, 1123764, 920326, 700329, + 478507, 256533, 32026, -180003, -384095, -577687, + -756750, -917410, -1053764, -1170111, -1262779, -1332577, + -1375847, -1391609, -1392675, -1362480, -1311761, -1240430, + -1148291, -1039811, -917895, -784334, -643710, -496828, + -337593, -185408, -35103, 113070, 255859, 384329, + 506227, 617511, 714746, 795605, 860777, 906972, + 935925, 948904, 945758, 926027, 895773, 846226, + 784240, 710311, 628903, 537754, 444898, 341692, + 241505, 133447, 30958, -72515, -164395, -255320, + -334239, -410321, -475067, -526756, -573940, -605247, + -622819, -637482, -631446, -619838, -596390, -563572, + -523754, -477660, -426306, -363560, -295189, -230207, + -163718, -95093, -25083, 39224, 104809, 164493, + 216339, 268715, 307770, 343381, 376591, 393894, + 409214, 415048, 414650, 407176, 388288, 370122, + 344328, 309042, 277273, 235390, 198767, 149968, + 107391, 64774, 22734, -22484, -65777, -100024, + -134431, -168338, -196571, -218070, -239916, -252287, + -260207, -264251, -263475, -255361, -249920, -233428, + -219729, -200561, -178348, -152486, -126879, -98039, + -68509, -40108, -12510, 10116, 37364, 62735, + 83265, 105535, 122744, 134992, 146756, 157803, + 160999, 161373, 164013, 160462, 154774, 144884, + 134790, 126783, 107178, 92058, 76572, 57882, + 43557, 26865, 12612, -4122, -19611, -34312, + -50182, -64020, -71989, -80713, -88451, -93341, + -97703, -97951, -98271, -96134, -93821, -85291, + -81741, -74008, -66399, -57833, -47278, -35028, + -26476, -14656, -4700, 3292, 15438, 21244, + 29516, 33116, 42333, 47158, 49673, 56057, + 53914, 56647, 58612, 53778, 50181, 53182, + 46382, 38717, 37541, 30440, 26852, 21271, + 13076, 9386, 4881, -3217, -6456, -9167, + -14339, -21479, -24043, -24056, -31348, -29829, + -29018, -30209, -31831, -31798, -28027, -29569, + -25967, -24517, -21618, -18204, -15378, -12502, + -9278, -7266, -3404, 3091, 4309, 6623, + 9973, 9739, 10030, 12920, 16929, 18390, + 15259, 16991, 14629, 15567, 17209, 11571, + 14371, 12597, 11641, 9273, 6978, 8908, + 3725, -56, 2695, -189, -624, -3726, + -4651, -5443, -6305, -5793, -8874, -6473, + -7155, -7928, -4337, -5197, -10879, -8256, + -9328, -5819, -4801, -1061, -4140, -2848, + -5058, 450, 134, -988, -2654, -529, + 1305, -1503, 1327, 2653, 2727, 3765, + 1663, 2376, 5098, 3599, 2627, 4534, + 1709, 4472, 1626, 4200, -688, 1716, + 455, 2684, -1687, -2322, 540, 278, + 2327, 931, -1176, -108, -3519, -317, + -1225, -1108, -971, -844, -2534, -1462, + -554, -41, -29, -263, -2482, 614, + -2090, 576, -2654, -2080, 656, 174, + -297, -1757, 738, -493, 1359, 229, + 934, -956, -1365, -707, 3858, 1771, + 799, -1000, -779, -93, -1114, 804, + -1730, -1932, -731, 1052, 108, -1784, + -1167, -1073, 2003, 441, -543, -445, + -1474, 253, -2753, 1985, -2835, 1736, +}; + +static const int32_t ifft_ref_imag_3072_q31[3072] = { + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, +}; diff --git a/test/cmocka/src/math/matrix/matrix.c b/test/cmocka/src/math/matrix/matrix.c index 66200bc3e632..63632ba58c4b 100644 --- a/test/cmocka/src/math/matrix/matrix.c +++ b/test/cmocka/src/math/matrix/matrix.c @@ -4,6 +4,7 @@ // // Author: Seppo Ingalsuo <seppo.ingalsuo@linux.intel.com> +#include <sof/audio/module_adapter/module/generic.h> #include <errno.h> #include <stdlib.h> #include <stdio.h> @@ -23,6 +24,8 @@ #define MATRIX_MULT_16_MAX_ERROR_ABS 1.5 #define MATRIX_MULT_16_MAX_ERROR_RMS 0.5 +struct processing_module dummy; + static void matrix_mult_16_test(const int16_t *a_ref, const int16_t *b_ref, const int16_t *c_ref, int elementwise, int a_rows, int a_columns, int b_rows, int b_columns, int c_rows, int c_columns, @@ -38,20 +41,20 @@ static void matrix_mult_16_test(const int16_t *a_ref, const int16_t *b_ref, cons int16_t x; int i, j, k; - a_matrix = mat_matrix_alloc_16b(a_rows, a_columns, a_frac); + a_matrix = mod_mat_matrix_alloc_16b(&dummy, a_rows, a_columns, a_frac); if (!a_matrix) exit(EXIT_FAILURE); - b_matrix = mat_matrix_alloc_16b(b_rows, b_columns, b_frac); + b_matrix = mod_mat_matrix_alloc_16b(&dummy, b_rows, b_columns, b_frac); if (!b_matrix) { - free(a_matrix); + mod_free(&dummy, a_matrix); exit(EXIT_FAILURE); } - c_matrix = mat_matrix_alloc_16b(c_rows, c_columns, c_frac); + c_matrix = mod_mat_matrix_alloc_16b(&dummy, c_rows, c_columns, c_frac); if (!c_matrix) { - free(a_matrix); - free(b_matrix); + mod_free(&dummy, a_matrix); + mod_free(&dummy, b_matrix); exit(EXIT_FAILURE); } @@ -83,6 +86,10 @@ static void matrix_mult_16_test(const int16_t *a_ref, const int16_t *b_ref, cons assert_true(error_rms < MATRIX_MULT_16_MAX_ERROR_RMS); assert_true(delta_max < MATRIX_MULT_16_MAX_ERROR_ABS); + + mod_free(&dummy, a_matrix); + mod_free(&dummy, b_matrix); + mod_free(&dummy, c_matrix); } static void test_matrix_mult_16_test1(void **state) diff --git a/test/cmocka/src/math/numbers/CMakeLists.txt b/test/cmocka/src/math/numbers/CMakeLists.txt deleted file mode 100644 index 7a8c71670daf..000000000000 --- a/test/cmocka/src/math/numbers/CMakeLists.txt +++ /dev/null @@ -1,32 +0,0 @@ -# SPDX-License-Identifier: BSD-3-Clause - -cmocka_test(gcd - gcd.c - ${PROJECT_SOURCE_DIR}/src/math/numbers.c -) - -cmocka_test(ceil_divide - ceil_divide.c - ${PROJECT_SOURCE_DIR}/src/math/numbers.c -) -target_link_libraries(ceil_divide PRIVATE -lm) - -cmocka_test(find_equal_int16 - find_equal_int16.c - ${PROJECT_SOURCE_DIR}/src/math/numbers.c -) - -cmocka_test(find_min_int16 - find_min_int16.c - ${PROJECT_SOURCE_DIR}/src/math/numbers.c -) - -cmocka_test(find_max_abs_int32 - find_max_abs_int32.c - ${PROJECT_SOURCE_DIR}/src/math/numbers.c -) - -cmocka_test(norm_int32 - norm_int32.c - ${PROJECT_SOURCE_DIR}/src/math/numbers.c -) diff --git a/test/cmocka/src/math/numbers/ceil_divide.c b/test/cmocka/src/math/numbers/ceil_divide.c deleted file mode 100644 index 578df9b281b3..000000000000 --- a/test/cmocka/src/math/numbers/ceil_divide.c +++ /dev/null @@ -1,59 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -// -// Copyright(c) 2018 Intel Corporation. All rights reserved. -// -// Author: Slawomir Blauciak <slawomir.blauciak@linux.intel.com> - -#include <sof/math/numbers.h> - -#include <stdio.h> -#include <stdarg.h> -#include <stddef.h> -#include <setjmp.h> -#include <math.h> -#include <stdint.h> -#include <cmocka.h> - -static void test_math_numbers_ceil_divide(void **state) -{ - (void)state; - - int params[8] = { - -1000, - 300, - 123, - -10, - 1337, - -6, - 999, - -2 - }; - - int i, j; - - for (i = 0; i < 8; ++i) { - for (j = 0; j < 8; ++j) { - int ref = ceilf((float)params[i] / (float)params[j]); - int r = ceil_divide(params[i], params[j]); - - if (r != ref) { - printf("%s: %d / %d = %d (ref: %d)\n", __func__, - params[i], params[j], r, ref); - } - - assert_int_equal(r, ref); - } - } - -} - -int main(void) -{ - const struct CMUnitTest tests[] = { - cmocka_unit_test(test_math_numbers_ceil_divide) - }; - - cmocka_set_message_output(CM_OUTPUT_TAP); - - return cmocka_run_group_tests(tests, NULL, NULL); -} diff --git a/test/cmocka/src/math/numbers/find_equal_int16.c b/test/cmocka/src/math/numbers/find_equal_int16.c deleted file mode 100644 index fc202f50f75b..000000000000 --- a/test/cmocka/src/math/numbers/find_equal_int16.c +++ /dev/null @@ -1,55 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -// -// Copyright(c) 2018 Intel Corporation. All rights reserved. -// -// Author: Slawomir Blauciak <slawomir.blauciak@linux.intel.com> - -#include <sof/math/numbers.h> - -#include <stdarg.h> -#include <stddef.h> -#include <setjmp.h> -#include <stdint.h> -#include <cmocka.h> - -static void test_math_numbers_find_equal_int16_for_5_123_5_10_123_500_123_n_123_equals_1_4_and_6 - (void **state) -{ - (void)state; - - int16_t r[4]; - int16_t vec[] = {5, 123, 5, 10, 123, 500, 123}; - int16_t template[] = {1, 4, 6}; - - int r_num = find_equal_int16(r, vec, 123, 7, 4); - - assert_int_equal(r_num, 3); - assert_memory_equal(r, template, sizeof(int16_t) * 3); -} - -static void test_math_numbers_find_equal_int16_for_1_2_3_4_5_n_0_equals_nothing - (void **state) -{ - (void)state; - - int16_t r[4]; - int16_t vec[] = {1, 2, 3, 4, 5}; - - int r_num = find_equal_int16(r, vec, 0, 5, 4); - - assert_int_equal(r_num, 0); -} - -int main(void) -{ - const struct CMUnitTest tests[] = { - cmocka_unit_test - (test_math_numbers_find_equal_int16_for_5_123_5_10_123_500_123_n_123_equals_1_4_and_6), - cmocka_unit_test - (test_math_numbers_find_equal_int16_for_1_2_3_4_5_n_0_equals_nothing) - }; - - cmocka_set_message_output(CM_OUTPUT_TAP); - - return cmocka_run_group_tests(tests, NULL, NULL); -} diff --git a/test/cmocka/src/math/numbers/find_max_abs_int32.c b/test/cmocka/src/math/numbers/find_max_abs_int32.c deleted file mode 100644 index aa669887dcab..000000000000 --- a/test/cmocka/src/math/numbers/find_max_abs_int32.c +++ /dev/null @@ -1,50 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -// -// Copyright(c) 2018 Intel Corporation. All rights reserved. -// -// Author: Slawomir Blauciak <slawomir.blauciak@linux.intel.com> - -#include <sof/math/numbers.h> -#include <sof/common.h> - -#include <stdarg.h> -#include <stddef.h> -#include <setjmp.h> -#include <stdint.h> -#include <cmocka.h> - -static void test_math_numbers_find_max_abs_int32_for_neg100_99_98_50_equals_100 - (void **state) -{ - (void)state; - - int32_t vec[] = {-100, 99, 98, 50}; - int r = find_max_abs_int32(vec, ARRAY_SIZE(vec)); - - assert_int_equal(r, 100); -} - -static void test_math_numbers_find_max_abs_int32_for_neg100_99_98_50_101_equals_101 - (void **state) -{ - (void)state; - - int32_t vec[] = {-100, 99, 98, 50, 101}; - int r = find_max_abs_int32(vec, ARRAY_SIZE(vec)); - - assert_int_equal(r, 101); -} - -int main(void) -{ - const struct CMUnitTest tests[] = { - cmocka_unit_test - (test_math_numbers_find_max_abs_int32_for_neg100_99_98_50_equals_100), - cmocka_unit_test - (test_math_numbers_find_max_abs_int32_for_neg100_99_98_50_101_equals_101) - }; - - cmocka_set_message_output(CM_OUTPUT_TAP); - - return cmocka_run_group_tests(tests, NULL, NULL); -} diff --git a/test/cmocka/src/math/numbers/find_min_int16.c b/test/cmocka/src/math/numbers/find_min_int16.c deleted file mode 100644 index 919898fe5710..000000000000 --- a/test/cmocka/src/math/numbers/find_min_int16.c +++ /dev/null @@ -1,49 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -// -// Copyright(c) 2018 Intel Corporation. All rights reserved. -// -// Author: Slawomir Blauciak <slawomir.blauciak@linux.intel.com> - -#include <sof/math/numbers.h> -#include <sof/common.h> - -#include <stdarg.h> -#include <stddef.h> -#include <setjmp.h> -#include <stdint.h> -#include <cmocka.h> - -static void test_math_numbers_find_min_int16_for_2_equals_2(void **state) -{ - (void)state; - - int16_t vec[] = {2}; - int r = find_min_int16(vec, ARRAY_SIZE(vec)); - - assert_int_equal(r, 2); -} - -static void test_math_numbers_find_min_int16_for_5_2_3_4_1_equals_1 - (void **state) -{ - (void)state; - - int16_t vec[] = {5, 2, 3, 4, 1}; - int r = find_min_int16(vec, ARRAY_SIZE(vec)); - - assert_int_equal(r, 1); -} - -int main(void) -{ - const struct CMUnitTest tests[] = { - cmocka_unit_test - (test_math_numbers_find_min_int16_for_2_equals_2), - cmocka_unit_test - (test_math_numbers_find_min_int16_for_5_2_3_4_1_equals_1) - }; - - cmocka_set_message_output(CM_OUTPUT_TAP); - - return cmocka_run_group_tests(tests, NULL, NULL); -} diff --git a/test/cmocka/src/math/numbers/gcd.c b/test/cmocka/src/math/numbers/gcd.c deleted file mode 100644 index 23f2dad2191a..000000000000 --- a/test/cmocka/src/math/numbers/gcd.c +++ /dev/null @@ -1,116 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -// -// Copyright(c) 2018 Intel Corporation. All rights reserved. -// -// Author: Marcin Maka <marcin.maka@linux.intel.com> -// Janusz Jankowski <janusz.jankowski@linux.intel.com> - -#include <sof/math/numbers.h> - -#include <stdarg.h> -#include <stddef.h> -#include <setjmp.h> -#include <stdint.h> -#include <cmocka.h> - -static void test_math_numbers_gcd_for_5083_and_391_equals_391(void **state) -{ - int r; - - (void)state; - - r = gcd(5083, 391); - assert_int_equal(r, 391); -} - -static void test_math_numbers_gcd_for_12_and_9_equals_3(void **state) -{ - int r; - - (void)state; - - r = gcd(12, 9); - assert_int_equal(r, 3); -} - -static void test_math_numbers_gcd_for_5_and_0_equals_5(void **state) -{ - int r; - - (void)state; - - r = gcd(5, 0); - assert_int_equal(r, 5); -} - -static void test_math_numbers_gcd_for_0_and_5_equals_5(void **state) -{ - int r; - - (void)state; - - r = gcd(0, 5); - assert_int_equal(r, 5); -} - -static void test_math_numbers_gcd_for_0_and_0_equals_0(void **state) -{ - int r; - - (void)state; - - r = gcd(0, 0); - assert_int_equal(r, 0); -} - -static void test_math_numbers_gcd_for_neg_4_and_14_equals_2(void **state) -{ - int r; - - (void)state; - - r = gcd(-4, 14); - assert_int_equal(r, 2); -} - -static void test_math_numbers_gcd_for_4_and_neg_14_equals_2(void **state) -{ - int r; - - (void)state; - - r = gcd(4, -14); - assert_int_equal(r, 2); -} - -static void test_math_numbers_gcd_for_neg_4_and_neg_14_equals_2(void **state) -{ - int r; - - (void)state; - - r = gcd(-4, -14); - assert_int_equal(r, 2); -} - -int main(void) -{ - const struct CMUnitTest tests[] = { - cmocka_unit_test - (test_math_numbers_gcd_for_5083_and_391_equals_391), - cmocka_unit_test(test_math_numbers_gcd_for_12_and_9_equals_3), - cmocka_unit_test(test_math_numbers_gcd_for_5_and_0_equals_5), - cmocka_unit_test(test_math_numbers_gcd_for_0_and_5_equals_5), - cmocka_unit_test(test_math_numbers_gcd_for_0_and_0_equals_0), - cmocka_unit_test - (test_math_numbers_gcd_for_neg_4_and_14_equals_2), - cmocka_unit_test - (test_math_numbers_gcd_for_4_and_neg_14_equals_2), - cmocka_unit_test - (test_math_numbers_gcd_for_neg_4_and_neg_14_equals_2) - }; - - cmocka_set_message_output(CM_OUTPUT_TAP); - - return cmocka_run_group_tests(tests, NULL, NULL); -} diff --git a/test/cmocka/src/math/numbers/norm_int32.c b/test/cmocka/src/math/numbers/norm_int32.c deleted file mode 100644 index 7397d2359c7a..000000000000 --- a/test/cmocka/src/math/numbers/norm_int32.c +++ /dev/null @@ -1,55 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -// -// Copyright(c) 2018 Intel Corporation. All rights reserved. -// -// Author: Slawomir Blauciak <slawomir.blauciak@linux.intel.com> - -#include <sof/math/numbers.h> - -#include <stdarg.h> -#include <stddef.h> -#include <setjmp.h> -#include <stdint.h> -#include <cmocka.h> - -static void test_math_numbers_norm_int32_for_0_equals_31(void **state) -{ - (void)state; - - int r = norm_int32(0); - - assert_int_equal(r, 31); -} - -static void test_math_numbers_norm_int32_for_35_equals_10(void **state) -{ - (void)state; - - int r = norm_int32(35); - - assert_int_equal(r, 25); -} - -static void test_math_numbers_norm_int32_for_2147483647_equals_0(void **state) -{ - (void)state; - - int r = norm_int32(2147483647); - - assert_int_equal(r, 0); -} - -int main(void) -{ - const struct CMUnitTest tests[] = { - cmocka_unit_test(test_math_numbers_norm_int32_for_0_equals_31), - cmocka_unit_test - (test_math_numbers_norm_int32_for_35_equals_10), - cmocka_unit_test - (test_math_numbers_norm_int32_for_2147483647_equals_0) - }; - - cmocka_set_message_output(CM_OUTPUT_TAP); - - return cmocka_run_group_tests(tests, NULL, NULL); -} diff --git a/test/cmocka/src/math/trig/CMakeLists.txt b/test/cmocka/src/math/trig/CMakeLists.txt deleted file mode 100644 index 2d39eecd27b0..000000000000 --- a/test/cmocka/src/math/trig/CMakeLists.txt +++ /dev/null @@ -1,46 +0,0 @@ -# SPDX-License-Identifier: BSD-3-Clause - -cmocka_test(sin_32b_fixed - sin_32b_fixed.c - ${PROJECT_SOURCE_DIR}/src/math/trig.c -) - -cmocka_test(cos_32b_fixed - cos_32b_fixed.c - ${PROJECT_SOURCE_DIR}/src/math/trig.c -) - -cmocka_test(sin_16b_fixed - sin_16b_fixed.c - ${PROJECT_SOURCE_DIR}/src/math/trig.c -) - -cmocka_test(cos_16b_fixed - cos_16b_fixed.c - ${PROJECT_SOURCE_DIR}/src/math/trig.c -) - -cmocka_test(asin_32b_fixed - asin_32b_fixed.c - ${PROJECT_SOURCE_DIR}/src/math/trig.c -) - -cmocka_test(acos_32b_fixed - acos_32b_fixed.c - ${PROJECT_SOURCE_DIR}/src/math/trig.c -) - -cmocka_test(asin_16b_fixed - asin_16b_fixed.c - ${PROJECT_SOURCE_DIR}/src/math/trig.c -) - -cmocka_test(acos_16b_fixed - acos_16b_fixed.c - ${PROJECT_SOURCE_DIR}/src/math/trig.c -) - -cmocka_test(lut_sin_16b_fixed - lut_sin_16b_fixed.c - ${PROJECT_SOURCE_DIR}/src/math/lut_trig.c -) diff --git a/test/cmocka/src/math/trig/acos_16b_fixed.c b/test/cmocka/src/math/trig/acos_16b_fixed.c deleted file mode 100644 index b0454b104dd9..000000000000 --- a/test/cmocka/src/math/trig/acos_16b_fixed.c +++ /dev/null @@ -1,62 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -// -// Copyright(c) 2021 Intel Corporation. All rights reserved. -// -// Author: Shriram Shastry <malladi.sastry@linux.intel.com> -// - -#include <stdio.h> -#include <stdint.h> -#include <stdarg.h> -#include <stddef.h> -#include <setjmp.h> -#include <math.h> -#include <cmocka.h> - -#include <sof/audio/format.h> -#include <sof/math/trig.h> -#include <sof/common.h> -#include "trig_tables.h" -/* ' Error (max = 0.000059799232976), THD+N = -89.824298401466635 (dBc)' */ -#define CMP_TOLERANCE 0.0001196862 -#define _M_PI 3.14159265358979323846 /* pi */ - -static void test_math_trig_acos_16b_fixed(void **state) -{ - (void)state; - double u; - double v; - int indx; - int b_i; - - for (indx = 0; indx < ARRAY_SIZE(degree_table); ++indx) { - /* convert angle unit degrees to radians */ - /* angleInRadians = pi/180 * angleInDegrees & const Q2.30 format */ - u = (0.017453292519943295 * (double)degree_table[indx] * 0x40000000); - v = fabs(u); - /* GitHub macro Q_CONVERT_FLOAT is inaccurate, so replaced with below */ - u = (v >= 0.5) ? floor(u + 0.5) : 0.0; - b_i = (int)u; - - float r = Q_CONVERT_QTOF(acos_fixed_16b(b_i), 13); - float diff = fabsf(acos_ref_table[indx] - r); - - if (diff > CMP_TOLERANCE) { - printf("%s: diff for %.16f deg = %.10f\n", __func__, - ((180 / _M_PI) * b_i) / (1 << 30), diff); - } - - assert_true(diff <= CMP_TOLERANCE); - } -} - -int main(void) -{ - const struct CMUnitTest tests[] = { - cmocka_unit_test(test_math_trig_acos_16b_fixed) - }; - - cmocka_set_message_output(CM_OUTPUT_TAP); - - return cmocka_run_group_tests(tests, NULL, NULL); -} diff --git a/test/cmocka/src/math/trig/acos_32b_fixed.c b/test/cmocka/src/math/trig/acos_32b_fixed.c deleted file mode 100644 index 01c7a754ba75..000000000000 --- a/test/cmocka/src/math/trig/acos_32b_fixed.c +++ /dev/null @@ -1,62 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -// -// Copyright(c) 2021 Intel Corporation. All rights reserved. -// -// Author: Shriram Shastry <malladi.sastry@linux.intel.com> -// - -#include <stdio.h> -#include <stdint.h> -#include <stdarg.h> -#include <stddef.h> -#include <setjmp.h> -#include <math.h> -#include <cmocka.h> - -#include <sof/audio/format.h> -#include <sof/math/trig.h> -#include <sof/common.h> -#include "trig_tables.h" -/* 'Error (max = 0.000000026077032), THD+N = -157.948952635422842 (dBc)' */ -#define CMP_TOLERANCE 0.000000060077032 -#define _M_PI 3.14159265358979323846 /* pi */ - -static void test_math_trig_acos_32b_fixed(void **state) -{ - (void)state; - double u; - double v; - int indx; - int b_i; - - for (indx = 0; indx < ARRAY_SIZE(degree_table); ++indx) { - /* convert angle unit degrees to radians */ - /* angleInRadians = pi/180 * angleInDegrees & const Q2.30 format */ - u = (0.017453292519943295 * (double)degree_table[indx] * 0x40000000); - v = fabs(u); - /* GitHub macro Q_CONVERT_FLOAT is inaccurate, so replaced with below */ - u = (v >= 0.5) ? floor(u + 0.5) : 0.0; - b_i = (int)u; - - float r = Q_CONVERT_QTOF(acos_fixed_32b(b_i), 29); - float diff = fabsf(acos_ref_table[indx] - r); - - if (diff > CMP_TOLERANCE) { - printf("%s: diff for %.16f deg = %.10f\n", __func__, - ((180 / _M_PI) * b_i) / (1 << 30), diff); - } - - assert_true(diff <= CMP_TOLERANCE); - } -} - -int main(void) -{ - const struct CMUnitTest tests[] = { - cmocka_unit_test(test_math_trig_acos_32b_fixed) - }; - - cmocka_set_message_output(CM_OUTPUT_TAP); - - return cmocka_run_group_tests(tests, NULL, NULL); -} diff --git a/test/cmocka/src/math/trig/asin_16b_fixed.c b/test/cmocka/src/math/trig/asin_16b_fixed.c deleted file mode 100644 index 8361c8aa5858..000000000000 --- a/test/cmocka/src/math/trig/asin_16b_fixed.c +++ /dev/null @@ -1,62 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -// -// Copyright(c) 2018 Intel Corporation. All rights reserved. -// -// Author: Shriram Shastry <malladi.sastry@linux.intel.com> - -#include <stdio.h> -#include <stdint.h> -#include <stdarg.h> -#include <stddef.h> -#include <setjmp.h> -#include <math.h> -#include <cmocka.h> - -#include <sof/audio/format.h> -#include <sof/math/trig.h> -#include <sof/common.h> -#include "trig_tables.h" -/* 'Error (max = 0.000059799232976), THD+N = -89.824298401466635 (dBc) */ -#define CMP_TOLERANCE 0.0001152158 -#define _M_PI 3.14159265358979323846 /* pi */ - -static void test_math_trig_asin_16b_fixed(void **state) -{ - (void)state; - - double u; - double v; - int indx; - int b_i; - - for (indx = 0; indx < ARRAY_SIZE(degree_table); ++indx) { - /* convert angle unit degrees to radians */ - /* angleInRadians = pi/180 * angleInDegrees & const Q2.30 format */ - u = (0.017453292519943295 * (double)degree_table[indx] * 0x40000000); - v = fabs(u); - /* GitHub macro Q_CONVERT_FLOAT is inaccurate, so replaced with below */ - u = (v >= 0.5) ? floor(u + 0.5) : 0.0; - b_i = (int)u; - - float r = Q_CONVERT_QTOF(asin_fixed_16b(b_i), 13); - float diff = fabsf(asin_ref_table[indx] - r); - - if (diff > CMP_TOLERANCE) { - printf("%s: diff for %.16f deg = %.10f\n", __func__, - ((180 / _M_PI) * b_i) / (1 << 30), diff); - } - - assert_true(diff <= CMP_TOLERANCE); - } -} - -int main(void) -{ - const struct CMUnitTest tests[] = { - cmocka_unit_test(test_math_trig_asin_16b_fixed) - }; - - cmocka_set_message_output(CM_OUTPUT_TAP); - - return cmocka_run_group_tests(tests, NULL, NULL); -} diff --git a/test/cmocka/src/math/trig/asin_32b_fixed.c b/test/cmocka/src/math/trig/asin_32b_fixed.c deleted file mode 100644 index f830711a9761..000000000000 --- a/test/cmocka/src/math/trig/asin_32b_fixed.c +++ /dev/null @@ -1,62 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -// -// Copyright(c) 2018 Intel Corporation. All rights reserved. -// -// Author: Shriram Shastry <malladi.sastry@linux.intel.com> - -#include <stdio.h> -#include <stdint.h> -#include <stdarg.h> -#include <stddef.h> -#include <setjmp.h> -#include <math.h> -#include <cmocka.h> - -#include <sof/audio/format.h> -#include <sof/math/trig.h> -#include <sof/common.h> -#include "trig_tables.h" -/* 'Error (max = 0.000000027939677), THD+N = -157.454534077921551 (dBc)' */ -#define CMP_TOLERANCE 0.000000068141916 -#define _M_PI 3.14159265358979323846 /* pi */ - -static void test_math_trig_asin_32b_fixed(void **state) -{ - (void)state; - - double u; - double v; - int indx; - int b_i; - - for (indx = 0; indx < ARRAY_SIZE(degree_table); ++indx) { - /* convert angle unit degrees to radians */ - /* angleInRadians = pi/180 * angleInDegrees & const Q2.30 format */ - u = (0.017453292519943295 * (double)degree_table[indx] * 0x40000000); - v = fabs(u); - /* GitHub macro Q_CONVERT_FLOAT is inaccurate, so replaced with below */ - u = (v >= 0.5) ? floor(u + 0.5) : 0.0; - b_i = (int)u; - - float r = Q_CONVERT_QTOF(asin_fixed_32b(b_i), 29); - float diff = fabsf(asin_ref_table[indx] - r); - - if (diff > CMP_TOLERANCE) { - printf("%s: diff for %.16f deg = %.10f\n", __func__, - ((180 / _M_PI) * b_i) / (1 << 30), diff); - } - - assert_true(diff <= CMP_TOLERANCE); - } -} - -int main(void) -{ - const struct CMUnitTest tests[] = { - cmocka_unit_test(test_math_trig_asin_32b_fixed) - }; - - cmocka_set_message_output(CM_OUTPUT_TAP); - - return cmocka_run_group_tests(tests, NULL, NULL); -} diff --git a/test/cmocka/src/math/trig/cos_16b_fixed.c b/test/cmocka/src/math/trig/cos_16b_fixed.c deleted file mode 100644 index 2049a03c23f8..000000000000 --- a/test/cmocka/src/math/trig/cos_16b_fixed.c +++ /dev/null @@ -1,53 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -// -// Copyright(c) 2021 Intel Corporation. All rights reserved. -// -// Author: Shriram Shastry <malladi.sastry@linux.intel.com> -// - -#include <stdio.h> -#include <stdint.h> -#include <stdarg.h> -#include <stddef.h> -#include <setjmp.h> -#include <math.h> -#include <cmocka.h> - -#include <sof/audio/format.h> -#include <sof/math/trig.h> -#include "trig_tables.h" -/* 'Error (max = 0.000061), THD+N = -91.518584' */ -#define CMP_TOLERANCE 0.000065 -#define _M_PI 3.14159265358979323846 /* pi */ - -static void test_math_trig_cos_fixed(void **state) -{ - (void)state; - int theta; - - for (theta = 0; theta < 360; ++theta) { - double rad = _M_PI * (theta / 180.0); - int32_t rad_q28 = Q_CONVERT_FLOAT(rad, 28); - - float r = Q_CONVERT_QTOF(cos_fixed_16b(rad_q28), 15); - float diff = fabsf(cos_ref_table[theta] - r); - - if (diff > CMP_TOLERANCE) { - printf("%s: diff for %d deg = %.10f\n", __func__, - theta, diff); - } - - assert_true(diff <= CMP_TOLERANCE); - } -} - -int main(void) -{ - const struct CMUnitTest tests[] = { - cmocka_unit_test(test_math_trig_cos_fixed) - }; - - cmocka_set_message_output(CM_OUTPUT_TAP); - - return cmocka_run_group_tests(tests, NULL, NULL); -} diff --git a/test/cmocka/src/math/trig/cos_32b_fixed.c b/test/cmocka/src/math/trig/cos_32b_fixed.c deleted file mode 100644 index d9c2282852dd..000000000000 --- a/test/cmocka/src/math/trig/cos_32b_fixed.c +++ /dev/null @@ -1,53 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -// -// Copyright(c) 2021 Intel Corporation. All rights reserved. -// -// Author: Shriram Shastry <malladi.sastry@linux.intel.com> -// - -#include <stdio.h> -#include <stdint.h> -#include <stdarg.h> -#include <stddef.h> -#include <setjmp.h> -#include <math.h> -#include <cmocka.h> - -#include <sof/audio/format.h> -#include <sof/math/trig.h> -#include "trig_tables.h" -/* 'Error (max = 0.000000011175871), THD+N = -170.674358910916226' */ -#define CMP_TOLERANCE 0.0000000611175871 -#define _M_PI 3.14159265358979323846 /* pi */ - -static void test_math_trig_cos_fixed(void **state) -{ - (void)state; - int theta; - - for (theta = 0; theta < 360; ++theta) { - double rad = _M_PI * (theta / 180.0); - int32_t rad_q28 = Q_CONVERT_FLOAT(rad, 28); - - float r = Q_CONVERT_QTOF(cos_fixed_32b(rad_q28), 31); - float diff = fabsf(cos_ref_table[theta] - r); - - if (diff > CMP_TOLERANCE) { - printf("%s: diff for %d deg = %.10f\n", __func__, - theta, diff); - } - - assert_true(diff <= CMP_TOLERANCE); - } -} - -int main(void) -{ - const struct CMUnitTest tests[] = { - cmocka_unit_test(test_math_trig_cos_fixed) - }; - - cmocka_set_message_output(CM_OUTPUT_TAP); - - return cmocka_run_group_tests(tests, NULL, NULL); -} diff --git a/test/cmocka/src/math/trig/lut_sin_16b_fixed.c b/test/cmocka/src/math/trig/lut_sin_16b_fixed.c deleted file mode 100644 index b95390022903..000000000000 --- a/test/cmocka/src/math/trig/lut_sin_16b_fixed.c +++ /dev/null @@ -1,54 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -// -// Copyright(c) 2024 Intel Corporation. All rights reserved. -// -// Author: Slawomir Blauciak <slawomir.blauciak@linux.intel.com> -// Author: Seppo Ingalsuo <seppo.ingalsuo@linux.intel.com> - -#include <stdio.h> -#include <stdint.h> -#include <stdarg.h> -#include <stddef.h> -#include <setjmp.h> -#include <math.h> -#include <cmocka.h> - -#include <sof/audio/format.h> -#include <sof/math/lut_trig.h> - -#include "trig_tables.h" - -#define CMP_TOLERANCE 3.1e-5 -#define _M_PI 3.14159265358979323846 /* pi */ - -static void test_math_trig_lut_sin_fixed(void **state) -{ - (void)state; - - int theta; - - for (theta = 0; theta < 360; ++theta) { - double rad = _M_PI / 180.0 * theta; - int32_t rad_q28 = Q_CONVERT_FLOAT(rad, 28); - float r = Q_CONVERT_QTOF(sofm_lut_sin_fixed_16b(rad_q28), 15); - float diff = fabsf(sin_ref_table[theta] - r); - - if (diff > CMP_TOLERANCE) { - printf("%s: diff for %d deg = %g\n", __func__, - theta, diff); - } - - assert_true(diff <= CMP_TOLERANCE); - } -} - -int main(void) -{ - const struct CMUnitTest tests[] = { - cmocka_unit_test(test_math_trig_lut_sin_fixed) - }; - - cmocka_set_message_output(CM_OUTPUT_TAP); - - return cmocka_run_group_tests(tests, NULL, NULL); -} diff --git a/test/cmocka/src/math/trig/sin_16b_fixed.c b/test/cmocka/src/math/trig/sin_16b_fixed.c deleted file mode 100644 index 47864c41d669..000000000000 --- a/test/cmocka/src/math/trig/sin_16b_fixed.c +++ /dev/null @@ -1,53 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -// -// Copyright(c) 2018 Intel Corporation. All rights reserved. -// -// Author: Shriram Shastry <malladi.sastry@linux.intel.com> - -#include <stdio.h> -#include <stdint.h> -#include <stdarg.h> -#include <stddef.h> -#include <setjmp.h> -#include <math.h> -#include <cmocka.h> - -#include <sof/audio/format.h> -#include <sof/math/trig.h> -#include "trig_tables.h" -/* 'Error (max = 0.000061), THD+N = -91.502670' */ -#define CMP_TOLERANCE 0.000065 -#define _M_PI 3.14159265358979323846 /* pi */ - -static void test_math_trig_sin_fixed(void **state) -{ - (void)state; - - int theta; - - for (theta = 0; theta < 360; ++theta) { - double rad = _M_PI * (theta / 180.0); - int32_t rad_q28 = Q_CONVERT_FLOAT(rad, 28); - - float r = Q_CONVERT_QTOF(sin_fixed_16b(rad_q28), 15); - float diff = fabsf(sin_ref_table[theta] - r); - - if (diff > CMP_TOLERANCE) { - printf("%s: diff for %d deg = %.10f\n", __func__, - theta, diff); - } - - assert_true(diff <= CMP_TOLERANCE); - } -} - -int main(void) -{ - const struct CMUnitTest tests[] = { - cmocka_unit_test(test_math_trig_sin_fixed) - }; - - cmocka_set_message_output(CM_OUTPUT_TAP); - - return cmocka_run_group_tests(tests, NULL, NULL); -} diff --git a/test/cmocka/src/math/trig/sin_32b_fixed.c b/test/cmocka/src/math/trig/sin_32b_fixed.c deleted file mode 100644 index 6db3ffcbc862..000000000000 --- a/test/cmocka/src/math/trig/sin_32b_fixed.c +++ /dev/null @@ -1,53 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -// -// Copyright(c) 2018 Intel Corporation. All rights reserved. -// -// Author: Slawomir Blauciak <slawomir.blauciak@linux.intel.com> - -#include <stdio.h> -#include <stdint.h> -#include <stdarg.h> -#include <stddef.h> -#include <setjmp.h> -#include <math.h> -#include <cmocka.h> - -#include <sof/audio/format.h> -#include <sof/math/trig.h> -#include "trig_tables.h" -/* 'Error (max = 0.000000011175871), THD+N = -170.152933168271659' */ -#define CMP_TOLERANCE 0.0000000611175871 -#define _M_PI 3.14159265358979323846 /* pi */ - -static void test_math_trig_sin_fixed(void **state) -{ - (void)state; - - int theta; - - for (theta = 0; theta < 360; ++theta) { - double rad = _M_PI * (theta / 180.0); - int32_t rad_q28 = Q_CONVERT_FLOAT(rad, 28); - - float r = Q_CONVERT_QTOF(sin_fixed_32b(rad_q28), 31); - float diff = fabsf(sin_ref_table[theta] - r); - - if (diff > CMP_TOLERANCE) { - printf("%s: diff for %d deg = %.10f\n", __func__, - theta, diff); - } - - assert_true(diff <= CMP_TOLERANCE); - } -} - -int main(void) -{ - const struct CMUnitTest tests[] = { - cmocka_unit_test(test_math_trig_sin_fixed) - }; - - cmocka_set_message_output(CM_OUTPUT_TAP); - - return cmocka_run_group_tests(tests, NULL, NULL); -} diff --git a/test/cmocka/src/math/window/window.c b/test/cmocka/src/math/window/window.c index 3e04c452f6bd..82e0f66a6bec 100644 --- a/test/cmocka/src/math/window/window.c +++ b/test/cmocka/src/math/window/window.c @@ -54,7 +54,7 @@ static float test_window(char *window_name, const int16_t *ref_win, int window_l if (!strcmp(window_name, "rectangular")) { win_rectangular_16b(win, window_length); } else if (!strcmp(window_name, "blackman")) { - win_blackman_16b(win, window_length, WIN_BLACKMAN_A0); + win_blackman_16b(win, window_length, WIN_BLACKMAN_A0_Q15); } else if (!strcmp(window_name, "hamming")) { win_hamming_16b(win, window_length); } else if (!strcmp(window_name, "povey")) { diff --git a/test/cmocka/src/util.h b/test/cmocka/src/util.h index 42d0700804c6..38561e81e42e 100644 --- a/test/cmocka/src/util.h +++ b/test/cmocka/src/util.h @@ -23,7 +23,7 @@ static inline struct comp_buffer *create_test_sink(struct comp_dev *dev, }, .size = buffer_size, }; - struct comp_buffer *buffer = buffer_new(&desc, BUFFER_USAGE_NOT_SHARED); + struct comp_buffer *buffer = buffer_new(NULL, &desc, BUFFER_USAGE_NOT_SHARED); memset(buffer->stream.addr, 0, buffer_size); @@ -58,7 +58,7 @@ static inline struct comp_buffer *create_test_source(struct comp_dev *dev, }, .size = buffer_size, }; - struct comp_buffer *buffer = buffer_new(&desc, BUFFER_USAGE_NOT_SHARED); + struct comp_buffer *buffer = buffer_new(NULL, &desc, BUFFER_USAGE_NOT_SHARED); memset(buffer->stream.addr, 0, buffer_size); diff --git a/test/ztest/unit/common/alloc.c b/test/ztest/unit/common/alloc.c new file mode 100644 index 000000000000..d00dc3c7b831 --- /dev/null +++ b/test/ztest/unit/common/alloc.c @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* Copyright(c) 2026 Intel Corporation. All rights reserved. */ + +#include <zephyr/ztest.h> +#include <sof/common.h> + +#include <stddef.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> + +void *__wrap_rzalloc(uint32_t flags, size_t bytes) +{ + void *ret; + (void)flags; + + ret = malloc(bytes); + + zassert_not_null(ret, "Memory allocation should not fail"); + + memset(ret, 0, bytes); + + return ret; +} + +void __wrap_rfree(void *ptr) +{ + free(ptr); +} + +struct k_heap; +void *__wrap_sof_heap_alloc(struct k_heap *heap, uint32_t flags, size_t bytes, size_t alignment) +{ + void *ret; + + (void)flags; + (void)heap; + + if (alignment) + ret = aligned_alloc(alignment, ALIGN_UP(bytes, alignment)); + else + ret = malloc(bytes); + + zassert_not_null(ret, "Memory allocation should not fail"); + + return ret; +} + +void __wrap_sof_heap_free(struct k_heap *heap, void *ptr) +{ + (void)heap; + + free(ptr); +} + +struct k_heap *__wrap_sof_sys_heap_get(void) +{ + return NULL; +} diff --git a/test/ztest/unit/fast-get/CMakeLists.txt b/test/ztest/unit/fast-get/CMakeLists.txt index 45de96da341c..39b79142d8bd 100644 --- a/test/ztest/unit/fast-get/CMakeLists.txt +++ b/test/ztest/unit/fast-get/CMakeLists.txt @@ -16,16 +16,17 @@ target_include_directories(app PRIVATE # Define SOF-specific configurations for unit testing target_compile_definitions(app PRIVATE - -DCONFIG_SOF_LOG_LEVEL=CONFIG_LOG_DEFAULT_LEVEL -DCONFIG_ZEPHYR_POSIX=1 ) target_sources(app PRIVATE test_fast_get_ztest.c + ${SOF_ROOT}/src/lib/objpool.c ${SOF_ROOT}/zephyr/lib/fast-get.c + ${SOF_ROOT}/test/ztest/unit/common/alloc.c ) -target_link_libraries(app PRIVATE "-Wl,--wrap=rzalloc,--wrap=rmalloc,--wrap=rfree") +target_link_libraries(app PRIVATE "-Wl,--wrap=rzalloc,--wrap=rfree,--wrap=sof_heap_alloc,--wrap=sof_heap_free") # Add RELATIVE_FILE definitions for SOF trace functionality sof_append_relative_path_definitions(app) diff --git a/test/ztest/unit/fast-get/prj.conf b/test/ztest/unit/fast-get/prj.conf index 9467c2926896..d34c7781cd0a 100644 --- a/test/ztest/unit/fast-get/prj.conf +++ b/test/ztest/unit/fast-get/prj.conf @@ -1 +1,2 @@ CONFIG_ZTEST=y +CONFIG_SOF_FULL_ZEPHYR_APPLICATION=n diff --git a/test/ztest/unit/fast-get/test_fast_get_ztest.c b/test/ztest/unit/fast-get/test_fast_get_ztest.c index f43f9c1bbd14..3022d80e5ab7 100644 --- a/test/ztest/unit/fast-get/test_fast_get_ztest.c +++ b/test/ztest/unit/fast-get/test_fast_get_ztest.c @@ -6,6 +6,7 @@ // generative artificial intelligence solutions. #include <zephyr/ztest.h> +#include <sof/common.h> #include <sof/lib/fast-get.h> #include <stdlib.h> @@ -55,39 +56,6 @@ static const int testdata[33][100] = { { 33 }, }; -/* Mock memory allocation functions for testing purposes */ - -void *__wrap_rzalloc(uint32_t flags, size_t bytes) -{ - void *ret; - (void)flags; - - ret = malloc(bytes); - - zassert_not_null(ret, "Memory allocation should not fail"); - - memset(ret, 0, bytes); - - return ret; -} - -void *__wrap_rmalloc(uint32_t flags, size_t bytes) -{ - void *ret; - (void)flags; - - ret = malloc(bytes); - - zassert_not_null(ret, "Memory allocation should not fail"); - - return ret; -} - -void __wrap_rfree(void *ptr) -{ - free(ptr); -} - /** * @brief Test basic fast_get and fast_put functionality * @@ -98,13 +66,13 @@ ZTEST(fast_get_suite, test_simple_fast_get_put) { const void *ret; - ret = fast_get(testdata[0], sizeof(testdata[0])); + ret = fast_get(NULL, testdata[0], sizeof(testdata[0])); zassert_not_null(ret, "fast_get should return valid pointer"); zassert_mem_equal(ret, testdata[0], sizeof(testdata[0]), "Returned data should match original data"); - fast_put(ret); + fast_put(NULL, NULL, ret); } /** @@ -117,16 +85,16 @@ ZTEST(fast_get_suite, test_fast_get_size_missmatch_test) { const void *ret[2]; - ret[0] = fast_get(testdata[0], sizeof(testdata[0])); + ret[0] = fast_get(NULL, testdata[0], sizeof(testdata[0])); zassert_not_null(ret[0], "First fast_get should succeed"); zassert_mem_equal(ret[0], testdata[0], sizeof(testdata[0]), "Returned data should match original data"); - ret[1] = fast_get(testdata[0], sizeof(testdata[0]) + 1); + ret[1] = fast_get(NULL, testdata[0], sizeof(testdata[0]) + 1); zassert_is_null(ret[1], "fast_get with different size should return NULL"); - fast_put(ret[0]); + fast_put(NULL, NULL, ret[0]); } /** @@ -141,14 +109,14 @@ ZTEST(fast_get_suite, test_over_32_fast_gets_and_puts) int i; for (i = 0; i < ARRAY_SIZE(copy); i++) - copy[i] = fast_get(testdata[i], sizeof(testdata[0])); + copy[i] = fast_get(NULL, testdata[i], sizeof(testdata[0])); for (i = 0; i < ARRAY_SIZE(copy); i++) zassert_mem_equal(copy[i], testdata[i], sizeof(testdata[0]), "Data at index %d should match original", i); for (i = 0; i < ARRAY_SIZE(copy); i++) - fast_put(copy[i]); + fast_put(NULL, NULL, copy[i]); } /** @@ -164,10 +132,10 @@ ZTEST(fast_get_suite, test_fast_get_refcounting) int i; for (i = 0; i < ARRAY_SIZE(copy[0]); i++) - copy[0][i] = fast_get(testdata[i], sizeof(testdata[0])); + copy[0][i] = fast_get(NULL, testdata[i], sizeof(testdata[0])); for (i = 0; i < ARRAY_SIZE(copy[0]); i++) - copy[1][i] = fast_get(testdata[i], sizeof(testdata[0])); + copy[1][i] = fast_get(NULL, testdata[i], sizeof(testdata[0])); for (i = 0; i < ARRAY_SIZE(copy[0]); i++) zassert_equal_ptr(copy[0][i], copy[1][i], @@ -179,7 +147,7 @@ ZTEST(fast_get_suite, test_fast_get_refcounting) /* Release first set of references */ for (i = 0; i < ARRAY_SIZE(copy[0]); i++) - fast_put(copy[0][i]); + fast_put(NULL, NULL, copy[0][i]); /* Data should still be valid through second set of references */ for (i = 0; i < ARRAY_SIZE(copy[0]); i++) @@ -188,7 +156,7 @@ ZTEST(fast_get_suite, test_fast_get_refcounting) /* Release second set of references */ for (i = 0; i < ARRAY_SIZE(copy[0]); i++) - fast_put(copy[1][i]); + fast_put(NULL, NULL, copy[1][i]); } /** diff --git a/test/ztest/unit/fast-get/testcase.yaml b/test/ztest/unit/fast-get/testcase.yaml index 458febdd9f09..e3648bfc4e5d 100644 --- a/test/ztest/unit/fast-get/testcase.yaml +++ b/test/ztest/unit/fast-get/testcase.yaml @@ -6,7 +6,7 @@ # generative artificial intelligence solutions. tests: - fast_get.basic_functionality: + sof.unit.fast_get: tags: fast_get memory cache platform_allow: native_sim integration_platforms: diff --git a/test/ztest/unit/list/prj.conf b/test/ztest/unit/list/prj.conf index 9467c2926896..d34c7781cd0a 100644 --- a/test/ztest/unit/list/prj.conf +++ b/test/ztest/unit/list/prj.conf @@ -1 +1,2 @@ CONFIG_ZTEST=y +CONFIG_SOF_FULL_ZEPHYR_APPLICATION=n diff --git a/test/ztest/unit/list/test_list_ztest.c b/test/ztest/unit/list/test_list_ztest.c index f9cd8031c41a..38dc72a3f9f5 100644 --- a/test/ztest/unit/list/test_list_ztest.c +++ b/test/ztest/unit/list/test_list_ztest.c @@ -162,4 +162,56 @@ ZTEST(sof_list_suite, test_list_item_is_last) "item2 should be the last item in the list"); } +/** + * @brief Test list_relink functionality + * + * Tests that list_relink correctly updates references when a list head is moved + */ +ZTEST(sof_list_suite, test_list_relink) +{ + struct list_item old_head; + struct list_item new_head; + struct list_item item1; + struct list_item item2; + + /* Test case 1: Empty list relinking */ + list_init(&old_head); + new_head = old_head; /* Copy the old head structure */ + + list_relink(&new_head, &old_head); + + /* After relinking empty list, new_head should be properly initialized */ + zassert_equal(&new_head, new_head.next, + "Empty list: new_head->next should point to itself"); + zassert_equal(&new_head, new_head.prev, + "Empty list: new_head->prev should point to itself"); + + /* Test case 2: Non-empty list relinking */ + list_init(&old_head); + list_item_append(&item1, &old_head); + list_item_append(&item2, &old_head); + + /* Verify initial state - items point to old_head */ + zassert_equal(&old_head, item1.prev, "Initial: item1 prev should point to old_head"); + zassert_equal(&old_head, item2.next, "Initial: item2 next should point to old_head"); + + /* Simulate moving list to new location by copying head structure */ + new_head = old_head; + /* Now new_head.next points to item1, new_head.prev points to item2 */ + /* But item1.prev and item2.next still point to &old_head */ + + /* Perform the relinking */ + list_relink(&new_head, &old_head); + + /* After relinking, items should now point to new_head instead of old_head */ + zassert_equal(&new_head, item1.prev, "After relink: item1 prev should point to new_head"); + zassert_equal(&new_head, item2.next, "After relink: item2 next should point to new_head"); + zassert_equal(&item1, new_head.next, "After relink: new_head next should point to item1"); + zassert_equal(&item2, new_head.prev, "After relink: new_head prev should point to item2"); + + /* Verify list integrity - items should still be properly linked */ + zassert_equal(&item2, item1.next, "After relink: item1 next should point to item2"); + zassert_equal(&item1, item2.prev, "After relink: item2 prev should point to item1"); +} + ZTEST_SUITE(sof_list_suite, NULL, NULL, NULL, NULL, NULL); diff --git a/test/ztest/unit/list/testcase.yaml b/test/ztest/unit/list/testcase.yaml index 90e8eafebbbe..38b1697f5d2c 100644 --- a/test/ztest/unit/list/testcase.yaml +++ b/test/ztest/unit/list/testcase.yaml @@ -6,7 +6,7 @@ # generative artificial intelligence solutions. tests: - sof.list: + sof.unit.list: platform_allow: native_sim harness: ztest tags: unit diff --git a/test/ztest/unit/math/advanced/functions/CMakeLists.txt b/test/ztest/unit/math/advanced/functions/CMakeLists.txt new file mode 100644 index 000000000000..615fc86a7db6 --- /dev/null +++ b/test/ztest/unit/math/advanced/functions/CMakeLists.txt @@ -0,0 +1,49 @@ +cmake_minimum_required(VERSION 3.20.0) + +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(test_math_advanced_functions) + +set(SOF_ROOT "${PROJECT_SOURCE_DIR}/../../../../../..") + +# Set SOF top directory for UUID registry generation +set(sof_top_dir ${SOF_ROOT}) + +# Include SOF CMake utilities for proper SOF source compilation +include(${SOF_ROOT}/scripts/cmake/misc.cmake) +include(${SOF_ROOT}/scripts/cmake/uuid-registry.cmake) + +target_include_directories(app PRIVATE + ${SOF_ROOT}/zephyr/include + ${SOF_ROOT}/src/include + ${SOF_ROOT}/src/platform/posix/include + ${SOF_ROOT}/test/cmocka/include + ${PROJECT_BINARY_DIR}/include/generated # For uuid-registry.h +) + +# Define SOF-specific configurations for unit testing +target_compile_definitions(app PRIVATE + -DCONFIG_ZEPHYR_POSIX=1 + -DCONFIG_LIBRARY=1 + -DUNIT_TEST=1 +) + +target_sources(app PRIVATE + test_scalar_power_ztest.c + test_base2_logarithm_ztest.c + test_base10_logarithm_ztest.c + test_base_e_logarithm_ztest.c + test_exponential_ztest.c + test_square_root_ztest.c + ${SOF_ROOT}/src/math/power.c + ${SOF_ROOT}/src/math/base2log.c + ${SOF_ROOT}/src/math/log_10.c + ${SOF_ROOT}/src/math/log_e.c + ${SOF_ROOT}/src/math/exp_fcn.c + ${SOF_ROOT}/src/math/sqrt_int16.c + # Note: exp_fcn_hifi.c is conditionally compiled only for Xtensa HiFi platforms. + # TODO: Enable these tests on Xtensa platforms to also test HiFi-optimized code paths. + ${SOF_ROOT}/src/math/exp_fcn_hifi.c +) + +# Apply SOF relative path definitions for proper compilation +sof_append_relative_path_definitions(app) diff --git a/test/ztest/unit/math/advanced/functions/prj.conf b/test/ztest/unit/math/advanced/functions/prj.conf new file mode 100644 index 000000000000..d34c7781cd0a --- /dev/null +++ b/test/ztest/unit/math/advanced/functions/prj.conf @@ -0,0 +1,2 @@ +CONFIG_ZTEST=y +CONFIG_SOF_FULL_ZEPHYR_APPLICATION=n diff --git a/test/ztest/unit/math/advanced/functions/test_base10_logarithm_ztest.c b/test/ztest/unit/math/advanced/functions/test_base10_logarithm_ztest.c new file mode 100644 index 000000000000..b880cfd388a5 --- /dev/null +++ b/test/ztest/unit/math/advanced/functions/test_base10_logarithm_ztest.c @@ -0,0 +1,89 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// Copyright(c) 2026 Intel Corporation. All rights reserved. +// +// Converted from CMock to Ztest +// +// Original test from sof/test/cmocka/src/math/arithmetic/base_10_logarithm.c: +// Author: Shriram Shastry <malladi.sastry@linux.intel.com> + +#include <zephyr/ztest.h> +#include <stdint.h> +#include <math.h> + +#include <sof/math/log.h> + +/* 'Error[max] = 0.0000071279028671,THD = -102.9407645424143993 ' */ +/* 'Error[max] = rms(log10() - double(log10_int32()))' */ +/* 'THD = 20*log10(Error[max])' */ +#define CMP_TOLERANCE 0.0000071279028671 + +/* Base-10 logarithm log10(X) reference table generated by matlab/Octave */ +/* UQ4.28 */ +static const double common_log10_ref_table[] = { + 0.0000000000000000, 7.6373246662453802, 7.9383546619093615, 8.1144459209650428, + 8.2393846575733427, 8.3362946705813989, 8.4154759166290241, 8.4824227062596371, + 8.5404146532373240, 8.5915671756847054, 8.6373246662453802, 8.6787173514036056, + 8.7165059122930053, 8.7512680193222625, 8.7834527026386606, 8.8134159259684335, + 8.8414446495269665, 8.8677735882125130, 8.8925971719048302, 8.9160782677250818, + 8.9383546624098908, 8.9595439614559940, 8.9797473475226131, 8.9990525026982162, + 9.0175359083740947, 9.0352646753178405, 9.0522980146012202, 9.0686884307751292, + 9.0844826979451199, 9.0997226644895282, 9.1144459212987297, 9.1286863604025754, + 9.1424746448781171, 9.1558386064266184, 9.1688035835820649, 9.1813927108816724, + 9.1936271672907388, 9.2055263908534872, 9.2171082633890631, 9.2283892737852433, + 9.2393846580738721, 9.2501085234534361, 9.2605739571199752, 9.2707931222905753, + 9.2807773431865943, 9.2905371804656394, 9.3000824983621975, 9.3094225246070792, + 9.3185659040380759, 9.3275207466824899, 9.3362946709818218, 9.3448948427358882, + 9.3533280102652014, 9.3616005362239267, 9.3697184264391105, 9.3776873561036460, + 9.3855126936091011, 9.3931995222691196, 9.4007526601535094, 9.4081766782268659, + 9.4154759169627091, 9.4226545015843630, 9.4297163562280168, 9.4366652161756566, + 9.4435046406985137, 9.4502380233502628, 9.4568686022422757, 9.4633994693944423, + 9.4698335793932600, 9.4761737574178788, 9.4824227066886628, 9.4885830153874373, + 9.4946571630937573, 9.5006475267772306, 9.5065563863821918, 9.5123859300375031, + 9.5181382589213257, 9.5238153918078847, 9.5294192693208828, 9.5349517579159713, + 9.5404146536127215, 9.5458096854947918, 9.5511385189953373, 9.5564027589832818, + 9.5616039526647825, 9.5667435923129869, 9.5718231178381536, 9.5768439193242560, + 9.5818073388505756, 9.5867146733402073, 9.5915671761296206, 9.5963660590064990, + 9.6011124940261787, 9.6058076152298781, 9.6104525202710605, 9.6150482719557271, + 9.6195958997020572, 9.6240964009244330, 9.6285507423464711, 9.6329598611462810}; + +/* testvector in Q32.0 */ +static const uint32_t uv[100] = { + 1ULL, 43383509ULL, 86767017ULL, 130150525ULL, 173534033ULL, 216917541ULL, + 260301049ULL, 303684557ULL, 347068065ULL, 390451573ULL, 433835081ULL, 477218589ULL, + 520602097ULL, 563985605ULL, 607369113ULL, 650752621ULL, 694136129ULL, 737519638ULL, + 780903146ULL, 824286654ULL, 867670162ULL, 911053670ULL, 954437178ULL, 997820686ULL, + 1041204194ULL, 1084587702ULL, 1127971210ULL, 1171354718ULL, 1214738226ULL, 1258121734ULL, + 1301505242ULL, 1344888750ULL, 1388272258ULL, 1431655766ULL, 1475039274ULL, 1518422782ULL, + 1561806290ULL, 1605189798ULL, 1648573306ULL, 1691956814ULL, 1735340322ULL, 1778723830ULL, + 1822107338ULL, 1865490846ULL, 1908874354ULL, 1952257862ULL, 1995641370ULL, 2039024878ULL, + 2082408386ULL, 2125791894ULL, 2169175403ULL, 2212558911ULL, 2255942419ULL, 2299325927ULL, + 2342709435ULL, 2386092943ULL, 2429476451ULL, 2472859959ULL, 2516243467ULL, 2559626975ULL, + 2603010483ULL, 2646393991ULL, 2689777499ULL, 2733161007ULL, 2776544515ULL, 2819928023ULL, + 2863311531ULL, 2906695039ULL, 2950078547ULL, 2993462055ULL, 3036845563ULL, 3080229071ULL, + 3123612579ULL, 3166996087ULL, 3210379595ULL, 3253763103ULL, 3297146611ULL, 3340530119ULL, + 3383913627ULL, 3427297135ULL, 3470680643ULL, 3514064151ULL, 3557447659ULL, 3600831168ULL, + 3644214676ULL, 3687598184ULL, 3730981692ULL, 3774365200ULL, 3817748708ULL, 3861132216ULL, + 3904515724ULL, 3947899232ULL, 3991282740ULL, 4034666248ULL, 4078049756ULL, 4121433264ULL, + 4164816772ULL, 4208200280ULL, 4251583788ULL, 4294967295ULL}; + +ZTEST(math_advanced_functions_suite, test_math_arithmetic_base10log_fixed) +{ + double clogfxp; + double diff; + int i; + + BUILD_ASSERT(ARRAY_SIZE(uv) == ARRAY_SIZE(common_log10_ref_table), + "Test vector size must match reference table size"); + + for (i = 0; i < ARRAY_SIZE(common_log10_ref_table); i++) { + clogfxp = log10_int32(uv[i]); + diff = fabs(common_log10_ref_table[i] - (double)clogfxp / (1 << 28)); + + if (diff > CMP_TOLERANCE) { + zassert_true(diff <= CMP_TOLERANCE, + "log10_int32(%u): delta %.16f > tolerance (got %.16f)", + uv[i], diff, clogfxp / (1 << 28)); + } + } +} \ No newline at end of file diff --git a/test/ztest/unit/math/advanced/functions/test_base2_logarithm_ztest.c b/test/ztest/unit/math/advanced/functions/test_base2_logarithm_ztest.c new file mode 100644 index 000000000000..52d1eb0e6a0b --- /dev/null +++ b/test/ztest/unit/math/advanced/functions/test_base2_logarithm_ztest.c @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// Copyright(c) 2026 Intel Corporation. All rights reserved. +// +// These contents may have been developed with support from one or more Intel-operated +// generative artificial intelligence solutions. +// +// Converted from CMock to Ztest +// +// Original test from sof/test/cmocka/src/math/arithmetic/base2_logarithm.c +// Author: Shriram Shastry <malladi.sastry@linux.intel.com> + +#include <zephyr/ztest.h> +#include <sof/audio/format.h> +#include <sof/math/log.h> +#include <math.h> +#include <rtos/string.h> + +/* Test data tables from MATLAB-generated reference */ +#include "log2_tables.h" + +/* 'Error[max] = 0.0000236785999981,THD(-dBc) = -92.5128795787487235' */ +#define CMP_TOLERANCE 0.0000236785691029f + +/* testvector in Q32.0 */ +static const uint32_t uv[100] = { + 1ULL, 43383509ULL, 86767017ULL, 130150525ULL, 173534033ULL, 216917541ULL, + 260301049ULL, 303684557ULL, 347068065ULL, 390451573ULL, 433835081ULL, 477218589ULL, + 520602097ULL, 563985605ULL, 607369113ULL, 650752621ULL, 694136129ULL, 737519638ULL, + 780903146ULL, 824286654ULL, 867670162ULL, 911053670ULL, 954437178ULL, 997820686ULL, + 1041204194ULL, 1084587702ULL, 1127971210ULL, 1171354718ULL, 1214738226ULL, 1258121734ULL, + 1301505242ULL, 1344888750ULL, 1388272258ULL, 1431655766ULL, 1475039274ULL, 1518422782ULL, + 1561806290ULL, 1605189798ULL, 1648573306ULL, 1691956814ULL, 1735340322ULL, 1778723830ULL, + 1822107338ULL, 1865490846ULL, 1908874354ULL, 1952257862ULL, 1995641370ULL, 2039024878ULL, + 2082408386ULL, 2125791894ULL, 2169175403ULL, 2212558911ULL, 2255942419ULL, 2299325927ULL, + 2342709435ULL, 2386092943ULL, 2429476451ULL, 2472859959ULL, 2516243467ULL, 2559626975ULL, + 2603010483ULL, 2646393991ULL, 2689777499ULL, 2733161007ULL, 2776544515ULL, 2819928023ULL, + 2863311531ULL, 2906695039ULL, 2950078547ULL, 2993462055ULL, 3036845563ULL, 3080229071ULL, + 3123612579ULL, 3166996087ULL, 3210379595ULL, 3253763103ULL, 3297146611ULL, 3340530119ULL, + 3383913627ULL, 3427297135ULL, 3470680643ULL, 3514064151ULL, 3557447659ULL, 3600831168ULL, + 3644214676ULL, 3687598184ULL, 3730981692ULL, 3774365200ULL, 3817748708ULL, 3861132216ULL, + 3904515724ULL, 3947899232ULL, 3991282740ULL, 4034666248ULL, 4078049756ULL, 4121433264ULL, + 4164816772ULL, 4208200280ULL, 4251583788ULL, 4294967295ULL}; + +/** + * @brief Test base-2 logarithm function with fixed-point arithmetic + * + * This test validates the base2_logarithm() function against MATLAB-generated + * reference values. It tests 100 uniformly distributed input values across + * the full uint32_t range, checking that the fixed-point logarithm calculation + * stays within acceptable tolerance. + * + * Input values: Q32.0 format (unsigned 32-bit integers) + * Result: Q16.16 fixed-point format + * Reference: MATLAB log2() function results + */ +ZTEST(math_advanced_functions_suite, test_math_arithmetic_base2log_fixed) +{ + uint32_t u[100]; + int i, ret; + + BUILD_ASSERT(ARRAY_SIZE(uv) == ARRAY_SIZE(log2_lookup_table), + "Test vector size must match reference table size"); + + ret = memcpy_s((void *)&u[0], sizeof(u), (void *)&uv[0], 100U * sizeof(uint32_t)); + zassert_equal(ret, 0, "memcpy_s failed with error code %d", ret); + + for (i = 0; i < ARRAY_SIZE(u); i++) { + float y = Q_CONVERT_QTOF(base2_logarithm(u[i]), 0); + float delta = fabsf((float)(log2_lookup_table[i] - (double)y / (1 << 16))); + + zassert_true(delta <= CMP_TOLERANCE, + "base2_logarithm(%u): delta %.16f > tolerance (expected %.16f, got %.16f)", + u[i], (double)delta, log2_lookup_table[i], (double)y / (1 << 16)); + } +} diff --git a/test/ztest/unit/math/advanced/functions/test_base_e_logarithm_ztest.c b/test/ztest/unit/math/advanced/functions/test_base_e_logarithm_ztest.c new file mode 100644 index 000000000000..e400b8b76a59 --- /dev/null +++ b/test/ztest/unit/math/advanced/functions/test_base_e_logarithm_ztest.c @@ -0,0 +1,100 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// Copyright(c) 2026 Intel Corporation. All rights reserved. +// +// These contents may have been developed with support from one or more Intel-operated +// generative artificial intelligence solutions. +// +// Converted from CMock to Ztest +// +// Original test from sof/test/cmocka/src/math/arithmetic/base_e_logarithm.c +// Author: Shriram Shastry <malladi.sastry@linux.intel.com> + +#include <zephyr/ztest.h> +#include <stdint.h> +#include <math.h> + +#include <sof/math/log.h> + +/* 'Error[max] = 0.0000164133276926,THD(-dBc) = -95.6960671942683234' */ +/* 'Error[max] = rms(log() - double(log_int32()))' */ +/* 'THD = 20*log10(Error[max])' */ +#define CMP_TOLERANCE 0.0000164133276926 + +/* Natural logarithm loge(X) reference table generated by matlab/Octave */ +/* UQ5.27 */ +static const double natural_log_lookup_table[] = { + 0.0000000000000000, 17.5855899268523359, 18.2787371074122831, 18.6842022155204468, + 18.9718842879722267, 19.1950278392864391, 19.3773493960803940, 19.5315000759076511, + 19.6650314685321739, 19.7828145041885577, 19.8881750198463827, 19.9834851996507084, + 20.0704965766403376, 20.1505392860869676, 20.2246472581140395, 20.2936401294912301, + 20.3581786505327571, 20.4188032722644479, 20.4759616860290699, 20.5300289072319480, + 20.5813222015588408, 20.6301123656733907, 20.6766323812583899, 20.7210841437836706, + 20.7636437581607112, 20.8044657526425461, 20.8436864657603671, 20.8814267937103786, + 20.9177944378507625, 20.9528857576336485, 20.9867873092828354, 21.0195771320810394, + 21.0513258303723845, 21.0820974890173112, 21.1119504521464485, 21.1409379890003279, + 21.1691088659487328, 21.1965078407425196, 21.2231760877918916, 21.2491515741640455, + 21.2744693821187845, 21.2991619946810467, 21.3232595462333343, 21.3467900436180038, + 21.3697795618183370, 21.3922524176471107, 21.4142313243436178, 21.4357375295432568, + 21.4567909387206548, 21.4774102259037889, 21.4976129332024932, 21.5174155604805932, + 21.5368336463203107, 21.5558818412742781, 21.5745739742703257, 21.5929231129229997, + 21.6109416184107097, 21.6286411954956677, 21.6460329381935921, 21.6631273715394208, + 21.6799344898427790, 21.6964637917813938, 21.7127243130127638, 21.7287246543415016, + 21.7444730112924880, 21.7599771978118319, 21.7752446699265008, 21.7902825472754031, + 21.8050976330453672, 21.8196964324517815, 21.8340851698895619, 21.8482698048676056, + 21.8622560468288185, 21.8760493689479993, 21.8896550209909755, 21.9030780413106569, + 21.9163232680485471, 21.9293953496040821, 21.9422987544284780, 21.9550377801946830, + 21.9676165623906030, 21.9800390823784895, 21.9923091749598925, 22.0044305354820757, + 22.0164067265188734, 22.0282411841561903, 22.0399372239099236, 22.0514980465667030, + 22.0629267423782807, 22.0742262976204415, 22.0853995982070579, 22.0964494343823858, + 22.1073785049035614, 22.1181894209970018, 22.1288847101032040, 22.1394668194234150, + 22.1499381192805984, 22.1603009063062437, 22.1705574064637361, 22.1807097776854185}; + +/* testvector in Q32.0 */ +static const uint32_t uv[100] = { + 1ULL, 43383509ULL, 86767017ULL, 130150525ULL, 173534033ULL, 216917541ULL, + 260301049ULL, 303684557ULL, 347068065ULL, 390451573ULL, 433835081ULL, 477218589ULL, + 520602097ULL, 563985605ULL, 607369113ULL, 650752621ULL, 694136129ULL, 737519638ULL, + 780903146ULL, 824286654ULL, 867670162ULL, 911053670ULL, 954437178ULL, 997820686ULL, + 1041204194ULL, 1084587702ULL, 1127971210ULL, 1171354718ULL, 1214738226ULL, 1258121734ULL, + 1301505242ULL, 1344888750ULL, 1388272258ULL, 1431655766ULL, 1475039274ULL, 1518422782ULL, + 1561806290ULL, 1605189798ULL, 1648573306ULL, 1691956814ULL, 1735340322ULL, 1778723830ULL, + 1822107338ULL, 1865490846ULL, 1908874354ULL, 1952257862ULL, 1995641370ULL, 2039024878ULL, + 2082408386ULL, 2125791894ULL, 2169175403ULL, 2212558911ULL, 2255942419ULL, 2299325927ULL, + 2342709435ULL, 2386092943ULL, 2429476451ULL, 2472859959ULL, 2516243467ULL, 2559626975ULL, + 2603010483ULL, 2646393991ULL, 2689777499ULL, 2733161007ULL, 2776544515ULL, 2819928023ULL, + 2863311531ULL, 2906695039ULL, 2950078547ULL, 2993462055ULL, 3036845563ULL, 3080229071ULL, + 3123612579ULL, 3166996087ULL, 3210379595ULL, 3253763103ULL, 3297146611ULL, 3340530119ULL, + 3383913627ULL, 3427297135ULL, 3470680643ULL, 3514064151ULL, 3557447659ULL, 3600831168ULL, + 3644214676ULL, 3687598184ULL, 3730981692ULL, 3774365200ULL, 3817748708ULL, 3861132216ULL, + 3904515724ULL, 3947899232ULL, 3991282740ULL, 4034666248ULL, 4078049756ULL, 4121433264ULL, + 4164816772ULL, 4208200280ULL, 4251583788ULL, 4294967295ULL}; + +/** + * @brief Test natural (base-e) logarithm function with fixed-point arithmetic + * + * This test validates the ln_int32() function against MATLAB-generated reference + * values. It tests 100 uniformly distributed input values across the full uint32_t + * range, checking that the fixed-point logarithm calculation stays within acceptable + * tolerance. + * + * Input values: Q32.0 format (unsigned 32-bit integers) + * Result: UQ5.27 fixed-point format + * Reference: MATLAB log() function results + */ +ZTEST(math_advanced_functions_suite, test_math_arithmetic_base_e_log_fixed) +{ + int i; + + BUILD_ASSERT(ARRAY_SIZE(uv) == ARRAY_SIZE(natural_log_lookup_table), + "Test vector size must match reference table size"); + + for (i = 0; i < ARRAY_SIZE(uv); i++) { + double logefxp = ln_int32(uv[i]); + double diff = fabs(natural_log_lookup_table[i] - logefxp / (1 << 27)); + + zassert_true(diff <= CMP_TOLERANCE, + "ln_int32(%u): diff %.16f > tolerance (expected %.16f, got %.16f)", + uv[i], diff, natural_log_lookup_table[i], logefxp / (1 << 27)); + } +} diff --git a/test/ztest/unit/math/advanced/functions/test_exponential_ztest.c b/test/ztest/unit/math/advanced/functions/test_exponential_ztest.c new file mode 100644 index 000000000000..1fd03b6bb5af --- /dev/null +++ b/test/ztest/unit/math/advanced/functions/test_exponential_ztest.c @@ -0,0 +1,294 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright(c) 2022-2026 Intel Corporation. + * + * These contents may have been developed with support from one or more Intel-operated + * generative artificial intelligence solutions. + * + * Converted from CMock to Ztest + * + * Original test from sof/test/cmocka/src/math/arithmetic/exponential.c + * + * Author: Shriram Shastry <malladi.sastry@linux.intel.com> + * Seppo Ingalsuo <seppo.ingalsuo@linux.intel.com> + */ + +#include <zephyr/ztest.h> +#include <zephyr/logging/log.h> +#include <sof/math/exp_fcn.h> +#include <sof/audio/format.h> +#include <sof/common.h> +#include <math.h> +#include <rtos/string.h> + +LOG_MODULE_REGISTER(test_exponential, LOG_LEVEL_INF); + +#define ULP_TOLERANCE 1.0 +#define ULP_SCALE 1.9073e-06 /* For exp() output Q13.19, 1 / 2^19 */ +#define NUMTESTSAMPLES 256 + +#define NUMTESTSAMPLES_TEST2 100 +#define ABS_DELTA_TOLERANCE_TEST2 2.0e-6 +#define REL_DELTA_TOLERANCE_TEST2 1000.0 /* rel. error is large with values near zero */ +#define NUMTESTSAMPLES_TEST3 100 +#define ABS_DELTA_TOLERANCE_TEST3 2.0e-6 +#define REL_DELTA_TOLERANCE_TEST3 10.0e-2 +#define SOFM_EXP_FIXED_ARG_MIN -11.5 +#define SOFM_EXP_FIXED_ARG_MAX 7.6245 + +#define NUMTESTSAMPLES_TEST4 100 +#define ABS_DELTA_TOLERANCE_TEST4 2.5e-5 +#define REL_DELTA_TOLERANCE_TEST4 1000.0 /* rel. error is large with values near zero */ + +/** + * Saturates input to 32 bits + * @param x Input value + * @return Saturated output value + */ +static int32_t saturate32(int64_t x) +{ + if (x < INT32_MIN) + return INT32_MIN; + else if (x > INT32_MAX) + return INT32_MAX; + + return x; +} + +/** + * Generates linearly spaced values for a vector with end points and number points in + * desired fractional Q-format for 32 bit integer. If the test values exceed int32_t + * range, the values are saturated to INT32_MIN to INT32_MAX range. + * + * @param a First value of test vector + * @param b Last value of test vector + * @param step_count Number of values in vector + * @param point Calculate n-th point of vector 0 .. step_count - 1 + * @param qformat Number of fractional bits y in Qx.y format + * @param fout Pointer to calculated test vector value, double + * @param iout Pointer to calculated test vector value, int32_t + */ +static void gen_testvector_linspace_int32(double a, double b, int step_count, int point, + int qformat, double *fout, int32_t *iout) +{ + double fstep = (b - a) / (step_count - 1); + double fvalue = a + fstep * point; + int64_t itmp; + + itmp = (int64_t)round(fvalue * (double)(1 << qformat)); + *iout = saturate32(itmp); + *fout = (double)*iout / (1 << qformat); +} + +/** + * Calculate reference exponent value + * @param x Input value + * @param qformat Fractional bits y in Qx.y format + * @return Saturated exponent value to match fractional format + */ +static double ref_exp(double x, int qformat) +{ + double yf; + int64_t yi; + + yf = exp(x); + yi = yf * (1 << qformat); + + if (yi > INT32_MAX) + yi = INT32_MAX; + else if (yi < INT32_MIN) + yi = INT32_MIN; + + yf = (double)yi / (1 << qformat); + return yf; +} + +/** + * Calculates test exponent function and compares result to reference exponent. + * @param ivalue Fractional format input value Q5.27 + * @param iexp_value Fractional format output value Q12.20 + * @param abs_delta_max Calculated absolute error + * @param rel_delta_max Calculated relative error + * @param abs_delta_tolerance Tolerance for absolute error + * @param rel_delta_tolerance Tolerance for relative error + */ +static void test_exp_with_input_value(int32_t ivalue, int32_t *iexp_value, + double *abs_delta_max, double *rel_delta_max, + double abs_delta_tolerance, double rel_delta_tolerance) +{ + double fvalue, fexp_value, ref_exp_value; + double rel_delta, abs_delta; + double eps = 1e-9; + + *iexp_value = sofm_exp_fixed(ivalue); + fvalue = (double)ivalue / (1 << 27); /* Q5.27 */ + fexp_value = (double)*iexp_value / (1 << 20); /* Q12.20 */ + ref_exp_value = ref_exp(fvalue, 20); + abs_delta = fabs(ref_exp_value - fexp_value); + rel_delta = abs_delta / (ref_exp_value + eps); + + if (abs_delta > *abs_delta_max) + *abs_delta_max = abs_delta; + + if (rel_delta > *rel_delta_max) + *rel_delta_max = rel_delta; + + zassert_true(abs_delta <= abs_delta_tolerance, + "sofm_exp_fixed: Absolute error %g exceeds limit %g, input %g output %g", + abs_delta, abs_delta_tolerance, fvalue, fexp_value); + + zassert_true(rel_delta <= rel_delta_tolerance, + "sofm_exp_fixed: Relative error %g exceeds limit %g, input %g output %g", + rel_delta, rel_delta_tolerance, fvalue, fexp_value); +} + +/** + * Reference function for dB to linear conversion + * @param x Input value + * @param qformat Fractional bits y in Qx.y format for saturation + * @return Saturated linear value + */ +static double ref_db2lin(double x, int qformat) +{ + double fref; + int64_t iref; + + fref = pow(10, x / 20); + iref = fref * (1 << qformat); + return (double)saturate32(iref) / (1 << qformat); +} + +/** + * @brief Test sofm_exp_approx() function with ULP error validation + * + * This test validates the sofm_exp_approx() exponential approximation function + * against the C standard library exp() function. It tests 256 linearly spaced + * input values and checks that the ULP (Unit in the Last Place) error stays + * within acceptable tolerance. + * + * Input values: Q28 format, range -8 to 8 + * Result: Q19 format + * Validation: ULP error < 1.0 ULP + */ +ZTEST(math_advanced_functions_suite, test_function_sofm_exp_approx) +{ + int32_t accum; + int i; + double a_i; + double max_ulp = 0; + double ulp; + double a_tmp = -8; + double b_tmp = 8; + int32_t b_i; + + for (i = 0; i < NUMTESTSAMPLES; i++) { + gen_testvector_linspace_int32(a_tmp, b_tmp, NUMTESTSAMPLES, i, 28, &a_i, &b_i); + accum = sofm_exp_approx(b_i); + ulp = fabs(exp(a_i) - (double)accum / (1 << 19)) / ULP_SCALE; + if (ulp > max_ulp) + max_ulp = ulp; + + zassert_true(ulp <= ULP_TOLERANCE, + "sofm_exp_approx: ULP %.16f exceeds tolerance, value=%.16f, exp=%.16f", + ulp, (double)b_i / (1 << 28), (double)accum / (1 << 19)); + } + + LOG_INF("Worst-case ULP: %g ULP_SCALE %g", max_ulp, ULP_SCALE); +} + +/** + * @brief Test sofm_exp_fixed() function with absolute and relative error validation + * + * This test validates the sofm_exp_fixed() fixed-point exponential function + * against a reference implementation. It performs two sub-tests with different + * input ranges and tolerance requirements. + * + * Sub-test 1: Coarse grid across max range + * - Input values: Q27 format, range -16 to 16 + * - Result: Q20 format + * - Tolerances: abs 2.0e-6, rel 1000.0 + * + * Sub-test 2: Fine grid across typical range + * - Input values: Q27 format, range -11.5 to 7.6245 + * - Result: Q20 format + * - Tolerances: abs 2.0e-6, rel 10.0e-2 + */ +ZTEST(math_advanced_functions_suite, test_function_sofm_exp_fixed) +{ + double rel_delta_max, abs_delta_max; + double tmp; + int32_t ivalue, iexp_value; + int i; + + /* Test max int32_t range with coarse grid */ + rel_delta_max = 0; + abs_delta_max = 0; + for (i = 0; i < NUMTESTSAMPLES_TEST2; i++) { + gen_testvector_linspace_int32(-16, 16, NUMTESTSAMPLES_TEST2, i, 27, &tmp, &ivalue); + test_exp_with_input_value(ivalue, &iexp_value, &abs_delta_max, &rel_delta_max, + ABS_DELTA_TOLERANCE_TEST2, REL_DELTA_TOLERANCE_TEST2); + } + + LOG_INF("Absolute max error was %.6e (max range)", abs_delta_max); + LOG_INF("Relative max error was %.6e (max range)", rel_delta_max); + + /* Test max int32_t middle range with fine grid */ + rel_delta_max = 0; + abs_delta_max = 0; + for (i = 0; i < NUMTESTSAMPLES_TEST3; i++) { + gen_testvector_linspace_int32(SOFM_EXP_FIXED_ARG_MIN, SOFM_EXP_FIXED_ARG_MAX, + NUMTESTSAMPLES_TEST3, i, 27, &tmp, &ivalue); + test_exp_with_input_value(ivalue, &iexp_value, &abs_delta_max, &rel_delta_max, + ABS_DELTA_TOLERANCE_TEST3, REL_DELTA_TOLERANCE_TEST3); + } + + LOG_INF("Absolute max error was %.6e (middle)", abs_delta_max); + LOG_INF("Relative max error was %.6e (middle)", rel_delta_max); +} + +/** + * @brief Test sofm_db2lin_fixed() function for dB to linear conversion + * + * This test validates the sofm_db2lin_fixed() function that converts decibel + * values to linear scale using fixed-point arithmetic. It compares against + * a reference implementation using floating-point pow(10, x/20). + * + * Input values: Q24 format, range -128 to 128 dB + * Result: Q20 format + * Tolerances: abs 2.5e-5, rel 1000.0 + */ +ZTEST(math_advanced_functions_suite, test_function_sofm_db2lin_fixed) +{ + double abs_delta, rel_delta, abs_delta_max, rel_delta_max; + double fin, fout, fref; + double eps = 1e-9; + int32_t iin, iout; + int i; + + rel_delta_max = 0; + abs_delta_max = 0; + for (i = 0; i < NUMTESTSAMPLES_TEST4; i++) { + gen_testvector_linspace_int32(-128, 128, NUMTESTSAMPLES_TEST4, i, 24, &fin, &iin); + iout = sofm_db2lin_fixed(iin); + fout = (double)iout / (1 << 20); + fref = ref_db2lin(fin, 20); + abs_delta = fabs(fref - fout); + rel_delta = abs_delta / (fref + eps); + if (abs_delta > abs_delta_max) + abs_delta_max = abs_delta; + + if (rel_delta > rel_delta_max) + rel_delta_max = rel_delta; + + zassert_true(abs_delta <= ABS_DELTA_TOLERANCE_TEST4, + "sofm_db2lin_fixed: Absolute error %g exceeds limit %g, input %g output %g", + abs_delta, ABS_DELTA_TOLERANCE_TEST4, fin, fout); + + zassert_true(rel_delta <= REL_DELTA_TOLERANCE_TEST4, + "sofm_db2lin_fixed: Relative error %g exceeds limit %g, input %g output %g", + rel_delta, REL_DELTA_TOLERANCE_TEST4, fin, fout); + } + + LOG_INF("Absolute max error was %.6e", abs_delta_max); + LOG_INF("Relative max error was %.6e", rel_delta_max); +} diff --git a/test/ztest/unit/math/advanced/functions/test_scalar_power_ztest.c b/test/ztest/unit/math/advanced/functions/test_scalar_power_ztest.c new file mode 100644 index 000000000000..a3c2d6fe911c --- /dev/null +++ b/test/ztest/unit/math/advanced/functions/test_scalar_power_ztest.c @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// Copyright(c) 2025 Intel Corporation. All rights reserved. +// +// These contents may have been developed with support from one or more Intel-operated +// generative artificial intelligence solutions. +// +// Converted from CMock to Ztest +// +// Original test from sof/test/cmocka/src/math/arithmetic/scalar_power.c: +// Author: Shriram Shastry <malladi.sastry@linux.intel.com> + +#include <zephyr/ztest.h> +#include <zephyr/logging/log.h> +#include <sof/audio/format.h> +#include <sof/math/power.h> +#include <sof/common.h> +#include <math.h> + +LOG_MODULE_REGISTER(test_scalar_power, LOG_LEVEL_INF); + +/* Test data tables from MATLAB-generated reference */ +#include "power_tables.h" + +/* Error tolerance: max error = 0.000034912111005, THD+N = -96.457180359025074 */ +#define CMP_TOLERANCE 0.0000150363575813f + +/** + * @brief Test scalar power function with fixed-point arithmetic + * + * This test validates the power_int32() function against MATLAB-generated + * reference values. It tests 64 base values against 6 exponent values, + * checking that the fixed-point power calculation stays within acceptable + * tolerance. + * + * Base values: Fixed-point Q6.25 format (range -1.0 to 1.0) + * Exponent values: Fixed-point Q2.29 format + * Result: Fixed-point Q16.15 format + */ +ZTEST(math_advanced_functions_suite, test_math_arithmetic_power_fixed) +{ + double p; + int i; + int j; + + for (i = 0; i < ARRAY_SIZE(b); i++) { + for (j = 0; j < ARRAY_SIZE(e); j++) { + p = power_int32(b[i], e[j]); + float delta = fabsf((float)(power_table[i][j] - (double)p / (1 << 15))); + + zassert_true(delta <= CMP_TOLERANCE, + "Power calc error: delta=%f > %f at b[%d]=%d, e[%d]=%d", + (double)delta, (double)CMP_TOLERANCE, i, b[i], j, e[j]); + } + } +} + +ZTEST_SUITE(math_advanced_functions_suite, NULL, NULL, NULL, NULL, NULL); diff --git a/test/ztest/unit/math/advanced/functions/test_square_root_ztest.c b/test/ztest/unit/math/advanced/functions/test_square_root_ztest.c new file mode 100644 index 000000000000..2dcb45ffa0ff --- /dev/null +++ b/test/ztest/unit/math/advanced/functions/test_square_root_ztest.c @@ -0,0 +1,156 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// Copyright(c) 2026 Intel Corporation. All rights reserved. +// +// Converted from CMock to Ztest +// +// Original test from sof/test/cmocka/src/math/arithmetic/square_root.c: +// Author: Shriram Shastry <malladi.sastry@linux.intel.com> + +#include <zephyr/ztest.h> +#include <stdint.h> +#include <math.h> + +#include <sof/math/sqrt.h> +#include <sof/audio/format.h> +#include <rtos/string.h> +#include <sof/common.h> + +/* 'Error[max] = 0.0003000860000000,THD(-dBc) = -87.1210823527511309' */ +#define CMP_TOLERANCE 0.0001689942 + +static const double sqrt_ref_table[] = { + 0.0000000000000000, 0.2529127196287289, 0.3580137261684250, 0.4383362543470480, + 0.5060667106469264, 0.5657458434447398, 0.6199010757774179, 0.6695089151758922, + 0.7156864056624241, 0.7590598625931949, 0.8002380017063674, 0.8392530626247365, + 0.8765332548597343, 0.9124250825410271, 0.9468285879714448, 0.9800251112854200, + 1.0121334212938529, 1.0433710015497843, 1.0735864616438677, 1.1029744939820685, + 1.1317074351394887, 1.1596234572049671, 1.1868830634270588, 1.2135304899342250, + 1.2397036881347898, 1.2652391387105444, 1.2902693214499832, 1.3148230929007141, + 1.3390178303517843, 1.3626935069009465, 1.3859648038460428, 1.4089384024417106, + 1.4314580907679415, 1.4536289448738284, 1.4754666899408471, 1.4970674458754356, + 1.5182805344369004, 1.5392012945030940, 1.5598414883410430, 1.5802893574042065, + 1.6003997303408295, 1.6202605162827983, 1.6399552204252408, 1.6593426315110451, + 1.6785061252494731, 1.6974532854396907, 1.7162624043615824, 1.7347972458979175, + 1.7531361407845656, 1.7712851751976586, 1.7893183496097054, 1.8071040368501201, + 1.8247163735084968, 1.8422265952170487, 1.8595062978852748, 1.8766268983537990, + 1.8935927121149891, 1.9104717594746068, 1.9271396388170734, 1.9436645881555799, + 1.9601125007572908, 1.9763617734046062, 1.9924785326635266, 2.0084659685628234, + 2.0243874459327196, 2.0401248429936829, 2.0557417684986605, 2.0712409474756917, + 2.0866835042418388, 2.1019545405705138, 2.1171154277400652, 2.1322257663648099, + 2.1471729232877355, 2.1620167451363552, 2.1767593459084997, 2.1914584719713490, + 2.2060043241401410, 2.2204548907543695, 2.2348120201987909, 2.2491317769308226, + 2.2633070038662453, 2.2773940013752560, 2.2914476694602910, 2.3053627188850347, + 2.3191942802134968, 2.3329438383992445, 2.3466648541067809, 2.3602543890966499, + 2.3737661268541177, 2.3872013883939496, 2.4006123079591588, 2.4138981537908761, + 2.4271112748749282, 2.4403028756693299, 2.4533737931163282, 2.4663754402969551, + 2.4793089069839604, 2.4922242356226696, 2.5050242482608827, 2.5177591878742098, + 2.5304782774210888, 2.5430857547967194, 2.5556310375326090, 2.5681150370943278, + 2.5805859466650203, 2.5929498012639969, 2.6052549809231724, 2.6175023131556161, + 2.6297390257875399, 2.6418728560436060, 2.6539512111660981, 2.6660206330081166, + 2.6779900782816579, 2.6899062628881700, 2.7017698915479462, 2.7136266381449752, + 2.7253870138018930, 2.7370968595849874, 2.7487568212739375, 2.7604117531402812, + 2.7719736453698474, 2.7834875128828940, 2.7949976241045360, 2.8064170328908711, + 2.8177901636300033, 2.8291175744390689, 2.8404427884354582, 2.8516802201728368, + 2.8628735427669523, 2.8740232715872360, 2.8851722218959477, 2.8962361080806240, + 2.9072578897476569, 2.9182798738083706, 2.9292187124939990, 2.9401168530136688, + 2.9509747462702896, 2.9618340496219568, 2.9726126187665289, 2.9833522462156559, + 2.9940941216626773, 3.0047569707257522, 3.0153821145710538, 3.0259699503836783, + 3.0365610688738007, 3.0470753139280951, 3.0575534030495688, 3.0679957066870220, + 3.0784422425351754, 3.0888139284157279, 3.0991509043809078, 3.1094927741514371, + 3.1197612338526808, 3.1299960063872287, 3.1401974211424988, 3.1504045499149789, + 3.1605400917999757, 3.1706432337342845, 3.1807142844611178, 3.1907918051402224, + 3.2007994606816590, 3.2107759235502562, 3.2207593849316032, 3.2306742112715421, + 3.2405587023112234, 3.2504131347991749, 3.2602752232365293, 3.2700702400713046, + 3.2798360048560355, 3.2895727781126838, 3.2993178153786578, 3.3089972636170311, + 3.3186484800856810, 3.3283083869662677, 3.3379037677111065, 3.3474716438306089, + 3.3570122504989461, 3.3665620793882591, 3.3760487375221642, 3.3855088128485207, + 3.3949784839156196, 3.4043859578490805, 3.4137675072784321, 3.4231233453528955, + 3.4324892457042018, 3.4417941928048226, 3.4510740515635128, 3.4603290238249023, + 3.4695944917958350, 3.4788001927748020, 3.4879815975718680, 3.4971738031409019, + 3.5063070962374359, 3.5154166604934614, 3.5245026799003858, 3.5335998818485379, + 3.5426392659640071, 3.5516556438511886, 3.5606491902811768, 3.5696542746637245, + 3.5786025882144274, 3.5875285822032135, 3.5964663647113397, 3.6053481324623839, + 3.6142080737002402, 3.6230463485511746, 3.6318967259718442, 3.6406920594682268, + 3.6494661959833250, 3.6582526566654741, 3.6669847755001657, 3.6756961500510350, + 3.6843869274616097, 3.6930903069956198, 3.7017402474207994, 3.7103700224000571, + 3.7189797723132347, 3.7276023837381045, 3.7361724230822109, 3.7447228493908598, + 3.7532863204297375, 3.7617978476886553, 3.7702901600042669, 3.7787633869263368, + 3.7872498886065071, 3.7956852559847478, 3.8041019184887777, 3.8125000000000000, + 3.8209115711273665, 3.8292727871131094, 3.8376157861196840, 3.8459724266107265, + 3.8542792776341468, 3.8625682639598748, 3.8708395003538962, 3.8791245690071618, + 3.8873605782876637, 3.8955791750874478, 3.9037804693815712, 3.9119957742180653, + 3.9201627238228260, 3.9283126944020128, 3.9364768015796816, 3.9445930655930783, + 3.9526926641056979, 3.9607756993580185, 3.9688730295891301, 3.9769231786332004, + 3.9849570653270532, 3.9930053589840071, 3.9999694823054588, 3.9999694823054588}; + +/* testvector in Q4.12 */ +static const uint32_t uv[252] = { + 0U, 262U, 525U, 787U, 1049U, 1311U, + 1574U, 1836U, 2098U, 2360U, 2623U, 2885U, + 3147U, 3410U, 3672U, 3934U, 4196U, 4459U, + 4721U, 4983U, 5246U, 5508U, 5770U, 6032U, + 6295U, 6557U, 6819U, 7081U, 7344U, 7606U, + 7868U, 8131U, 8393U, 8655U, 8917U, 9180U, + 9442U, 9704U, 9966U, 10229U, 10491U, 10753U, + 11016U, 11278U, 11540U, 11802U, 12065U, 12327U, + 12589U, 12851U, 13114U, 13376U, 13638U, 13901U, + 14163U, 14425U, 14687U, 14950U, 15212U, 15474U, + 15737U, 15999U, 16261U, 16523U, 16786U, 17048U, + 17310U, 17572U, 17835U, 18097U, 18359U, 18622U, + 18884U, 19146U, 19408U, 19671U, 19933U, 20195U, + 20457U, 20720U, 20982U, 21244U, 21507U, 21769U, + 22031U, 22293U, 22556U, 22818U, 23080U, 23342U, + 23605U, 23867U, 24129U, 24392U, 24654U, 24916U, + 25178U, 25441U, 25703U, 25965U, 26228U, 26490U, + 26752U, 27014U, 27277U, 27539U, 27801U, 28063U, + 28326U, 28588U, 28850U, 29113U, 29375U, 29637U, + 29899U, 30162U, 30424U, 30686U, 30948U, 31211U, + 31473U, 31735U, 31998U, 32260U, 32522U, 32784U, + 33047U, 33309U, 33571U, 33833U, 34096U, 34358U, + 34620U, 34883U, 35145U, 35407U, 35669U, 35932U, + 36194U, 36456U, 36719U, 36981U, 37243U, 37505U, + 37768U, 38030U, 38292U, 38554U, 38817U, 39079U, + 39341U, 39604U, 39866U, 40128U, 40390U, 40653U, + 40915U, 41177U, 41439U, 41702U, 41964U, 42226U, + 42489U, 42751U, 43013U, 43275U, 43538U, 43800U, + 44062U, 44324U, 44587U, 44849U, 45111U, 45374U, + 45636U, 45898U, 46160U, 46423U, 46685U, 46947U, + 47210U, 47472U, 47734U, 47996U, 48259U, 48521U, + 48783U, 49045U, 49308U, 49570U, 49832U, 50095U, + 50357U, 50619U, 50881U, 51144U, 51406U, 51668U, + 51930U, 52193U, 52455U, 52717U, 52980U, 53242U, + 53504U, 53766U, 54029U, 54291U, 54553U, 54816U, + 55078U, 55340U, 55602U, 55865U, 56127U, 56389U, + 56651U, 56914U, 57176U, 57438U, 57701U, 57963U, + 58225U, 58487U, 58750U, 59012U, 59274U, 59536U, + 59799U, 60061U, 60323U, 60586U, 60848U, 61110U, + 61372U, 61635U, 61897U, 62159U, 62421U, 62684U, + 62946U, 63208U, 63471U, 63733U, 63995U, 64257U, + 64520U, 64782U, 65044U, 65307U, UINT16_MAX, UINT16_MAX}; + +ZTEST(math_advanced_functions_suite, test_math_arithmetic_sqrt_fixed) +{ + uint32_t u[ARRAY_SIZE(uv)]; + int i; + double y; + double diff; + int ret; + + BUILD_ASSERT(ARRAY_SIZE(uv) == ARRAY_SIZE(sqrt_ref_table), + "Test vector size must match reference table size"); + + ret = memcpy_s(u, sizeof(u), uv, sizeof(uv)); + zassert_equal(ret, 0, "memcpy_s failed: %d", ret); + + for (i = 0; i < ARRAY_SIZE(sqrt_ref_table); i++) { + y = (double)Q_CONVERT_QTOF(sofm_sqrt_int16(u[i]), 12); + diff = fabs(sqrt_ref_table[i] - y); + + if (diff > CMP_TOLERANCE) { + zassert_true(diff <= CMP_TOLERANCE, + "sqrt delta=%f ref=%f actual=%f index=%d", + diff, sqrt_ref_table[i], y, i); + } + } +} diff --git a/test/ztest/unit/math/advanced/functions/testcase.yaml b/test/ztest/unit/math/advanced/functions/testcase.yaml new file mode 100644 index 000000000000..1d58ccae0591 --- /dev/null +++ b/test/ztest/unit/math/advanced/functions/testcase.yaml @@ -0,0 +1,17 @@ +# SPDX-License-Identifier: BSD-3-Clause +# +# Copyright(c) 2025 Intel Corporation. All rights reserved. +# +# Math advanced functions unit tests for Ztest framework +# +# These contents may have been developed with support from one or more Intel-operated +# generative artificial intelligence solutions. +# + +tests: + sof.unit.math.advanced.functions: + tags: math advanced functions power logarithm base2 base10 exponential exp db2lin sqrt log10 + platform_allow: native_sim + integration_platforms: + - native_sim + build_only: false diff --git a/test/ztest/unit/math/basic/arithmetic/CMakeLists.txt b/test/ztest/unit/math/basic/arithmetic/CMakeLists.txt index b6822ca7f2a2..b71031491abf 100644 --- a/test/ztest/unit/math/basic/arithmetic/CMakeLists.txt +++ b/test/ztest/unit/math/basic/arithmetic/CMakeLists.txt @@ -12,7 +12,6 @@ target_include_directories(app PRIVATE # Define SOF-specific configurations for unit testing target_compile_definitions(app PRIVATE - -DCONFIG_SOF_LOG_LEVEL=CONFIG_LOG_DEFAULT_LEVEL -DCONFIG_ZEPHYR_POSIX=1 -DCONFIG_LIBRARY=1 -DCONFIG_NUMBERS_VECTOR_FIND=1 @@ -27,6 +26,7 @@ target_sources(app PRIVATE test_find_min_int16_ztest.c test_find_max_abs_int32_ztest.c test_norm_int32_ztest.c + test_crc32_ztest.c ${SOF_ROOT}/src/math/numbers.c ) diff --git a/test/ztest/unit/math/basic/arithmetic/prj.conf b/test/ztest/unit/math/basic/arithmetic/prj.conf index 9467c2926896..d34c7781cd0a 100644 --- a/test/ztest/unit/math/basic/arithmetic/prj.conf +++ b/test/ztest/unit/math/basic/arithmetic/prj.conf @@ -1 +1,2 @@ CONFIG_ZTEST=y +CONFIG_SOF_FULL_ZEPHYR_APPLICATION=n diff --git a/test/ztest/unit/math/basic/arithmetic/test_ceil_divide_ztest.c b/test/ztest/unit/math/basic/arithmetic/test_ceil_divide_ztest.c index 91203e376c04..e9ad0c7833a7 100644 --- a/test/ztest/unit/math/basic/arithmetic/test_ceil_divide_ztest.c +++ b/test/ztest/unit/math/basic/arithmetic/test_ceil_divide_ztest.c @@ -6,6 +6,8 @@ // generative artificial intelligence solutions. // // Converted from CMock to Ztest +// Original file: sof/test/cmocka/src/math/numbers/ceil_divide.c +// Author: Slawomir Blauciak <slawomir.blauciak@linux.intel.com> #include <zephyr/ztest.h> #include <sof/math/numbers.h> diff --git a/test/ztest/unit/math/basic/arithmetic/test_crc32_ztest.c b/test/ztest/unit/math/basic/arithmetic/test_crc32_ztest.c new file mode 100644 index 000000000000..7485fc13dbc8 --- /dev/null +++ b/test/ztest/unit/math/basic/arithmetic/test_crc32_ztest.c @@ -0,0 +1,104 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// Copyright(c) 2026 Intel Corporation. All rights reserved. +// +// Author: Tomasz Leman <tomasz.m.leman@intel.com> + +#include <zephyr/ztest.h> +#include <sof/math/numbers.h> +#include <stdint.h> + +/** + * @brief Test crc32 with an empty buffer + * + * With zero bytes the outer loop does not execute and the function returns + * ~(~base) == 0 for base == 0. + */ +ZTEST(math_arithmetic_suite, test_math_numbers_crc32_empty_buffer) +{ + uint8_t data[1] = {0}; + uint32_t result = crc32(0, data, 0); + + zassert_equal(result, 0x00000000U, + "crc32 of empty buffer with base=0 should be 0x00000000"); +} + +/** + * @brief Test crc32 with a single zero byte + * + * Exercises one iteration of the outer loop and all 8 iterations of the + * inner bit-processing loop. + */ +ZTEST(math_arithmetic_suite, test_math_numbers_crc32_single_zero_byte) +{ + uint8_t data[] = {0x00}; + uint32_t result = crc32(0, data, sizeof(data)); + + zassert_equal(result, 0xD202EF8DU, + "crc32({0x00}, base=0) should be 0xD202EF8D"); +} + +/** + * @brief Test crc32 with a single 0xFF byte + * + * Exercises the branch where (cur & 1) is false in the inner loop. + */ +ZTEST(math_arithmetic_suite, test_math_numbers_crc32_single_ff_byte) +{ + uint8_t data[] = {0xFF}; + uint32_t result = crc32(0, data, sizeof(data)); + + zassert_equal(result, 0xFF000000U, + "crc32({0xFF}, base=0) should be 0xFF000000"); +} + +/** + * @brief Test crc32 against the well-known CRC-32 check value + * + * The string "123456789" must produce 0xCBF43926 — the standard reference + * value for CRC-32 (ISO 3309 / ITU-T V.42). + */ +ZTEST(math_arithmetic_suite, test_math_numbers_crc32_known_vector_123456789) +{ + const uint8_t data[] = "123456789"; + uint32_t result = crc32(0, data, sizeof(data) - 1); /* exclude NUL */ + + zassert_equal(result, 0xCBF43926U, + "crc32(\"123456789\", base=0) should be 0xCBF43926"); +} + +/** + * @brief Test crc32 with a non-zero base + * + * A non-zero base changes the initial CRC register value (~base), allowing + * incremental / chained CRC computation. Verifies the result differs from the + * base=0 case and matches the expected precomputed value. + */ +ZTEST(math_arithmetic_suite, test_math_numbers_crc32_nonzero_base) +{ + uint8_t data[] = {0xAB, 0xCD, 0xEF}; + uint32_t result_base0 = crc32(0x00000000U, data, sizeof(data)); + uint32_t result_nonzero = crc32(0xDEADBEEFU, data, sizeof(data)); + + zassert_equal(result_base0, 0x648D3D79U, + "crc32({0xAB,0xCD,0xEF}, base=0) should be 0x648D3D79"); + zassert_equal(result_nonzero, 0x1412F659U, + "crc32({0xAB,0xCD,0xEF}, base=0xDEADBEEF) should be 0x1412F659"); + zassert_not_equal(result_base0, result_nonzero, + "Different bases must yield different CRC values"); +} + +/** + * @brief Test crc32 over a four-byte all-zeros buffer + * + * Exercises multiple iterations of the outer byte loop, producing a result + * that differs from the single-byte zero-byte case. + */ +ZTEST(math_arithmetic_suite, test_math_numbers_crc32_four_zero_bytes) +{ + uint8_t data[] = {0x00, 0x00, 0x00, 0x00}; + uint32_t result = crc32(0, data, sizeof(data)); + + zassert_equal(result, 0x2144DF1CU, + "crc32({0,0,0,0}, base=0) should be 0x2144DF1C"); +} diff --git a/test/ztest/unit/math/basic/arithmetic/test_find_equal_int16_ztest.c b/test/ztest/unit/math/basic/arithmetic/test_find_equal_int16_ztest.c index ce224907cdc0..268b031eb3e2 100644 --- a/test/ztest/unit/math/basic/arithmetic/test_find_equal_int16_ztest.c +++ b/test/ztest/unit/math/basic/arithmetic/test_find_equal_int16_ztest.c @@ -6,6 +6,8 @@ // generative artificial intelligence solutions. // // Converted from CMock to Ztest +// Original tests from test/cmocka/src/math/numbers/find_equal_int16.c: +// Author: Slawomir Blauciak <slawomir.blauciak@linux.intel.com> #include <zephyr/ztest.h> #include <sof/math/numbers.h> diff --git a/test/ztest/unit/math/basic/arithmetic/test_find_max_abs_int32_ztest.c b/test/ztest/unit/math/basic/arithmetic/test_find_max_abs_int32_ztest.c index 6a6fd78468de..7b978188c9a7 100644 --- a/test/ztest/unit/math/basic/arithmetic/test_find_max_abs_int32_ztest.c +++ b/test/ztest/unit/math/basic/arithmetic/test_find_max_abs_int32_ztest.c @@ -6,6 +6,8 @@ // generative artificial intelligence solutions. // // Converted from CMock to Ztest +// Original tests from test/cmocka/src/math/numbers/find_max_abs_int32.c: +// Author: Slawomir Blauciak <slawomir.blauciak@linux.intel.com> #include <zephyr/ztest.h> #include <sof/math/numbers.h> diff --git a/test/ztest/unit/math/basic/arithmetic/test_find_min_int16_ztest.c b/test/ztest/unit/math/basic/arithmetic/test_find_min_int16_ztest.c index cbf6c86dc88c..ac9225081a43 100644 --- a/test/ztest/unit/math/basic/arithmetic/test_find_min_int16_ztest.c +++ b/test/ztest/unit/math/basic/arithmetic/test_find_min_int16_ztest.c @@ -6,6 +6,8 @@ // generative artificial intelligence solutions. // // Converted from CMock to Ztest +// Original tests from test/cmocka/src/math/numbers/find_min_int16.c: +// Author: Slawomir Blauciak <slawomir.blauciak@linux.intel.com> #include <zephyr/ztest.h> #include <sof/math/numbers.h> diff --git a/test/ztest/unit/math/basic/arithmetic/test_gcd_ztest.c b/test/ztest/unit/math/basic/arithmetic/test_gcd_ztest.c index 821210cac5a8..508afaecb8e1 100644 --- a/test/ztest/unit/math/basic/arithmetic/test_gcd_ztest.c +++ b/test/ztest/unit/math/basic/arithmetic/test_gcd_ztest.c @@ -6,6 +6,9 @@ // generative artificial intelligence solutions. // // Converted from CMock to Ztest +// Original tests from test/cmocka/src/math/numbers/gcd.c: +// Author: Marcin Maka <marcin.maka@linux.intel.com> +// Janusz Jankowski <janusz.jankowski@linux.intel.com> #include <zephyr/ztest.h> #include <sof/math/numbers.h> diff --git a/test/ztest/unit/math/basic/arithmetic/test_norm_int32_ztest.c b/test/ztest/unit/math/basic/arithmetic/test_norm_int32_ztest.c index fddb689d3471..4e3d1b830817 100644 --- a/test/ztest/unit/math/basic/arithmetic/test_norm_int32_ztest.c +++ b/test/ztest/unit/math/basic/arithmetic/test_norm_int32_ztest.c @@ -6,6 +6,8 @@ // generative artificial intelligence solutions. // // Converted from CMock to Ztest +// Original tests from test/cmocka/src/math/numbers/norm_int32.c: +// Author: Slawomir Blauciak <slawomir.blauciak@linux.intel.com> #include <zephyr/ztest.h> #include <sof/math/numbers.h> diff --git a/test/ztest/unit/math/basic/arithmetic/testcase.yaml b/test/ztest/unit/math/basic/arithmetic/testcase.yaml index ca2c40938dae..8afd7eca7daa 100644 --- a/test/ztest/unit/math/basic/arithmetic/testcase.yaml +++ b/test/ztest/unit/math/basic/arithmetic/testcase.yaml @@ -9,7 +9,7 @@ # tests: - math.basic.arithmetic: + sof.unit.math.basic.arithmetic: tags: math arithmetic numbers platform_allow: native_sim integration_platforms: diff --git a/test/ztest/unit/math/basic/complex/CMakeLists.txt b/test/ztest/unit/math/basic/complex/CMakeLists.txt new file mode 100644 index 000000000000..db756e341b5b --- /dev/null +++ b/test/ztest/unit/math/basic/complex/CMakeLists.txt @@ -0,0 +1,29 @@ +cmake_minimum_required(VERSION 3.20.0) + +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(test_math_complex) + +set(SOF_ROOT "${PROJECT_SOURCE_DIR}/../../../../../..") + +target_include_directories(app PRIVATE + ${SOF_ROOT}/zephyr/include + ${SOF_ROOT}/src/include +) + +# Define SOF-specific configurations for unit testing +target_compile_definitions(app PRIVATE + -DCONFIG_ZEPHYR_POSIX=1 + -DCONFIG_LIBRARY=1 + -DUNIT_TEST=1 +) + +target_sources(app PRIVATE + test_complex_polar.c + ${SOF_ROOT}/src/math/complex.c + ${SOF_ROOT}/src/math/sqrt_int32.c + ${SOF_ROOT}/src/math/trig.c + ${SOF_ROOT}/src/math/atan2.c +) + +# Link math library for standard math functions +target_link_libraries(app PRIVATE m) diff --git a/test/ztest/unit/math/basic/complex/prj.conf b/test/ztest/unit/math/basic/complex/prj.conf new file mode 100644 index 000000000000..d34c7781cd0a --- /dev/null +++ b/test/ztest/unit/math/basic/complex/prj.conf @@ -0,0 +1,2 @@ +CONFIG_ZTEST=y +CONFIG_SOF_FULL_ZEPHYR_APPLICATION=n diff --git a/test/ztest/unit/math/basic/complex/test_complex_polar.c b/test/ztest/unit/math/basic/complex/test_complex_polar.c new file mode 100644 index 000000000000..22d5e1d3471b --- /dev/null +++ b/test/ztest/unit/math/basic/complex/test_complex_polar.c @@ -0,0 +1,129 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// Copyright(c) 2026 Intel Corporation. + +#include <math.h> +#include <sof/common.h> +#include <sof/math/icomplex32.h> +#include <zephyr/logging/log.h> +#include <zephyr/ztest.h> + +/* Test data tables from Octave generated reference */ +#include "test_complex_polar_tables.h" + +LOG_MODULE_REGISTER(test_complex_polar, LOG_LEVEL_INF); + +#define COMPLEX_ABS_TOL 1.2e-8 +#define MAGNITUDE_ABS_TOL 7.1e-8 +#define ANGLE_ABS_TOL 2.0e-5 + +/** + * @brief Test complex to polar conversion function + * + * This test validates the icomplex32_to_polar() function against + * Octave-generated reference values. The test includes 1000 data points. + * + * Complex number values are Q1.31 -1.0 to +1.0 + * Polar magnitude values are Q2.30 0 to +2.0 + * Polar angle values are Q3.29 from -pi to +pi + */ +ZTEST(math_complex, test_icomplex32_to_polar) +{ + struct icomplex32 complex, complex_mag_max, complex_ang_max; + struct ipolar32 polar; + double ref_magnitude, ref_angle; + double magnitude, angle; + double delta_mag, delta_ang; + double magnitude_scale_q30 = 1.0 / 1073741824.0; /* 1.0 / 2^30 */ + double angle_scale_q29 = 1.0 / 536870912.0; /* 1.0 / 2^29 */ + double delta_mag_max = 0; + double delta_ang_max = 0; + int i; + + for (i = 0; i < TEST_COMPLEX_POLAR_NUM_POINTS; i++) { + complex.real = test_real_values[i]; + complex.imag = test_imag_values[i]; + ref_magnitude = magnitude_scale_q30 * test_magnitude_values[i]; + ref_angle = angle_scale_q29 * test_angle_values[i]; + sofm_icomplex32_to_polar(&complex, &polar); + + magnitude = magnitude_scale_q30 * polar.magnitude; + delta_mag = fabs(ref_magnitude - magnitude); + if (delta_mag > delta_mag_max) { + delta_mag_max = delta_mag; + complex_mag_max = complex; + } + + angle = angle_scale_q29 * polar.angle; + delta_ang = fabs(ref_angle - angle); + if (delta_ang > delta_ang_max) { + delta_ang_max = delta_ang; + complex_ang_max = complex; + } + + zassert_true(delta_mag <= MAGNITUDE_ABS_TOL, "Magnitude calc error at (%d, %d)", + complex.real, complex.imag); + zassert_true(delta_ang <= ANGLE_ABS_TOL, "Angle calc error at (%d, %d)", + complex.real, complex.imag); + } + + /* Re-run worst cases to print info */ + sofm_icomplex32_to_polar(&complex_mag_max, &polar); + printf("delta_mag_max = %g at (%d, %d) -> (%d, %d)\n", delta_mag_max, complex_mag_max.real, + complex_mag_max.imag, polar.magnitude, polar.angle); + + sofm_icomplex32_to_polar(&complex_ang_max, &polar); + printf("delta_ang_max = %g at (%d, %d) -> (%d, %d)\n", delta_ang_max, complex_ang_max.real, + complex_ang_max.imag, polar.magnitude, polar.angle); +} + +ZTEST(math_complex, test_ipolar32_to_complex) +{ + struct icomplex32 complex; + struct ipolar32 polar, polar_real_max, polar_imag_max; + double ref_real, ref_imag; + double real, imag; + double delta_real, delta_imag; + double scale_q31 = 1.0 / 2147483648.0; + double delta_real_max = 0; + double delta_imag_max = 0; + int i; + + for (i = 0; i < TEST_COMPLEX_POLAR_NUM_POINTS; i++) { + polar.magnitude = test_magnitude_values[i]; + polar.angle = test_angle_values[i]; + ref_real = scale_q31 * test_real_values[i]; + ref_imag = scale_q31 * test_imag_values[i]; + sofm_ipolar32_to_complex(&polar, &complex); + + real = scale_q31 * complex.real; + delta_real = fabs(ref_real - real); + if (delta_real > delta_real_max) { + delta_real_max = delta_real; + polar_real_max = polar; + } + + imag = scale_q31 * complex.imag; + delta_imag = fabs(ref_imag - imag); + if (delta_imag > delta_imag_max) { + delta_imag_max = delta_imag; + polar_imag_max = polar; + } + + zassert_true(delta_real <= COMPLEX_ABS_TOL, "Real calc error at (%d, %d)", + polar.magnitude, polar.angle); + zassert_true(delta_imag <= COMPLEX_ABS_TOL, "Imag calc error at (%d, %d)", + polar.magnitude, polar.angle); + } + + /* Re-run worst cases to print info */ + sofm_ipolar32_to_complex(&polar_real_max, &complex); + printf(" INFO - delta_real_max = %g at (%d, %d) -> (%d, %d)\n", delta_real_max, + polar_real_max.magnitude, polar_real_max.angle, complex.real, complex.imag); + + sofm_ipolar32_to_complex(&polar_imag_max, &complex); + printf(" INFO - delta_imag_max = %g at (%d, %d) -> (%d, %d)\n", delta_imag_max, + polar_imag_max.magnitude, polar_imag_max.angle, complex.real, complex.imag); +} + +ZTEST_SUITE(math_complex, NULL, NULL, NULL, NULL, NULL); diff --git a/test/ztest/unit/math/basic/complex/test_complex_polar_reference.m b/test/ztest/unit/math/basic/complex/test_complex_polar_reference.m new file mode 100644 index 000000000000..5f7bed37b682 --- /dev/null +++ b/test/ztest/unit/math/basic/complex/test_complex_polar_reference.m @@ -0,0 +1,78 @@ +function test_complex_polar_reference() + + % Make a box flattened spiral of (re,im) values to exercise the + % Q1.31 complex numbers range + q31_scale = 2^31; + num_points = 1000; + magnitude0 = linspace(0, sqrt(2), num_points); + angle0 = pi/4 + linspace(0, 10*2*pi, num_points); + re = max(min(magnitude0 .* cos(angle0), 1), -1); + im = max(min(magnitude0 .* sin(angle0), 1), -1); + ref_re = int32(re * q31_scale); + ref_im = int32(im * q31_scale); + re = double(ref_re)/q31_scale; + im = double(ref_im)/q31_scale; + figure(1) + plot(re, im) + grid on + + % In polar format magnitude is Q2.30 and angle Q3.29 + q29_scale = 2^29; + q30_scale = 2^30; + magnitude = sqrt(re.^2 + im.^2); + phase = angle(complex(re, im)); + ref_magnitude = int32(magnitude * q30_scale); + ref_angle = int32(phase * q29_scale); + magnitude = double(ref_magnitude)/q30_scale; + phase = double(ref_angle)/q29_scale; + + figure(2) + subplot(2,1,1); + plot(magnitude); + grid on + subplot(2,1,2); + plot(phase); + grid on + + fh = export_headerfile('test_complex_polar_tables.h'); + dn = 'TEST_COMPLEX_POLAR_NUM_POINTS'; + vl = 6; + export_define(fh, dn, num_points); + export_array(fh, 'test_real_values', dn, vl, ref_re); + export_array(fh, 'test_imag_values', dn, vl, ref_im); + export_array(fh, 'test_magnitude_values', dn, vl, ref_magnitude); + export_array(fh, 'test_angle_values', dn, vl, ref_angle); + fclose(fh); + +end + +function fh = export_headerfile(headerfn) + fh = fopen(headerfn, 'w'); + fprintf(fh, '/* SPDX-License-Identifier: BSD-3-Clause\n'); + fprintf(fh, ' *\n'); + fprintf(fh, ' * Copyright(c) %s Intel Corporation.\n', ... + datestr(now, 'yyyy')); + fprintf(fh, ' */\n\n'); +end + +function export_define(fh, dn, val) + fprintf(fh, '#define %s %d\n', dn, val); +end + +function export_array(fh, vn, size_str, vl, data) + fprintf(fh, '\n'); + fprintf(fh, 'static const int32_t %s[%s] = {\n', vn, size_str); + + n = length(data); + k = 0; + for i = 1:vl:n + fprintf(fh, '\t'); + for j = 1:min(vl, n-k) + k = k + 1; + fprintf(fh, '%12d,', data(k)); + end + fprintf(fh, '\n'); + end + + fprintf(fh, '};\n'); +end diff --git a/test/ztest/unit/math/basic/complex/test_complex_polar_tables.h b/test/ztest/unit/math/basic/complex/test_complex_polar_tables.h new file mode 100644 index 000000000000..638768767ae6 --- /dev/null +++ b/test/ztest/unit/math/basic/complex/test_complex_polar_tables.h @@ -0,0 +1,686 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * + * Copyright(c) 2026 Intel Corporation. + */ + +#define TEST_COMPLEX_POLAR_NUM_POINTS 1000 + +static const int32_t test_real_values[TEST_COMPLEX_POLAR_NUM_POINTS] = { + 0, 2010271, 3725920, 5124845, 6187393, 6896488, + 7237756, 7199623, 6773402, 5953363, 4736785, 3123990, + 1118360, -1273667, -4042611, -7175994, -10658392, -14471508, + -18594262, -23002899, -27671114, -32570187, -37669141, -42934909, + -48332514, -53825266, -59374967, -64942126, -70486186, -75965757, + -81338858, -86563159, -91596238, -96395831, -100920087, -105127827, + -108978795, -112433915, -115455535, -118007677, -120056267, -121569369, + -122517407, -122873369, -122613014, -121715052, -120161316, -117936927, + -115030428, -111433912, -107143132, -102157588, -96480602, -90119364, + -83084968, -75392423, -67060644, -58112422, -48574376, -38476879, + -27853971, -16743244, -5185716, 6774328, 19089497, 31709487, + 44581284, 57649387, 70856046, 84141512, 97444305, 110701494, + 123848984, 136821814, 149554472, 161981203, 174036334, 185654596, + 196771457, 207323444, 217248478, 226486192, 234978262, 242668716, + 249504251, 255434527, 260412465, 264394521, 267340956, 269216089, + 269988529, 269631397, 268122524, 265444636, 261585512, 256538120, + 250300736, 242877038, 234276169, 224512787, 213607079, 201584756, + 188477021, 174320506, 159157195, 143034304, 126004153, 108123999, + 89455850, 70066259, 50026081, 29410226, 8297371, -13230336, + -35087593, -57186288, -79435849, -101743616, -124015229, -146155020, + -168066426, -189652405, -210815858, -231460066, -251489116, -270808344, + -289324766, -306947511, -323588255, -339161636, -353585678, -366782188, + -378677155, -389201124, -398289563, -405883204, -411928378, -416377312, + -419188416, -420326545, -419763226, -417476873, -413452963, -407684185, + -400170564, -390919549, -379946071, -367272574, -352929002, -336952767, + -319388673, -300288814, -279712434, -257725762, -234401803, -209820111, + -184066521, -157232854, -129416598, -100720550, -71252444, -41124543, + -10453216, 20641515, 52036441, 83605659, 115221069, 146752903, + 178070250, 209041601, 239535398, 269420591, 298567200, 326846870, + 354133435, 380303474, 405236856, 428817285, 450932827, 471476430, + 490346422, 507446994, 522688663, 535988713, 547271612, 556469398, + 563522043, 568377780, 570993410, 571334557, 569375910, 565101409, + 558504407, 549587788, 538364050, 524855342, 509093464, 491119833, + 470985394, 448750502, 424484760, 398266816, 370184118, 340332636, + 308816539, 275747839, 241246001, 205437508, 168455409, 130438820, + 91532404, 51885823, 11653162, -29007671, -69935565, -110966841, + -151935909, -192675939, -233019531, -272799413, -311849123, -350003711, + -387100428, -422979418, -457484405, -490463364, -521769184, -551260318, + -578801413, -604263917, -627526666, -648476446, -667008521, -683027136, + -696445987, -707188646, -715188962, -720391416, -722751431, -722235644, + -718822138, -712500616, -703272542, -691151224, -676161857, -658341510, + -637739067, -614415122, -588441819, -559902651, -528892197, -495515829, + -459889360, -422138650, -382399165, -340815504, -297540866, -252736498, + -206571092, -159220151, -110865324, -61693712, -11897138, 38328592, + 88784468, 139269058, 189579315, 239511395, 288861476, 337426598, + 385005490, 431399405, 476412946, 519854889, 561538988, 601284771, + 638918316, 674273001, 707190239, 737520175, 765122358, 789866376, + 811632461, 830312042, 845808268, 858036480, 866924641, 872413711, + 874457976, 873025324, 868097468, 859670112, 847753061, 832370279, + 813559881, 791374074, 765879040, 737154758, 705294769, 670405888, + 632607856, 592032937, 548825466, 503141340, 455147462, 405021134, + 352949406, 299128380, 243762474, 187063647, 129250588, 70547873, + 11185092, -48604046, -108582637, -168511515, -228150215, -287257925, + -345594471, -402921282, -459002370, -513605298, -566502143, -617470444, + -666294131, -712764442, -756680808, -797851714, -836095524, -871241275, + -903129435, -931612608, -956556208, -977839072, -995354034, -1009008434, + -1018724582, -1024440155, -1026108541, -1023699117, -1017197463, -1006605519, + -991941664, -973240745, -950554022, -923949063, -893509556, -859335070, + -821540743, -780256901, -735628623, -687815235, -636989754, -583338260, + -527059225, -468362778, -407469927, -344611725, -280028399, -213968429, + -146687596, -78447990, -9516990, 59833783, 129329538, 198693400, + 267647516, 335914167, 403216884, 469281571, 533837615, 596618999, + 657365393, 715823231, 771746768, 824899108, 875053204, 921992824, + 965513473, 1005423279, 1041543829, 1073710959, 1101775481, 1125603867, + 1145078861, 1160100036, 1170584284, 1176466239, 1177698625, 1174252547, + 1166117690, 1153302463, 1135834055, 1113758421, 1087140193, 1056062514, + 1020626795, 980952399, 937176255, 889452389, 837951397, 782859836, + 724379560, 662726978, 598132266, 530838500, 461100754, 389185126, + 315367731, 239933637, 163175768, 85393765, 6892818, -72017533, + -151024619, -229813879, -308070111, -385478738, -461727067, -536505555, + -609509063, -680438101, -749000052, -814910383, -877893819, -937685487, + -994032034, -1046692688, -1095440283, -1140062237, -1180361463, -1216157237, + -1247285993, -1273602059, -1294978319, -1311306812, -1322499248, -1328487452, + -1329223727, -1324681139, -1314853718, -1299756576, -1279425943, -1253919113, + -1223314312, -1187710477, -1147226950, -1102003096, -1052197829, -997989064, + -939573091, -877163865, -810992232, -741305080, -668364414, -592446383, + -513840229, -432847191, -349779351, -264958429, -178714543, -91384921, + -3312582, 85155009, 173667311, 261872102, 349416872, 435950238, + 521123353, 604591309, 686014536, 765060179, 841403464, 914729020, + 984732190, 1051120287, 1113613817, 1171947650, 1225872142, 1275154193, + 1319578251, 1358947246, 1393083452, 1421829276, 1445047976, 1462624287, + 1474464972, 1480499286, 1480679346, 1474980420, 1463401119, 1445963494, + 1422713045, 1393718635, 1359072305, 1318889005, 1273306222, 1222483522, + 1166602002, 1105863652, 1040490631, 970724462, 896825138, 819070164, + 737753515, 653184524, 565686712, 475596548, 383262159, 289041982, + 193303378, 96421195, -1223696, -99245902, -197257024, -294867199, + -391686652, -487327250, -581404058, -673536888, -763351836, -850482796, + -934572954, -1015276244, -1092258774, -1165200200, -1233795059, -1297754046, + -1356805225, -1410695180, -1459190100, -1502076781, -1539163558, -1570281144, + -1595283397, -1614047983, -1626476954, -1632497230, -1632060983, -1625145921, + -1611755471, -1591918856, -1565691082, -1533152805, -1494410107, -1449594166, + -1398860823, -1342390050, -1280385322, -1213072890, -1140700959, -1063538782, + -981875656, -896019849, -806297430, -713051040, -616638580, -517431842, + -415815073, -312183493, -206941750, -100502348, 6715973, 114289887, + 221793148, 328798283, 434878288, 539608337, 642567479, 743340331, + 841518752, 936703496, 1028505836, 1116549146, 1200470449, 1279921905, + 1354572256, 1424108199, 1488235698, 1546681223, 1599192907, 1645541631, + 1685522007, 1718953282, 1745680143, 1765573419, 1778530688, 1784476774, + 1783364142, 1775173175, 1759912353, 1737618309, 1708355783, 1672217452, + 1629323661, 1579822034, 1523886975, 1461719066, 1393544352, 1319613531, + 1240201029, 1155603995, 1066141184, 972151767, 873994039, 772044064, + 666694228, 558351735, 447437031, 334382174, 219629153, 103628158, + -13164188, -130286620, -247275059, -363664447, -478990597, -592792047, + -704611899, -813999660, -920513054, -1023719807, -1123199405, -1218544801, + -1309364080, -1395282072, -1475941891, -1551006421, -1620159719, -1683108337, + -1739582565, -1789337578, -1832154489, -1867841304, -1896233769, -1917196112, + -1930621670, -1936433411, -1934584326, -1925057716, -1907867350, -1883057503, + -1850702880, -1810908409, -1763808919, -1709568693, -1648380911, -1580466964, + -1506075665, -1425482338, -1338987809, -1246917285, -1149619133, -1047463566, + -940841237, -830161746, -715852069, -598354909, -478126986, -355637260, + -231365102, -105798420, 20568260, 147235736, 273702109, 399464766, + 524022380, 646876907, 767535579, 885512881, 1000332498, 1111529244, + 1218650944, 1321260268, 1418936519, 1511277353, 1597900434, 1678445014, + 1752573434, 1819972530, 1880354956, 1933460397, 1979056689, 2016940822, + 2046939832, 2068911577, 2082745395, 2088362629, 2085717041, 2074795083, + 2055616049, 2028232093, 1992728115, 1949221519, 1897861838, 1838830236, + 1772338873, 1698630153, 1617975845, 1530676089, 1437058282, 1337475852, + 1232306929, 1121952911, 1006836935, 887402254, 764110532, 637440063, + 507883912, 375948001, 242149128, 107012950, -28928090, -165136854, + -301073636, -436198297, -569972415, -701861428, -831336767, -957877980, + -1080974821, -1200129303, -1314857719, -1424692595, -1529184604, -1627904392, + -1720444345, -1806420269, -1885472979, -1957269800, -2021505955, -2077905857, + -2126224286, -2147483648, -2147483648, -2147483648, -2147483648, -2147483648, + -2147483648, -2147483648, -2147483648, -2147483648, -2134427233, -2087152629, + -2031478388, -1967602767, -1895757115, -1816205050, -1729241491, -1635191576, + -1534409444, -1427276909, -1314202015, -1195617484, -1071979063, -943763772, + -811468066, -675605914, -536706801, -395313667, -251980784, -107271583, + 38243559, 183989571, 329388955, 473864078, 616839468, 757744102, + 896013690, 1031092931, 1162437746, 1289517467, 1411816980, 1528838814, + 1640105159, 1745159817, 1843570071, 1934928464, 2018854482, 2094996135, + 2147483647, 2147483647, 2147483647, 2147483647, 2147483647, 2147483647, + 2147483647, 2147483647, 2147483647, 2147483647, 2147483647, 2147483647, + 2147483647, 2147483647, 2147483647, 2095882397, 2018631901, 1933188085, + 1839869214, 1739025602, 1631038305, 1516317685, 1395301849, 1268454981, + 1136265566, 999244503, 857923137, 712851198, 564594663, 413733548, + 260859639, 106574173, -48514532, -203793467, -358647365, -512461128, + -664622282, -814523405, -961564559, -1105155685, -1244718977, -1379691200, + -1509525966, -1633695942, -1751694994, -1863040244, -1967274048, -2063965870, + -2147483648, -2147483648, -2147483648, -2147483648, -2147483648, -2147483648, + -2147483648, -2147483648, -2147483648, -2147483648, -2147483648, -2147483648, + -2147483648, -2147483648, -2147483648, -2147483648, -2147483648, -2147483648, + -2140959504, -2049575701, -1949855636, -1842174985, -1726941891, -1604595423, + -1475603904, -1340463117, -1199694407, -1053842666, -903474228, -749174669, + -591546527, -431206951, -268785283, -104920590, 59740851, 224548104, + 388848145, 551988449, 713319584, 872197794, 1027987568, 1180064182, + 1327816205, 1470647954, 1607981896, 1739260983, 1863950908, 1981542280, + 2091552701, 2147483647, 2147483647, 2147483647, 2147483647, 2147483647, + 2147483647, 2147483647, 2147483647, 2147483647, 2147483647, 2147483647, + 2147483647, 2147483647, 2147483647, 2147483647, 2147483647, 2147483647, + 2147483647, 2147483647, 2147483647, 2147483647, 2059197392, 1944636555, + 1822117240, 1692107383, 1555105670, 1411639621, 1262263565, 1107556498, + 948119841, 784575098, 617561439, 447733201, 275757323, 102310727, + -71922343, -246253024, -419990557, -592445023, -762930082, -930765707, + -1095280893, -1255816341, -1411727102, -1562385162, -1707181977, -1845530925, + -1976869684, -2100662514, -2147483648, -2147483648, -2147483648, -2147483648, + -2147483648, -2147483648, -2147483648, -2147483648, -2147483648, -2147483648, + -2147483648, -2147483648, -2147483648, -2147483648, -2147483648, -2147483648, + -2147483648, -2147483648, -2147483648, -2147483648, -2147483648, -2147483648, + -2147483648, -2046407157, -1916561405, -1778850838, -1633804651, -1481982238, + -1323971036, -1160384254, -991858494, -819051274, -642638465, -463311644, + -281775385, -98744494, 85058813, 268907750, 452073843, 633829815, + 813452467, 990225566, 1163442693, 1332410066, 1496449324, 1654900245, + 1807123402, 1952502745, 2090448093, 2147483647, 2147483647, 2147483647, + 2147483647, 2147483647, 2147483647, 2147483647, 2147483647, 2147483647, + 2147483647, 2147483647, 2147483647, 2147483647, 2147483647, 2147483647, + 2147483647, 2147483647, 2147483647, 2147483647, 2147483647, 2147483647, + 2147483647, 2147483647, 2147483647, 2147483647, +}; + +static const int32_t test_imag_values[TEST_COMPLEX_POLAR_NUM_POINTS] = { + 0, 2280495, 4804675, 7544043, 10468320, 13545649, + 16742800, 20025381, 23358065, 26704813, 30029111, 33294205, + 36463340, 39499998, 42368139, 45032439, 47458523, 49613195, + 51464665, 52982763, 54139155, 54907533, 55263817, 55186324, + 54655939, 53656265, 52173763, 50197872, 47721119, 44739207, + 41251086, 37259012, 32768581, 27788749, 22331828, 16413469, + 10052626, 3271493, -3904569, -11447125, -19324782, -27503316, + -35945824, -44612882, -53462730, -62451464, -71533245, -80660524, + -89784269, -98854220, -107819134, -116627055, -125225582, -133562147, + -141584294, -149239968, -156477796, -163247376, -169499562, -175186750, + -180263151, -184685069, -188411163, -191402709, -193623847, -195041816, + -195627177, -195354030, -194200203, -192147436, -189181540, -185292545, + -180474822, -174727190, -168053001, -160460202, -151961376, -142573766, + -132319265, -121224392, -109320247, -96642432, -83230957, -69130128, + -54388396, -39058203, -23195794, -6861013, 9882924, 26969672, + 44330020, 61892171, 79582039, 97323560, 115039017, 132649379, + 150074644, 167234202, 184047196, 200432896, 216311074, 231602379, + 246228722, 260113649, 273182721, 285363882, 296587829, 306788366, + 315902751, 323872039, 330641397, 336160422, 340383425, 343269713, + 344783842, 344895851, 343581478, 340822348, 336606140, 330926726, + 323784289, 315185406, 305143109, 293676920, 280812849, 266583368, + 251027358, 234190024, 216122776, 196883092, 176534343, 155145589, + 132791360, 109551391, 85510349, 60757523, 35386492, 9494776, + -16816541, -43443203, -70278199, -97212191, -124133959, -150930858, + -177489291, -203695186, -229434484, -254593631, -279060074, -302722755, + -325472612, -347203067, -367810515, -387194804, -405259702, -421913360, + -437068750, -450644099, -462563293, -472756271, -481159391, -487715772, + -492375618, -495096503, -495843637, -494590097, -491317033, -486013829, + -478678244, -469316510, -457943399, -444582251, -429264969, -412031974, + -392932124, -372022602, -349368756, -325043912, -299129152, -271713045, + -242891359, -212766730, -181448298, -149051321, -115696746, -81510765, + -46624336, -11172682, 24705234, 60867249, 97168571, 133462352, + 169600285, 205433208, 240811719, 275586798, 309610431, 342736240, + 374820102, 405720778, 435300526, 463425704, 489967369, 514801854, + 537811332, 558884360, 577916399, 594810315, 609476843, 621835035, + 631812669, 639346627, 644383234, 646878573, 646798747, 644120109, + 638829452, 630924157, 620412291, 607312670, 591654878, 573479230, + 552836704, 529788822, 504407482, 476774755, 446982629, 415132713, + 381335905, 345712005, 308389303, 269504118, 229200302, 187628715, + 144946659, 101317283, 56908963, 11894650, -33548800, -79241326, + -125000369, -170641608, -215979698, -260829021, -305004451, -348322109, + -390600134, -431659438, -471324465, -509423935, -545791581, -580266864, + -612695679, -642931032, -670833695, -696272835, -719126612, -739282748, + -756639051, -771103915, -782596769, -791048488, -796401763, -798611419, + -797644690, -793481442, -786114351, -775549022, -761804063, -744911098, + -724914741, -701872496, -675854626, -646943948, -615235591, -580836694, + -543866053, -504453723, -462740564, -418877743, -373026195, -325356033, + -276045917, -225282393, -173259184, -120176451, -66240028, -11660620, + 43347018, 98564927, 153772807, 208748896, 263270860, 317116694, + 370065621, 421899000, 472401226, 521360624, 568570334, 613829180, + 656942528, 697723108, 735991831, 771578561, 804322866, 834074727, + 860695216, 884057126, 904045564, 920558492, 933507222, 942816860, + 948426696, 950290537, 948376986, 942669659, 933167350, 919884121, + 902849348, 882107693, 857719013, 829758219, 798315059, 763493846, + 725413129, 684205295, 640016118, 593004254, 543340674, 491208047, + 436800076, 380320774, 321983708, 262011190, 200633431, 138087661, + 74617206, 10470549, -54099646, -118837530, -183485081, -247783134, + -311472418, -374294603, -435993344, -496315331, -555011323, -611837177, + -666554862, -718933455, -768750105, -815790985, -859852197, -900740653, + -938274911, -972285972, -1002618031, -1029129172, -1051692022, -1070194342, + -1084539559, -1094647244, -1100453524, -1101911425, -1098991157, -1091680325, + -1079984072, -1063925154, -1043543944, -1018898360, -990063728, -957132573, + -920214336, -879435024, -834936792, -786877452, -735429928, -680781633, + -623133796, -562700722, -499709000, -434396656, -367012256, -297813959, + -227068533, -155050322, -82040188, -8324413, 65806424, 140058591, + 214136368, 287743222, 360582998, 432361108, 502785721, 571568950, + 638428026, 703086462, 765275191, 824733684, 881211039, 934467032, + 984273140, 1030413508, 1072685885, 1110902496, 1144890869, 1174494601, + 1199574061, 1220007038, 1235689307, 1246535140, 1252477736, 1253469580, + 1249482727, 1240509005, 1226560146, 1207667830, 1183883652, 1155279014, + 1121944928, 1083991746, 1041548804, 994764001, 943803283, 888850071, + 830104601, 767783203, 702117507, 633353587, 561751041, 487582013, + 411130162, 332689574, 252563637, 171063864, 88508687, 5222207, + -78467074, -162227550, -245725826, -328628042, -410601210, -491314552, + -570440832, -647657683, -722648920, -795105831, -864728447, -931226783, + -994322041, -1053747774, -1109251010, -1160593320, -1207551838, -1249920218, + -1287509538, -1320149123, -1347687313, -1369992150, -1386951992, -1398476047, + -1404494823, -1404960503, -1399847223, -1389151273, -1372891207, -1351107860, + -1323864282, -1291245579, -1253358666, -1210331933, -1162314819, -1109477306, + -1052009323, -990120072, -924037270, -854006320, -780289398, -703164480, + -622924289, -539875196, -454336043, -366636924, -277117911, -186127736, + -94022433, -1163946, 92081298, 185343827, 278252596, 370436457, + 461525644, 551153259, 638956742, 724579345, 807671574, 887892617, + 964911740, 1038409650, 1108079811, 1173629724, 1234782149, 1291276269, + 1342868805, 1389335049, 1430469836, 1466088443, 1496027403, 1520145244, + 1538323138, 1550465462, 1556500276, 1556379693, 1550080173, 1537602702, + 1518972892, 1494240964, 1463481650, 1426793984, 1384301002, 1336149343, + 1282508749, 1223571484, 1159551643, 1090684388, 1017225083, 939448359, + 857647080, 772131254, 683226852, 591274574, 496628537, 399654920, + 300730542, 200241404, 98581176, -3850338, -106648779, -209406823, + -311715799, -413167311, -513354874, -611875536, -708331500, -802331731, + -893493540, -981444140, -1065822168, -1146279171, -1222481036, -1294109383, + -1360862883, -1422458528, -1478632818, -1529142890, -1573767555, -1612308258, + -1644589953, -1670461882, -1689798263, -1702498883, -1708489584, -1707722653, + -1700177108, -1685858871, -1664800843, -1637062866, -1602731579, -1561920169, + -1514768007, -1461440191, -1402126973, -1337043088, -1266426988, -1190539966, + -1109665203, -1024106706, -934188176, -840251780, -742656855, -641778531, + -538006296, -431742487, -323400735, -213404354, -102184684, 9820593, + 122169183, 234415920, 346114537, 456819433, 566087456, 673479672, + 778563134, 880912619, 980112355, 1075757704, 1167456813, 1254832216, + 1337522390, 1415183239, 1487489533, 1554136256, 1614839895, 1669339637, + 1717398481, 1758804263, 1793370582, 1820937624, 1841372888, 1854571804, + 1860458238, 1858984887, 1850133564, 1833915363, 1810370710, 1779569298, + 1741609902, 1696620080, 1644755759, 1586200705, 1521165879, 1449888687, + 1372632115, 1289683769, 1201354806, 1107978767, 1009910327, 907523945, + 801212438, 691385473, 578467993, 462898573, 345127714, 225616089, + 104832744, -16746748, -138642154, -260370483, -381447896, -501391630, + -619721926, -735963941, -849649659, -960319774, -1067525541, -1170830599, + -1269812742, -1364065646, -1453200533, -1536847770, -1614658403, -1686305606, + -1751486049, -1809921176, -1861358393, -1905572145, -1942364901, -1971568024, + -1993042528, -2006679721, -2012401729, -2010161898, -1999945075, -1981767762, + -1955678145, -1921755996, -1880112455, -1830889673, -1774260348, -1710427123, + -1639621871, -1562104859, -1478163796, -1388112772, -1292291083, -1191061960, + -1084811190, -973945653, -858891760, -740093820, -618012319, -493122141, + -365910719, -236876134, -106525162, 24628712, 156067320, 287269855, + 417714940, 546882693, 674256803, 799326595, 921589072, 1040550941, + 1155730603, 1266660098, 1372887010, 1473976304, 1569512113, 1659099440, + 1742365791, 1818962720, 1888567281, 1950883387, 2005643062, 2052607583, + 2091568519, 2122348637, 2144802698, 2147483647, 2147483647, 2147483647, + 2147483647, 2129411660, 2100718805, 2063618704, 2018235083, 1964724911, + 1903277871, 1834115693, 1757491361, 1673688197, 1583018816, 1485823964, + 1382471243, 1273353720, 1158888438, 1039514824, 915692999, 787902012, + 656637981, 522412174, 385749013, 247184030, 107261763, -33466378, + -174444288, -315113364, -454914717, -593291393, -729690591, -863565873, + -994379352, -1121603851, -1244725030, -1363243461, -1476676654, -1584561022, + -1686453767, -1781934703, -1870607982, -1952103729, -2026079585, -2092222141, + -2147483648, -2147483648, -2147483648, -2147483648, -2147483648, -2147483648, + -2147483648, -2147483648, -2147483648, -2147483648, -2147483648, -2147483648, + -2147483648, -2098121767, -2031804437, -1957262673, -1874770773, -1784635305, + -1687193971, -1582814348, -1471892505, -1354851496, -1232139760, -1104229395, + -971614348, -834808505, -694343705, -550767672, -404641875, -256539338, + -107042392, 43259617, 193772649, 343900318, 493046257, 640616484, + 786021771, 928679993, 1068018461, 1203476217, 1334506296, 1460577931, + 1581178701, 1695816616, 1804022117, 1905350001, 1999381249, 2085724755, + 2147483647, 2147483647, 2147483647, 2147483647, 2147483647, 2147483647, + 2147483647, 2147483647, 2147483647, 2147483647, 2147483647, 2147483647, + 2147483647, 2147483647, 2147483647, 2147483647, 2147483647, 2079864332, + 1991456543, 1894942798, 1790686072, 1679080943, 1560552105, 1435552756, + 1304562862, 1168087321, 1026654016, 880811774, 731128237, 578187655, + 422588602, 264941637, 105866910, -54008284, -214051971, -373630004, + -532108568, -688856702, -843248811, -994667159, -1142504345, -1286165736, + -1425071859, -1558660737, -1686390161, -1807739892, -1922213774, -2029341764, + -2128681858, -2147483648, -2147483648, -2147483648, -2147483648, -2147483648, + -2147483648, -2147483648, -2147483648, -2147483648, -2147483648, -2147483648, + -2147483648, -2147483648, -2147483648, -2147483648, -2147483648, -2147483648, + -2147483648, -2147483648, -2107545118, -2004607307, -1893491944, -1774620780, + -1648447297, -1515454979, -1376155464, -1231086572, -1080810233, -925910312, + -766990339, -604671161, -439588511, -272390527, -103735200, 65712212, + 235281808, 404301694, 572100644, 738010765, 901370158, 1061525555, + 1217834934, 1369670091, 1516419162, 1657489093, 1792308030, 1920327639, + 2041025335, 2147483647, 2147483647, 2147483647, 2147483647, 2147483647, + 2147483647, 2147483647, 2147483647, 2147483647, 2147483647, 2147483647, + 2147483647, 2147483647, 2147483647, 2147483647, 2147483647, 2147483647, + 2147483647, 2147483647, 2147483647, 2147483647, 2147483647, 2113625473, + 1995608424, 1869430904, 1735575344, 1594555664, 1446915305, 1293225136, + 1134081244, 970102629, 801928792, 630217245, 455640938, 278885624, + 100647162, -78371218, -257461692, -435914639, -613021457, -788077372, + -960384243, -1129253349, -1294008139, -1453986944, -1608545631, -1757060198, + -1898929290, -2033576637, -2147483648, -2147483648, -2147483648, -2147483648, + -2147483648, -2147483648, -2147483648, -2147483648, -2147483648, -2147483648, + -2147483648, -2147483648, -2147483648, -2147483648, -2147483648, -2147483648, + -2147483648, -2147483648, -2147483648, -2147483648, -2147483648, -2147483648, + -2147483648, -2147483648, -2097032362, -1963508376, -1821933528, -1672852323, + -1516840140, -1354501017, -1186465312, -1013387253, -835942395, -654824984, + -470745238, -284426564, -96602715, 91985099, 280591136, 468468072, + 654869963, 839055203, 1020289478, 1197848691, 1371021856, 1539113946, + 1701448678, 1857371236, 2006250910, 2147483647, +}; + +static const int32_t test_magnitude_values[TEST_COMPLEX_POLAR_NUM_POINTS] = { + 0, 1520020, 3040040, 4560061, 6080081, 7600101, + 9120121, 10640142, 12160162, 13680182, 15200203, 16720223, + 18240243, 19760264, 21280284, 22800304, 24320324, 25840345, + 27360365, 28880385, 30400406, 31920426, 33440446, 34960466, + 36480486, 38000507, 39520527, 41040547, 42560567, 44080588, + 45600608, 47120628, 48640648, 50160669, 51680689, 53200710, + 54720730, 56240750, 57760770, 59280791, 60800811, 62320831, + 63840852, 65360871, 66880892, 68400912, 69920932, 71440953, + 72960973, 74480993, 76001014, 77521034, 79041054, 80561074, + 82081094, 83601115, 85121135, 86641156, 88161176, 89681196, + 91201216, 92721237, 94241257, 95761277, 97281297, 98801318, + 100321338, 101841358, 103361378, 104881399, 106401419, 107921439, + 109441459, 110961479, 112481500, 114001520, 115521541, 117041561, + 118561581, 120081601, 121601622, 123121642, 124641662, 126161682, + 127681703, 129201723, 130721743, 132241764, 133761784, 135281804, + 136801824, 138321845, 139841865, 141361885, 142881905, 144401926, + 145921946, 147441966, 148961987, 150482007, 152002027, 153522047, + 155042068, 156562088, 158082108, 159602128, 161122148, 162642169, + 164162189, 165682210, 167202229, 168722250, 170242270, 171762290, + 173282311, 174802331, 176322351, 177842371, 179362392, 180882412, + 182402432, 183922453, 185442473, 186962493, 188482514, 190002534, + 191522554, 193042574, 194562595, 196082615, 197602635, 199122655, + 200642676, 202162696, 203682716, 205202736, 206722757, 208242777, + 209762797, 211282818, 212802838, 214322858, 215842878, 217362899, + 218882919, 220402939, 221922959, 223442980, 224963000, 226483020, + 228003040, 229523061, 231043081, 232563101, 234083122, 235603142, + 237123162, 238643183, 240163203, 241683223, 243203243, 244723263, + 246243284, 247763304, 249283325, 250803344, 252323365, 253843385, + 255363405, 256883426, 258403446, 259923466, 261443486, 262963507, + 264483527, 266003547, 267523568, 269043588, 270563608, 272083628, + 273603648, 275123669, 276643689, 278163709, 279683730, 281203750, + 282723771, 284243790, 285763811, 287283831, 288803851, 290323872, + 291843892, 293363912, 294883932, 296403953, 297923973, 299443993, + 300964014, 302484034, 304004054, 305524074, 307044095, 308564115, + 310084135, 311604155, 313124176, 314644196, 316164216, 317684237, + 319204257, 320724277, 322244297, 323764318, 325284338, 326804358, + 328324378, 329844399, 331364419, 332884439, 334404460, 335924480, + 337444500, 338964520, 340484540, 342004561, 343524581, 345044601, + 346564622, 348084642, 349604662, 351124682, 352644703, 354164723, + 355684743, 357204764, 358724784, 360244804, 361764825, 363284845, + 364804865, 366324885, 367844906, 369364925, 370884946, 372404966, + 373924987, 375445007, 376965027, 378485047, 380005068, 381525088, + 383045108, 384565128, 386085149, 387605169, 389125189, 390645210, + 392165230, 393685250, 395205270, 396725290, 398245311, 399765331, + 401285351, 402805372, 404325392, 405845412, 407365433, 408885452, + 410405473, 411925493, 413445514, 414965534, 416485554, 418005574, + 419525595, 421045615, 422565635, 424085655, 425605676, 427125696, + 428645716, 430165737, 431685757, 433205777, 434725797, 436245818, + 437765838, 439285858, 440805879, 442325899, 443845919, 445365939, + 446885960, 448405980, 449926000, 451446020, 452966041, 454486061, + 456006081, 457526101, 459046122, 460566142, 462086162, 463606182, + 465126203, 466646223, 468166243, 469686264, 471206284, 472726304, + 474246324, 475766344, 477286365, 478806385, 480326406, 481846426, + 483366446, 484886466, 486406487, 487926507, 489446527, 490966547, + 492486568, 494006588, 495526608, 497046628, 498566649, 500086669, + 501606689, 503126709, 504646730, 506166750, 507686770, 509206791, + 510726811, 512246831, 513766851, 515286872, 516806892, 518326912, + 519846932, 521366953, 522886973, 524406993, 525927014, 527447034, + 528967054, 530487074, 532007095, 533527115, 535047135, 536567156, + 538087176, 539607196, 541127216, 542647237, 544167257, 545687277, + 547207298, 548727318, 550247338, 551767358, 553287378, 554807399, + 556327419, 557847439, 559367459, 560887480, 562407500, 563927520, + 565447540, 566967561, 568487581, 570007601, 571527622, 573047642, + 574567662, 576087683, 577607703, 579127723, 580647743, 582167764, + 583687784, 585207804, 586727824, 588247845, 589767865, 591287885, + 592807905, 594327926, 595847946, 597367966, 598887986, 600408007, + 601928027, 603448047, 604968068, 606488088, 608008108, 609528128, + 611048149, 612568169, 614088189, 615608209, 617128230, 618648250, + 620168270, 621688291, 623208311, 624728331, 626248351, 627768372, + 629288392, 630808412, 632328432, 633848453, 635368473, 636888493, + 638408513, 639928534, 641448554, 642968574, 644488595, 646008615, + 647528635, 649048656, 650568676, 652088696, 653608716, 655128736, + 656648757, 658168777, 659688797, 661208817, 662728838, 664248858, + 665768878, 667288899, 668808919, 670328939, 671848959, 673368980, + 674889000, 676409020, 677929041, 679449061, 680969081, 682489101, + 684009122, 685529142, 687049162, 688569182, 690089203, 691609223, + 693129243, 694649264, 696169284, 697689304, 699209324, 700729345, + 702249365, 703769385, 705289406, 706809426, 708329446, 709849466, + 711369487, 712889507, 714409527, 715929547, 717449568, 718969588, + 720489608, 722009629, 723529649, 725049669, 726569689, 728089710, + 729609730, 731129750, 732649770, 734169790, 735689811, 737209831, + 738729851, 740249872, 741769892, 743289912, 744809933, 746329953, + 747849973, 749369993, 750890013, 752410034, 753930054, 755450075, + 756970095, 758490115, 760010135, 761530155, 763050176, 764570196, + 766090216, 767610237, 769130257, 770650277, 772170297, 773690318, + 775210338, 776730358, 778250379, 779770399, 781290419, 782810439, + 784330460, 785850480, 787370500, 788890520, 790410540, 791930561, + 793450581, 794970601, 796490622, 798010642, 799530662, 801050682, + 802570703, 804090723, 805610743, 807130764, 808650784, 810170804, + 811690824, 813210845, 814730865, 816250885, 817770905, 819290925, + 820810946, 822330966, 823850986, 825371007, 826891027, 828411047, + 829931068, 831451088, 832971108, 834491129, 836011148, 837531169, + 839051189, 840571210, 842091230, 843611250, 845131270, 846651290, + 848171311, 849691331, 851211351, 852731372, 854251392, 855771412, + 857291432, 858811453, 860331473, 861851493, 863371513, 864891534, + 866411554, 867931574, 869451595, 870971615, 872491635, 874011655, + 875531676, 877051696, 878571716, 880091737, 881611757, 883131777, + 884651797, 886171818, 887691838, 889211858, 890731879, 892251898, + 893771919, 895291939, 896811960, 898331980, 899852000, 901372020, + 902892040, 904412061, 905932081, 907452101, 908972122, 910492142, + 912012162, 913532182, 915052203, 916572223, 918092243, 919612263, + 921132284, 922652304, 924172324, 925692345, 927212365, 928732385, + 930252405, 931772426, 933292446, 934812466, 936332486, 937852507, + 939372527, 940892547, 942412567, 943932588, 945452608, 946972629, + 948492649, 950012669, 951532689, 953052709, 954572730, 956092750, + 957612770, 959132791, 960652811, 962172831, 963692851, 965212872, + 966732892, 968252912, 969772932, 971292953, 972812973, 974332993, + 975853013, 977373034, 978893054, 980413074, 981933095, 983453115, + 984973135, 986493155, 988013176, 989533196, 991053216, 992573236, + 994093257, 995613277, 997133297, 998653318, 1000173338, 1001693358, + 1003213378, 1004733399, 1006253419, 1007773439, 1009293459, 1010813480, + 1012333500, 1013853520, 1015373541, 1016893561, 1018413581, 1019933601, + 1021453621, 1022973642, 1024493662, 1026013682, 1027533703, 1029053723, + 1030573743, 1032093763, 1033613784, 1035133804, 1036653824, 1038173844, + 1039693865, 1041213885, 1042733905, 1044253925, 1045773946, 1047293966, + 1048813986, 1050334007, 1051854027, 1053374047, 1054894067, 1056414088, + 1057934108, 1059454128, 1060974149, 1062494169, 1064014189, 1065534210, + 1067054230, 1068574250, 1070094270, 1071614290, 1073134311, 1074654331, + 1076174351, 1077694372, 1079214392, 1075074159, 1073839239, 1076911811, + 1084242979, 1086814493, 1088334513, 1089854534, 1091374554, 1092894574, + 1094414594, 1095934615, 1097454635, 1098974655, 1100494676, 1102014696, + 1103534716, 1105054737, 1106574756, 1108094777, 1109614797, 1111134818, + 1112654838, 1105056616, 1090927165, 1080831389, 1075080358, 1073872201, + 1077278612, 1085239887, 1097569294, 1113965966, 1127855041, 1129375061, + 1130895081, 1132415101, 1133935122, 1135455142, 1136975162, 1138495183, + 1140015203, 1141535223, 1143055243, 1144575264, 1146095284, 1147615304, + 1147842154, 1125625312, 1106767840, 1091782821, 1081108266, 1075080603, + 1073912076, 1077675529, 1086299119, 1099571869, 1117159003, 1138624405, + 1163456762, 1168895588, 1170415608, 1171935628, 1173455649, 1174975669, + 1176495689, 1178015709, 1179535730, 1181055750, 1182575770, 1184095790, + 1178528792, 1152019016, 1128472330, 1108493464, 1092636841, 1081376258, + 1075074891, 1073959660, 1078104106, 1087422807, 1101678336, 1120499386, + 1143406778, 1169843210, 1199202803, 1208416115, 1209936135, 1211456155, + 1212976175, 1214496196, 1216016216, 1217536236, 1219056257, 1220576277, + 1214782434, 1184290462, 1156256689, 1131353486, 1110231186, 1093487707, + 1081634615, 1075063262, 1074015791, 1078565922, 1088613102, 1103891120, + 1123989412, 1148383036, 1176466363, 1207585929, 1241069231, 1247936642, + 1249456662, 1250976682, 1252496703, 1254016723, 1255536743, 1257056764, + 1256340945, 1222304586, 1190137018, 1160551098, 1134265678, 1111978752, + 1094333946, 1081882629, 1075045792, 1074081342, 1079062587, 1089872171, + 1106212632, 1127631320, 1153554786, 1183326749, 1216244445, 1251589821, + 1285937149, 1287457169, 1288977189, 1290497210, 1292017230, 1293537250, + 1295057270, 1265753470, 1229934215, 1196063374, 1164898247, 1137205863, + 1113733957, 1095174125, 1082119627, 1075022598, 1074157226, 1079595743, + 1091202192, 1108645262, 1131427290, 1158923532, 1190424762, 1225177282, + 1262416521, 1301392660, 1325457676, 1326977696, 1328497716, 1330017737, + 1331537757, 1314181286, 1275293091, 1237665157, 1202064555, 1169294223, + 1140171061, 1115494647, 1096006851, 1082344979, 1074993838, 1074244398, + 1080167064, 1092605358, 1111191383, 1135379441, 1164490680, 1197760673, + 1234383241, 1273546443, 1314459308, 1356369797, 1364978203, 1366498223, + 1368018243, 1367014394, 1325711098, 1284952570, 1245491401, 1208135693, + 1173735197, 1143158355, 1117258715, 1096830779, 1082558095, 1074959708, + 1074343851, 1080778257, 1094083873, 1113853348, 1139489834, 1170257537, + 1205334627, 1243860995, 1284976598, 1327849145, 1371692050, 1402978709, + 1404498730, 1406018750, 1380571240, 1337375582, 1294724866, 1253407084, + 1214272034, 1178217417, 1146164888, 1119024105, 1097644601, 1082758423, + 1074920447, 1074456615, 1081431059, 1095639949, 1116633481, 1143760460, + 1176225309, 1213146647, 1253609098, 1296703903, 1341557403, 1387348781, + 1433319440, 1442499237, 1439174558, 1394273675, 1349166748, 1304603136, + 1261406489, 1220468930, 1182737216, 1149187866, 1120788809, 1098447051, + 1082945449, 1074876329, 1074583759, 1082127233, 1097275806, 1119534083, + 1148193248, 1182395100, 1221196638, 1263625993, 1308725200, 1355579289, + 1403333621, 1451202345, 1480499743, 1454909890, 1408112903, 1361076863, + 1314580734, 1269484051, 1226721846, 1187291007, 1152224555, 1122550865, + 1099236905, 1083118701, 1074827672, 1074726392, 1082868575, 1098993670, + 1122557424, 1152790055, 1188767916, 1229484394, 1273910019, 1321037258, + 1369909999, 1419640265, 1469415592, 1518500249, +}; + +static const int32_t test_angle_values[TEST_COMPLEX_POLAR_NUM_POINTS] = { + 0, 455423907, 489190152, 522956546, 556722871, 590489235, + 624255603, 658021957, 691788319, 725554680, 759321041, 793087401, + 826853754, 860620121, 894386476, 928152832, 961919193, 995685560, + 1029451923, 1063218283, 1096984641, 1130751002, 1164517360, 1198283724, + 1232050084, 1265816445, 1299582805, 1333349167, 1367115527, 1400881883, + 1434648246, 1468414607, 1502180969, 1535947328, 1569713687, 1603480052, + 1637246411, 1671012770, -1668480294, -1634713934, -1600947572, -1567181214, + -1533414852, -1499648491, -1465882130, -1432115770, -1398349410, -1364583047, + -1330816690, -1297050328, -1263283967, -1229517604, -1195751244, -1161984884, + -1128218524, -1094452163, -1060685802, -1026919441, -993153083, -959386722, + -925620362, -891853999, -858087640, -824321279, -790554919, -756788558, + -723022197, -689255837, -655489475, -621723114, -587956754, -554190394, + -520424032, -486657673, -452891312, -419124952, -385358590, -351592230, + -317825870, -284059508, -250293147, -216526788, -182760426, -148994067, + -115227706, -81461345, -47694984, -13928624, 19837736, 53604097, + 87370458, 121136819, 154903180, 188669541, 222435900, 256202261, + 289968622, 323734982, 357501343, 391267703, 425034064, 458800425, + 492566785, 526333147, 560099507, 593865868, 627632228, 661398588, + 695164950, 728931309, 762697671, 796464031, 830230391, 863996753, + 897763113, 931529473, 965295834, 999062194, 1032828555, 1066594916, + 1100361277, 1134127638, 1167893998, 1201660359, 1235426719, 1269193079, + 1302959441, 1336725801, 1370492162, 1404258523, 1438024883, 1471791244, + 1505557604, 1539323965, 1573090326, 1606856686, 1640623046, 1674389407, + -1665103658, -1631337297, -1597570937, -1563804577, -1530038216, -1496271855, + -1462505495, -1428739134, -1394972773, -1361206413, -1327440051, -1293673691, + -1259907330, -1226140970, -1192374609, -1158608249, -1124841888, -1091075528, + -1057309167, -1023542806, -989776446, -956010085, -922243724, -888477363, + -854711003, -820944642, -787178282, -753411921, -719645561, -685879201, + -652112840, -618346479, -584580118, -550813757, -517047396, -483281036, + -449514676, -415748315, -381981955, -348215594, -314449234, -280682873, + -246916512, -213150151, -179383790, -145617431, -111851070, -78084709, + -44318349, -10551988, 23214373, 56980733, 90747094, 124513454, + 158279815, 192046176, 225812537, 259578897, 293345258, 327111619, + 360877979, 394644339, 428410700, 462177061, 495943422, 529709782, + 563476143, 597242504, 631008864, 664775225, 698541586, 732307946, + 766074307, 799840668, 833607028, 867373388, 901139749, 934906110, + 968672470, 1002438831, 1036205191, 1069971552, 1103737913, 1137504273, + 1171270634, 1205036995, 1238803355, 1272569716, 1306336077, 1340102437, + 1373868797, 1407635158, 1441401519, 1475167879, 1508934240, 1542700601, + 1576466962, 1610233322, 1643999683, 1677766044, -1661727022, -1627960661, + -1594194301, -1560427940, -1526661580, -1492895219, -1459128858, -1425362498, + -1391596137, -1357829777, -1324063416, -1290297056, -1256530695, -1222764334, + -1188997973, -1155231613, -1121465252, -1087698892, -1053932531, -1020166170, + -986399810, -952633449, -918867088, -885100728, -851334367, -817568007, + -783801646, -750035285, -716268925, -682502564, -648736204, -614969843, + -581203483, -547437121, -513670761, -479904400, -446138040, -412371679, + -378605318, -344838958, -311072598, -277306237, -243539876, -209773516, + -176007155, -142240794, -108474434, -74708073, -40941712, -7175351, + 26591009, 60357370, 94123730, 127890091, 161656451, 195422812, + 229189173, 262955533, 296721894, 330488255, 364254615, 398020976, + 431787337, 465553697, 499320058, 533086418, 566852779, 600619139, + 634385500, 668151861, 701918221, 735684582, 769450943, 803217303, + 836983664, 870750024, 904516385, 938282746, 972049107, 1005815467, + 1039581828, 1073348188, 1107114549, 1140880909, 1174647270, 1208413631, + 1242179992, 1275946352, 1309712712, 1343479073, 1377245434, 1411011795, + 1444778155, 1478544515, 1512310876, 1546077237, 1579843598, 1613609958, + 1647376319, 1681142680, -1658350386, -1624584025, -1590817665, -1557051304, + -1523284944, -1489518583, -1455752222, -1421985862, -1388219501, -1354453140, + -1320686780, -1286920419, -1253154059, -1219387698, -1185621337, -1151854977, + -1118088616, -1084322256, -1050555895, -1016789534, -983023174, -949256813, + -915490452, -881724092, -847957731, -814191371, -780425010, -746658649, + -712892289, -679125928, -645359567, -611593207, -577826846, -544060486, + -510294125, -476527764, -442761404, -408995043, -375228683, -341462322, + -307695961, -273929601, -240163240, -206396879, -172630519, -138864158, + -105097798, -71331437, -37565076, -3798716, 29967645, 63734006, + 97500366, 131266727, 165033087, 198799448, 232565809, 266332170, + 300098530, 333864891, 367631251, 401397612, 435163972, 468930333, + 502696694, 536463054, 570229415, 603995776, 637762136, 671528497, + 705294857, 739061218, 772827579, 806593940, 840360300, 874126660, + 907893021, 941659382, 975425742, 1009192103, 1042958464, 1076724824, + 1110491185, 1144257546, 1178023906, 1211790267, 1245556628, 1279322988, + 1313089349, 1346855709, 1380622070, 1414388430, 1448154791, 1481921152, + 1515687512, 1549453873, 1583220234, 1616986594, 1650752955, 1684519316, + -1654973750, -1621207390, -1587441029, -1553674668, -1519908307, -1486141947, + -1452375586, -1418609226, -1384842865, -1351076504, -1317310144, -1283543783, + -1249777423, -1216011062, -1182244701, -1148478341, -1114711980, -1080945620, + -1047179259, -1013412898, -979646538, -945880177, -912113816, -878347456, + -844581095, -810814734, -777048374, -743282013, -709515652, -675749292, + -641982931, -608216571, -574450210, -540683850, -506917489, -473151128, + -439384768, -405618407, -371852046, -338085686, -304319325, -270552965, + -236786604, -203020243, -169253882, -135487522, -101721161, -67954801, + -34188440, -422080, 33344281, 67110642, 100877002, 134643363, + 168409723, 202176084, 235942445, 269708806, 303475166, 337241527, + 371007887, 404774248, 438540609, 472306969, 506073330, 539839690, + 573606051, 607372412, 641138772, 674905133, 708671493, 742437854, + 776204215, 809970575, 843736936, 877503297, 911269657, 945036018, + 978802379, 1012568739, 1046335100, 1080101460, 1113867821, 1147634182, + 1181400542, 1215166903, 1248933264, 1282699624, 1316465985, 1350232345, + 1383998706, 1417765067, 1451531427, 1485297788, 1519064148, 1552830509, + 1586596870, 1620363230, 1654129591, -1685363475, -1651597114, -1617830753, + -1584064393, -1550298032, -1516531672, -1482765311, -1448998950, -1415232589, + -1381466229, -1347699868, -1313933508, -1280167147, -1246400786, -1212634426, + -1178868065, -1145101704, -1111335344, -1077568983, -1043802623, -1010036262, + -976269901, -942503541, -908737180, -874970820, -841204459, -807438098, + -773671738, -739905377, -706139016, -672372656, -638606295, -604839935, + -571073574, -537307213, -503540853, -469774492, -436008132, -402241771, + -368475410, -334709050, -300942689, -267176328, -233409968, -199643607, + -165877247, -132110886, -98344525, -64578165, -30811804, 2954557, + 36720917, 70487278, 104253638, 138019999, 171786360, 205552720, + 239319081, 273085441, 306851802, 340618163, 374384523, 408150884, + 441917245, 475683605, 509449966, 543216326, 576982687, 610749048, + 644515408, 678281769, 712048130, 745814490, 779580851, 813347211, + 847113572, 880879933, 914646293, 948412654, 982179014, 1015945375, + 1049711736, 1083478096, 1117244457, 1151010818, 1184777178, 1218543539, + 1252309900, 1286076260, 1319842621, 1353608981, 1387375342, 1421141703, + 1454908063, 1488674424, 1522440785, 1556207145, 1589973506, 1623739866, + 1657506227, -1681986838, -1648220478, -1614454117, -1580687756, -1546921396, + -1513155035, -1479388675, -1445622314, -1411855954, -1378089593, -1344323232, + -1310556872, -1276790511, -1243024150, -1209257790, -1175491429, -1141725069, + -1107958708, -1074192347, -1040425987, -1006659626, -972893265, -939126905, + -905360544, -871594184, -837827823, -804061462, -770295102, -736528741, + -702762380, -668996020, -635229659, -601463299, -567696938, -533930577, + -500164217, -466397856, -432631495, -398865135, -365098774, -331332414, + -297566053, -263799692, -230033332, -196266971, -162500611, -128734250, + -94967889, -61201529, -27435168, 6331193, 40097553, 73863914, + 107630275, 141396635, 175162996, 208929356, 242695717, 276462078, + 310228438, 343994799, 377761160, 411527520, 445293881, 479060241, + 512826602, 546592963, 580359323, 614125684, 647892044, 681658405, + 715424766, 749191126, 782957487, 816583731, 850546442, 884517983, + 918095853, 951789290, 985555651, 1019322011, 1053088372, 1086854732, + 1120621093, 1154387454, 1188153814, 1221920175, 1255686536, 1289452896, + 1323219257, 1356985618, 1390751978, 1424518339, 1458284699, 1492051060, + 1525817421, 1558515203, 1591210057, 1625104467, 1659836538, -1678263796, + -1643114188, -1608409583, -1574557824, -1541916605, -1509778399, -1476012039, + -1442245678, -1408479317, -1374712957, -1340946596, -1307180236, -1273413875, + -1239647514, -1205881154, -1172114793, -1138348432, -1104582072, -1070815711, + -1037277627, -1006953160, -974798160, -941049125, -906023309, -870110480, + -833754977, -797429519, -761604435, -726717287, -693147741, -661201051, + -631101277, -598086662, -564320302, -530553941, -496787581, -463021220, + -429254859, -395488499, -361722138, -327955777, -294189417, -260423056, + -228107767, -199050376, -167890154, -134786733, -99988130, -63832335, + -26738468, 10813442, 48312328, 85251237, 121161768, 155642038, + 188374348, 219131472, 247773022, 279838714, 313605074, 347371435, + 381137795, 414904156, 448670517, 482436877, 516203238, 549969599, + 582037330, 609501076, 639262763, 671246375, 705289776, 741133442, + 778417896, 816693154, 855441427, 894111101, 932156768, 969078115, + 1004450907, 1037945710, 1069333432, 1098479713, 1125331797, 1157764090, + 1191530450, 1225296811, 1259063172, 1292829532, 1326595893, 1360362254, + 1393549421, 1419174932, 1447210699, 1477661220, 1510456009, 1545431067, + 1582315409, 1620727322, 1660184395, -1673130488, -1633292893, -1594147962, + -1556228930, -1519982405, -1485748705, -1453757368, -1424135103, -1396921696, + -1371336321, -1337569960, -1303803600, -1270037239, -1236270878, -1202504518, + -1168738157, -1142902306, -1116829630, -1088239207, -1057115054, -1023521125, + -987622598, -949701839, -910163545, -869524163, -828383495, -787381090, + -747144824, -708241805, -671140908, -636192336, -603624541, -573554891, + -546008662, -520941146, -493410945, -459644584, -425878223, -392111863, + -358345502, -329925268, -305960772, -279458857, -250330735, -218547869, + -184166647, -147352692, -108399636, -67735914, -25913657, 16422928, + 58586777, 99905998, 139778742, 177714100, 213353127, 246470106, + 276958755, 304809839, 330086146, 352899001, 384514432, 418280792, + 452047153, 485034379, 506829690, 531112614, 558026402, 587675688, + 620102643, 655259517, 692981020, 732962399, 774750734, 817756500, + 861288724, 904610383, 947003720, 987831357, 1026580736, 1062885451, + 1096524280, 1127403811, 1155532370, 1180991934, 1203912500, 1228673447, + 1262439808, 1296206168, 1321710457, 1343731052, 1368315799, 1395624655, + 1425779004, 1458835741, 1494756849, 1533378191, 1574384078, 1617296344, + 1661486321, -1667045600, -1622570045, -1579111903, -1537344766, -1497802318, + -1460856986, -1426723649, -1395481024, -1367101734, -1341483576, -1318477274, + -1297908655, -1266660603, -1234499623, -1214714269, -1192483396, -1167612757, + -1139925070, -1109281313, -1075608776, -1038934618, -999420963, -957394220, + -913358561, -867983604, -822061263, -776436023, -731922866, -689232184, + -648918104, -611357492, -576756938, -545178627, -516574676, -490821684, + -467750585, -447169895, -422501587, -397647381, -377724508, -355297630, + -330156367, -302105515, -270987551, -236712861, -199296591, -158897953, + -115853835, -70695178, -24134408, 22982226, 69752633, 115310525, + 158908241, 199972059, 238122394, 273162971, 305049991, 333853246, + 359718224, 382834217, 403410102, 421657428, +}; diff --git a/test/ztest/unit/math/basic/complex/testcase.yaml b/test/ztest/unit/math/basic/complex/testcase.yaml new file mode 100644 index 000000000000..729fbd762b58 --- /dev/null +++ b/test/ztest/unit/math/basic/complex/testcase.yaml @@ -0,0 +1,21 @@ +# SPDX-License-Identifier: BSD-3-Clause +# +# Copyright(c) 2026 Intel Corporation. +# +# Math basic complex numbers unit tests +# + +common: + tags: + - SOF + - unit_test + - math + - complex + integration_platforms: + - native_sim + arch_exclude: xtensa # Test is for host builds only + +tests: + sof.unit.math.basic.complex: + platform_allow: + - native_sim diff --git a/test/ztest/unit/math/basic/trigonometry/CMakeLists.txt b/test/ztest/unit/math/basic/trigonometry/CMakeLists.txt new file mode 100644 index 000000000000..48d009abbee8 --- /dev/null +++ b/test/ztest/unit/math/basic/trigonometry/CMakeLists.txt @@ -0,0 +1,28 @@ +cmake_minimum_required(VERSION 3.20.0) + +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(test_math_trigonometry) + +set(SOF_ROOT "${PROJECT_SOURCE_DIR}/../../../../../..") + +target_include_directories(app PRIVATE + ${SOF_ROOT}/zephyr/include + ${SOF_ROOT}/src/include +) + +# Define SOF-specific configurations for unit testing +target_compile_definitions(app PRIVATE + -DCONFIG_ZEPHYR_POSIX=1 + -DCONFIG_LIBRARY=1 + -DUNIT_TEST=1 +) + +target_sources(app PRIVATE + trig_test.c + ${SOF_ROOT}/src/math/trig.c + ${SOF_ROOT}/src/math/lut_trig.c + ${SOF_ROOT}/src/math/atan2.c +) + +# Link math library for standard math functions +target_link_libraries(app PRIVATE m) diff --git a/test/ztest/unit/math/basic/trigonometry/atan2_tables.h b/test/ztest/unit/math/basic/trigonometry/atan2_tables.h new file mode 100644 index 000000000000..f7ec1e6587fa --- /dev/null +++ b/test/ztest/unit/math/basic/trigonometry/atan2_tables.h @@ -0,0 +1,151 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * + * Copyright(c) 2026 Intel Corporation. + */ + +#ifndef __ATAN2_TABLES_H__ +#define __ATAN2_TABLES_H__ + +#include <stdint.h> + +#define ATAN2_TEST_TABLE_SIZE 256 + +static const int32_t atan2_test_y[ATAN2_TEST_TABLE_SIZE] = { + 0, 593678131, -532114458, 826669466, 85578803, 2092045542, + -1879009088, -1576694240, 1539058893, 1401024205, -1764925184, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, -1002953741, 562543872, 1322188032, + -1091606067, -400921101, 2147483647, -1690120538, 2050372122, -350646912, + -1880522864, 320130202, 1583472563, 1870898586, 1777558605, 1275437645, + 2147483647, 645138560, 1016824371, 1133248230, -1409419475, -65799642, + 751321472, -554752, -481011942, -1824835690, 1120701542, 1219868109, + 1276479565, -1402484230, 1094266906, 345378458, 1388947226, 1235927757, + -67347315, -113975488, 352132070, 646445466, -175333594, 1313648794, + 498544640, 689375667, 1101318170, -1891416384, 973726336, 260722458, + 2049027200, 1619159731, 659320781, 811575142, 2062582016, 1804117709, + -556148173, 2147483647, -1581181395, -1384873248, -2147483648, 1919463322, + -715849728, 1482341555, -2106111198, 2147483647, -1581934605, -2147483648, + 2147483647, -1977711542, -1219287520, -1913594461, -1213455936, 1750786893, + -420132698, -222367974, -435172250, -2087889971, -919525235, 834603827, + -1669661664, 2147483647, 1584016051, 2082185882, 212862848, 1926648627, + -1889370525, -1133371149, 2147483647, -125891533, 629086234, -580819290, + -814994048, -940839821, -2147483648, -11732723, -499195981, 2003249459, + -1797551043, 1543991962, 703381043, -200345024, 1618129075, 1901467674, + 2147483647, 747880038, 2017725389, 1628990106, -2147483648, 2147483647, + -451489280, 486361498, -2132152282, -58571955, 157509299, -834756314, + 1251017293, 1892762010, -1455901638, 90604237, -957637261, -2147483648, + 752198656, -1829826099, -1725792499, -569240883, 42635648, -1498449850, + -437911232, -23190746, -661963034, -324850381, -2134133197, 297851418, + 980486426, -357129907, 1688064717, -819664806, 1316280909, 13885414, + -1496825299, 18689510, 1180361037, -324324774, 370182912, 2146612582, + -652058458, -259263629, 277880346, -1507396774, -1420640883, 1603635123, + 1512286899, 925569638, 64723789, 2101077862, 2046962227, -1243819526, + 1021511040, -1659139469, 1906461286, 1168691814, 1900425472, 195734528, + -689034931, 510480538, -1956886624, 965214694, -1640061773, -2146964829, + -1588224, -1125154624, 34522470, 1535756570, 692508749, 292941158, + -143428173, 715886899, 1614379853, 33759053, -1082890266, 610783923, + 1470741325, 1262575846, -53151718, 2033863603, 844302131, 19598797, + 654380390, -411747354, -1558739706, -1846029434, 1652960461, -2147483648, + -587736512, -595937408, 1554227840, 562723533, 1592959667, -46749261, + 1489178803, 582155622, -2126878934, -1168122701, -2147483648, 1081324570, + 1864094003, 1138184397, -547145984, 772395853, -41048269, 1008931123, + -844305651, 1003180570, 2147483647, 2147483647, -715760602, -2035354781, + -1371158131, 1678904832, 494394701, -1407855398, 440926182, 2082209818, + -1291104390, 1887003290, 744169114, -1480452019, 2087889126, 23236224, + -1745024160, -72790784, 523330227, -115015155, 1655730278, 923114086, + 880698086, 1312830746, 132706534, -978800064, +}; + +static const int32_t atan2_test_x[ATAN2_TEST_TABLE_SIZE] = { + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, -1399816141, + -230097050, -1261376090, -2081157232, -440471258, -876176998, -1642130829, + -171532698, 104774067, -928553894, 803256115, 1159264128, -451441549, + 1477543373, -1407978810, -155422643, -1755391194, 1797471949, -2131587568, + -251177766, -2147483648, -868828787, 1321866445, -550897318, -288342067, + 158300877, 370411853, 1242460877, 560164634, 560658278, 1170291584, + -1531339462, 1861627750, 881152307, -76744870, 1266329293, -808628198, + 779498650, -1042297485, 29521254, -2147483648, -1831276832, 315334554, + 1677842637, -314975091, 1873312179, 1370099174, -853027507, 1308034816, + 1094235366, 2147483647, 268095872, 2147483647, -834663949, 838375578, + -574429504, -707173350, 1876573952, 874547379, 1613107021, -2076715221, + 1954203187, -1230503296, 1975324314, 1574426445, 150468173, -2147483648, + -2045228592, 102846515, -1659941536, 2147483647, 523171686, -919468352, + 1801804365, 25905792, -2147483648, 2100982400, -2147483648, -592549478, + 774778470, -794157056, -687915571, 1915112320, -1895780832, -651402752, + -1753523904, -2127834438, -1292520768, -72287987, 471997645, -1600389824, + 1050780826, 909749350, 1526121907, -480663181, 1690915917, 152633958, + 1151843968, -1504568947, -2135539402, 799874662, 1630831770, -643009382, + -1334604269, -2147483648, -1856544518, 1010088781, 1164046259, 914998093, + 2147483647, 560258406, 1061672550, 1741415526, 730181760, -1559917498, + 1348011034, 280000230, 247261696, 1805741133, -998184000, 2003341542, + 182103398, -343834726, -665013325, 681587456, -965692570, 773658266, + -854945766, -1014991155, -663706278, 200810086, -2147483648, 1065857690, + -1146915827, -2147483648, 1094864461, 1700262502, -1050449382, -713241830, + -1989792816, -1679438253, -2144952374, 1737513395, -76085786, -999872192, + 1268036915, 1857466829, 1334412570, 351298253, 1767924506, -206440256, + -2147483648, -1185212653, 2030329805, 2147483647, 1363327539, 848855885, + 1660833434, 1357533338, -1650400646, 991000525, 2106705357, 2147483647, + -1964598170, 1936573338, 799362995, 406677427, -395381466, 2147483647, + -1469184218, 204290944, 1538024576, 2093231642, 2147483647, -1031910246, + -2147483648, 2147483647, -172979558, 1774660941, 795639117, 1787846861, + 1848797773, 282311603, -379224384, -889636070, -1874511760, -1696152198, + -96912922, 1257014528, 1355642675, -28044262, -1442693542, -2147483648, + 586790195, 170231987, -474024038, 1662995277, -139357645, -447027750, + -774900826, 2147483647, 1843413581, 1234652954, -991492621, -2147483648, + -272780570, -1956468659, 1327163059, 1548914611, 1038220902, 2147483647, + -1708905722, 550796928, -1898930634, 1387736064, -1010688026, -1955962237, + 2147483647, 1687800013, -538678554, -2051015613, 1152012083, -1856525088, + -784337101, 361366016, 1829781043, -926315456, 584568653, -421916493, + 1094186931, 1752872986, -1590665542, -714551270, 1503289216, -2001796262, + -554735245, -1757686726, -904190426, -2083870624, -561217114, -885333222, + -314317274, 1836398643, -1568309389, 2094263142, +}; + +static const int32_t atan2_test_ref[ATAN2_TEST_TABLE_SIZE] = { + 0, 843314857, -843314857, 843314857, 843314857, 843314857, + -843314857, -843314857, 843314857, 843314857, -843314857, 1686629713, + 1686629713, 1686629713, 1686629713, 1686629713, 1686629713, 1686629713, + 1686629713, 0, 1686629713, -480774686, 242550644, 1019958505, + -341607303, -1537697774, 882102887, -1275141377, 456892678, -1599098184, + -914601725, 1607182223, 1112734724, 513084281, 1004661107, 962680342, + 803811086, 563496816, 368216934, 596849638, -640053917, -30153818, + 1441750136, -159984, -268264737, -865880079, 388944604, 1157587081, + 548989894, -1186440684, 828834606, 1601018220, 1338258195, 709198926, + -21538024, -1500229594, 99753206, 236681525, -1577795578, 422807062, + 229517388, 166764936, 715116877, -387665176, 1223768840, 161868893, + 990055806, 1064389455, 181392893, 401616028, 486983396, 1302621340, + -148853162, 1122663950, -362401539, -387315937, -805759192, 1295041388, + -1505874184, 806125749, -1201663508, 421657428, -671841804, -1060503213, + 468530021, -836282855, -1409396260, -396616142, -1410500034, 1018520658, + -266761868, -1540056775, -1383811666, -444815486, -1444175423, 1199116908, + -1278122081, 1262504863, 1210753289, 861946112, 227456622, 1215451470, + -570823003, -480185572, 511613552, -1549105769, 191218374, -705349581, + -330593881, -1386609757, -1263475094, -7874366, -159473626, 1010064033, + -1186190715, 1351971671, 1492198979, -105120786, 508515064, 602528322, + 421657428, 498137329, 583272798, 403755854, -667353222, 1180588320, + -173509607, 562873277, -781331817, -17408120, 1602606516, -211958872, + 765710697, 939789547, -1073348055, 70950927, -1267220800, -657671271, + 1299248585, -1115207171, -1040423529, -661241101, 1675972201, -511381404, + -1490815052, -1680832252, -291950578, -101352536, -1088879133, 1474249986, + 1440830194, -1574140544, 1328665659, -236647292, 874313438, 1679174565, + -465981743, 5401720, 388810509, -400234846, 110813576, 894787567, + -1528364782, -1571011137, 73024931, -328578059, -432708387, 581946141, + 396542676, 321267954, 1665586022, 606704001, 413936026, -281848594, + 1429206964, -380316068, 630163376, 663532294, 953439316, 48798797, + -1451193270, 638942546, -485695493, 231958476, -350157942, -1083853093, + -1686232657, -259102718, 1580872968, 382979880, 384511044, 87192252, + -41566736, 641651025, 967182272, 1666266823, -1405387221, 1501063583, + 878640321, 422842424, -21038732, 850717120, 1402364287, 1681730150, + 450864858, -632841657, -1001810229, -449635784, 888470538, -953498287, + -1338255336, -145327507, 376072239, 229590563, 1142214026, -1674944244, + 940578011, 1531360468, -543807078, -346902051, -601547020, 250433321, + 1241668689, 601343996, -1536018589, 272669402, -1664837116, 1430957794, + -201109884, 287908177, 975262018, 1252638885, -298470119, -1240320708, + -1122270737, 729495693, 141675968, -1155748018, 346943281, 950647349, + -465878468, 441432301, 1451702422, -1084733681, 508293499, 1680398164, + -1008559815, -1664409066, 1404948145, -1657028209, 1018764798, 1253757970, + 1027356695, 333210160, 1641308961, -234723315, +}; + +#endif /* __ATAN2_TABLES_H__ */ diff --git a/test/ztest/unit/math/basic/trigonometry/atan2_tables.m b/test/ztest/unit/math/basic/trigonometry/atan2_tables.m new file mode 100644 index 000000000000..37728a20e0d7 --- /dev/null +++ b/test/ztest/unit/math/basic/trigonometry/atan2_tables.m @@ -0,0 +1,95 @@ +% SPDX-License-Identifier: BSD-3-Clause +% +% Copyright(c) 2026 Intel Corporation. + +function atan2_tables() + + % Create random test points with bias to min/max values + q31_scale = 2^31; + q29_scale = 2^29; + num_points = 256; + rand('seed', 42); + x = max(min(2.2 * rand(num_points, 1) - 1.1, 1), -1); + y = max(min(2.2 * rand(num_points, 1) - 1.1, 1), -1); + + % Force 1st test point to be (0, 0) to ensure it is tested + x(1) = 0; + y(1) = 0; + + % Then force 10 test points to have x = 0, the reference values are 0 or pi + x(2:11) = 0; + + % And force 10 test points to have y = 0, the reference values are +/-pi + y(12:21) = 0; + + % Quantize input values + ref_x = int32(x * q31_scale); + ref_y = int32(y * q31_scale); + x = double(ref_x)/q31_scale; + y = double(ref_y)/q31_scale; + a = atan2(y, x); + ref_a = int32(a * q29_scale); + + figure(1) + subplot(2,1,1) + plot(x, y, 'o'); + grid on; + subplot(2,1,2) + plot(a, 'o'); + grid on; + + fn = 'atan2_tables.h'; + fh = export_headerfile(fn); + dn = 'ATAN2_TEST_TABLE_SIZE'; + vl = 6; + fu = export_ifndef(fh, fn); + export_define(fh, dn, num_points); + export_array(fh, 'atan2_test_y', dn, vl, ref_y); + export_array(fh, 'atan2_test_x', dn, vl, ref_x); + export_array(fh, 'atan2_test_ref', dn, vl, ref_a); + export_fclose(fh, fu); + +end + +function [fh] = export_headerfile(headerfn) + fh = fopen(headerfn, 'w'); + fprintf(fh, '/* SPDX-License-Identifier: BSD-3-Clause\n'); + fprintf(fh, ' *\n'); + fprintf(fh, ' * Copyright(c) %s Intel Corporation.\n', ... + datestr(now, 'yyyy')); + fprintf(fh, ' */\n\n'); +end + +function fu = export_ifndef(fh, fn) + fu = sprintf('__%s__', upper(strrep(fn, '.', '_'))); + fprintf(fh, '#ifndef %s\n', fu); + fprintf(fh, '#define %s\n\n', fu); + fprintf(fh, '#include <stdint.h>\n\n'); +end + +function export_define(fh, dn, val) + fprintf(fh, '#define %s %d\n', dn, val); +end + +function export_array(fh, vn, size_str, vl, data) + fprintf(fh, '\n'); + fprintf(fh, 'static const int32_t %s[%s] = {\n', vn, size_str); + + n = length(data); + k = 0; + for i = 1:vl:n + fprintf(fh, '\t'); + for j = 1:min(vl, n-k) + k = k + 1; + fprintf(fh, '%12d,', data(k)); + end + fprintf(fh, '\n'); + end + + fprintf(fh, '};\n'); +end + +function export_fclose(fh, fu) + fprintf(fh, "\n#endif /* %s */\n", fu); + fclose(fh); +end diff --git a/test/ztest/unit/math/basic/trigonometry/prj.conf b/test/ztest/unit/math/basic/trigonometry/prj.conf new file mode 100644 index 000000000000..d34c7781cd0a --- /dev/null +++ b/test/ztest/unit/math/basic/trigonometry/prj.conf @@ -0,0 +1,2 @@ +CONFIG_ZTEST=y +CONFIG_SOF_FULL_ZEPHYR_APPLICATION=n diff --git a/test/ztest/unit/math/basic/trigonometry/testcase.yaml b/test/ztest/unit/math/basic/trigonometry/testcase.yaml new file mode 100644 index 000000000000..90bb941c4a22 --- /dev/null +++ b/test/ztest/unit/math/basic/trigonometry/testcase.yaml @@ -0,0 +1,24 @@ +# SPDX-License-Identifier: BSD-3-Clause +# +# Copyright(c) 2025 Intel Corporation. All rights reserved. +# +# Math basic trigonometry unit tests converted from CMock to Ztest +# +# These contents may have been developed with support from one or more Intel-operated +# generative artificial intelligence solutions. +# + +common: + tags: + - SOF + - unit_test + - math + - trigonometry + integration_platforms: + - native_sim + arch_exclude: xtensa # Test is for host builds only + +tests: + sof.unit.math.basic.trigonometry: + platform_allow: + - native_sim diff --git a/test/cmocka/include/trig_tables.h b/test/ztest/unit/math/basic/trigonometry/trig_tables.h similarity index 100% rename from test/cmocka/include/trig_tables.h rename to test/ztest/unit/math/basic/trigonometry/trig_tables.h diff --git a/test/ztest/unit/math/basic/trigonometry/trig_test.c b/test/ztest/unit/math/basic/trigonometry/trig_test.c new file mode 100644 index 000000000000..0e9471712891 --- /dev/null +++ b/test/ztest/unit/math/basic/trigonometry/trig_test.c @@ -0,0 +1,269 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// Copyright(c) 2025 Intel Corporation. All rights reserved. +// +// These contents may have been developed with support from one or more Intel-operated +// generative artificial intelligence solutions. +// +// Converted from CMock to Ztest +// +// Original tests from sof/test/cmocka/src/math/trig/: +// - sin_32b_fixed.c (Author: Slawomir Blauciak <slawomir.blauciak@linux.intel.com>) +// - sin_16b_fixed.c (Author: Shriram Shastry <malladi.sastry@linux.intel.com>) +// - cos_32b_fixed.c (Author: Shriram Shastry <malladi.sastry@linux.intel.com>) +// - cos_16b_fixed.c (Author: Shriram Shastry <malladi.sastry@linux.intel.com>) +// - asin_32b_fixed.c (Author: Shriram Shastry <malladi.sastry@linux.intel.com>) +// - asin_16b_fixed.c (Author: Shriram Shastry <malladi.sastry@linux.intel.com>) +// - acos_32b_fixed.c (Author: Shriram Shastry <malladi.sastry@linux.intel.com>) +// - acos_16b_fixed.c (Author: Shriram Shastry <malladi.sastry@linux.intel.com>) +// - lut_sin_16b_fixed.c (Authors: Slawomir Blauciak <slawomir.blauciak@linux.intel.com>, +// Seppo Ingalsuo <seppo.ingalsuo@linux.intel.com>) + +#define _USE_MATH_DEFINES +#include <math.h> +#include <stddef.h> +#include <stdint.h> +#include <stdio.h> +#include <zephyr/ztest.h> +#include <sof/audio/format.h> +#include <sof/audio/format_generic.h> +#include <sof/math/trig.h> +#include <sof/math/cordic.h> +#include <sof/math/lut_trig.h> +#include "trig_tables.h" +#include "atan2_tables.h" + +/* Define M_PI if not available */ +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +/* Conversion factor from degrees to radians (PI/180) */ +#define DEGREES_TO_RADIANS 0.017453292519943295 + +#define CMP_TOLERANCE_32B 0.0000000611175871f +#define CMP_TOLERANCE_16B 0.000065f +#define CMP_TOLERANCE_ASIN_32B 0.000000068141916f +#define CMP_TOLERANCE_ACOS_32B 0.000000060077032f +#define CMP_TOLERANCE_ASIN_16B 0.0001152158f +#define CMP_TOLERANCE_ACOS_16B 0.0001196862f +#define CMP_TOLERANCE_SIN 3.1e-5f +#define CMP_TOLERANCE_ATAN2 2.0e-5 /* ~0.001 degrees in radians */ + +/* + * Helper function for rounding double values to nearest integer + * Implements custom rounding: round to 0 if absolute value < 0.5, otherwise round normally + */ +static inline int32_t round_to_nearest_int(double value) +{ + double abs_value = fabs(value); + + return (int32_t) (abs_value >= 0.5) ? floor(value + 0.5) : 0.0; +} + +/* Test sin_32b_fixed function */ +ZTEST(trigonometry, test_sin_32b_fixed) +{ + int theta; + float delta; + double rad; + int32_t sin_val; + + for (theta = 0; theta < ARRAY_SIZE(sin_ref_table); ++theta) { + rad = M_PI * ((double)theta / 180.0); + sin_val = sin_fixed_32b(Q_CONVERT_FLOAT(rad, 28)); + delta = fabsf(sin_ref_table[theta] - Q_CONVERT_QTOF(sin_val, 31)); + zassert_true(delta <= CMP_TOLERANCE_32B, + "sin_32b_fixed failed for angle %d", theta); + } +} + +/* Test sin_16b_fixed function */ +ZTEST(trigonometry, test_sin_16b_fixed) +{ + int theta; + float delta; + double rad; + int16_t sin_val; + + for (theta = 0; theta < ARRAY_SIZE(sin_ref_table); ++theta) { + rad = M_PI * ((double)theta / 180.0); + sin_val = sin_fixed_16b(Q_CONVERT_FLOAT(rad, 28)); + delta = fabsf(sin_ref_table[theta] - Q_CONVERT_QTOF(sin_val, 15)); + zassert_true(delta <= CMP_TOLERANCE_16B, + "sin_16b_fixed failed for angle %d", theta); + } +} + +/* Test cos_32b_fixed function */ +ZTEST(trigonometry, test_cos_32b_fixed) +{ + int theta; + float delta; + double rad; + int32_t cos_val; + + for (theta = 0; theta < ARRAY_SIZE(cos_ref_table); ++theta) { + rad = M_PI * ((double)theta / 180.0); + cos_val = cos_fixed_32b(Q_CONVERT_FLOAT(rad, 28)); + delta = fabsf(cos_ref_table[theta] - Q_CONVERT_QTOF(cos_val, 31)); + zassert_true(delta <= CMP_TOLERANCE_32B, + "cos_32b_fixed failed for angle %d", theta); + } +} + +/* Test cos_16b_fixed function */ +ZTEST(trigonometry, test_cos_16b_fixed) +{ + int theta; + float delta; + double rad; + int16_t cos_val; + + for (theta = 0; theta < ARRAY_SIZE(cos_ref_table); ++theta) { + rad = M_PI * ((double)theta / 180.0); + cos_val = cos_fixed_16b(Q_CONVERT_FLOAT(rad, 28)); + delta = fabsf(cos_ref_table[theta] - Q_CONVERT_QTOF(cos_val, 15)); + zassert_true(delta <= CMP_TOLERANCE_16B, + "cos_16b_fixed failed for angle %d", theta); + } +} + +/* Test asin_32b_fixed function */ +ZTEST(trigonometry, test_asin_32b_fixed) +{ + int index; + float delta; + double u; + int32_t asin_val, input_val; + + for (index = 0; index < ARRAY_SIZE(degree_table); ++index) { + /* Convert degree to input value in Q2.30 format like original test */ + /* angleInRadians = PI/180 * angleInDegrees & const Q2.30 format */ + u = (DEGREES_TO_RADIANS * degree_table[index] * 0x40000000); + input_val = round_to_nearest_int(u); + + asin_val = asin_fixed_32b(input_val); + delta = fabsf(asin_ref_table[index] - Q_CONVERT_QTOF(asin_val, 29)); + zassert_true(delta <= CMP_TOLERANCE_ASIN_32B, + "asin_32b_fixed failed for index %d", index); + } +} + +/* Test asin_16b_fixed function */ +ZTEST(trigonometry, test_asin_16b_fixed) +{ + int index; + float delta; + double u; + int16_t asin_val; + int32_t input_val; + + for (index = 0; index < ARRAY_SIZE(degree_table); ++index) { + /* Convert degree to input value in Q2.30 format like original test */ + /* angleInRadians = PI/180 * angleInDegrees & const Q2.30 format */ + u = (DEGREES_TO_RADIANS * degree_table[index] * 0x40000000); + input_val = round_to_nearest_int(u); + + asin_val = asin_fixed_16b(input_val); + delta = fabsf(asin_ref_table[index] - Q_CONVERT_QTOF(asin_val, 13)); + zassert_true(delta <= CMP_TOLERANCE_ASIN_16B, + "asin_16b_fixed failed for index %d", index); + } +} + +/* Test acos_32b_fixed function */ +ZTEST(trigonometry, test_acos_32b_fixed) +{ + int index; + float delta; + double u; + int32_t acos_val, input_val; + + for (index = 0; index < ARRAY_SIZE(degree_table); ++index) { + /* Convert degree to input value in Q2.30 format like original test */ + /* angleInRadians = PI/180 * angleInDegrees & const Q2.30 format */ + u = (DEGREES_TO_RADIANS * degree_table[index] * 0x40000000); + input_val = round_to_nearest_int(u); + + acos_val = acos_fixed_32b(input_val); + delta = fabsf(acos_ref_table[index] - Q_CONVERT_QTOF(acos_val, 29)); + zassert_true(delta <= CMP_TOLERANCE_ACOS_32B, + "acos_32b_fixed failed for index %d", index); + } +} + +/* Test acos_16b_fixed function */ +ZTEST(trigonometry, test_acos_16b_fixed) +{ + int index; + float delta; + double u; + int16_t acos_val; + int32_t input_val; + + for (index = 0; index < ARRAY_SIZE(degree_table); ++index) { + /* Convert degree to input value in Q2.30 format like original test */ + /* angleInRadians = PI/180 * angleInDegrees & const Q2.30 format */ + u = (DEGREES_TO_RADIANS * degree_table[index] * 0x40000000); + input_val = round_to_nearest_int(u); + + acos_val = acos_fixed_16b(input_val); + delta = fabsf(acos_ref_table[index] - Q_CONVERT_QTOF(acos_val, 13)); + zassert_true(delta <= CMP_TOLERANCE_ACOS_16B, + "acos_16b_fixed failed for index %d", index); + } +} + +/* Test sin lookup table function */ +ZTEST(trigonometry, test_sin_lut_16b_fixed) +{ + int theta; + float delta; + double rad; + int16_t sin_val; + + for (theta = 0; theta < ARRAY_SIZE(sin_ref_table); ++theta) { + rad = M_PI * (double)theta / 180.0; + sin_val = sofm_lut_sin_fixed_16b(Q_CONVERT_FLOAT(rad, 28)); + delta = fabsf(sin_ref_table[theta] - Q_CONVERT_QTOF(sin_val, 15)); + zassert_true(delta <= CMP_TOLERANCE_SIN, + "sin_lut_16b_fixed failed for angle %d", theta); + } +} + +/* Test sofm_atan2_32b function */ +ZTEST(trigonometry, test_atan2) +{ + double reference; + double result; + double delta; + double delta_max = 0.0; + int32_t result_q29_max = 0; + int32_t result_q29; + int i_max = 0; + int i; + + /* Note that the first atan2_test_y[], atan2_test_x[] have forced to + * zero y or x, values to test edge cases where x == 0 or y == 0. See + * script atan2_tables.m for details. + */ + for (i = 0; i < ATAN2_TEST_TABLE_SIZE; ++i) { + result_q29 = sofm_atan2_32b(atan2_test_y[i], atan2_test_x[i]); + result = Q_CONVERT_QTOD(result_q29, 29); + reference = Q_CONVERT_QTOD(atan2_test_ref[i], 29); + delta = fabs(reference - result); + if (i == 0 || delta > delta_max) { + result_q29_max = result_q29; + delta_max = delta; + i_max = i; + } + zassert_true(delta <= CMP_TOLERANCE_ATAN2, + "sofm_atan2_32b failed for input %d: (%d, %d) result %d, expected %d", + i, atan2_test_y[i], atan2_test_x[i], result_q29, atan2_test_ref[i]); + } + printf(" INFO - Maximum delta for atan2 test %d: %.6e (%d, %d) result %d\n", + i_max, delta_max, atan2_test_y[i_max], atan2_test_x[i_max], result_q29_max); +} + +ZTEST_SUITE(trigonometry, NULL, NULL, NULL, NULL, NULL); diff --git a/test/ztest/unit/objpool/CMakeLists.txt b/test/ztest/unit/objpool/CMakeLists.txt new file mode 100644 index 000000000000..2ce499607463 --- /dev/null +++ b/test/ztest/unit/objpool/CMakeLists.txt @@ -0,0 +1,31 @@ +cmake_minimum_required(VERSION 3.20.0) + +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(test_objpool) + +set(SOF_ROOT "${PROJECT_SOURCE_DIR}/../../../..") + +# Include SOF CMake functions +include(${SOF_ROOT}/scripts/cmake/misc.cmake) + +target_include_directories(app PRIVATE + ${SOF_ROOT}/zephyr/include + ${SOF_ROOT}/src/include + ${SOF_ROOT}/src/platform/posix/include +) + +# Define SOF-specific configurations for unit testing +target_compile_definitions(app PRIVATE + -DCONFIG_ZEPHYR_POSIX=1 +) + +target_sources(app PRIVATE + test_objpool_ztest.c + ${SOF_ROOT}/test/ztest/unit/common/alloc.c + ${SOF_ROOT}/src/lib/objpool.c +) + +target_link_libraries(app PRIVATE "-Wl,--wrap=rzalloc,--wrap=sof_heap_alloc,--wrap=sof_heap_free,--wrap=sof_sys_heap_get") + +# Add RELATIVE_FILE definitions for SOF trace functionality +sof_append_relative_path_definitions(app) diff --git a/test/ztest/unit/objpool/prj.conf b/test/ztest/unit/objpool/prj.conf new file mode 100644 index 000000000000..d34c7781cd0a --- /dev/null +++ b/test/ztest/unit/objpool/prj.conf @@ -0,0 +1,2 @@ +CONFIG_ZTEST=y +CONFIG_SOF_FULL_ZEPHYR_APPLICATION=n diff --git a/test/ztest/unit/objpool/test_objpool_ztest.c b/test/ztest/unit/objpool/test_objpool_ztest.c new file mode 100644 index 000000000000..596fb3accd70 --- /dev/null +++ b/test/ztest/unit/objpool/test_objpool_ztest.c @@ -0,0 +1,118 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// Copyright(c) 2025 Intel Corporation. + +#define DATA_SIZE 5 +#define ALIGNED_SIZE ALIGN_UP(DATA_SIZE, sizeof(int)) + +#include <zephyr/ztest.h> +#include <sof/objpool.h> +#include <sof/common.h> +#include <sof/list.h> + +#include <rtos/alloc.h> + +#include <stdlib.h> +#include <string.h> + +ZTEST(objpool_suite, test_objpool_wrong_size) +{ + struct objpool_head head = {.list = LIST_INIT(head.list)}; + /* new object pool of 2 blocks */ + uint8_t *block1 = objpool_alloc(&head, DATA_SIZE, 0); + /* should fail because of a different size */ + uint8_t *block2 = objpool_alloc(&head, DATA_SIZE + 1, 0); + /* second block in the first object pool */ + uint8_t *block3 = objpool_alloc(&head, DATA_SIZE, 0); + /* new object pool of 4 blocks */ + uint8_t *block4 = objpool_alloc(&head, DATA_SIZE, 0); + /* should fail because of a different size */ + uint8_t *block5 = objpool_alloc(&head, DATA_SIZE * 2, 0); + /* should fail because of different flags */ + uint8_t *block6 = objpool_alloc(&head, DATA_SIZE * 2, SOF_MEM_FLAG_COHERENT); + + zassert_not_null(block1); + zassert_is_null(block2); + zassert_not_null(block3); + zassert_not_null(block4); + zassert_is_null(block5); + zassert_is_null(block6); + + zassert_not_ok(objpool_free(&head, block1 + 1)); + zassert_ok(objpool_free(&head, block1)); + zassert_not_ok(objpool_free(&head, block3 + 1)); + zassert_ok(objpool_free(&head, block3)); + zassert_not_ok(objpool_free(&head, block4 + 1)); + zassert_ok(objpool_free(&head, block4)); +} + +ZTEST(objpool_suite, test_objpool) +{ + struct objpool_head head = {.list = LIST_INIT(head.list)}; + void *blocks[62]; /* 2 + 4 + 8 + 16 + 32 */ + unsigned int k = 0; + + /* Loop over all powers: 2^1..2^5 */ + for (unsigned int i = 1; i <= 5; i++) { + unsigned int n = 1 << i; + uint8_t *start; + + for (unsigned int j = 0; j < n; j++) { + uint8_t *block = objpool_alloc(&head, DATA_SIZE, 0); + + zassert_not_null(block, "allocation failed loop %u iter %u", i, j); + + if (!j) + start = block; + else + zassert_equal(block, start + ALIGNED_SIZE * j, "wrong pointer"); + + blocks[k++] = block; + } + } + + while (k--) + zassert_ok(objpool_free(&head, blocks[k]), "free failed"); +} + +struct test_objpool_data { + char cnt; + uint8_t reserved[DATA_SIZE - sizeof(char)]; +} __packed; + +static unsigned int test_objpool_check; + +static bool test_objpool_cb(void *data, void *arg) +{ + struct test_objpool_data *odata = data; + + zassert_equal(test_objpool_check++, odata->cnt, "Counter mismatch"); + zassert_equal((unsigned int)arg, 2, "Wrong argument"); + + return odata->cnt == (unsigned int)arg; +} + +ZTEST(objpool_suite, test_objpool_iterate) +{ + struct objpool_head head = {.list = LIST_INIT(head.list)}; + unsigned int i; + + for (i = 0; i < 4; i++) { + struct test_objpool_data *odata = objpool_alloc(&head, sizeof(*odata), 0); + + zassert_not_null(odata, "allocation failed loop %u", i); + + odata->cnt = i; + } + + int ret = objpool_iterate(&head, test_objpool_cb, (void *)2); + + zassert_equal(test_objpool_check, 3); + + zassert_ok(ret); + + /* Reset for a possible rerun */ + test_objpool_check = 0; +} + +ZTEST_SUITE(objpool_suite, NULL, NULL, NULL, NULL, NULL); diff --git a/test/ztest/unit/objpool/testcase.yaml b/test/ztest/unit/objpool/testcase.yaml new file mode 100644 index 000000000000..5558c1f50a6b --- /dev/null +++ b/test/ztest/unit/objpool/testcase.yaml @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: BSD-3-Clause +# +# Copyright(c) 2025 Intel Corporation. +# +# Object pool allocator unit tests for Ztest framework + +tests: + sof.unit.objpool: + tags: unit + platform_allow: native_sim + integration_platforms: + - native_sim + build_only: false diff --git a/third_party/include/dax_inf.h b/third_party/include/dax_inf.h new file mode 100644 index 000000000000..8e25f52fc4a7 --- /dev/null +++ b/third_party/include/dax_inf.h @@ -0,0 +1,242 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY THIS LICENSE + * + * Copyright(c) 2025 Dolby Laboratories. All rights reserved. + * + * Author: Jun Lai <jun.lai@dolby.com> + */ + +#ifndef DAX_INF_H +#define DAX_INF_H + +#include <stddef.h> +#include <stdint.h> +#include <string.h> + +enum dax_device { + DAX_DEVICE_UNSUPPORTED = -1, + DAX_DEVICE_SPEAKER = 0, + DAX_DEVICE_HEADPHONE = 1, +}; + +enum dax_frame_fmt { + DAX_FMT_UNSUPPORTED = -1, + DAX_FMT_SHORT_16 = 4, + DAX_FMT_INT = 5, + DAX_FMT_FLOAT = 7, +}; + +enum dax_sample_rate { + DAX_RATE_UNSUPPORTED = -1, +}; + +enum dax_channels { + DAX_CHANNLES_UNSUPPORTED = -1, +}; + +enum dax_buffer_fmt { + DAX_BUFFER_LAYOUT_UNSUPPORTED = -1, + DAX_BUFFER_LAYOUT_INTERLEAVED, + DAX_BUFFER_LAYOUT_NONINTERLEAVED, +}; + +enum dax_param_id { + DAX_PARAM_ID_ENABLE = 0x08001026, + DAX_PARAM_ID_TUNING_FILE = 0x08001027, + DAX_PARAM_ID_PROFILE = 0x08001028, + DAX_PARAM_ID_ENDPOINT = 0x08001029, + DAX_PARAM_ID_TUNING_DEVICE = 0x08001030, + DAX_PARAM_ID_CP_ENABLE = 0x08001031, + DAX_PARAM_ID_OUT_DEVICE = 0x08001032, + DAX_PARAM_ID_ABSOLUTE_VOLUME = 0x08001033, + DAX_PARAM_ID_CTC_ENABLE = 0x08001034, +}; + +struct dax_media_fmt { + enum dax_frame_fmt data_format; + uint32_t sampling_rate; + uint32_t num_channels; + enum dax_buffer_fmt layout; + uint32_t bytes_per_sample; +}; + +struct dax_buffer { + void *addr; + uint32_t size; /* Total buffer size in bytes */ + uint32_t avail; /* Available bytes for reading */ + uint32_t free; /* Free bytes for writing */ +}; + +struct sof_dax { + /* SOF module parameters */ + uint32_t sof_period_bytes; + + /* DAX state parameters */ + uint32_t period_bytes; + uint32_t period_us; + int32_t endpoint; + int32_t tuning_device; + void *blob_handler; + void *p_dax; + struct dax_media_fmt input_media_format; + struct dax_media_fmt output_media_format; + + /* DAX control parameters */ + int32_t enable; + int32_t profile; + int32_t out_device; + int32_t ctc_enable; + int32_t content_processing_enable; + int32_t volume; + uint32_t update_flags; /* Deprecated */ + + /* DAX buffers */ + struct dax_buffer persist_buffer; /* Used for dax instance */ + struct dax_buffer scratch_buffer; /* Used for dax process */ + struct dax_buffer input_buffer; + struct dax_buffer output_buffer; + struct dax_buffer tuning_file_buffer; +}; + +/** + * @brief Query the persistent memory requirements for the DAX module + * + * @param[in] dax_ctx Pointer to the DAX context structure + * + * @return Size of required persistent memory in bytes + */ +uint32_t dax_query_persist_memory(struct sof_dax *dax_ctx); + +/** + * @brief Query the scratch memory requirements for the DAX module + * + * @param[in] dax_ctx Pointer to the DAX context structure + * + * @return Size of required scratch memory in bytes + */ +uint32_t dax_query_scratch_memory(struct sof_dax *dax_ctx); + +/** + * @brief Query the number of frames in a processing period + * + * @param[in] dax_ctx Pointer to the DAX context structure + * + * @return Number of frames per period + */ +uint32_t dax_query_period_frames(struct sof_dax *dax_ctx); + +/** + * @brief Free the DAX module + * + * @param[in] dax_ctx Pointer to the DAX context structure + * + * This function free all resources built on persistent buffer. + * DO NOT USE THE INSTANCE AFTER CALLING FREE. + * + * @return 0 on success, negative error code on failure + */ +int dax_free(struct sof_dax *dax_ctx); + +/** + * @brief Initialize the DAX module + * + * @param[in] dax_ctx Pointer to the DAX context structure + * + * @return 0 on success, negative error code on failure + */ +int dax_init(struct sof_dax *dax_ctx); + +/** + * @brief Process audio data through the DAX module + * + * If DAX is disabled or the DAX instance is invalid (dax_ctx->p_dax is NULL), + * the dax_process will by default perform only copy operations, without any + * audio processing. + * + * @param[in] dax_ctx Pointer to the DAX context structure + * + * @return Bytes of processed. negative error code on failure + */ +int dax_process(struct sof_dax *dax_ctx); + +/** + * @brief Set a parameter value for the DAX module + * + * @param[in] id Parameter identifier + * @param[in] val Pointer to parameter value + * @param[in] val_sz Size of parameter value in bytes + * @param[in] dax_ctx Pointer to the DAX context structure + * + * @return 0 on success, negative error code on failure + */ +int dax_set_param(uint32_t id, const void *val, uint32_t val_sz, struct sof_dax *dax_ctx); + +/** + * @brief Enable/Disable the DAX module + * + * @param[in] enable 0:disable, 1:enable. + * @param[in] dax_ctx Pointer to the DAX context structure + * + * @return 0 on success, negative error code on failure + */ +int dax_set_enable(int32_t enable, struct sof_dax *dax_ctx); + +/** + * @brief Set the volume for the DAX module + * + * @param[in] volume Value to apply + * @param[in] dax_ctx Pointer to the DAX context structure + * + * @return 0 or positive code on success, negative error code on failure + */ +int dax_set_volume(int32_t volume, struct sof_dax *dax_ctx); + +/** + * @brief Update the output device configuration + * + * @param[in] out_device Output device identifier. Supported devices: + * 0: speaker + * 1: headphone + * @param[in] dax_ctx Pointer to the DAX context structure + * + * @return 0 on success, negative error code on failure + */ +int dax_set_device(int32_t out_device, struct sof_dax *dax_ctx); + +/** + * @brief Enable/Disable crosstalk cancellation feature + * + * @param[in] enable 0:disable, 1:enable. + * @param[in] dax_ctx Pointer to the DAX context structure + * + * @return 0 on success, negative error code on failure + */ +int dax_set_ctc_enable(int32_t enable, struct sof_dax *dax_ctx); + +/** + * @brief Get the DAX module version string + * + * @return Pointer to null-terminated version string + */ +const char *dax_get_version(void); + +/** + * @brief Find parameters in a buffer based on query criteria + * + * @param[in] query_id ID of the parameter to search for. Supported query IDs: + * - DAX_PARAM_ID_PROFILE + * - DAX_PARAM_ID_TUNING_DEVICE + * - DAX_PARAM_ID_CP_ENABLE + * @param[in] query_val Value to match when searching + * @param[out] query_sz Pointer to store the size of the found parameters + * @param[in] dax_ctx Pointer to the DAX context structure + * + * @return Pointer to the found parameters, or NULL if not found + */ +void *dax_find_params(uint32_t query_id, + int32_t query_val, + uint32_t *query_sz, + struct sof_dax *dax_ctx); + +#endif /* DAX_INF_H */ diff --git a/tools/ctl/ipc4/drc/generic_notebook_speaker.txt b/tools/ctl/ipc4/drc/generic_notebook_speaker.txt deleted file mode 100644 index 104210eec4d9..000000000000 --- a/tools/ctl/ipc4/drc/generic_notebook_speaker.txt +++ /dev/null @@ -1 +0,0 @@ -877023059,0,108,50438144,0,0,0,0,108,0,0,0,0,1,3875536896,251658240,167772160,6442451,60380940,107374182,12708444,2327743,4292230659,5305422,162898102,73470028,7456540,4286019447,2062296,5,4423680,294359,2477728,622039,46513, diff --git a/tools/ctl/ipc4/drc/speaker_default.txt b/tools/ctl/ipc4/drc/speaker_default.txt new file mode 100644 index 000000000000..03d17e103040 --- /dev/null +++ b/tools/ctl/ipc4/drc/speaker_default.txt @@ -0,0 +1 @@ +3,140,877023059,0,108,50450433,0,0,0,0,108,0,0,0,0,1,3791650816,335544320,167772160,6442451,33954698,107374182,12228399,1969176,4292887072,5305422,137666458,81276115,7456540,4286019447,2062296,5,4423680,294359,2477728,622039,46513 diff --git a/tools/debug_stream/debug_stream.py b/tools/debug_stream/debug_stream.py index 9c3706d90f99..39770cab4cd3 100644 --- a/tools/debug_stream/debug_stream.py +++ b/tools/debug_stream/debug_stream.py @@ -46,7 +46,6 @@ class DebugStreamRecord(ctypes.Structure): ("size_words", ctypes.c_uint), ] - class CPUInfo(ctypes.Structure): """ Thread Info record header @@ -73,6 +72,17 @@ class ThreadInfo(ctypes.Structure): ] +class TextMsg(ctypes.Structure): + """ + Text Msg record header + """ + + _pack_ = 1 + _fields_ = [ + ("hdr", DebugStreamRecord), + ] + + WSIZE = ctypes.sizeof(ctypes.c_uint) @@ -83,6 +93,7 @@ class RecordPrinter: RECORD_ID_UNINITIALIZED = 0 RECORD_ID_THREAD_INFO = 1 + RECORD_ID_TEXT_MSG = 2 def print_record(self, record, cpu): """prints debug-stream record""" @@ -92,7 +103,9 @@ def print_record(self, record, cpu): ) if recp.contents.id == self.RECORD_ID_THREAD_INFO: return self.print_thread_info(record, cpu) - logging.warning("cpu %u: Unsupported recodrd type %u", cpu, recp.contents.id) + if recp.contents.id == self.RECORD_ID_TEXT_MSG: + return self.print_text_msg(record, cpu) + logging.warning("cpu %u: Unsupported record type %u", cpu, recp.contents.id) return True def print_thread_info(self, record, cpu): @@ -141,6 +154,18 @@ def print_thread_info(self, record, cpu): ) return True + def print_text_msg(self, record, cpu): + """prints text-msg record""" + if len(record) - ctypes.sizeof(TextMsg) < 0: + logging.info("Buffer end reached, parsing failed") + return False + buffer = ( + ctypes.c_ubyte * (len(record) - ctypes.sizeof(TextMsg)) + ).from_address(ctypes.addressof(record) + ctypes.sizeof(TextMsg)) + payload = bytes(buffer) + msg = payload.split(b"\0", 1)[0].decode("utf-8", errors="replace") + print("CPU %u:\n%s" % (cpu, msg)) + return True class DebugStreamSectionDescriptor(ctypes.Structure): """ diff --git a/tools/rimage/.checkpatch.conf b/tools/rimage/.checkpatch.conf deleted file mode 100644 index 98ddafcb3fb1..000000000000 --- a/tools/rimage/.checkpatch.conf +++ /dev/null @@ -1,6 +0,0 @@ ---codespell ---codespellfile scripts/spelling.txt ---ignore C99_COMMENT_TOLERANCE ---no-tree ---strict --g diff --git a/tools/rimage/README.md b/tools/rimage/README.md index 9f32fffa5cb1..797bb662f6d6 100644 --- a/tools/rimage/README.md +++ b/tools/rimage/README.md @@ -1,15 +1,10 @@ # rimage -`rimage` is a DSP firmware image creation and signing tool targeting -the DSP on certain Intel System-on-Chip (SoC). This is used by -the [Sound Open Firmware (SOF)](https://github.com/thesofproject/sof) -to generate binary image files. +`rimage` is a DSP firmware image creation and signing tool targeting the DSP on certain Intel System-on-Chip (SoC). This is used by the [Sound Open Firmware (SOF)](https://github.com/thesofproject/sof) to generate binary image files. ## Building -Most SOF users never build `rimage` directly but as an ExternalProject -defined by CMake in SOF. This makes sure they always use an up-to-date -version of rimage and configuration files that have been fully tested. +Most SOF users never build `rimage` directly but as an ExternalProject defined by CMake in SOF. This makes sure they always use an up-to-date version of rimage and configuration files that have been fully tested. If needed, `rimage` can be built manually with the usual CMake commands: @@ -19,87 +14,109 @@ $ make -C build/ help # lists all targets $ make -C build/ ``` -The `build/rimage` executable can then be copied to a directory in the -PATH. Zephyr users can run `west config rimage.path -/path/to/rimage/build/rimage`; Zephyr documentation and `west sign -h` -have more details. +The `build/rimage` executable can then be copied to a directory in the PATH. Zephyr users can run `west config rimage.path /path/to/rimage/build/rimage`; Zephyr documentation and `west sign -h` have more details. ## Testing tomlc99 changes with SOF Continuous Integration -This section is about leveraging SOF validation to test tomlc99 changes -_before_ submitting them to the tomlc99 repository. +This section is about leveraging SOF validation to test tomlc99 changes _before_ submitting them to the tomlc99 repository. -Nothing here is actually specific to SOF and tomlc99; you can apply the -same test logic to any submodule and parent on Github. In fact the same -logic applies to submodule alternatives. Github is the only requirement. +Nothing here is actually specific to SOF and tomlc99; you can apply the same test logic to any submodule and parent on GitHub. In fact the same logic applies to submodule alternatives. GitHub is the only requirement. ### Get familiar with git submodules This is unfortunately not optional for SOF and tomlc99. -For various reasons submodules seem to confuse many git users. Maybe -because the versions of the submodules are not directly visible in some -configuration file like with most alternatives? Either way, an -unfortunate prerequisite before doing any tomlc99 work is to get familiar -with git submodules in general. As submodules are built-in there are -many resources about them on the Internet. One possible starting point -is https://git-scm.com/book/en/v2/Git-Tools-Submodules but feel free -to use any other good tutorial instead. Make sure you actually practice -a tutorial; don't just read it. Practicing on a temporary and throw-away -copy of SOF + tomlc99 is a great idea. +For various reasons submodules seem to confuse many git users. Maybe because the versions of the submodules are not directly visible in some configuration file like with most alternatives? Either way, an unfortunate prerequisite before doing any tomlc99 work is to get familiar with git submodules in general. As submodules are built-in there are many resources about them on the Internet. One possible starting point is https://git-scm.com/book/en/v2/Git-Tools-Submodules but feel free to use any other good tutorial instead. Make sure you actually practice a tutorial; don't just read it. Practicing on a temporary and throw-away copy of SOF + tomlc99 is a great idea. -Obviously, you also need to be familiar with regular Github pull -requests. +Obviously, you also need to be familiar with regular GitHub pull requests. ### Run SOF tests on unmerged tomlc99 commits -First, push the tomlc99 commits you want to be tested to any branch of -your tomlc99 fork on Github. Do _not_ submit an tomlc99 pull request yet. - -Note your tomlc99 fork must have been created using the actual "fork" -button on Github so Github is aware of the connection with the upstream -tomlc99 repo. In the top-left corner you should see `forked from -thesofproject/tomlc99` under the name of your fork. If not then search -the Internet for "re-attach detached github fork". - -Then, **pretend** these tomlc99 commits have already been accepted and -merged (they have been neither) and submit to SOF a draft pull request -that updates the main SOF branch with your brand new tomlc99 commits to -test. The only SOF commit in this SOF TEST pull request is an SOF commit -that updates the tomlc99 pointer to the SHA of your last tomlc99 -commit. If you're not sure how to do this then you must go back to the -previous section and practice submodules more. - -Submit this SOF pull request as a Github _draft_ so reviewers are _not_ -notified. Starting every pull request as a draft is always a good idea -but in this case this particular SOF pull request can be especially -confusing because it points at commits in a different repo and commits -that are not merged yet. So you _really_ don't want to bother busy -reviewers (here's a secret: some of the reviewers don't like submodules -either). You can freely switch back and forth between draft and ready -status and should indeed switch to draft if you forgot at submission -time but you can never "un-notify" reviewers. - -Github has very good support for submodules and will display your SOF -TEST pull request better than what the git command line can show. For -instance Github will list your tomlc99 changes directly in the SOF Pull -Request. So if something looks unexpected on Github then it means you -did something wrong. Stop immediately (except for switching to draft if -you forgot) and ask the closest git guru for help. - -Search for "Submodule" in the build logs and make sure the last of your -new tomlc99 commits has been checked out. - -Iterate and force-push your tomlc99 branch and your SOF TEST pull request -until all the SOF tests pass. Then you can submit your tomlc99 pull -request as usual. In the comments section of the tomlc99 pull request, -point at your test results on the SOF side to impress the tomlc99 -reviewers and get your tomlc99 changes merged faster. - -Finally, after your tomlc99 changes have been merged, you can if you want -submit one final SOF pull request that points to the final tomlc99 -SHA. Or, if your tomlc99 change is not urgently needed, you can just wait -for someone else to do it later. If you do it, copy the tomlc99 git log ---oneline in the SOF commit message. Find some good (and less good) -commit message examples for submodule updates at -https://github.com/thesofproject/sof/commits/main/rimage +First, push the tomlc99 commits you want to be tested to any branch of your tomlc99 fork on GitHub. Do _not_ submit a tomlc99 pull request yet. + +Note your tomlc99 fork must have been created using the actual "fork" button on GitHub so GitHub is aware of the connection with the upstream tomlc99 repo. In the top-left corner you should see `forked from thesofproject/tomlc99` under the name of your fork. If not then search the Internet for "re-attach detached github fork". + +Then, **pretend** these tomlc99 commits have already been accepted and merged (they have been neither) and submit to SOF a draft pull request that updates the main SOF branch with your brand new tomlc99 commits to test. The only SOF commit in this SOF TEST pull request is an SOF commit that updates the tomlc99 pointer to the SHA of your last tomlc99 commit. If you're not sure how to do this then you must go back to the previous section and practice submodules more. + +Submit this SOF pull request as a GitHub _draft_ so reviewers are _not_ notified. Starting every pull request as a draft is always a good idea but in this case this particular SOF pull request can be especially confusing because it points at commits in a different repo and commits that are not merged yet. So you _really_ don't want to bother busy reviewers (here's a secret: some of the reviewers don't like submodules either). You can freely switch back and forth between draft and ready status and should indeed switch to draft if you forgot at submission time but you can never "un-notify" reviewers. + +GitHub has very good support for submodules and will display your SOF TEST pull request better than what the git command line can show. For instance GitHub will list your tomlc99 changes directly in the SOF Pull Request. So if something looks unexpected on GitHub then it means you did something wrong. Stop immediately (except for switching to draft if you forgot) and ask the closest git guru for help. + +Search for "Submodule" in the build logs and make sure the last of your new tomlc99 commits has been checked out. + +Iterate and force-push your tomlc99 branch and your SOF TEST pull request until all the SOF tests pass. Then you can submit your tomlc99 pull request as usual. In the comments section of the tomlc99 pull request, point at your test results on the SOF side to impress the tomlc99 reviewers and get your tomlc99 changes merged faster. + +Finally, after your tomlc99 changes have been merged, you can if you want submit one final SOF pull request that points to the final tomlc99 SHA. Or, if your tomlc99 change is not urgently needed, you can just wait for someone else to do it later. If you do it, copy the tomlc99 git log --oneline in the SOF commit message. Find some good (and less good) commit message examples for submodule updates at https://github.com/thesofproject/sof/commits/main/rimage + +## Deep Dive: ELF to Image Conversion and Manifest Structure + +`rimage` is responsible for converting standard ELF libraries and executables into DSP-compatible firmware images. It parses input ELF files, extracts loadable sections, calculates cryptographic hashes and signatures, and encapsulates them with platform-specific manifests. + +### The Build Process (ELF to Image) + +```mermaid +graph TD + A[Command Line Args] --> B[Initialize ADSP Config] + C[Input ELF Files] --> D[Parse ELF Headers & Sections] + D --> E[Register Modules & Metadata] + B --> F[Allocate Manifest Memory] + E --> F + F --> G["Copy .text, .rodata to Image Buffer"] + G --> H["Hash Module Segments (SHA-256 / SHA-384)"] + H --> I["Finalize Manifest Headers (CSS/CSE/FW Desc)"] + I --> J[Sign Root Manifest with RSA Key] + J --> K[Write Output Files] + K --> L[.ri Signed Image] + K --> M[.uns Unsigned Module] + K --> N[.met Manifest Metadata] +``` + +1. **Configuration Initialization**: `rimage` parses its command-line arguments to find the ADSP machine configuration (e.g., Apollolake `apl`, Tigerlake `tgl`, Meteorlake `mtl`). This configuration defines the specific sizes, offsets, and versions the firmware image will require. +2. **Parsing ELF Files**: The `module_open()` and `module_parse_sections()` functions read the ELF headers (`.text`, `.rodata`, `.bss`). These sections are extracted to form the payload of the modules. +3. **Module Registration**: `rimage` reads either the `.module` metadata section directly from the embedded ELF, or a corresponding `TOML` configuration file, which holds instructions on module attributes (e.g., UUID, thread affinity, entry points). +4. **Manifest and Memory Allocation**: Space is pre-allocated for the firmware image. `man_init_image_xxx()` loads the ADSP template manifest into the start of the image buffer. Then, `man_copy_elf_sections()` copies `.text` and `.rodata` sections directly into the buffer, aligning properties sequentially up to `MAN_PAGE_SIZE` (4096 bytes). +5. **Cryptographic Hashing**: Using `hash_sha256()` or `hash_sha384()` (depending on the platform), `rimage` generates a cryptographic digest of each module's `.text` and `.rodata` block. These hash digests are stored in the respective `sof_man_module` entries of the manifest. +6. **Manifest Signing**: Once all modules and internal structures are mapped, the entire structure is finalized with converged security headers (`ri_css_xxx_hdr_create`, `ri_cse_create`). The root manifest signature is then signed (`ri_manifest_sign_...()`) with an RSA private key. +7. **Writing the Payload**: Finally, the newly encapsulated firmware is written to a `.ri` file (`man_write_fw_mod()`). `rimage` also outputs a stripped `.uns` (unsigned module payload) and `.met` (manifest metadata) file for debugging. + +### Manifest Structure + +```mermaid +graph TD + A[FW Image Manifest] + A --> B[1. CSS Header] + A --> C[2. CSE Header] + A --> D[3. Firmware Descriptor] + A --> E[4. Module Entries Array] + A --> F[5. ADSP Extension Metadata] + + B --> B1("RSA Signatures") + B --> B2("Public Key & Modulus") + + C --> C1("Security Partitions Directory") + + D --> D1("Versions & Build IDs") + D --> D2("num_module_entries") + + E --> E1[Module 0] + E --> E2[Module N] + E1 --> E1a("UUID, Entry Point") + E1 --> E1b("Segment Relative Offsets") + E1 --> E1c("SHA Hash of Sub-Segments") + + F --> F1("Proprietary Components") + F --> F2("Hardware Specific Hashes") +``` + +The firmware image begins with a structured Manifest. Different platforms use altered sizes and variants (e.g., v1.5, v1.8, v2.5, ace_v1.5), but they typically follow the same overarching hierarchy (note: ordering of CSS and CSE headers may be swapped depending on the manifest version - e.g., v1.8+/v2.5 places CSE first): + +1. **CSS Header** (CSS header struct, e.g. `struct css_header_v1_8`, `struct css_header_v2_5`): Cryptographic Signature Structure. Defines the fundamental size, modulus, public key, and RSA signatures used to authenticate the firmware image on the CSME/DSP side. +2. **CSE Header** (CSE partition directory header struct, e.g. `struct CsePartitionDirHeader`, `struct CsePartitionDirHeader_v2_5`): Converged Security Engine Header. A directory mapping security partitions to the ADSP firmware metadata. +3. **Firmware Descriptor** (`struct sof_man_fw_desc` and versioned variants): + - `header`: Holds the basic firmware versions, build IDs, `preload_page_count`, and the `num_module_entries`. +4. **Module Entries** (`struct sof_man_module`): An array corresponding to each module contained in the image. + - `struct_id`: Has the literal value `"$AME"`. + - `name`, `uuid`, `entry_point`, `affinity`. + - `segment`: Details on the relative position `file_offset` and memory offset `v_base_addr` of `.text`, `.rodata`, and `.bss` partitions. + - `hash`: SHA-256 or SHA-384 digest of the active segment. +5. **ADSP Extension Metadata** (e.g., `sof_man_adsp_meta_file_ext_v2_5`): Additional proprietary component descriptors attached after the base manifest, frequently hashed recursively for verification integrity. diff --git a/tools/rimage/config/imx8m_cm7.toml b/tools/rimage/config/imx8m_cm7.toml new file mode 100644 index 000000000000..2ba4fb5d16f6 --- /dev/null +++ b/tools/rimage/config/imx8m_cm7.toml @@ -0,0 +1,15 @@ +version = [1, 0] + +[adsp] +name = "imx8m_cm7" + +[[adsp.mem_zone]] +type = "IRAM" +base = "0x0000000" +size = "0x2000" +host_offset = "0x0" +[[adsp.mem_zone]] +type = "SRAM" +base = "0x80000000" +size = "0x100000" +host_offset = "0x0" diff --git a/tools/rimage/config/lnl.toml.h b/tools/rimage/config/lnl.toml.h index 5fc7008bfbe8..7a574d4ae906 100644 --- a/tools/rimage/config/lnl.toml.h +++ b/tools/rimage/config/lnl.toml.h @@ -62,6 +62,10 @@ #include <audio/src/src.toml> #endif +#if defined(CONFIG_CADENCE_CODEC) || defined(LLEXT_FORCE_ALL_MODULAR) +#include <audio/module_adapter/module/cadence.toml> +#endif + #if defined(CONFIG_COMP_SEL) || defined(LLEXT_FORCE_ALL_MODULAR) #include <audio/selector/selector.toml> #endif @@ -150,5 +154,13 @@ #include <audio/sound_dose/sound_dose.toml> #endif +#if defined(CONFIG_COMP_TONE) || defined(LLEXT_FORCE_ALL_MODULAR) +#include <audio/tone/tone.toml> +#endif + +#if defined(CONFIG_COMP_STFT_PROCESS) || defined(LLEXT_FORCE_ALL_MODULAR) +#include <audio/stft_process/stft_process.toml> +#endif + [module] count = __COUNTER__ diff --git a/tools/rimage/config/mtl.toml.h b/tools/rimage/config/mtl.toml.h index ac4287051b07..ec9436488ca3 100644 --- a/tools/rimage/config/mtl.toml.h +++ b/tools/rimage/config/mtl.toml.h @@ -78,6 +78,10 @@ #include <audio/mux/mux.toml> #endif +#if defined(CONFIG_CADENCE_CODEC) || defined(LLEXT_FORCE_ALL_MODULAR) +#include <audio/module_adapter/module/cadence.toml> +#endif + #ifdef CONFIG_SAMPLE_KEYPHRASE #include <samples/audio/detect_test.toml> #endif @@ -118,6 +122,10 @@ #include <audio/dcblock/dcblock.toml> #endif +#if defined(CONFIG_COMP_DOLBY_DAX_AUDIO_PROCESSING) || defined(LLEXT_FORCE_ALL_MODULAR) +#include <audio/module_adapter/module/dolby/dax.toml> +#endif + #if defined(CONFIG_COMP_GOOGLE_RTC_AUDIO_PROCESSING) || defined(LLEXT_FORCE_ALL_MODULAR) #include <audio/google/google_rtc_audio_processing.toml> #endif @@ -134,10 +142,6 @@ #include <audio/codec/dts/dts.toml> #endif -#ifdef CONFIG_CADENCE_CODEC -#include <audio/module_adapter/module/cadence.toml> -#endif - #if defined(CONFIG_WAVES_CODEC) || defined(LLEXT_FORCE_ALL_MODULAR) #include <audio/module_adapter/module/waves/waves.toml> #endif @@ -170,5 +174,13 @@ #include <audio/sound_dose/sound_dose.toml> #endif +#if defined(CONFIG_COMP_TONE) || defined(LLEXT_FORCE_ALL_MODULAR) +#include <audio/tone/tone.toml> +#endif + +#if defined(CONFIG_COMP_STFT_PROCESS) || defined(LLEXT_FORCE_ALL_MODULAR) +#include <audio/stft_process/stft_process.toml> +#endif + [module] count = __COUNTER__ diff --git a/tools/rimage/config/nvl.toml.h b/tools/rimage/config/nvl.toml.h index eafe7501ac8d..4fa0ca805877 100644 --- a/tools/rimage/config/nvl.toml.h +++ b/tools/rimage/config/nvl.toml.h @@ -12,6 +12,20 @@ auto_start = "0" index = __COUNTER__ +#if CONFIG_COLD_STORE_EXECUTE_DRAM +[[module.entry]] +name = "COLD" +uuid = UUIDREG_STR_COLD +affinity_mask = "3" +instance_count = "1" +domain_types = "0" +load_type = "0" +module_type = "0" +auto_start = "0" + +index = __COUNTER__ +#endif + [[module.entry]] name = "BASEFW" uuid = "0E398C32-5ADE-BA4B-93B1-C50432280EE4" @@ -24,35 +38,39 @@ auto_start = "0" index = __COUNTER__ -#if defined(CONFIG_COMP_TESTER) +#if defined(CONFIG_COMP_TESTER) || defined(LLEXT_FORCE_ALL_MODULAR) #include <debug/tester/tester.toml> #endif -#ifdef CONFIG_COMP_MIXIN_MIXOUT +#if defined(CONFIG_COMP_MIXIN_MIXOUT) || defined(LLEXT_FORCE_ALL_MODULAR) #include <audio/mixin_mixout/mixin_mixout.toml> #endif -#ifdef CONFIG_COMP_COPIER +#if defined(CONFIG_COMP_COPIER) || defined(LLEXT_FORCE_ALL_MODULAR) #include <audio/copier/copier.toml> #endif -#ifdef CONFIG_COMP_VOLUME +#if defined(CONFIG_COMP_VOLUME) || defined(LLEXT_FORCE_ALL_MODULAR) #include <audio/volume/volume.toml> #endif -#ifdef CONFIG_COMP_ASRC +#if defined(CONFIG_COMP_ASRC) || defined(LLEXT_FORCE_ALL_MODULAR) #include <audio/asrc/asrc.toml> #endif -#ifdef CONFIG_COMP_SRC +#if defined(CONFIG_COMP_SRC) || defined(LLEXT_FORCE_ALL_MODULAR) #include <audio/src/src.toml> #endif -#ifdef CONFIG_COMP_SEL +#if defined(CONFIG_CADENCE_CODEC) || defined(LLEXT_FORCE_ALL_MODULAR) +#include <audio/module_adapter/module/cadence.toml> +#endif + +#if defined(CONFIG_COMP_SEL) || defined(LLEXT_FORCE_ALL_MODULAR) #include <audio/selector/selector.toml> #endif -#ifdef CONFIG_COMP_UP_DOWN_MIXER +#if defined(CONFIG_COMP_UP_DOWN_MIXER) || defined(LLEXT_FORCE_ALL_MODULAR) #include <audio/up_down_mixer/up_down_mixer.toml> #endif @@ -60,7 +78,7 @@ index = __COUNTER__ #include <probe/probe.toml> #endif -#ifdef CONFIG_COMP_MUX +#if defined(CONFIG_COMP_MUX) || defined(LLEXT_FORCE_ALL_MODULAR) #include <audio/mux/mux.toml> #endif @@ -68,65 +86,73 @@ index = __COUNTER__ #include <samples/audio/detect_test.toml> #endif -#ifdef CONFIG_COMP_KPB +#if defined(CONFIG_COMP_KPB) || defined(LLEXT_FORCE_ALL_MODULAR) #include <audio/kpb.toml> #endif -#ifdef CONFIG_SAMPLE_SMART_AMP +#if defined(CONFIG_SAMPLE_SMART_AMP) || defined(LLEXT_FORCE_ALL_MODULAR) #include <samples/audio/smart_amp_test.toml> #endif -#ifdef CONFIG_COMP_IIR +#if defined(CONFIG_COMP_IIR) || defined(LLEXT_FORCE_ALL_MODULAR) #include <audio/eq_iir/eq_iir.toml> #endif -#ifdef CONFIG_COMP_FIR +#if defined(CONFIG_COMP_FIR) || defined(LLEXT_FORCE_ALL_MODULAR) #include <audio/eq_fir/eq_fir.toml> #endif -#ifdef CONFIG_COMP_ARIA +#if defined(CONFIG_COMP_ARIA) || defined(LLEXT_FORCE_ALL_MODULAR) #include <audio/aria/aria.toml> #endif -#ifdef CONFIG_COMP_DRC +#if defined(CONFIG_COMP_DRC) || defined(LLEXT_FORCE_ALL_MODULAR) #include <audio/drc/drc.toml> #endif -#ifdef CONFIG_COMP_CROSSOVER +#if defined(CONFIG_COMP_CROSSOVER) || defined(LLEXT_FORCE_ALL_MODULAR) #include <audio/crossover/crossover.toml> #endif -#ifdef CONFIG_COMP_MULTIBAND_DRC +#if defined(CONFIG_COMP_MULTIBAND_DRC) || defined(LLEXT_FORCE_ALL_MODULAR) #include <audio/multiband_drc/multiband_drc.toml> #endif -#ifdef CONFIG_COMP_DCBLOCK +#if defined(CONFIG_COMP_DCBLOCK) || defined(LLEXT_FORCE_ALL_MODULAR) #include <audio/dcblock/dcblock.toml> #endif -#ifdef CONFIG_COMP_GOOGLE_RTC_AUDIO_PROCESSING +#if defined(CONFIG_COMP_GOOGLE_RTC_AUDIO_PROCESSING) || defined(LLEXT_FORCE_ALL_MODULAR) #include <audio/google/google_rtc_audio_processing.toml> #endif -#ifdef CONFIG_COMP_TDFB +#if defined(CONFIG_COMP_TDFB) || defined(LLEXT_FORCE_ALL_MODULAR) #include <audio/tdfb/tdfb.toml> #endif -#ifdef CONFIG_COMP_RTNR +#if defined(CONFIG_COMP_RTNR) || defined(LLEXT_FORCE_ALL_MODULAR) #include <audio/rtnr/rtnr.toml> #endif -#ifdef CONFIG_COMP_IGO_NR +#if defined(CONFIG_COMP_IGO_NR) || defined(LLEXT_FORCE_ALL_MODULAR) #include <audio/igo_nr/igo_nr.toml> #endif -#ifdef CONFIG_COMP_MFCC +#if defined(CONFIG_COMP_MFCC) || defined(LLEXT_FORCE_ALL_MODULAR) #include <audio/mfcc/mfcc.toml> #endif -#if defined(CONFIG_COMP_SOUND_DOSE) +#if defined(CONFIG_COMP_SOUND_DOSE) || defined(LLEXT_FORCE_ALL_MODULAR) #include <audio/sound_dose/sound_dose.toml> #endif +#if defined(CONFIG_COMP_TONE) || defined(LLEXT_FORCE_ALL_MODULAR) +#include <audio/tone/tone.toml> +#endif + +#if defined(CONFIG_COMP_STFT_PROCESS) || defined(LLEXT_FORCE_ALL_MODULAR) +#include <audio/stft_process/stft_process.toml> +#endif + [module] count = __COUNTER__ diff --git a/tools/rimage/config/platform-tgl-h.toml b/tools/rimage/config/platform-tgl-h.toml new file mode 100644 index 000000000000..8e98866d9bcb --- /dev/null +++ b/tools/rimage/config/platform-tgl-h.toml @@ -0,0 +1,60 @@ +version = [2, 5] + +[adsp] +name = "tgl" +image_size = "0x1F0000" # (30 + 1) bank * 64KB +alias_mask = "0xE0000000" + +[[adsp.mem_zone]] +type = "ROM" +base = "0x9F180000" +size = "0x00002000" +[[adsp.mem_zone]] +type = "IMR" +base = "0xB0000000" +size = "0x1000000" +[[adsp.mem_zone]] +type = "HP-SRAM" +base = "0xBE000000" +size = "0x800000" +[[adsp.mem_zone]] +type = "LP-SRAM" +base = "0xBE800000" +size = "0x40" + +[[adsp.mem_alias]] +type = "uncached" +base = "0x9E000000" +[[adsp.mem_alias]] +type = "cached" +base = "0xBE000000" + +[cse] +partition_name = "ADSP" +[[cse.entry]] +name = "ADSP.man" +offset = "0x5c" +length = "0x464" +[[cse.entry]] +name = "cavs0015.met" +offset = "0x4c0" +length = "0x70" +[[cse.entry]] +name = "cavs0015" +offset = "0x540" +length = "0x0" # calculated by rimage + +[css] + +[signed_pkg] +name = "ADSP" +[[signed_pkg.module]] +name = "cavs0015.met" + +[adsp_file] +[[adsp_file.comp]] +base_offset = "0x2000" + +[fw_desc.header] +name = "ADSPFW" +load_offset = "0x30000" diff --git a/tools/rimage/config/platform-tgl.toml b/tools/rimage/config/platform-tgl.toml new file mode 100644 index 000000000000..ffbf15862ac4 --- /dev/null +++ b/tools/rimage/config/platform-tgl.toml @@ -0,0 +1,60 @@ +version = [2, 5] + +[adsp] +name = "tgl" +image_size = "0x2F0000" +alias_mask = "0xE0000000" + +[[adsp.mem_zone]] +type = "ROM" +base = "0x9F180000" +size = "0x00002000" +[[adsp.mem_zone]] +type = "IMR" +base = "0xB0000000" +size = "0x1000000" +[[adsp.mem_zone]] +type = "HP-SRAM" +base = "0xBE000000" +size = "0x800000" +[[adsp.mem_zone]] +type = "LP-SRAM" +base = "0xBE800000" +size = "0x40" + +[[adsp.mem_alias]] +type = "uncached" +base = "0x9E000000" +[[adsp.mem_alias]] +type = "cached" +base = "0xBE000000" + +[cse] +partition_name = "ADSP" +[[cse.entry]] +name = "ADSP.man" +offset = "0x5c" +length = "0x464" +[[cse.entry]] +name = "cavs0015.met" +offset = "0x4c0" +length = "0x70" +[[cse.entry]] +name = "cavs0015" +offset = "0x540" +length = "0x0" # calculated by rimage + +[css] + +[signed_pkg] +name = "ADSP" +[[signed_pkg.module]] +name = "cavs0015.met" + +[adsp_file] +[[adsp_file.comp]] +base_offset = "0x2000" + +[fw_desc.header] +name = "ADSPFW" +load_offset = "0x30000" diff --git a/tools/rimage/config/platform.toml b/tools/rimage/config/platform.toml index 0210477b7f30..978323fa4ebc 100644 --- a/tools/rimage/config/platform.toml +++ b/tools/rimage/config/platform.toml @@ -2,6 +2,8 @@ #include "platform-mtl.toml" #elif CONFIG_LUNARLAKE #include "platform-lnl.toml" -#elif CONFIG_SOC_INTEL_ACE30 +#elif CONFIG_SOC_ACE30 #include "platform-ptl.toml" +#elif CONFIG_SOC_ACE40 +#include "platform-nvl.toml" #endif diff --git a/tools/rimage/config/ptl.toml.h b/tools/rimage/config/ptl.toml.h index 1a447c93c1c1..e622c8af968e 100644 --- a/tools/rimage/config/ptl.toml.h +++ b/tools/rimage/config/ptl.toml.h @@ -62,6 +62,10 @@ index = __COUNTER__ #include <audio/src/src.toml> #endif +#if defined(CONFIG_CADENCE_CODEC) || defined(LLEXT_FORCE_ALL_MODULAR) +#include <audio/module_adapter/module/cadence.toml> +#endif + #if defined(CONFIG_COMP_SEL) || defined(LLEXT_FORCE_ALL_MODULAR) #include <audio/selector/selector.toml> #endif @@ -102,6 +106,10 @@ index = __COUNTER__ #include <audio/aria/aria.toml> #endif +#if defined(CONFIG_COMP_DOLBY_DAX_AUDIO_PROCESSING) || defined(LLEXT_FORCE_ALL_MODULAR) +#include <audio/module_adapter/module/dolby/dax.toml> +#endif + #if defined(CONFIG_COMP_DRC) || defined(LLEXT_FORCE_ALL_MODULAR) #include <audio/drc/drc.toml> #endif @@ -122,10 +130,18 @@ index = __COUNTER__ #include <audio/google/google_rtc_audio_processing.toml> #endif +#if defined(CONFIG_COMP_GOOGLE_CTC_AUDIO_PROCESSING) || defined(LLEXT_FORCE_ALL_MODULAR) +#include <audio/google/google_ctc_audio_processing.toml> +#endif + #if defined(CONFIG_COMP_TDFB) || defined(LLEXT_FORCE_ALL_MODULAR) #include <audio/tdfb/tdfb.toml> #endif +#if defined(CONFIG_DTS_CODEC) || defined(LLEXT_FORCE_ALL_MODULAR) +#include <audio/codec/dts/dts.toml> +#endif + #if defined(CONFIG_COMP_RTNR) || defined(LLEXT_FORCE_ALL_MODULAR) #include <audio/rtnr/rtnr.toml> #endif @@ -150,5 +166,13 @@ index = __COUNTER__ #include <audio/sound_dose/sound_dose.toml> #endif +#if defined(CONFIG_COMP_TONE) || defined(LLEXT_FORCE_ALL_MODULAR) +#include <audio/tone/tone.toml> +#endif + +#if defined(CONFIG_COMP_STFT_PROCESS) || defined(LLEXT_FORCE_ALL_MODULAR) +#include <audio/stft_process/stft_process.toml> +#endif + [module] count = __COUNTER__ diff --git a/tools/rimage/config/tgl-h.toml b/tools/rimage/config/tgl-h.toml deleted file mode 100644 index 822423382578..000000000000 --- a/tools/rimage/config/tgl-h.toml +++ /dev/null @@ -1,641 +0,0 @@ -version = [2, 5] - -[adsp] -name = "tgl" -image_size = "0x1F0000" # (30 + 1) bank * 64KB -alias_mask = "0xE0000000" - -[[adsp.mem_zone]] -type = "ROM" -base = "0x9F180000" -size = "0x00002000" -[[adsp.mem_zone]] -type = "IMR" -base = "0xB0000000" -size = "0x1000000" -[[adsp.mem_zone]] -type = "HP-SRAM" -base = "0xBE000000" -size = "0x800000" -[[adsp.mem_zone]] -type = "LP-SRAM" -base = "0xBE800000" -size = "0x40" - -[[adsp.mem_alias]] -type = "uncached" -base = "0x9E000000" -[[adsp.mem_alias]] -type = "cached" -base = "0xBE000000" - -[cse] -partition_name = "ADSP" -[[cse.entry]] -name = "ADSP.man" -offset = "0x5c" -length = "0x464" -[[cse.entry]] -name = "cavs0015.met" -offset = "0x4c0" -length = "0x70" -[[cse.entry]] -name = "cavs0015" -offset = "0x540" -length = "0x0" # calculated by rimage - -[css] - -[signed_pkg] -name = "ADSP" -[[signed_pkg.module]] -name = "cavs0015.met" - -[adsp_file] -[[adsp_file.comp]] -base_offset = "0x2000" - -[fw_desc.header] -name = "ADSPFW" -load_offset = "0x30000" - -[module] -count = 29 - [[module.entry]] - name = "BRNGUP" - uuid = "61EB0CB9-34D8-4F59-A21D-04C54C21D3A4" - affinity_mask = "0x1" - instance_count = "1" - domain_types = "0" - load_type = "0" - module_type = "0" - auto_start = "0" - - [[module.entry]] - name = "BASEFW" - uuid = "383B9BE2-3518-4DB0-8891-B1470A8914F8" - affinity_mask = "3" - instance_count = "1" - domain_types = "0" - load_type = "0" - module_type = "0" - auto_start = "0" - - [[module.entry]] - name = "MIXIN" - uuid = "39656EB2-3B71-4049-8D3F-F92CD5C43C09" - affinity_mask = "0x1" - instance_count = "30" - domain_types = "0" - load_type = "0" - module_type = "1" - auto_start = "0" - sched_caps = [1, 0x00008000] - - # pin = [dir, type, sample rate, size, container, channel-cfg] - pin = [0, 0, 0xfeef, 0xc, 0x8, 0x45ff, - 1, 0, 0xfeef, 0xc, 0x8, 0x1ff, - 1, 0, 0xfeef, 0xc, 0x8, 0x1ff, - 1, 0, 0xfeef, 0xc, 0x8, 0x1ff, - 1, 0, 0xfeef, 0xc, 0x8, 0x1ff] - - # mod_cfg [PAR_0 PAR_1 PAR_2 PAR_3 IS_BYTES CPS IBS OBS MOD_FLAGS CPC OBLS] - mod_cfg = [ 0, 0, 0, 0, 296, 644000, 45, 60, 0, 0, 0, - 1, 0, 0, 0, 296, 669900, 48, 64, 0, 0, 0, - 2, 0, 0, 0, 296, 934000, 96, 128, 0, 0, 0, - 3, 0, 0, 0, 296, 1137000, 96, 128, 0, 0, 0, - 4, 0, 0, 0, 296, 1482000, 48, 64, 0, 0, 0, - 5, 0, 0, 0, 296, 1746000, 96, 128, 0, 0, 0, - 6, 0, 0, 0, 296, 2274000, 192, 256, 0, 0, 0, - 7, 0, 0, 0, 296, 2700000, 48, 64, 0, 0, 0, - 8, 0, 0, 0, 296, 2964000, 96, 128, 0, 0, 0, - 9, 0, 0, 0, 296, 3492000, 192, 256, 0, 0, 0] - - [[module.entry]] - name = "MIXOUT" - uuid = "3C56505A-24D7-418F-BDDC-C1F5A3AC2AE0" - affinity_mask = "0x1" - instance_count = "30" - domain_types = "0" - load_type = "0" - module_type = "2" - auto_start = "0" - sched_caps = [1, 0x00008000] - - # pin = [dir, type, sample rate, size, container, channel-cfg] - pin = [1, 0, 0xfeef, 0xc, 0x8, 0x45ff, - 1, 0, 0xfeef, 0xc, 0x8, 0x1ff, - 1, 0, 0xfeef, 0xc, 0x8, 0x1ff, - 1, 0, 0xfeef, 0xc, 0x8, 0x1ff, - 1, 0, 0xfeef, 0xc, 0x8, 0x1ff, - 1, 0, 0xfeef, 0xc, 0x8, 0x1ff, - 1, 0, 0xfeef, 0xc, 0x8, 0x1ff, - 1, 0, 0xfeef, 0xc, 0x8, 0x1ff, - 0, 0, 0xfeef, 0xc, 0x8, 0x1ff] - - # mod_cfg [PAR_0 PAR_1 PAR_2 PAR_3 IS_BYTES CPS IBS OBS MOD_FLAGS CPC OBLS] - mod_cfg = [0, 0, 0, 0, 520, 649600, 48, 64, 0, 0, 0, - 1, 0, 0, 0, 520, 966300, 96, 128, 0, 0, 0, - 2, 0, 0, 0, 520, 2101000, 48, 64, 0, 0, 0, - 3, 0, 0, 0, 520, 2500800, 192, 256, 0, 0, 0, - 4, 0, 0, 0, 520, 2616700, 192, 256, 0, 0, 0, - 5, 0, 0, 0, 520, 2964500, 192, 256, 0, 0, 0, - 6, 0, 0, 0, 520, 4202000, 96, 128, 0, 0, 0, - 7, 0, 0, 0, 520, 3730000, 192, 256, 0, 0, 0] - - [[module.entry]] - name = "COPIER" - uuid = "9BA00C83-CA12-4A83-943C-1FA2E82F9DDA" - affinity_mask = "0x1" - instance_count = "32" - domain_types = "0" - load_type = "0" - module_type = "3" - auto_start = "0" - sched_caps = [1, 0x00008000] - - # pin = [dir, type, sample rate, size, container, channel-cfg] - pin = [0, 0, 0xfeef, 0xf, 0xf, 0x45ff, - 1, 0, 0xfeef, 0xf, 0xf, 0x1ff, - 1, 0, 0xfeef, 0xf, 0xf, 0x1ff, - 1, 0, 0xfeef, 0xf, 0xf, 0x1ff, - 1, 0, 0xfeef, 0xf, 0xf, 0x1ff] - - # mod_cfg [PAR_0 PAR_1 PAR_2 PAR_3 IS_BYTES CPS IBS OBS MOD_FLAGS CPC OBLS] - mod_cfg = [ 0, 0, 0, 0, 280, 640100, 45, 60, 0, 0, 0, - 1, 0, 0, 0, 280, 1106300, 192, 192, 0, 0, 0, - 2, 0, 0, 0, 280, 1573000, 45, 45, 0, 0, 0, - 3, 0, 0, 0, 280, 2040600, 192, 256, 0, 0, 0, - 4, 0, 0, 0, 280, 2507500, 192, 256, 0, 0, 0, - 5, 0, 0, 0, 280, 2999000, 192, 256, 0, 0, 0, - 6, 0, 0, 0, 280, 3501000, 45, 60, 0, 0, 0, - 7, 0, 0, 0, 280, 3927000, 192, 256, 0, 0, 0, - 8, 0, 0, 0, 280, 4424000, 192, 256, 0, 0, 0, - 9, 0, 0, 0, 280, 4941000, 192, 256, 0, 0, 0] - - [[module.entry]] - name = "PEAKVOL" - uuid = "8A171323-94A3-4E1D-AFE9-FE5DBAA4C393" - affinity_mask = "0x1" - instance_count = "10" - domain_types = "0" - load_type = "0" - module_type = "4" - auto_start = "0" - sched_caps = [1, 0x00008000] - - # pin = [dir, type, sample rate, size, container, channel-cfg] - pin = [0, 0, 0xfeef, 0xf, 0xa, 0x45ff, - 1, 0, 0xfeef, 0xf, 0xa, 0x1ff] - - # mod_cfg [PAR_0 PAR_1 PAR_2 PAR_3 IS_BYTES CPS IBS OBS MOD_FLAGS CPC OBLS] - mod_cfg = [0, 0, 0, 0, 480, 1114000, 48, 64, 0, 0, 0, - 1, 0, 0, 0, 480, 3321600, 192, 256, 0, 0, 0, - 2, 0, 0, 0, 480, 3786000, 192, 256, 0, 0, 0, - 3, 0, 0, 0, 480, 4333000, 48, 64, 0, 0, 0, - 4, 0, 0, 0, 480, 4910000, 192, 256, 0, 0, 0, - 5, 0, 0, 0, 480, 5441000, 192, 256, 0, 0, 0, - 6, 0, 0, 0, 480, 6265000, 192, 256, 0, 0, 0] - - [[module.entry]] - name = "GAIN" - uuid = "61BCA9A8-18D0-4A18-8E7B-2639219804B7" - affinity_mask = "0x1" - instance_count = "40" - domain_types = "0" - load_type = "0" - module_type = "9" - auto_start = "0" - sched_caps = [1, 0x00008000] - - # pin = [dir, type, sample rate, size, container, channel-cfg] - pin = [0, 0, 0xfeef, 0xf, 0xf, 0x45ff, - 1, 0, 0xfeef, 0xf, 0xf, 0x1ff] - - # mod_cfg [PAR_0 PAR_1 PAR_2 PAR_3 IS_BYTES CPS IBS OBS MOD_FLAGS CPC OBLS] - mod_cfg = [0, 0, 0, 0, 416, 914000, 48, 64, 0, 0, 0, - 1, 0, 0, 0, 416, 1321600, 192, 256, 0, 0, 0, - 2, 0, 0, 0, 416, 1786000, 192, 256, 0, 0, 0, - 3, 0, 0, 0, 416, 2333000, 48, 64, 0, 0, 0, - 4, 0, 0, 0, 416, 2910000, 192, 256, 0, 0, 0, - 5, 0, 0, 0, 416, 3441000, 192, 256, 0, 0, 0, - 6, 0, 0, 0, 416, 4265000, 192, 256, 0, 0, 0] - - [[module.entry]] - name = "PROBE" - uuid = "7CAD0808-AB10-CD23-EF45-12AB34CD56EF" - affinity_mask = "0x1" - instance_count = "1" - domain_types = "0" - load_type = "0" - module_type = "9" - auto_start = "0" - sched_caps = [1, 0x00008000] - - # mod_cfg [PAR_0 PAR_1 PAR_2 PAR_3 IS_BYTES CPS IBS OBS MOD_FLAGS CPC OBLS] - mod_cfg = [0, 0, 0, 0, 4096, 100000, 48, 48, 0, 1000, 0] - - [[module.entry]] - name = "SRCINTC" - uuid = "e61bb28d-149a-4c1f-b709-46823ef5f5ae" - affinity_mask = "0xF" - instance_count = "10" - domain_types = "0" - load_type = "0" - module_type = "0x7" - auto_start = "0" - sched_caps = [1, 0x00008000] - - # pin = [dir, type, sample rate, size, container, channel-cfg] - pin = [0, 0, 0xffff, 0xc, 0x8, 0x45ff, - 1, 0, 0xf6c9, 0xc, 0x8, 0x45ff] - - # mod_cfg [PAR_0 PAR_1 PAR_2 PAR_3 IS_BYTES CPS IBS OBS MOD_FLAGS CPC OBLS] - mod_cfg = [0, 0, 0, 0, 12832, 1365500, 0, 0, 0, 1365, 0, - 1, 0, 0, 0, 12832, 2302300, 0, 0, 0, 2302, 0, - 2, 0, 0, 0, 12832, 3218200, 0, 0, 0, 3218, 0, - 3, 0, 0, 0, 12832, 4169700, 0, 0, 0, 4169, 0, - 4, 0, 0, 0, 12832, 5095100, 0, 0, 0, 5095, 0, - 5, 0, 0, 0, 12832, 6014800, 0, 0, 0, 6014, 0, - 6, 0, 0, 0, 12832, 6963500, 0, 0, 0, 6963, 0, - 7, 0, 0, 0, 12832, 7791000, 0, 0, 0, 7791, 0, - 8, 0, 0, 0, 12832, 8843000, 0, 0, 0, 8843, 0, - 9, 0, 0, 0, 12832, 9755100, 0, 0, 0, 9755, 0, - 10, 0, 0, 0, 12832, 10726500, 0, 0, 0, 10726, 0, - 11, 0, 0, 0, 12832, 11624100, 0, 0, 0, 11624, 0, - 12, 0, 0, 0, 12832, 12518700, 0, 0, 0, 12518, 0, - 13, 0, 0, 0, 12832, 13555000, 0, 0, 0, 13555, 0, - 14, 0, 0, 0, 12832, 14144500, 0, 0, 0, 14144, 0, - 15, 0, 0, 0, 12832, 15809800, 0, 0, 0, 15809, 0, - 16, 0, 0, 0, 12832, 16749000, 0, 0, 0, 16749, 0, - 17, 0, 0, 0, 12832, 18433500, 0, 0, 0, 18433, 0, - 18, 0, 0, 0, 12832, 19425900, 0, 0, 0, 19425, 0, - 19, 0, 0, 0, 12832, 20396900, 0, 0, 0, 20396, 0, - 20, 0, 0, 0, 12832, 20881000, 0, 0, 0, 20881, 0, - 21, 0, 0, 0, 12832, 23431000, 0, 0, 0, 23431, 0, - 22, 0, 0, 0, 12832, 30471000, 0, 0, 0, 30471, 0] - - # smart amp test module config - [[module.entry]] - name = "SMATEST" - uuid = "167A961E-8AE4-11EA-89F1-000C29CE1635" - affinity_mask = "0x1" - instance_count = "1" - domain_types = "0" - load_type = "0" - module_type = "0xD" - init_config = "1" - auto_start = "0" - sched_caps = [1, 0x00008000] - - # pin = [dir, type, sample rate, size, container, channel-cfg] - pin = [0, 0, 0xfeef, 0xf, 0xa, 0x45ff, - 0, 0, 0xfeef, 0xf, 0xa, 0x45ff, - 1, 0, 0xfeef, 0xf, 0xa, 0x45ff] - - # mod_cfg [PAR_0 PAR_1 PAR_2 PAR_3 IS_BYTES CPS IBS OBS MOD_FLAGS CPC OBLS] - mod_cfg = [0, 0, 0, 0, 4096, 1000000, 128, 128, 0, 0, 0] - - # eq iir module config - [[module.entry]] - name = "EQIIR" - uuid = "5150C0E6-27F9-4EC8-8351-C705B642D12F" - affinity_mask = "0x1" - instance_count = "40" - domain_types = "0" - load_type = "0" - module_type = "9" - auto_start = "0" - sched_caps = [1, 0x00008000] - - # pin = [dir, type, sample rate, size, container, channel-cfg] - pin = [0, 0, 0xfeef, 0xf, 0xf, 0x45ff, - 1, 0, 0xfeef, 0xf, 0xf, 0x1ff] - - # mod_cfg [PAR_0 PAR_1 PAR_2 PAR_3 IS_BYTES CPS IBS OBS MOD_FLAGS CPC OBLS] - mod_cfg = [0, 0, 0, 0, 4096, 1000000, 128, 128, 0, 0, 0] - - # eq fir module config - [[module.entry]] - name = "EQFIR" - uuid = "43A90CE7-f3A5-41Df-AC06-BA98651AE6A3" - affinity_mask = "0x1" - instance_count = "40" - domain_types = "0" - load_type = "0" - module_type = "9" - auto_start = "0" - sched_caps = [1, 0x00008000] - - # pin = [dir, type, sample rate, size, container, channel-cfg] - pin = [0, 0, 0xfeef, 0xf, 0xf, 0x45ff, - 1, 0, 0xfeef, 0xf, 0xf, 0x1ff] - - # mod_cfg [PAR_0 PAR_1 PAR_2 PAR_3 IS_BYTES CPS IBS OBS MOD_FLAGS CPC OBLS] - mod_cfg = [0, 0, 0, 0, 4096, 1000000, 128, 128, 0, 0, 0] - - [[module.entry]] - name = "KDTEST" - uuid = "EBA8D51F-7827-47B5-82EE-DE6E7743AF67" - affinity_mask = "0x1" - instance_count = "1" - domain_types = "0" - load_type = "1" - module_type = "8" - auto_start = "0" - sched_caps = [1, 0x00008000] - - # pin = [dir, type, sample rate, size, container, channel-cfg] - pin = [0, 0, 0xfeef, 0xf, 0xf, 0x45ff, - 1, 0, 0xfeef, 0xf, 0xa, 0x45ff] - - # mod_cfg [PAR_0 PAR_1 PAR_2 PAR_3 IS_BYTES CPS IBS OBS MOD_FLAGS CPC OBLS] - mod_cfg = [0, 0, 0, 0, 480, 1114000, 64, 64, 0, 0, 0] - - [[module.entry]] - name = "KPB" - uuid = "D8218443-5FF3-4A4C-B388-6CFE07B9562E" - affinity_mask = "0x1" - instance_count = "1" - domain_types = "0" - load_type = "1" - module_type = "0xB" - auto_start = "0" - sched_caps = [1, 0x00008000] - - # pin = [dir, type, sample rate, size, container, channel-cfg] - pin = [0, 0, 0xfeef, 0xf, 0xf, 0x45ff, - 1, 0, 0xfeef, 0xf, 0xa, 0x45ff] - - # mod_cfg [PAR_0 PAR_1 PAR_2 PAR_3 IS_BYTES CPS IBS OBS MOD_FLAGS CPC OBLS] - mod_cfg = [0, 0, 0, 0, 14400, 1114000, 16, 16, 0, 0, 0] - - [[module.entry]] - name = "MICSEL" - uuid = "32FE92C1-1E17-4FC2-9758-C7F3542E980A" - affinity_mask = "0x1" - instance_count = "8" - domain_types = "0" - load_type = "0" - init_config = "1" - module_type = "0xC" - auto_start = "0" - sched_caps = [1, 0x00008000] - - # pin = [dir, type, sample rate, size, container, channel-cfg] - pin = [0, 0, 0xfeef, 0xe, 0xa, 0x45ff, 1, 0, 0xfeef, 0xe, 0xa, 0x45ff] - - # mod_cfg [PAR_0 PAR_1 PAR_2 PAR_3 IS_BYTES CPS IBS OBS MOD_FLAGS CPC OBLS] - mod_cfg = [0, 0, 0, 0, 960, 488500, 16, 16, 0, 0, 0, - 1, 0, 0, 0, 960, 964500, 16, 16, 0, 0, 0, - 2, 0, 0, 0, 960, 2003000, 16, 16, 0, 0, 0] - - # Aria module config - [[module.entry]] - name = "ARIA" - uuid = "99F7166D-372C-43EF-81F6-22007AA15F03" - affinity_mask = "0x1" - instance_count = "8" - domain_types = "0" - load_type = "0" - init_config = "1" - module_type = "30" - auto_start = "0" - sched_caps = [1, 0x00008000] - - # pin = [dir, type, sample rate, size, container, channel-cfg] - pin = [0, 0, 0xfeef, 0xf, 0xa, 0x45ff, - 1, 0, 0xfeef, 0xf, 0xa, 0x45ff] - - # mod_cfg [PAR_0 PAR_1 PAR_2 PAR_3 IS_BYTES CPS IBS OBS MOD_FLAGS CPC OBLS] - mod_cfg = [0, 0, 0, 0, 260, 1063000, 16, 21, 0, 0, 0, - 1, 0, 0, 0, 260, 1873500, 192, 256, 0, 0, 0, - 2, 0, 0, 0, 260, 2680000, 32, 42, 0, 0, 0, - 3, 0, 0, 0, 260, 3591000, 64, 85, 0, 0, 0, - 4, 0, 0, 0, 260, 4477000, 96, 128, 0, 0, 0, - 5, 0, 0, 0, 260, 7195000, 192, 192, 0, 0, 0] - - # DRC module config - [[module.entry]] - name = "DRC" - uuid = "B36EE4DA-006F-47F9-A06D-FECBE2D8B6CE" - affinity_mask = "0x1" - instance_count = "40" - domain_types = "0" - load_type = "0" - module_type = "9" - auto_start = "0" - sched_caps = [1, 0x00008000] - # pin = [dir, type, sample rate, size, container, channel-cfg] - pin = [0, 0, 0xfeef, 0xf, 0xf, 0x45ff, 1, 0, 0xfeef, 0xf, 0xf, 0x1ff] - # mod_cfg [PAR_0 PAR_1 PAR_2 PAR_3 IS_BYTES CPS IBS OBS MOD_FLAGS CPC OBLS] - mod_cfg = [0, 0, 0, 0, 4096, 1000000, 128, 128, 0, 0, 0] - - # Crossover module config - # Note: Crossover has init_config set to 1 to let kernel know that the base_cfg_ext needs to - # be appended to the IPC payload. The Extension is needed to know the output pin indices. - [[module.entry]] - name = "XOVER" - uuid = "948C9AD1-806A-4131-AD6C-B2BDA9E35A9F" - affinity_mask = "0x1" - instance_count = "40" - domain_types = "0" - load_type = "0" - module_type = "9" - init_config = "1" - auto_start = "0" - sched_caps = [1, 0x00008000] - # pin = [dir, type, sample rate, size, container, channel-cfg] - pin = [0, 0, 0xfeef, 0xf, 0xf, 0x45ff, 1, 0, 0xfeef, 0xf, 0xf, 0x1ff] - # mod_cfg [PAR_0 PAR_1 PAR_2 PAR_3 IS_BYTES CPS IBS OBS MOD_FLAGS CPC OBLS] - mod_cfg = [0, 0, 0, 0, 4096, 1000000, 128, 128, 0, 0, 0] - - # Multiband-DRC module config - [[module.entry]] - name = "MB_DRC" - uuid = "0D9F2256-8E4F-47B3-8448-239A334F1191" - affinity_mask = "0x1" - instance_count = "40" - domain_types = "0" - load_type = "0" - module_type = "9" - auto_start = "0" - sched_caps = [1, 0x00008000] - # pin = [dir, type, sample rate, size, container, channel-cfg] - pin = [0, 0, 0xfeef, 0xf, 0xf, 0x45ff, 1, 0, 0xfeef, 0xf, 0xf, 0x1ff] - # mod_cfg [PAR_0 PAR_1 PAR_2 PAR_3 IS_BYTES CPS IBS OBS MOD_FLAGS CPC OBLS] - mod_cfg = [0, 0, 0, 0, 4096, 1000000, 128, 128, 0, 0, 0] - - # DCblock module config - [[module.entry]] - name = "DCBLOCK" - uuid = "B809EFAF-5681-42B1-9ED6-04BB012DD384" - affinity_mask = "0x1" - instance_count = "40" - domain_types = "0" - load_type = "0" - module_type = "9" - auto_start = "0" - sched_caps = [1, 0x00008000] - # pin = [dir, type, sample rate, size, container, channel-cfg] - pin = [0, 0, 0xfeef, 0xf, 0xf, 0x45ff, 1, 0, 0xfeef, 0xf, 0xf, 0x1ff] - # mod_cfg [PAR_0 PAR_1 PAR_2 PAR_3 IS_BYTES CPS IBS OBS MOD_FLAGS CPC OBLS] - mod_cfg = [0, 0, 0, 0, 4096, 1000000, 128, 128, 0, 0, 0] - - # TDFB module config - [[module.entry]] - name = "TDFB" - uuid = "DD511749-D9FA-455C-B3A7-13585693F1AF" - affinity_mask = "0x1" - instance_count = "40" - domain_types = "0" - load_type = "0" - init_config = "1" - module_type = "9" - auto_start = "0" - sched_caps = [1, 0x00008000] - # pin = [dir, type, sample rate, size, container, channel-cfg] - pin = [0, 0, 0xfeef, 0xf, 0xf, 0x45ff, 1, 0, 0xfeef, 0xf, 0xf, 0x1ff] - # mod_cfg [PAR_0 PAR_1 PAR_2 PAR_3 IS_BYTES CPS IBS OBS MOD_FLAGS CPC OBLS] - mod_cfg = [0, 0, 0, 0, 4096, 1000000, 128, 128, 0, 0, 0] - - [[module.entry]] - name = "RTC_AEC" - uuid = "B780A0A6-269F-466F-B477-23DFA05AF758" - affinity_mask = "0x3" - instance_count = "1" - domain_types = "0" - load_type = "1" - module_type = "10" - init_config = "1" - auto_start = "0" - sched_caps = [1, 0x00008000] - - # pin = [dir, type, sample rate, size, container, channel-cfg] - pin = [0, 0, 0x8, 0x2, 0x2, 0x1, - 0, 0, 0x8, 0x2, 0x2, 0x4, - 1, 0, 0x8, 0x2, 0x2, 0x1] - - # RTNR module config - [[module.entry]] - name = "RTNR" - uuid = "5C7CA334-E15D-11EB-BA80-0242AC130004" - affinity_mask = "0x1" - instance_count = "40" - domain_types = "0" - load_type = "0" - module_type = "9" - auto_start = "0" - sched_caps = [1, 0x00008000] - # pin = [dir, type, sample rate, size, container, channel-cfg] - pin = [0, 0, 0xfeef, 0xf, 0xf, 0x45ff, 1, 0, 0xfeef, 0xf, 0xf, 0x1ff] - # mod_cfg [PAR_0 PAR_1 PAR_2 PAR_3 IS_BYTES CPS IBS OBS MOD_FLAGS CPC OBLS] - mod_cfg = [0, 0, 0, 0, 4096, 1000000, 128, 128, 0, 0, 0] - - # IGO_NR module config - [[module.entry]] - name = "IGO_NR" - uuid = "696AE2BC-2877-11EB-ADC1-0242AC120002" - affinity_mask = "0x1" - instance_count = "40" - domain_types = "0" - load_type = "0" - module_type = "9" - auto_start = "0" - sched_caps = [1, 0x00008000] - # pin = [dir, type, sample rate, size, container, channel-cfg] - pin = [0, 0, 0xfeef, 0xf, 0xf, 0x45ff, 1, 0, 0xfeef, 0xf, 0xf, 0x1ff] - # mod_cfg [PAR_0 PAR_1 PAR_2 PAR_3 IS_BYTES CPS IBS OBS MOD_FLAGS CPC OBLS] - mod_cfg = [0, 0, 0, 0, 4096, 1000000, 128, 128, 0, 0, 0] - - # MFCC module config - [[module.entry]] - name = "MFCC" - uuid = "DB10A773-1AA4-4CEA-A21F-2D57A5C982EB" - affinity_mask = "0x1" - instance_count = "40" - domain_types = "0" - load_type = "0" - module_type = "9" - auto_start = "0" - sched_caps = [1, 0x00008000] - # pin = [dir, type, sample rate, size, container, channel-cfg] - pin = [0, 0, 0xfeef, 0xf, 0xf, 0x45ff, 1, 0, 0xfeef, 0xf, 0xf, 0x1ff] - # mod_cfg [PAR_0 PAR_1 PAR_2 PAR_3 IS_BYTES CPS IBS OBS MOD_FLAGS CPC OBLS] - mod_cfg = [0, 0, 0, 0, 4096, 1000000, 128, 128, 0, 0, 0] - - # ASRC module config - [[module.entry]] - name = "ASRC" - uuid = "66B4402D-B468-42F2-81A7-B37121863DD4" - affinity_mask = "0x3" - instance_count = "2" - domain_types = "0" - load_type = "0" - module_type = "9" - auto_start = "0" - sched_caps = [1, 0x00008000] - pin = [0, 0, 0xfeef, 0xc, 0x8, 0x45ff, 1, 0, 0xfeef, 0xc, 0x8, 0x45ff] - mod_cfg = [0, 0, 0, 0, 20480, 21808000, 64, 192, 0, 21808, 0, - 1, 0, 0, 0, 20480, 45820000, 64, 384, 0, 45820, 0, - 2, 0, 0, 0, 20480, 75236000, 512, 1440, 0, 75236, 0, - 3, 0, 0, 0, 20480, 79732000, 512, 1536, 0, 79732, 0, - 4, 0, 0, 0, 20480, 50411000, 184, 384, 0, 50411, 0, - 5, 0, 0, 0, 20480, 24236000, 192, 128, 0, 24236, 0, - 6, 0, 0, 0, 20480, 46753000, 192, 384, 0, 46753, 0, - 7, 0, 0, 0, 20480, 30032000, 256, 256, 0, 30032, 0, - 8, 0, 0, 0, 20480, 48676000, 256, 384, 0, 48676, 0, - 9, 0, 0, 0, 20480, 46548000, 360, 360, 0, 46548, 0, - 10, 0, 0, 0, 20480, 94372000, 1440, 1536, 0, 94372, 0, - 11, 0, 0, 0, 20480, 42912000, 1536, 512, 0, 42912, 0, - 12, 0, 0, 0, 20480, 31871000, 384, 192, 0, 31871, 0, - 13, 0, 0, 0, 20480, 34216000, 384, 256, 0, 34216, 0, - 14, 0, 0, 0, 20480, 83448000, 1536, 1440, 0, 83448, 0] - - # Template component module config - [[module.entry]] - name = "TEMPLATE" - uuid = "A62DE1AF-5964-4E2E-B167-7FDC97279A29" - affinity_mask = "0x1" - instance_count = "40" - domain_types = "0" - load_type = "0" - module_type = "9" - auto_start = "0" - sched_caps = [1, 0x00008000] - # pin = [dir, type, sample rate, size, container, channel-cfg] - pin = [0, 0, 0xfeef, 0xf, 0xf, 0x45ff, 1, 0, 0xfeef, 0xf, 0xf, 0x1ff] - # mod_cfg [PAR_0 PAR_1 PAR_2 PAR_3 IS_BYTES CPS IBS OBS MOD_FLAGS CPC OBLS] - mod_cfg = [0, 0, 0, 0, 4096, 1000000, 128, 128, 0, 0, 0] - - # Level multiplier module config - [[module.entry]] - name = "LVLMULT" - uuid = "30397456-4661-4644-97e5-39a9e5ab1778" - affinity_mask = "0x1" - instance_count = "40" - domain_types = "0" - load_type = "0" - module_type = "9" - auto_start = "0" - sched_caps = [1, 0x00008000] - # pin = [dir, type, sample rate, size, container, channel-cfg] - pin = [0, 0, 0xfeef, 0xf, 0xf, 0x45ff, 1, 0, 0xfeef, 0xf, 0xf, 0x1ff] - # mod_cfg [PAR_0 PAR_1 PAR_2 PAR_3 IS_BYTES CPS IBS OBS MOD_FLAGS CPC OBLS] - mod_cfg = [0, 0, 0, 0, 4096, 1000000, 128, 128, 0, 0, 0] - - # Sound Dose component module config - [[module.entry]] - name = "SNDDOSE" - uuid = "A43F9D7C-EA75-44D5-942D-967991A33809" - affinity_mask = "0x1" - instance_count = "40" - domain_types = "0" - load_type = "0" - module_type = "9" - auto_start = "0" - sched_caps = [1, 0x00008000] - # pin = [dir, type, sample rate, size, container, channel-cfg] - pin = [0, 0, 0xfeef, 0xf, 0xf, 0x45ff, 1, 0, 0xfeef, 0xf, 0xf, 0x1ff] - # mod_cfg [PAR_0 PAR_1 PAR_2 PAR_3 IS_BYTES CPS IBS OBS MOD_FLAGS CPC OBLS] - mod_cfg = [0, 0, 0, 0, 4096, 1000000, 128, 128, 0, 0, 0] diff --git a/tools/rimage/config/tgl-h.toml.h b/tools/rimage/config/tgl-h.toml.h new file mode 100644 index 000000000000..ea5dc5d4b262 --- /dev/null +++ b/tools/rimage/config/tgl-h.toml.h @@ -0,0 +1,136 @@ +#include "platform-tgl-h.toml" + + [[module.entry]] + name = "BRNGUP" + uuid = UUIDREG_STR_BRNGUP + affinity_mask = "0x1" + instance_count = "1" + domain_types = "0" + load_type = "0" + module_type = "0" + auto_start = "0" + + index = __COUNTER__ + + [[module.entry]] + name = "BASEFW" + uuid = UUIDREG_STR_BASEFW + affinity_mask = "3" + instance_count = "1" + domain_types = "0" + load_type = "0" + module_type = "0" + auto_start = "0" + + index = __COUNTER__ + +#if defined(CONFIG_COMP_MIXIN_MIXOUT) || defined(LLEXT_FORCE_ALL_MODULAR) +#include <audio/mixin_mixout/mixin_mixout.toml> +#endif + +#if defined(CONFIG_COMP_COPIER) || defined(LLEXT_FORCE_ALL_MODULAR) +#include <audio/copier/copier.toml> +#endif + +#if defined(CONFIG_COMP_VOLUME) || defined(LLEXT_FORCE_ALL_MODULAR) +#include <audio/volume/volume.toml> +#endif + +#ifdef CONFIG_PROBE +#include <probe/probe.toml> +#endif + +#if defined(CONFIG_COMP_SRC) || defined(LLEXT_FORCE_ALL_MODULAR) +#include <audio/src/src.toml> +#endif + +#if defined(CONFIG_CADENCE_CODEC) || defined(LLEXT_FORCE_ALL_MODULAR) +#include <audio/module_adapter/module/cadence.toml> +#endif + +#if defined(CONFIG_SAMPLE_SMART_AMP) || defined(LLEXT_FORCE_ALL_MODULAR) +#include <samples/audio/smart_amp_test.toml> +#endif + +#if defined(CONFIG_COMP_IIR) || defined(LLEXT_FORCE_ALL_MODULAR) +#include <audio/eq_iir/eq_iir.toml> +#endif + +#if defined(CONFIG_COMP_FIR) || defined(LLEXT_FORCE_ALL_MODULAR) +#include <audio/eq_fir/eq_fir.toml> +#endif + +#ifdef CONFIG_SAMPLE_KEYPHRASE +#include <samples/audio/detect_test.toml> +#endif + +#if defined(CONFIG_COMP_KPB) || defined(LLEXT_FORCE_ALL_MODULAR) +#include <audio/kpb.toml> +#endif + +#if defined(CONFIG_COMP_SEL) || defined(LLEXT_FORCE_ALL_MODULAR) +#include <audio/selector/selector.toml> +#endif + +#if defined(CONFIG_COMP_ARIA) || defined(LLEXT_FORCE_ALL_MODULAR) +#include <audio/aria/aria.toml> +#endif + +#if defined(CONFIG_COMP_DRC) || defined(LLEXT_FORCE_ALL_MODULAR) +#include <audio/drc/drc.toml> +#endif + +#if defined(CONFIG_COMP_CROSSOVER) || defined(LLEXT_FORCE_ALL_MODULAR) +#include <audio/crossover/crossover.toml> +#endif + +#if defined(CONFIG_COMP_MULTIBAND_DRC) || defined(LLEXT_FORCE_ALL_MODULAR) +#include <audio/multiband_drc/multiband_drc.toml> +#endif + +#if defined(CONFIG_COMP_DCBLOCK) || defined(LLEXT_FORCE_ALL_MODULAR) +#include <audio/dcblock/dcblock.toml> +#endif + +#if defined(CONFIG_COMP_TDFB) || defined(LLEXT_FORCE_ALL_MODULAR) +#include <audio/tdfb/tdfb.toml> +#endif + +#if defined(CONFIG_COMP_GOOGLE_RTC_AUDIO_PROCESSING) || defined(LLEXT_FORCE_ALL_MODULAR) +#include <audio/google/google_rtc_audio_processing.toml> +#endif + +#if defined(CONFIG_COMP_RTNR) || defined(LLEXT_FORCE_ALL_MODULAR) +#include <audio/rtnr/rtnr.toml> +#endif + +#if defined(CONFIG_COMP_IGO_NR) || defined(LLEXT_FORCE_ALL_MODULAR) +#include <audio/igo_nr/igo_nr.toml> +#endif + +#if defined(CONFIG_COMP_MFCC) || defined(LLEXT_FORCE_ALL_MODULAR) +#include <audio/mfcc/mfcc.toml> +#endif + +#if defined(CONFIG_COMP_ASRC) || defined(LLEXT_FORCE_ALL_MODULAR) +#include <audio/asrc/asrc.toml> +#endif + +#if defined(CONFIG_COMP_TEMPLATE) || defined(LLEXT_FORCE_ALL_MODULAR) +#include <audio/template/template.toml> +#endif + +#if defined(CONFIG_COMP_LEVEL_MULTIPLIER) || defined(LLEXT_FORCE_ALL_MODULAR) +#include <audio/level_multiplier/level_multiplier.toml> +#endif + +#if defined(CONFIG_COMP_SOUND_DOSE) || defined(LLEXT_FORCE_ALL_MODULAR) +#include <audio/sound_dose/sound_dose.toml> +#endif + +#if defined(CONFIG_COMP_STFT_PROCESS) || defined(LLEXT_FORCE_ALL_MODULAR) +#include <audio/stft_process/stft_process.toml> +#endif + +[module] +count = __COUNTER__ diff --git a/tools/rimage/config/tgl.toml b/tools/rimage/config/tgl.toml deleted file mode 100644 index 14526675a7a0..000000000000 --- a/tools/rimage/config/tgl.toml +++ /dev/null @@ -1,641 +0,0 @@ -version = [2, 5] - -[adsp] -name = "tgl" -image_size = "0x2F0000" -alias_mask = "0xE0000000" - -[[adsp.mem_zone]] -type = "ROM" -base = "0x9F180000" -size = "0x00002000" -[[adsp.mem_zone]] -type = "IMR" -base = "0xB0000000" -size = "0x1000000" -[[adsp.mem_zone]] -type = "HP-SRAM" -base = "0xBE000000" -size = "0x800000" -[[adsp.mem_zone]] -type = "LP-SRAM" -base = "0xBE800000" -size = "0x40" - -[[adsp.mem_alias]] -type = "uncached" -base = "0x9E000000" -[[adsp.mem_alias]] -type = "cached" -base = "0xBE000000" - -[cse] -partition_name = "ADSP" -[[cse.entry]] -name = "ADSP.man" -offset = "0x5c" -length = "0x464" -[[cse.entry]] -name = "cavs0015.met" -offset = "0x4c0" -length = "0x70" -[[cse.entry]] -name = "cavs0015" -offset = "0x540" -length = "0x0" # calculated by rimage - -[css] - -[signed_pkg] -name = "ADSP" -[[signed_pkg.module]] -name = "cavs0015.met" - -[adsp_file] -[[adsp_file.comp]] -base_offset = "0x2000" - -[fw_desc.header] -name = "ADSPFW" -load_offset = "0x30000" - -[module] -count = 29 - [[module.entry]] - name = "BRNGUP" - uuid = "61EB0CB9-34D8-4F59-A21D-04C54C21D3A4" - affinity_mask = "0x1" - instance_count = "1" - domain_types = "0" - load_type = "0" - module_type = "0" - auto_start = "0" - - [[module.entry]] - name = "BASEFW" - uuid = "383B9BE2-3518-4DB0-8891-B1470A8914F8" - affinity_mask = "3" - instance_count = "1" - domain_types = "0" - load_type = "0" - module_type = "0" - auto_start = "0" - - [[module.entry]] - name = "MIXIN" - uuid = "39656EB2-3B71-4049-8D3F-F92CD5C43C09" - affinity_mask = "0x1" - instance_count = "30" - domain_types = "0" - load_type = "0" - module_type = "1" - auto_start = "0" - sched_caps = [1, 0x00008000] - - # pin = [dir, type, sample rate, size, container, channel-cfg] - pin = [0, 0, 0xfeef, 0xc, 0x8, 0x45ff, - 1, 0, 0xfeef, 0xc, 0x8, 0x1ff, - 1, 0, 0xfeef, 0xc, 0x8, 0x1ff, - 1, 0, 0xfeef, 0xc, 0x8, 0x1ff, - 1, 0, 0xfeef, 0xc, 0x8, 0x1ff] - - # mod_cfg [PAR_0 PAR_1 PAR_2 PAR_3 IS_BYTES CPS IBS OBS MOD_FLAGS CPC OBLS] - mod_cfg = [ 0, 0, 0, 0, 296, 644000, 45, 60, 0, 0, 0, - 1, 0, 0, 0, 296, 669900, 48, 64, 0, 0, 0, - 2, 0, 0, 0, 296, 934000, 96, 128, 0, 0, 0, - 3, 0, 0, 0, 296, 1137000, 96, 128, 0, 0, 0, - 4, 0, 0, 0, 296, 1482000, 48, 64, 0, 0, 0, - 5, 0, 0, 0, 296, 1746000, 96, 128, 0, 0, 0, - 6, 0, 0, 0, 296, 2274000, 192, 256, 0, 0, 0, - 7, 0, 0, 0, 296, 2700000, 48, 64, 0, 0, 0, - 8, 0, 0, 0, 296, 2964000, 96, 128, 0, 0, 0, - 9, 0, 0, 0, 296, 3492000, 192, 256, 0, 0, 0] - - [[module.entry]] - name = "MIXOUT" - uuid = "3C56505A-24D7-418F-BDDC-C1F5A3AC2AE0" - affinity_mask = "0x1" - instance_count = "30" - domain_types = "0" - load_type = "0" - module_type = "2" - auto_start = "0" - sched_caps = [1, 0x00008000] - - # pin = [dir, type, sample rate, size, container, channel-cfg] - pin = [1, 0, 0xfeef, 0xc, 0x8, 0x45ff, - 1, 0, 0xfeef, 0xc, 0x8, 0x1ff, - 1, 0, 0xfeef, 0xc, 0x8, 0x1ff, - 1, 0, 0xfeef, 0xc, 0x8, 0x1ff, - 1, 0, 0xfeef, 0xc, 0x8, 0x1ff, - 1, 0, 0xfeef, 0xc, 0x8, 0x1ff, - 1, 0, 0xfeef, 0xc, 0x8, 0x1ff, - 1, 0, 0xfeef, 0xc, 0x8, 0x1ff, - 0, 0, 0xfeef, 0xc, 0x8, 0x1ff] - - # mod_cfg [PAR_0 PAR_1 PAR_2 PAR_3 IS_BYTES CPS IBS OBS MOD_FLAGS CPC OBLS] - mod_cfg = [0, 0, 0, 0, 520, 649600, 48, 64, 0, 0, 0, - 1, 0, 0, 0, 520, 966300, 96, 128, 0, 0, 0, - 2, 0, 0, 0, 520, 2101000, 48, 64, 0, 0, 0, - 3, 0, 0, 0, 520, 2500800, 192, 256, 0, 0, 0, - 4, 0, 0, 0, 520, 2616700, 192, 256, 0, 0, 0, - 5, 0, 0, 0, 520, 2964500, 192, 256, 0, 0, 0, - 6, 0, 0, 0, 520, 4202000, 96, 128, 0, 0, 0, - 7, 0, 0, 0, 520, 3730000, 192, 256, 0, 0, 0] - - [[module.entry]] - name = "COPIER" - uuid = "9BA00C83-CA12-4A83-943C-1FA2E82F9DDA" - affinity_mask = "0x1" - instance_count = "32" - domain_types = "0" - load_type = "0" - module_type = "3" - auto_start = "0" - sched_caps = [1, 0x00008000] - - # pin = [dir, type, sample rate, size, container, channel-cfg] - pin = [0, 0, 0xfeef, 0xf, 0xf, 0x45ff, - 1, 0, 0xfeef, 0xf, 0xf, 0x1ff, - 1, 0, 0xfeef, 0xf, 0xf, 0x1ff, - 1, 0, 0xfeef, 0xf, 0xf, 0x1ff, - 1, 0, 0xfeef, 0xf, 0xf, 0x1ff] - - # mod_cfg [PAR_0 PAR_1 PAR_2 PAR_3 IS_BYTES CPS IBS OBS MOD_FLAGS CPC OBLS] - mod_cfg = [ 0, 0, 0, 0, 280, 640100, 45, 60, 0, 0, 0, - 1, 0, 0, 0, 280, 1106300, 192, 192, 0, 0, 0, - 2, 0, 0, 0, 280, 1573000, 45, 45, 0, 0, 0, - 3, 0, 0, 0, 280, 2040600, 192, 256, 0, 0, 0, - 4, 0, 0, 0, 280, 2507500, 192, 256, 0, 0, 0, - 5, 0, 0, 0, 280, 2999000, 192, 256, 0, 0, 0, - 6, 0, 0, 0, 280, 3501000, 45, 60, 0, 0, 0, - 7, 0, 0, 0, 280, 3927000, 192, 256, 0, 0, 0, - 8, 0, 0, 0, 280, 4424000, 192, 256, 0, 0, 0, - 9, 0, 0, 0, 280, 4941000, 192, 256, 0, 0, 0] - - [[module.entry]] - name = "PEAKVOL" - uuid = "8A171323-94A3-4E1D-AFE9-FE5DBAA4C393" - affinity_mask = "0x1" - instance_count = "10" - domain_types = "0" - load_type = "0" - module_type = "4" - auto_start = "0" - sched_caps = [1, 0x00008000] - - # pin = [dir, type, sample rate, size, container, channel-cfg] - pin = [0, 0, 0xfeef, 0xf, 0xa, 0x45ff, - 1, 0, 0xfeef, 0xf, 0xa, 0x1ff] - - # mod_cfg [PAR_0 PAR_1 PAR_2 PAR_3 IS_BYTES CPS IBS OBS MOD_FLAGS CPC OBLS] - mod_cfg = [0, 0, 0, 0, 480, 1114000, 48, 64, 0, 0, 0, - 1, 0, 0, 0, 480, 3321600, 192, 256, 0, 0, 0, - 2, 0, 0, 0, 480, 3786000, 192, 256, 0, 0, 0, - 3, 0, 0, 0, 480, 4333000, 48, 64, 0, 0, 0, - 4, 0, 0, 0, 480, 4910000, 192, 256, 0, 0, 0, - 5, 0, 0, 0, 480, 5441000, 192, 256, 0, 0, 0, - 6, 0, 0, 0, 480, 6265000, 192, 256, 0, 0, 0] - - [[module.entry]] - name = "GAIN" - uuid = "61BCA9A8-18D0-4A18-8E7B-2639219804B7" - affinity_mask = "0x1" - instance_count = "40" - domain_types = "0" - load_type = "0" - module_type = "9" - auto_start = "0" - sched_caps = [1, 0x00008000] - - # pin = [dir, type, sample rate, size, container, channel-cfg] - pin = [0, 0, 0xfeef, 0xf, 0xf, 0x45ff, - 1, 0, 0xfeef, 0xf, 0xf, 0x1ff] - - # mod_cfg [PAR_0 PAR_1 PAR_2 PAR_3 IS_BYTES CPS IBS OBS MOD_FLAGS CPC OBLS] - mod_cfg = [0, 0, 0, 0, 416, 914000, 48, 64, 0, 0, 0, - 1, 0, 0, 0, 416, 1321600, 192, 256, 0, 0, 0, - 2, 0, 0, 0, 416, 1786000, 192, 256, 0, 0, 0, - 3, 0, 0, 0, 416, 2333000, 48, 64, 0, 0, 0, - 4, 0, 0, 0, 416, 2910000, 192, 256, 0, 0, 0, - 5, 0, 0, 0, 416, 3441000, 192, 256, 0, 0, 0, - 6, 0, 0, 0, 416, 4265000, 192, 256, 0, 0, 0] - - [[module.entry]] - name = "PROBE" - uuid = "7CAD0808-AB10-CD23-EF45-12AB34CD56EF" - affinity_mask = "0x1" - instance_count = "1" - domain_types = "0" - load_type = "0" - module_type = "9" - auto_start = "0" - sched_caps = [1, 0x00008000] - - # mod_cfg [PAR_0 PAR_1 PAR_2 PAR_3 IS_BYTES CPS IBS OBS MOD_FLAGS CPC OBLS] - mod_cfg = [0, 0, 0, 0, 4096, 100000, 48, 48, 0, 1000, 0] - - [[module.entry]] - name = "SRCINTC" - uuid = "e61bb28d-149a-4c1f-b709-46823ef5f5ae" - affinity_mask = "0xF" - instance_count = "10" - domain_types = "0" - load_type = "0" - module_type = "0x7" - auto_start = "0" - sched_caps = [1, 0x00008000] - - # pin = [dir, type, sample rate, size, container, channel-cfg] - pin = [0, 0, 0xffff, 0xc, 0x8, 0x45ff, - 1, 0, 0xf6c9, 0xc, 0x8, 0x45ff] - - # mod_cfg [PAR_0 PAR_1 PAR_2 PAR_3 IS_BYTES CPS IBS OBS MOD_FLAGS CPC OBLS] - mod_cfg = [0, 0, 0, 0, 12832, 1365500, 0, 0, 0, 1365, 0, - 1, 0, 0, 0, 12832, 2302300, 0, 0, 0, 2302, 0, - 2, 0, 0, 0, 12832, 3218200, 0, 0, 0, 3218, 0, - 3, 0, 0, 0, 12832, 4169700, 0, 0, 0, 4169, 0, - 4, 0, 0, 0, 12832, 5095100, 0, 0, 0, 5095, 0, - 5, 0, 0, 0, 12832, 6014800, 0, 0, 0, 6014, 0, - 6, 0, 0, 0, 12832, 6963500, 0, 0, 0, 6963, 0, - 7, 0, 0, 0, 12832, 7791000, 0, 0, 0, 7791, 0, - 8, 0, 0, 0, 12832, 8843000, 0, 0, 0, 8843, 0, - 9, 0, 0, 0, 12832, 9755100, 0, 0, 0, 9755, 0, - 10, 0, 0, 0, 12832, 10726500, 0, 0, 0, 10726, 0, - 11, 0, 0, 0, 12832, 11624100, 0, 0, 0, 11624, 0, - 12, 0, 0, 0, 12832, 12518700, 0, 0, 0, 12518, 0, - 13, 0, 0, 0, 12832, 13555000, 0, 0, 0, 13555, 0, - 14, 0, 0, 0, 12832, 14144500, 0, 0, 0, 14144, 0, - 15, 0, 0, 0, 12832, 15809800, 0, 0, 0, 15809, 0, - 16, 0, 0, 0, 12832, 16749000, 0, 0, 0, 16749, 0, - 17, 0, 0, 0, 12832, 18433500, 0, 0, 0, 18433, 0, - 18, 0, 0, 0, 12832, 19425900, 0, 0, 0, 19425, 0, - 19, 0, 0, 0, 12832, 20396900, 0, 0, 0, 20396, 0, - 20, 0, 0, 0, 12832, 20881000, 0, 0, 0, 20881, 0, - 21, 0, 0, 0, 12832, 23431000, 0, 0, 0, 23431, 0, - 22, 0, 0, 0, 12832, 30471000, 0, 0, 0, 30471, 0] - - # smart amp test module config - [[module.entry]] - name = "SMATEST" - uuid = "167A961E-8AE4-11EA-89F1-000C29CE1635" - affinity_mask = "0x1" - instance_count = "1" - domain_types = "0" - load_type = "0" - module_type = "0xD" - init_config = "1" - auto_start = "0" - sched_caps = [1, 0x00008000] - - # pin = [dir, type, sample rate, size, container, channel-cfg] - pin = [0, 0, 0xfeef, 0xf, 0xa, 0x45ff, - 0, 0, 0xfeef, 0xf, 0xa, 0x45ff, - 1, 0, 0xfeef, 0xf, 0xa, 0x45ff] - - # mod_cfg [PAR_0 PAR_1 PAR_2 PAR_3 IS_BYTES CPS IBS OBS MOD_FLAGS CPC OBLS] - mod_cfg = [0, 0, 0, 0, 4096, 1000000, 128, 128, 0, 0, 0] - - # eq iir module config - [[module.entry]] - name = "EQIIR" - uuid = "5150C0E6-27F9-4EC8-8351-C705B642D12F" - affinity_mask = "0x1" - instance_count = "40" - domain_types = "0" - load_type = "0" - module_type = "9" - auto_start = "0" - sched_caps = [1, 0x00008000] - - # pin = [dir, type, sample rate, size, container, channel-cfg] - pin = [0, 0, 0xfeef, 0xf, 0xf, 0x45ff, - 1, 0, 0xfeef, 0xf, 0xf, 0x1ff] - - # mod_cfg [PAR_0 PAR_1 PAR_2 PAR_3 IS_BYTES CPS IBS OBS MOD_FLAGS CPC OBLS] - mod_cfg = [0, 0, 0, 0, 4096, 1000000, 128, 128, 0, 0, 0] - - # eq fir module config - [[module.entry]] - name = "EQFIR" - uuid = "43A90CE7-f3A5-41Df-AC06-BA98651AE6A3" - affinity_mask = "0x1" - instance_count = "40" - domain_types = "0" - load_type = "0" - module_type = "9" - auto_start = "0" - sched_caps = [1, 0x00008000] - - # pin = [dir, type, sample rate, size, container, channel-cfg] - pin = [0, 0, 0xfeef, 0xf, 0xf, 0x45ff, - 1, 0, 0xfeef, 0xf, 0xf, 0x1ff] - - # mod_cfg [PAR_0 PAR_1 PAR_2 PAR_3 IS_BYTES CPS IBS OBS MOD_FLAGS CPC OBLS] - mod_cfg = [0, 0, 0, 0, 4096, 1000000, 128, 128, 0, 0, 0] - - [[module.entry]] - name = "KDTEST" - uuid = "EBA8D51F-7827-47B5-82EE-DE6E7743AF67" - affinity_mask = "0x1" - instance_count = "1" - domain_types = "0" - load_type = "1" - module_type = "8" - auto_start = "0" - sched_caps = [1, 0x00008000] - - # pin = [dir, type, sample rate, size, container, channel-cfg] - pin = [0, 0, 0xfeef, 0xf, 0xf, 0x45ff, - 1, 0, 0xfeef, 0xf, 0xa, 0x45ff] - - # mod_cfg [PAR_0 PAR_1 PAR_2 PAR_3 IS_BYTES CPS IBS OBS MOD_FLAGS CPC OBLS] - mod_cfg = [0, 0, 0, 0, 480, 1114000, 64, 64, 0, 0, 0] - - [[module.entry]] - name = "KPB" - uuid = "D8218443-5FF3-4A4C-B388-6CFE07B9562E" - affinity_mask = "0x1" - instance_count = "1" - domain_types = "0" - load_type = "1" - module_type = "0xB" - auto_start = "0" - sched_caps = [1, 0x00008000] - - # pin = [dir, type, sample rate, size, container, channel-cfg] - pin = [0, 0, 0xfeef, 0xf, 0xf, 0x45ff, - 1, 0, 0xfeef, 0xf, 0xa, 0x45ff] - - # mod_cfg [PAR_0 PAR_1 PAR_2 PAR_3 IS_BYTES CPS IBS OBS MOD_FLAGS CPC OBLS] - mod_cfg = [0, 0, 0, 0, 14400, 1114000, 16, 16, 0, 0, 0] - - [[module.entry]] - name = "MICSEL" - uuid = "32FE92C1-1E17-4FC2-9758-C7F3542E980A" - affinity_mask = "0x1" - instance_count = "8" - domain_types = "0" - load_type = "0" - init_config = "1" - module_type = "0xC" - auto_start = "0" - sched_caps = [1, 0x00008000] - - # pin = [dir, type, sample rate, size, container, channel-cfg] - pin = [0, 0, 0xfeef, 0xe, 0xa, 0x45ff, 1, 0, 0xfeef, 0xe, 0xa, 0x45ff] - - # mod_cfg [PAR_0 PAR_1 PAR_2 PAR_3 IS_BYTES CPS IBS OBS MOD_FLAGS CPC OBLS] - mod_cfg = [0, 0, 0, 0, 960, 488500, 16, 16, 0, 0, 0, - 1, 0, 0, 0, 960, 964500, 16, 16, 0, 0, 0, - 2, 0, 0, 0, 960, 2003000, 16, 16, 0, 0, 0] - - # Aria module config - [[module.entry]] - name = "ARIA" - uuid = "99F7166D-372C-43EF-81F6-22007AA15F03" - affinity_mask = "0x1" - instance_count = "8" - domain_types = "0" - load_type = "0" - init_config = "1" - module_type = "30" - auto_start = "0" - sched_caps = [1, 0x00008000] - - # pin = [dir, type, sample rate, size, container, channel-cfg] - pin = [0, 0, 0xfeef, 0xf, 0xa, 0x45ff, - 1, 0, 0xfeef, 0xf, 0xa, 0x45ff] - - # mod_cfg [PAR_0 PAR_1 PAR_2 PAR_3 IS_BYTES CPS IBS OBS MOD_FLAGS CPC OBLS] - mod_cfg = [0, 0, 0, 0, 260, 1063000, 16, 21, 0, 0, 0, - 1, 0, 0, 0, 260, 1873500, 192, 256, 0, 0, 0, - 2, 0, 0, 0, 260, 2680000, 32, 42, 0, 0, 0, - 3, 0, 0, 0, 260, 3591000, 64, 85, 0, 0, 0, - 4, 0, 0, 0, 260, 4477000, 96, 128, 0, 0, 0, - 5, 0, 0, 0, 260, 7195000, 192, 192, 0, 0, 0] - - # DRC module config - [[module.entry]] - name = "DRC" - uuid = "B36EE4DA-006F-47F9-A06D-FECBE2D8B6CE" - affinity_mask = "0x1" - instance_count = "40" - domain_types = "0" - load_type = "0" - module_type = "9" - auto_start = "0" - sched_caps = [1, 0x00008000] - # pin = [dir, type, sample rate, size, container, channel-cfg] - pin = [0, 0, 0xfeef, 0xf, 0xf, 0x45ff, 1, 0, 0xfeef, 0xf, 0xf, 0x1ff] - # mod_cfg [PAR_0 PAR_1 PAR_2 PAR_3 IS_BYTES CPS IBS OBS MOD_FLAGS CPC OBLS] - mod_cfg = [0, 0, 0, 0, 4096, 1000000, 128, 128, 0, 0, 0] - - # Crossover module config - # Note: Crossover has init_config set to 1 to let kernel know that the base_cfg_ext needs to - # be appended to the IPC payload. The Extension is needed to know the output pin indices. - [[module.entry]] - name = "XOVER" - uuid = "948C9AD1-806A-4131-AD6C-B2BDA9E35A9F" - affinity_mask = "0x1" - instance_count = "40" - domain_types = "0" - load_type = "0" - module_type = "9" - init_config = "1" - auto_start = "0" - sched_caps = [1, 0x00008000] - # pin = [dir, type, sample rate, size, container, channel-cfg] - pin = [0, 0, 0xfeef, 0xf, 0xf, 0x45ff, 1, 0, 0xfeef, 0xf, 0xf, 0x1ff] - # mod_cfg [PAR_0 PAR_1 PAR_2 PAR_3 IS_BYTES CPS IBS OBS MOD_FLAGS CPC OBLS] - mod_cfg = [0, 0, 0, 0, 4096, 1000000, 128, 128, 0, 0, 0] - - # Multiband-DRC module config - [[module.entry]] - name = "MB_DRC" - uuid = "0D9F2256-8E4F-47B3-8448-239A334F1191" - affinity_mask = "0x1" - instance_count = "40" - domain_types = "0" - load_type = "0" - module_type = "9" - auto_start = "0" - sched_caps = [1, 0x00008000] - # pin = [dir, type, sample rate, size, container, channel-cfg] - pin = [0, 0, 0xfeef, 0xf, 0xf, 0x45ff, 1, 0, 0xfeef, 0xf, 0xf, 0x1ff] - # mod_cfg [PAR_0 PAR_1 PAR_2 PAR_3 IS_BYTES CPS IBS OBS MOD_FLAGS CPC OBLS] - mod_cfg = [0, 0, 0, 0, 4096, 1000000, 128, 128, 0, 0, 0] - - # DCblock module config - [[module.entry]] - name = "DCBLOCK" - uuid = "B809EFAF-5681-42B1-9ED6-04BB012DD384" - affinity_mask = "0x1" - instance_count = "40" - domain_types = "0" - load_type = "0" - module_type = "9" - auto_start = "0" - sched_caps = [1, 0x00008000] - # pin = [dir, type, sample rate, size, container, channel-cfg] - pin = [0, 0, 0xfeef, 0xf, 0xf, 0x45ff, 1, 0, 0xfeef, 0xf, 0xf, 0x1ff] - # mod_cfg [PAR_0 PAR_1 PAR_2 PAR_3 IS_BYTES CPS IBS OBS MOD_FLAGS CPC OBLS] - mod_cfg = [0, 0, 0, 0, 4096, 1000000, 128, 128, 0, 0, 0] - - # TDFB module config - [[module.entry]] - name = "TDFB" - uuid = "DD511749-D9FA-455C-B3A7-13585693F1AF" - affinity_mask = "0x1" - instance_count = "40" - domain_types = "0" - load_type = "0" - init_config = "1" - module_type = "9" - auto_start = "0" - sched_caps = [1, 0x00008000] - # pin = [dir, type, sample rate, size, container, channel-cfg] - pin = [0, 0, 0xfeef, 0xf, 0xf, 0x45ff, 1, 0, 0xfeef, 0xf, 0xf, 0x1ff] - # mod_cfg [PAR_0 PAR_1 PAR_2 PAR_3 IS_BYTES CPS IBS OBS MOD_FLAGS CPC OBLS] - mod_cfg = [0, 0, 0, 0, 4096, 1000000, 128, 128, 0, 0, 0] - - [[module.entry]] - name = "RTC_AEC" - uuid = "B780A0A6-269F-466F-B477-23DFA05AF758" - affinity_mask = "0x3" - instance_count = "1" - domain_types = "0" - load_type = "1" - module_type = "10" - init_config = "1" - auto_start = "0" - sched_caps = [1, 0x00008000] - - # pin = [dir, type, sample rate, size, container, channel-cfg] - pin = [0, 0, 0x8, 0x2, 0x2, 0x1, - 0, 0, 0x8, 0x2, 0x2, 0x4, - 1, 0, 0x8, 0x2, 0x2, 0x1] - - # RTNR module config - [[module.entry]] - name = "RTNR" - uuid = "5C7CA334-E15D-11EB-BA80-0242AC130004" - affinity_mask = "0x1" - instance_count = "40" - domain_types = "0" - load_type = "0" - module_type = "9" - auto_start = "0" - sched_caps = [1, 0x00008000] - # pin = [dir, type, sample rate, size, container, channel-cfg] - pin = [0, 0, 0xfeef, 0xf, 0xf, 0x45ff, 1, 0, 0xfeef, 0xf, 0xf, 0x1ff] - # mod_cfg [PAR_0 PAR_1 PAR_2 PAR_3 IS_BYTES CPS IBS OBS MOD_FLAGS CPC OBLS] - mod_cfg = [0, 0, 0, 0, 4096, 1000000, 128, 128, 0, 0, 0] - - # IGO_NR module config - [[module.entry]] - name = "IGO_NR" - uuid = "696AE2BC-2877-11EB-ADC1-0242AC120002" - affinity_mask = "0x1" - instance_count = "40" - domain_types = "0" - load_type = "0" - module_type = "9" - auto_start = "0" - sched_caps = [1, 0x00008000] - # pin = [dir, type, sample rate, size, container, channel-cfg] - pin = [0, 0, 0xfeef, 0xf, 0xf, 0x45ff, 1, 0, 0xfeef, 0xf, 0xf, 0x1ff] - # mod_cfg [PAR_0 PAR_1 PAR_2 PAR_3 IS_BYTES CPS IBS OBS MOD_FLAGS CPC OBLS] - mod_cfg = [0, 0, 0, 0, 4096, 1000000, 128, 128, 0, 0, 0] - - # MFCC module config - [[module.entry]] - name = "MFCC" - uuid = "DB10A773-1AA4-4CEA-A21F-2D57A5C982EB" - affinity_mask = "0x1" - instance_count = "40" - domain_types = "0" - load_type = "0" - module_type = "9" - auto_start = "0" - sched_caps = [1, 0x00008000] - # pin = [dir, type, sample rate, size, container, channel-cfg] - pin = [0, 0, 0xfeef, 0xf, 0xf, 0x45ff, 1, 0, 0xfeef, 0xf, 0xf, 0x1ff] - # mod_cfg [PAR_0 PAR_1 PAR_2 PAR_3 IS_BYTES CPS IBS OBS MOD_FLAGS CPC OBLS] - mod_cfg = [0, 0, 0, 0, 4096, 1000000, 128, 128, 0, 0, 0] - - # ASRC module config - [[module.entry]] - name = "ASRC" - uuid = "66B4402D-B468-42F2-81A7-B37121863DD4" - affinity_mask = "0x3" - instance_count = "2" - domain_types = "0" - load_type = "0" - module_type = "9" - auto_start = "0" - sched_caps = [1, 0x00008000] - pin = [0, 0, 0xfeef, 0xc, 0x8, 0x45ff, 1, 0, 0xfeef, 0xc, 0x8, 0x45ff] - mod_cfg = [0, 0, 0, 0, 20480, 21808000, 64, 192, 0, 21808, 0, - 1, 0, 0, 0, 20480, 45820000, 64, 384, 0, 45820, 0, - 2, 0, 0, 0, 20480, 75236000, 512, 1440, 0, 75236, 0, - 3, 0, 0, 0, 20480, 79732000, 512, 1536, 0, 79732, 0, - 4, 0, 0, 0, 20480, 50411000, 184, 384, 0, 50411, 0, - 5, 0, 0, 0, 20480, 24236000, 192, 128, 0, 24236, 0, - 6, 0, 0, 0, 20480, 46753000, 192, 384, 0, 46753, 0, - 7, 0, 0, 0, 20480, 30032000, 256, 256, 0, 30032, 0, - 8, 0, 0, 0, 20480, 48676000, 256, 384, 0, 48676, 0, - 9, 0, 0, 0, 20480, 46548000, 360, 360, 0, 46548, 0, - 10, 0, 0, 0, 20480, 94372000, 1440, 1536, 0, 94372, 0, - 11, 0, 0, 0, 20480, 42912000, 1536, 512, 0, 42912, 0, - 12, 0, 0, 0, 20480, 31871000, 384, 192, 0, 31871, 0, - 13, 0, 0, 0, 20480, 34216000, 384, 256, 0, 34216, 0, - 14, 0, 0, 0, 20480, 83448000, 1536, 1440, 0, 83448, 0] - - # Template component module config - [[module.entry]] - name = "TEMPLATE" - uuid = "A62DE1AF-5964-4E2E-B167-7FDC97279A29" - affinity_mask = "0x1" - instance_count = "40" - domain_types = "0" - load_type = "0" - module_type = "9" - auto_start = "0" - sched_caps = [1, 0x00008000] - # pin = [dir, type, sample rate, size, container, channel-cfg] - pin = [0, 0, 0xfeef, 0xf, 0xf, 0x45ff, 1, 0, 0xfeef, 0xf, 0xf, 0x1ff] - # mod_cfg [PAR_0 PAR_1 PAR_2 PAR_3 IS_BYTES CPS IBS OBS MOD_FLAGS CPC OBLS] - mod_cfg = [0, 0, 0, 0, 4096, 1000000, 128, 128, 0, 0, 0] - - # Level multiplier module config - [[module.entry]] - name = "LVLMULT" - uuid = "30397456-4661-4644-97e5-39a9e5ab1778" - affinity_mask = "0x1" - instance_count = "40" - domain_types = "0" - load_type = "0" - module_type = "9" - auto_start = "0" - sched_caps = [1, 0x00008000] - # pin = [dir, type, sample rate, size, container, channel-cfg] - pin = [0, 0, 0xfeef, 0xf, 0xf, 0x45ff, 1, 0, 0xfeef, 0xf, 0xf, 0x1ff] - # mod_cfg [PAR_0 PAR_1 PAR_2 PAR_3 IS_BYTES CPS IBS OBS MOD_FLAGS CPC OBLS] - mod_cfg = [0, 0, 0, 0, 4096, 1000000, 128, 128, 0, 0, 0] - - # Sound Dose component module config - [[module.entry]] - name = "SNDDOSE" - uuid = "A43F9D7C-EA75-44D5-942D-967991A33809" - affinity_mask = "0x1" - instance_count = "40" - domain_types = "0" - load_type = "0" - module_type = "9" - auto_start = "0" - sched_caps = [1, 0x00008000] - # pin = [dir, type, sample rate, size, container, channel-cfg] - pin = [0, 0, 0xfeef, 0xf, 0xf, 0x45ff, 1, 0, 0xfeef, 0xf, 0xf, 0x1ff] - # mod_cfg [PAR_0 PAR_1 PAR_2 PAR_3 IS_BYTES CPS IBS OBS MOD_FLAGS CPC OBLS] - mod_cfg = [0, 0, 0, 0, 4096, 1000000, 128, 128, 0, 0, 0] diff --git a/tools/rimage/config/tgl.toml.h b/tools/rimage/config/tgl.toml.h new file mode 100644 index 000000000000..2ca246880727 --- /dev/null +++ b/tools/rimage/config/tgl.toml.h @@ -0,0 +1,136 @@ +#include "platform-tgl.toml" + + [[module.entry]] + name = "BRNGUP" + uuid = UUIDREG_STR_BRNGUP + affinity_mask = "0x1" + instance_count = "1" + domain_types = "0" + load_type = "0" + module_type = "0" + auto_start = "0" + + index = __COUNTER__ + + [[module.entry]] + name = "BASEFW" + uuid = UUIDREG_STR_BASEFW + affinity_mask = "3" + instance_count = "1" + domain_types = "0" + load_type = "0" + module_type = "0" + auto_start = "0" + + index = __COUNTER__ + +#if defined(CONFIG_COMP_MIXIN_MIXOUT) || defined(LLEXT_FORCE_ALL_MODULAR) +#include <audio/mixin_mixout/mixin_mixout.toml> +#endif + +#if defined(CONFIG_COMP_COPIER) || defined(LLEXT_FORCE_ALL_MODULAR) +#include <audio/copier/copier.toml> +#endif + +#if defined(CONFIG_COMP_VOLUME) || defined(LLEXT_FORCE_ALL_MODULAR) +#include <audio/volume/volume.toml> +#endif + +#ifdef CONFIG_PROBE +#include <probe/probe.toml> +#endif + +#if defined(CONFIG_COMP_SRC) || defined(LLEXT_FORCE_ALL_MODULAR) +#include <audio/src/src.toml> +#endif + +#if defined(CONFIG_CADENCE_CODEC) || defined(LLEXT_FORCE_ALL_MODULAR) +#include <audio/module_adapter/module/cadence.toml> +#endif + +#if defined(CONFIG_SAMPLE_SMART_AMP) || defined(LLEXT_FORCE_ALL_MODULAR) +#include <samples/audio/smart_amp_test.toml> +#endif + +#if defined(CONFIG_COMP_IIR) || defined(LLEXT_FORCE_ALL_MODULAR) +#include <audio/eq_iir/eq_iir.toml> +#endif + +#if defined(CONFIG_COMP_FIR) || defined(LLEXT_FORCE_ALL_MODULAR) +#include <audio/eq_fir/eq_fir.toml> +#endif + +#ifdef CONFIG_SAMPLE_KEYPHRASE +#include <samples/audio/detect_test.toml> +#endif + +#if defined(CONFIG_COMP_KPB) || defined(LLEXT_FORCE_ALL_MODULAR) +#include <audio/kpb.toml> +#endif + +#if defined(CONFIG_COMP_SEL) || defined(LLEXT_FORCE_ALL_MODULAR) +#include <audio/selector/selector.toml> +#endif + +#if defined(CONFIG_COMP_ARIA) || defined(LLEXT_FORCE_ALL_MODULAR) +#include <audio/aria/aria.toml> +#endif + +#if defined(CONFIG_COMP_DRC) || defined(LLEXT_FORCE_ALL_MODULAR) +#include <audio/drc/drc.toml> +#endif + +#if defined(CONFIG_COMP_CROSSOVER) || defined(LLEXT_FORCE_ALL_MODULAR) +#include <audio/crossover/crossover.toml> +#endif + +#if defined(CONFIG_COMP_MULTIBAND_DRC) || defined(LLEXT_FORCE_ALL_MODULAR) +#include <audio/multiband_drc/multiband_drc.toml> +#endif + +#if defined(CONFIG_COMP_DCBLOCK) || defined(LLEXT_FORCE_ALL_MODULAR) +#include <audio/dcblock/dcblock.toml> +#endif + +#if defined(CONFIG_COMP_TDFB) || defined(LLEXT_FORCE_ALL_MODULAR) +#include <audio/tdfb/tdfb.toml> +#endif + +#if defined(CONFIG_COMP_GOOGLE_RTC_AUDIO_PROCESSING) || defined(LLEXT_FORCE_ALL_MODULAR) +#include <audio/google/google_rtc_audio_processing.toml> +#endif + +#if defined(CONFIG_COMP_RTNR) || defined(LLEXT_FORCE_ALL_MODULAR) +#include <audio/rtnr/rtnr.toml> +#endif + +#if defined(CONFIG_COMP_IGO_NR) || defined(LLEXT_FORCE_ALL_MODULAR) +#include <audio/igo_nr/igo_nr.toml> +#endif + +#if defined(CONFIG_COMP_MFCC) || defined(LLEXT_FORCE_ALL_MODULAR) +#include <audio/mfcc/mfcc.toml> +#endif + +#if defined(CONFIG_COMP_ASRC) || defined(LLEXT_FORCE_ALL_MODULAR) +#include <audio/asrc/asrc.toml> +#endif + +#if defined(CONFIG_COMP_TEMPLATE) || defined(LLEXT_FORCE_ALL_MODULAR) +#include <audio/template/template.toml> +#endif + +#if defined(CONFIG_COMP_LEVEL_MULTIPLIER) || defined(LLEXT_FORCE_ALL_MODULAR) +#include <audio/level_multiplier/level_multiplier.toml> +#endif + +#if defined(CONFIG_COMP_SOUND_DOSE) || defined(LLEXT_FORCE_ALL_MODULAR) +#include <audio/sound_dose/sound_dose.toml> +#endif + +#if defined(CONFIG_COMP_STFT_PROCESS) || defined(LLEXT_FORCE_ALL_MODULAR) +#include <audio/stft_process/stft_process.toml> +#endif + +[module] +count = __COUNTER__ diff --git a/tools/rimage/config/wcl.toml.h b/tools/rimage/config/wcl.toml.h index e7317018eec6..058383af6e46 100644 --- a/tools/rimage/config/wcl.toml.h +++ b/tools/rimage/config/wcl.toml.h @@ -62,6 +62,10 @@ index = __COUNTER__ #include <audio/src/src.toml> #endif +#if defined(CONFIG_CADENCE_CODEC) || defined(LLEXT_FORCE_ALL_MODULAR) +#include <audio/module_adapter/module/cadence.toml> +#endif + #if defined(CONFIG_COMP_SEL) || defined(LLEXT_FORCE_ALL_MODULAR) #include <audio/selector/selector.toml> #endif @@ -146,5 +150,13 @@ index = __COUNTER__ #include <audio/sound_dose/sound_dose.toml> #endif +#if defined(CONFIG_COMP_TONE) || defined(LLEXT_FORCE_ALL_MODULAR) +#include <audio/tone/tone.toml> +#endif + +#if defined(CONFIG_COMP_STFT_PROCESS) || defined(LLEXT_FORCE_ALL_MODULAR) +#include <audio/stft_process/stft_process.toml> +#endif + [module] count = __COUNTER__ diff --git a/tools/rimage/scripts/checkpatch.pl b/tools/rimage/scripts/checkpatch.pl deleted file mode 100755 index ce33a3b0ac98..000000000000 --- a/tools/rimage/scripts/checkpatch.pl +++ /dev/null @@ -1,6845 +0,0 @@ -#!/usr/bin/env perl -# SPDX-License-Identifier: GPL-2.0 -# -# (c) 2001, Dave Jones. (the file handling bit) -# (c) 2005, Joel Schopp <jschopp@austin.ibm.com> (the ugly bit) -# (c) 2007,2008, Andy Whitcroft <apw@uk.ibm.com> (new conditions, test suite) -# (c) 2008-2010 Andy Whitcroft <apw@canonical.com> -# (c) 2010-2018 Joe Perches <joe@perches.com> - -use strict; -use warnings; -use POSIX; -use File::Basename; -use Cwd 'abs_path'; -use Term::ANSIColor qw(:constants); -use Encode qw(decode encode); - -my $P = $0; -my $D = dirname(abs_path($P)); - -my $V = '0.32'; - -use Getopt::Long qw(:config no_auto_abbrev); - -my $SOF = 1; # enables SOF-specific behaviour -my $quiet = 0; -my $tree = 1; -my $chk_signoff = 1; -my $chk_patch = 1; -my $tst_only; -my $emacs = 0; -my $terse = 0; -my $showfile = 0; -my $file = 0; -my $git = 0; -my %git_commits = (); -my $check = 0; -my $check_orig = 0; -my $summary = 1; -my $mailback = 0; -my $summary_file = 0; -my $show_types = 0; -my $list_types = 0; -my $fix = 0; -my $fix_inplace = 0; -my $root; -my %debug; -my %camelcase = (); -my %use_type = (); -my @use = (); -my %ignore_type = (); -my @ignore = (); -my $help = 0; -my $configuration_file = ".checkpatch.conf"; -my $max_line_length = 100; -my $ignore_perl_version = 0; -my $minimum_perl_version = 5.10.0; -my $min_conf_desc_length = 4; -my $spelling_file = "$D/spelling.txt"; -my $codespell = 0; -my $codespellfile = "/usr/share/codespell/dictionary.txt"; -my $conststructsfile = "$D/const_structs.checkpatch"; -my $typedefsfile = ""; -my $color = "auto"; -my $allow_c99_comments = 1; # Can be overridden by --ignore C99_COMMENT_TOLERANCE -# git output parsing needs US English output, so first set backtick child process LANGUAGE -my $git_command ='export LANGUAGE=en_US.UTF-8; git'; - -sub help { - my ($exitcode) = @_; - - print << "EOM"; -Usage: $P [OPTION]... [FILE]... -Version: $V - -Options: - -q, --quiet quiet - --no-tree run without a kernel tree - --no-signoff do not check for 'Signed-off-by' line - --patch treat FILE as patchfile (default) - --emacs emacs compile window format - --terse one line per report - --showfile emit diffed file position, not input file position - -g, --git treat FILE as a single commit or git revision range - single git commit with: - <rev> - <rev>^ - <rev>~n - multiple git commits with: - <rev1>..<rev2> - <rev1>...<rev2> - <rev>-<count> - git merges are ignored - -f, --file treat FILE as regular source file - --subjective, --strict enable more subjective tests - --list-types list the possible message types - --types TYPE(,TYPE2...) show only these comma separated message types - --ignore TYPE(,TYPE2...) ignore various comma separated message types - --show-types show the specific message type in the output - --max-line-length=n set the maximum line length, (default $max_line_length) - if exceeded, warn on patches - requires --strict for use with --file - --min-conf-desc-length=n set the min description length, if shorter, warn - --root=PATH PATH to the kernel tree root - --no-summary suppress the per-file summary - --mailback only produce a report in case of warnings/errors - --summary-file include the filename in summary - --debug KEY=[0|1] turn on/off debugging of KEY, where KEY is one of - 'values', 'possible', 'type', and 'attr' (default - is all off) - --test-only=WORD report only warnings/errors containing WORD - literally - --fix EXPERIMENTAL - may create horrible results - If correctable single-line errors exist, create - "<inputfile>.EXPERIMENTAL-checkpatch-fixes" - with potential errors corrected to the preferred - checkpatch style - --fix-inplace EXPERIMENTAL - may create horrible results - Is the same as --fix, but overwrites the input - file. It's your fault if there's no backup or git - --ignore-perl-version override checking of perl version. expect - runtime errors. - --codespell Use the codespell dictionary for spelling/typos - (default:/usr/share/codespell/dictionary.txt) - --codespellfile Use this codespell dictionary - --typedefsfile Read additional types from this file - --color[=WHEN] Use colors 'always', 'never', or only when output - is a terminal ('auto'). Default is 'auto'. - -h, --help, --version display this help and exit - -When FILE is - read standard input. -EOM - - exit($exitcode); -} - -sub uniq { - my %seen; - return grep { !$seen{$_}++ } @_; -} - -sub list_types { - my ($exitcode) = @_; - - my $count = 0; - - local $/ = undef; - - open(my $script, '<', abs_path($P)) or - die "$P: Can't read '$P' $!\n"; - - my $text = <$script>; - close($script); - - my @types = (); - # Also catch when type or level is passed through a variable - for ($text =~ /(?:(?:\bCHK|\bWARN|\bERROR|&\{\$msg_level})\s*\(|\$msg_type\s*=)\s*"([^"]+)"/g) { - push (@types, $_); - } - @types = sort(uniq(@types)); - print("#\tMessage type\n\n"); - foreach my $type (@types) { - print(++$count . "\t" . $type . "\n"); - } - - exit($exitcode); -} - -my $conf = which_conf($configuration_file); -if (-f $conf) { - my @conf_args; - open(my $conffile, '<', "$conf") - or warn "$P: Can't find a readable $configuration_file file $!\n"; - - while (<$conffile>) { - my $line = $_; - - $line =~ s/\s*\n?$//g; - $line =~ s/^\s*//g; - $line =~ s/\s+/ /g; - - next if ($line =~ m/^\s*#/); - next if ($line =~ m/^\s*$/); - - my @words = split(" ", $line); - foreach my $word (@words) { - last if ($word =~ m/^#/); - push (@conf_args, $word); - } - } - close($conffile); - unshift(@ARGV, @conf_args) if @conf_args; -} - -# Perl's Getopt::Long allows options to take optional arguments after a space. -# Prevent --color by itself from consuming other arguments -foreach (@ARGV) { - if ($_ eq "--color" || $_ eq "-color") { - $_ = "--color=$color"; - } -} - -GetOptions( - 'q|quiet+' => \$quiet, - 'tree!' => \$tree, - 'signoff!' => \$chk_signoff, - 'patch!' => \$chk_patch, - 'emacs!' => \$emacs, - 'terse!' => \$terse, - 'showfile!' => \$showfile, - 'f|file!' => \$file, - 'g|git!' => \$git, - 'subjective!' => \$check, - 'strict!' => \$check, - 'ignore=s' => \@ignore, - 'types=s' => \@use, - 'show-types!' => \$show_types, - 'list-types!' => \$list_types, - 'max-line-length=i' => \$max_line_length, - 'min-conf-desc-length=i' => \$min_conf_desc_length, - 'root=s' => \$root, - 'summary!' => \$summary, - 'mailback!' => \$mailback, - 'summary-file!' => \$summary_file, - 'fix!' => \$fix, - 'fix-inplace!' => \$fix_inplace, - 'ignore-perl-version!' => \$ignore_perl_version, - 'debug=s' => \%debug, - 'test-only=s' => \$tst_only, - 'codespell!' => \$codespell, - 'codespellfile=s' => \$codespellfile, - 'typedefsfile=s' => \$typedefsfile, - 'color=s' => \$color, - 'no-color' => \$color, #keep old behaviors of -nocolor - 'nocolor' => \$color, #keep old behaviors of -nocolor - 'h|help' => \$help, - 'version' => \$help -) or help(1); - -help(0) if ($help); - -list_types(0) if ($list_types); - -$fix = 1 if ($fix_inplace); -$check_orig = $check; - -my $exit = 0; - -my $perl_version_ok = 1; -if ($^V && $^V lt $minimum_perl_version) { - $perl_version_ok = 0; - printf "$P: requires at least perl version %vd\n", $minimum_perl_version; - exit(1) if (!$ignore_perl_version); -} - -#if no filenames are given, push '-' to read patch from stdin -if ($#ARGV < 0) { - push(@ARGV, '-'); -} - -if ($color =~ /^[01]$/) { - $color = !$color; -} elsif ($color =~ /^always$/i) { - $color = 1; -} elsif ($color =~ /^never$/i) { - $color = 0; -} elsif ($color =~ /^auto$/i) { - $color = (-t STDOUT); -} else { - die "Invalid color mode: $color\n"; -} - -sub hash_save_array_words { - my ($hashRef, $arrayRef) = @_; - - my @array = split(/,/, join(',', @$arrayRef)); - foreach my $word (@array) { - $word =~ s/\s*\n?$//g; - $word =~ s/^\s*//g; - $word =~ s/\s+/ /g; - $word =~ tr/[a-z]/[A-Z]/; - - next if ($word =~ m/^\s*#/); - next if ($word =~ m/^\s*$/); - - $hashRef->{$word}++; - } -} - -sub hash_show_words { - my ($hashRef, $prefix) = @_; - - if (keys %$hashRef) { - print "\nNOTE: $prefix message types:"; - foreach my $word (sort keys %$hashRef) { - print " $word"; - } - print "\n"; - } -} - -hash_save_array_words(\%ignore_type, \@ignore); -hash_save_array_words(\%use_type, \@use); - -my $dbg_values = 0; -my $dbg_possible = 0; -my $dbg_type = 0; -my $dbg_attr = 0; -for my $key (keys %debug) { - ## no critic - eval "\${dbg_$key} = '$debug{$key}';"; - die "$@" if ($@); -} - -my $rpt_cleaners = 0; - -if ($terse) { - $emacs = 1; - $quiet++; -} - -if ($tree) { - if (defined $root) { - if (!top_of_kernel_tree($root)) { - die "$P: $root: --root does not point at a valid tree\n"; - } - } else { - if (top_of_kernel_tree('.')) { - $root = '.'; - } elsif ($0 =~ m@(.*)/scripts/[^/]*$@ && - top_of_kernel_tree($1)) { - $root = $1; - } - } - - if (!defined $root) { - print "Must be run from the top-level dir. of a kernel tree\n"; - exit(2); - } -} - -my $emitted_corrupt = 0; - -our $Ident = qr{ - [A-Za-z_][A-Za-z\d_]* - (?:\s*\#\#\s*[A-Za-z_][A-Za-z\d_]*)* - }x; -our $Storage = qr{extern|static|asmlinkage}; -our $Sparse = qr{ - __user| - __kernel| - __force| - __iomem| - __must_check| - __kprobes| - __ref| - __refconst| - __refdata| - __rcu| - __private - }x; -our $InitAttributePrefix = qr{__(?:mem|cpu|dev|net_|)}; -our $InitAttributeData = qr{$InitAttributePrefix(?:initdata\b)}; -our $InitAttributeConst = qr{$InitAttributePrefix(?:initconst\b)}; -our $InitAttributeInit = qr{$InitAttributePrefix(?:init\b)}; -our $InitAttribute = qr{$InitAttributeData|$InitAttributeConst|$InitAttributeInit}; - -# Notes to $Attribute: -# We need \b after 'init' otherwise 'initconst' will cause a false positive in a check -our $Attribute = qr{ - const| - __percpu| - __nocast| - __safe| - __bitwise| - __packed__| - __packed2__| - __naked| - __maybe_unused| - __always_unused| - __noreturn| - __used| - __cold| - __pure| - __noclone| - __deprecated| - __read_mostly| - __ro_after_init| - __kprobes| - $InitAttribute| - ____cacheline_aligned| - ____cacheline_aligned_in_smp| - ____cacheline_internodealigned_in_smp| - __weak - }x; -our $Modifier; -our $Inline = qr{inline|__always_inline|noinline|__inline|__inline__}; -our $Member = qr{->$Ident|\.$Ident|\[[^]]*\]}; -our $Lval = qr{$Ident(?:$Member)*}; - -our $Int_type = qr{(?i)llu|ull|ll|lu|ul|l|u}; -our $Binary = qr{(?i)0b[01]+$Int_type?}; -our $Hex = qr{(?i)0x[0-9a-f]+$Int_type?}; -our $Int = qr{[0-9]+$Int_type?}; -our $Octal = qr{0[0-7]+$Int_type?}; -our $String = qr{"[X\t]*"}; -our $Float_hex = qr{(?i)0x[0-9a-f]+p-?[0-9]+[fl]?}; -our $Float_dec = qr{(?i)(?:[0-9]+\.[0-9]*|[0-9]*\.[0-9]+)(?:e-?[0-9]+)?[fl]?}; -our $Float_int = qr{(?i)[0-9]+e-?[0-9]+[fl]?}; -our $Float = qr{$Float_hex|$Float_dec|$Float_int}; -our $Constant = qr{$Float|$Binary|$Octal|$Hex|$Int}; -our $Assignment = qr{\*\=|/=|%=|\+=|-=|<<=|>>=|&=|\^=|\|=|=}; -our $Compare = qr{<=|>=|==|!=|<|(?<!-)>}; -our $Arithmetic = qr{\+|-|\*|\/|%}; -our $Operators = qr{ - <=|>=|==|!=| - =>|->|<<|>>|<|>|!|~| - &&|\|\||,|\^|\+\+|--|&|\||$Arithmetic - }x; - -our $c90_Keywords = qr{do|for|while|if|else|return|goto|continue|switch|default|case|break}x; - -our $BasicType; -our $NonptrType; -our $NonptrTypeMisordered; -our $NonptrTypeWithAttr; -our $Type; -our $TypeMisordered; -our $Declare; -our $DeclareMisordered; - -our $NON_ASCII_UTF8 = qr{ - [\xC2-\xDF][\x80-\xBF] # non-overlong 2-byte - | \xE0[\xA0-\xBF][\x80-\xBF] # excluding overlongs - | [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} # straight 3-byte - | \xED[\x80-\x9F][\x80-\xBF] # excluding surrogates - | \xF0[\x90-\xBF][\x80-\xBF]{2} # planes 1-3 - | [\xF1-\xF3][\x80-\xBF]{3} # planes 4-15 - | \xF4[\x80-\x8F][\x80-\xBF]{2} # plane 16 -}x; - -our $UTF8 = qr{ - [\x09\x0A\x0D\x20-\x7E] # ASCII - | $NON_ASCII_UTF8 -}x; - -our $typeC99Typedefs = qr{(?:__)?(?:[us]_?)?int_?(?:8|16|32|64)_t}; -our $typeOtherOSTypedefs = qr{(?x: - u_(?:char|short|int|long) | # bsd - u(?:nchar|short|int|long) # sysv -)}; -our $typeKernelTypedefs = qr{(?x: - (?:__)?(?:u|s|be|le)(?:8|16|32|64)| - atomic_t -)}; -our $typeTypedefs = qr{(?x: - $typeC99Typedefs\b| - $typeOtherOSTypedefs\b| - $typeKernelTypedefs\b -)}; - -our $zero_initializer = qr{(?:(?:0[xX])?0+$Int_type?|NULL|false)\b}; - -our $logFunctions = qr{(?x: - printk(?:_ratelimited|_once|_deferred_once|_deferred|)| - (?:[a-z0-9]+_){1,2}(?:printk|emerg|alert|crit|err|warning|warn|notice|info|debug|dbg|vdbg|devel|cont|WARN)(?:_ratelimited|_once|)| - TP_printk| - WARN(?:_RATELIMIT|_ONCE|)| - panic| - MODULE_[A-Z_]+| - seq_vprintf|seq_printf|seq_puts| - trace[a-z_]+ -)}; - -our $allocFunctions = qr{(?x: - (?:(?:devm_)? - (?:kv|k|v)[czm]alloc(?:_node|_array)? | - kstrdup(?:_const)? | - kmemdup(?:_nul)?) | - (?:\w+)?alloc_skb(?:ip_align)? | - # dev_alloc_skb/netdev_alloc_skb, et al - dma_alloc_coherent -)}; - -our $signature_tags = qr{(?xi: - Signed-off-by:| - Co-developed-by:| - Acked-by:| - Tested-by:| - Reviewed-by:| - Reported-by:| - Suggested-by:| - To:| - Cc: -)}; - -our @typeListMisordered = ( - qr{char\s+(?:un)?signed}, - qr{int\s+(?:(?:un)?signed\s+)?short\s}, - qr{int\s+short(?:\s+(?:un)?signed)}, - qr{short\s+int(?:\s+(?:un)?signed)}, - qr{(?:un)?signed\s+int\s+short}, - qr{short\s+(?:un)?signed}, - qr{long\s+int\s+(?:un)?signed}, - qr{int\s+long\s+(?:un)?signed}, - qr{long\s+(?:un)?signed\s+int}, - qr{int\s+(?:un)?signed\s+long}, - qr{int\s+(?:un)?signed}, - qr{int\s+long\s+long\s+(?:un)?signed}, - qr{long\s+long\s+int\s+(?:un)?signed}, - qr{long\s+long\s+(?:un)?signed\s+int}, - qr{long\s+long\s+(?:un)?signed}, - qr{long\s+(?:un)?signed}, -); - -our @typeList = ( - qr{void}, - qr{(?:(?:un)?signed\s+)?char}, - qr{(?:(?:un)?signed\s+)?short\s+int}, - qr{(?:(?:un)?signed\s+)?short}, - qr{(?:(?:un)?signed\s+)?int}, - qr{(?:(?:un)?signed\s+)?long\s+int}, - qr{(?:(?:un)?signed\s+)?long\s+long\s+int}, - qr{(?:(?:un)?signed\s+)?long\s+long}, - qr{(?:(?:un)?signed\s+)?long}, - qr{(?:un)?signed}, - qr{float}, - qr{double}, - qr{bool}, - qr{struct\s+$Ident}, - qr{union\s+$Ident}, - qr{enum\s+$Ident}, - qr{${Ident}_t}, - qr{${Ident}_handler}, - qr{${Ident}_handler_fn}, - @typeListMisordered, -); - -our $C90_int_types = qr{(?x: - long\s+long\s+int\s+(?:un)?signed| - long\s+long\s+(?:un)?signed\s+int| - long\s+long\s+(?:un)?signed| - (?:(?:un)?signed\s+)?long\s+long\s+int| - (?:(?:un)?signed\s+)?long\s+long| - int\s+long\s+long\s+(?:un)?signed| - int\s+(?:(?:un)?signed\s+)?long\s+long| - - long\s+int\s+(?:un)?signed| - long\s+(?:un)?signed\s+int| - long\s+(?:un)?signed| - (?:(?:un)?signed\s+)?long\s+int| - (?:(?:un)?signed\s+)?long| - int\s+long\s+(?:un)?signed| - int\s+(?:(?:un)?signed\s+)?long| - - int\s+(?:un)?signed| - (?:(?:un)?signed\s+)?int -)}; - -our @typeListFile = (); -our @typeListWithAttr = ( - @typeList, - qr{struct\s+$InitAttribute\s+$Ident}, - qr{union\s+$InitAttribute\s+$Ident}, -); - -our @modifierList = ( - qr{fastcall}, -); -our @modifierListFile = (); - -our @mode_permission_funcs = ( - ["module_param", 3], - ["module_param_(?:array|named|string)", 4], - ["module_param_array_named", 5], - ["debugfs_create_(?:file|u8|u16|u32|u64|x8|x16|x32|x64|size_t|atomic_t|bool|blob|regset32|u32_array)", 2], - ["proc_create(?:_data|)", 2], - ["(?:CLASS|DEVICE|SENSOR|SENSOR_DEVICE|IIO_DEVICE)_ATTR", 2], - ["IIO_DEV_ATTR_[A-Z_]+", 1], - ["SENSOR_(?:DEVICE_|)ATTR_2", 2], - ["SENSOR_TEMPLATE(?:_2|)", 3], - ["__ATTR", 2], -); - -#Create a search pattern for all these functions to speed up a loop below -our $mode_perms_search = ""; -foreach my $entry (@mode_permission_funcs) { - $mode_perms_search .= '|' if ($mode_perms_search ne ""); - $mode_perms_search .= $entry->[0]; -} -$mode_perms_search = "(?:${mode_perms_search})"; - -our %deprecated_apis = ( - "synchronize_rcu_bh" => "synchronize_rcu", - "synchronize_rcu_bh_expedited" => "synchronize_rcu_expedited", - "call_rcu_bh" => "call_rcu", - "rcu_barrier_bh" => "rcu_barrier", - "synchronize_sched" => "synchronize_rcu", - "synchronize_sched_expedited" => "synchronize_rcu_expedited", - "call_rcu_sched" => "call_rcu", - "rcu_barrier_sched" => "rcu_barrier", - "get_state_synchronize_sched" => "get_state_synchronize_rcu", - "cond_synchronize_sched" => "cond_synchronize_rcu", -); - -#Create a search pattern for all these strings to speed up a loop below -our $deprecated_apis_search = ""; -foreach my $entry (keys %deprecated_apis) { - $deprecated_apis_search .= '|' if ($deprecated_apis_search ne ""); - $deprecated_apis_search .= $entry; -} -$deprecated_apis_search = "(?:${deprecated_apis_search})"; - -our $mode_perms_world_writable = qr{ - S_IWUGO | - S_IWOTH | - S_IRWXUGO | - S_IALLUGO | - 0[0-7][0-7][2367] -}x; - -our %mode_permission_string_types = ( - "S_IRWXU" => 0700, - "S_IRUSR" => 0400, - "S_IWUSR" => 0200, - "S_IXUSR" => 0100, - "S_IRWXG" => 0070, - "S_IRGRP" => 0040, - "S_IWGRP" => 0020, - "S_IXGRP" => 0010, - "S_IRWXO" => 0007, - "S_IROTH" => 0004, - "S_IWOTH" => 0002, - "S_IXOTH" => 0001, - "S_IRWXUGO" => 0777, - "S_IRUGO" => 0444, - "S_IWUGO" => 0222, - "S_IXUGO" => 0111, -); - -#Create a search pattern for all these strings to speed up a loop below -our $mode_perms_string_search = ""; -foreach my $entry (keys %mode_permission_string_types) { - $mode_perms_string_search .= '|' if ($mode_perms_string_search ne ""); - $mode_perms_string_search .= $entry; -} -our $single_mode_perms_string_search = "(?:${mode_perms_string_search})"; -our $multi_mode_perms_string_search = qr{ - ${single_mode_perms_string_search} - (?:\s*\|\s*${single_mode_perms_string_search})* -}x; - -sub perms_to_octal { - my ($string) = @_; - - return trim($string) if ($string =~ /^\s*0[0-7]{3,3}\s*$/); - - my $val = ""; - my $oval = ""; - my $to = 0; - my $curpos = 0; - my $lastpos = 0; - while ($string =~ /\b(($single_mode_perms_string_search)\b(?:\s*\|\s*)?\s*)/g) { - $curpos = pos($string); - my $match = $2; - my $omatch = $1; - last if ($lastpos > 0 && ($curpos - length($omatch) != $lastpos)); - $lastpos = $curpos; - $to |= $mode_permission_string_types{$match}; - $val .= '\s*\|\s*' if ($val ne ""); - $val .= $match; - $oval .= $omatch; - } - $oval =~ s/^\s*\|\s*//; - $oval =~ s/\s*\|\s*$//; - return sprintf("%04o", $to); -} - -our $allowed_asm_includes = qr{(?x: - irq| - memory| - time| - reboot -)}; -# memory.h: ARM has a custom one - -# Load common spelling mistakes and build regular expression list. -my $misspellings; -my %spelling_fix; - -if (open(my $spelling, '<', $spelling_file)) { - while (<$spelling>) { - my $line = $_; - - $line =~ s/\s*\n?$//g; - $line =~ s/^\s*//g; - - next if ($line =~ m/^\s*#/); - next if ($line =~ m/^\s*$/); - - my ($suspect, $fix) = split(/\|\|/, $line); - - $spelling_fix{$suspect} = $fix; - } - close($spelling); -} else { - warn "No typos will be found - file '$spelling_file': $!\n"; -} - -if ($codespell) { - if (open(my $spelling, '<', $codespellfile)) { - while (<$spelling>) { - my $line = $_; - - $line =~ s/\s*\n?$//g; - $line =~ s/^\s*//g; - - next if ($line =~ m/^\s*#/); - next if ($line =~ m/^\s*$/); - next if ($line =~ m/, disabled/i); - - $line =~ s/,.*$//; - - my ($suspect, $fix) = split(/->/, $line); - - $spelling_fix{$suspect} = $fix; - } - close($spelling); - } else { - warn "No codespell typos will be found - file '$codespellfile': $!\n"; - } -} - -$misspellings = join("|", sort keys %spelling_fix) if keys %spelling_fix; - -sub read_words { - my ($wordsRef, $file) = @_; - - if (open(my $words, '<', $file)) { - while (<$words>) { - my $line = $_; - - $line =~ s/\s*\n?$//g; - $line =~ s/^\s*//g; - - next if ($line =~ m/^\s*#/); - next if ($line =~ m/^\s*$/); - if ($line =~ /\s/) { - print("$file: '$line' invalid - ignored\n"); - next; - } - - $$wordsRef .= '|' if ($$wordsRef ne ""); - $$wordsRef .= $line; - } - close($file); - return 1; - } - - return 0; -} - -my $const_structs = ""; -read_words(\$const_structs, $conststructsfile) - or warn "No structs that should be const will be found - file '$conststructsfile': $!\n"; - -my $typeOtherTypedefs = ""; -if (length($typedefsfile)) { - read_words(\$typeOtherTypedefs, $typedefsfile) - or warn "No additional types will be considered - file '$typedefsfile': $!\n"; -} -$typeTypedefs .= '|' . $typeOtherTypedefs if ($typeOtherTypedefs ne ""); - -sub build_types { - my $mods = "(?x: \n" . join("|\n ", (@modifierList, @modifierListFile)) . "\n)"; - my $all = "(?x: \n" . join("|\n ", (@typeList, @typeListFile)) . "\n)"; - my $Misordered = "(?x: \n" . join("|\n ", @typeListMisordered) . "\n)"; - my $allWithAttr = "(?x: \n" . join("|\n ", @typeListWithAttr) . "\n)"; - $Modifier = qr{(?:$Attribute|$Sparse|$mods)}; - $BasicType = qr{ - (?:$typeTypedefs\b)| - (?:${all}\b) - }x; - $NonptrType = qr{ - (?:$Modifier\s+|const\s+)* - (?: - (?:typeof|__typeof__)\s*\([^\)]*\)| - (?:$typeTypedefs\b)| - (?:${all}\b) - ) - (?:\s+$Modifier|\s+const)* - }x; - $NonptrTypeMisordered = qr{ - (?:$Modifier\s+|const\s+)* - (?: - (?:${Misordered}\b) - ) - (?:\s+$Modifier|\s+const)* - }x; - $NonptrTypeWithAttr = qr{ - (?:$Modifier\s+|const\s+)* - (?: - (?:typeof|__typeof__)\s*\([^\)]*\)| - (?:$typeTypedefs\b)| - (?:${allWithAttr}\b) - ) - (?:\s+$Modifier|\s+const)* - }x; - $Type = qr{ - $NonptrType - (?:(?:\s|\*|\[\])+\s*const|(?:\s|\*\s*(?:const\s*)?|\[\])+|(?:\s*\[\s*\])+)? - (?:\s+$Inline|\s+$Modifier)* - }x; - $TypeMisordered = qr{ - $NonptrTypeMisordered - (?:(?:\s|\*|\[\])+\s*const|(?:\s|\*\s*(?:const\s*)?|\[\])+|(?:\s*\[\s*\])+)? - (?:\s+$Inline|\s+$Modifier)* - }x; - $Declare = qr{(?:$Storage\s+(?:$Inline\s+)?)?$Type}; - $DeclareMisordered = qr{(?:$Storage\s+(?:$Inline\s+)?)?$TypeMisordered}; -} -build_types(); - -our $Typecast = qr{\s*(\(\s*$NonptrType\s*\)){0,1}\s*}; - -# Using $balanced_parens, $LvalOrFunc, or $FuncArg -# requires at least perl version v5.10.0 -# Any use must be runtime checked with $^V - -our $balanced_parens = qr/(\((?:[^\(\)]++|(?-1))*\))/; -our $LvalOrFunc = qr{((?:[\&\*]\s*)?$Lval)\s*($balanced_parens{0,1})\s*}; -our $FuncArg = qr{$Typecast{0,1}($LvalOrFunc|$Constant|$String)}; - -our $declaration_macros = qr{(?x: - (?:$Storage\s+)?(?:[A-Z_][A-Z0-9]*_){0,2}(?:DEFINE|DECLARE)(?:_[A-Z0-9]+){1,6}\s*\(| - (?:$Storage\s+)?[HLP]?LIST_HEAD\s*\(| - (?:$Storage\s+)?${Type}\s+uninitialized_var\s*\(| - (?:SKCIPHER_REQUEST|SHASH_DESC|AHASH_REQUEST)_ON_STACK\s*\( -)}; - -sub deparenthesize { - my ($string) = @_; - return "" if (!defined($string)); - - while ($string =~ /^\s*\(.*\)\s*$/) { - $string =~ s@^\s*\(\s*@@; - $string =~ s@\s*\)\s*$@@; - } - - $string =~ s@\s+@ @g; - - return $string; -} - -sub seed_camelcase_file { - my ($file) = @_; - - return if (!(-f $file)); - - local $/; - - open(my $include_file, '<', "$file") - or warn "$P: Can't read '$file' $!\n"; - my $text = <$include_file>; - close($include_file); - - my @lines = split('\n', $text); - - foreach my $line (@lines) { - next if ($line !~ /(?:[A-Z][a-z]|[a-z][A-Z])/); - if ($line =~ /^[ \t]*(?:#[ \t]*define|typedef\s+$Type)\s+(\w*(?:[A-Z][a-z]|[a-z][A-Z])\w*)/) { - $camelcase{$1} = 1; - } elsif ($line =~ /^\s*$Declare\s+(\w*(?:[A-Z][a-z]|[a-z][A-Z])\w*)\s*[\(\[,;]/) { - $camelcase{$1} = 1; - } elsif ($line =~ /^\s*(?:union|struct|enum)\s+(\w*(?:[A-Z][a-z]|[a-z][A-Z])\w*)\s*[;\{]/) { - $camelcase{$1} = 1; - } - } -} - -sub is_maintained_obsolete { - my ($filename) = @_; - - return 0 if (!$tree || !(-e "$root/scripts/get_maintainer.pl")); - - my $status = `perl $root/scripts/get_maintainer.pl --status --nom --nol --nogit --nogit-fallback -f $filename 2>&1`; - - return $status =~ /obsolete/i; -} - -sub is_SPDX_License_valid { - my ($license) = @_; - - return 1 if (!$tree || which("python") eq "" || !(-e "$root/scripts/spdxcheck.py") || !(-e "$root/.git")); - - my $root_path = abs_path($root); - my $status = `cd "$root_path"; echo "$license" | python scripts/spdxcheck.py -`; - return 0 if ($status ne ""); - return 1; -} - -my $camelcase_seeded = 0; -sub seed_camelcase_includes { - return if ($camelcase_seeded); - - my $files; - my $camelcase_cache = ""; - my @include_files = (); - - $camelcase_seeded = 1; - - if (-e ".git") { - my $git_last_include_commit = `${git_command} log --no-merges --pretty=format:"%h%n" -1 -- include`; - chomp $git_last_include_commit; - $camelcase_cache = ".checkpatch-camelcase.git.$git_last_include_commit"; - } else { - my $last_mod_date = 0; - $files = `find $root/include -name "*.h"`; - @include_files = split('\n', $files); - foreach my $file (@include_files) { - my $date = POSIX::strftime("%Y%m%d%H%M", - localtime((stat $file)[9])); - $last_mod_date = $date if ($last_mod_date < $date); - } - $camelcase_cache = ".checkpatch-camelcase.date.$last_mod_date"; - } - - if ($camelcase_cache ne "" && -f $camelcase_cache) { - open(my $camelcase_file, '<', "$camelcase_cache") - or warn "$P: Can't read '$camelcase_cache' $!\n"; - while (<$camelcase_file>) { - chomp; - $camelcase{$_} = 1; - } - close($camelcase_file); - - return; - } - - if (-e ".git") { - $files = `${git_command} ls-files "include/*.h"`; - @include_files = split('\n', $files); - } - - foreach my $file (@include_files) { - seed_camelcase_file($file); - } - - if ($camelcase_cache ne "") { - unlink glob ".checkpatch-camelcase.*"; - open(my $camelcase_file, '>', "$camelcase_cache") - or warn "$P: Can't write '$camelcase_cache' $!\n"; - foreach (sort { lc($a) cmp lc($b) } keys(%camelcase)) { - print $camelcase_file ("$_\n"); - } - close($camelcase_file); - } -} - -sub git_commit_info { - my ($commit, $id, $desc) = @_; - - return ($id, $desc) if ((which("git") eq "") || !(-e ".git")); - - my $output = `${git_command} log --no-color --format='%H %s' -1 $commit 2>&1`; - $output =~ s/^\s*//gm; - my @lines = split("\n", $output); - - return ($id, $desc) if ($#lines < 0); - - if ($lines[0] =~ /^error: short SHA1 $commit is ambiguous/) { -# Maybe one day convert this block of bash into something that returns -# all matching commit ids, but it's very slow... -# -# echo "checking commits $1..." -# git rev-list --remotes | grep -i "^$1" | -# while read line ; do -# git log --format='%H %s' -1 $line | -# echo "commit $(cut -c 1-12,41-)" -# done - } elsif ($lines[0] =~ /^fatal: ambiguous argument '$commit': unknown revision or path not in the working tree\./) { - $id = undef; - } else { - $id = substr($lines[0], 0, 12); - $desc = substr($lines[0], 41); - } - - return ($id, $desc); -} - -$chk_signoff = 0 if ($file); - -my @rawlines = (); -my @lines = (); -my @fixed = (); -my @fixed_inserted = (); -my @fixed_deleted = (); -my $fixlinenr = -1; - -# If input is git commits, extract all commits from the commit expressions. -# For example, HEAD-3 means we need check 'HEAD, HEAD~1, HEAD~2'. -die "$P: No git repository found\n" if ($git && !-e ".git"); - -if ($git) { - my @commits = (); - foreach my $commit_expr (@ARGV) { - my $git_range; - if ($commit_expr =~ m/^(.*)-(\d+)$/) { - $git_range = "-$2 $1"; - } elsif ($commit_expr =~ m/\.\./) { - $git_range = "$commit_expr"; - } else { - $git_range = "-1 $commit_expr"; - } - my $lines = `${git_command} log --no-color --no-merges --pretty=format:'%H %s' $git_range`; - foreach my $line (split(/\n/, $lines)) { - $line =~ /^([0-9a-fA-F]{40,40}) (.*)$/; - next if (!defined($1) || !defined($2)); - my $sha1 = $1; - my $subject = $2; - unshift(@commits, $sha1); - $git_commits{$sha1} = $subject; - } - } - die "$P: no git commits after extraction!\n" if (@commits == 0); - @ARGV = @commits; -} - -my $vname; -$allow_c99_comments = !defined $ignore_type{"C99_COMMENT_TOLERANCE"}; -for my $filename (@ARGV) { - my $FILE; - if ($git) { - open($FILE, '-|', "git format-patch -M --stdout -1 $filename") || - die "$P: $filename: git format-patch failed - $!\n"; - } elsif ($file) { - open($FILE, '-|', "diff -u /dev/null $filename") || - die "$P: $filename: diff failed - $!\n"; - } elsif ($filename eq '-') { - open($FILE, '<&STDIN'); - } else { - open($FILE, '<', "$filename") || - die "$P: $filename: open failed - $!\n"; - } - if ($filename eq '-') { - $vname = 'Your patch'; - } elsif ($git) { - $vname = "Commit " . substr($filename, 0, 12) . ' ("' . $git_commits{$filename} . '")'; - } else { - $vname = $filename; - } - while (<$FILE>) { - chomp; - push(@rawlines, $_); - } - close($FILE); - - if ($#ARGV > 0 && $quiet == 0) { - print '-' x length($vname) . "\n"; - print "$vname\n"; - print '-' x length($vname) . "\n"; - } - - if (!process($filename)) { - $exit = 1; - } - @rawlines = (); - @lines = (); - @fixed = (); - @fixed_inserted = (); - @fixed_deleted = (); - $fixlinenr = -1; - @modifierListFile = (); - @typeListFile = (); - build_types(); -} - -if (!$quiet) { - hash_show_words(\%use_type, "Used"); - hash_show_words(\%ignore_type, "Ignored"); - - if (!$perl_version_ok) { - print << "EOM" - -NOTE: perl $^V is not modern enough to detect all possible issues. - An upgrade to at least perl $minimum_perl_version is suggested. -EOM - } - if ($exit) { - print << "EOM" - -NOTE: If any of the errors are false positives, please report - them to the maintainer, see CHECKPATCH in MAINTAINERS. -EOM - } -} - -exit($exit); - -sub top_of_kernel_tree { - my ($root) = @_; - - my @tree_check = ( - "COPYING", "CREDITS", "Kbuild", "MAINTAINERS", "Makefile", - "README", "Documentation", "arch", "include", "drivers", - "fs", "init", "ipc", "kernel", "lib", "scripts", - ); - - if ($SOF) { - @tree_check = ( - "LICENCE", "README.md", "rimage", "tools", - "scripts", "doc", "src", "CODEOWNERS", - "CMakeLists.txt", - ); - } - - foreach my $check (@tree_check) { - if (! -e $root . '/' . $check) { - return 0; - } - } - return 1; -} - -sub parse_email { - my ($formatted_email) = @_; - - my $name = ""; - my $address = ""; - my $comment = ""; - - if ($formatted_email =~ /^(.*)<(\S+\@\S+)>(.*)$/) { - $name = $1; - $address = $2; - $comment = $3 if defined $3; - } elsif ($formatted_email =~ /^\s*<(\S+\@\S+)>(.*)$/) { - $address = $1; - $comment = $2 if defined $2; - } elsif ($formatted_email =~ /(\S+\@\S+)(.*)$/) { - $address = $1; - $comment = $2 if defined $2; - $formatted_email =~ s/\Q$address\E.*$//; - $name = $formatted_email; - $name = trim($name); - $name =~ s/^\"|\"$//g; - # If there's a name left after stripping spaces and - # leading quotes, and the address doesn't have both - # leading and trailing angle brackets, the address - # is invalid. ie: - # "joe smith joe@smith.com" bad - # "joe smith <joe@smith.com" bad - if ($name ne "" && $address !~ /^<[^>]+>$/) { - $name = ""; - $address = ""; - $comment = ""; - } - } - - $name = trim($name); - $name =~ s/^\"|\"$//g; - $address = trim($address); - $address =~ s/^\<|\>$//g; - - if ($name =~ /[^\w \-]/i) { ##has "must quote" chars - $name =~ s/(?<!\\)"/\\"/g; ##escape quotes - $name = "\"$name\""; - } - - return ($name, $address, $comment); -} - -sub format_email { - my ($name, $address) = @_; - - my $formatted_email; - - $name = trim($name); - $name =~ s/^\"|\"$//g; - $address = trim($address); - - if ($name =~ /[^\w \-]/i) { ##has "must quote" chars - $name =~ s/(?<!\\)"/\\"/g; ##escape quotes - $name = "\"$name\""; - } - - if ("$name" eq "") { - $formatted_email = "$address"; - } else { - $formatted_email = "$name <$address>"; - } - - return $formatted_email; -} - -sub which { - my ($bin) = @_; - - foreach my $path (split(/:/, $ENV{PATH})) { - if (-e "$path/$bin") { - return "$path/$bin"; - } - } - - return ""; -} - -sub which_conf { - my ($conf) = @_; - - foreach my $path (split(/:/, ".:$ENV{HOME}:.scripts")) { - if (-e "$path/$conf") { - return "$path/$conf"; - } - } - - return ""; -} - -sub expand_tabs { - my ($str) = @_; - - my $res = ''; - my $n = 0; - for my $c (split(//, $str)) { - if ($c eq "\t") { - $res .= ' '; - $n++; - for (; ($n % 8) != 0; $n++) { - $res .= ' '; - } - next; - } - $res .= $c; - $n++; - } - - return $res; -} -sub copy_spacing { - (my $res = shift) =~ tr/\t/ /c; - return $res; -} - -sub line_stats { - my ($line) = @_; - - # Drop the diff line leader and expand tabs - $line =~ s/^.//; - $line = expand_tabs($line); - - # Pick the indent from the front of the line. - my ($white) = ($line =~ /^(\s*)/); - - return (length($line), length($white)); -} - -my $sanitise_quote = ''; - -sub sanitise_line_reset { - my ($in_comment) = @_; - - if ($in_comment) { - $sanitise_quote = '*/'; - } else { - $sanitise_quote = ''; - } -} -sub sanitise_line { - my ($line) = @_; - - my $res = ''; - my $l = ''; - - my $qlen = 0; - my $off = 0; - my $c; - - # Always copy over the diff marker. - $res = substr($line, 0, 1); - - for ($off = 1; $off < length($line); $off++) { - $c = substr($line, $off, 1); - - # Comments we are whacking completely including the begin - # and end, all to $;. - if ($sanitise_quote eq '' && substr($line, $off, 2) eq '/*') { - $sanitise_quote = '*/'; - - substr($res, $off, 2, "$;$;"); - $off++; - next; - } - if ($sanitise_quote eq '*/' && substr($line, $off, 2) eq '*/') { - $sanitise_quote = ''; - substr($res, $off, 2, "$;$;"); - $off++; - next; - } - if ($sanitise_quote eq '' && substr($line, $off, 2) eq '//') { - $sanitise_quote = '//'; - - substr($res, $off, 2, $sanitise_quote); - $off++; - next; - } - - # A \ in a string means ignore the next character. - if (($sanitise_quote eq "'" || $sanitise_quote eq '"') && - $c eq "\\") { - substr($res, $off, 2, 'XX'); - $off++; - next; - } - # Regular quotes. - if ($c eq "'" || $c eq '"') { - if ($sanitise_quote eq '') { - $sanitise_quote = $c; - - substr($res, $off, 1, $c); - next; - } elsif ($sanitise_quote eq $c) { - $sanitise_quote = ''; - } - } - - #print "c<$c> SQ<$sanitise_quote>\n"; - if ($off != 0 && $sanitise_quote eq '*/' && $c ne "\t") { - substr($res, $off, 1, $;); - } elsif ($off != 0 && $sanitise_quote eq '//' && $c ne "\t") { - substr($res, $off, 1, $;); - } elsif ($off != 0 && $sanitise_quote && $c ne "\t") { - substr($res, $off, 1, 'X'); - } else { - substr($res, $off, 1, $c); - } - } - - if ($sanitise_quote eq '//') { - $sanitise_quote = ''; - } - - # The pathname on a #include may be surrounded by '<' and '>'. - if ($res =~ /^.\s*\#\s*include\s+\<(.*)\>/) { - my $clean = 'X' x length($1); - $res =~ s@\<.*\>@<$clean>@; - - # The whole of a #error is a string. - } elsif ($res =~ /^.\s*\#\s*(?:error|warning)\s+(.*)\b/) { - my $clean = 'X' x length($1); - $res =~ s@(\#\s*(?:error|warning)\s+).*@$1$clean@; - } - - if ($allow_c99_comments && $res =~ m@(//.*$)@) { - my $match = $1; - $res =~ s/\Q$match\E/"$;" x length($match)/e; - } - - return $res; -} - -sub get_quoted_string { - my ($line, $rawline) = @_; - - return "" if (!defined($line) || !defined($rawline)); - return "" if ($line !~ m/($String)/g); - return substr($rawline, $-[0], $+[0] - $-[0]); -} - -sub ctx_statement_block { - my ($linenr, $remain, $off) = @_; - my $line = $linenr - 1; - my $blk = ''; - my $soff = $off; - my $coff = $off - 1; - my $coff_set = 0; - - my $loff = 0; - - my $type = ''; - my $level = 0; - my @stack = (); - my $p; - my $c; - my $len = 0; - - my $remainder; - while (1) { - @stack = (['', 0]) if ($#stack == -1); - - #warn "CSB: blk<$blk> remain<$remain>\n"; - # If we are about to drop off the end, pull in more - # context. - if ($off >= $len) { - for (; $remain > 0; $line++) { - last if (!defined $lines[$line]); - next if ($lines[$line] =~ /^-/); - $remain--; - $loff = $len; - $blk .= $lines[$line] . "\n"; - $len = length($blk); - $line++; - last; - } - # Bail if there is no further context. - #warn "CSB: blk<$blk> off<$off> len<$len>\n"; - if ($off >= $len) { - last; - } - if ($level == 0 && substr($blk, $off) =~ /^.\s*#\s*define/) { - $level++; - $type = '#'; - } - } - $p = $c; - $c = substr($blk, $off, 1); - $remainder = substr($blk, $off); - - #warn "CSB: c<$c> type<$type> level<$level> remainder<$remainder> coff_set<$coff_set>\n"; - - # Handle nested #if/#else. - if ($remainder =~ /^#\s*(?:ifndef|ifdef|if)\s/) { - push(@stack, [ $type, $level ]); - } elsif ($remainder =~ /^#\s*(?:else|elif)\b/) { - ($type, $level) = @{$stack[$#stack - 1]}; - } elsif ($remainder =~ /^#\s*endif\b/) { - ($type, $level) = @{pop(@stack)}; - } - - # Statement ends at the ';' or a close '}' at the - # outermost level. - if ($level == 0 && $c eq ';') { - last; - } - - # An else is really a conditional as long as its not else if - if ($level == 0 && $coff_set == 0 && - (!defined($p) || $p =~ /(?:\s|\}|\+)/) && - $remainder =~ /^(else)(?:\s|{)/ && - $remainder !~ /^else\s+if\b/) { - $coff = $off + length($1) - 1; - $coff_set = 1; - #warn "CSB: mark coff<$coff> soff<$soff> 1<$1>\n"; - #warn "[" . substr($blk, $soff, $coff - $soff + 1) . "]\n"; - } - - if (($type eq '' || $type eq '(') && $c eq '(') { - $level++; - $type = '('; - } - if ($type eq '(' && $c eq ')') { - $level--; - $type = ($level != 0)? '(' : ''; - - if ($level == 0 && $coff < $soff) { - $coff = $off; - $coff_set = 1; - #warn "CSB: mark coff<$coff>\n"; - } - } - if (($type eq '' || $type eq '{') && $c eq '{') { - $level++; - $type = '{'; - } - if ($type eq '{' && $c eq '}') { - $level--; - $type = ($level != 0)? '{' : ''; - - if ($level == 0) { - if (substr($blk, $off + 1, 1) eq ';') { - $off++; - } - last; - } - } - # Preprocessor commands end at the newline unless escaped. - if ($type eq '#' && $c eq "\n" && $p ne "\\") { - $level--; - $type = ''; - $off++; - last; - } - $off++; - } - # We are truly at the end, so shuffle to the next line. - if ($off == $len) { - $loff = $len + 1; - $line++; - $remain--; - } - - my $statement = substr($blk, $soff, $off - $soff + 1); - my $condition = substr($blk, $soff, $coff - $soff + 1); - - #warn "STATEMENT<$statement>\n"; - #warn "CONDITION<$condition>\n"; - - #print "coff<$coff> soff<$off> loff<$loff>\n"; - - return ($statement, $condition, - $line, $remain + 1, $off - $loff + 1, $level); -} - -sub statement_lines { - my ($stmt) = @_; - - # Strip the diff line prefixes and rip blank lines at start and end. - $stmt =~ s/(^|\n)./$1/g; - $stmt =~ s/^\s*//; - $stmt =~ s/\s*$//; - - my @stmt_lines = ($stmt =~ /\n/g); - - return $#stmt_lines + 2; -} - -sub statement_rawlines { - my ($stmt) = @_; - - my @stmt_lines = ($stmt =~ /\n/g); - - return $#stmt_lines + 2; -} - -sub statement_block_size { - my ($stmt) = @_; - - $stmt =~ s/(^|\n)./$1/g; - $stmt =~ s/^\s*{//; - $stmt =~ s/}\s*$//; - $stmt =~ s/^\s*//; - $stmt =~ s/\s*$//; - - my @stmt_lines = ($stmt =~ /\n/g); - my @stmt_statements = ($stmt =~ /;/g); - - my $stmt_lines = $#stmt_lines + 2; - my $stmt_statements = $#stmt_statements + 1; - - if ($stmt_lines > $stmt_statements) { - return $stmt_lines; - } else { - return $stmt_statements; - } -} - -sub ctx_statement_full { - my ($linenr, $remain, $off) = @_; - my ($statement, $condition, $level); - - my (@chunks); - - # Grab the first conditional/block pair. - ($statement, $condition, $linenr, $remain, $off, $level) = - ctx_statement_block($linenr, $remain, $off); - #print "F: c<$condition> s<$statement> remain<$remain>\n"; - push(@chunks, [ $condition, $statement ]); - if (!($remain > 0 && $condition =~ /^\s*(?:\n[+-])?\s*(?:if|else|do)\b/s)) { - return ($level, $linenr, @chunks); - } - - # Pull in the following conditional/block pairs and see if they - # could continue the statement. - for (;;) { - ($statement, $condition, $linenr, $remain, $off, $level) = - ctx_statement_block($linenr, $remain, $off); - #print "C: c<$condition> s<$statement> remain<$remain>\n"; - last if (!($remain > 0 && $condition =~ /^(?:\s*\n[+-])*\s*(?:else|do)\b/s)); - #print "C: push\n"; - push(@chunks, [ $condition, $statement ]); - } - - return ($level, $linenr, @chunks); -} - -sub ctx_block_get { - my ($linenr, $remain, $outer, $open, $close, $off) = @_; - my $line; - my $start = $linenr - 1; - my $blk = ''; - my @o; - my @c; - my @res = (); - - my $level = 0; - my @stack = ($level); - for ($line = $start; $remain > 0; $line++) { - next if ($rawlines[$line] =~ /^-/); - $remain--; - - $blk .= $rawlines[$line]; - - # Handle nested #if/#else. - if ($lines[$line] =~ /^.\s*#\s*(?:ifndef|ifdef|if)\s/) { - push(@stack, $level); - } elsif ($lines[$line] =~ /^.\s*#\s*(?:else|elif)\b/) { - $level = $stack[$#stack - 1]; - } elsif ($lines[$line] =~ /^.\s*#\s*endif\b/) { - $level = pop(@stack); - } - - foreach my $c (split(//, $lines[$line])) { - ##print "C<$c>L<$level><$open$close>O<$off>\n"; - if ($off > 0) { - $off--; - next; - } - - if ($c eq $close && $level > 0) { - $level--; - last if ($level == 0); - } elsif ($c eq $open) { - $level++; - } - } - - if (!$outer || $level <= 1) { - push(@res, $rawlines[$line]); - } - - last if ($level == 0); - } - - return ($level, @res); -} -sub ctx_block_outer { - my ($linenr, $remain) = @_; - - my ($level, @r) = ctx_block_get($linenr, $remain, 1, '{', '}', 0); - return @r; -} -sub ctx_block { - my ($linenr, $remain) = @_; - - my ($level, @r) = ctx_block_get($linenr, $remain, 0, '{', '}', 0); - return @r; -} -sub ctx_statement { - my ($linenr, $remain, $off) = @_; - - my ($level, @r) = ctx_block_get($linenr, $remain, 0, '(', ')', $off); - return @r; -} -sub ctx_block_level { - my ($linenr, $remain) = @_; - - return ctx_block_get($linenr, $remain, 0, '{', '}', 0); -} -sub ctx_statement_level { - my ($linenr, $remain, $off) = @_; - - return ctx_block_get($linenr, $remain, 0, '(', ')', $off); -} - -sub ctx_locate_comment { - my ($first_line, $end_line) = @_; - - # Catch a comment on the end of the line itself. - my ($current_comment) = ($rawlines[$end_line - 1] =~ m@.*(/\*.*\*/)\s*(?:\\\s*)?$@); - return $current_comment if (defined $current_comment); - - # Look through the context and try and figure out if there is a - # comment. - my $in_comment = 0; - $current_comment = ''; - for (my $linenr = $first_line; $linenr < $end_line; $linenr++) { - my $line = $rawlines[$linenr - 1]; - #warn " $line\n"; - if ($linenr == $first_line and $line =~ m@^.\s*\*@) { - $in_comment = 1; - } - if ($line =~ m@/\*@) { - $in_comment = 1; - } - if (!$in_comment && $current_comment ne '') { - $current_comment = ''; - } - $current_comment .= $line . "\n" if ($in_comment); - if ($line =~ m@\*/@) { - $in_comment = 0; - } - } - - chomp($current_comment); - return($current_comment); -} -sub ctx_has_comment { - my ($first_line, $end_line) = @_; - my $cmt = ctx_locate_comment($first_line, $end_line); - - ##print "LINE: $rawlines[$end_line - 1 ]\n"; - ##print "CMMT: $cmt\n"; - - return ($cmt ne ''); -} - -sub raw_line { - my ($linenr, $cnt) = @_; - - my $offset = $linenr - 1; - $cnt++; - - my $line; - while ($cnt) { - $line = $rawlines[$offset++]; - next if (defined($line) && $line =~ /^-/); - $cnt--; - } - - return $line; -} - -sub get_stat_real { - my ($linenr, $lc) = @_; - - my $stat_real = raw_line($linenr, 0); - for (my $count = $linenr + 1; $count <= $lc; $count++) { - $stat_real = $stat_real . "\n" . raw_line($count, 0); - } - - return $stat_real; -} - -sub get_stat_here { - my ($linenr, $cnt, $here) = @_; - - my $herectx = $here . "\n"; - for (my $n = 0; $n < $cnt; $n++) { - $herectx .= raw_line($linenr, $n) . "\n"; - } - - return $herectx; -} - -sub cat_vet { - my ($vet) = @_; - my ($res, $coded); - - $res = ''; - while ($vet =~ /([^[:cntrl:]]*)([[:cntrl:]]|$)/g) { - $res .= $1; - if ($2 ne '') { - $coded = sprintf("^%c", unpack('C', $2) + 64); - $res .= $coded; - } - } - $res =~ s/$/\$/; - - return $res; -} - -my $av_preprocessor = 0; -my $av_pending; -my @av_paren_type; -my $av_pend_colon; - -sub annotate_reset { - $av_preprocessor = 0; - $av_pending = '_'; - @av_paren_type = ('E'); - $av_pend_colon = 'O'; -} - -sub annotate_values { - my ($stream, $type) = @_; - - my $res; - my $var = '_' x length($stream); - my $cur = $stream; - - print "$stream\n" if ($dbg_values > 1); - - while (length($cur)) { - @av_paren_type = ('E') if ($#av_paren_type < 0); - print " <" . join('', @av_paren_type) . - "> <$type> <$av_pending>" if ($dbg_values > 1); - if ($cur =~ /^(\s+)/o) { - print "WS($1)\n" if ($dbg_values > 1); - if ($1 =~ /\n/ && $av_preprocessor) { - $type = pop(@av_paren_type); - $av_preprocessor = 0; - } - - } elsif ($cur =~ /^(\(\s*$Type\s*)\)/ && $av_pending eq '_') { - print "CAST($1)\n" if ($dbg_values > 1); - push(@av_paren_type, $type); - $type = 'c'; - - } elsif ($cur =~ /^($Type)\s*(?:$Ident|,|\)|\(|\s*$)/) { - print "DECLARE($1)\n" if ($dbg_values > 1); - $type = 'T'; - - } elsif ($cur =~ /^($Modifier)\s*/) { - print "MODIFIER($1)\n" if ($dbg_values > 1); - $type = 'T'; - - } elsif ($cur =~ /^(\#\s*define\s*$Ident)(\(?)/o) { - print "DEFINE($1,$2)\n" if ($dbg_values > 1); - $av_preprocessor = 1; - push(@av_paren_type, $type); - if ($2 ne '') { - $av_pending = 'N'; - } - $type = 'E'; - - } elsif ($cur =~ /^(\#\s*(?:undef\s*$Ident|include\b))/o) { - print "UNDEF($1)\n" if ($dbg_values > 1); - $av_preprocessor = 1; - push(@av_paren_type, $type); - - } elsif ($cur =~ /^(\#\s*(?:ifdef|ifndef|if))/o) { - print "PRE_START($1)\n" if ($dbg_values > 1); - $av_preprocessor = 1; - - push(@av_paren_type, $type); - push(@av_paren_type, $type); - $type = 'E'; - - } elsif ($cur =~ /^(\#\s*(?:else|elif))/o) { - print "PRE_RESTART($1)\n" if ($dbg_values > 1); - $av_preprocessor = 1; - - push(@av_paren_type, $av_paren_type[$#av_paren_type]); - - $type = 'E'; - - } elsif ($cur =~ /^(\#\s*(?:endif))/o) { - print "PRE_END($1)\n" if ($dbg_values > 1); - - $av_preprocessor = 1; - - # Assume all arms of the conditional end as this - # one does, and continue as if the #endif was not here. - pop(@av_paren_type); - push(@av_paren_type, $type); - $type = 'E'; - - } elsif ($cur =~ /^(\\\n)/o) { - print "PRECONT($1)\n" if ($dbg_values > 1); - - } elsif ($cur =~ /^(__attribute__)\s*\(?/o) { - print "ATTR($1)\n" if ($dbg_values > 1); - $av_pending = $type; - $type = 'N'; - - } elsif ($cur =~ /^(sizeof)\s*(\()?/o) { - print "SIZEOF($1)\n" if ($dbg_values > 1); - if (defined $2) { - $av_pending = 'V'; - } - $type = 'N'; - - } elsif ($cur =~ /^(if|while|for)\b/o) { - print "COND($1)\n" if ($dbg_values > 1); - $av_pending = 'E'; - $type = 'N'; - - } elsif ($cur =~/^(case)/o) { - print "CASE($1)\n" if ($dbg_values > 1); - $av_pend_colon = 'C'; - $type = 'N'; - - } elsif ($cur =~/^(return|else|goto|typeof|__typeof__)\b/o) { - print "KEYWORD($1)\n" if ($dbg_values > 1); - $type = 'N'; - - } elsif ($cur =~ /^(\()/o) { - print "PAREN('$1')\n" if ($dbg_values > 1); - push(@av_paren_type, $av_pending); - $av_pending = '_'; - $type = 'N'; - - } elsif ($cur =~ /^(\))/o) { - my $new_type = pop(@av_paren_type); - if ($new_type ne '_') { - $type = $new_type; - print "PAREN('$1') -> $type\n" - if ($dbg_values > 1); - } else { - print "PAREN('$1')\n" if ($dbg_values > 1); - } - - } elsif ($cur =~ /^($Ident)\s*\(/o) { - print "FUNC($1)\n" if ($dbg_values > 1); - $type = 'V'; - $av_pending = 'V'; - - } elsif ($cur =~ /^($Ident\s*):(?:\s*\d+\s*(,|=|;))?/) { - if (defined $2 && $type eq 'C' || $type eq 'T') { - $av_pend_colon = 'B'; - } elsif ($type eq 'E') { - $av_pend_colon = 'L'; - } - print "IDENT_COLON($1,$type>$av_pend_colon)\n" if ($dbg_values > 1); - $type = 'V'; - - } elsif ($cur =~ /^($Ident|$Constant)/o) { - print "IDENT($1)\n" if ($dbg_values > 1); - $type = 'V'; - - } elsif ($cur =~ /^($Assignment)/o) { - print "ASSIGN($1)\n" if ($dbg_values > 1); - $type = 'N'; - - } elsif ($cur =~/^(;|{|})/) { - print "END($1)\n" if ($dbg_values > 1); - $type = 'E'; - $av_pend_colon = 'O'; - - } elsif ($cur =~/^(,)/) { - print "COMMA($1)\n" if ($dbg_values > 1); - $type = 'C'; - - } elsif ($cur =~ /^(\?)/o) { - print "QUESTION($1)\n" if ($dbg_values > 1); - $type = 'N'; - - } elsif ($cur =~ /^(:)/o) { - print "COLON($1,$av_pend_colon)\n" if ($dbg_values > 1); - - substr($var, length($res), 1, $av_pend_colon); - if ($av_pend_colon eq 'C' || $av_pend_colon eq 'L') { - $type = 'E'; - } else { - $type = 'N'; - } - $av_pend_colon = 'O'; - - } elsif ($cur =~ /^(\[)/o) { - print "CLOSE($1)\n" if ($dbg_values > 1); - $type = 'N'; - - } elsif ($cur =~ /^(-(?![->])|\+(?!\+)|\*|\&\&|\&)/o) { - my $variant; - - print "OPV($1)\n" if ($dbg_values > 1); - if ($type eq 'V') { - $variant = 'B'; - } else { - $variant = 'U'; - } - - substr($var, length($res), 1, $variant); - $type = 'N'; - - } elsif ($cur =~ /^($Operators)/o) { - print "OP($1)\n" if ($dbg_values > 1); - if ($1 ne '++' && $1 ne '--') { - $type = 'N'; - } - - } elsif ($cur =~ /(^.)/o) { - print "C($1)\n" if ($dbg_values > 1); - } - if (defined $1) { - $cur = substr($cur, length($1)); - $res .= $type x length($1); - } - } - - return ($res, $var); -} - -sub possible { - my ($possible, $line) = @_; - my $notPermitted = qr{(?: - ^(?: - $Modifier| - $Storage| - $Type| - DEFINE_\S+ - )$| - ^(?: - goto| - return| - case| - else| - asm|__asm__| - do| - \#| - \#\#| - )(?:\s|$)| - ^(?:typedef|struct|enum)\b - )}x; - warn "CHECK<$possible> ($line)\n" if ($dbg_possible > 2); - if ($possible !~ $notPermitted) { - # Check for modifiers. - $possible =~ s/\s*$Storage\s*//g; - $possible =~ s/\s*$Sparse\s*//g; - if ($possible =~ /^\s*$/) { - - } elsif ($possible =~ /\s/) { - $possible =~ s/\s*$Type\s*//g; - for my $modifier (split(' ', $possible)) { - if ($modifier !~ $notPermitted) { - warn "MODIFIER: $modifier ($possible) ($line)\n" if ($dbg_possible); - push(@modifierListFile, $modifier); - } - } - - } else { - warn "POSSIBLE: $possible ($line)\n" if ($dbg_possible); - push(@typeListFile, $possible); - } - build_types(); - } else { - warn "NOTPOSS: $possible ($line)\n" if ($dbg_possible > 1); - } -} - -my $prefix = ''; - -sub show_type { - my ($type) = @_; - - $type =~ tr/[a-z]/[A-Z]/; - - return defined $use_type{$type} if (scalar keys %use_type > 0); - - return !defined $ignore_type{$type}; -} - -sub report { - my ($level, $type, $msg) = @_; - - if (!show_type($type) || - (defined $tst_only && $msg !~ /\Q$tst_only\E/)) { - return 0; - } - my $output = ''; - if ($color) { - if ($level eq 'ERROR') { - $output .= RED; - } elsif ($level eq 'WARNING') { - $output .= YELLOW; - } else { - $output .= GREEN; - } - } - $output .= $prefix . $level . ':'; - if ($show_types) { - $output .= BLUE if ($color); - $output .= "$type:"; - } - $output .= RESET if ($color); - $output .= ' ' . $msg . "\n"; - - if ($showfile) { - my @lines = split("\n", $output, -1); - splice(@lines, 1, 1); - $output = join("\n", @lines); - } - $output = (split('\n', $output))[0] . "\n" if ($terse); - - push(our @report, $output); - - return 1; -} - -sub report_dump { - our @report; -} - -sub fixup_current_range { - my ($lineRef, $offset, $length) = @_; - - if ($$lineRef =~ /^\@\@ -\d+,\d+ \+(\d+),(\d+) \@\@/) { - my $o = $1; - my $l = $2; - my $no = $o + $offset; - my $nl = $l + $length; - $$lineRef =~ s/\+$o,$l \@\@/\+$no,$nl \@\@/; - } -} - -sub fix_inserted_deleted_lines { - my ($linesRef, $insertedRef, $deletedRef) = @_; - - my $range_last_linenr = 0; - my $delta_offset = 0; - - my $old_linenr = 0; - my $new_linenr = 0; - - my $next_insert = 0; - my $next_delete = 0; - - my @lines = (); - - my $inserted = @{$insertedRef}[$next_insert++]; - my $deleted = @{$deletedRef}[$next_delete++]; - - foreach my $old_line (@{$linesRef}) { - my $save_line = 1; - my $line = $old_line; #don't modify the array - if ($line =~ /^(?:\+\+\+|\-\-\-)\s+\S+/) { #new filename - $delta_offset = 0; - } elsif ($line =~ /^\@\@ -\d+,\d+ \+\d+,\d+ \@\@/) { #new hunk - $range_last_linenr = $new_linenr; - fixup_current_range(\$line, $delta_offset, 0); - } - - while (defined($deleted) && ${$deleted}{'LINENR'} == $old_linenr) { - $deleted = @{$deletedRef}[$next_delete++]; - $save_line = 0; - fixup_current_range(\$lines[$range_last_linenr], $delta_offset--, -1); - } - - while (defined($inserted) && ${$inserted}{'LINENR'} == $old_linenr) { - push(@lines, ${$inserted}{'LINE'}); - $inserted = @{$insertedRef}[$next_insert++]; - $new_linenr++; - fixup_current_range(\$lines[$range_last_linenr], $delta_offset++, 1); - } - - if ($save_line) { - push(@lines, $line); - $new_linenr++; - } - - $old_linenr++; - } - - return @lines; -} - -sub fix_insert_line { - my ($linenr, $line) = @_; - - my $inserted = { - LINENR => $linenr, - LINE => $line, - }; - push(@fixed_inserted, $inserted); -} - -sub fix_delete_line { - my ($linenr, $line) = @_; - - my $deleted = { - LINENR => $linenr, - LINE => $line, - }; - - push(@fixed_deleted, $deleted); -} - -sub ERROR { - my ($type, $msg) = @_; - - if (report("ERROR", $type, $msg)) { - our $clean = 0; - our $cnt_error++; - return 1; - } - return 0; -} -sub WARN { - my ($type, $msg) = @_; - - if (report("WARNING", $type, $msg)) { - our $clean = 0; - our $cnt_warn++; - return 1; - } - return 0; -} -sub CHK { - my ($type, $msg) = @_; - - if ($check && report("CHECK", $type, $msg)) { - our $clean = 0; - our $cnt_chk++; - return 1; - } - return 0; -} - -sub check_absolute_file { - my ($absolute, $herecurr) = @_; - my $file = $absolute; - - ##print "absolute<$absolute>\n"; - - # See if any suffix of this path is a path within the tree. - while ($file =~ s@^[^/]*/@@) { - if (-f "$root/$file") { - ##print "file<$file>\n"; - last; - } - } - if (! -f _) { - return 0; - } - - # It is, so see if the prefix is acceptable. - my $prefix = $absolute; - substr($prefix, -length($file)) = ''; - - ##print "prefix<$prefix>\n"; - if ($prefix ne ".../") { - WARN("USE_RELATIVE_PATH", - "use relative pathname instead of absolute in changelog text\n" . $herecurr); - } -} - -sub trim { - my ($string) = @_; - - $string =~ s/^\s+|\s+$//g; - - return $string; -} - -sub ltrim { - my ($string) = @_; - - $string =~ s/^\s+//; - - return $string; -} - -sub rtrim { - my ($string) = @_; - - $string =~ s/\s+$//; - - return $string; -} - -sub string_find_replace { - my ($string, $find, $replace) = @_; - - $string =~ s/$find/$replace/g; - - return $string; -} - -sub tabify { - my ($leading) = @_; - - my $source_indent = 8; - my $max_spaces_before_tab = $source_indent - 1; - my $spaces_to_tab = " " x $source_indent; - - #convert leading spaces to tabs - 1 while $leading =~ s@^([\t]*)$spaces_to_tab@$1\t@g; - #Remove spaces before a tab - 1 while $leading =~ s@^([\t]*)( {1,$max_spaces_before_tab})\t@$1\t@g; - - return "$leading"; -} - -sub pos_last_openparen { - my ($line) = @_; - - my $pos = 0; - - my $opens = $line =~ tr/\(/\(/; - my $closes = $line =~ tr/\)/\)/; - - my $last_openparen = 0; - - if (($opens == 0) || ($closes >= $opens)) { - return -1; - } - - my $len = length($line); - - for ($pos = 0; $pos < $len; $pos++) { - my $string = substr($line, $pos); - if ($string =~ /^($FuncArg|$balanced_parens)/) { - $pos += length($1) - 1; - } elsif (substr($line, $pos, 1) eq '(') { - $last_openparen = $pos; - } elsif (index($string, '(') == -1) { - last; - } - } - - return length(expand_tabs(substr($line, 0, $last_openparen))) + 1; -} - -sub process { - my $filename = shift; - - my $linenr=0; - my $prevline=""; - my $prevrawline=""; - my $stashline=""; - my $stashrawline=""; - - my $length; - my $indent; - my $previndent=0; - my $stashindent=0; - - our $clean = 1; - my $signoff = 0; - my $author = ''; - my $authorsignoff = 0; - my $is_patch = 0; - my $is_binding_patch = -1; - my $in_header_lines = $file ? 0 : 1; - my $in_commit_log = 0; #Scanning lines before patch - my $has_commit_log = 0; #Encountered lines before patch - my $commit_log_lines = 0; #Number of commit log lines - my $commit_log_possible_stack_dump = 0; - my $commit_log_long_line = 0; - my $commit_log_has_diff = 0; - my $reported_maintainer_file = 0; - my $reported_abi_update = 0; - my $last_abi_file = ''; - my $non_utf8_charset = 0; - - my $last_blank_line = 0; - my $last_coalesced_string_linenr = -1; - - our @report = (); - our $cnt_lines = 0; - our $cnt_error = 0; - our $cnt_warn = 0; - our $cnt_chk = 0; - - # Trace the real file/line as we go. - my $realfile = ''; - my $realline = 0; - my $realcnt = 0; - my $here = ''; - my $context_function; #undef'd unless there's a known function - my $in_comment = 0; - my $comment_edge = 0; - my $first_line = 0; - my $p1_prefix = ''; - - my $prev_values = 'E'; - - # suppression flags - my %suppress_ifbraces; - my %suppress_whiletrailers; - my %suppress_export; - my $suppress_statement = 0; - - my %signatures = (); - - # Pre-scan the patch sanitizing the lines. - # Pre-scan the patch looking for any __setup documentation. - # - my @setup_docs = (); - my $setup_docs = 0; - - my $camelcase_file_seeded = 0; - - my $checklicenseline = 1; - - sanitise_line_reset(); - my $line; - foreach my $rawline (@rawlines) { - $linenr++; - $line = $rawline; - - push(@fixed, $rawline) if ($fix); - - if ($rawline=~/^\+\+\+\s+(\S+)/) { - $setup_docs = 0; - if ($1 =~ m@Documentation/admin-guide/kernel-parameters.rst$@) { - $setup_docs = 1; - } - #next; - } - if ($rawline =~ /^\@\@ -\d+(?:,\d+)? \+(\d+)(,(\d+))? \@\@/) { - $realline=$1-1; - if (defined $2) { - $realcnt=$3+1; - } else { - $realcnt=1+1; - } - $in_comment = 0; - - # Guestimate if this is a continuing comment. Run - # the context looking for a comment "edge". If this - # edge is a close comment then we must be in a comment - # at context start. - my $edge; - my $cnt = $realcnt; - for (my $ln = $linenr + 1; $cnt > 0; $ln++) { - next if (defined $rawlines[$ln - 1] && - $rawlines[$ln - 1] =~ /^-/); - $cnt--; - #print "RAW<$rawlines[$ln - 1]>\n"; - last if (!defined $rawlines[$ln - 1]); - if ($rawlines[$ln - 1] =~ m@(/\*|\*/)@ && - $rawlines[$ln - 1] !~ m@"[^"]*(?:/\*|\*/)[^"]*"@) { - ($edge) = $1; - last; - } - } - if (defined $edge && $edge eq '*/') { - $in_comment = 1; - } - - # Guestimate if this is a continuing comment. If this - # is the start of a diff block and this line starts - # ' *' then it is very likely a comment. - if (!defined $edge && - $rawlines[$linenr] =~ m@^.\s*(?:\*\*+| \*)(?:\s|$)@) - { - $in_comment = 1; - } - - ##print "COMMENT:$in_comment edge<$edge> $rawline\n"; - sanitise_line_reset($in_comment); - - } elsif ($realcnt && $rawline =~ /^(?:\+| |$)/) { - # Standardise the strings and chars within the input to - # simplify matching -- only bother with positive lines. - $line = sanitise_line($rawline); - } - - # Check if ABI is being updated. If so, there's probably no need to - # emit the "does ABI need updating?" message on file add/move/delete - if ($SOF && - ($line =~ /\+#define SOF_ABI_MAJOR*/ || - $line =~ /\+#define SOF_ABI_MINOR*/ || - $line =~ /\+#define SOF_ABI_PATCH*/)) { - $reported_abi_update = 1; - } - - push(@lines, $line); - - if ($realcnt > 1) { - $realcnt-- if ($line =~ /^(?:\+| |$)/); - } else { - $realcnt = 0; - } - - #print "==>$rawline\n"; - #print "-->$line\n"; - - if ($setup_docs && $line =~ /^\+/) { - push(@setup_docs, $line); - } - } - - $prefix = ''; - - $realcnt = 0; - $linenr = 0; - $fixlinenr = -1; - foreach my $line (@lines) { - $linenr++; - $fixlinenr++; - my $sline = $line; #copy of $line - $sline =~ s/$;/ /g; #with comments as spaces - - my $rawline = $rawlines[$linenr - 1]; - -# check if it's a mode change, rename or start of a patch - if (!$in_commit_log && - ($line =~ /^ mode change [0-7]+ => [0-7]+ \S+\s*$/ || - ($line =~ /^rename (?:from|to) \S+\s*$/ || - $line =~ /^diff --git a\/[\w\/\.\_\-]+ b\/\S+\s*$/))) { - $is_patch = 1; - } - -#extract the line range in the file after the patch is applied - if (!$in_commit_log && - $line =~ /^\@\@ -\d+(?:,\d+)? \+(\d+)(,(\d+))? \@\@(.*)/) { - my $context = $4; - $is_patch = 1; - $first_line = $linenr + 1; - $realline=$1-1; - if (defined $2) { - $realcnt=$3+1; - } else { - $realcnt=1+1; - } - annotate_reset(); - $prev_values = 'E'; - - %suppress_ifbraces = (); - %suppress_whiletrailers = (); - %suppress_export = (); - $suppress_statement = 0; - if ($context =~ /\b(\w+)\s*\(/) { - $context_function = $1; - } else { - undef $context_function; - } - next; - -# track the line number as we move through the hunk, note that -# new versions of GNU diff omit the leading space on completely -# blank context lines so we need to count that too. - } elsif ($line =~ /^( |\+|$)/) { - $realline++; - $realcnt-- if ($realcnt != 0); - - # Measure the line length and indent. - ($length, $indent) = line_stats($rawline); - - # Track the previous line. - ($prevline, $stashline) = ($stashline, $line); - ($previndent, $stashindent) = ($stashindent, $indent); - ($prevrawline, $stashrawline) = ($stashrawline, $rawline); - - #warn "line<$line>\n"; - - } elsif ($realcnt == 1) { - $realcnt--; - } - - my $hunk_line = ($realcnt != 0); - - $here = "#$linenr: " if (!$file); - $here = "#$realline: " if ($file); - - my $found_file = 0; - # extract the filename as it passes - if ($line =~ /^diff --git.*?(\S+)$/) { - $realfile = $1; - $realfile =~ s@^([^/]*)/@@ if (!$file); - $in_commit_log = 0; - $found_file = 1; - } elsif ($line =~ /^\+\+\+\s+(\S+)/) { - $realfile = $1; - $realfile =~ s@^([^/]*)/@@ if (!$file); - $in_commit_log = 0; - - $p1_prefix = $1; - if (!$file && $tree && $p1_prefix ne '' && - -e "$root/$p1_prefix") { - WARN("PATCH_PREFIX", - "patch prefix '$p1_prefix' exists, appears to be a -p0 patch\n"); - } - - if ($realfile =~ m@^include/asm/@) { - ERROR("MODIFIED_INCLUDE_ASM", - "do not modify files in include/asm, change architecture specific files in include/asm-<architecture>\n" . "$here$rawline\n"); - } - $found_file = 1; - } - -#make up the handle for any error we report on this line - if ($showfile) { - $prefix = "$realfile:$realline: " - } elsif ($emacs) { - if ($file) { - $prefix = "$filename:$realline: "; - } else { - $prefix = "$filename:$linenr: "; - } - } - - if ($found_file) { - if (is_maintained_obsolete($realfile)) { - WARN("OBSOLETE", - "$realfile is marked as 'obsolete' in the MAINTAINERS hierarchy. No unnecessary modifications please.\n"); - } - if ($realfile =~ m@^(?:drivers/net/|net/|drivers/staging/)@) { - $check = 1; - } else { - $check = $check_orig; - } - $checklicenseline = 1; - - if ($realfile !~ /^MAINTAINERS/) { - my $last_binding_patch = $is_binding_patch; - - $is_binding_patch = () = $realfile =~ m@^(?:Documentation/devicetree/|include/dt-bindings/)@; - - if (($last_binding_patch != -1) && - ($last_binding_patch ^ $is_binding_patch)) { - WARN("DT_SPLIT_BINDING_PATCH", - "DT binding docs and includes should be a separate patch. See: Documentation/devicetree/bindings/submitting-patches.txt\n"); - } - } - - next; - } - - $here .= "FILE: $realfile:$realline:" if ($realcnt != 0); - - my $hereline = "$here\n$rawline\n"; - my $herecurr = "$here\n$rawline\n"; - my $hereprev = "$here\n$prevrawline\n$rawline\n"; - - $cnt_lines++ if ($realcnt != 0); - -# Verify the existence of a commit log if appropriate -# 2 is used because a $signature is counted in $commit_log_lines - if ($in_commit_log) { - if ($line !~ /^\s*$/) { - $commit_log_lines++; #could be a $signature - } - } elsif ($has_commit_log && $commit_log_lines < 2) { - WARN("COMMIT_MESSAGE", - "Missing commit description - Add an appropriate one\n"); - $commit_log_lines = 2; #warn only once - } - -# Check if the commit log has what seems like a diff which can confuse patch - if ($in_commit_log && !$commit_log_has_diff && - (($line =~ m@^\s+diff\b.*a/[\w/]+@ && - $line =~ m@^\s+diff\b.*a/([\w/]+)\s+b/$1\b@) || - $line =~ m@^\s*(?:\-\-\-\s+a/|\+\+\+\s+b/)@ || - $line =~ m/^\s*\@\@ \-\d+,\d+ \+\d+,\d+ \@\@/)) { - ERROR("DIFF_IN_COMMIT_MSG", - "Avoid using diff content in the commit message - patch(1) might not work\n" . $herecurr); - $commit_log_has_diff = 1; - } - -# Check for incorrect file permissions - if ($line =~ /^new (file )?mode.*[7531]\d{0,2}$/) { - my $permhere = $here . "FILE: $realfile\n"; - if ($realfile !~ m@scripts/@ && - $realfile !~ /\.(py|pl|awk|sh)$/) { - ERROR("EXECUTE_PERMISSIONS", - "do not set execute permissions for source files\n" . $permhere); - } - } - -# Check the patch for a From: - if (decode("MIME-Header", $line) =~ /^From:\s*(.*)/) { - $author = $1; - $author = encode("utf8", $author) if ($line =~ /=\?utf-8\?/i); - $author =~ s/"//g; - } - -# Check the patch for a signoff: - if ($line =~ /^\s*signed-off-by:/i) { - $signoff++; - $in_commit_log = 0; - if ($author ne '') { - my $l = $line; - $l =~ s/"//g; - if ($l =~ /^\s*signed-off-by:\s*\Q$author\E/i) { - $authorsignoff = 1; - } - } - } - - -# Check if MAINTAINERS is being updated. If so, there's probably no need to -# emit the "does MAINTAINERS need updating?" message on file add/move/delete - if ($line =~ /^\s*MAINTAINERS\s*\|/) { - $reported_maintainer_file = 1; - } - -# Check signature styles - if (!$in_header_lines && - $line =~ /^(\s*)([a-z0-9_-]+by:|$signature_tags)(\s*)(.*)/i) { - my $space_before = $1; - my $sign_off = $2; - my $space_after = $3; - my $email = $4; - my $ucfirst_sign_off = ucfirst(lc($sign_off)); - - if ($sign_off !~ /$signature_tags/) { - WARN("BAD_SIGN_OFF", - "Non-standard signature: $sign_off\n" . $herecurr); - } - if (defined $space_before && $space_before ne "") { - if (WARN("BAD_SIGN_OFF", - "Do not use whitespace before $ucfirst_sign_off\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] = - "$ucfirst_sign_off $email"; - } - } - if ($sign_off =~ /-by:$/i && $sign_off ne $ucfirst_sign_off) { - if (WARN("BAD_SIGN_OFF", - "'$ucfirst_sign_off' is the preferred signature form\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] = - "$ucfirst_sign_off $email"; - } - - } - if (!defined $space_after || $space_after ne " ") { - if (WARN("BAD_SIGN_OFF", - "Use a single space after $ucfirst_sign_off\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] = - "$ucfirst_sign_off $email"; - } - } - - my ($email_name, $email_address, $comment) = parse_email($email); - my $suggested_email = format_email(($email_name, $email_address)); - if ($suggested_email eq "") { - ERROR("BAD_SIGN_OFF", - "Unrecognized email address: '$email'\n" . $herecurr); - } else { - my $dequoted = $suggested_email; - $dequoted =~ s/^"//; - $dequoted =~ s/" </ </; - # Don't force email to have quotes - # Allow just an angle bracketed address - if ("$dequoted$comment" ne $email && - "<$email_address>$comment" ne $email && - "$suggested_email$comment" ne $email) { - WARN("BAD_SIGN_OFF", - "email address '$email' might be better as '$suggested_email$comment'\n" . $herecurr); - } - } - -# Check for duplicate signatures - my $sig_nospace = $line; - $sig_nospace =~ s/\s//g; - $sig_nospace = lc($sig_nospace); - if (defined $signatures{$sig_nospace}) { - WARN("BAD_SIGN_OFF", - "Duplicate signature\n" . $herecurr); - } else { - $signatures{$sig_nospace} = 1; - } - -# Check Co-developed-by: immediately followed by Signed-off-by: with same name and email - if ($sign_off =~ /^co-developed-by:$/i) { - if ($email eq $author) { - WARN("BAD_SIGN_OFF", - "Co-developed-by: should not be used to attribute nominal patch author '$author'\n" . "$here\n" . $rawline); - } - if (!defined $lines[$linenr]) { - WARN("BAD_SIGN_OFF", - "Co-developed-by: must be immediately followed by Signed-off-by:\n" . "$here\n" . $rawline); - } elsif ($rawlines[$linenr] !~ /^\s*signed-off-by:\s*(.*)/i) { - WARN("BAD_SIGN_OFF", - "Co-developed-by: must be immediately followed by Signed-off-by:\n" . "$here\n" . $rawline . "\n" .$rawlines[$linenr]); - } elsif ($1 ne $email) { - WARN("BAD_SIGN_OFF", - "Co-developed-by and Signed-off-by: name/email do not match \n" . "$here\n" . $rawline . "\n" .$rawlines[$linenr]); - } - } - } - -# Check email subject for common tools that don't need to be mentioned - if ($in_header_lines && - $line =~ /^Subject:.*\b(?:checkpatch|sparse|smatch)\b[^:]/i) { - WARN("EMAIL_SUBJECT", - "A patch subject line should describe the change not the tool that found it\n" . $herecurr); - } - -# Check for unwanted Gerrit info - if ($in_commit_log && $line =~ /^\s*change-id:/i) { - ERROR("GERRIT_CHANGE_ID", - "Remove Gerrit Change-Id's before submitting upstream.\n" . $herecurr); - } - -# Check if the commit log is in a possible stack dump - if ($in_commit_log && !$commit_log_possible_stack_dump && - ($line =~ /^\s*(?:WARNING:|BUG:)/ || - $line =~ /^\s*\[\s*\d+\.\d{6,6}\s*\]/ || - # timestamp - $line =~ /^\s*\[\<[0-9a-fA-F]{8,}\>\]/) || - $line =~ /^(?:\s+\w+:\s+[0-9a-fA-F]+){3,3}/ || - $line =~ /^\s*\#\d+\s*\[[0-9a-fA-F]+\]\s*\w+ at [0-9a-fA-F]+/) { - # stack dump address styles - $commit_log_possible_stack_dump = 1; - } - -# Check for line lengths > 75 in commit log, warn once - if ($in_commit_log && !$commit_log_long_line && - length($line) > 75 && - !($line =~ /^\s*[a-zA-Z0-9_\/\.]+\s+\|\s+\d+/ || - # file delta changes - $line =~ /^\s*(?:[\w\.\-]+\/)++[\w\.\-]+:/ || - # filename then : - $line =~ /^\s*(?:Fixes:|Link:)/i || - # A Fixes: or Link: line - $commit_log_possible_stack_dump)) { - WARN("COMMIT_LOG_LONG_LINE", - "Possible unwrapped commit description (prefer a maximum 75 chars per line)\n" . $herecurr); - $commit_log_long_line = 1; - } - -# Reset possible stack dump if a blank line is found - if ($in_commit_log && $commit_log_possible_stack_dump && - $line =~ /^\s*$/) { - $commit_log_possible_stack_dump = 0; - } - -# Check for git id commit length and improperly formed commit descriptions - if ($in_commit_log && !$commit_log_possible_stack_dump && - $line !~ /^\s*(?:Link|Patchwork|http|https|BugLink):/i && - $line !~ /^This reverts commit [0-9a-f]{7,40}/ && - ($line =~ /\bcommit\s+[0-9a-f]{5,}\b/i || - ($line =~ /(?:\s|^)[0-9a-f]{12,40}(?:[\s"'\(\[]|$)/i && - $line !~ /[\<\[][0-9a-f]{12,40}[\>\]]/i && - $line !~ /\bfixes:\s*[0-9a-f]{12,40}/i))) { - my $init_char = "c"; - my $orig_commit = ""; - my $short = 1; - my $long = 0; - my $case = 1; - my $space = 1; - my $hasdesc = 0; - my $hasparens = 0; - my $id = '0123456789ab'; - my $orig_desc = "commit description"; - my $description = ""; - - if ($line =~ /\b(c)ommit\s+([0-9a-f]{5,})\b/i) { - $init_char = $1; - $orig_commit = lc($2); - } elsif ($line =~ /\b([0-9a-f]{12,40})\b/i) { - $orig_commit = lc($1); - } - - $short = 0 if ($line =~ /\bcommit\s+[0-9a-f]{12,40}/i); - $long = 1 if ($line =~ /\bcommit\s+[0-9a-f]{41,}/i); - $space = 0 if ($line =~ /\bcommit [0-9a-f]/i); - $case = 0 if ($line =~ /\b[Cc]ommit\s+[0-9a-f]{5,40}[^A-F]/); - if ($line =~ /\bcommit\s+[0-9a-f]{5,}\s+\("([^"]+)"\)/i) { - $orig_desc = $1; - $hasparens = 1; - } elsif ($line =~ /\bcommit\s+[0-9a-f]{5,}\s*$/i && - defined $rawlines[$linenr] && - $rawlines[$linenr] =~ /^\s*\("([^"]+)"\)/) { - $orig_desc = $1; - $hasparens = 1; - } elsif ($line =~ /\bcommit\s+[0-9a-f]{5,}\s+\("[^"]+$/i && - defined $rawlines[$linenr] && - $rawlines[$linenr] =~ /^\s*[^"]+"\)/) { - $line =~ /\bcommit\s+[0-9a-f]{5,}\s+\("([^"]+)$/i; - $orig_desc = $1; - $rawlines[$linenr] =~ /^\s*([^"]+)"\)/; - $orig_desc .= " " . $1; - $hasparens = 1; - } - - ($id, $description) = git_commit_info($orig_commit, - $id, $orig_desc); - - if (defined($id) && - ($short || $long || $space || $case || ($orig_desc ne $description) || !$hasparens)) { - ERROR("GIT_COMMIT_ID", - "Please use git commit description style 'commit <12+ chars of sha1> (\"<title line>\")' - ie: '${init_char}ommit $id (\"$description\")'\n" . $herecurr); - } - } - -# Check for added, moved or deleted files - if (!$SOF && - (!$reported_maintainer_file && !$in_commit_log && - ($line =~ /^(?:new|deleted) file mode\s*\d+\s*$/ || - $line =~ /^rename (?:from|to) [\w\/\.\-]+\s*$/ || - ($line =~ /\{\s*([\w\/\.\-]*)\s*\=\>\s*([\w\/\.\-]*)\s*\}/ && - (defined($1) || defined($2)))))) { - $is_patch = 1; - $reported_maintainer_file = 1; - WARN("FILE_PATH_CHANGES", - "added, moved or deleted file(s), does MAINTAINERS need updating?\n" . $herecurr); - } - -# Check for wrappage within a valid hunk of the file - if ($realcnt != 0 && $line !~ m{^(?:\+|-| |\\ No newline|$)}) { - ERROR("CORRUPTED_PATCH", - "patch seems to be corrupt (line wrapped?)\n" . - $herecurr) if (!$emitted_corrupt++); - } - -# UTF-8 regex found at http://www.w3.org/International/questions/qa-forms-utf-8.en.php - if (($realfile =~ /^$/ || $line =~ /^\+/) && - $rawline !~ m/^$UTF8*$/) { - my ($utf8_prefix) = ($rawline =~ /^($UTF8*)/); - - my $blank = copy_spacing($rawline); - my $ptr = substr($blank, 0, length($utf8_prefix)) . "^"; - my $hereptr = "$hereline$ptr\n"; - - CHK("INVALID_UTF8", - "Invalid UTF-8, patch and commit message should be encoded in UTF-8\n" . $hereptr); - } - -# Check if it's the start of a commit log -# (not a header line and we haven't seen the patch filename) - if ($in_header_lines && $realfile =~ /^$/ && - !($rawline =~ /^\s+(?:\S|$)/ || - $rawline =~ /^(?:commit\b|from\b|[\w-]+:)/i)) { - $in_header_lines = 0; - $in_commit_log = 1; - $has_commit_log = 1; - } - -# Check if there is UTF-8 in a commit log when a mail header has explicitly -# declined it, i.e defined some charset where it is missing. - if ($in_header_lines && - $rawline =~ /^Content-Type:.+charset="(.+)".*$/ && - $1 !~ /utf-8/i) { - $non_utf8_charset = 1; - } - - if ($in_commit_log && $non_utf8_charset && $realfile =~ /^$/ && - $rawline =~ /$NON_ASCII_UTF8/) { - WARN("UTF8_BEFORE_PATCH", - "8-bit UTF-8 used in possible commit log\n" . $herecurr); - } - -# Check for absolute kernel paths in commit message - if ($tree && $in_commit_log) { - while ($line =~ m{(?:^|\s)(/\S*)}g) { - my $file = $1; - - if ($file =~ m{^(.*?)(?::\d+)+:?$} && - check_absolute_file($1, $herecurr)) { - # - } else { - check_absolute_file($file, $herecurr); - } - } - } - -# Check for various typo / spelling mistakes - if (defined($misspellings) && - ($in_commit_log || $line =~ /^(?:\+|Subject:)/i)) { - while ($rawline =~ /(?:^|[^a-z@])($misspellings)(?:\b|$|[^a-z@])/gi) { - my $typo = $1; - my $typo_fix = $spelling_fix{lc($typo)}; - $typo_fix = ucfirst($typo_fix) if ($typo =~ /^[A-Z]/); - $typo_fix = uc($typo_fix) if ($typo =~ /^[A-Z]+$/); - my $msg_level = \&WARN; - $msg_level = \&CHK if ($file); - if (&{$msg_level}("TYPO_SPELLING", - "'$typo' may be misspelled - perhaps '$typo_fix'?\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/(^|[^A-Za-z@])($typo)($|[^A-Za-z@])/$1$typo_fix$3/; - } - } - } - -# check for invalid commit id - if ($in_commit_log && $line =~ /(^fixes:|\bcommit)\s+([0-9a-f]{6,40})\b/i) { - my $id; - my $description; - ($id, $description) = git_commit_info($2, undef, undef); - if (!defined($id)) { - WARN("UNKNOWN_COMMIT_ID", - "Unknown commit id '$2', maybe rebased or not pulled?\n" . $herecurr); - } - } - -# ignore non-hunk lines and lines being removed - next if (!$hunk_line || $line =~ /^-/); - -#trailing whitespace - if ($line =~ /^\+.*\015/) { - my $herevet = "$here\n" . cat_vet($rawline) . "\n"; - if (ERROR("DOS_LINE_ENDINGS", - "DOS line endings\n" . $herevet) && - $fix) { - $fixed[$fixlinenr] =~ s/[\s\015]+$//; - } - } elsif ($rawline =~ /^\+.*\S\s+$/ || $rawline =~ /^\+\s+$/) { - my $herevet = "$here\n" . cat_vet($rawline) . "\n"; - if (ERROR("TRAILING_WHITESPACE", - "trailing whitespace\n" . $herevet) && - $fix) { - $fixed[$fixlinenr] =~ s/\s+$//; - } - - $rpt_cleaners = 1; - } - -# Check for FSF mailing addresses. - if ($rawline =~ /\bwrite to the Free/i || - $rawline =~ /\b675\s+Mass\s+Ave/i || - $rawline =~ /\b59\s+Temple\s+Pl/i || - $rawline =~ /\b51\s+Franklin\s+St/i) { - my $herevet = "$here\n" . cat_vet($rawline) . "\n"; - my $msg_level = \&ERROR; - $msg_level = \&CHK if ($file); - &{$msg_level}("FSF_MAILING_ADDRESS", - "Do not include the paragraph about writing to the Free Software Foundation's mailing address from the sample GPL notice. The FSF has changed addresses in the past, and may do so again. Linux already includes a copy of the GPL.\n" . $herevet) - } - -# check for Kconfig help text having a real description -# Only applies when adding the entry originally, after that we do not have -# sufficient context to determine whether it is indeed long enough. - if ($realfile =~ /Kconfig/ && - # 'choice' is usually the last thing on the line (though - # Kconfig supports named choices), so use a word boundary - # (\b) rather than a whitespace character (\s) - $line =~ /^\+\s*(?:config|menuconfig|choice)\b/) { - my $length = 0; - my $cnt = $realcnt; - my $ln = $linenr + 1; - my $f; - my $is_start = 0; - my $is_end = 0; - for (; $cnt > 0 && defined $lines[$ln - 1]; $ln++) { - $f = $lines[$ln - 1]; - $cnt-- if ($lines[$ln - 1] !~ /^-/); - $is_end = $lines[$ln - 1] =~ /^\+/; - - next if ($f =~ /^-/); - last if (!$file && $f =~ /^\@\@/); - - if ($lines[$ln - 1] =~ /^\+\s*(?:bool|tristate|prompt)\s*["']/) { - $is_start = 1; - } elsif ($lines[$ln - 1] =~ /^\+\s*(?:help|---help---)\s*$/) { - if ($lines[$ln - 1] =~ "---help---") { - WARN("CONFIG_DESCRIPTION", - "prefer 'help' over '---help---' for new help texts\n" . $herecurr); - } - $length = -1; - } - - $f =~ s/^.//; - $f =~ s/#.*//; - $f =~ s/^\s+//; - next if ($f =~ /^$/); - - # This only checks context lines in the patch - # and so hopefully shouldn't trigger false - # positives, even though some of these are - # common words in help texts - if ($f =~ /^\s*(?:config|menuconfig|choice|endchoice| - if|endif|menu|endmenu|source)\b/x) { - $is_end = 1; - last; - } - $length++; - } - if ($is_start && $is_end && $length < $min_conf_desc_length) { - WARN("CONFIG_DESCRIPTION", - "please write a paragraph that describes the config symbol fully\n" . $herecurr); - } - #print "is_start<$is_start> is_end<$is_end> length<$length>\n"; - } - -# check for MAINTAINERS entries that don't have the right form - if ($realfile =~ /^MAINTAINERS$/ && - $rawline =~ /^\+[A-Z]:/ && - $rawline !~ /^\+[A-Z]:\t\S/) { - if (WARN("MAINTAINERS_STYLE", - "MAINTAINERS entries use one tab after TYPE:\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/^(\+[A-Z]):\s*/$1:\t/; - } - } - -# discourage the use of boolean for type definition attributes of Kconfig options - if ($realfile =~ /Kconfig/ && - $line =~ /^\+\s*\bboolean\b/) { - WARN("CONFIG_TYPE_BOOLEAN", - "Use of boolean is deprecated, please use bool instead.\n" . $herecurr); - } - - if (($realfile =~ /Makefile.*/ || $realfile =~ /Kbuild.*/) && - ($line =~ /\+(EXTRA_[A-Z]+FLAGS).*/)) { - my $flag = $1; - my $replacement = { - 'EXTRA_AFLAGS' => 'asflags-y', - 'EXTRA_CFLAGS' => 'ccflags-y', - 'EXTRA_CPPFLAGS' => 'cppflags-y', - 'EXTRA_LDFLAGS' => 'ldflags-y', - }; - - WARN("DEPRECATED_VARIABLE", - "Use of $flag is deprecated, please use \`$replacement->{$flag} instead.\n" . $herecurr) if ($replacement->{$flag}); - } - -# check for DT compatible documentation - if (defined $root && - (($realfile =~ /\.dtsi?$/ && $line =~ /^\+\s*compatible\s*=\s*\"/) || - ($realfile =~ /\.[ch]$/ && $line =~ /^\+.*\.compatible\s*=\s*\"/))) { - - my @compats = $rawline =~ /\"([a-zA-Z0-9\-\,\.\+_]+)\"/g; - - my $dt_path = $root . "/Documentation/devicetree/bindings/"; - my $vp_file = $dt_path . "vendor-prefixes.yaml"; - - foreach my $compat (@compats) { - my $compat2 = $compat; - $compat2 =~ s/\,[a-zA-Z0-9]*\-/\,<\.\*>\-/; - my $compat3 = $compat; - $compat3 =~ s/\,([a-z]*)[0-9]*\-/\,$1<\.\*>\-/; - `grep -Erq "$compat|$compat2|$compat3" $dt_path`; - if ( $? >> 8 ) { - WARN("UNDOCUMENTED_DT_STRING", - "DT compatible string \"$compat\" appears un-documented -- check $dt_path\n" . $herecurr); - } - - next if $compat !~ /^([a-zA-Z0-9\-]+)\,/; - my $vendor = $1; - `grep -Eq "\\"\\^\Q$vendor\E,\\.\\*\\":" $vp_file`; - if ( $? >> 8 ) { - WARN("UNDOCUMENTED_DT_STRING", - "DT compatible string vendor \"$vendor\" appears un-documented -- check $vp_file\n" . $herecurr); - } - } - } - -# check for using SPDX license tag at beginning of files - if ($realline == $checklicenseline) { - if ($rawline =~ /^[ \+]\s*\#\!\s*\//) { - $checklicenseline = 2; - } elsif ($rawline =~ /^\+/) { - my $comment = ""; - if ($realfile =~ /\.(h|s|S)$/) { - $comment = '/*'; - } elsif ($realfile =~ /\.(c|dts|dtsi)$/) { - $comment = '//'; - } elsif (($checklicenseline == 2) || $realfile =~ /\.(sh|pl|py|awk|tc)$/) { - $comment = '#'; - } elsif ($realfile =~ /\.rst$/) { - $comment = '..'; - } - -# check SPDX comment style for .[chsS] files - if ($realfile =~ /\.[chsS]$/ && - $rawline =~ /SPDX-License-Identifier:/ && - $rawline !~ m@^\+\s*\Q$comment\E\s*@) { - WARN("SPDX_LICENSE_TAG", - "Improper SPDX comment style for '$realfile', please use '$comment' instead\n" . $herecurr); - } - - if ($comment !~ /^$/ && - $rawline !~ m@^\+\Q$comment\E SPDX-License-Identifier: @) { - WARN("SPDX_LICENSE_TAG", - "Missing or malformed SPDX-License-Identifier tag in line $checklicenseline\n" . $herecurr); - } elsif ($rawline =~ /(SPDX-License-Identifier: .*)/) { - my $spdx_license = $1; - if (!is_SPDX_License_valid($spdx_license)) { - WARN("SPDX_LICENSE_TAG", - "'$spdx_license' is not supported in LICENSES/...\n" . $herecurr); - } - } - } - } - -# check we are in a valid source file if not then ignore this hunk - next if ($realfile !~ /\.(h|c|s|S|sh|dtsi|dts)$/); - -# check for using SPDX-License-Identifier on the wrong line number - if ($realline != $checklicenseline && - $rawline =~ /\bSPDX-License-Identifier:/ && - substr($line, @-, @+ - @-) eq "$;" x (@+ - @-)) { - WARN("SPDX_LICENSE_TAG", - "Misplaced SPDX-License-Identifier tag - use line $checklicenseline instead\n" . $herecurr); - } - -# line length limit (with some exclusions) -# -# There are a few types of lines that may extend beyond $max_line_length: -# logging functions like pr_info that end in a string -# lines with a single string -# #defines that are a single string -# lines with an RFC3986 like URL -# -# There are 3 different line length message types: -# LONG_LINE_COMMENT a comment starts before but extends beyond $max_line_length -# LONG_LINE_STRING a string starts before but extends beyond $max_line_length -# LONG_LINE all other lines longer than $max_line_length -# -# if LONG_LINE is ignored, the other 2 types are also ignored -# - - if ($line =~ /^\+/ && $length > $max_line_length) { - my $msg_type = "LONG_LINE"; - - # Check the allowed long line types first - - # logging functions that end in a string that starts - # before $max_line_length - if ($line =~ /^\+\s*$logFunctions\s*\(\s*(?:(?:KERN_\S+\s*|[^"]*))?($String\s*(?:|,|\)\s*;)\s*)$/ && - length(expand_tabs(substr($line, 1, length($line) - length($1) - 1))) <= $max_line_length) { - $msg_type = ""; - - # lines with only strings (w/ possible termination) - # #defines with only strings - } elsif ($line =~ /^\+\s*$String\s*(?:\s*|,|\)\s*;)\s*$/ || - $line =~ /^\+\s*#\s*define\s+\w+\s+$String$/) { - $msg_type = ""; - - # More special cases - } elsif ($line =~ /^\+.*\bEFI_GUID\s*\(/ || - $line =~ /^\+\s*(?:\w+)?\s*DEFINE_PER_CPU/) { - $msg_type = ""; - - # URL ($rawline is used in case the URL is in a comment) - } elsif ($rawline =~ /^\+.*\b[a-z][\w\.\+\-]*:\/\/\S+/i) { - $msg_type = ""; - - # Otherwise set the alternate message types - - # a comment starts before $max_line_length - } elsif ($line =~ /($;[\s$;]*)$/ && - length(expand_tabs(substr($line, 1, length($line) - length($1) - 1))) <= $max_line_length) { - $msg_type = "LONG_LINE_COMMENT" - - # a quoted string starts before $max_line_length - } elsif ($sline =~ /\s*($String(?:\s*(?:\\|,\s*|\)\s*;\s*))?)$/ && - length(expand_tabs(substr($line, 1, length($line) - length($1) - 1))) <= $max_line_length) { - $msg_type = "LONG_LINE_STRING" - } - - if ($msg_type ne "" && - (show_type("LONG_LINE") || show_type($msg_type))) { - my $msg_level = \&WARN; - $msg_level = \&CHK if ($file); - &{$msg_level}($msg_type, - "line length of $length exceeds $max_line_length columns\n" . $herecurr); - } - } - -# check for adding lines without a newline. - if ($line =~ /^\+/ && defined $lines[$linenr] && $lines[$linenr] =~ /^\\ No newline at end of file/) { - WARN("MISSING_EOF_NEWLINE", - "adding a line without newline at end of file\n" . $herecurr); - } - -# check we are in a valid source file C or perl if not then ignore this hunk - next if ($realfile !~ /\.(h|c|pl|dtsi|dts)$/); - -# at the beginning of a line any tabs must come first and anything -# more than 8 must use tabs. - if ($rawline =~ /^\+\s* \t\s*\S/ || - $rawline =~ /^\+\s* \s*/) { - my $herevet = "$here\n" . cat_vet($rawline) . "\n"; - $rpt_cleaners = 1; - if (ERROR("CODE_INDENT", - "code indent should use tabs where possible\n" . $herevet) && - $fix) { - $fixed[$fixlinenr] =~ s/^\+([ \t]+)/"\+" . tabify($1)/e; - } - } - -# check for space before tabs. - if ($rawline =~ /^\+/ && $rawline =~ / \t/) { - my $herevet = "$here\n" . cat_vet($rawline) . "\n"; - if (WARN("SPACE_BEFORE_TAB", - "please, no space before tabs\n" . $herevet) && - $fix) { - while ($fixed[$fixlinenr] =~ - s/(^\+.*) {8,8}\t/$1\t\t/) {} - while ($fixed[$fixlinenr] =~ - s/(^\+.*) +\t/$1\t/) {} - } - } - -# check for assignments on the start of a line - if ($sline =~ /^\+\s+($Assignment)[^=]/) { - CHK("ASSIGNMENT_CONTINUATIONS", - "Assignment operator '$1' should be on the previous line\n" . $hereprev); - } - -# check for && or || at the start of a line - if ($rawline =~ /^\+\s*(&&|\|\|)/) { - CHK("LOGICAL_CONTINUATIONS", - "Logical continuations should be on the previous line\n" . $hereprev); - } - -# check indentation starts on a tab stop - if ($perl_version_ok && - $sline =~ /^\+\t+( +)(?:$c90_Keywords\b|\{\s*$|\}\s*(?:else\b|while\b|\s*$)|$Declare\s*$Ident\s*[;=])/) { - my $indent = length($1); - if ($indent % 8) { - if (WARN("TABSTOP", - "Statements should start on a tabstop\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s@(^\+\t+) +@$1 . "\t" x ($indent/8)@e; - } - } - } - -# check multi-line statement indentation matches previous line - if ($perl_version_ok && - $prevline =~ /^\+([ \t]*)((?:$c90_Keywords(?:\s+if)\s*)|(?:$Declare\s*)?(?:$Ident|\(\s*\*\s*$Ident\s*\))\s*|(?:\*\s*)*$Lval\s*=\s*$Ident\s*)\(.*(\&\&|\|\||,)\s*$/) { - $prevline =~ /^\+(\t*)(.*)$/; - my $oldindent = $1; - my $rest = $2; - - my $pos = pos_last_openparen($rest); - if ($pos >= 0) { - $line =~ /^(\+| )([ \t]*)/; - my $newindent = $2; - - my $goodtabindent = $oldindent . - "\t" x ($pos / 8) . - " " x ($pos % 8); - my $goodspaceindent = $oldindent . " " x $pos; - - if ($newindent ne $goodtabindent && - $newindent ne $goodspaceindent) { - - if (CHK("PARENTHESIS_ALIGNMENT", - "Alignment should match open parenthesis\n" . $hereprev) && - $fix && $line =~ /^\+/) { - $fixed[$fixlinenr] =~ - s/^\+[ \t]*/\+$goodtabindent/; - } - } - } - } - -# check for space after cast like "(int) foo" or "(struct foo) bar" -# avoid checking a few false positives: -# "sizeof(<type>)" or "__alignof__(<type>)" -# function pointer declarations like "(*foo)(int) = bar;" -# structure definitions like "(struct foo) { 0 };" -# multiline macros that define functions -# known attributes or the __attribute__ keyword - if ($line =~ /^\+(.*)\(\s*$Type\s*\)([ \t]++)((?![={]|\\$|$Attribute|__attribute__))/ && - (!defined($1) || $1 !~ /\b(?:sizeof|__alignof__)\s*$/)) { - if (CHK("SPACING", - "No space is necessary after a cast\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ - s/(\(\s*$Type\s*\))[ \t]+/$1/; - } - } - -# Block comment styles -# Networking with an initial /* - if ($realfile =~ m@^(drivers/net/|net/)@ && - $prevrawline =~ /^\+[ \t]*\/\*[ \t]*$/ && - $rawline =~ /^\+[ \t]*\*/ && - $realline > 2) { - WARN("NETWORKING_BLOCK_COMMENT_STYLE", - "networking block comments don't use an empty /* line, use /* Comment...\n" . $hereprev); - } - -# UAPI ABI version - if ($SOF && $realfile ne $last_abi_file && - $realfile =~ m@^(src/include/ipc/|src/include/kernel/|src/include/user/)@ && - $rawline =~ /^\+/ && - !$reported_abi_update) { - $last_abi_file = $realfile; - WARN("ABI update ??", - "Please update ABI in accordance with http://semver.org\n" . $hereprev); - } - -# Block comments use * on subsequent lines - if ($prevline =~ /$;[ \t]*$/ && #ends in comment - $prevrawline =~ /^\+.*?\/\*/ && #starting /* - $prevrawline !~ /\*\/[ \t]*$/ && #no trailing */ - $rawline =~ /^\+/ && #line is new - $rawline !~ /^\+[ \t]*\*/) { #no leading * - WARN("BLOCK_COMMENT_STYLE", - "Block comments use * on subsequent lines\n" . $hereprev); - } - -# Block comments use */ on trailing lines - if ($rawline !~ m@^\+[ \t]*\*/[ \t]*$@ && #trailing */ - $rawline !~ m@^\+.*/\*.*\*/[ \t]*$@ && #inline /*...*/ - $rawline !~ m@^\+.*\*{2,}/[ \t]*$@ && #trailing **/ - $rawline =~ m@^\+[ \t]*.+\*\/[ \t]*$@) { #non blank */ - WARN("BLOCK_COMMENT_STYLE", - "Block comments use a trailing */ on a separate line\n" . $herecurr); - } - -# Block comment * alignment - if ($prevline =~ /$;[ \t]*$/ && #ends in comment - $line =~ /^\+[ \t]*$;/ && #leading comment - $rawline =~ /^\+[ \t]*\*/ && #leading * - (($prevrawline =~ /^\+.*?\/\*/ && #leading /* - $prevrawline !~ /\*\/[ \t]*$/) || #no trailing */ - $prevrawline =~ /^\+[ \t]*\*/)) { #leading * - my $oldindent; - $prevrawline =~ m@^\+([ \t]*/?)\*@; - if (defined($1)) { - $oldindent = expand_tabs($1); - } else { - $prevrawline =~ m@^\+(.*/?)\*@; - $oldindent = expand_tabs($1); - } - $rawline =~ m@^\+([ \t]*)\*@; - my $newindent = $1; - $newindent = expand_tabs($newindent); - if (length($oldindent) ne length($newindent)) { - WARN("BLOCK_COMMENT_STYLE", - "Block comments should align the * on each line\n" . $hereprev); - } - } - -# check for missing blank lines after struct/union declarations -# with exceptions for various attributes and macros - if ($prevline =~ /^[\+ ]};?\s*$/ && - $line =~ /^\+/ && - !($line =~ /^\+\s*$/ || - $line =~ /^\+\s*EXPORT_SYMBOL/ || - $line =~ /^\+\s*MODULE_/i || - $line =~ /^\+\s*\#\s*(?:end|elif|else)/ || - $line =~ /^\+[a-z_]*init/ || - $line =~ /^\+\s*(?:static\s+)?[A-Z_]*ATTR/ || - $line =~ /^\+\s*DECLARE/ || - $line =~ /^\+\s*builtin_[\w_]*driver/ || - $line =~ /^\+\s*__setup/)) { - if (CHK("LINE_SPACING", - "Please use a blank line after function/struct/union/enum declarations\n" . $hereprev) && - $fix) { - fix_insert_line($fixlinenr, "\+"); - } - } - -# check for multiple consecutive blank lines - if ($prevline =~ /^[\+ ]\s*$/ && - $line =~ /^\+\s*$/ && - $last_blank_line != ($linenr - 1)) { - if (CHK("LINE_SPACING", - "Please don't use multiple blank lines\n" . $hereprev) && - $fix) { - fix_delete_line($fixlinenr, $rawline); - } - - $last_blank_line = $linenr; - } - -# check for missing blank lines after declarations - if ($sline =~ /^\+\s+\S/ && #Not at char 1 - # actual declarations - ($prevline =~ /^\+\s+$Declare\s*$Ident\s*[=,;:\[]/ || - # function pointer declarations - $prevline =~ /^\+\s+$Declare\s*\(\s*\*\s*$Ident\s*\)\s*[=,;:\[\(]/ || - # foo bar; where foo is some local typedef or #define - $prevline =~ /^\+\s+$Ident(?:\s+|\s*\*\s*)$Ident\s*[=,;\[]/ || - # known declaration macros - $prevline =~ /^\+\s+$declaration_macros/) && - # for "else if" which can look like "$Ident $Ident" - !($prevline =~ /^\+\s+$c90_Keywords\b/ || - # other possible extensions of declaration lines - $prevline =~ /(?:$Compare|$Assignment|$Operators)\s*$/ || - # not starting a section or a macro "\" extended line - $prevline =~ /(?:\{\s*|\\)$/) && - # looks like a declaration - !($sline =~ /^\+\s+$Declare\s*$Ident\s*[=,;:\[]/ || - # function pointer declarations - $sline =~ /^\+\s+$Declare\s*\(\s*\*\s*$Ident\s*\)\s*[=,;:\[\(]/ || - # foo bar; where foo is some local typedef or #define - $sline =~ /^\+\s+$Ident(?:\s+|\s*\*\s*)$Ident\s*[=,;\[]/ || - # known declaration macros - $sline =~ /^\+\s+$declaration_macros/ || - # start of struct or union or enum - $sline =~ /^\+\s+(?:static\s+)?(?:const\s+)?(?:union|struct|enum|typedef)\b/ || - # start or end of block or continuation of declaration - $sline =~ /^\+\s+(?:$|[\{\}\.\#\"\?\:\(\[])/ || - # bitfield continuation - $sline =~ /^\+\s+$Ident\s*:\s*\d+\s*[,;]/ || - # other possible extensions of declaration lines - $sline =~ /^\+\s+\(?\s*(?:$Compare|$Assignment|$Operators)/) && - # indentation of previous and current line are the same - (($prevline =~ /\+(\s+)\S/) && $sline =~ /^\+$1\S/)) { - if (WARN("LINE_SPACING", - "Missing a blank line after declarations\n" . $hereprev) && - $fix) { - fix_insert_line($fixlinenr, "\+"); - } - } - -# check for spaces at the beginning of a line. -# Exceptions: -# 1) within comments -# 2) indented preprocessor commands -# 3) hanging labels - if ($rawline =~ /^\+ / && $line !~ /^\+ *(?:$;|#|$Ident:)/) { - my $herevet = "$here\n" . cat_vet($rawline) . "\n"; - if (WARN("LEADING_SPACE", - "please, no spaces at the start of a line\n" . $herevet) && - $fix) { - $fixed[$fixlinenr] =~ s/^\+([ \t]+)/"\+" . tabify($1)/e; - } - } - -# check we are in a valid C source file if not then ignore this hunk - next if ($realfile !~ /\.(h|c)$/); - -# check for unusual line ending [ or ( - if ($line =~ /^\+.*([\[\(])\s*$/) { - CHK("OPEN_ENDED_LINE", - "Lines should not end with a '$1'\n" . $herecurr); - } - -# check if this appears to be the start function declaration, save the name - if ($sline =~ /^\+\{\s*$/ && - $prevline =~ /^\+(?:(?:(?:$Storage|$Inline)\s*)*\s*$Type\s*)?($Ident)\(/) { - $context_function = $1; - } - -# check if this appears to be the end of function declaration - if ($sline =~ /^\+\}\s*$/) { - undef $context_function; - } - -# check indentation of any line with a bare else -# (but not if it is a multiple line "if (foo) return bar; else return baz;") -# if the previous line is a break or return and is indented 1 tab more... - if ($sline =~ /^\+([\t]+)(?:}[ \t]*)?else(?:[ \t]*{)?\s*$/) { - my $tabs = length($1) + 1; - if ($prevline =~ /^\+\t{$tabs,$tabs}break\b/ || - ($prevline =~ /^\+\t{$tabs,$tabs}return\b/ && - defined $lines[$linenr] && - $lines[$linenr] !~ /^[ \+]\t{$tabs,$tabs}return/)) { - WARN("UNNECESSARY_ELSE", - "else is not generally useful after a break or return\n" . $hereprev); - } - } - -# check indentation of a line with a break; -# if the previous line is a goto or return and is indented the same # of tabs - if ($sline =~ /^\+([\t]+)break\s*;\s*$/) { - my $tabs = $1; - if ($prevline =~ /^\+$tabs(?:goto|return)\b/) { - WARN("UNNECESSARY_BREAK", - "break is not useful after a goto or return\n" . $hereprev); - } - } - -# check for RCS/CVS revision markers - if ($rawline =~ /^\+.*\$(Revision|Log|Id)(?:\$|)/) { - WARN("CVS_KEYWORD", - "CVS style keyword markers, these will _not_ be updated\n". $herecurr); - } - -# check for old HOTPLUG __dev<foo> section markings - if ($line =~ /\b(__dev(init|exit)(data|const|))\b/) { - WARN("HOTPLUG_SECTION", - "Using $1 is unnecessary\n" . $herecurr); - } - -# Check for potential 'bare' types - my ($stat, $cond, $line_nr_next, $remain_next, $off_next, - $realline_next); -#print "LINE<$line>\n"; - if ($linenr > $suppress_statement && - $realcnt && $sline =~ /.\s*\S/) { - ($stat, $cond, $line_nr_next, $remain_next, $off_next) = - ctx_statement_block($linenr, $realcnt, 0); - $stat =~ s/\n./\n /g; - $cond =~ s/\n./\n /g; - -#print "linenr<$linenr> <$stat>\n"; - # If this statement has no statement boundaries within - # it there is no point in retrying a statement scan - # until we hit end of it. - my $frag = $stat; $frag =~ s/;+\s*$//; - if ($frag !~ /(?:{|;)/) { -#print "skip<$line_nr_next>\n"; - $suppress_statement = $line_nr_next; - } - - # Find the real next line. - $realline_next = $line_nr_next; - if (defined $realline_next && - (!defined $lines[$realline_next - 1] || - substr($lines[$realline_next - 1], $off_next) =~ /^\s*$/)) { - $realline_next++; - } - - my $s = $stat; - $s =~ s/{.*$//s; - - # Ignore goto labels. - if ($s =~ /$Ident:\*$/s) { - - # Ignore functions being called - } elsif ($s =~ /^.\s*$Ident\s*\(/s) { - - } elsif ($s =~ /^.\s*else\b/s) { - - # declarations always start with types - } elsif ($prev_values eq 'E' && $s =~ /^.\s*(?:$Storage\s+)?(?:$Inline\s+)?(?:const\s+)?((?:\s*$Ident)+?)\b(?:\s+$Sparse)?\s*\**\s*(?:$Ident|\(\*[^\)]*\))(?:\s*$Modifier)?\s*(?:;|=|,|\()/s) { - my $type = $1; - $type =~ s/\s+/ /g; - possible($type, "A:" . $s); - - # definitions in global scope can only start with types - } elsif ($s =~ /^.(?:$Storage\s+)?(?:$Inline\s+)?(?:const\s+)?($Ident)\b\s*(?!:)/s) { - possible($1, "B:" . $s); - } - - # any (foo ... *) is a pointer cast, and foo is a type - while ($s =~ /\(($Ident)(?:\s+$Sparse)*[\s\*]+\s*\)/sg) { - possible($1, "C:" . $s); - } - - # Check for any sort of function declaration. - # int foo(something bar, other baz); - # void (*store_gdt)(x86_descr_ptr *); - if ($prev_values eq 'E' && $s =~ /^(.(?:typedef\s*)?(?:(?:$Storage|$Inline)\s*)*\s*$Type\s*(?:\b$Ident|\(\*\s*$Ident\))\s*)\(/s) { - my ($name_len) = length($1); - - my $ctx = $s; - substr($ctx, 0, $name_len + 1, ''); - $ctx =~ s/\)[^\)]*$//; - - for my $arg (split(/\s*,\s*/, $ctx)) { - if ($arg =~ /^(?:const\s+)?($Ident)(?:\s+$Sparse)*\s*\**\s*(:?\b$Ident)?$/s || $arg =~ /^($Ident)$/s) { - - possible($1, "D:" . $s); - } - } - } - - } - -# -# Checks which may be anchored in the context. -# - -# Check for switch () and associated case and default -# statements should be at the same indent. - if ($line=~/\bswitch\s*\(.*\)/) { - my $err = ''; - my $sep = ''; - my @ctx = ctx_block_outer($linenr, $realcnt); - shift(@ctx); - for my $ctx (@ctx) { - my ($clen, $cindent) = line_stats($ctx); - if ($ctx =~ /^\+\s*(case\s+|default:)/ && - $indent != $cindent) { - $err .= "$sep$ctx\n"; - $sep = ''; - } else { - $sep = "[...]\n"; - } - } - if ($err ne '') { - ERROR("SWITCH_CASE_INDENT_LEVEL", - "switch and case should be at the same indent\n$hereline$err"); - } - } - -# if/while/etc brace do not go on next line, unless defining a do while loop, -# or if that brace on the next line is for something else - if ($line =~ /(.*)\b((?:if|while|for|switch|(?:[a-z_]+|)for_each[a-z_]+)\s*\(|do\b|else\b)/ && $line !~ /^.\s*\#/) { - my $pre_ctx = "$1$2"; - - my ($level, @ctx) = ctx_statement_level($linenr, $realcnt, 0); - - if ($line =~ /^\+\t{6,}/) { - WARN("DEEP_INDENTATION", - "Too many leading tabs - consider code refactoring\n" . $herecurr); - } - - my $ctx_cnt = $realcnt - $#ctx - 1; - my $ctx = join("\n", @ctx); - - my $ctx_ln = $linenr; - my $ctx_skip = $realcnt; - - while ($ctx_skip > $ctx_cnt || ($ctx_skip == $ctx_cnt && - defined $lines[$ctx_ln - 1] && - $lines[$ctx_ln - 1] =~ /^-/)) { - ##print "SKIP<$ctx_skip> CNT<$ctx_cnt>\n"; - $ctx_skip-- if (!defined $lines[$ctx_ln - 1] || $lines[$ctx_ln - 1] !~ /^-/); - $ctx_ln++; - } - - #print "realcnt<$realcnt> ctx_cnt<$ctx_cnt>\n"; - #print "pre<$pre_ctx>\nline<$line>\nctx<$ctx>\nnext<$lines[$ctx_ln - 1]>\n"; - - if ($ctx !~ /{\s*/ && defined($lines[$ctx_ln - 1]) && $lines[$ctx_ln - 1] =~ /^\+\s*{/) { - ERROR("OPEN_BRACE", - "that open brace { should be on the previous line\n" . - "$here\n$ctx\n$rawlines[$ctx_ln - 1]\n"); - } - if ($level == 0 && $pre_ctx !~ /}\s*while\s*\($/ && - $ctx =~ /\)\s*\;\s*$/ && - defined $lines[$ctx_ln - 1]) - { - my ($nlength, $nindent) = line_stats($lines[$ctx_ln - 1]); - if ($nindent > $indent) { - WARN("TRAILING_SEMICOLON", - "trailing semicolon indicates no statements, indent implies otherwise\n" . - "$here\n$ctx\n$rawlines[$ctx_ln - 1]\n"); - } - } - } - -# Check relative indent for conditionals and blocks. - if ($line =~ /\b(?:(?:if|while|for|(?:[a-z_]+|)for_each[a-z_]+)\s*\(|(?:do|else)\b)/ && $line !~ /^.\s*#/ && $line !~ /\}\s*while\s*/) { - ($stat, $cond, $line_nr_next, $remain_next, $off_next) = - ctx_statement_block($linenr, $realcnt, 0) - if (!defined $stat); - my ($s, $c) = ($stat, $cond); - - substr($s, 0, length($c), ''); - - # remove inline comments - $s =~ s/$;/ /g; - $c =~ s/$;/ /g; - - # Find out how long the conditional actually is. - my @newlines = ($c =~ /\n/gs); - my $cond_lines = 1 + $#newlines; - - # Make sure we remove the line prefixes as we have - # none on the first line, and are going to readd them - # where necessary. - $s =~ s/\n./\n/gs; - while ($s =~ /\n\s+\\\n/) { - $cond_lines += $s =~ s/\n\s+\\\n/\n/g; - } - - # We want to check the first line inside the block - # starting at the end of the conditional, so remove: - # 1) any blank line termination - # 2) any opening brace { on end of the line - # 3) any do (...) { - my $continuation = 0; - my $check = 0; - $s =~ s/^.*\bdo\b//; - $s =~ s/^\s*{//; - if ($s =~ s/^\s*\\//) { - $continuation = 1; - } - if ($s =~ s/^\s*?\n//) { - $check = 1; - $cond_lines++; - } - - # Also ignore a loop construct at the end of a - # preprocessor statement. - if (($prevline =~ /^.\s*#\s*define\s/ || - $prevline =~ /\\\s*$/) && $continuation == 0) { - $check = 0; - } - - my $cond_ptr = -1; - $continuation = 0; - while ($cond_ptr != $cond_lines) { - $cond_ptr = $cond_lines; - - # If we see an #else/#elif then the code - # is not linear. - if ($s =~ /^\s*\#\s*(?:else|elif)/) { - $check = 0; - } - - # Ignore: - # 1) blank lines, they should be at 0, - # 2) preprocessor lines, and - # 3) labels. - if ($continuation || - $s =~ /^\s*?\n/ || - $s =~ /^\s*#\s*?/ || - $s =~ /^\s*$Ident\s*:/) { - $continuation = ($s =~ /^.*?\\\n/) ? 1 : 0; - if ($s =~ s/^.*?\n//) { - $cond_lines++; - } - } - } - - my (undef, $sindent) = line_stats("+" . $s); - my $stat_real = raw_line($linenr, $cond_lines); - - # Check if either of these lines are modified, else - # this is not this patch's fault. - if (!defined($stat_real) || - $stat !~ /^\+/ && $stat_real !~ /^\+/) { - $check = 0; - } - if (defined($stat_real) && $cond_lines > 1) { - $stat_real = "[...]\n$stat_real"; - } - - #print "line<$line> prevline<$prevline> indent<$indent> sindent<$sindent> check<$check> continuation<$continuation> s<$s> cond_lines<$cond_lines> stat_real<$stat_real> stat<$stat>\n"; - - if ($check && $s ne '' && - (($sindent % 8) != 0 || - ($sindent < $indent) || - ($sindent == $indent && - ($s !~ /^\s*(?:\}|\{|else\b)/)) || - ($sindent > $indent + 8))) { - WARN("SUSPECT_CODE_INDENT", - "suspect code indent for conditional statements ($indent, $sindent)\n" . $herecurr . "$stat_real\n"); - } - } - - # Track the 'values' across context and added lines. - my $opline = $line; $opline =~ s/^./ /; - my ($curr_values, $curr_vars) = - annotate_values($opline . "\n", $prev_values); - $curr_values = $prev_values . $curr_values; - if ($dbg_values) { - my $outline = $opline; $outline =~ s/\t/ /g; - print "$linenr > .$outline\n"; - print "$linenr > $curr_values\n"; - print "$linenr > $curr_vars\n"; - } - $prev_values = substr($curr_values, -1); - -#ignore lines not being added - next if ($line =~ /^[^\+]/); - -# check for dereferences that span multiple lines - if ($prevline =~ /^\+.*$Lval\s*(?:\.|->)\s*$/ && - $line =~ /^\+\s*(?!\#\s*(?!define\s+|if))\s*$Lval/) { - $prevline =~ /($Lval\s*(?:\.|->))\s*$/; - my $ref = $1; - $line =~ /^.\s*($Lval)/; - $ref .= $1; - $ref =~ s/\s//g; - WARN("MULTILINE_DEREFERENCE", - "Avoid multiple line dereference - prefer '$ref'\n" . $hereprev); - } - -# check for declarations of signed or unsigned without int - while ($line =~ m{\b($Declare)\s*(?!char\b|short\b|int\b|long\b)\s*($Ident)?\s*[=,;\[\)\(]}g) { - my $type = $1; - my $var = $2; - $var = "" if (!defined $var); - if ($type =~ /^(?:(?:$Storage|$Inline|$Attribute)\s+)*((?:un)?signed)((?:\s*\*)*)\s*$/) { - my $sign = $1; - my $pointer = $2; - - $pointer = "" if (!defined $pointer); - - if (WARN("UNSPECIFIED_INT", - "Prefer '" . trim($sign) . " int" . rtrim($pointer) . "' to bare use of '$sign" . rtrim($pointer) . "'\n" . $herecurr) && - $fix) { - my $decl = trim($sign) . " int "; - my $comp_pointer = $pointer; - $comp_pointer =~ s/\s//g; - $decl .= $comp_pointer; - $decl = rtrim($decl) if ($var eq ""); - $fixed[$fixlinenr] =~ s@\b$sign\s*\Q$pointer\E\s*$var\b@$decl$var@; - } - } - } - -# TEST: allow direct testing of the type matcher. - if ($dbg_type) { - if ($line =~ /^.\s*$Declare\s*$/) { - ERROR("TEST_TYPE", - "TEST: is type\n" . $herecurr); - } elsif ($dbg_type > 1 && $line =~ /^.+($Declare)/) { - ERROR("TEST_NOT_TYPE", - "TEST: is not type ($1 is)\n". $herecurr); - } - next; - } -# TEST: allow direct testing of the attribute matcher. - if ($dbg_attr) { - if ($line =~ /^.\s*$Modifier\s*$/) { - ERROR("TEST_ATTR", - "TEST: is attr\n" . $herecurr); - } elsif ($dbg_attr > 1 && $line =~ /^.+($Modifier)/) { - ERROR("TEST_NOT_ATTR", - "TEST: is not attr ($1 is)\n". $herecurr); - } - next; - } - -# check for initialisation to aggregates open brace on the next line - if ($line =~ /^.\s*{/ && - $prevline =~ /(?:^|[^=])=\s*$/) { - if (ERROR("OPEN_BRACE", - "that open brace { should be on the previous line\n" . $hereprev) && - $fix && $prevline =~ /^\+/ && $line =~ /^\+/) { - fix_delete_line($fixlinenr - 1, $prevrawline); - fix_delete_line($fixlinenr, $rawline); - my $fixedline = $prevrawline; - $fixedline =~ s/\s*=\s*$/ = {/; - fix_insert_line($fixlinenr, $fixedline); - $fixedline = $line; - $fixedline =~ s/^(.\s*)\{\s*/$1/; - fix_insert_line($fixlinenr, $fixedline); - } - } - -# -# Checks which are anchored on the added line. -# - -# check for malformed paths in #include statements (uses RAW line) - if ($rawline =~ m{^.\s*\#\s*include\s+[<"](.*)[">]}) { - my $path = $1; - if ($path =~ m{//}) { - ERROR("MALFORMED_INCLUDE", - "malformed #include filename\n" . $herecurr); - } - if ($path =~ "^uapi/" && $realfile =~ m@\binclude/uapi/@) { - ERROR("UAPI_INCLUDE", - "No #include in ...include/uapi/... should use a uapi/ path prefix\n" . $herecurr); - } - } - -# no C99 // comments - if ($line =~ m{//}) { - if (ERROR("C99_COMMENTS", - "do not use C99 // comments\n" . $herecurr) && - $fix) { - my $line = $fixed[$fixlinenr]; - if ($line =~ /\/\/(.*)$/) { - my $comment = trim($1); - $fixed[$fixlinenr] =~ s@\/\/(.*)$@/\* $comment \*/@; - } - } - } - # Remove C99 comments. - $line =~ s@//.*@@; - $opline =~ s@//.*@@; - -# EXPORT_SYMBOL should immediately follow the thing it is exporting, consider -# the whole statement. -#print "APW <$lines[$realline_next - 1]>\n"; - if (defined $realline_next && - exists $lines[$realline_next - 1] && - !defined $suppress_export{$realline_next} && - ($lines[$realline_next - 1] =~ /EXPORT_SYMBOL.*\((.*)\)/ || - $lines[$realline_next - 1] =~ /EXPORT_UNUSED_SYMBOL.*\((.*)\)/)) { - # Handle definitions which produce identifiers with - # a prefix: - # XXX(foo); - # EXPORT_SYMBOL(something_foo); - my $name = $1; - if ($stat =~ /^(?:.\s*}\s*\n)?.([A-Z_]+)\s*\(\s*($Ident)/ && - $name =~ /^${Ident}_$2/) { -#print "FOO C name<$name>\n"; - $suppress_export{$realline_next} = 1; - - } elsif ($stat !~ /(?: - \n.}\s*$| - ^.DEFINE_$Ident\(\Q$name\E\)| - ^.DECLARE_$Ident\(\Q$name\E\)| - ^.LIST_HEAD\(\Q$name\E\)| - ^.(?:$Storage\s+)?$Type\s*\(\s*\*\s*\Q$name\E\s*\)\s*\(| - \b\Q$name\E(?:\s+$Attribute)*\s*(?:;|=|\[|\() - )/x) { -#print "FOO A<$lines[$realline_next - 1]> stat<$stat> name<$name>\n"; - $suppress_export{$realline_next} = 2; - } else { - $suppress_export{$realline_next} = 1; - } - } - if (!defined $suppress_export{$linenr} && - $prevline =~ /^.\s*$/ && - ($line =~ /EXPORT_SYMBOL.*\((.*)\)/ || - $line =~ /EXPORT_UNUSED_SYMBOL.*\((.*)\)/)) { -#print "FOO B <$lines[$linenr - 1]>\n"; - $suppress_export{$linenr} = 2; - } - if (defined $suppress_export{$linenr} && - $suppress_export{$linenr} == 2) { - WARN("EXPORT_SYMBOL", - "EXPORT_SYMBOL(foo); should immediately follow its function/variable\n" . $herecurr); - } - -# check for global initialisers. - if ($line =~ /^\+$Type\s*$Ident(?:\s+$Modifier)*\s*=\s*($zero_initializer)\s*;/) { - if (ERROR("GLOBAL_INITIALISERS", - "do not initialise globals to $1\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/(^.$Type\s*$Ident(?:\s+$Modifier)*)\s*=\s*$zero_initializer\s*;/$1;/; - } - } -# check for static initialisers. - if ($line =~ /^\+.*\bstatic\s.*=\s*($zero_initializer)\s*;/) { - if (ERROR("INITIALISED_STATIC", - "do not initialise statics to $1\n" . - $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/(\bstatic\s.*?)\s*=\s*$zero_initializer\s*;/$1;/; - } - } - -# check for misordered declarations of char/short/int/long with signed/unsigned - while ($sline =~ m{(\b$TypeMisordered\b)}g) { - my $tmp = trim($1); - WARN("MISORDERED_TYPE", - "type '$tmp' should be specified in [[un]signed] [short|int|long|long long] order\n" . $herecurr); - } - -# check for unnecessary <signed> int declarations of short/long/long long - while ($sline =~ m{\b($TypeMisordered(\s*\*)*|$C90_int_types)\b}g) { - my $type = trim($1); - next if ($type !~ /\bint\b/); - next if ($type !~ /\b(?:short|long\s+long|long)\b/); - my $new_type = $type; - $new_type =~ s/\b\s*int\s*\b/ /; - $new_type =~ s/\b\s*(?:un)?signed\b\s*/ /; - $new_type =~ s/^const\s+//; - $new_type = "unsigned $new_type" if ($type =~ /\bunsigned\b/); - $new_type = "const $new_type" if ($type =~ /^const\b/); - $new_type =~ s/\s+/ /g; - $new_type = trim($new_type); - if (WARN("UNNECESSARY_INT", - "Prefer '$new_type' over '$type' as the int is unnecessary\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/\b\Q$type\E\b/$new_type/; - } - } - -# check for static const char * arrays. - if ($line =~ /\bstatic\s+const\s+char\s*\*\s*(\w+)\s*\[\s*\]\s*=\s*/) { - WARN("STATIC_CONST_CHAR_ARRAY", - "static const char * array should probably be static const char * const\n" . - $herecurr); - } - -# check for initialized const char arrays that should be static const - if ($line =~ /^\+\s*const\s+(char|unsigned\s+char|_*u8|(?:[us]_)?int8_t)\s+\w+\s*\[\s*(?:\w+\s*)?\]\s*=\s*"/) { - if (WARN("STATIC_CONST_CHAR_ARRAY", - "const array should probably be static const\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/(^.\s*)const\b/${1}static const/; - } - } - -# check for static char foo[] = "bar" declarations. - if ($line =~ /\bstatic\s+char\s+(\w+)\s*\[\s*\]\s*=\s*"/) { - WARN("STATIC_CONST_CHAR_ARRAY", - "static char array declaration should probably be static const char\n" . - $herecurr); - } - -# check for const <foo> const where <foo> is not a pointer or array type - if ($sline =~ /\bconst\s+($BasicType)\s+const\b/) { - my $found = $1; - if ($sline =~ /\bconst\s+\Q$found\E\s+const\b\s*\*/) { - WARN("CONST_CONST", - "'const $found const *' should probably be 'const $found * const'\n" . $herecurr); - } elsif ($sline !~ /\bconst\s+\Q$found\E\s+const\s+\w+\s*\[/) { - WARN("CONST_CONST", - "'const $found const' should probably be 'const $found'\n" . $herecurr); - } - } - -# check for non-global char *foo[] = {"bar", ...} declarations. - if ($line =~ /^.\s+(?:static\s+|const\s+)?char\s+\*\s*\w+\s*\[\s*\]\s*=\s*\{/) { - WARN("STATIC_CONST_CHAR_ARRAY", - "char * array declaration might be better as static const\n" . - $herecurr); - } - -# check for sizeof(foo)/sizeof(foo[0]) that could be ARRAY_SIZE(foo) - if ($line =~ m@\bsizeof\s*\(\s*($Lval)\s*\)@) { - my $array = $1; - if ($line =~ m@\b(sizeof\s*\(\s*\Q$array\E\s*\)\s*/\s*sizeof\s*\(\s*\Q$array\E\s*\[\s*0\s*\]\s*\))@) { - my $array_div = $1; - if (WARN("ARRAY_SIZE", - "Prefer ARRAY_SIZE($array)\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/\Q$array_div\E/ARRAY_SIZE($array)/; - } - } - } - -# check for function declarations without arguments like "int foo()" - if ($line =~ /(\b$Type\s+$Ident)\s*\(\s*\)/) { - if (ERROR("FUNCTION_WITHOUT_ARGS", - "Bad function definition - $1() should probably be $1(void)\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/(\b($Type)\s+($Ident))\s*\(\s*\)/$2 $3(void)/; - } - } - -# check for new typedefs, only function parameters and sparse annotations -# make sense. - if ($line =~ /\btypedef\s/ && - $line !~ /\btypedef\s+$Type\s*\(\s*\*?$Ident\s*\)\s*\(/ && - $line !~ /\btypedef\s+$Type\s+$Ident\s*\(/ && - $line !~ /\b$typeTypedefs\b/ && - $line !~ /\b__bitwise\b/) { - WARN("NEW_TYPEDEFS", - "do not add new typedefs\n" . $herecurr); - } - -# * goes on variable not on type - # (char*[ const]) - while ($line =~ m{(\($NonptrType(\s*(?:$Modifier\b\s*|\*\s*)+)\))}g) { - #print "AA<$1>\n"; - my ($ident, $from, $to) = ($1, $2, $2); - - # Should start with a space. - $to =~ s/^(\S)/ $1/; - # Should not end with a space. - $to =~ s/\s+$//; - # '*'s should not have spaces between. - while ($to =~ s/\*\s+\*/\*\*/) { - } - -## print "1: from<$from> to<$to> ident<$ident>\n"; - if ($from ne $to) { - if (ERROR("POINTER_LOCATION", - "\"(foo$from)\" should be \"(foo$to)\"\n" . $herecurr) && - $fix) { - my $sub_from = $ident; - my $sub_to = $ident; - $sub_to =~ s/\Q$from\E/$to/; - $fixed[$fixlinenr] =~ - s@\Q$sub_from\E@$sub_to@; - } - } - } - while ($line =~ m{(\b$NonptrType(\s*(?:$Modifier\b\s*|\*\s*)+)($Ident))}g) { - #print "BB<$1>\n"; - my ($match, $from, $to, $ident) = ($1, $2, $2, $3); - - # Should start with a space. - $to =~ s/^(\S)/ $1/; - # Should not end with a space. - $to =~ s/\s+$//; - # '*'s should not have spaces between. - while ($to =~ s/\*\s+\*/\*\*/) { - } - # Modifiers should have spaces. - $to =~ s/(\b$Modifier$)/$1 /; - -## print "2: from<$from> to<$to> ident<$ident>\n"; - if ($from ne $to && $ident !~ /^$Modifier$/) { - if (ERROR("POINTER_LOCATION", - "\"foo${from}bar\" should be \"foo${to}bar\"\n" . $herecurr) && - $fix) { - - my $sub_from = $match; - my $sub_to = $match; - $sub_to =~ s/\Q$from\E/$to/; - $fixed[$fixlinenr] =~ - s@\Q$sub_from\E@$sub_to@; - } - } - } - -# avoid BUG() or BUG_ON() - if ($line =~ /\b(?:BUG|BUG_ON)\b/) { - my $msg_level = \&WARN; - $msg_level = \&CHK if ($file); - &{$msg_level}("AVOID_BUG", - "Avoid crashing the kernel - try using WARN_ON & recovery code rather than BUG() or BUG_ON()\n" . $herecurr); - } - -# avoid LINUX_VERSION_CODE - if ($line =~ /\bLINUX_VERSION_CODE\b/) { - WARN("LINUX_VERSION_CODE", - "LINUX_VERSION_CODE should be avoided, code should be for the version to which it is merged\n" . $herecurr); - } - -# check for uses of printk_ratelimit - if ($line =~ /\bprintk_ratelimit\s*\(/) { - WARN("PRINTK_RATELIMITED", - "Prefer printk_ratelimited or pr_<level>_ratelimited to printk_ratelimit\n" . $herecurr); - } - -# printk should use KERN_* levels - if ($line =~ /\bprintk\s*\(\s*(?!KERN_[A-Z]+\b)/) { - WARN("PRINTK_WITHOUT_KERN_LEVEL", - "printk() should include KERN_<LEVEL> facility level\n" . $herecurr); - } - - if ($line =~ /\bprintk\s*\(\s*KERN_([A-Z]+)/) { - my $orig = $1; - my $level = lc($orig); - $level = "warn" if ($level eq "warning"); - my $level2 = $level; - $level2 = "dbg" if ($level eq "debug"); - WARN("PREFER_PR_LEVEL", - "Prefer [subsystem eg: netdev]_$level2([subsystem]dev, ... then dev_$level2(dev, ... then pr_$level(... to printk(KERN_$orig ...\n" . $herecurr); - } - - if ($line =~ /\bpr_warning\s*\(/) { - if (WARN("PREFER_PR_LEVEL", - "Prefer pr_warn(... to pr_warning(...\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ - s/\bpr_warning\b/pr_warn/; - } - } - - if ($line =~ /\bdev_printk\s*\(\s*KERN_([A-Z]+)/) { - my $orig = $1; - my $level = lc($orig); - $level = "warn" if ($level eq "warning"); - $level = "dbg" if ($level eq "debug"); - WARN("PREFER_DEV_LEVEL", - "Prefer dev_$level(... to dev_printk(KERN_$orig, ...\n" . $herecurr); - } - -# ENOSYS means "bad syscall nr" and nothing else. This will have a small -# number of false positives, but assembly files are not checked, so at -# least the arch entry code will not trigger this warning. - if ($line =~ /\bENOSYS\b/) { - WARN("ENOSYS", - "ENOSYS means 'invalid syscall nr' and nothing else\n" . $herecurr); - } - -# function brace can't be on same line, except for #defines of do while, -# or if closed on same line - if ($perl_version_ok && - $sline =~ /$Type\s*$Ident\s*$balanced_parens\s*\{/ && - $sline !~ /\#\s*define\b.*do\s*\{/ && - $sline !~ /}/) { - if (ERROR("OPEN_BRACE", - "open brace '{' following function definitions go on the next line\n" . $herecurr) && - $fix) { - fix_delete_line($fixlinenr, $rawline); - my $fixed_line = $rawline; - $fixed_line =~ /(^..*$Type\s*$Ident\(.*\)\s*){(.*)$/; - my $line1 = $1; - my $line2 = $2; - fix_insert_line($fixlinenr, ltrim($line1)); - fix_insert_line($fixlinenr, "\+{"); - if ($line2 !~ /^\s*$/) { - fix_insert_line($fixlinenr, "\+\t" . trim($line2)); - } - } - } - -# open braces for enum, union and struct go on the same line. - if ($line =~ /^.\s*{/ && - $prevline =~ /^.\s*(?:typedef\s+)?(enum|union|struct)(?:\s+$Ident)?\s*$/) { - if (ERROR("OPEN_BRACE", - "open brace '{' following $1 go on the same line\n" . $hereprev) && - $fix && $prevline =~ /^\+/ && $line =~ /^\+/) { - fix_delete_line($fixlinenr - 1, $prevrawline); - fix_delete_line($fixlinenr, $rawline); - my $fixedline = rtrim($prevrawline) . " {"; - fix_insert_line($fixlinenr, $fixedline); - $fixedline = $rawline; - $fixedline =~ s/^(.\s*)\{\s*/$1\t/; - if ($fixedline !~ /^\+\s*$/) { - fix_insert_line($fixlinenr, $fixedline); - } - } - } - -# missing space after union, struct or enum definition - if ($line =~ /^.\s*(?:typedef\s+)?(enum|union|struct)(?:\s+$Ident){1,2}[=\{]/) { - if (WARN("SPACING", - "missing space after $1 definition\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ - s/^(.\s*(?:typedef\s+)?(?:enum|union|struct)(?:\s+$Ident){1,2})([=\{])/$1 $2/; - } - } - -# Function pointer declarations -# check spacing between type, funcptr, and args -# canonical declaration is "type (*funcptr)(args...)" - if ($line =~ /^.\s*($Declare)\((\s*)\*(\s*)($Ident)(\s*)\)(\s*)\(/) { - my $declare = $1; - my $pre_pointer_space = $2; - my $post_pointer_space = $3; - my $funcname = $4; - my $post_funcname_space = $5; - my $pre_args_space = $6; - -# the $Declare variable will capture all spaces after the type -# so check it for a missing trailing missing space but pointer return types -# don't need a space so don't warn for those. - my $post_declare_space = ""; - if ($declare =~ /(\s+)$/) { - $post_declare_space = $1; - $declare = rtrim($declare); - } - if ($declare !~ /\*$/ && $post_declare_space =~ /^$/) { - WARN("SPACING", - "missing space after return type\n" . $herecurr); - $post_declare_space = " "; - } - -# unnecessary space "type (*funcptr)(args...)" -# This test is not currently implemented because these declarations are -# equivalent to -# int foo(int bar, ...) -# and this is form shouldn't/doesn't generate a checkpatch warning. -# -# elsif ($declare =~ /\s{2,}$/) { -# WARN("SPACING", -# "Multiple spaces after return type\n" . $herecurr); -# } - -# unnecessary space "type ( *funcptr)(args...)" - if (defined $pre_pointer_space && - $pre_pointer_space =~ /^\s/) { - WARN("SPACING", - "Unnecessary space after function pointer open parenthesis\n" . $herecurr); - } - -# unnecessary space "type (* funcptr)(args...)" - if (defined $post_pointer_space && - $post_pointer_space =~ /^\s/) { - WARN("SPACING", - "Unnecessary space before function pointer name\n" . $herecurr); - } - -# unnecessary space "type (*funcptr )(args...)" - if (defined $post_funcname_space && - $post_funcname_space =~ /^\s/) { - WARN("SPACING", - "Unnecessary space after function pointer name\n" . $herecurr); - } - -# unnecessary space "type (*funcptr) (args...)" - if (defined $pre_args_space && - $pre_args_space =~ /^\s/) { - WARN("SPACING", - "Unnecessary space before function pointer arguments\n" . $herecurr); - } - - if (show_type("SPACING") && $fix) { - $fixed[$fixlinenr] =~ - s/^(.\s*)$Declare\s*\(\s*\*\s*$Ident\s*\)\s*\(/$1 . $declare . $post_declare_space . '(*' . $funcname . ')('/ex; - } - } - -# check for spacing round square brackets; allowed: -# 1. with a type on the left -- int [] a; -# 2. at the beginning of a line for slice initialisers -- [0...10] = 5, -# 3. inside a curly brace -- = { [0...10] = 5 } - while ($line =~ /(.*?\s)\[/g) { - my ($where, $prefix) = ($-[1], $1); - if ($prefix !~ /$Type\s+$/ && - ($where != 0 || $prefix !~ /^.\s+$/) && - $prefix !~ /[{,:]\s+$/) { - if (ERROR("BRACKET_SPACE", - "space prohibited before open square bracket '['\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ - s/^(\+.*?)\s+\[/$1\[/; - } - } - } - -# check for spaces between functions and their parentheses. - while ($line =~ /($Ident)\s+\(/g) { - my $name = $1; - my $ctx_before = substr($line, 0, $-[1]); - my $ctx = "$ctx_before$name"; - - # Ignore those directives where spaces _are_ permitted. - if ($name =~ /^(?: - if|for|while|switch|return|case| - volatile|__volatile__| - __attribute__|format|__extension__| - asm|__asm__)$/x) - { - # cpp #define statements have non-optional spaces, ie - # if there is a space between the name and the open - # parenthesis it is simply not a parameter group. - } elsif ($ctx_before =~ /^.\s*\#\s*define\s*$/) { - - # cpp #elif statement condition may start with a ( - } elsif ($ctx =~ /^.\s*\#\s*elif\s*$/) { - - # If this whole things ends with a type its most - # likely a typedef for a function. - } elsif ($ctx =~ /$Type$/) { - - } else { - if (WARN("SPACING", - "space prohibited between function name and open parenthesis '('\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ - s/\b$name\s+\(/$name\(/; - } - } - } - -# Check operator spacing. - if (!($line=~/\#\s*include/)) { - my $fixed_line = ""; - my $line_fixed = 0; - - my $ops = qr{ - <<=|>>=|<=|>=|==|!=| - \+=|-=|\*=|\/=|%=|\^=|\|=|&=| - =>|->|<<|>>|<|>|=|!|~| - &&|\|\||,|\^|\+\+|--|&|\||\+|-|\*|\/|%| - \?:|\?|: - }x; - my @elements = split(/($ops|;)/, $opline); - -## print("element count: <" . $#elements . ">\n"); -## foreach my $el (@elements) { -## print("el: <$el>\n"); -## } - - my @fix_elements = (); - my $off = 0; - - foreach my $el (@elements) { - push(@fix_elements, substr($rawline, $off, length($el))); - $off += length($el); - } - - $off = 0; - - my $blank = copy_spacing($opline); - my $last_after = -1; - - for (my $n = 0; $n < $#elements; $n += 2) { - - my $good = $fix_elements[$n] . $fix_elements[$n + 1]; - -## print("n: <$n> good: <$good>\n"); - - $off += length($elements[$n]); - - # Pick up the preceding and succeeding characters. - my $ca = substr($opline, 0, $off); - my $cc = ''; - if (length($opline) >= ($off + length($elements[$n + 1]))) { - $cc = substr($opline, $off + length($elements[$n + 1])); - } - my $cb = "$ca$;$cc"; - - my $a = ''; - $a = 'V' if ($elements[$n] ne ''); - $a = 'W' if ($elements[$n] =~ /\s$/); - $a = 'C' if ($elements[$n] =~ /$;$/); - $a = 'B' if ($elements[$n] =~ /(\[|\()$/); - $a = 'O' if ($elements[$n] eq ''); - $a = 'E' if ($ca =~ /^\s*$/); - - my $op = $elements[$n + 1]; - - my $c = ''; - if (defined $elements[$n + 2]) { - $c = 'V' if ($elements[$n + 2] ne ''); - $c = 'W' if ($elements[$n + 2] =~ /^\s/); - $c = 'C' if ($elements[$n + 2] =~ /^$;/); - $c = 'B' if ($elements[$n + 2] =~ /^(\)|\]|;)/); - $c = 'O' if ($elements[$n + 2] eq ''); - $c = 'E' if ($elements[$n + 2] =~ /^\s*\\$/); - } else { - $c = 'E'; - } - - my $ctx = "${a}x${c}"; - - my $at = "(ctx:$ctx)"; - - my $ptr = substr($blank, 0, $off) . "^"; - my $hereptr = "$hereline$ptr\n"; - - # Pull out the value of this operator. - my $op_type = substr($curr_values, $off + 1, 1); - - # Get the full operator variant. - my $opv = $op . substr($curr_vars, $off, 1); - - # Ignore operators passed as parameters. - if ($op_type ne 'V' && - $ca =~ /\s$/ && $cc =~ /^\s*[,\)]/) { - -# # Ignore comments -# } elsif ($op =~ /^$;+$/) { - - # ; should have either the end of line or a space or \ after it - } elsif ($op eq ';') { - if ($ctx !~ /.x[WEBC]/ && - $cc !~ /^\\/ && $cc !~ /^;/) { - if (ERROR("SPACING", - "space required after that '$op' $at\n" . $hereptr)) { - $good = $fix_elements[$n] . trim($fix_elements[$n + 1]) . " "; - $line_fixed = 1; - } - } - - # // is a comment - } elsif ($op eq '//') { - - # : when part of a bitfield - } elsif ($opv eq ':B') { - # skip the bitfield test for now - - # No spaces for: - # -> - } elsif ($op eq '->') { - if ($ctx =~ /Wx.|.xW/) { - if (ERROR("SPACING", - "spaces prohibited around that '$op' $at\n" . $hereptr)) { - $good = rtrim($fix_elements[$n]) . trim($fix_elements[$n + 1]); - if (defined $fix_elements[$n + 2]) { - $fix_elements[$n + 2] =~ s/^\s+//; - } - $line_fixed = 1; - } - } - - # , must not have a space before and must have a space on the right. - } elsif ($op eq ',') { - my $rtrim_before = 0; - my $space_after = 0; - if ($ctx =~ /Wx./) { - if (ERROR("SPACING", - "space prohibited before that '$op' $at\n" . $hereptr)) { - $line_fixed = 1; - $rtrim_before = 1; - } - } - if ($ctx !~ /.x[WEC]/ && $cc !~ /^}/) { - if (ERROR("SPACING", - "space required after that '$op' $at\n" . $hereptr)) { - $line_fixed = 1; - $last_after = $n; - $space_after = 1; - } - } - if ($rtrim_before || $space_after) { - if ($rtrim_before) { - $good = rtrim($fix_elements[$n]) . trim($fix_elements[$n + 1]); - } else { - $good = $fix_elements[$n] . trim($fix_elements[$n + 1]); - } - if ($space_after) { - $good .= " "; - } - } - - # '*' as part of a type definition -- reported already. - } elsif ($opv eq '*_') { - #warn "'*' is part of type\n"; - - # unary operators should have a space before and - # none after. May be left adjacent to another - # unary operator, or a cast - } elsif ($op eq '!' || $op eq '~' || - $opv eq '*U' || $opv eq '-U' || - $opv eq '&U' || $opv eq '&&U') { - if ($ctx !~ /[WEBC]x./ && $ca !~ /(?:\)|!|~|\*|-|\&|\||\+\+|\-\-|\{)$/) { - if (ERROR("SPACING", - "space required before that '$op' $at\n" . $hereptr)) { - if ($n != $last_after + 2) { - $good = $fix_elements[$n] . " " . ltrim($fix_elements[$n + 1]); - $line_fixed = 1; - } - } - } - if ($op eq '*' && $cc =~/\s*$Modifier\b/) { - # A unary '*' may be const - - } elsif ($ctx =~ /.xW/) { - if (ERROR("SPACING", - "space prohibited after that '$op' $at\n" . $hereptr)) { - $good = $fix_elements[$n] . rtrim($fix_elements[$n + 1]); - if (defined $fix_elements[$n + 2]) { - $fix_elements[$n + 2] =~ s/^\s+//; - } - $line_fixed = 1; - } - } - - # unary ++ and unary -- are allowed no space on one side. - } elsif ($op eq '++' or $op eq '--') { - if ($ctx !~ /[WEOBC]x[^W]/ && $ctx !~ /[^W]x[WOBEC]/) { - if (ERROR("SPACING", - "space required one side of that '$op' $at\n" . $hereptr)) { - $good = $fix_elements[$n] . trim($fix_elements[$n + 1]) . " "; - $line_fixed = 1; - } - } - if ($ctx =~ /Wx[BE]/ || - ($ctx =~ /Wx./ && $cc =~ /^;/)) { - if (ERROR("SPACING", - "space prohibited before that '$op' $at\n" . $hereptr)) { - $good = rtrim($fix_elements[$n]) . trim($fix_elements[$n + 1]); - $line_fixed = 1; - } - } - if ($ctx =~ /ExW/) { - if (ERROR("SPACING", - "space prohibited after that '$op' $at\n" . $hereptr)) { - $good = $fix_elements[$n] . trim($fix_elements[$n + 1]); - if (defined $fix_elements[$n + 2]) { - $fix_elements[$n + 2] =~ s/^\s+//; - } - $line_fixed = 1; - } - } - - # << and >> may either have or not have spaces both sides - } elsif ($op eq '<<' or $op eq '>>' or - $op eq '&' or $op eq '^' or $op eq '|' or - $op eq '+' or $op eq '-' or - $op eq '*' or $op eq '/' or - $op eq '%') - { - if ($check) { - if (defined $fix_elements[$n + 2] && $ctx !~ /[EW]x[EW]/) { - if (CHK("SPACING", - "spaces preferred around that '$op' $at\n" . $hereptr)) { - $good = rtrim($fix_elements[$n]) . " " . trim($fix_elements[$n + 1]) . " "; - $fix_elements[$n + 2] =~ s/^\s+//; - $line_fixed = 1; - } - } elsif (!defined $fix_elements[$n + 2] && $ctx !~ /Wx[OE]/) { - if (CHK("SPACING", - "space preferred before that '$op' $at\n" . $hereptr)) { - $good = rtrim($fix_elements[$n]) . " " . trim($fix_elements[$n + 1]); - $line_fixed = 1; - } - } - } elsif ($ctx =~ /Wx[^WCE]|[^WCE]xW/) { - if (ERROR("SPACING", - "need consistent spacing around '$op' $at\n" . $hereptr)) { - $good = rtrim($fix_elements[$n]) . " " . trim($fix_elements[$n + 1]) . " "; - if (defined $fix_elements[$n + 2]) { - $fix_elements[$n + 2] =~ s/^\s+//; - } - $line_fixed = 1; - } - } - - # A colon needs no spaces before when it is - # terminating a case value or a label. - } elsif ($opv eq ':C' || $opv eq ':L') { - if ($ctx =~ /Wx./) { - if (ERROR("SPACING", - "space prohibited before that '$op' $at\n" . $hereptr)) { - $good = rtrim($fix_elements[$n]) . trim($fix_elements[$n + 1]); - $line_fixed = 1; - } - } - - # All the others need spaces both sides. - } elsif ($ctx !~ /[EWC]x[CWE]/) { - my $ok = 0; - - # Ignore email addresses <foo@bar> - if (($op eq '<' && - $cc =~ /^\S+\@\S+>/) || - ($op eq '>' && - $ca =~ /<\S+\@\S+$/)) - { - $ok = 1; - } - - # for asm volatile statements - # ignore a colon with another - # colon immediately before or after - if (($op eq ':') && - ($ca =~ /:$/ || $cc =~ /^:/)) { - $ok = 1; - } - - # messages are ERROR, but ?: are CHK - if ($ok == 0) { - my $msg_level = \&ERROR; - $msg_level = \&CHK if (($op eq '?:' || $op eq '?' || $op eq ':') && $ctx =~ /VxV/); - - if (&{$msg_level}("SPACING", - "spaces required around that '$op' $at\n" . $hereptr)) { - $good = rtrim($fix_elements[$n]) . " " . trim($fix_elements[$n + 1]) . " "; - if (defined $fix_elements[$n + 2]) { - $fix_elements[$n + 2] =~ s/^\s+//; - } - $line_fixed = 1; - } - } - } - $off += length($elements[$n + 1]); - -## print("n: <$n> GOOD: <$good>\n"); - - $fixed_line = $fixed_line . $good; - } - - if (($#elements % 2) == 0) { - $fixed_line = $fixed_line . $fix_elements[$#elements]; - } - - if ($fix && $line_fixed && $fixed_line ne $fixed[$fixlinenr]) { - $fixed[$fixlinenr] = $fixed_line; - } - - - } - -# check for whitespace before a non-naked semicolon - if ($line =~ /^\+.*\S\s+;\s*$/) { - if (WARN("SPACING", - "space prohibited before semicolon\n" . $herecurr) && - $fix) { - 1 while $fixed[$fixlinenr] =~ - s/^(\+.*\S)\s+;/$1;/; - } - } - -# check for multiple assignments - if ($line =~ /^.\s*$Lval\s*=\s*$Lval\s*=(?!=)/) { - CHK("MULTIPLE_ASSIGNMENTS", - "multiple assignments should be avoided\n" . $herecurr); - } - -## # check for multiple declarations, allowing for a function declaration -## # continuation. -## if ($line =~ /^.\s*$Type\s+$Ident(?:\s*=[^,{]*)?\s*,\s*$Ident.*/ && -## $line !~ /^.\s*$Type\s+$Ident(?:\s*=[^,{]*)?\s*,\s*$Type\s*$Ident.*/) { -## -## # Remove any bracketed sections to ensure we do not -## # falsly report the parameters of functions. -## my $ln = $line; -## while ($ln =~ s/\([^\(\)]*\)//g) { -## } -## if ($ln =~ /,/) { -## WARN("MULTIPLE_DECLARATION", -## "declaring multiple variables together should be avoided\n" . $herecurr); -## } -## } - -#need space before brace following if, while, etc - if (($line =~ /\(.*\)\{/ && $line !~ /\($Type\)\{/) || - $line =~ /\b(?:else|do)\{/) { - if (ERROR("SPACING", - "space required before the open brace '{'\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/^(\+.*(?:do|else|\)))\{/$1 {/; - } - } - -## # check for blank lines before declarations -## if ($line =~ /^.\t+$Type\s+$Ident(?:\s*=.*)?;/ && -## $prevrawline =~ /^.\s*$/) { -## WARN("SPACING", -## "No blank lines before declarations\n" . $hereprev); -## } -## - -# closing brace should have a space following it when it has anything -# on the line - if ($line =~ /}(?!(?:,|;|\)|\}))\S/) { - if (ERROR("SPACING", - "space required after that close brace '}'\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ - s/}((?!(?:,|;|\)))\S)/} $1/; - } - } - -# check spacing on square brackets - if ($line =~ /\[\s/ && $line !~ /\[\s*$/) { - if (ERROR("SPACING", - "space prohibited after that open square bracket '['\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ - s/\[\s+/\[/; - } - } - if ($line =~ /\s\]/) { - if (ERROR("SPACING", - "space prohibited before that close square bracket ']'\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ - s/\s+\]/\]/; - } - } - -# check spacing on parentheses - if ($line =~ /\(\s/ && $line !~ /\(\s*(?:\\)?$/ && - $line !~ /for\s*\(\s+;/) { - if (ERROR("SPACING", - "space prohibited after that open parenthesis '('\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ - s/\(\s+/\(/; - } - } - if ($line =~ /(\s+)\)/ && $line !~ /^.\s*\)/ && - $line !~ /for\s*\(.*;\s+\)/ && - $line !~ /:\s+\)/) { - if (ERROR("SPACING", - "space prohibited before that close parenthesis ')'\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ - s/\s+\)/\)/; - } - } - -# check unnecessary parentheses around addressof/dereference single $Lvals -# ie: &(foo->bar) should be &foo->bar and *(foo->bar) should be *foo->bar - - while ($line =~ /(?:[^&]&\s*|\*)\(\s*($Ident\s*(?:$Member\s*)+)\s*\)/g) { - my $var = $1; - if (CHK("UNNECESSARY_PARENTHESES", - "Unnecessary parentheses around $var\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/\(\s*\Q$var\E\s*\)/$var/; - } - } - -# check for unnecessary parentheses around function pointer uses -# ie: (foo->bar)(); should be foo->bar(); -# but not "if (foo->bar) (" to avoid some false positives - if ($line =~ /(\bif\s*|)(\(\s*$Ident\s*(?:$Member\s*)+\))[ \t]*\(/ && $1 !~ /^if/) { - my $var = $2; - if (CHK("UNNECESSARY_PARENTHESES", - "Unnecessary parentheses around function pointer $var\n" . $herecurr) && - $fix) { - my $var2 = deparenthesize($var); - $var2 =~ s/\s//g; - $fixed[$fixlinenr] =~ s/\Q$var\E/$var2/; - } - } - -# check for unnecessary parentheses around comparisons in if uses -# when !drivers/staging or command-line uses --strict - if (($realfile !~ m@^(?:drivers/staging/)@ || $check_orig) && - $perl_version_ok && defined($stat) && - $stat =~ /(^.\s*if\s*($balanced_parens))/) { - my $if_stat = $1; - my $test = substr($2, 1, -1); - my $herectx; - while ($test =~ /(?:^|[^\w\&\!\~])+\s*\(\s*([\&\!\~]?\s*$Lval\s*(?:$Compare\s*$FuncArg)?)\s*\)/g) { - my $match = $1; - # avoid parentheses around potential macro args - next if ($match =~ /^\s*\w+\s*$/); - if (!defined($herectx)) { - $herectx = $here . "\n"; - my $cnt = statement_rawlines($if_stat); - for (my $n = 0; $n < $cnt; $n++) { - my $rl = raw_line($linenr, $n); - $herectx .= $rl . "\n"; - last if $rl =~ /^[ \+].*\{/; - } - } - CHK("UNNECESSARY_PARENTHESES", - "Unnecessary parentheses around '$match'\n" . $herectx); - } - } - -#goto labels aren't indented, allow a single space however - if ($line=~/^.\s+[A-Za-z\d_]+:(?![0-9]+)/ and - !($line=~/^. [A-Za-z\d_]+:/) and !($line=~/^.\s+default:/)) { - if (WARN("INDENTED_LABEL", - "labels should not be indented\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ - s/^(.)\s+/$1/; - } - } - -# return is not a function - if (defined($stat) && $stat =~ /^.\s*return(\s*)\(/s) { - my $spacing = $1; - if ($perl_version_ok && - $stat =~ /^.\s*return\s*($balanced_parens)\s*;\s*$/) { - my $value = $1; - $value = deparenthesize($value); - if ($value =~ m/^\s*$FuncArg\s*(?:\?|$)/) { - ERROR("RETURN_PARENTHESES", - "return is not a function, parentheses are not required\n" . $herecurr); - } - } elsif ($spacing !~ /\s+/) { - ERROR("SPACING", - "space required before the open parenthesis '('\n" . $herecurr); - } - } - -# unnecessary return in a void function -# at end-of-function, with the previous line a single leading tab, then return; -# and the line before that not a goto label target like "out:" - if ($sline =~ /^[ \+]}\s*$/ && - $prevline =~ /^\+\treturn\s*;\s*$/ && - $linenr >= 3 && - $lines[$linenr - 3] =~ /^[ +]/ && - $lines[$linenr - 3] !~ /^[ +]\s*$Ident\s*:/) { - WARN("RETURN_VOID", - "void function return statements are not generally useful\n" . $hereprev); - } - -# if statements using unnecessary parentheses - ie: if ((foo == bar)) - if ($perl_version_ok && - $line =~ /\bif\s*((?:\(\s*){2,})/) { - my $openparens = $1; - my $count = $openparens =~ tr@\(@\(@; - my $msg = ""; - if ($line =~ /\bif\s*(?:\(\s*){$count,$count}$LvalOrFunc\s*($Compare)\s*$LvalOrFunc(?:\s*\)){$count,$count}/) { - my $comp = $4; #Not $1 because of $LvalOrFunc - $msg = " - maybe == should be = ?" if ($comp eq "=="); - WARN("UNNECESSARY_PARENTHESES", - "Unnecessary parentheses$msg\n" . $herecurr); - } - } - -# comparisons with a constant or upper case identifier on the left -# avoid cases like "foo + BAR < baz" -# only fix matches surrounded by parentheses to avoid incorrect -# conversions like "FOO < baz() + 5" being "misfixed" to "baz() > FOO + 5" - if ($perl_version_ok && - $line =~ /^\+(.*)\b($Constant|[A-Z_][A-Z0-9_]*)\s*($Compare)\s*($LvalOrFunc)/) { - my $lead = $1; - my $const = $2; - my $comp = $3; - my $to = $4; - my $newcomp = $comp; - if ($lead !~ /(?:$Operators|\.)\s*$/ && - $to !~ /^(?:Constant|[A-Z_][A-Z0-9_]*)$/ && - WARN("CONSTANT_COMPARISON", - "Comparisons should place the constant on the right side of the test\n" . $herecurr) && - $fix) { - if ($comp eq "<") { - $newcomp = ">"; - } elsif ($comp eq "<=") { - $newcomp = ">="; - } elsif ($comp eq ">") { - $newcomp = "<"; - } elsif ($comp eq ">=") { - $newcomp = "<="; - } - $fixed[$fixlinenr] =~ s/\(\s*\Q$const\E\s*$Compare\s*\Q$to\E\s*\)/($to $newcomp $const)/; - } - } - -# Return of what appears to be an errno should normally be negative - if ($sline =~ /\breturn(?:\s*\(+\s*|\s+)(E[A-Z]+)(?:\s*\)+\s*|\s*)[;:,]/) { - my $name = $1; - if ($name ne 'EOF' && $name ne 'ERROR') { - WARN("USE_NEGATIVE_ERRNO", - "return of an errno should typically be negative (ie: return -$1)\n" . $herecurr); - } - } - -# Need a space before open parenthesis after if, while etc - if ($line =~ /\b(if|while|for|switch)\(/) { - if (ERROR("SPACING", - "space required before the open parenthesis '('\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ - s/\b(if|while|for|switch)\(/$1 \(/; - } - } - -# Check for illegal assignment in if conditional -- and check for trailing -# statements after the conditional. - if ($line =~ /do\s*(?!{)/) { - ($stat, $cond, $line_nr_next, $remain_next, $off_next) = - ctx_statement_block($linenr, $realcnt, 0) - if (!defined $stat); - my ($stat_next) = ctx_statement_block($line_nr_next, - $remain_next, $off_next); - $stat_next =~ s/\n./\n /g; - ##print "stat<$stat> stat_next<$stat_next>\n"; - - if ($stat_next =~ /^\s*while\b/) { - # If the statement carries leading newlines, - # then count those as offsets. - my ($whitespace) = - ($stat_next =~ /^((?:\s*\n[+-])*\s*)/s); - my $offset = - statement_rawlines($whitespace) - 1; - - $suppress_whiletrailers{$line_nr_next + - $offset} = 1; - } - } - if (!defined $suppress_whiletrailers{$linenr} && - defined($stat) && defined($cond) && - $line =~ /\b(?:if|while|for)\s*\(/ && $line !~ /^.\s*#/) { - my ($s, $c) = ($stat, $cond); - - if ($c =~ /\bif\s*\(.*[^<>!=]=[^=].*/s) { - ERROR("ASSIGN_IN_IF", - "do not use assignment in if condition\n" . $herecurr); - } - - # Find out what is on the end of the line after the - # conditional. - substr($s, 0, length($c), ''); - $s =~ s/\n.*//g; - $s =~ s/$;//g; # Remove any comments - if (length($c) && $s !~ /^\s*{?\s*\\*\s*$/ && - $c !~ /}\s*while\s*/) - { - # Find out how long the conditional actually is. - my @newlines = ($c =~ /\n/gs); - my $cond_lines = 1 + $#newlines; - my $stat_real = ''; - - $stat_real = raw_line($linenr, $cond_lines) - . "\n" if ($cond_lines); - if (defined($stat_real) && $cond_lines > 1) { - $stat_real = "[...]\n$stat_real"; - } - - ERROR("TRAILING_STATEMENTS", - "trailing statements should be on next line\n" . $herecurr . $stat_real); - } - } - -# Check for bitwise tests written as boolean - if ($line =~ / - (?: - (?:\[|\(|\&\&|\|\|) - \s*0[xX][0-9]+\s* - (?:\&\&|\|\|) - | - (?:\&\&|\|\|) - \s*0[xX][0-9]+\s* - (?:\&\&|\|\||\)|\]) - )/x) - { - WARN("HEXADECIMAL_BOOLEAN_TEST", - "boolean test with hexadecimal, perhaps just 1 \& or \|?\n" . $herecurr); - } - -# if and else should not have general statements after it - if ($line =~ /^.\s*(?:}\s*)?else\b(.*)/) { - my $s = $1; - $s =~ s/$;//g; # Remove any comments - if ($s !~ /^\s*(?:\sif|(?:{|)\s*\\?\s*$)/) { - ERROR("TRAILING_STATEMENTS", - "trailing statements should be on next line\n" . $herecurr); - } - } -# if should not continue a brace - if ($line =~ /}\s*if\b/) { - ERROR("TRAILING_STATEMENTS", - "trailing statements should be on next line (or did you mean 'else if'?)\n" . - $herecurr); - } -# case and default should not have general statements after them - if ($line =~ /^.\s*(?:case\s*.*|default\s*):/g && - $line !~ /\G(?: - (?:\s*$;*)(?:\s*{)?(?:\s*$;*)(?:\s*\\)?\s*$| - \s*return\s+ - )/xg) - { - ERROR("TRAILING_STATEMENTS", - "trailing statements should be on next line\n" . $herecurr); - } - - # Check for }<nl>else {, these must be at the same - # indent level to be relevant to each other. - if ($prevline=~/}\s*$/ and $line=~/^.\s*else\s*/ && - $previndent == $indent) { - if (ERROR("ELSE_AFTER_BRACE", - "else should follow close brace '}'\n" . $hereprev) && - $fix && $prevline =~ /^\+/ && $line =~ /^\+/) { - fix_delete_line($fixlinenr - 1, $prevrawline); - fix_delete_line($fixlinenr, $rawline); - my $fixedline = $prevrawline; - $fixedline =~ s/}\s*$//; - if ($fixedline !~ /^\+\s*$/) { - fix_insert_line($fixlinenr, $fixedline); - } - $fixedline = $rawline; - $fixedline =~ s/^(.\s*)else/$1} else/; - fix_insert_line($fixlinenr, $fixedline); - } - } - - if ($prevline=~/}\s*$/ and $line=~/^.\s*while\s*/ && - $previndent == $indent) { - my ($s, $c) = ctx_statement_block($linenr, $realcnt, 0); - - # Find out what is on the end of the line after the - # conditional. - substr($s, 0, length($c), ''); - $s =~ s/\n.*//g; - - if ($s =~ /^\s*;/) { - if (ERROR("WHILE_AFTER_BRACE", - "while should follow close brace '}'\n" . $hereprev) && - $fix && $prevline =~ /^\+/ && $line =~ /^\+/) { - fix_delete_line($fixlinenr - 1, $prevrawline); - fix_delete_line($fixlinenr, $rawline); - my $fixedline = $prevrawline; - my $trailing = $rawline; - $trailing =~ s/^\+//; - $trailing = trim($trailing); - $fixedline =~ s/}\s*$/} $trailing/; - fix_insert_line($fixlinenr, $fixedline); - } - } - } - -#Specific variable tests - while ($line =~ m{($Constant|$Lval)}g) { - my $var = $1; - -#CamelCase - if ($var !~ /^$Constant$/ && - $var =~ /[A-Z][a-z]|[a-z][A-Z]/ && -#Ignore Page<foo> variants - $var !~ /^(?:Clear|Set|TestClear|TestSet|)Page[A-Z]/ && -#Ignore SI style variants like nS, mV and dB (ie: max_uV, regulator_min_uA_show) - $var !~ /^(?:[a-z_]*?)_?[a-z][A-Z](?:_[a-z_]+)?$/ && -#Ignore some three character SI units explicitly, like MiB and KHz - $var !~ /^(?:[a-z_]*?)_?(?:[KMGT]iB|[KMGT]?Hz)(?:_[a-z_]+)?$/) { - while ($var =~ m{($Ident)}g) { - my $word = $1; - next if ($word !~ /[A-Z][a-z]|[a-z][A-Z]/); - if ($check) { - seed_camelcase_includes(); - if (!$file && !$camelcase_file_seeded) { - seed_camelcase_file($realfile); - $camelcase_file_seeded = 1; - } - } - if (!defined $camelcase{$word}) { - $camelcase{$word} = 1; - CHK("CAMELCASE", - "Avoid CamelCase: <$word>\n" . $herecurr); - } - } - } - } - -#no spaces allowed after \ in define - if ($line =~ /\#\s*define.*\\\s+$/) { - if (WARN("WHITESPACE_AFTER_LINE_CONTINUATION", - "Whitespace after \\ makes next lines useless\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/\s+$//; - } - } - -# warn if <asm/foo.h> is #included and <linux/foo.h> is available and includes -# itself <asm/foo.h> (uses RAW line) - if ($tree && $rawline =~ m{^.\s*\#\s*include\s*\<asm\/(.*)\.h\>}) { - my $file = "$1.h"; - my $checkfile = "include/linux/$file"; - if (-f "$root/$checkfile" && - $realfile ne $checkfile && - $1 !~ /$allowed_asm_includes/) - { - my $asminclude = `grep -Ec "#include\\s+<asm/$file>" $root/$checkfile`; - if ($asminclude > 0) { - if ($realfile =~ m{^arch/}) { - CHK("ARCH_INCLUDE_LINUX", - "Consider using #include <linux/$file> instead of <asm/$file>\n" . $herecurr); - } else { - WARN("INCLUDE_LINUX", - "Use #include <linux/$file> instead of <asm/$file>\n" . $herecurr); - } - } - } - } - -# multi-statement macros should be enclosed in a do while loop, grab the -# first statement and ensure its the whole macro if its not enclosed -# in a known good container - if ($realfile !~ m@/vmlinux.lds.h$@ && - $line =~ /^.\s*\#\s*define\s*$Ident(\()?/) { - my $ln = $linenr; - my $cnt = $realcnt; - my ($off, $dstat, $dcond, $rest); - my $ctx = ''; - my $has_flow_statement = 0; - my $has_arg_concat = 0; - ($dstat, $dcond, $ln, $cnt, $off) = - ctx_statement_block($linenr, $realcnt, 0); - $ctx = $dstat; - #print "dstat<$dstat> dcond<$dcond> cnt<$cnt> off<$off>\n"; - #print "LINE<$lines[$ln-1]> len<" . length($lines[$ln-1]) . "\n"; - - $has_flow_statement = 1 if ($ctx =~ /\b(goto|return)\b/); - $has_arg_concat = 1 if ($ctx =~ /\#\#/ && $ctx !~ /\#\#\s*(?:__VA_ARGS__|args)\b/); - - $dstat =~ s/^.\s*\#\s*define\s+$Ident(\([^\)]*\))?\s*//; - my $define_args = $1; - my $define_stmt = $dstat; - my @def_args = (); - - if (defined $define_args && $define_args ne "") { - $define_args = substr($define_args, 1, length($define_args) - 2); - $define_args =~ s/\s*//g; - $define_args =~ s/\\\+?//g; - @def_args = split(",", $define_args); - } - - $dstat =~ s/$;//g; - $dstat =~ s/\\\n.//g; - $dstat =~ s/^\s*//s; - $dstat =~ s/\s*$//s; - - # Flatten any parentheses and braces - while ($dstat =~ s/\([^\(\)]*\)/1/ || - $dstat =~ s/\{[^\{\}]*\}/1/ || - $dstat =~ s/.\[[^\[\]]*\]/1/) - { - } - - # Flatten any obvious string concatentation. - while ($dstat =~ s/($String)\s*$Ident/$1/ || - $dstat =~ s/$Ident\s*($String)/$1/) - { - } - - # Make asm volatile uses seem like a generic function - $dstat =~ s/\b_*asm_*\s+_*volatile_*\b/asm_volatile/g; - - my $exceptions = qr{ - $Declare| - module_param_named| - MODULE_PARM_DESC| - DECLARE_PER_CPU| - DEFINE_PER_CPU| - __typeof__\(| - union| - struct| - \.$Ident\s*=\s*| - ^\"|\"$| - ^\[ - }x; - #print "REST<$rest> dstat<$dstat> ctx<$ctx>\n"; - - $ctx =~ s/\n*$//; - my $stmt_cnt = statement_rawlines($ctx); - my $herectx = get_stat_here($linenr, $stmt_cnt, $here); - - if ($dstat ne '' && - $dstat !~ /^(?:$Ident|-?$Constant),$/ && # 10, // foo(), - $dstat !~ /^(?:$Ident|-?$Constant);$/ && # foo(); - $dstat !~ /^[!~-]?(?:$Lval|$Constant)$/ && # 10 // foo() // !foo // ~foo // -foo // foo->bar // foo.bar->baz - $dstat !~ /^'X'$/ && $dstat !~ /^'XX'$/ && # character constants - $dstat !~ /$exceptions/ && - $dstat !~ /^\.$Ident\s*=/ && # .foo = - $dstat !~ /^(?:\#\s*$Ident|\#\s*$Constant)\s*$/ && # stringification #foo - $dstat !~ /^do\s*$Constant\s*while\s*$Constant;?$/ && # do {...} while (...); // do {...} while (...) - $dstat !~ /^for\s*$Constant$/ && # for (...) - $dstat !~ /^for\s*$Constant\s+(?:$Ident|-?$Constant)$/ && # for (...) bar() - $dstat !~ /^do\s*{/ && # do {... - $dstat !~ /^\(\{/ && # ({... - $ctx !~ /^.\s*#\s*define\s+TRACE_(?:SYSTEM|INCLUDE_FILE|INCLUDE_PATH)\b/) - { - if ($dstat =~ /^\s*if\b/) { - ERROR("MULTISTATEMENT_MACRO_USE_DO_WHILE", - "Macros starting with if should be enclosed by a do - while loop to avoid possible if/else logic defects\n" . "$herectx"); - } elsif ($dstat =~ /;/) { - ERROR("MULTISTATEMENT_MACRO_USE_DO_WHILE", - "Macros with multiple statements should be enclosed in a do - while loop\n" . "$herectx"); - } else { - ERROR("COMPLEX_MACRO", - "Macros with complex values should be enclosed in parentheses\n" . "$herectx"); - } - - } - - # Make $define_stmt single line, comment-free, etc - my @stmt_array = split('\n', $define_stmt); - my $first = 1; - $define_stmt = ""; - foreach my $l (@stmt_array) { - $l =~ s/\\$//; - if ($first) { - $define_stmt = $l; - $first = 0; - } elsif ($l =~ /^[\+ ]/) { - $define_stmt .= substr($l, 1); - } - } - $define_stmt =~ s/$;//g; - $define_stmt =~ s/\s+/ /g; - $define_stmt = trim($define_stmt); - -# check if any macro arguments are reused (ignore '...' and 'type') - foreach my $arg (@def_args) { - next if ($arg =~ /\.\.\./); - next if ($arg =~ /^type$/i); - my $tmp_stmt = $define_stmt; - $tmp_stmt =~ s/\b(sizeof|typeof|__typeof__|__builtin\w+|typecheck\s*\(\s*$Type\s*,|\#+)\s*\(*\s*$arg\s*\)*\b//g; - $tmp_stmt =~ s/\#+\s*$arg\b//g; - $tmp_stmt =~ s/\b$arg\s*\#\#//g; - my $use_cnt = () = $tmp_stmt =~ /\b$arg\b/g; - if ($use_cnt > 1) { - CHK("MACRO_ARG_REUSE", - "Macro argument reuse '$arg' - possible side-effects?\n" . "$herectx"); - } -# check if any macro arguments may have other precedence issues - if ($tmp_stmt =~ m/($Operators)?\s*\b$arg\b\s*($Operators)?/m && - ((defined($1) && $1 ne ',') || - (defined($2) && $2 ne ','))) { - CHK("MACRO_ARG_PRECEDENCE", - "Macro argument '$arg' may be better as '($arg)' to avoid precedence issues\n" . "$herectx"); - } - } - -# check for macros with flow control, but without ## concatenation -# ## concatenation is commonly a macro that defines a function so ignore those - if ($has_flow_statement && !$has_arg_concat) { - my $cnt = statement_rawlines($ctx); - my $herectx = get_stat_here($linenr, $cnt, $here); - - WARN("MACRO_WITH_FLOW_CONTROL", - "Macros with flow control statements should be avoided\n" . "$herectx"); - } - -# check for line continuations outside of #defines, preprocessor #, and asm - - } else { - if ($prevline !~ /^..*\\$/ && - $line !~ /^\+\s*\#.*\\$/ && # preprocessor - $line !~ /^\+.*\b(__asm__|asm)\b.*\\$/ && # asm - $line =~ /^\+.*\\$/) { - WARN("LINE_CONTINUATIONS", - "Avoid unnecessary line continuations\n" . $herecurr); - } - } - -# do {} while (0) macro tests: -# single-statement macros do not need to be enclosed in do while (0) loop, -# macro should not end with a semicolon - if ($perl_version_ok && - $realfile !~ m@/vmlinux.lds.h$@ && - $line =~ /^.\s*\#\s*define\s+$Ident(\()?/) { - my $ln = $linenr; - my $cnt = $realcnt; - my ($off, $dstat, $dcond, $rest); - my $ctx = ''; - ($dstat, $dcond, $ln, $cnt, $off) = - ctx_statement_block($linenr, $realcnt, 0); - $ctx = $dstat; - - $dstat =~ s/\\\n.//g; - $dstat =~ s/$;/ /g; - - if ($dstat =~ /^\+\s*#\s*define\s+$Ident\s*${balanced_parens}\s*do\s*{(.*)\s*}\s*while\s*\(\s*0\s*\)\s*([;\s]*)\s*$/) { - my $stmts = $2; - my $semis = $3; - - $ctx =~ s/\n*$//; - my $cnt = statement_rawlines($ctx); - my $herectx = get_stat_here($linenr, $cnt, $here); - - if (($stmts =~ tr/;/;/) == 1 && - $stmts !~ /^\s*(if|while|for|switch)\b/) { - WARN("SINGLE_STATEMENT_DO_WHILE_MACRO", - "Single statement macros should not use a do {} while (0) loop\n" . "$herectx"); - } - if (defined $semis && $semis ne "") { - WARN("DO_WHILE_MACRO_WITH_TRAILING_SEMICOLON", - "do {} while (0) macros should not be semicolon terminated\n" . "$herectx"); - } - } elsif ($dstat =~ /^\+\s*#\s*define\s+$Ident.*;\s*$/) { - $ctx =~ s/\n*$//; - my $cnt = statement_rawlines($ctx); - my $herectx = get_stat_here($linenr, $cnt, $here); - - WARN("TRAILING_SEMICOLON", - "macros should not use a trailing semicolon\n" . "$herectx"); - } - } - -# check for redundant bracing round if etc - if ($line =~ /(^.*)\bif\b/ && $1 !~ /else\s*$/) { - my ($level, $endln, @chunks) = - ctx_statement_full($linenr, $realcnt, 1); - #print "chunks<$#chunks> linenr<$linenr> endln<$endln> level<$level>\n"; - #print "APW: <<$chunks[1][0]>><<$chunks[1][1]>>\n"; - if ($#chunks > 0 && $level == 0) { - my @allowed = (); - my $allow = 0; - my $seen = 0; - my $herectx = $here . "\n"; - my $ln = $linenr - 1; - for my $chunk (@chunks) { - my ($cond, $block) = @{$chunk}; - - # If the condition carries leading newlines, then count those as offsets. - my ($whitespace) = ($cond =~ /^((?:\s*\n[+-])*\s*)/s); - my $offset = statement_rawlines($whitespace) - 1; - - $allowed[$allow] = 0; - #print "COND<$cond> whitespace<$whitespace> offset<$offset>\n"; - - # We have looked at and allowed this specific line. - $suppress_ifbraces{$ln + $offset} = 1; - - $herectx .= "$rawlines[$ln + $offset]\n[...]\n"; - $ln += statement_rawlines($block) - 1; - - substr($block, 0, length($cond), ''); - - $seen++ if ($block =~ /^\s*{/); - - #print "cond<$cond> block<$block> allowed<$allowed[$allow]>\n"; - if (statement_lines($cond) > 1) { - #print "APW: ALLOWED: cond<$cond>\n"; - $allowed[$allow] = 1; - } - if ($block =~/\b(?:if|for|while)\b/) { - #print "APW: ALLOWED: block<$block>\n"; - $allowed[$allow] = 1; - } - if (statement_block_size($block) > 1) { - #print "APW: ALLOWED: lines block<$block>\n"; - $allowed[$allow] = 1; - } - $allow++; - } - if ($seen) { - my $sum_allowed = 0; - foreach (@allowed) { - $sum_allowed += $_; - } - if ($sum_allowed == 0) { - WARN("BRACES", - "braces {} are not necessary for any arm of this statement\n" . $herectx); - } elsif ($sum_allowed != $allow && - $seen != $allow) { - CHK("BRACES", - "braces {} should be used on all arms of this statement\n" . $herectx); - } - } - } - } - if (!defined $suppress_ifbraces{$linenr - 1} && - $line =~ /\b(if|while|for|else)\b/) { - my $allowed = 0; - - # Check the pre-context. - if (substr($line, 0, $-[0]) =~ /(\}\s*)$/) { - #print "APW: ALLOWED: pre<$1>\n"; - $allowed = 1; - } - - my ($level, $endln, @chunks) = - ctx_statement_full($linenr, $realcnt, $-[0]); - - # Check the condition. - my ($cond, $block) = @{$chunks[0]}; - #print "CHECKING<$linenr> cond<$cond> block<$block>\n"; - if (defined $cond) { - substr($block, 0, length($cond), ''); - } - if (statement_lines($cond) > 1) { - #print "APW: ALLOWED: cond<$cond>\n"; - $allowed = 1; - } - if ($block =~/\b(?:if|for|while)\b/) { - #print "APW: ALLOWED: block<$block>\n"; - $allowed = 1; - } - if (statement_block_size($block) > 1) { - #print "APW: ALLOWED: lines block<$block>\n"; - $allowed = 1; - } - # Check the post-context. - if (defined $chunks[1]) { - my ($cond, $block) = @{$chunks[1]}; - if (defined $cond) { - substr($block, 0, length($cond), ''); - } - if ($block =~ /^\s*\{/) { - #print "APW: ALLOWED: chunk-1 block<$block>\n"; - $allowed = 1; - } - } - if ($level == 0 && $block =~ /^\s*\{/ && !$allowed) { - my $cnt = statement_rawlines($block); - my $herectx = get_stat_here($linenr, $cnt, $here); - - WARN("BRACES", - "braces {} are not necessary for single statement blocks\n" . $herectx); - } - } - -# check for single line unbalanced braces - if ($sline =~ /^.\s*\}\s*else\s*$/ || - $sline =~ /^.\s*else\s*\{\s*$/) { - CHK("BRACES", "Unbalanced braces around else statement\n" . $herecurr); - } - -# check for unnecessary blank lines around braces - if (($line =~ /^.\s*}\s*$/ && $prevrawline =~ /^.\s*$/)) { - if (CHK("BRACES", - "Blank lines aren't necessary before a close brace '}'\n" . $hereprev) && - $fix && $prevrawline =~ /^\+/) { - fix_delete_line($fixlinenr - 1, $prevrawline); - } - } - if (($rawline =~ /^.\s*$/ && $prevline =~ /^..*{\s*$/)) { - if (CHK("BRACES", - "Blank lines aren't necessary after an open brace '{'\n" . $hereprev) && - $fix) { - fix_delete_line($fixlinenr, $rawline); - } - } - -# no volatiles please - my $asm_volatile = qr{\b(__asm__|asm)\s+(__volatile__|volatile)\b}; - if ($line =~ /\bvolatile\b/ && $line !~ /$asm_volatile/) { - WARN("VOLATILE", - "Use of volatile is usually wrong: see Documentation/process/volatile-considered-harmful.rst\n" . $herecurr); - } - -# Check for user-visible strings broken across lines, which breaks the ability -# to grep for the string. Make exceptions when the previous string ends in a -# newline (multiple lines in one string constant) or '\t', '\r', ';', or '{' -# (common in inline assembly) or is a octal \123 or hexadecimal \xaf value - if ($line =~ /^\+\s*$String/ && - $prevline =~ /"\s*$/ && - $prevrawline !~ /(?:\\(?:[ntr]|[0-7]{1,3}|x[0-9a-fA-F]{1,2})|;\s*|\{\s*)"\s*$/) { - if (WARN("SPLIT_STRING", - "quoted string split across lines\n" . $hereprev) && - $fix && - $prevrawline =~ /^\+.*"\s*$/ && - $last_coalesced_string_linenr != $linenr - 1) { - my $extracted_string = get_quoted_string($line, $rawline); - my $comma_close = ""; - if ($rawline =~ /\Q$extracted_string\E(\s*\)\s*;\s*$|\s*,\s*)/) { - $comma_close = $1; - } - - fix_delete_line($fixlinenr - 1, $prevrawline); - fix_delete_line($fixlinenr, $rawline); - my $fixedline = $prevrawline; - $fixedline =~ s/"\s*$//; - $fixedline .= substr($extracted_string, 1) . trim($comma_close); - fix_insert_line($fixlinenr - 1, $fixedline); - $fixedline = $rawline; - $fixedline =~ s/\Q$extracted_string\E\Q$comma_close\E//; - if ($fixedline !~ /\+\s*$/) { - fix_insert_line($fixlinenr, $fixedline); - } - $last_coalesced_string_linenr = $linenr; - } - } - -# check for missing a space in a string concatenation - if ($prevrawline =~ /[^\\]\w"$/ && $rawline =~ /^\+[\t ]+"\w/) { - WARN('MISSING_SPACE', - "break quoted strings at a space character\n" . $hereprev); - } - -# check for an embedded function name in a string when the function is known -# This does not work very well for -f --file checking as it depends on patch -# context providing the function name or a single line form for in-file -# function declarations - if (!$SOF && - $line =~ /^\+.*$String/ && - defined($context_function) && - get_quoted_string($line, $rawline) =~ /\b$context_function\b/ && - length(get_quoted_string($line, $rawline)) != (length($context_function) + 2)) { - WARN("EMBEDDED_FUNCTION_NAME", - "Prefer using '\"%s...\", __func__' to using '$context_function', this function's name, in a string\n" . $herecurr); - } - -# check for spaces before a quoted newline - if ($rawline =~ /^.*\".*\s\\n/) { - if (WARN("QUOTED_WHITESPACE_BEFORE_NEWLINE", - "unnecessary whitespace before a quoted newline\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/^(\+.*\".*)\s+\\n/$1\\n/; - } - - } - -# concatenated string without spaces between elements - if ($line =~ /$String[A-Za-z0-9_]/ || $line =~ /[A-Za-z0-9_]$String/) { - if (CHK("CONCATENATED_STRING", - "Concatenated strings should use spaces between elements\n" . $herecurr) && - $fix) { - while ($line =~ /($String)/g) { - my $extracted_string = substr($rawline, $-[0], $+[0] - $-[0]); - $fixed[$fixlinenr] =~ s/\Q$extracted_string\E([A-Za-z0-9_])/$extracted_string $1/; - $fixed[$fixlinenr] =~ s/([A-Za-z0-9_])\Q$extracted_string\E/$1 $extracted_string/; - } - } - } - -# uncoalesced string fragments - if ($line =~ /$String\s*"/) { - if (WARN("STRING_FRAGMENTS", - "Consecutive strings are generally better as a single string\n" . $herecurr) && - $fix) { - while ($line =~ /($String)(?=\s*")/g) { - my $extracted_string = substr($rawline, $-[0], $+[0] - $-[0]); - $fixed[$fixlinenr] =~ s/\Q$extracted_string\E\s*"/substr($extracted_string, 0, -1)/e; - } - } - } - -# check for non-standard and hex prefixed decimal printf formats - my $show_L = 1; #don't show the same defect twice - my $show_Z = 1; - while ($line =~ /(?:^|")([X\t]*)(?:"|$)/g) { - my $string = substr($rawline, $-[1], $+[1] - $-[1]); - $string =~ s/%%/__/g; - # check for %L - if ($show_L && $string =~ /%[\*\d\.\$]*L([diouxX])/) { - WARN("PRINTF_L", - "\%L$1 is non-standard C, use %ll$1\n" . $herecurr); - $show_L = 0; - } - # check for %Z - if ($show_Z && $string =~ /%[\*\d\.\$]*Z([diouxX])/) { - WARN("PRINTF_Z", - "%Z$1 is non-standard C, use %z$1\n" . $herecurr); - $show_Z = 0; - } - # check for 0x<decimal> - if ($string =~ /0x%[\*\d\.\$\Llzth]*[diou]/) { - ERROR("PRINTF_0XDECIMAL", - "Prefixing 0x with decimal output is defective\n" . $herecurr); - } - } - -# check for line continuations in quoted strings with odd counts of " - if ($rawline =~ /\\$/ && $sline =~ tr/"/"/ % 2) { - WARN("LINE_CONTINUATIONS", - "Avoid line continuations in quoted strings\n" . $herecurr); - } - -# warn about #if 0 - if ($line =~ /^.\s*\#\s*if\s+0\b/) { - WARN("IF_0", - "Consider removing the code enclosed by this #if 0 and its #endif\n" . $herecurr); - } - -# warn about #if 1 - if ($line =~ /^.\s*\#\s*if\s+1\b/) { - WARN("IF_1", - "Consider removing the #if 1 and its #endif\n" . $herecurr); - } - -# check for needless "if (<foo>) fn(<foo>)" uses - if ($prevline =~ /\bif\s*\(\s*($Lval)\s*\)/) { - my $tested = quotemeta($1); - my $expr = '\s*\(\s*' . $tested . '\s*\)\s*;'; - if ($line =~ /\b(kfree|usb_free_urb|debugfs_remove(?:_recursive)?|(?:kmem_cache|mempool|dma_pool)_destroy)$expr/) { - my $func = $1; - if (WARN('NEEDLESS_IF', - "$func(NULL) is safe and this check is probably not required\n" . $hereprev) && - $fix) { - my $do_fix = 1; - my $leading_tabs = ""; - my $new_leading_tabs = ""; - if ($lines[$linenr - 2] =~ /^\+(\t*)if\s*\(\s*$tested\s*\)\s*$/) { - $leading_tabs = $1; - } else { - $do_fix = 0; - } - if ($lines[$linenr - 1] =~ /^\+(\t+)$func\s*\(\s*$tested\s*\)\s*;\s*$/) { - $new_leading_tabs = $1; - if (length($leading_tabs) + 1 ne length($new_leading_tabs)) { - $do_fix = 0; - } - } else { - $do_fix = 0; - } - if ($do_fix) { - fix_delete_line($fixlinenr - 1, $prevrawline); - $fixed[$fixlinenr] =~ s/^\+$new_leading_tabs/\+$leading_tabs/; - } - } - } - } - -# check for unnecessary "Out of Memory" messages - if ($line =~ /^\+.*\b$logFunctions\s*\(/ && - $prevline =~ /^[ \+]\s*if\s*\(\s*(\!\s*|NULL\s*==\s*)?($Lval)(\s*==\s*NULL\s*)?\s*\)/ && - (defined $1 || defined $3) && - $linenr > 3) { - my $testval = $2; - my $testline = $lines[$linenr - 3]; - - my ($s, $c) = ctx_statement_block($linenr - 3, $realcnt, 0); -# print("line: <$line>\nprevline: <$prevline>\ns: <$s>\nc: <$c>\n\n\n"); - - if ($s =~ /(?:^|\n)[ \+]\s*(?:$Type\s*)?\Q$testval\E\s*=\s*(?:\([^\)]*\)\s*)?\s*$allocFunctions\s*\(/ && - $s !~ /\b__GFP_NOWARN\b/ ) { - WARN("OOM_MESSAGE", - "Possible unnecessary 'out of memory' message\n" . $hereprev); - } - } - -# check for logging functions with KERN_<LEVEL> - if ($line !~ /printk(?:_ratelimited|_once)?\s*\(/ && - $line =~ /\b$logFunctions\s*\(.*\b(KERN_[A-Z]+)\b/) { - my $level = $1; - if (WARN("UNNECESSARY_KERN_LEVEL", - "Possible unnecessary $level\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/\s*$level\s*//; - } - } - -# check for logging continuations - if ($line =~ /\bprintk\s*\(\s*KERN_CONT\b|\bpr_cont\s*\(/) { - WARN("LOGGING_CONTINUATION", - "Avoid logging continuation uses where feasible\n" . $herecurr); - } - -# check for mask then right shift without a parentheses - if ($perl_version_ok && - $line =~ /$LvalOrFunc\s*\&\s*($LvalOrFunc)\s*>>/ && - $4 !~ /^\&/) { # $LvalOrFunc may be &foo, ignore if so - WARN("MASK_THEN_SHIFT", - "Possible precedence defect with mask then right shift - may need parentheses\n" . $herecurr); - } - -# check for pointer comparisons to NULL - if ($perl_version_ok) { - while ($line =~ /\b$LvalOrFunc\s*(==|\!=)\s*NULL\b/g) { - my $val = $1; - my $equal = "!"; - $equal = "" if ($4 eq "!="); - if (CHK("COMPARISON_TO_NULL", - "Comparison to NULL could be written \"${equal}${val}\"\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/\b\Q$val\E\s*(?:==|\!=)\s*NULL\b/$equal$val/; - } - } - } - -# check for bad placement of section $InitAttribute (e.g.: __initdata) - if ($line =~ /(\b$InitAttribute\b)/) { - my $attr = $1; - if ($line =~ /^\+\s*static\s+(?:const\s+)?(?:$attr\s+)?($NonptrTypeWithAttr)\s+(?:$attr\s+)?($Ident(?:\[[^]]*\])?)\s*[=;]/) { - my $ptr = $1; - my $var = $2; - if ((($ptr =~ /\b(union|struct)\s+$attr\b/ && - ERROR("MISPLACED_INIT", - "$attr should be placed after $var\n" . $herecurr)) || - ($ptr !~ /\b(union|struct)\s+$attr\b/ && - WARN("MISPLACED_INIT", - "$attr should be placed after $var\n" . $herecurr))) && - $fix) { - $fixed[$fixlinenr] =~ s/(\bstatic\s+(?:const\s+)?)(?:$attr\s+)?($NonptrTypeWithAttr)\s+(?:$attr\s+)?($Ident(?:\[[^]]*\])?)\s*([=;])\s*/"$1" . trim(string_find_replace($2, "\\s*$attr\\s*", " ")) . " " . trim(string_find_replace($3, "\\s*$attr\\s*", "")) . " $attr" . ("$4" eq ";" ? ";" : " = ")/e; - } - } - } - -# check for $InitAttributeData (ie: __initdata) with const - if ($line =~ /\bconst\b/ && $line =~ /($InitAttributeData)/) { - my $attr = $1; - $attr =~ /($InitAttributePrefix)(.*)/; - my $attr_prefix = $1; - my $attr_type = $2; - if (ERROR("INIT_ATTRIBUTE", - "Use of const init definition must use ${attr_prefix}initconst\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ - s/$InitAttributeData/${attr_prefix}initconst/; - } - } - -# check for $InitAttributeConst (ie: __initconst) without const - if ($line !~ /\bconst\b/ && $line =~ /($InitAttributeConst)/) { - my $attr = $1; - if (ERROR("INIT_ATTRIBUTE", - "Use of $attr requires a separate use of const\n" . $herecurr) && - $fix) { - my $lead = $fixed[$fixlinenr] =~ - /(^\+\s*(?:static\s+))/; - $lead = rtrim($1); - $lead = "$lead " if ($lead !~ /^\+$/); - $lead = "${lead}const "; - $fixed[$fixlinenr] =~ s/(^\+\s*(?:static\s+))/$lead/; - } - } - -# check for __read_mostly with const non-pointer (should just be const) - if ($line =~ /\b__read_mostly\b/ && - $line =~ /($Type)\s*$Ident/ && $1 !~ /\*\s*$/ && $1 =~ /\bconst\b/) { - if (ERROR("CONST_READ_MOSTLY", - "Invalid use of __read_mostly with const type\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/\s+__read_mostly\b//; - } - } - -# don't use __constant_<foo> functions outside of include/uapi/ - if ($realfile !~ m@^include/uapi/@ && - $line =~ /(__constant_(?:htons|ntohs|[bl]e(?:16|32|64)_to_cpu|cpu_to_[bl]e(?:16|32|64)))\s*\(/) { - my $constant_func = $1; - my $func = $constant_func; - $func =~ s/^__constant_//; - if (WARN("CONSTANT_CONVERSION", - "$constant_func should be $func\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/\b$constant_func\b/$func/g; - } - } - -# prefer usleep_range over udelay - if ($line =~ /\budelay\s*\(\s*(\d+)\s*\)/) { - my $delay = $1; - # ignore udelay's < 10, however - if (! ($delay < 10) ) { - CHK("USLEEP_RANGE", - "usleep_range is preferred over udelay; see Documentation/timers/timers-howto.rst\n" . $herecurr); - } - if ($delay > 2000) { - WARN("LONG_UDELAY", - "long udelay - prefer mdelay; see arch/arm/include/asm/delay.h\n" . $herecurr); - } - } - -# warn about unexpectedly long msleep's - if ($line =~ /\bmsleep\s*\((\d+)\);/) { - if ($1 < 20) { - WARN("MSLEEP", - "msleep < 20ms can sleep for up to 20ms; see Documentation/timers/timers-howto.rst\n" . $herecurr); - } - } - -# check for comparisons of jiffies - if ($line =~ /\bjiffies\s*$Compare|$Compare\s*jiffies\b/) { - WARN("JIFFIES_COMPARISON", - "Comparing jiffies is almost always wrong; prefer time_after, time_before and friends\n" . $herecurr); - } - -# check for comparisons of get_jiffies_64() - if ($line =~ /\bget_jiffies_64\s*\(\s*\)\s*$Compare|$Compare\s*get_jiffies_64\s*\(\s*\)/) { - WARN("JIFFIES_COMPARISON", - "Comparing get_jiffies_64() is almost always wrong; prefer time_after64, time_before64 and friends\n" . $herecurr); - } - -# warn about #ifdefs in C files -# if ($line =~ /^.\s*\#\s*if(|n)def/ && ($realfile =~ /\.c$/)) { -# print "#ifdef in C files should be avoided\n"; -# print "$herecurr"; -# $clean = 0; -# } - -# warn about spacing in #ifdefs - if ($line =~ /^.\s*\#\s*(ifdef|ifndef|elif)\s\s+/) { - if (ERROR("SPACING", - "exactly one space required after that #$1\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ - s/^(.\s*\#\s*(ifdef|ifndef|elif))\s{2,}/$1 /; - } - - } - -# check for spinlock_t definitions without a comment. - if ($line =~ /^.\s*(struct\s+mutex|spinlock_t)\s+\S+;/ || - $line =~ /^.\s*(DEFINE_MUTEX)\s*\(/) { - my $which = $1; - if (!ctx_has_comment($first_line, $linenr)) { - CHK("UNCOMMENTED_DEFINITION", - "$1 definition without comment\n" . $herecurr); - } - } -# check for memory barriers without a comment. - - my $barriers = qr{ - mb| - rmb| - wmb| - read_barrier_depends - }x; - my $barrier_stems = qr{ - mb__before_atomic| - mb__after_atomic| - store_release| - load_acquire| - store_mb| - (?:$barriers) - }x; - my $all_barriers = qr{ - (?:$barriers)| - smp_(?:$barrier_stems)| - virt_(?:$barrier_stems) - }x; - - if ($line =~ /\b(?:$all_barriers)\s*\(/) { - if (!ctx_has_comment($first_line, $linenr)) { - WARN("MEMORY_BARRIER", - "memory barrier without comment\n" . $herecurr); - } - } - - my $underscore_smp_barriers = qr{__smp_(?:$barrier_stems)}x; - - if ($realfile !~ m@^include/asm-generic/@ && - $realfile !~ m@/barrier\.h$@ && - $line =~ m/\b(?:$underscore_smp_barriers)\s*\(/ && - $line !~ m/^.\s*\#\s*define\s+(?:$underscore_smp_barriers)\s*\(/) { - WARN("MEMORY_BARRIER", - "__smp memory barriers shouldn't be used outside barrier.h and asm-generic\n" . $herecurr); - } - -# check for waitqueue_active without a comment. - if ($line =~ /\bwaitqueue_active\s*\(/) { - if (!ctx_has_comment($first_line, $linenr)) { - WARN("WAITQUEUE_ACTIVE", - "waitqueue_active without comment\n" . $herecurr); - } - } - -# check for smp_read_barrier_depends and read_barrier_depends - if (!$file && $line =~ /\b(smp_|)read_barrier_depends\s*\(/) { - WARN("READ_BARRIER_DEPENDS", - "$1read_barrier_depends should only be used in READ_ONCE or DEC Alpha code\n" . $herecurr); - } - -# check of hardware specific defines - if ($line =~ m@^.\s*\#\s*if.*\b(__i386__|__powerpc64__|__sun__|__s390x__)\b@ && $realfile !~ m@include/asm-@) { - CHK("ARCH_DEFINES", - "architecture specific defines should be avoided\n" . $herecurr); - } - -# check that the storage class is not after a type - if ($line =~ /\b($Type)\s+($Storage)\b/) { - WARN("STORAGE_CLASS", - "storage class '$2' should be located before type '$1'\n" . $herecurr); - } -# Check that the storage class is at the beginning of a declaration - if ($line =~ /\b$Storage\b/ && - $line !~ /^.\s*$Storage/ && - $line =~ /^.\s*(.+?)\$Storage\s/ && - $1 !~ /[\,\)]\s*$/) { - WARN("STORAGE_CLASS", - "storage class should be at the beginning of the declaration\n" . $herecurr); - } - -# check the location of the inline attribute, that it is between -# storage class and type. - if ($line =~ /\b$Type\s+$Inline\b/ || - $line =~ /\b$Inline\s+$Storage\b/) { - ERROR("INLINE_LOCATION", - "inline keyword should sit between storage class and type\n" . $herecurr); - } - -# Check for __inline__ and __inline, prefer inline - if ($realfile !~ m@\binclude/uapi/@ && - $line =~ /\b(__inline__|__inline)\b/) { - if (WARN("INLINE", - "plain inline is preferred over $1\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/\b(__inline__|__inline)\b/inline/; - - } - } - -# Check for __attribute__ packed, prefer __packed - if ($realfile !~ m@\binclude/uapi/@ && - $line =~ /\b__attribute__\s*\(\s*\(.*\bpacked\b/) { - WARN("PREFER_PACKED", - "__packed is preferred over __attribute__((packed))\n" . $herecurr); - } - -# Check for __attribute__ aligned, prefer __aligned - if ($realfile !~ m@\binclude/uapi/@ && - $line =~ /\b__attribute__\s*\(\s*\(.*aligned/) { - WARN("PREFER_ALIGNED", - "__aligned(size) is preferred over __attribute__((aligned(size)))\n" . $herecurr); - } - -# Check for __attribute__ section, prefer __section - if ($realfile !~ m@\binclude/uapi/@ && - $line =~ /\b__attribute__\s*\(\s*\(.*_*section_*\s*\(\s*("[^"]*")/) { - my $old = substr($rawline, $-[1], $+[1] - $-[1]); - my $new = substr($old, 1, -1); - if (WARN("PREFER_SECTION", - "__section($new) is preferred over __attribute__((section($old)))\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/\b__attribute__\s*\(\s*\(\s*_*section_*\s*\(\s*\Q$old\E\s*\)\s*\)\s*\)/__section($new)/; - } - } - -# Check for __attribute__ format(printf, prefer __printf - if ($realfile !~ m@\binclude/uapi/@ && - $line =~ /\b__attribute__\s*\(\s*\(\s*format\s*\(\s*printf/) { - if (WARN("PREFER_PRINTF", - "__printf(string-index, first-to-check) is preferred over __attribute__((format(printf, string-index, first-to-check)))\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/\b__attribute__\s*\(\s*\(\s*format\s*\(\s*printf\s*,\s*(.*)\)\s*\)\s*\)/"__printf(" . trim($1) . ")"/ex; - - } - } - -# Check for __attribute__ format(scanf, prefer __scanf - if ($realfile !~ m@\binclude/uapi/@ && - $line =~ /\b__attribute__\s*\(\s*\(\s*format\s*\(\s*scanf\b/) { - if (WARN("PREFER_SCANF", - "__scanf(string-index, first-to-check) is preferred over __attribute__((format(scanf, string-index, first-to-check)))\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/\b__attribute__\s*\(\s*\(\s*format\s*\(\s*scanf\s*,\s*(.*)\)\s*\)\s*\)/"__scanf(" . trim($1) . ")"/ex; - } - } - -# Check for __attribute__ weak, or __weak declarations (may have link issues) - if ($perl_version_ok && - $line =~ /(?:$Declare|$DeclareMisordered)\s*$Ident\s*$balanced_parens\s*(?:$Attribute)?\s*;/ && - ($line =~ /\b__attribute__\s*\(\s*\(.*\bweak\b/ || - $line =~ /\b__weak\b/)) { - ERROR("WEAK_DECLARATION", - "Using weak declarations can have unintended link defects\n" . $herecurr); - } - -# check for c99 types like uint8_t used outside of uapi/ and tools/ - if (!$SOF && - $realfile !~ m@\binclude/uapi/@ && - $realfile !~ m@\btools/@ && - $line =~ /\b($Declare)\s*$Ident\s*[=;,\[]/) { - my $type = $1; - if ($type =~ /\b($typeC99Typedefs)\b/) { - $type = $1; - my $kernel_type = 'u'; - $kernel_type = 's' if ($type =~ /^_*[si]/); - $type =~ /(\d+)/; - $kernel_type .= $1; - if (CHK("PREFER_KERNEL_TYPES", - "Prefer kernel type '$kernel_type' over '$type'\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/\b$type\b/$kernel_type/; - } - } - } - -# check for cast of C90 native int or longer types constants - if ($line =~ /(\(\s*$C90_int_types\s*\)\s*)($Constant)\b/) { - my $cast = $1; - my $const = $2; - if (WARN("TYPECAST_INT_CONSTANT", - "Unnecessary typecast of c90 int constant\n" . $herecurr) && - $fix) { - my $suffix = ""; - my $newconst = $const; - $newconst =~ s/${Int_type}$//; - $suffix .= 'U' if ($cast =~ /\bunsigned\b/); - if ($cast =~ /\blong\s+long\b/) { - $suffix .= 'LL'; - } elsif ($cast =~ /\blong\b/) { - $suffix .= 'L'; - } - $fixed[$fixlinenr] =~ s/\Q$cast\E$const\b/$newconst$suffix/; - } - } - -# check for sizeof(&) - if ($line =~ /\bsizeof\s*\(\s*\&/) { - WARN("SIZEOF_ADDRESS", - "sizeof(& should be avoided\n" . $herecurr); - } - -# check for sizeof without parenthesis - if ($line =~ /\bsizeof\s+((?:\*\s*|)$Lval|$Type(?:\s+$Lval|))/) { - if (WARN("SIZEOF_PARENTHESIS", - "sizeof $1 should be sizeof($1)\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/\bsizeof\s+((?:\*\s*|)$Lval|$Type(?:\s+$Lval|))/"sizeof(" . trim($1) . ")"/ex; - } - } - -# check for struct spinlock declarations - if ($line =~ /^.\s*\bstruct\s+spinlock\s+\w+\s*;/) { - WARN("USE_SPINLOCK_T", - "struct spinlock should be spinlock_t\n" . $herecurr); - } - -# check for seq_printf uses that could be seq_puts - if ($sline =~ /\bseq_printf\s*\(.*"\s*\)\s*;\s*$/) { - my $fmt = get_quoted_string($line, $rawline); - $fmt =~ s/%%//g; - if ($fmt !~ /%/) { - if (WARN("PREFER_SEQ_PUTS", - "Prefer seq_puts to seq_printf\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/\bseq_printf\b/seq_puts/; - } - } - } - -# check for memcpy uses that should be memcpy_s - if ($SOF && ($line =~ /memcpy\s*\(.*/)) { - my $fmt = get_quoted_string($line, $rawline); - $fmt =~ s/%%//g; - if ($fmt !~ /%/) { - if (WARN("PREFER_MEMCPY_S", - "Use safe version of memcpy - memcpy_s whenever possible\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/memcpy\b/memcpy_s/; - } - } - } - -# check for vsprintf extension %p<foo> misuses - if ($perl_version_ok && - defined $stat && - $stat =~ /^\+(?![^\{]*\{\s*).*\b(\w+)\s*\(.*$String\s*,/s && - $1 !~ /^_*volatile_*$/) { - my $stat_real; - - my $lc = $stat =~ tr@\n@@; - $lc = $lc + $linenr; - for (my $count = $linenr; $count <= $lc; $count++) { - my $specifier; - my $extension; - my $bad_specifier = ""; - my $fmt = get_quoted_string($lines[$count - 1], raw_line($count, 0)); - $fmt =~ s/%%//g; - - while ($fmt =~ /(\%[\*\d\.]*p(\w))/g) { - $specifier = $1; - $extension = $2; - if ($extension !~ /[SsBKRraEhMmIiUDdgVCbGNOxt]/) { - $bad_specifier = $specifier; - last; - } - if ($extension eq "x" && !defined($stat_real)) { - if (!defined($stat_real)) { - $stat_real = get_stat_real($linenr, $lc); - } - WARN("VSPRINTF_SPECIFIER_PX", - "Using vsprintf specifier '\%px' potentially exposes the kernel memory layout, if you don't really need the address please consider using '\%p'.\n" . "$here\n$stat_real\n"); - } - } - if ($bad_specifier ne "") { - my $stat_real = get_stat_real($linenr, $lc); - my $ext_type = "Invalid"; - my $use = ""; - if ($bad_specifier =~ /p[Ff]/) { - $ext_type = "Deprecated"; - $use = " - use %pS instead"; - $use =~ s/pS/ps/ if ($bad_specifier =~ /pf/); - } - - WARN("VSPRINTF_POINTER_EXTENSION", - "$ext_type vsprintf pointer extension '$bad_specifier'$use\n" . "$here\n$stat_real\n"); - } - } - } - -# Check for misused memsets - if ($perl_version_ok && - defined $stat && - $stat =~ /^\+(?:.*?)\bmemset\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\,\s*$FuncArg\s*\)/) { - - my $ms_addr = $2; - my $ms_val = $7; - my $ms_size = $12; - - if ($ms_size =~ /^(0x|)0$/i) { - ERROR("MEMSET", - "memset to 0's uses 0 as the 2nd argument, not the 3rd\n" . "$here\n$stat\n"); - } elsif ($ms_size =~ /^(0x|)1$/i) { - WARN("MEMSET", - "single byte memset is suspicious. Swapped 2nd/3rd argument?\n" . "$here\n$stat\n"); - } - } - -# Check for memcpy(foo, bar, ETH_ALEN) that could be ether_addr_copy(foo, bar) -# if ($perl_version_ok && -# defined $stat && -# $stat =~ /^\+(?:.*?)\bmemcpy\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\,\s*ETH_ALEN\s*\)/) { -# if (WARN("PREFER_ETHER_ADDR_COPY", -# "Prefer ether_addr_copy() over memcpy() if the Ethernet addresses are __aligned(2)\n" . "$here\n$stat\n") && -# $fix) { -# $fixed[$fixlinenr] =~ s/\bmemcpy\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\,\s*ETH_ALEN\s*\)/ether_addr_copy($2, $7)/; -# } -# } - -# Check for memcmp(foo, bar, ETH_ALEN) that could be ether_addr_equal*(foo, bar) -# if ($perl_version_ok && -# defined $stat && -# $stat =~ /^\+(?:.*?)\bmemcmp\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\,\s*ETH_ALEN\s*\)/) { -# WARN("PREFER_ETHER_ADDR_EQUAL", -# "Prefer ether_addr_equal() or ether_addr_equal_unaligned() over memcmp()\n" . "$here\n$stat\n") -# } - -# check for memset(foo, 0x0, ETH_ALEN) that could be eth_zero_addr -# check for memset(foo, 0xFF, ETH_ALEN) that could be eth_broadcast_addr -# if ($perl_version_ok && -# defined $stat && -# $stat =~ /^\+(?:.*?)\bmemset\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\,\s*ETH_ALEN\s*\)/) { -# -# my $ms_val = $7; -# -# if ($ms_val =~ /^(?:0x|)0+$/i) { -# if (WARN("PREFER_ETH_ZERO_ADDR", -# "Prefer eth_zero_addr over memset()\n" . "$here\n$stat\n") && -# $fix) { -# $fixed[$fixlinenr] =~ s/\bmemset\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*,\s*ETH_ALEN\s*\)/eth_zero_addr($2)/; -# } -# } elsif ($ms_val =~ /^(?:0xff|255)$/i) { -# if (WARN("PREFER_ETH_BROADCAST_ADDR", -# "Prefer eth_broadcast_addr() over memset()\n" . "$here\n$stat\n") && -# $fix) { -# $fixed[$fixlinenr] =~ s/\bmemset\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*,\s*ETH_ALEN\s*\)/eth_broadcast_addr($2)/; -# } -# } -# } - -# typecasts on min/max could be min_t/max_t - if ($perl_version_ok && - defined $stat && - $stat =~ /^\+(?:.*?)\b(min|max)\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\)/) { - if (defined $2 || defined $7) { - my $call = $1; - my $cast1 = deparenthesize($2); - my $arg1 = $3; - my $cast2 = deparenthesize($7); - my $arg2 = $8; - my $cast; - - if ($cast1 ne "" && $cast2 ne "" && $cast1 ne $cast2) { - $cast = "$cast1 or $cast2"; - } elsif ($cast1 ne "") { - $cast = $cast1; - } else { - $cast = $cast2; - } - WARN("MINMAX", - "$call() should probably be ${call}_t($cast, $arg1, $arg2)\n" . "$here\n$stat\n"); - } - } - -# check usleep_range arguments - if ($perl_version_ok && - defined $stat && - $stat =~ /^\+(?:.*?)\busleep_range\s*\(\s*($FuncArg)\s*,\s*($FuncArg)\s*\)/) { - my $min = $1; - my $max = $7; - if ($min eq $max) { - WARN("USLEEP_RANGE", - "usleep_range should not use min == max args; see Documentation/timers/timers-howto.rst\n" . "$here\n$stat\n"); - } elsif ($min =~ /^\d+$/ && $max =~ /^\d+$/ && - $min > $max) { - WARN("USLEEP_RANGE", - "usleep_range args reversed, use min then max; see Documentation/timers/timers-howto.rst\n" . "$here\n$stat\n"); - } - } - -# check for naked sscanf - if ($perl_version_ok && - defined $stat && - $line =~ /\bsscanf\b/ && - ($stat !~ /$Ident\s*=\s*sscanf\s*$balanced_parens/ && - $stat !~ /\bsscanf\s*$balanced_parens\s*(?:$Compare)/ && - $stat !~ /(?:$Compare)\s*\bsscanf\s*$balanced_parens/)) { - my $lc = $stat =~ tr@\n@@; - $lc = $lc + $linenr; - my $stat_real = get_stat_real($linenr, $lc); - WARN("NAKED_SSCANF", - "unchecked sscanf return value\n" . "$here\n$stat_real\n"); - } - -# check for simple sscanf that should be kstrto<foo> - if ($perl_version_ok && - defined $stat && - $line =~ /\bsscanf\b/) { - my $lc = $stat =~ tr@\n@@; - $lc = $lc + $linenr; - my $stat_real = get_stat_real($linenr, $lc); - if ($stat_real =~ /\bsscanf\b\s*\(\s*$FuncArg\s*,\s*("[^"]+")/) { - my $format = $6; - my $count = $format =~ tr@%@%@; - if ($count == 1 && - $format =~ /^"\%(?i:ll[udxi]|[udxi]ll|ll|[hl]h?[udxi]|[udxi][hl]h?|[hl]h?|[udxi])"$/) { - WARN("SSCANF_TO_KSTRTO", - "Prefer kstrto<type> to single variable sscanf\n" . "$here\n$stat_real\n"); - } - } - } - -# check for new externs in .h files. - if ($realfile =~ /\.h$/ && - $line =~ /^\+\s*(extern\s+)$Type\s*$Ident\s*\(/s) { - if (CHK("AVOID_EXTERNS", - "extern prototypes should be avoided in .h files\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/(.*)\bextern\b\s*(.*)/$1$2/; - } - } - -# check for new externs in .c files. - if ($realfile =~ /\.c$/ && defined $stat && - $stat =~ /^.\s*(?:extern\s+)?$Type\s+($Ident)(\s*)\(/s) - { - my $function_name = $1; - my $paren_space = $2; - - my $s = $stat; - if (defined $cond) { - substr($s, 0, length($cond), ''); - } - if ($s =~ /^\s*;/ && - $function_name ne 'uninitialized_var') - { - WARN("AVOID_EXTERNS", - "externs should be avoided in .c files\n" . $herecurr); - } - - if ($paren_space =~ /\n/) { - WARN("FUNCTION_ARGUMENTS", - "arguments for function declarations should follow identifier\n" . $herecurr); - } - - } elsif ($realfile =~ /\.c$/ && defined $stat && - $stat =~ /^.\s*extern\s+/) - { - WARN("AVOID_EXTERNS", - "externs should be avoided in .c files\n" . $herecurr); - } - -# check for function declarations that have arguments without identifier names - if (defined $stat && - $stat =~ /^.\s*(?:extern\s+)?$Type\s*(?:$Ident|\(\s*\*\s*$Ident\s*\))\s*\(\s*([^{]+)\s*\)\s*;/s && - $1 ne "void") { - my $args = trim($1); - while ($args =~ m/\s*($Type\s*(?:$Ident|\(\s*\*\s*$Ident?\s*\)\s*$balanced_parens)?)/g) { - my $arg = trim($1); - if ($arg =~ /^$Type$/ && $arg !~ /enum\s+$Ident$/) { - WARN("FUNCTION_ARGUMENTS", - "function definition argument '$arg' should also have an identifier name\n" . $herecurr); - } - } - } - -# check for function definitions - if ($perl_version_ok && - defined $stat && - $stat =~ /^.\s*(?:$Storage\s+)?$Type\s*($Ident)\s*$balanced_parens\s*{/s) { - $context_function = $1; - -# check for multiline function definition with misplaced open brace - my $ok = 0; - my $cnt = statement_rawlines($stat); - my $herectx = $here . "\n"; - for (my $n = 0; $n < $cnt; $n++) { - my $rl = raw_line($linenr, $n); - $herectx .= $rl . "\n"; - $ok = 1 if ($rl =~ /^[ \+]\{/); - $ok = 1 if ($rl =~ /\{/ && $n == 0); - last if $rl =~ /^[ \+].*\{/; - } - if (!$ok) { - ERROR("OPEN_BRACE", - "open brace '{' following function definitions go on the next line\n" . $herectx); - } - } - -# checks for new __setup's - if ($rawline =~ /\b__setup\("([^"]*)"/) { - my $name = $1; - - if (!grep(/$name/, @setup_docs)) { - CHK("UNDOCUMENTED_SETUP", - "__setup appears un-documented -- check Documentation/admin-guide/kernel-parameters.rst\n" . $herecurr); - } - } - -# check for pointless casting of alloc functions - if ($line =~ /\*\s*\)\s*$allocFunctions\b/) { - WARN("UNNECESSARY_CASTS", - "unnecessary cast may hide bugs, see http://c-faq.com/malloc/mallocnocast.html\n" . $herecurr); - } - -# alloc style -# p = alloc(sizeof(struct foo), ...) should be p = alloc(sizeof(*p), ...) - if ($perl_version_ok && - $line =~ /\b($Lval)\s*\=\s*(?:$balanced_parens)?\s*((?:kv|k|v)[mz]alloc(?:_node)?)\s*\(\s*(sizeof\s*\(\s*struct\s+$Lval\s*\))/) { - CHK("ALLOC_SIZEOF_STRUCT", - "Prefer $3(sizeof(*$1)...) over $3($4...)\n" . $herecurr); - } - -# check for k[mz]alloc with multiplies that could be kmalloc_array/kcalloc - if ($perl_version_ok && - defined $stat && - $stat =~ /^\+\s*($Lval)\s*\=\s*(?:$balanced_parens)?\s*(k[mz]alloc)\s*\(\s*($FuncArg)\s*\*\s*($FuncArg)\s*,/) { - my $oldfunc = $3; - my $a1 = $4; - my $a2 = $10; - my $newfunc = "kmalloc_array"; - $newfunc = "kcalloc" if ($oldfunc eq "kzalloc"); - my $r1 = $a1; - my $r2 = $a2; - if ($a1 =~ /^sizeof\s*\S/) { - $r1 = $a2; - $r2 = $a1; - } - if ($r1 !~ /^sizeof\b/ && $r2 =~ /^sizeof\s*\S/ && - !($r1 =~ /^$Constant$/ || $r1 =~ /^[A-Z_][A-Z0-9_]*$/)) { - my $cnt = statement_rawlines($stat); - my $herectx = get_stat_here($linenr, $cnt, $here); - - if (WARN("ALLOC_WITH_MULTIPLY", - "Prefer $newfunc over $oldfunc with multiply\n" . $herectx) && - $cnt == 1 && - $fix) { - $fixed[$fixlinenr] =~ s/\b($Lval)\s*\=\s*(?:$balanced_parens)?\s*(k[mz]alloc)\s*\(\s*($FuncArg)\s*\*\s*($FuncArg)/$1 . ' = ' . "$newfunc(" . trim($r1) . ', ' . trim($r2)/e; - } - } - } - -# check for krealloc arg reuse - if ($perl_version_ok && - $line =~ /\b($Lval)\s*\=\s*(?:$balanced_parens)?\s*krealloc\s*\(\s*($Lval)\s*,/ && - $1 eq $3) { - WARN("KREALLOC_ARG_REUSE", - "Reusing the krealloc arg is almost always a bug\n" . $herecurr); - } - -# check for alloc argument mismatch - if ($line =~ /\b(kcalloc|kmalloc_array)\s*\(\s*sizeof\b/) { - WARN("ALLOC_ARRAY_ARGS", - "$1 uses number as first arg, sizeof is generally wrong\n" . $herecurr); - } - -# check for multiple semicolons - if ($line =~ /;\s*;\s*$/) { - if (WARN("ONE_SEMICOLON", - "Statements terminations use 1 semicolon\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/(\s*;\s*){2,}$/;/g; - } - } - -# check for #defines like: 1 << <digit> that could be BIT(digit), it is not exported to uapi - if ($realfile !~ m@^include/uapi/@ && - $line =~ /#\s*define\s+\w+\s+\(?\s*1\s*([ulUL]*)\s*\<\<\s*(?:\d+|$Ident)\s*\)?/) { - my $ull = ""; - $ull = "_ULL" if (defined($1) && $1 =~ /ll/i); - if (CHK("BIT_MACRO", - "Prefer using the BIT$ull macro\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/\(?\s*1\s*[ulUL]*\s*<<\s*(\d+|$Ident)\s*\)?/BIT${ull}($1)/; - } - } - -# check for #if defined CONFIG_<FOO> || defined CONFIG_<FOO>_MODULE - if ($line =~ /^\+\s*#\s*if\s+defined(?:\s*\(?\s*|\s+)(CONFIG_[A-Z_]+)\s*\)?\s*\|\|\s*defined(?:\s*\(?\s*|\s+)\1_MODULE\s*\)?\s*$/) { - my $config = $1; - if (WARN("PREFER_IS_ENABLED", - "Prefer IS_ENABLED(<FOO>) to CONFIG_<FOO> || CONFIG_<FOO>_MODULE\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] = "\+#if IS_ENABLED($config)"; - } - } - -# check for case / default statements not preceded by break/fallthrough/switch - if ($line =~ /^.\s*(?:case\s+(?:$Ident|$Constant)\s*|default):/) { - my $has_break = 0; - my $has_statement = 0; - my $count = 0; - my $prevline = $linenr; - while ($prevline > 1 && ($file || $count < 3) && !$has_break) { - $prevline--; - my $rline = $rawlines[$prevline - 1]; - my $fline = $lines[$prevline - 1]; - last if ($fline =~ /^\@\@/); - next if ($fline =~ /^\-/); - next if ($fline =~ /^.(?:\s*(?:case\s+(?:$Ident|$Constant)[\s$;]*|default):[\s$;]*)*$/); - $has_break = 1 if ($rline =~ /fall[\s_-]*(through|thru)/i); - next if ($fline =~ /^.[\s$;]*$/); - $has_statement = 1; - $count++; - $has_break = 1 if ($fline =~ /\bswitch\b|\b(?:break\s*;[\s$;]*$|exit\s*\(\b|return\b|goto\b|continue\b)/); - } - if (!$has_break && $has_statement) { - WARN("MISSING_BREAK", - "Possible switch case/default not preceded by break or fallthrough comment\n" . $herecurr); - } - } - -# check for switch/default statements without a break; - if ($perl_version_ok && - defined $stat && - $stat =~ /^\+[$;\s]*(?:case[$;\s]+\w+[$;\s]*:[$;\s]*|)*[$;\s]*\bdefault[$;\s]*:[$;\s]*;/g) { - my $cnt = statement_rawlines($stat); - my $herectx = get_stat_here($linenr, $cnt, $here); - - WARN("DEFAULT_NO_BREAK", - "switch default: should use break\n" . $herectx); - } - -# check for gcc specific __FUNCTION__ - if ($line =~ /\b__FUNCTION__\b/) { - if (WARN("USE_FUNC", - "__func__ should be used instead of gcc specific __FUNCTION__\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/\b__FUNCTION__\b/__func__/g; - } - } - -# check for uses of __DATE__, __TIME__, __TIMESTAMP__ - while ($line =~ /\b(__(?:DATE|TIME|TIMESTAMP)__)\b/g) { - ERROR("DATE_TIME", - "Use of the '$1' macro makes the build non-deterministic\n" . $herecurr); - } - -# check for use of yield() - if ($line =~ /\byield\s*\(\s*\)/) { - WARN("YIELD", - "Using yield() is generally wrong. See yield() kernel-doc (sched/core.c)\n" . $herecurr); - } - -# check for comparisons against true and false - if ($line =~ /\+\s*(.*?)\b(true|false|$Lval)\s*(==|\!=)\s*(true|false|$Lval)\b(.*)$/i) { - my $lead = $1; - my $arg = $2; - my $test = $3; - my $otype = $4; - my $trail = $5; - my $op = "!"; - - ($arg, $otype) = ($otype, $arg) if ($arg =~ /^(?:true|false)$/i); - - my $type = lc($otype); - if ($type =~ /^(?:true|false)$/) { - if (("$test" eq "==" && "$type" eq "true") || - ("$test" eq "!=" && "$type" eq "false")) { - $op = ""; - } - - CHK("BOOL_COMPARISON", - "Using comparison to $otype is error prone\n" . $herecurr); - -## maybe suggesting a correct construct would better -## "Using comparison to $otype is error prone. Perhaps use '${lead}${op}${arg}${trail}'\n" . $herecurr); - - } - } - -# check for semaphores initialized locked - if ($line =~ /^.\s*sema_init.+,\W?0\W?\)/) { - WARN("CONSIDER_COMPLETION", - "consider using a completion\n" . $herecurr); - } - -# recommend kstrto* over simple_strto* and strict_strto* - if ($line =~ /\b((simple|strict)_(strto(l|ll|ul|ull)))\s*\(/) { - WARN("CONSIDER_KSTRTO", - "$1 is obsolete, use k$3 instead\n" . $herecurr); - } - -# check for __initcall(), use device_initcall() explicitly or more appropriate function please - if ($line =~ /^.\s*__initcall\s*\(/) { - WARN("USE_DEVICE_INITCALL", - "please use device_initcall() or more appropriate function instead of __initcall() (see include/linux/init.h)\n" . $herecurr); - } - -# check for spin_is_locked(), suggest lockdep instead - if ($line =~ /\bspin_is_locked\(/) { - WARN("USE_LOCKDEP", - "Where possible, use lockdep_assert_held instead of assertions based on spin_is_locked\n" . $herecurr); - } - -# check for deprecated apis - if ($line =~ /\b($deprecated_apis_search)\b\s*\(/) { - my $deprecated_api = $1; - my $new_api = $deprecated_apis{$deprecated_api}; - WARN("DEPRECATED_API", - "Deprecated use of '$deprecated_api', prefer '$new_api' instead\n" . $herecurr); - } - -# check for various structs that are normally const (ops, kgdb, device_tree) -# and avoid what seem like struct definitions 'struct foo {' - if ($line !~ /\bconst\b/ && - $line =~ /\bstruct\s+($const_structs)\b(?!\s*\{)/) { - WARN("CONST_STRUCT", - "struct $1 should normally be const\n" . $herecurr); - } - -# use of NR_CPUS is usually wrong -# ignore definitions of NR_CPUS and usage to define arrays as likely right - if ($line =~ /\bNR_CPUS\b/ && - $line !~ /^.\s*\s*#\s*if\b.*\bNR_CPUS\b/ && - $line !~ /^.\s*\s*#\s*define\b.*\bNR_CPUS\b/ && - $line !~ /^.\s*$Declare\s.*\[[^\]]*NR_CPUS[^\]]*\]/ && - $line !~ /\[[^\]]*\.\.\.[^\]]*NR_CPUS[^\]]*\]/ && - $line !~ /\[[^\]]*NR_CPUS[^\]]*\.\.\.[^\]]*\]/) - { - WARN("NR_CPUS", - "usage of NR_CPUS is often wrong - consider using cpu_possible(), num_possible_cpus(), for_each_possible_cpu(), etc\n" . $herecurr); - } - -# Use of __ARCH_HAS_<FOO> or ARCH_HAVE_<BAR> is wrong. - if ($line =~ /\+\s*#\s*define\s+((?:__)?ARCH_(?:HAS|HAVE)\w*)\b/) { - ERROR("DEFINE_ARCH_HAS", - "#define of '$1' is wrong - use Kconfig variables or standard guards instead\n" . $herecurr); - } - -# likely/unlikely comparisons similar to "(likely(foo) > 0)" - if ($perl_version_ok && - $line =~ /\b((?:un)?likely)\s*\(\s*$FuncArg\s*\)\s*$Compare/) { - WARN("LIKELY_MISUSE", - "Using $1 should generally have parentheses around the comparison\n" . $herecurr); - } - -# nested likely/unlikely calls - if ($line =~ /\b(?:(?:un)?likely)\s*\(\s*!?\s*(IS_ERR(?:_OR_NULL|_VALUE)?|WARN)/) { - WARN("LIKELY_MISUSE", - "nested (un)?likely() calls, $1 already uses unlikely() internally\n" . $herecurr); - } - -# whine mightly about in_atomic - if ($line =~ /\bin_atomic\s*\(/) { - if ($realfile =~ m@^drivers/@) { - ERROR("IN_ATOMIC", - "do not use in_atomic in drivers\n" . $herecurr); - } elsif ($realfile !~ m@^kernel/@) { - WARN("IN_ATOMIC", - "use of in_atomic() is incorrect outside core kernel code\n" . $herecurr); - } - } - -# check for mutex_trylock_recursive usage - if ($line =~ /mutex_trylock_recursive/) { - ERROR("LOCKING", - "recursive locking is bad, do not use this ever.\n" . $herecurr); - } - -# check for lockdep_set_novalidate_class - if ($line =~ /^.\s*lockdep_set_novalidate_class\s*\(/ || - $line =~ /__lockdep_no_validate__\s*\)/ ) { - if ($realfile !~ m@^kernel/lockdep@ && - $realfile !~ m@^include/linux/lockdep@ && - $realfile !~ m@^drivers/base/core@) { - ERROR("LOCKDEP", - "lockdep_no_validate class is reserved for device->mutex.\n" . $herecurr); - } - } - - if ($line =~ /debugfs_create_\w+.*\b$mode_perms_world_writable\b/ || - $line =~ /DEVICE_ATTR.*\b$mode_perms_world_writable\b/) { - WARN("EXPORTED_WORLD_WRITABLE", - "Exporting world writable files is usually an error. Consider more restrictive permissions.\n" . $herecurr); - } - -# check for DEVICE_ATTR uses that could be DEVICE_ATTR_<FOO> -# and whether or not function naming is typical and if -# DEVICE_ATTR permissions uses are unusual too - if ($perl_version_ok && - defined $stat && - $stat =~ /\bDEVICE_ATTR\s*\(\s*(\w+)\s*,\s*\(?\s*(\s*(?:${multi_mode_perms_string_search}|0[0-7]{3,3})\s*)\s*\)?\s*,\s*(\w+)\s*,\s*(\w+)\s*\)/) { - my $var = $1; - my $perms = $2; - my $show = $3; - my $store = $4; - my $octal_perms = perms_to_octal($perms); - if ($show =~ /^${var}_show$/ && - $store =~ /^${var}_store$/ && - $octal_perms eq "0644") { - if (WARN("DEVICE_ATTR_RW", - "Use DEVICE_ATTR_RW\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/\bDEVICE_ATTR\s*\(\s*$var\s*,\s*\Q$perms\E\s*,\s*$show\s*,\s*$store\s*\)/DEVICE_ATTR_RW(${var})/; - } - } elsif ($show =~ /^${var}_show$/ && - $store =~ /^NULL$/ && - $octal_perms eq "0444") { - if (WARN("DEVICE_ATTR_RO", - "Use DEVICE_ATTR_RO\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/\bDEVICE_ATTR\s*\(\s*$var\s*,\s*\Q$perms\E\s*,\s*$show\s*,\s*NULL\s*\)/DEVICE_ATTR_RO(${var})/; - } - } elsif ($show =~ /^NULL$/ && - $store =~ /^${var}_store$/ && - $octal_perms eq "0200") { - if (WARN("DEVICE_ATTR_WO", - "Use DEVICE_ATTR_WO\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/\bDEVICE_ATTR\s*\(\s*$var\s*,\s*\Q$perms\E\s*,\s*NULL\s*,\s*$store\s*\)/DEVICE_ATTR_WO(${var})/; - } - } elsif ($octal_perms eq "0644" || - $octal_perms eq "0444" || - $octal_perms eq "0200") { - my $newshow = "$show"; - $newshow = "${var}_show" if ($show ne "NULL" && $show ne "${var}_show"); - my $newstore = $store; - $newstore = "${var}_store" if ($store ne "NULL" && $store ne "${var}_store"); - my $rename = ""; - if ($show ne $newshow) { - $rename .= " '$show' to '$newshow'"; - } - if ($store ne $newstore) { - $rename .= " '$store' to '$newstore'"; - } - WARN("DEVICE_ATTR_FUNCTIONS", - "Consider renaming function(s)$rename\n" . $herecurr); - } else { - WARN("DEVICE_ATTR_PERMS", - "DEVICE_ATTR unusual permissions '$perms' used\n" . $herecurr); - } - } - -# Mode permission misuses where it seems decimal should be octal -# This uses a shortcut match to avoid unnecessary uses of a slow foreach loop -# o Ignore module_param*(...) uses with a decimal 0 permission as that has a -# specific definition of not visible in sysfs. -# o Ignore proc_create*(...) uses with a decimal 0 permission as that means -# use the default permissions - if ($perl_version_ok && - defined $stat && - $line =~ /$mode_perms_search/) { - foreach my $entry (@mode_permission_funcs) { - my $func = $entry->[0]; - my $arg_pos = $entry->[1]; - - my $lc = $stat =~ tr@\n@@; - $lc = $lc + $linenr; - my $stat_real = get_stat_real($linenr, $lc); - - my $skip_args = ""; - if ($arg_pos > 1) { - $arg_pos--; - $skip_args = "(?:\\s*$FuncArg\\s*,\\s*){$arg_pos,$arg_pos}"; - } - my $test = "\\b$func\\s*\\(${skip_args}($FuncArg(?:\\|\\s*$FuncArg)*)\\s*[,\\)]"; - if ($stat =~ /$test/) { - my $val = $1; - $val = $6 if ($skip_args ne ""); - if (!($func =~ /^(?:module_param|proc_create)/ && $val eq "0") && - (($val =~ /^$Int$/ && $val !~ /^$Octal$/) || - ($val =~ /^$Octal$/ && length($val) ne 4))) { - ERROR("NON_OCTAL_PERMISSIONS", - "Use 4 digit octal (0777) not decimal permissions\n" . "$here\n" . $stat_real); - } - if ($val =~ /^$Octal$/ && (oct($val) & 02)) { - ERROR("EXPORTED_WORLD_WRITABLE", - "Exporting writable files is usually an error. Consider more restrictive permissions.\n" . "$here\n" . $stat_real); - } - } - } - } - -# check for uses of S_<PERMS> that could be octal for readability - while ($line =~ m{\b($multi_mode_perms_string_search)\b}g) { - my $oval = $1; - my $octal = perms_to_octal($oval); - if (WARN("SYMBOLIC_PERMS", - "Symbolic permissions '$oval' are not preferred. Consider using octal permissions '$octal'.\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/\Q$oval\E/$octal/; - } - } - -# validate content of MODULE_LICENSE against list from include/linux/module.h - if ($line =~ /\bMODULE_LICENSE\s*\(\s*($String)\s*\)/) { - my $extracted_string = get_quoted_string($line, $rawline); - my $valid_licenses = qr{ - GPL| - GPL\ v2| - GPL\ and\ additional\ rights| - Dual\ BSD/GPL| - Dual\ MIT/GPL| - Dual\ MPL/GPL| - Proprietary - }x; - if ($extracted_string !~ /^"(?:$valid_licenses)"$/x) { - WARN("MODULE_LICENSE", - "unknown module license " . $extracted_string . "\n" . $herecurr); - } - } - -# check for sysctl duplicate constants - if ($line =~ /\.extra[12]\s*=\s*&(zero|one|int_max)\b/) { - WARN("DUPLICATED_SYSCTL_CONST", - "duplicated sysctl range checking value '$1', consider using the shared one in include/linux/sysctl.h\n" . $herecurr); - } - } - - # If we have no input at all, then there is nothing to report on - # so just keep quiet. - if ($#rawlines == -1) { - exit(0); - } - - # In mailback mode only produce a report in the negative, for - # things that appear to be patches. - if ($mailback && ($clean == 1 || !$is_patch)) { - exit(0); - } - - # This is not a patch, and we are are in 'no-patch' mode so - # just keep quiet. - if (!$chk_patch && !$is_patch) { - exit(0); - } - - if (!$is_patch && $filename !~ /cover-letter\.patch$/) { - ERROR("NOT_UNIFIED_DIFF", - "Does not appear to be a unified-diff format patch\n"); - } - if ($is_patch && $has_commit_log && $chk_signoff) { - if ($signoff == 0) { - ERROR("MISSING_SIGN_OFF", - "Missing Signed-off-by: line(s)\n"); - } elsif (!$authorsignoff) { - WARN("NO_AUTHOR_SIGN_OFF", - "Missing Signed-off-by: line by nominal patch author '$author'\n"); - } - } - - print report_dump(); - if ($summary && !($clean == 1 && $quiet == 1)) { - print "$filename " if ($summary_file); - print "total: $cnt_error errors, $cnt_warn warnings, " . - (($check)? "$cnt_chk checks, " : "") . - "$cnt_lines lines checked\n"; - } - - if ($quiet == 0) { - # If there were any defects found and not already fixing them - if (!$clean and !$fix) { - print << "EOM" - -NOTE: For some of the reported defects, checkpatch may be able to - mechanically convert to the typical style using --fix or --fix-inplace. -EOM - } - # If there were whitespace errors which cleanpatch can fix - # then suggest that. - if ($rpt_cleaners) { - $rpt_cleaners = 0; - print << "EOM" - -NOTE: Whitespace errors detected. - You may wish to use scripts/cleanpatch or scripts/cleanfile -EOM - } - } - - if ($clean == 0 && $fix && - ("@rawlines" ne "@fixed" || - $#fixed_inserted >= 0 || $#fixed_deleted >= 0)) { - my $newfile = $filename; - $newfile .= ".EXPERIMENTAL-checkpatch-fixes" if (!$fix_inplace); - my $linecount = 0; - my $f; - - @fixed = fix_inserted_deleted_lines(\@fixed, \@fixed_inserted, \@fixed_deleted); - - open($f, '>', $newfile) - or die "$P: Can't open $newfile for write\n"; - foreach my $fixed_line (@fixed) { - $linecount++; - if ($file) { - if ($linecount > 3) { - $fixed_line =~ s/^\+//; - print $f $fixed_line . "\n"; - } - } else { - print $f $fixed_line . "\n"; - } - } - close($f); - - if (!$quiet) { - print << "EOM"; - -Wrote EXPERIMENTAL --fix correction(s) to '$newfile' - -Do _NOT_ trust the results written to this file. -Do _NOT_ submit these changes without inspecting them for correctness. - -This EXPERIMENTAL file is simply a convenience to help rewrite patches. -No warranties, expressed or implied... -EOM - } - } - - if ($quiet == 0) { - print "\n"; - if ($clean == 1) { - print "$vname has no obvious style problems and is ready for submission.\n"; - } else { - print "$vname has style problems, please review.\n"; - } - } - return $clean; -} diff --git a/tools/rimage/scripts/const_structs.checkpatch b/tools/rimage/scripts/const_structs.checkpatch deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/tools/rimage/src/include/rimage/module.h b/tools/rimage/src/include/rimage/module.h index 1fd3f6c020b7..bd4b93608078 100644 --- a/tools/rimage/src/include/rimage/module.h +++ b/tools/rimage/src/include/rimage/module.h @@ -127,10 +127,11 @@ void module_close(struct module *module); * @param module module structure * @param mem_cfg memory configration structure * @param verbose verbose logging selection + * @param ignore_detached do not mark detached sections * @return error code */ void module_parse_sections(struct module *module, const struct memory_config *mem_cfg, - bool verbose); + bool verbose, bool ignore_detached); /** * Read module section to memory buffer diff --git a/tools/rimage/src/include/rimage/rimage.h b/tools/rimage/src/include/rimage/rimage.h index 20eafb8dfd95..139b55951fdc 100644 --- a/tools/rimage/src/include/rimage/rimage.h +++ b/tools/rimage/src/include/rimage/rimage.h @@ -65,6 +65,9 @@ struct image { /* Output image is a loadable module */ bool loadable_module; + + /* Do not mark detached sections */ + bool ignore_detached; }; struct memory_zone { diff --git a/tools/rimage/src/include/rimage/sof/user/manifest.h b/tools/rimage/src/include/rimage/sof/user/manifest.h index 9abae6894901..3f20d89ff495 100644 --- a/tools/rimage/src/include/rimage/sof/user/manifest.h +++ b/tools/rimage/src/include/rimage/sof/user/manifest.h @@ -103,11 +103,19 @@ struct sof_man_uuid { uint8_t d[8]; }; +struct sof_man_runtime_info { + uint16_t module_id; + uint16_t state_flags; +}; + /* * Each module has an entry in the FW header. Used by ROM - Immutable. */ struct sof_man_module { - uint8_t struct_id[SOF_MAN_MOD_ID_LEN]; /* SOF_MAN_MOD_ID */ + union { + uint8_t struct_id[SOF_MAN_MOD_ID_LEN]; /* SOF_MAN_MOD_ID */ + struct sof_man_runtime_info runtime_info; + }; uint8_t name[SOF_MAN_MOD_NAME_LEN]; struct sof_man_uuid uuid; struct sof_man_module_type type; diff --git a/tools/rimage/src/module.c b/tools/rimage/src/module.c index f14e34da28e5..d35765c591ac 100644 --- a/tools/rimage/src/module.c +++ b/tools/rimage/src/module.c @@ -310,7 +310,8 @@ static enum module_section_type get_section_type(const struct elf_section_header } } -void module_parse_sections(struct module *module, const struct memory_config *mem_cfg, bool verbose) +void module_parse_sections(struct module *module, const struct memory_config *mem_cfg, bool verbose, + bool ignore_detached) { const uint32_t valid = (SHF_WRITE | SHF_ALLOC | SHF_EXECINSTR); uint16_t i; @@ -344,7 +345,7 @@ void module_parse_sections(struct module *module, const struct memory_config *me out_section->size = sect->data.size; out_section->type = get_section_type(sect); out_section->rom = section_is_rom(mem_cfg, sect); - out_section->detached = section_is_detached(mem_cfg, sect); + out_section->detached = !ignore_detached && section_is_detached(mem_cfg, sect); out_section->address = sect->data.vaddr; out_section->load_address = find_physical_address(&module->elf, sect->data.vaddr); diff --git a/tools/rimage/src/rimage.c b/tools/rimage/src/rimage.c index fb8aba95611d..8e50bf9f5033 100644 --- a/tools/rimage/src/rimage.c +++ b/tools/rimage/src/rimage.c @@ -6,6 +6,7 @@ #include <stdlib.h> #include <stdio.h> #include <unistd.h> +#include <getopt.h> #include <errno.h> #include <string.h> #include <stdbool.h> @@ -34,6 +35,8 @@ static void usage(char *name) fprintf(stdout, "\t -y verify signed file\n"); fprintf(stdout, "\t -q resign binary\n"); fprintf(stdout, "\t -p set PV bit\n"); + fprintf(stdout, "\t -d ignore detached sections\n"); + fprintf(stdout, "\t -Q, --quiet suppress informational stdout logs\n"); } int main(int argc, char *argv[]) @@ -45,12 +48,18 @@ int main(int argc, char *argv[]) int use_ext_man = 0; unsigned int pv_bit = 0; bool imr_type_override = false; + bool quiet = false; + static const struct option long_options[] = { + { "quiet", no_argument, NULL, 'Q' }, + { NULL, 0, NULL, 0 } + }; memset(&image, 0, sizeof(image)); image.imr_type = MAN_DEFAULT_IMR_TYPE; - while ((opt = getopt(argc, argv, "ho:va:s:k:ri:f:b:ec:y:q:pl")) != -1) { + while ((opt = getopt_long(argc, argv, "ho:va:s:k:ri:f:b:ec:y:q:pldQ", + long_options, NULL)) != -1) { switch (opt) { case 'o': image.out_file = optarg; @@ -101,6 +110,13 @@ int main(int argc, char *argv[]) case 'l': image.loadable_module = true; break; + case 'd': + /* ignore detached sections */ + image.ignore_detached = true; + break; + case 'Q': + quiet = true; + break; default: /* getopt's default error message is good enough */ return 1; @@ -153,6 +169,10 @@ int main(int argc, char *argv[]) return -EINVAL; } } + + if (quiet && !freopen("/dev/null", "w", stdout)) + fprintf(stderr, "error: unable to redirect stdout: %s\n", strerror(errno)); + /* find machine */ heap_adsp = malloc(sizeof(struct adsp)); if (!heap_adsp) { @@ -225,7 +245,8 @@ int main(int argc, char *argv[]) if (ret < 0) goto out; - module_parse_sections(&image.module[i].file, &image.adsp->mem, image.verbose); + module_parse_sections(&image.module[i].file, &image.adsp->mem, image.verbose, + image.ignore_detached); /* When there is more than one module, then first one is bootloader. * Does not apply to building a image of a loadable module. */ diff --git a/tools/test/audio/process_test.m b/tools/test/audio/process_test.m index 70df2978a439..1af293b1967a 100644 --- a/tools/test/audio/process_test.m +++ b/tools/test/audio/process_test.m @@ -442,7 +442,7 @@ function test_result_print(t, testverbose, testacronym, test) %% FIXME: get unique string to keep all the incremental logs for i = 1:length(test.ph) - title(test.ph(i), tstr); + title(test.ph(i), tstr, 'Interpreter', 'none'); end for i = 1:length(test.fh) diff --git a/tools/test/audio/src_test.m b/tools/test/audio/src_test.m index 2c99bc1bded5..6b28b62693d9 100644 --- a/tools/test/audio/src_test.m +++ b/tools/test/audio/src_test.m @@ -486,9 +486,9 @@ function src_test_result_print(t, testverbose, testacronym, ph) tstr = sprintf('%s SRC %d, %d', testverbose, t.fs1, t.fs2); if nargin > 3 && ~isempty(ph) - title(ph, tstr); + title(ph, tstr, 'Interpreter', 'none'); else - title(tstr); + title(tstr, 'Interpreter', 'none'); end pfn = sprintf('plots/%s_src_%d_%d.png', testacronym, t.fs1, t.fs2); print(pfn, '-dpng'); diff --git a/tools/test/audio/tdfb_test.m b/tools/test/audio/tdfb_test.m index 3dcf1fecf535..0c75c3cf1053 100644 --- a/tools/test/audio/tdfb_test.m +++ b/tools/test/audio/tdfb_test.m @@ -208,7 +208,7 @@ function tdfb_test(xtrun) hold off grid on; legend('ch1 in','ch1 out'); - title(tstr); + title(tstr, 'Interpreter', 'none'); end end diff --git a/tools/testbench/file.c b/tools/testbench/file.c index 282dc509cce0..18fb96481564 100644 --- a/tools/testbench/file.c +++ b/tools/testbench/file.c @@ -27,7 +27,6 @@ #include "testbench/file_ipc4.h" #include "../../src/audio/copier/copier.h" - SOF_DEFINE_REG_UUID(file); DECLARE_TR_CTX(file_tr, SOF_UUID(file_uuid), LOG_LEVEL_INFO); LOG_MODULE_REGISTER(file, CONFIG_SOF_LOG_LEVEL); @@ -669,7 +668,8 @@ static int file_init(struct processing_module *mod) } /* Change to DAI type is needed to avoid uninitialized hw params in - * pipeline_params, A file host can be left as SOF_COMP_MODULE_ADAPTER + * pipeline_params. For capture the file write is the host so set + * SOF_COMP_HOST to skip sink check in module_adapter_prepare(). */ if (dev->direction == SOF_IPC_STREAM_PLAYBACK) { dev->ipc_config.type = SOF_COMP_DAI; @@ -678,6 +678,8 @@ static int file_init(struct processing_module *mod) fprintf(stderr, "error: failed set dai data.\n"); goto error; } + } else { + dev->ipc_config.type = SOF_COMP_HOST; } break; diff --git a/tools/testbench/include/testbench/utils.h b/tools/testbench/include/testbench/utils.h index 7a35c935d66b..a52688cb93dc 100644 --- a/tools/testbench/include/testbench/utils.h +++ b/tools/testbench/include/testbench/utils.h @@ -65,7 +65,7 @@ struct tb_glb_state { #define TB_NAME_SIZE 256 #define TB_MAX_CONFIG_COUNT 2 #define TB_MAX_CONFIG_NAME_SIZE 64 -#define TB_MAX_CTLS 16 +#define TB_MAX_CTLS 32 struct tb_mq_desc { char queue_name[TB_NAME_SIZE]; @@ -148,6 +148,17 @@ struct testbench_prm { #endif }; +/** + * @brief Record of heap memory usage for a module. + * + * Stores the maximum heap usage observed for a specific module, + * used for profiling and memory analysis in testbench. + */ +struct tb_heap_usage_record { + char *module_name; /**< Name of the module */ + size_t heap_max; /**< Maximum heap usage in bytes */ +}; + extern int debug; int tb_decode_enum(struct snd_soc_tplg_enum_control *enum_ctl, char *token); @@ -169,6 +180,18 @@ int tb_set_up_all_pipelines(struct testbench_prm *tp); int tb_setup(struct sof *sof, struct testbench_prm *tp); bool tb_is_pipeline_enabled(struct testbench_prm *tp, int pipeline_id); bool tb_schedule_pipeline_check_state(struct testbench_prm *tp); + +/** + * @brief Collect heap usage statistics for all modules. + * + * Iterates over the active modules in the testbench and records the maximum + * heap usage for each one into the provided array. + * + * @param tp Pointer to testbench parameters. + * @param rec Array of heap usage records to populate. + * @param count Pointer to an integer that receives the number of records written. + */ +void tb_collect_heap_usage(struct testbench_prm *tp, struct tb_heap_usage_record *rec, int *count); void tb_debug_print(char *message); void tb_free(struct sof *sof); void tb_free_topology(struct testbench_prm *tp); diff --git a/tools/testbench/testbench.c b/tools/testbench/testbench.c index 45c5c8843494..670f34c1ab27 100644 --- a/tools/testbench/testbench.c +++ b/tools/testbench/testbench.c @@ -240,7 +240,9 @@ static int parse_input_args(int argc, char **argv, struct testbench_prm *tp) return ret; } -static void test_pipeline_stats(struct testbench_prm *tp, long long delta_t) +static void test_pipeline_stats(struct testbench_prm *tp, long long delta_t, + struct tb_heap_usage_record *heap_records, + int heap_records_count) { long long file_cycles, pipeline_cycles; float pipeline_mcps; @@ -284,22 +286,28 @@ static void test_pipeline_stats(struct testbench_prm *tp, long long delta_t) frames_out = n_out / tp->channels_out; printf("Input sample (frame) count: %d (%d)\n", n_in, n_in / tp->channels_in); printf("Output sample (frame) count: %d (%d)\n", n_out, frames_out); + if (heap_records_count > 0) { + for (i = 0; i < heap_records_count; i++) + printf("Heap usage for module %s: %u bytes\n", + heap_records[i].module_name, (uint32_t)heap_records[i].heap_max); + } + + printf("\n"); if (tp->total_cycles) { pipeline_cycles = tp->total_cycles - file_cycles; pipeline_mcps = (float)pipeline_cycles * tp->fs_out / frames_out / 1e6; + if (tb_check_trace(LOG_LEVEL_DEBUG)) + printf("Warning: Use -d 3 or smaller value to avoid traces to increase MCPS.\n"); + printf("Total execution cycles: %lld\n", tp->total_cycles); printf("File component cycles: %lld\n", file_cycles); printf("Pipeline cycles: %lld\n", pipeline_cycles); - printf("Pipeline MCPS: %6.2f\n", pipeline_mcps); - if (tb_check_trace(LOG_LEVEL_DEBUG)) - printf("Warning: Use -d 3 or smaller value to avoid traces to increase MCPS.\n"); + printf("Pipeline MCPS: %6.2f\n\n", pipeline_mcps); } if (delta_t) - printf("Total execution time: %lld us, %.2f x realtime\n", - delta_t, (float)frames_out / tp->fs_out * 1000000 / delta_t); - - printf("\n"); + printf("Total execution time: %lld us, %.2f x realtime\n\n", delta_t, + (float)frames_out / tp->fs_out * 1000000 / delta_t); } /* @@ -308,14 +316,16 @@ static void test_pipeline_stats(struct testbench_prm *tp, long long delta_t) */ static int pipline_test(struct testbench_prm *tp) { - float samples_to_ns; - int dp_count = 0; - struct timespec td0, td1; + struct tb_heap_usage_record heap_usage_records[TB_NUM_WIDGETS_SUPPORTED]; struct file_state *out_stat; - long long delta_t; + struct timespec td0 = {0}, td1 = {0}; + long long delta_t = 0; int64_t next_control_ns; int64_t time_ns; + float samples_to_ns; int err; + int heap_usage_records_count = 0; + int dp_count = 0; /* build, run and teardown pipelines */ while (dp_count < tp->dynamic_pipeline_iterations) { @@ -392,8 +402,10 @@ static int pipline_test(struct testbench_prm *tp) } tb_schedule_pipeline_check_state(tp); /* Once more to flush out remaining data */ - tb_gettime(&td1); + delta_t = (td1.tv_sec - td0.tv_sec) * 1000000; + delta_t += (td1.tv_nsec - td0.tv_nsec) / 1000; + tb_collect_heap_usage(tp, heap_usage_records, &heap_usage_records_count); out: err = tb_set_reset_state(tp); @@ -403,12 +415,7 @@ static int pipline_test(struct testbench_prm *tp) break; } - /* TODO: This should be printed after reset and free to get cleaner output - * but the file internal status would be lost there. - */ - delta_t = (td1.tv_sec - td0.tv_sec) * 1000000; - delta_t += (td1.tv_nsec - td0.tv_nsec) / 1000; - test_pipeline_stats(tp, delta_t); + test_pipeline_stats(tp, delta_t, heap_usage_records, heap_usage_records_count); err = tb_free_all_pipelines(tp); if (err < 0) { diff --git a/tools/testbench/topology_ipc4.c b/tools/testbench/topology_ipc4.c index 5917da8cb64e..9c4900f92fed 100644 --- a/tools/testbench/topology_ipc4.c +++ b/tools/testbench/topology_ipc4.c @@ -1286,6 +1286,7 @@ static int tb_kcontrol_cb_new(struct snd_soc_tplg_ctl_hdr *tplg_ctl, struct snd_soc_tplg_mixer_control *tplg_mixer; struct snd_soc_tplg_enum_control *tplg_enum; struct snd_soc_tplg_bytes_control *tplg_bytes; + struct sof_abi_hdr *abi; if (glb->num_ctls >= TB_MAX_CTLS) { fprintf(stderr, "Error: Too many controls already.\n"); @@ -1358,7 +1359,26 @@ static int tb_kcontrol_cb_new(struct snd_soc_tplg_ctl_hdr *tplg_ctl, tplg_bytes->priv.size); return -EINVAL; } - ctl->data = tplg_bytes->priv.data; + + if (tplg_bytes->priv.size >= sizeof(struct sof_abi_hdr)) { + abi = (struct sof_abi_hdr *)tplg_bytes->priv.data; + if (abi->size > TB_MAX_BYTES_DATA_SIZE) { + fprintf(stderr, + "Error: ABI payload size %u exceeds max %d\n", + abi->size, TB_MAX_BYTES_DATA_SIZE); + return -EINVAL; + } + if (tplg_bytes->priv.size < + sizeof(struct sof_abi_hdr) + abi->size) { + fprintf(stderr, + "Error: bytes data size %d is smaller than ABI header + payload (%zu + %u)\n", + tplg_bytes->priv.size, + sizeof(struct sof_abi_hdr), abi->size); + return -EINVAL; + } + ctl->data = tplg_bytes->priv.data; + } + ctl->comp_info = comp_info; strncpy(ctl->name, tplg_ctl->name, TB_MAX_CTL_NAME_CHARS); break; diff --git a/tools/testbench/utils.c b/tools/testbench/utils.c index f6efbbcbc387..597e3aaa043d 100644 --- a/tools/testbench/utils.c +++ b/tools/testbench/utils.c @@ -6,6 +6,8 @@ #include <platform/lib/ll_schedule.h> #include <sof/audio/component.h> #include <sof/ipc/topology.h> +#include <sof/ipc/msg.h> +#include <sof/ipc/notification_pool.h> #include <sof/lib/notifier.h> #include <ctype.h> @@ -211,6 +213,13 @@ void tb_free(struct sof *sof) } free(*arch_schedulers_get()); + /* Drain IPC message queue to free queued notifications */ + while (!list_is_empty(&sof->ipc->msg_list)) + ipc_send_queued_msg(); + + /* Free notification pool items */ + ipc_notification_pool_free(); + /* free IPC data */ iipc = sof->ipc->private; free(sof->ipc->comp_data); diff --git a/tools/testbench/utils_ipc3.c b/tools/testbench/utils_ipc3.c index 96804badaf21..32c368efeb9a 100644 --- a/tools/testbench/utils_ipc3.c +++ b/tools/testbench/utils_ipc3.c @@ -32,6 +32,7 @@ int tb_setup(struct sof *sof, struct testbench_prm *tp) sys_comp_module_crossover_interface_init(); sys_comp_module_dcblock_interface_init(); sys_comp_module_demux_interface_init(); + sys_comp_module_dolby_dax_audio_processing_interface_init(); sys_comp_module_drc_interface_init(); sys_comp_module_eq_fir_interface_init(); sys_comp_module_eq_iir_interface_init(); @@ -440,4 +441,9 @@ int tb_set_bytes_control(struct testbench_prm *tp, struct tb_ctl *ctl, uint32_t return 0; } +void tb_collect_heap_usage(struct testbench_prm *tp, struct tb_heap_usage_record *rec, int *count) +{ + *count = 0; +} + #endif /* CONFIG_IPC_MAJOR_3 */ diff --git a/tools/testbench/utils_ipc4.c b/tools/testbench/utils_ipc4.c index 584ba138c210..00449de3bbd3 100644 --- a/tools/testbench/utils_ipc4.c +++ b/tools/testbench/utils_ipc4.c @@ -4,6 +4,7 @@ #if CONFIG_IPC_MAJOR_4 +#include <sof/audio/module_adapter/module/generic.h> #include <sof/audio/component_ext.h> #include <sof/lib/notifier.h> #include <sof/audio/component_ext.h> @@ -43,9 +44,11 @@ int tb_setup(struct sof *sof, struct testbench_prm *tp) /* Module adapter components */ sys_comp_module_aria_interface_init(); + sys_comp_module_asrc_interface_init(); sys_comp_module_crossover_interface_init(); sys_comp_module_dcblock_interface_init(); sys_comp_module_demux_interface_init(); + sys_comp_module_dolby_dax_audio_processing_interface_init(); sys_comp_module_drc_interface_init(); sys_comp_module_eq_fir_interface_init(); sys_comp_module_eq_iir_interface_init(); @@ -63,7 +66,7 @@ int tb_setup(struct sof *sof, struct testbench_prm *tp) sys_comp_module_selector_interface_init(); sys_comp_module_sound_dose_interface_init(); sys_comp_module_src_interface_init(); - sys_comp_module_asrc_interface_init(); + sys_comp_module_stft_process_interface_init(); sys_comp_module_tdfb_interface_init(); sys_comp_module_template_interface_init(); sys_comp_module_volume_interface_init(); @@ -287,7 +290,8 @@ static int tb_set_up_widget(struct testbench_prm *tp, struct tplg_comp_info *com /* send the bytes data from kcontrols associated with current widget */ if (ctl->module_id != comp_info->module_id || ctl->instance_id != comp_info->instance_id || - ctl->type != SND_SOC_TPLG_TYPE_BYTES) + ctl->type != SND_SOC_TPLG_TYPE_BYTES || + !ctl->data) continue; abi = (struct sof_abi_hdr *)ctl->data; @@ -700,4 +704,41 @@ int tb_set_bytes_control(struct testbench_prm *tp, struct tb_ctl *ctl, uint32_t (struct sof_abi_hdr *)data); } +void tb_collect_heap_usage(struct testbench_prm *tp, struct tb_heap_usage_record *records, + int *count_out) +{ + struct list_item *item; + size_t hwm; + int count = 0; + + list_for_item(item, &tp->widget_list) { + struct tplg_comp_info *info = container_of(item, struct tplg_comp_info, item); + uint32_t comp_id = IPC4_COMP_ID(info->module_id, info->instance_id); + struct comp_dev *dev = ipc4_get_comp_dev(comp_id); + + if (!dev || !dev->mod) + continue; + + /* In testbench environment, skip AIF/DAI because they are not real components. */ + if (info->type == SND_SOC_TPLG_DAPM_AIF_IN || + info->type == SND_SOC_TPLG_DAPM_AIF_OUT || + info->type == SND_SOC_TPLG_DAPM_DAI_IN || + info->type == SND_SOC_TPLG_DAPM_DAI_OUT) + continue; + + if (count >= TB_NUM_WIDGETS_SUPPORTED) { + fprintf(stderr, "Error: Too many components for heap records, max %d.\n", + TB_NUM_WIDGETS_SUPPORTED); + break; + } + + module_adapter_heap_usage(dev->mod, &hwm); + records[count].module_name = info->name; + records[count].heap_max = hwm; + count++; + } + + *count_out = count; +} + #endif /* CONFIG_IPC_MAJOR_4 */ diff --git a/tools/topology/README.md b/tools/topology/README.md new file mode 100644 index 000000000000..8d10954a906a --- /dev/null +++ b/tools/topology/README.md @@ -0,0 +1,22 @@ +# SOF Topology Tools (`tools/topology`) + +The `tools/topology` directory contains the build infrastructure and source files used to generate Advanced Linux Sound Architecture (ALSA) topology (`.tplg`) binaries for Sound Open Firmware. + +ALSA topology files describe the audio DSP graph (pipelines, widgets, routes, DAIs) and are loaded dynamically by the Linux kernel SOF driver during initialization. This allows a single generic driver to support a wide variety of hardware configurations and routing paths without requiring source code changes in the kernel. + +## Directory Structure + +To support the evolution of the SOF topology syntax, the configuration files are split into two main versions: + +- [`topology1/`](topology1/README.md): Contains the legacy (v1) `m4`-based ALSA topology generation scripts and configuration files. Topology v1 heavily relies on `m4` macro preprocessing to generate the final `.conf` files before being compiled by `alsatplg`. +- [`topology2/`](topology2/README.md): Contains the newer (v2) ALSA topology generation files. Topology v2 introduces a more structured, object-oriented syntax natively supported by newer versions of the `alsatplg` compiler (specifically the pre-processor `-p` flag), reducing reliance on external macro languages. + +## Build Process + +The topology build process is managed by `CMakeLists.txt` in this root directory. It performs the following key steps: + +1. **Compiler Detection**: It locates the `alsatplg` tool (usually built in `tools/bin/alsatplg` alongside the firmware) and checks its version. +2. **Feature Validation**: It ensures the `alsatplg` version is at least `1.2.5`. Older versions are known to silently corrupt certain topology structures (e.g., converting `codec_consumer` to `codec_master`). If the tool is too old, topology generation is safely skipped with a warning. +3. **Target Generation**: It provides macros (`add_alsatplg_command` and `add_alsatplg2_command`) used by the subdirectories to invoke the topology compiler on the pre-processed `.conf` files to generate the final `.tplg` binaries. + +The `topologies` CMake target is the master target that depends on the generation targets inside both `topology1` and `topology2`. diff --git a/tools/topology/topology1/README.md b/tools/topology/topology1/README.md new file mode 100644 index 000000000000..78253bb06ee2 --- /dev/null +++ b/tools/topology/topology1/README.md @@ -0,0 +1,86 @@ +# ALSA Topology v1 (`tools/topology/topology1`) + +This directory contains the source files and `m4` macros used to generate version 1 of the ALSA topology binary files (`.tplg`) for Sound Open Firmware. + +## Overview + +Topology v1 relies heavily on the `m4` macro processor to handle the complexity and reusability of DSP graph definitions. Because raw ALSA configuration files for complex audio routing can become extremely verbose and repetitive, SOF uses `m4` to define logical blocks (like DAIs, SSPs, pipelines, and volume controls) that can be easily instantiated and connected. + +## Structure + +The core generation components include: + +- **`m4/`**: This directory contains the foundational macro definitions. These macros define how base elements like widgets (e.g., `PGA`, `Mixer`, `SRC`), pipelines, and routing paths are constructed in the ALSA `.conf` format. +- **`common/`**: Contains shared components and standard pipeline definitions that are reused across multiple different hardware platforms. +- **`platform/`**: Contains macros and configurations specific to individual hardware architectures (e.g., Intel cAVS, IMX). +- **Platform `.m4` files**: At the root of `topology1`, there are numerous `.m4` files (e.g., `sof-cavs-nocodec.m4`, `sof-imx8-wm8960.m4`). These are the top-level files that instantiate the macros to build a complete topology graph for a specific board or hardware configuration. + +## Component Assembly + +Building a topology in v1 is essentially a process of calling nested `m4` macros to piece together the DSP pipeline. Here's how the ingredients combine: + +1. **Base Macros (`m4/`)**: Define the raw ALSA syntax for things like a single PGA volume control or a DAI link. +2. **Pipelines (`common/`)**: Define a logical sequence of base widgets. For example, a "Playback Pipeline" macro might chain together a Host PCM, a Buffer, a Volume Control (PGA), and an output Buffer. +3. **Top-Level File (`*.m4`)**: Instantiates the pipelines, defines the physical DAI hardware connections, and sets up routing lines between the pipelines and the DAIs. + +```mermaid +graph TD + subgraph "Base Ingredients (m4/)" + A[Widget: PCM] + B[Widget: PGA Volume] + C[Widget: DAI] + D[Widget: Mixer] + end + + subgraph "Recipes (common/ & platform/)" + P1[Playback Pipeline] + P1 -.->|Includes| A + P1 -.->|Includes| B + + P2[Capture Pipeline] + P2 -.->|Includes| D + P2 -.->|Includes| C + end + + subgraph "The Meal (sof-board.m4)" + Top[Top-Level Topology] + Top ==>|Instantiates| P1 + Top ==>|Instantiates| P2 + Top ==>|Defines| Routes[Audio Routes] + Routes -.->|Connects Pipeline to DAI| Top + end +``` + +## Build Flow + +### Architecture Diagram + +```mermaid +flowchart TD + m4_core(["m4/ (Core Macros)"]) -.-> m4_plat(["platform/ (Platform Macros)"]) + m4_comp(["common/ (Shared Components)"]) -.-> m4_plat + + m4_plat -.-> m4_top(["sof-*.m4 (Top-level Platform File)"]) + + m4_top -->|"m4 processor"| conf["ALSA .conf Output"] + + conf -->|"alsatplg"| tplg["ALSA .tplg Binary"] +``` + +When the SOF build system compiles a v1 topology: + +1. The `m4` processor takes a top-level platform `.m4` file. +2. It expands all the macros defined in `m4/`, `common/`, and `platform/`. +3. The output is a raw ALSA `.conf` text file. +4. The `alsatplg` compiler parses this `.conf` file and compiles it into the final `.tplg` binary format loaded by the kernel. + +### Build Instructions + +Topologies are built automatically as part of the standard SOF CMake build process. To explicitly build all topologies (including v1): + +```bash +# From your build directory: +make topologies1 +# OR +cmake --build . --target topologies1 +``` diff --git a/tools/topology/topology1/sof/pipe-rtnr-capture.m4 b/tools/topology/topology1/sof/pipe-rtnr-capture.m4 index fd64a79ac9c2..b40f6bc6d286 100644 --- a/tools/topology/topology1/sof/pipe-rtnr-capture.m4 +++ b/tools/topology/topology1/sof/pipe-rtnr-capture.m4 @@ -42,7 +42,7 @@ CONTROLBYTES_PRIV(DEF_RTNR_PRIV, # RTNR Bytes control with max value of 255 C_CONTROLBYTES_READONLY(DEF_RTNR_BYTES, PIPELINE_ID, CONTROLBYTES_OPS(bytes, 258 binds the mixer control to bytes get handlers, 258), - CONTROLBYTES_EXTOPS(258 binds the mixer control to bytes get handlers, 258), + CONTROLBYTES_EXTOPS(258 binds the mixer control to bytes get/put handlers, 258, 258), , , , CONTROLBYTES_MAX(, 256), , diff --git a/tools/topology/topology1/sof/pipe-rtnr-google-rtc-audio-processing-capture.m4 b/tools/topology/topology1/sof/pipe-rtnr-google-rtc-audio-processing-capture.m4 index 4009f61ac100..6e5f3cd7aacc 100644 --- a/tools/topology/topology1/sof/pipe-rtnr-google-rtc-audio-processing-capture.m4 +++ b/tools/topology/topology1/sof/pipe-rtnr-google-rtc-audio-processing-capture.m4 @@ -44,7 +44,7 @@ CONTROLBYTES_PRIV(DEF_RTNR_PRIV, # RTNR Bytes control with max value of 255 C_CONTROLBYTES_READONLY(DEF_RTNR_BYTES, PIPELINE_ID, CONTROLBYTES_OPS(bytes, 258 binds the mixer control to bytes get/put handlers, 258), - CONTROLBYTES_EXTOPS(258 binds the mixer control to bytes get/put handlers, 258), + CONTROLBYTES_EXTOPS(258 binds the mixer control to bytes get/put handlers, 258, 258), , , , CONTROLBYTES_MAX(, 256), , diff --git a/tools/topology/topology2/README.md b/tools/topology/topology2/README.md new file mode 100644 index 000000000000..bea7ffcfdcde --- /dev/null +++ b/tools/topology/topology2/README.md @@ -0,0 +1,401 @@ +# ALSA Topology v2 (`tools/topology/topology2`) + +This directory contains the ALSA Topology v2 source files for Sound Open Firmware. + +This readme is a quick intro to the topic. Please refer to full documentation +at https://thesofproject.github.io/latest/developer_guides/topology2/topology2.html + +## Overview + +Topology v2 is a modernization of the ALSA topology infrastructure. It aims to solve the verbosity and complexity issues of Topology v1 without relying as heavily on external macro processors like `m4`. + +Topology v2 introduces an object-oriented pre-processing layer directly into the newer `alsatplg` compiler (invoked via the `-p` flag). This allows the configuration files to define classes, objects, and attributes natively within the ALSA configuration syntax. + +Building topologies requires `alsatplg` version 1.2.7 or later. The version check is +enforced in `CMakeLists.txt` at configure time. + +## Key Advantages + +- **Object-Oriented Syntax**: Topology v2 allows for the definition of classes (`Class.Widget`, `Class.Pipeline`) and object instantiation, making the topology files much easier to read, maintain, and extend. +- **Reduced Pre-Processing**: By handling templating and instantiation inside the `alsatplg` tool itself, the build process is cleaner and errors are easier to trace back to the source files, as opposed to deciphering expanded `m4` output. +- **Dynamic Variables**: Attributes can be parameterized and passed down to nested objects, allowing for highly flexible definitions of audio pipelines. + +Topology2 uses a class-based object model built on four core concepts: + +* **Classes** (`Class.Pipeline`, `Class.Widget`, `Class.PCM`) define reusable templates + with default attribute values +* **Objects** (`Object.Pipeline`, `Object.Widget`, `Object.PCM`) instantiate classes with + specific parameter values +* **Define blocks** provide variable substitution using `$VARIABLE` syntax, enabling + parameterized topologies +* **IncludeByKey** enables conditional includes based on variable values, used primarily + for platform-specific overrides + +## Structure and Component Assembly + +Topology v2 shifts the source code layout from macro definitions to class definitions, leveraging the structured nature of the newer compiler. + +The directory is built around these core parts: + +- **`include/`**: Contains the base ALSA topology class definitions. + - `components/`: Base classes for individual processing nodes (e.g., PGA, Mixer, SRC). + - `pipelines/`: Reusable pipeline class definitions that instantiate and connect several base components. + - `dais/`: Definitions for Digital Audio Interfaces (hardware endpoints). + - `controls/`: Definitions for volume, enum, and byte controls. +- **`platform/`**: Hardware-specific configurations and overrides (e.g., Intel-specific IPC attributes). +- **Top-Level `.conf` files**: The board-specific configurations (e.g., `cavs-rt5682.conf`). These behave like standard ALSA `.conf` files but utilize the `@include` directive to import classes and instantiate them dynamically. + +### Detailed Directory Layout + +```text +tools/topology/topology2/ +├── CMakeLists.txt # Build system entry point +├── get_abi.sh # ABI version extraction script +├── cavs-sdw.conf # SoundWire topology entry point +├── sof-hda-generic.conf # HDA generic topology entry point +├── cavs-mixin-mixout-hda.conf # HDA with mixer pipelines +├── cavs-nocodec.conf # SSP nocodec topology +├── ... # Other top-level .conf entry points +├── include/ +│ ├── common/ # Core class definitions (PCM, route, audio formats) +│ ├── components/ # Widget/component classes (gain, mixin, EQ, DRC) +│ ├── controls/ # Control classes (mixer, enum, bytes) +│ ├── dais/ # DAI classes (SSP, DMIC, HDA, ALH) +│ └── pipelines/ # Pipeline template classes +│ └── cavs/ # CAVS-architecture pipeline classes +├── platform/ +│ └── intel/ # Platform-specific overrides (tgl, mtl, lnl, ptl) +├── production/ # CMake targets for production topologies +│ ├── tplg-targets-ace1.cmake # Intel ACE1 (MTL) targets +│ ├── tplg-targets-ace2.cmake # Intel ACE2 (LNL) targets +│ ├── tplg-targets-ace3.cmake # Intel ACE3 (PTL) targets +│ └── ... # Additional platform target files +├── development/ # CMake targets for development/testing +└── doc/ # Doxygen documentation source +``` + +```mermaid +graph TD + subgraph "Class Definitions (include/)" + C_Comp[Class: components/pga.conf] + C_Pipe[Class: pipelines/volume-playback.conf] + C_DAI[Class: dais/ssp.conf] + end + + subgraph "Pipeline Object" + C_Pipe -.->|Instantiates| C_Comp + end + + subgraph "Top-Level Topology (board.conf)" + Board[cavs-board.conf] + Board -->|"@include"| C_Pipe + Board -->|"@include"| C_DAI + + Obj_Pipe[Object.Pipeline.volume-playback.1] + Obj_DAI[Object.Dai.SSP.1] + + Board -.->|Instantiates| Obj_Pipe + Board -.->|Instantiates| Obj_DAI + + Routes[Object.Base.route] + Board -.->|Connects Objects| Routes + end +``` + +## Architecture and Build Flow + +Unlike v1, Topology v2 processes objects and classes within the `alsatplg` compiler itself. + +### Diagram + +```mermaid +flowchart TD + conf_classes(["Class Definitions (.conf)"]) -.-> conf_objs(["Object Instantiations (.conf)"]) + + conf_objs -->|"alsatplg -p (Pre-processor Engine)"| tplg["ALSA .tplg Binary"] + + subgraph alsatplg_internal [alsatplg Internal Processing] + direction TB + parse["Parse Classes & Objects"] --> resolve["Resolve Attributes"] + resolve --> validate["Validate Topologies"] + end + + conf_objs -.-> alsatplg_internal + alsatplg_internal -.-> tplg +``` + +When building a v2 topology, the `CMakeLists.txt` in `tools/topology/` provides the `add_alsatplg2_command` macro. This macro specifically passes the `-p` flag to `alsatplg`, instructing it to use the new pre-processor engine to resolve the classes and objects defined in the `.conf` files before compiling them into the `.tplg` binary. + +### Build Instructions + +Topologies are built automatically as part of the standard SOF CMake build process. To explicitly build Topology v2 configurations: + +```bash +# From your build directory: +make topologies2 +# OR +cmake --build . --target topologies2 +``` + +To build a specific topology target: + +```bash +make sof-lnl-sdw-cs42l43-l0-cs35l56-l12 +``` + +## Best Practices for Adding New Topology Definitions + +### Topology Structure + +A top-level topology `.conf` file follows a layered configuration pattern: + +```conf +# 1. Search directories +<searchdir:include> +<searchdir:include/common> +<searchdir:include/components> +<searchdir:include/dais> +<searchdir:include/pipelines/cavs> +<searchdir:platform/intel> + +# 2. Include class files +<vendor-token.conf> +<tokens.conf> +<pcm.conf> +<host-copier-gain-mixin-playback.conf> +<mixout-gain-alh-dai-copier-playback.conf> + +# 3. Define block (default variable values) +Define { + PLATFORM "" + NUM_HDMIS 3 + DEEP_BUFFER_PCM_ID 31 +} + +# 4. Platform overrides (conditional includes) +IncludeByKey.PLATFORM { + "mtl" "platform/intel/mtl.conf" + "lnl" "platform/intel/lnl.conf" + "ptl" "platform/intel/ptl.conf" +} + +# 5. Conditional feature includes +IncludeByKey.NUM_HDMIS { + "3" "platform/intel/hdmi-generic.conf" +} + +# 6. DAI, Pipeline, PCM objects +# 7. Route definitions +``` + +### Reusing Existing Bases + +The most common way to add a new topology is to reuse an existing base `.conf` file and +override variables through a cmake target entry. Targets are defined in +`production/tplg-targets-*.cmake` files using a tuple format: + +```text +"input-conf;output-name;variables" +``` + +For example, to add a new SoundWire topology variant for ACE2 (Lunar Lake): + +```text +"cavs-sdw\;sof-lnl-sdw-cs42l43-l0-cs35l56-l12\;PLATFORM=lnl,NUM_SDW_AMP_LINKS=2" +``` + +The first element is the base `.conf` file (without extension), the second is the output +`.tplg` filename, and the third is a comma-separated list of variable overrides. + +### Creating a New Base Topology + +When existing bases do not cover a new use case, create a new top-level `.conf` file: + +1. Create a new `.conf` file in `tools/topology/topology2/` following the layered + structure described above +2. Include the required class files from `include/` directories via search directives +3. Define default variables in a `Define` block +4. Add `IncludeByKey.PLATFORM` entries for platform-specific overrides +5. Instantiate DAI, Pipeline, and PCM objects with appropriate IDs +6. Define routes connecting FE mixin outputs to BE mixout inputs +7. Register the topology as a cmake target in the appropriate + `production/tplg-targets-*.cmake` file + +### PCM ID Conventions + +PCM IDs identify audio streams exposed to userspace via ALSA. Each PCM ID must be unique +within a single topology. Different topology families (Intel SoundWire vs HDA) use different +default ID ranges for the same endpoint types. + +**Intel SoundWire PCM IDs:** + +| Endpoint | Default PCM ID | Override Variable | +|---|---|---| +| Jack (playback/capture) | 0 | — | +| Speaker amplifier | 2 | — | +| SDW DMIC | 4 | — | +| HDMI 1 | 5 | `HDMI1_PCM_ID` | +| HDMI 2 | 6 | `HDMI2_PCM_ID` | +| HDMI 3 | 7 | `HDMI3_PCM_ID` | +| PCH DMIC0 | 10 | `DMIC0_PCM_ID` | +| PCH DMIC1 | 11 | `DMIC1_PCM_ID` | +| Jack Echo Ref | 11 | `SDW_JACK_ECHO_REF_PCM_ID` | +| Speaker Echo Ref | 12 | `SDW_SPK_ECHO_REF_PCM_ID` | +| Bluetooth | 2 or 20 | `BT_PCM_ID` | +| Deep Buffer (Jack) | 31 | `DEEP_BUFFER_PCM_ID` | +| Deep Buffer (Speaker) | 35 | `DEEP_BUFFER_PCM_ID_2` | +| DMIC Deep Buffer | 46 | `DMIC0_DEEP_BUFFER_PCM_ID` | +| Compress Jack Out | 50 | `COMPR_PCM_ID` | +| Compress Speaker | 52 | `COMPR_2_PCM_ID` | + +> **Note:** Bluetooth defaults to PCM ID 2 in some topologies and 20 in others. Use the +> `BT_PCM_ID` override variable to set the correct value when BT coexists with a speaker +> amplifier (which also uses PCM ID 2 by default). + +**Intel HDA PCM IDs:** + +| Endpoint | Default PCM ID | Override Variable | +|---|---|---| +| HDA Analog | 0 | — | +| HDMI 1 | 3 | `HDMI1_PCM_ID` | +| HDMI 2 | 4 | `HDMI2_PCM_ID` | +| HDMI 3 | 5 | `HDMI3_PCM_ID` | +| DMIC0 | 6 | `DMIC0_PCM_ID` | +| Deep Buffer | 31 | `DEEP_BUFFER_PCM_ID` | +| Compress HDA Analog | 50 | `COMPR_PCM_ID` | + +Key rules: + +* PCM ID 0 is always the primary playback endpoint +* PCM IDs must be unique within a single topology +* When features coexist (SDW + PCH DMIC + HDMI), adjust IDs via `Define` overrides in + cmake targets to avoid conflicts +* Different topology families (SDW vs HDA) use different default ID ranges for the same + endpoint types + +### Pipeline ID Conventions + +Pipeline IDs are set via the `index` attribute on pipeline objects. Front-end (FE) and +back-end (BE) pipelines are paired, with the FE pipeline at index N and the BE pipeline +at index N+1. + +In SoundWire topologies, pipeline indexes follow the convention documented in +`sdw-amp-generic.conf` and `sdw-dmic-generic.conf`: pipeline index = PCM ID × 10. HDMI +pipelines use a stride-10 pattern where the host pipeline is at N0 and the DAI pipeline +is at N1 (50/51, 60/61, 70/71, 80/81). + +**Intel SoundWire Pipeline IDs:** + +| Pipeline | Default Index | Override Variable | +|---|---|---| +| Jack Playback FE / BE | 0 / 1 | — | +| Jack Capture FE / BE | 10 / 11 | — | +| Deep Buffer (Jack) | 15 | `DEEP_BUFFER_PIPELINE_ID` | +| Deep Buffer (Speaker) | 16 | `DEEP_BUFFER_PIPELINE_ID_2` | +| Speaker FE / BE | 20 / 21 | — | +| Speaker Echo Ref FE / BE | 22 / 23 | — | +| SDW DMIC FE / BE | 40 / 41 | `SDW_DMIC_HOST_PIPELINE_ID` | +| HDMI 1 Host / DAI | 50 / 51 | `HDMI1_HOST_PIPELINE_ID` / `HDMI1_DAI_PIPELINE_ID` | +| HDMI 2 Host / DAI | 60 / 61 | `HDMI2_HOST_PIPELINE_ID` / `HDMI2_DAI_PIPELINE_ID` | +| HDMI 3 Host / DAI | 70 / 71 | `HDMI3_HOST_PIPELINE_ID` / `HDMI3_DAI_PIPELINE_ID` | +| HDMI 4 Host / DAI | 80 / 81 | `HDMI4_HOST_PIPELINE_ID` / `HDMI4_DAI_PIPELINE_ID` | +| Compress Jack / Speaker | 90 / 92 | `COMPR_PIPELINE_ID` / `COMPR_2_PIPELINE_ID` | +| PCH DMIC0 Host / DAI | 100 / 101 | `DMIC0_HOST_PIPELINE_ID` / `DMIC0_DAI_PIPELINE_ID` | + +**Intel HDA Pipeline IDs:** + +| Pipeline | Default Index | Override Variable | +|---|---|---| +| Analog Playback FE / BE | 1 / 2 | — | +| Analog Capture FE / BE | 3 / 4 | — | +| DMIC0 Host / DAI | 11 / 12 | `DMIC0_HOST_PIPELINE_ID` / `DMIC0_DAI_PIPELINE_ID` | +| Deep Buffer | 15 | `DEEP_BUFFER_PIPELINE_ID` | +| HDMI 1 Host / DAI | 50 / 51 | `HDMI1_HOST_PIPELINE_ID` / `HDMI1_DAI_PIPELINE_ID` | +| HDMI 2 Host / DAI | 60 / 61 | `HDMI2_HOST_PIPELINE_ID` / `HDMI2_DAI_PIPELINE_ID` | +| HDMI 3 Host / DAI | 70 / 71 | `HDMI3_HOST_PIPELINE_ID` / `HDMI3_DAI_PIPELINE_ID` | +| HDMI 4 Host / DAI | 80 / 81 | `HDMI4_HOST_PIPELINE_ID` / `HDMI4_DAI_PIPELINE_ID` | +| Compress HDA Analog Host / DAI | 90 / 91 | `COMPR_PIPELINE_ID` | + +Key rules: + +* FE and BE pipelines are paired: FE = N, BE = N+1 +* SDW convention: pipeline index = PCM ID × 10 (documented in `sdw-amp-generic.conf` and + `sdw-dmic-generic.conf`) +* HDMI uses stride-10: Host = N0, DAI = N1 +* Pipeline IDs must be unique within a single topology +* When adding new endpoints, select IDs in unused ranges that do not conflict with + existing assignments + +### Widget Naming + +Widget names follow the convention `<type>.<pipeline-index>.<instance>`. Examples: + +* `gain.1.1` — gain widget in pipeline 1, instance 1 +* `mixin.15.1` — mixin widget in pipeline 15, instance 1 +* `host-copier.0.playback` — host copier in pipeline 0, playback direction +* `dai-copier.1.ALH` — DAI copier in pipeline 1, ALH type + +### Route Definitions + +Routes connect FE pipeline mixin outputs to BE pipeline mixout inputs. This is the +primary mechanism for linking front-end and back-end pipelines: + +```conf +Object.Base.route [ + { + source "mixin.15.1" + sink "mixout.2.1" + } +] +``` + +Multiple FE pipelines can feed into a single BE mixout. For example, both a normal +playback pipeline and a deep buffer pipeline can route to the same DAI output: + +```text +host-copier.0 -> gain.0 -> mixin.0 ─┐ + ├─> mixout.1 -> gain.1 -> dai-copier.1 -> DAI +host-copier.15 -> gain.15 -> mixin.15┘ +``` + +### Platform Overrides + +Platform-specific configurations are applied using the `IncludeByKey.PLATFORM` mechanism. +Each platform `.conf` file under `platform/intel/` contains `Define` blocks that override +variables such as `DMIC_DRIVER_VERSION`, `SSP_BLOB_VERSION`, and `NUM_HDMIS`. + +Supported platforms: + +* `tgl` — Intel Tiger Lake / Alder Lake (CAVS 2.5) +* `mtl` — Intel Meteor Lake (ACE 1.x) +* `lnl` — Intel Lunar Lake (ACE 2.x) +* `ptl` — Intel Panther Lake (ACE 3.x) + +```conf +IncludeByKey.PLATFORM { + "mtl" "platform/intel/mtl.conf" + "lnl" "platform/intel/lnl.conf" + "ptl" "platform/intel/ptl.conf" +} +``` + +### Registering CMake Targets + +Production topologies are registered in `production/tplg-targets-*.cmake` files. Each +target is a semicolon-separated tuple: + +```text +"input-conf;output-name;variable1=value1,variable2=value2" +``` + +Select the cmake file matching the target platform generation: + +| Platform | CMake Target File | +|---|---| +| Tiger Lake / Alder Lake | `tplg-targets-cavs25.cmake` | +| Meteor Lake | `tplg-targets-ace1.cmake` | +| Lunar Lake | `tplg-targets-ace2.cmake` | +| Panther Lake | `tplg-targets-ace3.cmake` | +| HDA generic | `tplg-targets-hda-generic.cmake` | + +Development and testing topologies go in `development/tplg-targets.cmake`. diff --git a/tools/topology/topology2/cavs-benchmark-hda.conf b/tools/topology/topology2/cavs-benchmark-hda.conf index 49e610ed1db9..95ab67431812 100644 --- a/tools/topology/topology2/cavs-benchmark-hda.conf +++ b/tools/topology/topology2/cavs-benchmark-hda.conf @@ -33,12 +33,14 @@ <aria.conf> <asrc.conf> <dcblock.conf> +<dolby-dax.conf> <drc.conf> <eqiir.conf> <eqfir.conf> <gain.conf> <igo_nr.conf> <level_multiplier.conf> +<mfcc.conf> <micsel.conf> <mixin.conf> <mixout.conf> @@ -47,6 +49,7 @@ <sound_dose.conf> <src.conf> <src_lite.conf> +<stft_process.conf> <tdfb.conf> <template_comp.conf> @@ -686,6 +689,22 @@ IncludeByKey.BENCH_CONFIG { <include/bench/dcblock_s32.conf> } + # + # DOLBY_DAX component + # + + "dolby-dax16" { + <include/bench/dolby-dax_s16.conf> + } + + "dolby-dax24" { + <include/bench/dolby-dax_s24.conf> + } + + "dolby-dax32" { + <include/bench/dolby-dax_s32.conf> + } + # # DRC component # @@ -799,6 +818,32 @@ IncludeByKey.BENCH_CONFIG { <include/bench/level_multiplier_s32.conf> } + # + # MFCC component + # + + "mfcc16" { + <include/bench/mfcc_s16.conf> + } + + "mfcc24" { + <include/bench/mfcc_s24.conf> + } + + "mfcc32" { + <include/bench/mfcc_s32.conf> + } + + "mfccmel16" { + <include/bench/mfccmel_s16.conf> + } + "mfccmel24" { + <include/bench/mfccmel_s24.conf> + } + "mfccmel32" { + <include/bench/mfccmel_s32.conf> + } + # # Micsel component # @@ -883,6 +928,56 @@ IncludeByKey.BENCH_CONFIG { <include/bench/src_lite_s32.conf> } + # + # stft_process component, with five different blob configurations. + # + + "stft_process_192_48_16" { + <include/bench/stft_process_s16.conf> + } + "stft_process_192_48_24" { + <include/bench/stft_process_s24.conf> + } + "stft_process_192_48_32" { + <include/bench/stft_process_s32.conf> + } + "stft_process_512_128_16" { + <include/bench/stft_process_s16.conf> + } + "stft_process_512_128_24" { + <include/bench/stft_process_s24.conf> + } + "stft_process_512_128_32" { + <include/bench/stft_process_s32.conf> + } + "stft_process_768_120_16" { + <include/bench/stft_process_s16.conf> + } + "stft_process_768_120_24" { + <include/bench/stft_process_s24.conf> + } + "stft_process_768_120_32" { + <include/bench/stft_process_s32.conf> + } + "stft_process_1024_256_16" { + <include/bench/stft_process_s16.conf> + } + "stft_process_1024_256_24" { + <include/bench/stft_process_s24.conf> + } + "stft_process_1024_256_32" { + <include/bench/stft_process_s32.conf> + } + "stft_process_1536_240_16" { + <include/bench/stft_process_s16.conf> + } + "stft_process_1536_240_24" { + <include/bench/stft_process_s24.conf> + } + "stft_process_1536_240_32" { + <include/bench/stft_process_s32.conf> + } + # # tdfb component # diff --git a/tools/topology/topology2/cavs-benchmark-sdw.conf b/tools/topology/topology2/cavs-benchmark-sdw.conf index 900d897eb7a4..1705facb4cc0 100644 --- a/tools/topology/topology2/cavs-benchmark-sdw.conf +++ b/tools/topology/topology2/cavs-benchmark-sdw.conf @@ -39,12 +39,14 @@ <gain.conf> <igo_nr.conf> <level_multiplier.conf> +<mfcc.conf> <micsel.conf> <multiband_drc.conf> <rtnr.conf> <sound_dose.conf> <src.conf> <src_lite.conf> +<stft_process.conf> <tdfb.conf> <template_comp.conf> @@ -439,6 +441,12 @@ IncludeByKey.BENCH_CONFIG { <include/bench/level_multiplier_s32.conf> } + # MFCC benchmark configs are intentionally not enabled in the SDW + # benchmark topology because the SDW ALH DAI/coprocessor side is + # constrained to $JACK_RATE while MFCC bench configs operate in a + # different rate domain. Re-enable only after adding a matching rate + # override or explicit sample-rate conversion. + # # Micsel component # @@ -523,6 +531,56 @@ IncludeByKey.BENCH_CONFIG { <include/bench/src_lite_s32.conf> } + # + # stft_process component, with five different blob configurations. + # + + "stft_process_192_48_16" { + <include/bench/stft_process_s16.conf> + } + "stft_process_192_48_24" { + <include/bench/stft_process_s24.conf> + } + "stft_process_192_48_32" { + <include/bench/stft_process_s32.conf> + } + "stft_process_512_128_16" { + <include/bench/stft_process_s16.conf> + } + "stft_process_512_128_24" { + <include/bench/stft_process_s24.conf> + } + "stft_process_512_128_32" { + <include/bench/stft_process_s32.conf> + } + "stft_process_768_120_16" { + <include/bench/stft_process_s16.conf> + } + "stft_process_768_120_24" { + <include/bench/stft_process_s24.conf> + } + "stft_process_768_120_32" { + <include/bench/stft_process_s32.conf> + } + "stft_process_1024_256_16" { + <include/bench/stft_process_s16.conf> + } + "stft_process_1024_256_24" { + <include/bench/stft_process_s24.conf> + } + "stft_process_1024_256_32" { + <include/bench/stft_process_s32.conf> + } + "stft_process_1536_240_16" { + <include/bench/stft_process_s16.conf> + } + "stft_process_1536_240_24" { + <include/bench/stft_process_s24.conf> + } + "stft_process_1536_240_32" { + <include/bench/stft_process_s32.conf> + } + # # tdfb component # diff --git a/tools/topology/topology2/cavs-es83x6.conf b/tools/topology/topology2/cavs-es83x6.conf index bcdaa725d379..6a5fc31054b7 100644 --- a/tools/topology/topology2/cavs-es83x6.conf +++ b/tools/topology/topology2/cavs-es83x6.conf @@ -38,6 +38,7 @@ <pipeline.conf> <dai.conf> <host.conf> +<bt-default.conf> <dmic-default.conf> <hdmi-default.conf> <hdmi-in-default.conf> @@ -61,6 +62,7 @@ Define { HEADSET_HW_CONFIG_NAME 'HEADSET HWCFG' HEADSET_PCM_NAME "Headset" HEADSET_PCM_ID 0 + INCLUDE_BT_OFFLOAD false } # override defaults with platform-specific config @@ -75,6 +77,10 @@ IncludeByKey.NUM_HDMIS { "[3-4]" "platform/intel/hdmi-generic.conf" } +IncludeByKey.INCLUDE_BT_OFFLOAD { + "true" "platform/intel/bt-generic.conf" +} + IncludeByKey.HEADSET_CODEC { "true" { # diff --git a/tools/topology/topology2/cavs-mixin-mixout-dax-hda.conf b/tools/topology/topology2/cavs-mixin-mixout-dax-hda.conf new file mode 100644 index 000000000000..bed5549d36bd --- /dev/null +++ b/tools/topology/topology2/cavs-mixin-mixout-dax-hda.conf @@ -0,0 +1,446 @@ +<include/components/tdfb.conf> + +Define { + ANALOG_PLAYBACK_PCM 'Analog Playback' + ANALOG_CAPTURE_PCM 'Analog Capture' + HDA_ANALOG_DAI_NAME 'Analog' + DEEP_BUFFER_PIPELINE_ID 15 + DEEP_BUFFER_PCM_ID 31 + DEEP_BUFFER_PIPELINE_SRC 'mixin.15.1' + DEEP_BUFFER_PIPELINE_SINK 'mixout.2.1' + DEEP_BUFFER_PCM_NAME 'Deepbuffer HDA Analog' + HDA_ANALOG_CAPTURE_RATE 48000 + HDA_ANALOG_PLAYBACK_RATE 48000 + EFX_FIR_PARAMS 'passthrough' + EFX_IIR_PARAMS 'passthrough' + EFX_DRC_PARAMS 'passthrough' + HDA_MIC_ENHANCED_CAPTURE 'false' + DOLBY_DAX_CORE_ID 0 +} + +# include deep buffer config if buffer size is in 1 - 1000 ms. +IncludeByKey.DEEPBUFFER_FW_DMA_MS{ + "[1-1000]" "platform/intel/deep-buffer.conf" +} + +Object.Dai.HDA [ + { + name $HDA_ANALOG_DAI_NAME + dai_index 0 + id 4 + default_hw_conf_id 4 + Object.Base.hw_config.1 { + name "HDA0" + } + direction duplex + } +] + +Object.Pipeline { + mixout-gain-dax-dai-copier-playback [ + { + index 2 + + Object.Widget.dai-copier.1 { + node_type $HDA_LINK_OUTPUT_CLASS + stream_name $HDA_ANALOG_DAI_NAME + dai_type "HDA" + copier_type "HDA" + num_input_audio_formats 1 + num_output_audio_formats 1 + Object.Base.input_audio_format [ + { + in_rate $HDA_ANALOG_PLAYBACK_RATE + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + Object.Base.output_audio_format [ + { + out_rate $HDA_ANALOG_PLAYBACK_RATE + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + } + + Object.Widget.gain.1 { + Object.Control.mixer.1 { + name 'Post Mixer $ANALOG_PLAYBACK_PCM Volume' + } + num_input_audio_formats 1 + num_output_audio_formats 1 + Object.Base.input_audio_format [ + { + in_rate $HDA_ANALOG_PLAYBACK_RATE + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + Object.Base.output_audio_format [ + { + out_rate $HDA_ANALOG_PLAYBACK_RATE + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + } + + Object.Widget.dolby-dax.1 { + core_id $DOLBY_DAX_CORE_ID + Object.Control { + mixer."1" { + name 'DAX $ANALOG_PLAYBACK_PCM Switch' + } + mixer."2" { + name 'DAX $ANALOG_PLAYBACK_PCM Switch CP' + } + mixer."3" { + name 'DAX $ANALOG_PLAYBACK_PCM Switch CTC' + } + mixer."4" { + name 'DAX $ANALOG_PLAYBACK_PCM Volume' + } + enum."1" { + name 'DAX $ANALOG_PLAYBACK_PCM Profile' + } + enum."2" { + name 'DAX $ANALOG_PLAYBACK_PCM Device' + } + bytes."1" { + name 'DAX $ANALOG_PLAYBACK_PCM Tuning' + max 8192 + } + } + } + } + ] + + host-copier-gain-mixin-playback [ + { + index 1 + + Object.Widget.host-copier.1 { + stream_name $ANALOG_PLAYBACK_PCM + pcm_id 0 + num_input_audio_formats 3 + num_output_audio_formats 1 + Object.Base.input_audio_format [ + { + in_rate $HDA_ANALOG_PLAYBACK_RATE + in_bit_depth 16 + in_valid_bit_depth 16 + } + { + in_rate $HDA_ANALOG_PLAYBACK_RATE + in_bit_depth 32 + in_valid_bit_depth 24 + } + { + in_rate $HDA_ANALOG_PLAYBACK_RATE + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + Object.Base.output_audio_format [ + { + out_rate $HDA_ANALOG_PLAYBACK_RATE + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + } + + Object.Widget.gain.1 { + Object.Control.mixer.1 { + name 'Pre Mixer $ANALOG_PLAYBACK_PCM Volume' + num_input_audio_formats 1 + num_output_audio_formats 1 + Object.Base.input_audio_format [ + { + in_rate $HDA_ANALOG_PLAYBACK_RATE + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + Object.Base.output_audio_format [ + { + out_rate $HDA_ANALOG_PLAYBACK_RATE + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + } + } + } + ] + + host-gateway-capture [ + { + index 3 + + Object.Widget.host-copier.1 { + stream_name $ANALOG_CAPTURE_PCM + pcm_id 0 + num_input_audio_formats 1 + num_output_audio_formats 3 + Object.Base.input_audio_format [ + { + in_rate $HDA_ANALOG_CAPTURE_RATE + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + Object.Base.output_audio_format [ + { + out_rate $HDA_ANALOG_CAPTURE_RATE + out_bit_depth 16 + out_valid_bit_depth 16 + } + { + out_rate $HDA_ANALOG_CAPTURE_RATE + out_bit_depth 32 + out_valid_bit_depth 24 + } + { + out_rate $HDA_ANALOG_CAPTURE_RATE + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + } + } + ] + + dai-copier-eqiir-module-copier-capture [ + { + index 4 + + Object.Widget.dai-copier."1" { + dai_type "HDA" + type "dai_out" + copier_type "HDA" + stream_name $HDA_ANALOG_DAI_NAME + node_type $HDA_LINK_INPUT_CLASS + num_output_pins 1 + num_input_audio_formats 1 + num_output_audio_formats 1 + Object.Base.input_audio_format [ + { + in_rate $HDA_ANALOG_CAPTURE_RATE + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + Object.Base.output_audio_format [ + { + out_rate $HDA_ANALOG_CAPTURE_RATE + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + } + + Object.Widget.eqiir.1 { + Object.Control.bytes."1" { + name '$ANALOG_CAPTURE_PCM IIR Eq' + <include/components/eqiir/highpass_40hz_0db_48khz.conf> + } + num_input_audio_formats 1 + num_output_audio_formats 1 + Object.Base.input_audio_format [ + { + in_rate $HDA_ANALOG_CAPTURE_RATE + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + Object.Base.output_audio_format [ + { + out_rate $HDA_ANALOG_CAPTURE_RATE + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + } + + Object.Widget.module-copier."2" { + stream_name $HDA_ANALOG_DAI_NAME + num_input_audio_formats 1 + num_output_audio_formats 1 + Object.Base.input_audio_format [ + { + in_rate $HDA_ANALOG_CAPTURE_RATE + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + Object.Base.output_audio_format [ + { + out_rate $HDA_ANALOG_CAPTURE_RATE + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + } + } + ] +} + +IncludeByKey.HDA_MIC_ENHANCED_CAPTURE { + "true" { + Object.Widget.tdfb.1 { + index 3 + + Object.Control { + bytes."1" { + name 'Analog Capture TDFB bytes' + max 16384 + IncludeByKey.EFX_HDA_MIC_TDFB_PARAMS { + "line2_pass" "include/components/tdfb/line2_pass.conf" + "line2_generic_pm10deg" "include/components/tdfb/line2_generic_pm10deg_48khz.conf" + "line2_50mm" "include/components/tdfb/line2_50mm_pm0_30_90deg_48khz.conf" + "line2_68mm" "include/components/tdfb/line2_68mm_pm0_30_90deg_48khz.conf" + "line2_74mm" "include/components/tdfb/line2_74mm_pm0_30_90deg_48khz.conf" + } + } + mixer."1" { + name 'Analog Capture TDFB beam switch' + } + enum."1" { + name 'Analog Capture TDFB angle set enum' + } + # Tracking is disabled from this topology, causes high MCPS + #mixer."2" { + # name 'Analog Capture TDFB track switch' + #} + #enum."2" { + # name 'Analog Capture TDFB angle estimate enum' + #} + } + num_input_audio_formats 1 + Object.Base.input_audio_format [ + { + in_rate $HDA_ANALOG_CAPTURE_RATE + in_channels 2 + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + num_output_audio_formats 1 + Object.Base.output_audio_format [ + { + out_channels 2 + out_rate $HDA_ANALOG_CAPTURE_RATE + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + } + + Object.Widget.drc.1 { + index 3 + + Object.Control { + bytes."1" { + name 'Analog Capture DRC bytes' + IncludeByKey.EFX_HDA_MIC_DRC_PARAMS { + "passthrough" "include/components/drc/passthrough.conf" + "amic_default" "include/components/drc/amic_default.conf" + } + } + mixer."1" { + name 'Analog Capture DRC switch' + } + } + num_input_audio_formats 1 + Object.Base.input_audio_format [ + { + in_rate $HDA_ANALOG_CAPTURE_RATE + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + num_output_audio_formats 1 + Object.Base.output_audio_format [ + { + out_rate $HDA_ANALOG_CAPTURE_RATE + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + } + } +} + +Object.PCM.pcm [ + { + id 0 + name 'HDA Analog' + Object.Base.fe_dai.1 { + name "HDA Analog" + } + Object.PCM.pcm_caps.1 { + direction "playback" + name $ANALOG_PLAYBACK_PCM + formats 'S32_LE,S24_LE,S16_LE' + rate_min $HDA_ANALOG_PLAYBACK_RATE + rate_max $HDA_ANALOG_PLAYBACK_RATE + } + Object.PCM.pcm_caps.2 { + direction "capture" + name $ANALOG_CAPTURE_PCM + formats 'S32_LE,S24_LE,S16_LE' + rate_min $HDA_ANALOG_CAPTURE_RATE + rate_max $HDA_ANALOG_CAPTURE_RATE + } + direction duplex + } +] + +# top-level pipeline connections +Object.Base.route [ + { + sink 'dai-copier.HDA.$HDA_ANALOG_DAI_NAME.playback' + source 'dolby-dax.2.1' + } + { + source 'mixin.1.1' + sink 'mixout.2.1' + } + { + source 'dai-copier.HDA.$HDA_ANALOG_DAI_NAME.capture' + sink 'eqiir.4.1' + } + { + source 'host-copier.0.playback' + sink 'gain.1.1' + } +] + +IncludeByKey.HDA_MIC_ENHANCED_CAPTURE { + "true" { + Object.Base.route [ + { + source 'module-copier.4.2' + sink 'tdfb.3.1' + } + { + source 'tdfb.3.1' + sink 'drc.3.1' + } + { + source 'drc.3.1' + sink 'host-copier.0.capture' + } + ] + } + "false" { + Object.Base.route [ + { + source 'module-copier.4.2' + sink 'host-copier.0.capture' + } + ] + } +} diff --git a/tools/topology/topology2/cavs-mixin-mixout-dax-ssp.conf b/tools/topology/topology2/cavs-mixin-mixout-dax-ssp.conf new file mode 100644 index 000000000000..23440bc5d163 --- /dev/null +++ b/tools/topology/topology2/cavs-mixin-mixout-dax-ssp.conf @@ -0,0 +1,371 @@ +# +# Pipeline definitions +# +# PCM0 ---> gain ----> Mixin ----> Mixout ----> gain ----> DAX ----> CTC ----> SSP0 +# PCM1 ---> gain ----> Mixin ----> Mixout ----> gain ----> DAX ----> CTC ----> SSP1 +# +# The gain before mixin is for the different routes(e.g., deepbuffer, headset). + +Define { + DOLBY_DAX_CORE_ID 0 + SSP_HEADSET_DAX "false" + USE_CTC_SPK "false" +} + + +# Pipeline ID:1 PCM ID: 0 +Object.Pipeline { + # playback pipelines + host-copier-gain-mixin-playback [ + { + index $HEADSET_HOST_PIPELINE_ID + + Object.Widget.host-copier.1 { + stream_name '$HEADSET_PLAYBACK_PCM_STREAM_NAME' + pcm_id $HEADSET_PCM_ID + } + Object.Widget.gain.1 { + Object.Control.mixer.1 { + name 'Pre Mixer $HEADSET_PCM_NAME Playback Volume' + } + } + } + { + index $SPEAKER_HOST_PIPELINE_ID + core_id $SPEAKER_PCM_CORE_ID + + Object.Widget.host-copier.1 { + stream_name '$SPEAKER_PLAYBACK_PCM_STREAM_NAME' + pcm_id $SPEAKER_PCM_ID + } + Object.Widget.gain.1 { + Object.Control.mixer.1 { + name 'Pre Mixer $SPEAKER_PCM_NAME Playback Volume' + } + } + Object.Widget.pipeline.1 { + core $SPEAKER_PCM_CORE_ID + } + } + ] + + IncludeByKey.SSP_HEADSET_DAX { + "true" { + mixout-gain-dax-dai-copier-playback [ + { + index $HEADSET_DAI_PIPELINE_ID + stream_name "$HEADSET_CODEC_NAME" + + Object.Widget.dai-copier.1 { + dai_index $HEADSET_SSP_DAI_INDEX + dai_type "SSP" + copier_type "SSP" + stream_name "$HEADSET_CODEC_NAME" + node_type $I2S_LINK_OUTPUT_CLASS + + IncludeByKey.SSP_HS_FMT_24 { + "true" { + Object.Base.output_audio_format [ + { + out_bit_depth 32 + out_valid_bit_depth 24 + out_sample_type $SAMPLE_TYPE_LSB_INTEGER + out_fmt_cfg "$[($out_channels | ($out_valid_bit_depth * 256))]" + } + ] + } + "false" { + Object.Base.output_audio_format [ + { + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + } + } + } + + Object.Widget.gain.1 { + Object.Control.mixer.1 { + name 'Post Mixer $HEADSET_PCM_NAME Playback Volume' + } + } + + Object.Widget.dolby-dax.1 { + core_id $DOLBY_DAX_CORE_ID + Object.Control { + mixer."1" { + name 'DAX Headphone Switch' + } + mixer."2" { + name 'DAX Headphone Switch CP' + } + mixer."3" { + name 'DAX Headphone Switch CTC' + } + mixer."4" { + name 'DAX Headphone Volume' + } + enum."1" { + name 'DAX Headphone Profile' + } + enum."2" { + name 'DAX Headphone Device' + } + bytes."1" { + name 'DAX Headphone Tuning' + max 8192 + } + } + } + } + ] + } + "false" { + mixout-gain-dai-copier-playback [ + { + index $HEADSET_DAI_PIPELINE_ID + + Object.Widget.dai-copier.1 { + dai_index $HEADSET_SSP_DAI_INDEX + dai_type "SSP" + copier_type "SSP" + stream_name "$HEADSET_CODEC_NAME" + node_type $I2S_LINK_OUTPUT_CLASS + + IncludeByKey.SSP_HS_FMT_24 { + "true" { + Object.Base.output_audio_format [ + { + out_bit_depth 32 + out_valid_bit_depth 24 + out_sample_type $SAMPLE_TYPE_LSB_INTEGER + out_fmt_cfg "$[($out_channels | ($out_valid_bit_depth * 256))]" + } + ] + } + "false" { + Object.Base.output_audio_format [ + { + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + } + } + } + + Object.Widget.gain.1 { + Object.Control.mixer.1 { + name 'Post Mixer $HEADSET_PCM_NAME Playback Volume' + } + } + } + ] + } + } + + IncludeByKey.USE_CTC_SPK { + "true" { + mixout-gain-dax-ctc-dai-copier-playback [ + { + index $SPEAKER_DAI_PIPELINE_ID + core_id $SPEAKER_PCM_CORE_ID + stream_name "$SPEAKER_CODEC_NAME" + + Object.Widget.dai-copier.1 { + dai_index $SPEAKER_SSP_DAI_INDEX + dai_type "SSP" + copier_type "SSP" + stream_name "$SPEAKER_CODEC_NAME" + node_type $I2S_LINK_OUTPUT_CLASS + + IncludeByKey.SSP_SPK_FMT_24 { + "true" { + Object.Base.output_audio_format [ + { + out_bit_depth 32 + out_valid_bit_depth 24 + out_sample_type $SAMPLE_TYPE_LSB_INTEGER + out_fmt_cfg "$[($out_channels | ($out_valid_bit_depth * 256))]" + } + ] + } + "false" { + Object.Base.output_audio_format [ + { + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + } + } + } + + Object.Widget.gain.1 { + Object.Control.mixer.1 { + name 'Post Mixer $SPEAKER_PCM_NAME Playback Volume' + } + } + + Object.Widget.dolby-dax.1 { + core_id $DOLBY_DAX_CORE_ID + Object.Control { + mixer."1" { + name 'DAX Speaker Switch' + } + mixer."2" { + name 'DAX Speaker Switch CP' + } + mixer."3" { + name 'DAX Speaker Switch CTC' + } + mixer."4" { + name 'DAX Speaker Volume' + } + enum."1" { + name 'DAX Speaker Profile' + } + enum."2" { + name 'DAX Speaker Device' + } + bytes."1" { + name 'DAX Speaker Tuning' + max 8192 + } + } + } + + Object.Widget.pipeline.1 { + core $SPEAKER_PCM_CORE_ID + } + } + ] + } + "false" { + mixout-gain-dax-dai-copier-playback [ + { + index $SPEAKER_DAI_PIPELINE_ID + core_id $SPEAKER_PCM_CORE_ID + stream_name "$SPEAKER_CODEC_NAME" + + Object.Widget.dai-copier.1 { + dai_index $SPEAKER_SSP_DAI_INDEX + dai_type "SSP" + copier_type "SSP" + stream_name "$SPEAKER_CODEC_NAME" + node_type $I2S_LINK_OUTPUT_CLASS + + IncludeByKey.SSP_SPK_FMT_24 { + "true" { + Object.Base.output_audio_format [ + { + out_bit_depth 32 + out_valid_bit_depth 24 + out_sample_type $SAMPLE_TYPE_LSB_INTEGER + out_fmt_cfg "$[($out_channels | ($out_valid_bit_depth * 256))]" + } + ] + } + "false" { + Object.Base.output_audio_format [ + { + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + } + } + } + + Object.Widget.gain.1 { + Object.Control.mixer.1 { + name 'Post Mixer $SPEAKER_PCM_NAME Playback Volume' + } + } + + Object.Widget.dolby-dax.1 { + core_id $DOLBY_DAX_CORE_ID + Object.Control { + mixer."1" { + name 'DAX Speaker Switch' + } + mixer."2" { + name 'DAX Speaker Switch CP' + } + mixer."3" { + name 'DAX Speaker Switch CTC' + } + mixer."4" { + name 'DAX Speaker Volume' + } + enum."1" { + name 'DAX Speaker Profile' + } + enum."2" { + name 'DAX Speaker Device' + } + bytes."1" { + name 'DAX Speaker Tuning' + max 8192 + } + } + } + + Object.Widget.pipeline.1 { + core $SPEAKER_PCM_CORE_ID + } + } + ] + } + } +} + +Object.Base.route [ + { + source "mixin.$HEADSET_HOST_PIPELINE_ID.1" + sink "mixout.$HEADSET_DAI_PIPELINE_ID.1" + } + { + source "mixin.$SPEAKER_HOST_PIPELINE_ID.1" + sink "mixout.$SPEAKER_DAI_PIPELINE_ID.1" + } +] + +IncludeByKey.SSP_HEADSET_DAX { + "true" { + Object.Base.route [ + { + source "dolby-dax.$HEADSET_DAI_PIPELINE_ID.1" + sink "dai-copier.SSP.$HEADSET_CODEC_NAME.playback" + } + ] + } + "false" { + Object.Base.route [ + { + source "gain.$HEADSET_DAI_PIPELINE_ID.1" + sink "dai-copier.SSP.$HEADSET_CODEC_NAME.playback" + } + ] + } +} + +IncludeByKey.USE_CTC_SPK { + "true" { + Object.Base.route [ + { + source "ctc.$SPEAKER_DAI_PIPELINE_ID.1" + sink "dai-copier.SSP.$SPEAKER_CODEC_NAME.playback" + } + ] + } + "false" { + Object.Base.route [ + { + source "dolby-dax.$SPEAKER_DAI_PIPELINE_ID.1" + sink "dai-copier.SSP.$SPEAKER_CODEC_NAME.playback" + } + ] + } +} diff --git a/tools/topology/topology2/cavs-mixin-mixout-hda.conf b/tools/topology/topology2/cavs-mixin-mixout-hda.conf index 83742186fb8b..e83499754107 100644 --- a/tools/topology/topology2/cavs-mixin-mixout-hda.conf +++ b/tools/topology/topology2/cavs-mixin-mixout-hda.conf @@ -9,6 +9,8 @@ Define { DEEP_BUFFER_PIPELINE_SRC 'mixin.15.1' DEEP_BUFFER_PIPELINE_SINK 'mixout.2.1' DEEP_BUFFER_PCM_NAME 'Deepbuffer HDA Analog' + COMPR_PIPELINE_SINK 'mixout.2.1' + COMPR_PCM_NAME 'Compress HDA Analog' HDA_ANALOG_CAPTURE_RATE 48000 HDA_ANALOG_PLAYBACK_RATE 48000 EFX_FIR_PARAMS 'passthrough' @@ -22,6 +24,10 @@ IncludeByKey.DEEPBUFFER_FW_DMA_MS{ "[1-1000]" "platform/intel/deep-buffer.conf" } +IncludeByKey.COMPRESSED { + "true" "platform/intel/compr.conf" +} + Object.Dai.HDA [ { name $HDA_ANALOG_DAI_NAME diff --git a/tools/topology/topology2/cavs-nocodec.conf b/tools/topology/topology2/cavs-nocodec.conf index 1795fdb12668..fea906f98741 100644 --- a/tools/topology/topology2/cavs-nocodec.conf +++ b/tools/topology/topology2/cavs-nocodec.conf @@ -108,6 +108,7 @@ IncludeByKey.PLATFORM { "mtl" "platform/intel/mtl.conf" "lnl" "platform/intel/lnl.conf" "ptl" "platform/intel/ptl.conf" + "nvl" "platform/intel/ptl.conf" # Note: Assume same configuration as PTL } # include DMIC config if needed. diff --git a/tools/topology/topology2/cavs-rt5682.conf b/tools/topology/topology2/cavs-rt5682.conf index d8e028775aeb..99ccfbfbef56 100644 --- a/tools/topology/topology2/cavs-rt5682.conf +++ b/tools/topology/topology2/cavs-rt5682.conf @@ -20,6 +20,8 @@ <mixout-gain-dai-copier-playback.conf> <mixout-gain-eqiir-dts-dai-copier-playback.conf> <mixout-gain-ctc-dai-copier-playback.conf> +<mixout-gain-dax-dai-copier-playback.conf> +<mixout-gain-dax-ctc-dai-copier-playback.conf> <deepbuffer-playback.conf> <dai-copier-be.conf> <dai-copier-gain-eqiir-module-copier-capture.conf> @@ -122,12 +124,16 @@ Define { DMIC_PIPELINE_PRIORITY 1 ECHO_REF_PIPELINE_PRIORITY 0 PLAYBACK_PIPELINE_SRC "volume" + USE_CTC_SPK "false" + DOLBY_DAX_CORE_ID 0 + SSP_HEADSET_DAX "false" SSP_SPK_FMT_24 false SSP_HS_FMT_24 false } # override defaults with platform-specific config IncludeByKey.PLATFORM { + "adl" "platform/intel/tgl.conf" "mtl" "platform/intel/mtl.conf" "lnl" "platform/intel/lnl.conf" "ptl" "platform/intel/ptl.conf" @@ -165,6 +171,7 @@ IncludeByKey.PLAYBACK_PIPELINE_SRC { "volume" "cavs-mixin-mixout-ssp.conf" "dts" "cavs-mixin-mixout-eqiir-dts-ssp.conf" "ctc" "cavs-mixin-mixout-ctc-ssp.conf" + "dax" "cavs-mixin-mixout-dax-ssp.conf" } diff --git a/tools/topology/topology2/cavs-sdw.conf b/tools/topology/topology2/cavs-sdw.conf index d16ab0ca4492..6932543c06e5 100644 --- a/tools/topology/topology2/cavs-sdw.conf +++ b/tools/topology/topology2/cavs-sdw.conf @@ -13,14 +13,22 @@ <virtual.conf> <host-copier-gain-mixin-playback.conf> <mixout-gain-alh-dai-copier-playback.conf> +<mixout-gain-dax-alh-dai-copier-playback.conf> +<mixout-gain-micsel-eqiir-eqfir-alh-dai-copier-playback.conf> <mixout-gain-eqiir-eqfir-drc-alh-dai-copier-playback.conf> +<mixout-gain-eqiir-eqfir-drc-micsel-eqiir-eqfir-alh-dai-copier-playback.conf> +<mixout-gain-eqiir-dts-alh-dai-copier-playback.conf> <dai-copier-gain-eqiir-module-copier-capture.conf> <gain-capture.conf> <gain-copier-capture.conf> <deepbuffer-playback.conf> +<deepbuffer-capture.conf> +<compr-playback.conf> <host-gateway-playback.conf> <host-gateway-capture.conf> <host-gateway-tdfb-drc-capture.conf> +<host-gateway-src-mfcc-capture.conf> +<src.conf> <io-gateway.conf> <io-gateway-capture.conf> <highpass-capture-be.conf> @@ -49,6 +57,8 @@ <ssp.conf> <bt-default.conf> <deep-buffer-default.conf> +<compr-default.conf> +<siggen-host-copier-capture.conf> Define { PLATFORM "none" @@ -66,6 +76,7 @@ Define { DMIC0_ID 4 DMIC1_ID 5 DMIC0_PCM_CAPS 'Gain Capture 13' + DEEP_BUFFER_PIPELINE_ID 15 DEEP_BUFFER_PCM_ID 31 DEEP_BUFFER_PIPELINE_SRC 'mixin.15.1' @@ -76,11 +87,18 @@ Define { DEEP_BUFFER_PIPELINE_SRC_2 'mixin.16.1' DEEP_BUFFER_PIPELINE_SINK_2 'mixout.21.1' DEEP_BUFFER_PCM_NAME_2 'Deepbuffer Speaker' + + COMPR_PIPELINE_SINK 'mixout.1.1' + COMPR_PCM_NAME 'Compress Jack Out' + COMPR_2_PIPELINE_SINK 'mixout.21.1' + COMPR_2_PCM_NAME 'Compress Speaker' + SDW_JACK_OUT_STREAM 'SDW0-Playback' SDW_JACK_IN_STREAM 'SDW0-Capture' SDW_JACK_OUT_BE_ID 0 SDW_JACK_IN_BE_ID 1 NUM_SDW_AMP_LINKS 0 + NUM_SDW_AMP_CTC_LINKS 0 SDW_DMIC 0 SDW_JACK true PASSTHROUGH false @@ -98,6 +116,15 @@ Define { EFX_MIC_TDFB_PARAMS "line2_generic_pm10deg" EFX_MIC_DRC_PARAMS "dmic_default" DMIC0_DAI_EQIIR "highpass_40hz_20db" + SDW_AMP_NUM_CHANNELS 2 + SDW_AMP_XOVER false + SDW_AMP_XOVER_SELECTOR_PARAMS default + SDW_AMP_XOVER_EQIIR_PARAMS default + SDW_AMP_XOVER_EQFIR_PARAMS default + DOLBY_DAX_CORE_ID 0 + SDW_AMP_PIPELINE_SRC "generic" + SDW_JACK_PIPELINE_SRC "generic" + SDW_ECHO_REF_DAI "false" } # override defaults with platform-specific config @@ -105,6 +132,7 @@ IncludeByKey.PLATFORM { "mtl" "platform/intel/mtl.conf" "lnl" "platform/intel/lnl.conf" "ptl" "platform/intel/ptl.conf" + "nvl" "platform/intel/ptl.conf" # Note: Assume same configuration as PTL } IncludeByKey.ADD_BT { @@ -125,8 +153,60 @@ IncludeByKey.NUM_HDMIS { "[3-4]" "platform/intel/hdmi-generic.conf" } -IncludeByKey.NUM_SDW_AMP_LINKS { -"[1-3]" "platform/intel/sdw-amp-generic.conf" +IncludeByKey.SDW_ECHO_REF_DAI { + "true" { + Object.Widget.alh-copier [ + { + stream_name "Loopback_Virtual" + direction "capture" + type "dai_out" + IncludeByKey.SDW_SPK_ECHO_REF { + "true" {index $SDW_SPK_ECHO_REF_PIPELINE_ID} + "false" {index $SDW_JACK_ECHO_REF_PIPELINE_ID} + } + dai_index 25 + node_type $ALH_LINK_INPUT_CLASS + num_input_audio_formats 1 + num_output_audio_formats 1 + Object.Base.input_audio_format [ + { + in_rate $JACK_RATE + in_bit_depth 32 + in_valid_bit_depth $SDW_LINK_VALID_BITS + } + ] + Object.Base.output_audio_format [ + { + out_rate $JACK_RATE + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + } + ] + } +} + +IncludeByKey.SDW_AMP_XOVER { + "false" { + IncludeByKey.NUM_SDW_AMP_LINKS { + "[1-3]" { + IncludeByKey.SDW_AMP_PIPELINE_SRC { + "dax" "platform/intel/sdw-amp-dax.conf" + "dts" "platform/intel/sdw-amp-dts.conf" + "generic" "platform/intel/sdw-amp-generic.conf" + } + } + } + IncludeByKey.NUM_SDW_AMP_CTC_LINKS { + "[1-3]" "platform/intel/sdw-amp-generic-ctc.conf" + } + } + "true" { + IncludeByKey.NUM_SDW_AMP_LINKS { + "[1-3]" "platform/intel/sdw-amp-xover.conf" + } + } } IncludeByKey.SDW_DMIC { @@ -134,6 +214,46 @@ IncludeByKey.SDW_DMIC { } IncludeByKey.SDW_JACK { -"true" "platform/intel/sdw-jack-generic.conf" +"true" { + IncludeByKey.SDW_JACK_PIPELINE_SRC { + "dax" "platform/intel/sdw-jack-dax.conf" + "dts" "platform/intel/sdw-jack-dts.conf" + "generic" "platform/intel/sdw-jack-generic.conf" + } +} +"false" { + Define { + # disable compressed audio for Jack + COMPRESSED_1 false + } + } +} + +IncludeByKey.NUM_SDW_AMP_LINKS { + "[1-3]" { + Define { + # Enable compressed audio for Speaker + COMPRESSED_2 true + } + } } +IncludeByKey.COMPRESSED { + "true" "platform/intel/compr.conf" +} + +IncludeByKey.SDW_JACK_ECHO_REF { + "true" "platform/intel/sdw-jack-echo-ref.conf" +} + +IncludeByKey.SDW_SPK_ECHO_REF { + "true" "platform/intel/sdw-amp-echo-ref.conf" +} + +IncludeByKey.SDW_JACK_AUDIO_FEATURE_CAPTURE { + "true" "platform/intel/sdw-jack-audio-feature.conf" +} + +IncludeByKey.SDW_DMIC_AUDIO_FEATURE_CAPTURE { + "true" "platform/intel/sdw-dmic-audio-feature.conf" +} diff --git a/tools/topology/topology2/development/tplg-targets-bench.cmake b/tools/topology/topology2/development/tplg-targets-bench.cmake index 4508fbb47d8f..5c0f82dc7dfc 100644 --- a/tools/topology/topology2/development/tplg-targets-bench.cmake +++ b/tools/topology/topology2/development/tplg-targets-bench.cmake @@ -10,6 +10,7 @@ set(sampleformats "16" "24" "32") set(components "asrc" "dcblock" + "dolby-dax" "drc" "drc_multiband" "eqiir" @@ -17,11 +18,18 @@ set(components "gain" "igo_nr" "level_multiplier" + "mfcc" + "mfccmel" "micsel" "rtnr" "sound_dose" "src" "src_lite" + "stft_process_192_48_" + "stft_process_512_128_" + "stft_process_768_120_" + "stft_process_1024_256_" + "stft_process_1536_240_" "tdfb" "template_comp" ) @@ -29,18 +37,26 @@ set(components set(component_parameters "BENCH_ASRC_PARAMS=default" "BENCH_DCBLOCK_PARAMS=default" - "BENCH_DRC_PARAMS=enabled" + "BENCH_DOLBY-DAX_PARAMS=default" + "BENCH_DRC_PARAMS=default_speaker_mic" "BENCH_DRC_MULTIBAND_PARAMS=default" "BENCH_EQIIR_PARAMS=loudness" "BENCH_EQFIR_PARAMS=loudness" "BENCH_GAIN_PARAMS=default" "BENCH_IGO_NR_PARAMS=default" "BENCH_LEVEL_MULTIPLIER_PARAMS=default" + "BENCH_MFCC_PARAMS=default" + "BENCH_MFCC_PARAMS=mel80" "BENCH_MICSEL_PARAMS=passthrough" "BENCH_RTNR_PARAMS=default" "BENCH_SOUND_DOSE_PARAMS=default" "BENCH_SRC_PARAMS=default" "BENCH_SRC_LITE_PARAMS=default" + "BENCH_STFT_PROCESS_PARAMS=hann_192_48" + "BENCH_STFT_PROCESS_PARAMS=hann_512_128" + "BENCH_STFT_PROCESS_PARAMS=hann_768_120" + "BENCH_STFT_PROCESS_PARAMS=hann_1024_256" + "BENCH_STFT_PROCESS_PARAMS=hann_1536_240" "BENCH_TDFB_PARAMS=default" "BENCH_TEMPLATE_COMP_PARAMS=default" ) @@ -72,7 +88,7 @@ foreach(sf ${sampleformats}) list(APPEND TPLGS "${item}") set(item "cavs-benchmark-sdw\;sof-${plat}-sdw-benchmark-${comp}${sf}-simplejack\;PLATFORM=${plat},SDW_JACK_OUT_STREAM=Playback-SimpleJack,SDW_JACK_IN_STREAM=Capture-SimpleJack,BENCH_MODULE_FORMAT=s${sf},BENCH_CONFIG=${comp}${sf},${bench_param}") list(APPEND TPLGS "${item}") -# #message(STATUS "Item=" ${item}) + #message(STATUS "Item=" ${item}) endforeach() endforeach() diff --git a/tools/topology/topology2/development/tplg-targets.cmake b/tools/topology/topology2/development/tplg-targets.cmake index 5c20ef592806..a906852d04f0 100644 --- a/tools/topology/topology2/development/tplg-targets.cmake +++ b/tools/topology/topology2/development/tplg-targets.cmake @@ -60,9 +60,9 @@ NHLT_BIN=nhlt-sof-lnl-nocodec-fpga-4ch.bin,PASSTHROUGH=true,DMIC_IO_CLK=19200000 # HDA topology with passthrough analog codec pipelines using CHAIN_DMA "sof-hda-generic\;sof-hda-passthrough-chain-dma\;HDA_CONFIG=passthrough,CODEC_HDA_CHAIN_DMA=true" -# SSP topology for PTL +# SSP topology for PTL, includes Data Processing SRC "cavs-nocodec\;sof-ptl-nocodec\;PLATFORM=ptl,NUM_DMICS=4,PDM1_MIC_A_ENABLE=1,PDM1_MIC_B_ENABLE=1,\ -PREPROCESS_PLUGINS=nhlt,NHLT_BIN=nhlt-sof-ptl-nocodec.bin" +PREPROCESS_PLUGINS=nhlt,NHLT_BIN=nhlt-sof-ptl-nocodec.bin,SRC_DOMAIN=DP" # SSP topology for PTL with 96 kHz DMIC "cavs-nocodec\;sof-ptl-nocodec-dmic-4ch-96k\;PLATFORM=ptl,NUM_DMICS=4,PDM1_MIC_A_ENABLE=1,PDM1_MIC_B_ENABLE=1,\ @@ -97,6 +97,10 @@ NHLT_BIN=nhlt-sof-ptl-nocodec-fpga-dmic-4ch-96k.bin,PASSTHROUGH=true,DMIC_IO_CLK PDM1_MIC_B_ENABLE=1,DMIC0_RATE=96000,SSP0_RATE=96000,PREPROCESS_PLUGINS=nhlt,\ NHLT_BIN=nhlt-sof-ptl-nocodec-fpga-dmic-4ch-96k-ssp0-96k.bin,PASSTHROUGH=true,DMIC_IO_CLK=19200000" +# SSP topology for NVL (limit to DSP cores 0/1) +"cavs-nocodec\;sof-nvl-nocodec\;PLATFORM=nvl,NUM_DMICS=4,PDM1_MIC_A_ENABLE=1,PDM1_MIC_B_ENABLE=1,SSP2_PCM_CORE_ID=0,DP_SRC_CORE_ID=0,\ +PREPROCESS_PLUGINS=nhlt,NHLT_BIN=nhlt-sof-nvl-nocodec.bin" + # Topology for PTL with max98357a and rt5682 "cavs-rt5682\;sof-ptl-max98357a-rt5682-ssp2-ssp0\;PLATFORM=ptl,NUM_DMICS=4,PDM1_MIC_A_ENABLE=1,\ PDM1_MIC_B_ENABLE=1,DMIC0_PCM_ID=99,PREPROCESS_PLUGINS=nhlt,\ @@ -306,9 +310,9 @@ PREPROCESS_PLUGINS=nhlt,NHLT_BIN=nhlt-sof-mtl-nocodec.bin,SRC_DOMAIN=DP" # SSP test topology for Data Processing SRC on LNL "cavs-nocodec\;sof-lnl-nocodec-dp-test\;PLATFORM=lnl,NUM_DMICS=4,PDM1_MIC_A_ENABLE=1,PDM1_MIC_B_ENABLE=1,\ PREPROCESS_PLUGINS=nhlt,NHLT_BIN=nhlt-sof-lnl-nocodec.bin,SRC_DOMAIN=DP" -# SSP test topology for Data Processing SRC on PTL +# SSP test topology for PTL with no DP "cavs-nocodec\;sof-ptl-nocodec-dp-test\;PLATFORM=ptl,NUM_DMICS=4,PDM1_MIC_A_ENABLE=1,PDM1_MIC_B_ENABLE=1,\ -PREPROCESS_PLUGINS=nhlt,NHLT_BIN=nhlt-sof-ptl-nocodec.bin,SRC_DOMAIN=DP" +PREPROCESS_PLUGINS=nhlt,NHLT_BIN=nhlt-sof-ptl-nocodec.bin" # SSP test topology for Data Processing on core 1 SRC for MTL "cavs-nocodec\;sof-mtl-nocodec-dp-core-test\;PLATFORM=mtl,NUM_DMICS=4,PDM1_MIC_A_ENABLE=1,PDM1_MIC_B_ENABLE=1,\ @@ -369,7 +373,117 @@ SPK_ID=6,SPEAKER_SSP_DAI_INDEX=0,HEADSET_CODEC_NAME=SSP2-Codec,SPEAKER_CODEC_NAM BT_NAME=SSP1-BT,BT_INDEX=1,BT_ID=7,BT_PCM_NAME=Bluetooth,INCLUDE_ECHO_REF=true,\ DEEP_BUF_SPK=true,PLAYBACK_PIPELINE_SRC=ctc,SSP_SPK_FMT_24=true,SSP_HS_FMT_24=true" +# mtl-rt1019-rt5650 with DAX +"cavs-rt5682\;sof-mtl-rt1019-rt5682-dax\;PLATFORM=mtl,NUM_DMICS=4,PDM1_MIC_A_ENABLE=1,\ +PDM1_MIC_B_ENABLE=1,DMIC0_PCM_ID=99,SPK_ID=6,PREPROCESS_PLUGINS=nhlt,\ +NHLT_BIN=nhlt-sof-mtl-rt1019-rt5682.bin,DEEPBUFFER_FW_DMA_MS=10,HEADSET_SSP_DAI_INDEX=2,\ +SPEAKER_SSP_DAI_INDEX=0,HEADSET_CODEC_NAME=SSP2-Codec,SPEAKER_CODEC_NAME=SSP0-Codec,\ +INCLUDE_ECHO_REF=true,PLAYBACK_PIPELINE_SRC=dax,DOLBY_DAX_CORE_ID=2,SSP_HEADSET_DAX=true,\ +BT_NAME=SSP1-BT,BT_INDEX=1,BT_ID=7,BT_PCM_NAME=Bluetooth,DEEP_BUF_SPK=true" + # CS35L56 SSP2 "cavs-cs35l56\;sof-tgl-cs35l56-ssp2\;PLATFORM=tgl,PREPROCESS_PLUGINS=nhlt,\ NHLT_BIN=nhlt-sof-tgl-cs35l56-ssp2.bin" + +# Topology for Google Brya +"cavs-rt5682\;sof-adl-max98357a-rt5682\;PLATFORM=adl,NUM_DMICS=4,PDM1_MIC_A_ENABLE=1,\ +PDM1_MIC_B_ENABLE=1,DMIC0_PCM_ID=99,PREPROCESS_PLUGINS=nhlt,NUM_HDMIS=4,\ +NHLT_BIN=nhlt-sof-adl-max98357a-rt5682.bin,SPK_ID=7,DEEPBUFFER_FW_DMA_MS=10,INCLUDE_ECHO_REF=true,\ +INCLUDE_BT_OFFLOAD=false,DEEP_BUF_SPK=true,SPEAKER_CODEC_NAME=SSP2-Codec,SPEAKER_SSP_DAI_INDEX=2" + +# RT721 eval board with PCH-DMIC, sof_sdw_quirk_table with SOC_SDW_PCH_DMIC +# Enable FLOAT_LE and U8 PCM formats +"cavs-sdw\;sof-ptl-rt721-4ch-allfmt\;PLATFORM=ptl,SDW_DMIC=1,NUM_SDW_AMP_LINKS=1,NUM_DMICS=4,\ +PDM1_MIC_A_ENABLE=1,PDM1_MIC_B_ENABLE=1,DMIC0_ID=5,DMIC1_ID=6,HDMI1_ID=7,HDMI2_ID=8,HDMI3_ID=9,\ +SDW_AMP_FEEDBACK=false,SDW_SPK_STREAM=Playback-SmartAmp,SDW_DMIC_STREAM=Capture-SmartMic,\ +SDW_JACK_OUT_STREAM=Playback-SimpleJack,SDW_JACK_IN_STREAM=Capture-SimpleJack,\ +PREPROCESS_PLUGINS=nhlt,NHLT_BIN=nhlt-sof-ptl-rt721-4ch-allfmt.bin,DMIC0_ENHANCED_CAPTURE=true,\ +EFX_DMIC0_TDFB_PARAMS=line4_pass,EFX_DMIC0_DRC_PARAMS=dmic_default,\ +DEEPBUFFER_FW_DMA_MS=10,DEEP_BUF_SPK=true,PCM_FORMAT_ALL=true" + +# Deep buffer DMIC0 +"sof-hda-generic\;sof-hda-generic-2ch-dmicdeepbuf\;HDA_CONFIG=mix,\ +NUM_DMICS=2,DMIC0_DEEP_BUFFER=true,DEEPBUFFER_FW_DMA_MS=10,DMIC0_ENHANCED_CAPTURE=true,\ +EFX_DMIC0_TDFB_PARAMS=line2_generic_pm10deg,EFX_DMIC0_DRC_PARAMS=dmic_default" + +"sof-hda-generic\;sof-hda-generic-4ch-dmicdeepbuf\;HDA_CONFIG=mix,\ +NUM_DMICS=4,PDM1_MIC_A_ENABLE=1,PDM1_MIC_B_ENABLE=1,DMIC0_DEEP_BUFFER=true,DEEPBUFFER_FW_DMA_MS=10,\ +DMIC0_ENHANCED_CAPTURE=true,EFX_DMIC0_TDFB_PARAMS=line4_pass,EFX_DMIC0_DRC_PARAMS=dmic_default" + +# Deep buffer DMIC0 with tplg NHLT for PTL, 2ch and 4ch +"sof-hda-generic\;sof-hda-generic-ace3-2ch-dmicdeepbuf\;PLATFORM=ptl,HDA_CONFIG=mix,NUM_DMICS=2,\ +PREPROCESS_PLUGINS=nhlt,NHLT_BIN=nhlt-sof-hda-generic-ace3-2ch-dmicdeepbuf.bin,\ +DMIC0_ENHANCED_CAPTURE=true,EFX_DMIC0_TDFB_PARAMS=line2_generic_pm10deg,\ +EFX_DMIC0_DRC_PARAMS=dmic_default,DMIC0_DEEP_BUFFER=true,DEEPBUFFER_FW_DMA_MS=10" + +"sof-hda-generic\;sof-hda-generic-ace3-4ch-dmicdeepbuf\;PLATFORM=ptl,HDA_CONFIG=mix,\ +NUM_DMICS=4,PDM1_MIC_A_ENABLE=1,PDM1_MIC_B_ENABLE=1,PREPROCESS_PLUGINS=nhlt,\ +NHLT_BIN=nhlt-sof-hda-generic-ace3-4ch-dmicdeepbuf.bin,\ +DMIC0_ENHANCED_CAPTURE=true,EFX_DMIC0_TDFB_PARAMS=line4_pass,\ +EFX_DMIC0_DRC_PARAMS=dmic_default,DMIC0_DEEP_BUFFER=true,DEEPBUFFER_FW_DMA_MS=10" + +# Deep buffer DMIC0 with RT721 eval board with PCH-DMIC +"cavs-sdw\;sof-ptl-rt721-4ch-dmicdeepbuf\;PLATFORM=ptl,SDW_DMIC=1,NUM_SDW_AMP_LINKS=1,NUM_DMICS=4,\ +PDM1_MIC_A_ENABLE=1,PDM1_MIC_B_ENABLE=1,DMIC0_ID=5,DMIC1_ID=6,HDMI1_ID=7,HDMI2_ID=8,HDMI3_ID=9,\ +SDW_AMP_FEEDBACK=false,SDW_SPK_STREAM=Playback-SmartAmp,SDW_DMIC_STREAM=Capture-SmartMic,\ +SDW_JACK_OUT_STREAM=Playback-SimpleJack,SDW_JACK_IN_STREAM=Capture-SimpleJack,\ +PREPROCESS_PLUGINS=nhlt,NHLT_BIN=nhlt-sof-ptl-rt721-4ch-dmicdeepbuf.bin,\ +DMIC0_ENHANCED_CAPTURE=true,EFX_DMIC0_TDFB_PARAMS=line4_pass,EFX_DMIC0_DRC_PARAMS=dmic_default,\ +DEEPBUFFER_FW_DMA_MS=10,DEEP_BUF_SPK=true,DMIC0_DEEP_BUFFER=true" + +# RT721 eval board with PCH-DMIC, sof_sdw_quirk_table with SOC_SDW_PCH_DMIC +# Enable echo reference capture from jack and speaker output +"cavs-sdw\;sof-ptl-rt721-4ch-echoref\;PLATFORM=ptl,SDW_DMIC=1,NUM_SDW_AMP_LINKS=1,NUM_DMICS=4,\ +PDM1_MIC_A_ENABLE=1,PDM1_MIC_B_ENABLE=1,DMIC0_ID=5,DMIC1_ID=6,HDMI1_ID=7,HDMI2_ID=8,HDMI3_ID=9,\ +SDW_AMP_FEEDBACK=false,SDW_SPK_STREAM=Playback-SmartAmp,SDW_DMIC_STREAM=Capture-SmartMic,\ +SDW_JACK_OUT_STREAM=Playback-SimpleJack,SDW_JACK_IN_STREAM=Capture-SimpleJack,\ +PREPROCESS_PLUGINS=nhlt,NHLT_BIN=nhlt-sof-ptl-rt721-4ch-echoref.bin,DMIC0_ENHANCED_CAPTURE=true,\ +EFX_DMIC0_TDFB_PARAMS=line4_pass,EFX_DMIC0_DRC_PARAMS=dmic_default,\ +DEEPBUFFER_FW_DMA_MS=10,DEEP_BUF_SPK=true,SDW_JACK_ECHO_REF=true,SDW_SPK_ECHO_REF=true,SDW_ECHO_REF_DAI=true" + +# RT721 eval board with PCH-DMIC, sof_sdw_quirk_table with SOC_SDW_PCH_DMIC +# Enable echo reference capture from jack and speaker output +# Enable SSP2 BT +"cavs-sdw\;sof-ptl-rt721-4ch-ssp2bt-echoref\;PLATFORM=ptl,SDW_DMIC=1,NUM_SDW_AMP_LINKS=1,NUM_DMICS=4,\ +PDM1_MIC_A_ENABLE=1,PDM1_MIC_B_ENABLE=1,DMIC0_ID=5,DMIC1_ID=6,HDMI1_ID=7,HDMI2_ID=8,HDMI3_ID=9,\ +SDW_AMP_FEEDBACK=false,SDW_SPK_STREAM=Playback-SmartAmp,SDW_DMIC_STREAM=Capture-SmartMic,\ +SDW_JACK_OUT_STREAM=Playback-SimpleJack,SDW_JACK_IN_STREAM=Capture-SimpleJack,\ +PREPROCESS_PLUGINS=nhlt,NHLT_BIN=nhlt-sof-ptl-rt721-4ch-echoref.bin,DMIC0_ENHANCED_CAPTURE=true,\ +EFX_DMIC0_TDFB_PARAMS=line4_pass,EFX_DMIC0_DRC_PARAMS=dmic_default,\ +BT_NAME=SSP2-BT,BT_PCM_ID=20,BT_ID=10,BT_PCM_NAME=Bluetooth,ADD_BT=true,\ +DEEPBUFFER_FW_DMA_MS=10,DEEP_BUF_SPK=true,SDW_JACK_ECHO_REF=true,SDW_SPK_ECHO_REF=true,SDW_ECHO_REF_DAI=true" + +# RT722 with PCH-DMIC and echo reference capture from jack and speaker output +"cavs-sdw\;sof-ptl-rt722-4ch-echoref\;PLATFORM=ptl,SDW_DMIC=1,NUM_SDW_AMP_LINKS=1,NUM_DMICS=4,\ +PDM1_MIC_A_ENABLE=1,PDM1_MIC_B_ENABLE=1,DMIC0_ID=5,DMIC1_ID=6,HDMI1_ID=7,HDMI2_ID=8,HDMI3_ID=9,\ +SDW_AMP_FEEDBACK=false,SDW_SPK_STREAM=Playback-SmartAmp,SDW_DMIC_STREAM=Capture-SmartMic,\ +SDW_JACK_OUT_STREAM=Playback-SimpleJack,SDW_JACK_IN_STREAM=Capture-SimpleJack,\ +PREPROCESS_PLUGINS=nhlt,NHLT_BIN=nhlt-sof-ptl-rt722-4ch-echoref.bin,DMIC0_ENHANCED_CAPTURE=true,\ +EFX_DMIC0_TDFB_PARAMS=line4_pass,EFX_DMIC0_DRC_PARAMS=dmic_default,\ +SDW_JACK_ECHO_REF=true,SDW_SPK_ECHO_REF=true,SDW_ECHO_REF_DAI=true" + +# Soundwire topologies with compressed support +"cavs-sdw\;sof-arl-cs42l43-l0-compr\;PLATFORM=mtl,NUM_SDW_AMP_LINKS=1,SDW_DMIC=1,\ +SDW_AMP_FEEDBACK=false,SDW_SPK_STREAM=Playback-SmartAmp,SDW_DMIC_STREAM=Capture-SmartMic,\ +SDW_JACK_OUT_STREAM=Playback-SimpleJack,SDW_JACK_IN_STREAM=Capture-SimpleJack,COMPRESSED=true" + +"cavs-sdw\;sof-mtl-rt713-l0-rt1316-l12-compr\;PLATFORM=mtl,NUM_SDW_AMP_LINKS=2,\ +HDMI1_ID=4,HDMI2_ID=5,HDMI3_ID=6,COMPRESSED=true" + +"cavs-sdw\;sof-ptl-rt721-compr\;PLATFORM=ptl,SDW_DMIC=1,NUM_SDW_AMP_LINKS=1,\ +SDW_AMP_FEEDBACK=false,SDW_SPK_STREAM=Playback-SmartAmp,SDW_DMIC_STREAM=Capture-SmartMic,\ +SDW_JACK_OUT_STREAM=Playback-SimpleJack,SDW_JACK_IN_STREAM=Capture-SimpleJack,COMPRESSED=true" + +"cavs-sdw\;sof-ptl-rt722-compr\;PLATFORM=ptl,SDW_DMIC=1,NUM_SDW_AMP_LINKS=1,\ +SDW_AMP_FEEDBACK=false,SDW_SPK_STREAM=Playback-SmartAmp,SDW_DMIC_STREAM=Capture-SmartMic,\ +SDW_JACK_OUT_STREAM=Playback-SimpleJack,SDW_JACK_IN_STREAM=Capture-SimpleJack,COMPRESSED=true" + +# Soundwire topologies with MFCC audio features capture +"cavs-sdw\;sof-mtl-rt713-l0-rt1316-l12-mfcc\;PLATFORM=mtl,NUM_SDW_AMP_LINKS=2,\ +HDMI1_ID=4,HDMI2_ID=5,HDMI3_ID=6,SDW_JACK_AUDIO_FEATURE_CAPTURE=true" + +"cavs-sdw\;sof-arl-cs42l43-l0-cs35l56-l23-mfcc\;PLATFORM=mtl,NUM_SDW_AMP_LINKS=2,SDW_DMIC=1,\ +SDW_AMP_FEEDBACK=false,SDW_SPK_STREAM=Playback-SmartAmp,SDW_DMIC_STREAM=Capture-SmartMic,\ +SDW_JACK_OUT_STREAM=Playback-SimpleJack,SDW_JACK_IN_STREAM=Capture-SimpleJack,\ +SDW_JACK_AUDIO_FEATURE_CAPTURE=true,SDW_DMIC_AUDIO_FEATURE_CAPTURE=true" ) diff --git a/tools/topology/topology2/include/bench/dolby-dax_controls_capture.conf b/tools/topology/topology2/include/bench/dolby-dax_controls_capture.conf new file mode 100644 index 000000000000..dde79828ea06 --- /dev/null +++ b/tools/topology/topology2/include/bench/dolby-dax_controls_capture.conf @@ -0,0 +1,28 @@ + # Created initially with script "./bench_comp_generate.sh dolby-dax" + # may need edits to modify controls + Object.Control { + # Un-comment the supported controls in DOLBY-DAX + mixer."1" { + name 'DAX $ANALOG_CAPTURE_PCM Playback Switch' + } + mixer."2" { + name 'DAX $ANALOG_CAPTURE_PCM Playback Switch CP' + } + mixer."3" { + name 'DAX $ANALOG_CAPTURE_PCM Playback Switch CTC' + } + mixer."4" { + name 'DAX $ANALOG_CAPTURE_PCM Playback Volume' + } + enum."1" { + name 'DAX $ANALOG_CAPTURE_PCM Playback Profile' + } + enum."2" { + name 'DAX $ANALOG_CAPTURE_PCM Playback Device' + } + bytes."1" { + name 'DAX $ANALOG_CAPTURE_PCM Playback Tuning' + max 8192 + <include/components/dax/default.conf> + } + } diff --git a/tools/topology/topology2/include/bench/dolby-dax_controls_playback.conf b/tools/topology/topology2/include/bench/dolby-dax_controls_playback.conf new file mode 100644 index 000000000000..9c56043a2ed0 --- /dev/null +++ b/tools/topology/topology2/include/bench/dolby-dax_controls_playback.conf @@ -0,0 +1,28 @@ + # Created initially with script "./bench_comp_generate.sh dolby-dax" + # may need edits to modify controls + Object.Control { + # Un-comment the supported controls in DOLBY-DAX + mixer."1" { + name 'DAX $ANALOG_PLAYBACK_PCM Playback Switch' + } + mixer."2" { + name 'DAX $ANALOG_PLAYBACK_PCM Playback Switch CP' + } + mixer."3" { + name 'DAX $ANALOG_PLAYBACK_PCM Playback Switch CTC' + } + mixer."4" { + name 'DAX $ANALOG_PLAYBACK_PCM Playback Volume' + } + enum."1" { + name 'DAX $ANALOG_PLAYBACK_PCM Playback Profile' + } + enum."2" { + name 'DAX $ANALOG_PLAYBACK_PCM Playback Device' + } + bytes."1" { + name 'DAX $ANALOG_PLAYBACK_PCM Playback Tuning' + max 8192 + <include/components/dax/default.conf> + } + } diff --git a/tools/topology/topology2/include/bench/dolby-dax_route.conf b/tools/topology/topology2/include/bench/dolby-dax_route.conf new file mode 100644 index 000000000000..1f5e0dc0dc80 --- /dev/null +++ b/tools/topology/topology2/include/bench/dolby-dax_route.conf @@ -0,0 +1,19 @@ + # Created with script "./bench_comp_generate.sh dolby-dax" + Object.Base.route [ + { + sink '$BENCH_PLAYBACK_DAI_COPIER' + source 'dolby-dax.$BENCH_PLAYBACK_HOST_PIPELINE.1' + } + { + sink 'dolby-dax.$BENCH_PLAYBACK_HOST_PIPELINE.1' + source 'host-copier.0.playback' + } + { + source '$BENCH_CAPTURE_DAI_COPIER' + sink 'dolby-dax.$BENCH_CAPTURE_HOST_PIPELINE.2' + } + { + source 'dolby-dax.$BENCH_CAPTURE_HOST_PIPELINE.2' + sink 'host-copier.0.capture' + } + ] diff --git a/tools/topology/topology2/include/bench/dolby-dax_s16.conf b/tools/topology/topology2/include/bench/dolby-dax_s16.conf new file mode 100644 index 000000000000..a4f622cebad3 --- /dev/null +++ b/tools/topology/topology2/include/bench/dolby-dax_s16.conf @@ -0,0 +1,13 @@ + # Created with script "./bench_comp_generate.sh dolby-dax" + Object.Widget.dolby-dax.1 { + index $BENCH_PLAYBACK_HOST_PIPELINE + <include/bench/one_input_output_format_s16.conf> + <include/bench/dolby-dax_controls_playback.conf> + } + Object.Widget.dolby-dax.2 { + index $BENCH_CAPTURE_HOST_PIPELINE + <include/bench/one_input_output_format_s16.conf> + <include/bench/dolby-dax_controls_capture.conf> + } + <include/bench/host_gateway_pipelines_s16.conf> + <include/bench/dolby-dax_route.conf> diff --git a/tools/topology/topology2/include/bench/dolby-dax_s24.conf b/tools/topology/topology2/include/bench/dolby-dax_s24.conf new file mode 100644 index 000000000000..b74c5d32af75 --- /dev/null +++ b/tools/topology/topology2/include/bench/dolby-dax_s24.conf @@ -0,0 +1,13 @@ + # Created with script "./bench_comp_generate.sh dolby-dax" + Object.Widget.dolby-dax.1 { + index $BENCH_PLAYBACK_HOST_PIPELINE + <include/bench/one_input_output_format_s24.conf> + <include/bench/dolby-dax_controls_playback.conf> + } + Object.Widget.dolby-dax.2 { + index $BENCH_CAPTURE_HOST_PIPELINE + <include/bench/one_input_output_format_s24.conf> + <include/bench/dolby-dax_controls_capture.conf> + } + <include/bench/host_gateway_pipelines_s24.conf> + <include/bench/dolby-dax_route.conf> diff --git a/tools/topology/topology2/include/bench/dolby-dax_s32.conf b/tools/topology/topology2/include/bench/dolby-dax_s32.conf new file mode 100644 index 000000000000..b239e4d5dab6 --- /dev/null +++ b/tools/topology/topology2/include/bench/dolby-dax_s32.conf @@ -0,0 +1,13 @@ + # Created with script "./bench_comp_generate.sh dolby-dax" + Object.Widget.dolby-dax.1 { + index $BENCH_PLAYBACK_HOST_PIPELINE + <include/bench/one_input_output_format_s32.conf> + <include/bench/dolby-dax_controls_playback.conf> + } + Object.Widget.dolby-dax.2 { + index $BENCH_CAPTURE_HOST_PIPELINE + <include/bench/one_input_output_format_s32.conf> + <include/bench/dolby-dax_controls_capture.conf> + } + <include/bench/host_gateway_pipelines_s32.conf> + <include/bench/dolby-dax_route.conf> diff --git a/tools/topology/topology2/include/bench/drc_controls_capture.conf b/tools/topology/topology2/include/bench/drc_controls_capture.conf index c586f940b578..42d8044a6398 100644 --- a/tools/topology/topology2/include/bench/drc_controls_capture.conf +++ b/tools/topology/topology2/include/bench/drc_controls_capture.conf @@ -8,6 +8,7 @@ "default" "include/components/drc/default.conf" "enabled" "include/components/drc/enabled.conf" "passthrough" "include/components/drc/passthrough.conf" + "default_speaker_mic" "include/components/drc/dmic_default.conf" } } mixer."1" { diff --git a/tools/topology/topology2/include/bench/drc_controls_playback.conf b/tools/topology/topology2/include/bench/drc_controls_playback.conf index 36217ab08965..2cab24d5325d 100644 --- a/tools/topology/topology2/include/bench/drc_controls_playback.conf +++ b/tools/topology/topology2/include/bench/drc_controls_playback.conf @@ -8,6 +8,7 @@ "default" "include/components/drc/default.conf" "enabled" "include/components/drc/enabled.conf" "passthrough" "include/components/drc/passthrough.conf" + "default_speaker_mic" "include/components/drc/speaker_default.conf" } } mixer."1" { diff --git a/tools/topology/topology2/include/bench/host_gateway_pipelines_s16_16k.conf b/tools/topology/topology2/include/bench/host_gateway_pipelines_s16_16k.conf new file mode 100644 index 000000000000..483a087a885f --- /dev/null +++ b/tools/topology/topology2/include/bench/host_gateway_pipelines_s16_16k.conf @@ -0,0 +1,74 @@ + Object.Pipeline { + host-gateway-playback [ + { + index $BENCH_PLAYBACK_HOST_PIPELINE + Object.Widget.host-copier.1 { + stream_name $ANALOG_PLAYBACK_PCM + pcm_id 0 + num_input_audio_formats 3 + num_output_audio_formats 1 + Object.Base.input_audio_format [ + { + in_bit_depth 16 + in_valid_bit_depth 16 + in_rate 16000 + } + { + in_bit_depth 32 + in_valid_bit_depth 24 + in_rate 16000 + } + { + in_bit_depth 32 + in_valid_bit_depth 32 + in_rate 16000 + } + ] + Object.Base.output_audio_format [ + { + out_bit_depth 16 + out_valid_bit_depth 16 + out_rate 16000 + } + ] + } + } + ] + + host-gateway-capture [ + { + index $BENCH_CAPTURE_HOST_PIPELINE + Object.Widget.host-copier.1 { + stream_name $ANALOG_CAPTURE_PCM + pcm_id 0 + num_input_audio_formats 1 + num_output_audio_formats 3 + Object.Base.input_audio_format [ + { + in_bit_depth 16 + in_valid_bit_depth 16 + in_rate 16000 + } + ] + Object.Base.output_audio_format [ + { + out_bit_depth 16 + out_valid_bit_depth 16 + out_rate 16000 + } + { + out_bit_depth 32 + out_valid_bit_depth 24 + out_rate 16000 + } + { + out_bit_depth 32 + out_valid_bit_depth 32 + out_rate 16000 + } + ] + } + } + ] + } + diff --git a/tools/topology/topology2/include/bench/host_gateway_pipelines_s24_16k.conf b/tools/topology/topology2/include/bench/host_gateway_pipelines_s24_16k.conf new file mode 100644 index 000000000000..4fc5b1eaf715 --- /dev/null +++ b/tools/topology/topology2/include/bench/host_gateway_pipelines_s24_16k.conf @@ -0,0 +1,74 @@ + Object.Pipeline { + host-gateway-playback [ + { + index $BENCH_PLAYBACK_HOST_PIPELINE + Object.Widget.host-copier.1 { + stream_name $ANALOG_PLAYBACK_PCM + pcm_id 0 + num_input_audio_formats 3 + num_output_audio_formats 1 + Object.Base.input_audio_format [ + { + in_bit_depth 16 + in_valid_bit_depth 16 + in_rate 16000 + } + { + in_bit_depth 32 + in_valid_bit_depth 24 + in_rate 16000 + } + { + in_bit_depth 32 + in_valid_bit_depth 32 + in_rate 16000 + } + ] + Object.Base.output_audio_format [ + { + out_bit_depth 32 + out_valid_bit_depth 24 + out_rate 16000 + } + ] + } + } + ] + + host-gateway-capture [ + { + index $BENCH_CAPTURE_HOST_PIPELINE + Object.Widget.host-copier.1 { + stream_name $ANALOG_CAPTURE_PCM + pcm_id 0 + num_input_audio_formats 1 + num_output_audio_formats 3 + Object.Base.input_audio_format [ + { + in_bit_depth 32 + in_valid_bit_depth 24 + in_rate 16000 + } + ] + Object.Base.output_audio_format [ + { + out_bit_depth 16 + out_valid_bit_depth 16 + out_rate 16000 + } + { + out_bit_depth 32 + out_valid_bit_depth 24 + out_rate 16000 + } + { + out_bit_depth 32 + out_valid_bit_depth 32 + out_rate 16000 + } + ] + } + } + ] + } + diff --git a/tools/topology/topology2/include/bench/host_gateway_pipelines_s32_16k.conf b/tools/topology/topology2/include/bench/host_gateway_pipelines_s32_16k.conf new file mode 100644 index 000000000000..229e686feeba --- /dev/null +++ b/tools/topology/topology2/include/bench/host_gateway_pipelines_s32_16k.conf @@ -0,0 +1,74 @@ + Object.Pipeline { + host-gateway-playback [ + { + index $BENCH_PLAYBACK_HOST_PIPELINE + Object.Widget.host-copier.1 { + stream_name $ANALOG_PLAYBACK_PCM + pcm_id 0 + num_input_audio_formats 3 + num_output_audio_formats 1 + Object.Base.input_audio_format [ + { + in_bit_depth 16 + in_valid_bit_depth 16 + in_rate 16000 + } + { + in_bit_depth 32 + in_valid_bit_depth 24 + in_rate 16000 + } + { + in_bit_depth 32 + in_valid_bit_depth 32 + in_rate 16000 + } + ] + Object.Base.output_audio_format [ + { + out_bit_depth 32 + out_valid_bit_depth 32 + out_rate 16000 + } + ] + } + } + ] + + host-gateway-capture [ + { + index $BENCH_CAPTURE_HOST_PIPELINE + Object.Widget.host-copier.1 { + stream_name $ANALOG_CAPTURE_PCM + pcm_id 0 + num_input_audio_formats 1 + num_output_audio_formats 3 + Object.Base.input_audio_format [ + { + in_bit_depth 32 + in_valid_bit_depth 32 + in_rate 16000 + } + ] + Object.Base.output_audio_format [ + { + out_bit_depth 16 + out_valid_bit_depth 16 + out_rate 16000 + } + { + out_bit_depth 32 + out_valid_bit_depth 24 + out_rate 16000 + } + { + out_bit_depth 32 + out_valid_bit_depth 32 + out_rate 16000 + } + ] + } + } + ] + } + diff --git a/tools/topology/topology2/include/bench/host_io_gateway_pipelines_s16.conf b/tools/topology/topology2/include/bench/host_io_gateway_pipelines_s16.conf deleted file mode 100644 index b6ecea830367..000000000000 --- a/tools/topology/topology2/include/bench/host_io_gateway_pipelines_s16.conf +++ /dev/null @@ -1,126 +0,0 @@ - Object.Pipeline { - host-gateway-playback [ - { - index 1 - - Object.Widget.host-copier.1 { - stream_name $ANALOG_PLAYBACK_PCM - pcm_id 0 - num_input_audio_formats 3 - num_output_audio_formats 1 - Object.Base.input_audio_format [ - { - in_bit_depth 16 - in_valid_bit_depth 16 - } - { - in_bit_depth 32 - in_valid_bit_depth 24 - } - { - in_bit_depth 32 - in_valid_bit_depth 32 - } - ] - Object.Base.output_audio_format [ - { - out_bit_depth 16 - out_valid_bit_depth 16 - } - ] - } - } - ] - - io-gateway [ - { - index 2 - direction playback - - Object.Widget.dai-copier.1 { - node_type $HDA_LINK_OUTPUT_CLASS - stream_name $HDA_ANALOG_DAI_NAME - dai_type "HDA" - copier_type "HDA" - num_input_pins 1 - num_input_audio_formats 1 - num_output_audio_formats 1 - Object.Base.input_audio_format [ - { - in_bit_depth 16 - in_valid_bit_depth 16 - } - ] - Object.Base.output_audio_format [ - { - out_bit_depth 32 - out_valid_bit_depth 32 - } - ] - } - } - ] - - host-gateway-capture [ - { - index 3 - Object.Widget.host-copier.1 { - stream_name $ANALOG_CAPTURE_PCM - pcm_id 0 - num_input_audio_formats 1 - num_output_audio_formats 3 - Object.Base.input_audio_format [ - { - in_bit_depth 16 - in_valid_bit_depth 16 - } - ] - Object.Base.output_audio_format [ - { - out_bit_depth 16 - out_valid_bit_depth 16 - } - { - out_bit_depth 32 - out_valid_bit_depth 24 - } - { - out_bit_depth 32 - out_valid_bit_depth 32 - } - ] - } - } - ] - - io-gateway-capture [ - { - index 4 - direction capture - - Object.Widget.dai-copier."1" { - dai_type "HDA" - type "dai_out" - copier_type "HDA" - stream_name $HDA_ANALOG_DAI_NAME - node_type $HDA_LINK_INPUT_CLASS - num_output_pins 1 - num_input_audio_formats 1 - num_output_audio_formats 1 - Object.Base.input_audio_format [ - { - in_bit_depth 32 - in_valid_bit_depth 32 - } - ] - Object.Base.output_audio_format [ - { - out_bit_depth 16 - out_valid_bit_depth 16 - } - ] - } - } - ] - } - diff --git a/tools/topology/topology2/include/bench/host_io_gateway_pipelines_s24.conf b/tools/topology/topology2/include/bench/host_io_gateway_pipelines_s24.conf deleted file mode 100644 index 4cac2acd2b8b..000000000000 --- a/tools/topology/topology2/include/bench/host_io_gateway_pipelines_s24.conf +++ /dev/null @@ -1,126 +0,0 @@ - Object.Pipeline { - host-gateway-playback [ - { - index 1 - - Object.Widget.host-copier.1 { - stream_name $ANALOG_PLAYBACK_PCM - pcm_id 0 - num_input_audio_formats 3 - num_output_audio_formats 1 - Object.Base.input_audio_format [ - { - in_bit_depth 16 - in_valid_bit_depth 16 - } - { - in_bit_depth 32 - in_valid_bit_depth 24 - } - { - in_bit_depth 32 - in_valid_bit_depth 32 - } - ] - Object.Base.output_audio_format [ - { - out_bit_depth 32 - out_valid_bit_depth 24 - } - ] - } - } - ] - - io-gateway [ - { - index 2 - direction playback - - Object.Widget.dai-copier.1 { - node_type $HDA_LINK_OUTPUT_CLASS - stream_name $HDA_ANALOG_DAI_NAME - dai_type "HDA" - copier_type "HDA" - num_input_pins 1 - num_input_audio_formats 1 - num_output_audio_formats 1 - Object.Base.input_audio_format [ - { - in_bit_depth 32 - in_valid_bit_depth 24 - } - ] - Object.Base.output_audio_format [ - { - out_bit_depth 32 - out_valid_bit_depth 32 - } - ] - } - } - ] - - host-gateway-capture [ - { - index 3 - Object.Widget.host-copier.1 { - stream_name $ANALOG_CAPTURE_PCM - pcm_id 0 - num_input_audio_formats 1 - num_output_audio_formats 3 - Object.Base.input_audio_format [ - { - in_bit_depth 32 - in_valid_bit_depth 24 - } - ] - Object.Base.output_audio_format [ - { - out_bit_depth 16 - out_valid_bit_depth 16 - } - { - out_bit_depth 32 - out_valid_bit_depth 24 - } - { - out_bit_depth 32 - out_valid_bit_depth 32 - } - ] - } - } - ] - - io-gateway-capture [ - { - index 4 - direction capture - - Object.Widget.dai-copier."1" { - dai_type "HDA" - type "dai_out" - copier_type "HDA" - stream_name $HDA_ANALOG_DAI_NAME - node_type $HDA_LINK_INPUT_CLASS - num_output_pins 1 - num_input_audio_formats 1 - num_output_audio_formats 1 - Object.Base.input_audio_format [ - { - in_bit_depth 32 - in_valid_bit_depth 32 - } - ] - Object.Base.output_audio_format [ - { - out_bit_depth 32 - out_valid_bit_depth 24 - } - ] - } - } - ] - } - diff --git a/tools/topology/topology2/include/bench/host_io_gateway_pipelines_s32.conf b/tools/topology/topology2/include/bench/host_io_gateway_pipelines_s32.conf deleted file mode 100644 index c598efcd3e06..000000000000 --- a/tools/topology/topology2/include/bench/host_io_gateway_pipelines_s32.conf +++ /dev/null @@ -1,126 +0,0 @@ - Object.Pipeline { - host-gateway-playback [ - { - index 1 - - Object.Widget.host-copier.1 { - stream_name $ANALOG_PLAYBACK_PCM - pcm_id 0 - num_input_audio_formats 3 - num_output_audio_formats 1 - Object.Base.input_audio_format [ - { - in_bit_depth 16 - in_valid_bit_depth 16 - } - { - in_bit_depth 32 - in_valid_bit_depth 24 - } - { - in_bit_depth 32 - in_valid_bit_depth 32 - } - ] - Object.Base.output_audio_format [ - { - out_bit_depth 32 - out_valid_bit_depth 32 - } - ] - } - } - ] - - io-gateway [ - { - index 2 - direction playback - - Object.Widget.dai-copier.1 { - node_type $HDA_LINK_OUTPUT_CLASS - stream_name $HDA_ANALOG_DAI_NAME - dai_type "HDA" - copier_type "HDA" - num_input_pins 1 - num_input_audio_formats 1 - num_output_audio_formats 1 - Object.Base.input_audio_format [ - { - in_bit_depth 32 - in_valid_bit_depth 32 - } - ] - Object.Base.output_audio_format [ - { - out_bit_depth 32 - out_valid_bit_depth 32 - } - ] - } - } - ] - - host-gateway-capture [ - { - index 3 - Object.Widget.host-copier.1 { - stream_name $ANALOG_CAPTURE_PCM - pcm_id 0 - num_input_audio_formats 1 - num_output_audio_formats 3 - Object.Base.input_audio_format [ - { - in_bit_depth 32 - in_valid_bit_depth 32 - } - ] - Object.Base.output_audio_format [ - { - out_bit_depth 16 - out_valid_bit_depth 16 - } - { - out_bit_depth 32 - out_valid_bit_depth 24 - } - { - out_bit_depth 32 - out_valid_bit_depth 32 - } - ] - } - } - ] - - io-gateway-capture [ - { - index 4 - direction capture - - Object.Widget.dai-copier."1" { - dai_type "HDA" - type "dai_out" - copier_type "HDA" - stream_name $HDA_ANALOG_DAI_NAME - node_type $HDA_LINK_INPUT_CLASS - num_output_pins 1 - num_input_audio_formats 1 - num_output_audio_formats 1 - Object.Base.input_audio_format [ - { - in_bit_depth 32 - in_valid_bit_depth 32 - } - ] - Object.Base.output_audio_format [ - { - out_bit_depth 32 - out_valid_bit_depth 32 - } - ] - } - } - ] - } - diff --git a/tools/topology/topology2/include/bench/mfcc_controls_capture.conf b/tools/topology/topology2/include/bench/mfcc_controls_capture.conf new file mode 100644 index 000000000000..d45baec1ee8f --- /dev/null +++ b/tools/topology/topology2/include/bench/mfcc_controls_capture.conf @@ -0,0 +1,18 @@ + # Created initially with script "./bench_comp_generate.sh mfcc" + # may need edits to modify controls + Object.Control { + # Un-comment the supported controls in MFCC + bytes."1" { + name '$ANALOG_CAPTURE_PCM MFCC bytes' + IncludeByKey.BENCH_MFCC_PARAMS { + "default" "include/components/mfcc/default.conf" + "mel80" "include/components/mfcc/mel80.conf" + } + } + #mixer."1" { + # name '$ANALOG_CAPTURE_PCM MFCC switch or volume' + #} + #enum."1" { + # name '$ANALOG_CAPTURE_PCM MFCC enum' + #} + } diff --git a/tools/topology/topology2/include/bench/mfcc_controls_playback.conf b/tools/topology/topology2/include/bench/mfcc_controls_playback.conf new file mode 100644 index 000000000000..cc2ada04b8d7 --- /dev/null +++ b/tools/topology/topology2/include/bench/mfcc_controls_playback.conf @@ -0,0 +1,18 @@ + # Created initially with script "./bench_comp_generate.sh mfcc" + # may need edits to modify controls + Object.Control { + # Un-comment the supported controls in MFCC + bytes."1" { + name '$ANALOG_PLAYBACK_PCM MFCC bytes' + IncludeByKey.BENCH_MFCC_PARAMS { + "default" "include/components/mfcc/default.conf" + "mel80" "include/components/mfcc/mel80.conf" + } + } + #mixer."1" { + # name '$ANALOG_PLAYBACK_PCM MFCC switch or volume' + #} + #enum."1" { + # name '$ANALOG_PLAYBACK_PCM MFCC enum' + #} + } diff --git a/tools/topology/topology2/include/bench/mfcc_route.conf b/tools/topology/topology2/include/bench/mfcc_route.conf new file mode 100644 index 000000000000..d4ce9af5f85d --- /dev/null +++ b/tools/topology/topology2/include/bench/mfcc_route.conf @@ -0,0 +1,19 @@ + # Created with script "./bench_comp_generate.sh mfcc" + Object.Base.route [ + { + sink '$BENCH_PLAYBACK_DAI_COPIER' + source 'mfcc.$BENCH_PLAYBACK_HOST_PIPELINE.1' + } + { + sink 'mfcc.$BENCH_PLAYBACK_HOST_PIPELINE.1' + source 'host-copier.0.playback' + } + { + source '$BENCH_CAPTURE_DAI_COPIER' + sink 'mfcc.$BENCH_CAPTURE_HOST_PIPELINE.2' + } + { + source 'mfcc.$BENCH_CAPTURE_HOST_PIPELINE.2' + sink 'host-copier.0.capture' + } + ] diff --git a/tools/topology/topology2/include/bench/mfcc_s16.conf b/tools/topology/topology2/include/bench/mfcc_s16.conf new file mode 100644 index 000000000000..ec89bffb90a1 --- /dev/null +++ b/tools/topology/topology2/include/bench/mfcc_s16.conf @@ -0,0 +1,13 @@ + # Created with script "./bench_comp_generate.sh mfcc" + Object.Widget.mfcc.1 { + index $BENCH_PLAYBACK_HOST_PIPELINE + <include/bench/one_input_output_format_s16_16k.conf> + <include/bench/mfcc_controls_playback.conf> + } + Object.Widget.mfcc.2 { + index $BENCH_CAPTURE_HOST_PIPELINE + <include/bench/one_input_output_format_s16_16k.conf> + <include/bench/mfcc_controls_capture.conf> + } + <include/bench/host_gateway_pipelines_s16_16k.conf> + <include/bench/mfcc_route.conf> diff --git a/tools/topology/topology2/include/bench/mfcc_s24.conf b/tools/topology/topology2/include/bench/mfcc_s24.conf new file mode 100644 index 000000000000..73571fabe5f2 --- /dev/null +++ b/tools/topology/topology2/include/bench/mfcc_s24.conf @@ -0,0 +1,13 @@ + # Created with script "./bench_comp_generate.sh mfcc" + Object.Widget.mfcc.1 { + index $BENCH_PLAYBACK_HOST_PIPELINE + <include/bench/one_input_output_format_s24_16k.conf> + <include/bench/mfcc_controls_playback.conf> + } + Object.Widget.mfcc.2 { + index $BENCH_CAPTURE_HOST_PIPELINE + <include/bench/one_input_output_format_s24_16k.conf> + <include/bench/mfcc_controls_capture.conf> + } + <include/bench/host_gateway_pipelines_s24_16k.conf> + <include/bench/mfcc_route.conf> diff --git a/tools/topology/topology2/include/bench/mfcc_s32.conf b/tools/topology/topology2/include/bench/mfcc_s32.conf new file mode 100644 index 000000000000..75c01eaf4a43 --- /dev/null +++ b/tools/topology/topology2/include/bench/mfcc_s32.conf @@ -0,0 +1,13 @@ + # Created with script "./bench_comp_generate.sh mfcc" + Object.Widget.mfcc.1 { + index $BENCH_PLAYBACK_HOST_PIPELINE + <include/bench/one_input_output_format_s32_16k.conf> + <include/bench/mfcc_controls_playback.conf> + } + Object.Widget.mfcc.2 { + index $BENCH_CAPTURE_HOST_PIPELINE + <include/bench/one_input_output_format_s32_16k.conf> + <include/bench/mfcc_controls_capture.conf> + } + <include/bench/host_gateway_pipelines_s32_16k.conf> + <include/bench/mfcc_route.conf> diff --git a/tools/topology/topology2/include/bench/mfccmel_s16.conf b/tools/topology/topology2/include/bench/mfccmel_s16.conf new file mode 100644 index 000000000000..ec89bffb90a1 --- /dev/null +++ b/tools/topology/topology2/include/bench/mfccmel_s16.conf @@ -0,0 +1,13 @@ + # Created with script "./bench_comp_generate.sh mfcc" + Object.Widget.mfcc.1 { + index $BENCH_PLAYBACK_HOST_PIPELINE + <include/bench/one_input_output_format_s16_16k.conf> + <include/bench/mfcc_controls_playback.conf> + } + Object.Widget.mfcc.2 { + index $BENCH_CAPTURE_HOST_PIPELINE + <include/bench/one_input_output_format_s16_16k.conf> + <include/bench/mfcc_controls_capture.conf> + } + <include/bench/host_gateway_pipelines_s16_16k.conf> + <include/bench/mfcc_route.conf> diff --git a/tools/topology/topology2/include/bench/mfccmel_s24.conf b/tools/topology/topology2/include/bench/mfccmel_s24.conf new file mode 100644 index 000000000000..73571fabe5f2 --- /dev/null +++ b/tools/topology/topology2/include/bench/mfccmel_s24.conf @@ -0,0 +1,13 @@ + # Created with script "./bench_comp_generate.sh mfcc" + Object.Widget.mfcc.1 { + index $BENCH_PLAYBACK_HOST_PIPELINE + <include/bench/one_input_output_format_s24_16k.conf> + <include/bench/mfcc_controls_playback.conf> + } + Object.Widget.mfcc.2 { + index $BENCH_CAPTURE_HOST_PIPELINE + <include/bench/one_input_output_format_s24_16k.conf> + <include/bench/mfcc_controls_capture.conf> + } + <include/bench/host_gateway_pipelines_s24_16k.conf> + <include/bench/mfcc_route.conf> diff --git a/tools/topology/topology2/include/bench/mfccmel_s32.conf b/tools/topology/topology2/include/bench/mfccmel_s32.conf new file mode 100644 index 000000000000..75c01eaf4a43 --- /dev/null +++ b/tools/topology/topology2/include/bench/mfccmel_s32.conf @@ -0,0 +1,13 @@ + # Created with script "./bench_comp_generate.sh mfcc" + Object.Widget.mfcc.1 { + index $BENCH_PLAYBACK_HOST_PIPELINE + <include/bench/one_input_output_format_s32_16k.conf> + <include/bench/mfcc_controls_playback.conf> + } + Object.Widget.mfcc.2 { + index $BENCH_CAPTURE_HOST_PIPELINE + <include/bench/one_input_output_format_s32_16k.conf> + <include/bench/mfcc_controls_capture.conf> + } + <include/bench/host_gateway_pipelines_s32_16k.conf> + <include/bench/mfcc_route.conf> diff --git a/tools/topology/topology2/include/bench/one_input_output_format_s16_16k.conf b/tools/topology/topology2/include/bench/one_input_output_format_s16_16k.conf new file mode 100644 index 000000000000..eca8c45d8bc9 --- /dev/null +++ b/tools/topology/topology2/include/bench/one_input_output_format_s16_16k.conf @@ -0,0 +1,19 @@ + num_input_audio_formats 1 + num_output_audio_formats 1 + + # 16-bit 16kHz + Object.Base.input_audio_format [ + { + in_bit_depth 16 + in_valid_bit_depth 16 + in_rate 16000 + } + ] + Object.Base.output_audio_format [ + { + out_bit_depth 16 + out_valid_bit_depth 16 + out_rate 16000 + } + ] + diff --git a/tools/topology/topology2/include/bench/one_input_output_format_s24_16k.conf b/tools/topology/topology2/include/bench/one_input_output_format_s24_16k.conf new file mode 100644 index 000000000000..742151e6948d --- /dev/null +++ b/tools/topology/topology2/include/bench/one_input_output_format_s24_16k.conf @@ -0,0 +1,19 @@ + num_input_audio_formats 1 + num_output_audio_formats 1 + + # 24-bit 16kHz + Object.Base.input_audio_format [ + { + in_bit_depth 32 + in_valid_bit_depth 24 + in_rate 16000 + } + ] + Object.Base.output_audio_format [ + { + out_bit_depth 32 + out_valid_bit_depth 24 + out_rate 16000 + } + ] + diff --git a/tools/topology/topology2/include/bench/one_input_output_format_s32_16k.conf b/tools/topology/topology2/include/bench/one_input_output_format_s32_16k.conf new file mode 100644 index 000000000000..ae4608b5ab76 --- /dev/null +++ b/tools/topology/topology2/include/bench/one_input_output_format_s32_16k.conf @@ -0,0 +1,19 @@ + num_input_audio_formats 1 + num_output_audio_formats 1 + + # 32-bit 16kHz + Object.Base.input_audio_format [ + { + in_bit_depth 32 + in_valid_bit_depth 32 + in_rate 16000 + } + ] + Object.Base.output_audio_format [ + { + out_bit_depth 32 + out_valid_bit_depth 32 + out_rate 16000 + } + ] + diff --git a/tools/topology/topology2/include/bench/stft_process_controls_capture.conf b/tools/topology/topology2/include/bench/stft_process_controls_capture.conf new file mode 100644 index 000000000000..67acf222d764 --- /dev/null +++ b/tools/topology/topology2/include/bench/stft_process_controls_capture.conf @@ -0,0 +1,21 @@ + # Created initially with script "./bench_comp_generate.sh stft_process" + # may need edits to modify controls + Object.Control { + # Un-comment the supported controls in STFT_PROCESS + bytes."1" { + name '$ANALOG_CAPTURE_PCM STFT_PROCESS bytes' + IncludeByKey.BENCH_STFT_PROCESS_PARAMS { + "hann_192_48" "include/components/stft_process/hann_192_48.conf" + "hann_512_128" "include/components/stft_process/hann_512_128.conf" + "hann_768_120" "include/components/stft_process/hann_768_120.conf" + "hann_1024_256" "include/components/stft_process/hann_1024_256.conf" + "hann_1536_240" "include/components/stft_process/hann_1536_240.conf" + } + } + #mixer."1" { + # name '$ANALOG_CAPTURE_PCM STFT_PROCESS switch or volume' + #} + #enum."1" { + # name '$ANALOG_CAPTURE_PCM STFT_PROCESS enum' + #} + } diff --git a/tools/topology/topology2/include/bench/stft_process_controls_playback.conf b/tools/topology/topology2/include/bench/stft_process_controls_playback.conf new file mode 100644 index 000000000000..82c702555e40 --- /dev/null +++ b/tools/topology/topology2/include/bench/stft_process_controls_playback.conf @@ -0,0 +1,21 @@ + # Created initially with script "./bench_comp_generate.sh stft_process" + # may need edits to modify controls + Object.Control { + # Un-comment the supported controls in STFT_PROCESS + bytes."1" { + name '$ANALOG_PLAYBACK_PCM STFT_PROCESS bytes' + IncludeByKey.BENCH_STFT_PROCESS_PARAMS { + "hann_192_48" "include/components/stft_process/hann_192_48.conf" + "hann_512_128" "include/components/stft_process/hann_512_128.conf" + "hann_768_120" "include/components/stft_process/hann_768_120.conf" + "hann_1024_256" "include/components/stft_process/hann_1024_256.conf" + "hann_1536_240" "include/components/stft_process/hann_1536_240.conf" + } + } + #mixer."1" { + # name '$ANALOG_PLAYBACK_PCM STFT_PROCESS switch or volume' + #} + #enum."1" { + # name '$ANALOG_PLAYBACK_PCM STFT_PROCESS enum' + #} + } diff --git a/tools/topology/topology2/include/bench/stft_process_route.conf b/tools/topology/topology2/include/bench/stft_process_route.conf new file mode 100644 index 000000000000..dcc4dc3feebc --- /dev/null +++ b/tools/topology/topology2/include/bench/stft_process_route.conf @@ -0,0 +1,19 @@ + # Created with script "./bench_comp_generate.sh stft_process" + Object.Base.route [ + { + sink '$BENCH_PLAYBACK_DAI_COPIER' + source 'stft_process.$BENCH_PLAYBACK_HOST_PIPELINE.1' + } + { + sink 'stft_process.$BENCH_PLAYBACK_HOST_PIPELINE.1' + source 'host-copier.0.playback' + } + { + source '$BENCH_CAPTURE_DAI_COPIER' + sink 'stft_process.$BENCH_CAPTURE_HOST_PIPELINE.2' + } + { + source 'stft_process.$BENCH_CAPTURE_HOST_PIPELINE.2' + sink 'host-copier.0.capture' + } + ] diff --git a/tools/topology/topology2/include/bench/stft_process_s16.conf b/tools/topology/topology2/include/bench/stft_process_s16.conf new file mode 100644 index 000000000000..ebe14bcd0c21 --- /dev/null +++ b/tools/topology/topology2/include/bench/stft_process_s16.conf @@ -0,0 +1,13 @@ + # Created with script "./bench_comp_generate.sh stft_process" + Object.Widget.stft_process.1 { + index $BENCH_PLAYBACK_HOST_PIPELINE + <include/bench/one_input_output_format_s16.conf> + <include/bench/stft_process_controls_playback.conf> + } + Object.Widget.stft_process.2 { + index $BENCH_CAPTURE_HOST_PIPELINE + <include/bench/one_input_output_format_s16.conf> + <include/bench/stft_process_controls_capture.conf> + } + <include/bench/host_gateway_pipelines_s16.conf> + <include/bench/stft_process_route.conf> diff --git a/tools/topology/topology2/include/bench/stft_process_s24.conf b/tools/topology/topology2/include/bench/stft_process_s24.conf new file mode 100644 index 000000000000..b75f35a3a486 --- /dev/null +++ b/tools/topology/topology2/include/bench/stft_process_s24.conf @@ -0,0 +1,13 @@ + # Created with script "./bench_comp_generate.sh stft_process" + Object.Widget.stft_process.1 { + index $BENCH_PLAYBACK_HOST_PIPELINE + <include/bench/one_input_output_format_s24.conf> + <include/bench/stft_process_controls_playback.conf> + } + Object.Widget.stft_process.2 { + index $BENCH_CAPTURE_HOST_PIPELINE + <include/bench/one_input_output_format_s24.conf> + <include/bench/stft_process_controls_capture.conf> + } + <include/bench/host_gateway_pipelines_s24.conf> + <include/bench/stft_process_route.conf> diff --git a/tools/topology/topology2/include/bench/stft_process_s32.conf b/tools/topology/topology2/include/bench/stft_process_s32.conf new file mode 100644 index 000000000000..d0d16557bbee --- /dev/null +++ b/tools/topology/topology2/include/bench/stft_process_s32.conf @@ -0,0 +1,13 @@ + # Created with script "./bench_comp_generate.sh stft_process" + Object.Widget.stft_process.1 { + index $BENCH_PLAYBACK_HOST_PIPELINE + <include/bench/one_input_output_format_s32.conf> + <include/bench/stft_process_controls_playback.conf> + } + Object.Widget.stft_process.2 { + index $BENCH_CAPTURE_HOST_PIPELINE + <include/bench/one_input_output_format_s32.conf> + <include/bench/stft_process_controls_capture.conf> + } + <include/bench/host_gateway_pipelines_s32.conf> + <include/bench/stft_process_route.conf> diff --git a/tools/topology/topology2/include/common/common_definitions.conf b/tools/topology/topology2/include/common/common_definitions.conf index 2c5d5d0825d0..87c69dd41e41 100644 --- a/tools/topology/topology2/include/common/common_definitions.conf +++ b/tools/topology/topology2/include/common/common_definitions.conf @@ -67,4 +67,10 @@ Define { SSP_BLOB_VERSION_1_0 0x100 SSP_BLOB_VERSION_1_5 0x105 SSP_BLOB_VERSION_3_0 0x300 + + PCM_FORMAT_ALL false # Basic s16/s24/s32, no float, 8-bit etc. + SDW_JACK_ECHO_REF false # No echo reference for 3.5mm jack + SDW_SPK_ECHO_REF false # No echo reference for speaker + SDW_JACK_AUDIO_FEATURE_CAPTURE false # No audio features capture for jack + SDW_DMIC_AUDIO_FEATURE_CAPTURE false # No audio features capture for microphone } diff --git a/tools/topology/topology2/include/common/tokens.conf b/tools/topology/topology2/include/common/tokens.conf index d1b986ab01d4..48b498280130 100644 --- a/tools/topology/topology2/include/common/tokens.conf +++ b/tools/topology/topology2/include/common/tokens.conf @@ -28,7 +28,9 @@ Object.Base.VendorToken { scheduler_domain 418 domain_id 419 stack_bytes_requirement 420 - heap_bytes_requirement 421 + interim_heap_bytes_requirement 421 + lifetime_heap_bytes_requirement 422 + shared_bytes_requirement 423 } "2" { @@ -84,6 +86,8 @@ Object.Base.VendorToken { lp_mode 207 use_chain_dma 209 kcps 210 + direction 211 + direction_valid 212 } "9" { diff --git a/tools/topology/topology2/include/components/dax/default.conf b/tools/topology/topology2/include/components/dax/default.conf new file mode 100644 index 000000000000..944157bb59c1 --- /dev/null +++ b/tools/topology/topology2/include/components/dax/default.conf @@ -0,0 +1,830 @@ +Object.Base.data."dolby_data" { + bytes " + 0x53,0x4f,0x46,0x34,0x01,0x00,0x00,0x00, + 0xb4,0x19,0x00,0x00,0x00,0xa0,0x01,0x03, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x27,0x10,0x00,0x08,0x78,0x19,0x00,0x00, + 0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x28,0x10,0x00,0x08,0x54,0x04,0x00,0x00, + 0x00,0x00,0x00,0x00,0xeb,0x08,0x01,0x00, + 0xac,0x00,0x00,0x00,0x14,0x00,0x00,0x00, + 0x2f,0x00,0x00,0x00,0x8d,0x00,0x00,0x00, + 0xea,0x00,0x00,0x00,0x48,0x01,0x00,0x00, + 0xd5,0x01,0x00,0x00,0x90,0x02,0x00,0x00, + 0x4c,0x03,0x00,0x00,0x07,0x04,0x00,0x00, + 0x21,0x05,0x00,0x00,0x98,0x06,0x00,0x00, + 0xca,0x08,0x00,0x00,0xb8,0x0b,0x00,0x00, + 0xa6,0x0e,0x00,0x00,0x50,0x12,0x00,0x00, + 0xb5,0x16,0x00,0x00,0xd5,0x1b,0x00,0x00, + 0x28,0x23,0x00,0x00,0xf2,0x2b,0x00,0x00, + 0x33,0x36,0x00,0x00,0xe8,0x4c,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xe6,0x08,0x01,0x00,0x0c,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0xe5,0x08,0x01,0x00, + 0x0c,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0xe8,0x08,0x01,0x00,0x0c,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0xe9,0x08,0x01,0x00, + 0x0c,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x40,0x07,0x01,0x00,0x0c,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x42,0x07,0x01,0x00, + 0x28,0x00,0x00,0x00,0x67,0x00,0x00,0x00, + 0x38,0x7f,0x00,0x00,0x9c,0x2b,0x00,0x00, + 0xe2,0x13,0x00,0x00,0x00,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x4c,0x07,0x01,0x00, + 0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x4b,0x07,0x01,0x00,0x0c,0x00,0x00,0x00, + 0x2d,0x00,0x00,0x00,0x29,0x10,0x00,0x08, + 0x90,0x01,0x00,0x00,0x00,0x00,0x00,0x00, + 0xea,0x08,0x01,0x00,0xac,0x00,0x00,0x00, + 0x14,0x00,0x00,0x00,0x2f,0x00,0x00,0x00, + 0x8d,0x00,0x00,0x00,0xea,0x00,0x00,0x00, + 0x48,0x01,0x00,0x00,0xd5,0x01,0x00,0x00, + 0x90,0x02,0x00,0x00,0x4c,0x03,0x00,0x00, + 0x07,0x04,0x00,0x00,0x21,0x05,0x00,0x00, + 0x98,0x06,0x00,0x00,0xca,0x08,0x00,0x00, + 0xb8,0x0b,0x00,0x00,0xa6,0x0e,0x00,0x00, + 0x50,0x12,0x00,0x00,0xb5,0x16,0x00,0x00, + 0xd5,0x1b,0x00,0x00,0x28,0x23,0x00,0x00, + 0xf2,0x2b,0x00,0x00,0x33,0x36,0x00,0x00, + 0xe8,0x4c,0x00,0x00,0x9d,0x00,0x00,0x00, + 0xa7,0x00,0x00,0x00,0xda,0x00,0x00,0x00, + 0xda,0x00,0x00,0x00,0xcb,0x00,0x00,0x00, + 0xbc,0x00,0x00,0x00,0xc0,0x00,0x00,0x00, + 0xc0,0x00,0x00,0x00,0xcd,0x00,0x00,0x00, + 0xd5,0x00,0x00,0x00,0xda,0x00,0x00,0x00, + 0xd1,0x00,0x00,0x00,0xc1,0x00,0x00,0x00, + 0x9f,0x00,0x00,0x00,0x86,0x00,0x00,0x00, + 0x61,0x00,0x00,0x00,0x47,0x00,0x00,0x00, + 0x16,0x00,0x00,0x00,0xa6,0xff,0xff,0xff, + 0xe5,0xfe,0xff,0xff,0x43,0x07,0x01,0x00, + 0x0c,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x6a,0x07,0x01,0x00,0x0c,0x00,0x00,0x00, + 0x06,0x00,0x00,0x00,0x68,0x07,0x01,0x00, + 0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xe7,0x08,0x01,0x00,0x0c,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x38,0x07,0x01,0x00, + 0x0c,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x39,0x07,0x01,0x00,0x0c,0x00,0x00,0x00, + 0x04,0x00,0x00,0x00,0x3a,0x07,0x01,0x00, + 0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x50,0x07,0x01,0x00,0x0c,0x00,0x00,0x00, + 0x00,0x01,0x00,0x00,0x36,0x07,0x01,0x00, + 0x0c,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0xe4,0x08,0x01,0x00,0x0c,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x57,0x07,0x01,0x00, + 0x0c,0x00,0x00,0x00,0x20,0x00,0x00,0x00, + 0x3c,0x07,0x01,0x00,0x0c,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x3d,0x07,0x01,0x00, + 0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x3e,0x07,0x01,0x00,0x0c,0x00,0x00,0x00, + 0x00,0xff,0xff,0xff,0x3f,0x07,0x01,0x00, + 0x0c,0x00,0x00,0x00,0x00,0xff,0xff,0xff, + 0x48,0x07,0x01,0x00,0x0c,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x76,0x74,0x6c,0x65, + 0x0c,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x44,0x07,0x01,0x00,0x0c,0x00,0x00,0x00, + 0x04,0x00,0x00,0x00,0x29,0x10,0x00,0x08, + 0x90,0x01,0x00,0x00,0x01,0x00,0x00,0x00, + 0xea,0x08,0x01,0x00,0xac,0x00,0x00,0x00, + 0x14,0x00,0x00,0x00,0x2f,0x00,0x00,0x00, + 0x8d,0x00,0x00,0x00,0xea,0x00,0x00,0x00, + 0x48,0x01,0x00,0x00,0xd5,0x01,0x00,0x00, + 0x90,0x02,0x00,0x00,0x4c,0x03,0x00,0x00, + 0x07,0x04,0x00,0x00,0x21,0x05,0x00,0x00, + 0x98,0x06,0x00,0x00,0xca,0x08,0x00,0x00, + 0xb8,0x0b,0x00,0x00,0xa6,0x0e,0x00,0x00, + 0x50,0x12,0x00,0x00,0xb5,0x16,0x00,0x00, + 0xd5,0x1b,0x00,0x00,0x28,0x23,0x00,0x00, + 0xf2,0x2b,0x00,0x00,0x33,0x36,0x00,0x00, + 0xe8,0x4c,0x00,0x00,0x9d,0x00,0x00,0x00, + 0xa7,0x00,0x00,0x00,0xda,0x00,0x00,0x00, + 0xda,0x00,0x00,0x00,0xcb,0x00,0x00,0x00, + 0xbc,0x00,0x00,0x00,0xc0,0x00,0x00,0x00, + 0xc0,0x00,0x00,0x00,0xcd,0x00,0x00,0x00, + 0xd5,0x00,0x00,0x00,0xda,0x00,0x00,0x00, + 0xd1,0x00,0x00,0x00,0xc1,0x00,0x00,0x00, + 0x9f,0x00,0x00,0x00,0x86,0x00,0x00,0x00, + 0x61,0x00,0x00,0x00,0x47,0x00,0x00,0x00, + 0x16,0x00,0x00,0x00,0xa6,0xff,0xff,0xff, + 0xe5,0xfe,0xff,0xff,0x43,0x07,0x01,0x00, + 0x0c,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x6a,0x07,0x01,0x00,0x0c,0x00,0x00,0x00, + 0x06,0x00,0x00,0x00,0x68,0x07,0x01,0x00, + 0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xe7,0x08,0x01,0x00,0x0c,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x38,0x07,0x01,0x00, + 0x0c,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x39,0x07,0x01,0x00,0x0c,0x00,0x00,0x00, + 0x04,0x00,0x00,0x00,0x3a,0x07,0x01,0x00, + 0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x50,0x07,0x01,0x00,0x0c,0x00,0x00,0x00, + 0x00,0x01,0x00,0x00,0x36,0x07,0x01,0x00, + 0x0c,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0xe4,0x08,0x01,0x00,0x0c,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x57,0x07,0x01,0x00, + 0x0c,0x00,0x00,0x00,0x20,0x00,0x00,0x00, + 0x3c,0x07,0x01,0x00,0x0c,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x3d,0x07,0x01,0x00, + 0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x3e,0x07,0x01,0x00,0x0c,0x00,0x00,0x00, + 0x00,0xff,0xff,0xff,0x3f,0x07,0x01,0x00, + 0x0c,0x00,0x00,0x00,0x00,0xff,0xff,0xff, + 0x48,0x07,0x01,0x00,0x0c,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x76,0x74,0x6c,0x65, + 0x0c,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x44,0x07,0x01,0x00,0x0c,0x00,0x00,0x00, + 0x04,0x00,0x00,0x00,0x28,0x10,0x00,0x08, + 0x54,0x04,0x00,0x00,0x01,0x00,0x00,0x00, + 0xeb,0x08,0x01,0x00,0xac,0x00,0x00,0x00, + 0x14,0x00,0x00,0x00,0x2f,0x00,0x00,0x00, + 0x8d,0x00,0x00,0x00,0xea,0x00,0x00,0x00, + 0x48,0x01,0x00,0x00,0xd5,0x01,0x00,0x00, + 0x90,0x02,0x00,0x00,0x4c,0x03,0x00,0x00, + 0x07,0x04,0x00,0x00,0x21,0x05,0x00,0x00, + 0x98,0x06,0x00,0x00,0xca,0x08,0x00,0x00, + 0xb8,0x0b,0x00,0x00,0xa6,0x0e,0x00,0x00, + 0x50,0x12,0x00,0x00,0xb5,0x16,0x00,0x00, + 0xd5,0x1b,0x00,0x00,0x28,0x23,0x00,0x00, + 0xf2,0x2b,0x00,0x00,0x33,0x36,0x00,0x00, + 0xe8,0x4c,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0xe6,0x08,0x01,0x00, + 0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xe5,0x08,0x01,0x00,0x0c,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0xe8,0x08,0x01,0x00, + 0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xe9,0x08,0x01,0x00,0x0c,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x40,0x07,0x01,0x00, + 0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x42,0x07,0x01,0x00,0x28,0x00,0x00,0x00, + 0x67,0x00,0x00,0x00,0x38,0x7f,0x00,0x00, + 0x9c,0x2b,0x00,0x00,0xe2,0x13,0x00,0x00, + 0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x4c,0x07,0x01,0x00,0x0c,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x4b,0x07,0x01,0x00, + 0x0c,0x00,0x00,0x00,0x2d,0x00,0x00,0x00, + 0x29,0x10,0x00,0x08,0x90,0x01,0x00,0x00, + 0x00,0x00,0x00,0x00,0xea,0x08,0x01,0x00, + 0xac,0x00,0x00,0x00,0x14,0x00,0x00,0x00, + 0x2f,0x00,0x00,0x00,0x8d,0x00,0x00,0x00, + 0xea,0x00,0x00,0x00,0x48,0x01,0x00,0x00, + 0xd5,0x01,0x00,0x00,0x90,0x02,0x00,0x00, + 0x4c,0x03,0x00,0x00,0x07,0x04,0x00,0x00, + 0x21,0x05,0x00,0x00,0x98,0x06,0x00,0x00, + 0xca,0x08,0x00,0x00,0xb8,0x0b,0x00,0x00, + 0xa6,0x0e,0x00,0x00,0x50,0x12,0x00,0x00, + 0xb5,0x16,0x00,0x00,0xd5,0x1b,0x00,0x00, + 0x28,0x23,0x00,0x00,0xf2,0x2b,0x00,0x00, + 0x33,0x36,0x00,0x00,0xe8,0x4c,0x00,0x00, + 0x9d,0x00,0x00,0x00,0xa7,0x00,0x00,0x00, + 0xda,0x00,0x00,0x00,0xda,0x00,0x00,0x00, + 0xcb,0x00,0x00,0x00,0xbc,0x00,0x00,0x00, + 0xc0,0x00,0x00,0x00,0xc0,0x00,0x00,0x00, + 0xcd,0x00,0x00,0x00,0xd5,0x00,0x00,0x00, + 0xda,0x00,0x00,0x00,0xd1,0x00,0x00,0x00, + 0xc1,0x00,0x00,0x00,0x9f,0x00,0x00,0x00, + 0x86,0x00,0x00,0x00,0x61,0x00,0x00,0x00, + 0x47,0x00,0x00,0x00,0x16,0x00,0x00,0x00, + 0xa6,0xff,0xff,0xff,0xe5,0xfe,0xff,0xff, + 0x43,0x07,0x01,0x00,0x0c,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x6a,0x07,0x01,0x00, + 0x0c,0x00,0x00,0x00,0x06,0x00,0x00,0x00, + 0x68,0x07,0x01,0x00,0x0c,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0xe7,0x08,0x01,0x00, + 0x0c,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x38,0x07,0x01,0x00,0x0c,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x39,0x07,0x01,0x00, + 0x0c,0x00,0x00,0x00,0x04,0x00,0x00,0x00, + 0x3a,0x07,0x01,0x00,0x0c,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x50,0x07,0x01,0x00, + 0x0c,0x00,0x00,0x00,0x00,0x01,0x00,0x00, + 0x36,0x07,0x01,0x00,0x0c,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0xe4,0x08,0x01,0x00, + 0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x57,0x07,0x01,0x00,0x0c,0x00,0x00,0x00, + 0x20,0x00,0x00,0x00,0x3c,0x07,0x01,0x00, + 0x0c,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x3d,0x07,0x01,0x00,0x0c,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x3e,0x07,0x01,0x00, + 0x0c,0x00,0x00,0x00,0x00,0xff,0xff,0xff, + 0x3f,0x07,0x01,0x00,0x0c,0x00,0x00,0x00, + 0x00,0xff,0xff,0xff,0x48,0x07,0x01,0x00, + 0x0c,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x76,0x74,0x6c,0x65,0x0c,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x44,0x07,0x01,0x00, + 0x0c,0x00,0x00,0x00,0x04,0x00,0x00,0x00, + 0x29,0x10,0x00,0x08,0x90,0x01,0x00,0x00, + 0x01,0x00,0x00,0x00,0xea,0x08,0x01,0x00, + 0xac,0x00,0x00,0x00,0x14,0x00,0x00,0x00, + 0x2f,0x00,0x00,0x00,0x8d,0x00,0x00,0x00, + 0xea,0x00,0x00,0x00,0x48,0x01,0x00,0x00, + 0xd5,0x01,0x00,0x00,0x90,0x02,0x00,0x00, + 0x4c,0x03,0x00,0x00,0x07,0x04,0x00,0x00, + 0x21,0x05,0x00,0x00,0x98,0x06,0x00,0x00, + 0xca,0x08,0x00,0x00,0xb8,0x0b,0x00,0x00, + 0xa6,0x0e,0x00,0x00,0x50,0x12,0x00,0x00, + 0xb5,0x16,0x00,0x00,0xd5,0x1b,0x00,0x00, + 0x28,0x23,0x00,0x00,0xf2,0x2b,0x00,0x00, + 0x33,0x36,0x00,0x00,0xe8,0x4c,0x00,0x00, + 0x9d,0x00,0x00,0x00,0xa7,0x00,0x00,0x00, + 0xda,0x00,0x00,0x00,0xda,0x00,0x00,0x00, + 0xcb,0x00,0x00,0x00,0xbc,0x00,0x00,0x00, + 0xc0,0x00,0x00,0x00,0xc0,0x00,0x00,0x00, + 0xcd,0x00,0x00,0x00,0xd5,0x00,0x00,0x00, + 0xda,0x00,0x00,0x00,0xd1,0x00,0x00,0x00, + 0xc1,0x00,0x00,0x00,0x9f,0x00,0x00,0x00, + 0x86,0x00,0x00,0x00,0x61,0x00,0x00,0x00, + 0x47,0x00,0x00,0x00,0x16,0x00,0x00,0x00, + 0xa6,0xff,0xff,0xff,0xe5,0xfe,0xff,0xff, + 0x43,0x07,0x01,0x00,0x0c,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x6a,0x07,0x01,0x00, + 0x0c,0x00,0x00,0x00,0x06,0x00,0x00,0x00, + 0x68,0x07,0x01,0x00,0x0c,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0xe7,0x08,0x01,0x00, + 0x0c,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x38,0x07,0x01,0x00,0x0c,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x39,0x07,0x01,0x00, + 0x0c,0x00,0x00,0x00,0x04,0x00,0x00,0x00, + 0x3a,0x07,0x01,0x00,0x0c,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x50,0x07,0x01,0x00, + 0x0c,0x00,0x00,0x00,0x00,0x01,0x00,0x00, + 0x36,0x07,0x01,0x00,0x0c,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0xe4,0x08,0x01,0x00, + 0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x57,0x07,0x01,0x00,0x0c,0x00,0x00,0x00, + 0x20,0x00,0x00,0x00,0x3c,0x07,0x01,0x00, + 0x0c,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x3d,0x07,0x01,0x00,0x0c,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x3e,0x07,0x01,0x00, + 0x0c,0x00,0x00,0x00,0x00,0xff,0xff,0xff, + 0x3f,0x07,0x01,0x00,0x0c,0x00,0x00,0x00, + 0x00,0xff,0xff,0xff,0x48,0x07,0x01,0x00, + 0x0c,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x76,0x74,0x6c,0x65,0x0c,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x44,0x07,0x01,0x00, + 0x0c,0x00,0x00,0x00,0x04,0x00,0x00,0x00, + 0x28,0x10,0x00,0x08,0x54,0x04,0x00,0x00, + 0x02,0x00,0x00,0x00,0xeb,0x08,0x01,0x00, + 0xac,0x00,0x00,0x00,0x14,0x00,0x00,0x00, + 0x2f,0x00,0x00,0x00,0x8d,0x00,0x00,0x00, + 0xea,0x00,0x00,0x00,0x48,0x01,0x00,0x00, + 0xd5,0x01,0x00,0x00,0x90,0x02,0x00,0x00, + 0x4c,0x03,0x00,0x00,0x07,0x04,0x00,0x00, + 0x21,0x05,0x00,0x00,0x98,0x06,0x00,0x00, + 0xca,0x08,0x00,0x00,0xb8,0x0b,0x00,0x00, + 0xa6,0x0e,0x00,0x00,0x50,0x12,0x00,0x00, + 0xb5,0x16,0x00,0x00,0xd5,0x1b,0x00,0x00, + 0x28,0x23,0x00,0x00,0xf2,0x2b,0x00,0x00, + 0x33,0x36,0x00,0x00,0xe8,0x4c,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xe6,0x08,0x01,0x00,0x0c,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0xe5,0x08,0x01,0x00, + 0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xe8,0x08,0x01,0x00,0x0c,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0xe9,0x08,0x01,0x00, + 0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x40,0x07,0x01,0x00,0x0c,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x42,0x07,0x01,0x00, + 0x28,0x00,0x00,0x00,0x67,0x00,0x00,0x00, + 0x38,0x7f,0x00,0x00,0x9c,0x2b,0x00,0x00, + 0xe2,0x13,0x00,0x00,0x00,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x4c,0x07,0x01,0x00, + 0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x4b,0x07,0x01,0x00,0x0c,0x00,0x00,0x00, + 0x2d,0x00,0x00,0x00,0x29,0x10,0x00,0x08, + 0x90,0x01,0x00,0x00,0x00,0x00,0x00,0x00, + 0xea,0x08,0x01,0x00,0xac,0x00,0x00,0x00, + 0x14,0x00,0x00,0x00,0x2f,0x00,0x00,0x00, + 0x8d,0x00,0x00,0x00,0xea,0x00,0x00,0x00, + 0x48,0x01,0x00,0x00,0xd5,0x01,0x00,0x00, + 0x90,0x02,0x00,0x00,0x4c,0x03,0x00,0x00, + 0x07,0x04,0x00,0x00,0x21,0x05,0x00,0x00, + 0x98,0x06,0x00,0x00,0xca,0x08,0x00,0x00, + 0xb8,0x0b,0x00,0x00,0xa6,0x0e,0x00,0x00, + 0x50,0x12,0x00,0x00,0xb5,0x16,0x00,0x00, + 0xd5,0x1b,0x00,0x00,0x28,0x23,0x00,0x00, + 0xf2,0x2b,0x00,0x00,0x33,0x36,0x00,0x00, + 0xe8,0x4c,0x00,0x00,0x9d,0x00,0x00,0x00, + 0xa7,0x00,0x00,0x00,0xda,0x00,0x00,0x00, + 0xda,0x00,0x00,0x00,0xcb,0x00,0x00,0x00, + 0xbc,0x00,0x00,0x00,0xc0,0x00,0x00,0x00, + 0xc0,0x00,0x00,0x00,0xcd,0x00,0x00,0x00, + 0xd5,0x00,0x00,0x00,0xda,0x00,0x00,0x00, + 0xd1,0x00,0x00,0x00,0xc1,0x00,0x00,0x00, + 0x9f,0x00,0x00,0x00,0x86,0x00,0x00,0x00, + 0x61,0x00,0x00,0x00,0x47,0x00,0x00,0x00, + 0x16,0x00,0x00,0x00,0xa6,0xff,0xff,0xff, + 0xe5,0xfe,0xff,0xff,0x43,0x07,0x01,0x00, + 0x0c,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x6a,0x07,0x01,0x00,0x0c,0x00,0x00,0x00, + 0x06,0x00,0x00,0x00,0x68,0x07,0x01,0x00, + 0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xe7,0x08,0x01,0x00,0x0c,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x38,0x07,0x01,0x00, + 0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x39,0x07,0x01,0x00,0x0c,0x00,0x00,0x00, + 0x04,0x00,0x00,0x00,0x3a,0x07,0x01,0x00, + 0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x50,0x07,0x01,0x00,0x0c,0x00,0x00,0x00, + 0x00,0x01,0x00,0x00,0x36,0x07,0x01,0x00, + 0x0c,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0xe4,0x08,0x01,0x00,0x0c,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x57,0x07,0x01,0x00, + 0x0c,0x00,0x00,0x00,0x20,0x00,0x00,0x00, + 0x3c,0x07,0x01,0x00,0x0c,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x3d,0x07,0x01,0x00, + 0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x3e,0x07,0x01,0x00,0x0c,0x00,0x00,0x00, + 0x00,0xff,0xff,0xff,0x3f,0x07,0x01,0x00, + 0x0c,0x00,0x00,0x00,0x00,0xff,0xff,0xff, + 0x48,0x07,0x01,0x00,0x0c,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x76,0x74,0x6c,0x65, + 0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x44,0x07,0x01,0x00,0x0c,0x00,0x00,0x00, + 0x04,0x00,0x00,0x00,0x29,0x10,0x00,0x08, + 0x90,0x01,0x00,0x00,0x01,0x00,0x00,0x00, + 0xea,0x08,0x01,0x00,0xac,0x00,0x00,0x00, + 0x14,0x00,0x00,0x00,0x2f,0x00,0x00,0x00, + 0x8d,0x00,0x00,0x00,0xea,0x00,0x00,0x00, + 0x48,0x01,0x00,0x00,0xd5,0x01,0x00,0x00, + 0x90,0x02,0x00,0x00,0x4c,0x03,0x00,0x00, + 0x07,0x04,0x00,0x00,0x21,0x05,0x00,0x00, + 0x98,0x06,0x00,0x00,0xca,0x08,0x00,0x00, + 0xb8,0x0b,0x00,0x00,0xa6,0x0e,0x00,0x00, + 0x50,0x12,0x00,0x00,0xb5,0x16,0x00,0x00, + 0xd5,0x1b,0x00,0x00,0x28,0x23,0x00,0x00, + 0xf2,0x2b,0x00,0x00,0x33,0x36,0x00,0x00, + 0xe8,0x4c,0x00,0x00,0x9d,0x00,0x00,0x00, + 0xa7,0x00,0x00,0x00,0xda,0x00,0x00,0x00, + 0xda,0x00,0x00,0x00,0xcb,0x00,0x00,0x00, + 0xbc,0x00,0x00,0x00,0xc0,0x00,0x00,0x00, + 0xc0,0x00,0x00,0x00,0xcd,0x00,0x00,0x00, + 0xd5,0x00,0x00,0x00,0xda,0x00,0x00,0x00, + 0xd1,0x00,0x00,0x00,0xc1,0x00,0x00,0x00, + 0x9f,0x00,0x00,0x00,0x86,0x00,0x00,0x00, + 0x61,0x00,0x00,0x00,0x47,0x00,0x00,0x00, + 0x16,0x00,0x00,0x00,0xa6,0xff,0xff,0xff, + 0xe5,0xfe,0xff,0xff,0x43,0x07,0x01,0x00, + 0x0c,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x6a,0x07,0x01,0x00,0x0c,0x00,0x00,0x00, + 0x06,0x00,0x00,0x00,0x68,0x07,0x01,0x00, + 0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xe7,0x08,0x01,0x00,0x0c,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x38,0x07,0x01,0x00, + 0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x39,0x07,0x01,0x00,0x0c,0x00,0x00,0x00, + 0x04,0x00,0x00,0x00,0x3a,0x07,0x01,0x00, + 0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x50,0x07,0x01,0x00,0x0c,0x00,0x00,0x00, + 0x00,0x01,0x00,0x00,0x36,0x07,0x01,0x00, + 0x0c,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0xe4,0x08,0x01,0x00,0x0c,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x57,0x07,0x01,0x00, + 0x0c,0x00,0x00,0x00,0x20,0x00,0x00,0x00, + 0x3c,0x07,0x01,0x00,0x0c,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x3d,0x07,0x01,0x00, + 0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x3e,0x07,0x01,0x00,0x0c,0x00,0x00,0x00, + 0x00,0xff,0xff,0xff,0x3f,0x07,0x01,0x00, + 0x0c,0x00,0x00,0x00,0x00,0xff,0xff,0xff, + 0x48,0x07,0x01,0x00,0x0c,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x76,0x74,0x6c,0x65, + 0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x44,0x07,0x01,0x00,0x0c,0x00,0x00,0x00, + 0x04,0x00,0x00,0x00,0x28,0x10,0x00,0x08, + 0x54,0x04,0x00,0x00,0x03,0x00,0x00,0x00, + 0xeb,0x08,0x01,0x00,0xac,0x00,0x00,0x00, + 0x14,0x00,0x00,0x00,0x2f,0x00,0x00,0x00, + 0x8d,0x00,0x00,0x00,0xea,0x00,0x00,0x00, + 0x48,0x01,0x00,0x00,0xd5,0x01,0x00,0x00, + 0x90,0x02,0x00,0x00,0x4c,0x03,0x00,0x00, + 0x07,0x04,0x00,0x00,0x21,0x05,0x00,0x00, + 0x98,0x06,0x00,0x00,0xca,0x08,0x00,0x00, + 0xb8,0x0b,0x00,0x00,0xa6,0x0e,0x00,0x00, + 0x50,0x12,0x00,0x00,0xb5,0x16,0x00,0x00, + 0xd5,0x1b,0x00,0x00,0x28,0x23,0x00,0x00, + 0xf2,0x2b,0x00,0x00,0x33,0x36,0x00,0x00, + 0xe8,0x4c,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0xe6,0x08,0x01,0x00, + 0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xe5,0x08,0x01,0x00,0x0c,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0xe8,0x08,0x01,0x00, + 0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xe9,0x08,0x01,0x00,0x0c,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x40,0x07,0x01,0x00, + 0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x42,0x07,0x01,0x00,0x28,0x00,0x00,0x00, + 0x23,0x00,0x00,0x00,0xff,0x7f,0x00,0x00, + 0xff,0x7f,0x00,0x00,0xff,0x7f,0x00,0x00, + 0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x4c,0x07,0x01,0x00,0x0c,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x4b,0x07,0x01,0x00, + 0x0c,0x00,0x00,0x00,0x2d,0x00,0x00,0x00, + 0x29,0x10,0x00,0x08,0x90,0x01,0x00,0x00, + 0x00,0x00,0x00,0x00,0xea,0x08,0x01,0x00, + 0xac,0x00,0x00,0x00,0x14,0x00,0x00,0x00, + 0x2f,0x00,0x00,0x00,0x8d,0x00,0x00,0x00, + 0xea,0x00,0x00,0x00,0x48,0x01,0x00,0x00, + 0xd5,0x01,0x00,0x00,0x90,0x02,0x00,0x00, + 0x4c,0x03,0x00,0x00,0x07,0x04,0x00,0x00, + 0x21,0x05,0x00,0x00,0x98,0x06,0x00,0x00, + 0xca,0x08,0x00,0x00,0xb8,0x0b,0x00,0x00, + 0xa6,0x0e,0x00,0x00,0x50,0x12,0x00,0x00, + 0xb5,0x16,0x00,0x00,0xd5,0x1b,0x00,0x00, + 0x28,0x23,0x00,0x00,0xf2,0x2b,0x00,0x00, + 0x33,0x36,0x00,0x00,0xe8,0x4c,0x00,0x00, + 0x9d,0x00,0x00,0x00,0xa7,0x00,0x00,0x00, + 0xda,0x00,0x00,0x00,0xda,0x00,0x00,0x00, + 0xcb,0x00,0x00,0x00,0xbc,0x00,0x00,0x00, + 0xc0,0x00,0x00,0x00,0xc0,0x00,0x00,0x00, + 0xcd,0x00,0x00,0x00,0xd5,0x00,0x00,0x00, + 0xda,0x00,0x00,0x00,0xd1,0x00,0x00,0x00, + 0xc1,0x00,0x00,0x00,0x9f,0x00,0x00,0x00, + 0x86,0x00,0x00,0x00,0x61,0x00,0x00,0x00, + 0x47,0x00,0x00,0x00,0x16,0x00,0x00,0x00, + 0xa6,0xff,0xff,0xff,0xe5,0xfe,0xff,0xff, + 0x43,0x07,0x01,0x00,0x0c,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x6a,0x07,0x01,0x00, + 0x0c,0x00,0x00,0x00,0x06,0x00,0x00,0x00, + 0x68,0x07,0x01,0x00,0x0c,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0xe7,0x08,0x01,0x00, + 0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x38,0x07,0x01,0x00,0x0c,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x39,0x07,0x01,0x00, + 0x0c,0x00,0x00,0x00,0x08,0x00,0x00,0x00, + 0x3a,0x07,0x01,0x00,0x0c,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x50,0x07,0x01,0x00, + 0x0c,0x00,0x00,0x00,0x00,0x01,0x00,0x00, + 0x36,0x07,0x01,0x00,0x0c,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0xe4,0x08,0x01,0x00, + 0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x57,0x07,0x01,0x00,0x0c,0x00,0x00,0x00, + 0x20,0x00,0x00,0x00,0x3c,0x07,0x01,0x00, + 0x0c,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x3d,0x07,0x01,0x00,0x0c,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x3e,0x07,0x01,0x00, + 0x0c,0x00,0x00,0x00,0x00,0xff,0xff,0xff, + 0x3f,0x07,0x01,0x00,0x0c,0x00,0x00,0x00, + 0x00,0xff,0xff,0xff,0x48,0x07,0x01,0x00, + 0x0c,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x76,0x74,0x6c,0x65,0x0c,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x44,0x07,0x01,0x00, + 0x0c,0x00,0x00,0x00,0x04,0x00,0x00,0x00, + 0x29,0x10,0x00,0x08,0x90,0x01,0x00,0x00, + 0x01,0x00,0x00,0x00,0xea,0x08,0x01,0x00, + 0xac,0x00,0x00,0x00,0x14,0x00,0x00,0x00, + 0x2f,0x00,0x00,0x00,0x8d,0x00,0x00,0x00, + 0xea,0x00,0x00,0x00,0x48,0x01,0x00,0x00, + 0xd5,0x01,0x00,0x00,0x90,0x02,0x00,0x00, + 0x4c,0x03,0x00,0x00,0x07,0x04,0x00,0x00, + 0x21,0x05,0x00,0x00,0x98,0x06,0x00,0x00, + 0xca,0x08,0x00,0x00,0xb8,0x0b,0x00,0x00, + 0xa6,0x0e,0x00,0x00,0x50,0x12,0x00,0x00, + 0xb5,0x16,0x00,0x00,0xd5,0x1b,0x00,0x00, + 0x28,0x23,0x00,0x00,0xf2,0x2b,0x00,0x00, + 0x33,0x36,0x00,0x00,0xe8,0x4c,0x00,0x00, + 0x9d,0x00,0x00,0x00,0xa7,0x00,0x00,0x00, + 0xda,0x00,0x00,0x00,0xda,0x00,0x00,0x00, + 0xcb,0x00,0x00,0x00,0xbc,0x00,0x00,0x00, + 0xc0,0x00,0x00,0x00,0xc0,0x00,0x00,0x00, + 0xcd,0x00,0x00,0x00,0xd5,0x00,0x00,0x00, + 0xda,0x00,0x00,0x00,0xd1,0x00,0x00,0x00, + 0xc1,0x00,0x00,0x00,0x9f,0x00,0x00,0x00, + 0x86,0x00,0x00,0x00,0x61,0x00,0x00,0x00, + 0x47,0x00,0x00,0x00,0x16,0x00,0x00,0x00, + 0xa6,0xff,0xff,0xff,0xe5,0xfe,0xff,0xff, + 0x43,0x07,0x01,0x00,0x0c,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x6a,0x07,0x01,0x00, + 0x0c,0x00,0x00,0x00,0x06,0x00,0x00,0x00, + 0x68,0x07,0x01,0x00,0x0c,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0xe7,0x08,0x01,0x00, + 0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x38,0x07,0x01,0x00,0x0c,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x39,0x07,0x01,0x00, + 0x0c,0x00,0x00,0x00,0x08,0x00,0x00,0x00, + 0x3a,0x07,0x01,0x00,0x0c,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x50,0x07,0x01,0x00, + 0x0c,0x00,0x00,0x00,0x00,0x01,0x00,0x00, + 0x36,0x07,0x01,0x00,0x0c,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0xe4,0x08,0x01,0x00, + 0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x57,0x07,0x01,0x00,0x0c,0x00,0x00,0x00, + 0x20,0x00,0x00,0x00,0x3c,0x07,0x01,0x00, + 0x0c,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x3d,0x07,0x01,0x00,0x0c,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x3e,0x07,0x01,0x00, + 0x0c,0x00,0x00,0x00,0x00,0xff,0xff,0xff, + 0x3f,0x07,0x01,0x00,0x0c,0x00,0x00,0x00, + 0x00,0xff,0xff,0xff,0x48,0x07,0x01,0x00, + 0x0c,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x76,0x74,0x6c,0x65,0x0c,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x44,0x07,0x01,0x00, + 0x0c,0x00,0x00,0x00,0x04,0x00,0x00,0x00, + 0x30,0x10,0x00,0x08,0xb0,0x04,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xec,0x08,0x01,0x00,0x00,0x01,0x00,0x00, + 0x14,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x2f,0x00,0x00,0x00,0x8d,0x00,0x00,0x00, + 0xea,0x00,0x00,0x00,0x48,0x01,0x00,0x00, + 0xd5,0x01,0x00,0x00,0x90,0x02,0x00,0x00, + 0x4c,0x03,0x00,0x00,0x07,0x04,0x00,0x00, + 0x21,0x05,0x00,0x00,0x98,0x06,0x00,0x00, + 0xca,0x08,0x00,0x00,0xb8,0x0b,0x00,0x00, + 0xa6,0x0e,0x00,0x00,0x50,0x12,0x00,0x00, + 0xb5,0x16,0x00,0x00,0xd5,0x1b,0x00,0x00, + 0x28,0x23,0x00,0x00,0xf2,0x2b,0x00,0x00, + 0x33,0x36,0x00,0x00,0xe8,0x4c,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xf5,0x08,0x01,0x00,0x4c,0x01,0x00,0x00, + 0x14,0x00,0x00,0x00,0x2f,0x00,0x00,0x00, + 0x8d,0x00,0x00,0x00,0xea,0x00,0x00,0x00, + 0x48,0x01,0x00,0x00,0xd5,0x01,0x00,0x00, + 0x90,0x02,0x00,0x00,0x4c,0x03,0x00,0x00, + 0x07,0x04,0x00,0x00,0x21,0x05,0x00,0x00, + 0x98,0x06,0x00,0x00,0xca,0x08,0x00,0x00, + 0xb8,0x0b,0x00,0x00,0xa6,0x0e,0x00,0x00, + 0x50,0x12,0x00,0x00,0xb5,0x16,0x00,0x00, + 0xd5,0x1b,0x00,0x00,0x28,0x23,0x00,0x00, + 0xf2,0x2b,0x00,0x00,0x33,0x36,0x00,0x00, + 0xe8,0x4c,0x00,0x00,0x40,0xff,0xff,0xff, + 0x40,0xff,0xff,0xff,0x40,0xff,0xff,0xff, + 0x40,0xff,0xff,0xff,0x40,0xff,0xff,0xff, + 0x40,0xff,0xff,0xff,0x40,0xff,0xff,0xff, + 0x40,0xff,0xff,0xff,0x40,0xff,0xff,0xff, + 0x40,0xff,0xff,0xff,0x40,0xff,0xff,0xff, + 0x40,0xff,0xff,0xff,0x40,0xff,0xff,0xff, + 0x40,0xff,0xff,0xff,0x40,0xff,0xff,0xff, + 0x40,0xff,0xff,0xff,0x40,0xff,0xff,0xff, + 0x40,0xff,0xff,0xff,0x40,0xff,0xff,0xff, + 0x40,0xff,0xff,0xff,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0xed,0x08,0x01,0x00, + 0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x4a,0x07,0x01,0x00,0x0c,0x00,0x00,0x00, + 0x06,0x00,0x00,0x00,0x49,0x07,0x01,0x00, + 0x28,0x00,0x00,0x00,0x67,0x00,0x00,0x00, + 0x38,0x7f,0x00,0x00,0x2a,0x1a,0x00,0x00, + 0xe2,0x13,0x00,0x00,0x00,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x64,0x07,0x01,0x00, + 0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x65,0x07,0x01,0x00,0x0c,0x00,0x00,0x00, + 0x0c,0x00,0x00,0x00,0xf6,0x08,0x01,0x00, + 0x1c,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00, + 0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00, + 0x63,0x07,0x01,0x00,0x0c,0x00,0x00,0x00, + 0x10,0x00,0x00,0x00,0x5b,0x07,0x01,0x00, + 0x48,0x01,0x00,0x00,0x01,0x00,0x00,0x00, + 0xc0,0xf9,0xff,0xff,0x00,0x00,0x00,0x00, + 0xa0,0xfb,0xff,0xff,0x70,0xfe,0xff,0xff, + 0x10,0xff,0xff,0xff,0x00,0x00,0x00,0x00, + 0x05,0x00,0x00,0x00,0x0a,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0xc0,0xf9,0xff,0xff,0x00,0x00,0x00,0x00, + 0xa0,0xfb,0xff,0xff,0x70,0xfe,0xff,0xff, + 0x10,0xff,0xff,0xff,0x00,0x00,0x00,0x00, + 0x05,0x00,0x00,0x00,0x0a,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0xc0,0xf9,0xff,0xff,0x00,0x00,0x00,0x00, + 0xa0,0xfb,0xff,0xff,0x70,0xfe,0xff,0xff, + 0x10,0xff,0xff,0xff,0x00,0x00,0x00,0x00, + 0x05,0x00,0x00,0x00,0x0a,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0xc0,0xf9,0xff,0xff,0x00,0x00,0x00,0x00, + 0xa0,0xfb,0xff,0xff,0x70,0xfe,0xff,0xff, + 0x10,0xff,0xff,0xff,0x00,0x00,0x00,0x00, + 0x05,0x00,0x00,0x00,0x0a,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0xc0,0xf9,0xff,0xff,0x00,0x00,0x00,0x00, + 0xa0,0xfb,0xff,0xff,0x70,0xfe,0xff,0xff, + 0x10,0xff,0xff,0xff,0x00,0x00,0x00,0x00, + 0x05,0x00,0x00,0x00,0x0a,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0xc0,0xf9,0xff,0xff,0x00,0x00,0x00,0x00, + 0xa0,0xfb,0xff,0xff,0x70,0xfe,0xff,0xff, + 0x10,0xff,0xff,0xff,0x00,0x00,0x00,0x00, + 0x05,0x00,0x00,0x00,0x0a,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0xc0,0xf9,0xff,0xff,0x00,0x00,0x00,0x00, + 0xa0,0xfb,0xff,0xff,0x70,0xfe,0xff,0xff, + 0x10,0xff,0xff,0xff,0x00,0x00,0x00,0x00, + 0x05,0x00,0x00,0x00,0x0a,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0xc0,0xf9,0xff,0xff,0x00,0x00,0x00,0x00, + 0xa0,0xfb,0xff,0xff,0x70,0xfe,0xff,0xff, + 0x10,0xff,0xff,0xff,0x00,0x00,0x00,0x00, + 0x05,0x00,0x00,0x00,0x0a,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0xe1,0x08,0x01,0x00, + 0x0c,0x00,0x00,0x00,0x0a,0x00,0x00,0x00, + 0xe3,0x08,0x01,0x00,0x0c,0x00,0x00,0x00, + 0x0a,0x00,0x00,0x00,0xe2,0x08,0x01,0x00, + 0x0c,0x00,0x00,0x00,0x0a,0x00,0x00,0x00, + 0x41,0x07,0x01,0x00,0x0c,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x59,0x07,0x01,0x00, + 0x0c,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x3b,0x07,0x01,0x00,0x0c,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0xf4,0x08,0x01,0x00, + 0x0c,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0xf7,0x08,0x01,0x00,0x0c,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x5a,0x07,0x01,0x00, + 0x0c,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x30,0x10,0x00,0x08,0x68,0x03,0x00,0x00, + 0x02,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x06,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0xec,0x08,0x01,0x00,0x00,0x01,0x00,0x00, + 0x14,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x2f,0x00,0x00,0x00,0x8d,0x00,0x00,0x00, + 0xea,0x00,0x00,0x00,0x48,0x01,0x00,0x00, + 0xd5,0x01,0x00,0x00,0x90,0x02,0x00,0x00, + 0x4c,0x03,0x00,0x00,0x07,0x04,0x00,0x00, + 0x21,0x05,0x00,0x00,0x98,0x06,0x00,0x00, + 0xca,0x08,0x00,0x00,0xb8,0x0b,0x00,0x00, + 0xa6,0x0e,0x00,0x00,0x50,0x12,0x00,0x00, + 0xb5,0x16,0x00,0x00,0xd5,0x1b,0x00,0x00, + 0x28,0x23,0x00,0x00,0xf2,0x2b,0x00,0x00, + 0x33,0x36,0x00,0x00,0xe8,0x4c,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xf5,0x08,0x01,0x00,0x4c,0x01,0x00,0x00, + 0x14,0x00,0x00,0x00,0x2f,0x00,0x00,0x00, + 0x8d,0x00,0x00,0x00,0xea,0x00,0x00,0x00, + 0x48,0x01,0x00,0x00,0xd5,0x01,0x00,0x00, + 0x90,0x02,0x00,0x00,0x4c,0x03,0x00,0x00, + 0x07,0x04,0x00,0x00,0x21,0x05,0x00,0x00, + 0x98,0x06,0x00,0x00,0xca,0x08,0x00,0x00, + 0xb8,0x0b,0x00,0x00,0xa6,0x0e,0x00,0x00, + 0x50,0x12,0x00,0x00,0xb5,0x16,0x00,0x00, + 0xd5,0x1b,0x00,0x00,0x28,0x23,0x00,0x00, + 0xf2,0x2b,0x00,0x00,0x33,0x36,0x00,0x00, + 0xe8,0x4c,0x00,0x00,0x40,0xff,0xff,0xff, + 0x40,0xff,0xff,0xff,0x40,0xff,0xff,0xff, + 0x40,0xff,0xff,0xff,0x40,0xff,0xff,0xff, + 0x40,0xff,0xff,0xff,0x40,0xff,0xff,0xff, + 0x40,0xff,0xff,0xff,0x40,0xff,0xff,0xff, + 0x40,0xff,0xff,0xff,0x40,0xff,0xff,0xff, + 0x40,0xff,0xff,0xff,0x40,0xff,0xff,0xff, + 0x40,0xff,0xff,0xff,0x40,0xff,0xff,0xff, + 0x40,0xff,0xff,0xff,0x40,0xff,0xff,0xff, + 0x40,0xff,0xff,0xff,0x40,0xff,0xff,0xff, + 0x40,0xff,0xff,0xff,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0xed,0x08,0x01,0x00, + 0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x4a,0x07,0x01,0x00,0x0c,0x00,0x00,0x00, + 0x06,0x00,0x00,0x00,0x49,0x07,0x01,0x00, + 0x28,0x00,0x00,0x00,0x67,0x00,0x00,0x00, + 0x38,0x7f,0x00,0x00,0x2a,0x1a,0x00,0x00, + 0xe2,0x13,0x00,0x00,0x00,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x64,0x07,0x01,0x00, + 0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x65,0x07,0x01,0x00,0x0c,0x00,0x00,0x00, + 0x0c,0x00,0x00,0x00,0xf6,0x08,0x01,0x00, + 0x1c,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00, + 0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00, + 0x63,0x07,0x01,0x00,0x0c,0x00,0x00,0x00, + 0x10,0x00,0x00,0x00,0xe1,0x08,0x01,0x00, + 0x0c,0x00,0x00,0x00,0x0a,0x00,0x00,0x00, + 0xe3,0x08,0x01,0x00,0x0c,0x00,0x00,0x00, + 0x0a,0x00,0x00,0x00,0xe2,0x08,0x01,0x00, + 0x0c,0x00,0x00,0x00,0x0a,0x00,0x00,0x00, + 0x41,0x07,0x01,0x00,0x0c,0x00,0x00,0x00, + 0xa0,0x00,0x00,0x00,0x59,0x07,0x01,0x00, + 0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x3b,0x07,0x01,0x00,0x0c,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0xf4,0x08,0x01,0x00, + 0x0c,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0xf7,0x08,0x01,0x00,0x0c,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x5a,0x07,0x01,0x00, + 0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x26,0x10,0x00,0x08,0x0c,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x28,0x10,0x00,0x08, + 0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x32,0x10,0x00,0x08,0x0c,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x31,0x10,0x00,0x08, + 0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x34,0x10,0x00,0x08,0x0c,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00" +} \ No newline at end of file diff --git a/tools/topology/topology2/include/components/decoder.conf b/tools/topology/topology2/include/components/decoder.conf new file mode 100644 index 000000000000..b01a1c56d050 --- /dev/null +++ b/tools/topology/topology2/include/components/decoder.conf @@ -0,0 +1,63 @@ +# +# Decoder widget component definition +# +# A generic decoder widget. All attributes defined herein are namespaced +# by alsatplg to "Object.Widget.decoder.N.attribute_name" +# +# Usage: this component can be used by declaring in a parent object. i.e. +# +# Object.Widget.decoder."N" { +# index 1 +# no_pm "true" +# } +# +# Where N is the unique instance number for decoder widget in the same alsaconf node. + +Class.Widget."decoder" { + # + # Pipeline ID for the decoder widget object + # + DefineAttribute."index" {} + + # + # decoder object instance + # + DefineAttribute."instance" {} + + #include common component definition + <include/components/widget-common.conf> + + # Attribute categories + attributes { + # + # The decoder widget name would be constructed using the index and instance attributes. + # For ex: "decoder.1.1" or "decoder.10.2" etc. + # + !constructor [ + "index" + "instance" + ] + + # + # immutable attributes cannot be modified in the object instance + # + !immutable [ + "type" + ] + + # + # decoder widget objects instantiated within the same alsaconf node must have unique + # instance attribute + # + unique "instance" + } + + # Default attribute values for decoder widget + type "decoder" + + # cadence codec UUID + uuid "43:84:21:d8:f3:5f:4c:4a:b3:88:6c:fe:07:b9:56:aa" + no_pm "true" + num_output_pins 1 + num_input_pins 1 +} diff --git a/tools/topology/topology2/include/components/dolby-dax.conf b/tools/topology/topology2/include/components/dolby-dax.conf new file mode 100644 index 000000000000..87b317da0261 --- /dev/null +++ b/tools/topology/topology2/include/components/dolby-dax.conf @@ -0,0 +1,164 @@ +<include/controls/mixer.conf> +<include/controls/enum.conf> + +Class.Widget."dolby-dax" { + DefineAttribute."index" { + type "integer" + } + + DefineAttribute."instance" { + type "integer" + } + + <include/components/widget-common.conf> + + attributes { + !constructor [ + "index" + "instance" + ] + !mandatory [ + "num_input_pins" + "num_output_pins" + "num_input_audio_formats" + "num_output_audio_formats" + ] + + !immutable [ + "uuid" + "type" + ] + !deprecated [ + "preload_count" + ] + unique "instance" + } + + Object.Control { + # Enable/Disable DAX module + mixer."1" { + Object.Base.channel.1 { + name "fc" + } + Object.Base.ops.1 { + name "ctl" + info "volsw" + ## get = 259 binds the mixer control to switch get/put handlers + get 259 + put 259 + } + + #max 1 indicates switch type control + max 1 + } + # Enable/Disable DAX content processing feature + mixer."2" { + Object.Base.channel.1 { + name "fc" + } + Object.Base.ops.1 { + name "ctl" + info "volsw" + ## get = 259 binds the mixer control to switch get/put handlers + get 259 + put 259 + } + + #max 1 indicates switch type control + max 1 + } + # Enable/Disable DAX crosstalk cancellation feature + mixer."3" { + Object.Base.channel.1 { + name "fc" + } + Object.Base.ops.1 { + name "ctl" + info "volsw" + ## get = 259 binds the mixer control to switch get/put handlers + get 259 + put 259 + } + + #max 1 indicates switch type control + max 1 + } + # Set volume to DAX module, range from 0 to 25 + mixer."4" { + Object.Base.channel.1 { + name "fc" + } + Object.Base.ops.1 { + name "ctl" + info "volsw" + get 256 + put 256 + } + max 25 + + Object.Base.tlv.1 { + name "vtlv_m50s200" + Object.Base.scale.1 { + name m0s131072 + min -5000 + step 200 + } + } + } + # Set profile to DAX module + enum."1" { + Object.Base { + channel.1 { + name "fc" + } + text.0 { + name "profiles" + !values [ + "0" # dynamic + "1" # movie + "2" # music + "3" # voice + ] + } + ops.1 { + name "ctl" + info "enum" + #257 binds the mixer control to enum get/put handlers + get 257 + put 257 + } + } + } + # Set output device to DAX module + enum."2" { + Object.Base { + channel.1 { + name "fc" + } + text.0 { + name "devices" + !values [ + "0" # speaker + "1" # 3.5mm wired headphone + ] + } + ops.1 { + name "ctl" + info "enum" + #257 binds the mixer control to enum get/put handlers + get 257 + put 257 + } + } + } + # Set binary effect parameters to DAX module + bytes."1" {} + } + + uuid "8b:6c:f6:40:a5:5a:45:43:89:19:53:ec:43:1a:aa:98" + type "effect" + no_pm "true" + cpc 675000 + num_input_pins 1 + num_output_pins 1 +} diff --git a/tools/topology/topology2/include/components/encoder.conf b/tools/topology/topology2/include/components/encoder.conf new file mode 100644 index 000000000000..cde2346e3885 --- /dev/null +++ b/tools/topology/topology2/include/components/encoder.conf @@ -0,0 +1,63 @@ +# +# Encoder widget component definition +# +# A generic encoder widget. All attributes defined herein are namespaced +# by alsatplg to "Object.Widget.encoder.N.attribute_name" +# +# Usage: this component can be used by declaring in a parent object. i.e. +# +# Object.Widget.encoder."N" { +# index 1 +# no_pm "true" +# } +# +# Where N is the unique instance number for encoder widget in the same alsaconf node. + +Class.Widget."encoder" { + # + # Pipeline ID for the encoder widget object + # + DefineAttribute."index" {} + + # + # encoder object instance + # + DefineAttribute."instance" {} + + #include common component definition + <include/components/widget-common.conf> + + # Attribute categories + attributes { + # + # The encoder widget name would be constructed using the index and instance attributes. + # For ex: "encoder.1.1" or "encoder.10.2" etc. + # + !constructor [ + "index" + "instance" + ] + + # + # immutable attributes cannot be modified in the object instance + # + !immutable [ + "type" + ] + + # + # encoder widget objects instantiated within the same alsaconf node must have unique + # instance attribute + # + unique "instance" + } + + # Default attribute values for encoder widget + type "encoder" + + # cadence codec UUID + uuid "43:84:21:d8:f3:5f:4c:4a:b3:88:6c:fe:07:b9:56:aa" + no_pm "true" + num_output_pins 1 + num_input_pins 1 +} diff --git a/tools/topology/topology2/include/components/eqiir/highpass_100hz_16db_16khz.conf b/tools/topology/topology2/include/components/eqiir/highpass_100hz_16db_16khz.conf new file mode 100644 index 000000000000..264bf3f13431 --- /dev/null +++ b/tools/topology/topology2/include/components/eqiir/highpass_100hz_16db_16khz.conf @@ -0,0 +1,20 @@ +# 100 Hz second order high-pass, gain 16 dB, created with sof_example_iir_eq.m 04-Feb-2026 +# cd src/audio/eq_iir/tune; octave --no-window-system sof_example_iir_eq.m +Object.Base.data."iir_eq" { + bytes " + 0x53,0x4f,0x46,0x34,0x00,0x00,0x00,0x00, + 0x58,0x00,0x00,0x00,0x01,0xd0,0x01,0x03, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x58,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0xf5,0x18,0x75,0xc3, + 0x6b,0x52,0x72,0x7c,0x6e,0x92,0x32,0x1f, + 0x24,0xdb,0x9a,0xc1,0x6e,0x92,0x32,0x1f, + 0xfd,0xff,0xff,0xff,0xb8,0x64,0x00,0x00" +} diff --git a/tools/topology/topology2/include/components/eqiir/highpass_100hz_16db_48khz.conf b/tools/topology/topology2/include/components/eqiir/highpass_100hz_16db_48khz.conf new file mode 100644 index 000000000000..856f1cd6328c --- /dev/null +++ b/tools/topology/topology2/include/components/eqiir/highpass_100hz_16db_48khz.conf @@ -0,0 +1,20 @@ +# 100 Hz second order high-pass, gain 16 dB, created with sof_example_iir_eq.m 04-Feb-2026 +# cd src/audio/eq_iir/tune; octave --no-window-system sof_example_iir_eq.m +Object.Base.data."iir_eq" { + bytes " + 0x53,0x4f,0x46,0x34,0x00,0x00,0x00,0x00, + 0x58,0x00,0x00,0x00,0x01,0xd0,0x01,0x03, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x58,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0xbd,0x82,0x2c,0xc1, + 0x2e,0xb5,0xd0,0x7e,0x0c,0xcc,0xc7,0x1f, + 0xe9,0x67,0x70,0xc0,0x0c,0xcc,0xc7,0x1f, + 0xfd,0xff,0xff,0xff,0xb8,0x64,0x00,0x00" +} diff --git a/tools/topology/topology2/include/components/eqiir/highpass_20hz_16db_16khz.conf b/tools/topology/topology2/include/components/eqiir/highpass_20hz_16db_16khz.conf new file mode 100644 index 000000000000..0f84b2c22601 --- /dev/null +++ b/tools/topology/topology2/include/components/eqiir/highpass_20hz_16db_16khz.conf @@ -0,0 +1,20 @@ +# 20 Hz second order high-pass, gain 16 dB, created with sof_example_iir_eq.m 04-Feb-2026 +# cd src/audio/eq_iir/tune; octave --no-window-system sof_example_iir_eq.m +Object.Base.data."iir_eq" { + bytes " + 0x53,0x4f,0x46,0x34,0x00,0x00,0x00,0x00, + 0x58,0x00,0x00,0x00,0x01,0xd0,0x01,0x03, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x58,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x3c,0xf9,0xb4,0xc0, + 0x79,0x05,0x4a,0x7f,0x92,0xf9,0xe5,0x1f, + 0xdc,0x0c,0x34,0xc0,0x92,0xf9,0xe5,0x1f, + 0xfd,0xff,0xff,0xff,0xb7,0x64,0x00,0x00" +} diff --git a/tools/topology/topology2/include/components/eqiir/highpass_20hz_16db_48khz.conf b/tools/topology/topology2/include/components/eqiir/highpass_20hz_16db_48khz.conf new file mode 100644 index 000000000000..5a57e789c335 --- /dev/null +++ b/tools/topology/topology2/include/components/eqiir/highpass_20hz_16db_48khz.conf @@ -0,0 +1,20 @@ +# 20 Hz second order high-pass, gain 16 dB, created with sof_example_iir_eq.m 04-Feb-2026 +# cd src/audio/eq_iir/tune; octave --no-window-system sof_example_iir_eq.m +Object.Base.data."iir_eq" { + bytes " + 0x53,0x4f,0x46,0x34,0x00,0x00,0x00,0x00, + 0x58,0x00,0x00,0x00,0x01,0xd0,0x01,0x03, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x58,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x4a,0x8c,0x3c,0xc0, + 0x04,0x57,0xc3,0x7f,0xc0,0x43,0x04,0x20, + 0x80,0x78,0xf7,0xbf,0xc0,0x43,0x04,0x20, + 0xfd,0xff,0xff,0xff,0xb7,0x64,0x00,0x00" +} diff --git a/tools/topology/topology2/include/components/eqiir/highpass_30hz_16db_16khz.conf b/tools/topology/topology2/include/components/eqiir/highpass_30hz_16db_16khz.conf new file mode 100644 index 000000000000..f1e2369164d4 --- /dev/null +++ b/tools/topology/topology2/include/components/eqiir/highpass_30hz_16db_16khz.conf @@ -0,0 +1,20 @@ +# 30 Hz second order high-pass, gain 16 dB, created with sof_example_iir_eq.m 04-Feb-2026 +# cd src/audio/eq_iir/tune; octave --no-window-system sof_example_iir_eq.m +Object.Base.data."iir_eq" { + bytes " + 0x53,0x4f,0x46,0x34,0x00,0x00,0x00,0x00, + 0x58,0x00,0x00,0x00,0x01,0xd0,0x01,0x03, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x58,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x98,0xb5,0x0e,0xc1, + 0x17,0x09,0xef,0x7e,0xbf,0x54,0xcf,0x1f, + 0x82,0x56,0x61,0xc0,0xbf,0x54,0xcf,0x1f, + 0xfd,0xff,0xff,0xff,0xb7,0x64,0x00,0x00" +} diff --git a/tools/topology/topology2/include/components/eqiir/highpass_30hz_16db_48khz.conf b/tools/topology/topology2/include/components/eqiir/highpass_30hz_16db_48khz.conf new file mode 100644 index 000000000000..490ac534dec7 --- /dev/null +++ b/tools/topology/topology2/include/components/eqiir/highpass_30hz_16db_48khz.conf @@ -0,0 +1,20 @@ +# 30 Hz second order high-pass, gain 16 dB, created with sof_example_iir_eq.m 04-Feb-2026 +# cd src/audio/eq_iir/tune; octave --no-window-system sof_example_iir_eq.m +Object.Base.data."iir_eq" { + bytes " + 0x53,0x4f,0x46,0x34,0x00,0x00,0x00,0x00, + 0x58,0x00,0x00,0x00,0x01,0xd0,0x01,0x03, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x58,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0xf1,0xbc,0x5a,0xc0, + 0x8f,0x02,0xa5,0x7f,0x83,0xae,0xfc,0x1f, + 0xfa,0xa2,0x06,0xc0,0x83,0xae,0xfc,0x1f, + 0xfd,0xff,0xff,0xff,0xb7,0x64,0x00,0x00" +} diff --git a/tools/topology/topology2/include/components/eqiir/highpass_40hz_16db_16khz.conf b/tools/topology/topology2/include/components/eqiir/highpass_40hz_16db_16khz.conf new file mode 100644 index 000000000000..54e8d8b37839 --- /dev/null +++ b/tools/topology/topology2/include/components/eqiir/highpass_40hz_16db_16khz.conf @@ -0,0 +1,20 @@ +# 40 Hz second order high-pass, gain 16 dB, created with sof_example_iir_eq.m 04-Feb-2026 +# cd src/audio/eq_iir/tune; octave --no-window-system sof_example_iir_eq.m +Object.Base.data."iir_eq" { + bytes " + 0x53,0x4f,0x46,0x34,0x00,0x00,0x00,0x00, + 0x58,0x00,0x00,0x00,0x01,0xd0,0x01,0x03, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x58,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0xba,0xf2,0x67,0xc1, + 0xc3,0x0d,0x94,0x7e,0xff,0xbf,0xb8,0x1f, + 0x03,0x80,0x8e,0xc0,0xff,0xbf,0xb8,0x1f, + 0xfd,0xff,0xff,0xff,0xb7,0x64,0x00,0x00" +} diff --git a/tools/topology/topology2/include/components/eqiir/highpass_40hz_16db_48khz.conf b/tools/topology/topology2/include/components/eqiir/highpass_40hz_16db_48khz.conf new file mode 100644 index 000000000000..1fbc3f74740a --- /dev/null +++ b/tools/topology/topology2/include/components/eqiir/highpass_40hz_16db_48khz.conf @@ -0,0 +1,20 @@ +# 40 Hz second order high-pass, gain 16 dB, created with sof_example_iir_eq.m 04-Feb-2026 +# cd src/audio/eq_iir/tune; octave --no-window-system sof_example_iir_eq.m +Object.Base.data."iir_eq" { + bytes " + 0x53,0x4f,0x46,0x34,0x00,0x00,0x00,0x00, + 0x58,0x00,0x00,0x00,0x01,0xd0,0x01,0x03, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x58,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x4c,0xdf,0x78,0xc0, + 0x23,0xae,0x86,0x7f,0x12,0x1b,0xf5,0x1f, + 0xdb,0xc9,0x15,0xc0,0x12,0x1b,0xf5,0x1f, + 0xfd,0xff,0xff,0xff,0xb7,0x64,0x00,0x00" +} diff --git a/tools/topology/topology2/include/components/eqiir/highpass_50hz_16db_16khz.conf b/tools/topology/topology2/include/components/eqiir/highpass_50hz_16db_16khz.conf new file mode 100644 index 000000000000..431a8f1c9335 --- /dev/null +++ b/tools/topology/topology2/include/components/eqiir/highpass_50hz_16db_16khz.conf @@ -0,0 +1,20 @@ +# 50 Hz second order high-pass, gain 16 dB, created with sof_example_iir_eq.m 04-Feb-2026 +# cd src/audio/eq_iir/tune; octave --no-window-system sof_example_iir_eq.m +Object.Base.data."iir_eq" { + bytes " + 0x53,0x4f,0x46,0x34,0x00,0x00,0x00,0x00, + 0x58,0x00,0x00,0x00,0x01,0xd0,0x01,0x03, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x58,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x57,0xb1,0xc0,0xc1, + 0xd2,0x13,0x39,0x7e,0x45,0x3b,0xa2,0x1f, + 0x76,0x89,0xbb,0xc0,0x45,0x3b,0xa2,0x1f, + 0xfd,0xff,0xff,0xff,0xb7,0x64,0x00,0x00" +} diff --git a/tools/topology/topology2/include/components/eqiir/highpass_50hz_16db_48khz.conf b/tools/topology/topology2/include/components/eqiir/highpass_50hz_16db_48khz.conf new file mode 100644 index 000000000000..29e57256981f --- /dev/null +++ b/tools/topology/topology2/include/components/eqiir/highpass_50hz_16db_48khz.conf @@ -0,0 +1,20 @@ +# 50 Hz second order high-pass, gain 16 dB, created with sof_example_iir_eq.m 04-Feb-2026 +# cd src/audio/eq_iir/tune; octave --no-window-system sof_example_iir_eq.m +Object.Base.data."iir_eq" { + bytes " + 0x53,0x4f,0x46,0x34,0x00,0x00,0x00,0x00, + 0x58,0x00,0x00,0x00,0x01,0xd0,0x01,0x03, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x58,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x63,0xf3,0x96,0xc0, + 0xc6,0x59,0x68,0x7f,0x6d,0x89,0xed,0x1f, + 0x27,0xed,0x24,0xc0,0x6d,0x89,0xed,0x1f, + 0xfd,0xff,0xff,0xff,0xb7,0x64,0x00,0x00" +} diff --git a/tools/topology/topology2/include/components/eqiir/xover_lr4_2000hz_hhll_48khz.conf b/tools/topology/topology2/include/components/eqiir/xover_lr4_2000hz_hhll_48khz.conf new file mode 100644 index 000000000000..8338a33decb2 --- /dev/null +++ b/tools/topology/topology2/include/components/eqiir/xover_lr4_2000hz_hhll_48khz.conf @@ -0,0 +1,35 @@ +# LR4 filter bank coefficients 08-Sep-2025 +# cd src/audio/eq_iir/tune; octave sof_example_lr4.m +Object.Base.data."IIR" { + bytes " + 0x53,0x4f,0x46,0x34,0x00,0x00,0x00,0x00, + 0xcc,0x00,0x00,0x00,0x01,0xd0,0x01,0x03, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xcc,0x00,0x00,0x00,0x04,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x2d,0x3a,0xcd,0xd3, + 0xc0,0xf5,0x82,0x68,0xb9,0x41,0x76,0x00, + 0x72,0x83,0xec,0x00,0xb9,0x41,0x76,0x00, + 0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x00, + 0x2d,0x3a,0xcd,0xd3,0xc0,0xf5,0x82,0x68, + 0xb9,0x41,0x76,0x00,0x72,0x83,0xec,0x00, + 0xb9,0x41,0x76,0x00,0xff,0xff,0xff,0xff, + 0x65,0x7f,0x00,0x00,0x02,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x7e,0x70,0xdd,0xd6, + 0xc0,0xf5,0x82,0x68,0xb6,0x9e,0x44,0x1a, + 0x94,0xc2,0x76,0xcb,0xb6,0x9e,0x44,0x1a, + 0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x00, + 0x7e,0x70,0xdd,0xd6,0xc0,0xf5,0x82,0x68, + 0xb6,0x9e,0x44,0x1a,0x94,0xc2,0x76,0xcb, + 0xb6,0x9e,0x44,0x1a,0xfe,0xff,0xff,0xff, + 0x92,0x41,0x00,0x00" +} diff --git a/tools/topology/topology2/include/components/eqiir/xover_lr4_2000hz_lhlh_48khz.conf b/tools/topology/topology2/include/components/eqiir/xover_lr4_2000hz_lhlh_48khz.conf new file mode 100644 index 000000000000..a7c04f3372a7 --- /dev/null +++ b/tools/topology/topology2/include/components/eqiir/xover_lr4_2000hz_lhlh_48khz.conf @@ -0,0 +1,35 @@ +# LR4 filter bank coefficients 08-Sep-2025 +# cd src/audio/eq_iir/tune; octave sof_example_lr4.m +Object.Base.data."IIR" { + bytes " + 0x53,0x4f,0x46,0x34,0x00,0x00,0x00,0x00, + 0xcc,0x00,0x00,0x00,0x01,0xd0,0x01,0x03, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xcc,0x00,0x00,0x00,0x04,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x2d,0x3a,0xcd,0xd3, + 0xc0,0xf5,0x82,0x68,0xb9,0x41,0x76,0x00, + 0x72,0x83,0xec,0x00,0xb9,0x41,0x76,0x00, + 0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x00, + 0x2d,0x3a,0xcd,0xd3,0xc0,0xf5,0x82,0x68, + 0xb9,0x41,0x76,0x00,0x72,0x83,0xec,0x00, + 0xb9,0x41,0x76,0x00,0xff,0xff,0xff,0xff, + 0x65,0x7f,0x00,0x00,0x02,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x7e,0x70,0xdd,0xd6, + 0xc0,0xf5,0x82,0x68,0xb6,0x9e,0x44,0x1a, + 0x94,0xc2,0x76,0xcb,0xb6,0x9e,0x44,0x1a, + 0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x00, + 0x7e,0x70,0xdd,0xd6,0xc0,0xf5,0x82,0x68, + 0xb6,0x9e,0x44,0x1a,0x94,0xc2,0x76,0xcb, + 0xb6,0x9e,0x44,0x1a,0xfe,0xff,0xff,0xff, + 0x92,0x41,0x00,0x00" +} diff --git a/tools/topology/topology2/include/components/eqiir/xover_lr4_2000hz_llhh_48khz.conf b/tools/topology/topology2/include/components/eqiir/xover_lr4_2000hz_llhh_48khz.conf new file mode 100644 index 000000000000..8612f617c9be --- /dev/null +++ b/tools/topology/topology2/include/components/eqiir/xover_lr4_2000hz_llhh_48khz.conf @@ -0,0 +1,35 @@ +# LR4 filter bank coefficients 08-Sep-2025 +# cd src/audio/eq_iir/tune; octave sof_example_lr4.m +Object.Base.data."IIR" { + bytes " + 0x53,0x4f,0x46,0x34,0x00,0x00,0x00,0x00, + 0xcc,0x00,0x00,0x00,0x01,0xd0,0x01,0x03, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xcc,0x00,0x00,0x00,0x04,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x2d,0x3a,0xcd,0xd3, + 0xc0,0xf5,0x82,0x68,0xb9,0x41,0x76,0x00, + 0x72,0x83,0xec,0x00,0xb9,0x41,0x76,0x00, + 0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x00, + 0x2d,0x3a,0xcd,0xd3,0xc0,0xf5,0x82,0x68, + 0xb9,0x41,0x76,0x00,0x72,0x83,0xec,0x00, + 0xb9,0x41,0x76,0x00,0xff,0xff,0xff,0xff, + 0x65,0x7f,0x00,0x00,0x02,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x7e,0x70,0xdd,0xd6, + 0xc0,0xf5,0x82,0x68,0xb6,0x9e,0x44,0x1a, + 0x94,0xc2,0x76,0xcb,0xb6,0x9e,0x44,0x1a, + 0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x00, + 0x7e,0x70,0xdd,0xd6,0xc0,0xf5,0x82,0x68, + 0xb6,0x9e,0x44,0x1a,0x94,0xc2,0x76,0xcb, + 0xb6,0x9e,0x44,0x1a,0xfe,0xff,0xff,0xff, + 0x92,0x41,0x00,0x00" +} diff --git a/tools/topology/topology2/include/components/mfcc/default.conf b/tools/topology/topology2/include/components/mfcc/default.conf index 1f9141886de9..42a6d6608b8b 100644 --- a/tools/topology/topology2/include/components/mfcc/default.conf +++ b/tools/topology/topology2/include/components/mfcc/default.conf @@ -1,9 +1,9 @@ -# Exported MFCC configuration 24-Jul-2024 -# cd tools/tune/mfcc; octave setup_mfcc.m +# Exported MFCC configuration 05-May-2026 +# cd src/audio/mfcc/tune; octave setup_mfcc.m Object.Base.data."mfcc_config" { bytes " 0x53,0x4f,0x46,0x34,0x00,0x00,0x00,0x00, - 0x68,0x00,0x00,0x00,0x00,0xa0,0x01,0x03, + 0x68,0x00,0x00,0x00,0x01,0xd0,0x01,0x03, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x68,0x00,0x00,0x00,0x00,0x00,0x00,0x00, @@ -14,7 +14,7 @@ Object.Base.data."mfcc_config" { 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x02,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0xc3,0x35,0x00,0x2c,0xff,0xff,0x00,0x00, + 0xc3,0x35,0x00,0x2c,0x00,0x00,0x00,0x00, 0x90,0x01,0xa0,0x00,0x00,0x00,0x14,0x00, 0x0d,0x00,0x17,0x00,0x00,0x00,0x00,0x64, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01, diff --git a/tools/topology/topology2/include/components/mfcc/mel80.conf b/tools/topology/topology2/include/components/mfcc/mel80.conf new file mode 100644 index 000000000000..04aa2a15c660 --- /dev/null +++ b/tools/topology/topology2/include/components/mfcc/mel80.conf @@ -0,0 +1,22 @@ +# Exported MFCC configuration 05-May-2026 +# cd src/audio/mfcc/tune; octave setup_mfcc.m +Object.Base.data."mfcc_config" { + bytes " + 0x53,0x4f,0x46,0x34,0x00,0x00,0x00,0x00, + 0x68,0x00,0x00,0x00,0x01,0xd0,0x01,0x03, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x68,0x00,0x00,0x00,0x00,0x02,0x00,0x04, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x80,0x3e,0x00,0x00, + 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x90,0x01,0xa0,0x00,0x40,0x1f,0x00,0x00, + 0x00,0x00,0x50,0x00,0x00,0x00,0x00,0x04, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x01,0x01,0x00,0x00,0x01,0x00,0x00" +} diff --git a/tools/topology/topology2/include/components/micsel/downmix_71_to_stereo.conf b/tools/topology/topology2/include/components/micsel/downmix_71_to_stereo.conf deleted file mode 100644 index df6af38dde34..000000000000 --- a/tools/topology/topology2/include/components/micsel/downmix_71_to_stereo.conf +++ /dev/null @@ -1,26 +0,0 @@ -# Exported with script sof_selector_blobs.m 04-Jun-2025 -# cd tools/tune/selector; octave sof_selector_blobs.m -Object.Base.data."selector_config" { - bytes " - 0x53,0x4f,0x46,0x34,0x00,0x00,0x00,0x00, - 0x84,0x00,0x00,0x00,0x01,0xd0,0x01,0x03, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x48,0x01,0x00,0x00, - 0xe8,0x00,0x00,0x00,0xe8,0x00,0x00,0x00, - 0xe8,0x00,0x00,0x00,0x00,0x00,0x48,0x01, - 0xe8,0x00,0x00,0x00,0x00,0x00,0xe8,0x00, - 0x00,0x00,0xe8,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00" -} diff --git a/tools/topology/topology2/include/components/micsel/stereo_endpoint_playback_updownmix.conf b/tools/topology/topology2/include/components/micsel/stereo_endpoint_playback_updownmix.conf new file mode 100644 index 000000000000..70500ef0e0fa --- /dev/null +++ b/tools/topology/topology2/include/components/micsel/stereo_endpoint_playback_updownmix.conf @@ -0,0 +1,75 @@ +# Exported with script sof_selector_blobs.m 10-Mar-2026 +# cd tools/tune/selector; octave sof_selector_blobs.m +Object.Base.data."selector_config" { + bytes " + 0x53,0x4f,0x46,0x34,0x00,0x00,0x00,0x00, + 0x10,0x02,0x00,0x00,0x01,0xd0,0x01,0x03, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x08,0x0c,0x0c,0x00,0x04,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x04,0x01,0x02,0x00,0x01, + 0xd5,0x02,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xd5,0x02,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x06,0x02,0x08,0x01,0x00,0x01,0x00,0x00, + 0xb5,0x00,0x96,0x01,0xb5,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01, + 0xb5,0x00,0x96,0x01,0x00,0x00,0xb5,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x08,0x02,0x0c,0x01, + 0xda,0x00,0x00,0x00,0x9a,0x00,0x59,0x01, + 0x9a,0x00,0x00,0x00,0x9a,0x00,0x00,0x00, + 0x00,0x00,0xda,0x00,0x9a,0x00,0x59,0x01, + 0x00,0x00,0x9a,0x00,0x00,0x00,0x9a,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00" +} diff --git a/tools/topology/topology2/include/components/micsel/downmix_51_to_mono.conf b/tools/topology/topology2/include/components/micsel/xover_selector_lr_to_llrr.conf similarity index 78% rename from tools/topology/topology2/include/components/micsel/downmix_51_to_mono.conf rename to tools/topology/topology2/include/components/micsel/xover_selector_lr_to_llrr.conf index cd1f65b86454..4b4be2b34840 100644 --- a/tools/topology/topology2/include/components/micsel/downmix_51_to_mono.conf +++ b/tools/topology/topology2/include/components/micsel/xover_selector_lr_to_llrr.conf @@ -1,4 +1,4 @@ -# Exported with script sof_selector_blobs.m 04-Jun-2025 +# Exported with script sof_selector_blobs.m 08-Sep-2025 # cd tools/tune/selector; octave sof_selector_blobs.m Object.Base.data."selector_config" { bytes " @@ -6,13 +6,13 @@ Object.Base.data."selector_config" { 0x84,0x00,0x00,0x00,0x01,0xd0,0x01,0x03, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0xd4,0x00,0xd4,0x00, - 0x2c,0x01,0x00,0x00,0x96,0x00,0x96,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, diff --git a/tools/topology/topology2/include/components/micsel/downmix_51_to_stereo.conf b/tools/topology/topology2/include/components/micsel/xover_selector_lr_to_lrlr.conf similarity index 78% rename from tools/topology/topology2/include/components/micsel/downmix_51_to_stereo.conf rename to tools/topology/topology2/include/components/micsel/xover_selector_lr_to_lrlr.conf index 3f7b32a251d8..70489a0a0040 100644 --- a/tools/topology/topology2/include/components/micsel/downmix_51_to_stereo.conf +++ b/tools/topology/topology2/include/components/micsel/xover_selector_lr_to_lrlr.conf @@ -1,4 +1,4 @@ -# Exported with script sof_selector_blobs.m 04-Jun-2025 +# Exported with script sof_selector_blobs.m 08-Sep-2025 # cd tools/tune/selector; octave sof_selector_blobs.m Object.Base.data."selector_config" { bytes " @@ -6,13 +6,13 @@ Object.Base.data."selector_config" { 0x84,0x00,0x00,0x00,0x01,0xd0,0x01,0x03, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0xa8,0x01,0x00,0x00, - 0x2c,0x01,0x00,0x00,0x2c,0x01,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0xa8,0x01, - 0x2c,0x01,0x00,0x00,0x00,0x00,0x2c,0x01, + 0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, diff --git a/tools/topology/topology2/include/components/micsel/downmix_71_to_mono.conf b/tools/topology/topology2/include/components/micsel/xover_selector_lr_to_rrll.conf similarity index 78% rename from tools/topology/topology2/include/components/micsel/downmix_71_to_mono.conf rename to tools/topology/topology2/include/components/micsel/xover_selector_lr_to_rrll.conf index 1e67d36bf839..d88ba2063cd6 100644 --- a/tools/topology/topology2/include/components/micsel/downmix_71_to_mono.conf +++ b/tools/topology/topology2/include/components/micsel/xover_selector_lr_to_rrll.conf @@ -1,4 +1,4 @@ -# Exported with script sof_selector_blobs.m 04-Jun-2025 +# Exported with script sof_selector_blobs.m 08-Sep-2025 # cd tools/tune/selector; octave sof_selector_blobs.m Object.Base.data."selector_config" { bytes " @@ -6,13 +6,13 @@ Object.Base.data."selector_config" { 0x84,0x00,0x00,0x00,0x01,0xd0,0x01,0x03, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0xa4,0x00,0xa4,0x00, - 0xe8,0x00,0x00,0x00,0x74,0x00,0x74,0x00, - 0x74,0x00,0x74,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, diff --git a/tools/topology/topology2/include/components/pipeline.conf b/tools/topology/topology2/include/components/pipeline.conf index 3a69354e30d2..caf972250f3e 100644 --- a/tools/topology/topology2/include/components/pipeline.conf +++ b/tools/topology/topology2/include/components/pipeline.conf @@ -102,6 +102,32 @@ Class.Widget."pipeline" { } } + DefineAttribute."direction" { + type "string" + token_ref "scheduler.word" + constraints { + !valid_values [ + "playback" + "capture" + ] + !tuple_values [ + 0 + 1 + ] + } + } + + DefineAttribute."direction_valid" { + type "string" + token_ref "scheduler.bool" + constraints { + !valid_values [ + "true" + "false" + ] + } + } + # Skip setting up the pipeline in the DSP in the case of chained DMA mode DefineAttribute."use_chain_dma" { type "string" @@ -144,4 +170,5 @@ Class.Widget."pipeline" { type "scheduler" no_pm "true" kcps $KCPS_PIPELINE_DEFAULT + direction_valid "true" } diff --git a/tools/topology/topology2/include/components/siggen.conf b/tools/topology/topology2/include/components/siggen.conf new file mode 100644 index 000000000000..ba806f8ae7dc --- /dev/null +++ b/tools/topology/topology2/include/components/siggen.conf @@ -0,0 +1,79 @@ +# +# Signal generator +# +# A generic siggen widget. All attributes defined herein are namespaced +# by alsatplg to "Object.Widget.siggen.N.attribute_name" +# +# Usage: this component can be used by declaring in a parent object. i.e. +# +# Object.Widget.siggen."N" { +# index 1 +# format s32le +# no_pm "true" +# } +# +# Where N is the unique instance number for siggen widget in the same alsaconf node. + +Class.Widget."siggen" { + # + # Pipeline ID for the siggen widget object + # + DefineAttribute."index" {} + + # + # siggen object instance + # + DefineAttribute."instance" {} + + #include common component definition + <include/components/widget-common.conf> + + # Attribute categories + attributes { + # + # The siggen widget name would be constructed using the index and instance attributes. + # For ex: "siggen.1.1" or "siggen.10.2" etc. + # + !constructor [ + "index" + "instance" + ] + + # + # immutable attributes cannot be modified in the object instance + # + !immutable [ + "uuid" + "type" + ] + + # + # deprecated attributes should not be added in the object instance + # + !deprecated [ + "preload_count" + ] + + # + # siggen widget objects instantiated within the same alsaconf node must have unique + # instance attribute + # + unique "instance" + } + + # siggen only supports one output pin with 32-bit audio format + num_output_pins 1 + num_output_audio_formats 1 + + Object.Base.output_audio_format [ + { + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + + # Default attribute values for siggen widget + type "effect" + uuid "94:f8:e3:04:5c:2c:2e:4f:8d:c1:69:4e:ea:ab:53:fa" + no_pm "true" +} diff --git a/tools/topology/topology2/include/components/stft_process.conf b/tools/topology/topology2/include/components/stft_process.conf new file mode 100644 index 000000000000..7e78352ddc4e --- /dev/null +++ b/tools/topology/topology2/include/components/stft_process.conf @@ -0,0 +1,64 @@ +# +# +# A STFT_PROCESS component for SOF. All attributes defined herein are namespaced +# by alsatplg to "Object.Widget.stft_process.attribute_name" +# +# Usage: this component can be used by declaring in the parent object. i.e. +# +# Object.Widget.stft_process."N" { +# index 1 +# } +# } + +# +# Where M is pipeline ID and N is a unique integer in the parent object. + +Class.Widget."stft_process" { + # + # Pipeline ID + # + DefineAttribute."index" { + type "integer" + } + + # + # Unique instance for STFT_PROCESS widget + # + DefineAttribute."instance" { + type "integer" + } + + # Include common widget attributes definition + <include/components/widget-common.conf> + + attributes { + !constructor [ + "index" + "instance" + ] + !mandatory [ + "num_input_pins" + "num_output_pins" + "num_input_audio_formats" + "num_output_audio_formats" + ] + + !immutable [ + "uuid" + "type" + ] + !deprecated [ + "preload_count" + ] + unique "instance" + } + + # + # Default attributes for stft_process + # + uuid "a6:6e:11:0d:50:91:de:46:98:b8:b2:b3:a7:91:da:29" + type "effect" + no_pm "true" + num_input_pins 1 + num_output_pins 1 +} diff --git a/tools/topology/topology2/include/components/stft_process/hann_1024_256.conf b/tools/topology/topology2/include/components/stft_process/hann_1024_256.conf new file mode 100644 index 000000000000..761398162213 --- /dev/null +++ b/tools/topology/topology2/include/components/stft_process/hann_1024_256.conf @@ -0,0 +1,17 @@ +# Exported STFT_PROCESS configuration 22-Dec-2025 +# cd tools/tune/stft_process; octave setup_stft_process.m +Object.Base.data."stft_process_config" { + bytes " + 0x53,0x4f,0x46,0x34,0x00,0x00,0x00,0x00, + 0x40,0x00,0x00,0x00,0x01,0xd0,0x01,0x03, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x80,0xbb,0x00,0x00, + 0x01,0xb0,0x6a,0x55,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x04,0x00,0x01,0x00,0x00, + 0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00" +} diff --git a/tools/topology/topology2/include/components/stft_process/hann_1536_240.conf b/tools/topology/topology2/include/components/stft_process/hann_1536_240.conf new file mode 100644 index 000000000000..497693eefde8 --- /dev/null +++ b/tools/topology/topology2/include/components/stft_process/hann_1536_240.conf @@ -0,0 +1,17 @@ +# Exported STFT_PROCESS configuration 22-Dec-2025 +# cd tools/tune/stft_process; octave setup_stft_process.m +Object.Base.data."stft_process_config" { + bytes " + 0x53,0x4f,0x46,0x34,0x00,0x00,0x00,0x00, + 0x40,0x00,0x00,0x00,0x01,0xd0,0x01,0x03, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x80,0xbb,0x00,0x00, + 0x5f,0x3a,0x5e,0x35,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x06,0xf0,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00" +} diff --git a/tools/topology/topology2/include/components/stft_process/hann_192_48.conf b/tools/topology/topology2/include/components/stft_process/hann_192_48.conf new file mode 100644 index 000000000000..4ed5586b1f54 --- /dev/null +++ b/tools/topology/topology2/include/components/stft_process/hann_192_48.conf @@ -0,0 +1,17 @@ +# Exported STFT_PROCESS configuration 22-Dec-2025 +# cd tools/tune/stft_process; octave setup_stft_process.m +Object.Base.data."stft_process_config" { + bytes " + 0x53,0x4f,0x46,0x34,0x00,0x00,0x00,0x00, + 0x40,0x00,0x00,0x00,0x01,0xd0,0x01,0x03, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x80,0xbb,0x00,0x00, + 0xf1,0xb4,0xc7,0x55,0x00,0x00,0x00,0x00, + 0x00,0x00,0xc0,0x00,0x30,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00" +} diff --git a/tools/topology/topology2/include/components/stft_process/hann_512_128.conf b/tools/topology/topology2/include/components/stft_process/hann_512_128.conf new file mode 100644 index 000000000000..61f9c20b0cdd --- /dev/null +++ b/tools/topology/topology2/include/components/stft_process/hann_512_128.conf @@ -0,0 +1,17 @@ +# Exported STFT_PROCESS configuration 22-Dec-2025 +# cd tools/tune/stft_process; octave setup_stft_process.m +Object.Base.data."stft_process_config" { + bytes " + 0x53,0x4f,0x46,0x34,0x00,0x00,0x00,0x00, + 0x40,0x00,0x00,0x00,0x01,0xd0,0x01,0x03, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x80,0xbb,0x00,0x00, + 0x60,0x15,0x80,0x55,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x02,0x80,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00" +} diff --git a/tools/topology/topology2/include/components/stft_process/hann_768_120.conf b/tools/topology/topology2/include/components/stft_process/hann_768_120.conf new file mode 100644 index 000000000000..23aa3e4da281 --- /dev/null +++ b/tools/topology/topology2/include/components/stft_process/hann_768_120.conf @@ -0,0 +1,17 @@ +# Exported STFT_PROCESS configuration 22-Dec-2025 +# cd tools/tune/stft_process; octave setup_stft_process.m +Object.Base.data."stft_process_config" { + bytes " + 0x53,0x4f,0x46,0x34,0x00,0x00,0x00,0x00, + 0x40,0x00,0x00,0x00,0x01,0xd0,0x01,0x03, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x80,0xbb,0x00,0x00, + 0x61,0x22,0x67,0x35,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x03,0x78,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00" +} diff --git a/tools/topology/topology2/include/components/widget-common.conf b/tools/topology/topology2/include/components/widget-common.conf index 851e78ae1721..43ac5691c778 100644 --- a/tools/topology/topology2/include/components/widget-common.conf +++ b/tools/topology/topology2/include/components/widget-common.conf @@ -149,9 +149,33 @@ DefineAttribute."stack_bytes_requirement" { token_ref "comp.word" } -## Heap size requirement in bytes for this component. Zero indicates default heap size. -DefineAttribute."heap_bytes_requirement" { +## Interim Heap size requirement in bytes for this component. +## Used for resources that may change in size or be freed during the lifetime of the component. +## In practice this means anything allocated outside module's init() call-back. +DefineAttribute."interim_heap_bytes_requirement" { # Token set reference name and type token_ref "comp.word" } +## Lifetime heap memory allocation requirement in bytes for this component. +## This token's value indicates the amount of allocated memory needed at component init phase +## and used over the lifetime of the component until the component is destroyed. +## In practice this means anything allocated in init() call-back. +DefineAttribute."lifetime_heap_bytes_requirement" { + # Token set reference name and type + token_ref "comp.word" +} + +## Shared memory allocation requirement in bytes for this component. +## This token's value indicates the amount of shared memory the component may need to allocated. +## Shared memory may be shared to other components. +DefineAttribute."shared_bytes_requirement" { + # Token set reference name and type + token_ref "comp.word" +} + +# These default values are here until we have measured module specific numbers +stack_bytes_requirement 8192 +interim_heap_bytes_requirement 4096 +lifetime_heap_bytes_requirement 16384 +shared_bytes_requirement 4096 \ No newline at end of file diff --git a/tools/topology/topology2/include/formats/compr_input_audio_formats.conf b/tools/topology/topology2/include/formats/compr_input_audio_formats.conf new file mode 100644 index 000000000000..8d5694400988 --- /dev/null +++ b/tools/topology/topology2/include/formats/compr_input_audio_formats.conf @@ -0,0 +1,107 @@ + # All input formats for e.g. host-copier, decoder, + # and module-copier + num_input_audio_formats 40 + CombineArrays.Object.Base.input_audio_format [ + { + in_channels [ 1 ] + in_rate [ + 8000 + 44100 + 48000 + 96000 + 192000 + ] + in_bit_depth [ 8 ] + in_valid_bit_depth [ 8 ] + in_sample_type [ $SAMPLE_TYPE_UNSIGNED_INTEGER ] + in_ch_map [ $CHANNEL_MAP_MONO ] + in_ch_cfg [ $CHANNEL_CONFIG_MONO ] + } + { + in_channels [ 1 ] + in_rate [ + 8000 + 44100 + 48000 + 96000 + 192000 + ] + in_bit_depth [ 16 ] + in_valid_bit_depth [ 16 ] + in_ch_map [ $CHANNEL_MAP_MONO ] + in_ch_cfg [ $CHANNEL_CONFIG_MONO ] + } + { + in_channels [ 1 ] + in_rate [ + 8000 + 44100 + 48000 + 96000 + 192000 + ] + in_bit_depth [ 32 ] + in_valid_bit_depth [ 24 ] + in_ch_map [ $CHANNEL_MAP_MONO ] + in_ch_cfg [ $CHANNEL_CONFIG_MONO ] + } + { + in_channels [ 1 ] + in_rate [ + 8000 + 44100 + 48000 + 96000 + 192000 + ] + in_bit_depth [ 32 ] + in_valid_bit_depth [ 32 ] + in_ch_map [ $CHANNEL_MAP_MONO ] + in_ch_cfg [ $CHANNEL_CONFIG_MONO ] + } + { + in_rate [ + 8000 + 44100 + 48000 + 96000 + 192000 + ] + in_bit_depth [ 8 ] + in_valid_bit_depth [ 8 ] + in_sample_type [ $SAMPLE_TYPE_UNSIGNED_INTEGER ] + } + { + in_rate [ + 8000 + 44100 + 48000 + 96000 + 192000 + ] + in_bit_depth [ 16 ] + in_valid_bit_depth [ 16 ] + } + { + in_rate [ + 8000 + 44100 + 48000 + 96000 + 192000 + ] + in_bit_depth [ 32 ] + in_valid_bit_depth [ 24 ] + } + { + in_rate [ + 8000 + 44100 + 48000 + 96000 + 192000 + ] + in_bit_depth [ 32 ] + in_valid_bit_depth [ 32 ] + } + ] diff --git a/tools/topology/topology2/include/formats/compr_input_audio_formats_s32_48k.conf b/tools/topology/topology2/include/formats/compr_input_audio_formats_s32_48k.conf new file mode 100644 index 000000000000..9ea5b19a2f16 --- /dev/null +++ b/tools/topology/topology2/include/formats/compr_input_audio_formats_s32_48k.conf @@ -0,0 +1,29 @@ + # Input formats for e.g. micsel + num_input_audio_formats 10 + CombineArrays.Object.Base.input_audio_format [ + { + in_channels [ 1 ] + in_rate [ + 8000 + 44100 + 48000 + 96000 + 192000 + ] + in_bit_depth [ 32 ] + in_valid_bit_depth [ 32 ] + in_ch_map [ $CHANNEL_MAP_MONO ] + in_ch_cfg [ $CHANNEL_CONFIG_MONO ] + } + { + in_rate [ + 8000 + 44100 + 48000 + 96000 + 192000 + ] + in_bit_depth [ 32 ] + in_valid_bit_depth [ 32 ] + } + ] diff --git a/tools/topology/topology2/include/formats/compr_input_output_formats_src_s32_to_48k.conf b/tools/topology/topology2/include/formats/compr_input_output_formats_src_s32_to_48k.conf new file mode 100644 index 000000000000..836855126233 --- /dev/null +++ b/tools/topology/topology2/include/formats/compr_input_output_formats_src_s32_to_48k.conf @@ -0,0 +1,65 @@ + # Input and output audio formats for SRC + num_input_audio_formats 28 + CombineArrays.Object.Base.input_audio_format [ + { + in_channels [ 1 ] + in_rate [ + 8000 + 11025 + 12000 + 16000 + 22050 + 24000 + 32000 + 44100 + 48000 + 64000 + 88200 + 96000 + 176400 + 192000 + ] + in_bit_depth [ 32 ] + in_valid_bit_depth [ 32 ] + in_ch_map [ $CHANNEL_MAP_MONO ] + in_ch_cfg [ $CHANNEL_CONFIG_MONO ] + } + { + in_rate [ + 8000 + 11025 + 12000 + 16000 + 22050 + 24000 + 32000 + 44100 + 48000 + 64000 + 88200 + 96000 + 176400 + 192000 + ] + in_bit_depth [ 32 ] + in_valid_bit_depth [ 32 ] + } + ] + + num_output_audio_formats 2 + Object.Base.output_audio_format [ + { + out_channels 1 + out_rate 48000 + out_bit_depth 32 + out_valid_bit_depth 32 + out_ch_map $CHANNEL_MAP_MONO + out_ch_cfg $CHANNEL_CONFIG_MONO + } + { + out_channels 2 + out_rate 48000 + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] diff --git a/tools/topology/topology2/include/formats/compr_output_audio_formats.conf b/tools/topology/topology2/include/formats/compr_output_audio_formats.conf new file mode 100644 index 000000000000..98d11fa51cbc --- /dev/null +++ b/tools/topology/topology2/include/formats/compr_output_audio_formats.conf @@ -0,0 +1,106 @@ + # Output audio formats for e.g. host-copier and decoder + num_output_audio_formats 40 + CombineArrays.Object.Base.output_audio_format [ + { + out_channels [ 1 ] + out_rate [ + 8000 + 44100 + 48000 + 96000 + 192000 + ] + out_bit_depth [ 8 ] + out_valid_bit_depth [ 8 ] + out_sample_type [ $SAMPLE_TYPE_UNSIGNED_INTEGER ] + out_ch_map [ $CHANNEL_MAP_MONO ] + out_ch_cfg [ $CHANNEL_CONFIG_MONO ] + } + { + out_channels [ 1 ] + out_rate [ + 8000 + 44100 + 48000 + 96000 + 192000 + ] + out_bit_depth [ 16 ] + out_valid_bit_depth [ 16 ] + out_ch_map [ $CHANNEL_MAP_MONO ] + out_ch_cfg [ $CHANNEL_CONFIG_MONO ] + } + { + out_channels [ 1 ] + out_rate [ + 8000 + 44100 + 48000 + 96000 + 192000 + ] + out_bit_depth [ 32 ] + out_valid_bit_depth [ 24 ] + out_ch_map [ $CHANNEL_MAP_MONO ] + out_ch_cfg [ $CHANNEL_CONFIG_MONO ] + } + { + out_channels [ 1 ] + out_rate [ + 8000 + 44100 + 48000 + 96000 + 192000 + ] + out_bit_depth [ 32 ] + out_valid_bit_depth [ 32 ] + out_ch_map [ $CHANNEL_MAP_MONO ] + out_ch_cfg [ $CHANNEL_CONFIG_MONO ] + } + { + out_rate [ + 8000 + 44100 + 48000 + 96000 + 192000 + ] + out_bit_depth [ 8 ] + out_valid_bit_depth [ 8 ] + out_sample_type [ $SAMPLE_TYPE_UNSIGNED_INTEGER ] + } + { + out_rate [ + 8000 + 44100 + 48000 + 96000 + 192000 + ] + out_bit_depth [ 16 ] + out_valid_bit_depth [ 16 ] + } + { + out_rate [ + 8000 + 44100 + 48000 + 96000 + 192000 + ] + out_bit_depth [ 32 ] + out_valid_bit_depth [ 24 ] + } + { + out_rate [ + 8000 + 44100 + 48000 + 96000 + 192000 + ] + out_bit_depth [ 32 ] + out_valid_bit_depth [ 32 ] + } + ] diff --git a/tools/topology/topology2/include/formats/compr_output_audio_formats_s32.conf b/tools/topology/topology2/include/formats/compr_output_audio_formats_s32.conf new file mode 100644 index 000000000000..e377afa38687 --- /dev/null +++ b/tools/topology/topology2/include/formats/compr_output_audio_formats_s32.conf @@ -0,0 +1,29 @@ + # Output audio formats e.g. module-copier + num_output_audio_formats 10 + CombineArrays.Object.Base.output_audio_format [ + { + out_channels [ 1 ] + out_rate [ + 8000 + 44100 + 48000 + 96000 + 192000 + ] + out_bit_depth [ 32 ] + out_valid_bit_depth [ 32 ] + out_ch_map [ $CHANNEL_MAP_MONO ] + out_ch_cfg [ $CHANNEL_CONFIG_MONO ] + } + { + out_rate [ + 8000 + 44100 + 48000 + 96000 + 192000 + ] + out_bit_depth [ 32 ] + out_valid_bit_depth [ 32 ] + } + ] diff --git a/tools/topology/topology2/include/formats/compr_output_audio_formats_s32_48k_stereo.conf b/tools/topology/topology2/include/formats/compr_output_audio_formats_s32_48k_stereo.conf new file mode 100644 index 000000000000..13ebd43e389d --- /dev/null +++ b/tools/topology/topology2/include/formats/compr_output_audio_formats_s32_48k_stereo.conf @@ -0,0 +1,10 @@ + # Micsel single format output + num_output_audio_formats 1 + Object.Base.output_audio_format [ + { + out_channels 2 + out_rate 48000 + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] diff --git a/tools/topology/topology2/include/pipelines/cavs/compr-playback.conf b/tools/topology/topology2/include/pipelines/cavs/compr-playback.conf new file mode 100644 index 000000000000..a9cbdde992aa --- /dev/null +++ b/tools/topology/topology2/include/pipelines/cavs/compr-playback.conf @@ -0,0 +1,103 @@ +# +# FE playback pipeline: compr-playback +# +# All attributes defined herein are namespaced +# by alsatplg to "Object.Pipeline.compr-playback.N.attribute_name" +# +# Usage: compr-playback pipeline object can be instantiated as: +# +# Object.Pipeline.compr-playback."N" { +# period 1000 +# time_domain "timer" +# } +# +# Where N is the unique pipeline ID within the same alsaconf node. +# + +<include/common/input_audio_format.conf> +<include/common/output_audio_format.conf> +<include/components/host-copier.conf> +<include/components/module-copier.conf> +<include/components/mixin.conf> +<include/components/pipeline.conf> +<include/components/gain.conf> +<include/components/decoder.conf> +<include/components/micsel.conf> +<include/components/src.conf> + +Class.Pipeline."compr-playback" { + + <include/pipelines/pipeline-common.conf> + + attributes { + !constructor [ + "index" + ] + + !immutable [ + "direction" + ] + + # + # compr-playback objects instantiated within the same alsaconf node must have + # unique pipeline_id attribute + # + unique "instance" + } + + Object.Widget { + host-copier."1" { + type "aif_in" + node_type $HDA_HOST_OUTPUT_CLASS + num_output_pins 1 + deep_buffer_dma_ms $COMPR_DEEPBUFFER_MS + } + + decoder."1" { + scheduler_domain "DP" + } + + module-copier."2" {} + + src."1" {} + + micsel."1" {} + + gain."1" {} + + mixin."1" {} + + pipeline."1" { + priority 0 + # enable lp mode + lp_mode 1 + } + } + + Object.Base.route [ + { + source decoder.$index.1 + sink module-copier.$index.2 + } + { + source module-copier.$index.2 + sink src.$index.1 + } + { + source src.$index.1 + sink micsel.$index.1 + } + { + source micsel.$index.1 + sink gain.$index.1 + } + { + source gain.$index.1 + sink mixin.$index.1 + } + ] + + direction "playback" + dynamic_pipeline 1 + time_domain "timer" +} diff --git a/tools/topology/topology2/include/pipelines/cavs/deepbuffer-capture.conf b/tools/topology/topology2/include/pipelines/cavs/deepbuffer-capture.conf new file mode 100644 index 000000000000..4fa8c82743aa --- /dev/null +++ b/tools/topology/topology2/include/pipelines/cavs/deepbuffer-capture.conf @@ -0,0 +1,111 @@ +# +# FE capture pipeline: deepbuffer-capture +# +# All attributes defined herein are namespaced +# by alsatplg to "Object.Pipeline.deepbuffer-capture.N.attribute_name" +# +# Usage: deepbuffer-capture pipeline object can be instantiated as: +# +# Object.Pipeline.deepbuffer-capture."N" { +# period 1000 +# time_domain "timer" +# } +# +# Where N is the unique pipeline ID within the same alsaconf node. +# + +<include/common/input_audio_format.conf> +<include/common/output_audio_format.conf> +<include/components/host-copier.conf> +<include/components/pipeline.conf> + +Class.Pipeline."deepbuffer-capture" { + + <include/pipelines/pipeline-common.conf> + + attributes { + !constructor [ + "index" + ] + + !immutable [ + "direction" + ] + + # + # deepbuffer-capture objects instantiated within the same alsaconf node must have + # unique pipeline_id attribute + # + unique "instance" + } + + Object.Widget { + host-copier."1" { + type "aif_out" + node_type $HDA_HOST_INPUT_CLASS + deep_buffer_dma_ms $DEEPBUFFER_FW_DMA_MS + num_input_pins 1 + num_input_audio_formats 2 + num_output_audio_formats 6 + # Input sample format is always 32-bit for capture + Object.Base.input_audio_format [ + { + in_bit_depth 32 + in_valid_bit_depth 32 + } + { + in_bit_depth 32 + in_valid_bit_depth 32 + in_channels 4 + in_ch_cfg $CHANNEL_CONFIG_3_POINT_1 + in_ch_map $CHANNEL_MAP_3_POINT_1 + } + ] + Object.Base.output_audio_format [ + { + out_bit_depth 32 + out_valid_bit_depth 24 + } + { + out_bit_depth 32 + out_valid_bit_depth 32 + } + { + out_bit_depth 16 + out_valid_bit_depth 16 + } + { + out_bit_depth 32 + out_valid_bit_depth 24 + out_channels 4 + out_ch_cfg $CHANNEL_CONFIG_3_POINT_1 + out_ch_map $CHANNEL_MAP_3_POINT_1 + } + { + out_bit_depth 32 + out_valid_bit_depth 32 + out_channels 4 + out_ch_cfg $CHANNEL_CONFIG_3_POINT_1 + out_ch_map $CHANNEL_MAP_3_POINT_1 + } + { + out_bit_depth 16 + out_valid_bit_depth 16 + out_channels 4 + out_ch_cfg $CHANNEL_CONFIG_3_POINT_1 + out_ch_map $CHANNEL_MAP_3_POINT_1 + } + ] + } + + pipeline."1" { + priority 0 + # enable lp mode + lp_mode 1 + } + } + + direction "capture" + dynamic_pipeline 1 + time_domain "timer" +} diff --git a/tools/topology/topology2/include/pipelines/cavs/host-gateway-src-mfcc-capture.conf b/tools/topology/topology2/include/pipelines/cavs/host-gateway-src-mfcc-capture.conf new file mode 100644 index 000000000000..793f71b883ab --- /dev/null +++ b/tools/topology/topology2/include/pipelines/cavs/host-gateway-src-mfcc-capture.conf @@ -0,0 +1,134 @@ +# +# SRC-MFCC capture pipeline +# +# This class provides host pipeline for capture with MFCC audio features input. +# All attributes defined herein are namespaced by alsatplg to +# "Object.Pipeline.host-gateway-src-mfcc-capture.N.attribute_name". +# +# Usage: host-gateway-src-mfcc-capture pipeline object can be instantiated as: +# +# Object.Pipeline.host-gateway-src-mfcc-capture."N" { +# period 1000 +# time_domain "timer" +# } +# +# Where N is the unique pipeline ID within the same alsaconf node. +# + +<include/common/input_audio_format.conf> +<include/common/output_audio_format.conf> +<include/components/pipeline.conf> +<include/components/host-copier.conf> +<include/components/src.conf> +<include/components/mfcc.conf> + +Class.Pipeline."host-gateway-src-mfcc-capture" { + + <include/pipelines/pipeline-common.conf> + + attributes { + !constructor [ + "index" + ] + + # + # host-gateway-src-mfcc-capture objects instantiated within the same alsaconf + # node must have unique pipeline_id attribute + # + unique "instance" + } + + Object.Widget { + src."1" { + num_input_pins 1 + num_output_pins 1 + num_input_audio_formats 3 + num_output_audio_formats 1 + Object.Base.input_audio_format [ + { + in_bit_depth 32 + in_valid_bit_depth 32 + in_rate 48000 + } + { + in_bit_depth 32 + in_valid_bit_depth 32 + in_rate 96000 + } + { + in_bit_depth 32 + in_valid_bit_depth 32 + in_rate 192000 + } + ] + Object.Base.output_audio_format [ + { + out_bit_depth 32 + out_valid_bit_depth 32 + out_rate 16000 + } + ] + } + + mfcc."1" { + num_input_audio_formats 1 + num_output_audio_formats 1 + Object.Base.input_audio_format [ + { + in_bit_depth 32 + in_valid_bit_depth 32 + in_rate 16000 + } + ] + Object.Base.output_audio_format [ + { + out_bit_depth 32 + out_valid_bit_depth 32 + out_rate 16000 + } + ] + } + + host-copier."1" { + type "aif_out" + node_type $HDA_HOST_INPUT_CLASS + num_input_pins 1 + num_output_pins 1 + num_input_audio_formats 1 + num_output_audio_formats 1 + Object.Base.input_audio_format [ + { + in_bit_depth 32 + in_valid_bit_depth 32 + in_rate 16000 + } + ] + Object.Base.output_audio_format [ + { + out_bit_depth 32 + out_valid_bit_depth 32 + out_rate 16000 + } + ] + } + + pipeline."1" { + priority 0 + lp_mode 0 + } + } + + + Object.Base { + !route [ + { + source src.$index.1 + sink mfcc.$index.1 + } + ] + } + + direction "capture" + dynamic_pipeline 1 + time_domain "timer" +} diff --git a/tools/topology/topology2/include/pipelines/cavs/mixout-gain-ctc-alh-dai-copier-playback.conf b/tools/topology/topology2/include/pipelines/cavs/mixout-gain-ctc-alh-dai-copier-playback.conf new file mode 100644 index 000000000000..f8346454824d --- /dev/null +++ b/tools/topology/topology2/include/pipelines/cavs/mixout-gain-ctc-alh-dai-copier-playback.conf @@ -0,0 +1,71 @@ +# +# BE playback pipeline: mixout-gain-ctc-alh-dai-copier. +# This pipeline is an extension of the mixout-gain-alh-dai-copier-playback pipeline class. +# +# All attributes defined herein are namespaced +# by alsatplg to "Object.Pipeline.mixout-gain-ctc-alh-dai-copier-playback.N.attribute_name" +# +# Usage: mixout-gain-ctc-alh-dai-copier-playback pipeline object can be instantiated as: +# +# Object.Pipeline.mixout-gain-ctc-alh-dai-copier-playback."N" { +# period 1000 +# time_domain "timer" +# } +# +# Where N is the unique pipeline ID within the same alsaconf node. +# + +<include/common/input_audio_format.conf> +<include/common/output_audio_format.conf> +<include/components/alh-dai-copier.conf> +<include/components/gain.conf> +<include/components/mixout.conf> +<include/components/pipeline.conf> +<include/components/ctc.conf> +<platform/intel/efx-default.conf> +<include/pipelines/cavs/mixout-gain-alh-dai-copier-playback.conf> + +Class.Pipeline."mixout-gain-ctc-alh-dai-copier-playback" { + SubTreeCopy.baseclass { + # this class extends the mixout-gain-alh-dai-copier-playback class definition + source "Class.Pipeline.mixout-gain-alh-dai-copier-playback" + + # target node is not defined which means that the new subtree will be copied to + # the parent node containing the SubTreeCopy node i.e in this case the + # Class.Pipeline.mixout-gain-ctc-alh-dai-copier-playback {} node. + + # default copy type is to extend the base class ie the widgets and routes + # will be added to the existing list of widgets/routes in the base class + + tree { + Object.Widget { + ctc."1" { + num_input_audio_formats 1 + num_output_audio_formats 1 + + Object.Base.input_audio_format [ + { + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + Object.Base.output_audio_format [ + { + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + } + } + + Object.Base { + ! route [ + { + source gain.$index.1 + sink ctc.$index.1 + } + ] + } + } + } +} diff --git a/tools/topology/topology2/include/pipelines/cavs/mixout-gain-dax-alh-dai-copier-playback.conf b/tools/topology/topology2/include/pipelines/cavs/mixout-gain-dax-alh-dai-copier-playback.conf new file mode 100644 index 000000000000..71c367e48481 --- /dev/null +++ b/tools/topology/topology2/include/pipelines/cavs/mixout-gain-dax-alh-dai-copier-playback.conf @@ -0,0 +1,130 @@ +# +# BE playback pipeline: mixout-gain-dax-alh-dai-copier. +# +# All attributes defined herein are namespaced +# by alsatplg to "Object.Pipeline.mixout-gain-dax-alh-dai-copier-playback.N.attribute_name" +# +# Usage: mixout-gain-dax-alh-dai-copier-playback pipeline object can be instantiated as: +# +# Object.Pipeline.mixout-gain-dax-alh-dai-copier-playback."N" { +# time_domain "timer" +# } +# +# Where N is the unique pipeline ID within the same alsaconf node. +# + +<include/components/alh-dai-copier.conf> +<include/components/dolby-dax.conf> +<include/components/gain.conf> +<include/components/mixout.conf> +<include/components/pipeline.conf> + +Class.Pipeline."mixout-gain-dax-alh-dai-copier-playback" { + + <include/pipelines/pipeline-common.conf> + + attributes { + !constructor [ + "index" + ] + + !immutable [ + "direction" + ] + + # + # mixout-gain-dax-alh-dai-copier-playback objects instantiated within the same alsaconf + # node must have unique instance attribute + # + unique "instance" + } + + Object.Widget { + mixout."1" {} + alh-copier."1" { + type dai_in + num_input_audio_formats 1 + num_output_audio_formats 1 + num_input_pins 1 + + # copier only supports one format based on mixin/mixout requirements: + # 32-bit 48KHz 2ch + Object.Base.input_audio_format [ + { + in_bit_depth 32 + in_valid_bit_depth 32 + in_sample_type $SAMPLE_TYPE_MSB_INTEGER + in_fmt_cfg "$[($in_channels | ($in_valid_bit_depth * 256))]" + } + ] + Object.Base.output_audio_format [ + { + out_bit_depth 32 + out_valid_bit_depth 32 + out_sample_type $SAMPLE_TYPE_MSB_INTEGER + out_fmt_cfg "$[($out_channels | ($out_valid_bit_depth * 256))]" + } + ] + } + gain."1" { + num_input_audio_formats 1 + num_output_audio_formats 1 + + # 32-bit 48KHz 2ch + Object.Base.input_audio_format [ + { + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + Object.Base.output_audio_format [ + { + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + } + dolby-dax."1" { + num_input_audio_formats 1 + num_output_audio_formats 1 + Object.Base.input_audio_format [ + { + in_rate 48000 + in_bit_depth 32 + in_valid_bit_depth 32 + ibs "$[(256 * ($[($in_bit_depth / 8)])) * ($in_channels)]" + } + ] + Object.Base.output_audio_format [ + { + out_rate 48000 + out_bit_depth 32 + out_valid_bit_depth 32 + obs "$[(256 * ($[($out_bit_depth / 8)])) * ($out_channels)]" + } + ] + } + + pipeline."1" { + priority 0 + lp_mode 0 + } + } + + Object.Base { + !route [ + { + source gain.$index.1 + sink dolby-dax.$index.1 + } + { + source mixout.$index.1 + sink gain.$index.1 + } + ] + } + + direction "playback" + dynamic_pipeline 1 + time_domain "timer" +} diff --git a/tools/topology/topology2/include/pipelines/cavs/mixout-gain-dax-ctc-dai-copier-playback.conf b/tools/topology/topology2/include/pipelines/cavs/mixout-gain-dax-ctc-dai-copier-playback.conf new file mode 100644 index 000000000000..2353aa7af719 --- /dev/null +++ b/tools/topology/topology2/include/pipelines/cavs/mixout-gain-dax-ctc-dai-copier-playback.conf @@ -0,0 +1,74 @@ +# +# BE playback pipeline: mixout-gain-dax-ctc-dai-copier-playback. +# + +<include/components/dolby-dax.conf> +<include/components/ctc.conf> +<include/pipelines/cavs/mixout-gain-dai-copier-playback.conf> + +Class.Pipeline."mixout-gain-dax-ctc-dai-copier-playback" { + SubTreeCopy.baseclass { + # this class extends the mixout-gain-dai-copier-playback class definition + source "Class.Pipeline.mixout-gain-dai-copier-playback" + + tree { + Object.Widget { + dolby-dax."1" { + num_input_audio_formats 1 + num_output_audio_formats 1 + Object.Base.input_audio_format [ + { + in_rate 48000 + in_bit_depth 32 + in_valid_bit_depth 32 + ibs "$[(256 * ($[($in_bit_depth / 8)])) * ($in_channels)]" + } + ] + Object.Base.output_audio_format [ + { + out_rate 48000 + out_bit_depth 32 + out_valid_bit_depth 32 + obs "$[(256 * ($[($out_bit_depth / 8)])) * ($out_channels)]" + } + ] + } + + ctc."1" { + num_input_audio_formats 1 + num_output_audio_formats 1 + + Object.Base.input_audio_format [ + { + in_rate 48000 + in_bit_depth 32 + in_valid_bit_depth 32 + ibs "$[(256 * ($[($in_bit_depth / 8)])) * ($in_channels)]" + } + ] + Object.Base.output_audio_format [ + { + out_rate 48000 + out_bit_depth 32 + out_valid_bit_depth 32 + obs "$[(256 * ($[($out_bit_depth / 8)])) * ($out_channels)]" + } + ] + } + } + + Object.Base { + ! route [ + { + source gain.$index.1 + sink dolby-dax.$index.1 + } + { + source dolby-dax.$index.1 + sink ctc.$index.1 + } + ] + } + } + } +} diff --git a/tools/topology/topology2/include/pipelines/cavs/mixout-gain-dax-dai-copier-playback.conf b/tools/topology/topology2/include/pipelines/cavs/mixout-gain-dax-dai-copier-playback.conf new file mode 100644 index 000000000000..0f136364e3d5 --- /dev/null +++ b/tools/topology/topology2/include/pipelines/cavs/mixout-gain-dax-dai-copier-playback.conf @@ -0,0 +1,58 @@ +# +# BE playback pipeline: mixout-gain-dax-dai-copier. +# +# All attributes defined herein are namespaced +# by alsatplg to "Object.Pipeline.mixout-gain-dax-dai-copier-playback.N.attribute_name" +# +# Usage: mixout-gain-dax-dai-copier-playback pipeline object can be instantiated as: +# +# Object.Pipeline.mixout-gain-dax-dai-copier-playback."N" { +# period 1000 +# time_domain "timer" +# } +# +# Where N is the unique pipeline ID within the same alsaconf node. +# + +<include/components/dolby-dax.conf> +<include/pipelines/cavs/mixout-gain-dai-copier-playback.conf> + +Class.Pipeline."mixout-gain-dax-dai-copier-playback" { + SubTreeCopy.baseclass { + # this class extends the mixout-gain-dai-copier-playback pipeline class. + source "Class.Pipeline.mixout-gain-dai-copier-playback" + tree { + Object.Widget { + dolby-dax."1" { + num_input_audio_formats 1 + num_output_audio_formats 1 + Object.Base.input_audio_format [ + { + in_rate 48000 + in_bit_depth 32 + in_valid_bit_depth 32 + ibs "$[(256 * ($[($in_bit_depth / 8)])) * ($in_channels)]" + } + ] + Object.Base.output_audio_format [ + { + out_rate 48000 + out_bit_depth 32 + out_valid_bit_depth 32 + obs "$[(256 * ($[($out_bit_depth / 8)])) * ($out_channels)]" + } + ] + } + } + + Object.Base { + !route [ + { + source gain.$index.1 + sink dolby-dax.$index.1 + } + ] + } + } + } +} diff --git a/tools/topology/topology2/include/pipelines/cavs/mixout-gain-eqiir-dts-alh-dai-copier-playback.conf b/tools/topology/topology2/include/pipelines/cavs/mixout-gain-eqiir-dts-alh-dai-copier-playback.conf new file mode 100644 index 000000000000..a34659b0fdfe --- /dev/null +++ b/tools/topology/topology2/include/pipelines/cavs/mixout-gain-eqiir-dts-alh-dai-copier-playback.conf @@ -0,0 +1,156 @@ +# +# BE playback pipeline: mixout-gain-eqiir-dts-alh-dai-copier. +# +# All attributes defined herein are namespaced +# by alsatplg to "Object.Pipeline.mixout-gain-eqiir-dts-alh-dai-copier-playback.N.attribute_name" +# +# Usage: mixout-gain-eqiir-dts-alh-dai-copier-playback pipeline object can be instantiated as: +# +# Object.Pipeline.mixout-gain-eqiir-dts-alh-dai-copier-playback."N" { +# time_domain "timer" +# } +# +# Where N is the unique pipeline ID within the same alsaconf node. +# + +<include/components/alh-dai-copier.conf> +<include/components/gain.conf> +<include/components/mixout.conf> +<include/components/pipeline.conf> +<include/controls/bytes.conf> +<include/components/eqiir.conf> +<include/components/dts.conf> + +Class.Pipeline."mixout-gain-eqiir-dts-alh-dai-copier-playback" { + + <include/pipelines/pipeline-common.conf> + + attributes { + !constructor [ + "index" + ] + + !immutable [ + "direction" + ] + + # + # mixout-gain-eqiir-dts-alh-dai-copier-playback objects instantiated within the same alsaconf + # node must have unique instance attribute + # + unique "instance" + } + + Object.Widget { + mixout."1" {} + alh-copier."1" { + type dai_in + num_input_audio_formats 1 + num_output_audio_formats 1 + num_input_pins 1 + + # copier only supports one format based on mixin/mixout requirements: + # 32-bit 48KHz 2ch + Object.Base.input_audio_format [ + { + in_bit_depth 32 + in_valid_bit_depth 32 + in_sample_type $SAMPLE_TYPE_MSB_INTEGER + in_fmt_cfg "$[($in_channels | ($in_valid_bit_depth * 256))]" + } + ] + Object.Base.output_audio_format [ + { + out_bit_depth 32 + out_valid_bit_depth 32 + out_sample_type $SAMPLE_TYPE_MSB_INTEGER + out_fmt_cfg "$[($out_channels | ($out_valid_bit_depth * 256))]" + } + ] + } + gain."1" { + num_input_audio_formats 1 + num_output_audio_formats 1 + + # 32-bit 48KHz 2ch + Object.Base.input_audio_format [ + { + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + Object.Base.output_audio_format [ + { + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + } + + eqiir."1" { + num_input_audio_formats 1 + num_output_audio_formats 1 + + Object.Base.input_audio_format [ + { + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + Object.Base.output_audio_format [ + { + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + + Object.Control.bytes."1" { + <include/components/eqiir/flat.conf> + } + } + + dts."1" { + num_input_audio_formats 1 + num_output_audio_formats 1 + + Object.Base.input_audio_format [ + { + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + Object.Base.output_audio_format [ + { + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + } + + pipeline."1" { + priority 0 + lp_mode 0 + } + } + + Object.Base { + !route [ + { + source mixout.$index.1 + sink gain.$index.1 + } + { + source gain.$index.1 + sink eqiir.$index.1 + } + { + source eqiir.$index.1 + sink dts.$index.1 + } + ] + } + + direction "playback" + dynamic_pipeline 1 + time_domain "timer" +} diff --git a/tools/topology/topology2/include/pipelines/cavs/mixout-gain-eqiir-dts-dai-copier-playback.conf b/tools/topology/topology2/include/pipelines/cavs/mixout-gain-eqiir-dts-dai-copier-playback.conf index 87b9002f8eb2..4c1836e849ca 100644 --- a/tools/topology/topology2/include/pipelines/cavs/mixout-gain-eqiir-dts-dai-copier-playback.conf +++ b/tools/topology/topology2/include/pipelines/cavs/mixout-gain-eqiir-dts-dai-copier-playback.conf @@ -1,12 +1,12 @@ # -# BE playback pipeline: mixout-gain-dts-dai-copier. +# BE playback pipeline: mixout-gain-eqiir-dts-dai-copier. # # All attributes defined herein are namespaced # by alsatplg to "Object.Pipeline.mixout-gain-eqiir-dts-dai-copier-playback.N.attribute_name" # -# Usage: mixout-gain-dts-dai-copier-playback pipeline object can be instantiated as: +# Usage: mixout-gain-eqiir-dts-dai-copier-playback pipeline object can be instantiated as: # -# Object.Pipeline.mixout-gain-dts-dai-copier-playback."N" { +# Object.Pipeline.mixout-gain-eqiir-dts-dai-copier-playback."N" { # period 1000 # time_domain "timer" # } diff --git a/tools/topology/topology2/include/pipelines/cavs/mixout-gain-eqiir-eqfir-drc-micsel-eqiir-eqfir-alh-dai-copier-playback.conf b/tools/topology/topology2/include/pipelines/cavs/mixout-gain-eqiir-eqfir-drc-micsel-eqiir-eqfir-alh-dai-copier-playback.conf new file mode 100644 index 000000000000..cc95dd9799a7 --- /dev/null +++ b/tools/topology/topology2/include/pipelines/cavs/mixout-gain-eqiir-eqfir-drc-micsel-eqiir-eqfir-alh-dai-copier-playback.conf @@ -0,0 +1,132 @@ +# +# BE playback pipeline: mixout-gain-eqiir-eqfir-drc-micsel-eqiir-eqfir-alh-dai-copier. +# +# All attributes defined herein are namespaced +# by alsatplg to "Object.Pipeline.mixout-gain-eqiir-eqfir-drc-micsel-eqiir-eqfir-alh-dai-copier-playback.N.attribute_name" +# +# Usage: mixout-gain-eqiir-eqfir-drc-micsel-eqiir-eqfir-alh-dai-copier-playback pipeline object can be instantiated as: +# +# Object.Pipeline.mixout-gain-eqiir-eqfir-drc-micsel-eqiir-eqfir-alh-dai-copier-playback."N" { +# time_domain "timer" +# } +# +# Where N is the unique pipeline ID within the same alsaconf node. +# + +<include/pipelines/cavs/mixout-gain-eqiir-eqfir-drc-alh-dai-copier-playback.conf> +<include/components/micsel.conf> + +Class.Pipeline."mixout-gain-eqiir-eqfir-drc-micsel-eqiir-eqfir-alh-dai-copier-playback" { + + SubTreeCopy.baseclass { + # this class extends the pipeline class. + source "Class.Pipeline.mixout-gain-eqiir-eqfir-drc-alh-dai-copier-playback" + + # target node is not defined which means that the new subtree will be copied to + # the parent node containing the SubTreeCopy node i.e in this case the + # Class.Pipeline.mixout-gain-eqiir-eqfir-drc-micsel-eqiir-eqfir-alh-dai-copier-playback {} node. + + # default copy type is to extend the base class ie the widgets and routes + # will be added to the existing list of widgets/routes in the base class + + tree { + Object.Widget { + micsel."1" { + num_input_audio_formats 1 + Object.Base.input_audio_format [ + { + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + num_output_audio_formats 1 + Object.Base.output_audio_format [ + { + out_channels $SDW_AMP_NUM_CHANNELS + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + Object.Control { + bytes."1" { + IncludeByKey.SDW_AMP_XOVER_SELECTOR_PARAMS { + "default" "include/components/micsel/passthrough.conf" + "xover_selector_lr_to_llrr" "include/components/micsel/xover_selector_lr_to_llrr.conf" + "xover_selector_lr_to_lrlr" "include/components/micsel/xover_selector_lr_to_lrlr.conf" + } + } + } + } + + eqiir."2" { + num_input_audio_formats 1 + Object.Base.input_audio_format [ + { + in_channels $SDW_AMP_NUM_CHANNELS + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + num_output_audio_formats 1 + Object.Base.output_audio_format [ + { + out_channels $SDW_AMP_NUM_CHANNELS + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + Object.Control.bytes."1" { + IncludeByKey.SDW_AMP_XOVER_EQIIR_PARAMS { + "default" "include/components/eqiir/passthrough.conf" + "xover_lr4_2000hz_lhlh_48khz" "include/components/eqiir/xover_lr4_2000hz_lhlh_48khz.conf" + "xover_lr4_2000hz_llhh_48khz" "include/components/eqiir/xover_lr4_2000hz_llhh_48khz.conf" + "xover_lr4_2000hz_hhll_48khz" "include/components/eqiir/xover_lr4_2000hz_hhll_48khz.conf" + } + } + } + eqfir."2" { + num_input_audio_formats 1 + Object.Base.input_audio_format [ + { + in_channels $SDW_AMP_NUM_CHANNELS + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + num_output_audio_formats 1 + Object.Base.output_audio_format [ + { + out_channels $SDW_AMP_NUM_CHANNELS + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + Object.Control.bytes."1" { + IncludeByKey.SDW_AMP_XOVER_EQFIR_PARAMS { + "default" "include/components/eqfir/passthrough.conf" + "xover_dd00_100us_48khz.conf" "include/components/eqfir/xover_dd00_100us_48khz.conf" + "xover_00dd_100us_48khz.conf" "include/components/eqfir/xover_00dd_100us_48khz.conf" + } + } + } + } + + Object.Base { + !route [ + { + source drc.$index.1 + sink micsel.$index.1 + } + { + source micsel.$index.1 + sink eqiir.$index.2 + } + { + source eqiir.$index.2 + sink eqfir.$index.2 + } + ] + } + } + } +} diff --git a/tools/topology/topology2/include/pipelines/cavs/mixout-gain-micsel-eqiir-eqfir-alh-dai-copier-playback.conf b/tools/topology/topology2/include/pipelines/cavs/mixout-gain-micsel-eqiir-eqfir-alh-dai-copier-playback.conf new file mode 100644 index 000000000000..a12503d5d83a --- /dev/null +++ b/tools/topology/topology2/include/pipelines/cavs/mixout-gain-micsel-eqiir-eqfir-alh-dai-copier-playback.conf @@ -0,0 +1,132 @@ +# +# BE playback pipeline: mixout-gain-micsel-eqiir-eqfir-alh-dai-copier. +# +# All attributes defined herein are namespaced +# by alsatplg to "Object.Pipeline.mixout-gain-micsel-eqiir-eqfir-alh-dai-copier-playback.N.attribute_name" +# +# Usage: mixout-gain-micsel-eqiir-eqfir-alh-dai-copier-playback pipeline object can be instantiated as: +# +# Object.Pipeline.mixout-gain-micsel-eqiir-eqfir-alh-dai-copier-playback."N" { +# time_domain "timer" +# } +# +# Where N is the unique pipeline ID within the same alsaconf node. +# + +<include/pipelines/cavs/mixout-gain-alh-dai-copier-playback.conf> +<include/components/micsel.conf> + +Class.Pipeline."mixout-gain-micsel-eqiir-eqfir-alh-dai-copier-playback" { + + SubTreeCopy.baseclass { + # this class extends the pipeline class. + source "Class.Pipeline.mixout-gain-alh-dai-copier-playback" + + # target node is not defined which means that the new subtree will be copied to + # the parent node containing the SubTreeCopy node i.e in this case the + # Class.Pipeline.mixout-gain-micsel-eqiir-eqfir-alh-dai-copier-playback {} node. + + # default copy type is to extend the base class ie the widgets and routes + # will be added to the existing list of widgets/routes in the base class + + tree { + Object.Widget { + micsel."1" { + num_input_audio_formats 1 + Object.Base.input_audio_format [ + { + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + num_output_audio_formats 1 + Object.Base.output_audio_format [ + { + out_channels $SDW_AMP_NUM_CHANNELS + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + Object.Control { + bytes."1" { + IncludeByKey.SDW_AMP_XOVER_SELECTOR_PARAMS { + "default" "include/components/micsel/passthrough.conf" + "xover_selector_lr_to_llrr" "include/components/micsel/xover_selector_lr_to_llrr.conf" + "xover_selector_lr_to_lrlr" "include/components/micsel/xover_selector_lr_to_lrlr.conf" + } + } + } + } + + eqiir."2" { + num_input_audio_formats 1 + Object.Base.input_audio_format [ + { + in_channels $SDW_AMP_NUM_CHANNELS + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + num_output_audio_formats 1 + Object.Base.output_audio_format [ + { + out_channels $SDW_AMP_NUM_CHANNELS + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + Object.Control.bytes."1" { + IncludeByKey.SDW_AMP_XOVER_EQIIR_PARAMS { + "default" "include/components/eqiir/passthrough.conf" + "xover_lr4_2000hz_lhlh_48khz" "include/components/eqiir/xover_lr4_2000hz_lhlh_48khz.conf" + "xover_lr4_2000hz_llhh_48khz" "include/components/eqiir/xover_lr4_2000hz_llhh_48khz.conf" + "xover_lr4_2000hz_hhll_48khz" "include/components/eqiir/xover_lr4_2000hz_hhll_48khz.conf" + } + } + } + eqfir."2" { + num_input_audio_formats 1 + Object.Base.input_audio_format [ + { + in_channels $SDW_AMP_NUM_CHANNELS + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + num_output_audio_formats 1 + Object.Base.output_audio_format [ + { + out_channels $SDW_AMP_NUM_CHANNELS + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + Object.Control.bytes."1" { + IncludeByKey.SDW_AMP_XOVER_EQFIR_PARAMS { + "default" "include/components/eqfir/passthrough.conf" + "xover_dd00_100us_48khz.conf" "include/components/eqfir/xover_dd00_100us_48khz.conf" + "xover_00dd_100us_48khz.conf" "include/components/eqfir/xover_00dd_100us_48khz.conf" + } + } + } + } + + Object.Base { + !route [ + { + source gain.$index.1 + sink micsel.$index.1 + } + { + source micsel.$index.1 + sink eqiir.$index.2 + } + { + source eqiir.$index.2 + sink eqfir.$index.2 + } + ] + } + } + } +} diff --git a/tools/topology/topology2/include/pipelines/cavs/siggen-host-copier-capture.conf b/tools/topology/topology2/include/pipelines/cavs/siggen-host-copier-capture.conf new file mode 100644 index 000000000000..26de0b1766f0 --- /dev/null +++ b/tools/topology/topology2/include/pipelines/cavs/siggen-host-copier-capture.conf @@ -0,0 +1,98 @@ +# +# Siggen capture pipeline +# +# This class provides host pipeline for capture with siggen input. All +# attributes defined herein are namespaced by alsatplg to +# "Object.Pipeline.siggen-host-copier-capture.N.attribute_name". +# +# Usage: siggen-host-copier-capture pipeline object can be instantiated as: +# +# Object.Pipeline.siggen-host-copier-capture."N" { +# period 1000 +# time_domain "timer" +# } +# +# Where N is the unique pipeline ID within the same alsaconf node. +# + +<include/common/input_audio_format.conf> +<include/common/output_audio_format.conf> +<include/components/pipeline.conf> +<include/components/host-copier.conf> +<include/components/siggen.conf> + +Class.Pipeline."siggen-host-copier-capture" { + + <include/pipelines/pipeline-common.conf> + + attributes { + !constructor [ + "index" + ] + + # + # siggen-host-copier-capture objects instantiated within the same alsaconf + # node must have unique pipeline_id attribute + # + unique "instance" + } + + Object.Widget { + siggen."1" { + num_input_pins 1 + num_output_pins 1 + num_input_audio_formats 1 + num_output_audio_formats 1 + Object.Base.input_audio_format [ + { + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + Object.Base.output_audio_format [ + { + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + } + + host-copier."1" { + type "aif_out" + node_type $HDA_HOST_INPUT_CLASS + num_input_pins 1 + num_output_pins 1 + num_input_audio_formats 1 + num_output_audio_formats 3 + Object.Base.input_audio_format [ + { + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + Object.Base.output_audio_format [ + { + out_bit_depth 16 + out_valid_bit_depth 16 + } + { + out_bit_depth 32 + out_valid_bit_depth 24 + } + { + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + } + + pipeline."1" { + priority 0 + lp_mode 0 + } + } + + direction "capture" + dynamic_pipeline 1 + time_domain "timer" +} diff --git a/tools/topology/topology2/platform/intel/compr-default.conf b/tools/topology/topology2/platform/intel/compr-default.conf new file mode 100644 index 000000000000..12ab39185f5a --- /dev/null +++ b/tools/topology/topology2/platform/intel/compr-default.conf @@ -0,0 +1,16 @@ +# default settings for Compressed Audio Playback +Define { + COMPRESSED false + COMPR_DEEPBUFFER_MS 100 # 100 ms copier dma size + + COMPRESSED_1 true + COMPR_PCM_NAME 'Compress Playback' + COMPR_PCM_ID 50 + COMPR_PIPELINE_ID 90 + + + COMPRESSED_2 false + COMPR_2_PCM_NAME 'Compress Playback 2' + COMPR_2_PCM_ID 52 + COMPR_2_PIPELINE_ID 92 +} diff --git a/tools/topology/topology2/platform/intel/compr.conf b/tools/topology/topology2/platform/intel/compr.conf new file mode 100644 index 000000000000..14c07a0d974d --- /dev/null +++ b/tools/topology/topology2/platform/intel/compr.conf @@ -0,0 +1,229 @@ + +IncludeByKey.COMPRESSED_1 { +"true" { + Object.Pipeline.compr-playback [ + { + index $COMPR_PIPELINE_ID + + Object.Widget.host-copier.1 { + stream_name $COMPR_PCM_NAME + pcm_id $COMPR_PCM_ID + <include/formats/compr_input_audio_formats.conf> + <include/formats/compr_output_audio_formats.conf> + } + + Object.Widget.decoder.1 { + core_id 1 + <include/formats/compr_input_audio_formats.conf> + <include/formats/compr_output_audio_formats.conf> + } + + # converts the input to S32_LE + Object.Widget.module-copier.2 { + <include/formats/compr_input_audio_formats.conf> + <include/formats/compr_output_audio_formats_s32.conf> + } + + # converts the input to 48KHz + # note: this is wrong for 96/192KHz HiRes audio!!!! + Object.Widget.src.1 { + rate_out 48000 + <include/formats/compr_input_output_formats_src_s32_to_48k.conf> + } + + # converts the input to stereo + Object.Widget.micsel.1 { + Object.Control.bytes."1" { + name '$COMPR_PCM_NAME selector bytes' + <include/components/micsel/stereo_endpoint_playback_updownmix.conf> + } + <include/formats/compr_input_audio_formats_s32_48k.conf> + <include/formats/compr_output_audio_formats_s32_48k_stereo.conf> + } + + Object.Widget.gain.1 { + Object.Control.mixer.1 { + name 'Pre Mixer $COMPR_PCM_NAME Volume' + } + num_input_audio_formats 1 + Object.Base.input_audio_format [ + { + in_rate 48000 + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + num_output_audio_formats 1 + Object.Base.output_audio_format [ + { + out_rate 48000 + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + } + Object.Widget.mixin.1 { + Object.Base.input_audio_format [ + { + in_rate 48000 + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + Object.Base.output_audio_format [ + { + out_rate 48000 + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + } + } + ] + + Object.PCM.pcm [ + { + name $COMPR_PCM_NAME + id $COMPR_PCM_ID + direction playback + compress "true" + + Object.Base.fe_dai.1 { + name "$COMPR_PCM_NAME" + } + + Object.PCM.pcm_caps.1 { + name $COMPR_PCM_NAME + formats 'S32_LE' + rates '8000' + } + } + ] + + Object.Base.route [ + { + source mixin.$COMPR_PIPELINE_ID.1 + sink $COMPR_PIPELINE_SINK + } + { + source host-copier.$COMPR_PCM_ID.playback + sink decoder.$COMPR_PIPELINE_ID.1 + } + ] +} +} + +# Spawn another instance for speaker +IncludeByKey.COMPRESSED_2 { +"true" { + Object.Pipeline.compr-playback [ + { + index $COMPR_2_PIPELINE_ID + + Object.Widget.host-copier.1 { + stream_name $COMPR_2_PCM_NAME + pcm_id $COMPR_2_PCM_ID + <include/formats/compr_input_audio_formats.conf> + <include/formats/compr_output_audio_formats.conf> + } + + Object.Widget.decoder.1 { + core_id 1 + <include/formats/compr_input_audio_formats.conf> + <include/formats/compr_output_audio_formats.conf> + } + + # converts the input to S32_LE + Object.Widget.module-copier.2 { + <include/formats/compr_input_audio_formats.conf> + <include/formats/compr_output_audio_formats_s32.conf> + } + + # converts the input to 48KHz + # note: this is wrong for 96/192KHz HiRes audio!!!! + Object.Widget.src.1 { + rate_out 48000 + <include/formats/compr_input_output_formats_src_s32_to_48k.conf> + } + + # converts the input to stereo + Object.Widget.micsel.1 { + Object.Control.bytes."1" { + name '$COMPR_2_PCM_NAME selector bytes' + <include/components/micsel/stereo_endpoint_playback_updownmix.conf> + } + <include/formats/compr_input_audio_formats_s32_48k.conf> + <include/formats/compr_output_audio_formats_s32_48k_stereo.conf> + } + + Object.Widget.gain.1 { + Object.Control.mixer.1 { + name 'Pre Mixer $COMPR_2_PCM_NAME Volume' + } + num_input_audio_formats 1 + Object.Base.input_audio_format [ + { + in_rate 48000 + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + num_output_audio_formats 1 + Object.Base.output_audio_format [ + { + out_rate 48000 + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + } + Object.Widget.mixin.1 { + Object.Base.input_audio_format [ + { + in_rate 48000 + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + Object.Base.output_audio_format [ + { + out_rate 48000 + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + } + } + ] + + Object.PCM.pcm [ + { + name $COMPR_2_PCM_NAME + id $COMPR_2_PCM_ID + direction playback + compress "true" + + Object.Base.fe_dai.1 { + name "$COMPR_2_PCM_NAME" + } + + Object.PCM.pcm_caps.1 { + name $COMPR_2_PCM_NAME + formats 'S32_LE' + rates '8000' + } + } + ] + + Object.Base.route [ + { + source mixin.$COMPR_2_PIPELINE_ID.1 + sink $COMPR_2_PIPELINE_SINK + } + { + source host-copier.$COMPR_2_PCM_ID.playback + sink decoder.$COMPR_2_PIPELINE_ID.1 + } + ] +} +} diff --git a/tools/topology/topology2/platform/intel/deep-buffer-spk.conf b/tools/topology/topology2/platform/intel/deep-buffer-spk.conf new file mode 100644 index 000000000000..b5743653f84f --- /dev/null +++ b/tools/topology/topology2/platform/intel/deep-buffer-spk.conf @@ -0,0 +1,59 @@ +Define { + SPEAKER_PCM_CORE_ID 0 +} + +Object.Pipeline.deepbuffer-playback [ + { + index $DEEP_BUFFER_PIPELINE_ID_2 + core_id $SPEAKER_PCM_CORE_ID + + Object.Widget.host-copier.1 { + stream_name $DEEP_BUFFER_PCM_NAME_2 + pcm_id $DEEP_BUFFER_PCM_ID_2 + } + Object.Widget.gain.1 { + Object.Control.mixer.1 { + name 'Pre Mixer $DEEP_BUFFER_PCM_NAME_2 Volume' + } + } + Object.Widget.pipeline.1 { + core $SPEAKER_PCM_CORE_ID + } + } +] + +Object.PCM.pcm [ + { + name $DEEP_BUFFER_PCM_NAME_2 + id $DEEP_BUFFER_PCM_ID_2 + direction playback + playback_compatible_d0i3 $DEEPBUFFER_D0I3_COMPATIBLE + + Object.Base.fe_dai.1 { + name "$DEEP_BUFFER_PCM_NAME_2" + } + Object.PCM.pcm_caps.1 { + name $DEEP_BUFFER_PCM_NAME_2 + formats 'S16_LE,S24_LE,S32_LE' + # To avoid DMA spinning on a buffer we need bigger + # buffer than the host buffer size, let's say twice as + # big + # (S16_LE, Stereo, 48KHz, DEEPBUFFER_FW_DMA_MS) * 2 + # + # Note: The lower limit for the buffer size is rate + # dependent + buffer_size_min "$[(((2 * $channels_min) * 48) * $DEEPBUFFER_FW_DMA_MS) * 2]" + } + } +] + +Object.Base.route [ + { + source $DEEP_BUFFER_PIPELINE_SRC_2 + sink $DEEP_BUFFER_PIPELINE_SINK_2 + } + { + source host-copier.$DEEP_BUFFER_PCM_ID_2.playback + sink gain.$DEEP_BUFFER_PIPELINE_ID_2.1 + } +] diff --git a/tools/topology/topology2/platform/intel/deep-buffer.conf b/tools/topology/topology2/platform/intel/deep-buffer.conf index 282210baaac9..5b86088feddf 100644 --- a/tools/topology/topology2/platform/intel/deep-buffer.conf +++ b/tools/topology/topology2/platform/intel/deep-buffer.conf @@ -6,23 +6,60 @@ Object.Pipeline.deepbuffer-playback [ Object.Widget.host-copier.1 { stream_name $DEEP_BUFFER_PCM_NAME pcm_id $DEEP_BUFFER_PCM_ID - Object.Base.input_audio_format [ - { - in_rate $DEEP_BUF_JACK_RATE - in_bit_depth 16 - in_valid_bit_depth 16 - } - { - in_rate $DEEP_BUF_JACK_RATE - in_bit_depth 32 - in_valid_bit_depth 24 - } - { - in_rate $DEEP_BUF_JACK_RATE - in_bit_depth 32 - in_valid_bit_depth 32 + IncludeByKey.PCM_FORMAT_ALL { + "true" { + num_input_audio_formats 5 + Object.Base.input_audio_format [ + { + in_rate $DEEP_BUF_JACK_RATE + in_bit_depth 16 + in_valid_bit_depth 16 + } + { + in_rate $DEEP_BUF_JACK_RATE + in_bit_depth 32 + in_valid_bit_depth 24 + } + { + in_rate $DEEP_BUF_JACK_RATE + in_bit_depth 32 + in_valid_bit_depth 32 + } + { + in_rate $DEEP_BUF_JACK_RATE + in_bit_depth 32 + in_valid_bit_depth 32 + in_sample_type $SAMPLE_TYPE_FLOAT + } + { + in_rate $DEEP_BUF_JACK_RATE + in_bit_depth 8 + in_valid_bit_depth 8 + in_sample_type $SAMPLE_TYPE_UNSIGNED_INTEGER + } + ] + } + "false" { + num_input_audio_formats 3 + Object.Base.input_audio_format [ + { + in_rate $DEEP_BUF_JACK_RATE + in_bit_depth 16 + in_valid_bit_depth 16 + } + { + in_rate $DEEP_BUF_JACK_RATE + in_bit_depth 32 + in_valid_bit_depth 24 + } + { + in_rate $DEEP_BUF_JACK_RATE + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] } - ] + } Object.Base.output_audio_format [ { out_rate $DEEP_BUF_JACK_RATE @@ -83,7 +120,14 @@ Object.PCM.pcm [ Object.PCM.pcm_caps.1 { name $DEEP_BUFFER_PCM_NAME - formats 'S16_LE,S24_LE,S32_LE' + IncludeByKey.PCM_FORMAT_ALL { + "true" { + formats 'S16_LE,S24_LE,S32_LE,U8,FLOAT_LE' + } + "false" { + formats 'S16_LE,S24_LE,S32_LE' + } + } # To avoid DMA spinning on a buffer we need bigger # buffer than the host buffer size, let's say twice as # big @@ -120,65 +164,5 @@ Object.Base.route [ # Spawn another instance IncludeByKey.DEEP_BUF_SPK { -"true" { - Define { - SPEAKER_PCM_CORE_ID 0 - } - - Object.Pipeline.deepbuffer-playback [ - { - index $DEEP_BUFFER_PIPELINE_ID_2 - core_id $SPEAKER_PCM_CORE_ID - - Object.Widget.host-copier.1 { - stream_name $DEEP_BUFFER_PCM_NAME_2 - pcm_id $DEEP_BUFFER_PCM_ID_2 - } - Object.Widget.gain.1 { - Object.Control.mixer.1 { - name 'Pre Mixer $DEEP_BUFFER_PCM_NAME_2 Volume' - } - } - Object.Widget.pipeline.1 { - core $SPEAKER_PCM_CORE_ID - } - } - ] - - Object.PCM.pcm [ - { - name $DEEP_BUFFER_PCM_NAME_2 - id $DEEP_BUFFER_PCM_ID_2 - direction playback - playback_compatible_d0i3 $DEEPBUFFER_D0I3_COMPATIBLE - - Object.Base.fe_dai.1 { - name "$DEEP_BUFFER_PCM_NAME_2" - } - Object.PCM.pcm_caps.1 { - name $DEEP_BUFFER_PCM_NAME_2 - formats 'S16_LE,S24_LE,S32_LE' - # To avoid DMA spinning on a buffer we need bigger - # buffer than the host buffer size, let's say twice as - # big - # (S16_LE, Stereo, 48KHz, DEEPBUFFER_FW_DMA_MS) * 2 - # - # Note: The lower limit for the buffer size is rate - # dependent - buffer_size_min "$[(((2 * $channels_min) * 48) * $DEEPBUFFER_FW_DMA_MS) * 2]" - } - } - ] - - Object.Base.route [ - { - source $DEEP_BUFFER_PIPELINE_SRC_2 - sink $DEEP_BUFFER_PIPELINE_SINK_2 - } - { - source host-copier.$DEEP_BUFFER_PCM_ID_2.playback - sink gain.$DEEP_BUFFER_PIPELINE_ID_2.1 - } - ] - } +"true" "platform/intel/deep-buffer-spk.conf" } diff --git a/tools/topology/topology2/platform/intel/dmic-deep-buffer.conf b/tools/topology/topology2/platform/intel/dmic-deep-buffer.conf new file mode 100644 index 000000000000..5e8615c2db21 --- /dev/null +++ b/tools/topology/topology2/platform/intel/dmic-deep-buffer.conf @@ -0,0 +1,117 @@ + Object.Pipeline.deepbuffer-capture [ + { + format $FORMAT + index $DMIC0_DEEP_BUFFER_PIPELINE_ID + Object.Widget.pipeline.1 { + stream_name "$DMIC0_DEEP_BUFFER_PCM_NAME" + } + Object.Widget.host-copier.1 { + stream_name $DMIC0_DEEP_BUFFER_PCM_NAME + pcm_id $DMIC0_DEEP_BUFFER_PCM_ID + num_input_audio_formats 2 + Object.Base.input_audio_format [ + { + in_rate $DMIC0_RATE + in_bit_depth 32 + in_valid_bit_depth 32 + } + { + in_rate $DMIC0_RATE + in_channels 4 + in_bit_depth 32 + in_valid_bit_depth 32 + in_ch_cfg $CHANNEL_CONFIG_3_POINT_1 + in_ch_map $CHANNEL_MAP_3_POINT_1 + } + ] + + num_output_audio_formats 6 + Object.Base.output_audio_format [ + { + out_rate $DMIC0_RATE + out_bit_depth 16 + out_valid_bit_depth 16 + } + { + out_rate $DMIC0_RATE + out_bit_depth 32 + out_valid_bit_depth 24 + } + { + out_rate $DMIC0_RATE + out_bit_depth 32 + out_valid_bit_depth 32 + } + { + out_rate $DMIC0_RATE + out_channels 4 + out_bit_depth 16 + out_valid_bit_depth 16 + out_ch_cfg $CHANNEL_CONFIG_3_POINT_1 + out_ch_map $CHANNEL_MAP_3_POINT_1 + } + { + out_rate $DMIC0_RATE + out_channels 4 + out_bit_depth 32 + out_valid_bit_depth 24 + out_ch_cfg $CHANNEL_CONFIG_3_POINT_1 + out_ch_map $CHANNEL_MAP_3_POINT_1 + } + { + out_rate $DMIC0_RATE + out_channels 4 + out_bit_depth 32 + out_valid_bit_depth 32 + out_ch_cfg $CHANNEL_CONFIG_3_POINT_1 + out_ch_map $CHANNEL_MAP_3_POINT_1 + } + ] + } + } + + ] + Object.PCM.pcm [ + { + name "$DMIC0_DEEP_BUFFER_PCM_NAME" + id $DMIC0_DEEP_BUFFER_PCM_ID + direction "capture" + capture_compatible_d0i3 $DEEPBUFFER_D0I3_COMPATIBLE + + Object.Base.fe_dai."$DMIC0_DEEP_BUFFER_PCM_NAME" {} + + Object.PCM.pcm_caps."capture" { + name $DMIC0_DEEP_BUFFER_PCM_NAME + formats 'S16_LE,S24_LE,S32_LE' + channels_min $NUM_DMICS + channels_max $NUM_DMICS + # To avoid DMA spinning on a buffer we need bigger + # buffer than the host buffer size, let's say twice as + # big + # (S16_LE, Stereo, 48KHz, DEEPBUFFER_FW_DMA_MS) * 2 + # + # Note: The lower limit for the buffer size is rate + # dependent + IncludeByKey.DMIC0_RATE { + "16000" { + buffer_size_min "$[(((2 * $NUM_DMICS) * 16) * $DEEPBUFFER_FW_DMA_MS) * 2]" + rates '16000' + } + "48000" { + buffer_size_min "$[(((2 * $NUM_DMICS) * 48) * $DEEPBUFFER_FW_DMA_MS) * 2]" + rates '48000' + } + "96000" { + buffer_size_min "$[(((2 * $NUM_DMICS) * 96) * $DEEPBUFFER_FW_DMA_MS) * 2]" + rates '96000' + } + } + } + } + ] + Object.Base.route [ + { + source "module-copier.$DMIC0_DAI_PIPELINE_ID.2" + sink "host-copier.$DMIC0_DEEP_BUFFER_PCM_ID.capture" + } + ] \ No newline at end of file diff --git a/tools/topology/topology2/platform/intel/dmic-default.conf b/tools/topology/topology2/platform/intel/dmic-default.conf index 61569b42ec80..1bb96a45465b 100644 --- a/tools/topology/topology2/platform/intel/dmic-default.conf +++ b/tools/topology/topology2/platform/intel/dmic-default.conf @@ -47,4 +47,10 @@ Define { # Note: This will be redefined in dmic-generic.conf if not set from cmake DMIC0_PCM_CHANNELS 0 + + # Deep buffer capture + DMIC0_DEEP_BUFFER_PCM_NAME "DMIC Deep Buffer" + DMIC0_DEEP_BUFFER_PIPELINE_ID 18 + DMIC0_DEEP_BUFFER_PCM_ID 46 + DMIC0_DEEP_BUFFER false } diff --git a/tools/topology/topology2/platform/intel/dmic-generic.conf b/tools/topology/topology2/platform/intel/dmic-generic.conf index 7b65d70ce0bf..c473f1eea470 100644 --- a/tools/topology/topology2/platform/intel/dmic-generic.conf +++ b/tools/topology/topology2/platform/intel/dmic-generic.conf @@ -107,7 +107,6 @@ IncludeByKey.PASSTHROUGH { stream_name $DMIC0_PCM_CAPS pcm_id $DMIC0_PCM_ID num_input_audio_formats 1 - num_output_audio_formats 1 IncludeByKey.DMIC0_PCM_CHANNELS { "1" { Object.Base.input_audio_format [ @@ -120,56 +119,192 @@ IncludeByKey.PASSTHROUGH { in_ch_map $CHANNEL_MAP_MONO } ] - Object.Base.output_audio_format [ + IncludeByKey.PCM_FORMAT_ALL { + "true" { + num_output_audio_formats 5 + Object.Base.output_audio_format [ + { + out_rate $DMIC0_RATE + out_bit_depth 16 + out_valid_bit_depth 16 + out_ch_cfg $CHANNEL_CONFIG_MONO + out_ch_map $CHANNEL_MAP_MONO + } + { + out_rate $DMIC0_RATE + out_bit_depth 32 + out_valid_bit_depth 32 + out_ch_cfg $CHANNEL_CONFIG_MONO + out_ch_map $CHANNEL_MAP_MONO + } + { + out_rate $DMIC0_RATE + out_bit_depth 32 + out_valid_bit_depth 24 + out_ch_cfg $CHANNEL_CONFIG_MONO + out_ch_map $CHANNEL_MAP_MONO + } + { + out_rate $DMIC0_RATE + out_bit_depth 32 + out_valid_bit_depth 32 + out_sample_type $SAMPLE_TYPE_FLOAT + out_ch_cfg $CHANNEL_CONFIG_MONO + out_ch_map $CHANNEL_MAP_MONO + } + { + out_rate $DMIC0_RATE + out_bit_depth 8 + out_valid_bit_depth 8 + out_sample_type $SAMPLE_TYPE_UNSIGNED_INTEGER + out_ch_cfg $CHANNEL_CONFIG_MONO + out_ch_map $CHANNEL_MAP_MONO + } + ] + } + "false" { + num_output_audio_formats 1 + Object.Base.output_audio_format [ + { + out_rate $DMIC0_RATE + out_channels 1 + out_bit_depth 32 + out_valid_bit_depth 32 + out_ch_cfg $CHANNEL_CONFIG_MONO + out_ch_map $CHANNEL_MAP_MONO + } + ] + } + } + } + "2" { + Object.Base.input_audio_format [ { - out_rate $DMIC0_RATE - out_channels 1 - out_bit_depth 32 - out_valid_bit_depth 32 - out_ch_cfg $CHANNEL_CONFIG_MONO - out_ch_map $CHANNEL_MAP_MONO + in_rate $DMIC0_RATE + in_bit_depth 32 + in_valid_bit_depth 32 } ] + IncludeByKey.PCM_FORMAT_ALL { + "true" { + num_output_audio_formats 5 + Object.Base.output_audio_format [ + { + out_rate $DMIC0_RATE + out_bit_depth 16 + out_valid_bit_depth 16 + } + { + out_rate $DMIC0_RATE + out_bit_depth 32 + out_valid_bit_depth 24 + } + { + out_rate $DMIC0_RATE + out_bit_depth 32 + out_valid_bit_depth 32 + } + { + out_rate $DMIC0_RATE + out_bit_depth 32 + out_valid_bit_depth 32 + out_sample_type $SAMPLE_TYPE_FLOAT + } + { + out_rate $DMIC0_RATE + out_bit_depth 8 + out_valid_bit_depth 8 + out_sample_type $SAMPLE_TYPE_UNSIGNED_INTEGER + } + ] + } + "false" { + num_output_audio_formats 1 + Object.Base.output_audio_format [ + { + out_rate $DMIC0_RATE + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + } + } } - "2" { - Object.Base.input_audio_format [ - { - in_rate $DMIC0_RATE - in_bit_depth 32 - in_valid_bit_depth 32 - } - ] - Object.Base.output_audio_format [ - { - out_rate $DMIC0_RATE - out_bit_depth 32 - out_valid_bit_depth 32 - } - ] - } - "4" { - Object.Base.input_audio_format [ - { - in_rate $DMIC0_RATE - in_channels 4 - in_bit_depth 32 - in_valid_bit_depth 32 - in_ch_cfg $CHANNEL_CONFIG_3_POINT_1 - in_ch_map $CHANNEL_MAP_3_POINT_1 - } - ] - Object.Base.output_audio_format [ - { - out_rate $DMIC0_RATE - out_channels 4 - out_bit_depth 32 - out_valid_bit_depth 32 - out_ch_cfg $CHANNEL_CONFIG_3_POINT_1 - out_ch_map $CHANNEL_MAP_3_POINT_1 - } - ] - } - } + "4" { + Object.Base.input_audio_format [ + { + in_rate $DMIC0_RATE + in_channels 4 + in_bit_depth 32 + in_valid_bit_depth 32 + in_ch_cfg $CHANNEL_CONFIG_3_POINT_1 + in_ch_map $CHANNEL_MAP_3_POINT_1 + } + ] + IncludeByKey.PCM_FORMAT_ALL { + "true" { + num_output_audio_formats 5 + Object.Base.output_audio_format [ + { + out_rate $DMIC0_RATE + out_channels 4 + out_bit_depth 16 + out_valid_bit_depth 16 + out_ch_cfg $CHANNEL_CONFIG_3_POINT_1 + out_ch_map $CHANNEL_MAP_3_POINT_1 + } + { + out_rate $DMIC0_RATE + out_channels 4 + out_bit_depth 32 + out_valid_bit_depth 24 + out_ch_cfg $CHANNEL_CONFIG_3_POINT_1 + out_ch_map $CHANNEL_MAP_3_POINT_1 + } + { + out_rate $DMIC0_RATE + out_channels 4 + out_bit_depth 32 + out_valid_bit_depth 32 + out_ch_cfg $CHANNEL_CONFIG_3_POINT_1 + out_ch_map $CHANNEL_MAP_3_POINT_1 + } + { + out_rate $DMIC0_RATE + out_channels 4 + out_bit_depth 32 + out_valid_bit_depth 32 + out_ch_cfg $CHANNEL_CONFIG_3_POINT_1 + out_ch_map $CHANNEL_MAP_3_POINT_1 + out_sample_type $SAMPLE_TYPE_FLOAT + } + { + out_rate $DMIC0_RATE + out_channels 4 + out_bit_depth 8 + out_valid_bit_depth 8 + out_ch_cfg $CHANNEL_CONFIG_3_POINT_1 + out_ch_map $CHANNEL_MAP_3_POINT_1 + out_sample_type $SAMPLE_TYPE_UNSIGNED_INTEGER + } + ] + } + "false" { + num_output_audio_formats 1 + Object.Base.output_audio_format [ + { + out_rate $DMIC0_RATE + out_channels 4 + out_bit_depth 32 + out_valid_bit_depth 32 + out_ch_cfg $CHANNEL_CONFIG_3_POINT_1 + out_ch_map $CHANNEL_MAP_3_POINT_1 + } + ] + } + } + } + } } Object.Widget.pipeline."1" { @@ -881,8 +1016,14 @@ Object.PCM.pcm [ Object.PCM.pcm_caps.1 { name $DMIC0_PCM_CAPS - # only 32-bit capture supported now - formats 'S32_LE' + IncludeByKey.PCM_FORMAT_ALL { + "true" { + formats 'S16_LE,S24_LE,S32_LE,U8,FLOAT_LE' + } + "false" { + formats 'S32_LE' + } + } channels_min $DMIC0_PCM_CHANNELS channels_max $DMIC0_PCM_CHANNELS IncludeByKey.DMIC0_RATE { @@ -900,6 +1041,10 @@ Object.PCM.pcm [ } ] +IncludeByKey.DMIC0_DEEP_BUFFER { + "true" "platform/intel/dmic-deep-buffer.conf" +} + IncludeByKey.DMIC1_ENABLE { "passthrough" "platform/intel/dmic1-passthrough.conf" "mfcc" "platform/intel/dmic1-mfcc.conf" diff --git a/tools/topology/topology2/platform/intel/sdw-amp-dax.conf b/tools/topology/topology2/platform/intel/sdw-amp-dax.conf new file mode 100644 index 000000000000..f1d93d01e100 --- /dev/null +++ b/tools/topology/topology2/platform/intel/sdw-amp-dax.conf @@ -0,0 +1,742 @@ +# route and pipeline index start from pcm id * 10 + +Define { + SDW_SPK_STREAM 'SDW1-Playback' + SDW_SPK_IN_STREAM 'SDW1-Capture' + ALH_2ND_SPK_ID 22 + ALH_3RD_SPK_ID 23 + ALH_2ND_SPK_IN_ID 32 + ALH_3RD_SPK_IN_ID 33 + SDW_AMP_BE_ID 2 + SDW_AMP_IN_BE_ID 3 + AMP_FEEDBACK_CH 2 + AMP_FEEDBACK_CH_PER_LINK 2 + SDW_AMP_FEEDBACK true + AMP_PLAYBACK_NAME 'Speaker Playback' +} + +# include deep buffer config if buffer size is in 1 - 1000 ms. +IncludeByKey.DEEPBUFFER_FW_DMA_MS { + "([1-9]|[1-9][0-9]|[1-9][0-9][0-9]|1000)" { + IncludeByKey.DEEP_BUF_SPK { + "true" { + #deep-buffer-spk.conf is included in deep-buffer.conf + #and deep-buffer.conf is included if SDW_JACK is true. + #Therefore, only include deep-buffer-spk.conf when + #SDW_JACK is false to avoid duplicated. + IncludeByKey.SDW_JACK { + "false" "platform/intel/deep-buffer-spk.conf" + } + } + } + } +} + +Object.Dai.ALH [ + { + dai_index 20 + id $SDW_AMP_BE_ID + direction "playback" + name $SDW_SPK_STREAM + default_hw_conf_id 0 + rate 48000 + channels 2 + + Object.Base.hw_config.1 { + id 0 + name "ALH514" + } + } +] + +Object.Widget.module-copier."22" { + index 21 + num_input_pins 1 + num_output_pins 2 + num_input_audio_formats 1 + num_output_audio_formats 1 + Object.Base.input_audio_format [ + { + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + Object.Base.output_audio_format [ + { + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] +} + +Object.Pipeline { + host-copier-gain-mixin-playback [ + { + index 20 + + Object.Widget.host-copier.1 { + stream_name "sdw amplifiers" + pcm_id 2 + IncludeByKey.PCM_FORMAT_ALL { + "true" { + num_input_audio_formats 5 + Object.Base.input_audio_format [ + { + in_bit_depth 16 + in_valid_bit_depth 16 + } + { + in_bit_depth 32 + in_valid_bit_depth 32 + } + { + in_bit_depth 32 + in_valid_bit_depth 24 + } + { + in_bit_depth 32 + in_valid_bit_depth 32 + in_sample_type $SAMPLE_TYPE_FLOAT + } + { + in_bit_depth 8 + in_valid_bit_depth 8 + in_sample_type $SAMPLE_TYPE_UNSIGNED_INTEGER + } + ] + } + "false" { + num_input_audio_formats 3 + Object.Base.input_audio_format [ + { + in_bit_depth 16 + in_valid_bit_depth 16 + } + { + in_bit_depth 32 + in_valid_bit_depth 24 + } + { + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + } + } + } + Object.Widget.gain.1 { + Object.Control.mixer.1 { + name 'Pre Mixer $AMP_PLAYBACK_NAME Volume' + } + } + } + ] + + mixout-gain-dax-alh-dai-copier-playback [ + { + index 21 + + Object.Widget.alh-copier.1 { + stream_name $SDW_SPK_STREAM + node_type $ALH_LINK_OUTPUT_CLASS + num_input_audio_formats 3 + Object.Base.input_audio_format [ + { + in_bit_depth 16 + in_valid_bit_depth 16 + } + { + in_bit_depth 32 + in_valid_bit_depth 24 + } + { + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + num_output_audio_formats 1 + Object.Base.output_audio_format [ + { + out_bit_depth 32 + out_valid_bit_depth $SDW_LINK_VALID_BITS + out_sample_type $SAMPLE_TYPE_MSB_INTEGER + out_fmt_cfg "$[($out_channels | ($out_valid_bit_depth * 256))]" + } + ] + } + Object.Widget.gain.1 { + Object.Control.mixer.1 { + name 'Post Mixer $AMP_PLAYBACK_NAME Volume' + } + } + Object.Widget.dolby-dax.1 { + core_id $DOLBY_DAX_CORE_ID + Object.Control { + mixer."1" { + name 'DAX Speaker Switch' + } + mixer."2" { + name 'DAX Speaker Switch CP' + } + mixer."3" { + name 'DAX Speaker Switch CTC' + } + mixer."4" { + name 'DAX Speaker Volume' + } + enum."1" { + name 'DAX Speaker Profile' + } + enum."2" { + name 'DAX Speaker Device' + } + bytes."1" { + name 'DAX Speaker Tuning' + max 8192 + } + } + } + } + ] +} + +IncludeByKey.NUM_SDW_AMP_LINKS { +"2" { + Define { + AMP_FEEDBACK_CH 4 + AMP_FEEDBACK_CH_PER_LINK 2 + } + + Object.Widget { + alh-copier [ + { + index $ALH_2ND_SPK_ID + type dai_in + stream_name $SDW_SPK_STREAM + dai_index 1 + type "dai_in" + direction "playback" + node_type $ALH_LINK_OUTPUT_CLASS + num_input_audio_formats 1 + num_output_audio_formats 1 + num_input_pins 1 + Object.Base.input_audio_format [ + { + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + Object.Base.output_audio_format [ + { + out_bit_depth 32 + out_valid_bit_depth $SDW_LINK_VALID_BITS + out_sample_type $SAMPLE_TYPE_MSB_INTEGER + out_fmt_cfg "$[($out_channels | ($out_valid_bit_depth * 256))]" + } + ] + } + ] + IncludeByKey.SDW_AMP_FEEDBACK { + "true" { + alh-copier [ + { + index $ALH_2ND_SPK_IN_ID + type dai_out + stream_name $SDW_SPK_IN_STREAM + dai_index 1 + type "dai_out" + direction "capture" + node_type $ALH_LINK_INPUT_CLASS + num_input_audio_formats 1 + num_output_audio_formats 1 + num_output_pins 1 + + Object.Base.input_audio_format [ + { + in_channels $AMP_FEEDBACK_CH + in_bit_depth 32 + in_valid_bit_depth $SDW_LINK_VALID_BITS + in_sample_type $SAMPLE_TYPE_MSB_INTEGER + in_fmt_cfg "$[($in_channels | ($in_valid_bit_depth * 256))]" + } + ] + Object.Base.output_audio_format [ + { + out_channels $AMP_FEEDBACK_CH + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + } + ] + } + } + pipeline [ + { + index $ALH_2ND_SPK_ID + priority 0 + lp_mode 0 + dynamic_pipeline 1 + } + { + index $ALH_2ND_SPK_IN_ID + priority 0 + lp_mode 0 + dynamic_pipeline 1 + } + ] + # Add a virtual widget to connect the aggregated 2nd DAI copier + virtual [ + { + name 'virtual.sdw-amp' + type output + index $ALH_2ND_SPK_ID + } + ] + } + + Object.Base.route [ + { + source "gain.21.1" + sink "virtual.sdw-amp" + } + { + source "virtual.sdw-amp" + sink "alh-copier.$SDW_SPK_STREAM.1" + } + ] + } + +"3" { + Define { + AMP_FEEDBACK_CH 6 + AMP_FEEDBACK_CH_PER_LINK 2 + } + + Object.Widget { + alh-copier [ + { + index $ALH_2ND_SPK_ID + type dai_in + stream_name $SDW_SPK_STREAM + dai_index 1 + type "dai_in" + direction "playback" + node_type $ALH_LINK_OUTPUT_CLASS + num_input_audio_formats 1 + num_output_audio_formats 1 + num_input_pins 1 + Object.Base.input_audio_format [ + { + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + Object.Base.output_audio_format [ + { + out_bit_depth 32 + out_valid_bit_depth $SDW_LINK_VALID_BITS + out_sample_type $SAMPLE_TYPE_MSB_INTEGER + out_fmt_cfg "$[($out_channels | ($out_valid_bit_depth * 256))]" + } + ] + } + { + index $ALH_3RD_SPK_ID + type dai_in + stream_name $SDW_SPK_STREAM + dai_index 2 + type "dai_in" + direction "playback" + node_type $ALH_LINK_OUTPUT_CLASS + num_input_audio_formats 1 + num_output_audio_formats 1 + num_input_pins 1 + Object.Base.input_audio_format [ + { + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + Object.Base.output_audio_format [ + { + out_bit_depth 32 + out_valid_bit_depth $SDW_LINK_VALID_BITS + out_sample_type $SAMPLE_TYPE_MSB_INTEGER + out_fmt_cfg "$[($out_channels | ($out_valid_bit_depth * 256))]" + } + ] + } + ] + IncludeByKey.SDW_AMP_FEEDBACK { + "true" { + alh-copier [ + { + index $ALH_2ND_SPK_IN_ID + type dai_out + stream_name $SDW_SPK_IN_STREAM + dai_index 1 + type "dai_out" + direction "capture" + node_type $ALH_LINK_INPUT_CLASS + num_input_audio_formats 1 + num_output_audio_formats 1 + num_output_pins 1 + + Object.Base.input_audio_format [ + { + in_channels $AMP_FEEDBACK_CH + in_bit_depth 32 + in_valid_bit_depth $SDW_LINK_VALID_BITS + in_sample_type $SAMPLE_TYPE_MSB_INTEGER + in_fmt_cfg "$[($in_channels | ($in_valid_bit_depth * 256))]" + } + ] + Object.Base.output_audio_format [ + { + out_channels $AMP_FEEDBACK_CH + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + } + { + index $ALH_3RD_SPK_IN_ID + type dai_out + stream_name $SDW_SPK_IN_STREAM + dai_index 2 + type "dai_out" + direction "capture" + node_type $ALH_LINK_INPUT_CLASS + num_input_audio_formats 1 + num_output_audio_formats 1 + num_output_pins 1 + + Object.Base.input_audio_format [ + { + in_channels $AMP_FEEDBACK_CH + in_bit_depth 32 + in_valid_bit_depth $SDW_LINK_VALID_BITS + in_sample_type $SAMPLE_TYPE_MSB_INTEGER + in_fmt_cfg "$[($in_channels | ($in_valid_bit_depth * 256))]" + } + ] + Object.Base.output_audio_format [ + { + out_channels $AMP_FEEDBACK_CH + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + } + ] + } + } + pipeline [ + { + index $ALH_2ND_SPK_ID + priority 0 + lp_mode 0 + dynamic_pipeline 1 + } + { + index $ALH_3RD_SPK_ID + priority 0 + lp_mode 0 + dynamic_pipeline 1 + } + { + index $ALH_2ND_SPK_IN_ID + priority 0 + lp_mode 0 + dynamic_pipeline 1 + } + { + index $ALH_3RD_SPK_IN_ID + priority 0 + lp_mode 0 + dynamic_pipeline 1 + } + ] + virtual [ + { + name 'virtual.sdw-amp' + type output + index $ALH_2ND_SPK_ID + } + { + name 'virtual.sdw-amp' + type output + index $ALH_3RD_SPK_ID + } + ] + } + + Object.Base.route [ + { + source "gain.21.1" + sink "virtual.sdw-amp" + } + { + source "virtual.sdw-amp" + sink "alh-copier.$SDW_SPK_STREAM.1" + } + { + source "virtual.sdw-amp" + sink "alh-copier.$SDW_SPK_STREAM.2" + } + ] + } +} + +Object.PCM.pcm [ + { + name "Speaker" + id 2 + direction "playback" + Object.Base.fe_dai.1 { + name "Speaker" + } + + Object.PCM.pcm_caps.1 { + name "sdw amplifiers" + IncludeByKey.PCM_FORMAT_ALL { + "true" { + formats 'S16_LE,S24_LE,S32_LE,U8,FLOAT_LE' + } + "false" { + formats 'S16_LE,S24_LE,S32_LE' + } + } + } + } +] + +Object.Base.route [ + { + source "dolby-dax.21.1" + sink "module-copier.21.22" + } + { + source "module-copier.21.22" + sink "alh-copier.$SDW_SPK_STREAM.0" + } + { + source 'mixin.20.1' + sink 'mixout.21.1' + } + { + source 'host-copier.2.playback' + sink 'gain.20.1' + } +] + +IncludeByKey.SDW_AMP_FEEDBACK { + "true" { + Object.Dai.ALH [ + { + dai_index 30 + id $SDW_AMP_IN_BE_ID + direction "capture" + name $SDW_SPK_IN_STREAM + default_hw_conf_id 1 + rate 48000 + channels $AMP_FEEDBACK_CH_PER_LINK + + Object.Base.hw_config.1 { + id 1 + name "ALH515" + } + } + ] + Object.Pipeline { + host-gateway-capture [ + { + index 30 + + Object.Widget.host-copier.1 { + stream_name "amp feedback" + pcm_id 3 + + IncludeByKey.AMP_FEEDBACK_CH { + "6" { + num_input_audio_formats 1 + num_output_audio_formats 3 + Object.Base.input_audio_format [ + { + in_channels 6 + in_bit_depth 32 + in_valid_bit_depth 32 + in_ch_cfg $CHANNEL_CONFIG_5_POINT_1 + in_ch_map $CHANNEL_MAP_5_POINT_1 + } + ] + Object.Base.output_audio_format [ + { + out_channels 6 + out_bit_depth 32 + out_valid_bit_depth 24 + out_ch_cfg $CHANNEL_CONFIG_5_POINT_1 + out_ch_map $CHANNEL_MAP_5_POINT_1 + } + { + out_channels 6 + out_bit_depth 32 + out_valid_bit_depth 32 + out_ch_cfg $CHANNEL_CONFIG_5_POINT_1 + out_ch_map $CHANNEL_MAP_5_POINT_1 + } + { + out_channels 6 + out_bit_depth 16 + out_valid_bit_depth 16 + out_ch_cfg $CHANNEL_CONFIG_5_POINT_1 + out_ch_map $CHANNEL_MAP_5_POINT_1 + } + ] + } + "8" { + num_input_audio_formats 1 + num_output_audio_formats 3 + Object.Base.input_audio_format [ + { + in_channels 8 + in_bit_depth 32 + in_valid_bit_depth 32 + in_ch_cfg $CHANNEL_CONFIG_7_POINT_1 + in_ch_map $CHANNEL_MAP_7_POINT_1 + } + ] + Object.Base.output_audio_format [ + { + out_channels 8 + out_bit_depth 32 + out_valid_bit_depth 24 + out_ch_cfg $CHANNEL_CONFIG_7_POINT_1 + out_ch_map $CHANNEL_MAP_7_POINT_1 + } + { + out_channels 8 + out_bit_depth 32 + out_valid_bit_depth 32 + out_ch_cfg $CHANNEL_CONFIG_7_POINT_1 + out_ch_map $CHANNEL_MAP_7_POINT_1 + } + { + out_channels 8 + out_bit_depth 16 + out_valid_bit_depth 16 + out_ch_cfg $CHANNEL_CONFIG_7_POINT_1 + out_ch_map $CHANNEL_MAP_7_POINT_1 + } + ] + } + } + } + } + ] + } + Object.Widget { + alh-copier [ + { + index 31 + type dai_out + stream_name $SDW_SPK_IN_STREAM + type "dai_out" + direction "capture" + node_type $ALH_LINK_INPUT_CLASS + num_input_audio_formats 1 + num_output_audio_formats 1 + num_output_pins 1 + + Object.Base.input_audio_format [ + { + in_channels $AMP_FEEDBACK_CH + in_bit_depth 32 + in_valid_bit_depth $SDW_LINK_VALID_BITS + + IncludeByKey.AMP_FEEDBACK_CH { + "2" { + in_ch_cfg $CHANNEL_CONFIG_STEREO + in_ch_map $CHANNEL_MAP_STEREO + } + "4" { + in_ch_cfg $CHANNEL_CONFIG_3_POINT_1 + in_ch_map $CHANNEL_MAP_3_POINT_1 + } + "6" { + in_ch_cfg $CHANNEL_CONFIG_5_POINT_1 + in_ch_map $CHANNEL_MAP_5_POINT_1 + } + "8" { + in_ch_cfg $CHANNEL_CONFIG_7_POINT_1 + in_ch_map $CHANNEL_MAP_7_POINT_1 + } + } + + in_sample_type $SAMPLE_TYPE_MSB_INTEGER + in_fmt_cfg "$[($in_channels | ($in_valid_bit_depth * 256))]" + } + ] + Object.Base.output_audio_format [ + { + out_channels $AMP_FEEDBACK_CH + out_bit_depth 32 + out_valid_bit_depth 32 + + IncludeByKey.AMP_FEEDBACK_CH { + "2" { + out_ch_cfg $CHANNEL_CONFIG_STEREO + out_ch_map $CHANNEL_MAP_STEREO + } + "4" { + out_ch_cfg $CHANNEL_CONFIG_3_POINT_1 + out_ch_map $CHANNEL_MAP_3_POINT_1 + } + "6" { + out_ch_cfg $CHANNEL_CONFIG_5_POINT_1 + out_ch_map $CHANNEL_MAP_5_POINT_1 + } + "8" { + out_ch_cfg $CHANNEL_CONFIG_7_POINT_1 + out_ch_map $CHANNEL_MAP_7_POINT_1 + } + } + } + ] + } + ] + pipeline [ + { + index 31 + priority 0 + lp_mode 0 + dynamic_pipeline 1 + } + ] + } + Object.PCM.pcm [ + { + name "Amp feedback" + id 3 + direction "capture" + Object.Base.fe_dai.1 { + name "Amp feedback" + } + + Object.PCM.pcm_caps.1 { + name "amp feedback" + formats 'S16_LE,S24_LE,S32_LE' + channels_min $AMP_FEEDBACK_CH + channels_max $AMP_FEEDBACK_CH + } + } + ] + Object.Base.route [ + { + source "alh-copier.$SDW_SPK_IN_STREAM.0" + sink "host-copier.3.capture" + } + ] + } +} diff --git a/tools/topology/topology2/platform/intel/sdw-amp-dts.conf b/tools/topology/topology2/platform/intel/sdw-amp-dts.conf new file mode 100644 index 000000000000..60710c634782 --- /dev/null +++ b/tools/topology/topology2/platform/intel/sdw-amp-dts.conf @@ -0,0 +1,922 @@ +# route and pipeline index start from pcm id * 10 + +Define { + SDW_SPK_STREAM 'SDW1-Playback' + SDW_SPK_IN_STREAM 'SDW1-Capture' + ALH_2ND_SPK_ID 22 + ALH_3RD_SPK_ID 23 + ALH_2ND_SPK_IN_ID 32 + ALH_3RD_SPK_IN_ID 33 + SDW_AMP_BE_ID 2 + SDW_AMP_IN_BE_ID 3 + AMP_FEEDBACK_CH 2 + AMP_FEEDBACK_CH_PER_LINK 2 + SDW_AMP_FEEDBACK true + AMP_PLAYBACK_NAME 'Speaker Playback' +} + +# include deep buffer config if buffer size is in 1 - 1000 ms. +IncludeByKey.PASSTHROUGH { +"false" { + IncludeByKey.DEEPBUFFER_FW_DMA_MS { + "([1-9]|[1-9][0-9]|[1-9][0-9][0-9]|1000)" { + IncludeByKey.DEEP_BUF_SPK { + "true" { + #deep-buffer-spk.conf is included in deep-buffer.conf + #and deep-buffer.conf is included if SDW_JACK is true. + #Therefore, only include deep-buffer-spk.conf when + #SDW_JACK is false to avoid duplicated. + IncludeByKey.SDW_JACK { + "false" "platform/intel/deep-buffer-spk.conf" + } + } + } + } + } +} +} + +Object.Dai.ALH [ + { + dai_index 20 + id $SDW_AMP_BE_ID + direction "playback" + name $SDW_SPK_STREAM + default_hw_conf_id 0 + rate 48000 + channels 2 + + Object.Base.hw_config.1 { + id 0 + name "ALH514" + } + } +] + +Object.Widget.module-copier."22" { + index 21 + num_input_pins 1 + num_output_pins 2 + num_input_audio_formats 1 + num_output_audio_formats 1 + Object.Base.input_audio_format [ + { + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + Object.Base.output_audio_format [ + { + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] +} + +IncludeByKey.PASSTHROUGH { +"false" { + Object.Pipeline { + host-copier-gain-mixin-playback [ + { + index 20 + + Object.Widget.host-copier.1 { + stream_name "sdw amplifiers" + pcm_id 2 + IncludeByKey.PCM_FORMAT_ALL { + "true" { + num_input_audio_formats 5 + Object.Base.input_audio_format [ + { + in_bit_depth 16 + in_valid_bit_depth 16 + } + { + in_bit_depth 32 + in_valid_bit_depth 32 + } + { + in_bit_depth 32 + in_valid_bit_depth 24 + } + { + in_bit_depth 32 + in_valid_bit_depth 32 + in_sample_type $SAMPLE_TYPE_FLOAT + } + { + in_bit_depth 8 + in_valid_bit_depth 8 + in_sample_type $SAMPLE_TYPE_UNSIGNED_INTEGER + } + ] + } + "false" { + num_input_audio_formats 3 + Object.Base.input_audio_format [ + { + in_bit_depth 16 + in_valid_bit_depth 16 + } + { + in_bit_depth 32 + in_valid_bit_depth 24 + } + { + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + } + } + } + Object.Widget.gain.1 { + Object.Control.mixer.1 { + name 'Pre Mixer $AMP_PLAYBACK_NAME Volume' + } + } + } + ] + + IncludeByKey.SDW_SPK_ENHANCED_PLAYBACK { + "true" { + mixout-gain-eqiir-dts-alh-dai-copier-playback [ + { + index 21 + + Object.Widget.alh-copier.1 { + stream_name $SDW_SPK_STREAM + node_type $ALH_LINK_OUTPUT_CLASS + num_input_audio_formats 3 + Object.Base.input_audio_format [ + { + in_bit_depth 16 + in_valid_bit_depth 16 + } + { + in_bit_depth 32 + in_valid_bit_depth 24 + } + { + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + num_output_audio_formats 1 + Object.Base.output_audio_format [ + { + out_bit_depth 32 + out_valid_bit_depth $SDW_LINK_VALID_BITS + out_sample_type $SAMPLE_TYPE_MSB_INTEGER + out_fmt_cfg "$[($out_channels | ($out_valid_bit_depth * 256)) | ($out_sample_type * 65536)]" + } + ] + } + Object.Widget.gain.1 { + Object.Control.mixer.1 { + name 'Post Mixer $AMP_PLAYBACK_NAME Volume' + } + } + + Object.Widget.eqiir.1 { + Object.Control.bytes."1" { + name 'Post Mixer $AMP_PLAYBACK_NAME IIR Eq bytes' + } + } + + Object.Widget.dts.1 { + Object.Control { + bytes."1" { + name 'Post Mixer $AMP_PLAYBACK_NAME DTS bytes' + max 2048 + } + } + } + } + ] + } + "false" { + mixout-gain-alh-dai-copier-playback [ + { + index 21 + + Object.Widget.alh-copier.1 { + stream_name $SDW_SPK_STREAM + node_type $ALH_LINK_OUTPUT_CLASS + num_input_audio_formats 3 + Object.Base.input_audio_format [ + { + in_bit_depth 16 + in_valid_bit_depth 16 + } + { + in_bit_depth 32 + in_valid_bit_depth 24 + } + { + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + num_output_audio_formats 1 + Object.Base.output_audio_format [ + { + out_bit_depth 32 + out_valid_bit_depth $SDW_LINK_VALID_BITS + out_sample_type $SAMPLE_TYPE_MSB_INTEGER + out_fmt_cfg "$[($out_channels | ($out_valid_bit_depth * 256)) | ($out_sample_type * 65536)]" + } + ] + } + Object.Widget.gain.1 { + Object.Control.mixer.1 { + name 'Post Mixer $AMP_PLAYBACK_NAME Volume' + } + } + } + ] + } + } + } +} +"true" { + Object.Pipeline.host-gateway-playback [ + { + index 20 + Object.Widget.host-copier.1 { + stream_name "sdw amplifiers" + pcm_id 2 + num_input_audio_formats 3 + Object.Base.input_audio_format [ + { + in_bit_depth 16 + in_valid_bit_depth 16 + } + { + in_bit_depth 32 + in_valid_bit_depth 24 + } + { + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + num_output_audio_formats 3 + Object.Base.output_audio_format [ + { + out_bit_depth 16 + out_valid_bit_depth 16 + } + { + out_bit_depth 32 + out_valid_bit_depth 24 + } + { + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + } + } + ] + + Object.Widget { + alh-copier [ + { + stream_name $SDW_SPK_STREAM + node_type $ALH_LINK_OUTPUT_CLASS + num_input_pins 1 + direction playback + type dai_in + index 21 + num_input_audio_formats 3 + Object.Base.input_audio_format [ + { + in_bit_depth 16 + in_valid_bit_depth 16 + } + { + in_bit_depth 32 + in_valid_bit_depth 24 + } + { + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + num_output_audio_formats 1 + Object.Base.output_audio_format [ + { + out_bit_depth 32 + out_valid_bit_depth $SDW_LINK_VALID_BITS + out_sample_type $SAMPLE_TYPE_MSB_INTEGER + out_fmt_cfg "$[($out_channels | ($out_valid_bit_depth * 256))]" + } + ] + } + ] + pipeline [ + { + index 21 + priority 0 + lp_mode 0 + dynamic_pipeline 1 + } + ] + } + } +} + +IncludeByKey.NUM_SDW_AMP_LINKS { +"2" { + Define { + AMP_FEEDBACK_CH 4 + AMP_FEEDBACK_CH_PER_LINK 2 + } + + Object.Widget { + alh-copier [ + { + index $ALH_2ND_SPK_ID + stream_name $SDW_SPK_STREAM + dai_index 1 + type "dai_in" + direction "playback" + node_type $ALH_LINK_OUTPUT_CLASS + num_input_audio_formats 1 + num_output_audio_formats 1 + num_input_pins 1 + Object.Base.input_audio_format [ + { + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + Object.Base.output_audio_format [ + { + out_bit_depth 32 + out_valid_bit_depth $SDW_LINK_VALID_BITS + out_sample_type $SAMPLE_TYPE_MSB_INTEGER + out_fmt_cfg "$[($out_channels | ($out_valid_bit_depth * 256))]" + } + ] + } + ] + IncludeByKey.SDW_AMP_FEEDBACK { + "true" { + alh-copier [ + { + index $ALH_2ND_SPK_IN_ID + stream_name $SDW_SPK_IN_STREAM + dai_index 1 + type "dai_out" + direction "capture" + node_type $ALH_LINK_INPUT_CLASS + num_input_audio_formats 1 + num_output_audio_formats 1 + num_output_pins 1 + + Object.Base.input_audio_format [ + { + in_bit_depth 32 + in_valid_bit_depth $SDW_LINK_VALID_BITS + in_sample_type $SAMPLE_TYPE_MSB_INTEGER + in_fmt_cfg "$[($in_channels | ($in_valid_bit_depth * 256))]" + } + ] + Object.Base.output_audio_format [ + { + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + } + ] + } + } + pipeline [ + { + index $ALH_2ND_SPK_ID + priority 0 + lp_mode 0 + dynamic_pipeline 1 + } + { + index $ALH_2ND_SPK_IN_ID + priority 0 + lp_mode 0 + dynamic_pipeline 1 + } + ] + # Add a virtual widget to connect the aggregated 2nd DAI copier + virtual [ + { + name 'virtual.sdw-amp' + type output + index $ALH_2ND_SPK_ID + } + ] + } + + # Add the connection from the gain module to the aggregated 2nd DAI copier + # via the virtual widget. The virtual widget ensures that the routes between + # the gain and copier do not get established in the firmware. These are purely + # to show the existence of aggregation in the topology graph. + IncludeByKey.PASSTHROUGH { + "false" { + Object.Base.route [ + { + source "gain.21.1" + sink "virtual.sdw-amp" + } + ] + } + "true" { + Object.Base.route [ + { + source "host-copier.2.playback" + sink "virtual.sdw-amp" + } + ] + } + } + Object.Base.route [ + { + source "virtual.sdw-amp" + sink "alh-copier.$SDW_SPK_STREAM.1" + } + ] + } + +"3" { + Define { + AMP_FEEDBACK_CH 6 + AMP_FEEDBACK_CH_PER_LINK 2 + } + + Object.Widget { + alh-copier [ + { + index $ALH_2ND_SPK_ID + stream_name $SDW_SPK_STREAM + dai_index 1 + type "dai_in" + direction "playback" + node_type $ALH_LINK_OUTPUT_CLASS + num_input_audio_formats 1 + num_output_audio_formats 1 + num_input_pins 1 + Object.Base.input_audio_format [ + { + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + Object.Base.output_audio_format [ + { + out_bit_depth 32 + out_valid_bit_depth $SDW_LINK_VALID_BITS + out_sample_type $SAMPLE_TYPE_MSB_INTEGER + out_fmt_cfg "$[($out_channels | ($out_valid_bit_depth * 256))]" + } + ] + } + { + index $ALH_3RD_SPK_ID + stream_name $SDW_SPK_STREAM + dai_index 2 + type "dai_in" + direction "playback" + node_type $ALH_LINK_OUTPUT_CLASS + num_input_audio_formats 1 + num_output_audio_formats 1 + num_input_pins 1 + Object.Base.input_audio_format [ + { + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + Object.Base.output_audio_format [ + { + out_bit_depth 32 + out_valid_bit_depth $SDW_LINK_VALID_BITS + out_sample_type $SAMPLE_TYPE_MSB_INTEGER + out_fmt_cfg "$[($out_channels | ($out_valid_bit_depth * 256))]" + } + ] + } + ] + IncludeByKey.SDW_AMP_FEEDBACK { + "true" { + alh-copier [ + { + index $ALH_2ND_SPK_IN_ID + stream_name $SDW_SPK_IN_STREAM + dai_index 1 + type "dai_out" + direction "capture" + node_type $ALH_LINK_INPUT_CLASS + num_input_audio_formats 1 + num_output_audio_formats 1 + num_output_pins 1 + + Object.Base.input_audio_format [ + { + in_bit_depth 32 + in_valid_bit_depth $SDW_LINK_VALID_BITS + in_sample_type $SAMPLE_TYPE_MSB_INTEGER + in_fmt_cfg "$[($in_channels | ($in_valid_bit_depth * 256))]" + } + ] + Object.Base.output_audio_format [ + { + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + } + { + index $ALH_3RD_SPK_IN_ID + stream_name $SDW_SPK_IN_STREAM + dai_index 2 + type "dai_out" + direction "capture" + node_type $ALH_LINK_INPUT_CLASS + num_input_audio_formats 1 + num_output_audio_formats 1 + num_output_pins 1 + + Object.Base.input_audio_format [ + { + in_bit_depth 32 + in_valid_bit_depth $SDW_LINK_VALID_BITS + in_sample_type $SAMPLE_TYPE_MSB_INTEGER + in_fmt_cfg "$[($in_channels | ($in_valid_bit_depth * 256))]" + } + ] + Object.Base.output_audio_format [ + { + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + } + ] + } + } + pipeline [ + { + index $ALH_2ND_SPK_ID + priority 0 + lp_mode 0 + dynamic_pipeline 1 + } + { + index $ALH_3RD_SPK_ID + priority 0 + lp_mode 0 + dynamic_pipeline 1 + } + { + index $ALH_2ND_SPK_IN_ID + priority 0 + lp_mode 0 + dynamic_pipeline 1 + } + { + index $ALH_3RD_SPK_IN_ID + priority 0 + lp_mode 0 + dynamic_pipeline 1 + } + ] + virtual [ + { + name 'virtual.sdw-amp' + type output + index $ALH_2ND_SPK_ID + } + { + name 'virtual.sdw-amp' + type output + index $ALH_3RD_SPK_ID + } + ] + } + + # Add the connection from the gain module to the aggregated 2nd DAI copier + # via the virtual widget. The virtual widget ensures that the routes between + # the gain and copier do not get established in the firmware. These are purely + # to show the existence of aggregation in the topology graph. + IncludeByKey.PASSTHROUGH { + "false" { + Object.Base.route [ + { + source "gain.21.1" + sink "virtual.sdw-amp" + } + ] + } + "true" { + Object.Base.route [ + { + source "host-copier.2.playback" + sink "virtual.sdw-amp" + } + ] + } + } + Object.Base.route [ + { + source "virtual.sdw-amp" + sink "alh-copier.$SDW_SPK_STREAM.1" + } + { + source "virtual.sdw-amp" + sink "alh-copier.$SDW_SPK_STREAM.2" + } + ] + } +} + +Object.PCM.pcm [ + { + name "Speaker" + id 2 + direction "playback" + Object.Base.fe_dai.1 { + name "Speaker" + } + + Object.PCM.pcm_caps.1 { + name "sdw amplifiers" + IncludeByKey.PCM_FORMAT_ALL { + "true" { + formats 'S16_LE,S24_LE,S32_LE,U8,FLOAT_LE' + } + "false" { + formats 'S16_LE,S24_LE,S32_LE' + } + } + } + } +] + +IncludeByKey.PASSTHROUGH { +"false" { + IncludeByKey.SDW_SPK_ENHANCED_PLAYBACK { + "true" { + Object.Base.route [ + { + source "dts.21.1" + sink "module-copier.21.22" + } + { + source "module-copier.21.22" + sink "alh-copier.$SDW_SPK_STREAM.0" + } + ] + } + "false" { + Object.Base.route [ + { + source "gain.21.1" + sink "module-copier.21.22" + } + { + source "module-copier.21.22" + sink "alh-copier.$SDW_SPK_STREAM.0" + } + ] + } + } + Object.Base.route [ + { + source 'mixin.20.1' + sink 'mixout.21.1' + } + { + source 'host-copier.2.playback' + sink 'gain.20.1' + } + ] +} +"true" { + Object.Base.route [ + { + source "host-copier.2.playback" + sink "alh-copier.$SDW_SPK_STREAM.0" + } + ] +} +} + +IncludeByKey.SDW_AMP_FEEDBACK { + "true" { + Object.Dai.ALH [ + { + dai_index 30 + id $SDW_AMP_IN_BE_ID + direction "capture" + name $SDW_SPK_IN_STREAM + default_hw_conf_id 1 + rate 48000 + channels $AMP_FEEDBACK_CH_PER_LINK + + Object.Base.hw_config.1 { + id 1 + name "ALH515" + } + } + ] + Object.Pipeline { + host-gateway-capture [ + { + index 30 + + Object.Widget.host-copier.1 { + stream_name "amp feedback" + pcm_id 3 + + IncludeByKey.AMP_FEEDBACK_CH { + "6" { + num_input_audio_formats 1 + num_output_audio_formats 3 + Object.Base.input_audio_format [ + { + in_channels 6 + in_bit_depth 32 + in_valid_bit_depth 32 + in_ch_cfg $CHANNEL_CONFIG_5_POINT_1 + in_ch_map $CHANNEL_MAP_5_POINT_1 + } + ] + Object.Base.output_audio_format [ + { + out_channels 6 + out_bit_depth 32 + out_valid_bit_depth 24 + out_ch_cfg $CHANNEL_CONFIG_5_POINT_1 + out_ch_map $CHANNEL_MAP_5_POINT_1 + } + { + out_channels 6 + out_bit_depth 32 + out_valid_bit_depth 32 + out_ch_cfg $CHANNEL_CONFIG_5_POINT_1 + out_ch_map $CHANNEL_MAP_5_POINT_1 + } + { + out_channels 6 + out_bit_depth 16 + out_valid_bit_depth 16 + out_ch_cfg $CHANNEL_CONFIG_5_POINT_1 + out_ch_map $CHANNEL_MAP_5_POINT_1 + } + ] + } + "8" { + num_input_audio_formats 1 + num_output_audio_formats 3 + Object.Base.input_audio_format [ + { + in_channels 8 + in_bit_depth 32 + in_valid_bit_depth 32 + in_ch_cfg $CHANNEL_CONFIG_7_POINT_1 + in_ch_map $CHANNEL_MAP_7_POINT_1 + } + ] + Object.Base.output_audio_format [ + { + out_channels 8 + out_bit_depth 32 + out_valid_bit_depth 24 + out_ch_cfg $CHANNEL_CONFIG_7_POINT_1 + out_ch_map $CHANNEL_MAP_7_POINT_1 + } + { + out_channels 8 + out_bit_depth 32 + out_valid_bit_depth 32 + out_ch_cfg $CHANNEL_CONFIG_7_POINT_1 + out_ch_map $CHANNEL_MAP_7_POINT_1 + } + { + out_channels 8 + out_bit_depth 16 + out_valid_bit_depth 16 + out_ch_cfg $CHANNEL_CONFIG_7_POINT_1 + out_ch_map $CHANNEL_MAP_7_POINT_1 + } + ] + } + } + } + } + ] + } + Object.Widget { + alh-copier [ + { + index 31 + stream_name $SDW_SPK_IN_STREAM + type "dai_out" + direction "capture" + node_type $ALH_LINK_INPUT_CLASS + num_input_audio_formats 1 + num_output_audio_formats 1 + num_output_pins 1 + + Object.Base.input_audio_format [ + { + in_channels $AMP_FEEDBACK_CH + in_bit_depth 32 + in_valid_bit_depth $SDW_LINK_VALID_BITS + + IncludeByKey.AMP_FEEDBACK_CH { + "2" { + in_ch_cfg $CHANNEL_CONFIG_STEREO + in_ch_map $CHANNEL_MAP_STEREO + } + "4" { + in_ch_cfg $CHANNEL_CONFIG_3_POINT_1 + in_ch_map $CHANNEL_MAP_3_POINT_1 + } + "6" { + in_ch_cfg $CHANNEL_CONFIG_5_POINT_1 + in_ch_map $CHANNEL_MAP_5_POINT_1 + } + "8" { + in_ch_cfg $CHANNEL_CONFIG_7_POINT_1 + in_ch_map $CHANNEL_MAP_7_POINT_1 + } + } + + in_sample_type $SAMPLE_TYPE_MSB_INTEGER + in_fmt_cfg "$[($in_channels | ($in_valid_bit_depth * 256))]" + } + ] + Object.Base.output_audio_format [ + { + out_channels $AMP_FEEDBACK_CH + out_bit_depth 32 + out_valid_bit_depth 32 + + IncludeByKey.AMP_FEEDBACK_CH { + "2" { + out_ch_cfg $CHANNEL_CONFIG_STEREO + out_ch_map $CHANNEL_MAP_STEREO + } + "4" { + out_ch_cfg $CHANNEL_CONFIG_3_POINT_1 + out_ch_map $CHANNEL_MAP_3_POINT_1 + } + "6" { + out_ch_cfg $CHANNEL_CONFIG_5_POINT_1 + out_ch_map $CHANNEL_MAP_5_POINT_1 + } + "8" { + out_ch_cfg $CHANNEL_CONFIG_7_POINT_1 + out_ch_map $CHANNEL_MAP_7_POINT_1 + } + } + } + ] + } + ] + pipeline [ + { + index 31 + priority 0 + lp_mode 0 + dynamic_pipeline 1 + } + ] + } + Object.PCM.pcm [ + { + name "Amp feedback" + id 3 + direction "capture" + Object.Base.fe_dai.1 { + name "Amp feedback" + } + + Object.PCM.pcm_caps.1 { + name "amp feedback" + formats 'S16_LE,S24_LE,S32_LE' + channels_min $AMP_FEEDBACK_CH + channels_max $AMP_FEEDBACK_CH + } + } + ] + Object.Base.route [ + { + source "alh-copier.$SDW_SPK_IN_STREAM.0" + sink "host-copier.3.capture" + } + ] + } +} diff --git a/tools/topology/topology2/platform/intel/sdw-amp-echo-ref.conf b/tools/topology/topology2/platform/intel/sdw-amp-echo-ref.conf new file mode 100644 index 000000000000..abde20519f2e --- /dev/null +++ b/tools/topology/topology2/platform/intel/sdw-amp-echo-ref.conf @@ -0,0 +1,52 @@ +Define { + SDW_SPK_ECHO_REF_PCM_ID 12 + SDW_SPK_ECHO_REF_PIPELINE_ID 120 +} + +IncludeByKey.SDW_SPK_ECHO_REF { + "true" { + Object.Pipeline { + siggen-host-copier-capture [ + { + direction "capture" + index $SDW_SPK_ECHO_REF_PIPELINE_ID + Object.Widget.host-copier."1" { + stream_name "Speaker Echo Reference" + pcm_id $SDW_SPK_ECHO_REF_PCM_ID + } + Object.Widget.siggen."1" {} + } + ] + } + + Object.PCM.pcm [ + { + name "Speaker Echo Reference" + id $SDW_SPK_ECHO_REF_PCM_ID + direction "capture" + Object.Base.fe_dai.1 { + name "Speaker Echo Reference" + } + Object.PCM.pcm_caps.1 { + name "Speaker Echo Reference" + formats 'S16_LE,S24_LE,S32_LE' + } + } + ] + + Object.Base.route [ + { + source "alh-copier.Loopback_Virtual.25" + sink "siggen.$SDW_SPK_ECHO_REF_PIPELINE_ID.1" + } + { + source "module-copier.21.22" + sink "siggen.$SDW_SPK_ECHO_REF_PIPELINE_ID.1" + } + { + source "siggen.$SDW_SPK_ECHO_REF_PIPELINE_ID.1" + sink "host-copier.$SDW_SPK_ECHO_REF_PCM_ID.capture" + } + ] + } # SDW_SPK_ECHO_REF true +} diff --git a/tools/topology/topology2/platform/intel/sdw-amp-generic-ctc.conf b/tools/topology/topology2/platform/intel/sdw-amp-generic-ctc.conf new file mode 100644 index 000000000000..dd78692f1aa5 --- /dev/null +++ b/tools/topology/topology2/platform/intel/sdw-amp-generic-ctc.conf @@ -0,0 +1,853 @@ +# route and pipeline index start from pcm id * 10 + +Define { + SDW_SPK_STREAM 'SDW1-Playback' + SDW_SPK_IN_STREAM 'SDW1-Capture' + ALH_2ND_SPK_ID 22 + ALH_3RD_SPK_ID 23 + ALH_2ND_SPK_IN_ID 32 + ALH_3RD_SPK_IN_ID 33 + SDW_AMP_BE_ID 2 + SDW_AMP_IN_BE_ID 3 + AMP_FEEDBACK_CH 2 + AMP_FEEDBACK_CH_PER_LINK 2 + SDW_AMP_FEEDBACK true + AMP_PLAYBACK_NAME 'Speaker Playback' +} + +Object.Dai.ALH [ + { + dai_index 20 + id $SDW_AMP_BE_ID + direction "playback" + name $SDW_SPK_STREAM + default_hw_conf_id 0 + rate 48000 + channels 2 + + Object.Base.hw_config.1 { + id 0 + name "ALH514" + } + } +] + +IncludeByKey.PASSTHROUGH { +"false" { + Object.Pipeline { + host-copier-gain-mixin-playback [ + { + index 20 + + Object.Widget.host-copier.1 { + stream_name "sdw amplifiers" + pcm_id 2 + } + Object.Widget.gain.1 { + Object.Control.mixer.1 { + name 'Pre Mixer $AMP_PLAYBACK_NAME Volume' + } + } + } + ] + + IncludeByKey.SDW_SPK_ENHANCED_PLAYBACK { + "true" { + mixout-gain-eqiir-eqfir-drc-alh-dai-copier-playback [ + { + index 21 + + Object.Widget.alh-copier.1 { + stream_name $SDW_SPK_STREAM + node_type $ALH_LINK_OUTPUT_CLASS + num_input_audio_formats 3 + Object.Base.input_audio_format [ + { + in_bit_depth 16 + in_valid_bit_depth 16 + } + { + in_bit_depth 32 + in_valid_bit_depth 24 + } + { + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + num_output_audio_formats 1 + Object.Base.output_audio_format [ + { + out_bit_depth 32 + out_valid_bit_depth $SDW_LINK_VALID_BITS + out_sample_type $SAMPLE_TYPE_MSB_INTEGER + out_fmt_cfg "$[($out_channels | ($out_valid_bit_depth * 256))]" + } + ] + } + Object.Widget.gain.1 { + Object.Control.mixer.1 { + name 'Post Mixer $AMP_PLAYBACK_NAME Volume' + } + } + + Object.Widget.eqiir.1 { + Object.Control.bytes."1" { + name 'Post Mixer $AMP_PLAYBACK_NAME IIR Eq bytes' + } + } + Object.Widget.eqfir.1 { + Object.Control.bytes."1" { + name 'Post Mixer $AMP_PLAYBACK_NAME FIR Eq bytes' + } + } + Object.Widget.drc.1 { + Object.Control { + bytes."1" { + name 'Post Mixer $AMP_PLAYBACK_NAME DRC bytes' + } + mixer."1" { + name 'Post Mixer $AMP_PLAYBACK_NAME DRC switch' + } + } + } + } + ] + } + "false" { + mixout-gain-ctc-alh-dai-copier-playback [ + { + index 21 + + Object.Widget.alh-copier.1 { + stream_name $SDW_SPK_STREAM + node_type $ALH_LINK_OUTPUT_CLASS + num_input_audio_formats 3 + Object.Base.input_audio_format [ + { + in_bit_depth 16 + in_valid_bit_depth 16 + } + { + in_bit_depth 32 + in_valid_bit_depth 24 + } + { + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + num_output_audio_formats 1 + Object.Base.output_audio_format [ + { + out_bit_depth 32 + out_valid_bit_depth $SDW_LINK_VALID_BITS + out_sample_type $SAMPLE_TYPE_MSB_INTEGER + out_fmt_cfg "$[($out_channels | ($out_valid_bit_depth * 256))]" + } + ] + } + Object.Widget.gain.1 { + Object.Control.mixer.1 { + name 'Post Mixer $AMP_PLAYBACK_NAME Volume' + } + } + Object.Widget.ctc.1 { + Object.Control { + mixer."1" { + name 'CTC Switch' + } + bytes."1" { + name 'CTC.0' + max 4160 + } + } + } + } + ] + } + } + + } + } +"true" { + Object.Pipeline.host-gateway-playback [ + { + index 20 + Object.Widget.host-copier.1 { + stream_name "sdw amplifiers" + pcm_id 2 + num_input_audio_formats 3 + Object.Base.input_audio_format [ + { + in_bit_depth 16 + in_valid_bit_depth 16 + } + { + in_bit_depth 32 + in_valid_bit_depth 24 + } + { + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + num_output_audio_formats 3 + Object.Base.output_audio_format [ + { + out_bit_depth 16 + out_valid_bit_depth 16 + } + { + out_bit_depth 32 + out_valid_bit_depth 24 + } + { + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + } + } + ] + + Object.Widget { + alh-copier [ + { + stream_name $SDW_SPK_STREAM + node_type $ALH_LINK_OUTPUT_CLASS + num_input_pins 1 + direction playback + type dai_in + index 21 + num_input_audio_formats 3 + Object.Base.input_audio_format [ + { + in_bit_depth 16 + in_valid_bit_depth 16 + } + { + in_bit_depth 32 + in_valid_bit_depth 24 + } + { + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + num_output_audio_formats 1 + Object.Base.output_audio_format [ + { + out_bit_depth 32 + out_valid_bit_depth $SDW_LINK_VALID_BITS + out_sample_type $SAMPLE_TYPE_MSB_INTEGER + out_fmt_cfg "$[($out_channels | ($out_valid_bit_depth * 256))]" + } + ] + } + ] + pipeline [ + { + index 21 + priority 0 + lp_mode 0 + dynamic_pipeline 1 + } + ] + } + } +} + +IncludeByKey.NUM_SDW_AMP_LINKS { +"2" { + Define { + AMP_FEEDBACK_CH 4 + AMP_FEEDBACK_CH_PER_LINK 2 + } + + Object.Widget { + alh-copier [ + { + index $ALH_2ND_SPK_ID + type dai_in + stream_name $SDW_SPK_STREAM + dai_index 1 + type "dai_in" + direction "playback" + node_type $ALH_LINK_OUTPUT_CLASS + num_input_audio_formats 1 + num_output_audio_formats 1 + num_input_pins 1 + Object.Base.input_audio_format [ + { + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + Object.Base.output_audio_format [ + { + out_bit_depth 32 + out_valid_bit_depth $SDW_LINK_VALID_BITS + out_sample_type $SAMPLE_TYPE_MSB_INTEGER + out_fmt_cfg "$[($out_channels | ($out_valid_bit_depth * 256))]" + } + ] + } + ] + IncludeByKey.SDW_AMP_FEEDBACK { + "true" { + alh-copier [ + { + index $ALH_2ND_SPK_IN_ID + type dai_out + stream_name $SDW_SPK_IN_STREAM + dai_index 1 + type "dai_out" + direction "capture" + node_type $ALH_LINK_INPUT_CLASS + num_input_audio_formats 1 + num_output_audio_formats 1 + num_output_pins 1 + + Object.Base.input_audio_format [ + { + in_bit_depth 32 + in_valid_bit_depth $SDW_LINK_VALID_BITS + in_sample_type $SAMPLE_TYPE_MSB_INTEGER + in_fmt_cfg "$[($in_channels | ($in_valid_bit_depth * 256))]" + } + ] + Object.Base.output_audio_format [ + { + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + } + ] + } + } + pipeline [ + { + index $ALH_2ND_SPK_ID + priority 0 + lp_mode 0 + dynamic_pipeline 1 + } + { + index $ALH_2ND_SPK_IN_ID + priority 0 + lp_mode 0 + dynamic_pipeline 1 + } + ] + # Add a virtual widget to connect the aggregated 2nd DAI copier + virtual [ + { + name 'virtual.sdw-amp' + type output + index $ALH_2ND_SPK_ID + } + ] + } + + # Add the connection from the gain module to the aggregated 2nd DAI copier + # via the virtual widget. The virtual widget ensures that the routes between + # the gain and copier do not get established in the firmware. These are purely + # to show the existence of aggregation in the topology graph. + IncludeByKey.PASSTHROUGH { + "false" { + Object.Base.route [ + { + source "gain.21.1" + sink "virtual.sdw-amp" + } + ] + } + "true" { + Object.Base.route [ + { + source "host-copier.2.playback" + sink "virtual.sdw-amp" + } + ] + } + } + Object.Base.route [ + { + source "virtual.sdw-amp" + sink "ctc.21.1" + } + { + source "ctc.21.1" + sink "alh-copier.$SDW_SPK_STREAM.1" + } + ] + } + +"3" { + Define { + AMP_FEEDBACK_CH 6 + AMP_FEEDBACK_CH_PER_LINK 2 + } + + Object.Widget { + alh-copier [ + { + index $ALH_2ND_SPK_ID + type dai_in + stream_name $SDW_SPK_STREAM + dai_index 1 + type "dai_in" + direction "playback" + node_type $ALH_LINK_OUTPUT_CLASS + num_input_audio_formats 1 + num_output_audio_formats 1 + num_input_pins 1 + Object.Base.input_audio_format [ + { + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + Object.Base.output_audio_format [ + { + out_bit_depth 32 + out_valid_bit_depth $SDW_LINK_VALID_BITS + out_sample_type $SAMPLE_TYPE_MSB_INTEGER + out_fmt_cfg "$[($out_channels | ($out_valid_bit_depth * 256))]" + } + ] + } + { + index $ALH_3RD_SPK_ID + type dai_in + stream_name $SDW_SPK_STREAM + dai_index 2 + type "dai_in" + direction "playback" + node_type $ALH_LINK_OUTPUT_CLASS + num_input_audio_formats 1 + num_output_audio_formats 1 + num_input_pins 1 + Object.Base.input_audio_format [ + { + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + Object.Base.output_audio_format [ + { + out_bit_depth 32 + out_valid_bit_depth $SDW_LINK_VALID_BITS + out_sample_type $SAMPLE_TYPE_MSB_INTEGER + out_fmt_cfg "$[($out_channels | ($out_valid_bit_depth * 256))]" + } + ] + } + ] + IncludeByKey.SDW_AMP_FEEDBACK { + "true" { + alh-copier [ + { + index $ALH_2ND_SPK_IN_ID + type dai_out + stream_name $SDW_SPK_IN_STREAM + dai_index 1 + type "dai_out" + direction "capture" + node_type $ALH_LINK_INPUT_CLASS + num_input_audio_formats 1 + num_output_audio_formats 1 + num_output_pins 1 + + Object.Base.input_audio_format [ + { + in_bit_depth 32 + in_valid_bit_depth $SDW_LINK_VALID_BITS + in_sample_type $SAMPLE_TYPE_MSB_INTEGER + in_fmt_cfg "$[($in_channels | ($in_valid_bit_depth * 256))]" + } + ] + Object.Base.output_audio_format [ + { + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + } + { + index $ALH_3RD_SPK_IN_ID + type dai_out + stream_name $SDW_SPK_IN_STREAM + dai_index 2 + type "dai_out" + direction "capture" + node_type $ALH_LINK_INPUT_CLASS + num_input_audio_formats 1 + num_output_audio_formats 1 + num_output_pins 1 + + Object.Base.input_audio_format [ + { + in_bit_depth 32 + in_valid_bit_depth $SDW_LINK_VALID_BITS + in_sample_type $SAMPLE_TYPE_MSB_INTEGER + in_fmt_cfg "$[($in_channels | ($in_valid_bit_depth * 256))]" + } + ] + Object.Base.output_audio_format [ + { + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + } + ] + } + } + pipeline [ + { + index $ALH_2ND_SPK_ID + priority 0 + lp_mode 0 + dynamic_pipeline 1 + } + { + index $ALH_3RD_SPK_ID + priority 0 + lp_mode 0 + dynamic_pipeline 1 + } + { + index $ALH_2ND_SPK_IN_ID + priority 0 + lp_mode 0 + dynamic_pipeline 1 + } + { + index $ALH_3RD_SPK_IN_ID + priority 0 + lp_mode 0 + dynamic_pipeline 1 + } + ] + virtual [ + { + name 'virtual.sdw-amp' + type output + index $ALH_2ND_SPK_ID + } + { + name 'virtual.sdw-amp' + type output + index $ALH_3RD_SPK_ID + } + ] + } + + # Add the connection from the gain module to the aggregated 2nd DAI copier + # via the virtual widget. The virtual widget ensures that the routes between + # the gain and copier do not get established in the firmware. These are purely + # to show the existence of aggregation in the topology graph. + IncludeByKey.PASSTHROUGH { + "false" { + Object.Base.route [ + { + source "gain.21.1" + sink "virtual.sdw-amp" + } + ] + } + "true" { + Object.Base.route [ + { + source "host-copier.2.playback" + sink "virtual.sdw-amp" + } + ] + } + } + Object.Base.route [ + { + source "virtual.sdw-amp" + sink "ctc.21.1" + } + { + source "ctc.21.1" + sink "alh-copier.$SDW_SPK_STREAM.1" + } + { + source "ctc.21.1" + sink "alh-copier.$SDW_SPK_STREAM.2" + } + ] + } +} + +Object.PCM.pcm [ + { + name "Speaker" + id 2 + direction "playback" + Object.Base.fe_dai.1 { + name "Speaker" + } + + Object.PCM.pcm_caps.1 { + name "sdw amplifiers" + formats 'S16_LE,S24_LE,S32_LE' + } + } +] + +IncludeByKey.PASSTHROUGH { +"false" { + IncludeByKey.SDW_SPK_ENHANCED_PLAYBACK { + "true" { + Object.Base.route [ + { + source "drc.21.1" + sink "alh-copier.$SDW_SPK_STREAM.0" + } + ] + } + "false" { + Object.Base.route [ + { + source "gain.21.1" + sink "alh-copier.$SDW_SPK_STREAM.0" + } + ] + } + } + Object.Base.route [ + { + source 'mixin.20.1' + sink 'mixout.21.1' + } + { + source 'host-copier.2.playback' + sink 'gain.20.1' + } + ] +} +"true" { + Object.Base.route [ + { + source "host-copier.2.playback" + sink "alh-copier.$SDW_SPK_STREAM.0" + } + ] +} +} + +IncludeByKey.SDW_AMP_FEEDBACK { + "true" { + Object.Dai.ALH [ + { + dai_index 30 + id $SDW_AMP_IN_BE_ID + direction "capture" + name $SDW_SPK_IN_STREAM + default_hw_conf_id 1 + rate 48000 + channels $AMP_FEEDBACK_CH_PER_LINK + + Object.Base.hw_config.1 { + id 1 + name "ALH515" + } + } + ] + Object.Pipeline { + host-gateway-capture [ + { + index 30 + + Object.Widget.host-copier.1 { + stream_name "amp feedback" + pcm_id 3 + + IncludeByKey.AMP_FEEDBACK_CH { + "6" { + num_input_audio_formats 1 + num_output_audio_formats 3 + Object.Base.input_audio_format [ + { + in_channels 6 + in_bit_depth 32 + in_valid_bit_depth 32 + in_ch_cfg $CHANNEL_CONFIG_5_POINT_1 + in_ch_map $CHANNEL_MAP_5_POINT_1 + } + ] + Object.Base.output_audio_format [ + { + out_channels 6 + out_bit_depth 32 + out_valid_bit_depth 24 + out_ch_cfg $CHANNEL_CONFIG_5_POINT_1 + out_ch_map $CHANNEL_MAP_5_POINT_1 + } + { + out_channels 6 + out_bit_depth 32 + out_valid_bit_depth 32 + out_ch_cfg $CHANNEL_CONFIG_5_POINT_1 + out_ch_map $CHANNEL_MAP_5_POINT_1 + } + { + out_channels 6 + out_bit_depth 16 + out_valid_bit_depth 16 + out_ch_cfg $CHANNEL_CONFIG_5_POINT_1 + out_ch_map $CHANNEL_MAP_5_POINT_1 + } + ] + } + "8" { + num_input_audio_formats 1 + num_output_audio_formats 3 + Object.Base.input_audio_format [ + { + in_channels 8 + in_bit_depth 32 + in_valid_bit_depth 32 + in_ch_cfg $CHANNEL_CONFIG_7_POINT_1 + in_ch_map $CHANNEL_MAP_7_POINT_1 + } + ] + Object.Base.output_audio_format [ + { + out_channels 8 + out_bit_depth 32 + out_valid_bit_depth 24 + out_ch_cfg $CHANNEL_CONFIG_7_POINT_1 + out_ch_map $CHANNEL_MAP_7_POINT_1 + } + { + out_channels 8 + out_bit_depth 32 + out_valid_bit_depth 32 + out_ch_cfg $CHANNEL_CONFIG_7_POINT_1 + out_ch_map $CHANNEL_MAP_7_POINT_1 + } + { + out_channels 8 + out_bit_depth 16 + out_valid_bit_depth 16 + out_ch_cfg $CHANNEL_CONFIG_7_POINT_1 + out_ch_map $CHANNEL_MAP_7_POINT_1 + } + ] + } + } + } + } + ] + } + Object.Widget { + alh-copier [ + { + index 31 + type dai_out + stream_name $SDW_SPK_IN_STREAM + type "dai_out" + direction "capture" + node_type $ALH_LINK_INPUT_CLASS + num_input_audio_formats 1 + num_output_audio_formats 1 + num_output_pins 1 + + Object.Base.input_audio_format [ + { + in_channels $AMP_FEEDBACK_CH + in_bit_depth 32 + in_valid_bit_depth $SDW_LINK_VALID_BITS + + IncludeByKey.AMP_FEEDBACK_CH { + "2" { + in_ch_cfg $CHANNEL_CONFIG_STEREO + in_ch_map $CHANNEL_MAP_STEREO + } + "4" { + in_ch_cfg $CHANNEL_CONFIG_3_POINT_1 + in_ch_map $CHANNEL_MAP_3_POINT_1 + } + "6" { + in_ch_cfg $CHANNEL_CONFIG_5_POINT_1 + in_ch_map $CHANNEL_MAP_5_POINT_1 + } + "8" { + in_ch_cfg $CHANNEL_CONFIG_7_POINT_1 + in_ch_map $CHANNEL_MAP_7_POINT_1 + } + } + + in_sample_type $SAMPLE_TYPE_MSB_INTEGER + in_fmt_cfg "$[($in_channels | ($in_valid_bit_depth * 256))]" + } + ] + Object.Base.output_audio_format [ + { + out_channels $AMP_FEEDBACK_CH + out_bit_depth 32 + out_valid_bit_depth 32 + + IncludeByKey.AMP_FEEDBACK_CH { + "2" { + out_ch_cfg $CHANNEL_CONFIG_STEREO + out_ch_map $CHANNEL_MAP_STEREO + } + "4" { + out_ch_cfg $CHANNEL_CONFIG_3_POINT_1 + out_ch_map $CHANNEL_MAP_3_POINT_1 + } + "6" { + out_ch_cfg $CHANNEL_CONFIG_5_POINT_1 + out_ch_map $CHANNEL_MAP_5_POINT_1 + } + "8" { + out_ch_cfg $CHANNEL_CONFIG_7_POINT_1 + out_ch_map $CHANNEL_MAP_7_POINT_1 + } + } + } + ] + } + ] + pipeline [ + { + index 31 + priority 0 + lp_mode 0 + dynamic_pipeline 1 + } + ] + } + Object.PCM.pcm [ + { + name "Amp feedback" + id 3 + direction "capture" + Object.Base.fe_dai.1 { + name "Amp feedback" + } + + Object.PCM.pcm_caps.1 { + name "amp feedback" + formats 'S16_LE,S24_LE,S32_LE' + channels_min $AMP_FEEDBACK_CH + channels_max $AMP_FEEDBACK_CH + } + } + ] + Object.Base.route [ + { + source "alh-copier.$SDW_SPK_IN_STREAM.0" + sink "host-copier.3.capture" + } + ] + } +} diff --git a/tools/topology/topology2/platform/intel/sdw-amp-generic.conf b/tools/topology/topology2/platform/intel/sdw-amp-generic.conf index 303db9515253..d4913ca7dd4f 100644 --- a/tools/topology/topology2/platform/intel/sdw-amp-generic.conf +++ b/tools/topology/topology2/platform/intel/sdw-amp-generic.conf @@ -15,6 +15,27 @@ Define { AMP_PLAYBACK_NAME 'Speaker Playback' } +# include deep buffer config if buffer size is in 1 - 1000 ms. +IncludeByKey.PASSTHROUGH { +"false" { + IncludeByKey.DEEPBUFFER_FW_DMA_MS { + "([1-9]|[1-9][0-9]|[1-9][0-9][0-9]|1000)" { + IncludeByKey.DEEP_BUF_SPK { + "true" { + #deep-buffer-spk.conf is included in deep-buffer.conf + #and deep-buffer.conf is included if SDW_JACK is true. + #Therefore, only include deep-buffer-spk.conf when + #SDW_JACK is false to avoid duplicated. + IncludeByKey.SDW_JACK { + "false" "platform/intel/deep-buffer-spk.conf" + } + } + } + } + } +} +} + Object.Dai.ALH [ { dai_index 20 @@ -32,6 +53,26 @@ Object.Dai.ALH [ } ] +Object.Widget.module-copier."22" { + index 21 + num_input_pins 1 + num_output_pins 2 + num_input_audio_formats 1 + num_output_audio_formats 1 + Object.Base.input_audio_format [ + { + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + Object.Base.output_audio_format [ + { + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] +} + IncludeByKey.PASSTHROUGH { "false" { Object.Pipeline { @@ -42,6 +83,52 @@ IncludeByKey.PASSTHROUGH { Object.Widget.host-copier.1 { stream_name "sdw amplifiers" pcm_id 2 + IncludeByKey.PCM_FORMAT_ALL { + "true" { + num_input_audio_formats 5 + Object.Base.input_audio_format [ + { + in_bit_depth 16 + in_valid_bit_depth 16 + } + { + in_bit_depth 32 + in_valid_bit_depth 32 + } + { + in_bit_depth 32 + in_valid_bit_depth 24 + } + { + in_bit_depth 32 + in_valid_bit_depth 32 + in_sample_type $SAMPLE_TYPE_FLOAT + } + { + in_bit_depth 8 + in_valid_bit_depth 8 + in_sample_type $SAMPLE_TYPE_UNSIGNED_INTEGER + } + ] + } + "false" { + num_input_audio_formats 3 + Object.Base.input_audio_format [ + { + in_bit_depth 16 + in_valid_bit_depth 16 + } + { + in_bit_depth 32 + in_valid_bit_depth 24 + } + { + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + } + } } Object.Widget.gain.1 { Object.Control.mixer.1 { @@ -300,6 +387,7 @@ IncludeByKey.NUM_SDW_AMP_LINKS { Object.Base.input_audio_format [ { + in_channels $AMP_FEEDBACK_CH in_bit_depth 32 in_valid_bit_depth $SDW_LINK_VALID_BITS in_sample_type $SAMPLE_TYPE_MSB_INTEGER @@ -308,6 +396,7 @@ IncludeByKey.NUM_SDW_AMP_LINKS { ] Object.Base.output_audio_format [ { + out_channels $AMP_FEEDBACK_CH out_bit_depth 32 out_valid_bit_depth 32 } @@ -448,6 +537,7 @@ IncludeByKey.NUM_SDW_AMP_LINKS { Object.Base.input_audio_format [ { + in_channels $AMP_FEEDBACK_CH in_bit_depth 32 in_valid_bit_depth $SDW_LINK_VALID_BITS in_sample_type $SAMPLE_TYPE_MSB_INTEGER @@ -456,6 +546,7 @@ IncludeByKey.NUM_SDW_AMP_LINKS { ] Object.Base.output_audio_format [ { + out_channels $AMP_FEEDBACK_CH out_bit_depth 32 out_valid_bit_depth 32 } @@ -475,6 +566,7 @@ IncludeByKey.NUM_SDW_AMP_LINKS { Object.Base.input_audio_format [ { + in_channels $AMP_FEEDBACK_CH in_bit_depth 32 in_valid_bit_depth $SDW_LINK_VALID_BITS in_sample_type $SAMPLE_TYPE_MSB_INTEGER @@ -483,6 +575,7 @@ IncludeByKey.NUM_SDW_AMP_LINKS { ] Object.Base.output_audio_format [ { + out_channels $AMP_FEEDBACK_CH out_bit_depth 32 out_valid_bit_depth 32 } @@ -577,50 +670,65 @@ Object.PCM.pcm [ Object.PCM.pcm_caps.1 { name "sdw amplifiers" - formats 'S16_LE,S24_LE,S32_LE' + IncludeByKey.PCM_FORMAT_ALL { + "true" { + formats 'S16_LE,S24_LE,S32_LE,U8,FLOAT_LE' + } + "false" { + formats 'S16_LE,S24_LE,S32_LE' + } + } } } ] IncludeByKey.PASSTHROUGH { -"false" { - IncludeByKey.SDW_SPK_ENHANCED_PLAYBACK { - "true" { - Object.Base.route [ - { - source "drc.21.1" - sink "alh-copier.$SDW_SPK_STREAM.0" - } - ] - } - "false" { - Object.Base.route [ - { - source "gain.21.1" - sink "alh-copier.$SDW_SPK_STREAM.0" - } - ] - } - } - Object.Base.route [ - { - source 'mixin.20.1' - sink 'mixout.21.1' - } - { - source 'host-copier.2.playback' - sink 'gain.20.1' - } - ] -} -"true" { - Object.Base.route [ - { - source "host-copier.2.playback" - sink "alh-copier.$SDW_SPK_STREAM.0" + "false" { + IncludeByKey.SDW_SPK_ENHANCED_PLAYBACK { + "true" { + Object.Base.route [ + { + source "drc.21.1" + sink "module-copier.21.22" + } + { + source "module-copier.21.22" + sink "alh-copier.$SDW_SPK_STREAM.0" + } + ] + } + "false" { + Object.Base.route [ + { + source "gain.21.1" + sink "module-copier.21.22" + } + { + source "module-copier.21.22" + sink "alh-copier.$SDW_SPK_STREAM.0" + } + ] + } } - ] -} + Object.Base.route [ + { + source 'mixin.20.1' + sink 'mixout.21.1' + } + { + source 'host-copier.2.playback' + sink 'gain.20.1' + } + ] + } + "true" { + Object.Base.route [ + { + source "host-copier.2.playback" + sink "alh-copier.$SDW_SPK_STREAM.0" + } + ] + } } IncludeByKey.SDW_AMP_FEEDBACK { diff --git a/tools/topology/topology2/platform/intel/sdw-amp-xover.conf b/tools/topology/topology2/platform/intel/sdw-amp-xover.conf new file mode 100644 index 000000000000..3347d4911bc4 --- /dev/null +++ b/tools/topology/topology2/platform/intel/sdw-amp-xover.conf @@ -0,0 +1,1003 @@ +# route and pipeline index start from pcm id * 10 + +Define { + SDW_SPK_STREAM 'SDW1-Playback' + SDW_SPK_IN_STREAM 'SDW1-Capture' + ALH_2ND_SPK_ID 22 + ALH_3RD_SPK_ID 23 + ALH_2ND_SPK_IN_ID 32 + ALH_3RD_SPK_IN_ID 33 + SDW_AMP_BE_ID 2 + SDW_AMP_IN_BE_ID 3 + AMP_FEEDBACK_CH 2 + AMP_FEEDBACK_CH_PER_LINK 2 + SDW_AMP_FEEDBACK true + AMP_PLAYBACK_NAME 'Speaker Playback' +} + +Object.Dai.ALH [ + { + dai_index 20 + id $SDW_AMP_BE_ID + direction "playback" + name $SDW_SPK_STREAM + default_hw_conf_id 0 + rate 48000 + + Object.Base.hw_config.1 { + id 0 + name "ALH514" + } + } +] + +Object.Pipeline { + host-copier-gain-mixin-playback [ + { + index 20 + + Object.Widget.host-copier.1 { + stream_name "sdw amplifiers" + pcm_id 2 + } + Object.Widget.gain.1 { + Object.Control.mixer.1 { + name 'Pre Mixer $AMP_PLAYBACK_NAME Volume' + } + } + } + ] + + IncludeByKey.SDW_SPK_ENHANCED_PLAYBACK { + "true" { + mixout-gain-eqiir-eqfir-drc-micsel-eqiir-eqfir-alh-dai-copier-playback [ + { + index 21 + Object.Widget.alh-copier.1 { + stream_name $SDW_SPK_STREAM + node_type $ALH_LINK_OUTPUT_CLASS + num_input_audio_formats 1 + Object.Base.input_audio_format [ + { + in_channels $SDW_AMP_NUM_CHANNELS + IncludeByKey.SDW_AMP_NUM_CHANNELS { + "2" { + in_ch_cfg $CHANNEL_CONFIG_STEREO + in_ch_map $CHANNEL_MAP_STEREO + } + "4" { + in_ch_cfg $CHANNEL_CONFIG_3_POINT_1 + in_ch_map $CHANNEL_MAP_3_POINT_1 + } + } + in_bit_depth 32 + in_valid_bit_depth 32 + in_fmt_cfg "$[($in_channels | ($in_valid_bit_depth * 256))]" + } + ] + num_output_audio_formats 1 + Object.Base.output_audio_format [ + { + out_channels $SDW_AMP_NUM_CHANNELS + IncludeByKey.SDW_AMP_NUM_CHANNELS { + "2" { + out_ch_cfg $CHANNEL_CONFIG_STEREO + out_ch_map $CHANNEL_MAP_STEREO + } + "4" { + out_ch_cfg $CHANNEL_CONFIG_3_POINT_1 + out_ch_map $CHANNEL_MAP_3_POINT_1 + } + } + out_bit_depth 32 + out_valid_bit_depth $SDW_LINK_VALID_BITS + out_sample_type $SAMPLE_TYPE_MSB_INTEGER + out_fmt_cfg "$[($out_channels | ($out_valid_bit_depth * 256))]" + } + ] + } + Object.Widget.gain.1 { + Object.Control.mixer.1 { + name 'Post Mixer $AMP_PLAYBACK_NAME Volume' + } + } + Object.Widget.eqiir.1 { + Object.Control.bytes."1" { + name 'Post Mixer $AMP_PLAYBACK_NAME IIR Eq bytes' + } + } + Object.Widget.eqfir.1 { + Object.Control.bytes."1" { + name 'Post Mixer $AMP_PLAYBACK_NAME FIR Eq bytes' + } + } + Object.Widget.drc.1 { + Object.Control { + bytes."1" { + name 'Post Mixer $AMP_PLAYBACK_NAME DRC bytes' + } + mixer."1" { + name 'Post Mixer $AMP_PLAYBACK_NAME DRC switch' + } + } + } + Object.Widget.micsel.1 { + num_input_audio_formats 1 + Object.Base.input_audio_format [ + { + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + num_output_audio_formats 1 + Object.Base.output_audio_format [ + { + IncludeByKey.SDW_AMP_NUM_CHANNELS { + "2" { + out_ch_cfg $CHANNEL_CONFIG_STEREO + out_ch_map $CHANNEL_MAP_STEREO + } + "4" { + out_ch_cfg $CHANNEL_CONFIG_3_POINT_1 + out_ch_map $CHANNEL_MAP_3_POINT_1 + } + } + out_channels $SDW_AMP_NUM_CHANNELS + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + + Object.Control.bytes."1" { + name 'Post Mixer $AMP_PLAYBACK_NAME Xover Selector bytes' + } + } + Object.Widget.eqiir.2 { + num_input_audio_formats 1 + Object.Base.input_audio_format [ + { + IncludeByKey.SDW_AMP_NUM_CHANNELS { + "2" { + in_ch_cfg $CHANNEL_CONFIG_STEREO + in_ch_map $CHANNEL_MAP_STEREO + } + "4" { + in_ch_cfg $CHANNEL_CONFIG_3_POINT_1 + in_ch_map $CHANNEL_MAP_3_POINT_1 + } + } + in_channels $SDW_AMP_NUM_CHANNELS + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + num_output_audio_formats 1 + Object.Base.output_audio_format [ + { + IncludeByKey.SDW_AMP_NUM_CHANNELS { + "2" { + out_ch_cfg $CHANNEL_CONFIG_STEREO + out_ch_map $CHANNEL_MAP_STEREO + } + "4" { + out_ch_cfg $CHANNEL_CONFIG_3_POINT_1 + out_ch_map $CHANNEL_MAP_3_POINT_1 + } + } + out_channels $SDW_AMP_NUM_CHANNELS + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + Object.Control.bytes."1" { + name 'Post Mixer $AMP_PLAYBACK_NAME Xover IIR bytes' + } + } + Object.Widget.eqfir.2 { + num_input_audio_formats 1 + Object.Base.input_audio_format [ + { + IncludeByKey.SDW_AMP_NUM_CHANNELS { + "2" { + in_ch_cfg $CHANNEL_CONFIG_STEREO + in_ch_map $CHANNEL_MAP_STEREO + } + "4" { + in_ch_cfg $CHANNEL_CONFIG_3_POINT_1 + in_ch_map $CHANNEL_MAP_3_POINT_1 + } + } + in_channels $SDW_AMP_NUM_CHANNELS + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + num_output_audio_formats 1 + Object.Base.output_audio_format [ + { + IncludeByKey.SDW_AMP_NUM_CHANNELS { + "2" { + out_ch_cfg $CHANNEL_CONFIG_STEREO + out_ch_map $CHANNEL_MAP_STEREO + } + "4" { + out_ch_cfg $CHANNEL_CONFIG_3_POINT_1 + out_ch_map $CHANNEL_MAP_3_POINT_1 + } + } + out_channels $SDW_AMP_NUM_CHANNELS + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + Object.Control.bytes."1" { + name 'Post Mixer $AMP_PLAYBACK_NAME Xover FIR bytes' + } + } + } + ] + } + "false" { + mixout-gain-micsel-eqiir-eqfir-alh-dai-copier-playback [ + { + index 21 + Object.Widget.alh-copier.1 { + stream_name $SDW_SPK_STREAM + node_type $ALH_LINK_OUTPUT_CLASS + num_input_audio_formats 1 + Object.Base.input_audio_format [ + { + in_channels $SDW_AMP_NUM_CHANNELS + IncludeByKey.SDW_AMP_NUM_CHANNELS { + "2" { + in_ch_cfg $CHANNEL_CONFIG_STEREO + in_ch_map $CHANNEL_MAP_STEREO + } + "4" { + in_ch_cfg $CHANNEL_CONFIG_3_POINT_1 + in_ch_map $CHANNEL_MAP_3_POINT_1 + } + } + in_bit_depth 32 + in_valid_bit_depth 32 + in_fmt_cfg "$[($in_channels | ($in_valid_bit_depth * 256))]" + } + ] + num_output_audio_formats 1 + Object.Base.output_audio_format [ + { + out_channels $SDW_AMP_NUM_CHANNELS + IncludeByKey.SDW_AMP_NUM_CHANNELS { + "2" { + out_ch_cfg $CHANNEL_CONFIG_STEREO + out_ch_map $CHANNEL_MAP_STEREO + } + "4" { + out_ch_cfg $CHANNEL_CONFIG_3_POINT_1 + out_ch_map $CHANNEL_MAP_3_POINT_1 + } + } + out_bit_depth 32 + out_valid_bit_depth $SDW_LINK_VALID_BITS + out_sample_type $SAMPLE_TYPE_MSB_INTEGER + out_fmt_cfg "$[($out_channels | ($out_valid_bit_depth * 256))]" + } + ] + } + Object.Widget.gain.1 { + Object.Control.mixer.1 { + name 'Post Mixer $AMP_PLAYBACK_NAME Volume' + } + } + Object.Widget.micsel.1 { + num_input_audio_formats 1 + Object.Base.input_audio_format [ + { + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + num_output_audio_formats 1 + Object.Base.output_audio_format [ + { + IncludeByKey.SDW_AMP_NUM_CHANNELS { + "2" { + out_ch_cfg $CHANNEL_CONFIG_STEREO + out_ch_map $CHANNEL_MAP_STEREO + } + "4" { + out_ch_cfg $CHANNEL_CONFIG_3_POINT_1 + out_ch_map $CHANNEL_MAP_3_POINT_1 + } + } + out_channels $SDW_AMP_NUM_CHANNELS + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + + Object.Control.bytes."1" { + name 'Post Mixer $AMP_PLAYBACK_NAME Xover Selector bytes' + } + } + Object.Widget.eqiir.2 { + num_input_audio_formats 1 + Object.Base.input_audio_format [ + { + IncludeByKey.SDW_AMP_NUM_CHANNELS { + "2" { + in_ch_cfg $CHANNEL_CONFIG_STEREO + in_ch_map $CHANNEL_MAP_STEREO + } + "4" { + in_ch_cfg $CHANNEL_CONFIG_3_POINT_1 + in_ch_map $CHANNEL_MAP_3_POINT_1 + } + } + in_channels $SDW_AMP_NUM_CHANNELS + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + num_output_audio_formats 1 + Object.Base.output_audio_format [ + { + IncludeByKey.SDW_AMP_NUM_CHANNELS { + "2" { + out_ch_cfg $CHANNEL_CONFIG_STEREO + out_ch_map $CHANNEL_MAP_STEREO + } + "4" { + out_ch_cfg $CHANNEL_CONFIG_3_POINT_1 + out_ch_map $CHANNEL_MAP_3_POINT_1 + } + } + out_channels $SDW_AMP_NUM_CHANNELS + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + Object.Control.bytes."1" { + name 'Post Mixer $AMP_PLAYBACK_NAME Xover IIR bytes' + } + } + Object.Widget.eqfir.2 { + num_input_audio_formats 1 + Object.Base.input_audio_format [ + { + IncludeByKey.SDW_AMP_NUM_CHANNELS { + "2" { + in_ch_cfg $CHANNEL_CONFIG_STEREO + in_ch_map $CHANNEL_MAP_STEREO + } + "4" { + in_ch_cfg $CHANNEL_CONFIG_3_POINT_1 + in_ch_map $CHANNEL_MAP_3_POINT_1 + } + } + in_channels $SDW_AMP_NUM_CHANNELS + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + num_output_audio_formats 1 + Object.Base.output_audio_format [ + { + IncludeByKey.SDW_AMP_NUM_CHANNELS { + "2" { + out_ch_cfg $CHANNEL_CONFIG_STEREO + out_ch_map $CHANNEL_MAP_STEREO + } + "4" { + out_ch_cfg $CHANNEL_CONFIG_3_POINT_1 + out_ch_map $CHANNEL_MAP_3_POINT_1 + } + } + out_channels $SDW_AMP_NUM_CHANNELS + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + Object.Control.bytes."1" { + name 'Post Mixer $AMP_PLAYBACK_NAME Xover FIR bytes' + } + } + } + ] + } + } +} + +IncludeByKey.NUM_SDW_AMP_LINKS { +"2" { + Define { + AMP_FEEDBACK_CH 4 + AMP_FEEDBACK_CH_PER_LINK 2 + } + + Object.Widget { + alh-copier [ + { + index $ALH_2ND_SPK_ID + type dai_in + stream_name $SDW_SPK_STREAM + dai_index 1 + type "dai_in" + direction "playback" + node_type $ALH_LINK_OUTPUT_CLASS + num_input_audio_formats 1 + num_output_audio_formats 1 + num_input_pins 1 + Object.Base.input_audio_format [ + { + IncludeByKey.SDW_AMP_NUM_CHANNELS { + "2" { + in_ch_cfg $CHANNEL_CONFIG_STEREO + in_ch_map $CHANNEL_MAP_STEREO + } + "4" { + in_ch_cfg $CHANNEL_CONFIG_3_POINT_1 + in_ch_map $CHANNEL_MAP_3_POINT_1 + } + } + in_bit_depth 32 + in_valid_bit_depth 32 + in_fmt_cfg "$[($in_channels | ($in_valid_bit_depth * 256))]" + } + ] + Object.Base.output_audio_format [ + { + IncludeByKey.SDW_AMP_NUM_CHANNELS { + "2" { + out_ch_cfg $CHANNEL_CONFIG_STEREO + out_ch_map $CHANNEL_MAP_STEREO + } + "4" { + out_ch_cfg $CHANNEL_CONFIG_3_POINT_1 + out_ch_map $CHANNEL_MAP_3_POINT_1 + } + } + out_bit_depth 32 + out_valid_bit_depth $SDW_LINK_VALID_BITS + out_sample_type $SAMPLE_TYPE_MSB_INTEGER + out_fmt_cfg "$[($out_channels | ($out_valid_bit_depth * 256))]" + } + ] + } + ] + IncludeByKey.SDW_AMP_FEEDBACK { + "true" { + alh-copier [ + { + index $ALH_2ND_SPK_IN_ID + type dai_out + stream_name $SDW_SPK_IN_STREAM + dai_index 1 + type "dai_out" + direction "capture" + node_type $ALH_LINK_INPUT_CLASS + num_input_audio_formats 1 + num_output_audio_formats 1 + num_output_pins 1 + + Object.Base.input_audio_format [ + { + in_channels $AMP_FEEDBACK_CH + in_bit_depth 32 + in_valid_bit_depth $SDW_LINK_VALID_BITS + in_sample_type $SAMPLE_TYPE_MSB_INTEGER + in_fmt_cfg "$[($in_channels | ($in_valid_bit_depth * 256))]" + } + ] + Object.Base.output_audio_format [ + { + out_channels $AMP_FEEDBACK_CH + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + } + ] + } + } + pipeline [ + { + index $ALH_2ND_SPK_ID + priority 0 + lp_mode 0 + dynamic_pipeline 1 + } + { + index $ALH_2ND_SPK_IN_ID + priority 0 + lp_mode 0 + dynamic_pipeline 1 + } + ] + # Add a virtual widget to connect the aggregated 2nd DAI copier + virtual [ + { + name 'virtual.sdw-amp' + type output + index $ALH_2ND_SPK_ID + } + ] + } + + # Add the connection from the gain module to the aggregated 2nd DAI copier + # via the virtual widget. The virtual widget ensures that the routes between + # the gain and copier do not get established in the firmware. These are purely + # to show the existence of aggregation in the topology graph. + Object.Base.route [ + { + source "eqfir.21.2" + sink "virtual.sdw-amp" + } + { + source "virtual.sdw-amp" + sink "alh-copier.$SDW_SPK_STREAM.1" + } + ] + } + +"3" { + Define { + AMP_FEEDBACK_CH 6 + AMP_FEEDBACK_CH_PER_LINK 2 + } + + Object.Widget { + alh-copier [ + { + index $ALH_2ND_SPK_ID + type dai_in + stream_name $SDW_SPK_STREAM + dai_index 1 + type "dai_in" + direction "playback" + node_type $ALH_LINK_OUTPUT_CLASS + num_input_audio_formats 1 + num_output_audio_formats 1 + num_input_pins 1 + Object.Base.input_audio_format [ + { + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + Object.Base.output_audio_format [ + { + out_bit_depth 32 + out_valid_bit_depth $SDW_LINK_VALID_BITS + out_sample_type $SAMPLE_TYPE_MSB_INTEGER + out_fmt_cfg "$[($out_channels | ($out_valid_bit_depth * 256))]" + } + ] + } + { + index $ALH_3RD_SPK_ID + type dai_in + stream_name $SDW_SPK_STREAM + dai_index 2 + type "dai_in" + direction "playback" + node_type $ALH_LINK_OUTPUT_CLASS + num_input_audio_formats 1 + num_output_audio_formats 1 + num_input_pins 1 + Object.Base.input_audio_format [ + { + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + Object.Base.output_audio_format [ + { + out_bit_depth 32 + out_valid_bit_depth $SDW_LINK_VALID_BITS + out_sample_type $SAMPLE_TYPE_MSB_INTEGER + out_fmt_cfg "$[($out_channels | ($out_valid_bit_depth * 256))]" + } + ] + } + ] + IncludeByKey.SDW_AMP_FEEDBACK { + "true" { + alh-copier [ + { + index $ALH_2ND_SPK_IN_ID + type dai_out + stream_name $SDW_SPK_IN_STREAM + dai_index 1 + type "dai_out" + direction "capture" + node_type $ALH_LINK_INPUT_CLASS + num_input_audio_formats 1 + num_output_audio_formats 1 + num_output_pins 1 + + Object.Base.input_audio_format [ + { + in_channels $AMP_FEEDBACK_CH + in_bit_depth 32 + in_valid_bit_depth $SDW_LINK_VALID_BITS + in_sample_type $SAMPLE_TYPE_MSB_INTEGER + in_fmt_cfg "$[($in_channels | ($in_valid_bit_depth * 256))]" + } + ] + Object.Base.output_audio_format [ + { + out_channels $AMP_FEEDBACK_CH + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + } + { + index $ALH_3RD_SPK_IN_ID + type dai_out + stream_name $SDW_SPK_IN_STREAM + dai_index 2 + type "dai_out" + direction "capture" + node_type $ALH_LINK_INPUT_CLASS + num_input_audio_formats 1 + num_output_audio_formats 1 + num_output_pins 1 + + Object.Base.input_audio_format [ + { + in_bit_depth 32 + in_valid_bit_depth $SDW_LINK_VALID_BITS + in_sample_type $SAMPLE_TYPE_MSB_INTEGER + in_channels $AMP_FEEDBACK_CH + in_fmt_cfg "$[($in_channels | ($in_valid_bit_depth * 256))]" + } + ] + Object.Base.output_audio_format [ + { + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + } + ] + } + } + pipeline [ + { + index $ALH_2ND_SPK_ID + priority 0 + lp_mode 0 + dynamic_pipeline 1 + } + { + index $ALH_3RD_SPK_ID + priority 0 + lp_mode 0 + dynamic_pipeline 1 + } + { + index $ALH_2ND_SPK_IN_ID + priority 0 + lp_mode 0 + dynamic_pipeline 1 + } + { + index $ALH_3RD_SPK_IN_ID + priority 0 + lp_mode 0 + dynamic_pipeline 1 + } + ] + virtual [ + { + name 'virtual.sdw-amp' + type output + index $ALH_2ND_SPK_ID + } + { + name 'virtual.sdw-amp' + type output + index $ALH_3RD_SPK_ID + } + ] + } + + # Add the connection from the gain module to the aggregated 2nd DAI copier + # via the virtual widget. The virtual widget ensures that the routes between + # the gain and copier do not get established in the firmware. These are purely + # to show the existence of aggregation in the topology graph. + Object.Base.route [ + { + source "gain.21.1" + sink "virtual.sdw-amp" + } + { + source "virtual.sdw-amp" + sink "alh-copier.$SDW_SPK_STREAM.1" + } + { + source "virtual.sdw-amp" + sink "alh-copier.$SDW_SPK_STREAM.2" + } + ] + } +} + +Object.PCM.pcm [ + { + name "Speaker" + id 2 + direction "playback" + Object.Base.fe_dai.1 { + name "Speaker" + } + + Object.PCM.pcm_caps.1 { + name "sdw amplifiers" + formats 'S16_LE,S24_LE,S32_LE' + } + } +] + +Object.Base.route [ + { + source "eqfir.21.2" + sink "alh-copier.$SDW_SPK_STREAM.0" + } + { + source 'mixin.20.1' + sink 'mixout.21.1' + } + { + source 'host-copier.2.playback' + sink 'gain.20.1' + } +] + +IncludeByKey.SDW_AMP_FEEDBACK { + "true" { + Object.Dai.ALH [ + { + dai_index 30 + id $SDW_AMP_IN_BE_ID + direction "capture" + name $SDW_SPK_IN_STREAM + default_hw_conf_id 1 + rate 48000 + channels $AMP_FEEDBACK_CH_PER_LINK + + Object.Base.hw_config.1 { + id 1 + name "ALH515" + } + } + ] + Object.Pipeline { + host-gateway-capture [ + { + index 30 + + Object.Widget.host-copier.1 { + stream_name "amp feedback" + pcm_id 3 + + IncludeByKey.AMP_FEEDBACK_CH { + "4" { + num_input_audio_formats 1 + num_output_audio_formats 3 + Object.Base.input_audio_format [ + { + in_channels 4 + in_bit_depth 32 + in_valid_bit_depth 32 + in_ch_cfg $CHANNEL_CONFIG_3_POINT_1 + in_ch_map $CHANNEL_MAP_3_POINT_1 + } + ] + Object.Base.output_audio_format [ + { + out_channels 4 + out_bit_depth 32 + out_valid_bit_depth 24 + out_ch_cfg $CHANNEL_CONFIG_3_POINT_1 + out_ch_map $CHANNEL_MAP_3_POINT_1 + } + { + out_channels 4 + out_bit_depth 32 + out_valid_bit_depth 32 + out_ch_cfg $CHANNEL_CONFIG_3_POINT_1 + out_ch_map $CHANNEL_MAP_3_POINT_1 + } + { + out_channels 4 + out_bit_depth 16 + out_valid_bit_depth 16 + out_ch_cfg $CHANNEL_CONFIG_3_POINT_1 + out_ch_map $CHANNEL_MAP_3_POINT_1 + } + ] + } + "6" { + num_input_audio_formats 1 + num_output_audio_formats 3 + Object.Base.input_audio_format [ + { + in_channels 6 + in_bit_depth 32 + in_valid_bit_depth 32 + in_ch_cfg $CHANNEL_CONFIG_5_POINT_1 + in_ch_map $CHANNEL_MAP_5_POINT_1 + } + ] + Object.Base.output_audio_format [ + { + out_channels 6 + out_bit_depth 32 + out_valid_bit_depth 24 + out_ch_cfg $CHANNEL_CONFIG_5_POINT_1 + out_ch_map $CHANNEL_MAP_5_POINT_1 + } + { + out_channels 6 + out_bit_depth 32 + out_valid_bit_depth 32 + out_ch_cfg $CHANNEL_CONFIG_5_POINT_1 + out_ch_map $CHANNEL_MAP_5_POINT_1 + } + { + out_channels 6 + out_bit_depth 16 + out_valid_bit_depth 16 + out_ch_cfg $CHANNEL_CONFIG_5_POINT_1 + out_ch_map $CHANNEL_MAP_5_POINT_1 + } + ] + } + "8" { + num_input_audio_formats 1 + num_output_audio_formats 3 + Object.Base.input_audio_format [ + { + in_channels 8 + in_bit_depth 32 + in_valid_bit_depth 32 + in_ch_cfg $CHANNEL_CONFIG_7_POINT_1 + in_ch_map $CHANNEL_MAP_7_POINT_1 + } + ] + Object.Base.output_audio_format [ + { + out_channels 8 + out_bit_depth 32 + out_valid_bit_depth 24 + out_ch_cfg $CHANNEL_CONFIG_7_POINT_1 + out_ch_map $CHANNEL_MAP_7_POINT_1 + } + { + out_channels 8 + out_bit_depth 32 + out_valid_bit_depth 32 + out_ch_cfg $CHANNEL_CONFIG_7_POINT_1 + out_ch_map $CHANNEL_MAP_7_POINT_1 + } + { + out_channels 8 + out_bit_depth 16 + out_valid_bit_depth 16 + out_ch_cfg $CHANNEL_CONFIG_7_POINT_1 + out_ch_map $CHANNEL_MAP_7_POINT_1 + } + ] + } + } + } + } + ] + } + Object.Widget { + alh-copier [ + { + index 31 + type dai_out + stream_name $SDW_SPK_IN_STREAM + type "dai_out" + direction "capture" + node_type $ALH_LINK_INPUT_CLASS + num_input_audio_formats 1 + num_output_audio_formats 1 + num_output_pins 1 + + Object.Base.input_audio_format [ + { + in_channels $AMP_FEEDBACK_CH + in_bit_depth 32 + in_valid_bit_depth $SDW_LINK_VALID_BITS + + IncludeByKey.AMP_FEEDBACK_CH { + "2" { + in_ch_cfg $CHANNEL_CONFIG_STEREO + in_ch_map $CHANNEL_MAP_STEREO + } + "4" { + in_ch_cfg $CHANNEL_CONFIG_3_POINT_1 + in_ch_map $CHANNEL_MAP_3_POINT_1 + } + "6" { + in_ch_cfg $CHANNEL_CONFIG_5_POINT_1 + in_ch_map $CHANNEL_MAP_5_POINT_1 + } + "8" { + in_ch_cfg $CHANNEL_CONFIG_7_POINT_1 + in_ch_map $CHANNEL_MAP_7_POINT_1 + } + } + + in_sample_type $SAMPLE_TYPE_MSB_INTEGER + in_fmt_cfg "$[($in_channels | ($in_valid_bit_depth * 256))]" + } + ] + Object.Base.output_audio_format [ + { + out_channels $AMP_FEEDBACK_CH + out_bit_depth 32 + out_valid_bit_depth 32 + + IncludeByKey.AMP_FEEDBACK_CH { + "2" { + out_ch_cfg $CHANNEL_CONFIG_STEREO + out_ch_map $CHANNEL_MAP_STEREO + } + "4" { + out_ch_cfg $CHANNEL_CONFIG_3_POINT_1 + out_ch_map $CHANNEL_MAP_3_POINT_1 + } + "6" { + out_ch_cfg $CHANNEL_CONFIG_5_POINT_1 + out_ch_map $CHANNEL_MAP_5_POINT_1 + } + "8" { + out_ch_cfg $CHANNEL_CONFIG_7_POINT_1 + out_ch_map $CHANNEL_MAP_7_POINT_1 + } + } + } + ] + } + ] + pipeline [ + { + index 31 + priority 0 + lp_mode 0 + dynamic_pipeline 1 + } + ] + } + Object.PCM.pcm [ + { + name "Amp feedback" + id 3 + direction "capture" + Object.Base.fe_dai.1 { + name "Amp feedback" + } + + Object.PCM.pcm_caps.1 { + name "amp feedback" + formats 'S16_LE,S24_LE,S32_LE' + channels_min $AMP_FEEDBACK_CH + channels_max $AMP_FEEDBACK_CH + } + } + ] + Object.Base.route [ + { + source "alh-copier.$SDW_SPK_IN_STREAM.0" + sink "host-copier.3.capture" + } + ] + } +} diff --git a/tools/topology/topology2/platform/intel/sdw-dmic-audio-feature.conf b/tools/topology/topology2/platform/intel/sdw-dmic-audio-feature.conf new file mode 100644 index 000000000000..87039b261597 --- /dev/null +++ b/tools/topology/topology2/platform/intel/sdw-dmic-audio-feature.conf @@ -0,0 +1,56 @@ +Define { + SDW_DMIC_MODULE_COPIER_ID 41 + SDW_DMIC_AUDIO_FEATURE_CAPTURE_PCM_NAME "Microphone Audio Features" + SDW_DMIC_AUDIO_FEATURE_CAPTURE_PCM_ID 48 + SDW_DMIC_AUDIO_FEATURE_CAPTURE_STREAM_NAME "Microphone Audio Features Stream" + SDW_DMIC_AUDIO_FEATURE_CAPTURE_PIPELINE_ID 131 +} + +Object.Pipeline.host-gateway-src-mfcc-capture [ + { + index $SDW_DMIC_AUDIO_FEATURE_CAPTURE_PIPELINE_ID + + Object.Widget.host-copier.1 { + stream_name "$SDW_DMIC_AUDIO_FEATURE_CAPTURE_STREAM_NAME" + pcm_id $SDW_DMIC_AUDIO_FEATURE_CAPTURE_PCM_ID + } + + Object.Widget.mfcc.1 { + Object.Control { + bytes."1" { + name "$SDW_DMIC_AUDIO_FEATURE_CAPTURE_PCM_NAME MFCC bytes" + <include/components/mfcc/mel80.conf> + } + } + } + } +] +Object.Base.route [ + { + source "module-copier.$SDW_DMIC_MODULE_COPIER_ID.0" + sink "src.$SDW_DMIC_AUDIO_FEATURE_CAPTURE_PIPELINE_ID.1" + } + { + source "mfcc.$SDW_DMIC_AUDIO_FEATURE_CAPTURE_PIPELINE_ID.1" + sink "host-copier.$SDW_DMIC_AUDIO_FEATURE_CAPTURE_PCM_ID.capture" + } +] + +Object.PCM.pcm [ + { + name "$SDW_DMIC_AUDIO_FEATURE_CAPTURE_PCM_NAME" + id $SDW_DMIC_AUDIO_FEATURE_CAPTURE_PCM_ID + direction "capture" + Object.Base.fe_dai.1 { + name "$SDW_DMIC_AUDIO_FEATURE_CAPTURE_PCM_NAME" + } + + Object.PCM.pcm_caps.1 { + name "$SDW_DMIC_AUDIO_FEATURE_CAPTURE_STREAM_NAME" + formats 'S32_LE' + rates '16000' + channels_min 2 + channels_max 2 + } + } +] diff --git a/tools/topology/topology2/platform/intel/sdw-dmic-generic.conf b/tools/topology/topology2/platform/intel/sdw-dmic-generic.conf index 7f1fe52fbbda..f8fc51303966 100644 --- a/tools/topology/topology2/platform/intel/sdw-dmic-generic.conf +++ b/tools/topology/topology2/platform/intel/sdw-dmic-generic.conf @@ -36,7 +36,53 @@ IncludeByKey.SDW_DMIC_ENHANCED_CAPTURE { Object.Widget.host-copier.1 { stream_name "sdw dmic" pcm_id 4 - } + IncludeByKey.PCM_FORMAT_ALL { + "true" { + num_output_audio_formats 5 + Object.Base.output_audio_format [ + { + out_bit_depth 16 + out_valid_bit_depth 16 + } + { + out_bit_depth 32 + out_valid_bit_depth 32 + } + { + out_bit_depth 32 + out_valid_bit_depth 24 + } + { + out_bit_depth 32 + out_valid_bit_depth 32 + out_sample_type $SAMPLE_TYPE_FLOAT + } + { + out_bit_depth 8 + out_valid_bit_depth 8 + out_sample_type $SAMPLE_TYPE_UNSIGNED_INTEGER + } + ] + } + "false" { + num_output_audio_formats 3 + Object.Base.output_audio_format [ + { + out_bit_depth 16 + out_valid_bit_depth 16 + } + { + out_bit_depth 32 + out_valid_bit_depth 24 + } + { + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + } + } + } Object.Widget.tdfb.1 { Object.Control { @@ -180,7 +226,14 @@ Object.PCM.pcm [ Object.PCM.pcm_caps.1 { name "sdw dmic" - formats 'S16_LE,S24_LE,S32_LE' + IncludeByKey.PCM_FORMAT_ALL { + "true" { + formats 'S16_LE,S24_LE,S32_LE,U8,FLOAT_LE' + } + "false" { + formats 'S16_LE,S24_LE,S32_LE' + } + } } } ] diff --git a/tools/topology/topology2/platform/intel/sdw-jack-audio-feature.conf b/tools/topology/topology2/platform/intel/sdw-jack-audio-feature.conf new file mode 100644 index 000000000000..9645199d6907 --- /dev/null +++ b/tools/topology/topology2/platform/intel/sdw-jack-audio-feature.conf @@ -0,0 +1,56 @@ +Define { + SDW_JACK_MODULE_COPIER_ID 11 + SDW_JACK_AUDIO_FEATURE_CAPTURE_PCM_NAME "Jack In Audio Features" + SDW_JACK_AUDIO_FEATURE_CAPTURE_PCM_ID 47 + SDW_JACK_AUDIO_FEATURE_CAPTURE_STREAM_NAME "Jack In Audio Features Stream" + SDW_JACK_AUDIO_FEATURE_CAPTURE_PIPELINE_ID 130 +} + +Object.Pipeline.host-gateway-src-mfcc-capture [ + { + index $SDW_JACK_AUDIO_FEATURE_CAPTURE_PIPELINE_ID + + Object.Widget.host-copier.1 { + stream_name "$SDW_JACK_AUDIO_FEATURE_CAPTURE_STREAM_NAME" + pcm_id $SDW_JACK_AUDIO_FEATURE_CAPTURE_PCM_ID + } + + Object.Widget.mfcc.1 { + Object.Control { + bytes."1" { + name "$SDW_JACK_AUDIO_FEATURE_CAPTURE_PCM_NAME MFCC bytes" + <include/components/mfcc/mel80.conf> + } + } + } + } +] +Object.Base.route [ + { + source "module-copier.$SDW_JACK_MODULE_COPIER_ID.0" + sink "src.$SDW_JACK_AUDIO_FEATURE_CAPTURE_PIPELINE_ID.1" + } + { + source "mfcc.$SDW_JACK_AUDIO_FEATURE_CAPTURE_PIPELINE_ID.1" + sink "host-copier.$SDW_JACK_AUDIO_FEATURE_CAPTURE_PCM_ID.capture" + } +] + +Object.PCM.pcm [ + { + name "$SDW_JACK_AUDIO_FEATURE_CAPTURE_PCM_NAME" + id $SDW_JACK_AUDIO_FEATURE_CAPTURE_PCM_ID + direction "capture" + Object.Base.fe_dai.1 { + name "$SDW_JACK_AUDIO_FEATURE_CAPTURE_PCM_NAME" + } + + Object.PCM.pcm_caps.1 { + name "$SDW_JACK_AUDIO_FEATURE_CAPTURE_STREAM_NAME" + formats 'S32_LE' + rates '16000' + channels_min $SDW_JACK_CAPTURE_CH + channels_max $SDW_JACK_CAPTURE_CH + } + } +] diff --git a/tools/topology/topology2/platform/intel/sdw-jack-dax.conf b/tools/topology/topology2/platform/intel/sdw-jack-dax.conf new file mode 100644 index 000000000000..9816d85b2018 --- /dev/null +++ b/tools/topology/topology2/platform/intel/sdw-jack-dax.conf @@ -0,0 +1,517 @@ +# include deep buffer config if buffer size is in 1 - 1000 ms. +IncludeByKey.DEEPBUFFER_FW_DMA_MS { + "([1-9]|[1-9][0-9]|[1-9][0-9][0-9]|1000)" "platform/intel/deep-buffer.conf" +} + +Define { + JACK_PLAYBACK_PCM_NAME "Jack Out" + JACK_CAPTURE_PCM_NAME "Jack In" + JACK_RATE 48000 +} + +# +# List of all DAIs +# +Object.Dai.ALH [ + { + dai_index 0 + id $SDW_JACK_OUT_BE_ID + direction "playback" + name $SDW_JACK_OUT_STREAM + default_hw_conf_id 0 + rate $JACK_RATE + channels 2 + + Object.Base.hw_config.1 { + id 0 + name "ALH2" + } + } + { + dai_index 10 + id $SDW_JACK_IN_BE_ID + direction "capture" + name $SDW_JACK_IN_STREAM + default_hw_conf_id 0 + rate $JACK_RATE + channels 2 + + Object.Base.hw_config.1 { + id 0 + name "ALH3" + } + } +] + +# +# Pipeline definitions +# +Object.Pipeline { + host-copier-gain-mixin-playback [ + { + index 0 + + Object.Widget.host-copier.1 { + stream_name "volume playback 0" + pcm_id 0 + IncludeByKey.PCM_FORMAT_ALL { + "true" { + num_input_audio_formats 5 + Object.Base.input_audio_format [ + { + in_rate $JACK_RATE + in_bit_depth 16 + in_valid_bit_depth 16 + } + { + in_rate $JACK_RATE + in_bit_depth 32 + in_valid_bit_depth 32 + } + { + in_rate $JACK_RATE + in_bit_depth 32 + in_valid_bit_depth 24 + } + { + in_rate $JACK_RATE + in_bit_depth 32 + in_valid_bit_depth 32 + in_sample_type $SAMPLE_TYPE_FLOAT + } + { + in_rate $JACK_RATE + in_bit_depth 8 + in_valid_bit_depth 8 + in_sample_type $SAMPLE_TYPE_UNSIGNED_INTEGER + } + ] + } + "false" { + num_input_audio_formats 3 + Object.Base.input_audio_format [ + { + in_rate $JACK_RATE + in_bit_depth 16 + in_valid_bit_depth 16 + } + { + in_rate $JACK_RATE + in_bit_depth 32 + in_valid_bit_depth 32 + } + { + in_rate $JACK_RATE + in_bit_depth 32 + in_valid_bit_depth 24 + } + ] + } + } + Object.Base.output_audio_format [ + { + out_rate $JACK_RATE + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + } + Object.Widget.gain.1 { + Object.Control.mixer.1 { + name 'Pre Mixer $JACK_PLAYBACK_PCM_NAME Playback Volume' + } + Object.Base.input_audio_format [ + { + in_rate $JACK_RATE + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + Object.Base.output_audio_format [ + { + out_rate $JACK_RATE + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + } + Object.Widget.mixin.1 { + Object.Base.input_audio_format [ + { + in_rate $JACK_RATE + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + Object.Base.output_audio_format [ + { + out_rate $JACK_RATE + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + } + } + ] + + mixout-gain-dax-alh-dai-copier-playback [ + { + index 1 + + Object.Widget.mixout.1 { + Object.Base.input_audio_format [ + { + in_rate $JACK_RATE + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + Object.Base.output_audio_format [ + { + out_rate $JACK_RATE + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + } + Object.Widget.alh-copier.1 { + stream_name $SDW_JACK_OUT_STREAM + node_type $ALH_LINK_OUTPUT_CLASS + num_input_audio_formats 3 + Object.Base.input_audio_format [ + { + in_rate $JACK_RATE + in_bit_depth 16 + in_valid_bit_depth 16 + } + { + in_rate $JACK_RATE + in_bit_depth 32 + in_valid_bit_depth 24 + } + { + in_rate $JACK_RATE + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + num_output_audio_formats 1 + Object.Base.output_audio_format [ + { + out_rate $JACK_RATE + out_bit_depth 32 + out_valid_bit_depth $SDW_LINK_VALID_BITS + out_sample_type $SAMPLE_TYPE_MSB_INTEGER + out_fmt_cfg "$[($out_channels | ($out_valid_bit_depth * 256)) | ($out_sample_type * 65536)]" + } + ] + } + Object.Widget.gain.1 { + Object.Control.mixer.1 { + name 'Post Mixer $JACK_PLAYBACK_PCM_NAME Playback Volume' + } + Object.Base.input_audio_format [ + { + in_rate $JACK_RATE + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + Object.Base.output_audio_format [ + { + out_rate $JACK_RATE + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + } + Object.Widget.dolby-dax.1 { + core_id $DOLBY_DAX_CORE_ID + Object.Control { + mixer."1" { + name 'DAX Headphone Switch' + } + mixer."2" { + name 'DAX Headphone Switch CP' + } + mixer."3" { + name 'DAX Headphone Switch CTC' + } + mixer."4" { + name 'DAX Headphone Volume' + } + enum."1" { + name 'DAX Headphone Profile' + } + enum."2" { + name 'DAX Headphone Device' + } + bytes."1" { + name 'DAX Headphone Tuning' + max 8192 + } + } + } + } + ] + +} + +Object.Pipeline.host-gateway-capture [ + { + index 10 + + Object.Widget.host-copier.1 { + stream_name "Passthrough Capture 0" + pcm_id 1 + num_input_audio_formats 1 + Object.Base.input_audio_format [ + { + in_rate $JACK_RATE + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + IncludeByKey.PCM_FORMAT_ALL { + "true" { + num_output_audio_formats 5 + Object.Base.output_audio_format [ + { + out_rate $JACK_RATE + out_bit_depth 16 + out_valid_bit_depth 16 + } + { + out_rate $JACK_RATE + out_bit_depth 32 + out_valid_bit_depth 24 + } + { + out_rate $JACK_RATE + out_bit_depth 32 + out_valid_bit_depth 32 + } + { + out_rate $JACK_RATE + out_bit_depth 32 + out_valid_bit_depth 32 + out_sample_type $SAMPLE_TYPE_FLOAT + } + { + out_rate $JACK_RATE + out_bit_depth 8 + out_valid_bit_depth 8 + out_sample_type $SAMPLE_TYPE_UNSIGNED_INTEGER + } + ] + } + "false" { + num_output_audio_formats 3 + Object.Base.output_audio_format [ + { + out_rate $JACK_RATE + out_bit_depth 16 + out_valid_bit_depth 16 + } + { + out_rate $JACK_RATE + out_bit_depth 32 + out_valid_bit_depth 24 + } + { + out_rate $JACK_RATE + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + } + } + } + } +] + +# Jack capture pipeline widgets +Object.Widget { + module-copier."12" { + index 1 + num_input_pins 1 + num_output_pins 2 + num_input_audio_formats 1 + num_output_audio_formats 1 + Object.Base.input_audio_format [ + { + in_rate $JACK_RATE + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + Object.Base.output_audio_format [ + { + out_rate $JACK_RATE + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + } + + alh-copier [ + { + stream_name $SDW_JACK_IN_STREAM + direction "capture" + type "dai_out" + index 11 + node_type $ALH_LINK_INPUT_CLASS + num_input_audio_formats 1 + num_output_audio_formats 1 + num_output_pins 1 + + Object.Base.input_audio_format [ + { + in_rate $JACK_RATE + in_bit_depth 32 + in_valid_bit_depth $SDW_LINK_VALID_BITS + in_sample_type $SAMPLE_TYPE_MSB_INTEGER + in_fmt_cfg "$[($in_channels | ($in_valid_bit_depth * 256))]" + } + ] + Object.Base.output_audio_format [ + { + out_rate $JACK_RATE + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + } + ] + + pipeline [ + { + index 11 + priority 0 + lp_mode 0 + dynamic_pipeline 1 + + Object.Widget.eqiir [ + { + num_input_audio_formats 1 + num_output_audio_formats 1 + # index 11 is inherited from the pipeline definition + # the instance number is automatically generated as '0' + Object.Base.input_audio_format [ + { + in_rate $JACK_RATE + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + Object.Base.output_audio_format [ + { + out_rate $JACK_RATE + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + + Object.Control.bytes."1" { + <include/components/eqiir/highpass_40hz_0db_48khz.conf> + name '$JACK_CAPTURE_PCM_NAME Capture IIR Eq' + } + } + ] + } + ] +} + +Object.PCM.pcm [ + { + name "$JACK_PLAYBACK_PCM_NAME" + id 0 + direction "playback" + Object.Base.fe_dai.1 { + name "$JACK_PLAYBACK_PCM_NAME" + } + + Object.PCM.pcm_caps.1 { + name "volume playback 0" + IncludeByKey.PCM_FORMAT_ALL { + "true" { + formats 'S16_LE,S24_LE,S32_LE,U8,FLOAT_LE' + } + "false" { + formats 'S16_LE,S24_LE,S32_LE' + } + } + IncludeByKey.JACK_RATE { + "48000" { + rates '48000' + } + "96000" { + rates '96000' + } + "192000" { + rates '192000' + } + } + } + } + { + name "$JACK_CAPTURE_PCM_NAME" + id 1 + direction "capture" + Object.Base.fe_dai.1 { + name "$JACK_CAPTURE_PCM_NAME" + } + + Object.PCM.pcm_caps.1 { + name "Passthrough Capture 0" + IncludeByKey.PCM_FORMAT_ALL { + "true" { + formats 'S16_LE,S24_LE,S32_LE,U8,FLOAT_LE' + } + "false" { + formats 'S16_LE,S24_LE,S32_LE' + } + } + IncludeByKey.JACK_RATE { + "48000" { + rates '48000' + } + "96000" { + rates '96000' + } + "192000" { + rates '192000' + } + } + channels_min $SDW_JACK_CAPTURE_CH + channels_max $SDW_JACK_CAPTURE_CH + } + } +] + +Object.Base.route [ + { + source "dolby-dax.1.1" + sink "module-copier.1.12" + } + { + source "module-copier.1.12" + sink "alh-copier.$SDW_JACK_OUT_STREAM.0" + } + { + source "mixin.0.1" + sink "mixout.1.1" + } + { + source "alh-copier.$SDW_JACK_IN_STREAM.0" + sink "eqiir.11.0" + } + { + source "eqiir.11.0" + sink "host-copier.1.capture" + } + { + source "host-copier.0.playback" + sink "gain.0.1" + } +] diff --git a/tools/topology/topology2/platform/intel/sdw-jack-dts.conf b/tools/topology/topology2/platform/intel/sdw-jack-dts.conf new file mode 100644 index 000000000000..3a72531d7219 --- /dev/null +++ b/tools/topology/topology2/platform/intel/sdw-jack-dts.conf @@ -0,0 +1,632 @@ +# include deep buffer config if buffer size is in 1 - 1000 ms. +IncludeByKey.PASSTHROUGH { +"false" { + IncludeByKey.DEEPBUFFER_FW_DMA_MS { + "([1-9]|[1-9][0-9]|[1-9][0-9][0-9]|1000)" "platform/intel/deep-buffer.conf" + } +} +} + +Define { + JACK_PLAYBACK_PCM_NAME "Jack Out" + JACK_CAPTURE_PCM_NAME "Jack In" + JACK_RATE 48000 +} + +# +# List of all DAIs +# +Object.Dai.ALH [ + { + dai_index 0 + id $SDW_JACK_OUT_BE_ID + direction "playback" + name $SDW_JACK_OUT_STREAM + default_hw_conf_id 0 + rate $JACK_RATE + channels 2 + + Object.Base.hw_config.1 { + id 0 + name "ALH2" + } + } + { + dai_index 10 + id $SDW_JACK_IN_BE_ID + direction "capture" + name $SDW_JACK_IN_STREAM + default_hw_conf_id 0 + rate $JACK_RATE + channels 2 + + Object.Base.hw_config.1 { + id 0 + name "ALH3" + } + } +] + +# +# Pipeline definitions +# +IncludeByKey.PASSTHROUGH { +"false" { + Object.Pipeline { + host-copier-gain-mixin-playback [ + { + index 0 + + Object.Widget.host-copier.1 { + stream_name "volume playback 0" + pcm_id 0 + IncludeByKey.PCM_FORMAT_ALL { + "true" { + num_input_audio_formats 5 + Object.Base.input_audio_format [ + { + in_rate $JACK_RATE + in_bit_depth 16 + in_valid_bit_depth 16 + } + { + in_rate $JACK_RATE + in_bit_depth 32 + in_valid_bit_depth 32 + } + { + in_rate $JACK_RATE + in_bit_depth 32 + in_valid_bit_depth 24 + } + { + in_rate $JACK_RATE + in_bit_depth 32 + in_valid_bit_depth 32 + in_sample_type $SAMPLE_TYPE_FLOAT + } + { + in_rate $JACK_RATE + in_bit_depth 8 + in_valid_bit_depth 8 + in_sample_type $SAMPLE_TYPE_UNSIGNED_INTEGER + } + ] + } + "false" { + num_input_audio_formats 3 + Object.Base.input_audio_format [ + { + in_rate $JACK_RATE + in_bit_depth 16 + in_valid_bit_depth 16 + } + { + in_rate $JACK_RATE + in_bit_depth 32 + in_valid_bit_depth 32 + } + { + in_rate $JACK_RATE + in_bit_depth 32 + in_valid_bit_depth 24 + } + ] + } + } + Object.Base.output_audio_format [ + { + out_rate $JACK_RATE + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + } + Object.Widget.gain.1 { + Object.Control.mixer.1 { + name 'Pre Mixer $JACK_PLAYBACK_PCM_NAME Playback Volume' + } + Object.Base.input_audio_format [ + { + in_rate $JACK_RATE + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + Object.Base.output_audio_format [ + { + out_rate $JACK_RATE + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + } + Object.Widget.mixin.1 { + Object.Base.input_audio_format [ + { + in_rate $JACK_RATE + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + Object.Base.output_audio_format [ + { + out_rate $JACK_RATE + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + } + } + ] + + mixout-gain-eqiir-dts-alh-dai-copier-playback [ + { + index 1 + + Object.Widget.mixout.1 { + Object.Base.input_audio_format [ + { + in_rate $JACK_RATE + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + Object.Base.output_audio_format [ + { + out_rate $JACK_RATE + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + } + + Object.Widget.alh-copier.1 { + stream_name $SDW_JACK_OUT_STREAM + node_type $ALH_LINK_OUTPUT_CLASS + num_input_audio_formats 3 + Object.Base.input_audio_format [ + { + in_rate $JACK_RATE + in_bit_depth 16 + in_valid_bit_depth 16 + } + { + in_rate $JACK_RATE + in_bit_depth 32 + in_valid_bit_depth 24 + } + { + in_rate $JACK_RATE + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + num_output_audio_formats 1 + Object.Base.output_audio_format [ + { + out_rate $JACK_RATE + out_bit_depth 32 + out_valid_bit_depth $SDW_LINK_VALID_BITS + out_sample_type $SAMPLE_TYPE_MSB_INTEGER + out_fmt_cfg "$[($out_channels | ($out_valid_bit_depth * 256)) | ($out_sample_type * 65536)]" + } + ] + } + Object.Widget.gain.1 { + Object.Control.mixer.1 { + name 'Post Mixer $JACK_PLAYBACK_PCM_NAME Playback Volume' + } + Object.Base.input_audio_format [ + { + in_rate $JACK_RATE + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + Object.Base.output_audio_format [ + { + out_rate $JACK_RATE + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + } + + Object.Widget.eqiir.1 { + Object.Control.bytes."1" { + name 'Post Mixer $JACK_PLAYBACK_PCM_NAME IIR Eq bytes' + } + } + + Object.Widget.dts.1 { + Object.Control { + bytes."1" { + name 'Post Mixer $JACK_PLAYBACK_PCM_NAME DTS bytes' + max 2048 + } + } + } + } + ] + } +} +"true" { + Object.Pipeline.host-gateway-playback [ + { + index 0 + Object.Widget.host-copier.1 { + stream_name "volume playback 0" + pcm_id 0 + num_input_audio_formats 3 + Object.Base.input_audio_format [ + { + in_rate $JACK_RATE + in_bit_depth 16 + in_valid_bit_depth 16 + } + { + in_rate $JACK_RATE + in_bit_depth 32 + in_valid_bit_depth 24 + } + { + in_rate $JACK_RATE + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + num_output_audio_formats 3 + Object.Base.output_audio_format [ + { + out_rate $JACK_RATE + out_bit_depth 16 + out_valid_bit_depth 16 + } + { + out_rate $JACK_RATE + out_bit_depth 32 + out_valid_bit_depth 24 + } + { + out_rate $JACK_RATE + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + } + } + ] + + Object.Widget { + alh-copier [ + { + stream_name $SDW_JACK_OUT_STREAM + node_type $ALH_LINK_OUTPUT_CLASS + index 1 + type dai_in + direction playback + num_input_pins 1 + num_input_audio_formats 3 + Object.Base.input_audio_format [ + { + in_rate $JACK_RATE + in_bit_depth 16 + in_valid_bit_depth 16 + } + { + in_rate $JACK_RATE + in_bit_depth 32 + in_valid_bit_depth 24 + } + { + in_rate $JACK_RATE + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + num_output_audio_formats 1 + Object.Base.output_audio_format [ + { + out_rate $JACK_RATE + out_bit_depth 32 + out_valid_bit_depth $SDW_LINK_VALID_BITS + out_sample_type $SAMPLE_TYPE_MSB_INTEGER + out_fmt_cfg "$[($out_channels | ($out_valid_bit_depth * 256))]" + } + ] + } + ] + pipeline [ + { + index 1 + priority 0 + lp_mode 0 + dynamic_pipeline 1 + } + ] + } + } +} + +Object.Pipeline.host-gateway-capture [ + { + index 10 + + Object.Widget.host-copier.1 { + stream_name "Passthrough Capture 0" + pcm_id 1 + num_input_audio_formats 1 + Object.Base.input_audio_format [ + { + in_rate $JACK_RATE + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + IncludeByKey.PCM_FORMAT_ALL { + "true" { + num_output_audio_formats 5 + Object.Base.output_audio_format [ + { + out_rate $JACK_RATE + out_bit_depth 16 + out_valid_bit_depth 16 + } + { + out_rate $JACK_RATE + out_bit_depth 32 + out_valid_bit_depth 24 + } + { + out_rate $JACK_RATE + out_bit_depth 32 + out_valid_bit_depth 32 + } + { + out_rate $JACK_RATE + out_bit_depth 32 + out_valid_bit_depth 32 + out_sample_type $SAMPLE_TYPE_FLOAT + } + { + out_rate $JACK_RATE + out_bit_depth 8 + out_valid_bit_depth 8 + out_sample_type $SAMPLE_TYPE_UNSIGNED_INTEGER + } + ] + } + "false" { + num_output_audio_formats 3 + Object.Base.output_audio_format [ + { + out_rate $JACK_RATE + out_bit_depth 16 + out_valid_bit_depth 16 + } + { + out_rate $JACK_RATE + out_bit_depth 32 + out_valid_bit_depth 24 + } + { + out_rate $JACK_RATE + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + } + } + } + } +] + +# Jack capture pipeline widgets +Object.Widget { + module-copier."12" { + index 1 + num_input_pins 1 + num_output_pins 2 + num_input_audio_formats 1 + num_output_audio_formats 1 + Object.Base.input_audio_format [ + { + in_rate $JACK_RATE + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + Object.Base.output_audio_format [ + { + out_rate $JACK_RATE + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + } + + alh-copier [ + { + stream_name $SDW_JACK_IN_STREAM + direction "capture" + type "dai_out" + index 11 + node_type $ALH_LINK_INPUT_CLASS + num_input_audio_formats 1 + num_output_audio_formats 1 + num_output_pins 1 + + Object.Base.input_audio_format [ + { + in_rate $JACK_RATE + in_bit_depth 32 + in_valid_bit_depth $SDW_LINK_VALID_BITS + in_sample_type $SAMPLE_TYPE_MSB_INTEGER + in_fmt_cfg "$[($in_channels | ($in_valid_bit_depth * 256))]" + } + ] + Object.Base.output_audio_format [ + { + out_rate $JACK_RATE + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + } + ] + + pipeline [ + { + index 11 + priority 0 + lp_mode 0 + dynamic_pipeline 1 + + IncludeByKey.PASSTHROUGH { + "false" { + Object.Widget.eqiir [ + { + num_input_audio_formats 1 + num_output_audio_formats 1 + # index 11 is inherited from the pipeline definition + # the instance number is automatically generated as '0' + Object.Base.input_audio_format [ + { + in_rate $JACK_RATE + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + Object.Base.output_audio_format [ + { + out_rate $JACK_RATE + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + + Object.Control.bytes."1" { + <include/components/eqiir/highpass_40hz_0db_48khz.conf> + name '$JACK_CAPTURE_PCM_NAME Capture IIR Eq' + } + } + ] + } + } + } + ] +} + +Object.PCM.pcm [ + { + name "$JACK_PLAYBACK_PCM_NAME" + id 0 + direction "playback" + Object.Base.fe_dai.1 { + name "$JACK_PLAYBACK_PCM_NAME" + } + + Object.PCM.pcm_caps.1 { + name "volume playback 0" + IncludeByKey.PCM_FORMAT_ALL { + "true" { + formats 'S16_LE,S24_LE,S32_LE,U8,FLOAT_LE' + } + "false" { + formats 'S16_LE,S24_LE,S32_LE' + } + } + IncludeByKey.JACK_RATE { + "48000" { + rates '48000' + } + "96000" { + rates '96000' + } + "192000" { + rates '192000' + } + } + } + } + { + name "$JACK_CAPTURE_PCM_NAME" + id 1 + direction "capture" + Object.Base.fe_dai.1 { + name "$JACK_CAPTURE_PCM_NAME" + } + + Object.PCM.pcm_caps.1 { + name "Passthrough Capture 0" + IncludeByKey.PCM_FORMAT_ALL { + "true" { + formats 'S16_LE,S24_LE,S32_LE,U8,FLOAT_LE' + } + "false" { + formats 'S16_LE,S24_LE,S32_LE' + } + } + IncludeByKey.JACK_RATE { + "48000" { + rates '48000' + } + "96000" { + rates '96000' + } + "192000" { + rates '192000' + } + } + channels_min $SDW_JACK_CAPTURE_CH + channels_max $SDW_JACK_CAPTURE_CH + } + } +] + +IncludeByKey.PASSTHROUGH { +"false" { + Object.Base.route [ + { + source "dts.1.1" + sink "module-copier.1.12" + } + { + source "module-copier.1.12" + sink "alh-copier.$SDW_JACK_OUT_STREAM.0" + } + ] + + Object.Base.route [ + { + source "mixin.0.1" + sink "mixout.1.1" + } + { + source "alh-copier.$SDW_JACK_IN_STREAM.0" + sink "eqiir.11.0" + } + { + source "eqiir.11.0" + sink "host-copier.1.capture" + } + { + source "host-copier.0.playback" + sink "gain.0.1" + } + ] + } +"true" { + Object.Base.route [ + { + source "alh-copier.$SDW_JACK_IN_STREAM.0" + sink "host-copier.1.capture" + } + { + source "host-copier.0.playback" + sink "alh-copier.$SDW_JACK_OUT_STREAM.0" + } + ] + } +} diff --git a/tools/topology/topology2/platform/intel/sdw-jack-echo-ref.conf b/tools/topology/topology2/platform/intel/sdw-jack-echo-ref.conf new file mode 100644 index 000000000000..fa506c6b2d46 --- /dev/null +++ b/tools/topology/topology2/platform/intel/sdw-jack-echo-ref.conf @@ -0,0 +1,108 @@ +Define { + SDW_JACK_ECHO_REF_PCM_ID 11 + SDW_JACK_ECHO_REF_PIPELINE_ID 12 +} + +IncludeByKey.SDW_JACK_ECHO_REF { + "true" { + Object.Pipeline { + siggen-host-copier-capture [ + { + direction "capture" + index $SDW_JACK_ECHO_REF_PIPELINE_ID + Object.Widget.host-copier."1" { + stream_name "Jack Echo Reference" + pcm_id $SDW_JACK_ECHO_REF_PCM_ID + num_input_audio_formats 1 + Object.Base.input_audio_format [ + { + in_rate $JACK_RATE + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + num_output_audio_formats 3 + Object.Base.output_audio_format [ + { + out_rate $JACK_RATE + out_bit_depth 16 + out_valid_bit_depth 16 + } + { + out_rate $JACK_RATE + out_bit_depth 32 + out_valid_bit_depth 24 + } + { + out_rate $JACK_RATE + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + } + Object.Widget.siggen."1" { + num_input_pins 1 + num_output_pins 1 + num_input_audio_formats 1 + num_output_audio_formats 1 + Object.Base.input_audio_format [ + { + in_rate $JACK_RATE + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + Object.Base.output_audio_format [ + { + out_rate $JACK_RATE + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + } + } + ] + } + + Object.PCM.pcm [ + { + name "Jack Echo Reference" + id $SDW_JACK_ECHO_REF_PCM_ID + direction "capture" + Object.Base.fe_dai.1 { + name "Jack Echo Reference" + } + Object.PCM.pcm_caps.1 { + name "Jack Echo Reference" + formats 'S16_LE,S24_LE,S32_LE' + } + IncludeByKey.JACK_RATE { + "48000" { + rates '48000' + } + "96000" { + rates '96000' + } + "192000" { + rates '192000' + } + } + } + ] + + Object.Base.route [ + { + source "alh-copier.Loopback_Virtual.25" + sink "siggen.$SDW_JACK_ECHO_REF_PIPELINE_ID.1" + } + { + source "module-copier.1.12" + sink "siggen.$SDW_JACK_ECHO_REF_PIPELINE_ID.1" + } + { + source "siggen.$SDW_JACK_ECHO_REF_PIPELINE_ID.1" + sink "host-copier.$SDW_JACK_ECHO_REF_PCM_ID.capture" + } + ] + } # SDW_JACK_ECHO_REF true +} diff --git a/tools/topology/topology2/platform/intel/sdw-jack-generic.conf b/tools/topology/topology2/platform/intel/sdw-jack-generic.conf index 99d15eb0aba8..b2e1a259d9a0 100644 --- a/tools/topology/topology2/platform/intel/sdw-jack-generic.conf +++ b/tools/topology/topology2/platform/intel/sdw-jack-generic.conf @@ -60,23 +60,60 @@ IncludeByKey.PASSTHROUGH { Object.Widget.host-copier.1 { stream_name "volume playback 0" pcm_id 0 - Object.Base.input_audio_format [ - { - in_rate $JACK_RATE - in_bit_depth 16 - in_valid_bit_depth 16 - } - { - in_rate $JACK_RATE - in_bit_depth 32 - in_valid_bit_depth 32 + IncludeByKey.PCM_FORMAT_ALL { + "true" { + num_input_audio_formats 5 + Object.Base.input_audio_format [ + { + in_rate $JACK_RATE + in_bit_depth 16 + in_valid_bit_depth 16 + } + { + in_rate $JACK_RATE + in_bit_depth 32 + in_valid_bit_depth 32 + } + { + in_rate $JACK_RATE + in_bit_depth 32 + in_valid_bit_depth 24 + } + { + in_rate $JACK_RATE + in_bit_depth 32 + in_valid_bit_depth 32 + in_sample_type $SAMPLE_TYPE_FLOAT + } + { + in_rate $JACK_RATE + in_bit_depth 8 + in_valid_bit_depth 8 + in_sample_type $SAMPLE_TYPE_UNSIGNED_INTEGER + } + ] } - { - in_rate $JACK_RATE - in_bit_depth 32 - in_valid_bit_depth 24 + "false" { + num_input_audio_formats 3 + Object.Base.input_audio_format [ + { + in_rate $JACK_RATE + in_bit_depth 16 + in_valid_bit_depth 16 + } + { + in_rate $JACK_RATE + in_bit_depth 32 + in_valid_bit_depth 32 + } + { + in_rate $JACK_RATE + in_bit_depth 32 + in_valid_bit_depth 24 + } + ] } - ] + } Object.Base.output_audio_format [ { out_rate $JACK_RATE @@ -311,30 +348,88 @@ Object.Pipeline.host-gateway-capture [ in_valid_bit_depth 32 } ] - num_output_audio_formats 3 - Object.Base.output_audio_format [ - { - out_rate $JACK_RATE - out_bit_depth 16 - out_valid_bit_depth 16 - } - { - out_rate $JACK_RATE - out_bit_depth 32 - out_valid_bit_depth 24 + IncludeByKey.PCM_FORMAT_ALL { + "true" { + num_output_audio_formats 5 + Object.Base.output_audio_format [ + { + out_rate $JACK_RATE + out_bit_depth 16 + out_valid_bit_depth 16 + } + { + out_rate $JACK_RATE + out_bit_depth 32 + out_valid_bit_depth 24 + } + { + out_rate $JACK_RATE + out_bit_depth 32 + out_valid_bit_depth 32 + } + { + out_rate $JACK_RATE + out_bit_depth 32 + out_valid_bit_depth 32 + out_sample_type $SAMPLE_TYPE_FLOAT + } + { + out_rate $JACK_RATE + out_bit_depth 8 + out_valid_bit_depth 8 + out_sample_type $SAMPLE_TYPE_UNSIGNED_INTEGER + } + ] } - { - out_rate $JACK_RATE - out_bit_depth 32 - out_valid_bit_depth 32 + "false" { + num_output_audio_formats 3 + Object.Base.output_audio_format [ + { + out_rate $JACK_RATE + out_bit_depth 16 + out_valid_bit_depth 16 + } + { + out_rate $JACK_RATE + out_bit_depth 32 + out_valid_bit_depth 24 + } + { + out_rate $JACK_RATE + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] } - ] + } } } ] # Jack capture pipeline widgets Object.Widget { + module-copier."12" { + index 1 + num_input_pins 1 + num_output_pins 2 + num_input_audio_formats 1 + num_output_audio_formats 1 + Object.Base.input_audio_format [ + { + in_rate $JACK_RATE + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + Object.Base.output_audio_format [ + { + out_rate $JACK_RATE + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + } + alh-copier [ { stream_name $SDW_JACK_IN_STREAM @@ -401,6 +496,28 @@ Object.Widget { } } ] + Object.Widget.module-copier [ + { + num_input_audio_formats 1 + num_output_audio_formats 1 + # index 11 is inherited from the pipeline definition + # the instance number is automatically generated as '0' + Object.Base.input_audio_format [ + { + in_rate $JACK_RATE + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + Object.Base.output_audio_format [ + { + out_rate $JACK_RATE + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + } + ] } } } @@ -418,7 +535,14 @@ Object.PCM.pcm [ Object.PCM.pcm_caps.1 { name "volume playback 0" - formats 'S16_LE,S24_LE,S32_LE' + IncludeByKey.PCM_FORMAT_ALL { + "true" { + formats 'S16_LE,S24_LE,S32_LE,U8,FLOAT_LE' + } + "false" { + formats 'S16_LE,S24_LE,S32_LE' + } + } IncludeByKey.JACK_RATE { "48000" { rates '48000' @@ -442,7 +566,14 @@ Object.PCM.pcm [ Object.PCM.pcm_caps.1 { name "Passthrough Capture 0" - formats 'S16_LE,S24_LE,S32_LE' + IncludeByKey.PCM_FORMAT_ALL { + "true" { + formats 'S16_LE,S24_LE,S32_LE,U8,FLOAT_LE' + } + "false" { + formats 'S16_LE,S24_LE,S32_LE' + } + } IncludeByKey.JACK_RATE { "48000" { rates '48000' @@ -465,6 +596,10 @@ IncludeByKey.PASSTHROUGH { Object.Base.route [ { source "gain.1.1" + sink "module-copier.1.12" + } + { + source "module-copier.1.12" sink "alh-copier.$SDW_JACK_OUT_STREAM.0" } { @@ -477,6 +612,10 @@ IncludeByKey.PASSTHROUGH { } { source "eqiir.11.0" + sink "module-copier.11.0" + } + { + source "module-copier.11.0" sink "host-copier.1.capture" } { diff --git a/tools/topology/topology2/production/CMakeLists.txt b/tools/topology/topology2/production/CMakeLists.txt index 14da2eb3be97..c2cb86400e4b 100644 --- a/tools/topology/topology2/production/CMakeLists.txt +++ b/tools/topology/topology2/production/CMakeLists.txt @@ -6,6 +6,7 @@ include(tplg-targets-cavs25.cmake) include(tplg-targets-ace1.cmake) include(tplg-targets-ace2.cmake) include(tplg-targets-ace3.cmake) +include(tplg-targets-ace4.cmake) include(tplg-targets-imx8.cmake) add_custom_target(topology2_prod) diff --git a/tools/topology/topology2/production/tplg-targets-ace1.cmake b/tools/topology/topology2/production/tplg-targets-ace1.cmake index c00e2e1d746d..6c3bc7214663 100644 --- a/tools/topology/topology2/production/tplg-targets-ace1.cmake +++ b/tools/topology/topology2/production/tplg-targets-ace1.cmake @@ -107,6 +107,11 @@ SDW_AMP_FEEDBACK=true,SDW_SPK_STREAM=Playback,SDW_SPK_IN_STREAM=Capture,\ AMP_FEEDBACK_CH=8,AMP_FEEDBACK_CH_PER_LINK=4,SDW_JACK=false,NUM_DMICS=0,\ HDMI1_ID=4,HDMI2_ID=5,HDMI3_ID=6,SDW_SPK_ENHANCED_PLAYBACK=false" +"cavs-sdw\;sof-mtl-cs35l56-l01-fb6\;PLATFORM=mtl,NUM_SDW_AMP_LINKS=2,\ +SDW_AMP_FEEDBACK=true,SDW_SPK_STREAM=Playback,SDW_SPK_IN_STREAM=Capture,\ +AMP_FEEDBACK_CH=6,AMP_FEEDBACK_CH_PER_LINK=3,SDW_JACK=false,NUM_DMICS=0,\ +HDMI1_ID=4,HDMI2_ID=5,HDMI3_ID=6,SDW_SPK_ENHANCED_PLAYBACK=false" + # ARL has the same DSP generation. So reuse the platform = mtl. "cavs-sdw\;sof-arl-cs42l43-l0\;PLATFORM=mtl,NUM_SDW_AMP_LINKS=1,SDW_DMIC=1,\ SDW_AMP_FEEDBACK=false,SDW_SPK_STREAM=Playback-SmartAmp,SDW_DMIC_STREAM=Capture-SmartMic,\ @@ -144,6 +149,18 @@ DMIC0_ENHANCED_CAPTURE=true,EFX_DMIC0_TDFB_PARAMS=line2_generic_pm10deg,EFX_DMIC PREPROCESS_PLUGINS=nhlt,NHLT_BIN=sof-arl-cs42l43-l2-cs35l56-l3-2ch.bin,\ HDMI1_ID=5,HDMI2_ID=6,HDMI3_ID=7" +"cavs-sdw\;sof-arl-rt711-l0\;PLATFORM=mtl,HDMI1_ID=2,HDMI2_ID=3,HDMI3_ID=4" + +"cavs-sdw\;sof-arl-rt711-l0-2ch\;PLATFORM=mtl,NUM_DMICS=2,PDM1_MIC_A_ENABLE=0,PDM1_MIC_B_ENABLE=0,\ +DMIC0_ID=2,DMIC1_ID=3,PREPROCESS_PLUGINS=nhlt,NHLT_BIN=nhlt-sof-arl-rt711-2ch.bin,\ +HDMI1_ID=4,HDMI2_ID=5,HDMI3_ID=6,DMIC0_ENHANCED_CAPTURE=true,\ +EFX_DMIC0_TDFB_PARAMS=line2_generic_pm10deg,EFX_DMIC0_DRC_PARAMS=dmic_default" + +"cavs-sdw\;sof-arl-rt711-l0-4ch\;PLATFORM=mtl,NUM_DMICS=4,PDM1_MIC_A_ENABLE=1,PDM1_MIC_B_ENABLE=1,\ +DMIC0_ID=2,DMIC1_ID=3,PREPROCESS_PLUGINS=nhlt,NHLT_BIN=nhlt-sof-arl-rt711-4ch.bin,\ +HDMI1_ID=4,HDMI2_ID=5,HDMI3_ID=6,DMIC0_ENHANCED_CAPTURE=true,\ +EFX_DMIC0_TDFB_PARAMS=line4_pass,EFX_DMIC0_DRC_PARAMS=dmic_default" + "cavs-sdw\;sof-arl-rt722-l0_rt1320-l2\;PLATFORM=mtl,NUM_SDW_AMP_LINKS=1,SDW_DMIC=1,\ SDW_AMP_FEEDBACK=false,SDW_SPK_STREAM=Playback-SmartAmp,SDW_DMIC_STREAM=Capture-SmartMic,\ SDW_JACK_OUT_STREAM=Playback-SimpleJack,SDW_JACK_IN_STREAM=Capture-SimpleJack" @@ -169,67 +186,83 @@ EFX_DMIC0_TDFB_PARAMS=line2_generic_pm10deg,EFX_DMIC0_DRC_PARAMS=dmic_default" # Split topologies "cavs-sdw\;sof-arl-dmic-2ch-id2\;PLATFORM=mtl,SDW_JACK=false,NUM_HDMIS=0,NUM_DMICS=2,\ PDM1_MIC_A_ENABLE=0,PDM1_MIC_B_ENABLE=0,DMIC0_ID=2,DMIC1_ID=3,PREPROCESS_PLUGINS=nhlt,\ -NHLT_BIN=nhlt-sof-arl-dmic-2ch-id2.bin" +NHLT_BIN=nhlt-sof-arl-dmic-2ch-id2.bin,DMIC0_ENHANCED_CAPTURE=true,\ +EFX_DMIC0_TDFB_PARAMS=line2_generic_pm10deg,EFX_DMIC0_DRC_PARAMS=dmic_default" "cavs-sdw\;sof-arl-dmic-2ch-id3\;PLATFORM=mtl,SDW_JACK=false,NUM_HDMIS=0,NUM_DMICS=2,\ PDM1_MIC_A_ENABLE=0,PDM1_MIC_B_ENABLE=0,DMIC0_ID=3,DMIC1_ID=4,PREPROCESS_PLUGINS=nhlt,\ -NHLT_BIN=nhlt-sof-arl-dmic-2ch-id3.bin" +NHLT_BIN=nhlt-sof-arl-dmic-2ch-id3.bin,DMIC0_ENHANCED_CAPTURE=true,\ +EFX_DMIC0_TDFB_PARAMS=line2_generic_pm10deg,EFX_DMIC0_DRC_PARAMS=dmic_default" "cavs-sdw\;sof-arl-dmic-2ch-id4\;PLATFORM=mtl,SDW_JACK=false,NUM_HDMIS=0,NUM_DMICS=2,\ PDM1_MIC_A_ENABLE=0,PDM1_MIC_B_ENABLE=0,DMIC0_ID=4,DMIC1_ID=5,PREPROCESS_PLUGINS=nhlt,\ -NHLT_BIN=nhlt-sof-arl-dmic-2ch-id4.bin" +NHLT_BIN=nhlt-sof-arl-dmic-2ch-id4.bin,DMIC0_ENHANCED_CAPTURE=true,\ +EFX_DMIC0_TDFB_PARAMS=line2_generic_pm10deg,EFX_DMIC0_DRC_PARAMS=dmic_default" "cavs-sdw\;sof-arl-dmic-2ch-id5\;PLATFORM=mtl,SDW_JACK=false,NUM_HDMIS=0,NUM_DMICS=2,\ PDM1_MIC_A_ENABLE=0,PDM1_MIC_B_ENABLE=0,DMIC0_ID=5,DMIC1_ID=6,PREPROCESS_PLUGINS=nhlt,\ -NHLT_BIN=nhlt-sof-arl-dmic-2ch-id5.bin" +NHLT_BIN=nhlt-sof-arl-dmic-2ch-id5.bin,DMIC0_ENHANCED_CAPTURE=true,\ +EFX_DMIC0_TDFB_PARAMS=line2_generic_pm10deg,EFX_DMIC0_DRC_PARAMS=dmic_default" "cavs-sdw\;sof-arl-dmic-4ch-id2\;PLATFORM=mtl,SDW_JACK=false,NUM_HDMIS=0,NUM_DMICS=4,\ PDM1_MIC_A_ENABLE=1,PDM1_MIC_B_ENABLE=1,DMIC0_ID=2,DMIC1_ID=3,PREPROCESS_PLUGINS=nhlt,\ -NHLT_BIN=nhlt-sof-arl-dmic-4ch-id2.bin" +NHLT_BIN=nhlt-sof-arl-dmic-4ch-id2.bin,DMIC0_ENHANCED_CAPTURE=true,\ +EFX_DMIC0_TDFB_PARAMS=line4_pass,EFX_DMIC0_DRC_PARAMS=dmic_default" "cavs-sdw\;sof-arl-dmic-4ch-id3\;PLATFORM=mtl,SDW_JACK=false,NUM_HDMIS=0,NUM_DMICS=4,\ PDM1_MIC_A_ENABLE=1,PDM1_MIC_B_ENABLE=1,DMIC0_ID=3,DMIC1_ID=4,PREPROCESS_PLUGINS=nhlt,\ -NHLT_BIN=nhlt-sof-arl-dmic-4ch-id3.bin" +NHLT_BIN=nhlt-sof-arl-dmic-4ch-id3.bin,DMIC0_ENHANCED_CAPTURE=true,\ +EFX_DMIC0_TDFB_PARAMS=line4_pass,EFX_DMIC0_DRC_PARAMS=dmic_default" "cavs-sdw\;sof-arl-dmic-4ch-id4\;PLATFORM=mtl,SDW_JACK=false,NUM_HDMIS=0,NUM_DMICS=4,\ PDM1_MIC_A_ENABLE=1,PDM1_MIC_B_ENABLE=1,DMIC0_ID=4,DMIC1_ID=5,PREPROCESS_PLUGINS=nhlt,\ -NHLT_BIN=nhlt-sof-arl-dmic-4ch-id4.bin" +NHLT_BIN=nhlt-sof-arl-dmic-4ch-id4.bin,DMIC0_ENHANCED_CAPTURE=true,\ +EFX_DMIC0_TDFB_PARAMS=line4_pass,EFX_DMIC0_DRC_PARAMS=dmic_default" "cavs-sdw\;sof-arl-dmic-4ch-id5\;PLATFORM=mtl,SDW_JACK=false,NUM_HDMIS=0,NUM_DMICS=4,\ PDM1_MIC_A_ENABLE=1,PDM1_MIC_B_ENABLE=1,DMIC0_ID=5,DMIC1_ID=6,PREPROCESS_PLUGINS=nhlt,\ -NHLT_BIN=nhlt-sof-arl-dmic-4ch-id5.bin" +NHLT_BIN=nhlt-sof-arl-dmic-4ch-id5.bin,DMIC0_ENHANCED_CAPTURE=true,\ +EFX_DMIC0_TDFB_PARAMS=line4_pass,EFX_DMIC0_DRC_PARAMS=dmic_default" "cavs-sdw\;sof-mtl-dmic-2ch-id2\;PLATFORM=mtl,SDW_JACK=false,NUM_HDMIS=0,NUM_DMICS=2,\ PDM1_MIC_A_ENABLE=0,PDM1_MIC_B_ENABLE=0,DMIC0_ID=2,DMIC1_ID=3,PREPROCESS_PLUGINS=nhlt,\ -NHLT_BIN=nhlt-sof-mtl-dmic-2ch-id2.bin" +NHLT_BIN=nhlt-sof-mtl-dmic-2ch-id2.bin,DMIC0_ENHANCED_CAPTURE=true,\ +EFX_DMIC0_TDFB_PARAMS=line2_generic_pm10deg,EFX_DMIC0_DRC_PARAMS=dmic_default" "cavs-sdw\;sof-mtl-dmic-2ch-id3\;PLATFORM=mtl,SDW_JACK=false,NUM_HDMIS=0,NUM_DMICS=2,\ PDM1_MIC_A_ENABLE=0,PDM1_MIC_B_ENABLE=0,DMIC0_ID=3,DMIC1_ID=4,PREPROCESS_PLUGINS=nhlt,\ -NHLT_BIN=nhlt-sof-mtl-dmic-2ch-id3.bin" +NHLT_BIN=nhlt-sof-mtl-dmic-2ch-id3.bin,DMIC0_ENHANCED_CAPTURE=true,\ +EFX_DMIC0_TDFB_PARAMS=line2_generic_pm10deg,EFX_DMIC0_DRC_PARAMS=dmic_default" "cavs-sdw\;sof-mtl-dmic-2ch-id4\;PLATFORM=mtl,SDW_JACK=false,NUM_HDMIS=0,NUM_DMICS=2,\ PDM1_MIC_A_ENABLE=0,PDM1_MIC_B_ENABLE=0,DMIC0_ID=4,DMIC1_ID=5,PREPROCESS_PLUGINS=nhlt,\ -NHLT_BIN=nhlt-sof-mtl-dmic-2ch-id4.bin" +NHLT_BIN=nhlt-sof-mtl-dmic-2ch-id4.bin,DMIC0_ENHANCED_CAPTURE=true,\ +EFX_DMIC0_TDFB_PARAMS=line2_generic_pm10deg,EFX_DMIC0_DRC_PARAMS=dmic_default" "cavs-sdw\;sof-mtl-dmic-2ch-id5\;PLATFORM=mtl,SDW_JACK=false,NUM_HDMIS=0,NUM_DMICS=2,\ PDM1_MIC_A_ENABLE=0,PDM1_MIC_B_ENABLE=0,DMIC0_ID=5,DMIC1_ID=6,PREPROCESS_PLUGINS=nhlt,\ -NHLT_BIN=nhlt-sof-mtl-dmic-2ch-id5.bin" +NHLT_BIN=nhlt-sof-mtl-dmic-2ch-id5.bin,DMIC0_ENHANCED_CAPTURE=true,\ +EFX_DMIC0_TDFB_PARAMS=line2_generic_pm10deg,EFX_DMIC0_DRC_PARAMS=dmic_default" "cavs-sdw\;sof-mtl-dmic-4ch-id2\;PLATFORM=mtl,SDW_JACK=false,NUM_HDMIS=0,NUM_DMICS=4,\ PDM1_MIC_A_ENABLE=1,PDM1_MIC_B_ENABLE=1,DMIC0_ID=2,DMIC1_ID=3,PREPROCESS_PLUGINS=nhlt,\ -NHLT_BIN=nhlt-sof-mtl-dmic-4ch-id2.bin" +NHLT_BIN=nhlt-sof-mtl-dmic-4ch-id2.bin,DMIC0_ENHANCED_CAPTURE=true,\ +EFX_DMIC0_TDFB_PARAMS=line4_pass,EFX_DMIC0_DRC_PARAMS=dmic_default" "cavs-sdw\;sof-mtl-dmic-4ch-id3\;PLATFORM=mtl,SDW_JACK=false,NUM_HDMIS=0,NUM_DMICS=4,\ PDM1_MIC_A_ENABLE=1,PDM1_MIC_B_ENABLE=1,DMIC0_ID=3,DMIC1_ID=4,PREPROCESS_PLUGINS=nhlt,\ -NHLT_BIN=nhlt-sof-mtl-dmic-4ch-id3.bin" +NHLT_BIN=nhlt-sof-mtl-dmic-4ch-id3.bin,DMIC0_ENHANCED_CAPTURE=true,\ +EFX_DMIC0_TDFB_PARAMS=line4_pass,EFX_DMIC0_DRC_PARAMS=dmic_default" "cavs-sdw\;sof-mtl-dmic-4ch-id4\;PLATFORM=mtl,SDW_JACK=false,NUM_HDMIS=0,NUM_DMICS=4,\ PDM1_MIC_A_ENABLE=1,PDM1_MIC_B_ENABLE=1,DMIC0_ID=4,DMIC1_ID=5,PREPROCESS_PLUGINS=nhlt,\ -NHLT_BIN=nhlt-sof-mtl-dmic-4ch-id4.bin" +NHLT_BIN=nhlt-sof-mtl-dmic-4ch-id4.bin,DMIC0_ENHANCED_CAPTURE=true,\ +EFX_DMIC0_TDFB_PARAMS=line4_pass,EFX_DMIC0_DRC_PARAMS=dmic_default" "cavs-sdw\;sof-mtl-dmic-4ch-id5\;PLATFORM=mtl,SDW_JACK=false,NUM_HDMIS=0,NUM_DMICS=4,\ PDM1_MIC_A_ENABLE=1,PDM1_MIC_B_ENABLE=1,DMIC0_ID=5,DMIC1_ID=6,PREPROCESS_PLUGINS=nhlt,\ -NHLT_BIN=nhlt-sof-mtl-dmic-4ch-id5.bin" +NHLT_BIN=nhlt-sof-mtl-dmic-4ch-id5.bin,DMIC0_ENHANCED_CAPTURE=true,\ +EFX_DMIC0_TDFB_PARAMS=line4_pass,EFX_DMIC0_DRC_PARAMS=dmic_default" # Below topologies are used on Chromebooks diff --git a/tools/topology/topology2/production/tplg-targets-ace2.cmake b/tools/topology/topology2/production/tplg-targets-ace2.cmake index 7c066ad27cc1..9891b45812a8 100644 --- a/tools/topology/topology2/production/tplg-targets-ace2.cmake +++ b/tools/topology/topology2/production/tplg-targets-ace2.cmake @@ -27,6 +27,9 @@ SDW_JACK_OUT_STREAM=Playback-SimpleJack,SDW_JACK_IN_STREAM=Capture-SimpleJack" SDW_AMP_FEEDBACK=false,SDW_SPK_STREAM=Playback-SmartAmp,SDW_DMIC_STREAM=Capture-SmartMic,\ SDW_JACK_OUT_STREAM=Playback-SimpleJack,SDW_JACK_IN_STREAM=Capture-SimpleJack" +"cavs-sdw\;sof-lnl-rt713-l0-rt1318-l1\;PLATFORM=lnl,NUM_SDW_AMP_LINKS=1,\ +SDW_SPK_STREAM=SDW1-Playback,SDW_AMP_FEEDBACK=false" + "cavs-sdw\;sof-lnl-rt713-l0-rt1318-l1-2ch\;PLATFORM=lnl,NUM_SDW_AMP_LINKS=1,\ SDW_SPK_STREAM=SDW1-Playback,SDW_AMP_FEEDBACK=false,\ NUM_DMICS=2,PDM1_MIC_A_ENABLE=0,PDM1_MIC_B_ENABLE=0,\ @@ -37,35 +40,43 @@ EFX_DMIC0_TDFB_PARAMS=line2_generic_pm10deg,EFX_DMIC0_DRC_PARAMS=dmic_default" # Split topologies "cavs-sdw\;sof-lnl-dmic-2ch-id2\;PLATFORM=lnl,SDW_JACK=false,NUM_HDMIS=0,NUM_DMICS=2,\ PDM1_MIC_A_ENABLE=0,PDM1_MIC_B_ENABLE=0,DMIC0_ID=2,DMIC1_ID=3,PREPROCESS_PLUGINS=nhlt,\ -NHLT_BIN=nhlt-sof-lnl-dmic-2ch-id2.bin" +NHLT_BIN=nhlt-sof-lnl-dmic-2ch-id2.bin,DMIC0_ENHANCED_CAPTURE=true,\ +EFX_DMIC0_TDFB_PARAMS=line2_generic_pm10deg,EFX_DMIC0_DRC_PARAMS=dmic_default" "cavs-sdw\;sof-lnl-dmic-2ch-id3\;PLATFORM=lnl,SDW_JACK=false,NUM_HDMIS=0,NUM_DMICS=2,\ PDM1_MIC_A_ENABLE=0,PDM1_MIC_B_ENABLE=0,DMIC0_ID=3,DMIC1_ID=4,PREPROCESS_PLUGINS=nhlt,\ -NHLT_BIN=nhlt-sof-lnl-dmic-2ch-id3.bin" +NHLT_BIN=nhlt-sof-lnl-dmic-2ch-id3.bin,DMIC0_ENHANCED_CAPTURE=true,\ +EFX_DMIC0_TDFB_PARAMS=line2_generic_pm10deg,EFX_DMIC0_DRC_PARAMS=dmic_default" "cavs-sdw\;sof-lnl-dmic-2ch-id4\;PLATFORM=lnl,SDW_JACK=false,NUM_HDMIS=0,NUM_DMICS=2,\ PDM1_MIC_A_ENABLE=0,PDM1_MIC_B_ENABLE=0,DMIC0_ID=4,DMIC1_ID=5,PREPROCESS_PLUGINS=nhlt,\ -NHLT_BIN=nhlt-sof-lnl-dmic-2ch-id4.bin" +NHLT_BIN=nhlt-sof-lnl-dmic-2ch-id4.bin,DMIC0_ENHANCED_CAPTURE=true,\ +EFX_DMIC0_TDFB_PARAMS=line2_generic_pm10deg,EFX_DMIC0_DRC_PARAMS=dmic_default" "cavs-sdw\;sof-lnl-dmic-2ch-id5\;PLATFORM=lnl,SDW_JACK=false,NUM_HDMIS=0,NUM_DMICS=2,\ PDM1_MIC_A_ENABLE=0,PDM1_MIC_B_ENABLE=0,DMIC0_ID=5,DMIC1_ID=6,PREPROCESS_PLUGINS=nhlt,\ -NHLT_BIN=nhlt-sof-lnl-dmic-2ch-id5.bin" +NHLT_BIN=nhlt-sof-lnl-dmic-2ch-id5.bin,DMIC0_ENHANCED_CAPTURE=true,\ +EFX_DMIC0_TDFB_PARAMS=line2_generic_pm10deg,EFX_DMIC0_DRC_PARAMS=dmic_default" "cavs-sdw\;sof-lnl-dmic-4ch-id2\;PLATFORM=lnl,SDW_JACK=false,NUM_HDMIS=0,NUM_DMICS=4,\ PDM1_MIC_A_ENABLE=1,PDM1_MIC_B_ENABLE=1,DMIC0_ID=2,DMIC1_ID=3,PREPROCESS_PLUGINS=nhlt,\ -NHLT_BIN=nhlt-sof-lnl-dmic-4ch-id2.bin" +NHLT_BIN=nhlt-sof-lnl-dmic-4ch-id2.bin,DMIC0_ENHANCED_CAPTURE=true,\ +EFX_DMIC0_TDFB_PARAMS=line4_pass,EFX_DMIC0_DRC_PARAMS=dmic_default" "cavs-sdw\;sof-lnl-dmic-4ch-id3\;PLATFORM=lnl,SDW_JACK=false,NUM_HDMIS=0,NUM_DMICS=4,\ PDM1_MIC_A_ENABLE=1,PDM1_MIC_B_ENABLE=1,DMIC0_ID=3,DMIC1_ID=4,PREPROCESS_PLUGINS=nhlt,\ -NHLT_BIN=nhlt-sof-lnl-dmic-4ch-id3.bin" +NHLT_BIN=nhlt-sof-lnl-dmic-4ch-id3.bin,DMIC0_ENHANCED_CAPTURE=true,\ +EFX_DMIC0_TDFB_PARAMS=line4_pass,EFX_DMIC0_DRC_PARAMS=dmic_default" "cavs-sdw\;sof-lnl-dmic-4ch-id4\;PLATFORM=lnl,SDW_JACK=false,NUM_HDMIS=0,NUM_DMICS=4,\ PDM1_MIC_A_ENABLE=1,PDM1_MIC_B_ENABLE=1,DMIC0_ID=4,DMIC1_ID=5,PREPROCESS_PLUGINS=nhlt,\ -NHLT_BIN=nhlt-sof-lnl-dmic-4ch-id4.bin" +NHLT_BIN=nhlt-sof-lnl-dmic-4ch-id4.bin,DMIC0_ENHANCED_CAPTURE=true,\ +EFX_DMIC0_TDFB_PARAMS=line4_pass,EFX_DMIC0_DRC_PARAMS=dmic_default" "cavs-sdw\;sof-lnl-dmic-4ch-id5\;PLATFORM=lnl,SDW_JACK=false,NUM_HDMIS=0,NUM_DMICS=4,\ PDM1_MIC_A_ENABLE=1,PDM1_MIC_B_ENABLE=1,DMIC0_ID=5,DMIC1_ID=6,PREPROCESS_PLUGINS=nhlt,\ -NHLT_BIN=nhlt-sof-lnl-dmic-4ch-id5.bin" +NHLT_BIN=nhlt-sof-lnl-dmic-4ch-id5.bin,DMIC0_ENHANCED_CAPTURE=true,\ +EFX_DMIC0_TDFB_PARAMS=line4_pass,EFX_DMIC0_DRC_PARAMS=dmic_default" # No SDW Jack. SDW DMIC+SPK "cavs-sdw\;sof-lnl-rt1318-l12-rt714-l0\;PLATFORM=lnl,SDW_JACK=false,SDW_DMIC=1,\ diff --git a/tools/topology/topology2/production/tplg-targets-ace3.cmake b/tools/topology/topology2/production/tplg-targets-ace3.cmake index d83e2e2637d3..953964b068a9 100644 --- a/tools/topology/topology2/production/tplg-targets-ace3.cmake +++ b/tools/topology/topology2/production/tplg-targets-ace3.cmake @@ -2,7 +2,29 @@ # Array of "input-file-name;output-file-name;comma separated pre-processor variables" list(APPEND TPLGS +# MOCKUP topology for PTL +"cavs-sdw\;sof-ptl-rt711-rt1308-rt715\;NUM_SDW_AMP_LINKS=2,SDW_DMIC=1,SDW_AMP_FEEDBACK=false" + +"cavs-sdw\;sof-ptl-rt711-rt1308-mono-rt715\;NUM_SDW_AMP_LINKS=1,SDW_DMIC=1,SDW_AMP_FEEDBACK=false" + +"cavs-sdw\;sof-ptl-rt715-rt711-rt1308-mono\;NUM_SDW_AMP_LINKS=1,SDW_DMIC=1,\ +SDW_JACK_OUT_STREAM=SDW1-Playback,SDW_JACK_IN_STREAM=SDW1-Capture,\ +SDW_SPK_STREAM=SDW2-Playback,SDW_DMIC_STREAM=SDW0-Capture,SDW_AMP_FEEDBACK=false,\ +SDW_SPK_ENHANCED_PLAYBACK=false,SDW_DMIC_ENHANCED_CAPTURE=false" + # SDW topology for PTL RVP +"cavs-sdw\;sof-ptl-rt711\;PLATFORM=ptl,HDMI1_ID=2,HDMI2_ID=3,HDMI3_ID=4" + +"cavs-sdw\;sof-ptl-rt711-2ch\;PLATFORM=ptl,NUM_DMICS=2,PDM1_MIC_A_ENABLE=0,PDM1_MIC_B_ENABLE=0,\ +DMIC0_ID=2,DMIC1_ID=3,PREPROCESS_PLUGINS=nhlt,NHLT_BIN=nhlt-sof-ptl-rt711-2ch.bin,\ +HDMI1_ID=4,HDMI2_ID=5,HDMI3_ID=6,DMIC0_ENHANCED_CAPTURE=true,\ +EFX_DMIC0_TDFB_PARAMS=line2_generic_pm10deg,EFX_DMIC0_DRC_PARAMS=dmic_default" + +"cavs-sdw\;sof-ptl-rt711-4ch\;PLATFORM=ptl,NUM_DMICS=4,PDM1_MIC_A_ENABLE=1,PDM1_MIC_B_ENABLE=1,\ +DMIC0_ID=2,DMIC1_ID=3,PREPROCESS_PLUGINS=nhlt,NHLT_BIN=nhlt-sof-ptl-rt711-4ch.bin,\ +HDMI1_ID=4,HDMI2_ID=5,HDMI3_ID=6,DMIC0_ENHANCED_CAPTURE=true,\ +EFX_DMIC0_TDFB_PARAMS=line4_pass,EFX_DMIC0_DRC_PARAMS=dmic_default" + # RT721 eval board with SDW-DMIC, sof_sdw_quirk_table without SOC_SDW_PCH_DMIC "cavs-sdw\;sof-ptl-rt721\;PLATFORM=ptl,SDW_DMIC=1,NUM_SDW_AMP_LINKS=1,\ SDW_AMP_FEEDBACK=false,SDW_SPK_STREAM=Playback-SmartAmp,SDW_DMIC_STREAM=Capture-SmartMic,\ @@ -17,6 +39,16 @@ PREPROCESS_PLUGINS=nhlt,NHLT_BIN=nhlt-sof-ptl-rt721-4ch.bin,DMIC0_ENHANCED_CAPTU EFX_DMIC0_TDFB_PARAMS=line4_pass,EFX_DMIC0_DRC_PARAMS=dmic_default,\ DEEPBUFFER_FW_DMA_MS=10,DEEP_BUF_SPK=true" +# RT721 eval board with PCH-DMIC, sof_sdw_quirk_table with SOC_SDW_PCH_DMIC, DMIC1 with 96kHz +"cavs-sdw\;sof-ptl-rt721-4ch-dmic1-96k\;PLATFORM=ptl,SDW_DMIC=1,NUM_SDW_AMP_LINKS=1,NUM_DMICS=4,\ +PDM1_MIC_A_ENABLE=1,PDM1_MIC_B_ENABLE=1,DMIC0_ID=5,DMIC1_ID=6,HDMI1_ID=7,HDMI2_ID=8,HDMI3_ID=9,\ +SDW_AMP_FEEDBACK=false,SDW_SPK_STREAM=Playback-SmartAmp,SDW_DMIC_STREAM=Capture-SmartMic,\ +SDW_JACK_OUT_STREAM=Playback-SimpleJack,SDW_JACK_IN_STREAM=Capture-SimpleJack,\ +PREPROCESS_PLUGINS=nhlt,NHLT_BIN=nhlt-sof-ptl-rt721-4ch-dmic1-96k.bin,\ +DMIC0_ENHANCED_CAPTURE=true,EFX_DMIC0_TDFB_PARAMS=line4_pass,EFX_DMIC0_DRC_PARAMS=dmic_default,\ +DMIC1_ENABLE=passthrough,DMIC1_RATE=96000,DMIC1_PCM_ID=11,\ +DEEPBUFFER_FW_DMA_MS=10,DEEP_BUF_SPK=true" + # RT721 eval board with PCH-DMIC, sof_sdw_quirk_table with SOC_SDW_PCH_DMIC, 96KHz Jack-rate "cavs-sdw\;sof-ptl-rt721-4ch-96k\;PLATFORM=ptl,SDW_DMIC=1,NUM_SDW_AMP_LINKS=1,NUM_DMICS=4,\ PDM1_MIC_A_ENABLE=1,PDM1_MIC_B_ENABLE=1,DMIC0_ID=5,DMIC1_ID=6,HDMI1_ID=7,HDMI2_ID=8,HDMI3_ID=9,\ @@ -26,6 +58,24 @@ PREPROCESS_PLUGINS=nhlt,NHLT_BIN=nhlt-sof-ptl-rt721-4ch.bin,DMIC0_ENHANCED_CAPTU EFX_DMIC0_TDFB_PARAMS=line4_pass,EFX_DMIC0_DRC_PARAMS=dmic_default,\ DEEPBUFFER_FW_DMA_MS=10,DEEP_BUF_SPK=true,JACK_RATE=96000,DEEP_BUF_JACK_RATE=96000" +"cavs-sdw\;sof-ptl-rt721-l3-rt1320-l3-4ch-ssp2-bt\;PLATFORM=ptl,NUM_SDW_AMP_LINKS=1,NUM_DMICS=4,\ +PDM1_MIC_A_ENABLE=1,PDM1_MIC_B_ENABLE=1,DMIC0_ID=3,DMIC1_ID=4,\ +SDW_AMP_FEEDBACK=false,SDW_SPK_STREAM=Playback-SmartAmp,\ +SDW_JACK_OUT_STREAM=Playback-SimpleJack,SDW_JACK_IN_STREAM=Capture-SimpleJack,\ +PREPROCESS_PLUGINS=nhlt,NHLT_BIN=nhlt-sof-ptl-rt721-l3-rt1320-l3-4ch-ssp2-bt.bin,\ +DMIC0_ENHANCED_CAPTURE=true,EFX_DMIC0_TDFB_PARAMS=line4_pass,EFX_DMIC0_DRC_PARAMS=dmic_default,\ +DEEP_BUF_SPK=true,BT_PCM_ID=8,BT_ID=8,BT_PCM_NAME=Bluetooth,ADD_BT=true" + +# DTS +"cavs-sdw\;sof-ptl-rt721-l3-rt1320-l3-4ch-ssp2-bt-dts\;PLATFORM=ptl,NUM_SDW_AMP_LINKS=1,NUM_DMICS=4,\ +PDM1_MIC_A_ENABLE=1,PDM1_MIC_B_ENABLE=1,DMIC0_ID=3,DMIC1_ID=4,\ +SDW_AMP_FEEDBACK=false,SDW_SPK_STREAM=Playback-SmartAmp,\ +SDW_JACK_OUT_STREAM=Playback-SimpleJack,SDW_JACK_IN_STREAM=Capture-SimpleJack,\ +PREPROCESS_PLUGINS=nhlt,NHLT_BIN=nhlt-sof-ptl-rt721-l3-rt1320-l3-4ch-ssp2-bt.bin,\ +DMIC0_ENHANCED_CAPTURE=true,EFX_DMIC0_TDFB_PARAMS=line4_pass,EFX_DMIC0_DRC_PARAMS=dmic_default,\ +DEEP_BUF_SPK=true,BT_PCM_ID=8,BT_ID=8,BT_PCM_NAME=Bluetooth,ADD_BT=true,SDW_AMP_PIPELINE_SRC=dts,\ +SDW_JACK_PIPELINE_SRC=dts" + "cavs-sdw\;sof-ptl-rt722\;PLATFORM=ptl,SDW_DMIC=1,NUM_SDW_AMP_LINKS=1,\ SDW_AMP_FEEDBACK=false,SDW_SPK_STREAM=Playback-SmartAmp,SDW_DMIC_STREAM=Capture-SmartMic,\ SDW_JACK_OUT_STREAM=Playback-SimpleJack,SDW_JACK_IN_STREAM=Capture-SimpleJack" @@ -56,6 +106,25 @@ SDW_JACK_OUT_STREAM=Playback-SimpleJack,SDW_JACK_IN_STREAM=Capture-SimpleJack,\ PREPROCESS_PLUGINS=nhlt,NHLT_BIN=nhlt-sof-ptl-rt722-2ch.bin,DMIC0_ENHANCED_CAPTURE=true,\ EFX_DMIC0_TDFB_PARAMS=line2_generic_pm10deg,EFX_DMIC0_DRC_PARAMS=dmic_default" +"cavs-sdw\;sof-ptl-rt722-l0-rt1320-l23\;PLATFORM=ptl,SDW_DMIC=1,NUM_SDW_AMP_LINKS=2,\ +SDW_AMP_FEEDBACK=false,SDW_SPK_STREAM=Playback-SmartAmp,SDW_DMIC_STREAM=Capture-SmartMic,\ +SDW_JACK_OUT_STREAM=Playback-SimpleJack,SDW_JACK_IN_STREAM=Capture-SimpleJack" + +"cavs-sdw\;sof-ptl-rt722-l0-rt1320-l23-2ch\;PLATFORM=ptl,NUM_SDW_AMP_LINKS=2,NUM_DMICS=2,\ +PDM1_MIC_A_ENABLE=0,PDM1_MIC_B_ENABLE=0,DMIC0_ID=3,DMIC1_ID=4,\ +SDW_AMP_FEEDBACK=false,SDW_SPK_STREAM=SDW2-Playback,\ +PREPROCESS_PLUGINS=nhlt,NHLT_BIN=nhlt-sof-ptl-rt722-l0-rt1320-l23-2ch.bin,DMIC0_ENHANCED_CAPTURE=true,\ +EFX_DMIC0_TDFB_PARAMS=line4_pass,EFX_DMIC0_DRC_PARAMS=dmic_default" + +"cavs-sdw\;sof-ptl-rt722-l0-rt1320-l23-4ch\;PLATFORM=ptl,NUM_SDW_AMP_LINKS=2,NUM_DMICS=4,\ +PDM1_MIC_A_ENABLE=1,PDM1_MIC_B_ENABLE=1,DMIC0_ID=5,DMIC1_ID=6,HDMI1_ID=7,HDMI2_ID=8,HDMI3_ID=9,\ +SDW_AMP_FEEDBACK=false,SDW_SPK_STREAM=Playback-SmartAmp,\ +SDW_JACK_OUT_STREAM=Playback-SimpleJack,SDW_JACK_IN_STREAM=Capture-SimpleJack,\ +PREPROCESS_PLUGINS=nhlt,NHLT_BIN=nhlt-sof-ptl-rt722-l0-rt1320-l23-4ch.bin,DMIC0_ENHANCED_CAPTURE=true,\ +EFX_DMIC0_TDFB_PARAMS=line4_pass,EFX_DMIC0_DRC_PARAMS=dmic_default,\ +DEEPBUFFER_FW_DMA_MS=10,DEEP_BUF_SPK=true,\ +BT_PCM_ID=20,BT_ID=10,BT_PCM_NAME=Bluetooth,ADD_BT=true" + "cavs-sdw\;sof-ptl-rt712-l2-rt1320-l1\;PLATFORM=ptl,SDW_DMIC=1,NUM_SDW_AMP_LINKS=2,\ SDW_AMP_FEEDBACK=false,SDW_SPK_STREAM=Playback-SmartAmp,SDW_DMIC_STREAM=Capture-SmartMic,\ SDW_JACK_OUT_STREAM=Playback-SimpleJack,SDW_JACK_IN_STREAM=Capture-SimpleJack" @@ -68,6 +137,10 @@ SDW_JACK_OUT_STREAM=Playback-SimpleJack,SDW_JACK_IN_STREAM=Capture-SimpleJack" SDW_AMP_FEEDBACK=false,SDW_SPK_STREAM=Playback-SmartAmp,SDW_DMIC_STREAM=Capture-SmartMic,\ SDW_JACK_OUT_STREAM=Playback-SimpleJack,SDW_JACK_IN_STREAM=Capture-SimpleJack" +"cavs-sdw\;sof-ptl-rt712-l3-rt1320-l3-ctc\;PLATFORM=ptl,SDW_DMIC=1,NUM_SDW_AMP_CTC_LINKS=1,\ +SDW_AMP_FEEDBACK=false,SDW_SPK_STREAM=Playback-SmartAmp,SDW_DMIC_STREAM=Capture-SmartMic,\ +SDW_JACK_OUT_STREAM=Playback-SimpleJack,SDW_JACK_IN_STREAM=Capture-SimpleJack" + "cavs-sdw\;sof-ptl-rt712-l3-rt1320-l3-4ch\;PLATFORM=ptl,NUM_SDW_AMP_LINKS=1,NUM_DMICS=4,\ PDM1_MIC_A_ENABLE=1,PDM1_MIC_B_ENABLE=1,DMIC0_ID=5,DMIC1_ID=6,HDMI1_ID=7,HDMI2_ID=8,HDMI3_ID=9,\ SDW_AMP_FEEDBACK=false,SDW_SPK_STREAM=Playback-SmartAmp,SDW_DMIC_STREAM=Capture-SmartMic,\ @@ -85,10 +158,38 @@ SDW_JACK_OUT_STREAM=Playback-SimpleJack,SDW_JACK_IN_STREAM=Capture-SimpleJack" SDW_AMP_FEEDBACK=false,SDW_SPK_STREAM=Playback-SmartAmp,SDW_DMIC_STREAM=Capture-SmartMic,\ SDW_JACK_OUT_STREAM=Playback-SimpleJack,SDW_JACK_IN_STREAM=Capture-SimpleJack" +# LG Gram Pro 2026 (16Z90U-KU7BK) with RT713 on link 3 and single stereo RT1320 on link 1 +"cavs-sdw\;sof-ptl-rt713-l3-rt1320-l1\;PLATFORM=ptl,SDW_DMIC=0,NUM_SDW_AMP_LINKS=1,\ +NUM_DMICS=2,PDM1_MIC_A_ENABLE=0,PDM1_MIC_B_ENABLE=0,DMIC0_ID=3,DMIC1_ID=4,\ +SDW_AMP_FEEDBACK=false,SDW_SPK_STREAM=SDW1-Playback,\ +SDW_JACK_OUT_STREAM=SDW3-Playback,SDW_JACK_IN_STREAM=SDW3-Capture,\ +PREPROCESS_PLUGINS=nhlt,NHLT_BIN=nhlt-sof-ptl-rt713-l3-rt1320-l1.bin" + "cavs-sdw\;sof-ptl-cs42l43-l2-cs35l56x6-l13\;NUM_SDW_AMP_LINKS=3,SDW_DMIC=1,\ SDW_AMP_FEEDBACK=false,SDW_SPK_STREAM=Playback-SmartAmp,SDW_DMIC_STREAM=Capture-SmartMic,\ SDW_JACK_OUT_STREAM=Playback-SimpleJack,SDW_JACK_IN_STREAM=Capture-SimpleJack" +"cavs-sdw\;sof-ptl-cs42l43-agg-l3-cs35l56-l2\;PLATFORM=ptl,SDW_DMIC=1,NUM_SDW_AMP_LINKS=2,\ +SDW_AMP_FEEDBACK=false,SDW_SPK_STREAM=Playback-SmartAmp,SDW_DMIC_STREAM=Capture-SmartMic,\ +SDW_JACK_OUT_STREAM=Playback-SimpleJack,SDW_JACK_IN_STREAM=Capture-SimpleJack,\ +DEEPBUFFER_FW_DMA_MS=10,DEEP_BUF_SPK=true,BT_PCM_ID=20,BT_ID=8,BT_PCM_NAME=Bluetooth,ADD_BT=true,\ +SDW_SPK_ENHANCED_PLAYBACK=false" + +"cavs-sdw\;sof-ptl-cs42l43-agg-l3-cs35l56-l2-dax\;PLATFORM=ptl,SDW_DMIC=1,NUM_SDW_AMP_LINKS=2,\ +SDW_AMP_FEEDBACK=false,SDW_SPK_STREAM=Playback-SmartAmp,SDW_DMIC_STREAM=Capture-SmartMic,\ +SDW_JACK_OUT_STREAM=Playback-SimpleJack,SDW_JACK_IN_STREAM=Capture-SimpleJack,\ +DEEPBUFFER_FW_DMA_MS=10,DEEP_BUF_SPK=true,BT_PCM_ID=20,BT_ID=8,BT_PCM_NAME=Bluetooth,ADD_BT=true,\ +SDW_AMP_PIPELINE_SRC=dax,SDW_JACK_PIPELINE_SRC=dax,DOLBY_DAX_CORE_ID=0" + +"cavs-sdw\;sof-ptl-cs42l43-agg-l3-cs35l56-l2-4ch\;PLATFORM=ptl,NUM_SDW_AMP_LINKS=2,NUM_DMICS=4,\ +PDM1_MIC_A_ENABLE=1,PDM1_MIC_B_ENABLE=1,DMIC0_ID=3,DMIC1_ID=4,\ +SDW_AMP_FEEDBACK=false,SDW_SPK_STREAM=Playback-SmartAmp,\ +SDW_JACK_OUT_STREAM=Playback-SimpleJack,SDW_JACK_IN_STREAM=Capture-SimpleJack,\ +PREPROCESS_PLUGINS=nhlt,NHLT_BIN=nhlt-sof-ptl-cs42l43-agg-l3-cs35l56-l2-4ch.bin,\ +DMIC0_ENHANCED_CAPTURE=true,EFX_DMIC0_TDFB_PARAMS=line4_pass,EFX_DMIC0_DRC_PARAMS=dmic_default,\ +DEEPBUFFER_FW_DMA_MS=10,DEEP_BUF_SPK=true,BT_PCM_ID=20,BT_ID=8,BT_PCM_NAME=Bluetooth,ADD_BT=true,\ +SDW_SPK_ENHANCED_PLAYBACK=false" + # SSP codec topologies for PTL # ES83x6 codec alone without HDMI-in capture "cavs-es83x6\;sof-ptl-es8336-ssp1\;PLATFORM=ptl,PREPROCESS_PLUGINS=nhlt,\ @@ -107,36 +208,137 @@ HDMI_IN_CAPTURE=true" NHLT_BIN=nhlt-sof-ptl-hdmi-ssp02.bin,HEADSET_CODEC=false,HDMI_IN_CAPTURE=true,\ HDMI_IN_1_ID=0,HDMI_IN_2_ID=1" +# Topology for PTL with rt5682 (SSP 0), tas2563 (SSP 1), BT (SSP 2) and PDM0 enabled +"cavs-rt5682\;sof-ptl-tas2563-rt5682\;PLATFORM=ptl,NUM_DMICS=2,DMIC0_PCM_ID=99,PREPROCESS_PLUGINS=nhlt,\ +NHLT_BIN=nhlt-sof-ptl-tas2563-rt5682.bin,SPK_ID=6,DEEPBUFFER_FW_DMA_MS=10,INCLUDE_ECHO_REF=true,\ +BT_NAME=SSP2-BT,BT_ID=7,BT_PCM_NAME=Bluetooth,DEEP_BUF_SPK=true" + +"cavs-rt5682\;sof-ptl-tas2563-rt5682-ctc\;PLATFORM=ptl,NUM_DMICS=2,DMIC0_PCM_ID=99,PREPROCESS_PLUGINS=nhlt,\ +NHLT_BIN=nhlt-sof-ptl-tas2563-rt5682-ctc.bin,SPK_ID=6,DEEPBUFFER_FW_DMA_MS=10,INCLUDE_ECHO_REF=true,\ +BT_NAME=SSP2-BT,BT_ID=7,BT_PCM_NAME=Bluetooth,DEEP_BUF_SPK=true,PLAYBACK_PIPELINE_SRC=ctc,USE_CTC_SPK=true" + +"cavs-rt5682\;sof-ptl-tas2563-rt5682-dax\;PLATFORM=ptl,NUM_DMICS=2,DMIC0_PCM_ID=99,\ +PREPROCESS_PLUGINS=nhlt,NHLT_BIN=nhlt-sof-ptl-tas2563-rt5682-dax.bin,SPK_ID=6,DEEPBUFFER_FW_DMA_MS=10,\ +INCLUDE_ECHO_REF=true,BT_NAME=SSP2-BT,BT_ID=7,BT_PCM_NAME=Bluetooth,DEEP_BUF_SPK=true,\ +PLAYBACK_PIPELINE_SRC=dax,SSP_HEADSET_DAX=true,DOLBY_DAX_CORE_ID=0" + +"cavs-rt5682\;sof-ptl-tas2563-rt5682-dax-ctc\;PLATFORM=ptl,NUM_DMICS=2,DMIC0_PCM_ID=99,\ +PREPROCESS_PLUGINS=nhlt,NHLT_BIN=nhlt-sof-ptl-tas2563-rt5682-dax-ctc.bin,SPK_ID=6,DEEPBUFFER_FW_DMA_MS=10,\ +INCLUDE_ECHO_REF=true,BT_NAME=SSP2-BT,BT_ID=7,BT_PCM_NAME=Bluetooth,DEEP_BUF_SPK=true,\ +PLAYBACK_PIPELINE_SRC=dax,DOLBY_DAX_CORE_ID=0,USE_CTC_SPK=true" + + # Split topologies "cavs-sdw\;sof-ptl-dmic-2ch-id2\;PLATFORM=ptl,SDW_JACK=false,NUM_HDMIS=0,NUM_DMICS=2,\ PDM1_MIC_A_ENABLE=0,PDM1_MIC_B_ENABLE=0,DMIC0_ID=2,DMIC1_ID=3,PREPROCESS_PLUGINS=nhlt,\ -NHLT_BIN=nhlt-sof-ptl-dmic-2ch-id2.bin" +NHLT_BIN=nhlt-sof-ptl-dmic-2ch-id2.bin,DMIC0_ENHANCED_CAPTURE=true,\ +EFX_DMIC0_TDFB_PARAMS=line2_generic_pm10deg,EFX_DMIC0_DRC_PARAMS=dmic_default" "cavs-sdw\;sof-ptl-dmic-2ch-id3\;PLATFORM=ptl,SDW_JACK=false,NUM_HDMIS=0,NUM_DMICS=2,\ PDM1_MIC_A_ENABLE=0,PDM1_MIC_B_ENABLE=0,DMIC0_ID=3,DMIC1_ID=4,PREPROCESS_PLUGINS=nhlt,\ -NHLT_BIN=nhlt-sof-ptl-dmic-2ch-id3.bin" +NHLT_BIN=nhlt-sof-ptl-dmic-2ch-id3.bin,DMIC0_ENHANCED_CAPTURE=true,\ +EFX_DMIC0_TDFB_PARAMS=line2_generic_pm10deg,EFX_DMIC0_DRC_PARAMS=dmic_default" "cavs-sdw\;sof-ptl-dmic-2ch-id4\;PLATFORM=ptl,SDW_JACK=false,NUM_HDMIS=0,NUM_DMICS=2,\ PDM1_MIC_A_ENABLE=0,PDM1_MIC_B_ENABLE=0,DMIC0_ID=4,DMIC1_ID=5,PREPROCESS_PLUGINS=nhlt,\ -NHLT_BIN=nhlt-sof-ptl-dmic-2ch-id4.bin" +NHLT_BIN=nhlt-sof-ptl-dmic-2ch-id4.bin,DMIC0_ENHANCED_CAPTURE=true,\ +EFX_DMIC0_TDFB_PARAMS=line2_generic_pm10deg,EFX_DMIC0_DRC_PARAMS=dmic_default" "cavs-sdw\;sof-ptl-dmic-2ch-id5\;PLATFORM=ptl,SDW_JACK=false,NUM_HDMIS=0,NUM_DMICS=2,\ PDM1_MIC_A_ENABLE=0,PDM1_MIC_B_ENABLE=0,DMIC0_ID=5,DMIC1_ID=6,PREPROCESS_PLUGINS=nhlt,\ -NHLT_BIN=nhlt-sof-ptl-dmic-2ch-id5.bin" +NHLT_BIN=nhlt-sof-ptl-dmic-2ch-id5.bin,DMIC0_ENHANCED_CAPTURE=true,\ +EFX_DMIC0_TDFB_PARAMS=line2_generic_pm10deg,EFX_DMIC0_DRC_PARAMS=dmic_default" "cavs-sdw\;sof-ptl-dmic-4ch-id2\;PLATFORM=ptl,SDW_JACK=false,NUM_HDMIS=0,NUM_DMICS=4,\ PDM1_MIC_A_ENABLE=1,PDM1_MIC_B_ENABLE=1,DMIC0_ID=2,DMIC1_ID=3,PREPROCESS_PLUGINS=nhlt,\ -NHLT_BIN=nhlt-sof-ptl-dmic-4ch-id2.bin" +NHLT_BIN=nhlt-sof-ptl-dmic-4ch-id2.bin,DMIC0_ENHANCED_CAPTURE=true,\ +EFX_DMIC0_TDFB_PARAMS=line4_pass,EFX_DMIC0_DRC_PARAMS=dmic_default" "cavs-sdw\;sof-ptl-dmic-4ch-id3\;PLATFORM=ptl,SDW_JACK=false,NUM_HDMIS=0,NUM_DMICS=4,\ PDM1_MIC_A_ENABLE=1,PDM1_MIC_B_ENABLE=1,DMIC0_ID=3,DMIC1_ID=4,PREPROCESS_PLUGINS=nhlt,\ -NHLT_BIN=nhlt-sof-ptl-dmic-4ch-id3.bin" +NHLT_BIN=nhlt-sof-ptl-dmic-4ch-id3.bin,DMIC0_ENHANCED_CAPTURE=true,\ +EFX_DMIC0_TDFB_PARAMS=line4_pass,EFX_DMIC0_DRC_PARAMS=dmic_default" "cavs-sdw\;sof-ptl-dmic-4ch-id4\;PLATFORM=ptl,SDW_JACK=false,NUM_HDMIS=0,NUM_DMICS=4,\ PDM1_MIC_A_ENABLE=1,PDM1_MIC_B_ENABLE=1,DMIC0_ID=4,DMIC1_ID=5,PREPROCESS_PLUGINS=nhlt,\ -NHLT_BIN=nhlt-sof-ptl-dmic-4ch-id4.bin" +NHLT_BIN=nhlt-sof-ptl-dmic-4ch-id4.bin,DMIC0_ENHANCED_CAPTURE=true,\ +EFX_DMIC0_TDFB_PARAMS=line4_pass,EFX_DMIC0_DRC_PARAMS=dmic_default" "cavs-sdw\;sof-ptl-dmic-4ch-id5\;PLATFORM=ptl,SDW_JACK=false,NUM_HDMIS=0,NUM_DMICS=4,\ PDM1_MIC_A_ENABLE=1,PDM1_MIC_B_ENABLE=1,DMIC0_ID=5,DMIC1_ID=6,PREPROCESS_PLUGINS=nhlt,\ -NHLT_BIN=nhlt-sof-ptl-dmic-4ch-id5.bin" +NHLT_BIN=nhlt-sof-ptl-dmic-4ch-id5.bin,DMIC0_ENHANCED_CAPTURE=true,\ +EFX_DMIC0_TDFB_PARAMS=line4_pass,EFX_DMIC0_DRC_PARAMS=dmic_default" + +# for WCL +"cavs-sdw\;sof-wcl-dmic-2ch-id2\;PLATFORM=wcl,SDW_JACK=false,NUM_HDMIS=0,NUM_DMICS=2,\ +PDM1_MIC_A_ENABLE=0,PDM1_MIC_B_ENABLE=0,DMIC0_ID=2,DMIC1_ID=3,PREPROCESS_PLUGINS=nhlt,\ +NHLT_BIN=nhlt-sof-wcl-dmic-2ch-id2.bin,DMIC0_ENHANCED_CAPTURE=true,\ +EFX_DMIC0_TDFB_PARAMS=line2_generic_pm10deg,EFX_DMIC0_DRC_PARAMS=dmic_default" + +"cavs-sdw\;sof-wcl-dmic-2ch-id3\;PLATFORM=wcl,SDW_JACK=false,NUM_HDMIS=0,NUM_DMICS=2,\ +PDM1_MIC_A_ENABLE=0,PDM1_MIC_B_ENABLE=0,DMIC0_ID=3,DMIC1_ID=4,PREPROCESS_PLUGINS=nhlt,\ +NHLT_BIN=nhlt-sof-wcl-dmic-2ch-id3.bin,DMIC0_ENHANCED_CAPTURE=true,\ +EFX_DMIC0_TDFB_PARAMS=line2_generic_pm10deg,EFX_DMIC0_DRC_PARAMS=dmic_default" + +"cavs-sdw\;sof-wcl-dmic-2ch-id4\;PLATFORM=wcl,SDW_JACK=false,NUM_HDMIS=0,NUM_DMICS=2,\ +PDM1_MIC_A_ENABLE=0,PDM1_MIC_B_ENABLE=0,DMIC0_ID=4,DMIC1_ID=5,PREPROCESS_PLUGINS=nhlt,\ +NHLT_BIN=nhlt-sof-wcl-dmic-2ch-id4.bin,DMIC0_ENHANCED_CAPTURE=true,\ +EFX_DMIC0_TDFB_PARAMS=line2_generic_pm10deg,EFX_DMIC0_DRC_PARAMS=dmic_default" + +"cavs-sdw\;sof-wcl-dmic-2ch-id5\;PLATFORM=wcl,SDW_JACK=false,NUM_HDMIS=0,NUM_DMICS=2,\ +PDM1_MIC_A_ENABLE=0,PDM1_MIC_B_ENABLE=0,DMIC0_ID=5,DMIC1_ID=6,PREPROCESS_PLUGINS=nhlt,\ +NHLT_BIN=nhlt-sof-wcl-dmic-2ch-id5.bin,DMIC0_ENHANCED_CAPTURE=true,\ +EFX_DMIC0_TDFB_PARAMS=line2_generic_pm10deg,EFX_DMIC0_DRC_PARAMS=dmic_default" + +"cavs-sdw\;sof-wcl-dmic-4ch-id2\;PLATFORM=wcl,SDW_JACK=false,NUM_HDMIS=0,NUM_DMICS=4,\ +PDM1_MIC_A_ENABLE=1,PDM1_MIC_B_ENABLE=1,DMIC0_ID=2,DMIC1_ID=3,PREPROCESS_PLUGINS=nhlt,\ +NHLT_BIN=nhlt-sof-wcl-dmic-4ch-id2.bin,DMIC0_ENHANCED_CAPTURE=true,\ +EFX_DMIC0_TDFB_PARAMS=line4_pass,EFX_DMIC0_DRC_PARAMS=dmic_default" + +"cavs-sdw\;sof-wcl-dmic-4ch-id3\;PLATFORM=wcl,SDW_JACK=false,NUM_HDMIS=0,NUM_DMICS=4,\ +PDM1_MIC_A_ENABLE=1,PDM1_MIC_B_ENABLE=1,DMIC0_ID=3,DMIC1_ID=4,PREPROCESS_PLUGINS=nhlt,\ +NHLT_BIN=nhlt-sof-wcl-dmic-4ch-id3.bin,DMIC0_ENHANCED_CAPTURE=true,\ +EFX_DMIC0_TDFB_PARAMS=line4_pass,EFX_DMIC0_DRC_PARAMS=dmic_default" + +"cavs-sdw\;sof-wcl-dmic-4ch-id4\;PLATFORM=wcl,SDW_JACK=false,NUM_HDMIS=0,NUM_DMICS=4,\ +PDM1_MIC_A_ENABLE=1,PDM1_MIC_B_ENABLE=1,DMIC0_ID=4,DMIC1_ID=5,PREPROCESS_PLUGINS=nhlt,\ +NHLT_BIN=nhlt-sof-wcl-dmic-4ch-id4.bin,DMIC0_ENHANCED_CAPTURE=true,\ +EFX_DMIC0_TDFB_PARAMS=line4_pass,EFX_DMIC0_DRC_PARAMS=dmic_default" + +"cavs-sdw\;sof-wcl-dmic-4ch-id5\;PLATFORM=wcl,SDW_JACK=false,NUM_HDMIS=0,NUM_DMICS=4,\ +PDM1_MIC_A_ENABLE=1,PDM1_MIC_B_ENABLE=1,DMIC0_ID=5,DMIC1_ID=6,PREPROCESS_PLUGINS=nhlt,\ +NHLT_BIN=nhlt-sof-wcl-dmic-4ch-id5.bin,DMIC0_ENHANCED_CAPTURE=true,\ +EFX_DMIC0_TDFB_PARAMS=line4_pass,EFX_DMIC0_DRC_PARAMS=dmic_default" + +"cavs-es83x6\;sof-ptl-ssp1-jack-id0\;PLATFORM=ptl,PREPROCESS_PLUGINS=nhlt,\ +NHLT_BIN=nhlt-sof-ptl-ssp1-jack-id0.bin,HEADSET_SSP_DAI_INDEX=1,\ +HEADSET_CODEC=true,HEADSET_CODEC_NAME=SSP1-Codec,NUM_HDMIS=0,\ +HDMI_IN_CAPTURE=false" + +# HDMI IN port: SSP0 and SSP2, SSP number is a mask in HDMI_IN_1_ID and HDMI_IN_2_ID +# For example, SSP5 indicates SSP 0 and 2 are used for HDMI IN +"cavs-es83x6\;sof-ptl-ssp5-hdmiin-id1\;PLATFORM=ptl,PREPROCESS_PLUGINS=nhlt,NUM_HDMIS=0,\ +NHLT_BIN=nhlt-sof-ptl-ssp5-hdmiin-id1.bin,HEADSET_CODEC=false,HDMI_IN_CAPTURE=true,\ +HDMI_IN_1_ID=0,HDMI_IN_2_ID=1" + +"cavs-es83x6\;sof-ptl-ssp5-hdmiin-id2\;PLATFORM=ptl,PREPROCESS_PLUGINS=nhlt,NUM_HDMIS=0,\ +NHLT_BIN=nhlt-sof-ptl-ssp5-hdmiin-id2.bin,HEADSET_CODEC=false,HDMI_IN_CAPTURE=true,\ +HDMI_IN_1_ID=1,HDMI_IN_2_ID=2" + +"cavs-es83x6\;sof-ptl-ssp5-hdmiin-id7\;PLATFORM=ptl,PREPROCESS_PLUGINS=nhlt,NUM_HDMIS=0,\ +NHLT_BIN=nhlt-sof-ptl-ssp5-hdmiin-id7.bin,HEADSET_CODEC=false,HDMI_IN_CAPTURE=true,\ +HDMI_IN_1_ID=6,HDMI_IN_2_ID=7" + +"cavs-es83x6\;sof-ptl-ssp2-bt-id2\;PLATFORM=ptl,INCLUDE_BT_OFFLOAD=true,NUM_HDMIS=0,\ +HEADSET_CODEC=false,HDMI_IN_CAPTURE=false,BT_ID=2,PREPROCESS_PLUGINS=nhlt,\ +NHLT_BIN=nhlt-sof-ptl-ssp2-bt-id2.bin" + +#BT_ID=8, 10 for sdw_sof machine +"cavs-sdw\;sof-ptl-ssp2-bt-id8\;PLATFORM=ptl,ADD_BT=true,SDW_JACK=false,NUM_HDMIS=0,\ +HEADSET_CODEC=false,HDMI_IN_CAPTURE=false,BT_ID=8,BT_PCM_ID=20,BT_PCM_NAME=Bluetooth,\ +BT_CP_HOST_PIPELINE_ID=201,PREPROCESS_PLUGINS=nhlt,NHLT_BIN=nhlt-sof-ptl-ssp2-bt-id8.bin" + +"cavs-sdw\;sof-ptl-ssp2-bt-id10\;PLATFORM=ptl,ADD_BT=true,SDW_JACK=false,NUM_HDMIS=0,\ +HEADSET_CODEC=false,HDMI_IN_CAPTURE=false,BT_ID=10,BT_PCM_ID=20,BT_PCM_NAME=Bluetooth,\ +BT_CP_HOST_PIPELINE_ID=201,PREPROCESS_PLUGINS=nhlt,NHLT_BIN=nhlt-sof-ptl-ssp2-bt-id10.bin" ) diff --git a/tools/topology/topology2/production/tplg-targets-ace4.cmake b/tools/topology/topology2/production/tplg-targets-ace4.cmake new file mode 100644 index 000000000000..66edfda9a1a6 --- /dev/null +++ b/tools/topology/topology2/production/tplg-targets-ace4.cmake @@ -0,0 +1,112 @@ +# SPDX-License-Identifier: BSD-3-Clause + +# Array of "input-file-name;output-file-name;comma separated pre-processor variables" +list(APPEND TPLGS +# MOCKUP topology for PTL +"cavs-sdw\;sof-nvl-rt711-rt1308-rt715\;NUM_SDW_AMP_LINKS=2,SDW_DMIC=1,SDW_AMP_FEEDBACK=false" + +"cavs-sdw\;sof-nvl-rt711-rt1308-mono-rt715\;NUM_SDW_AMP_LINKS=1,SDW_DMIC=1,SDW_AMP_FEEDBACK=false" + +"cavs-sdw\;sof-nvl-rt715-rt711-rt1308-mono\;NUM_SDW_AMP_LINKS=1,SDW_DMIC=1,\ +SDW_JACK_OUT_STREAM=SDW1-Playback,SDW_JACK_IN_STREAM=SDW1-Capture,\ +SDW_SPK_STREAM=SDW2-Playback,SDW_DMIC_STREAM=SDW0-Capture,SDW_AMP_FEEDBACK=false,\ +SDW_SPK_ENHANCED_PLAYBACK=false,SDW_DMIC_ENHANCED_CAPTURE=false" + +# RT721 eval board with PCH-DMIC, sof_sdw_quirk_table with SOC_SDW_PCH_DMIC +"cavs-sdw\;sof-nvl-rt721-4ch\;PLATFORM=nvl,SDW_DMIC=1,NUM_SDW_AMP_LINKS=1,NUM_DMICS=4,\ +PDM1_MIC_A_ENABLE=1,PDM1_MIC_B_ENABLE=1,DMIC0_ID=5,DMIC1_ID=6,HDMI1_ID=7,HDMI2_ID=8,HDMI3_ID=9,\ +SDW_AMP_FEEDBACK=false,SDW_SPK_STREAM=Playback-SmartAmp,SDW_DMIC_STREAM=Capture-SmartMic,\ +SDW_JACK_OUT_STREAM=Playback-SimpleJack,SDW_JACK_IN_STREAM=Capture-SimpleJack,\ +PREPROCESS_PLUGINS=nhlt,NHLT_BIN=nhlt-sof-nvl-rt721-4ch.bin,DMIC0_ENHANCED_CAPTURE=true,\ +EFX_DMIC0_TDFB_PARAMS=line4_pass,EFX_DMIC0_DRC_PARAMS=dmic_default,\ +DEEPBUFFER_FW_DMA_MS=10,DEEP_BUF_SPK=true" + +# RT721 eval board with PCH-DMIC, sof_sdw_quirk_table with SOC_SDW_PCH_DMIC, 96KHz Jack-rate +"cavs-sdw\;sof-nvl-rt721-4ch-96k\;PLATFORM=nvl,SDW_DMIC=1,NUM_SDW_AMP_LINKS=1,NUM_DMICS=4,\ +PDM1_MIC_A_ENABLE=1,PDM1_MIC_B_ENABLE=1,DMIC0_ID=5,DMIC1_ID=6,HDMI1_ID=7,HDMI2_ID=8,HDMI3_ID=9,\ +SDW_AMP_FEEDBACK=false,SDW_SPK_STREAM=Playback-SmartAmp,SDW_DMIC_STREAM=Capture-SmartMic,\ +SDW_JACK_OUT_STREAM=Playback-SimpleJack,SDW_JACK_IN_STREAM=Capture-SimpleJack,\ +PREPROCESS_PLUGINS=nhlt,NHLT_BIN=nhlt-sof-nvl-rt721-4ch.bin,DMIC0_ENHANCED_CAPTURE=true,\ +EFX_DMIC0_TDFB_PARAMS=line4_pass,EFX_DMIC0_DRC_PARAMS=dmic_default,\ +DEEPBUFFER_FW_DMA_MS=10,DEEP_BUF_SPK=true,JACK_RATE=96000,DEEP_BUF_JACK_RATE=96000" + +"cavs-sdw\;sof-nvl-rt722\;PLATFORM=nvl,SDW_DMIC=1,NUM_SDW_AMP_LINKS=1,\ +SDW_AMP_FEEDBACK=false,SDW_SPK_STREAM=Playback-SmartAmp,SDW_DMIC_STREAM=Capture-SmartMic,\ +SDW_JACK_OUT_STREAM=Playback-SimpleJack,SDW_JACK_IN_STREAM=Capture-SimpleJack" + +"cavs-sdw\;sof-nvl-rt722-96k\;PLATFORM=nvl,SDW_DMIC=1,NUM_SDW_AMP_LINKS=1,\ +SDW_AMP_FEEDBACK=false,SDW_SPK_STREAM=Playback-SmartAmp,SDW_DMIC_STREAM=Capture-SmartMic,\ +SDW_JACK_OUT_STREAM=Playback-SimpleJack,SDW_JACK_IN_STREAM=Capture-SimpleJack,\ +JACK_RATE=96000,DEEP_BUF_JACK_RATE=96000,\ +SDW_SPK_ENHANCED_PLAYBACK=false,SDW_DMIC_ENHANCED_CAPTURE=false" + +"cavs-sdw\;sof-nvl-rt722-192k\;PLATFORM=nvl,SDW_DMIC=1,NUM_SDW_AMP_LINKS=1,\ +SDW_AMP_FEEDBACK=false,SDW_SPK_STREAM=Playback-SmartAmp,SDW_DMIC_STREAM=Capture-SmartMic,\ +SDW_JACK_OUT_STREAM=Playback-SimpleJack,SDW_JACK_IN_STREAM=Capture-SimpleJack,\ +JACK_RATE=192000,DEEP_BUF_JACK_RATE=192000,\ +SDW_SPK_ENHANCED_PLAYBACK=false,SDW_DMIC_ENHANCED_CAPTURE=false" + +"cavs-sdw\;sof-nvl-rt722-4ch\;PLATFORM=nvl,SDW_DMIC=1,NUM_SDW_AMP_LINKS=1,NUM_DMICS=4,\ +PDM1_MIC_A_ENABLE=1,PDM1_MIC_B_ENABLE=1,DMIC0_ID=5,DMIC1_ID=6,HDMI1_ID=7,HDMI2_ID=8,HDMI3_ID=9,\ +SDW_AMP_FEEDBACK=false,SDW_SPK_STREAM=Playback-SmartAmp,SDW_DMIC_STREAM=Capture-SmartMic,\ +SDW_JACK_OUT_STREAM=Playback-SimpleJack,SDW_JACK_IN_STREAM=Capture-SimpleJack,\ +PREPROCESS_PLUGINS=nhlt,NHLT_BIN=nhlt-sof-nvl-rt722-4ch.bin,DMIC0_ENHANCED_CAPTURE=true,\ +EFX_DMIC0_TDFB_PARAMS=line4_pass,EFX_DMIC0_DRC_PARAMS=dmic_default" + +"cavs-sdw\;sof-nvl-rt722-2ch\;PLATFORM=nvl,SDW_DMIC=1,NUM_SDW_AMP_LINKS=1,NUM_DMICS=2,\ +PDM1_MIC_A_ENABLE=0,PDM1_MIC_B_ENABLE=0,DMIC0_ID=5,DMIC1_ID=6,HDMI1_ID=7,HDMI2_ID=8,HDMI3_ID=9,\ +SDW_AMP_FEEDBACK=false,SDW_SPK_STREAM=Playback-SmartAmp,SDW_DMIC_STREAM=Capture-SmartMic,\ +SDW_JACK_OUT_STREAM=Playback-SimpleJack,SDW_JACK_IN_STREAM=Capture-SimpleJack,\ +PREPROCESS_PLUGINS=nhlt,NHLT_BIN=nhlt-sof-nvl-rt722-2ch.bin,DMIC0_ENHANCED_CAPTURE=true,\ +EFX_DMIC0_TDFB_PARAMS=line2_generic_pm10deg,EFX_DMIC0_DRC_PARAMS=dmic_default" + +"cavs-sdw\;sof-nvl-rt712-l3-rt1320-l3-4ch-ssp2-bt\;PLATFORM=nvl,NUM_SDW_AMP_LINKS=1,NUM_DMICS=4,\ +PDM1_MIC_A_ENABLE=1,PDM1_MIC_B_ENABLE=1,DMIC0_ID=5,DMIC1_ID=6,HDMI1_ID=7,HDMI2_ID=8,HDMI3_ID=9,\ +SDW_AMP_FEEDBACK=false,SDW_SPK_STREAM=Playback-SmartAmp,SDW_DMIC_STREAM=Capture-SmartMic,\ +SDW_JACK_OUT_STREAM=Playback-SimpleJack,SDW_JACK_IN_STREAM=Capture-SimpleJack,\ +PREPROCESS_PLUGINS=nhlt,NHLT_BIN=nhlt-sof-nvl-rt712-l3-rt1320-l3-4ch.bin,DMIC0_ENHANCED_CAPTURE=true,\ +EFX_DMIC0_TDFB_PARAMS=line4_pass,EFX_DMIC0_DRC_PARAMS=dmic_default,\ +BT_PCM_ID=20,BT_ID=10,BT_PCM_NAME=Bluetooth,ADD_BT=true,\ +DEEPBUFFER_FW_DMA_MS=10,DEEP_BUF_SPK=true" + +# Split topologies +"cavs-sdw\;sof-nvl-dmic-2ch-id2\;PLATFORM=nvl,SDW_JACK=false,NUM_HDMIS=0,NUM_DMICS=2,\ +PDM1_MIC_A_ENABLE=0,PDM1_MIC_B_ENABLE=0,DMIC0_ID=2,DMIC1_ID=3,PREPROCESS_PLUGINS=nhlt,\ +NHLT_BIN=nhlt-sof-nvl-dmic-2ch-id2.bin,DMIC0_ENHANCED_CAPTURE=true,\ +EFX_DMIC0_TDFB_PARAMS=line2_generic_pm10deg,EFX_DMIC0_DRC_PARAMS=dmic_default" + +"cavs-sdw\;sof-nvl-dmic-2ch-id3\;PLATFORM=nvl,SDW_JACK=false,NUM_HDMIS=0,NUM_DMICS=2,\ +PDM1_MIC_A_ENABLE=0,PDM1_MIC_B_ENABLE=0,DMIC0_ID=3,DMIC1_ID=4,PREPROCESS_PLUGINS=nhlt,\ +NHLT_BIN=nhlt-sof-nvl-dmic-2ch-id3.bin,DMIC0_ENHANCED_CAPTURE=true,\ +EFX_DMIC0_TDFB_PARAMS=line2_generic_pm10deg,EFX_DMIC0_DRC_PARAMS=dmic_default" + +"cavs-sdw\;sof-nvl-dmic-2ch-id4\;PLATFORM=nvl,SDW_JACK=false,NUM_HDMIS=0,NUM_DMICS=2,\ +PDM1_MIC_A_ENABLE=0,PDM1_MIC_B_ENABLE=0,DMIC0_ID=4,DMIC1_ID=5,PREPROCESS_PLUGINS=nhlt,\ +NHLT_BIN=nhlt-sof-nvl-dmic-2ch-id4.bin,DMIC0_ENHANCED_CAPTURE=true,\ +EFX_DMIC0_TDFB_PARAMS=line2_generic_pm10deg,EFX_DMIC0_DRC_PARAMS=dmic_default" + +"cavs-sdw\;sof-nvl-dmic-2ch-id5\;PLATFORM=nvl,SDW_JACK=false,NUM_HDMIS=0,NUM_DMICS=2,\ +PDM1_MIC_A_ENABLE=0,PDM1_MIC_B_ENABLE=0,DMIC0_ID=5,DMIC1_ID=6,PREPROCESS_PLUGINS=nhlt,\ +NHLT_BIN=nhlt-sof-nvl-dmic-2ch-id5.bin,DMIC0_ENHANCED_CAPTURE=true,\ +EFX_DMIC0_TDFB_PARAMS=line2_generic_pm10deg,EFX_DMIC0_DRC_PARAMS=dmic_default" + +"cavs-sdw\;sof-nvl-dmic-4ch-id2\;PLATFORM=nvl,SDW_JACK=false,NUM_HDMIS=0,NUM_DMICS=4,\ +PDM1_MIC_A_ENABLE=1,PDM1_MIC_B_ENABLE=1,DMIC0_ID=2,DMIC1_ID=3,PREPROCESS_PLUGINS=nhlt,\ +NHLT_BIN=nhlt-sof-nvl-dmic-4ch-id2.bin,DMIC0_ENHANCED_CAPTURE=true,\ +EFX_DMIC0_TDFB_PARAMS=line4_pass,EFX_DMIC0_DRC_PARAMS=dmic_default" + +"cavs-sdw\;sof-nvl-dmic-4ch-id3\;PLATFORM=nvl,SDW_JACK=false,NUM_HDMIS=0,NUM_DMICS=4,\ +PDM1_MIC_A_ENABLE=1,PDM1_MIC_B_ENABLE=1,DMIC0_ID=3,DMIC1_ID=4,PREPROCESS_PLUGINS=nhlt,\ +NHLT_BIN=nhlt-sof-nvl-dmic-4ch-id3.bin,DMIC0_ENHANCED_CAPTURE=true,\ +EFX_DMIC0_TDFB_PARAMS=line4_pass,EFX_DMIC0_DRC_PARAMS=dmic_default" + +"cavs-sdw\;sof-nvl-dmic-4ch-id4\;PLATFORM=nvl,SDW_JACK=false,NUM_HDMIS=0,NUM_DMICS=4,\ +PDM1_MIC_A_ENABLE=1,PDM1_MIC_B_ENABLE=1,DMIC0_ID=4,DMIC1_ID=5,PREPROCESS_PLUGINS=nhlt,\ +NHLT_BIN=nhlt-sof-nvl-dmic-4ch-id4.bin,DMIC0_ENHANCED_CAPTURE=true,\ +EFX_DMIC0_TDFB_PARAMS=line4_pass,EFX_DMIC0_DRC_PARAMS=dmic_default" + +"cavs-sdw\;sof-nvl-dmic-4ch-id5\;PLATFORM=nvl,SDW_JACK=false,NUM_HDMIS=0,NUM_DMICS=4,\ +PDM1_MIC_A_ENABLE=1,PDM1_MIC_B_ENABLE=1,DMIC0_ID=5,DMIC1_ID=6,PREPROCESS_PLUGINS=nhlt,\ +NHLT_BIN=nhlt-sof-nvl-dmic-4ch-id5.bin,DMIC0_ENHANCED_CAPTURE=true,\ +EFX_DMIC0_TDFB_PARAMS=line4_pass,EFX_DMIC0_DRC_PARAMS=dmic_default" +) diff --git a/tools/topology/topology2/production/tplg-targets-hda-generic.cmake b/tools/topology/topology2/production/tplg-targets-hda-generic.cmake index c4a795e02053..72872d110a4d 100644 --- a/tools/topology/topology2/production/tplg-targets-hda-generic.cmake +++ b/tools/topology/topology2/production/tplg-targets-hda-generic.cmake @@ -13,6 +13,12 @@ list(APPEND TPLGS "sof-hda-generic\;sof-hda-generic\;HDA_CONFIG=mix,HDA_MIC_ENHANCED_CAPTURE=true,\ EFX_HDA_MIC_TDFB_PARAMS=line2_pass,EFX_HDA_MIC_DRC_PARAMS=passthrough" +"sof-hda-generic\;sof-hda-generic-compr\;HDA_CONFIG=mix,HDA_MIC_ENHANCED_CAPTURE=true,\ +EFX_HDA_MIC_TDFB_PARAMS=line2_pass,EFX_HDA_MIC_DRC_PARAMS=passthrough,COMPRESSED=true" + +"sof-hda-generic\;sof-hda-generic-dax\;HDA_CONFIG=dax,HDA_MIC_ENHANCED_CAPTURE=true,\ +EFX_HDA_MIC_TDFB_PARAMS=line2_pass,EFX_HDA_MIC_DRC_PARAMS=passthrough,DOLBY_DAX_CORE_ID=1" + # HDA topology with mixer-based pipelines for HDA and # passthrough pipelines for HDMI and # 2 or 4 DMIC, no NHLT blob included in topology @@ -26,6 +32,10 @@ EFX_DMIC0_DRC_PARAMS=dmic_default" PDM1_MIC_A_ENABLE=1,PDM1_MIC_B_ENABLE=1,DMIC0_ENHANCED_CAPTURE=true,\ EFX_DMIC0_TDFB_PARAMS=line4_pass,EFX_DMIC0_DRC_PARAMS=dmic_default" +"sof-hda-generic\;sof-hda-generic-2ch-dax\;HDA_CONFIG=dax,NUM_DMICS=2,\ +DMIC0_ENHANCED_CAPTURE=true,EFX_DMIC0_TDFB_PARAMS=line2_generic_pm10deg,\ +EFX_DMIC0_DRC_PARAMS=dmic_default,DOLBY_DAX_CORE_ID=1" + # HDA topology with mixer-based pipelines for HDA and # passthrough pipelines for HDMI and # 2 or 4 DMIC, no NHLT blob included in topology @@ -55,6 +65,11 @@ PREPROCESS_PLUGINS=nhlt,NHLT_BIN=nhlt-sof-hda-generic-cavs25-4ch.bin,\ DMIC0_ENHANCED_CAPTURE=true,\ EFX_DMIC0_TDFB_PARAMS=line4_pass,EFX_DMIC0_DRC_PARAMS=dmic_default" +"sof-hda-generic\;sof-hda-generic-cavs25-2ch-dax\;HDA_CONFIG=dax,NUM_DMICS=2,\ +PREPROCESS_PLUGINS=nhlt,NHLT_BIN=nhlt-sof-hda-generic-cavs25-2ch-dax.bin,\ +DMIC0_ENHANCED_CAPTURE=true,EFX_DMIC0_TDFB_PARAMS=line2_generic_pm10deg,\ +EFX_DMIC0_DRC_PARAMS=dmic_default,DOLBY_DAX_CORE_ID=1" + # Topologies for ACE1 and ACE2 architectures # HDMI + DMICs "sof-hda-generic\;sof-hda-generic-ace1-idisp-2ch\;PLATFORM=mtl,NUM_DMICS=2,\ @@ -76,6 +91,11 @@ PDM1_MIC_A_ENABLE=1,PDM1_MIC_B_ENABLE=1,\ PREPROCESS_PLUGINS=nhlt,NHLT_BIN=nhlt-sof-hda-generic-ace1-4ch.bin,DMIC0_ENHANCED_CAPTURE=true,\ EFX_DMIC0_TDFB_PARAMS=line4_pass,EFX_DMIC0_DRC_PARAMS=dmic_default" +"sof-hda-generic\;sof-hda-generic-ace1-2ch-dax\;PLATFORM=mtl,HDA_CONFIG=dax,NUM_DMICS=2,\ +PREPROCESS_PLUGINS=nhlt,NHLT_BIN=nhlt-sof-hda-generic-ace1-2ch-dax.bin,\ +DMIC0_ENHANCED_CAPTURE=true,EFX_DMIC0_TDFB_PARAMS=line2_generic_pm10deg,\ +EFX_DMIC0_DRC_PARAMS=dmic_default,DOLBY_DAX_CORE_ID=1" + # Topologies for ACE3 architecture # HDMI + DMICs "sof-hda-generic\;sof-hda-generic-ace3-idisp-2ch\;PLATFORM=ptl,NUM_DMICS=2,\ @@ -98,4 +118,9 @@ EFX_DMIC0_DRC_PARAMS=dmic_default" PDM1_MIC_A_ENABLE=1,PDM1_MIC_B_ENABLE=1,\ PREPROCESS_PLUGINS=nhlt,NHLT_BIN=nhlt-sof-hda-generic-ace3-4ch.bin,DMIC0_ENHANCED_CAPTURE=true,\ EFX_DMIC0_TDFB_PARAMS=line4_pass,EFX_DMIC0_DRC_PARAMS=dmic_default" + +"sof-hda-generic\;sof-hda-generic-ace3-2ch-dax\;PLATFORM=ptl,HDA_CONFIG=dax,NUM_DMICS=2,\ +PREPROCESS_PLUGINS=nhlt,NHLT_BIN=nhlt-sof-hda-generic-ace3-2ch-dax.bin,\ +DMIC0_ENHANCED_CAPTURE=true,EFX_DMIC0_TDFB_PARAMS=line2_generic_pm10deg,\ +EFX_DMIC0_DRC_PARAMS=dmic_default,DOLBY_DAX_CORE_ID=1" ) diff --git a/tools/topology/topology2/production/tplg-targets-sdca-generic.cmake b/tools/topology/topology2/production/tplg-targets-sdca-generic.cmake index 414212cb9d23..c7d5b5d8af1b 100644 --- a/tools/topology/topology2/production/tplg-targets-sdca-generic.cmake +++ b/tools/topology/topology2/production/tplg-targets-sdca-generic.cmake @@ -12,10 +12,20 @@ SDW_JACK_OUT_STREAM=Playback-SimpleJack,SDW_JACK_IN_STREAM=Capture-SimpleJack" SDW_JACK_IN_STREAM=Capture-SimpleJack,NUM_HDMIS=0" "cavs-sdw\;sof-sdca-1amp-id2\;NUM_SDW_AMP_LINKS=1,SDW_JACK=false,\ -SDW_AMP_FEEDBACK=false,SDW_SPK_STREAM=Playback-SmartAmp,NUM_HDMIS=0" +SDW_AMP_FEEDBACK=false,SDW_SPK_STREAM=Playback-SmartAmp,NUM_HDMIS=0,\ +DEEP_BUF_SPK=true" "cavs-sdw\;sof-sdca-2amp-id2\;NUM_SDW_AMP_LINKS=2,SDW_JACK=false,\ -SDW_AMP_FEEDBACK=false,SDW_SPK_STREAM=Playback-SmartAmp,NUM_HDMIS=0" +SDW_AMP_FEEDBACK=false,SDW_SPK_STREAM=Playback-SmartAmp,NUM_HDMIS=0,\ +DEEP_BUF_SPK=true" + +"cavs-sdw\;sof-sdca-3amp-id2\;NUM_SDW_AMP_LINKS=3,SDW_JACK=false,\ +SDW_AMP_FEEDBACK=false,SDW_SPK_STREAM=Playback-SmartAmp,NUM_HDMIS=0,\ +DEEP_BUF_SPK=true" + +"cavs-sdw\;sof-sdca-4amp-id2\;NUM_SDW_AMP_LINKS=4,SDW_JACK=false,\ +SDW_AMP_FEEDBACK=false,SDW_SPK_STREAM=Playback-SmartAmp,NUM_HDMIS=0,\ +DEEP_BUF_SPK=true" "cavs-sdw\;sof-sdca-mic-id4\;SDW_JACK=false,SDW_DMIC=1,NUM_HDMIS=0,\ SDW_DMIC_STREAM=Capture-SmartMic" @@ -27,4 +37,28 @@ SDW_DMIC_STREAM=Capture-SmartMic" "cavs-sdw\;sof-hdmi-pcm5-id6\;SDW_JACK=false,HDMI1_ID=6,HDMI2_ID=7,HDMI3_ID=8" "cavs-sdw\;sof-hdmi-pcm5-id7\;SDW_JACK=false,HDMI1_ID=7,HDMI2_ID=8,HDMI3_ID=9" +# Feature topologies which should work with the function topologies above +"cavs-sdw\;sof-sdca-amp-ref\;SDW_JACK=false,NUM_HDMIS=0,JACK_RATE=48000,\ +SDW_AMP_FEEDBACK=false,SDW_SPK_ECHO_REF=true,SDW_SPK_ECHO_REF_PCM_ID=12" + +"cavs-sdw\;sof-sdca-amp-ref-dai\;SDW_JACK=false,NUM_HDMIS=0,JACK_RATE=48000,\ +SDW_AMP_FEEDBACK=false,SDW_ECHO_REF_DAI=true,SDW_SPK_ECHO_REF=true,SDW_SPK_ECHO_REF_PCM_ID=12" + +"cavs-sdw\;sof-sdca-jack-ref-dai\;SDW_JACK=false,NUM_HDMIS=0,JACK_RATE=48000,\ +SDW_ECHO_REF_DAI=true,SDW_JACK_ECHO_REF=true,SDW_JACK_ECHO_REF_PCM_ID=11" + +# Topology for speaker with 2-way crossover filter in SOF +# with channels order L-low, R-low, L-high, R-high +"cavs-sdw\;sof-sdca-2amp-id2-xover\;NUM_SDW_AMP_LINKS=2,SDW_JACK=false,\ +SDW_AMP_FEEDBACK=false,SDW_SPK_STREAM=Playback-SmartAmp,NUM_HDMIS=0,\ +SDW_AMP_NUM_CHANNELS=4,SDW_AMP_XOVER=true,\ +SDW_AMP_XOVER_SELECTOR_PARAMS=xover_selector_lr_to_lrlr,\ +SDW_AMP_XOVER_EQIIR_PARAMS=xover_lr4_2000hz_llhh_48khz" + +"cavs-sdw\;sof-sdca-2amp-feedback-id3-xover\;NUM_SDW_AMP_LINKS=2,\ +SDW_JACK=false,SDW_AMP_FEEDBACK=true,SDW_SPK_STREAM=Playback-SmartAmp,\ +SDW_SPK_IN_STREAM=Capture-SmartAmp,NUM_HDMIS=0,\ +SDW_AMP_NUM_CHANNELS=4,SDW_AMP_XOVER=true,\ +SDW_AMP_XOVER_SELECTOR_PARAMS=xover_selector_lr_to_lrlr,\ +SDW_AMP_XOVER_EQIIR_PARAMS=xover_lr4_2000hz_llhh_48khz" ) diff --git a/tools/topology/topology2/sof-hda-generic.conf b/tools/topology/topology2/sof-hda-generic.conf index e73ba0634b77..01a020bad17f 100644 --- a/tools/topology/topology2/sof-hda-generic.conf +++ b/tools/topology/topology2/sof-hda-generic.conf @@ -18,12 +18,15 @@ <mixout-gain-dai-copier-playback.conf> <mixout-aria-gain-mixin-playback.conf> <mixout-gain-efx-dai-copier-playback.conf> +<mixout-gain-dax-dai-copier-playback.conf> <mixout-gain-efx-mbdrc-dai-copier-playback.conf> <mixout-gain-host-copier-capture.conf> <dai-copier-eqiir-module-copier-capture.conf> <dai-copier-gain-eqiir-module-copier-capture.conf> <gain-capture.conf> <deepbuffer-playback.conf> +<deepbuffer-capture.conf> +<compr-playback.conf> <io-gateway.conf> <io-gateway-capture.conf> <highpass-capture-be.conf> @@ -41,6 +44,7 @@ <dmic-default.conf> <hdmi-default.conf> <deep-buffer-default.conf> +<compr-default.conf> <module-copier.conf> Define { @@ -67,6 +71,7 @@ IncludeByKey.HDA_CONFIG { "mix" "cavs-mixin-mixout-hda.conf" "efx" "cavs-mixin-mixout-efx-hda.conf" "src" "cavs-src-mixin-mixout-hda.conf" + "dax" "cavs-mixin-mixout-dax-hda.conf" "passthrough" "cavs-passthrough-hda.conf" } diff --git a/tools/tune/mfcc/README.txt b/tools/tune/mfcc/README.txt deleted file mode 100644 index fb8208992ed4..000000000000 --- a/tools/tune/mfcc/README.txt +++ /dev/null @@ -1,29 +0,0 @@ -This directory contains a tool to create configuration blob for SOF -MFCC component. It's simply run in Matlab or Octave with command -"setup_mfcc". The MFCC configuration parameters can be edited from the -script. - -The configuration can be test run with testbench. First the test topologies -need to be created with "scripts/build-tools.sh -t". Next the testbench -is build with "scripts/rebuild-testbench.sh". - -Once the previous steps are done, a sample wav file can be processed -into stream of cepstral coefficients with script run_mfcc.sh. E.g. -next command processes an ALSA test file with speech clip "front center". -The output file is hard-coded to mfcc.raw. - -./run_mfcc.sh /usr/share/sounds/alsa/Front_Center.wav - -The output can be plotted and retrieved with Matlab or Octave command: - -[ceps, t, n] = decode_ceps('mfcc.raw', 13); - -In the above it's known from configuration script that MFCC was set up to -output 13 cepstral coefficients from each FFT -> Mel -> DCT -> Cepstral -coefficients computation run. - -Other kind of signals have quite big visual difference in audio features. Try -e.g. other sound files found in computer. - -./run_mfcc.sh /usr/share/sounds/gnome/default/alerts/bark.ogg -./run_mfcc.sh /usr/share/sounds/gnome/default/alerts/sonar.ogg diff --git a/tools/tune/mfcc/run_mfcc.sh b/tools/tune/mfcc/run_mfcc.sh deleted file mode 100755 index 8a48c97b9def..000000000000 --- a/tools/tune/mfcc/run_mfcc.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/sh -# SPDX-License-Identifier: BSD-3-Clause -# Copyright(c) 2022 Intel Corporation. All rights reserved. - -set -e - -RAW_INPUT=in.raw -RAW_OUTPUT=mfcc.raw - -export LD_LIBRARY_PATH=../../testbench/build_testbench/sof_ep/install/lib:../../testbench/build_testbench/sof_parser/install/lib - -TESTBENCH=../../testbench/build_testbench/install/bin/testbench -OPT="-q -r 16000 -R 16000 -c 1 -n 1 -b S16_LE -t ../../build_tools/test/topology/test-playback-ssp5-mclk-0-I2S-mfcc-s16le-s16le-48k-24576k-codec.tplg" - -# Convert input audio file raw 16 kHz 1 channel 16 bit -sox --encoding signed-integer "$1" -L -r 16000 -c 1 -b 16 "$RAW_INPUT" - -# Run testbench -$TESTBENCH $OPT -i "$RAW_INPUT" -o "$RAW_OUTPUT" - -echo ----------------------------------------------- -echo The MFCC data was output to file $RAW_OUTPUT -echo ----------------------------------------------- diff --git a/uuid-registry.txt b/uuid-registry.txt index e581be7b607a..dc5cf5e15297 100644 --- a/uuid-registry.txt +++ b/uuid-registry.txt @@ -64,6 +64,7 @@ bc3526a7-9b86-4ab4-84a52e02ae70cc10 dma 729bf8b5-e873-4bf5-96908e2a3fd33911 dma_copy 58782c63-1326-4185-845922272e12d1f1 dma_trace 2b972272-c5b1-4b7e-926f0fc5cb4c4690 dma_trace_task +40f66c8b-5aa5-4345-891953ec431aaa98 dolby_dax_audio_processing 87858bc2-baa9-40b6-8e4c2c95ba8b1545 dp_sched ee755917-96b9-4130-b49e37b9d0501993 dp_task b36ee4da-006f-47f9-a06dfecbe2d8b6ce drc @@ -111,6 +112,8 @@ e50057a5-8b27-4db4-bd799a639cee5f50 kpb_task 4f9c3ec7-7b55-400c-86b3502b4420e625 ll_sched 9f130ed8-2bbf-421c-836ad5269147c9e7 ll_sched_lib 37f1d41f-252d-448d-b9c41e2bee8e1bf1 main_task +107eb437-0347-4336-ab60f4baaeb5ea49 math_fft +590af515-8fc0-4827-8748c8efbe6ac514 math_fft_multi d23cf8d0-8dfe-497c-82025f909cf72735 math_power 0cd84e80-ebd3-11ea-adc10242ac120002 maxim_dsm 425d6e68-145c-4455-b0b2c7260b0600a5 mem @@ -160,6 +163,7 @@ a417b6fb-459d-4cf9-be65d38dc9057b80 spi_completion c1c5326d-8390-46b4-aa4795c3beca6550 src e61bb28d-149a-4c1f-b70946823ef5f5ae src4 33441051-44cd-466a-83a3178478708aea src_lite +0d116ea6-9150-46de-98b8b2b3a791da29 stft_process eb0bd14b-7d5e-4dfa-bbe27762adb279f0 swaudiodai dd511749-d9fa-455c-b3a713585693f1af tdfb a62de1af-5964-4e2e-b1677fdc97279a29 template @@ -169,6 +173,7 @@ c51dc642-a2e1-48df-a490e2748cb6363e tflmcly 04e3f894-2c5c-4f2e-8dc1694eeaab53fa tone e93326d8-0d14-4bf0-bcb9e063d3d80136 twb_sched 42f8060c-832f-4dbf-b24751e961997b34 up_down_mixer +6f6b6f4b-6f73-7466-20e1e62b9779f003 userspace_proxy b77e677e-5ff4-4188-af14fba8bdbf8682 volume 8a171323-94a3-4e1d-afe9fe5dbaa4c393 volume4 1028070e-04e8-46ab-8d8110a0116ce738 wait diff --git a/versions.json b/versions.json index 47679041789a..89bebab40c35 100644 --- a/versions.json +++ b/versions.json @@ -1,7 +1,7 @@ { "SOF": { "MAJOR": "2", - "MINOR": "13", + "MINOR": "14", "MICRO": "99" } } diff --git a/west.yml b/west.yml index 86ab1a9aad21..faf374b914fb 100644 --- a/west.yml +++ b/west.yml @@ -43,7 +43,7 @@ manifest: - name: zephyr repo-path: zephyr - revision: 5bef4a9d6264761c10d739b345eb2f7ded719099 + revision: c162980efd9ad8616d0e2fe886ca917d8d8d240a remote: zephyrproject # Import some projects listed in zephyr/west.yml@revision diff --git a/zephyr/CMakeLists.txt b/zephyr/CMakeLists.txt index 3e0207b17a0d..91598a776db0 100644 --- a/zephyr/CMakeLists.txt +++ b/zephyr/CMakeLists.txt @@ -2,7 +2,7 @@ # compile every Zephyr module that can be found. See # sof/zephyr/module.yml and # https://docs.zephyrproject.org/latest/develop/modules.html -if(CONFIG_SOF) +if(CONFIG_SOF_FULL_ZEPHYR_APPLICATION) if(CONFIG_ZEPHYR_POSIX) set(ARCH host) @@ -184,6 +184,8 @@ set_property(TARGET modules_sof PROPERTY CXX_STANDARD 17) zephyr_include_directories(include) zephyr_include_directories(${ZEPHYR_BASE}/kernel/include) zephyr_include_directories(${ZEPHYR_BASE}/arch/${ARCH}/include) +zephyr_include_directories(${SOF_SRC_PATH}/include/sof/audio/module_adapter/iadk/) +zephyr_include_directories(${SOF_SRC_PATH}/include/sof/audio/module_adapter/library/) # SOC level sources # Files that are commented may not be needed. @@ -271,24 +273,28 @@ if (CONFIG_SOC_SERIES_INTEL_ADSP_ACE) lib/dma.c ) - zephyr_library_sources_ifdef(CONFIG_SOC_INTEL_ACE15_MTPM + zephyr_library_sources_ifdef(CONFIG_SOC_ACE15_MTPM ${SOF_PLATFORM_PATH}/meteorlake/lib/clk.c ) - zephyr_library_sources_ifdef(CONFIG_SOC_INTEL_ACE20_LNL + zephyr_library_sources_ifdef(CONFIG_SOC_ACE20_LNL ${SOF_PLATFORM_PATH}/lunarlake/lib/clk.c ) - zephyr_library_sources_ifdef(CONFIG_SOC_INTEL_ACE30 + zephyr_library_sources_ifdef(CONFIG_SOC_ACE30 ${SOF_PLATFORM_PATH}/ace30/lib/clk.c ) - zephyr_library_sources_ifdef(CONFIG_SOC_INTEL_ACE40 + zephyr_library_sources_ifdef(CONFIG_SOC_ACE40 ${SOF_PLATFORM_PATH}/novalake/lib/clk.c ) - # Sources for virtual heap management - zephyr_library_sources( + zephyr_library_sources_ifdef(CONFIG_SOF_VREGIONS + lib/vpage.c + lib/vregion.c + ) + + zephyr_library_sources_ifdef(CONFIG_VIRTUAL_HEAP lib/regions_mm.c ) @@ -300,13 +306,13 @@ if (CONFIG_SOC_SERIES_INTEL_ADSP_ACE) ${SOF_PLATFORM_PATH}/intel/ace/lib/watchdog.c ) - if (CONFIG_SOC_INTEL_ACE15_MTPM) + if (CONFIG_SOC_ACE15_MTPM) set(PLATFORM "meteorlake") - elseif(CONFIG_SOC_INTEL_ACE20_LNL) + elseif(CONFIG_SOC_ACE20_LNL) set(PLATFORM "lunarlake") - elseif(CONFIG_SOC_INTEL_ACE30) + elseif(CONFIG_SOC_ACE30) set(PLATFORM "ace30") - elseif(CONFIG_SOC_INTEL_ACE40) + elseif(CONFIG_SOC_ACE40) set(PLATFORM "novalake") endif() @@ -353,6 +359,34 @@ if (CONFIG_SOC_MIMX8ML8_ADSP) set(PLATFORM "imx8m") endif() +if (CONFIG_SOC_MIMX8ML8_M7) + zephyr_library_sources( + ${SOF_PLATFORM_PATH}/imx8m_cm7/platform.c + ${SOF_PLATFORM_PATH}/imx8m_cm7/lib/clk.c + lib/dma.c + ) + + zephyr_library_sources( + ${SOF_DRIVERS_PATH}/imx/ipc.c + ) + + # SOF-specific linker script additions + zephyr_linker_sources(DATA_SECTIONS ${sof_top_dir}/src/platform/imx8m_cm7/linker/data-sections.ld) + + set(PLATFORM "imx8m_cm7") + + add_custom_target(zephyr.ri ALL + DEPENDS ${CMAKE_BINARY_DIR}/zephyr/zephyr.ri) + + add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/zephyr/zephyr.ri + COMMAND west sign --if-tool-available --tool rimage + --build-dir ${CMAKE_BINARY_DIR} ${WEST_SIGN_OPTS} + DEPENDS ${CMAKE_BINARY_DIR}/zephyr/${KERNEL_ELF_NAME}) + + board_set_rimage_target(imx8m_cm7) + +endif() + if (CONFIG_SOC_MIMX8UD7_ADSP) zephyr_library_sources( ${SOF_DRIVERS_PATH}/imx/ipc.c @@ -404,6 +438,22 @@ if (CONFIG_SOC_MIMX9596_M7) zephyr_linker_sources(DATA_SECTIONS ${sof_top_dir}/src/platform/imx95/linker/data-sections.ld) set(PLATFORM "imx95") + + # no longer done in Zephyr and should be handled by SOF + # See [1], [2], and [3] for more context + # + # [1]: https://github.com/zephyrproject-rtos/zephyr/issues/91061 + # [2]: https://github.com/zephyrproject-rtos/zephyr/pull/97946 + # [3]: https://github.com/zephyrproject-rtos/zephyr/pull/97988 + add_custom_target(zephyr.ri ALL + DEPENDS ${CMAKE_BINARY_DIR}/zephyr/zephyr.ri) + + add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/zephyr/zephyr.ri + COMMAND west sign --if-tool-available --tool rimage + --build-dir ${CMAKE_BINARY_DIR} ${WEST_SIGN_OPTS} + DEPENDS ${CMAKE_BINARY_DIR}/zephyr/${KERNEL_ELF_NAME}) + + board_set_rimage_target(imx95) endif() # AMD RMB platforms @@ -432,7 +482,6 @@ if (CONFIG_SOC_ACP_6_0) ${SOF_PLATFORM_PATH}/amd/rembrandt/lib/clk.c ${SOF_PLATFORM_PATH}/amd/rembrandt/lib/dai.c ${SOF_PLATFORM_PATH}/amd/rembrandt/lib/dma.c - ${SOF_PLATFORM_PATH}/amd/rembrandt/lib/memory.c ) # SOF core infrastructure - runs on top of Zephyr @@ -447,6 +496,22 @@ if (CONFIG_SOC_ACP_6_0) set(PLATFORM "acp_6_0") endif() +if (CONFIG_SOC_ACP_7_0) + # Platform sources + zephyr_library_sources( + ${SOF_DRIVERS_PATH}/amd/rembrandt/ipc.c + ${SOF_DRIVERS_PATH}/amd/common/ipc.c + ${SOF_PLATFORM_PATH}/amd/acp_7_0/platform.c + ${SOF_PLATFORM_PATH}/amd/acp_7_0/lib/clk.c + ) + zephyr_library_sources(lib/dma.c) + zephyr_include_directories(${SOF_PLATFORM_PATH}/amd/acp_7_0/include) + zephyr_include_directories(${SOF_PLATFORM_PATH}/amd/common/include) + zephyr_include_directories(${SOF_PLATFORM_PATH}/amd/acp_7_0/include/arch) + + set(PLATFORM "acp_7_0") +endif() + if (CONFIG_SOC_FAMILY_MTK) set(PLATFORM "mtk") @@ -469,11 +534,21 @@ zephyr_library_sources_ifdef(CONFIG_ZEPHYR_POSIX ${SOF_PLATFORM_PATH}/posix/fuzz.c ) +if (CONFIG_BOARD_QEMU_XTENSA) + set(PLATFORM "qemu_xtensa") + zephyr_library_sources(${SOF_PLATFORM_PATH}/qemu_xtensa/platform.c) +endif() + if(NOT DEFINED PLATFORM) message(FATAL_ERROR "PLATFORM is not defined, check your Kconfiguration?") endif() zephyr_include_directories(${SOF_PLATFORM_PATH}/${PLATFORM}/include) +if(CONFIG_SOF_USERSPACE_INTERFACE_DMA) + zephyr_library_sources(syscall/sof_dma.c) + zephyr_syscall_header(include/sof/lib/sof_dma.h) +endif() + # Mandatory Files used on all platforms. # Commented files will be added/removed as integration dictates. zephyr_library_sources( @@ -497,7 +572,9 @@ zephyr_library_sources( # SOF module interface functions add_subdirectory(../src/module module_unused_install/) +if(CONFIG_COLD_STORE_EXECUTE_DRAM) zephyr_library_sources_ifdef(CONFIG_FAST_GET lib/fast-get.c) +endif() # Optional SOF sources - depends on Kconfig - WIP @@ -513,6 +590,9 @@ zephyr_library_sources_ifdef(CONFIG_SHELL sof_shell.c ) +zephyr_syscall_header(${SOF_SRC_PATH}/include/sof/audio/module_adapter/module/generic.h) +zephyr_syscall_header(${SOF_SRC_PATH}/include/sof/lib/fast-get.h) + zephyr_library_link_libraries(SOF) target_link_libraries(SOF INTERFACE zephyr_interface) @@ -536,10 +616,6 @@ if (NOT CONFIG_COMPILER_INLINE_FUNCTION_OPTION) target_compile_options(SOF INTERFACE -fno-inline-functions) endif() -if (CONFIG_USERSPACE) -target_compile_options(SOF INTERFACE -mno-global-merge) -endif() - # SOF needs `typeof`, `__VA_ARGS__` and maybe other GNU C99 # extensions. TODO other flags required ? target_compile_options(SOF INTERFACE $<$<COMPILE_LANGUAGE:C,ASM>: -std=gnu99>) @@ -555,4 +631,4 @@ include(../scripts/cmake/uuid-registry.cmake) # Create Trace realtive file paths sof_append_relative_path_definitions(modules_sof) -endif() # CONFIG_SOF +endif() # CONFIG_SOF_FULL_ZEPHYR_APPLICATION diff --git a/zephyr/Kconfig b/zephyr/Kconfig index 1314454a0466..f1d1896c4234 100644 --- a/zephyr/Kconfig +++ b/zephyr/Kconfig @@ -1,4 +1,10 @@ -if SOF +config SOF_FULL_ZEPHYR_APPLICATION + bool "Build full SOF Zephyr application" + default y + help + Top-level build option to enable full build of the SOF application + on top of Zephyr. This should be set to no only for unit test and + such special build targets. config SOF_STAGING bool "Enable SOF staging features and modules" @@ -17,6 +23,22 @@ config SOF_USERSPACE processing mode as userspace code and data. This feature is WIP and is not yet ready for production, for developers only. +config SOF_USERSPACE_INTERFACE_DMA + bool "Enable SOF DMA interface to userspace threads" + depends on USERSPACE + help + Allow user-space threads to use the SOF DMA interface. + +config SOF_USERSPACE_LL + bool "Run Low-Latency pipelines in userspace threads" + depends on USERSPACE + select SOF_USERSPACE_INTERFACE_DMA + help + Run Low-Latency (LL) pipelines in userspace threads. This adds + memory protection between operating system resources and + audio application threads. + If unsure, select "N". + config SOF_ZEPHYR_HEAP_CACHED bool "Cached Zephyr heap for SOF memory non-shared zones" default y if CAVS || ACE @@ -26,8 +48,9 @@ config SOF_ZEPHYR_HEAP_CACHED config SOF_ZEPHYR_HEAP_SIZE hex "Size of the Zephyr heap for SOF" - default 0xF0000 if SOC_INTEL_ACE15_MTPM || SOC_INTEL_ACE20_LNL - default 0xD0000 if SOC_INTEL_ACE30 || SOC_INTEL_ACE40 + default 0x40000 if VIRTUAL_HEAP + default 0xF0000 if SOC_ACE15_MTPM || SOC_ACE20_LNL + default 0xD0000 if SOC_ACE30 || SOC_ACE40 default 0x0 help Support scaling of the heap size for different platforms and different types @@ -38,10 +61,18 @@ config SOF_ZEPHYR_HEAP_SIZE NOTE: Keep in mind that the heap size should not be greater than the physical memory size of the system defined in DT (and this includes baseFW text/data). +config SOF_USERSPACE_USE_SHARED_HEAP + bool "Use shared heap for SOF userspace modules" + depends on USERSPACE + help + When set a shared heap will be used for audio buffers between SOF + kernel and userspace modules. + config SOF_ZEPHYR_SHARED_BUFFER_HEAP_SIZE hex "Size of the shared buffer heap for SOF userspace modules" - default 0x1E000 if SOC_INTEL_ACE15_MTPM || SOC_INTEL_ACE20_LNL - default 0x1A000 if SOC_INTEL_ACE30 + default 0x0 if !SOF_USERSPACE_USE_SHARED_HEAP + default 0x1E000 if SOC_ACE15_MTPM || SOC_ACE20_LNL + default 0x10000 if SOC_ACE30 default 0x0 depends on USERSPACE help @@ -51,22 +82,31 @@ config SOF_ZEPHYR_SHARED_BUFFER_HEAP_SIZE NOTE: Keep in mind that the shared heap size should not be greater than the heap size. -config SOF_ZEPHYR_VIRTUAL_HEAP_SIZE - hex "Size of the Zephyr virtual heap for SOF" - depends on VIRTUAL_HEAP - default 0x40000 +config VIRTUAL_HEAP_EXTENDED + bool "Extend the Virtual Heap configuration with additional buffers" + depends on MM_DRV_INTEL_ADSP_MTL_TLB + default y help - Support scaling of the virtual address heap size for different platforms. - NOTE: Keep in mind that the heap size should not be greater than the physical - memory size of the system defined in DT (and this includes baseFW text/data). + Enable additional Virtual Heap buffers. Some configurations need this + while others need the memory for other purposes, e.g. for the virtual + page / virtual region storage. config SOF_ZEPHYR_VIRTUAL_HEAP_REGION_SIZE hex "Size in bytes of virtual memory region for virtual heap shared for all cores" depends on MM_DRV_INTEL_ADSP_MTL_TLB + default 0x140000 if VIRTUAL_HEAP_EXTENDED default 0x100000 help This config defines size of virtual heap region shared between all cores +config SOF_USERSPACE_USE_DRIVER_HEAP + bool "Use driver heap for SOF userspace modules" + depends on USERSPACE + help + When selected, multiple instances of the same userspace module will share + a private heap created for that module's driver. Otherwise, each module + instance will have its own independent private heap. + config SOF_ZEPHYR_USERSPACE_MODULE_HEAP_SIZE hex "Size of the private heap created for each userspace module" default 0x1000 @@ -76,6 +116,51 @@ config SOF_ZEPHYR_USERSPACE_MODULE_HEAP_SIZE module has its own independent heap to which only it has access. This heap is shared between instances of the same module. +config SOF_USERSPACE_PROXY + bool "Use userspace proxy to support userspace modules" + select SOF_USERSPACE_USE_DRIVER_HEAP + select SOF_USERSPACE_USE_SHARED_HEAP + help + When set, userspace modules are launched inside a container created by userspace proxy. + It is responsible for forwarding module function calls coming from sof running in + kernelspace to the module code executed with user privileges. + +config SOF_USERSPACE_MOD_IPC_BY_DP_THREAD + bool "Handle modules IPC in DP thread" + depends on SOF_USERSPACE_PROXY + help + When enabled, IPC requests targeted to a userspace module are executed + in its DP thread instead of the shared user worker thread. + +config SOF_USERSPACE_PROXY_WORKER_STACK_SIZE + int "Userspace IPC worker stack size" + default 4096 + help + Size of the stack used by the IPC worker thread created by the + userspace proxy. This worker handles IPC requests for modules + running in userspace. + +config SOF_USERSPACE_APPLICATION + bool + default USERSPACE && !SOF_USERSPACE_PROXY + depends on SOF_VREGIONS + help + Not manually settable. This is effectively a shortcut to replace numerous + checks for (CONFIG_USERSPACE && !CONFIG_SOF_USERSPACE_PROXY) + +config SOF_VPAGE_MAX_ALLOCS + int "Number of virtual memory page allocations" + default 128 + help + This setting defines the maximum number of virtual memory page + allocations that can be tracked. Each allocation represents a + contiguous block of virtual memory allocated from the virtual memory + region. This API isn't intended for end-user allocations, instead it + should be used by the framework to allocate memory, which is then + used, e.g. to create one or multiple heap allocators in it. Increasing + this number allows for more simultaneous allocations, but also + increases the memory overhead for tracking these allocations. + config ZEPHYR_NATIVE_DRIVERS bool "Use Zephyr native drivers" help @@ -131,9 +216,21 @@ config CROSS_CORE_STREAM can be processed on different cores, however, each stream is processed entirely on single core. +config SOF_BOOT_TEST_ALLOWED + bool "Enable SOF boot-time testing" + help + boot-time testing enabled if supported by the platform + +config SOF_BOOT_TEST_SUPPORTED + bool "Platform supports boot-time testing" + default y + help + boot-time testing supported by the platform + config SOF_BOOT_TEST - bool "enable SOF run-time testing" + bool depends on ZTEST + default y help Run tests during boot. This enables an SOF boot-time self-test. When enabled, the resulting image will run a number of self-tests when the @@ -141,14 +238,34 @@ config SOF_BOOT_TEST initialized. After that SOF will continue running and be usable as usual. +config SOF_BOOT_TEST_STANDALONE + bool "enable tests at boot time that are run instead of SOF main" + select SOF_BOOT_TEST + select ZTEST + help + Run extended set of tests at boot that can use IPC and interfere + with system state. Normal IPC handling of the SOF application + is disabled to allow more complex tests to run. + config SOF_ZEPHYR_NO_SOF_CLOCK bool help Do not use SOF clk.h interface to set the DSP clock frequency. Requires implementation of platform/lib/clk.h. +config SOF_VREGIONS + bool "Enable virtual memory regions" + default y if ACE && !ACE_VERSION_1_5 && !ACE_VERSION_2_0 + depends on ACE + help + Enable the virtual regions memory allocator for pipeline resource management. + This provides a way to manage memory resources for audio pipelines, + including + 1) multiple pipeline static lifetime allocations. + 2) runtime pipeline allocations. + config VIRTUAL_HEAP - bool "Use virtual memory heap to allocate a buffers" + bool "Use virtual memory heap to allocate buffers" default y if ACE depends on ACE help @@ -166,5 +283,3 @@ config STACK_SIZE_IPC_TX default 2048 help IPC sender work-queue thread stack size. Keep a power of 2. - -endif diff --git a/zephyr/boot_test.c b/zephyr/boot_test.c index 4cbbfa597d8b..020072856ecb 100644 --- a/zephyr/boot_test.c +++ b/zephyr/boot_test.c @@ -12,9 +12,7 @@ LOG_MODULE_REGISTER(sof_boot_test, LOG_LEVEL_DBG); ZTEST_SUITE(sof_boot, NULL, NULL, NULL, NULL, NULL); -void sys_run_boot_tests(void) +void sof_run_boot_tests(void) { ztest_run_all(NULL, false, 1, 1); } -SYS_INIT(sys_run_boot_tests, APPLICATION, 99); - diff --git a/zephyr/docker-build.sh b/zephyr/docker-build.sh index 161ab896c389..fa54fe318d87 100755 --- a/zephyr/docker-build.sh +++ b/zephyr/docker-build.sh @@ -50,8 +50,6 @@ unset ZEPHYR_SDK_INSTALL_DIR # CMake v3.21 changed the order object files are passed to the linker. # This makes builds before that version not reproducible. # To save time don't install if recent enough. -pip install 'cmake>=3.21' -PATH="$HOME"/.local/bin:"$PATH" if test -e .west || test -e zephyr; then init_update='' diff --git a/zephyr/docker-run.sh b/zephyr/docker-run.sh index 8cc27df65a21..e71e266d52b8 100755 --- a/zephyr/docker-run.sh +++ b/zephyr/docker-run.sh @@ -54,8 +54,8 @@ main() run_command() { - # zephyr-lite:v0.27.4 has /opt/toolchains/zephyr-sdk-0.17.0 - # zephyr-lite:v0.27.4 is based on zephyr-build:v0.27.4 + # zephyr-lite:v0.29.0 has /opt/toolchains/zephyr-sdk-1.0.0 + # zephyr-lite:v0.29.0 is based on zephyr-build:v0.29.0 # https://hub.docker.com/r/zephyrprojectrtos/zephyr-build/tags # https://hub.docker.com/r/thesofproject/zephyr-lite/tags # @@ -65,7 +65,7 @@ run_command() --workdir /zep_workspace \ $SOF_DOCKER_RUN \ --env REAL_CC --env http_proxy --env https_proxy \ - thesofproject/zephyr-lite:v0.27.4 \ + thesofproject/zephyr-lite:v0.29.0 \ ./sof/scripts/sudo-cwd.sh "$@" } diff --git a/zephyr/edf_schedule.c b/zephyr/edf_schedule.c index 8aca007137e7..1a0231426c27 100644 --- a/zephyr/edf_schedule.c +++ b/zephyr/edf_schedule.c @@ -5,6 +5,7 @@ // Author: Bartosz Kokoszko <bartoszx.kokoszko@linux.intel.com> #include <sof/audio/component.h> +#include <rtos/alloc.h> #include <rtos/task.h> #include <stdint.h> #include <sof/schedule/edf_schedule.h> @@ -111,8 +112,11 @@ int scheduler_init_edf(void) k_thread_suspend(thread); + k_thread_heap_assign(thread, sof_sys_heap_get()); +#ifdef CONFIG_SCHED_CPU_MASK k_thread_cpu_mask_clear(thread); k_thread_cpu_mask_enable(thread, PLATFORM_PRIMARY_CORE_ID); +#endif k_thread_name_set(thread, "edf_workq"); k_thread_resume(thread); diff --git a/zephyr/include/rtos/alloc.h b/zephyr/include/rtos/alloc.h index 0cf885ffe2ad..e21e498f0471 100644 --- a/zephyr/include/rtos/alloc.h +++ b/zephyr/include/rtos/alloc.h @@ -25,26 +25,28 @@ * the first two positions are reserved for SOF_BUF_ flags */ -/** \name Heap zone flags +/** \name Allocation flags * @{ */ - /** \brief Indicates we should return DMA-able memory. */ + /** \brief Allocate DMA-able memory. */ #define SOF_MEM_FLAG_DMA BIT(2) -/** \brief Indicates that original content should not be copied by realloc. */ +/** \brief realloc() skips copying the original content. */ #define SOF_MEM_FLAG_NO_COPY BIT(3) -/** \brief Indicates that if we should return uncached address. */ +/** \brief Allocate uncached address. */ #define SOF_MEM_FLAG_COHERENT BIT(4) -/** \brief Indicates that if we should return L3 address. */ +/** \brief Allocate L3 address. */ #define SOF_MEM_FLAG_L3 BIT(5) -/** \brief Indicates that if we should return Low power memory address. */ +/** \brief Allocate Low power memory address. */ #define SOF_MEM_FLAG_LOW_POWER BIT(6) -/** \brief Indicates that if we should return kernel memory address. */ +/** \brief Allocate kernel memory address. */ #define SOF_MEM_FLAG_KERNEL BIT(7) -/** \brief Indicates that if we should return user memory address. */ +/** \brief Allocate user memory address. */ #define SOF_MEM_FLAG_USER BIT(8) -/** \brief Indicates that if we should return shared user memory address. */ +/** \brief Allocate shared user memory address. */ #define SOF_MEM_FLAG_USER_SHARED_BUFFER BIT(9) +/** \brief Use allocation method for large buffers. */ +#define SOF_MEM_FLAG_LARGE_BUFFER BIT(10) /** @} */ @@ -52,9 +54,15 @@ * Allocates memory block. * @param flags Flags, see SOF_MEM_FLAG_.... * @param bytes Size in bytes. + * @param alignment Alignment in bytes. * @return Pointer to the allocated memory or NULL if failed. * */ +void *rmalloc_align(uint32_t flags, size_t bytes, uint32_t alignment); + +/** + * Similar to rmalloc_align(), but no alignment can be specified. + */ void *rmalloc(uint32_t flags, size_t bytes); /** @@ -80,29 +88,6 @@ static inline void *rballoc(uint32_t flags, size_t bytes) return rballoc_align(flags, bytes, PLATFORM_DCACHE_ALIGN); } -/** - * Changes size of the memory block allocated. - * @param ptr Address of the block to resize. - * @param flags Flags, see SOF_MEM_FLAG_... - * @param bytes New size in bytes. - * @param old_bytes Old size in bytes. - * @param alignment Alignment in bytes. - * @return Pointer to the resized memory of NULL if failed. - */ -void *rbrealloc_align(void *ptr, uint32_t flags, size_t bytes, - size_t old_bytes, uint32_t alignment); - -/** - * Similar to rballoc_align(), returns resized buffer aligned to - * PLATFORM_DCACHE_ALIGN. - */ -static inline void *rbrealloc(void *ptr, uint32_t flags, - size_t bytes, size_t old_bytes) -{ - return rbrealloc_align(ptr, flags, bytes, old_bytes, - PLATFORM_DCACHE_ALIGN); -} - /** * Frees the memory block. * @param ptr Pointer to the memory block. @@ -114,12 +99,41 @@ void rfree(void *ptr); */ void l3_heap_save(void); +void *sof_heap_alloc(struct k_heap *heap, uint32_t flags, size_t bytes, + size_t alignment); +void sof_heap_free(struct k_heap *heap, void *addr); +#if CONFIG_SOF_FULL_ZEPHYR_APPLICATION +struct k_heap *sof_sys_heap_get(void); +#else +/* for unit-testing */ +static inline struct k_heap *sof_sys_heap_get(void) +{ + return NULL; +} +#endif + +/** + * Returns heap object to use for SOF heap allocations + * for audio application code. + * + * The returned value may be NULL. This matches with semantics + * of sof_heap_alloc() that allows passing NULL as the 'heap'. + * In this case, the heap implementation will choose the heap to + * use. + * + * The function should not be used for heap allocations for objects that + * are only used in SOF kernel space. + * + * Note: audio modules should use mod_alloc() instead! + */ +struct k_heap *sof_sys_user_heap_get(void); + /* TODO: remove - debug only - only needed for linking */ static inline void heap_trace_all(int force) {} /** @}*/ -#if CONFIG_USERSPACE +#if CONFIG_SOF_USERSPACE_USE_SHARED_HEAP /** * Returns the start address of shared memory heap for buffers. * diff --git a/zephyr/include/rtos/interrupt.h b/zephyr/include/rtos/interrupt.h index 101aa7e65bef..200d36e4751e 100644 --- a/zephyr/include/rtos/interrupt.h +++ b/zephyr/include/rtos/interrupt.h @@ -11,7 +11,7 @@ /* TODO: to be removed completely when the following platforms are switched * to native drivers. */ -#if defined(CONFIG_AMD) +#if defined(CONFIG_AMD) && !defined(CONFIG_ZEPHYR_NATIVE_DRIVERS) /* imx currently has no IRQ driver in Zephyr so we force to xtos IRQ */ #include "../../../xtos/include/rtos/interrupt.h" #else @@ -86,6 +86,6 @@ static inline void platform_interrupt_init(void) {} #define irq_local_enable(flags) \ arch_irq_unlock(flags) -#endif /* IMX */ +#endif /* CONFIG_AMD && !CONFIG_ZEPHYR_NATIVE_DRIVERS */ #endif /* __ZEPHYR_RTOS_INTERRUPT_H__ */ diff --git a/zephyr/include/rtos/kernel.h b/zephyr/include/rtos/kernel.h index 17d388cbf8e8..328fc96d2eaa 100644 --- a/zephyr/include/rtos/kernel.h +++ b/zephyr/include/rtos/kernel.h @@ -8,4 +8,11 @@ #include <zephyr/kernel.h> +#include <stdbool.h> + +static inline bool thread_is_userspace(struct k_thread *thread) +{ + return !!(thread->base.user_options & K_USER); +} + #endif /* __ZEPHYR_RTOS_KERNEL_H__ */ diff --git a/zephyr/include/rtos/mutex.h b/zephyr/include/rtos/mutex.h index 9f26c7a86d75..a8886f768d1e 100644 --- a/zephyr/include/rtos/mutex.h +++ b/zephyr/include/rtos/mutex.h @@ -7,5 +7,6 @@ #define __ZEPHYR_RTOS_MUTEX_H__ #include <zephyr/kernel.h> /* k_mutex_*() */ +#include <zephyr/sys/mutex.h> /* for sys_mutex */ #endif /* __ZEPHYR_RTOS_MUTEX_H__ */ diff --git a/zephyr/include/rtos/sof.h b/zephyr/include/rtos/sof.h index 1de60c5cd6ff..573e6ac63d77 100644 --- a/zephyr/include/rtos/sof.h +++ b/zephyr/include/rtos/sof.h @@ -111,11 +111,6 @@ struct sof { struct ext_library *ext_library; #endif -#if CONFIG_IPC_MAJOR_4 - /* lock for fw_reg access */ - struct k_spinlock fw_reg_lock; -#endif - __aligned(PLATFORM_DCACHE_ALIGN) int alignment[0]; } __aligned(PLATFORM_DCACHE_ALIGN); diff --git a/zephyr/include/rtos/string.h b/zephyr/include/rtos/string.h index 49c26acd17da..37bd657ff91d 100644 --- a/zephyr/include/rtos/string.h +++ b/zephyr/include/rtos/string.h @@ -69,8 +69,14 @@ static inline int memset_s(void *dest, size_t dest_size, int data, size_t count) if (count > dest_size) return -EINVAL; - if (!memset(dest, data, count)) - return -ENOMEM; + memset(dest, data, count); + /* + * Prevent compiler from optimizing away the memset. + * Memory barrier prevents dead store elimination. + */ +#if defined(CONFIG_XTENSA) + __asm__ __volatile__("memw" ::: "memory"); +#endif return 0; } diff --git a/zephyr/include/rtos/userspace_helper.h b/zephyr/include/rtos/userspace_helper.h index 254c8a2f96f1..7d8f7919d865 100644 --- a/zephyr/include/rtos/userspace_helper.h +++ b/zephyr/include/rtos/userspace_helper.h @@ -12,25 +12,35 @@ #ifndef __ZEPHYR_LIB_USERSPACE_HELPER_H__ #define __ZEPHYR_LIB_USERSPACE_HELPER_H__ +#include <zephyr/kernel.h> + #ifndef CONFIG_USERSPACE #define APP_TASK_BSS #define APP_TASK_DATA #else - +#include <zephyr/cache.h> #include <zephyr/app_memory/app_memdomain.h> -#define DRV_HEAP_SIZE ALIGN_UP(CONFIG_SOF_ZEPHYR_USERSPACE_MODULE_HEAP_SIZE, \ - CONFIG_MM_DRV_PAGE_SIZE) +#define USER_MOD_HEAP_SIZE ALIGN_UP(CONFIG_SOF_ZEPHYR_USERSPACE_MODULE_HEAP_SIZE, \ + CONFIG_MM_DRV_PAGE_SIZE) #define APP_TASK_BSS K_APP_BMEM(common_partition) #define APP_TASK_DATA K_APP_DMEM(common_partition) +#ifdef CONFIG_SOF_USERSPACE_LL +#define APP_SYSUSER_BSS K_APP_BMEM(sysuser_partition) +#define APP_SYSUSER_DATA K_APP_DMEM(sysuser_partition) +#else +#define APP_SYSUSER_BSS +#define APP_SYSUSER_DATA +#endif + struct processing_module; struct userspace_context; /** * Initialize private processing module heap. * @param N/A. - * @return pointer to the sys_heap structure. + * @return pointer to the k_heap structure. * * @note * Function used only when CONFIG_USERSPACE is set. @@ -38,19 +48,19 @@ struct userspace_context; * that should be isolated. The heap helps to accumulate all dynamic allocations in single memory * region which is then added to modules memory domain. */ -struct sys_heap *module_driver_heap_init(void); +struct k_heap *module_driver_heap_init(void); /** - * Add DP scheduler created thread to module memory domain. - * @param thread_id - id of thread to be added to memory domain. - * @param module - processing module structure + * Attach common userspace memory partition to a module memory domain. + * @param dom - memory domain to attach the common partition to. * * @return 0 for success, error otherwise. * * @note * Function used only when CONFIG_USERSPACE is set. + * The common partition contains shared objects required by user-space modules. */ -int user_memory_init_shared(k_tid_t thread_id, struct processing_module *mod); +int user_memory_attach_common_partition(struct k_mem_domain *dom); #endif @@ -82,55 +92,80 @@ void *user_stack_allocate(size_t stack_size, uint32_t options); int user_stack_free(void *p_stack); /** - * Allocates memory block from private module sys_heap if exists, otherwise call rballoc_align(). - * @param sys_heap - pointer to the sys_heap structure - * @param flags - Flags, see SOF_MEM_FLAG_... - * @param bytes - Size in bytes. - * @param alignment - Alignment in bytes. - * @return Pointer to the allocated memory or NULL if failed. - * - * @note When CONFIG_USERSPACE not set function calls rballoc_align() + * Free private processing module heap. + * @param sys_heap pointer to the sys_heap structure. + * + * @note + * Function used only when CONFIG_USERSPACE is set. + * Frees private module heap. */ -void *module_driver_heap_aligned_alloc(struct sys_heap *mod_drv_heap, uint32_t flags, size_t bytes, - uint32_t align); +void module_driver_heap_remove(struct k_heap *mod_drv_heap); + +#ifdef CONFIG_USERSPACE /** - * Allocates memory block from private module sys_heap if exists, otherwise call rmalloc. - * @param sys_heap - pointer to the sys_heap structure - * @param flags - Flags, see SOF_MEM_FLAG_... - * @param bytes - Size in bytes. - * @return - Pointer to the allocated memory or NULL if failed. + * Add access to mailbox.h interface to a user-space thread. * - * * @note When CONFIG_USERSPACE not set function calls rmalloc() + * @param domain memory domain to add the mailbox partitions to + * @param thread_id user-space thread for which access is added */ -void *module_driver_heap_rmalloc(struct sys_heap *mod_drv_heap, uint32_t flags, size_t bytes); +int user_access_to_mailbox(struct k_mem_domain *domain, k_tid_t thread_id); /** - * Similar to user_rmalloc(), guarantees that returned block is zeroed. + * Derive partition attribute from the pointer. If the address is cacheable, sets the cacheable + * attribute. * - * @note When CONFIG_USERSPACE not set function calls rzalloc() + * @param ptr Address of the partition start */ -void *module_driver_heap_rzalloc(struct sys_heap *mod_drv_heap, uint32_t flags, size_t bytes); +static inline uint32_t user_get_partition_attr(uintptr_t ptr) +{ + return sys_cache_is_ptr_cached(UINT_TO_POINTER(ptr)) ? XTENSA_MMU_CACHED_WB : 0; +} /** - * Frees the memory block from private module sys_heap if exists. Otherwise call rfree. - * @param ptr Pointer to the memory block. + * Grant DAI device access to a user-space thread. * - * @note User should take care to not free memory allocated from sys_heap - * with mod_drv_heap set to NULL. It will cause exception. + * @param thread user-space thread for which DAI access is granted + */ +void user_grant_dai_access_all(struct k_thread *thread); + +/** + * Grant DMA device access to a user-space thread. * - * When CONFIG_USERSPACE not set function calls rfree() + * @param thread user-space thread for which DMA access is granted */ -void module_driver_heap_free(struct sys_heap *mod_drv_heap, void *mem); +void user_grant_dma_access_all(struct k_thread *thread); + +#else + +static inline int user_access_to_mailbox(struct k_mem_domain *domain, k_tid_t thread_id) +{ + return 0; +} + +#endif /* CONFIG_USERSPACE */ + +#ifdef CONFIG_SOF_USERSPACE_LL + +int user_memory_attach_system_user_partition(struct k_mem_domain *dom); + +#else /** - * Free private processing module heap. - * @param sys_heap pointer to the sys_heap structure. + * Attach SOF system user memory partition to a memory domain. + * @param dom - memory domain to attach the sysuser partition to. + * + * @return 0 for success, error otherwise. * * @note * Function used only when CONFIG_USERSPACE is set. - * Frees private module heap. + * The sysuser partition contains shared objects required by user-space modules. */ -void module_driver_heap_remove(struct sys_heap *mod_drv_heap); +static inline int user_memory_attach_system_user_partition(struct k_mem_domain *dom) +{ + return 0; +} + +#endif /* CONFIG_SOF_USERSPACE_LL */ #endif /* __ZEPHYR_LIB_USERSPACE_HELPER_H__ */ diff --git a/zephyr/include/sof/lib/dma.h b/zephyr/include/sof/lib/dma.h index 3c7f56d7b5f5..509495634097 100644 --- a/zephyr/include/sof/lib/dma.h +++ b/zephyr/include/sof/lib/dma.h @@ -34,6 +34,7 @@ struct comp_buffer; struct comp_dev; +struct k_heap; /** \addtogroup sof_dma_drivers DMA Drivers * SOF DMA Drivers API specification (deprecated interface, to be @@ -266,24 +267,13 @@ typedef int (*dma_process_func)(const struct audio_stream *source, */ int dmac_init(struct sof *sof); -/** - * \brief API to request a platform DMAC. - * - * Users can request DMAC based on dev type, copy direction, capabilities - * and access privilege. - * For exclusive access, ret DMAC with no channels draining. - * For shared access, ret DMAC with the least number of channels draining. - */ -struct sof_dma *sof_dma_get(uint32_t dir, uint32_t caps, uint32_t dev, uint32_t flags); - -/** - * \brief API to release a platform DMAC. - * - * @param[in] dma DMAC to relese. +/* + * Need to use sof_dma.h to avoid "syscalls/dma.h" name conflict + * with Zephyr autogenerated headers for syscall support. */ -void sof_dma_put(struct sof_dma *dma); - -#ifndef CONFIG_ZEPHYR_NATIVE_DRIVERS +#ifdef CONFIG_ZEPHYR_NATIVE_DRIVERS +#include "sof_dma.h" +#else #include "dma-legacy.h" #endif /* !CONFIG_ZEPHYR_NATIVE_DRIVERS */ @@ -302,13 +292,14 @@ static inline void dma_sg_init(struct dma_sg_elem_array *ea) ea->elems = NULL; } -int dma_sg_alloc(struct dma_sg_elem_array *ea, +int dma_sg_alloc(struct k_heap *heap, + struct dma_sg_elem_array *ea, uint32_t flags, 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); +void dma_sg_free(struct k_heap *heap, struct dma_sg_elem_array *ea); /** * \brief Get the total size of SG buffer diff --git a/zephyr/include/sof/lib/memory.h b/zephyr/include/sof/lib/memory.h index be01675951d5..6fa6a8ef558d 100644 --- a/zephyr/include/sof/lib/memory.h +++ b/zephyr/include/sof/lib/memory.h @@ -32,6 +32,9 @@ void dbg_path_cold_enter(const char *fn); static inline void __assert_can_be_cold(const char *fn) { + if (k_is_user_context()) + return; + __ASSERT(!ll_sch_is_current(), "%s() called from an LL thread!", fn); dbg_path_cold_enter(fn); } diff --git a/zephyr/include/sof/lib/regions_mm.h b/zephyr/include/sof/lib/regions_mm.h index 757094e32fcf..abaaf158d3d4 100644 --- a/zephyr/include/sof/lib/regions_mm.h +++ b/zephyr/include/sof/lib/regions_mm.h @@ -18,8 +18,9 @@ #include <zephyr/sys/mem_blocks.h> /* Attributes for memory regions */ -#define VIRTUAL_REGION_SHARED_HEAP_ATTR 1U /*< region dedicated for shared virtual heap */ -#define VIRTUAL_REGION_LLEXT_LIBRARIES_ATTR 2U /*< region dedicated for LLEXT libraries */ +#define VIRTUAL_REGION_SHARED_HEAP_ATTR 1U /*< region for shared virtual heap */ +#define VIRTUAL_REGION_LLEXT_LIBRARIES_ATTR 2U /*< region for LLEXT libraries */ +#define VIRTUAL_REGION_VPAGES_ATTR 3U /*< region for virtual page allocator */ /* Dependency on ipc/topology.h created due to memory capability definitions * that are defined there @@ -87,6 +88,9 @@ int vmh_free_heap(struct vmh_heap *heap); int vmh_free(struct vmh_heap *heap, void *ptr); void vmh_get_default_heap_config(const struct sys_mm_drv_region *region, struct vmh_heap_config *cfg); +#ifdef CONFIG_SYS_MEM_BLOCKS_RUNTIME_STATS +void vmh_log_stats(struct vmh_heap *heap); +#endif /** * @brief Checks if ptr is in range of given memory range * diff --git a/zephyr/include/sof/lib/sof_dma.h b/zephyr/include/sof/lib/sof_dma.h new file mode 100644 index 000000000000..bdd64aeac9fa --- /dev/null +++ b/zephyr/include/sof/lib/sof_dma.h @@ -0,0 +1,193 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 2025, Intel Corporation. + */ + +/* + * Need to use sof_dma.h to avoid "syscalls/dma.h" name conflict + * with Zephyr autogenerated headers for syscall support. + */ + +#ifndef SOF_DMA_H +#define SOF_DMA_H + +/** + * \brief API to request a platform DMAC. + * + * Users can request DMAC based on dev type, copy direction, capabilities + * and access privilege. + * For exclusive access, ret DMAC with no channels draining. + * For shared access, ret DMAC with the least number of channels draining. + */ +__syscall struct sof_dma *sof_dma_get(uint32_t dir, uint32_t caps, uint32_t dev, uint32_t flags); + +struct sof_dma *z_impl_sof_dma_get(uint32_t dir, uint32_t cap, uint32_t dev, uint32_t flags); + + +/** + * \brief API to release a platform DMAC. + * + * @param[in] dma DMAC to release. + */ +__syscall void sof_dma_put(struct sof_dma *dma); + +void z_impl_sof_dma_put(struct sof_dma *dma); + +__syscall int sof_dma_get_attribute(struct sof_dma *dma, uint32_t type, uint32_t *value); + +__syscall int sof_dma_request_channel(struct sof_dma *dma, uint32_t stream_tag); + +__syscall void sof_dma_release_channel(struct sof_dma *dma, + uint32_t channel); + +__syscall int sof_dma_config(struct sof_dma *dma, uint32_t channel, + struct dma_config *config); + +__syscall int sof_dma_start(struct sof_dma *dma, uint32_t channel); + +__syscall int sof_dma_stop(struct sof_dma *dma, uint32_t channel); + +__syscall int sof_dma_get_status(struct sof_dma *dma, uint32_t channel, struct dma_status *stat); + +__syscall int sof_dma_reload(struct sof_dma *dma, uint32_t channel, size_t size); + +__syscall int sof_dma_suspend(struct sof_dma *dma, uint32_t channel); + +__syscall int sof_dma_resume(struct sof_dma *dma, uint32_t channel); + +static inline int z_impl_sof_dma_get_attribute(struct sof_dma *dma, uint32_t type, uint32_t *value) +{ + return dma_get_attribute(dma->z_dev, type, value); +} + +static inline int z_impl_sof_dma_request_channel(struct sof_dma *dma, uint32_t stream_tag) +{ + return dma_request_channel(dma->z_dev, &stream_tag); +} + +static inline void z_impl_sof_dma_release_channel(struct sof_dma *dma, + uint32_t channel) +{ + dma_release_channel(dma->z_dev, channel); +} + +static inline int z_impl_sof_dma_config(struct sof_dma *dma, uint32_t channel, + struct dma_config *config) +{ + return dma_config(dma->z_dev, channel, config); +} + + +static inline int z_impl_sof_dma_start(struct sof_dma *dma, uint32_t channel) +{ + return dma_start(dma->z_dev, channel); +} + +static inline int z_impl_sof_dma_stop(struct sof_dma *dma, uint32_t channel) +{ + return dma_stop(dma->z_dev, channel); +} + +static inline int z_impl_sof_dma_get_status(struct sof_dma *dma, uint32_t channel, + struct dma_status *stat) +{ + return dma_get_status(dma->z_dev, channel, stat); +} + +static inline int z_impl_sof_dma_reload(struct sof_dma *dma, uint32_t channel, size_t size) +{ + return dma_reload(dma->z_dev, channel, 0, 0, size); +} + +static inline int z_impl_sof_dma_suspend(struct sof_dma *dma, uint32_t channel) +{ + return dma_suspend(dma->z_dev, channel); +} + +static inline int z_impl_sof_dma_resume(struct sof_dma *dma, uint32_t channel) +{ + return dma_resume(dma->z_dev, channel); +} + +#ifdef CONFIG_SOF_USERSPACE_INTERFACE_DMA + +/* include definitions from generated file */ +#include <zephyr/syscalls/sof_dma.h> + +#else /* !CONFIG_SOF_USERSPACE_INTERFACE_DMA */ + +/* + * SOF-specific mechanism to allow building SOF with user-space + * support enabled in Zephyr, but not including all syscall + * interfaces in the SOF binary. Thee downside is we cannot + * use the zephyr/syscalls/sof_dma.h boilerplate that is autogenerated + * but instead need a manual wrapper that is below here. + * + * This can be removed if DMA is used in all SOF user-space builds. + */ + +static inline struct sof_dma *sof_dma_get(uint32_t dir, uint32_t caps, uint32_t dev, uint32_t flags) +{ + return z_impl_sof_dma_get(dir, caps, dev, flags); +} + +static inline void sof_dma_put(struct sof_dma *dma) +{ + return z_impl_sof_dma_put(dma); +} + +static inline int sof_dma_get_attribute(struct sof_dma *dma, uint32_t type, uint32_t *value) +{ + return z_impl_sof_dma_get_attribute(dma, type, value); +} + +static inline int sof_dma_request_channel(struct sof_dma *dma, uint32_t stream_tag) +{ + return z_impl_sof_dma_request_channel(dma, stream_tag); +} + +static inline void sof_dma_release_channel(struct sof_dma *dma, + uint32_t channel) +{ + return z_impl_sof_dma_release_channel(dma, channel); +} + +static inline int sof_dma_config(struct sof_dma *dma, uint32_t channel, + struct dma_config *config) +{ + return z_impl_sof_dma_config(dma, channel, config); +} + +static inline int sof_dma_start(struct sof_dma *dma, uint32_t channel) +{ + return z_impl_sof_dma_start(dma, channel); +} + +static inline int sof_dma_stop(struct sof_dma *dma, uint32_t channel) +{ + return z_impl_sof_dma_stop(dma, channel); +} + +static inline int sof_dma_get_status(struct sof_dma *dma, uint32_t channel, struct dma_status *stat) +{ + return z_impl_sof_dma_get_status(dma, channel, stat); +} + +static inline int sof_dma_reload(struct sof_dma *dma, uint32_t channel, size_t size) +{ + return z_impl_sof_dma_reload(dma, channel, size); +} + +static inline int sof_dma_suspend(struct sof_dma *dma, uint32_t channel) +{ + return z_impl_sof_dma_suspend(dma, channel); +} + +static inline int sof_dma_resume(struct sof_dma *dma, uint32_t channel) +{ + return z_impl_sof_dma_resume(dma, channel); +} + +#endif /* CONFIG_SOF_USERSPACE_INTERFACE_DMA */ + +#endif diff --git a/zephyr/include/sof/lib/vpage.h b/zephyr/include/sof/lib/vpage.h new file mode 100644 index 000000000000..f3fc8b89e968 --- /dev/null +++ b/zephyr/include/sof/lib/vpage.h @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: BSD-3-Clause +// Copyright(c) 2025 Intel Corporation. + +/* Virtual Page Allocator API */ +#ifndef __SOF_LIB_VPAGE_H__ +#define __SOF_LIB_VPAGE_H__ + +#include <zephyr/kernel.h> +#include <stdint.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Allocate virtual pages + * Allocates a specified number of contiguous virtual memory pages by mapping + * physical pages. + * + * @param[in] pages Number of pages (usually 4kB large) to allocate. + * + * @return Pointer to the allocated virtual memory region, or NULL on failure. + */ +void *vpage_alloc(unsigned int pages); + +/** + * @brief Free virtual pages + * Frees previously allocated virtual memory pages and unmaps them. + * + * @param[in] ptr Pointer to the memory pages to free. + */ +void vpage_free(void *ptr); + +#ifdef __cplusplus +} +#endif + +#endif /* __SOF_LIB_VPAGE_H__ */ diff --git a/zephyr/lib/alloc.c b/zephyr/lib/alloc.c index 2af0eb83397d..8192c2caff0f 100644 --- a/zephyr/lib/alloc.c +++ b/zephyr/lib/alloc.c @@ -9,29 +9,27 @@ #include <rtos/idc.h> #include <rtos/interrupt.h> #include <sof/drivers/interrupt-map.h> -#include <sof/lib/dma.h> #include <sof/schedule/schedule.h> #include <sof/lib/notifier.h> #include <sof/lib/pm_runtime.h> #include <sof/audio/pipeline.h> -#include <sof/audio/component_ext.h> +#include <sof/schedule/ll_schedule_domain.h> /* for zephyr_ll_user_heap() */ #include <sof/trace/trace.h> #include <rtos/symbol.h> #include <rtos/wait.h> #define SHARED_BUFFER_HEAP_MEM_SIZE 0 +#if CONFIG_L3_HEAP && CONFIG_MMU +#include <kernel_arch_interface.h> +#endif + #if CONFIG_VIRTUAL_HEAP #include <sof/lib/regions_mm.h> #include <zephyr/drivers/mm/mm_drv_intel_adsp_mtl_tlb.h> struct vmh_heap; struct vmh_heap *virtual_buffers_heap; - -#undef HEAPMEM_SIZE -/* Buffers are allocated from virtual space so we can safely reduce the heap size. - */ -#define HEAPMEM_SIZE CONFIG_SOF_ZEPHYR_VIRTUAL_HEAP_SIZE #endif /* CONFIG_VIRTUAL_HEAP */ @@ -105,8 +103,10 @@ static uint8_t __aligned(PLATFORM_DCACHE_ALIGN) heapmem[HEAPMEM_SIZE]; #undef SHARED_BUFFER_HEAP_MEM_SIZE #define SHARED_BUFFER_HEAP_MEM_SIZE ROUND_UP(CONFIG_SOF_ZEPHYR_SHARED_BUFFER_HEAP_SIZE, \ HOST_PAGE_SIZE) +#if CONFIG_SOF_USERSPACE_USE_SHARED_HEAP __section(".shared_heap_mem") static uint8_t __aligned(HOST_PAGE_SIZE) shared_heapmem[SHARED_BUFFER_HEAP_MEM_SIZE]; +#endif #endif /* CONFIG_USERSPACE */ __section(".heap_mem") static uint8_t __aligned(HOST_PAGE_SIZE) heapmem[HEAPMEM_SIZE - SHARED_BUFFER_HEAP_MEM_SIZE]; @@ -120,10 +120,17 @@ char __aligned(8) heapmem[HEAPMEM_SIZE]; #elif defined(CONFIG_SOC_FAMILY_MTK) extern char _mtk_adsp_sram_end; +#if defined(CONFIG_SOC_MT8365) +#define SRAM_START DT_REG_ADDR(DT_NODELABEL(sram1)) +#define SRAM_SIZE DT_REG_SIZE(DT_NODELABEL(sram1)) +#define heapmem ((uint8_t *)SRAM_START) +#else #define SRAM_START DT_REG_ADDR(DT_NODELABEL(sram0)) #define SRAM_SIZE DT_REG_SIZE(DT_NODELABEL(sram0)) -#define SRAM_END (SRAM_START + SRAM_SIZE) #define heapmem ((uint8_t *)ALIGN_UP((uintptr_t)&_mtk_adsp_sram_end, PLATFORM_DCACHE_ALIGN)) +#endif /* CONFIG_SOC_MT8365 */ + +#define SRAM_END (SRAM_START + SRAM_SIZE) /* Heap size is limited to 0x7fffU chunk units when CONFIG_SYS_HEAP_SMALL_ONLY is set */ #if defined(CONFIG_SYS_HEAP_SMALL_ONLY) @@ -142,20 +149,28 @@ extern char _end[], _heap_sentry[]; static struct k_heap sof_heap; -#if CONFIG_USERSPACE -static struct k_heap shared_buffer_heap; - -static bool is_shared_buffer_heap_pointer(void *ptr) +/** + * Checks whether pointer is from a given heap memory. + * @param heap Pointer to a heap. + * @param ptr Pointer to memory being checked. + * @return True if pointer falls into heap memory region, false otherwise. + */ +static bool is_heap_pointer(const struct k_heap *heap, void *ptr) { - uintptr_t shd_heap_start = POINTER_TO_UINT(shared_heapmem); - uintptr_t shd_heap_end = POINTER_TO_UINT(shared_heapmem + SHARED_BUFFER_HEAP_MEM_SIZE); + uintptr_t heap_start = + POINTER_TO_UINT(sys_cache_cached_ptr_get(heap->heap.init_mem)); + uintptr_t heap_end = heap_start + heap->heap.init_bytes; - if (is_cached(ptr)) - ptr = sys_cache_uncached_ptr_get((__sparse_force void __sparse_cache *)ptr); + if (!sys_cache_is_ptr_cached(ptr)) + ptr = (__sparse_force void *)sys_cache_cached_ptr_get(ptr); - return (POINTER_TO_UINT(ptr) >= shd_heap_start) && (POINTER_TO_UINT(ptr) < shd_heap_end); + return ((POINTER_TO_UINT(ptr) >= heap_start) && + (POINTER_TO_UINT(ptr) < heap_end)); } +#if CONFIG_SOF_USERSPACE_USE_SHARED_HEAP +static struct k_heap shared_buffer_heap; + /** * Returns the start of HPSRAM Shared memory heap. * @return Pointer to the HPSRAM Shared memory location which can be used @@ -174,7 +189,7 @@ size_t get_shared_buffer_heap_size(void) { return ROUND_DOWN(SHARED_BUFFER_HEAP_MEM_SIZE, HOST_PAGE_SIZE); } -#endif /* CONFIG_USERSPACE */ +#endif /* CONFIG_SOF_USERSPACE_USE_SHARED_HEAP */ #if CONFIG_L3_HEAP static struct k_heap l3_heap; @@ -208,7 +223,9 @@ static inline size_t get_l3_heap_size(void) * - IMR base address * - actual IMR heap start */ - return ROUND_DOWN(IMR_L3_HEAP_SIZE, L3_MEM_PAGE_SIZE); + size_t offset = IMR_L3_HEAP_BASE - L3_MEM_BASE_ADDR; + + return ROUND_DOWN(ace_imr_get_mem_size() - offset, L3_MEM_PAGE_SIZE); } void l3_heap_save(void) @@ -221,22 +238,6 @@ void l3_heap_save(void) get_l3_heap_size()); } -/** - * Checks whether pointer is from L3 heap memory range. - * @param ptr Pointer to memory being checked. - * @return True if pointer falls into L3 heap region, false otherwise. - */ -static bool is_l3_heap_pointer(void *ptr) -{ - uintptr_t l3_heap_start = get_l3_heap_start(); - uintptr_t l3_heap_end = l3_heap_start + get_l3_heap_size(); - - if ((POINTER_TO_UINT(ptr) >= l3_heap_start) && (POINTER_TO_UINT(ptr) < l3_heap_end)) - return true; - - return false; -} - static void *l3_heap_alloc_aligned(struct k_heap *h, size_t min_align, size_t bytes) { k_spinlock_key_t key; @@ -283,10 +284,14 @@ static void *virtual_heap_alloc(struct vmh_heap *heap, uint32_t flags, size_t by { void *mem = vmh_alloc(heap, bytes); - if (!mem) + if (!mem) { +#ifdef CONFIG_SYS_MEM_BLOCKS_RUNTIME_STATS + vmh_log_stats(heap); +#endif return NULL; + } - assert(IS_ALIGNED(mem, align)); + assert(align == 0 || IS_ALIGNED(mem, align)); if (flags & SOF_MEM_FLAG_COHERENT) return sys_cache_uncached_ptr_get((__sparse_force void __sparse_cache *)mem); @@ -307,7 +312,7 @@ static bool is_virtual_heap_pointer(void *ptr) POINTER_TO_UINT(sys_cache_cached_ptr_get(&_unused_ram_start_marker)); uintptr_t virtual_heap_end = CONFIG_KERNEL_VM_BASE + CONFIG_KERNEL_VM_SIZE; - if (!is_cached(ptr)) + if (!sys_cache_is_ptr_cached(ptr)) ptr = (__sparse_force void *)sys_cache_cached_ptr_get(ptr); return ((POINTER_TO_UINT(ptr) >= virtual_heap_start) && @@ -335,6 +340,9 @@ static const struct vmh_heap_config static_hp_buffers = { { 2048, 8}, { 4096, 11}, { 8192, 10}, +#if CONFIG_VIRTUAL_HEAP_EXTENDED + { 32768, 8}, +#endif { 65536, 3}, { 131072, 1}, { 524288, 1} /* buffer for kpb */ @@ -368,6 +376,21 @@ SYS_INIT(virtual_heap_init, POST_KERNEL, 1); #endif /* CONFIG_VIRTUAL_HEAP */ +struct k_heap *sof_sys_heap_get(void) +{ + return &sof_heap; +} + +struct k_heap *sof_sys_user_heap_get(void) +{ +#ifdef CONFIG_SOF_USERSPACE_LL + return zephyr_ll_user_heap(); +#else + /* let sof_heap_alloc() pick */ + return NULL; +#endif +} + static void *heap_alloc_aligned(struct k_heap *h, size_t min_align, size_t bytes) { k_spinlock_key_t key; @@ -376,6 +399,20 @@ static void *heap_alloc_aligned(struct k_heap *h, size_t min_align, size_t bytes struct sys_memory_stats stats; #endif + /* + * Zephyr sys_heap stores metadata at start of each + * heap allocation. To ensure no allocated cached buffer + * overlaps the same cacheline with the metadata chunk, + * align both allocation start and size of allocation + * to cacheline. As cached and non-cached allocations are + * mixed, same rules need to be followed for both type of + * allocations. + */ +#ifdef CONFIG_SOF_ZEPHYR_HEAP_CACHED + min_align = MAX(PLATFORM_DCACHE_ALIGN, min_align); + bytes = ALIGN_UP(bytes, PLATFORM_DCACHE_ALIGN); +#endif + key = k_spin_lock(&h->lock); ret = sys_heap_aligned_alloc(&h->heap, min_align, bytes); k_spin_unlock(&h->lock, key); @@ -394,20 +431,6 @@ static void __sparse_cache *heap_alloc_aligned_cached(struct k_heap *h, { void __sparse_cache *ptr; - /* - * Zephyr sys_heap stores metadata at start of each - * heap allocation. To ensure no allocated cached buffer - * overlaps the same cacheline with the metadata chunk, - * align both allocation start and size of allocation - * to cacheline. As cached and non-cached allocations are - * mixed, same rules need to be followed for both type of - * allocations. - */ -#ifdef CONFIG_SOF_ZEPHYR_HEAP_CACHED - min_align = MAX(PLATFORM_DCACHE_ALIGN, min_align); - bytes = ALIGN_UP(bytes, min_align); -#endif - ptr = (__sparse_force void __sparse_cache *)heap_alloc_aligned(h, min_align, bytes); #ifdef CONFIG_SOF_ZEPHYR_HEAP_CACHED @@ -424,7 +447,7 @@ static void heap_free(struct k_heap *h, void *mem) #ifdef CONFIG_SOF_ZEPHYR_HEAP_CACHED void *mem_uncached; - if (is_cached(mem)) { + if (sys_cache_is_ptr_cached(mem)) { mem_uncached = sys_cache_uncached_ptr_get((__sparse_force void __sparse_cache *)mem); sys_cache_data_flush_and_invd_range(mem, sys_heap_usable_size(&h->heap, mem_uncached)); @@ -439,7 +462,7 @@ static void heap_free(struct k_heap *h, void *mem) } -void *rmalloc(uint32_t flags, size_t bytes) +void *rmalloc_align(uint32_t flags, size_t bytes, uint32_t alignment) { void *ptr; struct k_heap *heap; @@ -453,13 +476,13 @@ void *rmalloc(uint32_t flags, size_t bytes) tr_err(&zephyr_tr, "L3_HEAP available for cached addresses only!"); return NULL; } - ptr = (__sparse_force void *)l3_heap_alloc_aligned(heap, 0, bytes); + ptr = (__sparse_force void *)l3_heap_alloc_aligned(heap, alignment, bytes); return ptr; #else k_panic(); #endif -#if CONFIG_USERSPACE +#if CONFIG_SOF_USERSPACE_USE_SHARED_HEAP } else if (flags & SOF_MEM_FLAG_USER_SHARED_BUFFER) { heap = &shared_buffer_heap; #endif @@ -468,17 +491,19 @@ void *rmalloc(uint32_t flags, size_t bytes) } if (!(flags & SOF_MEM_FLAG_COHERENT)) { - ptr = (__sparse_force void *)heap_alloc_aligned_cached(heap, 0, bytes); + ptr = (__sparse_force void *)heap_alloc_aligned_cached(heap, alignment, bytes); } else { - /* - * XTOS alloc implementation has used dcache alignment, - * so SOF application code is expecting this behaviour. - */ - ptr = heap_alloc_aligned(heap, PLATFORM_DCACHE_ALIGN, bytes); + ptr = heap_alloc_aligned(heap, alignment, bytes); } return ptr; } +EXPORT_SYMBOL(rmalloc_align); + +void *rmalloc(uint32_t flags, size_t bytes) +{ + return rmalloc_align(flags, bytes, 0); +} EXPORT_SYMBOL(rmalloc); void *rbrealloc_align(void *ptr, uint32_t flags, size_t bytes, @@ -532,8 +557,7 @@ EXPORT_SYMBOL(rzalloc); * @param align Alignment in bytes. * @return Pointer to the allocated memory or NULL if failed. */ -void *rballoc_align(uint32_t flags, size_t bytes, - uint32_t align) +void *rballoc_align(uint32_t flags, size_t bytes, uint32_t align) { struct k_heap *heap; @@ -546,15 +570,15 @@ void *rballoc_align(uint32_t flags, size_t bytes, tr_err(&zephyr_tr, "L3_HEAP not available."); return NULL; #endif -#if CONFIG_USERSPACE +#if CONFIG_SOF_USERSPACE_USE_SHARED_HEAP } else if (flags & SOF_MEM_FLAG_USER_SHARED_BUFFER) { heap = &shared_buffer_heap; #endif /* CONFIG_USERSPACE */ } else { #if CONFIG_VIRTUAL_HEAP - /* Use virtual heap if it is available */ - if (virtual_buffers_heap) - return virtual_heap_alloc(virtual_buffers_heap, flags, bytes, align); + /* Use virtual heap if it is available */ + if (virtual_buffers_heap) + return virtual_heap_alloc(virtual_buffers_heap, flags, bytes, align); #endif /* CONFIG_VIRTUAL_HEAP */ heap = &sof_heap; @@ -576,7 +600,7 @@ void rfree(void *ptr) return; #if CONFIG_L3_HEAP - if (is_l3_heap_pointer(ptr)) { + if (is_heap_pointer(&l3_heap, ptr)) { l3_heap_free(&l3_heap, ptr); return; } @@ -589,8 +613,8 @@ void rfree(void *ptr) } #endif -#if CONFIG_USERSPACE - if (is_shared_buffer_heap_pointer(ptr)) { +#if CONFIG_SOF_USERSPACE_USE_SHARED_HEAP + if (is_heap_pointer(&shared_buffer_heap, ptr)) { heap_free(&shared_buffer_heap, ptr); return; } @@ -600,20 +624,61 @@ void rfree(void *ptr) } EXPORT_SYMBOL(rfree); +/* + * To match the fall-back SOF main heap all private heaps should also be in the + * uncached address range. + */ +void *sof_heap_alloc(struct k_heap *heap, uint32_t flags, size_t bytes, + size_t alignment) +{ + if (flags & (SOF_MEM_FLAG_LARGE_BUFFER | SOF_MEM_FLAG_USER_SHARED_BUFFER)) + return rballoc_align(flags, bytes, alignment); + + if (!heap) + heap = &sof_heap; + + if (flags & SOF_MEM_FLAG_COHERENT) + return heap_alloc_aligned(heap, alignment, bytes); + + return (__sparse_force void *)heap_alloc_aligned_cached(heap, alignment, bytes); +} + +void sof_heap_free(struct k_heap *heap, void *addr) +{ + if (heap && addr && is_heap_pointer(heap, addr)) + heap_free(heap, addr); + else + rfree(addr); +} + static int heap_init(void) { sys_heap_init(&sof_heap.heap, heapmem, HEAPMEM_SIZE - SHARED_BUFFER_HEAP_MEM_SIZE); -#if CONFIG_USERSPACE +#if CONFIG_SOF_USERSPACE_USE_SHARED_HEAP + shared_buffer_heap.heap.init_mem = shared_heapmem; + shared_buffer_heap.heap.init_bytes = SHARED_BUFFER_HEAP_MEM_SIZE; sys_heap_init(&shared_buffer_heap.heap, shared_heapmem, SHARED_BUFFER_HEAP_MEM_SIZE); #endif #if CONFIG_L3_HEAP - if (l3_heap_copy.heap.heap) - l3_heap = l3_heap_copy; - else - sys_heap_init(&l3_heap.heap, UINT_TO_POINTER(get_l3_heap_start()), - get_l3_heap_size()); + if (ace_imr_used()) { + void *l3_heap_start = UINT_TO_POINTER(get_l3_heap_start()); + size_t l3_heap_size = get_l3_heap_size(); +#if CONFIG_MMU + void *cached_ptr = sys_cache_cached_ptr_get(l3_heap_start); + uintptr_t va = POINTER_TO_UINT(cached_ptr); + + arch_mem_map(l3_heap_start, va, l3_heap_size, K_MEM_PERM_RW | K_MEM_CACHE_WB); +#endif + if (l3_heap_copy.heap.heap) { + l3_heap = l3_heap_copy; + } else { + l3_heap.heap.init_mem = l3_heap_start; + l3_heap.heap.init_bytes = l3_heap_size; + sys_heap_init(&l3_heap.heap, l3_heap_start, l3_heap_size); + } + } #endif return 0; diff --git a/zephyr/lib/cpu.c b/zephyr/lib/cpu.c index 16a583b2970a..94f004bd7356 100644 --- a/zephyr/lib/cpu.c +++ b/zephyr/lib/cpu.c @@ -105,6 +105,7 @@ static void resume_dais(void) struct processing_module *mod; struct copier_data *cd; struct dai_data *dd; + size_t gtw_cfg_size; #if CONFIG_INTEL_ADSP_MIC_PRIVACY /* Re-initialize mic privacy manager first to ensure proper state before DAI resume */ @@ -119,8 +120,14 @@ static void resume_dais(void) mod = comp_mod(icd->cd); cd = module_get_private_data(mod); dd = cd->dd[0]; + /* gtw_cfg.config_length is in words */ + gtw_cfg_size = cd->config.gtw_cfg.config_length << 2; if (dai_probe(dd->dai->dev) < 0) { - tr_err(&zephyr_tr, "DAI resume failed, type %d index %d", + tr_err(&zephyr_tr, "DAI resume failed on probe, type %d index %d", + dd->dai->type, dd->dai->index); + } else if (dai_set_config(dd->dai, &dd->ipc_config, cd->config.gtw_cfg.config_data, + gtw_cfg_size) < 0) { + tr_err(&zephyr_tr, "DAI resume failed on config, type %d index %d", dd->dai->type, dd->dai->index); } @@ -211,7 +218,12 @@ void cpu_notify_state_exit(enum pm_state state) global_imr_ram_storage = NULL; /* send FW Ready message */ - platform_boot_complete(0); + int ret = platform_boot_complete(0); + + if (ret) { + tr_err(&zephyr_tr, "platform_boot_complete failed: %d", ret); + k_panic(); + } #endif } } diff --git a/zephyr/lib/dma.c b/zephyr/lib/dma.c index 3003a4346374..380c31d48843 100644 --- a/zephyr/lib/dma.c +++ b/zephyr/lib/dma.c @@ -78,13 +78,13 @@ SHARED_DATA struct sof_dma dma[] = { .plat_data = { .dir = SOF_DMA_DIR_DEV_TO_MEM, .caps = SOF_DMA_CAP_HDA, -#if defined(CONFIG_SOC_INTEL_ACE20_LNL) || defined(CONFIG_SOC_INTEL_ACE30) || \ - defined(CONFIG_SOC_INTEL_ACE40) +#if defined(CONFIG_SOC_ACE20_LNL) || defined(CONFIG_SOC_ACE30) || \ + defined(CONFIG_SOC_ACE40) .devs = SOF_DMA_DEV_HDA | SOF_DMA_DEV_SSP | SOF_DMA_DEV_DMIC | SOF_DMA_DEV_ALH, #else .devs = SOF_DMA_DEV_HDA, -#endif /* CONFIG_SOC_INTEL_ACE20_LNL || CONFIG_SOC_INTEL_ACE30 || CONFIG_SOC_INTEL_ACE40 */ +#endif /* CONFIG_SOC_ACE20_LNL || CONFIG_SOC_ACE30 || CONFIG_SOC_ACE40 */ .channels = DT_PROP(DT_NODELABEL(hda_link_in), dma_channels), .period_count = HDA_DMA_BUFFER_PERIOD_COUNT, }, @@ -96,13 +96,13 @@ SHARED_DATA struct sof_dma dma[] = { .plat_data = { .dir = SOF_DMA_DIR_MEM_TO_DEV, .caps = SOF_DMA_CAP_HDA, -#if defined(CONFIG_SOC_INTEL_ACE20_LNL) || defined(CONFIG_SOC_INTEL_ACE30) || \ - defined(CONFIG_SOC_INTEL_ACE40) +#if defined(CONFIG_SOC_ACE20_LNL) || defined(CONFIG_SOC_ACE30) || \ + defined(CONFIG_SOC_ACE40) .devs = SOF_DMA_DEV_HDA | SOF_DMA_DEV_SSP | SOF_DMA_DEV_DMIC | SOF_DMA_DEV_ALH, #else .devs = SOF_DMA_DEV_HDA, -#endif /* CONFIG_SOC_INTEL_ACE20_LNL || CONFIG_SOC_INTEL_ACE30 || CONFIG_SOC_INTEL_ACE40 */ +#endif /* CONFIG_SOC_ACE20_LNL || CONFIG_SOC_ACE30 || CONFIG_SOC_ACE40 */ .channels = DT_PROP(DT_NODELABEL(hda_link_out), dma_channels), .period_count = HDA_DMA_BUFFER_PERIOD_COUNT, }, @@ -152,7 +152,7 @@ SHARED_DATA struct sof_dma dma[] = { .z_dev = DEVICE_DT_GET(DT_NODELABEL(host_dma)), }, #endif -#if defined(CONFIG_SOC_MIMX8ML8_ADSP) +#if defined(CONFIG_SOC_MIMX8ML8_ADSP) || defined(CONFIG_SOC_MIMX8ML8_M7) { .plat_data = { .dir = SOF_DMA_DIR_MEM_TO_DEV | SOF_DMA_DIR_DEV_TO_MEM, @@ -212,6 +212,33 @@ SHARED_DATA struct sof_dma dma[] = { .z_dev = DEVICE_DT_GET(DT_NODELABEL(host_dma)), }, #endif /* CONFIG_SOC_MIMX9596_M7 */ +#if defined(CONFIG_SOC_ACP_7_0) +{ + .plat_data = { + .dir = SOF_DMA_DIR_LMEM_TO_HMEM | + SOF_DMA_DIR_HMEM_TO_LMEM, + .devs = SOF_DMA_DEV_HOST, + .base = DMA0_BASE, + .chan_size = DMA0_SIZE, + .channels = 8, + .period_count = 2, + }, + .z_dev = DEVICE_DT_GET(DT_NODELABEL(acp_host_dma)), +}, +{ + .plat_data = { + .dir = SOF_DMA_DIR_MEM_TO_DEV | + SOF_DMA_DIR_DEV_TO_MEM, + .devs = SOF_DMA_DEV_SW, + .caps = SOF_DMA_CAP_SW, + .base = DMA0_BASE, + .chan_size = DMA0_SIZE, + .channels = 12, + .period_count = 2, + }, + .z_dev = DEVICE_DT_GET(DT_NODELABEL(acp_sdw_dma)), +}, +#endif }; const struct dma_info lib_dma = { diff --git a/zephyr/lib/fast-get.c b/zephyr/lib/fast-get.c index 3be9475ff90c..9c502284f082 100644 --- a/zephyr/lib/fast-get.c +++ b/zephyr/lib/fast-get.c @@ -8,13 +8,26 @@ #include <stdint.h> #include <errno.h> +#include <sof/audio/component.h> #include <sof/lib/fast-get.h> +#include <sof/lib/vregion.h> +#include <sof/objpool.h> #include <rtos/alloc.h> #include <rtos/cache.h> +#include <rtos/kernel.h> #include <rtos/spinlock.h> #include <rtos/symbol.h> #include <ipc/topology.h> +#ifdef __ZEPHYR__ +#include <zephyr/logging/log.h> +#else +#define LOG_DBG(...) do {} while (0) +#define LOG_INF(...) do {} while (0) +#define LOG_WRN(...) do {} while (0) +#define LOG_ERR(...) do {} while (0) +#endif + struct sof_fast_get_entry { const void *dram_ptr; void *sram_ptr; @@ -24,148 +37,269 @@ struct sof_fast_get_entry { struct sof_fast_get_data { struct k_spinlock lock; - size_t num_entries; - struct sof_fast_get_entry *entries; + struct objpool_head pool; }; static struct sof_fast_get_data fast_get_data = { - .num_entries = 0, - .entries = NULL, + .pool.list = LIST_INIT(fast_get_data.pool.list), }; LOG_MODULE_REGISTER(fast_get, CONFIG_SOF_LOG_LEVEL); -static int fast_get_realloc(struct sof_fast_get_data *data) +struct fast_get_find { + const void *ptr; + struct sof_fast_get_entry *entry; +}; + +static bool fast_get_find_entry(void *data, void *arg) { - struct sof_fast_get_entry *entries; - /* - * Allocate 8 entries for the beginning. Currently we only use 2 entries - * at most, so this should provide a reasonable first allocation. - */ - const unsigned int init_n_entries = 8; - unsigned int n_entries = data->num_entries ? data->num_entries * 2 : init_n_entries; - - entries = rzalloc(SOF_MEM_FLAG_USER | SOF_MEM_FLAG_COHERENT, - n_entries * sizeof(*entries)); - if (!entries) - return -ENOMEM; - - if (data->num_entries) { - memcpy_s(entries, n_entries * sizeof(*entries), data->entries, - data->num_entries * sizeof(*entries)); - rfree(data->entries); - } + struct sof_fast_get_entry *entry = data; + struct fast_get_find *find = arg; - data->entries = entries; - data->num_entries = n_entries; + if (find->ptr != entry->dram_ptr) + return false; - return 0; + find->entry = entry; + return true; } -static struct sof_fast_get_entry *fast_get_find_entry(struct sof_fast_get_data *data, - const void *dram_ptr) +#if CONFIG_MM_DRV +#define PAGE_SZ CONFIG_MM_DRV_PAGE_SIZE +#define FAST_GET_MAX_COPY_SIZE (PAGE_SZ / 2) +#else +#include <sof/platform.h> +#define PAGE_SZ HOST_PAGE_SIZE +#define FAST_GET_MAX_COPY_SIZE 0 +#endif + +#if CONFIG_USERSPACE +static bool fast_get_partition_exists(struct k_mem_domain *domain, void *start, size_t size) { - int i; + for (unsigned int i = 0; i < domain->num_partitions; i++) { + struct k_mem_partition *dpart = &domain->partitions[i]; - for (i = 0; i < data->num_entries; i++) { - if (data->entries[i].dram_ptr == dram_ptr) - return &data->entries[i]; + if (dpart->start == (uintptr_t)start && dpart->size == size) + return true; } - for (i = 0; i < data->num_entries; i++) { - if (data->entries[i].dram_ptr == NULL) - return &data->entries[i]; - } + return false; +} - return NULL; +static int fast_get_access_grant(struct k_mem_domain *mdom, void *addr, size_t size) +{ + struct k_mem_partition part = { + .start = (uintptr_t)addr, + .size = ALIGN_UP(size, CONFIG_MM_DRV_PAGE_SIZE), + .attr = K_MEM_PARTITION_P_RO_U_RO | XTENSA_MMU_CACHED_WB, + }; + + LOG_DBG("add %#zx @ %p", part.size, addr); + return k_mem_domain_add_partition(mdom, &part); } +#endif /* CONFIG_USERSPACE */ -const void *fast_get(const void *dram_ptr, size_t size) +const void *fast_get(struct mod_alloc_ctx *alloc, const void *dram_ptr, size_t size) { + struct k_heap *heap = alloc ? alloc->heap : NULL; +#if CONFIG_USERSPACE + bool current_is_userspace = thread_is_userspace(k_current_get()); +#endif struct sof_fast_get_data *data = &fast_get_data; + uint32_t alloc_flags = SOF_MEM_FLAG_USER; struct sof_fast_get_entry *entry; + size_t alloc_size, alloc_align; + const void *alloc_ptr; k_spinlock_key_t key; void *ret; key = k_spin_lock(&data->lock); - do { - entry = fast_get_find_entry(data, dram_ptr); + if (IS_ENABLED(CONFIG_USERSPACE) && size > FAST_GET_MAX_COPY_SIZE) { + alloc_size = ALIGN_UP(size, PAGE_SZ); + alloc_align = PAGE_SZ; + alloc_flags |= SOF_MEM_FLAG_LARGE_BUFFER; + } else { + alloc_size = size; + alloc_align = PLATFORM_DCACHE_ALIGN; + } + + if (size > FAST_GET_MAX_COPY_SIZE || !IS_ENABLED(CONFIG_USERSPACE)) + alloc_ptr = dram_ptr; + else + /* When userspace is enabled only share large buffers */ + alloc_ptr = NULL; + + struct fast_get_find find = { + .ptr = alloc_ptr, + }; + objpool_iterate(&fast_get_data.pool, fast_get_find_entry, &find); + entry = find.entry; + if (!entry) { + entry = objpool_alloc(&fast_get_data.pool, sizeof(*entry), + SOF_MEM_FLAG_USER | SOF_MEM_FLAG_COHERENT); if (!entry) { - if (fast_get_realloc(data)) { - ret = NULL; - goto out; - } + ret = NULL; + goto out; } - } while (!entry); + } + +#if CONFIG_USERSPACE + LOG_DBG("%s: %#zx bytes alloc %p entry %p DRAM %p", + current_is_userspace ? "userspace" : "kernel", size, + alloc_ptr, entry->sram_ptr, dram_ptr); +#endif if (entry->sram_ptr) { if (entry->size != size || entry->dram_ptr != dram_ptr) { - tr_err(fast_get, "size %u != %u or ptr %p != %p mismatch", + LOG_ERR("size %u != %u or ptr %p != %p mismatch", entry->size, size, entry->dram_ptr, dram_ptr); ret = NULL; goto out; } ret = entry->sram_ptr; - entry->refcount++; + +#if CONFIG_USERSPACE + struct k_mem_domain *mdom = k_current_get()->mem_domain_info.mem_domain; + /* - * The data is constant, so it's safe to use cached access to - * it, but initially we have to invalidate cached + * We only get there for large buffers, since small buffers with + * enabled userspace don't create fast-get entries */ - dcache_invalidate_region((__sparse_force void __sparse_cache *)ret, size); + if (current_is_userspace) { + if (!fast_get_partition_exists(mdom, ret, + ALIGN_UP(size, CONFIG_MM_DRV_PAGE_SIZE))) { + LOG_DBG("grant access to domain %p", mdom); + + int err = fast_get_access_grant(mdom, ret, size); + + if (err < 0) { + LOG_ERR("failed to grant additional access err=%d", err); + ret = NULL; + goto out; + } + /* + * The data is constant, so it's safe to use cached access to + * it, but initially we have to invalidate caches + */ + dcache_invalidate_region((__sparse_force void __sparse_cache *)ret, + size); + } else { + LOG_WRN("Repeated access request by thread"); + } + } +#endif + + entry->refcount++; goto out; } - ret = rmalloc(SOF_MEM_FLAG_USER, size); + if (alloc && alloc->vreg && size <= FAST_GET_MAX_COPY_SIZE) + /* A userspace allocation, that won't be shared */ + ret = vregion_alloc_align(alloc->vreg, VREGION_MEM_TYPE_INTERIM, alloc_size, + alloc_align); + else + ret = sof_heap_alloc(heap, alloc_flags, alloc_size, alloc_align); if (!ret) goto out; + + memcpy_s(ret, alloc_size, dram_ptr, size); + dcache_writeback_region((__sparse_force void __sparse_cache *)ret, size); + +#if CONFIG_USERSPACE + if (size > FAST_GET_MAX_COPY_SIZE && current_is_userspace) { + /* Otherwise we've allocated on thread's heap, so it already has access */ + int err = fast_get_access_grant(k_current_get()->mem_domain_info.mem_domain, + ret, size); + + if (err < 0) { + LOG_ERR("failed to grant access err=%d", err); + sof_heap_free(heap, ret); + ret = NULL; + goto out; + } + } +#endif /* CONFIG_USERSPACE */ + + entry->dram_ptr = dram_ptr; entry->size = size; entry->sram_ptr = ret; - memcpy_s(entry->sram_ptr, entry->size, dram_ptr, size); - entry->dram_ptr = dram_ptr; entry->refcount = 1; out: k_spin_unlock(&data->lock, key); - tr_dbg(fast_get, "get %p, %p, size %u, refcnt %u", dram_ptr, ret, size, - entry ? entry->refcount : 0); + LOG_DBG("get %p, %p, size %u, refcnt %u", dram_ptr, ret, size, entry ? entry->refcount : 0); return ret; } EXPORT_SYMBOL(fast_get); -static struct sof_fast_get_entry *fast_put_find_entry(struct sof_fast_get_data *data, - const void *sram_ptr) +static bool fast_put_find_entry(void *data, void *arg) { - int i; + struct sof_fast_get_entry *entry = data; + struct fast_get_find *find = arg; - for (i = 0; i < data->num_entries; i++) { - if (data->entries[i].sram_ptr == sram_ptr) - return &data->entries[i]; - } + if (find->ptr != entry->sram_ptr) + return false; - return NULL; + find->entry = entry; + return true; } -void fast_put(const void *sram_ptr) +void fast_put(struct mod_alloc_ctx *alloc, struct k_mem_domain *mdom, const void *sram_ptr) { + struct k_heap *heap = alloc ? alloc->heap : NULL; struct sof_fast_get_data *data = &fast_get_data; struct sof_fast_get_entry *entry; k_spinlock_key_t key; key = k_spin_lock(&fast_get_data.lock); - entry = fast_put_find_entry(data, sram_ptr); + + struct fast_get_find find = { + .ptr = sram_ptr, + }; + objpool_iterate(&fast_get_data.pool, fast_put_find_entry, &find); + entry = find.entry; if (!entry) { - tr_err(fast_get, "Put called to unknown address %p", sram_ptr); + LOG_ERR("Put called to unknown address %p", sram_ptr); goto out; } + entry->refcount--; + if (!entry->refcount) { - rfree(entry->sram_ptr); - memset(entry, 0, sizeof(*entry)); + LOG_DBG("freeing buffer %p", sram_ptr); + if (alloc && alloc->vreg && entry->size <= FAST_GET_MAX_COPY_SIZE) + vregion_free(alloc->vreg, entry->sram_ptr); + else + sof_heap_free(heap, entry->sram_ptr); } + +#if CONFIG_USERSPACE + /* + * For large buffers, each thread that called fast_get() has a partition + * in its memory domain. Each thread must remove its own partition here + * to prevent partition leaks. + */ + if (entry->size > FAST_GET_MAX_COPY_SIZE && mdom) { + struct k_mem_partition part = { + .start = (uintptr_t)sram_ptr, + .size = ALIGN_UP(entry->size, CONFIG_MM_DRV_PAGE_SIZE), + .attr = K_MEM_PARTITION_P_RO_U_RO | XTENSA_MMU_CACHED_WB, + }; + + LOG_DBG("removing partition %p size %#zx memory domain %p", + (void *)part.start, part.size, mdom); + int err = k_mem_domain_remove_partition(mdom, &part); + + if (err) + LOG_WRN("partition removal failed: %d", err); + } +#endif + + if (!entry->refcount) + memset(entry, 0, sizeof(*entry)); out: - tr_dbg(fast_get, "put %p, DRAM %p size %u refcnt %u", sram_ptr, entry ? entry->dram_ptr : 0, - entry ? entry->size : 0, entry ? entry->refcount : 0); + LOG_DBG("put %p, DRAM %p size %u refcnt %u", sram_ptr, entry ? entry->dram_ptr : 0, + entry ? entry->size : 0, entry ? entry->refcount : 0); k_spin_unlock(&data->lock, key); } EXPORT_SYMBOL(fast_put); diff --git a/zephyr/lib/regions_mm.c b/zephyr/lib/regions_mm.c index c9b307b8f298..9b8db4b504af 100644 --- a/zephyr/lib/regions_mm.c +++ b/zephyr/lib/regions_mm.c @@ -10,6 +10,7 @@ #if defined(CONFIG_MM_DRV) #include <sof/lib/regions_mm.h> +LOG_MODULE_DECLARE(mem_allocator, CONFIG_SOF_LOG_LEVEL); /** @struct vmh_heap * @@ -32,6 +33,10 @@ struct vmh_heap { const struct sys_mm_drv_region *virtual_region; struct sys_mem_blocks *physical_blocks_allocators[MAX_MEMORY_ALLOCATORS_COUNT]; struct sys_bitarray *allocation_sizes[MAX_MEMORY_ALLOCATORS_COUNT]; +#ifdef CONFIG_SYS_MEM_BLOCKS_RUNTIME_STATS + unsigned int out_of_blocks[MAX_MEMORY_ALLOCATORS_COUNT]; + bool logged; +#endif bool allocating_continuously; }; @@ -398,6 +403,9 @@ static void *_vmh_alloc(struct vmh_heap *heap, uint32_t alloc_size) int mem_block_iterator, allocation_error_code = -ENOMEM; size_t allocation_bitarray_offset, block_count = 0, block_size = 0, allocation_bitarray_position = 0; +#ifdef CONFIG_SYS_MEM_BLOCKS_RUNTIME_STATS + bool first_match = true; +#endif /* We will gather error code when allocating on physical block * allocators. @@ -487,6 +495,11 @@ static void *_vmh_alloc(struct vmh_heap *heap, uint32_t alloc_size) sys_bitarray_set_region(heap->allocation_sizes[mem_block_iterator], block_count - 1, allocation_bitarray_position); break; +#ifdef CONFIG_SYS_MEM_BLOCKS_RUNTIME_STATS + } else if (first_match) { + ++heap->out_of_blocks[mem_block_iterator]; + first_match = false; +#endif } } @@ -699,6 +712,39 @@ int vmh_free(struct vmh_heap *heap, void *ptr) return ret; } +#ifdef CONFIG_SYS_MEM_BLOCKS_RUNTIME_STATS +/** + * @brief Print stats on heap usage per allocator + * + * @param heap pointer to a heap for which statistics are collected + */ +void vmh_log_stats(struct vmh_heap *heap) +{ + if (heap->logged) + return; + + LOG_INF("Virtual heap stats per allocator"); + LOG_INF(" ID | Total | Max use| Times run out of blocks"); + + for (int idx = 0; idx < MAX_MEMORY_ALLOCATORS_COUNT; idx++) { + if (!heap->physical_blocks_allocators[idx]) + continue; + + struct sys_memory_stats stats = {0}; + + sys_mem_blocks_runtime_stats_get(heap->physical_blocks_allocators[idx], &stats); + + size_t block_size = 1 << heap->physical_blocks_allocators[idx]->info.blk_sz_shift; + size_t block_num = heap->physical_blocks_allocators[idx]->info.num_blocks; + + LOG_INF("%7d| %7u| %7u| %7u", idx, block_num, + (stats.max_allocated_bytes / block_size), + heap->out_of_blocks[idx]); + } + heap->logged = true; +} +#endif + /** * @brief Get default configuration for heap * diff --git a/zephyr/lib/userspace_helper.c b/zephyr/lib/userspace_helper.c index b26370b12d25..c7c361295269 100644 --- a/zephyr/lib/userspace_helper.c +++ b/zephyr/lib/userspace_helper.c @@ -15,187 +15,168 @@ #include <stdint.h> #include <rtos/alloc.h> +#include <rtos/sof.h> #include <rtos/userspace_helper.h> #include <sof/audio/module_adapter/module/generic.h> #include <sof/audio/module_adapter/library/userspace_proxy.h> +#include <sof/lib/mailbox.h> +#include <sof/lib/dai.h> +#include <sof/lib/dma.h> #define MODULE_DRIVER_HEAP_CACHED CONFIG_SOF_ZEPHYR_HEAP_CACHED /* Zephyr includes */ #include <zephyr/kernel.h> #include <zephyr/app_memory/app_memdomain.h> +#include <zephyr/logging/log.h> + +LOG_MODULE_REGISTER(userspace_helper, CONFIG_SOF_LOG_LEVEL); #if CONFIG_USERSPACE K_APPMEM_PARTITION_DEFINE(common_partition); -struct sys_heap *module_driver_heap_init(void) +#ifdef CONFIG_SOF_USERSPACE_LL +K_APPMEM_PARTITION_DEFINE(sysuser_partition); +#endif + +struct k_heap *module_driver_heap_init(void) { - struct sys_heap *mod_drv_heap = rballoc(SOF_MEM_FLAG_USER, sizeof(struct sys_heap)); + struct k_heap *mod_drv_heap = rballoc(SOF_MEM_FLAG_USER, sizeof(*mod_drv_heap)); if (!mod_drv_heap) return NULL; - void *mem = rballoc_align(SOF_MEM_FLAG_USER | SOF_MEM_FLAG_COHERENT, DRV_HEAP_SIZE, + void *mem = rballoc_align(SOF_MEM_FLAG_USER | SOF_MEM_FLAG_COHERENT, USER_MOD_HEAP_SIZE, CONFIG_MM_DRV_PAGE_SIZE); if (!mem) { rfree(mod_drv_heap); return NULL; } - sys_heap_init(mod_drv_heap, mem, DRV_HEAP_SIZE); - mod_drv_heap->init_mem = mem; - mod_drv_heap->init_bytes = DRV_HEAP_SIZE; + k_heap_init(mod_drv_heap, mem, USER_MOD_HEAP_SIZE); + mod_drv_heap->heap.init_mem = mem; + mod_drv_heap->heap.init_bytes = USER_MOD_HEAP_SIZE; return mod_drv_heap; } -void *module_driver_heap_aligned_alloc(struct sys_heap *mod_drv_heap, uint32_t flags, size_t bytes, - uint32_t align) +void module_driver_heap_remove(struct k_heap *mod_drv_heap) { -#ifdef MODULE_DRIVER_HEAP_CACHED - const bool cached = (flags & SOF_MEM_FLAG_COHERENT) == 0; -#endif /* MODULE_DRIVER_HEAP_CACHED */ - if (mod_drv_heap) { -#ifdef MODULE_DRIVER_HEAP_CACHED - if (cached) { - /* - * Zephyr sys_heap stores metadata at start of each - * heap allocation. To ensure no allocated cached buffer - * overlaps the same cacheline with the metadata chunk, - * align both allocation start and size of allocation - * to cacheline. As cached and non-cached allocations are - * mixed, same rules need to be followed for both type of - * allocations. - */ - align = MAX(PLATFORM_DCACHE_ALIGN, align); - bytes = ALIGN_UP(bytes, align); - } -#endif /* MODULE_DRIVER_HEAP_CACHED */ - void *mem = sys_heap_aligned_alloc(mod_drv_heap, align, bytes); -#ifdef MODULE_DRIVER_HEAP_CACHED - if (cached) - return sys_cache_cached_ptr_get(mem); -#endif /* MODULE_DRIVER_HEAP_CACHED */ - return mem; - } else { - return rballoc_align(flags, bytes, align); + rfree(mod_drv_heap->heap.init_mem); + rfree(mod_drv_heap); } } -void *module_driver_heap_rmalloc(struct sys_heap *mod_drv_heap, uint32_t flags, size_t bytes) +void *user_stack_allocate(size_t stack_size, uint32_t options) { - if (mod_drv_heap) - return module_driver_heap_aligned_alloc(mod_drv_heap, flags, bytes, 0); - else - return rmalloc(flags, bytes); + return k_thread_stack_alloc(stack_size, options & K_USER); } -void *module_driver_heap_rzalloc(struct sys_heap *mod_drv_heap, uint32_t flags, size_t bytes) +int user_stack_free(void *p_stack) { - void *ptr; + if (!p_stack) + return 0; + return k_thread_stack_free(p_stack); +} - ptr = module_driver_heap_rmalloc(mod_drv_heap, flags, bytes); - if (ptr) - memset(ptr, 0, bytes); +int user_memory_attach_common_partition(struct k_mem_domain *dom) +{ + return k_mem_domain_add_partition(dom, &common_partition); +} - return ptr; +#ifdef CONFIG_SOF_USERSPACE_LL +int user_memory_attach_system_user_partition(struct k_mem_domain *dom) +{ + return k_mem_domain_add_partition(dom, &sysuser_partition); } -void module_driver_heap_free(struct sys_heap *mod_drv_heap, void *mem) +int user_access_to_mailbox(struct k_mem_domain *domain, k_tid_t thread_id) { - if (mod_drv_heap) { -#ifdef MODULE_DRIVER_HEAP_CACHED - if (is_cached(mem)) { - void *mem_uncached = sys_cache_uncached_ptr_get( - (__sparse_force void __sparse_cache *)mem); + struct k_mem_partition mem_partition; + int ret; + + /* + * Start with mailbox_swregs. This is aligned with mailbox.h + * implementation with uncached addressed used for register I/O. + */ + mem_partition.start = + (uintptr_t)sys_cache_uncached_ptr_get((void __sparse_cache *)MAILBOX_SW_REG_BASE); - sys_cache_data_invd_range(mem, - sys_heap_usable_size(mod_drv_heap, mem_uncached)); + BUILD_ASSERT(MAILBOX_SW_REG_SIZE == CONFIG_MMU_PAGE_SIZE); + mem_partition.size = CONFIG_MMU_PAGE_SIZE; + mem_partition.attr = K_MEM_PARTITION_P_RW_U_RW; - mem = mem_uncached; - } + ret = k_mem_domain_add_partition(domain, &mem_partition); + if (ret < 0) + return ret; + +#ifndef CONFIG_IPC_MAJOR_4 + /* + * Next mailbox_stream (not available in IPC4). Stream access is cached, + * so different mapping this time. + */ + mem_partition.start = + (uintptr_t)sys_cache_cached_ptr_get((void *)SRAM_STREAM_BASE); + BUILD_ASSERT(MAILBOX_STREAM_SIZE == CONFIG_MMU_PAGE_SIZE); + /* size and attr the same as for mailbox_swregs */ + + ret = k_mem_domain_add_partition(domain, &mem_partition); + if (ret < 0) + return ret; #endif - sys_heap_free(mod_drv_heap, mem); - } else { - rfree(mem); - } -} -void module_driver_heap_remove(struct sys_heap *mod_drv_heap) -{ - if (mod_drv_heap) { - rfree(mod_drv_heap->init_mem); - rfree(mod_drv_heap); - } -} + k_mem_domain_add_thread(domain, thread_id); -void *user_stack_allocate(size_t stack_size, uint32_t options) -{ - return (__sparse_force void __sparse_cache *) - k_thread_stack_alloc(stack_size, options & K_USER); + return 0; } -int user_stack_free(void *p_stack) +void user_grant_dai_access_all(struct k_thread *thread) { - if (!p_stack) - return 0; - return k_thread_stack_free((__sparse_force void *)p_stack); + const struct device **devices; + size_t count; + size_t i; + + devices = dai_get_device_list(&count); + + for (i = 0; i < count; i++) + k_thread_access_grant(thread, devices[i]); + + LOG_DBG("Granted DAI access to thread %p for %zu devices", thread, count); } -int user_memory_init_shared(k_tid_t thread_id, struct processing_module *mod) +void user_grant_dma_access_all(struct k_thread *thread) { - struct k_mem_domain *comp_dom = mod->user_ctx->comp_dom; - int ret; + const struct dma_info *info = dma_info_get(); + struct sof_dma *d; - ret = k_mem_domain_add_partition(comp_dom, &common_partition); - if (ret < 0) - return ret; - - return k_mem_domain_add_thread(comp_dom, thread_id); + for (d = info->dma_array; d < info->dma_array + info->num_dmas; d++) { + k_thread_access_grant(thread, d->z_dev); + LOG_DBG("Granted DMA device access: %s to thread %p", d->z_dev->name, thread); + } } +#endif /* CONFIG_SOF_USERSPACE_LL */ + #else /* CONFIG_USERSPACE */ void *user_stack_allocate(size_t stack_size, uint32_t options) { /* allocate stack - must be aligned and cached so a separate alloc */ stack_size = K_KERNEL_STACK_LEN(stack_size); - void *p_stack = (__sparse_force void __sparse_cache *) - rballoc_align(SOF_MEM_FLAG_USER, stack_size, Z_KERNEL_STACK_OBJ_ALIGN); + void *p_stack = rballoc_align(SOF_MEM_FLAG_USER, stack_size, Z_KERNEL_STACK_OBJ_ALIGN); return p_stack; } int user_stack_free(void *p_stack) { - rfree((__sparse_force void *)p_stack); + rfree(p_stack); return 0; } -void *module_driver_heap_rmalloc(struct sys_heap *mod_drv_heap, uint32_t flags, size_t bytes) -{ - return rmalloc(flags, bytes); -} - -void *module_driver_heap_aligned_alloc(struct sys_heap *mod_drv_heap, uint32_t flags, size_t bytes, - uint32_t align) -{ - return rballoc_align(flags, bytes, align); -} - -void *module_driver_heap_rzalloc(struct sys_heap *mod_drv_heap, uint32_t flags, size_t bytes) -{ - return rzalloc(flags, bytes); -} - -void module_driver_heap_free(struct sys_heap *mod_drv_heap, void *mem) -{ - rfree(mem); -} - -void module_driver_heap_remove(struct sys_heap *mod_drv_heap) +void module_driver_heap_remove(struct k_heap *mod_drv_heap) { } #endif /* CONFIG_USERSPACE */ diff --git a/zephyr/lib/vpage.c b/zephyr/lib/vpage.c new file mode 100644 index 000000000000..ce0da7b5ac97 --- /dev/null +++ b/zephyr/lib/vpage.c @@ -0,0 +1,341 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright(c) 2025 Intel Corporation. + * + * Author: Liam Girdwood <liam.r.girdwood@linux.intel.com> + */ + +#include <zephyr/init.h> +#include <zephyr/kernel.h> +#include <zephyr/logging/log.h> +#include <zephyr/sys/check.h> +#include <sof/lib/regions_mm.h> +#include <sof/lib/vpage.h> +#include <zephyr/drivers/mm/mm_drv_intel_adsp_mtl_tlb.h> + +LOG_MODULE_REGISTER(vpage, CONFIG_SOF_LOG_LEVEL); + +/* Simple Page Allocator. + * + * This allocator manages the allocation and deallocation of virtual memory + * pages from a predefined virtual memory region which is larger than the + * physical memory region. + * + * Both memory regions are divided into pages (usually 4kB) that are represented + * as blocks in a bitmap using the zephyr sys_mem_blocks API. The virtual block + * map tracks the allocation of virtual memory pages while the physical block + * map in the Zephyr MM driver tracks the allocation of physical memory pages. + */ + +/* max number of allocations */ +#define VPAGE_MAX_ALLOCS CONFIG_SOF_VPAGE_MAX_ALLOCS + +/* + * Virtual memory allocation element - tracks allocated virtual page id and size + */ +struct valloc_elem { + unsigned short pages; /* number of pages allocated in contiguous block */ + unsigned short vpage; /* virtual page number from start of region */ +}; + +/* + * Virtual page allocator context + * + * This structure holds all information about virtual memory pages including the + * number of free and total pages, the virtual memory region, the block + * allocator for virtual pages and the allocations. + */ +struct vpage_context { + struct k_mutex lock; + unsigned int free_pages; /* number of free pages */ + unsigned int total_pages; /* total number of pages */ + + /* Virtual memory region information */ + const struct sys_mm_drv_region *virtual_region; + struct sys_mem_blocks vpage_blocks; + sys_bitarray_t bitmap; + + /* allocation elements to track page id to allocation size */ + unsigned int num_elems_in_use; /* number of allocated elements in use*/ + struct valloc_elem velems[VPAGE_MAX_ALLOCS]; +}; + +/* uncache persistent across all cores */ +static struct vpage_context vpage_ctx; + +/** + * @brief Allocate and map virtual memory pages + * + * Allocates memory pages from the virtual page allocator. + * Maps physical memory pages to the virtual region as needed. + * + * @param pages Number of pages to allocate. + * @param ptr Pointer to store the address of allocated pages. + * @retval 0 if successful. + */ +static int vpages_alloc_and_map(unsigned int pages, void **ptr) +{ + void *vaddr; + int ret; + + /* check for valid pages and ptr */ + if (!ptr) + return -EINVAL; + + if (!pages) { + *ptr = NULL; + return 0; + } + + /* quick check for enough free pages */ + if (vpage_ctx.free_pages < pages) { + LOG_ERR("error: not enough free pages %u for requested pages %u", + vpage_ctx.free_pages, pages); + return -ENOMEM; + } + + /* check for allocation elements */ + if (vpage_ctx.num_elems_in_use >= VPAGE_MAX_ALLOCS) { + LOG_ERR("error: max allocation elements reached"); + return -ENOMEM; + } + + /* allocate virtual continuous blocks */ + ret = sys_mem_blocks_alloc_contiguous(&vpage_ctx.vpage_blocks, pages, &vaddr); + if (ret < 0) { + LOG_ERR("error: failed to allocate %u continuous virtual pages, free %u", + pages, vpage_ctx.free_pages); + return ret; + } + + /* map the virtual blocks in virtual region to free physical blocks */ + ret = sys_mm_drv_map_region_safe(vpage_ctx.virtual_region, vaddr, + 0, pages * CONFIG_MM_DRV_PAGE_SIZE, SYS_MM_MEM_PERM_RW); + if (ret < 0) { + LOG_ERR("error: failed to map virtual region %p to physical region %p, error %d", + vaddr, vpage_ctx.virtual_region->addr, ret); + sys_mem_blocks_free_contiguous(&vpage_ctx.vpage_blocks, vaddr, pages); + return ret; + } + + /* success update the free pages */ + vpage_ctx.free_pages -= pages; + + /* Elements are acquired densely, just use the next one */ + vpage_ctx.velems[vpage_ctx.num_elems_in_use].pages = pages; + vpage_ctx.velems[vpage_ctx.num_elems_in_use].vpage = + (POINTER_TO_UINT(vaddr) - + POINTER_TO_UINT(vpage_ctx.virtual_region->addr)) / + CONFIG_MM_DRV_PAGE_SIZE; + vpage_ctx.num_elems_in_use++; + + /* return the virtual address */ + *ptr = vaddr; + + return 0; +} + +/** + * @brief Allocate virtual memory pages + * + * Allocates virtual memory pages from the virtual page allocator. + * + * @param pages Number of pages (usually 4kB large) to allocate. + * @retval NULL on allocation failure. + */ +void *vpage_alloc(unsigned int pages) +{ + void *ptr = NULL; + int ret; + + k_mutex_lock(&vpage_ctx.lock, K_FOREVER); + ret = vpages_alloc_and_map(pages, &ptr); + k_mutex_unlock(&vpage_ctx.lock); + if (ret < 0) + LOG_ERR("vpage_alloc failed %d for %d pages, total %d free %d", + ret, pages, vpage_ctx.total_pages, vpage_ctx.free_pages); + else + LOG_INF("vpage_alloc ptr %p pages %u free %u/%u", ptr, pages, vpage_ctx.free_pages, + vpage_ctx.total_pages); + return ptr; +} + +/** + * @brief Free and unmap virtual memory pages + * + * Frees previously allocated virtual memory pages and unmaps them. + * + * @param ptr Pointer to the memory pages to free. + * @retval 0 if successful. + * @retval -EINVAL if ptr is invalid. + */ +static int vpages_free_and_unmap(uintptr_t *ptr) +{ + unsigned int alloc_idx, elem_idx; + unsigned int pages = 0; + int ret; + + /* check for valid ptr which must be page aligned */ + CHECKIF(!IS_ALIGNED(ptr, CONFIG_MM_DRV_PAGE_SIZE)) { + LOG_ERR("error: invalid non aligned page pointer %p", ptr); + return -EINVAL; + } + + alloc_idx = (POINTER_TO_UINT(ptr) - POINTER_TO_UINT(vpage_ctx.virtual_region->addr)) / + CONFIG_MM_DRV_PAGE_SIZE; + + /* find the allocation element */ + for (elem_idx = 0; elem_idx < VPAGE_MAX_ALLOCS; elem_idx++) { + if (vpage_ctx.velems[elem_idx].pages > 0 && + vpage_ctx.velems[elem_idx].vpage == alloc_idx) { + pages = vpage_ctx.velems[elem_idx].pages; + + LOG_DBG("found allocation element %d pages %u vpage %u for ptr %p", + elem_idx, vpage_ctx.velems[elem_idx].pages, + vpage_ctx.velems[elem_idx].vpage, ptr); + break; + } + } + + /* check we found allocation element */ + CHECKIF(!pages) { + LOG_ERR("error: invalid page pointer %p not found", ptr); + return -EINVAL; + } + + /* unmap the pages from virtual region */ + ret = sys_mm_drv_unmap_region((void *)ptr, pages * CONFIG_MM_DRV_PAGE_SIZE); + if (ret < 0) { + LOG_ERR("error: failed to unmap virtual region %p pages %u, error %d", + ptr, pages, ret); + return ret; + } + + /* free physical blocks */ + ret = sys_mem_blocks_free_contiguous(&vpage_ctx.vpage_blocks, ptr, pages); + if (ret < 0) { + LOG_ERR("error: failed to free %u continuous virtual page blocks at %p, error %d", + pages, ptr, ret); + return ret; + } + + /* move the last element over the released one, clear the last element */ + if (vpage_ctx.num_elems_in_use != elem_idx) + vpage_ctx.velems[elem_idx] = vpage_ctx.velems[vpage_ctx.num_elems_in_use]; + vpage_ctx.velems[vpage_ctx.num_elems_in_use].pages = 0; + vpage_ctx.velems[vpage_ctx.num_elems_in_use].vpage = 0; + vpage_ctx.num_elems_in_use--; + + /* success update the free pages */ + vpage_ctx.free_pages += pages; + + return ret; +} + +/** + * @brief Free virtual pages + * Frees previously allocated virtual memory pages and unmaps them. + * + * @param ptr + */ +void vpage_free(void *ptr) +{ + int ret; + + k_mutex_lock(&vpage_ctx.lock, K_FOREVER); + ret = vpages_free_and_unmap((uintptr_t *)ptr); + k_mutex_unlock(&vpage_ctx.lock); + + if (!ret) + LOG_INF("vptr %p free/total pages %d/%d", ptr, vpage_ctx.free_pages, + vpage_ctx.total_pages); +} + +/** + * @brief Initialize virtual page allocator + * + * Initializes a virtual page allocator that manages a virtual memory region + * using a page table and block structures. + * + * @retval 0 if successful. + * @retval -ENOMEM on creation failure. + */ +static int vpage_init(void) +{ + const struct sys_mm_drv_region *virtual_memory_regions; + const struct sys_mm_drv_region *region; + uint32_t *bundles = NULL; + unsigned int block_count, bitmap_num_bundles; + int ret; + + /* create the virtual memory region and add it to the system */ + size_t remaining_ram = L2_SRAM_BASE + L2_SRAM_SIZE - + (adsp_mm_get_unused_l2_start_aligned() + + CONFIG_SOF_ZEPHYR_VIRTUAL_HEAP_REGION_SIZE + + CONFIG_LIBRARY_REGION_SIZE); + + ret = adsp_add_virtual_memory_region(adsp_mm_get_unused_l2_start_aligned() + + CONFIG_SOF_ZEPHYR_VIRTUAL_HEAP_REGION_SIZE, + remaining_ram, VIRTUAL_REGION_VPAGES_ATTR); + if (ret) + return ret; + + k_mutex_init(&vpage_ctx.lock); + + /* now find the virtual region in all memory regions */ + virtual_memory_regions = sys_mm_drv_query_memory_regions(); + SYS_MM_DRV_MEMORY_REGION_FOREACH(virtual_memory_regions, region) { + if (region->attr == VIRTUAL_REGION_VPAGES_ATTR) { + vpage_ctx.virtual_region = region; + break; + } + } + sys_mm_drv_query_memory_regions_free(virtual_memory_regions); + + /* check for a valid region */ + if (!vpage_ctx.virtual_region) { + LOG_ERR("error: no valid virtual region found"); + k_panic(); + } + + block_count = region->size / CONFIG_MM_DRV_PAGE_SIZE; + if (block_count == 0) { + LOG_ERR("error: virtual region too small %zu", region->size); + k_panic(); + } + + vpage_ctx.total_pages = block_count; + vpage_ctx.free_pages = block_count; + vpage_ctx.num_elems_in_use = 0; + + /* bundles are uint32_t of bits */ + bitmap_num_bundles = SOF_DIV_ROUND_UP(block_count, sizeof(uint32_t) * 8); + + /* allocate memory for bitmap bundles */ + bundles = rzalloc(SOF_MEM_FLAG_KERNEL | SOF_MEM_FLAG_COHERENT, + bitmap_num_bundles * sizeof(uint32_t)); + if (!bundles) { + LOG_ERR("error: virtual region bitmap alloc failed"); + k_panic(); + } + + /* Fill allocators data based on config and virtual region data */ + vpage_ctx.vpage_blocks.info.num_blocks = block_count; + vpage_ctx.vpage_blocks.info.blk_sz_shift = ilog2(CONFIG_MM_DRV_PAGE_SIZE); + /* buffer is the start of the virtual memory region */ + vpage_ctx.vpage_blocks.buffer = (uint8_t *)vpage_ctx.virtual_region->addr; + + /* initialize bitmap */ + vpage_ctx.bitmap.num_bits = block_count; + vpage_ctx.bitmap.num_bundles = bitmap_num_bundles; + vpage_ctx.bitmap.bundles = bundles; + vpage_ctx.vpage_blocks.bitmap = &vpage_ctx.bitmap; + + LOG_INF("region %p size %#zx pages %u", + (void *)vpage_ctx.virtual_region->addr, + vpage_ctx.virtual_region->size, block_count); + + return 0; +} + +SYS_INIT(vpage_init, POST_KERNEL, 1); diff --git a/zephyr/lib/vregion.c b/zephyr/lib/vregion.c new file mode 100644 index 000000000000..84af0d0645e6 --- /dev/null +++ b/zephyr/lib/vregion.c @@ -0,0 +1,440 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright(c) 2025 Intel Corporation. + * + * Author: Liam Girdwood <liam.r.girdwood@linux.intel.com> + */ + +#include <zephyr/init.h> +#include <zephyr/kernel.h> +#include <zephyr/logging/log.h> +#include <sof/lib/vpage.h> +#include <sof/lib/vregion.h> +#include <rtos/alloc.h> +#include <sof/common.h> + +LOG_MODULE_REGISTER(vregion, CONFIG_SOF_LOG_LEVEL); + +/* + * Pre Allocated Contiguous Virtual Memory Region Allocator + * + * This allocator manages a pre-allocated virtual memory region that uses the + * virtual page allocator to allocate and free memory pages. + * + * It is designed for use cases where a contiguous virtual memory region + * is required, such as for batched allocation of audio pipelines and modules. + * + * New pipelines will create a new virtual region and will specify the size of the region + * which can be divided into multiple areas for different allocation lifetimes, permissions + * and sharing requirements. + * + * Advantages: + * + * 1) Contiguous virtual memory region for easier management and tracking of + * pipeline & DP module memory. i.e. we just need to track the vregion pointer. + * 2) Reduced fragmentation and better cache utilization by using a simple linear + * allocator for lifetime objects. + * + * Note: Software must pass in the size of the region areas at pipeline creation time. + */ + +/** + * @brief virtual region memory structure. + * + * This structure represents a virtual memory region, which includes + * information about the base address, size, and allocation status + * of the region. + * + * Currently the virtual region memory can be partitioned into two areas on + * page-aligned boundaries: + * + * 1. Interim Heap: An interim memory area used for multiple temporary + * allocations and frees over the lifetime of the audio processing pipeline. + * E.g. for module kcontrol derived allocations. + * + * 2. Lifetime Allocator: A simple incrementing allocator used for long-term + * static allocations that persist for the lifetime of the audio processing + * pipeline. This allocator compresses allocations for better cache + * utilization. + * + * More types can be added in the future. + * + * * TODO: Pipeline/module reset() could reset the dynamic heap. + */ + + /* linear heap used for lifetime allocations */ +struct vlinear_heap { + uint8_t *base; /* base address of linear allocator */ + uint8_t *ptr; /* current alloc pointer */ + size_t size; /* size of linear allocator in bytes */ + size_t used; /* used bytes in linear allocator */ + int free_count; /* number of frees - tuning only */ +}; + +/* zephyr k_heap for interim allocations. TODO: make lockless for improved performance */ +struct interim_heap { + struct k_heap heap; +}; + +/* Main vregion context, see above intro for more details. + * TODO: Add support to flag which heaps should have their contexts saved and restored. + */ +struct vregion { + /* region context */ + uint8_t *base; /* base address of entire region */ + size_t size; /* size of whole region in bytes */ + unsigned int pages; /* size of whole region in pages */ + struct k_mutex lock; /* protect vregion heaps and use-count */ + unsigned int use_count; + + /* interim heap */ + struct interim_heap interim; /* interim heap */ + + /* lifetime heap */ + struct vlinear_heap lifetime; /* lifetime linear heap */ +}; + +/** + * @brief Create a new virtual region instance. + * + * Create a new VIRTUAL REGION instance with specified static and dynamic + * sizes. Total size is their sum. + * + * @param[in] lifetime_size Size of the virtual region lifetime partition. + * @param[in] interim_size Size of the virtual region interim partition. + * @return struct vregion* Pointer to the new virtual region instance, or NULL on failure. + */ +struct vregion *vregion_create(size_t lifetime_size, size_t interim_size) +{ + struct vregion *vr; + unsigned int pages; + size_t total_size; + uint8_t *vregion_base; + + if (!lifetime_size || !interim_size) { + LOG_ERR("error: invalid vregion lifetime size %zu or interim size %zu", + lifetime_size, interim_size); + return NULL; + } + + /* + * Align up lifetime sizes and interim sizes to nearest page, the + * vregion structure is stored in lifetime area so account for its size too. + */ + lifetime_size += sizeof(*vr); + lifetime_size = ALIGN_UP(lifetime_size, CONFIG_MM_DRV_PAGE_SIZE); + interim_size = ALIGN_UP(interim_size, CONFIG_MM_DRV_PAGE_SIZE); + total_size = lifetime_size + interim_size; + + /* allocate pages for vregion */ + pages = total_size / CONFIG_MM_DRV_PAGE_SIZE; + vregion_base = vpage_alloc(pages); + if (!vregion_base) + return NULL; + + /* init vregion - place it at the start of the lifetime region */ + vr = (struct vregion *)(vregion_base + interim_size); + vr->base = vregion_base; + vr->size = total_size; + vr->pages = pages; + + /* set partition sizes */ + vr->interim.heap.heap.init_bytes = interim_size; + vr->lifetime.size = lifetime_size; + + /* set base addresses for partitions */ + vr->interim.heap.heap.init_mem = vr->base; + vr->lifetime.base = vr->base + interim_size; + + /* set alloc ptr addresses for lifetime linear partitions */ + vr->lifetime.ptr = vr->lifetime.base + + ALIGN_UP(sizeof(*vr), CONFIG_DCACHE_LINE_SIZE); /* skip vregion struct */ + vr->lifetime.used = ALIGN_UP(sizeof(*vr), CONFIG_DCACHE_LINE_SIZE); + + /* init interim heaps */ + k_heap_init(&vr->interim.heap, vr->interim.heap.heap.init_mem, interim_size); + + k_mutex_init(&vr->lock); + /* The creator is the first user */ + vr->use_count = 1; + + /* log the new vregion */ + LOG_INF("new at base %p size %#zx pages %u struct embedded at %p", + (void *)vr->base, total_size, pages, (void *)vr); + LOG_DBG(" interim size %#zx at %p", interim_size, (void *)vr->interim.heap.heap.init_mem); + LOG_DBG(" lifetime size %#zx at %p", lifetime_size, (void *)vr->lifetime.base); + + return vr; +} + +struct vregion *vregion_get(struct vregion *vr) +{ + if (!vr) + return NULL; + + k_mutex_lock(&vr->lock, K_FOREVER); + vr->use_count++; + k_mutex_unlock(&vr->lock); + + return vr; +} + +/** + * @brief Decrement virtual region's user count or destroy it. + * + * @param[in] vr Pointer to the virtual region instance to release. + * @return struct vregion* Pointer to the virtual region instance or NULL if it has been destroyed. + */ +struct vregion *vregion_put(struct vregion *vr) +{ + unsigned int use_count; + + if (!vr) + return NULL; + + k_mutex_lock(&vr->lock, K_FOREVER); + use_count = --vr->use_count; + k_mutex_unlock(&vr->lock); + + if (use_count) + return vr; + + /* Last user: nobody else can access the instance. */ + + /* log the vregion being destroyed */ + LOG_DBG("destroy %p size %#zx pages %u", (void *)vr->base, vr->size, vr->pages); + LOG_DBG(" lifetime used %zu free count %d", vr->lifetime.used, vr->lifetime.free_count); + vpage_free(vr->base); + + return NULL; +} + +/** + * @brief Allocate memory with alignment from the virtual region dynamic heap. + * + * @param[in] heap Pointer to the heap to use. + * @param[in] size Size of the allocation. + * @param[in] align Alignment of the allocation. + * @return void* Pointer to the allocated memory, or NULL on failure. + */ +static void *interim_alloc(struct interim_heap *heap, + size_t size, size_t align) +{ + void *ptr; + + ptr = k_heap_aligned_alloc(&heap->heap, align, size, K_NO_WAIT); + if (!ptr) + LOG_WRN("interim alloc failed for %d bytes align %d", + size, align); + + return ptr; +} + +/** + * @brief Free memory from the virtual region interim heap. + * + * @param[in] heap Pointer to the heap to use. + * @param[in] ptr Pointer to the memory to free. + */ +static void interim_free(struct interim_heap *heap, void *ptr) +{ + k_heap_free(&heap->heap, ptr); +} + +/** + * @brief Allocate memory from the virtual region lifetime allocator. + * + * @param[in] heap Pointer to the linear heap to use. + * @param[in] size Size of the allocation. + * @param[in] align Alignment of the allocation. + * + * @return void* Pointer to the allocated memory, or NULL on failure. + */ +static void *lifetime_alloc(struct vlinear_heap *heap, + size_t size, size_t align) +{ + void *ptr; + uint8_t *aligned_ptr; + size_t heap_obj_size; + + /* align heap pointer to alignment requested */ + aligned_ptr = UINT_TO_POINTER(ALIGN_UP(POINTER_TO_UINT(heap->ptr), align)); + + /* also align up size to D$ bytes if asked - allocation head and tail aligned */ + if (align == CONFIG_DCACHE_LINE_SIZE) + size = ALIGN_UP(size, CONFIG_DCACHE_LINE_SIZE); + + /* calculate new heap object size for object and alignment */ + heap_obj_size = aligned_ptr - heap->ptr + size; + + /* check we have enough lifetime space left */ + if (heap_obj_size + heap->used > heap->size) { + LOG_WRN("lifetime alloc failed for object %d heap %d bytes free %d", + size, heap_obj_size, heap->size - heap->used); + return NULL; + } + + /* allocate memory */ + ptr = aligned_ptr; + heap->ptr += heap_obj_size; + heap->used += heap_obj_size; + + return ptr; +} + +/** + * @brief Free memory from the virtual region lifetime allocator. + * + * @param[in] heap Pointer to the linear heap to use. + * @param[in] ptr Pointer to the memory to free. + */ +static void lifetime_free(struct vlinear_heap *heap, void *ptr) +{ + /* simple free, just increment free count, this is for tuning only */ + heap->free_count++; + + LOG_DBG("lifetime free %p count %d", ptr, heap->free_count); +} + +/** + * @brief Free memory from the virtual region. + * + * @param vr Pointer to the virtual region instance. + * @param ptr Pointer to the memory to free. + */ +void vregion_free(struct vregion *vr, void *ptr) +{ + if (!vr || !ptr) + return; + + k_mutex_lock(&vr->lock, K_FOREVER); + + if (sys_cache_is_ptr_uncached(ptr)) + ptr = sys_cache_cached_ptr_get(ptr); + + if (ptr >= (void *)vr->interim.heap.heap.init_mem && + ptr < (void *)((uint8_t *)vr->interim.heap.heap.init_mem + + vr->interim.heap.heap.init_bytes)) + /* pointer is in interim heap */ + interim_free(&vr->interim, ptr); + else if (ptr >= (void *)vr->lifetime.base && + ptr < (void *)(vr->lifetime.base + vr->lifetime.size)) + /* pointer is in lifetime heap */ + lifetime_free(&vr->lifetime, ptr); + else + LOG_ERR("error: vregion free invalid pointer %p", ptr); + + k_mutex_unlock(&vr->lock); +} +EXPORT_SYMBOL(vregion_free); + +/** + * @brief Allocate memory type from the virtual region. + * + * @param[in] vr Pointer to the virtual region instance. + * @param[in] type Memory type to allocate. + * @param[in] size Size of the allocation. + * @param[in] alignment Alignment of the allocation. + * + * @return void* Pointer to the allocated memory, or NULL on failure. + */ +void *vregion_alloc_align(struct vregion *vr, enum vregion_mem_type type, + size_t size, size_t alignment) +{ + void *p; + + if (!vr || !size) + return NULL; + + if (alignment < PLATFORM_DCACHE_ALIGN) + alignment = PLATFORM_DCACHE_ALIGN; + + k_mutex_lock(&vr->lock, K_FOREVER); + + switch (type) { + case VREGION_MEM_TYPE_INTERIM: + p = interim_alloc(&vr->interim, size, alignment); + break; + case VREGION_MEM_TYPE_LIFETIME: + p = lifetime_alloc(&vr->lifetime, size, alignment); + break; + default: + LOG_ERR("error: invalid memory type %d", type); + p = NULL; + } + + k_mutex_unlock(&vr->lock); + + return p; +} +EXPORT_SYMBOL(vregion_alloc_align); + +/** + * @brief Allocate memory from the virtual region. + * @param[in] vr Pointer to the virtual region instance. + * @param[in] type Memory type to allocate. + * @param[in] size Size of the allocation. + * @return void* Pointer to the allocated memory, or NULL on failure. + */ +void *vregion_alloc(struct vregion *vr, enum vregion_mem_type type, size_t size) +{ + return vregion_alloc_align(vr, type, size, 0); +} +EXPORT_SYMBOL(vregion_alloc); + +void *vregion_alloc_coherent(struct vregion *vr, enum vregion_mem_type type, size_t size) +{ + size = ALIGN_UP(size, CONFIG_DCACHE_LINE_SIZE); + + void *p = vregion_alloc_align(vr, type, size, CONFIG_DCACHE_LINE_SIZE); + + if (!p) + return NULL; + + sys_cache_data_invd_range(p, size); + + return sys_cache_uncached_ptr_get(p); +} + +void *vregion_alloc_coherent_align(struct vregion *vr, enum vregion_mem_type type, + size_t size, size_t alignment) +{ + if (alignment < CONFIG_DCACHE_LINE_SIZE) + alignment = CONFIG_DCACHE_LINE_SIZE; + size = ALIGN_UP(size, CONFIG_DCACHE_LINE_SIZE); + + void *p = vregion_alloc_align(vr, type, size, alignment); + + if (!p) + return NULL; + + sys_cache_data_invd_range(p, size); + + return sys_cache_uncached_ptr_get(p); +} + +/** + * @brief Log virtual region memory usage. + * + * @param[in] vr Pointer to the virtual region instance. + */ +void vregion_info(struct vregion *vr) +{ + if (!vr) + return; + + LOG_INF("base %p size %#zx pages %u", + (void *)vr->base, vr->size, vr->pages); + LOG_INF("lifetime used %#zx free count %d", + vr->lifetime.used, vr->lifetime.free_count); +} +EXPORT_SYMBOL(vregion_info); + +void vregion_mem_info(struct vregion *vr, size_t *size, uintptr_t *start) +{ + if (size) + *size = vr->size; + + if (start) + *start = (uintptr_t)vr->base; +} diff --git a/zephyr/sof_shell.c b/zephyr/sof_shell.c index f10a2c9275b5..9e90abe417bb 100644 --- a/zephyr/sof_shell.c +++ b/zephyr/sof_shell.c @@ -62,8 +62,8 @@ static int cmd_sof_module_heap_usage(const struct shell *sh, continue; usage = module_adapter_heap_usage(comp_mod(icd->cd), &hwm); - shell_print(sh, "comp id 0x%08x%9zu usage%9zu hwm %9zu max\tbytes", - icd->id, usage, hwm, comp_mod(icd->cd)->priv.cfg.heap_bytes); + shell_print(sh, "comp id 0x%08x%9zu usage%9zu hwm\tbytes", + icd->id, usage, hwm); } return 0; } diff --git a/zephyr/syscall/sof_dma.c b/zephyr/syscall/sof_dma.c new file mode 100644 index 000000000000..ed69ffc78423 --- /dev/null +++ b/zephyr/syscall/sof_dma.c @@ -0,0 +1,254 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// Copyright(c) 2025 Intel Corporation. + +#include <sof/lib/dma.h> +#include <zephyr/kernel.h> +#include <zephyr/internal/syscall_handler.h> + +#ifdef CONFIG_SOF_USERSPACE_INTERFACE_DMA + +static inline bool sof_dma_has_access(struct sof_dma *dma) +{ + /* + * use the Zephyr dma.h device handle to check calling + * thread has access to it + */ + return k_object_is_valid(dma->z_dev, K_OBJ_DRIVER_DMA); +} + +static inline bool sof_dma_is_valid(struct sof_dma *dma) +{ + const struct dma_info *info = dma_info_get(); + uintptr_t offset = (uintptr_t)dma - (uintptr_t)info->dma_array; + struct sof_dma *array_end = info->dma_array + info->num_dmas; + + if (!info->num_dmas) + return false; + + /* + * The 'dma' pointer is not trusted, so we must first ensure it + * points to a valid "struct sof_dma *" kernel object. + */ + if (dma < info->dma_array || dma >= array_end || offset % sizeof(struct sof_dma)) + return false; + + return sof_dma_has_access(dma); +} + +static inline struct sof_dma *z_vrfy_sof_dma_get(uint32_t dir, uint32_t cap, + uint32_t dev, uint32_t flags) +{ + struct sof_dma *dma = z_impl_sof_dma_get(dir, cap, dev, flags); + + /* + * note: Usually validation is done first, but here + * z_impl_sof_dma_get() is first called with unvalidated input + * arguments on purpose. This is done to reuse the existing SOF + * code to track DMA kernel objects. When called from + * user-space, we use existing functionality to look up the + * kernel object, but add an extra layer to check for access + * permission. + */ + if (dma) { + if (sof_dma_has_access(dma)) + return dma; + + /* no access, release reference on error */ + z_impl_sof_dma_put(dma); + } + + return NULL; +} +#include <zephyr/syscalls/sof_dma_get_mrsh.c> + +static inline void z_vrfy_sof_dma_put(struct sof_dma *dma) +{ + K_OOPS(!sof_dma_is_valid(dma)); + + z_impl_sof_dma_put(dma); +} +#include <zephyr/syscalls/sof_dma_put_mrsh.c> + +static inline int z_vrfy_sof_dma_get_attribute(struct sof_dma *dma, uint32_t type, uint32_t *value) +{ + K_OOPS(!sof_dma_is_valid(dma)); + K_OOPS(K_SYSCALL_MEMORY_WRITE(value, sizeof(*value))); + + return z_impl_sof_dma_get_attribute(dma, type, value); +} +#include <zephyr/syscalls/sof_dma_get_attribute_mrsh.c> + +static inline int z_vrfy_sof_dma_request_channel(struct sof_dma *dma, uint32_t stream_tag) +{ + K_OOPS(!sof_dma_is_valid(dma)); + + return z_impl_sof_dma_request_channel(dma, stream_tag); +} +#include <zephyr/syscalls/sof_dma_request_channel_mrsh.c> + +static inline void z_vrfy_sof_dma_release_channel(struct sof_dma *dma, + uint32_t channel) +{ + K_OOPS(!sof_dma_is_valid(dma)); + + return z_impl_sof_dma_release_channel(dma, channel); +} +#include <zephyr/syscalls/sof_dma_release_channel_mrsh.c> + +/** + * Creates a deep copy of the DMA transfer blocks in kernel address space, + * based on the DMA config description given as argument. + * + * All pointers in 'cfg' are validated for access permission, and if + * ok, contents is copied to a kernel side object. + * + * @arg cfg kernel object for DMA configuration that contains + * user-space pointers to DMA transfer objects + * @return array of kernel DMA block/transfer config objects + */ +static inline struct dma_block_config *deep_copy_dma_blk_cfg_list(struct dma_config *cfg) +{ + struct dma_block_config *kern_cfg = + rmalloc(0, sizeof(*kern_cfg) * cfg->block_count); + struct dma_block_config *kern_prev = NULL, *kern_next, *user_next; + int i = 0; + + if (!kern_cfg) + return NULL; + + for (user_next = cfg->head_block, kern_next = kern_cfg; + user_next; + user_next = user_next->next_block, kern_next++) { + if (++i > cfg->block_count) + goto err; + + if (k_usermode_from_copy(kern_next, user_next, sizeof(*kern_next))) + goto err; + + /* check access permissions for DMA src/dest memory */ + switch (cfg->channel_direction) { + case MEMORY_TO_MEMORY: + if (K_SYSCALL_MEMORY_WRITE((void *)kern_next->dest_address, + kern_next->block_size)) + goto err; + COMPILER_FALLTHROUGH; + case MEMORY_TO_PERIPHERAL: + case MEMORY_TO_HOST: + if (K_SYSCALL_MEMORY_READ((void *)kern_next->source_address, + kern_next->block_size)) + goto err; + break; + case PERIPHERAL_TO_MEMORY: + case HOST_TO_MEMORY: + if (K_SYSCALL_MEMORY_WRITE((void *)kern_next->dest_address, + kern_next->block_size)) + goto err; + break; + default: + goto err; + } + + if (kern_prev) + kern_prev->next_block = kern_next; + + kern_prev = kern_next; + } + + /* set transfer list to point to first kernel transfer config object */ + cfg->head_block = kern_cfg; + + return kern_cfg; + +err: + /* do not call K_OOPS until kernel memory is freed */ + rfree(kern_cfg); + return NULL; +} + +static inline int z_vrfy_sof_dma_config(struct sof_dma *dma, uint32_t channel, + struct dma_config *config) +{ + struct dma_block_config *blk_cfgs; + struct dma_config kern_cfg, user_cfg; + int ret; + + K_OOPS(!sof_dma_is_valid(dma)); + K_OOPS(k_usermode_from_copy(&user_cfg, config, sizeof(user_cfg))); + + /* use only DMA config attributes that are safe to use from user-space */ + kern_cfg.dma_slot = user_cfg.dma_slot; + kern_cfg.channel_direction = user_cfg.channel_direction; + kern_cfg.cyclic = user_cfg.cyclic; + kern_cfg.source_data_size = user_cfg.source_data_size; + kern_cfg.dest_data_size = user_cfg.dest_data_size; + kern_cfg.source_burst_length = user_cfg.source_burst_length; + kern_cfg.dest_burst_length = user_cfg.dest_burst_length; + kern_cfg.block_count = user_cfg.block_count; + kern_cfg.head_block = user_cfg.head_block; + + /* validate and copy transfer blocks to kernel */ + blk_cfgs = deep_copy_dma_blk_cfg_list(&kern_cfg); + K_OOPS(blk_cfgs == NULL); + + /* TODO: add checks for peripheral/host FIFO access? */ + + ret = z_impl_sof_dma_config(dma, channel, &kern_cfg); + + rfree(blk_cfgs); + + return ret; +} +#include <zephyr/syscalls/sof_dma_config_mrsh.c> + +static inline int z_vrfy_sof_dma_start(struct sof_dma *dma, uint32_t channel) +{ + K_OOPS(!sof_dma_is_valid(dma)); + + return z_impl_sof_dma_start(dma, channel); +} +#include <zephyr/syscalls/sof_dma_start_mrsh.c> + +static inline int z_vrfy_sof_dma_stop(struct sof_dma *dma, uint32_t channel) +{ + K_OOPS(!sof_dma_is_valid(dma)); + + return z_impl_sof_dma_stop(dma, channel); +} +#include <zephyr/syscalls/sof_dma_stop_mrsh.c> + +static inline int z_vrfy_sof_dma_get_status(struct sof_dma *dma, uint32_t channel, + struct dma_status *stat) +{ + K_OOPS(!sof_dma_is_valid(dma)); + K_OOPS(K_SYSCALL_MEMORY_WRITE(stat, sizeof(*stat))); + + return z_impl_sof_dma_get_status(dma, channel, stat); +} +#include <zephyr/syscalls/sof_dma_get_status_mrsh.c> + +static inline int z_vrfy_sof_dma_reload(struct sof_dma *dma, uint32_t channel, size_t size) +{ + K_OOPS(!sof_dma_is_valid(dma)); + + return z_impl_sof_dma_reload(dma, channel, size); +} +#include <zephyr/syscalls/sof_dma_reload_mrsh.c> + +static inline int z_vrfy_sof_dma_suspend(struct sof_dma *dma, uint32_t channel) +{ + K_OOPS(!sof_dma_is_valid(dma)); + + return z_impl_sof_dma_suspend(dma, channel); +} +#include <zephyr/syscalls/sof_dma_suspend_mrsh.c> + +static inline int z_vrfy_sof_dma_resume(struct sof_dma *dma, uint32_t channel) +{ + K_OOPS(!sof_dma_is_valid(dma)); + + return z_impl_sof_dma_resume(dma, channel); +} +#include <zephyr/syscalls/sof_dma_resume_mrsh.c> + +#endif /* CONFIG_SOF_USERSPACE_INTERFACE_DMA */ diff --git a/zephyr/test/CMakeLists.txt b/zephyr/test/CMakeLists.txt index e0718b8e0754..f548c98c5e73 100644 --- a/zephyr/test/CMakeLists.txt +++ b/zephyr/test/CMakeLists.txt @@ -1,5 +1,28 @@ -if (CONFIG_ACE_VERSION_1_5) - zephyr_library_sources_ifdef(CONFIG_SOF_BOOT_TEST - vmh.c - ) +if(CONFIG_SOF_BOOT_TEST) + zephyr_library_sources_ifdef(CONFIG_VIRTUAL_HEAP + vmh.c + ) + zephyr_library_sources_ifdef(CONFIG_SOF_VREGIONS + vpage.c vregion.c + ) + zephyr_library_sources_ifdef(CONFIG_USERSPACE + userspace/ksem.c + ) +endif() + +if(CONFIG_SOF_BOOT_TEST_STANDALONE AND CONFIG_SOF_USERSPACE_INTERFACE_DMA) + if(CONFIG_DT_HAS_INTEL_ADSP_HDA_HOST_IN_ENABLED) + zephyr_library_sources(userspace/test_intel_hda_dma.c) + endif() + if(CONFIG_DT_HAS_INTEL_ADSP_HDA_SSP_CAP_ENABLED) + zephyr_library_sources(userspace/test_intel_ssp_dai.c) + endif() +endif() + +if(CONFIG_SOF_BOOT_TEST_STANDALONE AND CONFIG_USERSPACE) + zephyr_library_sources(userspace/test_mailbox.c) +endif() + +if(CONFIG_SOF_BOOT_TEST_STANDALONE AND CONFIG_SOF_USERSPACE_LL) + zephyr_library_sources(userspace/test_ll_task.c) endif() diff --git a/zephyr/test/userspace/README.md b/zephyr/test/userspace/README.md new file mode 100644 index 000000000000..6c14300ee333 --- /dev/null +++ b/zephyr/test/userspace/README.md @@ -0,0 +1,48 @@ +User-space interface tests for Intel ADSP +----------------------------------------- + +This folder contains multiple tests to exercise Intel DSP device interfaces +from a user-space Zephyr thread. + +Available tests: +- test_intel_hda_dma.c + - Test Intel HDA DMA host interface from a userspace + Zephyr thread. Use cavstool.py as host runner. +- test_intel_ssp_dai.c + - Test Zephyr DAI interface, together with SOF DMA + wrapper from a user thread. Mimics the call flows done in + sof/src/audio/dai-zephyr.c. Use cavstool.py as host runner. +- test_ll_task.c + - Test Low-Latency (LL) scheduler in user-space mode. Creates + a user-space LL scheduler, and uses it to create and run tasks. + - Tests functionality used by SOF audio pipeline framework to + create tasks for audio pipeline logic. +- test_mailbox.c + - Test use of sof/mailbox.h interface from a Zephyr user thread. + +Building for Intel Panther Lake: +./scripts/xtensa-build-zephyr.py --cmake-args=-DCONFIG_SOF_BOOT_TEST_STANDALONE=y \ + --cmake-args=-DCONFIG_SOF_USERSPACE_INTERFACE_DMA=y \ + --cmake-args=-DCONFIG_SOF_USERSPACE_LL=y \ + -o app/overlays/ptl/userspace_overlay.conf -o app/winconsole_overlay.conf ptl + +Running test: +- Copy resulting firmware (sof-ptl.ri) to device under test. +- Boot and run the test with cavstool.py: + sudo ./cavstool.py sof-ptl.ri +- Test results printed to cavstool.py + +Running test on QEMU (dc233c MMU): +- Tests can also be built and run locally using Zephyr's QEMU simulator. +- First, build the test application using `xtensa-build-zephyr.py`: + ./scripts/xtensa-build-zephyr.py qemu_xtensa_mmu --cmake-args=-DCONFIG_SOF_BOOT_TEST_STANDALONE=y \ + --cmake-args=-DCONFIG_SOF_USERSPACE_INTERFACE_DMA=y --cmake-args=-DCONFIG_SOF_USERSPACE_LL=y +- Once built, run the test in QEMU: + west build -d build-qemu_xtensa_mmu -t run + +References to related assets in Zephyr codebase: +- cavstool.py + - zephyr/soc/intel/intel_adsp/tools/cavstool.py +- HD DMA tests in Zephyr + - zephyr/tests/boards/intel_adsp/hda/ + - larger set in kernel space, using DMA interface directly without SOF dependencies diff --git a/zephyr/test/userspace/ksem.c b/zephyr/test/userspace/ksem.c new file mode 100644 index 000000000000..cb167191f956 --- /dev/null +++ b/zephyr/test/userspace/ksem.c @@ -0,0 +1,118 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright(c) 2025 Intel Corporation. + */ + +#include <sof/boot_test.h> + +#include <zephyr/kernel.h> +#include <zephyr/ztest.h> +#include <zephyr/logging/log.h> + +LOG_MODULE_DECLARE(sof_boot_test, LOG_LEVEL_DBG); + +#define USER_STACKSIZE 2048 + +static struct k_thread user_thread; +static K_THREAD_STACK_DEFINE(user_stack, USER_STACKSIZE); +K_SEM_DEFINE(user_sem, 0, 1); + +static void user_function(void *p1, void *p2, void *p3) +{ + __ASSERT(k_is_user_context(), "isn't user"); + LOG_INF("SOF thread %s (%s)", + k_is_user_context() ? "UserSpace!" : "privileged mode.", + CONFIG_BOARD_TARGET); +} + +static void user_sem_function(void *p1, void *p2, void *p3) +{ + __ASSERT(k_is_user_context(), "isn't user"); + LOG_INF("SOF thread %s (%s)", + k_is_user_context() ? "UserSpace!" : "privileged mode.", + CONFIG_BOARD_TARGET); + k_sem_give(&user_sem); +} + +static void test_user_thread(void) +{ + k_thread_create(&user_thread, user_stack, USER_STACKSIZE, + user_function, NULL, NULL, NULL, + -1, K_USER, K_MSEC(0)); + k_thread_join(&user_thread, K_FOREVER); +} + +static void test_user_thread_with_sem(void) +{ + /* Start in 10ms to have time to grant the thread access to the semaphore */ + k_thread_create(&user_thread, user_stack, USER_STACKSIZE, + user_sem_function, NULL, NULL, NULL, + -1, K_USER, K_MSEC(10)); + k_thread_access_grant(&user_thread, &user_sem); + k_sem_take(&user_sem, K_FOREVER); + k_thread_join(&user_thread, K_FOREVER); +} + +ZTEST(sof_boot, user_space) +{ + test_user_thread(); + test_user_thread_with_sem(); +} + +#include <zephyr/sys/sem.h> +#include <zephyr/app_memory/mem_domain.h> + +struct sem_mem { + struct sys_sem sem1; + struct sys_sem sem2; + uint8_t reserved[4096 - 2 * sizeof(struct sys_sem)]; +}; + +static struct sem_mem simple_sem __attribute__((aligned(4096))); +static struct k_mem_domain dp_mdom; + +static void sys_sem_function(void *p1, void *p2, void *p3) +{ + __ASSERT(k_is_user_context(), "isn't user"); + /* This is the goal, but it hangs with this disabled too */ + sys_sem_give(&simple_sem.sem1); + int ret = sys_sem_take(&simple_sem.sem2, K_MSEC(20)); + + LOG_INF("SOF thread %s (%s) sem %p: %d", + k_is_user_context() ? "UserSpace!" : "privileged mode.", + CONFIG_BOARD_TARGET, &simple_sem, ret); +} + +static void test_user_thread_sys_sem(void) +{ + struct k_mem_partition mpart = { + .start = (uintptr_t)&simple_sem, + .size = 4096, + .attr = K_MEM_PARTITION_P_RW_U_RW/* | XTENSA_MMU_CACHED_WB*/, + }; + + k_mem_domain_init(&dp_mdom, 0, NULL); + sys_sem_init(&simple_sem.sem1, 0, 1); + sys_sem_init(&simple_sem.sem2, 0, 1); + + k_thread_create(&user_thread, user_stack, USER_STACKSIZE, + sys_sem_function, NULL, NULL, NULL, + -1, K_USER, K_FOREVER); + k_mem_domain_add_partition(&dp_mdom, &mpart); + k_mem_domain_add_thread(&dp_mdom, &user_thread); + + k_thread_start(&user_thread); + + /* This is what doesn't work: enabling this line crashes the DSP */ + zassert_ok(sys_sem_take(&simple_sem.sem1, K_MSEC(20))); + + sys_sem_give(&simple_sem.sem2); + + k_thread_join(&user_thread, K_FOREVER); + k_mem_domain_remove_partition(&dp_mdom, &mpart); +} + +ZTEST(sof_boot, test_sys_sem) +{ + test_user_thread_sys_sem(); +} diff --git a/zephyr/test/userspace/test_intel_hda_dma.c b/zephyr/test/userspace/test_intel_hda_dma.c new file mode 100644 index 000000000000..dd54e6d85f2a --- /dev/null +++ b/zephyr/test/userspace/test_intel_hda_dma.c @@ -0,0 +1,242 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright(c) 2025 Intel Corporation. + */ + +/* + * Test case for user-space use of the SOF DMA interface. The tests + * transfer data from DSP to host using the host HD DMA instance. + * The test uses the cavstool.py infrastructure to perform host side + * programming of the HDA DMA, and to verify the transferred data. + * + * This test is based on the Zephyr kernel tests for Intel HD DMA + * driver (zephyr/tests/boards/intel_adsp/hda/) written by Tom + * Burdick. This test performs only subset of flows. Driver testing + * should primarily done with the Zephyr kernel tests and this test + * is solely to test the added syscall layer added in SOF. + */ + +#include <sof/boot_test.h> + +#include <zephyr/kernel.h> +#include <zephyr/ztest.h> +#include <zephyr/logging/log.h> +#include <sof/lib/dma.h> + +LOG_MODULE_DECLARE(sof_boot_test, LOG_LEVEL_DBG); + +#define USER_STACKSIZE 2048 +#define TEST_BUF_SIZE 256 +#define TEST_CHANNEL 0 +#define HD_DMA_BUF_ALIGN 128 + +static struct k_thread user_thread; +static K_THREAD_STACK_DEFINE(user_stack, USER_STACKSIZE); + +K_SEM_DEFINE(ipc_sem_wake_user, 0, 1); +K_SEM_DEFINE(ipc_sem_wake_kernel, 0, 1); + +static void intel_hda_dma_user(void *p1, void *p2, void *p3) +{ + uint8_t data_buf[TEST_BUF_SIZE] __aligned(HD_DMA_BUF_ALIGN); + struct dma_block_config dma_block_cfg; + struct dma_config config; + struct dma_status stat; + struct sof_dma *dma; + uint32_t addr_align; + int err, channel; + + zassert_true(k_is_user_context(), "isn't user"); + + LOG_INF("SOF thread %s (%s)", + k_is_user_context() ? "UserSpace!" : "privileged mode.", + CONFIG_BOARD_TARGET); + + /* + * note: this gets a pointer to kernel memory this thread + * cannot access + */ + dma = sof_dma_get(SOF_DMA_DIR_LMEM_TO_HMEM, 0, SOF_DMA_DEV_HOST, SOF_DMA_ACCESS_SHARED); + + k_sem_take(&ipc_sem_wake_user, K_FOREVER); + LOG_INF("configure DMA channel"); + + channel = sof_dma_request_channel(dma, TEST_CHANNEL); + zassert_equal(channel, TEST_CHANNEL); + LOG_INF("sof_dma_request_channel: ret %d", channel); + + err = sof_dma_get_attribute(dma, DMA_ATTR_BUFFER_ADDRESS_ALIGNMENT, + &addr_align); + zassert_equal(err, 0); + zassert_true(addr_align == HD_DMA_BUF_ALIGN); + + /* set up a DMA transfer */ + memset(&dma_block_cfg, 0, sizeof(dma_block_cfg)); + dma_block_cfg.dest_address = 0; /* host fifo */ + dma_block_cfg.source_address = (uintptr_t)data_buf; + dma_block_cfg.block_size = sizeof(data_buf); + + /* + * fill data ramp, this payload is expected by host test + * harness + */ + for (uint32_t i = 0; i < TEST_BUF_SIZE; i++) { + data_buf[i] = i & 0xff; + } + sys_cache_data_flush_range(data_buf, sizeof(data_buf)); + + memset(&config, 0, sizeof(config)); + config.channel_direction = MEMORY_TO_HOST; + config.block_count = 1; + config.head_block = &dma_block_cfg; + + err = sof_dma_config(dma, channel, &config); + zassert_equal(err, 0); + LOG_INF("sof_dma_config: success"); + + err = sof_dma_start(dma, channel); + zassert_equal(err, 0); + LOG_INF("sof_dma_start: ch %d", channel); + + k_sem_give(&ipc_sem_wake_kernel); + LOG_INF("setup ready, waiting for kernel to configure host-side of the test"); + k_sem_take(&ipc_sem_wake_user, K_FOREVER); + LOG_INF("start DMA test and transfer data"); + + err = sof_dma_get_status(dma, channel, &stat); + zassert_equal(err, 0); + LOG_INF("sof_dma_get_status start: pend %u free %u", + stat.pending_length, stat.free); + + err = sof_dma_reload(dma, channel, sizeof(data_buf)); + zassert_equal(err, 0); + + for (int i = 0; stat.pending_length < TEST_BUF_SIZE; i++) { + err = sof_dma_get_status(dma, channel, &stat); + zassert_equal(err, 0); + LOG_INF("sof_dma_get_status %d: pend %u free %u", i, + stat.pending_length, stat.free); + + zassert_true(i < 100, "DMA transfer completes in 100usec"); + + /* let DMA transfer complete */ + k_sleep(K_USEC(1)); + } + + err = sof_dma_get_status(dma, channel, &stat); + zassert_equal(err, 0); + LOG_INF("sof_dma_get_status end: pend %u free %u", + stat.pending_length, stat.free); + + LOG_INF("transfer done, asking host to validate output"); + k_sem_give(&ipc_sem_wake_kernel); + k_sem_take(&ipc_sem_wake_user, K_FOREVER); + LOG_INF("test done, cleaning up resources"); + + err = sof_dma_stop(dma, channel); + zassert_equal(err, 0); + + sof_dma_release_channel(dma, channel); + + sof_dma_put(dma); + + LOG_INF("DMA stopped and resources freed"); + + k_sem_give(&ipc_sem_wake_kernel); +} + +#define IPC_TIMEOUT K_MSEC(1500) +#define DMA_BUF_SIZE 256 + +#define ALIGNMENT DMA_BUF_ADDR_ALIGNMENT(DT_NODELABEL(hda_host_in)) +static __aligned(ALIGNMENT) uint8_t dma_buf[DMA_BUF_SIZE]; + +#include <intel_adsp_hda.h> +#include <../../../../zephyr/tests/boards/intel_adsp/hda/src/tests.h> + +static int msg_validate_res; + +static bool ipc_message(const struct device *dev, void *arg, + uint32_t data, uint32_t ext_data) +{ + LOG_DBG("HDA message received, data %u, ext_data %u", data, ext_data); + msg_validate_res = ext_data; + return true; +} + +static void intel_hda_dma_kernel(void) +{ + const struct device *dma; + + LOG_INF("run %s with buffer at address %p, size %d", + __func__, (void *)dma_buf, DMA_BUF_SIZE); + + intel_adsp_ipc_set_message_handler(INTEL_ADSP_IPC_HOST_DEV, ipc_message, NULL); + + k_thread_create(&user_thread, user_stack, USER_STACKSIZE, + intel_hda_dma_user, NULL, NULL, NULL, + -1, K_USER, K_FOREVER); + + k_thread_access_grant(&user_thread, &ipc_sem_wake_user); + k_thread_access_grant(&user_thread, &ipc_sem_wake_kernel); + + dma = DEVICE_DT_GET(DT_NODELABEL(hda_host_in)); + k_thread_access_grant(&user_thread, dma); + + hda_ipc_msg(INTEL_ADSP_IPC_HOST_DEV, IPCCMD_HDA_RESET, + TEST_CHANNEL, IPC_TIMEOUT); + + hda_ipc_msg(INTEL_ADSP_IPC_HOST_DEV, IPCCMD_HDA_CONFIG, + TEST_CHANNEL | (DMA_BUF_SIZE << 8), IPC_TIMEOUT); + + k_thread_start(&user_thread); + + LOG_INF("user started, waiting for it to be ready"); + + k_sem_give(&ipc_sem_wake_user); + k_sem_take(&ipc_sem_wake_kernel, K_FOREVER); + + LOG_INF("user ready, starting HDA test"); + + hda_ipc_msg(INTEL_ADSP_IPC_HOST_DEV, IPCCMD_HDA_START, TEST_CHANNEL, IPC_TIMEOUT); + + k_sem_give(&ipc_sem_wake_user); + k_sem_take(&ipc_sem_wake_kernel, K_FOREVER); + + LOG_INF("transfer done, validating results"); + + hda_ipc_msg(INTEL_ADSP_IPC_HOST_DEV, IPCCMD_HDA_VALIDATE, TEST_CHANNEL, + IPC_TIMEOUT); + + hda_dump_regs(HOST_OUT, HDA_REGBLOCK_SIZE, TEST_CHANNEL, "host reset"); + + k_sem_give(&ipc_sem_wake_user); + k_sem_take(&ipc_sem_wake_kernel, K_FOREVER); + + LOG_INF("test done, terminate user thread"); + + k_thread_join(&user_thread, K_FOREVER); + + zassert_true(msg_validate_res == 1, "DMA transferred data invalid payload"); +} + +ZTEST(userspace_intel_hda_dma, dma_mem_to_host) +{ + intel_hda_dma_kernel(); + + ztest_test_pass(); +} + +ZTEST_SUITE(userspace_intel_hda_dma, NULL, NULL, NULL, NULL, NULL); + +/** + * SOF main has booted up and IPC handling is stopped. + * Run test suites with ztest_run_all. + */ +static int run_tests(void) +{ + ztest_run_test_suite(userspace_intel_hda_dma, false, 1, 1, NULL); + return 0; +} + +SYS_INIT(run_tests, APPLICATION, 99); diff --git a/zephyr/test/userspace/test_intel_ssp_dai.c b/zephyr/test/userspace/test_intel_ssp_dai.c new file mode 100644 index 000000000000..6c700c3839bb --- /dev/null +++ b/zephyr/test/userspace/test_intel_ssp_dai.c @@ -0,0 +1,348 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright(c) 2025 Intel Corporation. + */ + +/* + * Test case for user-space use of the SOF DMA interface. The tests + * covers all key interfaces of DMA and DAI, testing their use from + * a user-space threads. Due to hardware constraints, the actual DMA + * transfers cannot be tested as this would require cooperation with a + * host entity that would manage the HDA link DMA in sync with the DP + * test case. Test does check all programming can be done and no + * errors are raised from the drivers. Valid configuration blobs are + * passed, to fully exercise the drivers interfaces. + + * Requirements for host side test execution environment: + * - I2S offload must be enabled on host side (HDAMLI2S) to allow + * the DAI driver to access hardware registers. + */ + +#include <sof/boot_test.h> + +#include <zephyr/kernel.h> +#include <zephyr/ztest.h> +#include <zephyr/logging/log.h> +#include <sof/lib/dai.h> +#include <sof/lib/uuid.h> +#include <sof/lib/dma.h> +#include <sof/audio/component_ext.h> + +#include "../../../src/audio/copier/dai_copier.h" +#include "../../../../zephyr/drivers/dai/intel/ssp/ssp.h" + +LOG_MODULE_DECLARE(sof_boot_test, LOG_LEVEL_DBG); + +#define USER_STACKSIZE 8192 +#define HD_DMA_BUF_ALIGN 128 +#define TEST_BUF_SIZE (2*HD_DMA_BUF_ALIGN) +#define TEST_CHANNEL_OUT 3 +#define TEST_CHANNEL_IN 4 +#define SSP_DEVICE ssp00 + +static struct k_thread user_thread; +static K_THREAD_STACK_DEFINE(user_stack, USER_STACKSIZE); + +static K_SEM_DEFINE(ipc_sem_wake_user, 0, 1); +static K_SEM_DEFINE(ipc_sem_wake_kernel, 0, 1); + +static int call_dai_set_ssp_v3_config_48k_2ch_32bit(const struct device *dai_dev) +{ + union hdalink_cfg link_cfg; + const uint8_t stream_id = 0; + + link_cfg.full = 0; + link_cfg.part.dir = DAI_DIR_TX; + link_cfg.part.stream = stream_id; + + struct dai_config common_config = { + .type = DAI_INTEL_SSP_NHLT, + .dai_index = 0, + .channels = 2, + .rate = 48000, + .format = DAI_CBC_CFC | DAI_PROTO_I2S | DAI_INVERSION_NB_NF, + .options = 0, + .word_size = 32, + .block_size = 0, + .link_config = link_cfg.full, + .tdm_slot_group = 0 + }; + + /* + * There are no suitable struct definitions to create these + * config objects, so we have to define a custom type that + * includes the common header, a single MDIV entry, one TLV + * entry and the link_ctl struct. These are normally part of + * ACPI NHLT and can be alternatively created with alsa-utils + * nhlt plugin. + */ + struct { + struct dai_intel_ipc4_ssp_configuration_blob_ver_3_0 b; + uint32_t mdivr0; + uint32_t type; + uint32_t size; + struct ssp_intel_link_ctl link_ctl; + } __packed blob30; + + memset(&blob30, 0, sizeof(blob30)); + /* DAI config blob header for SSP v3 */ + blob30.b.version = SSP_BLOB_VER_3_0; + blob30.b.size = sizeof(blob30); + /* I2S config matching sof-ptl-nocodec.tplg (32bit/48kHz/2ch) */ + blob30.b.i2s_ssp_config.ssc0 = 0x81d0077f; + blob30.b.i2s_ssp_config.ssc1 = 0xd0400004; + blob30.b.i2s_ssp_config.sscto = 0; + blob30.b.i2s_ssp_config.sspsp = 0x02200000; + blob30.b.i2s_ssp_config.ssc2 = 0x00004002; + blob30.b.i2s_ssp_config.sspsp2 = 0; + blob30.b.i2s_ssp_config.ssc3 = 0; + blob30.b.i2s_ssp_config.ssioc = 0x00000020; + /* clock control settings matching sof-ptl-nocodec.tplg */ + blob30.b.i2s_mclk_control.mdivctlr = 0x00010001; + blob30.b.i2s_mclk_control.mdivrcnt = 1; + /* variable-size section of clock control, one entry for mdivr */ + blob30.mdivr0 = 0xfff; + /* aux-data with one TLV entry for link-clk-source */ + blob30.type = SSP_LINK_CLK_SOURCE; + blob30.size = sizeof(struct ssp_intel_link_ctl); + blob30.link_ctl.clock_source = 1; + + return dai_config_set(dai_dev, &common_config, &blob30, sizeof(blob30)); +} + +static void intel_ssp_dai_user(void *p1, void *p2, void *p3) +{ + struct dma_block_config dma_block_cfg; + struct sof_dma *dma_in, *dma_out; + uint8_t data_buf_out[TEST_BUF_SIZE] __aligned(HD_DMA_BUF_ALIGN); + uint8_t data_buf_in[TEST_BUF_SIZE] __aligned(HD_DMA_BUF_ALIGN); + uint32_t addr_align = 0; + const struct device *dai_dev; + struct dai_properties dai_props; + struct dma_config config; + struct dma_status stat; + int err, channel_out, channel_in; + + zassert_true(k_is_user_context()); + + /* + * note: this gets a pointer to kernel memory this thread + * cannot access + */ + dma_in = sof_dma_get(SOF_DMA_DIR_DEV_TO_MEM, 0, SOF_DMA_DEV_SSP, SOF_DMA_ACCESS_SHARED); + dma_out = sof_dma_get(SOF_DMA_DIR_MEM_TO_DEV, 0, SOF_DMA_DEV_SSP, SOF_DMA_ACCESS_SHARED); + + k_sem_take(&ipc_sem_wake_user, K_FOREVER); + + LOG_INF("create a DAI device for %s", STRINGIFY(SSP_DEVICE)); + + dai_dev = DEVICE_DT_GET(DT_NODELABEL(SSP_DEVICE)); + err = dai_probe(dai_dev); + zassert_equal(err, 0); + + channel_out = sof_dma_request_channel(dma_out, TEST_CHANNEL_OUT); + zassert_equal(channel_out, TEST_CHANNEL_OUT); + LOG_INF("sof_dma_request_channel (out): ret ch %d", channel_out); + channel_in = sof_dma_request_channel(dma_in, TEST_CHANNEL_IN); + zassert_equal(channel_in, TEST_CHANNEL_IN); + LOG_INF("sof_dma_request_channel (in): ret ch %d", channel_in); + + err = sof_dma_get_attribute(dma_out, DMA_ATTR_BUFFER_ADDRESS_ALIGNMENT, + &addr_align); + zassert_equal(err, 0); + zassert_true(addr_align == HD_DMA_BUF_ALIGN); + + /* set up a DMA transfer */ + memset(&dma_block_cfg, 0, sizeof(dma_block_cfg)); + + err = dai_get_properties_copy(dai_dev, DAI_DIR_TX, 0, &dai_props); + zassert_equal(err, 0); + + LOG_INF("dai_get_properties_copy (TX), ret %d, fifo %u", err, dai_props.fifo_address); + + dma_block_cfg.dest_address = dai_props.fifo_address; /* dai fifo */ + dma_block_cfg.source_address = (uintptr_t)data_buf_out; + dma_block_cfg.block_size = sizeof(data_buf_out); + + memset(&config, 0, sizeof(config)); + config.channel_direction = MEMORY_TO_PERIPHERAL; + config.block_count = 1; + config.head_block = &dma_block_cfg; + config.source_data_size = 4; + config.dest_data_size = 4; + + err = sof_dma_config(dma_out, channel_out, &config); + zassert_equal(err, 0); + + err = dai_get_properties_copy(dai_dev, DAI_DIR_RX, 0, &dai_props); + zassert_equal(err, 0); + LOG_INF("dai_get_properties_copy (RX), ret %d, fifo %u", err, dai_props.fifo_address); + + dma_block_cfg.dest_address = (uintptr_t)data_buf_in; + dma_block_cfg.source_address = dai_props.fifo_address; /* dai fifo */ + dma_block_cfg.block_size = sizeof(data_buf_in); + + config.channel_direction = PERIPHERAL_TO_MEMORY; + config.block_count = 1; + + err = sof_dma_config(dma_in, channel_in, &config); + zassert_equal(err, 0, "dma-config error"); + + err = call_dai_set_ssp_v3_config_48k_2ch_32bit(dai_dev); + zassert_equal(err, 0); + LOG_INF("DAI configuration ready, sync with kernel on start"); + + k_sem_give(&ipc_sem_wake_kernel); + k_sem_take(&ipc_sem_wake_user, K_FOREVER); + LOG_INF("start DMA test and transfer data"); + + err = dai_trigger(dai_dev, DAI_DIR_RX, DAI_TRIGGER_PRE_START); + zassert_equal(err, 0); + + err = dai_trigger(dai_dev, DAI_DIR_TX, DAI_TRIGGER_PRE_START); + zassert_equal(err, 0); + LOG_INF("dai_trigger RX+TX PRE_START done"); + + err = sof_dma_get_status(dma_in, channel_in, &stat); + zassert_equal(err, 0); + LOG_INF("sof_dma_get_status ( dma_in/start):\tpend %3u free %3u", + stat.pending_length, stat.free); + + err = sof_dma_get_status(dma_out, channel_out, &stat); + zassert_equal(err, 0); + LOG_INF("sof_dma_get_status (dma_out/start):\tpend %3u free %3u", + stat.pending_length, stat.free); + + err = sof_dma_start(dma_in, channel_in); + zassert_equal(err, 0); + + err = sof_dma_start(dma_out, channel_out); + zassert_equal(err, 0); + + err = dai_trigger(dai_dev, DAI_DIR_RX, DAI_TRIGGER_START); + zassert_equal(err, 0); + + err = dai_trigger(dai_dev, DAI_DIR_TX, DAI_TRIGGER_START); + zassert_equal(err, 0); + LOG_INF("DMAs and DAIs started."); + + k_sleep(K_USEC(10)); + + err = sof_dma_get_status(dma_in, channel_in, &stat); + zassert_equal(err, 0); + /* after start, there should be at least some free data */ + zassert_true(stat.free > 0); + zassert_true(stat.pending_length < TEST_BUF_SIZE); + LOG_INF("sof_dma_get_status ( dma_in/run):\tpend %3u free %3u", + stat.pending_length, stat.free); + + err = sof_dma_reload(dma_in, channel_in, sizeof(data_buf_in)); + zassert_equal(err, 0); + + err = sof_dma_get_status(dma_in, channel_in, &stat); + zassert_equal(err, 0); + /* after reload, there should be at least some data pending */ + zassert_true(stat.free < TEST_BUF_SIZE); + zassert_true(stat.pending_length > 0); + + err = sof_dma_get_status(dma_out, channel_out, &stat); + zassert_equal(err, 0); + LOG_INF("sof_dma_get_status (dma_out/run):\tpend %3u free %3u", + stat.pending_length, stat.free); + zassert_true(stat.free < TEST_BUF_SIZE); + zassert_true(stat.pending_length > 0); + + LOG_INF("DMA setup done, asking host to clean up "); + k_sem_give(&ipc_sem_wake_kernel); + k_sem_take(&ipc_sem_wake_user, K_FOREVER); + LOG_INF("Cleaning up resources"); + + err = sof_dma_stop(dma_out, channel_out); + zassert_equal(err, 0); + + err = sof_dma_stop(dma_in, channel_in); + zassert_equal(err, 0); + + err = dai_trigger(dai_dev, DAI_DIR_TX, DAI_TRIGGER_STOP); + zassert_equal(err, 0); + + err = dai_trigger(dai_dev, DAI_DIR_RX, DAI_TRIGGER_STOP); + zassert_equal(err, 0); + + sof_dma_release_channel(dma_out, channel_out); + + sof_dma_release_channel(dma_in, channel_in); + + err = dai_remove(dai_dev); + zassert_equal(err, 0); + + sof_dma_put(dma_in); + sof_dma_put(dma_out); + + LOG_INF("Cleanup successful, terminating user thread."); + + k_sem_give(&ipc_sem_wake_kernel); +} + +static void intel_ssp_dai_kernel(void) +{ + const struct device *dma_out, *dma_in; + const struct device *dai_dev; + + k_thread_create(&user_thread, user_stack, USER_STACKSIZE, + intel_ssp_dai_user, NULL, NULL, NULL, + -1, K_USER, K_FOREVER); + + k_thread_access_grant(&user_thread, &ipc_sem_wake_user); + k_thread_access_grant(&user_thread, &ipc_sem_wake_kernel); + + dma_out = DEVICE_DT_GET(DT_NODELABEL(hda_link_out)); + dma_in = DEVICE_DT_GET(DT_NODELABEL(hda_link_in)); + dai_dev = DEVICE_DT_GET(DT_NODELABEL(SSP_DEVICE)); + + k_thread_access_grant(&user_thread, dma_out); + k_thread_access_grant(&user_thread, dma_in); + k_thread_access_grant(&user_thread, dai_dev); + + k_thread_start(&user_thread); + + LOG_INF("user started, waiting for it to be ready"); + + k_sem_give(&ipc_sem_wake_user); + k_sem_take(&ipc_sem_wake_kernel, K_FOREVER); + + LOG_INF("user ready, starting HDA test"); + + k_sem_give(&ipc_sem_wake_user); + k_sem_take(&ipc_sem_wake_kernel, K_FOREVER); + + LOG_INF("transfer done, grant permission to clean up"); + + k_sem_give(&ipc_sem_wake_user); + k_sem_take(&ipc_sem_wake_kernel, K_FOREVER); + + LOG_INF("test done, terminate user thread"); + + k_thread_join(&user_thread, K_FOREVER); +} + +ZTEST(userspace_intel_dai_ssp, dai_ssp_loopback_setup) +{ + intel_ssp_dai_kernel(); + + ztest_test_pass(); +} + +ZTEST_SUITE(userspace_intel_dai_ssp, NULL, NULL, NULL, NULL, NULL); + +/** + * SOF main has booted up and IPC handling is stopped. + * Run test suites with ztest_run_all. + */ +static int run_tests(void) +{ + ztest_run_test_suite(userspace_intel_dai_ssp, false, 1, 1, NULL); + return 0; +} + +SYS_INIT(run_tests, APPLICATION, 99); diff --git a/zephyr/test/userspace/test_ll_task.c b/zephyr/test/userspace/test_ll_task.c new file mode 100644 index 000000000000..234423defc60 --- /dev/null +++ b/zephyr/test/userspace/test_ll_task.c @@ -0,0 +1,144 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright(c) 2026 Intel Corporation. + */ + +/* + * Test case for creation of low-latency threads in user-space. + */ + +#include <sof/boot_test.h> +#include <sof/lib/mailbox.h> +#include <sof/lib/uuid.h> +#include <sof/schedule/schedule.h> +#include <sof/schedule/ll_schedule.h> +#include <sof/schedule/ll_schedule_domain.h> +#include <sof/audio/pipeline.h> +#include <rtos/task.h> +#include <rtos/userspace_helper.h> +#include <ipc4/fw_reg.h> + +#include <zephyr/kernel.h> +#include <zephyr/ztest.h> +#include <zephyr/logging/log.h> +#include <zephyr/app_memory/app_memdomain.h> + +#include <stddef.h> /* offsetof() */ + +LOG_MODULE_DECLARE(sof_boot_test, LOG_LEVEL_DBG); + +/* f11818eb-e92e-4082-82a3-dc54c604ebf3 */ +SOF_DEFINE_UUID("test_task", test_task_uuid, 0xf11818eb, 0xe92e, 0x4082, + 0x82, 0xa3, 0xdc, 0x54, 0xc6, 0x04, 0xeb, 0xf3); + +K_APPMEM_PARTITION_DEFINE(userspace_ll_part); + +/* Global variable for test runs counter, accessible from user-space */ +K_APP_BMEM(userspace_ll_part) static int test_runs; + +static enum task_state task_callback(void *arg) +{ + LOG_INF("entry"); + + if (++test_runs > 3) + return SOF_TASK_STATE_COMPLETED; + + return SOF_TASK_STATE_RESCHEDULE; +} + +static void ll_task_test(void) +{ + struct task *task; + int priority = 0; + int core = 0; + int ret; + + /* Initialize global test runs counter */ + test_runs = 0; + + task = zephyr_ll_task_alloc(); + zassert_not_null(task, "task allocation failed"); + + /* allow user space to report status via 'test_runs' */ + k_mem_domain_add_partition(zephyr_ll_mem_domain(), &userspace_ll_part); + + /* work in progress, see pipeline-schedule.c */ + ret = schedule_task_init_ll(task, SOF_UUID(test_task_uuid), SOF_SCHEDULE_LL_TIMER, + priority, task_callback, + (void *)&test_runs, core, 0); + zassert_equal(ret, 0); + + LOG_INF("task init done"); + + /* Schedule the task to run immediately with 1ms period */ + ret = schedule_task(task, 0, 1000); /* 0 = start now, 1000us = 1ms period */ + zassert_equal(ret, 0); + + LOG_INF("task scheduled and running"); + + /* Let the task run for a bit */ + k_sleep(K_MSEC(10)); + + /* Cancel the task to stop any scheduled execution */ + ret = schedule_task_cancel(task); + zassert_equal(ret, 0); + + /* Free task resources */ + ret = schedule_task_free(task); + zassert_equal(ret, 0); + + LOG_INF("test complete"); +} + +ZTEST(userspace_ll, ll_task_test) +{ + ll_task_test(); +} + +static void pipeline_check(void) +{ + struct pipeline *p; + struct k_heap *heap; + uint32_t pipeline_id = 1; + uint32_t priority = 5; + uint32_t comp_id = 10; + int ret; + + heap = zephyr_ll_user_heap(); + zassert_not_null(heap, "user heap not found"); + + /* Create pipeline on user heap */ + p = pipeline_new(heap, pipeline_id, priority, comp_id, NULL); + zassert_not_null(p, "pipeline creation failed"); + + /* Verify heap assignment */ + zassert_equal(p->heap, heap, "pipeline heap not equal to user heap"); + + /* Verify pipeline properties */ + zassert_equal(p->pipeline_id, pipeline_id, "pipeline id mismatch"); + zassert_equal(p->priority, priority, "priority mismatch"); + zassert_equal(p->comp_id, comp_id, "comp id mismatch"); + + /* Free pipeline */ + ret = pipeline_free(p); + zassert_ok(ret, "pipeline free failed"); +} + +ZTEST(userspace_ll, pipeline_check) +{ + pipeline_check(); +} + +ZTEST_SUITE(userspace_ll, NULL, NULL, NULL, NULL, NULL); + +/** + * SOF main has booted up and IPC handling is stopped. + * Run test suites with ztest_run_all. + */ +static int run_tests(void) +{ + ztest_run_test_suite(userspace_ll, false, 1, 1, NULL); + return 0; +} + +SYS_INIT(run_tests, APPLICATION, 99); diff --git a/zephyr/test/userspace/test_mailbox.c b/zephyr/test/userspace/test_mailbox.c new file mode 100644 index 000000000000..766dababb553 --- /dev/null +++ b/zephyr/test/userspace/test_mailbox.c @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright(c) 2026 Intel Corporation. + */ + +/* + * Test case for sof/mailbox.h interface use from a Zephyr user + * thread. + */ + +#include <sof/boot_test.h> +#include <sof/lib/mailbox.h> +#include <rtos/userspace_helper.h> + +#include <zephyr/kernel.h> +#include <zephyr/ztest.h> +#include <zephyr/logging/log.h> +#include <zephyr/app_memory/app_memdomain.h> + +#include <ipc4/fw_reg.h> /* mailbox definitions */ + +LOG_MODULE_DECLARE(sof_boot_test, LOG_LEVEL_DBG); + +#define USER_STACKSIZE 2048 + +static struct k_thread user_thread; +static K_THREAD_STACK_DEFINE(user_stack, USER_STACKSIZE); + +static void mailbox_write_to_pipeline_regs(void) +{ + unsigned int offset = offsetof(struct ipc4_fw_registers, pipeline_regs); + struct ipc4_pipeline_registers pipe_reg; + + /* + * IPC4 pipe_reg struct used for test, but this test also + * works for IPC3 targets. + */ + pipe_reg.stream_start_offset = (uint64_t)-1; + pipe_reg.stream_end_offset = (uint64_t)-1; + + LOG_INF("Write to sw_regs mailbox at offset %u", offset); + + mailbox_sw_regs_write(offset, &pipe_reg, sizeof(pipe_reg)); +} + +static void mailbox_test_thread(void *p1, void *p2, void *p3) +{ + zassert_true(k_is_user_context(), "isn't user"); + + LOG_INF("SOF thread %s (%s)", + k_is_user_context() ? "UserSpace!" : "privileged mode.", + CONFIG_BOARD_TARGET); + + mailbox_write_to_pipeline_regs(); +} + +static void mailbox_test(void) +{ + struct k_mem_domain domain; + int ret = k_mem_domain_init(&domain, 0, NULL); + + zassert_equal(ret, 0); + + k_thread_create(&user_thread, user_stack, USER_STACKSIZE, + mailbox_test_thread, NULL, NULL, NULL, + -1, K_USER, K_FOREVER); + + LOG_INF("set up user access to mailbox"); + + ret = user_access_to_mailbox(&domain, &user_thread); + zassert_equal(ret, 0); + + k_thread_start(&user_thread); + + LOG_INF("user started, waiting in kernel until test complete"); + + k_thread_join(&user_thread, K_FOREVER); +} + +ZTEST(userspace_mailbox, mailbox_test) +{ + /* first test from kernel */ + mailbox_write_to_pipeline_regs(); + + /* then full test in userspace */ + mailbox_test(); + + ztest_test_pass(); +} + +ZTEST_SUITE(userspace_mailbox, NULL, NULL, NULL, NULL, NULL); + +/** + * SOF main has booted up and IPC handling is stopped. + * Run test suites with ztest_run_all. + */ +static int run_tests(void) +{ + ztest_run_test_suite(userspace_mailbox, false, 1, 1, NULL); + return 0; +} + +SYS_INIT(run_tests, APPLICATION, 99); diff --git a/zephyr/test/vmh.c b/zephyr/test/vmh.c index 26e044a2d9f0..abb00b561b3d 100644 --- a/zephyr/test/vmh.c +++ b/zephyr/test/vmh.c @@ -25,10 +25,8 @@ static void test_vmh_init_and_free_heap(struct vmh_heap_config *config, bool expect_success) { struct vmh_heap *heap = vmh_init_heap(config, allocating_continuously); - if (expect_success) { - zassert_not_null(heap, - "Heap initialization expected to succeed but failed"); - } + if (expect_success) + zassert_not_null(heap, "Heap initialization expected to succeed but failed"); else zassert_is_null(heap, "Heap initialization expected to fail but succeeded"); @@ -110,7 +108,6 @@ static void test_vmh_multiple_allocs(struct vmh_heap *heap, int num_allocs, { void *ptrs[num_allocs]; uint32_t alloc_size; - bool success; int ret; /* Perform multiple allocations */ @@ -192,7 +189,7 @@ static void test_vmh_alloc_free(bool allocating_continuously) /* Test case for vmh_alloc and vmh_free with and without config */ static void test_heap_creation(void) { - test_vmh_init_and_free_heap(NULL, 0, false, true); + test_vmh_init_and_free_heap(NULL, false, true); /* Try to setup with pre defined heap config */ struct vmh_heap_config config = {0}; @@ -205,7 +202,7 @@ static void test_heap_creation(void) config.block_bundles_table[1].number_of_blocks = 512; - test_vmh_init_and_free_heap(&config, 0, false, true); + test_vmh_init_and_free_heap(&config, false, true); } /* Test case for alloc/free on configured heap */ diff --git a/zephyr/test/vpage.c b/zephyr/test/vpage.c new file mode 100644 index 000000000000..038e299eba70 --- /dev/null +++ b/zephyr/test/vpage.c @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright(c) 2026 Intel Corporation. + */ + +#include <errno.h> +#include <stdbool.h> +#include <stdlib.h> + +#include <sof/boot_test.h> +#include <sof/lib/vpage.h> + +#include <zephyr/logging/log.h> +#include <zephyr/ztest.h> + +LOG_MODULE_DECLARE(sof_boot_test, CONFIG_SOF_LOG_LEVEL); + +ZTEST(sof_boot, vpage) +{ + void *p1 = vpage_alloc(1); + + zassert_not_null(p1); + + void *p2 = vpage_alloc(2); + + zassert_not_null(p2); + + vpage_free(p1); + vpage_free(p2); + + p1 = vpage_alloc(2); + + zassert_not_null(p1); + + vpage_free(p1); +} diff --git a/zephyr/test/vregion.c b/zephyr/test/vregion.c new file mode 100644 index 000000000000..eb59f68a14d7 --- /dev/null +++ b/zephyr/test/vregion.c @@ -0,0 +1,92 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright(c) 2026 Intel Corporation. + */ + +#include <errno.h> +#include <stdbool.h> +#include <stdlib.h> + +#include <sof/boot_test.h> +#include <sof/lib/vregion.h> + +#include <zephyr/logging/log.h> +#include <zephyr/ztest.h> + +LOG_MODULE_DECLARE(sof_boot_test, CONFIG_SOF_LOG_LEVEL); + +static struct vregion *test_vreg_create(void) +{ + struct vregion *vreg = vregion_create(CONFIG_MM_DRV_PAGE_SIZE - 100, + CONFIG_MM_DRV_PAGE_SIZE); + + zassert_not_null(vreg); + + return vreg; +} + +static void test_vreg_alloc_lifet(struct vregion *vreg) +{ + void *ptr = vregion_alloc(vreg, VREGION_MEM_TYPE_LIFETIME, 2000); + + zassert_not_null(ptr); + + void *ptr_align = vregion_alloc_align(vreg, VREGION_MEM_TYPE_LIFETIME, 1600, 16); + + zassert_not_null(ptr_align); + zassert_equal((uintptr_t)ptr_align & 15, 0); + + void *ptr_nomem = vregion_alloc(vreg, VREGION_MEM_TYPE_LIFETIME, 2000); + + zassert_is_null(ptr_nomem); + + vregion_free(vreg, ptr_align); + vregion_free(vreg, ptr); + + /* Freeing isn't possible with LIFETIME */ + ptr_nomem = vregion_alloc(vreg, VREGION_MEM_TYPE_LIFETIME, 2000); + + zassert_is_null(ptr_nomem); +} + +static void test_vreg_alloc_tmp(struct vregion *vreg) +{ + void *ptr = vregion_alloc(vreg, VREGION_MEM_TYPE_INTERIM, 20); + + zassert_not_null(ptr); + + void *ptr_align = vregion_alloc_align(vreg, VREGION_MEM_TYPE_INTERIM, 2000, 16); + + zassert_not_null(ptr_align); + zassert_equal((uintptr_t)ptr_align & 15, 0); + + void *ptr_nomem = vregion_alloc(vreg, VREGION_MEM_TYPE_INTERIM, 2000); + + zassert_is_null(ptr_nomem); + + vregion_free(vreg, ptr_align); + vregion_free(vreg, ptr); + + /* Should be possible to allocate again */ + ptr = vregion_alloc(vreg, VREGION_MEM_TYPE_INTERIM, 2000); + + zassert_not_null(ptr); +} + +static void test_vreg_destroy(struct vregion *vreg) +{ + vregion_info(vreg); + vregion_put(vreg); +} + +ZTEST(sof_boot, vregion) +{ + struct vregion *vreg = test_vreg_create(); + + /* Test interim allocations */ + test_vreg_alloc_tmp(vreg); + /* Test lifetime allocations */ + test_vreg_alloc_lifet(vreg); + + test_vreg_destroy(vreg); +} diff --git a/zephyr/wrapper.c b/zephyr/wrapper.c index 2e302f22c94b..c0c167b930fe 100644 --- a/zephyr/wrapper.c +++ b/zephyr/wrapper.c @@ -57,7 +57,7 @@ const char irq_name_level2[] = "level2"; const char irq_name_level5[] = "level5"; /* imx currently has no IRQ driver in Zephyr so we force to xtos IRQ */ -#if defined(CONFIG_AMD) +#if defined(CONFIG_AMD) && !defined(CONFIG_ZEPHYR_NATIVE_DRIVERS) int interrupt_register(uint32_t irq, void(*handler)(void *arg), void *arg) { #ifdef CONFIG_DYNAMIC_INTERRUPTS @@ -333,7 +333,10 @@ void k_sys_fatal_error_handler(unsigned int reason, /* flush and switch to immediate mode */ LOG_PANIC(); + /* IPC not set up in standalone test mode */ +#ifndef CONFIG_SOF_BOOT_TEST_STANDALONE ipc_send_panic_notification(); +#endif #if defined(CONFIG_ARCH_POSIX) || defined(CONFIG_ZEPHYR_POSIX) LOG_ERR("Halting emulation");