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..9115fa26d071 100644 --- a/.github/workflows/build_all.yml +++ b/.github/workflows/build_all.yml @@ -8,6 +8,15 @@ name: Build test all components # yamllint disable-line rule:truthy on: [pull_request, workflow_dispatch, workflow_call] +permissions: + contents: read + +# Group workflows together to cancel outdated runs, do not cancel manual runs +concurrency: + group: "${{ github.workflow }}-${{ github.event_name }}-\ + ${{ github.event_name == 'pull_request' && github.ref || github.run_id }}" + cancel-in-progress: true + 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/close-stale.yml b/.github/workflows/close-stale.yml index 052878a5cdd2..e680dc31c604 100644 --- a/.github/workflows/close-stale.yml +++ b/.github/workflows/close-stale.yml @@ -12,7 +12,7 @@ permissions: jobs: stale: name: Find stale issues - runs-on: ubuntu-24.04 + runs-on: ubuntu-26.04 if: github.repository == 'thesofproject/sof' permissions: issues: write diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 000000000000..cc99ee2c209b --- /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 + +# Group workflows together to cancel outdated runs +concurrency: + group: ${{ github.workflow }}-${{ github.event_name }}-${{ 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..fab95196af88 100644 --- a/.github/workflows/codestyle.yml +++ b/.github/workflows/codestyle.yml @@ -14,45 +14,16 @@ 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 +permissions: + contents: read - # 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 +# Group workflows together to cancel outdated runs, do not cancel manual runs +concurrency: + group: "${{ github.workflow }}-${{ github.event_name }}-\ + ${{ github.event_name == 'pull_request' && github.ref || github.run_id }}" + cancel-in-progress: true +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..6b89339e7445 100644 --- a/.github/workflows/ipc_fuzzer.yml +++ b/.github/workflows/ipc_fuzzer.yml @@ -22,6 +22,15 @@ on: pull_request: # TODO: can we provide a default inputs here too? +permissions: + contents: read + +# Group workflows together to cancel outdated runs, do not cancel manual runs +concurrency: + group: "${{ github.workflow }}-${{ github.event_name }}-\ + ${{ github.event_name == 'pull_request' && github.ref || github.run_id }}" + cancel-in-progress: true + jobs: simple-IPC-fuzz_sh: @@ -72,6 +81,16 @@ jobs: west init -l west update --narrow --fetch-opt=--filter=tree:0 + - name: generate IPC dictionary + # A generator failure (e.g. after a header rename) must not break + # fuzzing: the step is best-effort and fuzz.sh runs without the + # dictionary when the file is missing or empty (see -d below). + continue-on-error: true + run: | + cd workspace + python3 sof/scripts/gen_fuzz_ipc${{ matrix.IPC }}_dict.py \ + -o "$RUNNER_TEMP/ipc${{ matrix.IPC }}.dict" + - name: build and run fuzzer for a few minutes run: | cd workspace @@ -80,7 +99,8 @@ jobs: duration="${{inputs.fuzzing_duration_s}}" duration="${duration:-301}" # pull_request has not 'inputs.' :-( # Note libFuzzer makes a difference between -jobs and -workers (capped at nproc/2) - sof/scripts/fuzz.sh -i '${{ matrix.IPC }}' -o fuzz-stdout.txt -t "$duration" -j"$(nproc)" + sof/scripts/fuzz.sh -i '${{ matrix.IPC }}' -o fuzz-stdout.txt -t "$duration" \ + -j"$(nproc)" -d "$RUNNER_TEMP/ipc${{ matrix.IPC }}.dict" - name: Upload stdout uses: actions/upload-artifact@v4 diff --git a/.github/workflows/llext.yml b/.github/workflows/llext.yml index 21b812b47600..c93be69af642 100644 --- a/.github/workflows/llext.yml +++ b/.github/workflows/llext.yml @@ -7,9 +7,24 @@ name: Zephyr LLEXT # yamllint disable-line rule:truthy on: [pull_request, workflow_dispatch] +defaults: + run: + shell: bash + +permissions: + contents: read + +# Group workflows together to cancel outdated runs +concurrency: + group: "${{ github.workflow }}-${{ github.event_name }}-\ + ${{ github.event_name == 'pull_request' && github.ref || github.run_id }}" + cancel-in-progress: true + jobs: build: - runs-on: ubuntu-22.04 + runs-on: ubuntu-26.04 + container: + image: thesofproject/zephyr-lite:v0.29.3 strategy: fail-fast: false @@ -17,33 +32,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..bc2475c9c45e 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -31,10 +31,19 @@ on: # Allows to call this forkflow from other workflows workflow_call: +permissions: + contents: read + +# Group workflows together to cancel outdated runs, do not cancel manual runs +concurrency: + group: "${{ github.workflow }}-${{ github.event_name }}-\ + ${{ github.event_name == 'pull_request' && github.ref || github.run_id }}" + cancel-in-progress: true + jobs: doxygen: - runs-on: ubuntu-22.04 + runs-on: ubuntu-26.04 steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/repro-build.yml b/.github/workflows/repro-build.yml index 034ae4ac286a..e250eea7fc99 100644 --- a/.github/workflows/repro-build.yml +++ b/.github/workflows/repro-build.yml @@ -14,9 +14,18 @@ name: Reproducible builds # yamllint disable-line rule:truthy on: [pull_request, workflow_dispatch, workflow_call] +permissions: + contents: read + +# Group workflows together to cancel outdated runs, do not cancel manual runs +concurrency: + group: "${{ github.workflow }}-${{ github.event_name }}-\ + ${{ github.event_name == 'pull_request' && github.ref || github.run_id }}" + cancel-in-progress: true + jobs: main: - runs-on: ubuntu-22.04 + runs-on: ubuntu-26.04 steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/rimage.yml b/.github/workflows/rimage.yml index b6e66c23c449..28ca8fffa4d4 100644 --- a/.github/workflows/rimage.yml +++ b/.github/workflows/rimage.yml @@ -22,11 +22,20 @@ on: paths: - tools/rimage/** +permissions: + contents: read + +# Group workflows together to cancel outdated runs, do not cancel manual runs +concurrency: + group: "${{ github.workflow }}-${{ github.event_name }}-\ + ${{ github.event_name == 'pull_request' && github.ref || github.run_id }}" + cancel-in-progress: true + jobs: # Basic build test build: - runs-on: ubuntu-22.04 + runs-on: ubuntu-26.04 env: # FIXME: add -Wpointer-arith _CFLGS: -Werror -Wall -Wmissing-prototypes @@ -46,7 +55,7 @@ jobs: # cppcheck cppcheck: - runs-on: ubuntu-22.04 + runs-on: ubuntu-26.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..6db83a0a82d1 100644 --- a/.github/workflows/sof-docs.yml +++ b/.github/workflows/sof-docs.yml @@ -17,6 +17,15 @@ on: # Allows to call this forkflow from other workflows workflow_call: +permissions: + contents: read + +# Group workflows together to cancel outdated runs, do not cancel manual runs +concurrency: + group: "${{ github.workflow }}-${{ github.event_name }}-\ + ${{ github.event_name == 'pull_request' && github.ref || github.run_id }}" + cancel-in-progress: true + jobs: # This is unfortunately a mix of sof-docs/.github/ + pull-request.yml#doxygen @@ -26,7 +35,7 @@ jobs: # example in # https://github.com/thesofproject/sof/pull/5731#issuecomment-1175630147 sof-docs: - runs-on: ubuntu-24.04 + runs-on: ubuntu-26.04 steps: - uses: actions/checkout@v4 @@ -56,4 +65,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..65e915d3c5ae 100644 --- a/.github/workflows/sparse-zephyr.yml +++ b/.github/workflows/sparse-zephyr.yml @@ -7,6 +7,19 @@ name: Sparse Zephyr # yamllint disable-line rule:truthy on: [push, pull_request, workflow_dispatch, workflow_call] +defaults: + run: + shell: bash + +permissions: + contents: read + +# Group workflows together to cancel outdated runs, do not cancel manual runs +concurrency: + group: "${{ github.workflow }}-${{ github.event_name }}-\ + ${{ github.event_name == 'pull_request' && github.ref || github.run_id }}" + cancel-in-progress: true + 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 +27,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 + runs-on: ubuntu-26.04 + container: + image: thesofproject/zephyr-lite:v0.29.3 strategy: fail-fast: false @@ -25,43 +43,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 +83,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..82e26470ff17 100644 --- a/.github/workflows/testbench.yml +++ b/.github/workflows/testbench.yml @@ -28,9 +28,18 @@ on: workflow_dispatch: workflow_call: +permissions: + contents: read + +# Group workflows together to cancel outdated runs, do not cancel manual runs +concurrency: + group: "${{ github.workflow }}-${{ github.event_name }}-\ + ${{ github.event_name == 'pull_request' && github.ref || github.run_id }}" + cancel-in-progress: true + jobs: build-and-test: - runs-on: ubuntu-24.04 + runs-on: ubuntu-26.04 steps: - name: Checkout SOF repository (PR source) @@ -46,12 +55,27 @@ jobs: octave octave-signal automake autoconf libtool gettext libasound2-dev + # Ubuntu 24.04 ships autoconf 2.71, but current alsa-lib/alsa-utils + # require >= 2.72. Build a newer autoconf from the GNU release + # tarball and install it into /usr/local (ahead of /usr/bin in PATH). + - name: Install autoconf 2.72 + run: | + cd ${GITHUB_WORKSPACE} + wget -q https://ftp.gnu.org/gnu/autoconf/autoconf-2.72.tar.gz + tar xf autoconf-2.72.tar.gz + cd autoconf-2.72 + ./configure --prefix=/usr/local + make -j"$(nproc)" + sudo make install + hash -r + autoconf --version | head -n1 + - name: Build Alsa-lib run: | cd ${GITHUB_WORKSPACE} git clone https://github.com/thesofproject/alsa-lib.git cd alsa-lib - git checkout df8f1cc1ec9d9ee15be5e2c23ad25b9389fd8766 -b build + git checkout 08b532cd3da9ac8f683bcb4e4beb9b74c39c1782 -b build ./gitcompile --prefix=${GITHUB_WORKSPACE}/tools make install @@ -60,7 +84,7 @@ jobs: cd ${GITHUB_WORKSPACE} git clone https://github.com/thesofproject/alsa-utils.git cd alsa-utils - git checkout 0ffa105942a06cdfa98e5918b8dc82e3cac12792 -b build + git checkout 9feb22ba45b48729729c8d194aaf1bc082a6842a -b build ./gitcompile --prefix=${GITHUB_WORKSPACE}/tools \ --with-alsa-prefix=${GITHUB_WORKSPACE}/tools \ --with-alsa-inc-prefix=${GITHUB_WORKSPACE}/tools/include \ diff --git a/.github/workflows/tools.yml b/.github/workflows/tools.yml index 9c8197a6cbcc..e5f257bb288c 100644 --- a/.github/workflows/tools.yml +++ b/.github/workflows/tools.yml @@ -7,32 +7,90 @@ name: User space tools/ directory # yamllint disable-line rule:truthy on: [pull_request, workflow_dispatch, workflow_call] +permissions: + contents: read + +# Group workflows together to cancel outdated runs, do not cancel manual runs +concurrency: + group: "${{ github.workflow }}-${{ github.event_name }}-\ + ${{ github.event_name == 'pull_request' && github.ref || github.run_id }}" + cancel-in-progress: true + jobs: # This is not the same as building every ./build-tools.sh option. top-level_default_CMake_target_ALL: - runs-on: ubuntu-latest + runs-on: ubuntu-26.04 steps: - uses: actions/checkout@v4 with: filter: 'tree:0' + path: sof + + - name: apt get + run: sudo apt-get update && + sudo apt-get -y install ninja-build + automake autoconf libtool gettext libasound2-dev + pkg-config + + # Ubuntu 24.04 ships autoconf 2.71, but current alsa-lib/alsa-utils + # require >= 2.72. Build a newer autoconf from the GNU release + # tarball and install it into /usr/local (ahead of /usr/bin in PATH). + - name: Install autoconf 2.72 + run: | + cd ${GITHUB_WORKSPACE} + wget -q https://ftp.gnu.org/gnu/autoconf/autoconf-2.72.tar.gz + tar xf autoconf-2.72.tar.gz + cd autoconf-2.72 + ./configure --prefix=/usr/local + make -j"$(nproc)" + sudo make install + hash -r + autoconf --version | head -n1 + + - name: Build Alsa-lib + run: | + cd ${GITHUB_WORKSPACE} + git clone https://github.com/thesofproject/alsa-lib.git + cd alsa-lib + git checkout 08b532cd3da9ac8f683bcb4e4beb9b74c39c1782 -b build + ./gitcompile --prefix=${GITHUB_WORKSPACE}/tools + make install - # The ALSA version in Ubuntu 20.04 is buggy - # (https://github.com/thesofproject/sof/issues/2543) and likely - # getting out of date soon - - name: docker - run: docker pull thesofproject/sof && docker tag thesofproject/sof sof + - name: Build Alsa-utils + run: | + cd ${GITHUB_WORKSPACE} + git clone https://github.com/thesofproject/alsa-utils.git + cd alsa-utils + git checkout 9feb22ba45b48729729c8d194aaf1bc082a6842a -b build + ./gitcompile --prefix=${GITHUB_WORKSPACE}/tools \ + --with-alsa-prefix=${GITHUB_WORKSPACE}/tools \ + --with-alsa-inc-prefix=${GITHUB_WORKSPACE}/tools/include \ + --with-sysroot=${GITHUB_WORKSPACE}/tools \ + --with-udev-rules-dir=${GITHUB_WORKSPACE}/tools \ + PKG_CONFIG_PATH=${GITHUB_WORKSPACE}/tools \ + LDFLAGS=-L${GITHUB_WORKSPACE}/tools/lib \ + --disable-old-symbols \ + --enable-alsatopology \ + --with-asound-state-dir=${GITHUB_WORKSPACE}/tools/var/lib/alsa \ + --with-systemdsystemunitdir=${GITHUB_WORKSPACE}/tools/lib/systemd/system + make install # For some reason gcc has more warnings in Release mode - name: build-tools - run: CMAKE_BUILD_TYPE=Release ./scripts/docker-run.sh - ./scripts/build-tools.sh || + env: + PKG_CONFIG_PATH: ${{ github.workspace }}/tools/lib/pkgconfig + LD_LIBRARY_PATH: ${{ github.workspace }}/tools/lib + PATH: ${{ github.workspace }}/tools/bin:/usr/local/bin:/usr/bin:/bin + run: CMAKE_BUILD_TYPE=Release ./sof/scripts/build-tools.sh || VERBOSE=1 NO_PROCESSORS=1 USE_XARGS=no - CMAKE_BUILD_TYPE=Release ./scripts/docker-run.sh - ./scripts/build-tools.sh + CMAKE_BUILD_TYPE=Release ./sof/scripts/build-tools.sh SOF-alsa-plugin: - runs-on: ubuntu-22.04 + # TODO: Update ubuntu to 26.04 when openvino situation is resolved + # It's not in the base ubuntu 26.04 OS and installation fails + # https://github.com/openvinotoolkit/openvino/issues/37509 + 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..76e8c5c9e0b7 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -10,6 +10,15 @@ name: Unit tests # yamllint disable-line rule:truthy on: [pull_request, workflow_dispatch, workflow_call] +permissions: + contents: read + +# Group workflows together to cancel outdated runs, do not cancel manual runs +concurrency: + group: "${{ github.workflow }}-${{ github.event_name }}-\ + ${{ github.event_name == 'pull_request' && github.ref || github.run_id }}" + cancel-in-progress: true + 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..5c78fee0bfb4 --- /dev/null +++ b/.github/workflows/zephyr-shell.yml @@ -0,0 +1,70 @@ +--- +# 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 + +# Group workflows together to cancel outdated runs, do not cancel manual runs +concurrency: + group: "${{ github.workflow }}-${{ github.event_name }}-\ + ${{ github.event_name == 'pull_request' && github.ref || github.run_id }}" + cancel-in-progress: true + +jobs: + build: + runs-on: ubuntu-26.04 + container: + image: thesofproject/zephyr-lite:v0.29.3 + + 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..d435d6f1402b 100644 --- a/.github/workflows/zephyr-unit-tests.yml +++ b/.github/workflows/zephyr-unit-tests.yml @@ -1,5 +1,5 @@ --- -name: "Unit tests" +name: "Zephyr Unit tests" # yamllint disable-line rule:truthy on: pull_request: @@ -9,15 +9,15 @@ on: permissions: contents: read -# Specifies group name that stops previous workflows if the name matches +# Group workflows together to cancel outdated runs concurrency: - group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.head_ref || github.ref }} + group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.ref }} cancel-in-progress: true jobs: zephyr-utests: name: Zephyr Unit Tests (ZTest) - runs-on: ubuntu-24.04 + runs-on: ubuntu-26.04 timeout-minutes: 10 steps: @@ -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..826b970e76b3 100644 --- a/.github/workflows/zephyr.yml +++ b/.github/workflows/zephyr.yml @@ -5,28 +5,46 @@ 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] - -# Specifies group name that stops previous wokrflows if the name matches +on: + push: + pull_request: + workflow_dispatch: + workflow_call: + inputs: + zephyr_revision: + description: 'Zephyr revision to build against' + type: string + required: false + default: 'mnfst' + +# Group workflows together to cancel outdated runs, do not cancel manual runs concurrency: - # eg. "Zephyr-pull_request-my_fork_branch_to_merge" - # eg. "Zephyr-push-refs/heads/my_branch_merging" - group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.head_ref || github.ref }} + # eg. 'Zephyr-workflow_dispatch-62137' + # eg. 'Zephyr-pull_request-refs/pull/{pr_number}/merge' + group: "${{ github.workflow }}-${{ github.event_name }}-\ + ${{ github.event_name == 'pull_request' && github.ref || github.run_id }}" 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.3 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 +53,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 +61,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 +80,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-26.04 + defaults: + run: + shell: bash + container: + image: thesofproject/zephyr-lite:v0.29.3 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-26.04 + defaults: + run: + shell: bash + container: + image: thesofproject/zephyr-lite:v0.29.3 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 +149,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 +164,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 +183,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 +192,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 +203,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 +221,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 +303,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 +316,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 +343,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 +360,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 +403,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 +418,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 +446,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 +499,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/.github/workflows/zizmor.yml b/.github/workflows/zizmor.yml new file mode 100644 index 000000000000..8f66645c692f --- /dev/null +++ b/.github/workflows/zizmor.yml @@ -0,0 +1,38 @@ +--- +name: "Zizmor GitHub Actions Analysis" +# yamllint disable-line rule:truthy +on: + push: + branches: + - 'main' + pull_request: + branches: + - 'main' + +permissions: {} + +# Group workflows together to cancel outdated runs, do not cancel manual runs +concurrency: + group: "${{ github.workflow }}-${{ github.event_name }}-\ + ${{ github.event_name == 'pull_request' && github.ref || github.run_id }}" + cancel-in-progress: true + +jobs: + zizmor: + name: Analyze workflows with zizmor + runs-on: ubuntu-latest + permissions: + security-events: write # Required to upload SARIF results to the Security tab + actions: read # Required to read workflow information + contents: read # Required to checkout repository + + steps: + - name: Checkout repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + + - name: Run zizmor + uses: zizmorcore/zizmor-action@5f14fd08f7cf1cb1609c1e344975f152c7ee938d # v0.5.6 + with: + inputs: .github/workflows/ 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..df63811e652b 100644 --- a/Kconfig.sof +++ b/Kconfig.sof @@ -33,6 +33,7 @@ config COLD_STORE_EXECUTE_DRAM config COLD_STORE_EXECUTE_DEBUG bool "Enable checks for cold code on hot paths" + depends on !SOF_USERSPACE_LL help This enables an assert_can_be_cold() check, which causes an exception if called in the LL task context and assert() evaluation is enabled. @@ -47,6 +48,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..5eb2ff9f7b95 --- /dev/null +++ b/app/boards/acp_7_0_adsp.conf @@ -0,0 +1,55 @@ +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_DMA_AMD_ACP_TDM=y +CONFIG_DAI_AMD_TDM=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 +CONFIG_COMP_MFCC=n \ No newline at end of file diff --git a/app/boards/acp_7_x_adsp.conf b/app/boards/acp_7_x_adsp.conf new file mode 100644 index 000000000000..2515f5d763d6 --- /dev/null +++ b/app/boards/acp_7_x_adsp.conf @@ -0,0 +1,60 @@ +CONFIG_ACP_7_X=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_DMA_AMD_ACP_SDW_CHANNEL_COUNT=64 +CONFIG_DMA_AMD_ACP_TDM=y +CONFIG_DAI_AMD_SDW=y +CONFIG_DAI_AMD_TDM=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 +CONFIG_PCM_CONVERTER_FORMAT_S24_4LE_MSB=y +CONFIG_XTENSA_INTERRUPT_NONPREEMPTABLE=y +CONFIG_DEBUG_COREDUMP=y +CONFIG_DEBUG_COREDUMP_BACKEND_AMD_ACP_PANIC_DUMP=y +CONFIG_DEBUG_COREDUMP_MEMORY_DUMP_MIN=y 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..4c9ce43c9452 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>; @@ -21,6 +26,10 @@ status = "okay"; }; +&edma1 { + status = "disabled"; +}; + &edma2 { compatible = "nxp,edma"; reg = <0x42000000 (DT_SIZE_K(64) * 33)>; diff --git a/app/boards/intel_adsp/Kconfig.defconfig b/app/boards/intel_adsp/Kconfig.defconfig index a2ba2db15db6..a7423ef1d630 100644 --- a/app/boards/intel_adsp/Kconfig.defconfig +++ b/app/boards/intel_adsp/Kconfig.defconfig @@ -114,15 +114,15 @@ config DAI_INTEL_DMIC config DAI_INTEL_SSP default y -config INTEL_ADSP_IPC - default y - config INTEL_ADSP_TIMER default y config MM_DRV default y +config UAOL + default y if ACE + # # Zephyr / power settings # ---------------------------------------- @@ -162,5 +162,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..0b5472cd282a 100644 --- a/app/boards/intel_adsp_ace15_mtpm.conf +++ b/app/boards/intel_adsp_ace15_mtpm.conf @@ -15,6 +15,8 @@ CONFIG_COMP_MFCC=y CONFIG_COMP_MULTIBAND_DRC=y CONFIG_FORMAT_CONVERT_HIFI3=n CONFIG_SAMPLE_KEYPHRASE=y +CONFIG_COMP_PHASE_VOCODER=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 +36,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 @@ -53,6 +56,7 @@ CONFIG_HEAP_MEM_POOL_SIZE=8192 CONFIG_LLEXT=y CONFIG_LLEXT_STORAGE_WRITABLE=y CONFIG_LLEXT_EXPERIMENTAL=y +CONFIG_LLEXT_EDK=n CONFIG_MODULES=y CONFIG_TIMING_FUNCTIONS=y CONFIG_WATCHDOG=y @@ -69,7 +73,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 +81,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..c0b225650c01 100644 --- a/app/boards/intel_adsp_ace20_lnl.conf +++ b/app/boards/intel_adsp_ace20_lnl.conf @@ -11,6 +11,8 @@ CONFIG_COMP_TESTER=m CONFIG_COMP_SRC_IPC4_FULL_MATRIX=y CONFIG_FORMAT_CONVERT_HIFI3=n CONFIG_SAMPLE_KEYPHRASE=y +CONFIG_COMP_PHASE_VOCODER=y +CONFIG_COMP_STFT_PROCESS=y # SOF / infrastructure CONFIG_AMS=y @@ -37,6 +39,7 @@ CONFIG_HEAP_MEM_POOL_SIZE=8192 CONFIG_LLEXT=y CONFIG_LLEXT_STORAGE_WRITABLE=y CONFIG_LLEXT_EXPERIMENTAL=y +CONFIG_LLEXT_EDK=n CONFIG_MODULES=y CONFIG_TIMING_FUNCTIONS=y @@ -48,7 +51,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 +59,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..b6ac41938398 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 @@ -41,6 +42,7 @@ CONFIG_HEAP_MEM_POOL_SIZE=8192 CONFIG_LLEXT=y CONFIG_LLEXT_STORAGE_WRITABLE=y CONFIG_LLEXT_EXPERIMENTAL=y +CONFIG_LLEXT_EDK=n CONFIG_MODULES=y # Zephyr / device drivers @@ -52,15 +54,27 @@ 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 + +CONFIG_MAX_DOMAIN_PARTITIONS=32 diff --git a/app/boards/intel_adsp_ace30_ptl_sim.conf b/app/boards/intel_adsp_ace30_ptl_sim.conf index 477450d2d271..70aa151b0739 100644 --- a/app/boards/intel_adsp_ace30_ptl_sim.conf +++ b/app/boards/intel_adsp_ace30_ptl_sim.conf @@ -35,8 +35,6 @@ CONFIG_SOF_LOG_LEVEL_INF=n CONFIG_SOF_LOG_LEVEL_OFF=y CONFIG_ZEPHYR_LOG=n -CONFIG_INTEL_ADSP_IPC=y - # Temporary disabled options CONFIG_PM_DEVICE_RUNTIME=n diff --git a/app/boards/intel_adsp_ace30_wcl.conf b/app/boards/intel_adsp_ace30_wcl.conf index faa20dd97a9b..2196af333e65 100644 --- a/app/boards/intel_adsp_ace30_wcl.conf +++ b/app/boards/intel_adsp_ace30_wcl.conf @@ -9,15 +9,26 @@ 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 CONFIG_INTEL_MODULES=y CONFIG_LIBRARY_MANAGER=y +CONFIG_LIBRARY_AUTH_SUPPORT=y CONFIG_LIBRARY_BASE_ADDRESS=0xa0688000 CONFIG_LIBRARY_BUILD_LIB=y CONFIG_LIBRARY_DEFAULT_MODULAR=y @@ -30,6 +41,7 @@ CONFIG_HEAP_MEM_POOL_SIZE=8192 CONFIG_LLEXT=y CONFIG_LLEXT_STORAGE_WRITABLE=y CONFIG_LLEXT_EXPERIMENTAL=y +CONFIG_LLEXT_EDK=n CONFIG_MODULES=y # Zephyr / device drivers @@ -38,16 +50,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..bb39cf026ada 100644 --- a/app/boards/intel_adsp_ace40_nvl.conf +++ b/app/boards/intel_adsp_ace40_nvl.conf @@ -12,13 +12,23 @@ CONFIG_COMP_SRC_IPC4_FULL_MATRIX=y CONFIG_COMP_TESTER=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 + # 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 +36,11 @@ 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_LLEXT_EDK=n +CONFIG_MODULES=y # Zephyr / device drivers CONFIG_DAI_INIT_PRIORITY=70 @@ -34,14 +49,20 @@ 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 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..bb39cf026ada 100644 --- a/app/boards/intel_adsp_ace40_nvls.conf +++ b/app/boards/intel_adsp_ace40_nvls.conf @@ -12,13 +12,23 @@ CONFIG_COMP_SRC_IPC4_FULL_MATRIX=y CONFIG_COMP_TESTER=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 + # 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 +36,11 @@ 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_LLEXT_EDK=n +CONFIG_MODULES=y # Zephyr / device drivers CONFIG_DAI_INIT_PRIORITY=70 @@ -34,14 +49,20 @@ 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 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/mt8196_mt8196_adsp.conf b/app/boards/mt8196_mt8196_adsp.conf index 0d44864b6144..95f7ac3a5655 100644 --- a/app/boards/mt8196_mt8196_adsp.conf +++ b/app/boards/mt8196_mt8196_adsp.conf @@ -4,3 +4,7 @@ # Board-level config goes in Zephyr (and ideally in DTS). App-level # config goes in prj.conf. CONFIG_MTK=y +CONFIG_RIMAGE_SIGNING_SCHEMA="mt8196" +CONFIG_ZEPHYR_NATIVE_DRIVERS=y +CONFIG_DMA=y +CONFIG_DAI=y 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/native_sim.conf b/app/boards/native_sim.conf new file mode 100644 index 000000000000..6df3eb89b648 --- /dev/null +++ b/app/boards/native_sim.conf @@ -0,0 +1,4 @@ +CONFIG_ZEPHYR_POSIX=y +CONFIG_SYS_HEAP_BIG_ONLY=y +CONFIG_ZEPHYR_NATIVE_DRIVERS=y +CONFIG_ZEPHYR_LOG=y diff --git a/app/boards/native_sim_libfuzzer.conf b/app/boards/native_sim_libfuzzer.conf index ece1e35845c9..096bfe9dea47 100644 --- a/app/boards/native_sim_libfuzzer.conf +++ b/app/boards/native_sim_libfuzzer.conf @@ -5,6 +5,8 @@ CONFIG_ASSERT=y CONFIG_EXCEPTION_DEBUG=y CONFIG_ARCH_POSIX_TRAP_ON_FATAL=y CONFIG_SYS_HEAP_BIG_ONLY=y +CONFIG_SYS_HEAP_HARDENING_FULL=y +CONFIG_STACK_SENTINEL=y CONFIG_ZEPHYR_NATIVE_DRIVERS=y CONFIG_ARCH_POSIX_LIBFUZZER=y CONFIG_ZEPHYR_POSIX_FUZZ_TICKS=100 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..6f90c50b1524 --- /dev/null +++ b/app/compr/README.txt @@ -0,0 +1,38 @@ +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 +pcm.conf - PCM (WAV) decoder +all_codecs.conf - All supported codecs (MP3, AAC, Vorbis, PCM) + +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..f4aeaf3c5da5 --- /dev/null +++ b/app/compr/all_codecs.conf @@ -0,0 +1,11 @@ +# All Cadence codecs (MP3, AAC, Vorbis) plus SOF PCM/WAV decoder +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" +CONFIG_SOF_COMPRESS_CODEC_PCM_DEC=y 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/pcm.conf b/app/compr/pcm.conf new file mode 100644 index 000000000000..18a0e0e2810b --- /dev/null +++ b/app/compr/pcm.conf @@ -0,0 +1,3 @@ +# PCM (wav) decoder +CONFIG_CADENCE_CODEC=y +CONFIG_SOF_COMPRESS_CODEC_PCM_DEC=y 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/configs/fuzz_asan.conf b/app/configs/fuzz_asan.conf index a4b504ea7617..81b76171cd09 100644 --- a/app/configs/fuzz_asan.conf +++ b/app/configs/fuzz_asan.conf @@ -1 +1,2 @@ CONFIG_ASAN=y +CONFIG_SYS_HEAP_SANITIZER_ASAN=y diff --git a/app/configs/fuzz_ubsan.conf b/app/configs/fuzz_ubsan.conf index ceae016bdc4f..b21e104ef677 100644 --- a/app/configs/fuzz_ubsan.conf +++ b/app/configs/fuzz_ubsan.conf @@ -1 +1,2 @@ CONFIG_UBSAN=y +CONFIG_SYS_HEAP_HARDENING_EXTREME=y 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..7b0b6cba60cc 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,14 @@ 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 + 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/ll_userspace_overlay.conf b/app/overlays/ptl/ll_userspace_overlay.conf new file mode 100644 index 000000000000..53b881320e6f --- /dev/null +++ b/app/overlays/ptl/ll_userspace_overlay.conf @@ -0,0 +1,39 @@ +# This is overlay configuration file to enable user-space LL audio +# pipeline support. This overlay is intended for development +# purposes. If user-space LL is enabled for some build target by +# default, the settings should be made in the SOF board file directly. + +# Enable user-space LL support in SOF +# ----------------------------------- + +CONFIG_SOF_USERSPACE_LL=y + +# make the drivers work in user-space +CONFIG_SOF_USERSPACE_INTERFACE_DMA=y +CONFIG_DAI_USERSPACE=y + +# Temporary settings that are needed currently to enable user-space LL +# -------------------------------------------------------------------- + +# problem with DSP panics due to illegal instruction hit in user-space if cold +# store execution is enabled. Disable it for now until rootcause is found. +CONFIG_COLD_STORE_EXECUTE_DRAM=n +CONFIG_COLD_STORE_EXECUTE_DEBUG=n + +# telemetry not yet user-space compatible +CONFIG_SOF_TELEMETRY_PERFORMANCE_MEASUREMENTS=n +CONFIG_SOF_TELEMETRY_IO_PERFORMANCE_MEASUREMENTS=n + +# disable loadable modules (hits privilege issues in user-space now) +CONFIG_LLEXT_STORAGE_WRITABLE=n +CONFIG_LLEXT_EXPERIMENTAL=n +CONFIG_MODULES=n + +# some of current boot tests interfere with user-space setup +CONFIG_SOF_BOOT_TEST_ALLOWED=n + +# misc features not yet compatible with user-space +CONFIG_CROSS_CORE_STREAM=n +CONFIG_INTEL_ADSP_MIC_PRIVACY=n +CONFIG_XRUN_NOTIFICATIONS_ENABLE=n +CONFIG_ZEPHYR_DP_SCHEDULER=n 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..aa9ee6960d8d 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,47 @@ static int sof_app_main(void) return 0; } +#if 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 + +#ifdef CONFIG_REBOOT +void sys_arch_reboot(int type) +{ +#if defined(QEMU_BOOT_TESTS) + qemu_xtensa_exit(type); +#endif + while (1) { + k_cpu_idle(); + } +} +#endif + #if CONFIG_ZTEST void test_main(void) { sof_app_main(); - k_sleep(K_FOREVER); +#if CONFIG_SOF_BOOT_TEST && (defined(QEMU_BOOT_TESTS) || CONFIG_SOF_BOOT_TEST_STANDALONE) + sof_run_boot_tests(); +#if defined(QEMU_BOOT_TESTS) + /* qemu_xtensa_exit() only exists for QEMU targets; a standalone + * boot test (e.g. native_sim) just returns from test_main() + */ + qemu_xtensa_exit(0); +#endif +#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/lmdk/cmake/build.cmake b/lmdk/cmake/build.cmake index 047a085257f8..df6a2955bfc8 100644 --- a/lmdk/cmake/build.cmake +++ b/lmdk/cmake/build.cmake @@ -11,7 +11,10 @@ include(${CMAKE_CURRENT_LIST_DIR}/config.cmake) # Build common module functions from sof to a static library add_library(sof STATIC) -target_include_directories(sof PRIVATE "${SOF_BASE}/src/include") +target_include_directories(sof PRIVATE + "${SOF_BASE}/src/include" + "${LMDK_BASE}/include" +) add_subdirectory("${SOF_BASE}/src/module" module_api) foreach(MODULE ${MODULES_LIST}) diff --git a/lmdk/include/rtos/panic.h b/lmdk/include/rtos/panic.h new file mode 100644 index 000000000000..baef9541ddb9 --- /dev/null +++ b/lmdk/include/rtos/panic.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * + * Copyright(c) 2026 Intel Corporation. All rights reserved. + * + * Author: Piotr Hoppe + */ + +#ifndef __LMDK_RTOS_PANIC_H__ +#define __LMDK_RTOS_PANIC_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/* runtime assertion */ +#define assert(x) do {if (!(x)) while (1);} while (0) + +#ifdef __cplusplus +} +#endif + +#endif /* __LMDK_RTOS_PANIC_H__ */ + diff --git a/lmdk/include/sof/math/numbers.h b/lmdk/include/sof/math/numbers.h new file mode 100644 index 000000000000..36bf29fab8f2 --- /dev/null +++ b/lmdk/include/sof/math/numbers.h @@ -0,0 +1,53 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * + * Copyright(c) 2016 Intel Corporation. All rights reserved. + * + * Author: Seppo Ingalsuo + * Liam Girdwood + * Keyon Jie + */ + +/* + * LMDK-local copy of . + * + * Loadable modules must not pull in the whole SOF src/include tree. This file + * mirrors the parts of the SOF header that the sink/source module API relies on + * (mainly the ROUND_UP/ROUND_DOWN/MIN/MAX helpers) so that the unchanged + * "#include " resolves inside the LMDK build without + * exposing the rest of the SOF headers. + */ + +#ifndef __SOF_MATH_NUMBERS_H__ +#define __SOF_MATH_NUMBERS_H__ + +#include + +/* Unsafe and portable macros for consistency with Zephyr. + * See SEI CERT-C PRE31-C + */ +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#define MAX(a, b) ((a) < (b) ? (b) : (a)) + +#define ROUND_DOWN(size, alignment) ({ \ + __typeof__(size) __size = (size); \ + __typeof__(alignment) __alignment = (alignment); \ + __size - (__size % __alignment); \ +}) + +#define ROUND_UP(size, alignment) ({ \ + __typeof__(size) __size = (size); \ + __typeof__(alignment) __alignment = (alignment); \ + ((__size + __alignment - 1) / __alignment) * __alignment; \ +}) + +#define ABS(a) ({ \ + __typeof__(a) __a = (a); \ + __a < 0 ? -__a : __a; \ +}) +#define SGN(a) ({ \ + __typeof__(a) __a = (a); \ + __a < 0 ? -1 : \ + __a > 0 ? 1 : 0; \ +}) + +#endif /* __SOF_MATH_NUMBERS_H__ */ diff --git a/posix/include/rtos/alloc.h b/posix/include/rtos/alloc.h index 7927f17394e5..c599afe08d82 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); /** @@ -89,40 +98,77 @@ static inline void *rballoc(uint32_t flags, size_t bytes) } /** - * 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. + * Frees the memory block. + * @param ptr Pointer to the memory block. */ -void *rbrealloc_align(void *ptr, uint32_t flags, size_t bytes, - size_t old_bytes, uint32_t alignment); +void rfree(void *ptr); /** - * Similar to rballoc_align(), returns resized buffer aligned to - * PLATFORM_DCACHE_ALIGN. + * Allocates memory block from the system heap reserved for the specified core. + * @param core Core id. + * @param bytes Size in bytes. */ -static inline void *rbrealloc(void *ptr, uint32_t flags, - size_t bytes, size_t old_bytes) +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) { - return rbrealloc_align(ptr, flags, bytes, old_bytes, - PLATFORM_DCACHE_ALIGN); } +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); + +/* Posix version of struct mod_alloc_ctx without vregion support */ +struct vregion; + +struct mod_alloc_ctx { + struct k_heap *heap; + struct vregion *vreg; +}; /** - * Frees the memory block. - * @param ptr Pointer to the memory block. + * Allocate memory from a mod_alloc_ctx context. + * Dummy version, only heap allocation is supported */ -void rfree(void *ptr); +static inline void *sof_ctx_alloc(struct mod_alloc_ctx *ctx, uint32_t flags, + size_t size, size_t alignment) +{ + return sof_heap_alloc(ctx ? ctx->heap : NULL, flags, size, alignment); +} /** - * Allocates memory block from the system heap reserved for the specified core. - * @param core Core id. - * @param bytes Size in bytes. + * Allocate zero-initialized memory from a mod_alloc_ctx context. + * @param ctx Allocation context. + * @param flags Allocation flags (SOF_MEM_FLAG_*). + * @param size Size in bytes. + * @param alignment Required alignment in bytes. + * @return Pointer to allocated memory or NULL on failure. */ -void *rzalloc_core_sys(int core, size_t bytes); +static inline void *sof_ctx_zalloc(struct mod_alloc_ctx *ctx, uint32_t flags, + size_t size, size_t alignment) +{ + void *ptr = sof_ctx_alloc(ctx, flags, size, alignment); + + if (ptr) + memset(ptr, 0, size); + + return ptr; +} + +/** + * Free memory allocated from a mod_alloc_ctx context. + * @param ctx Allocation context. + * @param ptr Pointer to free. + */ +static inline void sof_ctx_free(struct mod_alloc_ctx *ctx, void *ptr) +{ + if (!ptr) + return; + + sof_heap_free(ctx ? ctx->heap : NULL, ptr); +} /** * Calculates length of the null-terminated string. diff --git a/posix/include/rtos/idc.h b/posix/include/rtos/idc.h index a9a03258712f..3f4c48bf0f51 100644 --- a/posix/include/rtos/idc.h +++ b/posix/include/rtos/idc.h @@ -160,7 +160,7 @@ struct idc_msg { uint32_t extension; /**< extension value */ uint32_t core; /**< core id */ uint32_t size; /**< payload size in bytes */ - void *payload; /**< pointer to payload data */ + const void *payload; /**< pointer to payload data */ }; /** \brief IDC data. */ 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..f82b4169d73c 100644 --- a/posix/include/rtos/mutex.h +++ b/posix/include/rtos/mutex.h @@ -16,6 +16,7 @@ #include #define K_FOREVER ((k_timeout_t) { .ticks = 0xffffffff }) +#define K_MUTEX_DEFINE(name) struct k_mutex name struct k_mutex { struct k_spinlock lock; @@ -40,4 +41,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/task.h b/posix/include/rtos/task.h index 5aca574a5787..f73384b3cd58 100644 --- a/posix/include/rtos/task.h +++ b/posix/include/rtos/task.h @@ -18,6 +18,7 @@ struct comp_dev; struct sof; +struct schedule_data; /** \brief Predefined LL task priorities. */ #define SOF_TASK_PRI_HIGH 0 /* priority level 0 - high */ @@ -57,9 +58,10 @@ struct task { uint64_t start; /**< start time in [ms] since now (LL only) */ const struct sof_uuid_entry *uid; /**< Uuid */ uint16_t type; /**< type of the task (LL or EDF) */ - uint16_t priority; /**< priority of the task (used by LL) */ + int16_t priority; /**< priority of the task (used by LL); lower runs first */ uint16_t core; /**< execution core */ uint16_t flags; /**< custom flags */ + struct schedule_data *sch; /**< scheduler bound to task */ enum task_state state; /**< current state */ void *data; /**< custom data passed to all ops */ struct list_item list; /**< used by schedulers to hold tasks */ diff --git a/posix/include/rtos/userspace_helper.h b/posix/include/rtos/userspace_helper.h index dda440a33ac4..bb5d7670e9a1 100644 --- a/posix/include/rtos/userspace_helper.h +++ b/posix/include/rtos/userspace_helper.h @@ -19,6 +19,8 @@ #define APP_TASK_BSS #define APP_TASK_DATA +#define APP_SYSUSER_BSS +#define APP_SYSUSER_DATA struct sys_heap; @@ -26,7 +28,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 +36,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 +50,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..0d81c4cb552e 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. @@ -264,10 +265,11 @@ struct dma_info { size_t num_dmas; }; -struct audio_stream; -typedef int (*dma_process_func)(const struct audio_stream __sparse_cache *source, - uint32_t ioffset, struct audio_stream __sparse_cache *sink, - uint32_t ooffset, uint32_t source_samples, uint32_t chmap); +struct cir_buf_source; +struct cir_buf_sink; +typedef int (*dma_process_func)(const struct cir_buf_source *source, + uint32_t src_channels, struct cir_buf_sink *sink, + uint32_t sink_channels, size_t source_samples, uint32_t chmap); /** * \brief API to initialize a platform DMA controllers. @@ -511,13 +513,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..8833eb33e21e --- /dev/null +++ b/scripts/README.md @@ -0,0 +1,168 @@ +# 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 +* `-s` : Force sequential (one at a time) topology builds, for debugging. Topologies build in parallel by default. +* `-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/build-alsa-tools.sh b/scripts/build-alsa-tools.sh index 5e1e7f75d39d..01cdb761ee1b 100755 --- a/scripts/build-alsa-tools.sh +++ b/scripts/build-alsa-tools.sh @@ -16,8 +16,8 @@ declare -a REPOS=( # the repository if this commit ID is not found. Leave empty to skip. # This array order must align with REPO array above. declare -a COMMIT_ID=( - "df8f1cc1ec9d9ee15be5e2c23ad25b9389fd8766" - "09550cd393b1a7d307ee6f26637b1ed7bd275e38" + "08b532cd3da9ac8f683bcb4e4beb9b74c39c1782" + "9feb22ba45b48729729c8d194aaf1bc082a6842a" # Add more IDs here... ) diff --git a/scripts/build-tools.sh b/scripts/build-tools.sh index 6304265820cd..e2f1de4ad797 100755 --- a/scripts/build-tools.sh +++ b/scripts/build-tools.sh @@ -16,7 +16,7 @@ Attention: the list of selected shortcuts below is _not_ exhaustive. To build _everything_ don't select any particular target; this will build CMake's default target "ALL". -usage: $0 [-c|-f|-h|-l|-p|-t|-T|-X|-Y|-A] +usage: $0 [-c|-f|-h|-l|-p|-s|-t|-T|-X|-Y|-A] -h Display help -c Rebuild ctl/ @@ -25,6 +25,8 @@ usage: $0 [-c|-f|-h|-l|-p|-t|-T|-X|-Y|-A] -T Rebuild topology/ (not topology/development/! Use ALL) -X Rebuild topology1 only -Y Rebuild topology2 only + -s Force sequential (one at a time) topology builds, for debugging. + Normally topologies build in parallel with -j "$NO_PROCESSORS". -t Rebuild test/topology/ (or tools/test/topology/tplg-build.sh directly) -A Clone and rebuild local ALSA lib and utils. @@ -41,10 +43,19 @@ reconfigure_build() mkdir -p "$BUILD_TOOLS_DIR" ( cd "$BUILD_TOOLS_DIR" - cmake -GNinja -DCMAKE_BUILD_TYPE="$CMAKE_BUILD_TYPE" "${SOF_REPO}/tools" + cmake -GNinja -DCMAKE_BUILD_TYPE="$CMAKE_BUILD_TYPE" \ + -DTPLG_SEQUENTIAL_BUILD="$TPLG_SEQUENTIAL_BUILD" "${SOF_REPO}/tools" ) } +# Update the TPLG_SEQUENTIAL_BUILD CMake cache entry without wiping an +# existing, already configured build tree. +update_sequential_build_option() +{ + cmake -S "${SOF_REPO}/tools" -B "$BUILD_TOOLS_DIR" \ + -DTPLG_SEQUENTIAL_BUILD="$TPLG_SEQUENTIAL_BUILD" +} + make_tool() { # if no argument provided, all the tools will be built. Empty tool is @@ -95,12 +106,13 @@ main() { local DO_BUILD_ctl DO_BUILD_logger DO_BUILD_probes \ DO_BUILD_tests DO_BUILD_topologies1 DO_BUILD_topologies2 SCRIPT_DIR SOF_REPO \ - CMAKE_ONLY BUILD_ALL + CMAKE_ONLY BUILD_ALL TPLG_SEQUENTIAL_BUILD SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd) SOF_REPO=$(dirname "$SCRIPT_DIR") : "${BUILD_TOOLS_DIR:=$SOF_REPO/tools/build_tools}" : "${NO_PROCESSORS:=$(nproc)}" BUILD_ALL=false + TPLG_SEQUENTIAL_BUILD=OFF if [ $# -eq 0 ]; then BUILD_ALL=true @@ -120,11 +132,12 @@ main() # eval is a sometimes necessary evil # shellcheck disable=SC2034 - while getopts "cfhlptTCXYA" OPTION; do + while getopts "cfhlpstTCXYA" OPTION; do case "$OPTION" in c) DO_BUILD_ctl=true ;; l) DO_BUILD_logger=true ;; p) DO_BUILD_probes=true ;; + s) TPLG_SEQUENTIAL_BUILD=ON ;; t) DO_BUILD_tests=true ;; T) DO_BUILD_topologies1=true ; DO_BUILD_topologies2=true ;; X) DO_BUILD_topologies1=true ;; @@ -153,6 +166,10 @@ main() reconfigure_build } + # Switching -s on or off must apply even to an already configured, + # incremental build tree, so update the cache unconditionally. + update_sequential_build_option + if "$BUILD_ALL"; then # default CMake targets make_tool # trust set -e 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..0ff9cee45a94 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.3 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 - -# Use ubuntu24.04 as base for zephyr-lite -FROM ubuntu:24.04 as zephyr-lite - -# Copy needed files from base to zephyr-lite +# and install only needed toolchains in the future. +USER root + +RUN rm -rvf /opt/toolchains/zephyr-sdk-1.0.1/hosttools/sysroots && \ + rm -rvf /opt/fvps && \ + cd /opt/toolchains/zephyr-sdk-1.0.1/gnu && \ + rm -rvf arc* \ + micro* \ + mips* \ + nios* \ + risc* \ + sparc* \ + x86* \ + xtensa-espressif* \ + xtensa-sample* \ + xtensa-dc233c* + +# Use ubuntu26.04 as base for zephyr-lite +FROM ubuntu:26.04 as 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.1 +ENV ZSDK_VERSION=1.0.1 ENV ZEPHYR_TOOLCHAIN_VARIANT=zephyr + +CMD ["/bin/bash", "-l"] diff --git a/scripts/fuzz.sh b/scripts/fuzz.sh index e29a0eed1623..27ff96dc637f 100755 --- a/scripts/fuzz.sh +++ b/scripts/fuzz.sh @@ -21,6 +21,8 @@ Usage: -j n Number of concurrent -jobs=n. Defaults to 1. The value 0 uses the output of the 'nproc' command. -a arch The architecture to build against (i386, x86_64) + -d dfile Pass -dict=dfile to libFuzzer. A missing or empty dfile + is a non-fatal warning: the fuzzer then runs without it. Arguments after -- are passed as is to CMake (through west). When passing conflicting -DVAR='VAL UE1' -DVAR='VAL UE2' to CMake, @@ -93,9 +95,10 @@ main() local ARCH=i386 local IPC local BOARD + local DICT # Parse "$@". getopts stops after '--' - while getopts "i:hj:ps:a:o:t:b" opt; do + while getopts "i:hj:ps:a:o:t:bd:" opt; do case "$opt" in i) IPC="$OPTARG";; h) print_help; exit 0;; @@ -105,6 +108,7 @@ main() o) FUZZER_STDOUT="$OPTARG";; t) TEST_DURATION="$OPTARG";; a) ARCH="$OPTARG";; + d) DICT="$OPTARG";; b) BUILD_ONLY=true;; *) print_help; exit 1;; esac @@ -169,11 +173,23 @@ main() jobs_opts+=( -jobs="$JOBS" -close_fd_mask=1 ) fi + # Optional libFuzzer dictionary (-d). An absent or empty file is a + # non-fatal warning: the dictionary is a pure enhancement and must + # never block a fuzzing run (e.g. when its generator fails in CI). + local dict_opts=( ) + if [ -n "$DICT" ]; then + if [ -s "$DICT" ]; then + dict_opts+=( -dict="$DICT" ) + else + >&2 printf 'WARN: dictionary %s missing or empty; running without it\n' "$DICT" + fi + fi + date # Help is at: -help=1 ( set -x >"$FUZZER_STDOUT" build-fuzz/zephyr/zephyr.exe -max_total_time="$TEST_DURATION" \ - -verbosity=0 "${jobs_opts[@]}" ./fuzz_corpus ) || { + -verbosity=0 "${jobs_opts[@]}" "${dict_opts[@]}" ./fuzz_corpus ) || { ret=$? >&2 printf 'zephyr.exe returned: %d\n' $ret date @@ -189,7 +205,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/gen_fuzz_ipc3_dict.py b/scripts/gen_fuzz_ipc3_dict.py new file mode 100755 index 000000000000..a5e80411454e --- /dev/null +++ b/scripts/gen_fuzz_ipc3_dict.py @@ -0,0 +1,208 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(c) 2026 Intel Corporation. All rights reserved. +# +# Generate dictionary/ipc3.dict for the SOF IPC3 libFuzzer harness. +# +# libFuzzer's dictionary is consulted by mutation strategies that +# inject known byte sequences into the candidate inputs. For SOF +# IPC3 the single most valuable token is the 32-bit "cmd" dword that +# lives at offset 4 of every message (struct sof_ipc_cmd_hdr: the +# size dword is rewritten by the harness, so only cmd matters for +# dispatch). That dword packs two fields: +# +# glb_type : bits 28..31 (SOF_GLB_TYPE) +# cmd_type : bits 16..27 (SOF_CMD_TYPE) +# +# The firmware switches on glb_type first and then on cmd_type, so a +# token that is only a bare glb_type reaches a handler but stalls at +# its inner switch. We therefore emit, for every command group, the +# fully-combined "glb_type | cmd_type" dword that lands directly in a +# leaf handler, plus the bare glb_type values on their own (useful as +# a 4-byte splice when the fuzzer is building a message from scratch). +# +# The constants are harvested directly from src/include/ipc/header.h +# so the dictionary stays in sync with firmware code; only the +# SOF_GLB_TYPE() / SOF_CMD_TYPE() #defines are parsed, which keeps the +# harvester simple and robust. +# +# Usage: +# python3 scripts/gen_fuzz_ipc3_dict.py [-o dictionary/ipc3.dict] +# +# The output is regenerated on demand from the header (for example by +# CI, into a temporary file) and is intentionally not committed to the +# tree; regenerate it whenever the harvested header changes. + +import argparse +import re +import sys +from pathlib import Path + +# Path to the IPC3 header, relative to the SOF repo root (the checkout +# that contains this script's `scripts/` directory). +HEADER_REL = Path("src/include/ipc/header.h") + +# Bit layout of the command dword (see SOF_GLB_TYPE / SOF_CMD_TYPE in +# src/include/ipc/header.h). +GLB_TYPE_SHIFT = 28 +CMD_TYPE_SHIFT = 16 + +# Command groups: each SOF_CMD_TYPE() value is only meaningful when +# OR-ed with the global type that selects its handler. This table +# maps a cmd #define name prefix to the SOF_GLB_TYPE() #define that +# dispatches it. Order controls the layout of the emitted file. +CMD_GROUPS = [ + ("SOF_IPC_TPLG_", "SOF_IPC_GLB_TPLG_MSG"), + ("SOF_IPC_PM_", "SOF_IPC_GLB_PM_MSG"), + ("SOF_IPC_COMP_", "SOF_IPC_GLB_COMP_MSG"), + ("SOF_IPC_DAI_", "SOF_IPC_GLB_DAI_MSG"), + ("SOF_IPC_STREAM_", "SOF_IPC_GLB_STREAM_MSG"), + ("SOF_IPC_TRACE_", "SOF_IPC_GLB_TRACE_MSG"), + ("SOF_IPC_PROBE_", "SOF_IPC_GLB_PROBE"), + ("SOF_IPC_DEBUG_", "SOF_IPC_GLB_DEBUG"), + ("SOF_IPC_TEST_", "SOF_IPC_GLB_TEST"), +] + +GLB_RE = re.compile( + r"#define\s+(?P<name>SOF_IPC_\w+)\s+SOF_GLB_TYPE\(\s*" + r"(?P<value>0x[0-9a-fA-F]+|\d+)U?L?\s*\)" +) +CMD_RE = re.compile( + r"#define\s+(?P<name>SOF_IPC_\w+)\s+SOF_CMD_TYPE\(\s*" + r"(?P<value>0x[0-9a-fA-F]+|\d+)U?L?\s*\)" +) + + +def u32_le(value): + """Encode an unsigned 32-bit value as 4 little-endian bytes.""" + value &= 0xFFFFFFFF + return bytes([ + value & 0xFF, + (value >> 8) & 0xFF, + (value >> 16) & 0xFF, + (value >> 24) & 0xFF, + ]) + + +def fmt_dict_entry(name, raw_bytes): + """Format a libFuzzer dictionary line: name="\\xNN\\xNN...". + + libFuzzer accepts ASCII printables unescaped; we escape everything + except space..~ (excluding the quote and backslash) which keeps the + file readable while remaining unambiguous. + """ + pieces = [] + for b in raw_bytes: + if 0x20 <= b <= 0x7E and b not in (0x22, 0x5C): + pieces.append(chr(b)) + else: + pieces.append(f"\\x{b:02x}") + return f'{name}="{"".join(pieces)}"' + + +def harvest(repo_root): + """Parse header.h, returning (glb_defs, cmd_defs) name->value dicts.""" + path = repo_root / HEADER_REL + if not path.is_file(): + print(f"error: {path} not found", file=sys.stderr) + sys.exit(1) + text = path.read_text() + + glb_defs = {} + for m in GLB_RE.finditer(text): + glb_defs[m.group("name")] = int(m.group("value"), 0) << GLB_TYPE_SHIFT + + cmd_defs = {} + for m in CMD_RE.finditer(text): + cmd_defs[m.group("name")] = int(m.group("value"), 0) << CMD_TYPE_SHIFT + + return glb_defs, cmd_defs + + +def build_sections(glb_defs, cmd_defs): + """Return list of (section_label, [(name, raw_bytes), ...]).""" + sections = [] + + # Bare global message types. + glb_entries = [(name, u32_le(val)) for name, val in glb_defs.items()] + if glb_entries: + sections.append(("Global message types (glb_type only)", glb_entries)) + + # Combined glb_type | cmd_type dwords, one section per group. + for prefix, glb_name in CMD_GROUPS: + glb_val = glb_defs.get(glb_name) + if glb_val is None: + print(f"warning: {glb_name} not found in header, " + f"skipping group {prefix}*", file=sys.stderr) + continue + entries = [] + for name, cmd_val in cmd_defs.items(): + if not name.startswith(prefix): + continue + entries.append((name, u32_le(glb_val | cmd_val))) + if entries: + sections.append((f"{glb_name} commands", entries)) + + return sections + + +def render(sections): + out_lines = [ + "# SOF IPC3 libFuzzer dictionary", + "#", + "# Generated by scripts/gen_fuzz_ipc3_dict.py from the IPC3", + "# header src/include/ipc/header.h. Do not edit by hand;", + "# regenerate instead.", + "#", + "# Each entry is a 4-byte little-endian command dword. Command", + "# entries combine the global type and the command type so they", + "# land directly in a leaf handler when libFuzzer's CMP /", + "# dictionary mutators splice them into the cmd field at offset 4", + "# of a message.", + "", + ] + for label, entries in sections: + out_lines.append(f"# --- {label} ---") + for name, raw in entries: + out_lines.append(fmt_dict_entry(name, raw)) + out_lines.append("") + return "\n".join(out_lines) + + +def main(): + parser = argparse.ArgumentParser( + description="Generate the SOF IPC3 libFuzzer dictionary from the " + "in-tree IPC3 header.") + parser.add_argument( + "-o", "--output", + help="Output path (default: stdout)", + ) + parser.add_argument( + "--repo-root", + default=None, + help="SOF repository root (default: auto-detected from script " + "location)", + ) + args = parser.parse_args() + + if args.repo_root: + repo_root = Path(args.repo_root).resolve() + else: + # Script lives at <repo>/scripts/gen_fuzz_ipc3_dict.py. + repo_root = Path(__file__).resolve().parents[1] + + glb_defs, cmd_defs = harvest(repo_root) + sections = build_sections(glb_defs, cmd_defs) + if not sections: + print("error: no dictionary entries harvested; refusing to write " + "an empty dictionary", file=sys.stderr) + sys.exit(1) + text = render(sections) + if args.output: + Path(args.output).write_text(text) + else: + sys.stdout.write(text) + + +if __name__ == "__main__": + main() diff --git a/scripts/gen_fuzz_ipc4_dict.py b/scripts/gen_fuzz_ipc4_dict.py new file mode 100755 index 000000000000..cecb52c3e6d9 --- /dev/null +++ b/scripts/gen_fuzz_ipc4_dict.py @@ -0,0 +1,268 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(c) 2026 Intel Corporation. All rights reserved. +# +# Generate dictionary/ipc4.dict for the SOF IPC4 libFuzzer harness. +# +# libFuzzer's dictionary is consulted by mutation strategies that +# inject known byte sequences into the candidate inputs. For SOF +# IPC4 the most valuable seed values are: +# +# * The 4-byte "primary" message header dat for each well-known +# dispatch path: msg_tgt|rsp|type encoded into bits 24-30 with +# the rest left zero so libFuzzer can mutate the lower 24 bits +# (which carry module_id / instance_id / global type parameters). +# * Small enum constants that appear inline in payloads +# (pipeline state values, pipeline extension object IDs, +# large_config param IDs). +# +# The constants are harvested directly from sof/src/include/ipc4/*.h +# so the dictionary stays in sync with firmware code; only enums +# whose entries are all integer literals (decimal or hex) are +# parsed, which keeps the harvester simple and robust. +# +# Usage: +# python3 scripts/gen_fuzz_ipc4_dict.py [-o dictionary/ipc4.dict] +# +# The output is regenerated on demand from the headers (for example by +# CI, into a temporary file) and is intentionally not committed to the +# tree; regenerate it whenever the harvested headers change. + +import argparse +import re +import sys +from pathlib import Path + +# Path to the IPC4 public include directory, relative to the SOF repo +# root (the checkout that contains this script's `scripts/` directory). +IPC4_INC_REL = Path("src/include/ipc4") + +# Enums we want to harvest, keyed by the source header (relative to +# IPC4_INC_REL) and the C enum tag. Each entry also records what +# kind of dictionary token to emit: +# "global_pri" - 4-byte LE pri value with msg_tgt=0, rsp=0, type=N +# "module_pri" - 4-byte LE pri value with msg_tgt=1, rsp=0, type=N +# "u32" - raw 4-byte LE value of N +ENUM_SOURCES = [ + ("header.h", "ipc4_message_type", "global_pri"), + ("module.h", "sof_ipc4_module_type", "module_pri"), + ("module.h", "ipc4_mod_init_data_glb_id", "u32"), + ("pipeline.h", "ipc4_pipeline_state", "u32"), + ("pipeline.h", "ipc4_pipeline_ext_obj_id", "u32"), + ("pipeline.h", "ipc4_pipeline_priority", "u32"), + ("base_fw.h", "ipc4_basefw_params", "u32"), + ("base_fw.h", "ipc4_fw_config_params", "u32"), + ("base_fw.h", "ipc4_hw_config_params", "u32"), + ("base_fw.h", "ipc4_memory_type", "u32"), + ("base_fw.h", "ipc4_clock_src", "u32"), + ("notification.h", "sof_ipc4_notification_type", "u32"), + ("notification.h", "sof_ipc4_resource_event_type", "u32"), + ("notification.h", "sof_ipc4_resource_type", "u32"), + ("error_status.h", "ipc4_status", "u32"), +] + +# Bit layout of struct ipc4_message_request::primary (see +# src/include/ipc4/header.h): +# rsvd0 : 24 bits 0..23 +# type : 5 bits 24..28 +# rsp : 1 bit 29 +# msg_tgt : 1 bit 30 +# reserved: 1 bit 31 +PRI_TYPE_SHIFT = 24 +PRI_RSP_SHIFT = 29 +PRI_MSG_TGT_SHIFT = 30 + +ENUM_BLOCK_RE = re.compile( + r"enum\s+(?P<name>[A-Za-z_][A-Za-z0-9_]*)\s*\{(?P<body>.*?)\}\s*;", + re.DOTALL, +) + +# Match `NAME` or `NAME = VALUE` inside an enum body. VALUE may be +# decimal or hex. Any non-numeric initialiser causes us to skip the +# whole enum (we don't resolve macros / arithmetic). +ENUM_ENTRY_RE = re.compile( + r""" + ^\s* + (?P<name>[A-Za-z_][A-Za-z0-9_]*) + (?:\s*=\s*(?P<value>0x[0-9a-fA-F]+|\d+))? + \s*,?\s*(?://.*|/\*.*?\*/)?\s*$ + """, + re.VERBOSE, +) + + +def find_enum(header_text, enum_tag): + """Return the enum body text for `enum enum_tag { ... }`, else None.""" + for m in ENUM_BLOCK_RE.finditer(header_text): + if m.group("name") == enum_tag: + return m.group("body") + return None + + +def parse_enum(body): + """Parse a C enum body of integer-literal entries. + + Returns a list of (name, value) pairs. If an entry uses a + non-literal initialiser (macro, arithmetic, cross-reference to + another enum name) we stop parsing at that point and return + everything harvested so far -- typically the trailing sentinels + (``_MAX``, ``_COUNT``) of an otherwise integer-literal enum. + Returning a truncated list is preferable to silently emitting + wrong values for auto-incremented entries that follow.""" + out = [] + next_value = 0 + body = re.sub(r"/\*.*?\*/", "", body, flags=re.DOTALL) + body = re.sub(r"//[^\n]*", "", body) + for raw_line in body.splitlines(): + line = raw_line.strip() + if not line: + continue + m = ENUM_ENTRY_RE.match(line) + if not m: + # First non-literal entry: stop here. Anything that + # follows would need correct auto-increment tracking + # which we cannot guarantee once the count is broken. + break + name = m.group("name") + if m.group("value") is not None: + value = int(m.group("value"), 0) + else: + value = next_value + out.append((name, value)) + next_value = value + 1 + return out + + +def u32_le(value): + """Encode an unsigned 32-bit value as 4 little-endian bytes.""" + value &= 0xFFFFFFFF + return bytes([ + value & 0xFF, + (value >> 8) & 0xFF, + (value >> 16) & 0xFF, + (value >> 24) & 0xFF, + ]) + + +def fmt_dict_entry(name, raw_bytes): + """Format a libFuzzer dictionary line: name="\\xNN\\xNN...". + + libFuzzer accepts ASCII printables unescaped; we escape + everything except space..~ excluding the quote and backslash, + which keeps the file readable while remaining unambiguous. + """ + pieces = [] + for b in raw_bytes: + if 0x20 <= b <= 0x7E and b not in (0x22, 0x5C): + pieces.append(chr(b)) + else: + pieces.append(f"\\x{b:02x}") + return f'{name}="{"".join(pieces)}"' + + +def encode(kind, value): + if kind == "global_pri": + return u32_le(value << PRI_TYPE_SHIFT) + if kind == "module_pri": + return u32_le((1 << PRI_MSG_TGT_SHIFT) | (value << PRI_TYPE_SHIFT)) + if kind == "u32": + return u32_le(value) + raise ValueError(f"unknown kind: {kind}") + + +def harvest(repo_root): + """Return list of (section_label, [(name, raw_bytes), ...]).""" + sections = [] + for fname, enum_tag, kind in ENUM_SOURCES: + path = repo_root / IPC4_INC_REL / fname + if not path.is_file(): + print(f"error: {path} not found", file=sys.stderr) + sys.exit(1) + text = path.read_text() + body = find_enum(text, enum_tag) + if body is None: + print(f"warning: enum {enum_tag} not found in {fname}", + file=sys.stderr) + continue + entries = parse_enum(body) + if not entries: + print(f"warning: enum {enum_tag} in {fname} yielded no " + "literal entries, skipping", file=sys.stderr) + continue + encoded = [] + for name, value in entries: + # Skip MAX / COUNT sentinels which are not real dispatch + # values; keep everything else even if the value collides + # with another entry (libFuzzer dedups automatically). + if (name.endswith("_MAX") + or name.endswith("_MAX_IXC_MESSAGE_TYPE") + or name.endswith("_PARAMS_COUNT")): + continue + # The primary-header `type` field is only 5 bits wide; a + # value that does not fit cannot be a real dispatch type and + # would corrupt the rsp / msg_tgt bits, so drop it regardless + # of the enumerator's name (robust to header typo fixes). + if kind in ("global_pri", "module_pri") and not 0 <= value < 32: + continue + encoded.append((name, encode(kind, value))) + sections.append((f"{fname}::{enum_tag} ({kind})", encoded)) + return sections + + +def render(sections): + out_lines = [ + "# SOF IPC4 libFuzzer dictionary", + "#", + "# Generated by scripts/gen_fuzz_ipc4_dict.py from the IPC4", + "# public headers under src/include/ipc4/. Do not edit by", + "# hand; regenerate instead.", + "#", + "# Each entry is a 4-byte little-endian value drawn from an IPC4", + "# enum, encoded so libFuzzer's CMP / dictionary mutators can", + "# splice it into candidate inputs at any 4-byte-aligned offset.", + "", + ] + for label, entries in sections: + out_lines.append(f"# --- {label} ---") + for name, raw in entries: + out_lines.append(fmt_dict_entry(name, raw)) + out_lines.append("") + return "\n".join(out_lines) + + +def main(): + parser = argparse.ArgumentParser( + description="Generate the SOF IPC4 libFuzzer dictionary from the " + "in-tree IPC4 headers.") + parser.add_argument( + "-o", "--output", + help="Output path (default: stdout)", + ) + parser.add_argument( + "--repo-root", + default=None, + help="SOF repository root (default: auto-detected from script " + "location)", + ) + args = parser.parse_args() + + if args.repo_root: + repo_root = Path(args.repo_root).resolve() + else: + # Script lives at <repo>/scripts/gen_fuzz_ipc4_dict.py. + repo_root = Path(__file__).resolve().parents[1] + + sections = harvest(repo_root) + if not any(entries for _, entries in sections): + print("error: no dictionary entries harvested; refusing to write " + "an empty dictionary", file=sys.stderr) + sys.exit(1) + text = render(sections) + if args.output: + Path(args.output).write_text(text) + else: + sys.stdout.write(text) + + +if __name__ == "__main__": + main() 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/llext_link_helper.py b/scripts/llext_link_helper.py index f10777c9918f..3b6c2d451e7c 100755 --- a/scripts/llext_link_helper.py +++ b/scripts/llext_link_helper.py @@ -144,8 +144,7 @@ def main(): # critical functions in a .cold ELF section, read-only data in a # .coldrodata ELF section, etc. When compiling and linking such # functions, an additional .cold.literal section is automatically - # created. Note, that for some reason the compiler also marks .cold as - # executable. + # created. # This script links those sections at address 0. We could hard-code # section names, but so far we choose to only link .text the "original" # way and all other executable sections we link at 0. For data sections @@ -168,6 +167,8 @@ def main(): dram_addr += section.header['sh_size'] + dram_addr = align_up(dram_addr, 0x1000) + for section in readonly_dram: s_alignment = section.header['sh_addralign'] s_name = section.name 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..d0ab97b8466f --- /dev/null +++ b/scripts/sof-crash-decode.py @@ -0,0 +1,583 @@ +#!/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 +import ast +import operator + + +# Largest shift we accept: linker addresses/sizes fit well under 2**64, so a +# bigger shift is nonsensical and only serves to build a huge integer. +_MAX_SHIFT = 64 +# Reject any literal or intermediate result beyond 64 bits, and cap the source +# length, so a crafted expression can't build a huge integer or stall the parse. +_MAX_VALUE = 1 << 64 +_MAX_EXPR_LEN = 256 + + +def _trunc_div(a, b): + # truncate toward zero (C semantics); Python's // floors instead + q = abs(a) // abs(b) + return -q if (a < 0) != (b < 0) else q + + +def _lshift(a, b): + # reject absurd shift counts to avoid building a massive integer + if b < 0 or b > _MAX_SHIFT: + raise ValueError("shift count out of range") + return a << b + + +# Operators allowed when evaluating arithmetic from an untrusted linker script. +_SAFE_OPS = { + ast.Add: operator.add, ast.Sub: operator.sub, + ast.Mult: operator.mul, ast.Div: _trunc_div, + ast.Mod: operator.mod, ast.LShift: _lshift, + ast.RShift: operator.rshift, ast.BitOr: operator.or_, + ast.BitAnd: operator.and_, ast.BitXor: operator.xor, + ast.USub: operator.neg, ast.UAdd: operator.pos, +} + + +def safe_eval_int(expr): + """Evaluate an integer arithmetic expression without executing code. + + A crash bundle's linker.cmd is attacker-controllable, so its MEMORY + expressions must never be passed to eval(). Only integer literals and + basic arithmetic operators are accepted, and values are bounded to 64 + bits. Anything unsupported or out of range raises an exception + (ValueError, or ZeroDivisionError on '/ 0'); the caller treats any such + failure as 'skip this entry'. + """ + def _check(value): + if abs(value) >= _MAX_VALUE: + raise ValueError("value out of range") + return value + + def _eval(node): + if isinstance(node, ast.Expression): + return _eval(node.body) + if isinstance(node, ast.Constant): + # reject bool (a subclass of int) and everything non-integer + if type(node.value) is int: + return _check(node.value) + raise ValueError("non-integer constant") + if isinstance(node, ast.BinOp) and type(node.op) in _SAFE_OPS: + return _check(_SAFE_OPS[type(node.op)](_eval(node.left), + _eval(node.right))) + if isinstance(node, ast.UnaryOp) and type(node.op) in _SAFE_OPS: + return _check(_SAFE_OPS[type(node.op)](_eval(node.operand))) + raise ValueError("unsupported expression") + + if len(expr) > _MAX_EXPR_LEN: + raise ValueError("expression too long") + return _eval(ast.parse(expr, mode='eval')) + +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|LCOUNT|SAR|EPC\d+|EPS\d+|PS|THREADPTR)$', reg): + registers[reg] = val + else: + # Standard format + # regex for registers: we want standalone pairs like PC 0x123 or A0 0x123 or EXCCAUSE 9 + # Also handle (nil) for zero-valued registers like A4 (nil) or THREADPTR (nil) + reg_pattern = re.compile(r'\b([A-Z0-9]+)\s+(?:(0x[0-9a-fA-F]+)\b|(\d+)\b|\(nil\))') + for match in reg_pattern.finditer(content): + reg = match.group(1) + hex_str = match.group(2) + dec_str = match.group(3) + if hex_str: + val = int(hex_str, 16) + elif dec_str: + val = int(dec_str) + else: + val = 0 + + # Keep only known registers or likely candidates + if re.match(r'^(PC|PR|SP|A\d+|AR\d+|EXCCAUSE|VADDR|LBEG|LEND|LCOUNT|SAR|EPC\d+|EPS\d+|PS|THREADPTR)$', reg): + registers[reg] = val + + # Backtrace parsing: support both single-line and multi-line formats + # Single-line: "Backtrace: 0xPC:0xSP 0xPC:0xSP ..." + # Multi-line: "Backtrace:\n0xPC:0xSP\n0xPC:0xSP\n..." + bt_idx = content.find("Backtrace:") + if bt_idx != -1: + bt_rest = content[bt_idx + len("Backtrace:"):] + bt_pattern = re.compile(r'(0x[0-9a-fA-F]+):(0x[0-9a-fA-F]+)') + for match in bt_pattern.finditer(bt_rest): + # Stop if we hit a line that doesn't look like backtrace data + preceding = bt_rest[:match.start()] + # Allow only whitespace/newlines between entries + last_newline = preceding.rfind('\n') + if last_newline != -1: + line_before = preceding[last_newline + 1:match.start()].strip() + if line_before and not bt_pattern.match(line_before): + break + 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 = safe_eval_int(org_expr) + len_val = safe_eval_int(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..4fc40ac439e7 --- /dev/null +++ b/scripts/sof-qemu-run.py @@ -0,0 +1,247 @@ +#!/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 pathlib +import shutil + +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'.") + parser.add_argument("--valgrind", action="store_true", help="Run the executable under Valgrind (only valid for native_sim).") + args = parser.parse_args() + + # Resolve to an absolute path; args.build_dir may be passed relative to + # the caller's working directory. + 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 + 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) + + # Detect the board configuration using zcmake (same approach as xtensa-build-zephyr.py) + is_native_sim = False + try: + # zcmake lives in the Zephyr tree, so its location is added to sys.path + # at runtime (same approach as xtensa-build-zephyr.py) and the import is + # kept local and guarded so a missing Zephyr tree degrades gracefully. + sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), + '..', '..', 'zephyr', 'scripts', 'west_commands')) + import zcmake + cmake_cache = zcmake.CMakeCache.from_build_dir(pathlib.Path(build_dir)) + board = cmake_cache.get("CACHED_BOARD") or cmake_cache.get("BOARD") or "" + if "native_sim" in board: + is_native_sim = True + except Exception: + pass + + # Determine execution command + # If the user is running the python script directly from outside the workspace, we need to provide the source directory. + # But if west finds it automatically (or we are in the build dir), providing `-s` might clear the CACHED_BOARD config. + run_cmd = [west_path, "-v", "build", "-d", build_dir] + + # Check if we are physically sitting inside the build directory + if os.path.abspath(".") != os.path.abspath(build_dir): + # We need to explicitly supply the app source to prevent west from crashing + app_source_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "app")) + run_cmd.extend(["-s", app_source_dir]) + + run_cmd.extend(["-t", "run"]) + + if args.valgrind: + if not is_native_sim: + print("[sof-qemu-run] Error: --valgrind is only supported for the native_sim board.") + sys.exit(1) + + print("[sof-qemu-run] Rebuilding before valgrind...") + subprocess.run([west_path, "build", "-d", build_dir], check=True) + + valgrind_path = shutil.which("valgrind") + if not valgrind_path: + print("[sof-qemu-run] Error: 'valgrind' command not found in PATH.") + sys.exit(1) + + exe_path = os.path.join(build_dir, "zephyr", "zephyr.exe") + if not os.path.isfile(exe_path): + print(f"[sof-qemu-run] Error: Executable not found at {exe_path}") + sys.exit(1) + + run_cmd = [valgrind_path, exe_path] + + child = pexpect.spawn(run_cmd[0], run_cmd[1:], 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: + if is_native_sim: + print("\n[sof-qemu-run] No crash detected. (Skipping QEMU monitor interaction for native_sim)") + 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..ef915fbb1318 --- /dev/null +++ b/scripts/sof-qemu-run.sh @@ -0,0 +1,61 @@ +#!/bin/bash +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(c) 2026 Intel Corporation. All rights reserved. + +# Get the directory of this script +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +SOF_WORKSPACE="$(dirname "$(dirname "$SCRIPT_DIR")")" + +TARGET="native_sim" +VALGRIND_ARG="" + +usage() { + cat <<EOF +Usage: $0 [OPTIONS] [TARGET] + +Options: + -h, --help Show this help message and exit + --valgrind Run under valgrind + +TARGET The QEMU target name (e.g., native_sim, qemu_xtensa, qemu_xtensa_mmu). + The build directory will be resolved to 'build-<TARGET>' relative + to the workspace. (default: native_sim) +EOF + exit 0 +} + +while [[ $# -gt 0 ]]; do + case $1 in + -h|--help) + usage + ;; + --valgrind) + VALGRIND_ARG="--valgrind" + ;; + *) + # Allow users who pass the directory name directly out of habit to still work + if [[ "$1" == build-* ]]; then + TARGET="${1#build-}" + else + TARGET="$1" + fi + ;; + esac + shift +done + +BUILD_DIR="${SOF_WORKSPACE}/build-${TARGET}" + +# 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. + +# 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 + +# 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}" $VALGRIND_ARG 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..f20c95f7262a 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,19 @@ 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" + ), + "acp_7_x" : PlatformConfig( + "amd", "acp_7_x_adsp/acp_7_x", + f"RI-2022.9{xtensa_tools_version_postfix}", + "ACP73x_HiFi5_NNE_PROD", + RIMAGE_KEY = "key param ignored by acp_7_x", + aliases = ["acp7x"] + ), # MediaTek platforms # (move to platform_configs_all on next Zephyr SDK release after 0.17.0) "mt8195" : PlatformConfig( @@ -145,6 +146,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 +198,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 +229,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 +243,18 @@ 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" + ), + "native_sim" : PlatformConfig( + "zephyr", "native_sim", + "", "", "zephyr" + ), } platform_configs = platform_configs_all.copy() @@ -688,9 +721,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 +922,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 +974,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 @@ -943,6 +990,22 @@ def build_platforms(): env=platf_build_environ, sof_log_env=True) print() + # Delete the LLEXT VMA accumulator file before every build. + # llext_offset_calc.py uses module_size as a persistent counter + # across cmake custom-command invocations. If ninja is invoked + # more than once without a pristine rebuild this counter keeps + # accumulating, pushing pre-linked VMAs beyond the valid LLEXT + # virtual region and causing sys_mm_drv_map_region() to fail + # silently at runtime (IPC4_MOD_NOT_INITIALIZED error 104). + # llext_offset_calc.py already handles a missing file as size=0, + # so deletion is the simplest and most reliable reset. + zephyr_build_dir = pathlib.Path(abs_build_dir) / "zephyr" + if zephyr_build_dir.is_dir(): + acc_path = zephyr_build_dir / "module_size" + if acc_path.is_file(): + acc_path.unlink() + print(f"Deleted LLEXT VMA accumulator: {acc_path}") + # Build try: execute_command(build_cmd, cwd=west_top, env=platf_build_environ, sof_log_env=True) @@ -1135,8 +1198,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) + # loses file owner and group - file is commonly accessible, don't install for qemu. + if platform not in ("qemu_xtensa", "qemu_xtensa_mmu", "native_sim"): + 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 +1350,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', 'acp_7_x'] +RI_INFO_UNSUPPORTED += ['mt8186', 'mt8188', 'mt8195', 'mt8196', 'mt8365'] +RI_INFO_UNSUPPORTED += ['qemu_xtensa', 'qemu_xtensa_mmu', 'native_sim'] + # For temporary workarounds. Unlike _UNSUPPORTED above, the platforms below will print a warning. RI_INFO_FIXME = [ ] diff --git a/smex/elf.c b/smex/elf.c index eb0994530c6b..6069f12f6b92 100644 --- a/smex/elf.c +++ b/smex/elf.c @@ -44,8 +44,29 @@ static int elf_read_sections(struct elf_module *module, bool verbose) return count > 0 ? -ENODATA : -errno; } - /* read in strings */ - module->strings = calloc(1, section[hdr->shstrndx].size); + /* the string-table section index comes from the ELF header and is used + * to index section[]; reject an out-of-range value before dereferencing + */ + if (hdr->shstrndx >= hdr->shnum) { + fprintf(stderr, "error: %s invalid shstrndx %u >= shnum %u\n", + module->elf_file, hdr->shstrndx, hdr->shnum); + return -ENOEXEC; + } + + /* a zero-size string section leaves module->strings unusable and would + * break later string lookups; reject it explicitly + */ + if (section[hdr->shstrndx].size == 0) { + fprintf(stderr, "error: %s has zero-size string section\n", + module->elf_file); + return -ENOEXEC; + } + + /* read in strings; allocate one extra byte (calloc zeroes it) so the + * table is always NUL-terminated and string lookups cannot run off the + * end even if the section itself lacks a terminator + */ + module->strings = calloc(1, section[hdr->shstrndx].size + 1); if (!module->strings) { fprintf(stderr, "error: failed %s to read ELF strings for %d\n", module->elf_file, -errno); @@ -67,6 +88,18 @@ static int elf_read_sections(struct elf_module *module, bool verbose) return count > 0 ? -ENODATA : -errno; } + /* every section name is used as an offset into the string table; make + * sure each stays within it so later "module->strings + name" reads + * cannot run past the table + */ + for (i = 0; i < hdr->shnum; i++) { + if (section[i].name >= section[hdr->shstrndx].size) { + fprintf(stderr, "error: %s section %d name offset %u out of range\n", + module->elf_file, i, section[i].name); + return -ENOEXEC; + } + } + module->bss_index = elf_find_section(module, ".bss"); if (module->bss_index < 0) { fprintf(stderr, "Can't find .bss section in %s", @@ -392,8 +425,22 @@ int elf_find_section(const struct elf_module *module, const char *name) return -EINVAL; } + if (hdr->shstrndx >= hdr->shnum) { + fprintf(stderr, "error: invalid shstrndx %u >= shnum %u\n", + hdr->shstrndx, hdr->shnum); + return -EINVAL; + } + section = &module->section[hdr->shstrndx]; + /* a zero-size string section would make the buffer[size - 1] + * NUL-termination below write before the allocation + */ + if (section->size == 0) { + fprintf(stderr, "error: zero-size string section\n"); + return -EINVAL; + } + /* alloc data data */ buffer = calloc(1, section->size); if (!buffer) @@ -482,6 +529,10 @@ int elf_read_section(const struct elf_module *module, const char *section_name, error: free(*dst_buff); + /* clear the caller's pointer so a caller cleanup path (e.g. ldc.c's + * "if (buffer) free(buffer)") does not free the same buffer again + */ + *dst_buff = NULL; return ret; } diff --git a/smex/ldc.c b/smex/ldc.c index 4eccdb1c0918..a44b67a05973 100644 --- a/smex/ldc.c +++ b/smex/ldc.c @@ -26,6 +26,13 @@ static int fw_version_copy(const struct elf_module *src, if (section_size < 0) return section_size; + if ((size_t)section_size < sizeof(struct sof_ipc_fw_ready)) { + fprintf(stderr, "error: .fw_ready section too small: %d\n", + section_size); + free(buffer); + return -EINVAL; + } + memcpy(&header->version, &((struct sof_ipc_fw_ready *)buffer)->version, sizeof(header->version)); @@ -50,13 +57,37 @@ static int fw_version_copy(const struct elf_module *src, return section_size; ext_hdr = (struct ext_man_elem_header *)buffer; - while ((uintptr_t)ext_hdr < (uintptr_t)buffer + section_size) { + while ((uintptr_t)ext_hdr + sizeof(*ext_hdr) <= + (uintptr_t)buffer + section_size) { if (ext_hdr->type == EXT_MAN_ELEM_DBG_ABI) { + /* make sure the whole dbg-abi element is within the + * section before reading it + */ + if (ext_hdr->elem_size < sizeof(struct ext_man_dbg_abi) || + (uintptr_t)ext_hdr + sizeof(struct ext_man_dbg_abi) > + (uintptr_t)buffer + section_size) { + fprintf(stderr, "error: %s truncated dbg-abi element\n", + src->elf_file); + free(buffer); + return -ENOEXEC; + } header->version.abi_version = ((struct ext_man_dbg_abi *) ext_hdr)->dbg_abi.abi_dbg_version; break; } + /* a malformed element size would loop forever (0) or advance + * the cursor past the section; reject the image rather than + * silently stopping + */ + if (ext_hdr->elem_size == 0 || + (uintptr_t)ext_hdr + ext_hdr->elem_size > + (uintptr_t)buffer + section_size) { + fprintf(stderr, "error: %s malformed ext-manifest element\n", + src->elf_file); + free(buffer); + return -ENOEXEC; + } //move to the next entry ext_hdr = (struct ext_man_elem_header *) ((uint8_t *)ext_hdr + ext_hdr->elem_size); 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..1279dd26c924 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 @@ -13,13 +14,16 @@ CONFIG_COMP_MFCC=y CONFIG_COMP_MODULE_ADAPTER=y CONFIG_COMP_MULTIBAND_DRC=y CONFIG_COMP_MUX=y +CONFIG_COMP_PHASE_VOCODER=y CONFIG_COMP_RTNR=y 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/arch/xtensa/configs/acp_7_x_defconfig b/src/arch/xtensa/configs/acp_7_x_defconfig new file mode 100644 index 000000000000..d978611fadde --- /dev/null +++ b/src/arch/xtensa/configs/acp_7_x_defconfig @@ -0,0 +1,32 @@ +CONFIG_ACP_7_X=y +CONFIG_RIMAGE_SIGNING_SCHEMA="acp_7_3" +CONFIG_TRACE_CHANNEL=7 +CONFIG_WRAP_ACTUAL_POSITION=y +CONFIG_CORE_COUNT=1 +CONFIG_FORMAT_CONVERT_HIFI3=n +CONFIG_LP_SRAM=n +CONFIG_HAVE_AGENT=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_SWITCH=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_MUX=n +CONFIG_COMP_SEL=n +CONFIG_COMP_MIXER=n +CONFIG_PROBE=n diff --git a/src/arch/xtensa/configs/unit_test_defconfig b/src/arch/xtensa/configs/unit_test_defconfig index afe031acedba..f7703eb7f26c 100644 --- a/src/arch/xtensa/configs/unit_test_defconfig +++ b/src/arch/xtensa/configs/unit_test_defconfig @@ -1,2 +1,3 @@ CONFIG_METEORLAKE=y CONFIG_COMP_DRC=y +CONFIG_COMP_DCBLOCK=y diff --git a/src/audio/CMakeLists.txt b/src/audio/CMakeLists.txt index d9de3c578d8e..92002c8b7c1c 100644 --- a/src/audio/CMakeLists.txt +++ b/src/audio/CMakeLists.txt @@ -71,6 +71,9 @@ if(NOT CONFIG_COMP_MODULE_SHARED_LIBRARY_BUILD) if(CONFIG_COMP_MUX) add_subdirectory(mux) endif() + if(CONFIG_COMP_PHASE_VOCODER) + add_subdirectory(phase_vocoder) + endif() if(CONFIG_COMP_RTNR) add_subdirectory(rtnr) endif() @@ -86,6 +89,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 +127,10 @@ 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_UAOL_INTEL_ADSP) + add_local_sources(sof intel_uaol.c) 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..8accb25738a2 100644 --- a/src/audio/Kconfig +++ b/src/audio/Kconfig @@ -42,6 +42,15 @@ config HOST_DMA_STREAM_SYNCHRONIZATION for each group, different than the default one determined by the system tick frequency. This feature will allow host lower power consumption in scenarios with deep buffering. +config HOST_DMA_IPC_POSITION_UPDATES + bool "Support for stream position updates via IPC messages" + depends on IPC_MAJOR_3 + default y + help + Support firmware functionality to report stream position updates + by sending an IPC message whenever one period of audio is transferred. + Most platforms provide more efficient ways to query the DMA status. + config COMP_CHAIN_DMA bool "Chain DMA component" depends on IPC_MAJOR_4 @@ -93,13 +102,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 @@ -146,14 +148,17 @@ rsource "module_adapter/Kconfig" rsource "multiband_drc/Kconfig" rsource "mux/Kconfig" rsource "nxp/Kconfig" +rsource "phase_vocoder/Kconfig" rsource "rtnr/Kconfig" 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..ef7285d2f564 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,19 @@ 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)); + /* sample group size is used as a divisor below, reject configs that make it zero */ + if (!base_cfg->audio_fmt.channels_count || base_cfg->audio_fmt.depth < 8) { + comp_err(dev, "invalid channels:%u depth:%d", + base_cfg->audio_fmt.channels_count, base_cfg->audio_fmt.depth); + return -EINVAL; + } + + cd = mod_zalloc(mod, sizeof(*cd)); if (!cd) { return -ENOMEM; } @@ -145,10 +149,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 +164,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 +175,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 +186,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 +197,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 +228,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 +255,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 +282,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 +327,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..b4911f6c28fd 100644 --- a/src/audio/asrc/asrc.c +++ b/src/audio/asrc/asrc.c @@ -8,10 +8,11 @@ #include <sof/audio/component.h> #include <sof/audio/format.h> #include <sof/audio/pipeline.h> +#include <sof/audio/sink_api.h> +#include <sof/audio/source_api.h> #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> @@ -33,35 +34,22 @@ LOG_MODULE_REGISTER(asrc, CONFIG_SOF_LOG_LEVEL); -/* In-line functions */ - -static inline void src_inc_wrap(int32_t **ptr, int32_t *end, size_t size) -{ - if (*ptr >= end) - *ptr = (int32_t *)((uint8_t *)*ptr - size); -} - -static inline void src_inc_wrap_s16(int16_t **ptr, int16_t *end, size_t size) -{ - if (*ptr >= end) - *ptr = (int16_t *)((uint8_t *)*ptr - size); -} - /* A fast copy function for same in and out rate */ static void src_copy_s32(struct processing_module *mod, - const struct audio_stream *source, - struct audio_stream *sink, - int *n_read, int *n_written) + struct cir_buf_source *source, + struct cir_buf_sink *sink, + unsigned int channels, + size_t *n_read, size_t *n_written) { struct comp_data *cd = module_get_private_data(mod); struct comp_dev *dev = mod->dev; int32_t *buf; - int32_t *src = audio_stream_get_rptr(source); - int32_t *snk = audio_stream_get_wptr(sink); + const int32_t *src = source->ptr; + int32_t *snk = sink->ptr; int n_wrap_src; int n_wrap_snk; int n_copy; - int n; + unsigned int n; int ret; int i; int in_frames = 0; @@ -73,17 +61,16 @@ static void src_copy_s32(struct processing_module *mod, /* Copy input data from source */ buf = (int32_t *)cd->ibuf[0]; - n = cd->source_frames * audio_stream_get_channels(source); + n = cd->source_frames * channels; while (n > 0) { - n_wrap_src = (int32_t *)audio_stream_get_end_addr(source) - src; + n_wrap_src = cir_buf_samples_without_wrap_s32(src, source->buf_end); n_copy = (n < n_wrap_src) ? n : n_wrap_src; for (i = 0; i < n_copy; i++) *buf++ = (*src++) << cd->data_shift; /* Update and check both source and destination for wrap */ n -= n_copy; - src_inc_wrap(&src, audio_stream_get_end_addr(source), - audio_stream_get_size(source)); + src = cir_buf_wrap(src, source->buf_start, source->buf_end); } /* Run ASRC */ @@ -101,20 +88,19 @@ 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); + n = out_frames * channels; while (n > 0) { - n_wrap_snk = (int32_t *)audio_stream_get_end_addr(sink) - snk; + n_wrap_snk = cir_buf_samples_without_wrap_s32(snk, sink->buf_end); n_copy = (n < n_wrap_snk) ? n : n_wrap_snk; for (i = 0; i < n_copy; i++) *snk++ = (*buf++) >> cd->data_shift; /* Update and check both source and destination for wrap */ n -= n_copy; - src_inc_wrap(&snk, audio_stream_get_end_addr(sink), - audio_stream_get_size(sink)); + snk = cir_buf_wrap(snk, sink->buf_start, sink->buf_end); } *n_read = in_frames; @@ -122,21 +108,22 @@ static void src_copy_s32(struct processing_module *mod, } static void src_copy_s16(struct processing_module *mod, - const struct audio_stream *source, - struct audio_stream *sink, - int *n_read, int *n_written) + struct cir_buf_source *source, + struct cir_buf_sink *sink, + unsigned int channels, + size_t *n_read, size_t *n_written) { struct comp_data *cd = module_get_private_data(mod); struct comp_dev *dev = mod->dev; - int16_t *src = audio_stream_get_rptr(source); - int16_t *snk = audio_stream_get_wptr(sink); + const int16_t *src = source->ptr; + int16_t *snk = sink->ptr; int16_t *buf; int n_wrap_src; int n_wrap_snk; int n_copy; int s_copy; int ret; - int n; + unsigned int n; int in_frames = 0; int out_frames = 0; int idx = 0; @@ -145,9 +132,9 @@ static void src_copy_s16(struct processing_module *mod, /* Copy input data from source */ buf = (int16_t *)cd->ibuf[0]; - n = cd->source_frames * audio_stream_get_channels(source); + n = cd->source_frames * channels; while (n > 0) { - n_wrap_src = (int16_t *)audio_stream_get_end_addr(source) - src; + n_wrap_src = cir_buf_samples_without_wrap_s16(src, source->buf_end); n_copy = (n < n_wrap_src) ? n : n_wrap_src; s_copy = n_copy * sizeof(int16_t); ret = memcpy_s(buf, s_copy, src, s_copy); @@ -157,8 +144,7 @@ static void src_copy_s16(struct processing_module *mod, n -= n_copy; src += n_copy; buf += n_copy; - src_inc_wrap_s16(&src, audio_stream_get_end_addr(source), - audio_stream_get_size(source)); + src = cir_buf_wrap(src, source->buf_start, source->buf_end); } /* Run ASRC */ @@ -177,12 +163,12 @@ 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); + n = out_frames * channels; while (n > 0) { - n_wrap_snk = (int16_t *)audio_stream_get_end_addr(sink) - snk; + n_wrap_snk = cir_buf_samples_without_wrap_s16(snk, sink->buf_end); n_copy = (n < n_wrap_snk) ? n : n_wrap_snk; s_copy = n_copy * sizeof(int16_t); ret = memcpy_s(snk, s_copy, buf, s_copy); @@ -192,8 +178,7 @@ static void src_copy_s16(struct processing_module *mod, n -= n_copy; snk += n_copy; buf += n_copy; - src_inc_wrap_s16(&snk, audio_stream_get_end_addr(sink), - audio_stream_get_size(sink)); + snk = cir_buf_wrap(snk, sink->buf_start, sink->buf_end); } *n_read = in_frames; @@ -207,17 +192,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 +227,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 +246,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 +257,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 +269,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 +284,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 +293,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 +303,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,91 +318,47 @@ 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; } -static int asrc_verify_params(struct processing_module *mod, - struct sof_ipc_stream_params *params) -{ - struct comp_data *cd = module_get_private_data(mod); - struct comp_dev *dev = mod->dev; - int ret; - - comp_dbg(dev, "asrc_verify_params()"); - - /* check whether params->rate (received from driver) are equal - * to asrc->source_rate (PLAYBACK) or asrc->sink_rate (CAPTURE) set - * during creating src component in asrc_new(). - * src->source/sink_rate = 0 means that source/sink rate can vary. - */ - if (dev->direction == SOF_IPC_STREAM_PLAYBACK) { - if (asrc_get_source_rate(&cd->ipc_config) && - params->rate != asrc_get_source_rate(&cd->ipc_config)) { - comp_err(dev, "runtime stream pcm rate %u does not match rate %u fetched from ipc.", - params->rate, asrc_get_source_rate(&cd->ipc_config)); - return -EINVAL; - } - } else { - if (asrc_get_sink_rate(&cd->ipc_config) && - params->rate != asrc_get_sink_rate(&cd->ipc_config)) { - comp_err(dev, "runtime stream pcm rate %u does not match rate %u fetched from ipc.", - params->rate, asrc_get_sink_rate(&cd->ipc_config)); - return -EINVAL; - } - } - - /* update downstream (playback) or upstream (capture) buffer parameters - */ - ret = comp_verify_params(dev, BUFF_PARAMS_RATE, params); - if (ret < 0) { - comp_err(dev, "comp_verify_params() failed."); - return ret; - } - - return 0; -} - /* set component audio stream parameters */ -static int asrc_params(struct processing_module *mod) +static int asrc_params(struct processing_module *mod, + struct sof_source *source, struct sof_sink *sink) { struct sof_ipc_stream_params *pcm_params = mod->stream_params; struct comp_data *cd = module_get_private_data(mod); struct comp_dev *dev = mod->dev; - struct comp_buffer *sourceb, *sinkb; int err; - comp_info(dev, "asrc_params()"); + comp_info(dev, "entry"); asrc_set_stream_params(cd, pcm_params); - err = asrc_verify_params(mod, pcm_params); - if (err < 0) { - comp_err(dev, "pcm params verification failed."); + err = asrc_verify_stream_params(mod, pcm_params); + if (err < 0) return -EINVAL; - } - 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; + err = comp_verify_params(dev, BUFF_PARAMS_RATE, pcm_params); + if (err < 0) { + comp_err(dev, "comp_verify_params() failed."); + return -EINVAL; } /* update the source/sink buffer formats. Sink rate will be modified below */ - asrc_update_buffer_format(sourceb, cd); - asrc_update_buffer_format(sinkb, cd); + asrc_update_source_format(source, cd); + asrc_update_sink_format(sink, cd); /* Don't change sink rate if value from IPC is 0 (auto detect) */ if (asrc_get_sink_rate(&cd->ipc_config)) - audio_stream_set_rate(&sinkb->stream, asrc_get_sink_rate(&cd->ipc_config)); + sink_set_rate(sink, asrc_get_sink_rate(&cd->ipc_config)); /* set source/sink_frames/rate */ - cd->source_rate = audio_stream_get_rate(&sourceb->stream); - cd->sink_rate = audio_stream_get_rate(&sinkb->stream); + cd->source_rate = source_get_rate(source); + cd->sink_rate = sink_get_rate(sink); if (!cd->sink_rate) { - comp_err(dev, "asrc_params(), zero sink rate"); + comp_err(dev, "zero sink rate"); return -EINVAL; } @@ -436,7 +377,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 +448,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) { @@ -536,7 +477,8 @@ static int asrc_prepare(struct processing_module *mod, { struct comp_data *cd = module_get_private_data(mod); struct comp_dev *dev = mod->dev; - struct comp_buffer *sourceb, *sinkb; + struct sof_source *source; + struct sof_sink *sink; uint32_t source_period_bytes; uint32_t sink_period_bytes; int sample_bytes; @@ -547,52 +489,44 @@ 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); + /* SRC component will only ever have 1 source and 1 sink buffer */ + if (num_of_sources != 1 || num_of_sinks != 1) { + comp_err(dev, "expected exactly 1 source and 1 sink (got %d/%d)", num_of_sources, + num_of_sinks); + return -ENOTCONN; + } + + source = sources[0]; + sink = sinks[0]; + + ret = asrc_params(mod, source, sink); if (ret < 0) return ret; - /* - * SRC component will only ever have 1 source and 1 sink buffer, - * asrc_params() has checked their validity already - */ - sourceb = comp_dev_get_first_data_producer(dev); - sinkb = comp_dev_get_first_data_consumer(dev); - /* get source data format and period bytes */ - cd->source_format = audio_stream_get_frm_fmt(&sourceb->stream); - source_period_bytes = audio_stream_period_bytes(&sourceb->stream, - cd->source_frames); + cd->source_format = source_get_frm_fmt(source); + source_period_bytes = source_get_frame_bytes(source) * cd->source_frames; /* get sink data format and period bytes */ - cd->sink_format = audio_stream_get_frm_fmt(&sinkb->stream); - sink_period_bytes = audio_stream_period_bytes(&sinkb->stream, - cd->sink_frames); - - if (audio_stream_get_size(&sinkb->stream) < - dev->ipc_config.periods_sink * sink_period_bytes) { - comp_err(dev, "sink buffer size %d is insufficient < %d * %d", - audio_stream_get_size(&sinkb->stream), dev->ipc_config.periods_sink, - sink_period_bytes); - ret = -ENOMEM; - goto err; - } + cd->sink_format = sink_get_frm_fmt(sink); + sink_period_bytes = sink_get_frame_bytes(sink) * cd->sink_frames; /* 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; } /* ASRC supports S16_LE, S24_4LE and S32_LE formats */ - switch (audio_stream_get_frm_fmt(&sourceb->stream)) { + switch (source_get_frm_fmt(source)) { case SOF_IPC_FRAME_S16_LE: cd->asrc_func = src_copy_s16; break; @@ -605,45 +539,43 @@ 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; } /* Allocate input and output data buffer for ASRC processing */ - frame_bytes = audio_stream_frame_bytes(&sourceb->stream); + frame_bytes = source_get_frame_bytes(source); 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; } - sample_bytes = frame_bytes / audio_stream_get_channels(&sourceb->stream); - for (i = 0; i < audio_stream_get_channels(&sourceb->stream); i++) { + sample_bytes = frame_bytes / source_get_channels(source); + for (i = 0; i < source_get_channels(source); i++) { cd->ibuf[i] = cd->buf + i * sample_bytes; cd->obuf[i] = cd->ibuf[i] + cd->source_frames_max * frame_bytes; } /* Get required size and allocate memory for ASRC */ sample_bits = sample_bytes * 8; - ret = asrc_get_required_size(dev, &cd->asrc_size, - audio_stream_get_channels(&sourceb->stream), + ret = asrc_get_required_size(mod, &cd->asrc_size, + source_get_channels(source), 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 +591,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, source_get_channels(source), fs_prim, fs_sec, ASRC_IOF_INTERLEAVED, ASRC_IOF_INTERLEAVED, ASRC_BM_LINEAR, cd->frames, sample_bits, @@ -670,7 +602,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 +620,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 +630,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 +691,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; } @@ -787,30 +719,32 @@ static int asrc_control_loop(struct comp_dev *dev, struct comp_data *cd) /* copy and process stream data from source to sink buffers */ static int asrc_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 sof_source **sources, int num_of_sources, + struct sof_sink **sinks, int num_of_sinks) { struct comp_data *cd = module_get_private_data(mod); + struct sof_source *source = sources[0]; + struct sof_sink *sink = sinks[0]; + size_t src_frame_bytes = source_get_frame_bytes(source); + size_t snk_frame_bytes = sink_get_frame_bytes(sink); + unsigned int channels = source_get_channels(source); + struct cir_buf_source source_buf; struct comp_dev *dev = mod->dev; - struct comp_buffer *source, *sink; - struct audio_stream *source_s = input_buffers[0].data; - struct audio_stream *sink_s = output_buffers[0].data; - int frames_src; - int frames_snk; + size_t frames_src, frames_snk; + struct cir_buf_sink sink_buf; + size_t consumed = 0; + size_t produced = 0; + size_t bytes; int ret; - comp_dbg(dev, "asrc_process()"); + comp_dbg(dev, "entry"); ret = asrc_control_loop(dev, cd); if (ret) return ret; - /* asrc component needs 1 source and 1 sink buffer */ - source = comp_dev_get_first_data_producer(dev); - sink = comp_dev_get_first_data_consumer(dev); - - frames_src = audio_stream_get_avail_frames(source_s); - frames_snk = audio_stream_get_free_frames(sink_s); + frames_src = source_get_data_frames_available(source); + frames_snk = sink_get_free_frames(sink); if (cd->mode == ASRC_OM_PULL) { /* Let ASRC access max number of source frames in pull mode. @@ -835,20 +769,29 @@ static int asrc_process(struct processing_module *mod, cd->source_frames = MIN(cd->source_frames, frames_src); } - if (cd->source_frames && cd->sink_frames) { - int consumed = 0; - int produced = 0; - - /* consumed bytes are not known at this point */ - buffer_stream_invalidate(source, audio_stream_get_size(source_s)); - cd->asrc_func(mod, source_s, sink_s, &consumed, &produced); - buffer_stream_writeback(sink, produced * audio_stream_frame_bytes(sink_s)); + if (!cd->source_frames || !cd->sink_frames) + return 0; - comp_dbg(dev, "asrc_process(), consumed = %u, produced = %u", consumed, produced); + ret = source_get_data(source, cd->source_frames * src_frame_bytes, + &source_buf.ptr, &source_buf.buf_start, &bytes); + if (ret) + return ret; + source_buf.buf_end = (const char *)source_buf.buf_start + bytes; - output_buffers[0].size = produced * audio_stream_frame_bytes(sink_s); - input_buffers[0].consumed = consumed * audio_stream_frame_bytes(source_s); + ret = sink_get_buffer(sink, cd->sink_frames * snk_frame_bytes, + &sink_buf.ptr, &sink_buf.buf_start, &bytes); + if (ret) { + source_release_data(source, 0); + return ret; } + sink_buf.buf_end = (char *)sink_buf.buf_start + bytes; + + cd->asrc_func(mod, &source_buf, &sink_buf, channels, &consumed, &produced); + + comp_dbg(dev, "consumed = %zu, produced = %zu", consumed, produced); + + source_release_data(source, consumed * src_frame_bytes); + sink_commit_buffer(sink, produced * snk_frame_bytes); return 0; } @@ -858,17 +801,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; @@ -878,7 +821,7 @@ static int asrc_reset(struct processing_module *mod) static const struct module_interface asrc_interface = { .init = asrc_init, .prepare = asrc_prepare, - .process_audio_stream = asrc_process, + .process = asrc_process, .trigger = asrc_trigger, .set_configuration = asrc_set_config, .reset = asrc_reset, diff --git a/src/audio/asrc/asrc.h b/src/audio/asrc/asrc.h index 1c86a2cf757e..021bf4e6d0d0 100644 --- a/src/audio/asrc/asrc.h +++ b/src/audio/asrc/asrc.h @@ -12,6 +12,8 @@ #include "asrc_farrow.h" struct comp_data; +struct sof_source; +struct sof_sink; #ifdef CONFIG_IPC_MAJOR_4 #include <ipc4/base-config.h> @@ -82,10 +84,11 @@ int asrc_dai_get_timestamp(struct comp_data *cd, struct timestamp_data *tsd); #define COEF_C2 Q_CONVERT_FLOAT(0.99, 30) typedef void (*asrc_proc_func)(struct processing_module *mod, - const struct audio_stream *source, - struct audio_stream *sink, - int *consumed, - int *produced); + struct cir_buf_source *source, + struct cir_buf_sink *sink, + unsigned int channels, + size_t *consumed, + size_t *produced); /* asrc component private data */ struct comp_data { ipc_asrc_cfg ipc_config; @@ -122,8 +125,10 @@ struct comp_data { int asrc_dai_configure_timestamp(struct comp_data *cd); int asrc_dai_start_timestamp(struct comp_data *cd); int asrc_dai_stop_timestamp(struct comp_data *cd); -void asrc_update_buffer_format(struct comp_buffer *buf_c, struct comp_data *cd); +void asrc_update_source_format(struct sof_source *source, struct comp_data *cd); +void asrc_update_sink_format(struct sof_sink *sink, struct comp_data *cd); void asrc_set_stream_params(struct comp_data *cd, struct sof_ipc_stream_params *params); +int asrc_verify_stream_params(struct processing_module *mod, struct sof_ipc_stream_params *params); extern struct tr_ctx asrc_tr; /* Different UUID names for different build configurations... */ 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..3dbfbd0f6328 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> @@ -18,6 +17,7 @@ SOF_DEFINE_REG_UUID(asrc); DECLARE_TR_CTX(asrc_tr, SOF_UUID(asrc_uuid), LOG_LEVEL_INFO); +LOG_MODULE_DECLARE(asrc, CONFIG_SOF_LOG_LEVEL); int asrc_dai_configure_timestamp(struct comp_data *cd) { @@ -55,12 +55,51 @@ int asrc_dai_get_timestamp(struct comp_data *cd, struct timestamp_data *tsd) return cd->dai_dev->drv->ops.dai_ts_get(cd->dai_dev, tsd); } -void asrc_update_buffer_format(struct comp_buffer *buf_c, struct comp_data *cd) +void asrc_update_source_format(struct sof_source *source, struct comp_data *cd) { + (void)source; + (void)cd; + /* IPC3 don't need to update audio stream format here. */ +} + +void asrc_update_sink_format(struct sof_sink *sink, struct comp_data *cd) +{ + (void)sink; + (void)cd; /* IPC3 don't need to update audio stream format here. */ } void asrc_set_stream_params(struct comp_data *cd, struct sof_ipc_stream_params *params) { + (void)cd; + (void)params; /* IPC3 don't need to update audio stream format here. */ } + +int asrc_verify_stream_params(struct processing_module *mod, struct sof_ipc_stream_params *params) +{ + struct comp_data *cd = module_get_private_data(mod); + struct comp_dev *dev = mod->dev; + + /* PCM sampling rate received in params is dynamically selected based on the mode + * (PULL/PUSH). Need to validate it against the initial fixed ASRC configuration + * src->source/sink_rate = 0 means that source/sink rate can vary. + */ + if (dev->direction == SOF_IPC_STREAM_PLAYBACK) { + if (asrc_get_source_rate(&cd->ipc_config) && + params->rate != asrc_get_source_rate(&cd->ipc_config)) { + comp_err(dev, "runtime stream pcm rate %u does not match rate %u fetched from ipc.", + params->rate, asrc_get_source_rate(&cd->ipc_config)); + return -EINVAL; + } + } else { + if (asrc_get_sink_rate(&cd->ipc_config) && + params->rate != asrc_get_sink_rate(&cd->ipc_config)) { + comp_err(dev, "runtime stream pcm rate %u does not match rate %u fetched from ipc.", + params->rate, asrc_get_sink_rate(&cd->ipc_config)); + return -EINVAL; + } + } + + return 0; +} diff --git a/src/audio/asrc/asrc_ipc4.c b/src/audio/asrc/asrc_ipc4.c index 3e5d8bf7818f..91537edc46f5 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> @@ -67,12 +66,29 @@ int asrc_dai_get_timestamp(struct comp_data *cd, struct timestamp_data *tsd) return ops->endpoint_ops->dai_ts_get(cd->dai_dev, tsd); } -void asrc_update_buffer_format(struct comp_buffer *buf_c, struct comp_data *cd) +void asrc_update_source_format(struct sof_source *source, struct comp_data *cd) { - ipc4_update_buffer_format(buf_c, &cd->ipc_config.base.audio_fmt); + ipc4_update_source_format(source, &cd->ipc_config.base.audio_fmt); +} + +void asrc_update_sink_format(struct sof_sink *sink, struct comp_data *cd) +{ + ipc4_update_sink_format(sink, &cd->ipc_config.base.audio_fmt); } void asrc_set_stream_params(struct comp_data *cd, struct sof_ipc_stream_params *params) { ipc4_base_module_cfg_to_stream_params(&cd->ipc_config.base, params); } + +int asrc_verify_stream_params(struct processing_module *mod, struct sof_ipc_stream_params *params) +{ + /* IPC4 params contain the same fixed sampling rate that is already stored in + * ipc4_asrc_module_cfg, rather than a PCM sampling rate that is dynamically selected + * based on the mode (PULL/PUSH) as in IPC3. For now leave the function empty. + */ + (void)mod; + (void)params; + + return 0; +} 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..1ad9580d74b5 100644 --- a/src/audio/base_fw.c +++ b/src/audio/base_fw.c @@ -4,14 +4,15 @@ // #include <sof/audio/component.h> -#include <sof/lib/memory.h> /* for SHARED_DATA */ #include <sof/ut.h> #include <sof/tlv.h> #include <ipc4/base_fw.h> #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 +45,98 @@ 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 +#ifdef CONFIG_COMP_MFCC + codec_info.items[codec_info.count++] = + SET_CODEC_INFO_ITEM(SND_AUDIOCODEC_BESPOKE, SOF_IPC_STREAM_CAPTURE); +#endif +#ifdef CONFIG_SOF_COMPRESS_CODEC_PCM_DEC + codec_info.items[codec_info.count++] = + SET_CODEC_INFO_ITEM(SND_AUDIOCODEC_PCM, 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 +217,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); @@ -184,6 +284,9 @@ __cold static uint32_t basefw_set_system_time(uint32_t param_id, bool first_bloc if (!(first_block && last_block)) return IPC4_INVALID_REQUEST; + if (data_offset < sizeof(struct ipc4_system_time)) + return IPC4_ERROR_INVALID_PARAM; + global_system_time_info.host_time.val_l = ((const struct ipc4_system_time *)data)->val_l; global_system_time_info.host_time.val_u = ((const struct ipc4_system_time *)data)->val_u; @@ -230,6 +333,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; @@ -243,6 +351,12 @@ __cold static int basefw_kcps_allocation_request(struct ipc4_resource_kcps *requ assert_can_be_cold(); #if CONFIG_KCPS_DYNAMIC_CLOCK_CONTROL + if (request->core_id >= CONFIG_CORE_COUNT) { + tr_err(&ipc_tr, "invalid core_id %u", + request->core_id); + return IPC4_ERROR_INVALID_PARAM; + } + if (core_kcps_adjust(request->core_id, request->kcps)) return IPC4_ERROR_INVALID_PARAM; #endif @@ -260,6 +374,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 +438,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 +447,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 +466,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 +545,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 +656,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 +734,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. * @@ -629,6 +786,13 @@ __cold static int basefw_dma_control(bool first_block, bool last_block, uint32_t } dma_control = (struct ipc4_dma_control *)data; + + /* data_offset must cover the fixed header before computing the payload size */ + if (data_offset < sizeof(struct ipc4_dma_control)) { + tr_err(&ipc_tr, "DMA Control message too short: %u", data_offset); + return IPC4_ERROR_INVALID_PARAM; + } + data_size = data_offset - sizeof(struct ipc4_dma_control); if (data_size < (dma_control->config_length * sizeof(uint32_t))) { @@ -655,12 +819,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); @@ -688,14 +856,13 @@ static const struct comp_driver comp_basefw = { }, }; -static SHARED_DATA struct comp_driver_info comp_basefw_info = { +static struct comp_driver_info comp_basefw_info = { .drv = &comp_basefw, }; UT_STATIC void sys_comp_basefw_init(void) { - comp_register(platform_shared_get(&comp_basefw_info, - sizeof(comp_basefw_info))); + comp_register(&comp_basefw_info); } DECLARE_MODULE(sys_comp_basefw_init); diff --git a/src/audio/base_fw_intel.c b/src/audio/base_fw_intel.c index 7fa0d5c28705..bcb9fca8e1f2 100644 --- a/src/audio/base_fw_intel.c +++ b/src/audio/base_fw_intel.c @@ -22,6 +22,7 @@ #include <zephyr/pm/device_runtime.h> #include <sof/lib/memory.h> +#include <sof/lib_manager.h> #include <ipc4/base_fw.h> #include <ipc4/alh.h> @@ -32,11 +33,19 @@ #include <sof/audio/mic_privacy_manager.h> #endif +#if CONFIG_UAOL_INTEL_ADSP +#include <sof/audio/intel_uaol.h> +#endif + struct ipc4_modules_info { uint32_t modules_count; 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); + /* * TODO: default to value of ACE1.x platforms. This is defined * in multiple places in Zephyr, mm_drv_intel_adsp.h and @@ -67,7 +76,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); @@ -103,7 +112,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 +129,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 +155,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; - module_info->modules_count = desc->header.num_module_entries; + 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; + } - 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)); + /* 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); + + 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; @@ -306,7 +353,7 @@ __cold int basefw_vendor_get_large_config(struct comp_dev *dev, uint32_t param_i extended_param_id.full = param_id; - uint32_t ret = IPC4_ERROR_INVALID_PARAM; + uint32_t ret = IPC4_INVALID_REQUEST; switch (extended_param_id.part.parameter_type) { case IPC4_MEMORY_STATE_INFO_GET: @@ -352,17 +399,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 +452,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)(uint8_t)(*data); - uint32_t mic_disable_status = (uint32_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); @@ -415,14 +489,15 @@ __cold int basefw_vendor_set_large_config(struct comp_dev *dev, uint32_t param_i break; } - return IPC4_UNKNOWN_MESSAGE_TYPE; + return IPC4_INVALID_REQUEST; } __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 +517,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..37d2cc07e16c 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,29 @@ 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()"); +#if CONFIG_PROBE + if (buffer->probe_cb_free) + buffer->probe_cb_free(buffer->probe_cb_arg); +#endif + + struct mod_alloc_ctx *alloc = buffer->audio_buffer.alloc; - notifier_event(buffer, NOTIFIER_ID_BUFFER_FREE, - NOTIFIER_TARGET_CORE_LOCAL, &cb_data, sizeof(cb_data)); + assert(!IS_ENABLED(CONFIG_SOF_USERSPACE_LL) || alloc); - /* In case some listeners didn't unregister from buffer's callbacks */ - notifier_unregister_all(NULL, buffer); + if (alloc) + sof_ctx_free(alloc, buffer->stream.addr); + else + sof_heap_free(sof_sys_user_heap_get(), buffer->stream.addr); - rfree(buffer->stream.addr); - rfree(buffer); + if (alloc && alloc->vreg) { + vregion_free(alloc->vreg, buffer); + if (!vregion_put(alloc->vreg)) + sof_heap_free(alloc->heap, alloc); + } else { + sof_heap_free(alloc ? alloc->heap : NULL, buffer); + } } APP_TASK_DATA static const struct source_ops comp_buffer_source_ops = { @@ -178,6 +188,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 +196,29 @@ 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)); - + buffer = sof_ctx_alloc(alloc, flags, sizeof(*buffer), 0); 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 +233,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 +241,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 +255,35 @@ struct comp_buffer *buffer_alloc(size_t size, uint32_t flags, uint32_t align, return NULL; } - stream_addr = rballoc_align(flags, size, align); + assert(!IS_ENABLED(CONFIG_SOF_USERSPACE_LL) || alloc); + + if (alloc) + stream_addr = sof_ctx_alloc(alloc, flags, size, align); + else + stream_addr = sof_heap_alloc(sof_sys_user_heap_get(), + flags | SOF_MEM_FLAG_LARGE_BUFFER, size, align); + 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"); - rfree(stream_addr); + + if (alloc) + sof_ctx_free(alloc, stream_addr); + else + sof_heap_free(sof_sys_user_heap_get(), stream_addr); } 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; @@ -277,8 +303,16 @@ struct comp_buffer *buffer_alloc_range(size_t preferred_size, size_t minimum_siz if (preferred_size % minimum_size) preferred_size += minimum_size - preferred_size % minimum_size; + assert(!IS_ENABLED(CONFIG_SOF_USERSPACE_LL) || alloc); + for (size = preferred_size; size >= minimum_size; size -= minimum_size) { - stream_addr = rballoc_align(flags, size, align); + if (alloc) + stream_addr = sof_ctx_alloc(alloc, flags, size, align); + else + stream_addr = sof_heap_alloc(sof_sys_user_heap_get(), + flags | SOF_MEM_FLAG_LARGE_BUFFER, size, + align); + if (stream_addr) break; } @@ -291,10 +325,14 @@ 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"); - rfree(stream_addr); + + if (alloc) + sof_ctx_free(alloc, stream_addr); + else + sof_heap_free(sof_sys_user_heap_get(), stream_addr); } return buffer; @@ -302,7 +340,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 +353,7 @@ 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; + struct mod_alloc_ctx *alloc = buffer->audio_buffer.alloc; CORE_CHECK_STRUCT(&buffer->audio_buffer); @@ -327,14 +366,15 @@ 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)); + assert(!IS_ENABLED(CONFIG_SOF_USERSPACE_LL) || alloc); + + if (alloc) + new_ptr = sof_ctx_alloc(alloc, buffer->flags, 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(sof_sys_user_heap_get(), + buffer->flags | SOF_MEM_FLAG_LARGE_BUFFER, size, + alignment); + /* 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 +383,14 @@ 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) { + if (alloc) + sof_ctx_free(alloc, audio_stream_get_addr(&buffer->stream)); + else + sof_heap_free(sof_sys_user_heap_get(), audio_stream_get_addr(&buffer->stream)); + + audio_stream_set_addr(&buffer->stream, new_ptr); + } buffer_init_stream(buffer, size); @@ -354,10 +400,10 @@ 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; + struct mod_alloc_ctx *alloc = buffer->audio_buffer.alloc; CORE_CHECK_STRUCT(&buffer->audio_buffer); @@ -375,22 +421,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; - } + assert(!IS_ENABLED(CONFIG_SOF_USERSPACE_LL) || alloc); + + for (new_size = preferred_size; new_size >= minimum_size; + new_size -= minimum_size) { + if (alloc) + new_ptr = sof_ctx_alloc(alloc, buffer->flags, new_size, alignment); + else + new_ptr = sof_heap_alloc(sof_sys_user_heap_get(), + buffer->flags | SOF_MEM_FLAG_LARGE_BUFFER, + new_size, alignment); + + if (new_ptr) + break; } /* we couldn't allocate bigger chunk */ @@ -401,8 +444,14 @@ 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) { + if (alloc) + sof_ctx_free(alloc, audio_stream_get_addr(&buffer->stream)); + else + sof_heap_free(sof_sys_user_heap_get(), audio_stream_get_addr(&buffer->stream)); + + audio_stream_set_addr(&buffer->stream, new_ptr); + } buffer_init_stream(buffer, new_size); @@ -418,7 +467,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 +476,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 +513,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 +528,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 +561,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 +569,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 +580,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,20 +600,30 @@ 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. + * + * Returns -EALREADY if the buffer is already attached in this direction. + * Attaching the same buffer twice would corrupt the list (list_item_prepend + * on an already-linked node creates a self-loop), so callers must propagate + * the error. */ -void buffer_attach(struct comp_buffer *buffer, struct list_item *head, int dir) +int buffer_attach(struct comp_buffer *buffer, struct list_item *head, int dir) { struct list_item *list = buffer_comp_list(buffer, dir); CORE_CHECK_STRUCT(&buffer->audio_buffer); + if (!list_is_empty(list)) + return -EALREADY; list_item_prepend(list, head); + return 0; } /* - * 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..51245e91ca40 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,12 @@ 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); + sof_ctx_free(alloc, (__sparse_force void *)ring_buffer->_data_buffer); + sof_ctx_free(alloc, ring_buffer); } static void ring_buffer_reset(struct sof_audio_buffer *audio_buffer) @@ -273,11 +276,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 +289,18 @@ 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; 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)); + ring_buffer = sof_ctx_alloc(alloc, memory_flags, sizeof(*ring_buffer), 0); 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 +310,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 +366,19 @@ 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; + + data_buf = sof_ctx_alloc(alloc, 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 +387,6 @@ 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); + sof_ctx_free(alloc, ring_buffer); return NULL; } diff --git a/src/audio/chain_dma.c b/src/audio/chain_dma.c index 975f2c6daaa1..1260cf44a21c 100644 --- a/src/audio/chain_dma.c +++ b/src/audio/chain_dma.c @@ -15,7 +15,9 @@ #include <ipc/dai.h> #include <ipc4/gateway.h> #include <sof/schedule/ll_schedule.h> +#include <sof/schedule/ll_schedule_domain.h> #include <sof/schedule/schedule.h> +#include <rtos/alloc.h> #include <rtos/task.h> #include <sof/lib/dma.h> #include <sof/lib/memory.h> @@ -26,7 +28,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 @@ -62,13 +63,13 @@ struct chain_dma_data { /* local host DMA config */ struct sof_dma *dma_host; - struct dma_chan_data *chan_host; + int chan_host_index; struct dma_config z_config_host; struct dma_block_config dma_block_cfg_host; /* local link DMA config */ struct sof_dma *dma_link; - struct dma_chan_data *chan_link; + int chan_link_index; struct dma_config z_config_link; struct dma_block_config dma_block_cfg_link; @@ -80,12 +81,18 @@ static int chain_host_start(struct comp_dev *dev) struct chain_dma_data *cd = comp_get_drvdata(dev); int err; - err = dma_start(cd->chan_host->dma->z_dev, cd->chan_host->index); + if (cd->chan_host_index < 0 || !cd->dma_host) { + comp_err(dev, "incomplete initialization detected, aborting host %p", + cd->dma_host); + return -ENODEV; + } + + err = sof_dma_start(cd->dma_host, cd->chan_host_index); if (err < 0) return err; - comp_info(dev, "dma_start() host chan_index = %u", - cd->chan_host->index); + comp_info(dev, "dma_start() host chan_index = %d", + cd->chan_host_index); return 0; } @@ -94,12 +101,18 @@ static int chain_link_start(struct comp_dev *dev) struct chain_dma_data *cd = comp_get_drvdata(dev); int err; - err = dma_start(cd->chan_link->dma->z_dev, cd->chan_link->index); + if (cd->chan_link_index < 0 || !cd->dma_link) { + comp_err(dev, "incomplete initialization detected, aborting link %p", + cd->dma_link); + return -ENODEV; + } + + err = sof_dma_start(cd->dma_link, cd->chan_link_index); if (err < 0) return err; - comp_info(dev, "dma_start() link chan_index = %u", - cd->chan_link->index); + comp_info(dev, "dma_start() link chan_index = %d", + cd->chan_link_index); return 0; } @@ -108,12 +121,18 @@ static int chain_link_stop(struct comp_dev *dev) struct chain_dma_data *cd = comp_get_drvdata(dev); int err; - err = dma_stop(cd->chan_link->dma->z_dev, cd->chan_link->index); + if (cd->chan_link_index < 0 || !cd->dma_link) { + comp_err(dev, "incomplete initialization detected, aborting link %p", + cd->dma_link); + return -ENODEV; + } + + err = sof_dma_stop(cd->dma_link, cd->chan_link_index); if (err < 0) return err; - comp_info(dev, "dma_stop() link chan_index = %u", - cd->chan_link->index); + comp_info(dev, "dma_stop() link chan_index = %d", + cd->chan_link_index); return 0; } @@ -123,12 +142,18 @@ static int chain_host_stop(struct comp_dev *dev) struct chain_dma_data *cd = comp_get_drvdata(dev); int err; - err = dma_stop(cd->chan_host->dma->z_dev, cd->chan_host->index); + if (cd->chan_host_index < 0 || !cd->dma_host) { + comp_err(dev, "incomplete initialization detected, aborting host %p", + cd->dma_host); + return -ENODEV; + } + + err = sof_dma_stop(cd->dma_host, cd->chan_host_index); if (err < 0) return err; - comp_info(dev, "dma_stop() host chan_index = %u", - cd->chan_host->index); + comp_info(dev, "dma_stop() host chan_index = %d", + cd->chan_host_index); return 0; } @@ -143,32 +168,16 @@ static size_t chain_get_transferred_data_size(const uint32_t out_read_pos, const return buff_size - in_read_pos + out_read_pos; } -/* get status from dma and check for xrun */ -static int chain_get_dma_status(struct chain_dma_data *cd, struct dma_chan_data *chan, - struct dma_status *stat) -{ - 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; - } - } else if (!ret) { - cd->xrun_notification_sent = false; - } -#endif - return ret; +static void chain_dma_send_xrun_notif(struct chain_dma_data *cd) +{ + uint32_t resource_id = cd->link_connector_node_id.dw; + enum sof_ipc_stream_direction dir = cd->stream_direction; + + if (!cd->xrun_notification_sent) + cd->xrun_notification_sent = send_gateway_xrun_notif_msg(resource_id, dir); } +#endif static enum task_state chain_task_run(void *data) { @@ -182,11 +191,17 @@ static enum task_state chain_task_run(void *data) /* Link DMA can return -EPIPE and current status if xrun occurs, then it is not critical * and flow shall continue. Other error values will be treated as critical. */ - ret = chain_get_dma_status(cd, cd->chan_link, &stat); + ret = sof_dma_get_status(cd->dma_link, cd->chan_link_index, &stat); switch (ret) { case 0: +#if CONFIG_XRUN_NOTIFICATIONS_ENABLE + cd->xrun_notification_sent = false; +#endif break; case -EPIPE: +#if CONFIG_XRUN_NOTIFICATIONS_ENABLE + chain_dma_send_xrun_notif(cd); +#endif tr_warn(&chain_dma_tr, "dma_get_status() link xrun occurred," " ret = %d", ret); break; @@ -200,7 +215,7 @@ static enum task_state chain_task_run(void *data) link_read_pos = stat.read_position; /* Host DMA does not report xruns. All error values will be treated as critical. */ - ret = chain_get_dma_status(cd, cd->chan_host, &stat); + ret = sof_dma_get_status(cd->dma_host, cd->chan_host_index, &stat); if (ret < 0) { tr_err(&chain_dma_tr, "dma_get_status() error, ret = %d", ret); return SOF_TASK_STATE_COMPLETED; @@ -218,14 +233,14 @@ static enum task_state chain_task_run(void *data) */ const size_t increment = MIN(host_free_bytes, link_avail_bytes); - ret = dma_reload(cd->chan_host->dma->z_dev, cd->chan_host->index, 0, 0, increment); + ret = sof_dma_reload(cd->dma_host, cd->chan_host_index, increment); if (ret < 0) { tr_err(&chain_dma_tr, "dma_reload() host error, ret = %d", ret); return SOF_TASK_STATE_COMPLETED; } - ret = dma_reload(cd->chan_link->dma->z_dev, cd->chan_link->index, 0, 0, increment); + ret = sof_dma_reload(cd->dma_link, cd->chan_link_index, increment); if (ret < 0) { tr_err(&chain_dma_tr, "dma_reload() link error, ret = %d", ret); @@ -241,9 +256,8 @@ static enum task_state chain_task_run(void *data) const size_t half_buff_size = buff_size / 2; if (!cd->first_data_received && host_avail_bytes > half_buff_size) { - ret = dma_reload(cd->chan_link->dma->z_dev, - cd->chan_link->index, 0, 0, - MIN(host_avail_bytes, link_free_bytes)); + ret = sof_dma_reload(cd->dma_link, cd->chan_link_index, + MIN(host_avail_bytes, link_free_bytes)); if (ret < 0) { tr_err(&chain_dma_tr, "dma_reload() link error, ret = %d", ret); @@ -257,8 +271,8 @@ static enum task_state chain_task_run(void *data) host_read_pos, buff_size); - ret = dma_reload(cd->chan_host->dma->z_dev, cd->chan_host->index, - 0, 0, transferred); + ret = sof_dma_reload(cd->dma_host, cd->chan_host_index, + transferred); if (ret < 0) { tr_err(&chain_dma_tr, "dma_reload() host error, ret = %d", ret); @@ -267,8 +281,8 @@ static enum task_state chain_task_run(void *data) if (host_avail_bytes >= half_buff_size && link_free_bytes >= half_buff_size) { - ret = dma_reload(cd->chan_link->dma->z_dev, cd->chan_link->index, - 0, 0, half_buff_size); + ret = sof_dma_reload(cd->dma_link, cd->chan_link_index, + half_buff_size); if (ret < 0) { tr_err(&chain_dma_tr, "dma_reload() link error, ret = %d", ret); @@ -378,10 +392,21 @@ __cold static void chain_release(struct comp_dev *dev) assert_can_be_cold(); - dma_release_channel(cd->chan_host->dma->z_dev, cd->chan_host->index); - sof_dma_put(cd->dma_host); - dma_release_channel(cd->chan_link->dma->z_dev, cd->chan_link->index); - sof_dma_put(cd->dma_link); + if (cd->dma_host) { + if (cd->chan_host_index >= 0) + sof_dma_release_channel(cd->dma_host, cd->chan_host_index); + sof_dma_put(cd->dma_host); + cd->dma_host = NULL; + cd->chan_host_index = -EINVAL; + } + + if (cd->dma_link) { + if (cd->chan_link_index >= 0) + sof_dma_release_channel(cd->dma_link, cd->chan_link_index); + sof_dma_put(cd->dma_link); + cd->dma_link = NULL; + cd->chan_link_index = -EINVAL; + } if (cd->dma_buffer) { buffer_free(cd->dma_buffer); @@ -468,43 +493,44 @@ __cold static int chain_init(struct comp_dev *dev, void *addr, size_t length) /* get host DMA channel */ channel = cd->host_connector_node_id.f.v_index; - channel = dma_request_channel(cd->dma_host->z_dev, &channel); + channel = sof_dma_request_channel(cd->dma_host, 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]; + cd->chan_host_index = channel; - err = dma_config(cd->dma_host->z_dev, cd->chan_host->index, dma_cfg_host); + err = sof_dma_config(cd->dma_host, 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; } /* get link DMA channel */ channel = cd->link_connector_node_id.f.v_index; - channel = dma_request_channel(cd->dma_link->z_dev, &channel); + channel = sof_dma_request_channel(cd->dma_link, 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; } - cd->chan_link = &cd->dma_link->chan[channel]; + cd->chan_link_index = channel; - err = dma_config(cd->dma_link->z_dev, cd->chan_link->index, dma_cfg_link); + err = sof_dma_config(cd->dma_link, 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; error_link: - dma_release_channel(cd->dma_link->z_dev, cd->chan_link->index); - cd->chan_link = NULL; + sof_dma_release_channel(cd->dma_link, cd->chan_link_index); error_host: - dma_release_channel(cd->dma_host->z_dev, cd->chan_host->index); - cd->chan_host = NULL; + sof_dma_release_channel(cd->dma_host, cd->chan_host_index); return err; } @@ -512,6 +538,7 @@ __cold static int chain_task_init(struct comp_dev *dev, uint8_t host_dma_id, uin uint32_t fifo_size) { struct chain_dma_data *cd = comp_get_drvdata(dev); + struct mod_alloc_ctx *alloc_ctx = NULL; uint32_t addr_align; size_t buff_size; void *buff_addr; @@ -561,8 +588,8 @@ __cold static int chain_task_init(struct comp_dev *dev, uint8_t host_dma_id, uin } /* retrieve DMA buffer address alignment */ - ret = dma_get_attribute(cd->dma_host->z_dev, DMA_ATTR_BUFFER_ADDRESS_ALIGNMENT, - &addr_align); + ret = sof_dma_get_attribute(cd->dma_host, DMA_ATTR_BUFFER_ADDRESS_ALIGNMENT, + &addr_align); if (ret < 0) { comp_err(dev, "could not get dma buffer address alignment, err = %d", ret); @@ -590,8 +617,14 @@ __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); + +#ifdef CONFIG_SOF_USERSPACE_LL + alloc_ctx = ipc_get()->ll_alloc; +#endif + /* allocate not shared buffer */ - cd->dma_buffer = buffer_alloc(fifo_size, SOF_MEM_FLAG_USER | SOF_MEM_FLAG_DMA, + cd->dma_buffer = buffer_alloc(alloc_ctx, fifo_size, + SOF_MEM_FLAG_USER | SOF_MEM_FLAG_DMA, addr_align, BUFFER_USAGE_NOT_SHARED); if (!cd->dma_buffer) { @@ -633,6 +666,70 @@ static int chain_task_trigger(struct comp_dev *dev, int cmd) } } +/* + * comp_dev and private data allocation helpers. For user-space LL both + * objects must live on the user heap so the (unprivileged) user LL thread + * can access them; otherwise the normal component/rmalloc paths are used. + */ +#ifdef CONFIG_SOF_USERSPACE_LL +__cold static struct comp_dev *chain_dev_alloc(const struct comp_driver *drv) +{ + struct comp_dev *dev; + + dev = sof_heap_alloc(sof_sys_user_heap_get(), + SOF_MEM_FLAG_USER | SOF_MEM_FLAG_COHERENT, + sizeof(*dev), 0); + if (!dev) + return NULL; + + memset(dev, 0, sizeof(*dev)); + comp_init(drv, dev, sizeof(*dev)); + + return dev; +} + +__cold static struct chain_dma_data *chain_cd_alloc(void) +{ + struct chain_dma_data *cd; + + cd = sof_heap_alloc(sof_sys_user_heap_get(), SOF_MEM_FLAG_USER, sizeof(*cd), 0); + if (cd) + memset(cd, 0, sizeof(*cd)); + + return cd; +} + +__cold static void chain_dev_free(struct comp_dev *dev) +{ + sof_heap_free(sof_sys_user_heap_get(), dev); +} + +__cold static void chain_cd_free(struct chain_dma_data *cd) +{ + sof_heap_free(sof_sys_user_heap_get(), cd); +} +#else +__cold static struct comp_dev *chain_dev_alloc(const struct comp_driver *drv) +{ + return comp_alloc(drv, sizeof(struct comp_dev)); +} + +__cold static struct chain_dma_data *chain_cd_alloc(void) +{ + return rzalloc(SOF_MEM_FLAG_USER, sizeof(struct chain_dma_data)); +} + +__cold static void chain_dev_free(struct comp_dev *dev) +{ + comp_free_device(dev); +} + +__cold static void chain_cd_free(struct chain_dma_data *cd) +{ + rfree(cd); +} +#endif + __cold static struct comp_dev *chain_task_create(const struct comp_driver *drv, const struct comp_ipc_config *ipc_config, const void *ipc_specific_config) @@ -651,17 +748,19 @@ __cold static struct comp_dev *chain_task_create(const struct comp_driver *drv, if (host_dma_id >= max_chain_number) return NULL; - dev = comp_alloc(drv, sizeof(*dev)); + dev = chain_dev_alloc(drv); if (!dev) return NULL; - cd = rzalloc(SOF_MEM_FLAG_USER, sizeof(*cd)); + cd = chain_cd_alloc(); if (!cd) goto error; cd->first_data_received = false; cd->cs = scs ? 2 : 4; cd->chain_task.state = SOF_TASK_STATE_INIT; + cd->chan_host_index = -EINVAL; + cd->chan_link_index = -EINVAL; comp_set_drvdata(dev, cd); @@ -669,9 +768,9 @@ __cold static struct comp_dev *chain_task_create(const struct comp_driver *drv, if (!ret) return dev; - rfree(cd); + chain_cd_free(cd); error: - rfree(dev); + chain_dev_free(dev); return NULL; } @@ -682,8 +781,8 @@ __cold static void chain_task_free(struct comp_dev *dev) assert_can_be_cold(); chain_release(dev); - rfree(cd); - rfree(dev); + chain_cd_free(cd); + chain_dev_free(dev); } static const struct comp_driver comp_chain_dma = { @@ -696,14 +795,13 @@ static const struct comp_driver comp_chain_dma = { }, }; -static SHARED_DATA struct comp_driver_info comp_chain_dma_info = { +static struct comp_driver_info comp_chain_dma_info = { .drv = &comp_chain_dma, }; UT_STATIC void sys_comp_chain_dma_init(void) { - comp_register(platform_shared_get(&comp_chain_dma_info, - sizeof(comp_chain_dma_info))); + comp_register(&comp_chain_dma_info); } DECLARE_MODULE(sys_comp_chain_dma_init); 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..bc4795055576 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,19 +322,32 @@ 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; } /* Allow for multiple module_params to be packed into the data pointed to by config */ + param_header_size = sizeof(param->id) + sizeof(param->size); for (i = 0; i < config_data_size; param_number++) { + /* Need at least a param header in the remaining bytes to read id/size */ + if (config_data_size - i < param_header_size) { + comp_err(dev, "param header truncated"); + return -EINVAL; + } + param = (struct module_param *)((char *)config->data + i); - param_header_size = sizeof(param->id) + sizeof(param->size); /* 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; + } + + /* The whole param (header + data) must fit in the remaining config data */ + if (param->size > config_data_size - i) { + comp_err(dev, "param size %u exceeds remaining %u", + param->size, config_data_size - i); return -EINVAL; } @@ -345,7 +356,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 +365,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 +376,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 +388,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 +408,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 +491,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..af0b9c278368 100644 --- a/src/audio/component.c +++ b/src/audio/component.c @@ -13,7 +13,6 @@ #include <sof/ipc/msg.h> #include <rtos/alloc.h> #include <rtos/cache.h> -#include <sof/lib/memory.h> /* for SHARED_DATA */ #include <sof/lib/uuid.h> #include <sof/list.h> #include <rtos/sof.h> @@ -36,7 +35,15 @@ LOG_MODULE_REGISTER(component, CONFIG_SOF_LOG_LEVEL); -static SHARED_DATA struct comp_driver_list cd; +static APP_SYSUSER_BSS struct comp_driver_list cd; + +#ifdef CONFIG_SOF_USERSPACE_LL +struct comp_driver_list *comp_drivers_get(void) +{ + assert(!ll_sch_is_current()); + return &cd; +} +#endif SOF_DEFINE_REG_UUID(component); @@ -45,24 +52,22 @@ DECLARE_TR_CTX(comp_tr, SOF_UUID(component_uuid), LOG_LEVEL_INFO); int comp_register(struct comp_driver_info *drv) { struct comp_driver_list *drivers = comp_drivers_get(); - k_spinlock_key_t key; - key = k_spin_lock(&drivers->lock); + /* + * No locking needed: the driver list is only modified at FW boot, + * where module init runs serially on the primary core, and at + * runtime from the serialized IPC thread (library load). These + * never overlap, so concurrent modification is not possible. + */ list_item_prepend(&drv->list, &drivers->list); - k_spin_unlock(&drivers->lock, key); return 0; } -EXPORT_SYMBOL(comp_register); void comp_unregister(struct comp_driver_info *drv) { - struct comp_driver_list *drivers = comp_drivers_get(); - k_spinlock_key_t key; - - key = k_spin_lock(&drivers->lock); + /* see comp_register() on why no locking is needed */ list_item_del(&drv->list); - k_spin_unlock(&drivers->lock, key); } int comp_set_adapter_ops(const struct comp_driver *drv, const struct module_interface *ops) @@ -104,7 +109,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; @@ -188,10 +193,9 @@ EXPORT_SYMBOL(comp_set_state); void sys_comp_init(struct sof *sof) { - sof->comp_drivers = platform_shared_get(&cd, sizeof(cd)); + sof->comp_drivers = &cd; list_init(&sof->comp_drivers->list); - k_spinlock_init(&sof->comp_drivers->lock); } void comp_get_copy_limits(struct comp_buffer *source, @@ -264,7 +268,7 @@ int audio_stream_copy(const struct audio_stream *source, uint32_t ioffset, return samples; } -void cir_buf_copy(void *src, void *src_addr, void *src_end, void *dst, +void cir_buf_copy(const void *src, const void *src_addr, const void *src_end, void *dst, void *dst_addr, void *dst_end, size_t byte_size) { size_t bytes = byte_size; @@ -276,7 +280,7 @@ void cir_buf_copy(void *src, void *src_addr, void *src_end, void *dst, ae_int16x4 in_sample1, in_sample2; ae_valignx2 inu; ae_valignx2 outu = AE_ZALIGN128(); - ae_int16x8 *in = (ae_int16x8 *)src; + const ae_int16x8 *in = cir_buf_wrap(src, src_addr, src_end); ae_int16x8 *out = (ae_int16x8 *)dst; while (bytes) { @@ -355,7 +359,7 @@ int audio_stream_copy(const struct audio_stream *source, uint32_t ioffset, return samples; } -void cir_buf_copy(void *src, void *src_addr, void *src_end, void *dst, +void cir_buf_copy(const void *src, const void *src_addr, const void *src_end, void *dst, void *dst_addr, void *dst_end, size_t byte_size) { size_t bytes = byte_size; @@ -368,7 +372,7 @@ void cir_buf_copy(void *src, void *src_addr, void *src_end, void *dst, ae_int16x4 in_sample = AE_ZERO16(); ae_valign inu = AE_ZALIGN64(); ae_valign outu = AE_ZALIGN64(); - ae_int16x4 *in = (ae_int16x4 *)src; + ae_int16x4 *in = cir_buf_wrap(src, src_addr, src_end); ae_int16x4 *out = (ae_int16x4 *)dst; while (bytes) { @@ -428,14 +432,14 @@ int audio_stream_copy(const struct audio_stream *source, uint32_t ioffset, return samples; } -void cir_buf_copy(void *src, void *src_addr, void *src_end, void *dst, +void cir_buf_copy(const void *src, const void *src_addr, const void *src_end, void *dst, void *dst_addr, void *dst_end, size_t byte_size) { size_t bytes = byte_size; size_t bytes_src; size_t bytes_dst; size_t bytes_copied; - uint8_t *in = (uint8_t *)src; + const uint8_t *in = src; uint8_t *out = (uint8_t *)dst; while (bytes) { @@ -517,7 +521,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 +529,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..2375b540b493 100644 --- a/src/audio/copier/copier.c +++ b/src/audio/copier/copier.c @@ -13,11 +13,12 @@ #include <rtos/interrupt.h> #include <sof/ipc/msg.h> #include <sof/ipc/topology.h> +#include <sof/schedule/ll_schedule_domain.h> #include <rtos/interrupt.h> #include <rtos/timer.h> -#include <rtos/alloc.h> #include <rtos/cache.h> #include <rtos/init.h> +#include <rtos/userspace_helper.h> #include <sof/lib/memory.h> #include <sof/lib/uuid.h> #include <sof/list.h> @@ -53,8 +54,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 +61,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 +81,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 +98,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,101 +109,105 @@ 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 (!cd) - return -ENOMEM; + if (copier->gtw_cfg.config_length > 1) { + /* + * config_length is host-controlled and expressed in dwords. A + * huge value overflows the 32-bit size arithmetic below: the + * "<< 2" wraps around, which shrinks both cfg_total_size and the + * mod_zalloc() size to a small value and defeats the payload + * check further down. The gateway blob must fit within the + * received init payload, so reject anything larger than that + * before doing any size arithmetic on it. + */ + if (copier->gtw_cfg.config_length > md->cfg.size / sizeof(uint32_t)) { + comp_err(dev, "copier_init(): invalid gtw_cfg.config_length %u", + copier->gtw_cfg.config_length); + return -EINVAL; + } + + /* 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; + } - 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! + * gtw_cfg.config_length is host-controlled; make sure the resulting + * copy length does not read past the init payload in the mailbox. + * cfg_total_size is at least sizeof(*copier), so this also rejects an + * empty (md->cfg.size == 0) or otherwise too-small init payload. */ - if (memcpy_s(&cd->config, sizeof(cd->config), copier, sizeof(*copier)) < 0) { - ret = -EINVAL; - goto error_cd; + if (cfg_total_size > md->cfg.size) { + comp_err(dev, "copier_init(): cfg size %zu exceeds init payload %zu", + cfg_total_size, md->cfg.size); + return -EINVAL; } - /* 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; - } + cd = mod_zalloc(mod, sizeof(*cd) + gtw_cfg_var_size); + if (!cd) + return -ENOMEM; - 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; - } + md->private = cd; - 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) { + /* A gateway copier wires itself into the pipeline graph, so it needs a + * valid parent pipeline (base FW modules using IPC4_INVALID_PIPELINE_ID + * are exempt in comp_new_ipc4()). Reject here to avoid a NULL deref. + */ + if (!dev->pipeline) { + comp_err(dev, "gateway copier requires a valid parent pipeline"); + ret = -EINVAL; + goto error; + } + cd->direction = get_gateway_direction(node_id.f.dma_type); 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 +215,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 +230,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 +250,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 +275,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 +287,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 +324,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 +366,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; @@ -398,13 +398,13 @@ static int copier_comp_trigger(struct comp_dev *dev, int cmd) { struct processing_module *mod = comp_mod(dev); struct copier_data *cd = module_get_private_data(mod); - struct sof_ipc_stream_posn posn; + struct sof_ipc_stream_posn posn = { 0 }; struct comp_dev *dai_copier; struct comp_buffer *buffer; uint32_t latency; int ret; - comp_dbg(dev, "copier_comp_trigger()"); + comp_dbg(dev, "entry"); ret = comp_set_state(dev, cmd); if (ret < 0) @@ -531,6 +531,16 @@ static int do_conversion_copy(struct comp_dev *dev, struct comp_buffer *sink, struct comp_copy_limits *processed_data) { + struct cir_buf_source src_cir = { + .buf_start = audio_stream_get_addr(&src->stream), + .buf_end = audio_stream_get_end_addr(&src->stream), + .ptr = audio_stream_get_rptr(&src->stream), + }; + struct cir_buf_sink snk_cir = { + .buf_start = audio_stream_get_addr(&sink->stream), + .buf_end = audio_stream_get_end_addr(&sink->stream), + .ptr = audio_stream_get_wptr(&sink->stream), + }; int i; /* buffer params might be not yet configured by component on another pipeline */ @@ -540,12 +550,18 @@ 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); - cd->converter[i](&src->stream, 0, &sink->stream, 0, + cd->converter[i](&src_cir, audio_stream_get_channels(&src->stream), + &snk_cir, audio_stream_get_channels(&sink->stream), processed_data->frames * audio_stream_get_channels(&src->stream), DUMMY_CHMAP); @@ -614,20 +630,40 @@ static int copier_module_copy(struct processing_module *mod, sink_dev = comp_buffer_get_sink_component(sink_c); processed_data.sink_bytes = 0; if (sink_dev->state == COMP_STATE_ACTIVE) { + /* Bridge the legacy audio_stream buffers into cir_buf descriptors for + * the new pcm_converter interface (read/write pointers = offset 0). + */ + struct cir_buf_source src_cir = { + .buf_start = audio_stream_get_addr(input_buffers[0].data), + .buf_end = audio_stream_get_end_addr(input_buffers[0].data), + .ptr = audio_stream_get_rptr(input_buffers[0].data), + }; + struct cir_buf_sink snk_cir = { + .buf_start = audio_stream_get_addr(output_buffers[i].data), + .buf_end = audio_stream_get_end_addr(output_buffers[i].data), + .ptr = audio_stream_get_wptr(output_buffers[i].data), + }; uint32_t source_samples; int sink_queue_id; + pcm_converter_func converter; - 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; + converter = cd->converter[sink_queue_id]; comp_get_copy_limits(src_c, sink_c, &processed_data); source_samples = processed_data.frames * audio_stream_get_channels(input_buffers[0].data); - cd->converter[sink_queue_id](input_buffers[0].data, 0, - output_buffers[i].data, 0, - source_samples, DUMMY_CHMAP); + converter(&src_cir, audio_stream_get_channels(input_buffers[0].data), + &snk_cir, audio_stream_get_channels(output_buffers[i].data), + source_samples, DUMMY_CHMAP); output_buffers[i].size = processed_data.sink_bytes; cd->output_total_data_processed += processed_data.sink_bytes; @@ -700,7 +736,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 +766,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); @@ -813,9 +849,11 @@ __cold static int set_attenuation(struct comp_dev *dev, uint32_t data_offset, co assert_can_be_cold(); - /* only support attenuation in format of 32bit */ - if (data_offset > sizeof(uint32_t)) { - comp_err(dev, "attenuation data size %d is incorrect", data_offset); + /* only support attenuation in format of 32bit; the payload is + * dereferenced as a uint32_t below so it must be exactly that size + */ + if (data_offset != sizeof(uint32_t)) { + comp_err(dev, "attenuation data size %u is incorrect", data_offset); return -EINVAL; } @@ -852,7 +890,9 @@ __cold static int set_chmap(struct comp_dev *dev, const void *data, size_t data_ pcm_converter_func process; pcm_converter_func converters[IPC4_COPIER_MODULE_OUTPUT_PINS_COUNT]; int i; +#ifndef CONFIG_SOF_USERSPACE_LL uint32_t irq_flags; +#endif assert_can_be_cold(); @@ -906,15 +946,26 @@ __cold static int set_chmap(struct comp_dev *dev, const void *data, size_t data_ } } - /* Atomically update chmap, process and converters */ + /* Atomically update chmap, process and converters. + * In user-space builds irq_local_disable() is privileged, + * use the LL scheduler lock instead. + */ +#ifdef CONFIG_SOF_USERSPACE_LL + user_ll_lock_sched(dev->pipeline->core); +#else irq_local_disable(irq_flags); +#endif cd->dd[0]->chmap = chmap_cfg->channel_map; cd->dd[0]->process = process; for (i = 0; i < IPC4_COPIER_MODULE_OUTPUT_PINS_COUNT; i++) cd->converter[i] = converters[i]; +#ifdef CONFIG_SOF_USERSPACE_LL + user_ll_unlock_sched(dev->pipeline->core); +#else irq_local_enable(irq_flags); +#endif return 0; } @@ -958,8 +1009,9 @@ __cold static int copier_get_configuration(struct processing_module *mod, struct copier_data *cd = module_get_private_data(mod); struct ipc4_llp_reading_extended llp_ext; struct comp_dev *dev = mod->dev; - struct sof_ipc_stream_posn posn; + struct sof_ipc_stream_posn posn = { 0 }; struct ipc4_llp_reading llp; + int ret; assert_can_be_cold(); @@ -989,7 +1041,9 @@ __cold static int copier_get_configuration(struct processing_module *mod, } /* get llp from dai */ - comp_position(dev, &posn); + ret = comp_position(dev, &posn); + if (ret < 0) + return ret; convert_u64_to_u32s(posn.comp_posn, &llp.llp_l, &llp.llp_u); convert_u64_to_u32s(posn.wallclock, &llp.wclk_l, &llp.wclk_u); @@ -1019,7 +1073,9 @@ __cold static int copier_get_configuration(struct processing_module *mod, } /* get llp from dai */ - comp_position(dev, &posn); + ret = comp_position(dev, &posn); + if (ret < 0) + return ret; convert_u64_to_u32s(posn.comp_posn, &llp_ext.llp_reading.llp_l, &llp_ext.llp_reading.llp_u); @@ -1173,7 +1229,7 @@ __cold static int copier_get_hw_params(struct comp_dev *dev, struct sof_ipc_stre return dai_common_get_hw_params(dd, dev, params, dir); } -__cold static int copier_bind(struct processing_module *mod, struct bind_info *bind_data) +__cold static int copier_bind(struct processing_module *mod, const struct bind_info *bind_data) { const struct ipc4_module_bind_unbind *const bu = bind_data->ipc4_data; const uint32_t src_id = IPC4_COMP_ID(bu->primary.r.module_id, bu->primary.r.instance_id); @@ -1202,7 +1258,7 @@ __cold static int copier_bind(struct processing_module *mod, struct bind_info *b return -ENODEV; } -__cold static int copier_unbind(struct processing_module *mod, struct bind_info *unbind_data) +__cold static int copier_unbind(struct processing_module *mod, const struct bind_info *unbind_data) { struct copier_data *cd = module_get_private_data(mod); struct comp_dev *dev = mod->dev; @@ -1218,7 +1274,7 @@ __cold static int copier_unbind(struct processing_module *mod, struct bind_info return 0; } -static struct module_endpoint_ops copier_endpoint_ops = { +static APP_SYSUSER_DATA const struct module_endpoint_ops copier_endpoint_ops = { .get_total_data_processed = copier_get_processed_data, .position = copier_position, .dai_ts_config = copier_dai_ts_config_op, @@ -1229,7 +1285,7 @@ static struct module_endpoint_ops copier_endpoint_ops = { .trigger = copier_comp_trigger }; -static const struct module_interface copier_interface = { +static APP_SYSUSER_DATA const struct module_interface copier_interface = { .init = copier_init, .prepare = copier_prepare, .process_audio_stream = copier_process, @@ -1242,5 +1298,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..0e54c0c7a9e5 100644 --- a/src/audio/copier/copier_dai.c +++ b/src/audio/copier/copier_dai.c @@ -77,7 +77,8 @@ static int copier_alh_assign_dai_index(struct comp_dev *dev, struct copier_data *cd = module_get_private_data(mod); const struct sof_alh_configuration_blob *alh_blob = gtw_cfg_data; uint8_t *dma_config; - size_t alh_cfg_size, dma_config_length; + size_t alh_cfg_size, dma_config_length, blob_size; + uint32_t alh_count; int i, dai_num, ret; if (!cd->config.gtw_cfg.config_length) { @@ -85,10 +86,29 @@ static int copier_alh_assign_dai_index(struct comp_dev *dev, return -EINVAL; } + blob_size = (size_t)cd->config.gtw_cfg.config_length << 2; + if (blob_size < sizeof(alh_blob->gtw_attributes) + sizeof(alh_blob->alh_cfg.count)) { + comp_err(mod->dev, "Invalid ALH gateway config: blob=%zu bytes", blob_size); + return -EINVAL; + } + + alh_count = alh_blob->alh_cfg.count; + if (alh_count > IPC4_ALH_MAX_NUMBER_OF_GTW || + get_alh_config_size(alh_blob) > blob_size) { + comp_err(mod->dev, "Invalid ALH gateway config: count=%u, blob=%zu bytes", + alh_count, blob_size); + return -EINVAL; + } + alh_cfg_size = get_alh_config_size(alh_blob); + switch (dai->type) { case SOF_DAI_INTEL_HDA: /* We use DAI_INTEL_HDA for ACE 2.0 platforms */ - alh_cfg_size = get_alh_config_size(alh_blob); + if (!alh_count) { + comp_err(mod->dev, "Invalid ALH count: %u", + alh_count); + return -EINVAL; + } dma_config = (uint8_t *)gtw_cfg_data + alh_cfg_size; dma_config_length = (cd->config.gtw_cfg.config_length << 2) - alh_cfg_size; @@ -108,7 +128,7 @@ static int copier_alh_assign_dai_index(struct comp_dev *dev, } dai_num = alh_blob->alh_cfg.count; - if (dai_num > IPC4_ALH_MAX_NUMBER_OF_GTW || dai_num < 0) { + if (dai_num > IPC4_ALH_MAX_NUMBER_OF_GTW || dai_num <= 0) { comp_err(mod->dev, "Invalid dai_count: %d", dai_num); return -EINVAL; } @@ -138,7 +158,7 @@ static int copier_alh_assign_dai_index(struct comp_dev *dev, } dai_num = alh_blob->alh_cfg.count; - if (dai_num > IPC4_ALH_MAX_NUMBER_OF_GTW || dai_num < 0) { + if (dai_num > IPC4_ALH_MAX_NUMBER_OF_GTW || dai_num <= 0) { comp_err(mod->dev, "Invalid dai_count: %d", dai_num); return -EINVAL; } @@ -204,9 +224,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 +244,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 +267,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 +290,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 +305,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 +320,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 +337,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_all_dma_configs_tlvs_only(&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 +408,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..842f92997108 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; @@ -154,7 +150,7 @@ int copier_gain_dma_control(union ipc4_connector_node_id node, const char *confi } struct ipc4_copier_module_cfg *copier_cfg = cd->dd[0]->dai_spec_config; - const int channels = copier_cfg->base.audio_fmt.channels_count; + const uint32_t channels = copier_cfg->base.audio_fmt.channels_count; ret = copier_set_gain(dev, cd->dd[0]->gain_data, gain_data, channels); if (ret) @@ -166,7 +162,7 @@ int copier_gain_dma_control(union ipc4_connector_node_id node, const char *confi } int copier_set_gain(struct comp_dev *dev, struct copier_gain_params *gain_params, - struct gain_dma_control_data *gain_data, int channels) + struct gain_dma_control_data *gain_data, uint32_t channels) { uint16_t static_gain[MAX_GAIN_COEFFS_CNT]; int ret; @@ -176,12 +172,17 @@ int copier_set_gain(struct comp_dev *dev, struct copier_gain_params *gain_params return -EINVAL; } + if (channels == 0 || channels > MAX_GAIN_COEFFS_CNT) { + comp_err(dev, "invalid channels count %u", channels); + return -EINVAL; + } + /* Set gain coefficients */ comp_info(dev, "Update gain coefficients from DMA_CONTROL ipc"); size_t gain_coef_size = channels * sizeof(uint16_t); - ret = memcpy_s(static_gain, gain_coef_size, gain_data->gain_coeffs, + ret = memcpy_s(static_gain, sizeof(static_gain), gain_data->gain_coeffs, gain_coef_size); if (ret) { comp_err(dev, "memcpy_s failed with error %d", ret); diff --git a/src/audio/copier/copier_gain.h b/src/audio/copier/copier_gain.h index ec55ff999206..bd7cc5719f5b 100644 --- a/src/audio/copier/copier_gain.h +++ b/src/audio/copier/copier_gain.h @@ -8,6 +8,7 @@ #ifndef __SOF_COPIER_GAIN_H__ #define __SOF_COPIER_GAIN_H__ +#include <stdint.h> #include <sof/audio/buffer.h> #include <ipc4/base_fw.h> #include <ipc4/gateway.h> @@ -219,7 +220,7 @@ enum copier_gain_state copier_gain_eval_state(struct copier_gain_params *gain_pa * @return 0 on success, otherwise a negative error code. */ int copier_set_gain(struct comp_dev *dev, struct copier_gain_params *gain_params, - struct gain_dma_control_data *gain_data, int channels); + struct gain_dma_control_data *gain_data, uint32_t channels); /** * Checks for unity gain mode. diff --git a/src/audio/copier/copier_generic.c b/src/audio/copier/copier_generic.c index da1ca4dbf942..bb5918819e5b 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,13 @@ 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)); + + /* src_queue id is host-controlled; bound it before indexing out_fmt[] */ + if (j >= IPC4_COPIER_MODULE_OUTPUT_PINS_COUNT) { + comp_err(dev, "invalid src_queue id %d", j); + continue; + } ipc4_update_buffer_format(sink, &cd->out_fmt[j]); } @@ -427,7 +433,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 +572,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..3e704b2b1163 100644 --- a/src/audio/copier/copier_host.c +++ b/src/audio/copier/copier_host.c @@ -9,6 +9,7 @@ #include <sof/tlv.h> #include <sof/trace/trace.h> #include <sof/audio/module_adapter/module/generic.h> +#include <rtos/userspace_helper.h> #include "copier.h" #include "host_copier.h" @@ -27,7 +28,7 @@ struct fpi_sync_group { struct list_item item; }; -static struct list_item group_list_head = LIST_INIT(group_list_head); +static APP_SYSUSER_DATA struct list_item group_list_head = LIST_INIT(group_list_head); __cold static struct fpi_sync_group *find_group_by_id(uint32_t id) { @@ -50,6 +51,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 +64,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 +85,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 +98,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 +136,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 +183,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 +211,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 +257,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 +285,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 +303,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..115c9a4a7e2d 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; @@ -225,13 +223,27 @@ __cold int copier_ipcgtw_create(struct comp_dev *dev, struct copier_data *cd, return -EINVAL; } + /* config_length is in dwords; require enough dwords to cover the + * gateway config header and the blob read below. Compare dword counts + * (rather than scaling config_length by 4) so a large host-supplied + * value cannot overflow the multiplication on 32-bit size_t. + */ + if (gtw_cfg->config_length < + SOF_DIV_ROUND_UP(sizeof(struct ipc4_gateway_config_data) + + sizeof(struct ipc4_ipc_gateway_config_blob), + sizeof(uint32_t))) { + comp_err(dev, "ipc4_gateway_config_data too small: %u", + gtw_cfg->config_length); + return -EINVAL; + } + cd->ipc_gtw = true; /* The IPC gateway is treated as a host gateway */ 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 +285,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..eb540efb86c4 100644 --- a/src/audio/copier/dai_copier.h +++ b/src/audio/copier/dai_copier.h @@ -61,7 +61,7 @@ static inline int dai_zephyr_multi_endpoint_copy(struct dai_data **dd, struct co return 0; } -static inline int dai_zephyr_unbind(struct dai_data *dd, struct comp_dev *dev, void *data) +static inline int dai_zephyr_unbind(struct dai_data *dd, struct comp_dev *dev, const void *data) { return 0; } @@ -69,7 +69,8 @@ static inline int dai_zephyr_unbind(struct dai_data *dd, struct comp_dev *dev, v int dai_zephyr_multi_endpoint_copy(struct dai_data **dd, struct comp_dev *dev, struct comp_buffer *multi_endpoint_buffer, int num_endpoints); -int dai_zephyr_unbind(struct dai_data *dd, struct comp_dev *dev, struct bind_info *unbind_data); +int dai_zephyr_unbind(struct dai_data *dd, struct comp_dev *dev, + const struct bind_info *unbind_data); #endif @@ -79,7 +80,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..d00907959e8f 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; @@ -65,7 +70,6 @@ struct host_data { /* host position reporting related */ uint32_t host_size; /**< Host buffer size (in bytes) */ - uint32_t report_pos; /**< Position in current report period */ uint32_t local_pos; /**< Local position in host buffer */ uint32_t host_period_bytes; uint16_t stream_tag; @@ -101,7 +105,10 @@ struct host_data { /* stream info */ struct sof_ipc_stream_posn posn; /* TODO: update this */ +#if CONFIG_HOST_DMA_IPC_POSITION_UPDATES + uint32_t report_pos; /**< Position in current report period */ struct ipc_msg *msg; /**< host notification */ +#endif #if CONFIG_XRUN_NOTIFICATIONS_ENABLE bool xrun_notification_sent; #endif @@ -112,6 +119,11 @@ 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 + + struct mod_alloc_ctx alloc_ctx; }; int host_common_new(struct host_data *hd, struct comp_dev *dev, @@ -129,10 +141,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..cbc45a04ec50 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; } @@ -369,26 +350,61 @@ static int crossover_free(struct processing_module *mod) * \brief Verifies that the config is formatted correctly. * * The function can only be called after the buffers have been initialized. + * + * \param[in] mod Processing module owning the configuration. The blob + * under validation is read from the module's private data + * (\c cd->config); callers must store it there beforehand. + * \param[in] new_size Size in bytes reported by the framework for the blob; + * must match the size field embedded in the blob header. + * \return 0 on success, negative errno on invalid configuration. */ -static int crossover_validate_config(struct processing_module *mod, - struct sof_crossover_config *config) +static int crossover_validate_config(struct processing_module *mod, size_t new_size) { + struct comp_data *cd = module_get_private_data(mod); + struct sof_crossover_config *config = cd->config; struct comp_dev *dev = mod->dev; - uint32_t size = config->size; + size_t required_size; int32_t num_assigned_sinks; + int32_t num_lr4s; + uint32_t size; + + if (!config) { + comp_err(dev, "NULL config blob"); + return -EINVAL; + } - if (size > SOF_CROSSOVER_MAX_SIZE || !size) { - comp_err(dev, "crossover_validate_config(), size %d is invalid", size); + /* Reject truncated blobs before touching config->size: the framework- + * reported new_size must cover at least the fixed header. + */ + if (new_size < sizeof(*config)) { + comp_err(dev, "size %u is smaller than header", (uint32_t)new_size); + return -EINVAL; + } + + size = config->size; + if (size > SOF_CROSSOVER_MAX_SIZE || size != new_size) { + comp_err(dev, "size %u 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; } + /* Each channel reads 2 * num_lr4s biquads from config->coef[]; the + * runtime uses 1 LR4 pair for 2-way and 3 LR4 pairs otherwise. + * See tune/sof_crossover_generate_config.m script. + */ + num_lr4s = (config->num_sinks == CROSSOVER_2WAY_NUM_SINKS) ? 1 : 3; + required_size = sizeof(*config) + (size_t)num_lr4s * 2 * sizeof(struct sof_eq_iir_biquad); + if (size < required_size) { + comp_err(dev, "size %u too small for num_sinks %u", size, config->num_sinks); + return -EINVAL; + } + /* Align the crossover's sinks, to their respective configuration in * the config. */ @@ -398,7 +414,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 +431,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 +449,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) @@ -467,17 +483,39 @@ static int crossover_process_audio_stream(struct processing_module *mod, uint32_t frames = input_buffers[0].size; uint32_t frame_bytes = audio_stream_frame_bytes(input_buffers[0].data); uint32_t processed_bytes; + struct sof_crossover_config *prev_config; + uint32_t prev_num_sinks; + size_t cfg_size; 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); + prev_config = cd->config; + prev_num_sinks = prev_config ? prev_config->num_sinks : 0; + + cd->config = comp_get_data_blob(cd->model_handler, &cfg_size, NULL); + ret = crossover_validate_config(mod, cfg_size); + if (ret < 0) + return ret; + + /* num_sinks must not change at runtime: cd->crossover_split was + * selected for the previous count, and a smaller value would + * cause the split function to write past the sink array. + * Reject the blob; the previous config and split stay in use. + */ + if (prev_num_sinks && cd->config->num_sinks != prev_num_sinks) { + comp_err(dev, "runtime num_sinks change %u -> %u not supported", + prev_num_sinks, cd->config->num_sinks); + cd->config = prev_config; + return -EINVAL; + } + 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 +568,11 @@ 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; + int ret; - comp_info(dev, "crossover_prepare()"); + comp_info(dev, "entry"); source = comp_dev_get_first_data_producer(dev); if (!source) { @@ -558,45 +598,45 @@ 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); - - /* 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); - } + cd->config = comp_get_data_blob(cd->model_handler, &data_size, NULL); + /* A NULL cd->config is a supported operating mode: the component runs + * in passthrough (see the else-branch below), so only validate when a + * blob is actually present. + */ if (cd->config) { - int ret = crossover_setup(mod, channels); + ret = crossover_validate_config(mod, data_size); + if (ret < 0) + return ret; + 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 +654,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 +689,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..49fb8e011ffd 100644 --- a/src/audio/dai-legacy.c +++ b/src/audio/dai-legacy.c @@ -19,7 +19,6 @@ #include <rtos/cache.h> #include <rtos/init.h> #include <sof/lib/dai.h> -#include <sof/lib/memory.h> /* for SHARED_DATA */ #include <sof/lib/notifier.h> #include <sof/lib/uuid.h> #include <sof/list.h> @@ -41,8 +40,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 +59,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 +70,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 +94,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 +133,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 +195,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 +210,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 +247,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 +290,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 +368,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 +376,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 +433,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 +441,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, @@ -469,7 +466,7 @@ int dai_common_params(struct dai_data *dd, struct comp_dev *dev, uint32_t period_bytes; uint32_t buffer_size; uint32_t addr_align; - uint32_t align; + uint32_t align = 0; int err; comp_dbg(dev, "dai_params()"); @@ -561,7 +558,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 +586,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 +613,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 +681,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 +708,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 +724,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 +738,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 +749,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 +792,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 +815,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 +840,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 +850,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 +864,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 +949,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 +987,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 +1048,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 +1065,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 +1082,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 +1102,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), @@ -1127,14 +1127,13 @@ static const struct comp_driver comp_dai = { }, }; -static SHARED_DATA struct comp_driver_info comp_dai_info = { +static struct comp_driver_info comp_dai_info = { .drv = &comp_dai, }; UT_STATIC void sys_comp_dai_init(void) { - comp_register(platform_shared_get(&comp_dai_info, - sizeof(comp_dai_info))); + comp_register(&comp_dai_info); } DECLARE_MODULE(sys_comp_dai_init); diff --git a/src/audio/dai-zephyr.c b/src/audio/dai-zephyr.c index ef1abb48cc31..9f4e5dc57295 100644 --- a/src/audio/dai-zephyr.c +++ b/src/audio/dai-zephyr.c @@ -19,7 +19,6 @@ #include <rtos/cache.h> #include <rtos/init.h> #include <sof/lib/dai.h> -#include <sof/lib/memory.h> /* for SHARED_DATA */ #include <sof/lib/notifier.h> #include <sof/lib/uuid.h> #include <sof/lib/dma.h> @@ -37,7 +36,6 @@ #include <stdint.h> #if CONFIG_XRUN_NOTIFICATIONS_ENABLE -#include <sof/ipc/notification_pool.h> #include <ipc4/notification.h> #endif @@ -64,8 +62,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 +139,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,65 +191,133 @@ __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_AMD_HS: + case SOF_DAI_AMD_HS_VIRTUAL: + case SOF_DAI_AMD_SP: + case SOF_DAI_AMD_SP_VIRTUAL: + case SOF_DAI_AMD_BT: + case SOF_DAI_AMD_TDM: + cfg.type = DAI_AMD_TDM; + cfg_params = &sof_cfg->acptdm; + 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; + case SOF_DAI_MEDIATEK_AFE: + cfg.type = DAI_MEDIATEK_AFE; + cfg_params = &sof_cfg->afe; + 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); +} + +static int dai_get_properties_safe(struct dai *dai, int direction, + int stream_id, struct dai_properties *props) +{ + const struct dai_properties *props_p; + int ret; + + ret = dai_get_properties_copy(dai->dev, direction, stream_id, props); + + /* + * User LL builds require DAI drivers to implement the copy method. + * Allow fallback to dai_get_properties() in kernel LL builds. + */ + if (ret == -ENOSYS && !IS_ENABLED(CONFIG_SOF_USERSPACE_LL)) { + k_spinlock_key_t key = k_spin_lock(&dai->lock); + + /* legacy support for drivers that do not implement copy */ + props_p = dai_get_properties(dai->dev, direction, stream_id); + if (props_p) { + *props = *props_p; + ret = 0; + } else { + ret = -ENOENT; + } + + k_spin_unlock(&dai->lock, key); + } + + return ret; } /* called from ipc/ipc3/dai.c */ int dai_get_handshake(struct dai *dai, int direction, int stream_id) { - k_spinlock_key_t key = k_spin_lock(&dai->lock); - const struct dai_properties *props = dai_get_properties(dai->dev, direction, - stream_id); - int hs_id = props->dma_hs_id; + struct dai_properties props; + int ret; - k_spin_unlock(&dai->lock, key); + ret = dai_get_properties_safe(dai, direction, stream_id, &props); + if (ret < 0) + return ret; - return hs_id; + return props.dma_hs_id; } /* called from ipc/ipc3/dai.c and ipc/ipc4/dai.c */ int dai_get_fifo_depth(struct dai *dai, int direction) { - const struct dai_properties *props; - k_spinlock_key_t key; - int fifo_depth; + struct dai_properties props; + int ret; if (!dai) return 0; - key = k_spin_lock(&dai->lock); - props = dai_get_properties(dai->dev, direction, 0); - fifo_depth = props->fifo_depth; - k_spin_unlock(&dai->lock, key); + ret = dai_get_properties_safe(dai, direction, 0, &props); + + if (ret < 0) + return 0; - return fifo_depth; + return props.fifo_depth; } int dai_get_stream_id(struct dai *dai, int direction) { - k_spinlock_key_t key = k_spin_lock(&dai->lock); - const struct dai_properties *props = dai_get_properties(dai->dev, direction, 0); - int stream_id = props->stream_id; + struct dai_properties props; + int ret; + + ret = dai_get_properties_safe(dai, direction, 0, &props); - k_spin_unlock(&dai->lock, key); + if (ret < 0) + return ret; - return stream_id; + return props.stream_id; } static int dai_get_fifo(struct dai *dai, int direction, int stream_id) { - k_spinlock_key_t key = k_spin_lock(&dai->lock); - const struct dai_properties *props = dai_get_properties(dai->dev, direction, - stream_id); - int fifo_address = props->fifo_address; + struct dai_properties props; + int ret; + + ret = dai_get_properties_safe(dai, direction, stream_id, &props); - k_spin_unlock(&dai->lock, key); + if (ret < 0) + return ret; - return fifo_address; + return props.fifo_address; } /* this is called by DMA driver every time descriptor has completed */ @@ -263,7 +328,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) { @@ -289,37 +354,41 @@ dai_dma_cb(struct dai_data *dd, struct comp_dev *dev, uint32_t bytes, /* * copy from local buffer to all sinks that are not gateway buffers * using the right PCM converter function. + * Skip in case of endpoint DAI devices created by the copier. */ - struct comp_buffer *sink; + if (converter) { + struct comp_buffer *sink; - comp_dev_for_each_consumer(dev, sink) { - struct comp_dev *sink_dev; - int j; + comp_dev_for_each_consumer(dev, sink) { + struct comp_dev *sink_dev; + int j; - if (sink == dd->dma_buffer) - continue; + if (sink == dd->dma_buffer) + continue; - sink_dev = comp_buffer_get_sink_component(sink); + sink_dev = comp_buffer_get_sink_component(sink); - j = IPC4_SRC_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", - j, IPC4_COPIER_MODULE_OUTPUT_PINS_COUNT); - ret = -EINVAL; - continue; - } + if (j >= IPC4_COPIER_MODULE_OUTPUT_PINS_COUNT) { + comp_err(dev, "Sink queue ID: %d >= max output pin count: %d\n", + j, IPC4_COPIER_MODULE_OUTPUT_PINS_COUNT); + ret = -EINVAL; + continue; + } - if (!converter[j]) { - comp_err(dev, "No PCM converter for sink queue %d\n", j); - ret = -EINVAL; - continue; - } + if (!converter[j]) { + comp_err(dev, "No PCM converter for sink queue %d\n", j); + ret = -EINVAL; + continue; + } - if (sink_dev && sink_dev->state == COMP_STATE_ACTIVE && - audio_buffer_hw_params_configured(&sink->audio_buffer)) { - ret = stream_copy_from_no_consume(dev, dd->local_buffer, sink, - converter[j], bytes, dd->chmap); + if (sink_dev && sink_dev->state == COMP_STATE_ACTIVE && + audio_buffer_hw_params_configured(&sink->audio_buffer)) { + ret = stream_copy_from_no_consume(dev, dd->local_buffer, + sink, converter[j], + bytes, dd->chmap); + } } } #endif @@ -362,7 +431,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 +477,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 +491,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 +564,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 +576,17 @@ __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; + +#ifdef CONFIG_SOF_USERSPACE_LL + /* + * copier_dai_create() uses mod_zalloc() to allocate + * the 'dd' dai data object and does not set dd->alloc_ctx. + * If LL is run in user-space, assign the 'heap' here. + */ + dd->alloc_ctx.heap = sof_sys_user_heap_get(); +#else + dd->alloc_ctx.heap = NULL; +#endif /* 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 +621,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 @@ -560,6 +640,7 @@ __cold static struct comp_dev *dai_new(const struct comp_driver *drv, struct comp_dev *dev; const struct ipc_config_dai *dai_cfg = spec; struct dai_data *dd; + struct k_heap *heap = NULL; int ret; assert_can_be_cold(); @@ -572,10 +653,12 @@ __cold static struct comp_dev *dai_new(const struct comp_driver *drv, dev->ipc_config = *config; - dd = rzalloc(SOF_MEM_FLAG_USER | SOF_MEM_FLAG_COHERENT, sizeof(*dd)); + dd = sof_heap_alloc(heap, SOF_MEM_FLAG_USER | SOF_MEM_FLAG_COHERENT, sizeof(*dd), 0); if (!dd) goto e_data; + memset(dd, 0, sizeof(*dd)); + comp_set_drvdata(dev, dd); ret = dai_common_new(dd, dev, dai_cfg); @@ -589,9 +672,9 @@ __cold static struct comp_dev *dai_new(const struct comp_driver *drv, return dev; error: - rfree(dd); + sof_heap_free(heap, dd); e_data: - rfree(dev); + comp_free_device(dev); return NULL; } @@ -600,15 +683,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); @@ -617,7 +700,7 @@ __cold void dai_common_free(struct dai_data *dd) dai_put(dd->dai); - rfree(dd->dai_spec_config); + sof_heap_free(dd->alloc_ctx.heap, dd->dai_spec_config); } __cold static void dai_free(struct comp_dev *dev) @@ -631,8 +714,9 @@ __cold static void dai_free(struct comp_dev *dev) dai_common_free(dd); - rfree(dd); - rfree(dev); + /* heap is NULL to match what is passed in dai_new() */ + sof_heap_free(NULL, dd); + comp_free_device(dev); } int dai_common_get_hw_params(struct dai_data *dd, struct comp_dev *dev, @@ -641,7 +725,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 +767,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 +882,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 +910,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(dd->alloc_ctx.heap, &config->elem_array, SOF_MEM_FLAG_USER, config->direction, period_count, period_bytes, @@ -850,10 +934,11 @@ 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, - sizeof(struct dma_config)); + dma_cfg = sof_heap_alloc(dd->alloc_ctx.heap, + SOF_MEM_FLAG_USER | SOF_MEM_FLAG_COHERENT | SOF_MEM_FLAG_DMA, + sizeof(struct dma_config), 0); if (!dma_cfg) { comp_err(dev, "dma_cfg allocation failed"); return -ENOMEM; @@ -882,10 +967,11 @@ static int dai_set_dma_config(struct dai_data *dd, struct comp_dev *dev) else dma_cfg->dma_slot = config->src_dev; - dma_block_cfg = rballoc(SOF_MEM_FLAG_USER | SOF_MEM_FLAG_COHERENT | SOF_MEM_FLAG_DMA, - sizeof(struct dma_block_config) * dma_cfg->block_count); + dma_block_cfg = sof_heap_alloc(dd->alloc_ctx.heap, + SOF_MEM_FLAG_USER | SOF_MEM_FLAG_COHERENT | SOF_MEM_FLAG_DMA, + sizeof(struct dma_block_config) * dma_cfg->block_count, 0); if (!dma_block_cfg) { - rfree(dma_cfg); + sof_heap_free(dd->alloc_ctx.heap, dma_cfg); comp_err(dev, "dma_block_config allocation failed"); return -ENOMEM; } @@ -931,10 +1017,10 @@ static int dai_set_dma_buffer(struct dai_data *dd, struct comp_dev *dev, uint32_t buffer_size; uint32_t buffer_size_preferred; uint32_t addr_align; - uint32_t align; + uint32_t align = 0; 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 +1040,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 +1105,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(&dd->alloc_ctx, buffer_size_preferred, buffer_size, SOF_MEM_FLAG_USER | SOF_MEM_FLAG_DMA, addr_align, BUFFER_USAGE_NOT_SHARED); if (!dd->dma_buffer) { @@ -1107,8 +1193,8 @@ 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); - rfree(dd->z_config); + dma_sg_free(dd->alloc_ctx.heap, &config->elem_array); + sof_heap_free(dd->alloc_ctx.heap, dd->z_config); dd->z_config = NULL; } @@ -1119,7 +1205,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 +1225,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 +1241,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 +1259,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 +1281,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 +1293,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,10 +1320,10 @@ 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(dd->alloc_ctx.heap, &config->elem_array); if (dd->z_config) { - rfree(dd->z_config->head_block); - rfree(dd->z_config); + sof_heap_free(dd->alloc_ctx.heap, dd->z_config->head_block); + sof_heap_free(dd->alloc_ctx.heap, dd->z_config); dd->z_config = NULL; } @@ -1259,7 +1341,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 +1368,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 +1406,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 +1443,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 +1457,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 +1532,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 +1550,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 +1571,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 +1664,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 +1690,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 +1882,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 +1892,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 +1932,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 +1955,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); @@ -1931,17 +2013,17 @@ static int dai_ts_stop_op(struct comp_dev *dev) uint32_t dai_get_init_delay_ms(struct dai *dai) { - const struct dai_properties *props; - k_spinlock_key_t key; - uint32_t init_delay; + struct dai_properties props; + uint32_t init_delay = 0; + int ret; if (!dai) return 0; - key = k_spin_lock(&dai->lock); - props = dai_get_properties(dai->dev, 0, 0); - init_delay = props->reg_init_delay; - k_spin_unlock(&dai->lock, key); + ret = dai_get_properties_safe(dai, 0, 0, &props); + + if (!ret) + init_delay = props.reg_init_delay; return init_delay; } @@ -1962,10 +2044,10 @@ static uint64_t dai_get_processed_data(struct comp_dev *dev, uint32_t stream_no, } #ifdef CONFIG_IPC_MAJOR_4 -__cold -int dai_zephyr_unbind(struct dai_data *dd, struct comp_dev *dev, struct bind_info *unbind_data) +__cold int dai_zephyr_unbind(struct dai_data *dd, struct comp_dev *dev, + const struct bind_info *unbind_data) { - struct ipc4_module_bind_unbind *bu; + const struct ipc4_module_bind_unbind *bu; int buf_id; assert_can_be_cold(); @@ -1984,6 +2066,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), @@ -2007,13 +2091,13 @@ static const struct comp_driver comp_dai = { }, }; -static SHARED_DATA struct comp_driver_info comp_dai_info = { +static struct comp_driver_info comp_dai_info = { .drv = &comp_dai, }; UT_STATIC void sys_comp_dai_init(void) { - comp_register(platform_shared_get(&comp_dai_info, sizeof(comp_dai_info))); + comp_register(&comp_dai_info); } DECLARE_MODULE(sys_comp_dai_init); diff --git a/src/audio/data_blob.c b/src/audio/data_blob.c index 42d612fb3104..94ea19e829a7 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; } @@ -330,6 +330,14 @@ int ipc4_comp_data_blob_set(struct comp_data_blob_handler *blob_handler, "data_offset = %d", data_offset); + /* Reject a new first_block mid-transfer: reusing stale data_new while + * overwriting new_data_size defeats the memcpy_s bound (heap overflow). + */ + if (blob_handler->data_new && first_block) { + comp_err(blob_handler->dev, "busy with previous request"); + return -EBUSY; + } + /* in case when the current package is the first, we should allocate * memory for whole model data */ @@ -365,8 +373,16 @@ int ipc4_comp_data_blob_set(struct comp_data_blob_handler *blob_handler, valid_data_size = last_block ? data_offset : MAILBOX_DSPBOX_SIZE; ret = memcpy_s((char *)blob_handler->data_new, - valid_data_size, data, valid_data_size); - assert(!ret); + blob_handler->new_data_size, data, valid_data_size); + if (ret) { + comp_err(blob_handler->dev, "failed to copy fragment"); + blob_handler->free(blob_handler->data_new); + blob_handler->data_new = NULL; + blob_handler->new_data_size = 0; + blob_handler->data_pos = 0; + blob_handler->data_ready = false; + return ret; + } blob_handler->data_pos += valid_data_size; } else { @@ -391,8 +407,17 @@ int ipc4_comp_data_blob_set(struct comp_data_blob_handler *blob_handler, valid_data_size = blob_handler->new_data_size - data_offset; ret = memcpy_s((char *)blob_handler->data_new + data_offset, - valid_data_size, data, valid_data_size); - assert(!ret); + blob_handler->new_data_size - data_offset, + data, valid_data_size); + if (ret) { + comp_err(blob_handler->dev, "failed to copy fragment"); + blob_handler->free(blob_handler->data_new); + blob_handler->data_new = NULL; + blob_handler->new_data_size = 0; + blob_handler->data_pos = 0; + blob_handler->data_ready = false; + return ret; + } blob_handler->data_pos += valid_data_size; } @@ -441,13 +466,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 +481,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 +606,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 +615,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); } @@ -603,6 +628,18 @@ int comp_data_blob_get_cmd(struct comp_data_blob_handler *blob_handler, return -EINVAL; } + /* Bound data_pos against data_size: host-controlled num_elems + * advances it per fragment and could leak adjacent heap. + */ + if (blob_handler->data_pos >= blob_handler->data_size || + cdata->num_elems > blob_handler->data_size - blob_handler->data_pos) { + comp_err(blob_handler->dev, + "out of bounds read: pos %u elems %u size %u", + blob_handler->data_pos, cdata->num_elems, + blob_handler->data_size); + return -EINVAL; + } + /* copy required size of data */ ret = memcpy_s(cdata->data->data, size, (char *)blob_handler->data + blob_handler->data_pos, @@ -624,12 +661,13 @@ EXPORT_SYMBOL(comp_data_blob_get_cmd); static void *default_alloc(size_t size) { - return rballoc(SOF_MEM_FLAG_USER, size); + return sof_heap_alloc(sof_sys_user_heap_get(), + SOF_MEM_FLAG_USER | SOF_MEM_FLAG_LARGE_BUFFER, size, 0); } static void default_free(void *buf) { - rfree(buf); + sof_heap_free(sof_sys_user_heap_get(), buf); } struct comp_data_blob_handler * @@ -639,12 +677,13 @@ 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)); + handler = sof_heap_alloc(sof_sys_user_heap_get(), SOF_MEM_FLAG_USER, + sizeof(struct comp_data_blob_handler), 0); if (handler) { + memset(handler, 0, sizeof(*handler)); handler->dev = dev; handler->single_blob = single_blob; handler->alloc = alloc ? alloc : default_alloc; @@ -662,6 +701,6 @@ void comp_data_blob_handler_free(struct comp_data_blob_handler *blob_handler) comp_free_data_blob(blob_handler); - rfree(blob_handler); + sof_heap_free(sof_sys_user_heap_get(), blob_handler); } EXPORT_SYMBOL(comp_data_blob_handler_free); 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..6e620b2983d7 100644 --- a/src/audio/dcblock/dcblock.c +++ b/src/audio/dcblock/dcblock.c @@ -10,6 +10,8 @@ #include <sof/audio/data_blob.h> #include <sof/audio/format.h> #include <sof/audio/pipeline.h> +#include <sof/audio/sink_api.h> +#include <sof/audio/source_api.h> #include <sof/audio/ipc-config.h> #include <sof/common.h> #include <rtos/panic.h> @@ -36,8 +38,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 +48,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 +82,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 +97,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 +112,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; @@ -158,25 +142,62 @@ static int dcblock_set_config(struct processing_module *mod, uint32_t config_id, /** * \brief Copies and processes stream data. - * \param[in,out] dev DC Blocking Filter module. + * \param[in,out] mod DC Blocking Filter module. * \return Error code. */ static int dcblock_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 sof_source **sources, int num_of_sources, + struct sof_sink **sinks, int num_of_sinks) { struct comp_data *cd = module_get_private_data(mod); - struct audio_stream *source = input_buffers[0].data; - struct audio_stream *sink = output_buffers[0].data; - uint32_t frames = input_buffers[0].size; + struct comp_dev *dev = mod->dev; + struct sof_source *source = sources[0]; + struct sof_sink *sink = sinks[0]; + struct cir_buf_source source_buf; + struct cir_buf_sink sink_buf; + size_t source_frame_bytes = source_get_frame_bytes(source); + size_t sink_frame_bytes = sink_get_frame_bytes(sink); + size_t source_bytes, sink_bytes, bytes; + uint32_t frames = source_get_data_frames_available(source); + uint32_t sink_frames = sink_get_free_frames(sink); + int ret; - comp_dbg(mod->dev, "dcblock_process()"); + comp_dbg(dev, "entry"); - cd->dcblock_func(cd, source, sink, frames); + frames = MIN(frames, sink_frames); + if (!frames) + return 0; - module_update_buffer_position(&input_buffers[0], &output_buffers[0], frames); + source_bytes = frames * source_frame_bytes; + sink_bytes = frames * sink_frame_bytes; + + /* acquire the source and sink circular buffer views once */ + ret = source_get_data(source, source_bytes, &source_buf.ptr, + &source_buf.buf_start, &bytes); + if (ret) + return ret; + source_buf.buf_end = (const char *)source_buf.buf_start + bytes; + + ret = sink_get_buffer(sink, sink_bytes, &sink_buf.ptr, + &sink_buf.buf_start, &bytes); + if (ret) { + source_release_data(source, 0); + return ret; + } + sink_buf.buf_end = (char *)sink_buf.buf_start + bytes; + + ret = cd->dcblock_func(cd, &source_buf, &sink_buf, frames); + if (ret) { + /* Undo the acquire without consuming source data or + * publishing partially-written output. + */ + source_release_data(source, 0); + sink_commit_buffer(sink, 0); + return ret; + } + + source_release_data(source, source_bytes); + sink_commit_buffer(sink, sink_bytes); return 0; } @@ -190,39 +211,57 @@ static int dcblock_prepare(struct processing_module *mod, struct sof_sink **sinks, int num_of_sinks) { 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); - sinkb = comp_dev_get_first_data_consumer(dev); - if (!sourceb || !sinkb) { - comp_err(dev, "no source or sink buffer"); - return -ENOTCONN; + if (num_of_sources != 1 || num_of_sinks != 1) { + comp_err(dev, "Invalid number of sources/sinks"); + return -EINVAL; } dcblock_params(mod); /* get source data format */ - cd->source_format = audio_stream_get_frm_fmt(&sourceb->stream); + cd->source_format = source_get_frm_fmt(sources[0]); - /* get sink data format and period bytes */ - cd->sink_format = audio_stream_get_frm_fmt(&sinkb->stream); + /* get sink data format */ + cd->sink_format = sink_get_frm_fmt(sinks[0]); + + if (cd->source_format != cd->sink_format) { + comp_err(dev, "source and sink frame formats do not match"); + return -EINVAL; + } + + /* The processing uses a single channel count as the frame stride for + * both source and sink, so they must match to avoid corrupt output. + */ + if (source_get_channels(sources[0]) != sink_get_channels(sinks[0])) { + comp_err(dev, "mismatch source/sink stream channels"); + return -EINVAL; + } + + /* get channel count for processing */ + cd->channels = source_get_channels(sources[0]); 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); + /* dcblock_copy_coefficients() copies sizeof(R_coeffs) from the blob, so + * require the blob to actually hold that many bytes; fall back to + * passthrough otherwise instead of over-reading the blob + */ + if (cd->config && data_size >= sizeof(cd->R_coeffs)) dcblock_copy_coefficients(mod); else dcblock_set_passthrough(mod); @@ -239,7 +278,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); @@ -251,7 +290,7 @@ static int dcblock_reset(struct processing_module *mod) static const struct module_interface dcblock_interface = { .init = dcblock_init, .prepare = dcblock_prepare, - .process_audio_stream = dcblock_process, + .process = dcblock_process, .set_configuration = dcblock_set_config, .get_configuration = dcblock_get_config, .reset = dcblock_reset, @@ -272,6 +311,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.h b/src/audio/dcblock/dcblock.h index eaf09da22bf2..dca1308b80ba 100644 --- a/src/audio/dcblock/dcblock.h +++ b/src/audio/dcblock/dcblock.h @@ -12,11 +12,17 @@ #include <sof/platform.h> #include <ipc/stream.h> #include <sof/compiler_info.h> +#include <sof/audio/sink_api.h> +#include <sof/audio/source_api.h> #include <module/module/base.h> #include <module/module/interface.h> struct audio_stream; struct comp_dev; +struct sof_source; +struct sof_sink; +struct cir_buf_source; +struct cir_buf_sink; struct dcblock_state { int32_t x_prev; /**< state variable referring to x[n-1] */ @@ -30,10 +36,10 @@ struct dcblock_state { struct comp_data; -typedef void (*dcblock_func)(struct comp_data *cd, - const struct audio_stream *source, - const struct audio_stream *sink, - uint32_t frames); +typedef int (*dcblock_func)(struct comp_data *cd, + struct cir_buf_source *source, + struct cir_buf_sink *sink, + uint32_t frames); /* DC Blocking Filter component private data */ struct comp_data { @@ -48,6 +54,7 @@ struct comp_data { enum sof_ipc_frame source_format; enum sof_ipc_frame sink_format; + int channels; /**< number of channels */ dcblock_func dcblock_func; /**< processing function */ }; diff --git a/src/audio/dcblock/dcblock_generic.c b/src/audio/dcblock/dcblock_generic.c index 4ff5fa11315b..0c8468868949 100644 --- a/src/audio/dcblock/dcblock_generic.c +++ b/src/audio/dcblock/dcblock_generic.c @@ -7,6 +7,7 @@ #include <stdint.h> #include <sof/audio/component.h> #include <sof/audio/format.h> +#include <sof/audio/audio_stream.h> #include "dcblock.h" @@ -36,119 +37,141 @@ static int32_t dcblock_generic(struct dcblock_state *state, } #if CONFIG_FORMAT_S16LE -static void dcblock_s16_default(struct comp_data *cd, - const struct audio_stream *source, - const struct audio_stream *sink, - uint32_t frames) +/** + * dcblock_s16_default() - Process S16_LE format. + * @cd: DC blocking filter component private data. + * @source: Source for PCM samples data. + * @sink: Sink for PCM samples data. + * @frames: Number of audio data frames to process. + * + * Return: Value zero for success, otherwise an error code. + */ +static int dcblock_s16_default(struct comp_data *cd, + struct cir_buf_source *source, + struct cir_buf_sink *sink, + uint32_t frames) { - struct dcblock_state *state; - int16_t *x = audio_stream_get_rptr(source); - int16_t *y = audio_stream_get_wptr(sink); - int32_t R; + const int16_t *x = source->ptr; + int16_t *y = sink->ptr; + int samples_without_wrap; + int nch = cd->channels; + int remaining_samples = frames * nch; int32_t tmp; - int idx; - int ch; - int i, n, nmax; - int nch = audio_stream_get_channels(source); - int samples = nch * frames; - - while (samples) { - nmax = audio_stream_samples_without_wrap_s16(source, x); - n = MIN(samples, nmax); - nmax = audio_stream_samples_without_wrap_s16(sink, y); - n = MIN(n, nmax); - for (ch = 0; ch < nch; ch++) { - state = &cd->state[ch]; - R = cd->R_coeffs[ch]; - idx = ch; - for (i = 0; i < n; i += nch) { - tmp = dcblock_generic(state, R, x[idx] << 16); - y[idx] = sat_int16(Q_SHIFT_RND(tmp, 31, 15)); - idx += nch; - } + int ch = 0; + int i; + + while (remaining_samples) { + samples_without_wrap = cir_buf_samples_without_wrap_s16(x, source->buf_end); + samples_without_wrap = MIN(samples_without_wrap, + cir_buf_samples_without_wrap_s16(y, sink->buf_end)); + samples_without_wrap = MIN(samples_without_wrap, remaining_samples); + for (i = 0; i < samples_without_wrap; i++) { + tmp = dcblock_generic(&cd->state[ch], cd->R_coeffs[ch], + *x << 16); + *y = sat_int16(Q_SHIFT_RND(tmp, 31, 15)); + x++; + y++; + if (++ch == nch) + ch = 0; } - samples -= n; - x = audio_stream_wrap(source, x + n); - y = audio_stream_wrap(sink, y + n); + x = cir_buf_wrap((void *)x, source->buf_start, source->buf_end); + y = cir_buf_wrap(y, sink->buf_start, sink->buf_end); + remaining_samples -= samples_without_wrap; } + return 0; } #endif /* CONFIG_FORMAT_S16LE */ #if CONFIG_FORMAT_S24LE -static void dcblock_s24_default(struct comp_data *cd, - const struct audio_stream *source, - const struct audio_stream *sink, - uint32_t frames) +/** + * dcblock_s24_default() - Process S24_4LE format. + * @cd: DC blocking filter component private data. + * @source: Source for PCM samples data. + * @sink: Sink for PCM samples data. + * @frames: Number of audio data frames to process. + * + * Return: Value zero for success, otherwise an error code. + */ +static int dcblock_s24_default(struct comp_data *cd, + struct cir_buf_source *source, + struct cir_buf_sink *sink, + uint32_t frames) { - struct dcblock_state *state; - int32_t *x = audio_stream_get_rptr(source); - int32_t *y = audio_stream_get_wptr(sink); - int32_t R; + const int32_t *x = source->ptr; + int32_t *y = sink->ptr; + int samples_without_wrap; + int nch = cd->channels; + int remaining_samples = frames * nch; int32_t tmp; - int idx; - int ch; - int i, n, nmax; - int nch = audio_stream_get_channels(source); - int samples = nch * frames; - - while (samples) { - nmax = audio_stream_samples_without_wrap_s24(source, x); - n = MIN(samples, nmax); - nmax = audio_stream_samples_without_wrap_s24(sink, y); - n = MIN(n, nmax); - for (ch = 0; ch < nch; ch++) { - state = &cd->state[ch]; - R = cd->R_coeffs[ch]; - idx = ch; - for (i = 0; i < n; i += nch) { - tmp = dcblock_generic(state, R, x[idx] << 8); - y[idx] = sat_int24(Q_SHIFT_RND(tmp, 31, 23)); - idx += nch; - } + int ch = 0; + int i; + + while (remaining_samples) { + samples_without_wrap = cir_buf_samples_without_wrap_s32(x, source->buf_end); + samples_without_wrap = MIN(samples_without_wrap, + cir_buf_samples_without_wrap_s32(y, sink->buf_end)); + samples_without_wrap = MIN(samples_without_wrap, remaining_samples); + for (i = 0; i < samples_without_wrap; i++) { + tmp = dcblock_generic(&cd->state[ch], cd->R_coeffs[ch], + *x << 8); + *y = sat_int24(Q_SHIFT_RND(tmp, 31, 23)); + x++; + y++; + if (++ch == nch) + ch = 0; } - samples -= n; - x = audio_stream_wrap(source, x + n); - y = audio_stream_wrap(sink, y + n); + x = cir_buf_wrap((void *)x, source->buf_start, source->buf_end); + y = cir_buf_wrap(y, sink->buf_start, sink->buf_end); + remaining_samples -= samples_without_wrap; } + return 0; } #endif /* CONFIG_FORMAT_S24LE */ #if CONFIG_FORMAT_S32LE -static void dcblock_s32_default(struct comp_data *cd, - const struct audio_stream *source, - const struct audio_stream *sink, - uint32_t frames) +/** + * dcblock_s32_default() - Process S32_LE format. + * @cd: DC blocking filter component private data. + * @source: Source for PCM samples data. + * @sink: Sink for PCM samples data. + * @frames: Number of audio data frames to process. + * + * Return: Value zero for success, otherwise an error code. + */ +static int dcblock_s32_default(struct comp_data *cd, + struct cir_buf_source *source, + struct cir_buf_sink *sink, + uint32_t frames) { - struct dcblock_state *state; - int32_t *x = audio_stream_get_rptr(source); - int32_t *y = audio_stream_get_wptr(sink); - int32_t R; - int idx; - int ch; - int i, n, nmax; - int nch = audio_stream_get_channels(source); - int samples = nch * frames; - - while (samples) { - nmax = audio_stream_samples_without_wrap_s32(source, x); - n = MIN(samples, nmax); - nmax = audio_stream_samples_without_wrap_s32(sink, y); - n = MIN(n, nmax); - for (ch = 0; ch < nch; ch++) { - state = &cd->state[ch]; - R = cd->R_coeffs[ch]; - idx = ch; - for (i = 0; i < n; i += nch) { - y[idx] = dcblock_generic(state, R, x[idx]); - idx += nch; - } + const int32_t *x = source->ptr; + int32_t *y = sink->ptr; + int samples_without_wrap; + int nch = cd->channels; + int remaining_samples = frames * nch; + int ch = 0; + int i; + + while (remaining_samples) { + samples_without_wrap = cir_buf_samples_without_wrap_s32(x, source->buf_end); + samples_without_wrap = MIN(samples_without_wrap, + cir_buf_samples_without_wrap_s32(y, sink->buf_end)); + samples_without_wrap = MIN(samples_without_wrap, remaining_samples); + for (i = 0; i < samples_without_wrap; i++) { + *y = dcblock_generic(&cd->state[ch], cd->R_coeffs[ch], + *x); + x++; + y++; + if (++ch == nch) + ch = 0; } - samples -= n; - x = audio_stream_wrap(source, x + n); - y = audio_stream_wrap(sink, y + n); + x = cir_buf_wrap((void *)x, source->buf_start, source->buf_end); + y = cir_buf_wrap(y, sink->buf_start, sink->buf_end); + remaining_samples -= samples_without_wrap; } + + return 0; } #endif /* CONFIG_FORMAT_S32LE */ diff --git a/src/audio/dcblock/dcblock_hifi3.c b/src/audio/dcblock/dcblock_hifi3.c index d49a1ccca986..cd03fb101b16 100644 --- a/src/audio/dcblock/dcblock_hifi3.c +++ b/src/audio/dcblock/dcblock_hifi3.c @@ -7,6 +7,7 @@ #include <stdint.h> #include <sof/audio/component.h> #include <sof/audio/format.h> +#include <sof/audio/audio_stream.h> #include "dcblock.h" @@ -29,35 +30,44 @@ static inline ae_int32x2 dcblock_cal(ae_int32x2 R, ae_int32x2 state_x, ae_int32 return AE_ROUND32F64SSYM(AE_SLAI64S(out, 1)); } -/* Setup circular for component source */ -static inline void dcblock_set_circular(const struct audio_stream *source) +/* Set source as circular buffer 0 for the strided per-channel reads */ +static inline void dcblock_set_circular(void *src_begin, void *src_end) { - /* Set source as circular buffer 0 */ - AE_SETCBEGIN0(audio_stream_get_addr(source)); - AE_SETCEND0(audio_stream_get_end_addr(source)); + AE_SETCBEGIN0(src_begin); + AE_SETCEND0(src_end); } #if CONFIG_FORMAT_S16LE -static void dcblock_s16_default(struct comp_data *cd, - const struct audio_stream *source, - const struct audio_stream *sink, - uint32_t frames) +static int dcblock_s16_default(struct comp_data *cd, + struct cir_buf_source *source, + struct cir_buf_sink *sink, + uint32_t frames) { - ae_int16 *src = audio_stream_get_rptr(source); - ae_int16 *dst = audio_stream_get_wptr(sink); - ae_int16 *in; - ae_int16 *out; + ae_int16 *src = (ae_int16 *)source->ptr; + ae_int16 *dst = (ae_int16 *)sink->ptr; + ae_int16 *x_start = (ae_int16 *)source->buf_start; + ae_int16 *y_start = (ae_int16 *)sink->buf_start; + ae_int16 *x_end = (ae_int16 *)source->buf_end; + ae_int16 *y_end = (ae_int16 *)sink->buf_end; + ae_int16 *in, *out; ae_int32x2 R, state_x, state_y, sample; ae_int16x4 in_sample, out_sample; - int ch, i, n; - int nch = audio_stream_get_channels(source); + int x_size = x_end - x_start; + int y_size = y_end - y_start; + int nch = cd->channels; + int remaining = frames * nch; const int inc = nch * sizeof(ae_int16); - int samples = nch * frames; + int ch, i, n; + + /* Set source as circular buffer 0 for the strided per-channel reads */ + dcblock_set_circular(x_start, x_end); - dcblock_set_circular(source); - while (samples) { - n = audio_stream_samples_without_wrap_s16(sink, dst); - n = MIN(n, samples); + while (remaining) { + /* The sink uses linear stores, so limit the chunk to the sink + * contiguous space. The source wraps via circular addressing. + */ + n = y_end - dst; + n = MIN(n, remaining); for (ch = 0; ch < nch; ch++) { in = src + ch; out = dst + ch; @@ -77,34 +87,46 @@ static void dcblock_s16_default(struct comp_data *cd, cd->state[ch].x_prev = state_x; cd->state[ch].y_prev = state_y; } - samples -= n; - dst = audio_stream_wrap(sink, dst + n); - src = audio_stream_wrap(source, src + n); + remaining -= n; + dst += n; + if (dst >= y_end) + dst -= y_size; + src += n; + while (src >= x_end) + src -= x_size; } + + return 0; } #endif /* CONFIG_FORMAT_S16LE */ #if CONFIG_FORMAT_S24LE -static void dcblock_s24_default(struct comp_data *cd, - const struct audio_stream *source, - const struct audio_stream *sink, - uint32_t frames) +static int dcblock_s24_default(struct comp_data *cd, + struct cir_buf_source *source, + struct cir_buf_sink *sink, + uint32_t frames) { - ae_int32 *src = audio_stream_get_rptr(source); - ae_int32 *dst = audio_stream_get_wptr(sink); - ae_int32 *in; - ae_int32 *out; + ae_int32 *src = (ae_int32 *)source->ptr; + ae_int32 *dst = (ae_int32 *)sink->ptr; + ae_int32 *x_start = (ae_int32 *)source->buf_start; + ae_int32 *y_start = (ae_int32 *)sink->buf_start; + ae_int32 *x_end = (ae_int32 *)source->buf_end; + ae_int32 *y_end = (ae_int32 *)sink->buf_end; + ae_int32 *in, *out; ae_int32x2 R, state_x, state_y; ae_int32x2 in_sample, out_sample; - int ch, i, n; - int nch = audio_stream_get_channels(source); + int x_size = x_end - x_start; + int y_size = y_end - y_start; + int nch = cd->channels; + int remaining = frames * nch; const int inc = nch * sizeof(ae_int32); - int samples = nch * frames; + int ch, i, n; - dcblock_set_circular(source); - while (samples) { - n = audio_stream_samples_without_wrap_s24(sink, dst); - n = MIN(n, samples); + dcblock_set_circular(x_start, x_end); + + while (remaining) { + n = y_end - dst; + n = MIN(n, remaining); for (ch = 0; ch < nch; ch++) { in = src + ch; out = dst + ch; @@ -124,34 +146,46 @@ static void dcblock_s24_default(struct comp_data *cd, cd->state[ch].x_prev = state_x; cd->state[ch].y_prev = state_y; } - samples -= n; - dst = audio_stream_wrap(sink, dst + n); - src = audio_stream_wrap(source, src + n); + remaining -= n; + dst += n; + if (dst >= y_end) + dst -= y_size; + src += n; + while (src >= x_end) + src -= x_size; } + + return 0; } #endif /* CONFIG_FORMAT_S24LE */ #if CONFIG_FORMAT_S32LE -static void dcblock_s32_default(struct comp_data *cd, - const struct audio_stream *source, - const struct audio_stream *sink, - uint32_t frames) +static int dcblock_s32_default(struct comp_data *cd, + struct cir_buf_source *source, + struct cir_buf_sink *sink, + uint32_t frames) { - ae_int32 *src = audio_stream_get_rptr(source); - ae_int32 *dst = audio_stream_get_wptr(sink); - ae_int32 *in; - ae_int32 *out; + ae_int32 *src = (ae_int32 *)source->ptr; + ae_int32 *dst = (ae_int32 *)sink->ptr; + ae_int32 *x_start = (ae_int32 *)source->buf_start; + ae_int32 *y_start = (ae_int32 *)sink->buf_start; + ae_int32 *x_end = (ae_int32 *)source->buf_end; + ae_int32 *y_end = (ae_int32 *)sink->buf_end; + ae_int32 *in, *out; ae_int32x2 R, state_x, state_y; ae_int32x2 in_sample; - int ch, i, n; - int nch = audio_stream_get_channels(source); + int x_size = x_end - x_start; + int y_size = y_end - y_start; + int nch = cd->channels; + int remaining = frames * nch; const int inc = nch * sizeof(ae_int32); - int samples = nch * frames; + int ch, i, n; + + dcblock_set_circular(x_start, x_end); - dcblock_set_circular(source); - while (samples) { - n = audio_stream_samples_without_wrap_s32(sink, dst); - n = MIN(n, samples); + while (remaining) { + n = y_end - dst; + n = MIN(n, remaining); for (ch = 0; ch < nch; ch++) { in = src + ch; out = dst + ch; @@ -167,10 +201,16 @@ static void dcblock_s32_default(struct comp_data *cd, cd->state[ch].x_prev = state_x; cd->state[ch].y_prev = state_y; } - samples -= n; - dst = audio_stream_wrap(sink, dst + n); - src = audio_stream_wrap(source, src + n); + remaining -= n; + dst += n; + if (dst >= y_end) + dst -= y_size; + src += n; + while (src >= x_end) + src -= x_size; } + + return 0; } #endif /* CONFIG_FORMAT_S32LE */ diff --git a/src/audio/dcblock/dcblock_hifi4.c b/src/audio/dcblock/dcblock_hifi4.c index f6ff06a55a63..50a61acda505 100644 --- a/src/audio/dcblock/dcblock_hifi4.c +++ b/src/audio/dcblock/dcblock_hifi4.c @@ -7,6 +7,7 @@ #include <stdint.h> #include <sof/audio/component.h> #include <sof/audio/format.h> +#include <sof/audio/audio_stream.h> #include "dcblock.h" @@ -29,37 +30,40 @@ static inline ae_int32x2 dcblock_cal(ae_int32x2 R, ae_int32x2 state_x, ae_int32 return AE_ROUND32F64SSYM(AE_SLAI64S(out, 1)); } -/* Setup circular for component sink and source */ -static inline void dcblock_set_circular(const struct audio_stream *source, - const struct audio_stream *sink) +/* Set source as circular buffer 0 and sink as circular buffer 1 */ +static inline void dcblock_set_circular(void *src_begin, void *src_end, + void *sink_begin, void *sink_end) { - /* Set source as circular buffer 0 */ - AE_SETCBEGIN0(audio_stream_get_addr(source)); - AE_SETCEND0(audio_stream_get_end_addr(source)); - - /* Set sink as circular buffer 1 */ - AE_SETCBEGIN1(audio_stream_get_addr(sink)); - AE_SETCEND1(audio_stream_get_end_addr(sink)); + AE_SETCBEGIN0(src_begin); + AE_SETCEND0(src_end); + AE_SETCBEGIN1(sink_begin); + AE_SETCEND1(sink_end); } #if CONFIG_FORMAT_S16LE -static void dcblock_s16_default(struct comp_data *cd, - const struct audio_stream *source, - const struct audio_stream *sink, - uint32_t frames) +static int dcblock_s16_default(struct comp_data *cd, + struct cir_buf_source *source, + struct cir_buf_sink *sink, + uint32_t frames) { - ae_int16 *in; - ae_int16 *out; + ae_int16 *src = (ae_int16 *)source->ptr; + ae_int16 *dst = (ae_int16 *)sink->ptr; + ae_int16 *x_start = (ae_int16 *)source->buf_start; + ae_int16 *y_start = (ae_int16 *)sink->buf_start; + ae_int16 *x_end = (ae_int16 *)source->buf_end; + ae_int16 *y_end = (ae_int16 *)sink->buf_end; + ae_int16 *in, *out; ae_int32x2 R, state_x, state_y, sample; ae_int16x4 in_sample, out_sample; - int ch, i; - int nch = audio_stream_get_channels(source); + int nch = cd->channels; const int inc = nch * sizeof(ae_int16); + int ch, i; + + dcblock_set_circular(x_start, x_end, y_start, y_end); - dcblock_set_circular(source, sink); for (ch = 0; ch < nch; ch++) { - in = (ae_int16 *)audio_stream_get_rptr(source) + ch; - out = (ae_int16 *)audio_stream_get_wptr(sink) + ch; + in = src + ch; + out = dst + ch; state_x = cd->state[ch].x_prev; state_y = cd->state[ch].y_prev; R = cd->R_coeffs[ch]; @@ -76,28 +80,35 @@ static void dcblock_s16_default(struct comp_data *cd, cd->state[ch].x_prev = state_x; cd->state[ch].y_prev = state_y; } + + return 0; } #endif /* CONFIG_FORMAT_S16LE */ #if CONFIG_FORMAT_S24LE -static void dcblock_s24_default(struct comp_data *cd, - const struct audio_stream *source, - const struct audio_stream *sink, - uint32_t frames) +static int dcblock_s24_default(struct comp_data *cd, + struct cir_buf_source *source, + struct cir_buf_sink *sink, + uint32_t frames) { - ae_int32 *in; - ae_int32 *out; + ae_int32 *src = (ae_int32 *)source->ptr; + ae_int32 *dst = (ae_int32 *)sink->ptr; + ae_int32 *x_start = (ae_int32 *)source->buf_start; + ae_int32 *y_start = (ae_int32 *)sink->buf_start; + ae_int32 *x_end = (ae_int32 *)source->buf_end; + ae_int32 *y_end = (ae_int32 *)sink->buf_end; + ae_int32 *in, *out; ae_int32x2 R, state_x, state_y; ae_int32x2 in_sample, out_sample; - int ch, i; - int nch = audio_stream_get_channels(source); + int nch = cd->channels; const int inc = nch * sizeof(ae_int32); + int ch, i; - dcblock_set_circular(source, sink); - for (ch = 0; ch < nch; ch++) { - in = (ae_int32 *)audio_stream_get_rptr(source) + ch; - out = (ae_int32 *)audio_stream_get_wptr(sink) + ch; + dcblock_set_circular(x_start, x_end, y_start, y_end); + for (ch = 0; ch < nch; ch++) { + in = src + ch; + out = dst + ch; state_x = cd->state[ch].x_prev; state_y = cd->state[ch].y_prev; R = cd->R_coeffs[ch]; @@ -114,28 +125,35 @@ static void dcblock_s24_default(struct comp_data *cd, cd->state[ch].x_prev = state_x; cd->state[ch].y_prev = state_y; } + + return 0; } #endif /* CONFIG_FORMAT_S24LE */ #if CONFIG_FORMAT_S32LE -static void dcblock_s32_default(struct comp_data *cd, - const struct audio_stream *source, - const struct audio_stream *sink, - uint32_t frames) +static int dcblock_s32_default(struct comp_data *cd, + struct cir_buf_source *source, + struct cir_buf_sink *sink, + uint32_t frames) { - ae_int32 *in; - ae_int32 *out; + ae_int32 *src = (ae_int32 *)source->ptr; + ae_int32 *dst = (ae_int32 *)sink->ptr; + ae_int32 *x_start = (ae_int32 *)source->buf_start; + ae_int32 *y_start = (ae_int32 *)sink->buf_start; + ae_int32 *x_end = (ae_int32 *)source->buf_end; + ae_int32 *y_end = (ae_int32 *)sink->buf_end; + ae_int32 *in, *out; ae_int32x2 R, state_x, state_y; ae_int32x2 in_sample; - int ch, i; - int nch = audio_stream_get_channels(source); + int nch = cd->channels; const int inc = nch * sizeof(ae_int32); + int ch, i; - dcblock_set_circular(source, sink); - for (ch = 0; ch < nch; ch++) { - in = (ae_int32 *)audio_stream_get_rptr(source) + ch; - out = (ae_int32 *)audio_stream_get_wptr(sink) + ch; + dcblock_set_circular(x_start, x_end, y_start, y_end); + for (ch = 0; ch < nch; ch++) { + in = src + ch; + out = dst + ch; state_x = cd->state[ch].x_prev; state_y = cd->state[ch].y_prev; R = cd->R_coeffs[ch]; @@ -148,6 +166,8 @@ static void dcblock_s32_default(struct comp_data *cd, cd->state[ch].x_prev = state_x; cd->state[ch].y_prev = state_y; } + + return 0; } #endif /* CONFIG_FORMAT_S32LE */ 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..a727b68d55fd 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_alloc(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; @@ -138,6 +139,20 @@ static int drc_setup(struct drc_comp_data *cd, uint16_t channels, uint32_t rate) return drc_set_pre_delay_time(&cd->state, cd->config->params.pre_delay_time, rate); } +static int drc_validator(struct comp_dev *dev, void *new_data, uint32_t new_data_size) +{ + struct sof_drc_config *config = new_data; + + if (new_data_size != sizeof(struct sof_drc_config) || + new_data_size != config->size) { + comp_err(dev, "invalid configuration blob, size %u, expected %zu", + new_data_size, sizeof(struct sof_drc_config)); + return -EINVAL; + } + + return 0; +} + /* * End of DRC setup code. Next the standard component methods. */ @@ -146,46 +161,33 @@ __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()"); + comp_info(dev, "entry"); - /* 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; - } - - 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; - } + /* Reject malformed blobs at IPC time so a bad run-time update cannot + * replace the working configuration. + */ + comp_data_blob_set_validator(cd->model_handler, drc_validator); - 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 +198,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 +209,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 +224,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 +234,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 +244,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 +263,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 +281,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 +323,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); @@ -347,7 +349,7 @@ static int drc_prepare(struct processing_module *mod, 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 +369,19 @@ 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); + comp_info(dev, "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); + 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 +398,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 +406,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_hifi4.c b/src/audio/drc/drc_hifi4.c index b2252058e885..a4c32c0deb15 100644 --- a/src/audio/drc/drc_hifi4.c +++ b/src/audio/drc/drc_hifi4.c @@ -116,7 +116,7 @@ void drc_update_detector_average(struct drc_state *state, int nch) { ae_f32 detector_average = state->detector_average; /* Q2.30 */ - ae_int32 abs_input_array[DRC_DIVISION_FRAMES]; /* Q1.31 */ + ae_int32 abs_input_array[DRC_DIVISION_FRAMES] __attribute__((aligned(8))); /* Q1.31 */ ae_int32 *abs_input_array_p; int div_start, i, ch; ae_int16 *sample16_p; /* for s16 format case */ 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..6c4c3d630562 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++) @@ -74,12 +72,13 @@ static void eq_fir_free_delaylines(struct comp_data *cd) } static int eq_fir_init_coef(struct comp_dev *dev, struct sof_eq_fir_config *config, - struct fir_state_32x16 *fir, int nch) + size_t config_size, struct fir_state_32x16 *fir, int nch) { struct sof_fir_coef_data *lookup[SOF_EQ_FIR_MAX_RESPONSES]; struct sof_fir_coef_data *eq; int16_t *assign_response; int16_t *coef_data; + size_t coef_words_max; size_t size_sum = 0; int resp = 0; int i; @@ -103,27 +102,70 @@ static int eq_fir_init_coef(struct comp_dev *dev, struct sof_eq_fir_config *conf config->number_of_responses, config->channels_in_config, nch); /* Sanity checks */ + if (config->size != config_size) { + comp_err(dev, "Incorrect configuration blob size"); + return -EINVAL; + } + 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; + } + /* channels_in_config indexes into a int16_t array. An odd count would + * leave the coefficient area at a 2-byte alignment, breaking the + * 4-byte aligned int32_t loads in the optimized FIR kernels. + */ + if (config->channels_in_config & 0x1) { + comp_err(dev, "channels_in_config %u must be even", + config->channels_in_config); 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; } + /* Compute the size of the coefficient area in int16_t words from the + * blob's self-declared size. The blob layout is: + * sizeof(*config) header bytes + * channels_in_config int16_t assign_response[] + * coefficient data[] + */ + if (config->size < sizeof(*config) || + config->size - sizeof(*config) < + (size_t)config->channels_in_config * sizeof(int16_t)) { + comp_err(dev, "config size %u too small", config->size); + return -EINVAL; + } + coef_words_max = (config->size - sizeof(*config)) / sizeof(int16_t) - + config->channels_in_config; + /* Collect index of response start positions in all_coefficients[] */ j = 0; assign_response = ASSUME_ALIGNED(&config->data[0], 4); - coef_data = ASSUME_ALIGNED(&config->data[config->channels_in_config], - 4); + coef_data = ASSUME_ALIGNED(&config->data[config->channels_in_config], 4); for (i = 0; i < SOF_EQ_FIR_MAX_RESPONSES; i++) { if (i < config->number_of_responses) { + /* Header must fit before reading length */ + if (j + SOF_FIR_COEF_NHEADER > coef_words_max) { + comp_err(dev, "response %d header out of bounds", i); + return -EINVAL; + } eq = (struct sof_fir_coef_data *)&coef_data[j]; + /* Bound length so it is valid and the coefficient data + * stays within the blob. + */ + if (eq->length <= 0 || eq->length > SOF_FIR_MAX_LENGTH || + (eq->length & 0x3) || + j + SOF_FIR_COEF_NHEADER + eq->length > coef_words_max) { + comp_err(dev, "response %d length %d out of bounds", + i, eq->length); + return -EINVAL; + } lookup[i] = eq; - j += SOF_FIR_COEF_NHEADER + coef_data[j]; + j += SOF_FIR_COEF_NHEADER + eq->length; } else { lookup[i] = NULL; } @@ -145,14 +187,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 +205,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 +219,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,18 +240,20 @@ 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; /* Set coefficients for each channel EQ from coefficient blob */ - delay_size = eq_fir_init_coef(dev, cd->config, cd->fir, nch); + delay_size = eq_fir_init_coef(dev, cd->config, cd->config_size, cd->fir, nch); if (delay_size < 0) return delay_size; /* Contains error code */ @@ -220,9 +264,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_alloc(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; } @@ -234,9 +278,25 @@ static int eq_fir_setup(struct comp_dev *dev, struct comp_data *cd, int nch) return 0; } +static int eq_fir_check_blob_size(struct comp_dev *dev, size_t size) +{ + if (size < sizeof(struct sof_eq_fir_config) || size > SOF_EQ_FIR_MAX_SIZE) { + comp_err(dev, "invalid configuration blob, size %zu", size); + return -EINVAL; + } + + return 0; +} + static int eq_fir_validator(struct comp_dev *dev, void *new_data, uint32_t new_data_size) { - return eq_fir_init_coef(dev, new_data, NULL, -1); + int ret; + + ret = eq_fir_check_blob_size(dev, new_data_size); + if (ret < 0) + return ret; + + return eq_fir_init_coef(dev, new_data, new_data_size, NULL, -1); } /* @@ -247,24 +307,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 +322,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 +358,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 +370,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 +388,25 @@ 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)); + cd->config = comp_get_data_blob(cd->model_handler, &cd->config_size, NULL); + if (!cd->config || eq_fir_check_blob_size(mod->dev, cd->config_size) < 0) + return -EINVAL; + 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 +428,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, @@ -414,7 +447,7 @@ static int eq_fir_prepare(struct processing_module *mod, enum sof_ipc_frame frame_fmt; int ret = 0; - 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 +463,17 @@ 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); + cd->config = comp_get_data_blob(cd->model_handler, &cd->config_size, NULL); if (cd->config) { - ret = eq_fir_setup(dev, cd, channels); + if (eq_fir_check_blob_size(dev, cd->config_size) < 0) + return -EINVAL; + + ret = eq_fir_setup(mod, channels); if (ret < 0) comp_err(dev, "eq_fir_setup failed."); else if (cd->fir_delay_size) @@ -460,11 +496,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 +533,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.h b/src/audio/eq_fir/eq_fir.h index 921d39939d69..5af74dc5674a 100644 --- a/src/audio/eq_fir/eq_fir.h +++ b/src/audio/eq_fir/eq_fir.h @@ -36,6 +36,7 @@ struct comp_data { struct comp_data_blob_handler *model_handler; struct sof_eq_fir_config *config; int32_t *fir_delay; /**< pointer to allocated RAM */ + size_t config_size; /**< configuration size */ size_t fir_delay_size; /**< allocated size */ void (*eq_fir_func)(struct fir_state_32x16 fir[], struct input_stream_buffer *bsource, 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..a9a4bc4aa24a 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,44 @@ 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; + comp_err(dev, "mod_data_blob_handler_new() failed."); + mod_free(mod, cd); + return -ENOMEM; } - /* 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. + /* Reject malformed blobs at IPC time so a bad run-time update cannot + * replace the working configuration. */ - 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_data_blob_set_validator(cd->model_handler, eq_iir_validate_config); 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 +94,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 +107,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); } @@ -141,10 +122,16 @@ static int eq_iir_process(struct processing_module *mod, uint32_t frame_count = input_buffers[0].size; int ret; - /* Check for changed configuration */ + /* Check for changed configuration. The IPC-time validator installed + * in eq_iir_init() has already structurally validated the blob, so + * only NULL needs to be guarded here. + */ 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), + cd->config = comp_get_data_blob(cd->model_handler, &cd->config_size, NULL); + if (!cd->config) + return -EINVAL; + + 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 +153,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); @@ -185,7 +172,7 @@ static int eq_iir_prepare(struct processing_module *mod, 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 +193,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, &cd->config_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); + 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 +221,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 +254,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..7b9bf77976f1 100644 --- a/src/audio/eq_iir/eq_iir.h +++ b/src/audio/eq_iir/eq_iir.h @@ -39,6 +39,7 @@ struct comp_data { struct comp_data_blob_handler *model_handler; struct sof_eq_iir_config *config; int32_t *iir_delay; /**< pointer to allocated RAM */ + size_t config_size; /**< configuration size */ size_t iir_delay_size; /**< allocated size */ eq_iir_func eq_iir_func; /**< processing function */ }; @@ -56,9 +57,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 +71,7 @@ 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); +int eq_iir_validate_config(struct comp_dev *dev, void *new_data, uint32_t new_data_size); + +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..b9f723b0c778 100644 --- a/src/audio/eq_iir/eq_iir_generic.c +++ b/src/audio/eq_iir/eq_iir_generic.c @@ -180,6 +180,171 @@ void eq_iir_s32_default(struct processing_module *mod, struct input_stream_buffe } #endif /* CONFIG_FORMAT_S32LE */ +static int eq_iir_blob_words_max(struct comp_dev *dev, + const struct sof_eq_iir_config *config, + size_t blob_size, + uint32_t *coef_words_max) +{ + size_t payload_bytes; + + /* Compute the size of the coefficient area in int32_t words from the + * framework-reported blob size. The blob layout is: + * sizeof(*config) header bytes + * channels_in_config int32_t assign_response[] + * coefficient data[] + * channels_in_config is bounded above, so the multiply fits in size_t. + * The blob's self-declared config->size is cross-checked against the + * authoritative blob_size so all later parsing stays within the buffer. + */ + if (blob_size < sizeof(*config) || config->size != blob_size) { + comp_err(dev, "blob size %zu / header size %u mismatch or too small", + blob_size, config->size); + return -EINVAL; + } + payload_bytes = blob_size - sizeof(*config); + if (payload_bytes % sizeof(int32_t) || + payload_bytes < (size_t)config->channels_in_config * sizeof(int32_t)) { + comp_err(dev, "blob size %zu misaligned or too small", blob_size); + return -EINVAL; + } + *coef_words_max = payload_bytes / sizeof(int32_t) - config->channels_in_config; + return 0; +} + +/** + * @brief Parse one response header from the coefficient blob. + * + * Reads the response at word offset *j in @p coef_data, checks that both + * the fixed header and the following biquad sections fit inside the + * @p coef_words_max blob budget, and advances *j past this response so + * the next call continues where this one left off. On success @p *eq_out + * is set to point at the in-place header; callers that only want to walk + * the blob for validation can discard that pointer. + * + * @param dev Component device, used for error logging. + * @param idx Response index, used in error messages. + * @param coef_data Base of the coefficient word array in the blob. + * @param coef_words_max Total words available in @p coef_data. + * @param j In/out cursor in words; advanced past this response. + * @param eq_out Out; pointer to the parsed response header. + * + * @return 0 on success, -EINVAL if the header or sections would run off + * the end of the blob or num_sections exceeds the platform limit. + */ +static int eq_iir_init_response(struct comp_dev *dev, int idx, + int32_t *coef_data, uint32_t coef_words_max, + uint32_t *j, struct sof_eq_iir_header **eq_out) +{ + struct sof_eq_iir_header *eq; + uint32_t header_end = *j + SOF_EQ_IIR_NHEADER; + uint32_t section_end; + + /* Header must fit before reading num_sections */ + if (header_end > coef_words_max) { + comp_err(dev, "response %d header out of bounds", idx); + return -EINVAL; + } + eq = (struct sof_eq_iir_header *)&coef_data[*j]; + /* Bound num_sections so the multiply cannot overflow and the section + * data stays within the blob. + */ + section_end = header_end + (uint32_t)SOF_EQ_IIR_NBIQUAD * eq->num_sections; + if (eq->num_sections > SOF_EQ_IIR_BIQUADS_MAX || section_end > coef_words_max) { + comp_err(dev, "response %d num_sections %u out of bounds", + idx, eq->num_sections); + return -EINVAL; + } + *eq_out = eq; + *j = section_end; + return 0; +} + +/* Validate the config blob layout and, if lookup is non-NULL, populate it + * with pointers to each response header. Pass lookup = NULL to validate only. + */ +static int eq_iir_walk_config(struct comp_dev *dev, + struct sof_eq_iir_config *config, + size_t config_size, + struct sof_eq_iir_header **lookup) +{ + struct sof_eq_iir_header *eq; + uint32_t coef_words_max; + int32_t *coef_data; + int ret; + int i; + uint32_t j; + + if (config->channels_in_config > PLATFORM_MAX_CHANNELS || + !config->channels_in_config) { + comp_err(dev, "invalid channels_in_config %u", config->channels_in_config); + return -EINVAL; + } + if (config->number_of_responses > SOF_EQ_IIR_MAX_RESPONSES) { + comp_err(dev, "# of resp %u exceeds max", config->number_of_responses); + return -EINVAL; + } + + ret = eq_iir_blob_words_max(dev, config, config_size, &coef_words_max); + if (ret < 0) + return ret; + + j = 0; + coef_data = ASSUME_ALIGNED(&config->data[config->channels_in_config], 4); + if (lookup) + memset(lookup, 0, SOF_EQ_IIR_MAX_RESPONSES * sizeof(*lookup)); + + for (i = 0; i < config->number_of_responses; i++) { + /* Bounds-check response i, advance the walk cursor j past it, + * and get a pointer to its in-place header. Stored in lookup[] + * for later use, or discarded when we are walking the blob + * only to validate it. + */ + ret = eq_iir_init_response(dev, i, coef_data, coef_words_max, &j, &eq); + if (ret < 0) + return ret; + if (lookup) + lookup[i] = eq; + } + + return 0; +} + +int eq_iir_validate_config(struct comp_dev *dev, void *new_data, uint32_t new_data_size) +{ + struct sof_eq_iir_config *config = new_data; + int32_t *assign_response; + int32_t resp; + int ret; + int i; + + if (new_data_size < sizeof(struct sof_eq_iir_config) || + new_data_size > SOF_EQ_IIR_MAX_SIZE) { + comp_err(dev, "invalid configuration blob, size %u", new_data_size); + return -EINVAL; + } + + ret = eq_iir_walk_config(dev, config, new_data_size, NULL); + if (ret < 0) + return ret; + + /* Validate every assign_response[] entry that the per-channel loop in + * eq_iir_init_coef() could pick up. Entries beyond channels_in_config + * reuse the last assigned value, so checking [0, channels_in_config) + * covers all reachable nch. + */ + assign_response = ASSUME_ALIGNED(&config->data[0], 4); + for (i = 0; i < config->channels_in_config; i++) { + resp = assign_response[i]; + if (resp >= 0 && resp >= config->number_of_responses) { + comp_err(dev, "assign_response[%d] = %d exceeds %u", + i, resp, config->number_of_responses); + return -EINVAL; + } + } + + return 0; +} + static int eq_iir_init_coef(struct processing_module *mod, int nch) { struct comp_data *cd = module_get_private_data(mod); @@ -188,45 +353,26 @@ static int eq_iir_init_coef(struct processing_module *mod, int nch) struct sof_eq_iir_header *lookup[SOF_EQ_IIR_MAX_RESPONSES]; struct sof_eq_iir_header *eq; int32_t *assign_response; - int32_t *coef_data; int size_sum = 0; int resp = 0; int i; - int j; int s; + int ret; comp_info(mod->dev, "%u responses, %u channels, stream %d channels", config->number_of_responses, config->channels_in_config, nch); - /* Sanity checks */ - 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"); - return -EINVAL; - } - if (config->number_of_responses > SOF_EQ_IIR_MAX_RESPONSES) { - comp_err(mod->dev, "eq_iir_init_coef(), # of resp exceeds max"); + if (nch > PLATFORM_MAX_CHANNELS) { + comp_err(mod->dev, "invalid stream channels %d", nch); return -EINVAL; } - /* Collect index of response start positions in all_coefficients[] */ - j = 0; - assign_response = ASSUME_ALIGNED(&config->data[0], 4); - coef_data = ASSUME_ALIGNED(&config->data[config->channels_in_config], - 4); - for (i = 0; i < SOF_EQ_IIR_MAX_RESPONSES; i++) { - if (i < config->number_of_responses) { - eq = (struct sof_eq_iir_header *)&coef_data[j]; - lookup[i] = eq; - j += SOF_EQ_IIR_NHEADER - + SOF_EQ_IIR_NBIQUAD * eq->num_sections; - } else { - lookup[i] = NULL; - } - } + ret = eq_iir_walk_config(mod->dev, config, cd->config_size, lookup); + if (ret < 0) + return ret; /* Initialize 1st phase */ + assign_response = ASSUME_ALIGNED(&config->data[0], 4); for (i = 0; i < nch; i++) { /* Check for not reading past blob response to channel assign * map. The previous channel response is assigned for any @@ -241,13 +387,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 +404,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 +431,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,9 +462,12 @@ 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 */ + /* Set coefficients for each channel EQ from coefficient blob. + * eq_iir_init_coef() / eq_iir_blob_words_max() perform all blob size + * sanity checks, including config->size vs cd->config_size. + */ delay_size = eq_iir_init_coef(mod, nch); if (delay_size < 0) return delay_size; /* Contains error code */ @@ -329,10 +479,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/eq_iir/tune/sof_mls_freq_resp.m b/src/audio/eq_iir/tune/sof_mls_freq_resp.m index a30864d93a3b..ccb67c756102 100644 --- a/src/audio/eq_iir/tune/sof_mls_freq_resp.m +++ b/src/audio/eq_iir/tune/sof_mls_freq_resp.m @@ -1,13 +1,18 @@ -function [f, m_db] = sof_mls_freq_resp(id) +function [f, m_db] = sof_mls_freq_resp(id, play_config_fn, rec_config_fn, data_dir) %% Measure frequency response with MLS test signal % -% [f, m] = mls_freq_resp(id) +% [f, m] = sof_mls_freq_resp(id, play_config_fn, rec_config_fn, data_dir) % % Input parameters -% id - A string identifier for test case. An id 'selftest' is for special -% usage. It calculates response of filtered MLS signal and computes -% the measurement vs. known. Deviation is reported as error. It can -% be useful if the internal MLS measurement parameters are adjusted. +% id - A string identifier for test case. An id 'selftest' is for +% special usage. It calculates response of filtered MLS signal +% and computes the measurement vs. known. Deviation is +% reported as error. It can be useful if the internal MLS +% measurement parameters are adjusted. +% play_config_fn - Playback config file (default 'mls_play_config.txt'). +% rec_config_fn - Capture config file (default 'mls_rec_config.txt'). +% data_dir - Directory for the mls-<id>.{wav,txt} outputs and the +% mls-ref.wav reference (default '.'). Created if missing. % % Output parameters % f - Frequency vector in Hz @@ -17,7 +22,8 @@ % mls_play_config.txt % mls_rec_config.txt % -% The script will return also a text CSV format file with name mls-<id>.txt. +% The script will also write a text CSV format file with name mls-<id>.txt +% and a PNG plot of the aligned response as mls-<id>.png into data_dir. % % SPDX-License-Identifier: BSD-3-Clause @@ -26,24 +32,42 @@ % % Author: Seppo Ingalsuo <seppo.ingalsuo@linux.intel.com> +%% Octave dependencies: mlsp12/sync_chirp and the resampling path pull in +%% functions from the signal package. On most distros it ships separately +%% from core Octave, so load it explicitly rather than fail cryptically. +if exist('OCTAVE_VERSION', 'builtin') + pkg load signal; +end + %% Settings np = 1024; % Number of frequency points to use f_lo = 100; % Lower frequency limit for analysis f_hi = 20e3; % Upper frequency limit for analysis -t_tot = 10e-3; % MLS analysis window length in s +f_align_hz = 1000; % Response is normalized to 0 dB at this frequency +t_tot = 10e-3; % MLS analysis window length in s (480 samples @ 48 kHz) t_mls_s = 1.0; % MLS test signal length in s a_mls_db = -10; % MLS test signal amplitude in dB fs = 48e3; % Sample rate in Hz bits = 16; % Audio format to use (bits) fmt = 'S16_LE'; % Audio format to use (ALSA) -dir = '/tmp'; % Directory for temporary files +tmp_dir = '/tmp'; % Directory for temporary files capture_level_max_db = -1; % Expected max. level -capture_level_min_db = -30; % Expacted min. level +capture_level_min_db = -30; % Expected min. level %% Get device identifier to use if nargin < 1 id = 'unknown'; end +if nargin < 2 || isempty(play_config_fn) + play_config_fn = 'mls_play_config.txt'; +end +if nargin < 3 || isempty(rec_config_fn) + rec_config_fn = 'mls_rec_config.txt'; +end +if nargin < 4 || isempty(data_dir) + data_dir = '.'; +end +ensure_dir(data_dir); if strcmp(id, 'selftest') selftest = 1; @@ -57,16 +81,23 @@ else selftest = 0; end -measfn = sprintf('mls-%s.wav', id); -csvfn = sprintf('mls-%s.txt', id); +measfn = fullfile(data_dir, sprintf('mls-%s.wav', id)); +csvfn = fullfile(data_dir, sprintf('mls-%s.txt', id)); %% Paths -addpath('../../../../tools/test/audio/test_utils'); +% Resolve the test_utils dir from the script location so the script works +% independent of the caller's current working directory. onCleanup makes +% sure rmpath still runs if an error is raised later on. +script_dir = fileparts(mfilename('fullpath')); +test_utils_dir = fullfile(script_dir, '..', '..', '..', '..', ... + 'tools', 'test', 'audio', 'test_utils'); +addpath(test_utils_dir); +path_cleanup = onCleanup(@() rmpath(test_utils_dir)); %#ok<NASGU> %% MLS n_mls = round(fs*t_mls_s); mls = 10^(a_mls_db/20) * (2 * mlsp12(1, n_mls) - 1); -mlsfn = 'mls-ref.wav'; +mlsfn = fullfile(data_dir, 'mls-ref.wav'); audiowrite(mlsfn, mls, fs); %% Chip markers and parameters for find sync @@ -93,18 +124,20 @@ z(i2 + 1:end) = x2; %% Get config -rec_cfg = meas_remote_rec_config(fs, fmt); -play_cfg = meas_remote_play_config; +rec_cfg = meas_remote_rec_config(fs, fmt, rec_config_fn); +play_cfg = meas_remote_play_config(play_config_fn); %% Capture MLS from all playback channel at time mixfn = 'mlsmix.wav'; recfn = 'recch.wav'; -y = []; +nrec = play_cfg.nch * rec_cfg.nch; +y = []; % Preallocated once nt is known from the first find_test_signal +nt_expected = []; if selftest - labels = cell(play_cfg.nch * rec_cfg.nch + 1, 1); - labels(end) = 'Reference'; + labels = cell(nrec + 1, 1); + labels{end} = 'Reference'; else - labels = cell(play_cfg.nch * rec_cfg.nch, 1); + labels = cell(nrec, 1); end label_idx = 1; for i=1:play_cfg.nch @@ -119,7 +152,16 @@ else x = zeros(length(z), play_cfg.nch); x(:,i) = z; - mixdfn = sprintf('%s/%s', dir, mixfn); + % For an ssh play target we stage the WAV under tmp_dir and let + % copy_playback scp it over to play_cfg.dir. For a local play + % target copy_playback is a no-op, so write the WAV directly + % into play_cfg.dir where remote_play (which is local aplay in + % that case) will look for it. + if play_cfg.ssh + mixdfn = fullfile(tmp_dir, mixfn); + else + mixdfn = fullfile(play_cfg.dir, mixfn); + end audiowrite(mixdfn, x, fs, 'BitsPerSample', bits); copy_playback(mixdfn, play_cfg); tcap = floor(6 + t_mls_s); % Capture for MLS +6s @@ -130,7 +172,7 @@ r = get_recording(recfn, rec_cfg); end for j = 1:rec_cfg.nch - labels(label_idx) = sprintf('p%d-r%d', i, j); + labels{label_idx} = sprintf('p%d-r%d', i, j); label_idx = label_idx + 1; end [d, nt] = find_test_signal(r(:,1), fnd); @@ -148,11 +190,21 @@ m_db = []; return else - si = d:d + nt; + % nt is the sample count of the test signal, so the range + % highlighted here must be r(d:d+nt-1, :). + si = d:d + nt - 1; hold on - plot(ts(si), r(si), 'g'); + plot(ts(si), r(si, 1), 'g'); hold off end + if isempty(nt_expected) + nt_expected = nt; + y = zeros(nt, nrec); + elseif nt ~= nt_expected + error(['find_test_signal returned length %d on playback channel ' ... + '%d but %d on the first channel; captures are inconsistent'], ... + nt, i, nt_expected); + end for j = 1:rec_cfg.nch y(:, rec_cfg.nch*(i-1) + j) = r(d:d + nt -1, j); end @@ -178,8 +230,11 @@ [f, m_db] = apply_mic_calibration(f, m_db, rec_cfg); -figure -idx = find(f>1e3, 1, 'first') - 1; +main_fig = figure; +idx = find(f > f_align_hz, 1, 'first') - 1; +if isempty(idx) || idx < 1 + error('No frequency bin at or below %.0f Hz for alignment', f_align_hz); +end m_db_align = m_db - m_db(idx); semilogx(f, m_db_align); ax=axis(); axis([f_lo f_hi ax(3:4)]); @@ -195,10 +250,28 @@ hold on; plot(f, ref_db_align, 'r--'); hold off; +else + % Interpreter 'none' so underscores in the id are drawn literally + % instead of being taken as TeX subscript markers. + title(sprintf('Measured frequency response: %s', id), ... + 'Interpreter', 'none'); end legend(labels); +%% Save the plot as PNG next to the CSV so the response is easy to share +%% without pulling the raw capture through Octave again. print() picks +%% whichever graphics toolkit is active (qt on desktop, gnuplot in +%% --no-window-system runs), so both interactive and headless invocations +%% produce a file. +plot_fn = fullfile(data_dir, sprintf('mls-%s.png', id)); +try + print(main_fig, plot_fn, '-dpng', '-r150'); + fprintf('Wrote %s\n', plot_fn); +catch err + warning('Could not save %s: %s', plot_fn, err.message); +end + if selftest idx = find(f < f_hi); idx = find(f(idx) > f_lo); @@ -221,15 +294,16 @@ fprintf('Response RMS error is %4.1f dB.\n', e_db); end -rmpath('../../../../tools/test/audio/test_utils'); +% path_cleanup / onCleanup takes care of rmpath on both normal return and +% error paths. end function copy_playback(fn, cfg) if cfg.ssh - cmd = sprintf('scp %s %s:%s/', fn, cfg.user, cfg.dir); + cmd = sprintf('scp %s %s:%s/', shq(fn), cfg.user, shq(cfg.dir)); fprintf('Remote copy: %s\n', cmd); - system(cmd); + run_shell(cmd); else %cmd = sprintf('cp %s %s/', fn, cfg.dir); %fprintf('Local copy: %s\n', cmd); @@ -238,44 +312,83 @@ function copy_playback(fn, cfg) function y = get_recording(fn, cfg) if cfg.ssh - cmd = sprintf('scp %s:%s/%s %s', cfg.user, cfg.dir, fn, fn); + cmd = sprintf('scp %s:%s/%s %s', cfg.user, shq(cfg.dir), ... + shq(fn), shq(fn)); fprintf('Remote copy: %s\n', cmd); else - cmd = sprintf('cp %s/%s %s', cfg.dir, fn, fn); + cmd = sprintf('cp %s/%s %s', shq(cfg.dir), shq(fn), shq(fn)); fprintf('Local copy: %s\n', cmd); end - system(cmd); + run_shell(cmd); y = audioread(fn); delete(fn); end function remote_play(fn, cfg) if cfg.ssh - cmd = sprintf('ssh %s aplay -D%s %s/%s', cfg.user, cfg.dev, cfg.dir, fn); + cmd = sprintf('ssh %s aplay -D%s %s/%s', cfg.user, cfg.dev, ... + shq(cfg.dir), shq(fn)); fprintf('Remote play: %s\n', cmd); else - cmd = sprintf('aplay -D%s %s/%s', cfg.dev, cfg.dir, fn); + cmd = sprintf('aplay -D%s %s/%s', cfg.dev, shq(cfg.dir), shq(fn)); fprintf('Local play: %s\n', cmd); end - system(cmd); + run_shell(cmd); end function remote_capture(fn, cfg, t) if cfg.ssh cmd = sprintf('ssh %s arecord -q -D%s %s -d %d %s/%s &', ... - cfg.user, cfg.dev, cfg.fmt, t, cfg.dir, fn); + cfg.user, cfg.dev, cfg.fmt, t, ... + shq(cfg.dir), shq(fn)); fprintf('Remote capture: %s\n', cmd); else cmd = sprintf('arecord -q -D%s %s -d %d %s/%s &', ... - cfg.dev, cfg.fmt, t, cfg.dir, fn); + cfg.dev, cfg.fmt, t, shq(cfg.dir), shq(fn)); fprintf('Local capture: %s\n', cmd); end - system(cmd); + % Backgrounded capture: system() returns immediately with the shell's + % own exit code (0 for a successful fork), so a non-zero return here + % is a startup failure worth surfacing. + run_shell(cmd); end -function play = meas_remote_play_config() - play = get_config('mls_play_config.txt', 'play'); +function run_shell(cmd) +% Wrapper around system() that fails loudly instead of letting a broken +% aplay/arecord/scp step propagate as silent NaNs later on. + status = system(cmd); + if status ~= 0 + error('Shell command failed (status %d): %s', status, cmd); + end +end + +function q = shq(s) +% Wrap a string in POSIX single quotes for use in a shell command line, +% escaping any embedded single quotes. Cheap defense against paths that +% contain spaces or shell metacharacters. Any ' in s becomes the standard +% POSIX-shell '\'' close/escape/reopen sequence. + sq = char(39); % single quote + esc = [sq, '\', sq, sq]; % '\'' + if isempty(s) + q = [sq, sq]; + return; + end + q = [sq, strrep(s, sq, esc), sq]; +end + +function ensure_dir(d) + if ~exist(d, 'dir') + [ok, msg] = mkdir(d); + if ~ok + error('mkdir %s failed: %s', d, msg); + end + end +end + +function play = meas_remote_play_config(fn) + play = get_config(fn, 'play'); fprintf('\nThe settings for remote playback are\n'); + fprintf('Config : %s\n', fn); fprintf('Use ssh : %d\n', play.ssh); fprintf('User : %s\n', play.user); fprintf('Directory : %s\n', play.dir); @@ -283,12 +396,21 @@ function remote_capture(fn, cfg, t) fprintf('Channels : %d\n', play.nch); end -function rec = meas_remote_rec_config(fs, fmt) - rec = get_config('mls_rec_config.txt', 'rec'); +function rec = meas_remote_rec_config(fs, fmt, fn) + rec = get_config(fn, 'rec'); + % Let the config file override the ALSA format: some capture + % devices (e.g. SOF DMIC) only expose S32_LE, so the shell driver + % probes --dump-hw-params and writes rec.fmt_name / rec.bits in. + if isfield(rec, 'fmt_name') && ~isempty(rec.fmt_name) + fmt_use = rec.fmt_name; + else + fmt_use = fmt; + end rec.fmt = sprintf('-t wav -c %d -f %s -r %d', ... - rec.nch, fmt, fs); + rec.nch, fmt_use, fs); fprintf('\nThe settings for remote capture are\n'); + fprintf('Config : %s\n', fn); fprintf('Use ssh : %d\n', rec.ssh); fprintf('User : %s\n', rec.user); fprintf('Directory : %s\n', rec.dir); @@ -326,6 +448,7 @@ function remote_capture(fn, cfg, t) if fh < 0 error('Cannot open calibration data file'); end + fh_cleanup = onCleanup(@() fclose(fh)); %#ok<NASGU> n = 1; f = []; m = []; @@ -333,13 +456,13 @@ function remote_capture(fn, cfg, t) desc = ''; str = fgets(fh); idx = strfind(str, '"'); - while length(idx) > 0 + while ~isempty(idx) line = str(idx(1)+1:idx(2)-1); desc = sprintf('%s%s ', desc, line); str = fgets(fh); idx = strfind(str, '"'); end - if length(strfind(str, 'Sens')) + if ~isempty(strfind(str, 'Sens')) desc = str; str = fgets(fh); end @@ -352,7 +475,7 @@ function remote_capture(fn, cfg, t) end % Strip possible linefeed from description end - if double(desc(end)) == 10 + if ~isempty(desc) && double(desc(end)) == 10 desc = desc(1:end-1); end fprintf('Calibration Info : %s\n', desc); @@ -426,6 +549,11 @@ function remote_capture(fn, cfg, t) end function ret = get_config(fn, var) +% WARNING: this reads `fn` and executes it as Octave/MATLAB source via +% eval(). The config files (mls_play_config.txt / mls_rec_config.txt) are +% assumed to be locally owned and trusted; do not point this at anything +% you did not write yourself. Kept as-is for backward compatibility with +% existing user config files that rely on full script syntax. s = fileread(fn); eval(s); cmd = sprintf('ret = %s;', var); diff --git a/src/audio/eq_iir/tune/sof_mls_freq_resp_local.sh b/src/audio/eq_iir/tune/sof_mls_freq_resp_local.sh new file mode 100755 index 000000000000..eb03acce0a65 --- /dev/null +++ b/src/audio/eq_iir/tune/sof_mls_freq_resp_local.sh @@ -0,0 +1,883 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: BSD-3-Clause +# +# Copyright (c) 2026, Intel Corporation. +# +# sof_mls_freq_resp_local.sh +# +# Detect a suitable local ALSA speaker (aplay -l) + microphone +# (arecord -l, preferring a calibrated USB measurement mic), generate the +# mls_play_config.txt and mls_rec_config.txt files that sof_mls_freq_resp.m +# expects, and optionally run the Octave measurement into a data +# subdirectory. The id string is derived from DMI vendor + product so the +# output filenames identify the host that produced them. +# +# Typical use: +# ./sof_mls_freq_resp_local.sh # detect + write configs +# ./sof_mls_freq_resp_local.sh --run # + invoke Octave right away +# +# The Octave measurement can also be launched manually later with the +# printed command line. + +set -euo pipefail + +# -------------------------------------------------------------------------- +# Defaults & CLI parsing +# -------------------------------------------------------------------------- + +script_dir=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd) +data_dir="sof_mls_freq_resp_data" +id="" +id_auto=1 # 0 once --id STR was supplied by the user +append_mic_tag=1 # 0 with --no-mic-tag +speaker_override="" +mic_override="" +mic_channels="" # empty = auto-probe max from arecord +speaker_channels=2 +rec_fmt_override="" # non-empty with --rec-fmt to skip auto-probe +rec_cal="" # optional mic calibration file passed to rec.cal +do_run=0 +dry_run=0 +do_prep=0 +iir_pass_blob="$script_dir/../../../../tools/ctl/ipc4/eq_iir/pass.txt" +fir_pass_blob="$script_dir/../../../../tools/ctl/ipc4/eq_fir/pass.txt" + +usage() { + cat <<EOF +Usage: $(basename "$0") [options] + +Options: + --dir DIR Output directory (default: $data_dir). + --id STR Measurement id (default: derived from DMI vendor + + product + a sanitized microphone tag). When --id + is given, it is used verbatim and the mic tag is + not appended. + --no-mic-tag Do not append the microphone tag to the auto- + derived id. Useful when re-measuring with the same + mic and you want to overwrite the previous run. + --speaker hw:C,D Force ALSA speaker device (skip auto-detect). + --mic hw:C,D Force ALSA microphone device (skip auto-detect). + --mic-ch N Microphone channel count for capture (default: + max supported by the mic, probed with + arecord --dump-hw-params). + --spk-ch N Speaker channel count for playback (default: 2). + --rec-fmt FMT Force ALSA sample format for capture (e.g. S32_LE). + Default: probe with arecord --dump-hw-params and + prefer S32_LE > S16_LE. + --cal PATH Path to a two-column [freq_hz, mag_db] microphone + calibration file. Written verbatim into rec.cal + so sof_mls_freq_resp.m compensates the measured + response with it. + --prep Before measuring, program pass-through IIR/FIR blobs + and switch DRC/MBDRC/TDFB controls off on the + speaker card so the response is not shaped by any + leftover DSP state. Uses amixer + sof-ctl. + --iir-blob PATH Override the IIR pass-through blob used by --prep. + (default: tools/ctl/ipc4/eq_iir/pass.txt) + --fir-blob PATH Override the FIR pass-through blob used by --prep. + (default: tools/ctl/ipc4/eq_fir/pass.txt) + --run Invoke Octave to run sof_mls_freq_resp() right after + writing the configs. + --dry-run Detect and print what would be written; do not touch + the filesystem or DSP controls. + -h, --help Show this help. +EOF +} + +while [[ $# -gt 0 ]]; do + case "$1" in + --dir) data_dir="$2"; shift 2 ;; + --id) id="$2"; id_auto=0; shift 2 ;; + --no-mic-tag) append_mic_tag=0; shift ;; + --speaker) speaker_override="$2"; shift 2 ;; + --mic) mic_override="$2"; shift 2 ;; + --mic-ch) mic_channels="$2"; shift 2 ;; + --spk-ch) speaker_channels="$2"; shift 2 ;; + --rec-fmt) rec_fmt_override="$2"; shift 2 ;; + --cal) rec_cal="$2"; shift 2 ;; + --prep) do_prep=1; shift ;; + --iir-blob) iir_pass_blob="$2"; shift 2 ;; + --fir-blob) fir_pass_blob="$2"; shift 2 ;; + --run) do_run=1; shift ;; + --dry-run) dry_run=1; shift ;; + -h|--help) usage; exit 0 ;; + *) echo "Unknown option: $1" >&2; usage >&2; exit 2 ;; + esac +done + +# -------------------------------------------------------------------------- +# Dependencies +# -------------------------------------------------------------------------- + +need() { + command -v "$1" >/dev/null 2>&1 || { + echo "Required command '$1' not found in PATH" >&2 + exit 3 + } +} +need aplay +need arecord +need awk +# octave is only invoked when --run is given; skip the check in the +# default "generate configs only" mode so the script works on hosts +# without Octave installed. +if (( do_run )); then + need octave +fi + +extract_card() { + # "hw:0,0" -> "0". Used both by the prep step and by --dry-run. + echo "$1" | sed -n 's/^hw:\([0-9]\+\).*/\1/p' +} + +probe_capture_format() { + # Ask arecord which sample formats the device advertises via + # --dump-hw-params and pick one we know how to write in the rec + # config, preferring S32_LE over S16_LE (the DMIC PCM on many SOF + # platforms only exposes S32_LE). Falls back to S16_LE if the probe + # returns nothing usable (device busy, unknown output format) so + # the config file always has a definite value. + local dev="$1" formats want f + formats=$(arecord -D "$dev" --dump-hw-params 2>&1 \ + | sed -n 's/^FORMAT:[[:space:]]*//p' | head -1) + if [[ -n "$formats" ]]; then + for want in S32_LE S16_LE; do + for f in $formats; do + if [[ "$f" == "$want" ]]; then + echo "$want" + return 0 + fi + done + done + # Nothing on our preference list; take the first advertised. + echo "$formats" | awk '{print $1}' + return 0 + fi + echo "S16_LE" +} + +format_to_bits() { + case "$1" in + S32_LE|S32_BE|FLOAT_LE|FLOAT_BE) echo 32 ;; + S24_LE|S24_BE|S24_3LE|S24_3BE) echo 24 ;; + S16_LE|S16_BE) echo 16 ;; + *) echo 16 ;; + esac +} + +probe_capture_channels() { + # Return the max channel count arecord --dump-hw-params reports + # for the device, or empty string on failure. Handles both the + # single-value ("CHANNELS: 2") and range ("CHANNELS: [1 4]") forms. + local dev="$1" line n max=0 + line=$(arecord -D "$dev" --dump-hw-params 2>&1 \ + | sed -n 's/^CHANNELS:[[:space:]]*//p' | head -1) + [[ -z "$line" ]] && return 0 + line=${line//[/} + line=${line//]/} + for n in $line; do + if [[ "$n" =~ ^[0-9]+$ ]] && (( n > max )); then + max=$n + fi + done + (( max > 0 )) && echo "$max" +} + +# -------------------------------------------------------------------------- +# DMI-derived id +# -------------------------------------------------------------------------- + +dmi_read() { + # Read /sys/devices/virtual/dmi/id/<key>, or empty on failure. + local key="$1" val="" + if [[ -r "/sys/devices/virtual/dmi/id/$key" ]]; then + val=$(cat "/sys/devices/virtual/dmi/id/$key" 2>/dev/null | tr -d '\n') + fi + printf '%s' "$val" +} + +sanitize_id() { + # Lower case, keep [a-z0-9], collapse everything else into a single _ + # and trim leading/trailing underscores. + echo "$1" | tr '[:upper:]' '[:lower:]' \ + | sed -e 's/[^a-z0-9]\+/_/g' -e 's/^_\+//' -e 's/_\+$//' +} + +# Read the raw DMI strings once so both the sanitized id below and the +# report at the end can show the exact values that ALSA UCM and the +# UCM blob generator expect (they must match /sys/devices/virtual/dmi/id +# verbatim, including case and whitespace). +dmi_vendor_raw=$(dmi_read sys_vendor) +dmi_product_raw=$(dmi_read product_name) + +if [[ -z "$id" ]]; then + vendor=$(sanitize_id "$dmi_vendor_raw") + product=$(sanitize_id "$dmi_product_raw") + if [[ -n "$vendor" && -n "$product" ]]; then + id="${vendor}_${product}" + elif [[ -n "$product" ]]; then + id="$product" + elif [[ -n "$vendor" ]]; then + id="$vendor" + else + id="unknown" + fi +fi + +# -------------------------------------------------------------------------- +# ALSA device discovery +# -------------------------------------------------------------------------- + +# Parse `aplay -l` / `arecord -l` lines of the form: +# card N: SHORT [LONG], device D: DESCR (...) [SUBNAME] +# Emits: "N|D|SHORT|LONG|DESCR" per PCM device. +# +# Pure POSIX-ERE sed (invoked with -E) instead of awk. gawk's 3-argument +# match() form is not portable to mawk (Ubuntu default /usr/bin/awk) or +# busybox awk (Alpine), so a sed-only extractor keeps the script working +# regardless of which awk implementation is installed. +parse_alsa_list() { + sed -nE ' + /^card [0-9]+: [^ ]+ \[[^]]+\], device [0-9]+: / { + # Rewrite the line as c|d|sn|ln|desc, then peel off the trailing + # " [SUBNAME]" and then the " (...)" annotation from desc. + # Bracket-strip has to run first so a line like + # ... device 0: Jack Out (*) [] + # does not leave the parenthesised part dangling at end-of-line. + s/^card ([0-9]+): ([^ ]+) \[([^]]+)\], device ([0-9]+): (.*)$/\1|\4|\2|\3|\5/ + s/ *\[[^]]*\] *$// + s/ *\([^)]*\) *$// + p + } + ' +} + +playback_devices=$(aplay -l 2>/dev/null | parse_alsa_list) +capture_devices=$(arecord -l 2>/dev/null | parse_alsa_list) + +if [[ -z "$playback_devices" ]]; then + echo "aplay -l returned no PCM devices" >&2 + exit 4 +fi +if [[ -z "$capture_devices" ]]; then + echo "arecord -l returned no PCM devices" >&2 + exit 4 +fi + +# -------------------------------------------------------------------------- +# Speaker selection +# -------------------------------------------------------------------------- +# +# Preference order: +# 1) Any PCM whose descriptor contains "Speaker" (common on SOF+SDW, +# where the aplay -l descriptors are literal endpoint names such as +# "Jack Out", "Speaker", "HDMI1", "Deepbuffer Jack Out"). This has +# to run before the analog rules because SDW cards do not advertise +# "Analog" in their descriptors and would otherwise fall through to +# the "first non-HDMI/DP" pass and pick "Jack Out". +# 2) SOF card ("sof*" short name) with an "Analog" descriptor that is +# not "Deepbuffer". +# 3) Any card device whose descriptor contains "Analog" but not "HDMI", +# "DP" or "Deepbuffer". +# 4) First non-HDMI/DP/Deepbuffer device. +# HDMI/DP outputs are always skipped so the mic never accidentally captures +# monitor speaker output. + +pick_speaker() { + local line c d sn ln desc lc_ln lc_desc + + # Pass 1: descriptor explicitly named "Speaker" + while IFS='|' read -r c d sn ln desc; do + [[ -z "$c" ]] && continue + lc_desc=$(echo "$desc" | tr '[:upper:]' '[:lower:]') + if [[ "$lc_desc" == *speaker* ]] \ + && [[ "$lc_desc" != *hdmi* ]] \ + && [[ "$lc_desc" != *dp* ]]; then + echo "hw:$c,$d|$sn|$ln|$desc|speaker" + return + fi + done <<<"$playback_devices" + + # Pass 2: SOF analog + while IFS='|' read -r c d sn ln desc; do + [[ -z "$c" ]] && continue + lc_desc=$(echo "$desc" | tr '[:upper:]' '[:lower:]') + lc_ln=$(echo "$ln" | tr '[:upper:]' '[:lower:]') + if [[ "$lc_ln" == sof* ]] \ + && [[ "$lc_desc" == *analog* ]] \ + && [[ "$lc_desc" != *deepbuffer* ]] \ + && [[ "$lc_desc" != *hdmi* ]] \ + && [[ "$lc_desc" != *dp* ]]; then + echo "hw:$c,$d|$sn|$ln|$desc|sof-analog" + return + fi + done <<<"$playback_devices" + + # Pass 3: any analog, still skipping HDMI, DP and Deepbuffer. + while IFS='|' read -r c d sn ln desc; do + [[ -z "$c" ]] && continue + lc_desc=$(echo "$desc" | tr '[:upper:]' '[:lower:]') + if [[ "$lc_desc" == *analog* ]] \ + && [[ "$lc_desc" != *deepbuffer* ]] \ + && [[ "$lc_desc" != *hdmi* ]] \ + && [[ "$lc_desc" != *dp* ]]; then + echo "hw:$c,$d|$sn|$ln|$desc|analog" + return + fi + done <<<"$playback_devices" + + # Pass 4: first non-hdmi/dp/deepbuffer + while IFS='|' read -r c d sn ln desc; do + [[ -z "$c" ]] && continue + lc_desc=$(echo "$desc" | tr '[:upper:]' '[:lower:]') + if [[ "$lc_desc" != *hdmi* ]] \ + && [[ "$lc_desc" != *dp* ]] \ + && [[ "$lc_desc" != *deepbuffer* ]]; then + echo "hw:$c,$d|$sn|$ln|$desc|first-non-hdmi" + return + fi + done <<<"$playback_devices" + + return 1 +} + +# -------------------------------------------------------------------------- +# Microphone selection +# -------------------------------------------------------------------------- +# +# Preference order: +# 1) A known calibrated measurement mic by long-name substring +# (UMM-6, UMIK, MiniDSP, EMM-6, ECM8000). +# 2) Any USB-Audio-class card that is not obviously a webcam or a full +# interface (best-effort heuristic on the long name). +# 3) A SOF DMIC device (internal digital mic array). +# 4) The SOF analog line-in / any first analog capture. + +is_measurement_mic() { + local lc="$1" + case "$lc" in + *umm-6*|*umm6*|*umik*|*minidsp*|*emm-6*|*emm6*|*ecm8000*) + return 0 ;; + *) return 1 ;; + esac +} + +is_webcam() { + local lc="$1" + case "$lc" in + *webcam*|*c920*|*c930*|*brio*|*logitech*camera*|*camera*) + return 0 ;; + *) return 1 ;; + esac +} + +pick_mic() { + local line c d sn ln desc lc_ln lc_desc lc_sn + + # Pass 1: known measurement mics + while IFS='|' read -r c d sn ln desc; do + [[ -z "$c" ]] && continue + lc_ln=$(echo "$ln" | tr '[:upper:]' '[:lower:]') + lc_sn=$(echo "$sn" | tr '[:upper:]' '[:lower:]') + if is_measurement_mic "$lc_ln" || is_measurement_mic "$lc_sn"; then + echo "hw:$c,$d|$sn|$ln|$desc|measurement-usb" + return + fi + done <<<"$capture_devices" + + # Pass 2: generic USB audio card, not a webcam + while IFS='|' read -r c d sn ln desc; do + [[ -z "$c" ]] && continue + lc_ln=$(echo "$ln" | tr '[:upper:]' '[:lower:]') + lc_desc=$(echo "$desc" | tr '[:upper:]' '[:lower:]') + lc_sn=$(echo "$sn" | tr '[:upper:]' '[:lower:]') + if [[ "$lc_sn" == usb* || "$lc_desc" == *usb* ]] \ + && ! is_webcam "$lc_ln"; then + echo "hw:$c,$d|$sn|$ln|$desc|usb-generic" + return + fi + done <<<"$capture_devices" + + # Pass 3: SOF DMIC (built-in digital mic array) + while IFS='|' read -r c d sn ln desc; do + [[ -z "$c" ]] && continue + lc_ln=$(echo "$ln" | tr '[:upper:]' '[:lower:]') + lc_desc=$(echo "$desc" | tr '[:upper:]' '[:lower:]') + if [[ "$lc_ln" == sof* ]] && [[ "$lc_desc" == *dmic* ]]; then + echo "hw:$c,$d|$sn|$ln|$desc|sof-dmic" + return + fi + done <<<"$capture_devices" + + # Pass 4: SOF / first analog capture + while IFS='|' read -r c d sn ln desc; do + [[ -z "$c" ]] && continue + lc_desc=$(echo "$desc" | tr '[:upper:]' '[:lower:]') + if [[ "$lc_desc" == *analog* ]]; then + echo "hw:$c,$d|$sn|$ln|$desc|analog-capture" + return + fi + done <<<"$capture_devices" + + # Pass 5: give up and take the very first entry + IFS='|' read -r c d sn ln desc <<<"$(echo "$capture_devices" | head -n 1)" + echo "hw:$c,$d|$sn|$ln|$desc|first-listed" +} + +# -------------------------------------------------------------------------- +# Resolve devices +# -------------------------------------------------------------------------- + +if [[ -n "$speaker_override" ]]; then + speaker_hw="$speaker_override" + speaker_info="override" + speaker_name="$speaker_override" +else + IFS='|' read -r speaker_hw _spk_sn speaker_name _spk_desc speaker_info \ + <<<"$(pick_speaker || true)" + if [[ -z "${speaker_hw:-}" ]]; then + echo "Could not identify a suitable speaker device from aplay -l" >&2 + aplay -l >&2 + exit 5 + fi +fi + +if [[ -n "$mic_override" ]]; then + mic_hw="$mic_override" + mic_info="override" + # Try to look the override up in the capture list so the mic tag is + # still descriptive (e.g. dmic_raw) instead of just the hw address. + ov_card=$(extract_card "$mic_override") + ov_dev=$(echo "$mic_override" | sed -n 's/^hw:[0-9]\+,\([0-9]\+\).*/\1/p') + mic_name="$mic_override" + mic_desc="$mic_override" + if [[ -n "$ov_card" && -n "$ov_dev" ]]; then + while IFS='|' read -r c d _sn ln desc; do + if [[ "$c" == "$ov_card" && "$d" == "$ov_dev" ]]; then + mic_name="$ln" + mic_desc="$desc" + break + fi + done <<<"$capture_devices" + fi +else + IFS='|' read -r mic_hw _mic_sn mic_name mic_desc mic_info \ + <<<"$(pick_mic)" + if [[ -z "${mic_hw:-}" ]]; then + echo "Could not identify any capture device from arecord -l" >&2 + arecord -l >&2 + exit 5 + fi +fi + +# -------------------------------------------------------------------------- +# Microphone tag for the id +# -------------------------------------------------------------------------- +# +# For USB / measurement mics the long name ("MiniDSP UMM-6", "HD Pro +# Webcam C920") is the most identifiable label. For the SOF card the +# long name collapses to "sof-hda-dsp" for every capture PCM on the +# card, so fall back to the PCM descriptor ("DMIC Raw", "HDA Analog") +# to distinguish e.g. the DMIC array from a wired line-in. If both are +# generic, fall back to the hw:C,D address so we always get something +# unique. + +mic_tag_from() { + local ln="$1" desc="$2" hw="$3" base lc_ln + lc_ln=$(printf '%s' "$ln" | tr '[:upper:]' '[:lower:]') + if [[ -n "$ln" ]] && [[ "$lc_ln" != sof* ]] && [[ "$lc_ln" != hda* ]]; then + base="$ln" + elif [[ -n "$desc" ]]; then + base="$desc" + else + base="$hw" + fi + sanitize_id "$base" +} + +mic_tag=$(mic_tag_from "$mic_name" "$mic_desc" "$mic_hw") +if (( id_auto )) && (( append_mic_tag )) && [[ -n "$mic_tag" ]]; then + id="${id}_${mic_tag}" +fi + +# -------------------------------------------------------------------------- +# Avoid overwriting a previous measurement +# -------------------------------------------------------------------------- +# +# The Octave side writes mls-<id>.{wav,txt,png} into $data_dir. If any of +# them already exists (typical when re-measuring the same box at a new mic +# location), suffix the id with -2, -3, ... until nothing would collide. +# Only applied when the id was auto-derived; a user-supplied --id is +# honored verbatim so scripted overwrites still work. +uniquify_id() { + local base="$1" candidate="$1" n=2 + while [[ -e "$data_dir/mls-${candidate}.wav" \ + || -e "$data_dir/mls-${candidate}.txt" \ + || -e "$data_dir/mls-${candidate}.png" ]]; do + candidate="${base}-${n}" + n=$(( n + 1 )) + done + printf '%s' "$candidate" +} + +if (( id_auto )); then + new_id=$(uniquify_id "$id") + if [[ "$new_id" != "$id" ]]; then + echo "Note: prior measurement for '$id' found in $data_dir;" \ + "using id '$new_id' to avoid overwrite" >&2 + id="$new_id" + fi +fi + +# Resolve the ALSA capture format. Honor --rec-fmt if the user forced +# one; otherwise probe the mic and prefer S32_LE over S16_LE. +if [[ -n "$rec_fmt_override" ]]; then + rec_fmt="$rec_fmt_override" +else + rec_fmt=$(probe_capture_format "$mic_hw") +fi +rec_bits=$(format_to_bits "$rec_fmt") + +# Resolve the capture channel count similarly: if --mic-ch was given, +# use it verbatim; otherwise probe the max the mic advertises. Fall +# back to 1 when the probe returns nothing (device busy, etc.). +if [[ -z "$mic_channels" ]]; then + probed_ch=$(probe_capture_channels "$mic_hw") + if [[ -n "$probed_ch" ]]; then + mic_channels="$probed_ch" + else + mic_channels=1 + echo "Note: could not probe channel count for $mic_hw, using 1" >&2 + fi +fi + +# -------------------------------------------------------------------------- +# Report + write configs +# -------------------------------------------------------------------------- + +echo "DMI vendor : ${dmi_vendor_raw:-<unknown>}" +echo "DMI product : ${dmi_product_raw:-<unknown>}" +echo "Mic tag : ${mic_tag:-<none>}" +if (( id_auto )); then + echo "DMI id : $id (sanitized, used in blob file names)" +else + echo "DMI id : $id (user-supplied via --id, used verbatim)" +fi +echo "Speaker : $speaker_hw ($speaker_name, $speaker_info)" +echo "Microphone : $mic_hw ($mic_name, $mic_info)" +echo "Rec format : $rec_fmt (${rec_bits}-bit)" +echo "Mic cal file : ${rec_cal:-<none>}" +echo "Data dir : $data_dir" +echo "Speaker chs : $speaker_channels" +echo "Mic chs : $mic_channels" + +if [[ -n "$rec_cal" ]] && [[ ! -f "$rec_cal" ]]; then + echo "Warning: calibration file '$rec_cal' does not exist;" \ + "sof_mls_freq_resp.m will error out." >&2 +fi + +if is_webcam "$(echo "$mic_name" | tr '[:upper:]' '[:lower:]')"; then + echo "Warning: selected mic looks like a webcam; measurements are unlikely" \ + "to be usable. Consider passing --mic hw:C,D explicitly." >&2 +fi +# Base the calibrated-mic note on the actual device name/description so that +# a user forcing --mic hw:C,D on a real UMM-6 does not get a misleading +# "no calibrated mic" warning. mic_info can be "override" in that case, so +# it is not a reliable signal on its own. +lc_mic_name=$(echo "$mic_name" | tr '[:upper:]' '[:lower:]') +lc_mic_desc=$(echo "$mic_desc" | tr '[:upper:]' '[:lower:]') +if ! is_measurement_mic "$lc_mic_name" && ! is_measurement_mic "$lc_mic_desc"; then + echo "Note: no calibrated measurement microphone (UMM-6/UMIK/MiniDSP)" \ + "detected; results will not be calibrated." >&2 +fi + +play_conf="$data_dir/mls_play_config.txt" +rec_conf="$data_dir/mls_rec_config.txt" + +if (( dry_run )); then + echo + echo "-- Would write $play_conf --" + echo "-- Would write $rec_conf --" + if (( do_prep )); then + echo "-- Would prep card $(extract_card "$speaker_hw"):" + echo " IIR blob = $iir_pass_blob" + echo " FIR blob = $fir_pass_blob" + echo " DRC/MBDRC/TDFB switches -> off" + fi + exit 0 +fi + +mkdir -p -- "$data_dir" + +cat > "$play_conf" <<EOF +% Generated by sof_mls_freq_resp_local.sh for $id +% Speaker: $speaker_name ($speaker_info) +play.ssh = 0; +play.user = ''; +play.dir = '/tmp'; +play.dev = '$speaker_hw'; +play.nch = $speaker_channels; +EOF + +cat > "$rec_conf" <<EOF +% Generated by sof_mls_freq_resp_local.sh for $id +% Microphone: $mic_name ($mic_info) +rec.ssh = 0; +rec.user = ''; +rec.dir = '/tmp'; +rec.dev = '$mic_hw'; +rec.nch = $mic_channels; +rec.fmt_name = '$rec_fmt'; +rec.bits = $rec_bits; +% Path to a two-column [freq_hz, mag_db] calibration file, or '' for none. +rec.cal = '${rec_cal//\'/\'\'}'; +EOF + +echo +echo "Wrote:" +echo " $play_conf" +echo " $rec_conf" + +# -------------------------------------------------------------------------- +# Optional pre-measurement DSP prep +# -------------------------------------------------------------------------- +# +# For a valid frequency response we want the signal path from the +# playback PCM to the analog output to be as flat as possible: the IIR +# and FIR EQ bytes controls loaded with a pass-through blob, and any +# DRC / MBDRC / TDFB switch turned off so no dynamic processing or +# beam steering is applied while the MLS is playing. sof-ctl programs +# the IPC4 bytes blobs and amixer handles the boolean/enum switches. + +prep_controls_passthrough() { + local card="$1" iir_blob="$2" fir_blob="$3" + local ctldev="hw:$card" + + if [[ ! -f "$iir_blob" ]]; then + echo "IIR pass-through blob not found: $iir_blob" >&2 + return 1 + fi + if [[ ! -f "$fir_blob" ]]; then + echo "FIR pass-through blob not found: $fir_blob" >&2 + return 1 + fi + + need amixer + need sof-ctl + need timeout + + echo + echo "Prep: card=$ctldev" + echo " IIR pass blob = $iir_blob" + echo " FIR pass blob = $fir_blob" + + # probe_bytes_size <card> <name> + # + # Return the on-device max byte-buffer size of the given BYTES + # control by asking sof-ctl to read it (no -s) and parsing the + # "Control size is N." line sof-ctl prints on stdout. amixer cannot + # read large bytes controls (they use the TLV path), so sof-ctl is + # the only usable probe. + # + # sof-ctl CLI: `-c name='...'` looks up the control by name; this + # is essential because `-n numid=N` for the same control on some + # SOF topologies returns "Control size is 1." (wrong max), for the + # same reason amixer's numid= path is buggy on these controls. + # `-i 4` is intentionally omitted here — we are only reading and + # do not care about the ABI type for size reporting. + # + # Empty on any failure (sof-ctl missing, control not found, ioctl + # error, timeout). + probe_bytes_size() { + local card="$1" name="$2" out + out=$(timeout 5 sof-ctl -D "hw:$card" -c name="$name" 2>&1) \ + || true + printf '%s\n' "$out" \ + | sed -nE 's/^Control size is ([0-9]+)\..*/\1/p' \ + | head -1 + } + + # probe_type_desc <card> <name> + # + # Return a short human-readable diagnostic line from sof-ctl's + # combined stderr+stdout, used only for the skip message. Prefers + # the "Control size is N." line, then a "hdr: magic ..." line, + # then any error-looking line, else the first line. Empty on total + # probe failure. + probe_type_desc() { + local card="$1" name="$2" out line + out=$(timeout 5 sof-ctl -D "hw:$card" -c name="$name" 2>&1) \ + || true + line=$(printf '%s\n' "$out" | grep -E '^Control size is ' | head -1) + if [[ -z "$line" ]]; then + line=$(printf '%s\n' "$out" | grep -E '^hdr: ' | head -1) + fi + if [[ -z "$line" ]]; then + line=$(printf '%s\n' "$out" | grep -Ei 'error|failed|not ' | head -1) + fi + if [[ -z "$line" ]]; then + line=$(printf '%s\n' "$out" | head -1) + fi + printf '%s' "$line" + } + + # Blob writes below the minimum size are refused; the SOF pass- + # through IIR blob is a few hundred bytes and FIR is >= 1 KiB, so + # anything under 64 bytes is definitely not a real EQ buffer. + local min_blob_bytes=64 + + # Snapshot the matching controls into an array so the loop body + # runs in the parent shell (not a pipeline subshell). This makes + # any hang or failure inside the body visible instead of silently + # terminating the subshell mid-loop. The grep filter is wrapped in + # `{ ... || true; }` so that a card without any IIR/FIR/DRC/MBDRC/ + # TDFB controls does not trip `set -euo pipefail`. + local ctl_lines line numid name size rc reason + mapfile -t ctl_lines < <(amixer -c "$card" controls \ + | { grep -E "IIR|FIR|DRC|MBDRC|TDFB" || true; }) + + if (( ${#ctl_lines[@]} == 0 )); then + echo " (no IIR/FIR/DRC/MBDRC/TDFB controls found on card $card)" + return 0 + fi + echo " ${#ctl_lines[@]} matching control(s) to process" + + for line in "${ctl_lines[@]}"; do + numid=$(printf '%s\n' "$line" \ + | sed -n 's/^numid=\([0-9]\+\),.*/\1/p') + name=$(printf '%s\n' "$line" \ + | sed -n "s/.*name='\\(.*\\)'[[:space:]]*$/\\1/p") + [[ -z "$numid" || -z "$name" ]] && continue + + case "$name" in + # Switches: DRC / MBDRC / TDFB -> off + *[Dd][Rr][Cc]*switch \ + | *[Tt][Dd][Ff][Bb]*switch \ + | *[Mm][Bb][Dd][Rr][Cc]*switch) + printf ' switch off numid=%s %s\n' \ + "$numid" "$name" + # amixer >= 1.2.13 has a known assertion + # failure ("info->id.name[0] || info->id.numid") + # in snd_ctl_elem_info() when cset is invoked + # with numid= for some switch controls; the + # process aborts with a core dump. Use the + # iface=MIXER,name='...' form instead, which + # takes the name-lookup path and sidesteps the + # buggy branch. `timeout` guards against a + # hung ioctl (e.g. DSP not responding). + rc=0 + timeout 5 amixer -q -c "$card" cset \ + iface=MIXER,name="$name" off || rc=$? + if (( rc != 0 )); then + echo " ...amixer failed (exit=$rc)" >&2 + fi + ;; + # IIR bytes / IIR Eq -> pass-through + *[Ii][Ii][Rr]*[Bb]ytes \ + | *[Ii][Ii][Rr]" Eq") + # `|| size=""` is REQUIRED here: under `set -e`, + # an assignment `x=$(cmd)` where the command + # substitution's pipeline fails (e.g. sof-ctl + # returns non-zero, and pipefail propagates it) + # will silently kill the entire script. Neutralise + # it so probe_bytes_size is treated as best-effort. + size=$(probe_bytes_size "$card" "$name") || size="" + if [[ -z "$size" ]]; then + reason=$(probe_type_desc "$card" "$name") + printf ' iir skip numid=%s %s (%s)\n' \ + "$numid" "$name" "${reason:-sof-ctl probe failed}" + continue + fi + if (( size < min_blob_bytes )); then + printf ' iir skip numid=%s %s (bytes size=%s, not a real EQ buffer)\n' \ + "$numid" "$name" "$size" + continue + fi + printf ' iir pass numid=%s %s (%s bytes)\n' \ + "$numid" "$name" "$size" + rc=0 + # Set via -c name= for the same reason the probe + # does: -n numid= can report wrong max sizes and + # is prone to the same buggy code path amixer + # hits with numid= access. + timeout 5 sof-ctl -i 4 -D "$ctldev" -c name="$name" \ + -s "$iir_blob" >/dev/null || rc=$? + if (( rc != 0 )); then + echo " ...sof-ctl failed (exit=$rc)" >&2 + fi + ;; + # FIR bytes / FIR Eq -> pass-through + *[Ff][Ii][Rr]*[Bb]ytes \ + | *[Ff][Ii][Rr]" Eq") + size=$(probe_bytes_size "$card" "$name") || size="" + if [[ -z "$size" ]]; then + reason=$(probe_type_desc "$card" "$name") + printf ' fir skip numid=%s %s (%s)\n' \ + "$numid" "$name" "${reason:-sof-ctl probe failed}" + continue + fi + if (( size < min_blob_bytes )); then + printf ' fir skip numid=%s %s (bytes size=%s, not a real EQ buffer)\n' \ + "$numid" "$name" "$size" + continue + fi + printf ' fir pass numid=%s %s (%s bytes)\n' \ + "$numid" "$name" "$size" + rc=0 + timeout 5 sof-ctl -i 4 -D "$ctldev" -c name="$name" \ + -s "$fir_blob" >/dev/null || rc=$? + if (( rc != 0 )); then + echo " ...sof-ctl failed (exit=$rc)" >&2 + fi + ;; + # Bytes controls we don't have a canonical pass-through + # for are just reported so the user sees what was left + # alone. + *[Bb]ytes|*enum|*[Ee]num) + printf ' skipped numid=%s %s\n' \ + "$numid" "$name" + ;; + esac + done +} + +if (( do_prep )); then + spk_card=$(extract_card "$speaker_hw") + if [[ -z "$spk_card" ]]; then + echo "Cannot extract card number from speaker '$speaker_hw'" >&2 + exit 6 + fi + prep_controls_passthrough "$spk_card" \ + "$iir_pass_blob" "$fir_pass_blob" +fi + +# -------------------------------------------------------------------------- +# Optionally invoke Octave +# -------------------------------------------------------------------------- + +# Escape single quotes so they survive being embedded in an Octave +# single-quoted string literal (the doubling rule Octave uses for ''). +# In practice id is sanitized to [a-z0-9_] and the paths come from our +# own $TMPDIR / script location, but a --dir or --id with a stray ' in +# it would otherwise break the string or inject additional Octave code. +oct_squote() { + printf "%s" "${1//\'/\'\'}" +} + +q_script_dir=$(oct_squote "$script_dir") +q_id=$(oct_squote "$id") +q_play_conf=$(oct_squote "$play_conf") +q_rec_conf=$(oct_squote "$rec_conf") +q_data_dir=$(oct_squote "$data_dir") + +octave_eval="cd('$q_script_dir'); sof_mls_freq_resp('$q_id', '$q_play_conf', '$q_rec_conf', '$q_data_dir');" + +echo +echo "To run the measurement manually:" +echo " octave --no-gui --no-window-system --eval \"$octave_eval\"" + +if (( do_run )); then + echo + echo "Running measurement..." + octave --no-gui --no-window-system --eval "$octave_eval" +fi diff --git a/src/audio/eq_iir/tune/sof_ucm2_eq_example.txt b/src/audio/eq_iir/tune/sof_ucm2_eq_example.txt new file mode 100644 index 000000000000..1b47705f08bd --- /dev/null +++ b/src/audio/eq_iir/tune/sof_ucm2_eq_example.txt @@ -0,0 +1,1024 @@ + 100.000, -32.508, -34.491 + 100.519, -32.455, -34.372 + 101.041, -32.402, -34.252 + 101.566, -32.349, -34.130 + 102.093, -32.296, -34.008 + 102.623, -32.242, -33.885 + 103.156, -32.189, -33.761 + 103.692, -32.135, -33.636 + 104.230, -32.080, -33.512 + 104.772, -32.026, -33.387 + 105.316, -31.971, -33.261 + 105.863, -31.916, -33.136 + 106.412, -31.861, -33.011 + 106.965, -31.806, -32.885 + 107.520, -31.750, -32.760 + 108.079, -31.694, -32.635 + 108.640, -31.638, -32.510 + 109.204, -31.581, -32.386 + 109.771, -31.525, -32.262 + 110.341, -31.468, -32.138 + 110.914, -31.411, -32.015 + 111.490, -31.353, -31.893 + 112.069, -31.295, -31.771 + 112.651, -31.237, -31.649 + 113.236, -31.179, -31.528 + 113.824, -31.120, -31.407 + 114.415, -31.061, -31.288 + 115.009, -31.001, -31.168 + 115.606, -30.941, -31.050 + 116.206, -30.881, -30.932 + 116.810, -30.820, -30.814 + 117.416, -30.759, -30.698 + 118.026, -30.698, -30.582 + 118.639, -30.636, -30.466 + 119.255, -30.573, -30.351 + 119.874, -30.510, -30.237 + 120.497, -30.447, -30.124 + 121.122, -30.383, -30.011 + 121.751, -30.318, -29.899 + 122.383, -30.253, -29.787 + 123.019, -30.188, -29.676 + 123.658, -30.121, -29.565 + 124.300, -30.055, -29.456 + 124.945, -29.987, -29.346 + 125.594, -29.919, -29.238 + 126.246, -29.850, -29.129 + 126.902, -29.781, -29.022 + 127.561, -29.711, -28.915 + 128.223, -29.640, -28.808 + 128.889, -29.568, -28.702 + 129.558, -29.496, -28.596 + 130.231, -29.423, -28.491 + 130.907, -29.349, -28.387 + 131.587, -29.274, -28.283 + 132.270, -29.198, -28.179 + 132.957, -29.122, -28.076 + 133.647, -29.045, -27.973 + 134.341, -28.967, -27.870 + 135.039, -28.888, -27.768 + 135.740, -28.808, -27.666 + 136.445, -28.727, -27.565 + 137.154, -28.646, -27.464 + 137.866, -28.563, -27.363 + 138.582, -28.480, -27.263 + 139.301, -28.395, -27.163 + 140.025, -28.310, -27.063 + 140.752, -28.224, -26.963 + 141.483, -28.137, -26.864 + 142.217, -28.048, -26.765 + 142.956, -27.959, -26.666 + 143.698, -27.869, -26.567 + 144.444, -27.778, -26.468 + 145.194, -27.686, -26.370 + 145.948, -27.593, -26.272 + 146.706, -27.499, -26.174 + 147.468, -27.404, -26.076 + 148.234, -27.308, -25.978 + 149.003, -27.211, -25.880 + 149.777, -27.114, -25.782 + 150.555, -27.015, -25.685 + 151.337, -26.915, -25.587 + 152.122, -26.815, -25.489 + 152.912, -26.713, -25.392 + 153.706, -26.611, -25.294 + 154.504, -26.508, -25.196 + 155.307, -26.403, -25.099 + 156.113, -26.298, -25.001 + 156.924, -26.193, -24.903 + 157.739, -26.086, -24.805 + 158.558, -25.978, -24.707 + 159.381, -25.870, -24.609 + 160.209, -25.761, -24.511 + 161.041, -25.651, -24.412 + 161.877, -25.540, -24.314 + 162.717, -25.429, -24.215 + 163.562, -25.317, -24.116 + 164.412, -25.204, -24.017 + 165.265, -25.091, -23.918 + 166.123, -24.977, -23.818 + 166.986, -24.862, -23.718 + 167.853, -24.747, -23.618 + 168.725, -24.631, -23.518 + 169.601, -24.515, -23.417 + 170.482, -24.398, -23.316 + 171.367, -24.281, -23.215 + 172.257, -24.163, -23.113 + 173.151, -24.045, -23.012 + 174.050, -23.926, -22.909 + 174.954, -23.807, -22.807 + 175.863, -23.687, -22.704 + 176.776, -23.568, -22.601 + 177.694, -23.448, -22.497 + 178.616, -23.327, -22.393 + 179.544, -23.206, -22.289 + 180.476, -23.086, -22.184 + 181.413, -22.964, -22.079 + 182.355, -22.843, -21.973 + 183.302, -22.722, -21.867 + 184.254, -22.600, -21.761 + 185.211, -22.478, -21.654 + 186.173, -22.356, -21.547 + 187.139, -22.234, -21.439 + 188.111, -22.112, -21.331 + 189.088, -21.990, -21.223 + 190.070, -21.868, -21.114 + 191.057, -21.746, -21.005 + 192.049, -21.624, -20.895 + 193.046, -21.502, -20.785 + 194.048, -21.381, -20.675 + 195.056, -21.259, -20.565 + 196.069, -21.137, -20.453 + 197.087, -21.016, -20.342 + 198.110, -20.895, -20.230 + 199.139, -20.773, -20.118 + 200.173, -20.652, -20.006 + 201.213, -20.532, -19.893 + 202.257, -20.411, -19.780 + 203.308, -20.291, -19.667 + 204.363, -20.171, -19.554 + 205.424, -20.051, -19.440 + 206.491, -19.932, -19.326 + 207.563, -19.812, -19.212 + 208.641, -19.693, -19.097 + 209.725, -19.575, -18.983 + 210.814, -19.457, -18.868 + 211.908, -19.339, -18.753 + 213.009, -19.221, -18.638 + 214.115, -19.104, -18.523 + 215.227, -18.987, -18.408 + 216.344, -18.870, -18.293 + 217.468, -18.754, -18.178 + 218.597, -18.639, -18.063 + 219.732, -18.523, -17.947 + 220.873, -18.408, -17.832 + 222.020, -18.294, -17.717 + 223.173, -18.180, -17.603 + 224.331, -18.066, -17.488 + 225.496, -17.953, -17.373 + 226.667, -17.840, -17.259 + 227.844, -17.727, -17.145 + 229.027, -17.616, -17.031 + 230.217, -17.504, -16.918 + 231.412, -17.393, -16.804 + 232.614, -17.283, -16.691 + 233.822, -17.172, -16.579 + 235.036, -17.063, -16.467 + 236.256, -16.954, -16.355 + 237.483, -16.845, -16.244 + 238.716, -16.737, -16.133 + 239.956, -16.630, -16.023 + 241.202, -16.523, -15.914 + 242.454, -16.416, -15.805 + 243.713, -16.310, -15.696 + 244.979, -16.205, -15.588 + 246.251, -16.100, -15.481 + 247.529, -15.996, -15.375 + 248.815, -15.892, -15.269 + 250.107, -15.790, -15.164 + 251.405, -15.687, -15.060 + 252.711, -15.585, -14.957 + 254.023, -15.484, -14.854 + 255.342, -15.384, -14.753 + 256.668, -15.284, -14.652 + 258.001, -15.185, -14.552 + 259.341, -15.086, -14.453 + 260.687, -14.989, -14.355 + 262.041, -14.891, -14.258 + 263.402, -14.795, -14.162 + 264.769, -14.699, -14.067 + 266.144, -14.604, -13.973 + 267.526, -14.510, -13.880 + 268.915, -14.416, -13.788 + 270.312, -14.324, -13.697 + 271.715, -14.232, -13.607 + 273.126, -14.140, -13.518 + 274.545, -14.049, -13.430 + 275.970, -13.959, -13.344 + 277.403, -13.870, -13.258 + 278.844, -13.781, -13.174 + 280.292, -13.693, -13.090 + 281.747, -13.606, -13.008 + 283.210, -13.519, -12.926 + 284.681, -13.433, -12.846 + 286.159, -13.348, -12.767 + 287.645, -13.263, -12.688 + 289.138, -13.178, -12.611 + 290.640, -13.094, -12.535 + 292.149, -13.011, -12.459 + 293.666, -12.927, -12.385 + 295.191, -12.844, -12.311 + 296.724, -12.762, -12.238 + 298.264, -12.679, -12.166 + 299.813, -12.597, -12.095 + 301.370, -12.514, -12.024 + 302.935, -12.432, -11.955 + 304.508, -12.350, -11.885 + 306.089, -12.267, -11.817 + 307.679, -12.184, -11.748 + 309.276, -12.101, -11.681 + 310.882, -12.018, -11.613 + 312.497, -11.934, -11.546 + 314.119, -11.849, -11.479 + 315.750, -11.764, -11.412 + 317.390, -11.678, -11.346 + 319.038, -11.591, -11.279 + 320.695, -11.503, -11.212 + 322.360, -11.414, -11.145 + 324.034, -11.324, -11.078 + 325.716, -11.232, -11.010 + 327.408, -11.139, -10.942 + 329.108, -11.045, -10.874 + 330.817, -10.950, -10.805 + 332.535, -10.852, -10.735 + 334.261, -10.753, -10.664 + 335.997, -10.653, -10.592 + 337.742, -10.550, -10.519 + 339.495, -10.446, -10.446 + 341.258, -10.340, -10.370 + 343.030, -10.232, -10.294 + 344.812, -10.122, -10.216 + 346.602, -10.010, -10.137 + 348.402, -9.896, -10.057 + 350.211, -9.781, -9.974 + 352.029, -9.663, -9.890 + 353.857, -9.543, -9.804 + 355.695, -9.422, -9.717 + 357.542, -9.298, -9.627 + 359.398, -9.173, -9.535 + 361.265, -9.047, -9.442 + 363.141, -8.918, -9.346 + 365.026, -8.788, -9.249 + 366.922, -8.657, -9.149 + 368.827, -8.524, -9.048 + 370.742, -8.391, -8.944 + 372.667, -8.256, -8.838 + 374.602, -8.120, -8.730 + 376.548, -7.983, -8.620 + 378.503, -7.845, -8.508 + 380.468, -7.707, -8.394 + 382.444, -7.569, -8.278 + 384.430, -7.430, -8.160 + 386.426, -7.291, -8.040 + 388.433, -7.152, -7.918 + 390.450, -7.013, -7.794 + 392.477, -6.874, -7.669 + 394.515, -6.736, -7.543 + 396.564, -6.599, -7.414 + 398.623, -6.462, -7.284 + 400.693, -6.326, -7.153 + 402.773, -6.191, -7.021 + 404.865, -6.057, -6.887 + 406.967, -5.924, -6.752 + 409.080, -5.792, -6.616 + 411.204, -5.662, -6.478 + 413.340, -5.533, -6.340 + 415.486, -5.406, -6.201 + 417.644, -5.281, -6.062 + 419.812, -5.157, -5.921 + 421.992, -5.035, -5.780 + 424.183, -4.915, -5.638 + 426.386, -4.797, -5.495 + 428.600, -4.680, -5.352 + 430.826, -4.566, -5.209 + 433.063, -4.454, -5.065 + 435.311, -4.343, -4.921 + 437.572, -4.235, -4.776 + 439.844, -4.128, -4.631 + 442.128, -4.024, -4.486 + 444.424, -3.921, -4.341 + 446.732, -3.821, -4.195 + 449.051, -3.722, -4.049 + 451.383, -3.626, -3.904 + 453.727, -3.531, -3.758 + 456.083, -3.439, -3.612 + 458.451, -3.348, -3.466 + 460.832, -3.259, -3.321 + 463.225, -3.173, -3.176 + 465.630, -3.088, -3.031 + 468.048, -3.005, -2.886 + 470.478, -2.923, -2.742 + 472.921, -2.844, -2.599 + 475.377, -2.767, -2.456 + 477.845, -2.691, -2.314 + 480.327, -2.618, -2.174 + 482.821, -2.546, -2.034 + 485.328, -2.477, -1.896 + 487.848, -2.410, -1.759 + 490.381, -2.344, -1.623 + 492.928, -2.281, -1.489 + 495.487, -2.220, -1.358 + 498.060, -2.161, -1.228 + 500.646, -2.105, -1.101 + 503.246, -2.050, -0.976 + 505.859, -1.999, -0.854 + 508.486, -1.949, -0.734 + 511.126, -1.902, -0.618 + 513.780, -1.858, -0.505 + 516.448, -1.816, -0.395 + 519.130, -1.777, -0.288 + 521.826, -1.741, -0.186 + 524.535, -1.707, -0.087 + 527.259, -1.676, 0.008 + 529.997, -1.647, 0.099 + 532.749, -1.621, 0.185 + 535.515, -1.598, 0.267 + 538.296, -1.577, 0.345 + 541.091, -1.559, 0.418 + 543.901, -1.543, 0.486 + 546.725, -1.530, 0.550 + 549.564, -1.519, 0.608 + 552.418, -1.510, 0.662 + 555.286, -1.503, 0.711 + 558.170, -1.498, 0.755 + 561.068, -1.495, 0.795 + 563.982, -1.493, 0.829 + 566.910, -1.493, 0.858 + 569.854, -1.494, 0.882 + 572.813, -1.496, 0.901 + 575.787, -1.499, 0.915 + 578.777, -1.503, 0.924 + 581.783, -1.508, 0.927 + 584.804, -1.513, 0.926 + 587.840, -1.518, 0.920 + 590.893, -1.524, 0.908 + 593.961, -1.529, 0.891 + 597.045, -1.534, 0.869 + 600.145, -1.539, 0.842 + 603.262, -1.543, 0.809 + 606.394, -1.547, 0.771 + 609.543, -1.550, 0.727 + 612.708, -1.553, 0.679 + 615.890, -1.555, 0.624 + 619.088, -1.556, 0.565 + 622.303, -1.556, 0.500 + 625.534, -1.556, 0.429 + 628.782, -1.556, 0.353 + 632.047, -1.555, 0.272 + 635.329, -1.554, 0.185 + 638.628, -1.552, 0.093 + 641.944, -1.550, -0.004 + 645.278, -1.549, -0.106 + 648.628, -1.548, -0.212 + 651.996, -1.547, -0.324 + 655.382, -1.548, -0.439 + 658.785, -1.549, -0.559 + 662.206, -1.551, -0.682 + 665.645, -1.555, -0.809 + 669.101, -1.560, -0.938 + 672.575, -1.568, -1.071 + 676.068, -1.577, -1.205 + 679.578, -1.589, -1.342 + 683.107, -1.604, -1.480 + 686.654, -1.622, -1.619 + 690.220, -1.642, -1.759 + 693.804, -1.666, -1.900 + 697.407, -1.693, -2.041 + 701.028, -1.724, -2.181 + 704.668, -1.758, -2.321 + 708.327, -1.797, -2.460 + 712.005, -1.839, -2.599 + 715.703, -1.886, -2.736 + 719.419, -1.937, -2.872 + 723.155, -1.993, -3.006 + 726.910, -2.053, -3.139 + 730.684, -2.118, -3.270 + 734.478, -2.188, -3.399 + 738.292, -2.262, -3.526 + 742.126, -2.342, -3.651 + 745.980, -2.427, -3.772 + 749.853, -2.517, -3.890 + 753.747, -2.613, -4.004 + 757.661, -2.714, -4.113 + 761.595, -2.820, -4.217 + 765.550, -2.932, -4.313 + 769.525, -3.048, -4.402 + 773.521, -3.169, -4.482 + 777.537, -3.295, -4.551 + 781.575, -3.424, -4.609 + 785.633, -3.556, -4.654 + 789.713, -3.690, -4.685 + 793.813, -3.824, -4.702 + 797.935, -3.959, -4.704 + 802.079, -4.091, -4.690 + 806.244, -4.221, -4.662 + 810.430, -4.345, -4.619 + 814.639, -4.463, -4.563 + 818.869, -4.574, -4.495 + 823.121, -4.675, -4.417 + 827.395, -4.765, -4.331 + 831.691, -4.844, -4.239 + 836.010, -4.912, -4.143 + 840.351, -4.967, -4.044 + 844.715, -5.010, -3.945 + 849.101, -5.041, -3.846 + 853.510, -5.062, -3.751 + 857.942, -5.073, -3.659 + 862.397, -5.075, -3.571 + 866.875, -5.069, -3.488 + 871.376, -5.057, -3.409 + 875.901, -5.040, -3.336 + 880.449, -5.018, -3.266 + 885.021, -4.994, -3.200 + 889.617, -4.966, -3.135 + 894.236, -4.937, -3.071 + 898.880, -4.907, -3.006 + 903.547, -4.875, -2.938 + 908.239, -4.842, -2.866 + 912.955, -4.807, -2.787 + 917.696, -4.770, -2.699 + 922.461, -4.731, -2.602 + 927.251, -4.689, -2.494 + 932.066, -4.644, -2.374 + 936.906, -4.596, -2.243 + 941.771, -4.544, -2.100 + 946.661, -4.488, -1.947 + 951.577, -4.428, -1.785 + 956.518, -4.364, -1.615 + 961.485, -4.296, -1.440 + 966.477, -4.225, -1.262 + 971.496, -4.151, -1.083 + 976.541, -4.076, -0.905 + 981.611, -3.999, -0.731 + 986.709, -3.923, -0.563 + 991.832, -3.846, -0.402 + 996.982, -3.772, -0.249 + 1002.159, -3.699, -0.106 + 1007.363, -3.630, 0.026 + 1012.594, -3.564, 0.147 + 1017.852, -3.502, 0.257 + 1023.137, -3.444, 0.355 + 1028.450, -3.391, 0.442 + 1033.791, -3.343, 0.518 + 1039.159, -3.300, 0.582 + 1044.555, -3.260, 0.636 + 1049.979, -3.224, 0.680 + 1055.431, -3.192, 0.715 + 1060.911, -3.161, 0.740 + 1066.420, -3.130, 0.758 + 1071.958, -3.100, 0.768 + 1077.524, -3.067, 0.770 + 1083.119, -3.031, 0.766 + 1088.743, -2.990, 0.757 + 1094.397, -2.943, 0.742 + 1100.080, -2.888, 0.724 + 1105.792, -2.826, 0.702 + 1111.534, -2.755, 0.679 + 1117.306, -2.675, 0.656 + 1123.107, -2.587, 0.635 + 1128.939, -2.492, 0.617 + 1134.802, -2.391, 0.605 + 1140.694, -2.285, 0.601 + 1146.617, -2.176, 0.607 + 1152.571, -2.067, 0.624 + 1158.556, -1.958, 0.656 + 1164.572, -1.853, 0.702 + 1170.619, -1.753, 0.765 + 1176.698, -1.659, 0.844 + 1182.808, -1.572, 0.939 + 1188.950, -1.493, 1.048 + 1195.124, -1.424, 1.170 + 1201.330, -1.363, 1.303 + 1207.568, -1.310, 1.445 + 1213.838, -1.265, 1.591 + 1220.141, -1.226, 1.739 + 1226.477, -1.192, 1.885 + 1232.845, -1.162, 2.028 + 1239.247, -1.133, 2.163 + 1245.682, -1.103, 2.288 + 1252.151, -1.070, 2.401 + 1258.652, -1.033, 2.500 + 1265.188, -0.989, 2.584 + 1271.758, -0.938, 2.651 + 1278.362, -0.879, 2.701 + 1285.000, -0.812, 2.734 + 1291.672, -0.738, 2.750 + 1298.379, -0.657, 2.751 + 1305.121, -0.570, 2.737 + 1311.898, -0.480, 2.709 + 1318.711, -0.386, 2.669 + 1325.558, -0.291, 2.619 + 1332.441, -0.195, 2.561 + 1339.360, -0.097, 2.495 + 1346.315, 0.001, 2.423 + 1353.306, 0.102, 2.347 + 1360.333, 0.207, 2.268 + 1367.397, 0.317, 2.186 + 1374.497, 0.436, 2.104 + 1381.634, 0.566, 2.022 + 1388.809, 0.710, 1.942 + 1396.020, 0.869, 1.864 + 1403.269, 1.046, 1.791 + 1410.556, 1.241, 1.725 + 1417.881, 1.454, 1.668 + 1425.243, 1.684, 1.621 + 1432.644, 1.927, 1.587 + 1440.083, 2.182, 1.566 + 1447.561, 2.443, 1.560 + 1455.078, 2.706, 1.568 + 1462.633, 2.966, 1.588 + 1470.228, 3.219, 1.618 + 1477.862, 3.458, 1.655 + 1485.536, 3.682, 1.696 + 1493.250, 3.884, 1.737 + 1501.004, 4.064, 1.773 + 1508.798, 4.218, 1.802 + 1516.633, 4.345, 1.820 + 1524.508, 4.445, 1.825 + 1532.425, 4.519, 1.816 + 1540.382, 4.569, 1.793 + 1548.380, 4.598, 1.755 + 1556.421, 4.611, 1.706 + 1564.503, 4.611, 1.647 + 1572.626, 4.603, 1.582 + 1580.792, 4.593, 1.515 + 1589.001, 4.585, 1.449 + 1597.252, 4.580, 1.389 + 1605.546, 4.580, 1.336 + 1613.883, 4.585, 1.292 + 1622.263, 4.593, 1.256 + 1630.687, 4.600, 1.226 + 1639.155, 4.603, 1.199 + 1647.666, 4.597, 1.169 + 1656.222, 4.578, 1.131 + 1664.822, 4.542, 1.077 + 1673.467, 4.486, 1.003 + 1682.157, 4.410, 0.901 + 1690.891, 4.312, 0.770 + 1699.672, 4.194, 0.605 + 1708.497, 4.057, 0.408 + 1717.369, 3.904, 0.183 + 1726.287, 3.737, -0.062 + 1735.251, 3.559, -0.316 + 1744.261, 3.371, -0.561 + 1753.318, 3.175, -0.774 + 1762.423, 2.973, -0.933 + 1771.574, 2.765, -1.015 + 1780.773, 2.554, -1.007 + 1790.020, 2.341, -0.906 + 1799.315, 2.130, -0.722 + 1808.658, 1.925, -0.474 + 1818.050, 1.730, -0.186 + 1827.491, 1.548, 0.120 + 1836.980, 1.383, 0.423 + 1846.519, 1.233, 0.711 + 1856.107, 1.096, 0.976 + 1865.745, 0.962, 1.212 + 1875.433, 0.822, 1.420 + 1885.172, 0.662, 1.601 + 1894.961, 0.468, 1.757 + 1904.801, 0.228, 1.889 + 1914.692, -0.067, 1.999 + 1924.634, -0.418, 2.085 + 1934.628, -0.816, 2.147 + 1944.674, -1.236, 2.180 + 1954.772, -1.636, 2.182 + 1964.922, -1.951, 2.148 + 1975.125, -2.109, 2.078 + 1985.381, -2.054, 1.972 + 1995.691, -1.772, 1.834 + 2006.053, -1.300, 1.675 + 2016.470, -0.706, 1.511 + 2026.941, -0.065, 1.362 + 2037.466, 0.565, 1.252 + 2048.046, 1.144, 1.200 + 2058.681, 1.649, 1.220 + 2069.371, 2.069, 1.310 + 2080.116, 2.402, 1.461 + 2090.917, 2.651, 1.655 + 2101.775, 2.827, 1.875 + 2112.688, 2.942, 2.110 + 2123.659, 3.013, 2.356 + 2134.686, 3.057, 2.617 + 2145.771, 3.088, 2.903 + 2156.913, 3.121, 3.224 + 2168.113, 3.163, 3.585 + 2179.371, 3.221, 3.983 + 2190.688, 3.293, 4.409 + 2202.063, 3.376, 4.845 + 2213.498, 3.467, 5.275 + 2224.992, 3.560, 5.683 + 2236.545, 3.651, 6.058 + 2248.159, 3.739, 6.392 + 2259.833, 3.823, 6.681 + 2271.567, 3.904, 6.924 + 2283.363, 3.983, 7.120 + 2295.219, 4.061, 7.269 + 2307.138, 4.140, 7.373 + 2319.118, 4.222, 7.432 + 2331.160, 4.309, 7.448 + 2343.265, 4.401, 7.420 + 2355.433, 4.501, 7.351 + 2367.663, 4.609, 7.241 + 2379.958, 4.726, 7.095 + 2392.316, 4.854, 6.919 + 2404.739, 4.994, 6.727 + 2417.225, 5.150, 6.537 + 2429.777, 5.322, 6.372 + 2442.394, 5.514, 6.255 + 2455.077, 5.725, 6.205 + 2467.825, 5.950, 6.231 + 2480.639, 6.182, 6.326 + 2493.520, 6.411, 6.475 + 2506.468, 6.624, 6.656 + 2519.484, 6.811, 6.850 + 2532.566, 6.961, 7.043 + 2545.717, 7.071, 7.227 + 2558.936, 7.140, 7.399 + 2572.224, 7.171, 7.557 + 2585.580, 7.168, 7.696 + 2599.006, 7.140, 7.810 + 2612.502, 7.093, 7.891 + 2626.068, 7.036, 7.930 + 2639.704, 6.970, 7.920 + 2653.411, 6.897, 7.856 + 2667.189, 6.809, 7.741 + 2681.039, 6.695, 7.578 + 2694.960, 6.543, 7.374 + 2708.954, 6.342, 7.137 + 2723.021, 6.083, 6.875 + 2737.160, 5.768, 6.592 + 2751.374, 5.405, 6.291 + 2765.660, 5.012, 5.976 + 2780.021, 4.611, 5.644 + 2794.457, 4.228, 5.297 + 2808.968, 3.882, 4.934 + 2823.554, 3.579, 4.561 + 2838.215, 3.316, 4.189 + 2852.953, 3.080, 3.837 + 2867.767, 2.858, 3.527 + 2882.659, 2.639, 3.279 + 2897.627, 2.422, 3.105 + 2912.674, 2.209, 3.001 + 2927.798, 2.008, 2.948 + 2943.001, 1.829, 2.925 + 2958.283, 1.683, 2.904 + 2973.644, 1.577, 2.866 + 2989.085, 1.515, 2.798 + 3004.606, 1.486, 2.694 + 3020.208, 1.469, 2.558 + 3035.891, 1.435, 2.404 + 3051.655, 1.355, 2.251 + 3067.501, 1.208, 2.118 + 3083.430, 0.990, 2.019 + 3099.441, 0.726, 1.951 + 3115.535, 0.467, 1.889 + 3131.713, 0.280, 1.797 + 3147.975, 0.218, 1.632 + 3164.321, 0.288, 1.355 + 3180.752, 0.457, 0.938 + 3197.269, 0.677, 0.372 + 3213.871, 0.918, -0.336 + 3230.559, 1.170, -1.138 + 3247.335, 1.432, -1.928 + 3264.197, 1.698, -2.527 + 3281.147, 1.947, -2.742 + 3298.184, 2.146, -2.510 + 3315.311, 2.264, -1.959 + 3332.526, 2.285, -1.292 + 3349.830, 2.216, -0.668 + 3367.225, 2.081, -0.171 + 3384.710, 1.917, 0.171 + 3402.285, 1.757, 0.372 + 3419.952, 1.612, 0.464 + 3437.710, 1.465, 0.495 + 3455.561, 1.285, 0.502 + 3473.505, 1.051, 0.513 + 3491.541, 0.764, 0.543 + 3509.672, 0.452, 0.604 + 3527.896, 0.148, 0.703 + 3546.215, -0.134, 0.833 + 3564.629, -0.419, 0.961 + 3583.139, -0.749, 1.033 + 3601.745, -1.158, 0.988 + 3620.448, -1.629, 0.786 + 3639.247, -2.095, 0.420 + 3658.144, -2.470, -0.075 + 3677.140, -2.699, -0.628 + 3696.234, -2.803, -1.164 + 3715.427, -2.843, -1.626 + 3734.720, -2.847, -1.983 + 3754.113, -2.776, -2.195 + 3773.607, -2.564, -2.204 + 3793.202, -2.200, -1.968 + 3812.898, -1.779, -1.529 + 3832.697, -1.441, -1.010 + 3852.599, -1.297, -0.548 + 3872.604, -1.386, -0.256 + 3892.713, -1.676, -0.200 + 3912.927, -2.088, -0.391 + 3933.245, -2.529, -0.764 + 3953.669, -2.942, -1.180 + 3974.199, -3.338, -1.481 + 3994.835, -3.787, -1.589 + 4015.579, -4.352, -1.567 + 4036.430, -5.023, -1.565 + 4057.390, -5.679, -1.724 + 4078.459, -6.101, -2.132 + 4099.637, -6.069, -2.834 + 4120.925, -5.561, -3.859 + 4142.323, -4.789, -5.264 + 4163.833, -4.018, -7.179 + 4185.454, -3.441, -9.843 + 4207.187, -3.175, -13.476 + 4229.034, -3.291, -16.662 + 4250.993, -3.819, -15.263 + 4273.067, -4.728, -12.543 + 4295.256, -5.939, -10.774 + 4317.559, -7.337, -9.828 + 4339.979, -8.753, -9.406 + 4362.515, -9.868, -9.261 + 4385.168, -10.286, -9.180 + 4407.938, -10.048, -8.976 + 4430.827, -9.665, -8.512 + 4453.835, -9.483, -7.790 + 4476.962, -9.421, -6.959 + 4500.209, -9.119, -6.188 + 4523.577, -8.363, -5.571 + 4547.066, -7.338, -5.118 + 4570.678, -6.360, -4.781 + 4594.411, -5.614, -4.477 + 4618.268, -5.155, -4.149 + 4642.249, -4.983, -3.808 + 4666.355, -5.087, -3.525 + 4690.586, -5.407, -3.363 + 4714.942, -5.739, -3.307 + 4739.425, -5.714, -3.227 + 4764.035, -5.084, -2.947 + 4788.773, -4.053, -2.415 + 4813.639, -3.021, -1.776 + 4838.635, -2.223, -1.244 + 4863.760, -1.716, -0.947 + 4889.016, -1.472, -0.887 + 4914.403, -1.419, -0.968 + 4939.921, -1.466, -1.066 + 4965.572, -1.541, -1.123 + 4991.357, -1.614, -1.183 + 5017.275, -1.708, -1.327 + 5043.328, -1.870, -1.594 + 5069.516, -2.127, -1.955 + 5095.840, -2.441, -2.362 + 5122.301, -2.678, -2.771 + 5148.899, -2.625, -3.098 + 5175.636, -2.168, -3.156 + 5202.511, -1.448, -2.786 + 5229.525, -0.746, -2.056 + 5256.680, -0.276, -1.223 + 5283.976, -0.114, -0.509 + 5311.414, -0.222, -0.004 + 5338.994, -0.511, 0.313 + 5366.718, -0.897, 0.503 + 5394.585, -1.306, 0.622 + 5422.597, -1.659, 0.675 + 5450.755, -1.905, 0.629 + 5479.059, -2.070, 0.477 + 5507.509, -2.242, 0.247 + 5536.108, -2.518, -0.078 + 5564.855, -2.954, -0.599 + 5593.751, -3.515, -1.372 + 5622.797, -4.057, -2.256 + 5651.994, -4.428, -2.984 + 5681.343, -4.666, -3.456 + 5710.844, -4.998, -3.835 + 5740.499, -5.593, -4.217 + 5770.307, -6.404, -4.443 + 5800.270, -7.202, -4.380 + 5830.389, -7.699, -4.204 + 5860.664, -7.679, -4.240 + 5891.096, -7.167, -4.652 + 5921.686, -6.465, -5.360 + 5952.435, -6.012, -6.068 + 5983.344, -6.175, -6.427 + 6014.413, -7.093, -6.494 + 6045.644, -8.490, -6.699 + 6077.037, -9.407, -7.254 + 6108.593, -9.036, -7.923 + 6140.312, -8.078, -8.234 + 6172.197, -7.434, -8.040 + 6204.247, -7.359, -7.681 + 6236.463, -7.728, -7.472 + 6268.847, -8.259, -7.460 + 6301.399, -8.740, -7.533 + 6334.119, -9.255, -7.542 + 6367.010, -10.077, -7.421 + 6400.072, -11.221, -7.290 + 6433.305, -12.171, -7.449 + 6466.711, -12.438, -8.167 + 6500.290, -12.441, -9.431 + 6534.043, -12.716, -10.924 + 6567.972, -13.375, -12.318 + 6602.077, -14.522, -13.594 + 6636.360, -16.291, -14.726 + 6670.820, -18.101, -15.238 + 6705.459, -18.039, -15.202 + 6740.278, -16.402, -15.660 + 6775.278, -15.051, -17.365 + 6810.459, -14.271, -20.048 + 6845.823, -13.987, -21.818 + 6881.371, -14.444, -21.562 + 6917.104, -16.046, -20.989 + 6953.022, -18.976, -20.814 + 6989.126, -21.513, -20.848 + 7025.418, -19.935, -20.559 + 7061.898, -17.556, -19.332 + 7098.568, -16.478, -17.667 + 7135.428, -16.342, -16.496 + 7172.480, -16.694, -16.255 + 7209.724, -17.631, -16.975 + 7247.162, -19.119, -18.228 + 7284.793, -20.456, -19.398 + 7322.621, -20.687, -20.406 + 7360.644, -19.696, -21.304 + 7398.865, -18.155, -20.938 + 7437.285, -16.363, -18.936 + 7475.904, -15.019, -16.690 + 7514.724, -14.858, -15.023 + 7553.745, -15.810, -14.036 + 7592.969, -17.228, -13.553 + 7632.396, -17.894, -13.531 + 7672.028, -17.607, -13.920 + 7711.866, -17.483, -14.575 + 7751.911, -17.746, -15.310 + 7792.164, -17.784, -15.947 + 7832.626, -17.193, -16.403 + 7873.298, -16.241, -16.281 + 7914.181, -15.411, -15.424 + 7955.276, -15.321, -14.933 + 7996.585, -16.266, -15.499 + 8038.109, -17.631, -16.236 + 8079.847, -18.283, -16.140 + 8121.803, -18.228, -15.764 + 8163.977, -18.534, -15.990 + 8206.369, -19.440, -16.806 + 8248.982, -20.279, -17.370 + 8291.816, -21.029, -17.339 + 8334.872, -21.487, -16.961 + 8378.152, -20.743, -15.922 + 8421.657, -20.368, -15.269 + 8465.387, -22.097, -16.216 + 8509.345, -24.351, -17.655 + 8553.531, -25.517, -18.692 + 8597.946, -27.087, -20.607 + 8642.592, -27.679, -22.159 + 8687.470, -27.150, -21.397 + 8732.581, -28.437, -20.818 + 8777.926, -31.018, -21.232 + 8823.506, -33.644, -22.472 + 8869.323, -34.609, -24.474 + 8915.379, -33.982, -25.161 + 8961.673, -33.633, -25.372 + 9008.208, -33.089, -26.322 + 9054.984, -35.767, -28.407 + 9102.003, -41.735, -32.372 + 9149.266, -34.312, -33.912 + 9196.775, -32.116, -31.573 + 9244.531, -32.322, -29.695 + 9292.534, -35.271, -27.913 + 9340.787, -40.656, -27.612 + 9389.290, -43.153, -29.349 + 9438.045, -39.375, -30.869 + 9487.053, -32.993, -29.169 + 9536.316, -28.298, -30.903 + 9585.835, -25.244, -35.381 + 9635.610, -25.701, -35.362 + 9685.645, -26.142, -32.926 + 9735.939, -25.770, -30.588 + 9786.494, -27.603, -29.455 + 9837.311, -28.201, -30.668 + 9888.393, -26.653, -32.910 + 9939.740, -25.131, -34.270 + 9991.353, -24.468, -35.300 + 10043.235, -23.680, -30.777 + 10095.385, -24.079, -28.870 + 10147.807, -24.705, -27.825 + 10200.501, -21.853, -24.193 + 10253.468, -19.870, -20.828 + 10306.711, -20.054, -19.489 + 10360.230, -18.561, -19.124 + 10414.026, -16.977, -19.539 + 10468.103, -18.750, -22.053 + 10522.460, -19.384, -22.842 + 10577.099, -16.125, -20.135 + 10632.022, -14.385, -19.305 + 10687.230, -13.973, -19.471 + 10742.725, -13.647, -18.969 + 10798.508, -14.294, -19.198 + 10854.581, -14.520, -19.317 + 10910.944, -13.225, -18.710 + 10967.601, -12.438, -18.775 + 11024.552, -12.464, -19.154 + 11081.798, -12.716, -18.998 + 11139.342, -13.167, -18.335 + 11197.184, -13.560, -17.458 + 11255.327, -11.920, -15.783 + 11313.772, -10.510, -14.429 + 11372.520, -10.493, -13.845 + 11431.573, -10.929, -13.800 + 11490.933, -10.800, -13.789 + 11550.601, -11.014, -14.439 + 11610.579, -11.331, -14.387 + 11670.869, -11.260, -14.252 + 11731.471, -10.606, -13.932 + 11792.389, -10.766, -13.623 + 11853.622, -12.072, -13.561 + 11915.174, -13.699, -14.575 + 11977.045, -13.911, -14.760 + 12039.237, -13.612, -13.833 + 12101.752, -14.254, -15.248 + 12164.592, -14.255, -16.082 + 12227.759, -15.246, -15.230 + 12291.253, -17.082, -15.578 + 12355.077, -17.119, -16.278 + 12419.232, -18.754, -17.374 + 12483.721, -20.074, -18.117 + 12548.544, -19.721, -17.465 + 12613.704, -22.686, -17.832 + 12679.202, -21.220, -19.830 + 12745.041, -19.104, -21.238 + 12811.221, -19.327, -23.423 + 12877.745, -18.651, -29.805 + 12944.614, -17.624, -25.798 + 13011.831, -18.196, -21.551 + 13079.397, -18.380, -19.626 + 13147.313, -17.632, -19.430 + 13215.582, -17.013, -20.859 + 13284.206, -15.295, -20.423 + 13353.186, -14.005, -18.827 + 13422.524, -13.332, -18.428 + 13492.222, -14.533, -18.642 + 13562.282, -14.689, -17.755 + 13632.706, -13.633, -17.931 + 13703.496, -13.694, -17.529 + 13774.653, -14.440, -17.448 + 13846.180, -15.071, -18.632 + 13918.078, -15.652, -19.169 + 13990.349, -15.433, -18.595 + 14062.996, -15.518, -17.754 + 14136.020, -15.975, -16.881 + 14209.423, -15.197, -15.931 + 14283.207, -15.387, -16.473 + 14357.375, -16.436, -17.261 + 14431.927, -17.499, -17.709 + 14506.867, -17.735, -17.362 + 14582.196, -18.070, -17.592 + 14657.916, -17.755, -17.638 + 14734.029, -18.716, -17.712 + 14810.537, -18.667, -18.692 + 14887.443, -18.899, -21.009 + 14964.748, -19.456, -21.171 + 15042.454, -20.952, -21.413 + 15120.564, -21.670, -21.334 + 15199.080, -21.980, -22.066 + 15278.003, -25.323, -23.947 + 15357.336, -26.823, -24.928 + 15437.081, -25.991, -27.074 + 15517.240, -27.629, -30.389 + 15597.815, -30.773, -32.039 + 15678.809, -35.757, -31.495 + 15760.223, -29.072, -31.792 + 15842.060, -28.077, -35.229 + 15924.322, -27.585, -42.646 + 16007.012, -25.155, -34.823 + 16090.130, -25.171, -32.693 + 16173.680, -25.795, -32.305 + 16257.664, -25.643, -31.496 + 16342.084, -25.355, -32.089 + 16426.943, -25.736, -31.500 + 16512.242, -26.246, -30.598 + 16597.984, -25.192, -29.239 + 16684.171, -27.304, -30.238 + 16770.806, -28.107, -31.071 + 16857.890, -27.198, -30.781 + 16945.427, -27.967, -33.128 + 17033.418, -29.094, -33.147 + 17121.867, -30.431, -33.102 + 17210.774, -32.619, -34.209 + 17300.143, -34.268, -35.872 + 17389.977, -34.497, -36.592 + 17480.276, -31.588, -34.715 + 17571.045, -37.675, -40.715 + 17662.285, -37.569, -39.255 + 17753.999, -42.735, -42.000 + 17846.189, -49.213, -49.727 + 17938.857, -47.611, -50.239 + 18032.007, -45.206, -42.393 + 18125.641, -51.820, -50.161 + 18219.761, -55.094, -53.106 + 18314.369, -52.647, -50.265 + 18409.469, -53.595, -49.285 + 18505.062, -49.637, -61.368 + 18601.152, -52.866, -55.880 + 18697.741, -44.759, -50.507 + 18794.832, -43.593, -55.002 + 18892.426, -42.729, -53.713 + 18990.528, -42.671, -50.713 + 19089.139, -40.584, -49.937 + 19188.261, -34.787, -43.509 + 19287.899, -41.757, -48.445 + 19388.054, -32.058, -42.623 + 19488.729, -31.664, -39.266 + 19589.927, -31.893, -36.969 + 19691.650, -30.779, -35.801 + 19793.901, -31.022, -36.402 + 19896.684, -32.701, -36.436 + 20000.000, -36.437, -39.544 diff --git a/src/audio/eq_iir/tune/sof_ucm2_eq_example.xlsx b/src/audio/eq_iir/tune/sof_ucm2_eq_example.xlsx new file mode 100644 index 000000000000..6e8ece702ff7 Binary files /dev/null and b/src/audio/eq_iir/tune/sof_ucm2_eq_example.xlsx differ diff --git a/src/audio/eq_iir/tune/sof_ucm2_eq_generate.m b/src/audio/eq_iir/tune/sof_ucm2_eq_generate.m new file mode 100644 index 000000000000..2eb2fc34f291 --- /dev/null +++ b/src/audio/eq_iir/tune/sof_ucm2_eq_generate.m @@ -0,0 +1,565 @@ +function sof_ucm2_eq_generate(sys_vendor, product_name, endpoint, meas_file) + +% SOF_UCM2_EQ_GENERATE Fit IIR + FIR endpoint EQ from a measurement. +% +% sof_ucm2_eq_generate(SYS_VENDOR, PRODUCT_NAME, ENDPOINT, MEAS_FILE) +% +% SYS_VENDOR DMI /sys/devices/virtual/dmi/id/sys_vendor value, e.g. +% 'Acme Ltd.'. Kept verbatim (with spaces and case) for the +% product_configs directory name. +% PRODUCT_NAME DMI product_name value, e.g. 'Model 100'. Kept +% verbatim for the .conf file name. +% ENDPOINT Endpoint being tuned, e.g. 'speaker' or 'headphone'. Used +% lower case in blob file names and capitalized in the +% Define.PostMixer<Endpoint>Playback... UCM keys. +% MEAS_FILE Path to a comma separated numbers text file whose first +% column is the measurement frequency in Hz and whose +% remaining columns are one or more measured magnitude +% traces in dB. Multiple traces are averaged. +% Such a file can be produced with sof_mls_freq_resp.m. +% Excel workbooks (.xls, .xlsx, .xlsm) and OpenDocument +% spreadsheets (.ods) are also accepted; the format is +% detected from the file extension and imported via +% xlsread from the Octave 'io' package. The workbook +% is expected to contain the same [freq, resp...] numeric +% layout with no header row, matching +% sof_ucm2_eq_example.xlsx. +% +% A cell array of paths is also accepted, e.g. +% { 'mls-a.txt', 'mls-b.txt', 'mls-c.txt' }. Each file is +% loaded independently (per-file column averaging still +% applies) and the per-file magnitudes are then averaged +% together. All files must share the same frequency +% grid; a mismatch raises an error. +% +% The binary IIR and FIR blobs consumed by UCM are written to +% ./ucm2_blobs_sof/ipc4/eq_iir/<endpoint>_<vendor>_<product>_iir.bin +% ./ucm2_blobs_sof/ipc4/eq_fir/<endpoint>_<vendor>_<product>_fir.bin +% +% The matching sof-ctl text dumps for manual testing are written under +% the SOF source tree (independent of the caller's current directory): +% <sof>/tools/ctl/ipc4/eq_iir/<endpoint>_<vendor>_<product>_iir.txt +% <sof>/tools/ctl/ipc4/eq_fir/<endpoint>_<vendor>_<product>_fir.txt +% +% and a UCM include file that will be picked up automatically by UCM is +% written to +% ./ucm2_blobs_sof/product_configs/<SYS_VENDOR>/<PRODUCT_NAME>.conf +% +% Run this to see a design example and produced configuration for ALSA UCMv2 +% sof_ucm2_eq_generate('example', 'example', 'speaker', 'sof_ucm2_eq_example.txt'); +% or +% sof_ucm2_eq_generate('example', 'example', 'speaker', 'sof_ucm2_eq_example.xlsx'); + +% SPDX-License-Identifier: BSD-3-Clause +% +% Copyright (c) 2026, Intel Corporation. + +if nargin < 4 + help sof_ucm2_eq_generate + printf('\n'); + error('Usage: sof_ucm2_eq_generate(sys_vendor, product_name, endpoint, meas_file)'); +end + +%% Validate DMI and endpoint inputs before they are used to build any +%% path or UCM key. sys_vendor and product_name are kept verbatim as +%% directory / file name components under product_configs/, so they must +%% not contain path separators or traversal segments. endpoint is +%% capitalized and dropped into Define.PostMixer<Endpoint>Playback... +%% keys, so it has to be a plain letter identifier. +validate_dmi_field('sys_vendor', sys_vendor); +validate_dmi_field('product_name', product_name); +validate_endpoint(endpoint); + +%% Load the signal package up front so the script fails cleanly on a +%% system missing it, before any output directories are created. The io +%% package is only pulled in when the measurement file is a spreadsheet +%% (see load_measurement below), so text-only users do not need it. +pkg load signal; + +%% Derive blob base name and output paths from the DMI info + endpoint. +%% The binary blobs go into a UCM-shaped staging tree (ucm2_blobs_sof/) +%% that mirrors the alsa-ucm-conf layout. The sof-ctl text dumps go into +%% the regular tools/ctl/ipc4/eq_{iir,fir}/ locations so they can be used +%% for manual testing with sof-ctl the same way as the other tune scripts; +%% UCM itself does not consume these .txt files. The tools/ctl path is +%% derived from the location of this script so it works regardless of the +%% caller's current directory. +endpoint_lc = lower(endpoint); +base = sprintf('%s_%s_%s', endpoint_lc, ... + sanitize_name('sys_vendor', sys_vendor), ... + sanitize_name('product_name', product_name)); +out_root = 'ucm2_blobs_sof'; +cpath4 = fullfile(out_root, 'ipc4'); +iir_bin = fullfile('eq_iir', [base '_iir.bin']); +fir_bin = fullfile('eq_fir', [base '_fir.bin']); +conf_dir = fullfile(out_root, 'product_configs', sys_vendor); +conf_file = fullfile(conf_dir, [product_name '.conf']); +ensure_dir(fullfile(cpath4, 'eq_iir')); +ensure_dir(fullfile(cpath4, 'eq_fir')); +ensure_dir(conf_dir); + +script_dir = fileparts(mfilename('fullpath')); +ctl_root = fullfile(script_dir, '..', '..', '..', '..', 'tools', 'ctl', 'ipc4'); +ctl_iir_dir = fullfile(ctl_root, 'eq_iir'); +ctl_fir_dir = fullfile(ctl_root, 'eq_fir'); +ensure_dir(ctl_iir_dir); +ensure_dir(ctl_fir_dir); +iir_txt = fullfile(ctl_iir_dir, [base '_iir.txt']); +fir_txt = fullfile(ctl_fir_dir, [base '_fir.txt']); + +enable_common_paths(script_dir, true); + +%% Base equalizer setup +eq = sof_eq_defaults(); +eq.fs = 48e3; +eq.logsmooth_plot = 1.0; +eq.logsmooth_eq = 1.0; +eq.enable_iir = 1; +eq.enable_fir = 1; +eq.iir_norm_type = 'loudness'; +eq.iir_norm_offs_db = -1; +eq.fir_norm_type = 'loudness'; +eq.fir_norm_offs_db = -1; +eq.p_fmin = 20; +eq.p_fmax = 20e3; + +%% Parametric target: keep the corrected response band-limited so the fit +%% does not chase noise below 80 Hz (below the speaker's useful range) or +%% brighten the top octave. HP2 80 Hz rolls off sub-bass in the target, +%% LP2 10 kHz tames the highs. Both the IIR stage fit (stage_rms) and the +%% FIR design in sof_eq_compute honor eq.t_db that this generates. +eq.parametric_target_response = [ ... + eq.PEQ_HP2 80 0 0; ... + eq.PEQ_LP2 10000 0 0; ... +]; + +%% Load measurement, fit IIR + FIR, compute the final response and export +eq = load_measurement(eq, meas_file); +eq = design_iir_stages(eq); +eq = configure_fir(eq); +eq = sof_eq_compute(eq); +sof_eq_plot(eq, 1); +export_blobs(eq, cpath4, iir_txt, iir_bin, fir_txt, fir_bin); +write_product_conf(conf_file, sys_vendor, product_name, endpoint, base, eq); + +enable_common_paths(script_dir, false); + +printf('\n'); +printf('To install these blobs into alsa-ucm-conf, copy the contents of\n'); +printf('%s/ recursively into alsa-ucm-conf/ucm2/blobs/sof:\n', out_root); +printf(' cp -r %s/* <alsa-ucm-conf>/ucm2/blobs/sof/\n', out_root); +printf('sof-ctl text dumps written under tools/ctl/ipc4/eq_{iir,fir}/.\n'); + +end + +%% ----------------------------------------------------------------------- +%% Measurement loading +%% ----------------------------------------------------------------------- +function eq = load_measurement(eq, meas_file) +%% meas_file is either a single path or a cell array of paths. Each file +%% is loaded and its response columns (if more than one) are averaged +%% into a single trace, then the per-file traces are averaged together. +%% All files must share the exact same frequency grid; any mismatch +%% aborts before any fit is attempted. +if iscell(meas_file) + files = meas_file; +elseif ischar(meas_file) + files = { meas_file }; +else + error(['meas_file must be a char path or a cell array of ' ... + 'char paths (got %s)'], class(meas_file)); +end + +if isempty(files) + error('meas_file must not be an empty cell array'); +end +for k = 1:numel(files) + if ~ischar(files{k}) || isempty(files{k}) + error(['meas_file{%d} must be a non-empty char path ' ... + '(got %s)'], k, class(files{k})); + end +end + +f_ref = []; +m_stack = []; +for k = 1:numel(files) + fn = files{k}; + if ~exist(fn, 'file') + error('Measurement file not found: %s', fn); + end + [~, ~, ext] = fileparts(fn); + switch lower(ext) + case {'.xls', '.xlsx', '.xlsm', '.ods'} + pkg load io; + meas = xlsread(fn); + otherwise + %% dlmread with no explicit delimiter auto-detects whitespace or + %% comma separation, which covers both the sof_mls_freq_resp.m + %% output and legacy whitespace-delimited measurement files. + meas = dlmread(fn); + end + if size(meas, 2) < 2 + error('%s must have at least a frequency column and one response column', ... + fn); + end + f_k = meas(:, 1); + if size(meas, 2) == 2 + m_k = meas(:, 2); + else + m_k = mean(meas(:, 2:end), 2); + fprintf('Averaged %d response columns from %s\n', ... + size(meas, 2) - 1, fn); + end + if isempty(f_ref) + f_ref = f_k; + m_stack = m_k; + else + if ~isequal(size(f_k), size(f_ref)) || any(f_k(:) ~= f_ref(:)) + error(['Frequency grid of %s does not match %s. ' ... + 'All measurement files must share the same ' ... + '[freq, resp...] grid.'], fn, files{1}); + end + m_stack = [m_stack, m_k]; + end +end + +eq.raw_f = f_ref; +if size(m_stack, 2) == 1 + eq.raw_m_db = m_stack; +else + eq.raw_m_db = mean(m_stack, 2); + fprintf('Averaged %d measurement files\n', size(m_stack, 2)); +end +end + +%% ----------------------------------------------------------------------- +%% Multi-stage IIR fit +%% ----------------------------------------------------------------------- +function eq = design_iir_stages(eq) + +%% Cap on the combined response shaping between the mid PN2 (stage 1) and +% the bass LS2 (stage 2). If stage 1 pulls the mids down by A dB, the +% LS2 upper bound is reduced so that bass_boost + |mid_atten| does not +% exceed this limit. This keeps the bass-to-mid tilt from becoming +% excessive (e.g. 26 dB total is already an aggressive shape). +max_bass_plus_mid_atten_db = 26; + +%% Filter budget (4 biquads max): +% 1) HP2 at 80 Hz to protect the speaker +% 2) PN2 mid shaper -> fc [2000, 4000], gain [-12, +6], Q [0.5, 1.0] +% 3) LS2 bass boost -> fc [120, 1000], gain [0, +12] +% 4) PN2 fine correction (low band) -> fc [200, 4000], gain [-6, +12], Q [0.5, 1.0] +% Fitting order matters: the mid PN2 is fit first so the subsequent low +% shelf can settle on top of an already-flattened midrange instead of +% chasing a mid bump with bass gain. The remaining high-band correction +% is left to the mid-band FIR that runs after the IIR, which keeps the +% IIR order low and avoids the audible ringing seen with a larger IIR +% budget. +hp_fc = 80; + +opts = optimset('Display', 'notify', 'MaxIter', 300, 'MaxFunEvals', 2000, ... + 'TolX', 1e-3, 'TolFun', 1e-3); + +peq_fixed = [eq.PEQ_HP2, hp_fc, 0, 0]; + +%% Stage 1: fit a mid PN2 peak/notch first, on top of the HP2. Doing this +% before the low shelf keeps the shelf from over-compensating for a +% broad midrange bump. The gain upper bound is kept low (+6 dB) so this +% stage stays a mid *attenuator* rather than a boost. +% Params: [fc, gain, Q]. fc [2000, 4000] Hz, gain [-12, +6] dB, Q [0.5, 1.0]. +fmin_fit = 400; +fmax_fit = 7000; +p1_0 = [2500, -6, 0.6]; +p1_bounds = [2000, 4000; -12, +6; 0.5, 1.0]; +p1 = fminsearch(@(p) stage_rms(peq_fixed, pn_row(p, p1_bounds, eq), eq, fmin_fit, fmax_fit), ... + p1_0, opts); +peq_fixed = [peq_fixed; pn_row(p1, p1_bounds, eq)]; +fprintf('Stage 1 (mid PN2): fc=%.1f Hz g=%.2f dB Q=%.2f\n', ... + clamp(p1(1), p1_bounds(1,1), p1_bounds(1,2)), ... + clamp(p1(2), p1_bounds(2,1), p1_bounds(2,2)), ... + clamp(p1(3), p1_bounds(3,1), p1_bounds(3,2))); + +%% Stage 2: fit the low shelf on top of the flattened midrange. +% Params: [ls_fc, ls_g]. fc [120, 1000] Hz, gain [0, +12] dB. +% The upper bass gain bound is shrunk when stage 1 attenuated the mids, +% so the total bass-to-mid shaping stays within max_bass_plus_mid_atten_db. +fmin_fit = 200; +fmax_fit = 2000; +ls0 = [200, 8]; +ls_bounds = [120, 1000; 0, 12]; +mid_atten_db = min(0, clamp(p1(2), p1_bounds(2,1), p1_bounds(2,2))); +ls_bounds(2,2) = min(ls_bounds(2,2), max(0, max_bass_plus_mid_atten_db - abs(mid_atten_db))); +if ls_bounds(2,2) < ls0(2) + ls0(2) = ls_bounds(2,2); +end +fprintf('Stage 2 bass gain upper bound = %.2f dB (mid atten %.2f dB, cap %.1f dB)\n', ... + ls_bounds(2,2), mid_atten_db, max_bass_plus_mid_atten_db); +ls = fminsearch(@(p) stage_rms(peq_fixed, shelf_row(eq.PEQ_LS2, p, ls_bounds), ... + eq, fmin_fit, fmax_fit), ls0, opts); +peq_fixed = [peq_fixed; shelf_row(eq.PEQ_LS2, ls, ls_bounds)]; +fprintf('Stage 2 (LS2): fc=%.1f Hz g=%.2f dB\n', clamp(ls(1), ls_bounds(1,1), ls_bounds(1,2)), ... + clamp(ls(2), ls_bounds(2,1), ls_bounds(2,2))); + +%% Widen the fit band for the fine correction stage: below 400 Hz the LS2 +% already dominates and above the mid we still want to shape the response +% out to 7 kHz. +fmin_fit = 400; +fmax_fit = 7000; + +%% Stage 3: single fine correction across the low/mid band. The higher +% frequencies are left to the mid-band FIR, so this biquad can focus on +% whatever residual the shelf + mid PN2 left behind. +% Params: [fc, gain, Q]. fc [200, 4000] Hz, gain [-6, +12] dB, Q [0.5, 1.0]. +p2_0 = [800, -3, 1.0]; +p2_bounds = [200, 4000; -6, 12; 0.5, 1.0]; +p2 = fminsearch(@(p) stage_rms(peq_fixed, pn_row(p, p2_bounds, eq), eq, fmin_fit, fmax_fit), ... + p2_0, opts); +peq_fixed = [peq_fixed; pn_row(p2, p2_bounds, eq)]; +fprintf('Stage 3 (fine PN2): fc=%.1f Hz g=%.2f dB Q=%.2f\n', ... + clamp(p2(1), p2_bounds(1,1), p2_bounds(1,2)), ... + clamp(p2(2), p2_bounds(2,1), p2_bounds(2,2)), ... + clamp(p2(3), p2_bounds(3,1), p2_bounds(3,2))); + +eq.peq = peq_fixed; +end + +%% ----------------------------------------------------------------------- +%% FIR configuration for mid-band residual correction +%% ----------------------------------------------------------------------- +function eq = configure_fir(eq) +%% The IIR takes care of the coarse bass shelf plus two low/mid PN2 +% shapers. Whatever residual vs. the target is left after the IIR +% (fir_compensate_iir = 1 in the defaults) is picked up here by a short +% minimum-phase FIR limited to [fmin_fir, fmax_fir], so it does not +% spend taps on the LF/HF regions the IIR already handles or where the +% measurement is unreliable. +eq.fir_length = 63; +eq.fir_beta = 10; +eq.fir_minph = 1; +eq.fir_autoband = 0; +eq.fmin_fir = 400; +eq.fmax_fir = 7000; +fprintf('FIR: length=%d taps, mid band [%d, %d] Hz\n', ... + eq.fir_length, eq.fmin_fir, eq.fmax_fir); +end + +%% ----------------------------------------------------------------------- +%% IIR + FIR blob packing and export +%% ----------------------------------------------------------------------- +function export_blobs(eq, cpath, iir_txt, iir_bin, fir_txt, fir_bin) +%% Two-channel blob with a single shared response. Both channels are +%% assigned to response 0, which suits identical L/R drivers on a +%% single endpoint. For endpoints with distinct per-channel tuning, +%% pass num_responses > 1 and adjust assign_response accordingly. +%% +%% iir_bin / fir_bin are relative to `cpath` (the UCM staging tree), +%% iir_txt / fir_txt are full paths to the sof-ctl text dumps under +%% tools/ctl/ipc4/eq_{iir,fir}/ and are written as-is. +channels_in_config = 2; +num_responses = 1; +assign_response = [0 0]; + +%% IIR blob +bq_iir = sof_eq_iir_blob_quant(eq.p_z, eq.p_p, eq.p_k); +bm_iir = sof_eq_iir_blob_merge(channels_in_config, num_responses, ... + assign_response, bq_iir); +bp_iir = sof_eq_iir_blob_pack(bm_iir, 4); % IPC4 +sof_alsactl_write(iir_txt, bp_iir); +sof_ucm_blob_write(fullfile(cpath, iir_bin), bp_iir); + +%% FIR blob +bq_fir = sof_eq_fir_blob_quant(eq.b_fir); +bm_fir = sof_eq_fir_blob_merge(channels_in_config, num_responses, ... + assign_response, bq_fir); +bp_fir = sof_eq_fir_blob_pack(bm_fir, 4); % IPC4 +sof_alsactl_write(fir_txt, bp_fir); +sof_ucm_blob_write(fullfile(cpath, fir_bin), bp_fir); +end + +%% ----------------------------------------------------------------------- +%% Small helpers used by the IIR stages +%% ----------------------------------------------------------------------- +function peq = shelf_row(type, p, b) +peq = [type, clamp(p(1), b(1,1), b(1,2)), clamp(p(2), b(2,1), b(2,2)), 0]; +end + +function peq = pn_row(p, b, eq) +peq = [eq.PEQ_PN2, clamp(p(1), b(1,1), b(1,2)), ... + clamp(p(2), b(2,1), b(2,2)), ... + clamp(p(3), b(3,1), b(3,2))]; +end + +function e = stage_rms(peq_fixed, new_row, eq, fmin_fit, fmax_fit) +eq.peq = [peq_fixed; new_row]; +%% Skip the FIR design pass on every fminsearch evaluation. The stage +%% objective below only looks at eq.err_db_s and eq.iir_eq_db, which +%% sof_eq_compute produces before the FIR stage, so designing the FIR +%% here would just be wasted work inside the optimization loop. +eq.enable_fir = 0; +try + eq2 = sof_eq_compute(eq); +catch + e = 1e6; + return; +end +idx = eq2.f >= fmin_fit & eq2.f <= fmax_fit; +%% eq.err_db_s is the eq-smoothed (logsmooth_eq) target-minus-measurement +%% error the FIR will pick up. What remains for the FIR after the IIR is +%% err_db_s - iir_eq_db, so minimizing its shape here fits the IIR against +%% the same signal the FIR design consumes and applies logsmooth_eq (not +%% the plot-only logsmooth_plot). +residual = eq2.err_db_s(idx) - eq2.iir_eq_db(idx); +residual = residual - mean(residual); +e = sqrt(mean(residual .^ 2)); +end + +function y = clamp(x, lo, hi) +y = min(hi, max(lo, x)); +end + +%% ----------------------------------------------------------------------- +%% UCM product .conf generation and misc string / filesystem helpers +%% ----------------------------------------------------------------------- +function write_product_conf(conf_file, sys_vendor, product_name, endpoint, base, eq) +ep_cap = capitalize(endpoint); +iir_key = sprintf('Define.PostMixer%sPlaybackIirBlob', ep_cap); +fir_key = sprintf('Define.PostMixer%sPlaybackFirBlob', ep_cap); +iir_path = sprintf('/usr/share/alsa/ucm2/blobs/sof/ipc4/eq_iir/%s_iir.bin', base); +fir_path = sprintf('/usr/share/alsa/ucm2/blobs/sof/ipc4/eq_fir/%s_fir.bin', base); + +fid = fopen(conf_file, 'w'); +if fid < 0 + error('Could not open %s for writing', conf_file); +end +fprintf(fid, '# Add bespoke %s equalizer for %s %s\n', endpoint, sys_vendor, product_name); +fprintf(fid, '#\n'); +fprintf(fid, '# This file was generated with %s.m\n', mfilename()); +fprintf(fid, '#\n'); +fprintf(fid, '# IIR is defined as parametric equalizer and FIR carries the mid-band residual\n'); +fprintf(fid, '# correction, see:\n'); +fprintf(fid, '# https://github.com/thesofproject/sof/tree/main/src/audio/eq_iir/tune\n'); +fprintf(fid, '#\n'); +fprintf(fid, '# IIR biquad stages:\n'); +fprintf(fid, '# type fc [Hz] gain [dB] Q\n'); +for i = 1:size(eq.peq, 1) + fprintf(fid, '# %-4s %8.1f %+6.2f %5.2f\n', ... + peq_short_name(eq, eq.peq(i, 1)), ... + eq.peq(i, 2), eq.peq(i, 3), eq.peq(i, 4)); +end +fprintf(fid, '#\n'); +fprintf(fid, '# FIR parameters:\n'); +fprintf(fid, '# length = %d taps\n', eq.fir_length); +fprintf(fid, '# kaiser beta = %g\n', eq.fir_beta); +fprintf(fid, '# minph = %d\n', eq.fir_minph); +fprintf(fid, '# band = [%g, %g] Hz\n', eq.fmin_fir, eq.fmax_fir); +fprintf(fid, '\n'); +fprintf(fid, '%s "%s"\n', iir_key, iir_path); +fprintf(fid, '%s "%s"\n', fir_key, fir_path); +fclose(fid); +fprintf('Wrote %s\n', conf_file); +end + +function s = sanitize_name(field_name, s) +% Lower case, replace any non-alphanumeric run with a single underscore, and +% trim leading/trailing underscores. e.g. 'Acme Ltd.' -> 'acme_ltd', +% 'Model 100' -> 'model_100'. +% +% Error out if the result is empty (input had no ASCII letters or digits, +% e.g. a non-ASCII-only DMI value). Otherwise the blob base name would +% collapse to something like 'speaker__' and every product with such a +% DMI value would land on the same blob path. +raw = s; +s = lower(s); +s = regexprep(s, '[^a-z0-9]+', '_'); +s = regexprep(s, '^_+|_+$', ''); +if isempty(s) + error(['%s "%s" has no ASCII letters or digits and would produce ' ... + 'an empty blob name component; please pass a value that ' ... + 'contains at least one [a-z0-9] character.'], field_name, raw); +end +end + +function validate_dmi_field(field_name, s) +% Reject DMI values that would escape the output tree or otherwise produce +% a bad directory / file name when used verbatim. Path separators and the +% special names '.' and '..' are refused; any other printable UTF-8 string +% is accepted because DMI content is noisy in the wild. +if ~ischar(s) || isempty(s) + error('%s must be a non-empty string', field_name); +end +if any(s == '/') || any(s == '\') + error('%s must not contain path separators: "%s"', field_name, s); +end +if strcmp(s, '.') || strcmp(s, '..') + error('%s must not be "." or "..": "%s"', field_name, s); +end +if any(s < 32) + error('%s must not contain control characters', field_name); +end +end + +function validate_endpoint(s) +% endpoint becomes part of the UCM key Define.PostMixer<Endpoint>Playback... +% and a component of blob file names, so restrict it to a plain letters-only +% identifier. Extend the pattern here if a new endpoint naming scheme is +% needed. +if ~ischar(s) || isempty(s) + error('endpoint must be a non-empty string'); +end +if isempty(regexp(s, '^[A-Za-z]+$', 'once')) + error('endpoint must be letters only (e.g. speaker, headphone): "%s"', s); +end +end + +function s = capitalize(s) +if isempty(s) + return; +end +s = [upper(s(1)), lower(s(2:end))]; +end + +function ensure_dir(d) +if ~exist(d, 'dir') + [ok, msg] = mkdir(d); + if ~ok + error('mkdir %s failed: %s', d, msg); + end +end +end + +function enable_common_paths(script_dir, enable) +% sof_eq_paths() addpath/rmpaths a relative '../../../../tools/tune/common' +% that is resolved against the current working directory, so it only works +% when called from src/audio/eq_iir/tune/. Do a scoped cd to script_dir so +% the (dis)enable resolves correctly regardless of the caller's CWD. +% onCleanup restores the original directory even if sof_eq_paths errors. +orig_dir = pwd(); +cleanup = onCleanup(@() cd(orig_dir)); %#ok<NASGU> +cd(script_dir); +sof_eq_paths(enable); +end + +function name = peq_type_name(eq, type_num) +% Look up the PEQ_* field name whose value matches type_num, using the +% constants that sof_eq_defaults() already stored on the eq struct. This +% keeps the mapping in sync with sof_eq_define_parametric_eq.m without +% duplicating the enum here. +fns = fieldnames(eq); +for k = 1:numel(fns) + if strncmp(fns{k}, 'PEQ_', 4) && isnumeric(eq.(fns{k})) && ... + isscalar(eq.(fns{k})) && eq.(fns{k}) == type_num + name = fns{k}; + return; + end +end +name = sprintf('PEQ_%d', type_num); +end + +function name = peq_short_name(eq, type_num) +% Same lookup as peq_type_name but with the 'PEQ_' prefix stripped, e.g. +% 'HP2' or 'LS2', for compact human-readable summaries in the .conf header. +name = peq_type_name(eq, type_num); +if strncmp(name, 'PEQ_', 4) + name = name(5:end); +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..d67a97a5823f 100644 --- a/src/audio/google/google_hotword_detect.c +++ b/src/audio/google/google_hotword_detect.c @@ -15,7 +15,6 @@ #include <sof/ipc/msg.h> #include <rtos/alloc.h> #include <rtos/init.h> -#include <sof/lib/memory.h> /* for SHARED_DATA */ #include <sof/lib/notifier.h> #include <rtos/wait.h> #include <sof/lib/uuid.h> @@ -45,7 +44,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 +61,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 +70,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 +133,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 +141,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 +392,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 +423,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 +436,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 +445,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), @@ -465,14 +465,13 @@ static const struct comp_driver ghd_driver = { }, }; -static SHARED_DATA struct comp_driver_info ghd_driver_info = { +static struct comp_driver_info ghd_driver_info = { .drv = &ghd_driver, }; UT_STATIC void sys_comp_ghd_init(void) { - comp_register(platform_shared_get(&ghd_driver_info, - sizeof(ghd_driver_info))); + comp_register(&ghd_driver_info); } DECLARE_MODULE(sys_comp_ghd_init); diff --git a/src/audio/google/google_hotword_detect.toml b/src/audio/google/google_hotword_detect.toml new file mode 100644 index 000000000000..1c51b2f07eb3 --- /dev/null +++ b/src/audio/google/google_hotword_detect.toml @@ -0,0 +1,23 @@ +#ifndef LOAD_TYPE +#define LOAD_TYPE "0" +#endif + +[[module.entry]] +name = "HOTWORD" +uuid = UUIDREG_STR_GOOGLE_HOTWORD +affinity_mask = "0x1" +instance_count = "1" +domain_types = "0" +load_type = LOAD_TYPE +module_type = "10" +auto_start = "0" +sched_caps = [1, 0x00008000] + +REM # pin = [dir, type, sample rate, size, container, channel-cfg] +pin = [0, 0, 0x8, 0x2, 0x2, 0x3, + 1, 0, 0xfeef, 0xf, 0xf, 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, 0, 4000000, 0, 0, 0, 0, 0] + +index = __COUNTER__ 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..d17da437ffb4 100644 --- a/src/audio/host-legacy.c +++ b/src/audio/host-legacy.c @@ -17,7 +17,6 @@ #include <rtos/init.h> #include <sof/lib/dma.h> #include <sof/lib/mailbox.h> -#include <sof/lib/memory.h> /* for SHARED_DATA */ #include <sof/lib/notifier.h> #include <sof/lib/uuid.h> #include <sof/list.h> @@ -25,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> @@ -40,8 +40,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 +127,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 +195,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) { @@ -228,7 +226,9 @@ void host_common_update(struct host_data *hd, struct comp_dev *dev, uint32_t byt struct comp_buffer *sink; int ret; bool update_mailbox = false; +#if CONFIG_HOST_DMA_IPC_POSITION_UPDATES bool send_ipc = false; +#endif if (dev->direction == SOF_IPC_STREAM_PLAYBACK) { source = hd->dma_buffer; @@ -242,7 +242,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 +252,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 @@ -269,6 +273,7 @@ void host_common_update(struct host_data *hd, struct comp_dev *dev, uint32_t byt if (hd->cont_update_posn) update_mailbox = true; +#if CONFIG_HOST_DMA_IPC_POSITION_UPDATES /* Don't send stream position if no_stream_position == 1 */ if (!hd->no_stream_position) { hd->report_pos += bytes; @@ -288,13 +293,16 @@ void host_common_update(struct host_data *hd, struct comp_dev *dev, uint32_t byt send_ipc = true; } } +#endif if (update_mailbox) { pipeline_get_timestamp(dev->pipeline, dev, &hd->posn); mailbox_stream_write(dev->pipeline->posn_offset, &hd->posn, sizeof(hd->posn)); +#if CONFIG_HOST_DMA_IPC_POSITION_UPDATES if (send_ipc) ipc_msg_send(hd->msg, &hd->posn, false); +#endif } } @@ -344,7 +352,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 +419,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 +450,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 +460,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 +523,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) @@ -550,12 +558,14 @@ int host_common_new(struct host_data *hd, struct comp_dev *dev, ipc_build_stream_posn(&hd->posn, SOF_IPC_STREAM_POSITION, config_id); +#if CONFIG_HOST_DMA_IPC_POSITION_UPDATES hd->msg = ipc_msg_init(hd->posn.rhdr.hdr.cmd, hd->posn.rhdr.hdr.size); if (!hd->msg) { comp_err(dev, "ipc_msg_init failed"); dma_put(hd->dma); return -ENOMEM; } +#endif hd->chan = NULL; hd->copy_type = COMP_COPY_NORMAL; @@ -595,26 +605,36 @@ 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); +#if CONFIG_HOST_DMA_IPC_POSITION_UPDATES ipc_msg_free(hd->msg); - dma_sg_free(&hd->config.elem_array); +#endif + 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 +675,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) { @@ -675,7 +695,7 @@ int host_common_params(struct host_data *hd, struct comp_dev *dev, uint32_t period_bytes; uint32_t buffer_size; uint32_t addr_align; - uint32_t align; + uint32_t align = 0; int err; /* host params always installed by pipeline IPC */ @@ -764,7 +784,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 +851,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 +888,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 +910,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 +946,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)); @@ -921,7 +961,9 @@ void host_common_reset(struct host_data *hd, uint16_t state) /* reset buffer pointers */ hd->local_pos = 0; +#if CONFIG_HOST_DMA_IPC_POSITION_UPDATES hd->report_pos = 0; +#endif hd->total_data_processed = 0; hd->copy_type = COMP_COPY_NORMAL; @@ -933,7 +975,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 +1046,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), @@ -1023,14 +1067,13 @@ static const struct comp_driver comp_host = { }, }; -static SHARED_DATA struct comp_driver_info comp_host_info = { +static struct comp_driver_info comp_host_info = { .drv = &comp_host, }; UT_STATIC void sys_comp_host_init(void) { - comp_register(platform_shared_get(&comp_host_info, - sizeof(comp_host_info))); + comp_register(&comp_host_info); } DECLARE_MODULE(sys_comp_host_init); diff --git a/src/audio/host-zephyr.c b/src/audio/host-zephyr.c index 6bf1fcf78217..5f23ff405857 100644 --- a/src/audio/host-zephyr.c +++ b/src/audio/host-zephyr.c @@ -17,13 +17,14 @@ #include <rtos/init.h> #include <sof/lib/dma.h> #include <sof/lib/mailbox.h> -#include <sof/lib/memory.h> /* for SHARED_DATA */ #include <sof/lib/uuid.h> #include <sof/list.h> #include <sof/math/numbers.h> #include <rtos/string.h> #include <sof/ut.h> #include <sof/trace/trace.h> +#include <sof/debug/telemetry/performance_monitor.h> +#include <sof/schedule/ll_schedule_domain.h> /* zephyr_ll_user_heap() */ #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; } @@ -247,7 +245,9 @@ void host_common_update(struct host_data *hd, struct comp_dev *dev, uint32_t byt struct comp_buffer *sink; int ret; bool update_mailbox = false; +#if CONFIG_HOST_DMA_IPC_POSITION_UPDATES bool send_ipc = false; +#endif if (dev->direction == SOF_IPC_STREAM_PLAYBACK) { source = hd->dma_buffer; @@ -265,6 +265,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 @@ -282,6 +286,7 @@ void host_common_update(struct host_data *hd, struct comp_dev *dev, uint32_t byt if (hd->cont_update_posn) update_mailbox = true; +#if CONFIG_HOST_DMA_IPC_POSITION_UPDATES /* Don't send stream position if no_stream_position == 1 */ if (!hd->no_stream_position) { hd->report_pos += bytes; @@ -301,13 +306,16 @@ void host_common_update(struct host_data *hd, struct comp_dev *dev, uint32_t byt send_ipc = true; } } +#endif if (update_mailbox) { pipeline_get_timestamp(dev->pipeline, dev, &hd->posn); mailbox_stream_write(dev->pipeline->posn_offset, &hd->posn, sizeof(hd->posn)); +#if CONFIG_HOST_DMA_IPC_POSITION_UPDATES if (send_ipc) ipc_msg_send(hd->msg, &hd->posn, false); +#endif } } @@ -354,7 +362,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 +375,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 +405,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 +434,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 +477,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); - - 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->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; - 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 +556,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 +589,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 +616,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(hd->alloc_ctx.heap, &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 +626,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(hd->alloc_ctx.heap, 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 +657,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 +665,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 +689,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) @@ -726,15 +726,28 @@ __cold int host_common_new(struct host_data *hd, struct comp_dev *dev, ipc_build_stream_posn(&hd->posn, SOF_IPC_STREAM_POSITION, config_id); +#if CONFIG_HOST_DMA_IPC_POSITION_UPDATES hd->msg = ipc_msg_init(hd->posn.rhdr.hdr.cmd, sizeof(hd->posn)); if (!hd->msg) { comp_err(dev, "ipc_msg_init failed"); sof_dma_put(hd->dma); return -ENOMEM; } - hd->chan = NULL; +#endif + hd->chan_index = -EINVAL; hd->copy_type = COMP_COPY_NORMAL; +#ifdef CONFIG_SOF_USERSPACE_LL + /* + * copier_host_create() uses mod_zalloc() to allocate + * the 'hd' host data object and does not set hd->alloc_ctx. + * If LL is run in user-space, assign the 'heap' here. + */ + hd->alloc_ctx.heap = zephyr_ll_user_heap(); +#else + hd->alloc_ctx.heap = NULL; +#endif + return 0; } @@ -745,6 +758,7 @@ __cold static struct comp_dev *host_new(const struct comp_driver *drv, struct comp_dev *dev; struct host_data *hd; const struct ipc_config_host *ipc_host = spec; + struct k_heap *heap = NULL; int ret; assert_can_be_cold(); @@ -756,10 +770,12 @@ __cold static struct comp_dev *host_new(const struct comp_driver *drv, return NULL; dev->ipc_config = *config; - hd = rzalloc(SOF_MEM_FLAG_USER, sizeof(*hd)); + hd = sof_heap_alloc(heap, SOF_MEM_FLAG_USER, sizeof(*hd), 0); if (!hd) goto e_data; + memset(hd, 0, sizeof(*hd)); + hd->nobytes_last_logged = k_uptime_get(); comp_set_drvdata(dev, hd); @@ -772,9 +788,9 @@ __cold static struct comp_dev *host_new(const struct comp_driver *drv, return dev; e_dev: - rfree(hd); + sof_heap_free(heap, hd); e_data: - rfree(dev); + comp_free_device(dev); return NULL; } @@ -782,10 +798,27 @@ __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); +#if CONFIG_HOST_DMA_IPC_POSITION_UPDATES ipc_msg_free(hd->msg); - dma_sg_free(&hd->config.elem_array); +#endif + dma_sg_free(hd->alloc_ctx.heap, &hd->config.elem_array); } __cold static void host_free(struct comp_dev *dev) @@ -794,10 +827,11 @@ __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); + /* NULL heap passed in host_new(), must match! */ + sof_heap_free(NULL, hd); + comp_free_device(dev); } static int host_elements_reset(struct host_data *hd, int direction) @@ -838,7 +872,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) { @@ -862,8 +896,8 @@ int host_common_params(struct host_data *hd, struct comp_dev *dev, uint32_t buffer_size; uint32_t buffer_size_preferred; uint32_t addr_align; - uint32_t align; - int i, channel, err; + uint32_t align = 0; + int i, err; bool is_scheduling_source = dev == dev->pipeline->sched_comp; uint32_t round_up_size; @@ -875,8 +909,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 +918,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 +988,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(&hd->alloc_ctx, buffer_size_preferred, buffer_size, SOF_MEM_FLAG_USER | SOF_MEM_FLAG_DMA, addr_align, BUFFER_USAGE_NOT_SHARED); if (!hd->dma_buffer) { @@ -999,26 +1033,20 @@ 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, - sizeof(*dma_block_cfg)); + dma_block_cfg = sof_heap_alloc(hd->alloc_ctx.heap, SOF_MEM_FLAG_USER, + sizeof(*dma_block_cfg), 0); if (!dma_block_cfg) { comp_err(dev, "dma_block_config allocation failed"); @@ -1026,6 +1054,8 @@ int host_common_params(struct host_data *hd, struct comp_dev *dev, goto err_release_channel; } + memset(dma_block_cfg, 0, sizeof(*dma_block_cfg)); + dma_cfg->block_count = 1; dma_cfg->source_data_size = config->src_width; dma_cfg->dest_data_size = config->dest_width; @@ -1061,14 +1091,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 +1115,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); + sof_heap_free(hd->alloc_ctx.heap, 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 +1151,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 +1173,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 +1198,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(hd->alloc_ctx.heap, &hd->host.elem_array); + dma_sg_free(hd->alloc_ctx.heap, &hd->local.elem_array); + dma_sg_free(hd->alloc_ctx.heap, &hd->config.elem_array); /* free DMA buffer */ if (hd->dma_buffer) { @@ -1169,11 +1217,13 @@ void host_common_reset(struct host_data *hd, uint16_t state) /* free DMA block configuration */ if (hd->z_config.head_block) - rfree(hd->z_config.head_block); + sof_heap_free(hd->alloc_ctx.heap, hd->z_config.head_block); /* reset buffer pointers */ hd->local_pos = 0; +#if CONFIG_HOST_DMA_IPC_POSITION_UPDATES hd->report_pos = 0; +#endif hd->total_data_processed = 0; hd->copy_type = COMP_COPY_NORMAL; @@ -1185,7 +1235,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 +1310,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), @@ -1279,14 +1331,13 @@ static const struct comp_driver comp_host = { }, }; -static SHARED_DATA struct comp_driver_info comp_host_info = { +static struct comp_driver_info comp_host_info = { .drv = &comp_host, }; UT_STATIC void sys_comp_host_init(void) { - comp_register(platform_shared_get(&comp_host_info, - sizeof(comp_host_info))); + comp_register(&comp_host_info); } DECLARE_MODULE(sys_comp_host_init); 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..f762cf86b49d 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 @@ -72,10 +69,10 @@ static int igo_nr_capture_s16(struct comp_data *cd, struct sof_sink *sink, int32_t frames) { - int16_t *x, *y1, *y2; - int16_t *x_start, *y_start, *x_end, *y_end; + int16_t const *x, *x_start, *x_end; + int16_t *y1, *y2, *y_start, *y_end; int16_t sample; - size_t x_size, y_size; + int x_size, y_size; size_t request_size = frames * source_get_frame_bytes(source); int sink_samples_without_wrap; int samples_without_wrap; @@ -96,12 +93,14 @@ static int igo_nr_capture_s16(struct comp_data *cd, #endif - ret = source_get_data(source, request_size, (void const **)&x, - (void const **)&x_start, &x_size); + /* use the sample-count source/sink accessors so the returned sizes are + * already in samples and need no byte-to-element conversion + */ + ret = source_get_data_s16(source, request_size, &x, &x_start, &x_size); if (ret) return ret; - ret = sink_get_buffer(sink, request_size, (void **)&y1, (void **)&y_start, &y_size); + ret = sink_get_buffer_s16(sink, request_size, &y1, &y_start, &y_size); if (ret) { source_release_data(source, 0); return ret; @@ -133,7 +132,10 @@ static int igo_nr_capture_s16(struct comp_data *cd, i++; } - x = cir_buf_wrap(x, x_start, x_end); + /* x is const (read-only source), so wrap manually instead of + * cir_buf_wrap() which takes a non-const void * + */ + x = (x >= x_end) ? x - x_size : x; y1 = cir_buf_wrap(y1, y_start, y_end); samples_left -= samples_without_wrap; } @@ -175,9 +177,9 @@ static int igo_nr_capture_s24(struct comp_data *cd, struct sof_sink *sink, int32_t frames) { - int32_t *x, *y1, *y2; - int32_t *x_start, *y_start, *x_end, *y_end; - size_t x_size, y_size; + int32_t const *x, *x_start, *x_end; + int32_t *y1, *y2, *y_start, *y_end; + int x_size, y_size; size_t request_size = frames * source_get_frame_bytes(source); int sink_samples_without_wrap; int samples_without_wrap; @@ -198,12 +200,14 @@ static int igo_nr_capture_s24(struct comp_data *cd, #endif - ret = source_get_data(source, request_size, (void const **)&x, - (void const **)&x_start, &x_size); + /* use the sample-count source/sink accessors so the returned sizes are + * already in samples and need no byte-to-element conversion + */ + ret = source_get_data_s32(source, request_size, &x, &x_start, &x_size); if (ret) return ret; - ret = sink_get_buffer(sink, request_size, (void **)&y1, (void **)&y_start, &y_size); + ret = sink_get_buffer_s32(sink, request_size, &y1, &y_start, &y_size); if (ret) { source_release_data(source, 0); return ret; @@ -234,7 +238,10 @@ static int igo_nr_capture_s24(struct comp_data *cd, i++; } - x = cir_buf_wrap(x, x_start, x_end); + /* x is const (read-only source), so wrap manually instead of + * cir_buf_wrap() which takes a non-const void * + */ + x = (x >= x_end) ? x - x_size : x; y1 = cir_buf_wrap(y1, y_start, y_end); samples_left -= samples_without_wrap; } @@ -276,9 +283,9 @@ static int igo_nr_capture_s32(struct comp_data *cd, struct sof_sink *sink, int32_t frames) { - int32_t *x, *y1, *y2; - int32_t *x_start, *y_start, *x_end, *y_end; - size_t x_size, y_size; + int32_t const *x, *x_start, *x_end; + int32_t *y1, *y2, *y_start, *y_end; + int x_size, y_size; size_t request_size = frames * source_get_frame_bytes(source); int sink_samples_without_wrap; int samples_without_wrap; @@ -299,12 +306,14 @@ static int igo_nr_capture_s32(struct comp_data *cd, #endif - ret = source_get_data(source, request_size, (void const **)&x, - (void const **)&x_start, &x_size); + /* use the sample-count source/sink accessors so the returned sizes are + * already in samples and need no byte-to-element conversion + */ + ret = source_get_data_s32(source, request_size, &x, &x_start, &x_size); if (ret) return ret; - ret = sink_get_buffer(sink, request_size, (void **)&y1, (void **)&y_start, &y_size); + ret = sink_get_buffer_s32(sink, request_size, &y1, &y_start, &y_size); if (ret) { source_release_data(source, 0); return ret; @@ -335,7 +344,10 @@ static int igo_nr_capture_s32(struct comp_data *cd, i++; } - x = cir_buf_wrap(x, x_start, x_end); + /* x is const (read-only source), so wrap manually instead of + * cir_buf_wrap() which takes a non-const void * + */ + x = (x >= x_end) ? x - x_size : x; y1 = cir_buf_wrap(y1, y_start, y_end); samples_left -= samples_without_wrap; } @@ -380,24 +392,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 +424,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 +445,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 +475,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 +489,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 +505,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 +541,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 +578,17 @@ 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: + if (cdata->num_elems > SOF_IPC_MAX_CHANNELS) { + comp_err(dev, "num_elems %u out of range", cdata->num_elems); + return -EINVAL; + } 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 +645,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 +676,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; } @@ -718,17 +734,32 @@ static void igo_nr_print_config(struct processing_module *mod) static void igo_nr_set_igo_params(struct processing_module *mod) { struct comp_data *cd = module_get_private_data(mod); - struct sof_igo_nr_config *p_config = comp_get_data_blob(cd->model_handler, NULL, NULL); + size_t config_size; + struct sof_igo_nr_config *p_config = comp_get_data_blob(cd->model_handler, + &config_size, NULL); struct comp_dev *dev = mod->dev; - comp_info(dev, "igo_nr_set_igo_params()"); - igo_nr_check_config_validity(dev, cd); + comp_info(dev, "entry"); + + if (!p_config) + return; - if (p_config) { - comp_info(dev, "New config detected."); - cd->config = *p_config; - igo_nr_print_config(mod); + /* the whole config struct is copied below, so the blob must be at + * least that large + */ + if (config_size < sizeof(cd->config)) { + comp_err(dev, "New config too small: %zu < %zu", + config_size, sizeof(cd->config)); + return; } + + /* Adopt the host blob only when the new config is valid */ + if (igo_nr_check_config_validity(dev, cd) != 0) + return; + + comp_info(dev, "New config detected."); + cd->config = *p_config; + igo_nr_print_config(mod); } /* copy and process stream data from source to sink buffers */ @@ -816,7 +847,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 +866,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 +897,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 +932,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/intel_uaol.c b/src/audio/intel_uaol.c new file mode 100644 index 000000000000..baeb4a217103 --- /dev/null +++ b/src/audio/intel_uaol.c @@ -0,0 +1,79 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * + * Copyright 2026 Intel Corporation. All rights reserved. + */ + +#include <zephyr/drivers/uaol.h> +#include <rtos/string.h> +#include <sof/tlv.h> +#include <sof/audio/component_ext.h> +#include <sof/audio/intel_uaol.h> + +LOG_MODULE_REGISTER(uaol, CONFIG_SOF_LOG_LEVEL); + +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); + +#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 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 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; +} diff --git a/src/audio/kpb.c b/src/audio/kpb.c index e8a8bb104818..4e5049919baf 100644 --- a/src/audio/kpb.c +++ b/src/audio/kpb.c @@ -26,7 +26,6 @@ #include <rtos/alloc.h> #include <rtos/clk.h> #include <rtos/init.h> -#include <sof/lib/memory.h> /* for SHARED_DATA */ #include <sof/lib/pm_runtime.h> #include <sof/lib/uuid.h> #include <sof/list.h> @@ -70,8 +69,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 +188,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 +284,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; @@ -340,14 +337,14 @@ static int kpb_get_attribute(struct comp_dev *dev, * \param[in] bind_data - bind/unbind data. * \return: none. */ -static int kpb_bind(struct comp_dev *dev, struct bind_info *bind_data) +static int kpb_bind(struct comp_dev *dev, const struct bind_info *bind_data) { struct comp_data *kpb = comp_get_drvdata(dev); - struct ipc4_module_bind_unbind *bu; + const struct ipc4_module_bind_unbind *bu; 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); @@ -387,13 +384,13 @@ static int kpb_bind(struct comp_dev *dev, struct bind_info *bind_data) * \param[in] data - ipc4 bind/unbind data. * \return: none. */ -static int kpb_unbind(struct comp_dev *dev, struct bind_info *unbind_data) +static int kpb_unbind(struct comp_dev *dev, const struct bind_info *unbind_data) { struct comp_data *kpb = comp_get_drvdata(dev); - struct ipc4_module_bind_unbind *bu; + const 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 +500,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 +508,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 +543,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 +573,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 +664,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 +691,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 +719,7 @@ static void kpb_free(struct comp_dev *dev) /* Free KPB */ rfree(kpb); - rfree(dev); + comp_free_device(dev); } /** @@ -733,7 +730,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 +740,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 +803,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); @@ -1035,18 +1032,19 @@ static void kpb_micselect_copy16(struct comp_buffer *sink, { struct audio_stream *istream = &source->stream; struct audio_stream *ostream = &sink->stream; - uint16_t ch; + uint32_t ch; size_t i; AE_SETCBEGIN0(audio_stream_get_addr(ostream)); AE_SETCEND0(audio_stream_get_end_addr(ostream)); - buffer_stream_invalidate(source, size); + const size_t samples_per_chan = size / (sizeof(ae_int16) * micsel_channels); + + buffer_stream_invalidate(source, samples_per_chan * in_channels * sizeof(ae_int16)); const ae_int16 *in_ptr = audio_stream_get_rptr(istream); ae_int16x4 d16 = AE_ZERO16(); const size_t in_offset = in_channels * sizeof(ae_int16); const size_t out_offset = micsel_channels * sizeof(ae_int16); - const size_t samples_per_chan = size / (sizeof(uint16_t) * micsel_channels); ae_int16 *out_ptr; for (ch = 0; ch < micsel_channels; ch++) { @@ -1068,19 +1066,20 @@ static void kpb_micselect_copy32(struct comp_buffer *sink, { struct audio_stream *istream = &source->stream; struct audio_stream *ostream = &sink->stream; - uint16_t ch; + uint32_t ch; size_t i; AE_SETCBEGIN0(audio_stream_get_addr(ostream)); AE_SETCEND0(audio_stream_get_end_addr(ostream)); - buffer_stream_invalidate(source, size); + const size_t samples_per_chan = size / (sizeof(ae_int32) * micsel_channels); + + buffer_stream_invalidate(source, samples_per_chan * in_channels * sizeof(ae_int32)); const ae_int32 *in_ptr = audio_stream_get_rptr(istream); ae_int32x2 d32 = AE_ZERO32(); const size_t in_offset = in_channels * sizeof(ae_int32); const size_t out_offset = micsel_channels * sizeof(ae_int32); - const size_t samples_per_chan = size / (sizeof(uint32_t) * micsel_channels); ae_int32 *out_ptr; for (ch = 0; ch < micsel_channels; ch++) { @@ -1102,14 +1101,14 @@ static void kpb_micselect_copy16(struct comp_buffer *sink, { struct audio_stream *istream = &source->stream; struct audio_stream *ostream = &sink->stream; - - buffer_stream_invalidate(source, size); size_t out_samples; - uint16_t ch; + uint32_t ch; const int16_t *in_data; int16_t *out_data; - const uint32_t samples_per_chan = size / (sizeof(uint16_t) * micsel_channels); + const size_t samples_per_chan = size / (sizeof(uint16_t) * micsel_channels); + + buffer_stream_invalidate(source, samples_per_chan * in_channels * sizeof(uint16_t)); for (ch = 0; ch < micsel_channels; ch++) { out_samples = 0; @@ -1134,13 +1133,13 @@ static void kpb_micselect_copy32(struct comp_buffer *sink, { struct audio_stream *istream = &source->stream; struct audio_stream *ostream = &sink->stream; - - buffer_stream_invalidate(source, size); size_t out_samples; - uint16_t ch; + uint32_t ch; const int32_t *in_data; int32_t *out_data; - const uint32_t samples_per_chan = size / (sizeof(uint32_t) * micsel_channels); + const size_t samples_per_chan = size / (sizeof(uint32_t) * micsel_channels); + + buffer_stream_invalidate(source, samples_per_chan * in_channels * sizeof(uint32_t)); for (ch = 0; ch < micsel_channels; ch++) { out_samples = 0; @@ -1211,7 +1210,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 +1410,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 +1565,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"); @@ -1609,7 +1608,7 @@ static void kpb_init_draining(struct comp_dev *dev, struct kpb_client *cli) struct comp_data *kpb = comp_get_drvdata(dev); bool is_sink_ready = (comp_buffer_get_sink_state(kpb->host_sink) == COMP_STATE_ACTIVE); size_t sample_width = kpb->config.sampling_width; - size_t drain_req = cli->drain_req * kpb->config.channels * + size_t drain_req = (size_t)cli->drain_req * kpb->config.channels * (kpb->config.sampling_freq / 1000) * (KPB_SAMPLE_CONTAINER_SIZE(sample_width) / 8); struct history_buffer *buff = kpb->hd.c_hb; @@ -1618,7 +1617,7 @@ static void kpb_init_draining(struct comp_dev *dev, struct kpb_client *cli) size_t local_buffered; size_t drain_interval; size_t host_period_size = kpb->host_period_size; - size_t bytes_per_ms = KPB_SAMPLES_PER_MS * + size_t bytes_per_ms = (size_t)KPB_SAMPLES_PER_MS * (KPB_SAMPLE_CONTAINER_SIZE(sample_width) / 8) * kpb->config.channels; size_t period_bytes_limit; @@ -1720,10 +1719,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; @@ -1790,7 +1789,7 @@ static void adjust_drain_interval(struct comp_data *kpb, struct draining_data *d /* average drained bytes per second */ actual_pace = (size_t)k_ms_to_cyc_ceil64(1000) / elapsed * drained; - pipeline_period = KPB_SAMPLES_PER_MS * + pipeline_period = (size_t)KPB_SAMPLES_PER_MS * (KPB_SAMPLE_CONTAINER_SIZE(dd->sample_width) / 8) * kpb->config.channels; /* desired draining pace in bytes per second */ optimal_pace = pipeline_period * KPB_DRAIN_NUM_OF_PPL_PERIODS_AT_ONCE * 1000; @@ -1848,7 +1847,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 +1967,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 +2211,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 +2362,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; @@ -2396,7 +2395,7 @@ static inline bool validate_host_params(struct comp_dev *dev, */ struct comp_data *kpb = comp_get_drvdata(dev); size_t sample_width = kpb->config.sampling_width; - size_t bytes_per_ms = KPB_SAMPLES_PER_MS * + size_t bytes_per_ms = (size_t)KPB_SAMPLES_PER_MS * (KPB_SAMPLE_CONTAINER_SIZE(sample_width) / 8) * kpb->config.channels; size_t pipeline_period_size = (dev->pipeline->period / 1000) @@ -2459,12 +2458,27 @@ static int kpb_set_micselect(struct comp_dev *dev, const void *data, { const struct kpb_micselector_config *mic_sel = data; struct comp_data *kpb = comp_get_drvdata(dev); - const size_t mic_cnt = kpb->config.channels - KPB_REFERENCE_SUPPORT_CHANNELS; - const uint8_t valid_mask = KPB_COUNT_TO_BITMASK(mic_cnt); + size_t mic_cnt; + uint8_t valid_mask; size_t i; + if (max_data_size < (int)sizeof(*mic_sel)) { + comp_err(dev, "micselector payload too small: got %d, need %d", + max_data_size, (int)sizeof(*mic_sel)); + return -EINVAL; + } + + if (kpb->config.channels < KPB_REFERENCE_SUPPORT_CHANNELS || + kpb->config.channels > KPB_MAX_SUPPORTED_CHANNELS) { + comp_err(dev, "unsupported channel count %u", kpb->config.channels); + return -EINVAL; + } + + mic_cnt = kpb->config.channels - KPB_REFERENCE_SUPPORT_CHANNELS; + valid_mask = KPB_COUNT_TO_BITMASK(mic_cnt); + if ((valid_mask & mic_sel->mask) == 0) { - comp_err(dev, "error: invalid micselector bit mask"); + comp_err(dev, "invalid micselector bit mask"); return -EINVAL; } /* selected mics counter */ @@ -2472,6 +2486,10 @@ static int kpb_set_micselect(struct comp_dev *dev, const void *data, for (i = 0; i < mic_cnt; i++) { if (KPB_IS_BIT_SET(mic_sel->mask, i)) { + if (num_of_sel_mic >= ARRAY_SIZE(kpb->offsets)) { + comp_err(dev, "too many selected mics"); + return -EINVAL; + } kpb->offsets[num_of_sel_mic] = i; num_of_sel_mic++; } @@ -2542,32 +2560,42 @@ static int prepare_fmt_modules_list(struct comp_dev *kpb_dev, struct kpb_fmt_dev_list *fmt_device_list = &((struct comp_data *)comp_get_drvdata(kpb_dev))->fmt_device_list; + if (outpin_idx >= KPB_MAX_SINK_CNT) + return -EINVAL; + fmt_device_list->kpb_list_item[outpin_idx] = kpb_dev; ret = devicelist_push(&fmt_device_list->device_list[outpin_idx], &fmt_device_list->kpb_list_item[outpin_idx]); if (ret < 0) - return ret; + goto err; for (size_t mod_idx = 0; mod_idx < modules_to_prepare->number_of_modules; ++mod_idx) { uint32_t comp_id = IPC4_COMP_ID(modules_to_prepare->dev_ids[mod_idx].module_id, modules_to_prepare->dev_ids[mod_idx].instance_id); dev = ipc4_get_comp_dev(comp_id); - if (!dev) - return -EINVAL; + if (!dev) { + ret = -EINVAL; + goto err; + } struct comp_dev **new_list_item_ptr; ret = alloc_fmt_module_list_item(fmt_device_list, dev, &new_list_item_ptr); if (ret < 0) - return ret; + goto err; *new_list_item_ptr = dev; ret = devicelist_push(&fmt_device_list->device_list[outpin_idx], new_list_item_ptr); if (ret < 0) - return ret; + goto err; } return 0; + +err: + /* drop any entries pushed so far to avoid leaving a half-configured list */ + clear_fmt_modules_list(fmt_device_list, outpin_idx); + return ret; } static int clear_fmt_modules_list(struct kpb_fmt_dev_list *fmt_device_list, @@ -2655,7 +2683,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; @@ -2669,6 +2697,15 @@ static int kpb_set_large_config(struct comp_dev *dev, uint32_t param_id, const struct kpb_task_params *cfg = (struct kpb_task_params *)data; uint32_t outpin_id = extended_param_id.part.parameter_instance; + /* payload must cover the header and all declared dev_ids[] entries */ + if (!cfg || data_offset < offsetof(struct kpb_task_params, dev_ids)) + return -EINVAL; + + if (cfg->number_of_modules > + (data_offset - offsetof(struct kpb_task_params, dev_ids)) / + sizeof(cfg->dev_ids[0])) + return -EINVAL; + return configure_fast_mode_task(dev, cfg, outpin_id); } #endif @@ -2679,6 +2716,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), @@ -2702,14 +2741,13 @@ static const struct comp_driver comp_kpb = { }, }; -static SHARED_DATA struct comp_driver_info comp_kpb_info = { +static struct comp_driver_info comp_kpb_info = { .drv = &comp_kpb, }; UT_STATIC void sys_comp_kpb_init(void) { - comp_register(platform_shared_get(&comp_kpb_info, - sizeof(comp_kpb_info))); + comp_register(&comp_kpb_info); } DECLARE_MODULE(sys_comp_kpb_init); 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/CMakeLists.txt b/src/audio/mfcc/CMakeLists.txt index f8af79d1ca8a..274c7aa05eb8 100644 --- a/src/audio/mfcc/CMakeLists.txt +++ b/src/audio/mfcc/CMakeLists.txt @@ -4,5 +4,8 @@ if(CONFIG_COMP_MFCC STREQUAL "m" AND DEFINED CONFIG_LLEXT) add_subdirectory(llext ${PROJECT_BINARY_DIR}/mfcc_llext) add_dependencies(app mfcc) else() - add_local_sources(sof mfcc.c mfcc_setup.c mfcc_common.c mfcc_generic.c mfcc_hifi4.c mfcc_hifi3.c) + add_local_sources(sof mfcc.c mfcc_setup.c mfcc_common.c mfcc_generic.c mfcc_hifi4.c mfcc_hifi3.c mfcc_vad.c) + if(CONFIG_IPC_MAJOR_4) + add_local_sources(sof mfcc_ipc4.c) + endif() endif() 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..724d3d7faf06 100644 --- a/src/audio/mfcc/mfcc.c +++ b/src/audio/mfcc/mfcc.c @@ -12,6 +12,8 @@ #include <sof/audio/format.h> #include <sof/audio/pipeline.h> #include <sof/audio/ipc-config.h> +#include <module/audio/source_api.h> +#include <module/audio/sink_api.h> #include <sof/common.h> #include <rtos/panic.h> #include <sof/ipc/msg.h> @@ -36,31 +38,39 @@ 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); +/** \brief Source/sink API based source copy function map. */ +struct mfcc_source_func_map { + uint8_t source_fmt; + mfcc_source_func func; +}; -__cold_rodata const struct mfcc_func_map mfcc_fm[] = { +__cold_rodata static const struct mfcc_source_func_map mfcc_sfm[] = { #if CONFIG_FORMAT_S16LE - {SOF_IPC_FRAME_S16_LE, mfcc_s16_default}, -#endif /* CONFIG_FORMAT_S16LE */ + {SOF_IPC_FRAME_S16_LE, mfcc_source_copy_s16}, +#endif #if CONFIG_FORMAT_S24LE - {SOF_IPC_FRAME_S24_4LE, NULL}, -#endif /* CONFIG_FORMAT_S24LE */ + {SOF_IPC_FRAME_S24_4LE, mfcc_source_copy_s24}, +#endif #if CONFIG_FORMAT_S32LE - {SOF_IPC_FRAME_S32_LE, NULL}, -#endif /* CONFIG_FORMAT_S32LE */ + {SOF_IPC_FRAME_S32_LE, mfcc_source_copy_s32}, +#endif }; -static mfcc_func mfcc_find_func(enum sof_ipc_frame source_format, - enum sof_ipc_frame sink_format, - const struct mfcc_func_map *map, - int n) +/** + * \brief Look up the source copy function for a given input format. + * + * \param[in] source_format SOF IPC frame format of the input stream. + * + * \return Pointer to the matching mfcc_source_func, or NULL if the + * format is not supported in this build. + */ +static mfcc_source_func mfcc_find_source_func(enum sof_ipc_frame source_format) { int i; - /* Find suitable processing function from map. */ - for (i = 0; i < n; i++) { - if (source_format == map[i].source) - return map[i].func; + for (i = 0; i < ARRAY_SIZE(mfcc_sfm); i++) { + if (source_format == mfcc_sfm[i].source_fmt) + return mfcc_sfm[i].func; } return NULL; @@ -70,111 +80,133 @@ static mfcc_func mfcc_find_func(enum sof_ipc_frame source_format, * End of MFCC setup code. Next the standard component methods. */ +/** + * \brief MFCC module init callback. + * + * Allocates the component private data and the configuration blob + * handler used to receive the MFCC configuration from user space. + * + * \param[in,out] mod Processing module being initialized. + * + * \return 0 on success, -ENOMEM on allocation failure. + */ 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; } +/** + * \brief MFCC module free callback. + * + * Releases the IPC notification message, configuration blob handler, + * runtime buffers and the private data structure. + * + * \param[in,out] mod Processing module being freed. + * + * \return 0 on success. + */ 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"); + ipc_msg_free(cd->msg); + cd->msg = NULL; + mod_data_blob_handler_free(mod, cd->model_handler); + mfcc_free_buffers(mod); + mod_free(mod, cd); return 0; } -static int mfcc_get_config(struct processing_module *mod, - uint32_t config_id, uint32_t *data_offset_size, - uint8_t *fragment, size_t fragment_size) -{ - 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()"); - - return comp_data_blob_get_cmd(cd->model_handler, cdata, fragment_size); -} - -static int mfcc_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) -{ - struct mfcc_comp_data *cd = module_get_private_data(mod); - - comp_info(mod->dev, "mfcc_set_config()"); - - return comp_data_blob_set(cd->model_handler, pos, data_offset_size, - fragment, fragment_size); -} - +/** + * \brief Source/sink API based process function for MFCC. + * + * Reads input audio from sof_source, runs the STFT/Mel/DCT stage, and + * delegates output formatting and commit handling to mfcc_common.c. + * + * \param[in,out] mod Processing module. + * \param[in,out] sources Source array; only sources[0] is used. + * \param[in] num_of_sources Number of sources (must be 1). + * \param[in,out] sinks Sink array; only sinks[0] is used. + * \param[in] num_of_sinks Number of sinks (must be 1). + * + * \return 0 on success, or a negative error code from the STFT or output stages. + */ static int mfcc_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 sof_source **sources, int num_of_sources, + struct sof_sink **sinks, int num_of_sinks) { struct mfcc_comp_data *cd = module_get_private_data(mod); - struct audio_stream *source = input_buffers->data; - struct audio_stream *sink = output_buffers->data; - int frames = input_buffers->size; - - comp_dbg(mod->dev, "mfcc_process(), start"); - - frames = MIN(frames, cd->max_frames); - cd->mfcc_func(mod, input_buffers, output_buffers, frames); - - /* 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"); - return 0; + struct comp_dev *dev = mod->dev; + struct mfcc_state *state = &cd->state; + bool pending; + size_t source_avail; + int frames; + int num_ceps; + + comp_dbg(dev, "start"); + + /* In compress mode, retry pending output first and avoid producing + * new frames until previous frame has been committed. In legacy + * mode, pending output is held in a dedicated staging buffer + * (state->out_stage) that STFT does not touch, so input processing + * can continue while the previous period is drained. + */ + pending = state->header_pending || state->out_remain > 0; + if (cd->config->compress_output && pending) + return mfcc_process_output(mod, cd, sources, sinks, 0, 0); + + source_avail = source_get_data_frames_available(sources[0]); + frames = MIN(source_avail, cd->max_frames); + if (!frames) + return 0; + + /* Copy input audio from source to MFCC internal circular buffer */ + cd->source_func(sources[0], &state->buf, &state->emph, frames, state->source_channel); + + /* Run STFT and Mel/DCT processing */ + num_ceps = mfcc_stft_process(mod, cd); + if (num_ceps < 0) + return num_ceps; + + return mfcc_process_output(mod, cd, sources, sinks, num_ceps, frames); } +/** + * \brief MFCC module prepare callback. + * + * Validates the source/sink connection, reads the configuration blob, + * initializes the MFCC processing state via mfcc_setup(), selects the + * input copy function for the source frame format, and prepares the + * VAD switch control notification when enabled. + * + * \param[in,out] mod Processing module being prepared. + * \param[in,out] sources Source array; only sources[0] is used. + * \param[in] num_of_sources Number of sources (must be 1). + * \param[in,out] sinks Sink array; only sinks[0] is used. + * \param[in] num_of_sinks Number of sinks (must be 1). + * + * \return 0 on success or a negative error code. + */ static int mfcc_prepare(struct processing_module *mod, struct sof_source **sources, int num_of_sources, struct sof_sink **sinks, int num_of_sinks) @@ -185,10 +217,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,50 +235,77 @@ 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."); - goto err; + comp_err(dev, "setup failed."); + return ret; } + } else { + comp_err(dev, "configuration is missing."); + return -EINVAL; } - 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"); - ret = -EINVAL; - goto err; + cd->source_func = mfcc_find_source_func(source_format); + if (!cd->source_func) { + comp_err(dev, "No source func"); + mfcc_free_buffers(mod); + return -EINVAL; } - return 0; + cd->source_format = source_format; + + if (cd->config->compress_output) + comp_info(dev, "compress PCM output mode enabled"); + + if (cd->config->enable_dtx && !cd->config->compress_output) + comp_warn(dev, "enable_dtx ignored in normal PCM mode, only applies to compress"); + + /* Initialize VAD switch control notification if enabled */ + if (cd->config->enable_vad && cd->config->update_controls) { + if (!cd->msg) { + ret = mfcc_ipc_notification_init(mod); + if (ret < 0) { + mfcc_free_buffers(mod); + return ret; + } + } + } -err: - comp_set_state(dev, COMP_TRIGGER_RESET); - return ret; + cd->vad_prev = false; + return 0; } +/** + * \brief MFCC module reset callback. + * + * Frees runtime buffers (NULLing their pointers) so a subsequent + * mfcc_prepare() can re-allocate cleanly. The configuration blob + * handler and IPC notification message are preserved. + * + * \param[in,out] mod Processing module being reset. + * + * \return 0 on success. + */ 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"); + + /* Free MFCC buffers to prevent leaks on reset->prepare cycles. + * mfcc_free_buffers() NULLs the pointers after free. + */ + mfcc_free_buffers(mod); /* Reset to similar state as init() */ - cd->mfcc_func = NULL; + cd->source_func = NULL; return 0; } @@ -255,7 +314,7 @@ static const struct module_interface mfcc_interface = { .free = mfcc_free, .set_configuration = mfcc_set_config, .get_configuration = mfcc_get_config, - .process_audio_stream = mfcc_process, + .process = mfcc_process, .prepare = mfcc_prepare, .reset = mfcc_reset, }; @@ -274,6 +333,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..3890671165fd 100644 --- a/src/audio/mfcc/mfcc_common.c +++ b/src/audio/mfcc/mfcc_common.c @@ -1,14 +1,17 @@ // 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> #include <sof/audio/mfcc/mfcc_comp.h> #include <sof/audio/component.h> -#include <sof/audio/audio_stream.h> +#include <module/audio/sink_api.h> +#include <module/audio/source_api.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> @@ -18,39 +21,284 @@ #include <errno.h> #include <stddef.h> #include <stdint.h> +#include <rtos/string.h> + +#include <sof/audio/mfcc/mfcc_vad.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. + +/* + * Source/sink API based source copy functions. + * These use sof_source API and are compiled on all platforms (generic, HiFi3, HiFi4). + */ + +#if CONFIG_FORMAT_S16LE +/** + * \brief Copy S16_LE input frames from source to MFCC circular buffer. + * + * Reads \p frames frames from \p source, selects channel \p source_channel, + * optionally applies the pre-emphasis filter \p emph, and writes Q1.15 mono + * samples to the MFCC input circular buffer \p buf. The source read is + * released after the copy completes. On source read failure the function + * returns without modifying \p buf state. + * + * \param[in,out] source Audio source providing interleaved S16_LE frames. + * \param[in,out] buf MFCC input circular buffer (mono Q1.15). + * \param[in,out] emph Pre-emphasis filter state; updated when enabled. + * \param[in] frames Number of input frames to copy. + * \param[in] source_channel Channel index to extract from the source. + */ +void mfcc_source_copy_s16(struct sof_source *source, struct mfcc_buffer *buf, + struct mfcc_pre_emph *emph, int frames, int source_channel) +{ + int16_t const *x; + int16_t const *x_start; + int16_t const *x_end; + int x_size; + int num_channels = source_get_channels(source); + size_t req_bytes = frames * num_channels * sizeof(int16_t); + int16_t *w = buf->w_ptr; + int src_without_wrap; + int dst_without_wrap; + int samples_without_wrap; + int remaining = frames; + int32_t s; + int ret; + int i; + + ret = source_get_data_s16(source, req_bytes, &x, &x_start, &x_size); + if (ret) + return; + + x += source_channel; + x_end = x_start + x_size; + + while (remaining) { + src_without_wrap = (x_end - x) / num_channels; + dst_without_wrap = buf->end_addr - w; + samples_without_wrap = MIN(src_without_wrap, dst_without_wrap); + samples_without_wrap = MIN(samples_without_wrap, remaining); + if (emph->enable) { + for (i = 0; i < samples_without_wrap; i++) { + s = (int32_t)emph->delay * emph->coef + + Q_SHIFT_LEFT(*x, 15, 30); + *w = sat_int16(Q_SHIFT_RND(s, 30, 15)); + emph->delay = *x; + x += num_channels; + w++; + } + } else { + for (i = 0; i < samples_without_wrap; i++) { + *w = *x; + x += num_channels; + w++; + } + } + x = (x >= x_end) ? x - x_size : x; + w = mfcc_buffer_wrap(buf, w); + remaining -= samples_without_wrap; + } + + buf->s_avail += frames; + buf->s_free -= frames; + buf->w_ptr = w; + source_release_data(source, req_bytes); +} +#endif /* CONFIG_FORMAT_S16LE */ + +#if CONFIG_FORMAT_S24LE +/** + * \brief Copy S24_4LE input frames from source to MFCC circular buffer. + * + * Same as mfcc_source_copy_s16() but accepts S24_4LE input (24-bit data + * sign-extended in a 32-bit container). Samples are downscaled to Q1.15 + * for the MFCC pipeline. + * + * \param[in,out] source Audio source providing interleaved S24_4LE frames. + * \param[in,out] buf MFCC input circular buffer (mono Q1.15). + * \param[in,out] emph Pre-emphasis filter state; updated when enabled. + * \param[in] frames Number of input frames to copy. + * \param[in] source_channel Channel index to extract from the source. */ -#if MFCC_FFT_BITS == 16 -#define MFCC_NORMALIZE_FFT -#else -#undef MFCC_NORMALIZE_FFT -#endif -#define MFCC_NORMALIZE_MAX_SHIFT 10 +void mfcc_source_copy_s24(struct sof_source *source, struct mfcc_buffer *buf, + struct mfcc_pre_emph *emph, int frames, int source_channel) +{ + int32_t const *x; + int32_t const *x_start; + int32_t const *x_end; + int x_size; + int num_channels = source_get_channels(source); + size_t req_bytes = frames * num_channels * sizeof(int32_t); + int16_t *w = buf->w_ptr; + int src_without_wrap; + int dst_without_wrap; + int samples_without_wrap; + int remaining = frames; + int32_t s, tmp; + int ret; + int i; + + ret = source_get_data_s32(source, req_bytes, &x, &x_start, &x_size); + if (ret) + return; + + x += source_channel; + x_end = x_start + x_size; + + while (remaining) { + src_without_wrap = (x_end - x) / num_channels; + dst_without_wrap = buf->end_addr - w; + samples_without_wrap = MIN(src_without_wrap, dst_without_wrap); + samples_without_wrap = MIN(samples_without_wrap, remaining); + if (emph->enable) { + for (i = 0; i < samples_without_wrap; i++) { + s = (int32_t)((uint32_t)*x << 8); + 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)); + x += num_channels; + w++; + } + } else { + for (i = 0; i < samples_without_wrap; i++) { + s = (int32_t)((uint32_t)*x << 8); + *w = sat_int16(Q_SHIFT_RND(s, 31, 15)); + x += num_channels; + w++; + } + + } + x = (x >= x_end) ? x - x_size : x; + w = mfcc_buffer_wrap(buf, w); + remaining -= samples_without_wrap; + } + + buf->s_avail += frames; + buf->s_free -= frames; + buf->w_ptr = w; + source_release_data(source, req_bytes); +} +#endif /* CONFIG_FORMAT_S24LE */ + +#if CONFIG_FORMAT_S32LE +/** + * \brief Copy S32_LE input frames from source to MFCC circular buffer. + * + * Same as mfcc_source_copy_s16() but accepts S32_LE input. Samples are + * downscaled to Q1.15 for the MFCC pipeline. + * + * \param[in,out] source Audio source providing interleaved S32_LE frames. + * \param[in,out] buf MFCC input circular buffer (mono Q1.15). + * \param[in,out] emph Pre-emphasis filter state; updated when enabled. + * \param[in] frames Number of input frames to copy. + * \param[in] source_channel Channel index to extract from the source. + */ +void mfcc_source_copy_s32(struct sof_source *source, struct mfcc_buffer *buf, + struct mfcc_pre_emph *emph, int frames, int source_channel) +{ + int32_t const *x; + int32_t const *x_start; + int32_t const *x_end; + int x_size; + int num_channels = source_get_channels(source); + size_t req_bytes = frames * num_channels * sizeof(int32_t); + int16_t *w = buf->w_ptr; + int src_without_wrap; + int dst_without_wrap; + int samples_without_wrap; + int remaining = frames; + int32_t s; + int ret; + int i; + + ret = source_get_data_s32(source, req_bytes, &x, &x_start, &x_size); + if (ret) + return; + + x += source_channel; + x_end = x_start + x_size; + while (remaining) { + src_without_wrap = (x_end - x) / num_channels; + dst_without_wrap = buf->end_addr - w; + samples_without_wrap = MIN(src_without_wrap, dst_without_wrap); + samples_without_wrap = MIN(samples_without_wrap, remaining); + if (emph->enable) { + for (i = 0; i < samples_without_wrap; i++) { + s = (int32_t)emph->delay * emph->coef + + Q_SHIFT(*x, 31, 30); + *w = sat_int16(Q_SHIFT_RND(s, 30, 15)); + emph->delay = sat_int16(Q_SHIFT_RND(*x, 31, 15)); + x += num_channels; + w++; + } + } else { + for (i = 0; i < samples_without_wrap; i++) { + *w = sat_int16(Q_SHIFT_RND(*x, 31, 15)); + x += num_channels; + w++; + } + } + + x = (x >= x_end) ? x - x_size : x; + w = mfcc_buffer_wrap(buf, w); + remaining -= samples_without_wrap; + } + + buf->s_avail += frames; + buf->s_free -= frames; + buf->w_ptr = w; + source_release_data(source, req_bytes); +} +#endif /* CONFIG_FORMAT_S32LE */ /* * The main processing function for MFCC */ -static int mfcc_stft_process(const struct comp_dev *dev, struct mfcc_state *state) +/** + * \brief Run one STFT hop and Mel/DCT processing. + * + * Waits until the input circular buffer has at least one FFT window of + * samples, then for each available hop: copies the overlap-add window + * into the FFT scratch buffer, applies the analysis window, runs the + * 32-bit FFT, computes Mel log spectra, and either keeps the Mel output + * (when \c state->mel_only is true) or applies DCT and cepstral lifter + * to produce cepstral coefficients. Optionally runs VAD on the Mel + * spectrum and sends an IPC notification on VAD state changes. + * + * \param[in,out] mod Processing module. + * \param[in,out] cd MFCC component data; state and configuration updated + * in-place. Output values land in \c state->mel_log_32 + * (Mel mode) or \c state->cepstral_coef (cepstral mode). + * + * \return Number of int32_t output values produced this call (0 if not + * enough input data was available, or no error condition was + * detected). + */ +int mfcc_stft_process(struct processing_module *mod, struct mfcc_comp_data *cd) { + const struct comp_dev *dev = mod->dev; + 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; + bool vad_now; /* 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 +314,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 +327,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,87 +340,468 @@ 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; + + /* 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)); + } + + /* 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); + + /* 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); - /* 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); + /* Apply cepstral lifter */ + if (state->lifter.cepstral_lifter != 0) { + mat_multiply_elementwise(state->cepstral_coef, state->lifter.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; + } - cc_count += state->dct.num_out; + /* Use hop counter for frame numbering (independent of VAD enable) */ + state->header.frame_number = state->hop_count; - /* Output to sink buffer */ + /* Run VAD on the mel log spectrum (available in both modes) */ + if (config->enable_vad) { + mfcc_vad_update(&cd->vad, state->mel_log_32); + + /* Populate data header for this output frame */ + state->header.energy = cd->vad.energy; + state->header.noise_energy = cd->vad.noise_energy; + state->header.vad_flag = cd->vad.is_speech ? 1 : 0; + } + + /* Increment hop counter at end of hop processing */ + state->hop_count++; + + /* Send notification when VAD state changes */ + if (config->enable_vad && config->update_controls) { + vad_now = cd->vad.is_speech; + + if (vad_now != cd->vad_prev) { + mfcc_send_vad_notification(mod, vad_now ? 1 : 0); + cd->vad_prev = vad_now; + } + } } - /* TODO: This version handles only one FFT run per copy(). How to pass multiple - * cepstral coefficients sets return is an open. - */ return cc_count; } -#if CONFIG_FORMAT_S16LE -void mfcc_s16_default(struct processing_module *mod, struct input_stream_buffer *bsource, - struct output_stream_buffer *bsink, int frames) +/** + * \brief Write bytes into a possibly wrapped sink buffer. + * + * Caller must ensure max_bytes <= buf_size; otherwise the wrap branch + * would write past the end of the buffer. Returns 0 on bound violation. + */ +static size_t mfcc_sink_write_bytes(uint8_t **dst, uint8_t *buf_start, + size_t buf_size, const uint8_t *src, + size_t max_bytes) { - 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; - 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; - - /* 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. + uint8_t *buf_end = buf_start + buf_size; + size_t chunk; + + if (max_bytes == 0) + return 0; + + /* Guard: a single write must not exceed total sink buffer size. */ + if (max_bytes > buf_size) + return 0; + + chunk = MIN(max_bytes, (size_t)(buf_end - *dst)); + memcpy(*dst, src, chunk); + if (chunk < max_bytes) { + memcpy(buf_start, src + chunk, max_bytes - chunk); + *dst = buf_start + (max_bytes - chunk); + } else { + *dst += chunk; + if (*dst >= buf_end) + *dst = buf_start; + } + + return max_bytes; +} + +/** + * \brief Prepare the next MFCC output frame after STFT processing. + * + * Copies Mel (int32 Q9.23) or cepstral (int16 Q9.7 widened to int32 Q9.23) + * output into \c state->out_stage, sets up the read pointer for staged output, + * and marks the data header as pending. + * + * \param[in,out] state MFCC state. + * \param[in] num_ceps Number of int32_t values to publish; no-op if <= 0. + */ +static void mfcc_prepare_output(struct mfcc_state *state, int num_ceps) +{ + int k; + + if (num_ceps <= 0) + return; + + /* Copy into out_stage so the next STFT hop may freely reuse + * mel_log_32 / cepstral_coef while this frame is still pending. */ - num_ceps = mfcc_stft_process(mod->dev, state); + if (state->mel_only) { + for (k = 0; k < num_ceps; k++) + state->out_stage[k] = state->mel_log_32[k]; + } else { + /* Widen int16 Q9.7 cepstral coefficients to int32 Q9.23. */ + for (k = 0; k < num_ceps; k++) + state->out_stage[k] = (int32_t)state->cepstral_coef->data[k] << 16; + } + + state->out_data_ptr = state->out_stage; + state->out_remain = num_ceps; + state->header_pending = true; +} + +/** + * \brief Commit MFCC output in compress mode. + * + * Writes the pending data header (if any) followed by the pending + * int32_t Mel/cepstral payload into the sink as a contiguous blob, + * without zero padding. When VAD + DTX are enabled, suppresses silence + * frames after a configurable trailing-silence count and optionally + * sends periodic keep-alive silence updates. + * + * \param[in,out] mod Processing module. + * \param[in,out] cd MFCC component data. + * \param[in,out] sinks Sink array; only sinks[0] is used. + * \param[in] num_ceps Number of int32_t values prepared for this hop. + * + * \return 0 on success, -ENOSPC if the sink cannot accept the frame, + * or a negative error from the sink API. + */ +static int mfcc_output_compress(struct processing_module *mod, struct mfcc_comp_data *cd, + struct sof_sink **sinks, int num_ceps) +{ + struct mfcc_state *state = &cd->state; + size_t out_bytes; + size_t commit_bytes; + void *sink_ptr; + void *sink_start; + size_t sink_buf_size; + uint8_t *dst; + int ret; + bool pending; + bool send_periodic; + + pending = state->header_pending || state->out_remain > 0; + if (!pending) + return 0; + + if (num_ceps > 0 && cd->config->enable_vad && !cd->vad.is_speech) { + state->vad_silence_count++; + /* With DTX enabled, send trailing silence frames + * (configurable count) then suppress. After trailing + * frames, optionally send periodic silence updates + * at the configured interval. This gives the host + * enough silence to detect end-of-speech while + * keeping alive updates during long silence. + * Without DTX, output every frame regardless of VAD. + */ + if (cd->config->enable_dtx && + state->vad_silence_count > state->dtx_trailing_silence) { + send_periodic = false; + + /* Check periodic silence frame send */ + if (state->dtx_silence_interval > 0) { + state->dtx_silence_counter++; + if (state->dtx_silence_counter >= + state->dtx_silence_interval) { + state->dtx_silence_counter = 0; + send_periodic = true; + } + } + + if (!send_periodic) { + /* Suppress this frame */ + state->header_pending = false; + state->out_remain = 0; + return 0; + } + } + } else { + state->vad_silence_count = 0; + state->dtx_silence_counter = 0; + } + + out_bytes = (state->header_pending ? sizeof(state->header) : 0) + + state->out_remain * sizeof(int32_t); + if (out_bytes == 0) + return 0; - /* 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. + commit_bytes = out_bytes; + if (sink_get_free_size(sinks[0]) < commit_bytes) + return -ENOSPC; + + ret = sink_get_buffer(sinks[0], commit_bytes, &sink_ptr, + &sink_start, &sink_buf_size); + if (ret) + return ret; + + dst = sink_ptr; + if (state->header_pending) { + mfcc_sink_write_bytes(&dst, sink_start, sink_buf_size, + (uint8_t *)&state->header, + sizeof(state->header)); + } + + if (state->out_remain > 0) { + mfcc_sink_write_bytes(&dst, sink_start, sink_buf_size, + (uint8_t *)state->out_data_ptr, + state->out_remain * sizeof(int32_t)); + } + + ret = sink_commit_buffer(sinks[0], commit_bytes); + if (ret) + return ret; + + state->header_pending = false; + state->out_remain = 0; + return 0; +} + +/** + * \brief Commit MFCC output in legacy PCM mode. + * + * Acquires a sink period sized as \p frames audio frames, zero-fills it, + * then writes any pending data header and pending int32_t Mel/cepstral + * payload. Any payload that does not fit in the current period stays in + * \c state->out_data_ptr / \c state->out_remain for the next call. + * + * \param[in,out] mod Processing module. + * \param[in,out] cd MFCC component data. + * \param[in,out] sources Sources array (unused; reserved for symmetry). + * \param[in,out] sinks Sink array; only sinks[0] is used. + * \param[in] frames Number of audio frames the sink expects this period. + * + * \return 0 on success, -ENOSPC if the sink cannot accept the period, + * or a negative error from the sink API. + */ +static int mfcc_output_legacy(struct processing_module *mod, struct mfcc_comp_data *cd, + struct sof_source **sources, struct sof_sink **sinks, + int frames) +{ + struct mfcc_state *state = &cd->state; + size_t commit_bytes; + size_t bytes_to_end; + size_t avail; + size_t hdr_size; + size_t data_bytes; + size_t to_write; + void *sink_ptr; + void *sink_start; + size_t sink_buf_size; + uint8_t *dst; + int n32; + int ret; + + /* The MFCC sink is treated as an opaque byte container: the period + * carries an MFCC blob (header + int32 features), not PCM audio. + * Sizing the commit as sink_frame_bytes * frames keeps the period + * size matched to whatever the sink advertises (S16_LE / S24_4LE / + * S32_LE), so no format-specific conversion is needed. Any payload + * that does not fit in the current period is carried over via + * state->out_remain and drained in the next call(s). The host side + * decodes the bytes as MFCC features regardless of the sink's + * nominal PCM format, so non-S32 sinks (e.g. bench S16 topologies) + * remain supported. */ - zero_samples = frames * audio_stream_get_channels(sink); - if (num_ceps > 0) { - zero_samples -= num_ceps + 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); + commit_bytes = sink_get_frame_bytes(sinks[0]) * frames; + if (sink_get_free_size(sinks[0]) < commit_bytes) + return -ENOSPC; + + ret = sink_get_buffer(sinks[0], commit_bytes, &sink_ptr, + &sink_start, &sink_buf_size); + if (ret) + return ret; + + /* Zero-fill entire period first */ + bytes_to_end = (size_t)((uint8_t *)sink_start + sink_buf_size - + (uint8_t *)sink_ptr); + + if (bytes_to_end >= commit_bytes) { + memset(sink_ptr, 0, commit_bytes); + } else { + memset(sink_ptr, 0, bytes_to_end); + memset(sink_start, 0, commit_bytes - bytes_to_end); } - w_ptr = mfcc_sink_copy_zero_s16(sink, w_ptr, zero_samples); + dst = sink_ptr; + avail = commit_bytes; + + /* Write pending header */ + if (state->header_pending && avail > 0) { + hdr_size = sizeof(state->header); + + if (avail >= hdr_size) { + mfcc_sink_write_bytes(&dst, sink_start, sink_buf_size, + (uint8_t *)&state->header, hdr_size); + avail -= hdr_size; + state->header_pending = false; + } + } + + /* Write pending feature data (always int32) */ + if (state->out_remain > 0 && avail > 0) { + data_bytes = state->out_remain * sizeof(int32_t); + to_write = MIN(data_bytes, avail) & ~(size_t)3; + if (to_write > 0) { + mfcc_sink_write_bytes(&dst, sink_start, sink_buf_size, + (uint8_t *)state->out_data_ptr, + to_write); + n32 = to_write / sizeof(int32_t); + state->out_data_ptr += n32; + state->out_remain -= n32; + } + } + + return sink_commit_buffer(sinks[0], commit_bytes); } -#endif /* CONFIG_FORMAT_S16LE */ -#if CONFIG_FORMAT_S24LE -#endif /* CONFIG_FORMAT_S24LE */ +int mfcc_process_output(struct processing_module *mod, struct mfcc_comp_data *cd, + struct sof_source **sources, struct sof_sink **sinks, + int num_ceps, int frames) +{ + bool pending; -#if CONFIG_FORMAT_S32LE -#endif /* CONFIG_FORMAT_S32LE */ + pending = cd->state.header_pending || cd->state.out_remain > 0; + + if (cd->config->compress_output) { + /* Keep retrying pending frame first; don't overwrite pending state. */ + if (!pending && num_ceps > 0) + mfcc_prepare_output(&cd->state, num_ceps); + else if (pending) + num_ceps = 0; + + return mfcc_output_compress(mod, cd, sinks, num_ceps); + } + + /* Legacy PCM mode: same guard. Overwriting out_data_ptr / out_remain + * while a previous period's data is still pending would drop samples. + */ + if (!pending && num_ceps > 0) + mfcc_prepare_output(&cd->state, num_ceps); + + return mfcc_output_legacy(mod, cd, sources, sinks, frames); +} + +/** + * \brief Refill the FFT input scratch from the input circular buffer. + * + * Copies the saved overlap (\c state->prev_data) into the first samples + * of \c fft->fft_buf, appends one \c fft_hop_size of new data from the + * input circular buffer (consuming it), and finally saves the trailing + * window of the FFT input back to \c state->prev_data for the next hop. + * The caller is expected to have zeroed \c fft->fft_buf so that the + * complex imaginary parts remain 0. + * + * \param[in,out] state MFCC state. Input buffer pointers/counters and + * \c prev_data are updated; \c fft->fft_buf is filled. + */ +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; + } +} diff --git a/src/audio/mfcc/mfcc_generic.c b/src/audio/mfcc/mfcc_generic.c index 9384b16e0cee..65f4760a0ea3 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> @@ -8,8 +8,9 @@ #ifdef MFCC_GENERIC #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,53 +25,17 @@ * MFCC algorithm code */ -void mfcc_source_copy_s16(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; - int16_t *x0; - int16_t *x = audio_stream_get_rptr(source); - int16_t *w = buf->w_ptr; - int copied; - int nmax; - int n1; - int n2; - int n; - int i; - int num_channels = audio_stream_get_channels(source); - - /* Copy from source to pre-buffer for FFT. - * The pre-emphasis filter is done in this step. - */ - 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++) { - if (emph->enable) { - /* Q1.15 x Q1.15 -> Q2.30 */ - s = (int32_t)emph->delay * emph->coef + Q_SHIFT_LEFT(*x0, 15, 30); - *w = sat_int16(Q_SHIFT_RND(s, 30, 15)); - emph->delay = *x0; - } else { - *w = *x0; - } - x0 += num_channels; - w++; - } - - x = audio_stream_wrap(source, x + n * audio_stream_get_channels(source)); - w = mfcc_buffer_wrap(buf, w); - } - buf->s_avail += copied; - buf->s_free -= copied; - buf->w_ptr = w; -} - +/** + * \brief Generic-C copy of the overlap window from the input circular buffer. + * + * Copies \p prev_data_length samples from the front of \p buf into + * \p prev_data and advances the read pointer. Used once on first frame + * to seed the overlap region for subsequent STFT hops. + * + * \param[in,out] buf Input circular buffer (Q1.15 mono). + * \param[out] prev_data Destination overlap buffer. + * \param[in] prev_data_length Number of samples to copy. + */ void mfcc_fill_prev_samples(struct mfcc_buffer *buf, int16_t *prev_data, int prev_data_length) { @@ -96,144 +61,28 @@ void mfcc_fill_prev_samples(struct mfcc_buffer *buf, int16_t *prev_data, buf->r_ptr = r; } -void mfcc_fill_fft_buffer(struct mfcc_state *state) -{ - struct mfcc_buffer *buf = &state->buf; - struct mfcc_fft *fft = &state->fft; - int16_t *r = buf->r_ptr; - int copied; - int nmax; - int idx = fft->fft_fill_start_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[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); - n = MIN(n, nmax); - for (j = 0; j < n; j++) { - fft->fft_buf[idx].real = *r; - r++; - idx++; - } - 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; - } - - shift = norm_int32(smax << 15) - 1; /* 16 bit data */ - shift = MAX(shift, 0); - shift = MIN(shift, MFCC_NORMALIZE_MAX_SHIFT); - return shift; -} -#endif - +/** + * \brief Generic-C window function application on the FFT input buffer. + * + * Multiplies the real part of \c fft->fft_buf (sized \c fft->fft_size, + * Q1.15 input upcast to int32) by \c state->window (Q1.15) in-place and + * left-shifts by \p input_shift + 1 to produce Q1.31 fixed-point input + * to the FFT. + * + * \param[in,out] state MFCC state; \c state->fft.fft_buf is updated in-place. + * \param[in] input_shift Additional left shift applied to the windowed sample. + */ 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; -#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 */ - - 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) -{ - int copied; - int nmax; - int i; - 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; - - 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 = *r_ptr; - r_ptr++; - w_ptr++; - } - - w_ptr = audio_stream_wrap(sink, w_ptr); - } - - return w_ptr; } -#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..fdf8e47d2deb 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> @@ -9,8 +9,9 @@ #ifdef MFCC_HIFI3 #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,64 +34,16 @@ static inline void set_circular_buf0(const void *start, const void *end) * MFCC algorithm code */ -void mfcc_source_copy_s16(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 num_channels = audio_stream_get_channels(source); - ae_int16 *in; - ae_int16 *x = (ae_int16 *)audio_stream_get_rptr(source); - ae_int16 *out = (ae_int16 *)buf->w_ptr; - ae_int16x4 sample; - ae_int32x2 temp; - ae_int16x4 coef = emph->coef; - ae_int16x4 delay; - const int in_inc = sizeof(ae_int16) * num_channels; - - /* Copy from source to pre-buffer for FFT. - * The pre-emphasis filter is done in this step. - */ - 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_L16_XP(sample, in, in_inc); - /* Q1.15 -> Q1.31 */ - temp = AE_CVT32X2F16_10(sample); - AE_MULAF16SS_00(temp, delay, coef); - delay = sample; - sample = AE_ROUND16X4F32SSYM(temp, temp); - /* 2 = sizeof(ae_int16)*/ - AE_S16_0_IP(sample, out, 2); - } - emph->delay = delay; - - } else { - for (i = 0; i < n; i++) { - AE_L16_XP(sample, in, in_inc); - /* 2 = sizeof(ae_int16)*/ - AE_S16_0_IP(sample, out, 2); - } - } - - 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; -} - +/** + * \brief HiFi3 copy of the overlap window from the input circular buffer. + * + * HiFi3 implementation equivalent to the generic mfcc_fill_prev_samples(); + * uses xtensa circular addressing for the source pointer. + * + * \param[in,out] buf Input circular buffer (Q1.15 mono). + * \param[out] prev_data Destination overlap buffer. + * \param[in] prev_data_length Number of samples to copy. + */ void mfcc_fill_prev_samples(struct mfcc_buffer *buf, int16_t *prev_data, int prev_data_length) { @@ -124,72 +77,16 @@ 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 +/** + * \brief HiFi3 window function application on the FFT input buffer. + * + * HiFi3 implementation equivalent to the generic mfcc_apply_window(); + * uses fractional multiply with saturating shift to produce Q1.31 + * FFT input data. + * + * \param[in,out] state MFCC state; \c state->fft.fft_buf is updated in-place. + * \param[in] input_shift Additional left shift applied to the windowed sample. + */ void mfcc_apply_window(struct mfcc_state *state, int input_shift) { struct mfcc_fft *fft = &state->fft; @@ -200,92 +97,18 @@ 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) -{ - 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); - - 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); - - return (int16_t *)out; -} - -int16_t *mfcc_sink_copy_data_s16(const struct audio_stream *sink, int16_t *w_ptr, - int samples, int16_t *r_ptr) -{ - 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); - - 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); - } - - return (int16_t *)out; } -#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..e86d066b408b 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> @@ -9,8 +9,9 @@ #ifdef MFCC_HIFI4 #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> @@ -29,63 +30,21 @@ static inline void set_circular_buf0(const void *start, const void *end) AE_SETCEND0(end); } -/* Setup circular for buffer 1 */ -static inline void set_circular_buf1(const void *start, const void *end) -{ - AE_SETCBEGIN1(start); - AE_SETCEND1(end); -} - /* * MFCC algorithm code */ -void mfcc_source_copy_s16(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_int16 *in = (ae_int16 *)source->r_ptr + source_channel; - ae_int16 *out = (ae_int16 *)buf->w_ptr; - ae_int16x4 sample; - ae_int32x2 temp; - ae_int16x4 coef; - ae_int16x4 delay; - const int in_inc = sizeof(ae_int16) * num_channels; - const int out_inc = sizeof(ae_int16); - int i; - - set_circular_buf1(buf->addr, buf->end_addr); - set_circular_buf0(source->addr, source->end_addr); - - /* Copy from source to pre-buffer for FFT. - * The pre-emphasis filter is done in this step. - */ - if (emph->enable) { - delay = emph->delay; - coef = emph->coef; - for (i = 0; i < frames; i++) { - AE_L16_XC(sample, in, in_inc); - - /* 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_L16_XC(sample, in, in_inc); - AE_S16_0_XC1(sample, out, out_inc); - } - } - - buf->s_avail += frames; - buf->s_free -= frames; - buf->w_ptr = (int16_t *)out; -} +/** + * \brief HiFi4 copy of the overlap window from the input circular buffer. + * + * HiFi4/HiFi5 implementation equivalent to the generic + * mfcc_fill_prev_samples(); uses xtensa circular addressing for the + * source pointer. + * + * \param[in,out] buf Input circular buffer (Q1.15 mono). + * \param[out] prev_data Destination overlap buffer. + * \param[in] prev_data_length Number of samples to copy. + */ void mfcc_fill_prev_samples(struct mfcc_buffer *buf, int16_t *prev_data, int prev_data_length) { @@ -119,73 +78,16 @@ 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 - +/** + * \brief HiFi4 window function application on the FFT input buffer. + * + * HiFi4/HiFi5 implementation equivalent to the generic + * mfcc_apply_window(); uses fractional multiply with saturating shift to + * produce Q1.31 FFT input data. + * + * \param[in,out] state MFCC state; \c state->fft.fft_buf is updated in-place. + * \param[in] input_shift Additional left shift applied to the windowed sample. + */ void mfcc_apply_window(struct mfcc_state *state, int input_shift) { struct mfcc_fft *fft = &state->fft; @@ -196,92 +98,18 @@ 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) -{ - 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); - - 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); - - return (int16_t *)out; -} - -int16_t *mfcc_sink_copy_data_s16(const struct audio_stream *sink, int16_t *w_ptr, - int samples, int16_t *r_ptr) -{ - 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); - - 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); - } - - return (int16_t *)out; } -#endif /* CONFIG_FORMAT_S16LE */ -#endif +#endif /* MFCC_HIFI4 */ diff --git a/src/audio/mfcc/mfcc_ipc4.c b/src/audio/mfcc/mfcc_ipc4.c new file mode 100644 index 000000000000..bb20d85e413b --- /dev/null +++ b/src/audio/mfcc/mfcc_ipc4.c @@ -0,0 +1,140 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// Copyright(c) 2026 Intel Corporation. +// +// Author: Seppo Ingalsuo <seppo.ingalsuo@linux.intel.com> + +/** + * \file mfcc_ipc4.c + * \brief IPC4-specific functions for MFCC component. + * + * Provides VAD switch control notification to user space via the + * IPC4 module notification mechanism. + */ + +#include <sof/audio/mfcc/mfcc_comp.h> +#include <sof/audio/module_adapter/module/generic.h> +#include <sof/audio/component.h> +#include <sof/ipc/msg.h> +#include <sof/trace/trace.h> +#include <ipc4/base-config.h> +#include <ipc4/header.h> +#include <ipc4/module.h> +#include <ipc4/notification.h> +#include <rtos/string.h> +#include <errno.h> +#include <stdint.h> + +LOG_MODULE_DECLARE(mfcc, CONFIG_SOF_LOG_LEVEL); + +/** + * \brief Initialize IPC notification message for VAD switch control. + * + * Allocates and configures the IPC message used to send VAD state + * change notifications to user space via a switch control. + */ +int mfcc_ipc_notification_init(struct processing_module *mod) +{ + struct mfcc_comp_data *cd = module_get_private_data(mod); + struct ipc_msg msg_proto; + struct comp_dev *dev = mod->dev; + 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 sof_ipc4_control_msg_payload *msg_payload; + + 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; + cd->msg = ipc_msg_w_ext_init(msg_proto.header, msg_proto.extension, + sizeof(struct sof_ipc4_notify_module_data) + + sizeof(struct sof_ipc4_control_msg_payload) + + sizeof(struct sof_ipc4_ctrl_value_chan)); + if (!cd->msg) { + comp_err(dev, "Failed to initialize VAD notification"); + return -ENOMEM; + } + + msg_module_data = (struct sof_ipc4_notify_module_data *)cd->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_ALSA_MAGIC_VAL | + SOF_IPC4_SWITCH_CONTROL_PARAM_ID; + msg_module_data->event_data_size = sizeof(struct sof_ipc4_control_msg_payload) + + sizeof(struct sof_ipc4_ctrl_value_chan); + + msg_payload = (struct sof_ipc4_control_msg_payload *)msg_module_data->event_data; + msg_payload->id = MFCC_CTRL_INDEX_VAD; + msg_payload->num_elems = 1; + msg_payload->chanv[0].channel = 0; + + comp_dbg(dev, "VAD notification init: instance_id = 0x%08x, module_id = 0x%08x", + msg_module_data->instance_id, msg_module_data->module_id); + return 0; +} + +/** + * \brief Send VAD switch control notification to user space. + * \param mod Processing module. + * \param val VAD value: 1 = speech, 0 = silence. + */ +void mfcc_send_vad_notification(struct processing_module *mod, uint32_t val) +{ + struct mfcc_comp_data *cd = module_get_private_data(mod); + struct sof_ipc4_notify_module_data *msg_module_data; + struct sof_ipc4_control_msg_payload *msg_payload; + + if (!cd->msg) + return; + + msg_module_data = (struct sof_ipc4_notify_module_data *)cd->msg->tx_data; + msg_payload = (struct sof_ipc4_control_msg_payload *)msg_module_data->event_data; + msg_payload->chanv[0].value = val; + ipc_msg_send(cd->msg, NULL, false); +} + +int mfcc_get_config(struct processing_module *mod, + uint32_t config_id, uint32_t *data_offset_size, + uint8_t *fragment, size_t fragment_size) +{ + struct sof_ipc_ctrl_data *cdata = (struct sof_ipc_ctrl_data *)fragment; + struct mfcc_comp_data *cd = module_get_private_data(mod); + struct sof_ipc4_control_msg_payload *ctl; + + comp_info(mod->dev, "entry"); + + switch (config_id) { + case SOF_IPC4_SWITCH_CONTROL_PARAM_ID: + ctl = (struct sof_ipc4_control_msg_payload *)fragment; + if (ctl->id == MFCC_CTRL_INDEX_VAD && ctl->num_elems == 1) { + ctl->chanv[0].value = cd->vad_prev ? 1 : 0; + *data_offset_size = sizeof(*ctl) + sizeof(ctl->chanv[0]); + return 0; + } + return -EINVAL; + default: + return comp_data_blob_get_cmd(cd->model_handler, cdata, fragment_size); + } +} + +int mfcc_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) +{ + struct mfcc_comp_data *cd = module_get_private_data(mod); + + comp_info(mod->dev, "entry"); + + switch (config_id) { + case SOF_IPC4_SWITCH_CONTROL_PARAM_ID: + /* VAD switch is read-only, ignore set requests */ + return 0; + default: + return comp_data_blob_set(cd->model_handler, pos, data_offset_size, + fragment, fragment_size); + } +} diff --git a/src/audio/mfcc/mfcc_setup.c b/src/audio/mfcc/mfcc_setup.c index 802ea058bb6f..1d062b2a5206 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> @@ -16,6 +18,8 @@ #include <stddef.h> #include <stdint.h> +#include <sof/audio/mfcc/mfcc_vad.h> + /* Definitions for cepstral lifter */ #define PI_Q23 Q_CONVERT_FLOAT(3.1415926536, 23) #define TWO_PI_Q23 Q_CONVERT_FLOAT(6.2831853072, 23) @@ -23,6 +27,13 @@ LOG_MODULE_REGISTER(mfcc_setup, CONFIG_SOF_LOG_LEVEL); +/** + * \brief Initialize an MFCC input circular buffer descriptor. + * + * \param[out] buf Circular buffer descriptor to initialize. + * \param[in] base Backing storage (Q1.15 samples). + * \param[in] size Buffer length in samples. + */ static void mfcc_init_buffer(struct mfcc_buffer *buf, int16_t *base, int size) { buf->addr = base; @@ -34,6 +45,15 @@ static void mfcc_init_buffer(struct mfcc_buffer *buf, int16_t *base, int size) buf->s_length = size; } +/** + * \brief Fill \c state->window with the configured analysis window. + * + * \param[in,out] state MFCC state; \c state->window is written, sized + * \c state->fft.fft_size and in Q1.15. + * \param[in] name Window type identifier from the MFCC configuration. + * + * \return 0 on success, -EINVAL for an unsupported window type. + */ static int mfcc_get_window(struct mfcc_state *state, enum sof_mfcc_fft_window_type name) { struct mfcc_fft *fft = &state->fft; @@ -48,10 +68,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 +87,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 +97,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; @@ -97,6 +119,24 @@ static int mfcc_get_cepstral_lifter(struct mfcc_cepstral_lifter *cl) /* TODO mfcc 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. */ +/** + * \brief Allocate and initialize MFCC runtime state from the config blob. + * + * Validates the supplied configuration, allocates the input circular + * buffer, overlap buffer, window vector, FFT scratch, Mel filterbank, + * DCT/cepstral lifter matrices (when \c num_ceps > 0) and VAD state + * (when enabled). All allocations are freed on any failure path. + * + * \param[in,out] mod Processing module that owns the MFCC instance. + * \param[in] max_frames Maximum input frames the pipeline will deliver + * per call; sizes the input buffer. + * \param[in] sample_rate Input sample rate in Hz; must match + * \c config->sample_frequency. + * \param[in] channels Number of channels in the input stream. + * + * \return 0 on success or a negative error code (\c -EINVAL for bad + * configuration, \c -ENOMEM for an allocation failure). + */ int mfcc_setup(struct processing_module *mod, int max_frames, int sample_rate, int channels) { struct mfcc_comp_data *cd = module_get_private_data(mod); @@ -106,9 +146,13 @@ int mfcc_setup(struct processing_module *mod, int max_frames, int sample_rate, i struct mfcc_fft *fft = &state->fft; struct psy_mel_filterbank *fb = &state->melfb; struct dct_plan_16 *dct = &state->dct; + int mel_log_32_space; + int max_out_per_hop; + int out_per_hop; + int sink_per_hop; int ret; - comp_dbg(dev, "mfcc_setup()"); + comp_dbg(dev, "entry"); /* Check size */ if (config->size != sizeof(struct sof_mfcc_config)) { @@ -123,11 +167,48 @@ int mfcc_setup(struct processing_module *mod, int max_frames, int sample_rate, i return -EINVAL; } + if (sample_rate <= 0 || sample_rate > MFCC_MAX_SAMPLE_RATE) { + comp_err(dev, "Sample rate %d out of range (1..%d Hz)", + sample_rate, MFCC_MAX_SAMPLE_RATE); + return -EINVAL; + } + if (config->sample_frequency != sample_rate) { comp_err(dev, "Config sample_frequency does not match stream"); return -EINVAL; } + /* Validate configuration parameters up front so that runtime code + * (executed every hop in the DSP) can skip divide-by-zero / range + * checks. Any later division by these fields is safe after this. + */ + if (config->frame_length <= 0 || config->frame_shift <= 0 || + config->frame_shift > config->frame_length) { + comp_err(dev, "Illegal frame_length %d or frame_shift %d", + config->frame_length, config->frame_shift); + return -EINVAL; + } + + if (config->num_mel_bins <= 0) { + comp_err(dev, "Illegal num_mel_bins %d", config->num_mel_bins); + return -EINVAL; + } + + if (config->num_ceps < 0) { + comp_err(dev, "Illegal num_ceps %d", config->num_ceps); + return -EINVAL; + } + + /* cepstral_lifter is used as a divisor in mfcc_get_cepstral_lifter(). + * Either disabled (== 0) or strictly positive. Negative values are + * rejected to keep runtime math (every hop in DSP) free of guards. + */ + if (config->num_ceps > 0 && config->cepstral_lifter < 0) { + comp_err(dev, "Illegal cepstral_lifter %d (must be >= 0)", + config->cepstral_lifter); + return -EINVAL; + } + cd->max_frames = max_frames; state->sample_rate = sample_rate; state->low_freq = config->low_freq; @@ -137,10 +218,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 +229,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 +237,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 +249,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 +264,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 +282,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 +318,61 @@ 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*/ + if (state->lifter.cepstral_lifter > 0) { + ret = mfcc_get_cepstral_lifter(mod, &state->lifter); + if (ret < 0) { + comp_err(dev, "Failed cepstral lifter"); + goto free_dct_matrix; + } + } else { + state->lifter.matrix = NULL; + } + + 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 +382,152 @@ 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 */ + 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. + */ + 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 + header int32_t output values. The sink provides + * at least fft_hop_size * channels int32_t samples per hop (worst case s32). + * If output exceeds this, data accumulates and will eventually overflow. + * This check is not needed in compress output mode where only actual data + * bytes are committed without zero padding. + */ + out_per_hop = max_out_per_hop + sizeof(state->header) / sizeof(int32_t); + sink_per_hop = fft->fft_hop_size * channels; + + if (!config->compress_output && out_per_hop > sink_per_hop) { + comp_err(dev, "Output %d int32 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; + } + + /* Staging buffer for pending output. Decouples committed data from + * the FFT scratch (mel_log_32) so the next STFT hop can run while a + * previous frame is still being drained across multiple periods. + */ + state->out_stage_size = max_out_per_hop; + state->out_stage = mod_zalloc(mod, max_out_per_hop * sizeof(int32_t)); + if (!state->out_stage) { + comp_err(dev, "Failed output staging buffer allocate"); + ret = -ENOMEM; + goto free_lifter; + } /* Set initial state for STFT */ state->waiting_fill = true; state->prev_samples_valid = false; + state->header_pending = false; + state->hop_count = 0; + memset(&state->header, 0, sizeof(state->header)); + state->header.magic = MFCC_MAGIC; + state->out_data_ptr = NULL; + state->out_remain = 0; + state->vad_silence_count = 0; + state->dtx_trailing_silence = config->dtx_trailing_silence_hops; + state->dtx_silence_interval = config->dtx_silence_hops_interval; + state->dtx_silence_counter = 0; + + if (config->enable_vad) { + ret = mfcc_vad_init(&cd->vad, config->num_mel_bins, sample_rate, mod); + if (ret < 0) { + comp_err(dev, "Failed VAD init"); + goto free_out_stage; + } + } - comp_dbg(dev, "mfcc_setup(), done"); + comp_dbg(dev, "done"); return 0; +free_out_stage: + mod_free(mod, state->out_stage); + +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) +/** + * \brief Free a single MFCC allocation and clear the caller's pointer. + * + * \param[in,out] mod Processing module owning the allocation. + * \param[in,out] ptr Address of the pointer to free; set to NULL on return. + */ +static void mfcc_free_and_null(struct processing_module *mod, void **ptr) +{ + mod_free(mod, *ptr); + *ptr = NULL; +} + +/* Free MFCC buffers to prevent leaks on reset->prepare cycles. + * mfcc_free_buffers() NULLs the pointers after free. + */ +/** + * \brief Release all MFCC runtime allocations. + * + * Frees FFT plan, scratch buffers, input/overlap buffers, Mel + * filterbank data, DCT matrix, cepstral lifter matrix, and VAD state. + * All freed pointers are set to NULL so the function is safe to call + * across repeated reset/prepare cycles. + * + * \param[in,out] mod Processing module that owns the MFCC instance. + */ +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); + cd->state.fft.fft_plan = NULL; + mfcc_free_and_null(mod, (void **)&cd->state.fft.fft_buf); + mfcc_free_and_null(mod, (void **)&cd->state.fft.fft_out); + mfcc_free_and_null(mod, (void **)&cd->state.buffers); + mfcc_free_and_null(mod, (void **)&cd->state.melfb.data); + mfcc_free_and_null(mod, (void **)&cd->state.dct.matrix); + mfcc_free_and_null(mod, (void **)&cd->state.lifter.matrix); + mfcc_free_and_null(mod, (void **)&cd->state.out_stage); + mfcc_free_and_null(mod, (void **)&cd->vad.noise_floor); + mfcc_free_and_null(mod, (void **)&cd->vad.weights); } diff --git a/src/audio/mfcc/mfcc_vad.c b/src/audio/mfcc/mfcc_vad.c new file mode 100644 index 000000000000..f44a89a7dea3 --- /dev/null +++ b/src/audio/mfcc/mfcc_vad.c @@ -0,0 +1,229 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// Copyright(c) 2026 Intel Corporation. +// +// Author: Seppo Ingalsuo <seppo.ingalsuo@linux.intel.com> + +/** + * \file mfcc_vad.c + * \brief Voice Activity Detection based on Mel spectrum energy. + * + * Implements a VAD that tracks per-bin noise floor and computes a + * speech-frequency weighted energy above the floor. Speech is declared + * when the weighted delta exceeds a threshold, with hangover to prevent + * rapid toggling. + */ + +#include <sof/audio/mfcc/mfcc_vad.h> + +#include <sof/audio/component.h> +#include <sof/audio/format.h> +#include <sof/audio/module_adapter/module/generic.h> +#include <sof/math/auditory.h> +#include <sof/trace/trace.h> +#include <errno.h> +#include <stddef.h> + +LOG_MODULE_DECLARE(mfcc, CONFIG_SOF_LOG_LEVEL); + +/** + * \brief A-weighting table: 1/3 octave band center frequencies in Hz (Q16.0). + * + * From IEC 61672-1:2013, source: + * https://acousticalengineer.com/a-weighting-table/ + */ +#define A_WEIGHT_TABLE_SIZE 36 + +static const int16_t a_weight_hz[A_WEIGHT_TABLE_SIZE] = { + 6, 8, 10, 13, 16, 20, 25, 32, + 40, 50, 63, 80, 100, 125, 160, 200, + 250, 315, 400, 500, 630, 800, 1000, 1250, + 1600, 2000, 2500, 3150, 4000, 5000, 6300, 8000, + 10000, 12500, 16000, 20000, +}; + +/** + * \brief A-weighting linear amplitude, scaled so peak (at 2500 Hz) maps + * to INT16_MAX (32767). Original dB values converted via + * 10^(dB/20) then scaled by 32767 / max. + */ +static const int16_t a_weight_lin[A_WEIGHT_TABLE_SIZE] = { + 2, 4, 9, 19, 43, 85, 162, 299, + 531, 862, 1382, 2140, 3129, 4370, 6172, 8136, + 10362, 13196, 16234, 19518, 22669, 25730, 28212, 30230, + 31655, 32392, 32767, 32392, 31655, 30230, 27889, 24856, + 21156, 17196, 13045, 9670, +}; + +/** + * \brief Compute A-weighted speech-frequency emphasis weights for Mel bins. + * + * Weights are computed by linearly interpolating the A-weighting table + * at each Mel bin center frequency. Output weights are in Q1.15 and + * sum to approximately 2^15. + * + * \param[out] weights Output weight array. + * \param[in] num_mel Number of Mel bins. + * \param[in] sample_rate Sample rate in Hz. + */ +static void mfcc_vad_build_weights(int16_t *weights, int num_mel, int32_t sample_rate) +{ + int32_t scaled, num; + int32_t sum = 0; + int16_t f_hz, f0, f1, w, w0, w1, den; + int16_t mel_end = psy_hz_to_mel((int16_t)(sample_rate / 2)); /* Nyquist (max 32767 Hz) in Mel */ + int16_t mel_step = mel_end / (num_mel + 1); + int i, j; + + if (!num_mel) + return; + + for (i = 0; i < num_mel; i++) { + f_hz = psy_mel_to_hz((int16_t)((i + 1) * mel_step)); + + /* Find the table interval containing f_hz and interpolate */ + if (f_hz <= a_weight_hz[0]) { + w = a_weight_lin[0]; + } else if (f_hz >= a_weight_hz[A_WEIGHT_TABLE_SIZE - 1]) { + w = a_weight_lin[A_WEIGHT_TABLE_SIZE - 1]; + } else { + /* Find j such that a_weight_hz[j] <= f_hz < a_weight_hz[j+1] */ + for (j = 0; j < A_WEIGHT_TABLE_SIZE - 2; j++) { + if (f_hz < a_weight_hz[j + 1]) + break; + } + + /* Linear interpolation: w = w0 + (w1 - w0) * (f - f0) / (f1 - f0) */ + f0 = a_weight_hz[j]; + f1 = a_weight_hz[j + 1]; + w0 = a_weight_lin[j]; + w1 = a_weight_lin[j + 1]; + num = (int32_t)(w1 - w0) * (f_hz - f0); + den = f1 - f0; + w = w0 + (int16_t)(num / den); + } + + weights[i] = w; + sum += w; + } + + /* Normalize weights so they sum to 1.0 */ + for (i = 0; i < num_mel; i++) { + scaled = ((int32_t)weights[i] << 16) / sum; /* Q1.16 */ + weights[i] = (int16_t)Q_SHIFT_RND(scaled, 16, 15); /* Round to Q1.15 */ + } +} + +int mfcc_vad_init(struct mfcc_vad_state *vad, int num_mel_bins, int32_t sample_rate, + struct processing_module *mod) +{ + if (!vad) + return -EINVAL; + + if (num_mel_bins <= 0) + return -EINVAL; + + vad->num_mel_bins = num_mel_bins; + vad->energy_threshold = MFCC_VAD_ENERGY_THRESHOLD; + vad->noise_rise_alpha_slow = MFCC_VAD_NOISE_RISE_ALPHA; + vad->noise_rise_alpha_fast = MFCC_VAD_NOISE_RISE_ALPHA_FAST; + vad->hangover_max = MFCC_VAD_HANGOVER_FRAMES; + vad->hangover_counter = 0; + vad->init_frames = MFCC_VAD_NOISE_INIT_FRAMES; + vad->frame_count = 0; + vad->is_speech = false; + vad->initialized = false; + + /* Allocate per-bin noise floor */ + vad->noise_floor = mod_zalloc(mod, num_mel_bins * sizeof(int32_t)); + if (!vad->noise_floor) + return -ENOMEM; + + /* Allocate and compute per-bin weights */ + vad->weights = mod_zalloc(mod, num_mel_bins * sizeof(int16_t)); + if (!vad->weights) { + mod_free(mod, vad->noise_floor); + vad->noise_floor = NULL; + return -ENOMEM; + } + + mfcc_vad_build_weights(vad->weights, num_mel_bins, sample_rate); + return 0; +} + +int mfcc_vad_update(struct mfcc_vad_state *vad, const int32_t *mel_log) +{ + int64_t signal_energy = 0; + int64_t noise_energy = 0; + int64_t energy_delta = 0; + int32_t delta; + int32_t p; + int16_t alpha; + int i; + + if (!vad || !mel_log) + return 0; + + /* Stop incrementing after init phase to avoid wrap-around restarting fast alpha. + * Select rise alpha based on convergence phase. + */ + if (vad->frame_count < vad->init_frames) { + vad->frame_count++; + alpha = vad->noise_rise_alpha_fast; + } else { + alpha = vad->noise_rise_alpha_slow; + } + + /* Initialize noise floor to first frame */ + if (!vad->initialized) { + for (i = 0; i < vad->num_mel_bins; i++) + vad->noise_floor[i] = mel_log[i]; + + vad->initialized = true; + } + + /* Update noise floor: follow down instantly, rise slowly */ + for (i = 0; i < vad->num_mel_bins; i++) { + if (mel_log[i] < vad->noise_floor[i]) { + /* Instant follow-down */ + vad->noise_floor[i] = mel_log[i]; + } else { + /* Slow rise: floor += alpha * (mel - floor) + * Q9.23 + Q1.15 * Q9.23 => need Q9.23 result + * alpha is Q1.15, delta is Q9.23 + */ + delta = mel_log[i] - vad->noise_floor[i]; + p = (int32_t)Q_MULTSR_32X32((int64_t)alpha, delta, 15, 23, 23); + vad->noise_floor[i] += p; + } + } + + /* Compute weighted signal energy and noise floor energy. + * weights are Q1.15, mel values are Q9.23 + * Products are Q10.38, accumulate in int64_t then shift to Q9.23 + */ + + for (i = 0; i < vad->num_mel_bins; i++) { + signal_energy += (int64_t)vad->weights[i] * mel_log[i]; + noise_energy += (int64_t)vad->weights[i] * vad->noise_floor[i]; + } + + vad->energy = sat_int32(Q_SHIFT_RND(signal_energy, 38, 23)); + vad->noise_energy = sat_int32(Q_SHIFT_RND(noise_energy, 38, 23)); + energy_delta = vad->energy - vad->noise_energy; + + /* Round accumulated energy from Q10.38 to Q9.23, saturate to int32 */ + if (energy_delta > vad->energy_threshold) { + vad->hangover_counter = vad->hangover_max; + vad->is_speech = true; + } else { + if (vad->hangover_counter > 0) { + vad->hangover_counter--; + vad->is_speech = true; + } else { + vad->is_speech = false; + } + } + + return vad->is_speech ? 1 : 0; +} diff --git a/src/audio/mfcc/tune/README.md b/src/audio/mfcc/tune/README.md new file mode 100644 index 000000000000..422f849b56b4 --- /dev/null +++ b/src/audio/mfcc/tune/README.md @@ -0,0 +1,259 @@ +# SOF MFCC Tuning Tools + +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. + +## Testbench run + +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 built 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: + +| File | Content | +|------|---------| +| `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_`: + +| File | Content | +|------|---------| +| `xt_mfcc_s16.raw`, `xt_mfcc_s24.raw`, `xt_mfcc_s32.raw` | Cepstral coefficients | +| `xt_mel_s16.raw`, `xt_mel_s24.raw`, `xt_mel_s32.raw` | Mel spectrogram | + +## Decoding and Plotting + +All output files can be decoded and plotted at once in Matlab or Octave +with the `decode_all.m` script: + +```matlab +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: + +```matlab +[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: + +```matlab +[mel, t, n] = decode_mel('mel_s16.raw', 80); +``` + +## Live Whisper Transcription with DSP VAD + +The directory contains a Python script `sof_mel_to_text_live_dsp_vad.py`. +It can be used with development topologies +`sof-arl-cs42l43-l0-cs35l56-l23-mfcc-mel-normal.tplg` and +`sof-mtl-rt713-l0-rt1316-l12-mfcc-mel-normal.tplg`. + +It captures from default audio device `hw:0,47` (headset microphone PCM). +The Mel audio features and VAD flags are packed to be compatible with a +normal PCM stream. The captured frames with detected speech are sent to the +Whisper speech recognizer model for conversion to text. + +The more efficient method for audio features capture uses the compress PCM without +continuous redundant data. Such MFCC development topologies are: + +- `sof-mtl-rt713-l0-rt1316-l12-mfcc-mel-compr.tplg` +- `sof-mtl-rt713-l0-rt1316-l12-mfcc-ceps-compr.tplg` +- `sof-arl-cs42l43-l0-cs35l56-l23-mfcc-mel-compr.tplg` +- `sof-arl-cs42l43-l0-cs35l56-l23-mfcc-ceps-compr.tplg` + +E.g. the script `sof_mel_spectrogram_compress.py` uses the mfcc-mel-compr topology version. + +### Prerequisites + +The script needs OpenVINO. Please follow the install procedure from +<https://docs.openvino.ai/2025/get-started/install-openvino.html>. + +The following Python pip installs are needed into the same OpenVINO venv. + +```bash +pip install openvino openvino-tokenizers openvino-genai +pip install optimum[intel] +pip install transformers +pip install huggingface_hub +``` + +The real-time spectrogram viewers in this directory use GTK 4; their setup is described +under [Live Spectrogram Viewers](#live-spectrogram-viewers) below. + +### NPU / GPU Support + +The script by default runs the Whisper encoder model in the NPU. To +use the NPU, install the driver from +<https://github.com/intel/linux-npu-driver/releases>. If the NPU is not +available, change the encoder to CPU with run option `--encoder-device CPU`. +With a GPU both `--encoder-device GPU` and `--decoder-device GPU` can be set. + +### Example run + +Check which capture devices are available. + +```bash +arecord -l +``` + +In this example the devices hw:0,47 and hw:0,48 support the audio +features stream. + +```bash +**** List of CAPTURE Hardware Devices **** +card 0: sofsoundwire [sof-soundwire], device 1: Jack In (*) [] + Subdevices: 1/1 + Subdevice #0: subdevice #0 +card 0: sofsoundwire [sof-soundwire], device 4: Microphone (*) [] + Subdevices: 1/1 + Subdevice #0: subdevice #0 +card 0: sofsoundwire [sof-soundwire], device 47: Jack In Audio Features (*) [] + Subdevices: 1/1 + Subdevice #0: subdevice #0 +card 0: sofsoundwire [sof-soundwire], device 48: Microphone Audio Features (*) [] + Subdevices: 1/1 + Subdevice #0: subdevice #0 +``` + +With Whisper model run the CPU and with internal microphones of laptop +the run command is: + +```bash +python3 sof_mel_to_text_live_dsp_vad.py --encoder-device CPU --device hw:0,48 +``` + +The script run output is shown below + +```bash +=== Live SOF Mel → Whisper Transcription (DSP VAD) === + +Starting capture: arecord -D hw:0,48 -f S32_LE -c 2 -r 16000 -t raw --buffer-size 8192 +VAD source: DSP (embedded in stream) +Silence trigger: 100ms (10 frames) +Whisper model: whisper-medium-int4-ov (encoder: CPU, decoder: CPU) + + [ 0.01s] SILENCE + [ 1.39s] SPEECH + [ 2.57s] SILENCE + [ 2.66s] Transcribing 118 frames (1.2s)... + [Whisper] encoder: 1.30s + [Whisper] decoder: 0.59s (3 tokens) + + >> "Hello computer" +``` +## Live Whisper Transcription with Compress PCM + +The `sof_mel_to_text_live_compress.py` script captures Mel spectrogram +frames from a SOF compress PCM device and performs live Whisper +transcription using OpenVINO. Unlike `sof_mel_to_text_live_dsp_vad.py` +which uses `arecord`, this script reads directly from the compress PCM +device with discontinuous frames handling. + +The same OpenVINO prerequisites and pip packages apply as described above +for `sof_mel_to_text_live_dsp_vad.py`. + +```bash +# Microphone compress audio features +python3 sof_mel_to_text_live_compress.py --card 0 --device 54 --model whisper-medium-int4-ov + +# Jack In compress audio features +python3 sof_mel_to_text_live_compress.py --card 0 --device 53 --model whisper-medium-int4-ov +``` + +### Compress PCM device IDs + +The compress audio-features capture PCMs in the topology2 platform +configs use the following IDs (see +`tools/topology/topology2/platform/intel/sdw-*-audio-feature-compress.conf`): + +| Device | Name | PCM ID | +|--------|-----------------------------------|--------| +| Jack | "Jack In Compress Audio Features" | 53 | +| Mic | "Microphone Compress Audio Features" | 54 | + +The non-compress PCMs (`47` jack, `48` mic, "Audio Features") are PCM +streams intended for `arecord`/`hw:0,N` (used by +`sof_mel_to_text_live_dsp_vad.py` above) and will not work with the +compress scripts below. + +## Live Spectrogram Viewers + +These viewers are helpful for interactively checking VAD operation and +developing improvements, or for educational visualization of Mel and +cepstral-coefficient spectrograms. + +#### Additional dependencies to install + +GTK 4 and the introspection bindings are shipped by the distribution. +On Debian / Ubuntu (24.04 or newer): + +```bash +sudo apt install python3-gi gir1.2-gtk-4.0 python3-numpy +sudo apt install libgirepository-2.0-dev libcairo2-dev pkg-config \ + python3-dev gir1.2-gtk-4.0 build-essential +pip install PyGObject pycairo numpy +``` + +#### Renderer selection + +GSK auto-selects the renderer; to force the GL or Vulkan back-ends +(usually the smoothest): + +```bash +GSK_RENDERER=ngl python3 sof_mel_spectrogram_compress.py ... +GSK_RENDERER=vulkan python3 sof_mel_spectrogram_compress.py ... +``` + +### Mel Spectrogram + +The `sof_mel_spectrogram_compress.py` script captures Mel spectrogram +frames from a SOF compress PCM device and displays them as a live +scrolling spectrogram with VAD status in a GTK 4 window. This is a +lightweight viewer that does not run Whisper inference. + +```bash +# Microphone compress audio features +python3 sof_mel_spectrogram_compress.py --card 0 --device 54 --width 300 + +# Jack In compress audio features +python3 sof_mel_spectrogram_compress.py --card 0 --device 53 --width 300 +``` + +### Cepstral Spectrogram + +The `sof_ceps_spectrogram_compress.py` script is the counterpart +that displays cepstral coefficients (MFCC) instead of Mel bands. It +imports the GPU-accelerated widgets from `sof_mel_spectrogram_compress.py`, +so both files must remain in the same directory. + +```bash +# Microphone compress audio features +python3 sof_ceps_spectrogram_compress.py --card 0 --device 54 --num-ceps 13 --width 300 + +# Jack In compress audio features +python3 sof_ceps_spectrogram_compress.py --card 0 --device 53 --num-ceps 13 --width 300 +``` diff --git a/src/audio/mfcc/tune/decode_all.m b/src/audio/mfcc/tune/decode_all.m new file mode 100644 index 000000000000..4c377bf5029a --- /dev/null +++ b/src/audio/mfcc/tune/decode_all.m @@ -0,0 +1,38 @@ +% 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 (all int32 output, Q9.23) +ceps_files = {'mfcc_s16.raw', 'mfcc_s24.raw', 'mfcc_s32.raw'}; + +% Mel output files (all int32 output, Q9.23) +mel_files = {'mel_s16.raw', 'mel_s24.raw', 'mel_s32.raw'}; + +% 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]; + +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, vad, energy, noise_energy, frame_num] = ... + decode_ceps(fn, num_ceps); + end +end + +for i = 1:length(all_mel_files) + fn = all_mel_files{i}; + if exist(fn, 'file') + fprintf('Decoding Mel: %s\n', fn); + [mel, t, n, vad, energy, noise_energy, frame_num] = ... + decode_mel(fn, num_mel); + end +end diff --git a/src/audio/mfcc/tune/decode_ceps.m b/src/audio/mfcc/tune/decode_ceps.m new file mode 100644 index 000000000000..480eadea2945 --- /dev/null +++ b/src/audio/mfcc/tune/decode_ceps.m @@ -0,0 +1,148 @@ +% [ceps, t, n, vad, energy, noise_energy, frame_number] = decode_ceps(fn, num_ceps, hop, num_channels) +% +% Input +% fn - File with MFCC data in .raw or .wav format +% num_ceps - number of cepstral coefficients per frame +% hop - STFT hop in seconds, defaults to 10e-3 for 10 ms +% num_channels - needed for .raw format, omit for .wav, default 1 +% +% Outputs +% ceps - cepstral coefficients +% t - time vector for plotting +% n - ceps 1..num_ceps vector for plotting +% vad - VAD flag per frame from DSP +% energy - weighted signal energy per frame from DSP +% noise_energy - weighted noise floor energy per frame from DSP +% frame_number - frame number from DSP + +% SPDX-License-Identifier: BSD-3-Clause +% Copyright(c) 2022-2026 Intel Corporation. All rights reserved. + +function [ceps, t, n, vad, energy, noise_energy, frame_number] = ... + decode_ceps(fn, num_ceps, hop, num_channels) + +if nargin < 3 + hop = 10e-3; +end +if nargin < 4 + num_channels = 1; +end + +% MFCC stream +qformat = 23; % Q9.23 in int32 +magic = int32(1835426659); % 0x6D666363 as int32 +num_magic = 1; % magic word is 1 x int32 + +% Load output data (always int32) +[data, num_channels] = get_file(fn, num_channels); + +idx = find(data == magic); + +if isempty(idx) + error('No magic value markers found from stream'); +end + +num_frames = length(idx); + +% Header after magic is [frame_number, reserved, energy, noise_energy, vad_flag] +% as int32, followed by num_ceps coefficients (int32). +payload_len = 5 + num_ceps; + +% Last frame can be incomplete due to span over multiple periods +last = idx(end) + num_magic + payload_len - 1; +if (last > length(data)) + num_frames = num_frames - 1; +end + +payload = zeros(payload_len, num_frames); +for i = 1:num_frames + i1 = idx(i) + num_magic; + i2 = i1 + payload_len - 1; + payload(:,i) = double(data(i1:i2)); +end + +frame_number = payload(1, :); +% payload(2,:) is reserved, skip +energy = payload(3, :) / 2^23; +noise_energy = payload(4, :) / 2^23; +vad = payload(5, :); +ceps = payload(6:payload_len, :) / 2^qformat; + +% Fill gaps from DTX-suppressed VAD=0 frames to create continuous timeline. +% Missing frames are filled with the minimum ceps value found in the data. +first_frame = frame_number(1); +last_frame = frame_number(end); +total_frames = last_frame - first_frame + 1; +if total_frames > num_frames + ceps_fill = min(ceps(:)); + ceps_full = ones(num_ceps, total_frames) * ceps_fill; + vad_full = zeros(1, total_frames); + energy_full = zeros(1, total_frames); + noise_energy_full = zeros(1, total_frames); + frame_number_full = first_frame:last_frame; + has_data = false(1, total_frames); + for i = 1:num_frames + fi = frame_number(i) - first_frame + 1; + ceps_full(:, fi) = ceps(:, i); + vad_full(fi) = vad(i); + energy_full(fi) = energy(i); + noise_energy_full(fi) = noise_energy(i); + has_data(fi) = true; + end + % Forward-fill gaps with last received values + for fi = 2:total_frames + if ~has_data(fi) + ceps_full(:, fi) = ceps_full(:, fi - 1); + energy_full(fi) = energy_full(fi - 1); + noise_energy_full(fi) = noise_energy_full(fi - 1); + end + end + ceps = ceps_full; + vad = vad_full; + energy = energy_full; + noise_energy = noise_energy_full; + frame_number = frame_number_full; +end + +t = (frame_number - first_frame) * hop; +n = 1:num_ceps; + +figure; +surf(t, n, ceps, 'EdgeColor', 'none'); +colormap(jet); +view(45, 60) +tstr = sprintf('SOF MFCC cepstral coefficients (%s)', fn); +title(tstr, 'Interpreter', 'None'); +xlabel('Time (s)'); +ylabel('Cepstral coef #'); + +end + +function [data, num_channels] = get_file(fn, num_channels) + +[~, ~, ext] = fileparts(fn); + +switch lower(ext) + case '.raw' + fh = fopen(fn, 'r'); + data = fread(fh, 'int32'); + fclose(fh); + case '.wav' + tmp = audioread(fn, 'native'); + t = whos('tmp'); + if ~strcmp(t.class, 'int32') + error('Expected 32-bit wav for int32 MFCC output format'); + end + s = size(tmp); + num_channels = s(2); + if num_channels > 1 + data = int32(zeros(prod(s), 1)); + for i = 1:num_channels + data(i:num_channels:end) = tmp(:, i); + end + end + otherwise + error('Unknown audio format'); +end + +end diff --git a/src/audio/mfcc/tune/decode_mel.m b/src/audio/mfcc/tune/decode_mel.m new file mode 100644 index 000000000000..0aca1e35ec8d --- /dev/null +++ b/src/audio/mfcc/tune/decode_mel.m @@ -0,0 +1,176 @@ +% [mel, t, n, vad, energy, noise_energy, frame_number] = decode_mel(fn, num_mel, hop, num_channels) +% +% Input +% fn - File with Mel data in .raw or .wav format +% hop - STFT hop in seconds, defaults to 10e-3 for 10 ms +% num_mel - number of Mel coefficients per frame +% num_channels - needed for .raw format, omit for .wav, default 1 +% +% Outputs +% mel - Mel coefficients +% t - time vector for plotting +% n - mel 1..num_mel vector for plotting +% vad - VAD flag per frame from DSP +% energy - weighted signal energy per frame from DSP +% noise_energy - weighted noise floor energy per frame from DSP +% frame_number - frame number from DSP + +% SPDX-License-Identifier: BSD-3-Clause +% Copyright(c) 2026 Intel Corporation. + +function [mel, t, n, vad, energy, noise_energy, frame_number] = ... + decode_mel(fn, num_mel, hop, num_channels) + +if nargin < 3 + hop = 10e-3; +end +if nargin < 4 + num_channels = 1; +end + +% MFCC stream +fs = 16e3; +qformat = 23; % Q9.23 in int32 + +magic = int32(1835426659); % 0x6D666363 as int32 +num_magic = 1; % magic word is 1 x int32 +num_other_header = 5; % frame_number, reserved, energy, noise, vad (all int32) + +% Load output data (always int32) +[data, num_channels] = get_file(fn, num_channels); + +if isempty(data) + error('File %s is empty', fn); +end + +idx = find(data == magic); + +if isempty(idx) + error('No magic value markers found from stream'); +end + +period_mel = idx(2)-idx(1); +num_frames = length(idx); + +% Header after magic is [frame_number, reserved, energy, noise_energy, vad_flag] +% as int32, followed by num_mel coefficients. +% For s16 each int32 occupies 2 int16 slots. +payload_len = num_other_header + num_mel; + +% Last frame can be incomplete due to span over multiple periods +last = idx(end) + num_magic + payload_len - 1; +if (last > length(data)) + num_frames = num_frames - 1; +end + +payload = zeros(payload_len, num_frames); +for i = 1:num_frames + i1 = idx(i) + num_magic; + i2 = i1 + payload_len - 1; + payload(:,i) = double(data(i1:i2)); +end + +frame_number = payload(1, :); +% payload(2,:) is reserved, skip +energy = payload(3, :) / 2^23; +noise_energy = payload(4, :) / 2^23; +vad = payload(5, :); +mel = payload(6:payload_len, :) / 2^qformat; + +% Fill gaps from DTX-suppressed VAD=0 frames to create continuous timeline. +% Missing frames are filled with the minimum Mel value found in the data. +first_frame = frame_number(1); +last_frame = frame_number(end); +total_frames = last_frame - first_frame + 1; +if total_frames > num_frames + mel_fill = min(mel(:)); + mel_full = ones(num_mel, total_frames) * mel_fill; + vad_full = zeros(1, total_frames); + energy_full = zeros(1, total_frames); + noise_energy_full = zeros(1, total_frames); + frame_number_full = first_frame:last_frame; + has_data = false(1, total_frames); + for i = 1:num_frames + fi = frame_number(i) - first_frame + 1; + mel_full(:, fi) = mel(:, i); + vad_full(fi) = vad(i); + energy_full(fi) = energy(i); + noise_energy_full(fi) = noise_energy(i); + has_data(fi) = true; + end + % Forward-fill gaps with last received values + for fi = 2:total_frames + if ~has_data(fi) + mel_full(:, fi) = mel_full(:, fi - 1); + energy_full(fi) = energy_full(fi - 1); + noise_energy_full(fi) = noise_energy_full(fi - 1); + end + end + mel = mel_full; + vad = vad_full; + energy = energy_full; + noise_energy = noise_energy_full; + frame_number = frame_number_full; +end + +t = (frame_number - first_frame) * hop; +n = 1:num_mel; + +figure +imagesc(t, n, mel); +axis xy; +colormap(jet); +colorbar; +tstr = sprintf('SOF MFCC Mel coefficients (%s)', fn); +title(tstr, 'Interpreter', 'None'); +ylabel('Mel coef #'); + +figure +subplot(2,1,1); +plot(t, vad) +ax = axis(); +axis([ax(1:2) -0.1 1.1]); +grid on; +title(tstr, 'Interpreter', 'None'); +xlabel('Time (s)'); +ylabel('VAD flag'); + +subplot(2,1,2); +plot(t, energy, t, noise_energy); +grid on; +legend('Energy', 'Noise Energy'); +xlabel('Time (s)'); +ylabel('Energy'); + +end + +function [data, num_channels] = get_file(fn, num_channels) + +[~, ~, ext] = fileparts(fn); + +switch lower(ext) + case '.raw' + fh = fopen(fn, 'r'); + data = fread(fh, 'int32'); + fclose(fh); + case '.wav' + tmp = audioread(fn, 'native'); + t = whos('tmp'); + if ~strcmp(t.class, 'int32') + error('Expected 32-bit wav for int32 MFCC output format'); + 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/src/audio/mfcc/tune/setup_mfcc.m b/src/audio/mfcc/tune/setup_mfcc.m new file mode 100644 index 000000000000..dbf69587a74f --- /dev/null +++ b/src/audio/mfcc/tune/setup_mfcc.m @@ -0,0 +1,339 @@ +% setup_mfcc() +% +% 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-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); + + % Blob for mel spectrogram data + setup = get_mel_spectrogram_config(); + setup.tplg_fn = 'mel80.conf'; + export_mfcc_setup(gen_cfg, setup); + + % Blob for mel spectrogram with compress PCM output + setup = get_mel_spectrogram_config(); + setup.compress_output = true; + setup.tplg_fn = 'mel80_compress.conf'; + export_mfcc_setup(gen_cfg, setup); + + % Blob for mel spectrogram with compress PCM output and DTX + setup = get_mel_spectrogram_config(); + setup.compress_output = true; + setup.enable_dtx = true; + setup.dtx_trailing_silence_hops = 20; + setup.dtx_silence_hops_interval = 500; + setup.tplg_fn = 'mel80_compress_dtx.conf'; + export_mfcc_setup(gen_cfg, setup); + + % Default MFCC (cepstral) with compress PCM output + setup = get_mfcc_default_config(); + setup.compress_output = true; + setup.enable_vad = true; + setup.enable_dtx = true; + setup.dtx_trailing_silence_hops = 20; + setup.dtx_silence_hops_interval = 500; + setup.update_controls = true; + setup.tplg_fn = 'ceps13_compress_dtx.conf'; + export_mfcc_setup(gen_cfg, setup); + +end + +function cfg = get_mfcc_default_config() + cfg.blackman_coef = 0.42; + cfg.cepstral_lifter = 22.0; + 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 + cfg.frame_shift = 10.0; % ms + cfg.high_freq = 0.0; % 0 is Nyquist + cfg.htk_compat = false; % no support + cfg.low_freq = 20.0; % Hz + cfg.num_ceps = 13; + cfg.min_duration = 0.0; % no support + cfg.norm = 'none'; % Use none or slaney + cfg.num_mel_bins = 23; + cfg.preemphasis_coefficient = 0; % disable + cfg.raw_energy = true; + cfg.remove_dc_offset = true; + cfg.round_to_power_of_two = true; % must be true + cfg.sample_frequency = 16000; + cfg.snip_edges = true; % must be true + cfg.subtract_mean = false; % must be false + cfg.use_energy = false; + cfg.vtln_high = -500.0; % no support + cfg.vtln_low = 100.0; % no support + cfg.vtln_warp = 1.0; % must be 1.0 (vocal tract length normalization) + cfg.window_type = 'hamming'; + 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 + cfg.enable_vad = false; + cfg.enable_dtx = false; + cfg.dtx_trailing_silence_hops = 0; + cfg.dtx_silence_hops_interval = 0; + cfg.update_controls = false; + cfg.compress_output = false; +end + +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; + cfg.enable_vad = true; + cfg.enable_dtx = false; + cfg.dtx_trailing_silence_hops = 0; + cfg.dtx_silence_hops_interval = 0; + cfg.update_controls = true; + cfg.compress_output = false; +end + +function export_mfcc_setup(gen_cfg, cfg) + +%% Use blob tool from EQ +addpath([gen_cfg.tools_path 'tune/common']); + +%% Blob size, size plus reserved(8) + current parameters +nbytes_data = 116; + +%% Little endian +sh32 = [0 -8 -16 -24]; +sh16 = [0 -8]; + +%% Get ABI information +[abi_bytes, nbytes_abi] = sof_get_abi(nbytes_data, gen_cfg.ipc_ver); + +%% Initialize correct size uint8 array +nbytes = nbytes_abi + nbytes_data; +b8 = uint8(zeros(1,nbytes)); + +%% Insert ABI header +fprintf(1, 'MFCC 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 MFCC configuration, first struct header and reserved, then data +[b8, j] = add_w32b(nbytes_data, b8, j); + +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); + +v = cfg.dtx_trailing_silence_hops; [b8, j] = add_w16b(v, b8, j); % DTX trailing silence hops +v = cfg.dtx_silence_hops_interval; [b8, j] = add_w16b(v, b8, j); % DTX silence frame interval +% Reserved +for i = 1:5 + [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 = 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 +v = q_convert(cfg.blackman_coef, 15); [b8, j] = add_w16b(v, b8, j); +v = q_convert(cfg.cepstral_lifter, 9); [b8, j] = add_w16b(v, b8, j); +v = cfg.channel; [b8, j] = add_w16b(v, b8, j); +v = cfg.dither; [b8, j] = add_w16b(v, b8, j); +v = round(cfg.frame_length/1000 * cfg.sample_frequency); [b8, j] = add_w16b(v, b8, j); +v = round(cfg.frame_shift/1000 * cfg.sample_frequency); [b8, j] = add_w16b(v, b8, j); +v = q_convert(cfg.high_freq, 0); [b8, j] = add_w16b(v, b8, j); +v = q_convert(cfg.low_freq, 0); [b8, j] = add_w16b(v, b8, j); +v = cfg.num_ceps; [b8, j] = add_w16b(v, b8, j); +v = cfg.num_mel_bins; [b8, j] = add_w16b(v, b8, j); +v = q_convert(cfg.preemphasis_coefficient, 15); [b8, j] = add_w16b(v, b8, j); +v = q_convert(cfg.top_db, 7); [b8, j] = add_w16b(v, b8, j); +v = 0; [b8, j] = add_w16b(v, b8, j); % vtln_high Qx.y TBD +v = 0; [b8, j] = add_w16b(v, b8, j); % vtln_low Qx.y TBD +v = 0; [b8, j] = add_w16b(v, b8, j); % vtln_warp Qx.y TBD +% reserved16[3] +for i = 1:3 + [b8, j] = add_w16b(0, b8, j); +end +v = cfg.htk_compat; [b8, j] = add_w8b(v, b8, j); % bool +v = cfg.raw_energy; [b8, j] = add_w8b(v, b8, j); % bool +v = cfg.remove_dc_offset; [b8, j] = add_w8b(v, b8, j); % bool +v = cfg.round_to_power_of_two; [b8, j] = add_w8b(v, b8, j); % bool +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 +v = cfg.enable_vad; [b8, j] = add_w8b(v, b8, j); % bool +v = cfg.enable_dtx; [b8, j] = add_w8b(v, b8, j); % bool +v = cfg.update_controls; [b8, j] = add_w8b(v, b8, j); % bool +v = cfg.compress_output; [b8, j] = add_w8b(v, b8, j); % bool +% reserved_bool[4] +for i = 1:4 + [b8, j] = add_w8b(0, b8, j); +end + +%% Export +tplg_fn = [gen_cfg.mfcc_conf_path cfg.tplg_fn]; +switch gen_cfg.tplg_ver + case 1 + sof_tplg_write(tplg_fn, b8, "DEF_MFCC_PRIV", ... + "Exported with script setup_mfcc.m", ... + "cd src/audio/mfcc/tune; octave setup_mfcc.m"); + case 2 + sof_tplg2_write(tplg_fn, b8, "mfcc_config", ... + "Exported MFCC configuration", ... + "cd src/audio/mfcc/tune; octave setup_mfcc.m"); + otherwise + error("Illegal tplg_ver, use 1 for topology v1 or 2 topology v2."); +end + +rmpath([gen_cfg.tools_path 'tune/common']); + +end + +%% Helper functions + +function n = get_window(cfg) + switch lower(cfg.window_type) + case 'rectangular' + n = 0; + case 'blackman' + n = 1; + case 'hamming' + n = 2; + case 'hann' + n = 3; + case 'povey' + n = 4; + otherwise + error('Unknown window type'); + 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); +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/mfcc/tune/sof_ceps_spectrogram_compress.py b/src/audio/mfcc/tune/sof_ceps_spectrogram_compress.py new file mode 100755 index 000000000000..8cb8190c56b4 --- /dev/null +++ b/src/audio/mfcc/tune/sof_ceps_spectrogram_compress.py @@ -0,0 +1,374 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: BSD-3-Clause +# +# Copyright (c) 2026, Intel Corporation. + +"""Live scrolling cepstral coefficient viewer for SOF compress PCM capture. + +GTK 4 application that displays a live scrolling MFCC cepstral +spectrogram with VAD status from a SOF compress audio-features PCM. +Rendering goes through ``Gtk.Snapshot`` with ``Gdk.MemoryTexture`` for +the spectrogram and native GSK render nodes (``Gsk.Path`` strokes, +``append_color``, ``append_layout``) for the strip plots, so the +GL/Vulkan renderer handles compositing on the GPU. Vsync-aligned +redraws (``add_tick_callback``) and sub-pixel scrolling keep the 100 Hz +data stream looking smooth on a 60+ Hz display. + +Dependencies (Debian/Ubuntu): + sudo apt install python3-gi gir1.2-gtk-4.0 + pip install numpy + +Frame format: [magic(int32), frame_number(uint32), reserved(int32), + energy(int32), noise_energy(int32), vad_flag(int32), + ceps[0..N-1](int32)] + +Cepstral coefficients are in Q9.23 fixed-point format. + +Usage: + python sof_ceps_spectrogram_compress.py [--card 0] [--device 54] + python sof_ceps_spectrogram_compress.py --num-ceps 13 --width 300 +""" + +import argparse +import os +import queue +import signal +import struct +import subprocess +import sys +import threading +import time + +import numpy as np + +import gi +gi.require_version('Gtk', '4.0') +gi.require_version('Gdk', '4.0') +gi.require_version('Gsk', '4.0') +gi.require_version('Graphene', '1.0') +gi.require_version('Pango', '1.0') +from gi.repository import GLib, Gtk # noqa: E402 + +# Reuse the GPU-accelerated widgets from the mel viewer. +from sof_mel_spectrogram_compress import ( # noqa: E402 + MelSpectrogramArea as SpectrogramArea, + CurveStripArea, + GUI_REFRESH_MS, +) + +# SOF compress frame format constants (with DSP data header) +SOF_MAGIC_BYTES = struct.pack('<i', 0x6D666363) # ASCII 'mfcc' as int32 +SOF_NUM_HEADER = 6 # magic, frame_number, reserved, energy, noise_energy, vad_flag +SOF_HEADER_BYTES = SOF_NUM_HEADER * 4 # 24 bytes +SOF_HOP_SEC = 0.01 # 10 ms per STFT hop + +SPECTROGRAM_WIDTH = 200 # default number of frames visible +DEFAULT_NUM_CEPS = 13 # default cepstral coefficients +Q_FORMAT = 23 # Q9.23 fixed-point + +# Color range for the image (cepstral coefficients in float) +CEPS_VMIN = -50.0 +CEPS_VMAX = 50.0 + + +def decode_ceps_frame(raw_ints): + """Convert int32 Q9.23 cepstral coefficients to float32.""" + return raw_ints.astype(np.float32) / (2 ** Q_FORMAT) + + +def parse_frame(buf, num_ceps): + """Parse one complete ceps frame from a bytearray buffer. + + Mutates buf in-place (deletes consumed bytes). + Returns: (frame_number, vad_flag, energy, noise, ceps_ints) or + (None, None, None, None, None) when no frame is ready. + """ + frame_bytes = SOF_HEADER_BYTES + num_ceps * 4 + idx = buf.find(SOF_MAGIC_BYTES) + if idx < 0: + if len(buf) > 3: + del buf[:-3] + return None, None, None, None, None + end = idx + frame_bytes + if end > len(buf): + del buf[:idx] + return None, None, None, None, None + + frame_number = struct.unpack_from('<I', buf, idx + 4)[0] + energy_int = struct.unpack_from('<i', buf, idx + 12)[0] + noise_int = struct.unpack_from('<i', buf, idx + 16)[0] + vad_flag = struct.unpack_from('<i', buf, idx + 20)[0] + + ceps_bytes = bytes(buf[idx + SOF_HEADER_BYTES:end]) + ceps_ints = np.frombuffer(ceps_bytes, dtype=np.int32) + del buf[:end] + energy = energy_int / (2 ** Q_FORMAT) + noise = noise_int / (2 ** Q_FORMAT) + return frame_number, vad_flag, energy, noise, ceps_ints + + +def start_crecord(card, device): + """Spawn crecord as subprocess; return Popen.""" + crecord_cmd = [ + 'crecord', '-v', + '-c', str(card), + '-d', str(device), + '-I', 'BESPOKE', + '-R', '16000', + '-C', '2', + '-F', 'S32_LE', + '-b', '6144', + '-f', '3', + ] + cmd = ['stdbuf', '-o0'] + crecord_cmd + print(f"Starting compress capture: {' '.join(crecord_cmd)}") + return subprocess.Popen(cmd, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + bufsize=0) + + +class CepsViewer: + """Top-level controller: capture process + GTK window.""" + + def __init__(self, app, card, device, width, num_ceps): + self.app = app + self.num_ceps = num_ceps + self.width_frames = width + self.frame_bytes = SOF_HEADER_BYTES + num_ceps * 4 + + # Ring buffers stored as (frames, ceps) for cheap shifts along axis 0. + self.ceps_buf = np.zeros((width, num_ceps), dtype=np.float32) + self.vad_buf = np.zeros(width, dtype=np.float32) + self.energy_buf = np.zeros(width, dtype=np.float32) + self.noise_buf = np.zeros(width, dtype=np.float32) + + self.recv_frames = 0 + self.prev_speech = None + self._closing = False + self._last_frame_mono = None + self._frame_period = SOF_HOP_SEC + + self._build_ui() + + # Start capture + self.proc = start_crecord(card, device) + self.frame_q = queue.Queue() + self.reader = threading.Thread(target=self._reader_thread, daemon=True) + self.reader.start() + + # Vsync-aligned redraws via the frame clock; fall back to a periodic + # timer if add_tick_callback is unavailable. + self._tick_id = 0 + self._timer_id = 0 + if hasattr(self.ceps_area, 'add_tick_callback'): + self._tick_id = self.ceps_area.add_tick_callback(self._on_tick_clock) + else: + self._timer_id = GLib.timeout_add(GUI_REFRESH_MS, self._on_timer) + + print(f"Cepstral coefficients: {num_ceps} (frame size: {self.frame_bytes} bytes)") + print(f"Spectrogram width: {width} frames ({width * SOF_HOP_SEC:.1f}s)") + if self._tick_id: + print("GUI refresh: vsync-aligned via frame clock, " + "sub-pixel scroll enabled") + else: + print(f"GUI refresh: fallback timer at {GUI_REFRESH_MS} ms " + f"({1000.0 / GUI_REFRESH_MS:.0f} FPS)") + print() + + def _build_ui(self): + self.window = Gtk.ApplicationWindow(application=self.app) + self.window.set_title( + f"SOF MFCC Cepstral Coefficients ({self.num_ceps} ceps) — DSP VAD" + ) + self.window.set_default_size(1100, 600) + self.window.connect('close-request', self._on_close) + + box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=2) + box.set_margin_start(4) + box.set_margin_end(4) + box.set_margin_top(4) + box.set_margin_bottom(4) + self.window.set_child(box) + + # Spectrogram-style cepstral image (re-uses the GPU texture widget). + self.ceps_area = SpectrogramArea(self.ceps_buf, CEPS_VMIN, CEPS_VMAX) + self.vad_area = CurveStripArea( + lambda: {'vad': self.vad_buf}, + ymin=-0.1, ymax=1.1, + lines=[('vad', (0.2, 0.9, 0.2, 1.0))], + yticks=[(0, 'Silent'), (1, 'Speech')], + label='VAD', + height_request=70, + ) + self.level_area = CurveStripArea( + lambda: {'energy': self.energy_buf, 'noise': self.noise_buf}, + autoscale=True, + lines=[ + ('energy', (1.0, 0.9, 0.2, 1.0)), + ('noise', (0.2, 0.9, 1.0, 1.0)), + ], + label='Level (Y=speech, C=noise)', + height_request=110, + ) + + self.ceps_area.set_vexpand(True) + box.append(self.ceps_area) + box.append(self.vad_area) + box.append(self.level_area) + + # --- I/O thread --------------------------------------------------- + def _reader_thread(self): + buf = bytearray() + raw_fd = self.proc.stdout.fileno() + try: + while True: + data = os.read(raw_fd, self.frame_bytes * 4) + if not data: + break + buf.extend(data) + while True: + fn, vf, en, no, ci = parse_frame(buf, self.num_ceps) + if ci is None: + break + self.frame_q.put((fn, vf, en, no, ci)) + except OSError: + pass + self.frame_q.put(None) + + # --- GUI tick ----------------------------------------------------- + def _drain_and_redraw(self): + drained = 0 + eof = False + while True: + try: + item = self.frame_q.get_nowait() + except queue.Empty: + break + if item is None: + eof = True + break + fn, vf, en, no, ci = item + self._ingest(fn, vf, en, no, ci) + drained += 1 + if drained >= 64: + break + + if self._last_frame_mono is None: + sub = 0.0 + else: + sub = (time.monotonic() - self._last_frame_mono) / self._frame_period + if sub < 0.0: + sub = 0.0 + elif sub > 1.0: + sub = 1.0 + self.ceps_area.set_sub_offset(sub) + self.vad_area.set_sub_offset(sub) + self.level_area.set_sub_offset(sub) + + if eof: + self._on_eof() + return False + return True + + def _on_tick_clock(self, _widget, _frame_clock): + return True if self._drain_and_redraw() else False + + def _on_timer(self): + return GLib.SOURCE_CONTINUE if self._drain_and_redraw() \ + else GLib.SOURCE_REMOVE + + def _ingest(self, frame_number, vad_flag, energy, noise, ceps_ints): + self.recv_frames += 1 + self._last_frame_mono = time.monotonic() + ceps = decode_ceps_frame(ceps_ints) + speech = vad_flag != 0 + + if speech != self.prev_speech: + t = frame_number * SOF_HOP_SEC + tag = "SPEECH" if speech else "SILENCE" + print(f" [{t:7.2f}s] {tag} (hop {frame_number})", flush=True) + self.prev_speech = speech + + # Scroll left and append new frame on the right. + self.ceps_buf[:-1] = self.ceps_buf[1:] + self.ceps_buf[-1] = ceps + self.vad_buf[:-1] = self.vad_buf[1:] + self.vad_buf[-1] = 1.0 if speech else 0.0 + self.energy_buf[:-1] = self.energy_buf[1:] + self.energy_buf[-1] = energy + self.noise_buf[:-1] = self.noise_buf[1:] + self.noise_buf[-1] = noise + + def _on_eof(self): + if self._closing: + return + if self.proc.poll() is None: + self.proc.terminate() + try: + rc = self.proc.wait(timeout=2) + except subprocess.TimeoutExpired: + self.proc.kill() + rc = self.proc.wait() + try: + stderr_out = self.proc.stderr.read().decode(errors='replace') + except Exception: + stderr_out = '' + print(f"\ncrecord exited with code {rc}") + if stderr_out: + print(f"stderr: {stderr_out}") + self.window.close() + + # --- shutdown ----------------------------------------------------- + def _on_close(self, *_args): + self._closing = True + if self._tick_id: + self.ceps_area.remove_tick_callback(self._tick_id) + self._tick_id = 0 + if self._timer_id: + GLib.source_remove(self._timer_id) + self._timer_id = 0 + if self.proc.poll() is None: + self.proc.terminate() + try: + self.proc.wait(timeout=3) + except subprocess.TimeoutExpired: + self.proc.kill() + self.proc.wait() + print(f"\nCapture stopped. Received {self.recv_frames} frames.") + return False + + +def main(): + parser = argparse.ArgumentParser( + description="Live scrolling MFCC cepstral coefficient viewer " + "from SOF compress PCM capture (GTK 4 / GSK)") + parser.add_argument('--card', '-c', type=int, default=0, + help='ALSA card number (default: 0)') + parser.add_argument('--device', '-d', type=int, default=54, + help='ALSA compress device number (default: 54)') + parser.add_argument('--width', '-w', type=int, default=SPECTROGRAM_WIDTH, + help=f'Spectrogram width in frames (default: {SPECTROGRAM_WIDTH})') + parser.add_argument('--num-ceps', '-n', type=int, default=DEFAULT_NUM_CEPS, + help=f'Number of cepstral coefficients (default: {DEFAULT_NUM_CEPS})') + args = parser.parse_args() + + print("=== SOF MFCC Cepstral Coefficient Viewer (Compress PCM, GTK 4) ===\n") + + signal.signal(signal.SIGINT, signal.SIG_DFL) + + app = Gtk.Application(application_id='org.sof.ceps_spectrogram') + holder = {} + + def on_activate(application): + viewer = CepsViewer(application, args.card, args.device, + args.width, args.num_ceps) + viewer.window.present() + holder['viewer'] = viewer + + app.connect('activate', on_activate) + sys.exit(app.run(None)) + + +if __name__ == '__main__': + main() diff --git a/src/audio/mfcc/tune/sof_mel_spectrogram_compress.py b/src/audio/mfcc/tune/sof_mel_spectrogram_compress.py new file mode 100755 index 000000000000..e85e0494cf22 --- /dev/null +++ b/src/audio/mfcc/tune/sof_mel_spectrogram_compress.py @@ -0,0 +1,625 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: BSD-3-Clause +# +# Copyright (c) 2026, Intel Corporation. + +"""Live scrolling mel spectrogram viewer for SOF compress PCM capture. + +GTK 4 application that displays a live scrolling mel spectrogram with +VAD status from a SOF compress audio-features PCM. Rendering uses +``Gtk.Snapshot`` with a ``Gdk.MemoryTexture`` for the spectrogram so +that the per-frame upload is handed to the GSK GPU renderer; the small +VAD / level strips use Cairo snapshot nodes. + +Dependencies (Debian/Ubuntu): + sudo apt install python3-gi gir1.2-gtk-4.0 + pip install numpy + +Frame format: [magic(int32), frame_number(uint32), reserved(int32), + energy(int32), noise_energy(int32), vad_flag(int32), + mel[0..79](int32)] + +Usage: + python sof_mel_spectrogram_compress.py [--card 0] [--device 54] + python sof_mel_spectrogram_compress.py --width 400 +""" + +import argparse +import os +import queue +import signal +import struct +import subprocess +import sys +import threading +import time + +import numpy as np + +import gi +gi.require_version('Gtk', '4.0') +gi.require_version('Gdk', '4.0') +gi.require_version('Gsk', '4.0') +gi.require_version('Graphene', '1.0') +gi.require_version('Pango', '1.0') +from gi.repository import Gdk, GLib, Gsk, Gtk, Graphene, Pango # noqa: E402 + +# SOF compress mel frame format constants (with DSP data header) +SOF_MAGIC_BYTES = struct.pack('<i', 0x6D666363) # ASCII 'mfcc' as int32 +SOF_NUM_HEADER = 6 # magic, frame_number, reserved, energy, noise_energy, vad_flag +SOF_Q_FORMAT = 23 # Q9.23 fixed-point +SOF_NUM_MEL = 80 +SOF_FRAME_INTS = SOF_NUM_HEADER + SOF_NUM_MEL # 86 int32 per frame +SOF_FRAME_BYTES = SOF_FRAME_INTS * 4 # 344 bytes per frame +SOF_HOP_SEC = 0.01 # 10 ms per STFT hop + +SPECTROGRAM_WIDTH = 200 # default number of frames visible + +# Color range for the mel image (float coefficients) +MEL_VMIN = -2.0 +MEL_VMAX = 2.0 + +# Fallback redraw period when no frame clock is available (ms). +GUI_REFRESH_MS = 16 + + +def decode_mel_frame(raw_ints): + """Convert 80 int32 Q9.23 values to float32 mel coefficients.""" + return raw_ints.astype(np.float32) / (2 ** SOF_Q_FORMAT) + + +def parse_frame(buf): + """Parse one complete mel frame from a bytearray buffer. + + Mutates buf in-place (deletes consumed bytes). + Returns: (frame_number, vad_flag, energy, noise, mel_ints) or + (None, None, None, None, None) when no frame is ready. + """ + idx = buf.find(SOF_MAGIC_BYTES) + if idx < 0: + if len(buf) > 3: + del buf[:-3] + return None, None, None, None, None + end = idx + SOF_FRAME_BYTES + if end > len(buf): + del buf[:idx] + return None, None, None, None, None + + frame_number = struct.unpack_from('<I', buf, idx + 4)[0] + energy_int = struct.unpack_from('<i', buf, idx + 12)[0] + noise_int = struct.unpack_from('<i', buf, idx + 16)[0] + vad_flag = struct.unpack_from('<i', buf, idx + 20)[0] + + mel_bytes = bytes(buf[idx + SOF_NUM_HEADER * 4:end]) + mel_ints = np.frombuffer(mel_bytes, dtype=np.int32) + del buf[:end] + energy = energy_int / (2 ** SOF_Q_FORMAT) + noise = noise_int / (2 ** SOF_Q_FORMAT) + return frame_number, vad_flag, energy, noise, mel_ints + + +def start_crecord(card, device): + """Spawn crecord as subprocess; return Popen.""" + crecord_cmd = [ + 'crecord', '-v', + '-c', str(card), + '-d', str(device), + '-I', 'BESPOKE', + '-R', '16000', + '-C', '2', + '-F', 'S32_LE', + '-b', '6144', + '-f', '3', + ] + cmd = ['stdbuf', '-o0'] + crecord_cmd + print(f"Starting compress capture: {' '.join(crecord_cmd)}") + return subprocess.Popen(cmd, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + bufsize=0) + + +def _build_turbo_lut(n=256): + """Return an Nx4 uint8 RGBA lookup table for the 'turbo' colormap. + + Polynomial approximation by A. Mikhailov (Google, public-domain + snippet) — fast and dependency-free. + """ + t = np.linspace(0.0, 1.0, n, dtype=np.float32) + r = (0.13572138 + 4.61539260 * t - 42.66032258 * t**2 + + 132.13108234 * t**3 - 152.94239396 * t**4 + + 59.28637943 * t**5) + g = (0.09140261 + 2.19418839 * t + 4.84296658 * t**2 + - 14.18503333 * t**3 + 4.27729857 * t**4 + + 2.82956604 * t**5) + b = (0.10667330 + 12.64194608 * t - 60.58204836 * t**2 + + 110.36276771 * t**3 - 89.90310912 * t**4 + + 27.34824973 * t**5) + rgb = np.clip(np.stack([r, g, b], axis=1), 0.0, 1.0) + rgba = np.concatenate( + [rgb, np.ones((n, 1), dtype=np.float32)], axis=1) + return (rgba * 255.0).astype(np.uint8) + + +TURBO_LUT = _build_turbo_lut(256) + + +class MelSpectrogramArea(Gtk.Widget): + """GTK 4 widget that renders the mel ring buffer as a GPU texture. + + The numpy buffer is colour-mapped to RGBA8 once per redraw, wrapped + in a ``Gdk.MemoryTexture`` and pushed to the snapshot. The GSK + renderer uploads it to the GPU and scales it to the widget size. + + Supports sub-pixel scrolling via :meth:`set_sub_offset`: the texture + is built with one extra column on the right (a hold of the latest + frame) and translated left by ``sub * column_width`` so the newest + column slides smoothly in between data arrivals. + """ + + def __init__(self, mel_buf, vmin, vmax): + super().__init__() + self._mel_buf = mel_buf # shape (frames, bins) + self._vmin = vmin + self._vmax = vmax + self._lut = TURBO_LUT + self._sub = 0.0 + self.set_hexpand(True) + self.set_vexpand(True) + + def set_sub_offset(self, sub): + if sub < 0.0: + sub = 0.0 + elif sub > 1.0: + sub = 1.0 + if sub != self._sub: + self._sub = sub + self.queue_draw() + + def _build_texture(self): + bins = self._mel_buf.shape[1] + frames = self._mel_buf.shape[0] + norm = (self._mel_buf - self._vmin) / (self._vmax - self._vmin) + np.clip(norm, 0.0, 1.0, out=norm) + idx = (norm * 255.0).astype(np.uint8) # (frames, bins) + # Extend on the right by holding the latest column constant so the + # sub-pixel translate has something to reveal at the right edge. + idx_ext = np.empty((frames + 1, bins), dtype=np.uint8) + idx_ext[:frames] = idx + idx_ext[frames] = idx[-1] + idx_img = idx_ext.T[::-1, :] # (bins, frames+1) + rgba = self._lut[idx_img] # (bins, frames+1, 4) + data = rgba.tobytes() + gbytes = GLib.Bytes.new(data) + return Gdk.MemoryTexture.new( + frames + 1, bins, + Gdk.MemoryFormat.R8G8B8A8, + gbytes, + (frames + 1) * 4, + ) + + def do_snapshot(self, snapshot): + w = self.get_width() + h = self.get_height() + if w <= 0 or h <= 0: + return + frames = self._mel_buf.shape[0] + col_w = w / frames + offset_x = -self._sub * col_w + ext_w = w + col_w + clip_rect = Graphene.Rect().init(0.0, 0.0, float(w), float(h)) + draw_rect = Graphene.Rect().init(offset_x, 0.0, float(ext_w), float(h)) + texture = self._build_texture() + snapshot.push_clip(clip_rect) + if hasattr(snapshot, 'append_scaled_texture'): + snapshot.append_scaled_texture( + texture, Gsk.ScalingFilter.LINEAR, draw_rect) + else: + snapshot.append_texture(texture, draw_rect) + snapshot.pop() + + +class CurveStripArea(Gtk.Widget): + """Strip plot rendered with GSK render nodes (GPU path). + + All drawing goes through native ``Gsk`` nodes so the GL/Vulkan + renderer handles compositing on the GPU: + + * background & grid -> ``snapshot.append_color()`` + * curves -> ``snapshot.append_stroke()`` with ``Gsk.Path`` + * tick / title text -> ``snapshot.append_layout()`` + + Requires GTK 4.14+ (for ``Gsk.PathBuilder`` and + ``Gtk.Snapshot.append_stroke``). + """ + + def __init__(self, get_data, *, ymin=None, ymax=None, autoscale=False, + lines=(), background=(0.10, 0.10, 0.12), + yticks=None, label=None, height_request=80): + super().__init__() + self._get_data = get_data + self._ymin = ymin + self._ymax = ymax + self._autoscale = autoscale + self._lines = lines + self._bg = self._mk_rgba((background[0], background[1], background[2], 1.0)) + self._grid_color = self._mk_rgba((0.4, 0.4, 0.45, 0.6)) + self._text_color = self._mk_rgba((0.85, 0.85, 0.85, 0.9)) + self._line_colors = [self._mk_rgba(rgba) for _, rgba in lines] + self._stroke = Gsk.Stroke.new(1.5) + self._stroke.set_line_cap(Gsk.LineCap.ROUND) + self._stroke.set_line_join(Gsk.LineJoin.ROUND) + self._yticks = yticks + self._label = label + self._tick_font = Pango.FontDescription.from_string("sans-serif 9") + self._title_font = Pango.FontDescription.from_string("sans-serif 10") + self._tick_layouts = None # built lazily once we have a Pango context + self._title_layout = None + self._sub = 0.0 + self.set_hexpand(True) + self.set_size_request(-1, height_request) + + def set_sub_offset(self, sub): + if sub < 0.0: + sub = 0.0 + elif sub > 1.0: + sub = 1.0 + if sub != self._sub: + self._sub = sub + self.queue_draw() + + @staticmethod + def _mk_rgba(t): + c = Gdk.RGBA() + c.red, c.green, c.blue, c.alpha = ( + float(t[0]), float(t[1]), float(t[2]), float(t[3])) + return c + + def _ensure_layouts(self): + if self._tick_layouts is not None: + return + pctx = self.get_pango_context() + self._tick_layouts = [] + if self._yticks: + for yv, txt in self._yticks: + layout = Pango.Layout(pctx) + layout.set_font_description(self._tick_font) + layout.set_text(str(txt), -1) + self._tick_layouts.append((yv, layout)) + if self._label: + self._title_layout = Pango.Layout(pctx) + self._title_layout.set_font_description(self._title_font) + self._title_layout.set_text(self._label, -1) + + def _yrange(self, arrays): + if not self._autoscale: + return self._ymin, self._ymax + vmin = min(float(a.min()) for a in arrays if a.size) + vmax = max(float(a.max()) for a in arrays if a.size) + if vmax - vmin < 1e-6: + vmax = vmin + 1.0 + margin = 0.05 * (vmax - vmin) + return vmin - margin, vmax + margin + + @staticmethod + def _build_path(arr, x_scale, h, ymin, yspan): + """Build a stroke path covering [0, n*x_scale] (one extra sample held).""" + builder = Gsk.PathBuilder.new() + n = arr.shape[0] + ys = h - (arr - ymin) / yspan * h + builder.move_to(0.0, float(ys[0])) + for i in range(1, n): + builder.line_to(float(i * x_scale), float(ys[i])) + # Hold last value for one extra column so the slide-in area is filled. + builder.line_to(float(n * x_scale), float(ys[-1])) + return builder.to_path() + + def do_snapshot(self, snapshot): + w = self.get_width() + h = self.get_height() + if w <= 0 or h <= 0: + return + + bg_rect = Graphene.Rect().init(0.0, 0.0, float(w), float(h)) + snapshot.append_color(self._bg, bg_rect) + + data = self._get_data() + arrays = [data[k] for k, _ in self._lines] + if not arrays or arrays[0].size == 0: + return + n = arrays[0].size + ymin, ymax = self._yrange(arrays) + yspan = (ymax - ymin) or 1.0 + x_scale = w / max(n - 1, 1) + offset_x = -self._sub * x_scale + + # Y grid lines as 1px coloured rectangles (GSK color nodes). + if self._yticks: + for yv, _ in self._yticks: + py = h - (yv - ymin) / yspan * h + grid_rect = Graphene.Rect().init(0.0, py - 0.5, float(w), 1.0) + snapshot.append_color(self._grid_color, grid_rect) + + # Curves -> GSK stroke nodes (GPU rasterised), translated for smooth scroll. + clip_rect = Graphene.Rect().init(0.0, 0.0, float(w), float(h)) + snapshot.push_clip(clip_rect) + snapshot.save() + snapshot.translate(Graphene.Point().init(offset_x, 0.0)) + for (key, _), color in zip(self._lines, self._line_colors): + path = self._build_path(data[key], x_scale, h, ymin, yspan) + snapshot.append_stroke(path, self._stroke, color) + snapshot.restore() + snapshot.pop() + + # Text -> Pango layouts via append_layout (no scroll offset). + self._ensure_layouts() + if self._tick_layouts: + for yv, layout in self._tick_layouts: + py = h - (yv - ymin) / yspan * h + _, ext = layout.get_pixel_extents() + ty = max(0.0, min(float(h) - ext.height, py - ext.height - 1.0)) + snapshot.save() + snapshot.translate(Graphene.Point().init(4.0, ty)) + snapshot.append_layout(layout, self._text_color) + snapshot.restore() + + if self._title_layout is not None: + _, ext = self._title_layout.get_pixel_extents() + snapshot.save() + snapshot.translate( + Graphene.Point().init(float(w) - ext.width - 6.0, 2.0)) + snapshot.append_layout(self._title_layout, self._text_color) + snapshot.restore() + + +class MelViewer: + """Top-level controller: capture process + GTK window.""" + + def __init__(self, app, card, device, width): + self.app = app + self.width_frames = width + + # Ring buffer stored as (frames, mel_bins) for cheap shifts along axis 0. + self.mel_buf = np.zeros((width, SOF_NUM_MEL), dtype=np.float32) + self.vad_buf = np.zeros(width, dtype=np.float32) + self.energy_buf = np.zeros(width, dtype=np.float32) + self.noise_buf = np.zeros(width, dtype=np.float32) + + self.recv_frames = 0 + self.prev_speech = None + self._closing = False + self._last_frame_mono = None # timestamp of most recent ingest + self._frame_period = SOF_HOP_SEC # nominal 10 ms per hop + + self._build_ui() + + # Start capture + self.proc = start_crecord(card, device) + self.frame_q = queue.Queue() + self.reader = threading.Thread(target=self._reader_thread, daemon=True) + self.reader.start() + + # Vsync-aligned redraws via the frame clock — fires once per monitor + # refresh while the widget is mapped. Falls back to a periodic timer + # if add_tick_callback isn't available for some reason. + self._tick_id = 0 + self._timer_id = 0 + if hasattr(self.mel_area, 'add_tick_callback'): + self._tick_id = self.mel_area.add_tick_callback(self._on_tick_clock) + else: + self._timer_id = GLib.timeout_add(GUI_REFRESH_MS, self._on_timer) + + print(f"Spectrogram width: {width} frames ({width * SOF_HOP_SEC:.1f}s)") + if self._tick_id: + print("GUI refresh: vsync-aligned via frame clock, " + "sub-pixel scroll enabled") + else: + print(f"GUI refresh: fallback timer at {GUI_REFRESH_MS} ms " + f"({1000.0 / GUI_REFRESH_MS:.0f} FPS)") + print() + + def _build_ui(self): + self.window = Gtk.ApplicationWindow(application=self.app) + self.window.set_title("SOF Mel Spectrogram (compress PCM) — DSP VAD") + self.window.set_default_size(1100, 600) + self.window.connect('close-request', self._on_close) + + box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=2) + box.set_margin_start(4) + box.set_margin_end(4) + box.set_margin_top(4) + box.set_margin_bottom(4) + self.window.set_child(box) + + self.mel_area = MelSpectrogramArea(self.mel_buf, MEL_VMIN, MEL_VMAX) + self.vad_area = CurveStripArea( + lambda: {'vad': self.vad_buf}, + ymin=-0.1, ymax=1.1, + lines=[('vad', (0.2, 0.9, 0.2, 1.0))], + yticks=[(0, 'Silent'), (1, 'Speech')], + label='VAD', + height_request=70, + ) + self.level_area = CurveStripArea( + lambda: {'energy': self.energy_buf, 'noise': self.noise_buf}, + autoscale=True, + lines=[ + ('energy', (1.0, 0.9, 0.2, 1.0)), + ('noise', (0.2, 0.9, 1.0, 1.0)), + ], + label='Level (Y=speech, C=noise)', + height_request=110, + ) + + # Vertical stretch: spectrogram dominates. + self.mel_area.set_vexpand(True) + box.append(self.mel_area) + box.append(self.vad_area) + box.append(self.level_area) + + # --- I/O thread --------------------------------------------------- + def _reader_thread(self): + buf = bytearray() + raw_fd = self.proc.stdout.fileno() + try: + while True: + data = os.read(raw_fd, SOF_FRAME_BYTES * 4) + if not data: + break + buf.extend(data) + while True: + fn, vf, en, no, mi = parse_frame(buf) + if mi is None: + break + self.frame_q.put((fn, vf, en, no, mi)) + except OSError: + pass + self.frame_q.put(None) + + # --- GUI tick ----------------------------------------------------- + def _drain_and_redraw(self): + """Drain queued frames, update sub-pixel offset, queue redraws. + + Returns True while running, False when EOF. + """ + drained = 0 + eof = False + while True: + try: + item = self.frame_q.get_nowait() + except queue.Empty: + break + if item is None: + eof = True + break + fn, vf, en, no, mi = item + self._ingest(fn, vf, en, no, mi) + drained += 1 + if drained >= 64: + break + + # Sub-pixel scroll position: time since last ingest as a fraction of + # the nominal frame period, clamped to [0, 1] so we never overshoot. + if self._last_frame_mono is None: + sub = 0.0 + else: + sub = (time.monotonic() - self._last_frame_mono) / self._frame_period + if sub < 0.0: + sub = 0.0 + elif sub > 1.0: + sub = 1.0 + self.mel_area.set_sub_offset(sub) + self.vad_area.set_sub_offset(sub) + self.level_area.set_sub_offset(sub) + + # If no new data arrived, still queue a redraw so the sub-pixel + # translate advances each vsync. set_sub_offset only does it on + # a change; force it when sub is at 1.0 (no-op) and frames were 0. + if drained == 0 and sub >= 1.0: + # We've slid the maximum distance; nothing to redraw. + pass + + if eof: + self._on_eof() + return False + return True + + def _on_tick_clock(self, _widget, _frame_clock): + return True if self._drain_and_redraw() else False + + def _on_timer(self): + return GLib.SOURCE_CONTINUE if self._drain_and_redraw() \ + else GLib.SOURCE_REMOVE + + def _ingest(self, frame_number, vad_flag, energy, noise, mel_ints): + self.recv_frames += 1 + self._last_frame_mono = time.monotonic() + mel = decode_mel_frame(mel_ints) + speech = vad_flag != 0 + + if speech != self.prev_speech: + t = frame_number * SOF_HOP_SEC + tag = "SPEECH" if speech else "SILENCE" + print(f" [{t:7.2f}s] {tag} (hop {frame_number})", flush=True) + self.prev_speech = speech + + # Scroll left and append new frame on the right. + self.mel_buf[:-1] = self.mel_buf[1:] + self.mel_buf[-1] = mel + self.vad_buf[:-1] = self.vad_buf[1:] + self.vad_buf[-1] = 1.0 if speech else 0.0 + self.energy_buf[:-1] = self.energy_buf[1:] + self.energy_buf[-1] = energy + self.noise_buf[:-1] = self.noise_buf[1:] + self.noise_buf[-1] = noise + + def _on_eof(self): + if self._closing: + return + if self.proc.poll() is None: + self.proc.terminate() + try: + rc = self.proc.wait(timeout=2) + except subprocess.TimeoutExpired: + self.proc.kill() + rc = self.proc.wait() + try: + stderr_out = self.proc.stderr.read().decode(errors='replace') + except Exception: + stderr_out = '' + print(f"\ncrecord exited with code {rc}") + if stderr_out: + print(f"stderr: {stderr_out}") + self.window.close() + + # --- shutdown ----------------------------------------------------- + def _on_close(self, *_args): + self._closing = True + if self._tick_id: + self.mel_area.remove_tick_callback(self._tick_id) + self._tick_id = 0 + if self._timer_id: + GLib.source_remove(self._timer_id) + self._timer_id = 0 + if self.proc.poll() is None: + self.proc.terminate() + try: + self.proc.wait(timeout=3) + except subprocess.TimeoutExpired: + self.proc.kill() + self.proc.wait() + print(f"\nCapture stopped. Received {self.recv_frames} frames.") + return False # allow the window to close + + +def main(): + parser = argparse.ArgumentParser( + description="Live scrolling mel spectrogram viewer " + "from SOF compress PCM capture (GTK 4 / GSK)") + parser.add_argument('--card', '-c', type=int, default=0, + help='ALSA card number (default: 0)') + parser.add_argument('--device', '-d', type=int, default=54, + help='ALSA compress device number (default: 54)') + parser.add_argument('--width', '-w', type=int, default=SPECTROGRAM_WIDTH, + help=f'Spectrogram width in frames (default: {SPECTROGRAM_WIDTH})') + args = parser.parse_args() + + print("=== SOF Mel Spectrogram Viewer (Compress PCM, GTK 4) ===\n") + + # Let Python receive Ctrl-C while the GLib loop is running. + signal.signal(signal.SIGINT, signal.SIG_DFL) + + app = Gtk.Application(application_id='org.sof.mel_spectrogram') + holder = {} + + def on_activate(application): + viewer = MelViewer(application, args.card, args.device, args.width) + viewer.window.present() + holder['viewer'] = viewer + + app.connect('activate', on_activate) + sys.exit(app.run(None)) + + +if __name__ == '__main__': + main() diff --git a/src/audio/mfcc/tune/sof_mel_to_text_live_compress.py b/src/audio/mfcc/tune/sof_mel_to_text_live_compress.py new file mode 100755 index 000000000000..6f0032d241fa --- /dev/null +++ b/src/audio/mfcc/tune/sof_mel_to_text_live_compress.py @@ -0,0 +1,490 @@ +#!/usr/bin/env python3 + +# SPDX-License-Identifier: BSD-3-Clause +# +# Copyright (c) 2026, Intel Corporation. + +"""Live SOF mel capture from compress PCM with DTX-aware Whisper transcription. + +Captures mel frames from ALSA compress device (crecord) with embedded VAD flag. +Frame format: [magic(int32), frame_number(uint32), reserved(int32), + energy(int32), noise_energy(int32), vad_flag(int32), + mel[0..79](int32)] + +With DTX enabled, the DSP sends a configurable number of trailing silence +frames (e.g. 20 = 200ms) after each speech-to-silence transition, then +suppresses further silence. This gives the host enough silence to detect +end-of-speech via a wall-clock patience timer. + +Usage: + python sof_mel_to_text_live_compress.py [--card 0] [--device 54] [--model whisper-medium-int4-ov] +""" + +import argparse +import os +import queue +import struct +import subprocess +import threading +import time +import numpy as np +import openvino as ov +import huggingface_hub as hf_hub +from pathlib import Path + +# SOF compress mel frame format constants (with DSP data header) +SOF_MAGIC_BYTES = struct.pack('<i', 0x6D666363) # ASCII 'mfcc' as int32 +SOF_NUM_HEADER = 6 # magic, frame_number, reserved, energy, noise_energy, vad_flag +SOF_Q_FORMAT = 23 # Q9.23 fixed-point +SOF_NUM_MEL = 80 +SOF_FRAME_INTS = SOF_NUM_HEADER + SOF_NUM_MEL # 86 int32 per frame +SOF_FRAME_BYTES = SOF_FRAME_INTS * 4 # 344 bytes per frame +SOF_HOP_SEC = 0.01 # 10 ms per STFT hop + +# Speech buffering +SILENCE_PATIENCE_S = 0.2 # seconds of silence patience before triggering +MIN_SPEECH_MS = 500 # minimum speech duration to send to Whisper +MIN_SPEECH_FRAMES = MIN_SPEECH_MS // 10 # 50 frames at 10ms/frame +MAX_SPEECH_MS = 60000 # max speech buffer before forced transcription +MAX_SPEECH_FRAMES = MAX_SPEECH_MS // 10 # 6000 frames at 10ms/frame + +# Whisper model constants +WHISPER_FEATURE_SIZE = 80 +WHISPER_NB_MAX_FRAMES = 3000 # 30 seconds at 10ms per frame + + +def decode_mel_frame(raw_ints): + """Convert 80 int32 Q9.23 values to float32 mel coefficients.""" + return raw_ints.astype(np.float32) / (2 ** SOF_Q_FORMAT) + + +# ---------- Whisper inference ---------- + +class WhisperTranscriber: + """Whisper encoder+decoder using OpenVINO, runs in a background thread.""" + + def __init__(self, model_path, encoder_device="NPU", decoder_device="CPU"): + self.model_path = model_path + core = ov.Core() + encoder_xml = str(Path(model_path) / "openvino_encoder_model.xml") + decoder_xml = str(Path(model_path) / "openvino_decoder_model.xml") + # NPU requires static shapes — fix [?,?,3000] to [1,80,3000] + encoder_model = core.read_model(encoder_xml) + encoder_model.reshape({0: [1, WHISPER_FEATURE_SIZE, WHISPER_NB_MAX_FRAMES]}) + self.encoder = core.compile_model(encoder_model, encoder_device) + self.decoder = core.compile_model(decoder_xml, decoder_device) + self._load_tokenizer() + self._busy = False + self._lock = threading.Lock() + + def _load_tokenizer(self): + """Load Whisper tokenizer.""" + try: + from transformers import WhisperTokenizer + self.tokenizer = WhisperTokenizer.from_pretrained(self.model_path) + self._tokenizer_type = "hf" + except ImportError: + import openvino_genai as ov_genai + self.tokenizer = ov_genai.Tokenizer(self.model_path) + self._tokenizer_type = "ov" + + def is_busy(self): + with self._lock: + return self._busy + + def transcribe_async(self, mel_frames, callback): + """Run transcription in a background thread. + + Args: + mel_frames: list of np.ndarray [80] mel frames + callback: function(text) called with result + """ + with self._lock: + if self._busy: + return False + self._busy = True + + t = threading.Thread(target=self._run, args=(mel_frames, callback), + daemon=True) + t.start() + return True + + def _run(self, mel_frames, callback): + try: + text = self._transcribe(mel_frames) + callback(text) + except Exception as e: + print(f" [Whisper ERROR] {e}", flush=True) + finally: + with self._lock: + self._busy = False + + def _transcribe(self, mel_frames): + """Encode mel frames and decode to text.""" + n_frames = len(mel_frames) + if n_frames == 0: + return "" + + # Stack frames into [80, n_frames] + features = np.column_stack(mel_frames).astype(np.float32) + + # Pad to 3000 frames + silence_val = features.min() + padded = np.full((WHISPER_FEATURE_SIZE, WHISPER_NB_MAX_FRAMES), + silence_val, dtype=np.float32) + n = min(n_frames, WHISPER_NB_MAX_FRAMES) + padded[:, :n] = features[:, :n] + + # Encoder + t0 = time.time() + encoder_input = padded.reshape(1, WHISPER_FEATURE_SIZE, WHISPER_NB_MAX_FRAMES) + encoder_req = self.encoder.create_infer_request() + encoder_req.set_tensor("input_features", ov.Tensor(encoder_input)) + encoder_req.infer() + hidden_state = encoder_req.get_tensor("last_hidden_state").data.copy() + t1 = time.time() + print(f" [Whisper] encoder: {t1-t0:.2f}s", flush=True) + + # Decoder: greedy decode + token_ids = self._greedy_decode(hidden_state) + t2 = time.time() + print(f" [Whisper] decoder: {t2-t1:.2f}s ({len(token_ids)} tokens)", + flush=True) + + # Convert to text + text_tokens = [t for t in token_ids if t < 50257] + text = self.tokenizer.decode(text_tokens) + + return text.strip() + + def _greedy_decode(self, hidden_state, max_tokens=448): + """Greedy decoding loop.""" + sot_tokens = [50258, 50259, 50359, 50363] + eos_token = 50257 + + decoder_req = self.decoder.create_infer_request() + input_names = [inp.get_any_name() for inp in self.decoder.inputs] + has_cache_position = "cache_position" in input_names + + decoder_req.set_tensor("encoder_hidden_states", ov.Tensor(hidden_state)) + + # Prefill with SOT tokens + input_ids = np.array([sot_tokens], dtype=np.int64) + beam_idx = np.array([0], dtype=np.int32) + + decoder_req.set_tensor("input_ids", ov.Tensor(input_ids)) + if "beam_idx" in input_names: + decoder_req.set_tensor("beam_idx", ov.Tensor(beam_idx)) + if has_cache_position: + cache_pos = np.arange(len(sot_tokens), dtype=np.int64).reshape(1, -1) + decoder_req.set_tensor("cache_position", ov.Tensor(cache_pos)) + + decoder_req.infer() + logits = decoder_req.get_tensor("logits").data + next_token = int(np.argmax(logits[0, -1, :])) + + generated = [next_token] + position = len(sot_tokens) + + for _ in range(max_tokens - 1): + if next_token == eos_token: + break + + decoder_req.set_tensor("input_ids", + ov.Tensor(np.array([[next_token]], dtype=np.int64))) + if "beam_idx" in input_names: + decoder_req.set_tensor("beam_idx", ov.Tensor(beam_idx)) + if has_cache_position: + decoder_req.set_tensor("cache_position", + ov.Tensor(np.array([[position]], dtype=np.int64))) + + decoder_req.infer() + logits = decoder_req.get_tensor("logits").data + next_token = int(np.argmax(logits[0, -1, :])) + generated.append(next_token) + position += 1 + + return generated + + +# ---------- Frame parser ---------- + +def parse_frame(buf): + """Parse one complete mel frame from a bytearray buffer. + + Frame layout: [magic(4B), frame_number(4B), reserved(4B), energy(4B), + noise_energy(4B), vad_flag(4B), mel[0..79](320B)] = 344 bytes + + In compress PCM mode, each read delivers complete frames with no zero + padding, so we search for magic and extract. + + Mutates buf in-place (deletes consumed bytes). + Returns: (frame_number, vad_flag, mel_ints) or (None, None, None) + """ + idx = buf.find(SOF_MAGIC_BYTES) + if idx < 0: + if len(buf) > 3: + del buf[:-3] + return None, None, None + end = idx + SOF_FRAME_BYTES + if end > len(buf): + del buf[:idx] + return None, None, None + + # Parse header fields + frame_number = struct.unpack_from('<I', buf, idx + 4)[0] + vad_flag = struct.unpack_from('<i', buf, idx + 20)[0] + + # Parse 80 mel coefficients (after 24-byte header) + mel_bytes = bytes(buf[idx + SOF_NUM_HEADER * 4:end]) + mel_ints = np.frombuffer(mel_bytes, dtype=np.int32) + del buf[:end] + return frame_number, vad_flag, mel_ints + + +# ---------- Main capture + transcription loop ---------- + +def try_transcribe(transcriber, speech_buffer, t, on_transcription): + """Attempt to send speech buffer to Whisper. Returns True if sent or discarded.""" + n = len(speech_buffer) + duration = n * SOF_HOP_SEC + + if n < MIN_SPEECH_FRAMES: + print(f" [{t:7.2f}s] Too short ({duration:.1f}s), " + f"discarding {n} frames", flush=True) + return True + + if not transcriber.is_busy(): + print(f" [{t:7.2f}s] Transcribing {n} frames " + f"({duration:.1f}s)...", flush=True) + frames_copy = list(speech_buffer) + transcriber.transcribe_async(frames_copy, on_transcription) + return True + + print(f" [{t:7.2f}s] (Whisper busy, queuing {n} frames)", flush=True) + return False + + +def run_capture(card, device, model_path, encoder_device, decoder_device, + patience=SILENCE_PATIENCE_S): + """Main capture loop: crecord compress PCM → DSP VAD → buffer speech → Whisper. + + With DTX, the FW sends: + - All VAD=1 (speech) frames + - Trailing VAD=0 silence frames (e.g. 20 = 200ms) after speech ends + + A wall-clock patience timer triggers transcription after silence. + If speech resumes within the patience window, buffering continues. + """ + + transcriber = WhisperTranscriber(model_path, encoder_device=encoder_device, + decoder_device=decoder_device) + + crecord_cmd = [ + 'crecord', '-v', + '-c', str(card), + '-d', str(device), + '-I', 'BESPOKE', + '-R', '16000', + '-C', '2', + '-F', 'S32_LE', + '-b', '6144', + '-f', '3', + ] + + # Wrap with stdbuf to disable crecord's stdio buffering. When stdout + # is a pipe, C stdio uses full buffering (~4-8KB). A single DTX + # silence frame (344 bytes) would sit in crecord's buffer until enough + # data accumulates, delaying the patience timer by many seconds. + cmd = ['stdbuf', '-o0'] + crecord_cmd + + print(f"Starting compress capture: {' '.join(crecord_cmd)}") + print(f"VAD source: DSP (embedded in stream, DTX mode)") + print(f"Silence patience: {patience}s") + print(f"Whisper model: {model_path} (encoder: {encoder_device}, decoder: {decoder_device})") + print() + + proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, + bufsize=0) + + # Reader thread feeds parsed frames into a queue to decouple pipe I/O + # from the patience timer in the main thread. + frame_q = queue.Queue() + + def reader_thread(): + buf = bytearray() + raw_fd = proc.stdout.fileno() + try: + while True: + # Use os.read() for unbuffered reads — returns immediately + # when any data is available. Python's read(n) waits for + # exactly n bytes, which delays SILENCE frames until the + # next speech burst fills the buffer. + data = os.read(raw_fd, SOF_FRAME_BYTES * 4) + if not data: + break + buf.extend(data) + while True: + frame_number, vad_flag, frame_ints = parse_frame(buf) + if frame_ints is None: + break + frame_q.put((frame_number, vad_flag, frame_ints)) + except (OSError, ValueError): + pass + frame_q.put(None) # sentinel + + reader = threading.Thread(target=reader_thread, daemon=True) + reader.start() + + recv_frames = 0 + prev_speech = None + last_hop = 0 + + # Speech buffering state + speech_buffer = [] # list of mel frames during speech + silence_time = None # wall-clock time when first VAD=0 arrived + pending_queue = None # queued frames waiting for Whisper to become free + pending_t = 0.0 # timestamp for queued frames + + def on_transcription(text): + if text: + print(f"\n >> \"{text}\"\n", flush=True) + else: + print(" [Whisper] empty result", flush=True) + + def flush_speech(t_now): + """Flush speech buffer to Whisper.""" + nonlocal speech_buffer, silence_time, pending_queue, pending_t + if not speech_buffer: + silence_time = None + return + if not try_transcribe(transcriber, speech_buffer, t_now, + on_transcription): + pending_queue = list(speech_buffer) + pending_t = t_now + speech_buffer.clear() + silence_time = None + + try: + while True: + # Calculate queue timeout based on patience timer + get_timeout = 0.1 # default polling interval + if silence_time is not None: + remaining = patience - (time.monotonic() - silence_time) + get_timeout = max(remaining, 0.01) + + try: + item = frame_q.get(timeout=get_timeout) + except queue.Empty: + # Patience expired — flush speech to Whisper + if silence_time is not None: + elapsed = time.monotonic() - silence_time + if elapsed >= patience: + t = last_hop * SOF_HOP_SEC + flush_speech(t) + + # Drain pending queue when Whisper becomes free + if pending_queue is not None and not transcriber.is_busy(): + print(f" [{pending_t:7.2f}s] Whisper free, sending " + f"{len(pending_queue)} queued frames", flush=True) + transcriber.transcribe_async(pending_queue, on_transcription) + pending_queue = None + continue + + if item is None: + # Reader thread ended (crecord exited) + stderr_out = proc.stderr.read().decode(errors='replace') + rc = proc.wait() + print(f"\ncrecord exited with code {rc}") + if stderr_out: + print(f"stderr: {stderr_out}") + break + + frame_number, vad_flag, frame_ints = item + recv_frames += 1 + last_hop = frame_number + mel = decode_mel_frame(frame_ints) + speech = vad_flag != 0 + t = frame_number * SOF_HOP_SEC + + # Print VAD transitions + if speech != prev_speech: + tag = "SPEECH" if speech else "SILENCE" + print(f" [{t:7.2f}s] {tag} (hop {frame_number}, " + f"received {recv_frames})", flush=True) + prev_speech = speech + + # Drain pending queue when Whisper becomes free + if pending_queue is not None and not transcriber.is_busy(): + print(f" [{pending_t:7.2f}s] Whisper free, sending " + f"{len(pending_queue)} queued frames", flush=True) + transcriber.transcribe_async(pending_queue, on_transcription) + pending_queue = None + + # --- Speech buffering logic --- + if speech: + if len(speech_buffer) >= MAX_SPEECH_FRAMES: + n = len(speech_buffer) + duration = n * SOF_HOP_SEC + print(f" [{t:7.2f}s] Buffer full ({duration:.1f}s), " + f"forcing transcription", flush=True) + flush_speech(t) + + speech_buffer.append(mel.copy()) + silence_time = None # speech resumed, cancel patience timer + + else: + # VAD=0: start patience timer if we have buffered speech. + # Don't refresh if already running so trailing silence + # frames don't extend the wait. + if speech_buffer and silence_time is None: + silence_time = time.monotonic() + + except (KeyboardInterrupt, BrokenPipeError): + pass + finally: + # Flush remaining speech + if speech_buffer: + t = last_hop * SOF_HOP_SEC + flush_speech(t) + if proc.poll() is None: + proc.terminate() + try: + proc.wait(timeout=3) + except subprocess.TimeoutExpired: + proc.kill() + proc.wait() + print(f"\n\nCapture stopped. Received {recv_frames} frames.") + + +def main(): + parser = argparse.ArgumentParser( + description="Live SOF mel capture from compress PCM with DTX-aware " + "Whisper transcription") + parser.add_argument('--card', '-c', type=int, default=0, + help='ALSA card number (default: 0)') + parser.add_argument('--device', '-d', type=int, default=54, + help='ALSA compress device number (default: 54)') + parser.add_argument('--model', '-m', default='whisper-medium-int4-ov', + help='Path to Whisper OpenVINO model directory') + parser.add_argument('--encoder-device', default='NPU', + help='OpenVINO device for encoder (default: NPU)') + parser.add_argument('--decoder-device', default='CPU', + help='OpenVINO device for decoder (default: CPU)') + parser.add_argument('--patience', type=float, default=SILENCE_PATIENCE_S, + help=f'Seconds of silence patience before triggering ' + f'transcription (default: {SILENCE_PATIENCE_S})') + args = parser.parse_args() + + model_id = "OpenVINO/" + os.path.basename(args.model) + if not os.path.isdir(args.model): + print(f"Downloading model {model_id} ...") + hf_hub.snapshot_download(model_id, local_dir=args.model) + + print("=== Live SOF Mel → Whisper Transcription (Compress PCM, DTX) ===\n") + run_capture(args.card, args.device, args.model, args.encoder_device, + args.decoder_device, patience=args.patience) + + +if __name__ == '__main__': + main() diff --git a/src/audio/mfcc/tune/sof_mel_to_text_live_dsp_vad.py b/src/audio/mfcc/tune/sof_mel_to_text_live_dsp_vad.py new file mode 100755 index 000000000000..90941b42822a --- /dev/null +++ b/src/audio/mfcc/tune/sof_mel_to_text_live_dsp_vad.py @@ -0,0 +1,390 @@ +#!/usr/bin/env python3 + +# SPDX-License-Identifier: BSD-3-Clause +# +# Copyright (c) 2026, Intel Corporation. + +"""Live SOF mel capture with DSP VAD-triggered Whisper transcription. + +Captures mel frames from ALSA with embedded VAD flag from the DSP. +Frame format: [magic(int32), frame_number(uint32), reserved(int32), energy(int32), noise_energy(int32), vad_flag(int32), mel[0..79](int32)] +When silence of 100ms is detected after speech, sends the buffered mel +features to Whisper (OpenVINO encoder+decoder) for transcription. +Capture continues running during Whisper inference. + +Usage: + python sof_mel_to_text_live_dsp_vad.py [--device hw:0,47] [--model whisper-medium-int4-ov] +""" + +import argparse +import os +import struct +import subprocess +import threading +import time +import numpy as np +import openvino as ov +import huggingface_hub as hf_hub +from pathlib import Path + +# SOF mel_s32.raw format constants (with DSP data header) +SOF_MAGIC_BYTES = struct.pack('<i', 0x6D666363) # ASCII 'mfcc' as int32 +SOF_NUM_HEADER = 6 # magic, frame_number, reserved, energy, noise_energy, vad_flag +SOF_Q_FORMAT = 23 # Q9.23 fixed-point +SOF_NUM_MEL = 80 +SOF_FRAME_INTS = SOF_NUM_HEADER + SOF_NUM_MEL # 86 int32 per frame +SOF_FRAME_BYTES = SOF_FRAME_INTS * 4 # 344 bytes per frame + +# Speech buffering +SILENCE_TRIGGER_MS = 100 # ms of silence after speech to trigger transcription +SILENCE_TRIGGER_FRAMES = SILENCE_TRIGGER_MS // 10 # 10 frames at 10ms/frame +MIN_SPEECH_MS = 500 # minimum speech duration to send to Whisper +MIN_SPEECH_FRAMES = MIN_SPEECH_MS // 10 # 50 frames at 10ms/frame +MAX_SPEECH_MS = 60000 # max speech buffer before forced transcription +MAX_SPEECH_FRAMES = MAX_SPEECH_MS // 10 # 6000 frames at 10ms/frame + +# Whisper model constants +WHISPER_FEATURE_SIZE = 80 +WHISPER_NB_MAX_FRAMES = 3000 # 30 seconds at 10ms per frame + + +def decode_mel_frame(raw_ints): + """Convert 80 int32 Q9.23 values to float32 mel coefficients.""" + return raw_ints.astype(np.float32) / (2 ** SOF_Q_FORMAT) + + +# ---------- Whisper inference ---------- + +class WhisperTranscriber: + """Whisper encoder+decoder using OpenVINO, runs in a background thread.""" + + def __init__(self, model_path, encoder_device="NPU", decoder_device="CPU"): + self.model_path = model_path + core = ov.Core() + encoder_xml = str(Path(model_path) / "openvino_encoder_model.xml") + decoder_xml = str(Path(model_path) / "openvino_decoder_model.xml") + # NPU requires static shapes — fix [?,?,3000] to [1,80,3000] + encoder_model = core.read_model(encoder_xml) + encoder_model.reshape({0: [1, WHISPER_FEATURE_SIZE, WHISPER_NB_MAX_FRAMES]}) + self.encoder = core.compile_model(encoder_model, encoder_device) + self.decoder = core.compile_model(decoder_xml, decoder_device) + self._load_tokenizer() + self._busy = False + self._lock = threading.Lock() + + def _load_tokenizer(self): + """Load Whisper tokenizer.""" + try: + from transformers import WhisperTokenizer + self.tokenizer = WhisperTokenizer.from_pretrained(self.model_path) + self._tokenizer_type = "hf" + except ImportError: + import openvino_genai as ov_genai + self.tokenizer = ov_genai.Tokenizer(self.model_path) + self._tokenizer_type = "ov" + + def is_busy(self): + with self._lock: + return self._busy + + def transcribe_async(self, mel_frames, callback): + """Run transcription in a background thread. + + Args: + mel_frames: list of np.ndarray [80] mel frames + callback: function(text) called with result + """ + with self._lock: + if self._busy: + return False + self._busy = True + + t = threading.Thread(target=self._run, args=(mel_frames, callback), + daemon=True) + t.start() + return True + + def _run(self, mel_frames, callback): + try: + text = self._transcribe(mel_frames) + callback(text) + except Exception as e: + print(f" [Whisper ERROR] {e}", flush=True) + finally: + with self._lock: + self._busy = False + + def _transcribe(self, mel_frames): + """Encode mel frames and decode to text.""" + n_frames = len(mel_frames) + if n_frames == 0: + return "" + + # Stack frames into [80, n_frames] + features = np.column_stack(mel_frames).astype(np.float32) + + # Pad to 3000 frames + silence_val = features.min() + padded = np.full((WHISPER_FEATURE_SIZE, WHISPER_NB_MAX_FRAMES), + silence_val, dtype=np.float32) + n = min(n_frames, WHISPER_NB_MAX_FRAMES) + padded[:, :n] = features[:, :n] + + # Encoder + t0 = time.time() + encoder_input = padded.reshape(1, WHISPER_FEATURE_SIZE, WHISPER_NB_MAX_FRAMES) + encoder_req = self.encoder.create_infer_request() + encoder_req.set_tensor("input_features", ov.Tensor(encoder_input)) + encoder_req.infer() + hidden_state = encoder_req.get_tensor("last_hidden_state").data.copy() + t1 = time.time() + print(f" [Whisper] encoder: {t1-t0:.2f}s", flush=True) + + # Decoder: greedy decode + token_ids = self._greedy_decode(hidden_state) + t2 = time.time() + print(f" [Whisper] decoder: {t2-t1:.2f}s ({len(token_ids)} tokens)", + flush=True) + + # Convert to text + text_tokens = [t for t in token_ids if t < 50257] + text = self.tokenizer.decode(text_tokens) + + return text.strip() + + def _greedy_decode(self, hidden_state, max_tokens=448): + """Greedy decoding loop.""" + sot_tokens = [50258, 50259, 50359, 50363] + eos_token = 50257 + + decoder_req = self.decoder.create_infer_request() + input_names = [inp.get_any_name() for inp in self.decoder.inputs] + has_cache_position = "cache_position" in input_names + + decoder_req.set_tensor("encoder_hidden_states", ov.Tensor(hidden_state)) + + # Prefill with SOT tokens + input_ids = np.array([sot_tokens], dtype=np.int64) + beam_idx = np.array([0], dtype=np.int32) + + decoder_req.set_tensor("input_ids", ov.Tensor(input_ids)) + if "beam_idx" in input_names: + decoder_req.set_tensor("beam_idx", ov.Tensor(beam_idx)) + if has_cache_position: + cache_pos = np.arange(len(sot_tokens), dtype=np.int64).reshape(1, -1) + decoder_req.set_tensor("cache_position", ov.Tensor(cache_pos)) + + decoder_req.infer() + logits = decoder_req.get_tensor("logits").data + next_token = int(np.argmax(logits[0, -1, :])) + + generated = [next_token] + position = len(sot_tokens) + + for _ in range(max_tokens - 1): + if next_token == eos_token: + break + + decoder_req.set_tensor("input_ids", + ov.Tensor(np.array([[next_token]], dtype=np.int64))) + if "beam_idx" in input_names: + decoder_req.set_tensor("beam_idx", ov.Tensor(beam_idx)) + if has_cache_position: + decoder_req.set_tensor("cache_position", + ov.Tensor(np.array([[position]], dtype=np.int64))) + + decoder_req.infer() + logits = decoder_req.get_tensor("logits").data + next_token = int(np.argmax(logits[0, -1, :])) + generated.append(next_token) + position += 1 + + return generated + + +# ---------- Frame parser ---------- + +def find_frame_in_buffer(buf): + """Find the first complete mel frame with data header in a bytearray. + + Frame layout: [magic(4B), frame_number(4B), reserved(4B), energy(4B), + noise_energy(4B), vad_flag(4B), mel[0..79](320B)] = 344 bytes + Mutates buf in-place (deletes consumed bytes). + Returns: (vad_flag, mel_ints) or (None, None) + """ + idx = buf.find(SOF_MAGIC_BYTES) + if idx < 0: + if len(buf) > 3: + del buf[:-3] + return None, None + end = idx + SOF_FRAME_BYTES + if end > len(buf): + del buf[:idx] + return None, None + # Parse vad_flag at offset 20 (after magic + frame_number + reserved + energy + noise_energy) + vad_flag = struct.unpack_from('<i', buf, idx + 20)[0] + # Parse 80 mel coefficients (after 24-byte header) + mel_bytes = bytes(buf[idx + SOF_NUM_HEADER * 4 : end]) + mel_ints = np.frombuffer(mel_bytes, dtype=np.int32) + del buf[:end] + return vad_flag, mel_ints + + +# ---------- Main capture + transcription loop ---------- + +def run_capture(device, rate, model_path, encoder_device, decoder_device): + """Main capture loop: ALSA → DSP VAD → buffer speech → Whisper.""" + + transcriber = WhisperTranscriber(model_path, encoder_device=encoder_device, + decoder_device=decoder_device) + + cmd = [ + 'arecord', '-D', device, '-f', 'S32_LE', '-c', '2', + '-r', str(rate), '-t', 'raw', '--buffer-size', '8192', + ] + + print(f"Starting capture: {' '.join(cmd)}") + print(f"VAD source: DSP (embedded in stream)") + print(f"Silence trigger: {SILENCE_TRIGGER_MS}ms ({SILENCE_TRIGGER_FRAMES} frames)") + print(f"Whisper model: {model_path} (encoder: {encoder_device}, decoder: {decoder_device})") + print() + + proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + + buf = bytearray() + read_chunk = SOF_FRAME_BYTES * 4 + frame_num = 0 + prev_speech = None + + # Speech buffering state + speech_buffer = [] # list of mel frames during speech + silence_counter = 0 # consecutive silence frames after speech + was_speaking = False # True if we have buffered speech frames + + def on_transcription(text): + if text: + print(f"\n >> \"{text}\"\n", flush=True) + else: + print(" [Whisper] empty result", flush=True) + + try: + while True: + data = proc.stdout.read(read_chunk) + if not data: + rc = proc.poll() + if rc is not None: + stderr_out = proc.stderr.read().decode(errors='replace') + print(f"\narecord exited with code {rc}") + if stderr_out: + print(f"stderr: {stderr_out}") + break + continue + + buf.extend(data) + + while True: + vad_flag, frame_ints = find_frame_in_buffer(buf) + if frame_ints is None: + break + + frame_num += 1 + mel = decode_mel_frame(frame_ints) + speech = vad_flag != 0 + + # Print VAD transitions + if speech != prev_speech: + t = frame_num * 0.01 + tag = "SPEECH" if speech else "SILENCE" + print(f" [{t:7.2f}s] {tag}", flush=True) + prev_speech = speech + + # --- Speech buffering logic --- + if speech: + if len(speech_buffer) >= MAX_SPEECH_FRAMES: + n = len(speech_buffer) + duration = n * 0.01 + t = frame_num * 0.01 + print(f" [{t:7.2f}s] Buffer full ({duration:.1f}s), " + f"forcing transcription of {n} frames", + flush=True) + if not transcriber.is_busy(): + frames_copy = list(speech_buffer) + transcriber.transcribe_async( + frames_copy, on_transcription) + else: + print(f" [{t:7.2f}s] (Whisper busy, " + f"dropping {n} frames)", flush=True) + speech_buffer.clear() + speech_buffer.append(mel.copy()) + silence_counter = 0 + was_speaking = True + else: + if was_speaking: + silence_counter += 1 + if silence_counter >= SILENCE_TRIGGER_FRAMES: + n = len(speech_buffer) + duration = n * 0.01 + t = frame_num * 0.01 + + if n < MIN_SPEECH_FRAMES: + # Too short — discard + speech_buffer.clear() + silence_counter = 0 + was_speaking = False + continue + + # Silence threshold reached — send to Whisper + print(f" [{t:7.2f}s] Transcribing {n} frames " + f"({duration:.1f}s)...", flush=True) + + if not transcriber.is_busy(): + frames_copy = list(speech_buffer) + transcriber.transcribe_async( + frames_copy, on_transcription) + else: + print(f" [{t:7.2f}s] (Whisper busy, " + f"dropping {n} frames)", flush=True) + + speech_buffer.clear() + silence_counter = 0 + was_speaking = False + + except (KeyboardInterrupt, BrokenPipeError): + pass + finally: + if proc.poll() is None: + proc.terminate() + try: + proc.wait(timeout=3) + except subprocess.TimeoutExpired: + proc.kill() + proc.wait() + print("\n\nCapture stopped.") + + +def main(): + parser = argparse.ArgumentParser( + description="Live SOF mel capture with DSP VAD-triggered Whisper transcription") + parser.add_argument('--device', '-D', default='hw:0,47', + help='ALSA capture device (default: hw:0,47)') + parser.add_argument('--rate', '-r', type=int, default=16000, + help='Sample rate for arecord (default: 16000)') + parser.add_argument('--model', '-m', default='whisper-medium-int4-ov', + help='Path to Whisper OpenVINO model directory') + parser.add_argument('--encoder-device', default='NPU', + help='OpenVINO device for encoder (default: NPU)') + parser.add_argument('--decoder-device', default='CPU', + help='OpenVINO device for decoder (default: CPU)') + args = parser.parse_args() + model_id = "OpenVINO/" + os.path.basename(args.model) + if not os.path.isdir(args.model): + print(f"Downloading model {model_id} ...") + hf_hub.snapshot_download(model_id, local_dir=args.model) + + print("=== Live SOF Mel → Whisper Transcription (DSP VAD) ===\n") + run_capture(args.device, args.rate, args.model, args.encoder_device, + args.decoder_device) + + +if __name__ == '__main__': + main() 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/mic_privacy_manager/mic_privacy_manager_intel.c b/src/audio/mic_privacy_manager/mic_privacy_manager_intel.c index 5120c2ab4b59..a5ff86a04fd1 100644 --- a/src/audio/mic_privacy_manager/mic_privacy_manager_intel.c +++ b/src/audio/mic_privacy_manager/mic_privacy_manager_intel.c @@ -10,6 +10,7 @@ #include <sof/audio/audio_stream.h> #include <sof/audio/buffer.h> #include <sof/audio/mic_privacy_manager.h> +#include <sof/lib/memory.h> const struct device *mic_priv_dev; @@ -88,10 +89,12 @@ void mic_privacy_enable_dmic_irq(bool enable_irq) } } -int mic_privacy_manager_init(void) +__cold int mic_privacy_manager_init(void) { mic_priv_dev = DEVICE_DT_GET(DT_NODELABEL(mic_privacy)); + assert_can_be_cold(); + if (!mic_priv_dev) return -EINVAL; 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..78cea04daf19 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->pipeline->pipeline_id, eos_detected, source_avail, sinks_free); - ipc_msg_send(notify, notify->tx_data, false); } } } @@ -423,8 +412,10 @@ static int mixin_process(struct processing_module *mod, frames_to_copy = MIN(source_avail_frames, sinks_free_frames); bytes_to_consume = frames_to_copy * source_get_frame_bytes(sources[0]); - source_get_data(sources[0], bytes_to_consume, (const void **)&source_ptr.ptr, - (const void **)&source_ptr.buf_start, &buf_size); + ret = source_get_data(sources[0], bytes_to_consume, (const void **)&source_ptr.ptr, + (const void **)&source_ptr.buf_start, &buf_size); + if (ret < 0) + return ret; source_ptr.buf_end = (uint8_t *)source_ptr.buf_start + buf_size; } else { /* if source does not produce any data -- do NOT block mixing but generate @@ -469,8 +460,13 @@ static int mixin_process(struct processing_module *mod, size_t free_bytes = sink_get_free_size(sink); size_t buf_size; - sink_get_buffer(sink, free_bytes, &mixout_data->acquired_buf.ptr, - &mixout_data->acquired_buf.buf_start, &buf_size); + ret = sink_get_buffer(sink, free_bytes, &mixout_data->acquired_buf.ptr, + &mixout_data->acquired_buf.buf_start, &buf_size); + /* on failed acquire, skip this mixout and keep mixing the rest; + * source is consumed once after the loop so it is not reprocessed + */ + if (ret < 0) + continue; mixout_data->acquired_buf.buf_end = (uint8_t *)mixout_data->acquired_buf.buf_start + buf_size; mixout_data->acquired_buf_free_frames = @@ -524,7 +520,7 @@ static int mixout_process(struct processing_module *mod, uint32_t frames_to_produce = INT32_MAX; uint32_t bytes_to_produce; struct pending_frames *pending_frames; - int i; + int i, ret; comp_dbg(dev, "entry"); @@ -585,8 +581,11 @@ static int mixout_process(struct processing_module *mod, if (!md->acquired_buf.ptr) { size_t buf_size; - sink_get_buffer(sinks[0], bytes_to_produce, &md->acquired_buf.ptr, - &md->acquired_buf.buf_start, &buf_size); + ret = sink_get_buffer(sinks[0], bytes_to_produce, &md->acquired_buf.ptr, + &md->acquired_buf.buf_start, &buf_size); + if (ret < 0) + return ret; + md->acquired_buf.buf_end = (uint8_t *)md->acquired_buf.buf_start + buf_size; } @@ -718,7 +717,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) @@ -805,9 +806,9 @@ static int mixout_prepare(struct processing_module *mod, return 0; } -static int mixout_bind(struct processing_module *mod, struct bind_info *bind_data) +static int mixout_bind(struct processing_module *mod, const struct bind_info *bind_data) { - struct ipc4_module_bind_unbind *bu; + const struct ipc4_module_bind_unbind *bu; struct comp_dev *mixin; struct pending_frames *pending_frames; int src_id; @@ -852,9 +853,9 @@ static int mixout_bind(struct processing_module *mod, struct bind_info *bind_dat return 0; } -static int mixout_unbind(struct processing_module *mod, struct bind_info *unbind_data) +static int mixout_unbind(struct processing_module *mod, const struct bind_info *unbind_data) { - struct ipc4_module_bind_unbind *bu; + const struct ipc4_module_bind_unbind *bu; struct comp_dev *mixin; struct pending_frames *pending_frames; int src_id; @@ -1051,9 +1052,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.h b/src/audio/mixin_mixout/mixin_mixout.h index 97aa0e12cdab..5cfb30f26c43 100644 --- a/src/audio/mixin_mixout/mixin_mixout.h +++ b/src/audio/mixin_mixout/mixin_mixout.h @@ -99,13 +99,6 @@ struct ipc4_mixer_mode_config { struct ipc4_mixer_mode_sink_config mixer_mode_sink_configs[1]; } __packed __aligned(4); -/* Pointer to data in circular buffer together with buffer boundaries */ -struct cir_buf_ptr { - void *buf_start; - void *buf_end; - void *ptr; -}; - /** * \brief mixin processing function interface */ 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..ca9ffbd8f787 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,32 @@ if(zephyr) ### Zephyr ### zephyr_library_import(xa_mp3_enc ${CONFIG_CADENCE_CODEC_MP3_ENC_LIB}) endif() + zephyr_library_sources_ifdef(CONFIG_SOF_COMPRESS_CODEC_PCM_DEC + module/compress_other/xa_pcm_dec.c) + + 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 +76,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 +151,24 @@ if(NOT CONFIG_COMP_MODULE_SHARED_LIBRARY_BUILD) endif() + if(CONFIG_SOF_COMPRESS_CODEC_PCM_DEC) + add_subdirectory(module/compress_other) + 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..fe8425ae5467 100644 --- a/src/audio/module_adapter/Kconfig +++ b/src/audio/module_adapter/Kconfig @@ -13,17 +13,6 @@ menu "Processing modules" containers to allocate at once is selected by this config option. - config MODULE_MEMORY_API_DEBUG - bool "Turn on memory API thread safety checks" - default y if DEBUG - help - The Module Memory API structures are not protected - by locks. This is because the initialization, - allocation, and freeing of resources should always - be done in the same thread. This option adds an - assert to make sure no other thread makes such - operations. - config CADENCE_CODEC bool "Cadence codec" help @@ -173,8 +162,32 @@ if CADENCE_CODEC This option is a string and takes the full name of the SRC library binary. endif + config SOF_COMPRESS_CODEC_PCM_DEC + bool "SOF PCM (WAV) decoder" + help + Select to enable the PCM (WAV) passthrough decoder. It plugs + into the Cadence codec wrapper but is an open-source + implementation; no Cadence binary is required. To use it, + build tinycompress cplay with the --enable-pcm option. + 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/module_initial_settings_concrete.cpp b/src/audio/module_adapter/iadk/module_initial_settings_concrete.cpp index 82e1e02f5e96..83ac99a07835 100644 --- a/src/audio/module_adapter/iadk/module_initial_settings_concrete.cpp +++ b/src/audio/module_adapter/iadk/module_initial_settings_concrete.cpp @@ -54,11 +54,11 @@ ModuleInitialSettingsConcrete::ModuleInitialSettingsConcrete(DwordArray const &c const size_t computed_msg_size = sizeof(CompoundCfg) - /* CompoundCfg already contains one InputPinFormat and - * one InputPinFormat + * one OutputPinFormat */ (sizeof(InputPinFormat) + sizeof(OutputPinFormat)) + unvalidated_compound_cfg->cfg_ext.nb_input_pins*sizeof(InputPinFormat) + - unvalidated_compound_cfg->cfg_ext.nb_output_pins*sizeof(InputPinFormat); + unvalidated_compound_cfg->cfg_ext.nb_output_pins*sizeof(OutputPinFormat); /* check size consistency */ if (ipc_msg_size != computed_msg_size) { diff --git a/src/audio/module_adapter/iadk/system_agent.cpp b/src/audio/module_adapter/iadk/system_agent.cpp index 860d5b1b279b..7f5bc63a9559 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, @@ -85,10 +87,13 @@ int SystemAgent::CheckIn(ProcessingModuleFactoryInterface& module_factory, void *obfuscated_parent_ppl, void **obfuscated_modinst_p) { + ErrorCode::Type error; IoPinsInfo pins_info; const dsp_fw::DwordArray& cfg_ipc_msg = *reinterpret_cast<const dsp_fw::DwordArray*>(obfuscated_mod_cfg); ModuleInitialSettingsConcrete settings(cfg_ipc_msg); + if (!settings.IsParsable()) + return -EINVAL; ProcessingModulePrerequisites prerequisites; module_factory.GetPrerequisites(prerequisites); @@ -102,13 +107,17 @@ int SystemAgent::CheckIn(ProcessingModuleFactoryInterface& module_factory, (prerequisites.input_pins_count > INPUT_PIN_COUNT) || (prerequisites.output_pins_count < 1) || (prerequisites.output_pins_count > OUTPUT_PIN_COUNT)) - return -1; + return -EINVAL; /* Deduce BaseModuleCfgExt if it was not part of the INIT_INSTANCE IPC message */ settings.DeduceBaseModuleCfgExt(prerequisites.input_pins_count, prerequisites.output_pins_count); - module_factory.Create(*this, module_placeholder, ModuleInitialSettings(settings), pins_info); + error = module_factory.Create(*this, module_placeholder, ModuleInitialSettings(settings), + pins_info); + if (error != ErrorCode::NO_ERROR) + return -EINVAL; + IadkModuleAdapter& module_adapter = *reinterpret_cast<IadkModuleAdapter*>(module_handle_); *obfuscated_modinst_p = &module_adapter; reinterpret_cast<intel_adsp::ProcessingModuleInterface*>(module_placeholder)->Init(); @@ -124,16 +133,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..485bd7f157c3 --- /dev/null +++ b/src/audio/module_adapter/library/userspace_proxy.c @@ -0,0 +1,944 @@ +// 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/llext_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; + work_item->params.mod = NULL; + 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_cache_attr(ipc_req_buf) | K_MEM_PARTITION_P_RO_U_RO, + }; + int ret = 0, ret2; + + params->cmd = cmd; + +#if !IS_ENABLED(CONFIG_SOF_USERSPACE_MOD_IPC_BY_DP_THREAD) + /* The worker must not run while its CPU affinity and memory domain are reconfigured. */ + k_thread_suspend(worker.thread_id); + +#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); + k_thread_resume(worker.thread_id); + return ret; + } +#elif CONFIG_CORE_COUNT > 1 +#error "CONFIG_SCHED_CPU_MASK is required" +#endif +#endif + + 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); +#if !IS_ENABLED(CONFIG_SOF_USERSPACE_MOD_IPC_BY_DP_THREAD) + k_thread_resume(worker.thread_id); +#endif + 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); + k_thread_resume(worker.thread_id); + if (ret < 0) { + tr_err(&userspace_proxy_tr, "Failed to switch memory domain, error: %d", ret); + goto done; + } + + 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); + + /* When a new memory domain is created, only the "factory" entries from the L2 page + * tables are copied. Memory that was dynamically mapped during firmware execution + * will not be accessible from the new domain. The k_heap structure (drv->user_heap) + * resides in such dynamically mapped memory, so we must explicitly add a partition + * for it to ensure that syscalls can access this structure from the userspace domain. + */ + struct k_mem_partition heap_struct_part = { + .attr = K_MEM_PARTITION_P_RW_U_NA | + user_get_partition_cache_attr(POINTER_TO_UINT(drv->user_heap)) + }; + + k_mem_region_align(&heap_struct_part.start, &heap_struct_part.size, + POINTER_TO_UINT(drv->user_heap), + sizeof(*drv->user_heap), CONFIG_MM_DRV_PAGE_SIZE); + + tr_dbg(&userspace_proxy_tr, "Heap struct partition %#lx + %zx, attr = %u", + heap_struct_part.start, heap_struct_part.size, heap_struct_part.attr); + +#if defined(CONFIG_SOF_ZEPHYR_HEAP_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 CONFIG_SOF_ZEPHYR_HEAP_CACHED + &heap_cached_part, +#endif + &heap_part, + &heap_struct_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_cache_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_cache_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 agent_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; + + if (agent_fn) + ret = userspace_proxy_add_sections(context, agent_params->instance_id, manifest); + else + /* llext modules do not use the system agent. */ + ret = llext_manager_add_domain(agent_params->module_id, domain); + + 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, agent_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_cache_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, const 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, const 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..672cef86c0a2 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, @@ -101,371 +74,49 @@ static struct cadence_api cadence_api_table[] = { .api = xa_src_pp, }, #endif +#ifdef CONFIG_SOF_COMPRESS_CODEC_PCM_DEC + { + .id = SOF_COMPRESS_CODEC_PCM_DEC_ID, + .api = xa_pcm_dec, + }, +#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: return CADENCE_CODEC_VORBIS_DEC_ID; + case SND_AUDIOCODEC_PCM: + return SOF_COMPRESS_CODEC_PCM_DEC_ID; default: return -EINVAL; } } -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; - } - } - - /* 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 + 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]); -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 +131,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 +139,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 +163,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 +207,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); +} + +unsigned 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: @@ -588,6 +244,22 @@ static int cadence_codec_get_samples(struct processing_module *mod) return 1152; case CADENCE_CODEC_AAC_DEC_ID: return 1024; + case SOF_COMPRESS_CODEC_PCM_DEC_ID: { + /* + * xa_pcm_dec has no fixed frame size: it can produce up to the + * full configured output buffer per call. Report the upper + * bound (ceiling division) so the IPC3 free-space gate covers + * what the codec may actually write even when out_buff_size is + * not an exact multiple of frame_bytes. + */ + struct module_data *codec = &mod->priv; + uint32_t frame_bytes = mod->stream_params->sample_container_bytes * + mod->stream_params->channels; + + if (!frame_bytes) + return 0; + return (codec->mpd.out_buff_size + frame_bytes - 1) / frame_bytes; + } default: break; } @@ -595,35 +267,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 +299,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 +321,329 @@ 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); - comp_dbg(dev, "cadence_codec_prepare() start"); + mod_free(mod, cd->setup_cfg.data); + + cadence_codec_free_memory_tables(mod); + mod_free(mod, cd->mem_tabs); + + mod_free(mod, cd->self); + mod_free(mod, cd); + return 0; +} - ret = cadence_codec_post_init(mod); - if (ret) +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 = 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); + comp_err(dev, "runtime config apply failed with error %x: ", 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; - } + comp_dbg(dev, "config applied"); - 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; + return 0; +} + +int cadence_codec_apply_params(struct processing_module *mod, int size, void *data) +{ + struct module_data *codec = &mod->priv; + struct comp_dev *dev = mod->dev; + struct cadence_codec_data *cd = codec->private; + struct module_param *param; + uint16_t param_id; + uint16_t codec_id; + int ret; + + /* 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; + + if (size < (int)sizeof(*param)) { + comp_err(dev, "param header truncated, %d bytes left", size); + return -EINVAL; + } + + /* param->size covers the whole record and must fit */ + if (param->size <= sizeof(*param) || param->size > (uint32_t)size) { + comp_err(dev, "invalid param size %u, %d bytes left", + param->size, size); + return -EINVAL; + } + + 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, "allocated %d bytes for memtabs", mem_tabs_size); + return 0; +} + +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; + + ret = cadence_codec_resolve_api(mod); + if (ret < 0) + return ret; - API_CALL(cd, XA_API_CMD_SET_MEMTABS_PTR, 0, cd->mem_tabs, 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_prepare() error %x: failed to set memtabs", - ret); - goto free; + comp_err(dev, "failed to get lib name error: %x: ", ret); + return ret; } - - ret = init_memory_tables(mod); + /* 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_prepare() error %x: failed to init memory tables", - ret); - goto free; + comp_err(dev, "failed to get lib object size 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); + /* 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; + } + + 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_init_process() error %x: failed to get lib init status", - ret); + mod_free(mod, cd->self); 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_resolve_api_with_id(struct processing_module *mod, uint32_t codec_id, + uint32_t direction) { - struct comp_buffer *local_buff; + struct cadence_codec_data *cd = module_get_private_data(mod); struct comp_dev *dev = mod->dev; - struct module_data *codec = &mod->priv; - 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; - int ret; + uint32_t api_id; + uint32_t n_apis = cadence_api_table_size(); + xa_codec_func_t *api = NULL; + int i; - if (!cadence_codec_deep_buff_allowed(mod)) { - mod->deep_buff_bytes = 0; + api_id = cadence_codec_get_api_id(codec_id, direction); + if (api_id < 0) + return api_id; + + /* 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; + } } - /* 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; + /* 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; - 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; + return 0; +} - ret = cadence_codec_init_process(mod); - if (ret) - return ret; +static void cadence_store_api_error_code(struct comp_dev *dev, + XA_ERRORCODE *api_error_code, + int ret, const char *cmd_name) +{ + if (!api_error_code) + return; - remaining -= codec->mpd.consumed; - input_buffers[0].consumed = codec->mpd.consumed; - } + 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); - /* 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; + *api_error_code = ret; +} + +int cadence_codec_process_data(struct processing_module *mod, + XA_ERRORCODE *api_error_code) +{ + 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; - /* Proceed only if we have enough data to fill the module buffer completely */ - if (remaining < codec->mpd.in_buff_size) - return -ENODATA; + if (api_error_code) + *api_error_code = 0; - 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; + if (codec->mpd.eos_reached) { + codec->mpd.produced = 0; + codec->mpd.consumed = 0; - comp_dbg(dev, "cadence_codec_process() start"); + return 0; + } + + 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, "cadence_codec_process() error %x: failed to set size of input data", - ret); + 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, "cadence_codec_process() error %x: processing failed", - ret); + comp_err(dev, "done query failed with error: %x", ret); return ret; } - comp_warn(dev, "cadence_codec_process() nonfatal error %x", ret); } API_CALL(cd, XA_API_CMD_GET_OUTPUT_BYTES, 0, &codec->mpd.produced, ret); if (ret != LIB_NO_ERROR) { - comp_err(dev, "cadence_codec_process() error %x: could not get produced bytes", + comp_err(dev, "could not get produced bytes, error %x:", 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_process() error %x: could not get consumed bytes", - ret); + comp_err(dev, "could not get consumed bytes, error: %x", 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; + 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; - comp_dbg(dev, "cadence_codec_process() done"); + if (done) + codec->mpd.eos_reached = true; + } return 0; } -static int cadence_codec_reset(struct processing_module *mod) +void cadence_copy_data_from_buffer(void *dest, const void *buffer_ptr, size_t bytes_to_copy, + size_t buffer_size, void const *buffer_start) { - 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); - - /* 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; - - rfree(cd->self); - cd->self = NULL; - - return ret; -} + size_t bytes_to_end = (size_t)((const uint8_t *)buffer_start + + buffer_size - (const uint8_t *)buffer_ptr); -static int cadence_codec_free(struct processing_module *mod) -{ - struct cadence_codec_data *cd = module_get_private_data(mod); + if (bytes_to_end >= bytes_to_copy) { + /* No wrap, copy directly */ + memcpy_s(dest, bytes_to_copy, buffer_ptr, bytes_to_copy); + return; + } - rfree(cd->setup_cfg.data); - mod_free_all(mod); - rfree(cd->self); - rfree(cd); - return 0; + /* 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_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) +void cadence_copy_data_to_buffer(void *buffer_ptr, size_t bytes_to_copy, + size_t buffer_size, void *buffer_start, + const void *src) { - struct module_data *md = &mod->priv; - struct comp_dev *dev = mod->dev; - int 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; + size_t bytes_to_end = (size_t)((uint8_t *)buffer_start + + buffer_size - (uint8_t *)buffer_ptr); - /* whole configuration received, apply it now */ - ret = cadence_codec_apply_config(mod); - if (ret) { - comp_err(dev, "error %x: runtime config apply failed", - ret); - return ret; + if (bytes_to_end >= bytes_to_copy) { + /* No wrap, copy directly */ + memcpy_s(buffer_ptr, bytes_to_copy, src, bytes_to_copy); + return; } - comp_dbg(dev, "config applied"); - - return 0; + /* Wrap occurs, copy in two parts */ + memcpy_s(buffer_ptr, bytes_to_end, src, bytes_to_end); + memcpy_s(buffer_start, bytes_to_copy - bytes_to_end, + (const uint8_t *)src + bytes_to_end, bytes_to_copy - bytes_to_end); } - -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_ipc3.c b/src/audio/module_adapter/module/cadence_ipc3.c new file mode 100644 index 000000000000..c9a6f7705062 --- /dev/null +++ b/src/audio/module_adapter/module/cadence_ipc3.c @@ -0,0 +1,339 @@ +// 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 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 output_bytes = cadence_codec_get_samples(mod) * + mod->stream_params->sample_container_bytes * + mod->stream_params->channels; + size_t remaining = source_get_data_available(sources[0]); + const void *source_buffer_start, *src_ptr; + void *sink_ptr, *sink_buffer_start; + size_t src_bytes, sink_bytes; + 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 (remaining < codec->mpd.in_buff_size) { + comp_dbg(dev, "not enough data to process"); + return -ENODATA; + } + + if (!codec->mpd.init_done) { + ret = source_get_data(sources[0], codec->mpd.in_buff_size, &src_ptr, + &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) { + source_release_data(sources[0], 0); + return ret; + } + + remaining -= codec->mpd.consumed; + source_release_data(sources[0], codec->mpd.consumed); + } + + /* do not proceed with processing if not enough free space left in the sink buffer */ + if (sink_get_free_size(sinks[0]) < 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; + + ret = source_get_data(sources[0], codec->mpd.in_buff_size, &src_ptr, &source_buffer_start, + &src_bytes); + if (ret) + 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; + + comp_dbg(dev, "cadence_codec_process() start"); + + ret = cadence_codec_process_data(mod, NULL); + if (ret) { + source_release_data(sources[0], 0); + return ret; + } + + source_release_data(sources[0], codec->mpd.consumed); + + ret = sink_get_buffer(sinks[0], codec->mpd.produced, &sink_ptr, &sink_buffer_start, + &sink_bytes); + if (ret) { + comp_err(dev, "cannot get sink buffer"); + return ret; + } + + /* Copy the produced samples into the output buffer */ + cadence_copy_data_to_buffer(sink_ptr, codec->mpd.produced, sink_bytes, + sink_buffer_start, codec->mpd.out_buff); + + sink_commit_buffer(sinks[0], 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 = 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..763272964d61 --- /dev/null +++ b/src/audio/module_adapter/module/cadence_ipc4.c @@ -0,0 +1,587 @@ +// 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; + case SOF_COMPRESS_CODEC_PCM_DEC_ID: + /* No configuration needed for PCM decoder */ + return 0; + default: + break; + } + + comp_err(mod->dev, "Unsupported codec API ID: %u", cd->api_id); + return -EINVAL; +} + +static struct ipc_msg *cadence_codec_notification_init(struct processing_module *mod) +{ + struct comp_dev *dev = mod->dev; + struct comp_ipc_config *ipc_config = &dev->ipc_config; + struct sof_ipc4_notify_module_data *msg_module_data; + union ipc4_notification_header primary; + struct ipc_msg *msg; + + primary.dat = 0; + 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(primary.dat, 0, sizeof(*msg_module_data)); + if (!msg) + return NULL; + + 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; + + return msg; +} + +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; + cd->msg = cadence_codec_notification_init(mod); + if (!cd->msg) { + comp_err(dev, "failed to allocate IPC notification template"); + ret = -ENOMEM; + goto free_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; + uint32_t direction; + + /* the init payload holds the codec params followed by the + * direction word; validate the size up front before using it + */ + if (size < (int)(sizeof(struct snd_codec) + sizeof(uint32_t))) { + comp_err(dev, "setup config too small: %d", size); + ret = -EINVAL; + goto free_cd; + } + + 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_notification; + } + + 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; copy it out rather than + * dereferencing a possibly unaligned uint32_t pointer + */ + init_bytes = (uint8_t *)ext_data->module_data; + memcpy(&direction, init_bytes + sizeof(struct snd_codec), + sizeof(direction)); + cd->direction = direction; + + 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_notification: + ipc_msg_free(cd->msg); +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 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]); + const void *source_buffer_start, *src_ptr; + void *sink_buffer_start, *sink_ptr; + size_t src_bytes, sink_bytes; + uint32_t remaining = in_size; + 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); + } + + 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, &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 cadence_codec_data *cd = module_get_private_data(mod); + + ipc_msg_send(cd->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; + } + + ret = sink_get_buffer(sinks[0], codec->mpd.produced, &sink_ptr, &sink_buffer_start, + &sink_bytes); + if (ret) { + comp_err(dev, "cannot get sink buffer"); + return ret; + } + + /* Copy the produced samples into the output buffer */ + cadence_copy_data_to_buffer(sink_ptr, codec->mpd.produced, sink_bytes, + sink_buffer_start, codec->mpd.out_buff); + + 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 int ipc4_cadence_codec_free(struct processing_module *mod) +{ + struct cadence_codec_data *cd = module_get_private_data(mod); + + ipc_msg_free(cd->msg); + + return cadence_codec_free(mod); +} + +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 = ipc4_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/compress_other/CMakeLists.txt b/src/audio/module_adapter/module/compress_other/CMakeLists.txt new file mode 100644 index 000000000000..4261b9cf8c71 --- /dev/null +++ b/src/audio/module_adapter/module/compress_other/CMakeLists.txt @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: BSD-3-Clause + +if(CONFIG_SOF_COMPRESS_CODEC_PCM_DEC) + add_local_sources(sof xa_pcm_dec.c) +endif() diff --git a/src/audio/module_adapter/module/compress_other/xa_pcm_dec.c b/src/audio/module_adapter/module/compress_other/xa_pcm_dec.c new file mode 100644 index 000000000000..7e31cd768614 --- /dev/null +++ b/src/audio/module_adapter/module/compress_other/xa_pcm_dec.c @@ -0,0 +1,311 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// Copyright(c) 2026 Intel Corporation. +// + +#include <sof/audio/cadence/xa_type_def.h> +#include <sof/audio/cadence/xa_error_standards.h> +#include <sof/audio/cadence/xa_apicmd_standards.h> +#include <sof/audio/cadence/xa_memory_standards.h> +#include <sof/audio/compress_other/pcm_dec/xa_pcm_dec_api.h> +#include <rtos/string.h> +#include <stdint.h> + +/* For XA_API_CMD_GET_MEM_INFO_SIZE */ +#define PCM_DEC_IN_BUF_SIZE 16384 +#define PCM_DEC_OUT_BUF_SIZE 16384 + +/* Maximum library-name buffer size advertised to callers of + * XA_API_CMD_GET_LIB_ID_STRINGS. Must stay <= LIB_NAME_MAX_LEN (30) from + * sof/audio/module_adapter/module/cadence.h, which is the size of the + * destination buffer (cadence_codec_data::name). + */ +#define XA_PCM_DEC_LIB_NAME_MAX 30 + +/* Number of XA_CMD_TYPE_DO_EXECUTE calls after input_over before declaring + * end-of-stream when the host keeps providing full input buffers. PCM + * passthrough has no internal state to detect end-of-data, so this is a + * timeout fallback used when the input buffer never shrinks below the full + * size. With a 16 KB input buffer at 48 kHz stereo S16, each full buffer is + * ~85 ms, so ~12 calls bound the trailing silence to ~1 s. + */ +#define PCM_DEC_EOS_FULL_BUF_COUNT 12 + +/* PCM decoder state structure */ +struct xa_pcm_dec_state { + /* Configuration parameters */ + uint32_t sample_rate; + uint32_t num_channels; + uint32_t pcm_width; + + /* State variables */ + uint32_t bytes_consumed; + uint32_t bytes_produced; + uint32_t init_done; + uint32_t exec_done; + uint32_t input_over; + uint32_t eos_full_buf_count; + + /* Memory pointers */ + void *input_buf; + void *output_buf; + uint32_t output_buf_size; + uint32_t input_bytes; +}; + +static const char lib_name[] = "PCM Decoder"; + +/* Main codec API function */ +XA_ERRORCODE xa_pcm_dec(xa_codec_handle_t handle, WORD32 cmd, WORD32 idx, pVOID value) +{ + struct xa_pcm_dec_state *state = (struct xa_pcm_dec_state *)handle; + + /* Commands that don't need initialized state */ + switch (cmd) { + case XA_API_CMD_GET_API_SIZE: + *(WORD32 *)value = sizeof(struct xa_pcm_dec_state); + return XA_NO_ERROR; + + case XA_API_CMD_GET_LIB_ID_STRINGS: + if (idx == XA_CMD_TYPE_LIB_NAME) { + memcpy_s(value, XA_PCM_DEC_LIB_NAME_MAX, + lib_name, sizeof(lib_name)); + return XA_NO_ERROR; + } + return XA_API_FATAL_INVALID_CMD_TYPE; + + case XA_API_CMD_GET_MEMTABS_SIZE: + /* PCM decoder needs minimal memtabs structure */ + *(WORD32 *)value = 4; + return XA_NO_ERROR; + + case XA_API_CMD_SET_MEMTABS_PTR: + /* PCM decoder doesn't use memtabs, just return success */ + return XA_NO_ERROR; + + default: + break; + } + + /* All other commands need initialized state */ + if (!handle) + return XA_PCMDEC_EXECUTE_FATAL_UNINITIALIZED; + + switch (cmd) { + case XA_API_CMD_INIT: + switch (idx) { + case XA_CMD_TYPE_INIT_API_PRE_CONFIG_PARAMS: + /* Initialize with default values */ + bzero(state, sizeof(*state)); + state->sample_rate = 48000; + state->num_channels = 2; + state->pcm_width = 16; + return XA_NO_ERROR; + + case XA_CMD_TYPE_INIT_API_POST_CONFIG_PARAMS: + /* Nothing to do here for simple PCM decoder */ + return XA_NO_ERROR; + + case XA_CMD_TYPE_INIT_PROCESS: + state->init_done = 1; + return XA_NO_ERROR; + + case XA_CMD_TYPE_INIT_DONE_QUERY: + *(WORD32 *)value = state->init_done; + return XA_NO_ERROR; + + default: + return XA_API_FATAL_INVALID_CMD_TYPE; + } + + case XA_API_CMD_SET_CONFIG_PARAM: + switch (idx) { + case XA_PCM_DEC_CONFIG_PARAM_SAMPLE_RATE: + state->sample_rate = *(WORD32 *)value; + return XA_NO_ERROR; + + case XA_PCM_DEC_CONFIG_PARAM_CHANNELS: + state->num_channels = *(WORD32 *)value; + return XA_NO_ERROR; + + case XA_PCM_DEC_CONFIG_PARAM_PCM_WIDTH: { + WORD32 width = *(WORD32 *)value; + + if (width != 8 && width != 16 && width != 24 && width != 32) + return XA_PCMDEC_CONFIG_NONFATAL_INVALID_PCM_WIDTH; + state->pcm_width = width; + return XA_NO_ERROR; + } + + case XA_PCM_DEC_CONFIG_PARAM_INTERLEAVE: + return XA_NO_ERROR; + + default: + return XA_API_FATAL_INVALID_CMD_TYPE; + } + + case XA_API_CMD_GET_CONFIG_PARAM: + switch (idx) { + case XA_PCM_DEC_CONFIG_PARAM_SAMPLE_RATE: + *(WORD32 *)value = state->sample_rate; + return XA_NO_ERROR; + + case XA_PCM_DEC_CONFIG_PARAM_CHANNELS: + *(WORD32 *)value = state->num_channels; + return XA_NO_ERROR; + + case XA_PCM_DEC_CONFIG_PARAM_PCM_WIDTH: + *(WORD32 *)value = state->pcm_width; + return XA_NO_ERROR; + + case XA_PCM_DEC_CONFIG_PARAM_PRODUCED: + *(WORD32 *)value = state->bytes_produced; + return XA_NO_ERROR; + + default: + return XA_API_FATAL_INVALID_CMD_TYPE; + } + + case XA_API_CMD_GET_N_MEMTABS: + /* We need 2 memory tables: input and output buffers */ + *(WORD32 *)value = 2; + return XA_NO_ERROR; + + case XA_API_CMD_GET_MEM_INFO_TYPE: + if (idx == 0) + *(WORD32 *)value = XA_MEMTYPE_INPUT; + else if (idx == 1) + *(WORD32 *)value = XA_MEMTYPE_OUTPUT; + else + return XA_API_FATAL_INVALID_CMD_TYPE; + return XA_NO_ERROR; + + case XA_API_CMD_GET_MEM_INFO_SIZE: + if (idx == 0) + *(WORD32 *)value = PCM_DEC_IN_BUF_SIZE; + else if (idx == 1) + *(WORD32 *)value = PCM_DEC_OUT_BUF_SIZE; + else + return XA_API_FATAL_INVALID_CMD_TYPE; + return XA_NO_ERROR; + + case XA_API_CMD_GET_MEM_INFO_ALIGNMENT: + *(WORD32 *)value = 4; /* 4-byte alignment */ + return XA_NO_ERROR; + + case XA_API_CMD_SET_MEM_PTR: + if (idx == 0) { + state->input_buf = value; + } else if (idx == 1) { + state->output_buf = value; + state->output_buf_size = PCM_DEC_OUT_BUF_SIZE; + } else { + return XA_API_FATAL_INVALID_CMD_TYPE; + } + return XA_NO_ERROR; + + case XA_API_CMD_SET_INPUT_BYTES: + state->input_bytes = *(WORD32 *)value; + state->bytes_consumed = 0; + if (state->input_bytes > 0) + state->exec_done = 0; + return XA_NO_ERROR; + + case XA_API_CMD_GET_OUTPUT_BYTES: + *(WORD32 *)value = state->bytes_produced; + return XA_NO_ERROR; + + case XA_API_CMD_GET_CURIDX_INPUT_BUF: + *(WORD32 *)value = state->bytes_consumed; + return XA_NO_ERROR; + + case XA_API_CMD_INPUT_OVER: + /* Indicate no more input buffers will be provided */ + state->input_over = 1; + return XA_NO_ERROR; + + case XA_API_CMD_GET_N_TABLES: + /* PCM decoder doesn't use tables */ + *(WORD32 *)value = 0; + return XA_NO_ERROR; + + case XA_API_CMD_GET_TABLE_PTR: + case XA_API_CMD_SET_TABLE_PTR: + case XA_API_CMD_GET_TABLE_INFO_SIZE: + case XA_API_CMD_GET_TABLE_INFO_ALIGNMENT: + case XA_API_CMD_GET_TABLE_INFO_PRIORITY: + /* PCM decoder doesn't use tables, return success */ + return XA_NO_ERROR; + + case XA_API_CMD_GET_MEM_INFO_PLACEMENT: + case XA_API_CMD_GET_MEM_INFO_PRIORITY: + case XA_API_CMD_SET_MEM_INFO_SIZE: + case XA_API_CMD_SET_MEM_PLACEMENT: + /* Return success for optional memory info commands */ + return XA_NO_ERROR; + + case XA_API_CMD_EXECUTE: + if (idx == XA_CMD_TYPE_DO_EXECUTE) { + uint32_t to_copy; + + state->bytes_produced = 0; + state->bytes_consumed = 0; + + /* + * Buffers must have been set via XA_API_CMD_SET_MEM_PTR + * before EXECUTE. Treat a missing pointer as a fatal + * uninitialized error so the caller fails fast instead + * of silently dropping audio. + */ + if (!state->input_buf || !state->output_buf) + return XA_PCMDEC_EXECUTE_FATAL_UNINITIALIZED; + + /* No data and source still live: signal the wrapper to + * skip this round without treating it as an error. + */ + if (state->input_bytes == 0 && !state->input_over) + return XA_PCMDEC_EXECUTE_NONFATAL_INSUFFICIENT_DATA; + + /* Copy PCM data from input to output */ + to_copy = state->input_bytes; + if (to_copy > state->output_buf_size) + to_copy = state->output_buf_size; + + if (to_copy > 0) { + memcpy_s(state->output_buf, state->output_buf_size, + state->input_buf, to_copy); + state->bytes_produced = to_copy; + state->bytes_consumed = to_copy; + } + + /* PCM passthrough has no internal state to flush. Two + * end-of-stream paths: + * 1. Deterministic: a partial input buffer after + * input_over means the source is drained. + * 2. Fallback: if the host keeps delivering full + * buffers (typical when the DMA ring is kept full), + * declare done after a bounded number of calls past + * input_over so playback ends within ~1 s of the + * real end-of-data. + */ + if (state->input_over) { + if (state->input_bytes < PCM_DEC_IN_BUF_SIZE) { + state->exec_done = 1; + } else if (++state->eos_full_buf_count > + PCM_DEC_EOS_FULL_BUF_COUNT) { + state->exec_done = 1; + } + } + + return XA_NO_ERROR; + } else if (idx == XA_CMD_TYPE_DONE_QUERY) { + /* Query if execution is done */ + *(WORD32 *)value = state->exec_done; + return XA_NO_ERROR; + } + return XA_API_FATAL_INVALID_CMD_TYPE; + + default: + return XA_API_FATAL_INVALID_CMD; + } +} 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..09f3cf1711c2 --- /dev/null +++ b/src/audio/module_adapter/module/dolby/dax.c @@ -0,0 +1,1010 @@ +// 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_TUNING_FILE_MASK 0x40 +#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) +{ + 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; + struct dax_buffer old_tuning_buf = {0}; + k_spinlock_key_t key; + + key = k_spin_lock(&adapter_data->lock); + if (adapter_data->cache_tuning_buf.addr && adapter_data->cache_tuning_buf.size > 0) { + old_tuning_buf = dax_ctx->tuning_file_buffer; + dax_ctx->tuning_file_buffer = adapter_data->cache_tuning_buf; + adapter_data->cache_tuning_buf = (struct dax_buffer){0}; + ret = 0; + } + k_spin_unlock(&adapter_data->lock, key); + + dax_buffer_release(mod, &old_tuning_buf); + + comp_info(dev, "apply tuning %p, ret %d", dax_ctx->tuning_file_buffer.addr, 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; + struct dax_buffer new_tuning_buf = { 0 }; + struct dax_buffer old_tuning_buf = { 0 }; + k_spinlock_key_t key; + int32_t tmp_val; + + switch (id) { + case DAX_PARAM_ID_TUNING_FILE: + ret = dax_buffer_alloc(mod, &new_tuning_buf, size); + if (ret != 0) { + comp_err(dev, "allocate %u bytes failed for tuning file, ret %d", size, + ret); + break; + } + ret = memcpy_s(new_tuning_buf.addr, new_tuning_buf.free, value, size); + if (ret != 0) { + comp_err(dev, "copy tuning file failed, ret %d", ret); + dax_buffer_release(mod, &new_tuning_buf); + break; + } + key = k_spin_lock(&adapter_data->lock); + old_tuning_buf = adapter_data->cache_tuning_buf; + adapter_data->cache_tuning_buf = new_tuning_buf; + k_spin_unlock(&adapter_data->lock, key); + dax_buffer_release(mod, &old_tuning_buf); + flag_process(adapter_data, DAX_TUNING_FILE_MASK, DAX_FLAG_SET); + comp_info(dev, "allocated: tuning %p, size %u", new_tuning_buf.addr, + new_tuning_buf.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_TUNING_FILE_MASK, DAX_FLAG_READ_AND_CLEAR)) { + set_tuning_file(mod); + flag_process(adapter_data, DAX_DEVICE_MASK, DAX_FLAG_SET); + flag_process(adapter_data, DAX_VOLUME_MASK, DAX_FLAG_SET); + } + + 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; + dax_buffer_release(mod, &adapter_data->cache_tuning_buf); + 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; + k_spinlock_init(&adapter_data->lock); + 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..43c900253dc6 --- /dev/null +++ b/src/audio/module_adapter/module/dolby/dax.h @@ -0,0 +1,81 @@ +/* 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/spinlock.h> +#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; + struct dax_buffer cache_tuning_buf; + struct k_spinlock lock; +}; + +/** + * @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..92803cad0820 --- /dev/null +++ b/src/audio/module_adapter/module/dolby/dax_mock.c @@ -0,0 +1,95 @@ +// 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) +{ + dax_ctx->p_dax = NULL; + return 0; +} + +int dax_init(struct sof_dax *dax_ctx) +{ + dax_ctx->p_dax = dax_ctx->persist_buffer.addr; + 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 "mock_version"; +} + +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..3e46bdac73fd 100644 --- a/src/audio/module_adapter/module/generic.c +++ b/src/audio/module_adapter/module/generic.c @@ -12,21 +12,31 @@ */ #include <rtos/symbol.h> - +#include <rtos/mutex.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> - -/* 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") -#else -#define MEM_API_CHECK_THREAD(res) +#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 LOG_MODULE_DECLARE(module_adapter, CONFIG_SOF_LOG_LEVEL); +/* + * A single lock shared by all module resource pools. The resource API is only + * ever entered from supervisor context (the z_impl_* syscall bodies), so the + * lock does not need to live in the per-module, user-writable struct - a static + * kernel object serialises all pools. Contention is negligible because resource + * bookkeeping only happens at module setup/teardown, not on the processing path. + */ +static K_MUTEX_DEFINE(mod_res_lock); + int module_load_config(struct comp_dev *dev, const void *cfg, size_t size) { int ret; @@ -47,13 +57,15 @@ int module_load_config(struct comp_dev *dev, const void *cfg, size_t size) if (!dst->data) { /* No space for config available yet, allocate now */ - dst->data = rballoc(SOF_MEM_FLAG_USER, size); + dst->data = sof_heap_alloc(sof_sys_user_heap_get(), + SOF_MEM_FLAG_USER | SOF_MEM_FLAG_LARGE_BUFFER, size, 0); } else if (dst->size != size) { /* The size allocated for previous config doesn't match the new one. * Free old container and allocate new one. */ - rfree(dst->data); - dst->data = rballoc(SOF_MEM_FLAG_USER, size); + sof_heap_free(sof_sys_user_heap_get(), dst->data); + dst->data = sof_heap_alloc(sof_sys_user_heap_get(), + SOF_MEM_FLAG_USER | SOF_MEM_FLAG_LARGE_BUFFER, size, 0); } if (!dst->data) { comp_err(dev, "failed to allocate space for setup config."); @@ -71,10 +83,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,149 +122,165 @@ 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(); -#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 *z_impl_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); - if (!container) + k_mutex_lock(&mod_res_lock, K_FOREVER); + + container = container_get(mod); + if (!container) { + k_mutex_unlock(&mod_res_lock); return NULL; + } if (!size) { comp_err(mod->dev, "requested allocation of 0 bytes."); container_put(mod, container); + k_mutex_unlock(&mod_res_lock); 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); + k_mutex_unlock(&mod_res_lock); return NULL; } /* Store reference to allocated memory */ 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) res->heap_high_water_mark = res->heap_usage; + k_mutex_unlock(&mod_res_lock); return ptr; } -EXPORT_SYMBOL(mod_alloc_align); +EXPORT_SYMBOL(z_impl_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); + k_mutex_lock(&mod_res_lock, K_FOREVER); - if (ret) - memset(ret, 0, size); + container = container_get(mod); + if (!container) { + k_mutex_unlock(&mod_res_lock); + return NULL; + } - return ret; + if (!size) { + comp_err(mod->dev, "requested allocation of 0 bytes."); + container_put(mod, container); + k_mutex_unlock(&mod_res_lock); + return NULL; + } + + /* Allocate memory for module */ + void *ptr = sof_ctx_alloc(res->alloc, flags, 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); + k_mutex_unlock(&mod_res_lock); + 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; + + k_mutex_unlock(&mod_res_lock); + 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,31 +290,34 @@ 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 *z_impl_mod_data_blob_handler_new(struct processing_module *mod) { - struct module_resources *res = &mod->priv.resources; - struct module_resource *container = container_get(mod); struct comp_data_blob_handler *bhp; + struct module_resource *container; - MEM_API_CHECK_THREAD(res); - if (!container) + k_mutex_lock(&mod_res_lock, K_FOREVER); + + container = container_get(mod); + if (!container) { + k_mutex_unlock(&mod_res_lock); return NULL; + } bhp = comp_data_blob_handler_new_ext(mod->dev, false, NULL, NULL); if (!bhp) { container_put(mod, container); + k_mutex_unlock(&mod_res_lock); return NULL; } container->bhp = bhp; container->size = 0; container->type = MOD_RES_BLOB_HANDLER; - list_item_prepend(&container->list, &res->res_list); + k_mutex_unlock(&mod_res_lock); return bhp; } -EXPORT_SYMBOL(mod_data_blob_handler_new); +EXPORT_SYMBOL(z_impl_mod_data_blob_handler_new); #endif /** @@ -286,39 +328,48 @@ 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); - if (!container) + k_mutex_lock(&mod_res_lock, K_FOREVER); + + container = container_get(mod); + if (!container) { + k_mutex_unlock(&mod_res_lock); return NULL; + } - ptr = fast_get(dram_ptr, size); + ptr = fast_get(res->alloc, dram_ptr, size); if (!ptr) { container_put(mod, container); + k_mutex_unlock(&mod_res_lock); return NULL; } container->sram_ptr = ptr; container->size = 0; container->type = MOD_RES_FAST_GET; - list_item_prepend(&container->list, &res->res_list); + k_mutex_unlock(&mod_res_lock); 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); + sof_ctx_free(res->alloc, container->ptr); res->heap_usage -= container->size; return 0; #if CONFIG_COMP_BLOB @@ -328,7 +379,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 +393,151 @@ 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}; - list_item_del(&container->list); - container_put(mod, container); - return ret; - } - } + k_mutex_lock(&mod_res_lock, K_FOREVER); + int ret = objpool_iterate(&res->objpool, mod_res_free, &cb_arg); - comp_err(mod->dev, "error: could not find memory pointed by %p", ptr); + k_mutex_unlock(&mod_res_lock); - return -EINVAL; + if (ret < 0) + comp_err(mod->dev, "error: could not find memory pointed by %p", ptr); + + return ret; } -EXPORT_SYMBOL(mod_free); +EXPORT_SYMBOL(z_impl_mod_free); + +#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); +} +#include <zephyr/syscalls/mod_alloc_ext_mrsh.c> + +void *z_vrfy_mod_balloc_align(struct processing_module *mod, 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_balloc_align(mod, size, alignment); +} +#include <zephyr/syscalls/mod_balloc_align_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> + +void z_vrfy_mod_free_all(struct processing_module *mod) +{ + 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)); + + z_impl_mod_free_all(mod); +} +#include <zephyr/syscalls/mod_free_all_mrsh.c> + +#if CONFIG_COMP_BLOB +struct comp_data_blob_handler *z_vrfy_mod_data_blob_handler_new(struct processing_module *mod) +{ + 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_data_blob_handler_new(mod); +} +#include <zephyr/syscalls/mod_data_blob_handler_new_mrsh.c> +#endif +#endif #if CONFIG_COMP_BLOB void mod_data_blob_handler_free(struct processing_module *mod, struct comp_data_blob_handler *dbh) @@ -403,7 +572,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); @@ -415,7 +601,7 @@ int module_prepare(struct processing_module *mod, * as it has been applied during the procedure - it is safe to * free it. */ - rfree(md->cfg.data); + sof_heap_free(sof_sys_user_heap_get(), md->cfg.data); md->cfg.avail = false; md->cfg.data = NULL; @@ -522,11 +708,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, @@ -537,7 +736,7 @@ int module_reset(struct processing_module *mod) md->cfg.avail = false; md->cfg.size = 0; - rfree(md->cfg.data); + sof_heap_free(sof_sys_user_heap_get(), md->cfg.data); md->cfg.data = NULL; #if CONFIG_IPC_MAJOR_3 @@ -556,31 +755,21 @@ int module_reset(struct processing_module *mod) * * This function is called automatically when the module is unloaded. */ -void mod_free_all(struct processing_module *mod) +void z_impl_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); + k_mutex_lock(&mod_res_lock, K_FOREVER); + objpool_iterate(&res->objpool, mod_res_free, &cb_arg); + objpool_prune(&res->objpool); + k_mutex_unlock(&mod_res_lock); - 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 +777,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); @@ -597,10 +787,10 @@ int module_free(struct processing_module *mod) /* Free all memory shared by module_adapter & module */ md->cfg.avail = false; md->cfg.size = 0; - rfree(md->cfg.data); + sof_heap_free(sof_sys_user_heap_get(), md->cfg.data); md->cfg.data = NULL; if (md->runtime_params) { - rfree(md->runtime_params); + sof_heap_free(sof_sys_user_heap_get(), md->runtime_params); md->runtime_params = NULL; } #if CONFIG_IPC_MAJOR_3 @@ -667,7 +857,9 @@ int module_set_configuration(struct processing_module *mod, } /* Allocate buffer for new params */ - md->runtime_params = rballoc(SOF_MEM_FLAG_USER, md->new_cfg_size); + md->runtime_params = sof_heap_alloc(sof_sys_user_heap_get(), + SOF_MEM_FLAG_USER | SOF_MEM_FLAG_LARGE_BUFFER, + md->new_cfg_size, 0); if (!md->runtime_params) { comp_err(dev, "space allocation for new params failed"); return -ENOMEM; @@ -708,14 +900,14 @@ int module_set_configuration(struct processing_module *mod, md->new_cfg_size = 0; if (md->runtime_params) - rfree(md->runtime_params); + sof_heap_free(sof_sys_user_heap_get(), md->runtime_params); md->runtime_params = NULL; return ret; } EXPORT_SYMBOL(module_set_configuration); -int module_bind(struct processing_module *mod, struct bind_info *bind_data) +int module_bind(struct processing_module *mod, const struct bind_info *bind_data) { int ret; const struct module_interface *const ops = mod->dev->drv->adapter_ops; @@ -733,13 +925,22 @@ 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; } -int module_unbind(struct processing_module *mod, struct bind_info *unbind_data) +int module_unbind(struct processing_module *mod, const struct bind_info *unbind_data) { int ret; const struct module_interface *const ops = mod->dev->drv->adapter_ops; @@ -757,8 +958,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 +984,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..4a0a8eb2a655 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) { @@ -64,18 +63,23 @@ static int passthrough_codec_init_process(struct processing_module *mod) return 0; } -static int -passthrough_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) +static int passthrough_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; + const void *src_ptr, *src_buf_start; + void *snk_ptr, *snk_buf_start; + size_t src_buf_size, snk_buf_size; + size_t n_bytes = codec->mpd.in_buff_size; + size_t size_to_wrap; + int err; comp_dbg(dev, "entry"); /* Proceed only if we have enough data to fill the module buffer completely */ - if (input_buffers[0].size < codec->mpd.in_buff_size) { + if (source_get_data_available(sources[0]) < n_bytes) { comp_dbg(dev, "not enough data to process"); return -ENODATA; } @@ -83,19 +87,38 @@ passthrough_codec_process(struct processing_module *mod, if (!codec->mpd.init_done) passthrough_codec_init_process(mod); - memcpy_s(codec->mpd.in_buff, codec->mpd.in_buff_size, - input_buffers[0].data, codec->mpd.in_buff_size); - - memcpy_s(codec->mpd.out_buff, codec->mpd.out_buff_size, - codec->mpd.in_buff, codec->mpd.in_buff_size); - codec->mpd.produced = mod->period_bytes; - codec->mpd.consumed = mod->period_bytes; - input_buffers[0].consumed = codec->mpd.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; + err = source_get_data(sources[0], n_bytes, &src_ptr, &src_buf_start, &src_buf_size); + if (err) + return err; + + /* src_buf_size is the total ring buffer size; handle wrap when copying to in_buff */ + size_to_wrap = (const uint8_t *)src_buf_start + src_buf_size - (const uint8_t *)src_ptr; + if (n_bytes <= size_to_wrap) { + memcpy_s(codec->mpd.in_buff, n_bytes, src_ptr, n_bytes); + } else { + memcpy_s(codec->mpd.in_buff, n_bytes, src_ptr, size_to_wrap); + memcpy_s((uint8_t *)codec->mpd.in_buff + size_to_wrap, n_bytes - size_to_wrap, + src_buf_start, n_bytes - size_to_wrap); + } + source_release_data(sources[0], n_bytes); + + memcpy_s(codec->mpd.out_buff, codec->mpd.out_buff_size, codec->mpd.in_buff, n_bytes); + + err = sink_get_buffer(sinks[0], n_bytes, &snk_ptr, &snk_buf_start, &snk_buf_size); + if (err) + return err; + + /* snk_buf_size is the total ring buffer size; handle wrap when copying from out_buff */ + size_to_wrap = (uint8_t *)snk_buf_start + snk_buf_size - (uint8_t *)snk_ptr; + if (n_bytes <= size_to_wrap) { + memcpy_s(snk_ptr, n_bytes, codec->mpd.out_buff, n_bytes); + } else { + memcpy_s(snk_ptr, n_bytes, codec->mpd.out_buff, size_to_wrap); + memcpy_s(snk_buf_start, n_bytes - size_to_wrap, + (const uint8_t *)codec->mpd.out_buff + size_to_wrap, + n_bytes - size_to_wrap); + } + sink_commit_buffer(sinks[0], n_bytes); return 0; } @@ -122,10 +145,11 @@ static int passthrough_codec_free(struct processing_module *mod) static const struct module_interface passthrough_interface = { .init = passthrough_codec_init, .prepare = passthrough_codec_prepare, - .process_raw_data = passthrough_codec_process, + .process = passthrough_codec_process, .reset = passthrough_codec_reset, .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..aa800b32f992 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; } @@ -613,27 +612,36 @@ static int waves_effect_apply_config(struct processing_module *mod) for (index = 0; index < cfg->size && (!ret); param_number++) { uint32_t param_data_size; + /* make sure a whole param header remains before reading + * param->size / param->id below + */ + if (index + header_size > cfg->size) { + comp_err(dev, "module_param header at index %u (header %u) exceeds cfg size %zu", + index, header_size, cfg->size); + return -EINVAL; + } + 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 +658,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 +674,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 +690,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 +698,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 +722,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 +740,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 +753,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; @@ -754,18 +762,25 @@ static int waves_codec_init_process(struct processing_module *mod) return 0; } -static int -waves_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) +static int waves_codec_process(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 module_data *codec = &mod->priv; struct waves_codec_data *waves_codec = codec->private; + const void *src_ptr; + const void *src_buf_start; + size_t src_buf_size; + void *snk_ptr; + void *snk_buf_start; + size_t snk_buf_size; + size_t n_bytes = codec->mpd.in_buff_size; + size_t size_to_wrap; /* Proceed only if we have enough data to fill the module buffer completely */ - if (input_buffers[0].size < codec->mpd.in_buff_size) { + if (source_get_data_available(sources[0]) < n_bytes) { comp_dbg(dev, "not enough data to process"); return -ENODATA; } @@ -773,11 +788,22 @@ waves_codec_process(struct processing_module *mod, if (!codec->mpd.init_done) waves_codec_init_process(mod); - 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 = source_get_data(sources[0], n_bytes, &src_ptr, &src_buf_start, &src_buf_size); + if (ret) + return ret; + + /* src_buf_size is the total ring buffer size; handle wrap when copying to in_buff */ + size_to_wrap = (const uint8_t *)src_buf_start + src_buf_size - (const uint8_t *)src_ptr; + if (n_bytes <= size_to_wrap) { + memcpy_s(codec->mpd.in_buff, n_bytes, src_ptr, n_bytes); + } else { + memcpy_s(codec->mpd.in_buff, n_bytes, src_ptr, size_to_wrap); + memcpy_s((uint8_t *)codec->mpd.in_buff + size_to_wrap, n_bytes - size_to_wrap, + src_buf_start, n_bytes - size_to_wrap); + } + codec->mpd.avail = n_bytes; - 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 +815,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,24 +833,40 @@ 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); + source_release_data(sources[0], 0); + comp_err(dev, "MaxxEffect_Process returned %d", status); ret = -EINVAL; } else { codec->mpd.produced = waves_codec->o_stream.numAvailableSamples * waves_codec->o_format.numChannels * waves_codec->sample_size_in_bytes; codec->mpd.consumed = codec->mpd.produced; - input_buffers[0].consumed = codec->mpd.consumed; - ret = 0; - /* 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; + + source_release_data(sources[0], codec->mpd.consumed); + + ret = sink_get_buffer(sinks[0], codec->mpd.produced, + &snk_ptr, &snk_buf_start, &snk_buf_size); + if (!ret) { + /* snk_buf_size is the total ring buffer size; handle wrap */ + size_to_wrap = (uint8_t *)snk_buf_start + snk_buf_size - + (uint8_t *)snk_ptr; + if (codec->mpd.produced <= size_to_wrap) { + memcpy_s(snk_ptr, codec->mpd.produced, + codec->mpd.out_buff, codec->mpd.produced); + } else { + memcpy_s(snk_ptr, codec->mpd.produced, + codec->mpd.out_buff, size_to_wrap); + memcpy_s(snk_buf_start, codec->mpd.produced - size_to_wrap, + (const uint8_t *)codec->mpd.out_buff + size_to_wrap, + codec->mpd.produced - size_to_wrap); + } + sink_commit_buffer(sinks[0], codec->mpd.produced); + } } 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 +878,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 +896,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; } @@ -900,7 +942,7 @@ waves_codec_set_configuration(struct processing_module *mod, uint32_t config_id, static const struct module_interface waves_interface = { .init = waves_codec_init, .prepare = waves_codec_prepare, - .process_raw_data = waves_codec_process, + .process = waves_codec_process, .set_configuration = waves_codec_set_configuration, .reset = waves_codec_reset, .free = waves_codec_free @@ -920,6 +962,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..f94d5fe4f572 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,70 +48,218 @@ 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, NULL); +} + +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 vreg_size; + uintptr_t vreg_start; + + if (config->proc_domain == COMP_PROCESSING_DOMAIN_DP && IS_ENABLED(CONFIG_SOF_VREGIONS) && + IS_ENABLED(CONFIG_USERSPACE) && !IS_ENABLED(CONFIG_SOF_USERSPACE_USE_DRIVER_HEAP)) { + /* src-lite with 8 channels has been seen allocating 14k in one go */ + /* FIXME: the size will be derived from configuration */ + vreg_size = 28 * 1024; + mod_vreg = vregion_create_map(&vreg_start, &vreg_size); + if (!mod_vreg) { + comp_cl_err(drv, "Failed to allocate DP module heap / vregion"); + return NULL; + } +#ifdef CONFIG_SOF_USERSPACE_LL + mod_heap = sof_sys_user_heap_get(); +#else + mod_heap = NULL; +#endif + } else { +#ifdef CONFIG_SOF_USERSPACE_LL + mod_heap = sof_sys_user_heap_get(); + comp_cl_dbg(drv, "using ll user heap for module"); +#else + mod_heap = drv->user_heap; +#endif + vreg_size = 0; + vreg_start = 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, sizeof(*mod)); + else + mod = vregion_alloc(mod_vreg, sizeof(*mod)); + + if (!mod) { + comp_cl_err(drv, "failed to allocate memory for module"); + goto emod; + } + + struct mod_alloc_ctx *alloc = sof_heap_alloc(mod_heap, flags, sizeof(*alloc), 0); + + 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, 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: + sof_heap_free(mod_heap, 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)) + sof_heap_free(alloc->heap, alloc); + } else { + sof_heap_free(mod_heap, mod->dev); + sof_heap_free(mod_heap, mod); + sof_heap_free(mod_heap, 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. * * Note: Use the ext version if you need to set the module's private data before calling * the create method. + * Note 2: ATM runs in privileged / kernel mode for DP modules */ 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, + const struct module_interface *ops) { int ret; - struct comp_dev *dev; - struct processing_module *mod; struct module_config *dst; - const struct module_interface *const interface = drv->adapter_ops; - + const struct module_interface *const interface = ops ? : 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 */ + + 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 */ - ret = module_adapter_init_data(dev, dst, config, spec); + 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 +280,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 +304,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 +320,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,23 +385,13 @@ 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; ret = module_adapter_params(dev, ¶ms); if (ret) { - 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."); + comp_err(dev, "module params failed: %d", ret); return ret; } } @@ -301,10 +461,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); @@ -334,6 +499,14 @@ int module_adapter_prepare(struct comp_dev *dev) return -EINVAL; } + if (mod->num_of_sources > mod->max_sources || + mod->num_of_sinks > mod->max_sinks) { + comp_err(dev, "connected sources %d (max %d) or sinks %d (max %d) exceed module capacity", + mod->num_of_sources, mod->max_sources, + mod->num_of_sinks, mod->max_sinks); + return -EINVAL; + } + /* check processing mode */ if (IS_PROCESSING_MODE_AUDIO_STREAM(mod) && mod->max_sources > 1 && mod->max_sinks > 1) { comp_err(dev, "Invalid use of simple_copy"); @@ -346,11 +519,13 @@ int module_adapter_prepare(struct comp_dev *dev) /* allocate memory for input buffers */ if (mod->max_sources) { mod->input_buffers = - rzalloc(memory_flags, sizeof(*mod->input_buffers) * mod->max_sources); + sof_heap_alloc(sof_sys_user_heap_get(), memory_flags, + sizeof(*mod->input_buffers) * mod->max_sources, 0); if (!mod->input_buffers) { comp_err(dev, "failed to allocate input buffers"); return -ENOMEM; } + memset(mod->input_buffers, 0, sizeof(*mod->input_buffers) * mod->max_sources); } else { mod->input_buffers = NULL; } @@ -358,12 +533,14 @@ int module_adapter_prepare(struct comp_dev *dev) /* allocate memory for output buffers */ if (mod->max_sinks) { mod->output_buffers = - rzalloc(memory_flags, sizeof(*mod->output_buffers) * mod->max_sinks); + sof_heap_alloc(sof_sys_user_heap_get(), memory_flags, + sizeof(*mod->output_buffers) * mod->max_sinks, 0); if (!mod->output_buffers) { comp_err(dev, "failed to allocate output buffers"); ret = -ENOMEM; goto in_out_free; } + memset(mod->output_buffers, 0, sizeof(*mod->output_buffers) * mod->max_sinks); } else { mod->output_buffers = NULL; } @@ -424,7 +601,8 @@ int module_adapter_prepare(struct comp_dev *dev) size_t size = MAX(mod->deep_buff_bytes, mod->period_bytes); list_for_item(blist, &dev->bsource_list) { - mod->input_buffers[i].data = rballoc(memory_flags, size); + mod->input_buffers[i].data = sof_heap_alloc(sof_sys_user_heap_get(), + memory_flags, size, 0); if (!mod->input_buffers[i].data) { comp_err(mod->dev, "Failed to alloc input buffer data"); ret = -ENOMEM; @@ -436,7 +614,9 @@ int module_adapter_prepare(struct comp_dev *dev) /* allocate memory for output buffer data */ i = 0; list_for_item(blist, &dev->bsink_list) { - mod->output_buffers[i].data = rballoc(memory_flags, md->mpd.out_buff_size); + mod->output_buffers[i].data = sof_heap_alloc(sof_sys_user_heap_get(), + memory_flags, + md->mpd.out_buff_size, 0); if (!mod->output_buffers[i].data) { comp_err(mod->dev, "Failed to alloc output buffer data"); ret = -ENOMEM; @@ -449,10 +629,10 @@ 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; if (!buffer) { comp_err(dev, "failed to allocate local buffer"); @@ -460,9 +640,9 @@ int module_adapter_prepare(struct comp_dev *dev) goto free; } - irq_local_disable(flags); + vregion_get(md->resources.alloc->vreg); + list_item_prepend(&buffer->buffers_list, &mod->raw_data_buffers_list); - irq_local_enable(flags); buffer_set_params(buffer, mod->stream_params, BUFFER_UPDATE_FORCE); audio_buffer_reset(&buffer->audio_buffer); @@ -492,30 +672,26 @@ int module_adapter_prepare(struct comp_dev *dev) list_for_item_safe(blist, _blist, &mod->raw_data_buffers_list) { struct comp_buffer *buffer = container_of(blist, struct comp_buffer, buffers_list); - uint32_t flags; - irq_local_disable(flags); list_item_del(&buffer->buffers_list); - irq_local_enable(flags); buffer_free(buffer); } out_data_free: for (i = 0; i < mod->num_of_sinks; i++) - rfree(mod->output_buffers[i].data); + sof_heap_free(sof_sys_user_heap_get(), mod->output_buffers[i].data); in_data_free: for (i = 0; i < mod->num_of_sources; i++) - rfree(mod->input_buffers[i].data); + sof_heap_free(sof_sys_user_heap_get(), mod->input_buffers[i].data); in_out_free: - rfree(mod->output_buffers); + sof_heap_free(sof_sys_user_heap_get(), mod->output_buffers); mod->output_buffers = NULL; - rfree(mod->input_buffers); + sof_heap_free(sof_sys_user_heap_get(), mod->input_buffers); 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 +700,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 +731,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 +1345,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) { @@ -1183,6 +1357,7 @@ int module_adapter_trigger(struct comp_dev *dev, int cmd) if (dev->ipc_config.type == SOF_COMP_HOST || dev->ipc_config.type == SOF_COMP_DAI) return interface->endpoint_ops->trigger(dev, cmd); +#if CONFIG_IPC_MAJOR_3 /* * If the module doesn't support pause, keep it active along with the rest of the * downstream modules @@ -1191,12 +1366,24 @@ int module_adapter_trigger(struct comp_dev *dev, int cmd) dev->state = COMP_STATE_ACTIVE; return PPL_STATUS_PATH_STOP; } - if (interface->trigger) +#endif + + 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) { @@ -1215,14 +1402,16 @@ int module_adapter_reset(struct comp_dev *dev) if (IS_PROCESSING_MODE_RAW_DATA(mod)) { for (i = 0; i < mod->num_of_sinks; i++) - rfree((__sparse_force void *)mod->output_buffers[i].data); + sof_heap_free(sof_sys_user_heap_get(), + (__sparse_force void *)mod->output_buffers[i].data); for (i = 0; i < mod->num_of_sources; i++) - rfree((__sparse_force void *)mod->input_buffers[i].data); + sof_heap_free(sof_sys_user_heap_get(), + (__sparse_force void *)mod->input_buffers[i].data); } if (IS_PROCESSING_MODE_RAW_DATA(mod) || IS_PROCESSING_MODE_AUDIO_STREAM(mod)) { - rfree(mod->output_buffers); - rfree(mod->input_buffers); + sof_heap_free(sof_sys_user_heap_get(), mod->output_buffers); + sof_heap_free(sof_sys_user_heap_get(), mod->input_buffers); mod->num_of_sources = 0; mod->num_of_sinks = 0; @@ -1237,14 +1426,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,32 +1442,36 @@ 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); - uint32_t flags; - - irq_local_disable(flags); list_item_del(&buffer->buffers_list); - irq_local_enable(flags); 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 +1482,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 +1504,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 +1524,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 +1543,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 +1562,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 +1581,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 +1605,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..e6098dec0799 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; } @@ -184,21 +184,25 @@ static int module_adapter_get_set_params(struct comp_dev *dev, struct sof_ipc_ct const struct module_interface *const interface = mod->dev->drv->adapter_ops; enum module_cfg_fragment_position pos; uint32_t data_offset_size; - static uint32_t size; comp_dbg(dev, "num_of_elem %d, elem remain %d msg_index %u", cdata->num_elems, cdata->elems_remaining, cdata->msg_index); - /* set the fragment position, data offset and config data size */ + /* + * The total fragmented-transfer size is kept per instance in + * mod->runtime_params_size; a file-scope static would be shared across + * all module_adapter components and corrupted by interleaved transfers. + */ if (!cdata->msg_index) { - size = cdata->num_elems + cdata->elems_remaining; - data_offset_size = size; + mod->runtime_params_size = cdata->num_elems + cdata->elems_remaining; + data_offset_size = mod->runtime_params_size; if (cdata->elems_remaining) pos = MODULE_CFG_FRAGMENT_FIRST; else pos = MODULE_CFG_FRAGMENT_SINGLE; } else { - data_offset_size = size - (cdata->num_elems + cdata->elems_remaining); + data_offset_size = mod->runtime_params_size - + (cdata->num_elems + cdata->elems_remaining); if (cdata->elems_remaining) pos = MODULE_CFG_FRAGMENT_MIDDLE; else @@ -270,7 +274,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 +311,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..da372fc477dc 100644 --- a/src/audio/module_adapter/module_adapter_ipc4.c +++ b/src/audio/module_adapter/module_adapter_ipc4.c @@ -16,29 +16,36 @@ #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> #include <rtos/symbol.h> +#include <ipc4/base_fw.h> #include <limits.h> #include <stdint.h> 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 +53,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 +74,33 @@ 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 heap %u", + dp_data->domain_id, dp_data->stack_bytes, + dp_data->heap_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,19 +143,26 @@ 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; + /* Host-supplied channel count indexes PLATFORM_MAX_CHANNELS arrays. */ + if (dst->base_cfg.audio_fmt.channels_count == 0 || + dst->base_cfg.audio_fmt.channels_count > PLATFORM_MAX_CHANNELS) { + comp_err(dev, "invalid channels count %u", + dst->base_cfg.audio_fmt.channels_count); + return -EINVAL; + } + if (cfgsz >= sizeof(*cfg)) { int n_in = cfg->base_cfg_ext.nb_input_pins; int n_out = cfg->base_cfg_ext.nb_output_pins; @@ -134,11 +170,14 @@ int module_adapter_init_data(struct comp_dev *dev, + (n_out * sizeof(*dst->output_pins)); if (cfgsz == (sizeof(*cfg) + pinsz)) { + if (n_in > IPC4_MAX_SRC_QUEUE || n_out > IPC4_MAX_DST_QUEUE) + return -EINVAL; + 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 +187,17 @@ int module_adapter_init_data(struct comp_dev *dev, } } - dst->init_data = cfg; /* legacy API */ - dst->avail = true; + /* Always provide init_data so legacy modules have a valid pointer. + * Extended-init-aware modules (e.g. cadence) read + * ext_data->module_data directly and never dereference init_data. + * Note: cfg points into the IPC mailbox and must not be used + * persistently; it is reset to NULL by module_adapter_reset_data() + * in module_adapter_new_ext() after module_init() returns. + */ + dst->init_data = cfg; + if (!config->ipc_extended_init || !dst->ext_data->module_data) + dst->avail = true; + return 0; } @@ -206,13 +254,17 @@ int module_set_large_config(struct comp_dev *dev, uint32_t param_id, bool first_ return -EINVAL; } + if (fragment_size > MAILBOX_HOSTBOX_SIZE) { + comp_err(dev, "invalid large_config fragment size %u", fragment_size); + return -EINVAL; + } + if (interface->set_configuration) return interface->set_configuration(mod, param_id, pos, data_offset_size, (const uint8_t *)data, fragment_size, 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) @@ -229,10 +281,16 @@ int module_get_large_config(struct comp_dev *dev, uint32_t param_id, bool first_ else fragment_size = SOF_IPC_MSG_MAX_SIZE; } else { - if (!last_block) + if (!last_block) { fragment_size = SOF_IPC_MSG_MAX_SIZE; - else + } else { + if (*data_offset_size > md->cfg.size) { + comp_err(dev, "invalid data_offset_size %u > cfg size %zu", + *data_offset_size, md->cfg.size); + return -EINVAL; + } fragment_size = md->cfg.size - *data_offset_size; + } } if (interface->get_configuration) @@ -244,7 +302,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 +323,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 +340,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 +375,47 @@ static bool module_adapter_multi_sink_source_prepare(struct comp_dev *dev) return false; } -int module_adapter_bind(struct comp_dev *dev, struct bind_info *bind_data) +static int module_update_source_buffer_params(struct processing_module *mod, + const 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, const 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,9 +424,8 @@ 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) +int module_adapter_unbind(struct comp_dev *dev, const struct bind_info *unbind_data) { struct processing_module *mod = comp_mod(dev); int ret; @@ -348,7 +438,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 +453,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..091cad43b03d 100644 --- a/src/audio/multiband_drc/multiband_drc.c +++ b/src/audio/multiband_drc/multiband_drc.c @@ -5,10 +5,8 @@ // Author: Pin-chih Lin <johnylin@google.com> #include <sof/audio/module_adapter/module/generic.h> -#include <sof/audio/buffer.h> #include <sof/audio/format.h> #include <sof/audio/ipc-config.h> -#include <sof/audio/pipeline.h> #include <sof/ipc/msg.h> #include <sof/lib/memory.h> #include <sof/lib/uuid.h> @@ -39,31 +37,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 +70,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 +83,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 +106,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 +115,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 +138,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 +178,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 +199,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,12 +210,45 @@ 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); } +static int multiband_drc_validator(struct comp_dev *dev, void *new_data, uint32_t new_data_size) +{ + struct sof_multiband_drc_config *config = new_data; + size_t expected; + + if (new_data_size < sizeof(struct sof_multiband_drc_config) || + new_data_size > SOF_MULTIBAND_DRC_MAX_BLOB_SIZE) { + comp_err(dev, "invalid configuration blob, size %u", new_data_size); + return -EINVAL; + } + + if (new_data_size != config->size) { + comp_err(dev, "blob size %u does not match header size %u", + new_data_size, config->size); + return -EINVAL; + } + + if (!config->num_bands || config->num_bands > SOF_MULTIBAND_DRC_MAX_BANDS) { + comp_err(dev, "invalid num_bands %u", config->num_bands); + return -EINVAL; + } + + expected = sizeof(struct sof_multiband_drc_config) + + (size_t)config->num_bands * sizeof(struct sof_drc_params); + if (new_data_size != expected) { + comp_err(dev, "blob size %u does not match expected %zu for %u bands", + new_data_size, expected, config->num_bands); + return -EINVAL; + } + + return 0; +} + /* * End of Multiband DRC setup code. Next the standard component methods. */ @@ -227,23 +257,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 +276,21 @@ 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); + /* Reject malformed blobs at IPC time so a bad run-time update cannot + * replace the working configuration. + */ + comp_data_blob_set_validator(cd->model_handler, multiband_drc_validator); - return 0; + multiband_drc_reset_state(mod, &cd->state); -cd_fail: - comp_data_blob_handler_free(cd->model_handler); - rfree(cd); - return ret; + return 0; } __cold static int multiband_drc_free(struct processing_module *mod) @@ -287,11 +299,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 +317,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,44 +331,50 @@ __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); } static int multiband_drc_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 sof_source **sources, int num_of_sources, + struct sof_sink **sinks, int num_of_sinks) { struct multiband_drc_comp_data *cd = module_get_private_data(mod); struct comp_dev *dev = mod->dev; - struct audio_stream *source = input_buffers[0].data; - struct audio_stream *sink = output_buffers[0].data; - int frames = input_buffers[0].size; + int frames = source_get_data_frames_available(sources[0]); + int sink_frames = sink_get_free_frames(sinks[0]); int ret; - comp_dbg(dev, "multiband_drc_process()"); + comp_dbg(dev, "entry"); + + /* Process only as many frames as both the source provides and the + * sink can accept, otherwise the copy would fail with -ENODATA. + */ + frames = MIN(frames, sink_frames); + if (!frames) + return 0; /* 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 = multiband_drc_setup(mod, (int16_t)audio_stream_get_channels(sink), - audio_stream_get_rate(sink)); + ret = multiband_drc_setup(mod, (int16_t)sink_get_channels(sinks[0]), + sink_get_rate(sinks[0])); if (ret < 0) { - comp_err(dev, "multiband_drc_process(), failed DRC setup"); + comp_err(dev, "failed DRC setup"); return ret; } } if (cd->process_enabled) - cd->multiband_drc_func(mod, source, sink, frames); + ret = cd->multiband_drc_func(mod, sources[0], sinks[0], frames); else - multiband_drc_default_pass(mod, source, sink, frames); + ret = multiband_drc_default_pass(mod, sources[0], sinks[0], frames); - /* calc new free and available */ - module_update_buffer_position(&input_buffers[0], &output_buffers[0], frames); - return 0; + if (ret < 0) + comp_err(dev, "processing failed: %d", ret); + + return ret; } static int multiband_drc_prepare(struct processing_module *mod, @@ -365,44 +383,37 @@ 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; 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) return ret; - /* DRC component will only ever have 1 source and 1 sink buffer */ - sourceb = comp_dev_get_first_data_producer(dev); - if (!sourceb) { - comp_err(dev, "no source buffer"); - return -ENOTCONN; - } - /* get source data format */ - cd->source_format = audio_stream_get_frm_fmt(&sourceb->stream); - channels = audio_stream_get_channels(&sourceb->stream); - rate = audio_stream_get_rate(&sourceb->stream); + cd->source_format = source_get_frm_fmt(sources[0]); + channels = (int16_t)source_get_channels(sources[0]); + rate = source_get_rate(sources[0]); /* 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) { 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 +424,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; @@ -427,7 +438,7 @@ static int multiband_drc_reset(struct processing_module *mod) static const struct module_interface multiband_drc_interface = { .init = multiband_drc_init, .prepare = multiband_drc_prepare, - .process_audio_stream = multiband_drc_process, + .process = multiband_drc_process, .set_configuration = multiband_drc_set_config, .get_configuration = multiband_drc_get_config, .reset = multiband_drc_reset, @@ -449,6 +460,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..ba939663de5d 100644 --- a/src/audio/multiband_drc/multiband_drc.h +++ b/src/audio/multiband_drc/multiband_drc.h @@ -28,10 +28,10 @@ struct multiband_drc_state { struct iir_state_df1 deemphasis[PLATFORM_MAX_CHANNELS]; }; -typedef void (*multiband_drc_func)(const struct processing_module *mod, - const struct audio_stream *source, - struct audio_stream *sink, - uint32_t frames); +typedef int (*multiband_drc_func)(const struct processing_module *mod, + struct sof_source *source, + struct sof_sink *sink, + uint32_t frames); /* Multiband DRC component private data */ struct multiband_drc_comp_data { @@ -54,10 +54,10 @@ extern const struct multiband_drc_proc_fnmap multiband_drc_proc_fnmap[]; extern const struct multiband_drc_proc_fnmap multiband_drc_proc_fnmap_pass[]; extern const size_t multiband_drc_proc_fncount; -void multiband_drc_default_pass(const struct processing_module *mod, - const struct audio_stream *source, - struct audio_stream *sink, - uint32_t frames); +int multiband_drc_default_pass(const struct processing_module *mod, + struct sof_source *source, + struct sof_sink *sink, + uint32_t frames); /** * \brief Returns Multiband DRC processing function. @@ -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_generic.c b/src/audio/multiband_drc/multiband_drc_generic.c index bd64f5012fc8..b14387a1baaf 100644 --- a/src/audio/multiband_drc/multiband_drc_generic.c +++ b/src/audio/multiband_drc/multiband_drc_generic.c @@ -6,17 +6,18 @@ #include <stdint.h> #include <sof/audio/format.h> +#include <sof/audio/sink_source_utils.h> #include <sof/math/iir_df1.h> #include "multiband_drc.h" #include "../drc/drc_algorithm.h" -void multiband_drc_default_pass(const struct processing_module *mod, - const struct audio_stream *source, - struct audio_stream *sink, - uint32_t frames) +int multiband_drc_default_pass(const struct processing_module *mod, + struct sof_source *source, + struct sof_sink *sink, + uint32_t frames) { - audio_stream_copy(source, 0, sink, 0, audio_stream_get_channels(source) * frames); + return source_to_sink_copy(source, sink, true, frames * source_get_frame_bytes(source)); } static void multiband_drc_process_emp_crossover(struct multiband_drc_state *state, @@ -203,10 +204,10 @@ static void multiband_drc_process_deemp(struct multiband_drc_state *state, * :buf_drc_src[nch*nband] :buf_sink[nch] */ #if CONFIG_FORMAT_S16LE -static void multiband_drc_s16_default(const struct processing_module *mod, - const struct audio_stream *source, - struct audio_stream *sink, - uint32_t frames) +static int multiband_drc_s16_default(const struct processing_module *mod, + struct sof_source *source, + struct sof_sink *sink, + uint32_t frames) { struct multiband_drc_comp_data *cd = module_get_private_data(mod); struct multiband_drc_state *state = &cd->state; @@ -216,22 +217,35 @@ static void multiband_drc_s16_default(const struct processing_module *mod, int32_t buf_drc_sink[PLATFORM_MAX_CHANNELS * SOF_MULTIBAND_DRC_MAX_BANDS]; int32_t *band_buf_drc_src; int32_t *band_buf_drc_sink; - int16_t *x = audio_stream_get_rptr(source); - int16_t *y = audio_stream_get_wptr(sink); + const int16_t *x, *buf_x_start; + int16_t *y, *buf_y_start; + int buf_x_samples, buf_y_samples; + size_t n_bytes = frames * source_get_frame_bytes(source); int band; int nbuf; int npcm; + int ret; int ch; int i; - int nch = audio_stream_get_channels(source); + int nch = source_get_channels(source); int nband = cd->config->num_bands; int enable_emp_deemp = cd->config->enable_emp_deemp; int samples = frames * nch; + ret = source_get_data_s16(source, n_bytes, &x, &buf_x_start, &buf_x_samples); + if (ret) + return ret; + + ret = sink_get_buffer_s16(sink, n_bytes, &y, &buf_y_start, &buf_y_samples); + if (ret) { + source_release_data(source, 0); + return ret; + } + while (samples) { - nbuf = audio_stream_samples_without_wrap_s16(source, x); + nbuf = cir_buf_samples_to_wrap_s16(x, buf_x_start, buf_x_samples); npcm = MIN(samples, nbuf); - nbuf = audio_stream_samples_without_wrap_s16(sink, y); + nbuf = cir_buf_samples_to_wrap_s16(y, buf_y_start, buf_y_samples); npcm = MIN(npcm, nbuf); for (i = 0; i < npcm; i += nch) { for (ch = 0; ch < nch; ch++) { @@ -263,17 +277,22 @@ static void multiband_drc_s16_default(const struct processing_module *mod, } } samples -= npcm; - x = audio_stream_wrap(source, x); - y = audio_stream_wrap(sink, y); + if (x >= buf_x_start + buf_x_samples) + x = buf_x_start; + if (y >= buf_y_start + buf_y_samples) + y = buf_y_start; } + source_release_data(source, n_bytes); + sink_commit_buffer(sink, n_bytes); + return 0; } #endif /* CONFIG_FORMAT_S16LE */ #if CONFIG_FORMAT_S24LE -static void multiband_drc_s24_default(const struct processing_module *mod, - const struct audio_stream *source, - struct audio_stream *sink, - uint32_t frames) +static int multiband_drc_s24_default(const struct processing_module *mod, + struct sof_source *source, + struct sof_sink *sink, + uint32_t frames) { struct multiband_drc_comp_data *cd = module_get_private_data(mod); struct multiband_drc_state *state = &cd->state; @@ -283,22 +302,35 @@ static void multiband_drc_s24_default(const struct processing_module *mod, int32_t buf_drc_sink[PLATFORM_MAX_CHANNELS * SOF_MULTIBAND_DRC_MAX_BANDS]; int32_t *band_buf_drc_src; int32_t *band_buf_drc_sink; - int32_t *x = audio_stream_get_rptr(source); - int32_t *y = audio_stream_get_wptr(sink); + const int32_t *x, *buf_x_start; + int32_t *y, *buf_y_start; + int buf_x_samples, buf_y_samples; + size_t n_bytes = frames * source_get_frame_bytes(source); int band; int nbuf; int npcm; + int ret; int ch; int i; - int nch = audio_stream_get_channels(source); + int nch = source_get_channels(source); int nband = cd->config->num_bands; int enable_emp_deemp = cd->config->enable_emp_deemp; int samples = frames * nch; + ret = source_get_data_s32(source, n_bytes, &x, &buf_x_start, &buf_x_samples); + if (ret) + return ret; + + ret = sink_get_buffer_s32(sink, n_bytes, &y, &buf_y_start, &buf_y_samples); + if (ret) { + source_release_data(source, 0); + return ret; + } + while (samples) { - nbuf = audio_stream_samples_without_wrap_s24(source, x); + nbuf = cir_buf_samples_to_wrap_s32(x, buf_x_start, buf_x_samples); npcm = MIN(samples, nbuf); - nbuf = audio_stream_samples_without_wrap_s24(sink, y); + nbuf = cir_buf_samples_to_wrap_s32(y, buf_y_start, buf_y_samples); npcm = MIN(npcm, nbuf); for (i = 0; i < npcm; i += nch) { for (ch = 0; ch < nch; ch++) { @@ -330,17 +362,22 @@ static void multiband_drc_s24_default(const struct processing_module *mod, } } samples -= npcm; - x = audio_stream_wrap(source, x); - y = audio_stream_wrap(sink, y); + if (x >= buf_x_start + buf_x_samples) + x = buf_x_start; + if (y >= buf_y_start + buf_y_samples) + y = buf_y_start; } + source_release_data(source, n_bytes); + sink_commit_buffer(sink, n_bytes); + return 0; } #endif /* CONFIG_FORMAT_S24LE */ #if CONFIG_FORMAT_S32LE -static void multiband_drc_s32_default(const struct processing_module *mod, - const struct audio_stream *source, - struct audio_stream *sink, - uint32_t frames) +static int multiband_drc_s32_default(const struct processing_module *mod, + struct sof_source *source, + struct sof_sink *sink, + uint32_t frames) { struct multiband_drc_comp_data *cd = module_get_private_data(mod); struct multiband_drc_state *state = &cd->state; @@ -350,22 +387,35 @@ static void multiband_drc_s32_default(const struct processing_module *mod, int32_t buf_drc_sink[PLATFORM_MAX_CHANNELS * SOF_MULTIBAND_DRC_MAX_BANDS]; int32_t *band_buf_drc_src; int32_t *band_buf_drc_sink; - int32_t *x = audio_stream_get_rptr(source); - int32_t *y = audio_stream_get_wptr(sink); + const int32_t *x, *buf_x_start; + int32_t *y, *buf_y_start; + int buf_x_samples, buf_y_samples; + size_t n_bytes = frames * source_get_frame_bytes(source); int band; int nbuf; int npcm; + int ret; int ch; int i; - int nch = audio_stream_get_channels(source); + int nch = source_get_channels(source); int nband = cd->config->num_bands; int enable_emp_deemp = cd->config->enable_emp_deemp; int samples = frames * nch; + ret = source_get_data_s32(source, n_bytes, &x, &buf_x_start, &buf_x_samples); + if (ret) + return ret; + + ret = sink_get_buffer_s32(sink, n_bytes, &y, &buf_y_start, &buf_y_samples); + if (ret) { + source_release_data(source, 0); + return ret; + } + while (samples) { - nbuf = audio_stream_samples_without_wrap_s32(source, x); + nbuf = cir_buf_samples_to_wrap_s32(x, buf_x_start, buf_x_samples); npcm = MIN(samples, nbuf); - nbuf = audio_stream_samples_without_wrap_s32(sink, y); + nbuf = cir_buf_samples_to_wrap_s32(y, buf_y_start, buf_y_samples); npcm = MIN(npcm, nbuf); for (i = 0; i < npcm; i += nch) { for (ch = 0; ch < nch; ch++) { @@ -397,9 +447,14 @@ static void multiband_drc_s32_default(const struct processing_module *mod, } } samples -= npcm; - x = audio_stream_wrap(source, x); - y = audio_stream_wrap(sink, y); + if (x >= buf_x_start + buf_x_samples) + x = buf_x_start; + if (y >= buf_y_start + buf_y_samples) + y = buf_y_start; } + source_release_data(source, n_bytes); + sink_commit_buffer(sink, n_bytes); + return 0; } #endif /* CONFIG_FORMAT_S32LE */ 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..c651f772a598 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,20 +107,22 @@ 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; } mod->verify_params_flags = BUFF_PARAMS_CHANNELS; +#if CONFIG_IPC_MAJOR_3 mod->no_pause = true; +#endif cd->comp_type = type; 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 +137,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 +240,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) { @@ -292,9 +292,11 @@ static int demux_trigger(struct processing_module *mod, int cmd) */ if (cmd == COMP_TRIGGER_PRE_START) { struct comp_buffer *b; + struct comp_dev *sink_comp; - comp_dev_for_each_producer(mod->dev, b) { - if (comp_buffer_get_sink_component(b)->pipeline != mod->dev->pipeline) + comp_dev_for_each_consumer(mod->dev, b) { + sink_comp = comp_buffer_get_sink_component(b); + if (sink_comp && sink_comp->pipeline != mod->dev->pipeline) audio_stream_set_overrun(&b->stream, true); } } @@ -314,10 +316,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 +344,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 +369,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 +393,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 +422,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 +434,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..b8454043e0fd 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) { @@ -69,8 +70,10 @@ static int mux_set_values(struct processing_module *mod) } if (cd->comp_type == SOF_COMP_MUX) { - if (mux_mix_check(cfg)) + if (mux_mix_check(cfg)) { comp_err(dev, "mux component is not able to mix channels"); + return -EINVAL; + } } for (i = 0; i < cfg->num_streams; i++) { @@ -98,6 +101,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..8f33e44ea1ae 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,13 +32,25 @@ 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); + uint32_t base_ch = cfg->base_cfg.audio_fmt.channels_count; + uint32_t ref_ch = cfg->reference_format.channels_count; + uint32_t out_ch = cfg->output_format.channels_count; int mask = 1; int i; + /* base+reference map 1:1 to contiguous output channels: sink width + * must equal their sum and fit the 8-bit output mask + */ + if (base_ch + ref_ch > PLATFORM_MAX_CHANNELS || out_ch != base_ch + ref_ch) { + comp_err(dev, "invalid channel count: base %u + reference %u -> output %u (max %d)", + base_ch, ref_ch, out_ch, PLATFORM_MAX_CHANNELS); + return -EINVAL; + } + cd->config.num_streams = MUX_MAX_STREAMS; /* clear masks */ @@ -45,12 +58,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 < base_ch; 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 < ref_ch; i++) { cd->config.streams[1].mask[i] = mask; mask <<= 1; } @@ -67,7 +80,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 int 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 +89,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 +108,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,30 +118,45 @@ 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)); + /* host-supplied queue ID indexes streams[] */ + if (j >= MUX_MAX_STREAMS) { + comp_err(dev, "invalid source queue ID %d (valid range 0..%d)", + j, MUX_MAX_STREAMS - 1); + return -EINVAL; + } 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); } } mux_prepare_look_up_table(mod); + + return 0; } 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 != sizeof(*cfg)) { + 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); - - return ret; + return set_mux_params(mod, cfg); } #endif /* CONFIG_COMP_MUX */ 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/pcm_converter/pcm_converter.c b/src/audio/pcm_converter/pcm_converter.c index b489cc6e664b..4718d1814ede 100644 --- a/src/audio/pcm_converter/pcm_converter.c +++ b/src/audio/pcm_converter/pcm_converter.c @@ -15,35 +15,23 @@ #include <sof/audio/pcm_converter.h> #include <rtos/panic.h> -int pcm_convert_as_linear(const struct audio_stream *source, uint32_t ioffset, - struct audio_stream *sink, uint32_t ooffset, - uint32_t samples, pcm_converter_lin_func converter) +int pcm_convert_as_linear(const struct cir_buf_source *source, size_t s_size_in, + struct cir_buf_sink *sink, size_t s_size_out, + size_t samples, pcm_converter_lin_func converter) { - const int s_size_in = audio_stream_sample_bytes(source); - const int s_size_out = audio_stream_sample_bytes(sink); const int log2_s_size_in = ffs(s_size_in) - 1; const int log2_s_size_out = ffs(s_size_out) - 1; - char *r_ptr = audio_stream_get_frag(source, audio_stream_get_rptr(source), ioffset, - s_size_in); - char *w_ptr = audio_stream_get_frag(sink, audio_stream_get_wptr(sink), ooffset, - s_size_out); - int i = 0; - int chunk; - int N1, N2; + const char *r_ptr = source->ptr; + char *w_ptr = sink->ptr; + size_t i = 0; + size_t chunk; + size_t N1, N2; - /* assert enough avail/free samples in source and sink buffer */ - if (audio_stream_get_avail_samples(source) < samples + ioffset) - return -EINVAL; - if (audio_stream_get_free_samples(sink) < samples + ooffset) - return -EINVAL; while (i < samples) { - /* calculate chunk size */ - /* "">> log2_s_size" is equal "/ s_size" here */ - N1 = audio_stream_bytes_without_wrap(source, r_ptr) >> - log2_s_size_in; - N2 = audio_stream_bytes_without_wrap(sink, w_ptr) >> - log2_s_size_out; + /* calculate chunk size; shifting by log2_s_size is dividing by s_size */ + N1 = ((const char *)source->buf_end - r_ptr) >> log2_s_size_in; + N2 = ((char *)sink->buf_end - w_ptr) >> log2_s_size_out; chunk = MIN(N1, N2); chunk = MIN(chunk, samples - i); @@ -51,16 +39,52 @@ int pcm_convert_as_linear(const struct audio_stream *source, uint32_t ioffset, converter(r_ptr, w_ptr, chunk); /* move pointers */ - r_ptr = audio_stream_wrap(source, r_ptr + chunk * s_size_in); - w_ptr = audio_stream_wrap(sink, w_ptr + chunk * s_size_out); + r_ptr = cir_buf_wrap(r_ptr + chunk * s_size_in, + source->buf_start, source->buf_end); + w_ptr = cir_buf_wrap(w_ptr + chunk * s_size_out, + sink->buf_start, sink->buf_end); i += chunk; } return samples; } -int just_copy(const struct audio_stream *source, uint32_t ioffset, - struct audio_stream *sink, uint32_t ooffset, uint32_t samples, uint32_t chmap) +/* Copy "bytes" of raw data from a source to a sink circular buffer. */ +static void just_copy_bytes(const struct cir_buf_source *source, struct cir_buf_sink *sink, + size_t bytes) { - return audio_stream_copy(source, ioffset, sink, ooffset, samples); + cir_buf_copy(source->ptr, source->buf_start, source->buf_end, + sink->ptr, sink->buf_start, sink->buf_end, bytes); +} + +int just_copy_1b(const struct cir_buf_source *source, uint32_t src_channels, + struct cir_buf_sink *sink, uint32_t sink_channels, + size_t samples, uint32_t chmap) +{ + just_copy_bytes(source, sink, samples); + return samples; +} + +int just_copy_2b(const struct cir_buf_source *source, uint32_t src_channels, + struct cir_buf_sink *sink, uint32_t sink_channels, + size_t samples, uint32_t chmap) +{ + just_copy_bytes(source, sink, samples * sizeof(int16_t)); + return samples; +} + +int just_copy_3b(const struct cir_buf_source *source, uint32_t src_channels, + struct cir_buf_sink *sink, uint32_t sink_channels, + size_t samples, uint32_t chmap) +{ + just_copy_bytes(source, sink, samples * 3); + return samples; +} + +int just_copy_4b(const struct cir_buf_source *source, uint32_t src_channels, + struct cir_buf_sink *sink, uint32_t sink_channels, + size_t samples, uint32_t chmap) +{ + just_copy_bytes(source, sink, samples * sizeof(int32_t)); + return samples; } diff --git a/src/audio/pcm_converter/pcm_converter_generic.c b/src/audio/pcm_converter/pcm_converter_generic.c index fe96e6d1f124..fe346737750b 100644 --- a/src/audio/pcm_converter/pcm_converter_generic.c +++ b/src/audio/pcm_converter/pcm_converter_generic.c @@ -36,24 +36,22 @@ #define BYTES_TO_S32_SAMPLES 2 #if CONFIG_PCM_CONVERTER_FORMAT_U8 && CONFIG_PCM_CONVERTER_FORMAT_S32LE -static int pcm_convert_u8_to_s32(const struct audio_stream *source, - uint32_t ioffset, struct audio_stream *sink, - uint32_t ooffset, uint32_t samples, uint32_t chmap) +static int pcm_convert_u8_to_s32(const struct cir_buf_source *source, + uint32_t src_channels, struct cir_buf_sink *sink, + uint32_t sink_channels, size_t samples, uint32_t chmap) { - uint8_t *src = audio_stream_get_rptr(source); - int32_t *dst = audio_stream_get_wptr(sink); - uint32_t processed; - uint32_t nmax, i, n; + const uint8_t *src = source->ptr; + int32_t *dst = sink->ptr; + size_t processed; + size_t nmax, i, n; - src += ioffset; - dst += ooffset; for (processed = 0; processed < samples; processed += n) { - src = audio_stream_wrap(source, src); - dst = audio_stream_wrap(sink, dst); + src = cir_buf_wrap(src, source->buf_start, source->buf_end); + dst = cir_buf_wrap(dst, sink->buf_start, sink->buf_end); n = samples - processed; - nmax = audio_stream_bytes_without_wrap(source, src) >> BYTES_TO_U8_SAMPLES; + nmax = cir_buf_bytes_without_wrap(src, source->buf_end) >> BYTES_TO_U8_SAMPLES; n = MIN(n, nmax); - nmax = audio_stream_bytes_without_wrap(sink, dst) >> BYTES_TO_S32_SAMPLES; + nmax = cir_buf_bytes_without_wrap(dst, sink->buf_end) >> BYTES_TO_S32_SAMPLES; n = MIN(n, nmax); for (i = 0; i < n; i++) { *dst = (INT8_MIN + *src) << 24; @@ -65,24 +63,22 @@ static int pcm_convert_u8_to_s32(const struct audio_stream *source, return samples; } -static int pcm_convert_s32_to_u8(const struct audio_stream *source, - uint32_t ioffset, struct audio_stream *sink, - uint32_t ooffset, uint32_t samples, uint32_t chmap) +static int pcm_convert_s32_to_u8(const struct cir_buf_source *source, + uint32_t src_channels, struct cir_buf_sink *sink, + uint32_t sink_channels, size_t samples, uint32_t chmap) { - int32_t *src = audio_stream_get_rptr(source); - uint8_t *dst = audio_stream_get_wptr(sink); - uint32_t processed; - uint32_t nmax, i, n; + const int32_t *src = source->ptr; + uint8_t *dst = sink->ptr; + size_t processed; + size_t nmax, i, n; - src += ioffset; - dst += ooffset; for (processed = 0; processed < samples; processed += n) { - src = audio_stream_wrap(source, src); - dst = audio_stream_wrap(sink, dst); + src = cir_buf_wrap(src, source->buf_start, source->buf_end); + dst = cir_buf_wrap(dst, sink->buf_start, sink->buf_end); n = samples - processed; - nmax = audio_stream_bytes_without_wrap(source, src) >> BYTES_TO_S32_SAMPLES; + nmax = cir_buf_bytes_without_wrap(src, source->buf_end) >> BYTES_TO_S32_SAMPLES; n = MIN(n, nmax); - nmax = audio_stream_bytes_without_wrap(sink, dst) >> BYTES_TO_U8_SAMPLES; + nmax = cir_buf_bytes_without_wrap(dst, sink->buf_end) >> BYTES_TO_U8_SAMPLES; n = MIN(n, nmax); for (i = 0; i < n; i++) { *dst = sat_int8(Q_SHIFT_RND(*src, 24, 0)) - INT8_MIN; @@ -96,24 +92,22 @@ static int pcm_convert_s32_to_u8(const struct audio_stream *source, #endif /* CONFIG_PCM_CONVERTER_FORMAT_U8 && CONFIG_PCM_CONVERTER_FORMAT_S32LE */ #if CONFIG_PCM_CONVERTER_FORMAT_A_LAW && CONFIG_PCM_CONVERTER_FORMAT_S32LE -static int pcm_convert_alaw_to_s32(const struct audio_stream *source, - uint32_t ioffset, struct audio_stream *sink, - uint32_t ooffset, uint32_t samples, uint32_t chmap) +static int pcm_convert_alaw_to_s32(const struct cir_buf_source *source, + uint32_t src_channels, struct cir_buf_sink *sink, + uint32_t sink_channels, size_t samples, uint32_t chmap) { - const uint8_t *src = audio_stream_get_rptr(source); - int32_t *dst = audio_stream_get_wptr(sink); - uint32_t processed; - uint32_t nmax, i, n; + const uint8_t *src = source->ptr; + int32_t *dst = sink->ptr; + size_t processed; + size_t nmax, i, n; - src += ioffset; - dst += ooffset; for (processed = 0; processed < samples; processed += n) { - src = audio_stream_wrap(source, (void *)src); - dst = audio_stream_wrap(sink, dst); + src = cir_buf_wrap(src, source->buf_start, source->buf_end); + dst = cir_buf_wrap(dst, sink->buf_start, sink->buf_end); n = samples - processed; - nmax = audio_stream_bytes_without_wrap(source, src) >> BYTES_TO_U8_SAMPLES; + nmax = cir_buf_bytes_without_wrap(src, source->buf_end) >> BYTES_TO_U8_SAMPLES; n = MIN(n, nmax); - nmax = audio_stream_bytes_without_wrap(sink, dst) >> BYTES_TO_S32_SAMPLES; + nmax = cir_buf_bytes_without_wrap(dst, sink->buf_end) >> BYTES_TO_S32_SAMPLES; n = MIN(n, nmax); for (i = 0; i < n; i++) { *dst = sofm_a_law_decode(*src) << 16; @@ -125,24 +119,22 @@ static int pcm_convert_alaw_to_s32(const struct audio_stream *source, return samples; } -static int pcm_convert_s32_to_alaw(const struct audio_stream *source, - uint32_t ioffset, struct audio_stream *sink, - uint32_t ooffset, uint32_t samples, uint32_t chmap) +static int pcm_convert_s32_to_alaw(const struct cir_buf_source *source, + uint32_t src_channels, struct cir_buf_sink *sink, + uint32_t sink_channels, size_t samples, uint32_t chmap) { - const int32_t *src = audio_stream_get_rptr(source); - uint8_t *dst = audio_stream_get_wptr(sink); - uint32_t processed; - uint32_t nmax, i, n; + const int32_t *src = source->ptr; + uint8_t *dst = sink->ptr; + size_t processed; + size_t nmax, i, n; - src += ioffset; - dst += ooffset; for (processed = 0; processed < samples; processed += n) { - src = audio_stream_wrap(source, (void *)src); - dst = audio_stream_wrap(sink, dst); + src = cir_buf_wrap(src, source->buf_start, source->buf_end); + dst = cir_buf_wrap(dst, sink->buf_start, sink->buf_end); n = samples - processed; - nmax = audio_stream_bytes_without_wrap(source, src) >> BYTES_TO_S32_SAMPLES; + nmax = cir_buf_bytes_without_wrap(src, source->buf_end) >> BYTES_TO_S32_SAMPLES; n = MIN(n, nmax); - nmax = audio_stream_bytes_without_wrap(sink, dst) >> BYTES_TO_U8_SAMPLES; + nmax = cir_buf_bytes_without_wrap(dst, sink->buf_end) >> BYTES_TO_U8_SAMPLES; n = MIN(n, nmax); for (i = 0; i < n; i++) { *dst = sofm_a_law_encode(*src >> 16); @@ -156,24 +148,22 @@ static int pcm_convert_s32_to_alaw(const struct audio_stream *source, #endif /* CONFIG_PCM_CONVERTER_FORMAT_A_LAW && CONFIG_PCM_CONVERTER_FORMAT_S32LE */ #if CONFIG_PCM_CONVERTER_FORMAT_MU_LAW && CONFIG_PCM_CONVERTER_FORMAT_S32LE -static int pcm_convert_mulaw_to_s32(const struct audio_stream *source, - uint32_t ioffset, struct audio_stream *sink, - uint32_t ooffset, uint32_t samples, uint32_t chmap) +static int pcm_convert_mulaw_to_s32(const struct cir_buf_source *source, + uint32_t src_channels, struct cir_buf_sink *sink, + uint32_t sink_channels, size_t samples, uint32_t chmap) { - const uint8_t *src = audio_stream_get_rptr(source); - int32_t *dst = audio_stream_get_wptr(sink); - uint32_t processed; - uint32_t nmax, i, n; + const uint8_t *src = source->ptr; + int32_t *dst = sink->ptr; + size_t processed; + size_t nmax, i, n; - src += ioffset; - dst += ooffset; for (processed = 0; processed < samples; processed += n) { - src = audio_stream_wrap(source, (void *)src); - dst = audio_stream_wrap(sink, dst); + src = cir_buf_wrap(src, source->buf_start, source->buf_end); + dst = cir_buf_wrap(dst, sink->buf_start, sink->buf_end); n = samples - processed; - nmax = audio_stream_bytes_without_wrap(source, src) >> BYTES_TO_U8_SAMPLES; + nmax = cir_buf_bytes_without_wrap(src, source->buf_end) >> BYTES_TO_U8_SAMPLES; n = MIN(n, nmax); - nmax = audio_stream_bytes_without_wrap(sink, dst) >> BYTES_TO_S32_SAMPLES; + nmax = cir_buf_bytes_without_wrap(dst, sink->buf_end) >> BYTES_TO_S32_SAMPLES; n = MIN(n, nmax); for (i = 0; i < n; i++) { *dst = sofm_mu_law_decode(*src) << 16; @@ -185,24 +175,22 @@ static int pcm_convert_mulaw_to_s32(const struct audio_stream *source, return samples; } -static int pcm_convert_s32_to_mulaw(const struct audio_stream *source, - uint32_t ioffset, struct audio_stream *sink, - uint32_t ooffset, uint32_t samples, uint32_t chmap) +static int pcm_convert_s32_to_mulaw(const struct cir_buf_source *source, + uint32_t src_channels, struct cir_buf_sink *sink, + uint32_t sink_channels, size_t samples, uint32_t chmap) { - const int32_t *src = audio_stream_get_rptr(source); - uint8_t *dst = audio_stream_get_wptr(sink); - uint32_t processed; - uint32_t nmax, i, n; + const int32_t *src = source->ptr; + uint8_t *dst = sink->ptr; + size_t processed; + size_t nmax, i, n; - src += ioffset; - dst += ooffset; for (processed = 0; processed < samples; processed += n) { - src = audio_stream_wrap(source, (void *)src); - dst = audio_stream_wrap(sink, dst); + src = cir_buf_wrap(src, source->buf_start, source->buf_end); + dst = cir_buf_wrap(dst, sink->buf_start, sink->buf_end); n = samples - processed; - nmax = audio_stream_bytes_without_wrap(source, src) >> BYTES_TO_S32_SAMPLES; + nmax = cir_buf_bytes_without_wrap(src, source->buf_end) >> BYTES_TO_S32_SAMPLES; n = MIN(n, nmax); - nmax = audio_stream_bytes_without_wrap(sink, dst) >> BYTES_TO_U8_SAMPLES; + nmax = cir_buf_bytes_without_wrap(dst, sink->buf_end) >> BYTES_TO_U8_SAMPLES; n = MIN(n, nmax); for (i = 0; i < n; i++) { *dst = sofm_mu_law_encode(*src >> 16); @@ -217,24 +205,22 @@ static int pcm_convert_s32_to_mulaw(const struct audio_stream *source, #if CONFIG_PCM_CONVERTER_FORMAT_S16LE && CONFIG_PCM_CONVERTER_FORMAT_S24LE -static int pcm_convert_s16_to_s24(const struct audio_stream *source, - uint32_t ioffset, struct audio_stream *sink, - uint32_t ooffset, uint32_t samples, uint32_t chmap) +static int pcm_convert_s16_to_s24(const struct cir_buf_source *source, + uint32_t src_channels, struct cir_buf_sink *sink, + uint32_t sink_channels, size_t samples, uint32_t chmap) { - int16_t *src = audio_stream_get_rptr(source); - int32_t *dst = audio_stream_get_wptr(sink); - int processed; - int nmax, i, n; + const int16_t *src = source->ptr; + int32_t *dst = sink->ptr; + size_t processed; + size_t nmax, i, n; - src += ioffset; - dst += ooffset; for (processed = 0; processed < samples; processed += n) { - src = audio_stream_wrap(source, src); - dst = audio_stream_wrap(sink, dst); + src = cir_buf_wrap(src, source->buf_start, source->buf_end); + dst = cir_buf_wrap(dst, sink->buf_start, sink->buf_end); n = samples - processed; - nmax = audio_stream_bytes_without_wrap(source, src) >> BYTES_TO_S16_SAMPLES; + nmax = cir_buf_bytes_without_wrap(src, source->buf_end) >> BYTES_TO_S16_SAMPLES; n = MIN(n, nmax); - nmax = audio_stream_bytes_without_wrap(sink, dst) >> BYTES_TO_S32_SAMPLES; + nmax = cir_buf_bytes_without_wrap(dst, sink->buf_end) >> BYTES_TO_S32_SAMPLES; n = MIN(n, nmax); for (i = 0; i < n; i++) { *dst = *src << 8; @@ -246,24 +232,22 @@ static int pcm_convert_s16_to_s24(const struct audio_stream *source, return samples; } -static int pcm_convert_s24_to_s16(const struct audio_stream *source, - uint32_t ioffset, struct audio_stream *sink, - uint32_t ooffset, uint32_t samples, uint32_t chmap) +static int pcm_convert_s24_to_s16(const struct cir_buf_source *source, + uint32_t src_channels, struct cir_buf_sink *sink, + uint32_t sink_channels, size_t samples, uint32_t chmap) { - int32_t *src = audio_stream_get_rptr(source); - int16_t *dst = audio_stream_get_wptr(sink); - int processed; - int nmax, i, n; + const int32_t *src = source->ptr; + int16_t *dst = sink->ptr; + size_t processed; + size_t nmax, i, n; - src += ioffset; - dst += ooffset; for (processed = 0; processed < samples; processed += n) { - src = audio_stream_wrap(source, src); - dst = audio_stream_wrap(sink, dst); + src = cir_buf_wrap(src, source->buf_start, source->buf_end); + dst = cir_buf_wrap(dst, sink->buf_start, sink->buf_end); n = samples - processed; - nmax = audio_stream_bytes_without_wrap(source, src) >> BYTES_TO_S32_SAMPLES; + nmax = cir_buf_bytes_without_wrap(src, source->buf_end) >> BYTES_TO_S32_SAMPLES; n = MIN(n, nmax); - nmax = audio_stream_bytes_without_wrap(sink, dst) >> BYTES_TO_S16_SAMPLES; + nmax = cir_buf_bytes_without_wrap(dst, sink->buf_end) >> BYTES_TO_S16_SAMPLES; n = MIN(n, nmax); for (i = 0; i < n; i++) { *dst = sat_int16(Q_SHIFT_RND(sign_extend_s24(*src), 23, 15)); @@ -279,24 +263,22 @@ static int pcm_convert_s24_to_s16(const struct audio_stream *source, #if CONFIG_PCM_CONVERTER_FORMAT_S16LE && CONFIG_PCM_CONVERTER_FORMAT_S32LE -static int pcm_convert_s16_to_s32(const struct audio_stream *source, - uint32_t ioffset, struct audio_stream *sink, - uint32_t ooffset, uint32_t samples, uint32_t chmap) +static int pcm_convert_s16_to_s32(const struct cir_buf_source *source, + uint32_t src_channels, struct cir_buf_sink *sink, + uint32_t sink_channels, size_t samples, uint32_t chmap) { - int16_t *src = audio_stream_get_rptr(source); - int32_t *dst = audio_stream_get_wptr(sink); - int processed; - int nmax, i, n; + const int16_t *src = source->ptr; + int32_t *dst = sink->ptr; + size_t processed; + size_t nmax, i, n; - src += ioffset; - dst += ooffset; for (processed = 0; processed < samples; processed += n) { - src = audio_stream_wrap(source, src); - dst = audio_stream_wrap(sink, dst); + src = cir_buf_wrap(src, source->buf_start, source->buf_end); + dst = cir_buf_wrap(dst, sink->buf_start, sink->buf_end); n = samples - processed; - nmax = audio_stream_bytes_without_wrap(source, src) >> BYTES_TO_S16_SAMPLES; + nmax = cir_buf_bytes_without_wrap(src, source->buf_end) >> BYTES_TO_S16_SAMPLES; n = MIN(n, nmax); - nmax = audio_stream_bytes_without_wrap(sink, dst) >> BYTES_TO_S32_SAMPLES; + nmax = cir_buf_bytes_without_wrap(dst, sink->buf_end) >> BYTES_TO_S32_SAMPLES; n = MIN(n, nmax); for (i = 0; i < n; i++) { *dst = *src << 16; @@ -308,24 +290,22 @@ static int pcm_convert_s16_to_s32(const struct audio_stream *source, return samples; } -static int pcm_convert_s32_to_s16(const struct audio_stream *source, - uint32_t ioffset, struct audio_stream *sink, - uint32_t ooffset, uint32_t samples, uint32_t chmap) +static int pcm_convert_s32_to_s16(const struct cir_buf_source *source, + uint32_t src_channels, struct cir_buf_sink *sink, + uint32_t sink_channels, size_t samples, uint32_t chmap) { - int32_t *src = audio_stream_get_rptr(source); - int16_t *dst = audio_stream_get_wptr(sink); - int processed; - int nmax, i, n; + const int32_t *src = source->ptr; + int16_t *dst = sink->ptr; + size_t processed; + size_t nmax, i, n; - src += ioffset; - dst += ooffset; for (processed = 0; processed < samples; processed += n) { - src = audio_stream_wrap(source, src); - dst = audio_stream_wrap(sink, dst); + src = cir_buf_wrap(src, source->buf_start, source->buf_end); + dst = cir_buf_wrap(dst, sink->buf_start, sink->buf_end); n = samples - processed; - nmax = audio_stream_bytes_without_wrap(source, src) >> BYTES_TO_S32_SAMPLES; + nmax = cir_buf_bytes_without_wrap(src, source->buf_end) >> BYTES_TO_S32_SAMPLES; n = MIN(n, nmax); - nmax = audio_stream_bytes_without_wrap(sink, dst) >> BYTES_TO_S16_SAMPLES; + nmax = cir_buf_bytes_without_wrap(dst, sink->buf_end) >> BYTES_TO_S16_SAMPLES; n = MIN(n, nmax); for (i = 0; i < n; i++) { *dst = sat_int16(Q_SHIFT_RND(*src, 31, 15)); @@ -341,24 +321,22 @@ static int pcm_convert_s32_to_s16(const struct audio_stream *source, #if CONFIG_PCM_CONVERTER_FORMAT_S24LE && CONFIG_PCM_CONVERTER_FORMAT_S32LE -static int pcm_convert_s24_to_s32(const struct audio_stream *source, - uint32_t ioffset, struct audio_stream *sink, - uint32_t ooffset, uint32_t samples, uint32_t chmap) +static int pcm_convert_s24_to_s32(const struct cir_buf_source *source, + uint32_t src_channels, struct cir_buf_sink *sink, + uint32_t sink_channels, size_t samples, uint32_t chmap) { - int32_t *src = audio_stream_get_rptr(source); - int32_t *dst = audio_stream_get_wptr(sink); - int processed; - int nmax, i, n; + const int32_t *src = source->ptr; + int32_t *dst = sink->ptr; + size_t processed; + size_t nmax, i, n; - src += ioffset; - dst += ooffset; for (processed = 0; processed < samples; processed += n) { - src = audio_stream_wrap(source, src); - dst = audio_stream_wrap(sink, dst); + src = cir_buf_wrap(src, source->buf_start, source->buf_end); + dst = cir_buf_wrap(dst, sink->buf_start, sink->buf_end); n = samples - processed; - nmax = audio_stream_bytes_without_wrap(source, src) >> BYTES_TO_S32_SAMPLES; + nmax = cir_buf_bytes_without_wrap(src, source->buf_end) >> BYTES_TO_S32_SAMPLES; n = MIN(n, nmax); - nmax = audio_stream_bytes_without_wrap(sink, dst) >> BYTES_TO_S32_SAMPLES; + nmax = cir_buf_bytes_without_wrap(dst, sink->buf_end) >> BYTES_TO_S32_SAMPLES; n = MIN(n, nmax); for (i = 0; i < n; i++) { *dst = *src << 8; @@ -370,24 +348,22 @@ static int pcm_convert_s24_to_s32(const struct audio_stream *source, return samples; } -static int pcm_convert_s32_to_s24(const struct audio_stream *source, - uint32_t ioffset, struct audio_stream *sink, - uint32_t ooffset, uint32_t samples, uint32_t chmap) +static int pcm_convert_s32_to_s24(const struct cir_buf_source *source, + uint32_t src_channels, struct cir_buf_sink *sink, + uint32_t sink_channels, size_t samples, uint32_t chmap) { - int32_t *src = audio_stream_get_rptr(source); - int32_t *dst = audio_stream_get_wptr(sink); - int processed; - int nmax, i, n; + const int32_t *src = source->ptr; + int32_t *dst = sink->ptr; + size_t processed; + size_t nmax, i, n; - src += ioffset; - dst += ooffset; for (processed = 0; processed < samples; processed += n) { - src = audio_stream_wrap(source, src); - dst = audio_stream_wrap(sink, dst); + src = cir_buf_wrap(src, source->buf_start, source->buf_end); + dst = cir_buf_wrap(dst, sink->buf_start, sink->buf_end); n = samples - processed; - nmax = audio_stream_bytes_without_wrap(source, src) >> BYTES_TO_S32_SAMPLES; + nmax = cir_buf_bytes_without_wrap(src, source->buf_end) >> BYTES_TO_S32_SAMPLES; n = MIN(n, nmax); - nmax = audio_stream_bytes_without_wrap(sink, dst) >> BYTES_TO_S32_SAMPLES; + nmax = cir_buf_bytes_without_wrap(dst, sink->buf_end) >> BYTES_TO_S32_SAMPLES; n = MIN(n, nmax); for (i = 0; i < n; i++) { *dst = sat_int24(Q_SHIFT_RND(*src, 31, 23)); @@ -399,24 +375,22 @@ static int pcm_convert_s32_to_s24(const struct audio_stream *source, return samples; } -static int pcm_convert_s32_to_s24_be(const struct audio_stream *source, - uint32_t ioffset, struct audio_stream *sink, - uint32_t ooffset, uint32_t samples, uint32_t chmap) +static int pcm_convert_s32_to_s24_be(const struct cir_buf_source *source, + uint32_t src_channels, struct cir_buf_sink *sink, + uint32_t sink_channels, size_t samples, uint32_t chmap) { - int32_t *src = audio_stream_get_rptr(source); - int32_t *dst = audio_stream_get_wptr(sink); - int processed; - int nmax, i, n; + const int32_t *src = source->ptr; + int32_t *dst = sink->ptr; + size_t processed; + size_t nmax, i, n; - src += ioffset; - dst += ooffset; for (processed = 0; processed < samples; processed += n) { - src = audio_stream_wrap(source, src); - dst = audio_stream_wrap(sink, dst); + src = cir_buf_wrap(src, source->buf_start, source->buf_end); + dst = cir_buf_wrap(dst, sink->buf_start, sink->buf_end); n = samples - processed; - nmax = audio_stream_samples_without_wrap_s32(source, src); + nmax = cir_buf_samples_without_wrap_s32(src, source->buf_end); n = MIN(n, nmax); - nmax = audio_stream_samples_without_wrap_s32(sink, dst); + nmax = cir_buf_samples_without_wrap_s32(dst, sink->buf_end); n = MIN(n, nmax); for (i = 0; i < n; i++) { *dst = sat_int24(Q_SHIFT_RND(*src, 31, 23)) << 8; @@ -539,7 +513,7 @@ static int32_t _pcm_convert_i_to_f(int32_t src, int32_t pow) #if CONFIG_PCM_CONVERTER_FORMAT_FLOAT && CONFIG_PCM_CONVERTER_FORMAT_S16LE static void pcm_convert_s16_to_f_lin(const void *psrc, void *pdst, - uint32_t samples) + size_t samples) { const int16_t *src = psrc; int32_t *dst = pdst; /* float */ @@ -552,7 +526,7 @@ static void pcm_convert_s16_to_f_lin(const void *psrc, void *pdst, } static void pcm_convert_f_to_s16_lin(const void *psrc, void *pdst, - uint32_t samples) + size_t samples) { const int32_t *src = psrc; /* float */ int16_t *dst = pdst; @@ -564,26 +538,26 @@ static void pcm_convert_f_to_s16_lin(const void *psrc, void *pdst, dst[i] = sat_int16(_pcm_convert_f_to_i(src[i], 15)); } -static int pcm_convert_s16_to_f(const struct audio_stream *source, - uint32_t ioffset, struct audio_stream *sink, - uint32_t ooffset, uint32_t samples, uint32_t chmap) +static int pcm_convert_s16_to_f(const struct cir_buf_source *source, + uint32_t src_channels, struct cir_buf_sink *sink, + uint32_t sink_channels, size_t samples, uint32_t chmap) { - return pcm_convert_as_linear(source, ioffset, sink, ooffset, samples, + return pcm_convert_as_linear(source, sizeof(int16_t), sink, sizeof(int32_t), samples, pcm_convert_s16_to_f_lin); } -static int pcm_convert_f_to_s16(const struct audio_stream *source, - uint32_t ioffset, struct audio_stream *sink, - uint32_t ooffset, uint32_t samples, uint32_t chmap) +static int pcm_convert_f_to_s16(const struct cir_buf_source *source, + uint32_t src_channels, struct cir_buf_sink *sink, + uint32_t sink_channels, size_t samples, uint32_t chmap) { - return pcm_convert_as_linear(source, ioffset, sink, ooffset, samples, + return pcm_convert_as_linear(source, sizeof(int32_t), sink, sizeof(int16_t), samples, pcm_convert_f_to_s16_lin); } #endif /* CONFIG_PCM_CONVERTER_FORMAT_FLOAT && CONFIG_PCM_CONVERTER_FORMAT_S16LE */ #if CONFIG_PCM_CONVERTER_FORMAT_FLOAT && CONFIG_PCM_CONVERTER_FORMAT_S24LE static void pcm_convert_s24_to_f_lin(const void *psrc, void *pdst, - uint32_t samples) + size_t samples) { const int32_t *src = psrc; int32_t *dst = pdst; /* float */ @@ -596,7 +570,7 @@ static void pcm_convert_s24_to_f_lin(const void *psrc, void *pdst, } static void pcm_convert_f_to_s24_lin(const void *psrc, void *pdst, - uint32_t samples) + size_t samples) { const int32_t *src = psrc; /* float */ int32_t *dst = pdst; @@ -608,26 +582,26 @@ static void pcm_convert_f_to_s24_lin(const void *psrc, void *pdst, dst[i] = sat_int24(_pcm_convert_f_to_i(src[i], 23)); } -static int pcm_convert_s24_to_f(const struct audio_stream *source, - uint32_t ioffset, struct audio_stream *sink, - uint32_t ooffset, uint32_t samples, uint32_t chmap) +static int pcm_convert_s24_to_f(const struct cir_buf_source *source, + uint32_t src_channels, struct cir_buf_sink *sink, + uint32_t sink_channels, size_t samples, uint32_t chmap) { - return pcm_convert_as_linear(source, ioffset, sink, ooffset, samples, + return pcm_convert_as_linear(source, sizeof(int32_t), sink, sizeof(int32_t), samples, pcm_convert_s24_to_f_lin); } -static int pcm_convert_f_to_s24(const struct audio_stream *source, - uint32_t ioffset, struct audio_stream *sink, - uint32_t ooffset, uint32_t samples, uint32_t chmap) +static int pcm_convert_f_to_s24(const struct cir_buf_source *source, + uint32_t src_channels, struct cir_buf_sink *sink, + uint32_t sink_channels, size_t samples, uint32_t chmap) { - return pcm_convert_as_linear(source, ioffset, sink, ooffset, samples, + return pcm_convert_as_linear(source, sizeof(int32_t), sink, sizeof(int32_t), samples, pcm_convert_f_to_s24_lin); } #endif /* CONFIG_PCM_CONVERTER_FORMAT_FLOAT && CONFIG_PCM_CONVERTER_FORMAT_S24LE */ #if CONFIG_PCM_CONVERTER_FORMAT_FLOAT && CONFIG_PCM_CONVERTER_FORMAT_S32LE static void pcm_convert_s32_to_f_lin(const void *psrc, void *pdst, - uint32_t samples) + size_t samples) { const int32_t *src = psrc; int32_t *dst = pdst; /* float */ @@ -640,7 +614,7 @@ static void pcm_convert_s32_to_f_lin(const void *psrc, void *pdst, } static void pcm_convert_f_to_s32_lin(const void *psrc, void *pdst, - uint32_t samples) + size_t samples) { const int32_t *src = psrc; /* float */ int32_t *dst = pdst; @@ -652,60 +626,60 @@ static void pcm_convert_f_to_s32_lin(const void *psrc, void *pdst, dst[i] = _pcm_convert_f_to_i(src[i], 31); } -static int pcm_convert_s32_to_f(const struct audio_stream *source, - uint32_t ioffset, struct audio_stream *sink, - uint32_t ooffset, uint32_t samples, uint32_t chmap) +static int pcm_convert_s32_to_f(const struct cir_buf_source *source, + uint32_t src_channels, struct cir_buf_sink *sink, + uint32_t sink_channels, size_t samples, uint32_t chmap) { - return pcm_convert_as_linear(source, ioffset, sink, ooffset, samples, + return pcm_convert_as_linear(source, sizeof(int32_t), sink, sizeof(int32_t), samples, pcm_convert_s32_to_f_lin); } -static int pcm_convert_f_to_s32(const struct audio_stream *source, - uint32_t ioffset, struct audio_stream *sink, - uint32_t ooffset, uint32_t samples, uint32_t chmap) +static int pcm_convert_f_to_s32(const struct cir_buf_source *source, + uint32_t src_channels, struct cir_buf_sink *sink, + uint32_t sink_channels, size_t samples, uint32_t chmap) { - return pcm_convert_as_linear(source, ioffset, sink, ooffset, samples, + return pcm_convert_as_linear(source, sizeof(int32_t), sink, sizeof(int32_t), samples, pcm_convert_f_to_s32_lin); } #endif /* CONFIG_PCM_CONVERTER_FORMAT_FLOAT && CONFIG_PCM_CONVERTER_FORMAT_S32LE */ const struct pcm_func_map pcm_func_map[] = { #if CONFIG_PCM_CONVERTER_FORMAT_U8 - { SOF_IPC_FRAME_U8, SOF_IPC_FRAME_U8, just_copy }, + { SOF_IPC_FRAME_U8, SOF_IPC_FRAME_U8, just_copy_1b }, #endif /* CONFIG_PCM_CONVERTER_FORMAT_U8 */ #if CONFIG_PCM_CONVERTER_FORMAT_U8 && CONFIG_PCM_CONVERTER_FORMAT_S32LE { SOF_IPC_FRAME_U8, SOF_IPC_FRAME_S32_LE, pcm_convert_u8_to_s32 }, { SOF_IPC_FRAME_S32_LE, SOF_IPC_FRAME_U8, pcm_convert_s32_to_u8 }, #endif /* CONFIG_PCM_CONVERTER_FORMAT_U8 && CONFIG_PCM_CONVERTER_FORMAT_S32LE */ #if CONFIG_PCM_CONVERTER_FORMAT_A_LAW - { SOF_IPC_FRAME_A_LAW, SOF_IPC_FRAME_A_LAW, just_copy }, + { SOF_IPC_FRAME_A_LAW, SOF_IPC_FRAME_A_LAW, just_copy_1b }, #endif /* CONFIG_PCM_CONVERTER_FORMAT_A_LAW */ #if CONFIG_PCM_CONVERTER_FORMAT_A_LAW && CONFIG_PCM_CONVERTER_FORMAT_S32LE { SOF_IPC_FRAME_A_LAW, SOF_IPC_FRAME_S32_LE, pcm_convert_alaw_to_s32 }, { SOF_IPC_FRAME_S32_LE, SOF_IPC_FRAME_A_LAW, pcm_convert_s32_to_alaw }, #endif /* CONFIG_PCM_CONVERTER_FORMAT_A_LAW && CONFIG_PCM_CONVERTER_FORMAT_S32LE */ #if CONFIG_PCM_CONVERTER_FORMAT_MU_LAW - { SOF_IPC_FRAME_MU_LAW, SOF_IPC_FRAME_MU_LAW, just_copy }, + { SOF_IPC_FRAME_MU_LAW, SOF_IPC_FRAME_MU_LAW, just_copy_1b }, #endif /* CONFIG_PCM_CONVERTER_FORMAT_MU_LAW */ #if CONFIG_PCM_CONVERTER_FORMAT_MU_LAW && CONFIG_PCM_CONVERTER_FORMAT_S32LE { SOF_IPC_FRAME_MU_LAW, SOF_IPC_FRAME_S32_LE, pcm_convert_mulaw_to_s32 }, { SOF_IPC_FRAME_S32_LE, SOF_IPC_FRAME_MU_LAW, pcm_convert_s32_to_mulaw }, #endif /* CONFIG_PCM_CONVERTER_FORMAT_A_LAW && CONFIG_PCM_CONVERTER_FORMAT_S32LE */ #if CONFIG_PCM_CONVERTER_FORMAT_S16LE - { SOF_IPC_FRAME_S16_LE, SOF_IPC_FRAME_S16_LE, just_copy }, + { SOF_IPC_FRAME_S16_LE, SOF_IPC_FRAME_S16_LE, just_copy_2b }, #endif /* CONFIG_PCM_CONVERTER_FORMAT_S16LE */ #if CONFIG_PCM_CONVERTER_FORMAT_S24LE - { SOF_IPC_FRAME_S24_4LE, SOF_IPC_FRAME_S24_4LE, just_copy }, + { SOF_IPC_FRAME_S24_4LE, SOF_IPC_FRAME_S24_4LE, just_copy_4b }, #endif /* CONFIG_PCM_CONVERTER_FORMAT_S24LE */ #if CONFIG_PCM_CONVERTER_FORMAT_S24_3LE - { SOF_IPC_FRAME_S24_3LE, SOF_IPC_FRAME_S24_3LE, just_copy }, + { SOF_IPC_FRAME_S24_3LE, SOF_IPC_FRAME_S24_3LE, just_copy_3b }, #endif /* CONFIG_PCM_CONVERTER_FORMAT_S24_3LE */ #if CONFIG_PCM_CONVERTER_FORMAT_S24LE && CONFIG_PCM_CONVERTER_FORMAT_S16LE { SOF_IPC_FRAME_S16_LE, SOF_IPC_FRAME_S24_4LE, pcm_convert_s16_to_s24 }, { SOF_IPC_FRAME_S24_4LE, SOF_IPC_FRAME_S16_LE, pcm_convert_s24_to_s16 }, #endif /* CONFIG_PCM_CONVERTER_FORMAT_S24LE && CONFIG_PCM_CONVERTER_FORMAT_S16LE */ #if CONFIG_PCM_CONVERTER_FORMAT_S32LE - { SOF_IPC_FRAME_S32_LE, SOF_IPC_FRAME_S32_LE, just_copy }, + { SOF_IPC_FRAME_S32_LE, SOF_IPC_FRAME_S32_LE, just_copy_4b }, #endif /* CONFIG_PCM_CONVERTER_FORMAT_S32LE */ #if CONFIG_PCM_CONVERTER_FORMAT_S32LE && CONFIG_PCM_CONVERTER_FORMAT_S16LE { SOF_IPC_FRAME_S16_LE, SOF_IPC_FRAME_S32_LE, pcm_convert_s16_to_s32 }, @@ -715,8 +689,12 @@ const struct pcm_func_map pcm_func_map[] = { { SOF_IPC_FRAME_S24_4LE, SOF_IPC_FRAME_S32_LE, pcm_convert_s24_to_s32 }, { SOF_IPC_FRAME_S32_LE, SOF_IPC_FRAME_S24_4LE, pcm_convert_s32_to_s24 }, #endif /* CONFIG_PCM_CONVERTER_FORMAT_S32LE && CONFIG_PCM_CONVERTER_FORMAT_S24LE */ +#if CONFIG_PCM_CONVERTER_FORMAT_S32LE && CONFIG_PCM_CONVERTER_FORMAT_S24_4LE_MSB + { SOF_IPC_FRAME_S32_LE, SOF_IPC_FRAME_S24_4LE_MSB, pcm_convert_s32_to_s24_be }, + { SOF_IPC_FRAME_S24_4LE_MSB, SOF_IPC_FRAME_S32_LE, just_copy_4b }, +#endif /* CONFIG_PCM_CONVERTER_FORMAT_S32LE && CONFIG_PCM_CONVERTER_FORMAT_S24_4LE_MSB */ #if CONFIG_PCM_CONVERTER_FORMAT_FLOAT - { SOF_IPC_FRAME_FLOAT, SOF_IPC_FRAME_FLOAT, just_copy }, + { SOF_IPC_FRAME_FLOAT, SOF_IPC_FRAME_FLOAT, just_copy_4b }, #endif /* CONFIG_PCM_CONVERTER_FORMAT_FLOAT */ #if CONFIG_PCM_CONVERTER_FORMAT_FLOAT && CONFIG_PCM_CONVERTER_FORMAT_S16LE { SOF_IPC_FRAME_S16_LE, SOF_IPC_FRAME_FLOAT, pcm_convert_s16_to_f }, @@ -735,24 +713,22 @@ const struct pcm_func_map pcm_func_map[] = { const size_t pcm_func_count = ARRAY_SIZE(pcm_func_map); #if CONFIG_PCM_CONVERTER_FORMAT_S16_C16_AND_S16_C32 -static int pcm_convert_s16_c16_to_s16_c32(const struct audio_stream *source, - uint32_t ioffset, struct audio_stream *sink, - uint32_t ooffset, uint32_t samples, uint32_t chmap) +static int pcm_convert_s16_c16_to_s16_c32(const struct cir_buf_source *source, + uint32_t src_channels, struct cir_buf_sink *sink, + uint32_t sink_channels, size_t samples, uint32_t chmap) { - int16_t *src = audio_stream_get_rptr(source); - int32_t *dst = audio_stream_get_wptr(sink); - int processed; - int nmax, i, n; + const int16_t *src = source->ptr; + int32_t *dst = sink->ptr; + size_t processed; + size_t nmax, i, n; - src += ioffset; - dst += ooffset; for (processed = 0; processed < samples; processed += n) { - src = audio_stream_wrap(source, src); - dst = audio_stream_wrap(sink, dst); + src = cir_buf_wrap(src, source->buf_start, source->buf_end); + dst = cir_buf_wrap(dst, sink->buf_start, sink->buf_end); n = samples - processed; - nmax = audio_stream_bytes_without_wrap(source, src) >> BYTES_TO_S16_SAMPLES; + nmax = cir_buf_bytes_without_wrap(src, source->buf_end) >> BYTES_TO_S16_SAMPLES; n = MIN(n, nmax); - nmax = audio_stream_bytes_without_wrap(sink, dst) >> BYTES_TO_S32_SAMPLES; + nmax = cir_buf_bytes_without_wrap(dst, sink->buf_end) >> BYTES_TO_S32_SAMPLES; n = MIN(n, nmax); for (i = 0; i < n; i++) { *dst = *src; @@ -764,24 +740,22 @@ static int pcm_convert_s16_c16_to_s16_c32(const struct audio_stream *source, return samples; } -static int pcm_convert_s16_c32_to_s16_c16(const struct audio_stream *source, - uint32_t ioffset, struct audio_stream *sink, - uint32_t ooffset, uint32_t samples, uint32_t chmap) +static int pcm_convert_s16_c32_to_s16_c16(const struct cir_buf_source *source, + uint32_t src_channels, struct cir_buf_sink *sink, + uint32_t sink_channels, size_t samples, uint32_t chmap) { - int32_t *src = audio_stream_get_rptr(source); - int16_t *dst = audio_stream_get_wptr(sink); - int processed; - int nmax, i, n; + const int32_t *src = source->ptr; + int16_t *dst = sink->ptr; + size_t processed; + size_t nmax, i, n; - src += ioffset; - dst += ooffset; for (processed = 0; processed < samples; processed += n) { - src = audio_stream_wrap(source, src); - dst = audio_stream_wrap(sink, dst); + src = cir_buf_wrap(src, source->buf_start, source->buf_end); + dst = cir_buf_wrap(dst, sink->buf_start, sink->buf_end); n = samples - processed; - nmax = audio_stream_bytes_without_wrap(source, src) >> BYTES_TO_S32_SAMPLES; + nmax = cir_buf_bytes_without_wrap(src, source->buf_end) >> BYTES_TO_S32_SAMPLES; n = MIN(n, nmax); - nmax = audio_stream_bytes_without_wrap(sink, dst) >> BYTES_TO_S16_SAMPLES; + nmax = cir_buf_bytes_without_wrap(dst, sink->buf_end) >> BYTES_TO_S16_SAMPLES; n = MIN(n, nmax); for (i = 0; i < n; i++) { *dst = *src & 0xffff; @@ -794,24 +768,22 @@ static int pcm_convert_s16_c32_to_s16_c16(const struct audio_stream *source, } #endif #if CONFIG_PCM_CONVERTER_FORMAT_S16_C32_AND_S32_C32 -static int pcm_convert_s16_c32_to_s32_c32(const struct audio_stream *source, - uint32_t ioffset, struct audio_stream *sink, - uint32_t ooffset, uint32_t samples, uint32_t chmap) +static int pcm_convert_s16_c32_to_s32_c32(const struct cir_buf_source *source, + uint32_t src_channels, struct cir_buf_sink *sink, + uint32_t sink_channels, size_t samples, uint32_t chmap) { - int32_t *src = audio_stream_get_rptr(source); - int32_t *dst = audio_stream_get_wptr(sink); - int processed; - int nmax, i, n; + const int32_t *src = source->ptr; + int32_t *dst = sink->ptr; + size_t processed; + size_t nmax, i, n; - src += ioffset; - dst += ooffset; for (processed = 0; processed < samples; processed += n) { - src = audio_stream_wrap(source, src); - dst = audio_stream_wrap(sink, dst); + src = cir_buf_wrap(src, source->buf_start, source->buf_end); + dst = cir_buf_wrap(dst, sink->buf_start, sink->buf_end); n = samples - processed; - nmax = audio_stream_bytes_without_wrap(source, src) >> BYTES_TO_S32_SAMPLES; + nmax = cir_buf_bytes_without_wrap(src, source->buf_end) >> BYTES_TO_S32_SAMPLES; n = MIN(n, nmax); - nmax = audio_stream_bytes_without_wrap(sink, dst) >> BYTES_TO_S32_SAMPLES; + nmax = cir_buf_bytes_without_wrap(dst, sink->buf_end) >> BYTES_TO_S32_SAMPLES; n = MIN(n, nmax); for (i = 0; i < n; i++) { *dst = *src << 16; @@ -823,24 +795,22 @@ static int pcm_convert_s16_c32_to_s32_c32(const struct audio_stream *source, return samples; } -static int pcm_convert_s32_c32_to_s16_c32(const struct audio_stream *source, - uint32_t ioffset, struct audio_stream *sink, - uint32_t ooffset, uint32_t samples, uint32_t chmap) +static int pcm_convert_s32_c32_to_s16_c32(const struct cir_buf_source *source, + uint32_t src_channels, struct cir_buf_sink *sink, + uint32_t sink_channels, size_t samples, uint32_t chmap) { - int32_t *src = audio_stream_get_rptr(source); - int32_t *dst = audio_stream_get_wptr(sink); - int processed; - int nmax, i, n; + const int32_t *src = source->ptr; + int32_t *dst = sink->ptr; + size_t processed; + size_t nmax, i, n; - src += ioffset; - dst += ooffset; for (processed = 0; processed < samples; processed += n) { - src = audio_stream_wrap(source, src); - dst = audio_stream_wrap(sink, dst); + src = cir_buf_wrap(src, source->buf_start, source->buf_end); + dst = cir_buf_wrap(dst, sink->buf_start, sink->buf_end); n = samples - processed; - nmax = audio_stream_bytes_without_wrap(source, src) >> BYTES_TO_S32_SAMPLES; + nmax = cir_buf_bytes_without_wrap(src, source->buf_end) >> BYTES_TO_S32_SAMPLES; n = MIN(n, nmax); - nmax = audio_stream_bytes_without_wrap(sink, dst) >> BYTES_TO_S32_SAMPLES; + nmax = cir_buf_bytes_without_wrap(dst, sink->buf_end) >> BYTES_TO_S32_SAMPLES; n = MIN(n, nmax); for (i = 0; i < n; i++) { *dst = sat_int16(Q_SHIFT_RND(*src, 31, 15)); @@ -853,24 +823,22 @@ static int pcm_convert_s32_c32_to_s16_c32(const struct audio_stream *source, } #endif #if CONFIG_PCM_CONVERTER_FORMAT_S16_C32_AND_S24_C32 -static int pcm_convert_s16_c32_to_s24_c32(const struct audio_stream *source, - uint32_t ioffset, struct audio_stream *sink, - uint32_t ooffset, uint32_t samples, uint32_t chmap) +static int pcm_convert_s16_c32_to_s24_c32(const struct cir_buf_source *source, + uint32_t src_channels, struct cir_buf_sink *sink, + uint32_t sink_channels, size_t samples, uint32_t chmap) { - int32_t *src = audio_stream_get_rptr(source); - int32_t *dst = audio_stream_get_wptr(sink); - int processed; - int nmax, i, n; + const int32_t *src = source->ptr; + int32_t *dst = sink->ptr; + size_t processed; + size_t nmax, i, n; - src += ioffset; - dst += ooffset; for (processed = 0; processed < samples; processed += n) { - src = audio_stream_wrap(source, src); - dst = audio_stream_wrap(sink, dst); + src = cir_buf_wrap(src, source->buf_start, source->buf_end); + dst = cir_buf_wrap(dst, sink->buf_start, sink->buf_end); n = samples - processed; - nmax = audio_stream_bytes_without_wrap(source, src) >> BYTES_TO_S32_SAMPLES; + nmax = cir_buf_bytes_without_wrap(src, source->buf_end) >> BYTES_TO_S32_SAMPLES; n = MIN(n, nmax); - nmax = audio_stream_bytes_without_wrap(sink, dst) >> BYTES_TO_S32_SAMPLES; + nmax = cir_buf_bytes_without_wrap(dst, sink->buf_end) >> BYTES_TO_S32_SAMPLES; n = MIN(n, nmax); for (i = 0; i < n; i++) { *dst = *src << 8; @@ -882,24 +850,22 @@ static int pcm_convert_s16_c32_to_s24_c32(const struct audio_stream *source, return samples; } -static int pcm_convert_s24_c32_to_s16_c32(const struct audio_stream *source, - uint32_t ioffset, struct audio_stream *sink, - uint32_t ooffset, uint32_t samples, uint32_t chmap) +static int pcm_convert_s24_c32_to_s16_c32(const struct cir_buf_source *source, + uint32_t src_channels, struct cir_buf_sink *sink, + uint32_t sink_channels, size_t samples, uint32_t chmap) { - int32_t *src = audio_stream_get_rptr(source); - int32_t *dst = audio_stream_get_wptr(sink); - int processed; - int nmax, i, n; + const int32_t *src = source->ptr; + int32_t *dst = sink->ptr; + size_t processed; + size_t nmax, i, n; - src += ioffset; - dst += ooffset; for (processed = 0; processed < samples; processed += n) { - src = audio_stream_wrap(source, src); - dst = audio_stream_wrap(sink, dst); + src = cir_buf_wrap(src, source->buf_start, source->buf_end); + dst = cir_buf_wrap(dst, sink->buf_start, sink->buf_end); n = samples - processed; - nmax = audio_stream_bytes_without_wrap(source, src) >> BYTES_TO_S32_SAMPLES; + nmax = cir_buf_bytes_without_wrap(src, source->buf_end) >> BYTES_TO_S32_SAMPLES; n = MIN(n, nmax); - nmax = audio_stream_bytes_without_wrap(sink, dst) >> BYTES_TO_S32_SAMPLES; + nmax = cir_buf_bytes_without_wrap(dst, sink->buf_end) >> BYTES_TO_S32_SAMPLES; n = MIN(n, nmax); for (i = 0; i < n; i++) { *dst = sat_int16(Q_SHIFT_RND(sign_extend_s24(*src & 0xffffff), 23, 15)); @@ -913,24 +879,22 @@ static int pcm_convert_s24_c32_to_s16_c32(const struct audio_stream *source, #endif #if CONFIG_PCM_CONVERTER_FORMAT_S24_C24_AND_S24_C32 -static int pcm_convert_s24_c24_to_s24_c32(const struct audio_stream *source, - uint32_t ioffset, struct audio_stream *sink, - uint32_t ooffset, uint32_t samples, uint32_t chmap) +static int pcm_convert_s24_c24_to_s24_c32(const struct cir_buf_source *source, + uint32_t src_channels, struct cir_buf_sink *sink, + uint32_t sink_channels, size_t samples, uint32_t chmap) { - uint8_t *src = audio_stream_get_rptr(source); - int32_t *dst = audio_stream_get_wptr(sink); - int processed; - int nmax, i, n; + const uint8_t *src = source->ptr; + int32_t *dst = sink->ptr; + size_t processed; + size_t nmax, i, n; - src += ioffset * 3; - dst += ooffset; for (processed = 0; processed < samples; processed += n) { - src = audio_stream_wrap(source, src); - dst = audio_stream_wrap(sink, dst); + src = cir_buf_wrap(src, source->buf_start, source->buf_end); + dst = cir_buf_wrap(dst, sink->buf_start, sink->buf_end); n = samples - processed; - nmax = audio_stream_bytes_without_wrap(source, src) / 3; + nmax = cir_buf_bytes_without_wrap(src, source->buf_end) / 3; n = MIN(n, nmax); - nmax = audio_stream_bytes_without_wrap(sink, dst) >> BYTES_TO_S32_SAMPLES; + nmax = cir_buf_bytes_without_wrap(dst, sink->buf_end) >> BYTES_TO_S32_SAMPLES; n = MIN(n, nmax); for (i = 0; i < n; i += 1) { *dst = (*(src + 2) << 24) | (*(src + 1) << 16) | (*(src + 0) << 8); @@ -943,24 +907,22 @@ static int pcm_convert_s24_c24_to_s24_c32(const struct audio_stream *source, return samples; } -static int pcm_convert_s24_c32_to_s24_c24(const struct audio_stream *source, - uint32_t ioffset, struct audio_stream *sink, - uint32_t ooffset, uint32_t samples, uint32_t chmap) +static int pcm_convert_s24_c32_to_s24_c24(const struct cir_buf_source *source, + uint32_t src_channels, struct cir_buf_sink *sink, + uint32_t sink_channels, size_t samples, uint32_t chmap) { - int32_t *src = audio_stream_get_rptr(source); - uint8_t *dst = audio_stream_get_wptr(sink); - int processed; - int nmax, i, n; + const int32_t *src = source->ptr; + uint8_t *dst = sink->ptr; + size_t processed; + size_t nmax, i, n; - src += ioffset; - dst += ooffset * 3; for (processed = 0; processed < samples; processed += n) { - src = audio_stream_wrap(source, src); - dst = audio_stream_wrap(sink, dst); + src = cir_buf_wrap(src, source->buf_start, source->buf_end); + dst = cir_buf_wrap(dst, sink->buf_start, sink->buf_end); n = samples - processed; - nmax = audio_stream_bytes_without_wrap(source, src) >> BYTES_TO_S32_SAMPLES; + nmax = cir_buf_bytes_without_wrap(src, source->buf_end) >> BYTES_TO_S32_SAMPLES; n = MIN(n, nmax); - nmax = audio_stream_bytes_without_wrap(sink, dst) / 3; + nmax = cir_buf_bytes_without_wrap(dst, sink->buf_end) / 3; n = MIN(n, nmax); for (i = 0; i < n; i += 1) { *dst = *src & 0xFF; @@ -977,25 +939,23 @@ static int pcm_convert_s24_c32_to_s24_c24(const struct audio_stream *source, } /* 2x24bit samples are packed into 3x16bit samples for hda link dma */ -static int pcm_convert_s24_c32_to_s24_c24_link_gtw(const struct audio_stream *source, - uint32_t ioffset, struct audio_stream *sink, - uint32_t ooffset, uint32_t samples, +static int pcm_convert_s24_c32_to_s24_c24_link_gtw(const struct cir_buf_source *source, + uint32_t src_channels, struct cir_buf_sink *sink, + uint32_t sink_channels, size_t samples, uint32_t chmap) { - int32_t *src = audio_stream_get_rptr(source); - uint16_t *dst = audio_stream_get_wptr(sink); - int processed; - int nmax, i = 0, n = 0; + const int32_t *src = source->ptr; + uint16_t *dst = sink->ptr; + size_t processed; + size_t nmax, i = 0, n = 0; - src += ioffset; - assert(ooffset == 0); for (processed = 0; processed < samples; processed += n) { - src = audio_stream_wrap(source, src); - dst = audio_stream_wrap(sink, dst); + src = cir_buf_wrap(src, source->buf_start, source->buf_end); + dst = cir_buf_wrap(dst, sink->buf_start, sink->buf_end); n = samples - processed; - nmax = audio_stream_bytes_without_wrap(source, src) >> BYTES_TO_S32_SAMPLES; + nmax = cir_buf_bytes_without_wrap(src, source->buf_end) >> BYTES_TO_S32_SAMPLES; n = MIN(n, nmax); - nmax = audio_stream_bytes_without_wrap(sink, dst) / 3; + nmax = cir_buf_bytes_without_wrap(dst, sink->buf_end) / 3; n = MIN(n, nmax); for (i = 0; i < n; i += 2) { *dst = (*src >> 8) & 0xFFFF; @@ -1041,11 +1001,11 @@ const struct pcm_func_vc_map pcm_func_vc_map[] = { #endif #if CONFIG_PCM_CONVERTER_FORMAT_U8 && CONFIG_PCM_CONVERTER_FORMAT_S16LE { SOF_IPC_FRAME_S16_LE, SOF_IPC_FRAME_U8, SOF_IPC_FRAME_S16_LE, SOF_IPC_FRAME_U8, - just_copy }, + just_copy_2b }, #endif /* CONFIG_PCM_CONVERTER_FORMAT_U8 && CONFIG_PCM_CONVERTER_FORMAT_S16LE */ #if CONFIG_PCM_CONVERTER_FORMAT_S32LE && CONFIG_PCM_CONVERTER_FORMAT_S24LE { SOF_IPC_FRAME_S32_LE, SOF_IPC_FRAME_S24_4LE, SOF_IPC_FRAME_S32_LE, SOF_IPC_FRAME_S24_4LE, - just_copy}, + just_copy_4b}, { SOF_IPC_FRAME_S32_LE, SOF_IPC_FRAME_S24_4LE, SOF_IPC_FRAME_S32_LE, SOF_IPC_FRAME_S32_LE, pcm_convert_s24_to_s32}, { SOF_IPC_FRAME_S32_LE, SOF_IPC_FRAME_S32_LE, SOF_IPC_FRAME_S32_LE, SOF_IPC_FRAME_S24_4LE, @@ -1067,7 +1027,7 @@ const struct pcm_func_vc_map pcm_func_vc_map[] = { #endif #if CONFIG_PCM_CONVERTER_FORMAT_S16_C32_AND_S16_C32 { SOF_IPC_FRAME_S32_LE, SOF_IPC_FRAME_S16_LE, SOF_IPC_FRAME_S32_LE, SOF_IPC_FRAME_S16_LE, - just_copy }, + just_copy_4b }, #endif #if CONFIG_PCM_CONVERTER_FORMAT_S24_4LE_MSB && CONFIG_PCM_CONVERTER_FORMAT_S24LE @@ -1076,14 +1036,14 @@ const struct pcm_func_vc_map pcm_func_vc_map[] = { { SOF_IPC_FRAME_S32_LE, SOF_IPC_FRAME_S24_4LE_MSB, SOF_IPC_FRAME_S32_LE, SOF_IPC_FRAME_S24_4LE, pcm_convert_s32_to_s24}, { SOF_IPC_FRAME_S32_LE, SOF_IPC_FRAME_S24_4LE_MSB, SOF_IPC_FRAME_S32_LE, - SOF_IPC_FRAME_S24_4LE_MSB, just_copy}, + SOF_IPC_FRAME_S24_4LE_MSB, just_copy_4b}, #endif #if CONFIG_PCM_CONVERTER_FORMAT_S32LE && CONFIG_PCM_CONVERTER_FORMAT_S24_4LE_MSB { SOF_IPC_FRAME_S32_LE, SOF_IPC_FRAME_S32_LE, SOF_IPC_FRAME_S32_LE, SOF_IPC_FRAME_S24_4LE_MSB, pcm_convert_s32_to_s24_be}, { SOF_IPC_FRAME_S32_LE, SOF_IPC_FRAME_S24_4LE_MSB, SOF_IPC_FRAME_S32_LE, - SOF_IPC_FRAME_S32_LE, just_copy}, + SOF_IPC_FRAME_S32_LE, just_copy_4b}, #endif #if CONFIG_PCM_CONVERTER_FORMAT_S24_4LE_MSB && CONFIG_PCM_CONVERTER_FORMAT_S16LE diff --git a/src/audio/pcm_converter/pcm_converter_hifi3.c b/src/audio/pcm_converter/pcm_converter_hifi3.c index 2b6ca607415d..a25a0881f20c 100644 --- a/src/audio/pcm_converter/pcm_converter_hifi3.c +++ b/src/audio/pcm_converter/pcm_converter_hifi3.c @@ -36,24 +36,21 @@ * \param[in,out] sink Destination buffer. * \param[in] samples Number of samples to process. */ -static int pcm_convert_s16_to_s24(const struct audio_stream *source, - uint32_t ioffset, struct audio_stream *sink, - uint32_t ooffset, uint32_t samples, uint32_t chmap) +static int pcm_convert_s16_to_s24(const struct cir_buf_source *source, + uint32_t src_channels, struct cir_buf_sink *sink, + uint32_t sink_channels, size_t samples, uint32_t chmap) { ae_int16x4 sample = AE_ZERO16(); - uint32_t nmax, i, n, m, left, left_samples; + size_t nmax, i, n, m, left, left_samples; ae_valign inu = AE_ZALIGN64(); ae_valign outu = AE_ZALIGN64(); - int16_t *src = audio_stream_get_rptr(source); - int32_t *dst = audio_stream_get_wptr(sink); - - ae_int16x4 *in = audio_stream_wrap(source, src + ioffset); - ae_int32x2 *out = audio_stream_wrap(sink, dst + ooffset); + const ae_int16x4 *in = source->ptr; + ae_int32x2 *out = sink->ptr; for (left_samples = samples; left_samples; left_samples -= n) { - nmax = audio_stream_samples_without_wrap_s16(source, in); + nmax = cir_buf_samples_without_wrap_s16(in, source->buf_end); n = MIN(left_samples, nmax); - nmax = audio_stream_samples_without_wrap_s32(sink, out); + nmax = cir_buf_samples_without_wrap_s32(out, sink->buf_end); n = MIN(n, nmax); m = n >> 2; left = n & 0x03; @@ -73,15 +70,15 @@ static int pcm_convert_s16_to_s24(const struct audio_stream *source, */ for (i = 0; i < left ; i++) { /* load one 16 bit samples */ - AE_L16_IP(sample, (ae_int16 *)in, sizeof(ae_int16)); + AE_L16_IP(sample, (const ae_int16 *)in, sizeof(ae_int16)); /* shift right and store one 32 bit sample */ AE_S32_L_IP(AE_SRAI32(AE_CVT32X2F16_32(sample), 8), (ae_int32 *)out, sizeof(ae_int32)); } - in = audio_stream_wrap(source, in); - out = audio_stream_wrap(sink, out); + in = cir_buf_wrap(in, source->buf_start, source->buf_end); + out = cir_buf_wrap(out, sink->buf_start, sink->buf_end); } return samples; @@ -110,26 +107,23 @@ static ae_int32x2 pcm_shift_s24_to_s16(ae_int32x2 sample) * \param[in] samples Number of samples to process. * \return error code or number of processed samples. */ -static int pcm_convert_s24_to_s16(const struct audio_stream *source, - uint32_t ioffset, struct audio_stream *sink, - uint32_t ooffset, uint32_t samples, uint32_t chmap) +static int pcm_convert_s24_to_s16(const struct cir_buf_source *source, + uint32_t src_channels, struct cir_buf_sink *sink, + uint32_t sink_channels, size_t samples, uint32_t chmap) { ae_int16x4 sample = AE_ZERO16(); ae_int32x2 sample_1 = AE_ZERO32(); ae_int32x2 sample_2 = AE_ZERO32(); - uint32_t nmax, i, n, m, left, left_samples; + size_t nmax, i, n, m, left, left_samples; ae_valign inu = AE_ZALIGN64(); ae_valign outu = AE_ZALIGN64(); - ae_int32 *src = audio_stream_get_rptr(source); - ae_int16 *dst = audio_stream_get_wptr(sink); - - ae_int32x2 *in = audio_stream_wrap(source, src + ioffset); - ae_int16x4 *out = audio_stream_wrap(sink, dst + ooffset); + const ae_int32x2 *in = source->ptr; + ae_int16x4 *out = sink->ptr; for (left_samples = samples; left_samples; left_samples -= n) { - nmax = audio_stream_samples_without_wrap_s32(source, in); + nmax = cir_buf_samples_without_wrap_s32(in, source->buf_end); n = MIN(left_samples, nmax); - nmax = audio_stream_samples_without_wrap_s16(sink, out); + nmax = cir_buf_samples_without_wrap_s16(out, sink->buf_end); n = MIN(n, nmax); m = n >> 2; left = n & 0x03; @@ -153,7 +147,7 @@ static int pcm_convert_s24_to_s16(const struct audio_stream *source, */ for (i = 0; i < left ; i++) { /* load one 32 bit sample */ - AE_L32_IP(sample_1, (ae_int32 *)in, sizeof(ae_int32)); + AE_L32_IP(sample_1, (const ae_int32 *)in, sizeof(ae_int32)); /* shift and round */ sample_1 = pcm_shift_s24_to_s16(sample_1); @@ -164,8 +158,8 @@ static int pcm_convert_s24_to_s16(const struct audio_stream *source, sizeof(ae_int16)); } - in = audio_stream_wrap(source, in); - out = audio_stream_wrap(sink, out); + in = cir_buf_wrap(in, source->buf_start, source->buf_end); + out = cir_buf_wrap(out, sink->buf_start, sink->buf_end); } return samples; } @@ -181,24 +175,21 @@ static int pcm_convert_s24_to_s16(const struct audio_stream *source, * \param[in] samples Number of samples to process. * \return error code or number of processed samples. */ -static int pcm_convert_s16_to_s32(const struct audio_stream *source, - uint32_t ioffset, struct audio_stream *sink, - uint32_t ooffset, uint32_t samples, uint32_t chmap) +static int pcm_convert_s16_to_s32(const struct cir_buf_source *source, + uint32_t src_channels, struct cir_buf_sink *sink, + uint32_t sink_channels, size_t samples, uint32_t chmap) { - int16_t *src = audio_stream_get_rptr(source); - int32_t *dst = audio_stream_get_wptr(sink); ae_int16x4 sample = AE_ZERO16(); - uint32_t nmax, i, n, m, left, left_samples; + size_t nmax, i, n, m, left, left_samples; ae_valign inu = AE_ZALIGN64(); ae_valign outu = AE_ZALIGN64(); - - ae_int16x4 *in = audio_stream_wrap(source, src + ioffset); - ae_int32x2 *out = audio_stream_wrap(sink, dst + ooffset); + const ae_int16x4 *in = source->ptr; + ae_int32x2 *out = sink->ptr; for (left_samples = samples; left_samples; left_samples -= n) { - nmax = audio_stream_samples_without_wrap_s16(source, in); + nmax = cir_buf_samples_without_wrap_s16(in, source->buf_end); n = MIN(left_samples, nmax); - nmax = audio_stream_samples_without_wrap_s32(sink, out); + nmax = cir_buf_samples_without_wrap_s32(out, sink->buf_end); n = MIN(n, nmax); m = n >> 2; left = n & 0x03; @@ -218,14 +209,14 @@ static int pcm_convert_s16_to_s32(const struct audio_stream *source, */ for (i = 0; i < left ; i++) { /* load one 16 bit samples */ - AE_L16_IP(sample, (ae_int16 *)in, sizeof(ae_int16)); + AE_L16_IP(sample, (const ae_int16 *)in, sizeof(ae_int16)); /* store one 32 bit sample */ AE_S32_L_IP(AE_CVT32X2F16_32(sample), (ae_int32 *)out, sizeof(ae_int32)); } - in = audio_stream_wrap(source, in); - out = audio_stream_wrap(sink, out); + in = cir_buf_wrap(in, source->buf_start, source->buf_end); + out = cir_buf_wrap(out, sink->buf_start, sink->buf_end); } return samples; } @@ -237,26 +228,23 @@ static int pcm_convert_s16_to_s32(const struct audio_stream *source, * \param[in] samples Number of samples to process. * \return error code or number of processed samples. */ -static int pcm_convert_s32_to_s16(const struct audio_stream *source, - uint32_t ioffset, struct audio_stream *sink, - uint32_t ooffset, uint32_t samples, uint32_t chmap) +static int pcm_convert_s32_to_s16(const struct cir_buf_source *source, + uint32_t src_channels, struct cir_buf_sink *sink, + uint32_t sink_channels, size_t samples, uint32_t chmap) { - int32_t *src = audio_stream_get_rptr(source); - int16_t *dst = audio_stream_get_wptr(sink); ae_int16x4 sample = AE_ZERO16(); ae_int32x2 sample_1 = AE_ZERO32(); ae_int32x2 sample_2 = AE_ZERO32(); - uint32_t nmax, i, n, m, left, left_samples; + size_t nmax, i, n, m, left, left_samples; ae_valign outu = AE_ZALIGN64(); ae_valign inu = AE_ZALIGN64(); - - ae_int32x2 *in = audio_stream_wrap(source, src + ioffset); - ae_int16x4 *out = audio_stream_wrap(sink, dst + ooffset); + const ae_int32x2 *in = source->ptr; + ae_int16x4 *out = sink->ptr; for (left_samples = samples; left_samples; left_samples -= n) { - nmax = audio_stream_samples_without_wrap_s32(source, in); + nmax = cir_buf_samples_without_wrap_s32(in, source->buf_end); n = MIN(left_samples, nmax); - nmax = audio_stream_samples_without_wrap_s16(sink, out); + nmax = cir_buf_samples_without_wrap_s16(out, sink->buf_end); n = MIN(n, nmax); m = n >> 2; left = n & 0x3; @@ -277,7 +265,7 @@ static int pcm_convert_s32_to_s16(const struct audio_stream *source, */ for (i = 0; i < left ; i++) { /* load one 32 bit samples */ - AE_L32_IP(sample_1, (ae_int32 *)in, sizeof(ae_int32)); + AE_L32_IP(sample_1, (const ae_int32 *)in, sizeof(ae_int32)); /* shift and round */ sample = AE_ROUND16X4F32SSYM(sample_1, sample_1); /* store one 16 bit sample */ @@ -285,8 +273,8 @@ static int pcm_convert_s32_to_s16(const struct audio_stream *source, sizeof(ae_int16)); } - in = audio_stream_wrap(source, in); - out = audio_stream_wrap(sink, out); + in = cir_buf_wrap(in, source->buf_start, source->buf_end); + out = cir_buf_wrap(out, sink->buf_start, sink->buf_end); } return samples; @@ -303,24 +291,21 @@ static int pcm_convert_s32_to_s16(const struct audio_stream *source, * \param[in] samples Number of samples to process. * \return error code or number of processed samples. */ -static int pcm_convert_s24_to_s32(const struct audio_stream *source, - uint32_t ioffset, struct audio_stream *sink, - uint32_t ooffset, uint32_t samples, uint32_t chmap) +static int pcm_convert_s24_to_s32(const struct cir_buf_source *source, + uint32_t src_channels, struct cir_buf_sink *sink, + uint32_t sink_channels, size_t samples, uint32_t chmap) { - int32_t *src = audio_stream_get_rptr(source); - int32_t *dst = audio_stream_get_wptr(sink); ae_int32x2 sample = AE_ZERO32(); - uint32_t nmax, i, n, m, left_samples; + size_t nmax, i, n, m, left_samples; ae_valign outu = AE_ZALIGN64(); ae_valign inu = AE_ZALIGN64(); - - ae_int32x2 *in = audio_stream_wrap(source, src + ioffset); - ae_int32x2 *out = audio_stream_wrap(sink, dst + ooffset); + const ae_int32x2 *in = source->ptr; + ae_int32x2 *out = sink->ptr; for (left_samples = samples; left_samples; left_samples -= n) { - nmax = audio_stream_samples_without_wrap_s32(source, in); + nmax = cir_buf_samples_without_wrap_s32(in, source->buf_end); n = MIN(left_samples, nmax); - nmax = audio_stream_samples_without_wrap_s32(sink, out); + nmax = cir_buf_samples_without_wrap_s32(out, sink->buf_end); n = MIN(n, nmax); m = n >> 1; inu = AE_LA64_PP(in); @@ -334,13 +319,13 @@ static int pcm_convert_s24_to_s32(const struct audio_stream *source, /* process the left 1 sample to avoid memory access overrun */ if (n & 0x01) { - AE_L32_IP(sample, (ae_int32 *)in, sizeof(ae_int32)); + AE_L32_IP(sample, (const ae_int32 *)in, sizeof(ae_int32)); AE_S32_L_IP(AE_SLAI32(sample, 8), (ae_int32 *)out, sizeof(ae_int32)); } - in = audio_stream_wrap(source, in); - out = audio_stream_wrap(sink, out); + in = cir_buf_wrap(in, source->buf_start, source->buf_end); + out = cir_buf_wrap(out, sink->buf_start, sink->buf_end); } return samples; @@ -368,24 +353,21 @@ static ae_int32x2 pcm_shift_s32_to_s24(ae_int32x2 sample) * \param[in] samples Number of samples to process. * \return error code or number of processed samples. */ -static int pcm_convert_s32_to_s24(const struct audio_stream *source, - uint32_t ioffset, struct audio_stream *sink, - uint32_t ooffset, uint32_t samples, uint32_t chmap) +static int pcm_convert_s32_to_s24(const struct cir_buf_source *source, + uint32_t src_channels, struct cir_buf_sink *sink, + uint32_t sink_channels, size_t samples, uint32_t chmap) { - int32_t *src = audio_stream_get_rptr(source); - int32_t *dst = audio_stream_get_wptr(sink); ae_int32x2 sample = AE_ZERO32(); - uint32_t nmax, i, n, m, left_samples; + size_t nmax, i, n, m, left_samples; ae_valign outu = AE_ZALIGN64(); ae_valign inu = AE_ZALIGN64(); - - ae_int32x2 *in = audio_stream_wrap(source, src + ioffset); - ae_int32x2 *out = audio_stream_wrap(sink, dst + ooffset); + const ae_int32x2 *in = source->ptr; + ae_int32x2 *out = sink->ptr; for (left_samples = samples; left_samples; left_samples -= n) { - nmax = audio_stream_samples_without_wrap_s32(source, in); + nmax = cir_buf_samples_without_wrap_s32(in, source->buf_end); n = MIN(left_samples, nmax); - nmax = audio_stream_samples_without_wrap_s32(sink, out); + nmax = cir_buf_samples_without_wrap_s32(out, sink->buf_end); n = MIN(n, nmax); m = n >> 1; inu = AE_LA64_PP(in); @@ -400,37 +382,34 @@ static int pcm_convert_s32_to_s24(const struct audio_stream *source, /* process the left 1 sample to avoid memory access overrun */ if (n & 0x01) { /* load one 32 bit sample */ - AE_L32_IP(sample, (ae_int32 *)in, sizeof(ae_int32)); + AE_L32_IP(sample, (const ae_int32 *)in, sizeof(ae_int32)); /* shift right and store one 32 bit sample */ sample = pcm_shift_s32_to_s24(sample); AE_S32_L_IP(sample, (ae_int32 *)out, sizeof(ae_int32)); } - in = audio_stream_wrap(source, in); - out = audio_stream_wrap(sink, out); + in = cir_buf_wrap(in, source->buf_start, source->buf_end); + out = cir_buf_wrap(out, sink->buf_start, sink->buf_end); } return samples; } -static int pcm_convert_s32_to_s24_be(const struct audio_stream *source, - uint32_t ioffset, struct audio_stream *sink, - uint32_t ooffset, uint32_t samples, uint32_t chmap) +static int pcm_convert_s32_to_s24_be(const struct cir_buf_source *source, + uint32_t src_channels, struct cir_buf_sink *sink, + uint32_t sink_channels, size_t samples, uint32_t chmap) { - int32_t *src = audio_stream_get_rptr(source); - int32_t *dst = audio_stream_get_wptr(sink); ae_int32x2 sample = AE_ZERO32(); - uint32_t nmax, i, n, m, left_samples; + size_t nmax, i, n, m, left_samples; ae_valign outu = AE_ZALIGN64(); ae_valign inu = AE_ZALIGN64(); - - ae_int32x2 *in = audio_stream_wrap(source, src + ioffset); - ae_int32x2 *out = audio_stream_wrap(sink, dst + ooffset); + const ae_int32x2 *in = source->ptr; + ae_int32x2 *out = sink->ptr; for (left_samples = samples; left_samples; left_samples -= n) { - nmax = audio_stream_samples_without_wrap_s32(source, in); + nmax = cir_buf_samples_without_wrap_s32(in, source->buf_end); n = MIN(left_samples, nmax); - nmax = audio_stream_samples_without_wrap_s32(sink, out); + nmax = cir_buf_samples_without_wrap_s32(out, sink->buf_end); n = MIN(n, nmax); m = n >> 1; inu = AE_LA64_PP(in); @@ -447,15 +426,15 @@ static int pcm_convert_s32_to_s24_be(const struct audio_stream *source, /* process the left 1 sample to avoid memory access overrun */ if (n & 0x01) { /* load one 32 bit sample */ - AE_L32_IP(sample, (ae_int32 *)in, sizeof(ae_int32)); + AE_L32_IP(sample, (const ae_int32 *)in, sizeof(ae_int32)); /* shift with saturation and rounding and store one 32 bit sample */ sample = AE_SRAA32RS(sample, 8); sample = AE_SLAI32S(sample, 8); AE_S32_L_IP(sample, (ae_int32 *)out, sizeof(ae_int32)); } - in = audio_stream_wrap(source, in); - out = audio_stream_wrap(sink, out); + in = cir_buf_wrap(in, source->buf_start, source->buf_end); + out = cir_buf_wrap(out, sink->buf_start, sink->buf_end); } return samples; @@ -481,14 +460,13 @@ static inline int _round_s(xtfloat x) * \param[out] pdst destination linear buffer. * \param[in] samples Number of samples to process. */ -static void pcm_convert_s16_to_f_lin(const void *psrc, void *pdst, - uint32_t samples) +static void pcm_convert_s16_to_f_lin(const void *psrc, void *pdst, size_t samples) { const ae_int16 *in = psrc; xtfloat *out = pdst; ae_int16x4 sample = AE_ZERO16(); xtfloat fl; - int i = 0; + size_t i = 0; while (i < samples) { /* load one 16 bit sample */ @@ -511,14 +489,13 @@ static void pcm_convert_s16_to_f_lin(const void *psrc, void *pdst, * \param[out] pdst destination linear buffer. * \param[in] samples Number of samples to process. */ -static void pcm_convert_f_to_s16_lin(const void *psrc, void *pdst, - uint32_t samples) +static void pcm_convert_f_to_s16_lin(const void *psrc, void *pdst, size_t samples) { const xtfloat *in = psrc; ae_int16x4 *out = pdst; xtfloat x; int y; - int i = 0; + size_t i = 0; while (i < samples) { /* load one 32 bit sample */ @@ -541,11 +518,11 @@ static void pcm_convert_f_to_s16_lin(const void *psrc, void *pdst, * \param[in] samples Number of samples to process. * \return error code or number of processed samples. */ -static int pcm_convert_s16_to_f(const struct audio_stream *source, - uint32_t ioffset, struct audio_stream *sink, - uint32_t ooffset, uint32_t samples, uint32_t chmap) +static int pcm_convert_s16_to_f(const struct cir_buf_source *source, + uint32_t src_channels, struct cir_buf_sink *sink, + uint32_t sink_channels, size_t samples, uint32_t chmap) { - return pcm_convert_as_linear(source, ioffset, sink, ooffset, samples, + return pcm_convert_as_linear(source, sizeof(int16_t), sink, sizeof(int32_t), samples, pcm_convert_s16_to_f_lin); } @@ -556,11 +533,11 @@ static int pcm_convert_s16_to_f(const struct audio_stream *source, * \param[in] samples Number of samples to process. * \return error code or number of processed samples. */ -static int pcm_convert_f_to_s16(const struct audio_stream *source, - uint32_t ioffset, struct audio_stream *sink, - uint32_t ooffset, uint32_t samples, uint32_t chmap) +static int pcm_convert_f_to_s16(const struct cir_buf_source *source, + uint32_t src_channels, struct cir_buf_sink *sink, + uint32_t sink_channels, size_t samples, uint32_t chmap) { - return pcm_convert_as_linear(source, ioffset, sink, ooffset, samples, + return pcm_convert_as_linear(source, sizeof(int32_t), sink, sizeof(int16_t), samples, pcm_convert_f_to_s16_lin); } #endif /* CONFIG_PCM_CONVERTER_FORMAT_FLOAT && CONFIG_PCM_CONVERTER_FORMAT_S16LE */ @@ -573,15 +550,14 @@ static int pcm_convert_f_to_s16(const struct audio_stream *source, * \param[out] pdst destination linear buffer. * \param[in] samples Number of samples to process. */ -static void pcm_convert_s24_to_f_lin(const void *psrc, void *pdst, - uint32_t samples) +static void pcm_convert_s24_to_f_lin(const void *psrc, void *pdst, size_t samples) { const ae_int32 *in = psrc; xtfloat *out = pdst; ae_int32x2 sample = AE_ZERO32(); xtfloat fl; const xtfloat ratio = (xtfloat)(1.f / (1 << (23 - 15))); - int i = 0; + size_t i = 0; while (i < samples) { /* load one 24 bit sample */ @@ -607,14 +583,13 @@ static void pcm_convert_s24_to_f_lin(const void *psrc, void *pdst, * \param[out] pdst destination linear buffer. * \param[in] samples Number of samples to process. */ -static void pcm_convert_f_to_s24_lin(const void *psrc, void *pdst, - uint32_t samples) +static void pcm_convert_f_to_s24_lin(const void *psrc, void *pdst, size_t samples) { const xtfloat *in = psrc; ae_int32 *out = pdst; xtfloat x; ae_int32x2 y1; - int i = 0; + size_t i = 0; const xtfloat ratio = (xtfloat)(1 << (23 - 15)); while (i < samples) { @@ -642,11 +617,11 @@ static void pcm_convert_f_to_s24_lin(const void *psrc, void *pdst, * \param[in] samples Number of samples to process. * \return error code or number of processed samples. */ -static int pcm_convert_s24_to_f(const struct audio_stream *source, - uint32_t ioffset, struct audio_stream *sink, - uint32_t ooffset, uint32_t samples, uint32_t chmap) +static int pcm_convert_s24_to_f(const struct cir_buf_source *source, + uint32_t src_channels, struct cir_buf_sink *sink, + uint32_t sink_channels, size_t samples, uint32_t chmap) { - return pcm_convert_as_linear(source, ioffset, sink, ooffset, samples, + return pcm_convert_as_linear(source, sizeof(int32_t), sink, sizeof(int32_t), samples, pcm_convert_s24_to_f_lin); } @@ -657,11 +632,11 @@ static int pcm_convert_s24_to_f(const struct audio_stream *source, * \param[in] samples Number of samples to process. * \return error code or number of processed samples. */ -static int pcm_convert_f_to_s24(const struct audio_stream *source, - uint32_t ioffset, struct audio_stream *sink, - uint32_t ooffset, uint32_t samples, uint32_t chmap) +static int pcm_convert_f_to_s24(const struct cir_buf_source *source, + uint32_t src_channels, struct cir_buf_sink *sink, + uint32_t sink_channels, size_t samples, uint32_t chmap) { - return pcm_convert_as_linear(source, ioffset, sink, ooffset, samples, + return pcm_convert_as_linear(source, sizeof(int32_t), sink, sizeof(int32_t), samples, pcm_convert_f_to_s24_lin); } #endif /* CONFIG_PCM_CONVERTER_FORMAT_FLOAT && CONFIG_PCM_CONVERTER_FORMAT_24LE */ @@ -674,15 +649,14 @@ static int pcm_convert_f_to_s24(const struct audio_stream *source, * \param[out] pdst destination linear buffer. * \param[in] samples Number of samples to process. */ -static void pcm_convert_s32_to_f_lin(const void *psrc, void *pdst, - uint32_t samples) +static void pcm_convert_s32_to_f_lin(const void *psrc, void *pdst, size_t samples) { const ae_int32 *in = psrc; xtfloat *out = pdst; ae_int32x2 sample = AE_ZERO32(); xtfloat fl; const xtfloat ratio = (xtfloat)(1.f / (1ul << (31 - 15))); - int i = 0; + size_t i = 0; while (i < samples) { /* load one 32 bit sample */ @@ -706,14 +680,13 @@ static void pcm_convert_s32_to_f_lin(const void *psrc, void *pdst, * \param[out] pdst destination linear buffer. * \param[in] samples Number of samples to process. */ -static void pcm_convert_f_to_s32_lin(const void *psrc, void *pdst, - uint32_t samples) +static void pcm_convert_f_to_s32_lin(const void *psrc, void *pdst, size_t samples) { const xtfloat *in = psrc; ae_int32 *out = pdst; xtfloat x; ae_int32x2 y1; - int i = 0; + size_t i = 0; const xtfloat ratio = (xtfloat)(1ul << (31 - 15)); while (i < samples) { @@ -739,11 +712,11 @@ static void pcm_convert_f_to_s32_lin(const void *psrc, void *pdst, * \param[in] samples Number of samples to process. * \return error code or number of processed samples. */ -static int pcm_convert_s32_to_f(const struct audio_stream *source, - uint32_t ioffset, struct audio_stream *sink, - uint32_t ooffset, uint32_t samples, uint32_t chmap) +static int pcm_convert_s32_to_f(const struct cir_buf_source *source, + uint32_t src_channels, struct cir_buf_sink *sink, + uint32_t sink_channels, size_t samples, uint32_t chmap) { - return pcm_convert_as_linear(source, ioffset, sink, ooffset, samples, + return pcm_convert_as_linear(source, sizeof(int32_t), sink, sizeof(int32_t), samples, pcm_convert_s32_to_f_lin); } @@ -754,11 +727,11 @@ static int pcm_convert_s32_to_f(const struct audio_stream *source, * \param[in] samples Number of samples to process. * \return error code or number of processed samples. */ -static int pcm_convert_f_to_s32(const struct audio_stream *source, - uint32_t ioffset, struct audio_stream *sink, - uint32_t ooffset, uint32_t samples, uint32_t chmap) +static int pcm_convert_f_to_s32(const struct cir_buf_source *source, + uint32_t src_channels, struct cir_buf_sink *sink, + uint32_t sink_channels, size_t samples, uint32_t chmap) { - return pcm_convert_as_linear(source, ioffset, sink, ooffset, samples, + return pcm_convert_as_linear(source, sizeof(int32_t), sink, sizeof(int32_t), samples, pcm_convert_f_to_s32_lin); } #endif /* CONFIG_PCM_CONVERTER_FORMAT_FLOAT && CONFIG_PCM_CONVERTER_FORMAT_32LE */ @@ -766,20 +739,20 @@ static int pcm_convert_f_to_s32(const struct audio_stream *source, const struct pcm_func_map pcm_func_map[] = { #if CONFIG_PCM_CONVERTER_FORMAT_S16LE - { SOF_IPC_FRAME_S16_LE, SOF_IPC_FRAME_S16_LE, just_copy }, + { SOF_IPC_FRAME_S16_LE, SOF_IPC_FRAME_S16_LE, just_copy_2b }, #endif /* CONFIG_PCM_CONVERTER_FORMAT_S16LE */ #if CONFIG_PCM_CONVERTER_FORMAT_S24LE - { SOF_IPC_FRAME_S24_4LE, SOF_IPC_FRAME_S24_4LE, just_copy }, + { SOF_IPC_FRAME_S24_4LE, SOF_IPC_FRAME_S24_4LE, just_copy_4b }, #endif /* CONFIG_PCM_CONVERTER_FORMAT_S24LE */ #if CONFIG_PCM_CONVERTER_FORMAT_S24_3LE - { SOF_IPC_FRAME_S24_3LE, SOF_IPC_FRAME_S24_3LE, just_copy }, + { SOF_IPC_FRAME_S24_3LE, SOF_IPC_FRAME_S24_3LE, just_copy_3b }, #endif /* CONFIG_PCM_CONVERTER_FORMAT_S24_3LE */ #if CONFIG_PCM_CONVERTER_FORMAT_S24LE && CONFIG_PCM_CONVERTER_FORMAT_S16LE { SOF_IPC_FRAME_S16_LE, SOF_IPC_FRAME_S24_4LE, pcm_convert_s16_to_s24 }, { SOF_IPC_FRAME_S24_4LE, SOF_IPC_FRAME_S16_LE, pcm_convert_s24_to_s16 }, #endif /* CONFIG_PCM_CONVERTER_FORMAT_S24LE && CONFIG_PCM_CONVERTER_FORMAT_S16LE */ #if CONFIG_PCM_CONVERTER_FORMAT_S32LE - { SOF_IPC_FRAME_S32_LE, SOF_IPC_FRAME_S32_LE, just_copy }, + { SOF_IPC_FRAME_S32_LE, SOF_IPC_FRAME_S32_LE, just_copy_4b }, #endif /* CONFIG_PCM_CONVERTER_FORMAT_S32LE */ #if CONFIG_PCM_CONVERTER_FORMAT_S32LE && CONFIG_PCM_CONVERTER_FORMAT_S16LE { SOF_IPC_FRAME_S16_LE, SOF_IPC_FRAME_S32_LE, pcm_convert_s16_to_s32 }, @@ -791,7 +764,7 @@ const struct pcm_func_map pcm_func_map[] = { #endif /* CONFIG_PCM_CONVERTER_FORMAT_S32LE && CONFIG_PCM_CONVERTER_FORMAT_S24LE */ #if XCHAL_HAVE_FP #if CONFIG_PCM_CONVERTER_FORMAT_FLOAT - { SOF_IPC_FRAME_FLOAT, SOF_IPC_FRAME_FLOAT, just_copy }, + { SOF_IPC_FRAME_FLOAT, SOF_IPC_FRAME_FLOAT, just_copy_4b }, #endif /* CONFIG_PCM_CONVERTER_FORMAT_FLOAT */ #if CONFIG_PCM_CONVERTER_FORMAT_FLOAT && CONFIG_PCM_CONVERTER_FORMAT_S16LE { SOF_IPC_FRAME_S16_LE, SOF_IPC_FRAME_FLOAT, pcm_convert_s16_to_f }, @@ -810,25 +783,22 @@ const struct pcm_func_map pcm_func_map[] = { const size_t pcm_func_count = ARRAY_SIZE(pcm_func_map); #if CONFIG_PCM_CONVERTER_FORMAT_S16_C16_AND_S16_C32 -static int pcm_convert_s16_c16_to_s16_c32(const struct audio_stream *source, - uint32_t ioffset, - struct audio_stream *sink, - uint32_t ooffset, uint32_t samples, uint32_t chmap) +static int pcm_convert_s16_c16_to_s16_c32(const struct cir_buf_source *source, + uint32_t src_channels, + struct cir_buf_sink *sink, + uint32_t sink_channels, size_t samples, uint32_t chmap) { - int16_t *src = audio_stream_get_rptr(source); - int32_t *dst = audio_stream_get_wptr(sink); ae_int16x4 sample = AE_ZERO16(); - uint32_t nmax, i, n, m, left, left_samples; + size_t nmax, i, n, m, left, left_samples; ae_valign inu = AE_ZALIGN64(); ae_valign outu = AE_ZALIGN64(); - - ae_int16x4 *in = audio_stream_wrap(source, src + ioffset); - ae_int32x2 *out = audio_stream_wrap(sink, dst + ooffset); + const ae_int16x4 *in = source->ptr; + ae_int32x2 *out = sink->ptr; for (left_samples = samples; left_samples; left_samples -= n) { - nmax = audio_stream_samples_without_wrap_s16(source, in); + nmax = cir_buf_samples_without_wrap_s16(in, source->buf_end); n = MIN(left_samples, nmax); - nmax = audio_stream_samples_without_wrap_s32(sink, out); + nmax = cir_buf_samples_without_wrap_s32(out, sink->buf_end); n = MIN(n, nmax); m = n >> 2; left = n & 0x03; @@ -848,39 +818,36 @@ static int pcm_convert_s16_c16_to_s16_c32(const struct audio_stream *source, */ for (i = 0; i < left ; i++) { /* load one 16 bit samples */ - AE_L16_IP(sample, (ae_int16 *)in, sizeof(ae_int16)); + AE_L16_IP(sample, (const ae_int16 *)in, sizeof(ae_int16)); /* store one 32 bit sample */ AE_S32_L_IP(AE_SEXT32X2D16_32(sample), (ae_int32 *)out, sizeof(ae_int32)); } - in = audio_stream_wrap(source, in); - out = audio_stream_wrap(sink, out); + in = cir_buf_wrap(in, source->buf_start, source->buf_end); + out = cir_buf_wrap(out, sink->buf_start, sink->buf_end); } return samples; } -static int pcm_convert_s16_c32_to_s16_c16(const struct audio_stream *source, - uint32_t ioffset, - struct audio_stream *sink, - uint32_t ooffset, uint32_t samples, uint32_t chmap) +static int pcm_convert_s16_c32_to_s16_c16(const struct cir_buf_source *source, + uint32_t src_channels, + struct cir_buf_sink *sink, + uint32_t sink_channels, size_t samples, uint32_t chmap) { - int32_t *src = audio_stream_get_rptr(source); - int16_t *dst = audio_stream_get_wptr(sink); ae_int16x4 sample = AE_ZERO16(); ae_int32x2 sample_1 = AE_ZERO32(); ae_int32x2 sample_2 = AE_ZERO32(); - uint32_t nmax, i, n, m, left, left_samples; + size_t nmax, i, n, m, left, left_samples; ae_valign outu = AE_ZALIGN64(); ae_valign inu = AE_ZALIGN64(); - - ae_int32x2 *in = audio_stream_wrap(source, src + ioffset); - ae_int16x4 *out = audio_stream_wrap(sink, dst + ooffset); + const ae_int32x2 *in = source->ptr; + ae_int16x4 *out = sink->ptr; for (left_samples = samples; left_samples; left_samples -= n) { - nmax = audio_stream_samples_without_wrap_s32(source, in); + nmax = cir_buf_samples_without_wrap_s32(in, source->buf_end); n = MIN(left_samples, nmax); - nmax = audio_stream_samples_without_wrap_s16(sink, out); + nmax = cir_buf_samples_without_wrap_s16(out, sink->buf_end); n = MIN(n, nmax); m = n >> 2; left = n & 0x3; @@ -903,15 +870,15 @@ static int pcm_convert_s16_c32_to_s16_c16(const struct audio_stream *source, */ for (i = 0; i < left ; i++) { /* load one 32 bit samples */ - AE_L32_IP(sample_1, (ae_int32 *)in, sizeof(ae_int32)); + AE_L32_IP(sample_1, (const ae_int32 *)in, sizeof(ae_int32)); /* shift and round */ sample = AE_CVT16X4(sample_1, sample_1); /* store one 16 bit sample */ AE_S16_0_IP(AE_MOVAD16_0(sample), (ae_int16 *)out, sizeof(ae_int16)); } - in = audio_stream_wrap(source, in); - out = audio_stream_wrap(sink, out); + in = cir_buf_wrap(in, source->buf_start, source->buf_end); + out = cir_buf_wrap(out, sink->buf_start, sink->buf_end); } return samples; @@ -919,25 +886,22 @@ static int pcm_convert_s16_c32_to_s16_c16(const struct audio_stream *source, #endif #if CONFIG_PCM_CONVERTER_FORMAT_S16_C32_AND_S32_C32 -static int pcm_convert_s16_c32_to_s32_c32(const struct audio_stream *source, - uint32_t ioffset, - struct audio_stream *sink, - uint32_t ooffset, uint32_t samples, uint32_t chmap) +static int pcm_convert_s16_c32_to_s32_c32(const struct cir_buf_source *source, + uint32_t src_channels, + struct cir_buf_sink *sink, + uint32_t sink_channels, size_t samples, uint32_t chmap) { - int32_t *src = audio_stream_get_rptr(source); - int32_t *dst = audio_stream_get_wptr(sink); ae_int32x2 sample = AE_ZERO32(); - uint32_t nmax, i, n, m, left_samples; + size_t nmax, i, n, m, left_samples; ae_valign outu = AE_ZALIGN64(); ae_valign inu = AE_ZALIGN64(); - - ae_int32x2 *in = audio_stream_wrap(source, src + ioffset); - ae_int32x2 *out = audio_stream_wrap(sink, dst + ooffset); + const ae_int32x2 *in = source->ptr; + ae_int32x2 *out = sink->ptr; for (left_samples = samples; left_samples; left_samples -= n) { - nmax = audio_stream_samples_without_wrap_s32(source, in); + nmax = cir_buf_samples_without_wrap_s32(in, source->buf_end); n = MIN(left_samples, nmax); - nmax = audio_stream_samples_without_wrap_s32(sink, out); + nmax = cir_buf_samples_without_wrap_s32(out, sink->buf_end); n = MIN(n, nmax); m = n >> 1; inu = AE_LA64_PP(in); @@ -951,36 +915,33 @@ static int pcm_convert_s16_c32_to_s32_c32(const struct audio_stream *source, /* process the left 1 sample to avoid memory access overrun */ if (n & 0x01) { - AE_L32_IP(sample, (ae_int32 *)in, sizeof(ae_int32)); + AE_L32_IP(sample, (const ae_int32 *)in, sizeof(ae_int32)); AE_S32_L_IP(AE_SLAI32(sample, 16), (ae_int32 *)out, sizeof(ae_int32)); } - in = audio_stream_wrap(source, in); - out = audio_stream_wrap(sink, out); + in = cir_buf_wrap(in, source->buf_start, source->buf_end); + out = cir_buf_wrap(out, sink->buf_start, sink->buf_end); } return samples; } -static int pcm_convert_s32_c32_to_s16_c32(const struct audio_stream *source, - uint32_t ioffset, - struct audio_stream *sink, - uint32_t ooffset, uint32_t samples, uint32_t chmap) +static int pcm_convert_s32_c32_to_s16_c32(const struct cir_buf_source *source, + uint32_t src_channels, + struct cir_buf_sink *sink, + uint32_t sink_channels, size_t samples, uint32_t chmap) { - int32_t *src = audio_stream_get_rptr(source); - int32_t *dst = audio_stream_get_wptr(sink); ae_int32x2 sample = AE_ZERO32(); - uint32_t nmax, i, n, m, left_samples; + size_t nmax, i, n, m, left_samples; ae_valign outu = AE_ZALIGN64(); ae_valign inu = AE_ZALIGN64(); - - ae_int32x2 *in = audio_stream_wrap(source, src + ioffset); - ae_int32x2 *out = audio_stream_wrap(sink, dst + ooffset); + const ae_int32x2 *in = source->ptr; + ae_int32x2 *out = sink->ptr; for (left_samples = samples; left_samples; left_samples -= n) { - nmax = audio_stream_samples_without_wrap_s32(source, in); + nmax = cir_buf_samples_without_wrap_s32(in, source->buf_end); n = MIN(left_samples, nmax); - nmax = audio_stream_samples_without_wrap_s32(sink, out); + nmax = cir_buf_samples_without_wrap_s32(out, sink->buf_end); n = MIN(n, nmax); m = n >> 1; inu = AE_LA64_PP(in); @@ -994,12 +955,12 @@ static int pcm_convert_s32_c32_to_s16_c32(const struct audio_stream *source, /* process the left 1 sample to avoid memory access overrun */ if (n & 0x01) { - AE_L32_IP(sample, (ae_int32 *)in, sizeof(ae_int32)); + AE_L32_IP(sample, (const ae_int32 *)in, sizeof(ae_int32)); AE_S32_L_IP(AE_SRAA32RS(sample, 16), (ae_int32 *)out, sizeof(ae_int32)); } - in = audio_stream_wrap(source, in); - out = audio_stream_wrap(sink, out); + in = cir_buf_wrap(in, source->buf_start, source->buf_end); + out = cir_buf_wrap(out, sink->buf_start, sink->buf_end); } return samples; @@ -1007,25 +968,22 @@ static int pcm_convert_s32_c32_to_s16_c32(const struct audio_stream *source, #endif #if CONFIG_PCM_CONVERTER_FORMAT_S16_C32_AND_S24_C32 -static int pcm_convert_s16_c32_to_s24_c32(const struct audio_stream *source, - uint32_t ioffset, - struct audio_stream *sink, - uint32_t ooffset, uint32_t samples, uint32_t chmap) +static int pcm_convert_s16_c32_to_s24_c32(const struct cir_buf_source *source, + uint32_t src_channels, + struct cir_buf_sink *sink, + uint32_t sink_channels, size_t samples, uint32_t chmap) { - int32_t *src = audio_stream_get_rptr(source); - int32_t *dst = audio_stream_get_wptr(sink); ae_int32x2 sample = AE_ZERO32(); - uint32_t nmax, i, n, m, left_samples; + size_t nmax, i, n, m, left_samples; ae_valign outu = AE_ZALIGN64(); ae_valign inu = AE_ZALIGN64(); - - ae_int32x2 *in = audio_stream_wrap(source, src + ioffset); - ae_int32x2 *out = audio_stream_wrap(sink, dst + ooffset); + const ae_int32x2 *in = source->ptr; + ae_int32x2 *out = sink->ptr; for (left_samples = samples; left_samples; left_samples -= n) { - nmax = audio_stream_samples_without_wrap_s32(source, in); + nmax = cir_buf_samples_without_wrap_s32(in, source->buf_end); n = MIN(left_samples, nmax); - nmax = audio_stream_samples_without_wrap_s32(sink, out); + nmax = cir_buf_samples_without_wrap_s32(out, sink->buf_end); n = MIN(n, nmax); m = n >> 1; inu = AE_LA64_PP(in); @@ -1039,12 +997,12 @@ static int pcm_convert_s16_c32_to_s24_c32(const struct audio_stream *source, /* process the left 1 sample to avoid memory access overrun */ if (n & 0x01) { - AE_L32_IP(sample, (ae_int32 *)in, sizeof(ae_int32)); + AE_L32_IP(sample, (const ae_int32 *)in, sizeof(ae_int32)); AE_S32_L_IP(AE_SLAI32(sample, 8), (ae_int32 *)out, sizeof(ae_int32)); } - in = audio_stream_wrap(source, in); - out = audio_stream_wrap(sink, out); + in = cir_buf_wrap(in, source->buf_start, source->buf_end); + out = cir_buf_wrap(out, sink->buf_start, sink->buf_end); } return samples; @@ -1062,25 +1020,22 @@ static ae_int32x2 pcm_shift_s24_c32_to_s16(ae_int32x2 sample) return sample; } -static int pcm_convert_s24_c32_to_s16_c32(const struct audio_stream *source, - uint32_t ioffset, - struct audio_stream *sink, - uint32_t ooffset, uint32_t samples, uint32_t chmap) +static int pcm_convert_s24_c32_to_s16_c32(const struct cir_buf_source *source, + uint32_t src_channels, + struct cir_buf_sink *sink, + uint32_t sink_channels, size_t samples, uint32_t chmap) { - int32_t *src = audio_stream_get_rptr(source); - int32_t *dst = audio_stream_get_wptr(sink); ae_int32x2 sample = AE_ZERO32(); - uint32_t nmax, i, n, m, left_samples; + size_t nmax, i, n, m, left_samples; ae_valign outu = AE_ZALIGN64(); ae_valign inu = AE_ZALIGN64(); - - ae_int32x2 *in = audio_stream_wrap(source, src + ioffset); - ae_int32x2 *out = audio_stream_wrap(sink, dst + ooffset); + const ae_int32x2 *in = source->ptr; + ae_int32x2 *out = sink->ptr; for (left_samples = samples; left_samples; left_samples -= n) { - nmax = audio_stream_samples_without_wrap_s32(source, in); + nmax = cir_buf_samples_without_wrap_s32(in, source->buf_end); n = MIN(left_samples, nmax); - nmax = audio_stream_samples_without_wrap_s32(sink, out); + nmax = cir_buf_samples_without_wrap_s32(out, sink->buf_end); n = MIN(n, nmax); m = n >> 1; inu = AE_LA64_PP(in); @@ -1095,13 +1050,13 @@ static int pcm_convert_s24_c32_to_s16_c32(const struct audio_stream *source, /* process the left 1 sample to avoid memory access overrun */ if (n & 0x01) { - AE_L32_IP(sample, (ae_int32 *)in, sizeof(ae_int32)); + AE_L32_IP(sample, (const ae_int32 *)in, sizeof(ae_int32)); sample = pcm_shift_s24_c32_to_s16(sample); AE_S32_L_IP(sample, (ae_int32 *)out, sizeof(ae_int32)); } - in = audio_stream_wrap(source, in); - out = audio_stream_wrap(sink, out); + in = cir_buf_wrap(in, source->buf_start, source->buf_end); + out = cir_buf_wrap(out, sink->buf_start, sink->buf_end); } return samples; @@ -1109,26 +1064,23 @@ static int pcm_convert_s24_c32_to_s16_c32(const struct audio_stream *source, #endif #if CONFIG_PCM_CONVERTER_FORMAT_S24_C24_AND_S24_C32 -static int pcm_convert_s24_c24_to_s24_c32(const struct audio_stream *source, - uint32_t ioffset, - struct audio_stream *sink, - uint32_t ooffset, uint32_t samples, uint32_t chmap) +static int pcm_convert_s24_c24_to_s24_c32(const struct cir_buf_source *source, + uint32_t src_channels, + struct cir_buf_sink *sink, + uint32_t sink_channels, size_t samples, uint32_t chmap) { - uint8_t *src = audio_stream_get_rptr(source); - int32_t *dst = audio_stream_get_wptr(sink); ae_int24x2 sample24 = AE_ZERO24(); ae_int32x2 sample = AE_ZERO32(); - uint32_t nmax, i, n, m, left_samples; + size_t nmax, i, n, m, left_samples; ae_valign outu = AE_ZALIGN64(); ae_valign inu = AE_ZALIGN64(); - - ae_int24x2 *in = audio_stream_wrap(source, src + ioffset * 3); - ae_int32x2 *out = audio_stream_wrap(sink, dst + ooffset); + const ae_int24x2 *in = source->ptr; + ae_int32x2 *out = sink->ptr; for (left_samples = samples; left_samples; left_samples -= n) { - nmax = audio_stream_bytes_without_wrap(source, in) / 3; + nmax = cir_buf_bytes_without_wrap(in, source->buf_end) / 3; n = MIN(left_samples, nmax); - nmax = audio_stream_samples_without_wrap_s32(sink, out); + nmax = cir_buf_samples_without_wrap_s32(out, sink->buf_end); n = MIN(n, nmax); m = n >> 1; inu = AE_LA64_PP(in); @@ -1149,33 +1101,30 @@ static int pcm_convert_s24_c24_to_s24_c32(const struct audio_stream *source, sizeof(ae_int32)); } - in = audio_stream_wrap(source, in); - out = audio_stream_wrap(sink, out); + in = cir_buf_wrap(in, source->buf_start, source->buf_end); + out = cir_buf_wrap(out, sink->buf_start, sink->buf_end); } return samples; } -static int pcm_convert_s24_c32_to_s24_c24(const struct audio_stream *source, - uint32_t ioffset, - struct audio_stream *sink, - uint32_t ooffset, uint32_t samples, uint32_t chmap) +static int pcm_convert_s24_c32_to_s24_c24(const struct cir_buf_source *source, + uint32_t src_channels, + struct cir_buf_sink *sink, + uint32_t sink_channels, size_t samples, uint32_t chmap) { - int32_t *src = audio_stream_get_rptr(source); - uint8_t *dst = audio_stream_get_wptr(sink); ae_int32x2 sample = AE_ZERO32(); ae_int24x2 sample24 = AE_ZERO24(); - uint32_t nmax, i, n, m, left_samples; + size_t nmax, i, n, m, left_samples; ae_valign outu = AE_ZALIGN64(); ae_valign inu = AE_ZALIGN64(); - - ae_int32x2 *in = audio_stream_wrap(source, src + ioffset); - ae_int24x2 *out = audio_stream_wrap(sink, dst + ooffset * 3); + const ae_int32x2 *in = source->ptr; + ae_int24x2 *out = sink->ptr; for (left_samples = samples; left_samples; left_samples -= n) { - nmax = audio_stream_samples_without_wrap_s32(source, in); + nmax = cir_buf_samples_without_wrap_s32(in, source->buf_end); n = MIN(left_samples, nmax); - nmax = audio_stream_bytes_without_wrap(sink, out) / 3; + nmax = cir_buf_bytes_without_wrap(out, sink->buf_end) / 3; n = MIN(n, nmax); m = n >> 1; inu = AE_LA64_PP(in); @@ -1189,13 +1138,13 @@ static int pcm_convert_s24_c32_to_s24_c24(const struct audio_stream *source, /* process the left 1 sample to avoid memory access overrun */ if (n & 0x01) { - AE_L32_IP(sample, (ae_int32 *)in, sizeof(ae_int32)); + AE_L32_IP(sample, (const ae_int32 *)in, sizeof(ae_int32)); sample24 = AE_MOVINT24X2_FROMF32X2(sample); AE_SA24_IP(sample24, outu, out); } - in = audio_stream_wrap(source, in); - out = audio_stream_wrap(sink, out); + in = cir_buf_wrap(in, source->buf_start, source->buf_end); + out = cir_buf_wrap(out, sink->buf_start, sink->buf_end); } return samples; @@ -1227,7 +1176,7 @@ const struct pcm_func_vc_map pcm_func_vc_map[] = { #endif #if CONFIG_PCM_CONVERTER_FORMAT_S32LE && CONFIG_PCM_CONVERTER_FORMAT_S24LE { SOF_IPC_FRAME_S32_LE, SOF_IPC_FRAME_S24_4LE, SOF_IPC_FRAME_S32_LE, SOF_IPC_FRAME_S24_4LE, - just_copy}, + just_copy_4b}, { SOF_IPC_FRAME_S32_LE, SOF_IPC_FRAME_S24_4LE, SOF_IPC_FRAME_S32_LE, SOF_IPC_FRAME_S32_LE, pcm_convert_s24_to_s32}, { SOF_IPC_FRAME_S32_LE, SOF_IPC_FRAME_S32_LE, SOF_IPC_FRAME_S32_LE, SOF_IPC_FRAME_S24_4LE, @@ -1249,7 +1198,7 @@ const struct pcm_func_vc_map pcm_func_vc_map[] = { #endif #if CONFIG_PCM_CONVERTER_FORMAT_S16_C32_AND_S16_C32 { SOF_IPC_FRAME_S32_LE, SOF_IPC_FRAME_S16_LE, SOF_IPC_FRAME_S32_LE, SOF_IPC_FRAME_S16_LE, - just_copy }, + just_copy_4b }, #endif #if CONFIG_PCM_CONVERTER_FORMAT_S24_4LE_MSB && CONFIG_PCM_CONVERTER_FORMAT_S24LE @@ -1258,14 +1207,14 @@ const struct pcm_func_vc_map pcm_func_vc_map[] = { { SOF_IPC_FRAME_S32_LE, SOF_IPC_FRAME_S24_4LE_MSB, SOF_IPC_FRAME_S32_LE, SOF_IPC_FRAME_S24_4LE, pcm_convert_s32_to_s24}, { SOF_IPC_FRAME_S32_LE, SOF_IPC_FRAME_S24_4LE_MSB, SOF_IPC_FRAME_S32_LE, - SOF_IPC_FRAME_S24_4LE_MSB, just_copy}, + SOF_IPC_FRAME_S24_4LE_MSB, just_copy_4b}, #endif #if CONFIG_PCM_CONVERTER_FORMAT_S32LE && CONFIG_PCM_CONVERTER_FORMAT_S24_4LE_MSB { SOF_IPC_FRAME_S32_LE, SOF_IPC_FRAME_S32_LE, SOF_IPC_FRAME_S32_LE, SOF_IPC_FRAME_S24_4LE_MSB, pcm_convert_s32_to_s24_be}, { SOF_IPC_FRAME_S32_LE, SOF_IPC_FRAME_S24_4LE_MSB, SOF_IPC_FRAME_S32_LE, - SOF_IPC_FRAME_S32_LE, just_copy}, + SOF_IPC_FRAME_S32_LE, just_copy_4b}, #endif #if CONFIG_PCM_CONVERTER_FORMAT_S24_4LE_MSB && CONFIG_PCM_CONVERTER_FORMAT_S16LE diff --git a/src/audio/pcm_converter/pcm_remap.c b/src/audio/pcm_converter/pcm_remap.c index 9204b21ee8ab..81d0fefd725d 100644 --- a/src/audio/pcm_converter/pcm_remap.c +++ b/src/audio/pcm_converter/pcm_remap.c @@ -6,17 +6,17 @@ #include <sof/audio/pcm_converter.h> #include <sof/audio/audio_stream.h> -static void mute_channel_c16(struct audio_stream *stream, int channel, int frames) +static void mute_channel_c16(struct cir_buf_sink *sink, uint32_t num_channels, uint32_t channel, + size_t frames) { - int num_channels = audio_stream_get_channels(stream); - int16_t *ptr = (int16_t *)audio_stream_get_wptr(stream) + channel; + int16_t *ptr = (int16_t *)sink->ptr + channel; while (frames) { - int samples_wo_wrap, n, i; + size_t samples_wo_wrap, n, i; - ptr = audio_stream_wrap(stream, ptr); + ptr = cir_buf_wrap(ptr, sink->buf_start, sink->buf_end); - samples_wo_wrap = audio_stream_samples_without_wrap_s16(stream, ptr); + samples_wo_wrap = cir_buf_samples_without_wrap_s16(ptr, sink->buf_end); n = SOF_DIV_ROUND_UP(samples_wo_wrap, num_channels); n = MIN(n, frames); @@ -29,17 +29,17 @@ static void mute_channel_c16(struct audio_stream *stream, int channel, int frame } } -static void mute_channel_c32(struct audio_stream *stream, int channel, int frames) +static void mute_channel_c32(struct cir_buf_sink *sink, uint32_t num_channels, uint32_t channel, + size_t frames) { - int num_channels = audio_stream_get_channels(stream); - int32_t *ptr = (int32_t *)audio_stream_get_wptr(stream) + channel; + int32_t *ptr = (int32_t *)sink->ptr + channel; while (frames) { - int samples_wo_wrap, n, i; + size_t samples_wo_wrap, n, i; - ptr = audio_stream_wrap(stream, ptr); + ptr = cir_buf_wrap(ptr, sink->buf_start, sink->buf_end); - samples_wo_wrap = audio_stream_samples_without_wrap_s32(stream, ptr); + samples_wo_wrap = cir_buf_samples_without_wrap_s32(ptr, sink->buf_end); n = SOF_DIV_ROUND_UP(samples_wo_wrap, num_channels); n = MIN(n, frames); @@ -52,54 +52,52 @@ static void mute_channel_c32(struct audio_stream *stream, int channel, int frame } } -static int remap_c16(const struct audio_stream *source, uint32_t dummy1, - struct audio_stream *sink, uint32_t dummy2, - uint32_t source_samples, uint32_t chmap) +static int remap_c16(const struct cir_buf_source *source, uint32_t src_channels, + struct cir_buf_sink *sink, uint32_t sink_channels, + size_t source_samples, uint32_t chmap) { - int src_channel, sink_channel; - int num_src_channels = audio_stream_get_channels(source); - int num_sink_channels = audio_stream_get_channels(sink); - int frames = source_samples / num_src_channels; + uint32_t src_channel, sink_channel; + size_t frames = source_samples / src_channels; - for (sink_channel = 0; sink_channel < num_sink_channels; sink_channel++) { - int16_t *src, *dst; - int frames_left; + for (sink_channel = 0; sink_channel < sink_channels; sink_channel++) { + const int16_t *src; + int16_t *dst; + size_t frames_left; src_channel = chmap & 0xf; chmap >>= 4; - if (src_channel == 0xf) { - mute_channel_c16(sink, sink_channel, frames); + /* 0xf means "mute"; also mute any out-of-range source channel so + * a crafted chmap nibble cannot index past the source frame. + */ + if (src_channel == 0xf || src_channel >= src_channels) { + mute_channel_c16(sink, sink_channels, sink_channel, frames); continue; } - assert(src_channel < num_src_channels); - - src = (int16_t *)audio_stream_get_rptr(source) + src_channel; - dst = (int16_t *)audio_stream_get_wptr(sink) + sink_channel; + src = (const int16_t *)source->ptr + src_channel; + dst = (int16_t *)sink->ptr + sink_channel; frames_left = frames; while (frames_left) { - int src_samples_wo_wrap, dst_samples_wo_wrap; - int src_loops, dst_loops, n, i; + size_t samples_wo_wrap, n, i; - src = audio_stream_wrap(source, src); - dst = audio_stream_wrap(sink, dst); + src = cir_buf_wrap(src, source->buf_start, source->buf_end); + dst = cir_buf_wrap(dst, sink->buf_start, sink->buf_end); - src_samples_wo_wrap = audio_stream_samples_without_wrap_s16(source, src); - src_loops = SOF_DIV_ROUND_UP(src_samples_wo_wrap, num_src_channels); + samples_wo_wrap = cir_buf_samples_without_wrap_s16(src, source->buf_end); + n = SOF_DIV_ROUND_UP(samples_wo_wrap, src_channels); - dst_samples_wo_wrap = audio_stream_samples_without_wrap_s16(sink, dst); - dst_loops = SOF_DIV_ROUND_UP(dst_samples_wo_wrap, num_sink_channels); + samples_wo_wrap = cir_buf_samples_without_wrap_s16(dst, sink->buf_end); + n = MIN(n, SOF_DIV_ROUND_UP(samples_wo_wrap, sink_channels)); - n = MIN(src_loops, dst_loops); n = MIN(n, frames_left); for (i = 0; i < n; i++) { *dst = *src; - src += num_src_channels; - dst += num_sink_channels; + src += src_channels; + dst += sink_channels; } frames_left -= n; @@ -109,49 +107,49 @@ static int remap_c16(const struct audio_stream *source, uint32_t dummy1, return source_samples; } -static inline int remap_c32_left_shift(const struct audio_stream *source, - struct audio_stream *sink, - uint32_t source_samples, uint32_t chmap, +static inline int remap_c32_left_shift(const struct cir_buf_source *source, + uint32_t num_src_channels, + struct cir_buf_sink *sink, + uint32_t num_sink_channels, + size_t source_samples, uint32_t chmap, int shift) { - int src_channel, sink_channel; - int num_src_channels = audio_stream_get_channels(source); - int num_sink_channels = audio_stream_get_channels(sink); - int frames = source_samples / num_src_channels; + uint32_t src_channel, sink_channel; + size_t frames = source_samples / num_src_channels; for (sink_channel = 0; sink_channel < num_sink_channels; sink_channel++) { - int32_t *src, *dst; - int frames_left; + const int32_t *src; + int32_t *dst; + size_t frames_left; src_channel = chmap & 0xf; chmap >>= 4; - if (src_channel == 0xf) { - mute_channel_c32(sink, sink_channel, frames); + /* 0xf means "mute"; also mute any out-of-range source channel so + * a crafted chmap nibble cannot index past the source frame. + */ + if (src_channel == 0xf || src_channel >= num_src_channels) { + mute_channel_c32(sink, num_sink_channels, sink_channel, frames); continue; } - assert(src_channel < num_src_channels); - - src = (int32_t *)audio_stream_get_rptr(source) + src_channel; - dst = (int32_t *)audio_stream_get_wptr(sink) + sink_channel; + src = (const int32_t *)source->ptr + src_channel; + dst = (int32_t *)sink->ptr + sink_channel; frames_left = frames; while (frames_left) { - int src_samples_wo_wrap, dst_samples_wo_wrap; - int src_loops, dst_loops, n, i; + size_t samples_wo_wrap, n, i; - src = audio_stream_wrap(source, src); - dst = audio_stream_wrap(sink, dst); + src = cir_buf_wrap(src, source->buf_start, source->buf_end); + dst = cir_buf_wrap(dst, sink->buf_start, sink->buf_end); - src_samples_wo_wrap = audio_stream_samples_without_wrap_s32(source, src); - src_loops = SOF_DIV_ROUND_UP(src_samples_wo_wrap, num_src_channels); + samples_wo_wrap = cir_buf_samples_without_wrap_s32(src, source->buf_end); + n = SOF_DIV_ROUND_UP(samples_wo_wrap, num_src_channels); - dst_samples_wo_wrap = audio_stream_samples_without_wrap_s32(sink, dst); - dst_loops = SOF_DIV_ROUND_UP(dst_samples_wo_wrap, num_sink_channels); + samples_wo_wrap = cir_buf_samples_without_wrap_s32(dst, sink->buf_end); + n = MIN(n, SOF_DIV_ROUND_UP(samples_wo_wrap, num_sink_channels)); - n = MIN(src_loops, dst_loops); n = MIN(n, frames_left); for (i = 0; i < n; i++) { @@ -167,49 +165,49 @@ static inline int remap_c32_left_shift(const struct audio_stream *source, return source_samples; } -static inline int remap_c32_right_shift(const struct audio_stream *source, - struct audio_stream *sink, - uint32_t source_samples, uint32_t chmap, +static inline int remap_c32_right_shift(const struct cir_buf_source *source, + uint32_t num_src_channels, + struct cir_buf_sink *sink, + uint32_t num_sink_channels, + size_t source_samples, uint32_t chmap, int shift) { - int src_channel, sink_channel; - int num_src_channels = audio_stream_get_channels(source); - int num_sink_channels = audio_stream_get_channels(sink); - int frames = source_samples / num_src_channels; + uint32_t src_channel, sink_channel; + size_t frames = source_samples / num_src_channels; for (sink_channel = 0; sink_channel < num_sink_channels; sink_channel++) { - int32_t *src, *dst; - int frames_left; + const int32_t *src; + int32_t *dst; + size_t frames_left; src_channel = chmap & 0xf; chmap >>= 4; - if (src_channel == 0xf) { - mute_channel_c32(sink, sink_channel, frames); + /* 0xf means "mute"; also mute any out-of-range source channel so + * a crafted chmap nibble cannot index past the source frame. + */ + if (src_channel == 0xf || src_channel >= num_src_channels) { + mute_channel_c32(sink, num_sink_channels, sink_channel, frames); continue; } - assert(src_channel < num_src_channels); - - src = (int32_t *)audio_stream_get_rptr(source) + src_channel; - dst = (int32_t *)audio_stream_get_wptr(sink) + sink_channel; + src = (const int32_t *)source->ptr + src_channel; + dst = (int32_t *)sink->ptr + sink_channel; frames_left = frames; while (frames_left) { - int src_samples_wo_wrap, dst_samples_wo_wrap; - int src_loops, dst_loops, n, i; + size_t samples_wo_wrap, n, i; - src = audio_stream_wrap(source, src); - dst = audio_stream_wrap(sink, dst); + src = cir_buf_wrap(src, source->buf_start, source->buf_end); + dst = cir_buf_wrap(dst, sink->buf_start, sink->buf_end); - src_samples_wo_wrap = audio_stream_samples_without_wrap_s32(source, src); - src_loops = SOF_DIV_ROUND_UP(src_samples_wo_wrap, num_src_channels); + samples_wo_wrap = cir_buf_samples_without_wrap_s32(src, source->buf_end); + n = SOF_DIV_ROUND_UP(samples_wo_wrap, num_src_channels); - dst_samples_wo_wrap = audio_stream_samples_without_wrap_s32(sink, dst); - dst_loops = SOF_DIV_ROUND_UP(dst_samples_wo_wrap, num_sink_channels); + samples_wo_wrap = cir_buf_samples_without_wrap_s32(dst, sink->buf_end); + n = MIN(n, SOF_DIV_ROUND_UP(samples_wo_wrap, num_sink_channels)); - n = MIN(src_loops, dst_loops); n = MIN(n, frames_left); for (i = 0; i < n; i++) { @@ -225,50 +223,49 @@ static inline int remap_c32_right_shift(const struct audio_stream *source, return source_samples; } -static inline int remap_c16_to_c32(const struct audio_stream *source, - struct audio_stream *sink, - uint32_t source_samples, uint32_t chmap, +static inline int remap_c16_to_c32(const struct cir_buf_source *source, + uint32_t num_src_channels, + struct cir_buf_sink *sink, + uint32_t num_sink_channels, + size_t source_samples, uint32_t chmap, int shift) { - int src_channel, sink_channel; - int num_src_channels = audio_stream_get_channels(source); - int num_sink_channels = audio_stream_get_channels(sink); - int frames = source_samples / num_src_channels; + uint32_t src_channel, sink_channel; + size_t frames = source_samples / num_src_channels; for (sink_channel = 0; sink_channel < num_sink_channels; sink_channel++) { - int16_t *src; + const int16_t *src; int32_t *dst; - int frames_left; + size_t frames_left; src_channel = chmap & 0xf; chmap >>= 4; - if (src_channel == 0xf) { - mute_channel_c32(sink, sink_channel, frames); + /* 0xf means "mute"; also mute any out-of-range source channel so + * a crafted chmap nibble cannot index past the source frame. + */ + if (src_channel == 0xf || src_channel >= num_src_channels) { + mute_channel_c32(sink, num_sink_channels, sink_channel, frames); continue; } - assert(src_channel < num_src_channels); - - src = (int16_t *)audio_stream_get_rptr(source) + src_channel; - dst = (int32_t *)audio_stream_get_wptr(sink) + sink_channel; + src = (const int16_t *)source->ptr + src_channel; + dst = (int32_t *)sink->ptr + sink_channel; frames_left = frames; while (frames_left) { - int src_samples_wo_wrap, dst_samples_wo_wrap; - int src_loops, dst_loops, n, i; + size_t samples_wo_wrap, n, i; - src = audio_stream_wrap(source, src); - dst = audio_stream_wrap(sink, dst); + src = cir_buf_wrap(src, source->buf_start, source->buf_end); + dst = cir_buf_wrap(dst, sink->buf_start, sink->buf_end); - src_samples_wo_wrap = audio_stream_samples_without_wrap_s16(source, src); - src_loops = SOF_DIV_ROUND_UP(src_samples_wo_wrap, num_src_channels); + samples_wo_wrap = cir_buf_samples_without_wrap_s16(src, source->buf_end); + n = SOF_DIV_ROUND_UP(samples_wo_wrap, num_src_channels); - dst_samples_wo_wrap = audio_stream_samples_without_wrap_s32(sink, dst); - dst_loops = SOF_DIV_ROUND_UP(dst_samples_wo_wrap, num_sink_channels); + samples_wo_wrap = cir_buf_samples_without_wrap_s32(dst, sink->buf_end); + n = MIN(n, SOF_DIV_ROUND_UP(samples_wo_wrap, num_sink_channels)); - n = MIN(src_loops, dst_loops); n = MIN(n, frames_left); for (i = 0; i < n; i++) { @@ -284,50 +281,49 @@ static inline int remap_c16_to_c32(const struct audio_stream *source, return source_samples; } -static inline int remap_c32_to_c16(const struct audio_stream *source, - struct audio_stream *sink, - uint32_t source_samples, uint32_t chmap, +static inline int remap_c32_to_c16(const struct cir_buf_source *source, + uint32_t num_src_channels, + struct cir_buf_sink *sink, + uint32_t num_sink_channels, + size_t source_samples, uint32_t chmap, int shift) { - int src_channel, sink_channel; - int num_src_channels = audio_stream_get_channels(source); - int num_sink_channels = audio_stream_get_channels(sink); - int frames = source_samples / num_src_channels; + uint32_t src_channel, sink_channel; + size_t frames = source_samples / num_src_channels; for (sink_channel = 0; sink_channel < num_sink_channels; sink_channel++) { - int32_t *src; + const int32_t *src; int16_t *dst; - int frames_left; + size_t frames_left; src_channel = chmap & 0xf; chmap >>= 4; - if (src_channel == 0xf) { - mute_channel_c16(sink, sink_channel, frames); + /* 0xf means "mute"; also mute any out-of-range source channel so + * a crafted chmap nibble cannot index past the source frame. + */ + if (src_channel == 0xf || src_channel >= num_src_channels) { + mute_channel_c16(sink, num_sink_channels, sink_channel, frames); continue; } - assert(src_channel < num_src_channels); - - src = (int32_t *)audio_stream_get_rptr(source) + src_channel; - dst = (int16_t *)audio_stream_get_wptr(sink) + sink_channel; + src = (const int32_t *)source->ptr + src_channel; + dst = (int16_t *)sink->ptr + sink_channel; frames_left = frames; while (frames_left) { - int src_samples_wo_wrap, dst_samples_wo_wrap; - int src_loops, dst_loops, n, i; + size_t samples_wo_wrap, n, i; - src = audio_stream_wrap(source, src); - dst = audio_stream_wrap(sink, dst); + src = cir_buf_wrap(src, source->buf_start, source->buf_end); + dst = cir_buf_wrap(dst, sink->buf_start, sink->buf_end); - src_samples_wo_wrap = audio_stream_samples_without_wrap_s32(source, src); - src_loops = SOF_DIV_ROUND_UP(src_samples_wo_wrap, num_src_channels); + samples_wo_wrap = cir_buf_samples_without_wrap_s32(src, source->buf_end); + n = SOF_DIV_ROUND_UP(samples_wo_wrap, num_src_channels); - dst_samples_wo_wrap = audio_stream_samples_without_wrap_s16(sink, dst); - dst_loops = SOF_DIV_ROUND_UP(dst_samples_wo_wrap, num_sink_channels); + samples_wo_wrap = cir_buf_samples_without_wrap_s16(dst, sink->buf_end); + n = MIN(n, SOF_DIV_ROUND_UP(samples_wo_wrap, num_sink_channels)); - n = MIN(src_loops, dst_loops); n = MIN(n, frames_left); for (i = 0; i < n; i++) { @@ -343,81 +339,96 @@ static inline int remap_c32_to_c16(const struct audio_stream *source, return source_samples; } -static int remap_c32(const struct audio_stream *source, uint32_t dummy1, - struct audio_stream *sink, uint32_t dummy2, - uint32_t source_samples, uint32_t chmap) +static int remap_c32(const struct cir_buf_source *source, uint32_t src_channels, + struct cir_buf_sink *sink, uint32_t sink_channels, + size_t source_samples, uint32_t chmap) { - return remap_c32_left_shift(source, sink, source_samples, chmap, 0); + return remap_c32_left_shift(source, src_channels, sink, sink_channels, + source_samples, chmap, 0); } -static int remap_c32_to_c16_right_shift_16(const struct audio_stream *source, uint32_t dummy1, - struct audio_stream *sink, uint32_t dummy2, - uint32_t source_samples, uint32_t chmap) +static int remap_c32_to_c16_right_shift_16(const struct cir_buf_source *source, + uint32_t src_channels, struct cir_buf_sink *sink, + uint32_t sink_channels, + size_t source_samples, uint32_t chmap) { - return remap_c32_to_c16(source, sink, source_samples, chmap, 16); + return remap_c32_to_c16(source, src_channels, sink, sink_channels, + source_samples, chmap, 16); } -static int remap_c16_to_c32_left_shift_16(const struct audio_stream *source, uint32_t dummy1, - struct audio_stream *sink, uint32_t dummy2, - uint32_t source_samples, uint32_t chmap) +static int remap_c16_to_c32_left_shift_16(const struct cir_buf_source *source, + uint32_t src_channels, struct cir_buf_sink *sink, + uint32_t sink_channels, + size_t source_samples, uint32_t chmap) { - return remap_c16_to_c32(source, sink, source_samples, chmap, 16); + return remap_c16_to_c32(source, src_channels, sink, sink_channels, + source_samples, chmap, 16); } -static int remap_c32_to_c16_right_shift_8(const struct audio_stream *source, uint32_t dummy1, - struct audio_stream *sink, uint32_t dummy2, - uint32_t source_samples, uint32_t chmap) +static int remap_c32_to_c16_right_shift_8(const struct cir_buf_source *source, + uint32_t src_channels, struct cir_buf_sink *sink, + uint32_t sink_channels, + size_t source_samples, uint32_t chmap) { - return remap_c32_to_c16(source, sink, source_samples, chmap, 8); + return remap_c32_to_c16(source, src_channels, sink, sink_channels, + source_samples, chmap, 8); } -static int remap_c16_to_c32_left_shift_8(const struct audio_stream *source, uint32_t dummy1, - struct audio_stream *sink, uint32_t dummy2, - uint32_t source_samples, uint32_t chmap) +static int remap_c16_to_c32_left_shift_8(const struct cir_buf_source *source, + uint32_t src_channels, struct cir_buf_sink *sink, + uint32_t sink_channels, + size_t source_samples, uint32_t chmap) { - return remap_c16_to_c32(source, sink, source_samples, chmap, 8); + return remap_c16_to_c32(source, src_channels, sink, sink_channels, + source_samples, chmap, 8); } -static int remap_c32_right_shift_8(const struct audio_stream *source, uint32_t dummy1, - struct audio_stream *sink, uint32_t dummy2, - uint32_t source_samples, uint32_t chmap) +static int remap_c32_right_shift_8(const struct cir_buf_source *source, uint32_t src_channels, + struct cir_buf_sink *sink, uint32_t sink_channels, + size_t source_samples, uint32_t chmap) { - return remap_c32_right_shift(source, sink, source_samples, chmap, 8); + return remap_c32_right_shift(source, src_channels, sink, sink_channels, + source_samples, chmap, 8); } -static int remap_c32_left_shift_8(const struct audio_stream *source, uint32_t dummy1, - struct audio_stream *sink, uint32_t dummy2, - uint32_t source_samples, uint32_t chmap) +static int remap_c32_left_shift_8(const struct cir_buf_source *source, uint32_t src_channels, + struct cir_buf_sink *sink, uint32_t sink_channels, + size_t source_samples, uint32_t chmap) { - return remap_c32_left_shift(source, sink, source_samples, chmap, 8); + return remap_c32_left_shift(source, src_channels, sink, sink_channels, + source_samples, chmap, 8); } -static int remap_c32_right_shift_16(const struct audio_stream *source, uint32_t dummy1, - struct audio_stream *sink, uint32_t dummy2, - uint32_t source_samples, uint32_t chmap) +static int remap_c32_right_shift_16(const struct cir_buf_source *source, uint32_t src_channels, + struct cir_buf_sink *sink, uint32_t sink_channels, + size_t source_samples, uint32_t chmap) { - return remap_c32_right_shift(source, sink, source_samples, chmap, 16); + return remap_c32_right_shift(source, src_channels, sink, sink_channels, + source_samples, chmap, 16); } -static int remap_c32_left_shift_16(const struct audio_stream *source, uint32_t dummy1, - struct audio_stream *sink, uint32_t dummy2, - uint32_t source_samples, uint32_t chmap) +static int remap_c32_left_shift_16(const struct cir_buf_source *source, uint32_t src_channels, + struct cir_buf_sink *sink, uint32_t sink_channels, + size_t source_samples, uint32_t chmap) { - return remap_c32_left_shift(source, sink, source_samples, chmap, 16); + return remap_c32_left_shift(source, src_channels, sink, sink_channels, + source_samples, chmap, 16); } -static int remap_c32_to_c16_no_shift(const struct audio_stream *source, uint32_t dummy1, - struct audio_stream *sink, uint32_t dummy2, - uint32_t source_samples, uint32_t chmap) +static int remap_c32_to_c16_no_shift(const struct cir_buf_source *source, uint32_t src_channels, + struct cir_buf_sink *sink, uint32_t sink_channels, + size_t source_samples, uint32_t chmap) { - return remap_c32_to_c16(source, sink, source_samples, chmap, 0); + return remap_c32_to_c16(source, src_channels, sink, sink_channels, + source_samples, chmap, 0); } -static int remap_c16_to_c32_no_shift(const struct audio_stream *source, uint32_t dummy1, - struct audio_stream *sink, uint32_t dummy2, - uint32_t source_samples, uint32_t chmap) +static int remap_c16_to_c32_no_shift(const struct cir_buf_source *source, uint32_t src_channels, + struct cir_buf_sink *sink, uint32_t sink_channels, + size_t source_samples, uint32_t chmap) { - return remap_c16_to_c32(source, sink, source_samples, chmap, 0); + return remap_c16_to_c32(source, src_channels, sink, sink_channels, + source_samples, chmap, 0); } /* Unfortunately, all these nice "if"s were commented out to suppress diff --git a/src/audio/phase_vocoder/CMakeLists.txt b/src/audio/phase_vocoder/CMakeLists.txt new file mode 100644 index 000000000000..b632c50af6cb --- /dev/null +++ b/src/audio/phase_vocoder/CMakeLists.txt @@ -0,0 +1,15 @@ +# SPDX-License-Identifier: BSD-3-Clause + +if(CONFIG_COMP_PHASE_VOCODER STREQUAL "m" AND DEFINED CONFIG_LLEXT) + add_subdirectory(llext ${PROJECT_BINARY_DIR}/phase_vocoder_llext) + add_dependencies(app phase_vocoder) +else() + add_local_sources(sof phase_vocoder.c) + add_local_sources(sof phase_vocoder_setup.c) + add_local_sources(sof phase_vocoder_common.c) + add_local_sources(sof phase_vocoder-generic.c) + + if(CONFIG_IPC_MAJOR_4) + add_local_sources(sof phase_vocoder-ipc4.c) + endif() +endif() diff --git a/src/audio/phase_vocoder/Kconfig b/src/audio/phase_vocoder/Kconfig new file mode 100644 index 000000000000..a3eecfc8023f --- /dev/null +++ b/src/audio/phase_vocoder/Kconfig @@ -0,0 +1,14 @@ +# SPDX-License-Identifier: BSD-3-Clause + +config COMP_PHASE_VOCODER + tristate "Phase Vocoder component" + default m if LIBRARY_DEFAULT_MODULAR + select MATH_FFT + select MATH_32BIT_FFT + help + Select for phase_vocoder component. The component provides + render speed control in range 0.5-2.0x. The pitch is + preserved in audio waveform stretch or shorten. The module + is using a frequency domain algorithm in STFT domain to + interpolate magnitude and phase of output IFFT frames from + input FFT frames. diff --git a/src/audio/phase_vocoder/llext/CMakeLists.txt b/src/audio/phase_vocoder/llext/CMakeLists.txt new file mode 100644 index 000000000000..ffefa29c7080 --- /dev/null +++ b/src/audio/phase_vocoder/llext/CMakeLists.txt @@ -0,0 +1,11 @@ +# Copyright (c) 2026 Intel Corporation. +# SPDX-License-Identifier: Apache-2.0 + +sof_llext_build("phase_vocoder" + SOURCES ../phase_vocoder.c + ../phase_vocoder_setup.c + ../phase_vocoder_common.c + ../phase_vocoder-generic.c + ../phase_vocoder-ipc4.c + LIB openmodules +) diff --git a/src/audio/phase_vocoder/llext/llext.toml.h b/src/audio/phase_vocoder/llext/llext.toml.h new file mode 100644 index 000000000000..94cfd0d67d50 --- /dev/null +++ b/src/audio/phase_vocoder/llext/llext.toml.h @@ -0,0 +1,6 @@ +#include <tools/rimage/config/platform.toml> +#define LOAD_TYPE "2" +#include "../phase_vocoder.toml" + +[module] +count = __COUNTER__ diff --git a/src/audio/phase_vocoder/phase_vocoder-generic.c b/src/audio/phase_vocoder/phase_vocoder-generic.c new file mode 100644 index 000000000000..5cc382cb5b8b --- /dev/null +++ b/src/audio/phase_vocoder/phase_vocoder-generic.c @@ -0,0 +1,694 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// Copyright(c) 2026 Intel Corporation. + +#include <sof/audio/module_adapter/module/generic.h> +#include <sof/audio/component.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 <stdint.h> +#include "phase_vocoder.h" + +#if CONFIG_FORMAT_S32LE +/** + * phase_vocoder_source_s32() - Read S32_LE samples into the input ring buffer. + * @cd: Pointer to component private data. + * @source: Source for PCM samples data. + * @frames: Number of audio data frames to consume. + * + * Copies up to @frames frames of S32_LE PCM from @source into the per-channel + * input ring buffers. In mono-mix mode the channels are pre-summed using + * @cd->mono_mix_coef so that downstream STFT processing runs on a single + * channel. + * + * Return: Value zero for success, otherwise an error code. + */ +int phase_vocoder_source_s32(struct phase_vocoder_comp_data *cd, struct sof_source *source, + int frames) +{ + struct phase_vocoder_state *state = &cd->state; + struct phase_vocoder_buffer *ibuf; + int32_t const *x, *x_start, *x_end; + const int32_t mix_gain = cd->mono_mix_coef; + int64_t mix; + int frames_left; + int x_size; + int bytes; + int ret; + int n1; + int n2; + int n; + int i; + int j; + int stream_channels = cd->stream_channels; + int process_channels = cd->process_channels; + bool process_mono = cd->config->mono; + + ibuf = &state->ibuf[0]; + frames = MIN(frames, ibuf->s_free); + bytes = frames * cd->frame_bytes; + + /* 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; + + frames_left = frames; + while (frames_left) { + /* Find out samples to process before first wrap or end of data. */ + ibuf = &state->ibuf[0]; + n1 = (x_end - x) / stream_channels; + n2 = phase_vocoder_buffer_samples_without_wrap(ibuf, ibuf->w_ptr); + n = MIN(n1, n2); + n = MIN(n, frames_left); + if (process_mono) { + for (i = 0; i < n; i++) { + mix = 0; + for (j = 0; j < stream_channels; j++) + mix += Q_MULTSR_32X32((int64_t)mix_gain, *x++, 31, 31, 31); + + *ibuf->w_ptr++ = sat_int32(mix); + } + } else { + for (i = 0; i < n; i++) { + for (j = 0; j < stream_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 < process_channels; j++) { + ibuf = &state->ibuf[j]; + ibuf->w_ptr = phase_vocoder_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 < process_channels; j++) { + ibuf = &state->ibuf[j]; + ibuf->s_avail += frames; + ibuf->s_free -= frames; + } + + return 0; +} + +/** + * phase_vocoder_sink_s32() - Write processed samples out as S32_LE. + * @cd: Pointer to component private data. + * @sink: Sink for PCM samples data. + * @frames: Number of audio data frames to produce. + * + * Copies up to @frames frames from the per-channel output ring buffer to + * @sink in S32_LE format. In mono-mix mode the single processed channel is + * replicated to all output channels. The output ring buffer slots are + * cleared after read so the next overlap-add pass starts from zero. + * + * Return: Value zero for success, otherwise an error code. + */ +int phase_vocoder_sink_s32(struct phase_vocoder_comp_data *cd, struct sof_sink *sink, int frames) +{ + struct phase_vocoder_state *state = &cd->state; + struct phase_vocoder_buffer *obuf; + int32_t *y, *y_start, *y_end; + int frames_remain; + int bytes; + int y_size; + int ret; + int ch, n1, n, i; + int stream_channels = cd->stream_channels; + int process_channels = cd->process_channels; + bool process_mono = cd->config->mono; + + obuf = &state->obuf[0]; + frames = MIN(frames, obuf->s_avail); + if (!frames) + return 0; + + /* Get pointer to sink data in circular buffer */ + bytes = frames * cd->frame_bytes; + 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; + + frames_remain = frames; + while (frames_remain) { + /* Find out samples to process before first wrap or end of data. */ + obuf = &state->obuf[0]; + n1 = (y_end - y) / stream_channels; + n = phase_vocoder_buffer_samples_without_wrap(obuf, obuf->r_ptr); + n = MIN(n1, n); + n = MIN(n, frames_remain); + + if (process_mono) { + for (i = 0; i < n; i++) { + for (ch = 0; ch < stream_channels; ch++) + *y++ = *obuf->r_ptr; + + *obuf->r_ptr++ = 0; /* clear overlap add mix */ + } + } else { + for (i = 0; i < n; i++) { + for (ch = 0; ch < stream_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 < process_channels; ch++) { + obuf = &state->obuf[ch]; + obuf->r_ptr = phase_vocoder_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 < process_channels; ch++) { + obuf = &state->obuf[ch]; + obuf->s_avail -= frames; + obuf->s_free += frames; + } + + return 0; +} +#endif /* CONFIG_FORMAT_S32LE */ + +#if CONFIG_FORMAT_S24LE +/** + * phase_vocoder_source_s24() - Read S24_4LE samples into the input ring buffer. + * @cd: Pointer to component private data. + * @source: Source for PCM samples data. + * @frames: Number of audio data frames to consume. + * + * Copies up to @frames frames of S24_4LE PCM from @source into the per-channel + * input ring buffers, left-shifting each 24-bit sample by 8 to align to Q1.31 + * for STFT processing. In mono-mix mode the channels are pre-summed using + * @cd->mono_mix_coef so that downstream STFT processing runs on a single + * channel. + * + * Return: Value zero for success, otherwise an error code. + */ +int phase_vocoder_source_s24(struct phase_vocoder_comp_data *cd, struct sof_source *source, + int frames) +{ + struct phase_vocoder_state *state = &cd->state; + struct phase_vocoder_buffer *ibuf; + int32_t const *x, *x_start, *x_end; + const int32_t mix_gain = cd->mono_mix_coef; + int64_t mix; + int32_t tmp; + int frames_left; + int x_size; + int bytes; + int ret; + int n1; + int n2; + int n; + int i; + int j; + int stream_channels = cd->stream_channels; + int process_channels = cd->process_channels; + bool process_mono = cd->config->mono; + + ibuf = &state->ibuf[0]; + frames = MIN(frames, ibuf->s_free); + bytes = frames * cd->frame_bytes; + + /* 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; + + frames_left = frames; + while (frames_left) { + /* Find out samples to process before first wrap or end of data. */ + ibuf = &state->ibuf[0]; + n1 = (x_end - x) / stream_channels; + n2 = phase_vocoder_buffer_samples_without_wrap(ibuf, ibuf->w_ptr); + n = MIN(n1, n2); + n = MIN(n, frames_left); + if (process_mono) { + for (i = 0; i < n; i++) { + mix = 0; + for (j = 0; j < stream_channels; j++) { + tmp = (int32_t)(((uint32_t)*x++) << 8); + mix += Q_MULTSR_32X32((int64_t)mix_gain, tmp, 31, 31, 31); + } + + *ibuf->w_ptr++ = sat_int32(mix); + } + } else { + for (i = 0; i < n; i++) { + for (j = 0; j < stream_channels; j++) { + ibuf = &state->ibuf[j]; + tmp = (int32_t)(((uint32_t)*x++) << 8); + *ibuf->w_ptr++ = tmp; + } + } + } + + /* One of the buffers needs a wrap (or end of data), so check for wrap */ + for (j = 0; j < process_channels; j++) { + ibuf = &state->ibuf[j]; + ibuf->w_ptr = phase_vocoder_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 < process_channels; j++) { + ibuf = &state->ibuf[j]; + ibuf->s_avail += frames; + ibuf->s_free -= frames; + } + + return 0; +} + +/** + * phase_vocoder_sink_s24() - Write processed samples out as S24_4LE. + * @cd: Pointer to component private data. + * @sink: Sink for PCM samples data. + * @frames: Number of audio data frames to produce. + * + * Copies up to @frames frames from the per-channel output ring buffer to + * @sink in S24_4LE format, right-shifting each Q1.31 sample by 8 to produce + * the 24-bit output. In mono-mix mode the single processed channel is + * replicated to all output channels. The output ring buffer slots are + * cleared after read so the next overlap-add pass starts from zero. + * + * Return: Value zero for success, otherwise an error code. + */ +int phase_vocoder_sink_s24(struct phase_vocoder_comp_data *cd, struct sof_sink *sink, int frames) +{ + struct phase_vocoder_state *state = &cd->state; + struct phase_vocoder_buffer *obuf; + int32_t *y, *y_start, *y_end; + int frames_remain; + int bytes; + int y_size; + int ret; + int ch, n1, n, i; + int stream_channels = cd->stream_channels; + int process_channels = cd->process_channels; + bool process_mono = cd->config->mono; + + obuf = &state->obuf[0]; + frames = MIN(frames, obuf->s_avail); + if (!frames) + return 0; + + /* Get pointer to sink data in circular buffer */ + bytes = frames * cd->frame_bytes; + 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; + + frames_remain = frames; + while (frames_remain) { + /* Find out samples to process before first wrap or end of data. */ + obuf = &state->obuf[0]; + n1 = (y_end - y) / stream_channels; + n = phase_vocoder_buffer_samples_without_wrap(obuf, obuf->r_ptr); + n = MIN(n1, n); + n = MIN(n, frames_remain); + + if (process_mono) { + for (i = 0; i < n; i++) { + for (ch = 0; ch < stream_channels; ch++) + *y++ = *obuf->r_ptr >> 8; + + *obuf->r_ptr++ = 0; /* clear overlap add mix */ + } + } else { + for (i = 0; i < n; i++) { + for (ch = 0; ch < stream_channels; ch++) { + obuf = &state->obuf[ch]; + *y++ = *obuf->r_ptr >> 8; + *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 < process_channels; ch++) { + obuf = &state->obuf[ch]; + obuf->r_ptr = phase_vocoder_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 < process_channels; ch++) { + obuf = &state->obuf[ch]; + obuf->s_avail -= frames; + obuf->s_free += frames; + } + + return 0; +} +#endif /* CONFIG_FORMAT_S24LE */ + +#if CONFIG_FORMAT_S16LE +/** + * phase_vocoder_source_s16() - Read S16_LE samples into the input ring buffer. + * @cd: Pointer to component private data. + * @source: Source for PCM samples data. + * @frames: Number of audio data frames to consume. + * + * Copies up to @frames frames of S16_LE PCM from @source into the per-channel + * input ring buffers, sign-extending each sample to S32 left-aligned. In + * mono-mix mode the channels are pre-summed using @cd->mono_mix_coef. + * + * Return: Value zero for success, otherwise an error code. + */ +int phase_vocoder_source_s16(struct phase_vocoder_comp_data *cd, struct sof_source *source, + int frames) +{ + struct phase_vocoder_state *state = &cd->state; + struct phase_vocoder_buffer *ibuf; + const int32_t mix_gain = cd->mono_mix_coef; + int64_t mix; + int16_t const *x, *x_start, *x_end; + int16_t in; + int frames_left; + int x_size; + int bytes; + int ret; + int n1; + int n2; + int n; + int i; + int j; + int stream_channels = cd->stream_channels; + int process_channels = cd->process_channels; + bool process_mono = cd->config->mono; + + ibuf = &state->ibuf[0]; + frames = MIN(frames, ibuf->s_free); + bytes = frames * cd->frame_bytes; + frames_left = frames; + + /* Get pointer to source data in circular buffer, get buffer start and size to + * check for wrap. The size in bytes is converted to number of s16 samples to + * control the samples process loop. If the number of bytes requested is not + * possible, an error is returned. + */ + ret = source_get_data_s16(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) / stream_channels; + n2 = phase_vocoder_buffer_samples_without_wrap(ibuf, ibuf->w_ptr); + n = MIN(n1, n2); + n = MIN(n, frames_left); + if (process_mono) { + for (i = 0; i < n; i++) { + mix = 0; + for (j = 0; j < stream_channels; j++) + mix += Q_MULTSR_32X32((int64_t)mix_gain, *x++, 31, 15, 31); + + *ibuf->w_ptr++ = sat_int32(mix); + } + } else { + for (i = 0; i < n; i++) { + for (j = 0; j < stream_channels; j++) { + ibuf = &state->ibuf[j]; + in = *x++; + *ibuf->w_ptr++ = (int32_t)in << 16; + } + } + } + + /* One of the buffers needs a wrap (or end of data), so check for wrap */ + for (j = 0; j < process_channels; j++) { + ibuf = &state->ibuf[j]; + ibuf->w_ptr = phase_vocoder_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 < process_channels; j++) { + ibuf = &state->ibuf[j]; + ibuf->s_avail += frames; + ibuf->s_free -= frames; + } + return 0; +} + +/** + * phase_vocoder_sink_s16() - Write processed samples out as S16_LE. + * @cd: Pointer to component private data. + * @sink: Sink for PCM samples data. + * @frames: Number of audio data frames to produce. + * + * Copies up to @frames frames from the per-channel output ring buffer to + * @sink in S16_LE format with rounding shift from Q1.31 down to Q1.15. In + * mono-mix mode the single processed channel is replicated to all output + * channels. The output ring buffer slots are cleared after read. + * + * Return: Value zero for success, otherwise an error code. + */ +int phase_vocoder_sink_s16(struct phase_vocoder_comp_data *cd, struct sof_sink *sink, int frames) +{ + struct phase_vocoder_state *state = &cd->state; + struct phase_vocoder_buffer *obuf; + int16_t sample; + int16_t *y, *y_start, *y_end; + int frames_remain; + int y_size; + int bytes; + int ret; + int ch, n1, n, i; + int stream_channels = cd->stream_channels; + int process_channels = cd->process_channels; + bool process_mono = cd->config->mono; + + obuf = &state->obuf[0]; + frames = MIN(frames, obuf->s_avail); + if (!frames) + return 0; + + /* Get pointer to sink data in circular buffer */ + bytes = frames * cd->frame_bytes; + frames_remain = frames; + 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 (frames_remain) { + /* Find out samples to process before first wrap or end of data. */ + obuf = &state->obuf[0]; + n1 = (y_end - y) / stream_channels; + n = phase_vocoder_buffer_samples_without_wrap(obuf, obuf->r_ptr); + n = MIN(n1, n); + n = MIN(n, frames_remain); + + if (process_mono) { + for (i = 0; i < n; i++) { + sample = sat_int16(Q_SHIFT_RND(*obuf->r_ptr, 31, 15)); + for (ch = 0; ch < stream_channels; ch++) + *y++ = sample; + + *obuf->r_ptr++ = 0; /* clear overlap add mix */ + } + } else { + for (i = 0; i < n; i++) { + for (ch = 0; ch < stream_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 */ + } + } + } + + /* One of the buffers needs a wrap (or end of data), so check for wrap */ + for (ch = 0; ch < process_channels; ch++) { + obuf = &state->obuf[ch]; + obuf->r_ptr = phase_vocoder_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 < process_channels; ch++) { + obuf = &state->obuf[ch]; + obuf->s_avail -= frames; + obuf->s_free += frames; + } + + return 0; +} +#endif /* CONFIG_FORMAT_S16LE */ + +void phase_vocoder_fill_fft_buffer(struct phase_vocoder_state *state, int ch) +{ + struct phase_vocoder_buffer *ibuf = &state->ibuf[ch]; + struct phase_vocoder_fft *fft = &state->fft; + struct icomplex32 *fft_buf_ptr; + int32_t *prev_data = state->prev_data[ch]; + int32_t *r = ibuf->r_ptr; + const int prev_data_size = state->prev_data_size; + const int fft_hop_size = fft->fft_hop_size; + int samples_remain = fft_hop_size; + int j; + int n; + + /* Copy overlapped samples from state buffer. Imaginary part of input + * remains zero. + */ + fft_buf_ptr = fft->fft_buf; + for (j = 0; j < prev_data_size; j++) { + *fft_buf_ptr = (struct icomplex32){.real = prev_data[j], .imag = 0}; + fft_buf_ptr++; + } + + /* Copy hop size of new data from circular buffer */ + fft_buf_ptr = &fft->fft_buf[prev_data_size]; + while (samples_remain) { + n = phase_vocoder_buffer_samples_without_wrap(ibuf, r); + n = MIN(n, samples_remain); + for (j = 0; j < n; j++) { + *fft_buf_ptr = (struct icomplex32){.real = *r++, .imag = 0}; + fft_buf_ptr++; + } + r = phase_vocoder_buffer_wrap(ibuf, r); + samples_remain -= n; + } + + ibuf->r_ptr = r; + ibuf->s_avail -= fft_hop_size; + ibuf->s_free += fft_hop_size; + + /* Copy for next time data back to input data overlap buffer */ + fft_buf_ptr = &fft->fft_buf[fft_hop_size]; + for (j = 0; j < prev_data_size; j++) + *prev_data++ = fft_buf_ptr++->real; +} + +int phase_vocoder_overlap_add_ifft_buffer(struct phase_vocoder_state *state, int ch) +{ + struct phase_vocoder_buffer *obuf = &state->obuf[ch]; + struct phase_vocoder_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; + + if (obuf->s_free < samples_remain) + return -EINVAL; + + while (samples_remain) { + n = phase_vocoder_buffer_samples_without_wrap(obuf, w); + n = MIN(samples_remain, 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 = phase_vocoder_buffer_wrap(obuf, w); + samples_remain -= n; + } + + w = obuf->w_ptr + fft->fft_hop_size; + obuf->w_ptr = phase_vocoder_buffer_wrap(obuf, w); + obuf->s_avail += fft->fft_hop_size; + obuf->s_free -= fft->fft_hop_size; + return 0; +} + +void phase_vocoder_apply_window(struct phase_vocoder_state *state) +{ + struct phase_vocoder_fft *fft = &state->fft; + struct icomplex32 *fft_buf_ptr = fft->fft_buf; + const int32_t *window = state->window; + const int fft_size = fft->fft_size; + int i; + + for (i = 0; i < fft_size; i++) { + fft_buf_ptr->real = sat_int32(Q_MULTSR_32X32((int64_t)fft_buf_ptr->real, + window[i], 31, 31, 31)); + fft_buf_ptr++; + } +} diff --git a/src/audio/phase_vocoder/phase_vocoder-ipc4.c b/src/audio/phase_vocoder/phase_vocoder-ipc4.c new file mode 100644 index 000000000000..f28e7d5db833 --- /dev/null +++ b/src/audio/phase_vocoder/phase_vocoder-ipc4.c @@ -0,0 +1,89 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// Copyright(c) 2026 Intel Corporation. + +#include <sof/audio/module_adapter/module/generic.h> +#include <sof/audio/component.h> +#include "phase_vocoder.h" + +LOG_MODULE_DECLARE(phase_vocoder, CONFIG_SOF_LOG_LEVEL); + +/* IPC4 controls handler */ +__cold int phase_vocoder_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 sof_ipc4_control_msg_payload *ctl = (struct sof_ipc4_control_msg_payload *)fragment; + struct phase_vocoder_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: + if (ctl->id != 0) { + comp_err(dev, "Illegal switch control id = %d.", ctl->id); + return -EINVAL; + } + + if (ctl->num_elems != 1) { + comp_err(dev, "Illegal switch control num_elems = %d.", ctl->num_elems); + return -EINVAL; + } + + cd->enable = ctl->chanv[0].value; + comp_info(dev, "enable = %d", cd->enable); + return 0; + + case SOF_IPC4_ENUM_CONTROL_PARAM_ID: + if (ctl->id != 0) { + comp_err(dev, "Illegal enum control id = %d.", ctl->id); + return -EINVAL; + } + + if (ctl->num_elems != 1) { + comp_err(dev, "Illegal enum control num_elems = %d.", ctl->num_elems); + return -EINVAL; + } + + if (ctl->chanv[0].value > 15) { + comp_err(dev, "Illegal enum control value = %d.", ctl->chanv[0].value); + return -EINVAL; + } + + cd->speed_enum = ctl->chanv[0].value; + cd->speed_ctrl = PHASE_VOCODER_MIN_SPEED_Q29 + + Q_MULTSR_32X32((int64_t)cd->speed_enum, PHASE_VOCODER_SPEED_STEP_Q31, + 0, 31, 29); + + comp_info(dev, "speed_enum = %d, speed = %d", cd->speed_enum, cd->speed_ctrl); + return 0; + } + + if (fragment_size != sizeof(struct sof_phase_vocoder_config)) { + comp_err(dev, "Illegal fragment size %d, expect %d.", fragment_size, + sizeof(struct sof_phase_vocoder_config)); + return -EINVAL; + } + + if (!cd->config) { + cd->config = mod_alloc(mod, sizeof(struct sof_phase_vocoder_config)); + if (!cd->config) { + comp_err(dev, "Failed to allocate configuration."); + return -ENOMEM; + } + } + + memcpy_s(cd->config, sizeof(struct sof_phase_vocoder_config), fragment, fragment_size); + return 0; +} + +/* Not used in IPC4 systems, if IPC4 only component, omit .get_configuration set */ +__cold int phase_vocoder_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/phase_vocoder/phase_vocoder.c b/src/audio/phase_vocoder/phase_vocoder.c new file mode 100644 index 000000000000..b756dce72072 --- /dev/null +++ b/src/audio/phase_vocoder/phase_vocoder.c @@ -0,0 +1,291 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// Copyright(c) 2026 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 <ipc4/base-config.h> +#include <rtos/init.h> +#include "phase_vocoder.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(phase_vocoder); + +/* Creates logging data for the component */ +LOG_MODULE_REGISTER(phase_vocoder, CONFIG_SOF_LOG_LEVEL); + +/* Creates the component trace. Traces show in trace console the component + * info, warning, and error messages. + */ +DECLARE_TR_CTX(phase_vocoder_tr, SOF_UUID(phase_vocoder_uuid), LOG_LEVEL_INFO); + +#if STFT_DEBUG +FILE *stft_debug_fft_in_fh; +FILE *stft_debug_fft_out_fh; +FILE *stft_debug_ifft_out_fh; +#endif + +static void phase_vocoder_reset_parameters(struct processing_module *mod) +{ + struct phase_vocoder_comp_data *cd = module_get_private_data(mod); + struct sof_phase_vocoder_config *config = cd->config; + + memset(cd, 0, sizeof(*cd)); + cd->config = config; + cd->enable = false; + cd->speed_ctrl = PHASE_VOCODER_SPEED_NORMAL; + + /* Sync state.speed */ + phase_vocoder_reset_for_new_speed(cd); +} + +/** + * phase_vocoder_init() - Initialize the phase_vocoder 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 phase_vocoder_init(struct processing_module *mod) +{ + struct module_data *md = &mod->priv; + struct comp_dev *dev = mod->dev; + struct phase_vocoder_comp_data *cd; + + assert_can_be_cold(); + + comp_info(dev, "phase_vocoder_init()"); + + cd = mod_zalloc(mod, sizeof(*cd)); + if (!cd) + return -ENOMEM; + + md->private = cd; + phase_vocoder_reset_parameters(mod); + +#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) { + fclose(stft_debug_fft_in_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_in_fh); + fclose(stft_debug_fft_out_fh); + return -EINVAL; + } +#endif + + return 0; +} + +/** + * phase_vocoder_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 phase_vocoder_process(struct processing_module *mod, struct sof_source **sources, + int num_of_sources, struct sof_sink **sinks, int num_of_sinks) +{ + struct phase_vocoder_comp_data *cd = module_get_private_data(mod); + struct sof_source *source = sources[0]; /* One input */ + struct sof_sink *sink = sinks[0]; /* One output */ + int source_frames = source_get_data_frames_available(source); + int sink_frames = sink_get_free_frames(sink); + int frames; + int ret; + + if (cd->speed_ctrl != cd->state.speed) + phase_vocoder_reset_for_new_speed(cd); + + if (cd->enable) { + ret = cd->phase_vocoder_func(mod, source, sink, source_frames, sink_frames); + if (ret) + comp_err(mod->dev, "Failure, check the setup parameters."); + + return ret; + } + + /* Just copy from source to sink. */ + frames = MIN(source_frames, sink_frames); + source_to_sink_copy(source, sink, true, frames * cd->frame_bytes); + return 0; +} + +/** + * phase_vocoder_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 phase_vocoder_prepare(struct processing_module *mod, struct sof_source **sources, + int num_of_sources, struct sof_sink **sinks, int num_of_sinks) +{ + struct module_data *mod_priv = &mod->priv; + struct ipc4_base_module_cfg *base_cfg = &mod_priv->cfg.base_cfg; + struct phase_vocoder_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; + } + +#if CONFIG_IPC_MAJOR_4 + /* Push base_cfg.audio_fmt onto endpoints so valid_sample_fmt is not left at 0 (S16_LE). */ + ipc4_update_source_format(sources[0], &base_cfg->audio_fmt); + ipc4_update_sink_format(sinks[0], &base_cfg->audio_fmt); +#endif + + /* get source data format */ + cd->frame_bytes = source_get_frame_bytes(sources[0]); + cd->stream_channels = source_get_channels(sources[0]); + cd->sample_rate = source_get_rate(sources[0]); + + /* Note: dev->frames is zero, use ibs */ + cd->max_input_frames = base_cfg->ibs / cd->frame_bytes + PHASE_VOCODER_MAX_FRAMES_MARGIN; + cd->max_output_frames = base_cfg->obs / cd->frame_bytes + PHASE_VOCODER_MAX_FRAMES_MARGIN; + source_format = source_get_valid_fmt(sources[0]); + comp_info(dev, "source_format %d channels %d max_input_frames %d max_output_frames %d", + source_format, cd->stream_channels, cd->max_input_frames, cd->max_output_frames); + + ret = phase_vocoder_setup(mod); + if (ret < 0) { + comp_err(dev, "setup failed."); + return ret; + } + + cd->phase_vocoder_func = phase_vocoder_find_proc_func(source_format); + if (!cd->phase_vocoder_func) { + comp_err(dev, "No processing function found for format %d.", source_format); + return -EINVAL; + } + + return 0; +} + +/** + * phase_vocoder_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 phase_vocoder_reset(struct processing_module *mod) +{ + comp_dbg(mod->dev, "reset"); + + phase_vocoder_free_buffers(mod); + phase_vocoder_reset_parameters(mod); + return 0; +} + +/** + * phase_vocoder_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 phase_vocoder_free(struct processing_module *mod) +{ + struct phase_vocoder_comp_data *cd = module_get_private_data(mod); + + assert_can_be_cold(); + + comp_dbg(mod->dev, "free"); + mod_free(mod, cd->config); + 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 phase_vocoder_interface = { + .init = phase_vocoder_init, + .prepare = phase_vocoder_prepare, + .process = phase_vocoder_process, + .set_configuration = phase_vocoder_set_config, + .get_configuration = phase_vocoder_get_config, + .reset = phase_vocoder_reset, + .free = phase_vocoder_free}; + +/* This controls build of the module. If COMP_MODULE is selected in kconfig + * this is build as dynamically loadable module. + */ +#if CONFIG_COMP_PHASE_VOCODER_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("PHASEVOC", &phase_vocoder_interface, 1, + SOF_REG_UUID(phase_vocoder), 40); + +SOF_LLEXT_BUILDINFO; + +#else + +DECLARE_MODULE_ADAPTER(phase_vocoder_interface, phase_vocoder_uuid, phase_vocoder_tr); +SOF_MODULE_INIT(phase_vocoder, sys_comp_module_phase_vocoder_interface_init); + +#endif diff --git a/src/audio/phase_vocoder/phase_vocoder.h b/src/audio/phase_vocoder/phase_vocoder.h new file mode 100644 index 000000000000..fbf95fdeeeec --- /dev/null +++ b/src/audio/phase_vocoder/phase_vocoder.h @@ -0,0 +1,334 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * + * Copyright(c) 2026 Intel Corporation. + * + */ +#ifndef __SOF_AUDIO_PHASE_VOCODER_H__ +#define __SOF_AUDIO_PHASE_VOCODER_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 PHASE_VOCODER_MAX_FRAMES_MARGIN 0 /* Adds to buffer size */ +#define PHASE_VOCODER_MIN_SPEED_Q29 Q_CONVERT_FLOAT(0.5, 29) /* Min. speed is 0.5 */ +#define PHASE_VOCODER_MAX_SPEED_Q29 Q_CONVERT_FLOAT(2.0, 29) /* Max. speed is 2.0 */ +#define PHASE_VOCODER_SPEED_STEP_Q31 Q_CONVERT_FLOAT((2 - 0.5) / 15, 31) /* Steps for enum ctrl */ +#define PHASE_VOCODER_SPEED_NORMAL Q_CONVERT_FLOAT(1.0, 29) /* Default to speed 1 */ +#define PHASE_VOCODER_ONE_Q29 Q_CONVERT_FLOAT(1.0, 29) /* One as Q29 */ +#define PHASE_VOCODER_HALF_Q29 Q_CONVERT_FLOAT(0.5, 29) /* 0.5 as Q29 */ + +#define PHASE_VOCODER_PI_Q28 843314857 /* int32(pi * 2^28), Q28 */ +#define PHASE_VOCODER_TWO_PI_Q28 1686629713 /* int32(2 * pi * 2^28), Q28 */ + +#define PHASE_VOCODER_PI_Q27 421657428 /* int32(pi * 2^27) */ +#define PHASE_VOCODER_TWO_PI_Q27 843314857 /* int32(2 * pi * 2^27) */ + +enum sof_phase_vocoder_fft_window_type { + STFT_RECTANGULAR_WINDOW = 0, + STFT_BLACKMAN_WINDOW = 1, + STFT_HAMMING_WINDOW = 2, + STFT_HANN_WINDOW = 3, +}; + +/** + * struct sof_phase_vocoder_config - IPC configuration blob for phase vocoder + * @size: Size of this struct in bytes + * @reserved: Reserved for future use + * @sample_frequency: Sample rate in Hz, e.g. 16000 + * @window_gain_comp: Q1.31 gain for IFFT + * @reserved_32: Reserved for future use + * @mono: Set to 1 for mono, zero for all channels + * @frame_length: Frame length in samples, e.g. 400 for 25 ms @ 16 kHz + * @frame_shift: Frame shift in samples, e.g. 160 for 10 ms @ 16 kHz + * @reserved_16: Reserved for future use + * @reserved_pad: Reserved for future use, kept for ABI compatibility + * @window: Window type, use RECTANGULAR_WINDOW, etc. + */ +struct sof_phase_vocoder_config { + uint32_t size; + uint32_t reserved[8]; + int32_t sample_frequency; + int32_t window_gain_comp; + int32_t reserved_32; + int16_t mono; + int16_t frame_length; + int16_t frame_shift; + int16_t reserved_16; + int32_t reserved_pad; + enum sof_phase_vocoder_fft_window_type window; +} __attribute__((packed)); + +/** + * struct phase_vocoder_buffer - Circular buffer for phase vocoder audio data + * @addr: Start address of the buffer + * @end_addr: End address of the buffer + * @r_ptr: Read pointer + * @w_ptr: Write pointer + * @s_avail: Available samples count + * @s_free: Free samples count + * @s_length: Buffer length in samples for wrap + */ +struct phase_vocoder_buffer { + int32_t *addr; + int32_t *end_addr; + int32_t *r_ptr; + int32_t *w_ptr; + int s_avail; + int s_free; + int s_length; +}; + +/** + * struct phase_vocoder_fft - FFT processing state + * @fft_buf: FFT input buffer, size is fft_size + * @fft_out: FFT output buffer, size is fft_size + * @fft_plan: FFT plan instance + * @ifft_plan: Inverse FFT plan instance + * @fft_size: FFT length in samples + * @fft_hop_size: FFT hop size in samples + * @half_fft_size: Half of the FFT size + * @fft_buffer_size: FFT buffer size in bytes + */ +struct phase_vocoder_fft { + struct icomplex32 *fft_buf; + struct icomplex32 *fft_out; + struct fft_plan *fft_plan; + struct fft_plan *ifft_plan; + int fft_size; + int fft_hop_size; + int half_fft_size; + size_t fft_buffer_size; +}; + +/** + * struct phase_vocoder_polar - Polar domain processing data + * @polar: Current polar representation per channel + * @polar_prev: Previous polar representation per channel + * @polar_tmp: Temporary polar buffer + * @polar_buffer: Base address of the joint polar/angle/output-phase allocation + * @angle_delta_prev: Previous angle delta per channel + * @angle_delta: Current angle delta per channel + * @output_phase: Output phase per channel + */ +struct phase_vocoder_polar { + struct ipolar32 *polar[PLATFORM_MAX_CHANNELS]; + struct ipolar32 *polar_prev[PLATFORM_MAX_CHANNELS]; + struct ipolar32 *polar_tmp; + int32_t *polar_buffer; + int32_t *angle_delta_prev[PLATFORM_MAX_CHANNELS]; + int32_t *angle_delta[PLATFORM_MAX_CHANNELS]; + int32_t *output_phase[PLATFORM_MAX_CHANNELS]; +}; + +/** + * struct phase_vocoder_state - Phase vocoder algorithm state + * @ibuf: Circular buffers for input data, per channel + * @obuf: Circular buffers for output data, per channel + * @fft: FFT instance, common for all channels + * @polar: Processing state in polar domain + * @phase_vocoder_polar_bytes: Size in bytes of the joint polar allocation + * @prev_data: Previous frame data per channel, size is prev_data_size + * @buffers: Pointer to allocated memory for all buffers + * @window: Window function coefficients, size is fft_size + * @num_input_fft_to_use: Number of input FFTs to use for processing + * @num_input_fft: Total input FFTs count + * @num_output_ifft: Total output IFFTs count + * @gain_comp: Gain to compensate window gain + * @interpolate_fraction: Q3.29 interpolation coefficient + * @speed: Q3.29 actual render speed + * @prev_data_size: Size of previous data buffer + * @first_output_ifft_done: True after first output IFFT is completed + */ +struct phase_vocoder_state { + struct phase_vocoder_buffer ibuf[PLATFORM_MAX_CHANNELS]; + struct phase_vocoder_buffer obuf[PLATFORM_MAX_CHANNELS]; + struct phase_vocoder_fft fft; + struct phase_vocoder_polar polar; + size_t phase_vocoder_polar_bytes; + int32_t *prev_data[PLATFORM_MAX_CHANNELS]; + int32_t *buffers; + int32_t *window; + int32_t num_input_fft_to_use; + int32_t num_input_fft; + int32_t num_output_ifft; + int32_t gain_comp; + int32_t interpolate_fraction; + int32_t speed; + int prev_data_size; + bool first_output_ifft_done; +}; + +/** + * typedef phase_vocoder_func - Function pointer for process function + * @mod: Pointer to module data. + * @source: Source for PCM samples data. + * @sink: Sink for PCM samples data. + * @source_frames: Number of source audio data frames to process. + * @sink_frames: Number of sink audio data frames to produce. + */ +typedef int (*phase_vocoder_func)(const struct processing_module *mod, struct sof_source *source, + struct sof_sink *sink, uint32_t source_frames, + uint32_t sink_frames); + +/** + * struct phase_vocoder_comp_data - Phase vocoder component data + * @phase_vocoder_func: Pointer to used processing function. + * @state: Phase vocoder algorithm state. + * @config: Configuration blob for the module. + * @mono_mix_coef: Gain for channel for mixing, Q1.15 + * @sample_rate: Audio sample rate in Hz + * @speed_ctrl: Speed Q3.29, allowed range 0.5 to 2.0 + * @speed_enum: Speed control value as enum 0-15 + * @frame_bytes: Number of bytes in an audio frame. + * @max_input_frames: Maximum number of input frames per processing call + * @max_output_frames: Maximum number of output frames per processing call + * @stream_channels: Channels count to consume/produce + * @process_channels: Channels count to process. + * @enable: Control processing on/off, off is pass-through + */ +struct phase_vocoder_comp_data { + phase_vocoder_func phase_vocoder_func; + struct phase_vocoder_state state; + struct sof_phase_vocoder_config *config; + int32_t mono_mix_coef; + int32_t sample_rate; + int32_t speed_ctrl; + int32_t speed_enum; + size_t frame_bytes; + int max_input_frames; + int max_output_frames; + int stream_channels; + int process_channels; + bool enable; +}; + +static inline int phase_vocoder_buffer_samples_without_wrap(struct phase_vocoder_buffer *buffer, + int32_t *ptr) +{ + return buffer->end_addr - ptr; +} + +static inline int32_t *phase_vocoder_buffer_wrap(struct phase_vocoder_buffer *buffer, int32_t *ptr) +{ + if (ptr >= buffer->end_addr) + ptr -= buffer->s_length; + + return ptr; +} + +/** + * struct phase_vocoder_proc_fnmap - processing functions for frame formats + * @frame_fmt: Current frame format + * @phase_vocoder_proc_func: Function pointer for the suitable processing function + */ +struct phase_vocoder_proc_fnmap { + enum sof_ipc_frame frame_fmt; + phase_vocoder_func phase_vocoder_function; +}; + +/** + * phase_vocoder_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. + */ +phase_vocoder_func phase_vocoder_find_proc_func(enum sof_ipc_frame src_fmt); + +#if CONFIG_IPC_MAJOR_4 +/** + * phase_vocoder_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 phase_vocoder_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); +/** + * phase_vocoder_get_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 phase_vocoder_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 phase_vocoder_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 phase_vocoder_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 + +void phase_vocoder_apply_window(struct phase_vocoder_state *state); + +void phase_vocoder_fill_fft_buffer(struct phase_vocoder_state *state, int ch); + +void phase_vocoder_free_buffers(struct processing_module *mod); + +int phase_vocoder_overlap_add_ifft_buffer(struct phase_vocoder_state *state, int ch); + +void phase_vocoder_reset_for_new_speed(struct phase_vocoder_comp_data *cd); + +int phase_vocoder_setup(struct processing_module *mod); + +int phase_vocoder_sink_s16(struct phase_vocoder_comp_data *cd, struct sof_sink *sink, int frames); + +int phase_vocoder_sink_s24(struct phase_vocoder_comp_data *cd, struct sof_sink *sink, int frames); + +int phase_vocoder_sink_s32(struct phase_vocoder_comp_data *cd, struct sof_sink *sink, int frames); + +int phase_vocoder_source_s16(struct phase_vocoder_comp_data *cd, struct sof_source *source, + int frames); + +int phase_vocoder_source_s24(struct phase_vocoder_comp_data *cd, struct sof_source *source, + int frames); + +int phase_vocoder_source_s32(struct phase_vocoder_comp_data *cd, struct sof_source *source, + int frames); + +#endif // __SOF_AUDIO_PHASE_VOCODER_H__ diff --git a/src/audio/phase_vocoder/phase_vocoder.toml b/src/audio/phase_vocoder/phase_vocoder.toml new file mode 100644 index 000000000000..9ec249fe01a5 --- /dev/null +++ b/src/audio/phase_vocoder/phase_vocoder.toml @@ -0,0 +1,21 @@ +#ifndef LOAD_TYPE +#define LOAD_TYPE "0" +#endif + + REM # Template component module config + [[module.entry]] + name = "PHASEVOC" + uuid = UUIDREG_STR_PHASE_VOCODER + 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/phase_vocoder/phase_vocoder_common.c b/src/audio/phase_vocoder/phase_vocoder_common.c new file mode 100644 index 000000000000..e31bc0239819 --- /dev/null +++ b/src/audio/phase_vocoder/phase_vocoder_common.c @@ -0,0 +1,615 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// Copyright(c) 2026 Intel Corporation. + +#include <sof/audio/component.h> +#include <sof/audio/audio_stream.h> +#include <sof/math/auditory.h> +#include <sof/math/icomplex32.h> +#include <sof/math/matrix.h> +#include <sof/math/numbers.h> +#include <sof/math/sqrt.h> +#include <sof/math/trig.h> +#include <sof/math/window.h> +#include <sof/trace/trace.h> + +#include "phase_vocoder.h" + +#include <errno.h> +#include <stddef.h> +#include <stdint.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 + +LOG_MODULE_REGISTER(phase_vocoder_common, CONFIG_SOF_LOG_LEVEL); + +static int stft_get_num_ffts_avail(struct phase_vocoder_state *state, int channel) +{ + struct phase_vocoder_buffer *ibuf = &state->ibuf[channel]; + struct phase_vocoder_fft *fft = &state->fft; + + /* Wait for FFT hop size of new data */ + return ibuf->s_avail / fft->fft_hop_size; +} + +static void stft_do_fft(struct phase_vocoder_state *state, int ch) +{ + struct phase_vocoder_fft *fft = &state->fft; + + /* Copy data to FFT input buffer from overlap buffer and from new samples buffer */ + phase_vocoder_fill_fft_buffer(state, ch); + + /* Window function */ + phase_vocoder_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_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 int stft_do_ifft(struct phase_vocoder_state *state, int ch) +{ + struct phase_vocoder_fft *fft = &state->fft; + + /* Compute IFFT */ + fft_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 */ + phase_vocoder_apply_window(state); + + /* Copy to output buffer */ + return phase_vocoder_overlap_add_ifft_buffer(state, ch); +} + +/** + * stft_convert_to_polar() - Convert FFT output to polar format + * @fft: FFT state containing the FFT output buffer and parameters as Q1.31. + * @polar_data: Output buffer for polar data, size is half of FFT size. + * Magnitude is Q2.30 and phase is Q5.27. + */ +static void stft_convert_to_polar(struct phase_vocoder_fft *fft, struct ipolar32 *polar_data) +{ + int i; + + /* Get magnitude and phase, convert phase from Q3.29 to Q5.27. */ + for (i = 0; i < fft->half_fft_size; i++) { + sofm_icomplex32_to_polar(&fft->fft_out[i], &polar_data[i]); + polar_data[i].angle = Q_SHIFT_RND(polar_data[i].angle, 29, 27); + } +} + +/** + * stft_convert_to_complex() - Convert polar data back to complex format for IFFT + * @polar_data: Input polar data, size is half of FFT size. + * Magnitude is Q2.30 and phase is Q5.27. + * @fft: FFT state containing the output buffer where complex data will + * be stored and parameters as Q1.31. + */ +static void stft_convert_to_complex(struct ipolar32 *polar_data, struct phase_vocoder_fft *fft) +{ + int i; + + /* Convert phase from Q5.27 to Q3.29, and then to (re, im) complex format as Q1.31*/ + for (i = 0; i < fft->half_fft_size; i++) { + polar_data[i].angle = Q_SHIFT_LEFT(polar_data[i].angle, 27, 29); + sofm_ipolar32_to_complex(&polar_data[i], &fft->fft_out[i]); + } +} + +static void stft_apply_fft_symmetry(struct phase_vocoder_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; + } +} + +/** + * phase_vocoder_normalize_counters - Prevent int32 overflow in frame counters + * + * The interpolation fraction depends on (num_output_ifft * speed) mod 2^29. + * By subtracting a multiple of the fraction cycle period from num_output_ifft + * (and the corresponding integer amount from num_input_fft), the fraction is + * preserved exactly while keeping both counters small. + */ +static void phase_vocoder_normalize_counters(struct phase_vocoder_state *state, int32_t speed) +{ + int32_t g; + int32_t output_period; + int32_t delta_output; + int64_t delta_input_scaled; + + if (state->num_output_ifft < (1 << 28)) + return; + + /* The fraction cycle repeats every (2^29 / gcd(speed, 2^29)) output IFFTs */ + g = gcd(speed, 1 << 29); + output_period = (1 << 29) / g; + + /* Subtract the largest multiple of output_period that fits */ + delta_output = (state->num_output_ifft / output_period) * output_period; + if (!delta_output) + return; + + /* delta_output * speed is a multiple of 2^29 by construction, + * so the interpolation fraction is exactly preserved. + */ + delta_input_scaled = (int64_t)delta_output * speed; + + state->num_output_ifft -= delta_output; + state->num_input_fft -= (int32_t)(delta_input_scaled >> 29); +} + +static void phase_vocoder_interpolation_parameters(struct phase_vocoder_comp_data *cd) +{ + struct phase_vocoder_state *state = &cd->state; + int64_t input_frame_num_frac; + int32_t input_frame_num_floor; + + phase_vocoder_normalize_counters(state, cd->state.speed); + + input_frame_num_frac = (int64_t)state->num_output_ifft * cd->state.speed; /* Q31.29 */ + input_frame_num_floor = (int32_t)(input_frame_num_frac >> 29); /* Use floor, not round */ + state->num_input_fft_to_use = input_frame_num_floor + 1; + state->interpolate_fraction = input_frame_num_frac - ((int64_t)input_frame_num_floor << 29); +} + +#if 0 +static int32_t unwrap_angle(int32_t angle) +{ + if (angle > PHASE_VOCODER_PI_Q28) + return angle - PHASE_VOCODER_TWO_PI_Q28; + else if (angle < -PHASE_VOCODER_PI_Q28) + return angle + PHASE_VOCODER_TWO_PI_Q28; + else + return angle; +} +#endif + +static int32_t unwrap_angle_q27(int32_t angle) +{ + while (angle > PHASE_VOCODER_PI_Q27) + angle -= PHASE_VOCODER_TWO_PI_Q27; + + while (angle < -PHASE_VOCODER_PI_Q27) + angle += PHASE_VOCODER_TWO_PI_Q27; + + return angle; +} + +void phase_vocoder_reset_for_new_speed(struct phase_vocoder_comp_data *cd) +{ + struct phase_vocoder_state *state = &cd->state; + struct phase_vocoder_polar *polar = &state->polar; + struct phase_vocoder_fft *fft = &state->fft; + struct ipolar32 *polar_prev_ch; + int32_t *angle_delta_prev_ch; + int32_t *output_phase_ch; + int ch, i; + + state->speed = cd->speed_ctrl; + + /* Keep num_input_fft at 1, not 0, to avoid re-entering the first + * analysis FFT cold-start path. That path sets angle_delta to the + * absolute phase angle (not a delta between frames). For non-unity + * speeds, multiple IFFTs are produced per input FFT, and the + * absolute angle gets repeatedly accumulated into output_phase + * causing random inter-bin phase incoherence and gain variation. + * + * By setting num_input_fft = 1 the existing polar, angle_delta, + * and output_phase state from the last processing step is preserved. + * These contain proper phase deltas, so interpolation continues + * correctly at the new speed. + */ + state->num_input_fft = 1; + state->num_output_ifft = 0; + + /* Re-anchor synthesis phase so the first post-reset IFFT lands + * output_phase back on polar_prev.angle (the steady-state invariant). + * Between speed changes output_phase advances by one analysis-hop + * delta per output IFFT regardless of speed, so at speed != 1 it + * drifts away from the true polar angle; and each reset also adds + * one extra angle_delta_prev on the first no-consume IFFT. If not done, + * both accumulate across interactive speed changes as a per-bin + * phase offset that smears transients and dulls the sound. + */ + for (ch = 0; ch < cd->process_channels; ch++) { + polar_prev_ch = polar->polar_prev[ch]; + angle_delta_prev_ch = polar->angle_delta_prev[ch]; + output_phase_ch = polar->output_phase[ch]; + if (!polar_prev_ch || !angle_delta_prev_ch || !output_phase_ch) + continue; + + for (i = 0; i < fft->half_fft_size; i++) + output_phase_ch[i] = + unwrap_angle_q27(polar_prev_ch[i].angle - angle_delta_prev_ch[i]); + } +} + +static void copy_polar_angles(int32_t *angle_delta_ch, struct ipolar32 *polar_data_ch, + int num_angles) +{ + int i; + + for (i = 0; i < num_angles; i++) + angle_delta_ch[i] = polar_data_ch[i].angle; +} + +// TODO: Civilized input and output counters reset to prevent int32_t wrap +static int stft_do_fft_ifft(const struct processing_module *mod) +{ + struct phase_vocoder_comp_data *cd = module_get_private_data(mod); + struct phase_vocoder_state *state = &cd->state; + struct phase_vocoder_polar *polar = &state->polar; + struct phase_vocoder_fft *fft = &state->fft; + struct ipolar32 *polar_data_prev_ch; + struct ipolar32 *polar_data_ch; + int32_t *angle_delta_prev_ch; + int32_t *angle_delta_ch; + int32_t *output_phase_ch; + int32_t one_minus_frac; + int32_t frac; + int32_t p1, p2; + int32_t a; + const size_t polar_fft_half_bytes = sizeof(struct ipolar32) * fft->half_fft_size; + const size_t int32_fft_half_bytes = sizeof(int32_t) * fft->half_fft_size; + int num_fft; + int ret = 0; + int ch; + int i; + + num_fft = stft_get_num_ffts_avail(state, 0); + if (!num_fft) + return 0; + + /* First analysis FFT */ + if (!state->num_input_fft) { + for (ch = 0; ch < cd->process_channels; ch++) { + stft_do_fft(state, ch); + + /* Convert half-FFT to polar. Magnitude is Q2.30 and phase + * angle is Q5.27. + */ + polar_data_ch = polar->polar[ch]; /* struct ipolar32 */ + stft_convert_to_polar(&state->fft, polar_data_ch); + + /* Initialize output phase directly to the absolute phase + * of the first frame. This avoids the accumulation path + * which would double-count the initial phase when it + * later moves from angle_delta to angle_delta_prev. + */ + output_phase_ch = polar->output_phase[ch]; + copy_polar_angles(output_phase_ch, polar_data_ch, fft->half_fft_size); + + /* Initialize prev polar to same data so magnitude + * interpolation works before second FFT is consumed. + * Leave angle_delta and angle_delta_prev at zero so the + * phase does not advance until real deltas are computed. + */ + memcpy(polar->polar_prev[ch], polar_data_ch, polar_fft_half_bytes); + } + state->num_input_fft++; + num_fft--; + } + + phase_vocoder_interpolation_parameters(cd); + while (state->num_input_fft < state->num_input_fft_to_use && num_fft > 0) { + for (ch = 0; ch < cd->process_channels; ch++) { + stft_do_fft(state, ch); + + /* Update previous polar data. + * Note: Not using memcpy_s() since this is hot algorithm code. + */ + polar_data_prev_ch = polar->polar_prev[ch]; + polar_data_ch = polar->polar[ch]; + memcpy(polar_data_prev_ch, polar_data_ch, polar_fft_half_bytes); + + /* Convert half-FFT to polar. Magnitude is Q2.30 and phase + * angle is Q5.27. + */ + stft_convert_to_polar(&state->fft, polar_data_ch); + + /* Update previous delta phase data */ + angle_delta_ch = polar->angle_delta[ch]; + angle_delta_prev_ch = polar->angle_delta_prev[ch]; + memcpy(angle_delta_prev_ch, angle_delta_ch, int32_fft_half_bytes); + + /* Calculate new delta phase */ + for (i = 0; i < fft->half_fft_size; i++) { + a = polar_data_ch[i].angle - polar_data_prev_ch[i].angle; + angle_delta_ch[i] = unwrap_angle_q27(a); + } + } + state->num_input_fft++; + num_fft--; + } + + if (state->num_input_fft < state->num_input_fft_to_use) + return 0; + + /* Interpolate IFFT frame */ + frac = state->interpolate_fraction; + one_minus_frac = PHASE_VOCODER_ONE_Q29 - frac; + + for (ch = 0; ch < cd->process_channels; ch++) { + polar_data_prev_ch = polar->polar_prev[ch]; + polar_data_ch = polar->polar[ch]; + angle_delta_ch = polar->angle_delta[ch]; + angle_delta_prev_ch = polar->angle_delta_prev[ch]; + output_phase_ch = polar->output_phase[ch]; + + for (i = 0; i < fft->half_fft_size; i++) { + p1 = Q_MULTSR_32X32((int64_t)one_minus_frac, + polar_data_prev_ch[i].magnitude, 29, 30, 30); + p2 = Q_MULTSR_32X32((int64_t)frac, polar_data_ch[i].magnitude, 29, 30, 30); + polar->polar_tmp[i].magnitude = p1 + p2; + + a = output_phase_ch[i]; + p1 = Q_MULTSR_32X32((int64_t)one_minus_frac, angle_delta_prev_ch[i], 29, 27, + 27); + p2 = Q_MULTSR_32X32((int64_t)frac, angle_delta_ch[i], 29, 27, 27); + a = output_phase_ch[i] + p1 + p2; + output_phase_ch[i] = unwrap_angle_q27(a); + polar->polar_tmp[i].angle = output_phase_ch[i]; + } + + /* Convert back to (re, im) complex, and fix upper part */ + stft_convert_to_complex(polar->polar_tmp, &state->fft); + stft_apply_fft_symmetry(&state->fft); + ret = stft_do_ifft(state, ch); + if (ret) { + comp_err(mod->dev, "IFFT failure, check output overlap-add buffer size"); + return ret; + } + } + + comp_dbg(mod->dev, "no = %d, ni = %d, frac = %d", state->num_output_ifft, + state->num_input_fft, frac); + state->num_output_ifft++; + state->first_output_ifft_done = true; + return 0; +} + +static int phase_vocoder_check_fft_run_need(struct phase_vocoder_comp_data *cd) +{ + return cd->state.obuf[0].s_avail < cd->state.fft.fft_hop_size; +} + +#if CONFIG_FORMAT_S32LE || CONFIG_FORMAT_S24LE +static int phase_vocoder_output_zeros_s32(struct phase_vocoder_comp_data *cd, struct sof_sink *sink, + int frames) +{ + int32_t *y, *y_start, *y_end; + int samples = frames * cd->stream_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; +} +#endif + +#if CONFIG_FORMAT_S32LE +static int phase_vocoder_s32(const struct processing_module *mod, struct sof_source *source, + struct sof_sink *sink, uint32_t source_frames, uint32_t sink_frames) +{ + struct phase_vocoder_comp_data *cd = module_get_private_data(mod); + int ret; + + if (phase_vocoder_check_fft_run_need(cd)) { + /* Get samples from source buffer */ + ret = phase_vocoder_source_s32(cd, source, source_frames); + if (ret) + return ret; + + /* Do STFT, processing and inverse STFT */ + ret = stft_do_fft_ifft(mod); + if (ret) + return ret; + } + + /* Get samples from source buffer */ + if (cd->state.first_output_ifft_done) + ret = phase_vocoder_sink_s32(cd, sink, sink_frames); + else + ret = phase_vocoder_output_zeros_s32(cd, sink, sink_frames); + + return ret; +} +#endif /* CONFIG_FORMAT_S32LE */ + +#if CONFIG_FORMAT_S24LE +static int phase_vocoder_s24(const struct processing_module *mod, struct sof_source *source, + struct sof_sink *sink, uint32_t source_frames, uint32_t sink_frames) +{ + struct phase_vocoder_comp_data *cd = module_get_private_data(mod); + int ret; + + if (phase_vocoder_check_fft_run_need(cd)) { + /* Get samples from source buffer */ + ret = phase_vocoder_source_s24(cd, source, source_frames); + if (ret) + return ret; + + /* Do STFT, processing and inverse STFT */ + ret = stft_do_fft_ifft(mod); + if (ret) + return ret; + } + + /* Get samples from source buffer */ + if (cd->state.first_output_ifft_done) + ret = phase_vocoder_sink_s24(cd, sink, sink_frames); + else + /* Use the s32 function */ + ret = phase_vocoder_output_zeros_s32(cd, sink, sink_frames); + + return ret; +} +#endif /* CONFIG_FORMAT_S24LE */ + +#if CONFIG_FORMAT_S16LE +static int phase_vocoder_output_zeros_s16(struct phase_vocoder_comp_data *cd, struct sof_sink *sink, + int frames) +{ + int16_t *y, *y_start, *y_end; + int samples = frames * cd->stream_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 phase_vocoder_s16(const struct processing_module *mod, struct sof_source *source, + struct sof_sink *sink, uint32_t source_frames, uint32_t sink_frames) +{ + struct phase_vocoder_comp_data *cd = module_get_private_data(mod); + int ret; + + if (phase_vocoder_check_fft_run_need(cd)) { + /* Get samples from source buffer */ + ret = phase_vocoder_source_s16(cd, source, source_frames); + if (ret) + return ret; + + /* Do STFT, processing and inverse STFT */ + ret = stft_do_fft_ifft(mod); + if (ret) + return ret; + } + + /* Get samples from source buffer */ + if (cd->state.first_output_ifft_done) + ret = phase_vocoder_sink_s16(cd, sink, sink_frames); + else + ret = phase_vocoder_output_zeros_s16(cd, sink, sink_frames); + + return ret; +} +#endif /* CONFIG_FORMAT_S16LE */ + +/* This struct array defines the used processing functions for + * the PCM formats. + */ +const struct phase_vocoder_proc_fnmap phase_vocoder_functions[] = { +#if CONFIG_FORMAT_S16LE + {SOF_IPC_FRAME_S16_LE, phase_vocoder_s16}, +#endif +#if CONFIG_FORMAT_S24LE + {SOF_IPC_FRAME_S24_4LE, phase_vocoder_s24}, +#endif +#if CONFIG_FORMAT_S32LE + {SOF_IPC_FRAME_S32_LE, phase_vocoder_s32}, +#endif +}; + +/** + * phase_vocoder_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. + */ +phase_vocoder_func phase_vocoder_find_proc_func(enum sof_ipc_frame src_fmt) +{ + int i; + + /* Find suitable processing function from map */ + for (i = 0; i < ARRAY_SIZE(phase_vocoder_functions); i++) + if (src_fmt == phase_vocoder_functions[i].frame_fmt) + return phase_vocoder_functions[i].phase_vocoder_function; + + return NULL; +} diff --git a/src/audio/phase_vocoder/phase_vocoder_setup.c b/src/audio/phase_vocoder/phase_vocoder_setup.c new file mode 100644 index 000000000000..c6baf5b8908a --- /dev/null +++ b/src/audio/phase_vocoder/phase_vocoder_setup.c @@ -0,0 +1,264 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// Copyright(c) 2026 Intel Corporation. + +#include <sof/audio/component.h> +#include <sof/audio/audio_stream.h> +#include <sof/math/auditory.h> +#include <sof/math/icomplex32.h> +#include <sof/math/trig.h> +#include <sof/math/window.h> +#include <sof/trace/trace.h> +#include "phase_vocoder.h" + +#include <errno.h> +#include <stddef.h> +#include <stdint.h> + +#define STFT_MAX_ALLOC_SIZE 65536 + +LOG_MODULE_REGISTER(phase_vocoder_setup, CONFIG_SOF_LOG_LEVEL); + +static void phase_vocoder_init_buffer(struct phase_vocoder_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 phase_vocoder_get_window(struct phase_vocoder_state *state, + enum sof_phase_vocoder_fft_window_type name) +{ + struct phase_vocoder_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; + } +} + +int phase_vocoder_setup(struct processing_module *mod) +{ + struct phase_vocoder_comp_data *cd = module_get_private_data(mod); + struct comp_dev *dev = mod->dev; + struct sof_phase_vocoder_config *config = cd->config; + struct phase_vocoder_state *state = &cd->state; + struct phase_vocoder_fft *fft = &state->fft; + struct phase_vocoder_polar *polar = &state->polar; + size_t sample_buffers_size; + size_t ibuf_size; + size_t obuf_size; + size_t prev_size; + int32_t *addr; + int channels; + int ret; + int i; + + comp_dbg(dev, "phase_vocoder_setup()"); + + /* Check size */ + if (config->size != sizeof(struct sof_phase_vocoder_config)) { + comp_err(dev, "Illegal configuration size %d.", config->size); + return -EINVAL; + } + + if (config->sample_frequency != cd->sample_rate) + comp_warn(dev, "Config sample_frequency does not match stream"); + + if (config->mono) { + cd->process_channels = 1; + if (cd->stream_channels > 1) + cd->mono_mix_coef = (int32_t)((1LL << 31) / cd->stream_channels); + else + cd->mono_mix_coef = INT32_MAX; /* Q1.31 1.0 */ + + comp_info(dev, "mono_mix_coef %d", cd->mono_mix_coef); + } else { + cd->process_channels = cd->stream_channels; + cd->mono_mix_coef = 0; + } + + fft->fft_size = config->frame_length; + fft->fft_hop_size = config->frame_shift; + fft->half_fft_size = (fft->fft_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 */ + prev_size = fft->fft_size - fft->fft_hop_size; + ibuf_size = fft->fft_hop_size + cd->max_input_frames; + obuf_size = fft->fft_size + fft->fft_hop_size; + state->prev_data_size = prev_size; + + /* Allocate buffer for input samples, overlap buffer, window */ + channels = cd->process_channels; + sample_buffers_size = + sizeof(int32_t) * (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 -EINVAL; + } + + addr = mod_balloc(mod, sample_buffers_size); + if (!addr) { + comp_err(dev, "Failed buffer allocate"); + ret = -ENOMEM; + goto exit; + } + + memset(addr, 0, sample_buffers_size); + state->buffers = addr; + for (i = 0; i < channels; i++) { + phase_vocoder_init_buffer(&state->ibuf[i], addr, ibuf_size); + addr += ibuf_size; + phase_vocoder_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_size * sizeof(struct icomplex32); + fft->fft_buf = mod_balloc(mod, fft->fft_buffer_size); + if (!fft->fft_buf) { + comp_err(dev, "Failed FFT buffer allocate"); + ret = -ENOMEM; + goto cleanup; + } + + fft->fft_out = mod_balloc(mod, fft->fft_buffer_size); + if (!fft->fft_out) { + comp_err(dev, "Failed FFT output allocate"); + ret = -ENOMEM; + goto cleanup; + } + + /* Setup FFT */ + fft->fft_plan = mod_fft_plan_new(mod, fft->fft_buf, fft->fft_out, fft->fft_size, 32); + if (!fft->fft_plan) { + comp_err(dev, "Failed FFT init"); + ret = -EINVAL; + goto cleanup; + } + + fft->ifft_plan = mod_fft_plan_new(mod, fft->fft_out, fft->fft_buf, fft->fft_size, 32); + if (!fft->ifft_plan) { + comp_err(dev, "Failed IFFT init"); + ret = -EINVAL; + goto cleanup; + } + + /* Setup window */ + ret = phase_vocoder_get_window(state, config->window); + if (ret < 0) { + comp_err(dev, "Failed Window function"); + goto cleanup; + } + + /* Need to compensate the window function gain */ + state->gain_comp = config->window_gain_comp; + + /* Allocate buffers for polar format data for magnitude and phase interpolation */ + state->phase_vocoder_polar_bytes = + channels * fft->half_fft_size * (2 * sizeof(struct ipolar32) + 3 * sizeof(int32_t)); + comp_info(dev, "polar buffers size %zu", state->phase_vocoder_polar_bytes); + addr = mod_balloc(mod, state->phase_vocoder_polar_bytes); + if (!addr) { + comp_err(dev, "Failed polar data buffer allocate"); + ret = -ENOMEM; + goto cleanup; + } + + memset(addr, 0, state->phase_vocoder_polar_bytes); + polar->polar_buffer = addr; + for (i = 0; i < channels; i++) { + polar->polar[i] = (struct ipolar32 *)addr; + addr = (int32_t *)((struct ipolar32 *)addr + fft->half_fft_size); + } + + for (i = 0; i < channels; i++) { + polar->polar_prev[i] = (struct ipolar32 *)addr; + addr = (int32_t *)((struct ipolar32 *)addr + fft->half_fft_size); + } + + for (i = 0; i < channels; i++) { + polar->angle_delta[i] = addr; + addr += fft->half_fft_size; + } + + for (i = 0; i < channels; i++) { + polar->angle_delta_prev[i] = addr; + addr += fft->half_fft_size; + } + + for (i = 0; i < channels; i++) { + polar->output_phase[i] = addr; + addr += fft->half_fft_size; + } + + /* Use FFT buffer as scratch */ + polar->polar_tmp = (struct ipolar32 *)fft->fft_out; + comp_dbg(dev, "phase_vocoder_setup(), done"); + return 0; + +cleanup: + phase_vocoder_free_buffers(mod); +exit: + return ret; +} + +void phase_vocoder_free_buffers(struct processing_module *mod) +{ + struct phase_vocoder_comp_data *cd = module_get_private_data(mod); + struct phase_vocoder_state *state = &cd->state; + struct phase_vocoder_fft *fft = &state->fft; + struct phase_vocoder_polar *polar = &state->polar; + + /* All free helpers tolerate NULL; clear pointers so a subsequent reset + * does not double-free if setup() failed mid-way. + */ + mod_fft_plan_free(mod, fft->ifft_plan); + fft->ifft_plan = NULL; + mod_fft_plan_free(mod, fft->fft_plan); + fft->fft_plan = NULL; + mod_free(mod, fft->fft_buf); + fft->fft_buf = NULL; + mod_free(mod, fft->fft_out); + fft->fft_out = NULL; + mod_free(mod, state->buffers); + state->buffers = NULL; + mod_free(mod, polar->polar_buffer); + polar->polar_buffer = NULL; +} diff --git a/src/audio/phase_vocoder/tune/phase_vocoder_s16.sh b/src/audio/phase_vocoder/tune/phase_vocoder_s16.sh new file mode 100755 index 000000000000..5cfa0ee441a5 --- /dev/null +++ b/src/audio/phase_vocoder/tune/phase_vocoder_s16.sh @@ -0,0 +1,48 @@ +#!/bin/bash +set -euo pipefail + +if [ $# -lt 2 ]; then + echo "Usage: $0 <input_file.wav/flac/etc.> <output_file.wav>" >&2 + exit 1 +fi + +: "${SOF_WORKSPACE:?SOF_WORKSPACE must be set}" + +CLIP_IN=$1 +WAV_OUT=$2 +TESTBENCH=$SOF_WORKSPACE/sof/tools/testbench/build_testbench/install/bin/sof-testbench4 +TPLG_DIR=$SOF_WORKSPACE/sof/tools/build_tools/topology/topology2/development +TPLG=sof-hda-benchmark-phase_vocoder16.tplg + +RAW_IN=$(mktemp --suffix=_in.raw) +RAW_OUT=$(mktemp --suffix=_out.raw) +CONTROL_SCRIPT_LONG=$(mktemp --suffix=_controls.sh) +trap 'rm -f "$RAW_IN" "$RAW_OUT" "$CONTROL_SCRIPT_LONG"' EXIT + +# Uncomment to run under valgrind: +# VALGRIND=(valgrind --leak-check=full --track-origins=yes) +VALGRIND=() +CONTROL_SCRIPT=(-s "$CONTROL_SCRIPT_LONG") + +# Generated script is consumed by the testbench's -s option, not executed +# directly by a shell. +{ + echo "#!/bin/sh" + echo "amixer -c0 cset name='Analog Playback Phase Vocoder speed' 0.5" + echo "amixer -c0 cset name='Analog Playback Phase Vocoder enable' on" + for s in 0.5 0.6 0.7 0.8 0.9 1.0 1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9 \ + 2.0 1.9 1.8 1.7 1.6 1.5 1.4 1.3 1.2 1.1 1.0; do + echo "sleep 1" + echo "amixer -c0 cset name='Analog Playback Phase Vocoder speed' $s" + done + echo "amixer -c0 cset name='Analog Playback Phase Vocoder enable' off" +} > "$CONTROL_SCRIPT_LONG" + +sox "$CLIP_IN" --encoding signed-integer -L -r 48000 -c 2 -b 16 "$RAW_IN" +"${VALGRIND[@]}" "$TESTBENCH" "${CONTROL_SCRIPT[@]}" \ + -r 48000 -c 2 -b S16_LE -p 1,2 \ + -t "$TPLG_DIR/$TPLG" -i "$RAW_IN" -o "$RAW_OUT" +sox --encoding signed-integer -L -r 48000 -c 2 -b 16 "$RAW_OUT" "$WAV_OUT" + +printf '\n' +file "$WAV_OUT" diff --git a/src/audio/phase_vocoder/tune/phase_vocoder_s32.sh b/src/audio/phase_vocoder/tune/phase_vocoder_s32.sh new file mode 100755 index 000000000000..26dbe89b3e1a --- /dev/null +++ b/src/audio/phase_vocoder/tune/phase_vocoder_s32.sh @@ -0,0 +1,48 @@ +#!/bin/bash +set -euo pipefail + +if [ $# -lt 2 ]; then + echo "Usage: $0 <input_file.wav/flac/etc.> <output_file.wav>" >&2 + exit 1 +fi + +: "${SOF_WORKSPACE:?SOF_WORKSPACE must be set}" + +CLIP_IN=$1 +WAV_OUT=$2 +TESTBENCH=$SOF_WORKSPACE/sof/tools/testbench/build_testbench/install/bin/sof-testbench4 +TPLG_DIR=$SOF_WORKSPACE/sof/tools/build_tools/topology/topology2/development +TPLG=sof-hda-benchmark-phase_vocoder32.tplg + +RAW_IN=$(mktemp --suffix=_in.raw) +RAW_OUT=$(mktemp --suffix=_out.raw) +CONTROL_SCRIPT_LONG=$(mktemp --suffix=_controls.sh) +trap 'rm -f "$RAW_IN" "$RAW_OUT" "$CONTROL_SCRIPT_LONG"' EXIT + +# Uncomment to run under valgrind: +# VALGRIND=(valgrind --leak-check=full --track-origins=yes) +VALGRIND=() +CONTROL_SCRIPT=(-s "$CONTROL_SCRIPT_LONG") + +# Generated script is consumed by the testbench's -s option, not executed +# directly by a shell. +{ + echo "#!/bin/sh" + echo "amixer -c0 cset name='Analog Playback Phase Vocoder speed' 0.5" + echo "amixer -c0 cset name='Analog Playback Phase Vocoder enable' on" + for s in 0.5 0.6 0.7 0.8 0.9 1.0 1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9 \ + 2.0 1.9 1.8 1.7 1.6 1.5 1.4 1.3 1.2 1.1 1.0; do + echo "sleep 1" + echo "amixer -c0 cset name='Analog Playback Phase Vocoder speed' $s" + done + echo "amixer -c0 cset name='Analog Playback Phase Vocoder enable' off" +} > "$CONTROL_SCRIPT_LONG" + +sox "$CLIP_IN" --encoding signed-integer -L -r 48000 -c 2 -b 32 "$RAW_IN" +"${VALGRIND[@]}" "$TESTBENCH" "${CONTROL_SCRIPT[@]}" \ + -r 48000 -c 2 -b S32_LE -p 1,2 \ + -t "$TPLG_DIR/$TPLG" -i "$RAW_IN" -o "$RAW_OUT" +sox --encoding signed-integer -L -r 48000 -c 2 -b 32 "$RAW_OUT" "$WAV_OUT" + +printf '\n' +file "$WAV_OUT" diff --git a/src/audio/phase_vocoder/tune/setup_phase_vocoder.m b/src/audio/phase_vocoder/tune/setup_phase_vocoder.m new file mode 100644 index 000000000000..0bc2ab4061d8 --- /dev/null +++ b/src/audio/phase_vocoder/tune/setup_phase_vocoder.m @@ -0,0 +1,180 @@ +% setup_phase_vocoder() +% +% Create binary configuration blob for Phase Vocoder component. + +% SPDX-License-Identifier: BSD-3-Clause +% +% Copyright (c) 2026, Intel Corporation. + +function setup_phase_vocoder(cfg) + + cfg.tools_path = '../../../../tools/'; + cfg.tplg_path = [cfg.tools_path 'topology/topology2/include/components/phase_vocoder/']; + cfg.common_path = [cfg.tools_path 'tune/common']; + cfg.tplg_ver = 2; + cfg.ipc_ver = 4; + cfg.mono = 0; + cfg.sample_frequency = 48000; + + cfg.frame_length = 256; % 5.3 ms + cfg.frame_shift = 128; % 2.7 ms + cfg.window_type = 'hann'; + cfg.tplg_fn = 'hann_256_128.conf'; + export_phase_vocoder_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_phase_vocoder_setup(cfg); + + cfg.frame_length = 512; % 10.7 ms + cfg.frame_shift = 256; % 5.3 ms + cfg.window_type = 'hann'; + cfg.tplg_fn = 'hann_512_256.conf'; + export_phase_vocoder_setup(cfg); + + % Default configuration, stereo + 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_phase_vocoder_setup(cfg); + + % Default configuration, force mono processing + cfg.mono = 1; + cfg.tplg_fn = 'hann_1024_256_mono.conf'; + export_phase_vocoder_setup(cfg); + +end + +function export_phase_vocoder_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, 'PHASE_VOCODER 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 PHASE_VOCODER 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.mono; [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); % reserved_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, "phase_vocoder_config", ... + "Exported PHASE_VOCODER configuration", ... + "cd src/audio/phase_vocoder/tune; octave setup_phase_vocoder.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; + 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/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..670df08e9383 100644 --- a/src/audio/pipeline/pipeline-graph.c +++ b/src/audio/pipeline/pipeline-graph.c @@ -7,10 +7,15 @@ #include <sof/audio/buffer.h> #include <sof/audio/component_ext.h> +#include <sof/audio/module_adapter/module/generic.h> #include <sof/audio/pipeline.h> #include <sof/ipc/msg.h> +#include <sof/schedule/ll_schedule_domain.h> +#include <sof/lib/vregion.h> #include <rtos/interrupt.h> #include <rtos/symbol.h> +#include <rtos/alloc.h> +#include <rtos/userspace_helper.h> #include <sof/lib/mm_heap.h> #include <sof/lib/uuid.h> #include <sof/compiler_attributes.h> @@ -40,10 +45,19 @@ DECLARE_TR_CTX(pipe_tr, SOF_UUID(pipe_uuid), LOG_LEVEL_INFO); /* lookup table to determine busy/free pipeline metadata objects */ struct pipeline_posn { bool posn_offset[PPL_POSN_OFFSETS]; /**< available offsets */ +#ifndef CONFIG_SOF_USERSPACE_LL struct k_spinlock lock; /**< lock mechanism */ +#endif }; /* the pipeline position lookup table */ -static SHARED_DATA struct pipeline_posn pipeline_posn_shared; +static APP_SYSUSER_BSS struct pipeline_posn pipeline_posn_shared; + +#ifdef CONFIG_SOF_USERSPACE_LL +/* Mutex pointer in user-accessible partition so user-space threads + * can read the pointer for syscalls. + */ +static APP_SYSUSER_BSS struct k_mutex *pipeline_posn_mutex; +#endif /** * \brief Retrieves pipeline position structure. @@ -51,9 +65,48 @@ static SHARED_DATA struct pipeline_posn pipeline_posn_shared; */ static inline struct pipeline_posn *pipeline_posn_get(void) { +#ifdef CONFIG_SOF_USERSPACE_LL + return &pipeline_posn_shared; +#else return sof_get()->pipeline_posn; +#endif +} + +/* + * Position table locking. User-space LL cannot use a spinlock (disabling + * interrupts is privileged), so it uses a mutex; the config split is kept + * here so the callers below stay identical for both configurations. + */ +#ifdef CONFIG_SOF_USERSPACE_LL +typedef int pipeline_posn_key_t; + +static inline pipeline_posn_key_t pipeline_posn_lock(struct pipeline_posn *posn) +{ + (void)posn; + k_mutex_lock(pipeline_posn_mutex, K_FOREVER); + return 0; } +static inline void pipeline_posn_unlock(struct pipeline_posn *posn, pipeline_posn_key_t key) +{ + (void)posn; + (void)key; + k_mutex_unlock(pipeline_posn_mutex); +} +#else +typedef k_spinlock_key_t pipeline_posn_key_t; + +static inline pipeline_posn_key_t pipeline_posn_lock(struct pipeline_posn *posn) +{ + return k_spin_lock(&posn->lock); +} + +static inline void pipeline_posn_unlock(struct pipeline_posn *posn, pipeline_posn_key_t key) +{ + k_spin_unlock(&posn->lock, key); +} +#endif + /** * \brief Retrieves first free pipeline position offset. * \param[in,out] posn_offset Pipeline position offset to be set. @@ -64,9 +117,7 @@ static inline int pipeline_posn_offset_get(uint32_t *posn_offset) struct pipeline_posn *pipeline_posn = pipeline_posn_get(); int ret = -EINVAL; uint32_t i; - k_spinlock_key_t key; - - key = k_spin_lock(&pipeline_posn->lock); + pipeline_posn_key_t key = pipeline_posn_lock(pipeline_posn); for (i = 0; i < PPL_POSN_OFFSETS; ++i) { if (!pipeline_posn->posn_offset[i]) { @@ -77,8 +128,7 @@ static inline int pipeline_posn_offset_get(uint32_t *posn_offset) } } - - k_spin_unlock(&pipeline_posn->lock, key); + pipeline_posn_unlock(pipeline_posn, key); return ret; } @@ -91,24 +141,38 @@ static inline void pipeline_posn_offset_put(uint32_t posn_offset) { struct pipeline_posn *pipeline_posn = pipeline_posn_get(); int i = posn_offset / sizeof(struct sof_ipc_stream_posn); - k_spinlock_key_t key; - - key = k_spin_lock(&pipeline_posn->lock); + pipeline_posn_key_t key = pipeline_posn_lock(pipeline_posn); pipeline_posn->posn_offset[i] = false; - k_spin_unlock(&pipeline_posn->lock, key); + pipeline_posn_unlock(pipeline_posn, key); } void pipeline_posn_init(struct sof *sof) { - sof->pipeline_posn = platform_shared_get(&pipeline_posn_shared, - sizeof(pipeline_posn_shared)); + sof->pipeline_posn = &pipeline_posn_shared; +#ifdef CONFIG_SOF_USERSPACE_LL + pipeline_posn_mutex = k_object_alloc(K_OBJ_MUTEX); + if (!pipeline_posn_mutex) { + pipe_cl_err("pipeline posn mutex alloc failed"); + k_panic(); + } + k_mutex_init(pipeline_posn_mutex); +#else k_spinlock_init(&sof->pipeline_posn->lock); +#endif } +#ifdef CONFIG_SOF_USERSPACE_LL +void pipeline_posn_grant_access(struct k_thread *thread) +{ + k_thread_access_grant(thread, pipeline_posn_mutex); +} +#endif + /* 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,24 +185,36 @@ 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; p->status = COMP_STATE_INIT; p->trigger.cmd = COMP_TRIGGER_NO_ACTION; + +#ifndef CONFIG_SOF_USERSPACE_LL + /* + * pipe_tr lives in the .trace_ctx section, which is not mapped into + * the sysuser partition, so it cannot be read from a user-mode thread. + * The copy is also unnecessary in that configuration: with Zephyr + * logging the pipe_*() macros use the global pipe_tr, not p->tctx. + */ ret = memcpy_s(&p->tctx, sizeof(struct tr_ctx), &pipe_tr, sizeof(struct tr_ctx)); if (ret < 0) { pipe_err(p, "failed to copy trace settings"); goto free; } +#endif ret = pipeline_posn_offset_get(&p->posn_offset); if (ret < 0) { @@ -160,7 +236,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 +249,52 @@ 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 +/* + * User-space LL: callers (IPC handlers) already hold the per-core LL + * lock via user_ll_lock_sched()/ll_block() while modifying pipeline + * connections, which provides mutual exclusion with the LL thread. No + * additional lock is taken here; instead assert that the lock is held. + */ +#define PPL_LOCK_DECLARE +#define PPL_LOCK(x) user_ll_assert_locked(x) +#define PPL_UNLOCK() +#else +/* + * Kernel-space LL. When modifying pipeline connections, block IRQs + * and prevent LL from running. No locking needed when iterating + * the pipeline in the LL thread. + */ +#define PPL_LOCK_DECLARE uint32_t flags +#define PPL_LOCK(x) 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; + int ret; + 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(buffer->core); comp_list = comp_buffer_list(comp, dir); - buffer_attach(buffer, comp_list, dir); + ret = buffer_attach(buffer, comp_list, dir); + if (ret < 0) { + comp_err(comp, "buffer %d already connected dir %d", + buf_get_id(buffer), dir); + PPL_UNLOCK(); + return ret; + } buffer_set_comp(buffer, comp, dir); - irq_local_enable(flags); + PPL_UNLOCK(); return 0; } @@ -198,26 +302,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(buffer->core); 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 @@ -225,19 +329,14 @@ int pipeline_free(struct pipeline *p) */ /* remove from any scheduling */ - if (p->pipe_task) { -#if !CONFIG_LIBRARY || UNIT_TEST - schedule_task_free(p->pipe_task); -#endif - rfree(p->pipe_task); - } + pipeline_comp_ll_task_free(p); ipc_msg_free(p->msg); 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 +350,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; } @@ -266,8 +365,14 @@ static int pipeline_comp_complete(struct comp_dev *current, * It will be calculated during module prepare operation * either by the module or to default value based on module's OBS */ - if (current->ipc_config.proc_domain == COMP_PROCESSING_DOMAIN_LL) + if (current->ipc_config.proc_domain == COMP_PROCESSING_DOMAIN_LL) { current->period = ppl_data->p->period; + } else { + struct processing_module *mod = comp_mod(current); + + if (mod && mod->priv.resources.alloc) + vregion_set_interim(mod->priv.resources.alloc->vreg); + } current->priority = ppl_data->p->priority; @@ -326,7 +431,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) { @@ -341,7 +446,7 @@ static int pipeline_comp_reset(struct comp_dev *current, * scheduled together, except for IPC4, where each pipeline receives * commands from the host separately */ - if (!is_single_ppl && IPC4_MOD_ID(current->ipc_config.id)) + if (!is_single_ppl && IS_ENABLED(CONFIG_IPC_MAJOR_4)) return 0; /* Propagate reset across pipelines only in the same direction @@ -465,8 +570,10 @@ struct comp_dev *pipeline_get_dai_comp(uint32_t pipeline_id, int dir) buffer = buffer_from_list(blist->next, dir); comp = buffer_get_comp(buffer, dir); - /* buffer_comp is in another pipeline and it is not complete */ - if (!comp->pipeline) + /* A half-connected buffer has no peer component on this side, or + * buffer_comp is in another pipeline and it is not complete. + */ + if (!comp || !comp->pipeline) return NULL; crt = ipc_get_ppl_comp(ipc, comp->pipeline->pipeline_id, dir); diff --git a/src/audio/pipeline/pipeline-params.c b/src/audio/pipeline/pipeline-params.c index f2aa30b978c1..ed32fc9e3b02 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,12 +262,12 @@ 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)) { /* ipc4 module is only prepared in its parent pipeline */ - if (IPC4_MOD_ID(current->ipc_config.id)) + if (IS_ENABLED(CONFIG_IPC_MAJOR_4)) return 0; /* Propagate prepare only to pipelines in the same direction */ diff --git a/src/audio/pipeline/pipeline-schedule.c b/src/audio/pipeline/pipeline-schedule.c index a13e95982ccc..b62b4eda6c0f 100644 --- a/src/audio/pipeline/pipeline-schedule.c +++ b/src/audio/pipeline/pipeline-schedule.c @@ -8,16 +8,19 @@ #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> #include <sof/schedule/ll_schedule.h> +#include <sof/schedule/ll_schedule_domain.h> #include <sof/schedule/dp_schedule.h> #include <sof/schedule/schedule.h> #include <sof/audio/module_adapter/module/generic.h> #include <rtos/task.h> #include <rtos/spinlock.h> #include <rtos/string.h> +#include <rtos/userspace_helper.h> #include <ipc/header.h> #include <ipc/stream.h> #include <ipc/topology.h> @@ -32,6 +35,16 @@ LOG_MODULE_DECLARE(pipe, CONFIG_SOF_LOG_LEVEL); SOF_DEFINE_REG_UUID(pipe_task); +#ifdef CONFIG_IPC_MAJOR_4 +SOF_DEFINE_REG_UUID(pipe_trigger_task); + +/* + * Track a delayed trigger so later trigger tasks cannot overtake + * it and change the pipeline order requested by the host. + */ +static APP_SYSUSER_BSS struct pipeline *delayed_trigger_owner[CONFIG_CORE_COUNT]; +#endif + #if CONFIG_ZEPHYR_DP_SCHEDULER SOF_DEFINE_REG_UUID(dp_task); @@ -139,7 +152,7 @@ static enum task_state pipeline_task_cmd(struct pipeline *p, err = SOF_TASK_STATE_RESCHEDULE; } else if (p->status == COMP_STATE_PAUSED) { /* reset the pipeline components for IPC4 after the STOP trigger */ - if (cmd == COMP_TRIGGER_STOP && IPC4_MOD_ID(host->ipc_config.id)) { + if (cmd == COMP_TRIGGER_STOP && IS_ENABLED(CONFIG_IPC_MAJOR_4)) { err = pipeline_reset(host->pipeline, host); if (err < 0) reply->error = err; @@ -158,7 +171,9 @@ static enum task_state pipeline_task_cmd(struct pipeline *p, return err; } -static enum task_state pipeline_task(void *arg) +#ifndef CONFIG_IPC_MAJOR_4 +/** Run IPC3 pipeline trigger and copy processing. */ +static enum task_state ipc3_pipeline_task(void *arg) { struct sof_ipc_reply reply = { .hdr.cmd = SOF_IPC_GLB_REPLY, @@ -167,7 +182,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,24 +247,145 @@ static enum task_state pipeline_task(void *arg) } } - pipe_dbg(p, "pipeline_task() sched"); + pipe_dbg(p, "sched"); + + return SOF_TASK_STATE_RESCHEDULE; +} + +#else /* CONFIG_IPC_MAJOR_4 */ + +/** Run an IPC4 pipeline trigger task. */ +static enum task_state ipc4_pipeline_trigger_task(void *arg) +{ + struct sof_ipc_reply reply = { + .hdr.cmd = SOF_IPC_GLB_REPLY, + .hdr.size = sizeof(reply), + }; + struct pipeline *p = arg; + enum task_state state; + struct pipeline *owner = delayed_trigger_owner[p->core]; + + /* some other trigger task is still waiting for its delayed trigger */ + if (owner && owner != p) + return SOF_TASK_STATE_RESCHEDULE; + + if (p->xrun_bytes) { + delayed_trigger_owner[p->core] = NULL; + + if (p->trigger.cmd != COMP_TRIGGER_NO_ACTION) { + p->trigger.cmd = COMP_TRIGGER_NO_ACTION; + reply.error = -EPIPE; + ipc_msg_reply(&reply); + } + + return SOF_TASK_STATE_COMPLETED; + } + + if (p->trigger.delay) { + delayed_trigger_owner[p->core] = p; + p->trigger.delay--; + return SOF_TASK_STATE_RESCHEDULE; + } + + if (p->trigger.cmd == COMP_TRIGGER_NO_ACTION) { + delayed_trigger_owner[p->core] = NULL; + return SOF_TASK_STATE_COMPLETED; + } + + state = pipeline_task_cmd(p, &reply); + if (state == SOF_TASK_STATE_RESCHEDULE && p->trigger.delay) { + delayed_trigger_owner[p->core] = p; + return state; + } + + delayed_trigger_owner[p->core] = NULL; + + /* RUNNING means that the independent copy task should keep running. */ + return state == SOF_TASK_STATE_RUNNING ? SOF_TASK_STATE_COMPLETED : state; +} + +/** Run IPC4 pipeline copy processing. */ +static enum task_state ipc4_pipeline_copy_task(void *arg) +{ + struct pipeline *p = arg; + int err; + + pipe_dbg(p, "entry"); + + /* are we in xrun ? */ + if (p->xrun_bytes) { + /* try to recover */ + err = pipeline_xrun_recover(p); + if (err < 0) + /* skip copy if still in xrun */ + return SOF_TASK_STATE_COMPLETED; + } + + /* Do not copy until the pipeline trigger sequence has completed. */ + if (task_is_active(p->trigger_task)) + return SOF_TASK_STATE_RESCHEDULE; + + if (p->status == COMP_STATE_PAUSED) + return SOF_TASK_STATE_COMPLETED; + + err = pipeline_copy(p); + if (err < 0) { + /* try to recover */ + err = pipeline_xrun_recover(p); + if (err < 0) { + pipe_err(p, "xrun recovery failed! pipeline is stopped."); + /* failed - host will stop this pipeline */ + return SOF_TASK_STATE_COMPLETED; + } + } + + pipe_dbg(p, "sched"); return SOF_TASK_STATE_RESCHEDULE; } +/** Allocate and initialize an IPC4 pipeline trigger task. */ +static struct task *ipc4_pipeline_trigger_task_init(struct pipeline *p, uint32_t type) +{ + struct task *task; + + task = sof_heap_alloc(p->heap, SOF_MEM_FLAG_USER, sizeof(*task), 0); + if (!task) + return NULL; + + memset(task, 0, sizeof(*task)); + + /* All trigger tasks use the highest priority, regardless of pipeline priority. */ + if (schedule_task_init_ll(task, SOF_UUID(pipe_trigger_task_uuid), type, -1, + ipc4_pipeline_trigger_task, p, p->core, 0) < 0) { + sof_heap_free(p->heap, task); + return NULL; + } + + return task; +} +#endif /* CONFIG_IPC_MAJOR_4 */ + 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->priority, +#ifdef CONFIG_IPC_MAJOR_4 + ipc4_pipeline_copy_task, +#else + ipc3_pipeline_task, +#endif p, p->core, 0) < 0) { - rfree(task); + sof_heap_free(p->heap, task); return NULL; } @@ -279,14 +415,31 @@ void pipeline_schedule_triggered(struct pipeline_walk_context *ctx, struct pipeline_data *ppl_data = ctx->comp_data; struct list_item *tlist; struct pipeline *p; - uint32_t flags; +#ifdef CONFIG_IPC_MAJOR_4 /* - * Interrupts have to be disabled while adding tasks to or removing them + * With IPC4, each pipeline is triggered separately. Exactly 1 pipeline + * is expected in the pipelines list (it's unclear whether an empty list + * should be tolerated). + */ + assert(list_is_empty(&ctx->pipelines) || + list_item_is_last(ctx->pipelines.next, &ctx->pipelines)); +#endif + + /* + * Block scheduler execution when adding tasks or removing them * from the scheduler list. Without that scheduling can begin * immediately before all pipelines achieved a consistent state. */ +#ifdef CONFIG_SOF_USERSPACE_LL + int sched_core = ppl_data->start->ipc_config.core; + + user_ll_lock_sched(sched_core); +#else + uint32_t flags; + irq_local_disable(flags); +#endif switch (cmd) { case COMP_TRIGGER_PAUSE: @@ -304,6 +457,10 @@ void pipeline_schedule_triggered(struct pipeline_walk_context *ctx, p->trigger.pending = true; p->trigger.host = ppl_data->start; ppl_data->start = NULL; +#ifdef CONFIG_IPC_MAJOR_4 + if (schedule_task(p->trigger_task, 0, 0) < 0) + pipe_err(p, "failed to schedule trigger task"); +#endif } else { pipeline_schedule_cancel(p); p->status = COMP_STATE_PAUSED; @@ -325,6 +482,10 @@ void pipeline_schedule_triggered(struct pipeline_walk_context *ctx, p->trigger.pending = true; p->trigger.host = ppl_data->start; ppl_data->start = NULL; +#ifdef CONFIG_IPC_MAJOR_4 + if (schedule_task(p->trigger_task, 0, 0) < 0) + pipe_err(p, "failed to schedule trigger task"); +#endif } else { p->status = COMP_STATE_ACTIVE; } @@ -342,22 +503,35 @@ void pipeline_schedule_triggered(struct pipeline_walk_context *ctx, p->xrun_bytes = 1; } } - +#ifdef CONFIG_SOF_USERSPACE_LL + user_ll_unlock_sched(sched_core); +#else irq_local_enable(flags); +#endif } int pipeline_comp_ll_task_init(struct pipeline *p) { uint32_t type; + /* right now we always consider pipeline as a low latency + * component, but it may change in the future + */ + type = pipeline_is_timer_driven(p) ? SOF_SCHEDULE_LL_TIMER : + SOF_SCHEDULE_LL_DMA; + +#ifdef CONFIG_IPC_MAJOR_4 + if (!p->trigger_task) { + p->trigger_task = ipc4_pipeline_trigger_task_init(p, type); + if (!p->trigger_task) { + pipe_err(p, "trigger task init failed"); + return -ENOMEM; + } + } +#endif + /* initialize task if necessary */ if (!p->pipe_task) { - /* right now we always consider pipeline as a low latency - * component, but it may change in the future - */ - type = pipeline_is_timer_driven(p) ? SOF_SCHEDULE_LL_TIMER : - SOF_SCHEDULE_LL_DMA; - p->pipe_task = pipeline_task_init(p, type); if (!p->pipe_task) { pipe_err(p, "task init failed"); @@ -368,6 +542,37 @@ int pipeline_comp_ll_task_init(struct pipeline *p) return 0; } +/** Free pipeline LL tasks and release delayed-trigger ownership. */ +void pipeline_comp_ll_task_free(struct pipeline *p) +{ +#ifdef CONFIG_IPC_MAJOR_4 + if (p->trigger_task) { +#if !CONFIG_LIBRARY || UNIT_TEST + schedule_task_free(p->trigger_task); +#endif + } + + /* + * Since the task has now been removed from the scheduler, a possible race + * with LL between the check and store below cannot cause any harm: other + * tasks do nothing when they see another owner. Therefore, LL locking can + * be skipped here. + */ + if (delayed_trigger_owner[p->core] == p) + delayed_trigger_owner[p->core] = NULL; + + if (p->trigger_task) + sof_heap_free(p->heap, p->trigger_task); +#endif + + if (p->pipe_task) { +#if !CONFIG_LIBRARY || UNIT_TEST + schedule_task_free(p->pipe_task); +#endif + sof_heap_free(p->heap, p->pipe_task); + } +} + #if CONFIG_ZEPHYR_DP_SCHEDULER static enum task_state dp_task_run(void *data) { @@ -383,7 +588,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 +596,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..5fd795e818db 100644 --- a/src/audio/pipeline/pipeline-stream.c +++ b/src/audio/pipeline/pipeline-stream.c @@ -21,7 +21,7 @@ #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> +#include <sof/schedule/ll_schedule_domain.h> #ifdef CONFIG_IPC_MAJOR_4 #include <ipc4/notification.h> @@ -96,14 +96,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 +108,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; } @@ -153,6 +146,20 @@ static int pipeline_comp_copy(struct comp_dev *current, return err; } +#ifdef CONFIG_SOF_USERSPACE_LL +/* + * User-space LL: pipeline_copy() runs as an LL task, with the per-core + * LL lock already held by the LL thread for the whole tick. Taking the + * lock again here would only add syscall overhead on this hot path, so + * we just assert that the lock is in fact held. + */ +#define PPL_LOCK(x) user_ll_assert_locked(x) +#define PPL_UNLOCK() +#else +#define PPL_LOCK(x) +#define PPL_UNLOCK() +#endif + /* Copy data across all pipeline components. * For capture pipelines it always starts from source component * and continues downstream and for playback pipelines it first @@ -170,6 +177,8 @@ int pipeline_copy(struct pipeline *p) uint32_t dir; int ret; + PPL_LOCK(p->core); + if (p->source_comp->direction == SOF_IPC_STREAM_PLAYBACK) { dir = PPL_DIR_UPSTREAM; start = p->sink_comp; @@ -186,6 +195,8 @@ int pipeline_copy(struct pipeline *p) pipe_err(p, "ret = %d, start->comp.id = %u, dir = %u", ret, dev_comp_id(start), dir); + PPL_UNLOCK(); + return ret; } @@ -237,9 +248,9 @@ static int pipeline_comp_list(struct comp_dev *current, * component and we aren't using IPC4. With IPC4 each pipeline receives * commands separately so we don't need to trigger them together */ - if (!is_single_ppl && (!is_same_sched || IPC4_MOD_ID(current->ipc_config.id))) { + if (!is_single_ppl && (!is_same_sched || IS_ENABLED(CONFIG_IPC_MAJOR_4))) { pipe_dbg(current->pipeline, - "pipeline_comp_list(), current is from another pipeline"); + "current is from another pipeline"); return 0; } @@ -473,7 +484,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 +531,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..cee7756c41c5 100644 --- a/src/audio/rtnr/rtnr.c +++ b/src/audio/rtnr/rtnr.c @@ -12,6 +12,7 @@ #include <sof/audio/format.h> #include <sof/audio/pipeline.h> #include <sof/audio/rtnr/rtnr.h> +#include <sof/audio/sink_source_utils.h> #include <sof/common.h> #include <rtos/panic.h> #include <sof/ipc/msg.h> @@ -74,27 +75,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 +198,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 +207,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 +242,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 +251,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 +286,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,32 +295,32 @@ 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; } /* Check component audio stream parameters */ -static int rtnr_check_params(struct processing_module *mod, struct audio_stream *source, - struct audio_stream *sink) +static int rtnr_check_params(struct processing_module *mod, struct sof_source *source, + struct sof_sink *sink) { struct comp_dev *dev = mod->dev; 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); - cd->sink_rate = audio_stream_get_rate(sink); - cd->sources_stream[0].rate = audio_stream_get_rate(source); - cd->sink_stream.rate = audio_stream_get_rate(sink); - channels_valid = audio_stream_get_channels(source) == audio_stream_get_channels(sink); + cd->source_rate = source_get_rate(source); + cd->sink_rate = sink_get_rate(sink); + cd->sources_stream[0].rate = cd->source_rate; + cd->sink_stream.rate = cd->sink_rate; + channels_valid = source_get_channels(source) == sink_get_channels(sink); if (!cd->sink_rate) { comp_err(dev, "rtnr_nr_params(), zero sink rate"); @@ -346,14 +347,18 @@ static int rtnr_check_params(struct processing_module *mod, struct audio_stream } /* set source/sink stream channels */ - cd->sources_stream[0].channels = audio_stream_get_channels(source); - cd->sink_stream.channels = audio_stream_get_channels(sink); + cd->sources_stream[0].channels = source_get_channels(source); + cd->sink_stream.channels = sink_get_channels(sink); - /* set source/sink stream overrun/underrun permitted */ - cd->sources_stream[0].overrun_permitted = audio_stream_get_overrun(source); - cd->sink_stream.overrun_permitted = audio_stream_get_overrun(sink); - cd->sources_stream[0].underrun_permitted = audio_stream_get_underrun(source); - cd->sink_stream.underrun_permitted = audio_stream_get_underrun(sink); + /* + * Set source/sink stream overrun/underrun permitted. The sink/source API exposes only + * the direction-relevant flag (a reader can underrun, a writer can overrun); the + * cross-direction flags have no getter and are hardcoded to false. + */ + cd->sources_stream[0].overrun_permitted = false; + cd->sink_stream.overrun_permitted = sink_get_overrun(sink); + cd->sources_stream[0].underrun_permitted = source_get_underrun(source); + cd->sink_stream.underrun_permitted = false; return 0; } @@ -439,7 +444,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,13 +452,18 @@ 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: return rtnr_get_bin_data(mod, cdata, fragment_size); case SOF_CTRL_CMD_SWITCH: + if (cdata->num_elems > SOF_IPC_MAX_CHANNELS) { + comp_err(dev, "num_elems %u > max %u", + cdata->num_elems, SOF_IPC_MAX_CHANNELS); + return -EINVAL; + } for (j = 0; j < cdata->num_elems; j++) { cdata->chanv[j].channel = j; cdata->chanv[j].value = cd->process_enable; @@ -478,7 +488,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 +504,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 */ @@ -560,9 +570,15 @@ static int32_t rtnr_set_value(struct processing_module *mod, void *ctl_data) uint32_t val = 0; int32_t j; + if (cdata->num_elems > SOF_IPC_MAX_CHANNELS) { + comp_err(dev, "num_elems %u > max %u", + cdata->num_elems, SOF_IPC_MAX_CHANNELS); + return -EINVAL; + } + 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 +630,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 +645,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 +653,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 +664,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,46 +691,81 @@ 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 } -void rtnr_copy_from_sof_stream(struct audio_stream_rtnr *dst, struct audio_stream *src) +/* + * Acquire the source's circular buffer and describe it for the RTNR library, which does its + * own circular-buffer arithmetic. It needs the buffer base/end address, the read pointer and + * the number of bytes to process this cycle. Only the acquired bytes are reported as available + * (with the write pointer placed at the end of that window), so the library processes exactly + * the amount released afterwards and never more than the sink can accept. + */ +static int rtnr_source_get_stream(struct audio_stream_rtnr *dst, struct sof_source *source, + size_t bytes) { + const void *data_ptr, *buf_start; + size_t buf_size; + int ret; - dst->size = audio_stream_get_size(src); - dst->avail = audio_stream_get_avail(src); - dst->free = audio_stream_get_free(src); - dst->w_ptr = audio_stream_get_wptr(src); - dst->r_ptr = audio_stream_get_rptr(src); - dst->addr = audio_stream_get_addr(src); - dst->end_addr = audio_stream_get_end_addr(src); + ret = source_get_data(source, bytes, &data_ptr, &buf_start, &buf_size); + if (ret) + return ret; + + dst->addr = (void *)buf_start; + dst->end_addr = (char *)buf_start + buf_size; + dst->size = buf_size; + dst->r_ptr = (void *)data_ptr; + dst->avail = bytes; + dst->free = buf_size - bytes; + dst->w_ptr = cir_buf_wrap((char *)data_ptr + bytes, dst->addr, dst->end_addr); + + return 0; } -void rtnr_copy_to_sof_stream(struct audio_stream *dst, struct audio_stream_rtnr *src) +/* + * Acquire the sink's circular buffer and describe it for the RTNR library. See + * rtnr_source_get_stream(); here data_ptr is the write pointer, only the acquired bytes are + * reported as free and the read pointer is placed at the end of that window. + */ +static int rtnr_sink_get_stream(struct audio_stream_rtnr *dst, struct sof_sink *sink, + size_t bytes) { - audio_stream_set_size(dst, src->size); - audio_stream_set_avail(dst, src->avail); - audio_stream_set_free(dst, src->free); - audio_stream_set_wptr(dst, src->w_ptr); - audio_stream_set_rptr(dst, src->r_ptr); - audio_stream_set_addr(dst, src->addr); - audio_stream_set_end_addr(dst, src->end_addr); + void *data_ptr, *buf_start; + size_t buf_size; + int ret; + + ret = sink_get_buffer(sink, bytes, &data_ptr, &buf_start, &buf_size); + if (ret) + return ret; + + dst->addr = buf_start; + dst->end_addr = (char *)buf_start + buf_size; + dst->size = buf_size; + dst->w_ptr = data_ptr; + dst->free = bytes; + dst->avail = buf_size - bytes; + dst->r_ptr = cir_buf_wrap((char *)data_ptr + bytes, dst->addr, dst->end_addr); + + return 0; } /* copy and process stream data from source to sink buffers */ static int rtnr_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 sof_source **sources, int num_of_sources, + struct sof_sink **sinks, int num_of_sinks) { - struct comp_dev *dev = mod->dev; - struct audio_stream *source = input_buffers[0].data; - struct audio_stream *sink = output_buffers[0].data; - int frames = input_buffers[0].size; struct comp_data *cd = module_get_private_data(mod); + struct sof_source *source = sources[0]; + struct sof_sink *sink = sinks[0]; + size_t src_frame_bytes = source_get_frame_bytes(source); + size_t snk_frame_bytes = sink_get_frame_bytes(sink); struct audio_stream_rtnr *sources_stream[RTNR_MAX_SOURCES]; struct audio_stream_rtnr *sink_stream = &cd->sink_stream; + struct comp_dev *dev = mod->dev; + uint32_t frames; int32_t i; int ret; @@ -730,18 +781,25 @@ static int rtnr_process(struct processing_module *mod, comp_dbg(dev, "rtnr_copy()"); /* put empty data into output queue*/ - RTKMA_API_First_Copy(cd->rtk_agl, cd->source_rate, audio_stream_get_channels(source)); + RTKMA_API_First_Copy(cd->rtk_agl, cd->source_rate, source_get_channels(source)); + frames = MIN(source_get_data_frames_available(source), sink_get_free_frames(sink)); if (!frames) return 0; - comp_dbg(dev, "rtnr_copy() frames = %d", frames); + comp_dbg(dev, "rtnr_copy() frames = %u", frames); if (cd->process_enable) { - /* Run processing function */ - /* copy required data from sof audio stream to RTNR audio stream */ - rtnr_copy_from_sof_stream(sources_stream[0], source); - rtnr_copy_from_sof_stream(sink_stream, sink); + /* acquire the source/sink circular buffers and describe them for the library */ + ret = rtnr_source_get_stream(sources_stream[0], source, frames * src_frame_bytes); + if (ret) + return ret; + + ret = rtnr_sink_get_stream(sink_stream, sink, frames * snk_frame_bytes); + if (ret) { + source_release_data(source, 0); + return ret; + } /* * Processing function uses an array of pointers to source streams @@ -755,38 +813,30 @@ static int rtnr_process(struct processing_module *mod, */ RTKMA_API_Process(cd->rtk_agl, 0, cd->source_rate, MicNum); - /* copy required data from RTNR audio stream to sof audio stream */ - rtnr_copy_to_sof_stream(source, sources_stream[0]); - rtnr_copy_to_sof_stream(sink, sink_stream); - + source_release_data(source, frames * src_frame_bytes); + sink_commit_buffer(sink, frames * snk_frame_bytes); } else { comp_dbg(dev, "rtnr_copy() passthrough"); - audio_stream_copy(source, 0, sink, 0, frames * audio_stream_get_channels(source)); + return source_to_sink_copy(source, sink, true, frames * src_frame_bytes); } - /* Track consume and produce */ - module_update_buffer_position(&input_buffers[0], &output_buffers[0], frames); return 0; } #if CONFIG_IPC_MAJOR_4 -static void rtnr_params(struct processing_module *mod) +static void rtnr_params(struct processing_module *mod, struct sof_source *source, + struct sof_sink *sink) { struct sof_ipc_stream_params *params = mod->stream_params; - struct comp_buffer *sinkb, *sourceb; struct comp_dev *dev = mod->dev; ipc4_base_module_cfg_to_stream_params(&mod->priv.cfg.base_cfg, params); component_set_nearest_period_frames(dev, params->rate); /* The caller has checked validity of source and sink buffers */ - - sourceb = comp_dev_get_first_data_producer(dev); - ipc4_update_buffer_format(sourceb, &mod->priv.cfg.base_cfg.audio_fmt); - - sinkb = comp_dev_get_first_data_consumer(dev); - ipc4_update_buffer_format(sinkb, &mod->priv.cfg.base_cfg.audio_fmt); + ipc4_update_source_format(source, &mod->priv.cfg.base_cfg.audio_fmt); + ipc4_update_sink_format(sink, &mod->priv.cfg.base_cfg.audio_fmt); } #endif @@ -796,20 +846,17 @@ static int rtnr_prepare(struct processing_module *mod, { struct comp_data *cd = module_get_private_data(mod); struct comp_dev *dev = mod->dev; - 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); - if (!sourceb || !sinkb) { + if (!num_of_sources || !num_of_sinks) { comp_err(dev, "no source or sink buffer"); return -ENOTCONN; } #if CONFIG_IPC_MAJOR_4 - rtnr_params(mod); + rtnr_params(mod, sources[0], sinks[0]); #endif /* Check config */ @@ -822,14 +869,14 @@ static int rtnr_prepare(struct processing_module *mod, /* Initialize RTNR */ /* Get sink data format */ - cd->sink_format = audio_stream_get_frm_fmt(&sinkb->stream); - cd->sink_stream.frame_fmt = audio_stream_get_frm_fmt(&sinkb->stream); - ret = rtnr_check_params(mod, &sourceb->stream, &sinkb->stream); + cd->sink_format = sink_get_frm_fmt(sinks[0]); + cd->sink_stream.frame_fmt = cd->sink_format; + ret = rtnr_check_params(mod, sources[0], sinks[0]); if (ret) 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 +900,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; @@ -866,7 +913,7 @@ static int rtnr_reset(struct processing_module *mod) static const struct module_interface rtnr_interface = { .init = rtnr_init, .prepare = rtnr_prepare, - .process_audio_stream = rtnr_process, + .process = rtnr_process, .set_configuration = rtnr_set_config, .get_configuration = rtnr_get_config, .reset = rtnr_reset, diff --git a/src/audio/rtnr/rtnr_stub.c b/src/audio/rtnr/rtnr_stub.c index a825396ece3f..5c5a975aa5bb 100644 --- a/src/audio/rtnr/rtnr_stub.c +++ b/src/audio/rtnr/rtnr_stub.c @@ -8,23 +8,32 @@ #include <sof/audio/rtnr/rtklib/include/RTK_MA_API.h> #include <sof/audio/audio_stream.h> #include <rtos/alloc.h> +#include <stddef.h> +#include <stdint.h> #define RTNR_STUB_CONTEXT_SIZE 42 /* Just some random size to allocate */ +/* + * The stub replaces the proprietary RTNR library with a plain passthrough: the requested frames + * are copied straight from the source to the sink circular buffer, honouring wrap on both sides. + * It works directly on the audio_stream_rtnr descriptors filled in by the component. + */ +static void rtnr_stub_passthrough(struct audio_stream_rtnr **sources, + struct audio_stream_rtnr *sink, int frames, size_t sample_bytes) +{ + struct audio_stream_rtnr *source = sources[0]; + + cir_buf_copy(source->r_ptr, source->addr, source->end_addr, + sink->w_ptr, sink->addr, sink->end_addr, + (size_t)frames * sink->channels * sample_bytes); +} + void RTKMA_API_S16_Default(void *Context, struct audio_stream_rtnr **sources, struct audio_stream_rtnr *sink, int frames, _Bool ref_active, int in_idx, int ref_idx, int ref_32bits, int ref_shift) { - struct audio_stream sof_source; - struct audio_stream sof_sink; - - rtnr_copy_to_sof_stream(&sof_source, sources[0]); - rtnr_copy_to_sof_stream(&sof_sink, sink); - audio_stream_copy(&sof_source, 0, &sof_sink, 0, - frames * audio_stream_get_channels(&sof_sink)); - rtnr_copy_from_sof_stream(sources[0], &sof_source); - rtnr_copy_from_sof_stream(sink, &sof_sink); + rtnr_stub_passthrough(sources, sink, frames, sizeof(int16_t)); } void RTKMA_API_S24_Default(void *Context, struct audio_stream_rtnr **sources, @@ -32,15 +41,7 @@ void RTKMA_API_S24_Default(void *Context, struct audio_stream_rtnr **sources, _Bool ref_active, int in_idx, int ref_idx, int ref_32bits, int ref_shift) { - struct audio_stream sof_source; - struct audio_stream sof_sink; - - rtnr_copy_to_sof_stream(&sof_source, sources[0]); - rtnr_copy_to_sof_stream(&sof_sink, sink); - audio_stream_copy(&sof_source, 0, &sof_sink, 0, - frames * audio_stream_get_channels(&sof_sink)); - rtnr_copy_from_sof_stream(sources[0], &sof_source); - rtnr_copy_from_sof_stream(sink, &sof_sink); + rtnr_stub_passthrough(sources, sink, frames, sizeof(int32_t)); } void RTKMA_API_S32_Default(void *Context, struct audio_stream_rtnr **sources, @@ -48,15 +49,7 @@ void RTKMA_API_S32_Default(void *Context, struct audio_stream_rtnr **sources, _Bool ref_active, int in_idx, int ref_idx, int ref_32bits, int ref_shift) { - struct audio_stream sof_source; - struct audio_stream sof_sink; - - rtnr_copy_to_sof_stream(&sof_source, sources[0]); - rtnr_copy_to_sof_stream(&sof_sink, sink); - audio_stream_copy(&sof_source, 0, &sof_sink, 0, - frames * audio_stream_get_channels(&sof_sink)); - rtnr_copy_from_sof_stream(sources[0], &sof_source); - rtnr_copy_from_sof_stream(sink, &sof_sink); + rtnr_stub_passthrough(sources, sink, frames, sizeof(int32_t)); } void RTKMA_API_First_Copy(void *Context, int SampleRate, int MicCh) 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..bc48f99b5126 100644 --- a/src/audio/selector/selector.c +++ b/src/audio/selector/selector.c @@ -22,7 +22,6 @@ #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 <rtos/string.h> @@ -37,13 +36,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 +55,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 +166,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 +175,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 +191,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 +209,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) { @@ -228,16 +230,86 @@ static int selector_ctrl_set_data(struct comp_dev *dev, struct sof_ipc_ctrl_data *cdata) { struct comp_data *cd = comp_get_drvdata(dev); + struct comp_buffer *src; struct sof_sel_config *cfg; + uint32_t src_channels; int ret = 0; 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"); + + if (cdata->data->size < sizeof(struct sof_sel_config)) { + comp_err(dev, "invalid config blob size %u", cdata->data->size); + return -EINVAL; + } cfg = (struct sof_sel_config *) ASSUME_ALIGNED(&cdata->data->data, 4); + /* + * The config validated at .params() time can be replaced here at + * runtime, so re-validate the new channel counts and selected + * channel before accepting them; otherwise an out-of-range value + * later indexes past the source channels in the copy routine. + */ + switch (cfg->in_channels_count) { + case 0: + case SEL_SOURCE_1CH: + case SEL_SOURCE_2CH: + case SEL_SOURCE_4CH: + break; + default: + comp_err(dev, "invalid in_channels_count %u", + cfg->in_channels_count); + return -EINVAL; + } + + switch (cfg->out_channels_count) { + case 0: + case SEL_SINK_1CH: + case SEL_SINK_2CH: + case SEL_SINK_4CH: + break; + default: + comp_err(dev, "invalid out_channels_count %u", + cfg->out_channels_count); + return -EINVAL; + } + + /* sel_channel indexes the source channels, so it must be below + * the source channel count, otherwise the copy routine reads + * past the end of each source frame. The copy routine strides by + * the live producer stream channel count, so when the source is + * already connected that live count is the authority for both the + * selected channel and any fixed input count; before connection + * fall back to the configured input count. Always cap by the + * maximum supported source width. + */ + src = comp_dev_get_first_data_producer(dev); + if (src) + src_channels = audio_stream_get_channels(&src->stream); + else + src_channels = cfg->in_channels_count; + + /* A fixed (non-zero) input count must not exceed the live source + * width, otherwise sel_channel could be accepted below the + * requested count yet still index past the actual stream. + */ + if (src && cfg->in_channels_count && + cfg->in_channels_count > src_channels) { + comp_err(dev, "in_channels_count %u exceeds source channels %u", + cfg->in_channels_count, src_channels); + return -EINVAL; + } + + if (cfg->sel_channel >= SEL_SOURCE_4CH || + (src_channels && cfg->sel_channel >= src_channels)) { + comp_err(dev, "invalid sel_channel %u (source channels %u)", + cfg->sel_channel, src_channels); + return -EINVAL; + } + /* Just set the configuration */ cd->config.in_channels_count = cfg->in_channels_count; cd->config.out_channels_count = cfg->out_channels_count; @@ -269,7 +341,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 +378,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 +388,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"); @@ -338,10 +410,10 @@ static int selector_cmd(struct comp_dev *dev, int cmd, void *data, static int selector_trigger(struct comp_dev *dev, int cmd) { struct comp_buffer *sourceb; - enum sof_comp_type type; + struct comp_dev *source_comp; int ret; - comp_dbg(dev, "selector_trigger()"); + comp_dbg(dev, "entry"); sourceb = comp_dev_get_first_data_producer(dev); if (!sourceb) { @@ -350,6 +422,8 @@ static int selector_trigger(struct comp_dev *dev, int cmd) } ret = comp_set_state(dev, cmd); + if (ret < 0) + return ret; if (ret == COMP_STATUS_STATE_ALREADY_SET) ret = 0; @@ -357,9 +431,14 @@ static int selector_trigger(struct comp_dev *dev, int cmd) * kpb_init_draining() and kpb_draining_task() are interrupted by * new pipeline_task() */ - type = dev_comp_type(comp_buffer_get_source_component(sourceb)); + /* The source buffer may be present without an attached producer + * component (e.g. a partially connected pipeline), so guard against a + * NULL source component before dereferencing it. + */ + source_comp = comp_buffer_get_source_component(sourceb); - return type == SOF_COMP_KPB ? PPL_STATUS_PATH_TERMINATE : ret; + return source_comp && dev_comp_type(source_comp) == SOF_COMP_KPB ? + PPL_STATUS_PATH_TERMINATE : ret; } /** @@ -375,7 +454,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 +467,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 +494,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 +578,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 +589,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, @@ -527,15 +608,14 @@ static const struct comp_driver comp_selector = { }, }; -static SHARED_DATA struct comp_driver_info comp_selector_info = { +static struct comp_driver_info comp_selector_info = { .drv = &comp_selector, }; /** \brief Initializes selector component. */ UT_STATIC void sys_comp_selector_init(void) { - comp_register(platform_shared_get(&comp_selector_info, - sizeof(comp_selector_info))); + comp_register(&comp_selector_info); } DECLARE_MODULE(sys_comp_selector_init); @@ -544,7 +624,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 +653,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 +668,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 +692,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 +771,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 +810,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 +830,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 +849,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 +906,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 +1023,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"); + + if (cd->new_config) { + cd->new_config = false; + ret = selector_find_coefficients(mod); + if (ret) + return ret; + } - comp_dbg(mod->dev, "selector_process()"); + 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 +1069,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 +1082,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 +1091,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 +1122,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 +1139,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 +1175,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..b2f70069ee0b 100644 --- a/src/audio/selector/tune/sof_selector_blobs.m +++ b/src/audio/selector/tune/sof_selector_blobs.m @@ -1,47 +1,80 @@ -% 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_DUAL_MONO = 9; + 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"); + + % Stereo to dual-mono downmix + sel.ch_count = [2 2]; + sel.ch_config = [IPC4_CHANNEL_CONFIG_STEREO IPC4_CHANNEL_CONFIG_DUAL_MONO]; + sel.coeffs = zeros(8,8); + sel.coeffs(1, 1) = 0.5; + sel.coeffs(1, 2) = 0.5; + sel.coeffs(2, 1) = 0.5; + sel.coeffs(2, 2) = 0.5; + stereo_to_doublemono_pack8 = write_blob(sel, "downmix_stereo_to_doublemono"); % 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 +87,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 src/audio/selector/tune; 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 +249,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 +261,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..38577545b36e 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,20 +275,25 @@ 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) + /* bs is the host-set config.size and is used as the memcpy source + * length from the fixed-size sad->config, so bound it by the struct + * size. memcpy_s() below already bounds it against the destination. + */ + if (bs == 0 || bs > sizeof(struct sof_smart_amp_config)) return -EINVAL; ret = memcpy_s(cdata->data->data, size, &sad->config, bs); @@ -316,24 +305,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 +336,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 +376,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 +393,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 +407,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 +419,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 +507,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; @@ -533,15 +525,22 @@ static int smart_amp_ff_process(struct comp_dev *dev, return 0; } - if (frames > SMART_AMP_FF_BUF_DB_SZ) { - comp_err(dev, "feed forward frame size overflow: %u", frames); + /* + * The remap functions write frames * ff_mod.channels samples into + * ff_mod.buf, which holds SMART_AMP_FF_BUF_DB_SZ samples. Bound the + * total sample count, not just the frame count, so an unexpected + * channel count cannot overflow the buffer. + */ + if ((uint64_t)frames * sad->ff_mod.channels > SMART_AMP_FF_BUF_DB_SZ) { + comp_err(dev, "feed forward frame size overflow: %u frames, %u ch", + frames, sad->ff_mod.channels); sad->ff_mod.consumed = frames; return -EINVAL; } 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 +551,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; @@ -567,15 +567,17 @@ static int smart_amp_fb_process(struct comp_dev *dev, return 0; } - if (frames > SMART_AMP_FB_BUF_DB_SZ) { - comp_err(dev, "feedback frame size overflow: %u", frames); + /* bound total samples (frames * channels) against the buffer size */ + if ((uint64_t)frames * sad->fb_mod.channels > SMART_AMP_FB_BUF_DB_SZ) { + comp_err(dev, "feedback frame size overflow: %u frames, %u ch", + frames, sad->fb_mod.channels); sad->fb_mod.consumed = frames; return -EINVAL; } 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 +586,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 +601,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 +628,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 +647,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 +665,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 +689,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 +712,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 +728,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 +782,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 +812,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_generic.c b/src/audio/smart_amp/smart_amp_generic.c index 91223eb398bd..72f81d0e5117 100644 --- a/src/audio/smart_amp/smart_amp_generic.c +++ b/src/audio/smart_amp/smart_amp_generic.c @@ -35,7 +35,11 @@ static void remap_s32_to_s32(struct smart_amp_mod_stream *src_mod, uint32_t fram n = MIN(num_samples_remaining, nmax); for (ch = 0; ch < src_mod->channels; ch++) { - if (chan_map[ch] == -1) + /* skip unmapped (-1) and out-of-range source channels; + * the uint8_t cast folds the negative case into the + * single upper-bound check (-1 becomes 255 >= src_ch) + */ + if ((uint8_t)chan_map[ch] >= src_ch) continue; mod_ptr = mod_ptr_base + ch; @@ -103,7 +107,11 @@ static void remap_s16_to_s16(struct smart_amp_mod_stream *src_mod, uint32_t fram n = MIN(num_samples_remaining, nmax); for (ch = 0; ch < src_mod->channels; ch++) { - if (chan_map[ch] == -1) + /* skip unmapped (-1) and out-of-range source channels; + * the uint8_t cast folds the negative case into the + * single upper-bound check (-1 becomes 255 >= src_ch) + */ + if ((uint8_t)chan_map[ch] >= src_ch) continue; mod_ptr = mod_ptr_base + ch; @@ -147,7 +155,11 @@ static void remap_s16_to_b32(struct smart_amp_mod_stream *src_mod, uint32_t fram n = MIN(num_samples_remaining, nmax); for (ch = 0; ch < src_mod->channels; ch++) { - if (chan_map[ch] == -1) + /* skip unmapped (-1) and out-of-range source channels; + * the uint8_t cast folds the negative case into the + * single upper-bound check (-1 becomes 255 >= src_ch) + */ + if ((uint8_t)chan_map[ch] >= src_ch) continue; mod_ptr = mod_ptr_base + ch; diff --git a/src/audio/smart_amp/smart_amp_maxim_dsm.c b/src/audio/smart_amp/smart_amp_maxim_dsm.c index 3360ff5c3b86..4b808dfaaf36 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,18 @@ 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 %zu", bs); + return -EINVAL; + } + + /* Host controls msg_index and therefore data_pos; make sure the + * fragment stays inside caldata->data to avoid leaking adjacent + * heap back to the host. + */ + if (caldata->data_pos >= caldata->data_size || + bs > caldata->data_size - caldata->data_pos) { + comp_err(dev, "[DSM] invalid data_pos %u, size %zu, total %u", + caldata->data_pos, bs, caldata->data_size); return -EINVAL; } @@ -383,6 +396,13 @@ static int maxim_dsm_set_param(struct smart_amp_mod_struct_t *hspk, id = DSM_CH_MASK(param->param.id); ch = (param->param.id & DSM_CH1_BITMASK) ? 0 : 1; + /* id indexes the model database; reject values past its size */ + if (id >= hspk->param.max_param) { + comp_err(dev, "[DSM] invalid param id:%x max:%d", + id, hspk->param.max_param); + return -EINVAL; + } + /* 2nd channel has (hspk->param.max_param * DSM_PARAM_MAX) sized offset */ db[(id + ch * hspk->param.max_param) * DSM_PARAM_MAX + DSM_PARAM_VALUE] = param->param.value; @@ -392,7 +412,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 +440,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..bf3d1dd8ff5e 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++) @@ -304,73 +302,185 @@ static int wrap_180(int a) return a; } -static int tdfb_init_coef(struct processing_module *mod, int source_nch, - int sink_nch) +/* Walk the TDFB blob and verify that every FIR section and trailing array + * fits exactly inside the IPC payload. The walk mirrors tdfb_init_coef() + * but stays bounded so a malformed blob cannot push tdfb_filter_seek() + * past the buffer end at setup time. The channel-vs-stream relationships + * are also checked here so a mismatching blob cannot replace the working + * configuration during streaming. + */ +static int tdfb_validate_config(struct comp_dev *dev, + struct sof_tdfb_config *config, + size_t config_size) { + struct processing_module *mod = comp_mod(dev); struct tdfb_comp_data *cd = module_get_private_data(mod); struct sof_fir_coef_data *coef_data; - struct sof_tdfb_config *config = cd->config; - struct comp_dev *dev = mod->dev; - int16_t *output_channel_mix_beam_off = NULL; - int16_t *coefp; - int size_sum = 0; - int min_delta_idx; /* Index to beam angle with smallest delta vs. target */ - int min_delta; /* Smallest angle difference found in degrees */ - int max_ch; - int num_filters; - int target_az; /* Target azimuth angle in degrees */ - int delta; /* Target minus found angle in degrees absolute value */ - int idx; - int s; + struct sof_tdfb_angle *filter_angles; + int16_t *input_channel_select; + uint8_t *p; + uint8_t *end; + size_t remaining; + size_t step; + size_t expected; + int16_t max_ch; + int total_filters; int i; - /* 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", + if ((size_t)config->size != config_size) { + comp_err(dev, "blob size %zu / header size %u mismatch", + config_size, config->size); + return -EINVAL; + } + if (!config->num_output_channels || + config->num_output_channels > PLATFORM_MAX_CHANNELS) { + comp_err(dev, "invalid num_output_channels %u", 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", - sink_nch, config->num_output_channels); + if (!config->num_filters || config->num_filters > SOF_TDFB_FIR_MAX_COUNT) { + comp_err(dev, "invalid num_filters %u", config->num_filters); return -EINVAL; } - - if (config->num_filters > SOF_TDFB_FIR_MAX_COUNT) { - comp_err(dev, "tdfb_init_coef(), invalid num_filters %d", - config->num_filters); + if (!config->num_angles || config->num_angles > SOF_TDFB_MAX_ANGLES) { + comp_err(dev, "invalid num_angles %u", config->num_angles); return -EINVAL; } - - if (config->num_angles > SOF_TDFB_MAX_ANGLES) { - comp_err(dev, "tdfb_init_coef(), invalid num_angles %d", - config->num_angles); + if (!config->angle_enum_mult) { + comp_err(dev, "invalid angle_enum_mult"); 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 %u", 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 %u", config->num_mic_locations); return -EINVAL; } - /* In SOF v1.6 - 1.8 based beamformer topologies the multiple angles, mic locations, - * and beam on/off switch were not defined. Return error if such configuration is seen. - * A most basic blob has num_angles equals 1. Mic locations data is optional. + total_filters = config->num_filters * (config->num_angles + config->beam_off_defined); + p = (uint8_t *)&config->data[0]; + end = (uint8_t *)config + config_size; + for (i = 0; i < total_filters; i++) { + remaining = end - p; + if (remaining < sizeof(struct sof_fir_coef_data)) { + comp_err(dev, "FIR %d header out of bounds", i); + return -EINVAL; + } + coef_data = (struct sof_fir_coef_data *)p; + if (fir_delay_size(coef_data) <= 0) { + comp_err(dev, "FIR %d invalid length %d", + i, coef_data->length); + return -EINVAL; + } + step = sizeof(struct sof_fir_coef_data) + + (size_t)coef_data->length * sizeof(int16_t); + if (step > remaining) { + comp_err(dev, "FIR %d coefs out of bounds", i); + return -EINVAL; + } + p += step; + } + + /* p now points at input_channel_select[]. The remaining bytes must + * hold exactly: 3 per-filter int16 arrays (input_channel_select, + * output_channel_mix, output_stream_mix), an optional beam-off output + * channel mix, num_angles angle entries and num_mic_locations + * microphone entries. + */ + input_channel_select = (int16_t *)p; + expected = ((size_t)config->num_filters * SOF_TDFB_CONFIG_FILTER_CONTROL_NUM_WORDS + + (size_t)config->beam_off_defined * config->num_filters) * + sizeof(int16_t) + + (size_t)config->num_angles * sizeof(struct sof_tdfb_angle) + + (size_t)config->num_mic_locations * + sizeof(struct sof_tdfb_mic_location); + if ((size_t)(end - p) != expected) { + comp_err(dev, "blob trailer size mismatch: have %zu, expected %zu", + (size_t)(end - p), expected); + return -EINVAL; + } + + /* Each filter_angles[].filter_index points at the first filter of a + * num_filters-wide bank, so it must be in [0, total_filters - + * num_filters]. Validating here ensures the runtime path in + * tdfb_init_coef() cannot be reached with an out-of-range index. + */ + filter_angles = (struct sof_tdfb_angle *) + (p + ((size_t)config->num_filters * SOF_TDFB_CONFIG_FILTER_CONTROL_NUM_WORDS + + (size_t)config->beam_off_defined * config->num_filters) * sizeof(int16_t)); + for (i = 0; i < config->num_angles; i++) { + if (filter_angles[i].filter_index < 0 || + filter_angles[i].filter_index + config->num_filters > total_filters) { + comp_err(dev, "invalid filter_index %d for angle %d", + filter_angles[i].filter_index, i); + return -EINVAL; + } + } + + /* The blob must match the running stream. Skip these checks when no + * stream is bound yet (cached channel counts are zero before prepare). */ - 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."); + if (cd->sink_channels && config->num_output_channels != cd->sink_channels) { + comp_err(dev, "blob num_output_channels %u does not match sink %d", + config->num_output_channels, cd->sink_channels); + return -EINVAL; + } + if (cd->source_channels) { + max_ch = 0; + for (i = 0; i < config->num_filters; i++) { + if (input_channel_select[i] < 0) { + comp_err(dev, "invalid channel select for filter %d", i); + return -EINVAL; + } + if (input_channel_select[i] > max_ch) + max_ch = input_channel_select[i]; + } + if (max_ch + 1 > cd->source_channels) { + comp_err(dev, "blob needs %d source channels, stream has %d", + max_ch + 1, cd->source_channels); + return -EINVAL; + } + } + + return 0; +} + +static int tdfb_validator(struct comp_dev *dev, void *new_data, uint32_t new_data_size) +{ + if (new_data_size < sizeof(struct sof_tdfb_config) || + new_data_size > SOF_TDFB_MAX_SIZE) { + comp_err(dev, "invalid configuration blob, size %u", new_data_size); return -EINVAL; } + return tdfb_validate_config(dev, new_data, new_data_size); +} + +static int tdfb_init_coef(struct processing_module *mod) +{ + struct tdfb_comp_data *cd = module_get_private_data(mod); + struct sof_fir_coef_data *coef_data; + struct sof_tdfb_config *config = cd->config; + struct comp_dev *dev = mod->dev; + int16_t *output_channel_mix_beam_off = NULL; + int16_t *coefp; + int size_sum = 0; + int min_delta_idx; /* Index to beam angle with smallest delta vs. target */ + int min_delta; /* Smallest angle difference found in degrees */ + int num_filters; + int target_az; /* Target azimuth angle in degrees */ + int delta; /* Target minus found angle in degrees absolute value */ + int idx; + int i; + + /* Blob layout, bounds, size and stream channel relationships are + * pre-validated by tdfb_validator() / tdfb_validate_config(). + */ + /* Skip filter coefficients */ num_filters = config->num_filters * (config->num_angles + config->beam_off_defined); coefp = tdfb_filter_seek(config, num_filters); @@ -383,8 +493,8 @@ static int tdfb_init_coef(struct processing_module *mod, int source_nch, cd->output_stream_mix = coefp; coefp += config->num_filters; - /* Check if there's beam-off configured, then get pointers to beam angles data - * and microphone locations. Finally check that size matches. + /* Check if there's beam-off configured, then get pointers to beam angles + * data and microphone locations. */ if (config->beam_off_defined) { output_channel_mix_beam_off = coefp; @@ -393,11 +503,6 @@ static int tdfb_init_coef(struct processing_module *mod, int source_nch, cd->filter_angles = (struct sof_tdfb_angle *)coefp; cd->mic_locations = (struct sof_tdfb_mic_location *) (&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"); - return -EINVAL; - } /* Skip to requested coefficient set */ min_delta = 360; @@ -414,56 +519,36 @@ 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); } + if (idx < 0 || idx + (int)config->num_filters > num_filters) { + comp_err(dev, "invalid filter_index %d for angle %d", + idx, cd->filter_angles[min_delta_idx].azimuth); + return -EINVAL; + } + /* Seek to proper filter for requested angle or beam off configuration */ coefp = tdfb_filter_seek(config, idx); - /* Initialize filter bank */ + /* Initialize filter bank. FIR header bounds and length validity were + * already checked when the blob entered the component. + */ for (i = 0; i < config->num_filters; i++) { - /* Get delay line size */ coef_data = (struct sof_fir_coef_data *)coefp; - s = fir_delay_size(coef_data); - if (s > 0) { - size_sum += s; - } else { - comp_err(dev, "tdfb_init_coef(), FIR length %d is invalid", - coef_data->length); - return -EINVAL; - } - - /* Initialize coefficients for FIR filter and find next - * filter. - */ + size_sum += fir_delay_size(coef_data); fir_init_coef(&cd->fir[i], coef_data); coefp = coef_data->coef + coef_data->length; } - /* Find max used input channel */ - max_ch = 0; - for (i = 0; i < config->num_filters; i++) { - if (cd->input_channel_select[i] > max_ch) - max_ch = cd->input_channel_select[i]; - } - - /* The stream must contain at least the number of channels that is - * 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", - source_nch, max_ch + 1); - return -EINVAL; - } - return size_sum; } @@ -499,7 +584,7 @@ static int tdfb_setup(struct processing_module *mod, int source_nch, int sink_nc } /* Set coefficients for each channel from coefficient blob */ - delay_size = tdfb_init_coef(mod, source_nch, sink_nch); + delay_size = tdfb_init_coef(mod); if (delay_size < 0) return delay_size; /* Contains error code */ @@ -511,12 +596,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 +624,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()"); + comp_info(dev, "entry"); - /* 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; - } - - cd = rzalloc(SOF_MEM_FLAG_USER, sizeof(*cd)); + cd = mod_zalloc(mod, sizeof(*cd)); if (!cd) return -ENOMEM; @@ -574,19 +650,19 @@ 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; - } + /* Reject malformed blobs at IPC time so a bad run-time update cannot + * replace the working configuration. The channel-count checks inside + * the validator are skipped until tdfb_prepare() caches the stream + * channel counts. + */ + comp_data_blob_set_validator(cd->model_handler, tdfb_validator); for (i = 0; i < PLATFORM_MAX_CHANNELS; i++) fir_reset(&cd->fir[i]); @@ -599,11 +675,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 +689,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,16 +731,21 @@ 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 */ + /* Check for changed configuration. The IPC-time validator installed + * in tdfb_init() has already structurally validated the blob, so + * only NULL needs to be guarded here. + */ if (comp_is_new_data_blob_available(cd->model_handler)) { - cd->config = comp_get_data_blob(cd->model_handler, NULL, NULL); + cd->config = comp_get_data_blob(cd->model_handler, &cd->config_size, NULL); + if (!cd->config) + return -EINVAL; ret = tdfb_setup(mod, audio_stream_get_channels(source), 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 +757,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 +788,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, @@ -729,7 +809,7 @@ static int tdfb_prepare(struct processing_module *mod, 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,23 +825,41 @@ 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); sink_channels = audio_stream_get_channels(&sinkb->stream); rate = audio_stream_get_rate(&sourceb->stream); + /* Cache stream channel counts for the blob validator before any + * validate_config() call runs. + */ + cd->source_channels = source_channels; + cd->sink_channels = sink_channels; + /* Initialize filter */ - cd->config = comp_get_data_blob(cd->model_handler, NULL, NULL); + cd->config = comp_get_data_blob(cd->model_handler, &cd->config_size, NULL); if (!cd->config) { ret = -EINVAL; goto out; } + /* Re-validate now that the stream channel counts are cached: the + * IPC-time validator skips the channel checks before prepare, so the + * blob must be walked again here before tdfb_setup() uses it. + * Discard a malformed blob so the runtime path (tdfb_process()) + * cannot dereference it. + */ + ret = tdfb_validator(dev, cd->config, (uint32_t)cd->config_size); + if (ret < 0) { + cd->config = NULL; + 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,19 +878,19 @@ 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); - if (!ret) { - comp_info(dev, "max_lag = %d, xcorr_size = %zu", - cd->direction.max_lag, cd->direction.d_size); - comp_info(dev, "line_array = %d, a_step = %d, a_offs = %d", - (int)cd->direction.line_array, cd->config->angle_enum_mult, - cd->config->angle_enum_offs); - } - -out: + ret = tdfb_direction_init(mod, rate, source_channels); if (ret < 0) - comp_set_state(dev, COMP_TRIGGER_RESET); + goto out; + comp_info(dev, "max_lag = %d, xcorr_size = %zu", + cd->direction.max_lag, cd->direction.d_size); + comp_info(dev, "line_array = %d, a_step = %d, a_offs = %d", + (int)cd->direction.line_array, cd->config->angle_enum_mult, + cd->config->angle_enum_offs); + + return 0; +out: + comp_set_state(dev, COMP_TRIGGER_RESET); return ret; } @@ -801,9 +899,12 @@ 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"); + + cd->source_channels = 0; + cd->sink_channels = 0; - tdfb_free_delaylines(cd); + tdfb_free_delaylines(mod); cd->tdfb_func = NULL; for (i = 0; i < PLATFORM_MAX_CHANNELS; i++) @@ -840,6 +941,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.h b/src/audio/tdfb/tdfb.h index d5a09776301f..1787fef919e6 100644 --- a/src/audio/tdfb/tdfb.h +++ b/src/audio/tdfb/tdfb.h @@ -8,16 +8,17 @@ #ifndef __USER_TDFB_H__ #define __USER_TDFB_H__ +#include <sof/platform.h> #include <stdint.h> #define SOF_TDFB_NUM_INPUT_PINS 1 /* One source */ #define SOF_TDFB_NUM_OUTPUT_PINS 1 /* One sink */ -#define SOF_TDFB_MAX_SIZE 4096 /* Max size for coef data in bytes */ +#define SOF_TDFB_MAX_SIZE 16384 /* Max size for coef data in bytes */ #define SOF_TDFB_FIR_MAX_LENGTH 256 /* Max length for individual filter */ #define SOF_TDFB_FIR_MAX_COUNT 16 /* A blob can define max 16 FIR EQs */ #define SOF_TDFB_MAX_STREAMS 8 /* Support 1..8 sinks */ #define SOF_TDFB_MAX_ANGLES 360 /* Up to 1 degree precision for 360 degrees coverage */ -#define SOF_TDFB_MAX_MICROPHONES 16 /* Up to 16 microphone locations */ +#define SOF_TDFB_MAX_MICROPHONES PLATFORM_MAX_CHANNELS /* Bounded by direction state arrays */ /* The driver assigns running numbers for control index. If there's single control of * type switch, enum, binary they all have index 0. @@ -28,6 +29,13 @@ #define SOF_TDFB_CTRL_INDEX_AZIMUTH_ESTIMATE 1 /* enum */ #define SOF_TDFB_CTRL_INDEX_FILTERBANK 0 /* bytes */ +/* Helper for magic value for number of words for input_channel_select, output_channel_mix, + * and output_stream_mix. It used to validate that the blob size is sufficient for all the + * filters defined for controllable beam angles. The words count is in the check code + * multiplied with num_filters and sizeof(). See the configuration data description below: + */ +#define SOF_TDFB_CONFIG_FILTER_CONTROL_NUM_WORDS 3 + /* * sof_tdfb_config data[] diff --git a/src/audio/tdfb/tdfb_comp.h b/src/audio/tdfb/tdfb_comp.h index e2d5075e06b5..f59ff48bacaf 100644 --- a/src/audio/tdfb/tdfb_comp.h +++ b/src/audio/tdfb/tdfb_comp.h @@ -88,6 +88,9 @@ struct tdfb_comp_data { int16_t *output_stream_mix; /**< for each FIR define stream */ int16_t az_value; /**< beam steer azimuth as in control enum */ int16_t az_value_estimate; /**< beam steer azimuth as in control enum */ + int16_t source_channels; /**< source channel count cached for validator */ + int16_t sink_channels; /**< sink channel count cached for validator */ + size_t config_size; /**< size of the configuration blob */ size_t fir_delay_size; /**< allocated size */ unsigned int max_frames; /**< max frames to process */ bool direction_updates:1; /**< set true if direction angle control is updated */ @@ -118,10 +121,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..bd435fa2ebd3 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 */ @@ -384,7 +386,7 @@ static int16_t distance_from_source(struct tdfb_comp_data *cd, int mic_n, static void theoretical_time_differences(struct tdfb_comp_data *cd, int16_t az) { - int16_t d[PLATFORM_MAX_CHANNELS]; + int16_t d[SOF_TDFB_MAX_MICROPHONES]; int16_t src_x; int16_t src_y; int16_t sin_az; diff --git a/src/audio/tdfb/tdfb_ipc3.c b/src/audio/tdfb/tdfb_ipc3.c index 52662a141e56..1ebdb9641ccf 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; @@ -112,18 +112,21 @@ static int tdfb_cmd_get_value(struct processing_module *mod, struct sof_ipc_ctrl { struct tdfb_comp_data *cd = module_get_private_data(mod); + if (cdata->num_elems == 0 || cdata->num_elems > SOF_IPC_MAX_CHANNELS) + return -EINVAL; + 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 +140,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 +193,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 +218,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/llext/CMakeLists.txt b/src/audio/template/llext/CMakeLists.txt index f84bd6daf916..52a1bcf0639f 100644 --- a/src/audio/template/llext/CMakeLists.txt +++ b/src/audio/template/llext/CMakeLists.txt @@ -1,7 +1,7 @@ # Copyright (c) 2025 Intel Corporation. # SPDX-License-Identifier: Apache-2.0 -sof_llext_build("template_comp" +sof_llext_build("template" SOURCES ../template.c ../template-generic.c ../template-ipc4.c diff --git a/src/audio/template/llext/llext.toml.h b/src/audio/template/llext/llext.toml.h index 4808c9af8f42..d7183adc306f 100644 --- a/src/audio/template/llext/llext.toml.h +++ b/src/audio/template/llext/llext.toml.h @@ -1,6 +1,6 @@ #include <tools/rimage/config/platform.toml> #define LOAD_TYPE "2" -#include "../template_comp.toml" +#include "../template.toml" [module] count = __COUNTER__ 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..9c93329bce1c 100644 --- a/src/audio/tensorflow/tflm-classify.c +++ b/src/audio/tensorflow/tflm-classify.c @@ -3,8 +3,9 @@ // Copyright(c) 2025 Intel Corporation. All rights reserved. #include <sof/audio/module_adapter/module/generic.h> -#include <sof/audio/buffer.h> #include <sof/audio/component.h> +#include <sof/audio/sink_api.h> +#include <sof/audio/source_api.h> #include <sof/audio/data_blob.h> #include <sof/audio/format.h> #include <sof/audio/ipc-config.h> @@ -18,7 +19,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 +36,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 +57,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 +87,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 +112,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 +128,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); @@ -175,30 +175,35 @@ int8_t expected_feature_yes[TFLM_FEATURE_SIZE] = { */ static int tflm_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 sof_source **sources, int num_of_sources, + struct sof_sink **sinks, int num_of_sinks) { struct tflm_comp_data *cd = module_get_private_data(mod); struct comp_dev *dev = mod->dev; - struct audio_stream *source = input_buffers[0].data; - struct audio_stream *sink = output_buffers[0].data; - int features = input_buffers[0].size; + size_t frame_bytes = source_get_frame_bytes(sources[0]); + int features = source_get_data_frames_available(sources[0]); + const void *data_ptr, *buf_start; + size_t buf_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. */ while (features >= TFLM_FEATURE_ELEM_COUNT) { - cd->tfc.audio_features = source->r_ptr; + ret = source_get_data(sources[0], TFLM_FEATURE_ELEM_COUNT * frame_bytes, + &data_ptr, &buf_start, &buf_size); + if (ret) + return ret; + + cd->tfc.audio_features = data_ptr; cd->tfc.audio_data_size = TFLM_FEATURE_ELEM_COUNT; ret = TF_ProcessClassify(&cd->tfc); if (!ret) { comp_err(dev, "classify failed %s.", cd->tfc.error); + source_release_data(sources[0], 0); return ret; } @@ -208,10 +213,9 @@ static int tflm_process(struct processing_module *mod, cd->tfc.predictions[i], prediction[i]); } - /* calc new free and available after moving onto next feature */ - module_update_buffer_position(&input_buffers[0], - &output_buffers[0], TFLM_FEATURE_SIZE); - features = input_buffers[0].size; + /* advance by one stride */ + source_release_data(sources[0], TFLM_FEATURE_SIZE * frame_bytes); + features = source_get_data_frames_available(sources[0]); } return ret; @@ -227,13 +231,14 @@ static int tflm_reset(struct processing_module *mod) static const struct module_interface tflmcly_interface = { .init = tflm_init, // .prepare = tflm_prepare, - .process_audio_stream = tflm_process, + .process = tflm_process, .set_configuration = tflm_set_config, // .get_configuration = tflm_get_config, .reset = tflm_reset, .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..75017fe3910f --- /dev/null +++ b/src/audio/tone/tone-ipc3.c @@ -0,0 +1,407 @@ +// 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/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 struct comp_driver_info comp_tone_info = { + .drv = &comp_tone, +}; + +UT_STATIC void sys_comp_tone_init(void) +{ + comp_register(&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..a54ed0b2b862 --- /dev/null +++ b/src/audio/tone/tone-ipc4.c @@ -0,0 +1,228 @@ +// 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/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, const 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, const 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..dac32a3c187e --- /dev/null +++ b/src/audio/tone/tone.c @@ -0,0 +1,354 @@ +// 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/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..1a3978b710b5 --- /dev/null +++ b/src/audio/tone/tone.h @@ -0,0 +1,171 @@ +/* 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/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..270c114ee93c 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, @@ -311,6 +307,13 @@ static int init_mix(struct processing_module *mod, return -EINVAL; } + /* select_mix_out_*() return NULL for unsupported in/out combos */ + if (!cd->mix_routine) { + comp_err(dev, "unsupported channel configuration (in=%d out=%d)", + format->ch_cfg, out_channel_config); + return -EINVAL; + } + /* Update audio format. */ cd->out_fmt[0].valid_bit_depth = IPC4_DEPTH_24BIT; cd->out_fmt[0].depth = IPC4_DEPTH_32BIT; @@ -326,9 +329,12 @@ 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); + if (!cd) + return 0; + + mod_free(mod, cd->buf_in); + mod_free(mod, cd->buf_out); + mod_free(mod, cd); return 0; } @@ -342,16 +348,14 @@ 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)); - if (!cd) { - comp_free(dev); + cd = mod_zalloc(mod, sizeof(*cd)); + if (!cd) return -ENOMEM; - } 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 +412,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 +452,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/up_down_mixer/up_down_mixer_hifi3.c b/src/audio/up_down_mixer/up_down_mixer_hifi3.c index 259cf5128b9f..87540d02e8e3 100644 --- a/src/audio/up_down_mixer/up_down_mixer_hifi3.c +++ b/src/audio/up_down_mixer/up_down_mixer_hifi3.c @@ -1247,7 +1247,7 @@ void downmix16bit_4ch_mono(struct up_down_mixer_data *cd, const uint8_t * const uint32_t idx3 = get_channel_index(cd->in_channel_map, 2); uint32_t idx4 = get_channel_index(cd->in_channel_map, 3); - uint16_t coeffs[4] = {cd->downmix_coefficients[idx1], + uint16_t coeffs[4] __attribute__((aligned(8))) = {cd->downmix_coefficients[idx1], cd->downmix_coefficients[idx2], cd->downmix_coefficients[idx3], cd->downmix_coefficients[idx4] 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..d43f314e1674 100644 --- a/src/audio/volume/volume.c +++ b/src/audio/volume/volume.c @@ -21,10 +21,10 @@ #include <sof/audio/module_adapter/module/generic.h> #include <sof/audio/pipeline.h> #include <sof/audio/ipc-config.h> +#include <sof/audio/sink_source_utils.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/cpu.h> #include <sof/lib/uuid.h> @@ -52,30 +52,31 @@ LOG_MODULE_REGISTER(volume, CONFIG_SOF_LOG_LEVEL); #if CONFIG_FORMAT_S16LE /** * \brief Used to find nearest zero crossing frame for 16 bit format. - * \param[in,out] source Source buffer. + * \param[in,out] source Source circular buffer view. + * \param[in] channels Number of channels in the stream. * \param[in] frames Number of frames. * \param[in,out] prev_sum Previous sum of channel samples. */ -static uint32_t vol_zc_get_s16(const struct audio_stream *source, +static uint32_t vol_zc_get_s16(struct cir_buf_source *source, const int channels, uint32_t frames, int64_t *prev_sum) { uint32_t curr_frames = frames; int32_t sum; - int16_t *x = audio_stream_get_rptr(source); + const int16_t *x = source->ptr; int bytes; int nmax; int i, j, n; - const int nch = audio_stream_get_channels(source); - int remaining_samples = frames * nch; + int remaining_samples = frames * channels; - x = audio_stream_wrap(source, x + remaining_samples - 1); /* Go to last channel */ + /* Go to last channel */ + x = cir_buf_wrap(x + remaining_samples - 1, source->buf_start, source->buf_end); while (remaining_samples) { - bytes = audio_stream_rewind_bytes_without_wrap(source, x); + bytes = cir_buf_bytes_without_wrap_rewind(x, source->buf_start); nmax = VOL_BYTES_TO_S16_SAMPLES(bytes) + 1; n = MIN(nmax, remaining_samples); - for (i = 0; i < n; i += nch) { + for (i = 0; i < n; i += channels) { sum = 0; - for (j = 0; j < nch; j++) { + for (j = 0; j < channels; j++) { sum += *x; x--; } @@ -88,7 +89,7 @@ static uint32_t vol_zc_get_s16(const struct audio_stream *source, curr_frames--; } remaining_samples -= n; - x = audio_stream_rewind_wrap(source, x); + x = source_cir_buf_rewind_wrap(x, source->buf_start, source->buf_end); } /* sign change not detected, process all samples */ @@ -100,30 +101,31 @@ static uint32_t vol_zc_get_s16(const struct audio_stream *source, #if CONFIG_FORMAT_S24LE /** * \brief Used to find nearest zero crossing frame for 24 in 32 bit format. - * \param[in,out] source Source buffer. + * \param[in,out] source Source circular buffer view. + * \param[in] channels Number of channels in the stream. * \param[in] frames Number of frames. * \param[in,out] prev_sum Previous sum of channel samples. */ -static uint32_t vol_zc_get_s24(const struct audio_stream *source, +static uint32_t vol_zc_get_s24(struct cir_buf_source *source, const int channels, uint32_t frames, int64_t *prev_sum) { int64_t sum; uint32_t curr_frames = frames; - int32_t *x = audio_stream_get_rptr(source); + const int32_t *x = source->ptr; int bytes; int nmax; int i, j, n; - const int nch = audio_stream_get_channels(source); - int remaining_samples = frames * nch; + int remaining_samples = frames * channels; - x = audio_stream_wrap(source, x + remaining_samples - 1); /* Go to last channel */ + /* Go to last channel */ + x = cir_buf_wrap(x + remaining_samples - 1, source->buf_start, source->buf_end); while (remaining_samples) { - bytes = audio_stream_rewind_bytes_without_wrap(source, x); + bytes = cir_buf_bytes_without_wrap_rewind(x, source->buf_start); nmax = VOL_BYTES_TO_S32_SAMPLES(bytes) + 1; n = MIN(nmax, remaining_samples); - for (i = 0; i < n; i += nch) { + for (i = 0; i < n; i += channels) { sum = 0; - for (j = 0; j < nch; j++) { + for (j = 0; j < channels; j++) { sum += sign_extend_s24(*x); x--; } @@ -136,7 +138,7 @@ static uint32_t vol_zc_get_s24(const struct audio_stream *source, curr_frames--; } remaining_samples -= n; - x = audio_stream_rewind_wrap(source, x); + x = source_cir_buf_rewind_wrap(x, source->buf_start, source->buf_end); } /* sign change not detected, process all samples */ @@ -148,30 +150,31 @@ static uint32_t vol_zc_get_s24(const struct audio_stream *source, #if CONFIG_FORMAT_S32LE /** * \brief Used to find nearest zero crossing frame for 32 bit format. - * \param[in,out] source Source buffer. + * \param[in,out] source Source circular buffer view. + * \param[in] channels Number of channels in the stream. * \param[in] frames Number of frames. * \param[in,out] prev_sum Previous sum of channel samples. */ -static uint32_t vol_zc_get_s32(const struct audio_stream *source, +static uint32_t vol_zc_get_s32(struct cir_buf_source *source, const int channels, uint32_t frames, int64_t *prev_sum) { int64_t sum; uint32_t curr_frames = frames; - int32_t *x = audio_stream_get_rptr(source); + const int32_t *x = source->ptr; int bytes; int nmax; int i, j, n; - const int nch = audio_stream_get_channels(source); - int remaining_samples = frames * nch; + int remaining_samples = frames * channels; - x = audio_stream_wrap(source, x + remaining_samples - 1); /* Go to last channel */ + /* Go to last channel */ + x = cir_buf_wrap(x + remaining_samples - 1, source->buf_start, source->buf_end); while (remaining_samples) { - bytes = audio_stream_rewind_bytes_without_wrap(source, x); + bytes = cir_buf_bytes_without_wrap_rewind(x, source->buf_start); nmax = VOL_BYTES_TO_S32_SAMPLES(bytes) + 1; n = MIN(nmax, remaining_samples); - for (i = 0; i < n; i += nch) { + for (i = 0; i < n; i += channels) { sum = 0; - for (j = 0; j < nch; j++) { + for (j = 0; j < channels; j++) { sum += *x; x--; } @@ -184,7 +187,7 @@ static uint32_t vol_zc_get_s32(const struct audio_stream *source, curr_frames--; } remaining_samples -= n; - x = audio_stream_rewind_wrap(source, x); + x = source_cir_buf_rewind_wrap(x, source->buf_start, source->buf_end); } /* sign change not detected, process all samples */ @@ -490,14 +493,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; } @@ -553,15 +556,45 @@ void volume_set_chan_unmute(struct processing_module *mod, int chan) * \return Error code. */ static int volume_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 sof_source **sources, int num_of_sources, + struct sof_sink **sinks, int num_of_sinks) { struct vol_data *cd = module_get_private_data(mod); - uint32_t avail_frames = input_buffers[0].size; + struct sof_source *source = sources[0]; + struct sof_sink *sink = sinks[0]; + struct cir_buf_source source_buf; + struct cir_buf_sink sink_buf; + const int nch = cd->channels; + size_t source_frame_bytes = source_get_frame_bytes(source); + size_t sink_frame_bytes = sink_get_frame_bytes(sink); + size_t source_bytes, sink_bytes, bytes; + uint32_t avail_frames; uint32_t frames; int64_t prev_sum = 0; + int ret; + + comp_dbg(mod->dev, "entry"); + + avail_frames = source_sink_avail_frames_aligned(source, sink); + if (!avail_frames) + return 0; - comp_dbg(mod->dev, "volume_process()"); + source_bytes = avail_frames * source_frame_bytes; + sink_bytes = avail_frames * sink_frame_bytes; + + /* acquire source and sink buffers once for the whole available period */ + ret = source_get_data(source, source_bytes, &source_buf.ptr, + &source_buf.buf_start, &bytes); + if (ret < 0) + return ret; + source_buf.buf_end = (const char *)source_buf.buf_start + bytes; + + ret = sink_get_buffer(sink, sink_bytes, &sink_buf.ptr, &sink_buf.buf_start, &bytes); + if (ret < 0) { + source_release_data(source, 0); + return ret; + } + sink_buf.buf_end = (char *)sink_buf.buf_start + bytes; while (avail_frames) { #if CONFIG_COMP_PEAK_VOL @@ -572,22 +605,49 @@ 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_buf, nch, 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 = source_align_frames_round_nearest(source, frames); + if (!frames) + frames = source_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 = source_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; } /* copy and scale volume */ - cd->scale_vol(mod, &input_buffers[0], &output_buffers[0], frames, cd->attenuation); + cd->scale_vol(mod, &source_buf, &sink_buf, frames, cd->attenuation); + + /* advance the views by the processed frames */ + source_buf.ptr = cir_buf_wrap((const char *)source_buf.ptr + + frames * source_frame_bytes, + source_buf.buf_start, source_buf.buf_end); + sink_buf.ptr = cir_buf_wrap((char *)sink_buf.ptr + frames * sink_frame_bytes, + sink_buf.buf_start, sink_buf.buf_end); avail_frames -= frames; } + + /* commit the consumed and produced data */ + source_release_data(source, source_bytes); + sink_commit_buffer(sink, sink_bytes); + #if CONFIG_COMP_PEAK_VOL cd->peak_cnt++; if (cd->peak_cnt == cd->peak_report_cnt) { @@ -608,13 +668,13 @@ static int volume_process(struct processing_module *mod, * \param[in,out] dev Volume base component device. */ static vol_zc_func vol_get_zc_function(struct comp_dev *dev, - struct comp_buffer *sinkb) + struct sof_sink *sink) { int i; /* map the zc function to frame format */ for (i = 0; i < ARRAY_SIZE(zc_func_map); i++) { - if (audio_stream_get_valid_fmt(&sinkb->stream) == zc_func_map[i].frame_fmt) + if (sink_get_valid_fmt(sink) == zc_func_map[i].frame_fmt) return zc_func_map[i].func; } @@ -624,30 +684,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 sof_source *source) { - /* Both source and sink buffer in HiFi5 processing version, - * xtensa intrinsics ask for 16-byte aligned. + const int channels = source_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. */ -#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; + 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(5, VOLUME) + const int n = (source_get_valid_fmt(source) == SOF_IPC_FRAME_S16_LE) ? 8 : 4; #else - const uint32_t byte_align = SOF_FRAME_BYTE_ALIGN; + const int n = (source_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); + source_set_alignment_constants(source, byte_align, frame_align_req); } /** @@ -666,35 +733,24 @@ static int volume_prepare(struct processing_module *mod, struct vol_data *cd = module_get_private_data(mod); struct module_data *md = &mod->priv; struct comp_dev *dev = mod->dev; - struct comp_buffer *sourceb, *sinkb; uint32_t sink_period_bytes; 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); - sourceb = comp_dev_get_first_data_producer(dev); - if (!sourceb || !sinkb) { + if (num_of_sources < 1 || num_of_sinks < 1 ) { comp_err(dev, "no source or sink buffer"); return -ENOTCONN; } ret = volume_peak_prepare(cd, mod); - volume_set_alignment(&sourceb->stream, &sinkb->stream); + volume_set_alignment(sources[0]); /* get sink period bytes */ - sink_period_bytes = audio_stream_period_bytes(&sinkb->stream, - dev->frames); - - 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; - } + sink_period_bytes = dev->frames * sink_get_frame_bytes(sinks[0]); set_volume_process(cd, dev, false); @@ -705,7 +761,7 @@ static int volume_prepare(struct processing_module *mod, goto err; } - cd->zc_get = vol_get_zc_function(dev, sinkb); + cd->zc_get = vol_get_zc_function(dev, sinks[0]); if (!cd->zc_get) { comp_err(dev, "invalid cd->zc_get"); ret = -EINVAL; @@ -720,14 +776,14 @@ static int volume_prepare(struct processing_module *mod, */ cd->ramp_finished = false; - cd->channels = audio_stream_get_channels(&sinkb->stream); + cd->channels = sink_get_channels(sinks[0]); if (cd->channels > SOF_IPC_MAX_CHANNELS) { ret = -EINVAL; goto err; } cd->sample_rate_inv = (int32_t)(1000LL * INT32_MAX / - audio_stream_get_rate(&sinkb->stream)); + sink_get_rate(sinks[0])); for (i = 0; i < cd->channels; i++) { cd->volume[i] = cd->vol_min; @@ -761,7 +817,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 +830,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; } @@ -786,7 +842,7 @@ static int volume_free(struct processing_module *mod) static const struct module_interface volume_interface = { .init = volume_init, .prepare = volume_prepare, - .process_audio_stream = volume_process, + .process = volume_process, .set_configuration = volume_set_config, .get_configuration = volume_get_config, .reset = volume_reset, @@ -797,7 +853,7 @@ static const struct module_interface volume_interface = { static const struct module_interface gain_interface = { .init = volume_init, .prepare = volume_prepare, - .process_audio_stream = volume_process, + .process = volume_process, .set_configuration = volume_set_config, .get_configuration = volume_get_config, .reset = volume_reset, diff --git a/src/audio/volume/volume.h b/src/audio/volume/volume.h index 7e7c41ea00d7..478e80673089 100644 --- a/src/audio/volume/volume.h +++ b/src/audio/volume/volume.h @@ -146,14 +146,18 @@ struct sof_ipc_ctrl_value_chan; /** * \brief volume processing function interface + * + * The source and sink circular buffers are acquired and committed once by the + * module's process callback. Each processing function receives ready-to-use + * circular buffer views and only performs the gain scaling. */ -typedef void (*vol_scale_func)(struct processing_module *mod, struct input_stream_buffer *source, - struct output_stream_buffer *sink, uint32_t frames, uint32_t attenuation); +typedef void (*vol_scale_func)(struct processing_module *mod, struct cir_buf_source *source, + struct cir_buf_sink *sink, uint32_t frames, uint32_t attenuation); /** * \brief volume interface for function getting nearest zero crossing frame */ -typedef uint32_t (*vol_zc_func)(const struct audio_stream *source, +typedef uint32_t (*vol_zc_func)(struct cir_buf_source *source, const int channels, uint32_t frames, int64_t *prev_sum); /** @@ -300,7 +304,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_generic.c b/src/audio/volume/volume_generic.c index 0830677c7c94..a721823dd766 100644 --- a/src/audio/volume/volume_generic.c +++ b/src/audio/volume/volume_generic.c @@ -56,29 +56,23 @@ static inline int32_t vol_mult_s24_to_s24(int32_t x, int32_t vol) * Copy and scale volume from 24/32 bit source buffer * to 24/32 bit destination buffer. */ -static void vol_s24_to_s24(struct processing_module *mod, struct input_stream_buffer *bsource, - struct output_stream_buffer *bsink, uint32_t frames, - uint32_t attenuation) +static void vol_s24_to_s24(struct processing_module *mod, struct cir_buf_source *source, + struct cir_buf_sink *sink, uint32_t frames, uint32_t attenuation) { struct vol_data *cd = module_get_private_data(mod); - struct audio_stream *source = bsource->data; - struct audio_stream *sink = bsink->data; - int32_t vol; - int32_t *x, *x0; + const int32_t *x, *x0; int32_t *y, *y0; + int32_t vol; int nmax, n, i, j; - const int nch = audio_stream_get_channels(source); + const int nch = cd->channels; int remaining_samples = frames * nch; - x = audio_stream_wrap(source, (char *)audio_stream_get_rptr(source) + bsource->consumed); - y = audio_stream_wrap(sink, (char *)audio_stream_get_wptr(sink) + bsink->size); - - bsource->consumed += VOL_S32_SAMPLES_TO_BYTES(remaining_samples); - bsink->size += VOL_S32_SAMPLES_TO_BYTES(remaining_samples); + x = source->ptr; + y = sink->ptr; while (remaining_samples) { - nmax = audio_stream_samples_without_wrap_s24(source, x); + nmax = cir_buf_samples_without_wrap_s32(x, source->buf_end); n = MIN(remaining_samples, nmax); - nmax = audio_stream_samples_without_wrap_s24(sink, y); + nmax = cir_buf_samples_without_wrap_s32(y, sink->buf_end); n = MIN(n, nmax); for (j = 0; j < nch; j++) { x0 = x + j; @@ -89,8 +83,8 @@ static void vol_s24_to_s24(struct processing_module *mod, struct input_stream_bu } } remaining_samples -= n; - x = audio_stream_wrap(source, x + n); - y = audio_stream_wrap(sink, y + n); + x = cir_buf_wrap(x + n, source->buf_start, source->buf_end); + y = cir_buf_wrap(y + n, sink->buf_start, sink->buf_end); } } @@ -106,32 +100,28 @@ static void vol_s24_to_s24(struct processing_module *mod, struct input_stream_bu * to 24/32 bit destination buffer. */ static void vol_passthrough_s24_to_s24(struct processing_module *mod, - struct input_stream_buffer *bsource, - struct output_stream_buffer *bsink, uint32_t frames, + struct cir_buf_source *source, + struct cir_buf_sink *sink, uint32_t frames, uint32_t attenuation) { - struct audio_stream *source = bsource->data; - struct audio_stream *sink = bsink->data; - int32_t *x; + struct vol_data *cd = module_get_private_data(mod); + const int32_t *x; int32_t *y; int nmax, n; - const int nch = audio_stream_get_channels(source); + const int nch = cd->channels; int remaining_samples = frames * nch; - x = audio_stream_wrap(source, (char *)audio_stream_get_rptr(source) + bsource->consumed); - y = audio_stream_wrap(sink, (char *)audio_stream_get_wptr(sink) + bsink->size); - - bsource->consumed += VOL_S32_SAMPLES_TO_BYTES(remaining_samples); - bsink->size += VOL_S32_SAMPLES_TO_BYTES(remaining_samples); + x = source->ptr; + y = sink->ptr; while (remaining_samples) { - nmax = audio_stream_samples_without_wrap_s24(source, x); + nmax = cir_buf_samples_without_wrap_s32(x, source->buf_end); n = MIN(remaining_samples, nmax); - nmax = audio_stream_samples_without_wrap_s24(sink, y); + nmax = cir_buf_samples_without_wrap_s32(y, sink->buf_end); n = MIN(n, nmax); memcpy_s(y, n * sizeof(int32_t), x, n * sizeof(int32_t)); remaining_samples -= n; - x = audio_stream_wrap(source, x + n); - y = audio_stream_wrap(sink, y + n); + x = cir_buf_wrap(x + n, source->buf_start, source->buf_end); + y = cir_buf_wrap(y + n, sink->buf_start, sink->buf_end); } } #endif /* CONFIG_FORMAT_S24LE */ @@ -148,28 +138,23 @@ static void vol_passthrough_s24_to_s24(struct processing_module *mod, * Copy and scale volume from 32 bit source buffer * to 32 bit destination buffer. */ -static void vol_s32_to_s32(struct processing_module *mod, struct input_stream_buffer *bsource, - struct output_stream_buffer *bsink, uint32_t frames, - uint32_t attenuation) +static void vol_s32_to_s32(struct processing_module *mod, struct cir_buf_source *source, + struct cir_buf_sink *sink, uint32_t frames, uint32_t attenuation) { struct vol_data *cd = module_get_private_data(mod); - struct audio_stream *source = bsource->data; - struct audio_stream *sink = bsink->data; - int32_t vol; - int32_t *x, *x0; + const int32_t *x, *x0; int32_t *y, *y0; + int32_t vol; int nmax, n, i, j; - const int nch = audio_stream_get_channels(source); + const int nch = cd->channels; int remaining_samples = frames * nch; - x = audio_stream_wrap(source, (char *)audio_stream_get_rptr(source) + bsource->consumed); - y = audio_stream_wrap(sink, (char *)audio_stream_get_wptr(sink) + bsink->size); - bsource->consumed += VOL_S32_SAMPLES_TO_BYTES(remaining_samples); - bsink->size += VOL_S32_SAMPLES_TO_BYTES(remaining_samples); + x = source->ptr; + y = sink->ptr; while (remaining_samples) { - nmax = audio_stream_samples_without_wrap_s32(source, x); + nmax = cir_buf_samples_without_wrap_s32(x, source->buf_end); n = MIN(remaining_samples, nmax); - nmax = audio_stream_samples_without_wrap_s32(sink, y); + nmax = cir_buf_samples_without_wrap_s32(y, sink->buf_end); n = MIN(n, nmax); /* Note: on Xtensa processing one channel volume at time performed slightly * better than simpler interleaved code version (average 19 us vs. 20 us). @@ -184,8 +169,8 @@ static void vol_s32_to_s32(struct processing_module *mod, struct input_stream_bu } } remaining_samples -= n; - x = audio_stream_wrap(source, x + n); - y = audio_stream_wrap(sink, y + n); + x = cir_buf_wrap(x + n, source->buf_start, source->buf_end); + y = cir_buf_wrap(y + n, sink->buf_start, sink->buf_end); } } @@ -201,31 +186,28 @@ static void vol_s32_to_s32(struct processing_module *mod, struct input_stream_bu * to 32 bit destination buffer. */ static void vol_passthrough_s32_to_s32(struct processing_module *mod, - struct input_stream_buffer *bsource, - struct output_stream_buffer *bsink, uint32_t frames, + struct cir_buf_source *source, + struct cir_buf_sink *sink, uint32_t frames, uint32_t attenuation) { - struct audio_stream *source = bsource->data; - struct audio_stream *sink = bsink->data; - int32_t *x; + struct vol_data *cd = module_get_private_data(mod); + const int32_t *x; int32_t *y; int nmax, n; - const int nch = audio_stream_get_channels(source); + const int nch = cd->channels; int remaining_samples = frames * nch; - x = audio_stream_wrap(source, (char *)audio_stream_get_rptr(source) + bsource->consumed); - y = audio_stream_wrap(sink, (char *)audio_stream_get_wptr(sink) + bsink->size); - bsource->consumed += VOL_S32_SAMPLES_TO_BYTES(remaining_samples); - bsink->size += VOL_S32_SAMPLES_TO_BYTES(remaining_samples); + x = source->ptr; + y = sink->ptr; while (remaining_samples) { - nmax = audio_stream_samples_without_wrap_s32(source, x); + nmax = cir_buf_samples_without_wrap_s32(x, source->buf_end); n = MIN(remaining_samples, nmax); - nmax = audio_stream_samples_without_wrap_s32(sink, y); + nmax = cir_buf_samples_without_wrap_s32(y, sink->buf_end); n = MIN(n, nmax); memcpy_s(y, n * sizeof(int32_t), x, n * sizeof(int32_t)); remaining_samples -= n; - x = audio_stream_wrap(source, x + n); - y = audio_stream_wrap(sink, y + n); + x = cir_buf_wrap(x + n, source->buf_start, source->buf_end); + y = cir_buf_wrap(y + n, sink->buf_start, sink->buf_end); } } #endif /* CONFIG_FORMAT_S32LE */ @@ -242,28 +224,23 @@ static void vol_passthrough_s32_to_s32(struct processing_module *mod, * Copy and scale volume from 16 bit source buffer * to 16 bit destination buffer. */ -static void vol_s16_to_s16(struct processing_module *mod, struct input_stream_buffer *bsource, - struct output_stream_buffer *bsink, uint32_t frames, - uint32_t attenuation) +static void vol_s16_to_s16(struct processing_module *mod, struct cir_buf_source *source, + struct cir_buf_sink *sink, uint32_t frames, uint32_t attenuation) { struct vol_data *cd = module_get_private_data(mod); - struct audio_stream *source = bsource->data; - struct audio_stream *sink = bsink->data; - int32_t vol; - int16_t *x, *x0; + const int16_t *x, *x0; int16_t *y, *y0; + int32_t vol; int nmax, n, i, j; - const int nch = audio_stream_get_channels(source); + const int nch = cd->channels; int remaining_samples = frames * nch; - x = audio_stream_wrap(source, (char *)audio_stream_get_rptr(source) + bsource->consumed); - y = audio_stream_wrap(sink, (char *)audio_stream_get_wptr(sink) + bsink->size); - bsource->consumed += VOL_S16_SAMPLES_TO_BYTES(remaining_samples); - bsink->size += VOL_S16_SAMPLES_TO_BYTES(remaining_samples); + x = source->ptr; + y = sink->ptr; while (remaining_samples) { - nmax = audio_stream_samples_without_wrap_s16(source, x); + nmax = cir_buf_samples_without_wrap_s16(x, source->buf_end); n = MIN(remaining_samples, nmax); - nmax = audio_stream_samples_without_wrap_s16(sink, y); + nmax = cir_buf_samples_without_wrap_s16(y, sink->buf_end); n = MIN(n, nmax); for (j = 0; j < nch; j++) { x0 = x + j; @@ -275,8 +252,8 @@ static void vol_s16_to_s16(struct processing_module *mod, struct input_stream_bu } } remaining_samples -= n; - x = audio_stream_wrap(source, x + n); - y = audio_stream_wrap(sink, y + n); + x = cir_buf_wrap(x + n, source->buf_start, source->buf_end); + y = cir_buf_wrap(y + n, sink->buf_start, sink->buf_end); } } @@ -292,31 +269,28 @@ static void vol_s16_to_s16(struct processing_module *mod, struct input_stream_bu * to 16 bit destination buffer. */ static void vol_passthrough_s16_to_s16(struct processing_module *mod, - struct input_stream_buffer *bsource, - struct output_stream_buffer *bsink, uint32_t frames, + struct cir_buf_source *source, + struct cir_buf_sink *sink, uint32_t frames, uint32_t attenuation) { - struct audio_stream *source = bsource->data; - struct audio_stream *sink = bsink->data; - int16_t *x; + struct vol_data *cd = module_get_private_data(mod); + const int16_t *x; int16_t *y; int nmax, n; - const int nch = audio_stream_get_channels(source); + const int nch = cd->channels; int remaining_samples = frames * nch; - x = audio_stream_wrap(source, (char *)audio_stream_get_rptr(source) + bsource->consumed); - y = audio_stream_wrap(sink, (char *)audio_stream_get_wptr(sink) + bsink->size); - bsource->consumed += VOL_S16_SAMPLES_TO_BYTES(remaining_samples); - bsink->size += VOL_S16_SAMPLES_TO_BYTES(remaining_samples); + x = source->ptr; + y = sink->ptr; while (remaining_samples) { - nmax = audio_stream_samples_without_wrap_s16(source, x); + nmax = cir_buf_samples_without_wrap_s16(x, source->buf_end); n = MIN(remaining_samples, nmax); - nmax = audio_stream_samples_without_wrap_s16(sink, y); + nmax = cir_buf_samples_without_wrap_s16(y, sink->buf_end); n = MIN(n, nmax); memcpy_s(y, n * sizeof(int16_t), x, n * sizeof(int16_t)); remaining_samples -= n; - x = audio_stream_wrap(source, x + n); - y = audio_stream_wrap(sink, y + n); + x = cir_buf_wrap(x + n, source->buf_start, source->buf_end); + y = cir_buf_wrap(y + n, sink->buf_start, sink->buf_end); } } #endif /* CONFIG_FORMAT_S16LE */ diff --git a/src/audio/volume/volume_generic_with_peakvol.c b/src/audio/volume/volume_generic_with_peakvol.c index 106e1d3c7bdd..dbca458ed4eb 100644 --- a/src/audio/volume/volume_generic_with_peakvol.c +++ b/src/audio/volume/volume_generic_with_peakvol.c @@ -52,30 +52,24 @@ static inline int32_t vol_mult_s24_to_s24(int32_t x, int32_t vol) * Copy and scale volume from 24/32 bit source buffer * to 24/32 bit destination buffer. */ -static void vol_s24_to_s24(struct processing_module *mod, struct input_stream_buffer *bsource, - struct output_stream_buffer *bsink, uint32_t frames, - uint32_t attenuation) +static void vol_s24_to_s24(struct processing_module *mod, struct cir_buf_source *source, + struct cir_buf_sink *sink, uint32_t frames, uint32_t attenuation) { struct vol_data *cd = module_get_private_data(mod); - struct audio_stream *source = bsource->data; - struct audio_stream *sink = bsink->data; - int32_t vol; - int32_t *x, *x0; + const int32_t *x, *x0; int32_t *y, *y0; + int32_t vol; + int32_t tmp; int nmax, n, i, j; - const int nch = audio_stream_get_channels(source); + const int nch = cd->channels; int remaining_samples = frames * nch; - int32_t tmp; - - x = audio_stream_wrap(source, (char *)audio_stream_get_rptr(source) + bsource->consumed); - y = audio_stream_wrap(sink, (char *)audio_stream_get_wptr(sink) + bsink->size); - bsource->consumed += VOL_S32_SAMPLES_TO_BYTES(remaining_samples); - bsink->size += VOL_S32_SAMPLES_TO_BYTES(remaining_samples); + x = source->ptr; + y = sink->ptr; while (remaining_samples) { - nmax = audio_stream_samples_without_wrap_s24(source, x); + nmax = cir_buf_samples_without_wrap_s32(x, source->buf_end); n = MIN(remaining_samples, nmax); - nmax = audio_stream_samples_without_wrap_s24(sink, y); + nmax = cir_buf_samples_without_wrap_s32(y, sink->buf_end); n = MIN(n, nmax); for (j = 0; j < nch; j++) { x0 = x + j; @@ -90,8 +84,8 @@ static void vol_s24_to_s24(struct processing_module *mod, struct input_stream_bu cd->peak_regs.peak_meter[j] = MAX(tmp, cd->peak_regs.peak_meter[j]); } remaining_samples -= n; - x = audio_stream_wrap(source, x + n); - y = audio_stream_wrap(sink, y + n); + x = cir_buf_wrap(x + n, source->buf_start, source->buf_end); + y = cir_buf_wrap(y + n, sink->buf_start, sink->buf_end); } } @@ -107,29 +101,24 @@ static void vol_s24_to_s24(struct processing_module *mod, struct input_stream_bu * to 24/32 bit destination buffer. */ static void vol_passthrough_s24_to_s24(struct processing_module *mod, - struct input_stream_buffer *bsource, - struct output_stream_buffer *bsink, uint32_t frames, + struct cir_buf_source *source, + struct cir_buf_sink *sink, uint32_t frames, uint32_t attenuation) { struct vol_data *cd = module_get_private_data(mod); - struct audio_stream *source = bsource->data; - struct audio_stream *sink = bsink->data; - int32_t *x, *x0; + const int32_t *x, *x0; int32_t *y, *y0; + int32_t tmp; int nmax, n, i, j; - const int nch = audio_stream_get_channels(source); + const int nch = cd->channels; int remaining_samples = frames * nch; - int32_t tmp; - - x = audio_stream_wrap(source, (char *)audio_stream_get_rptr(source) + bsource->consumed); - y = audio_stream_wrap(sink, (char *)audio_stream_get_wptr(sink) + bsink->size); - bsource->consumed += VOL_S32_SAMPLES_TO_BYTES(remaining_samples); - bsink->size += VOL_S32_SAMPLES_TO_BYTES(remaining_samples); + x = source->ptr; + y = sink->ptr; while (remaining_samples) { - nmax = audio_stream_samples_without_wrap_s24(source, x); + nmax = cir_buf_samples_without_wrap_s32(x, source->buf_end); n = MIN(remaining_samples, nmax); - nmax = audio_stream_samples_without_wrap_s24(sink, y); + nmax = cir_buf_samples_without_wrap_s32(y, sink->buf_end); n = MIN(n, nmax); for (j = 0; j < nch; j++) { x0 = x + j; @@ -143,8 +132,8 @@ static void vol_passthrough_s24_to_s24(struct processing_module *mod, cd->peak_regs.peak_meter[j] = MAX(tmp, cd->peak_regs.peak_meter[j]); } remaining_samples -= n; - x = audio_stream_wrap(source, x + n); - y = audio_stream_wrap(sink, y + n); + x = cir_buf_wrap(x + n, source->buf_start, source->buf_end); + y = cir_buf_wrap(y + n, sink->buf_start, sink->buf_end); } } #endif /* CONFIG_FORMAT_S24LE */ @@ -161,29 +150,24 @@ static void vol_passthrough_s24_to_s24(struct processing_module *mod, * Copy and scale volume from 32 bit source buffer * to 32 bit destination buffer. */ -static void vol_s32_to_s32(struct processing_module *mod, struct input_stream_buffer *bsource, - struct output_stream_buffer *bsink, uint32_t frames, - uint32_t attenuation) +static void vol_s32_to_s32(struct processing_module *mod, struct cir_buf_source *source, + struct cir_buf_sink *sink, uint32_t frames, uint32_t attenuation) { struct vol_data *cd = module_get_private_data(mod); - struct audio_stream *source = bsource->data; - struct audio_stream *sink = bsink->data; - int32_t vol; - int32_t *x, *x0; + const int32_t *x, *x0; int32_t *y, *y0; + int32_t vol; + int32_t tmp; int nmax, n, i, j; - const int nch = audio_stream_get_channels(source); + const int nch = cd->channels; int remaining_samples = frames * nch; - int32_t tmp; - x = audio_stream_wrap(source, (char *)audio_stream_get_rptr(source) + bsource->consumed); - y = audio_stream_wrap(sink, (char *)audio_stream_get_wptr(sink) + bsink->size); - bsource->consumed += VOL_S32_SAMPLES_TO_BYTES(remaining_samples); - bsink->size += VOL_S32_SAMPLES_TO_BYTES(remaining_samples); + x = source->ptr; + y = sink->ptr; while (remaining_samples) { - nmax = audio_stream_samples_without_wrap_s32(source, x); + nmax = cir_buf_samples_without_wrap_s32(x, source->buf_end); n = MIN(remaining_samples, nmax); - nmax = audio_stream_samples_without_wrap_s32(sink, y); + nmax = cir_buf_samples_without_wrap_s32(y, sink->buf_end); n = MIN(n, nmax); /* Note: on Xtensa processing one channel volume at time performed slightly * better than simpler interleaved code version (average 19 us vs. 20 us). @@ -202,8 +186,8 @@ static void vol_s32_to_s32(struct processing_module *mod, struct input_stream_bu cd->peak_regs.peak_meter[j] = MAX(tmp, cd->peak_regs.peak_meter[j]); } remaining_samples -= n; - x = audio_stream_wrap(source, x + n); - y = audio_stream_wrap(sink, y + n); + x = cir_buf_wrap(x + n, source->buf_start, source->buf_end); + y = cir_buf_wrap(y + n, sink->buf_start, sink->buf_end); } } @@ -219,28 +203,24 @@ static void vol_s32_to_s32(struct processing_module *mod, struct input_stream_bu * to 32 bit destination buffer. */ static void vol_passthrough_s32_to_s32(struct processing_module *mod, - struct input_stream_buffer *bsource, - struct output_stream_buffer *bsink, uint32_t frames, + struct cir_buf_source *source, + struct cir_buf_sink *sink, uint32_t frames, uint32_t attenuation) { struct vol_data *cd = module_get_private_data(mod); - struct audio_stream *source = bsource->data; - struct audio_stream *sink = bsink->data; - int32_t *x, *x0; + const int32_t *x, *x0; int32_t *y, *y0; + int32_t tmp; int nmax, n, i, j; - const int nch = audio_stream_get_channels(source); + const int nch = cd->channels; int remaining_samples = frames * nch; - int32_t tmp; - x = audio_stream_wrap(source, (char *)audio_stream_get_rptr(source) + bsource->consumed); - y = audio_stream_wrap(sink, (char *)audio_stream_get_wptr(sink) + bsink->size); - bsource->consumed += VOL_S32_SAMPLES_TO_BYTES(remaining_samples); - bsink->size += VOL_S32_SAMPLES_TO_BYTES(remaining_samples); + x = source->ptr; + y = sink->ptr; while (remaining_samples) { - nmax = audio_stream_samples_without_wrap_s32(source, x); + nmax = cir_buf_samples_without_wrap_s32(x, source->buf_end); n = MIN(remaining_samples, nmax); - nmax = audio_stream_samples_without_wrap_s32(sink, y); + nmax = cir_buf_samples_without_wrap_s32(y, sink->buf_end); n = MIN(n, nmax); /* Note: on Xtensa processing one channel volume at time performed slightly * better than simpler interleaved code version (average 19 us vs. 20 us). @@ -257,8 +237,8 @@ static void vol_passthrough_s32_to_s32(struct processing_module *mod, cd->peak_regs.peak_meter[j] = MAX(tmp, cd->peak_regs.peak_meter[j]); } remaining_samples -= n; - x = audio_stream_wrap(source, x + n); - y = audio_stream_wrap(sink, y + n); + x = cir_buf_wrap(x + n, source->buf_start, source->buf_end); + y = cir_buf_wrap(y + n, sink->buf_start, sink->buf_end); } } #endif /* CONFIG_FORMAT_S32LE */ @@ -275,30 +255,24 @@ static void vol_passthrough_s32_to_s32(struct processing_module *mod, * Copy and scale volume from 16 bit source buffer * to 16 bit destination buffer. */ -static void vol_s16_to_s16(struct processing_module *mod, struct input_stream_buffer *bsource, - struct output_stream_buffer *bsink, uint32_t frames, - uint32_t attenuation) +static void vol_s16_to_s16(struct processing_module *mod, struct cir_buf_source *source, + struct cir_buf_sink *sink, uint32_t frames, uint32_t attenuation) { struct vol_data *cd = module_get_private_data(mod); - struct audio_stream *source = bsource->data; - struct audio_stream *sink = bsink->data; - int32_t vol; - int16_t *x, *x0; + const int16_t *x, *x0; int16_t *y, *y0; + int32_t vol; + uint32_t tmp; int nmax, n, i, j; - const int nch = audio_stream_get_channels(source); + const int nch = cd->channels; int remaining_samples = frames * nch; - uint32_t tmp; - - x = audio_stream_wrap(source, (char *)audio_stream_get_rptr(source) + bsource->consumed); - y = audio_stream_wrap(sink, (char *)audio_stream_get_wptr(sink) + bsink->size); - bsource->consumed += VOL_S16_SAMPLES_TO_BYTES(remaining_samples); - bsink->size += VOL_S16_SAMPLES_TO_BYTES(remaining_samples); + x = source->ptr; + y = sink->ptr; while (remaining_samples) { - nmax = audio_stream_samples_without_wrap_s16(source, x); + nmax = cir_buf_samples_without_wrap_s16(x, source->buf_end); n = MIN(remaining_samples, nmax); - nmax = audio_stream_samples_without_wrap_s16(sink, y); + nmax = cir_buf_samples_without_wrap_s16(y, sink->buf_end); n = MIN(n, nmax); for (j = 0; j < nch; j++) { x0 = x + j; @@ -314,8 +288,8 @@ static void vol_s16_to_s16(struct processing_module *mod, struct input_stream_bu cd->peak_regs.peak_meter[j] = MAX(tmp, cd->peak_regs.peak_meter[j]); } remaining_samples -= n; - x = audio_stream_wrap(source, x + n); - y = audio_stream_wrap(sink, y + n); + x = cir_buf_wrap(x + n, source->buf_start, source->buf_end); + y = cir_buf_wrap(y + n, sink->buf_start, sink->buf_end); } } @@ -331,29 +305,24 @@ static void vol_s16_to_s16(struct processing_module *mod, struct input_stream_bu * to 16 bit destination buffer. */ static void vol_passthrough_s16_to_s16(struct processing_module *mod, - struct input_stream_buffer *bsource, - struct output_stream_buffer *bsink, uint32_t frames, + struct cir_buf_source *source, + struct cir_buf_sink *sink, uint32_t frames, uint32_t attenuation) { struct vol_data *cd = module_get_private_data(mod); - struct audio_stream *source = bsource->data; - struct audio_stream *sink = bsink->data; - int16_t *x, *x0; + const int16_t *x, *x0; int16_t *y, *y0; + uint32_t tmp; int nmax, n, i, j; - const int nch = audio_stream_get_channels(source); + const int nch = cd->channels; int remaining_samples = frames * nch; - uint32_t tmp; - - x = audio_stream_wrap(source, (char *)audio_stream_get_rptr(source) + bsource->consumed); - y = audio_stream_wrap(sink, (char *)audio_stream_get_wptr(sink) + bsink->size); - bsource->consumed += VOL_S16_SAMPLES_TO_BYTES(remaining_samples); - bsink->size += VOL_S16_SAMPLES_TO_BYTES(remaining_samples); + x = source->ptr; + y = sink->ptr; while (remaining_samples) { - nmax = audio_stream_samples_without_wrap_s16(source, x); + nmax = cir_buf_samples_without_wrap_s16(x, source->buf_end); n = MIN(remaining_samples, nmax); - nmax = audio_stream_samples_without_wrap_s16(sink, y); + nmax = cir_buf_samples_without_wrap_s16(y, sink->buf_end); n = MIN(n, nmax); for (j = 0; j < nch; j++) { x0 = x + j; @@ -367,8 +336,8 @@ static void vol_passthrough_s16_to_s16(struct processing_module *mod, cd->peak_regs.peak_meter[j] = MAX(tmp, cd->peak_regs.peak_meter[j]); } remaining_samples -= n; - x = audio_stream_wrap(source, x + n); - y = audio_stream_wrap(sink, y + n); + x = cir_buf_wrap(x + n, source->buf_start, source->buf_end); + y = cir_buf_wrap(y + n, sink->buf_start, sink->buf_end); } } #endif diff --git a/src/audio/volume/volume_hifi3.c b/src/audio/volume/volume_hifi3.c index afbb8ff44c02..1956993f7282 100644 --- a/src/audio/volume/volume_hifi3.c +++ b/src/audio/volume/volume_hifi3.c @@ -56,13 +56,11 @@ static void vol_store_gain(struct vol_data *cd, const int channels_count) * \param[in] frames Number of frames to process. * \param[in] attenuation factor for peakmeter adjustment (unused) */ -static void vol_s24_to_s24_s32(struct processing_module *mod, struct input_stream_buffer *bsource, - struct output_stream_buffer *bsink, uint32_t frames, +static void vol_s24_to_s24_s32(struct processing_module *mod, struct cir_buf_source *source, + struct cir_buf_sink *sink, uint32_t frames, uint32_t attenuation) { struct vol_data *cd = module_get_private_data(mod); - struct audio_stream *source = bsource->data; - struct audio_stream *sink = bsink->data; ae_f32x2 in_sample; ae_f32x2 out_sample; ae_f32x2 volume; @@ -72,11 +70,9 @@ static void vol_s24_to_s24_s32(struct processing_module *mod, struct input_strea ae_f32x2 *vol; ae_valign inu; ae_valign outu = AE_ZALIGN64(); - ae_f32x2 *in = (ae_f32x2 *)audio_stream_wrap(source, (char *)audio_stream_get_rptr(source) - + bsource->consumed); - ae_f32x2 *out = (ae_f32x2 *)audio_stream_wrap(sink, (char *)audio_stream_get_wptr(sink) - + bsink->size); - const int channels_count = audio_stream_get_channels(sink); + const ae_f32x2 *in = source->ptr; + ae_f32x2 *out = sink->ptr; + const int channels_count = cd->channels; const int inc = sizeof(ae_f32x2); int samples = channels_count * frames; @@ -94,13 +90,10 @@ static void vol_s24_to_s24_s32(struct processing_module *mod, struct input_strea AE_SETCBEGIN0(buf); AE_SETCEND0(buf_end); - bsource->consumed += VOL_S32_SAMPLES_TO_BYTES(samples); - bsink->size += VOL_S32_SAMPLES_TO_BYTES(samples); - while (samples) { - m = audio_stream_samples_without_wrap_s24(source, in); + m = cir_buf_samples_without_wrap_s32(in, source->buf_end); n = MIN(m, samples); - m = audio_stream_samples_without_wrap_s24(sink, out); + m = cir_buf_samples_without_wrap_s32(out, sink->buf_end); n = MIN(m, n); inu = AE_LA64_PP(in); /* process two continuous sample data once */ @@ -130,8 +123,8 @@ static void vol_s24_to_s24_s32(struct processing_module *mod, struct input_strea } AE_SA64POS_FP(outu, out); samples -= n; - in = audio_stream_wrap(source, in); - out = audio_stream_wrap(sink, out); + in = cir_buf_wrap(in, source->buf_start, source->buf_end); + out = cir_buf_wrap(out, sink->buf_start, sink->buf_end); } } @@ -144,30 +137,24 @@ static void vol_s24_to_s24_s32(struct processing_module *mod, struct input_strea * \param[in] attenuation factor for peakmeter adjustment (unused) */ static void vol_passthrough_s24_to_s24_s32(struct processing_module *mod, - struct input_stream_buffer *bsource, - struct output_stream_buffer *bsink, uint32_t frames, + struct cir_buf_source *source, + struct cir_buf_sink *sink, uint32_t frames, uint32_t attenuation) { - struct audio_stream *source = bsource->data; - struct audio_stream *sink = bsink->data; + struct vol_data *cd = module_get_private_data(mod); ae_f32x2 in_sample; int i, n, m; ae_valign inu; ae_valign outu = AE_ZALIGN64(); - ae_f32x2 *in = (ae_f32x2 *)audio_stream_wrap(source, (char *)audio_stream_get_rptr(source) - + bsource->consumed); - ae_f32x2 *out = (ae_f32x2 *)audio_stream_wrap(sink, (char *)audio_stream_get_wptr(sink) - + bsink->size); - const int channels_count = audio_stream_get_channels(sink); + const ae_f32x2 *in = source->ptr; + ae_f32x2 *out = sink->ptr; + const int channels_count = cd->channels; int samples = channels_count * frames; - bsource->consumed += VOL_S32_SAMPLES_TO_BYTES(samples); - bsink->size += VOL_S32_SAMPLES_TO_BYTES(samples); - while (samples) { - m = audio_stream_samples_without_wrap_s24(source, in); + m = cir_buf_samples_without_wrap_s32(in, source->buf_end); n = MIN(m, samples); - m = audio_stream_samples_without_wrap_s24(sink, out); + m = cir_buf_samples_without_wrap_s32(out, sink->buf_end); n = MIN(m, n); inu = AE_LA64_PP(in); /* process two continuous sample data once */ @@ -179,8 +166,8 @@ static void vol_passthrough_s24_to_s24_s32(struct processing_module *mod, } AE_SA64POS_FP(outu, out); samples -= n; - in = audio_stream_wrap(source, in); - out = audio_stream_wrap(sink, out); + in = cir_buf_wrap(in, source->buf_start, source->buf_end); + out = cir_buf_wrap(out, sink->buf_start, sink->buf_end); } } #endif /* CONFIG_FORMAT_S24LE */ @@ -194,13 +181,11 @@ static void vol_passthrough_s24_to_s24_s32(struct processing_module *mod, * \param[in] frames Number of frames to process. * \param[in] attenuation factor for peakmeter adjustment (unused) */ -static void vol_s32_to_s24_s32(struct processing_module *mod, struct input_stream_buffer *bsource, - struct output_stream_buffer *bsink, uint32_t frames, +static void vol_s32_to_s24_s32(struct processing_module *mod, struct cir_buf_source *source, + struct cir_buf_sink *sink, uint32_t frames, uint32_t attenuation) { struct vol_data *cd = module_get_private_data(mod); - struct audio_stream *source = bsource->data; - struct audio_stream *sink = bsink->data; ae_f32x2 in_sample; ae_f32x2 out_sample; ae_f32x2 volume; @@ -210,13 +195,11 @@ static void vol_s32_to_s24_s32(struct processing_module *mod, struct input_strea ae_f32x2 *vol; ae_valign inu; ae_valign outu = AE_ZALIGN64(); - const int channels_count = audio_stream_get_channels(sink); + const int channels_count = cd->channels; const int inc = sizeof(ae_f32x2); int samples = channels_count * frames; - ae_f32x2 *in = (ae_f32x2 *)audio_stream_wrap(source, (char *)audio_stream_get_rptr(source) - + bsource->consumed); - ae_f32x2 *out = (ae_f32x2 *)audio_stream_wrap(sink, (char *)audio_stream_get_wptr(sink) - + bsink->size); + const ae_f32x2 *in = source->ptr; + ae_f32x2 *out = sink->ptr; /* to ensure the address is 8-byte aligned and avoid risk of * error loading of volume gain while the cd->vol would be set @@ -232,13 +215,10 @@ static void vol_s32_to_s24_s32(struct processing_module *mod, struct input_strea AE_SETCBEGIN0(buf); AE_SETCEND0(buf_end); - bsource->consumed += VOL_S32_SAMPLES_TO_BYTES(samples); - bsink->size += VOL_S32_SAMPLES_TO_BYTES(samples); - while (samples) { - m = audio_stream_samples_without_wrap_s32(source, in); + m = cir_buf_samples_without_wrap_s32(in, source->buf_end); n = MIN(m, samples); - m = audio_stream_samples_without_wrap_s32(sink, out); + m = cir_buf_samples_without_wrap_s32(out, sink->buf_end); n = MIN(m, n); inu = AE_LA64_PP(in); /* process two continuous sample data once */ @@ -266,8 +246,8 @@ static void vol_s32_to_s24_s32(struct processing_module *mod, struct input_strea } AE_SA64POS_FP(outu, out); samples -= n; - in = audio_stream_wrap(source, in); - out = audio_stream_wrap(sink, out); + in = cir_buf_wrap(in, source->buf_start, source->buf_end); + out = cir_buf_wrap(out, sink->buf_start, sink->buf_end); } } @@ -280,30 +260,24 @@ static void vol_s32_to_s24_s32(struct processing_module *mod, struct input_strea * \param[in] attenuation factor for peakmeter adjustment (unused) */ static void vol_passthrough_s32_to_s24_s32(struct processing_module *mod, - struct input_stream_buffer *bsource, - struct output_stream_buffer *bsink, uint32_t frames, + struct cir_buf_source *source, + struct cir_buf_sink *sink, uint32_t frames, uint32_t attenuation) { - struct audio_stream *source = bsource->data; - struct audio_stream *sink = bsink->data; + struct vol_data *cd = module_get_private_data(mod); ae_f32x2 in_sample; int i, n, m; ae_valign inu; ae_valign outu = AE_ZALIGN64(); - const int channels_count = audio_stream_get_channels(sink); + const int channels_count = cd->channels; int samples = channels_count * frames; - ae_f32x2 *in = (ae_f32x2 *)audio_stream_wrap(source, (char *)audio_stream_get_rptr(source) - + bsource->consumed); - ae_f32x2 *out = (ae_f32x2 *)audio_stream_wrap(sink, (char *)audio_stream_get_wptr(sink) - + bsink->size); - - bsource->consumed += VOL_S32_SAMPLES_TO_BYTES(samples); - bsink->size += VOL_S32_SAMPLES_TO_BYTES(samples); + const ae_f32x2 *in = source->ptr; + ae_f32x2 *out = sink->ptr; while (samples) { - m = audio_stream_samples_without_wrap_s32(source, in); + m = cir_buf_samples_without_wrap_s32(in, source->buf_end); n = MIN(m, samples); - m = audio_stream_samples_without_wrap_s32(sink, out); + m = cir_buf_samples_without_wrap_s32(out, sink->buf_end); n = MIN(m, n); inu = AE_LA64_PP(in); /* process two continuous sample data once */ @@ -314,8 +288,8 @@ static void vol_passthrough_s32_to_s24_s32(struct processing_module *mod, } AE_SA64POS_FP(outu, out); samples -= n; - in = audio_stream_wrap(source, in); - out = audio_stream_wrap(sink, out); + in = cir_buf_wrap(in, source->buf_start, source->buf_end); + out = cir_buf_wrap(out, sink->buf_start, sink->buf_end); } } #endif /* CONFIG_FORMAT_S32LE */ @@ -329,13 +303,11 @@ static void vol_passthrough_s32_to_s24_s32(struct processing_module *mod, * \param[in] frames Number of frames to process. * \param[in] attenuation factor for peakmeter adjustment (unused) */ -static void vol_s16_to_s16(struct processing_module *mod, struct input_stream_buffer *bsource, - struct output_stream_buffer *bsink, uint32_t frames, +static void vol_s16_to_s16(struct processing_module *mod, struct cir_buf_source *source, + struct cir_buf_sink *sink, uint32_t frames, uint32_t attenuation) { struct vol_data *cd = module_get_private_data(mod); - struct audio_stream *source = bsource->data; - struct audio_stream *sink = bsink->data; ae_f32x2 volume0; ae_f32x2 volume1; ae_f32x2 out_sample0; @@ -348,11 +320,9 @@ static void vol_s16_to_s16(struct processing_module *mod, struct input_stream_bu ae_f32x2 *vol; ae_valign inu; ae_valign outu = AE_ZALIGN64(); - ae_f16x4 *in = (ae_f16x4 *)audio_stream_wrap(source, (char *)audio_stream_get_rptr(source) - + bsource->consumed); - ae_f16x4 *out = (ae_f16x4 *)audio_stream_wrap(sink, (char *)audio_stream_get_wptr(sink) - + bsink->size); - const int channels_count = audio_stream_get_channels(sink); + const ae_f16x4 *in = source->ptr; + ae_f16x4 *out = sink->ptr; + const int channels_count = cd->channels; const int inc = sizeof(ae_f32x2); int samples = channels_count * frames; @@ -372,9 +342,9 @@ static void vol_s16_to_s16(struct processing_module *mod, struct input_stream_bu AE_SETCEND0(buf_end); while (samples) { - m = audio_stream_samples_without_wrap_s16(source, in); + m = cir_buf_samples_without_wrap_s16(in, source->buf_end); n = MIN(m, samples); - m = audio_stream_samples_without_wrap_s16(sink, out); + m = cir_buf_samples_without_wrap_s16(out, sink->buf_end); n = MIN(m, n); inu = AE_LA64_PP(in); for (i = 0; i < n; i += 4) { @@ -414,10 +384,8 @@ static void vol_s16_to_s16(struct processing_module *mod, struct input_stream_bu } AE_SA64POS_FP(outu, out); samples -= n; - bsource->consumed += VOL_S16_SAMPLES_TO_BYTES(n); - bsink->size += VOL_S16_SAMPLES_TO_BYTES(n); - in = audio_stream_wrap(source, in); - out = audio_stream_wrap(sink, out); + in = cir_buf_wrap(in, source->buf_start, source->buf_end); + out = cir_buf_wrap(out, sink->buf_start, sink->buf_end); } } @@ -430,27 +398,24 @@ static void vol_s16_to_s16(struct processing_module *mod, struct input_stream_bu * \param[in] attenuation factor for peakmeter adjustment (unused) */ static void vol_passthrough_s16_to_s16(struct processing_module *mod, - struct input_stream_buffer *bsource, - struct output_stream_buffer *bsink, uint32_t frames, + struct cir_buf_source *source, + struct cir_buf_sink *sink, uint32_t frames, uint32_t attenuation) { - struct audio_stream *source = bsource->data; - struct audio_stream *sink = bsink->data; + struct vol_data *cd = module_get_private_data(mod); ae_f16x4 in_sample; int i, n, m; ae_valign inu; ae_valign outu = AE_ZALIGN64(); - ae_f16x4 *in = (ae_f16x4 *)audio_stream_wrap(source, (char *)audio_stream_get_rptr(source) - + bsource->consumed); - ae_f16x4 *out = (ae_f16x4 *)audio_stream_wrap(sink, (char *)audio_stream_get_wptr(sink) - + bsink->size); - const int channels_count = audio_stream_get_channels(sink); + const ae_f16x4 *in = source->ptr; + ae_f16x4 *out = sink->ptr; + const int channels_count = cd->channels; int samples = channels_count * frames; while (samples) { - m = audio_stream_samples_without_wrap_s16(source, in); + m = cir_buf_samples_without_wrap_s16(in, source->buf_end); n = MIN(m, samples); - m = audio_stream_samples_without_wrap_s16(sink, out); + m = cir_buf_samples_without_wrap_s16(out, sink->buf_end); n = MIN(m, n); inu = AE_LA64_PP(in); for (i = 0; i < n; i += 4) { @@ -460,10 +425,8 @@ static void vol_passthrough_s16_to_s16(struct processing_module *mod, } AE_SA64POS_FP(outu, out); samples -= n; - bsource->consumed += VOL_S16_SAMPLES_TO_BYTES(n); - bsink->size += VOL_S16_SAMPLES_TO_BYTES(n); - in = audio_stream_wrap(source, in); - out = audio_stream_wrap(sink, out); + in = cir_buf_wrap(in, source->buf_start, source->buf_end); + out = cir_buf_wrap(out, sink->buf_start, sink->buf_end); } } #endif /* CONFIG_FORMAT_S16LE */ diff --git a/src/audio/volume/volume_hifi3_with_peakvol.c b/src/audio/volume/volume_hifi3_with_peakvol.c index 4f89330baca7..bb38fc9bf7ef 100644 --- a/src/audio/volume/volume_hifi3_with_peakvol.c +++ b/src/audio/volume/volume_hifi3_with_peakvol.c @@ -36,34 +36,28 @@ LOG_MODULE_DECLARE(volume_hifi3, CONFIG_SOF_LOG_LEVEL); * \param[in] frames Number of frames to process. * \param[in] attenuation factor for peakmeter adjustment */ -static void vol_s24_to_s24_s32(struct processing_module *mod, struct input_stream_buffer *bsource, - struct output_stream_buffer *bsink, uint32_t frames, +static void vol_s24_to_s24_s32(struct processing_module *mod, struct cir_buf_source *source, + struct cir_buf_sink *sink, uint32_t frames, uint32_t attenuation) { struct vol_data *cd = module_get_private_data(mod); - struct audio_stream *source = bsource->data; - struct audio_stream *sink = bsink->data; ae_f32x2 in_sample; ae_f32x2 out_sample; ae_f32x2 volume; int channel, n, i, m; - ae_f32 *in0 = (ae_f32 *)audio_stream_wrap(source, (char *)audio_stream_get_rptr(source) - + bsource->consumed); - ae_f32 *out0 = (ae_f32 *)audio_stream_wrap(sink, (char *)audio_stream_get_wptr(sink) - + bsink->size); + ae_f32 *in0 = (void *)source->ptr; + ae_f32 *out0 = sink->ptr; ae_f32 *in, *out; - const int channels_count = audio_stream_get_channels(sink); + const int channels_count = cd->channels; const int inc = sizeof(ae_f32) * channels_count; int samples = channels_count * frames; ae_f32x2 peak_vol; uint32_t *peak_meter = cd->peak_regs.peak_meter; - bsource->consumed += VOL_S32_SAMPLES_TO_BYTES(samples); - bsink->size += VOL_S32_SAMPLES_TO_BYTES(samples); while (samples) { - m = audio_stream_samples_without_wrap_s32(source, in0); + m = cir_buf_samples_without_wrap_s32(in0, source->buf_end); n = MIN(m, samples); - m = audio_stream_samples_without_wrap_s32(sink, out0); + m = cir_buf_samples_without_wrap_s32(out0, sink->buf_end); n = MIN(m, n); for (channel = 0; channel < channels_count; channel++) { peak_vol = AE_ZERO32(); @@ -100,8 +94,8 @@ static void vol_s24_to_s24_s32(struct processing_module *mod, struct input_strea peak_meter[channel] = AE_MAX32(peak_vol, peak_meter[channel]); } samples -= n; - out0 = audio_stream_wrap(sink, out0 + n); - in0 = audio_stream_wrap(source, in0 + n); + out0 = cir_buf_wrap(out0 + n, sink->buf_start, sink->buf_end); + in0 = cir_buf_wrap(in0 + n, source->buf_start, source->buf_end); } } @@ -114,32 +108,26 @@ static void vol_s24_to_s24_s32(struct processing_module *mod, struct input_strea * \param[in] attenuation factor for peakmeter adjustment */ static void vol_passthrough_s24_to_s24_s32(struct processing_module *mod, - struct input_stream_buffer *bsource, - struct output_stream_buffer *bsink, uint32_t frames, + struct cir_buf_source *source, + struct cir_buf_sink *sink, uint32_t frames, uint32_t attenuation) { struct vol_data *cd = module_get_private_data(mod); - struct audio_stream *source = bsource->data; - struct audio_stream *sink = bsink->data; ae_f32x2 in_sample; int channel, n, i, m; - ae_f32 *in0 = (ae_f32 *)audio_stream_wrap(source, (char *)audio_stream_get_rptr(source) - + bsource->consumed); - ae_f32 *out0 = (ae_f32 *)audio_stream_wrap(sink, (char *)audio_stream_get_wptr(sink) - + bsink->size); + ae_f32 *in0 = (void *)source->ptr; + ae_f32 *out0 = sink->ptr; ae_f32 *in, *out; - const int channels_count = audio_stream_get_channels(sink); + const int channels_count = cd->channels; const int inc = sizeof(ae_f32) * channels_count; int samples = channels_count * frames; ae_f32x2 peak_vol; uint32_t *peak_meter = cd->peak_regs.peak_meter; - bsource->consumed += VOL_S32_SAMPLES_TO_BYTES(samples); - bsink->size += VOL_S32_SAMPLES_TO_BYTES(samples); while (samples) { - m = audio_stream_samples_without_wrap_s32(source, in0); + m = cir_buf_samples_without_wrap_s32(in0, source->buf_end); n = MIN(m, samples); - m = audio_stream_samples_without_wrap_s32(sink, out0); + m = cir_buf_samples_without_wrap_s32(out0, sink->buf_end); n = MIN(m, n); for (channel = 0; channel < channels_count; channel++) { peak_vol = AE_ZERO32(); @@ -159,8 +147,8 @@ static void vol_passthrough_s24_to_s24_s32(struct processing_module *mod, peak_meter[channel] = AE_MAX32(peak_vol, peak_meter[channel]); } samples -= n; - out0 = audio_stream_wrap(sink, out0 + n); - in0 = audio_stream_wrap(source, in0 + n); + out0 = cir_buf_wrap(out0 + n, sink->buf_start, sink->buf_end); + in0 = cir_buf_wrap(in0 + n, source->buf_start, source->buf_end); } } #endif /* CONFIG_FORMAT_S24LE */ @@ -174,34 +162,28 @@ static void vol_passthrough_s24_to_s24_s32(struct processing_module *mod, * \param[in] frames Number of frames to process. * \param[in] attenuation factor for peakmeter adjustment */ -static void vol_s32_to_s24_s32(struct processing_module *mod, struct input_stream_buffer *bsource, - struct output_stream_buffer *bsink, uint32_t frames, +static void vol_s32_to_s24_s32(struct processing_module *mod, struct cir_buf_source *source, + struct cir_buf_sink *sink, uint32_t frames, uint32_t attenuation) { struct vol_data *cd = module_get_private_data(mod); - struct audio_stream *source = bsource->data; - struct audio_stream *sink = bsink->data; ae_f32x2 in_sample; ae_f32x2 out_sample; ae_f32x2 volume; int i, n, channel, m; - const int channels_count = audio_stream_get_channels(sink); + const int channels_count = cd->channels; const int inc = sizeof(ae_f32) * channels_count; int samples = channels_count * frames; - ae_f32 *in0 = (ae_f32 *)audio_stream_wrap(source, (char *)audio_stream_get_rptr(source) - + bsource->consumed); - ae_f32 *out0 = (ae_f32 *)audio_stream_wrap(sink, (char *)audio_stream_get_wptr(sink) - + bsink->size); + ae_f32 *in0 = (void *)source->ptr; + ae_f32 *out0 = sink->ptr; ae_f32 *in, *out; ae_f32x2 peak_vol; uint32_t *peak_meter = cd->peak_regs.peak_meter; - bsource->consumed += VOL_S32_SAMPLES_TO_BYTES(samples); - bsink->size += VOL_S32_SAMPLES_TO_BYTES(samples); while (samples) { - m = audio_stream_samples_without_wrap_s32(source, in0); + m = cir_buf_samples_without_wrap_s32(in0, source->buf_end); n = MIN(m, samples); - m = audio_stream_samples_without_wrap_s32(sink, out0); + m = cir_buf_samples_without_wrap_s32(out0, sink->buf_end); n = MIN(m, n); for (channel = 0; channel < channels_count; channel++) { peak_vol = AE_ZERO32(); @@ -236,8 +218,8 @@ static void vol_s32_to_s24_s32(struct processing_module *mod, struct input_strea peak_meter[channel] = AE_MAX32(peak_vol, peak_meter[channel]); } samples -= n; - out0 = audio_stream_wrap(sink, out0 + n); - in0 = audio_stream_wrap(source, in0 + n); + out0 = cir_buf_wrap(out0 + n, sink->buf_start, sink->buf_end); + in0 = cir_buf_wrap(in0 + n, source->buf_start, source->buf_end); } } @@ -250,32 +232,26 @@ static void vol_s32_to_s24_s32(struct processing_module *mod, struct input_strea * \param[in] attenuation factor for peakmeter adjustment */ static void vol_passthrough_s32_to_s24_s32(struct processing_module *mod, - struct input_stream_buffer *bsource, - struct output_stream_buffer *bsink, uint32_t frames, + struct cir_buf_source *source, + struct cir_buf_sink *sink, uint32_t frames, uint32_t attenuation) { struct vol_data *cd = module_get_private_data(mod); - struct audio_stream *source = bsource->data; - struct audio_stream *sink = bsink->data; ae_f32x2 in_sample; int i, n, channel, m; - const int channels_count = audio_stream_get_channels(sink); + const int channels_count = cd->channels; const int inc = sizeof(ae_f32) * channels_count; int samples = channels_count * frames; - ae_f32 *in0 = (ae_f32 *)audio_stream_wrap(source, (char *)audio_stream_get_rptr(source) - + bsource->consumed); - ae_f32 *out0 = (ae_f32 *)audio_stream_wrap(sink, (char *)audio_stream_get_wptr(sink) - + bsink->size); + ae_f32 *in0 = (void *)source->ptr; + ae_f32 *out0 = sink->ptr; ae_f32 *in, *out; ae_f32x2 peak_vol; uint32_t *peak_meter = cd->peak_regs.peak_meter; - bsource->consumed += VOL_S32_SAMPLES_TO_BYTES(samples); - bsink->size += VOL_S32_SAMPLES_TO_BYTES(samples); while (samples) { - m = audio_stream_samples_without_wrap_s32(source, in0); + m = cir_buf_samples_without_wrap_s32(in0, source->buf_end); n = MIN(m, samples); - m = audio_stream_samples_without_wrap_s32(sink, out0); + m = cir_buf_samples_without_wrap_s32(out0, sink->buf_end); n = MIN(m, n); for (channel = 0; channel < channels_count; channel++) { peak_vol = AE_ZERO32(); @@ -295,8 +271,8 @@ static void vol_passthrough_s32_to_s24_s32(struct processing_module *mod, peak_meter[channel] = AE_MAX32(peak_vol, peak_meter[channel]); } samples -= n; - out0 = audio_stream_wrap(sink, out0 + n); - in0 = audio_stream_wrap(source, in0 + n); + out0 = cir_buf_wrap(out0 + n, sink->buf_start, sink->buf_end); + in0 = cir_buf_wrap(in0 + n, source->buf_start, source->buf_end); } } #endif /* CONFIG_FORMAT_S32LE */ @@ -310,13 +286,11 @@ static void vol_passthrough_s32_to_s24_s32(struct processing_module *mod, * \param[in] frames Number of frames to process. * \param[in] attenuation factor for peakmeter adjustment (unused for 16bit) */ -static void vol_s16_to_s16(struct processing_module *mod, struct input_stream_buffer *bsource, - struct output_stream_buffer *bsink, uint32_t frames, +static void vol_s16_to_s16(struct processing_module *mod, struct cir_buf_source *source, + struct cir_buf_sink *sink, uint32_t frames, uint32_t attenuation) { struct vol_data *cd = module_get_private_data(mod); - struct audio_stream *source = bsource->data; - struct audio_stream *sink = bsink->data; ae_f32x2 volume; ae_f32x2 out_sample0; ae_f16x4 in_sample; @@ -324,20 +298,18 @@ static void vol_s16_to_s16(struct processing_module *mod, struct input_stream_bu int i, n, channel, m; ae_f16 *in; ae_f16 *out; - ae_f16 *in0 = (ae_f16 *)audio_stream_wrap(source, (char *)audio_stream_get_rptr(source) - + bsource->consumed); - ae_f16 *out0 = (ae_f16 *)audio_stream_wrap(sink, (char *)audio_stream_get_wptr(sink) - + bsink->size); - const int channels_count = audio_stream_get_channels(sink); + ae_f16 *in0 = (void *)source->ptr; + ae_f16 *out0 = sink->ptr; + const int channels_count = cd->channels; const int inc = sizeof(ae_f16) * channels_count; int samples = channels_count * frames; ae_f32x2 peak_vol; uint32_t *peak_meter = cd->peak_regs.peak_meter; while (samples) { - m = audio_stream_samples_without_wrap_s16(source, in0); + m = cir_buf_samples_without_wrap_s16(in0, source->buf_end); n = MIN(m, samples); - m = audio_stream_samples_without_wrap_s16(sink, out0); + m = cir_buf_samples_without_wrap_s16(out0, sink->buf_end); n = MIN(m, n); for (channel = 0; channel < channels_count; channel++) { peak_vol = AE_ZERO32(); @@ -377,11 +349,9 @@ static void vol_s16_to_s16(struct processing_module *mod, struct input_stream_bu peak_vol = AE_SLAA32(peak_vol, PEAK_16S_32C_ADJUST); peak_meter[channel] = AE_MAX32(peak_vol, peak_meter[channel]); } - out0 = audio_stream_wrap(sink, out0 + n); - in0 = audio_stream_wrap(source, in0 + n); + out0 = cir_buf_wrap(out0 + n, sink->buf_start, sink->buf_end); + in0 = cir_buf_wrap(in0 + n, source->buf_start, source->buf_end); samples -= n; - bsource->consumed += VOL_S16_SAMPLES_TO_BYTES(n); - bsink->size += VOL_S16_SAMPLES_TO_BYTES(n); } } @@ -394,31 +364,27 @@ static void vol_s16_to_s16(struct processing_module *mod, struct input_stream_bu * \param[in] attenuation factor for peakmeter adjustment (unused for 16bit) */ static void vol_passthrough_s16_to_s16(struct processing_module *mod, - struct input_stream_buffer *bsource, - struct output_stream_buffer *bsink, uint32_t frames, + struct cir_buf_source *source, + struct cir_buf_sink *sink, uint32_t frames, uint32_t attenuation) { struct vol_data *cd = module_get_private_data(mod); - struct audio_stream *source = bsource->data; - struct audio_stream *sink = bsink->data; ae_f16x4 in_sample; int i, n, channel, m; ae_f16 *in; ae_f16 *out; - ae_f16 *in0 = (ae_f16 *)audio_stream_wrap(source, (char *)audio_stream_get_rptr(source) - + bsource->consumed); - ae_f16 *out0 = (ae_f16 *)audio_stream_wrap(sink, (char *)audio_stream_get_wptr(sink) - + bsink->size); - const int channels_count = audio_stream_get_channels(sink); + ae_f16 *in0 = (void *)source->ptr; + ae_f16 *out0 = sink->ptr; + const int channels_count = cd->channels; const int inc = sizeof(ae_f16) * channels_count; int samples = channels_count * frames; ae_f32x2 peak_vol; uint32_t *peak_meter = cd->peak_regs.peak_meter; while (samples) { - m = audio_stream_samples_without_wrap_s16(source, in0); + m = cir_buf_samples_without_wrap_s16(in0, source->buf_end); n = MIN(m, samples); - m = audio_stream_samples_without_wrap_s16(sink, out0); + m = cir_buf_samples_without_wrap_s16(out0, sink->buf_end); n = MIN(m, n); for (channel = 0; channel < channels_count; channel++) { peak_vol = AE_ZERO32(); @@ -439,11 +405,9 @@ static void vol_passthrough_s16_to_s16(struct processing_module *mod, peak_vol = AE_SLAA32(peak_vol, PEAK_16S_32C_ADJUST); peak_meter[channel] = AE_MAX32(peak_vol, peak_meter[channel]); } - out0 = audio_stream_wrap(sink, out0 + n); - in0 = audio_stream_wrap(source, in0 + n); + out0 = cir_buf_wrap(out0 + n, sink->buf_start, sink->buf_end); + in0 = cir_buf_wrap(in0 + n, source->buf_start, source->buf_end); samples -= n; - bsource->consumed += VOL_S16_SAMPLES_TO_BYTES(n); - bsink->size += VOL_S16_SAMPLES_TO_BYTES(n); } } #endif /* CONFIG_FORMAT_S16LE */ diff --git a/src/audio/volume/volume_hifi4.c b/src/audio/volume/volume_hifi4.c index 2f879a31ec0e..44d88eda586b 100644 --- a/src/audio/volume/volume_hifi4.c +++ b/src/audio/volume/volume_hifi4.c @@ -56,13 +56,11 @@ static void vol_store_gain(struct vol_data *cd, const int channels_count) * \param[in] frames Number of frames to process. * \param[in] attenuation factor for peakmeter adjustment (unused) */ -static void vol_s24_to_s24_s32(struct processing_module *mod, struct input_stream_buffer *bsource, - struct output_stream_buffer *bsink, uint32_t frames, +static void vol_s24_to_s24_s32(struct processing_module *mod, struct cir_buf_source *source, + struct cir_buf_sink *sink, uint32_t frames, uint32_t attenuation) { struct vol_data *cd = module_get_private_data(mod); - struct audio_stream *source = bsource->data; - struct audio_stream *sink = bsink->data; ae_f32x2 in_sample; ae_f32x2 out_sample; ae_f32x2 volume; @@ -72,11 +70,9 @@ static void vol_s24_to_s24_s32(struct processing_module *mod, struct input_strea ae_f32x2 *vol; ae_valign inu; ae_valign outu = AE_ZALIGN64(); - ae_f32x2 *in = (ae_f32x2 *)audio_stream_wrap(source, (char *)audio_stream_get_rptr(source) - + bsource->consumed); - ae_f32x2 *out = (ae_f32x2 *)audio_stream_wrap(sink, (char *)audio_stream_get_wptr(sink) - + bsink->size); - const int channels_count = audio_stream_get_channels(sink); + const ae_f32x2 *in = source->ptr; + ae_f32x2 *out = sink->ptr; + const int channels_count = cd->channels; const int inc = sizeof(ae_f32x2); int samples = channels_count * frames; @@ -94,13 +90,10 @@ static void vol_s24_to_s24_s32(struct processing_module *mod, struct input_strea AE_SETCBEGIN0(buf); AE_SETCEND0(buf_end); - bsource->consumed += VOL_S32_SAMPLES_TO_BYTES(samples); - bsink->size += VOL_S32_SAMPLES_TO_BYTES(samples); - while (samples) { - m = audio_stream_samples_without_wrap_s24(source, in); + m = cir_buf_samples_without_wrap_s32(in, source->buf_end); n = MIN(m, samples); - m = audio_stream_samples_without_wrap_s24(sink, out); + m = cir_buf_samples_without_wrap_s32(out, sink->buf_end); n = MIN(m, n); inu = AE_LA64_PP(in); /* process two continuous sample data once */ @@ -130,8 +123,8 @@ static void vol_s24_to_s24_s32(struct processing_module *mod, struct input_strea } AE_SA64POS_FP(outu, out); samples -= n; - in = audio_stream_wrap(source, in); - out = audio_stream_wrap(sink, out); + in = cir_buf_wrap(in, source->buf_start, source->buf_end); + out = cir_buf_wrap(out, sink->buf_start, sink->buf_end); } } @@ -144,29 +137,23 @@ static void vol_s24_to_s24_s32(struct processing_module *mod, struct input_strea * \param[in] attenuation factor for peakmeter adjustment (unused) */ static void vol_passthrough_s24_to_s24_s32(struct processing_module *mod, - struct input_stream_buffer *bsource, - struct output_stream_buffer *bsink, uint32_t frames, + struct cir_buf_source *source, + struct cir_buf_sink *sink, uint32_t frames, uint32_t attenuation) { - struct audio_stream *source = bsource->data; - struct audio_stream *sink = bsink->data; + struct vol_data *cd = module_get_private_data(mod); ae_f32x2 in_sample; int i, n, m; ae_valign inu; ae_valign outu = AE_ZALIGN64(); - ae_f32x2 *in = (ae_f32x2 *)audio_stream_wrap(source, (char *)audio_stream_get_rptr(source) - + bsource->consumed); - ae_f32x2 *out = (ae_f32x2 *)audio_stream_wrap(sink, (char *)audio_stream_get_wptr(sink) - + bsink->size); - int samples = audio_stream_get_channels(sink) * frames; - - bsource->consumed += VOL_S32_SAMPLES_TO_BYTES(samples); - bsink->size += VOL_S32_SAMPLES_TO_BYTES(samples); + const ae_f32x2 *in = source->ptr; + ae_f32x2 *out = sink->ptr; + int samples = cd->channels * frames; while (samples) { - m = audio_stream_samples_without_wrap_s24(source, in); + m = cir_buf_samples_without_wrap_s32(in, source->buf_end); n = MIN(m, samples); - m = audio_stream_samples_without_wrap_s24(sink, out); + m = cir_buf_samples_without_wrap_s32(out, sink->buf_end); n = MIN(m, n); inu = AE_LA64_PP(in); /* process two continuous sample data once */ @@ -178,8 +165,8 @@ static void vol_passthrough_s24_to_s24_s32(struct processing_module *mod, } AE_SA64POS_FP(outu, out); samples -= n; - in = audio_stream_wrap(source, in); - out = audio_stream_wrap(sink, out); + in = cir_buf_wrap(in, source->buf_start, source->buf_end); + out = cir_buf_wrap(out, sink->buf_start, sink->buf_end); } } @@ -194,13 +181,11 @@ static void vol_passthrough_s24_to_s24_s32(struct processing_module *mod, * \param[in] frames Number of frames to process. * \param[in] attenuation factor for peakmeter adjustment (unused) */ -static void vol_s32_to_s24_s32(struct processing_module *mod, struct input_stream_buffer *bsource, - struct output_stream_buffer *bsink, uint32_t frames, +static void vol_s32_to_s24_s32(struct processing_module *mod, struct cir_buf_source *source, + struct cir_buf_sink *sink, uint32_t frames, uint32_t attenuation) { struct vol_data *cd = module_get_private_data(mod); - struct audio_stream *source = bsource->data; - struct audio_stream *sink = bsink->data; ae_f32x2 in_sample; ae_f32x2 out_sample; ae_f32x2 volume; @@ -210,13 +195,11 @@ static void vol_s32_to_s24_s32(struct processing_module *mod, struct input_strea ae_f32x2 *vol; ae_valign inu; ae_valign outu = AE_ZALIGN64(); - const int channels_count = audio_stream_get_channels(sink); + const int channels_count = cd->channels; const int inc = sizeof(ae_f32x2); int samples = channels_count * frames; - ae_f32x2 *in = (ae_f32x2 *)audio_stream_wrap(source, (char *)audio_stream_get_rptr(source) - + bsource->consumed); - ae_f32x2 *out = (ae_f32x2 *)audio_stream_wrap(sink, (char *)audio_stream_get_wptr(sink) - + bsink->size); + const ae_f32x2 *in = source->ptr; + ae_f32x2 *out = sink->ptr; /* to ensure the address is 8-byte aligned and avoid risk of * error loading of volume gain while the cd->vol would be set @@ -232,13 +215,10 @@ static void vol_s32_to_s24_s32(struct processing_module *mod, struct input_strea AE_SETCBEGIN0(buf); AE_SETCEND0(buf_end); - bsource->consumed += VOL_S32_SAMPLES_TO_BYTES(samples); - bsink->size += VOL_S32_SAMPLES_TO_BYTES(samples); - while (samples) { - m = audio_stream_samples_without_wrap_s32(source, in); + m = cir_buf_samples_without_wrap_s32(in, source->buf_end); n = MIN(m, samples); - m = audio_stream_samples_without_wrap_s32(sink, out); + m = cir_buf_samples_without_wrap_s32(out, sink->buf_end); n = MIN(m, n); inu = AE_LA64_PP(in); /* process two continuous sample data once */ @@ -268,8 +248,8 @@ static void vol_s32_to_s24_s32(struct processing_module *mod, struct input_strea } AE_SA64POS_FP(outu, out); samples -= n; - in = audio_stream_wrap(source, in); - out = audio_stream_wrap(sink, out); + in = cir_buf_wrap(in, source->buf_start, source->buf_end); + out = cir_buf_wrap(out, sink->buf_start, sink->buf_end); } } @@ -282,29 +262,24 @@ static void vol_s32_to_s24_s32(struct processing_module *mod, struct input_strea * \param[in] attenuation factor for peakmeter adjustment (unused) */ static void vol_passthrough_s32_to_s24_s32(struct processing_module *mod, - struct input_stream_buffer *bsource, - struct output_stream_buffer *bsink, uint32_t frames, + struct cir_buf_source *source, + struct cir_buf_sink *sink, uint32_t frames, uint32_t attenuation) { - struct audio_stream *source = bsource->data; - struct audio_stream *sink = bsink->data; + struct vol_data *cd = module_get_private_data(mod); ae_f32x2 in_sample; int i, n, m; ae_valign inu; ae_valign outu = AE_ZALIGN64(); - const int channels_count = audio_stream_get_channels(sink); + const int channels_count = cd->channels; int samples = channels_count * frames; - ae_f32x2 *in = (ae_f32x2 *)audio_stream_wrap(source, (char *)audio_stream_get_rptr(source) - + bsource->consumed); - ae_f32x2 *out = (ae_f32x2 *)audio_stream_wrap(sink, (char *)audio_stream_get_wptr(sink) - + bsink->size); + const ae_f32x2 *in = source->ptr; + ae_f32x2 *out = sink->ptr; - bsource->consumed += VOL_S32_SAMPLES_TO_BYTES(samples); - bsink->size += VOL_S32_SAMPLES_TO_BYTES(samples); while (samples) { - m = audio_stream_samples_without_wrap_s32(source, in); + m = cir_buf_samples_without_wrap_s32(in, source->buf_end); n = MIN(m, samples); - m = audio_stream_samples_without_wrap_s32(sink, out); + m = cir_buf_samples_without_wrap_s32(out, sink->buf_end); n = MIN(m, n); inu = AE_LA64_PP(in); /* process two continuous sample data once */ @@ -315,8 +290,8 @@ static void vol_passthrough_s32_to_s24_s32(struct processing_module *mod, } AE_SA64POS_FP(outu, out); samples -= n; - in = audio_stream_wrap(source, in); - out = audio_stream_wrap(sink, out); + in = cir_buf_wrap(in, source->buf_start, source->buf_end); + out = cir_buf_wrap(out, sink->buf_start, sink->buf_end); } } #endif /* CONFIG_FORMAT_S32LE */ @@ -330,13 +305,11 @@ static void vol_passthrough_s32_to_s24_s32(struct processing_module *mod, * \param[in] frames Number of frames to process. * \param[in] attenuation factor for peakmeter adjustment (unused) */ -static void vol_s16_to_s16(struct processing_module *mod, struct input_stream_buffer *bsource, - struct output_stream_buffer *bsink, uint32_t frames, +static void vol_s16_to_s16(struct processing_module *mod, struct cir_buf_source *source, + struct cir_buf_sink *sink, uint32_t frames, uint32_t attenuation) { struct vol_data *cd = module_get_private_data(mod); - struct audio_stream *source = bsource->data; - struct audio_stream *sink = bsink->data; ae_f32x2 volume0; ae_f32x2 volume1; ae_f32x2 out_sample0; @@ -349,11 +322,9 @@ static void vol_s16_to_s16(struct processing_module *mod, struct input_stream_bu ae_f32x2 *vol; ae_valign inu; ae_valign outu = AE_ZALIGN64(); - ae_f16x4 *in = (ae_f16x4 *)audio_stream_wrap(source, (char *)audio_stream_get_rptr(source) - + bsource->consumed); - ae_f16x4 *out = (ae_f16x4 *)audio_stream_wrap(sink, (char *)audio_stream_get_wptr(sink) - + bsink->size); - const int channels_count = audio_stream_get_channels(sink); + const ae_f16x4 *in = source->ptr; + ae_f16x4 *out = sink->ptr; + const int channels_count = cd->channels; const int inc = sizeof(ae_f32x2); int samples = channels_count * frames; @@ -373,9 +344,9 @@ static void vol_s16_to_s16(struct processing_module *mod, struct input_stream_bu AE_SETCEND0(buf_end); while (samples) { - m = audio_stream_samples_without_wrap_s16(source, in); + m = cir_buf_samples_without_wrap_s16(in, source->buf_end); n = MIN(m, samples); - m = audio_stream_samples_without_wrap_s16(sink, out); + m = cir_buf_samples_without_wrap_s16(out, sink->buf_end); n = MIN(m, n); inu = AE_LA64_PP(in); m = n >> 2; @@ -442,10 +413,8 @@ static void vol_s16_to_s16(struct processing_module *mod, struct input_stream_bu } samples -= n; - bsource->consumed += VOL_S16_SAMPLES_TO_BYTES(n); - bsink->size += VOL_S16_SAMPLES_TO_BYTES(n); - in = audio_stream_wrap(source, in); - out = audio_stream_wrap(sink, out); + in = cir_buf_wrap(in, source->buf_start, source->buf_end); + out = cir_buf_wrap(out, sink->buf_start, sink->buf_end); } } @@ -458,29 +427,24 @@ static void vol_s16_to_s16(struct processing_module *mod, struct input_stream_bu * \param[in] attenuation factor for peakmeter adjustment (unused) */ static void vol_passthrough_s16_to_s16(struct processing_module *mod, - struct input_stream_buffer *bsource, - struct output_stream_buffer *bsink, uint32_t frames, + struct cir_buf_source *source, + struct cir_buf_sink *sink, uint32_t frames, uint32_t attenuation) { - struct audio_stream *source = bsource->data; - struct audio_stream *sink = bsink->data; + struct vol_data *cd = module_get_private_data(mod); ae_f16x4 in_sample; int i, n, m, left; ae_valign inu; ae_valign outu = AE_ZALIGN64(); - ae_f16x4 *in = (ae_f16x4 *)audio_stream_wrap(source, (char *)audio_stream_get_rptr(source) - + bsource->consumed); - ae_f16x4 *out = (ae_f16x4 *)audio_stream_wrap(sink, (char *)audio_stream_get_wptr(sink) - + bsink->size); - const int channels_count = audio_stream_get_channels(sink); + const ae_f16x4 *in = source->ptr; + ae_f16x4 *out = sink->ptr; + const int channels_count = cd->channels; int samples = channels_count * frames; - bsource->consumed += VOL_S16_SAMPLES_TO_BYTES(samples); - bsink->size += VOL_S16_SAMPLES_TO_BYTES(samples); while (samples) { - m = audio_stream_samples_without_wrap_s16(source, in); + m = cir_buf_samples_without_wrap_s16(in, source->buf_end); n = MIN(m, samples); - m = audio_stream_samples_without_wrap_s16(sink, out); + m = cir_buf_samples_without_wrap_s16(out, sink->buf_end); n = MIN(m, n); inu = AE_LA64_PP(in); m = n >> 2; @@ -501,8 +465,8 @@ static void vol_passthrough_s16_to_s16(struct processing_module *mod, } samples -= n; - in = audio_stream_wrap(source, in); - out = audio_stream_wrap(sink, out); + in = cir_buf_wrap(in, source->buf_start, source->buf_end); + out = cir_buf_wrap(out, sink->buf_start, sink->buf_end); } } #endif /* CONFIG_FORMAT_S16LE */ diff --git a/src/audio/volume/volume_hifi4_with_peakvol.c b/src/audio/volume/volume_hifi4_with_peakvol.c index f3908ee1a8a0..a1d0ed70a579 100644 --- a/src/audio/volume/volume_hifi4_with_peakvol.c +++ b/src/audio/volume/volume_hifi4_with_peakvol.c @@ -49,13 +49,11 @@ static inline void vol_store_gain(struct vol_data *cd, const int channels_count) * \param[in] frames Number of frames to process. * \param[in] attenuation factor for peakmeter adjustment */ -static void vol_s24_to_s24_s32(struct processing_module *mod, struct input_stream_buffer *bsource, - struct output_stream_buffer *bsink, uint32_t frames, +static void vol_s24_to_s24_s32(struct processing_module *mod, struct cir_buf_source *source, + struct cir_buf_sink *sink, uint32_t frames, uint32_t attenuation) { struct vol_data *cd = module_get_private_data(mod); - struct audio_stream *source = bsource->data; - struct audio_stream *sink = bsink->data; ae_f32x2 in_sample; ae_f32x2 out_sample; ae_f32x2 volume; @@ -63,11 +61,9 @@ static void vol_s24_to_s24_s32(struct processing_module *mod, struct input_strea ae_f32x2 *vol; ae_valign inu; ae_valign outu = AE_ZALIGN64(); - ae_f32x2 *in = (ae_f32x2 *)audio_stream_wrap(source, (char *)audio_stream_get_rptr(source) - + bsource->consumed); - ae_f32x2 *out = (ae_f32x2 *)audio_stream_wrap(sink, (char *)audio_stream_get_wptr(sink) - + bsink->size); - const int channels_count = audio_stream_get_channels(sink); + const ae_f32x2 *in = source->ptr; + ae_f32x2 *out = sink->ptr; + const int channels_count = cd->channels; const int inc = sizeof(ae_f32x2); int samples = channels_count * frames; ae_f32x2 temp; @@ -89,13 +85,10 @@ static void vol_s24_to_s24_s32(struct processing_module *mod, struct input_strea AE_SETCBEGIN0(vol); AE_SETCEND0(cd->vol + channels_count * 2); - bsource->consumed += VOL_S32_SAMPLES_TO_BYTES(samples); - bsink->size += VOL_S32_SAMPLES_TO_BYTES(samples); - while (samples) { - m = audio_stream_samples_without_wrap_s32(source, in); + m = cir_buf_samples_without_wrap_s32(in, source->buf_end); n = MIN(m, samples); - m = audio_stream_samples_without_wrap_s16(sink, out); + m = cir_buf_samples_without_wrap_s16(out, sink->buf_end); n = MIN(m, n); inu = AE_LA64_PP(in); /* process two continuous sample data once */ @@ -128,8 +121,8 @@ static void vol_s24_to_s24_s32(struct processing_module *mod, struct input_strea } AE_SA64POS_FP(outu, out); samples -= n; - in = audio_stream_wrap(source, in); - out = audio_stream_wrap(sink, out); + in = cir_buf_wrap(in, source->buf_start, source->buf_end); + out = cir_buf_wrap(out, sink->buf_start, sink->buf_end); } for (i = 0; i < channels_count; i++) cd->peak_regs.peak_meter[i] = MAX(cd->peak_vol[i], @@ -146,23 +139,19 @@ static void vol_s24_to_s24_s32(struct processing_module *mod, struct input_strea * \param[in] attenuation factor for peakmeter adjustment */ static void vol_passthrough_s24_to_s24_s32(struct processing_module *mod, - struct input_stream_buffer *bsource, - struct output_stream_buffer *bsink, uint32_t frames, + struct cir_buf_source *source, + struct cir_buf_sink *sink, uint32_t frames, uint32_t attenuation) { struct vol_data *cd = module_get_private_data(mod); - struct audio_stream *source = bsource->data; - struct audio_stream *sink = bsink->data; ae_f32x2 in_sample; int i, n, m; ae_valign inu; ae_valign outu = AE_ZALIGN64(); - ae_f32x2 *in = (ae_f32x2 *)audio_stream_wrap(source, (char *)audio_stream_get_rptr(source) - + bsource->consumed); - ae_f32x2 *out = (ae_f32x2 *)audio_stream_wrap(sink, (char *)audio_stream_get_wptr(sink) - + bsink->size); - const int channels_count = audio_stream_get_channels(sink); + const ae_f32x2 *in = source->ptr; + ae_f32x2 *out = sink->ptr; + const int channels_count = cd->channels; const int inc = sizeof(ae_f32x2); int samples = channels_count * frames; ae_f32x2 temp; @@ -172,13 +161,10 @@ static void vol_passthrough_s24_to_s24_s32(struct processing_module *mod, AE_SETCBEGIN1(cd->peak_vol); AE_SETCEND1(cd->peak_vol + channels_count * 2); - bsource->consumed += VOL_S32_SAMPLES_TO_BYTES(samples); - bsink->size += VOL_S32_SAMPLES_TO_BYTES(samples); - while (samples) { - m = audio_stream_samples_without_wrap_s32(source, in); + m = cir_buf_samples_without_wrap_s32(in, source->buf_end); n = MIN(m, samples); - m = audio_stream_samples_without_wrap_s16(sink, out); + m = cir_buf_samples_without_wrap_s16(out, sink->buf_end); n = MIN(m, n); inu = AE_LA64_PP(in); /* process two continuous sample data once */ @@ -194,8 +180,8 @@ static void vol_passthrough_s24_to_s24_s32(struct processing_module *mod, } AE_SA64POS_FP(outu, out); samples -= n; - in = audio_stream_wrap(source, in); - out = audio_stream_wrap(sink, out); + in = cir_buf_wrap(in, source->buf_start, source->buf_end); + out = cir_buf_wrap(out, sink->buf_start, sink->buf_end); } for (i = 0; i < channels_count; i++) cd->peak_regs.peak_meter[i] = MAX(cd->peak_vol[i], @@ -213,13 +199,11 @@ static void vol_passthrough_s24_to_s24_s32(struct processing_module *mod, * \param[in] frames Number of frames to process. * \param[in] attenuation factor for peakmeter adjustment */ -static void vol_s32_to_s24_s32(struct processing_module *mod, struct input_stream_buffer *bsource, - struct output_stream_buffer *bsink, uint32_t frames, +static void vol_s32_to_s24_s32(struct processing_module *mod, struct cir_buf_source *source, + struct cir_buf_sink *sink, uint32_t frames, uint32_t attenuation) { struct vol_data *cd = module_get_private_data(mod); - struct audio_stream *source = bsource->data; - struct audio_stream *sink = bsink->data; ae_f32x2 in_sample; ae_f32x2 out_sample; ae_f32x2 volume; @@ -229,13 +213,11 @@ static void vol_s32_to_s24_s32(struct processing_module *mod, struct input_strea ae_f32x2 *vol; ae_valign inu; ae_valign outu = AE_ZALIGN64(); - const int channels_count = audio_stream_get_channels(sink); + const int channels_count = cd->channels; const int inc = sizeof(ae_f32x2); int samples = channels_count * frames; - ae_f32x2 *in = (ae_f32x2 *)audio_stream_wrap(source, (char *)audio_stream_get_rptr(source) - + bsource->consumed); - ae_f32x2 *out = (ae_f32x2 *)audio_stream_wrap(sink, (char *)audio_stream_get_wptr(sink) - + bsink->size); + const ae_f32x2 *in = source->ptr; + ae_f32x2 *out = sink->ptr; ae_f32x2 temp; ae_f32x2 *peakvol = (ae_f32x2 *)cd->peak_vol; @@ -257,13 +239,10 @@ static void vol_s32_to_s24_s32(struct processing_module *mod, struct input_strea AE_SETCBEGIN0(buf); AE_SETCEND0(buf_end); - bsource->consumed += VOL_S32_SAMPLES_TO_BYTES(samples); - bsink->size += VOL_S32_SAMPLES_TO_BYTES(samples); - while (samples) { - m = audio_stream_samples_without_wrap_s32(source, in); + m = cir_buf_samples_without_wrap_s32(in, source->buf_end); n = MIN(m, samples); - m = audio_stream_samples_without_wrap_s32(sink, out); + m = cir_buf_samples_without_wrap_s32(out, sink->buf_end); n = MIN(m, n); inu = AE_LA64_PP(in); /* process two continuous sample data once */ @@ -297,8 +276,8 @@ static void vol_s32_to_s24_s32(struct processing_module *mod, struct input_strea } AE_SA64POS_FP(outu, out); samples -= n; - in = audio_stream_wrap(source, in); - out = audio_stream_wrap(sink, out); + in = cir_buf_wrap(in, source->buf_start, source->buf_end); + out = cir_buf_wrap(out, sink->buf_start, sink->buf_end); } for (i = 0; i < channels_count; i++) cd->peak_regs.peak_meter[i] = MAX(cd->peak_vol[i], @@ -314,37 +293,30 @@ static void vol_s32_to_s24_s32(struct processing_module *mod, struct input_strea * \param[in] attenuation factor for peakmeter adjustment */ static void vol_passthrough_s32_to_s24_s32(struct processing_module *mod, - struct input_stream_buffer *bsource, - struct output_stream_buffer *bsink, uint32_t frames, + struct cir_buf_source *source, + struct cir_buf_sink *sink, uint32_t frames, uint32_t attenuation) { struct vol_data *cd = module_get_private_data(mod); - struct audio_stream *source = bsource->data; - struct audio_stream *sink = bsink->data; ae_f32x2 in_sample; int i, n, m; ae_valign inu; ae_valign outu = AE_ZALIGN64(); - const int channels_count = audio_stream_get_channels(sink); + const int channels_count = cd->channels; const int inc = sizeof(ae_f32x2); int samples = channels_count * frames; - ae_f32x2 *in = (ae_f32x2 *)audio_stream_wrap(source, (char *)audio_stream_get_rptr(source) - + bsource->consumed); - ae_f32x2 *out = (ae_f32x2 *)audio_stream_wrap(sink, (char *)audio_stream_get_wptr(sink) - + bsink->size); + const ae_f32x2 *in = source->ptr; + ae_f32x2 *out = sink->ptr; ae_f32x2 temp; ae_f32x2 *peakvol = (ae_f32x2 *)cd->peak_vol; /* Set peakvol(which stores the peak volume data twice) as circular buffer */ AE_SETCBEGIN1(cd->peak_vol); AE_SETCEND1(cd->peak_vol + channels_count * 2); - bsource->consumed += VOL_S32_SAMPLES_TO_BYTES(samples); - bsink->size += VOL_S32_SAMPLES_TO_BYTES(samples); - while (samples) { - m = audio_stream_samples_without_wrap_s32(source, in); + m = cir_buf_samples_without_wrap_s32(in, source->buf_end); n = MIN(m, samples); - m = audio_stream_samples_without_wrap_s32(sink, out); + m = cir_buf_samples_without_wrap_s32(out, sink->buf_end); n = MIN(m, n); inu = AE_LA64_PP(in); /* process two continuous sample data once */ @@ -360,8 +332,8 @@ static void vol_passthrough_s32_to_s24_s32(struct processing_module *mod, } AE_SA64POS_FP(outu, out); samples -= n; - in = audio_stream_wrap(source, in); - out = audio_stream_wrap(sink, out); + in = cir_buf_wrap(in, source->buf_start, source->buf_end); + out = cir_buf_wrap(out, sink->buf_start, sink->buf_end); } for (i = 0; i < channels_count; i++) cd->peak_regs.peak_meter[i] = MAX(cd->peak_vol[i], @@ -378,13 +350,11 @@ static void vol_passthrough_s32_to_s24_s32(struct processing_module *mod, * \param[in] frames Number of frames to process. * \param[in] attenuation factor for peakmeter adjustment (unused for 16bit) */ -static void vol_s16_to_s16(struct processing_module *mod, struct input_stream_buffer *bsource, - struct output_stream_buffer *bsink, uint32_t frames, +static void vol_s16_to_s16(struct processing_module *mod, struct cir_buf_source *source, + struct cir_buf_sink *sink, uint32_t frames, uint32_t attenuation) { struct vol_data *cd = module_get_private_data(mod); - struct audio_stream *source = bsource->data; - struct audio_stream *sink = bsink->data; ae_f32x2 volume0; ae_f32x2 volume1; ae_f32x2 out_sample0; @@ -397,11 +367,9 @@ static void vol_s16_to_s16(struct processing_module *mod, struct input_stream_bu ae_f32x2 *vol; ae_valign inu; ae_valign outu = AE_ZALIGN64(); - ae_f16x4 *in = (ae_f16x4 *)audio_stream_wrap(source, (char *)audio_stream_get_rptr(source) - + bsource->consumed); - ae_f16x4 *out = (ae_f16x4 *)audio_stream_wrap(sink, (char *)audio_stream_get_wptr(sink) - + bsink->size); - const int channels_count = audio_stream_get_channels(sink); + const ae_f16x4 *in = source->ptr; + ae_f16x4 *out = sink->ptr; + const int channels_count = cd->channels; const int inc = sizeof(ae_f32x2); int samples = channels_count * frames; ae_f32x2 temp; @@ -427,9 +395,9 @@ static void vol_s16_to_s16(struct processing_module *mod, struct input_stream_bu AE_SETCEND0(buf_end); while (samples) { - m = audio_stream_samples_without_wrap_s16(source, in); + m = cir_buf_samples_without_wrap_s16(in, source->buf_end); n = MIN(m, samples); - m = audio_stream_samples_without_wrap_s16(sink, out); + m = cir_buf_samples_without_wrap_s16(out, sink->buf_end); n = MIN(m, n); inu = AE_LA64_PP(in); m = n >> 2; @@ -509,10 +477,8 @@ static void vol_s16_to_s16(struct processing_module *mod, struct input_stream_bu } samples -= n; - in = audio_stream_wrap(source, in); - out = audio_stream_wrap(sink, out); - bsource->consumed += VOL_S16_SAMPLES_TO_BYTES(n); - bsink->size += VOL_S16_SAMPLES_TO_BYTES(n); + in = cir_buf_wrap(in, source->buf_start, source->buf_end); + out = cir_buf_wrap(out, sink->buf_start, sink->buf_end); } for (i = 0; i < channels_count; i++) { m = MAX(cd->peak_vol[i], cd->peak_vol[i + channels_count]); @@ -531,22 +497,18 @@ static void vol_s16_to_s16(struct processing_module *mod, struct input_stream_bu * \param[in] attenuation factor for peakmeter adjustment (unused for 16bit) */ static void vol_passthrough_s16_to_s16(struct processing_module *mod, - struct input_stream_buffer *bsource, - struct output_stream_buffer *bsink, uint32_t frames, + struct cir_buf_source *source, + struct cir_buf_sink *sink, uint32_t frames, uint32_t attenuation) { struct vol_data *cd = module_get_private_data(mod); - struct audio_stream *source = bsource->data; - struct audio_stream *sink = bsink->data; ae_f16x4 in_sample; int i, n, m, left; ae_valign inu; ae_valign outu = AE_ZALIGN64(); - ae_f16x4 *in = (ae_f16x4 *)audio_stream_wrap(source, (char *)audio_stream_get_rptr(source) - + bsource->consumed); - ae_f16x4 *out = (ae_f16x4 *)audio_stream_wrap(sink, (char *)audio_stream_get_wptr(sink) - + bsink->size); - const int channels_count = audio_stream_get_channels(sink); + const ae_f16x4 *in = source->ptr; + ae_f16x4 *out = sink->ptr; + const int channels_count = cd->channels; const int inc = sizeof(ae_f32x2); int samples = channels_count * frames; ae_f32x2 temp; @@ -557,9 +519,9 @@ static void vol_passthrough_s16_to_s16(struct processing_module *mod, AE_SETCEND1(cd->peak_vol + channels_count * 4); while (samples) { - m = audio_stream_samples_without_wrap_s16(source, in); + m = cir_buf_samples_without_wrap_s16(in, source->buf_end); n = MIN(m, samples); - m = audio_stream_samples_without_wrap_s16(sink, out); + m = cir_buf_samples_without_wrap_s16(out, sink->buf_end); n = MIN(m, n); inu = AE_LA64_PP(in); m = n >> 2; @@ -594,10 +556,8 @@ static void vol_passthrough_s16_to_s16(struct processing_module *mod, } samples -= n; - in = (ae_f16x4 *)audio_stream_wrap(source, in); - out = (ae_f16x4 *)audio_stream_wrap(sink, out); - bsource->consumed += VOL_S16_SAMPLES_TO_BYTES(n); - bsink->size += VOL_S16_SAMPLES_TO_BYTES(n); + in = cir_buf_wrap(in, source->buf_start, source->buf_end); + out = cir_buf_wrap(out, sink->buf_start, sink->buf_end); } for (i = 0; i < channels_count; i++) { diff --git a/src/audio/volume/volume_hifi5.c b/src/audio/volume/volume_hifi5.c index 794330cddd9b..4d4041b38145 100644 --- a/src/audio/volume/volume_hifi5.c +++ b/src/audio/volume/volume_hifi5.c @@ -55,13 +55,11 @@ static void vol_store_gain(struct vol_data *cd, const int channels_count) * \param[in] frames Number of frames to process. * \param[in] attenuation factor for peakmeter adjustment (unused) */ -static void vol_s24_to_s24_s32(struct processing_module *mod, struct input_stream_buffer *bsource, - struct output_stream_buffer *bsink, uint32_t frames, +static void vol_s24_to_s24_s32(struct processing_module *mod, struct cir_buf_source *source, + struct cir_buf_sink *sink, uint32_t frames, uint32_t attenuation) { struct vol_data *cd = module_get_private_data(mod); - struct audio_stream *source = bsource->data; - struct audio_stream *sink = bsink->data; ae_int32x2 in_sample, in_sample1; ae_int32x2 out_sample, out_sample1; ae_int32x2 volume, volume1; @@ -71,13 +69,9 @@ static void vol_s24_to_s24_s32(struct processing_module *mod, struct input_strea ae_int32x4 *vol; ae_valignx2 inu; ae_valignx2 outu = AE_ZALIGN128(); - ae_int32x4 *in = (ae_int32x4 *)audio_stream_wrap(source, - (char *)audio_stream_get_rptr(source) - + bsource->consumed); - ae_int32x4 *out = (ae_int32x4 *)audio_stream_wrap(sink, - (char *)audio_stream_get_wptr(sink) - + bsink->size); - const int channels_count = audio_stream_get_channels(sink); + const ae_int32x4 *in = source->ptr; + ae_int32x4 *out = sink->ptr; + const int channels_count = cd->channels; const int inc = sizeof(ae_int32x4); int samples = channels_count * frames; @@ -95,13 +89,10 @@ static void vol_s24_to_s24_s32(struct processing_module *mod, struct input_strea AE_SETCBEGIN0(buf); AE_SETCEND0(buf_end); - bsource->consumed += VOL_S32_SAMPLES_TO_BYTES(samples); - bsink->size += VOL_S32_SAMPLES_TO_BYTES(samples); - while (samples) { - m = audio_stream_samples_without_wrap_s24(source, in); + m = cir_buf_samples_without_wrap_s32(in, source->buf_end); n = MIN(m, samples); - m = audio_stream_samples_without_wrap_s24(sink, out); + m = cir_buf_samples_without_wrap_s32(out, sink->buf_end); n = MIN(m, n); inu = AE_LA128_PP(in); /* process four continuous samples per iteration */ @@ -138,8 +129,8 @@ static void vol_s24_to_s24_s32(struct processing_module *mod, struct input_strea } AE_SA128POS_FP(outu, out); samples -= n; - in = audio_stream_wrap(source, in); - out = audio_stream_wrap(sink, out); + in = cir_buf_wrap(in, source->buf_start, source->buf_end); + out = cir_buf_wrap(out, sink->buf_start, sink->buf_end); } } @@ -152,31 +143,23 @@ static void vol_s24_to_s24_s32(struct processing_module *mod, struct input_strea * \param[in] attenuation factor for peakmeter adjustment (unused) */ static void vol_passthrough_s24_to_s24_s32(struct processing_module *mod, - struct input_stream_buffer *bsource, - struct output_stream_buffer *bsink, uint32_t frames, + struct cir_buf_source *source, + struct cir_buf_sink *sink, uint32_t frames, uint32_t attenuation) { - struct audio_stream *source = bsource->data; - struct audio_stream *sink = bsink->data; + struct vol_data *cd = module_get_private_data(mod); ae_int32x2 in_sample, in_sample1; int i, n, m; ae_valignx2 inu; ae_valignx2 outu = AE_ZALIGN128(); - ae_int32x4 *in = (ae_int32x4 *)audio_stream_wrap(source, - (char *)audio_stream_get_rptr(source) - + bsource->consumed); - ae_int32x4 *out = (ae_int32x4 *)audio_stream_wrap(sink, - (char *)audio_stream_get_wptr(sink) - + bsink->size); - int samples = audio_stream_get_channels(sink) * frames; - - bsource->consumed += VOL_S32_SAMPLES_TO_BYTES(samples); - bsink->size += VOL_S32_SAMPLES_TO_BYTES(samples); + const ae_int32x4 *in = source->ptr; + ae_int32x4 *out = sink->ptr; + int samples = cd->channels * frames; while (samples) { - m = audio_stream_samples_without_wrap_s24(source, in); + m = cir_buf_samples_without_wrap_s32(in, source->buf_end); n = MIN(m, samples); - m = audio_stream_samples_without_wrap_s24(sink, out); + m = cir_buf_samples_without_wrap_s32(out, sink->buf_end); n = MIN(m, n); inu = AE_LA128_PP(in); /* process 4 continuous samples once */ @@ -188,8 +171,8 @@ static void vol_passthrough_s24_to_s24_s32(struct processing_module *mod, } AE_SA128POS_FP(outu, out); samples -= n; - in = audio_stream_wrap(source, in); - out = audio_stream_wrap(sink, out); + in = cir_buf_wrap(in, source->buf_start, source->buf_end); + out = cir_buf_wrap(out, sink->buf_start, sink->buf_end); } } @@ -204,13 +187,11 @@ static void vol_passthrough_s24_to_s24_s32(struct processing_module *mod, * \param[in] frames Number of frames to process. * \param[in] attenuation factor for peakmeter adjustment (unused) */ -static void vol_s32_to_s24_s32(struct processing_module *mod, struct input_stream_buffer *bsource, - struct output_stream_buffer *bsink, uint32_t frames, +static void vol_s32_to_s24_s32(struct processing_module *mod, struct cir_buf_source *source, + struct cir_buf_sink *sink, uint32_t frames, uint32_t attenuation) { struct vol_data *cd = module_get_private_data(mod); - struct audio_stream *source = bsource->data; - struct audio_stream *sink = bsink->data; ae_int32x2 in_sample, in_sample1; ae_int32x2 out_sample, out_sample1; ae_int32x2 volume, volume1; @@ -220,15 +201,11 @@ static void vol_s32_to_s24_s32(struct processing_module *mod, struct input_strea ae_int32x4 *vol; ae_valignx2 inu; ae_valignx2 outu = AE_ZALIGN128(); - const int channels_count = audio_stream_get_channels(sink); + const int channels_count = cd->channels; const int inc = sizeof(ae_int32x4); int samples = channels_count * frames; - ae_int32x4 *in = (ae_int32x4 *)audio_stream_wrap(source, - (char *)audio_stream_get_rptr(source) - + bsource->consumed); - ae_int32x4 *out = (ae_int32x4 *)audio_stream_wrap(sink, - (char *)audio_stream_get_wptr(sink) - + bsink->size); + const ae_int32x4 *in = source->ptr; + ae_int32x4 *out = sink->ptr; /** to ensure the address is 16-byte aligned and avoid risk of * error loading of volume gain while the cd->vol would be set @@ -244,13 +221,10 @@ static void vol_s32_to_s24_s32(struct processing_module *mod, struct input_strea AE_SETCBEGIN0(buf); AE_SETCEND0(buf_end); - bsource->consumed += VOL_S32_SAMPLES_TO_BYTES(samples); - bsink->size += VOL_S32_SAMPLES_TO_BYTES(samples); - while (samples) { - m = audio_stream_samples_without_wrap_s32(source, in); + m = cir_buf_samples_without_wrap_s32(in, source->buf_end); n = MIN(m, samples); - m = audio_stream_samples_without_wrap_s32(sink, out); + m = cir_buf_samples_without_wrap_s32(out, sink->buf_end); n = MIN(m, n); inu = AE_LA128_PP(in); /* process four continuous samples per iteration */ @@ -285,8 +259,8 @@ static void vol_s32_to_s24_s32(struct processing_module *mod, struct input_strea } AE_SA128POS_FP(outu, out); samples -= n; - in = audio_stream_wrap(source, in); - out = audio_stream_wrap(sink, out); + in = cir_buf_wrap(in, source->buf_start, source->buf_end); + out = cir_buf_wrap(out, sink->buf_start, sink->buf_end); } } @@ -299,31 +273,24 @@ static void vol_s32_to_s24_s32(struct processing_module *mod, struct input_strea * \param[in] attenuation factor for peakmeter adjustment (unused) */ static void vol_passthrough_s32_to_s24_s32(struct processing_module *mod, - struct input_stream_buffer *bsource, - struct output_stream_buffer *bsink, uint32_t frames, + struct cir_buf_source *source, + struct cir_buf_sink *sink, uint32_t frames, uint32_t attenuation) { - struct audio_stream *source = bsource->data; - struct audio_stream *sink = bsink->data; + struct vol_data *cd = module_get_private_data(mod); ae_int32x2 in_sample, in_sample1; int i, n, m; ae_valignx2 inu; ae_valignx2 outu = AE_ZALIGN128(); - const int channels_count = audio_stream_get_channels(sink); + const int channels_count = cd->channels; int samples = channels_count * frames; - ae_int32x4 *in = (ae_int32x4 *)audio_stream_wrap(source, - (char *)audio_stream_get_rptr(source) - + bsource->consumed); - ae_int32x4 *out = (ae_int32x4 *)audio_stream_wrap(sink, - (char *)audio_stream_get_wptr(sink) - + bsink->size); - - bsource->consumed += VOL_S32_SAMPLES_TO_BYTES(samples); - bsink->size += VOL_S32_SAMPLES_TO_BYTES(samples); + const ae_int32x4 *in = source->ptr; + ae_int32x4 *out = sink->ptr; + while (samples) { - m = audio_stream_samples_without_wrap_s32(source, in); + m = cir_buf_samples_without_wrap_s32(in, source->buf_end); n = MIN(m, samples); - m = audio_stream_samples_without_wrap_s32(sink, out); + m = cir_buf_samples_without_wrap_s32(out, sink->buf_end); n = MIN(m, n); inu = AE_LA128_PP(in); /* process four continuous samples per iteration */ @@ -334,8 +301,8 @@ static void vol_passthrough_s32_to_s24_s32(struct processing_module *mod, } AE_SA128POS_FP(outu, out); samples -= n; - in = audio_stream_wrap(source, in); - out = audio_stream_wrap(sink, out); + in = cir_buf_wrap(in, source->buf_start, source->buf_end); + out = cir_buf_wrap(out, sink->buf_start, sink->buf_end); } } #endif /* CONFIG_FORMAT_S32LE */ @@ -349,13 +316,11 @@ static void vol_passthrough_s32_to_s24_s32(struct processing_module *mod, * \param[in] frames Number of frames to process. * \param[in] attenuation factor for peakmeter adjustment (unused) */ -static void vol_s16_to_s16(struct processing_module *mod, struct input_stream_buffer *bsource, - struct output_stream_buffer *bsink, uint32_t frames, +static void vol_s16_to_s16(struct processing_module *mod, struct cir_buf_source *source, + struct cir_buf_sink *sink, uint32_t frames, uint32_t attenuation) { struct vol_data *cd = module_get_private_data(mod); - struct audio_stream *source = bsource->data; - struct audio_stream *sink = bsink->data; ae_int32x2 volume, volume1, volume2, volume3; ae_int32x2 out_temp, out_temp1; ae_int16x4 in_sample, in_sample1; @@ -366,13 +331,9 @@ static void vol_s16_to_s16(struct processing_module *mod, struct input_stream_bu ae_int32x4 *vol; ae_valignx2 inu; ae_valignx2 outu = AE_ZALIGN128(); - ae_int16x8 *in = (ae_int16x8 *)audio_stream_wrap(source, - (char *)audio_stream_get_rptr(source) - + bsource->consumed); - ae_int16x8 *out = (ae_int16x8 *)audio_stream_wrap(sink, - (char *)audio_stream_get_wptr(sink) - + bsink->size); - const int channels_count = audio_stream_get_channels(sink); + const ae_int16x8 *in = source->ptr; + ae_int16x8 *out = sink->ptr; + const int channels_count = cd->channels; const int inc = sizeof(ae_int32x4); int samples = channels_count * frames; @@ -392,9 +353,9 @@ static void vol_s16_to_s16(struct processing_module *mod, struct input_stream_bu AE_SETCEND0(buf_end); while (samples) { - m = audio_stream_samples_without_wrap_s16(source, in); + m = cir_buf_samples_without_wrap_s16(in, source->buf_end); n = MIN(m, samples); - m = audio_stream_samples_without_wrap_s16(sink, out); + m = cir_buf_samples_without_wrap_s16(out, sink->buf_end); n = MIN(m, n); inu = AE_LA128_PP(in); for (i = 0; i < n; i += 8) { @@ -440,10 +401,8 @@ static void vol_s16_to_s16(struct processing_module *mod, struct input_stream_bu } AE_SA128POS_FP(outu, out); samples -= n; - bsource->consumed += VOL_S16_SAMPLES_TO_BYTES(n); - bsink->size += VOL_S16_SAMPLES_TO_BYTES(n); - in = audio_stream_wrap(source, in); - out = audio_stream_wrap(sink, out); + in = cir_buf_wrap(in, source->buf_start, source->buf_end); + out = cir_buf_wrap(out, sink->buf_start, sink->buf_end); } } @@ -456,31 +415,24 @@ static void vol_s16_to_s16(struct processing_module *mod, struct input_stream_bu * \param[in] attenuation factor for peakmeter adjustment (unused) */ static void vol_passthrough_s16_to_s16(struct processing_module *mod, - struct input_stream_buffer *bsource, - struct output_stream_buffer *bsink, uint32_t frames, + struct cir_buf_source *source, + struct cir_buf_sink *sink, uint32_t frames, uint32_t attenuation) { - struct audio_stream *source = bsource->data; - struct audio_stream *sink = bsink->data; + struct vol_data *cd = module_get_private_data(mod); ae_int16x4 in_sample, in_sample1; int i, n, m; ae_valignx2 inu; ae_valignx2 outu = AE_ZALIGN128(); - ae_int16x8 *in = (ae_int16x8 *)audio_stream_wrap(source, - (char *)audio_stream_get_rptr(source) - + bsource->consumed); - ae_int16x8 *out = (ae_int16x8 *)audio_stream_wrap(sink, - (char *)audio_stream_get_wptr(sink) - + bsink->size); - const int channels_count = audio_stream_get_channels(sink); + const ae_int16x8 *in = source->ptr; + ae_int16x8 *out = sink->ptr; + const int channels_count = cd->channels; int samples = channels_count * frames; - bsource->consumed += VOL_S16_SAMPLES_TO_BYTES(samples); - bsink->size += VOL_S16_SAMPLES_TO_BYTES(samples); while (samples) { - m = audio_stream_samples_without_wrap_s16(source, in); + m = cir_buf_samples_without_wrap_s16(in, source->buf_end); n = MIN(m, samples); - m = audio_stream_samples_without_wrap_s16(sink, out); + m = cir_buf_samples_without_wrap_s16(out, sink->buf_end); n = MIN(m, n); inu = AE_LA128_PP(in); for (i = 0; i < n; i += 8) { @@ -490,8 +442,8 @@ static void vol_passthrough_s16_to_s16(struct processing_module *mod, } AE_SA128POS_FP(outu, out); samples -= n; - in = audio_stream_wrap(source, in); - out = audio_stream_wrap(sink, out); + in = cir_buf_wrap(in, source->buf_start, source->buf_end); + out = cir_buf_wrap(out, sink->buf_start, sink->buf_end); } } #endif /* CONFIG_FORMAT_S16LE */ diff --git a/src/audio/volume/volume_hifi5_with_peakvol.c b/src/audio/volume/volume_hifi5_with_peakvol.c index 16d2f7cb8bf9..5d085dfe6522 100644 --- a/src/audio/volume/volume_hifi5_with_peakvol.c +++ b/src/audio/volume/volume_hifi5_with_peakvol.c @@ -55,13 +55,10 @@ static inline void vol_store_gain(struct vol_data *cd, const int channels_count) * \param[in] frames Number of frames to process. * \param[in] attenuation factor for peakmeter adjustment */ -static void vol_s24_to_s24_s32(struct processing_module *mod, struct input_stream_buffer *bsource, - struct output_stream_buffer *bsink, uint32_t frames, - uint32_t attenuation) +static void vol_s24_to_s24_s32(struct processing_module *mod, struct cir_buf_source *source, + struct cir_buf_sink *sink, uint32_t frames, uint32_t attenuation) { struct vol_data *cd = module_get_private_data(mod); - struct audio_stream *source = bsource->data; - struct audio_stream *sink = bsink->data; ae_int32x2 in_sample, in_sample1; ae_int32x2 out_sample, out_sample1; ae_int32x2 volume, volume1; @@ -69,13 +66,9 @@ static void vol_s24_to_s24_s32(struct processing_module *mod, struct input_strea ae_int32x4 *vol; ae_valignx2 inu; ae_valignx2 outu = AE_ZALIGN128(); - ae_int32x4 *in = (ae_int32x4 *)audio_stream_wrap(source, - (char *)audio_stream_get_rptr(source) - + bsource->consumed); - ae_int32x4 *out = (ae_int32x4 *)audio_stream_wrap(sink, - (char *)audio_stream_get_wptr(sink) - + bsink->size); - const int channels_count = audio_stream_get_channels(sink); + const ae_int32x4 *in = source->ptr; + ae_int32x4 *out = sink->ptr; + const int channels_count = cd->channels; const int inc = sizeof(ae_int32x4); int samples = channels_count * frames; ae_int32x2 temp, temp1; @@ -97,13 +90,10 @@ static void vol_s24_to_s24_s32(struct processing_module *mod, struct input_strea AE_SETCBEGIN0(vol); AE_SETCEND0(cd->vol + channels_count * 4); - bsource->consumed += VOL_S32_SAMPLES_TO_BYTES(samples); - bsink->size += VOL_S32_SAMPLES_TO_BYTES(samples); - while (samples) { - m = audio_stream_samples_without_wrap_s32(source, in); + m = cir_buf_samples_without_wrap_s32(in, source->buf_end); n = MIN(m, samples); - m = audio_stream_samples_without_wrap_s16(sink, out); + m = cir_buf_samples_without_wrap_s16(out, sink->buf_end); n = MIN(m, n); inu = AE_LA128_PP(in); /* process four continuous samples per loop */ @@ -145,8 +135,8 @@ static void vol_s24_to_s24_s32(struct processing_module *mod, struct input_strea } AE_SA128POS_FP(outu, out); samples -= n; - in = audio_stream_wrap(source, in); - out = audio_stream_wrap(sink, out); + in = cir_buf_wrap(in, source->buf_start, source->buf_end); + out = cir_buf_wrap(out, sink->buf_start, sink->buf_end); } for (i = 0; i < channels_count; i++) { @@ -166,24 +156,18 @@ static void vol_s24_to_s24_s32(struct processing_module *mod, struct input_strea * \param[in] attenuation factor for peakmeter adjustment */ static void vol_passthrough_s24_to_s24_s32(struct processing_module *mod, - struct input_stream_buffer *bsource, - struct output_stream_buffer *bsink, uint32_t frames, + struct cir_buf_source *source, + struct cir_buf_sink *sink, uint32_t frames, uint32_t attenuation) { struct vol_data *cd = module_get_private_data(mod); - struct audio_stream *source = bsource->data; - struct audio_stream *sink = bsink->data; ae_int32x2 in_sample, in_sample1; int i, n, m; ae_valignx2 inu; ae_valignx2 outu = AE_ZALIGN128(); - ae_int32x4 *in = (ae_int32x4 *)audio_stream_wrap(source, - (char *)audio_stream_get_rptr(source) - + bsource->consumed); - ae_int32x4 *out = (ae_int32x4 *)audio_stream_wrap(sink, - (char *)audio_stream_get_wptr(sink) - + bsink->size); - const int channels_count = audio_stream_get_channels(sink); + const ae_int32x4 *in = source->ptr; + ae_int32x4 *out = sink->ptr; + const int channels_count = cd->channels; const int inc = sizeof(ae_int32x4); int samples = channels_count * frames; ae_int32x2 temp, temp1; @@ -193,13 +177,10 @@ static void vol_passthrough_s24_to_s24_s32(struct processing_module *mod, AE_SETCBEGIN1(cd->peak_vol); AE_SETCEND1(cd->peak_vol + channels_count * 4); - bsource->consumed += VOL_S32_SAMPLES_TO_BYTES(samples); - bsink->size += VOL_S32_SAMPLES_TO_BYTES(samples); - while (samples) { - m = audio_stream_samples_without_wrap_s32(source, in); + m = cir_buf_samples_without_wrap_s32(in, source->buf_end); n = MIN(m, samples); - m = audio_stream_samples_without_wrap_s16(sink, out); + m = cir_buf_samples_without_wrap_s16(out, sink->buf_end); n = MIN(m, n); inu = AE_LA128_PP(in); /* process four continuous samples per loop */ @@ -216,8 +197,8 @@ static void vol_passthrough_s24_to_s24_s32(struct processing_module *mod, } AE_SA128POS_FP(outu, out); samples -= n; - in = audio_stream_wrap(source, in); - out = audio_stream_wrap(sink, out); + in = cir_buf_wrap(in, source->buf_start, source->buf_end); + out = cir_buf_wrap(out, sink->buf_start, sink->buf_end); } for (i = 0; i < channels_count; i++) { m = MAX(cd->peak_vol[i], cd->peak_vol[i + channels_count]); @@ -237,13 +218,10 @@ static void vol_passthrough_s24_to_s24_s32(struct processing_module *mod, * \param[in] frames Number of frames to process. * \param[in] attenuation factor for peakmeter adjustment */ -static void vol_s32_to_s24_s32(struct processing_module *mod, struct input_stream_buffer *bsource, - struct output_stream_buffer *bsink, uint32_t frames, - uint32_t attenuation) +static void vol_s32_to_s24_s32(struct processing_module *mod, struct cir_buf_source *source, + struct cir_buf_sink *sink, uint32_t frames, uint32_t attenuation) { struct vol_data *cd = module_get_private_data(mod); - struct audio_stream *source = bsource->data; - struct audio_stream *sink = bsink->data; ae_int32x2 in_sample, in_sample1; ae_int32x2 out_sample, out_sample1; ae_int32x2 volume, volume1; @@ -253,15 +231,11 @@ static void vol_s32_to_s24_s32(struct processing_module *mod, struct input_strea ae_int32x4 *vol; ae_valignx2 inu; ae_valignx2 outu = AE_ZALIGN128(); - const int channels_count = audio_stream_get_channels(sink); + const int channels_count = cd->channels; const int inc = sizeof(ae_int32x4); int samples = channels_count * frames; - ae_int32x4 *in = (ae_int32x4 *)audio_stream_wrap(source, - (char *)audio_stream_get_rptr(source) - + bsource->consumed); - ae_int32x4 *out = (ae_int32x4 *)audio_stream_wrap(sink, - (char *)audio_stream_get_wptr(sink) - + bsink->size); + const ae_int32x4 *in = source->ptr; + ae_int32x4 *out = sink->ptr; ae_int32x2 temp, temp1; ae_int32x4 *peakvol = (ae_int32x4 *)cd->peak_vol; @@ -283,13 +257,10 @@ static void vol_s32_to_s24_s32(struct processing_module *mod, struct input_strea AE_SETCBEGIN0(buf); AE_SETCEND0(buf_end); - bsource->consumed += VOL_S32_SAMPLES_TO_BYTES(samples); - bsink->size += VOL_S32_SAMPLES_TO_BYTES(samples); - while (samples) { - m = audio_stream_samples_without_wrap_s32(source, in); + m = cir_buf_samples_without_wrap_s32(in, source->buf_end); n = MIN(m, samples); - m = audio_stream_samples_without_wrap_s32(sink, out); + m = cir_buf_samples_without_wrap_s32(out, sink->buf_end); n = MIN(m, n); inu = AE_LA128_PP(in); /* process four continuous samples per loop */ @@ -329,8 +300,8 @@ static void vol_s32_to_s24_s32(struct processing_module *mod, struct input_strea } AE_SA128POS_FP(outu, out); samples -= n; - in = audio_stream_wrap(source, in); - out = audio_stream_wrap(sink, out); + in = cir_buf_wrap(in, source->buf_start, source->buf_end); + out = cir_buf_wrap(out, sink->buf_start, sink->buf_end); } for (i = 0; i < channels_count; i++) { m = MAX(cd->peak_vol[i], cd->peak_vol[i + channels_count]); @@ -349,39 +320,31 @@ static void vol_s32_to_s24_s32(struct processing_module *mod, struct input_strea * \param[in] attenuation factor for peakmeter adjustment */ static void vol_passthrough_s32_to_s24_s32(struct processing_module *mod, - struct input_stream_buffer *bsource, - struct output_stream_buffer *bsink, uint32_t frames, + struct cir_buf_source *source, + struct cir_buf_sink *sink, uint32_t frames, uint32_t attenuation) { struct vol_data *cd = module_get_private_data(mod); - struct audio_stream *source = bsource->data; - struct audio_stream *sink = bsink->data; ae_int32x2 in_sample, in_sample1; int i, n, m; ae_valignx2 inu; ae_valignx2 outu = AE_ZALIGN128(); - const int channels_count = audio_stream_get_channels(sink); + const int channels_count = cd->channels; const int inc = sizeof(ae_int32x4); int samples = channels_count * frames; - ae_int32x4 *in = (ae_int32x4 *)audio_stream_wrap(source, - (char *)audio_stream_get_rptr(source) - + bsource->consumed); - ae_int32x4 *out = (ae_int32x4 *)audio_stream_wrap(sink, - (char *)audio_stream_get_wptr(sink) - + bsink->size); + const ae_int32x4 *in = source->ptr; + ae_int32x4 *out = sink->ptr; ae_int32x2 temp, temp1; ae_int32x4 *peakvol = (ae_int32x4 *)cd->peak_vol; /* Set peakvol(which stores the peak volume data four times) as circular buffer */ AE_SETCBEGIN1(cd->peak_vol); AE_SETCEND1(cd->peak_vol + channels_count * 4); - bsource->consumed += VOL_S32_SAMPLES_TO_BYTES(samples); - bsink->size += VOL_S32_SAMPLES_TO_BYTES(samples); while (samples) { - m = audio_stream_samples_without_wrap_s32(source, in); + m = cir_buf_samples_without_wrap_s32(in, source->buf_end); n = MIN(m, samples); - m = audio_stream_samples_without_wrap_s32(sink, out); + m = cir_buf_samples_without_wrap_s32(out, sink->buf_end); n = MIN(m, n); inu = AE_LA128_PP(in); /* process four continuous samples per iteration */ @@ -398,8 +361,8 @@ static void vol_passthrough_s32_to_s24_s32(struct processing_module *mod, } AE_SA128POS_FP(outu, out); samples -= n; - in = audio_stream_wrap(source, in); - out = audio_stream_wrap(sink, out); + in = cir_buf_wrap(in, source->buf_start, source->buf_end); + out = cir_buf_wrap(out, sink->buf_start, sink->buf_end); } for (i = 0; i < channels_count; i++) { m = MAX(cd->peak_vol[i], cd->peak_vol[i + channels_count]); @@ -419,13 +382,10 @@ static void vol_passthrough_s32_to_s24_s32(struct processing_module *mod, * \param[in] frames Number of frames to process. * \param[in] attenuation factor for peakmeter adjustment (unused for 16bit) */ -static void vol_s16_to_s16(struct processing_module *mod, struct input_stream_buffer *bsource, - struct output_stream_buffer *bsink, uint32_t frames, - uint32_t attenuation) +static void vol_s16_to_s16(struct processing_module *mod, struct cir_buf_source *source, + struct cir_buf_sink *sink, uint32_t frames, uint32_t attenuation) { struct vol_data *cd = module_get_private_data(mod); - struct audio_stream *source = bsource->data; - struct audio_stream *sink = bsink->data; ae_int32x2 volume, volume1, volume2, volume3; ae_int32x2 out_temp, out_temp1; ae_int16x4 in_sample, in_sample1; @@ -436,13 +396,9 @@ static void vol_s16_to_s16(struct processing_module *mod, struct input_stream_bu ae_int32x4 *vol; ae_valignx2 inu; ae_valignx2 outu = AE_ZALIGN128(); - ae_int16x8 *in = (ae_int16x8 *)audio_stream_wrap(source, - (char *)audio_stream_get_rptr(source) - + bsource->consumed); - ae_int16x8 *out = (ae_int16x8 *)audio_stream_wrap(sink, - (char *)audio_stream_get_wptr(sink) - + bsink->size); - const int channels_count = audio_stream_get_channels(sink); + const ae_int16x8 *in = source->ptr; + ae_int16x8 *out = sink->ptr; + const int channels_count = cd->channels; const int inc = sizeof(ae_int32x4); int samples = channels_count * frames; ae_int32x2 temp, temp1; @@ -468,9 +424,9 @@ static void vol_s16_to_s16(struct processing_module *mod, struct input_stream_bu AE_SETCEND0(buf_end); while (samples) { - m = audio_stream_samples_without_wrap_s16(source, in); + m = cir_buf_samples_without_wrap_s16(in, source->buf_end); n = MIN(m, samples); - m = audio_stream_samples_without_wrap_s16(sink, out); + m = cir_buf_samples_without_wrap_s16(out, sink->buf_end); n = MIN(m, n); inu = AE_LA128_PP(in); for (i = 0; i < n; i += 8) { @@ -524,10 +480,8 @@ static void vol_s16_to_s16(struct processing_module *mod, struct input_stream_bu } AE_SA128POS_FP(outu, out); samples -= n; - in = audio_stream_wrap(source, in); - out = audio_stream_wrap(sink, out); - bsource->consumed += VOL_S16_SAMPLES_TO_BYTES(n); - bsink->size += VOL_S16_SAMPLES_TO_BYTES(n); + in = cir_buf_wrap(in, source->buf_start, source->buf_end); + out = cir_buf_wrap(out, sink->buf_start, sink->buf_end); } for (i = 0; i < channels_count; i++) { m = MAX(cd->peak_vol[i], cd->peak_vol[i + channels_count]); @@ -546,24 +500,18 @@ static void vol_s16_to_s16(struct processing_module *mod, struct input_stream_bu * \param[in] attenuation factor for peakmeter adjustment (unused for 16bit) */ static void vol_passthrough_s16_to_s16(struct processing_module *mod, - struct input_stream_buffer *bsource, - struct output_stream_buffer *bsink, uint32_t frames, + struct cir_buf_source *source, + struct cir_buf_sink *sink, uint32_t frames, uint32_t attenuation) { struct vol_data *cd = module_get_private_data(mod); - struct audio_stream *source = bsource->data; - struct audio_stream *sink = bsink->data; ae_int16x4 in_sample, in_sample1; int i, n, m; ae_valignx2 inu; ae_valignx2 outu = AE_ZALIGN128(); - ae_int16x8 *in = (ae_int16x8 *)audio_stream_wrap(source, - (char *)audio_stream_get_rptr(source) - + bsource->consumed); - ae_int16x8 *out = (ae_int16x8 *)audio_stream_wrap(sink, - (char *)audio_stream_get_wptr(sink) - + bsink->size); - const int channels_count = audio_stream_get_channels(sink); + const ae_int16x8 *in = source->ptr; + ae_int16x8 *out = sink->ptr; + const int channels_count = cd->channels; const int inc = sizeof(ae_int32x4); int samples = channels_count * frames; ae_int32x2 temp, temp1; @@ -574,9 +522,9 @@ static void vol_passthrough_s16_to_s16(struct processing_module *mod, AE_SETCEND1(cd->peak_vol + channels_count * 4); while (samples) { - m = audio_stream_samples_without_wrap_s16(source, in); + m = cir_buf_samples_without_wrap_s16(in, source->buf_end); n = MIN(m, samples); - m = audio_stream_samples_without_wrap_s16(sink, out); + m = cir_buf_samples_without_wrap_s16(out, sink->buf_end); n = MIN(m, n); inu = AE_LA128_PP(in); for (i = 0; i < n; i += 8) { @@ -598,10 +546,8 @@ static void vol_passthrough_s16_to_s16(struct processing_module *mod, } AE_SA128POS_FP(outu, out); samples -= n; - in = audio_stream_wrap(source, in); - out = audio_stream_wrap(sink, out); - bsource->consumed += VOL_S16_SAMPLES_TO_BYTES(n); - bsink->size += VOL_S16_SAMPLES_TO_BYTES(n); + in = cir_buf_wrap(in, source->buf_start, source->buf_end); + out = cir_buf_wrap(out, sink->buf_start, sink->buf_end); } for (i = 0; i < channels_count; i++) { m = MAX(cd->peak_vol[i], cd->peak_vol[i + channels_count]); diff --git a/src/audio/volume/volume_ipc3.c b/src/audio/volume/volume_ipc3.c index e83ebf25cff1..053b41e32152 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->vol); + mod_free(mod, cd); 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; } @@ -220,18 +219,23 @@ int volume_set_config(struct processing_module *mod, uint32_t config_id, volume_set_ramp_channel_counter(cd, cd->channels); volume_ramp_check(mod); + /* Update function pointer after all volume changes */ + if (comp_dev_get_first_data_consumer(dev) != NULL) { + cd->is_passthrough = false; + set_volume_process(cd, dev, false); + } 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 +266,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 +280,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 +289,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..e111752d2b78 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> @@ -116,6 +115,7 @@ int volume_init(struct processing_module *mod) uint32_t channels_count; uint8_t channel_cfg; uint8_t channel; + bool all_channels; uint32_t instance_id = IPC4_INST_ID(dev_comp_id(dev)); if (instance_id >= IPC4_MAX_PEAK_VOL_REG_SLOTS) { @@ -128,7 +128,27 @@ int volume_init(struct processing_module *mod) return -EINVAL; } - cd = rzalloc(SOF_MEM_FLAG_USER, sizeof(struct vol_data)); + /* The payload must hold at least one config entry, which is read below + * to detect the all-channels form. + */ + if (cfg->size < sizeof(*vol) + sizeof(vol->config[0])) { + comp_err(dev, "Invalid init payload size %zu", cfg->size); + return -EINVAL; + } + + /* In the all-channels form a single entry applies to every channel; + * otherwise the payload must hold one entry per channel as they are + * each read below. + */ + all_channels = vol->config[0].channel_id == IPC4_ALL_CHANNELS_MASK; + if (!all_channels && + cfg->size < sizeof(*vol) + channels_count * sizeof(vol->config[0])) { + comp_err(dev, "Invalid init payload size %zu for %u channels", + cfg->size, channels_count); + return -EINVAL; + } + + cd = mod_zalloc(mod, sizeof(struct vol_data)); if (!cd) return -ENOMEM; @@ -136,9 +156,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 +166,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; } @@ -157,16 +177,13 @@ int volume_init(struct processing_module *mod) md->private = cd; for (channel = 0; channel < channels_count; channel++) { - if (vol->config[0].channel_id == IPC4_ALL_CHANNELS_MASK) - channel_cfg = 0; - else - channel_cfg = channel; + channel_cfg = all_channels ? 0 : channel; target_volume[channel] = - convert_volume_ipc4_to_ipc3(dev, vol->config[channel].target_volume); + convert_volume_ipc4_to_ipc3(dev, vol->config[channel_cfg].target_volume); set_volume_ipc4(cd, channel, - target_volume[channel_cfg], + target_volume[channel], vol->config[channel_cfg].curve_type, vol->config[channel_cfg].curve_duration); @@ -189,14 +206,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) @@ -268,8 +286,10 @@ static int volume_set_attenuation(struct processing_module *mod, const uint8_t * struct comp_dev *dev = mod->dev; uint32_t attenuation; - /* only support attenuation in format of 32bit */ - if (data_size > sizeof(uint32_t)) { + /* only support attenuation in format of 32bit; the payload is + * dereferenced as a uint32_t below so it must be exactly that size + */ + if (data_size != (int)sizeof(uint32_t)) { comp_err(dev, "attenuation data size %d is incorrect", data_size); return -EINVAL; } @@ -351,7 +371,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 +433,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..db27490a98a4 100644 --- a/src/debug/debug_stream/debug_stream_slot.c +++ b/src/debug/debug_stream/debug_stream_slot.c @@ -10,21 +10,41 @@ #include <rtos/string.h> #include <user/debug_stream.h> #include <user/debug_stream_slot.h> +#include <zephyr/kernel.h> + +#ifdef CONFIG_USERSPACE +#include <zephyr/internal/syscall_handler.h> +#endif 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,19 +58,26 @@ 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); return (struct debug_stream_circular_buf *) (((uint8_t *)hdr) + desc->offset); } -int debug_stream_slot_send_record(struct debug_stream_record *rec) +int z_impl_debug_stream_slot_send_record(struct debug_stream_record *rec) { struct debug_stream_section_descriptor desc = { 0 }; struct debug_stream_circular_buf *buf = 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 +89,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,65 +117,71 @@ 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; } +#ifdef CONFIG_USERSPACE +static inline int z_vrfy_debug_stream_slot_send_record(struct debug_stream_record *rec) +{ + K_OOPS(K_SYSCALL_MEMORY_READ(rec, sizeof(*rec))); + K_OOPS(K_SYSCALL_MEMORY_READ(rec, rec->size_words * sizeof(uint32_t))); + return z_impl_debug_stream_slot_send_record(rec); +} +#include <zephyr/syscalls/debug_stream_slot_send_record_mrsh.c> +#endif + 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..f4b67d4d307a --- /dev/null +++ b/src/debug/debug_stream/debug_stream_text_msg.c @@ -0,0 +1,184 @@ +// 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); +} +EXPORT_SYMBOL(ds_msg); + +#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..f84b122b4d8a 100644 --- a/src/debug/telemetry/performance_monitor.c +++ b/src/debug/telemetry/performance_monitor.c @@ -8,6 +8,7 @@ #include <sof/debug/telemetry/performance_monitor.h> #include <sof/debug/telemetry/telemetry.h> #include <sof/lib/cpu.h> +#include <sof/lib/memory.h> #include <sof/lib_manager.h> #include <zephyr/sys/bitarray.h> @@ -128,7 +129,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 +139,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 +214,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 +347,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 +379,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))) @@ -411,11 +428,13 @@ SYS_BITARRAY_DEFINE_STATIC(io_performance_data_bit_array, PERFORMANCE_DATA_ENTRI static struct io_perf_monitor_ctx perf_monitor_ctx; static struct io_perf_data_item io_perf_data_items[IO_PERFORMANCE_MAX_ENTRIES]; -int io_perf_monitor_init(void) +__cold int io_perf_monitor_init(void) { int ret; struct io_perf_monitor_ctx *self = &perf_monitor_ctx; + assert_can_be_cold(); + k_spinlock_init(&self->lock); k_spinlock_key_t key = k_spin_lock(&self->lock); @@ -431,24 +450,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..5464b93bb281 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; @@ -188,7 +186,7 @@ static int tester_free(struct processing_module *mod) return ret; } -static int tester_bind(struct processing_module *mod, struct bind_info *bind_data) +static int tester_bind(struct processing_module *mod, const struct bind_info *bind_data) { struct tester_module_data *cd = module_get_private_data(mod); int ret = 0; @@ -199,7 +197,7 @@ static int tester_bind(struct processing_module *mod, struct bind_info *bind_dat return ret; } -static int tester_unbind(struct processing_module *mod, struct bind_info *unbind_data) +static int tester_unbind(struct processing_module *mod, const struct bind_info *unbind_data) { struct tester_module_data *cd = module_get_private_data(mod); int ret = 0; @@ -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.h b/src/debug/tester/tester.h index 79a65b65a7b2..3fc4e9c49038 100644 --- a/src/debug/tester/tester.h +++ b/src/debug/tester/tester.h @@ -75,12 +75,12 @@ struct tester_test_case_interface { /** * copy of module bind method, with additional ctx param */ - int (*bind)(void *ctx, struct processing_module *mod, struct bind_info *bind_data); + int (*bind)(void *ctx, struct processing_module *mod, const struct bind_info *bind_data); /** * copy of module unbind method, with additional ctx param */ - int (*unbind)(void *ctx, struct processing_module *mod, struct bind_info *unbind_data); + int (*unbind)(void *ctx, struct processing_module *mod, const struct bind_info *unbind_data); /** * copy of module trigger method, with additional ctx param 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/CMakeLists.txt b/src/drivers/amd/CMakeLists.txt index a6ece5e3a0ed..8b58af2b3017 100644 --- a/src/drivers/amd/CMakeLists.txt +++ b/src/drivers/amd/CMakeLists.txt @@ -1,9 +1,13 @@ # SPDX-License-Identifier: BSD-3-Clause -add_subdirectory(common) +if(CONFIG_RENOIR OR CONFIG_VANGOGH OR CONFIG_REMBRANDT OR CONFIG_ACP_6_3 OR CONFIG_ACP_7_0) + add_subdirectory(common) +endif() if(CONFIG_RENOIR) add_subdirectory(renoir) elseif(CONFIG_REMBRANDT OR CONFIG_ACP_6_3 OR CONFIG_ACP_7_0) add_subdirectory(rembrandt) elseif(CONFIG_VANGOGH) add_subdirectory(vangogh) +elseif(CONFIG_ACP_7_X) + add_subdirectory(acp_7_x) endif() diff --git a/src/drivers/amd/acp_7_x/CMakeLists.txt b/src/drivers/amd/acp_7_x/CMakeLists.txt new file mode 100644 index 000000000000..5dd81656d1ea --- /dev/null +++ b/src/drivers/amd/acp_7_x/CMakeLists.txt @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: BSD-3-Clause + +add_local_sources(sof + ipc.c +) diff --git a/src/drivers/amd/acp_7_x/ipc.c b/src/drivers/amd/acp_7_x/ipc.c new file mode 100644 index 000000000000..777ad3e08494 --- /dev/null +++ b/src/drivers/amd/acp_7_x/ipc.c @@ -0,0 +1,279 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright(c) 2026 AMD. All rights reserved. + * + * Author: Basavaraj Hiregoudar <basavaraj.hiregoudar@amd.com> + * Sneha Voona <sneha.voona@amd.com> + * DineshKumar Kalva <DineshKumar.Kalva@amd.com> + */ + +#include <rtos/panic.h> +#include <rtos/interrupt.h> +#include <sof/ipc/driver.h> +#include <sof/ipc/msg.h> +#include <sof/ipc/schedule.h> +#include <rtos/alloc.h> +#include <sof/lib/dma.h> +#include <sof/lib/mailbox.h> +#include <sof/lib/memory.h> +#include <sof/lib/uuid.h> +#include <rtos/wait.h> +#include <sof/list.h> +#include <sof/platform.h> +#include <sof/schedule/edf_schedule.h> +#include <sof/schedule/schedule.h> +#include <rtos/task.h> +#include <rtos/spinlock.h> +#include <ipc/header.h> +#include <ipc/topology.h> +#include <errno.h> +#include <stddef.h> +#include <stdint.h> +#include <platform/platform.h> +#include <platform/ipc.h> +#include <zephyr/logging/log.h> + +volatile acp_scratch_mem_config_t *pscratch_mem_cfg = + (volatile acp_scratch_mem_config_t *)(PU_SCRATCH_REG_BASE + + SCRATCH_REG_OFFSET); + +SOF_DEFINE_REG_UUID(ipc_task_amd); +LOG_MODULE_REGISTER(ipc_handler_file, LOG_LEVEL_DBG); + +#define HOST_TO_DSP_INTR 1 +#define INTERRUPT_DISABLE 0 +#define IRQ_NUM_EXT_LEVEL3 3 +#define IRQ_EXT_IPC_LEVEL_3 3 + +/* 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); +} + +/* Clear the Acknowledge ( status) for the host to DSP interrupt */ +static 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); +} + +inline uint32_t sof_ipc_host_status(void) +{ + return (pscratch_mem_cfg->acp_host_ack_write | pscratch_mem_cfg->acp_host_msg_write); +} + +inline uint32_t sof_ipc_host_msg_flag(void) +{ + return pscratch_mem_cfg->acp_host_msg_write; +} + +inline uint32_t sof_ipc_host_ack_flag(void) +{ + return pscratch_mem_cfg->acp_host_ack_write; +} + +inline uint32_t sof_ipc_dsp_status(void) +{ + return (pscratch_mem_cfg->acp_dsp_msg_write | pscratch_mem_cfg->acp_dsp_ack_write); +} + +inline void sof_ipc_host_ack_clear(void) +{ + pscratch_mem_cfg->acp_host_ack_write = 0; +} + +inline void sof_ipc_host_msg_clear(void) +{ + pscratch_mem_cfg->acp_host_msg_write = 0; +} + +inline void sof_ipc_dsp_ack_set(void) +{ + pscratch_mem_cfg->acp_dsp_ack_write = 1; +} + +inline void sof_ipc_dsp_msg_set(void) +{ + pscratch_mem_cfg->acp_dsp_msg_write = 1; +} + +enum task_state ipc_platform_do_cmd(struct ipc *ipc) +{ + struct ipc_cmd_hdr *hdr; + + hdr = mailbox_validate(); + ipc_cmd(hdr); + return SOF_TASK_STATE_COMPLETED; +} + +int platform_ipc_init(struct ipc *ipc) +{ + ipc_set_drvdata(ipc, NULL); + /* schedule */ + schedule_task_init_edf(&ipc->ipc_task, SOF_UUID(ipc_task_amd_uuid), + &ipc_task_ops, ipc, 0, 0); + interrupt_disable(IRQ_EXT_IPC_LEVEL_3, ipc); + interrupt_unregister(IRQ_EXT_IPC_LEVEL_3, ipc); + interrupt_register(IRQ_EXT_IPC_LEVEL_3, amd_irq_handler, ipc); + /* Enabling software interuppts */ + interrupt_enable(IRQ_EXT_IPC_LEVEL_3, ipc); + return 0; +} + +void amd_irq_handler(void *arg) +{ + struct ipc *ipc = arg; + uint32_t status; + uint32_t lock; + acp_dsp_sw_intr_stat_t swintrstat; + acp_sw_intr_trig_t swintrtrig; + + swintrstat = (acp_dsp_sw_intr_stat_t)io_reg_read(PU_REGISTER_BASE + ACP_DSP_SW_INTR_STAT); + status = swintrstat.u32all & HOST_TO_DSP_INTR; + if (status) { + /* Interrupt source */ + lock = io_reg_read(PU_REGISTER_BASE + ACP_AXI2DAGB_SEM_0); + /* Removed unbounded while(lock) loop - using bounded loop below instead */ + int timeout = 20000; + + while (lock && --timeout > 0) { + lock = io_reg_read(PU_REGISTER_BASE + ACP_AXI2DAGB_SEM_0); + } + if (timeout == 0) { + /* Give up, don't block forever */ + LOG_ERR("ACP semaphore timeout in IRQ handler"); + return; + } + /***************************************************** */ + /* Configures the trigger bit in ACP_DSP_SW_INTR_TRIG register */ + swintrtrig = (acp_sw_intr_trig_t) + io_reg_read(PU_REGISTER_BASE + ACP_SW_INTR_TRIG); + swintrtrig.bits.trig_host_to_dsp0_intr1 = INTERRUPT_DISABLE; + swintrtrig.bits.trig_dsp0_to_host_intr = INTERRUPT_DISABLE; + io_reg_write((PU_REGISTER_BASE + ACP_SW_INTR_TRIG), + swintrtrig.u32all); + + /* Clear the Host to DSP Status Register */ + acp_ack_intr_from_host(); + /********************************************* */ + + if (sof_ipc_host_status()) { + if (sof_ipc_host_ack_flag()) { + /* Clear the ACK from host */ + sof_ipc_host_ack_clear(); + } + /* Check if new message from host */ + if (sof_ipc_host_msg_flag()) { + /* Clear the msg bit from host*/ + sof_ipc_host_msg_clear(); + ipc_schedule_process(ipc); + } + } else { + tr_err(&ipc_tr, "IPC:interrupt without setting flags host status 0x%x", sof_ipc_host_status()); + io_reg_write((PU_REGISTER_BASE + ACP_AXI2DAGB_SEM_0), 0); + } + } +} + +void ipc_platform_complete_cmd(struct ipc *ipc) +{ + acp_sw_intr_trig_t sw_intr_trig; + + /* Set Dsp Ack for msg from host */ + sof_ipc_dsp_ack_set(); + /* Configures the trigger bit in ACP_DSP_SW_INTR_TRIG register */ + sw_intr_trig = (acp_sw_intr_trig_t) + io_reg_read(PU_REGISTER_BASE + ACP_SW_INTR_TRIG); + sw_intr_trig.bits.trig_host_to_dsp0_intr1 = INTERRUPT_DISABLE; + sw_intr_trig.bits.trig_dsp0_to_host_intr = INTERRUPT_DISABLE; + io_reg_write((PU_REGISTER_BASE + ACP_SW_INTR_TRIG), sw_intr_trig.u32all); + /* now interrupt host to tell it we have sent a message */ + acp_dsp_to_host_intr_trig(); + /* Disable the trigger bit in ACP_DSP_SW_INTR_TRIG register */ + sw_intr_trig = (acp_sw_intr_trig_t)io_reg_read(PU_REGISTER_BASE + ACP_SW_INTR_TRIG); + sw_intr_trig.bits.trig_dsp0_to_host_intr = INTERRUPT_DISABLE; + io_reg_write((PU_REGISTER_BASE + ACP_SW_INTR_TRIG), sw_intr_trig.u32all); + io_reg_write((PU_REGISTER_BASE + ACP_AXI2DAGB_SEM_0), 0); + LOG_DBG("IPC cmd completed"); + if (ipc->pm_prepare_D3) { + while (1) { + wait_for_interrupt(0); + } + } +} + +int ipc_platform_send_msg(const struct ipc_msg *msg) +{ + int ret = 0; + acp_sw_intr_trig_t sw_intr_trig; + acp_dsp_sw_intr_stat_t sw_intr_stat; + uint32_t status; + uint32_t lock; + /* Check if host cleared the status for previous messages */ + sw_intr_stat = (acp_dsp_sw_intr_stat_t) + io_reg_read(PU_REGISTER_BASE + ACP_DSP_SW_INTR_STAT); + status = sw_intr_stat.bits.dsp0_to_host_intr_stat; + if (sof_ipc_dsp_status() || status) { + return -EBUSY; + } + lock = io_reg_read(PU_REGISTER_BASE + ACP_AXI2DAGB_SEM_0); + int timeout = 20000; + + while (lock && --timeout > 0) { + lock = io_reg_read(PU_REGISTER_BASE + ACP_AXI2DAGB_SEM_0); + } + if (timeout == 0) { + /* Give up, don't block forever */ + LOG_ERR("ACP semaphore timeout in IPC send"); + return -EBUSY; + } + + /* Write new message in the mailbox */ + mailbox_dspbox_write(0, msg->tx_data, msg->tx_size); + /* Need to set DSP message flag */ + sof_ipc_dsp_msg_set(); + /* Trigger host interrupt to notify new message from DSP */ + /* Configures the trigger bit in ACP_DSP_SW_INTR_TRIG register */ + sw_intr_trig = (acp_sw_intr_trig_t) + io_reg_read(PU_REGISTER_BASE + ACP_SW_INTR_TRIG); + sw_intr_trig.bits.trig_host_to_dsp0_intr1 = INTERRUPT_DISABLE; + sw_intr_trig.bits.trig_dsp0_to_host_intr = INTERRUPT_DISABLE; + io_reg_write((PU_REGISTER_BASE + ACP_SW_INTR_TRIG), sw_intr_trig.u32all); + acp_dsp_to_host_intr_trig(); + /* Disable the trigger bit in ACP_DSP_SW_INTR_TRIG register */ + sw_intr_trig = (acp_sw_intr_trig_t)io_reg_read(PU_REGISTER_BASE + ACP_SW_INTR_TRIG); + sw_intr_trig.bits.trig_dsp0_to_host_intr = INTERRUPT_DISABLE; + io_reg_write((PU_REGISTER_BASE + ACP_SW_INTR_TRIG), sw_intr_trig.u32all); + io_reg_write((PU_REGISTER_BASE + ACP_AXI2DAGB_SEM_0), lock); + return ret; +} + +void ipc_platform_send_msg_direct(const struct ipc_msg *msg) +{ + acp_sw_intr_trig_t sw_intr_trig; + + /* Write message directly to mailbox - no status checks for emergency */ + mailbox_dspbox_write(0, msg->tx_data, msg->tx_size); + + /* Set DSP message flag */ + sof_ipc_dsp_msg_set(); + + /* Interrupt host immediately */ + acp_dsp_to_host_intr_trig(); + + /* Disable the trigger bit */ + sw_intr_trig = (acp_sw_intr_trig_t)io_reg_read(PU_REGISTER_BASE + ACP_SW_INTR_TRIG); + sw_intr_trig.bits.trig_dsp0_to_host_intr = INTERRUPT_DISABLE; + io_reg_write((PU_REGISTER_BASE + ACP_SW_INTR_TRIG), sw_intr_trig.u32all); +} 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_dma.c b/src/drivers/amd/common/acp_dma.c index bd1e6cba16a2..df68b8bebfb6 100644 --- a/src/drivers/amd/common/acp_dma.c +++ b/src/drivers/amd/common/acp_dma.c @@ -314,6 +314,7 @@ static int acp_dma_probe(struct dma *dma) sizeof(struct acp_dma_chan_data)); if (!acp_dma_chan) { rfree(dma->chan); + dma->chan = NULL; tr_err(&acpdma_tr, "acp-dma: %d channel %d private data alloc failed", dma->plat_data.id, channel); return -ENOMEM; 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..005980454798 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; @@ -532,8 +532,6 @@ static int dw_dma_set_config(struct dma_chan_data *channel, /* do we need to realloc descriptors */ if (config->elem_array.count != channel->desc_count) { - channel->desc_count = config->elem_array.count; - /* * Allocate descriptors for channel. They must be cache-line * size aligned to avoid corrupting adjacent memory when @@ -542,18 +540,25 @@ static int dw_dma_set_config(struct dma_chan_data *channel, * allocations on Zephyr to always force cache-line size * alignment. */ - if (dw_chan->lli) - rfree(dw_chan->lli); + rfree(dw_chan->lli); dw_chan->lli = rmalloc(SOF_MEM_FLAG_KERNEL | SOF_MEM_FLAG_COHERENT | SOF_MEM_FLAG_DMA, - sizeof(struct dw_lli) * channel->desc_count); + sizeof(struct dw_lli) * config->elem_array.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); + /* allocation failed, so dw_chan->lli is now NULL; reset + * the count to match it so a later config does not + * bzero() a NULL pointer using a stale count + */ + channel->desc_count = 0; ret = -ENOMEM; goto out; } + + /* only commit the new count once the buffer is allocated */ + channel->desc_count = config->elem_array.count; } /* initialise descriptors */ @@ -600,7 +605,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 +633,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 +711,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 +722,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 +844,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 +864,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 +893,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 +945,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 +966,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 +994,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 +1029,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 +1060,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 +1076,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 +1095,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..e14db9e6796e 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,9 +441,9 @@ 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) + if (eventnum < 0 || eventnum >= SDMA_HWEVENTS_COUNT) return; /* No change if request is invalid */ dma_reg_update_bits(channel->dma, SDMA_CHNENBL(eventnum), @@ -460,9 +459,9 @@ 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) + if (eventnum < 0 || eventnum >= SDMA_HWEVENTS_COUNT) return; /* No change if request is invalid */ dma_reg_update_bits(channel->dma, SDMA_CHNENBL(eventnum), @@ -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/interrupt.c b/src/drivers/interrupt.c index 5bc1a26e53f7..a1215314cb9d 100644 --- a/src/drivers/interrupt.c +++ b/src/drivers/interrupt.c @@ -43,7 +43,7 @@ DECLARE_TR_CTX(irq_tr, SOF_UUID(irq_uuid), LOG_LEVEL_INFO); #define interrupt_disable mux_interrupt_disable #endif -static SHARED_DATA struct cascade_root cascade_root; +static struct cascade_root cascade_root; static int interrupt_register_internal(uint32_t irq, void (*handler)(void *arg), void *arg, struct irq_desc *desc); @@ -168,8 +168,7 @@ struct irq_cascade_desc *interrupt_get_parent(uint32_t irq) void interrupt_init(struct sof *sof) { - sof->cascade_root = platform_shared_get(&cascade_root, - sizeof(cascade_root)); + sof->cascade_root = &cascade_root; sof->cascade_root->last_irq = PLATFORM_IRQ_FIRST_CHILD - 1; k_spinlock_init(&sof->cascade_root->lock); 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/drivers/mediatek/afe/zephyr_mtk_afe_memif.c b/src/drivers/mediatek/afe/zephyr_mtk_afe_memif.c new file mode 100644 index 000000000000..910e497f827e --- /dev/null +++ b/src/drivers/mediatek/afe/zephyr_mtk_afe_memif.c @@ -0,0 +1,418 @@ +/* + * Copyright 2026 MediaTek + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include <zephyr/drivers/dma.h> +#include <zephyr/logging/log.h> +#include <sof/drivers/afe-drv.h> +#include <sof/drivers/afe-dai.h> +#include <ipc/dai.h> + +#define DT_DRV_COMPAT mediatek_afe_memif_dma + +LOG_MODULE_REGISTER(mtk_afe, CONFIG_SOF_LOG_LEVEL); + +struct mtk_memif_chan_data { + int direction; /* 1 = downlink/playback, 0 = uplink/capture */ + int memif_id; + int dai_id; + int irq_id; + struct mtk_base_afe *afe; + + uint32_t dma_base; + uint32_t dma_size; + uint32_t rptr; + uint32_t wptr; + uint32_t period_size; + + unsigned int channel; + unsigned int rate; + unsigned int format; + + enum { + MEMIF_STATE_INIT = 0, + MEMIF_STATE_CONFIGURED, + MEMIF_STATE_ACTIVE, + MEMIF_STATE_PAUSED, + } state; +}; + +struct mtk_memif_dma_cfg { + uint32_t afe_base_reg; + uint32_t num_channels; +}; + +struct mtk_memif_dma_data { + struct dma_context ctx; + atomic_t channel_flags; + struct mtk_memif_chan_data *channels; + struct mtk_base_afe *afe; + bool afe_probed; +}; + +static int mtk_memif_config(const struct device *dev, uint32_t chan_id, + struct dma_config *config) +{ + struct mtk_memif_dma_data *data = dev->data; + struct mtk_memif_chan_data *chan; + int direction, dai_id, irq_id, ret; + uint32_t dma_addr = 0; + uint32_t dma_size = 0; + + if (chan_id >= data->ctx.dma_channels) { + LOG_ERR("channel %d out of range", chan_id); + return -EINVAL; + } + + if (!config->head_block) { + LOG_ERR("no block config provided"); + return -EINVAL; + } + + if (!config->cyclic) { + LOG_ERR("only cyclic configurations are supported"); + return -ENOTSUP; + } + + if (config->block_count == 0 || config->head_block->block_size == 0) { + LOG_ERR("invalid block_count %u or block_size %u", + config->block_count, config->head_block->block_size); + return -EINVAL; + } + + if (config->channel_direction != MEMORY_TO_PERIPHERAL && + config->channel_direction != PERIPHERAL_TO_MEMORY) { + LOG_ERR("unsupported direction: %d", config->channel_direction); + return -EINVAL; + } + + if (!data->afe_probed) { + struct mtk_base_afe *afe = afe_get(); + + ret = afe_probe(afe); + if (ret < 0) { + LOG_ERR("afe_probe failed: %d", ret); + return ret; + } + data->afe = afe; + data->afe_probed = true; + /* Initialize all channels with afe reference */ + for (uint32_t i = 0; i < data->ctx.dma_channels; i++) { + data->channels[i].afe = afe; + data->channels[i].memif_id = i; + } + } + + chan = &data->channels[chan_id]; + direction = afe_memif_get_direction(chan->afe, chan->memif_id); + + /* dma_slot is only 8 bits in Zephyr's struct dma_config, so only the + * low byte of the AFE handshake fits. The low byte contains the + * dai_id (AFE_HS_GET_DAI extracts bits [7:0]). The memif_index + * (bits [23:16]) doesn't fit but isn't needed here since chan_id + * already maps to the correct memif via chan_filter. + */ + dai_id = config->dma_slot & 0xFF; /* AFE_HS_GET_DAI equivalent */ + irq_id = 0; /* IRQ not used in Zephyr mode */ + + switch (config->channel_direction) { + case MEMORY_TO_PERIPHERAL: + if (direction != 1) { + LOG_ERR("channel %d is not a playback memif", chan_id); + return -EINVAL; + } + dma_addr = config->head_block->source_address; + break; + case PERIPHERAL_TO_MEMORY: + if (direction != 0) { + LOG_ERR("channel %d is not a capture memif", chan_id); + return -EINVAL; + } + dma_addr = config->head_block->dest_address; + break; + } + + dma_size = config->head_block->block_size * config->block_count; + + chan->dai_id = dai_id; + chan->irq_id = irq_id; + chan->dma_base = dma_addr; + chan->dma_size = dma_size; + chan->direction = direction; + chan->rptr = 0; + chan->wptr = 0; + chan->period_size = config->head_block->block_size; + + ret = afe_dai_get_config(chan->afe, dai_id, + &chan->channel, &chan->rate, &chan->format); + if (ret < 0) { + LOG_ERR("failed to get DAI config for dai %d", dai_id); + return ret; + } + + switch (config->source_data_size) { + case 2: + chan->format = SOF_IPC_FRAME_S16_LE; + break; + case 4: + chan->format = SOF_IPC_FRAME_S32_LE; + break; + default: + LOG_ERR("unsupported data width: %d", config->source_data_size); + return -ENOTSUP; + } + + ret = afe_memif_set_params(chan->afe, chan->memif_id, + chan->channel, chan->rate, chan->format); + if (ret < 0) { + return ret; + } + + ret = afe_memif_set_addr(chan->afe, chan->memif_id, + chan->dma_base, chan->dma_size); + if (ret < 0) { + return ret; + } + + chan->state = MEMIF_STATE_CONFIGURED; + + LOG_DBG("channel %d configured: memif=%d dai=%d base=0x%x size=%u", + chan_id, chan->memif_id, dai_id, dma_addr, dma_size); + + return 0; +} + +static int mtk_memif_start(const struct device *dev, uint32_t chan_id) +{ + struct mtk_memif_dma_data *data = dev->data; + struct mtk_memif_chan_data *chan; + + if (chan_id >= data->ctx.dma_channels) { + return -EINVAL; + } + + chan = &data->channels[chan_id]; + + if (chan->state != MEMIF_STATE_CONFIGURED && + chan->state != MEMIF_STATE_PAUSED) { + LOG_ERR("channel %d not in valid state for start: %d", + chan_id, chan->state); + return -EINVAL; + } + + chan->state = MEMIF_STATE_ACTIVE; + return afe_memif_set_enable(chan->afe, chan->memif_id, 1); +} + +static int mtk_memif_stop(const struct device *dev, uint32_t chan_id) +{ + struct mtk_memif_dma_data *data = dev->data; + struct mtk_memif_chan_data *chan; + + if (chan_id >= data->ctx.dma_channels) { + return -EINVAL; + } + + chan = &data->channels[chan_id]; + + switch (chan->state) { + case MEMIF_STATE_CONFIGURED: + return 0; + case MEMIF_STATE_PAUSED: + case MEMIF_STATE_ACTIVE: + break; + default: + return -EINVAL; + } + + chan->state = MEMIF_STATE_CONFIGURED; + return afe_memif_set_enable(chan->afe, chan->memif_id, 0); +} + +static int mtk_memif_suspend(const struct device *dev, uint32_t chan_id) +{ + struct mtk_memif_dma_data *data = dev->data; + struct mtk_memif_chan_data *chan; + + if (chan_id >= data->ctx.dma_channels) { + return -EINVAL; + } + + chan = &data->channels[chan_id]; + + if (chan->state != MEMIF_STATE_ACTIVE) { + return -EINVAL; + } + + chan->state = MEMIF_STATE_PAUSED; + return afe_memif_set_enable(chan->afe, chan->memif_id, 0); +} + +static int mtk_memif_resume(const struct device *dev, uint32_t chan_id) +{ + struct mtk_memif_dma_data *data = dev->data; + struct mtk_memif_chan_data *chan; + + if (chan_id >= data->ctx.dma_channels) { + return -EINVAL; + } + + chan = &data->channels[chan_id]; + + if (chan->state != MEMIF_STATE_PAUSED) { + return -EINVAL; + } + + chan->state = MEMIF_STATE_ACTIVE; + return afe_memif_set_enable(chan->afe, chan->memif_id, 1); +} + +static int mtk_memif_reload(const struct device *dev, uint32_t chan_id, + uint32_t src, uint32_t dst, size_t size) +{ + struct mtk_memif_dma_data *data = dev->data; + struct mtk_memif_chan_data *chan; + + if (chan_id >= data->ctx.dma_channels) { + return -EINVAL; + } + + chan = &data->channels[chan_id]; + + /* update software pointer tracking */ + if (chan->direction) { + chan->wptr = (chan->wptr + size) % chan->dma_size; + } else { + chan->rptr = (chan->rptr + size) % chan->dma_size; + } + + return 0; +} + +static int mtk_memif_get_status(const struct device *dev, uint32_t chan_id, + struct dma_status *stat) +{ + struct mtk_memif_dma_data *data = dev->data; + struct mtk_memif_chan_data *chan; + uint32_t hw_ptr; + + if (chan_id >= data->ctx.dma_channels) { + return -EINVAL; + } + + chan = &data->channels[chan_id]; + + hw_ptr = afe_memif_get_cur_position(chan->afe, chan->memif_id); + if (!hw_ptr) { + stat->read_position = 0; + stat->write_position = 0; + return -EINVAL; + } + + hw_ptr -= chan->dma_base; + + /* Validate hw_ptr is within buffer range */ + if (hw_ptr >= chan->dma_size) { + LOG_WRN("hw_ptr 0x%x out of range (size %u), clamping", + hw_ptr, chan->dma_size); + hw_ptr %= chan->dma_size; + } + + if (chan->direction) { + chan->rptr = hw_ptr; + } else { + chan->wptr = hw_ptr; + } + + stat->read_position = chan->rptr + chan->dma_base; + stat->write_position = chan->wptr + chan->dma_base; + stat->busy = (chan->state == MEMIF_STATE_ACTIVE); + + uint32_t avail = (chan->wptr + chan->dma_size - chan->rptr) % chan->dma_size; + + stat->pending_length = avail; + stat->free = chan->dma_size - avail; + + return 0; +} + +static int mtk_memif_get_attribute(const struct device *dev, + uint32_t type, uint32_t *value) +{ + switch (type) { + case DMA_ATTR_COPY_ALIGNMENT: + case DMA_ATTR_BUFFER_SIZE_ALIGNMENT: + *value = 4; + break; + case DMA_ATTR_BUFFER_ADDRESS_ALIGNMENT: + *value = CONFIG_DCACHE_LINE_SIZE; + break; + case DMA_ATTR_MAX_BLOCK_COUNT: + *value = 4; + break; + default: + return -ENOENT; + } + return 0; +} + +/* Channel filter: ensures dma_request_channel() returns the specific + * channel (memif index) requested via the stream_tag parameter, rather + * than an arbitrary free channel. Each AFE MEMIF channel corresponds to + * a specific hardware MEMIF, so channel IDs must match memif indices. + */ +static bool mtk_memif_chan_filter(const struct device *dev, int channel, + void *filter_param) +{ + uint32_t requested = *(uint32_t *)filter_param; + + return channel == requested; +} + +static DEVICE_API(dma, mtk_afe_memif_api) = { + .config = mtk_memif_config, + .start = mtk_memif_start, + .stop = mtk_memif_stop, + .suspend = mtk_memif_suspend, + .resume = mtk_memif_resume, + .reload = mtk_memif_reload, + .get_status = mtk_memif_get_status, + .get_attribute = mtk_memif_get_attribute, + .chan_filter = mtk_memif_chan_filter, +}; + +static int mtk_memif_init(const struct device *dev) +{ + struct mtk_memif_dma_data *data = dev->data; + + data->channel_flags = ATOMIC_INIT(0); + data->ctx.atomic = &data->channel_flags; + return 0; +} + +#define MTK_AFE_MEMIF_CHAN_DECLARE(idx) {} + +#define MTK_AFE_MEMIF_INIT(inst) \ + static struct mtk_memif_chan_data \ + mtk_memif_channels_##inst[DT_INST_PROP(inst, dma_channels)]; \ + \ + static struct mtk_memif_dma_data mtk_memif_data_##inst = { \ + .ctx.magic = DMA_MAGIC, \ + .ctx.dma_channels = DT_INST_PROP(inst, dma_channels), \ + .channels = mtk_memif_channels_##inst, \ + }; \ + \ + static const struct mtk_memif_dma_cfg mtk_memif_cfg_##inst = { \ + .afe_base_reg = DT_INST_REG_ADDR(inst), \ + .num_channels = DT_INST_PROP(inst, dma_channels), \ + }; \ + \ + DEVICE_DT_INST_DEFINE(inst, mtk_memif_init, NULL, \ + &mtk_memif_data_##inst, \ + &mtk_memif_cfg_##inst, \ + PRE_KERNEL_1, CONFIG_DMA_INIT_PRIORITY, \ + &mtk_afe_memif_api); + +DT_INST_FOREACH_STATUS_OKAY(MTK_AFE_MEMIF_INIT) diff --git a/src/drivers/mediatek/afe/zephyr_mtk_dai.c b/src/drivers/mediatek/afe/zephyr_mtk_dai.c new file mode 100644 index 000000000000..3e8fe0fa7dbe --- /dev/null +++ b/src/drivers/mediatek/afe/zephyr_mtk_dai.c @@ -0,0 +1,162 @@ +/* + * Copyright 2026 MediaTek + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include <zephyr/device.h> +#include <zephyr/drivers/dai.h> +#include <zephyr/logging/log.h> +#include <sof/drivers/afe-drv.h> +#include <ipc/stream.h> +#include <ipc/dai.h> +#include <ipc/dai-mediatek.h> + +#define DT_DRV_COMPAT mediatek_afe + +LOG_MODULE_REGISTER(dai_mtk_afe); + +struct dai_mtk_afe_cfg { + uint32_t dai_index; + uint32_t memif_index; + bool downlink; +}; + +struct dai_mtk_afe_data { + struct dai_config cfg; + struct dai_properties tx_props; + struct dai_properties rx_props; +}; + +static int dai_mtk_afe_probe(const struct device *dev) +{ + LOG_DBG("%s: probe", dev->name); + return 0; +} + +static int dai_mtk_afe_remove(const struct device *dev) +{ + LOG_DBG("%s: remove", dev->name); + return 0; +} + +static int dai_mtk_afe_config_set(const struct device *dev, + const struct dai_config *cfg, + const void *bespoke_cfg, size_t size) +{ + const struct dai_mtk_afe_cfg *dev_cfg = dev->config; + struct dai_mtk_afe_data *data = dev->data; + const struct sof_ipc_dai_afe_params *afe_cfg = bespoke_cfg; + struct mtk_base_afe *afe; + int ret; + + if (cfg) { + data->cfg.channels = cfg->channels; + data->cfg.rate = cfg->rate; + data->cfg.format = cfg->format; + data->cfg.word_size = cfg->word_size; + } + + if (afe_cfg) { + int probe_ret; + + afe = afe_get(); + probe_ret = afe_probe(afe); + if (probe_ret < 0) { + LOG_ERR("%s: afe_probe failed: %d", dev->name, probe_ret); + return probe_ret; + } + ret = afe_dai_set_config(afe, dev_cfg->dai_index, + afe_cfg->dai_channels, + afe_cfg->dai_rate, + afe_cfg->dai_format); + if (ret < 0) { + LOG_ERR("%s: afe_dai_set_config failed: %d", + dev->name, ret); + return ret; + } + + LOG_DBG("%s: config_set dai=%u ch=%u rate=%u fmt=%u", + dev->name, dev_cfg->dai_index, + afe_cfg->dai_channels, + afe_cfg->dai_rate, + afe_cfg->dai_format); + } + + return 0; +} + +static int dai_mtk_afe_config_get(const struct device *dev, + struct dai_config *cfg, enum dai_dir dir) +{ + const struct dai_mtk_afe_cfg *dev_cfg = dev->config; + struct dai_mtk_afe_data *data = dev->data; + + if (!cfg) { + return -EINVAL; + } + + *cfg = data->cfg; + cfg->type = DAI_MEDIATEK_AFE; + cfg->dai_index = dev_cfg->dai_index; + + return 0; +} + +static const struct dai_properties *dai_mtk_afe_get_properties( + const struct device *dev, enum dai_dir dir, int stream_id) +{ + const struct dai_mtk_afe_data *data = dev->data; + + if (dir == DAI_DIR_TX) { + return &data->tx_props; + } + return &data->rx_props; +} + +static int dai_mtk_afe_trigger(const struct device *dev, + enum dai_dir dir, enum dai_trigger_cmd cmd) +{ + LOG_DBG("%s: trigger dir=%d cmd=%d", dev->name, dir, cmd); + return 0; +} + +static DEVICE_API(dai, dai_mtk_afe_api) = { + .probe = dai_mtk_afe_probe, + .remove = dai_mtk_afe_remove, + .config_set = dai_mtk_afe_config_set, + .config_get = dai_mtk_afe_config_get, + .get_properties = dai_mtk_afe_get_properties, + .trigger = dai_mtk_afe_trigger, +}; + +#define DAI_MTK_AFE_HANDSHAKE(inst) \ + ((inst) << 16) | DT_INST_PROP(inst, dai_id) + +#define DAI_MTK_AFE_INIT(inst) \ + static struct dai_mtk_afe_data dai_mtk_afe_data_##inst = { \ + .cfg = { \ + .type = DAI_MEDIATEK_AFE, \ + .dai_index = DT_INST_PROP(inst, dai_id), \ + }, \ + .tx_props = { \ + .dma_hs_id = DAI_MTK_AFE_HANDSHAKE(inst), \ + }, \ + .rx_props = { \ + .dma_hs_id = DAI_MTK_AFE_HANDSHAKE(inst), \ + }, \ + }; \ + \ + static const struct dai_mtk_afe_cfg dai_mtk_afe_cfg_##inst = { \ + .dai_index = DT_INST_PROP(inst, dai_id), \ + .memif_index = inst, \ + .downlink = DT_INST_PROP(inst, downlink), \ + }; \ + \ + DEVICE_DT_INST_DEFINE(inst, NULL, NULL, \ + &dai_mtk_afe_data_##inst, \ + &dai_mtk_afe_cfg_##inst, \ + POST_KERNEL, CONFIG_DAI_INIT_PRIORITY, \ + &dai_mtk_afe_api); + +DT_INST_FOREACH_STATUS_OKAY(DAI_MTK_AFE_INIT) diff --git a/src/drivers/mediatek/afe/zephyr_mtk_host_dma.c b/src/drivers/mediatek/afe/zephyr_mtk_host_dma.c new file mode 100644 index 000000000000..67673e55a87e --- /dev/null +++ b/src/drivers/mediatek/afe/zephyr_mtk_host_dma.c @@ -0,0 +1,221 @@ +/* + * Copyright 2026 MediaTek + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include <zephyr/drivers/dma.h> +#include <zephyr/logging/log.h> +#include <zephyr/cache.h> +#include <platform/lib/memory.h> + +/* used for driver binding */ +#define DT_DRV_COMPAT mediatek_sof_host_dma + +/* macros used to parse DTS properties */ +#define IDENTITY_VARGS(V, ...) IDENTITY(V) + +#define _SOF_HOST_DMA_CHANNEL_INDEX_ARRAY(inst)\ + LISTIFY(DT_INST_PROP_OR(inst, dma_channels, 0), IDENTITY_VARGS, (,)) + +#define _SOF_HOST_DMA_CHANNEL_DECLARE(idx) {} + +#define SOF_HOST_DMA_CHANNELS_DECLARE(inst)\ + FOR_EACH(_SOF_HOST_DMA_CHANNEL_DECLARE,\ + (,), _SOF_HOST_DMA_CHANNEL_INDEX_ARRAY(inst)) + +LOG_MODULE_REGISTER(mtk_sof_host_dma); + +/* Software-based DMA driver for MTK SOF host component. + * MTK DSP can access host memory directly, so this driver + * implements DMA transfers using memcpy + cache management. + */ + +enum channel_state { + CHAN_STATE_INIT = 0, + CHAN_STATE_CONFIGURED, +}; + +struct sof_host_dma_channel { + uint32_t src; + uint32_t dest; + uint32_t size; + uint32_t direction; + enum channel_state state; +}; + +struct sof_host_dma_data { + struct dma_context ctx; + atomic_t channel_flags; + struct sof_host_dma_channel *channels; +}; + +static int channel_change_state(struct sof_host_dma_channel *chan, + enum channel_state next) +{ + switch (chan->state) { + case CHAN_STATE_INIT: + case CHAN_STATE_CONFIGURED: + if (next != CHAN_STATE_CONFIGURED) { + return -EPERM; + } + break; + default: + LOG_ERR("invalid channel previous state: %d", chan->state); + return -EINVAL; + } + + chan->state = next; + return 0; +} + +static int mtk_host_dma_reload(const struct device *dev, uint32_t chan_id, + uint32_t src, uint32_t dst, size_t size) +{ + return 0; +} + +static int mtk_host_dma_config(const struct device *dev, uint32_t chan_id, + struct dma_config *config) +{ + struct sof_host_dma_data *data = dev->data; + struct sof_host_dma_channel *chan; + int ret; + + if (chan_id >= data->ctx.dma_channels) { + LOG_ERR("channel %d is not a valid channel ID", chan_id); + return -EINVAL; + } + + chan = &data->channels[chan_id]; + + ret = channel_change_state(chan, CHAN_STATE_CONFIGURED); + if (ret < 0) { + LOG_ERR("failed to change channel %d's state to CONFIGURED", chan_id); + return ret; + } + + if (config->block_count != 1) { + LOG_ERR("invalid number of blocks: %d", config->block_count); + return -EINVAL; + } + + if (!config->head_block->source_address) { + LOG_ERR("got NULL source address"); + return -EINVAL; + } + + if (!config->head_block->dest_address) { + LOG_ERR("got NULL destination address"); + return -EINVAL; + } + + if (!config->head_block->block_size) { + LOG_ERR("got 0 bytes to copy"); + return -EINVAL; + } + + if (config->channel_direction != HOST_TO_MEMORY && + config->channel_direction != MEMORY_TO_HOST) { + LOG_ERR("invalid channel direction: %d", config->channel_direction); + return -EINVAL; + } + + chan->src = config->head_block->source_address; + chan->dest = config->head_block->dest_address; + chan->size = config->head_block->block_size; + chan->direction = config->channel_direction; + + if (chan->direction == MEMORY_TO_HOST) { + sys_cache_data_invd_range(UINT_TO_POINTER(chan->src), chan->size); + } + + memcpy(UINT_TO_POINTER(chan->dest), UINT_TO_POINTER(chan->src), chan->size); + + if (chan->direction == HOST_TO_MEMORY) { + sys_cache_data_flush_range(UINT_TO_POINTER(chan->dest), chan->size); + } + + return 0; +} + +static int mtk_host_dma_start(const struct device *dev, uint32_t chan_id) +{ + return 0; +} + +static int mtk_host_dma_stop(const struct device *dev, uint32_t chan_id) +{ + return 0; +} + +static int mtk_host_dma_suspend(const struct device *dev, uint32_t chan_id) +{ + return 0; +} + +static int mtk_host_dma_resume(const struct device *dev, uint32_t chan_id) +{ + return 0; +} + +static int mtk_host_dma_get_status(const struct device *dev, + uint32_t chan_id, struct dma_status *stat) +{ + return 0; +} + +static int mtk_host_dma_get_attribute(const struct device *dev, + uint32_t type, uint32_t *val) +{ + switch (type) { + case DMA_ATTR_COPY_ALIGNMENT: + case DMA_ATTR_BUFFER_SIZE_ALIGNMENT: + *val = sizeof(uint32_t); + break; + case DMA_ATTR_BUFFER_ADDRESS_ALIGNMENT: + *val = PLATFORM_DCACHE_ALIGN; + break; + default: + LOG_ERR("invalid attribute type: %d", type); + return -EINVAL; + } + + return 0; +} + +static DEVICE_API(dma, mtk_host_dma_api) = { + .reload = mtk_host_dma_reload, + .config = mtk_host_dma_config, + .start = mtk_host_dma_start, + .stop = mtk_host_dma_stop, + .suspend = mtk_host_dma_suspend, + .resume = mtk_host_dma_resume, + .get_status = mtk_host_dma_get_status, + .get_attribute = mtk_host_dma_get_attribute, +}; + +static int mtk_host_dma_init(const struct device *dev) +{ + struct sof_host_dma_data *data = dev->data; + + data->channel_flags = ATOMIC_INIT(0); + data->ctx.atomic = &data->channel_flags; + + return 0; +} + +static struct sof_host_dma_channel channels[] = { + SOF_HOST_DMA_CHANNELS_DECLARE(0), +}; + +static struct sof_host_dma_data mtk_host_dma_data = { + .ctx.magic = DMA_MAGIC, + .ctx.dma_channels = ARRAY_SIZE(channels), + .channels = channels, +}; + +DEVICE_DT_INST_DEFINE(0, mtk_host_dma_init, NULL, + &mtk_host_dma_data, NULL, + PRE_KERNEL_1, CONFIG_DMA_INIT_PRIORITY, + &mtk_host_dma_api); diff --git a/src/idc/idc.c b/src/idc/idc.c index 466f84eee63f..195de0a18d2a 100644 --- a/src/idc/idc.c +++ b/src/idc/idc.c @@ -15,6 +15,7 @@ #include <rtos/alloc.h> #include <rtos/clk.h> #include <sof/lib/cpu.h> +#include <sof/lib/memory.h> #include <sof/lib/notifier.h> #include <sof/lib/pm_runtime.h> #include <sof/lib/uuid.h> @@ -38,7 +39,7 @@ LOG_MODULE_REGISTER(idc, CONFIG_SOF_LOG_LEVEL); /** \brief IDC message payload per core. */ -static SHARED_DATA struct idc_payload static_payload[CONFIG_CORE_COUNT]; +static struct idc_payload static_payload[CONFIG_CORE_COUNT]; SOF_DEFINE_REG_UUID(idc); @@ -184,20 +185,22 @@ 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); + dev->task = NULL; goto out; } } @@ -221,32 +224,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); } /** @@ -438,14 +421,16 @@ void idc_cmd(struct idc_msg *msg) } /* Runs on each CPU */ -int idc_init(void) +__cold int idc_init(void) { struct idc **idc = idc_get(); + assert_can_be_cold(); + tr_dbg(&idc_tr, "entry"); /* initialize idc data */ - (*idc)->payload = platform_shared_get(static_payload, sizeof(static_payload)); + (*idc)->payload = static_payload; #ifdef CONFIG_SOF_TELEMETRY_IO_PERFORMANCE_MEASUREMENTS struct io_perf_data_item init_data = {IO_PERF_IDC_ID, diff --git a/src/idc/zephyr_idc.c b/src/idc/zephyr_idc.c index afd30628a5d3..771ae967bfb1 100644 --- a/src/idc/zephyr_idc.c +++ b/src/idc/zephyr_idc.c @@ -115,33 +115,49 @@ 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; + if (!cpu_is_core_enabled(target_cpu)) { + tr_err(&zephyr_idc_tr, "Core %u is down, cannot send IDC message", target_cpu); + return -EACCES; + } + + 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; work->handler = idc_handler; work->sync = mode == IDC_BLOCKING; - if (!cpu_is_core_enabled(target_cpu)) { - tr_err(&zephyr_idc_tr, "Core %u is down, cannot sent IDC message", target_cpu); - return -EACCES; - } if (msg->payload) { idc_send_memcpy_err = memcpy_s(payload->data, sizeof(payload->data), msg->payload, msg->size); @@ -158,6 +174,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); @@ -179,12 +197,23 @@ int idc_send_msg(struct idc_msg *msg, uint32_t mode) return ret; } -void idc_init_thread(void) +__cold void idc_init_thread(void) { + char thread_name[] = "idc_p4wq0"; int cpu = cpu_get_id(); + assert_can_be_cold(); + 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-amd.h b/src/include/ipc/dai-amd.h index ceeb870c163e..4bfa10b150e8 100644 --- a/src/include/ipc/dai-amd.h +++ b/src/include/ipc/dai-amd.h @@ -23,6 +23,8 @@ struct sof_ipc_dai_acp_params { uint32_t reserved0; uint32_t fsync_rate; uint32_t tdm_slots; + uint32_t tdm_mode; + uint32_t format; } __attribute__((packed, aligned(4))); /* ACP Configuration Request - SOF_IPC_DAI_AMD_SDW_CONFIG */ diff --git a/src/include/ipc/dai.h b/src/include/ipc/dai.h index b5b29316f9e6..49ffa5d5fa94 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,13 @@ 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 */ + SOF_DAI_AMD_TDM /**< Amd TDM */ }; +#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; @@ -122,7 +127,7 @@ struct sof_ipc_dai_config { struct sof_ipc_dai_acp_params acpbt; struct sof_ipc_dai_acp_params acpsp; struct sof_ipc_dai_acpdmic_params acpdmic; - struct sof_ipc_dai_acp_params acphs; + struct sof_ipc_dai_acp_params acptdm; struct sof_ipc_dai_afe_params afe; struct sof_ipc_dai_micfil_params micfil; struct sof_ipc_dai_acp_sdw_params acpsdw; 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..2ae358010c2d 100644 --- a/src/include/ipc4/base_fw.h +++ b/src/include/ipc4/base_fw.h @@ -17,14 +17,18 @@ #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 */ +/* Max src queue count supported by ipc4 */ #define IPC4_MAX_SRC_QUEUE 8 +/* Max dst queue count supported by ipc4 */ +#define IPC4_MAX_DST_QUEUE 8 + /* Max module instance for single module count supported by ipc4 */ #define IPC4_MAX_MODULE_INSTANCES 256 @@ -373,12 +377,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..d6f839458d54 --- /dev/null +++ b/src/include/ipc4/handler.h @@ -0,0 +1,118 @@ +/* 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 Process MOD_CONFIG_GET or MOD_CONFIG_SET in any execution context. + * @param[in] ipc4 IPC4 message request. + * @param[in] set true for CONFIG_SET, false for CONFIG_GET. + * @param[out] reply_ext Receives extension value for CONFIG_GET (may be NULL). + * @return IPC4 status code (0 on success). + */ +int ipc4_process_module_config(struct ipc4_message_request *ipc4, + bool set, uint32_t *reply_ext); + +/** + * @brief Process MOD_LARGE_CONFIG_GET in any execution context. + * @param[in] ipc4 IPC4 message request. + * @param[out] reply_ext Receives extension value for reply. + * @param[out] reply_tx_size Receives TX data size for reply. + * @param[out] reply_tx_data Receives TX data pointer for reply. + * @return IPC4 status code (0 on success). + */ +int ipc4_process_large_config_get(struct ipc4_message_request *ipc4, + uint32_t *reply_ext, + uint32_t *reply_tx_size, + void **reply_tx_data); + +/** + * @brief Process MOD_LARGE_CONFIG_SET in any execution context. + * @param[in] ipc4 IPC4 message request. + * @return IPC4 status code (0 on success). + */ +int ipc4_process_large_config_set(struct ipc4_message_request *ipc4); + +/** + * \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 Process SET_PIPELINE_STATE IPC4 message (prepare + trigger phases). + * @param[in] ipc4 IPC4 message request. + * @return 0 on success, IPC4 error code otherwise. + */ +int ipc4_set_pipeline_state(struct ipc4_message_request *ipc4); + +/** + * \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 Connect components together on a pipeline. + * @param ipc The global IPC context. + * @param bu IPC4 bind-unbind data. + * @return IPC4_SUCCESS on success, error code otherwise. + */ +int ipc4_comp_connect(struct ipc *ipc, const struct ipc4_module_bind_unbind *bu); + +/** + * \brief Disconnect components in a pipeline. + * @param ipc The global IPC context. + * @param bu IPC4 bind-unbind data. + * @return IPC4_SUCCESS on success, error code otherwise. + */ +int ipc4_comp_disconnect(struct ipc *ipc, const struct ipc4_module_bind_unbind *bu); + +#if defined(__ZEPHYR__) && defined(CONFIG_SOF_FULL_ZEPHYR_APPLICATION) +/** + * \brief Increment the IPC compound message pre-start counter. + * @param[in] msg_id IPC message ID. + */ +__syscall 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. + */ +__syscall void ipc_compound_post_start(uint32_t msg_id, int ret, bool delayed); + +/** + * \brief Wait for the IPC compound message to complete. + * @return 0 on success, error code otherwise on timeout. + */ +__syscall int ipc_wait_for_compound_msg(void); + +#include <zephyr/syscalls/handler.h> +#else +void z_impl_ipc_compound_pre_start(int msg_id); +#define ipc_compound_pre_start z_impl_ipc_compound_pre_start +void z_impl_ipc_compound_post_start(uint32_t msg_id, int ret, bool delayed); +#define ipc_compound_post_start z_impl_ipc_compound_post_start +int z_impl_ipc_wait_for_compound_msg(void); +#define ipc_wait_for_compound_msg z_impl_ipc_wait_for_compound_msg +#endif + +#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..f026f837801a 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,9 @@ 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 heap_bytes; /* required heap size in bytes */ } __attribute__((packed, aligned(4))); /* @@ -150,6 +151,10 @@ struct ipc4_module_init_data { \remark hide_methods */ + +/* Reserved ppl_instance_id: module has no parent pipeline (e.g. probe). */ +#define IPC4_INVALID_PIPELINE_ID 0xFF + struct ipc4_module_init_instance { union { 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..1e85072fd15c 100644 --- a/src/include/ipc4/pipeline.h +++ b/src/include/ipc4/pipeline.h @@ -69,6 +69,39 @@ 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 heap_bytes; /* required heap size 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 +139,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/audio_stream.h b/src/include/module/audio/audio_stream.h index e032ef322f85..2d8308447d11 100644 --- a/src/include/module/audio/audio_stream.h +++ b/src/include/module/audio/audio_stream.h @@ -11,6 +11,7 @@ #include <stdint.h> #include <stdbool.h> +#include <rtos/panic.h> #include "../ipc/stream.h" @@ -74,4 +75,146 @@ struct sof_audio_stream_params { enum sof_audio_buffer_state state; /**< audio stream state */ }; +/** + * @brief Read-only view of source data in a circular buffer. + * + * Describes a contiguous fragment of a circular buffer obtained from the source + * API together with the buffer boundaries needed for wrap handling. All pointers + * are const because the source data must not be modified. + */ +struct cir_buf_source { + const void *buf_start; /**< Start address of the circular buffer. */ + const void *buf_end; /**< End address of the circular buffer. */ + const void *ptr; /**< Current read pointer within the buffer. */ +}; + +/** + * @brief Writable view of sink data in a circular buffer. + * + * Describes a contiguous fragment of a circular buffer obtained from the sink + * API together with the buffer boundaries needed for wrap handling. + */ +struct cir_buf_sink { + void *buf_start; /**< Start address of the circular buffer. */ + void *buf_end; /**< End address of the circular buffer. */ + void *ptr; /**< Current write pointer within the buffer. */ +}; + +/** + * @brief Calculates numbers of s16 samples to buffer wrap. + * @param ptr Read or write pointer of circular buffer. + * @param buf_start Start address of circular buffer. + * @param buf_samples Total size of circular buffer in samples. + * @return Number of samples to buffer wrap. + */ +static inline size_t cir_buf_samples_to_wrap_s16(const int16_t *ptr, const int16_t *buf_start, + size_t buf_samples) +{ + const int16_t *const buf_end = buf_start + buf_samples; + + assert(buf_end >= ptr); + + return buf_end - ptr; +} + +/** + * @brief Calculates numbers of s32 samples to buffer wrap. + * @param ptr Read or write pointer of circular buffer. + * @param buf_start Start address of circular buffer. + * @param buf_samples Total size of circular buffer in samples. + * @return Number of samples to buffer wrap. + */ +static inline size_t cir_buf_samples_to_wrap_s32(const int32_t *ptr, const int32_t *buf_start, + size_t buf_samples) +{ + const int32_t *const buf_end = buf_start + buf_samples; + + assert(buf_end >= ptr); + + return buf_end - ptr; +} + +/** + * @brief Calculates numbers of s16 samples to buffer wrap when reading stream + * backwards from current sample pointed by ptr towards begin. + * @param ptr Read or write pointer of circular buffer. + * @param buf_end End address of circular buffer. + * @return Number of samples to buffer wrap. + */ +static inline int cir_buf_samples_without_wrap_s16(const void *ptr, const void *buf_end) +{ + int to_end = (const int16_t *)buf_end - (const int16_t *)ptr; + + assert((intptr_t)buf_end >= (intptr_t)ptr); + return to_end; +} + +/** + * @brief Calculates numbers of s32 samples to buffer wrap when reading stream + * backwards from current sample pointed by ptr towards begin. + * @param ptr Read or write pointer og circular buffer. + * @param buf_end End address of circular buffer. + * @return Number of bytes to buffer wrap. For number of samples calculate + * need to add size of sample to returned bytes count. + */ +static inline int cir_buf_samples_without_wrap_s32(const void *ptr, const void *buf_end) +{ + int to_end = (const int32_t *)buf_end - (const int32_t *)ptr; + + assert((intptr_t)buf_end >= (intptr_t)ptr); + return to_end; +} + +/** + * Verifies the pointer and performs rollover when reached the end of + * the circular buffer. + * @param ptr Pointer + * @param buf_addr Start address of the circular buffer. + * @param buf_end End address of the circular buffer. + * @return Pointer, adjusted if necessary. + */ +static inline void *cir_buf_wrap(const void *ptr, const void *buf_addr, const void *buf_end) +{ + if (ptr >= buf_end) + ptr = (const char *)buf_addr + + ((const char *)ptr - (const char *)buf_end); + + assert((intptr_t)ptr <= (intptr_t)buf_end); + + return (void *)ptr; +} + +/** + * @brief Calculates number of bytes to buffer wrap when reading a circular + * buffer backwards from current pointer towards the buffer start. + * @param ptr Read or write pointer of circular buffer. + * @param buf_start Start address of circular buffer. + * @return Number of bytes between the buffer start and the pointer. + */ +static inline int cir_buf_bytes_without_wrap_rewind(const void *ptr, const void *buf_start) +{ + assert((intptr_t)ptr >= (intptr_t)buf_start); + + return (intptr_t)ptr - (intptr_t)buf_start; +} + +/** + * @brief Verifies the pointer and performs rollover when reading a read-only circular + * buffer backwards past its start address. + * @param ptr Pointer that may have moved below the buffer start. + * @param buf_start Start address of the circular buffer. + * @param buf_end End address of the circular buffer. + * @return Pointer, wrapped to the end of the buffer if necessary. + */ +static inline const void *source_cir_buf_rewind_wrap(const void *ptr, const void *buf_start, + const void *buf_end) +{ + if (ptr < buf_start) + ptr = (const char *)buf_end - ((const char *)buf_start - (const char *)ptr); + + assert((intptr_t)ptr >= (intptr_t)buf_start); + + return ptr; +} + #endif /* __MODULE_AUDIO_AUDIO_STREAM_H__ */ diff --git a/src/include/module/audio/sink_api.h b/src/include/module/audio/sink_api.h index 527fd1dc11c6..14c14af0187b 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 * @@ -139,6 +145,17 @@ static inline size_t sink_get_free_size(struct sof_sink *sink) return sink->ops->get_free_size(sink); } +/** + * Retrieves number of free frames in sink aligned to the alignment constants + * set by sink_set_alignment_constants(). + * @return Number of aligned free frames. + */ +static inline size_t sink_get_free_frames_aligned(struct sof_sink *sink) +{ + return (sink_get_free_size(sink) >> sink->audio_stream_params->align_shift_idx) * + sink->audio_stream_params->align_frame_cnt; +} + static inline enum sof_ipc_frame sink_get_frm_fmt(struct sof_sink *sink) { return sink->audio_stream_params->frame_fmt; @@ -348,4 +365,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/audio/source_api.h b/src/include/module/audio/source_api.h index 556dead4a583..ea4af5442249 100644 --- a/src/include/module/audio/source_api.h +++ b/src/include/module/audio/source_api.h @@ -12,6 +12,8 @@ #include <stdbool.h> #include <stddef.h> +#include <sof/math/numbers.h> + #include "audio_stream.h" #include "format.h" @@ -140,6 +142,17 @@ static inline size_t source_get_data_available(struct sof_source *source) return source->ops->get_data_available(source); } +/** + * Retrieves number of available data frames aligned to the alignment constants + * set by source_set_alignment_constants(). + * @return Number of aligned available frames. + */ +static inline size_t source_get_aligned_frames_available(struct sof_source *source) +{ + return (source_get_data_available(source) >> source->audio_stream_params->align_shift_idx) * + source->audio_stream_params->align_frame_cnt; +} + static inline enum sof_ipc_frame source_get_frm_fmt(struct sof_source *source) { return source->audio_stream_params->frame_fmt; @@ -328,4 +341,34 @@ static inline enum sof_audio_buffer_state source_get_state(const struct sof_sour return source->audio_stream_params->state; } +static inline uint32_t source_align_frames_round_up(struct sof_source *source, uint32_t frames) +{ + uint16_t align = source->audio_stream_params->align_frame_cnt; + + if (align <= 1) + return frames; + + return ROUND_UP(frames, align); +} + +static inline uint32_t source_align_frames_round_down(struct sof_source *source, uint32_t frames) +{ + uint16_t align = source->audio_stream_params->align_frame_cnt; + + if (align <= 1) + return frames; + + return ROUND_DOWN(frames, align); +} + +static inline uint32_t source_align_frames_round_nearest(struct sof_source *source, uint32_t frames) +{ + uint16_t align = source->audio_stream_params->align_frame_cnt; + + if (!align) + return frames; + + return ROUND_DOWN(frames + (align >> 1), align); +} + #endif /* __MODULE_AUDIO_SOURCE_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/ipc4/base-config.h b/src/include/module/ipc4/base-config.h index e5a5b49effd5..82a6d552306b 100644 --- a/src/include/module/ipc4/base-config.h +++ b/src/include/module/ipc4/base-config.h @@ -115,14 +115,14 @@ enum ipc4_stream_type { }; struct ipc4_audio_format { - enum ipc4_sampling_frequency sampling_frequency; - enum ipc4_bit_depth depth; + uint32_t sampling_frequency; /* enum ipc4_sampling_frequency */ + uint32_t depth; /* enum ipc4_bit_depth */ uint32_t ch_map; - enum ipc4_channel_config ch_cfg; + uint32_t ch_cfg; /* enum ipc4_channel_config */ uint32_t interleaving_style; uint32_t channels_count : 8; uint32_t valid_bit_depth : 8; - enum ipc4_sample_type s_type : 8; + uint32_t s_type : 8; /* enum ipc4_sample_type */ uint32_t reserved : 8; } __attribute__((packed, aligned(4))); diff --git a/src/include/module/module/base.h b/src/include/module/module/base.h index ed116681afd2..5d41d2d8a6b4 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). @@ -151,8 +168,10 @@ struct processing_module { */ bool dp_startup_delay; +#if CONFIG_IPC_MAJOR_3 /* flag to indicate module does not pause */ bool no_pause; +#endif /* * flag to indicate that the sink buffer writeback should be skipped. It will be handled @@ -184,7 +203,15 @@ 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 */ + + /* total size of a fragmented runtime-params (get/set) transfer, kept + * per instance so concurrent transfers to different components do not + * corrupt each other's reassembly state. Appended here to avoid shifting + * the offsets of the fields above. + */ + uint32_t runtime_params_size; #endif /* SOF_MODULE_PRIVATE */ }; diff --git a/src/include/module/module/interface.h b/src/include/module/module/interface.h index 26c176aee9b1..8e31117f9469 100644 --- a/src/include/module/module/interface.h +++ b/src/include/module/module/interface.h @@ -239,13 +239,13 @@ struct module_interface { uint8_t *fragment, size_t fragment_size); /** - * (unused) Set processing mode for the module + * (IADK) Set processing mode for the module */ int (*set_processing_mode)(struct processing_module *mod, enum module_processing_mode mode); /** - * (unused) Get the current processing mode for the module + * (IADK) Get the current processing mode for the module */ enum module_processing_mode (*get_processing_mode)(struct processing_module *mod); @@ -268,13 +268,13 @@ struct module_interface { * (optional) Module specific bind procedure, called when modules are bound with each other. * Usually can be __cold */ - int (*bind)(struct processing_module *mod, struct bind_info *bind_data); + int (*bind)(struct processing_module *mod, const struct bind_info *bind_data); /** * (optional) Module specific unbind procedure, called when modules are disconnected from * one another. Usually can be __cold */ - int (*unbind)(struct processing_module *mod, struct bind_info *unbind_data); + int (*unbind)(struct processing_module *mod, const struct bind_info *unbind_data); /** * (optional) Module specific trigger procedure, called when modules are triggered. Usually diff --git a/src/include/module/module/llext.h b/src/include/module/module/llext.h index 2f05cb6c692a..2ef0d47d77cc 100644 --- a/src/include/module/module/llext.h +++ b/src/include/module/module/llext.h @@ -6,7 +6,7 @@ #ifndef MODULE_LLEXT_H #define MODULE_LLEXT_H -#define SOF_LLEXT_MODULE_MANIFEST(manifest_name, entry, affinity, mod_uuid, instances) \ +#define SOF_LLEXT_MODULE_MANIFEST(manifest_name, entry, affinity, mod_uuid, instances, ...) \ { \ .module = { \ .name = manifest_name, \ @@ -16,6 +16,8 @@ .type = { \ .load_type = SOF_MAN_MOD_TYPE_LLEXT, \ .domain_ll = 1, \ + .user_mode = COND_CODE_0(NUM_VA_ARGS_LESS_1(_, ##__VA_ARGS__), (0), \ + (GET_ARG_N(1, __VA_ARGS__))), \ }, \ .affinity_mask = (affinity), \ } \ 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..c614284b9358 100644 --- a/src/include/sof/audio/audio_stream.h +++ b/src/include/sof/audio/audio_stream.h @@ -66,6 +66,15 @@ struct audio_stream { struct sof_audio_stream_params runtime_stream_params; }; +/* A pointer to data in a ring buffer. Just for convenience to reduce the number of typical + * processing function parameters: e.g., just 3 parameters (in, out ptr, and size) instead of 7. + */ +struct cir_buf_ptr { + void *buf_start; + void *buf_end; + void *ptr; +}; + void audio_stream_recalc_align(struct audio_stream *stream); static inline void *audio_stream_get_rptr(const struct audio_stream *buf) @@ -401,25 +410,6 @@ static inline void *audio_stream_wrap(const struct audio_stream *buffer, void *p return ptr; } -/** - * Verifies the pointer and performs rollover when reached the end of - * the circular buffer. - * @param ptr Pointer - * @param buf_addr Start address of the circular buffer. - * @param buf_end End address of the circular buffer. - * @return Pointer, adjusted if necessary. - */ -static inline void *cir_buf_wrap(void *ptr, void *buf_addr, void *buf_end) -{ - if (ptr >= buf_end) - ptr = (char *)buf_addr + - ((char *)ptr - (char *)buf_end); - - assert((intptr_t)ptr <= (intptr_t)buf_end); - - return ptr; -} - /** * Verifies the pointer and performs rollover when reached the end of * the buffer. @@ -591,9 +581,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 +638,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); } /** @@ -869,28 +902,12 @@ audio_stream_samples_without_wrap_s32(const struct audio_stream *source, const v * @return Number of bytes to buffer wrap. For number of samples calculate * need to add size of sample to returned bytes count. */ -static inline int cir_buf_bytes_without_wrap(void *ptr, void *buf_end) +static inline int cir_buf_bytes_without_wrap(const void *ptr, const void *buf_end) { assert((intptr_t)buf_end >= (intptr_t)ptr); return (intptr_t)buf_end - (intptr_t)ptr; } -/** - * @brief Calculates numbers of s32 samples to buffer wrap when reading stream - * backwards from current sample pointed by ptr towards begin. - * @param ptr Read or write pointer og circular buffer. - * @param buf_end End address of circular buffer. - * @return Number of bytes to buffer wrap. For number of samples calculate - * need to add size of sample to returned bytes count. - */ -static inline int cir_buf_samples_without_wrap_s32(void *ptr, void *buf_end) -{ - int to_end = (int32_t *)buf_end - (int32_t *)ptr; - - assert((intptr_t)buf_end >= (intptr_t)ptr); - return to_end; -} - /** * @brief Calculates numbers of frames to buffer wrap and return * minimum of calculated value. @@ -929,7 +946,7 @@ int audio_stream_copy(const struct audio_stream *source, uint32_t ioffset, * @param dst_end End address of sink circular buffer. * @param byte_size Number of bytes to copy. */ -void cir_buf_copy(void *src, void *src_addr, void *src_end, void *dst, +void cir_buf_copy(const void *src, const void *src_addr, const void *src_end, void *dst, void *dst_addr, void *dst_end, size_t byte_size); /** diff --git a/src/include/sof/audio/buffer.h b/src/include/sof/audio/buffer.h index 768db978bb02..517f229e5420 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, @@ -280,8 +289,11 @@ static inline void buffer_stream_writeback(struct comp_buffer *buffer, uint32_t * really be the head of the list, not a list head within another buffer. We * don't synchronise its cache, so it must not be embedded in an object, using * the coherent API. The caller takes care to protect list heads. + * + * Returns -EINVAL if the buffer is already linked in this direction + * (re-attaching would create a self-loop and corrupt the list). */ -void buffer_attach(struct comp_buffer *buffer, struct list_item *head, int dir); +int buffer_attach(struct comp_buffer *buffer, struct list_item *head, int dir); /* * Detach a buffer from anywhere in the list. "head" is again the head of the 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..672603e430c7 100644 --- a/src/include/sof/audio/component.h +++ b/src/include/sof/audio/component.h @@ -21,8 +21,10 @@ #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/lib/uuid.h> #include <sof/schedule/schedule.h> #include <ipc/control.h> #include <sof/ipc/topology.h> @@ -281,7 +283,7 @@ enum bind_type { struct bind_info { /* pointer to IPC4 bind/unbind data */ - struct ipc4_module_bind_unbind *ipc4_data; + const struct ipc4_module_bind_unbind *ipc4_data; /* type of binding * bind call will be called twice for every component, first when binding a data source, @@ -514,7 +516,7 @@ struct comp_ops { * * Usually can be __cold. */ - int (*bind)(struct comp_dev *dev, struct bind_info *bind_data); + int (*bind)(struct comp_dev *dev, const struct bind_info *bind_data); /** * Unbind, atomic - used to notify component of unbind event. @@ -523,7 +525,7 @@ struct comp_ops { * * Usually can be __cold. */ - int (*unbind)(struct comp_dev *dev, struct bind_info *unbind_data); + int (*unbind)(struct comp_dev *dev, const struct bind_info *unbind_data); /** * Gets config in component. @@ -591,7 +593,8 @@ 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 */ + struct sof_uuid uid_cp; /**< UUID copy for LLEXT modules */ }; /** \brief Holds constant pointer to component driver */ @@ -849,6 +852,26 @@ 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); +#ifndef __ZEPHYR__ + memcpy_s(&dev->tctx, sizeof(dev->tctx), + trace_comp_drv_get_tr_ctx(dev->drv), sizeof(struct tr_ctx)); +#endif +} + /** * Allocates memory for the component device and initializes common part. * @param drv Parent component driver. @@ -857,27 +880,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 +951,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); @@ -935,12 +968,15 @@ void sys_comp_module_mixout_interface_init(void); void sys_comp_module_multiband_drc_interface_init(void); void sys_comp_module_mux_interface_init(void); void sys_comp_module_nxp_eap_interface_init(void); +void sys_comp_module_phase_vocoder_interface_init(void); void sys_comp_module_rtnr_interface_init(void); 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 +1236,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..63cff9759425 100644 --- a/src/include/sof/audio/component_ext.h +++ b/src/include/sof/audio/component_ext.h @@ -26,7 +26,6 @@ /** \brief Holds list of registered components' drivers */ struct comp_driver_list { struct list_item list; /**< list of component drivers */ - struct k_spinlock lock; /**< list lock */ }; /** \brief Retrieves the component device buffer list. */ @@ -38,23 +37,21 @@ struct comp_driver_list { #if CONFIG_IPC_MAJOR_3 struct comp_dev *comp_new(struct sof_ipc_comp *comp); #elif CONFIG_IPC_MAJOR_4 -struct comp_dev *comp_new_ipc4(struct ipc4_module_init_instance *module_init); +struct comp_dev *comp_new_ipc4(const struct ipc4_module_init_instance *module_init); #endif /** 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 +149,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; } } @@ -426,13 +424,21 @@ static inline void comp_make_shared(struct comp_dev *dev) dev->is_shared = true; } +/** + * Get the global component driver list. + * @return Pointer to the component driver list. + */ +#ifdef CONFIG_SOF_USERSPACE_LL +struct comp_driver_list *comp_drivers_get(void); +#else static inline struct comp_driver_list *comp_drivers_get(void) { return sof_get()->comp_drivers; } +#endif #if CONFIG_IPC_MAJOR_4 -static inline int comp_ipc4_bind_remote(struct comp_dev *dev, struct bind_info *bind_data) +static inline int comp_ipc4_bind_remote(struct comp_dev *dev, const struct bind_info *bind_data) { struct idc_msg msg = { IDC_MSG_BIND, IDC_EXTENSION(dev->ipc_config.id), dev->ipc_config.core, @@ -442,7 +448,7 @@ static inline int comp_ipc4_bind_remote(struct comp_dev *dev, struct bind_info * } #endif -static inline int comp_bind(struct comp_dev *dev, struct bind_info *bind_data) +static inline int comp_bind(struct comp_dev *dev, const struct bind_info *bind_data) { #if CONFIG_IPC_MAJOR_4 if (dev->drv->ops.bind) @@ -461,7 +467,7 @@ static inline int comp_bind(struct comp_dev *dev, struct bind_info *bind_data) } #if CONFIG_IPC_MAJOR_4 -static inline int comp_ipc4_unbind_remote(struct comp_dev *dev, struct bind_info *unbind_data) +static inline int comp_ipc4_unbind_remote(struct comp_dev *dev, const struct bind_info *unbind_data) { struct idc_msg msg = { IDC_MSG_UNBIND, IDC_EXTENSION(dev->ipc_config.id), dev->ipc_config.core, @@ -471,7 +477,7 @@ static inline int comp_ipc4_unbind_remote(struct comp_dev *dev, struct bind_info } #endif -static inline int comp_unbind(struct comp_dev *dev, struct bind_info *unbind_data) +static inline int comp_unbind(struct comp_dev *dev, const struct bind_info *unbind_data) { #if CONFIG_IPC_MAJOR_4 if (dev->drv->ops.unbind) diff --git a/src/include/sof/audio/compress_other/pcm_dec/xa_pcm_dec_api.h b/src/include/sof/audio/compress_other/pcm_dec/xa_pcm_dec_api.h new file mode 100644 index 000000000000..e79e1e85a200 --- /dev/null +++ b/src/include/sof/audio/compress_other/pcm_dec/xa_pcm_dec_api.h @@ -0,0 +1,56 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * + * Copyright(c) 2026 Intel Corporation. + * + */ + +#ifndef __XA_PCM_DEC_API_H__ +#define __XA_PCM_DEC_API_H__ + +#include <stdint.h> + +/*****************************************************************************/ +/* PCM Decoder specific API definitions */ +/*****************************************************************************/ + +/* pcm_dec-specific configuration parameters */ +enum xa_config_param_pcm_dec { + XA_PCM_DEC_CONFIG_PARAM_CHANNELS = 0, + XA_PCM_DEC_CONFIG_PARAM_SAMPLE_RATE = 1, + XA_PCM_DEC_CONFIG_PARAM_PCM_WIDTH = 2, + XA_PCM_DEC_CONFIG_PARAM_PRODUCED = 3, + XA_PCM_DEC_CONFIG_PARAM_INTERLEAVE = 5 +}; + +/* commands */ +#include <sof/audio/cadence/xa_apicmd_standards.h> + +/* error codes */ +#include <sof/audio/cadence/xa_error_standards.h> + +#define XA_CODEC_PCM_DEC 15 + +/* pcm_dec-specific error codes */ +/*****************************************************************************/ +/* Class 1: Configuration Errors */ +/*****************************************************************************/ +/* Nonfatal Errors */ +enum xa_error_nonfatal_config_pcm_dec { + XA_PCMDEC_CONFIG_NONFATAL_INVALID_PCM_WIDTH = + XA_ERROR_CODE(xa_severity_nonfatal, xa_class_config, XA_CODEC_PCM_DEC, 2) +}; + +/*****************************************************************************/ +/* Class 2: Execution Errors */ +/*****************************************************************************/ +/* Nonfatal Errors */ +enum xa_error_nonfatal_execute_pcm_dec { + XA_PCMDEC_EXECUTE_NONFATAL_INSUFFICIENT_DATA = + XA_ERROR_CODE(xa_severity_nonfatal, xa_class_execute, XA_CODEC_PCM_DEC, 0) +}; + +/* Fatal Errors */ +#define XA_PCMDEC_EXECUTE_FATAL_UNINITIALIZED \ + XA_ERROR_CODE((uint32_t)xa_severity_fatal, xa_class_execute, XA_CODEC_PCM_DEC, 0) + +#endif /* __XA_PCM_DEC_API_H__ */ 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/intel_uaol.h b/src/include/sof/audio/intel_uaol.h new file mode 100644 index 000000000000..e8964d2d7450 --- /dev/null +++ b/src/include/sof/audio/intel_uaol.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * + * Copyright 2026 Intel Corporation. All rights reserved. + */ + +#ifndef __SOF_AUDIO_INTEL_UAOL_H__ +#define __SOF_AUDIO_INTEL_UAOL_H__ + +#include <stdint.h> +#include <sof/lib/memory.h> + +struct sof_tlv; + +#if !CONFIG_SOF_OS_LINUX_COMPAT_PRIORITY +__cold void tlv_value_set_uaol_caps(struct sof_tlv *tuple, uint32_t type); +#endif /* !CONFIG_SOF_OS_LINUX_COMPAT_PRIORITY */ + +__cold int uaol_stream_id_to_hda_link_stream_id(int uaol_stream_id); + +#endif /* __SOF_AUDIO_INTEL_UAOL_H__ */ 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..885339004fc0 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> */ @@ -9,16 +9,19 @@ #define __SOF_AUDIO_MFCC_MFCC_COMP_H__ #include <sof/audio/module_adapter/module/generic.h> +#include <sof/audio/data_blob.h> #include <sof/math/auditory.h> #include <sof/math/dct.h> #include <sof/math/fft.h> +#include <sof/audio/mfcc/mfcc_vad.h> +#include <sof/ipc/msg.h> #include <stddef.h> #include <stdint.h> /* __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,36 +33,25 @@ #endif #define MFCC_MAGIC 0x6d666363 /* ASCII for "mfcc" */ +#define MFCC_FFT_BITS 32 +#define MFCC_MAX_SAMPLE_RATE 64000 /* Max sample rate in Hz, limited by int16_t Mel scale */ -/* 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 +/** \brief Switch control index for VAD notification to user space */ +#define MFCC_CTRL_INDEX_VAD 0 -/* 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. +/** + * \brief Data header prepended to every MFCC output frame. + * + * Written before the Mel spectrum or cepstral coefficient data in each + * output frame. */ -#if MFCC_FFT_BITS == 16 -#define MFCC_NORMALIZE_FFT -#else -#undef MFCC_NORMALIZE_FFT -#endif -#define MFCC_NORMALIZE_MAX_SHIFT 10 - -/** \brief Type definition for processing function select return value. */ -typedef void (*mfcc_func)(struct processing_module *mod, - struct input_stream_buffer *bsource, - struct output_stream_buffer *bsink, - int frames); - -/** \brief MFCC processing functions map item. */ -struct mfcc_func_map { - uint8_t source; /**< source frame format */ - mfcc_func func; /**< processing function */ +struct mfcc_data_header { + uint32_t magic; /**< Magic word MFCC_MAGIC (0x6d666363) */ + uint32_t frame_number; /**< Frame number, counting calculated frames starting from 0 */ + int32_t reserved; /**< Reserved for future use, set to 0 */ + int32_t energy; /**< Weighted signal energy in Q9.23 */ + int32_t noise_energy; /**< Weighted noise floor energy in Q9.23 */ + int32_t vad_flag; /**< VAD decision: 1 = speech, 0 = silence */ }; struct mfcc_buffer { @@ -78,16 +70,13 @@ struct mfcc_pre_emph { int enable; }; +/** \brief Type definition for source/sink based input copy function. */ +typedef void (*mfcc_source_func)(struct sof_source *source, struct mfcc_buffer *buf, + struct mfcc_pre_emph *emph, int frames, int source_channel); + 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 +103,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,18 +116,34 @@ 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 header_pending; /**< True when data header not yet written for current output */ + struct mfcc_data_header header; /**< Data header for current output frame */ size_t sample_buffers_size; /**< bytes */ + int32_t *out_data_ptr; /**< Read pointer into staging data for multi-period output */ + int out_remain; /**< Remaining int32_t samples to write to sink from staging */ + int32_t *out_stage; /**< Dedicated staging buffer for pending output, decoupled from STFT scratch */ + int out_stage_size; /**< Capacity of out_stage in int32_t samples */ + uint32_t hop_count; /**< FFT hop counter, increments every processed hop */ + int vad_silence_count; /**< Consecutive VAD=0 hops since last speech */ + int16_t dtx_trailing_silence; /**< Number of trailing silence hops to send, from config */ + int16_t dtx_silence_interval; /**< Send silence frame every Nth hop, 0 = disable */ + int dtx_silence_counter; /**< Counter for periodic silence frame send */ }; /* MFCC component private data */ struct mfcc_comp_data { struct mfcc_state state; + struct mfcc_vad_state vad; struct comp_data_blob_handler *model_handler; struct sof_mfcc_config *config; + struct ipc_msg *msg; /**< IPC notification for VAD switch control */ int max_frames; - mfcc_func mfcc_func; /**< processing function */ + enum sof_ipc_frame source_format; /**< Source audio format for output sizing */ + bool vad_prev; /**< Previous VAD state for edge detection */ + mfcc_source_func source_func; /**< source copy function */ }; static inline int mfcc_buffer_samples_without_wrap(struct mfcc_buffer *buffer, int16_t *ptr) @@ -154,35 +161,92 @@ 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); +/** + * \brief Run STFT and Mel/DCT processing. + * \return Number of output coefficients produced, or 0 if not enough data. + */ +int mfcc_stft_process(struct processing_module *mod, struct mfcc_comp_data *cd); + +/** + * \brief Prepare and commit MFCC output data after STFT processing. + * + * This handles the output data conversion and dispatches to either the + * compress-output or legacy PCM-output path. + * + * \return 0 on success or a negative error code. + */ +int mfcc_process_output(struct processing_module *mod, struct mfcc_comp_data *cd, + struct sof_source **sources, struct sof_sink **sinks, + int num_ceps, int frames); + #if CONFIG_FORMAT_S16LE +void mfcc_source_copy_s16(struct sof_source *source, struct mfcc_buffer *buf, + struct mfcc_pre_emph *emph, int frames, int source_channel); +#endif + +#if CONFIG_FORMAT_S24LE +void mfcc_source_copy_s24(struct sof_source *source, struct mfcc_buffer *buf, + struct mfcc_pre_emph *emph, int frames, int source_channel); +#endif + +#if CONFIG_FORMAT_S32LE +void mfcc_source_copy_s32(struct sof_source *source, struct mfcc_buffer *buf, + struct mfcc_pre_emph *emph, int frames, int source_channel); +#endif + +#if CONFIG_IPC_MAJOR_4 +int mfcc_ipc_notification_init(struct processing_module *mod); -int16_t *mfcc_sink_copy_zero_s16(const struct audio_stream *sink, - int16_t *w_ptr, int samples); +void mfcc_send_vad_notification(struct processing_module *mod, uint32_t val); -int16_t *mfcc_sink_copy_data_s16(const struct audio_stream *sink, int16_t *w_ptr, - int samples, int16_t *r_ptr); +int mfcc_get_config(struct processing_module *mod, + uint32_t config_id, uint32_t *data_offset_size, + uint8_t *fragment, size_t fragment_size); + +int mfcc_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); + +#else +static inline int mfcc_ipc_notification_init(struct processing_module *mod) +{ + return 0; +} -void mfcc_s16_default(struct processing_module *mod, struct input_stream_buffer *bsource, - struct output_stream_buffer *bsink, int frames); +static inline void mfcc_send_vad_notification(struct processing_module *mod, uint32_t val) +{ +} + +static inline int mfcc_get_config(struct processing_module *mod, + uint32_t config_id, uint32_t *data_offset_size, + uint8_t *fragment, size_t fragment_size) +{ + struct sof_ipc_ctrl_data *cdata = (struct sof_ipc_ctrl_data *)fragment; + struct mfcc_comp_data *cd = module_get_private_data(mod); + + return comp_data_blob_get_cmd(cd->model_handler, cdata, fragment_size); +} + +static inline int mfcc_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) +{ + struct mfcc_comp_data *cd = module_get_private_data(mod); + + return comp_data_blob_set(cd->model_handler, pos, data_offset_size, + fragment, fragment_size); +} #endif #ifdef UNIT_TEST diff --git a/src/include/sof/audio/mfcc/mfcc_vad.h b/src/include/sof/audio/mfcc/mfcc_vad.h new file mode 100644 index 000000000000..6873343d334e --- /dev/null +++ b/src/include/sof/audio/mfcc/mfcc_vad.h @@ -0,0 +1,92 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * + * Copyright(c) 2026 Intel Corporation. + * + * Author: Seppo Ingalsuo <seppo.ingalsuo@linux.intel.com> + */ + +/** + * \file mfcc_vad.h + * \brief Voice Activity Detection based on Mel spectrum energy. + * + * This VAD operates on the Q9.23 Mel log spectrum values produced by + * the MFCC component. It tracks a per-bin noise floor that follows + * the signal downward instantly and rises slowly, then computes a + * speech-weighted energy delta above the floor. + */ + +#ifndef __SOF_AUDIO_MFCC_MFCC_VAD_H__ +#define __SOF_AUDIO_MFCC_MFCC_VAD_H__ + +#include <stdint.h> +#include <stdbool.h> + +struct processing_module; + +/** + * \brief Number of frames for fast noise floor convergence at startup (~1 s at 10 ms/frame). + */ +#define MFCC_VAD_NOISE_INIT_FRAMES 100 + +/** + * \brief Slow noise floor rise coefficient in Q1.15 (0.003 * 2^15). + */ +#define MFCC_VAD_NOISE_RISE_ALPHA 98 + +/** + * \brief Fast noise floor rise coefficient in Q1.15 (0.020 * 2^15). + */ +#define MFCC_VAD_NOISE_RISE_ALPHA_FAST 655 + +/** + * \brief Energy threshold for speech detection in Q9.23 (0.30 * 2^23). + */ +#define MFCC_VAD_ENERGY_THRESHOLD 2516582 + +/** + * \brief Hangover frame count to keep VAD active after last speech detection. + */ +#define MFCC_VAD_HANGOVER_FRAMES 20 + +/** + * \brief VAD state structure. + */ +struct mfcc_vad_state { + int32_t *noise_floor; /**< Per-bin noise floor in Q9.23 */ + int16_t *weights; /**< Speech-frequency emphasis weights Q1.15 */ + int32_t energy; /**< Weighted signal energy in Q9.23 */ + int32_t energy_threshold; /**< Energy threshold Q9.23 */ + int32_t noise_energy; /**< Weighted noise floor energy in Q9.23 */ + int16_t frame_count; /**< Initial convergence frames processed */ + int16_t hangover_counter; /**< Current hangover counter */ + int16_t hangover_max; /**< Maximum hangover frames */ + int16_t init_frames; /**< Number of initial frames for fast convergence */ + int16_t noise_rise_alpha_fast; /**< Fast rise alpha Q1.15 */ + int16_t noise_rise_alpha_slow; /**< Slow rise alpha Q1.15 */ + int16_t num_mel_bins; /**< Number of Mel bins in use */ + bool initialized; /**< True after first frame processed */ + bool is_speech; /**< Current VAD decision */ +}; + +/** + * \brief Initialize VAD state. + * + * \param[out] vad Pointer to VAD state to initialize. + * \param[in] num_mel_bins Number of Mel bins. + * \param[in] sample_rate Audio sample rate in Hz. + * \param[in] mod Processing module for memory allocation. + * \return 0 on success, negative error code on failure. + */ +int mfcc_vad_init(struct mfcc_vad_state *vad, int num_mel_bins, int32_t sample_rate, + struct processing_module *mod); + +/** + * \brief Process one Mel spectrum frame and update VAD decision. + * + * \param[in,out] vad Pointer to VAD state. + * \param[in] mel_log Mel log spectrum in Q9.23, array of num_mel_bins values. + * \return 1 if speech detected, 0 if silence. + */ +int mfcc_vad_update(struct mfcc_vad_state *vad, const int32_t *mel_log); + +#endif /* __SOF_AUDIO_MFCC_MFCC_VAD_H__ */ 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..6585db5eb533 --- /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; + const 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..ccca668bf76a 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 */ @@ -31,6 +32,9 @@ extern xa_codec_func_t xa_mp3_enc; extern xa_codec_func_t xa_sbc_dec; extern xa_codec_func_t xa_vorbis_dec; extern xa_codec_func_t xa_src_pp; +extern xa_codec_func_t xa_pcm_dec; + +#define DEFAULT_CODEC_ID CADENCE_CODEC_WRAPPER_ID #define API_CALL(cd, cmd, sub_cmd, value, ret) \ do { \ @@ -48,15 +52,43 @@ struct cadence_api { xa_codec_func_t *api; }; +struct ipc_msg; + struct cadence_codec_data { +#if CONFIG_IPC_MAJOR_4 + struct ipc4_base_module_cfg base_cfg; + uint32_t direction; + struct ipc_msg *msg; +#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, + /* + * IDs >= 0x80 are reserved for in-tree open-source codecs that + * plug into the Cadence wrapper but are not Cadence proprietary + * codecs. + */ + SOF_COMPRESS_CODEC_PCM_DEC_ID = 0xC0, +}; + #if CONFIG_IPC_MAJOR_4 struct ipc4_cadence_module_cfg { struct ipc4_base_module_cfg base_cfg; @@ -65,4 +97,30 @@ 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); +unsigned 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); +void cadence_copy_data_from_buffer(void *dest, const void *buffer_ptr, size_t bytes_to_copy, + size_t buffer_size, void const *buffer_start); +void cadence_copy_data_to_buffer(void *buffer_ptr, size_t bytes_to_copy, + size_t buffer_size, void *buffer_start, + const void *src); + #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..625b3a180a60 100644 --- a/src/include/sof/audio/module_adapter/module/generic.h +++ b/src/include/sof/audio/module_adapter/module/generic.h @@ -13,16 +13,15 @@ #ifndef __SOF_AUDIO_MODULE_GENERIC__ #define __SOF_AUDIO_MODULE_GENERIC__ -#include <sof/audio/component.h> +#include <rtos/mutex.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" -/* The __ZEPHYR__ condition is to keep cmocka tests working */ -#if CONFIG_MODULE_MEMORY_API_DEBUG && defined(__ZEPHYR__) -#include <zephyr/kernel/thread.h> -#endif +#include <sof/compiler_attributes.h> /* * helpers to determine processing type @@ -79,14 +78,13 @@ static const struct comp_driver comp_##adapter##_module = { \ .adapter_ops = &(adapter), \ }; \ \ -static SHARED_DATA struct comp_driver_info comp_module_##adapter##_info = { \ +static struct comp_driver_info comp_module_##adapter##_info = { \ .drv = &comp_##adapter##_module, \ }; \ \ UT_STATIC void sys_comp_module_##adapter##_init(void) \ { \ - comp_register(platform_shared_get(&comp_module_##adapter##_info, \ - sizeof(comp_module_##adapter##_info))); \ + comp_register(&comp_module_##adapter##_info); \ } \ \ DECLARE_MODULE(sys_comp_module_##adapter##_init) @@ -127,14 +125,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; -#if CONFIG_MODULE_MEMORY_API_DEBUG && defined(__ZEPHYR__) - k_tid_t rsrc_mngr; -#endif + struct mod_alloc_ctx *alloc; }; enum mod_resource_type { @@ -170,6 +164,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,19 +184,83 @@ 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); +#if defined(__ZEPHYR__) && defined(CONFIG_SOF_FULL_ZEPHYR_APPLICATION) +__syscall void *mod_balloc_align(struct processing_module *mod, size_t size, size_t alignment); +#else +void *z_impl_mod_balloc_align(struct processing_module *mod, size_t size, size_t alignment); +#define mod_balloc_align z_impl_mod_balloc_align +#endif +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); +__syscall void mod_free_all(struct processing_module *mod); +#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); +void z_impl_mod_free_all(struct processing_module *mod); +#define mod_alloc_ext z_impl_mod_alloc_ext +#define mod_free z_impl_mod_free +#define mod_free_all z_impl_mod_free_all +#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); +#if defined(__ZEPHYR__) && defined(CONFIG_SOF_FULL_ZEPHYR_APPLICATION) +__syscall struct comp_data_blob_handler *mod_data_blob_handler_new(struct processing_module *mod); +#else +struct comp_data_blob_handler *z_impl_mod_data_blob_handler_new(struct processing_module *mod); +#define mod_data_blob_handler_new z_impl_mod_data_blob_handler_new +#endif 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); int module_prepare(struct processing_module *mod, struct sof_source **sources, int num_of_sources, struct sof_sink **sinks, int num_of_sinks); @@ -261,14 +321,16 @@ int module_set_configuration(struct processing_module *mod, enum module_cfg_fragment_position pos, size_t data_offset_size, const uint8_t *fragment, size_t fragment_size, uint8_t *response, size_t response_size); -int module_bind(struct processing_module *mod, struct bind_info *bind_data); -int module_unbind(struct processing_module *mod, struct bind_info *unbind_data); +int module_bind(struct processing_module *mod, const struct bind_info *bind_data); +int module_unbind(struct processing_module *mod, const 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, + const struct module_interface *ops); 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); @@ -307,13 +369,13 @@ int module_get_large_config(struct comp_dev *dev, uint32_t param_id, bool first_ } static inline -int module_adapter_bind(struct comp_dev *dev, struct bind_info *bind_data) +int module_adapter_bind(struct comp_dev *dev, const struct bind_info *bind_data) { return 0; } static inline -int module_adapter_unbind(struct comp_dev *dev, struct bind_info *unbind_data) +int module_adapter_unbind(struct comp_dev *dev, const struct bind_info *unbind_data) { return 0; } @@ -342,8 +404,8 @@ int module_get_large_config(struct comp_dev *dev, uint32_t param_id, bool first_ bool last_block, uint32_t *data_offset, char *data); int module_adapter_get_attribute(struct comp_dev *dev, uint32_t type, void *value); int module_adapter_set_attribute(struct comp_dev *dev, uint32_t type, void *value); -int module_adapter_bind(struct comp_dev *dev, struct bind_info *bind_data); -int module_adapter_unbind(struct comp_dev *dev, struct bind_info *unbind_data); +int module_adapter_bind(struct comp_dev *dev, const struct bind_info *bind_data); +int module_adapter_unbind(struct comp_dev *dev, const struct bind_info *unbind_data); uint64_t module_adapter_get_total_data_processed(struct comp_dev *dev, uint32_t stream_no, bool input); @@ -376,7 +438,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 +461,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/pcm_converter.h b/src/include/sof/audio/pcm_converter.h index 3563a0d6557e..1b165c4b926d 100644 --- a/src/include/sof/audio/pcm_converter.h +++ b/src/include/sof/audio/pcm_converter.h @@ -23,6 +23,8 @@ #include <stdint.h> struct audio_stream; +struct cir_buf_source; +struct cir_buf_sink; #if __XCC__ #include <xtensa/config/core-isa.h> @@ -38,17 +40,17 @@ struct audio_stream; /** * \brief PCM conversion function interface for data in circular buffer - * \param source buffer with samples to process, read pointer is not modified - * \param ioffset offset to first sample in source stream - * \param sink output buffer, write pointer is not modified - * \param ooffset offset to first sample in sink stream + * \param source circular-buffer source descriptor, read pointer is not modified + * \param src_channels number of channels in the source stream + * \param sink circular-buffer sink descriptor, write pointer is not modified + * \param sink_channels number of channels in the sink stream * \param source_samples number of samples to convert, for remapping -- number of source samples * \param chmap channel map for remapping, ignored by non-remapping conversion func * \return error code or number of processed samples (source samples in case of remapping). */ -typedef int (*pcm_converter_func)(const struct audio_stream *source, - uint32_t ioffset, struct audio_stream *sink, - uint32_t ooffset, uint32_t source_samples, uint32_t chmap); +typedef int (*pcm_converter_func)(const struct cir_buf_source *source, + uint32_t src_channels, struct cir_buf_sink *sink, + uint32_t sink_channels, size_t source_samples, uint32_t chmap); /* A channel map that does not perform any remapping. */ #define DUMMY_CHMAP 0x76543210 @@ -60,7 +62,7 @@ typedef int (*pcm_converter_func)(const struct audio_stream *source, * \param samples number of samples to convert */ typedef void (*pcm_converter_lin_func)(const void *psrc, void *pdst, - uint32_t samples); + size_t samples); /** \brief PCM conversion functions map. */ struct pcm_func_map { @@ -184,20 +186,30 @@ pcm_get_conversion_vc_function(enum sof_ipc_frame in_bits, /** * \brief Convert data from circular buffer using converter working on linear * memory space - * \param source buffer with samples to process, read pointer is not modified - * \param ioffset offset to first sample in source stream - * \param sink output buffer, write pointer is not modified - * \param ooffset offset to first sample in sink stream + * \param source circular-buffer source descriptor, read pointer is not modified + * \param s_size_in source sample size in bytes + * \param sink circular-buffer sink descriptor, write pointer is not modified + * \param s_size_out sink sample size in bytes * \param samples number of samples to convert * \param converter core conversion function working on linear memory regions * \return error code or number of processed samples */ -int pcm_convert_as_linear(const struct audio_stream *source, uint32_t ioffset, - struct audio_stream *sink, uint32_t ooffset, - uint32_t samples, pcm_converter_lin_func converter); - -/* Copy stream without conversion. chmap parameter is ignored. */ -int just_copy(const struct audio_stream *source, uint32_t ioffset, - struct audio_stream *sink, uint32_t ooffset, uint32_t samples, uint32_t chmap); +int pcm_convert_as_linear(const struct cir_buf_source *source, size_t s_size_in, + struct cir_buf_sink *sink, size_t s_size_out, + size_t samples, pcm_converter_lin_func converter); + +/* Copy stream without conversion, typed by sample size in bytes. chmap is ignored. */ +int just_copy_1b(const struct cir_buf_source *source, uint32_t src_channels, + struct cir_buf_sink *sink, uint32_t sink_channels, + size_t samples, uint32_t chmap); +int just_copy_2b(const struct cir_buf_source *source, uint32_t src_channels, + struct cir_buf_sink *sink, uint32_t sink_channels, + size_t samples, uint32_t chmap); +int just_copy_3b(const struct cir_buf_source *source, uint32_t src_channels, + struct cir_buf_sink *sink, uint32_t sink_channels, + size_t samples, uint32_t chmap); +int just_copy_4b(const struct cir_buf_source *source, uint32_t src_channels, + struct cir_buf_sink *sink, uint32_t sink_channels, + size_t samples, uint32_t chmap); #endif /* __SOF_AUDIO_PCM_CONVERTER_H__ */ diff --git a/src/include/sof/audio/pipeline-trace.h b/src/include/sof/audio/pipeline-trace.h index d1f89583ad45..dbd5ae7f7c84 100644 --- a/src/include/sof/audio/pipeline-trace.h +++ b/src/include/sof/audio/pipeline-trace.h @@ -59,6 +59,10 @@ extern struct tr_ctx pipe_tr; #else +#if defined(__ZEPHYR__) && defined(CONFIG_SOF_USERSPACE_LL) +#error "Invalid build config: User-space cannot access trace context." +#endif + #define pipe_err(pipe_p, __e, ...) \ trace_dev_err(trace_pipe_get_tr_ctx, trace_pipe_get_id, \ trace_pipe_get_subid, pipe_p, __e, ##__VA_ARGS__) diff --git a/src/include/sof/audio/pipeline.h b/src/include/sof/audio/pipeline.h index 5221d330e0f1..858c81d98a2f 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 */ @@ -71,7 +73,10 @@ struct pipeline { bool expect_eos; /* pipeline is expecting end of stream */ /* scheduling */ - struct task *pipe_task; /* pipeline processing task */ +#ifdef CONFIG_IPC_MAJOR_4 + struct task *trigger_task; /* IPC4 pipeline trigger task */ +#endif + struct task *pipe_task; /* IPC3 pipeline processing task or IPC4 pipeline copy task */ struct pipeline *sched_next; /* pipeline scheduled after this */ struct pipeline *sched_prev; /* pipeline scheduled before this */ @@ -134,6 +139,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 +157,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. @@ -194,6 +209,14 @@ int pipeline_complete(struct pipeline *p, struct comp_dev *source, */ void pipeline_posn_init(struct sof *sof); +#ifdef CONFIG_SOF_USERSPACE_LL +/** + * \brief Grants user-space thread access to pipeline position mutex. + * \param[in] thread Thread to grant access to. + */ +void pipeline_posn_grant_access(struct k_thread *thread); +#endif + /** * \brief Resets the pipeline and free runtime resources. * \param[in] p pipeline. @@ -257,7 +280,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. @@ -349,6 +372,12 @@ static inline bool pipeline_is_this_cpu(struct pipeline *p) */ int pipeline_comp_ll_task_init(struct pipeline *p); +/** + * \brief Free the LL tasks owned by a pipeline. + * \param[in] p pipeline. + */ +void pipeline_comp_ll_task_free(struct pipeline *p); + /** * \brief Init a DP task for a component * \param[in] comp a component the task is created for @@ -357,9 +386,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/rtnr/rtnr.h b/src/include/sof/audio/rtnr/rtnr.h index 19f9fb403e18..42b0ae7804d5 100644 --- a/src/include/sof/audio/rtnr/rtnr.h +++ b/src/include/sof/audio/rtnr/rtnr.h @@ -82,8 +82,4 @@ void rtnr_printf(int a, int b, int c, int d, int e); void *rtk_rballoc(unsigned int flags, unsigned int caps, unsigned int bytes); void rtk_rfree(void *ptr); -/* For stub processing functions */ -void rtnr_copy_from_sof_stream(struct audio_stream_rtnr *dst, struct audio_stream *src); -void rtnr_copy_to_sof_stream(struct audio_stream *dst, struct audio_stream_rtnr *src); - #endif /* __SOF_AUDIO_RTNR_RTNR_H__ */ diff --git a/src/include/sof/audio/selector.h b/src/include/sof/audio/selector.h index cd4aa926c7f4..fd413badab7f 100644 --- a/src/include/sof/audio/selector.h +++ b/src/include/sof/audio/selector.h @@ -27,8 +27,12 @@ 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_1CH 1 #define SEL_SOURCE_2CH 2 #define SEL_SOURCE_4CH 4 @@ -43,6 +47,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 +67,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 +118,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 +128,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/audio/sink_source_utils.h b/src/include/sof/audio/sink_source_utils.h index 818f29444c22..2d31666ac383 100644 --- a/src/include/sof/audio/sink_source_utils.h +++ b/src/include/sof/audio/sink_source_utils.h @@ -9,6 +9,7 @@ #include <sof/audio/sink_api.h> #include <sof/audio/source_api.h> +#include <sof/math/numbers.h> /** * copy bytes from source to sink @@ -38,4 +39,23 @@ int sink_fill_with_silence(struct sof_sink *sink, size_t size); */ int source_drop_data(struct sof_source *source, size_t size); +/** + * Computes maximum number of frames aligned with the source align criteria + * that can be copied from source to sink, verifying the number of available + * source frames vs. free space available in sink. + * + * @param source the data source + * @param sink the data sink + * @return Number of aligned frames available for processing. + */ +static inline size_t source_sink_avail_frames_aligned(struct sof_source *source, + struct sof_sink *sink) +{ + size_t src_frames = source_get_data_frames_available(source); + size_t sink_frames = sink_get_free_frames(sink); + size_t n = MIN(src_frames, sink_frames); + + return source_align_frames_round_down(source, n); +} + #endif /* SINK_SOURCE_UTILS_H */ diff --git a/src/include/sof/boot_test.h b/src/include/sof/boot_test.h index 965eec06a6a9..dfa8671ee55f 100644 --- a/src/include/sof/boot_test.h +++ b/src/include/sof/boot_test.h @@ -6,8 +6,16 @@ #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> +struct k_thread; + +#if CONFIG_SOF_BOOT_TEST #define TEST_RUN_ONCE(fn, ...) do { \ static bool once; \ if (!once) { \ @@ -15,6 +23,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 +36,20 @@ } \ } while (0) +void sof_run_boot_tests(void); + +/** + * Mark a boot-test thread as expected to trigger a fatal error. + * + * @param thread Thread that is allowed to fault once, or NULL to clear. + */ +#if CONFIG_SOF_BOOT_TEST +void sof_boot_test_set_fault_valid(struct k_thread *thread); +#else +static inline void sof_boot_test_set_fault_valid(struct k_thread *thread) +{ + (void)thread; +} +#endif + #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/common.h b/src/include/sof/ipc/common.h index e46fc10b9521..a910c6d42c92 100644 --- a/src/include/sof/ipc/common.h +++ b/src/include/sof/ipc/common.h @@ -18,12 +18,15 @@ #include <user/trace.h> #include <ipc/header.h> #include <ipc/stream.h> +#include <sof/ipc/ipc_reply.h> #include <stdbool.h> #include <stdint.h> +struct comp_driver; struct dma_sg_elem_array; struct ipc_msg; +struct ipc4_message_request; /* validates internal non tail structures within IPC command structure */ #define IPC_IS_SIZE_INVALID(object) \ @@ -53,6 +56,37 @@ extern struct tr_ctx ipc_tr; #define IPC_TASK_SECONDARY_CORE BIT(2) #define IPC_TASK_POWERDOWN BIT(3) +struct ipc_user { + struct k_thread *thread; + struct k_sem *sem; + struct k_event *event; + /** @brief Copy of IPC4 message primary word forwarded to user thread */ + uint32_t ipc_msg_pri; + /** @brief Copy of IPC4 message extension word forwarded to user thread */ + uint32_t ipc_msg_ext; + /** @brief Result code from user thread processing */ + int result; + /** @brief Reply extension word from user thread (e.g. CONFIG_GET result) */ + uint32_t reply_ext; + /** @brief Reply TX data size from user thread (e.g. LARGE_CONFIG_GET result) */ + uint32_t reply_tx_size; + /** @brief Reply TX data pointer from user thread (e.g. LARGE_CONFIG_GET result) */ + void *reply_tx_data; + struct ipc *ipc; + struct k_thread *audio_thread; + /** @brief Original kernel driver pointer for restoring dev->drv after create */ + const struct comp_driver *init_drv; + /** + * @brief User-accessible copy of comp_driver + tr_ctx for create(). + * + * The comp_driver and tr_ctx structs reside in kernel memory + * (.rodata/.data) which is not user-readable. The kernel handler + * copies them here before forwarding to the user thread. + * Size verified by BUILD_ASSERT in handler-user.c. + */ + uint8_t init_drv_data[160] __aligned(4); +}; + struct ipc { struct k_spinlock lock; /* locking mechanism */ void *comp_data; @@ -74,6 +108,11 @@ struct ipc { struct task ipc_task; #endif +#ifdef CONFIG_SOF_USERSPACE_LL + struct ipc_user *ipc_user_pdata; + struct mod_alloc_ctx *ll_alloc; +#endif + #ifdef CONFIG_SOF_TELEMETRY_IO_PERFORMANCE_MEASUREMENTS /* io performance measurement */ struct io_perf_data_item *io_perf_in_msg_count; @@ -95,6 +134,12 @@ struct ipc { extern struct task_ops ipc_task_ops; +#ifdef CONFIG_SOF_USERSPACE_LL + +struct ipc *ipc_get(void); + +#else + /** * \brief Get the IPC global context. * @return The global IPC context. @@ -104,6 +149,8 @@ static inline struct ipc *ipc_get(void) return sof_get()->ipc; } +#endif /* CONFIG_SOF_USERSPACE_LL */ + /** * \brief Initialise global IPC context. * @param[in,out] sof Global SOF context. @@ -166,6 +213,29 @@ struct dai_data; */ int ipc_dai_data_config(struct dai_data *dd, struct comp_dev *dev); +/** + * \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 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 create a IPC boot complete message. * @param[in] header header. @@ -173,7 +243,7 @@ int ipc_dai_data_config(struct dai_data *dd, struct comp_dev *dev); */ void ipc_boot_complete_msg(struct ipc_cmd_hdr *header, uint32_t data); -#if defined(CONFIG_PM_DEVICE) && defined(CONFIG_INTEL_ADSP_IPC) +#if defined(CONFIG_PM_DEVICE) && defined(CONFIG_IPC_SERVICE_BACKEND_INTEL_ADSP_HOST_IPC) /** * @brief Send an IPC response to Host power transition request informing * that power transition failed. @@ -186,7 +256,7 @@ void ipc_boot_complete_msg(struct ipc_cmd_hdr *header, uint32_t data); * IPC task but during power transition logic in the Idle thread. */ void ipc_send_failed_power_transition_response(void); -#endif /* CONFIG_PM_DEVICE && CONFIG_INTEL_ADSP_IPC */ +#endif /* CONFIG_PM_DEVICE && CONFIG_IPC_SERVICE_BACKEND_INTEL_ADSP_HOST_IPC */ /** * \brief Send a IPC notification that FW has hit * a DSP notification. @@ -236,12 +306,6 @@ void ipc_cmd(struct ipc_cmd_hdr *_hdr); */ int ipc_process_on_core(uint32_t core, bool blocking); -/** - * \brief reply to an IPC message. - * @param[in] reply pointer to the reply structure. - */ -void ipc_msg_reply(struct sof_ipc_reply *reply); - /** * \brief Call platform-specific IPC completion function. */ @@ -250,4 +314,30 @@ void ipc_complete_cmd(struct ipc *ipc); /* GDB stub: should enter GDB after completing the IPC processing */ extern bool ipc_enter_gdb; +#ifdef CONFIG_SOF_USERSPACE_LL +/** + * @brief Forward an IPC command to the user-space thread and wait for it. + * + * Protocol-agnostic: only the two raw message words are handed across the + * kernel/user boundary. The active IPC major's ipc_user_thread_dispatch() + * interprets them in the user thread. + * + * @param primary Primary message word + * @param extension Extension message word + * @return Result code from user thread processing + */ +int ipc_user_forward_cmd(uint32_t primary, uint32_t extension); + +/** + * @brief Protocol-specific dispatch of a forwarded IPC command. + * + * Runs in the user-space IPC thread. A __weak default returns -ENOSYS; + * the active IPC major (e.g. IPC4) provides the real implementation. + * + * @param ipc_user User IPC context holding the forwarded message words + * @return Result code to report back to the host + */ +int ipc_user_thread_dispatch(struct ipc_user *ipc_user); +#endif + #endif /* __SOF_DRIVERS_IPC_H__ */ diff --git a/src/include/sof/ipc/ipc_reply.h b/src/include/sof/ipc/ipc_reply.h new file mode 100644 index 000000000000..915b6261c6bd --- /dev/null +++ b/src/include/sof/ipc/ipc_reply.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * + * Copyright(c) 2026 Intel Corporation. All rights reserved. + */ + +#ifndef __SOF_IPC_IPC_REPLY_H__ +#define __SOF_IPC_IPC_REPLY_H__ + +#include <ipc/header.h> + +struct sof_ipc_reply; + +/** + * \brief reply to an IPC message. + * @param[in] reply pointer to the reply structure. + */ +#if defined(__ZEPHYR__) && defined(CONFIG_SOF_FULL_ZEPHYR_APPLICATION) +__syscall void ipc_msg_reply(struct sof_ipc_reply *reply); + +#include <zephyr/syscalls/ipc_reply.h> +#else +void z_impl_ipc_msg_reply(struct sof_ipc_reply *reply); +#define ipc_msg_reply z_impl_ipc_msg_reply +#endif + +#endif /* __SOF_IPC_IPC_REPLY_H__ */ 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/ipc/topology.h b/src/include/sof/ipc/topology.h index 3503c7a407d8..422563185a90 100644 --- a/src/include/sof/ipc/topology.h +++ b/src/include/sof/ipc/topology.h @@ -49,13 +49,22 @@ typedef uint32_t ipc_comp; struct ipc_comp_dev; const struct comp_driver *ipc4_get_comp_drv(uint32_t module_id); struct comp_dev *ipc4_get_comp_dev(uint32_t comp_id); -int ipc4_chain_manager_create(struct ipc4_chain_dma *cdma); -int ipc4_chain_dma_state(struct comp_dev *dev, struct ipc4_chain_dma *cdma); +int ipc4_add_comp_dev(struct comp_dev *dev); +#ifdef CONFIG_SOF_USERSPACE_LL +struct ipc4_message_request; +struct comp_driver; +struct comp_dev *comp_new_ipc4_user(struct ipc4_message_request *ipc4, + const struct comp_driver *drv); +#endif +int ipc4_chain_manager_create(const struct ipc4_chain_dma *cdma); +int ipc4_chain_dma_state(struct comp_dev *dev, const struct ipc4_chain_dma *cdma); int ipc4_create_chain_dma(struct ipc *ipc, struct ipc4_chain_dma *cdma); int ipc4_trigger_chain_dma(struct ipc *ipc, struct ipc4_chain_dma *cdma, bool *delay); int ipc4_process_on_core(uint32_t core, bool blocking); int ipc4_pipeline_complete(struct ipc *ipc, uint32_t comp_id, uint32_t cmd); int ipc4_find_dma_config(struct ipc_config_dai *dai, uint8_t *data_buffer, uint32_t size); +int ipc4_find_all_dma_configs_tlvs_only(struct ipc_config_dai *dai, + uint8_t *data_buffer, size_t size); int ipc4_pipeline_prepare(struct ipc_comp_dev *ppl_icd, uint32_t cmd); int ipc4_pipeline_trigger(struct ipc_comp_dev *ppl_icd, uint32_t cmd, bool *delayed); int ipc4_find_dma_config_multiple(struct ipc_config_dai *dai, uint8_t *data_buffer, @@ -156,7 +165,7 @@ int ipc_pipeline_complete(struct ipc *ipc, uint32_t comp_id); /** * \brief Connect components together on a pipeline. * @param ipc The global IPC context. - * @param connect Components to connect together.. + * @param connect Components to connect together. * @return 0 on success or negative error. */ int ipc_comp_connect(struct ipc *ipc, ipc_pipe_comp_connect *connect); diff --git a/src/include/sof/lib/ams.h b/src/include/sof/lib/ams.h index 8cb3ae217400..e43d6208f875 100644 --- a/src/include/sof/lib/ams.h +++ b/src/include/sof/lib/ams.h @@ -29,10 +29,6 @@ /* Space allocated for async message content*/ #define AMS_MAX_MSG_SIZE 0x1000 -/* Size of slots message, module id and instance id */ -#define AMS_SLOT_SIZE(msg) (AMS_MESSAGE_SIZE(msg) + sizeof(uint16_t) * 2) -#define AMS_MESSAGE_SIZE(msg) (sizeof(*msg) - sizeof(char) + (sizeof(char) * (msg->message_length))) - /** * \brief IXC message payload * 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..a0c42bff351e 100644 --- a/src/include/sof/lib/dai-zephyr.h +++ b/src/include/sof/lib/dai-zephyr.h @@ -28,6 +28,8 @@ #include <sof/ipc/topology.h> #include <sof/audio/pcm_converter.h> #include <sof/audio/ipc-config.h> +#include <sof/audio/component.h> +#include <ipc/dai.h> #include <errno.h> #include <stddef.h> #include <stdint.h> @@ -116,7 +118,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,10 +165,12 @@ 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; + + struct mod_alloc_ctx alloc_ctx; }; /* these 3 are here to satisfy clk.c and ssp.h interconnection, will be removed leter */ @@ -237,7 +241,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 +309,16 @@ 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); +__syscall 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); /** @}*/ +#include <zephyr/syscalls/dai-zephyr.h> + #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..f022b805144c 100644 --- a/src/include/sof/lib/notifier.h +++ b/src/include/sof/lib/notifier.h @@ -27,11 +27,7 @@ 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 * */ NOTIFIER_ID_DAI_TRIGGER, /* struct dai_group * */ NOTIFIER_ID_MIC_PRIVACY_STATE_CHANGE, /* struct mic_privacy_settings * */ diff --git a/src/include/sof/lib/vregion.h b/src/include/sof/lib/vregion.h new file mode 100644 index 000000000000..559c47ed3431 --- /dev/null +++ b/src/include/sof/lib/vregion.h @@ -0,0 +1,208 @@ +// 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> +#include <stdint.h> +#include <sof/compiler_attributes.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 */ + VREGION_MEM_TYPE_INVALID, /* Interim heap initialization failed */ +}; + +#if CONFIG_SOF_VREGIONS + +/** + * @brief Create a new virtual region instance. + * + * Create a new virtual region instance with specified memory size. + * Allocations start in LIFETIME mode. + * + * @param[in] memsize Total size of the virtual region memory. + * @return struct vregion* Pointer to the new virtual region instance, or NULL on failure. + */ +struct vregion *vregion_create(size_t memsize); + +/** + * @brief Create and map a new virtual region instance. + * + * Like above, but usable by userspace and can add domain access. + */ +__syscall struct vregion *vregion_create_map(uintptr_t *vreg_start, size_t *vreg_size); + +/** + * @brief Switch virtual region allocations to interim mode. + * + * After this call, all allocations from this vregion will use the interim + * heap. The interim heap is created lazily from remaining lifetime space. + * Multiple calls are allowed but log a warning. + * + * @param[in] vr Pointer to the virtual region instance. + */ +__syscall void vregion_set_interim(struct vregion *vr); + +/** + * @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. + */ +__syscall 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. + */ +__syscall struct vregion *vregion_put(struct vregion *vr); + +/** + * @brief Allocate aligned memory from the specified virtual region. + * + * Allocate aligned memory from the specified virtual region using the + * current allocation mode (lifetime or interim). + * + * @param[in] vr Pointer to the virtual region instance. + * @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. + */ +__syscall void *vregion_alloc_align(struct vregion *vr, size_t size, size_t alignment); + +/** + * @brief like vregion_alloc_align() but allocates coherent memory + */ +__syscall void *vregion_alloc_coherent_align(struct vregion *vr, size_t size, size_t alignment); + +/** + * @brief Allocate memory from the specified virtual region. + * + * @param[in] vr Pointer to the virtual region instance. + * @param[in] size Size of memory to allocate in bytes. + * @return void* Pointer to the allocated memory, or NULL on failure. + */ +static inline void *vregion_alloc(struct vregion *vr, size_t size) +{ + return vregion_alloc_align(vr, size, 0); +} + +/** + * @brief like vregion_alloc() but allocates coherent memory + */ +static inline void *vregion_alloc_coherent(struct vregion *vr, size_t size) +{ + return vregion_alloc_coherent_align(vr, size, 0); +} + +/** + * @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. + */ +__syscall 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); + +bool vregion_verify(struct vregion *vr); + +#include <zephyr/syscalls/vregion.h> + +#else /* CONFIG_SOF_VREGIONS */ + +struct vregion { + unsigned int use_count; +}; + +static inline struct vregion *vregion_create(size_t memsize) +{ + return NULL; +} +static inline struct vregion *vregion_create_map(uintptr_t *vreg_start, size_t *vreg_size) +{ + return NULL; +} +static inline void vregion_set_interim(struct vregion *vr) {} +static inline struct vregion *vregion_get(struct vregion *vr) +{ + return vr; +} +static inline struct vregion *vregion_put(struct vregion *vr) +{ + return vr; +} +static inline void *vregion_alloc(struct vregion *vr, size_t size) +{ + return NULL; +} +static inline void *vregion_alloc_coherent(struct vregion *vr, size_t size) +{ + return NULL; +} +static inline void *vregion_alloc_align(struct vregion *vr, size_t size, size_t alignment) +{ + return NULL; +} +static inline void *vregion_alloc_coherent_align(struct vregion *vr, 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; +} +static inline bool vregion_verify(struct vregion *vr) {return false;} + +#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..019695be69ef 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, }; @@ -109,13 +114,16 @@ struct lib_manager_module { struct llext *llext; /* Zephyr loadable extension context */ struct llext_buf_loader *ebl; /* Zephyr loadable extension buffer loader */ unsigned int n_dependent; /* For auxiliary modules: number of dependents */ + unsigned int n_mod; bool mapped; + bool domain_dp; struct lib_manager_segment_desc segment[LIB_MANAGER_N_SEGMENTS]; }; struct lib_manager_mod_ctx { void *base_addr; /* library cold storage address (e.g. DRAM) */ unsigned int n_mod; + unsigned int load_type; struct lib_manager_module *mod; }; @@ -198,15 +206,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..a68f0468d591 100644 --- a/src/include/sof/math/auditory.h +++ b/src/include/sof/math/auditory.h @@ -14,6 +14,7 @@ #include <sof/math/log.h> #include <stdint.h> +#define AUDITORY_MAX_MEL_BANDS 256 #define AUDITORY_EPS_Q31 1 /* Smallest nonzero Q1.31 value */ #define AUDITORY_LOG2_2P25_Q16 Q_CONVERT_FLOAT(25.0, 16) /* log2(2^25) */ @@ -76,7 +77,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 +104,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..d531b84a81fa 100644 --- a/src/include/sof/math/numbers.h +++ b/src/include/sof/math/numbers.h @@ -24,6 +24,12 @@ __typeof__(alignment) __alignment = (alignment); \ __size - (__size % __alignment); \ }) + +#define ROUND_UP(size, alignment) ({ \ + __typeof__(size) __size = (size); \ + __typeof__(alignment) __alignment = (alignment); \ + ((__size + __alignment - 1) / __alignment) * __alignment; \ +}) #endif /* ! __ZEPHYR__ */ #define ABS(a) ({ \ @@ -36,7 +42,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..1b55405d0fb1 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; @@ -76,6 +78,7 @@ int scheduler_dp_task_init(struct task **task, uint16_t core, size_t stack_size, uint32_t options); +void scheduler_dp_ll_tick(void); /** * \brief Extract information about scheduler's tasks @@ -86,4 +89,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 { + const 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.h b/src/include/sof/schedule/ll_schedule.h index 241a20daff40..b5baccfe3ab7 100644 --- a/src/include/sof/schedule/ll_schedule.h +++ b/src/include/sof/schedule/ll_schedule.h @@ -40,14 +40,14 @@ int scheduler_init_ll(struct ll_schedule_domain *domain); int schedule_task_init_ll(struct task *task, const struct sof_uuid_entry *uid, uint16_t type, - uint16_t priority, enum task_state (*run)(void *data), + int16_t priority, enum task_state (*run)(void *data), void *data, uint16_t core, uint32_t flags); #else int zephyr_ll_scheduler_init(struct ll_schedule_domain *domain); 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), + int16_t priority, enum task_state (*run)(void *data), void *data, uint16_t core, uint32_t flags); #define scheduler_init_ll zephyr_ll_scheduler_init diff --git a/src/include/sof/schedule/ll_schedule_domain.h b/src/include/sof/schedule/ll_schedule_domain.h index 5fc7691e3b13..03991debb0cb 100644 --- a/src/include/sof/schedule/ll_schedule_domain.h +++ b/src/include/sof/schedule/ll_schedule_domain.h @@ -22,7 +22,11 @@ #include <stdbool.h> #include <stdint.h> +#if defined(CONFIG_AMD) +#define LL_TIMER_PERIOD_US 500ULL /* 500us period for AMD ACP platforms */ +#else #define LL_TIMER_PERIOD_US 1000ULL /* default period in microseconds */ +#endif /* Default ll watchdog timeout in microseconds. * It was decided to have a timeout of two periods to give a safe margin of time between the start @@ -43,6 +47,20 @@ struct ll_schedule_domain_ops { void (*handler)(void *arg), void *arg); int (*domain_unregister)(struct ll_schedule_domain *domain, struct task *task, uint32_t num_tasks); +#if CONFIG_SOF_USERSPACE_LL + /* + * Initialize the scheduling thread and perform all privileged setup + * (thread creation, timer init, access grants). Called once from + * kernel context before any user-space domain_register() calls. + */ + int (*domain_thread_init)(struct ll_schedule_domain *domain, + struct task *task); + /* Free resources acquired by domain_thread_init(). Called from + * kernel context when the scheduling context is being torn down. + */ + void (*domain_thread_free)(struct ll_schedule_domain *domain, + uint32_t num_tasks); +#endif void (*domain_enable)(struct ll_schedule_domain *domain, int core); void (*domain_disable)(struct ll_schedule_domain *domain, int core); #if CONFIG_CROSS_CORE_STREAM @@ -66,7 +84,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 +115,36 @@ 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); +bool zephyr_ll_user_heap_verify(struct k_heap *heap); +void zephyr_ll_user_resources_init(void); +void user_ll_grant_access(struct k_thread *thread, int core); +void user_ll_lock_sched(int core); +void user_ll_unlock_sched(int core); +#ifdef CONFIG_ASSERT +/* Assert that the calling context already holds the LL lock for 'core'. */ +void user_ll_assert_locked(int core); +#else +static inline void user_ll_assert_locked(int core) { ARG_UNUSED(core); } +#endif +#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 +161,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); @@ -150,6 +205,31 @@ static inline void domain_task_cancel(struct ll_schedule_domain *domain, domain->ops->domain_task_cancel(domain, task); } +#if CONFIG_SOF_USERSPACE_LL +/* + * Initialize the scheduling thread and do all privileged setup. + * Must be called from kernel context before user-space tasks register. + */ +static inline int domain_thread_init(struct ll_schedule_domain *domain, + struct task *task) +{ + assert(domain->ops->domain_thread_init); + + return domain->ops->domain_thread_init(domain, task); +} + +/* + * Free resources acquired by domain_thread_init(). + * Must be called from kernel context. + */ +static inline void domain_thread_free(struct ll_schedule_domain *domain, + uint32_t num_tasks) +{ + if (domain->ops->domain_thread_free) + domain->ops->domain_thread_free(domain, num_tasks); +} +#endif + static inline int domain_register(struct ll_schedule_domain *domain, struct task *task, void (*handler)(void *arg), void *arg) @@ -246,7 +326,22 @@ 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_thread *zephyr_domain_thread_tid_for_core(int core); +struct k_mem_domain *zephyr_ll_mem_domain(void); +#endif /* CONFIG_SOF_USERSPACE_LL */ +#ifdef CONFIG_SOF_FULL_ZEPHYR_APPLICATION +__syscall int zephyr_ll_task_sem_alloc(struct task *task); +__syscall int zephyr_ll_task_sem_free(struct task *task); +#include <zephyr/syscalls/ll_schedule_domain.h> +#else +int z_impl_zephyr_ll_task_sem_alloc(struct task *task); +int z_impl_zephyr_ll_task_sem_free(struct task *task); +#define zephyr_ll_task_sem_alloc z_impl_zephyr_ll_task_sem_alloc +#define zephyr_ll_task_sem_free z_impl_zephyr_ll_task_sem_free +#endif /* CONFIG_SOF_FULL_ZEPHYR_APPLICATION */ +#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/schedule/schedule.h b/src/include/sof/schedule/schedule.h index bbdcbbecf3b4..f28f472b121f 100644 --- a/src/include/sof/schedule/schedule.h +++ b/src/include/sof/schedule/schedule.h @@ -140,6 +140,7 @@ struct scheduler_ops { */ int (*schedule_task_free)(void *data, struct task *task); +#if CONFIG_SOF_BOOT_TEST_STANDALONE || CONFIG_LIBRARY /** * Frees scheduler's resources. * @param data Private data of selected scheduler. @@ -149,15 +150,17 @@ struct scheduler_ops { * This operation is optional. */ void (*scheduler_free)(void *data, uint32_t flags); +#endif /** - * Restores scheduler's resources. + * Initializes context * @param data Private data of selected scheduler. - * @return 0 if succeeded, error code otherwise. + * @param task task that needs to be scheduled + * @return thread that will be used to run the scheduled task * * This operation is optional. */ - int (*scheduler_restore)(void *data); + struct k_thread *(*scheduler_init_context)(void *data, struct task *task); }; /** \brief Holds information about scheduler. */ @@ -179,17 +182,18 @@ struct schedulers { */ struct schedulers **arch_schedulers_get(void); -/** - * Retrieves scheduler's data. - * @param type SOF_SCHEDULE_ type. - * @return Pointer to scheduler's data. - */ -static inline void *scheduler_get_data(uint16_t type) +struct schedulers **arch_user_schedulers_get(void); + +struct schedulers **arch_user_schedulers_get_for_core(int core); + +static inline void *scheduler_list_get_data(struct schedulers *schedulers, uint16_t type) { - struct schedulers *schedulers = *arch_schedulers_get(); struct schedule_data *sch; struct list_item *slist; + if (!schedulers) + return NULL; + list_for_item(slist, &schedulers->list) { sch = container_of(slist, struct schedule_data, list); if (type == sch->type) @@ -199,154 +203,133 @@ static inline void *scheduler_get_data(uint16_t type) return NULL; } +/** + * Retrieves scheduler's data. + * @param type SOF_SCHEDULE_ type. + * @return Pointer to scheduler's data. + */ +static inline void *scheduler_get_data(uint16_t type) +{ + return scheduler_list_get_data(*arch_schedulers_get(), type); +} + +/** + * Retrieves userspace scheduler's data. + * @param type SOF_SCHEDULE_ type. + * @return Pointer to scheduler's data. + */ +static inline void *scheduler_get_user_data(uint16_t type) +{ + return scheduler_list_get_data(*arch_user_schedulers_get(), type); +} + /** See scheduler_ops::schedule_task_running */ static inline int schedule_task_running(struct task *task) { - struct schedulers *schedulers = *arch_schedulers_get(); struct schedule_data *sch; - struct list_item *slist; - list_for_item(slist, &schedulers->list) { - sch = container_of(slist, struct schedule_data, list); - if (task->type == sch->type) { - /* optional operation */ - if (!sch->ops->schedule_task_running) - return 0; + assert(task); + sch = task->sch; - return sch->ops->schedule_task_running(sch->data, task); - } - } + if (!sch->ops->schedule_task_running) + return 0; - return -ENODEV; + return sch->ops->schedule_task_running(sch->data, task); } /** See scheduler_ops::schedule_task */ static inline int schedule_task(struct task *task, uint64_t start, uint64_t period) { - struct schedulers *schedulers = *arch_schedulers_get(); struct schedule_data *sch; - struct list_item *slist; if (!task) return -EINVAL; - list_for_item(slist, &schedulers->list) { - sch = container_of(slist, struct schedule_data, list); - if (task->type == sch->type) - return sch->ops->schedule_task(sch->data, task, start, - period); - } + sch = task->sch; - return -ENODEV; + return sch->ops->schedule_task(sch->data, task, start, period); } /** See scheduler_ops::schedule_task_before */ static inline int schedule_task_before(struct task *task, uint64_t start, uint64_t period, struct task *before) { - struct schedulers *schedulers = *arch_schedulers_get(); struct schedule_data *sch; - struct list_item *slist; if (!task || !before) return -EINVAL; - list_for_item(slist, &schedulers->list) { - sch = container_of(slist, struct schedule_data, list); - if (task->type == sch->type) { - if (sch->ops->schedule_task_before) - return sch->ops->schedule_task_before(sch->data, task, start, - period, before); - - return sch->ops->schedule_task(sch->data, task, start, - period); - } - } + sch = task->sch; + + if (sch->ops->schedule_task_before) + return sch->ops->schedule_task_before(sch->data, task, start, + period, before); - return -ENODEV; + return sch->ops->schedule_task(sch->data, task, start, period); } /** See scheduler_ops::schedule_task_after */ static inline int schedule_task_after(struct task *task, uint64_t start, uint64_t period, struct task *after) { - struct schedulers *schedulers = *arch_schedulers_get(); struct schedule_data *sch; - struct list_item *slist; if (!task || !after) return -EINVAL; - list_for_item(slist, &schedulers->list) { - sch = container_of(slist, struct schedule_data, list); - if (task->type == sch->type) { - if (sch->ops->schedule_task_after) - return sch->ops->schedule_task_after(sch->data, task, start, - period, after); - - return sch->ops->schedule_task(sch->data, task, start, - period); - } - } + sch = task->sch; - return -ENODEV; + if (sch->ops->schedule_task_after) + return sch->ops->schedule_task_after(sch->data, task, start, + period, after); + + return sch->ops->schedule_task(sch->data, task, start, period); } /** See scheduler_ops::reschedule_task */ static inline int reschedule_task(struct task *task, uint64_t start) { - struct schedulers *schedulers = *arch_schedulers_get(); struct schedule_data *sch; - struct list_item *slist; - list_for_item(slist, &schedulers->list) { - sch = container_of(slist, struct schedule_data, list); - if (task->type == sch->type) { - /* optional operation */ - if (!sch->ops->reschedule_task) - return 0; - - return sch->ops->reschedule_task(sch->data, task, - start); - } - } + assert(task); + sch = task->sch; + + /* optional operation */ + if (!sch->ops->reschedule_task) + return 0; - return -ENODEV; + return sch->ops->reschedule_task(sch->data, task, start); } /** See scheduler_ops::schedule_task_cancel */ static inline int schedule_task_cancel(struct task *task) { - struct schedulers *schedulers = *arch_schedulers_get(); struct schedule_data *sch; - struct list_item *slist; - list_for_item(slist, &schedulers->list) { - sch = container_of(slist, struct schedule_data, list); - if (task->type == sch->type) - return sch->ops->schedule_task_cancel(sch->data, task); - } + if (!task || !task->sch) + return -EINVAL; + + sch = task->sch; - return -ENODEV; + return sch->ops->schedule_task_cancel(sch->data, task); } /** See scheduler_ops::schedule_task_free */ static inline int schedule_task_free(struct task *task) { - struct schedulers *schedulers = *arch_schedulers_get(); struct schedule_data *sch; - struct list_item *slist; - list_for_item(slist, &schedulers->list) { - sch = container_of(slist, struct schedule_data, list); - if (task->type == sch->type) - return sch->ops->schedule_task_free(sch->data, task); - } + if (!task || !task->sch) + return -EINVAL; + + sch = task->sch; - return -ENODEV; + return sch->ops->schedule_task_free(sch->data, task); } +/* Only used in a stand-alone test and in a testbench test */ +#if CONFIG_SOF_BOOT_TEST_STANDALONE || CONFIG_LIBRARY /** See scheduler_ops::scheduler_free */ static inline void schedule_free(uint32_t flags) { @@ -360,23 +343,20 @@ static inline void schedule_free(uint32_t flags) sch->ops->scheduler_free(sch->data, flags); } } +#endif -/** See scheduler_ops::scheduler_restore */ -static inline int schedulers_restore(void) +/** See scheduler_ops::scheduler_init_context */ +static inline struct k_thread *scheduler_init_context(struct task *task) { - struct schedulers *schedulers = *arch_schedulers_get(); struct schedule_data *sch; - struct list_item *slist; - assert(schedulers); + assert(task && task->sch); + sch = task->sch; - list_for_item(slist, &schedulers->list) { - sch = container_of(slist, struct schedule_data, list); - if (sch->ops->scheduler_restore) - return sch->ops->scheduler_restore(sch->data); - } + if (sch->ops->scheduler_init_context) + return sch->ops->scheduler_init_context(sch->data, task); - return 0; + return NULL; } /** @@ -393,7 +373,7 @@ static inline int schedulers_restore(void) */ int schedule_task_init(struct task *task, const struct sof_uuid_entry *uid, uint16_t type, - uint16_t priority, enum task_state (*run)(void *data), + int16_t priority, enum task_state (*run)(void *data), void *data, uint16_t core, uint32_t flags); /** 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_slot.h b/src/include/user/debug_stream_slot.h index 4464c9ed56b8..926b7f9634c8 100644 --- a/src/include/user/debug_stream_slot.h +++ b/src/include/user/debug_stream_slot.h @@ -127,6 +127,8 @@ struct debug_stream_record; * -ENODEV if debug stream slot is not configured * -ENOMEM if the record is too big */ -int debug_stream_slot_send_record(struct debug_stream_record *rec); +__syscall int debug_stream_slot_send_record(struct debug_stream_record *rec); + +#include <zephyr/syscalls/debug_stream_slot.h> #endif /* __SOC_DEBUG_WINDOW_SLOT_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..286ee4f5e985 100644 --- a/src/include/user/mfcc.h +++ b/src/include/user/mfcc.h @@ -50,7 +50,13 @@ 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 */ + uint16_t dtx_trailing_silence_hops; /**< DTX: number of silence hops to send after speech, 0 = send first only */ + uint16_t dtx_silence_hops_interval; /**< DTX: send silence frame every Nth hop during VAD=0, 0 = disable */ + uint32_t reserved[5]; 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,10 +75,11 @@ 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 */ + int16_t reserved16[3]; /**< Reserved for future 16-bit fields, set to 0 */ bool htk_compat; /**< Must be false */ bool raw_energy; /**< Reserved, no support */ bool remove_dc_offset; /**< Reserved, no support */ @@ -80,9 +87,12 @@ 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 reserved_bool2; - bool reserved_bool3; + bool dynamic_mmax; /**< Track max Mel value for clamp with top_db value */ + bool enable_vad; /**< Run VAD algorithm */ + bool enable_dtx; /**< Discontinuous transmission: suppress silence after trailing frames */ + bool update_controls; /**< Update controls with VAD decision */ + bool compress_output; /**< Use compress PCM output: variable size, no zero padding */ + bool reserved_bool[4]; /* Reserved for future boolean flags, set to false (0) */ } __attribute__((packed)); #endif /* __USER_MFCC_H__ */ 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..5990cfebc2dc 100644 --- a/src/init/init.c +++ b/src/init/init.c @@ -32,6 +32,7 @@ #include <sof/schedule/dp_schedule.h> #include <sof/schedule/ll_schedule.h> #include <sof/schedule/ll_schedule_domain.h> +#include <sof/audio/pipeline.h> #include <ipc/trace.h> #if CONFIG_IPC_MAJOR_4 #include <ipc4/fw_reg.h> @@ -41,7 +42,7 @@ #include <zephyr/logging/log_ctrl.h> #include <user/abi_dbg.h> #include <sof_versions.h> -#include <version.h> +#include <zephyr/version.h> #endif #include <sof/lib/ams.h> @@ -94,13 +95,15 @@ static bool check_restore(void) { struct idc *idc = *idc_get(); struct notify *notifier = *arch_notify_get(); - struct schedulers *schedulers = *arch_schedulers_get(); + struct schedulers **schedulers = arch_schedulers_get(); + struct schedulers **user_schedulers = arch_user_schedulers_get(); /* check whether basic core structures has been already allocated. If they * are available in memory, it means that this is not cold boot and memory * has not been powered off. */ - return !!idc && !!notifier && !!schedulers; + return !!idc && !!notifier && + ((schedulers && *schedulers) || (user_schedulers && *user_schedulers)); } static inline int secondary_core_restore(void) { return 0; }; @@ -110,6 +113,8 @@ __cold int secondary_core_init(struct sof *sof) int err; struct ll_schedule_domain *dma_domain; + assert_can_be_cold(); + /* check whether we are in a cold boot process or not (e.g. D0->D0ix * flow when primary core disables all secondary cores). If not, we do * not have allocate basic structures like e.g. schedulers, notifier, @@ -154,6 +159,8 @@ __cold int secondary_core_init(struct sof *sof) trace_point(TRACE_BOOT_PLATFORM); + LOG_INF("init done"); + return err; } @@ -161,6 +168,8 @@ __cold int secondary_core_init(struct sof *sof) __cold static void print_version_banner(void) { + assert_can_be_cold(); + /* * Non-Zephyr builds emit the version banner in DMA-trace * init and this is done at a later time as otherwise the @@ -191,6 +200,8 @@ static log_timestamp_t default_get_timestamp(void) __cold static int primary_core_init(int argc, char *argv[], struct sof *sof) { + assert_can_be_cold(); + /* setup context */ sof->argc = argc; sof->argv = argv; @@ -218,6 +229,15 @@ __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 pipeline position offsets - must be before platform_init() + * which calls ipc_init() -> ipc_user_init() that needs the posn mutex. + */ + pipeline_posn_init(sof); + /* init the platform */ if (platform_init(sof) < 0) sof_panic(SOF_IPC_PANIC_PLATFORM); @@ -232,8 +252,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..0e7b344510cd 100644 --- a/src/ipc/ipc-common.c +++ b/src/ipc/ipc-common.c @@ -24,6 +24,8 @@ #include <sof/lib/memory.h> #include <sof/list.h> #include <sof/platform.h> +#include <sof/schedule/ll_schedule.h> +#include <sof/schedule/ll_schedule_domain.h> #include <rtos/sof.h> #include <rtos/spinlock.h> #include <ipc/dai.h> @@ -35,6 +37,14 @@ #include <stddef.h> #include <stdint.h> +#ifdef __ZEPHYR__ +#include <zephyr/kernel.h> +#endif + +#ifdef CONFIG_SOF_USERSPACE_LL +#include <rtos/userspace_helper.h> +#endif + #include <sof/debug/telemetry/performance_monitor.h> LOG_MODULE_REGISTER(ipc, CONFIG_SOF_LOG_LEVEL); @@ -43,6 +53,17 @@ SOF_DEFINE_REG_UUID(ipc); DECLARE_TR_CTX(ipc_tr, SOF_UUID(ipc_uuid), LOG_LEVEL_INFO); +#ifdef CONFIG_SOF_USERSPACE_LL +K_APPMEM_PARTITION_DEFINE(ipc_context_part); + +K_APP_BMEM(ipc_context_part) static struct ipc ipc_context; + +struct ipc *ipc_get(void) +{ + return &ipc_context; +} +#endif + int ipc_process_on_core(uint32_t core, bool blocking) { struct ipc *ipc = ipc_get(); @@ -55,9 +76,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 +226,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 +246,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); @@ -286,29 +309,310 @@ void ipc_schedule_process(struct ipc *ipc) #endif } +#ifdef CONFIG_SOF_USERSPACE_LL +/* Generic user-space IPC handling thread infrastructure. Protocol-specific + * command interpretation lives in ipc_user_thread_dispatch(), implemented by + * the active IPC major (see src/ipc/ipc4/). + */ + +#define IPC_USER_EVENT_CMD BIT(0) +#define IPC_USER_EVENT_STOP BIT(1) + +static K_THREAD_STACK_DEFINE(ipc_user_stack, CONFIG_SOF_IPC_USER_THREAD_STACK_SIZE); + +/** + * @brief Forward an IPC command to the user-space thread. + * + * Called from kernel context (IPC EDF task) to forward the message words + * to the user-space thread for processing. Sets IPC_TASK_IN_THREAD in + * task_mask so the host is not signaled until the user thread completes. + * Blocks until the user thread finishes processing and returns the result. + * + * @param primary Primary message word + * @param extension Extension message word + * @return Result from user thread processing + */ +int ipc_user_forward_cmd(uint32_t primary, uint32_t extension) +{ + struct ipc *ipc = ipc_get(); + struct ipc_user *pdata = ipc->ipc_user_pdata; + k_spinlock_key_t key; + int ret; + + LOG_DBG("IPC: forward cmd %08x", primary); + + /* Copy message words — original buffer may be reused */ + pdata->ipc_msg_pri = primary; + pdata->ipc_msg_ext = extension; + pdata->ipc = ipc; + + /* Prevent host completion until user thread finishes */ + key = k_spin_lock(&ipc->lock); + ipc->task_mask |= IPC_TASK_IN_THREAD; + k_spin_unlock(&ipc->lock, key); + + /* Wake the user thread */ + k_event_set(pdata->event, IPC_USER_EVENT_CMD); + + /* Wait for user thread to complete */ + ret = k_sem_take(pdata->sem, K_MSEC(100)); + if (ret) { + LOG_ERR("IPC user: sem error %d\n", ret); + /* fall through to complete the cmd */ + } + + /* Clear the task mask bit and check for completion */ + key = k_spin_lock(&ipc->lock); + ipc->task_mask &= ~IPC_TASK_IN_THREAD; + ipc_complete_cmd(ipc); + k_spin_unlock(&ipc->lock, key); + + return !ret ? pdata->result : ret; +} + +/** + * @brief Protocol-specific dispatch of a forwarded IPC command. + * + * Weak default; the active IPC major overrides this. Runs in the user + * thread context with the forwarded message words in @p ipc_user. + */ +__weak int ipc_user_thread_dispatch(struct ipc_user *ipc_user) +{ + ARG_UNUSED(ipc_user); + return -ENOSYS; +} + +/** + * User-space IPC thread entry point. p1 points to the ipc_user context + * shared with the kernel launcher. + */ +static void ipc_user_thread_fn(void *p1, void *p2, void *p3) +{ + struct ipc_user *ipc_user = p1; + + ARG_UNUSED(p2); + ARG_UNUSED(p3); + + __ASSERT(k_is_user_context(), "expected user context"); + + /* Signal startup complete — unblocks init waiting on semaphore */ + k_sem_give(ipc_user->sem); + LOG_INF("IPC user-space thread started"); + + for (;;) { + uint32_t mask = k_event_wait_safe(ipc_user->event, + IPC_USER_EVENT_CMD | IPC_USER_EVENT_STOP, + false, K_FOREVER); + + LOG_DBG("IPC user wake, mask %u", mask); + + if (mask & IPC_USER_EVENT_CMD) { + ipc_user->result = ipc_user_thread_dispatch(ipc_user); + + /* Signal completion — kernel side will finish IPC */ + k_sem_give(ipc_user->sem); + } + + if (mask & IPC_USER_EVENT_STOP) + break; + } +} + +__cold static int ipc_user_init_thread(struct ipc_user *ipc_user) +{ + char thread_name[] = "ll_user0"; + int ret; + + assert_can_be_cold(); + + /* Allocate kernel objects for the user-space thread */ + ipc_user->event = k_object_alloc(K_OBJ_EVENT); + if (!ipc_user->event) { + LOG_ERR("user IPC event alloc failed"); + return -ENOMEM; + } + k_event_init(ipc_user->event); + + ipc_user->thread = k_object_alloc(K_OBJ_THREAD); + if (!ipc_user->thread) { + LOG_ERR("user IPC thread alloc failed"); + ret = -ENOMEM; + goto e_event; + } + + k_thread_create(ipc_user->thread, ipc_user_stack, + CONFIG_SOF_IPC_USER_THREAD_STACK_SIZE, + ipc_user_thread_fn, ipc_user, NULL, NULL, + -1, K_USER, K_FOREVER); + + k_thread_cpu_pin(ipc_user->thread, PLATFORM_PRIMARY_CORE_ID); + k_thread_name_set(ipc_user->thread, thread_name); + + /* + * Each userspace IPC thread must be able to wait on its private event + * and signal completion on the primary core semaphore + */ + k_thread_access_grant(ipc_user->thread, ipc_user->sem, ipc_user->event); + user_grant_dai_access_all(ipc_user->thread); + user_grant_dma_access_all(ipc_user->thread); + k_mem_domain_add_thread(zephyr_ll_mem_domain(), ipc_user->thread); + user_ll_grant_access(ipc_user->thread, PLATFORM_PRIMARY_CORE_ID); + pipeline_posn_grant_access(ipc_user->thread); + + return 0; + +e_event: + k_object_free(ipc_user->event); + + return ret; +} + +__cold static void ipc_user_init(void) +{ + struct ipc *ipc = ipc_get(); + struct ipc_user *ipc_user = sof_heap_alloc(sof_sys_user_heap_get(), + SOF_MEM_FLAG_USER | SOF_MEM_FLAG_COHERENT, + sizeof(*ipc_user), 0); + int ret; + + if (!ipc_user) { + LOG_ERR("user IPC pdata alloc failed"); + sof_panic(SOF_IPC_PANIC_IPC); + } + + ipc_user->sem = k_object_alloc(K_OBJ_SEM); + if (!ipc_user->sem) { + LOG_ERR("user IPC sem alloc failed"); + k_panic(); + } + + ret = k_mem_domain_add_partition(zephyr_ll_mem_domain(), &ipc_context_part); + if (ret < 0) + LOG_WRN("ipc context partition add failed: %d", ret); + + /* + * Grant user-space access to .cold (execute) and .coldrodata (read) + * sections in IMR. The prepare path walks component code that may + * reference __cold functions and __cold_rodata data. + */ +#ifdef CONFIG_COLD_STORE_EXECUTE_DRAM + extern char __cold_start[], __cold_end[]; + extern char __coldrodata_start[]; + extern char _imr_end[]; + + if (&__cold_end[0] > &__cold_start[0]) { + struct k_mem_partition cold_part = { + .start = (uintptr_t)__cold_start, + .size = ALIGN_UP((uintptr_t)__cold_end - (uintptr_t)__cold_start, + CONFIG_MMU_PAGE_SIZE), + .attr = K_MEM_PARTITION_P_RX_U_RX, + }; + + ret = k_mem_domain_add_partition(zephyr_ll_mem_domain(), &cold_part); + if (ret < 0) + LOG_WRN("cold text partition add failed: %d", ret); + } + + if (&_imr_end[0] > &__coldrodata_start[0]) { + struct k_mem_partition cold_part = { + .start = (uintptr_t)__coldrodata_start, + .size = ALIGN_UP((uintptr_t)_imr_end - (uintptr_t)__coldrodata_start, + CONFIG_MMU_PAGE_SIZE), + .attr = K_MEM_PARTITION_P_RO_U_RO, + }; + + ret = k_mem_domain_add_partition(zephyr_ll_mem_domain(), &cold_part); + if (ret < 0) + LOG_WRN("cold rodata partition %#zx @ %#lx add failed: %d", + cold_part.size, cold_part.start, ret); + } +#endif + + k_sem_init(ipc_user->sem, 0, 1); + + ret = ipc_user_init_thread(ipc_user); + if (ret < 0) { + LOG_ERR("user IPC thread initialization failed"); + sof_panic(SOF_IPC_PANIC_IPC); + } + + ret = user_access_to_mailbox(zephyr_ll_mem_domain(), ipc_user->thread); + if (ret < 0) { + LOG_ERR("ipc user: mailbox access grant failed: %d", ret); + sof_panic(SOF_IPC_PANIC_IPC); + } + + /* Store references in ipc struct so kernel handler can forward commands */ + ipc->ipc_user_pdata = ipc_user; + + k_thread_start(ipc_user->thread); + + struct task *task = zephyr_ll_task_alloc(); + + schedule_task_init_ll(task, SOF_UUID(ipc_uuid), SOF_SCHEDULE_LL_TIMER, + 0, NULL, NULL, cpu_get_id(), 0); + ipc_user->audio_thread = scheduler_init_context(task); + + /* Grant ipc_user thread permission on the audio thread object. + * Needed so user-space dai_common_new() can call + * k_thread_access_grant(audio_thread, dai_mutex) from user context. + */ + k_thread_access_grant(ipc_user->thread, ipc_user->audio_thread); + + /* Wait for user thread startup — consumes the initial k_sem_give from thread */ + k_sem_take(ipc->ipc_user_pdata->sem, K_FOREVER); +} +#else +static void ipc_user_init(void) +{ +} +#endif /* CONFIG_SOF_USERSPACE_LL */ + __cold int ipc_init(struct sof *sof) { + struct k_heap *heap; + struct ipc *ipc; + assert_can_be_cold(); tr_dbg(&ipc_tr, "entry"); +#ifdef CONFIG_SOF_USERSPACE_LL + heap = zephyr_ll_user_heap(); + + ipc = ipc_get(); + memset(ipc, 0, sizeof(*ipc)); + ipc->ll_alloc = sof_heap_alloc(heap, SOF_MEM_FLAG_USER | SOF_MEM_FLAG_COHERENT, + sizeof(*ipc->ll_alloc), 0); + if (!ipc->ll_alloc) { + tr_err(&ipc_tr, "Unable to allocate IPC ll_alloc"); + sof_panic(SOF_IPC_PANIC_IPC); + } + ipc->ll_alloc->heap = heap; + ipc->ll_alloc->vreg = NULL; +#else + heap = NULL; + /* init ipc data */ - sof->ipc = rzalloc(SOF_MEM_FLAG_USER | SOF_MEM_FLAG_COHERENT, sizeof(*sof->ipc)); - if (!sof->ipc) { + ipc = rzalloc(SOF_MEM_FLAG_USER | SOF_MEM_FLAG_COHERENT, sizeof(*ipc)); + if (!ipc) { tr_err(&ipc_tr, "Unable to allocate IPC data"); return -ENOMEM; } - sof->ipc->comp_data = rzalloc(SOF_MEM_FLAG_USER | SOF_MEM_FLAG_COHERENT, - SOF_IPC_MSG_MAX_SIZE); - if (!sof->ipc->comp_data) { + sof->ipc = ipc; +#endif + + ipc->comp_data = sof_heap_alloc(heap, SOF_MEM_FLAG_USER | SOF_MEM_FLAG_COHERENT, + SOF_IPC_MSG_MAX_SIZE, 0); + if (!ipc->comp_data) { tr_err(&ipc_tr, "Unable to allocate IPC component data"); - rfree(sof->ipc); - return -ENOMEM; + sof_panic(SOF_IPC_PANIC_IPC); } + memset(ipc->comp_data, 0, SOF_IPC_MSG_MAX_SIZE); - k_spinlock_init(&sof->ipc->lock); - list_init(&sof->ipc->msg_list); - list_init(&sof->ipc->comp_list); + k_spinlock_init(&ipc->lock); + list_init(&ipc->msg_list); + list_init(&ipc->comp_list); #ifdef CONFIG_SOF_TELEMETRY_IO_PERFORMANCE_MEASUREMENTS struct io_perf_data_item init_data = {IO_PERF_IPC_ID, @@ -317,28 +621,40 @@ __cold int ipc_init(struct sof *sof) IO_PERF_POWERED_UP_ENABLED, IO_PERF_D0IX_POWER_MODE, 0, 0, 0 }; - io_perf_monitor_init_data(&sof->ipc->io_perf_in_msg_count, &init_data); + io_perf_monitor_init_data(&ipc->io_perf_in_msg_count, &init_data); init_data.direction = IO_PERF_OUTPUT_DIRECTION; - io_perf_monitor_init_data(&sof->ipc->io_perf_out_msg_count, &init_data); + io_perf_monitor_init_data(&ipc->io_perf_out_msg_count, &init_data); +#endif + +#if CONFIG_SOF_BOOT_TEST_STANDALONE + LOG_INF("SOF_BOOT_TEST_STANDALONE, disabling IPC."); + return 0; #endif #ifdef __ZEPHYR__ - struct k_thread *thread = &sof->ipc->ipc_send_wq.thread; + k_tid_t thread; - k_work_queue_start(&sof->ipc->ipc_send_wq, ipc_send_wq_stack, + k_work_queue_init(&ipc->ipc_send_wq); + k_work_queue_start(&ipc->ipc_send_wq, ipc_send_wq_stack, K_THREAD_STACK_SIZEOF(ipc_send_wq_stack), 1, NULL); + thread = k_work_queue_thread_get(&ipc->ipc_send_wq); + 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); - k_work_init_delayable(&sof->ipc->z_delayed_work, ipc_work_handler); + k_work_init_delayable(&ipc->z_delayed_work, ipc_work_handler); #endif - return platform_ipc_init(sof->ipc); + ipc_user_init(); + + return platform_ipc_init(ipc); } /* Locking: call with ipc->lock held and interrupts disabled */ diff --git a/src/ipc/ipc-helper.c b/src/ipc/ipc-helper.c index 0b34915ea26f..4c46d104856f 100644 --- a/src/ipc/ipc-helper.c +++ b/src/ipc/ipc-helper.c @@ -17,6 +17,7 @@ #include <sof/ipc/msg.h> #include <sof/ipc/driver.h> #include <sof/ipc/schedule.h> +#include <sof/schedule/ll_schedule_domain.h> #include <rtos/alloc.h> #include <rtos/cache.h> #include <sof/lib/cpu.h> @@ -50,7 +51,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; @@ -66,6 +68,7 @@ __cold struct comp_buffer *buffer_new(const struct sof_ipc_buffer *desc, bool is return NULL; } +#if CONFIG_IPC_MAJOR_3 /* memory zones and caps are deprecated - convert to flags */ if (desc->caps & SOF_MEM_CAPS_DMA) flags |= SOF_MEM_FLAG_DMA; @@ -76,17 +79,19 @@ __cold struct comp_buffer *buffer_new(const struct sof_ipc_buffer *desc, bool is if (desc->caps) tr_warn(&buffer_tr, "Deprecated buffer caps 0x%x used, convert to flags 0x%x", desc->caps, flags); +#endif /* allocate buffer */ - buffer = buffer_alloc(desc->size, flags, PLATFORM_DCACHE_ALIGN, - is_shared); + buffer = buffer_alloc(alloc, desc->size, flags, PLATFORM_DCACHE_ALIGN, is_shared); if (buffer) { buffer->stream.runtime_stream_params.id = desc->comp.id; buffer->stream.runtime_stream_params.pipeline_id = desc->comp.pipeline_id; buffer->core = desc->comp.core; +#if !defined(CONFIG_SOF_USERSPACE_LL) memcpy_s(&buffer->tctx, sizeof(struct tr_ctx), &buffer_tr, sizeof(struct tr_ctx)); +#endif } return buffer; @@ -182,14 +187,12 @@ int comp_verify_params(struct comp_dev *dev, uint32_t flag, if (dir == PPL_DIR_DOWNSTREAM) { comp_dev_for_each_consumer(dev, buf) { comp_update_params(flag, params, buf); - buffer_set_params(buf, params, - BUFFER_UPDATE_FORCE); + buffer_set_params(buf, params, BUFFER_UPDATE_FORCE); } } else { comp_dev_for_each_producer(dev, buf) { comp_update_params(flag, params, buf); - buffer_set_params(buf, params, - BUFFER_UPDATE_FORCE); + buffer_set_params(buf, params, BUFFER_UPDATE_FORCE); } } @@ -234,7 +237,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 +251,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; } @@ -278,11 +281,9 @@ int ipc_pipeline_complete(struct ipc *ipc, uint32_t comp_id) pipeline_id = ipc_pipe->pipeline->pipeline_id; - tr_dbg(&ipc_tr, "ipc: pipe %d -> complete on comp 0x%x", pipeline_id, - comp_id); + tr_dbg(&ipc_tr, "ipc: pipe %d -> complete on comp 0x%x", pipeline_id, comp_id); - return pipeline_complete(ipc_pipe->pipeline, ipc_ppl_source->cd, - ipc_ppl_sink->cd); + return pipeline_complete(ipc_pipe->pipeline, ipc_ppl_source->cd, ipc_ppl_sink->cd); } __cold int ipc_comp_free(struct ipc *ipc, uint32_t comp_id) @@ -290,15 +291,17 @@ __cold int ipc_comp_free(struct ipc *ipc, uint32_t comp_id) struct ipc_comp_dev *icd; struct comp_buffer *buffer; struct comp_buffer *safe; + struct list_item *clist; +#ifndef CONFIG_SOF_USERSPACE_LL uint32_t flags; +#endif assert_can_be_cold(); /* check whether component exists */ icd = ipc_get_comp_by_id(ipc, comp_id); if (!icd) { - tr_err(&ipc_tr, "comp id: 0x%x is not found", - comp_id); + tr_err(&ipc_tr, "comp id: 0x%x is not found", comp_id); return -ENODEV; } @@ -327,12 +330,19 @@ __cold int ipc_comp_free(struct ipc *ipc, uint32_t comp_id) * leak on error. Bug-free host drivers won't do * this, this was found via fuzzing. */ - tr_err(&ipc_tr, "uninitialized buffer lists on comp 0x%x\n", - icd->id); + tr_err(&ipc_tr, "uninitialized buffer lists on comp 0x%x\n", icd->id); return -EINVAL; } + /* Lock buffer lists to prevent racing with the LL scheduler. + * In user-space builds, use the LL scheduler's lock + * (re-entrant, so safe if caller already holds it). + */ +#ifdef CONFIG_SOF_USERSPACE_LL + user_ll_lock_sched(icd->core); +#else irq_local_disable(flags); +#endif comp_dev_for_each_producer_safe(icd->cd, buffer, safe) { comp_buffer_set_sink_component(buffer, NULL); /* This breaks the list, but we anyway delete all buffers */ @@ -345,7 +355,29 @@ __cold int ipc_comp_free(struct ipc *ipc, uint32_t comp_id) comp_buffer_reset_source_list(buffer); } +#ifdef CONFIG_SOF_USERSPACE_LL + user_ll_unlock_sched(icd->core); +#else irq_local_enable(flags); +#endif + + /* + * A completed pipeline stores raw comp_dev pointers in its + * source_comp/sink_comp/sched_comp fields. + */ + list_for_item(clist, &ipc->comp_list) { + struct ipc_comp_dev *ppl_icd = container_of(clist, struct ipc_comp_dev, list); + + if (ppl_icd->type != COMP_TYPE_PIPELINE) + continue; + + if (ppl_icd->pipeline->source_comp == icd->cd) + ppl_icd->pipeline->source_comp = NULL; + if (ppl_icd->pipeline->sink_comp == icd->cd) + ppl_icd->pipeline->sink_comp = NULL; + if (ppl_icd->pipeline->sched_comp == icd->cd) + ppl_icd->pipeline->sched_comp = NULL; + } /* free component and remove from list */ comp_free(icd->cd); @@ -353,7 +385,7 @@ __cold int ipc_comp_free(struct ipc *ipc, uint32_t comp_id) icd->cd = NULL; list_item_del(&icd->list); - rfree(icd); + sof_heap_free(sof_sys_user_heap_get(), icd); return 0; } diff --git a/src/ipc/ipc-zephyr.c b/src/ipc/ipc-zephyr.c index 460089414cf7..5661c72bb269 100644 --- a/src/ipc/ipc-zephyr.c +++ b/src/ipc/ipc-zephyr.c @@ -9,11 +9,17 @@ // Andrey Borisovich <andrey.borisovich@intel.com> // Adrian Warecki <adrian.warecki@intel.com> -#include <autoconf.h> +#include <zephyr/autoconf.h> #include <zephyr/kernel.h> - +#include <zephyr/ipc/ipc_service.h> +#ifdef CONFIG_IPC_SERVICE_BACKEND_INTEL_ADSP_HOST_IPC +#include <zephyr/ipc/backends/intel_adsp_host_ipc.h> +#endif +#ifdef CONFIG_INTEL_ADSP_IPC_OLD_INTERFACE #include <intel_adsp_ipc.h> +#endif + #include <sof/ipc/common.h> #include <sof/ipc/schedule.h> @@ -58,25 +64,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,11 +97,9 @@ 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 +#if defined(CONFIG_PM_DEVICE) && defined(CONFIG_IPC_SERVICE_BACKEND_INTEL_ADSP_HOST_IPC) /** * @brief IPC device suspend handler callback function. * Checks whether device power state should be actually changed. @@ -159,9 +170,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), @@ -173,7 +181,7 @@ static int ipc_device_resume_handler(const struct device *dev, void *arg) #endif return 0; } -#endif /* CONFIG_PM_DEVICE */ +#endif /* CONFIG_PM_DEVICE && CONFIG_IPC_SERVICE_BACKEND_INTEL_ADSP_HOST_IPC */ #if CONFIG_DEBUG_IPC_COUNTERS @@ -254,7 +262,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 +274,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 +315,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_DEVICE) && defined(CONFIG_IPC_SERVICE_BACKEND_INTEL_ADSP_HOST_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 +340,7 @@ int platform_ipc_init(struct ipc *ipc) return 0; } +#ifdef CONFIG_INTEL_ADSP_IPC_OLD_INTERFACE static bool ipc_wait_complete(const struct device *dev, void *arg) { k_sem_give(arg); @@ -331,3 +360,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_OLD_INTERFACE */ 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..af06e21955cd 100644 --- a/src/ipc/ipc3/dai.c +++ b/src/ipc/ipc3/dai.c @@ -29,6 +29,8 @@ LOG_MODULE_DECLARE(ipc, CONFIG_SOF_LOG_LEVEL); +#define DAI_INDEX_INVALID 0xFFFF + void dai_set_link_hda_config(uint16_t *link_config, struct ipc_config_dai *common_config, const void *spec_config) @@ -93,10 +95,20 @@ 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_TDM: + case SOF_DAI_AMD_SDW: { + struct dai_config *params = (struct dai_config *)dd->dai->dev->config; + + params->dai_index = dd->dai->index; 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, dd->stream_id); @@ -173,23 +185,87 @@ int ipc_dai_data_config(struct dai_data *dd, struct comp_dev *dev) case SOF_DAI_IMX_ESAI: dd->config.burst_elems = dai_get_fifo_depth(dd->dai, dai->direction); break; - case SOF_DAI_AMD_BT: - dev->ipc_config.frame_fmt = SOF_IPC_FRAME_S16_LE; - break; - case SOF_DAI_AMD_SP: - case SOF_DAI_AMD_SP_VIRTUAL: - dev->ipc_config.frame_fmt = SOF_IPC_FRAME_S16_LE; - break; case SOF_DAI_AMD_DMIC: dev->ipc_config.frame_fmt = SOF_IPC_FRAME_S32_LE; if (dd->dma_buffer) audio_stream_set_frm_fmt(&dd->dma_buffer->stream, dev->ipc_config.frame_fmt); break; + case SOF_DAI_AMD_BT: + case SOF_DAI_AMD_SP: + case SOF_DAI_AMD_SP_VIRTUAL: 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_TDM: +#if defined(CONFIG_AMD) && !defined(CONFIG_SOC_ACP_6_0) + { + struct acp_dma_dev_data *tdm_data = dd->dma->z_dev->data; + struct tdm_context *tdm_ctx; + + /* Allocate coherent memory for TDM context shared between + * IPC and DMA contexts. + */ + if (!tdm_data->dai_index_ptr) { + tdm_ctx = rzalloc(SOF_MEM_FLAG_USER | SOF_MEM_FLAG_COHERENT, + sizeof(*tdm_ctx)); + if (!tdm_ctx) + return -ENOMEM; + tdm_data->dai_index_ptr = tdm_ctx; + } else { + tdm_ctx = (struct tdm_context *)tdm_data->dai_index_ptr; + } + tdm_ctx->index = dd->dai->index; + tdm_ctx->frame_fmt = dev->ipc_config.frame_fmt; + /* AMD HW needs 24-bit data MSB-aligned in 32-bit word */ + if (dev->ipc_config.frame_fmt == SOF_IPC_FRAME_S24_4LE) { + dev->ipc_config.frame_fmt = SOF_IPC_FRAME_S24_4LE_MSB; + if (dd->dma_buffer) { + audio_stream_set_frm_fmt(&dd->dma_buffer->stream, + dev->ipc_config.frame_fmt); + } + } + } +#endif + break; + case SOF_DAI_AMD_SDW: +#if defined(CONFIG_AMD) && !defined(CONFIG_SOC_ACP_6_0) + { + /* AMD SDW/HS HW needs 24-bit data MSB-aligned in 32-bit word */ + if (dev->ipc_config.frame_fmt == SOF_IPC_FRAME_S24_4LE) + dev->ipc_config.frame_fmt = SOF_IPC_FRAME_S24_4LE_MSB; + if (dd->dma_buffer) + audio_stream_set_frm_fmt(&dd->dma_buffer->stream, + dev->ipc_config.frame_fmt); + + 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 : DAI_INDEX_INVALID; +#else + pin_data->dma_channel = dd->chan ? dd->chan->index : DAI_INDEX_INVALID; +#endif +#if defined(CONFIG_SOC_ACP_7_X) + for (int i = 0; i < SDW_INSTANCES; i++) { + pin_data->index[i] = DAI_INDEX_INVALID; + } +#else + pin_data->index = DAI_INDEX_INVALID; +#endif + pin_data->instance = DAI_INDEX_INVALID; + dev_data->dai_index_ptr = pin_data; + } +#endif break; case SOF_DAI_MEDIATEK_AFE: break; @@ -215,7 +291,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 */ @@ -285,18 +361,36 @@ void dai_dma_release(struct dai_data *dd, struct comp_dev *dev) return; } +#if defined(CONFIG_AMD) + /* Free DAI-specific data allocated in ipc_dai_data_config() */ + if (dd->dma && dd->dma->z_dev && dd->dma->z_dev->data) { + struct acp_dma_dev_data *dev_data = dd->dma->z_dev->data; + + if (dev_data->dai_index_ptr) { + rfree(dev_data->dai_index_ptr); + dev_data->dai_index_ptr = NULL; + } + } +#endif + /* 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 +404,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 +421,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 +459,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 +488,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..7fa66228942f 100644 --- a/src/ipc/ipc3/helper.c +++ b/src/ipc/ipc3/helper.c @@ -81,7 +81,6 @@ static const struct comp_driver *get_drv(struct sof_ipc_comp *comp) struct comp_driver_info *info; struct sof_ipc_comp_ext *comp_ext; uintptr_t offset; - k_spinlock_key_t key; /* do we have extended data ? */ if (!comp->ext_data_length) { @@ -127,9 +126,10 @@ static const struct comp_driver *get_drv(struct sof_ipc_comp *comp) goto out; } - /* search driver list with UUID */ - key = k_spin_lock(&drivers->lock); - + /* + * search driver list with UUID; no locking needed as the driver + * list is only modified at boot and from the serialized IPC thread + */ list_for_item(clist, &drivers->list) { info = container_of(clist, struct comp_driver_info, list); @@ -148,8 +148,6 @@ static const struct comp_driver *get_drv(struct sof_ipc_comp *comp) *(uint32_t *)(&comp_ext->uuid[8]), *(uint32_t *)(&comp_ext->uuid[12])); - k_spin_unlock(&drivers->lock, key); - out: if (drv) tr_dbg(&comp_tr, "get_drv(), found driver type %d, uuid %pU", @@ -302,7 +300,11 @@ static int comp_specific_builder(struct sof_ipc_comp *comp, if (IPC_TAIL_IS_SIZE_INVALID(*proc)) return -EBADMSG; - if (proc->comp.hdr.size + proc->size > SOF_IPC_MSG_MAX_SIZE) + /* compare without adding the two host-supplied uint32_t values, + * which could wrap and let an oversized proc->size pass + */ + if (proc->comp.hdr.size > SOF_IPC_MSG_MAX_SIZE || + proc->size > SOF_IPC_MSG_MAX_SIZE - proc->comp.hdr.size) return -EBADMSG; config->process.type = proc->type; @@ -395,8 +397,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; @@ -439,6 +441,8 @@ int ipc_pipeline_new(struct ipc *ipc, ipc_pipe_new *_pipe_desc) int ipc_pipeline_free(struct ipc *ipc, uint32_t comp_id) { struct ipc_comp_dev *ipc_pipe; + struct ipc_comp_dev *icd; + struct list_item *clist; int ret; /* check whether pipeline exists */ @@ -457,6 +461,19 @@ int ipc_pipeline_free(struct ipc *ipc, uint32_t comp_id) if (!cpu_is_me(ipc_pipe->core)) return ipc_process_on_core(ipc_pipe->core, false); + /* Clear stale cd->pipeline pointers on all components and buffers + * that still belong to this pipeline. A well-behaved host driver + * frees components before freeing the pipeline, but if it does not + * (or in fuzz/error paths) the dangling pointer would be a + * use-after-free on any subsequent IPC referencing that component. + */ + list_for_item(clist, &ipc->comp_list) { + icd = container_of(clist, struct ipc_comp_dev, list); + if (icd->type == COMP_TYPE_COMPONENT && + icd->cd && icd->cd->pipeline == ipc_pipe->pipeline) + icd->cd->pipeline = NULL; + } + /* free buffer and remove from list */ ret = pipeline_free(ipc_pipe->pipeline); if (ret < 0) { @@ -485,7 +502,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; @@ -710,7 +727,7 @@ int ipc_comp_new(struct ipc *ipc, ipc_comp *_comp) return 0; } -void ipc_msg_reply(struct sof_ipc_reply *reply) +void z_impl_ipc_msg_reply(struct sof_ipc_reply *reply) { struct ipc *ipc = ipc_get(); k_spinlock_key_t key; diff --git a/src/ipc/ipc3/host-page-table.c b/src/ipc/ipc3/host-page-table.c index 7a3da31caa82..dbd0fd0fe030 100644 --- a/src/ipc/ipc3/host-page-table.c +++ b/src/ipc/ipc3/host-page-table.c @@ -16,6 +16,25 @@ LOG_MODULE_DECLARE(ipc, CONFIG_SOF_LOG_LEVEL); +/* + * Size in bytes of the DSP-side buffer that receives the compressed host page + * table. DSP targets define PLATFORM_PAGE_TABLE_SIZE; the host/library builds + * (e.g. testbench, fuzzer) allocate it as HOST_PAGE_SIZE in drivers/host/ipc.c + * and do not define PLATFORM_PAGE_TABLE_SIZE, so fall back to that. + */ +#ifndef PLATFORM_PAGE_TABLE_SIZE +#define PLATFORM_PAGE_TABLE_SIZE HOST_PAGE_SIZE +#endif + +/* + * The compressed page table stores one 20-bit entry per host page, so the + * number of pages whose entries fit in the page table buffer is + * (buffer bytes * 8 bits/byte) / 20 bits/page. + */ +#define HOST_PAGE_TABLE_BITS_PER_PAGE 20 +#define HOST_PAGE_TABLE_MAX_PAGES \ + (PLATFORM_PAGE_TABLE_SIZE * 8 / HOST_PAGE_TABLE_BITS_PER_PAGE) + /* * Parse the host page tables and create the audio DMA SG configuration * for host audio DMA buffer. This involves creating a dma_sg_elem for each @@ -98,7 +117,8 @@ static int ipc_get_page_descriptors(struct sof_dma *dmac, uint8_t *page_table, * reload() function which may not be the case for all * vendors. */ - if (!IS_ENABLED(CONFIG_DMA_NXP_SOF_HOST_DMA)) { + if (!IS_ENABLED(CONFIG_DMA_NXP_SOF_HOST_DMA) && + !IS_ENABLED(CONFIG_DMA_MTK_SOF_HOST_DMA)) { tr_err(&ipc_tr, "DMAC not supported for page transfer"); return -ENOTSUP; } @@ -215,6 +235,19 @@ int ipc_process_host_buffer(struct ipc *ipc, struct ipc_data_host_buffer *data_host_buffer; int err; + /* + * The host-supplied page count is used both to size DSP-side + * allocations and to compute the DMA transfer length for the + * compressed page table. Reject a count that would not fit in the + * page table buffer before doing any arithmetic that could overflow + * (ring->pages * 20). pages == 0 is also invalid and would underflow + * the ring->size sanity check in ipc_parse_page_descriptors(). + */ + if (ring->pages == 0 || ring->pages > HOST_PAGE_TABLE_MAX_PAGES) { + tr_err(&ipc_tr, "ipc: invalid page count %u", ring->pages); + return -EINVAL; + } + data_host_buffer = ipc_platform_get_host_buffer(ipc); dma_sg_init(elem_array); @@ -239,6 +272,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..c0ed1f52c3a1 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,14 @@ LOG_MODULE_DECLARE(ipc, CONFIG_SOF_LOG_LEVEL); +static APP_SYSUSER_BSS 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 +52,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; + /* UAOLxPCMSyCM has no direction bit, the stream selects it */ + 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 +100,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 +121,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 +142,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 +171,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 +182,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 +206,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 +215,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 +235,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); + sof_dma_stop(dd->dma, 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; + sof_dma_release_channel(dd->dma, 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 +254,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 +268,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 +282,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 +296,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 +305,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 +354,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 +363,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; } @@ -378,15 +386,17 @@ __cold int dai_config(struct dai_data *dd, struct comp_dev *dev, /* allocated dai_config if not yet */ if (!dd->dai_spec_config) { size = sizeof(*copier_cfg); - dd->dai_spec_config = rzalloc(SOF_MEM_FLAG_USER, size); + dd->dai_spec_config = sof_heap_alloc(dd->alloc_ctx.heap, SOF_MEM_FLAG_USER, size, 0); 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; } + memset(dd->dai_spec_config, 0, size); + ret = memcpy_s(dd->dai_spec_config, size, copier_cfg, size); if (ret < 0) { - rfree(dd->dai_spec_config); + sof_heap_free(dd->alloc_ctx.heap, dd->dai_spec_config); dd->dai_spec_config = NULL; return -EINVAL; } @@ -396,10 +406,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 +425,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 = sof_dma_get_status(dd->dma, dd->chan_index, &status); if (ret < 0) return ret; @@ -437,7 +450,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 = sof_dma_get_status(dd->dma, dd->chan_index, &status); if (ret < 0) return; @@ -451,51 +464,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..03c2e6c81e86 --- /dev/null +++ b/src/ipc/ipc4/handler-kernel.c @@ -0,0 +1,724 @@ +// 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/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> + +#ifdef __ZEPHYR__ +#include <zephyr/kernel.h> +#include <zephyr/internal/syscall_handler.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 z_impl_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); +} + +#ifdef CONFIG_USERSPACE +/** + * \brief Userspace verification wrapper for ipc_compound_pre_start(). + * + * Forwards the call to z_impl_ipc_compound_pre_start(). No pointer + * validation is needed as only primitive types are passed. + * + * @param[in] msg_id IPC message ID. + */ +void z_vrfy_ipc_compound_pre_start(int msg_id) +{ + z_impl_ipc_compound_pre_start(msg_id); +} +#include <zephyr/syscalls/ipc_compound_pre_start_mrsh.c> +#endif + +void z_impl_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); +} + +#ifdef CONFIG_USERSPACE +/** + * \brief Userspace verification wrapper for ipc_compound_post_start(). + * + * Forwards the call to z_impl_ipc_compound_post_start(). No pointer + * validation is needed as only primitive types are passed. + * + * @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 z_vrfy_ipc_compound_post_start(uint32_t msg_id, int ret, bool delayed) +{ + z_impl_ipc_compound_post_start(msg_id, ret, delayed); +} +#include <zephyr/syscalls/ipc_compound_post_start_mrsh.c> +#endif + +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 z_impl_ipc_wait_for_compound_msg(void) +{ + atomic_set(&msg_data.delayed_reply, 0); + return IPC4_SUCCESS; +} +#else +int z_impl_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; +} + +#ifdef CONFIG_USERSPACE +/** + * \brief Userspace verification wrapper for ipc_wait_for_compound_msg(). + * + * Forwards the call to z_impl_ipc_wait_for_compound_msg(). No pointer + * validation is needed as no pointers are passed. + * + * @return IPC4_SUCCESS on success, IPC4_FAILURE on timeout. + */ +int z_vrfy_ipc_wait_for_compound_msg(void) +{ + return z_impl_ipc_wait_for_compound_msg(); +} +#include <zephyr/syscalls/ipc_wait_for_compound_msg_mrsh.c> +#endif +#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_IPC_SERVICE_BACKEND_INTEL_ADSP_HOST_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_IPC_SERVICE_BACKEND_INTEL_ADSP_HOST_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 z_impl_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); +} + +#ifdef CONFIG_USERSPACE +void z_vrfy_ipc_msg_reply(struct sof_ipc_reply *reply) +{ + K_OOPS(K_SYSCALL_MEMORY_READ(reply, sizeof(*reply))); + z_impl_ipc_msg_reply(reply); +} +#include <zephyr/syscalls/ipc_msg_reply_mrsh.c> +#endif + +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 58% rename from src/ipc/ipc4/handler.c rename to src/ipc/ipc4/handler-user.c index 3eea8f623081..fbb68d809bd2 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) { @@ -133,6 +90,7 @@ static inline const struct ipc4_pipeline_set_state_data *ipc4_get_pipeline_data( /* * Global IPC Operations. */ +#ifndef CONFIG_SOF_USERSPACE_LL __cold static int ipc4_new_pipeline(struct ipc4_message_request *ipc4) { struct ipc *ipc = ipc_get(); @@ -141,7 +99,9 @@ __cold static int ipc4_new_pipeline(struct ipc4_message_request *ipc4) return ipc_pipeline_new(ipc, (ipc_pipe_new *)ipc4); } +#endif +#ifndef CONFIG_SOF_USERSPACE_LL __cold static int ipc4_delete_pipeline(struct ipc4_message_request *ipc4) { struct ipc4_pipeline_delete *pipe; @@ -154,6 +114,7 @@ __cold static int ipc4_delete_pipeline(struct ipc4_message_request *ipc4) return ipc_pipeline_free(ipc, pipe->primary.r.instance_id); } +#endif static int ipc4_pcm_params(struct ipc_comp_dev *pcm_dev) { @@ -186,31 +147,17 @@ 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; struct ipc *ipc = ipc_get(); int host_id; + if (!ppl_icd->pipeline->source_comp || !ppl_icd->pipeline->sink_comp) { + ipc_cmd_err(&ipc_tr, "pipeline %d: source/sink comp freed", ppl_icd->id); + return NULL; + } + /* If the source component's direction is not set but the sink's direction is, * this block will copy the direction from the sink to the source component and * mark the source's direction as set. @@ -474,107 +421,79 @@ 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) +__cold const struct ipc4_pipeline_set_state_data *ipc4_get_pipeline_data_wrapper(void) { - 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; - } + assert_can_be_cold(); - /* decrease counter if it is not scheduled by another thread */ - if (!delayed) - atomic_sub(&msg_data.delayed_reply, 1); + return ipc4_get_pipeline_data(); } -static void ipc_compound_msg_done(uint32_t msg_id, int error) +static int ipc4_pipeline_id_get(struct ipc4_message_request *ipc4, + struct ipc4_pipeline_set_state *state, + const uint32_t **ppl_id, unsigned int *ppl_count) { - if (!atomic_read(&msg_data.delayed_reply)) { - ipc_cmd_err(&ipc_tr, "unexpected delayed reply"); - return; + if (!state->extension.r.multi_ppl) { + if (ppl_count) + *ppl_count = 1; + if (ppl_id) + *ppl_id = NULL; + return state->primary.r.ppl_id; } - atomic_sub(&msg_data.delayed_reply, 1); + const struct ipc4_pipeline_set_state_data *ppl_data = ipc4_get_pipeline_data(); + unsigned int cnt = ppl_data->pipelines_count; - /* 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; + /* + * pipelines_count is read straight from the host-provided + * mailbox payload, so cap it at what the mailbox can + * physically hold. Anything larger means the host promised + * more ppl_id[] entries than fit in MAILBOX_HOSTBOX, and + * dereferencing the flex array would read out of bounds. + */ + if (cnt > (MAILBOX_HOSTBOX_SIZE - sizeof(struct ipc4_pipeline_set_state_data)) / + sizeof(uint32_t)) { + ipc_cmd_err(&ipc_tr, "ipc: pipelines_count %u exceeds mailbox bound", + cnt); + return -EINVAL; } + dcache_invalidate_region((__sparse_force void __sparse_cache *)ppl_data->ppl_id, + sizeof(int) * cnt); + if (ppl_count) + *ppl_count = cnt; + if (ppl_id) + *ppl_id = ppl_data->ppl_id; + + return ppl_data->ppl_id[0]; } -#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. +/** + * \brief Process SET_PIPELINE_STATE IPC4 message (prepare + trigger phases). + * @param[in] ipc4 IPC4 message request. + * @return 0 on success, IPC4 error code otherwise. */ -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 ipc4_set_pipeline_state(struct ipc4_message_request *ipc4) { - 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(); - - return ipc4_get_pipeline_data(); -} - -/* Entry point for ipc4_pipeline_trigger(), therefore cannot be cold */ -static int ipc4_set_pipeline_state(struct ipc4_message_request *ipc4) -{ - const struct ipc4_pipeline_set_state_data *ppl_data; struct ipc4_pipeline_set_state state; struct ipc_comp_dev *ppl_icd; struct ipc *ipc = ipc_get(); uint32_t cmd, ppl_count; - uint32_t id = 0; + unsigned int id; const uint32_t *ppl_id; bool use_idc = false; uint32_t idx; - int ret = 0; + int ret; int i; state.primary.dat = ipc4->primary.dat; state.extension.dat = ipc4->extension.dat; cmd = state.primary.r.ppl_state; - ppl_data = ipc4_get_pipeline_data(); - if (state.extension.r.multi_ppl) { - ppl_count = ppl_data->pipelines_count; - ppl_id = ppl_data->ppl_id; - dcache_invalidate_region((__sparse_force void __sparse_cache *)ppl_id, - sizeof(int) * ppl_count); - } else { - ppl_count = 1; - id = state.primary.r.ppl_id; + ret = ipc4_pipeline_id_get(ipc4, &state, &ppl_id, &ppl_count); + if (ret < 0) + return IPC4_ERROR_INVALID_PARAM; + + if (ppl_count == 1) { + id = ret; ppl_id = &id; } @@ -656,44 +575,14 @@ static int ipc4_set_pipeline_state(struct ipc4_message_request *ipc4) ipc_compound_pre_start(state.primary.r.type); ret = ipc4_pipeline_trigger(ppl_icd, cmd, &delayed); ipc_compound_post_start(state.primary.r.type, ret, delayed); - if (delayed) { - /* To maintain pipeline order for triggers, we must - * do a blocking wait until trigger is processed. - * This will add a max delay of 'ppl_count' LL ticks - * to process the full trigger list. - */ - if (ipc_wait_for_compound_msg() != 0) { - ipc_cmd_err(&ipc_tr, "ipc4: fail with delayed trigger"); - return IPC4_FAILURE; - } - } } if (ret != 0) return ret; } - 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) { @@ -702,15 +591,11 @@ __cold static int ipc4_process_chain_dma(struct ipc4_message_request *ipc4) #if CONFIG_COMP_CHAIN_DMA struct ipc_comp_dev *cdma_comp; struct ipc *ipc = ipc_get(); - struct ipc4_chain_dma cdma; + const struct ipc4_chain_dma *cdma = (const struct ipc4_chain_dma *)ipc4; int comp_id; int ret; - ret = memcpy_s(&cdma, sizeof(cdma), ipc4, sizeof(*ipc4)); - if (ret < 0) - return IPC4_FAILURE; - - comp_id = IPC4_COMP_ID(cdma.primary.r.host_dma_id + IPC4_MAX_MODULE_COUNT, 0); + comp_id = IPC4_COMP_ID(cdma->primary.r.host_dma_id + IPC4_MAX_MODULE_COUNT, 0); cdma_comp = ipc_get_comp_by_id(ipc, comp_id); if (!cdma_comp) { @@ -718,10 +603,10 @@ __cold static int ipc4_process_chain_dma(struct ipc4_message_request *ipc4) * Nothing to do when the chainDMA is not allocated and asked to * be freed */ - if (!cdma.primary.r.allocate && !cdma.primary.r.enable) + if (!cdma->primary.r.allocate && !cdma->primary.r.enable) return IPC4_SUCCESS; - ret = ipc4_chain_manager_create(&cdma); + ret = ipc4_chain_manager_create(cdma); if (ret < 0) return IPC4_FAILURE; @@ -730,22 +615,21 @@ __cold static int ipc4_process_chain_dma(struct ipc4_message_request *ipc4) return IPC4_FAILURE; } - ret = ipc4_chain_dma_state(cdma_comp->cd, &cdma); + ret = ipc4_chain_dma_state(cdma_comp->cd, cdma); if (ret < 0) { comp_free(cdma_comp->cd); + list_item_del(&cdma_comp->list); + rfree(cdma_comp); return IPC4_FAILURE; } return IPC4_SUCCESS; } - ret = ipc4_chain_dma_state(cdma_comp->cd, &cdma); + ret = ipc4_chain_dma_state(cdma_comp->cd, cdma); if (ret < 0) return IPC4_INVALID_CHAIN_STATE_TRANSITION; - if (!cdma.primary.r.allocate && !cdma.primary.r.enable) - list_item_del(&cdma_comp->list); - return IPC4_SUCCESS; #else return IPC4_UNAVAILABLE; @@ -764,11 +648,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 +672,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: @@ -811,13 +697,25 @@ static int ipc4_process_glb_message(struct ipc4_message_request *ipc4) /* pipeline settings */ case SOF_IPC4_GLB_CREATE_PIPELINE: +#ifdef CONFIG_SOF_USERSPACE_LL + ret = ipc_user_forward_cmd(ipc4->primary.dat, ipc4->extension.dat); +#else ret = ipc4_new_pipeline(ipc4); +#endif break; case SOF_IPC4_GLB_DELETE_PIPELINE: +#ifdef CONFIG_SOF_USERSPACE_LL + ret = ipc_user_forward_cmd(ipc4->primary.dat, ipc4->extension.dat); +#else ret = ipc4_delete_pipeline(ipc4); +#endif break; case SOF_IPC4_GLB_SET_PIPELINE_STATE: +#ifdef CONFIG_SOF_USERSPACE_LL + ret = ipc_user_forward_cmd(ipc4->primary.dat, ipc4->extension.dat); +#else ret = ipc4_set_pipeline_state(ipc4); +#endif break; case SOF_IPC4_GLB_GET_PIPELINE_STATE: @@ -828,15 +726,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; @@ -875,76 +764,77 @@ static int ipc4_process_glb_message(struct ipc4_message_request *ipc4) __cold static int ipc4_init_module_instance(struct ipc4_message_request *ipc4) { - struct ipc4_module_init_instance module_init; + const struct ipc4_module_init_instance *module_init = + (const struct ipc4_module_init_instance *)ipc4; struct comp_dev *dev; assert_can_be_cold(); - /* we only need the common header here, all we have from the IPC */ - int ret = memcpy_s(&module_init, sizeof(module_init), ipc4, sizeof(*ipc4)); - - if (ret < 0) - return IPC4_FAILURE; - - tr_dbg(&ipc_tr, - "ipc4_init_module_instance %x : %x", - (uint32_t)module_init.primary.r.module_id, - (uint32_t)module_init.primary.r.instance_id); + tr_dbg(&ipc_tr, "%x : %x", + (uint32_t)module_init->primary.r.module_id, + (uint32_t)module_init->primary.r.instance_id); /* Pass IPC to target core */ - if (!cpu_is_me(module_init.extension.r.core_id)) - return ipc4_process_on_core(module_init.extension.r.core_id, false); + if (!cpu_is_me(module_init->extension.r.core_id)) + return ipc4_process_on_core(module_init->extension.r.core_id, false); - dev = comp_new_ipc4(&module_init); + dev = comp_new_ipc4(module_init); if (!dev) { ipc_cmd_err(&ipc_tr, "error: failed to init module %x : %x", - (uint32_t)module_init.primary.r.module_id, - (uint32_t)module_init.primary.r.instance_id); + (uint32_t)module_init->primary.r.module_id, + (uint32_t)module_init->primary.r.instance_id); return IPC4_MOD_NOT_INITIALIZED; } return 0; } +#ifndef CONFIG_SOF_USERSPACE_LL __cold static int ipc4_bind_module_instance(struct ipc4_message_request *ipc4) { - struct ipc4_module_bind_unbind bu; + struct ipc4_module_bind_unbind *bu = (struct ipc4_module_bind_unbind *)ipc4; struct ipc *ipc = ipc_get(); assert_can_be_cold(); - int ret = memcpy_s(&bu, sizeof(bu), ipc4, sizeof(*ipc4)); + 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); - if (ret < 0) - return IPC4_FAILURE; - - tr_dbg(&ipc_tr, "ipc4_bind_module_instance %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); - - return ipc_comp_connect(ipc, (ipc_pipe_comp_connect *)&bu); + return ipc4_comp_connect(ipc, bu); } __cold static int ipc4_unbind_module_instance(struct ipc4_message_request *ipc4) { - struct ipc4_module_bind_unbind bu; + struct ipc4_module_bind_unbind *bu = (struct ipc4_module_bind_unbind *)ipc4; struct ipc *ipc = ipc_get(); assert_can_be_cold(); - int ret = memcpy_s(&bu, sizeof(bu), ipc4, sizeof(*ipc4)); - - if (ret < 0) - return IPC4_FAILURE; - - tr_dbg(&ipc_tr, "ipc4_unbind_module_instance %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); + 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); - return ipc_comp_disconnect(ipc, (ipc_pipe_comp_connect *)&bu); + return ipc4_comp_disconnect(ipc, bu); } +#endif /* !CONFIG_SOF_USERSPACE_LL */ -static int ipc4_set_get_config_module_instance(struct ipc4_message_request *ipc4, bool set) +/** + * @brief Process MOD_CONFIG_GET or MOD_CONFIG_SET in any execution context. + * + * Looks up the target component by module_id:instance_id, verifies the + * driver supports get_attribute/set_attribute, and dispatches the + * operation. For GET, the retrieved value is written to @p reply_ext. + * + * Callable from both the IPC kernel task and the IPC user thread. + * + * @param ipc4 Pointer to the IPC4 message request (primary + extension) + * @param set true for CONFIG_SET, false for CONFIG_GET + * @param reply_ext Output: receives the extension value for CONFIG_GET (may be NULL for SET) + * @return IPC4 status code (0 on success) + */ +__cold int ipc4_process_module_config(struct ipc4_message_request *ipc4, + bool set, uint32_t *reply_ext) { struct ipc4_module_config *config = (struct ipc4_module_config *)ipc4; int (*function)(struct comp_dev *dev, uint32_t type, void *value); @@ -952,7 +842,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); @@ -963,7 +853,7 @@ static int ipc4_set_get_config_module_instance(struct ipc4_message_request *ipc4 comp_id = IPC4_COMP_ID(config->primary.r.module_id, config->primary.r.instance_id); dev = ipc4_get_comp_dev(comp_id); if (!dev) - return IPC4_MOD_INVALID_ID; + return IPC4_INVALID_RESOURCE_ID; drv = dev->drv; @@ -971,11 +861,15 @@ static int ipc4_set_get_config_module_instance(struct ipc4_message_request *ipc4 if (!cpu_is_me(dev->ipc_config.core)) return ipc4_process_on_core(dev->ipc_config.core, false); } else { + /* BaseFW module has only 0th instance */ + if (config->primary.r.instance_id) + return IPC4_INVALID_RESOURCE_ID; + drv = ipc4_get_comp_drv(config->primary.r.module_id); } if (!drv) - return IPC4_MOD_INVALID_ID; + return IPC4_INVALID_RESOURCE_ID; function = set ? drv->ops.set_attribute : drv->ops.get_attribute; if (!function) @@ -983,18 +877,33 @@ 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); ret = IPC4_INVALID_CONFIG_PARAM_ID; } + if (!set && reply_ext) + *reply_ext = config->extension.dat; + + return ret; +} + +#ifndef CONFIG_SOF_USERSPACE_LL +static int ipc4_set_get_config_module_instance(struct ipc4_message_request *ipc4, bool set) +{ + uint32_t reply_ext; + int ret; + + ret = ipc4_process_module_config(ipc4, set, &reply_ext); + if (!set) - msg_reply.extension = config->extension.dat; + msg_reply->extension = reply_ext; return ret; } +#endif /* !CONFIG_SOF_USERSPACE_LL */ __cold static void ipc4_prepare_for_kcontrol_get(struct comp_dev *dev, uint8_t param_id, char *data_out, uint32_t data_size) @@ -1026,7 +935,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, @@ -1121,36 +1030,132 @@ __cold static int ipc4_get_vendor_config_module_instance(struct comp_dev *dev, return IPC4_SUCCESS; } -__cold static int ipc4_get_large_config_module_instance(struct ipc4_message_request *ipc4) +__cold int ipc4_process_large_config_get(struct ipc4_message_request *ipc4, + uint32_t *reply_ext, + uint32_t *reply_tx_size, + void **reply_tx_data) { struct ipc4_module_large_config_reply reply; - struct ipc4_module_large_config config; + const struct ipc4_module_large_config *config = + (const struct ipc4_module_large_config *)ipc4; char *data = ipc_get()->comp_data; const struct comp_driver *drv; struct comp_dev *dev = NULL; uint32_t data_offset; + int ret; assert_can_be_cold(); - int ret = memcpy_s(&config, sizeof(config), ipc4, sizeof(*ipc4)); + 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 + * component dev for basefw + */ + if (config->primary.r.module_id) { + uint32_t comp_id; + comp_id = IPC4_COMP_ID(config->primary.r.module_id, + config->primary.r.instance_id); + dev = ipc4_get_comp_dev(comp_id); + if (!dev) + return IPC4_MOD_INVALID_ID; + + drv = dev->drv; + + /* Multicore disabled for userspace forwarding; + * non-userspace path retains ipc4_process_on_core() + */ + } else { + drv = ipc4_get_comp_drv(config->primary.r.module_id); + } + + if (!drv) + return IPC4_MOD_INVALID_ID; + + if (!drv->ops.get_large_config) + return IPC4_INVALID_REQUEST; + + data_offset = config->extension.r.data_off_size; + + /* check for vendor param first */ + if (config->extension.r.large_param_id == VENDOR_CONFIG_PARAM) { + /* For now only vendor_config case uses payload from hostbox */ + dcache_invalidate_region((__sparse_force void __sparse_cache *)MAILBOX_HOSTBOX_BASE, + config->extension.r.data_off_size); + ret = ipc4_get_vendor_config_module_instance(dev, drv, + config->extension.r.init_block, + config->extension.r.final_block, + &data_offset, + data, + (const char *)MAILBOX_HOSTBOX_BASE); + } else { +#if CONFIG_LIBRARY + data += sizeof(reply); +#endif + ipc4_prepare_for_kcontrol_get(dev, config->extension.r.large_param_id, + data, data_offset); + + ret = drv->ops.get_large_config(dev, config->extension.r.large_param_id, + config->extension.r.init_block, + config->extension.r.final_block, + &data_offset, data); + } + + /* set up ipc4 error code for reply data */ if (ret < 0) - return IPC4_FAILURE; + ret = IPC4_MOD_INVALID_ID; - tr_dbg(&ipc_tr, "ipc4_get_large_config_module_instance %x : %x", - (uint32_t)config.primary.r.module_id, (uint32_t)config.primary.r.instance_id); + /* Copy host config and overwrite */ + reply.extension.dat = config->extension.dat; + reply.extension.r.data_off_size = data_offset; + + /* The last block, no more data */ + if (!config->extension.r.final_block && data_offset < SOF_IPC_MSG_MAX_SIZE) + reply.extension.r.final_block = 1; + + /* Indicate last block if error occurs */ + if (ret) + reply.extension.r.final_block = 1; + + /* no need to allocate memory for reply msg */ + if (ret) + return ret; + + /* Output via parameters instead of msg_reply */ + *reply_ext = reply.extension.dat; + *reply_tx_size = data_offset; + *reply_tx_data = data; + return ret; +} + +__cold static int ipc4_get_large_config_module_instance(struct ipc4_message_request *ipc4) +{ + struct ipc4_module_large_config_reply reply; + const struct ipc4_module_large_config *config = + (const struct ipc4_module_large_config *)ipc4; + char *data = ipc_get()->comp_data; + const struct comp_driver *drv; + struct comp_dev *dev = NULL; + uint32_t data_offset; + int ret; + + assert_can_be_cold(); + + 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 * component dev for basefw */ - if (config.primary.r.module_id) { + if (config->primary.r.module_id) { uint32_t comp_id; - comp_id = IPC4_COMP_ID(config.primary.r.module_id, - config.primary.r.instance_id); + comp_id = IPC4_COMP_ID(config->primary.r.module_id, + config->primary.r.instance_id); dev = ipc4_get_comp_dev(comp_id); if (!dev) - return IPC4_MOD_INVALID_ID; + return IPC4_INVALID_RESOURCE_ID; drv = dev->drv; @@ -1158,51 +1163,62 @@ __cold static int ipc4_get_large_config_module_instance(struct ipc4_message_requ if (!cpu_is_me(dev->ipc_config.core)) return ipc4_process_on_core(dev->ipc_config.core, false); } else { - drv = ipc4_get_comp_drv(config.primary.r.module_id); + /* BaseFW module has only 0th instance */ + if (config->primary.r.instance_id) + return IPC4_INVALID_RESOURCE_ID; + + drv = ipc4_get_comp_drv(config->primary.r.module_id); } if (!drv) - return IPC4_MOD_INVALID_ID; + return IPC4_INVALID_RESOURCE_ID; if (!drv->ops.get_large_config) return IPC4_INVALID_REQUEST; - data_offset = config.extension.r.data_off_size; + data_offset = config->extension.r.data_off_size; /* check for vendor param first */ - if (config.extension.r.large_param_id == VENDOR_CONFIG_PARAM) { + if (config->extension.r.large_param_id == VENDOR_CONFIG_PARAM) { + /* data_off_size is a 20-bit host-controlled field, so it can + * claim far more than the hostbox can physically hold. + */ + if (data_offset > MAILBOX_HOSTBOX_SIZE) { + ipc_cmd_err(&ipc_tr, "data_off_size %u exceeds mailbox bound", + data_offset); + return IPC4_INVALID_CONFIG_DATA_STRUCT; + } /* For now only vendor_config case uses payload from hostbox */ dcache_invalidate_region((__sparse_force void __sparse_cache *)MAILBOX_HOSTBOX_BASE, - config.extension.r.data_off_size); + config->extension.r.data_off_size); ret = ipc4_get_vendor_config_module_instance(dev, drv, - config.extension.r.init_block, - config.extension.r.final_block, - &data_offset, - data, + config->extension.r.init_block, + config->extension.r.final_block, + &data_offset, data, (const char *)MAILBOX_HOSTBOX_BASE); } else { #if CONFIG_LIBRARY data += sizeof(reply); #endif - ipc4_prepare_for_kcontrol_get(dev, config.extension.r.large_param_id, + ipc4_prepare_for_kcontrol_get(dev, config->extension.r.large_param_id, data, data_offset); - ret = drv->ops.get_large_config(dev, config.extension.r.large_param_id, - config.extension.r.init_block, - config.extension.r.final_block, + ret = drv->ops.get_large_config(dev, config->extension.r.large_param_id, + config->extension.r.init_block, + config->extension.r.final_block, &data_offset, data); } /* set up ipc4 error code for reply data */ if (ret < 0) - ret = IPC4_MOD_INVALID_ID; + ret = IPC4_INVALID_RESOURCE_ID; /* Copy host config and overwrite */ - reply.extension.dat = config.extension.dat; + reply.extension.dat = config->extension.dat; reply.extension.r.data_off_size = data_offset; /* The last block, no more data */ - if (!config.extension.r.final_block && data_offset < SOF_IPC_MSG_MAX_SIZE) + if (!config->extension.r.final_block && data_offset < SOF_IPC_MSG_MAX_SIZE) reply.extension.r.final_block = 1; /* Indicate last block if error occurs */ @@ -1213,9 +1229,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; } @@ -1232,15 +1248,21 @@ __cold static int ipc4_set_vendor_config_module_instance(struct comp_dev *dev, assert_can_be_cold(); + /* Validate host-controlled payload size before any use or arithmetic. */ + if (data_off_size > MAILBOX_HOSTBOX_SIZE) { + tr_err(&ipc_tr, "data_off_size greater than mailbox %u > %u", + data_off_size, (uint32_t)MAILBOX_HOSTBOX_SIZE); + return IPC4_INVALID_CONFIG_DATA_STRUCT; + } + if (init_block && data_off_size < sizeof(struct sof_tlv)) { + tr_err(&ipc_tr, "init_block data_off_size too small %u < %zu", + data_off_size, sizeof(struct sof_tlv)); + return IPC4_INVALID_CONFIG_DATA_STRUCT; + } + /* Old FW comment: bursted configs */ if (init_block && final_block) { const struct sof_tlv *tlv = (struct sof_tlv *)data; - /* if there is no payload in this large config set - * (4 bytes type | 4 bytes length=0 | no value) - * we do not handle such case - */ - if (data_off_size < sizeof(struct sof_tlv)) - return IPC4_INVALID_CONFIG_DATA_STRUCT; /* ===Iterate over payload=== * Payload can have multiple sof_tlv structures inside, @@ -1251,10 +1273,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 +1295,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)); } @@ -1284,31 +1317,96 @@ __cold static int ipc4_set_vendor_config_module_instance(struct comp_dev *dev, data_off_size, data); } -__cold static int ipc4_set_large_config_module_instance(struct ipc4_message_request *ipc4) +__cold int ipc4_process_large_config_set(struct ipc4_message_request *ipc4) { - struct ipc4_module_large_config config; + const struct ipc4_module_large_config *config = + (const struct ipc4_module_large_config *)ipc4; struct comp_dev *dev = NULL; const struct comp_driver *drv; + int ret; assert_can_be_cold(); - int ret = memcpy_s(&config, sizeof(config), ipc4, sizeof(*ipc4)); + dcache_invalidate_region((__sparse_force void __sparse_cache *)MAILBOX_HOSTBOX_BASE, + config->extension.r.data_off_size); + tr_dbg(&ipc_tr, "%x : %x", + (uint32_t)config->primary.r.module_id, (uint32_t)config->primary.r.instance_id); - if (ret < 0) - return IPC4_FAILURE; + if (config->primary.r.module_id) { + uint32_t comp_id; + + comp_id = IPC4_COMP_ID(config->primary.r.module_id, config->primary.r.instance_id); + dev = ipc4_get_comp_dev(comp_id); + if (!dev) + return IPC4_MOD_INVALID_ID; + + drv = dev->drv; + + /* Multicore disabled for userspace forwarding; + * non-userspace path retains ipc4_process_on_core() + */ + } else { + drv = ipc4_get_comp_drv(config->primary.r.module_id); + } + + if (!drv) + return IPC4_MOD_INVALID_ID; + + if (!drv->ops.set_large_config) + return IPC4_INVALID_REQUEST; + + /* check for vendor param first */ + if (config->extension.r.large_param_id == VENDOR_CONFIG_PARAM) { + ret = ipc4_set_vendor_config_module_instance(dev, drv, + (uint32_t)config->primary.r.module_id, + (uint32_t)config->primary.r.instance_id, + config->extension.r.init_block, + config->extension.r.final_block, + config->extension.r.data_off_size, + (const char *)MAILBOX_HOSTBOX_BASE); + } else { +#if CONFIG_LIBRARY + struct ipc *ipc = ipc_get(); + const char *data = (const char *)ipc->comp_data + sizeof(*config); +#else + const char *data = (const char *)MAILBOX_HOSTBOX_BASE; +#endif + ret = drv->ops.set_large_config(dev, config->extension.r.large_param_id, + config->extension.r.init_block, config->extension.r.final_block, + config->extension.r.data_off_size, data); + if (ret < 0) { + ipc_cmd_err(&ipc_tr, "failed to set large_config_module_instance %x : %x", + (uint32_t)config->primary.r.module_id, + (uint32_t)config->primary.r.instance_id); + ret = IPC4_INVALID_RESOURCE_ID; + } + } + + return ret; +} + +__cold static int ipc4_set_large_config_module_instance(struct ipc4_message_request *ipc4) +{ + const struct ipc4_module_large_config *config = + (const struct ipc4_module_large_config *)ipc4; + struct comp_dev *dev = NULL; + const struct comp_driver *drv; + int ret; + + assert_can_be_cold(); 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", - (uint32_t)config.primary.r.module_id, (uint32_t)config.primary.r.instance_id); + config->extension.r.data_off_size); + 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) { + if (config->primary.r.module_id) { uint32_t comp_id; - comp_id = IPC4_COMP_ID(config.primary.r.module_id, config.primary.r.instance_id); + comp_id = IPC4_COMP_ID(config->primary.r.module_id, config->primary.r.instance_id); dev = ipc4_get_comp_dev(comp_id); if (!dev) - return IPC4_MOD_INVALID_ID; + return IPC4_INVALID_RESOURCE_ID; drv = dev->drv; @@ -1316,38 +1414,42 @@ __cold static int ipc4_set_large_config_module_instance(struct ipc4_message_requ if (!cpu_is_me(dev->ipc_config.core)) return ipc4_process_on_core(dev->ipc_config.core, false); } else { - drv = ipc4_get_comp_drv(config.primary.r.module_id); + /* BaseFW module has only 0th instance */ + if (config->primary.r.instance_id) + return IPC4_INVALID_RESOURCE_ID; + + drv = ipc4_get_comp_drv(config->primary.r.module_id); } if (!drv) - return IPC4_MOD_INVALID_ID; + return IPC4_INVALID_RESOURCE_ID; if (!drv->ops.set_large_config) return IPC4_INVALID_REQUEST; /* check for vendor param first */ - if (config.extension.r.large_param_id == VENDOR_CONFIG_PARAM) { + if (config->extension.r.large_param_id == VENDOR_CONFIG_PARAM) { ret = ipc4_set_vendor_config_module_instance(dev, drv, - (uint32_t)config.primary.r.module_id, - (uint32_t)config.primary.r.instance_id, - config.extension.r.init_block, - config.extension.r.final_block, - config.extension.r.data_off_size, + (uint32_t)config->primary.r.module_id, + (uint32_t)config->primary.r.instance_id, + config->extension.r.init_block, + config->extension.r.final_block, + config->extension.r.data_off_size, (const char *)MAILBOX_HOSTBOX_BASE); } else { #if CONFIG_LIBRARY struct ipc *ipc = ipc_get(); - const char *data = (const char *)ipc->comp_data + sizeof(config); + const char *data = (const char *)ipc->comp_data + sizeof(*config); #else const char *data = (const char *)MAILBOX_HOSTBOX_BASE; #endif - ret = drv->ops.set_large_config(dev, config.extension.r.large_param_id, - config.extension.r.init_block, config.extension.r.final_block, - config.extension.r.data_off_size, data); + ret = drv->ops.set_large_config(dev, config->extension.r.large_param_id, + config->extension.r.init_block, config->extension.r.final_block, + config->extension.r.data_off_size, data); if (ret < 0) { ipc_cmd_err(&ipc_tr, "failed to set large_config_module_instance %x : %x", - (uint32_t)config.primary.r.module_id, - (uint32_t)config.primary.r.instance_id); + (uint32_t)config->primary.r.module_id, + (uint32_t)config->primary.r.instance_id); ret = IPC4_INVALID_RESOURCE_ID; } } @@ -1355,6 +1457,7 @@ __cold static int ipc4_set_large_config_module_instance(struct ipc4_message_requ return ret; } +#ifndef CONFIG_SOF_USERSPACE_LL __cold static int ipc4_delete_module_instance(struct ipc4_message_request *ipc4) { struct ipc4_module_delete_instance module; @@ -1368,7 +1471,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); @@ -1382,448 +1485,321 @@ __cold static int ipc4_delete_module_instance(struct ipc4_message_request *ipc4) return ret; } +#endif /* !CONFIG_SOF_USERSPACE_LL */ -/* 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) { + const struct ipc4_module_init_instance *mi; uint32_t type; int ret; assert_can_be_cold(); type = ipc4->primary.r.type; + msg_reply = reply; switch (type) { case SOF_IPC4_MOD_INIT_INSTANCE: - ret = ipc4_init_module_instance(ipc4); + mi = (const struct ipc4_module_init_instance *)ipc4; + + if (cpu_is_me(mi->extension.r.core_id) && !mi->extension.r.proc_domain && + IS_ENABLED(CONFIG_SOF_USERSPACE_LL)) { +#ifdef CONFIG_SOF_USERSPACE_LL + BUILD_ASSERT(sizeof(struct comp_driver) + sizeof(struct tr_ctx) <= + sizeof(((struct ipc_user *)0)->init_drv_data), + "ipc_user.init_drv_data too small for driver copy"); + + /* User-space init: kernel does driver lookup only (requires + * access to IMR manifest and driver list in kernel memory). + * Component creation (drv->ops.create) runs in user thread + * so untrusted module code does not execute in kernel context. + * Cross-core creation stays fully in kernel. + */ + struct ipc *ipc = ipc_get(); + uint32_t comp_id = IPC4_COMP_ID(mi->primary.r.module_id, + mi->primary.r.instance_id); + const struct comp_driver *drv = ipc4_get_comp_drv( + IPC4_MOD_ID(comp_id)); + struct ipc_user *pdata = ipc->ipc_user_pdata; + + if (!drv) { + ret = IPC4_MOD_NOT_INITIALIZED; + break; + } + + /* Copy comp_driver and tr_ctx into user-accessible ipc_user buffer + * originals are in kernel .rodata/.data and not readable from user mode. + */ + struct comp_driver *drv_copy = (struct comp_driver *)pdata->init_drv_data; + struct tr_ctx *tctx_copy = + (struct tr_ctx *)(pdata->init_drv_data + + sizeof(struct comp_driver)); + + ret = memcpy_s(drv_copy, sizeof(*drv_copy), drv, sizeof(*drv)); + if (!ret && drv->tctx) { + ret = memcpy_s(tctx_copy, sizeof(*tctx_copy), + drv->tctx, sizeof(*drv->tctx)); + drv_copy->tctx = tctx_copy; + } + + if (ret < 0) + break; + + pdata->init_drv = drv; + ret = ipc_user_forward_cmd(ipc4->primary.dat, ipc4->extension.dat); +#endif + } else { + /* + * DP module creation starts running in kernel mode and + * switches to userspace later via a scheduler_dp_thread_ipc() + * call in module_init(). + */ + ret = ipc4_init_module_instance(ipc4); + } break; case SOF_IPC4_MOD_CONFIG_GET: +#ifdef CONFIG_SOF_USERSPACE_LL + /* Forward to user thread for privilege-separated execution */ + ret = ipc_user_forward_cmd(ipc4->primary.dat, ipc4->extension.dat); + if (!ret) { + struct ipc *ipc = ipc_get(); + struct ipc_user *pdata = ipc->ipc_user_pdata; + + msg_reply->extension = pdata->reply_ext; + } +#else ret = ipc4_set_get_config_module_instance(ipc4, false); +#endif break; case SOF_IPC4_MOD_CONFIG_SET: +#ifdef CONFIG_SOF_USERSPACE_LL + /* Forward to user thread for privilege-separated execution */ + ret = ipc_user_forward_cmd(ipc4->primary.dat, ipc4->extension.dat); +#else ret = ipc4_set_get_config_module_instance(ipc4, true); +#endif break; case SOF_IPC4_MOD_LARGE_CONFIG_GET: +#ifdef CONFIG_SOF_USERSPACE_LL + { + const struct ipc4_module_large_config *config = + (const struct ipc4_module_large_config *)ipc4; + + if (config->primary.r.module_id) { + /* Module case: forward to user thread */ + ret = ipc_user_forward_cmd(ipc4->primary.dat, + ipc4->extension.dat); + if (!ret) { + struct ipc *ipc = ipc_get(); + struct ipc_user *pdata = ipc->ipc_user_pdata; + + msg_reply->extension = pdata->reply_ext; + msg_reply->tx_size = pdata->reply_tx_size; + msg_reply->tx_data = pdata->reply_tx_data; + } + } else { + /* Base firmware (module_id==0): keep in kernel — + * ipc4_get_comp_drv() accesses IMR manifest which + * has no user-space partition. + */ + ret = ipc4_get_large_config_module_instance(ipc4); + } + } +#else ret = ipc4_get_large_config_module_instance(ipc4); +#endif break; case SOF_IPC4_MOD_LARGE_CONFIG_SET: +#ifdef CONFIG_SOF_USERSPACE_LL + { + const struct ipc4_module_large_config *config = + (const struct ipc4_module_large_config *)ipc4; + + if (config->primary.r.module_id) { + ret = ipc_user_forward_cmd(ipc4->primary.dat, + ipc4->extension.dat); + } else { + /* Base firmware: keep in kernel (IMR access) */ + ret = ipc4_set_large_config_module_instance(ipc4); + } + } +#else ret = ipc4_set_large_config_module_instance(ipc4); +#endif break; case SOF_IPC4_MOD_BIND: +#ifdef CONFIG_SOF_USERSPACE_LL + ret = ipc_user_forward_cmd(ipc4->primary.dat, ipc4->extension.dat); +#else ret = ipc4_bind_module_instance(ipc4); +#endif break; case SOF_IPC4_MOD_UNBIND: +#ifdef CONFIG_SOF_USERSPACE_LL + ret = ipc_user_forward_cmd(ipc4->primary.dat, ipc4->extension.dat); +#else ret = ipc4_unbind_module_instance(ipc4); +#endif break; case SOF_IPC4_MOD_DELETE_INSTANCE: +#ifdef CONFIG_SOF_USERSPACE_LL + ret = ipc_user_forward_cmd(ipc4->primary.dat, ipc4->extension.dat); +#else 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); +#endif break; case SOF_IPC4_MOD_ENTER_MODULE_RESTORE: case SOF_IPC4_MOD_EXIT_MODULE_RESTORE: ret = IPC4_UNAVAILABLE; break; default: - ret = IPC4_UNAVAILABLE; + ret = IPC4_UNKNOWN_MESSAGE_TYPE; 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) +#ifdef CONFIG_SOF_USERSPACE_LL +/* + * IPC4 implementation of the user-thread dispatch hook. Runs in the + * user-space IPC thread; reconstructs the IPC4 message from the forwarded + * words and executes the privilege-separated part of each command. + */ +int ipc_user_thread_dispatch(struct ipc_user *ipc_user) { - /* a single msg_notify object is used */ - if (is_notification_queued(&msg_notify)) - return; + struct ipc4_message_request msg; + int result = -EINVAL; - msg_notify.header = SOF_IPC4_NOTIF_HEADER(SOF_IPC4_NOTIFY_LOG_BUFFER_STATUS); - msg_notify.extension = 0; - msg_notify.tx_size = 0; + /* Reconstruct the IPC4 message from copied words */ + msg.primary.dat = ipc_user->ipc_msg_pri; + msg.extension.dat = ipc_user->ipc_msg_ext; - tr_dbg(&ipc_tr, "tx-notify\t: %#x|%#x", msg_notify.header, msg_notify.extension); + ipc_user->reply_ext = 0; - ipc_msg_send(&msg_notify, NULL, true); -} -#endif + if (msg.primary.r.msg_tgt == SOF_IPC4_MESSAGE_TARGET_MODULE_MSG) { + /* Module message dispatch */ + switch (msg.primary.r.type) { + case SOF_IPC4_MOD_CONFIG_GET: + result = ipc4_process_module_config(&msg, false, + &ipc_user->reply_ext); + break; + case SOF_IPC4_MOD_CONFIG_SET: + result = ipc4_process_module_config(&msg, true, NULL); + break; + case SOF_IPC4_MOD_BIND: { + struct ipc4_module_bind_unbind bu; -void ipc_msg_reply(struct sof_ipc_reply *reply) -{ - struct ipc4_message_request in; + result = memcpy_s(&bu, sizeof(bu), &msg, sizeof(msg)); + if (result < 0) + break; - in.primary.dat = msg_data.msg_in.pri; - ipc_compound_msg_done(in.primary.r.type, reply->error); -} + result = ipc4_comp_connect(ipc_user->ipc, &bu); + break; + } + case SOF_IPC4_MOD_UNBIND: { + struct ipc4_module_bind_unbind bu; -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; + result = memcpy_s(&bu, sizeof(bu), &msg, sizeof(msg)); + if (result < 0) + break; -#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; - } + result = ipc4_comp_disconnect(ipc_user->ipc, &bu); + break; + } + case SOF_IPC4_MOD_INIT_INSTANCE: { + /* User thread creates the component — + * drv->ops.create() runs in user-space so untrusted + * module code does not execute with kernel privileges. + * + * init_drv = original kernel pointer + * init_drv_data = user-accessible copy + */ + const struct comp_driver *orig_drv = ipc_user->init_drv; + const struct comp_driver *drv_copy = + (const struct comp_driver *)ipc_user->init_drv_data; + struct comp_dev *dev; + + ipc_user->init_drv = NULL; + if (!orig_drv) { + result = IPC4_MOD_NOT_INITIALIZED; + break; + } - /* 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; - */ + dev = comp_new_ipc4_user(&msg, drv_copy); + if (!dev) { + result = IPC4_MOD_NOT_INITIALIZED; + break; + } - /* 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; + /* Restore original kernel driver pointer. comp_init() + * set dev->drv to the copy; runtime code expects the + * canonical kernel address. + */ + dev->drv = orig_drv; - /* 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; + result = ipc4_add_comp_dev(dev); + if (result != IPC4_SUCCESS) + break; - if (ipc_wait_for_compound_msg() != 0) { - ipc_cmd_err(&ipc_tr, "ipc4: failed to send delayed reply"); - err = IPC4_FAILURE; + comp_update_ibs_obs_cpc(dev); + result = 0; + break; } + case SOF_IPC4_MOD_DELETE_INSTANCE: { + struct ipc4_module_delete_instance module; + uint32_t comp_id; + + memcpy_s(&module, sizeof(module), &msg, sizeof(msg)); + comp_id = IPC4_COMP_ID(module.primary.r.module_id, + module.primary.r.instance_id); + result = ipc_comp_free(ipc_user->ipc, comp_id); + if (result < 0) + result = IPC4_INVALID_RESOURCE_ID; + break; + } + case SOF_IPC4_MOD_LARGE_CONFIG_GET: + result = ipc4_process_large_config_get(&msg, + &ipc_user->reply_ext, + &ipc_user->reply_tx_size, + &ipc_user->reply_tx_data); + break; + case SOF_IPC4_MOD_LARGE_CONFIG_SET: + result = ipc4_process_large_config_set(&msg); + break; + default: + LOG_ERR("IPC user: unsupported module cmd type %d", + msg.primary.r.type); + result = -EINVAL; + break; + } + } else { + /* Global message dispatch */ + switch (msg.primary.r.type) { + case SOF_IPC4_GLB_CREATE_PIPELINE: + result = ipc_pipeline_new(ipc_user->ipc, + (ipc_pipe_new *)&msg); + break; + case SOF_IPC4_GLB_DELETE_PIPELINE: { + struct ipc4_pipeline_delete *pipe = + (struct ipc4_pipeline_delete *)&msg; - /* 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); + result = ipc_pipeline_free(ipc_user->ipc, + pipe->primary.r.instance_id); + break; + } + case SOF_IPC4_GLB_SET_PIPELINE_STATE: + result = ipc4_set_pipeline_state(&msg); + break; + default: + LOG_ERR("IPC user: unsupported glb cmd type %d", + msg.primary.r.type); + result = -EINVAL; + break; + } } + + return result; } +#endif /* CONFIG_SOF_USERSPACE_LL */ diff --git a/src/ipc/ipc4/helper.c b/src/ipc/ipc4/helper.c index 511917ceb768..0907bcb03ace 100644 --- a/src/ipc/ipc4/helper.c +++ b/src/ipc/ipc4/helper.c @@ -18,11 +18,14 @@ #include <sof/ipc/topology.h> #include <sof/ipc/common.h> #include <ipc/dai.h> +#include <ipc4/handler.h> #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> @@ -62,7 +65,6 @@ LOG_MODULE_DECLARE(ipc, CONFIG_SOF_LOG_LEVEL); extern struct tr_ctx comp_tr; static const struct comp_driver *ipc4_get_drv(const void *uuid); -static int ipc4_add_comp_dev(struct comp_dev *dev); void ipc_build_stream_posn(struct sof_ipc_stream_posn *posn, uint32_t type, uint32_t id) @@ -87,63 +89,109 @@ void ipc_build_trace_posn(struct sof_ipc_dma_trace_posn *posn) } #if CONFIG_LIBRARY -static inline char *ipc4_get_comp_new_data(void) +static inline unsigned char *ipc4_get_comp_new_data(void) { struct ipc *ipc = ipc_get(); - char *data = (char *)ipc->comp_data + sizeof(struct ipc4_module_init_instance); + unsigned char *data = (unsigned char *)ipc->comp_data + + sizeof(struct ipc4_module_init_instance); return data; } -static const struct comp_driver *ipc4_library_get_comp_drv(char *data) +static const struct comp_driver *ipc4_library_get_comp_drv(unsigned char *data) { return ipc4_get_drv(data); } #else -__cold static inline char *ipc4_get_comp_new_data(void) +__cold static inline unsigned char *ipc4_get_comp_new_data(void) { assert_can_be_cold(); - return (char *)MAILBOX_HOSTBOX_BASE; + return (unsigned char *)MAILBOX_HOSTBOX_BASE; } #endif -/* Only called from ipc4_init_module_instance(), which is __cold */ -__cold struct comp_dev *comp_new_ipc4(struct ipc4_module_init_instance *module_init) +__cold static int ipc4_comp_new_config(struct comp_ipc_config *ipc_config, + const struct ipc4_module_init_instance *module_init) { - struct comp_ipc_config ipc_config; - const struct comp_driver *drv; - struct comp_dev *dev; - uint32_t comp_id; - char *data; + uint32_t comp_id = IPC4_COMP_ID(module_init->primary.r.module_id, + module_init->primary.r.instance_id); assert_can_be_cold(); - comp_id = IPC4_COMP_ID(module_init->primary.r.module_id, - module_init->primary.r.instance_id); - if (ipc4_get_comp_dev(comp_id)) { tr_err(&ipc_tr, "comp 0x%x exists", comp_id); - return NULL; + return -EEXIST; } if (module_init->extension.r.core_id >= CONFIG_CORE_COUNT) { tr_err(&ipc_tr, "ipc: comp->core = %u", (uint32_t)module_init->extension.r.core_id); - return NULL; + return -EINVAL; } - memset(&ipc_config, 0, sizeof(ipc_config)); - ipc_config.id = comp_id; - ipc_config.pipeline_id = module_init->extension.r.ppl_instance_id; - 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; + memset(ipc_config, 0, sizeof(*ipc_config)); + ipc_config->id = comp_id; + ipc_config->pipeline_id = module_init->extension.r.ppl_instance_id; + 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; + 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 -ENOSPC; + } - dcache_invalidate_region((__sparse_force void __sparse_cache *)MAILBOX_HOSTBOX_BASE, - MAILBOX_HOSTBOX_SIZE); + if (!module_init->extension.r.proc_domain) { + ipc_config->proc_domain = COMP_PROCESSING_DOMAIN_LL; + } else if (IS_ENABLED(CONFIG_ZEPHYR_DP_SCHEDULER)) { + ipc_config->proc_domain = COMP_PROCESSING_DOMAIN_DP; + } else { + tr_err(&ipc_tr, "ipc: DP scheduling is disabled, cannot create comp 0x%x", comp_id); + return -EINVAL; + } - data = ipc4_get_comp_new_data(); + return 0; +} + +/* Only called from ipc4_init_module_instance(), which is __cold */ +__cold struct comp_dev *comp_new_ipc4(const struct ipc4_module_init_instance *module_init) +{ + struct comp_ipc_config ipc_config; + const struct comp_driver *drv; + struct comp_dev *dev; + unsigned char *data; + uint32_t comp_id = IPC4_COMP_ID(module_init->primary.r.module_id, + module_init->primary.r.instance_id); + + assert_can_be_cold(); + + int ret = ipc4_comp_new_config(&ipc_config, module_init); + + if (ret < 0) + return NULL; + + /* Reject a module naming a non-existent parent pipeline: otherwise + * dev->pipeline stays NULL and a later init path (e.g. the copier) + * dereferences it. IPC4_INVALID_PIPELINE_ID is exempt - it marks base FW + * modules with no parent pipeline. IPC_COMP_ALL: the pipeline may run on a + * different core than this module. + */ + if (ipc_config.pipeline_id != IPC4_INVALID_PIPELINE_ID && + !ipc_get_comp_by_ppl_id(ipc_get(), COMP_TYPE_PIPELINE, ipc_config.pipeline_id, + IPC_COMP_ALL)) { + tr_err(&ipc_tr, "comp 0x%x: parent pipeline %u does not exist", comp_id, + (uint32_t)ipc_config.pipeline_id); + 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); @@ -153,28 +201,15 @@ __cold struct comp_dev *comp_new_ipc4(struct ipc4_module_init_instance *module_i if (!drv) return NULL; -#if CONFIG_ZEPHYR_DP_SCHEDULER - if (module_init->extension.r.proc_domain) - ipc_config.proc_domain = COMP_PROCESSING_DOMAIN_DP; - else - ipc_config.proc_domain = COMP_PROCESSING_DOMAIN_LL; -#else /* CONFIG_ZEPHYR_DP_SCHEDULER */ - if (module_init->extension.r.proc_domain) { - tr_err(&ipc_tr, "ipc: DP scheduling is disabled, cannot create comp 0x%x", comp_id); - return NULL; - } - ipc_config.proc_domain = COMP_PROCESSING_DOMAIN_LL; -#endif /* CONFIG_ZEPHYR_DP_SCHEDULER */ - if (drv->type == SOF_COMP_MODULE_ADAPTER) { const struct ipc_config_process spec = { - .data = (const unsigned char *)data, + .data = data, .size = ipc_config.ipc_config_size, }; - dev = drv->ops.create(drv, &ipc_config, (const void *)&spec); + dev = drv->ops.create(drv, &ipc_config, &spec); } else { - dev = drv->ops.create(drv, &ipc_config, (const void *)data); + dev = drv->ops.create(drv, &ipc_config, data); } if (!dev) return NULL; @@ -200,6 +235,66 @@ __cold struct comp_dev *comp_new_ipc4(struct ipc4_module_init_instance *module_i return dev; } +#ifdef CONFIG_SOF_USERSPACE_LL +/** + * comp_new_ipc4_user - Create component in user-space IPC thread context. + * + * Called from the user-space IPC thread. Receives a pre-resolved driver + * pointer from the kernel handler. Performs IPC4 message parsing, HOSTBOX + * data read, and calls drv->ops.create() in user-space context. + * + * @param ipc4 IPC4 message request (reconstructed from ipc_user pri/ext words) + * @param drv Component driver resolved by kernel via ipc4_get_comp_drv() + * @return Created component device, or NULL on failure + */ +__cold struct comp_dev *comp_new_ipc4_user(struct ipc4_message_request *ipc4, + const struct comp_driver *drv) +{ + struct ipc4_module_init_instance module_init; + struct comp_ipc_config ipc_config; + struct comp_dev *dev; + unsigned char *data; + + assert_can_be_cold(); + + int ret = memcpy_s(&module_init, sizeof(module_init), ipc4, sizeof(*ipc4)); + + if (ret < 0) + return NULL; + + ret = ipc4_comp_new_config(&ipc_config, &module_init); + if (ret < 0) + 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 (drv->type == SOF_COMP_MODULE_ADAPTER) { + const struct ipc_config_process spec = { + .data = data, + .size = ipc_config.ipc_config_size, + }; + + dev = drv->ops.create(drv, &ipc_config, &spec); + } else { + dev = drv->ops.create(drv, &ipc_config, data); + } + if (!dev) + return NULL; + + list_init(&dev->bsource_list); + list_init(&dev->bsink_list); + + return dev; +} +#endif /* CONFIG_SOF_USERSPACE_LL */ + /* Called from ipc4_set_pipeline_state(), so cannot be cold */ struct ipc_comp_dev *ipc_get_comp_by_ppl_id(struct ipc *ipc, uint16_t type, uint32_t ppl_id, @@ -229,11 +324,110 @@ 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)); + return -EINVAL; + } + 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 heap %u", + mem_data->domain_id, mem_data->stack_bytes, + mem_data->heap_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; struct ipc *ipc = ipc_get(); + struct k_heap *heap = sof_sys_user_heap_get(); assert_can_be_cold(); @@ -246,7 +440,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(heap, 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; @@ -261,12 +456,13 @@ __cold static int ipc4_create_pipeline(struct ipc4_pipeline_create *pipe_desc) pipe->core = pipe_desc->extension.r.core_id; /* allocate the IPC pipeline container */ - ipc_pipe = rzalloc(SOF_MEM_FLAG_USER | SOF_MEM_FLAG_COHERENT, - sizeof(struct ipc_comp_dev)); + ipc_pipe = sof_heap_alloc(heap, SOF_MEM_FLAG_USER | SOF_MEM_FLAG_COHERENT, + sizeof(struct ipc_comp_dev), 0); if (!ipc_pipe) { pipeline_free(pipe); return IPC4_OUT_OF_MEMORY; } + memset(ipc_pipe, 0, sizeof(*ipc_pipe)); ipc_pipe->pipeline = pipe; ipc_pipe->type = COMP_TYPE_PIPELINE; @@ -280,20 +476,55 @@ __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); + if (pipe_desc->extension.r.core_id >= CONFIG_CORE_COUNT) { + tr_err(&ipc_tr, "invalid pipeline core ID: %u", + (uint32_t)pipe_desc->extension.r.core_id); + return IPC4_INVALID_CORE_ID; + } + /* pass IPC to target core */ 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) @@ -311,16 +542,28 @@ __cold static int ipc_pipeline_module_free(uint32_t pipeline_id) struct ipc *ipc = ipc_get(); struct ipc_comp_dev *icd; int ret; +#ifdef CONFIG_SOF_USERSPACE_LL + int ppl_core; +#endif assert_can_be_cold(); icd = ipc_get_comp_by_ppl_id(ipc, COMP_TYPE_COMPONENT, pipeline_id, IPC_COMP_ALL); + if (!icd) + return IPC4_SUCCESS; + +#ifdef CONFIG_SOF_USERSPACE_LL + ppl_core = icd->core; + user_ll_lock_sched(ppl_core); +#endif + while (icd) { struct comp_buffer *buffer; struct comp_buffer *safe; /* 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 */ @@ -343,12 +586,20 @@ __cold static int ipc_pipeline_module_free(uint32_t pipeline_id) else ret = ipc_comp_free(ipc, icd->id); - if (ret) + if (ret) { +#ifdef CONFIG_SOF_USERSPACE_LL + user_ll_unlock_sched(ppl_core); +#endif return IPC4_INVALID_RESOURCE_STATE; + } icd = ipc_get_comp_by_ppl_id(ipc, COMP_TYPE_COMPONENT, pipeline_id, IPC_COMP_ALL); } +#ifdef CONFIG_SOF_USERSPACE_LL + user_ll_unlock_sched(ppl_core); +#endif + return IPC4_SUCCESS; } @@ -384,14 +635,14 @@ __cold int ipc_pipeline_free(struct ipc *ipc, uint32_t comp_id) ipc_pipe->pipeline = NULL; list_item_del(&ipc_pipe->list); - rfree(ipc_pipe); + sof_heap_free(sof_sys_user_heap_get(), ipc_pipe); return IPC4_SUCCESS; } __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; @@ -401,8 +652,16 @@ __cold static struct comp_buffer *ipc4_create_buffer(struct comp_dev *src, bool ipc_buf.size = buf_size; 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); + + /* The buffer belongs to the core on which it is created. For a same-core bind + * ipc4_process_on_core() has already migrated us to the src core (== dst core), + * while a cross-core bind is always processed on the primary core. cpu_get_id() + * is privileged and so avoided here. + */ + ipc_buf.comp.core = is_shared ? PLATFORM_PRIMARY_CORE_ID : src->ipc_config.core; + assert(IS_ENABLED(CONFIG_SOF_USERSPACE_LL) || ipc_buf.comp.core == cpu_get_id()); + + return buffer_new(alloc, &ipc_buf, is_shared); } #if CONFIG_CROSS_CORE_STREAM @@ -422,21 +681,25 @@ __cold static struct comp_buffer *ipc4_create_buffer(struct comp_dev *src, bool * disable any interrupts. */ -#define ll_block(cross_core_bind, flags) \ +#if CONFIG_SOF_USERSPACE_LL +#error "CONFIG_SOF_USERSPACE_LL not compatible with cross-core streams" +#else +#define ll_block(src_core, dst_core, flags) \ do { \ - if (cross_core_bind) \ + if (src_core != dst_core) \ domain_block(sof_get()->platform_timer_domain); \ else \ irq_local_disable(flags); \ } while (0) -#define ll_unblock(cross_core_bind, flags) \ +#define ll_unblock(src_core, dst_core, flags) \ do { \ - if (cross_core_bind) \ + if (src_core != dst_core) \ domain_unblock(sof_get()->platform_timer_domain); \ else \ irq_local_enable(flags); \ } while (0) +#endif /* Calling both ll_block() and ll_wait_finished_on_core() makes sure LL will not start its * next cycle and its current cycle on specified core has finished. @@ -468,15 +731,28 @@ static int ll_wait_finished_on_core(struct comp_dev *dev) #else -#define ll_block(cross_core_bind, flags) irq_local_disable(flags) -#define ll_unblock(cross_core_bind, flags) irq_local_enable(flags) +#if CONFIG_SOF_USERSPACE_LL +/* note: cross-core streams are disabled so src_core==dst_core */ +#define ll_block(src_core, dst_core, flags) \ + do { \ + user_ll_lock_sched(src_core); \ + ARG_UNUSED(flags); \ + } while(0) +#define ll_unblock(src_core, dst_core, flags) \ + do { \ + user_ll_unlock_sched(src_core); \ + ARG_UNUSED(flags); \ + } while(0) +#else +#define ll_block(src_core, dst_core, flags) irq_local_disable(flags) +#define ll_unblock(src_core, dst_core, flags) irq_local_enable(flags) +#endif #endif /* Only called from ipc4_bind_module_instance(), which is __cold */ -__cold int ipc_comp_connect(struct ipc *ipc, ipc_pipe_comp_connect *_connect) +__cold int ipc4_comp_connect(struct ipc *ipc, const struct ipc4_module_bind_unbind *bu) { - struct ipc4_module_bind_unbind *bu; struct bind_info bind_data; struct comp_buffer *buffer; struct comp_dev *source; @@ -492,7 +768,6 @@ __cold int ipc_comp_connect(struct ipc *ipc, ipc_pipe_comp_connect *_connect) assert_can_be_cold(); - bu = (struct ipc4_module_bind_unbind *)_connect; src_id = IPC4_COMP_ID(bu->primary.r.module_id, bu->primary.r.instance_id); sink_id = IPC4_COMP_ID(bu->extension.r.dst_module_id, bu->extension.r.dst_instance_id); source = ipc4_get_comp_dev(src_id); @@ -503,6 +778,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,7 +787,25 @@ __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 */ + +#ifdef CONFIG_SOF_USERSPACE_LL + if (!alloc) + alloc = ipc->ll_alloc; +#endif bool cross_core_bind = source->ipc_config.core != sink->ipc_config.core; /* If both components are on same core -- process IPC on that core, @@ -579,12 +874,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 +903,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); } @@ -626,7 +936,7 @@ __cold int ipc_comp_connect(struct ipc *ipc, ipc_pipe_comp_connect *_connect) * blocked on corresponding core(s) to prevent IPC or IDC task getting preempted which * could result in buffers being only half connected when a pipeline task gets executed. */ - ll_block(cross_core_bind, flags); + ll_block(source->ipc_config.core, sink->ipc_config.core, flags); if (cross_core_bind) { #if CONFIG_CROSS_CORE_STREAM @@ -646,14 +956,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; } @@ -683,7 +993,7 @@ __cold int ipc_comp_connect(struct ipc *ipc, ipc_pipe_comp_connect *_connect) source->direction_set = true; } - ll_unblock(cross_core_bind, flags); + ll_unblock(source->ipc_config.core, sink->ipc_config.core, flags); return IPC4_SUCCESS; @@ -697,7 +1007,7 @@ __cold int ipc_comp_connect(struct ipc *ipc, ipc_pipe_comp_connect *_connect) e_sink_connect: pipeline_disconnect(source, buffer, PPL_CONN_DIR_COMP_TO_BUFFER); free: - ll_unblock(cross_core_bind, flags); + ll_unblock(source->ipc_config.core, sink->ipc_config.core, flags); buffer_free(buffer); return IPC4_INVALID_RESOURCE_STATE; } @@ -708,9 +1018,8 @@ __cold int ipc_comp_connect(struct ipc *ipc, ipc_pipe_comp_connect *_connect) * pipeline and create it in modified form. */ /* Only called from ipc4_unbind_module_instance(), which is __cold */ -__cold int ipc_comp_disconnect(struct ipc *ipc, ipc_pipe_comp_connect *_connect) +__cold int ipc4_comp_disconnect(struct ipc *ipc, const struct ipc4_module_bind_unbind *bu) { - struct ipc4_module_bind_unbind *bu; struct comp_buffer *buffer = NULL; struct comp_buffer *buf; struct comp_dev *src, *sink; @@ -722,7 +1031,6 @@ __cold int ipc_comp_disconnect(struct ipc *ipc, ipc_pipe_comp_connect *_connect) assert_can_be_cold(); - bu = (struct ipc4_module_bind_unbind *)_connect; src_id = IPC4_COMP_ID(bu->primary.r.module_id, bu->primary.r.instance_id); sink_id = IPC4_COMP_ID(bu->extension.r.dst_module_id, bu->extension.r.dst_instance_id); src = ipc4_get_comp_dev(src_id); @@ -752,30 +1060,42 @@ __cold int ipc_comp_disconnect(struct ipc *ipc, ipc_pipe_comp_connect *_connect) if (!buffer) return IPC4_INVALID_RESOURCE_ID; + /* + * The buffer was located on the source's consumer list, but the sink was + * resolved solely from the host-supplied dst_module_id/dst_instance_id. + * Make sure the buffer is actually connected to that sink, otherwise an + * incorrect dst would leave the real sink bound to a buffer we are about + * to free, while unbinding an unrelated component instead. + */ + if (comp_buffer_get_sink_component(buffer) != sink) { + tr_err(&ipc_tr, "buffer %#x is not connected to sink %#x", buffer_id, sink_id); + return IPC4_INVALID_RESOURCE_ID; + } + /* * Disconnect and unbind buffer from source/sink components and continue to free the buffer * even in case of errors. Block LL processing during disconnect and unbinding to prevent * IPC or IDC task getting preempted which could result in buffers being only half connected * when a pipeline task gets executed. */ - ll_block(cross_core_unbind, flags); + ll_block(src->ipc_config.core, sink->ipc_config.core, flags); if (cross_core_unbind) { #if CONFIG_CROSS_CORE_STREAM /* Make sure LL has finished on both cores */ if (!cpu_is_me(src->ipc_config.core)) if (ll_wait_finished_on_core(src) < 0) { - ll_unblock(cross_core_unbind, flags); + ll_unblock(src->ipc_config.core, sink->ipc_config.core, flags); return IPC4_FAILURE; } if (!cpu_is_me(sink->ipc_config.core)) if (ll_wait_finished_on_core(sink) < 0) { - ll_unblock(cross_core_unbind, flags); + ll_unblock(src->ipc_config.core, sink->ipc_config.core, flags); return IPC4_FAILURE; } #else tr_err(&ipc_tr, "Cross-core binding is disabled"); - ll_unblock(cross_core_unbind, flags); + ll_unblock(src->ipc_config.core, sink->ipc_config.core, flags); return IPC4_FAILURE; #endif } @@ -792,7 +1112,7 @@ __cold int ipc_comp_disconnect(struct ipc *ipc, ipc_pipe_comp_connect *_connect) unbind_data.source = audio_buffer_get_source(&buffer->audio_buffer); ret1 = comp_unbind(sink, &unbind_data); - ll_unblock(cross_core_unbind, flags); + ll_unblock(src->ipc_config.core, sink->ipc_config.core, flags); buffer_free(buffer); @@ -804,7 +1124,7 @@ __cold int ipc_comp_disconnect(struct ipc *ipc, ipc_pipe_comp_connect *_connect) #if CONFIG_COMP_CHAIN_DMA /* Only called from ipc4_process_chain_dma(), which is __cold */ -__cold int ipc4_chain_manager_create(struct ipc4_chain_dma *cdma) +__cold int ipc4_chain_manager_create(const struct ipc4_chain_dma *cdma) { const struct sof_uuid uuid = SOF_REG_UUID(chain_dma); const struct comp_driver *drv; @@ -824,14 +1144,13 @@ __cold int ipc4_chain_manager_create(struct ipc4_chain_dma *cdma) const uint32_t comp_id = IPC4_COMP_ID(cdma->primary.r.host_dma_id + IPC4_MAX_MODULE_COUNT, 0); dev->ipc_config.id = comp_id; - dev->ipc_config.pipeline_id = cdma->primary.r.host_dma_id - + IPC4_MAX_MODULE_COUNT; + dev->ipc_config.pipeline_id = cdma->primary.r.host_dma_id + IPC4_MAX_MODULE_COUNT; return ipc4_add_comp_dev(dev); } /* Only called from ipc4_process_chain_dma(), which is __cold */ -__cold int ipc4_chain_dma_state(struct comp_dev *dev, struct ipc4_chain_dma *cdma) +__cold int ipc4_chain_dma_state(struct comp_dev *dev, const struct ipc4_chain_dma *cdma) { const bool allocate = cdma->primary.r.allocate; const bool enable = cdma->primary.r.enable; @@ -864,7 +1183,7 @@ __cold int ipc4_chain_dma_state(struct comp_dev *dev, struct ipc4_chain_dma *cdm if (icd->cd != dev) continue; list_item_del(&icd->list); - rfree(icd); + sof_heap_free(sof_sys_user_heap_get(), icd); break; } comp_free(dev); @@ -956,44 +1275,108 @@ 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; struct comp_driver_info *info; - uint32_t flags; assert_can_be_cold(); - irq_local_disable(flags); - /* search driver list with UUID */ list_for_item(clist, &drivers->list) { - info = container_of(clist, struct comp_driver_info, - list); + info = container_of(clist, struct comp_driver_info, list); if (!memcmp(info->drv->uid, uuid, UUID_SIZE)) { - tr_dbg(&comp_tr, - "found type %d, uuid %pU", - info->drv->type, - info->drv->tctx->uuid_p); + tr_dbg(&comp_tr, "found type %d, uuid %pU", + 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]); + 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]); -out: - irq_local_enable(flags); return drv; } +#if defined(CONFIG_ARCH_POSIX_LIBFUZZER) && !defined(RIMAGE_MANIFEST) +/* + * ipc4_get_fuzzer_drv - resolve a module driver for native_sim fuzz builds. + * + * native_sim fuzz builds have no rimage manifest, so the manifest lookup in + * ipc4_get_comp_drv() can never resolve a driver and every module instance + * verb (INIT_INSTANCE, CONFIG_GET/SET, LARGE_CONFIG, BIND, UNBIND, + * DELETE_INSTANCE) would be unreachable from the fuzzer. + * + * Mirror the IPC3 whitebox hack in posix/ipc.c and treat module_id as a + * 1-based index into the runtime component driver list, counting only + * instantiable module-adapter drivers so the fuzzer's module_id space matches + * what a real signed manifest would expose. + * + * module_id 0 (BaseFW) is deliberately not mapped: BaseFW is never created as + * a pipeline component - its messages are dispatched directly by base_fw.c - + * so returning NULL for it here is correct. + */ +static const struct comp_driver *ipc4_get_fuzzer_drv(uint32_t module_id) +{ + struct comp_driver_list *dlist = comp_drivers_get(); + struct list_item *iter; + uint32_t idx = 0; + + if (!module_id) + return NULL; + + list_for_item(iter, &dlist->list) { + struct comp_driver_info *inf = + container_of(iter, struct comp_driver_info, list); + + /* + * Skip query-only entries (e.g. BaseFW) that cannot be + * instantiated because they have no create op. + */ + if (!inf->drv->ops.create) + continue; + + /* + * A real signed manifest only lists module-adapter modules. + * The internal gateway drivers (SOF_COMP_HOST, SOF_COMP_DAI) + * are registered for IPC3 but are never IPC4 modules: on the + * IPC4 path they receive no params() pass and cannot be + * configured (e.g. their DMA buffer is never allocated). Skip + * non-module-adapter drivers so the fuzzer's module_id space + * matches a real manifest and cannot map to an unconfigurable + * component. + */ + if (inf->drv->type != SOF_COMP_MODULE_ADAPTER) + continue; + + if (++idx == module_id) + return inf->drv; + } + + tr_err(&comp_tr, "no instantiable driver at module_id %u (%u available)", + module_id, idx); + return NULL; +} +#endif + /* * Called from * - ipc4_get_large_config_module_instance() @@ -1004,7 +1387,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; @@ -1012,6 +1394,8 @@ __cold const struct comp_driver *ipc4_get_comp_drv(uint32_t module_id) #ifdef RIMAGE_MANIFEST desc = (const struct sof_man_fw_desc *)IMR_BOOT_LDR_MANIFEST_BASE; +#elif defined(CONFIG_ARCH_POSIX_LIBFUZZER) + return ipc4_get_fuzzer_drv(module_id); #else /* Non-rimage platforms have no component facility yet. * This needs to move to the platform layer. @@ -1043,7 +1427,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 +1437,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) @@ -1075,7 +1459,7 @@ struct comp_dev *ipc4_get_comp_dev(uint32_t comp_id) } EXPORT_SYMBOL(ipc4_get_comp_dev); -__cold static int ipc4_add_comp_dev(struct comp_dev *dev) +__cold int ipc4_add_comp_dev(struct comp_dev *dev) { struct ipc *ipc = ipc_get(); struct ipc_comp_dev *icd; @@ -1090,20 +1474,20 @@ __cold static int ipc4_add_comp_dev(struct comp_dev *dev) } /* allocate the IPC component container */ - icd = rzalloc(SOF_MEM_FLAG_USER | SOF_MEM_FLAG_COHERENT, - sizeof(struct ipc_comp_dev)); + icd = sof_heap_alloc(sof_sys_user_heap_get(), SOF_MEM_FLAG_USER | SOF_MEM_FLAG_COHERENT, + sizeof(struct ipc_comp_dev), 0); if (!icd) { tr_err(&ipc_tr, "alloc failed"); - rfree(icd); return IPC4_OUT_OF_MEMORY; } + memset(icd, 0, sizeof(*icd)); icd->cd = dev; icd->type = COMP_TYPE_COMPONENT; 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); @@ -1123,25 +1507,91 @@ int ipc4_find_dma_config(struct ipc_config_dai *dai, uint8_t *data_buffer, uint3 return IPC4_SUCCESS; } +/* Unlike the above ipc4_find_dma_config(), this can find multiple DMA configs. + * For example, a UAOL copier may use two DMA channels: one for audio and one + * for clock feedback. This function can only work when all data in data_buffer + * is in TLV format; however, this is not always the case for all gateway types. + * Therefore, the above ipc4_find_dma_config() is still used as it can skip non-TLV + * blob data at the beginning of data_buffer. + */ +int ipc4_find_all_dma_configs_tlvs_only(struct ipc_config_dai *dai, + uint8_t *data_buffer, size_t size) +{ + uintptr_t end_addr = (uintptr_t)data_buffer + size; + struct sof_tlv *tlvs; + struct ipc_dma_config *dma_cfg; + int count = 0; + + for (tlvs = (struct sof_tlv *)data_buffer; + tlvs && (uintptr_t)tlvs + sizeof(*tlvs) <= end_addr; + tlvs = tlv_next(tlvs)) { + /* + * tlvs->length is host-controlled: computing + * "tlvs->value + tlvs->length" can overflow the pointer and + * wrap below end_addr, bypassing this check and letting + * tlv_next() advance to a wild address. Compare against the + * remaining space instead. The loop condition guarantees the + * TLV header fits, so end_addr - tlvs->value does not underflow. + */ + if (tlvs->length > end_addr - (uintptr_t)tlvs->value) { + tr_err(&ipc_tr, "Unexpected TLV length %u: exceeds buffer size", tlvs->length); + return IPC4_INVALID_REQUEST; + } + + dma_cfg = tlv_value_ptr_get(tlvs, GTW_DMA_CONFIG_ID); + if (!dma_cfg) + continue; + + if (count >= GTW_DMA_DEVICE_MAX_COUNT) { + tr_err(&ipc_tr, "Unexpected DMA config count %d, max %d", + count, GTW_DMA_DEVICE_MAX_COUNT); + return IPC4_INVALID_REQUEST; + } + + dai->host_dma_config[count++] = dma_cfg; + } + + if (count == 0) { + tr_err(&ipc_tr, "No DMA config found"); + return IPC4_INVALID_REQUEST; + } + + return IPC4_SUCCESS; +} + int ipc4_find_dma_config_multiple(struct ipc_config_dai *dai, uint8_t *data_buffer, uint32_t size, uint32_t device_id, int dma_cfg_idx) { - uint32_t end_addr = (uint32_t)data_buffer + size; + uintptr_t end_addr = (uintptr_t)data_buffer + size; 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 && (uintptr_t)tlvs < end_addr; tlvs = tlv_next(tlvs)) { + /* Reject a host TLV that overruns the buffer or wraps tlv_next(). */ + uintptr_t remaining = end_addr - (uintptr_t)tlvs; + + if (remaining < sizeof(*tlvs) || tlvs->length > remaining - sizeof(*tlvs)) + return IPC4_INVALID_REQUEST; + dma_cfg = tlv_value_ptr_get(tlvs, GTW_DMA_CONFIG_ID); if (!dma_cfg) continue; + uint32_t device_count = dma_cfg->channel_map.device_count; + + if (device_count > GTW_DMA_DEVICE_MAX_COUNT) { + tr_err(&ipc_tr, "device_count %u exceeds max %u", + device_count, GTW_DMA_DEVICE_MAX_COUNT); + return IPC4_INVALID_REQUEST; + } + /* To be able to retrieve proper DMA config we need to check if * device_id value (which is alh_id) is equal to device_address. * They both contain SNDW master id and PDI. If they match then * proper config is found. */ - for (uint32_t i = 0; i < dma_cfg->channel_map.device_count; i++) { + for (uint32_t i = 0; i < device_count; i++) { if (dma_cfg->channel_map.map[i].device_address == device_id) { dai->host_dma_config[dma_cfg_idx] = dma_cfg; return IPC4_SUCCESS; @@ -1152,28 +1602,34 @@ 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, - &frame_fmt, &valid_fmt, - base_cfg->audio_fmt.s_type); + 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, 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); @@ -1185,10 +1641,8 @@ void ipc4_update_buffer_format(struct comp_buffer *buf_c, audio_stream_set_channels(&buf_c->stream, fmt->channels_count); audio_stream_set_rate(&buf_c->stream, fmt->sampling_frequency); - audio_stream_fmt_conversion(fmt->depth, - fmt->valid_bit_depth, - &frame_fmt, &valid_fmt, - fmt->s_type); + audio_stream_fmt_conversion(fmt->depth, fmt->valid_bit_depth, + &frame_fmt, &valid_fmt, fmt->s_type); audio_stream_set_frm_fmt(&buf_c->stream, frame_fmt); audio_stream_set_valid_fmt(&buf_c->stream, valid_fmt); @@ -1199,6 +1653,7 @@ void ipc4_update_buffer_format(struct comp_buffer *buf_c, audio_buffer_set_hw_params_configured(&buf_c->audio_buffer); } +EXPORT_SYMBOL(ipc4_update_buffer_format); void ipc4_update_source_format(struct sof_source *source, const struct ipc4_audio_format *fmt) @@ -1207,10 +1662,8 @@ void ipc4_update_source_format(struct sof_source *source, source_set_channels(source, fmt->channels_count); source_set_rate(source, fmt->sampling_frequency); - audio_stream_fmt_conversion(fmt->depth, - fmt->valid_bit_depth, - &frame_fmt, &valid_fmt, - fmt->s_type); + audio_stream_fmt_conversion(fmt->depth, fmt->valid_bit_depth, + &frame_fmt, &valid_fmt, fmt->s_type); source_set_frm_fmt(source, frame_fmt); source_set_valid_fmt(source, valid_fmt); @@ -1225,13 +1678,11 @@ void ipc4_update_sink_format(struct sof_sink *sink, sink_set_channels(sink, fmt->channels_count); sink_set_rate(sink, fmt->sampling_frequency); - audio_stream_fmt_conversion(fmt->depth, - fmt->valid_bit_depth, - &frame_fmt, &valid_fmt, - fmt->s_type); + audio_stream_fmt_conversion(fmt->depth, fmt->valid_bit_depth, + &frame_fmt, &valid_fmt, fmt->s_type); sink_set_frm_fmt(sink, frame_fmt); sink_set_valid_fmt(sink, valid_fmt); sink_set_buffer_fmt(sink, fmt->interleaving_style); } -EXPORT_SYMBOL(ipc4_update_buffer_format); +EXPORT_SYMBOL(ipc4_update_sink_format); 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..5679fbddfe59 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); } @@ -93,14 +93,16 @@ static enum task_state validate(void *data) return SOF_TASK_STATE_RESCHEDULE; } -void sa_init(struct sof *sof, uint64_t timeout) +__cold void sa_init(struct sof *sof, uint64_t timeout) { uint64_t ticks; + assert_can_be_cold(); + 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 +119,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..7f4e6ebb8664 100644 --- a/src/lib/ams.c +++ b/src/lib/ams.c @@ -16,7 +16,6 @@ #include <sof/ipc/topology.h> #include <rtos/idc.h> #include <rtos/alloc.h> -#include <sof/lib/memory.h> #include <errno.h> #include <stdbool.h> #include <stddef.h> @@ -285,9 +284,13 @@ static uint32_t ams_push_slot(struct ams_shared_context __sparse_cache *ctx_shar for (uint32_t i = 0; i < ARRAY_SIZE(ctx_shared->slots); ++i) { if (ctx_shared->slot_uses[i] == 0) { + /* the slot only carries the payload struct (read back + * via u.msg); message points to a caller-owned buffer + * rather than inline data, so copy exactly the struct + */ err = memcpy_s((__sparse_force void *)ctx_shared->slots[i].u.msg_raw, sizeof(ctx_shared->slots[i].u.msg_raw), - msg, AMS_MESSAGE_SIZE(msg)); + msg, sizeof(*msg)); if (err != 0) return AMS_INVALID_SLOT; @@ -495,7 +498,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); @@ -578,13 +581,15 @@ static int ams_create_shared_context(struct ams_shared_context *ctx) return 0; } -int ams_init(void) +__cold int ams_init(void) { struct ams_shared_context *ams_shared_ctx; struct async_message_service **ams = arch_ams_get(); struct sof *sof; int ret = 0; + assert_can_be_cold(); + *ams = rzalloc(SOF_MEM_FLAG_USER | SOF_MEM_FLAG_COHERENT, sizeof(**ams)); if (!*ams) diff --git a/src/lib/clk.c b/src/lib/clk.c index d93ab81c680b..89c159f20d31 100644 --- a/src/lib/clk.c +++ b/src/lib/clk.c @@ -24,7 +24,7 @@ SOF_DEFINE_REG_UUID(clock); DECLARE_TR_CTX(clock_tr, SOF_UUID(clock_uuid), LOG_LEVEL_INFO); -SHARED_DATA struct k_spinlock clk_lock; +struct k_spinlock clk_lock; static inline uint32_t clock_get_nearest_freq_idx(const struct freq_table *tab, uint32_t size, uint32_t hz) @@ -89,7 +89,6 @@ uint64_t clock_ticks_per_sample(int clock, uint32_t sample_rate) uint32_t ticks_per_msec; uint64_t ticks_per_sample; - platform_shared_get(clk_info, sizeof(*clk_info)); ticks_per_msec = clk_info->freqs[clk_info->current_freq_idx].ticks_per_msec; ticks_per_sample = sample_rate ? ticks_per_msec * 1000 / sample_rate : 0; diff --git a/src/lib/cpu-clk-manager.c b/src/lib/cpu-clk-manager.c index 59744063e4b9..df917fab4f9d 100644 --- a/src/lib/cpu-clk-manager.c +++ b/src/lib/cpu-clk-manager.c @@ -28,6 +28,13 @@ static int request_freq_change(unsigned int core, int freq) break; } + /* if no entry was larger than the requested frequency the loop ends with + * selected_freq_id == clk->freqs_num; clamp to the last valid entry + * before indexing + */ + if (selected_freq_id == clk->freqs_num) + selected_freq_id = clk->freqs_num - 1; + /* don't change clock frequency if already using proper clock */ current_freq = clock_get_freq(core); if (clk->freqs[selected_freq_id].freq != current_freq) diff --git a/src/lib/dai.c b/src/lib/dai.c index c4daf2b06918..68163282adc1 100644 --- a/src/lib/dai.c +++ b/src/lib/dai.c @@ -11,6 +11,7 @@ #include <sof/lib/uuid.h> #include <rtos/spinlock.h> #include <sof/trace/trace.h> +#include <sof/schedule/ll_schedule_domain.h> /* for zephyr_ll_user_heap() */ #include <ipc/topology.h> #include <ipc/dai.h> #include <user/trace.h> @@ -185,8 +186,28 @@ 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_AMD_TDM + DT_FOREACH_STATUS_OKAY(amd_tdm_dai, GET_DEVICE_LIST) +#endif +#if CONFIG_DAI_INTEL_UAOL + DT_FOREACH_STATUS_OKAY(intel_uaol_dai, GET_DEVICE_LIST) +#endif +#if DT_HAS_COMPAT_STATUS_OKAY(mediatek_afe) + DT_FOREACH_STATUS_OKAY(mediatek_afe, 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,31 +220,33 @@ 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: return DAI_IMX_ESAI; - case SOF_DAI_AMD_BT: - return DAI_AMD_BT; - case SOF_DAI_AMD_SP: - return DAI_AMD_SP; case SOF_DAI_AMD_DMIC: return DAI_AMD_DMIC; case SOF_DAI_MEDIATEK_AFE: return DAI_MEDIATEK_AFE; case SOF_DAI_IMX_MICFIL: return DAI_IMX_MICFIL; + case SOF_DAI_AMD_SDW: + return DAI_AMD_SDW; 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_SP: + case SOF_DAI_AMD_SP_VIRTUAL: + case SOF_DAI_AMD_BT: + case SOF_DAI_AMD_TDM: + return DAI_AMD_TDM; default: return -EINVAL; } } -const struct device *dai_get_device(uint32_t type, uint32_t index) +const struct device *z_impl_dai_get_device(enum sof_ipc_dai_type type, uint32_t index) { struct dai_config cfg; int z_type; @@ -277,9 +300,30 @@ 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; + /* All TDM-capable AMD DAIs share acp_tdm_dma. */ + case SOF_DAI_AMD_HS: + case SOF_DAI_AMD_HS_VIRTUAL: + case SOF_DAI_AMD_SP: + case SOF_DAI_AMD_SP_VIRTUAL: + case SOF_DAI_AMD_BT: + d->dma_dev = SOF_DMA_DEV_HS | SOF_DMA_DEV_SP | SOF_DMA_DEV_BT; + d->dma_caps = SOF_DMA_CAP_HS | SOF_DMA_CAP_SP | SOF_DMA_CAP_BT; + break; + case SOF_DAI_AMD_TDM: + d->dma_dev = SOF_DMA_DEV_TDM; + d->dma_caps = SOF_DMA_CAP_TDM; + break; + case SOF_DAI_MEDIATEK_AFE: + d->dma_dev = SOF_DMA_DEV_AFE_MEMIF; + break; default: break; } @@ -290,6 +334,11 @@ struct dai *dai_get(uint32_t type, uint32_t index, uint32_t flags) { const struct device *dev; struct dai *d; + struct k_heap *heap = NULL; + +#ifdef CONFIG_SOF_USERSPACE_LL + heap = zephyr_ll_user_heap(); +#endif dev = dai_get_device(type, index); if (!dev) { @@ -298,10 +347,12 @@ struct dai *dai_get(uint32_t type, uint32_t index, uint32_t flags) return NULL; } - d = rzalloc(SOF_MEM_FLAG_USER | SOF_MEM_FLAG_COHERENT, sizeof(struct dai)); + d = sof_heap_alloc(heap, SOF_MEM_FLAG_USER | SOF_MEM_FLAG_COHERENT, sizeof(struct dai), 0); if (!d) return NULL; + memset(d, 0, sizeof(struct dai)); + d->index = index; d->type = type; d->dev = dev; @@ -311,7 +362,7 @@ struct dai *dai_get(uint32_t type, uint32_t index, uint32_t flags) if (dai_probe(d->dev)) { tr_err(&dai_tr, "dai_get: failed to probe dai with index %d type %d", index, type); - rfree(d); + sof_heap_free(heap, d); return NULL; } @@ -322,14 +373,19 @@ struct dai *dai_get(uint32_t type, uint32_t index, uint32_t flags) void dai_put(struct dai *dai) { int ret; + struct k_heap *heap = NULL; + +#ifdef CONFIG_SOF_USERSPACE_LL + heap = zephyr_ll_user_heap(); +#endif 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); } - rfree(dai); + sof_heap_free(heap, dai); } #else static inline const struct dai_type_info *dai_find_type(uint32_t type) @@ -391,11 +447,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..6500e7557483 100644 --- a/src/lib/dma.c +++ b/src/lib/dma.c @@ -5,6 +5,7 @@ // Author: Ranjani Sridharan <ranjani.sridharan@linux.intel.com> #include <rtos/atomic.h> +#include <rtos/kernel.h> #include <sof/audio/audio_stream.h> #include <sof/audio/buffer.h> #include <sof/audio/component.h> @@ -17,7 +18,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 +36,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,13 +129,13 @@ 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; key = k_spin_lock(&dma->lock); if (--dma->sref == 0) { - rfree(dma->chan); + sof_heap_free(dma->heap, dma->chan); dma->chan = NULL; } @@ -147,18 +147,20 @@ void sof_dma_put(struct sof_dma *dma) static int dma_init(struct sof_dma *dma) { struct dma_chan_data *chan; + struct k_heap *heap = sof_sys_user_heap_get(); int i; /* allocate dma channels */ - dma->chan = rzalloc(SOF_MEM_FLAG_USER | SOF_MEM_FLAG_COHERENT, - sizeof(struct dma_chan_data) * dma->plat_data.channels); - + dma->chan = sof_heap_alloc(heap, SOF_MEM_FLAG_USER | SOF_MEM_FLAG_COHERENT, + sizeof(struct dma_chan_data) * dma->plat_data.channels, 0); if (!dma->chan) { - tr_err(&dma_tr, "dma_probe_sof(): dma %d allocaction of channels failed", + tr_err(&dma_tr, "dma %d allocation of channels failed", dma->plat_data.id); return -ENOMEM; } + dma->heap = heap; + memset(dma->chan, 0, sizeof(struct dma_chan_data) * dma->plat_data.channels); /* init work */ for (i = 0, chan = dma->chan; i < dma->plat_data.channels; i++, chan++) { @@ -168,8 +170,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 +278,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 +297,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 +325,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, @@ -380,8 +380,19 @@ int dma_buffer_copy_from(struct comp_buffer *source, /* source buffer contains data copied by DMA */ audio_stream_invalidate(istream, source_bytes); - /* process data */ - ret = process(istream, 0, &sink->stream, 0, source_samples, chmap); + /* process data; the converter consumes cir_buf descriptors */ + struct cir_buf_source cir_src = { + .buf_start = audio_stream_get_addr(istream), + .buf_end = audio_stream_get_end_addr(istream), + .ptr = audio_stream_get_rptr(istream), + }; + struct cir_buf_sink cir_snk = { + .buf_start = audio_stream_get_addr(&sink->stream), + .buf_end = audio_stream_get_end_addr(&sink->stream), + .ptr = audio_stream_get_wptr(&sink->stream), + }; + + ret = process(&cir_src, source_channels, &cir_snk, sink_channels, source_samples, chmap); buffer_stream_writeback(sink, sink_bytes); @@ -443,8 +454,19 @@ int dma_buffer_copy_to(struct comp_buffer *source, buffer_stream_invalidate(source, source_bytes); - /* process data */ - ret = process(&source->stream, 0, ostream, 0, source_samples, chmap); + /* process data; the converter consumes cir_buf descriptors */ + struct cir_buf_source cir_src = { + .buf_start = audio_stream_get_addr(&source->stream), + .buf_end = audio_stream_get_end_addr(&source->stream), + .ptr = audio_stream_get_rptr(&source->stream), + }; + struct cir_buf_sink cir_snk = { + .buf_start = audio_stream_get_addr(ostream), + .buf_end = audio_stream_get_end_addr(ostream), + .ptr = audio_stream_get_wptr(ostream), + }; + + ret = process(&cir_src, source_channels, &cir_snk, sink_channels, source_samples, chmap); /* sink buffer contains data meant to copied to DMA */ audio_stream_writeback(ostream, sink_bytes); @@ -505,8 +527,19 @@ int stream_copy_from_no_consume(struct comp_dev *dev, struct comp_buffer *source source_samples = frames * source_channels; } - /* process data */ - ret = process(istream, 0, &sink->stream, 0, source_samples, chmap); + /* process data; the converter consumes cir_buf descriptors */ + struct cir_buf_source cir_src = { + .buf_start = audio_stream_get_addr(istream), + .buf_end = audio_stream_get_end_addr(istream), + .ptr = audio_stream_get_rptr(istream), + }; + struct cir_buf_sink cir_snk = { + .buf_start = audio_stream_get_addr(&sink->stream), + .buf_end = audio_stream_get_end_addr(&sink->stream), + .ptr = audio_stream_get_wptr(&sink->stream), + }; + + ret = process(&cir_src, source_channels, &cir_snk, sink_channels, source_samples, chmap); #if CONFIG_INTEL_ADSP_MIC_PRIVACY struct processing_module *mod = comp_mod(dev); diff --git a/src/lib/notifier.c b/src/lib/notifier.c index 8ec3d02f8a82..20770bd828a2 100644 --- a/src/lib/notifier.c +++ b/src/lib/notifier.c @@ -26,7 +26,7 @@ SOF_DEFINE_REG_UUID(notifier); DECLARE_TR_CTX(nt_tr, SOF_UUID(notifier_uuid), LOG_LEVEL_INFO); -static SHARED_DATA struct notify_data notify_data_shared[CONFIG_CORE_COUNT]; +static struct notify_data notify_data_shared[CONFIG_CORE_COUNT]; struct callback_handle { void *receiver; @@ -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; } @@ -190,14 +190,17 @@ void notifier_event(const void *caller, enum notify_id type, uint32_t core_mask, } } -void init_system_notify(struct sof *sof) +__cold void init_system_notify(struct sof *sof) { struct notify **notify = arch_notify_get(); int i; + + assert_can_be_cold(); + *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); } @@ -206,8 +209,7 @@ void init_system_notify(struct sof *sof) list_init(&(*notify)->list[i]); if (cpu_get_id() == PLATFORM_PRIMARY_CORE_ID) - sof->notify_data = platform_shared_get(notify_data_shared, - sizeof(notify_data_shared)); + sof->notify_data = notify_data_shared; } void free_system_notify(void) diff --git a/src/lib/objpool.c b/src/lib/objpool.c new file mode 100644 index 000000000000..9636e018d3d1 --- /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, + aligned_size + sizeof(*pobjpool)); + else + pobjpool = vregion_alloc(head->vreg, + 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..a672f51133d2 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> @@ -34,6 +37,7 @@ #if CONFIG_LLEXT #include <zephyr/llext/llext.h> #endif +#include <zephyr/sys/math_extras.h> #if CONFIG_LIBRARY_AUTH_SUPPORT #include <auth/intel_auth_api.h> @@ -107,16 +111,17 @@ static int lib_manager_auth_proc(const void *buffer_data, size_t buffer_size, while (auth_api_busy(auth_ctx)) ; + if (phase != AUTH_PHASE_LAST) + return 0; + ret = auth_api_result(auth_ctx); + auth_api_cleanup(auth_ctx); if (ret != AUTH_IMAGE_TRUSTED) { tr_err(&lib_manager_tr, "Untrusted library!"); return -EACCES; } - if (phase == AUTH_PHASE_LAST) - auth_api_cleanup(auth_ctx); - return 0; } #endif /* CONFIG_LIBRARY_AUTH_SUPPORT */ @@ -125,7 +130,7 @@ static int lib_manager_auth_proc(const void *buffer_data, size_t buffer_size, #define PAGE_SZ CONFIG_MM_DRV_PAGE_SIZE -static int lib_manager_load_data_from_storage(void __sparse_cache *vma, void *s_addr, uint32_t size, +static int lib_manager_load_data_from_storage(void __sparse_cache *vma, void *s_addr, size_t size, uint32_t flags) { /* Region must be first mapped as writable in order to initialize its contents. */ @@ -143,16 +148,26 @@ static int lib_manager_load_data_from_storage(void __sparse_cache *vma, void *s_ return sys_mm_drv_update_region_flags((__sparse_force void *)vma, size, flags); } -static int lib_manager_load_module(const uint32_t module_id, const struct sof_man_module *const mod) +static int lib_manager_load_module(const struct sof_man_fw_desc *const desc, + const uint32_t module_id, const struct sof_man_module *const mod) { struct lib_manager_mod_ctx *ctx = lib_manager_get_mod_ctx(module_id); const uintptr_t load_offset = POINTER_TO_UINT(ctx->base_addr); + size_t lib_size; + size_t file_offset; void *src; void __sparse_cache *va_base; size_t size; uint32_t flags; int ret, idx; + /* + * ELF file segment offsets and sizes come from library files, so they + * have to be validated. + */ + if (size_mul_overflow(desc->header.preload_page_count, PAGE_SZ, &lib_size)) + return -EOVERFLOW; + for (idx = 0; idx < ARRAY_SIZE(mod->segment); ++idx) { if (!mod->segment[idx].flags.r.load) continue; @@ -164,9 +179,23 @@ static int lib_manager_load_module(const uint32_t module_id, const struct sof_ma else if (!mod->segment[idx].flags.r.readonly) flags = SYS_MM_MEM_PERM_RW; - src = UINT_TO_POINTER(mod->segment[idx].file_offset + load_offset); + file_offset = mod->segment[idx].file_offset; + if (size_mul_overflow(mod->segment[idx].flags.r.length, PAGE_SZ, &size)) { + ret = -EOVERFLOW; + goto err; + } + + /* Reject segments that would read outside the loaded library image. */ + if (file_offset > lib_size || size > lib_size - file_offset) { + tr_err(&lib_manager_tr, + "segment %d out of bounds: file_offset %#zx, size %#zx, total %#zx", + idx, file_offset, size, lib_size); + ret = -ENOSPC; + goto err; + } + + src = UINT_TO_POINTER(file_offset + load_offset); va_base = (void __sparse_cache *)UINT_TO_POINTER(mod->segment[idx].v_base_addr); - size = mod->segment[idx].flags.r.length * PAGE_SZ; ret = lib_manager_load_data_from_storage(va_base, src, size, flags); if (ret < 0) goto err; @@ -226,7 +255,8 @@ static int lib_manager_load_libcode_modules(const uint32_t module_id) for (idx = 0; idx < desc->header.num_module_entries; ++idx, ++module_entry) { if (module_entry->type.lib_code) { - ret = lib_manager_load_module(lib_id << LIB_MANAGER_LIB_ID_SHIFT | idx, + ret = lib_manager_load_module(desc, + lib_id << LIB_MANAGER_LIB_ID_SHIFT | idx, module_entry); if (ret < 0) goto err; @@ -277,7 +307,10 @@ 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) { - *size = mod->segment[SOF_MAN_SEGMENT_BSS].flags.r.length / mod->instance_max_count * + /* mod->instance_max_count should never be 0, assume 1 if that happens */ + unsigned int inst_cnt = mod->instance_max_count ? : 1; + + *size = mod->segment[SOF_MAN_SEGMENT_BSS].flags.r.length / inst_cnt * PAGE_SZ; size_t inst_offset = *size * instance_id; *va_addr = (void __sparse_cache *)(mod->segment[SOF_MAN_SEGMENT_BSS].v_base_addr + @@ -334,7 +367,8 @@ static int lib_manager_free_module_instance(uint32_t instance_id, const struct s * * Function is responsible to allocate module in available free memory and assigning proper address. */ -static uintptr_t lib_manager_allocate_module(const struct sof_man_module *mod, +static uintptr_t lib_manager_allocate_module(const struct sof_man_fw_desc *const desc, + const struct sof_man_module *mod, const struct comp_ipc_config *ipc_config, const void *ipc_specific_config) { @@ -347,7 +381,7 @@ static uintptr_t lib_manager_allocate_module(const struct sof_man_module *mod, if (module_is_llext(mod)) return llext_manager_allocate_module(ipc_config, ipc_specific_config); - ret = lib_manager_load_module(module_id, mod); + ret = lib_manager_load_module(desc, module_id, mod); if (ret < 0) return 0; @@ -373,7 +407,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); @@ -381,15 +423,18 @@ int lib_manager_free_module(const uint32_t component_id) tr_dbg(&lib_manager_tr, "mod_id: %#x", component_id); + const struct lib_manager_mod_ctx *const ctx = lib_manager_get_mod_ctx(module_id); + + if (ctx->load_type == SOF_MAN_MOD_TYPE_LLEXT || + ctx->load_type == SOF_MAN_MOD_TYPE_LLEXT_AUX) + return llext_manager_free_module(component_id); + mod = lib_manager_get_module_manifest(module_id); if (!mod) { tr_err(&lib_manager_tr, "failed to get module descriptor"); return -EINVAL; } - if (module_is_llext(mod)) - return llext_manager_free_module(component_id); - ret = lib_manager_unload_module(mod); if (ret < 0) return ret; @@ -412,14 +457,15 @@ 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 sof_man_fw_desc *const desc, + 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,11 +498,12 @@ 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); } ctx->base_addr = base_addr; + ctx->load_type = SOF_MAN_MOD_TYPE_MODULE; _ext_lib->desc[lib_id] = ctx; /* TODO: maybe need to call dcache_writeback here? */ @@ -487,34 +534,55 @@ 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; + int ret = 0; mod_cfg.data = (uint8_t *)args->data; /* 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); - if (ret) - tr_err(&lib_manager_tr, "System agent start failed %d!", ret); + 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); + } else +#endif /* CONFIG_SOF_USERSPACE_PROXY */ + if (agent) { + ret = agent(&agent_params, agent_interface); + if (ret) + tr_err(&lib_manager_tr, "System agent start failed %d!", ret); + } return ret; } @@ -535,6 +603,8 @@ static enum buildinfo_mod_type lib_manager_get_module_type(const struct sof_man_ const struct sof_module_api_build_info *const build_info = (const struct sof_module_api_build_info *)((const char *)desc - SOF_MAN_ELF_TEXT_OFFSET + mod->segment[SOF_MAN_SEGMENT_TEXT].file_offset); + const size_t lib_size = (size_t)desc->header.preload_page_count * PAGE_SZ; + const uint32_t text_off = mod->segment[SOF_MAN_SEGMENT_TEXT].file_offset; /* * llext modules store build info structure in separate section which is not accessible now. @@ -542,6 +612,17 @@ static enum buildinfo_mod_type lib_manager_get_module_type(const struct sof_man_ if (module_is_llext(mod)) return MOD_TYPE_LLEXT; + /* + * build_info is derived from a manifest-supplied file_offset; bound it + * against the library image size before dereferencing so a crafted + * offset cannot read outside the library buffer. + */ + if (text_off > lib_size || lib_size - text_off < sizeof(*build_info)) { + tr_err(&lib_manager_tr, "Invalid TEXT file_offset %u, lib_size %zu", + text_off, lib_size); + return MOD_TYPE_INVALID; + } + tr_info(&lib_manager_tr, "Module API version: %u.%u.%u, format: 0x%x", build_info->api_version_number.fields.major, build_info->api_version_number.fields.middle, @@ -578,9 +659,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 +670,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.", @@ -603,7 +692,8 @@ static struct comp_dev *lib_manager_module_create(const struct comp_driver *drv, mod = (const struct sof_man_module *) ((const uint8_t *)desc + SOF_MAN_MODULE_OFFSET(entry_index)); - const uintptr_t module_entry_point = lib_manager_allocate_module(mod, config, args->data); + const uintptr_t module_entry_point = lib_manager_allocate_module(desc, mod, config, + args->data); if (!module_entry_point) { tr_err(&lib_manager_tr, "lib_manager_allocate_module() failed!"); @@ -614,40 +704,45 @@ static struct comp_dev *lib_manager_module_create(const struct comp_driver *drv, case MOD_TYPE_LLEXT: agent = NULL; ops = (const struct module_interface *)module_entry_point; + agent_iface = NULL; break; case MOD_TYPE_LMDK: 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); + if (agent || IS_ENABLED(CONFIG_SOF_USERSPACE_PROXY)) { + /* At this point module resources are allocated and it is moved to L2 memory. */ + 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, NULL); 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; } @@ -669,7 +764,8 @@ static void lib_manager_module_free(struct comp_dev *dev) static void lib_manager_prepare_module_adapter(struct comp_driver *drv, const struct sof_uuid *uuid) { drv->type = SOF_COMP_MODULE_ADAPTER; - drv->uid = uuid; + drv->uid_cp = *uuid; + drv->uid = &drv->uid_cp; drv->tctx = &lib_manager_tr; drv->ops.create = lib_manager_module_create; drv->ops.prepare = module_adapter_prepare; @@ -694,18 +790,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 +816,28 @@ 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; + } + } +#else + drv_heap = sof_sys_user_heap_get(); +#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 +849,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; @@ -787,6 +896,9 @@ static int lib_manager_dma_init(struct lib_manager_dma_ext *dma_ext, uint32_t dm } chan_index = dma_request_channel(dma_ext->dma->z_dev, &dma_id); + if (chan_index < 0) + return chan_index; + dma_ext->chan = &dma_ext->dma->chan[chan_index]; if (!dma_ext->chan) return -EINVAL; @@ -892,9 +1004,17 @@ static int lib_manager_store_library(struct lib_manager_dma_ext *dma_ext, void __sparse_cache *library_base_address; const struct sof_man_fw_desc *man_desc = (struct sof_man_fw_desc *) ((__sparse_force uint8_t *)man_buffer + SOF_MAN_ELF_TEXT_OFFSET); - uint32_t preload_size = man_desc->header.preload_page_count * PAGE_SZ; int ret; + /* Zephyr UINT_MAX is explicitly 32 bits, and so is preload_page_count */ + if (man_desc->header.preload_page_count >= UINT_MAX / PAGE_SZ) { + tr_err(&lib_manager_tr, "Invalid preload page count %u.", + man_desc->header.preload_page_count); + return -EINVAL; + } + + uint32_t preload_size = man_desc->header.preload_page_count * PAGE_SZ; + /* * The module manifest structure always has its maximum size regardless of * the actual size of the manifest. @@ -904,10 +1024,10 @@ static int lib_manager_store_library(struct lib_manager_dma_ext *dma_ext, return -EINVAL; } - /* Prepare storage memory, note: it is never freed, library unloading is unsupported */ /* - * Prepare storage memory, note: it is never freed, it is assumed, that this - * memory is abundant, so we store all loaded modules there permanently + * Prepare storage memory, note: it is never freed, it is assumed, that + * this memory is abundant, so we store all loaded modules there + * permanently, unloading is unsupported */ library_base_address = lib_manager_allocate_store_mem(preload_size, 0); if (!library_base_address) @@ -937,6 +1057,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, @@ -1105,7 +1228,7 @@ int lib_manager_load_library(uint32_t dma_id, uint32_t lib_id, uint32_t type) uint32_t module_id = lib_id << LIB_MANAGER_LIB_ID_SHIFT; const struct sof_man_module *mod = lib_manager_get_module_manifest(module_id); - if (!ret && module_is_llext(mod)) + if (!ret && mod && module_is_llext(mod)) /* Auxiliary LLEXT libraries need to be linked upon loading */ ret = llext_manager_add_library(module_id); diff --git a/src/library_manager/llext_manager.c b/src/library_manager/llext_manager.c index 9c032319f82d..69e27c0c964e 100644 --- a/src/library_manager/llext_manager.c +++ b/src/library_manager/llext_manager.c @@ -10,6 +10,7 @@ */ #include <sof/audio/buffer.h> +#include <sof/audio/component.h> #include <sof/audio/component_ext.h> #include <sof/common.h> #include <sof/compiler_attributes.h> @@ -23,14 +24,17 @@ #include <sof/llext_manager.h> #include <sof/audio/module_adapter/module/generic.h> #include <sof/audio/module_adapter/module/modules.h> +#include <sof/schedule/ll_schedule_domain.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> #include <zephyr/llext/llext.h> #include <zephyr/logging/log_ctrl.h> #include <zephyr/llext/inspect.h> +#include <zephyr/sys/math_extras.h> #include <kernel_arch_interface.h> #include <rimage/sof/user/manifest.h> @@ -124,7 +128,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; } @@ -164,6 +168,38 @@ static int llext_manager_load_data_from_storage(const struct sys_mm_drv_region * return ret; } +#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); +} +#endif + static void llext_manager_unmap_detached_sections(const struct llext_loader *ldr, const struct llext *ext, enum llext_mem region, @@ -197,6 +233,13 @@ static void llext_manager_unmap_detached_sections(const struct llext_loader *ldr #endif } +#ifdef CONFIG_USERSPACE +static int llext_manager_add_mod_domain(struct lib_manager_module *mctx, + struct k_mem_domain *domain); +static int llext_manager_rm_mod_domain(struct lib_manager_module *mctx, + struct k_mem_domain *domain); +#endif + static int llext_manager_load_module(struct lib_manager_module *mctx) { /* Executable code (.text) */ @@ -226,9 +269,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 +296,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 */ @@ -283,10 +333,25 @@ static int llext_manager_load_module(struct lib_manager_module *mctx) memset((__sparse_force void *)bss_addr, 0, bss_size); mctx->mapped = true; - return 0; +#ifdef CONFIG_SOF_USERSPACE_LL + if (!mctx->domain_dp) { + ret = llext_manager_add_mod_domain(mctx, zephyr_ll_mem_domain()); + if (ret < 0) { + tr_err(&lib_manager_tr, "failed to add domain: %d", ret); + goto e_data; + } + } +#endif + return 0; +#ifdef CONFIG_SOF_USERSPACE_LL +e_data: + if (data_size) + llext_manager_align_unmap(va_base_data, data_size); +#endif 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,16 +376,37 @@ 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; +#ifdef CONFIG_SOF_USERSPACE_LL + unsigned int sect_cnt = llext_section_count(ext); + size_t total = sect_cnt * sizeof(elf_shdr_t); + const elf_shdr_t *shdr; + + ret = llext_get_section_info(ldr, ext, 0, &shdr, NULL, NULL); + if (ret < 0) + return ret; + + /* Temporarily map ELF section headers */ + llext_manager_add_partition(zephyr_ll_mem_domain(), (uintptr_t)shdr, total, + K_MEM_PARTITION_P_RW_U_NA | XTENSA_MMU_CACHED_WB); +#endif llext_manager_unmap_detached_sections(ldr, ext, LLEXT_MEM_TEXT, va_base_text, text_size); ret = llext_manager_align_unmap(va_base_text, text_size); 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); @@ -333,8 +419,17 @@ static int llext_manager_unload_module(struct lib_manager_module *mctx) if (ret < 0 && !err) err = ret; +#ifdef CONFIG_SOF_USERSPACE_LL + llext_manager_rm_partition(zephyr_ll_mem_domain(), (uintptr_t)shdr, total, + K_MEM_PARTITION_P_RW_U_NA | XTENSA_MMU_CACHED_WB); +#endif mctx->mapped = false; +#ifdef CONFIG_SOF_USERSPACE_LL + if (!mctx->domain_dp) + llext_manager_rm_mod_domain(mctx, zephyr_ll_mem_domain()); +#endif + return err; } @@ -440,9 +535,22 @@ static int llext_manager_mod_init(struct lib_manager_mod_ctx *ctx, { struct sof_man_module *mod_array = (struct sof_man_module *)((uint8_t *)desc + SOF_MAN_MODULE_OFFSET(0)); + /* preload_page_count was checked when library was loaded */ + size_t lib_size = desc->header.preload_page_count * PAGE_SZ; unsigned int i, n_mod; size_t offs; + /* We'll check overflows below */ + uintptr_t mod_end_addr = (uintptr_t)(mod_array + desc->header.num_module_entries); + uintptr_t img_end_addr = (uintptr_t)desc - SOF_MAN_ELF_TEXT_OFFSET + lib_size; + + if (mod_end_addr < (uintptr_t)mod_array || img_end_addr < (uintptr_t)desc || + mod_end_addr >= img_end_addr) { + tr_err(&lib_manager_tr, "invalid module entry count: %u", + desc->header.num_module_entries); + return -EOVERFLOW; + } + /* count modules */ for (i = 0, n_mod = 0, offs = ~0; i < desc->header.num_module_entries; i++) if (mod_array[i].segment[LIB_MANAGER_TEXT].file_offset != offs) { @@ -465,17 +573,23 @@ static int llext_manager_mod_init(struct lib_manager_mod_ctx *ctx, if (mod_array[i].segment[LIB_MANAGER_TEXT].file_offset != offs) { offs = mod_array[i].segment[LIB_MANAGER_TEXT].file_offset; ctx->mod[n_mod].mapped = false; + ctx->mod[n_mod].domain_dp = false; ctx->mod[n_mod].llext = NULL; ctx->mod[n_mod].ebl = NULL; ctx->mod[n_mod].n_dependent = 0; - ctx->mod[n_mod++].start_idx = i; + ctx->mod[n_mod].start_idx = i; + if (n_mod) + ctx->mod[n_mod - 1].n_mod = i - ctx->mod[n_mod - 1].start_idx; + n_mod++; } + ctx->mod[n_mod - 1].n_mod = desc->header.num_module_entries - ctx->mod[n_mod - 1].start_idx; + return 0; } /* Find a module context, containing the driver with the supplied index */ -static unsigned int llext_manager_mod_find(const struct lib_manager_mod_ctx *ctx, unsigned int idx) +static int llext_manager_mod_find(const struct lib_manager_mod_ctx *ctx, unsigned int idx) { unsigned int i; @@ -483,6 +597,12 @@ static unsigned int llext_manager_mod_find(const struct lib_manager_mod_ctx *ctx if (ctx->mod[i].start_idx > idx) break; + if (i == ctx->n_mod && ctx->mod[i - 1].start_idx + ctx->mod[i - 1].n_mod <= idx) { + tr_err(&lib_manager_tr, "%u beyond %u + %u after %u", idx, + ctx->mod[i - 1].start_idx, ctx->mod[i - 1].n_mod, i); + return -ENOENT; + } + return i - 1; } @@ -504,7 +624,11 @@ static int llext_manager_link_single(uint32_t module_id, const struct sof_man_fw return -EINVAL; } - unsigned int mod_ctx_idx = llext_manager_mod_find(ctx, entry_index); + int mod_ctx_idx = llext_manager_mod_find(ctx, entry_index); + + if (mod_ctx_idx < 0) + return mod_ctx_idx; + struct lib_manager_module *mctx = ctx->mod + mod_ctx_idx; size_t mod_size; int i, inst_idx; @@ -707,6 +831,8 @@ uintptr_t llext_manager_allocate_module(const struct comp_ipc_config *ipc_config dep_ctx[i] = dep; } + /* Avoid mapping DP modules to the LL domain */ + mctx->domain_dp = ipc_config->proc_domain == COMP_PROCESSING_DOMAIN_DP; /* Map executable code and data */ ret = llext_manager_load_module(mctx); if (ret < 0) @@ -716,25 +842,269 @@ uintptr_t llext_manager_allocate_module(const struct comp_ipc_config *ipc_config return mod_manifest->module.entry_point; } -int llext_manager_free_module(const uint32_t component_id) +#ifdef CONFIG_USERSPACE + +static int llext_manager_add_mod_domain(struct lib_manager_module *mctx, struct k_mem_domain *domain) +{ + 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; + + /* + * Add to domain on first load: for "normal" modules use_count == 1, + * for dependencies use_count == 2 and n_dependent == 1. + * This does not apply to DP modules, since they are, for now, + * all in their own memory domains. + * + * NOTE: This approach will make it impossible for a single + * module to have both LL and DP instances within one + * configuration. This will be fixed in the future. + */ + if (!mctx->domain_dp && ext->use_count > 1 && mctx->n_dependent != 1) + return 0; + + 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_add_domain(const uint32_t component_id, struct k_mem_domain *domain) { const uint32_t module_id = IPC4_MOD_ID(component_id); - struct sof_man_fw_desc *desc = (struct sof_man_fw_desc *)lib_manager_get_library_manifest(module_id); struct lib_manager_mod_ctx *ctx = lib_manager_get_mod_ctx(module_id); - uint32_t entry_index = LIB_MANAGER_GET_MODULE_INDEX(module_id); + const uint32_t entry_index = LIB_MANAGER_GET_MODULE_INDEX(module_id); + const int mod_idx = llext_manager_mod_find(ctx, entry_index); - if (entry_index >= desc->header.num_module_entries) { - tr_err(&lib_manager_tr, "Invalid driver index %u exceeds %d", - entry_index, desc->header.num_module_entries - 1); - return -ENOENT; + if (mod_idx < 0) + return mod_idx; + + struct lib_manager_module *mctx = ctx->mod + mod_idx; + + /* FIXME: handle dependencies */ + return llext_manager_add_mod_domain(mctx, domain); +} + +static int llext_manager_rm_mod_domain(struct lib_manager_module *mctx, struct k_mem_domain *domain) +{ + /* 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; +} + +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 int mod_idx = llext_manager_mod_find(ctx, entry_index); + + if (mod_idx < 0) + return mod_idx; + + struct lib_manager_module *mctx = ctx->mod + mod_idx; + + return llext_manager_rm_mod_domain(mctx, domain); +} +#endif + +int llext_manager_free_module(const uint32_t component_id) +{ + const uint32_t module_id = IPC4_MOD_ID(component_id); + struct lib_manager_mod_ctx *ctx = lib_manager_get_mod_ctx(module_id); + uint32_t entry_index = LIB_MANAGER_GET_MODULE_INDEX(module_id); + if (!ctx->mod) { tr_err(&lib_manager_tr, "NULL module array: ID %#x ctx %p", component_id, ctx); return -ENOENT; } - unsigned int mod_idx = llext_manager_mod_find(ctx, entry_index); + int mod_idx = llext_manager_mod_find(ctx, entry_index); + + if (mod_idx < 0) + return mod_idx; + struct lib_manager_module *mctx = ctx->mod + mod_idx; /* Protected by IPC serialization */ @@ -795,9 +1165,13 @@ int llext_manager_add_library(uint32_t module_id) const struct sof_man_fw_desc *desc = lib_manager_get_library_manifest(module_id); unsigned int i; + int ret; - if (!ctx->mod) - llext_manager_mod_init(ctx, desc); + if (!ctx->mod) { + ret = llext_manager_mod_init(ctx, desc); + if (ret < 0) + return ret; + } for (i = 0; i < ctx->n_mod; i++) { const struct sof_man_module *mod = lib_manager_get_module_manifest(module_id + i); @@ -805,13 +1179,14 @@ int llext_manager_add_library(uint32_t module_id) if (mod->type.load_type == SOF_MAN_MOD_TYPE_LLEXT_AUX) { const struct sof_man_module_manifest *mod_manifest; const struct sof_module_api_build_info *buildinfo; - int ret = llext_manager_link_single(module_id + i, desc, ctx, - (const void **)&buildinfo, &mod_manifest); + ret = llext_manager_link_single(module_id + i, desc, ctx, + (const void **)&buildinfo, &mod_manifest); if (ret < 0) return ret; } } + ctx->load_type = SOF_MAN_MOD_TYPE_LLEXT; return 0; } diff --git a/src/library_manager/llext_manager_dram.c b/src/library_manager/llext_manager_dram.c index 9c134a3fcd81..2f6cff2b3501 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++) { @@ -190,10 +200,14 @@ int llext_manager_restore_from_dram(void) continue; } - /* Panics on failure - use the same zone as during the first boot */ struct lib_manager_mod_ctx *ctx = rmalloc(SOF_MEM_FLAG_KERNEL | SOF_MEM_FLAG_COHERENT, sizeof(*ctx)); + if (!ctx) { + tr_err(&lib_manager_tr, "library context allocation failure"); + goto nomem; + } + /* Restore the library context */ *ctx = lib_manager_dram.ctx[j++]; @@ -262,26 +276,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..0abd10de9308 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> @@ -11,10 +12,13 @@ #include <sof/math/fft.h> #include <sof/math/log.h> #include <sof/math/numbers.h> +#include <sof/trace/trace.h> #include <ipc/topology.h> #include <errno.h> #include <stdint.h> +LOG_MODULE_REGISTER(math_auditory, CONFIG_SOF_LOG_LEVEL); + #define ONE_Q16 Q_CONVERT_FLOAT(1, 16) #define ONE_Q20 Q_CONVERT_FLOAT(1, 20) #define ONE_Q30 Q_CONVERT_FLOAT(1, 30) @@ -85,7 +89,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; @@ -116,6 +120,16 @@ int psy_get_mel_filterbank(struct psy_mel_filterbank *fb) if (!fb->scratch_data1 || !fb->scratch_data2) return -ENOMEM; + /* fb->mel_bins is used both as the loop bound and as part of the + * (mel_bins + 1) divisor below. Reject non-positive values up front to + * avoid a divide by zero (mel_bins == -1) and to keep mel_step + * meaningful. + */ + if (fb->mel_bins <= 0 || fb->mel_bins > AUDITORY_MAX_MEL_BANDS) { + comp_cl_err(mod->dev, "Invalid mel_bins %d", fb->mel_bins); + return -EINVAL; + } + /* Log power can be log, or log10 or dB, get multiply coef to convert * log to desired format. */ @@ -148,6 +162,17 @@ int psy_get_mel_filterbank(struct psy_mel_filterbank *fb) mel_start = psy_hz_to_mel(fb->start_freq); mel_end = psy_hz_to_mel(fb->end_freq); mel_step = (mel_end - mel_start) / (fb->mel_bins + 1); + /* delta_cl / delta_rc below are both equal to mel_step; guard against + * a non-positive step (start_freq >= end_freq, or so many bins that + * the integer division truncates to zero) before using them as + * divisors. + */ + if (mel_step <= 0) { + comp_cl_err(mod->dev, "Invalid mel_step %d (start_freq=%d end_freq=%d mel_bins=%d)", + mel_step, fb->start_freq, fb->end_freq, fb->mel_bins); + return -EINVAL; + } + for (i = 0; i < fb->mel_bins; i++) { left_mel = mel_start + i * mel_step; center_mel = mel_start + (i + 1) * mel_step; @@ -159,6 +184,11 @@ int psy_get_mel_filterbank(struct psy_mel_filterbank *fb) if (fb->slaney_normalize) { left_hz = psy_mel_to_hz(left_mel); right_hz = psy_mel_to_hz(right_mel); + if (right_hz <= left_hz) { + comp_cl_err(mod->dev, "Invalid Hz range left=%d right=%d at mel bin %d", + left_hz, right_hz, i); + return -EINVAL; + } scale = Q_SHIFT_RND(TWO_Q29 / (right_hz - left_hz), 29, 16); /* Q16.16*/ if (i == 0) { scale_inv = Q_SHIFT_LEFT(ONE_Q30 / scale, 14, 16); @@ -200,8 +230,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 +239,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..589571881443 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,8 @@ 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."); + mod_free(mod, plan); return NULL; } @@ -47,27 +65,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..794eb3002c47 --- /dev/null +++ b/src/math/fft/fft_multi.c @@ -0,0 +1,139 @@ +// 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; + } + + /* 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; + + 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: + /* mod_fft_multi_plan_free() handles partial state safely: + * - fft_plan[i] entries left NULL by mod_zalloc are skipped by mod_free, + * - tmp_i32[0] is freed only when num_ffts > 1, so it does not free + * the caller-provided inb in the single-FFT case. + */ + mod_fft_multi_plan_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/iir_df1.c b/src/math/iir_df1.c index 861c5cd2cf19..5e4d13b8cc70 100644 --- a/src/math/iir_df1.c +++ b/src/math/iir_df1.c @@ -16,9 +16,13 @@ int iir_delay_size_df1(struct sof_eq_iir_header *config) { - int n = config->num_sections; /* One section uses two unit delays */ + uint32_t n = config->num_sections; /* One section uses four unit delays */ - if (n > SOF_EQ_IIR_BIQUADS_MAX || n < 1) + if (!n || n > SOF_EQ_IIR_BIQUADS_MAX) + return -EINVAL; + + if (!config->num_sections_in_series || + config->num_sections_in_series > n) return -EINVAL; return 4 * n * sizeof(int32_t); diff --git a/src/math/iir_df2t.c b/src/math/iir_df2t.c index b2adb69be03f..ed2b50a21a4e 100644 --- a/src/math/iir_df2t.c +++ b/src/math/iir_df2t.c @@ -16,9 +16,13 @@ int iir_delay_size_df2t(struct sof_eq_iir_header *config) { - int n = config->num_sections; /* One section uses two unit delays */ + uint32_t n = config->num_sections; /* One section uses two unit delays */ - if (n > SOF_EQ_IIR_BIQUADS_MAX || n < 1) + if (!n || n > SOF_EQ_IIR_BIQUADS_MAX) + return -EINVAL; + + if (!config->num_sections_in_series || + config->num_sections_in_series > n) return -EINVAL; return 2 * n * sizeof(int64_t); 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/module/audio/sink_api.c b/src/module/audio/sink_api.c index c0a94031f105..969b37f5a987 100644 --- a/src/module/audio/sink_api.c +++ b/src/module/audio/sink_api.c @@ -109,6 +109,11 @@ EXPORT_SYMBOL(sink_get_frame_bytes); size_t sink_get_free_frames(struct sof_sink *sink) { + /* The frame size is a valid divisor: a host channel count of zero is + * rejected at module init (module_adapter_ipc4.c) before it reaches + * the stream, and the sample size is fixed by a valid frame format, so + * this is not re-checked on the hot path. + */ return sink_get_free_size(sink) / sink_get_frame_bytes(sink); } EXPORT_SYMBOL(sink_get_free_frames); 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..b2576dd2fb21 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,10 +247,23 @@ 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 +config ACP_7_X + bool "Build for ACP_7_X" + select XT_INTERRUPT_LEVEL_5 + select XT_INTERRUPT_LEVEL_3 + select XT_INTERRUPT_LEVEL_1 + select XT_INTERRUPT_LEVEL_4 + select XT_WAITI_DELAY + select XTENSA_EXCLUSIVE + select AMD + select AMD_BINARY_BUILD + help + Select if your target platform is acp_7_x-compatible + config MT8186 bool "Build for MTK MT8186 (XTOS)" select XT_INTERRUPT_LEVEL_1 @@ -304,7 +325,7 @@ config MT8365 config MTK bool "Build for Mediatek (Zephyr)" - select SCHEDULE_DMA_MULTI_CHANNEL + select SCHEDULE_DMA_MULTI_CHANNEL if !ZEPHYR_NATIVE_DRIVERS select HOST_PTABLE help Select if your target is a Mediatek DSP. Builds Zephyr firmware. @@ -332,21 +353,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,12 +549,15 @@ 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 default "acp_6_3" if ACP_6_3 default "acp_7_0" if ACP_7_0 + default "acp_7_x" if ACP_7_X default "mt8186" if MT8186 default "mt8188" if MT8188 default "mt8195" if MT8195 diff --git a/src/platform/amd/CMakeLists.txt b/src/platform/amd/CMakeLists.txt index 5b971f6e4b9e..9ae93911168d 100644 --- a/src/platform/amd/CMakeLists.txt +++ b/src/platform/amd/CMakeLists.txt @@ -1,5 +1,7 @@ # SPDX-License-Identifier: BSD-3-Clause -add_subdirectory(common) +if(CONFIG_RENOIR OR CONFIG_VANGOGH OR CONFIG_REMBRANDT OR CONFIG_ACP_6_3 OR CONFIG_ACP_7_0) + add_subdirectory(common) +endif() if(CONFIG_RENOIR) add_subdirectory(renoir) elseif(CONFIG_REMBRANDT) @@ -10,4 +12,6 @@ elseif(CONFIG_ACP_6_3) add_subdirectory(acp_6_3) elseif(CONFIG_ACP_7_0) add_subdirectory(acp_7_0) +elseif(CONFIG_ACP_7_X) + add_subdirectory(acp_7_x) endif() diff --git a/src/platform/amd/acp_6_3/include/platform/lib/memory.h b/src/platform/amd/acp_6_3/include/platform/lib/memory.h index 2597b0858715..6c9e7c7f5b36 100644 --- a/src/platform/amd/acp_6_3/include/platform/lib/memory.h +++ b/src/platform/amd/acp_6_3/include/platform/lib/memory.h @@ -162,14 +162,8 @@ #if !defined(__ASSEMBLER__) && !defined(LINKER) struct sof; -#define SHARED_DATA void platform_init_memmap(struct sof *sof); -static inline void *platform_shared_get(void *ptr, int bytes) -{ - return ptr; -} - static inline void *platform_rfree_prepare(void *ptr) { return ptr; diff --git a/src/platform/amd/acp_6_3/lib/clk.c b/src/platform/amd/acp_6_3/lib/clk.c index 5a735d4241fa..9b39b46be987 100644 --- a/src/platform/amd/acp_6_3/lib/clk.c +++ b/src/platform/amd/acp_6_3/lib/clk.c @@ -51,7 +51,7 @@ const struct freq_table platform_cpu_freq[] = { STATIC_ASSERT(ARRAY_SIZE(platform_cpu_freq) == NUM_CPU_FREQ, invalid_number_of_cpu_frequencies); -static SHARED_DATA struct clock_info platform_clocks_info[NUM_CLOCKS]; +static struct clock_info platform_clocks_info[NUM_CLOCKS]; void audio_pll_power_off(void); void audio_pll_power_on(void); @@ -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_6_3/lib/dma.c b/src/platform/amd/acp_6_3/lib/dma.c index 01da5ce0b528..e41c081af9e9 100644 --- a/src/platform/amd/acp_6_3/lib/dma.c +++ b/src/platform/amd/acp_6_3/lib/dma.c @@ -25,7 +25,7 @@ extern struct dma_ops acp_dai_sp_dma_ops; extern struct dma_ops acp_dai_hs_dma_ops; extern struct dma_ops acp_dai_sw_audio_dma_ops; -SHARED_DATA struct dma dma[PLATFORM_NUM_DMACS] = { +struct dma dma[PLATFORM_NUM_DMACS] = { { .plat_data = { .id = DMA_ID_DMA0, diff --git a/src/platform/amd/acp_6_3/lib/memory.c b/src/platform/amd/acp_6_3/lib/memory.c index 94047b7c488a..363f110516b5 100644 --- a/src/platform/amd/acp_6_3/lib/memory.c +++ b/src/platform/amd/acp_6_3/lib/memory.c @@ -12,29 +12,29 @@ #include <ipc/topology.h> /* Heap blocks for system runtime */ -static SHARED_DATA struct block_hdr sys_rt_block64[HEAP_SYS_RT_COUNT64]; -static SHARED_DATA struct block_hdr sys_rt_block512[HEAP_SYS_RT_COUNT512]; -static SHARED_DATA struct block_hdr sys_rt_block1024[HEAP_SYS_RT_COUNT1024]; +static struct block_hdr sys_rt_block64[HEAP_SYS_RT_COUNT64]; +static struct block_hdr sys_rt_block512[HEAP_SYS_RT_COUNT512]; +static struct block_hdr sys_rt_block1024[HEAP_SYS_RT_COUNT1024]; /* Heap memory for system runtime */ -static SHARED_DATA struct block_map sys_rt_heap_map[] = { +static struct block_map sys_rt_heap_map[] = { BLOCK_DEF(64, HEAP_SYS_RT_COUNT64, sys_rt_block64), BLOCK_DEF(512, HEAP_SYS_RT_COUNT512, sys_rt_block512), BLOCK_DEF(1024, HEAP_SYS_RT_COUNT1024, sys_rt_block1024), }; /* Heap blocks for modules */ -static SHARED_DATA struct block_hdr mod_block16[HEAP_RT_COUNT16]; -static SHARED_DATA struct block_hdr mod_block32[HEAP_RT_COUNT32]; -static SHARED_DATA struct block_hdr mod_block64[HEAP_RT_COUNT64]; -static SHARED_DATA struct block_hdr mod_block128[HEAP_RT_COUNT128]; -static SHARED_DATA struct block_hdr mod_block256[HEAP_RT_COUNT256]; -static SHARED_DATA struct block_hdr mod_block512[HEAP_RT_COUNT512]; -static SHARED_DATA struct block_hdr mod_block1024[HEAP_RT_COUNT1024]; -static SHARED_DATA struct block_hdr mod_block2048[HEAP_RT_COUNT2048]; +static struct block_hdr mod_block16[HEAP_RT_COUNT16]; +static struct block_hdr mod_block32[HEAP_RT_COUNT32]; +static struct block_hdr mod_block64[HEAP_RT_COUNT64]; +static struct block_hdr mod_block128[HEAP_RT_COUNT128]; +static struct block_hdr mod_block256[HEAP_RT_COUNT256]; +static struct block_hdr mod_block512[HEAP_RT_COUNT512]; +static struct block_hdr mod_block1024[HEAP_RT_COUNT1024]; +static struct block_hdr mod_block2048[HEAP_RT_COUNT2048]; /* Heap memory map for modules */ -static SHARED_DATA struct block_map rt_heap_map[] = { +static struct block_map rt_heap_map[] = { BLOCK_DEF(16, HEAP_RT_COUNT16, mod_block16), BLOCK_DEF(32, HEAP_RT_COUNT32, mod_block32), BLOCK_DEF(64, HEAP_RT_COUNT64, mod_block64), @@ -46,14 +46,14 @@ static SHARED_DATA struct block_map rt_heap_map[] = { }; /* Heap blocks for buffers */ -static SHARED_DATA struct block_hdr buf_block[HEAP_BUFFER_COUNT]; +static struct block_hdr buf_block[HEAP_BUFFER_COUNT]; /* Heap memory map for buffers */ -static SHARED_DATA struct block_map buf_heap_map[] = { +static struct block_map buf_heap_map[] = { BLOCK_DEF(HEAP_BUFFER_BLOCK_SIZE, HEAP_BUFFER_COUNT, buf_block), }; -static SHARED_DATA struct mm memmap = { +static struct mm memmap = { .system[0] = { .heap = HEAP_SYSTEM_BASE, .size = HEAP_SYSTEM_SIZE, diff --git a/src/platform/amd/acp_6_3/platform.c b/src/platform/amd/acp_6_3/platform.c index e0b1f2b8fe9f..e06cf8db0e2c 100644 --- a/src/platform/amd/acp_6_3/platform.c +++ b/src/platform/amd/acp_6_3/platform.c @@ -127,7 +127,7 @@ const struct ext_man_windows xsram_window }, }; -static SHARED_DATA struct timer timer = { +static struct timer timer = { .id = TIMER0, .irq = IRQ_NUM_TIMER0, }; 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..214b638da4df 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 @@ -169,20 +173,14 @@ #if !defined(__ASSEMBLER__) && !defined(LINKER) struct sof; -#define SHARED_DATA void platform_init_memmap(struct sof *sof); -static inline void *platform_shared_get(void *ptr, int bytes) -{ - return ptr; -} - 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..83eb206ee2e9 --- /dev/null +++ b/src/platform/amd/acp_7_0/include/platform/platform_misc.h @@ -0,0 +1,448 @@ +/* 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 { + uint64_t prev_pos; + uint32_t buff_size; + uint32_t tdm_instance; + uint32_t pin_dir; + uint32_t dma_channel; + uint32_t index; + uint32_t frame_fmt; +}; + +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..e0ab1baf8033 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); @@ -48,7 +50,7 @@ const struct freq_table platform_cpu_freq[] = { STATIC_ASSERT(ARRAY_SIZE(platform_cpu_freq) == NUM_CPU_FREQ, invalid_number_of_cpu_frequencies); -static SHARED_DATA struct clock_info platform_clocks_info[NUM_CLOCKS]; +static struct clock_info platform_clocks_info[NUM_CLOCKS]; void audio_pll_power_off(void); void audio_pll_power_on(void); @@ -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/lib/dma.c b/src/platform/amd/acp_7_0/lib/dma.c index bbc6b4a6cd8a..60fb1e631b5e 100644 --- a/src/platform/amd/acp_7_0/lib/dma.c +++ b/src/platform/amd/acp_7_0/lib/dma.c @@ -24,7 +24,7 @@ extern struct dma_ops acp_dai_sp_dma_ops; extern struct dma_ops acp_dai_hs_dma_ops; extern struct dma_ops acp_dai_sw_audio_dma_ops; -SHARED_DATA struct dma dma[PLATFORM_NUM_DMACS] = { +struct dma dma[PLATFORM_NUM_DMACS] = { { .plat_data = { .id = DMA_ID_DMA0, diff --git a/src/platform/amd/acp_7_0/lib/memory.c b/src/platform/amd/acp_7_0/lib/memory.c index ce3925e95860..fa1caa2ae726 100644 --- a/src/platform/amd/acp_7_0/lib/memory.c +++ b/src/platform/amd/acp_7_0/lib/memory.c @@ -13,29 +13,29 @@ #include <ipc/topology.h> /* Heap blocks for system runtime */ -static SHARED_DATA struct block_hdr sys_rt_block64[HEAP_SYS_RT_COUNT64]; -static SHARED_DATA struct block_hdr sys_rt_block512[HEAP_SYS_RT_COUNT512]; -static SHARED_DATA struct block_hdr sys_rt_block1024[HEAP_SYS_RT_COUNT1024]; +static struct block_hdr sys_rt_block64[HEAP_SYS_RT_COUNT64]; +static struct block_hdr sys_rt_block512[HEAP_SYS_RT_COUNT512]; +static struct block_hdr sys_rt_block1024[HEAP_SYS_RT_COUNT1024]; /* Heap memory for system runtime */ -static SHARED_DATA struct block_map sys_rt_heap_map[] = { +static struct block_map sys_rt_heap_map[] = { BLOCK_DEF(64, HEAP_SYS_RT_COUNT64, sys_rt_block64), BLOCK_DEF(512, HEAP_SYS_RT_COUNT512, sys_rt_block512), BLOCK_DEF(1024, HEAP_SYS_RT_COUNT1024, sys_rt_block1024), }; /* Heap blocks for modules */ -static SHARED_DATA struct block_hdr mod_block16[HEAP_RT_COUNT16]; -static SHARED_DATA struct block_hdr mod_block32[HEAP_RT_COUNT32]; -static SHARED_DATA struct block_hdr mod_block64[HEAP_RT_COUNT64]; -static SHARED_DATA struct block_hdr mod_block128[HEAP_RT_COUNT128]; -static SHARED_DATA struct block_hdr mod_block256[HEAP_RT_COUNT256]; -static SHARED_DATA struct block_hdr mod_block512[HEAP_RT_COUNT512]; -static SHARED_DATA struct block_hdr mod_block1024[HEAP_RT_COUNT1024]; -static SHARED_DATA struct block_hdr mod_block2048[HEAP_RT_COUNT2048]; +static struct block_hdr mod_block16[HEAP_RT_COUNT16]; +static struct block_hdr mod_block32[HEAP_RT_COUNT32]; +static struct block_hdr mod_block64[HEAP_RT_COUNT64]; +static struct block_hdr mod_block128[HEAP_RT_COUNT128]; +static struct block_hdr mod_block256[HEAP_RT_COUNT256]; +static struct block_hdr mod_block512[HEAP_RT_COUNT512]; +static struct block_hdr mod_block1024[HEAP_RT_COUNT1024]; +static struct block_hdr mod_block2048[HEAP_RT_COUNT2048]; /* Heap memory map for modules */ -static SHARED_DATA struct block_map rt_heap_map[] = { +static struct block_map rt_heap_map[] = { BLOCK_DEF(16, HEAP_RT_COUNT16, mod_block16), BLOCK_DEF(32, HEAP_RT_COUNT32, mod_block32), BLOCK_DEF(64, HEAP_RT_COUNT64, mod_block64), @@ -47,14 +47,14 @@ static SHARED_DATA struct block_map rt_heap_map[] = { }; /* Heap blocks for buffers */ -static SHARED_DATA struct block_hdr buf_block[HEAP_BUFFER_COUNT]; +static struct block_hdr buf_block[HEAP_BUFFER_COUNT]; /* Heap memory map for buffers */ -static SHARED_DATA struct block_map buf_heap_map[] = { +static struct block_map buf_heap_map[] = { BLOCK_DEF(HEAP_BUFFER_BLOCK_SIZE, HEAP_BUFFER_COUNT, buf_block), }; -static SHARED_DATA struct mm memmap = { +static struct mm memmap = { .system[0] = { .heap = HEAP_SYSTEM_BASE, .size = HEAP_SYSTEM_SIZE, 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/acp_7_x/CMakeLists.txt b/src/platform/amd/acp_7_x/CMakeLists.txt new file mode 100644 index 000000000000..3108ac12f8db --- /dev/null +++ b/src/platform/amd/acp_7_x/CMakeLists.txt @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: BSD-3-Clause + +add_subdirectory(lib) +add_local_sources(sof platform.c) diff --git a/src/platform/amd/acp_7_x/include/arch/xtensa/tie/xt_datacache.h b/src/platform/amd/acp_7_x/include/arch/xtensa/tie/xt_datacache.h new file mode 100755 index 000000000000..a84835c4b5f7 --- /dev/null +++ b/src/platform/amd/acp_7_x/include/arch/xtensa/tie/xt_datacache.h @@ -0,0 +1,103 @@ +// Customer ID=20511; Build=0xb6494; 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_datacache TIE package */ + +/* Do not modify. This is automatically generated.*/ + +/* parasoft-begin-suppress ALL "This file not MISRA checked." */ + +#ifndef _XTENSA_xt_datacache_HEADER +#define _XTENSA_xt_datacache_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 void _TIE_xt_datacache_DHI(const int * s, immediate i); +extern void _TIE_xt_datacache_DHU(const int * s, immediate i); +extern void _TIE_xt_datacache_DHWB(const int * s, immediate i); +extern void _TIE_xt_datacache_DHWBI(const int * s, immediate i); +extern void _TIE_xt_datacache_DII(const int * s, immediate i); +extern void _TIE_xt_datacache_DIU(const int * s, immediate i); +extern void _TIE_xt_datacache_DIWB(const int * s, immediate i); +extern void _TIE_xt_datacache_DIWBI(const int * s, immediate i); +extern void _TIE_xt_datacache_DIWBUI_P(const int * s /*inout*/); +extern void _TIE_xt_datacache_DPFL(const int * s, immediate i); +extern void _TIE_xt_datacache_DPFR(const int * s, immediate i); +extern void _TIE_xt_datacache_DPFRO(const int * s, immediate i); +extern void _TIE_xt_datacache_DPFW(const int * s, immediate i); +extern void _TIE_xt_datacache_DPFWO(const int * s, immediate i); +extern int _TIE_xt_datacache_LDCT(const int * s); +extern int _TIE_xt_datacache_LDCW(const int * s); +extern void _TIE_xt_datacache_SDCT(int t, int * s); +extern void _TIE_xt_datacache_SDCW(int t, int * s); + +#endif /*__ASSEMBLER__*/ +#endif /*_NOCLANGUAGE*/ +#endif /*_ASMLANGUAGE*/ + +#define XT_DHI _TIE_xt_datacache_DHI +#define XT_DHU _TIE_xt_datacache_DHU +#define XT_DHWB _TIE_xt_datacache_DHWB +#define XT_DHWBI _TIE_xt_datacache_DHWBI +#define XT_DII _TIE_xt_datacache_DII +#define XT_DIU _TIE_xt_datacache_DIU +#define XT_DIWB _TIE_xt_datacache_DIWB +#define XT_DIWBI _TIE_xt_datacache_DIWBI +#define XT_DIWBUI_P _TIE_xt_datacache_DIWBUI_P +#define XT_DPFL _TIE_xt_datacache_DPFL +#define XT_DPFR _TIE_xt_datacache_DPFR +#define XT_DPFRO _TIE_xt_datacache_DPFRO +#define XT_DPFW _TIE_xt_datacache_DPFW +#define XT_DPFWO _TIE_xt_datacache_DPFWO +#define XT_LDCT _TIE_xt_datacache_LDCT +#define XT_LDCW _TIE_xt_datacache_LDCW +#define XT_SDCT _TIE_xt_datacache_SDCT +#define XT_SDCW _TIE_xt_datacache_SDCW + +#endif /* __XCC__ */ + +#endif /* __XTENSA__ */ + +#endif /* !_XTENSA_xt_datacache_HEADER */ + +/* parasoft-end-suppress ALL "This file not MISRA checked." */ diff --git a/src/platform/amd/acp_7_x/include/platform/fw_scratch_mem.h b/src/platform/amd/acp_7_x/include/platform/fw_scratch_mem.h new file mode 100644 index 000000000000..8c7eec522e65 --- /dev/null +++ b/src/platform/amd/acp_7_x/include/platform/fw_scratch_mem.h @@ -0,0 +1,119 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * + * Copyright(c) 2026 AMD. All rights reserved. + * + * Author: Sneha Voona <sneha.voona@amd.com> + * DineshKumar Kalva <DineshKumar.Kalva@amd.com> + */ +#ifndef __EXT_SCRATCH_MEM_H__ +#define __EXT_SCRATCH_MEM_H__ + +#include <sof/lib/mailbox.h> + +/* MAX number of DMA descriptors */ +#define MAX_NUM_DMA_DESC_DSCR 64 +#define SCRATCH_REG_OFFSET 0x1250000 +#define ACP_SRAM 0x03800000 + +typedef struct acp_atu_grp_pte { + uint32_t low_part; + uint32_t high_part; +} __attribute__((packed, aligned(4))) acp_atu_grp_pte_t; + +typedef union acp_cfg_dma_trns_cnt { + struct{ + uint32_t trns_cnt : 19; + uint32_t reserved : 12; + uint32_t ioc : 1; + } bits; + unsigned int u32all; +} __attribute__((packed, aligned(4))) acp_cfg_dma_trns_cnt_t; + +typedef struct acp_config_dma_descriptor { + uint32_t src_addr; + uint32_t dest_addr; + acp_cfg_dma_trns_cnt_t trns_cnt; + uint32_t reserved; +} __attribute__((packed, aligned(4))) acp_cfg_dma_descriptor_t; + +typedef struct acp_config_dma_misc { + uint32_t channelstatus; + uint32_t channel; + uint32_t flag; +} __attribute__((packed, aligned(4))) acp_cfg_dma_misc_t; + +typedef struct acp_scratch_memory_config { + /* ACP out box buffer */ + uint8_t acp_outbox_buffer[MAILBOX_DSPBOX_SIZE]; + + /* ACP in box buffer */ + uint8_t acp_inbox_buffer[MAILBOX_HOSTBOX_SIZE]; + + /* ACP debug box buffer */ + uint8_t acp_debug_buffer[MAILBOX_DEBUG_SIZE]; + + /* ACP exception box buffer */ + uint8_t acp_except_buffer[MAILBOX_EXCEPTION_SIZE]; + + /* ACP stream buffer */ + uint8_t acp_stream_buffer[MAILBOX_STREAM_SIZE]; + + /* ACP trace buffer */ + uint8_t acp_trace_buffer[MAILBOX_TRACE_SIZE]; + + /* Host msg write flag */ + uint32_t acp_host_msg_write; + + /* Host ack flag */ + uint32_t acp_host_ack_write; + + /* Dsp msg write flag */ + uint32_t acp_dsp_msg_write; + + /* Dsp ack flag */ + uint32_t acp_dsp_ack_write; + + /* ACP pte1 table */ + acp_atu_grp_pte_t acp_atugrp1_pte[16]; + + /* ACP pte2 table */ + acp_atu_grp_pte_t acp_atugrp2_pte[16]; + + /* ACP pte3 table */ + acp_atu_grp_pte_t acp_atugrp3_pte[16]; + + /* ACP pte4 table */ + acp_atu_grp_pte_t acp_atugrp4_pte[16]; + + /* ACP pte5 table */ + acp_atu_grp_pte_t acp_atugrp5_pte[16]; + + /* ACP pte6 table */ + acp_atu_grp_pte_t acp_atugrp6_pte[16]; + + /* ACP pte7 table */ + acp_atu_grp_pte_t acp_atugrp7_pte[16]; + + /* ACP pte8 table */ + acp_atu_grp_pte_t acp_atugrp8_pte[16]; + + /* ACP DMA Descriptor */ + acp_cfg_dma_descriptor_t acp_cfg_dma_descriptor[MAX_NUM_DMA_DESC_DSCR]; + + /* Stream physical offset */ + uint32_t phy_offset[8]; + + /* Stream system memory size */ + uint32_t syst_buff_size[8]; + + /* Fifo buffers are not part of scratch memory on ACP_7_X */ + /* Added fifo members to align with Driver structure */ + /* ACP transmit fifo buffer */ + uint8_t acp_transmit_fifo_buffer[256] __attribute__((aligned(128))); + + /* ACP receive fifo buffer */ + uint8_t acp_receive_fifo_buffer[256] __attribute__((aligned(128))); + uint32_t reserve[]; +} __attribute__((packed, aligned(4))) acp_scratch_mem_config_t; + +#endif /* __EXT_SCRATCH_MEM_H__ */ diff --git a/src/platform/amd/acp_7_x/include/platform/lib/memory.h b/src/platform/amd/acp_7_x/include/platform/lib/memory.h new file mode 100644 index 000000000000..0a7039e4036b --- /dev/null +++ b/src/platform/amd/acp_7_x/include/platform/lib/memory.h @@ -0,0 +1,246 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * + * Copyright(c) 2026 AMD. All rights reserved. + * + * Author: Sneha Voona <sneha.voona@amd.com> + * DineshKumar Kalva <DineshKumar.Kalva@amd.com> + */ + +#ifdef __SOF_LIB_MEMORY_H__ + +#ifndef __PLATFORM_LIB_MEMORY_H__ +#define __PLATFORM_LIB_MEMORY_H__ + +#include <rtos/cache.h> + +/* data cache line alignment */ +#define PLATFORM_DCACHE_ALIGN 128 + +#define PU_REGISTER_BASE (0x9FD00000 - 0x01240000) + +/* physical DSP addresses */ +#define IRAM_BASE 0x7F000000 +#define IRAM_SIZE 0x20000 + +#define DRAM0_BASE 0xE0000000 +#define DRAM0_SIZE 0x10000 +#define SRAM0_BASE 0x9FF00000 + +#define DRAM1_BASE 0xE0010000 +#define DRAM1_SIZE 0x10000 +#define SRAM1_BASE 0x6000C000 +#define SRAM1_SIZE 0x274000 + +#define DMA0_BASE PU_REGISTER_BASE +#define DMA0_SIZE 0x4 + +/* DAI DMA register base address */ +#define DAI_BASE_I2S_TDM (PU_REGISTER_BASE + ACP_I2S_TDM_RX_RINGBUFADDR) +#define DAI_BASE_SW (PU_REGISTER_BASE + ACP_SW0_RX_DMA0_RINGBUFADDR) +#define DAI_BASE_PDM (PU_REGISTER_BASE + ACP_WOV_RX_RINGBUFADDR) +#define DAI_BASE_PDM1 (DAI_BASE_PDM + ACP_PDM_2_RX_RINGBUFADDR_96K) +#define DAI_SIZE 0x4 + +#define I2S_TDM_TX_FIFO_OFFST (ACP_I2S_TDM_TX_FIFOADDR - ACP_I2S_TDM_RX_RINGBUFADDR) +#define I2S_TDM_RX_FIFO_OFFST (ACP_I2S_TDM_RX_FIFOADDR - ACP_I2S_TDM_RX_RINGBUFADDR) + +/* SW0 FIFO */ +#define SW0_AUDIO0_TX_FIFO_OFFST (ACP_SW0_TX_DMA0_FIFOADDR - ACP_SW0_RX_DMA0_RINGBUFADDR) +#define SW0_AUDIO0_RX_FIFO_OFFST (ACP_SW0_RX_DMA0_FIFOADDR - ACP_SW0_RX_DMA0_RINGBUFADDR) +#define SW0_AUDIO1_TX_FIFO_OFFST (ACP_SW0_TX_DMA1_FIFOADDR - ACP_SW0_RX_DMA0_RINGBUFADDR) +#define SW0_AUDIO1_RX_FIFO_OFFST (ACP_SW0_RX_DMA1_FIFOADDR - ACP_SW0_RX_DMA0_RINGBUFADDR) +#define SW0_AUDIO2_TX_FIFO_OFFST (ACP_SW0_TX_DMA2_FIFOADDR - ACP_SW0_RX_DMA0_RINGBUFADDR) +#define SW0_AUDIO2_RX_FIFO_OFFST (ACP_SW0_RX_DMA2_FIFOADDR - ACP_SW0_RX_DMA0_RINGBUFADDR) +#define SW0_AUDIO3_TX_FIFO_OFFST (ACP_SW0_TX_DMA3_FIFOADDR - ACP_SW0_RX_DMA0_RINGBUFADDR) +#define SW0_AUDIO3_RX_FIFO_OFFST (ACP_SW0_RX_DMA3_FIFOADDR - ACP_SW0_RX_DMA0_RINGBUFADDR) +#define SW0_AUDIO4_TX_FIFO_OFFST (ACP_SW0_TX_DMA4_FIFOADDR - ACP_SW0_RX_DMA0_RINGBUFADDR) +#define SW0_AUDIO4_RX_FIFO_OFFST (ACP_SW0_RX_DMA4_FIFOADDR - ACP_SW0_RX_DMA0_RINGBUFADDR) +#define SW0_AUDIO5_TX_FIFO_OFFST (ACP_SW0_TX_DMA5_FIFOADDR - ACP_SW0_RX_DMA0_RINGBUFADDR) +#define SW0_AUDIO5_RX_FIFO_OFFST (ACP_SW0_RX_DMA5_FIFOADDR - ACP_SW0_RX_DMA0_RINGBUFADDR) +#define SW0_AUDIO6_TX_FIFO_OFFST (ACP_SW0_TX_DMA6_FIFOADDR - ACP_SW0_RX_DMA0_RINGBUFADDR) +#define SW0_AUDIO6_RX_FIFO_OFFST (ACP_SW0_RX_DMA6_FIFOADDR - ACP_SW0_RX_DMA0_RINGBUFADDR) +#define SW0_AUDIO7_TX_FIFO_OFFST (ACP_SW0_TX_DMA7_FIFOADDR - ACP_SW0_RX_DMA0_RINGBUFADDR) +#define SW0_AUDIO7_RX_FIFO_OFFST (ACP_SW0_RX_DMA7_FIFOADDR - ACP_SW0_RX_DMA0_RINGBUFADDR) + +/* SW1 FIFO */ +#define SW1_AUDIO0_TX_FIFO_OFFST (ACP_SW1_TX_DMA0_FIFOADDR - ACP_SW0_RX_DMA0_RINGBUFADDR) +#define SW1_AUDIO0_RX_FIFO_OFFST (ACP_SW1_RX_DMA0_FIFOADDR - ACP_SW0_RX_DMA0_RINGBUFADDR) +#define SW1_AUDIO1_TX_FIFO_OFFST (ACP_SW1_TX_DMA1_FIFOADDR - ACP_SW0_RX_DMA0_RINGBUFADDR) +#define SW1_AUDIO1_RX_FIFO_OFFST (ACP_SW1_RX_DMA1_FIFOADDR - ACP_SW0_RX_DMA0_RINGBUFADDR) +#define SW1_AUDIO2_TX_FIFO_OFFST (ACP_SW1_TX_DMA2_FIFOADDR - ACP_SW0_RX_DMA0_RINGBUFADDR) +#define SW1_AUDIO2_RX_FIFO_OFFST (ACP_SW1_RX_DMA2_FIFOADDR - ACP_SW0_RX_DMA0_RINGBUFADDR) +#define SW1_AUDIO3_TX_FIFO_OFFST (ACP_SW1_TX_DMA3_FIFOADDR - ACP_SW0_RX_DMA0_RINGBUFADDR) +#define SW1_AUDIO3_RX_FIFO_OFFST (ACP_SW1_RX_DMA3_FIFOADDR - ACP_SW0_RX_DMA0_RINGBUFADDR) +#define SW1_AUDIO4_TX_FIFO_OFFST (ACP_SW1_TX_DMA4_FIFOADDR - ACP_SW0_RX_DMA0_RINGBUFADDR) +#define SW1_AUDIO4_RX_FIFO_OFFST (ACP_SW1_RX_DMA4_FIFOADDR - ACP_SW0_RX_DMA0_RINGBUFADDR) +#define SW1_AUDIO5_TX_FIFO_OFFST (ACP_SW1_TX_DMA5_FIFOADDR - ACP_SW0_RX_DMA0_RINGBUFADDR) +#define SW1_AUDIO5_RX_FIFO_OFFST (ACP_SW1_RX_DMA5_FIFOADDR - ACP_SW0_RX_DMA0_RINGBUFADDR) +#define SW1_AUDIO6_TX_FIFO_OFFST (ACP_SW1_TX_DMA6_FIFOADDR - ACP_SW0_RX_DMA0_RINGBUFADDR) +#define SW1_AUDIO6_RX_FIFO_OFFST (ACP_SW1_RX_DMA6_FIFOADDR - ACP_SW0_RX_DMA0_RINGBUFADDR) +#define SW1_AUDIO7_TX_FIFO_OFFST (ACP_SW1_TX_DMA7_FIFOADDR - ACP_SW0_RX_DMA0_RINGBUFADDR) +#define SW1_AUDIO7_RX_FIFO_OFFST (ACP_SW1_RX_DMA7_FIFOADDR - ACP_SW0_RX_DMA0_RINGBUFADDR) + +/* SW2 FIFO */ +#define SW2_AUDIO0_TX_FIFO_OFFST (ACP_SW2_TX_DMA0_FIFOADDR - ACP_SW0_RX_DMA0_RINGBUFADDR) +#define SW2_AUDIO0_RX_FIFO_OFFST (ACP_SW2_RX_DMA0_FIFOADDR - ACP_SW0_RX_DMA0_RINGBUFADDR) +#define SW2_AUDIO1_TX_FIFO_OFFST (ACP_SW2_TX_DMA1_FIFOADDR - ACP_SW0_RX_DMA0_RINGBUFADDR) +#define SW2_AUDIO1_RX_FIFO_OFFST (ACP_SW2_RX_DMA1_FIFOADDR - ACP_SW0_RX_DMA0_RINGBUFADDR) +#define SW2_AUDIO2_TX_FIFO_OFFST (ACP_SW2_TX_DMA2_FIFOADDR - ACP_SW0_RX_DMA0_RINGBUFADDR) +#define SW2_AUDIO2_RX_FIFO_OFFST (ACP_SW2_RX_DMA2_FIFOADDR - ACP_SW0_RX_DMA0_RINGBUFADDR) +#define SW2_AUDIO3_TX_FIFO_OFFST (ACP_SW2_TX_DMA3_FIFOADDR - ACP_SW0_RX_DMA0_RINGBUFADDR) +#define SW2_AUDIO3_RX_FIFO_OFFST (ACP_SW2_RX_DMA3_FIFOADDR - ACP_SW0_RX_DMA0_RINGBUFADDR) +#define SW2_AUDIO4_TX_FIFO_OFFST (ACP_SW2_TX_DMA4_FIFOADDR - ACP_SW0_RX_DMA0_RINGBUFADDR) +#define SW2_AUDIO4_RX_FIFO_OFFST (ACP_SW2_RX_DMA4_FIFOADDR - ACP_SW0_RX_DMA0_RINGBUFADDR) +#define SW2_AUDIO5_TX_FIFO_OFFST (ACP_SW2_TX_DMA5_FIFOADDR - ACP_SW0_RX_DMA0_RINGBUFADDR) +#define SW2_AUDIO5_RX_FIFO_OFFST (ACP_SW2_RX_DMA5_FIFOADDR - ACP_SW0_RX_DMA0_RINGBUFADDR) +#define SW2_AUDIO6_TX_FIFO_OFFST (ACP_SW2_TX_DMA6_FIFOADDR - ACP_SW0_RX_DMA0_RINGBUFADDR) +#define SW2_AUDIO6_RX_FIFO_OFFST (ACP_SW2_RX_DMA6_FIFOADDR - ACP_SW0_RX_DMA0_RINGBUFADDR) +#define SW2_AUDIO7_TX_FIFO_OFFST (ACP_SW2_TX_DMA7_FIFOADDR - ACP_SW0_RX_DMA0_RINGBUFADDR) +#define SW2_AUDIO7_RX_FIFO_OFFST (ACP_SW2_RX_DMA7_FIFOADDR - ACP_SW0_RX_DMA0_RINGBUFADDR) + +/* SW3 FIFO */ +#define SW3_AUDIO0_TX_FIFO_OFFST (ACP_SW3_TX_DMA0_FIFOADDR - ACP_SW0_RX_DMA0_RINGBUFADDR) +#define SW3_AUDIO0_RX_FIFO_OFFST (ACP_SW3_RX_DMA0_FIFOADDR - ACP_SW0_RX_DMA0_RINGBUFADDR) +#define SW3_AUDIO1_TX_FIFO_OFFST (ACP_SW3_TX_DMA1_FIFOADDR - ACP_SW0_RX_DMA0_RINGBUFADDR) +#define SW3_AUDIO1_RX_FIFO_OFFST (ACP_SW3_RX_DMA1_FIFOADDR - ACP_SW0_RX_DMA0_RINGBUFADDR) +#define SW3_AUDIO2_TX_FIFO_OFFST (ACP_SW3_TX_DMA2_FIFOADDR - ACP_SW0_RX_DMA0_RINGBUFADDR) +#define SW3_AUDIO2_RX_FIFO_OFFST (ACP_SW3_RX_DMA2_FIFOADDR - ACP_SW0_RX_DMA0_RINGBUFADDR) +#define SW3_AUDIO3_TX_FIFO_OFFST (ACP_SW3_TX_DMA3_FIFOADDR - ACP_SW0_RX_DMA0_RINGBUFADDR) +#define SW3_AUDIO3_RX_FIFO_OFFST (ACP_SW3_RX_DMA3_FIFOADDR - ACP_SW0_RX_DMA0_RINGBUFADDR) +#define SW3_AUDIO4_TX_FIFO_OFFST (ACP_SW3_TX_DMA4_FIFOADDR - ACP_SW0_RX_DMA0_RINGBUFADDR) +#define SW3_AUDIO4_RX_FIFO_OFFST (ACP_SW3_RX_DMA4_FIFOADDR - ACP_SW0_RX_DMA0_RINGBUFADDR) +#define SW3_AUDIO5_TX_FIFO_OFFST (ACP_SW3_TX_DMA5_FIFOADDR - ACP_SW0_RX_DMA0_RINGBUFADDR) +#define SW3_AUDIO5_RX_FIFO_OFFST (ACP_SW3_RX_DMA5_FIFOADDR - ACP_SW0_RX_DMA0_RINGBUFADDR) +#define SW3_AUDIO6_TX_FIFO_OFFST (ACP_SW3_TX_DMA6_FIFOADDR - ACP_SW0_RX_DMA0_RINGBUFADDR) +#define SW3_AUDIO6_RX_FIFO_OFFST (ACP_SW3_RX_DMA6_FIFOADDR - ACP_SW0_RX_DMA0_RINGBUFADDR) +#define SW3_AUDIO7_TX_FIFO_OFFST (ACP_SW3_TX_DMA7_FIFOADDR - ACP_SW0_RX_DMA0_RINGBUFADDR) +#define SW3_AUDIO7_RX_FIFO_OFFST (ACP_SW3_RX_DMA7_FIFOADDR - ACP_SW0_RX_DMA0_RINGBUFADDR) + +#define UUID_ENTRY_ELF_BASE 0x1FFFA000 +#define UUID_ENTRY_ELF_SIZE 0x6000 + +/* Log buffer base need to be updated properly, these are used in linker scripts */ +#define LOG_ENTRY_ELF_BASE 0x20000000 +#define LOG_ENTRY_ELF_SIZE 0x2000000 + +#define EXT_MANIFEST_ELF_BASE (LOG_ENTRY_ELF_BASE + LOG_ENTRY_ELF_SIZE) +#define EXT_MANIFEST_ELF_SIZE 0x2000000 + +/* Stack configuration */ +#define SOF_STACK_SIZE (0x1000) +#define SOF_STACK_TOTAL_SIZE SOF_STACK_SIZE +#define SOF_STACK_END (DRAM1_BASE + DRAM1_SIZE - SOF_STACK_SIZE) +#define SOF_STACK_BASE (SOF_STACK_END + SOF_STACK_SIZE) + +/* Mailbox configuration */ +#define SRAM_OUTBOX_BASE SRAM0_BASE +#define SRAM_OUTBOX_SIZE 0x400 +#define SRAM_OUTBOX_OFFSET 0 + +#define SRAM_INBOX_BASE (SRAM_OUTBOX_BASE + SRAM_OUTBOX_SIZE) +#define SRAM_INBOX_SIZE 0x400 +#define SRAM_INBOX_OFFSET SRAM_OUTBOX_SIZE + +#define SRAM_DEBUG_BASE (SRAM_INBOX_BASE + SRAM_INBOX_SIZE) +#define SRAM_DEBUG_SIZE 0x400 +#define SRAM_DEBUG_OFFSET (SRAM_INBOX_OFFSET + SRAM_INBOX_SIZE) + +#define SRAM_EXCEPT_BASE (SRAM_DEBUG_BASE + SRAM_DEBUG_SIZE) +#define SRAM_EXCEPT_SIZE 0x400 +#define SRAM_EXCEPT_OFFSET (SRAM_DEBUG_OFFSET + SRAM_DEBUG_SIZE) + +#define SRAM_STREAM_BASE (SRAM_EXCEPT_BASE + SRAM_EXCEPT_SIZE) +#define SRAM_STREAM_SIZE 0x400 +#define SRAM_STREAM_OFFSET (SRAM_EXCEPT_OFFSET + SRAM_EXCEPT_SIZE) + +#define SRAM_TRACE_BASE (SRAM_STREAM_BASE + SRAM_STREAM_SIZE) +#define SRAM_TRACE_SIZE 0x400 +#define SRAM_TRACE_OFFSET (SRAM_STREAM_OFFSET + SRAM_STREAM_SIZE) +#define SOF_MAILBOX_SIZE (SRAM_INBOX_SIZE + SRAM_OUTBOX_SIZE \ + + SRAM_DEBUG_SIZE + SRAM_EXCEPT_SIZE \ + + SRAM_STREAM_SIZE + SRAM_TRACE_SIZE) + +/* Heap section sizes for module pool */ +#define HEAP_RT_COUNT8 0 +#define HEAP_RT_COUNT16 192 +#define HEAP_RT_COUNT32 192 +#define HEAP_RT_COUNT64 128 +#define HEAP_RT_COUNT128 240 +#define HEAP_RT_COUNT256 128 +#define HEAP_RT_COUNT512 16 +#define HEAP_RT_COUNT1024 73 +#define HEAP_RT_COUNT2048 48 + +/* Heap section sizes for system runtime heap */ +#define HEAP_SYS_RT_COUNT64 720 +#define HEAP_SYS_RT_COUNT512 240 +#define HEAP_SYS_RT_COUNT1024 91 + +#define SIZE_OF_MEMORY_FOR_DATA_SECTION 0x60000 + +/* Heap configuration */ +#define HEAP_SYSTEM_BASE (SRAM1_BASE + SIZE_OF_MEMORY_FOR_DATA_SECTION) +#define HEAP_SYSTEM_SIZE 0x40000 +#define HEAP_SYSTEM_0_BASE HEAP_SYSTEM_BASE +#define HEAP_SYS_RUNTIME_BASE (HEAP_SYSTEM_BASE + HEAP_SYSTEM_SIZE) +#define HEAP_SYS_RUNTIME_SIZE (HEAP_SYS_RT_COUNT64 * 64 + HEAP_SYS_RT_COUNT512 * 512 + \ + HEAP_SYS_RT_COUNT1024 * 1024) + +#define HEAP_RUNTIME_BASE (HEAP_SYS_RUNTIME_BASE + HEAP_SYS_RUNTIME_SIZE) +#define HEAP_RUNTIME_SIZE \ + (HEAP_RT_COUNT8 * 8 + HEAP_RT_COUNT16 * 16 + \ + HEAP_RT_COUNT32 * 32 + HEAP_RT_COUNT64 * 64 + \ + HEAP_RT_COUNT128 * 128 + HEAP_RT_COUNT256 * 256 + \ + HEAP_RT_COUNT512 * 512 + HEAP_RT_COUNT1024 * 1024 + \ + HEAP_RT_COUNT2048 * 2048) + +#define HEAP_BUFFER_BASE (HEAP_RUNTIME_BASE + HEAP_RUNTIME_SIZE) +#define HEAP_BUFFER_SIZE (0x60000) +#define HEAP_BUFFER_BLOCK_SIZE 0x180 +#define HEAP_BUFFER_COUNT (HEAP_BUFFER_SIZE / HEAP_BUFFER_BLOCK_SIZE) + +#define PLATFORM_HEAP_SYSTEM 1 +#define PLATFORM_HEAP_SYSTEM_RUNTIME 1 +#define PLATFORM_HEAP_RUNTIME 1 +#define PLATFORM_HEAP_BUFFER 1 + +/* Vector and literal sizes - not in core-isa.h */ +#define SOF_MEM_VECT_LIT_SIZE 0x7 +#define SOF_MEM_VECT_TEXT_SIZE 0x37 +#define SOF_MEM_VECT_SIZE (SOF_MEM_VECT_TEXT_SIZE + SOF_MEM_VECT_LIT_SIZE) + +#define SOF_MEM_RESET_TEXT_SIZE 0x400 +#define SOF_MEM_RESET_LIT_SIZE 0x8 +#define SOF_MEM_VECBASE_LIT_SIZE 0x178 +#define SOF_MEM_WIN_TEXT_SIZE 0x178 + +#define SOF_MEM_RO_SIZE 0x8 + +#define uncache_to_cache(address) address +#define cache_to_uncache(address) address +#define is_uncached(address) 0 + +#define HEAP_BUF_ALIGNMENT PLATFORM_DCACHE_ALIGN + +/* brief EDF task's default stack size in bytes */ +#define PLATFORM_TASK_DEFAULT_STACK_SIZE 3072 + +#if !defined(__ASSEMBLER__) && !defined(LINKER) +struct sof; + +void platform_init_memmap(struct sof *sof); + +static inline void *platform_rfree_prepare(void *ptr) +{ + return ptr; +} +#endif +#define host_to_local(addr) (addr) +#define local_to_host(addr) (addr) + +#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/amd/acp_7_x/include/platform/platform.h b/src/platform/amd/acp_7_x/include/platform/platform.h new file mode 100644 index 000000000000..2a6b0147446d --- /dev/null +++ b/src/platform/amd/acp_7_x/include/platform/platform.h @@ -0,0 +1,101 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * + * Copyright(c) 2026 AMD. All rights reserved. + * + * Author: Sneha Voona <sneha.voona@amd.com> + * DineshKumar Kalva <DineshKumar.Kalva@amd.com> + */ + +#ifdef __SOF_PLATFORM_H__ + +#ifndef __PLATFORM_PLATFORM_H__ +#define __PLATFORM_PLATFORM_H__ + +#if !defined(__ASSEMBLER__) && !defined(LINKER) + +#include <rtos/interrupt.h> +#include <rtos/clk.h> +#include <sof/lib/mailbox.h> +#include <stddef.h> +#include <stdint.h> +#include <platform/fw_scratch_mem.h> +#include <platform/platform_misc.h> + +struct ll_schedule_domain; +struct timer; +#define INTERRUPT_ENABLE 1 +#define PLATFORM_DEFAULT_CLOCK CLK_CPU(0) + +/* IPC Interrupt */ +#define PLATFORM_IPC_INTERRUPT IRQ_EXT_IPC_LEVEL_3 +#define PLATFORM_IPC_INTERRUPT_NAME NULL + +/* Host page size */ +#define HOST_PAGE_SIZE 65536 + +/* pipeline IRQ */ +#define PLATFORM_SCHEDULE_IRQ IRQ_NUM_SOFTWARE0 +#define PLATFORM_SCHEDULE_IRQ_NAME NULL + +/* Platform stream capabilities */ +#define PLATFORM_MAX_CHANNELS 8 +#define PLATFORM_MAX_STREAMS 5 + +/* local buffer size of DMA tracing */ +#define DMA_TRACE_LOCAL_SIZE 8192 + +/* trace bytes flushed during panic */ +#define DMA_FLUSH_TRACE_SIZE (MAILBOX_TRACE_SIZE >> 2) + +/* the interval of DMA trace copying */ +#define DMA_TRACE_PERIOD 500 /* 1000 / 50000 */ + +/* + * the interval of reschedule DMA trace copying in special case like half + * fullness of local DMA trace buffer + */ +#define DMA_TRACE_RESCHEDULE_TIME 100 + +/* DSP default delay in cycles */ +#define PLATFORM_DEFAULT_DELAY 12 + +/* default dma trace channel */ +#define DMA_TRACE_CHANNEL 7 + +/* debug offset */ +#define ACP_SOF_FW_STATUS 0 + +/* Platform defined panic code */ +static inline void platform_panic(uint32_t p) +{ + acp_sw_intr_trig_t sw_intr_trig; + volatile acp_scratch_mem_config_t *pscratch_mem_cfg = + (volatile acp_scratch_mem_config_t *)(PU_SCRATCH_REG_BASE + SCRATCH_REG_OFFSET); + + pscratch_mem_cfg->acp_dsp_msg_write = p; + mailbox_sw_reg_write(ACP_SOF_FW_STATUS, p); + /* 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 trigger register */ + io_reg_write((PU_REGISTER_BASE + ACP_SW_INTR_TRIG), sw_intr_trig.u32all); +} + +/* + * brief Platform specific CPU entering idle. + * May be power-optimized using platform specific capabilities. + * @param level Interrupt level. + */ +void platform_wait_for_interrupt(int level); + +extern intptr_t _module_init_start; +extern intptr_t _module_init_end; +#endif + +#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/amd/acp_7_x/include/platform/platform_misc.h b/src/platform/amd/acp_7_x/include/platform/platform_misc.h new file mode 100644 index 000000000000..949f3622374f --- /dev/null +++ b/src/platform/amd/acp_7_x/include/platform/platform_misc.h @@ -0,0 +1,577 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * + * Copyright(c) 2026 AMD. All rights reserved. + * + * Author: Sneha Voona <sneha.voona@amd.com> + */ + +#include <zephyr/drivers/dma.h> + +#define PU_REGISTER_BASE (0x9FD00000 - 0x01240000) +#define PU_SCRATCH_REG_BASE (0x9FF00000 - 0x01250000) +#define ACP_FUTURE_REG_ACLK_0 0x12418E0 /* don't use - reserved for driver gsync */ +#define ACP_FUTURE_REG_ACLK_1 0x12418E4 +#define ACP_FUTURE_REG_ACLK_2 0x12418E8 +#define ACP_FUTURE_REG_ACLK_3 0x12418EC +#define ACP_FUTURE_REG_ACLK_4 0x12418F0 +#define ACP_SW_INTR_TRIG 0x1241890 +#define ACP_DSP0_INTR_CNTL 0x1241800 +#define ACP_DSP0_INTR_STAT 0x1241818 +#define ACP_DSP_SW_INTR_CNTL 0x1241860 +#define ACP_DSP_SW_INTR_STAT 0x1241864 +#define ACP_AXI2DAGB_SEM_0 0x12418F4 +#define ACP_SRBM_CLIENT_BASE_ADDR 0x1241BF0 +#define ACP_SRBM_CLIENT_RDDATA 0x1241BF4 +#define ACP_SRBM_CYCLE_STS 0x1241BF8 +#define ACP_SRBM_CLIENT_CONFIG 0x1241BFC + +/* Registers from SYSTEMPLL2P0_N3_BUS_CSR_SNAP_6 block ACP_7X */ +#define ACP_7X_SYSTEMPLL2P0_N3_BUS_CSR_SNAP_6_REFCLK_BRDS 0x6D204 +#define ACP_7X_SYSTEMPLL2P0_N3_BUS_CSR_SNAP_6_NON_AI_CTRL0_BRDS 0x6D214 +#define ACP_7X_SYSTEMPLL2P0_N3_BUS_CSR_SNAP_6_AI_FREQ_CTRL6_1_BRDS 0x6D21C +#define ACP_7X_SYSTEMPLL2P0_N3_BUS_CSR_SNAP_6_AI_FREQ_CTRL7_BRDS 0x6D220 +#define ACP_7X_SYSTEMPLL2P0_N3_BUS_CSR_SNAP_6_AI_FREQ_CTRL9_BRDS 0x6D20C + +/* Registers from CLK6 block */ +#define mmCLK6_PLL0_DFS0_CNTL 0x6D014 +#define mmCLK6_PLL0_DFS0_STATUS 0x6D018 +#define mmCLK6_PLL0_DFS1_CNTL 0x6D01C +#define mmCLK6_PLL0_DFS1_STATUS 0x6D020 +#define mmCLK6_CLK0_BYPASS_CNTL 0x6D06C +#define mmCLK6_CLK1_BYPASS_CNTL 0x6D08C +#define mmCLK6_CLK_TICK_CNT_CONFIG_REG 0x6D188 +#define mmCLK6_CLK0_CURRENT_CNT 0x6D190 +#define mmCLK6_CLK1_CURRENT_CNT 0x6D194 + +/* CLK6 register aliases without mm prefix for clock management code */ +#define CLK6_PLL0_DFS0_CNTL mmCLK6_PLL0_DFS0_CNTL +#define CLK6_PLL0_DFS1_CNTL mmCLK6_PLL0_DFS1_CNTL +#define CLK6_PLL0_DFS1_STATUS mmCLK6_PLL0_DFS1_STATUS +#define CLK0_CURRENT_CNT mmCLK6_CLK0_CURRENT_CNT +#define CLK1_CURRENT_CNT mmCLK6_CLK1_CURRENT_CNT +#define CLK_TICK_CNT_CONFIG_REG mmCLK6_CLK_TICK_CNT_CONFIG_REG + +/* SYSTEMPLL2P0 register aliases without ACP_7X prefix */ +#define SYSTEMPLL2P0_N3_BUS_CSR_SNAP_6_NON_AI_CTRL0_BRDS ACP_7X_SYSTEMPLL2P0_N3_BUS_CSR_SNAP_6_NON_AI_CTRL0_BRDS +#define SYSTEMPLL2P0_N3_BUS_CSR_SNAP_6_REFCLK_BRDS ACP_7X_SYSTEMPLL2P0_N3_BUS_CSR_SNAP_6_REFCLK_BRDS +#define SYSTEMPLL2P0_N3_BUS_CSR_SNAP_6_AI_FREQ_CTRL6_1_BRDS ACP_7X_SYSTEMPLL2P0_N3_BUS_CSR_SNAP_6_AI_FREQ_CTRL6_1_BRDS +#define SYSTEMPLL2P0_N3_BUS_CSR_SNAP_6_AI_FREQ_CTRL7_BRDS ACP_7X_SYSTEMPLL2P0_N3_BUS_CSR_SNAP_6_AI_FREQ_CTRL7_BRDS +#define SYSTEMPLL2P0_N3_BUS_CSR_SNAP_6_AI_FREQ_CTRL9_BRDS ACP_7X_SYSTEMPLL2P0_N3_BUS_CSR_SNAP_6_AI_FREQ_CTRL9_BRDS + +#define mmMP1_C2PMSG_37 0x3B10994 +#define mmMP1_C2PMSG_38 0x3B10998 +#define mmMP1_C2PMSG_39 0x3B1099C +#define ACPSMC_MSG_PllPowerState 0x3 /* Toggle PLL power state ON/OFF */ +#define ACP_AUDIOPLL_POWER_OFF_REQ 0x0 /* ACP Power Down Request Value */ +#define ACP_AUDIOPLL_POWER_ON_REQ 0x1 /* ACP Power Up Request Value */ +#define ACP_AUDIOPLL_POWER_OFF_REQ_WITH_WOV_EN 0x2 /* ACP Power Down Request Value */ +#define ACP_AUDIOPLL_POWER_ON_REQ_WITH_WOV_EN 0x3 /* ACP Power Up Request Value */ +#define ACP_AUDIO_CLK_SEL 0x1241038 +#define ACP_PDM_CORE_CLK_SEL 0x124103C + +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 clk_tick_cnt_config_reg { + struct { + uint32_t TIMER_THRESHOLD:24; + uint32_t TIMER_ENABLE:1; + uint32_t HISTORY_ENABLE:1; + uint32_t HISTORY_SP_RESET:1; + uint32_t HISTORY_CLK_SEL:5; + } bitfields, bits; + uint32_t u32All; + int32_t i32All; + float f32All; +} clk_tick_cnt_config_reg_t; +typedef union SYSTEMPLL2P0_N3_BUS_CSR_SNAP_6_NON_AI_CTRL0_BRDS_u { + struct { + uint32_t vco_pre_div:2; + uint32_t dac_ibiastrim:4; + uint32_t:26; + } bitfields, bits; + uint32_t u32All; + int32_t i32All; + float f32All; +} SYSTEMPLL2P0_N3_BUS_CSR_SNAP_6_NON_AI_CTRL0_BRDS_u_t; + +typedef union SYSTEMPLL2P0_N3_BUS_CSR_SNAP_6_REFCLK_BRDS_u { + struct { + uint32_t refclk_rate:8; + uint32_t refclk_div: 6; + uint32_t:1; + uint32_t cml_sel:1; + uint32_t:16; + + } bitfields, bits; + uint32_t u32All; + int32_t i32All; + float f32All; +} SYSTEMPLL2P0_N3_BUS_CSR_SNAP_6_REFCLK_BRDS_u_t; + +typedef union SYSTEMPLL2P0_N3_BUS_CSR_SNAP_6_AI_FREQ_CTRL6_1_BRDS_u { + struct { + uint32_t fcw_int:9; + uint32_t:23; + } bitfields, bits; + uint32_t u32All; + int32_t i32All; + float f32All; +} SYSTEMPLL2P0_N3_BUS_CSR_SNAP_6_AI_FREQ_CTRL6_1_BRDS_u_t; + +typedef union SYSTEMPLL2P0_N3_BUS_CSR_SNAP_6_AI_FREQ_CTRL7_BRDS_u { + struct { + uint32_t fcw_frac:16; + uint32_t:16; + } bitfields, bits; + uint32_t u32All; + int32_t i32All; + float f32All; +} SYSTEMPLL2P0_N3_BUS_CSR_SNAP_6_AI_FREQ_CTRL7_BRDS_u_t; + +typedef union SYSTEMPLL2P0_N3_BUS_CSR_SNAP_6_AI_FREQ_CTRL9_BRDS_u { + struct { + uint32_t fcw0_frac_lsb:8; + uint32_t fcw1_frac_lsb:8; + uint32_t:16; + } bitfields, bits; + uint32_t u32All; + int32_t i32All; + float f32All; +} SYSTEMPLL2P0_N3_BUS_CSR_SNAP_6_AI_FREQ_CTRL9_BRDS_u_t; + +typedef union clk6_pll_dfs_cntl_u { + struct { + uint32_t PLL0_DFS0_DIVIDER:7; + uint32_t PLL0_DFS0_DDCA:2; + uint32_t:3; + uint32_t PLL0_DFS0_ADCA:4; + uint32_t PLL0_DFS0_AllowZeroDID :1; + uint32_t PLL0_DFS0_EnableCLKOffInBypass:1; + uint32_t PLL0_DFS0_EnableVCO48OffInBypass :1; + uint32_t:1; + uint32_t PLL0_DFS0_DiDtWait:3; + uint32_t:1; + uint32_t PLL0_DFS0_DiDtFloor : 2; + uint32_t:2; + uint32_t PLL0_DFS0_SlamDid:1; + uint32_t PLL0_DFS0_FastRamp :1; + uint32_t:2; + } bitfields, bits; + uint32_t u32All; + int32_t i32All; + float f32All; +} clk6_pll_dfs_cntl_u_t; + +typedef union clk_dfs_status_u { + struct { + uint32_t PLL0_DFS0_CURRENT_DFS_DID:7; + uint32_t:1; + uint32_t PLL0_DFS0_AUTOLAUCH_FSM_FULL_SPEED_IDLE:1; + uint32_t PLL0_DFS0_AUTOLAUCH_FSM_BYPASS_IDLE: 1; + uint32_t:6; + uint32_t PLL0_DFS0_DFS_STATE_IDLE:1; + uint32_t PLL0_DFS0_DID_FSM_IDLE:1; + uint32_t PLL0_DFS0_DFS_DIV_REQ_IDLE: 1; + uint32_t:13; + } bitfields, bits; + uint32_t u32All; + int32_t i32All; + float f32All; +} clk_dfs_status_u_t; + +typedef union clk6_clk_bypass_cntl_u { + struct { + uint32_t CLK_BYPASS_SEL:3; + uint32_t CLK_BYPASS_DIV:4; + uint32_t CLK_PMUX_SGMode:8; + uint32_t CLK_BYPASS_SEL_STARTUP:3; + uint32_t:1; + uint32_t CLK_BYPASS_DIV_STARTUP:4; + uint32_t:8; + } bitfields, bits; + uint32_t u32All; + int32_t i32All; + float f32All; +} clk6_bypass_cntl_u_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 64 +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 */ +}; + + +#define SDW_INSTANCES 4 +struct sdw_pin_data { + uint32_t pin_num; + uint32_t pin_dir; + uint32_t dma_channel; + uint32_t dma_channel1; + uint32_t index[SDW_INSTANCES]; + uint32_t instance; + uint32_t instance1; +}; + +struct tdm_context { + uint64_t prev_pos; + uint32_t buff_size; + uint32_t tdm_instance; + uint32_t pin_dir; + uint32_t dma_channel; + uint32_t index; + uint32_t frame_fmt; +}; + +struct dmic_context { + uint64_t prev_pos; + uint32_t dmic_instance; + uint32_t pin_dir; + uint32_t dma_channel; + uint32_t index; +}; + +struct acp_dma_dev_data { + struct dma_context 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_x/lib/CMakeLists.txt b/src/platform/amd/acp_7_x/lib/CMakeLists.txt new file mode 100644 index 000000000000..147d9d9bea93 --- /dev/null +++ b/src/platform/amd/acp_7_x/lib/CMakeLists.txt @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: BSD-3-Clause + +add_local_sources(sof + clk.c +) diff --git a/src/platform/amd/acp_7_x/lib/clk.c b/src/platform/amd/acp_7_x/lib/clk.c new file mode 100644 index 000000000000..1f0f3acd176e --- /dev/null +++ b/src/platform/amd/acp_7_x/lib/clk.c @@ -0,0 +1,427 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright(c) 2026 AMD. All rights reserved. + * + * Author: Sneha Voona <sneha.voona@amd.com> + * Basavaraj Hiregoudar <basavaraj.hiregoudar@amd.com> + */ + +#include <sof/common.h> +#include <rtos/clk.h> +#include <sof/lib/cpu.h> +#include <sof/lib/memory.h> +#include <sof/lib/notifier.h> +#include <rtos/sof.h> +#include <rtos/atomic.h> +#include <sof/audio/component.h> +#include <rtos/bit.h> +#include <sof/drivers/acp_dai_dma.h> +#include <rtos/interrupt.h> +#include <rtos/timer.h> +#include <rtos/alloc.h> +#include <rtos/clk.h> +#include <sof/lib/cpu.h> +#include <sof/lib/dma.h> +#include <sof/lib/io.h> +#include <sof/lib/pm_runtime.h> +#include <sof/lib/notifier.h> +#include <sof/platform.h> +#include <sof/schedule/schedule.h> +#include <rtos/spinlock.h> +#include <sof/math/numbers.h> +#include <sof/trace/trace.h> +#include <ipc/topology.h> +#include <user/trace.h> +#include <errno.h> +#include <stdbool.h> +#include <stddef.h> +#include <stdint.h> +#include <platform/fw_scratch_mem.h> + +SOF_DEFINE_REG_UUID(acp_clk); +LOG_MODULE_REGISTER(acp_clk, CONFIG_SOF_LOG_LEVEL); +DECLARE_TR_CTX(acp_clk_tr, SOF_UUID(acp_clk_uuid), LOG_LEVEL_INFO); + +/* Frequency tables */ +const struct freq_table platform_cpu_freq[] = { + {600000000, 600000 }, +}; + +STATIC_ASSERT(ARRAY_SIZE(platform_cpu_freq) == NUM_CPU_FREQ, + invalid_number_of_cpu_frequencies); + +/* Type definitions */ +typedef enum { + PLL_MODE_100MHZ_NORMAL, + PLL_MODE_48MHZ_NORMAL, + PLL_MODE_32KHZ_LPPM, + PLL_MODE_48MHZ_LPPM, + PLL_MODE_100MHZ_LPPM +} PLL_MODE; + +/* Enumeration for the Clock Types */ +typedef enum _acp_clock_type_ { + acp_aclk_clock, + acp_sclk_clock, + acp_clock_type_max, + acp_clock_type_force = 0xFF +} acp_clock_type_t; + +/* Static variables */ +static struct clock_info platform_clocks_info[NUM_CLOCKS]; + +/* Function prototypes */ +void audio_pll_power_off(void); +void audio_pll_power_on(void); +void clk_dfs_bypassexit(void); +void audio_pll_mode_switch(uint32_t mode, uint32_t fcw_int, uint32_t fcw_frac, + uint32_t fcw_denom, uint32_t pllspinediv); +void clk7_init_vco(void); +void acp_7_x_reg_wait(void); +void acp_7_x_get_boot_ref_clock(float *boot_ref_clk); +uint32_t acp_clk_update_dfs_did(uint32_t did, uint32_t clk_type); +void acp_cpl_aclk_dfs_did_update(bool poll, uint32_t timeout_cnt, uint8_t did); +void acp_cpl_audioclk_dfs_did_update(bool poll, uint32_t timeout_cnt, uint8_t did); +void acp_cpl_aclk_poll_dfs_div_req_idle(uint32_t timeout_cnt); +void acp_cpl_audioclk_poll_dfs_div_req_idle(uint32_t timeout_cnt); +void change_clock_notify(uint32_t clock_freq); +int acp_get_current_clk(acp_clock_type_t clk_type, + float *clk_value); + +/* Static function prototypes */ +static int acp_reg_read_via_smn(uint32_t reg_offset, uint32_t size); +static void acp_reg_write_via_smn(uint32_t reg_offset, uint32_t reg_value, uint32_t size); + +static int acp_reg_read_via_smn(uint32_t reg_offset, + uint32_t size) +{ + uint32_t reg_value; + uint32_t delay_cnt = 10000; + uint32_t smn_client_base_addr = (reg_offset >> 10); + uint32_t region_start_addr = (smn_client_base_addr << 10); + uint32_t apertureid = ((reg_offset >> 20) & 0xFFF); + acp_srbm_cycle_sts_t acp_srbm_cycle_sts; + + io_reg_write((PU_REGISTER_BASE + ACP_SRBM_CLIENT_CONFIG), apertureid); + io_reg_write((PU_REGISTER_BASE + ACP_SRBM_CLIENT_BASE_ADDR), smn_client_base_addr); + reg_value = (uint32_t)io_reg_read(PU_REGISTER_BASE + + (ACP_MASTER_REG_ACCESS_ADDRESS + reg_offset - region_start_addr + ACP_FIRST_REG_OFFSET)); + if (reg_value) { + reg_value = 0; + } + acp_srbm_cycle_sts = (acp_srbm_cycle_sts_t)io_reg_read(PU_REGISTER_BASE + + ACP_SRBM_CYCLE_STS); + while (delay_cnt > 0) { + if (!acp_srbm_cycle_sts.bits.srbm_clients_sts) { + return (uint32_t)io_reg_read(PU_REGISTER_BASE + ACP_SRBM_CLIENT_RDDATA); + } + acp_srbm_cycle_sts = (acp_srbm_cycle_sts_t)io_reg_read(PU_REGISTER_BASE + + ACP_SRBM_CYCLE_STS); + delay_cnt--; + } + return -1; +} + +static void acp_reg_write_via_smn(uint32_t reg_offset, + uint32_t value, uint32_t size) +{ + uint32_t delay_cnt = 10000; + uint32_t smn_client_base_addr = (reg_offset >> 10); + uint32_t region_start_addr = (smn_client_base_addr << 10); + uint32_t apertureid = ((reg_offset >> 20) & 0xFFF); + acp_srbm_cycle_sts_t acp_srbm_cycle_sts; + + io_reg_write((PU_REGISTER_BASE + ACP_SRBM_CLIENT_CONFIG), apertureid); + io_reg_write((PU_REGISTER_BASE + ACP_SRBM_CLIENT_BASE_ADDR), smn_client_base_addr); + io_reg_write((PU_REGISTER_BASE + (ACP_MASTER_REG_ACCESS_ADDRESS + + reg_offset - region_start_addr + + ACP_FIRST_REG_OFFSET)), value); + acp_srbm_cycle_sts = + (acp_srbm_cycle_sts_t)io_reg_read(PU_REGISTER_BASE + ACP_SRBM_CYCLE_STS); + while (delay_cnt > 0) { + acp_srbm_cycle_sts = (acp_srbm_cycle_sts_t)io_reg_read(PU_REGISTER_BASE + + ACP_SRBM_CYCLE_STS); + if (!acp_srbm_cycle_sts.bits.srbm_clients_sts) { + return; + } + delay_cnt--; + } +} + +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, + }; + } + /* acp_reg_write_via_smn(CLK6_PLL0_DFS0_CNTL, 0x19, sizeof(int)); */ +} +void acp_cpl_audioclk_poll_dfs_div_req_idle(uint32_t timeout_cnt) +{ + uint32_t timeout = 0; + clk_dfs_status_u_t clk_dfs_status; + + do { + clk_dfs_status.u32All = acp_reg_read_via_smn( + mmCLK6_PLL0_DFS0_STATUS, sizeof(uint32_t)); + timeout++; + } while ((clk_dfs_status.bitfields.PLL0_DFS0_DFS_DIV_REQ_IDLE == 0) & + (timeout < timeout_cnt)); + +} + +uint32_t mp1_mailbox_send(uint32_t _reg_id, uint32_t _value) +{ + uint32_t ready = 0; + uint32_t wait_cnt = 0; + + while (wait_cnt < 0x1000) { + ready = acp_reg_read_via_smn(mmMP1_C2PMSG_38, sizeof(ready)); + if (ready != 0) { + break; + } + wait_cnt++; + } + if (ready == 0) { + return 0; + } + /* Clear the response register */ + acp_reg_write_via_smn(mmMP1_C2PMSG_38, 0, sizeof(uint32_t)); + /* Write the argument (value) to the argument register */ + acp_reg_write_via_smn(mmMP1_C2PMSG_39, _value, sizeof(uint32_t)); + /* Write the message ID to the message register */ + acp_reg_write_via_smn(mmMP1_C2PMSG_37, _reg_id, sizeof(uint32_t)); + /* Poll until response is non-zero */ + uint32_t resp = 0; + /* TODO: Enable this code after bringup is complete. This is commented to avoid hang */ + int count = 0; + + do { + resp = acp_reg_read_via_smn(mmMP1_C2PMSG_38, sizeof(uint32_t)); + count++; + if (count >= 100) { + break; + } + } while (resp == 0); + /* Optionally, read back the argument register if needed (not used here) */ + + return resp; +} + +void acp_clk_d0_sequence(uint32_t clock_freq) +{ + /* Send message to PMFW to power on Audio PLL */ + mp1_mailbox_send(ACPSMC_MSG_PllPowerState, ACP_AUDIOPLL_POWER_ON_REQ); + + change_clock_notify(clock_freq); + + /* Audio PLL DFS Output for AUDIOCLK */ + acp_reg_write_via_smn(mmCLK6_CLK0_BYPASS_CNTL, 0, sizeof(uint32_t)); + /* Audio PLL DFS Output for ACLK */ + acp_reg_write_via_smn(mmCLK6_CLK1_BYPASS_CNTL, 0, sizeof(uint32_t)); + +} + +void acp_clk_d3_sequence(void) +{ + clk6_bypass_cntl_u_t bypass_cntl; + + bypass_cntl.u32All = 0; + bypass_cntl.bitfields.CLK_BYPASS_SEL = 4; + + /* SET AUDIOCLK TO LPPLL 196.608MHz */ + acp_reg_write_via_smn(mmCLK6_CLK0_BYPASS_CNTL, bypass_cntl.u32All, sizeof(uint32_t)); + + bypass_cntl.u32All = 0; + bypass_cntl.bitfields.CLK_BYPASS_SEL = 2; + + /* SET ACLK TO LPPLL 393MHz */ + acp_reg_write_via_smn(mmCLK6_CLK1_BYPASS_CNTL, bypass_cntl.u32All, sizeof(uint32_t)); + /* mp1_mailbox_send(ACPSMC_MSG_PllPowerState, ACP_AUDIOPLL_POWER_OFF_REQ); */ + + /* Send message to PMFW to power off Audio PLL */ + mp1_mailbox_send(ACPSMC_MSG_PllPowerState, ACP_AUDIOPLL_POWER_OFF_REQ_WITH_WOV_EN); +} + +void change_clock_notify(uint32_t clock_freq) +{ + uint32_t final_did = 4; /* Default DID value */ + float did = 0.0f; + float fraction_val = 0.0f; + uint32_t int_did_val = 0; + float boot_ref_clk = 0.0f; + acp_clock_type_t clock_type = acp_aclk_clock; + + acp_7_x_get_boot_ref_clock(&boot_ref_clk); + + tr_info(&acp_clk_tr, "acp_change_clock_notify clock_freq : %d clock_type : %d", clock_freq, clock_type); + + fraction_val = (float)(clock_freq / (float)1000000.0f); /*converting Hz to Mhz*/ + clock_freq = (clock_freq / 1000000); /*converting Hz to Mhz*/ + + did = (float)(boot_ref_clk/(float)fraction_val); + if (did > 62.0f) { + final_did = 0x7F; + } else { + /* Fractional part of the divider value. Based on fractional + * value increment the did value. + */ + /* Refer DID value sheet for reference. */ + /* Extracting the fractional value from float divider value */ + fraction_val = did - (uint8_t)(did); + + /* Extracting only integer part of the divider */ + did = did - fraction_val; + if (did <= 16.00f) { + did = (did * 4.0f); + } else if ((did > 16.0f) && (did <= 32.0f)) { + did = ((did - 16.0f) * 2.0f + 64.0f); + } else if ((did > 32.0f) && (did <= 62.0f)) { + did = ((did - 32.0f) + 96.0f); + } + + /* Following logic is to ensure the fractional divider value is + * only limited to 2 decimal places, in order to ensure correct + * calculation of DID value + */ + int_did_val = (uint32_t)(fraction_val * 100.0f); + fraction_val = (float)(int_did_val / 100.0f); + + if (fraction_val == 0.0f) { + final_did = (uint8_t)(did); + } else if (fraction_val <= 0.25f) { + final_did = (uint8_t)(did)+1; + } else if ((fraction_val > 0.25f) && (fraction_val <= 0.5f)) { + final_did = (uint8_t)(did)+2; + } else if ((fraction_val > 0.5f) && (fraction_val <= 0.75f)) { + final_did = (uint8_t)(did)+3; + } else if ((fraction_val > 0.75f)) { + final_did = (uint8_t)(did)+4; + } + } + + acp_clk_update_dfs_did((uint32_t)final_did, clock_type); + +} +void acp_change_clock_notify(uint32_t clock_freq) +{ + if (clock_freq) { + /* d0 sequence */ + acp_clk_d0_sequence(clock_freq); + } else { + /* d3 sequence */ + acp_clk_d3_sequence(); + } + +} + +uint32_t acp_clk_update_dfs_did(uint32_t did, uint32_t clk_type) +{ + uint32_t updated_clk = 0; + + if (clk_type == acp_aclk_clock) { + acp_cpl_aclk_dfs_did_update(true, 10, did); + } else if (clk_type == acp_sclk_clock) { + acp_cpl_audioclk_dfs_did_update(true, 10, did); + } + + acp_get_current_clk(acp_aclk_clock, (float *)&updated_clk); + return updated_clk; + +} +void acp_cpl_aclk_dfs_did_update(bool poll, uint32_t timeout_cnt, uint8_t did) +{ /* CLK instance #6, clock slice #1 */ + clk6_pll_dfs_cntl_u_t clk6_pll0_dfs1_cntl; + + clk6_pll0_dfs1_cntl.u32All = acp_reg_read_via_smn(CLK6_PLL0_DFS1_CNTL, sizeof(int)); + clk6_pll0_dfs1_cntl.bitfields.PLL0_DFS0_DIVIDER = did; + acp_reg_write_via_smn(CLK6_PLL0_DFS1_CNTL, clk6_pll0_dfs1_cntl.u32All, sizeof(int)); + if (poll) { + acp_cpl_aclk_poll_dfs_div_req_idle(10); + } +} +void acp_cpl_audioclk_dfs_did_update(bool poll, uint32_t timeout_cnt, uint8_t did) +{ /* CLK instance #6, clock slice #0 */ + clk6_pll_dfs_cntl_u_t clk6_pll0_dfs0_cntl; + + clk6_pll0_dfs0_cntl.u32All = acp_reg_read_via_smn(CLK6_PLL0_DFS0_CNTL, sizeof(uint32_t)); + clk6_pll0_dfs0_cntl.bitfields.PLL0_DFS0_DIVIDER = did; + acp_reg_write_via_smn(CLK6_PLL0_DFS0_CNTL, clk6_pll0_dfs0_cntl.u32All, sizeof(uint32_t)); + /* polling DID change done */ + if (poll) { + acp_cpl_audioclk_poll_dfs_div_req_idle(timeout_cnt); + } +} + +void acp_cpl_aclk_poll_dfs_div_req_idle(uint32_t timeout_cnt) +{ + uint32_t timeout = 0; + clk_dfs_status_u_t clk6_pll0_dfs1_status; + + do { + clk6_pll0_dfs1_status.u32All = acp_reg_read_via_smn( + CLK6_PLL0_DFS1_STATUS, sizeof(uint32_t)); + timeout++; + } while ((clk6_pll0_dfs1_status.bitfields.PLL0_DFS0_DFS_DIV_REQ_IDLE == 0) && + (timeout < timeout_cnt)); +} + +void acp_7_x_get_boot_ref_clock(float *_boot_ref_clk) +{ + volatile SYSTEMPLL2P0_N3_BUS_CSR_SNAP_6_NON_AI_CTRL0_BRDS_u_t non_ai_ctrl0; + volatile SYSTEMPLL2P0_N3_BUS_CSR_SNAP_6_REFCLK_BRDS_u_t clk_pll_refclk; + volatile SYSTEMPLL2P0_N3_BUS_CSR_SNAP_6_AI_FREQ_CTRL6_1_BRDS_u_t ai_freq_ctrl6_1_brds; + volatile SYSTEMPLL2P0_N3_BUS_CSR_SNAP_6_AI_FREQ_CTRL7_BRDS_u_t ai_freq_ctrl7_brds; + volatile SYSTEMPLL2P0_N3_BUS_CSR_SNAP_6_AI_FREQ_CTRL9_BRDS_u_t ai_freq_ctrl9_brds; + + non_ai_ctrl0.u32All = acp_reg_read_via_smn( + SYSTEMPLL2P0_N3_BUS_CSR_SNAP_6_NON_AI_CTRL0_BRDS, sizeof(uint32_t)); + clk_pll_refclk.u32All = acp_reg_read_via_smn( + SYSTEMPLL2P0_N3_BUS_CSR_SNAP_6_REFCLK_BRDS, sizeof(uint32_t)); + ai_freq_ctrl6_1_brds.u32All = acp_reg_read_via_smn( + SYSTEMPLL2P0_N3_BUS_CSR_SNAP_6_AI_FREQ_CTRL6_1_BRDS, sizeof(uint32_t)); + ai_freq_ctrl7_brds.u32All = acp_reg_read_via_smn( + SYSTEMPLL2P0_N3_BUS_CSR_SNAP_6_AI_FREQ_CTRL7_BRDS, sizeof(uint32_t)); + ai_freq_ctrl9_brds.u32All = acp_reg_read_via_smn( + SYSTEMPLL2P0_N3_BUS_CSR_SNAP_6_AI_FREQ_CTRL9_BRDS, sizeof(uint32_t)); + + *_boot_ref_clk = 0.0f; + /* 16777216 = 2^24 */ + *_boot_ref_clk = + (((float)(ai_freq_ctrl6_1_brds.bitfields.fcw_int) + + ((float)(ai_freq_ctrl7_brds.bitfields.fcw_frac << 8 | + ai_freq_ctrl9_brds.bitfields.fcw0_frac_lsb) / + (16777216.0f))) * + (clk_pll_refclk.bitfields.refclk_rate / 2) * + (1 << non_ai_ctrl0.bitfields.vco_pre_div)); +} + +int acp_get_current_clk(acp_clock_type_t clk_type, float *clk_value) +{ + uint32_t temp_clk_value = 0; + clk_tick_cnt_config_reg_t clk_tick_config; + + clk_tick_config.u32All = acp_reg_read_via_smn(CLK_TICK_CNT_CONFIG_REG, sizeof(uint32_t)); + + /* Get current output clock based on CLK TYPE */ + if (acp_sclk_clock == clk_type) { + temp_clk_value = acp_reg_read_via_smn(CLK0_CURRENT_CNT, sizeof(uint32_t)); + } else if (acp_aclk_clock == clk_type) { + temp_clk_value = acp_reg_read_via_smn(CLK1_CURRENT_CNT, sizeof(uint32_t)); + } else { + return -EINVAL; /* Invalid parameter */ + } + + *clk_value = temp_clk_value / (clk_tick_config.bitfields.TIMER_THRESHOLD / 48.0f); + return 0; /* Success */ +} diff --git a/src/platform/amd/acp_7_x/platform.c b/src/platform/amd/acp_7_x/platform.c new file mode 100644 index 000000000000..f6778dd0bf98 --- /dev/null +++ b/src/platform/amd/acp_7_x/platform.c @@ -0,0 +1,205 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright(c) 2026 AMD. All rights reserved. + * + * Author: Sneha Voona <sneha.voona@amd.com> + * DineshKumar Kalva <DineshKumar.Kalva@amd.com> + * Basavaraj Hiregoudar <basavaraj.hiregoudar@amd.com> + */ + +#include <sof/compiler_info.h> +#include <sof/debug/debug.h> +#include <rtos/interrupt.h> +#include <sof/drivers/acp_dai_dma.h> +#include <sof/ipc/driver.h> +#include <rtos/timer.h> +#include <sof/fw-ready-metadata.h> +#include <sof/lib/agent.h> +#include <rtos/clk.h> +#include <sof/lib/cpu.h> +#include <sof/lib/dai.h> +#include <sof/lib/dma.h> +#include <sof/lib/mailbox.h> +#include <sof/lib/memory.h> +#include <sof/lib/mm_heap.h> +#include <sof/platform.h> +#include <sof/schedule/edf_schedule.h> +#include <sof/schedule/ll_schedule.h> +#include <sof/schedule/ll_schedule_domain.h> +#include <rtos/sof.h> +#include <sof/trace/dma-trace.h> +#include <ipc/dai.h> +#include <ipc/header.h> +#include <ipc/info.h> +#include <kernel/abi.h> +#include <kernel/ext_manifest.h> +#include <sof_versions.h> +#include <errno.h> +#include <stdint.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 + __attribute__((section(".fw_ready"))) = { + .hdr = { + .cmd = SOF_IPC_FW_READY, + .size = sizeof(struct sof_ipc_fw_ready), + }, + /* dspbox is for DSP initiated IPC, hostbox is for host initiated IPC */ + .version = { + .hdr.size = sizeof(struct sof_ipc_fw_version), + .micro = SOF_MICRO, + .minor = SOF_MINOR, + .major = SOF_MAJOR, +#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, + }, + .flags = DEBUG_SET_FW_READY_FLAGS, +}; + +#define NUM_ACP_WINDOWS 6 + +const struct ext_man_windows xsram_window +#ifdef CONFIG_AMD_BINARY_BUILD + __aligned(EXT_MAN_ALIGN) __unused = { +#else + __aligned(EXT_MAN_ALIGN) __section(".fw_metadata") __unused = { +#endif + .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 = NUM_ACP_WINDOWS, + .window = { + { + .type = SOF_IPC_REGION_UPBOX, + .id = 0, + .flags = 0, + .size = MAILBOX_DSPBOX_SIZE, + .offset = MAILBOX_DSPBOX_OFFSET, + }, + { + .type = SOF_IPC_REGION_DOWNBOX, + .id = 0, + .flags = 0, + .size = MAILBOX_HOSTBOX_SIZE, + .offset = MAILBOX_HOSTBOX_OFFSET, + }, + { + .type = SOF_IPC_REGION_DEBUG, + .id = 0, + .flags = 0, + .size = MAILBOX_DEBUG_SIZE, + .offset = MAILBOX_DEBUG_OFFSET, + }, + { + .type = SOF_IPC_REGION_TRACE, + .id = 0, + .flags = 0, + .size = MAILBOX_TRACE_SIZE, + .offset = MAILBOX_TRACE_OFFSET, + }, + { + .type = SOF_IPC_REGION_STREAM, + .id = 0, + .flags = 0, + .size = MAILBOX_STREAM_SIZE, + .offset = MAILBOX_STREAM_OFFSET, + }, + { + .type = SOF_IPC_REGION_EXCEPTION, + .id = 0, + .flags = 0, + .size = MAILBOX_EXCEPTION_SIZE, + .offset = MAILBOX_EXCEPTION_OFFSET, + }, + }, + }, +}; + +int platform_init(struct sof *sof) +{ + int ret; + + 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 = 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 = dmac_init(sof); + if (ret < 0) { + return -ENODEV; + } + + /* initialize the host IPC mechanisms */ + ipc_init(sof); + /* initialize the DAI mechanisms */ + ret = dai_init(sof); + if (ret < 0) { + return -ENODEV; + } + + /* show heap status */ + heap_trace_all(1); + return 0; +} + +int platform_boot_complete(uint32_t boot_message) +{ + acp_sw_intr_trig_t swintrtrig; + volatile acp_scratch_mem_config_t *pscratch_mem_cfg = + (volatile acp_scratch_mem_config_t *)(PU_SCRATCH_REG_BASE + SCRATCH_REG_OFFSET); + mailbox_dspbox_write(0, &ready, sizeof(ready)); +#ifdef CONFIG_AMD_BINARY_BUILD + mailbox_dspbox_write(sizeof(ready), &xsram_window.window, sizeof(xsram_window.window)); +#endif + pscratch_mem_cfg->acp_dsp_msg_write = 1; + acp_dsp_to_host_intr_trig(); + /* Configures the trigger bit in ACP_DSP_SW_INTR_TRIG register */ + swintrtrig = (acp_sw_intr_trig_t)io_reg_read(PU_REGISTER_BASE + ACP_SW_INTR_TRIG); + swintrtrig.bits.trig_dsp0_to_host_intr = INTERRUPT_DISABLE; + io_reg_write((PU_REGISTER_BASE + ACP_SW_INTR_TRIG), swintrtrig.u32all); + clock_set_freq(CLK_CPU(cpu_get_id()), CLK_DEFAULT_CPU_HZ); + return 0; +} + +int platform_context_save(struct sof *sof) +{ + return 0; +} +#ifdef __XCC__ +/* This is a stub for the Xtensa libc (not their newlib version), + * which inexplicably wants to pull in an unlink() implementation when + * linked against the C++ standard library. Obviously nothing in SOF + * uses the C library filesystem layer, this is just spurious. + */ +int __attribute__((weak)) _unlink_r(struct _reent *ptr, const char *file) +{ + return -1; +} +#endif 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/include/platform/lib/memory.h b/src/platform/amd/rembrandt/include/platform/lib/memory.h index 92ac53b4c2ed..fb3f1fe30fe8 100644 --- a/src/platform/amd/rembrandt/include/platform/lib/memory.h +++ b/src/platform/amd/rembrandt/include/platform/lib/memory.h @@ -152,15 +152,8 @@ #if !defined(__ASSEMBLER__) && !defined(LINKER) struct sof; - -#define SHARED_DATA void platform_init_memmap(struct sof *sof); -static inline void *platform_shared_get(void *ptr, int bytes) -{ - return ptr; -} - static inline void *platform_rfree_prepare(void *ptr) { return ptr; diff --git a/src/platform/amd/rembrandt/lib/clk.c b/src/platform/amd/rembrandt/lib/clk.c index 071e8c74168f..8f2529f21d57 100644 --- a/src/platform/amd/rembrandt/lib/clk.c +++ b/src/platform/amd/rembrandt/lib/clk.c @@ -19,7 +19,7 @@ const struct freq_table platform_cpu_freq[] = { STATIC_ASSERT(NUM_CPU_FREQ == ARRAY_SIZE(platform_cpu_freq), invalid_number_of_cpu_frequencies); -static SHARED_DATA struct clock_info platform_clocks_info[NUM_CLOCKS]; +static struct clock_info platform_clocks_info[NUM_CLOCKS]; static int acp_reg_read_via_smn(uint32_t reg_offset, uint32_t size) diff --git a/src/platform/amd/rembrandt/lib/dma.c b/src/platform/amd/rembrandt/lib/dma.c index d739e77bad1b..838e954dbc4e 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> @@ -24,7 +25,7 @@ extern struct dma_ops acp_dai_sp_dma_ops; #endif extern struct dma_ops acp_dai_hs_dma_ops; -SHARED_DATA struct dma dma[PLATFORM_NUM_DMACS] = { +struct dma dma[PLATFORM_NUM_DMACS] = { { .plat_data = { .id = DMA_ID_DMA0, @@ -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/amd/renoir/include/platform/lib/memory.h b/src/platform/amd/renoir/include/platform/lib/memory.h index 4caeb0e30d06..d9a3825fbc86 100644 --- a/src/platform/amd/renoir/include/platform/lib/memory.h +++ b/src/platform/amd/renoir/include/platform/lib/memory.h @@ -147,15 +147,8 @@ #if !defined(__ASSEMBLER__) && !defined(LINKER) struct sof; - -#define SHARED_DATA void platform_init_memmap(struct sof *sof); -static inline void *platform_shared_get(void *ptr, int bytes) -{ - return ptr; -} - static inline void *platform_rfree_prepare(void *ptr) { return ptr; diff --git a/src/platform/amd/renoir/lib/clk.c b/src/platform/amd/renoir/lib/clk.c index dddc974f631a..ea81e756e9a1 100644 --- a/src/platform/amd/renoir/lib/clk.c +++ b/src/platform/amd/renoir/lib/clk.c @@ -19,7 +19,7 @@ static struct freq_table platform_cpu_freq[] = { STATIC_ASSERT(NUM_CPU_FREQ == ARRAY_SIZE(platform_cpu_freq), invalid_number_of_cpu_frequencies); -static SHARED_DATA struct clock_info platform_clocks_info[NUM_CLOCKS]; +static struct clock_info platform_clocks_info[NUM_CLOCKS]; static int acp_reg_read_via_smn(uint32_t reg_offset, uint32_t size) @@ -122,7 +122,7 @@ void platform_clock_init(struct sof *sof) { int i; - sof->clocks = platform_shared_get(platform_clocks_info, sizeof(platform_clocks_info)); + sof->clocks = platform_clocks_info; for (i = 0; i < PLATFORM_CORE_COUNT; i++) { sof->clocks[i] = (struct clock_info) { diff --git a/src/platform/amd/renoir/lib/dma.c b/src/platform/amd/renoir/lib/dma.c index e6c75bee3905..2183a3464951 100644 --- a/src/platform/amd/renoir/lib/dma.c +++ b/src/platform/amd/renoir/lib/dma.c @@ -24,7 +24,7 @@ extern struct dma_ops acp_dai_bt_dma_ops; #endif extern struct dma_ops acp_dai_sp_dma_ops; -static SHARED_DATA struct dma dma[PLATFORM_NUM_DMACS] = { +static struct dma dma[PLATFORM_NUM_DMACS] = { { .plat_data = { .id = DMA_ID_DMA0, diff --git a/src/platform/amd/renoir/lib/memory.c b/src/platform/amd/renoir/lib/memory.c index 9f16b1977f05..bfee7c08174d 100644 --- a/src/platform/amd/renoir/lib/memory.c +++ b/src/platform/amd/renoir/lib/memory.c @@ -12,29 +12,29 @@ #include <ipc/topology.h> /* Heap blocks for system runtime */ -static SHARED_DATA struct block_hdr sys_rt_block64[HEAP_SYS_RT_COUNT64]; -static SHARED_DATA struct block_hdr sys_rt_block512[HEAP_SYS_RT_COUNT512]; -static SHARED_DATA struct block_hdr sys_rt_block1024[HEAP_SYS_RT_COUNT1024]; +static struct block_hdr sys_rt_block64[HEAP_SYS_RT_COUNT64]; +static struct block_hdr sys_rt_block512[HEAP_SYS_RT_COUNT512]; +static struct block_hdr sys_rt_block1024[HEAP_SYS_RT_COUNT1024]; /* Heap memory for system runtime */ -static SHARED_DATA struct block_map sys_rt_heap_map[] = { +static struct block_map sys_rt_heap_map[] = { BLOCK_DEF(64, HEAP_SYS_RT_COUNT64, sys_rt_block64), BLOCK_DEF(512, HEAP_SYS_RT_COUNT512, sys_rt_block512), BLOCK_DEF(1024, HEAP_SYS_RT_COUNT1024, sys_rt_block1024), }; /* Heap blocks for modules */ -static SHARED_DATA struct block_hdr mod_block16[HEAP_RT_COUNT16]; -static SHARED_DATA struct block_hdr mod_block32[HEAP_RT_COUNT32]; -static SHARED_DATA struct block_hdr mod_block64[HEAP_RT_COUNT64]; -static SHARED_DATA struct block_hdr mod_block128[HEAP_RT_COUNT128]; -static SHARED_DATA struct block_hdr mod_block256[HEAP_RT_COUNT256]; -static SHARED_DATA struct block_hdr mod_block512[HEAP_RT_COUNT512]; -static SHARED_DATA struct block_hdr mod_block1024[HEAP_RT_COUNT1024]; -static SHARED_DATA struct block_hdr mod_block2048[HEAP_RT_COUNT2048]; +static struct block_hdr mod_block16[HEAP_RT_COUNT16]; +static struct block_hdr mod_block32[HEAP_RT_COUNT32]; +static struct block_hdr mod_block64[HEAP_RT_COUNT64]; +static struct block_hdr mod_block128[HEAP_RT_COUNT128]; +static struct block_hdr mod_block256[HEAP_RT_COUNT256]; +static struct block_hdr mod_block512[HEAP_RT_COUNT512]; +static struct block_hdr mod_block1024[HEAP_RT_COUNT1024]; +static struct block_hdr mod_block2048[HEAP_RT_COUNT2048]; /* Heap memory map for modules */ -static SHARED_DATA struct block_map rt_heap_map[] = { +static struct block_map rt_heap_map[] = { BLOCK_DEF(16, HEAP_RT_COUNT16, mod_block16), BLOCK_DEF(32, HEAP_RT_COUNT32, mod_block32), BLOCK_DEF(64, HEAP_RT_COUNT64, mod_block64), @@ -46,14 +46,14 @@ static SHARED_DATA struct block_map rt_heap_map[] = { }; /* Heap blocks for buffers */ -static SHARED_DATA struct block_hdr buf_block[HEAP_BUFFER_COUNT]; +static struct block_hdr buf_block[HEAP_BUFFER_COUNT]; /* Heap memory map for buffers */ -static SHARED_DATA struct block_map buf_heap_map[] = { +static struct block_map buf_heap_map[] = { BLOCK_DEF(HEAP_BUFFER_BLOCK_SIZE, HEAP_BUFFER_COUNT, buf_block), }; -static SHARED_DATA struct mm memmap = { +static struct mm memmap = { .system[0] = { .heap = HEAP_SYSTEM_BASE, .size = HEAP_SYSTEM_SIZE, @@ -91,5 +91,5 @@ static SHARED_DATA struct mm memmap = { void platform_init_memmap(struct sof *sof) { /* memmap has been initialized statically as a part of .data */ - sof->memory_map = platform_shared_get(&memmap, sizeof(memmap)); + sof->memory_map = &memmap; } diff --git a/src/platform/amd/renoir/platform.c b/src/platform/amd/renoir/platform.c index ad2fa16cf0a0..bccbd7c5c399 100644 --- a/src/platform/amd/renoir/platform.c +++ b/src/platform/amd/renoir/platform.c @@ -124,7 +124,7 @@ const struct ext_man_windows xsram_window }, }; -static SHARED_DATA struct timer timer_shared = { +static struct timer timer_shared = { .id = TIMER0, .irq = IRQ_NUM_TIMER0, }; @@ -133,7 +133,7 @@ int platform_init(struct sof *sof) { int ret; - sof->platform_timer = platform_shared_get(&timer_shared, sizeof(timer_shared)); + sof->platform_timer = &timer_shared; sof->cpu_timers = sof->platform_timer; /* to view system memory */ diff --git a/src/platform/amd/vangogh/include/platform/lib/memory.h b/src/platform/amd/vangogh/include/platform/lib/memory.h index 6e0b768cc57a..a9f42b597dd9 100644 --- a/src/platform/amd/vangogh/include/platform/lib/memory.h +++ b/src/platform/amd/vangogh/include/platform/lib/memory.h @@ -146,15 +146,8 @@ #if !defined(__ASSEMBLER__) && !defined(LINKER) struct sof; - -#define SHARED_DATA void platform_init_memmap(struct sof *sof); -static inline void *platform_shared_get(void *ptr, int bytes) -{ - return ptr; -} - static inline void *platform_rfree_prepare(void *ptr) { return ptr; diff --git a/src/platform/amd/vangogh/lib/clk.c b/src/platform/amd/vangogh/lib/clk.c index 25f7befed373..b91472a739ce 100644 --- a/src/platform/amd/vangogh/lib/clk.c +++ b/src/platform/amd/vangogh/lib/clk.c @@ -21,7 +21,7 @@ const struct freq_table platform_cpu_freq[] = { STATIC_ASSERT(NUM_CPU_FREQ == ARRAY_SIZE(platform_cpu_freq), invalid_number_of_cpu_frequencies); -static SHARED_DATA struct clock_info platform_clocks_info[NUM_CLOCKS]; +static struct clock_info platform_clocks_info[NUM_CLOCKS]; static int acp_reg_read_via_smn(uint32_t reg_offset, uint32_t size) diff --git a/src/platform/amd/vangogh/lib/dma.c b/src/platform/amd/vangogh/lib/dma.c index cd764542dcfa..3a16b1d1ba65 100644 --- a/src/platform/amd/vangogh/lib/dma.c +++ b/src/platform/amd/vangogh/lib/dma.c @@ -23,7 +23,7 @@ extern struct dma_ops acp_dai_bt_dma_ops; extern struct dma_ops acp_dai_sp_dma_ops; extern struct dma_ops acp_dai_hs_dma_ops; -SHARED_DATA struct dma dma[PLATFORM_NUM_DMACS] = { +struct dma dma[PLATFORM_NUM_DMACS] = { { .plat_data = { .id = DMA_ID_DMA0, diff --git a/src/platform/amd/vangogh/lib/memory.c b/src/platform/amd/vangogh/lib/memory.c index d4a0ab144706..566175154721 100644 --- a/src/platform/amd/vangogh/lib/memory.c +++ b/src/platform/amd/vangogh/lib/memory.c @@ -13,29 +13,29 @@ #include <ipc/topology.h> /* Heap blocks for system runtime */ -static SHARED_DATA struct block_hdr sys_rt_block64[HEAP_SYS_RT_COUNT64]; -static SHARED_DATA struct block_hdr sys_rt_block512[HEAP_SYS_RT_COUNT512]; -static SHARED_DATA struct block_hdr sys_rt_block1024[HEAP_SYS_RT_COUNT1024]; +static struct block_hdr sys_rt_block64[HEAP_SYS_RT_COUNT64]; +static struct block_hdr sys_rt_block512[HEAP_SYS_RT_COUNT512]; +static struct block_hdr sys_rt_block1024[HEAP_SYS_RT_COUNT1024]; /* Heap memory for system runtime */ -static SHARED_DATA struct block_map sys_rt_heap_map[] = { +static struct block_map sys_rt_heap_map[] = { BLOCK_DEF(64, HEAP_SYS_RT_COUNT64, sys_rt_block64), BLOCK_DEF(512, HEAP_SYS_RT_COUNT512, sys_rt_block512), BLOCK_DEF(1024, HEAP_SYS_RT_COUNT1024, sys_rt_block1024), }; /* Heap blocks for modules */ -static SHARED_DATA struct block_hdr mod_block16[HEAP_RT_COUNT16]; -static SHARED_DATA struct block_hdr mod_block32[HEAP_RT_COUNT32]; -static SHARED_DATA struct block_hdr mod_block64[HEAP_RT_COUNT64]; -static SHARED_DATA struct block_hdr mod_block128[HEAP_RT_COUNT128]; -static SHARED_DATA struct block_hdr mod_block256[HEAP_RT_COUNT256]; -static SHARED_DATA struct block_hdr mod_block512[HEAP_RT_COUNT512]; -static SHARED_DATA struct block_hdr mod_block1024[HEAP_RT_COUNT1024]; -static SHARED_DATA struct block_hdr mod_block2048[HEAP_RT_COUNT2048]; +static struct block_hdr mod_block16[HEAP_RT_COUNT16]; +static struct block_hdr mod_block32[HEAP_RT_COUNT32]; +static struct block_hdr mod_block64[HEAP_RT_COUNT64]; +static struct block_hdr mod_block128[HEAP_RT_COUNT128]; +static struct block_hdr mod_block256[HEAP_RT_COUNT256]; +static struct block_hdr mod_block512[HEAP_RT_COUNT512]; +static struct block_hdr mod_block1024[HEAP_RT_COUNT1024]; +static struct block_hdr mod_block2048[HEAP_RT_COUNT2048]; /* Heap memory map for modules */ -static SHARED_DATA struct block_map rt_heap_map[] = { +static struct block_map rt_heap_map[] = { BLOCK_DEF(16, HEAP_RT_COUNT16, mod_block16), BLOCK_DEF(32, HEAP_RT_COUNT32, mod_block32), BLOCK_DEF(64, HEAP_RT_COUNT64, mod_block64), @@ -47,14 +47,14 @@ static SHARED_DATA struct block_map rt_heap_map[] = { }; /* Heap blocks for buffers */ -static SHARED_DATA struct block_hdr buf_block[HEAP_BUFFER_COUNT]; +static struct block_hdr buf_block[HEAP_BUFFER_COUNT]; /* Heap memory map for buffers */ -static SHARED_DATA struct block_map buf_heap_map[] = { +static struct block_map buf_heap_map[] = { BLOCK_DEF(HEAP_BUFFER_BLOCK_SIZE, HEAP_BUFFER_COUNT, buf_block), }; -static SHARED_DATA struct mm memmap = { +static struct mm memmap = { .system[0] = { .heap = HEAP_SYSTEM_BASE, .size = HEAP_SYSTEM_SIZE, diff --git a/src/platform/amd/vangogh/platform.c b/src/platform/amd/vangogh/platform.c index 300239a023be..da2c24bfb067 100644 --- a/src/platform/amd/vangogh/platform.c +++ b/src/platform/amd/vangogh/platform.c @@ -123,7 +123,7 @@ const struct ext_man_windows xsram_window }, }; -static SHARED_DATA struct timer timer = { +static struct timer timer = { .id = TIMER0, .irq = IRQ_NUM_TIMER0, }; diff --git a/src/platform/imx8/include/platform/lib/memory.h b/src/platform/imx8/include/platform/lib/memory.h index 1c3d88fe4d4d..862abcd49d25 100644 --- a/src/platform/imx8/include/platform/lib/memory.h +++ b/src/platform/imx8/include/platform/lib/memory.h @@ -179,19 +179,8 @@ struct sof; -/** - * \brief Data shared between different cores. - * Does nothing, since IMX doesn't support SMP. - */ -#define SHARED_DATA - void platform_init_memmap(struct sof *sof); -static inline void *platform_shared_get(void *ptr, int bytes) -{ - return ptr; -} - #define uncache_to_cache(address) address #define cache_to_uncache(address) address #define cache_to_uncache_init(address) address @@ -200,9 +189,8 @@ static inline void *platform_shared_get(void *ptr, int bytes) /** * \brief Function for keeping shared data synchronized. * It's used after usage of data shared by different cores. - * Such data is either statically marked with SHARED_DATA - * or dynamically allocated with SOF_MEM_FLAG_SHARED flag. - * Does nothing, since IMX doesn't support SMP. + * Such data is dynamically allocated with SOF_MEM_FLAG_SHARED + * flag. Does nothing, since IMX doesn't support SMP. */ static inline void *platform_rfree_prepare(void *ptr) diff --git a/src/platform/imx8/lib/clk.c b/src/platform/imx8/lib/clk.c index 45edfe98461d..4eeb2dac3357 100644 --- a/src/platform/imx8/lib/clk.c +++ b/src/platform/imx8/lib/clk.c @@ -24,13 +24,13 @@ const struct freq_table platform_cpu_freq[] = { STATIC_ASSERT(NUM_CPU_FREQ == ARRAY_SIZE(platform_cpu_freq), invalid_number_of_cpu_frequencies); -static SHARED_DATA struct clock_info platform_clocks_info[NUM_CLOCKS]; +static struct clock_info platform_clocks_info[NUM_CLOCKS]; void platform_clock_init(struct sof *sof) { int i; - sof->clocks = platform_shared_get(platform_clocks_info, sizeof(platform_clocks_info)); + sof->clocks = platform_clocks_info; for (i = 0; i < CONFIG_CORE_COUNT; i++) { sof->clocks[i] = (struct clock_info) { diff --git a/src/platform/imx8/lib/dai.c b/src/platform/imx8/lib/dai.c index 69366cf18d3f..a0f8fcc39135 100644 --- a/src/platform/imx8/lib/dai.c +++ b/src/platform/imx8/lib/dai.c @@ -15,7 +15,7 @@ #include <ipc/dai.h> #include <ipc/stream.h> -static SHARED_DATA struct dai esai[] = { +static struct dai esai[] = { { .index = 0, .plat_data = { @@ -39,7 +39,7 @@ static SHARED_DATA struct dai esai[] = { }, }; -static SHARED_DATA struct dai sai[] = { +static struct dai sai[] = { { .index = 1, .plat_data = { diff --git a/src/platform/imx8/lib/dma.c b/src/platform/imx8/lib/dma.c index e5a40d251557..dc3efa653d0a 100644 --- a/src/platform/imx8/lib/dma.c +++ b/src/platform/imx8/lib/dma.c @@ -22,7 +22,7 @@ static const int edma0_ints[EDMA0_CHAN_MAX] = { [EDMA0_SAI_CHAN_TX] = EDMA0_SAI_CHAN_TX_IRQ, }; -static SHARED_DATA struct dma dma[PLATFORM_NUM_DMACS] = { +static struct dma dma[PLATFORM_NUM_DMACS] = { { .plat_data = { .id = DMA_ID_EDMA0, diff --git a/src/platform/imx8/lib/memory.c b/src/platform/imx8/lib/memory.c index 78cb05c86cbb..81794bafd6d6 100644 --- a/src/platform/imx8/lib/memory.c +++ b/src/platform/imx8/lib/memory.c @@ -12,29 +12,29 @@ #include <ipc/topology.h> /* Heap blocks for system runtime */ -static SHARED_DATA struct block_hdr sys_rt_block64[HEAP_SYS_RT_COUNT64]; -static SHARED_DATA struct block_hdr sys_rt_block512[HEAP_SYS_RT_COUNT512]; -static SHARED_DATA struct block_hdr sys_rt_block1024[HEAP_SYS_RT_COUNT1024]; +static struct block_hdr sys_rt_block64[HEAP_SYS_RT_COUNT64]; +static struct block_hdr sys_rt_block512[HEAP_SYS_RT_COUNT512]; +static struct block_hdr sys_rt_block1024[HEAP_SYS_RT_COUNT1024]; /* Heap memory for system runtime */ -static SHARED_DATA struct block_map sys_rt_heap_map[] = { +static struct block_map sys_rt_heap_map[] = { BLOCK_DEF(64, HEAP_SYS_RT_COUNT64, sys_rt_block64), BLOCK_DEF(512, HEAP_SYS_RT_COUNT512, sys_rt_block512), BLOCK_DEF(1024, HEAP_SYS_RT_COUNT1024, sys_rt_block1024), }; /* Heap blocks for modules */ -static SHARED_DATA struct block_hdr mod_block16[HEAP_RT_COUNT16]; -static SHARED_DATA struct block_hdr mod_block32[HEAP_RT_COUNT32]; -static SHARED_DATA struct block_hdr mod_block64[HEAP_RT_COUNT64]; -static SHARED_DATA struct block_hdr mod_block128[HEAP_RT_COUNT128]; -static SHARED_DATA struct block_hdr mod_block256[HEAP_RT_COUNT256]; -static SHARED_DATA struct block_hdr mod_block512[HEAP_RT_COUNT512]; -static SHARED_DATA struct block_hdr mod_block1024[HEAP_RT_COUNT1024]; -static SHARED_DATA struct block_hdr mod_block2048[HEAP_RT_COUNT2048]; +static struct block_hdr mod_block16[HEAP_RT_COUNT16]; +static struct block_hdr mod_block32[HEAP_RT_COUNT32]; +static struct block_hdr mod_block64[HEAP_RT_COUNT64]; +static struct block_hdr mod_block128[HEAP_RT_COUNT128]; +static struct block_hdr mod_block256[HEAP_RT_COUNT256]; +static struct block_hdr mod_block512[HEAP_RT_COUNT512]; +static struct block_hdr mod_block1024[HEAP_RT_COUNT1024]; +static struct block_hdr mod_block2048[HEAP_RT_COUNT2048]; /* Heap memory map for modules */ -static SHARED_DATA struct block_map rt_heap_map[] = { +static struct block_map rt_heap_map[] = { BLOCK_DEF(16, HEAP_RT_COUNT16, mod_block16), BLOCK_DEF(32, HEAP_RT_COUNT32, mod_block32), BLOCK_DEF(64, HEAP_RT_COUNT64, mod_block64), @@ -46,14 +46,14 @@ static SHARED_DATA struct block_map rt_heap_map[] = { }; /* Heap blocks for buffers */ -static SHARED_DATA struct block_hdr buf_block[HEAP_BUFFER_COUNT]; +static struct block_hdr buf_block[HEAP_BUFFER_COUNT]; /* Heap memory map for buffers */ -static SHARED_DATA struct block_map buf_heap_map[] = { +static struct block_map buf_heap_map[] = { BLOCK_DEF(HEAP_BUFFER_BLOCK_SIZE, HEAP_BUFFER_COUNT, buf_block), }; -static SHARED_DATA struct mm memmap = { +static struct mm memmap = { .system[0] = { .heap = HEAP_SYSTEM_BASE, .size = HEAP_SYSTEM_SIZE, @@ -95,5 +95,5 @@ static SHARED_DATA struct mm memmap = { void platform_init_memmap(struct sof *sof) { /* memmap has been initialized statically as a part of .data */ - sof->memory_map = platform_shared_get(&memmap, sizeof(memmap)); + sof->memory_map = &memmap; } diff --git a/src/platform/imx8m/include/platform/lib/memory.h b/src/platform/imx8m/include/platform/lib/memory.h index 6b2c00f89bad..671a9ca77095 100644 --- a/src/platform/imx8m/include/platform/lib/memory.h +++ b/src/platform/imx8m/include/platform/lib/memory.h @@ -198,12 +198,6 @@ struct sof; -/** - * \brief Data shared between different cores. - * Does nothing, since IMX doesn't support SMP. - */ -#define SHARED_DATA - void platform_init_memmap(struct sof *sof); #define uncache_to_cache(address) address @@ -211,17 +205,11 @@ void platform_init_memmap(struct sof *sof); #define cache_to_uncache_init(address) address #define is_uncached(address) 0 -static inline void *platform_shared_get(void *ptr, int bytes) -{ - return ptr; -} - /** * \brief Function for keeping shared data synchronized. * It's used after usage of data shared by different cores. - * Such data is either statically marked with SHARED_DATA - * or dynamically allocated with SOF_MEM_FLAG_SHARED flag. - * Does nothing, since IMX doesn't support SMP. + * Such data is dynamically allocated with SOF_MEM_FLAG_SHARED + * flag. Does nothing, since IMX doesn't support SMP. */ static inline void *platform_rfree_prepare(void *ptr) diff --git a/src/platform/imx8m/lib/clk.c b/src/platform/imx8m/lib/clk.c index 37b115e507a2..573858873ef8 100644 --- a/src/platform/imx8m/lib/clk.c +++ b/src/platform/imx8m/lib/clk.c @@ -19,13 +19,13 @@ const struct freq_table platform_cpu_freq[] = { STATIC_ASSERT(NUM_CPU_FREQ == ARRAY_SIZE(platform_cpu_freq), invalid_number_of_cpu_frequencies); -static SHARED_DATA struct clock_info platform_clocks_info[NUM_CLOCKS]; +static struct clock_info platform_clocks_info[NUM_CLOCKS]; void platform_clock_init(struct sof *sof) { int i; - sof->clocks = platform_shared_get(platform_clocks_info, sizeof(platform_clocks_info)); + sof->clocks = platform_clocks_info; for (i = 0; i < CONFIG_CORE_COUNT; i++) { sof->clocks[i] = (struct clock_info) { diff --git a/src/platform/imx8m/lib/dai.c b/src/platform/imx8m/lib/dai.c index 1293cdc6fd7f..433c27ef141a 100644 --- a/src/platform/imx8m/lib/dai.c +++ b/src/platform/imx8m/lib/dai.c @@ -15,7 +15,7 @@ #include <ipc/dai.h> #include <ipc/stream.h> -static SHARED_DATA struct dai sai[] = { +static struct dai sai[] = { { .index = 1, .plat_data = { @@ -144,7 +144,7 @@ static SHARED_DATA struct dai sai[] = { }; -static SHARED_DATA struct dai micfil[] = { +static struct dai micfil[] = { { .index = 2, .plat_data = { diff --git a/src/platform/imx8m/lib/dma.c b/src/platform/imx8m/lib/dma.c index f9cb5a74682a..c9fe4a98e030 100644 --- a/src/platform/imx8m/lib/dma.c +++ b/src/platform/imx8m/lib/dma.c @@ -14,7 +14,7 @@ extern struct dma_ops dummy_dma_ops; extern struct dma_ops sdma_ops; -static SHARED_DATA struct dma dma[PLATFORM_NUM_DMACS] = { +static struct dma dma[PLATFORM_NUM_DMACS] = { { .plat_data = { .id = DMA_ID_HOST, diff --git a/src/platform/imx8m/lib/memory.c b/src/platform/imx8m/lib/memory.c index 85596ca2b525..3f2eceee1afb 100644 --- a/src/platform/imx8m/lib/memory.c +++ b/src/platform/imx8m/lib/memory.c @@ -12,30 +12,30 @@ #include <ipc/topology.h> /* Heap blocks for system runtime */ -static SHARED_DATA struct block_hdr sys_rt_block64[HEAP_SYS_RT_COUNT64]; -static SHARED_DATA struct block_hdr sys_rt_block512[HEAP_SYS_RT_COUNT512]; -static SHARED_DATA struct block_hdr sys_rt_block1024[HEAP_SYS_RT_COUNT1024]; +static struct block_hdr sys_rt_block64[HEAP_SYS_RT_COUNT64]; +static struct block_hdr sys_rt_block512[HEAP_SYS_RT_COUNT512]; +static struct block_hdr sys_rt_block1024[HEAP_SYS_RT_COUNT1024]; /* Heap memory for system runtime */ -static SHARED_DATA struct block_map sys_rt_heap_map[] = { +static struct block_map sys_rt_heap_map[] = { BLOCK_DEF(64, HEAP_SYS_RT_COUNT64, sys_rt_block64), BLOCK_DEF(512, HEAP_SYS_RT_COUNT512, sys_rt_block512), BLOCK_DEF(1024, HEAP_SYS_RT_COUNT1024, sys_rt_block1024), }; /* Heap blocks for modules */ -static SHARED_DATA struct block_hdr mod_block16[HEAP_RT_COUNT16]; -static SHARED_DATA struct block_hdr mod_block32[HEAP_RT_COUNT32]; -static SHARED_DATA struct block_hdr mod_block64[HEAP_RT_COUNT64]; -static SHARED_DATA struct block_hdr mod_block128[HEAP_RT_COUNT128]; -static SHARED_DATA struct block_hdr mod_block256[HEAP_RT_COUNT256]; -static SHARED_DATA struct block_hdr mod_block512[HEAP_RT_COUNT512]; -static SHARED_DATA struct block_hdr mod_block1024[HEAP_RT_COUNT1024]; -static SHARED_DATA struct block_hdr mod_block2048[HEAP_RT_COUNT2048]; -static SHARED_DATA struct block_hdr mod_block4096[HEAP_RT_COUNT4096]; +static struct block_hdr mod_block16[HEAP_RT_COUNT16]; +static struct block_hdr mod_block32[HEAP_RT_COUNT32]; +static struct block_hdr mod_block64[HEAP_RT_COUNT64]; +static struct block_hdr mod_block128[HEAP_RT_COUNT128]; +static struct block_hdr mod_block256[HEAP_RT_COUNT256]; +static struct block_hdr mod_block512[HEAP_RT_COUNT512]; +static struct block_hdr mod_block1024[HEAP_RT_COUNT1024]; +static struct block_hdr mod_block2048[HEAP_RT_COUNT2048]; +static struct block_hdr mod_block4096[HEAP_RT_COUNT4096]; /* Heap memory map for modules */ -static SHARED_DATA struct block_map rt_heap_map[] = { +static struct block_map rt_heap_map[] = { BLOCK_DEF(16, HEAP_RT_COUNT16, mod_block16), BLOCK_DEF(32, HEAP_RT_COUNT32, mod_block32), BLOCK_DEF(64, HEAP_RT_COUNT64, mod_block64), @@ -48,14 +48,14 @@ static SHARED_DATA struct block_map rt_heap_map[] = { }; /* Heap blocks for buffers */ -static SHARED_DATA struct block_hdr buf_block[HEAP_BUFFER_COUNT]; +static struct block_hdr buf_block[HEAP_BUFFER_COUNT]; /* Heap memory map for buffers */ -static SHARED_DATA struct block_map buf_heap_map[] = { +static struct block_map buf_heap_map[] = { BLOCK_DEF(HEAP_BUFFER_BLOCK_SIZE, HEAP_BUFFER_COUNT, buf_block), }; -static SHARED_DATA struct mm memmap = { +static struct mm memmap = { .system[0] = { .heap = HEAP_SYSTEM_BASE, .size = HEAP_SYSTEM_SIZE, @@ -97,5 +97,5 @@ static SHARED_DATA struct mm memmap = { void platform_init_memmap(struct sof *sof) { /* memmap has been initialized statically as a part of .data */ - sof->memory_map = platform_shared_get(&memmap, sizeof(memmap)); + sof->memory_map = &memmap; } 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..14886bf371fc --- /dev/null +++ b/src/platform/imx8m_cm7/include/platform/lib/memory.h @@ -0,0 +1,35 @@ +/* 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 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 + +#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/imx8ulp/include/platform/lib/memory.h b/src/platform/imx8ulp/include/platform/lib/memory.h index 00aa9b8b2a41..a84c6f56da48 100644 --- a/src/platform/imx8ulp/include/platform/lib/memory.h +++ b/src/platform/imx8ulp/include/platform/lib/memory.h @@ -182,12 +182,6 @@ struct sof; -/** - * \brief Data shared between different cores. - * Does nothing, since IMX doesn't support SMP. - */ -#define SHARED_DATA - void platform_init_memmap(struct sof *sof); #define uncache_to_cache(address) address @@ -195,17 +189,11 @@ void platform_init_memmap(struct sof *sof); #define cache_to_uncache_init(address) address #define is_uncached(address) 0 -static inline void *platform_shared_get(void *ptr, int bytes) -{ - return ptr; -} - /** * \brief Function for keeping shared data synchronized. * It's used after usage of data shared by different cores. - * Such data is either statically marked with SHARED_DATA - * or dynamically allocated with SOF_MEM_FLAG_SHARED flag. - * Does nothing, since IMX doesn't support SMP. + * Such data is dynamically allocated with SOF_MEM_FLAG_SHARED + * flag. Does nothing, since IMX doesn't support SMP. */ static inline void platform_shared_commit(void *ptr, int bytes) { } diff --git a/src/platform/imx8ulp/lib/clk.c b/src/platform/imx8ulp/lib/clk.c index 21d1f4166188..3bdc873eacf9 100644 --- a/src/platform/imx8ulp/lib/clk.c +++ b/src/platform/imx8ulp/lib/clk.c @@ -19,13 +19,13 @@ const struct freq_table platform_cpu_freq[] = { STATIC_ASSERT(NUM_CPU_FREQ == ARRAY_SIZE(platform_cpu_freq), invalid_number_of_cpu_frequencies); -static SHARED_DATA struct clock_info platform_clocks_info[NUM_CLOCKS]; +static struct clock_info platform_clocks_info[NUM_CLOCKS]; void platform_clock_init(struct sof *sof) { int i; - sof->clocks = platform_shared_get(platform_clocks_info, sizeof(platform_clocks_info)); + sof->clocks = platform_clocks_info; for (i = 0; i < CONFIG_CORE_COUNT; i++) { sof->clocks[i] = (struct clock_info) { diff --git a/src/platform/imx8ulp/lib/dai.c b/src/platform/imx8ulp/lib/dai.c index 879da871ffcb..797bbc3260be 100644 --- a/src/platform/imx8ulp/lib/dai.c +++ b/src/platform/imx8ulp/lib/dai.c @@ -14,7 +14,7 @@ #include <ipc/dai.h> #include <ipc/stream.h> -static SHARED_DATA struct dai sai[] = { +static struct dai sai[] = { { .index = 5, .plat_data = { diff --git a/src/platform/imx8ulp/lib/dma.c b/src/platform/imx8ulp/lib/dma.c index 0cfc47f68039..81b3e0f09de2 100644 --- a/src/platform/imx8ulp/lib/dma.c +++ b/src/platform/imx8ulp/lib/dma.c @@ -20,7 +20,7 @@ static const int edma2_ints[IMX8ULP_EDMA2_CHAN_MAX] = { [IMX8ULP_EDMA2_CHAN1] = IMX8ULP_EDMA2_CHAN1_IRQ, }; -static SHARED_DATA struct dma dma[PLATFORM_NUM_DMACS] = { +static struct dma dma[PLATFORM_NUM_DMACS] = { { .plat_data = { .id = DMA_ID_EDMA2, diff --git a/src/platform/imx8ulp/lib/memory.c b/src/platform/imx8ulp/lib/memory.c index ac80bf55aae6..9224dae6e05f 100644 --- a/src/platform/imx8ulp/lib/memory.c +++ b/src/platform/imx8ulp/lib/memory.c @@ -13,29 +13,29 @@ #include <ipc/topology.h> /* Heap blocks for system runtime */ -static SHARED_DATA struct block_hdr sys_rt_block64[HEAP_SYS_RT_COUNT64]; -static SHARED_DATA struct block_hdr sys_rt_block512[HEAP_SYS_RT_COUNT512]; -static SHARED_DATA struct block_hdr sys_rt_block1024[HEAP_SYS_RT_COUNT1024]; +static struct block_hdr sys_rt_block64[HEAP_SYS_RT_COUNT64]; +static struct block_hdr sys_rt_block512[HEAP_SYS_RT_COUNT512]; +static struct block_hdr sys_rt_block1024[HEAP_SYS_RT_COUNT1024]; /* Heap memory for system runtime */ -static SHARED_DATA struct block_map sys_rt_heap_map[] = { +static struct block_map sys_rt_heap_map[] = { BLOCK_DEF(64, HEAP_SYS_RT_COUNT64, sys_rt_block64), BLOCK_DEF(512, HEAP_SYS_RT_COUNT512, sys_rt_block512), BLOCK_DEF(1024, HEAP_SYS_RT_COUNT1024, sys_rt_block1024), }; /* Heap blocks for modules */ -static SHARED_DATA struct block_hdr mod_block16[HEAP_RT_COUNT16]; -static SHARED_DATA struct block_hdr mod_block32[HEAP_RT_COUNT32]; -static SHARED_DATA struct block_hdr mod_block64[HEAP_RT_COUNT64]; -static SHARED_DATA struct block_hdr mod_block128[HEAP_RT_COUNT128]; -static SHARED_DATA struct block_hdr mod_block256[HEAP_RT_COUNT256]; -static SHARED_DATA struct block_hdr mod_block512[HEAP_RT_COUNT512]; -static SHARED_DATA struct block_hdr mod_block1024[HEAP_RT_COUNT1024]; -static SHARED_DATA struct block_hdr mod_block2048[HEAP_RT_COUNT2048]; +static struct block_hdr mod_block16[HEAP_RT_COUNT16]; +static struct block_hdr mod_block32[HEAP_RT_COUNT32]; +static struct block_hdr mod_block64[HEAP_RT_COUNT64]; +static struct block_hdr mod_block128[HEAP_RT_COUNT128]; +static struct block_hdr mod_block256[HEAP_RT_COUNT256]; +static struct block_hdr mod_block512[HEAP_RT_COUNT512]; +static struct block_hdr mod_block1024[HEAP_RT_COUNT1024]; +static struct block_hdr mod_block2048[HEAP_RT_COUNT2048]; /* Heap memory map for modules */ -static SHARED_DATA struct block_map rt_heap_map[] = { +static struct block_map rt_heap_map[] = { BLOCK_DEF(16, HEAP_RT_COUNT16, mod_block16), BLOCK_DEF(32, HEAP_RT_COUNT32, mod_block32), BLOCK_DEF(64, HEAP_RT_COUNT64, mod_block64), @@ -47,14 +47,14 @@ static SHARED_DATA struct block_map rt_heap_map[] = { }; /* Heap blocks for buffers */ -static SHARED_DATA struct block_hdr buf_block[HEAP_BUFFER_COUNT]; +static struct block_hdr buf_block[HEAP_BUFFER_COUNT]; /* Heap memory map for buffers */ -static SHARED_DATA struct block_map buf_heap_map[] = { +static struct block_map buf_heap_map[] = { BLOCK_DEF(HEAP_BUFFER_BLOCK_SIZE, HEAP_BUFFER_COUNT, buf_block), }; -static SHARED_DATA struct mm memmap = { +static struct mm memmap = { .system[0] = { .heap = HEAP_SYSTEM_BASE, .size = HEAP_SYSTEM_SIZE, @@ -96,5 +96,5 @@ static SHARED_DATA struct mm memmap = { void platform_init_memmap(struct sof *sof) { /* memmap has been initialized statically as a part of .data */ - sof->memory_map = platform_shared_get(&memmap, sizeof(memmap)); + sof->memory_map = &memmap; } diff --git a/src/platform/imx93_a55/include/platform/lib/memory.h b/src/platform/imx93_a55/include/platform/lib/memory.h index 77ca3ec6103b..6b74eb7e5d53 100644 --- a/src/platform/imx93_a55/include/platform/lib/memory.h +++ b/src/platform/imx93_a55/include/platform/lib/memory.h @@ -20,8 +20,6 @@ * thing so all the below cache/shared data "management" * functions aren't necessary. */ -#define SHARED_DATA - #define uncache_to_cache(address) address #define cache_to_uncache(address) address #define cache_to_uncache_init(address) address @@ -58,11 +56,6 @@ /* WM8962 is connected to SAI3 */ #define SAI3_BASE 0x42660000 -static inline void *platform_shared_get(void *ptr, int bytes) -{ - return ptr; -} - #endif /* __PLATFORM_LIB_MEMORY_H__ */ #else diff --git a/src/platform/imx95/include/platform/lib/memory.h b/src/platform/imx95/include/platform/lib/memory.h index bf3ff05a5c69..0679e74242de 100644 --- a/src/platform/imx95/include/platform/lib/memory.h +++ b/src/platform/imx95/include/platform/lib/memory.h @@ -12,8 +12,6 @@ #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 @@ -28,11 +26,6 @@ /* WAKEUP domain MU7 side B */ #define MU_BASE 0x42440000UL -static inline void *platform_shared_get(void *ptr, int bytes) -{ - return ptr; -} - #endif /* __PLATFORM_LIB_MEMORY_H__ */ #else diff --git a/src/platform/intel/ace/include/ace/lib/memory.h b/src/platform/intel/ace/include/ace/lib/memory.h index f021b18b00c9..803acec71ce9 100644 --- a/src/platform/intel/ace/include/ace/lib/memory.h +++ b/src/platform/intel/ace/include/ace/lib/memory.h @@ -19,34 +19,11 @@ /* data cache line alignment */ #define PLATFORM_DCACHE_ALIGN DCACHE_LINE_SIZE -/** - * \brief Data shared between different cores. - * Placed into dedicated section, which should be accessed through - * uncached memory region. SMP platforms without uncache can simply - * align to cache line size instead. - */ -#define SHARED_DATA - #include <zephyr/cache.h> #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) - -/** - * \brief Returns pointer to the memory shared by multiple cores. - * \param[in,out] ptr Initial pointer to the allocated memory. - * \param[in] bytes Size of the allocated memory - * \return Appropriate pointer to the shared memory. - * - * This function is called only once right after allocation of shared memory. - * Platforms with uncached memory region should return aliased address. - * On platforms without such region simple invalidate is enough. - */ -static inline void *platform_shared_get(void *ptr, int bytes) -{ - return ptr; -} +#define is_uncached(address) (!sys_cache_is_ptr_cached(address)) #endif /* __ACE_LIB_MEMORY_H__ */ diff --git a/src/platform/intel/ace/lib/watchdog.c b/src/platform/intel/ace/lib/watchdog.c index dcae80da801d..95ff5e3c4c6b 100644 --- a/src/platform/intel/ace/lib/watchdog.c +++ b/src/platform/intel/ace/lib/watchdog.c @@ -5,9 +5,11 @@ * Author: Adrian Warecki <adrian.warecki@intel.com> */ +#include <sof/lib/memory.h> #include <sof/lib/uuid.h> #include <rtos/idc.h> #include <sof/schedule/ll_schedule_domain.h> +#include <sof/ipc/driver.h> #include <sof/ipc/msg.h> #include <ipc4/notification.h> @@ -16,8 +18,6 @@ #if DT_NODE_HAS_STATUS(DT_NODELABEL(adsp_watchdog), okay) #include <adsp_watchdog.h> -#include <intel_adsp_ipc.h> -#include <intel_adsp_ipc_devtree.h> LOG_MODULE_REGISTER(wdt, CONFIG_SOF_LOG_LEVEL); @@ -26,6 +26,7 @@ SOF_DEFINE_REG_UUID(wdt); DECLARE_TR_CTX(wdt_tr, SOF_UUID(wdt_uuid), LOG_LEVEL_INFO); static const struct device *const watchdog = DEVICE_DT_GET(DT_NODELABEL(adsp_watchdog)); +static struct ipc_msg primary_timeout_ipc; static struct ipc_msg secondary_timeout_ipc; static void watchdog_primary_core_action_on_timeout(void) @@ -34,8 +35,15 @@ static void watchdog_primary_core_action_on_timeout(void) /* Send Watchdog Timeout IPC notification */ ipc4_notification_watchdog_init(¬if, cpu_get_id(), true); - intel_adsp_ipc_send_message_emergency(INTEL_ADSP_IPC_HOST_DEV, - notif.primary.dat, notif.extension.dat); + primary_timeout_ipc.header = notif.primary.dat; + primary_timeout_ipc.extension = notif.extension.dat; + + /* + * The primary core low latency scheduler is stalled, so bypass the IPC + * queue and push the notification straight to the host using the direct + * (critical) send path. + */ + ipc_platform_send_msg_direct(&primary_timeout_ipc); } static void watchdog_secondary_core_action_on_timeout(void) @@ -72,7 +80,7 @@ static void watchdog_timeout(const struct device *dev, int core) watchdog_secondary_core_action_on_timeout(); } -void watchdog_init(void) +__cold void watchdog_init(void) { int i, ret; const struct wdt_timeout_cfg watchdog_config = { @@ -80,6 +88,12 @@ void watchdog_init(void) .callback = watchdog_timeout, }; + assert_can_be_cold(); + + primary_timeout_ipc.tx_data = NULL; + primary_timeout_ipc.tx_size = 0; + list_init(&primary_timeout_ipc.list); + secondary_timeout_ipc.tx_data = NULL; secondary_timeout_ipc.tx_size = 0; list_init(&secondary_timeout_ipc.list); diff --git a/src/platform/intel/ace/platform.c b/src/platform/intel/ace/platform.c index 1fde2713049d..f19003117e08 100644 --- a/src/platform/intel/ace/platform.c +++ b/src/platform/intel/ace/platform.c @@ -65,6 +65,8 @@ __cold int platform_boot_complete(uint32_t boot_message) { struct ipc_cmd_hdr header; + assert_can_be_cold(); + /* get any IPC specific boot message and optional data */ ipc_boot_complete_msg(&header, 0); header.pri |= boot_message; @@ -92,6 +94,8 @@ __cold int platform_init(struct sof *sof) { int ret; + assert_can_be_cold(); + trace_point(TRACE_BOOT_PLATFORM_CLOCK); platform_clock_init(sof); diff --git a/src/platform/intel/cavs/include/cavs/lib/memory.h b/src/platform/intel/cavs/include/cavs/lib/memory.h index b53c76d64b6d..ee8cd030df3c 100644 --- a/src/platform/intel/cavs/include/cavs/lib/memory.h +++ b/src/platform/intel/cavs/include/cavs/lib/memory.h @@ -33,18 +33,6 @@ struct sof; -/** - * \brief Data shared between different cores. - * Placed into dedicated section, which should be accessed through - * uncached memory region. SMP platforms without uncache can simply - * align to cache line size instead. - */ -#if !defined(UNIT_TEST) && !defined __ZEPHYR__ -#define SHARED_DATA __section(".shared_data") __attribute__((aligned(PLATFORM_DCACHE_ALIGN))) -#else -#define SHARED_DATA -#endif - #define SRAM_ALIAS_BASE 0x9E000000 #define SRAM_ALIAS_MASK 0xFF000000 #define SRAM_ALIAS_OFFSET SRAM_UNCACHED_ALIAS @@ -75,26 +63,6 @@ static inline void *cache_to_uncache(void __sparse_cache *address) #define cache_to_uncache_init(address) address #endif -/** - * \brief Returns pointer to the memory shared by multiple cores. - * \param[in,out] ptr Initial pointer to the allocated memory. - * \param[in] bytes Size of the allocated memory - * \return Appropriate pointer to the shared memory. - * - * This function is called only once right after allocation of shared memory. - * Platforms with uncached memory region should return aliased address. - * On platforms without such region simple invalidate is enough. - */ -static inline void *platform_shared_get(void *ptr, int bytes) -{ -#if CONFIG_CORE_COUNT > 1 && !defined __ZEPHYR__ - dcache_invalidate_region((__sparse_force void __sparse_cache *)ptr, bytes); - return cache_to_uncache(ptr); -#else - return ptr; -#endif -} - /** * \brief Transforms pointer if necessary before freeing the memory. * \param[in,out] ptr Pointer to the allocated memory. 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/include/platform/lib/ll_schedule.h b/src/platform/library/include/platform/lib/ll_schedule.h index 06c918b84fdc..b5ba7f94da6a 100644 --- a/src/platform/library/include/platform/lib/ll_schedule.h +++ b/src/platform/library/include/platform/lib/ll_schedule.h @@ -19,7 +19,7 @@ int scheduler_init_ll(struct ll_schedule_domain *domain); int schedule_task_init_ll(struct task *task, const struct sof_uuid_entry *uid, uint16_t type, - uint16_t priority, enum task_state (*run)(void *data), + int16_t priority, enum task_state (*run)(void *data), void *data, uint16_t core, uint32_t flags); #endif /* __LIBRARY_INCLUDE_LIB_SCHEDULE_H__ */ diff --git a/src/platform/library/include/platform/lib/memory.h b/src/platform/library/include/platform/lib/memory.h index fafb3df303f7..4fe40783573c 100644 --- a/src/platform/library/include/platform/lib/memory.h +++ b/src/platform/library/include/platform/lib/memory.h @@ -35,13 +35,6 @@ uint8_t *get_library_mailbox(void); #define PLATFORM_HEAP_SYSTEM_SHARED 1 #define PLATFORM_HEAP_RUNTIME_SHARED 1 -#define SHARED_DATA - -static inline void *platform_shared_get(void *ptr, int bytes) -{ - return ptr; -} - void platform_init_memmap(struct sof *sof); static inline void *platform_rfree_prepare(void *ptr) 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/library/lib/memory.c b/src/platform/library/lib/memory.c index 1a03d1ecf503..3f92c62889d7 100644 --- a/src/platform/library/lib/memory.c +++ b/src/platform/library/lib/memory.c @@ -27,22 +27,22 @@ #define HEAP_BUFFER_BASE 0xBE1C0000 /* Heap blocks for system runtime for primary core */ -static SHARED_DATA struct block_hdr sys_rt_0_block64[HEAP_SYS_RT_0_COUNT64]; -static SHARED_DATA struct block_hdr sys_rt_0_block512[HEAP_SYS_RT_0_COUNT512]; -static SHARED_DATA struct block_hdr sys_rt_0_block1024[HEAP_SYS_RT_0_COUNT1024]; +static struct block_hdr sys_rt_0_block64[HEAP_SYS_RT_0_COUNT64]; +static struct block_hdr sys_rt_0_block512[HEAP_SYS_RT_0_COUNT512]; +static struct block_hdr sys_rt_0_block1024[HEAP_SYS_RT_0_COUNT1024]; /* Heap blocks for system runtime for secondary core */ #if CONFIG_CORE_COUNT > 1 -static SHARED_DATA struct block_hdr +static struct block_hdr sys_rt_x_block64[CONFIG_CORE_COUNT - 1][HEAP_SYS_RT_X_COUNT64]; -static SHARED_DATA struct block_hdr +static struct block_hdr sys_rt_x_block512[CONFIG_CORE_COUNT - 1][HEAP_SYS_RT_X_COUNT512]; -static SHARED_DATA struct block_hdr +static struct block_hdr sys_rt_x_block1024[CONFIG_CORE_COUNT - 1][HEAP_SYS_RT_X_COUNT1024]; #endif /* Heap memory for system runtime */ -static SHARED_DATA struct block_map sys_rt_heap_map[CONFIG_CORE_COUNT][3] = { +static struct block_map sys_rt_heap_map[CONFIG_CORE_COUNT][3] = { { BLOCK_DEF(64, HEAP_SYS_RT_0_COUNT64, uncached_block_hdr(sys_rt_0_block64)), BLOCK_DEF(512, HEAP_SYS_RT_0_COUNT512, @@ -76,16 +76,16 @@ static SHARED_DATA struct block_map sys_rt_heap_map[CONFIG_CORE_COUNT][3] = { }; /* Heap blocks for modules */ -static SHARED_DATA struct block_hdr mod_block64[HEAP_COUNT64]; -static SHARED_DATA struct block_hdr mod_block128[HEAP_COUNT128]; -static SHARED_DATA struct block_hdr mod_block256[HEAP_COUNT256]; -static SHARED_DATA struct block_hdr mod_block512[HEAP_COUNT512]; -static SHARED_DATA struct block_hdr mod_block1024[HEAP_COUNT1024]; -static SHARED_DATA struct block_hdr mod_block2048[HEAP_COUNT2048]; -static SHARED_DATA struct block_hdr mod_block4096[HEAP_COUNT4096]; +static struct block_hdr mod_block64[HEAP_COUNT64]; +static struct block_hdr mod_block128[HEAP_COUNT128]; +static struct block_hdr mod_block256[HEAP_COUNT256]; +static struct block_hdr mod_block512[HEAP_COUNT512]; +static struct block_hdr mod_block1024[HEAP_COUNT1024]; +static struct block_hdr mod_block2048[HEAP_COUNT2048]; +static struct block_hdr mod_block4096[HEAP_COUNT4096]; /* Heap memory map for modules */ -static SHARED_DATA struct block_map rt_heap_map[] = { +static struct block_map rt_heap_map[] = { BLOCK_DEF(64, HEAP_COUNT64, uncached_block_hdr(mod_block64)), BLOCK_DEF(128, HEAP_COUNT128, uncached_block_hdr(mod_block128)), BLOCK_DEF(256, HEAP_COUNT256, uncached_block_hdr(mod_block256)), @@ -97,14 +97,14 @@ static SHARED_DATA struct block_map rt_heap_map[] = { #if CONFIG_CORE_COUNT > 1 /* Heap blocks for runtime shared */ -static SHARED_DATA struct block_hdr rt_shared_block64[HEAP_RUNTIME_SHARED_COUNT64]; -static SHARED_DATA struct block_hdr rt_shared_block128[HEAP_RUNTIME_SHARED_COUNT128]; -static SHARED_DATA struct block_hdr rt_shared_block256[HEAP_RUNTIME_SHARED_COUNT256]; -static SHARED_DATA struct block_hdr rt_shared_block512[HEAP_RUNTIME_SHARED_COUNT512]; -static SHARED_DATA struct block_hdr rt_shared_block1024[HEAP_RUNTIME_SHARED_COUNT1024]; +static struct block_hdr rt_shared_block64[HEAP_RUNTIME_SHARED_COUNT64]; +static struct block_hdr rt_shared_block128[HEAP_RUNTIME_SHARED_COUNT128]; +static struct block_hdr rt_shared_block256[HEAP_RUNTIME_SHARED_COUNT256]; +static struct block_hdr rt_shared_block512[HEAP_RUNTIME_SHARED_COUNT512]; +static struct block_hdr rt_shared_block1024[HEAP_RUNTIME_SHARED_COUNT1024]; /* Heap memory map for runtime shared */ -static SHARED_DATA struct block_map rt_shared_heap_map[] = { +static struct block_map rt_shared_heap_map[] = { BLOCK_DEF(64, HEAP_RUNTIME_SHARED_COUNT64, uncached_block_hdr(rt_shared_block64)), BLOCK_DEF(128, HEAP_RUNTIME_SHARED_COUNT128, uncached_block_hdr(rt_shared_block128)), BLOCK_DEF(256, HEAP_RUNTIME_SHARED_COUNT256, uncached_block_hdr(rt_shared_block256)), @@ -114,21 +114,21 @@ static SHARED_DATA struct block_map rt_shared_heap_map[] = { #endif /* Heap blocks for buffers */ -static SHARED_DATA struct block_hdr buf_block[HEAP_BUFFER_COUNT_MAX]; -static SHARED_DATA struct block_hdr lp_buf_block[HEAP_LP_BUFFER_COUNT]; +static struct block_hdr buf_block[HEAP_BUFFER_COUNT_MAX]; +static struct block_hdr lp_buf_block[HEAP_LP_BUFFER_COUNT]; /* Heap memory map for buffers */ -static SHARED_DATA struct block_map buf_heap_map[] = { +static struct block_map buf_heap_map[] = { BLOCK_DEF(HEAP_BUFFER_BLOCK_SIZE, HEAP_BUFFER_COUNT_MAX, uncached_block_hdr(buf_block)), }; -static SHARED_DATA struct block_map lp_buf_heap_map[] = { +static struct block_map lp_buf_heap_map[] = { BLOCK_DEF(HEAP_LP_BUFFER_BLOCK_SIZE, HEAP_LP_BUFFER_COUNT, uncached_block_hdr(lp_buf_block)), }; -static SHARED_DATA struct mm memmap; +static struct mm memmap; void platform_init_memmap(struct sof *sof) { diff --git a/src/platform/library/platform.c b/src/platform/library/platform.c index 756c34c996f4..ced1dcbca32d 100644 --- a/src/platform/library/platform.c +++ b/src/platform/library/platform.c @@ -13,7 +13,7 @@ #include <sof/lib/mailbox.h> #include <sof/lib/dai.h> -static SHARED_DATA struct timer timer = {}; +static struct timer timer = {}; static uint8_t mailbox[MAILBOX_DSPBOX_SIZE + MAILBOX_HOSTBOX_SIZE + diff --git a/src/platform/library/schedule/edf_schedule.c b/src/platform/library/schedule/edf_schedule.c index 0c127c934e21..67c3a669c2f8 100644 --- a/src/platform/library/schedule/edf_schedule.c +++ b/src/platform/library/schedule/edf_schedule.c @@ -49,10 +49,12 @@ static int schedule_edf_task(void *data, struct task *task, uint64_t start, return 0; } +#if CONFIG_SOF_BOOT_TEST_STANDALONE || CONFIG_LIBRARY static void edf_scheduler_free(void *data, uint32_t flags) { free(data); } +#endif static int schedule_edf_task_cancel(void *data, struct task *task) { @@ -83,7 +85,9 @@ static struct scheduler_ops schedule_edf_ops = { .reschedule_task = NULL, .schedule_task_cancel = schedule_edf_task_cancel, .schedule_task_free = schedule_edf_task_free, +#if CONFIG_SOF_BOOT_TEST_STANDALONE || CONFIG_LIBRARY .scheduler_free = edf_scheduler_free, +#endif }; int schedule_task_init_edf(struct task *task, const struct sof_uuid_entry *uid, diff --git a/src/platform/library/schedule/ll_schedule.c b/src/platform/library/schedule/ll_schedule.c index 4cfd0a9d196c..f865f4db18e9 100644 --- a/src/platform/library/schedule/ll_schedule.c +++ b/src/platform/library/schedule/ll_schedule.c @@ -31,6 +31,7 @@ void schedule_ll_run_tasks(void) { struct list_item *tlist, *tlist_; struct task *task; + enum task_state state; /* list empty then return */ if (list_is_empty(&sched_list)) @@ -44,12 +45,14 @@ void schedule_ll_run_tasks(void) if (task->state == SOF_TASK_STATE_QUEUED) { task->state = SOF_TASK_STATE_RUNNING; - task->ops.run(task->data); + state = task_run(task); - /* only re-queue if not cancelled */ - if (task->state == SOF_TASK_STATE_RUNNING) + if (state == SOF_TASK_STATE_COMPLETED) { + task->state = SOF_TASK_STATE_COMPLETED; + list_item_del(&task->list); + } else if (task->state == SOF_TASK_STATE_RUNNING) { task->state = SOF_TASK_STATE_QUEUED; - + } } } } @@ -58,6 +61,10 @@ void schedule_ll_run_tasks(void) static int schedule_ll_task(void *data, struct task *task, uint64_t start, uint64_t period) { + if (task->state == SOF_TASK_STATE_QUEUED || + task->state == SOF_TASK_STATE_RUNNING) + return 0; + /* add task to list */ list_item_prepend(&task->list, &sched_list); task->state = SOF_TASK_STATE_QUEUED; @@ -66,17 +73,22 @@ static int schedule_ll_task(void *data, struct task *task, uint64_t start, return 0; } +#if CONFIG_SOF_BOOT_TEST_STANDALONE || CONFIG_LIBRARY static void ll_scheduler_free(void *data, uint32_t flags) { free(data); } +#endif /* TODO: scheduler free and cancel APIs can merge as part of Zephyr */ static int schedule_ll_task_cancel(void *data, struct task *task) { + if (task->state == SOF_TASK_STATE_QUEUED || + task->state == SOF_TASK_STATE_RUNNING) + list_item_del(&task->list); + /* delete task */ task->state = SOF_TASK_STATE_CANCEL; - list_item_del(&task->list); return 0; } @@ -84,8 +96,11 @@ static int schedule_ll_task_cancel(void *data, struct task *task) /* TODO: scheduler free and cancel APIs can merge as part of Zephyr */ static int schedule_ll_task_free(void *data, struct task *task) { + if (task->state == SOF_TASK_STATE_QUEUED || + task->state == SOF_TASK_STATE_RUNNING) + list_item_del(&task->list); + task->state = SOF_TASK_STATE_FREE; - list_item_del(&task->list); return 0; } @@ -96,12 +111,14 @@ static struct scheduler_ops schedule_ll_ops = { .reschedule_task = NULL, .schedule_task_cancel = schedule_ll_task_cancel, .schedule_task_free = schedule_ll_task_free, +#if CONFIG_SOF_BOOT_TEST_STANDALONE || CONFIG_LIBRARY .scheduler_free = ll_scheduler_free, +#endif }; int schedule_task_init_ll(struct task *task, const struct sof_uuid_entry *uid, uint16_t type, - uint16_t priority, enum task_state (*run)(void *data), + int16_t priority, enum task_state (*run)(void *data), void *data, uint16_t core, uint32_t flags) { return schedule_task_init(task, uid, SOF_SCHEDULE_LL_TIMER, 0, run, diff --git a/src/platform/library/schedule/schedule.c b/src/platform/library/schedule/schedule.c index b1dfcbb19445..ea4cc33608ac 100644 --- a/src/platform/library/schedule/schedule.c +++ b/src/platform/library/schedule/schedule.c @@ -22,12 +22,32 @@ struct schedulers **arch_schedulers_get(void) int schedule_task_init(struct task *task, const struct sof_uuid_entry *uid, uint16_t type, - uint16_t priority, enum task_state (*run)(void *data), + int16_t priority, enum task_state (*run)(void *data), void *data, uint16_t core, uint32_t flags) { + struct schedulers *schedulers = *arch_schedulers_get(); + struct schedule_data *sch = NULL; + struct list_item *slist; + if (type >= SOF_SCHEDULE_COUNT) return -EINVAL; + if (!schedulers) + return -ENODEV; + + task->sch = NULL; + list_for_item(slist, &schedulers->list) { + sch = container_of(slist, struct schedule_data, list); + if (type == sch->type) { + task->sch = sch; + break; + } + } + + if (!task->sch) { + return -ENODEV; + } + task->uid = uid; task->type = type; task->priority = priority; diff --git a/src/platform/mt8186/include/platform/lib/memory.h b/src/platform/mt8186/include/platform/lib/memory.h index 65148c9c348d..68c95bb53db0 100644 --- a/src/platform/mt8186/include/platform/lib/memory.h +++ b/src/platform/mt8186/include/platform/lib/memory.h @@ -184,19 +184,8 @@ struct sof; -/** - * \brief Data shared between different cores. - * Does nothing, since mt8186 doesn't support SMP. - */ -#define SHARED_DATA - void platform_init_memmap(struct sof *sof); -static inline void *platform_shared_get(void *ptr, int bytes) -{ - return ptr; -} - #define uncache_to_cache(address) address #define cache_to_uncache(address) address #define cache_to_uncache_init(address) address @@ -205,9 +194,8 @@ static inline void *platform_shared_get(void *ptr, int bytes) /** * \brief Function for keeping shared data synchronized. * It's used after usage of data shared by different cores. - * Such data is either statically marked with SHARED_DATA - * or dynamically allocated with SOF_MEM_FLAG_SHARED flag. - * Does nothing, since mt8186 doesn't support SMP. + * Such data is dynamically allocated with SOF_MEM_FLAG_SHARED + * flag. Does nothing, since mt8186 doesn't support SMP. */ static inline void *platform_rfree_prepare(void *ptr) diff --git a/src/platform/mt8186/lib/clk.c b/src/platform/mt8186/lib/clk.c index 555ab83c2a4f..b1b88ff9fe7b 100644 --- a/src/platform/mt8186/lib/clk.c +++ b/src/platform/mt8186/lib/clk.c @@ -32,11 +32,11 @@ const struct freq_table platform_cpu_freq[] = { STATIC_ASSERT(ARRAY_SIZE(platform_cpu_freq) == NUM_CPU_FREQ, invalid_number_of_cpu_frequencies); -static SHARED_DATA struct clock_info platform_clocks_info[NUM_CLOCKS]; +static 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); @@ -135,7 +135,7 @@ void platform_clock_init(struct sof *sof) { int i; - sof->clocks = platform_shared_get(platform_clocks_info, sizeof(platform_clocks_info)); + sof->clocks = platform_clocks_info; for (i = 0; i < CONFIG_CORE_COUNT; i++) { sof->clocks[i] = (struct clock_info){ diff --git a/src/platform/mt8186/lib/dai.c b/src/platform/mt8186/lib/dai.c index 4d302d48fbdc..a45056846a2e 100644 --- a/src/platform/mt8186/lib/dai.c +++ b/src/platform/mt8186/lib/dai.c @@ -23,7 +23,7 @@ static int afe_dai_handshake[MT8186_DAI_NUM] = { AFE_HANDSHAKE(MT8186_AFE_IO_I2S0, MT8186_IRQ_12, MT8186_MEMIF_UL2), }; -static SHARED_DATA struct dai afe_dai[MT8186_DAI_NUM]; +static struct dai afe_dai[MT8186_DAI_NUM]; const struct dai_type_info dti[] = { { diff --git a/src/platform/mt8186/lib/dma.c b/src/platform/mt8186/lib/dma.c index db00628ae547..a8e3308367fc 100644 --- a/src/platform/mt8186/lib/dma.c +++ b/src/platform/mt8186/lib/dma.c @@ -19,7 +19,7 @@ extern const struct dma_ops dummy_dma_ops; -static SHARED_DATA struct dma dma[PLATFORM_NUM_DMACS] = { +static struct dma dma[PLATFORM_NUM_DMACS] = { { .plat_data = { .id = DMA_ID_HOST, diff --git a/src/platform/mt8186/lib/memory.c b/src/platform/mt8186/lib/memory.c index 2a01e0333bab..beae268b7f6f 100644 --- a/src/platform/mt8186/lib/memory.c +++ b/src/platform/mt8186/lib/memory.c @@ -13,30 +13,30 @@ #include <ipc/topology.h> /* Heap blocks for system runtime */ -static SHARED_DATA struct block_hdr sys_rt_block64[HEAP_SYS_RT_COUNT64]; -static SHARED_DATA struct block_hdr sys_rt_block512[HEAP_SYS_RT_COUNT512]; -static SHARED_DATA struct block_hdr sys_rt_block1024[HEAP_SYS_RT_COUNT1024]; +static struct block_hdr sys_rt_block64[HEAP_SYS_RT_COUNT64]; +static struct block_hdr sys_rt_block512[HEAP_SYS_RT_COUNT512]; +static struct block_hdr sys_rt_block1024[HEAP_SYS_RT_COUNT1024]; /* Heap memory for system runtime */ -static SHARED_DATA struct block_map sys_rt_heap_map[] = { +static struct block_map sys_rt_heap_map[] = { BLOCK_DEF(64, HEAP_SYS_RT_COUNT64, sys_rt_block64), BLOCK_DEF(512, HEAP_SYS_RT_COUNT512, sys_rt_block512), BLOCK_DEF(1024, HEAP_SYS_RT_COUNT1024, sys_rt_block1024), }; /* Heap blocks for modules */ -static SHARED_DATA struct block_hdr mod_block16[HEAP_RT_COUNT16]; -static SHARED_DATA struct block_hdr mod_block32[HEAP_RT_COUNT32]; -static SHARED_DATA struct block_hdr mod_block64[HEAP_RT_COUNT64]; -static SHARED_DATA struct block_hdr mod_block128[HEAP_RT_COUNT128]; -static SHARED_DATA struct block_hdr mod_block256[HEAP_RT_COUNT256]; -static SHARED_DATA struct block_hdr mod_block512[HEAP_RT_COUNT512]; -static SHARED_DATA struct block_hdr mod_block1024[HEAP_RT_COUNT1024]; -static SHARED_DATA struct block_hdr mod_block2048[HEAP_RT_COUNT2048]; -static SHARED_DATA struct block_hdr mod_block4096[HEAP_RT_COUNT4096]; +static struct block_hdr mod_block16[HEAP_RT_COUNT16]; +static struct block_hdr mod_block32[HEAP_RT_COUNT32]; +static struct block_hdr mod_block64[HEAP_RT_COUNT64]; +static struct block_hdr mod_block128[HEAP_RT_COUNT128]; +static struct block_hdr mod_block256[HEAP_RT_COUNT256]; +static struct block_hdr mod_block512[HEAP_RT_COUNT512]; +static struct block_hdr mod_block1024[HEAP_RT_COUNT1024]; +static struct block_hdr mod_block2048[HEAP_RT_COUNT2048]; +static struct block_hdr mod_block4096[HEAP_RT_COUNT4096]; /* Heap memory map for modules */ -static SHARED_DATA struct block_map rt_heap_map[] = { +static struct block_map rt_heap_map[] = { BLOCK_DEF(16, HEAP_RT_COUNT16, mod_block16), BLOCK_DEF(32, HEAP_RT_COUNT32, mod_block32), BLOCK_DEF(64, HEAP_RT_COUNT64, mod_block64), @@ -49,14 +49,14 @@ static SHARED_DATA struct block_map rt_heap_map[] = { }; /* Heap blocks for buffers */ -static SHARED_DATA struct block_hdr buf_block[HEAP_BUFFER_COUNT]; +static struct block_hdr buf_block[HEAP_BUFFER_COUNT]; /* Heap memory map for buffers */ -static SHARED_DATA struct block_map buf_heap_map[] = { +static struct block_map buf_heap_map[] = { BLOCK_DEF(HEAP_BUFFER_BLOCK_SIZE, HEAP_BUFFER_COUNT, buf_block), }; -static SHARED_DATA struct mm memmap = { +static struct mm memmap = { .system[0] = { .heap = HEAP_SYSTEM_BASE, .size = HEAP_SYSTEM_SIZE, @@ -98,5 +98,5 @@ static SHARED_DATA struct mm memmap = { void platform_init_memmap(struct sof *sof) { /* memmap has been initialized statically as a part of .data */ - sof->memory_map = platform_shared_get(&memmap, sizeof(memmap)); + sof->memory_map = &memmap; } diff --git a/src/platform/mt8186/platform.c b/src/platform/mt8186/platform.c index f171e6cb7d0b..7d708750f8f0 100644 --- a/src/platform/mt8186/platform.c +++ b/src/platform/mt8186/platform.c @@ -121,7 +121,7 @@ const struct ext_man_windows xsram_window } }; -static SHARED_DATA struct timer timer_shared = { +static struct timer timer_shared = { .id = OSTIMER0, .irq = MTK_DSP_IRQ_OSTIMER32, }; @@ -163,7 +163,7 @@ int platform_init(struct sof *sof) { int ret; - sof->platform_timer = platform_shared_get(&timer_shared, sizeof(timer_shared)); + sof->platform_timer = &timer_shared; sof->cpu_timers = sof->platform_timer; platform_interrupt_init(); diff --git a/src/platform/mt8188/include/platform/lib/memory.h b/src/platform/mt8188/include/platform/lib/memory.h index 65148c9c348d..17343e78ef76 100644 --- a/src/platform/mt8188/include/platform/lib/memory.h +++ b/src/platform/mt8188/include/platform/lib/memory.h @@ -39,7 +39,7 @@ #define EXT_MANIFEST_ELF_SIZE (SRAM_BASE + SRAM_SIZE - EXT_MANIFEST_ELF_BASE) /* - * The Memory Layout on MT8186 are organised like this :- + * The Memory Layout on MT8188 are organised like this :- * * +--------------------------------------------------------------------------+ * | Offset | Region | Size | @@ -184,19 +184,8 @@ struct sof; -/** - * \brief Data shared between different cores. - * Does nothing, since mt8186 doesn't support SMP. - */ -#define SHARED_DATA - void platform_init_memmap(struct sof *sof); -static inline void *platform_shared_get(void *ptr, int bytes) -{ - return ptr; -} - #define uncache_to_cache(address) address #define cache_to_uncache(address) address #define cache_to_uncache_init(address) address @@ -205,9 +194,8 @@ static inline void *platform_shared_get(void *ptr, int bytes) /** * \brief Function for keeping shared data synchronized. * It's used after usage of data shared by different cores. - * Such data is either statically marked with SHARED_DATA - * or dynamically allocated with SOF_MEM_FLAG_SHARED flag. - * Does nothing, since mt8186 doesn't support SMP. + * Such data is dynamically allocated with SOF_MEM_FLAG_SHARED + * flag. Does nothing, since mt8188 doesn't support SMP. */ static inline void *platform_rfree_prepare(void *ptr) diff --git a/src/platform/mt8188/lib/clk.c b/src/platform/mt8188/lib/clk.c index d11b9fe2317b..7a179128c1fc 100644 --- a/src/platform/mt8188/lib/clk.c +++ b/src/platform/mt8188/lib/clk.c @@ -32,11 +32,11 @@ const struct freq_table platform_cpu_freq[] = { STATIC_ASSERT(ARRAY_SIZE(platform_cpu_freq) == NUM_CPU_FREQ, invalid_number_of_cpu_frequencies); -static SHARED_DATA struct clock_info platform_clocks_info[NUM_CLOCKS]; +static 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); @@ -123,7 +123,7 @@ void platform_clock_init(struct sof *sof) { int i; - sof->clocks = platform_shared_get(platform_clocks_info, sizeof(platform_clocks_info)); + sof->clocks = platform_clocks_info; for (i = 0; i < CONFIG_CORE_COUNT; i++) { sof->clocks[i] = (struct clock_info){ diff --git a/src/platform/mt8188/lib/dai.c b/src/platform/mt8188/lib/dai.c index d721bd4c1d39..53b30146a2fd 100644 --- a/src/platform/mt8188/lib/dai.c +++ b/src/platform/mt8188/lib/dai.c @@ -23,7 +23,7 @@ static int afe_dai_handshake[MT8188_DAI_NUM] = { AFE_HANDSHAKE(MT8188_AFE_IO_ETDM2_IN, MT8188_IRQ_22, MT8188_MEMIF_UL5), }; -static SHARED_DATA struct dai afe_dai[MT8188_DAI_NUM]; +static struct dai afe_dai[MT8188_DAI_NUM]; const struct dai_type_info dti[] = { { diff --git a/src/platform/mt8188/lib/dma.c b/src/platform/mt8188/lib/dma.c index aa377133b730..51bcb18a16ef 100644 --- a/src/platform/mt8188/lib/dma.c +++ b/src/platform/mt8188/lib/dma.c @@ -19,7 +19,7 @@ extern const struct dma_ops dummy_dma_ops; -static SHARED_DATA struct dma dma[PLATFORM_NUM_DMACS] = { +static struct dma dma[PLATFORM_NUM_DMACS] = { { .plat_data = { .id = DMA_ID_HOST, diff --git a/src/platform/mt8188/lib/memory.c b/src/platform/mt8188/lib/memory.c index 73d3af0f4d43..6475b01e48ad 100644 --- a/src/platform/mt8188/lib/memory.c +++ b/src/platform/mt8188/lib/memory.c @@ -13,30 +13,30 @@ #include <rtos/sof.h> /* Heap blocks for system runtime */ -static SHARED_DATA struct block_hdr sys_rt_block64[HEAP_SYS_RT_COUNT64]; -static SHARED_DATA struct block_hdr sys_rt_block512[HEAP_SYS_RT_COUNT512]; -static SHARED_DATA struct block_hdr sys_rt_block1024[HEAP_SYS_RT_COUNT1024]; +static struct block_hdr sys_rt_block64[HEAP_SYS_RT_COUNT64]; +static struct block_hdr sys_rt_block512[HEAP_SYS_RT_COUNT512]; +static struct block_hdr sys_rt_block1024[HEAP_SYS_RT_COUNT1024]; /* Heap memory for system runtime */ -static SHARED_DATA struct block_map sys_rt_heap_map[] = { +static struct block_map sys_rt_heap_map[] = { BLOCK_DEF(64, HEAP_SYS_RT_COUNT64, sys_rt_block64), BLOCK_DEF(512, HEAP_SYS_RT_COUNT512, sys_rt_block512), BLOCK_DEF(1024, HEAP_SYS_RT_COUNT1024, sys_rt_block1024), }; /* Heap blocks for modules */ -static SHARED_DATA struct block_hdr mod_block16[HEAP_RT_COUNT16]; -static SHARED_DATA struct block_hdr mod_block32[HEAP_RT_COUNT32]; -static SHARED_DATA struct block_hdr mod_block64[HEAP_RT_COUNT64]; -static SHARED_DATA struct block_hdr mod_block128[HEAP_RT_COUNT128]; -static SHARED_DATA struct block_hdr mod_block256[HEAP_RT_COUNT256]; -static SHARED_DATA struct block_hdr mod_block512[HEAP_RT_COUNT512]; -static SHARED_DATA struct block_hdr mod_block1024[HEAP_RT_COUNT1024]; -static SHARED_DATA struct block_hdr mod_block2048[HEAP_RT_COUNT2048]; -static SHARED_DATA struct block_hdr mod_block4096[HEAP_RT_COUNT4096]; +static struct block_hdr mod_block16[HEAP_RT_COUNT16]; +static struct block_hdr mod_block32[HEAP_RT_COUNT32]; +static struct block_hdr mod_block64[HEAP_RT_COUNT64]; +static struct block_hdr mod_block128[HEAP_RT_COUNT128]; +static struct block_hdr mod_block256[HEAP_RT_COUNT256]; +static struct block_hdr mod_block512[HEAP_RT_COUNT512]; +static struct block_hdr mod_block1024[HEAP_RT_COUNT1024]; +static struct block_hdr mod_block2048[HEAP_RT_COUNT2048]; +static struct block_hdr mod_block4096[HEAP_RT_COUNT4096]; /* Heap memory map for modules */ -static SHARED_DATA struct block_map rt_heap_map[] = { +static struct block_map rt_heap_map[] = { BLOCK_DEF(16, HEAP_RT_COUNT16, mod_block16), BLOCK_DEF(32, HEAP_RT_COUNT32, mod_block32), BLOCK_DEF(64, HEAP_RT_COUNT64, mod_block64), @@ -49,14 +49,14 @@ static SHARED_DATA struct block_map rt_heap_map[] = { }; /* Heap blocks for buffers */ -static SHARED_DATA struct block_hdr buf_block[HEAP_BUFFER_COUNT]; +static struct block_hdr buf_block[HEAP_BUFFER_COUNT]; /* Heap memory map for buffers */ -static SHARED_DATA struct block_map buf_heap_map[] = { +static struct block_map buf_heap_map[] = { BLOCK_DEF(HEAP_BUFFER_BLOCK_SIZE, HEAP_BUFFER_COUNT, buf_block), }; -static SHARED_DATA struct mm memmap = { +static struct mm memmap = { .system[0] = { .heap = HEAP_SYSTEM_BASE, .size = HEAP_SYSTEM_SIZE, @@ -98,5 +98,5 @@ static SHARED_DATA struct mm memmap = { void platform_init_memmap(struct sof *sof) { /* memmap has been initialized statically as a part of .data */ - sof->memory_map = platform_shared_get(&memmap, sizeof(memmap)); + sof->memory_map = &memmap; } diff --git a/src/platform/mt8188/platform.c b/src/platform/mt8188/platform.c index fc2e83dbed25..babedcba3ac6 100644 --- a/src/platform/mt8188/platform.c +++ b/src/platform/mt8188/platform.c @@ -121,7 +121,7 @@ const struct ext_man_windows xsram_window } }; -static SHARED_DATA struct timer timer_shared = { +static struct timer timer_shared = { .id = OSTIMER0, .irq = MTK_DSP_IRQ_OSTIMER32, }; @@ -161,7 +161,7 @@ int platform_init(struct sof *sof) { int ret; - sof->platform_timer = platform_shared_get(&timer_shared, sizeof(timer_shared)); + sof->platform_timer = &timer_shared; sof->cpu_timers = sof->platform_timer; platform_interrupt_init(); diff --git a/src/platform/mt8195/include/platform/lib/memory.h b/src/platform/mt8195/include/platform/lib/memory.h index 42ee7f7990ab..75b8b3348e04 100644 --- a/src/platform/mt8195/include/platform/lib/memory.h +++ b/src/platform/mt8195/include/platform/lib/memory.h @@ -175,19 +175,8 @@ struct sof; -/** - * \brief Data shared between different cores. - * Does nothing, since mt8195 doesn't support SMP. - */ -#define SHARED_DATA - void platform_init_memmap(struct sof *sof); -static inline void *platform_shared_get(void *ptr, int bytes) -{ - return ptr; -} - #define uncache_to_cache(address) address #define cache_to_uncache(address) address #define cache_to_uncache_init(address) address @@ -196,9 +185,8 @@ static inline void *platform_shared_get(void *ptr, int bytes) /** * \brief Function for keeping shared data synchronized. * It's used after usage of data shared by different cores. - * Such data is either statically marked with SHARED_DATA - * or dynamically allocated with SOF_MEM_FLAG_SHARED flag. - * Does nothing, since mt8195 doesn't support SMP. + * Such data is dynamically allocated with SOF_MEM_FLAG_SHARED + * flag. Does nothing, since mt8195 doesn't support SMP. */ static inline void *platform_rfree_prepare(void *ptr) diff --git a/src/platform/mt8195/lib/clk.c b/src/platform/mt8195/lib/clk.c index 3f69c5e2d8f1..6fc89418650e 100644 --- a/src/platform/mt8195/lib/clk.c +++ b/src/platform/mt8195/lib/clk.c @@ -41,7 +41,7 @@ const uint32_t cpu_freq_enc[] = { STATIC_ASSERT(ARRAY_SIZE(platform_cpu_freq) == NUM_CPU_FREQ, invalid_number_of_cpu_frequencies); -static SHARED_DATA struct clock_info platform_clocks_info[NUM_CLOCKS]; +static struct clock_info platform_clocks_info[NUM_CLOCKS]; static inline void clk_setl(uint32_t addr, uint32_t val) { @@ -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); @@ -177,7 +177,7 @@ void platform_clock_init(struct sof *sof) { int i; - sof->clocks = platform_shared_get(platform_clocks_info, sizeof(platform_clocks_info)); + sof->clocks = platform_clocks_info; for (i = 0; i < CONFIG_CORE_COUNT; i++) { sof->clocks[i] = (struct clock_info){ diff --git a/src/platform/mt8195/lib/dai.c b/src/platform/mt8195/lib/dai.c index c385331489a6..e2e291e19f6e 100644 --- a/src/platform/mt8195/lib/dai.c +++ b/src/platform/mt8195/lib/dai.c @@ -23,7 +23,7 @@ static int afe_dai_handshake[MT8195_DAI_NUM] = { AFE_HANDSHAKE(MT8195_AFE_IO_ETDM2_IN, MT8195_IRQ_22, MT8195_MEMIF_UL5), }; -static SHARED_DATA struct dai afe_dai[MT8195_DAI_NUM]; +static struct dai afe_dai[MT8195_DAI_NUM]; const struct dai_type_info dti[] = { { diff --git a/src/platform/mt8195/lib/dma.c b/src/platform/mt8195/lib/dma.c index 7a9719e8a30c..89ee43af7eac 100644 --- a/src/platform/mt8195/lib/dma.c +++ b/src/platform/mt8195/lib/dma.c @@ -18,7 +18,7 @@ extern struct dma_ops dummy_dma_ops; -static SHARED_DATA struct dma dma[PLATFORM_NUM_DMACS] = { +static struct dma dma[PLATFORM_NUM_DMACS] = { { .plat_data = { .id = DMA_ID_HOST, diff --git a/src/platform/mt8195/lib/memory.c b/src/platform/mt8195/lib/memory.c index b2d545ff6f1d..0fbfb0f5b3d1 100644 --- a/src/platform/mt8195/lib/memory.c +++ b/src/platform/mt8195/lib/memory.c @@ -12,30 +12,30 @@ #include <ipc/topology.h> /* Heap blocks for system runtime */ -static SHARED_DATA struct block_hdr sys_rt_block64[HEAP_SYS_RT_COUNT64]; -static SHARED_DATA struct block_hdr sys_rt_block512[HEAP_SYS_RT_COUNT512]; -static SHARED_DATA struct block_hdr sys_rt_block1024[HEAP_SYS_RT_COUNT1024]; +static struct block_hdr sys_rt_block64[HEAP_SYS_RT_COUNT64]; +static struct block_hdr sys_rt_block512[HEAP_SYS_RT_COUNT512]; +static struct block_hdr sys_rt_block1024[HEAP_SYS_RT_COUNT1024]; /* Heap memory for system runtime */ -static SHARED_DATA struct block_map sys_rt_heap_map[] = { +static struct block_map sys_rt_heap_map[] = { BLOCK_DEF(64, HEAP_SYS_RT_COUNT64, sys_rt_block64), BLOCK_DEF(512, HEAP_SYS_RT_COUNT512, sys_rt_block512), BLOCK_DEF(1024, HEAP_SYS_RT_COUNT1024, sys_rt_block1024), }; /* Heap blocks for modules */ -static SHARED_DATA struct block_hdr mod_block16[HEAP_RT_COUNT16]; -static SHARED_DATA struct block_hdr mod_block32[HEAP_RT_COUNT32]; -static SHARED_DATA struct block_hdr mod_block64[HEAP_RT_COUNT64]; -static SHARED_DATA struct block_hdr mod_block128[HEAP_RT_COUNT128]; -static SHARED_DATA struct block_hdr mod_block256[HEAP_RT_COUNT256]; -static SHARED_DATA struct block_hdr mod_block512[HEAP_RT_COUNT512]; -static SHARED_DATA struct block_hdr mod_block1024[HEAP_RT_COUNT1024]; -static SHARED_DATA struct block_hdr mod_block2048[HEAP_RT_COUNT2048]; -static SHARED_DATA struct block_hdr mod_block4096[HEAP_RT_COUNT4096]; +static struct block_hdr mod_block16[HEAP_RT_COUNT16]; +static struct block_hdr mod_block32[HEAP_RT_COUNT32]; +static struct block_hdr mod_block64[HEAP_RT_COUNT64]; +static struct block_hdr mod_block128[HEAP_RT_COUNT128]; +static struct block_hdr mod_block256[HEAP_RT_COUNT256]; +static struct block_hdr mod_block512[HEAP_RT_COUNT512]; +static struct block_hdr mod_block1024[HEAP_RT_COUNT1024]; +static struct block_hdr mod_block2048[HEAP_RT_COUNT2048]; +static struct block_hdr mod_block4096[HEAP_RT_COUNT4096]; /* Heap memory map for modules */ -static SHARED_DATA struct block_map rt_heap_map[] = { +static struct block_map rt_heap_map[] = { BLOCK_DEF(16, HEAP_RT_COUNT16, mod_block16), BLOCK_DEF(32, HEAP_RT_COUNT32, mod_block32), BLOCK_DEF(64, HEAP_RT_COUNT64, mod_block64), @@ -48,14 +48,14 @@ static SHARED_DATA struct block_map rt_heap_map[] = { }; /* Heap blocks for buffers */ -static SHARED_DATA struct block_hdr buf_block[HEAP_BUFFER_COUNT]; +static struct block_hdr buf_block[HEAP_BUFFER_COUNT]; /* Heap memory map for buffers */ -static SHARED_DATA struct block_map buf_heap_map[] = { +static struct block_map buf_heap_map[] = { BLOCK_DEF(HEAP_BUFFER_BLOCK_SIZE, HEAP_BUFFER_COUNT, buf_block), }; -static SHARED_DATA struct mm memmap = { +static struct mm memmap = { .system[0] = { .heap = HEAP_SYSTEM_BASE, .size = HEAP_SYSTEM_SIZE, @@ -97,5 +97,5 @@ static SHARED_DATA struct mm memmap = { void platform_init_memmap(struct sof *sof) { /* memmap has been initialized statically as a part of .data */ - sof->memory_map = platform_shared_get(&memmap, sizeof(memmap)); + sof->memory_map = &memmap; } diff --git a/src/platform/mt8195/platform.c b/src/platform/mt8195/platform.c index 7e713290437d..c1e59049b57a 100644 --- a/src/platform/mt8195/platform.c +++ b/src/platform/mt8195/platform.c @@ -121,7 +121,7 @@ const struct ext_man_windows xsram_window } }; -static SHARED_DATA struct timer timer_shared = { +static struct timer timer_shared = { .id = OSTIMER0, .irq = LX_ADSP_TIMTER_IRQ0_B, }; @@ -182,7 +182,7 @@ int platform_init(struct sof *sof) { int ret; - sof->platform_timer = platform_shared_get(&timer_shared, sizeof(timer_shared)); + sof->platform_timer = &timer_shared; sof->cpu_timers = sof->platform_timer; platform_interrupt_init(); diff --git a/src/platform/mt8196/include/platform/lib/memory.h b/src/platform/mt8196/include/platform/lib/memory.h index f6d8bd521208..41536d2ace4b 100644 --- a/src/platform/mt8196/include/platform/lib/memory.h +++ b/src/platform/mt8196/include/platform/lib/memory.h @@ -38,7 +38,7 @@ #define EXT_MANIFEST_ELF_SIZE (SRAM_BASE + SRAM_SIZE - EXT_MANIFEST_ELF_BASE) /* - * The Memory Layout on MT8189 are organised like this :- + * The Memory Layout on MT8196 are organised like this :- * * +--------------------------------------------------------------------------+ * | Offset | Region | Size | @@ -183,19 +183,8 @@ struct sof; -/** - * \brief Data shared between different cores. - * Does nothing, since mt8186 doesn't support SMP. - */ -#define SHARED_DATA - void platform_init_memmap(struct sof *sof); -static inline void *platform_shared_get(void *ptr, int bytes) -{ - return ptr; -} - #define uncache_to_cache(address) address #define cache_to_uncache(address) address #define cache_to_uncache_init(address) address @@ -204,9 +193,8 @@ static inline void *platform_shared_get(void *ptr, int bytes) /** * \brief Function for keeping shared data synchronized. * It's used after usage of data shared by different cores. - * Such data is either statically marked with SHARED_DATA - * or dynamically allocated with SOF_MEM_FLAG_SHARED flag. - * Does nothing, since mt8186 doesn't support SMP. + * Such data is dynamically allocated with SOF_MEM_FLAG_SHARED + * flag. Does nothing, since mt8196 doesn't support SMP. */ static inline void *platform_rfree_prepare(void *ptr) diff --git a/src/platform/mt8196/lib/clk.c b/src/platform/mt8196/lib/clk.c index 69af725dec73..a860a04b0133 100644 --- a/src/platform/mt8196/lib/clk.c +++ b/src/platform/mt8196/lib/clk.c @@ -30,14 +30,14 @@ const struct freq_table platform_cpu_freq[] = { STATIC_ASSERT(ARRAY_SIZE(platform_cpu_freq) == NUM_CPU_FREQ, invalid_number_of_cpu_frequencies); -static SHARED_DATA struct clock_info platform_clocks_info[NUM_CLOCKS]; +static struct clock_info platform_clocks_info[NUM_CLOCKS]; void platform_clock_init(struct sof *sof) { int i; tr_dbg(&clkdrv_tr, "clock init\n"); - sof->clocks = platform_shared_get(platform_clocks_info, sizeof(platform_clocks_info)); + sof->clocks = platform_clocks_info; /* When the system is in an active state, the DSP clock operates at 800MHz (0.75V). * In a low power scenario, the DSP enters WFI state, and the clock reduces to 26MHz. diff --git a/src/platform/mt8196/lib/dai.c b/src/platform/mt8196/lib/dai.c index 6f5300e6aa48..63a08e5eb095 100755 --- a/src/platform/mt8196/lib/dai.c +++ b/src/platform/mt8196/lib/dai.c @@ -24,7 +24,7 @@ static int afe_dai_handshake[MT8196_DAI_NUM] = { AFE_HANDSHAKE(MT8196_DAI_AP_DMIC_CH34, MT8196_IRQ_15, MT8196_MEMIF_UL2), }; -static SHARED_DATA struct dai afe_dai[MT8196_DAI_NUM]; +static struct dai afe_dai[MT8196_DAI_NUM]; const struct dai_type_info dti[] = { { diff --git a/src/platform/mt8196/lib/dma.c b/src/platform/mt8196/lib/dma.c index 0cc4e1043a26..fef728fc87ea 100644 --- a/src/platform/mt8196/lib/dma.c +++ b/src/platform/mt8196/lib/dma.c @@ -18,7 +18,7 @@ extern const struct dma_ops dummy_dma_ops; -static SHARED_DATA struct dma dma[PLATFORM_NUM_DMACS] = { +static struct dma dma[PLATFORM_NUM_DMACS] = { { .plat_data = { .id = DMA_ID_HOST, diff --git a/src/platform/mt8196/lib/memory.c b/src/platform/mt8196/lib/memory.c index dca28790d741..c46956ed4946 100644 --- a/src/platform/mt8196/lib/memory.c +++ b/src/platform/mt8196/lib/memory.c @@ -12,30 +12,30 @@ #include <rtos/sof.h> /* Heap blocks for system runtime */ -static SHARED_DATA struct block_hdr sys_rt_block64[HEAP_SYS_RT_COUNT64]; -static SHARED_DATA struct block_hdr sys_rt_block512[HEAP_SYS_RT_COUNT512]; -static SHARED_DATA struct block_hdr sys_rt_block1024[HEAP_SYS_RT_COUNT1024]; +static struct block_hdr sys_rt_block64[HEAP_SYS_RT_COUNT64]; +static struct block_hdr sys_rt_block512[HEAP_SYS_RT_COUNT512]; +static struct block_hdr sys_rt_block1024[HEAP_SYS_RT_COUNT1024]; /* Heap memory for system runtime */ -static SHARED_DATA struct block_map sys_rt_heap_map[] = { +static struct block_map sys_rt_heap_map[] = { BLOCK_DEF(64, HEAP_SYS_RT_COUNT64, sys_rt_block64), BLOCK_DEF(512, HEAP_SYS_RT_COUNT512, sys_rt_block512), BLOCK_DEF(1024, HEAP_SYS_RT_COUNT1024, sys_rt_block1024), }; /* Heap blocks for modules */ -static SHARED_DATA struct block_hdr mod_block16[HEAP_RT_COUNT16]; -static SHARED_DATA struct block_hdr mod_block32[HEAP_RT_COUNT32]; -static SHARED_DATA struct block_hdr mod_block64[HEAP_RT_COUNT64]; -static SHARED_DATA struct block_hdr mod_block128[HEAP_RT_COUNT128]; -static SHARED_DATA struct block_hdr mod_block256[HEAP_RT_COUNT256]; -static SHARED_DATA struct block_hdr mod_block512[HEAP_RT_COUNT512]; -static SHARED_DATA struct block_hdr mod_block1024[HEAP_RT_COUNT1024]; -static SHARED_DATA struct block_hdr mod_block2048[HEAP_RT_COUNT2048]; -static SHARED_DATA struct block_hdr mod_block4096[HEAP_RT_COUNT4096]; +static struct block_hdr mod_block16[HEAP_RT_COUNT16]; +static struct block_hdr mod_block32[HEAP_RT_COUNT32]; +static struct block_hdr mod_block64[HEAP_RT_COUNT64]; +static struct block_hdr mod_block128[HEAP_RT_COUNT128]; +static struct block_hdr mod_block256[HEAP_RT_COUNT256]; +static struct block_hdr mod_block512[HEAP_RT_COUNT512]; +static struct block_hdr mod_block1024[HEAP_RT_COUNT1024]; +static struct block_hdr mod_block2048[HEAP_RT_COUNT2048]; +static struct block_hdr mod_block4096[HEAP_RT_COUNT4096]; /* Heap memory map for modules */ -static SHARED_DATA struct block_map rt_heap_map[] = { +static struct block_map rt_heap_map[] = { BLOCK_DEF(16, HEAP_RT_COUNT16, mod_block16), BLOCK_DEF(32, HEAP_RT_COUNT32, mod_block32), BLOCK_DEF(64, HEAP_RT_COUNT64, mod_block64), @@ -48,14 +48,14 @@ static SHARED_DATA struct block_map rt_heap_map[] = { }; /* Heap blocks for buffers */ -static SHARED_DATA struct block_hdr buf_block[HEAP_BUFFER_COUNT]; +static struct block_hdr buf_block[HEAP_BUFFER_COUNT]; /* Heap memory map for buffers */ -static SHARED_DATA struct block_map buf_heap_map[] = { +static struct block_map buf_heap_map[] = { BLOCK_DEF(HEAP_BUFFER_BLOCK_SIZE, HEAP_BUFFER_COUNT, buf_block), }; -static SHARED_DATA struct mm memmap = { +static struct mm memmap = { .system[0] = { .heap = HEAP_SYSTEM_BASE, .size = HEAP_SYSTEM_SIZE, @@ -97,5 +97,5 @@ static SHARED_DATA struct mm memmap = { void platform_init_memmap(struct sof *sof) { /* memmap has been initialized statically as a part of .data */ - sof->memory_map = platform_shared_get(&memmap, sizeof(memmap)); + sof->memory_map = &memmap; } diff --git a/src/platform/mt8196/platform.c b/src/platform/mt8196/platform.c index fc7cd9a7eb34..8cb188ce2210 100644 --- a/src/platform/mt8196/platform.c +++ b/src/platform/mt8196/platform.c @@ -120,7 +120,7 @@ const struct ext_man_windows xsram_window } }; -static SHARED_DATA struct timer timer_shared = { +static struct timer timer_shared = { .id = OSTIMER0, .irq = MTK_DSP_IRQ_OSTIMER32_C0, }; @@ -162,7 +162,7 @@ int platform_init(struct sof *sof) { int ret; - sof->platform_timer = platform_shared_get(&timer_shared, sizeof(timer_shared)); + sof->platform_timer = &timer_shared; sof->cpu_timers = sof->platform_timer; platform_interrupt_init(); platform_clock_init(sof); diff --git a/src/platform/mt8365/include/platform/lib/memory.h b/src/platform/mt8365/include/platform/lib/memory.h index 3f0532d4d417..93bf268c93a2 100644 --- a/src/platform/mt8365/include/platform/lib/memory.h +++ b/src/platform/mt8365/include/platform/lib/memory.h @@ -198,19 +198,8 @@ struct sof; -/** - * \brief Data shared between different cores. - * Does nothing, since mt8195 doesn't support SMP. - */ -#define SHARED_DATA - void platform_init_memmap(struct sof *sof); -static inline void *platform_shared_get(void *ptr, int bytes) -{ - return ptr; -} - #define uncache_to_cache(address) address #define cache_to_uncache(address) address #define cache_to_uncache_init(address) address @@ -219,9 +208,8 @@ static inline void *platform_shared_get(void *ptr, int bytes) /** * \brief Function for keeping shared data synchronized. * It's used after usage of data shared by different cores. - * Such data is either statically marked with SHARED_DATA - * or dynamically allocated with SOF_MEM_FLAG_SHARED flag. - * Does nothing, since mt8195 doesn't support SMP. + * Such data is dynamically allocated with SOF_MEM_FLAG_SHARED + * flag. Does nothing, since mt8195 doesn't support SMP. */ static inline void *platform_rfree_prepare(void *ptr) diff --git a/src/platform/mt8365/lib/clk.c b/src/platform/mt8365/lib/clk.c index 80f95ea30708..e685e1e2d4fc 100644 --- a/src/platform/mt8365/lib/clk.c +++ b/src/platform/mt8365/lib/clk.c @@ -41,7 +41,7 @@ const uint32_t cpu_freq_enc[] = { STATIC_ASSERT(ARRAY_SIZE(platform_cpu_freq) == NUM_CPU_FREQ, invalid_number_of_cpu_frequencies); -static SHARED_DATA struct clock_info platform_clocks_info[NUM_CLOCKS]; +static struct clock_info platform_clocks_info[NUM_CLOCKS]; static inline void clk_setl(uint32_t addr, uint32_t val) { @@ -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); @@ -168,7 +168,7 @@ void platform_clock_init(struct sof *sof) { int i; - sof->clocks = platform_shared_get(platform_clocks_info, sizeof(platform_clocks_info)); + sof->clocks = platform_clocks_info; for (i = 0; i < CONFIG_CORE_COUNT; i++) { sof->clocks[i] = (struct clock_info){ diff --git a/src/platform/mt8365/lib/dai.c b/src/platform/mt8365/lib/dai.c index 6700e3257ac2..522952996d75 100644 --- a/src/platform/mt8365/lib/dai.c +++ b/src/platform/mt8365/lib/dai.c @@ -37,7 +37,7 @@ static int afe_dai_handshake[MT8365_DAI_NUM] = { AFE_HANDSHAKE(MT8365_AFE_IO_DMIC, MT8365_AFE_IRQ_4, MT8365_MEMIF_VUL), }; -static SHARED_DATA struct dai afe_dai[MT8365_DAI_NUM]; +static struct dai afe_dai[MT8365_DAI_NUM]; const struct dai_type_info dti[] = { { diff --git a/src/platform/mt8365/lib/dma.c b/src/platform/mt8365/lib/dma.c index 9e6209790731..d80c81723676 100644 --- a/src/platform/mt8365/lib/dma.c +++ b/src/platform/mt8365/lib/dma.c @@ -18,7 +18,7 @@ extern const struct dma_ops dummy_dma_ops; -static SHARED_DATA struct dma dma[PLATFORM_NUM_DMACS] = { +static struct dma dma[PLATFORM_NUM_DMACS] = { { .plat_data = { .id = DMA_ID_HOST, diff --git a/src/platform/mt8365/lib/memory.c b/src/platform/mt8365/lib/memory.c index 13c7a9b2bdd4..e64eef957742 100644 --- a/src/platform/mt8365/lib/memory.c +++ b/src/platform/mt8365/lib/memory.c @@ -12,30 +12,30 @@ #include <ipc/topology.h> /* Heap blocks for system runtime */ -static SHARED_DATA struct block_hdr sys_rt_block64[HEAP_SYS_RT_COUNT64]; -static SHARED_DATA struct block_hdr sys_rt_block512[HEAP_SYS_RT_COUNT512]; -static SHARED_DATA struct block_hdr sys_rt_block1024[HEAP_SYS_RT_COUNT1024]; +static struct block_hdr sys_rt_block64[HEAP_SYS_RT_COUNT64]; +static struct block_hdr sys_rt_block512[HEAP_SYS_RT_COUNT512]; +static struct block_hdr sys_rt_block1024[HEAP_SYS_RT_COUNT1024]; /* Heap memory for system runtime */ -static SHARED_DATA struct block_map sys_rt_heap_map[] = { +static struct block_map sys_rt_heap_map[] = { BLOCK_DEF(64, HEAP_SYS_RT_COUNT64, sys_rt_block64), BLOCK_DEF(512, HEAP_SYS_RT_COUNT512, sys_rt_block512), BLOCK_DEF(1024, HEAP_SYS_RT_COUNT1024, sys_rt_block1024), }; /* Heap blocks for modules */ -static SHARED_DATA struct block_hdr mod_block16[HEAP_RT_COUNT16]; -static SHARED_DATA struct block_hdr mod_block32[HEAP_RT_COUNT32]; -static SHARED_DATA struct block_hdr mod_block64[HEAP_RT_COUNT64]; -static SHARED_DATA struct block_hdr mod_block128[HEAP_RT_COUNT128]; -static SHARED_DATA struct block_hdr mod_block256[HEAP_RT_COUNT256]; -static SHARED_DATA struct block_hdr mod_block512[HEAP_RT_COUNT512]; -static SHARED_DATA struct block_hdr mod_block1024[HEAP_RT_COUNT1024]; -static SHARED_DATA struct block_hdr mod_block2048[HEAP_RT_COUNT2048]; -static SHARED_DATA struct block_hdr mod_block4096[HEAP_RT_COUNT4096]; +static struct block_hdr mod_block16[HEAP_RT_COUNT16]; +static struct block_hdr mod_block32[HEAP_RT_COUNT32]; +static struct block_hdr mod_block64[HEAP_RT_COUNT64]; +static struct block_hdr mod_block128[HEAP_RT_COUNT128]; +static struct block_hdr mod_block256[HEAP_RT_COUNT256]; +static struct block_hdr mod_block512[HEAP_RT_COUNT512]; +static struct block_hdr mod_block1024[HEAP_RT_COUNT1024]; +static struct block_hdr mod_block2048[HEAP_RT_COUNT2048]; +static struct block_hdr mod_block4096[HEAP_RT_COUNT4096]; /* Heap memory map for modules */ -static SHARED_DATA struct block_map rt_heap_map[] = { +static struct block_map rt_heap_map[] = { BLOCK_DEF(16, HEAP_RT_COUNT16, mod_block16), BLOCK_DEF(32, HEAP_RT_COUNT32, mod_block32), BLOCK_DEF(64, HEAP_RT_COUNT64, mod_block64), @@ -48,14 +48,14 @@ static SHARED_DATA struct block_map rt_heap_map[] = { }; /* Heap blocks for buffers */ -static SHARED_DATA struct block_hdr buf_block[HEAP_BUFFER_COUNT]; +static struct block_hdr buf_block[HEAP_BUFFER_COUNT]; /* Heap memory map for buffers */ -static SHARED_DATA struct block_map buf_heap_map[] = { +static struct block_map buf_heap_map[] = { BLOCK_DEF(HEAP_BUFFER_BLOCK_SIZE, HEAP_BUFFER_COUNT, buf_block), }; -static SHARED_DATA struct mm memmap = { +static struct mm memmap = { .system[0] = { .heap = HEAP_SYSTEM_BASE, .size = HEAP_SYSTEM_SIZE, @@ -97,5 +97,5 @@ static SHARED_DATA struct mm memmap = { void platform_init_memmap(struct sof *sof) { /* memmap has been initialized statically as a part of .data */ - sof->memory_map = platform_shared_get(&memmap, sizeof(memmap)); + sof->memory_map = &memmap; } diff --git a/src/platform/mt8365/platform.c b/src/platform/mt8365/platform.c index d57edc2f8431..93e617cdebbf 100644 --- a/src/platform/mt8365/platform.c +++ b/src/platform/mt8365/platform.c @@ -120,7 +120,7 @@ const struct ext_man_windows xsram_window } }; -static SHARED_DATA struct timer timer_shared = { +static struct timer timer_shared = { .id = OSTIMER0, .irq = SYSTICK_TIMER_IRQ, }; @@ -164,7 +164,7 @@ int platform_init(struct sof *sof) mailbox_sw_reg_write(SRAM_REG_OP_CPU2DSP, 0); mailbox_sw_reg_write(SRAM_REG_OP_DSP2CPU, 0); - sof->platform_timer = platform_shared_get(&timer_shared, sizeof(timer_shared)); + sof->platform_timer = &timer_shared; sof->cpu_timers = sof->platform_timer; platform_interrupt_init(); diff --git a/src/platform/mtk/dai.c b/src/platform/mtk/dai.c index cb6ebc144624..2e1d85a89dfc 100644 --- a/src/platform/mtk/dai.c +++ b/src/platform/mtk/dai.c @@ -1,9 +1,14 @@ // SPDX-License-Identifier: BSD-3-Clause // Copyright(c) 2024 Google LLC. All rights reserved. // Author: Andy Ross <andyross@google.com> +#ifndef CONFIG_ZEPHYR_NATIVE_DRIVERS #include <sof/lib/dai-legacy.h> +#endif #include <ipc/dai.h> #include <sof/drivers/afe-drv.h> +#include <stdbool.h> +#include <errno.h> +#include <zephyr/devicetree.h> /* The legacy driver stores register addresses as an offset from an * arbitrary base address (which is not actually a unified block of @@ -22,6 +27,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 +141,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) \ @@ -155,6 +163,7 @@ static struct mtk_base_memif_data afe_memifs[] = { DT_FOREACH_STATUS_OKAY(mediatek_afe, EMPTY_STRUCT) }; +#ifndef CONFIG_ZEPHYR_NATIVE_DRIVERS static struct dai mtk_dais[] = { DT_FOREACH_STATUS_OKAY(mediatek_afe, EMPTY_STRUCT) }; @@ -207,6 +216,7 @@ static const struct dai_info mtk_dai_info = { .dai_type_array = mtk_dai_types, .num_dai_types = ARRAY_SIZE(mtk_dai_types), }; +#endif /* !CONFIG_ZEPHYR_NATIVE_DRIVERS */ #if defined(CONFIG_SOC_SERIES_MT818X) || defined(CONFIG_SOC_MT8195) static unsigned int mtk_afe2adsp_addr(unsigned int addr) @@ -269,6 +279,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 }, @@ -307,7 +331,7 @@ struct mtk_base_afe_platform mtk_afe_platform = { .memif_32bit_supported = 0, .irq_datas = NULL, .irqs_size = 0, - .dais_size = ARRAY_SIZE(mtk_dais), + .dais_size = ARRAY_SIZE(afes), .afe_fs = mtk_afe_fs, .irq_fs = mtk_afe_fs_timing, #if defined(CONFIG_SOC_SERIES_MT818X) || defined(CONFIG_SOC_MT8195) @@ -325,6 +349,7 @@ int mtk_dai_init(struct sof *sof) afe_memifs[i].id = i; cfg_convert(&afes[i], &afe_memifs[i]); +#ifndef CONFIG_ZEPHYR_NATIVE_DRIVERS /* Also initialize the dais array */ extern const struct dai_driver afe_dai_driver; @@ -345,6 +370,7 @@ int mtk_dai_init(struct sof *sof) int hs = (i << 16) | di; mtk_dais[di].plat_data.fifo[0].handshake = hs; +#endif } /* DTS stores the direction as a boolean property, but the @@ -360,7 +386,9 @@ int mtk_dai_init(struct sof *sof) for (/**/; i < ARRAY_SIZE(afes); i++) __ASSERT_NO_MSG(!afes[i].downlink); +#ifndef CONFIG_ZEPHYR_NATIVE_DRIVERS sof->dai_info = &mtk_dai_info; sof->dma_info = &mtk_dma_info; +#endif return 0; } diff --git a/src/platform/mtk/include/platform/lib/memory.h b/src/platform/mtk/include/platform/lib/memory.h index a84809bf2d48..b5ce4de855ab 100644 --- a/src/platform/mtk/include/platform/lib/memory.h +++ b/src/platform/mtk/include/platform/lib/memory.h @@ -16,11 +16,6 @@ BUILD_ASSERT(PLATFORM_DCACHE_ALIGN == XCHAL_DCACHE_LINESIZE); #define uncache_to_cache(addr) (addr) #define cache_to_uncache(addr) (addr) -static inline void *platform_shared_get(void *ptr, int bytes) -{ - return ptr; -} - #define host_to_local(addr) (addr) #define PLATFORM_HEAP_SYSTEM 1 @@ -28,8 +23,6 @@ static inline void *platform_shared_get(void *ptr, int bytes) #define PLATFORM_HEAP_RUNTIME 1 #define PLATFORM_HEAP_BUFFER 1 -#define SHARED_DATA /* no special section attribute needed */ - /* Mailbox window addresses for the rimage extended manifest. The * struct is optimized out in generated code, it's just here to be a * little clearer than the pages of #defines used traditionally. @@ -42,7 +35,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..22497cf19641 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 @@ -108,14 +157,23 @@ struct ipc_data_host_buffer *ipc_platform_get_host_buffer(struct ipc *ipc) int platform_ipc_init(struct ipc *ipc) { mtk_host_buffer.page_table = hostbuf_ptable; +#ifdef CONFIG_ZEPHYR_NATIVE_DRIVERS + mtk_host_buffer.dmac = sof_dma_get(SOF_DMA_DIR_HMEM_TO_LMEM, 0, + SOF_DMA_DEV_HOST, SOF_DMA_ACCESS_SHARED); +#else mtk_host_buffer.dmac = dma_get(DMA_DIR_HMEM_TO_LMEM, 0, DMA_DEV_HOST, DMA_ACCESS_SHARED); +#endif 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; } @@ -157,18 +215,63 @@ void clocks_init(struct sof *sof) sof->clocks = clks; } +#ifdef CONFIG_ZEPHYR_NATIVE_DRIVERS +/* + * Allocate SOF-level channel descriptor arrays (struct dma_chan_data[]) for + * each sof_dma entry. Native Zephyr DMA drivers manage their own internal + * channel state but do not allocate the SOF-layer chan[] array that + * dai-zephyr.c and host-zephyr.c rely on (e.g. dd->chan = &dd->dma->chan[i]). + * Legacy DMA drivers (dummy-dma, afe-memif) did this in their own probe(); + * in the native path we do it here after dmac_init() registers the DMA info. + */ +static int mtk_dma_chan_alloc(struct sof *sof) +{ + int i, j; + + for (i = 0; i < sof->dma_info->num_dmas; i++) { + struct sof_dma *d = &sof->dma_info->dma_array[i]; + + if (!d->plat_data.channels || d->chan) + continue; + + d->chan = rzalloc(SOF_MEM_FLAG_KERNEL, + d->plat_data.channels * sizeof(struct dma_chan_data)); + if (!d->chan) + return -ENOMEM; + + for (j = 0; j < d->plat_data.channels; j++) { + d->chan[j].dma = d; + d->chan[j].index = j; + } + } + + return 0; +} +#endif + int platform_init(struct sof *sof) { + int ret; + clocks_init(sof); scheduler_init_edf(); sof->platform_timer_domain = zephyr_domain_init(PLATFORM_DEFAULT_CLOCK); scheduler_init_ll(sof->platform_timer_domain); mtk_dai_init(sof); +#ifdef CONFIG_ZEPHYR_NATIVE_DRIVERS + /* In native mode, DMA is registered in sof/zephyr/lib/dma.c */ + dmac_init(sof); + ret = mtk_dma_chan_alloc(sof); + if (ret < 0) + return ret; +#endif ipc_init(sof); +#if CONFIG_SCHEDULE_DMA_MULTI_CHANNEL sof->platform_dma_domain = dma_multi_chan_domain_init(&sof->dma_info->dma_array[0], sof->dma_info->num_dmas, PLATFORM_DEFAULT_CLOCK, false); +#endif sa_init(sof, CONFIG_SYSTICK_PERIOD); 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/posix/fuzz.c b/src/platform/posix/fuzz.c index 555a22d40578..e60d8e549035 100644 --- a/src/platform/posix/fuzz.c +++ b/src/platform/posix/fuzz.c @@ -10,37 +10,68 @@ #include <zephyr/sys/time_units.h> #include <nsi_cpu_if.h> #include <nsi_main_semipublic.h> +#include <platform/fuzz.h> const uint8_t *posix_fuzz_buf; size_t posix_fuzz_sz; +/* Number of simulator quanta the budget is split into for the + * drain-or-abort loop. More quanta = earlier exit on quick testcases. + */ +#define POSIX_FUZZ_DRAIN_QUANTA 8 + /** * Entry point for fuzzing. Works by placing the data * into two known symbols, triggering an app-visible interrupt, and - * then letting the simulator run for a fixed amount of time (intended to be - * "long enough" to handle the event and reach a quiescent state - * again) + * then letting the simulator run for up to a fixed amount of time + * split into small quanta, exiting as soon as the OS has drained the + * staged fuzz input. If the budget is exhausted before drain we drop + * pending state to keep testcases isolated. */ NATIVE_SIMULATOR_IF int LLVMFuzzerTestOneInput(const uint8_t *data, size_t sz) { static bool runner_initialized; + uint64_t total_us; + uint64_t quantum_us; + int i; if (!runner_initialized) { nsi_init(0, NULL); runner_initialized = true; } + total_us = k_ticks_to_us_ceil64(CONFIG_ZEPHYR_POSIX_FUZZ_TICKS); + quantum_us = (total_us + POSIX_FUZZ_DRAIN_QUANTA - 1) / POSIX_FUZZ_DRAIN_QUANTA; + if (quantum_us == 0) + quantum_us = 1; + + /* Fresh testcase: drop any leftovers from a previous case before + * staging the new buffer, so state from the prior input cannot + * influence this one. + */ + posix_fuzz_case_begin(); + /* Provide the fuzz data to the embedded OS as an interrupt, with - * "DMA-like" data placed into native_fuzz_buf/sz + * "DMA-like" data placed into posix_fuzz_buf/sz. */ - posix_fuzz_buf = (void *)data; + posix_fuzz_buf = data; posix_fuzz_sz = sz; hw_irq_ctrl_set_irq(CONFIG_ZEPHYR_POSIX_FUZZ_IRQ); - /* Give the OS time to process whatever happened in that - * interrupt and reach an idle state. + /* Bounded drain loop: run simulator in small quanta and exit + * as soon as both the raw input buffer and the staged IPC + * queue are empty. + */ + for (i = 0; i < POSIX_FUZZ_DRAIN_QUANTA; i++) { + nsi_exec_for(quantum_us); + if (!posix_fuzz_case_pending()) + return 0; + } + + /* Budget exhausted without full drain: hard reset so the next + * testcase starts clean. */ - nsi_exec_for(k_ticks_to_us_ceil64(CONFIG_ZEPHYR_POSIX_FUZZ_TICKS)); + posix_fuzz_case_abort(); return 0; } diff --git a/src/platform/posix/include/platform/fuzz.h b/src/platform/posix/include/platform/fuzz.h new file mode 100644 index 000000000000..91f28b3285d2 --- /dev/null +++ b/src/platform/posix/include/platform/fuzz.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* Copyright(c) 2026 Intel Corporation. All rights reserved. */ + +#ifndef PLATFORM_POSIX_FUZZ_H +#define PLATFORM_POSIX_FUZZ_H + +#include <stdbool.h> + +/** + * @brief Reset IPC staging state at the start of a new fuzz testcase. + * + * Must be called before staging new fuzz input so that any leftover + * state from a previous testcase that failed to drain within the tick + * budget cannot influence the new one. + */ +void posix_fuzz_case_begin(void); + +/** + * @brief Query whether any staged IPC fuzz input is still pending. + * + * @return true if the raw input buffer or the IPC staging queue is + * non-empty, false when fully drained. + */ +bool posix_fuzz_case_pending(void); + +/** + * @brief Hard-reset all staged IPC fuzz state. + * + * Called when the simulator tick budget is exhausted before the input + * has been fully drained, ensuring the next testcase starts clean. + */ +void posix_fuzz_case_abort(void); + +#endif /* PLATFORM_POSIX_FUZZ_H */ diff --git a/src/platform/posix/include/platform/lib/memory.h b/src/platform/posix/include/platform/lib/memory.h index e15e5f370aec..e24c48aa40b3 100644 --- a/src/platform/posix/include/platform/lib/memory.h +++ b/src/platform/posix/include/platform/lib/memory.h @@ -6,25 +6,38 @@ #define PLATFORM_HOST_PLATFORM_MEMORY_H #include <stdint.h> +#include <ipc/header.h> #define PLATFORM_DCACHE_ALIGN 64 #define uncache_to_cache(addr) (addr) #define cache_to_uncache(addr) (addr) +/* + * Mailbox base macros must yield byte-pointer arithmetic so that the + * generic mailbox API in `sof/src/include/sof/lib/mailbox.h` + * (`MAILBOX_HOSTBOX_BASE + offset`, etc., where `offset` is a byte + * offset) addresses the intended byte. The backing storage is + * declared as `uint32_t[]` only for natural-alignment; addressing is + * done through a `uint8_t *` cast so byte offsets do not get scaled + * by `sizeof(uint32_t)`. Every other SOF platform defines these + * bases as plain byte addresses (integer literals or + * `SRAM_INBOX_BASE`), so the cast keeps POSIX consistent with that + * ABI. + */ extern uint32_t posix_hostbox[]; -#define MAILBOX_HOSTBOX_SIZE 1024 -#define MAILBOX_HOSTBOX_BASE (&posix_hostbox[0]) +#define MAILBOX_HOSTBOX_SIZE SOF_IPC_MSG_MAX_SIZE +#define MAILBOX_HOSTBOX_BASE ((uint8_t *)&posix_hostbox[0]) extern uint32_t posix_dspbox[]; #define MAILBOX_DSPBOX_SIZE 4096 -#define MAILBOX_DSPBOX_BASE (&posix_dspbox[0]) +#define MAILBOX_DSPBOX_BASE ((uint8_t *)&posix_dspbox[0]) extern uint32_t posix_stream[]; #define MAILBOX_STREAM_SIZE 4096 -#define MAILBOX_STREAM_BASE (&posix_stream[0]) +#define MAILBOX_STREAM_BASE ((uint8_t *)&posix_stream[0]) extern uint32_t posix_trace[]; -#define MAILBOX_TRACE_BASE (&posix_trace[0]) +#define MAILBOX_TRACE_BASE ((uint8_t *)&posix_trace[0]) #define MAILBOX_TRACE_SIZE 4096 #define PLATFORM_HEAP_SYSTEM 1 @@ -34,11 +47,4 @@ extern uint32_t posix_trace[]; #define host_to_local(addr) (addr) -static inline void *platform_shared_get(void *ptr, int bytes) -{ - return ptr; -} - -#define SHARED_DATA /**/ - #endif /* PLATFORM_HOST_PLATFORM_MEMORY_H */ diff --git a/src/platform/posix/ipc.c b/src/platform/posix/ipc.c index 701686e6ff97..64586755d18c 100644 --- a/src/platform/posix/ipc.c +++ b/src/platform/posix/ipc.c @@ -1,19 +1,25 @@ // SPDX-License-Identifier: BSD-3-Clause // Copyright(c) 2022 Google LLC. All rights reserved. // Author: Andy Ross <andyross@google.com> +#include <platform/fuzz.h> #include <sof/lib/uuid.h> #include <sof/ipc/msg.h> #include <sof/lib/mailbox.h> #include <sof/ipc/common.h> #include <sof/ipc/schedule.h> +#include <sof/ipc/topology.h> #include <sof/schedule/edf_schedule.h> #include <sof/audio/component_ext.h> +#include <stdbool.h> +#include <stddef.h> +#include <errno.h> // 6c8f0d53-ff77-4ca1-b825-c0c4e1b0d322 SOF_DEFINE_REG_UUID(ipc_task_posix); static struct ipc *global_ipc; +#ifdef CONFIG_ARCH_POSIX_LIBFUZZER // Not an ISR, called from the native_posix fuzz interrupt. Left // alone for general hygiene. This is how a IPC interrupt would look // if we had one. @@ -22,8 +28,9 @@ static void posix_ipc_isr(void *arg) ipc_schedule_process(global_ipc); } -// External symbols set up by the fuzzing layer -extern uint8_t *posix_fuzz_buf, posix_fuzz_sz; +// External symbols set up by the fuzzing layer in fuzz.c. +extern const uint8_t *posix_fuzz_buf; +extern size_t posix_fuzz_sz; // Lots of space. Should really synchronize with the -max_len // parameter to libFuzzer (defaults to 4096), but that requires @@ -32,16 +39,196 @@ extern uint8_t *posix_fuzz_buf, posix_fuzz_sz; static uint8_t fuzz_in[65536]; static size_t fuzz_in_sz; -// The protocol here is super simple: the first byte is a message size -// in units of 16 bits (the buffer maximum defaults to 384 bytes, and -// I didn't want to waste space early in the buffer lest I confuse the -// fuzzing heuristics). We then copy that much of the input buffer -// (subject to clamping obviously) into the incoming IPC message -// buffer and invoke the ISR. Any remainder will be delivered -// synchronously as another message after receipt of "complete_cmd()" -// from the SOF engine, etc... Eventually we'll receive another fuzz -// input after some amount of simulated time has passed (c.f. -// CONFIG_ZEPHYR_POSIX_FUZZ_TICKS) +/* + * posix_ipc_teardown - drop all IPC-tracked objects left over from the + * previous fuzz testcase so the next one starts from a clean topology. + * + * Walking and freeing comp_list in the same pass is unsafe because the + * SOF free helpers unlink the entry from the list, so we snapshot the + * IDs of one type into a local array first and then call the typed + * free function for each one. Passes run in dependency order + * (COMPONENT -> BUFFER -> PIPELINE) to keep the SOF topology layer + * from dereferencing already-freed parents. + * + * Each typed pass drains in batches: it snapshots up to + * POSIX_TEARDOWN_BATCH ids, frees them, and repeats until no entry of + * that type is left, so the number of objects a testcase may create is + * not bounded by the snapshot size. + * + * A final force-drain pass then walks anything still on the list with + * list_for_item_safe() and removes it with list_item_del() + rfree() + * directly. That keeps the harness future-proof against new COMP_TYPE_* + * values and against entries a typed free could not release: whatever + * the reason, comp_list is guaranteed empty on return. The inner union + * object (cd/cb/pipeline) is leaked in that edge case, which is + * acceptable for a fuzzing harness. + */ +#define POSIX_TEARDOWN_BATCH 256 + +static void posix_ipc_teardown(void) +{ + static const uint16_t free_order[] = { + COMP_TYPE_COMPONENT, +#if CONFIG_IPC_MAJOR_3 + /* + * IPC4 never stores COMP_TYPE_BUFFER in comp_list + * (see ipc4/helper.c) and ipc_buffer_free() is not + * compiled for IPC4, so skip the buffer pass there. + */ + COMP_TYPE_BUFFER, +#endif + COMP_TYPE_PIPELINE, + }; + uint32_t ids[POSIX_TEARDOWN_BATCH]; + struct ipc_comp_dev *icd; + struct list_item *pos, *tmp; + int n; + + if (!global_ipc) + return; + + /* + * Pre-pass: prepare components and pipelines for the ordered free + * passes below. + * + * ipc_comp_free() refuses to free a component unless its state is + * COMP_STATE_READY. A fuzz testcase that ran INIT_INSTANCE followed + * by SET_PIPELINE_STATE:RUNNING will leave components in PREPARE, + * PAUSED, or ACTIVE state. Force every component back to READY so + * the free pass can proceed without silently skipping entries. + * + * For pipelines with an active scheduler task, cancel the task before + * ipc_pipeline_free() calls schedule_task_free() on it. Without this, + * schedule_task_free() blocks waiting for the task to complete, which + * on native_sim can stall for up to 100 LL periods. + */ + list_for_item(pos, &global_ipc->comp_list) { + icd = container_of(pos, struct ipc_comp_dev, list); + if (icd->type == COMP_TYPE_COMPONENT && icd->cd) { + icd->cd->state = COMP_STATE_READY; + /* Ensure buffer lists are valid so ipc_comp_free() + * does not bail at the uninitialized-list check. + * A component whose init failed partway may have + * NULL list pointers. + */ + if (!icd->cd->bsource_list.next) + list_init(&icd->cd->bsource_list); + if (!icd->cd->bsink_list.next) + list_init(&icd->cd->bsink_list); + } + if (icd->type == COMP_TYPE_PIPELINE && + icd->pipeline && icd->pipeline->pipe_task) + schedule_task_cancel(icd->pipeline->pipe_task); + } + + for (int pass = 0; pass < (int)ARRAY_SIZE(free_order); pass++) { + uint16_t type = free_order[pass]; + + /* + * Drain this type in batches. The ipc_*_free() helpers + * unlink each entry internally, so snapshot up to + * POSIX_TEARDOWN_BATCH ids, free them, and repeat until no + * entry of this type is left. A batch that frees nothing + * (e.g. an entry ipc_comp_free() refuses to release) ends the + * loop so it cannot spin forever; the force-drain pass below + * removes any such straggler. + */ + do { + int freed = 0; + + n = 0; + list_for_item(pos, &global_ipc->comp_list) { + icd = container_of(pos, struct ipc_comp_dev, + list); + if (icd->type == type && + n < POSIX_TEARDOWN_BATCH) + ids[n++] = icd->id; + } + + for (int i = 0; i < n; i++) { + int ret = -EINVAL; + + switch (type) { + case COMP_TYPE_COMPONENT: + ret = ipc_comp_free(global_ipc, ids[i]); + break; +#if CONFIG_IPC_MAJOR_3 + case COMP_TYPE_BUFFER: + ret = ipc_buffer_free(global_ipc, ids[i]); + break; +#endif + case COMP_TYPE_PIPELINE: + ret = ipc_pipeline_free(global_ipc, + ids[i]); + break; + } + if (!ret) + freed++; + } + + if (!freed) + break; + } while (n == POSIX_TEARDOWN_BATCH); + } + + /* + * Force-drain anything still on the list: unknown/future + * COMP_TYPE_* values or entries a typed free could not release. + * Walk with the _safe variant because each entry is unlinked as it + * is visited. The inner union pointer leaks, but comp_list is empty + * on return regardless of how many objects remained, so the next + * testcase never observes a stale entry. + */ + list_for_item_safe(pos, tmp, &global_ipc->comp_list) { + icd = container_of(pos, struct ipc_comp_dev, list); + list_item_del(&icd->list); + rfree(icd); + } +} + +/* + * Testcase-isolation helpers used by the libFuzzer entry point in + * fuzz.c. They keep ownership of the cross-call state in one module + * so a new testcase never observes leftovers from a previous one that + * failed to drain inside the simulator tick budget. + */ +void posix_fuzz_case_begin(void) +{ + posix_ipc_teardown(); + fuzz_in_sz = 0; +} + +bool posix_fuzz_case_pending(void) +{ + return posix_fuzz_sz != 0 || fuzz_in_sz != 0; +} + +void posix_fuzz_case_abort(void) +{ + posix_fuzz_sz = 0; + fuzz_in_sz = 0; +} + +// The framing protocol is super simple: the first two bytes are a +// little-endian message size in bytes (capped below at +// SOF_IPC_MSG_MAX_SIZE - 4 so it always fits in the IPC message +// buffer). We then copy that much of the input buffer (subject to +// clamping obviously) into the incoming IPC message buffer and invoke +// the ISR. Any remainder will be delivered synchronously as another +// message after receipt of "complete_cmd()" from the SOF engine, +// etc... Eventually we'll receive another fuzz input after some +// amount of simulated time has passed (c.f. +// CONFIG_ZEPHYR_POSIX_FUZZ_TICKS). +// +// The historical encoding used a single byte multiplied by 2, which +// capped each message at 510 bytes. That was fine for IPC3 +// (SOF_IPC_MSG_MAX_SIZE = 384) but limited IPC4 (max 4096) to ~12% +// of its envelope, hiding all large_config / vendor_config / +// pipeline-state-data paths from coverage feedback. Widening to two +// bytes lets the fuzzer reach the full IPC4 message range; existing +// corpora are reinterpreted but libFuzzer recovers within minutes. +#define POSIX_FUZZ_HDR_LEN 2 + static void fuzz_isr(const void *arg) { size_t rem, i, n = MIN(posix_fuzz_sz, sizeof(fuzz_in) - fuzz_in_sz); @@ -49,20 +236,22 @@ static void fuzz_isr(const void *arg) for (i = 0; i < n; i++) fuzz_in[fuzz_in_sz++] = posix_fuzz_buf[i]; - if (fuzz_in_sz == 0) + if (fuzz_in_sz < POSIX_FUZZ_HDR_LEN) return; if (!global_ipc->comp_data) return; - size_t maxsz = SOF_IPC_MSG_MAX_SIZE - 4, msgsz = fuzz_in[0] * 2; + size_t maxsz = SOF_IPC_MSG_MAX_SIZE - 4; + size_t msgsz = (size_t)fuzz_in[0] | ((size_t)fuzz_in[1] << 8); - n = MIN(msgsz, MIN(fuzz_in_sz - 1, maxsz)); - rem = fuzz_in_sz - (n + 1); + msgsz = MIN(msgsz, maxsz); + n = MIN(msgsz, fuzz_in_sz - POSIX_FUZZ_HDR_LEN); + rem = fuzz_in_sz - (n + POSIX_FUZZ_HDR_LEN); memset(global_ipc->comp_data, 0, maxsz); - memcpy(global_ipc->comp_data, &fuzz_in[1], n); - memmove(&fuzz_in[0], &fuzz_in[n + 1], rem); + memcpy(global_ipc->comp_data, &fuzz_in[POSIX_FUZZ_HDR_LEN], n); + memmove(&fuzz_in[0], &fuzz_in[n + POSIX_FUZZ_HDR_LEN], rem); fuzz_in_sz = rem; #ifdef CONFIG_IPC_MAJOR_3 @@ -131,6 +320,7 @@ static void fuzz_isr(const void *arg) posix_ipc_isr(NULL); } +#endif // This API is... confounded by its history. With IPC3, the job of // this function is to get a newly-received IPC message header (!) @@ -145,11 +335,38 @@ static void fuzz_isr(const void *arg) // ipc_platform_compact_read_msg(), writing 8 bytes unconditionally on // the header object it receives, which is then returned here, and // then passed to ipc_cmd(). +// +// The harness also mirrors the framed message into MAILBOX_HOSTBOX so +// that handlers reading payload directly from the hostbox region +// (large_config_set/get, set_dx, set_pipeline_state, vendor_config and +// friends in ipc4/handler-user.c and ipc4/handler-kernel.c) observe +// the fuzz bytes rather than stale or zero-filled memory. +// +// The two IPC majors split header and payload differently: +// +// * IPC3 carries the header in-band at the start of the message, and +// mailbox_validate() walks the full message starting from offset 0 +// of the hostbox. The full message is mirrored as-is. +// +// * IPC4 splits the 8-byte compact header (consumed via +// ipc_compact_read_msg()) from the payload, which on real hardware +// lives in HOSTBOX. The harness therefore mirrors only the +// post-header bytes, so the first dword of MAILBOX_HOSTBOX matches +// the first dword of the IPC4 payload (e.g. pipelines_count for +// SET_PIPELINE_STATE) instead of header bits. +// +// posix_hostbox is sized to SOF_IPC_MSG_MAX_SIZE (see +// platform/lib/memory.h), so the copy is always in bounds for both +// IPC3 and IPC4 message envelopes. enum task_state ipc_platform_do_cmd(struct ipc *ipc) { struct ipc_cmd_hdr *hdr; #ifdef CONFIG_IPC_MAJOR_4 + memset(posix_hostbox, 0, SOF_IPC_MSG_MAX_SIZE); + memcpy(posix_hostbox, + (const uint8_t *)global_ipc->comp_data + sizeof(struct ipc_cmd_hdr), + SOF_IPC_MSG_MAX_SIZE - sizeof(struct ipc_cmd_hdr)); hdr = ipc_compact_read_msg(); #else memcpy(posix_hostbox, global_ipc->comp_data, SOF_IPC_MSG_MAX_SIZE); @@ -172,12 +389,14 @@ int ipc_platform_compact_read_msg(struct ipc_cmd_hdr *hdr, int words) // Re-raise the interrupt if there's still fuzz data to process void ipc_platform_complete_cmd(struct ipc *ipc) { +#ifdef CONFIG_ARCH_POSIX_LIBFUZZER extern void posix_sw_set_pending_IRQ(unsigned int IRQn); if (fuzz_in_sz > 0) { posix_fuzz_sz = 0; posix_sw_set_pending_IRQ(CONFIG_ZEPHYR_POSIX_FUZZ_IRQ); } +#endif } int ipc_platform_send_msg(const struct ipc_msg *msg) @@ -200,8 +419,10 @@ void ipc_platform_send_msg_direct(const struct ipc_msg *msg) int platform_ipc_init(struct ipc *ipc) { +#ifdef CONFIG_ARCH_POSIX_LIBFUZZER IRQ_CONNECT(CONFIG_ZEPHYR_POSIX_FUZZ_IRQ, 0, fuzz_isr, NULL, 0); irq_enable(CONFIG_ZEPHYR_POSIX_FUZZ_IRQ); +#endif global_ipc = ipc; schedule_task_init_edf(&ipc->ipc_task, SOF_UUID(ipc_task_posix_uuid), 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..57f001de169b --- /dev/null +++ b/src/platform/qemu_xtensa/include/platform/lib/memory.h @@ -0,0 +1,14 @@ +/* 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 */ + +#define PLATFORM_DCACHE_ALIGN sizeof(void *) +#define HOST_PAGE_SIZE 4096 + +#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..2ce5f01f3999 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; } @@ -519,7 +519,7 @@ static int test_keyword_ctrl_set_bin_data(struct comp_dev *dev, * configuration will be used when playback/capture * starts. */ - comp_err(dev, "keyword_ctrl_set_bin_data(): driver is busy"); + comp_err(dev, "driver is busy"); return -EBUSY; } @@ -531,7 +531,7 @@ static int test_keyword_ctrl_set_bin_data(struct comp_dev *dev, ret = comp_data_blob_set_cmd(cd->model_handler, cdata); break; default: - comp_err(dev, "keyword_ctrl_set_bin_data(): unknown binary data type"); + comp_err(dev, "unknown binary data type"); break; } @@ -545,20 +545,20 @@ static int test_keyword_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, "test_keyword_cmd_set_data(): invalid version"); + comp_err(dev, "invalid version"); return -EINVAL; } switch (cdata->cmd) { case SOF_CTRL_CMD_ENUM: - comp_info(dev, "test_keyword_cmd_set_data(), SOF_CTRL_CMD_ENUM"); + comp_info(dev, "SOF_CTRL_CMD_ENUM"); break; case SOF_CTRL_CMD_BINARY: - comp_info(dev, "test_keyword_cmd_set_data(), SOF_CTRL_CMD_BINARY"); + comp_info(dev, "SOF_CTRL_CMD_BINARY"); ret = test_keyword_ctrl_set_bin_data(dev, cdata); break; default: - comp_err(dev, "test_keyword_cmd_set_data(): invalid cdata->cmd"); + comp_err(dev, "invalid cdata->cmd"); ret = -EINVAL; break; } @@ -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: @@ -667,7 +667,7 @@ static struct comp_dev *test_keyword_new(const struct comp_driver *drv, struct comp_data *cd = NULL; int ret = 0; - comp_cl_info(&comp_keyword, "test_keyword_new()"); + comp_cl_info(&comp_keyword, "entry"); dev = comp_alloc(drv, sizeof(*dev)); if (!dev) @@ -690,6 +690,10 @@ static struct comp_dev *test_keyword_new(const struct comp_driver *drv, /* component model data handler */ cd->model_handler = comp_data_blob_handler_new(dev); + if (!cd->model_handler) { + comp_err(dev, "comp_data_blob_handler_new failed"); + goto cd_fail; + } #if CONFIG_IPC_MAJOR_4 /* For IPC4 we only receive the base_cfg, make a copy of it */ @@ -700,12 +704,12 @@ static struct comp_dev *test_keyword_new(const struct comp_driver *drv, if (bs > 0) { if (bs < sizeof(struct sof_detect_test_config)) { - comp_err(dev, "test_keyword_new(): invalid data size"); + comp_err(dev, "invalid data size"); goto cd_fail; } if (test_keyword_apply_config(dev, cfg)) { - comp_err(dev, "test_keyword_new(): failed to apply config"); + comp_err(dev, "failed to apply config"); goto cd_fail; } } @@ -714,7 +718,7 @@ static struct comp_dev *test_keyword_new(const struct comp_driver *drv, ret = comp_init_data_blob(cd->model_handler, INITIAL_MODEL_DATA_SIZE, NULL); if (ret < 0) { - comp_err(dev, "test_keyword_new(): model data initial failed"); + comp_err(dev, "model data initial failed"); goto cd_fail; } @@ -731,7 +735,7 @@ static struct comp_dev *test_keyword_new(const struct comp_driver *drv, #endif /* CONFIG_IPC_MAJOR_4 */ if (!cd->msg) { - comp_err(dev, "test_keyword_new(): ipc notification init failed"); + comp_err(dev, "ipc notification init failed"); goto cd_fail; } @@ -740,7 +744,7 @@ static struct comp_dev *test_keyword_new(const struct comp_driver *drv, cd->input = rballoc_align(SOF_MEM_FLAG_USER, sizeof(int16_t) * KWD_NN_IN_BUFF_SIZE, 64); if (!cd->input) { - comp_err(dev, "test_keyword_new(): input alloc failed"); + comp_err(dev, "input alloc failed"); goto cd_fail; } bzero(cd->input, sizeof(int16_t) * KWD_NN_IN_BUFF_SIZE); @@ -766,7 +770,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 +778,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 +786,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 +800,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 +825,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 +838,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 +854,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 +884,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 +907,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 +931,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 +954,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 +973,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; } @@ -1103,14 +1107,13 @@ static const struct comp_driver comp_keyword = { }, }; -static SHARED_DATA struct comp_driver_info comp_keyword_info = { +static struct comp_driver_info comp_keyword_info = { .drv = &comp_keyword, }; UT_STATIC void sys_comp_keyword_init(void) { - comp_register(platform_shared_get(&comp_keyword_info, - sizeof(comp_keyword_info))); + comp_register(&comp_keyword_info); } DECLARE_MODULE(sys_comp_keyword_init); 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..da1f293eb55a 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) @@ -518,6 +518,16 @@ static int smart_amp_prepare(struct comp_dev *dev) sad->in_channels = audio_stream_get_channels(&sad->source_buf->stream); + /* out_channels bounds the processing loop that indexes the + * PLATFORM_MAX_CHANNELS-sized channel map, so reject a larger count + */ + if (sad->out_channels > PLATFORM_MAX_CHANNELS || + sad->in_channels > PLATFORM_MAX_CHANNELS) { + comp_err(dev, "invalid channel count, in %u out %u", + sad->in_channels, sad->out_channels); + return -EINVAL; + } + if (sad->feedback_buf) { audio_stream_set_channels(&sad->feedback_buf->stream, sad->config.feedback_channels); @@ -527,7 +537,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; @@ -549,14 +559,13 @@ static const struct comp_driver comp_smart_amp = { }, }; -static SHARED_DATA struct comp_driver_info comp_smart_amp_info = { +static struct comp_driver_info comp_smart_amp_info = { .drv = &comp_smart_amp, }; UT_STATIC void sys_comp_smart_amp_test_init(void) { - comp_register(platform_shared_get(&comp_smart_amp_info, - sizeof(comp_smart_amp_info))); + comp_register(&comp_smart_amp_info); } DECLARE_MODULE(sys_comp_smart_amp_test_init); 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..6c919ae96a19 --- /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 -->|LL Tick Source| 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 the DP scheduler for task deadline recalculation. + +### 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 98% rename from src/schedule/ll_schedule.c rename to src/schedule/ll_schedule_xtos.c index 10615045d4d2..704a680e144d 100644 --- a/src/schedule/ll_schedule.c +++ b/src/schedule/ll_schedule_xtos.c @@ -20,6 +20,7 @@ #include <sof/lib/uuid.h> #include <sof/list.h> #include <sof/platform.h> +#include <sof/schedule/dp_schedule.h> #include <sof/schedule/ll_schedule.h> #include <sof/schedule/ll_schedule_domain.h> #include <sof/schedule/schedule.h> @@ -317,8 +318,9 @@ static void schedule_ll_tasks_run(void *data) if (schedule_ll_is_pending(sch)) schedule_ll_tasks_execute(sch); - notifier_event(sch, NOTIFIER_ID_LL_POST_RUN, - NOTIFIER_TARGET_CORE_LOCAL, NULL, 0); +#ifdef CONFIG_ZEPHYR_DP_SCHEDULER + scheduler_dp_ll_tick(); +#endif perf_cnt_stamp(&sch->pcd, perf_ll_sched_trace, 0 /* ignored */); perf_cnt_average(&sch->pcd, perf_avg_ll_sched_trace, 0 /* ignored */); @@ -607,7 +609,7 @@ static int schedule_ll_task_after(void *data, struct task *task, uint64_t start, int schedule_task_init_ll(struct task *task, const struct sof_uuid_entry *uid, uint16_t type, - uint16_t priority, enum task_state (*run)(void *data), + int16_t priority, enum task_state (*run)(void *data), void *data, uint16_t core, uint32_t flags) { struct ll_task_pdata *ll_pdata; @@ -724,6 +726,7 @@ static int reschedule_ll_task(void *data, struct task *task, uint64_t start) } #endif +#if CONFIG_SOF_BOOT_TEST_STANDALONE || CONFIG_LIBRARY static void scheduler_free_ll(void *data, uint32_t flags) { struct ll_schedule_data *sch = data; @@ -739,6 +742,7 @@ static void scheduler_free_ll(void *data, uint32_t flags) irq_local_enable(irq_flags); } +#endif static void ll_scheduler_recalculate_tasks(struct ll_schedule_data *sch, struct clock_notify_data *clk_data) @@ -807,7 +811,8 @@ static const struct scheduler_ops schedule_ll_ops = { .schedule_task_free = schedule_ll_task_free, .schedule_task_cancel = schedule_ll_task_cancel, .reschedule_task = reschedule_ll_task, +#if CONFIG_SOF_BOOT_TEST_STANDALONE || CONFIG_LIBRARY .scheduler_free = scheduler_free_ll, - .scheduler_restore = NULL, +#endif .schedule_task_running = NULL, }; diff --git a/src/schedule/schedule.c b/src/schedule/schedule.c index 5e56b6c490e0..00ba29d1f9f0 100644 --- a/src/schedule/schedule.c +++ b/src/schedule/schedule.c @@ -22,16 +22,53 @@ SOF_DEFINE_REG_UUID(schedule); DECLARE_TR_CTX(sch_tr, SOF_UUID(schedule_uuid), LOG_LEVEL_INFO); +static inline bool scheduler_is_user(int type) +{ + /* + * currently only LL managed in user-space, but longterm + * goal is to move all audio application level scheduling + * to user-space and only keep Zephyr scheduler logic in + * kernel + */ + return type == SOF_SCHEDULE_LL_TIMER || type == SOF_SCHEDULE_DP; +} + int schedule_task_init(struct task *task, const struct sof_uuid_entry *uid, uint16_t type, - uint16_t priority, enum task_state (*run)(void *data), + int16_t priority, enum task_state (*run)(void *data), void *data, uint16_t core, uint32_t flags) { + struct schedulers *schedulers; + struct schedule_data *sch = NULL; + struct list_item *slist; + if (type >= SOF_SCHEDULE_COUNT) { tr_err(&sch_tr, "invalid task type"); return -EINVAL; } + if (IS_ENABLED(CONFIG_SOF_USERSPACE_LL) && scheduler_is_user(type)) + schedulers = *arch_user_schedulers_get_for_core(core); + else + schedulers = *arch_schedulers_get(); + + if (!schedulers) + return -ENODEV; + + task->sch = NULL; + list_for_item(slist, &schedulers->list) { + sch = container_of(slist, struct schedule_data, list); + if (type == sch->type) { + task->sch = sch; + break; + } + } + + if (!task->sch) { + tr_err(&sch_tr, "unable to bind scheduler to task %p (type %d)", task, type); + return -ENODEV; + } + task->uid = uid; task->type = type; task->priority = priority; @@ -44,37 +81,52 @@ int schedule_task_init(struct task *task, return 0; } -static void scheduler_register(struct schedule_data *scheduler) +__cold static void scheduler_register(struct schedule_data *scheduler) { struct schedulers **sch = arch_schedulers_get(); + struct k_heap *heap = NULL; + + assert_can_be_cold(); + + if (IS_ENABLED(CONFIG_SOF_USERSPACE_LL) && scheduler_is_user(scheduler->type)) { + sch = arch_user_schedulers_get(); + heap = sof_sys_user_heap_get(); + } if (!*sch) { /* init schedulers list */ - *sch = rzalloc(SOF_MEM_FLAG_KERNEL, - sizeof(**sch)); + *sch = sof_heap_alloc(heap, 0, sizeof(**sch), 0); if (!*sch) { tr_err(&sch_tr, "allocation failed"); return; } + memset(*sch, 0, sizeof(**sch)); list_init(&(*sch)->list); } list_item_append(&scheduler->list, &(*sch)->list); } -void scheduler_init(int type, const struct scheduler_ops *ops, void *data) +__cold void scheduler_init(int type, const struct scheduler_ops *ops, void *data) { struct schedule_data *sch; + struct k_heap *heap = NULL; + + assert_can_be_cold(); + + if (IS_ENABLED(CONFIG_SOF_USERSPACE_LL) && scheduler_is_user(type)) + heap = sof_sys_user_heap_get(); if (!ops || !ops->schedule_task || !ops->schedule_task_cancel || !ops->schedule_task_free) return; - sch = rzalloc(SOF_MEM_FLAG_KERNEL, sizeof(*sch)); + sch = sof_heap_alloc(heap, SOF_MEM_FLAG_KERNEL, sizeof(*sch), 0); if (!sch) { tr_err(&sch_tr, "allocation failed"); sof_panic(SOF_IPC_PANIC_IPC); } + memset(sch, 0, sizeof(*sch)); list_init(&sch->list); sch->type = type; sch->ops = ops; diff --git a/src/schedule/zephyr_dma_domain.c b/src/schedule/zephyr_dma_domain.c index 84165d363229..50901ef7b2af 100644 --- a/src/schedule/zephyr_dma_domain.c +++ b/src/schedule/zephyr_dma_domain.c @@ -461,8 +461,9 @@ static int zephyr_dma_domain_register(struct ll_schedule_domain *domain, 0, K_FOREVER); - k_thread_cpu_mask_clear(thread); - k_thread_cpu_mask_enable(thread, core); +#ifdef CONFIG_SCHED_CPU_MASK + k_thread_cpu_pin(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..b37f91285854 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(); @@ -112,7 +126,8 @@ static void zephyr_domain_thread_fn(void *p1, void *p2, void *p3) } #endif - dt->handler(dt->arg); + if (dt->handler) + dt->handler(dt->arg); #ifdef CONFIG_SCHEDULE_LL_STATS_LOG cycles1 = k_cycle_get_32(); @@ -120,7 +135,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 +178,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 +206,33 @@ 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); - k_thread_cpu_mask_clear(thread); - k_thread_cpu_mask_enable(thread, core); +#ifdef CONFIG_SCHED_CPU_MASK + k_thread_cpu_pin(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,268 @@ 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 */ + +/* + * Kernel-owned per-core LL thread table. Populated from the privileged + * domain-thread init path and consulted by z_impl_zephyr_ll_task_sem_alloc() + * so that privileged code never has to traverse the user-accessible + * scheduler/domain objects to find the LL thread of a given core. + */ +static struct k_thread *ll_thread_tid[CONFIG_CORE_COUNT]; + +/* + * Privileged thread initialization for userspace LL scheduling. + * Creates the scheduling thread, sets up timer, grants access to kernel + * objects. Must be called from kernel context before any user-space + * domain_register() calls. + */ +static int zephyr_domain_thread_init(struct ll_schedule_domain *domain, + struct task *task) +{ + struct zephyr_domain *zephyr_domain = ll_sch_domain_get_pdata(domain); + struct zephyr_domain_thread *dt; + char thread_name[] = "ll_thread0"; + k_tid_t thread; + int core = task->core; + + tr_dbg(&ll_tr, "entry"); + + if (core >= CONFIG_CORE_COUNT) + return -EINVAL; + + dt = zephyr_domain->domain_thread + core; + + /* thread only needs to be created once per core */ + if (dt->ll_thread) + return 0; + + dt->handler = NULL; + dt->arg = NULL; + + dt->sem = k_object_alloc(K_OBJ_SEM); + if (!dt->sem) { + tr_err(&ll_tr, "Failed to allocate semaphore for core %d", core); + return -ENOMEM; + } + + /* 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; + + /* 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); + k_object_free(dt->sem); + dt->sem = 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); + + /* record in the kernel-only table for syscall-context lookups */ + ll_thread_tid[core] = dt->ll_thread; + +#ifdef CONFIG_SCHED_CPU_MASK + k_thread_cpu_pin(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; +} + +/* + * User-space register: bookkeeping only. The privileged thread setup has + * already been done by domain_thread_init() called from kernel context. + */ +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); + struct zephyr_domain_thread *dt; + int core; + + tr_dbg(&ll_tr, "register_user entry"); + + if (task->core >= CONFIG_CORE_COUNT) + return -EINVAL; + + core = task->core; + dt = zephyr_domain->domain_thread + core; + + if (!dt->ll_thread) { + tr_err(&ll_tr, "domain_thread_init() not called for core %d", core); + return -EINVAL; + } + + __ASSERT_NO_MSG(!dt->handler || dt->handler == handler); + if (dt->handler) + return 0; + + dt->handler = handler; + dt->arg = arg; + + tr_info(&ll_tr, "task registered on core %d", core); + + 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); + + zephyr_domain->domain_thread[core].handler = NULL; + + k_mutex_unlock(domain->lock); + + /* + * In this user thread implementation, the timer is left + * running until privileged domain_thread_free() is called + * to clean up resources. + */ + + tr_dbg(&ll_tr, "exit"); return 0; } +/* + * Free resources acquired by zephyr_domain_thread_init(). + * Stops the timer, aborts the scheduling thread and frees the thread object. + * Must be called from kernel context. + */ +static void zephyr_domain_thread_free(struct ll_schedule_domain *domain, + uint32_t num_tasks) +{ + 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, "thread_free entry, core %d, num_tasks %u", core, num_tasks); + + /* Still tasks on other cores, only clean up this core's thread */ + k_mutex_lock(domain->lock, K_FOREVER); + + if (!num_tasks && !atomic_read(&domain->total_num_tasks)) { + /* Last task globally: stop the timer and watchdog */ + watchdog_disable(core); + + k_timer_stop(zephyr_domain->timer); + k_timer_user_data_set(zephyr_domain->timer, NULL); + } + + dt->handler = NULL; + dt->arg = NULL; + + k_mutex_unlock(domain->lock); + + if (dt->ll_thread) { + k_thread_abort(dt->ll_thread); + k_object_free(dt->ll_thread); + dt->ll_thread = NULL; + ll_thread_tid[core] = NULL; + } + + if (dt->sem) { + k_object_free(dt->sem); + dt->sem = NULL; + } + + tr_info(&ll_tr, "thread_free done, core %d", core); +} + +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; +} + +/* + * Return the LL scheduling thread for an explicitly given core. + * + * Reads a kernel-only table keyed by core, so it is safe to call from a + * privileged syscall context without dereferencing any user-accessible + * scheduler or domain object, and without relying on cpu_get_id(). + */ +struct k_thread *zephyr_domain_thread_tid_for_core(int core) +{ + if (core < 0 || core >= CONFIG_CORE_COUNT) + return NULL; + + return ll_thread_tid[core]; +} + +#endif /* CONFIG_SOF_USERSPACE_LL */ + #if CONFIG_CROSS_CORE_STREAM static void zephyr_domain_block(struct ll_schedule_domain *domain) { @@ -290,19 +552,28 @@ 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, + .domain_thread_init = zephyr_domain_thread_init, + .domain_thread_free = zephyr_domain_thread_free, +#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, #endif }; +/* Core 0 only */ struct ll_schedule_domain *zephyr_domain_init(int clk) { struct ll_schedule_domain *domain; struct zephyr_domain *zephyr_domain; + int core; domain = domain_init(SOF_SCHEDULE_LL_TIMER, clk, false, &zephyr_domain_ops); @@ -311,16 +582,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,19 +621,29 @@ 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++) { +#ifndef CONFIG_SOF_USERSPACE_LL + struct zephyr_domain_thread *dt = zephyr_domain->domain_thread + core; + + /* not allocated dynamically when LL in kernel space */ + dt->sem = &dt->sem_obj; +#endif + } + return domain; } /* Check if currently running in the LL scheduler thread context */ bool ll_sch_is_current(void) { - struct zephyr_domain *zephyr_domain = ll_sch_domain_get_pdata(zephyr_ll_domain()); + struct ll_schedule_domain *ll_dom = zephyr_ll_domain(); + struct zephyr_domain *zephyr_domain = ll_dom ? ll_sch_domain_get_pdata(ll_dom) : NULL; if (!zephyr_domain) return false; 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..fa8bbd285791 100644 --- a/src/schedule/zephyr_dp_schedule.c +++ b/src/schedule/zephyr_dp_schedule.c @@ -7,9 +7,9 @@ #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> #include <sof/schedule/dp_schedule.h> #include <sof/schedule/ll_schedule.h> #include <sof/schedule/ll_schedule_domain.h> @@ -20,39 +20,21 @@ #include <zephyr/sys_clock.h> #include <zephyr/sys/sem.h> #include <zephyr/sys/mutex.h> -#include <sof/lib/notifier.h> +#include <sof/lib/memory.h> #include <ipc4/base_fw.h> +#include "zephyr_dp_schedule.h" + #include <zephyr/kernel/thread.h> +#include <stdint.h> +#include <string.h> + LOG_MODULE_REGISTER(dp_schedule, CONFIG_SOF_LOG_LEVEL); 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 +45,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 +223,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 scheduler_dp_ll_tick(void) { - (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); + if (!dp_sch) + return; + + /* 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); 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()); + lock_key = scheduler_dp_lock(task->core); 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 +282,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 +292,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; -} + scheduler_dp_internal_free(task); -/* 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); - - /* 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,10 +307,14 @@ 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()); + lock_key = scheduler_dp_lock(task->core); + + 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 && @@ -442,39 +323,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,159 +331,44 @@ 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 = { +APP_SYSUSER_DATA 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, }; -int scheduler_dp_init(void) +/* Runs on each core */ +__cold int scheduler_dp_init(void) { - int ret; - struct scheduler_dp_data *dp_sch = rzalloc(SOF_MEM_FLAG_KERNEL, - sizeof(struct scheduler_dp_data)); + assert_can_be_cold(); + + struct scheduler_dp_data *dp_sch = sof_heap_alloc(sof_sys_user_heap_get(), + SOF_MEM_FLAG_KERNEL, sizeof(*dp_sch), 0); if (!dp_sch) return -ENOMEM; + memset(dp_sch, 0, sizeof(*dp_sch)); list_init(&dp_sch->tasks); scheduler_init(SOF_SCHEDULE_DP, &schedule_dp_ops, dp_sch); /* init src of DP tick */ - ret = schedule_task_init_ll(&dp_sch->ll_tick_src, - SOF_UUID(dp_sched_uuid), - SOF_SCHEDULE_LL_TIMER, - 0, scheduler_dp_ll_tick_dummy, dp_sch, - cpu_get_id(), 0); - - if (ret) - return ret; - - notifier_register(NULL, NULL, NOTIFIER_ID_LL_POST_RUN, scheduler_dp_ll_tick, 0); - - 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; + return schedule_task_init_ll(&dp_sch->ll_tick_src, + SOF_UUID(dp_sched_uuid), + SOF_SCHEDULE_LL_TIMER, + 0, scheduler_dp_ll_tick_dummy, dp_sch, + cpu_get_id(), 0); } void scheduler_get_task_info_dp(struct scheduler_props *scheduler_props, uint32_t *data_off_size) diff --git a/src/schedule/zephyr_dp_schedule.h b/src/schedule/zephyr_dp_schedule.h new file mode 100644 index 000000000000..694bb541f87e --- /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); +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..547823ca6e6f --- /dev/null +++ b/src/schedule/zephyr_dp_schedule_application.c @@ -0,0 +1,618 @@ +// 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 <stdio.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) +{ + 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 && 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)); +} + +#ifdef CONFIG_THREAD_NAME +static void scheduler_dp_thread_name_set(k_tid_t thread_id, struct processing_module *mod) +{ + char name[CONFIG_THREAD_MAX_NAME_LEN]; + + snprintf(name, sizeof(name), "DP:%#x", mod->dev->ipc_config.id); + + k_thread_name_set(thread_id, name); +} +#else +/* k_thread_name_set() is a no-op so skip constructing a thread name */ +#define scheduler_dp_thread_name_set(x, y) +#endif + +/* Called only in IPC context in kernel mode (this can change) */ +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); + assert(!k_is_user_context()); + + /* + * 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); + scheduler_dp_thread_name_set(pdata->thread_id, mod); + + unsigned int pidx; + size_t size; + uintptr_t start; + bool on_pool = false; + +#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); + + 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..f2c2434bc2b5 --- /dev/null +++ b/src/schedule/zephyr_dp_schedule_thread.c @@ -0,0 +1,356 @@ +// 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() + */ +static void scheduler_dp_recalculate_thread(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); + } + } + } +} + +void scheduler_dp_recalculate(struct scheduler_dp_data *dp_sch) +{ + scheduler_dp_recalculate_thread(dp_sch, true); +} + +/* 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_thread(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..8e55695538ec 100644 --- a/src/schedule/zephyr_ll.c +++ b/src/schedule/zephyr_ll.c @@ -9,7 +9,8 @@ #include <rtos/symbol.h> #include <sof/audio/component.h> #include <rtos/interrupt.h> -#include <sof/lib/notifier.h> +#include <sof/lib/memory.h> +#include <sof/schedule/dp_schedule.h> #include <sof/schedule/ll_schedule_domain.h> #include <sof/schedule/schedule.h> #include <rtos/task.h> @@ -30,28 +31,92 @@ 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 */ struct zephyr_ll_pdata { bool run; bool freeing; + struct k_sem *sem_p; +#if !CONFIG_DYNAMIC_OBJECTS struct k_sem sem; +#endif }; +#if CONFIG_SOF_USERSPACE_LL +/* + * Mutex pointer in user-accessible partition so user-space threads + * can read the pointer for syscalls. The actual lock object resides + * in kernel memory, there are just handles! + */ +static APP_SYSUSER_BSS struct k_mutex *zephyr_ll_locks[CONFIG_CORE_COUNT]; + +/* + * Shadow ownership tracking for the per-core LL mutex, used only to + * power user_ll_assert_locked(). The kernel k_mutex object is not + * readable from user-space threads, so its owner cannot be inspected + * directly; instead record the owner and recursion depth here, in a + * user-accessible partition. Updated on every lock/unlock of the LL + * mutex, by both the LL thread (zephyr_ll_lock()) and IPC handlers + * (user_ll_lock_sched()). Only ever mutated by the current lock holder + * while the lock is held, so no extra synchronization is required. + */ +#ifdef CONFIG_ASSERT +static APP_SYSUSER_BSS k_tid_t zephyr_ll_lock_owner[CONFIG_CORE_COUNT]; +static APP_SYSUSER_BSS uint32_t zephyr_ll_lock_depth[CONFIG_CORE_COUNT]; + +static inline void zephyr_ll_lock_acquired(int core) +{ + zephyr_ll_lock_owner[core] = k_current_get(); + zephyr_ll_lock_depth[core]++; +} + +static inline void zephyr_ll_lock_releasing(int core) +{ + assert(zephyr_ll_lock_owner[core] == k_current_get()); + if (--zephyr_ll_lock_depth[core] == 0) + zephyr_ll_lock_owner[core] = NULL; +} + +void user_ll_assert_locked(int core) +{ + assert(core < CONFIG_CORE_COUNT && + zephyr_ll_lock_owner[core] == k_current_get()); +} +#else +static inline void zephyr_ll_lock_acquired(int core) { (void)core; } +static inline void zephyr_ll_lock_releasing(int core) { (void)core; } +#endif /* CONFIG_ASSERT */ +#endif + static void zephyr_ll_lock(struct zephyr_ll *sch, uint32_t *flags) { +#if CONFIG_SOF_USERSPACE_LL + k_mutex_lock(sch->lock, K_FOREVER); + zephyr_ll_lock_acquired(sch->core); +#else irq_local_disable(*flags); +#endif } static void zephyr_ll_unlock(struct zephyr_ll *sch, uint32_t *flags) { +#if CONFIG_SOF_USERSPACE_LL + zephyr_ll_lock_releasing(sch->core); + 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 */ @@ -74,7 +139,7 @@ static void zephyr_ll_task_done(struct zephyr_ll *sch, * zephyr_ll_task_free() is trying to free this task. Complete * it and signal the semaphore to let the function proceed */ - k_sem_give(&pdata->sem); + k_sem_give(pdata->sem_p); tr_info(&ll_tr, "task complete %p %pU", task, task->uid); tr_info(&ll_tr, "num_tasks %d total_num_tasks %ld", @@ -98,8 +163,8 @@ static void zephyr_ll_task_insert_unlocked(struct zephyr_ll *sch, struct task *t /* * Tasks are added into the list in priority order. List order - * defines schedule order. Priority 0 indicates highest - * priority and is run first. Tasks with the same priority are + * defines schedule order. Lower values indicate higher priority + * and run first. Tasks with the same priority are * served on a first-come-first-served basis. */ list_for_item(list, &sch->tasks) { @@ -176,6 +241,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); /* @@ -206,7 +273,15 @@ static void zephyr_ll_run(void *data) list_item_del(list); list_item_append(list, &task_head); + /* in user-space LL builds, the LL lock protects LL + * tasks against IPC thread, so the lock must be held + * while running the tasks. + * in kernel LL builds, IPC thread blocks interrupts for + * critical section, so lock can be freed here. + */ +#ifndef CONFIG_SOF_USERSPACE_LL zephyr_ll_unlock(sch, &flags); +#endif /* * task's .run() should only return either @@ -216,12 +291,14 @@ 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; } +#ifndef CONFIG_SOF_USERSPACE_LL zephyr_ll_lock(sch, &flags); +#endif if (pdata->freeing || state == SOF_TASK_STATE_COMPLETED) { zephyr_ll_task_done(sch, task); @@ -248,8 +325,9 @@ static void zephyr_ll_run(void *data) zephyr_ll_unlock(sch, &flags); - notifier_event(sch, NOTIFIER_ID_LL_POST_RUN, - NOTIFIER_TARGET_CORE_LOCAL, NULL, 0); +#ifdef CONFIG_ZEPHYR_DP_SCHEDULER + scheduler_dp_ll_tick(); +#endif } static void schedule_ll_callback(void *data) @@ -342,8 +420,7 @@ static int zephyr_ll_task_schedule_common(struct zephyr_ll *sch, struct task *ta ret = domain_register(sch->ll_domain, task, &schedule_ll_callback, sch); if (ret < 0) - tr_err(&ll_tr, "cannot register domain %d", - ret); + tr_err(&ll_tr, "cannot register domain %d", ret); return 0; } @@ -372,6 +449,117 @@ static int zephyr_ll_task_schedule_after(void *data, struct task *task, uint64_t return zephyr_ll_task_schedule_common(sch, task, start, period, after, false); } +#if CONFIG_DYNAMIC_OBJECTS +static struct list_item zephyr_ll_task_sem_list = LIST_INIT(zephyr_ll_task_sem_list); + +struct zephyr_ll_task_sem { + struct task *task; + struct k_sem *sem; + struct list_item list; +}; + +int z_impl_zephyr_ll_task_sem_alloc(struct task *task) +{ + struct zephyr_ll_pdata *pdata = task->priv_data; + struct zephyr_ll_task_sem *ts = rmalloc(SOF_MEM_FLAG_COHERENT, sizeof(*ts)); + + if (!ts) + return -ENOMEM; + + ts->sem = k_object_alloc(K_OBJ_SEM); + if (!ts->sem) { + rfree(ts); + return -ENOMEM; + } + + k_sem_init(ts->sem, 0, 1); + +#if CONFIG_SOF_USERSPACE_LL + /* + * The per-task semaphore is signalled from zephyr_ll_task_done(), + * which runs in the (unprivileged) LL scheduler thread when a task is + * freed while it is still running. k_object_alloc() only grants access + * to the calling thread (the IPC handler that creates the task), so the + * LL thread must be granted access explicitly, otherwise its + * k_sem_give() traps with a userspace permission fault. + * + * Resolve the LL thread from kernel-only per-core state keyed by the + * task's target core; never traverse the user-accessible scheduler or + * domain objects from privileged context. + */ + struct k_thread *ll_tid = zephyr_domain_thread_tid_for_core(task->core); + + if (ll_tid) + k_thread_access_grant(ll_tid, ts->sem); +#endif + + ts->task = task; + pdata->sem_p = ts->sem; + /* List is protected by IPC serialization */ + list_item_append(&ts->list, &zephyr_ll_task_sem_list); + + return 0; +} + +int z_impl_zephyr_ll_task_sem_free(struct task *task) +{ + struct zephyr_ll_pdata *pdata = task->priv_data; + struct list_item *list; + struct zephyr_ll_task_sem *ts; + bool found = false; + + /* List is protected by IPC serialization */ + list_for_item(list, &zephyr_ll_task_sem_list) { + ts = container_of(list, struct zephyr_ll_task_sem, list); + if (ts->task == task) { + found = true; + break; + } + } + + if (!found) + return -ENOENT; + + if (pdata->sem_p != ts->sem) + return -EINVAL; + + list_item_del(list); + k_object_free(ts->sem); + rfree(ts); + + return 0; +} + +#ifdef CONFIG_USERSPACE +#include <zephyr/internal/syscall_handler.h> +static inline int z_vrfy_zephyr_ll_task_sem_alloc(struct task *task) +{ + if (!task) + return -EINVAL; + K_OOPS(K_SYSCALL_MEMORY_WRITE(task, sizeof(*task))); + if (!task->priv_data) + return -EINVAL; + K_OOPS(K_SYSCALL_MEMORY_WRITE(task->priv_data, sizeof(struct zephyr_ll_pdata))); + + return z_impl_zephyr_ll_task_sem_alloc(task); +} +#include <zephyr/syscalls/zephyr_ll_task_sem_alloc_mrsh.c> + +static inline int z_vrfy_zephyr_ll_task_sem_free(struct task *task) +{ + if (!task) + return -EINVAL; + K_OOPS(K_SYSCALL_MEMORY_WRITE(task, sizeof(*task))); + if (!task->priv_data) + return -EINVAL; + K_OOPS(K_SYSCALL_MEMORY_WRITE(task->priv_data, sizeof(struct zephyr_ll_pdata))); + + return z_impl_zephyr_ll_task_sem_free(task); +} +#include <zephyr/syscalls/zephyr_ll_task_sem_free_mrsh.c> +#endif +#endif + /* * This is synchronous - after this returns the object can be destroyed! * Assertion: under Zephyr this is always called from a thread context! @@ -385,10 +573,12 @@ static int zephyr_ll_task_free(void *data, struct task *task) zephyr_ll_assert_core(sch); +#ifndef CONFIG_SOF_USERSPACE_LL if (k_is_in_isr()) { tr_err(&ll_tr, "cannot free tasks from interrupt context!"); return -EDEADLK; } +#endif zephyr_ll_lock(sch, &flags); @@ -427,12 +617,15 @@ static int zephyr_ll_task_free(void *data, struct task *task) if (must_wait) /* Wait for up to 100 periods */ - k_sem_take(&pdata->sem, K_USEC(LL_TIMER_PERIOD_US * 100)); + k_sem_take(pdata->sem_p, K_USEC(LL_TIMER_PERIOD_US * 100)); /* Protect against racing with schedule_task() */ zephyr_ll_lock(sch, &flags); +#if CONFIG_DYNAMIC_OBJECTS + zephyr_ll_task_sem_free(task); +#endif task->priv_data = NULL; - rfree(pdata); + sof_heap_free(sch->heap, pdata); zephyr_ll_unlock(sch, &flags); return 0; @@ -468,21 +661,58 @@ static int zephyr_ll_task_cancel(void *data, struct task *task) return 0; } -/* - * Runs on secondary cores in their shutdown sequence. In theory tasks can still - * be active, but other schedulers ignore them too... And we don't need to free - * the scheduler data - it's allocated in the SYS zone. - */ +#if CONFIG_SOF_BOOT_TEST_STANDALONE || CONFIG_LIBRARY static void zephyr_ll_scheduler_free(void *data, uint32_t flags) { struct zephyr_ll *sch = data; zephyr_ll_assert_core(sch); +#if CONFIG_SOF_USERSPACE_LL + zephyr_ll_locks[sch->core] = NULL; + k_object_free(sch->lock); +#endif + if (sch->n_tasks) tr_err(&ll_tr, "%u tasks are still active!", sch->n_tasks); + +#if CONFIG_SOF_USERSPACE_LL + domain_thread_free(sch->ll_domain, sch->n_tasks); +#endif + sof_heap_free(sch->heap, sch); } +#endif + +#if CONFIG_SOF_USERSPACE_LL +struct k_thread *zephyr_ll_init_context(void *data, struct task *task) +{ + struct zephyr_ll *sch = data; + struct zephyr_ll_pdata *pdata = task->priv_data; + int ret; + + /* + * Use domain_thread_init() for privileged setup (thread creation, + * timer, access grants). domain_register() is now bookkeeping only + * and will be called later from user context when scheduling tasks. + */ + ret = domain_thread_init(sch->ll_domain, task); + if (ret < 0) { + tr_err(&ll_tr, "cannot init_context %d", ret); + return NULL; + } + + assert(!k_is_user_context()); + k_thread_access_grant(zephyr_domain_thread_tid(sch->ll_domain), sch->lock, pdata->sem_p); + + 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)); + + return zephyr_domain_thread_tid(sch->ll_domain); +} +#endif static const struct scheduler_ops zephyr_ll_ops = { .schedule_task = zephyr_ll_task_schedule, @@ -490,56 +720,165 @@ static const struct scheduler_ops zephyr_ll_ops = { .schedule_task_after = zephyr_ll_task_schedule_after, .schedule_task_free = zephyr_ll_task_free, .schedule_task_cancel = zephyr_ll_task_cancel, +#if CONFIG_SOF_BOOT_TEST_STANDALONE || CONFIG_LIBRARY .scheduler_free = zephyr_ll_scheduler_free, +#endif +#if CONFIG_SOF_USERSPACE_LL + .scheduler_init_context = zephyr_ll_init_context, +#endif }; +#if CONFIG_SOF_USERSPACE_LL +struct task *zephyr_ll_task_alloc(void) +{ + struct task *task = sof_heap_alloc(zephyr_ll_user_heap(), SOF_MEM_FLAG_USER, + sizeof(*task), sizeof(void *)); + + if (task) + /* At least .priv_data must be NULL for zephyr_ll_task_init() */ + memset(task, 0, sizeof(*task)); + + return task; +} + +void user_ll_grant_access(struct k_thread *thread, int core) +{ + assert(core < CONFIG_CORE_COUNT && zephyr_ll_locks[core] != NULL); + + k_thread_access_grant(thread, zephyr_ll_locks[core]); +} + +/** + * Lock the LL scheduler to prevent it from processing tasks. + * + * Uses the LL scheduler's own k_mutex which is re-entrant, so + * schedule_task() calls within the locked section will not deadlock. + * Must be paired with user_ll_unlock_sched(). + */ +void user_ll_lock_sched(int core) +{ + assert(core >= 0 && core < CONFIG_CORE_COUNT && zephyr_ll_locks[core] != NULL); + int __maybe_unused ret = k_mutex_lock(zephyr_ll_locks[core], K_FOREVER); + assert(!ret); + zephyr_ll_lock_acquired(core); +} + +/** + * Unlock the LL scheduler after a previous user_ll_lock_sched() call. + */ +void user_ll_unlock_sched(int core) +{ + assert(core >= 0 && core < CONFIG_CORE_COUNT && zephyr_ll_locks[core] != NULL); + zephyr_ll_lock_releasing(core); + int __maybe_unused ret = k_mutex_unlock(zephyr_ll_locks[core]); + assert(!ret); +} + +#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), + int16_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; + /* + * schedule_task_init() must be run on target core, see + * sof/zephyr/schedule.c:arch_schedulers_get() + */ + assert(cpu_get_id() == core); + ret = schedule_task_init(task, uid, type, priority, run, data, core, flags); 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; } - k_sem_init(&pdata->sem, 0, 1); + memset(pdata, 0, sizeof(*pdata)); task->priv_data = pdata; +#if CONFIG_DYNAMIC_OBJECTS + ret = zephyr_ll_task_sem_alloc(task); + if (ret < 0) { + sof_heap_free(heap, pdata); + task->priv_data = NULL; + return ret; + } +#else + pdata->sem_p = &pdata->sem; +#endif + + k_sem_init(pdata->sem_p, 0, 1); + return 0; } EXPORT_SYMBOL(zephyr_ll_task_init); /* TODO: low-power mode clock support */ /* Runs on each core during initialisation with the same domain argument */ -int zephyr_ll_scheduler_init(struct ll_schedule_domain *domain) +__cold 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; + + assert_can_be_cold(); + +#if CONFIG_SOF_USERSPACE_LL + heap = zephyr_ll_user_heap(); + flags = SOF_MEM_FLAG_USER | SOF_MEM_FLAG_COHERENT; +#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; + } + assert(core < CONFIG_CORE_COUNT && zephyr_ll_locks[core] == NULL); + zephyr_ll_locks[core] = sch->lock; + 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); @@ -550,9 +889,13 @@ void scheduler_get_task_info_ll(struct scheduler_props *scheduler_props, uint32_t *data_off_size) { uint32_t flags; +#if CONFIG_SOF_USERSPACE_LL + struct zephyr_ll *ll_sch = scheduler_get_user_data(SOF_SCHEDULE_LL_TIMER); +#else + struct zephyr_ll *ll_sch = scheduler_get_data(SOF_SCHEDULE_LL_TIMER); +#endif scheduler_props->processing_domain = COMP_PROCESSING_DOMAIN_LL; - struct zephyr_ll *ll_sch = (struct zephyr_ll *)scheduler_get_data(SOF_SCHEDULE_LL_TIMER); zephyr_ll_lock(ll_sch, &flags); scheduler_get_task_info(scheduler_props, data_off_size, &ll_sch->tasks); @@ -562,7 +905,11 @@ void scheduler_get_task_info_ll(struct scheduler_props *scheduler_props, /* Return a pointer to the LL scheduler timer domain */ struct ll_schedule_domain *zephyr_ll_domain(void) { +#if CONFIG_SOF_USERSPACE_LL + struct zephyr_ll *ll_sch = scheduler_get_user_data(SOF_SCHEDULE_LL_TIMER); +#else struct zephyr_ll *ll_sch = scheduler_get_data(SOF_SCHEDULE_LL_TIMER); +#endif - return ll_sch->ll_domain; + return ll_sch ? ll_sch->ll_domain : NULL; } diff --git a/src/schedule/zephyr_ll_user.c b/src/schedule/zephyr_ll_user.c new file mode 100644 index 000000000000..bad2f6c9cbb6 --- /dev/null +++ b/src/schedule/zephyr_ll_user.c @@ -0,0 +1,146 @@ +// 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. Only kernel accessible. + */ +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; + +/** + * Heap allocator for LL scheduler memory (user accessible pointer) + * + * Note: this is also user-writable, so kernel must not rely on this to + * be correct and must always validate it separately. + */ +APP_SYSUSER_DATA static struct k_heap *zephyr_ll_heap; + +static struct k_heap *ll_heap_alloc(void) +{ + const size_t alloc_size = CONFIG_SOF_ZEPHYR_SYS_USER_HEAP_SIZE; + + BUILD_ASSERT(CONFIG_SOF_ZEPHYR_SYS_USER_HEAP_SIZE % CONFIG_MM_DRV_PAGE_SIZE == 0); + + void *mem = rballoc_align(SOF_MEM_FLAG_USER | SOF_MEM_FLAG_COHERENT, alloc_size, + CONFIG_MM_DRV_PAGE_SIZE); + if (!mem) + return NULL; + + k_heap_init(&ll_mem_resources.heap, mem, alloc_size); + + /* + * k_heap_init() does not set these, so set the values + * manually here + */ + ll_mem_resources.heap.heap.init_mem = mem; + ll_mem_resources.heap.heap.init_bytes = alloc_size; + + return &ll_mem_resources.heap; +} + +static void ll_heap_init(void) +{ + struct k_heap *heap = ll_heap_alloc(); + struct k_mem_partition mem_partition; + int ret; + + 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(); + +#ifdef CONFIG_CACHE_HAS_MIRRORED_MEMORY_REGIONS + 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(); +#endif +} + +void zephyr_ll_user_resources_init(void) +{ + int ret; + + k_mem_domain_init(&ll_mem_resources.mem_domain, 0, NULL); + + ll_heap_init(); + + /* store a user-accessible pointer */ + zephyr_ll_heap = &ll_mem_resources.heap; + + /* attach common partition to LL domain */ + user_memory_attach_common_partition(zephyr_ll_mem_domain()); + + ret = user_memory_attach_system_user_partition(zephyr_ll_mem_domain()); + if (ret) + k_panic(); +} + +/** + * Check if 'heap' is a valid heap pointer. + * + * Available only in kernel mode. + * + * @return true if valid + */ +bool zephyr_ll_user_heap_verify(struct k_heap *heap) +{ + return heap == &ll_mem_resources.heap; +} + +/** + * Returns heap object to use in user-space LL code. + * + * Can be called from user-space. + * + * @return heap pointer that can be passed to sof_heap_alloc() + */ +struct k_heap *zephyr_ll_user_heap(void) +{ + return zephyr_ll_heap; +} + +/** + * Returns pointer to LL user-space memory domain. + * + * Available only in kernel mode. + * + * @return pointer to memory domain + */ +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..5e85cbf61134 100644 --- a/src/schedule/zephyr_twb_schedule.c +++ b/src/schedule/zephyr_twb_schedule.c @@ -7,6 +7,7 @@ #include <rtos/task.h> #include <stdint.h> +#include <sof/lib/memory.h> #include <sof/lib/uuid.h> #include <sof/schedule/schedule.h> #include <sof/schedule/ll_schedule.h> @@ -348,12 +349,14 @@ static struct scheduler_ops schedule_twb_ops = { .schedule_task_free = scheduler_twb_task_free, }; -int scheduler_twb_init(void) +__cold int scheduler_twb_init(void) { struct scheduler_twb_data *twb_sch = rzalloc(SOF_MEM_FLAG_KERNEL, sizeof(struct scheduler_twb_data)); int ret; + assert_can_be_cold(); + if (!twb_sch) return -ENOMEM; @@ -441,6 +444,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 +452,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..bfa359b4ba74 100644 --- a/src/trace/dma-trace.c +++ b/src/trace/dma-trace.c @@ -30,7 +30,7 @@ #include <sof_versions.h> #ifdef __ZEPHYR__ -#include <version.h> +#include <zephyr/version.h> #endif #include <errno.h> @@ -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/CMakeLists.txt b/test/cmocka/src/audio/CMakeLists.txt index d199fd18a0ea..a5b300b6f232 100644 --- a/test/cmocka/src/audio/CMakeLists.txt +++ b/test/cmocka/src/audio/CMakeLists.txt @@ -25,3 +25,6 @@ endif() if(CONFIG_COMP_DRC) add_subdirectory(drc) endif() +if(CONFIG_COMP_DCBLOCK) + add_subdirectory(dcblock) +endif() 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/dcblock/CMakeLists.txt b/test/cmocka/src/audio/dcblock/CMakeLists.txt new file mode 100644 index 000000000000..494d59217f7d --- /dev/null +++ b/test/cmocka/src/audio/dcblock/CMakeLists.txt @@ -0,0 +1,57 @@ +# SPDX-License-Identifier: BSD-3-Clause + +cmocka_test(dcblock_math_test + dcblock_math_test.c + ${PROJECT_SOURCE_DIR}/src/audio/dcblock/dcblock_generic.c + ${PROJECT_SOURCE_DIR}/src/math/numbers.c +) + +target_include_directories(dcblock_math_test PRIVATE ${PROJECT_SOURCE_DIR}/src/audio) +target_include_directories(dcblock_math_test PRIVATE ${PROJECT_SOURCE_DIR}/src/audio/dcblock) + +cmocka_test(dcblock_process + dcblock_process.c +) + +target_include_directories(dcblock_process PRIVATE ${PROJECT_SOURCE_DIR}/src/audio) +target_include_directories(dcblock_process PRIVATE ${PROJECT_SOURCE_DIR}/src/audio/dcblock) + +# make small version of libaudio so we don't have to care +# about unused missing references + +add_compile_options(-DUNIT_TEST) + +add_library(audio_for_dcblock STATIC + ${PROJECT_SOURCE_DIR}/src/audio/dcblock/dcblock.c + ${PROJECT_SOURCE_DIR}/src/audio/dcblock/dcblock_generic.c + ${PROJECT_SOURCE_DIR}/src/audio/dcblock/dcblock_ipc3.c + ${PROJECT_SOURCE_DIR}/src/math/numbers.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 + ${PROJECT_SOURCE_DIR}/src/audio/buffers/comp_buffer.c + ${PROJECT_SOURCE_DIR}/src/audio/buffers/audio_buffer.c + ${PROJECT_SOURCE_DIR}/src/audio/source_api_helper.c + ${PROJECT_SOURCE_DIR}/src/audio/sink_api_helper.c + ${PROJECT_SOURCE_DIR}/src/audio/sink_source_utils.c + ${PROJECT_SOURCE_DIR}/src/audio/audio_stream.c + ${PROJECT_SOURCE_DIR}/src/audio/component.c + ${PROJECT_SOURCE_DIR}/src/audio/data_blob.c + ${PROJECT_SOURCE_DIR}/src/module/audio/source_api.c + ${PROJECT_SOURCE_DIR}/src/module/audio/sink_api.c + ${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 + ${PROJECT_SOURCE_DIR}/src/audio/pipeline/pipeline-schedule.c + ${PROJECT_SOURCE_DIR}/src/audio/pipeline/pipeline-stream.c + ${PROJECT_SOURCE_DIR}/src/audio/pipeline/pipeline-xrun.c +) +sof_append_relative_path_definitions(audio_for_dcblock) + +target_link_libraries(audio_for_dcblock PRIVATE sof_options) + +target_link_libraries(dcblock_process PRIVATE audio_for_dcblock) diff --git a/test/cmocka/src/audio/dcblock/dcblock_math_test.c b/test/cmocka/src/audio/dcblock/dcblock_math_test.c new file mode 100644 index 000000000000..d41288c231ad --- /dev/null +++ b/test/cmocka/src/audio/dcblock/dcblock_math_test.c @@ -0,0 +1,345 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// Copyright(c) 2026 Intel Corporation. + +#include <math.h> +#include <stdarg.h> +#include <stddef.h> +#include <setjmp.h> +#include <stdint.h> +#include <stdio.h> +#include <string.h> +#include <cmocka.h> + +#include <sof/common.h> +#include <sof/audio/format.h> +#include <sof/audio/audio_stream.h> + +#include "dcblock.h" + +#define TEST_CHANNELS 2 +#define TEST_FRAMES 256 + +/* + * The dcblock processing functions operate on struct cir_buf_source / + * struct cir_buf_sink circular buffer views. The tests use static, non + * wrapping sample buffers, so the views simply span the whole array. + */ +static void cir_buf_src_setup(struct cir_buf_source *s, const void *data, size_t bytes) +{ + s->buf_start = data; + s->buf_end = (const char *)data + bytes; + s->ptr = data; +} + +static void cir_buf_snk_setup(struct cir_buf_sink *s, void *data, size_t bytes) +{ + s->buf_start = data; + s->buf_end = (char *)data + bytes; + s->ptr = data; +} + +/* Q2.30 coefficient close to 1.0 used for the DC removal test cases. */ +#define R_COEF_NEAR_ONE 1063004406 /* ~0.99 in Q2.30 */ + +/* + * Reference DC blocking filter implemented in floating point. Mirrors the + * fixed point recurrence y[n] = x[n] - x[n-1] + R * y[n-1] where R is the + * Q2.30 coefficient converted to a real number. + */ +struct ref_state { + double x_prev; + double y_prev; +}; + +static double dcblock_ref(struct ref_state *s, double r, double x) +{ + double y = x - s->x_prev + r * s->y_prev; + + s->x_prev = x; + s->y_prev = y; + return y; +} + +/* Fill the source buffer with a per-channel sinusoid plus a DC offset. */ +static void gen_input(double *ref_in, int channels, int frames, double dc) +{ + int ch, i; + + for (i = 0; i < frames; i++) { + for (ch = 0; ch < channels; ch++) { + double phase = 2.0 * M_PI * (i + 1) * (ch + 1) / 64.0; + + ref_in[i * channels + ch] = dc + 0.3 * sin(phase); + } + } +} + +/* + * Runs the S32 processing function and compares it to the floating point + * reference. Returns the mean absolute value of the last quarter of the + * output which is used to check DC convergence. + */ +static double run_s32_case(int32_t r_coeff, double dc, double tol_rel) +{ + struct comp_data cd; + struct cir_buf_source csrc; + struct cir_buf_sink csnk; + int32_t src[TEST_FRAMES * TEST_CHANNELS]; + int32_t dst[TEST_FRAMES * TEST_CHANNELS]; + double ref_in[TEST_FRAMES * TEST_CHANNELS]; + struct ref_state rstate[TEST_CHANNELS]; + double r = (double)r_coeff / (double)ONE_Q2_30; + double tail_abs_sum = 0.0; + int tail_count = 0; + dcblock_func func; + int ch, i; + + memset(&cd, 0, sizeof(cd)); + cd.channels = TEST_CHANNELS; + for (ch = 0; ch < TEST_CHANNELS; ch++) + cd.R_coeffs[ch] = r_coeff; + + memset(rstate, 0, sizeof(rstate)); + gen_input(ref_in, TEST_CHANNELS, TEST_FRAMES, dc); + + for (i = 0; i < TEST_FRAMES * TEST_CHANNELS; i++) + src[i] = (int32_t)round(ref_in[i] * 2147483647.0); + + cir_buf_src_setup(&csrc, src, sizeof(src)); + cir_buf_snk_setup(&csnk, dst, sizeof(dst)); + + func = dcblock_find_func(SOF_IPC_FRAME_S32_LE); + assert_non_null(func); + assert_int_equal(func(&cd, &csrc, &csnk, TEST_FRAMES), 0); + + for (i = 0; i < TEST_FRAMES; i++) { + for (ch = 0; ch < TEST_CHANNELS; ch++) { + int idx = i * TEST_CHANNELS + ch; + double refy = dcblock_ref(&rstate[ch], r, + (double)src[idx] / 2147483648.0); + double outy = (double)dst[idx] / 2147483648.0; + double delta = fabs(refy - outy); + + if (delta > tol_rel) { + printf("s32 mismatch idx %d ref %g out %g delta %g\n", + idx, refy, outy, delta); + assert_true(delta <= tol_rel); + } + + if (i >= TEST_FRAMES * 3 / 4) { + tail_abs_sum += fabs(outy); + tail_count++; + } + } + } + + return tail_count ? tail_abs_sum / tail_count : 0.0; +} + +/* Passthrough: R = 1.0 gives y[n] = x[n] - x[n-1], a pure differentiator. */ +static void test_dcblock_passthrough(void **state) +{ + (void)state; + + /* With no DC and R=1.0 the reference matches bit-close. */ + run_s32_case(ONE_Q2_30, 0.0, 1.0e-6); +} + +/* A strong DC offset must be attenuated towards zero in steady state. */ +static void test_dcblock_dc_removal(void **state) +{ + (void)state; + + double tail = run_s32_case(R_COEF_NEAR_ONE, 0.5, 1.0e-6); + + /* The residual DC plus small sinusoid must be well below the input DC. */ + printf("dc_removal tail mean abs = %g\n", tail); + assert_true(tail < 0.25); +} + +/* Full scale input must not overflow (saturation path in dcblock_generic). */ +static void test_dcblock_saturation(void **state) +{ + (void)state; + + struct comp_data cd; + struct cir_buf_source csrc; + struct cir_buf_sink csnk; + int32_t src[TEST_FRAMES * TEST_CHANNELS]; + int32_t dst[TEST_FRAMES * TEST_CHANNELS]; + dcblock_func func; + int i; + + memset(&cd, 0, sizeof(cd)); + cd.channels = TEST_CHANNELS; + for (i = 0; i < TEST_CHANNELS; i++) + cd.R_coeffs[i] = R_COEF_NEAR_ONE; + + /* + * Alternate per frame so that every channel sees the full-scale + * transitions and exercises the saturation path. + */ + for (i = 0; i < TEST_FRAMES * TEST_CHANNELS; i++) + src[i] = ((i / TEST_CHANNELS) & 1) ? INT32_MAX : INT32_MIN; + + cir_buf_src_setup(&csrc, src, sizeof(src)); + cir_buf_snk_setup(&csnk, dst, sizeof(dst)); + + func = dcblock_find_func(SOF_IPC_FRAME_S32_LE); + assert_non_null(func); + assert_int_equal(func(&cd, &csrc, &csnk, TEST_FRAMES), 0); + + for (i = 0; i < TEST_FRAMES * TEST_CHANNELS; i++) { + int32_t expected = ((i / TEST_CHANNELS) & 1) ? INT32_MAX : INT32_MIN; + + assert_int_equal(dst[i], expected); + } +} + +/* Bit-exactness check against the floating point reference for S32. */ +static void test_dcblock_bitexact_s32(void **state) +{ + (void)state; + + run_s32_case(R_COEF_NEAR_ONE, 0.1, 1.0e-6); +} + +/* + * Runs the S16 processing function and compares it to the floating point + * reference. The component internally works in Q1.31, so the tolerance must + * account for the 16-bit output quantization step (2 LSB of S16). + */ +static void run_s16_case(int32_t r_coeff, double dc) +{ + struct comp_data cd; + struct cir_buf_source csrc; + struct cir_buf_sink csnk; + int16_t src[TEST_FRAMES * TEST_CHANNELS]; + int16_t dst[TEST_FRAMES * TEST_CHANNELS]; + double ref_in[TEST_FRAMES * TEST_CHANNELS]; + struct ref_state rstate[TEST_CHANNELS]; + double r = (double)r_coeff / (double)ONE_Q2_30; + double tol = 2.0 / 32768.0; + dcblock_func func; + int ch, i; + + memset(&cd, 0, sizeof(cd)); + cd.channels = TEST_CHANNELS; + for (ch = 0; ch < TEST_CHANNELS; ch++) + cd.R_coeffs[ch] = r_coeff; + + memset(rstate, 0, sizeof(rstate)); + gen_input(ref_in, TEST_CHANNELS, TEST_FRAMES, dc); + + for (i = 0; i < TEST_FRAMES * TEST_CHANNELS; i++) + src[i] = (int16_t)round(ref_in[i] * 32767.0); + + cir_buf_src_setup(&csrc, src, sizeof(src)); + cir_buf_snk_setup(&csnk, dst, sizeof(dst)); + + func = dcblock_find_func(SOF_IPC_FRAME_S16_LE); + assert_non_null(func); + assert_int_equal(func(&cd, &csrc, &csnk, TEST_FRAMES), 0); + + for (i = 0; i < TEST_FRAMES; i++) { + for (ch = 0; ch < TEST_CHANNELS; ch++) { + int idx = i * TEST_CHANNELS + ch; + double refy = dcblock_ref(&rstate[ch], r, + (double)src[idx] / 32768.0); + double outy = (double)dst[idx] / 32768.0; + double delta = fabs(refy - outy); + + if (delta > tol) { + printf("s16 mismatch idx %d ref %g out %g delta %g\n", + idx, refy, outy, delta); + assert_true(delta <= tol); + } + } + } +} + +/* Bit-exactness check against the floating point reference for S16. */ +static void test_dcblock_bitexact_s16(void **state) +{ + (void)state; + + run_s16_case(R_COEF_NEAR_ONE, 0.1); +} + +/* + * Runs the S24 (in 32-bit container) processing function and compares it to + * the floating point reference with a tolerance of 2 LSB of S24. + */ +static void run_s24_case(int32_t r_coeff, double dc) +{ + struct comp_data cd; + struct cir_buf_source csrc; + struct cir_buf_sink csnk; + int32_t src[TEST_FRAMES * TEST_CHANNELS]; + int32_t dst[TEST_FRAMES * TEST_CHANNELS]; + double ref_in[TEST_FRAMES * TEST_CHANNELS]; + struct ref_state rstate[TEST_CHANNELS]; + double r = (double)r_coeff / (double)ONE_Q2_30; + double tol = 2.0 / 8388608.0; + dcblock_func func; + int ch, i; + + memset(&cd, 0, sizeof(cd)); + cd.channels = TEST_CHANNELS; + for (ch = 0; ch < TEST_CHANNELS; ch++) + cd.R_coeffs[ch] = r_coeff; + + memset(rstate, 0, sizeof(rstate)); + gen_input(ref_in, TEST_CHANNELS, TEST_FRAMES, dc); + + for (i = 0; i < TEST_FRAMES * TEST_CHANNELS; i++) + src[i] = (int32_t)round(ref_in[i] * 8388607.0); + + cir_buf_src_setup(&csrc, src, sizeof(src)); + cir_buf_snk_setup(&csnk, dst, sizeof(dst)); + + func = dcblock_find_func(SOF_IPC_FRAME_S24_4LE); + assert_non_null(func); + assert_int_equal(func(&cd, &csrc, &csnk, TEST_FRAMES), 0); + + for (i = 0; i < TEST_FRAMES; i++) { + for (ch = 0; ch < TEST_CHANNELS; ch++) { + int idx = i * TEST_CHANNELS + ch; + double refy = dcblock_ref(&rstate[ch], r, + (double)src[idx] / 8388608.0); + double outy = (double)dst[idx] / 8388608.0; + double delta = fabs(refy - outy); + + if (delta > tol) { + printf("s24 mismatch idx %d ref %g out %g delta %g\n", + idx, refy, outy, delta); + assert_true(delta <= tol); + } + } + } +} + +/* Bit-exactness check against the floating point reference for S24. */ +static void test_dcblock_bitexact_s24(void **state) +{ + (void)state; + + run_s24_case(R_COEF_NEAR_ONE, 0.1); +} + +int main(void) +{ + const struct CMUnitTest tests[] = { + cmocka_unit_test(test_dcblock_passthrough), + cmocka_unit_test(test_dcblock_dc_removal), + cmocka_unit_test(test_dcblock_saturation), + cmocka_unit_test(test_dcblock_bitexact_s32), + cmocka_unit_test(test_dcblock_bitexact_s16), + cmocka_unit_test(test_dcblock_bitexact_s24), + }; + + cmocka_set_message_output(CM_OUTPUT_TAP); + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/test/cmocka/src/audio/dcblock/dcblock_process.c b/test/cmocka/src/audio/dcblock/dcblock_process.c new file mode 100644 index 000000000000..9ebe197de213 --- /dev/null +++ b/test/cmocka/src/audio/dcblock/dcblock_process.c @@ -0,0 +1,639 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// Copyright(c) 2026 Intel Corporation. + +#include <math.h> +#include <stdarg.h> +#include <stddef.h> +#include <setjmp.h> +#include <stdint.h> +#include <stdlib.h> +#include <cmocka.h> +#include <kernel/header.h> +#include <sof/audio/component_ext.h> +#include <sof/audio/module_adapter/module/generic.h> +#include <sof/audio/audio_buffer.h> +#include <sof/audio/source_api.h> +#include <sof/audio/sink_api.h> +#include <sof/audio/format.h> +#include <ipc/control.h> + +#include "dcblock.h" + +#include "../../util.h" +#include "../../../include/cmocka_chirp_2ch.h" + +/* Allow a few LSB of error due to the internal Q-format rounding. */ +#define ERROR_TOLERANCE_S16 2 +#define ERROR_TOLERANCE_S24 4 +#define ERROR_TOLERANCE_S32 2048 + +/* Q2.30 DC blocking coefficient (~0.98) used for all channels. */ +#define R_COEF 1052266987 + +/* Thresholds for frames count jitter for rand() function */ +#define THR_RAND_PLUS_ONE ((RAND_MAX >> 1) + (RAND_MAX >> 2)) +#define THR_RAND_MINUS_ONE ((RAND_MAX >> 1) - (RAND_MAX >> 2)) + +struct buffer_state { + int idx; +}; + +static struct buffer_state buffer_fill_data; +static struct buffer_state buffer_verify_data; + +/* Floating point reference filter state, one per channel. */ +struct ref_state { + double x_prev; + double y_prev; +}; + +static struct ref_state ref_states[PLATFORM_MAX_CHANNELS]; + +static double dcblock_ref(int ch, double x) +{ + struct ref_state *s = &ref_states[ch]; + double r = (double)R_COEF / (double)ONE_Q2_30; + double y = x - s->x_prev + r * s->y_prev; + + /* The fixed point filter saturates the Q1.31 state after every + * sample (sat_int32). The DC blocker has a passband gain slightly + * above one, so without this clamp the reference state would drift + * away from the implementation for near full-scale input. + */ + if (y > 2147483647.0 / 2147483648.0) + y = 2147483647.0 / 2147483648.0; + else if (y < -1.0) + y = -1.0; + + s->x_prev = x; + s->y_prev = y; + return y; +} + +struct test_parameters { + uint32_t channels; + uint32_t frames; + uint32_t buffer_size_mult; + uint32_t source_format; + uint32_t sink_format; +}; + +struct test_data { + struct comp_dev *dev; + struct comp_buffer *sink; + struct comp_buffer *source; + struct test_parameters *params; + struct processing_module *mod; + bool continue_loop; +}; + +static int setup_group(void **state) +{ + sys_comp_init(sof_get()); + sys_comp_module_dcblock_interface_init(); + return 0; +} + +static struct sof_ipc_comp_process *create_dcblock_comp_ipc(struct test_data *td) +{ + struct sof_ipc_comp_process *ipc; + size_t ipc_size = sizeof(struct sof_ipc_comp_process); + const struct sof_uuid uuid = SOF_REG_UUID(dcblock); + + ipc = calloc(1, ipc_size + SOF_UUID_SIZE); + if (!ipc) + return NULL; + + memcpy_s(ipc + 1, SOF_UUID_SIZE, &uuid, 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 = 0; + ipc->comp.ext_data_length = SOF_UUID_SIZE; + return ipc; +} + +/* Send a configuration blob holding the per-channel R coefficients. */ +static int dcblock_send_config(struct processing_module *mod) +{ + const size_t coeff_size = PLATFORM_MAX_CHANNELS * sizeof(int32_t); + size_t cdata_size = sizeof(struct sof_ipc_ctrl_data) + + sizeof(struct sof_abi_hdr) + coeff_size; + struct sof_ipc_ctrl_data *cdata; + int32_t coeffs[PLATFORM_MAX_CHANNELS]; + int ret; + int i; + + cdata = calloc(1, cdata_size); + if (!cdata) + return -ENOMEM; + + for (i = 0; i < PLATFORM_MAX_CHANNELS; i++) + coeffs[i] = R_COEF; + + cdata->cmd = SOF_CTRL_CMD_BINARY; + cdata->num_elems = coeff_size; + cdata->data[0].magic = SOF_ABI_MAGIC; + cdata->data[0].type = 0; + cdata->data[0].size = coeff_size; + cdata->data[0].abi = SOF_ABI_VERSION; + memcpy_s(cdata->data[0].data, coeff_size, coeffs, coeff_size); + + ret = module_adapter_cmd(mod->dev, COMP_CMD_SET_DATA, cdata, cdata_size); + + free(cdata); + return ret; +} + +static void prepare_sink(struct test_data *td, struct processing_module *mod) +{ + struct test_parameters *parameters = td->params; + struct module_data *md = &mod->priv; + size_t size; + size_t free; + + size = parameters->frames * get_frame_bytes(parameters->sink_format, parameters->channels) * + parameters->buffer_size_mult; + + md->mpd.out_buff_size = parameters->frames * get_frame_bytes(parameters->sink_format, + parameters->channels); + + td->sink = create_test_sink(td->dev, 0, parameters->sink_format, + parameters->channels, size); + free = audio_stream_get_free_bytes(&td->sink->stream); + assert_int_equal(free, size); +} + +static void prepare_source(struct test_data *td, struct processing_module *mod) +{ + struct test_parameters *parameters = td->params; + struct module_data *md = &mod->priv; + size_t size; + size_t free; + + md->mpd.in_buff_size = parameters->frames * get_frame_bytes(parameters->source_format, + parameters->channels); + + size = parameters->frames * get_frame_bytes(parameters->source_format, + parameters->channels) * parameters->buffer_size_mult; + + td->source = create_test_source(td->dev, 0, parameters->source_format, + parameters->channels, size); + free = audio_stream_get_free_bytes(&td->source->stream); + assert_int_equal(free, size); +} + +static void cleanup_test_data(struct test_data *td) +{ + struct processing_module *mod; + + if (!td) + return; + + if (td->dev) { + mod = comp_mod(td->dev); + if (mod) { + test_free(mod->input_buffers); + mod->input_buffers = NULL; + test_free(mod->output_buffers); + mod->output_buffers = NULL; + test_free(mod->stream_params); + mod->stream_params = NULL; + } + } + + if (td->source) { + free_test_source(td->source); + td->source = NULL; + } + + if (td->sink) { + free_test_sink(td->sink); + td->sink = NULL; + } + + if (td->dev) { + comp_free(td->dev); + td->dev = NULL; + } + + test_free(td->params); + test_free(td); +} + +static int setup(void **state) +{ + struct test_parameters *params = *state; + struct processing_module *mod; + struct test_data *td; + struct sof_ipc_comp_process *ipc = NULL; + struct comp_dev *dev; + struct sof_source *sources[1]; + struct sof_sink *sinks[1]; + int ret; + int i; + + td = test_malloc(sizeof(*td)); + if (!td) { + *state = NULL; + return -EINVAL; + } + + *td = (struct test_data){ 0 }; + *state = td; + td->params = test_malloc(sizeof(*params)); + if (!td->params) { + ret = -ENOMEM; + goto error; + } + + memcpy_s(td->params, sizeof(*td->params), params, sizeof(*params)); + ipc = create_dcblock_comp_ipc(td); + if (!ipc) { + ret = -ENOMEM; + goto error; + } + + buffer_fill_data.idx = 0; + buffer_verify_data.idx = 0; + for (i = 0; i < PLATFORM_MAX_CHANNELS; i++) { + ref_states[i].x_prev = 0.0; + ref_states[i].y_prev = 0.0; + } + + dev = comp_new((struct sof_ipc_comp *)ipc); + free(ipc); + ipc = NULL; + if (!dev) { + ret = -EINVAL; + goto error; + } + + td->dev = dev; + dev->frames = params->frames; + mod = comp_mod(dev); + + ret = dcblock_send_config(mod); + if (ret) + goto error; + + prepare_sink(td, mod); + prepare_source(td, mod); + + mod->input_buffers = test_malloc(sizeof(struct input_stream_buffer)); + if (!mod->input_buffers) { + ret = -ENOMEM; + goto error; + } + mod->input_buffers[0].data = &td->source->stream; + mod->output_buffers = test_malloc(sizeof(struct output_stream_buffer)); + if (!mod->output_buffers) { + ret = -ENOMEM; + goto error; + } + mod->output_buffers[0].data = &td->sink->stream; + mod->stream_params = test_malloc(sizeof(struct sof_ipc_stream_params)); + if (!mod->stream_params) { + ret = -ENOMEM; + goto error; + } + mod->stream_params->channels = params->channels; + mod->period_bytes = get_frame_bytes(params->source_format, params->channels) * 48000 / 1000; + + sources[0] = audio_buffer_get_source(&td->source->audio_buffer); + sinks[0] = audio_buffer_get_sink(&td->sink->audio_buffer); + ret = module_prepare(mod, sources, 1, sinks, 1); + if (ret) + goto error; + + td->continue_loop = true; + + *state = td; + return 0; + +error: + free(ipc); + cleanup_test_data(td); + *state = NULL; + return ret; +} + +static int teardown(void **state) +{ + struct test_data *td = *state; + + cleanup_test_data(td); + return 0; +} + +#if CONFIG_FORMAT_S16LE +static void fill_source_s16(struct test_data *td, int frames_max) +{ + struct processing_module *mod = comp_mod(td->dev); + struct comp_dev *dev = td->dev; + struct comp_buffer *sb; + struct audio_stream *ss; + int16_t *x; + int bytes_total; + int samples; + int frames; + int i; + int samples_processed = 0; + + sb = comp_dev_get_first_data_producer(dev); + ss = &sb->stream; + frames = MIN(audio_stream_get_free_frames(ss), frames_max); + samples = frames * audio_stream_get_channels(ss); + for (i = 0; i < samples; i++) { + x = audio_stream_write_frag_s16(ss, i); + *x = sat_int16(Q_SHIFT_RND(chirp_2ch[buffer_fill_data.idx++], 31, 15)); + samples_processed++; + if (buffer_fill_data.idx == CHIRP_2CH_LENGTH) { + td->continue_loop = false; + break; + } + } + + if (samples_processed > 0) { + bytes_total = samples_processed * audio_stream_sample_bytes(ss); + comp_update_buffer_produce(sb, bytes_total); + } + + mod->input_buffers[0].size = samples_processed / audio_stream_get_channels(ss); +} + +static void verify_sink_s16(struct test_data *td) +{ + struct processing_module *mod = comp_mod(td->dev); + struct comp_dev *dev = td->dev; + struct comp_buffer *sb; + struct audio_stream *ss; + int nch = td->params->channels; + int32_t delta; + int32_t ref; + int32_t out; + int16_t *x; + int16_t in; + int samples; + int i; + + sb = comp_dev_get_first_data_consumer(dev); + ss = &sb->stream; + samples = mod->output_buffers[0].size >> 1; + for (i = 0; i < samples; i++) { + x = audio_stream_read_frag_s16(ss, i); + out = *x; + in = sat_int16(Q_SHIFT_RND(chirp_2ch[buffer_verify_data.idx], 31, 15)); + ref = (int32_t)round(dcblock_ref(buffer_verify_data.idx % nch, + (double)in / 32768.0) * 32768.0); + ref = sat_int16(ref); + buffer_verify_data.idx++; + delta = ref - out; + if (delta > ERROR_TOLERANCE_S16 || delta < -ERROR_TOLERANCE_S16) + assert_int_equal(out, ref); + } +} +#endif /* CONFIG_FORMAT_S16LE */ + +#if CONFIG_FORMAT_S24LE +static void fill_source_s24(struct test_data *td, int frames_max) +{ + struct processing_module *mod = comp_mod(td->dev); + struct comp_dev *dev = td->dev; + struct comp_buffer *sb; + struct audio_stream *ss; + int32_t *x; + int bytes_total; + int samples; + int frames; + int i; + int samples_processed = 0; + + sb = comp_dev_get_first_data_producer(dev); + ss = &sb->stream; + frames = MIN(audio_stream_get_free_frames(ss), frames_max); + samples = frames * audio_stream_get_channels(ss); + for (i = 0; i < samples; i++) { + x = audio_stream_write_frag_s32(ss, i); + *x = sat_int24(Q_SHIFT_RND(chirp_2ch[buffer_fill_data.idx++], 31, 23)); + samples_processed++; + if (buffer_fill_data.idx == CHIRP_2CH_LENGTH) { + td->continue_loop = false; + break; + } + } + + if (samples_processed > 0) { + bytes_total = samples_processed * audio_stream_sample_bytes(ss); + comp_update_buffer_produce(sb, bytes_total); + } + + mod->input_buffers[0].size = samples_processed / audio_stream_get_channels(ss); +} + +static void verify_sink_s24(struct test_data *td) +{ + struct processing_module *mod = comp_mod(td->dev); + struct comp_dev *dev = td->dev; + struct comp_buffer *sb; + struct audio_stream *ss; + int nch = td->params->channels; + int32_t delta; + int32_t ref; + int32_t out; + int32_t *x; + int32_t in; + int samples; + int i; + + sb = comp_dev_get_first_data_consumer(dev); + ss = &sb->stream; + samples = mod->output_buffers[0].size >> 2; + for (i = 0; i < samples; i++) { + x = audio_stream_read_frag_s32(ss, i); + out = (*x << 8) >> 8; /* Make sure there's no 24 bit overflow */ + in = sat_int24(Q_SHIFT_RND(chirp_2ch[buffer_verify_data.idx], 31, 23)); + ref = (int32_t)round(dcblock_ref(buffer_verify_data.idx % nch, + (double)in / 8388608.0) * 8388608.0); + ref = sat_int24(ref); + buffer_verify_data.idx++; + delta = ref - out; + if (delta > ERROR_TOLERANCE_S24 || delta < -ERROR_TOLERANCE_S24) + assert_int_equal(out, ref); + } +} +#endif /* CONFIG_FORMAT_S24LE */ + +#if CONFIG_FORMAT_S32LE +static void fill_source_s32(struct test_data *td, int frames_max) +{ + struct processing_module *mod = comp_mod(td->dev); + struct comp_dev *dev = td->dev; + struct comp_buffer *sb; + struct audio_stream *ss; + int32_t *x; + int bytes_total; + int samples; + int frames; + int i; + int samples_processed = 0; + + sb = comp_dev_get_first_data_producer(dev); + ss = &sb->stream; + frames = MIN(audio_stream_get_free_frames(ss), frames_max); + samples = frames * audio_stream_get_channels(ss); + for (i = 0; i < samples; i++) { + x = audio_stream_write_frag_s32(ss, i); + *x = chirp_2ch[buffer_fill_data.idx++]; + samples_processed++; + if (buffer_fill_data.idx == CHIRP_2CH_LENGTH) { + td->continue_loop = false; + break; + } + } + + if (samples_processed > 0) { + bytes_total = samples_processed * audio_stream_sample_bytes(ss); + comp_update_buffer_produce(sb, bytes_total); + } + + mod->input_buffers[0].size = samples_processed / audio_stream_get_channels(ss); +} + +static void verify_sink_s32(struct test_data *td) +{ + struct processing_module *mod = comp_mod(td->dev); + struct comp_dev *dev = td->dev; + struct comp_buffer *sb; + struct audio_stream *ss; + int nch = td->params->channels; + int64_t delta; + int32_t ref; + int32_t out; + int32_t *x; + int32_t in; + int samples; + int i; + + sb = comp_dev_get_first_data_consumer(dev); + ss = &sb->stream; + samples = mod->output_buffers[0].size >> 2; + for (i = 0; i < samples; i++) { + x = audio_stream_read_frag_s32(ss, i); + out = *x; + in = chirp_2ch[buffer_verify_data.idx]; + ref = (int32_t)round(dcblock_ref(buffer_verify_data.idx % nch, + (double)in / 2147483648.0) * 2147483648.0); + buffer_verify_data.idx++; + delta = (int64_t)ref - (int64_t)out; + if (delta > ERROR_TOLERANCE_S32 || delta < -ERROR_TOLERANCE_S32) + assert_int_equal(out, ref); + } +} +#endif /* CONFIG_FORMAT_S32LE */ + +static int frames_jitter(int frames) +{ + int r = rand(); + + if (r > THR_RAND_PLUS_ONE) + return frames + 1; + else if (r < THR_RAND_MINUS_ONE) + return frames - 1; + else + return frames; +} + +static void test_audio_dcblock(void **state) +{ + struct test_data *td = *state; + struct processing_module *mod = comp_mod(td->dev); + struct comp_buffer *source = td->source; + struct comp_buffer *sink = td->sink; + struct sof_source *sources[1]; + struct sof_sink *sinks[1]; + uint32_t avail_before; + int ret; + int frames; + + sources[0] = audio_buffer_get_source(&source->audio_buffer); + sinks[0] = audio_buffer_get_sink(&sink->audio_buffer); + + while (td->continue_loop) { + frames = frames_jitter(td->params->frames); + switch (audio_stream_get_frm_fmt(&source->stream)) { + case SOF_IPC_FRAME_S16_LE: + fill_source_s16(td, frames); + break; + case SOF_IPC_FRAME_S24_4LE: + fill_source_s24(td, frames); + break; + case SOF_IPC_FRAME_S32_LE: + fill_source_s32(td, frames); + break; + default: + assert(0); + break; + } + + /* Process exactly the frames just filled. The source/sink API + * updates the buffer read/write positions internally, so record + * the sink fill level to know how many bytes were produced. + */ + td->dev->frames = mod->input_buffers[0].size; + avail_before = audio_stream_get_avail_bytes(&sink->stream); + ret = module_process_sink_src(mod, sources, 1, sinks, 1); + assert_int_equal(ret, 0); + mod->output_buffers[0].size = + audio_stream_get_avail_bytes(&sink->stream) - avail_before; + + switch (audio_stream_get_frm_fmt(&sink->stream)) { + case SOF_IPC_FRAME_S16_LE: + verify_sink_s16(td); + break; + case SOF_IPC_FRAME_S24_4LE: + verify_sink_s24(td); + break; + case SOF_IPC_FRAME_S32_LE: + verify_sink_s32(td); + break; + default: + assert(0); + break; + } + + comp_update_buffer_consume(sink, mod->output_buffers[0].size); + } +} + +static struct test_parameters parameters[] = { +#if CONFIG_FORMAT_S16LE + { 2, 48, 2, SOF_IPC_FRAME_S16_LE, SOF_IPC_FRAME_S16_LE }, +#endif /* CONFIG_FORMAT_S16LE */ +#if CONFIG_FORMAT_S24LE + { 2, 48, 2, SOF_IPC_FRAME_S24_4LE, SOF_IPC_FRAME_S24_4LE }, +#endif /* CONFIG_FORMAT_S24LE */ +#if CONFIG_FORMAT_S32LE + { 2, 48, 2, SOF_IPC_FRAME_S32_LE, SOF_IPC_FRAME_S32_LE }, +#endif /* CONFIG_FORMAT_S32LE */ +}; + +int main(void) +{ + int i; + + struct CMUnitTest tests[ARRAY_SIZE(parameters)]; + + for (i = 0; i < ARRAY_SIZE(parameters); i++) { + tests[i].name = "test_audio_dcblock"; + tests[i].test_func = test_audio_dcblock; + tests[i].setup_func = setup; + tests[i].teardown_func = teardown; + tests[i].initial_state = ¶meters[i]; + } + + cmocka_set_message_output(CM_OUTPUT_TAP); + + return cmocka_run_group_tests(tests, setup_group, NULL); +} 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/pcm_converter/pcm_float.c b/test/cmocka/src/audio/pcm_converter/pcm_float.c index 70ccd3621758..0fed36a98774 100644 --- a/test/cmocka/src/audio/pcm_converter/pcm_float.c +++ b/test/cmocka/src/audio/pcm_converter/pcm_float.c @@ -99,7 +99,20 @@ static struct comp_buffer *_test_pcm_convert(enum sof_ipc_frame frm_in, enum sof /* run conversion */ fun = pcm_get_conversion_function(frm_in, frm_out); assert_non_null(fun); - fun(&source->stream, 0, &sink->stream, 0, samples, DUMMY_CHMAP); + + struct cir_buf_source cir_src = { + .buf_start = source->stream.addr, + .buf_end = source->stream.end_addr, + .ptr = source->stream.r_ptr, + }; + struct cir_buf_sink cir_snk = { + .buf_start = sink->stream.addr, + .buf_end = sink->stream.end_addr, + .ptr = sink->stream.w_ptr, + }; + + fun(&cir_src, audio_stream_get_channels(&source->stream), + &cir_snk, audio_stream_get_channels(&sink->stream), samples, DUMMY_CHMAP); /* assert last value in sink is untouched */ assert_int_equal(((uint8_t *)sink->stream.w_ptr)[outbytes - 1], fillval); 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..7315ce387562 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; @@ -49,6 +49,9 @@ struct pipeline_connect_data *get_standard_connect_objects(void) sch->ops = &schedule_mock_ops; list_item_append(&sch->list, &schedulers->list); + /* Bind the scheduler to the task, as schedule_task_init() would */ + pipe->pipe_task->sch = sch; + struct comp_dev *first = calloc(sizeof(struct comp_dev), 1); struct comp_ipc_config *first_comp = &first->ipc_config; @@ -83,7 +86,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..b6c1fec45e45 100644 --- a/test/cmocka/src/audio/volume/volume_process.c +++ b/test/cmocka/src/audio/volume/volume_process.c @@ -71,6 +71,9 @@ static int setup(void **state) cd->vol = test_malloc(vol_size); + /* number of channels used by the processing functions */ + cd->channels = vol_state->parameters.channels; + /* set processing function and volume */ #if CONFIG_IPC_MAJOR_4 cd->scale_vol = vol_get_processing_function(vol_state->mod->dev, cd); @@ -266,6 +269,8 @@ static void test_audio_vol(void **state) struct processing_module_test_data *vol_state = *state; struct processing_module *mod = vol_state->mod; struct vol_data *cd = module_get_private_data(mod); + struct cir_buf_source source_buf; + struct cir_buf_sink sink_buf; switch (audio_stream_get_frm_fmt(&vol_state->sinks[0]->stream)) { case SOF_IPC_FRAME_S16_LE: @@ -289,7 +294,15 @@ static void test_audio_vol(void **state) vol_state->input_buffers[0]->consumed = 0; vol_state->output_buffers[0]->size = 0; - cd->scale_vol(mod, vol_state->input_buffers[0], vol_state->output_buffers[0], + source_buf.buf_start = audio_stream_get_addr(&vol_state->sources[0]->stream); + source_buf.buf_end = audio_stream_get_end_addr(&vol_state->sources[0]->stream); + source_buf.ptr = audio_stream_get_rptr(&vol_state->sources[0]->stream); + + sink_buf.buf_start = audio_stream_get_addr(&vol_state->sinks[0]->stream); + sink_buf.buf_end = audio_stream_get_end_addr(&vol_state->sinks[0]->stream); + sink_buf.ptr = audio_stream_get_wptr(&vol_state->sinks[0]->stream); + + cd->scale_vol(mod, &source_buf, &sink_buf, mod->dev->frames, cd->attenuation); vol_state->verify(mod, vol_state->sinks[0], vol_state->sources[0]); @@ -315,7 +328,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 +351,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..633108a0730e 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,68 @@ 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; +} + +struct k_heap * WEAK sof_sys_user_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) { @@ -243,7 +312,7 @@ struct schedulers ** WEAK arch_schedulers_get(void) int WEAK schedule_task_init(struct task *task, const struct sof_uuid_entry *uid, uint16_t type, - uint16_t priority, enum task_state (*run)(void *data), + int16_t priority, enum task_state (*run)(void *data), void *data, uint16_t core, uint32_t flags) { (void)task; @@ -260,7 +329,7 @@ int WEAK schedule_task_init(struct task *task, int WEAK schedule_task_init_ll(struct task *task, const struct sof_uuid_entry *uid, uint16_t type, - uint16_t priority, enum task_state (*run)(void *data), + int16_t priority, enum task_state (*run)(void *data), void *data, uint16_t core, uint32_t flags) { return 0; 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/CMakeLists.txt b/test/cmocka/src/math/arithmetic/CMakeLists.txt index 721c50020ba5..6625aef444ef 100644 --- a/test/cmocka/src/math/arithmetic/CMakeLists.txt +++ b/test/cmocka/src/math/arithmetic/CMakeLists.txt @@ -1,37 +1,5 @@ # SPDX-License-Identifier: BSD-3-Clause -cmocka_test(scalar_power - scalar_power.c - ${PROJECT_SOURCE_DIR}/src/math/power.c -) - -cmocka_test(base2_logarithm - base2_logarithm.c - ${PROJECT_SOURCE_DIR}/src/math/base2log.c -) - -cmocka_test(exponential - exponential.c - ${PROJECT_SOURCE_DIR}/src/math/exp_fcn.c - ${PROJECT_SOURCE_DIR}/src/math/exp_fcn_hifi.c -) -cmocka_test(square_root - square_root.c - ${PROJECT_SOURCE_DIR}/src/math/sqrt_int16.c -) - -cmocka_test(base_10_logarithm - base_10_logarithm.c - ${PROJECT_SOURCE_DIR}/src/math/log_10.c - ${PROJECT_SOURCE_DIR}/src/math/base2log.c -) - -cmocka_test(base_e_logarithm - base_e_logarithm.c - ${PROJECT_SOURCE_DIR}/src/math/log_e.c - ${PROJECT_SOURCE_DIR}/src/math/base2log.c -) - cmocka_test(a_law_codec a_law_codec.c ${PROJECT_SOURCE_DIR}/src/math/a_law.c diff --git a/test/cmocka/src/math/arithmetic/scalar_power.c b/test/cmocka/src/math/arithmetic/scalar_power.c deleted file mode 100644 index 8b23791e979e..000000000000 --- a/test/cmocka/src/math/arithmetic/scalar_power.c +++ /dev/null @@ -1,55 +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/power.h> -#include <sof/common.h> -#include "power_tables.h" -/* 'Error (max = 0.000034912111005), THD+N = -96.457180359025074' */ -#define CMP_TOLERANCE 0.0000150363575813 - -static void test_math_arithmetic_power_fixed(void **state) -{ - (void)state; - - 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 diff = fabsf(power_table[i][j] - (double)p / (1 << 15)); - - if (diff > CMP_TOLERANCE) { - printf("%s: diff for %.16f: base = %.16f", - __func__, diff, (double)b[i] / (1 << 25)); - printf(", exponent = %.16f, power = %.16f\n", - (double)e[j] / (1 << 29), (double)p / (1 << 15)); - assert_true(diff <= CMP_TOLERANCE); - } - } - } -} - -int main(void) -{ - const struct CMUnitTest tests[] = { - cmocka_unit_test(test_math_arithmetic_power_fixed) - }; - - cmocka_set_message_output(CM_OUTPUT_TAP); - - return cmocka_run_group_tests(tests, NULL, NULL); -} 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..3fe6a87680f4 100644 --- a/test/ztest/unit/fast-get/CMakeLists.txt +++ b/test/ztest/unit/fast-get/CMakeLists.txt @@ -5,27 +5,33 @@ project(test_fast_get) set(SOF_ROOT "${PROJECT_SOURCE_DIR}/../../../..") +# Set SOF top directory for UUID registry generation +set(sof_top_dir ${SOF_ROOT}) + # Include SOF CMake functions 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 + ${PROJECT_BINARY_DIR}/include/generated # For uuid-registry.h ) # 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/cmocka/src/math/arithmetic/base_10_logarithm.c b/test/ztest/unit/math/advanced/functions/test_base10_logarithm_ztest.c similarity index 77% rename from test/cmocka/src/math/arithmetic/base_10_logarithm.c rename to test/ztest/unit/math/advanced/functions/test_base10_logarithm_ztest.c index 3973df31a515..b880cfd388a5 100644 --- a/test/cmocka/src/math/arithmetic/base_10_logarithm.c +++ b/test/ztest/unit/math/advanced/functions/test_base10_logarithm_ztest.c @@ -1,30 +1,24 @@ // SPDX-License-Identifier: BSD-3-Clause // -// Copyright(c) 2021 Intel Corporation. All rights reserved. -// -// Author: Shriram Shastry <malladi.sastry@linux.intel.com> +// 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 <stdio.h> +#include <zephyr/ztest.h> #include <stdint.h> -#include <stdarg.h> -#include <stddef.h> -#include <setjmp.h> #include <math.h> -#include <cmocka.h> -#include <string.h> #include <sof/math/log.h> -#include <sof/audio/format.h> -#include <rtos/string.h> -#include <sof/common.h> -#include "log2_tables.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 -/* Natural logarithm log10(X) reference table generated by matlab/Octave */ + +/* 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, @@ -55,10 +49,10 @@ static const double common_log10_ref_table[] = { /* 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, + 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, @@ -73,33 +67,23 @@ static const uint32_t uv[100] = { 3904515724ULL, 3947899232ULL, 3991282740ULL, 4034666248ULL, 4078049756ULL, 4121433264ULL, 4164816772ULL, 4208200280ULL, 4251583788ULL, 4294967295ULL}; -static void test_math_arithmetic_base10log_fixed(void **state) +ZTEST(math_advanced_functions_suite, test_math_arithmetic_base10log_fixed) { - (void)state; - 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) { - printf("%s: diff for %.16f: val = %16d, log10() = %.16f\n", __func__, diff, - uv[i], clogfxp / (1 << 28)); - assert_true(diff <= CMP_TOLERANCE); + zassert_true(diff <= CMP_TOLERANCE, + "log10_int32(%u): delta %.16f > tolerance (got %.16f)", + uv[i], diff, clogfxp / (1 << 28)); } } -} - -int main(void) -{ - const struct CMUnitTest tests[] = { - cmocka_unit_test(test_math_arithmetic_base10log_fixed) - }; - - cmocka_set_message_output(CM_OUTPUT_TAP); - - return cmocka_run_group_tests(tests, NULL, NULL); -} +} \ No newline at end of file diff --git a/test/cmocka/src/math/arithmetic/base2_logarithm.c b/test/ztest/unit/math/advanced/functions/test_base2_logarithm_ztest.c similarity index 54% rename from test/cmocka/src/math/arithmetic/base2_logarithm.c rename to test/ztest/unit/math/advanced/functions/test_base2_logarithm_ztest.c index a06e44d09b78..52d1eb0e6a0b 100644 --- a/test/cmocka/src/math/arithmetic/base2_logarithm.c +++ b/test/ztest/unit/math/advanced/functions/test_base2_logarithm_ztest.c @@ -1,25 +1,26 @@ // SPDX-License-Identifier: BSD-3-Clause // -// Copyright(c) 2021 Intel Corporation. All rights reserved. +// 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 <stdio.h> -#include <stdint.h> -#include <stdarg.h> -#include <stddef.h> -#include <setjmp.h> -#include <math.h> -#include <cmocka.h> -#include <string.h> - -#include <sof/math/log.h> +#include <zephyr/ztest.h> #include <sof/audio/format.h> +#include <sof/math/log.h> +#include <math.h> #include <rtos/string.h> -#include <sof/common.h> + +/* Test data tables from MATLAB-generated reference */ #include "log2_tables.h" + /* 'Error[max] = 0.0000236785999981,THD(-dBc) = -92.5128795787487235' */ -#define CMP_TOLERANCE 0.0000236785691029 +#define CMP_TOLERANCE 0.0000236785691029f /* testvector in Q32.0 */ static const uint32_t uv[100] = { @@ -41,33 +42,35 @@ static const uint32_t uv[100] = { 3904515724ULL, 3947899232ULL, 3991282740ULL, 4034666248ULL, 4078049756ULL, 4121433264ULL, 4164816772ULL, 4208200280ULL, 4251583788ULL, 4294967295ULL}; -static void test_math_arithmetic_base2log_fixed(void **state) +/** + * @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) { - (void)state; - uint32_t u[100]; - int i; - - memcpy_s((void *)&u[0], sizeof(u), (void *)&uv[0], 100U * sizeof(uint32_t)); - for (i = 0; i < ARRAY_SIZE(log2_lookup_table); i++) { - float y = Q_CONVERT_QTOF(base2_logarithm(u[i]), 0); - float diff = fabs(log2_lookup_table[i] - (double)y / (1 << 16)); + int i, ret; - if (diff > CMP_TOLERANCE) { - printf("%s: diff for %.16f: value = %.16f, log2 = %.16f\n", __func__, diff, - (double)u[i], (double)y / (1 << 16)); - assert_true(diff <= CMP_TOLERANCE); - } - } -} + BUILD_ASSERT(ARRAY_SIZE(uv) == ARRAY_SIZE(log2_lookup_table), + "Test vector size must match reference table size"); -int main(void) -{ - const struct CMUnitTest tests[] = { - cmocka_unit_test(test_math_arithmetic_base2log_fixed) - }; + 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); - cmocka_set_message_output(CM_OUTPUT_TAP); + 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))); - return cmocka_run_group_tests(tests, NULL, NULL); + 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/cmocka/src/math/arithmetic/base_e_logarithm.c b/test/ztest/unit/math/advanced/functions/test_base_e_logarithm_ztest.c similarity index 75% rename from test/cmocka/src/math/arithmetic/base_e_logarithm.c rename to test/ztest/unit/math/advanced/functions/test_base_e_logarithm_ztest.c index c2c1d2083c8b..e400b8b76a59 100644 --- a/test/cmocka/src/math/arithmetic/base_e_logarithm.c +++ b/test/ztest/unit/math/advanced/functions/test_base_e_logarithm_ztest.c @@ -1,23 +1,21 @@ // SPDX-License-Identifier: BSD-3-Clause // -// Copyright(c) 2021 Intel Corporation. All rights reserved. +// 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 <stdio.h> +#include <zephyr/ztest.h> #include <stdint.h> -#include <stdarg.h> -#include <stddef.h> -#include <setjmp.h> #include <math.h> -#include <cmocka.h> -#include <string.h> #include <sof/math/log.h> -#include <sof/audio/format.h> -#include <rtos/string.h> -#include <sof/common.h> -#include "log2_tables.h" + /* 'Error[max] = 0.0000164133276926,THD(-dBc) = -95.6960671942683234' */ /* 'Error[max] = rms(log() - double(log_int32()))' */ /* 'THD = 20*log10(Error[max])' */ @@ -72,33 +70,31 @@ static const uint32_t uv[100] = { 3904515724ULL, 3947899232ULL, 3991282740ULL, 4034666248ULL, 4078049756ULL, 4121433264ULL, 4164816772ULL, 4208200280ULL, 4251583788ULL, 4294967295ULL}; -static void test_math_arithmetic_base_e_log_fixed(void **state) +/** + * @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) { - (void)state; - - double logefxp; - double diff; int i; - for (i = 0; i < ARRAY_SIZE(natural_log_lookup_table); i++) { - logefxp = ln_int32(uv[i]); - diff = fabs(natural_log_lookup_table[i] - (double)logefxp / (1 << 27)); - - if (diff > CMP_TOLERANCE) { - printf("%s: diff for %.16f: value = %16d, log() = %.16f\n", __func__, diff, - uv[i], logefxp / (1 << 27)); - assert_true(diff <= CMP_TOLERANCE); - } - } -} + BUILD_ASSERT(ARRAY_SIZE(uv) == ARRAY_SIZE(natural_log_lookup_table), + "Test vector size must match reference table size"); -int main(void) -{ - const struct CMUnitTest tests[] = { - cmocka_unit_test(test_math_arithmetic_base_e_log_fixed) - }; + 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)); - cmocka_set_message_output(CM_OUTPUT_TAP); - - return cmocka_run_group_tests(tests, NULL, NULL); + 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/cmocka/src/math/arithmetic/exponential.c b/test/ztest/unit/math/advanced/functions/test_exponential_ztest.c similarity index 62% rename from test/cmocka/src/math/arithmetic/exponential.c rename to test/ztest/unit/math/advanced/functions/test_exponential_ztest.c index 3e44bf0c3743..1fd03b6bb5af 100644 --- a/test/cmocka/src/math/arithmetic/exponential.c +++ b/test/ztest/unit/math/advanced/functions/test_exponential_ztest.c @@ -1,27 +1,27 @@ // SPDX-License-Identifier: BSD-3-Clause /* - * Copyright(c) 2022-2025 Intel Corporation. + * 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 <stdbool.h> -#include <stdio.h> -#include <stdint.h> -#include <stdarg.h> -#include <stddef.h> -#include <setjmp.h> -#include <math.h> -#include <cmocka.h> -#include <string.h> -#include <time.h> -#include <stdlib.h> - +#include <zephyr/ztest.h> +#include <zephyr/logging/log.h> #include <sof/math/exp_fcn.h> #include <sof/audio/format.h> -#include <rtos/string.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 */ @@ -78,43 +78,8 @@ static void gen_testvector_linspace_int32(double a, double b, int step_count, in itmp = (int64_t)round(fvalue * (double)(1 << qformat)); *iout = saturate32(itmp); *fout = (double)*iout / (1 << qformat); - return; -} - -/** - * Test for sofm_exp_approx() - * @param state Cmocka internal state - */ -static void test_function_sofm_exp_approx(void **state) -{ - (void)state; - - 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; - - if (ulp > ULP_TOLERANCE) { - printf("%s: ULP for %.16f: value = %.16f, Exp = %.16f\n", __func__, - ulp, (double)b_i / (1 << 28), (double)accum / (1 << 19)); - assert_true(false); - } - } - printf("%s: Worst-case ULP: %g ULP_SCALE %g\n", __func__, max_ulp, ULP_SCALE); } - /** * Calculate reference exponent value * @param x Input value @@ -128,6 +93,7 @@ static double ref_exp(double x, int qformat) yf = exp(x); yi = yf * (1 << qformat); + if (yi > INT32_MAX) yi = INT32_MAX; else if (yi < INT32_MIN) @@ -160,32 +126,95 @@ static void test_exp_with_input_value(int32_t ivalue, int32_t *iexp_value, 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; - if (abs_delta > abs_delta_tolerance) { - printf("%s: Absolute error %g exceeds limit %g, input %g output %g.\n", - __func__, abs_delta, abs_delta_tolerance, fvalue, fexp_value); - assert_true(false); - } + 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; - if (rel_delta > rel_delta_tolerance) { - printf("%s: Relative error %g exceeds limit %g, input %g output %g.\n", - __func__, rel_delta, rel_delta_tolerance, fvalue, fexp_value); - assert_true(false); + 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); } /** - * Test for sofm_exp_fixed() - * @param state Cmocka internal state + * @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 */ -static void test_function_sofm_exp_fixed(void **state) +ZTEST(math_advanced_functions_suite, test_function_sofm_exp_fixed) { - (void)state; double rel_delta_max, abs_delta_max; double tmp; int32_t ivalue, iexp_value; @@ -200,8 +229,8 @@ static void test_function_sofm_exp_fixed(void **state) ABS_DELTA_TOLERANCE_TEST2, REL_DELTA_TOLERANCE_TEST2); } - printf("%s: Absolute max error was %.6e (max range).\n", __func__, abs_delta_max); - printf("%s: Relative max error was %.6e (max range).\n", __func__, rel_delta_max); + 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; @@ -213,33 +242,23 @@ static void test_function_sofm_exp_fixed(void **state) ABS_DELTA_TOLERANCE_TEST3, REL_DELTA_TOLERANCE_TEST3); } - printf("%s: Absolute max error was %.6e (middle).\n", __func__, abs_delta_max); - printf("%s: Relative max error was %.6e (middle).\n", __func__, rel_delta_max); + LOG_INF("Absolute max error was %.6e (middle)", abs_delta_max); + LOG_INF("Relative max error was %.6e (middle)", rel_delta_max); } /** - * 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); -} - -/** - * Test for sofm_db2lin_fixed() - * @param state Cmocka internal state + * @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 */ -static void test_function_sofm_db2lin_fixed(void **state) +ZTEST(math_advanced_functions_suite, test_function_sofm_db2lin_fixed) { - (void)state; double abs_delta, rel_delta, abs_delta_max, rel_delta_max; double fin, fout, fref; double eps = 1e-9; @@ -248,7 +267,7 @@ static void test_function_sofm_db2lin_fixed(void **state) rel_delta_max = 0; abs_delta_max = 0; - for (i = 0; i < NUMTESTSAMPLES_TEST2; i++) { + 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); @@ -261,31 +280,15 @@ static void test_function_sofm_db2lin_fixed(void **state) if (rel_delta > rel_delta_max) rel_delta_max = rel_delta; - if (abs_delta > ABS_DELTA_TOLERANCE_TEST4) { - printf("%s: Absolute error %g exceeds limit %g, input %g output %g.\n", - __func__, abs_delta, ABS_DELTA_TOLERANCE_TEST4, fin, fout); - assert_true(false); - } - - if (rel_delta > REL_DELTA_TOLERANCE_TEST4) { - printf("%s: Relative error %g exceeds limit %g, input %g output %g.\n", - __func__, rel_delta, REL_DELTA_TOLERANCE_TEST4, fin, fout); - assert_true(false); - } - } - printf("%s: Absolute max error was %.6e.\n", __func__, abs_delta_max); - printf("%s: Relative max error was %.6e.\n", __func__, rel_delta_max); -} - -int main(void) -{ - const struct CMUnitTest tests[] = { - cmocka_unit_test(test_function_sofm_exp_approx), - cmocka_unit_test(test_function_sofm_exp_fixed), - cmocka_unit_test(test_function_sofm_db2lin_fixed), - }; + 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); - cmocka_set_message_output(CM_OUTPUT_TAP); + 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); + } - return cmocka_run_group_tests(tests, NULL, NULL); + 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/cmocka/src/math/arithmetic/square_root.c b/test/ztest/unit/math/advanced/functions/test_square_root_ztest.c similarity index 60% rename from test/cmocka/src/math/arithmetic/square_root.c rename to test/ztest/unit/math/advanced/functions/test_square_root_ztest.c index f6c03c9c42cf..2dcb45ffa0ff 100644 --- a/test/cmocka/src/math/arithmetic/square_root.c +++ b/test/ztest/unit/math/advanced/functions/test_square_root_ztest.c @@ -1,16 +1,15 @@ // SPDX-License-Identifier: BSD-3-Clause // -// Copyright(c) 2021 Intel Corporation. All rights reserved. +// 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 <stdio.h> +#include <zephyr/ztest.h> #include <stdint.h> -#include <stdarg.h> -#include <stddef.h> -#include <setjmp.h> #include <math.h> -#include <cmocka.h> #include <sof/math/sqrt.h> #include <sof/audio/format.h> @@ -84,79 +83,74 @@ static const double sqrt_ref_table[] = { 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}; -static void test_math_arithmetic_sqrt_fixed(void **state) -{ - (void)state; + 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}; - uint32_t u[252]; +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); - 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 = (double)Q_CONVERT_QTOF(sofm_sqrt_int16(u[i]), 12); diff = fabs(sqrt_ref_table[i] - y); if (diff > CMP_TOLERANCE) { - printf("%s: diff for %.16f: reftbl = %.16f, sqrt = %.16f\n", __func__, - diff, (double)sqrt_ref_table[i], y); - assert_true(diff <= CMP_TOLERANCE); + zassert_true(diff <= CMP_TOLERANCE, + "sqrt delta=%f ref=%f actual=%f index=%d", + diff, sqrt_ref_table[i], y, i); } } } - -int main(void) -{ - const struct CMUnitTest tests[] = { - cmocka_unit_test(test_math_arithmetic_sqrt_fixed) - }; - - cmocka_set_message_output(CM_OUTPUT_TAP); - - return cmocka_run_group_tests(tests, NULL, NULL); -} 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/CMakeLists.txt b/tools/ctl/CMakeLists.txt index 5c4c2dd315b5..ba897c99d126 100644 --- a/tools/ctl/CMakeLists.txt +++ b/tools/ctl/CMakeLists.txt @@ -11,6 +11,16 @@ target_link_libraries(sof-ctl PRIVATE "-lasound" ) +# tools/lib is only needed as a -L link path for a locally built ALSA. +# Do not let CMake also embed it as an ELF RPATH/RUNPATH: it points into +# the build tree and, once stripped to empty on install, leaves an empty +# DT_RUNPATH tag that trips distro ELF security scanners (scanelf reports +# "NULL DT_RUNPATH"). Skip the build RPATH and keep the install RPATH +# empty so no DT_RUNPATH tag is emitted. See thesofproject/sof#10070. +set_target_properties(sof-ctl PROPERTIES + SKIP_BUILD_RPATH TRUE + INSTALL_RPATH "") + target_compile_options(sof-ctl PRIVATE -Wall -Werror ) diff --git a/tools/ctl/ctl.c b/tools/ctl/ctl.c index c669349b68f9..a1966473025a 100644 --- a/tools/ctl/ctl.c +++ b/tools/ctl/ctl.c @@ -121,7 +121,8 @@ static int read_setup(struct ctl_data *ctl_data) int n = 0; FILE *fh; int data_start_int_index = 0; - int data_int_index; + int val_index = 0; + int val_max; /* open input file */ fh = fdopen(ctl_data->in_fd, mode); @@ -144,22 +145,25 @@ static int read_setup(struct ctl_data *ctl_data) } /* reading for ASCII CSV txt */ - data_int_index = data_start_int_index; + /* whole uint32_t values the data area can hold, the trailing bytes of an + * unaligned control size are unusable + */ + val_max = (n_max - abi_size) / (int)sizeof(uint32_t); while (fscanf(fh, "%u", &x) != EOF) { - if (n < n_max) - ctl_data->buffer[data_int_index] = x; + if (val_index < val_max) + ctl_data->buffer[data_start_int_index + val_index] = x; - if (n > 0) + if (val_index > 0) fprintf(stdout, ","); fprintf(stdout, "%u", x); separator = fgetc(fh); while (separator != ',' && separator != EOF) separator = fgetc(fh); - data_int_index++; - n += sizeof(uint32_t); + val_index++; } + n = val_index * (int)sizeof(uint32_t); fprintf(stdout, "\n"); read_done: @@ -503,7 +507,7 @@ static int ctl_set_get(struct ctl_data *ctl_data) ref_size = ctl_data->buffer[BUFFER_SIZE_OFFSET] + BUFFER_TLV_HEADER_BYTES; if (read_size != ref_size) { fprintf(stderr, - "Error: Blob TLV header size %u (plus %lu) does not match with read bytes count %zu.\n", + "Error: Blob TLV header size %u (plus %zu) does not match with read bytes count %zu.\n", ctl_data->buffer[BUFFER_SIZE_OFFSET], BUFFER_TLV_HEADER_BYTES, read_size); return -EINVAL; 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/logger/convert.c b/tools/logger/convert.c index 4e0a6cdc59ae..834ee5a1b243 100644 --- a/tools/logger/convert.c +++ b/tools/logger/convert.c @@ -126,8 +126,14 @@ static const char *format_uid(uint32_t uid_ptr, int use_colors, bool be, bool up const struct sof_uuid_entry *uid_entry; char *str; + /* + * The whole struct sof_uuid_entry is read at uid_ptr, so require that + * many bytes to remain in the uids region; a bare ">=" upper bound + * would accept a pointer whose entry straddles the end of the buffer. + */ if (uid_ptr < uids_dict->base_address || - uid_ptr >= uids_dict->base_address + uids_dict->data_length) { + uid_ptr + sizeof(struct sof_uuid_entry) > + uids_dict->base_address + uids_dict->data_length) { str = calloc(1, strlen(BAD_PTR_STR) + 1 + 6); if (!str) { log_err("can't allocate memory\n"); diff --git a/tools/logger/filter.c b/tools/logger/filter.c index 0943f68d16b2..7a3d53c37246 100644 --- a/tools/logger/filter.c +++ b/tools/logger/filter.c @@ -104,7 +104,7 @@ static char *filter_parse_component_name(char *input_str, struct filter_element */ if (strlen(scan_format_string) == 0) { ret = snprintf(scan_format_string, sizeof(scan_format_string), - "%%%d[^0-9* ]s", UUID_NAME_MAX_LEN); + "%%%d[^0-9* ]s", UUID_NAME_MAX_LEN - 1); if (ret <= 0) return NULL; } diff --git a/tools/plugin/alsaplug/tplg.c b/tools/plugin/alsaplug/tplg.c index 7b05b7ead50a..140a0aac91c6 100644 --- a/tools/plugin/alsaplug/tplg.c +++ b/tools/plugin/alsaplug/tplg.c @@ -999,6 +999,12 @@ static int plug_prepare_widget(snd_sof_plug_t *plug, struct tplg_pcm_info *pcm_i } if (i == pipeline_list->count) { + if (pipeline_list->count >= TPLG_MAX_PCM_PIPELINES) { + SNDERR("error: too many pipelines for PCM, max %d\n", + TPLG_MAX_PCM_PIPELINES); + return -EINVAL; + } + pipeline_list->pipelines[pipeline_list->count] = comp_info->pipe_info; pipeline_list->count++; } diff --git a/tools/plugin/modules/ov_noise_suppression/CMakeLists.txt b/tools/plugin/modules/ov_noise_suppression/CMakeLists.txt index 039e9056ee45..fcf1439d0510 100644 --- a/tools/plugin/modules/ov_noise_suppression/CMakeLists.txt +++ b/tools/plugin/modules/ov_noise_suppression/CMakeLists.txt @@ -39,4 +39,22 @@ set_target_properties(sof_ns INSTALL_RPATH "${sof_install_directory}/alsa-lib" INSTALL_RPATH_USE_LINK_PATH TRUE ) + +# Regression test for the input-shape validation guard. Gated on +# OpenVINO_FOUND alongside the module itself, so a host without +# OpenVINO simply skips it. +add_executable(test_ns_shape_validation test_ns_shape_validation.cpp) +target_link_libraries(test_ns_shape_validation PRIVATE sof_ns_interface) +target_include_directories(test_ns_shape_validation PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR} + ${sof_source_directory}/src/include + ${sof_source_directory}/posix/include + ${sof_source_directory}/src/arch/host/include + ${sof_source_directory}/src/platform/posix/include) +set_target_properties(test_ns_shape_validation PROPERTIES LINKER_LANGUAGE CXX) + +add_test(NAME ns_shape_zero + COMMAND test_ns_shape_validation --dim 0 --dim 480) +add_test(NAME ns_shape_overflow + COMMAND test_ns_shape_validation --dim 9223372036854775807 --dim 1024) endif() diff --git a/tools/plugin/modules/ov_noise_suppression/noise_suppression_interface.cpp b/tools/plugin/modules/ov_noise_suppression/noise_suppression_interface.cpp index fa74736d062f..6e8356a7440e 100644 --- a/tools/plugin/modules/ov_noise_suppression/noise_suppression_interface.cpp +++ b/tools/plugin/modules/ov_noise_suppression/noise_suppression_interface.cpp @@ -12,6 +12,7 @@ #include "noise_suppression_interface.h" #define NS_MAX_SOURCE_CHANNELS 2 +#define NS_MAX_SHAPE_DIM (1u << 24) extern "C" { struct ns_data { @@ -85,6 +86,9 @@ extern "C" { nd->infer_request[i] = compiled_model.create_infer_request(); nd->inp_shape = nd->model->input("input").get_shape(); + for (auto dim : nd->inp_shape) + if (!dim || dim > NS_MAX_SHAPE_DIM) + return -EINVAL; return 0; } @@ -141,6 +145,9 @@ extern "C" { ov::Shape state_shape; state_shape = nd->model->input(inp_state_name).get_shape(); + for (auto dim : state_shape) + if (!dim || dim > NS_MAX_SHAPE_DIM) + return -EINVAL; if (nd->iter > 0) { /* * set input state by corresponding output state from prev diff --git a/tools/plugin/modules/ov_noise_suppression/test_ns_shape_validation.cpp b/tools/plugin/modules/ov_noise_suppression/test_ns_shape_validation.cpp new file mode 100644 index 000000000000..35a224581423 --- /dev/null +++ b/tools/plugin/modules/ov_noise_suppression/test_ns_shape_validation.cpp @@ -0,0 +1,149 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// Copyright(c) 2026 Intel Corporation. All rights reserved. +// +// Author: Pally Mediratta <mediratta01.pally@gmail.com> + +#include <cstdio> +#include <cstdlib> +#include <fstream> +#include <string> +#include <vector> +#include <unistd.h> + +#include "noise_suppression_interface.h" + +static void usage(const char *prog) +{ + std::printf( +"Usage: %s [--model PATH | --dim N [--dim N ...]] [--expect ok|reject]\n" +"\n" +"Regression test for the OpenVINO noise-suppression plugin's input-shape\n" +"validation (commit \"tools: plugin: ov_noise_suppression: validate model\n" +"input shape dimensions\"). Invokes ov_ns_init() against either:\n" +"\n" +" --model PATH a caller-supplied OpenVINO model XML file, or\n" +" --dim N [--dim N ...] a minimal synthesized model XML built from the\n" +" given input port dimensions and written to a\n" +" temporary file.\n" +"\n" +" --expect ok|reject expected outcome (default: reject).\n" +" \"ok\": ov_ns_init() must return 0.\n" +" \"reject\": ov_ns_init() must return non-zero.\n" +"\n" +"Returns 0 on PASS, 1 on FAIL, 2 on usage error.\n" +"\n" +"Examples:\n" +" %s --dim 0 --dim 480 # zero dim, expect reject\n" +" %s --dim 9223372036854775807 --dim 1024 # overflow, expect reject\n" +" %s --model real_model.xml --expect ok # real model, expect accept\n", + prog, prog, prog, prog); +} + +static int write_mock_model(const std::string &path, + const std::vector<long long> &dims) +{ + std::ofstream f(path); + if (!f) + return -1; + + f << "<?xml version=\"1.0\"?><net><layers>" + "<layer id=\"0\" name=\"input\" type=\"Parameter\">" + "<output><port id=\"0\" precision=\"FP32\">"; + for (auto d : dims) + f << "<dim>" << d << "</dim>"; + f << "</port></output></layer></layers></net>"; + return f ? 0 : -1; +} + +int main(int argc, char **argv) +{ + std::string model_path; + std::vector<long long> dims; + bool expect_reject = true; + + for (int i = 1; i < argc; i++) { + std::string a = argv[i]; + if (a == "-h" || a == "--help") { + usage(argv[0]); + return 0; + } + if (a == "--model" && i + 1 < argc) { + model_path = argv[++i]; + } else if (a == "--dim" && i + 1 < argc) { + dims.push_back(std::strtoll(argv[++i], nullptr, 0)); + } else if (a == "--expect" && i + 1 < argc) { + std::string v = argv[++i]; + if (v == "ok") { + expect_reject = false; + } else if (v == "reject") { + expect_reject = true; + } else { + std::fprintf(stderr, + "unknown --expect value: %s\n", + v.c_str()); + usage(argv[0]); + return 2; + } + } else { + std::fprintf(stderr, "unknown or incomplete arg: %s\n", + a.c_str()); + usage(argv[0]); + return 2; + } + } + + std::string scratch; + if (model_path.empty()) { + if (dims.empty()) { + std::fprintf(stderr, + "must supply --model PATH or one or more --dim N\n"); + usage(argv[0]); + return 2; + } + + char tmpl[] = "/tmp/ns_shape_test_XXXXXX.xml"; + int fd = mkstemps(tmpl, 4); + if (fd < 0) { + std::perror("mkstemps"); + return 2; + } + close(fd); + scratch = tmpl; + if (write_mock_model(scratch, dims) != 0) { + std::fprintf(stderr, "failed to write mock model %s\n", + scratch.c_str()); + std::remove(scratch.c_str()); + return 2; + } + model_path = scratch; + } + + struct noise_suppression_data *nd = + (struct noise_suppression_data *)std::calloc(1, sizeof(*nd)); + if (!nd) { + std::perror("calloc"); + if (!scratch.empty()) + std::remove(scratch.c_str()); + return 2; + } + + int ret = ov_ns_init(nd, model_path.c_str()); + + if (!scratch.empty()) + std::remove(scratch.c_str()); + + bool rejected = (ret != 0); + bool pass = (rejected == expect_reject); + + std::printf("%s: ov_ns_init() returned %d (%s), expected %s\n", + pass ? "PASS" : "FAIL", + ret, + rejected ? "rejected" : "accepted", + expect_reject ? "reject" : "ok"); + + ov_ns_free(nd); + std::free(nd); + + return pass ? 0 : 1; +} diff --git a/tools/probes/probes_demux.c b/tools/probes/probes_demux.c index a411eef117b9..7b875cf9bb8a 100644 --- a/tools/probes/probes_demux.c +++ b/tools/probes/probes_demux.c @@ -14,6 +14,7 @@ #include <stdint.h> #include <stdlib.h> #include <string.h> +#include <sys/types.h> #include <unistd.h> #include <ipc/probe_dma_frame.h> @@ -23,6 +24,7 @@ #define APP_NAME "sof-probes" #define PACKET_MAX_SIZE 4096 /**< Size limit for probe data packet */ +#define PACKET_DATA_SIZE_MAX (16u * 1024u * 1024u) /**< Sanity limit for packet data size */ #define DATA_READ_LIMIT 4096 /**< Data limit for file read */ #define FILES_LIMIT 32 /**< Maximum num of probe output files */ #define FILE_PATH_LIMIT 128 /**< Path limit for probe output files */ @@ -125,7 +127,17 @@ int init_wave(struct dma_frame_parser *p, uint32_t buffer_id, uint32_t format) p->files[i].header.fmt.subchunk_size = 16; p->files[i].header.fmt.audio_format = 1; p->files[i].header.fmt.num_channels = ((format & PROBE_MASK_NB_CHANNELS) >> PROBE_SHIFT_NB_CHANNELS) + 1; - p->files[i].header.fmt.sample_rate = sample_rate[(format & PROBE_MASK_SAMPLE_RATE) >> PROBE_SHIFT_SAMPLE_RATE]; + /* the sample-rate field is 4 bits (0-15) but the table has fewer + * entries; fall back to a valid default (48000 Hz) for an out-of-range + * index so the read stays in bounds and the WAV header keeps a usable + * rate + */ + uint32_t rate_idx = (format & PROBE_MASK_SAMPLE_RATE) >> PROBE_SHIFT_SAMPLE_RATE; + + if (rate_idx >= sizeof(sample_rate) / sizeof(sample_rate[0])) + p->files[i].header.fmt.sample_rate = 48000; + else + p->files[i].header.fmt.sample_rate = sample_rate[rate_idx]; p->files[i].header.fmt.bits_per_sample = (((format & PROBE_MASK_CONTAINER_SIZE) >> PROBE_SHIFT_CONTAINER_SIZE) + 1) * 8; p->files[i].header.fmt.byte_rate = p->files[i].header.fmt.sample_rate * p->files[i].header.fmt.num_channels * @@ -194,6 +206,12 @@ int process_sync(struct dma_frame_parser *p) { struct probe_data_packet *temp_packet; + if (p->packet->data_size_bytes > PACKET_DATA_SIZE_MAX) { + fprintf(stderr, "error: packet data size %u exceeds maximum %u\n", + p->packet->data_size_bytes, PACKET_DATA_SIZE_MAX); + return -EINVAL; + } + /* request to copy data_size from probe packet and 64-bit checksum */ p->total_data_to_copy = p->packet->data_size_bytes + sizeof(uint64_t); 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/acp_7_x.toml b/tools/rimage/config/acp_7_x.toml new file mode 100644 index 000000000000..7787bb3c6e1c --- /dev/null +++ b/tools/rimage/config/acp_7_x.toml @@ -0,0 +1,20 @@ +version = [1, 0] # use simple file write + +[adsp] +name = "acp_7_x" + +[[adsp.mem_zone]] +type = "IRAM" +base = "0x7F000000" +size = "0x20000" +host_offset = "0x0" +[[adsp.mem_zone]] +type = "DRAM" +base = "0xE0000000" +size = "0x20000" +host_offset = "0x0" +[[adsp.mem_zone]] +type = "SRAM" +base = "0x6000C000" +size = "0x274000" +host_offset = "0x0" \ No newline at end of file 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..faefbb8acadb 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,17 @@ #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 + +#if defined(CONFIG_COMP_PHASE_VOCODER) || defined(LLEXT_FORCE_ALL_MODULAR) +#include <audio/phase_vocoder/phase_vocoder.toml> +#endif + [module] count = __COUNTER__ diff --git a/tools/rimage/config/mtl.toml.h b/tools/rimage/config/mtl.toml.h index ac4287051b07..62672713dbae 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,17 @@ #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 + +#if defined(CONFIG_COMP_PHASE_VOCODER) || defined(LLEXT_FORCE_ALL_MODULAR) +#include <audio/phase_vocoder/phase_vocoder.toml> +#endif + [module] count = __COUNTER__ diff --git a/tools/rimage/config/nvl.toml.h b/tools/rimage/config/nvl.toml.h index eafe7501ac8d..cec992a4a2af 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,81 @@ 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_TEMPLATE) || defined(LLEXT_FORCE_ALL_MODULAR) +#include <audio/template/template.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_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 + +#if defined(CONFIG_COMP_PHASE_VOCODER) || defined(LLEXT_FORCE_ALL_MODULAR) +#include <audio/phase_vocoder/phase_vocoder.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..e7a53d1b1820 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,17 @@ 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 + +#if defined(CONFIG_COMP_PHASE_VOCODER) || defined(LLEXT_FORCE_ALL_MODULAR) +#include <audio/phase_vocoder/phase_vocoder.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..f239e063a459 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 @@ -122,6 +126,10 @@ index = __COUNTER__ #include <audio/google/google_rtc_audio_processing.toml> #endif +#if defined(CONFIG_COMP_GOOGLE_HOTWORD_DETECT) || defined(LLEXT_FORCE_ALL_MODULAR) +#include <audio/google/google_hotword_detect.toml> +#endif + #if defined(CONFIG_COMP_TDFB) || defined(LLEXT_FORCE_ALL_MODULAR) #include <audio/tdfb/tdfb.toml> #endif @@ -146,5 +154,17 @@ 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 + +#if defined(CONFIG_COMP_PHASE_VOCODER) || defined(LLEXT_FORCE_ALL_MODULAR) +#include <audio/phase_vocoder/phase_vocoder.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/adsp_config.c b/tools/rimage/src/adsp_config.c index 6cc7434e74bc..931c77e10403 100644 --- a/tools/rimage/src/adsp_config.c +++ b/tools/rimage/src/adsp_config.c @@ -2052,7 +2052,7 @@ static int parse_adsp_config_v1_5(const toml_table_t *toml, struct image *image) /* assign correct write functions */ out->write_firmware = man_write_fw_v1_5_sue; - out->write_firmware_meu = man_write_fw_meu_v1_5; + out->write_firmware_meu = NULL; out->verify_firmware = ri_manifest_verify_v1_5; /* parse others sibtables */ @@ -2235,7 +2235,7 @@ static int parse_adsp_config_ace_v1_5(const toml_table_t *toml, struct image *im /* assign correct write functions */ out->write_firmware = man_write_fw_ace_v1_5; - out->write_firmware_meu = man_write_fw_meu_v2_5; + out->write_firmware_meu = NULL; out->verify_firmware = ri_manifest_verify_v2_5; /* version array has already been parsed, so increment ctx.array_cnt */ diff --git a/tools/rimage/src/elf_file.c b/tools/rimage/src/elf_file.c index 991766d45062..c68a7d3af4e6 100644 --- a/tools/rimage/src/elf_file.c +++ b/tools/rimage/src/elf_file.c @@ -95,13 +95,13 @@ static struct name_val p_flags[] = { * * @param elf elf file structure * @param msg error message - * @param error error code to return - * @return error code + * @param error positive errno value, or an already negative error code + * @return negative error code */ static int elf_error(const struct elf_file *elf, const char *msg, int error) { fprintf(stderr, "Error: %s: %s\n", elf->filename, msg); - return -error; + return error < 0 ? error : -error; } /** @@ -531,10 +531,23 @@ int elf_strings_read_by_index(const struct elf_file *elf, int index, struct elf_ int elf_strings_get(const struct elf_strings *strings, int index, char **str) { - if (index >= strings->section.header.data.size) + size_t size = strings->section.header.data.size; + const char *base = (const char *)strings->section.data; + + if (index < 0 || (size_t)index >= size) + return -EINVAL; + + /* + * A crafted ELF may provide a string table that is not NUL-terminated; + * make sure a terminator exists within the section before strdup() so + * it cannot read past the end of the mapped section. + */ + if (strnlen(base + index, size - index) == size - index) { + fprintf(stderr, "error: unterminated string in string table\n"); return -EINVAL; + } - *str = strdup((const char *)strings->section.data + index); + *str = strdup(base + index); if (!*str) return -ENOMEM; diff --git a/tools/rimage/src/ext_manifest.c b/tools/rimage/src/ext_manifest.c index 03a157c08397..8beaa4943a7b 100644 --- a/tools/rimage/src/ext_manifest.c +++ b/tools/rimage/src/ext_manifest.c @@ -69,6 +69,12 @@ static int ext_man_validate(uint32_t section_size, const void *section_data) /* copy each head to local struct to omit memory align issues */ while (offset < section_size) { + /* make sure a whole header remains before copying it out */ + if (offset + sizeof(head) > section_size) { + fprintf(stderr, + "error: extended manifest header straddles section end\n"); + return -EINVAL; + } memcpy(&head, &sbuf[offset], sizeof(head)); fprintf(stdout, "Extended manifest found module, type: 0x%04X size: 0x%04X (%4d) offset: 0x%04X\n", head.type, head.elem_size, head.elem_size, offset); @@ -150,10 +156,12 @@ int ext_man_write(struct image *image) /* validate metadata section */ ret = ext_man_validate(ext_man->full_size - ext_man->header_size, (char *)ext_man + ext_man->header_size); - if (ret) { - ret = -errno; + if (ret) + /* ext_man_validate() already returns a negative errno; do not + * overwrite it with -errno, which is not set on validation + * failure and would mask the error + */ goto out; - } /* write extended metadata to file */ count = fwrite(ext_man, 1, ext_man->full_size, image->out_ext_man_fd); diff --git a/tools/rimage/src/hash.c b/tools/rimage/src/hash.c index 0604670e9c6e..9aff3ae3625a 100644 --- a/tools/rimage/src/hash.c +++ b/tools/rimage/src/hash.c @@ -174,7 +174,10 @@ int hash_single(const void *data, size_t size, const EVP_MD *algo, void *output, if (algo_out_size <= 0) return -EINVAL; - if (output_len > algo_out_size) + /* EVP_Digest writes algo_out_size bytes into output, so the buffer + * must be at least that large; reject an undersized output buffer + */ + if (output_len < (size_t)algo_out_size) return -ENOBUFS; if (!EVP_Digest(data, size, output, NULL, algo, NULL)) { 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/manifest.c b/tools/rimage/src/manifest.c index 6f3a161e37a7..db89d5f699b0 100644 --- a/tools/rimage/src/manifest.c +++ b/tools/rimage/src/manifest.c @@ -12,6 +12,7 @@ #include <unistd.h> #include <errno.h> #include <string.h> +#include <stdbool.h> #include <time.h> #include <sys/time.h> @@ -26,6 +27,33 @@ #include <rimage/misc_utils.h> #include <rimage/hash.h> +static bool cse_header_is_valid(const struct image *image, const void *buffer, size_t size) +{ + if (image->adsp->man_v2_5 || image->adsp->man_ace_v1_5) { + const struct CsePartitionDirHeader_v2_5 *cse_hdr = buffer; + + if (size < sizeof(*cse_hdr)) + return false; + + return cse_hdr->header_marker == CSE_HEADER_MAKER && + cse_hdr->nb_entries == MAN_CSE_PARTS && + cse_hdr->header_length >= sizeof(*cse_hdr); + } + + if (image->adsp->man_v1_5 || image->adsp->man_v1_5_sue || image->adsp->man_v1_8) { + const struct CsePartitionDirHeader *cse_hdr = buffer; + + if (size < sizeof(*cse_hdr)) + return false; + + return cse_hdr->header_marker == CSE_HEADER_MAKER && + cse_hdr->nb_entries == MAN_CSE_PARTS && + cse_hdr->header_length >= sizeof(*cse_hdr); + } + + return false; +} + static int man_open_rom_file(struct image *image) { uint32_t size; @@ -212,6 +240,7 @@ static void man_get_section_manifest(struct image *image, man_module->type.domain_dp = sof_mod->module.type.domain_dp; man_module->type.domain_ll = sof_mod->module.type.domain_ll; man_module->type.load_type = sof_mod->module.type.load_type; + man_module->type.user_mode = sof_mod->module.type.user_mode; /* text segment */ segment = &man_module->segment[SOF_MAN_SEGMENT_TEXT]; @@ -513,6 +542,11 @@ static int man_module_create_reloc(struct image *image, struct manifest_module * struct elf_section section; int err; + if (!modules) { + fprintf(stderr, "error: relocatable image requires module configuration\n"); + return -EINVAL; + } + /* load in module manifest data */ err = elf_section_read_by_name(&module->file.elf, ".module", §ion); if (err) { @@ -533,6 +567,20 @@ static int man_module_create_reloc(struct image *image, struct manifest_module * return -ENOEXEC; } + /* + * n_mod comes from the (potentially untrusted) ELF and each manifest + * consumes a sof_man_module descriptor slot written into fw_image. + * Bound the cumulative count across all input modules (this ELF adds + * n_mod to the running output_mod_cfg_count) so a crafted .module + * section cannot overflow the fixed manifest descriptor array. + */ + if (modules->output_mod_cfg_count + n_mod > MAX_MODULES) { + fprintf(stderr, "error: too many module manifests (%u + %u > %u).\n", + modules->output_mod_cfg_count, n_mod, MAX_MODULES); + elf_section_free(§ion); + return -ENOEXEC; + } + unsigned int i; for (i = 0, sof_mod = section.data; i < n_mod; i++, sof_mod++) { @@ -1629,8 +1677,8 @@ int man_write_fw_ace_v1_5(struct image *image) int verify_image(struct image *image) { FILE *in_file; - int ret; - void *buffer; + int ret = -EINVAL; + void *buffer = NULL; size_t size, read, i; /* is verify supported for target ? */ @@ -1663,11 +1711,25 @@ int verify_image(struct image *image) ret = file_error("unable to read whole file", image->verify_file); goto out; } - for (i = 0; i < size; i += sizeof(uint32_t)) { + for (i = 0; i + sizeof(uint32_t) <= size; i += sizeof(uint32_t)) { /* find CSE header marker "$CPD" */ - if (*(uint32_t *)(buffer + i) == CSE_HEADER_MAKER) { + if (cse_header_is_valid(image, buffer + i, size - i)) { image->fw_image = buffer + i; + /* size of the image from the CSE header to the end of the + * file, used by v1.5 verification and the signed-payload + * bounds checks + */ + image->image_end = size - i; ret = image->adsp->verify_firmware(image); + /* verify_firmware() returns 1 = valid, 0 = invalid and + * < 0 = error (OpenSSL RSA_verify semantics); map valid + * to 0 and invalid to -EINVAL, and leave a < 0 error code + * as-is, so the CLI exit code reflects the result + */ + if (ret == 1) + ret = 0; + else if (ret == 0) + ret = -EINVAL; goto out; } } @@ -1675,9 +1737,14 @@ int verify_image(struct image *image) /* no header found */ fprintf(stderr, "error: could not find valid CSE header $CPD in %s\n", image->verify_file); + ret = -EINVAL; out: + free(buffer); fclose(in_file); - return 0; + /* propagate verification result so callers (and the exit code) can + * detect a failed/missing verification instead of always seeing success + */ + return ret; } @@ -1716,16 +1783,20 @@ int resign_image(struct image *image) fclose(in_file); - for (i = 0; i < size; i += sizeof(uint32_t)) { + for (i = 0; i + sizeof(uint32_t) <= size; i += sizeof(uint32_t)) { /* find CSE header marker "$CPD" */ - if (*(uint32_t *)(buffer + i) == CSE_HEADER_MAKER) { + if (cse_header_is_valid(image, buffer + i, size - i)) { image->fw_image = buffer + i; break; } } - if (i >= size) { - fprintf(stderr, "error: didn't found header marker %d\n", i); + /* the scan stops a full marker before the end, so a trailing partial word + * leaves the loop index below the size; test the result of the scan itself + */ + if (!image->fw_image) { + fprintf(stderr, "error: could not find valid CSE header $CPD in %s\n", + image->in_file); ret = -EINVAL; goto out; } @@ -1780,7 +1851,26 @@ int resign_image(struct image *image) goto out; } - man_write_fw_mod(image); + ret = man_write_fw_mod(image); + if (ret < 0) + goto out; + + /* fclose() closes the stream even when it reports an error, so clear the + * handle first to stop the caller's cleanup from closing it a second time. + */ + ret = fclose(image->out_fd); + image->out_fd = NULL; + if (ret) { + ret = file_error("unable to close file after signing", image->out_file); + goto out; + } + + /* validate the re-signed output with the same private key */ + image->verify_file = image->out_file; + ret = verify_image(image); + image->verify_file = NULL; + if (ret < 0) + unlink(image->out_file); out: free(buffer); 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/pkcs1_5.c b/tools/rimage/src/pkcs1_5.c index de063ee03bec..cbd31bc101bf 100644 --- a/tools/rimage/src/pkcs1_5.c +++ b/tools/rimage/src/pkcs1_5.c @@ -929,9 +929,28 @@ int ri_manifest_verify_v1_8(struct image *image) MAN_RSA_SIGNATURE_LEN); char *const data2 = (char *)man + MAN_SIG_PKG_OFFSET_V1_8; + + /* css.size and css.header_len come from the (untrusted) image; reject + * a reversed ordering that would underflow the unsigned size2 below. + */ + if (man->css.size < man->css.header_len) { + fprintf(stderr, "error: invalid css size %u < header_len %u\n", + man->css.size, man->css.header_len); + return -EINVAL; + } + unsigned const size2 = (man->css.size - man->css.header_len) * sizeof(uint32_t); + /* size2 is derived from untrusted css fields and is hashed starting at + * data2; make sure that range stays within the verified image buffer. + */ + if (image->image_end < MAN_SIG_PKG_OFFSET_V1_8 || + size2 > image->image_end - MAN_SIG_PKG_OFFSET_V1_8) { + fprintf(stderr, "error: signed payload size 0x%x exceeds image\n", size2); + return -EINVAL; + } + return pkcs_v1_5_verify_man_v1_8(image, man, data1, size1, data2, size2); } @@ -946,9 +965,28 @@ int ri_manifest_verify_v2_5(struct image *image) MAN_RSA_SIGNATURE_LEN_2_5); char *const data2 = (char *)man + MAN_SIG_PKG_OFFSET_V2_5; + + /* css.size and css.header_len come from the (untrusted) image; reject + * a reversed ordering that would underflow the unsigned size2 below. + */ + if (man->css.size < man->css.header_len) { + fprintf(stderr, "error: invalid css size %u < header_len %u\n", + man->css.size, man->css.header_len); + return -EINVAL; + } + unsigned const size2 = (man->css.size - man->css.header_len) * sizeof(uint32_t); + /* size2 is derived from untrusted css fields and is hashed starting at + * data2; make sure that range stays within the verified image buffer. + */ + if (image->image_end < MAN_SIG_PKG_OFFSET_V2_5 || + size2 > image->image_end - MAN_SIG_PKG_OFFSET_V2_5) { + fprintf(stderr, "error: signed payload size 0x%x exceeds image\n", size2); + return -EINVAL; + } + return pkcs_v1_5_verify_man_v2_5(image, man, data1, size1, data2, size2); } diff --git a/tools/rimage/src/rimage.c b/tools/rimage/src/rimage.c index fb8aba95611d..0b41905944c2 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> @@ -32,8 +33,10 @@ static void usage(char *name) fprintf(stdout, "\t -e build extended manifest\n"); fprintf(stdout, "\t -l build loadable modules image (don't treat the first module as a bootloader)\n"); fprintf(stdout, "\t -y verify signed file\n"); - fprintf(stdout, "\t -q resign binary\n"); + fprintf(stdout, "\t -q resign binary from infile and validate the output signature\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; @@ -128,6 +144,11 @@ int main(int argc, char *argv[]) return -EINVAL; } + if (image.in_file && image.verify_file) { + fprintf(stderr, "error: resign and verify modes are mutually exclusive\n"); + return -EINVAL; + } + /* firmware version: major.minor.micro */ if (image.fw_ver_string) { ret = sscanf(image.fw_ver_string, "%hu.%hu.%hu", @@ -153,6 +174,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) { @@ -177,6 +202,12 @@ int main(int argc, char *argv[]) goto out; } + if (image.meu_offset && !image.adsp->write_firmware_meu) { + fprintf(stderr, "error: MEU signing is not supported for this target\n"); + ret = -EINVAL; + goto out; + } + /* set IMR Type and the PV bit in found machine definition */ if (image.adsp->man_v1_8) { if (imr_type_override) @@ -225,7 +256,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. */ @@ -248,7 +280,6 @@ int main(int argc, char *argv[]) /* process and write output */ if (image.meu_offset) { - assert(image.adsp->write_firmware_meu); ret = image.adsp->write_firmware_meu(&image); } else { assert(image.adsp->write_firmware); diff --git a/tools/sof_ri_info/sof_ri_info.py b/tools/sof_ri_info/sof_ri_info.py index be78d7aebf50..5fca3a42a3be 100755 --- a/tools/sof_ri_info/sof_ri_info.py +++ b/tools/sof_ri_info/sof_ri_info.py @@ -846,14 +846,25 @@ def parse_cse_manifest(reader): nb_entries = reader.read_dw() reader.info('# of entries {}'.format(nb_entries)) hdr.add_a(Adec('nb_entries', nb_entries)) - # read version (1byte for header ver and 1 byte for entry ver) - ver = reader.read_w() - hdr.add_a(Ahex('header_version', ver)) + # read version bytes + hdr.add_a(Ahex('header_version', reader.read_b())) + hdr.add_a(Ahex('entry_version', reader.read_b())) header_length = reader.read_b() hdr.add_a(Ahex('header_length', header_length)) - hdr.add_a(Ahex('checksum', reader.read_b())) + legacy_or_unused = reader.read_b() + # The v2.5 header (length 20) moves the checksum to a trailing CRC32 dword + # and leaves this byte unused. The legacy v1.x header (length 16) keeps the + # single-byte BSD checksum here, so discriminate on the v2.5 header length. + if header_length >= 20: + hdr.add_a(Ahex('not_used', legacy_or_unused)) + else: + hdr.add_a(Ahex('checksum', legacy_or_unused)) hdr.add_a(Astring('partition_name', reader.read_string(4))) + # CSE v2.5 extends header with a CRC32 checksum dword + if header_length >= 20: + hdr.add_a(Ahex('checksum32', reader.read_dw())) + reader.set_offset(cse_mft.file_offset + header_length) # Read entries nb_index = 0 @@ -862,13 +873,13 @@ def parse_cse_manifest(reader): entry_name = reader.read_string(12) entry_offset = reader.read_dw() entry_length = reader.read_dw() - # reserved field - reader.read_dw() + entry_reserved = reader.read_dw() hdr_entry = Component('cse_hdr_entry', 'Entry', reader.get_offset()) hdr_entry.add_a(Astring('entry_name', entry_name)) hdr_entry.add_a(Ahex('entry_offset', entry_offset)) hdr_entry.add_a(Ahex('entry_length', entry_length)) + hdr_entry.add_a(Ahex('entry_reserved', entry_reserved)) hdr.add_comp(hdr_entry) assert cse_mft.file_offset == reader.ext_mft_length @@ -916,12 +927,16 @@ def parse_css_manifest_4(css_mft, reader, size_limit): hdr = Component('css_mft_hdr', 'Header', reader.get_offset()) css_mft.add_comp(hdr) - hdr.add_a(Auint('type', reader.read_dw())) + header_type = reader.read_dw() + hdr.add_a(Auint('type', header_type)) + hdr.add_a(Auint('header_type', header_type)) header_len_dw = reader.read_dw() hdr.add_a(Auint('header_len_dw', header_len_dw)) hdr.add_a(Auint('header_version', reader.read_dw())) hdr.add_a(Auint('reserved0', reader.read_dw(), 'red')) - hdr.add_a(Ahex('mod_vendor', reader.read_dw())) + module_vendor = reader.read_dw() + hdr.add_a(Ahex('mod_vendor', module_vendor)) + hdr.add_a(Ahex('module_vendor', module_vendor)) date_start = reader.get_offset() hdr.add_a(Auint('date_start', date_start)) hdr.add_a(Adate('date', hex(reader.read_dw()))) @@ -930,10 +945,17 @@ def parse_css_manifest_4(css_mft, reader, size_limit): hdr.add_a(Auint('size', size)) hdr.add_a(Astring('header_id', reader.read_string(4))) hdr.add_a(Auint('padding', reader.read_dw())) - hdr.add_a(Aversion('fw_version', reader.read_w(), reader.read_w(), - reader.read_w(), reader.read_w())) + fw_major = reader.read_w() + fw_minor = reader.read_w() + fw_hotfix = reader.read_w() + fw_build = reader.read_w() + hdr.add_a(Auint('fw_major_version', fw_major)) + hdr.add_a(Auint('fw_minor_version', fw_minor)) + hdr.add_a(Auint('fw_hotfix_version', fw_hotfix)) + hdr.add_a(Auint('fw_build_version', fw_build)) + hdr.add_a(Aversion('fw_version', fw_major, fw_minor, fw_hotfix, fw_build)) hdr.add_a(Auint('svn', reader.read_dw())) - reader.read_bytes(18*4) + hdr.add_a(Abytes('reserved1', reader.read_bytes(18*4))) modulus_size = reader.read_dw() hdr.add_a(Adec('modulus_size', modulus_size)) exponent_size = reader.read_dw() @@ -965,56 +987,196 @@ def parse_css_manifest_4(css_mft, reader, size_limit): if ext_type == 0xffffffff: continue reader.set_offset(reader.get_offset() - 4) - css_mft.add_comp(parse_mft_extension(reader, ext_idx)) + css_mft.add_comp(parse_mft_extension(reader, ext_idx, size_limit)) ext_idx += 1 - assert reader.get_offset() == size_limit # wrong extension length + if reader.get_offset() != size_limit: + reader.info('warning: CSS extension parser finished at 0x{:x}, expected 0x{:x}; clamping'.format( + reader.get_offset(), size_limit)) + reader.set_offset(size_limit) css_mft.length = reader.get_offset() - css_mft.file_offset return css_mft -def parse_mft_extension(reader, ext_id): +def parse_mft_extension(reader, ext_id, max_end=None): """ Parses mft extension from sof binary """ begin_off = reader.get_offset() ext_type = reader.read_dw() ext_len = reader.read_dw() - if ext_type == 15: + ext_end = begin_off + ext_len + if max_end is not None and ext_end > max_end: + reader.info('warning: extension 0x{:x} length 0x{:x} exceeds CSS entry end; truncating'.format(ext_type, ext_len)) + ext_end = max_end + if ext_type == 3: + reader.info("Partition info extension") + ext = PartitionInfoExtension(ext_id, reader.get_offset()-8) + ext.add_a(Astring('name', reader.read_string(4))) + ext.add_a(Auint('partition_length', reader.read_dw())) + ext.add_a(Abytes('hash', reader.read_bytes(32))) + ext.add_a(Auint('vcn', reader.read_dw())) + ext.add_a(Auint('part_version', reader.read_dw())) + ext.add_a(Auint('fmt_version', reader.read_dw())) + ext.add_a(Auint('instance_id', reader.read_dw())) + ext.add_a(Auint('part_flags', reader.read_dw())) + ext.add_a(Abytes('reserved', reader.read_bytes(20), 'red')) + + mod_idx = 0 + while reader.get_offset() < ext_end: + mod = Component('partition_info_module_{}'.format(mod_idx), + 'Partition Info Module', reader.get_offset()) + mod.add_a(Astring('name', chararr_to_string(reader.read_bytes(12), 12))) + mod.add_a(Auint('type', reader.read_b())) + mod.add_a(Abytes('reserved', reader.read_bytes(3), 'red')) + mod.add_a(Auint('meta_size', reader.read_dw())) + mod.add_a(Abytes('hash', reader.read_bytes(32))) + ext.add_comp(mod) + mod_idx += 1 + + if reader.get_offset() > ext_end: + raise Exception('Malformed partition info extension length') + if reader.get_offset() < ext_end: + reader.ff_data(ext_end - reader.get_offset()) + elif ext_type == 15: reader.info("Plat Fw Auth extension") ext = PlatFwAuthExtension(ext_id, reader.get_offset()-8) ext.add_a(Astring('name', reader.read_string(4))) ext.add_a(Auint('vcn', reader.read_dw())) ext.add_a(Abytes('bitmap', reader.read_bytes(16), 'red')) ext.add_a(Auint('svn', reader.read_dw())) + + # Signed package info extension common fields + ext.add_a(Auint('fw_type', reader.read_b())) + ext.add_a(Auint('fw_sub_type', reader.read_b())) + ext.add_a(Abytes('reserved', reader.read_bytes(14), 'red')) + + mod_idx = 0 + while reader.get_offset() < ext_end: + mod = Component('signed_pkg_module_{}'.format(mod_idx), + 'Signed Package Module', reader.get_offset()) + mod.add_a(Astring('name', chararr_to_string(reader.read_bytes(12), 12))) + mod.add_a(Auint('type', reader.read_b())) + mod.add_a(Auint('hash_algo', reader.read_b())) + hash_size = reader.read_w() + mod.add_a(Auint('hash_size', hash_size)) + mod.add_a(Auint('meta_size', reader.read_dw())) + mod.add_a(Abytes('hash', reader.read_bytes(hash_size))) + ext.add_comp(mod) + mod_idx += 1 + + if reader.get_offset() > ext_end: + raise Exception('Malformed signed package extension length') + if reader.get_offset() < ext_end: + reader.ff_data(ext_end - reader.get_offset()) + elif ext_type == 0x16: + reader.info("Info extension 0x16") + ext = InfoExtension0x16(ext_id, reader.get_offset()-8) + ext.add_a(Astring('name', reader.read_string(4))) + ext.add_a(Auint('size', reader.read_dw())) + ext.add_a(Auint('data0', reader.read_dw())) + ext.add_a(Auint('data1', reader.read_dw())) + ext.add_a(Auint('data2', reader.read_dw())) + ext.add_a(Auint('data3', reader.read_dw())) + ext.add_a(Auint('data4', reader.read_dw())) + ext.add_a(Abytes('hash', reader.read_bytes(48))) + ext.add_a(Auint('data1_0', reader.read_dw())) + ext.add_a(Auint('data1_1', reader.read_dw())) + ext.add_a(Auint('data1_2', reader.read_dw())) + ext.add_a(Auint('data1_3', reader.read_dw())) + ext.add_a(Auint('data1_4', reader.read_dw())) read_len = reader.get_offset() - begin_off - reader.ff_data(ext_len - read_len) + effective_len = ext_end - begin_off + if read_len < effective_len: + reader.ff_data(effective_len - read_len) elif ext_type == 17: reader.info("ADSP metadata file extension") ext = AdspMetadataFileExt(ext_id, reader.get_offset()-8) ext.add_a(Auint('adsp_imr_type', reader.read_dw(), 'red')) - # skip reserved part - reader.read_bytes(16) - reader.read_dw() - reader.read_dw() - # - ext.add_a(Auint('version', reader.read_dw())) - ext.add_a(Abytes('sha_hash', reader.read_bytes(32))) - ext.add_a(Auint('base_offset', reader.read_dw())) - ext.add_a(Auint('limit_offset', reader.read_dw())) - ext.add_a(Abytes('attributes', reader.read_bytes(16))) + ext.add_a(Abytes('reserved', reader.read_bytes(16), 'red')) + + comp_desc = Component('adsp_comp_desc', 'Component Descriptor', + reader.get_offset()) + comp_desc_res0 = reader.read_dw() + comp_desc_res1 = reader.read_dw() + comp_desc_ver = reader.read_dw() + comp_desc.add_a(Auint('reserved0', comp_desc_res0)) + comp_desc.add_a(Auint('reserved1', comp_desc_res1)) + comp_desc.add_a(Auint('version', comp_desc_ver)) + ext.add_a(Auint('comp_desc_reserved0', comp_desc_res0)) + ext.add_a(Auint('comp_desc_reserved1', comp_desc_res1)) + ext.add_a(Auint('version', comp_desc_ver)) + + remaining = ext_end - reader.get_offset() + if remaining >= 72: + hash_len = 48 + else: + hash_len = 32 + + comp_hash = reader.read_bytes(hash_len) + comp_desc.add_a(Abytes('hash', comp_hash)) + ext.add_a(Abytes('sha_hash', comp_hash)) + + base_offset = reader.read_dw() + limit_offset = reader.read_dw() + comp_desc.add_a(Auint('base_offset', base_offset)) + comp_desc.add_a(Auint('limit_offset', limit_offset)) + ext.add_a(Auint('base_offset', base_offset)) + ext.add_a(Auint('limit_offset', limit_offset)) + + attr0 = reader.read_dw() + attr1 = reader.read_dw() + attr2 = reader.read_dw() + attr3 = reader.read_dw() + comp_desc.add_a(Auint('attribute0', attr0)) + comp_desc.add_a(Auint('attribute1', attr1)) + comp_desc.add_a(Auint('attribute2', attr2)) + comp_desc.add_a(Auint('attribute3', attr3)) + ext.add_a(Abytes('attributes', struct.pack('IIII', attr0, attr1, + attr2, attr3))) + ext.add_comp(comp_desc) + + read_len = reader.get_offset() - begin_off + effective_len = ext_end - begin_off + if read_len < effective_len: + reader.ff_data(effective_len - read_len) + elif read_len > effective_len: + raise Exception('Malformed ADSP metadata file extension length') elif ext_type == 35: reader.info("Signed package info extension") ext = SignedPkgInfoExtension(ext_id, reader.get_offset()-8) ext.add_a(Astring('name', reader.read_string(4))) ext.add_a(Auint('vcn', reader.read_dw())) ext.add_a(Auint('svn', reader.read_dw())) - ext.add_a(Auint('partition_usage', reader.read_b(), 'red')) - read_len = reader.get_offset() - begin_off - reader.ff_data(ext_len - read_len) + ext.add_a(Auint('partition_usage', reader.read_b())) + ext.add_a(Auint('reserved0', reader.read_b(), 'red')) + ext.add_a(Auint('fw_type', reader.read_b())) + ext.add_a(Auint('fw_sub_type', reader.read_b())) + number_of_modules = reader.read_b() + ext.add_a(Auint('number_of_modules', number_of_modules)) + ext.add_a(Auint('boot_strap_svn', reader.read_b())) + ext.add_a(Abytes('reserved', reader.read_bytes(14), 'red')) + + mod_idx = 0 + while reader.get_offset() < ext_end: + mod = Component('signed_pkg_ace_module_{}'.format(mod_idx), + 'Signed Package Module', reader.get_offset()) + mod.add_a(Astring('name', chararr_to_string(reader.read_bytes(12), 12))) + mod.add_a(Auint('type', reader.read_b())) + mod.add_a(Auint('hash_algo', reader.read_b())) + mod.add_a(Abytes('reserved', reader.read_bytes(2), 'red')) + mod.add_a(Auint('meta_size', reader.read_dw())) + mod.add_a(Abytes('hash', reader.read_bytes(48))) + ext.add_comp(mod) + mod_idx += 1 + + if reader.get_offset() > ext_end: + raise Exception('Malformed signed package ACE extension length') + if reader.get_offset() < ext_end: + reader.ff_data(ext_end - reader.get_offset()) else: reader.info("Other extension") ext = MftExtension(ext_id, 'Other Extension', reader.get_offset()-8) - reader.ff_data(ext_len-8) + reader.ff_data(max(0, ext_end - reader.get_offset())) ext.add_a(Auint('type', ext_type)) ext.add_a(Auint('length', ext_len)) reader.info("... end of extension") @@ -1037,16 +1199,22 @@ def parse_adsp_manifest_hdr(reader): reader.get_offset() -4) hdr.add_a(Astring('sig', sig)) - hdr.add_a(Auint('size', reader.read_dw())) + header_len = reader.read_dw() + hdr.add_a(Auint('header_len', header_len)) + hdr.add_a(Auint('size', header_len)) hdr.add_a(Astring('name', chararr_to_string(reader.read_bytes(8), 8))) - hdr.add_a(Auint('preload', reader.read_dw())) + preload_page_count = reader.read_dw() + hdr.add_a(Auint('preload_page_count', preload_page_count)) + hdr.add_a(Auint('preload', preload_page_count)) hdr.add_a(Auint('fw_image_flags', reader.read_dw())) hdr.add_a(Auint('feature_mask', reader.read_dw())) hdr.add_a(Aversion('build_version', reader.read_w(), reader.read_w(), reader.read_w(), reader.read_w())) hdr.add_a(Adec('num_module_entries', reader.read_dw())) - hdr.add_a(Ahex('hw_buf_base_addr', reader.read_dw())) + fw_compat = reader.read_dw() + hdr.add_a(Ahex('fw_compat', fw_compat)) + hdr.add_a(Ahex('hw_buf_base_addr', fw_compat)) hdr.add_a(Auint('hw_buf_length', reader.read_dw())) hdr.add_a(Ahex('load_offset', reader.read_dw())) @@ -1081,7 +1249,9 @@ def parse_adsp_manifest_mod_entry(index, reader): mod.add_a(Adec('cfg_count', reader.read_w())) mod.add_a(Auint('affinity_mask', reader.read_dw())) mod.add_a(Adec('instance_max_count', reader.read_w())) - mod.add_a(Auint('instance_stack_size', reader.read_w())) + instance_bss_size = reader.read_w() + mod.add_a(Auint('instance_bss_size', instance_bss_size)) + mod.add_a(Auint('instance_stack_size', instance_bss_size)) for i in range(0, 3): seg_flags = reader.read_dw() mod.add_a(Astring('seg_'+repr(i)+'_flags', @@ -1092,15 +1262,42 @@ def parse_adsp_manifest_mod_entry(index, reader): return mod +def parse_adsp_manifest_mod_config(index, reader): + """ Parses ADSP manifest module config from sof binary + """ + cfg = Component('mod_cfg_'+repr(index), 'Module Config', + reader.get_offset()) + cfg.add_a(Auint('par0', reader.read_dw())) + cfg.add_a(Auint('par1', reader.read_dw())) + cfg.add_a(Auint('par2', reader.read_dw())) + cfg.add_a(Auint('par3', reader.read_dw())) + cfg.add_a(Auint('is_pages', reader.read_dw())) + cfg.add_a(Auint('cps', reader.read_dw())) + cfg.add_a(Auint('ibs', reader.read_dw())) + cfg.add_a(Auint('obs', reader.read_dw())) + cfg.add_a(Auint('module_flags', reader.read_dw())) + cfg.add_a(Auint('cpc', reader.read_dw())) + cfg.add_a(Auint('obls', reader.read_dw())) + return cfg + def parse_adsp_manifest(reader, name): """ Parses ADSP manifest from sof binary """ adsp_mft = AdspManifest(name, reader.get_offset()) adsp_mft.add_comp(parse_adsp_manifest_hdr(reader)) num_module_entries = adsp_mft.cdir['adsp_mft_hdr'].adir['num_module_entries'].val + # Each module advertises its own cfg_count; the configs are packed + # contiguously after the module entries, so the total number written is the + # sum of every module's cfg_count (which can exceed num_module_entries). + num_mod_configs = 0 for i in range(0, num_module_entries): mod_entry = parse_adsp_manifest_mod_entry(i, reader) adsp_mft.add_comp(mod_entry) + num_mod_configs += mod_entry.adir['cfg_count'].val + + for i in range(0, num_mod_configs): + mod_cfg = parse_adsp_manifest_mod_config(i, reader) + adsp_mft.add_comp(mod_cfg) return adsp_mft @@ -1456,10 +1653,19 @@ def __init__(self, offset): def dump_info(self, pref, comp_filter): hdr = self.cdir['cse_mft_hdr'] - print('{}{} ver {} checksum {} partition name {}'. - format(pref, - self.name, hdr.adir['header_version'], - hdr.adir['checksum'], hdr.adir['partition_name'])) + out = '{}{} header_ver {} entry_ver {} partition name {}'.format( + pref, + self.name, + hdr.adir['header_version'], + hdr.adir['entry_version'], + hdr.adir['partition_name']) + if 'checksum' in hdr.adir: + out += ' checksum {}'.format(hdr.adir['checksum']) + if 'not_used' in hdr.adir: + out += ' not_used {}'.format(hdr.adir['not_used']) + if 'checksum32' in hdr.adir: + out += ' checksum32 {}'.format(hdr.adir['checksum32']) + print(out) self.dump_comp_info(pref, comp_filter + ['Header']) class CssManifest(Component): @@ -1504,6 +1710,7 @@ def dump_info(self, pref, comp_filter): print('{}{} type {} file offset 0x{:x} length {}'. format(pref, self.name, self.adir['type'], self.file_offset, self.adir['length'])) + self.dump_comp_info(pref, comp_filter) class PlatFwAuthExtension(MftExtension): """ Platform FW Auth Extension @@ -1519,6 +1726,9 @@ def dump_info(self, pref, comp_filter): out += ' vcn {}'.format(self.adir['vcn']) out += ' bitmap {}'.format(self.adir['bitmap']) out += ' svn {}'.format(self.adir['svn']) + out += ' fw_type {}'.format(self.adir['fw_type']) + out += ' fw_sub_type {}'.format(self.adir['fw_sub_type']) + out += ' reserved {}'.format(self.adir['reserved']) print(out) class SignedPkgInfoExtension(MftExtension): @@ -1535,6 +1745,54 @@ def dump_info(self, pref, comp_filter): out += ' vcn {}'.format(self.adir['vcn']) out += ' svn {}'.format(self.adir['svn']) out += ' partition_usage {}'.format(self.adir['partition_usage']) + out += ' reserved0 {}'.format(self.adir['reserved0']) + out += ' fw_type {}'.format(self.adir['fw_type']) + out += ' fw_sub_type {}'.format(self.adir['fw_sub_type']) + out += ' number_of_modules {}'.format(self.adir['number_of_modules']) + out += ' boot_strap_svn {}'.format(self.adir['boot_strap_svn']) + out += ' reserved {}'.format(self.adir['reserved']) + print(out) + +class PartitionInfoExtension(MftExtension): + """ Partition info Extension + """ + def __init__(self, ext_id, offset): + super(PartitionInfoExtension, + self).__init__(ext_id, 'Partition info Extension', offset) + + def dump_info(self, pref, comp_filter): + super().dump_info(pref, comp_filter) + out = '{}'.format(pref) + out += ' name {}'.format(self.adir['name']) + out += ' partition_length {}'.format(self.adir['partition_length']) + out += ' hash {}'.format(self.adir['hash']) + out += ' vcn {}'.format(self.adir['vcn']) + out += ' part_version {}'.format(self.adir['part_version']) + out += ' fmt_version {}'.format(self.adir['fmt_version']) + out += ' instance_id {}'.format(self.adir['instance_id']) + out += ' part_flags {}'.format(self.adir['part_flags']) + out += ' reserved {}'.format(self.adir['reserved']) + print(out) + +class InfoExtension0x16(MftExtension): + """ info_ext_0x16 Extension + """ + def __init__(self, ext_id, offset): + super(InfoExtension0x16, + self).__init__(ext_id, 'Info Extension 0x16', offset) + + def dump_info(self, pref, comp_filter): + super().dump_info(pref, comp_filter) + out = '{}'.format(pref) + out += ' name {}'.format(self.adir['name']) + out += ' size {}'.format(self.adir['size']) + out += ' data [{}, {}, {}, {}, {}]'.format( + self.adir['data0'], self.adir['data1'], self.adir['data2'], + self.adir['data3'], self.adir['data4']) + out += ' hash {}'.format(self.adir['hash']) + out += ' data1 [{}, {}, {}, {}, {}]'.format( + self.adir['data1_0'], self.adir['data1_1'], self.adir['data1_2'], + self.adir['data1_3'], self.adir['data1_4']) print(out) class AdspMetadataFileExt(MftExtension): @@ -1553,6 +1811,12 @@ def dump_info(self, pref, comp_filter): out += ' limit offset {}'.format(self.adir['limit_offset']) print(out) print('{} IMR type {}'.format(pref, self.adir['adsp_imr_type'])) + print('{} Reserved {}'.format(pref, self.adir['reserved'])) + print('{} Component desc reserved0 {}'.format(pref, + self.adir['comp_desc_reserved0'])) + print('{} Component desc reserved1 {}'.format(pref, + self.adir['comp_desc_reserved1'])) + print('{} SHA hash {}'.format(pref, self.adir['sha_hash'])) print('{} Attributes'.format(pref)) print('{} {}'.format(pref, self.adir['attributes'])) @@ -1569,6 +1833,7 @@ def dump_info(self, pref, comp_filter): out += ' build ver {}'.format(hdr.adir['build_version']) out += ' feature mask {}'.format(hdr.adir['feature_mask']) out += ' image flags {}'.format(hdr.adir['fw_image_flags']) + out += ' fw compat {}'.format(hdr.adir['fw_compat']) print(out) print('{} HW buffers base address {} length {}'. format(pref, @@ -1781,6 +2046,13 @@ def dump_info(self): DspMemorySegment('l2 lpsram', 0xbe800000, 1*64*1024) ]) +PTL_MEMORY_SPACE = DspMemory('Intel Pantherlake', + [ + DspMemorySegment('imr', 0xb0000000, 16*1024*1024), + DspMemorySegment('l2 hpsram', 0xbe000000, 30*64*1024), + DspMemorySegment('l2 lpsram', 0xbe800000, 1*64*1024) + ]) + DSP_MEM_SPACE_EXT = { 'apl' : APL_MEMORY_SPACE, 'glk' : APL_MEMORY_SPACE, @@ -1797,10 +2069,16 @@ def dump_info(self): 'ehl' : TGL_LP_MEMORY_SPACE, 'adl' : TGL_LP_MEMORY_SPACE, 'rpl' : TGL_LP_MEMORY_SPACE, + 'mtl' : TGL_LP_MEMORY_SPACE, + 'lnl' : TGL_LP_MEMORY_SPACE, + 'arl' : TGL_LP_MEMORY_SPACE, 'tgl-h' : TGL_H_MEMORY_SPACE, 'adl-s' : TGL_H_MEMORY_SPACE, 'rpl-s' : TGL_H_MEMORY_SPACE, + 'arl-s' : TGL_H_MEMORY_SPACE, + 'ptl' : PTL_MEMORY_SPACE, + 'nvl' : PTL_MEMORY_SPACE, } @@ -1923,6 +2201,7 @@ def main(args): comp_filter = [] if args.headers or args.no_modules: comp_filter.append('Module Entry') + comp_filter.append('Module Config') if args.no_headers: comp_filter.append('CSE Manifest') fw_bin.dump_info('', comp_filter) diff --git a/tools/test/audio/process_test.m b/tools/test/audio/process_test.m index 70df2978a439..98928b86f0ed 100644 --- a/tools/test/audio/process_test.m +++ b/tools/test/audio/process_test.m @@ -427,12 +427,39 @@ % No need to collect trace test.trace = [tempname('/tmp') '.txt']; + + switch t.comp + case 'phase_vocoder' + [fh, name, msg] = mkstemp('/tmp/phase_vocoder_enable.sh.XXXXXX'); + if (fh < 0) + error("mkstemp() failed: %s", msg); + end + test.extra_opts = sprintf('-s %s', name); + test.extra_opts_file = name; + fprintf(fh, "amixer -c0 cset name='Analog Playback Phase Vocoder speed' 1.0\n"); + fprintf(fh, "amixer -c0 cset name='Analog Playback Phase Vocoder enable' on\n"); + fclose(fh); + case 'tdfb' + [fh, name, msg] = mkstemp('/tmp/tdfb_enable.sh.XXXXXX'); + if (fh < 0) + error("mkstemp() failed: %s", msg); + end + test.extra_opts = sprintf('-s %s', name); + test.extra_opts_file = name; + fprintf(fh, "amixer -c0 cset name='Analog Playback TDFB track' off\n"); + fprintf(fh, "amixer -c0 cset name='Analog Playback TDFB angle set' 90\n"); + fprintf(fh, "amixer -c0 cset name='Analog Playback TDFB beam' on\n"); + fclose(fh); + end end function test = test_run_process(test) delete_check(1, test.fn_out); test = test_run(test); delete_check(1, test.trace); + if isfield(test, 'extra_opts_file') && ~isempty(test.extra_opts_file) + delete_check(1, test.extra_opts_file); + end end function test_result_print(t, testverbose, testacronym, test) @@ -442,7 +469,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_direction_test.m b/tools/test/audio/tdfb_direction_test.m index 9152ee28c53f..ec11914da22a 100644 --- a/tools/test/audio/tdfb_direction_test.m +++ b/tools/test/audio/tdfb_direction_test.m @@ -15,17 +15,17 @@ function tdfb_direction_test() % General settings cfg.delete_files = 1; cfg.do_plots = 1; -cfg.tunepath = '../../tune/tdfb/data'; +cfg.tunepath = '../../../src/audio/tdfb/tune/data'; % Arrays to test. Since the two beams configurations are merge of two designs (pm90deg) % need to specify a compatible data file identifier for a single beam design (az0el0deg) -array_data_list = {'line2_50mm_az0el0deg_48khz', 'line4_28mm_az0el0deg_48khz', 'circular8_100mm_az0el0deg_48khz'}; -tdfb_name_list = {'', 'line4_28mm_pm90deg_48khz', 'circular8_100mm_pm30deg_48khz'}; +array_data_list = {'line2_50mm_az0el0deg_48khz'}; +tdfb_name_list = {''}; %% Prepare addpath('std_utils'); addpath('test_utils'); -addpath('../../tune/tdfb'); +addpath('../../../src/audio/tdfb/tune'); for i = 1:length(array_data_list) @@ -80,7 +80,16 @@ function tdfb_direction_test() test.nch_out = bf.num_output_channels; test.ch_in = 1:test.nch_in; test.ch_out = 1:test.nch_out; -test.extra_opts='-d 4'; +[fh, name, msg] = mkstemp('/tmp/tdfb_enable.sh.XXXXXX'); +if (fh < 0) + error("mkstemp() failed: %s", msg); +end +test.extra_opts = sprintf('-d 4 -s %s', name); +test.extra_opts_file = name; +fprintf(fh, "amixer -c0 cset name='Analog Playback TDFB track' on\n"); +fprintf(fh, "amixer -c0 cset name='Analog Playback TDFB angle set' 90\n"); +fprintf(fh, "amixer -c0 cset name='Analog Playback TDFB beam' on\n"); +fclose(fh); test.trace = 'tdfb_direction.txt'; if length(arrayid) test.comp = sprintf('tdfb_%s', arrayid); @@ -92,6 +101,9 @@ function tdfb_direction_test() delete_check(1, test.fn_out); test = test_run(test); +if isfield(test, 'extra_opts_file') && ~isempty(test.extra_opts_file) + delete_check(1, test.extra_opts_file); +end end diff --git a/tools/test/audio/tdfb_test.m b/tools/test/audio/tdfb_test.m index 3dcf1fecf535..1a4f7927ffb4 100644 --- a/tools/test/audio/tdfb_test.m +++ b/tools/test/audio/tdfb_test.m @@ -77,7 +77,16 @@ function tdfb_test(xtrun) test.nch_out = bf.num_output_channels; test.ch_in = 1:test.nch_in; test.ch_out = 1:test.nch_out; -test.extra_opts='-s tdfb_enable.sh'; +[fh, name, msg] = mkstemp('/tmp/tdfb_enable.sh.XXXXXX'); +if (fh < 0) + error("mkstemp() failed: %s", msg); +end +test.extra_opts = sprintf('-s %s', name); +test.extra_opts_file = name; +fprintf(fh, "amixer -c0 cset name='Analog Playback TDFB track' off\n"); +fprintf(fh, "amixer -c0 cset name='Analog Playback TDFB angle set' 90\n"); +fprintf(fh, "amixer -c0 cset name='Analog Playback TDFB beam' on\n"); +fclose(fh); if length(arrayid) test.comp = sprintf('tdfb_%s', arrayid); end @@ -88,6 +97,9 @@ function tdfb_test(xtrun) delete_check(1, test.fn_out); test = test_run(test); +if isfield(test, 'extra_opts_file') && ~isempty(test.extra_opts_file) + delete_check(1, test.extra_opts_file); +end end @@ -208,7 +220,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..6e48cc184c38 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; @@ -817,6 +819,14 @@ static int file_prepare(struct processing_module *mod, /* set file function */ stream = &buffer->stream; + + /* + * Real copier/DAI components call ipc4_update_buffer_format() to populate + * valid_sample_fmt; the testbench file component is the host endpoint stub + * so propagate cd->frame_fmt to the buffer's valid format too. + */ + audio_stream_set_valid_fmt(stream, cd->frame_fmt); + switch (audio_stream_get_frm_fmt(stream)) { case SOF_IPC_FRAME_S16_LE: cd->file_func = file_s16; diff --git a/tools/testbench/include/testbench/file.h b/tools/testbench/include/testbench/file.h index c9552c5a4425..dd1735026564 100644 --- a/tools/testbench/include/testbench/file.h +++ b/tools/testbench/include/testbench/file.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: BSD-3-Clause * - * Copyright(c) 2018 Intel Corporation. All rights reserved. + * Copyright(c) 2018-2026 Intel Corporation. * * Author: Seppo Ingalsuo <seppo.ingalsuo@linux.intel.com> * Liam Girdwood <liam.r.girdwood@linux.intel.com> @@ -13,7 +13,7 @@ #include <stdint.h> -#define FILE_MAX_COPIES_TIMEOUT 3 +#define FILE_MAX_COPIES_TIMEOUT 16 /**< Convert with right shift a bytes count to samples count */ #define FILE_BYTES_TO_S16_SAMPLES(s) ((s) >> 1) 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_ipc3.c b/tools/testbench/topology_ipc3.c index b95dc9859e68..331ab2ff1103 100644 --- a/tools/testbench/topology_ipc3.c +++ b/tools/testbench/topology_ipc3.c @@ -98,7 +98,8 @@ static int tb_register_graph(struct tplg_context *ctx, struct tplg_comp_info *te for (i = 0; i < num_connections; i++) { ret = tplg_create_graph(ctx, num_comps, pipeline_id, temp_comp_list, - pipeline_string, &connection, i); + pipeline_string, sizeof(pipeline_string), + &connection, i); if (ret < 0) return ret; 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..91c08c62aab1 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); @@ -359,10 +368,18 @@ static int tb_parse_amixer(struct testbench_prm *tp, char *line) } len = end_str - name_str - find_len; + if (len < 0 || len >= TB_MAX_CTL_NAME_CHARS) { + fprintf(stderr, "error: control name too long in script line: %s\n", line); + return -EINVAL; + } memcpy(control_name, name_str + find_len, len); line_end = line + strlen(line); len = line_end - end_str - find_end_len; + if (len < 0 || len >= TB_MAX_CTL_NAME_CHARS) { + fprintf(stderr, "error: control value too long in script line: %s\n", line); + return -EINVAL; + } memcpy(control_params, &end_str[find_end_len], len); printf("Info: Setting control name '%s' to value (%s)\n", control_name, control_params); 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..6fed07e7b89b 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(); @@ -59,11 +62,12 @@ int tb_setup(struct sof *sof, struct testbench_prm *tp) sys_comp_module_mixout_interface_init(); sys_comp_module_multiband_drc_interface_init(); sys_comp_module_mux_interface_init(); + sys_comp_module_phase_vocoder_interface_init(); sys_comp_module_rtnr_interface_init(); 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 +291,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 +705,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/CMakeLists.txt b/tools/topology/CMakeLists.txt index 92a7acf6e5a6..263bc8a4f75f 100644 --- a/tools/topology/CMakeLists.txt +++ b/tools/topology/CMakeLists.txt @@ -2,6 +2,16 @@ set(SOF_TOPOLOGY_BINARY_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}") set(SOF_ALSA_TOOLS_DIR "${SOF_ROOT_SOURCE_DIRECTORY}/../tools/bin") set(ALSATPLG_CMD "${SOF_ALSA_TOOLS_DIR}/alsatplg") +# With the Ninja generator, USES_TERMINAL forces the single-job "console" +# pool, serializing every topology build regardless of -j. Enable this +# option to force that serialized, easier to debug behavior back on. +option(TPLG_SEQUENTIAL_BUILD "Build topologies one at a time (for debugging)" OFF) +if(TPLG_SEQUENTIAL_BUILD) + set(TPLG_USES_TERMINAL USES_TERMINAL) +else() + set(TPLG_USES_TERMINAL "") +endif() + function(alsatplg_version OUT_STATUS OUT_VERSION) execute_process(COMMAND ${ALSATPLG_CMD} --version RESULT_VARIABLE status @@ -69,7 +79,7 @@ macro(add_alsatplg_command) # the -o(utput) file. # See bug https://github.com/alsa-project/alsa-utils/issues/126 COMMAND ${ALSATPLG_CMD} \$\${VERBOSE:+-v 1} -c ${ARGV0} -o ${ARGV1} - USES_TERMINAL + ${TPLG_USES_TERMINAL} ) endmacro() @@ -97,7 +107,7 @@ macro(add_alsatplg2_command conf_header conf_target input_name output_name inclu # -p to pre-process Topology2.0 conf file COMMAND ALSA_CONFIG_DIR=${CMAKE_SOURCE_DIR}/topology/topology2 ${ALSATPLG_CMD} \$\${VERBOSE:+-v 1} -I ${include_path} -D "'${defines}'" -p -c ${output_name}.conf -o ${output_name}.tplg - USES_TERMINAL + ${TPLG_USES_TERMINAL} ) endmacro() 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/CMakeLists.txt b/tools/topology/topology1/CMakeLists.txt index c6ae06d0be04..95d81a01629f 100644 --- a/tools/topology/topology1/CMakeLists.txt +++ b/tools/topology/topology1/CMakeLists.txt @@ -122,6 +122,9 @@ set(TPLGS "sof-acp_6_3_sdw\;sof-acp_6_3-rt711-l0-rt1316-l0-rt714-l1" "sof-acp_7_0\;sof-acp_7_0" "sof-acp_7_0_sdw\;sof-acp_7_0-rt722-l0" + "sof-acp_7_x_sdw\;sof-acp7x-rt721-l0" + "sof-acp_7_x_i2s\;sof-acp7x" + "sof-acp_7_x_i2s_24bit\;sof-acp7x-24bit" ) # This empty 'production/' source subdirectory exists only to create the 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/m4/dai.m4 b/tools/topology/topology1/m4/dai.m4 index cd968c6c61fd..4dc5d5ec5135 100644 --- a/tools/topology/topology1/m4/dai.m4 +++ b/tools/topology/topology1/m4/dai.m4 @@ -155,7 +155,7 @@ define(`DO_DAI_CONFIG', `' ` id "'$3`"' `' -` ifelse($1, `SSP', $5, $1, `HDA', $5, $1, `ALH', $5, $1, `ESAI', $5, $1, `SAI', $5, $1, `MICFIL', $5, $1, `AFE', $5, $1, `ACP', $5, $1, `ACPSP', $5, $1,`ACPSP_VIRTUAL', $5, $1, `ACPHS', $5, $1, `ACPHS_VIRTUAL', $5, $1, `ACP_SDW', $5, $1, `ACPDMIC', $5, `}')' +` ifelse($1, `SSP', $5, $1, `HDA', $5, $1, `ALH', $5, $1, `ESAI', $5, $1, `SAI', $5, $1, `MICFIL', $5, $1, `AFE', $5, $1, `ACP', $5, $1, `ACPSP', $5, $1,`ACPSP_VIRTUAL', $5, $1, `ACPHS', $5, $1, `ACPHS_VIRTUAL', $5, $1, `ACP_SDW', $5, $1, `ACPDMIC', $5, $1, `ACPTDM', $5, `}')' `ifelse($1, `DMIC', $5, `')' `SectionVendorTuples."'N_DAI_CONFIG($1$2)`_tuples_common" {' ` tokens "sof_dai_tokens"' diff --git a/tools/topology/topology1/platform/common/acp-tdm.m4 b/tools/topology/topology1/platform/common/acp-tdm.m4 new file mode 100644 index 000000000000..22e72b0d5ea4 --- /dev/null +++ b/tools/topology/topology1/platform/common/acp-tdm.m4 @@ -0,0 +1,74 @@ +divert(-1) + +dnl ACPTDM related macros + +dnl ACP_CLOCK(clock, freq, codec_provider, polarity) +dnl polarity is optional +define(`ACP_CLOCK', + $1 STR($3) + $1_freq STR($2)) + `ifelse($4, `inverted', `$1_invert "true"',`')') + +dnl ACP_TDM(slots, width, tx_mask, rx_mask) +define(`ACP_TDM', +` tdm_slots 'STR($1) +` tdm_slot_width 'STR($2) +` tx_slots 'STR($3) +` rx_slots 'STR($4) +) + +dnl ACP_CONFIG(format, mclk, bclk, fsync, tdm, tdm_config_data) +define(`ACPTDM_CONFIG', +` format "'$1`"' +` '$2 +` '$3 +` '$4 +` '$5 +`}' +$6 +) + +dnl ACPTDM_VIRTUAL_CONFIG(format, mclk, bclk, fsync, tdm, tdm_config_data) +define(`ACPTDM_VIRTUAL_CONFIG', +` format "'$1`"' +` '$2 +` '$3 +` '$4 +` '$5 +`}' +$6 +) + +dnl ACPTDM_CONFIG_DATA(type, idx, rate, channel,i2s_tdm_mode) +#i2s_tdm_mode 1-> tdm mode, 0->i2s mode +define(`ACPTDM_CONFIG_DATA', +`SectionVendorTuples."'N_DAI_CONFIG($1$2)`_tuples" {' +` tokens "sof_acp_tokens"' +` tuples."word" {' +` SOF_TKN_AMD_ACP_RATE' STR($3) +` SOF_TKN_AMD_ACP_CH' STR($4) +` SOF_TKN_AMD_ACP_I2S_TDM_MODE' STR($5) +` SOF_TKN_AMD_ACP_I2S_BITDEPTH' STR($6) +` }' +`}' +`SectionData."'N_DAI_CONFIG($1$2)`_data" {' +` tuples "'N_DAI_CONFIG($1$2)`_tuples"' +`}' +) + +dnl ACPTDM_VIRTUAL_CONFIG_DATA(type, idx, rate, channel,i2s_tdm_mode) +#i2s_tdm_mode 1-> tdm mode, 0->i2s mode +define(`ACPTDM_VIRTUAL_CONFIG_DATA', +`SectionVendorTuples."'N_DAI_CONFIG($1$2)`_tuples" {' +` tokens "sof_acp_tokens"' +` tuples."word" {' +` SOF_TKN_AMD_ACP_RATE' STR($3) +` SOF_TKN_AMD_ACP_CH' STR($4) +` SOF_TKN_AMD_ACP_I2S_TDM_MODE' STR($5) +` }' +`}' +`SectionData."'N_DAI_CONFIG($1$2)`_data" {' +` tuples "'N_DAI_CONFIG($1$2)`_tuples"' +`}' +) +divert(0)dnl diff --git a/tools/topology/topology1/sof-acp_7_x_i2s.m4 b/tools/topology/topology1/sof-acp_7_x_i2s.m4 new file mode 100644 index 000000000000..6f633f700a56 --- /dev/null +++ b/tools/topology/topology1/sof-acp_7_x_i2s.m4 @@ -0,0 +1,187 @@ +# +# Topology for ACP_7_X with TDM. +# +# Include topology builder +include(`utils.m4') +include(`dai.m4') +include(`pipeline.m4') +include(`acp-tdm.m4') + +# Include TLV library +include(`common/tlv.m4') + +# Include Token library +include(`sof/tokens.m4') + +# Include ACP DSP configuration +include(`platform/amd/acp.m4') + +# +# Pipeline Graph (16-bit / s16le): +# +# PLAYBACK: +# [Host PCM ] -> [Passthrough Pipeline ] -> [ACPTDM DAI ] +# s16le 2ch s16le 48kHz s16le 2ch +# | +# [acp-i2s0-codec] +# I2S bclk=3.072MHz +# fsync=48kHz, 2ch +# | +# CAPTURE: +# [ACPTDM DAI ] -> [Passthrough Pipeline ] -> [Host PCM ] +# s16le 2ch s16le 48kHz s16le 2ch +# + +DEBUG_START +#====================================================================== +# Playback pipeline 1 on PCM 0 using max 2 channels of s16le. + +dnl PIPELINE_PCM_ADD(pipeline, +dnl pipe id, pcm, max channels, format, +dnl period, priority, core, +dnl pcm_min_rate, pcm_max_rate, pipeline_rate) +# Playback pipeline 0 on PCM 0, dai index 0, link id 0 using max 2 channels of s16le. + +# Schedule 96 frames per 2000us deadline on core 0 with priority 0 +PIPELINE_PCM_ADD(sof/pipe-passthrough-playback.m4, + 0, 0, 2, s16le, + 2000, 0, 0, + 48000, 48000, 48000) + +# Capture pipeline 3 on PCM 0, dai index 0 using max 2 channels of s16le. +PIPELINE_PCM_ADD(sof/pipe-passthrough-capture.m4, + 3, 0, 2, s16le, + 2000, 0, 0, + 48000, 48000, 48000) +#=========================================================================== +dnl DAI_ADD(pipeline, +dnl pipe id, dai type, dai_index, dai_be, +dnl buffer, periods, format, +dnl deadline, priority, core, time_domain) + +# Schedule 96 frames per 2000us deadline on core 0 with priority 0 + +# playback DAI is ACPTDM using 2 periods +DAI_ADD(sof/pipe-dai-playback.m4, + 0, ACPTDM, 0, acp-i2s0-codec, + PIPELINE_SOURCE_0, 2, s16le, + 2000, 0, 0, SCHEDULE_TIME_DOMAIN_TIMER) + +# Capture DAI is ACPTDM using 2 periods +DAI_ADD(sof/pipe-dai-capture.m4, + 3, ACPTDM, 0, acp-i2s0-codec, + PIPELINE_SINK_3, 2, s16le, + 2000, 0, 0, SCHEDULE_TIME_DOMAIN_TIMER) +#=========================================================================== +# playback DAI is ACPTDM using 2 periods + +dnl DAI_CONFIG(type, dai_index, link_id, name, ACPTDM_config/acpdmic_config) +dnl ACPTDM_CONFIG(format, mclk, bclk, fsync, tdm, ACPTDM_config_data) +dnl ACP_CLOCK(clock, freq, codec_provider, polarity) +dnl ACPTDM_CONFIG_DATA(type, idx, valid bits, mclk_id) +dnl mclk_id is optional + +DAI_CONFIG(ACPTDM, 0, 0, acp-i2s0-codec, + ACPTDM_CONFIG(I2S, ACP_CLOCK(mclk, 49152000, codec_mclk_in), + ACP_CLOCK(bclk, 3072000, codec_consumer), + ACP_CLOCK(fsync, 48000, codec_consumer), + ACP_TDM(2, 32, 3, 3),ACPTDM_CONFIG_DATA(ACPTDM, 0, 48000, 2, 0, 0)) +) +dnl PCM_DUPLEX_ADD(name, pcm_id, playback_pipeline, capture_pipeline) +PCM_DUPLEX_ADD(I2STDM0, 0, PIPELINE_PCM_0, PIPELINE_PCM_3) + +#==================================================================================================================== +# TDM instance 1 + +dnl PIPELINE_PCM_ADD(pipeline, +dnl pipe id, pcm, max channels, format, +dnl period, priority, core, +dnl pcm_min_rate, pcm_max_rate, pipeline_rate) + +PIPELINE_PCM_ADD(sof/pipe-passthrough-playback.m4, + 1, 1, 2, s16le, + 2000, 0, 0, + 48000, 48000, 48000) +PIPELINE_PCM_ADD(sof/pipe-passthrough-capture.m4, + 4, 1, 2, s16le, + 2000, 0, 0, + 48000, 48000, 48000) + +dnl DAI_ADD(pipeline, +dnl pipe id, dai type, dai_index, dai_be, +dnl buffer, periods, format, +dnl deadline, priority, core, time_domain) + +DAI_ADD(sof/pipe-dai-playback.m4, + 1, ACPTDM, 1, acp-i2s1-codec, + PIPELINE_SOURCE_1, 2, s16le, + 2000, 0, 0, SCHEDULE_TIME_DOMAIN_TIMER) + +DAI_ADD(sof/pipe-dai-capture.m4, + 4, ACPTDM, 1, acp-i2s1-codec, + PIPELINE_SINK_4, 2, s16le, + 2000, 0, 0, SCHEDULE_TIME_DOMAIN_TIMER) +dnl DAI_CONFIG(type, dai_index, link_id, name, ACPTDM_config/acpdmic_config) +dnl ACPTDM_CONFIG(format, mclk, bclk, fsync, tdm, ACPTDM_config_data) +dnl ACP_CLOCK(clock, freq, codec_provider, polarity) +dnl ACPTDM_CONFIG_DATA(type, idx, valid bits, mclk_id) +dnl mclk_id is optional + +DAI_CONFIG(ACPTDM, 1, 1, acp-i2s1-codec, + ACPTDM_CONFIG(I2S, ACP_CLOCK(mclk, 49152000, codec_mclk_in), + ACP_CLOCK(bclk, 3072000, codec_consumer), + ACP_CLOCK(fsync, 48000, codec_consumer), + ACP_TDM(2, 32, 3, 3), ACPTDM_CONFIG_DATA(ACPTDM, 1, 48000, 2, 0))) + +dnl PCM_DUPLEX_ADD(name, pcm_id, playback_pipeline, capture_pipeline) +PCM_DUPLEX_ADD(I2STDM1, 1, PIPELINE_PCM_1, PIPELINE_PCM_4) + +#==================================================================================================================== +# TDM instance 2 + +dnl PIPELINE_PCM_ADD(pipeline, +dnl pipe id, pcm, max channels, format, +dnl period, priority, core, +dnl pcm_min_rate, pcm_max_rate, pipeline_rate) + +PIPELINE_PCM_ADD(sof/pipe-passthrough-playback.m4, + 2, 2, 2, s16le, + 2000, 0, 0, + 48000, 48000, 48000) +PIPELINE_PCM_ADD(sof/pipe-passthrough-capture.m4, + 5, 2, 2, s16le, + 2000, 0, 0, + 48000, 48000, 48000) + +dnl DAI_ADD(pipeline, +dnl pipe id, dai type, dai_index, dai_be, +dnl buffer, periods, format, +dnl deadline, priority, core, time_domain) + +DAI_ADD(sof/pipe-dai-playback.m4, + 2, ACPTDM, 2, acp-i2s2-codec, + PIPELINE_SOURCE_2, 2, s16le, + 2000, 0, 0, SCHEDULE_TIME_DOMAIN_TIMER) + +DAI_ADD(sof/pipe-dai-capture.m4, + 5, ACPTDM, 2, acp-i2s2-codec, + PIPELINE_SINK_5, 2, s16le, + 2000, 0, 0, SCHEDULE_TIME_DOMAIN_TIMER) + +dnl DAI_CONFIG(type, dai_index, link_id, name, ACPTDM_config/acpdmic_config) +dnl ACPTDM_CONFIG(format, mclk, bclk, fsync, tdm, ACPTDM_config_data) +dnl ACP_CLOCK(clock, freq, codec_provider, polarity) +dnl ACPTDM_CONFIG_DATA(type, idx, valid bits, mclk_id) +dnl mclk_id is optional + +DAI_CONFIG(ACPTDM, 2, 2, acp-i2s2-codec, + ACPTDM_CONFIG(I2S, ACP_CLOCK(mclk, 49152000, codec_mclk_in), + ACP_CLOCK(bclk, 3072000, codec_consumer), + ACP_CLOCK(fsync, 48000, codec_consumer), + ACP_TDM(2, 32, 3, 3), ACPTDM_CONFIG_DATA(ACPTDM, 2, 48000, 2, 0))) + +dnl PCM_DUPLEX_ADD(name, pcm_id, playback_pipeline, capture_pipeline) +PCM_DUPLEX_ADD(I2STDM2, 2, PIPELINE_PCM_2, PIPELINE_PCM_5) +#==================================================================================================================== + +DEBUG_END diff --git a/tools/topology/topology1/sof-acp_7_x_i2s_24bit.m4 b/tools/topology/topology1/sof-acp_7_x_i2s_24bit.m4 new file mode 100644 index 000000000000..dabf7927d90c --- /dev/null +++ b/tools/topology/topology1/sof-acp_7_x_i2s_24bit.m4 @@ -0,0 +1,165 @@ +# +# Topology for ACP_7_x with TDM (24-bit). +# +# Include topology builder +include(`utils.m4') +include(`dai.m4') +include(`pipeline.m4') +include(`acp-tdm.m4') + +# Include TLV library +include(`common/tlv.m4') + +# Include Token library +include(`sof/tokens.m4') + +# Include ACP DSP configuration +include(`platform/amd/acp.m4') + +# +# Pipeline Graph (24-bit / s24le): +# +# PLAYBACK/CAPTURE: +# TDM0, TDM1, TDM2 each expose a duplex PCM. +# Host side uses s32le; DAI side uses s24le (frame_fmt=1). +# + +DEBUG_START +#====================================================================== +# Playback/Capture pipelines for three TDM instances (24-bit). + +# TDM instance 0 + +dnl PIPELINE_PCM_ADD(pipeline, +dnl pipe id, pcm, max channels, format, +dnl period, priority, core, +dnl pcm_min_rate, pcm_max_rate, pipeline_rate) +PIPELINE_PCM_ADD(sof/pipe-passthrough-playback.m4, + 0, 0, 2, s32le, + 2000, 0, 0, + 48000, 48000, 48000) + +PIPELINE_PCM_ADD(sof/pipe-passthrough-capture.m4, + 3, 0, 2, s32le, + 2000, 0, 0, + 48000, 48000, 48000) + +dnl DAI_ADD(pipeline, +dnl pipe id, dai type, dai_index, dai_be, +dnl buffer, periods, format, +dnl deadline, priority, core, time_domain) +DAI_ADD(sof/pipe-dai-playback.m4, + 0, ACPTDM, 0, acp-i2s0-codec, + PIPELINE_SOURCE_0, 2, s24le, + 2000, 0, 0, SCHEDULE_TIME_DOMAIN_TIMER) + +DAI_ADD(sof/pipe-dai-capture.m4, + 3, ACPTDM, 0, acp-i2s0-codec, + PIPELINE_SINK_3, 2, s24le, + 2000, 0, 0, SCHEDULE_TIME_DOMAIN_TIMER) + +dnl DAI_CONFIG(type, dai_index, link_id, name, ACPTDM_config/acpdmic_config) +dnl ACPTDM_CONFIG(format, mclk, bclk, fsync, tdm, ACPTDM_config_data) +dnl ACP_CLOCK(clock, freq, codec_provider, polarity) +dnl ACPTDM_CONFIG_DATA(type, idx, rate, tdm_mode, frame_fmt(0 for s16le, 1 for s24le, 2 for s32le)) +dnl mclk_id is optional +DAI_CONFIG(ACPTDM, 0, 0, acp-i2s0-codec, + ACPTDM_CONFIG(I2S, ACP_CLOCK(mclk, 49152000, codec_mclk_in), + ACP_CLOCK(bclk, 3072000, codec_consumer), + ACP_CLOCK(fsync, 48000, codec_consumer), + ACP_TDM(2, 32, 3, 3), ACPTDM_CONFIG_DATA(ACPTDM, 0, 48000, 2, 0, 1))) + +dnl PCM_DUPLEX_ADD(name, pcm_id, playback_pipeline, capture_pipeline) +PCM_DUPLEX_ADD(I2STDM0, 0, PIPELINE_PCM_0, PIPELINE_PCM_3) + +#============================ +# TDM instance 1 + +dnl PIPELINE_PCM_ADD(pipeline, +dnl pipe id, pcm, max channels, format, +dnl period, priority, core, +dnl pcm_min_rate, pcm_max_rate, pipeline_rate) +PIPELINE_PCM_ADD(sof/pipe-passthrough-playback.m4, + 1, 1, 2, s32le, + 2000, 0, 0, + 48000, 48000, 48000) + +PIPELINE_PCM_ADD(sof/pipe-passthrough-capture.m4, + 4, 1, 2, s32le, + 2000, 0, 0, + 48000, 48000, 48000) + +dnl DAI_ADD(pipeline, +dnl pipe id, dai type, dai_index, dai_be, +dnl buffer, periods, format, +dnl deadline, priority, core, time_domain) +DAI_ADD(sof/pipe-dai-playback.m4, + 1, ACPTDM, 1, acp-i2s1-codec, + PIPELINE_SOURCE_1, 2, s24le, + 2000, 0, 0, SCHEDULE_TIME_DOMAIN_TIMER) + +DAI_ADD(sof/pipe-dai-capture.m4, + 4, ACPTDM, 1, acp-i2s1-codec, + PIPELINE_SINK_4, 2, s24le, + 2000, 0, 0, SCHEDULE_TIME_DOMAIN_TIMER) + +dnl DAI_CONFIG(type, dai_index, link_id, name, ACPTDM_config/acpdmic_config) +dnl ACPTDM_CONFIG(format, mclk, bclk, fsync, tdm, ACPTDM_config_data) +dnl ACP_CLOCK(clock, freq, codec_provider, polarity) +dnl ACPTDM_CONFIG_DATA(type, idx, rate, tdm_mode, frame_fmt(0 for s16le, 1 for s24le, 2 for s32le)) +dnl mclk_id is optional +DAI_CONFIG(ACPTDM, 1, 1, acp-i2s1-codec, + ACPTDM_CONFIG(I2S, ACP_CLOCK(mclk, 49152000, codec_mclk_in), + ACP_CLOCK(bclk, 3072000, codec_consumer), + ACP_CLOCK(fsync, 48000, codec_consumer), + ACP_TDM(2, 32, 3, 3), ACPTDM_CONFIG_DATA(ACPTDM, 1, 48000, 2, 0, 1))) + +dnl PCM_DUPLEX_ADD(name, pcm_id, playback_pipeline, capture_pipeline) +PCM_DUPLEX_ADD(I2STDM1, 1, PIPELINE_PCM_1, PIPELINE_PCM_4) + +#============================ +# TDM instance 2 + +dnl PIPELINE_PCM_ADD(pipeline, +dnl pipe id, pcm, max channels, format, +dnl period, priority, core, +dnl pcm_min_rate, pcm_max_rate, pipeline_rate) +PIPELINE_PCM_ADD(sof/pipe-passthrough-playback.m4, + 2, 2, 2, s32le, + 2000, 0, 0, + 48000, 48000, 48000) + +PIPELINE_PCM_ADD(sof/pipe-passthrough-capture.m4, + 5, 2, 2, s32le, + 2000, 0, 0, + 48000, 48000, 48000) + +dnl DAI_ADD(pipeline, +dnl pipe id, dai type, dai_index, dai_be, +dnl buffer, periods, format, +dnl deadline, priority, core, time_domain) +DAI_ADD(sof/pipe-dai-playback.m4, + 2, ACPTDM, 2, acp-i2s2-codec, + PIPELINE_SOURCE_2, 2, s24le, + 2000, 0, 0, SCHEDULE_TIME_DOMAIN_TIMER) + +DAI_ADD(sof/pipe-dai-capture.m4, + 5, ACPTDM, 2, acp-i2s2-codec, + PIPELINE_SINK_5, 2, s24le, + 2000, 0, 0, SCHEDULE_TIME_DOMAIN_TIMER) + +dnl DAI_CONFIG(type, dai_index, link_id, name, ACPTDM_config/acpdmic_config) +dnl ACPTDM_CONFIG(format, mclk, bclk, fsync, tdm, ACPTDM_config_data) +dnl ACP_CLOCK(clock, freq, codec_provider, polarity) +dnl ACPTDM_CONFIG_DATA(type, idx, rate, tdm_mode, frame_fmt(0 for s16le, 1 for s24le, 2 for s32le)) +dnl mclk_id is optional +DAI_CONFIG(ACPTDM, 2, 2, acp-i2s2-codec, + ACPTDM_CONFIG(I2S, ACP_CLOCK(mclk, 49152000, codec_mclk_in), + ACP_CLOCK(bclk, 3072000, codec_consumer), + ACP_CLOCK(fsync, 48000, codec_consumer), + ACP_TDM(2, 32, 3, 3), ACPTDM_CONFIG_DATA(ACPTDM, 2, 48000, 2, 0, 1))) + +dnl PCM_DUPLEX_ADD(name, pcm_id, playback_pipeline, capture_pipeline) +PCM_DUPLEX_ADD(I2STDM2, 2, PIPELINE_PCM_2, PIPELINE_PCM_5) + +DEBUG_END diff --git a/tools/topology/topology1/sof-acp_7_x_sdw.m4 b/tools/topology/topology1/sof-acp_7_x_sdw.m4 new file mode 100644 index 000000000000..99ee0e6b3040 --- /dev/null +++ b/tools/topology/topology1/sof-acp_7_x_sdw.m4 @@ -0,0 +1,132 @@ +#Required Topology for rt721 with ACP DMIC Card for ACP_7_X +# +# PCM Description DAI LINK DAI BE +# 0 HS Playback 0 SDW0-PIN0-PLAYBACK AUDIO_TX +# 1 HS Capture 1 SDW0-PIN11-CAPTURE AUDIO_RX +# 2 Speaker playback 2 SDW0-PIN4-PLAYBACK BT_TX +# 4 SDW DMIC 4 SDW0-PIN16-CAPTURE HS_RX + +# +# Define the pipelines +# +# PCM0 ----> buffer ----> AUDIO_TX +# PCM1 <---- buffer <---- AUDIO_RX +# PCM2 ----> buffer ----> BT_TX +# PCM4 <---- buffer <---- HS_RX + +# Include topology builder +include(`utils.m4') +include(`dai.m4') +include(`pipeline.m4') +include(`acp-sdw.m4') +include(`acp-dmic.m4') + +# Include TLV library +include(`common/tlv.m4') + +# Include Token library +include(`sof/tokens.m4') + +# Include ACP DSP configuration +include(`platform/amd/acp.m4') + +DEBUG_START + +define(DI_SDW0_ACP_SW_AUDIO_0_TX, 0 ) +define(DI_SDW0_ACP_SW_AUDIO_4_TX, 4 ) +define(DI_SDW0_ACP_SW_AUDIO_0_RX, 11) +define(DI_SDW0_ACP_SW_AUDIO_5_RX, 16) + +define(DAI_BE_SDW0_ACP_SW_AUDIO_4_TX, SDW0-PIN4-PLAYBACK) +define(DAI_BE_SDW0_ACP_SW_AUDIO_0_TX, SDW0-PIN0-PLAYBACK) + +define(DAI_BE_SDW0_ACP_SW_AUDIO_0_RX, SDW0-PIN11-CAPTURE) +define(DAI_BE_SDW0_ACP_SW_AUDIO_5_RX, SDW0-PIN16-CAPTURE) + +#pipeline: name of the predefined pipeline +#pipe id: pipeline ID. This should be a unique ID identifying the pipeline +#pcm: PCM ID. This will be used to bind to the correct front end DAI link + +dnl PIPELINE_PCM_ADD(pipeline, +dnl pipe id, pcm, max channels, format, +dnl period, priority, core, +dnl pcm_min_rate, pcm_max_rate, pipeline_rate) + +dnl DAI_ADD(pipeline, +dnl pipe id, dai type, dai_index, dai_be, +dnl buffer, periods, format, +dnl deadline, priority, core, time_domain) + +dnl DAI_CONFIG(type, dai_index, link_id, name, acphs_config/acpdmic_config) +dnl ACPHS_CONFIG(format, mclk, bclk, fsync, tdm, acphs_config_data) +dnl ACP_CLOCK(clock, freq, codec_provider, polarity) +dnl ACPHS_CONFIG_DATA(type, idx, valid bits, mclk_id) +dnl mclk_id is optional + +#/**********************************************************************************/ +# PCM 0, HS Playback, DAI link id 0, Dai index 0(Audio_Tx), BE SW0-PIN0-PLAYBACK +PIPELINE_PCM_ADD(sof/pipe-passthrough-playback.m4, + 0, 0, 2, s16le, + 2000, 0, 0, + 48000, 48000, 48000) + +DAI_ADD(sof/pipe-dai-playback.m4, 0, ACP_SDW, DI_SDW0_ACP_SW_AUDIO_0_TX, DAI_BE_SDW0_ACP_SW_AUDIO_0_TX, +PIPELINE_SOURCE_0, 2, s16le, 2000, 0, 0, SCHEDULE_TIME_DOMAIN_TIMER) + +DAI_CONFIG(ACP_SDW, DI_SDW0_ACP_SW_AUDIO_0_TX, 0, DAI_BE_SDW0_ACP_SW_AUDIO_0_TX, + ACP_SDW_CONFIG(ACP_SDW_CONFIG_DATA(ACP_SDW, DI_SDW0_ACP_SW_AUDIO_0_TX, 48000, 2))) +PCM_PLAYBACK_ADD(ACP-SW0-PIN0-Playback-HS, 0, PIPELINE_PCM_0) + +#/**********************************************************************************/ +#PCM 2, Speaker Playback, PIPE line 2, DAI link id 2, Dai index 4(DI_SDW0_ACP_SW_AUDIO_4_TX), DAI BE SDW0-PIN4-PLAYBACK-SmartAmp +PIPELINE_PCM_ADD(sof/pipe-passthrough-playback.m4, + 2, 2, 2, s16le, + 2000, 0, 0, + 48000, 48000, 48000) + +# playback DAI is ACP soundwire using 2 periods +DAI_ADD(sof/pipe-dai-playback.m4, 2, ACP_SDW, DI_SDW0_ACP_SW_AUDIO_4_TX, DAI_BE_SDW0_ACP_SW_AUDIO_4_TX, + PIPELINE_SOURCE_2, 2, s16le, 2000, 0, 0, SCHEDULE_TIME_DOMAIN_TIMER) + +DAI_CONFIG(ACP_SDW, DI_SDW0_ACP_SW_AUDIO_4_TX, 2, DAI_BE_SDW0_ACP_SW_AUDIO_4_TX, + ACP_SDW_CONFIG(ACP_SDW_CONFIG_DATA(ACP_SDW, DI_SDW0_ACP_SW_AUDIO_4_TX, 48000, 2))) + +PCM_PLAYBACK_ADD(ACP-SW0-PIN4-Playback-SPK, 2, PIPELINE_PCM_2) +#/**********************************************************************************/ + +#/**********************************************************************************/ +#PCM 1, HS Capture, DAI link id 1, Dai index 11(DI_SDW0_ACP_SW_AUDIO_0_RX), BE DAI_BE_SDW0_ACP_SW_AUDIO_0_RX +# Capture pipeline 1 on PCM 1 using max 2 channels of s16le. +PIPELINE_PCM_ADD(sof/pipe-passthrough-capture.m4, + 1, 1, 2, s16le, + 2000, 0, 0, + 48000, 48000, 48000) + +# Capture DAI is ACP soundwire using 2 periods +DAI_ADD(sof/pipe-dai-capture.m4, 1, ACP_SDW, DI_SDW0_ACP_SW_AUDIO_0_RX, DAI_BE_SDW0_ACP_SW_AUDIO_0_RX, + PIPELINE_SINK_1, 2, s16le, 2000, 0, 0, SCHEDULE_TIME_DOMAIN_TIMER) + +DAI_CONFIG(ACP_SDW, DI_SDW0_ACP_SW_AUDIO_0_RX, 1, DAI_BE_SDW0_ACP_SW_AUDIO_0_RX, + ACP_SDW_CONFIG(ACP_SDW_CONFIG_DATA(ACP_SDW, DI_SDW0_ACP_SW_AUDIO_0_RX, 48000, 2))) + +PCM_CAPTURE_ADD(ACP-SW0-PIN11-Capture-HS, 1, PIPELINE_PCM_1) +#/**********************************************************************************/ +#/**********************************************************************************/ +#PCM 4, SDW Capture, DAI link id 4, Dai index 16(DI_SDW0_ACP_SW_AUDIO_5_RX), BE SDW0-PIN16-CAPTURE-SmartMic +# Capture pipeline 1 on PCM 1 using max 2 channels of s16le. +PIPELINE_PCM_ADD(sof/pipe-passthrough-capture.m4, + 4, 4, 2, s16le, + 2000, 0, 0, + 48000, 48000, 48000) + +# Capture DAI is ACP soundwire using 2 periods +DAI_ADD(sof/pipe-dai-capture.m4, 4, ACP_SDW, DI_SDW0_ACP_SW_AUDIO_5_RX, DAI_BE_SDW0_ACP_SW_AUDIO_5_RX, + PIPELINE_SINK_4, 2, s16le, 2000, 0, 0, SCHEDULE_TIME_DOMAIN_TIMER) + +DAI_CONFIG(ACP_SDW, DI_SDW0_ACP_SW_AUDIO_5_RX, 4, DAI_BE_SDW0_ACP_SW_AUDIO_5_RX, + ACP_SDW_CONFIG(ACP_SDW_CONFIG_DATA(ACP_SDW, DI_SDW0_ACP_SW_AUDIO_5_RX, 48000, 2))) + +PCM_CAPTURE_ADD(ACP-SW0-PIN16-CAPTURE-DMIC, 4, PIPELINE_PCM_4) +#/**********************************************************************************/ + +DEBUG_END \ No newline at end of file diff --git a/tools/topology/topology1/sof-acp_7_x_sdw_24bit.m4 b/tools/topology/topology1/sof-acp_7_x_sdw_24bit.m4 new file mode 100644 index 000000000000..7b34e3625501 --- /dev/null +++ b/tools/topology/topology1/sof-acp_7_x_sdw_24bit.m4 @@ -0,0 +1,139 @@ +#Required Topology for rt721 with ACP DMIC Card for ACP_7_X +# +# PCM Description DAI LINK DAI BE +# 0 HS Playback 0 SDW0-PIN0-PLAYBACK AUDIO_TX +# 1 HS Capture 1 SDW0-PIN11-CAPTURE AUDIO_RX +# 2 Speaker playback 2 SDW0-PIN4-PLAYBACK BT_TX +# 4 SDW DMIC 4 SDW0-PIN16-CAPTURE HS_RX + +# +# Define the pipelines +# +# PCM0 ----> buffer ----> AUDIO_TX +# PCM1 <---- buffer <---- AUDIO_RX +# PCM2 ----> buffer ----> BT_TX +# PCM4 <---- buffer <---- HS_RX + +# Include topology builder +include(`utils.m4') +include(`dai.m4') +include(`pipeline.m4') +include(`acp-sdw.m4') +include(`acp-dmic.m4') + +# Include TLV library +include(`common/tlv.m4') + +# Include Token library +include(`sof/tokens.m4') + +# Include ACP DSP configuration +include(`platform/amd/acp.m4') + +DEBUG_START + +#/**********************************************************************************/ +# PCM 0, HS Playback, DAI link id 0, Dai index 0(Audio_Tx), BE SW0-PIN0-PLAYBACK + +#Driver dai index and dai BE +#DAI Index(Instance * 64 + base_index) DAI_BE +#0(DI_SDW0_ACP_SW_AUDIO_0_TX) SDW0-PIN0-PLAYBACK-SimpleJack +# DI_SDW0_ACP_SW_AUDIO_4_TX SDW0-PIN4-PLAYBACK-SmartAmp + +define(DI_SDW0_ACP_SW_AUDIO_0_TX, 0) +define(DI_SDW0_ACP_SW_AUDIO_4_TX, 4) +define(DI_SDW0_ACP_SW_AUDIO_0_RX, 11) +define(DI_SDW0_ACP_SW_AUDIO_5_RX, 16) + + +define(DAI_BE_SDW0_ACP_SW_AUDIO_4_TX, SDW0-PIN4-PLAYBACK-SmartAmp) +define(DAI_BE_SDW0_ACP_SW_AUDIO_0_TX, SDW0-PIN0-PLAYBACK-SimpleJack) + +define(DAI_BE_SDW0_ACP_SW_AUDIO_0_RX, SDW0-PIN11-CAPTURE-SimpleJack) +define(DAI_BE_SDW0_ACP_SW_AUDIO_5_RX, SDW0-PIN16-CAPTURE-SmartMic) + +#pipeline: name of the predefined pipeline +#pipe id: pipeline ID. This should be a unique ID identifying the pipeline +#pcm: PCM ID. This will be used to bind to the correct front end DAI link + +dnl PIPELINE_PCM_ADD(pipeline, +dnl pipe id, pcm, max channels, format, +dnl period, priority, core, +dnl pcm_min_rate, pcm_max_rate, pipeline_rate) + +dnl DAI_ADD(pipeline, +dnl pipe id, dai type, dai_index, dai_be, +dnl buffer, periods, format, +dnl deadline, priority, core, time_domain) + +dnl DAI_CONFIG(type, dai_index, link_id, name, acphs_config/acpdmic_config) +dnl ACPHS_CONFIG(format, mclk, bclk, fsync, tdm, acphs_config_data) +dnl ACP_CLOCK(clock, freq, codec_provider, polarity) +dnl ACPHS_CONFIG_DATA(type, idx, valid bits, mclk_id) +dnl mclk_id is optional + +#/**********************************************************************************/ +PIPELINE_PCM_ADD(sof/pipe-passthrough-playback.m4, + 0, 0, 2, s32le, + 2000, 0, 0, + 48000, 48000, 48000) + +DAI_ADD(sof/pipe-dai-playback.m4, 0, ACP_SDW, DI_SDW0_ACP_SW_AUDIO_0_TX, DAI_BE_SDW0_ACP_SW_AUDIO_0_TX, +PIPELINE_SOURCE_0, 2, s24le, 2000, 0, 0, SCHEDULE_TIME_DOMAIN_TIMER) + +DAI_CONFIG(ACP_SDW, DI_SDW0_ACP_SW_AUDIO_0_TX, 0, DAI_BE_SDW0_ACP_SW_AUDIO_0_TX, + ACP_SDW_CONFIG(ACP_SDW_CONFIG_DATA(ACP_SDW, DI_SDW0_ACP_SW_AUDIO_0_TX, 48000, 2))) +PCM_PLAYBACK_ADD(ACP-SW0-PIN0-Playback-HS, 0, PIPELINE_PCM_0) + +#/**********************************************************************************/ +#PCM 2, Speaker Playback, PIPE line 2, DAI link id 2, Dai index 4(DI_SDW0_ACP_SW_AUDIO_4_TX), DAI BE SDW0-PIN4-PLAYBACK-SmartAmp +PIPELINE_PCM_ADD(sof/pipe-passthrough-playback.m4, + 2, 2, 2, s32le, + 2000, 0, 0, + 48000, 48000, 48000) + +# playback DAI is ACP soundwire using 2 periods +DAI_ADD(sof/pipe-dai-playback.m4, 2, ACP_SDW, DI_SDW0_ACP_SW_AUDIO_4_TX, DAI_BE_SDW0_ACP_SW_AUDIO_4_TX, + PIPELINE_SOURCE_2, 2, s24le, 2000, 0, 0, SCHEDULE_TIME_DOMAIN_TIMER) + +DAI_CONFIG(ACP_SDW, DI_SDW0_ACP_SW_AUDIO_4_TX, 2, DAI_BE_SDW0_ACP_SW_AUDIO_4_TX, + ACP_SDW_CONFIG(ACP_SDW_CONFIG_DATA(ACP_SDW, DI_SDW0_ACP_SW_AUDIO_4_TX, 48000, 2))) + +PCM_PLAYBACK_ADD(ACP-SW0-PIN4-Playback-SPK, 2, PIPELINE_PCM_2) +#/**********************************************************************************/ + +#/**********************************************************************************/ +#PCM 1, HS Capture, DAI link id 1, Dai index 11(DI_SDW0_ACP_SW_AUDIO_0_RX), BE DAI_BE_SDW0_ACP_SW_AUDIO_0_RX +# Capture pipeline 1 on PCM 1 using max 2 channels of s24le. +PIPELINE_PCM_ADD(sof/pipe-passthrough-capture.m4, + 1, 1, 2, s32le, + 2000, 0, 0, + 48000, 48000, 48000) + +# Capture DAI is ACP soundwire using 2 periods +DAI_ADD(sof/pipe-dai-capture.m4, 1, ACP_SDW, DI_SDW0_ACP_SW_AUDIO_0_RX, DAI_BE_SDW0_ACP_SW_AUDIO_0_RX, + PIPELINE_SINK_1, 2, s24le, 2000, 0, 0, SCHEDULE_TIME_DOMAIN_TIMER) + +DAI_CONFIG(ACP_SDW, DI_SDW0_ACP_SW_AUDIO_0_RX, 1, DAI_BE_SDW0_ACP_SW_AUDIO_0_RX, + ACP_SDW_CONFIG(ACP_SDW_CONFIG_DATA(ACP_SDW, DI_SDW0_ACP_SW_AUDIO_0_RX, 48000, 2))) + +PCM_CAPTURE_ADD(ACP-SW0-PIN11-Capture-HS, 1, PIPELINE_PCM_1) +#/**********************************************************************************/ +#/**********************************************************************************/ +#PCM 4, SDW Capture, DAI link id 4, Dai index 16(DI_SDW0_ACP_SW_AUDIO_5_RX), BE SDW0-PIN16-CAPTURE-SmartMic +# Capture pipeline 1 on PCM 1 using max 2 channels of s24le. +PIPELINE_PCM_ADD(sof/pipe-passthrough-capture.m4, + 4, 4, 2, s32le, + 2000, 0, 0, + 48000, 48000, 48000) + +# Capture DAI is ACP soundwire using 2 periods +DAI_ADD(sof/pipe-dai-capture.m4, 4, ACP_SDW, DI_SDW0_ACP_SW_AUDIO_5_RX, DAI_BE_SDW0_ACP_SW_AUDIO_5_RX, + PIPELINE_SINK_4, 2, s24le, 2000, 0, 0, SCHEDULE_TIME_DOMAIN_TIMER) + +DAI_CONFIG(ACP_SDW, DI_SDW0_ACP_SW_AUDIO_5_RX, 4, DAI_BE_SDW0_ACP_SW_AUDIO_5_RX, + ACP_SDW_CONFIG(ACP_SDW_CONFIG_DATA(ACP_SDW, DI_SDW0_ACP_SW_AUDIO_5_RX, 48000, 2))) + +PCM_CAPTURE_ADD(ACP-SW0-PIN16-CAPTURE-DMIC, 4, PIPELINE_PCM_4) + +DEBUG_END diff --git a/tools/topology/topology1/sof-acp_7_x_sdw_volume_comp.m4 b/tools/topology/topology1/sof-acp_7_x_sdw_volume_comp.m4 new file mode 100644 index 000000000000..1743a6c93830 --- /dev/null +++ b/tools/topology/topology1/sof-acp_7_x_sdw_volume_comp.m4 @@ -0,0 +1,229 @@ +#Required Topology for rt721 with ACP DMIC Card for ACP_7_X +# +# PCM Description DAI LINK DAI BE +# 0 HS Playback 0 SDW0-PIN0-PLAYBACK AUDIO_TX +# 1 HS Capture 1 SDW0-PIN11-CAPTURE AUDIO_RX +# 2 Speaker playback 2 SDW0-PIN4-PLAYBACK BT_TX +# 4 SDW DMIC 4 SDW0-PIN16-CAPTURE HS_RX + +# +# Define the pipelines +# +# PCM0 ----> buffer ----> AUDIO_TX +# PCM1 <---- buffer <---- AUDIO_RX +# PCM2 ----> buffer ----> BT_TX +# PCM4 <---- buffer <---- HS_RX + +# Include topology builder +include(`utils.m4') +include(`dai.m4') +include(`pipeline.m4') +include(`acp-sdw.m4') +include(`acp-dmic.m4') + +# Include TLV library +include(`common/tlv.m4') + +# Include Token library +include(`sof/tokens.m4') + +# Include ACP DSP configuration +include(`platform/amd/acp.m4') + +DEBUG_START + +#/**********************************************************************************/ +# PCM 0, HS Playback, DAI link id 0, Dai index 0(Audio_Tx), BE SW0-PIN0-PLAYBACK + +#Driver dai index and dai BE +#DAI Index(Instance * 64 + base_index) DAI_BE +#0(DI_SDW0_ACP_SW_AUDIO_0_TX) SDW0-PIN0-PLAYBACK-SimpleJack +# DI_SDW0_ACP_SW_AUDIO_4_TX SDW0-PIN4-PLAYBACK-SmartAmp + +define(DI_SDW0_ACP_SW_AUDIO_0_TX, 0 ) +define(DI_SDW0_ACP_SW_AUDIO_1_TX, 1 ) +define(DI_SDW0_ACP_SW_AUDIO_2_TX, 2 ) +define(DI_SDW0_ACP_SW_AUDIO_3_TX, 3 ) +define(DI_SDW0_ACP_SW_AUDIO_4_TX, 4 ) +define(DI_SDW0_ACP_SW_AUDIO_5_TX, 5 ) +define(DI_SDW0_ACP_SW_AUDIO_6_TX, 6 ) +define(DI_SDW0_ACP_SW_AUDIO_7_TX, 7 ) +define(DI_SDW0_ACP_SW_AUDIO_8_TX, 8 ) +define(DI_SDW0_ACP_SW_AUDIO_9_TX, 9 ) +define(DI_SDW0_ACP_SW_AUDIO_10_TX, 10) +define(DI_SDW0_ACP_SW_AUDIO_0_RX, 11) +define(DI_SDW0_ACP_SW_AUDIO_1_RX, 12) +define(DI_SDW0_ACP_SW_AUDIO_2_RX, 13) +define(DI_SDW0_ACP_SW_AUDIO_3_RX, 14) +define(DI_SDW0_ACP_SW_AUDIO_4_RX, 15) +define(DI_SDW0_ACP_SW_AUDIO_5_RX, 16) +define(DI_SDW0_ACP_SW_AUDIO_6_RX, 17) +define(DI_SDW0_ACP_SW_AUDIO_7_RX, 18) +define(DI_SDW0_ACP_SW_AUDIO_8_RX, 19) +define(DI_SDW0_ACP_SW_AUDIO_9_RX, 20) +define(DI_SDW0_ACP_SW_AUDIO_10_RX, 21) + +define(DI_SDW1_ACP_SW_AUDIO_0_TX, 64) +define(DI_SDW1_ACP_SW_AUDIO_1_TX, 65) +define(DI_SDW1_ACP_SW_AUDIO_2_TX, 66) +define(DI_SDW1_ACP_SW_AUDIO_3_TX, 67) +define(DI_SDW1_ACP_SW_AUDIO_4_TX, 68) +define(DI_SDW1_ACP_SW_AUDIO_5_TX, 69) +define(DI_SDW1_ACP_SW_AUDIO_6_TX, 70) +define(DI_SDW1_ACP_SW_AUDIO_7_TX, 71) +define(DI_SDW1_ACP_SW_AUDIO_8_TX, 72) +define(DI_SDW1_ACP_SW_AUDIO_9_TX, 73) +define(DI_SDW1_ACP_SW_AUDIO_10_TX, 74) +define(DI_SDW1_ACP_SW_AUDIO_0_RX, 75) +define(DI_SDW1_ACP_SW_AUDIO_1_RX, 76) +define(DI_SDW1_ACP_SW_AUDIO_2_RX, 77) +define(DI_SDW1_ACP_SW_AUDIO_3_RX, 78) +define(DI_SDW1_ACP_SW_AUDIO_4_RX, 79) +define(DI_SDW1_ACP_SW_AUDIO_5_RX, 80) +define(DI_SDW1_ACP_SW_AUDIO_6_RX, 81) +define(DI_SDW1_ACP_SW_AUDIO_7_RX, 82) +define(DI_SDW1_ACP_SW_AUDIO_8_RX, 83) +define(DI_SDW1_ACP_SW_AUDIO_9_RX, 84) +define(DI_SDW1_ACP_SW_AUDIO_10_RX, 85) + +define(DI_SDW2_ACP_SW_AUDIO_0_TX, 128) +define(DI_SDW2_ACP_SW_AUDIO_1_TX, 129) +define(DI_SDW2_ACP_SW_AUDIO_2_TX, 130) +define(DI_SDW2_ACP_SW_AUDIO_3_TX, 131) +define(DI_SDW2_ACP_SW_AUDIO_4_TX, 132) +define(DI_SDW2_ACP_SW_AUDIO_5_TX, 133) +define(DI_SDW2_ACP_SW_AUDIO_6_TX, 134) +define(DI_SDW2_ACP_SW_AUDIO_7_TX, 135) +define(DI_SDW2_ACP_SW_AUDIO_8_TX, 136) +define(DI_SDW2_ACP_SW_AUDIO_9_TX, 137) +define(DI_SDW2_ACP_SW_AUDIO_10_TX, 138) +define(DI_SDW2_ACP_SW_AUDIO_0_RX, 139) +define(DI_SDW2_ACP_SW_AUDIO_1_RX, 140) +define(DI_SDW2_ACP_SW_AUDIO_2_RX, 141) +define(DI_SDW2_ACP_SW_AUDIO_3_RX, 142) +define(DI_SDW2_ACP_SW_AUDIO_4_RX, 143) +define(DI_SDW2_ACP_SW_AUDIO_5_RX, 144) +define(DI_SDW2_ACP_SW_AUDIO_6_RX, 145) +define(DI_SDW2_ACP_SW_AUDIO_7_RX, 146) +define(DI_SDW2_ACP_SW_AUDIO_8_RX, 147) +define(DI_SDW2_ACP_SW_AUDIO_9_RX, 148) +define(DI_SDW2_ACP_SW_AUDIO_10_RX, 149) + +define(DI_SDW3_ACP_SW_AUDIO_0_TX, 192) +define(DI_SDW3_ACP_SW_AUDIO_1_TX, 193) +define(DI_SDW3_ACP_SW_AUDIO_2_TX, 194) +define(DI_SDW3_ACP_SW_AUDIO_3_TX, 195) +define(DI_SDW3_ACP_SW_AUDIO_4_TX, 196) +define(DI_SDW3_ACP_SW_AUDIO_5_TX, 197) +define(DI_SDW3_ACP_SW_AUDIO_6_TX, 198) +define(DI_SDW3_ACP_SW_AUDIO_7_TX, 199) +define(DI_SDW3_ACP_SW_AUDIO_8_TX, 200) +define(DI_SDW3_ACP_SW_AUDIO_9_TX, 201) +define(DI_SDW3_ACP_SW_AUDIO_10_TX, 202) +define(DI_SDW3_ACP_SW_AUDIO_0_RX, 203) +define(DI_SDW3_ACP_SW_AUDIO_1_RX, 204) +define(DI_SDW3_ACP_SW_AUDIO_2_RX, 205) +define(DI_SDW3_ACP_SW_AUDIO_3_RX, 206) +define(DI_SDW3_ACP_SW_AUDIO_4_RX, 207) +define(DI_SDW3_ACP_SW_AUDIO_5_RX, 208) +define(DI_SDW3_ACP_SW_AUDIO_6_RX, 209) +define(DI_SDW3_ACP_SW_AUDIO_7_RX, 210) +define(DI_SDW3_ACP_SW_AUDIO_8_RX, 211) +define(DI_SDW3_ACP_SW_AUDIO_9_RX, 212) +define(DI_SDW3_ACP_SW_AUDIO_10_RX, 213) + + +define(DAI_BE_SDW0_ACP_SW_AUDIO_4_TX, SDW0-PIN4-PLAYBACK-SmartAmp) +define(DAI_BE_SDW0_ACP_SW_AUDIO_0_TX, SDW0-PIN0-PLAYBACK-SimpleJack) +#define(DAI_BE_SDW1_ACP_SW_AUDIO_4_TX, SDW1-PIN4-PLAYBACK-SmartAmp) + +define(DAI_BE_SDW0_ACP_SW_AUDIO_0_RX, SDW0-PIN11-CAPTURE-SimpleJack) +define(DAI_BE_SDW0_ACP_SW_AUDIO_4_RX, SDW0-PIN15-CAPTURE-SmartMic) +define(DAI_BE_SDW0_ACP_SW_AUDIO_5_RX, SDW0-PIN16-CAPTURE-SmartMic) + +#pipeline: name of the predefined pipeline +#pipe id: pipeline ID. This should be a unique ID identifying the pipeline +#pcm: PCM ID. This will be used to bind to the correct front end DAI link + +dnl PIPELINE_PCM_ADD(pipeline, +dnl pipe id, pcm, max channels, format, +dnl period, priority, core, +dnl pcm_min_rate, pcm_max_rate, pipeline_rate) + +dnl DAI_ADD(pipeline, +dnl pipe id, dai type, dai_index, dai_be, +dnl buffer, periods, format, +dnl deadline, priority, core, time_domain) + +dnl DAI_CONFIG(type, dai_index, link_id, name, acphs_config/acpdmic_config) +dnl ACPHS_CONFIG(format, mclk, bclk, fsync, tdm, acphs_config_data) +dnl ACP_CLOCK(clock, freq, codec_provider, polarity) +dnl ACPHS_CONFIG_DATA(type, idx, valid bits, mclk_id) +dnl mclk_id is optional + +#/**********************************************************************************/ +PIPELINE_PCM_ADD(sof/pipe-volume-playback.m4, + 0, 0, 2, s16le, + 2000, 0, 0, + 48000, 48000, 48000) + +DAI_ADD(sof/pipe-dai-playback.m4, 0, ACP_SDW, DI_SDW0_ACP_SW_AUDIO_0_TX, DAI_BE_SDW0_ACP_SW_AUDIO_0_TX, +PIPELINE_SOURCE_0, 2, s16le, 2000, 0, 0, SCHEDULE_TIME_DOMAIN_TIMER) + +DAI_CONFIG(ACP_SDW, DI_SDW0_ACP_SW_AUDIO_0_TX, 0, DAI_BE_SDW0_ACP_SW_AUDIO_0_TX, + ACP_SDW_CONFIG(ACP_SDW_CONFIG_DATA(ACP_SDW, DI_SDW0_ACP_SW_AUDIO_0_TX, 48000, 2))) +PCM_PLAYBACK_ADD(ACP-SW0-PIN0-Playback-HS, 0, PIPELINE_PCM_0) + +#/**********************************************************************************/ +#PCM 2, Speaker Playback, PIPE line 2, DAI link id 2, Dai index 4(DI_SDW0_ACP_SW_AUDIO_4_TX), DAI BE SDW0-PIN4-PLAYBACK-SmartAmp +PIPELINE_PCM_ADD(sof/pipe-volume-playback.m4, + 2, 2, 2, s16le, + 2000, 0, 0, + 48000, 48000, 48000) + +# playback DAI is ACP soundwire using 2 periods +DAI_ADD(sof/pipe-dai-playback.m4, 2, ACP_SDW, DI_SDW0_ACP_SW_AUDIO_4_TX, DAI_BE_SDW0_ACP_SW_AUDIO_4_TX, + PIPELINE_SOURCE_2, 2, s16le, 2000, 0, 0, SCHEDULE_TIME_DOMAIN_TIMER) + +DAI_CONFIG(ACP_SDW, DI_SDW0_ACP_SW_AUDIO_4_TX, 2, DAI_BE_SDW0_ACP_SW_AUDIO_4_TX, + ACP_SDW_CONFIG(ACP_SDW_CONFIG_DATA(ACP_SDW, DI_SDW0_ACP_SW_AUDIO_4_TX, 48000, 2))) + +PCM_PLAYBACK_ADD(ACP-SW0-PIN4-Playback-SPK, 2, PIPELINE_PCM_2) +#/**********************************************************************************/ + +#/**********************************************************************************/ +#PCM 1, HS Capture, DAI link id 1, Dai index 11(DI_SDW0_ACP_SW_AUDIO_0_RX), BE DAI_BE_SDW0_ACP_SW_AUDIO_0_RX +# Capture pipeline 1 on PCM 1 using max 2 channels of s16le. +PIPELINE_PCM_ADD(sof/pipe-volume-capture.m4, + 1, 1, 2, s16le, + 2000, 0, 0, + 48000, 48000, 48000) + +# Capture DAI is ACP soundwire using 2 periods +DAI_ADD(sof/pipe-dai-capture.m4, 1, ACP_SDW, DI_SDW0_ACP_SW_AUDIO_0_RX, DAI_BE_SDW0_ACP_SW_AUDIO_0_RX, + PIPELINE_SINK_1, 2, s16le, 2000, 0, 0, SCHEDULE_TIME_DOMAIN_TIMER) + +DAI_CONFIG(ACP_SDW, DI_SDW0_ACP_SW_AUDIO_0_RX, 1, DAI_BE_SDW0_ACP_SW_AUDIO_0_RX, + ACP_SDW_CONFIG(ACP_SDW_CONFIG_DATA(ACP_SDW, DI_SDW0_ACP_SW_AUDIO_0_RX, 48000, 2))) + +PCM_CAPTURE_ADD(ACP-SW0-PIN11-Capture-HS, 1, PIPELINE_PCM_1) +#/**********************************************************************************/ +#/**********************************************************************************/ +#PCM 4, SDW Capture, DAI link id 4, Dai index 16(DI_SDW0_ACP_SW_AUDIO_5_RX), BE SDW0-PIN16-CAPTURE-SmartMic +# Capture pipeline 1 on PCM 1 using max 2 channels of s16le. +PIPELINE_PCM_ADD(sof/pipe-volume-capture.m4, + 4, 4, 2, s16le, + 2000, 0, 0, + 48000, 48000, 48000) + +# Capture DAI is ACP soundwire using 2 periods +DAI_ADD(sof/pipe-dai-capture.m4, 4, ACP_SDW, DI_SDW0_ACP_SW_AUDIO_5_RX, DAI_BE_SDW0_ACP_SW_AUDIO_5_RX, + PIPELINE_SINK_4, 2, s16le, 2000, 0, 0, SCHEDULE_TIME_DOMAIN_TIMER) + +DAI_CONFIG(ACP_SDW, DI_SDW0_ACP_SW_AUDIO_5_RX, 4, DAI_BE_SDW0_ACP_SW_AUDIO_5_RX, + ACP_SDW_CONFIG(ACP_SDW_CONFIG_DATA(ACP_SDW, DI_SDW0_ACP_SW_AUDIO_5_RX, 48000, 2))) + +PCM_CAPTURE_ADD(ACP-SW0-PIN16-CAPTURE-DMIC, 4, PIPELINE_PCM_4) +#/**********************************************************************************/ + +DEBUG_END 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/topology1/sof/tokens.m4 b/tools/topology/topology1/sof/tokens.m4 index b9748bb52fbc..a9a12f5264b6 100644 --- a/tools/topology/topology1/sof/tokens.m4 +++ b/tools/topology/topology1/sof/tokens.m4 @@ -141,6 +141,7 @@ SectionVendorTokens."sof_acp_tokens" { SOF_TKN_AMD_ACP_RATE "1700" SOF_TKN_AMD_ACP_CH "1701" SOF_TKN_AMD_ACP_I2S_TDM_MODE "1702" + SOF_TKN_AMD_ACP_I2S_BITDEPTH "1703" } SectionVendorTokens."sof_acpdmic_tokens" { diff --git a/tools/topology/topology2/README.md b/tools/topology/topology2/README.md new file mode 100644 index 000000000000..759867a47d1c --- /dev/null +++ b/tools/topology/topology2/README.md @@ -0,0 +1,406 @@ +# 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` | +| USB Audio Offload (UAOL) | 21 | `UAOL_PCM_ID` | +| Deepbuffer UAOL | 22 | `DEEP_BUFFER_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` | +| UAOL Playback Host / DAI | 210 / 211 | `UAOL_PB_HOST_PIPELINE_ID` / `UAOL_PB_DAI_PIPELINE_ID` | +| UAOL Capture Host / DAI | 212 / 213 | `UAOL_CP_HOST_PIPELINE_ID` / `UAOL_CP_DAI_PIPELINE_ID` | +| UAOL Deepbuffer | 214 | `DEEP_BUFFER_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..d1357caa20ab 100644 --- a/tools/topology/topology2/cavs-benchmark-hda.conf +++ b/tools/topology/topology2/cavs-benchmark-hda.conf @@ -33,20 +33,24 @@ <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> <multiband_drc.conf> +<phase_vocoder.conf> <rtnr.conf> <sound_dose.conf> <src.conf> <src_lite.conf> +<stft_process.conf> <tdfb.conf> <template_comp.conf> @@ -192,7 +196,7 @@ Object.PCM.pcm [ direction "playback" name $ANALOG_PLAYBACK_PCM formats 'S32_LE,S24_LE,S16_LE' - rates "8000,11025,16000,22050,32000,44100,48000,64000,88200,96000,176400,192000" + rates "8000,11025,16000,22050,24000,32000,44100,48000,64000,88200,96000,176400,192000" channels_min $BENCH_PCM_CHANNELS_MIN channels_max $BENCH_PCM_CHANNELS_MAX } @@ -200,7 +204,7 @@ Object.PCM.pcm [ direction "capture" name $ANALOG_CAPTURE_PCM formats 'S32_LE,S24_LE,S16_LE' - rates "8000,11025,16000,22050,32000,44100,48000,64000,88200,96000,176400,192000" + rates "8000,11025,16000,22050,24000,32000,44100,48000,64000,88200,96000,176400,192000" channels_min $BENCH_PCM_CHANNELS_MIN channels_max $BENCH_PCM_CHANNELS_MAX } @@ -686,6 +690,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 +819,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 # @@ -819,6 +865,22 @@ IncludeByKey.BENCH_CONFIG { <include/bench/micsel_multich_s32.conf> } + # + # Phase Vocoder component + # + + "phase_vocoder16" { + <include/bench/phase_vocoder_s16.conf> + } + + "phase_vocoder24" { + <include/bench/phase_vocoder_s24.conf> + } + + "phase_vocoder32" { + <include/bench/phase_vocoder_s32.conf> + } + # # RTNR component # @@ -883,6 +945,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..b19a26d74e7f 100644 --- a/tools/topology/topology2/cavs-benchmark-sdw.conf +++ b/tools/topology/topology2/cavs-benchmark-sdw.conf @@ -39,12 +39,15 @@ <gain.conf> <igo_nr.conf> <level_multiplier.conf> +<mfcc.conf> <micsel.conf> <multiband_drc.conf> +<phase_vocoder.conf> <rtnr.conf> <sound_dose.conf> <src.conf> <src_lite.conf> +<stft_process.conf> <tdfb.conf> <template_comp.conf> @@ -135,7 +138,7 @@ Object.PCM.pcm [ Object.PCM.pcm_caps.1 { name $ANALOG_PLAYBACK_PCM formats 'S16_LE,S24_LE,S32_LE' - rates "8000,11025,16000,22050,32000,44100,48000,64000,88200,96000,176400,192000" + rates "8000,11025,16000,22050,24000,32000,44100,48000,64000,88200,96000,176400,192000" channels_min $BENCH_PCM_CHANNELS_MIN channels_max $BENCH_PCM_CHANNELS_MAX } @@ -151,7 +154,7 @@ Object.PCM.pcm [ Object.PCM.pcm_caps.1 { name $ANALOG_CAPTURE_PCM formats 'S16_LE,S24_LE,S32_LE' - rates "8000,11025,16000,22050,32000,44100,48000,64000,88200,96000,176400,192000" + rates "8000,11025,16000,22050,24000,32000,44100,48000,64000,88200,96000,176400,192000" channels_min $BENCH_PCM_CHANNELS_MIN channels_max $BENCH_PCM_CHANNELS_MAX } @@ -439,6 +442,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 # @@ -459,6 +468,22 @@ IncludeByKey.BENCH_CONFIG { <include/bench/micsel_multich_s32.conf> } + # + # Phase Vocoder component + # + + "phase_vocoder16" { + <include/bench/phase_vocoder_s16.conf> + } + + "phase_vocoder24" { + <include/bench/phase_vocoder_s24.conf> + } + + "phase_vocoder32" { + <include/bench/phase_vocoder_s32.conf> + } + # # RTNR component # @@ -523,6 +548,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..aec5016de76d 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. @@ -697,9 +698,9 @@ IncludeByKey.PASSTHROUGH { IncludeByKey.SRC_DOMAIN { "DP" { core_id $DP_SRC_CORE_ID - domain_id 123 - stack_bytes_requirement 4096 - heap_bytes_requirement 8192 + domain_id 0 + stack_bytes_requirement 2048 + heap_bytes_requirement "$[(24 * 1024)]" } } } @@ -1381,9 +1382,9 @@ IncludeByKey.PASSTHROUGH { IncludeByKey.SRC_DOMAIN { "DP" { core_id $DP_SRC_CORE_ID - domain_id 123 - stack_bytes_requirement 4096 - heap_bytes_requirement 8192 + domain_id 0 + stack_bytes_requirement 2048 + heap_bytes_requirement "$[(24 * 1024)]" } } @@ -1697,14 +1698,14 @@ IncludeByKey.PASSTHROUGH { direction "playback" name "SSP2 Playback" formats 'S16_LE,S24_LE,S32_LE' - rates "8000,11025,16000,22050,32000,44100,48000,64000,88200,96000,176400,192000" + rates "8000,11025,16000,22050,24000,32000,44100,48000,64000,88200,96000,176400,192000" } Object.PCM.pcm_caps.2 { direction "capture" name "SSP2 Capture" formats 'S16_LE,S24_LE,S32_LE' - rates "8000,11025,16000,22050,32000,44100,48000,64000,88200,96000,176400,192000" + rates "8000,11025,16000,22050,24000,32000,44100,48000,64000,88200,96000,176400,192000" } } ] 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..311e0376b333 100644 --- a/tools/topology/topology2/cavs-sdw.conf +++ b/tools/topology/topology2/cavs-sdw.conf @@ -13,14 +13,26 @@ <virtual.conf> <host-copier-gain-mixin-playback.conf> <mixout-gain-alh-dai-copier-playback.conf> +<mixout-gain-ctc-alh-dai-copier-playback.conf> +<mixout-gain-dax-alh-dai-copier-playback.conf> +<mixout-gain-dax-ctc-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-eqfir-mbdrc-alh-dai-copier-playback.conf> +<mixout-gain-eqiir-dts-alh-dai-copier-playback.conf> +<mixout-gain-eqiir-dts-ctc-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-micsel-mfcc-capture.conf> +<src.conf> <io-gateway.conf> <io-gateway-capture.conf> <highpass-capture-be.conf> @@ -31,6 +43,7 @@ <alh.conf> <hda.conf> <dmic.conf> +<ctc.conf> <intel/hw_config_cardinal_clk.conf> <manifest.conf> <route.conf> @@ -49,6 +62,8 @@ <ssp.conf> <bt-default.conf> <deep-buffer-default.conf> +<compr-default.conf> +<siggen-host-copier-capture.conf> Define { PLATFORM "none" @@ -66,6 +81,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 +92,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 @@ -93,11 +116,22 @@ Define { SDW_DMIC_ENHANCED_CAPTURE true EFX_SPK_IIR_PARAMS "highpass_100hz_0db_48khz" EFX_SPK_FIR_PARAMS "passthrough" + EFX_SPK_DRC_COMPONENT "singleband" EFX_SPK_DRC_PARAMS "speaker_default" + EFX_SPK_MBDRC_PARAMS "passthrough" EFX_MIC_IIR_PARAMS "highpass_40hz_0db_48khz" 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 +139,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 +160,66 @@ 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]" { + IncludeByKey.SDW_AMP_PIPELINE_SRC { + "dax" "platform/intel/sdw-amp-dax-ctc.conf" + "dts" "platform/intel/sdw-amp-dts-ctc.conf" + "generic" "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 +227,63 @@ 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.NUM_SDW_AMP_CTC_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_JACK_COMPR_AUDIO_FEATURE_CAPTURE { + "true" "platform/intel/sdw-jack-audio-feature-compress.conf" +} + +IncludeByKey.SDW_DMIC_AUDIO_FEATURE_CAPTURE { + "true" "platform/intel/sdw-dmic-audio-feature.conf" +} + +IncludeByKey.SDW_DMIC_COMPR_AUDIO_FEATURE_CAPTURE { + "true" "platform/intel/sdw-dmic-audio-feature-compress.conf" +} diff --git a/tools/topology/topology2/cavs-src-mixin-mixout-hda.conf b/tools/topology/topology2/cavs-src-mixin-mixout-hda.conf index b567e5f810bb..77cc8329ec6e 100644 --- a/tools/topology/topology2/cavs-src-mixin-mixout-hda.conf +++ b/tools/topology/topology2/cavs-src-mixin-mixout-hda.conf @@ -7,6 +7,8 @@ Define { DEEP_BUFFER_PIPELINE_SRC 'mixin.15.1' DEEP_BUFFER_PIPELINE_SINK 'mixout.2.1' DEEP_BUFFER_PCM_NAME 'Deepbuffer HDA Analog' + SRC_CORE 0 + SRC_DOMAIN "default" } # include deep buffer config if buffer size is in 1 - 1000 ms. @@ -31,6 +33,7 @@ Object.Pipeline { mixout-gain-dai-copier-playback [ { index 2 + core_id $SRC_CORE Object.Widget.dai-copier.1 { node_type $HDA_LINK_OUTPUT_CLASS @@ -43,12 +46,16 @@ Object.Pipeline { name '2 Main Playback Volume' } } + Object.Widget.pipeline.1 { + core $SRC_CORE + } } ] host-copier-gain-src-mixin-playback [ { index 1 + core_id $SRC_CORE Object.Widget.host-copier.1 { stream_name $ANALOG_PLAYBACK_PCM @@ -56,6 +63,7 @@ Object.Pipeline { } Object.Widget.src.1 { + scheduler_domain "$SRC_DOMAIN" } Object.Widget.gain.1 { @@ -63,6 +71,9 @@ Object.Pipeline { name '1 2nd Playback Volume' } } + Object.Widget.pipeline.1 { + core $SRC_CORE + } } ] diff --git a/tools/topology/topology2/cavs-uaol.conf b/tools/topology/topology2/cavs-uaol.conf new file mode 100644 index 000000000000..0baa4cb28f74 --- /dev/null +++ b/tools/topology/topology2/cavs-uaol.conf @@ -0,0 +1,86 @@ +# Basic USB Audio Offload Link (UAOL) topology. +# +# This is a function topology, loaded next to the other function topologies of +# the sof_sdw card, so its PCM and pipeline IDs must not collide with theirs. +# +# The UAOL function itself lives in platform/intel/uaol-generic.conf. + +<searchdir:include> +<searchdir:include/common> +<searchdir:include/components> +<searchdir:include/dais> +<searchdir:include/pipelines> +<searchdir:include/pipelines/cavs> +<searchdir:platform> +<searchdir:platform/intel> + +<vendor-token.conf> +<manifest.conf> +<tokens.conf> +<virtual.conf> +<host-copier-gain-mixin-playback.conf> +<mixout-gain-dai-copier-playback.conf> +<deepbuffer-playback.conf> +<host-gateway-capture.conf> +<dai-copier-eqiir-module-copier-capture.conf> +<input_audio_format.conf> +<output_audio_format.conf> +<data.conf> +<pcm.conf> +<pcm_caps.conf> +<fe_dai.conf> +<uaol.conf> +<hw_config_simple.conf> +<manifest.conf> +<route.conf> +<common_definitions.conf> +<deep-buffer-default.conf> +<dai-copier.conf> +<module-copier.conf> +<pipeline.conf> +<dai.conf> +<host.conf> + +Define { + UAOL_PLAYBACK_PCM 'UAOL Playback' + UAOL_CAPTURE_PCM 'UAOL Capture' + # The two directions are separate DAI links so that the format fixup of + # a link is decided by the DAI copier of that direction alone. The names + # are abbreviated because alsatplg drops widget tuples above 36 characters + # and the dai-copier name is 'dai-copier.UAOL.<name>.<direction>' + UAOL_PB_DAI_NAME 'UAOL0-Play' + UAOL_CP_DAI_NAME 'UAOL0-Cap' + # The BE IDs are the dai link IDs that are matched with the machine + # driver, which creates the playback link first + UAOL_PB_BE_ID 9 + UAOL_CP_BE_ID 10 + # UAOL_DAI_INDEX is the UAOL link the DSP uses, only link 0 is addressable + UAOL_DAI_INDEX 0 + UAOL_PCM_NAME 'UAOL' + UAOL_PCM_ID 21 + UAOL_PB_HOST_PIPELINE_ID 210 + UAOL_PB_DAI_PIPELINE_ID 211 + UAOL_CP_HOST_PIPELINE_ID 212 + UAOL_CP_DAI_PIPELINE_ID 213 + # Both paths pass through whatever the offloaded USB device asks for, so + # the PCM advertises everything a USB device may support. 7 channels is + # left out of the widget formats, it has no channel config and no channel + # map, so it is the one count in the range which cannot be run. + UAOL_CHANNELS_MIN 1 + UAOL_CHANNELS_MAX 8 + # UAOL supports up to 192 kHz + UAOL_RATE_MIN 8000 + UAOL_RATE_MAX 192000 + UAOL_RATES '8000,11025,12000,16000,22050,24000,32000,44100,48000,64000,88200,96000,128000,176400,192000' + + # The deep buffer PCM feeds the same mixer as the normal playback PCM. It + # takes the source and the sink as plain widget names, so it repeats the + # pipeline IDs defined above. + DEEP_BUFFER_PCM_NAME 'Deepbuffer UAOL' + DEEP_BUFFER_PCM_ID 22 + DEEP_BUFFER_PIPELINE_ID 214 + DEEP_BUFFER_PIPELINE_SRC 'mixin.214.1' + DEEP_BUFFER_PIPELINE_SINK 'mixout.211.1' +} + +<platform/intel/uaol-generic.conf> diff --git a/tools/topology/topology2/development/tplg-targets-bench.cmake b/tools/topology/topology2/development/tplg-targets-bench.cmake index 4508fbb47d8f..2dd4f28bc07c 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,19 @@ set(components "gain" "igo_nr" "level_multiplier" + "mfcc" + "mfccmel" "micsel" + "phase_vocoder" "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 +38,27 @@ 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_PHASE_VOCODER_PARAMS=default" "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 +90,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..8835fad0635d 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,\ @@ -266,6 +270,10 @@ GOOGLE_RTC_AEC_SUPPORT=1,DEEP_BUF_SPK=true,GOOGLE_AEC_DP_CORE_ID=2" # CAVS HDA topology with gain and SRC before mixin for HDA and passthrough pipelines for HDMI "sof-hda-generic\;sof-hda-src-generic\;HDA_CONFIG=src" +# Like above but with SRC in DP mode on core 2 +"sof-hda-generic\;sof-hda-src-generic-dp\;HDA_CONFIG=src,DEEP_BUFFER_CORE=2,SRC_CORE=2,\ +SRC_DOMAIN=DP" + # BT offload for tgl "cavs-nocodec-bt\;sof-nocodec-bt-tgl\;PREPROCESS_PLUGINS=nhlt,NHLT_BIN=nhlt-sof-nocodec-bt-tgl.bin,\ PLATFORM=tgl" @@ -306,9 +314,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 +377,156 @@ 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-mel-normal\;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-mel-normal\;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" + +# Soundwire topologies with compress MFCC mel audio features capture +"cavs-sdw\;sof-mtl-rt713-l0-rt1316-l12-mfcc-mel-compr\;PLATFORM=mtl,NUM_SDW_AMP_LINKS=2,\ +HDMI1_ID=4,HDMI2_ID=5,HDMI3_ID=6,SDW_JACK_COMPR_AUDIO_FEATURE_CAPTURE=true,\ +MFCC_FRAME_BYTES=344,MFCC_BLOB=mel" + +# Soundwire topologies with compress MFCC cepstral audio features capture +"cavs-sdw\;sof-mtl-rt713-l0-rt1316-l12-mfcc-ceps-compr\;PLATFORM=mtl,NUM_SDW_AMP_LINKS=2,\ +HDMI1_ID=4,HDMI2_ID=5,HDMI3_ID=6,SDW_JACK_COMPR_AUDIO_FEATURE_CAPTURE=true,\ +MFCC_FRAME_BYTES=76,MFCC_BLOB=ceps" + +"cavs-sdw\;sof-arl-cs42l43-l0-cs35l56-l23-mfcc-mel-compr\;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_COMPR_AUDIO_FEATURE_CAPTURE=true,SDW_DMIC_COMPR_AUDIO_FEATURE_CAPTURE=true,\ +MFCC_FRAME_BYTES=344,MFCC_BLOB=mel" + +"cavs-sdw\;sof-arl-cs42l43-l0-cs35l56-l23-mfcc-ceps-compr\;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_COMPR_AUDIO_FEATURE_CAPTURE=true,SDW_DMIC_COMPR_AUDIO_FEATURE_CAPTURE=true,\ +MFCC_FRAME_BYTES=76,MFCC_BLOB=ceps" + +# Soundwire topologies with multiband-DRC +"cavs-sdw\;sof-mtl-rt713-l0-rt1316-l12-mbdrc\;PLATFORM=mtl,NUM_SDW_AMP_LINKS=2,\ +HDMI1_ID=4,HDMI2_ID=5,HDMI3_ID=6,EFX_SPK_DRC_COMPONENT=multiband" + +"cavs-sdw\;sof-arl-cs42l43-l0-cs35l56-l23-mbdrc\;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,\ +EFX_SPK_DRC_COMPONENT=multiband" + +# Soundwire compressed feature topologies +"cavs-sdw\;sof-sdca-jack-compr\;SDW_JACK=false,NUM_HDMIS=0,COMPRESSED=true,\ +COMPRESSED_1=true,COMPRESSED_2=false" + +"cavs-sdw\;sof-sdca-amp-compr\;SDW_JACK=false,NUM_HDMIS=0,COMPRESSED=true,\ +COMPRESSED_1=false,COMPRESSED_2=true" + ) diff --git a/tools/topology/topology2/dmic-wov-feature.conf b/tools/topology/topology2/dmic-wov-feature.conf new file mode 100644 index 000000000000..a260404cf33b --- /dev/null +++ b/tools/topology/topology2/dmic-wov-feature.conf @@ -0,0 +1,31 @@ +<searchdir:include> +<searchdir:include/common> +<searchdir:include/components> +<searchdir:include/dais> +<searchdir:include/pipelines> +<searchdir:include/pipelines/cavs> +<searchdir:platform> +<searchdir:platform/intel> + +<vendor-token.conf> +<tokens.conf> +<common_definitions.conf> +<dmic-default.conf> +<dai-kpb-be.conf> +<host-gateway-capture.conf> +<wov-detect.conf> +<dai-copier.conf> +<host-copier.conf> +<output_pin_binding.conf> +<input_pin_binding.conf> +<kpb.conf> +<wov.conf> +<data.conf> +<manifest.conf> +<virtual.conf> +<route.conf> +<pcm.conf> +<pcm_caps.conf> +<fe_dai.conf> + +<platform/intel/dmic-wov.conf> 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..8788387ec8c7 --- /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' + } + #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..007dbb91cd4f --- /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' + } + #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/one_input_output_format_s32_dp128.conf b/tools/topology/topology2/include/bench/one_input_output_format_s32_dp128.conf new file mode 100644 index 000000000000..379ee73f9eff --- /dev/null +++ b/tools/topology/topology2/include/bench/one_input_output_format_s32_dp128.conf @@ -0,0 +1,19 @@ + 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 + ibs 1024 + } + ] + Object.Base.output_audio_format [ + { + out_bit_depth 32 + out_valid_bit_depth 32 + obs 1024 + } + ] + diff --git a/tools/topology/topology2/include/bench/one_input_output_format_s32_dp192.conf b/tools/topology/topology2/include/bench/one_input_output_format_s32_dp192.conf new file mode 100644 index 000000000000..b3989ce90f36 --- /dev/null +++ b/tools/topology/topology2/include/bench/one_input_output_format_s32_dp192.conf @@ -0,0 +1,19 @@ + 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 + ibs 1536 + } + ] + Object.Base.output_audio_format [ + { + out_bit_depth 32 + out_valid_bit_depth 32 + obs 1536 + } + ] + diff --git a/tools/topology/topology2/include/bench/one_input_output_format_s32_dp256.conf b/tools/topology/topology2/include/bench/one_input_output_format_s32_dp256.conf new file mode 100644 index 000000000000..d753c1ea114f --- /dev/null +++ b/tools/topology/topology2/include/bench/one_input_output_format_s32_dp256.conf @@ -0,0 +1,19 @@ + 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 + ibs 2048 + } + ] + Object.Base.output_audio_format [ + { + out_bit_depth 32 + out_valid_bit_depth 32 + obs 2048 + } + ] + diff --git a/tools/topology/topology2/include/bench/one_input_output_format_s32_dp_5ms.conf b/tools/topology/topology2/include/bench/one_input_output_format_s32_dp_5ms.conf new file mode 100644 index 000000000000..097f75fb121e --- /dev/null +++ b/tools/topology/topology2/include/bench/one_input_output_format_s32_dp_5ms.conf @@ -0,0 +1,22 @@ + num_input_audio_formats 1 + num_output_audio_formats 1 + + # 32-bit 48KHz 2ch, ibs/obs is set to match + # 5 ms or 240 stereo frames period. + Object.Base.input_audio_format [ + { + in_bit_depth 32 + in_valid_bit_depth 32 + in_channels 2 + ibs 1920 + } + ] + Object.Base.output_audio_format [ + { + out_bit_depth 32 + out_valid_bit_depth 32 + out_channels 2 + obs 1920 + } + ] + diff --git a/tools/topology/topology2/include/bench/phase_vocoder_controls_capture.conf b/tools/topology/topology2/include/bench/phase_vocoder_controls_capture.conf new file mode 100644 index 000000000000..c25aaebfcc58 --- /dev/null +++ b/tools/topology/topology2/include/bench/phase_vocoder_controls_capture.conf @@ -0,0 +1,17 @@ + # Created initially with script "./bench_comp_generate.sh phase_vocoder" + # may need edits to modify controls + Object.Control { + # Un-comment the supported controls in PHASE_VOCODER + bytes."1" { + name '$ANALOG_CAPTURE_PCM Phase Vocoder bytes' + IncludeByKey.BENCH_PHASE_VOCODER_PARAMS { + "default" "include/components/phase_vocoder/hann_1024_256.conf" + } + } + mixer."1" { + name '$ANALOG_CAPTURE_PCM Phase Vocoder enable' + } + enum."1" { + name '$ANALOG_CAPTURE_PCM Phase Vocoder speed' + } + } diff --git a/tools/topology/topology2/include/bench/phase_vocoder_controls_playback.conf b/tools/topology/topology2/include/bench/phase_vocoder_controls_playback.conf new file mode 100644 index 000000000000..fb6c3da0e369 --- /dev/null +++ b/tools/topology/topology2/include/bench/phase_vocoder_controls_playback.conf @@ -0,0 +1,17 @@ + # Created initially with script "./bench_comp_generate.sh phase_vocoder" + # may need edits to modify controls + Object.Control { + # Un-comment the supported controls in PHASE_VOCODER + bytes."1" { + name '$ANALOG_PLAYBACK_PCM Phase Vocoder bytes' + IncludeByKey.BENCH_PHASE_VOCODER_PARAMS { + "default" "include/components/phase_vocoder/hann_1024_256.conf" + } + } + mixer."1" { + name '$ANALOG_PLAYBACK_PCM Phase Vocoder enable' + } + enum."1" { + name '$ANALOG_PLAYBACK_PCM Phase Vocoder speed' + } + } diff --git a/tools/topology/topology2/include/bench/phase_vocoder_route.conf b/tools/topology/topology2/include/bench/phase_vocoder_route.conf new file mode 100644 index 000000000000..4213b01c87ed --- /dev/null +++ b/tools/topology/topology2/include/bench/phase_vocoder_route.conf @@ -0,0 +1,19 @@ + # Created with script "./bench_comp_generate.sh phase_vocoder" + Object.Base.route [ + { + sink '$BENCH_PLAYBACK_DAI_COPIER' + source 'phase_vocoder.$BENCH_PLAYBACK_HOST_PIPELINE.1' + } + { + sink 'phase_vocoder.$BENCH_PLAYBACK_HOST_PIPELINE.1' + source 'host-copier.0.playback' + } + { + source '$BENCH_CAPTURE_DAI_COPIER' + sink 'phase_vocoder.$BENCH_CAPTURE_HOST_PIPELINE.2' + } + { + source 'phase_vocoder.$BENCH_CAPTURE_HOST_PIPELINE.2' + sink 'host-copier.0.capture' + } + ] diff --git a/tools/topology/topology2/include/bench/phase_vocoder_s16.conf b/tools/topology/topology2/include/bench/phase_vocoder_s16.conf new file mode 100644 index 000000000000..c5fdb6aeef5c --- /dev/null +++ b/tools/topology/topology2/include/bench/phase_vocoder_s16.conf @@ -0,0 +1,13 @@ + # Created with script "./bench_comp_generate.sh phase_vocoder" + Object.Widget.phase_vocoder.1 { + index $BENCH_PLAYBACK_HOST_PIPELINE + <include/bench/one_input_output_format_s16.conf> + <include/bench/phase_vocoder_controls_playback.conf> + } + Object.Widget.phase_vocoder.2 { + index $BENCH_CAPTURE_HOST_PIPELINE + <include/bench/one_input_output_format_s16.conf> + <include/bench/phase_vocoder_controls_capture.conf> + } + <include/bench/host_gateway_pipelines_s16.conf> + <include/bench/phase_vocoder_route.conf> diff --git a/tools/topology/topology2/include/bench/phase_vocoder_s24.conf b/tools/topology/topology2/include/bench/phase_vocoder_s24.conf new file mode 100644 index 000000000000..9865e4e8f8b7 --- /dev/null +++ b/tools/topology/topology2/include/bench/phase_vocoder_s24.conf @@ -0,0 +1,13 @@ + # Created with script "./bench_comp_generate.sh phase_vocoder" + Object.Widget.phase_vocoder.1 { + index $BENCH_PLAYBACK_HOST_PIPELINE + <include/bench/one_input_output_format_s24.conf> + <include/bench/phase_vocoder_controls_playback.conf> + } + Object.Widget.phase_vocoder.2 { + index $BENCH_CAPTURE_HOST_PIPELINE + <include/bench/one_input_output_format_s24.conf> + <include/bench/phase_vocoder_controls_capture.conf> + } + <include/bench/host_gateway_pipelines_s24.conf> + <include/bench/phase_vocoder_route.conf> diff --git a/tools/topology/topology2/include/bench/phase_vocoder_s32.conf b/tools/topology/topology2/include/bench/phase_vocoder_s32.conf new file mode 100644 index 000000000000..46f9cad71989 --- /dev/null +++ b/tools/topology/topology2/include/bench/phase_vocoder_s32.conf @@ -0,0 +1,21 @@ + # Created with script "./bench_comp_generate.sh phase_vocoder" + Object.Widget.phase_vocoder.1 { + index $BENCH_PLAYBACK_HOST_PIPELINE + scheduler_domain DP + domain_id 123 + stack_bytes_requirement 4096 + heap_bytes_requirement 49152 + <include/bench/one_input_output_format_s32_dp_5ms.conf> + <include/bench/phase_vocoder_controls_playback.conf> + } + Object.Widget.phase_vocoder.2 { + index $BENCH_CAPTURE_HOST_PIPELINE + scheduler_domain DP + domain_id 123 + stack_bytes_requirement 4096 + heap_bytes_requirement 49152 + <include/bench/one_input_output_format_s32_dp_5ms.conf> + <include/bench/phase_vocoder_controls_capture.conf> + } + <include/bench/host_gateway_pipelines_s32.conf> + <include/bench/phase_vocoder_route.conf> 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..06f0f425c5e2 100644 --- a/tools/topology/topology2/include/common/common_definitions.conf +++ b/tools/topology/topology2/include/common/common_definitions.conf @@ -67,4 +67,12 @@ 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_JACK_COMPR_AUDIO_FEATURE_CAPTURE false # No compress audio features capture for jack + SDW_DMIC_AUDIO_FEATURE_CAPTURE false # No audio features capture for microphone + SDW_DMIC_COMPR_AUDIO_FEATURE_CAPTURE false # No compress 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..d03092360591 100644 --- a/tools/topology/topology2/include/common/tokens.conf +++ b/tools/topology/topology2/include/common/tokens.conf @@ -84,6 +84,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/dai-copier.conf b/tools/topology/topology2/include/components/dai-copier.conf index e5508ada3df5..3b97ff0b4adf 100644 --- a/tools/topology/topology2/include/components/dai-copier.conf +++ b/tools/topology/topology2/include/components/dai-copier.conf @@ -44,6 +44,7 @@ Class.Widget."dai-copier" { "HDA" "SSP" "DMIC" + "UAOL" "SAI" ] } @@ -57,6 +58,7 @@ Class.Widget."dai-copier" { "SSP" "DMIC" "HDA" + "UAOL" "SAI" ] } @@ -103,6 +105,8 @@ Class.Widget."dai-copier" { $DMIC_LINK_INPUT_CLASS # DMIC link input (DSP <-) $I2S_LINK_OUTPUT_CLASS # I2S link output (DSP ->) $I2S_LINK_INPUT_CLASS # I2S link input (DSP <-) + $ALH_UAOL_STREAM_LINK_OUTPUT_CLASS # UAOL link output (DSP ->) + $ALH_UAOL_STREAM_LINK_INPUT_CLASS # UAOL link input (DSP <-) $IPC_OUTPUT_CLASS # IPC output (DSP ->) $IPC_INPUT_CLASS # IPC input (DSP <-) $I2S_MULTILINK_OUTPUT_CLASS # I2S Multi gtw output (DSP ->) diff --git a/tools/topology/topology2/include/components/dai.conf b/tools/topology/topology2/include/components/dai.conf index 4a34594b5eb0..41f884b4e9f9 100644 --- a/tools/topology/topology2/include/components/dai.conf +++ b/tools/topology/topology2/include/components/dai.conf @@ -28,6 +28,7 @@ Class.Widget."dai" { "DMIC" "HDA" "ALH" + "UAOL" "SAI" "ESAI" ] 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/host-copier.conf b/tools/topology/topology2/include/components/host-copier.conf index 73911ef6e128..896ab30bb1a2 100644 --- a/tools/topology/topology2/include/components/host-copier.conf +++ b/tools/topology/topology2/include/components/host-copier.conf @@ -83,6 +83,18 @@ Class.Widget."host-copier" { token_ref "copier.word" } + # + # D0I3 compatibility flags for streams. When set, the DSP can + # remain in D0i3 during S0ix while this stream is active. + # + DefineAttribute."playback_compatible_d0i3" { + token_ref "stream.bool" + } + + DefineAttribute."capture_compatible_d0i3" { + token_ref "stream.bool" + } + attributes { # # The host copier widget name would be constructed using the copier pcm_id and diff --git a/tools/topology/topology2/include/components/mfcc.conf b/tools/topology/topology2/include/components/mfcc.conf index 221df8f2d437..bf908e685048 100644 --- a/tools/topology/topology2/include/components/mfcc.conf +++ b/tools/topology/topology2/include/components/mfcc.conf @@ -13,6 +13,8 @@ # # Where M is pipeline ID and N is a unique integer in the parent object. +<include/controls/mixer.conf> + Class.Widget."mfcc" { # # Pipeline ID @@ -45,7 +47,6 @@ Class.Widget."mfcc" { !immutable [ "uuid" - "type" ] !deprecated [ "preload_count" @@ -53,6 +54,26 @@ Class.Widget."mfcc" { unique "instance" } + # + # MFCC Widget switch control to optionally notify VAD state changes + # + Object.Control { + mixer."1" { + Object.Base.channel.1 { + name "fc" + shift 0 + } + Object.Base.ops.1 { + name "ctl" + info "volsw" + #259 binds the mixer control to switch get/put handlers + get 259 + put 259 + } + max 1 + } + } + # # Default attributes for mfcc # diff --git a/tools/topology/topology2/include/components/mfcc/ceps13_compress_dtx.conf b/tools/topology/topology2/include/components/mfcc/ceps13_compress_dtx.conf new file mode 100644 index 000000000000..7056b9e7cb4b --- /dev/null +++ b/tools/topology/topology2/include/components/mfcc/ceps13_compress_dtx.conf @@ -0,0 +1,24 @@ +# Exported MFCC configuration 26-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, + 0x74,0x00,0x00,0x00,0x01,0xd0,0x01,0x03, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x74,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x14,0x00,0xf4,0x01, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x80,0x3e,0x00,0x00, + 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,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,0x00, + 0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x01, + 0x01,0x00,0x00,0x00,0x01,0x01,0x01,0x01, + 0x00,0x00,0x00,0x00" +} diff --git a/tools/topology/topology2/include/components/mfcc/default.conf b/tools/topology/topology2/include/components/mfcc/default.conf index 1f9141886de9..0ac19fa71d04 100644 --- a/tools/topology/topology2/include/components/mfcc/default.conf +++ b/tools/topology/topology2/include/components/mfcc/default.conf @@ -1,12 +1,12 @@ -# Exported MFCC configuration 24-Jul-2024 -# cd tools/tune/mfcc; octave setup_mfcc.m +# Exported MFCC configuration 26-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, + 0x74,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, + 0x74,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, @@ -14,9 +14,11 @@ 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, - 0x01,0x01,0x01,0x00,0x00,0x00,0x00,0x00" + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x01, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00" } 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..b18baadd459b --- /dev/null +++ b/tools/topology/topology2/include/components/mfcc/mel80.conf @@ -0,0 +1,24 @@ +# Exported MFCC configuration 26-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, + 0x74,0x00,0x00,0x00,0x01,0xd0,0x01,0x03, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x74,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,0x00,0x00,0x00,0x00,0x00,0x00,0x01, + 0x01,0x00,0x00,0x01,0x01,0x00,0x01,0x00, + 0x00,0x00,0x00,0x00" +} diff --git a/tools/topology/topology2/include/components/mfcc/mel80_compress.conf b/tools/topology/topology2/include/components/mfcc/mel80_compress.conf new file mode 100644 index 000000000000..f26f2af6980c --- /dev/null +++ b/tools/topology/topology2/include/components/mfcc/mel80_compress.conf @@ -0,0 +1,24 @@ +# Exported MFCC configuration 26-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, + 0x74,0x00,0x00,0x00,0x01,0xd0,0x01,0x03, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x74,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,0x00,0x00,0x00,0x00,0x00,0x00,0x01, + 0x01,0x00,0x00,0x01,0x01,0x00,0x01,0x01, + 0x00,0x00,0x00,0x00" +} diff --git a/tools/topology/topology2/include/components/mfcc/mel80_compress_dtx.conf b/tools/topology/topology2/include/components/mfcc/mel80_compress_dtx.conf new file mode 100644 index 000000000000..d225811ca4d1 --- /dev/null +++ b/tools/topology/topology2/include/components/mfcc/mel80_compress_dtx.conf @@ -0,0 +1,24 @@ +# Exported MFCC configuration 26-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, + 0x74,0x00,0x00,0x00,0x01,0xd0,0x01,0x03, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x74,0x00,0x00,0x00,0x00,0x02,0x00,0x04, + 0x00,0x00,0x00,0x00,0x14,0x00,0xf4,0x01, + 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,0x00,0x00,0x00,0x00,0x00,0x00,0x01, + 0x01,0x00,0x00,0x01,0x01,0x01,0x01,0x01, + 0x00,0x00,0x00,0x00" +} diff --git a/tools/topology/topology2/include/components/micsel/downmix_51_to_mono_with_lfe.conf b/tools/topology/topology2/include/components/micsel/downmix_51_to_mono_with_lfe.conf index 76aa09754ebe..ccfac873938d 100644 --- a/tools/topology/topology2/include/components/micsel/downmix_51_to_mono_with_lfe.conf +++ b/tools/topology/topology2/include/components/micsel/downmix_51_to_mono_with_lfe.conf @@ -1,5 +1,5 @@ -# Exported with script sof_selector_blobs.m 04-Jun-2025 -# cd tools/tune/selector; octave sof_selector_blobs.m +# Exported with script sof_selector_blobs.m 26-May-2026 +# cd src/audio/selector/tune; octave sof_selector_blobs.m Object.Base.data."selector_config" { bytes " 0x53,0x4f,0x46,0x34,0x00,0x00,0x00,0x00, diff --git a/tools/topology/topology2/include/components/micsel/downmix_51_to_stereo_with_lfe.conf b/tools/topology/topology2/include/components/micsel/downmix_51_to_stereo_with_lfe.conf index 0fb37e1d840b..b6e3ed571a74 100644 --- a/tools/topology/topology2/include/components/micsel/downmix_51_to_stereo_with_lfe.conf +++ b/tools/topology/topology2/include/components/micsel/downmix_51_to_stereo_with_lfe.conf @@ -1,5 +1,5 @@ -# Exported with script sof_selector_blobs.m 04-Jun-2025 -# cd tools/tune/selector; octave sof_selector_blobs.m +# Exported with script sof_selector_blobs.m 26-May-2026 +# cd src/audio/selector/tune; octave sof_selector_blobs.m Object.Base.data."selector_config" { bytes " 0x53,0x4f,0x46,0x34,0x00,0x00,0x00,0x00, diff --git a/tools/topology/topology2/include/components/micsel/downmix_71_to_51.conf b/tools/topology/topology2/include/components/micsel/downmix_71_to_51.conf index 93fe5fb1c3f9..75eab9b56b07 100644 --- a/tools/topology/topology2/include/components/micsel/downmix_71_to_51.conf +++ b/tools/topology/topology2/include/components/micsel/downmix_71_to_51.conf @@ -1,5 +1,5 @@ -# Exported with script sof_selector_blobs.m 04-Jun-2025 -# cd tools/tune/selector; octave sof_selector_blobs.m +# Exported with script sof_selector_blobs.m 26-May-2026 +# cd src/audio/selector/tune; octave sof_selector_blobs.m Object.Base.data."selector_config" { bytes " 0x53,0x4f,0x46,0x34,0x00,0x00,0x00,0x00, diff --git a/tools/topology/topology2/include/components/micsel/downmix_71_to_mono_with_lfe.conf b/tools/topology/topology2/include/components/micsel/downmix_71_to_mono_with_lfe.conf index 1e67d36bf839..ae3961a4af49 100644 --- a/tools/topology/topology2/include/components/micsel/downmix_71_to_mono_with_lfe.conf +++ b/tools/topology/topology2/include/components/micsel/downmix_71_to_mono_with_lfe.conf @@ -1,5 +1,5 @@ -# Exported with script sof_selector_blobs.m 04-Jun-2025 -# cd tools/tune/selector; octave sof_selector_blobs.m +# Exported with script sof_selector_blobs.m 26-May-2026 +# cd src/audio/selector/tune; octave sof_selector_blobs.m Object.Base.data."selector_config" { bytes " 0x53,0x4f,0x46,0x34,0x00,0x00,0x00,0x00, diff --git a/tools/topology/topology2/include/components/micsel/downmix_71_to_stereo_with_lfe.conf b/tools/topology/topology2/include/components/micsel/downmix_71_to_stereo_with_lfe.conf index b94ea59c1a9e..aab7d42c792b 100644 --- a/tools/topology/topology2/include/components/micsel/downmix_71_to_stereo_with_lfe.conf +++ b/tools/topology/topology2/include/components/micsel/downmix_71_to_stereo_with_lfe.conf @@ -1,5 +1,5 @@ -# Exported with script sof_selector_blobs.m 04-Jun-2025 -# cd tools/tune/selector; octave sof_selector_blobs.m +# Exported with script sof_selector_blobs.m 26-May-2026 +# cd src/audio/selector/tune; octave sof_selector_blobs.m Object.Base.data."selector_config" { bytes " 0x53,0x4f,0x46,0x34,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/downmix_stereo_to_doublemono.conf similarity index 80% rename from tools/topology/topology2/include/components/micsel/downmix_51_to_mono.conf rename to tools/topology/topology2/include/components/micsel/downmix_stereo_to_doublemono.conf index cd1f65b86454..e9e2d1e78608 100644 --- a/tools/topology/topology2/include/components/micsel/downmix_51_to_mono.conf +++ b/tools/topology/topology2/include/components/micsel/downmix_stereo_to_doublemono.conf @@ -1,14 +1,14 @@ -# Exported with script sof_selector_blobs.m 04-Jun-2025 -# cd tools/tune/selector; octave sof_selector_blobs.m +# Exported with script sof_selector_blobs.m 26-May-2026 +# cd src/audio/selector/tune; 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,0xd4,0x00,0xd4,0x00, - 0x2c,0x01,0x00,0x00,0x96,0x00,0x96,0x00, + 0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x02, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x02,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, diff --git a/tools/topology/topology2/include/components/micsel/downmix_stereo_to_mono.conf b/tools/topology/topology2/include/components/micsel/downmix_stereo_to_mono.conf index 510d303eb2d9..8eeb50cc9a96 100644 --- a/tools/topology/topology2/include/components/micsel/downmix_stereo_to_mono.conf +++ b/tools/topology/topology2/include/components/micsel/downmix_stereo_to_mono.conf @@ -1,5 +1,5 @@ -# Exported with script sof_selector_blobs.m 04-Jun-2025 -# cd tools/tune/selector; octave sof_selector_blobs.m +# Exported with script sof_selector_blobs.m 26-May-2026 +# cd src/audio/selector/tune; octave sof_selector_blobs.m Object.Base.data."selector_config" { bytes " 0x53,0x4f,0x46,0x34,0x00,0x00,0x00,0x00, diff --git a/tools/topology/topology2/include/components/micsel/passthrough.conf b/tools/topology/topology2/include/components/micsel/passthrough.conf index 0b795084ba29..23e1c7335bc0 100644 --- a/tools/topology/topology2/include/components/micsel/passthrough.conf +++ b/tools/topology/topology2/include/components/micsel/passthrough.conf @@ -1,5 +1,5 @@ -# Exported with script sof_selector_blobs.m 04-Jun-2025 -# cd tools/tune/selector; octave sof_selector_blobs.m +# Exported with script sof_selector_blobs.m 26-May-2026 +# cd src/audio/selector/tune; octave sof_selector_blobs.m Object.Base.data."selector_config" { bytes " 0x53,0x4f,0x46,0x34,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..1acc3f93eed6 --- /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 26-May-2026 +# cd src/audio/selector/tune; 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/upmix_mono_to_51.conf b/tools/topology/topology2/include/components/micsel/upmix_mono_to_51.conf index c19828913473..ee8a11355c89 100644 --- a/tools/topology/topology2/include/components/micsel/upmix_mono_to_51.conf +++ b/tools/topology/topology2/include/components/micsel/upmix_mono_to_51.conf @@ -1,5 +1,5 @@ -# Exported with script sof_selector_blobs.m 04-Jun-2025 -# cd tools/tune/selector; octave sof_selector_blobs.m +# Exported with script sof_selector_blobs.m 26-May-2026 +# cd src/audio/selector/tune; octave sof_selector_blobs.m Object.Base.data."selector_config" { bytes " 0x53,0x4f,0x46,0x34,0x00,0x00,0x00,0x00, diff --git a/tools/topology/topology2/include/components/micsel/upmix_mono_to_71.conf b/tools/topology/topology2/include/components/micsel/upmix_mono_to_71.conf index c19828913473..ee8a11355c89 100644 --- a/tools/topology/topology2/include/components/micsel/upmix_mono_to_71.conf +++ b/tools/topology/topology2/include/components/micsel/upmix_mono_to_71.conf @@ -1,5 +1,5 @@ -# Exported with script sof_selector_blobs.m 04-Jun-2025 -# cd tools/tune/selector; octave sof_selector_blobs.m +# Exported with script sof_selector_blobs.m 26-May-2026 +# cd src/audio/selector/tune; octave sof_selector_blobs.m Object.Base.data."selector_config" { bytes " 0x53,0x4f,0x46,0x34,0x00,0x00,0x00,0x00, diff --git a/tools/topology/topology2/include/components/micsel/upmix_mono_to_stereo.conf b/tools/topology/topology2/include/components/micsel/upmix_mono_to_stereo.conf index 33ef4a7672f2..12a956940eaf 100644 --- a/tools/topology/topology2/include/components/micsel/upmix_mono_to_stereo.conf +++ b/tools/topology/topology2/include/components/micsel/upmix_mono_to_stereo.conf @@ -1,5 +1,5 @@ -# Exported with script sof_selector_blobs.m 04-Jun-2025 -# cd tools/tune/selector; octave sof_selector_blobs.m +# Exported with script sof_selector_blobs.m 26-May-2026 +# cd src/audio/selector/tune; octave sof_selector_blobs.m Object.Base.data."selector_config" { bytes " 0x53,0x4f,0x46,0x34,0x00,0x00,0x00,0x00, diff --git a/tools/topology/topology2/include/components/micsel/upmix_stereo_to_51.conf b/tools/topology/topology2/include/components/micsel/upmix_stereo_to_51.conf index b411410cab70..4cfb29e5af54 100644 --- a/tools/topology/topology2/include/components/micsel/upmix_stereo_to_51.conf +++ b/tools/topology/topology2/include/components/micsel/upmix_stereo_to_51.conf @@ -1,5 +1,5 @@ -# Exported with script sof_selector_blobs.m 04-Jun-2025 -# cd tools/tune/selector; octave sof_selector_blobs.m +# Exported with script sof_selector_blobs.m 26-May-2026 +# cd src/audio/selector/tune; octave sof_selector_blobs.m Object.Base.data."selector_config" { bytes " 0x53,0x4f,0x46,0x34,0x00,0x00,0x00,0x00, diff --git a/tools/topology/topology2/include/components/micsel/upmix_stereo_to_71.conf b/tools/topology/topology2/include/components/micsel/upmix_stereo_to_71.conf index b411410cab70..4cfb29e5af54 100644 --- a/tools/topology/topology2/include/components/micsel/upmix_stereo_to_71.conf +++ b/tools/topology/topology2/include/components/micsel/upmix_stereo_to_71.conf @@ -1,5 +1,5 @@ -# Exported with script sof_selector_blobs.m 04-Jun-2025 -# cd tools/tune/selector; octave sof_selector_blobs.m +# Exported with script sof_selector_blobs.m 26-May-2026 +# cd src/audio/selector/tune; octave sof_selector_blobs.m Object.Base.data."selector_config" { bytes " 0x53,0x4f,0x46,0x34,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_llrr.conf similarity index 72% 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_llrr.conf index 3f7b32a251d8..110c98de9eb6 100644 --- a/tools/topology/topology2/include/components/micsel/downmix_51_to_stereo.conf +++ b/tools/topology/topology2/include/components/micsel/xover_selector_lr_to_llrr.conf @@ -1,18 +1,18 @@ -# Exported with script sof_selector_blobs.m 04-Jun-2025 -# cd tools/tune/selector; octave sof_selector_blobs.m +# Exported with script sof_selector_blobs.m 26-May-2026 +# cd src/audio/selector/tune; 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,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,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_71_to_mono.conf b/tools/topology/topology2/include/components/micsel/xover_selector_lr_to_lrlr.conf similarity index 72% 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_lrlr.conf index 1e67d36bf839..967e7327132b 100644 --- a/tools/topology/topology2/include/components/micsel/downmix_71_to_mono.conf +++ b/tools/topology/topology2/include/components/micsel/xover_selector_lr_to_lrlr.conf @@ -1,18 +1,18 @@ -# Exported with script sof_selector_blobs.m 04-Jun-2025 -# cd tools/tune/selector; octave sof_selector_blobs.m +# Exported with script sof_selector_blobs.m 26-May-2026 +# cd src/audio/selector/tune; 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,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,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_stereo.conf b/tools/topology/topology2/include/components/micsel/xover_selector_lr_to_rrll.conf similarity index 68% rename from tools/topology/topology2/include/components/micsel/downmix_71_to_stereo.conf rename to tools/topology/topology2/include/components/micsel/xover_selector_lr_to_rrll.conf index df6af38dde34..cf277480ecf7 100644 --- a/tools/topology/topology2/include/components/micsel/downmix_71_to_stereo.conf +++ b/tools/topology/topology2/include/components/micsel/xover_selector_lr_to_rrll.conf @@ -1,18 +1,18 @@ -# Exported with script sof_selector_blobs.m 04-Jun-2025 -# cd tools/tune/selector; octave sof_selector_blobs.m +# Exported with script sof_selector_blobs.m 26-May-2026 +# cd src/audio/selector/tune; 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,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/phase_vocoder.conf b/tools/topology/topology2/include/components/phase_vocoder.conf new file mode 100644 index 000000000000..c0541c2ce69d --- /dev/null +++ b/tools/topology/topology2/include/components/phase_vocoder.conf @@ -0,0 +1,121 @@ +# +# +# A PHASE_VOCODER component for SOF. All attributes defined herein are namespaced +# by alsatplg to "Object.Widget.phase_vocoder.attribute_name" +# +# Usage: this component can be used by declaring in the parent object. e.g. +# +# Object.Widget.phase_vocoder."N" { +# index 1 +# } + +# +# Where N is a unique integer in the parent object. + +Class.Widget."phase_vocoder" { + # + # Pipeline ID + # + DefineAttribute."index" { + type "integer" + } + + # + # Unique instance for PHASE_VOCODER 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" + } + + Object.Control { + # Switch controls + mixer."1" { + Object.Base.channel.1 { + name "fc" + shift 0 + } + Object.Base.ops.1 { + name "ctl" + info "volsw" + #259 binds the mixer control to switch get/put handlers + get 259 + put 259 + } + max 1 + } + + # Enum controls + enum."1" { + Object.Base { + channel.1 { + name "fc" + reg 3 + shift 0 + } + text.0 { + name "phase_vocoder_speed_enum" + !values [ + "0.5" + "0.6" + "0.7" + "0.8" + "0.9" + "1.0" + "1.1" + "1.2" + "1.3" + "1.4" + "1.5" + "1.6" + "1.7" + "1.8" + "1.9" + "2.0" + ] + } + ops.1 { + name "ctl" + info "enum" + #257 binds the mixer control to enum get/put handlers + get 257 + put 257 + } + } + } + } + + # + # Default attributes for phase_vocoder + # + + uuid "7a:cb:fb:09:c5:a9:57:4a:84:34:44:40:e5:98:ab:24" + type "effect" + no_pm "true" + num_input_pins 1 + num_output_pins 1 +} diff --git a/tools/topology/topology2/include/components/phase_vocoder/hann_1024_256.conf b/tools/topology/topology2/include/components/phase_vocoder/hann_1024_256.conf new file mode 100644 index 000000000000..fbce252b333a --- /dev/null +++ b/tools/topology/topology2/include/components/phase_vocoder/hann_1024_256.conf @@ -0,0 +1,17 @@ +# Exported PHASE_VOCODER configuration 17-Jun-2026 +# cd src/audio/phase_vocoder/tune; octave setup_phase_vocoder.m +Object.Base.data."phase_vocoder_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/phase_vocoder/hann_1024_256_mono.conf b/tools/topology/topology2/include/components/phase_vocoder/hann_1024_256_mono.conf new file mode 100644 index 000000000000..9aa3a2bdc326 --- /dev/null +++ b/tools/topology/topology2/include/components/phase_vocoder/hann_1024_256_mono.conf @@ -0,0 +1,17 @@ +# Exported PHASE_VOCODER configuration 17-Jun-2026 +# cd src/audio/phase_vocoder/tune; octave setup_phase_vocoder.m +Object.Base.data."phase_vocoder_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, + 0x01,0x00,0x00,0x04,0x00,0x01,0x00,0x00, + 0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00" +} diff --git a/tools/topology/topology2/include/components/phase_vocoder/hann_256_128.conf b/tools/topology/topology2/include/components/phase_vocoder/hann_256_128.conf new file mode 100644 index 000000000000..839b9e7e54ed --- /dev/null +++ b/tools/topology/topology2/include/components/phase_vocoder/hann_256_128.conf @@ -0,0 +1,17 @@ +# Exported PHASE_VOCODER configuration 17-Jun-2026 +# cd src/audio/phase_vocoder/tune; octave setup_phase_vocoder.m +Object.Base.data."phase_vocoder_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, + 0xab,0x00,0x56,0xab,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x01,0x80,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00" +} diff --git a/tools/topology/topology2/include/components/phase_vocoder/hann_512_128.conf b/tools/topology/topology2/include/components/phase_vocoder/hann_512_128.conf new file mode 100644 index 000000000000..0cf41f15424d --- /dev/null +++ b/tools/topology/topology2/include/components/phase_vocoder/hann_512_128.conf @@ -0,0 +1,17 @@ +# Exported PHASE_VOCODER configuration 17-Jun-2026 +# cd src/audio/phase_vocoder/tune; octave setup_phase_vocoder.m +Object.Base.data."phase_vocoder_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/phase_vocoder/hann_512_256.conf b/tools/topology/topology2/include/components/phase_vocoder/hann_512_256.conf new file mode 100644 index 000000000000..1197148a532e --- /dev/null +++ b/tools/topology/topology2/include/components/phase_vocoder/hann_512_256.conf @@ -0,0 +1,17 @@ +# Exported PHASE_VOCODER configuration 17-Jun-2026 +# cd src/audio/phase_vocoder/tune; octave setup_phase_vocoder.m +Object.Base.data."phase_vocoder_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, + 0xc0,0x2a,0x00,0xab,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x02,0x00,0x01,0x00,0x00, + 0x00,0x00,0x00,0x00,0x03,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..14ccd8fcf86a 100644 --- a/tools/topology/topology2/include/components/widget-common.conf +++ b/tools/topology/topology2/include/components/widget-common.conf @@ -149,9 +149,12 @@ DefineAttribute."stack_bytes_requirement" { token_ref "comp.word" } -## Heap size requirement in bytes for this component. Zero indicates default heap size. +## Heap size requirement in bytes for this component. DefineAttribute."heap_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 +heap_bytes_requirement 24576 diff --git a/tools/topology/topology2/include/dais/uaol.conf b/tools/topology/topology2/include/dais/uaol.conf new file mode 100644 index 000000000000..56d8dd726481 --- /dev/null +++ b/tools/topology/topology2/include/dais/uaol.conf @@ -0,0 +1,95 @@ +# +# UAOL DAI +# +# All attributes defined herein are namespaced by alsatplg to +# "Object.Dai.UAOL.attribute_name" +# +# UAOL (USB Audio Offload Link) is an ACE2+ alt link. It belongs to the ALH +# class family in the firmware gateway enumeration, but it is described by its +# own DAI class because the ALH class carries SoundWire specific attributes. +# +# Usage: this component can be used by declaring as follows: +# +# For Playback +#Object.Dai.UAOL."N" { +# direction "playback" +# name "UAOL0" +# id 0 +# Object.Base.hw_config."0" {} +# } +# For Capture +#Object.Dai.UAOL."N" { +# direction "capture" +# name "UAOL0" +# id 0 +# Object.Base.hw_config."1" {} +# } +# +# Where N is the dai index of the UAOL DAI in the firmware + +Class.Dai."UAOL" { + # + # instance ID of UAOL DAI object + # + DefineAttribute."instance" {} + + # + # UAOL link index in the firmware + # + DefineAttribute."dai_index" { + token_ref "dai.word" + } + + DefineAttribute."direction" { + type "string" + constraints { + !valid_values [ + "playback" + "capture" + "duplex" + ] + } + } + + # type of DAI + DefineAttribute.dai_type { + type "string" + token_ref "dai.string" + } + + # Backend DAI Link ID matching with the machine driver + DefineAttribute.id {} + + DefineAttribute."default_hw_config_id" {} + + DefineAttribute.name { + type "string" + } + + DefineAttribute.rate {} + + DefineAttribute.ch {} + + attributes { + !constructor [ + "name" + ] + !mandatory [ + "id" + "dai_index" + ] + !immutable [ + "type" + ] + # + # UAOL DAI objects instantiated within the same alsaconf node must + # have unique instance attribute + # + unique "instance" + } + + dai_type "UAOL" + default_hw_config_id 0 + rate 48000 + ch 2 +} 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/formats/uaol_input_audio_formats.conf b/tools/topology/topology2/include/formats/uaol_input_audio_formats.conf new file mode 100644 index 000000000000..4a8ca0c0c9f0 --- /dev/null +++ b/tools/topology/topology2/include/formats/uaol_input_audio_formats.conf @@ -0,0 +1,344 @@ + # Every format the offloaded USB endpoint can carry: S16_LE, S24_LE and + # S32_LE, 1 to 8 channels and every standard rate from 8 to 192 kHz. + # + # 7 channels is left out, it has no channel config and no channel map in + # common_definitions.conf. + num_input_audio_formats 315 + CombineArrays.Object.Base.input_audio_format [ + { + in_rate [ + 8000 + 11025 + 12000 + 16000 + 22050 + 24000 + 32000 + 44100 + 48000 + 64000 + 88200 + 96000 + 128000 + 176400 + 192000 + ] + in_bit_depth [ 16 ] + in_valid_bit_depth [ 16 ] + in_channels [ 1 ] + in_ch_cfg [ $CHANNEL_CONFIG_MONO ] + in_ch_map [ $CHANNEL_MAP_MONO ] + } + { + in_rate [ + 8000 + 11025 + 12000 + 16000 + 22050 + 24000 + 32000 + 44100 + 48000 + 64000 + 88200 + 96000 + 128000 + 176400 + 192000 + ] + in_bit_depth [ 32 ] + in_valid_bit_depth [ 24 32 ] + in_channels [ 1 ] + in_ch_cfg [ $CHANNEL_CONFIG_MONO ] + in_ch_map [ $CHANNEL_MAP_MONO ] + } + { + in_rate [ + 8000 + 11025 + 12000 + 16000 + 22050 + 24000 + 32000 + 44100 + 48000 + 64000 + 88200 + 96000 + 128000 + 176400 + 192000 + ] + in_bit_depth [ 16 ] + in_valid_bit_depth [ 16 ] + in_channels [ 2 ] + in_ch_cfg [ $CHANNEL_CONFIG_STEREO ] + in_ch_map [ $CHANNEL_MAP_STEREO ] + } + { + in_rate [ + 8000 + 11025 + 12000 + 16000 + 22050 + 24000 + 32000 + 44100 + 48000 + 64000 + 88200 + 96000 + 128000 + 176400 + 192000 + ] + in_bit_depth [ 32 ] + in_valid_bit_depth [ 24 32 ] + in_channels [ 2 ] + in_ch_cfg [ $CHANNEL_CONFIG_STEREO ] + in_ch_map [ $CHANNEL_MAP_STEREO ] + } + { + in_rate [ + 8000 + 11025 + 12000 + 16000 + 22050 + 24000 + 32000 + 44100 + 48000 + 64000 + 88200 + 96000 + 128000 + 176400 + 192000 + ] + in_bit_depth [ 16 ] + in_valid_bit_depth [ 16 ] + in_channels [ 3 ] + in_ch_cfg [ $CHANNEL_CONFIG_2_POINT_1 ] + in_ch_map [ $CHANNEL_MAP_2_POINT_1 ] + } + { + in_rate [ + 8000 + 11025 + 12000 + 16000 + 22050 + 24000 + 32000 + 44100 + 48000 + 64000 + 88200 + 96000 + 128000 + 176400 + 192000 + ] + in_bit_depth [ 32 ] + in_valid_bit_depth [ 24 32 ] + in_channels [ 3 ] + in_ch_cfg [ $CHANNEL_CONFIG_2_POINT_1 ] + in_ch_map [ $CHANNEL_MAP_2_POINT_1 ] + } + { + in_rate [ + 8000 + 11025 + 12000 + 16000 + 22050 + 24000 + 32000 + 44100 + 48000 + 64000 + 88200 + 96000 + 128000 + 176400 + 192000 + ] + in_bit_depth [ 16 ] + in_valid_bit_depth [ 16 ] + in_channels [ 4 ] + in_ch_cfg [ $CHANNEL_CONFIG_3_POINT_1 ] + in_ch_map [ $CHANNEL_MAP_3_POINT_1 ] + } + { + in_rate [ + 8000 + 11025 + 12000 + 16000 + 22050 + 24000 + 32000 + 44100 + 48000 + 64000 + 88200 + 96000 + 128000 + 176400 + 192000 + ] + in_bit_depth [ 32 ] + in_valid_bit_depth [ 24 32 ] + in_channels [ 4 ] + in_ch_cfg [ $CHANNEL_CONFIG_3_POINT_1 ] + in_ch_map [ $CHANNEL_MAP_3_POINT_1 ] + } + { + in_rate [ + 8000 + 11025 + 12000 + 16000 + 22050 + 24000 + 32000 + 44100 + 48000 + 64000 + 88200 + 96000 + 128000 + 176400 + 192000 + ] + in_bit_depth [ 16 ] + in_valid_bit_depth [ 16 ] + in_channels [ 5 ] + in_ch_cfg [ $CHANNEL_CONFIG_5_POINT_0 ] + in_ch_map [ $CHANNEL_MAP_5_POINT_0 ] + } + { + in_rate [ + 8000 + 11025 + 12000 + 16000 + 22050 + 24000 + 32000 + 44100 + 48000 + 64000 + 88200 + 96000 + 128000 + 176400 + 192000 + ] + in_bit_depth [ 32 ] + in_valid_bit_depth [ 24 32 ] + in_channels [ 5 ] + in_ch_cfg [ $CHANNEL_CONFIG_5_POINT_0 ] + in_ch_map [ $CHANNEL_MAP_5_POINT_0 ] + } + { + in_rate [ + 8000 + 11025 + 12000 + 16000 + 22050 + 24000 + 32000 + 44100 + 48000 + 64000 + 88200 + 96000 + 128000 + 176400 + 192000 + ] + in_bit_depth [ 16 ] + in_valid_bit_depth [ 16 ] + in_channels [ 6 ] + in_ch_cfg [ $CHANNEL_CONFIG_5_POINT_1 ] + in_ch_map [ $CHANNEL_MAP_5_POINT_1 ] + } + { + in_rate [ + 8000 + 11025 + 12000 + 16000 + 22050 + 24000 + 32000 + 44100 + 48000 + 64000 + 88200 + 96000 + 128000 + 176400 + 192000 + ] + in_bit_depth [ 32 ] + in_valid_bit_depth [ 24 32 ] + in_channels [ 6 ] + in_ch_cfg [ $CHANNEL_CONFIG_5_POINT_1 ] + in_ch_map [ $CHANNEL_MAP_5_POINT_1 ] + } + { + in_rate [ + 8000 + 11025 + 12000 + 16000 + 22050 + 24000 + 32000 + 44100 + 48000 + 64000 + 88200 + 96000 + 128000 + 176400 + 192000 + ] + in_bit_depth [ 16 ] + in_valid_bit_depth [ 16 ] + in_channels [ 8 ] + in_ch_cfg [ $CHANNEL_CONFIG_7_POINT_1 ] + in_ch_map [ $CHANNEL_MAP_7_POINT_1 ] + } + { + in_rate [ + 8000 + 11025 + 12000 + 16000 + 22050 + 24000 + 32000 + 44100 + 48000 + 64000 + 88200 + 96000 + 128000 + 176400 + 192000 + ] + in_bit_depth [ 32 ] + in_valid_bit_depth [ 24 32 ] + in_channels [ 8 ] + in_ch_cfg [ $CHANNEL_CONFIG_7_POINT_1 ] + in_ch_map [ $CHANNEL_MAP_7_POINT_1 ] + } + ] diff --git a/tools/topology/topology2/include/formats/uaol_input_audio_formats_s32.conf b/tools/topology/topology2/include/formats/uaol_input_audio_formats_s32.conf new file mode 100644 index 000000000000..a98428ba1533 --- /dev/null +++ b/tools/topology/topology2/include/formats/uaol_input_audio_formats_s32.conf @@ -0,0 +1,176 @@ + # The formats of a module which only processes 32 bit samples, for every + # channel count and rate the offloaded USB endpoint can carry. + # + # 7 channels is left out, it has no channel config and no channel map in + # common_definitions.conf. + num_input_audio_formats 105 + CombineArrays.Object.Base.input_audio_format [ + { + in_rate [ + 8000 + 11025 + 12000 + 16000 + 22050 + 24000 + 32000 + 44100 + 48000 + 64000 + 88200 + 96000 + 128000 + 176400 + 192000 + ] + in_bit_depth [ 32 ] + in_valid_bit_depth [ 32 ] + in_channels [ 1 ] + in_ch_cfg [ $CHANNEL_CONFIG_MONO ] + in_ch_map [ $CHANNEL_MAP_MONO ] + } + { + in_rate [ + 8000 + 11025 + 12000 + 16000 + 22050 + 24000 + 32000 + 44100 + 48000 + 64000 + 88200 + 96000 + 128000 + 176400 + 192000 + ] + in_bit_depth [ 32 ] + in_valid_bit_depth [ 32 ] + in_channels [ 2 ] + in_ch_cfg [ $CHANNEL_CONFIG_STEREO ] + in_ch_map [ $CHANNEL_MAP_STEREO ] + } + { + in_rate [ + 8000 + 11025 + 12000 + 16000 + 22050 + 24000 + 32000 + 44100 + 48000 + 64000 + 88200 + 96000 + 128000 + 176400 + 192000 + ] + in_bit_depth [ 32 ] + in_valid_bit_depth [ 32 ] + in_channels [ 3 ] + in_ch_cfg [ $CHANNEL_CONFIG_2_POINT_1 ] + in_ch_map [ $CHANNEL_MAP_2_POINT_1 ] + } + { + in_rate [ + 8000 + 11025 + 12000 + 16000 + 22050 + 24000 + 32000 + 44100 + 48000 + 64000 + 88200 + 96000 + 128000 + 176400 + 192000 + ] + 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 ] + } + { + in_rate [ + 8000 + 11025 + 12000 + 16000 + 22050 + 24000 + 32000 + 44100 + 48000 + 64000 + 88200 + 96000 + 128000 + 176400 + 192000 + ] + in_bit_depth [ 32 ] + in_valid_bit_depth [ 32 ] + in_channels [ 5 ] + in_ch_cfg [ $CHANNEL_CONFIG_5_POINT_0 ] + in_ch_map [ $CHANNEL_MAP_5_POINT_0 ] + } + { + in_rate [ + 8000 + 11025 + 12000 + 16000 + 22050 + 24000 + 32000 + 44100 + 48000 + 64000 + 88200 + 96000 + 128000 + 176400 + 192000 + ] + in_bit_depth [ 32 ] + in_valid_bit_depth [ 32 ] + in_channels [ 6 ] + in_ch_cfg [ $CHANNEL_CONFIG_5_POINT_1 ] + in_ch_map [ $CHANNEL_MAP_5_POINT_1 ] + } + { + in_rate [ + 8000 + 11025 + 12000 + 16000 + 22050 + 24000 + 32000 + 44100 + 48000 + 64000 + 88200 + 96000 + 128000 + 176400 + 192000 + ] + in_bit_depth [ 32 ] + in_valid_bit_depth [ 32 ] + in_channels [ 8 ] + in_ch_cfg [ $CHANNEL_CONFIG_7_POINT_1 ] + in_ch_map [ $CHANNEL_MAP_7_POINT_1 ] + } + ] diff --git a/tools/topology/topology2/include/formats/uaol_output_audio_formats.conf b/tools/topology/topology2/include/formats/uaol_output_audio_formats.conf new file mode 100644 index 000000000000..0ff39effcd62 --- /dev/null +++ b/tools/topology/topology2/include/formats/uaol_output_audio_formats.conf @@ -0,0 +1,344 @@ + # Every format the offloaded USB endpoint can carry: S16_LE, S24_LE and + # S32_LE, 1 to 8 channels and every standard rate from 8 to 192 kHz. + # + # 7 channels is left out, it has no channel config and no channel map in + # common_definitions.conf. + num_output_audio_formats 315 + CombineArrays.Object.Base.output_audio_format [ + { + out_rate [ + 8000 + 11025 + 12000 + 16000 + 22050 + 24000 + 32000 + 44100 + 48000 + 64000 + 88200 + 96000 + 128000 + 176400 + 192000 + ] + out_bit_depth [ 16 ] + out_valid_bit_depth [ 16 ] + out_channels [ 1 ] + out_ch_cfg [ $CHANNEL_CONFIG_MONO ] + out_ch_map [ $CHANNEL_MAP_MONO ] + } + { + out_rate [ + 8000 + 11025 + 12000 + 16000 + 22050 + 24000 + 32000 + 44100 + 48000 + 64000 + 88200 + 96000 + 128000 + 176400 + 192000 + ] + out_bit_depth [ 32 ] + out_valid_bit_depth [ 24 32 ] + out_channels [ 1 ] + out_ch_cfg [ $CHANNEL_CONFIG_MONO ] + out_ch_map [ $CHANNEL_MAP_MONO ] + } + { + out_rate [ + 8000 + 11025 + 12000 + 16000 + 22050 + 24000 + 32000 + 44100 + 48000 + 64000 + 88200 + 96000 + 128000 + 176400 + 192000 + ] + out_bit_depth [ 16 ] + out_valid_bit_depth [ 16 ] + out_channels [ 2 ] + out_ch_cfg [ $CHANNEL_CONFIG_STEREO ] + out_ch_map [ $CHANNEL_MAP_STEREO ] + } + { + out_rate [ + 8000 + 11025 + 12000 + 16000 + 22050 + 24000 + 32000 + 44100 + 48000 + 64000 + 88200 + 96000 + 128000 + 176400 + 192000 + ] + out_bit_depth [ 32 ] + out_valid_bit_depth [ 24 32 ] + out_channels [ 2 ] + out_ch_cfg [ $CHANNEL_CONFIG_STEREO ] + out_ch_map [ $CHANNEL_MAP_STEREO ] + } + { + out_rate [ + 8000 + 11025 + 12000 + 16000 + 22050 + 24000 + 32000 + 44100 + 48000 + 64000 + 88200 + 96000 + 128000 + 176400 + 192000 + ] + out_bit_depth [ 16 ] + out_valid_bit_depth [ 16 ] + out_channels [ 3 ] + out_ch_cfg [ $CHANNEL_CONFIG_2_POINT_1 ] + out_ch_map [ $CHANNEL_MAP_2_POINT_1 ] + } + { + out_rate [ + 8000 + 11025 + 12000 + 16000 + 22050 + 24000 + 32000 + 44100 + 48000 + 64000 + 88200 + 96000 + 128000 + 176400 + 192000 + ] + out_bit_depth [ 32 ] + out_valid_bit_depth [ 24 32 ] + out_channels [ 3 ] + out_ch_cfg [ $CHANNEL_CONFIG_2_POINT_1 ] + out_ch_map [ $CHANNEL_MAP_2_POINT_1 ] + } + { + out_rate [ + 8000 + 11025 + 12000 + 16000 + 22050 + 24000 + 32000 + 44100 + 48000 + 64000 + 88200 + 96000 + 128000 + 176400 + 192000 + ] + 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 ] + } + { + out_rate [ + 8000 + 11025 + 12000 + 16000 + 22050 + 24000 + 32000 + 44100 + 48000 + 64000 + 88200 + 96000 + 128000 + 176400 + 192000 + ] + out_bit_depth [ 32 ] + out_valid_bit_depth [ 24 32 ] + out_channels [ 4 ] + out_ch_cfg [ $CHANNEL_CONFIG_3_POINT_1 ] + out_ch_map [ $CHANNEL_MAP_3_POINT_1 ] + } + { + out_rate [ + 8000 + 11025 + 12000 + 16000 + 22050 + 24000 + 32000 + 44100 + 48000 + 64000 + 88200 + 96000 + 128000 + 176400 + 192000 + ] + out_bit_depth [ 16 ] + out_valid_bit_depth [ 16 ] + out_channels [ 5 ] + out_ch_cfg [ $CHANNEL_CONFIG_5_POINT_0 ] + out_ch_map [ $CHANNEL_MAP_5_POINT_0 ] + } + { + out_rate [ + 8000 + 11025 + 12000 + 16000 + 22050 + 24000 + 32000 + 44100 + 48000 + 64000 + 88200 + 96000 + 128000 + 176400 + 192000 + ] + out_bit_depth [ 32 ] + out_valid_bit_depth [ 24 32 ] + out_channels [ 5 ] + out_ch_cfg [ $CHANNEL_CONFIG_5_POINT_0 ] + out_ch_map [ $CHANNEL_MAP_5_POINT_0 ] + } + { + out_rate [ + 8000 + 11025 + 12000 + 16000 + 22050 + 24000 + 32000 + 44100 + 48000 + 64000 + 88200 + 96000 + 128000 + 176400 + 192000 + ] + out_bit_depth [ 16 ] + out_valid_bit_depth [ 16 ] + out_channels [ 6 ] + out_ch_cfg [ $CHANNEL_CONFIG_5_POINT_1 ] + out_ch_map [ $CHANNEL_MAP_5_POINT_1 ] + } + { + out_rate [ + 8000 + 11025 + 12000 + 16000 + 22050 + 24000 + 32000 + 44100 + 48000 + 64000 + 88200 + 96000 + 128000 + 176400 + 192000 + ] + out_bit_depth [ 32 ] + out_valid_bit_depth [ 24 32 ] + out_channels [ 6 ] + out_ch_cfg [ $CHANNEL_CONFIG_5_POINT_1 ] + out_ch_map [ $CHANNEL_MAP_5_POINT_1 ] + } + { + out_rate [ + 8000 + 11025 + 12000 + 16000 + 22050 + 24000 + 32000 + 44100 + 48000 + 64000 + 88200 + 96000 + 128000 + 176400 + 192000 + ] + out_bit_depth [ 16 ] + out_valid_bit_depth [ 16 ] + out_channels [ 8 ] + out_ch_cfg [ $CHANNEL_CONFIG_7_POINT_1 ] + out_ch_map [ $CHANNEL_MAP_7_POINT_1 ] + } + { + out_rate [ + 8000 + 11025 + 12000 + 16000 + 22050 + 24000 + 32000 + 44100 + 48000 + 64000 + 88200 + 96000 + 128000 + 176400 + 192000 + ] + out_bit_depth [ 32 ] + out_valid_bit_depth [ 24 32 ] + out_channels [ 8 ] + out_ch_cfg [ $CHANNEL_CONFIG_7_POINT_1 ] + out_ch_map [ $CHANNEL_MAP_7_POINT_1 ] + } + ] diff --git a/tools/topology/topology2/include/formats/uaol_output_audio_formats_s32.conf b/tools/topology/topology2/include/formats/uaol_output_audio_formats_s32.conf new file mode 100644 index 000000000000..8dea01de8020 --- /dev/null +++ b/tools/topology/topology2/include/formats/uaol_output_audio_formats_s32.conf @@ -0,0 +1,176 @@ + # The formats of a module which only processes 32 bit samples, for every + # channel count and rate the offloaded USB endpoint can carry. + # + # 7 channels is left out, it has no channel config and no channel map in + # common_definitions.conf. + num_output_audio_formats 105 + CombineArrays.Object.Base.output_audio_format [ + { + out_rate [ + 8000 + 11025 + 12000 + 16000 + 22050 + 24000 + 32000 + 44100 + 48000 + 64000 + 88200 + 96000 + 128000 + 176400 + 192000 + ] + out_bit_depth [ 32 ] + out_valid_bit_depth [ 32 ] + out_channels [ 1 ] + out_ch_cfg [ $CHANNEL_CONFIG_MONO ] + out_ch_map [ $CHANNEL_MAP_MONO ] + } + { + out_rate [ + 8000 + 11025 + 12000 + 16000 + 22050 + 24000 + 32000 + 44100 + 48000 + 64000 + 88200 + 96000 + 128000 + 176400 + 192000 + ] + out_bit_depth [ 32 ] + out_valid_bit_depth [ 32 ] + out_channels [ 2 ] + out_ch_cfg [ $CHANNEL_CONFIG_STEREO ] + out_ch_map [ $CHANNEL_MAP_STEREO ] + } + { + out_rate [ + 8000 + 11025 + 12000 + 16000 + 22050 + 24000 + 32000 + 44100 + 48000 + 64000 + 88200 + 96000 + 128000 + 176400 + 192000 + ] + out_bit_depth [ 32 ] + out_valid_bit_depth [ 32 ] + out_channels [ 3 ] + out_ch_cfg [ $CHANNEL_CONFIG_2_POINT_1 ] + out_ch_map [ $CHANNEL_MAP_2_POINT_1 ] + } + { + out_rate [ + 8000 + 11025 + 12000 + 16000 + 22050 + 24000 + 32000 + 44100 + 48000 + 64000 + 88200 + 96000 + 128000 + 176400 + 192000 + ] + 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_rate [ + 8000 + 11025 + 12000 + 16000 + 22050 + 24000 + 32000 + 44100 + 48000 + 64000 + 88200 + 96000 + 128000 + 176400 + 192000 + ] + out_bit_depth [ 32 ] + out_valid_bit_depth [ 32 ] + out_channels [ 5 ] + out_ch_cfg [ $CHANNEL_CONFIG_5_POINT_0 ] + out_ch_map [ $CHANNEL_MAP_5_POINT_0 ] + } + { + out_rate [ + 8000 + 11025 + 12000 + 16000 + 22050 + 24000 + 32000 + 44100 + 48000 + 64000 + 88200 + 96000 + 128000 + 176400 + 192000 + ] + out_bit_depth [ 32 ] + out_valid_bit_depth [ 32 ] + out_channels [ 6 ] + out_ch_cfg [ $CHANNEL_CONFIG_5_POINT_1 ] + out_ch_map [ $CHANNEL_MAP_5_POINT_1 ] + } + { + out_rate [ + 8000 + 11025 + 12000 + 16000 + 22050 + 24000 + 32000 + 44100 + 48000 + 64000 + 88200 + 96000 + 128000 + 176400 + 192000 + ] + out_bit_depth [ 32 ] + out_valid_bit_depth [ 32 ] + out_channels [ 8 ] + out_ch_cfg [ $CHANNEL_CONFIG_7_POINT_1 ] + out_ch_map [ $CHANNEL_MAP_7_POINT_1 ] + } + ] 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/dai-kpb-be.conf b/tools/topology/topology2/include/pipelines/cavs/dai-kpb-be.conf index 91ca50a62bed..64823ed4e573 100644 --- a/tools/topology/topology2/include/pipelines/cavs/dai-kpb-be.conf +++ b/tools/topology/topology2/include/pipelines/cavs/dai-kpb-be.conf @@ -125,13 +125,6 @@ Class.Pipeline."dai-kpb-be" { } } - Object.Base { - route."1" { - source "dai-copier.DMIC.$index.1" - sink "kpb.$index.1" - } - } - time_domain "timer" dynamic_pipeline 1 } 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-micsel-mfcc-capture.conf b/tools/topology/topology2/include/pipelines/cavs/host-gateway-src-micsel-mfcc-capture.conf new file mode 100644 index 000000000000..c721cb26b2d9 --- /dev/null +++ b/tools/topology/topology2/include/pipelines/cavs/host-gateway-src-micsel-mfcc-capture.conf @@ -0,0 +1,172 @@ +# +# SRC-Micsel-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-micsel-mfcc-capture.N.attribute_name". +# +# Usage: host-gateway-src-micsel-mfcc-capture pipeline object can be instantiated as: +# +# Object.Pipeline.host-gateway-src-micsel-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/micsel.conf> +<include/components/mfcc.conf> + +Define { + # Default MFCC output frame size (header + coefficients). + # Can be overridden by feature/platform includes or CMake variable overrides. + MFCC_FRAME_BYTES 344 +} + +Class.Pipeline."host-gateway-src-micsel-mfcc-capture" { + + <include/pipelines/pipeline-common.conf> + + attributes { + !constructor [ + "index" + ] + + # + # host-gateway-src-micsel-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 + } + ] + } + + micsel."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 + out_channels 2 + out_ch_cfg $CHANNEL_CONFIG_DUAL_MONO + } + ] + } + + 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 + # Compress output frame: header + coefficients. + # Size set by MFCC_FRAME_BYTES Define. + obs $MFCC_FRAME_BYTES + } + ] + } + + 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 + # Match MFCC compress output frame size + ibs $MFCC_FRAME_BYTES + } + ] + Object.Base.output_audio_format [ + { + out_bit_depth 32 + out_valid_bit_depth 32 + out_rate 16000 + obs $MFCC_FRAME_BYTES + } + ] + } + + pipeline."1" { + priority 0 + lp_mode 0 + } + } + + + Object.Base { + !route [ + { + source src.$index.1 + sink micsel.$index.1 + } + { + source micsel.$index.1 + sink mfcc.$index.1 + } + ] + } + + direction "capture" + dynamic_pipeline 1 + time_domain "timer" +} diff --git a/tools/topology/topology2/include/pipelines/cavs/host-gateway-tdfb-drc-capture.conf b/tools/topology/topology2/include/pipelines/cavs/host-gateway-tdfb-drc-capture.conf index c0912037a424..352ffb2389a3 100644 --- a/tools/topology/topology2/include/pipelines/cavs/host-gateway-tdfb-drc-capture.conf +++ b/tools/topology/topology2/include/pipelines/cavs/host-gateway-tdfb-drc-capture.conf @@ -19,6 +19,37 @@ <include/components/tdfb.conf> <include/pipelines/cavs/host-gateway-capture.conf> +Define { + TDFB_DMIC_PCM_CHANNELS 2 +} + +IncludeByKey.TDFB_DMIC_PCM_CHANNELS { + "1" { + Define { + TDFB_DMIC_CHANNEL_CFG $CHANNEL_CONFIG_MONO + TDFB_DMIC_CHANNEL_MAP $CHANNEL_MAP_MONO + } + } + "2" { + Define { + TDFB_DMIC_CHANNEL_CFG $CHANNEL_CONFIG_STEREO + TDFB_DMIC_CHANNEL_MAP $CHANNEL_MAP_STEREO + } + } + "4" { + Define { + TDFB_DMIC_CHANNEL_CFG $CHANNEL_CONFIG_3_POINT_1 + TDFB_DMIC_CHANNEL_MAP $CHANNEL_MAP_3_POINT_1 + } + } + "8" { + Define { + TDFB_SDW_DMIC_CHANNEL_CFG $CHANNEL_CONFIG_7_POINT_1 + TDFB_SDW_DMIC_CHANNEL_MAP $CHANNEL_MAP_7_POINT_1 + } + } +} + Class.Pipeline."host-gateway-tdfb-drc-capture" { SubTreeCopy.baseclass { @@ -37,7 +68,6 @@ Class.Pipeline."host-gateway-tdfb-drc-capture" { tdfb."1" { Object.Control { bytes."1" { - max 16384 IncludeByKey.EFX_MIC_TDFB_PARAMS { "line2_pass" "include/components/tdfb/line2_pass.conf" "line2_generic_pm10deg" "include/components/tdfb/line2_generic_pm10deg_48khz.conf" @@ -57,12 +87,18 @@ Class.Pipeline."host-gateway-tdfb-drc-capture" { { in_bit_depth 32 in_valid_bit_depth 32 + in_channels $TDFB_DMIC_PCM_CHANNELS + in_ch_cfg $TDFB_DMIC_CHANNEL_CFG + in_ch_map $TDFB_DMIC_CHANNEL_MAP } ] Object.Base.output_audio_format [ { out_bit_depth 32 out_valid_bit_depth 32 + out_channels $TDFB_DMIC_PCM_CHANNELS + out_ch_cfg $TDFB_DMIC_CHANNEL_CFG + out_ch_map $TDFB_DMIC_CHANNEL_MAP } ] } @@ -82,12 +118,18 @@ Class.Pipeline."host-gateway-tdfb-drc-capture" { { in_bit_depth 32 in_valid_bit_depth 32 + in_channels $TDFB_DMIC_PCM_CHANNELS + in_ch_cfg $TDFB_DMIC_CHANNEL_CFG + in_ch_map $TDFB_DMIC_CHANNEL_MAP } ] Object.Base.output_audio_format [ { out_bit_depth 32 out_valid_bit_depth 32 + out_channels $TDFB_DMIC_PCM_CHANNELS + out_ch_cfg $TDFB_DMIC_CHANNEL_CFG + out_ch_map $TDFB_DMIC_CHANNEL_MAP } ] } 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-alh-dai-copier-playback.conf b/tools/topology/topology2/include/pipelines/cavs/mixout-gain-dax-ctc-alh-dai-copier-playback.conf new file mode 100644 index 000000000000..887aad30ef78 --- /dev/null +++ b/tools/topology/topology2/include/pipelines/cavs/mixout-gain-dax-ctc-alh-dai-copier-playback.conf @@ -0,0 +1,57 @@ +# +# BE playback pipeline: mixout-gain-dax-ctc-alh-dai-copier. +# + +<include/components/dolby-dax.conf> +<include/components/ctc.conf> +<include/pipelines/cavs/mixout-gain-dax-alh-dai-copier-playback.conf> + +Class.Pipeline."mixout-gain-dax-ctc-alh-dai-copier-playback" { + SubTreeCopy.baseclass { + # this class extends the mixout-gain-dax-alh-dai-copier-playback class definition + source "Class.Pipeline.mixout-gain-dax-alh-dai-copier-playback" + + tree { + Object.Widget { + 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 + } + { + source mixout.$index.1 + sink gain.$index.1 + } + ] + } + } + } +} 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-ctc-alh-dai-copier-playback.conf b/tools/topology/topology2/include/pipelines/cavs/mixout-gain-eqiir-dts-ctc-alh-dai-copier-playback.conf new file mode 100644 index 000000000000..693d5a04378f --- /dev/null +++ b/tools/topology/topology2/include/pipelines/cavs/mixout-gain-eqiir-dts-ctc-alh-dai-copier-playback.conf @@ -0,0 +1,60 @@ +# +# BE playback pipeline: mixout-gain-eqiir-dts-ctc-alh-dai-copier. +# + +<include/components/ctc.conf> +<include/pipelines/cavs/mixout-gain-eqiir-dts-alh-dai-copier-playback.conf> + +Class.Pipeline."mixout-gain-eqiir-dts-ctc-alh-dai-copier-playback" { + SubTreeCopy.baseclass { + # this class extends the mixout-gain-eqiir-dts-alh-dai-copier-playback class definition + source "Class.Pipeline.mixout-gain-eqiir-dts-alh-dai-copier-playback" + + tree { + Object.Widget { + 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 eqiir.$index.1 + } + { + source eqiir.$index.1 + sink dts.$index.1 + } + { + source dts.$index.1 + sink ctc.$index.1 + } + { + source mixout.$index.1 + sink gain.$index.1 + } + ] + } + } + } +} 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-eqiir-eqfir-mbdrc-alh-dai-copier-playback.conf b/tools/topology/topology2/include/pipelines/cavs/mixout-gain-eqiir-eqfir-mbdrc-alh-dai-copier-playback.conf new file mode 100644 index 000000000000..b39ccaddaa81 --- /dev/null +++ b/tools/topology/topology2/include/pipelines/cavs/mixout-gain-eqiir-eqfir-mbdrc-alh-dai-copier-playback.conf @@ -0,0 +1,137 @@ +# +# BE playback pipeline: mixout-gain-eqiir-eqfir-mbdrc-alh-dai-copier. +# +# All attributes defined herein are namespaced by alsatplg to +# "Object.Pipeline.mixout-gain-eqiir-eqfir-mbdrc-alh-dai-copier-playback.N.attribute_name" +# +# Usage: mixout-gain-eqiir-eqfir-mbdrc-alh-dai-copier-playback pipeline object can be +# instantiated as: +# +# Object.Pipeline.mixout-gain-eqiir-eqfir-mbdrc-alh-dai-copier-playback."N" { +# time_domain "timer" +# } +# +# Where N is the unique pipeline ID within the same alsaconf node. +# + +<include/components/eqiir.conf> +<include/components/eqfir.conf> +<include/components/multiband_drc.conf> +<include/pipelines/cavs/mixout-gain-alh-dai-copier-playback.conf> + +Class.Pipeline."mixout-gain-eqiir-eqfir-mbdrc-alh-dai-copier-playback" { + + SubTreeCopy.baseclass { + # this class extends the mixout-gain-alh-dai-copier-playback 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-eqiir-eqfir-mbdrc-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 { + eqiir."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 + } + ] + + Object.Control.bytes."1" { + IncludeByKey.EFX_SPK_IIR_PARAMS { + "passthrough" "include/components/eqiir/passthrough.conf" + "highpass_20hz_0db_48khz" "include/components/eqiir/highpass_20hz_0db_48khz.conf" + "highpass_30hz_0db_48khz" "include/components/eqiir/highpass_30hz_0db_48khz.conf" + "highpass_40hz_0db_48khz" "include/components/eqiir/highpass_40hz_0db_48khz.conf" + "highpass_50hz_0db_48khz" "include/components/eqiir/highpass_50hz_0db_48khz.conf" + "highpass_100hz_0db_48khz" "include/components/eqiir/highpass_100hz_0db_48khz.conf" + } + } + } + + eqfir."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 + } + ] + + Object.Control.bytes."1" { + IncludeByKey.EFX_SPK_FIR_PARAMS { + "passthrough" "include/components/eqfir/passthrough.conf" + } + } + } + + multiband_drc."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 + } + ] + + Object.Control.bytes."1" { + IncludeByKey.EFX_SPK_MBDRC_PARAMS { + "passthrough" "include/components/multiband_drc/passthrough.conf" + "default" "include/components/multiband_drc/default.conf" + } + } + } + } + + Object.Base { + !route [ + { + source gain.$index.1 + sink eqiir.$index.1 + } + { + source eqiir.$index.1 + sink eqfir.$index.1 + } + { + source eqfir.$index.1 + sink multiband_drc.$index.1 + } + ] + } + } + } +} 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/include/pipelines/cavs/wov-detect.conf b/tools/topology/topology2/include/pipelines/cavs/wov-detect.conf index 0f4490525c83..ff192d4679ac 100644 --- a/tools/topology/topology2/include/pipelines/cavs/wov-detect.conf +++ b/tools/topology/topology2/include/pipelines/cavs/wov-detect.conf @@ -58,6 +58,8 @@ Class.Pipeline."wov-detect" { } micsel."1" { + num_input_audio_formats 1 + num_output_audio_formats 1 Object.Base.input_audio_format [ { in_rate 16000 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..a6cc498f5b14 100644 --- a/tools/topology/topology2/platform/intel/deep-buffer.conf +++ b/tools/topology/topology2/platform/intel/deep-buffer.conf @@ -1,28 +1,69 @@ +Define { + DEEP_BUFFER_CORE 0 +} Object.Pipeline.deepbuffer-playback [ { index $DEEP_BUFFER_PIPELINE_ID + core_id $DEEP_BUFFER_CORE 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 + 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 + } + ] } - { - 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 @@ -67,6 +108,9 @@ Object.Pipeline.deepbuffer-playback [ } ] } + Object.Widget.pipeline.1 { + core $DEEP_BUFFER_CORE + } } ] @@ -83,7 +127,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 +171,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/dmic-wov.conf b/tools/topology/topology2/platform/intel/dmic-wov.conf index 0094941e0c74..db9b9a45129b 100644 --- a/tools/topology/topology2/platform/intel/dmic-wov.conf +++ b/tools/topology/topology2/platform/intel/dmic-wov.conf @@ -110,6 +110,10 @@ Object.Pipeline { } Object.Base.route [ + { + source $DMIC_WOV_DAI_PIPELINE_SRC + sink $DMIC_WOV_DAI_PIPELINE_KPB + } { source $DMIC_WOV_DAI_PIPELINE_KPB sink "host-copier.11.capture" diff --git a/tools/topology/topology2/platform/intel/dmic1-mfcc.conf b/tools/topology/topology2/platform/intel/dmic1-mfcc.conf index f3926a283a8b..3aad756a85f5 100644 --- a/tools/topology/topology2/platform/intel/dmic1-mfcc.conf +++ b/tools/topology/topology2/platform/intel/dmic1-mfcc.conf @@ -454,11 +454,14 @@ Object.Widget.mfcc.1 { index $DMIC1_HOST_PIPELINE_ID Object.Control { bytes."1" { - name 'Analog Capture TDFB bytes' + name "Dmic1 Capture MFCC bytes" IncludeByKey.DMIC1_MFCC_PARAMS { "default" "include/components/mfcc/default.conf" } } + mixer."1" { + name "Dmic1 Capture MFCC VAD" + } } IncludeByKey.NUM_DMICS { "1" { diff --git a/tools/topology/topology2/platform/intel/sdw-amp-dax-ctc.conf b/tools/topology/topology2/platform/intel/sdw-amp-dax-ctc.conf new file mode 100644 index 000000000000..6e05f327a1c0 --- /dev/null +++ b/tools/topology/topology2/platform/intel/sdw-amp-dax-ctc.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-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.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_CTC_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 "ctc.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 "ctc.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 "ctc.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-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-ctc.conf b/tools/topology/topology2/platform/intel/sdw-amp-dts-ctc.conf new file mode 100644 index 000000000000..9e401196f2a7 --- /dev/null +++ b/tools/topology/topology2/platform/intel/sdw-amp-dts-ctc.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-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)) | ($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_CTC_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 "ctc.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 "ctc.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 "ctc.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-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..5844a0c40729 --- /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_CTC_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..02d187bfd9a7 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 { @@ -53,66 +140,134 @@ IncludeByKey.PASSTHROUGH { IncludeByKey.SDW_SPK_ENHANCED_PLAYBACK { "true" { - mixout-gain-eqiir-eqfir-drc-alh-dai-copier-playback [ - { - index 21 + # multiband and singleband branches differ only in the DRC widget + # and MBDRC/DRC control names; keep both branches in sync. + IncludeByKey.EFX_SPK_DRC_COMPONENT { + "multiband" { + mixout-gain-eqiir-eqfir-mbdrc-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 + 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))]" + } + ] } - { - in_bit_depth 32 - in_valid_bit_depth 24 + Object.Widget.gain.1 { + Object.Control.mixer.1 { + name 'Post Mixer $AMP_PLAYBACK_NAME Volume' + } } - { - in_bit_depth 32 - in_valid_bit_depth 32 + + Object.Widget.eqiir.1 { + Object.Control.bytes."1" { + name 'Post Mixer $AMP_PLAYBACK_NAME IIR Eq bytes' + } } - ] - 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.eqfir.1 { + Object.Control.bytes."1" { + name 'Post Mixer $AMP_PLAYBACK_NAME FIR Eq bytes' + } + } + Object.Widget.multiband_drc.1 { + Object.Control { + bytes."1" { + name 'Post Mixer $AMP_PLAYBACK_NAME MBDRC bytes' + } + mixer."1" { + name 'Post Mixer $AMP_PLAYBACK_NAME MBDRC switch' + } + } } - ] - } - Object.Widget.gain.1 { - Object.Control.mixer.1 { - name 'Post Mixer $AMP_PLAYBACK_NAME Volume' } - } + ] + } + "singleband" { + mixout-gain-eqiir-eqfir-drc-alh-dai-copier-playback [ + { + index 21 - 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' + 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' + } } - mixer."1" { - name 'Post Mixer $AMP_PLAYBACK_NAME DRC switch' + 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-alh-dai-copier-playback [ @@ -300,6 +455,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 +464,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 +605,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 +614,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 +634,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 +643,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 +738,83 @@ 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" { + # multiband and singleband branches differ only in the source widget; + # keep both branches in sync. + IncludeByKey.EFX_SPK_DRC_COMPONENT { + "multiband" { + Object.Base.route [ + { + source "multiband_drc.21.1" + sink "module-copier.21.22" + } + { + source "module-copier.21.22" + sink "alh-copier.$SDW_SPK_STREAM.0" + } + ] + } + "singleband" { + 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-compress.conf b/tools/topology/topology2/platform/intel/sdw-dmic-audio-feature-compress.conf new file mode 100644 index 000000000000..7f6b7daa804e --- /dev/null +++ b/tools/topology/topology2/platform/intel/sdw-dmic-audio-feature-compress.conf @@ -0,0 +1,80 @@ +Define { + SDW_DMIC_MODULE_COPIER_ID 41 + SDW_DMIC_COMPR_AUDIO_FEATURE_CAPTURE_PCM_NAME "Microphone Compress Audio Features" + SDW_DMIC_COMPR_AUDIO_FEATURE_CAPTURE_PCM_ID 54 + SDW_DMIC_COMPR_AUDIO_FEATURE_CAPTURE_STREAM_NAME "Microphone Compress Audio Features Stream" + SDW_DMIC_COMPR_AUDIO_FEATURE_CAPTURE_PIPELINE_ID 133 + # MFCC compress output frame size in bytes: + # Mel-only (80 bins): 24 + 80*4 = 344 + # Cepstral (13 ceps): 24 + 13*4 = 76 + MFCC_FRAME_BYTES 344 + # MFCC config blob: mel or ceps + MFCC_BLOB mel +} + +Object.Pipeline.host-gateway-src-micsel-mfcc-capture [ + { + index $SDW_DMIC_COMPR_AUDIO_FEATURE_CAPTURE_PIPELINE_ID + + Object.Widget.host-copier.1 { + stream_name "$SDW_DMIC_COMPR_AUDIO_FEATURE_CAPTURE_STREAM_NAME" + pcm_id $SDW_DMIC_COMPR_AUDIO_FEATURE_CAPTURE_PCM_ID + } + + Object.Widget.micsel.1 { + Object.Control { + bytes."1" { + name "$SDW_DMIC_COMPR_AUDIO_FEATURE_CAPTURE_PCM_NAME Micsel bytes" + <include/components/micsel/downmix_stereo_to_doublemono.conf> + } + } + } + + Object.Widget.mfcc.1 { + type "encoder" + Object.Control { + bytes."1" { + name "$SDW_DMIC_COMPR_AUDIO_FEATURE_CAPTURE_PCM_NAME MFCC bytes" + IncludeByKey.MFCC_BLOB { + "mel" "include/components/mfcc/mel80_compress_dtx.conf" + "ceps" "include/components/mfcc/ceps13_compress_dtx.conf" + } + } + mixer."1" { + name "$SDW_DMIC_COMPR_AUDIO_FEATURE_CAPTURE_PCM_NAME MFCC VAD" + } + } + } + } +] +Object.Base.route [ + { + source "module-copier.$SDW_DMIC_MODULE_COPIER_ID.0" + sink "src.$SDW_DMIC_COMPR_AUDIO_FEATURE_CAPTURE_PIPELINE_ID.1" + } + { + source "mfcc.$SDW_DMIC_COMPR_AUDIO_FEATURE_CAPTURE_PIPELINE_ID.1" + sink "host-copier.$SDW_DMIC_COMPR_AUDIO_FEATURE_CAPTURE_PCM_ID.capture" + } +] + +Object.PCM.pcm [ + { + name "$SDW_DMIC_COMPR_AUDIO_FEATURE_CAPTURE_PCM_NAME" + id $SDW_DMIC_COMPR_AUDIO_FEATURE_CAPTURE_PCM_ID + direction "capture" + compress "true" + + Object.Base.fe_dai.1 { + name "$SDW_DMIC_COMPR_AUDIO_FEATURE_CAPTURE_PCM_NAME" + } + + Object.PCM.pcm_caps.1 { + name "$SDW_DMIC_COMPR_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-audio-feature.conf b/tools/topology/topology2/platform/intel/sdw-dmic-audio-feature.conf new file mode 100644 index 000000000000..fdb4acbf9a6d --- /dev/null +++ b/tools/topology/topology2/platform/intel/sdw-dmic-audio-feature.conf @@ -0,0 +1,71 @@ +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 + # MFCC output frame size in bytes (24-byte header + coefficients): + # Mel-only (80 bins): 24 + 80*4 = 344 + MFCC_FRAME_BYTES 344 +} + +Object.Pipeline.host-gateway-src-micsel-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.micsel.1 { + Object.Control { + bytes."1" { + name "$SDW_DMIC_AUDIO_FEATURE_CAPTURE_PCM_NAME Micsel bytes" + <include/components/micsel/downmix_stereo_to_doublemono.conf> + } + } + } + + Object.Widget.mfcc.1 { + Object.Control { + bytes."1" { + name "$SDW_DMIC_AUDIO_FEATURE_CAPTURE_PCM_NAME MFCC bytes" + <include/components/mfcc/mel80.conf> + } + mixer."1" { + name "$SDW_DMIC_AUDIO_FEATURE_CAPTURE_PCM_NAME MFCC VAD" + } + } + } + } +] +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..7a42ade4704f 100644 --- a/tools/topology/topology2/platform/intel/sdw-dmic-generic.conf +++ b/tools/topology/topology2/platform/intel/sdw-dmic-generic.conf @@ -5,7 +5,37 @@ Define { SDW_DMIC_STREAM 'SDW3-Capture' SDW_DMIC_BE_ID 4 SDW_DMIC_PASSTHROUGH "false" - SDW_DMIC_HOST_PIPELINE_ID 40 + SDW_DMIC_HOST_PIPELINE_ID 40 + SDW_DMIC_PCM_CHANNELS 2 + SDW_DMIC_CHANNEL_CFG $CHANNEL_CONFIG_STEREO + SDW_DMIC_CHANNEL_MAP $CHANNEL_MAP_STEREO +} + +IncludeByKey.SDW_DMIC_PCM_CHANNELS { + "1" { + Define { + SDW_DMIC_CHANNEL_CFG $CHANNEL_CONFIG_MONO + SDW_DMIC_CHANNEL_MAP $CHANNEL_MAP_MONO + } + } + "2" { + Define { + SDW_DMIC_CHANNEL_CFG $CHANNEL_CONFIG_STEREO + SDW_DMIC_CHANNEL_MAP $CHANNEL_MAP_STEREO + } + } + "4" { + Define { + SDW_DMIC_CHANNEL_CFG $CHANNEL_CONFIG_3_POINT_1 + SDW_DMIC_CHANNEL_MAP $CHANNEL_MAP_3_POINT_1 + } + } + "8" { + Define { + SDW_DMIC_CHANNEL_CFG $CHANNEL_CONFIG_7_POINT_1 + SDW_DMIC_CHANNEL_MAP $CHANNEL_MAP_7_POINT_1 + } + } } Object.Dai.ALH [ @@ -16,7 +46,7 @@ Object.Dai.ALH [ name $SDW_DMIC_STREAM default_hw_conf_id 0 rate 48000 - channels 2 + channels $SDW_DMIC_PCM_CHANNELS Object.Base.hw_config.1 { id 0 @@ -36,12 +66,147 @@ IncludeByKey.SDW_DMIC_ENHANCED_CAPTURE { Object.Widget.host-copier.1 { stream_name "sdw dmic" pcm_id 4 - } + IncludeByKey.PCM_FORMAT_ALL { + "true" { + num_input_audio_formats 5 + num_output_audio_formats 5 + Object.Base.input_audio_format [ + { + in_channels $SDW_DMIC_PCM_CHANNELS + in_bit_depth 16 + in_valid_bit_depth 16 + in_ch_cfg $SDW_DMIC_CHANNEL_CFG + in_ch_map $SDW_DMIC_CHANNEL_MAP + } + { + in_channels $SDW_DMIC_PCM_CHANNELS + in_bit_depth 32 + in_valid_bit_depth 32 + in_ch_cfg $SDW_DMIC_CHANNEL_CFG + in_ch_map $SDW_DMIC_CHANNEL_MAP + } + { + in_channels $SDW_DMIC_PCM_CHANNELS + in_bit_depth 32 + in_valid_bit_depth 24 + in_ch_cfg $SDW_DMIC_CHANNEL_CFG + in_ch_map $SDW_DMIC_CHANNEL_MAP + } + { + in_channels $SDW_DMIC_PCM_CHANNELS + in_bit_depth 32 + in_valid_bit_depth 32 + in_sample_type $SAMPLE_TYPE_FLOAT + in_ch_cfg $SDW_DMIC_CHANNEL_CFG + in_ch_map $SDW_DMIC_CHANNEL_MAP + } + { + in_channels $SDW_DMIC_PCM_CHANNELS + in_bit_depth 8 + in_valid_bit_depth 8 + in_sample_type $SAMPLE_TYPE_UNSIGNED_INTEGER + in_ch_cfg $SDW_DMIC_CHANNEL_CFG + in_ch_map $SDW_DMIC_CHANNEL_MAP + } + ] + Object.Base.output_audio_format [ + { + out_channels $SDW_DMIC_PCM_CHANNELS + out_bit_depth 16 + out_valid_bit_depth 16 + out_ch_cfg $SDW_DMIC_CHANNEL_CFG + out_ch_map $SDW_DMIC_CHANNEL_MAP + } + { + out_channels $SDW_DMIC_PCM_CHANNELS + out_bit_depth 32 + out_valid_bit_depth 32 + out_ch_cfg $SDW_DMIC_CHANNEL_CFG + out_ch_map $SDW_DMIC_CHANNEL_MAP + } + { + out_channels $SDW_DMIC_PCM_CHANNELS + out_bit_depth 32 + out_valid_bit_depth 24 + out_ch_cfg $SDW_DMIC_CHANNEL_CFG + out_ch_map $SDW_DMIC_CHANNEL_MAP + } + { + out_channels $SDW_DMIC_PCM_CHANNELS + out_bit_depth 32 + out_valid_bit_depth 32 + out_sample_type $SAMPLE_TYPE_FLOAT + out_ch_cfg $SDW_DMIC_CHANNEL_CFG + out_ch_map $SDW_DMIC_CHANNEL_MAP + } + { + out_channels $SDW_DMIC_PCM_CHANNELS + out_bit_depth 8 + out_valid_bit_depth 8 + out_sample_type $SAMPLE_TYPE_UNSIGNED_INTEGER + out_ch_cfg $SDW_DMIC_CHANNEL_CFG + out_ch_map $SDW_DMIC_CHANNEL_MAP + } + ] + } + "false" { + num_input_audio_formats 3 + num_output_audio_formats 3 + Object.Base.input_audio_format [ + { + in_channels $SDW_DMIC_PCM_CHANNELS + in_bit_depth 16 + in_valid_bit_depth 16 + in_ch_cfg $SDW_DMIC_CHANNEL_CFG + in_ch_map $SDW_DMIC_CHANNEL_MAP + } + { + in_channels $SDW_DMIC_PCM_CHANNELS + in_bit_depth 32 + in_valid_bit_depth 24 + in_ch_cfg $SDW_DMIC_CHANNEL_CFG + in_ch_map $SDW_DMIC_CHANNEL_MAP + } + { + in_channels $SDW_DMIC_PCM_CHANNELS + in_bit_depth 32 + in_valid_bit_depth 32 + in_ch_cfg $SDW_DMIC_CHANNEL_CFG + in_ch_map $SDW_DMIC_CHANNEL_MAP + } + ] + Object.Base.output_audio_format [ + { + out_channels $SDW_DMIC_PCM_CHANNELS + out_bit_depth 16 + out_valid_bit_depth 16 + out_ch_cfg $SDW_DMIC_CHANNEL_CFG + out_ch_map $SDW_DMIC_CHANNEL_MAP + } + { + out_channels $SDW_DMIC_PCM_CHANNELS + out_bit_depth 32 + out_valid_bit_depth 24 + out_ch_cfg $SDW_DMIC_CHANNEL_CFG + out_ch_map $SDW_DMIC_CHANNEL_MAP + } + { + out_channels $SDW_DMIC_PCM_CHANNELS + out_bit_depth 32 + out_valid_bit_depth 32 + out_ch_cfg $SDW_DMIC_CHANNEL_CFG + out_ch_map $SDW_DMIC_CHANNEL_MAP + } + ] + } + } + } Object.Widget.tdfb.1 { Object.Control { bytes."1" { name '$SDW_DMIC_CAPTURE_PCM_NAME Capture TDFB bytes' + max 16384 } mixer."1" { name '$SDW_DMIC_CAPTURE_PCM_NAME Capture TDFB beam switch' @@ -75,6 +240,76 @@ 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_channels $SDW_DMIC_PCM_CHANNELS + out_bit_depth 16 + out_valid_bit_depth 16 + out_ch_cfg $SDW_DMIC_CHANNEL_CFG + out_ch_map $SDW_DMIC_CHANNEL_MAP + } + { + out_channels $SDW_DMIC_PCM_CHANNELS + out_bit_depth 32 + out_valid_bit_depth 32 + out_ch_cfg $SDW_DMIC_CHANNEL_CFG + out_ch_map $SDW_DMIC_CHANNEL_MAP + } + { + out_channels $SDW_DMIC_PCM_CHANNELS + out_bit_depth 32 + out_valid_bit_depth 24 + out_ch_cfg $SDW_DMIC_CHANNEL_CFG + out_ch_map $SDW_DMIC_CHANNEL_MAP + } + { + out_channels $SDW_DMIC_PCM_CHANNELS + out_bit_depth 32 + out_valid_bit_depth 32 + out_sample_type $SAMPLE_TYPE_FLOAT + out_ch_cfg $SDW_DMIC_CHANNEL_CFG + out_ch_map $SDW_DMIC_CHANNEL_MAP + } + { + out_channels $SDW_DMIC_PCM_CHANNELS + out_bit_depth 8 + out_valid_bit_depth 8 + out_sample_type $SAMPLE_TYPE_UNSIGNED_INTEGER + out_ch_cfg $SDW_DMIC_CHANNEL_CFG + out_ch_map $SDW_DMIC_CHANNEL_MAP + } + ] + } + "false" { + num_output_audio_formats 3 + Object.Base.output_audio_format [ + { + out_channels $SDW_DMIC_PCM_CHANNELS + out_bit_depth 16 + out_valid_bit_depth 16 + out_ch_cfg $SDW_DMIC_CHANNEL_CFG + out_ch_map $SDW_DMIC_CHANNEL_MAP + } + { + out_channels $SDW_DMIC_PCM_CHANNELS + out_bit_depth 32 + out_valid_bit_depth 24 + out_ch_cfg $SDW_DMIC_CHANNEL_CFG + out_ch_map $SDW_DMIC_CHANNEL_MAP + } + { + out_channels $SDW_DMIC_PCM_CHANNELS + out_bit_depth 32 + out_valid_bit_depth 32 + out_ch_cfg $SDW_DMIC_CHANNEL_CFG + out_ch_map $SDW_DMIC_CHANNEL_MAP + } + ] + } + } } } ] @@ -95,19 +330,25 @@ Object.Widget { num_output_audio_formats 1 num_output_pins 1 - # 32-bit 48KHz 2ch + # 32-bit 48KHz capture, channel count derived from NUM_DMICS. Object.Base.input_audio_format [ { + in_channels $SDW_DMIC_PCM_CHANNELS in_bit_depth 32 in_valid_bit_depth $SDW_LINK_VALID_BITS in_sample_type $SAMPLE_TYPE_MSB_INTEGER + in_ch_cfg $SDW_DMIC_CHANNEL_CFG + in_ch_map $SDW_DMIC_CHANNEL_MAP in_fmt_cfg "$[($in_channels | ($in_valid_bit_depth * 256))]" } ] Object.Base.output_audio_format [ { + out_channels $SDW_DMIC_PCM_CHANNELS out_bit_depth 32 out_valid_bit_depth 32 + out_ch_cfg $SDW_DMIC_CHANNEL_CFG + out_ch_map $SDW_DMIC_CHANNEL_MAP } ] } @@ -128,14 +369,20 @@ Object.Widget { # index 41 is in herited from the pipeline definition Object.Base.input_audio_format [ { + in_channels $SDW_DMIC_PCM_CHANNELS in_bit_depth 32 in_valid_bit_depth 32 + in_ch_cfg $SDW_DMIC_CHANNEL_CFG + in_ch_map $SDW_DMIC_CHANNEL_MAP } ] Object.Base.output_audio_format [ { + out_channels $SDW_DMIC_PCM_CHANNELS out_bit_depth 32 out_valid_bit_depth 32 + out_ch_cfg $SDW_DMIC_CHANNEL_CFG + out_ch_map $SDW_DMIC_CHANNEL_MAP } ] @@ -151,14 +398,20 @@ Object.Widget { num_output_audio_formats 1 Object.Base.input_audio_format [ { + in_channels $SDW_DMIC_PCM_CHANNELS in_bit_depth 32 in_valid_bit_depth 32 + in_ch_cfg $SDW_DMIC_CHANNEL_CFG + in_ch_map $SDW_DMIC_CHANNEL_MAP } ] Object.Base.output_audio_format [ { + out_channels $SDW_DMIC_PCM_CHANNELS out_bit_depth 32 out_valid_bit_depth 32 + out_ch_cfg $SDW_DMIC_CHANNEL_CFG + out_ch_map $SDW_DMIC_CHANNEL_MAP } ] } @@ -180,7 +433,16 @@ Object.PCM.pcm [ Object.PCM.pcm_caps.1 { name "sdw dmic" - formats 'S16_LE,S24_LE,S32_LE' + channels_min $SDW_DMIC_PCM_CHANNELS + channels_max $SDW_DMIC_PCM_CHANNELS + 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-compress.conf b/tools/topology/topology2/platform/intel/sdw-jack-audio-feature-compress.conf new file mode 100644 index 000000000000..34577ac0a3be --- /dev/null +++ b/tools/topology/topology2/platform/intel/sdw-jack-audio-feature-compress.conf @@ -0,0 +1,80 @@ +Define { + SDW_JACK_MODULE_COPIER_ID 11 + SDW_JACK_COMPR_AUDIO_FEATURE_CAPTURE_PCM_NAME "Jack In Compress Audio Features" + SDW_JACK_COMPR_AUDIO_FEATURE_CAPTURE_PCM_ID 53 + SDW_JACK_COMPR_AUDIO_FEATURE_CAPTURE_STREAM_NAME "Jack In Compress Audio Features Stream" + SDW_JACK_COMPR_AUDIO_FEATURE_CAPTURE_PIPELINE_ID 132 + # MFCC compress output frame size in bytes: + # Mel-only (80 bins): 24 + 80*4 = 344 + # Cepstral (13 ceps): 24 + 13*4 = 76 + MFCC_FRAME_BYTES 344 + # MFCC config blob: mel or ceps + MFCC_BLOB mel +} + +Object.Pipeline.host-gateway-src-micsel-mfcc-capture [ + { + index $SDW_JACK_COMPR_AUDIO_FEATURE_CAPTURE_PIPELINE_ID + + Object.Widget.host-copier.1 { + stream_name "$SDW_JACK_COMPR_AUDIO_FEATURE_CAPTURE_STREAM_NAME" + pcm_id $SDW_JACK_COMPR_AUDIO_FEATURE_CAPTURE_PCM_ID + } + + Object.Widget.micsel.1 { + Object.Control { + bytes."1" { + name "$SDW_JACK_COMPR_AUDIO_FEATURE_CAPTURE_PCM_NAME Micsel bytes" + <include/components/micsel/downmix_stereo_to_doublemono.conf> + } + } + } + + Object.Widget.mfcc.1 { + type "encoder" + Object.Control { + bytes."1" { + name "$SDW_JACK_COMPR_AUDIO_FEATURE_CAPTURE_PCM_NAME MFCC bytes" + IncludeByKey.MFCC_BLOB { + "mel" "include/components/mfcc/mel80_compress_dtx.conf" + "ceps" "include/components/mfcc/ceps13_compress_dtx.conf" + } + } + mixer."1" { + name "$SDW_JACK_COMPR_AUDIO_FEATURE_CAPTURE_PCM_NAME MFCC VAD" + } + } + } + } +] +Object.Base.route [ + { + source "module-copier.$SDW_JACK_MODULE_COPIER_ID.0" + sink "src.$SDW_JACK_COMPR_AUDIO_FEATURE_CAPTURE_PIPELINE_ID.1" + } + { + source "mfcc.$SDW_JACK_COMPR_AUDIO_FEATURE_CAPTURE_PIPELINE_ID.1" + sink "host-copier.$SDW_JACK_COMPR_AUDIO_FEATURE_CAPTURE_PCM_ID.capture" + } +] + +Object.PCM.pcm [ + { + name "$SDW_JACK_COMPR_AUDIO_FEATURE_CAPTURE_PCM_NAME" + id $SDW_JACK_COMPR_AUDIO_FEATURE_CAPTURE_PCM_ID + direction "capture" + compress "true" + + Object.Base.fe_dai.1 { + name "$SDW_JACK_COMPR_AUDIO_FEATURE_CAPTURE_PCM_NAME" + } + + Object.PCM.pcm_caps.1 { + name "$SDW_JACK_COMPR_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-audio-feature.conf b/tools/topology/topology2/platform/intel/sdw-jack-audio-feature.conf new file mode 100644 index 000000000000..3e97833ab431 --- /dev/null +++ b/tools/topology/topology2/platform/intel/sdw-jack-audio-feature.conf @@ -0,0 +1,71 @@ +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 + # MFCC output frame size in bytes (24-byte header + coefficients): + # Mel-only (80 bins): 24 + 80*4 = 344 + MFCC_FRAME_BYTES 344 +} + +Object.Pipeline.host-gateway-src-micsel-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.micsel.1 { + Object.Control { + bytes."1" { + name "$SDW_JACK_AUDIO_FEATURE_CAPTURE_PCM_NAME Micsel bytes" + <include/components/micsel/downmix_stereo_to_doublemono.conf> + } + } + } + + Object.Widget.mfcc.1 { + Object.Control { + bytes."1" { + name "$SDW_JACK_AUDIO_FEATURE_CAPTURE_PCM_NAME MFCC bytes" + <include/components/mfcc/mel80.conf> + } + mixer."1" { + name "$SDW_JACK_AUDIO_FEATURE_CAPTURE_PCM_NAME MFCC VAD" + } + } + } + } +] +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..424ff185c9ae 100644 --- a/tools/topology/topology2/platform/intel/sdw-jack-generic.conf +++ b/tools/topology/topology2/platform/intel/sdw-jack-generic.conf @@ -38,7 +38,8 @@ Object.Dai.ALH [ name $SDW_JACK_IN_STREAM default_hw_conf_id 0 rate $JACK_RATE - channels 2 + channels $SDW_JACK_CAPTURE_CH + Object.Base.hw_config.1 { id 0 @@ -60,23 +61,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 @@ -307,34 +345,155 @@ Object.Pipeline.host-gateway-capture [ Object.Base.input_audio_format [ { in_rate $JACK_RATE + in_channels $SDW_JACK_CAPTURE_CH in_bit_depth 32 in_valid_bit_depth 32 + IncludeByKey.SDW_JACK_CAPTURE_CH { + "1" { + in_ch_map $CHANNEL_MAP_MONO + in_ch_cfg $CHANNEL_CONFIG_MONO + } + } } ] - 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_channels $SDW_JACK_CAPTURE_CH + out_bit_depth 16 + out_valid_bit_depth 16 + IncludeByKey.SDW_JACK_CAPTURE_CH { + "1" { + out_ch_map $CHANNEL_MAP_MONO + out_ch_cfg $CHANNEL_CONFIG_MONO + } + } + } + { + out_rate $JACK_RATE + out_channels $SDW_JACK_CAPTURE_CH + out_bit_depth 32 + out_valid_bit_depth 24 + IncludeByKey.SDW_JACK_CAPTURE_CH { + "1" { + out_ch_map $CHANNEL_MAP_MONO + out_ch_cfg $CHANNEL_CONFIG_MONO + } + } + } + { + out_rate $JACK_RATE + out_channels $SDW_JACK_CAPTURE_CH + out_bit_depth 32 + out_valid_bit_depth 32 + IncludeByKey.SDW_JACK_CAPTURE_CH { + "1" { + out_ch_map $CHANNEL_MAP_MONO + out_ch_cfg $CHANNEL_CONFIG_MONO + } + } + } + { + out_rate $JACK_RATE + out_channels $SDW_JACK_CAPTURE_CH + out_bit_depth 32 + out_valid_bit_depth 32 + out_sample_type $SAMPLE_TYPE_FLOAT + IncludeByKey.SDW_JACK_CAPTURE_CH { + "1" { + out_ch_map $CHANNEL_MAP_MONO + out_ch_cfg $CHANNEL_CONFIG_MONO + } + } + } + { + out_rate $JACK_RATE + out_channels $SDW_JACK_CAPTURE_CH + out_bit_depth 8 + out_valid_bit_depth 8 + out_sample_type $SAMPLE_TYPE_UNSIGNED_INTEGER + IncludeByKey.SDW_JACK_CAPTURE_CH { + "1" { + out_ch_map $CHANNEL_MAP_MONO + out_ch_cfg $CHANNEL_CONFIG_MONO + } + } + } + ] } - { - 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_channels $SDW_JACK_CAPTURE_CH + out_bit_depth 16 + out_valid_bit_depth 16 + IncludeByKey.SDW_JACK_CAPTURE_CH { + "1" { + out_ch_map $CHANNEL_MAP_MONO + out_ch_cfg $CHANNEL_CONFIG_MONO + } + } + } + { + out_rate $JACK_RATE + out_channels $SDW_JACK_CAPTURE_CH + out_bit_depth 32 + out_valid_bit_depth 24 + IncludeByKey.SDW_JACK_CAPTURE_CH { + "1" { + out_ch_map $CHANNEL_MAP_MONO + out_ch_cfg $CHANNEL_CONFIG_MONO + } + } + } + { + out_rate $JACK_RATE + out_channels $SDW_JACK_CAPTURE_CH + out_bit_depth 32 + out_valid_bit_depth 32 + IncludeByKey.SDW_JACK_CAPTURE_CH { + "1" { + out_ch_map $CHANNEL_MAP_MONO + out_ch_cfg $CHANNEL_CONFIG_MONO + } + } + } + ] } - ] + } } } ] # 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 @@ -349,17 +508,31 @@ Object.Widget { Object.Base.input_audio_format [ { in_rate $JACK_RATE + in_channels $SDW_JACK_CAPTURE_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))]" + IncludeByKey.SDW_JACK_CAPTURE_CH { + "1" { + in_ch_map $CHANNEL_MAP_MONO + in_ch_cfg $CHANNEL_CONFIG_MONO + } + } } ] Object.Base.output_audio_format [ { out_rate $JACK_RATE + out_channels $SDW_JACK_CAPTURE_CH out_bit_depth 32 out_valid_bit_depth 32 + IncludeByKey.SDW_JACK_CAPTURE_CH { + "1" { + out_ch_map $CHANNEL_MAP_MONO + out_ch_cfg $CHANNEL_CONFIG_MONO + } + } } ] } @@ -383,6 +556,7 @@ Object.Widget { Object.Base.input_audio_format [ { in_rate $JACK_RATE + in_channels $SDW_JACK_CAPTURE_CH in_bit_depth 32 in_valid_bit_depth 32 } @@ -390,6 +564,7 @@ Object.Widget { Object.Base.output_audio_format [ { out_rate $JACK_RATE + out_channels $SDW_JACK_CAPTURE_CH out_bit_depth 32 out_valid_bit_depth 32 } @@ -401,6 +576,30 @@ 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_channels $SDW_JACK_CAPTURE_CH + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + Object.Base.output_audio_format [ + { + out_rate $JACK_RATE + out_channels $SDW_JACK_CAPTURE_CH + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + } + ] } } } @@ -418,7 +617,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 +648,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 +678,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 +694,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/platform/intel/uaol-generic.conf b/tools/topology/topology2/platform/intel/uaol-generic.conf new file mode 100644 index 000000000000..b1e56ad90b21 --- /dev/null +++ b/tools/topology/topology2/platform/intel/uaol-generic.conf @@ -0,0 +1,204 @@ +# USB Audio Offload Link (UAOL) function. +# +# The offloaded USB endpoint is driven by the UAOL gateway in the firmware: +# +# PCM ---------------> gain ---> mixin ---+ +# | +# Deepbuffer PCM ----> gain ---> mixin ---+--> mixout --> gain --> UAOL0-Play +# +# PCM <------------ module-copier <--- eqiir <---------------- UAOL0-Cap +# +# The capture path carries no mixer, as the number of channels follows the USB +# device and a mixer only handles a fixed channel count. +# +# What the endpoint can be run at is decided by the USB device, which is only +# known once one is plugged in, so every widget of both paths carries the whole +# set of formats a USB device may ask for: S16_LE, S24_LE and S32_LE, 1 to 8 +# channels and the standard rates from 8 to 192 kHz, which is as far as UAOL +# goes. The kernel narrows the PCM down to what the connected device actually +# supports. +# +# The deep buffer PCM is left at 48 kHz stereo, as it comes from the shared +# platform/intel/deep-buffer.conf. It can only join the mixer while the normal +# playback PCM runs at that format, or is not running at all. + +# Deep buffer playback, included when the DMA buffer is 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" +} + +Object.Dai.UAOL [ + { + name $UAOL_PB_DAI_NAME + dai_index $UAOL_DAI_INDEX + id $UAOL_PB_BE_ID + default_hw_conf_id 0 + Object.Base.hw_config.1 { + name "UAOL0-Play" + } + direction playback + } + { + name $UAOL_CP_DAI_NAME + dai_index $UAOL_DAI_INDEX + id $UAOL_CP_BE_ID + default_hw_conf_id 0 + Object.Base.hw_config.1 { + name "UAOL0-Cap" + } + direction capture + } +] + +Object.Pipeline { + host-copier-gain-mixin-playback [ + { + index $UAOL_PB_HOST_PIPELINE_ID + + Object.Widget.host-copier.1 { + stream_name $UAOL_PLAYBACK_PCM + pcm_id $UAOL_PCM_ID + <include/formats/uaol_input_audio_formats.conf> + <include/formats/uaol_output_audio_formats_s32.conf> + } + Object.Widget.gain.1 { + Object.Control.mixer.1 { + name 'Pre Mixer $UAOL_PCM_NAME Playback Volume' + } + <include/formats/uaol_input_audio_formats_s32.conf> + <include/formats/uaol_output_audio_formats_s32.conf> + } + Object.Widget.mixin.1 { + <include/formats/uaol_input_audio_formats_s32.conf> + <include/formats/uaol_output_audio_formats_s32.conf> + } + } + ] + + mixout-gain-dai-copier-playback [ + { + index $UAOL_PB_DAI_PIPELINE_ID + + Object.Widget.mixout.1 { + <include/formats/uaol_input_audio_formats_s32.conf> + <include/formats/uaol_output_audio_formats_s32.conf> + } + Object.Widget.dai-copier.1 { + node_type $ALH_UAOL_STREAM_LINK_OUTPUT_CLASS + stream_name $UAOL_PB_DAI_NAME + dai_type "UAOL" + copier_type "UAOL" + <include/formats/uaol_input_audio_formats_s32.conf> + <include/formats/uaol_output_audio_formats.conf> + } + Object.Widget.gain.1 { + Object.Control.mixer.1 { + name 'Post Mixer $UAOL_PCM_NAME Playback Volume' + } + <include/formats/uaol_input_audio_formats_s32.conf> + <include/formats/uaol_output_audio_formats_s32.conf> + } + } + ] + + host-gateway-capture [ + { + index $UAOL_CP_HOST_PIPELINE_ID + + Object.Widget.host-copier.1 { + stream_name $UAOL_CAPTURE_PCM + pcm_id $UAOL_PCM_ID + <include/formats/uaol_input_audio_formats_s32.conf> + <include/formats/uaol_output_audio_formats.conf> + } + } + ] + + dai-copier-eqiir-module-copier-capture [ + { + index $UAOL_CP_DAI_PIPELINE_ID + + Object.Widget.dai-copier.1 { + node_type $ALH_UAOL_STREAM_LINK_INPUT_CLASS + stream_name $UAOL_CP_DAI_NAME + dai_type "UAOL" + type dai_out + copier_type "UAOL" + num_output_pins 1 + <include/formats/uaol_input_audio_formats.conf> + <include/formats/uaol_output_audio_formats_s32.conf> + } + + # The coefficients are designed for 48 kHz, so the corner + # frequency scales with the rate the device is run at + Object.Widget.eqiir.1 { + Object.Control.bytes.1 { + name '$UAOL_PCM_NAME Capture IIR Eq' + <include/components/eqiir/highpass_40hz_0db_48khz.conf> + } + <include/formats/uaol_input_audio_formats_s32.conf> + <include/formats/uaol_output_audio_formats_s32.conf> + } + + Object.Widget.module-copier.2 { + <include/formats/uaol_input_audio_formats_s32.conf> + <include/formats/uaol_output_audio_formats_s32.conf> + } + } + ] +} + +Object.PCM.pcm [ + { + name $UAOL_PCM_NAME + id $UAOL_PCM_ID + direction "duplex" + Object.Base.fe_dai.1 { + name $UAOL_PCM_NAME + } + Object.PCM.pcm_caps.1 { + direction "playback" + name $UAOL_PLAYBACK_PCM + formats 'S16_LE,S24_LE,S32_LE' + channels_min $UAOL_CHANNELS_MIN + channels_max $UAOL_CHANNELS_MAX + rates $UAOL_RATES + rate_min $UAOL_RATE_MIN + rate_max $UAOL_RATE_MAX + } + Object.PCM.pcm_caps.2 { + direction "capture" + name $UAOL_CAPTURE_PCM + formats 'S16_LE,S24_LE,S32_LE' + channels_min $UAOL_CHANNELS_MIN + channels_max $UAOL_CHANNELS_MAX + rates $UAOL_RATES + rate_min $UAOL_RATE_MIN + rate_max $UAOL_RATE_MAX + } + } +] + +# top-level pipeline connections +Object.Base.route [ + { + source "host-copier.$UAOL_PCM_ID.playback" + sink "gain.$UAOL_PB_HOST_PIPELINE_ID.1" + } + { + source "mixin.$UAOL_PB_HOST_PIPELINE_ID.1" + sink "mixout.$UAOL_PB_DAI_PIPELINE_ID.1" + } + { + source "gain.$UAOL_PB_DAI_PIPELINE_ID.1" + sink "dai-copier.UAOL.$UAOL_PB_DAI_NAME.playback" + } + { + source "dai-copier.UAOL.$UAOL_CP_DAI_NAME.capture" + sink "eqiir.$UAOL_CP_DAI_PIPELINE_ID.1" + } + { + source "module-copier.$UAOL_CP_DAI_PIPELINE_ID.2" + sink "host-copier.$UAOL_PCM_ID.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..129a5fc0ceed 100644 --- a/tools/topology/topology2/production/tplg-targets-ace2.cmake +++ b/tools/topology/topology2/production/tplg-targets-ace2.cmake @@ -27,6 +27,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-lnl-rt713-l0-rt1318-l1\;PLATFORM=lnl,NUM_SDW_AMP_LINKS=1,\ +SDW_SPK_STREAM=SDW1-Playback,SDW_AMP_FEEDBACK=false,\ +HDMI1_ID=4,HDMI2_ID=5,HDMI3_ID=6" + "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 +41,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..06ffc7eb11a7 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,33 @@ 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-rt721-l3-rt1320-l3-4ch-ssp2-bt-dts-ctc\;PLATFORM=ptl,NUM_SDW_AMP_CTC_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-dts-ctc.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" @@ -49,6 +108,10 @@ SDW_JACK_OUT_STREAM=Playback-SimpleJack,SDW_JACK_IN_STREAM=Capture-SimpleJack,\ PREPROCESS_PLUGINS=nhlt,NHLT_BIN=nhlt-sof-ptl-rt722-4ch.bin,DMIC0_ENHANCED_CAPTURE=true,\ EFX_DMIC0_TDFB_PARAMS=line4_pass,EFX_DMIC0_DRC_PARAMS=dmic_default" +# DMIC WoV feature topology; platform-agnostic, shared across PTL/WCL +"dmic-wov-feature\;sof-dmic-4ch-wov\;PLATFORM=none,NUM_DMICS=4,\ +DMIC0_ID=5,DMIC1_ID=6,DMIC1_ENABLE=false,DMIC1_RATE=16000,INCLUDE_WOV=true" + "cavs-sdw\;sof-ptl-rt722-2ch\;PLATFORM=ptl,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,\ @@ -56,6 +119,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 +150,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 +171,59 @@ 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-2ch\;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-2ch.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-ctc\;PLATFORM=ptl,SDW_DMIC=1,NUM_SDW_AMP_CTC_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-dax-ctc\;PLATFORM=ptl,SDW_DMIC=1,NUM_SDW_AMP_CTC_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,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" + +"cavs-sdw\;sof-ptl-cs42l43-agg-l3-cs35l56-l2-4ch-ctc\;PLATFORM=ptl,NUM_SDW_AMP_CTC_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-ctc.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 +242,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..216a637d8b50 100644 --- a/tools/topology/topology2/production/tplg-targets-sdca-generic.cmake +++ b/tools/topology/topology2/production/tplg-targets-sdca-generic.cmake @@ -12,14 +12,29 @@ 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" + +#default NUM_DMICS=2 "cavs-sdw\;sof-sdca-mic-id4\;SDW_JACK=false,SDW_DMIC=1,NUM_HDMIS=0,\ SDW_DMIC_STREAM=Capture-SmartMic" +"cavs-sdw\;sof-sdca-mic-4ch-id4\;SDW_JACK=false,SDW_DMIC=1,NUM_HDMIS=0,\ +SDW_DMIC_STREAM=Capture-SmartMic,SDW_DMIC_PCM_CHANNELS=4,TDFB_DMIC_PCM_CHANNELS=4,\ +EFX_MIC_TDFB_PARAMS=line4_pass" + "cavs-sdw\;sof-hdmi-pcm5-id2\;SDW_JACK=false,HDMI1_ID=2,HDMI2_ID=3,HDMI3_ID=4" "cavs-sdw\;sof-hdmi-pcm5-id3\;SDW_JACK=false,HDMI1_ID=3,HDMI2_ID=4,HDMI3_ID=5" "cavs-sdw\;sof-hdmi-pcm5-id4\;SDW_JACK=false,HDMI1_ID=4,HDMI2_ID=5,HDMI3_ID=6" @@ -27,4 +42,41 @@ 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" +# USB Audio Offload Link function topology. The UAOL backend is the last one +# sof_sdw creates, so its dai link ID depends on the composition of the board: +# it follows the SoundWire links, the PCH DMIC pair, the three HDMI links and +# the BT link. Each direction is a separate link, so two consecutive IDs are +# taken. The range below covers a jack and amplifier board without DMIC and BT +# (6) up to a jack, amplifier with feedback, PCH DMIC and BT board (10). +# Add a variant here for every further ID that is needed. +"cavs-uaol\;sof-uaol-id6\;UAOL_PB_BE_ID=6,UAOL_CP_BE_ID=7" +"cavs-uaol\;sof-uaol-id7\;UAOL_PB_BE_ID=7,UAOL_CP_BE_ID=8" +"cavs-uaol\;sof-uaol-id8\;UAOL_PB_BE_ID=8,UAOL_CP_BE_ID=9" +"cavs-uaol\;sof-uaol-id9\;UAOL_PB_BE_ID=9,UAOL_CP_BE_ID=10" +"cavs-uaol\;sof-uaol-id10\;UAOL_PB_BE_ID=10,UAOL_CP_BE_ID=11" + +# 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/tplg_parser/graph.c b/tools/tplg_parser/graph.c index 7bc9f33449bd..f92b316893a9 100644 --- a/tools/tplg_parser/graph.c +++ b/tools/tplg_parser/graph.c @@ -23,6 +23,7 @@ /* load pipeline graph DAPM widget*/ int tplg_create_graph(struct tplg_context *ctx, int count, int pipeline_id, struct tplg_comp_info *temp_comp_list, char *pipeline_string, + size_t pipeline_string_size, struct sof_ipc_pipe_comp_connect *connection, int route_num) { @@ -64,13 +65,21 @@ int tplg_create_graph(struct tplg_context *ctx, int count, int pipeline_id, printf("loading route %s -> %s\n", source, sink); - strcat(pipeline_string, graph_elem->source); - strcat(pipeline_string, "->"); - - if (route_num == (count - 1)) { - strcat(pipeline_string, graph_elem->sink); - strcat(pipeline_string, "\n"); - } + size_t cur_len = strnlen(pipeline_string, pipeline_string_size); + size_t remaining = pipeline_string_size > cur_len ? + pipeline_string_size - cur_len : 0; + int written; + + if (route_num == (count - 1)) + written = snprintf(pipeline_string + cur_len, remaining, + "%s->%s\n", graph_elem->source, + graph_elem->sink); + else + written = snprintf(pipeline_string + cur_len, remaining, + "%s->", graph_elem->source); + + if (written < 0 || (size_t)written >= remaining) + fprintf(stderr, "warning: pipeline string truncated\n"); return 0; } diff --git a/tools/tplg_parser/include/tplg_parser/topology.h b/tools/tplg_parser/include/tplg_parser/topology.h index 04988d688082..b37d14444d14 100644 --- a/tools/tplg_parser/include/tplg_parser/topology.h +++ b/tools/tplg_parser/include/tplg_parser/topology.h @@ -13,6 +13,8 @@ #include <stdbool.h> #include <stdarg.h> #include <stddef.h> +#include <assert.h> +#include <stdio.h> #include <ipc/dai.h> #include <ipc/topology.h> #include <ipc/stream.h> @@ -194,43 +196,65 @@ struct tplg_context { #define tplg_get(ctx) ((void *)(ctx->tplg_base + ctx->tplg_offset)) +#define tplg_check_bounds(ctx, advance) \ + do { \ + if ((long)(advance) < 0 || \ + (ctx)->tplg_offset + (long)(advance) > (long)(ctx)->tplg_size) { \ + printf("%s %d topology offset %ld + %ld > size %zu\n", \ + __func__, __LINE__, (ctx)->tplg_offset, \ + (long)(advance), (ctx)->tplg_size); \ + exit(1); \ + } \ + } while (0) + #define tplg_get_hdr(ctx) \ ({struct snd_soc_tplg_hdr *ptr; \ - ptr = (struct snd_soc_tplg_hdr *)(ctx->tplg_base + ctx->tplg_offset); \ + tplg_check_bounds(ctx, sizeof(*ptr)); \ + ptr = (struct snd_soc_tplg_hdr *)((ctx)->tplg_base + (ctx)->tplg_offset); \ if (ptr->size != sizeof(*ptr)) { \ printf("%s %d hdr size mismatch 0x%x:0x%zx at offset %ld\n", \ __func__, __LINE__, ptr->size, sizeof(*ptr), \ - ctx->tplg_offset); assert(0); \ + (ctx)->tplg_offset); assert(0); \ } \ ctx->tplg_offset += sizeof(*ptr); (void *)ptr; }) #define tplg_skip_hdr_payload(ctx) \ ({struct snd_soc_tplg_hdr *ptr; \ - ptr = (struct snd_soc_tplg_hdr *)(ctx->tplg_base + ctx->tplg_offset); \ - ctx->tplg_offset += hdr->payload_size; (void *)ptr; }) + tplg_check_bounds(ctx, hdr->payload_size); \ + ptr = (struct snd_soc_tplg_hdr *)((ctx)->tplg_base + (ctx)->tplg_offset); \ + (ctx)->tplg_offset += hdr->payload_size; (void *)ptr; }) #define tplg_get_object(ctx, obj) \ - ({void *ptr; ptr = ctx->tplg_base + ctx->tplg_offset; \ - ctx->tplg_offset += sizeof(*(obj)); ptr; }) + ({void *ptr; \ + tplg_check_bounds(ctx, sizeof(*(obj))); \ + ptr = (ctx)->tplg_base + (ctx)->tplg_offset; \ + (ctx)->tplg_offset += sizeof(*(obj)); ptr; }) #define tplg_get_object_priv(ctx, obj, priv_size) \ - ({void *ptr; ptr = ctx->tplg_base + ctx->tplg_offset; \ - ctx->tplg_offset += sizeof(*(obj)) + priv_size; ptr; }) + ({void *ptr; \ + tplg_check_bounds(ctx, sizeof(*(obj)) + (priv_size)); \ + ptr = (ctx)->tplg_base + (ctx)->tplg_offset; \ + (ctx)->tplg_offset += sizeof(*(obj)) + (priv_size); ptr; }) #define tplg_get_widget(ctx) \ ({struct snd_soc_tplg_dapm_widget *w; \ - w = (struct snd_soc_tplg_dapm_widget *)(ctx->tplg_base + ctx->tplg_offset); \ - ctx->tplg_offset += sizeof(*w) + w->priv.size; w; }) + tplg_check_bounds(ctx, sizeof(*w)); \ + w = (struct snd_soc_tplg_dapm_widget *)((ctx)->tplg_base + (ctx)->tplg_offset); \ + tplg_check_bounds(ctx, sizeof(*w) + w->priv.size); \ + (ctx)->tplg_offset += sizeof(*w) + w->priv.size; w; }) #define tplg_get_graph(ctx) \ ({struct snd_soc_tplg_dapm_graph_elem *w; \ - w = (struct snd_soc_tplg_dapm_graph_elem *)(ctx->tplg_base + ctx->tplg_offset); \ - ctx->tplg_offset += sizeof(*w); w; }) + tplg_check_bounds(ctx, sizeof(*w)); \ + w = (struct snd_soc_tplg_dapm_graph_elem *)((ctx)->tplg_base + (ctx)->tplg_offset); \ + (ctx)->tplg_offset += sizeof(*w); w; }) #define tplg_get_pcm(ctx) \ ({struct snd_soc_tplg_pcm *pcm; \ - pcm = (struct snd_soc_tplg_pcm *)(ctx->tplg_base + ctx->tplg_offset); \ - ctx->tplg_offset += sizeof(*pcm) + pcm->priv.size; pcm; }) + tplg_check_bounds(ctx, sizeof(*pcm)); \ + pcm = (struct snd_soc_tplg_pcm *)((ctx)->tplg_base + (ctx)->tplg_offset); \ + tplg_check_bounds(ctx, sizeof(*pcm) + pcm->priv.size); \ + (ctx)->tplg_offset += sizeof(*pcm) + pcm->priv.size; pcm; }) static inline int tplg_valid_widget(struct snd_soc_tplg_dapm_widget *widget) { @@ -314,6 +338,7 @@ int tplg_new_process(struct tplg_context *ctx, void *process, size_t process_siz int tplg_create_graph(struct tplg_context *ctx, int count, int pipeline_id, struct tplg_comp_info *temp_comp_list, char *pipeline_string, + size_t pipeline_string_size, struct sof_ipc_pipe_comp_connect *connection, int route_num); diff --git a/tools/tplg_parser/object.c b/tools/tplg_parser/object.c index 4592031c7c01..ddc31e96f896 100644 --- a/tools/tplg_parser/object.c +++ b/tools/tplg_parser/object.c @@ -62,6 +62,14 @@ int tplg_create_object(struct tplg_context *ctx, return -EINVAL; } + /* a zero-size array never advances the cursor below: bail out + * instead of looping forever on a malformed topology + */ + if (array->size == 0) { + fprintf(stderr, "error: load %s zero-size array\n", name); + return -EINVAL; + } + for (i = 0; i < ipc->num_groups; i++) { const struct sof_topology_token *tokens = ipc->grp[i].tokens; int num_tokens = ipc->grp[i].num_tokens; diff --git a/tools/tplg_parser/process.c b/tools/tplg_parser/process.c index 5f0d8cdddaf2..97f7f5b7022b 100644 --- a/tools/tplg_parser/process.c +++ b/tools/tplg_parser/process.c @@ -151,6 +151,11 @@ static int process_append_data3(void *_process_ipc, /* Size is process IPC plus private data minus ABI header */ bytes_ctl = (struct snd_soc_tplg_bytes_control *)ctl; + if (bytes_ctl->priv.size < sizeof(struct sof_abi_hdr)) { + fprintf(stderr, "error: process priv data too small: %u\n", + bytes_ctl->priv.size); + return -EINVAL; + } size = bytes_ctl->priv.size - sizeof(struct sof_abi_hdr); ipc_size = sizeof(struct sof_ipc_comp_process) + UUID_SIZE + process_ipc->size + size; @@ -184,6 +189,11 @@ static int process_append_data4(void *_process_ipc, /* Size is process IPC plus private data minus ABI header */ bytes_ctl = (struct snd_soc_tplg_bytes_control *)ctl; + if (bytes_ctl->priv.size < sizeof(struct sof_abi_hdr)) { + fprintf(stderr, "error: process priv data too small: %u\n", + bytes_ctl->priv.size); + return -EINVAL; + } size = bytes_ctl->priv.size - sizeof(struct sof_abi_hdr); /* validate if everything will fit */ 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/decode_ceps.m b/tools/tune/mfcc/decode_ceps.m deleted file mode 100644 index a63677fa3731..000000000000 --- a/tools/tune/mfcc/decode_ceps.m +++ /dev/null @@ -1,93 +0,0 @@ -% [ceps, t, n] = decode_ceps(fn, num_ceps, num_channels) -% -% Input -% fn - File with MFCC data in .raw or .wav format -% num_ceps - number of cepstral coefficients per frame -% num_channels - needed for .raw format, omit for .wav -% -% Outputs -% ceps - cepstral coefficients -% t - time vector for plotting -% n - ceps 1..num_ceps vector for plotting - -% SPDX-License-Identifier: BSD-3-Clause -% Copyright(c) 2022 Intel Corporation. All rights reserved. - -function [ceps, t, n] = decode_ceps(fn, num_ceps, num_channels) - -if nargin < 3 - num_channels = 1; -end - -% MFCC stream -fs = 16e3; -qformat = 7; -magic = [25443 28006]; % ASCII 'mfcc' as int16 - -% Load output data -[data, num_channels] = get_file(fn, num_channels); - -idx1 = find(data == magic(1)); -idx = []; -for i = 1:length(idx1) - if data(idx1(i) + 1) == magic(2) - idx = [idx idx1(i)]; - end -end - -if isempty(idx) - error('No magic value markers found from stream'); -end - -period_ceps = idx(2)-idx(1); -num_frames = length(idx); -t_ceps = period_ceps / num_channels / fs; -t = (0:num_frames -1) * t_ceps; -n = 1:num_ceps; - -ceps = zeros(num_ceps, num_frames); -for i = 1:num_frames - i1 = idx(i) + 2; - i2 = i1 + num_ceps - 1; - ceps(:,i) = data(i1:i2) / 2^qformat; -end - -figure; -surf(t, n, ceps, 'EdgeColor', 'none'); -colormap(jet); -view(45, 60) -tstr = sprintf('SOF MFCC cepstral coefficients (%s)', fn); -title(tstr, 'Interpreter', 'None'); -xlabel('Time (s)'); -ylabel('Cepstral coef #'); - -end - -function [data, num_channels] = get_file(fn, num_channels) - -[~, ~, ext] = fileparts(fn); - -switch lower(ext) - case '.raw' - fh = fopen(fn, 'r'); - data = fread(fh, 'int16'); - fclose(fh); - case '.wav' - tmp = audioread(fn, 'native'); - t = whos('tmp'); - if ~strcmp(t.class, 'int16'); - error('Only 16-bit wav file format is supported'); - end - s = size(tmp); - num_channels = s(2); - if num_channels > 1 - data = int16(zeros(prod(s), 1)); - for i = 1:num_channels - data(i:num_channels:end) = tmp(:, i); - end - end - otherwise - error('Unknown audio format'); -end - -end 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/tools/tune/mfcc/setup_mfcc.m b/tools/tune/mfcc/setup_mfcc.m deleted file mode 100644 index 9b27703e98bd..000000000000 --- a/tools/tune/mfcc/setup_mfcc.m +++ /dev/null @@ -1,210 +0,0 @@ -% setup_mfcc(cfg) -% -% 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 - -% SPDX-License-Identifier: BSD-3-Clause -% -% Copyright (c) 2018-2020, Intel Corporation. All rights reserved. - -function setup_mfcc(cfg) - -if nargin < 1 - cfg.blackman_coef = 0.42; - cfg.cepstral_lifter = 22.0; - cfg.channel = -1; % -1 expect mono, 0 left, 1 right ... - cfg.dither = 0.0; % no support - cfg.energy_floor = 1.0; - cfg.frame_length = 25.0; % ms - cfg.frame_shift = 10.0; % ms - cfg.high_freq = 0.0; % 0 is Nyquist - cfg.htk_compat = false; % no support - cfg.low_freq = 20.0; % Hz - cfg.num_ceps = 13; - cfg.min_duration = 0.0; % no support - cfg.norm = 'none'; % Use none or slaney - cfg.num_mel_bins = 23; - cfg.preemphasis_coefficient = 0; % disable - cfg.raw_energy = true; - cfg.remove_dc_offset = true; - cfg.round_to_power_of_two = true; % must be true - cfg.sample_frequency = 16000; - cfg.snip_edges = true; % must be true - cfg.subtract_mean = false; % must be false - cfg.use_energy = false; - cfg.vtln_high = -500.0; % no support - cfg.vtln_low = 100.0; % no support - cfg.vtln_warp = 1.0; % must be 1.0 (vocal tract length normalization) - cfg.window_type = 'hamming'; - 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 -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); - -end - -function export_mfcc_setup(cfg) - -%% Use blob tool from EQ -addpath('../common'); - -%% Blob size, size plus reserved(8) + current parameters -nbytes_data = 104; - -%% Little endian -sh32 = [0 -8 -16 -24]; -sh16 = [0 -8]; - -%% 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, 'MFCC 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 MFCC 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 - -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 = 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 -v = q_convert(cfg.blackman_coef, 15); [b8, j] = add_w16b(v, b8, j); -v = q_convert(cfg.cepstral_lifter, 9); [b8, j] = add_w16b(v, b8, j); -v = cfg.channel; [b8, j] = add_w16b(v, b8, j); -v = cfg.dither; [b8, j] = add_w16b(v, b8, j); -v = round(cfg.frame_length/1000 * cfg.sample_frequency); [b8, j] = add_w16b(v, b8, j); -v = round(cfg.frame_shift/1000 * cfg.sample_frequency); [b8, j] = add_w16b(v, b8, j); -v = q_convert(cfg.high_freq, 0); [b8, j] = add_w16b(v, b8, j); -v = q_convert(cfg.low_freq, 0); [b8, j] = add_w16b(v, b8, j); -v = cfg.num_ceps; [b8, j] = add_w16b(v, b8, j); -v = cfg.num_mel_bins; [b8, j] = add_w16b(v, b8, j); -v = q_convert(cfg.preemphasis_coefficient, 15); [b8, j] = add_w16b(v, b8, j); -v = q_convert(cfg.top_db, 7); [b8, j] = add_w16b(v, b8, j); -v = 0; [b8, j] = add_w16b(v, b8, j); % vtln_high Qx.y TBD -v = 0; [b8, j] = add_w16b(v, b8, j); % vtln_low Qx.y TBD -v = 0; [b8, j] = add_w16b(v, b8, j); % vtln_warp Qx.y TBD -v = cfg.htk_compat; [b8, j] = add_w8b(v, b8, j); % bool -v = cfg.raw_energy; [b8, j] = add_w8b(v, b8, j); % bool -v = cfg.remove_dc_offset; [b8, j] = add_w8b(v, b8, j); % bool -v = cfg.round_to_power_of_two; [b8, j] = add_w8b(v, b8, j); % bool -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 - -%% Export -switch cfg.tplg_ver - case 1 - sof_tplg_write(cfg.tplg_fn, b8, "DEF_MFCC_PRIV", ... - "Exported with script setup_mfcc.m", ... - "cd tools/tune/mfcc; octave setup_mfcc.m"); - case 2 - sof_tplg2_write(cfg.tplg_fn, b8, "mfcc_config", ... - "Exported MFCC configuration", ... - "cd tools/tune/mfcc; octave setup_mfcc.m"); - otherwise - error("Illegal cfg.tplg_ver, use 1 for topology v1 or 2 topology v2."); -end - -rmpath('../common'); - -end - -%% Helper functions - -function n = get_window(cfg) - switch lower(cfg.window_type) - case 'rectangular' - n = 0; - case 'blackman' - n = 1; - case 'hamming' - n = 2; - case 'hann' - n = 3; - case 'povey' - n = 4; - otherwise - error('Unknown window type'); - end -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/uuid-registry.txt b/uuid-registry.txt index e581be7b607a..04c4a3881b21 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 @@ -130,8 +133,10 @@ f36BF24B-9AAF-83f4-8677E072E8AEADB7 notification_pool 127f4eec-23fa-11f0-a4a6bf0cd6b4583b nxp_eap 376b5e44-9c82-4ec2-bc8310ea101af88f passthrough 64a794f0-55d3-4bca-9d5b7b588badd037 passthru_smart_amp +09fbcb7a-a9c5-4a57-84344440e598ab24 phase_vocoder 4e934adb-b0ec-4d33-a086c1022f921321 pipe f11818eb-e92e-4082-82a3dc54c604ebb3 pipe_task +069b6fab-ae56-432f-8f4285ce63ea98bf pipe_trigger_task d7f6712d-131c-45a7-82ed6aa9dc2291ea pm_runtime 76cc9773-440c-4df9-95a872defe7796fc power 9d1fb66e-4ffb-497f-994b17719686596e probe @@ -160,6 +165,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 +175,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..a684deb8871a 100644 --- a/west.yml +++ b/west.yml @@ -43,7 +43,7 @@ manifest: - name: zephyr repo-path: zephyr - revision: 5bef4a9d6264761c10d739b345eb2f7ded719099 + revision: fbd71c30f7b9083de9826577edb57d360efc01ed remote: zephyrproject # Import some projects listed in zephyr/west.yml@revision diff --git a/zephyr/CMakeLists.txt b/zephyr/CMakeLists.txt index 3e0207b17a0d..c8f49c1f07cb 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,17 +496,60 @@ 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_ACP_7_X) + # Platform sources + zephyr_library_sources( + ${SOF_DRIVERS_PATH}/amd/acp_7_x/ipc.c + ${SOF_PLATFORM_PATH}/amd/acp_7_x/platform.c + ${SOF_PLATFORM_PATH}/amd/acp_7_x/lib/clk.c + ) + zephyr_library_sources(lib/dma.c) + zephyr_include_directories(${SOF_PLATFORM_PATH}/amd/acp_7_x/include) + zephyr_include_directories(${SOF_PLATFORM_PATH}/amd/common/include) + set(PLATFORM "acp_7_x") +endif() + if (CONFIG_SOC_FAMILY_MTK) set(PLATFORM "mtk") zephyr_library_sources( ${SOF_PLATFORM_PATH}/mtk/platform.c ${SOF_PLATFORM_PATH}/mtk/dai.c - ${SOF_DRIVERS_PATH}/generic/dummy-dma.c - ${SOF_SRC_PATH}/drivers/mediatek/afe/afe-memif.c - ${SOF_SRC_PATH}/drivers/mediatek/afe/afe-dai.c ${SOF_SRC_PATH}/drivers/mediatek/afe/afe-drv.c ) + + if (CONFIG_ZEPHYR_NATIVE_DRIVERS) + # Native Zephyr DMA/DAI path + zephyr_library_sources( + lib/dma.c + ${SOF_SRC_PATH}/drivers/mediatek/afe/zephyr_mtk_afe_memif.c + ${SOF_SRC_PATH}/drivers/mediatek/afe/zephyr_mtk_host_dma.c + ${SOF_SRC_PATH}/drivers/mediatek/afe/zephyr_mtk_dai.c + ) + else() + # Legacy DMA path + zephyr_library_sources( + ${SOF_DRIVERS_PATH}/generic/dummy-dma.c + ${SOF_SRC_PATH}/drivers/mediatek/afe/afe-memif.c + ${SOF_SRC_PATH}/drivers/mediatek/afe/afe-dai.c + ) + endif() endif() # Building for native_posix-based whole-OS host emulator @@ -466,14 +558,27 @@ zephyr_library_sources_ifdef(CONFIG_ZEPHYR_POSIX ${SOF_PLATFORM_PATH}/posix/dai.c ${SOF_PLATFORM_PATH}/posix/ipc.c ${SOF_PLATFORM_PATH}/posix/posix.c +) + +zephyr_library_sources_ifdef(CONFIG_ARCH_POSIX_LIBFUZZER ${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 +602,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 +620,22 @@ zephyr_library_sources_ifdef(CONFIG_SHELL sof_shell.c ) +zephyr_syscall_header(include/sof/lib/cpu.h) +zephyr_library_sources_ifdef(CONFIG_USERSPACE syscall/cpu.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_syscall_header(${SOF_SRC_PATH}/include/sof/ipc/ipc_reply.h) +zephyr_syscall_header(${SOF_SRC_PATH}/include/sof/schedule/ll_schedule_domain.h) +zephyr_syscall_header(${SOF_SRC_PATH}/include/ipc4/handler.h) +zephyr_syscall_header(include/rtos/alloc.h) +zephyr_library_sources_ifdef(CONFIG_SOF_USERSPACE_INTERFACE_ALLOC syscall/alloc.c) +zephyr_syscall_header(${SOF_SRC_PATH}/include/sof/lib/vregion.h) +zephyr_library_sources_ifdef(CONFIG_SOF_USERSPACE_INTERFACE_VREGION syscall/vregion.c) +zephyr_syscall_header(${SOF_SRC_PATH}/include/sof/lib/dai-zephyr.h) +zephyr_library_sources_ifdef(CONFIG_USERSPACE syscall/dai.c) + +zephyr_syscall_header(${SOF_SRC_PATH}/include/user/debug_stream_slot.h) + zephyr_library_link_libraries(SOF) target_link_libraries(SOF INTERFACE zephyr_interface) @@ -536,10 +659,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 +674,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..2d03a6d6f7ae 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,39 @@ 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_INTERFACE_ALLOC + bool "Enable SOF heap alloc interface to userspace threads" + depends on USERSPACE + help + Allow user-space threads to use sof_heap_alloc/sof_heap_free + as Zephyr system calls. + +config SOF_USERSPACE_INTERFACE_VREGION + bool "Enable SOF vregion interface to userspace threads" + depends on USERSPACE + depends on SOF_VREGIONS + help + Allow user-space threads to use vregion_alloc/vregion_free + and their variants as Zephyr system calls. + +config SOF_USERSPACE_LL + bool "Run Low-Latency pipelines in userspace threads" + depends on USERSPACE + select SOF_USERSPACE_INTERFACE_ALLOC + select SOF_USERSPACE_INTERFACE_DMA + select SOF_USERSPACE_INTERFACE_VREGION if SOF_VREGIONS + 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 @@ -24,10 +63,28 @@ config SOF_ZEPHYR_HEAP_CACHED Enable cached heap by mapping cached SOF memory zones to different Zephyr sys_heap objects and enable caching for non-shared zones. +config SOF_NATIVE_SIM_HOST_HEAP + bool "Use the host C library heap on native_sim" + depends on BOARD_NATIVE_SIM + default n + help + When enabled on the native_sim board, SOF heap allocations + (rmalloc/rzalloc/rballoc/rfree etc.) are routed through the + host C library via the Zephyr native simulator host trampolines + (nsi_host_malloc/nsi_host_free). This makes tools like Valgrind + effective at finding heap errors in SOF code. + + When disabled, the regular Zephyr sys_heap based implementation + is used, matching the behavior on real targets. This is the + default so that tooling relying on sys_heap (e.g. ASAN/fuzzer + setups) keeps working; enable it explicitly in memory-testing or + Valgrind-focused CI configurations. + 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 +95,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 +116,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 +150,62 @@ 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_ZEPHYR_SYS_USER_HEAP_SIZE + hex "Size of the shared LL user-space heap" + default 0x20000 + depends on SOF_USERSPACE_LL + help + The size of the shared heap used by the low-latency (LL) scheduler + user-space thread. This heap is shared across all pipelines running + on the LL thread and must be large enough to hold all host DMA + buffers, chain DMA data, SG elements, and module adapter buffers + for all simultaneously active pipelines. + +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 @@ -84,6 +214,14 @@ config ZEPHYR_NATIVE_DRIVERS dai-zephyr will be used instead of legacy xtos version. +config DMA_MTK_SOF_HOST_DMA + bool "MediaTek SOF host DMA driver" + default y if SOC_FAMILY_MTK && ZEPHYR_NATIVE_DRIVERS + help + Software-based DMA driver for MTK SOF host component. + MTK DSP can access host memory directly, so this driver + implements DMA transfers using memcpy + cache management. + config DMA_DOMAIN bool "Enable the usage of DMA domain." help @@ -131,9 +269,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 +291,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 @@ -167,4 +337,10 @@ config STACK_SIZE_IPC_TX help IPC sender work-queue thread stack size. Keep a power of 2. -endif +config SOF_IPC_USER_THREAD_STACK_SIZE + int "IPC user thread stack size" + default 8192 + depends on SOF_USERSPACE_LL + help + Stack size for the userspace IPC thread. + Keep a power of 2. 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..a24f80852538 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.3 has /opt/toolchains/zephyr-sdk-1.0.1 + # zephyr-lite:v0.29.3 is based on zephyr-build:v0.29.3 # 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.3 \ ./sof/scripts/sudo-cwd.sh "$@" } diff --git a/zephyr/edf_schedule.c b/zephyr/edf_schedule.c index 8aca007137e7..2a432ec8df68 100644 --- a/zephyr/edf_schedule.c +++ b/zephyr/edf_schedule.c @@ -5,6 +5,8 @@ // Author: Bartosz Kokoszko <bartoszx.kokoszko@linux.intel.com> #include <sof/audio/component.h> +#include <sof/lib/memory.h> +#include <rtos/alloc.h> #include <rtos/task.h> #include <stdint.h> #include <sof/schedule/edf_schedule.h> @@ -98,9 +100,11 @@ static struct scheduler_ops schedule_edf_ops = { .schedule_task_free = schedule_edf_task_free, }; -int scheduler_init_edf(void) +__cold int scheduler_init_edf(void) { - struct k_thread *thread = &edf_workq.thread; + k_tid_t thread; + + assert_can_be_cold(); scheduler_init(SOF_SCHEDULE_EDF, &schedule_edf_ops, NULL); @@ -109,10 +113,13 @@ int scheduler_init_edf(void) K_THREAD_STACK_SIZEOF(edf_workq_stack), CONFIG_EDF_THREAD_PRIORITY, NULL); + thread = k_work_queue_thread_get(&edf_workq); k_thread_suspend(thread); - k_thread_cpu_mask_clear(thread); - k_thread_cpu_mask_enable(thread, PLATFORM_PRIMARY_CORE_ID); + k_thread_heap_assign(thread, sof_sys_heap_get()); +#ifdef CONFIG_SCHED_CPU_MASK + k_thread_cpu_pin(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..b727d1e700fb 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,53 @@ void rfree(void *ptr); */ void l3_heap_save(void); +/* + * This is ugly to define the signatures twice, but this + * is required to support userspace builds that do not export alloc. + */ +#ifdef CONFIG_SOF_USERSPACE_INTERFACE_ALLOC +__syscall void *sof_heap_alloc(struct k_heap *heap, uint32_t flags, size_t bytes, + size_t alignment); +__syscall void sof_heap_free(struct k_heap *heap, void *addr); +#include <zephyr/syscalls/alloc.h> +#else +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); +#endif + +#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. * @@ -136,4 +162,71 @@ size_t get_shared_buffer_heap_size(void); #endif +#include <sof/lib/vregion.h> + +struct mod_alloc_ctx { + struct k_heap *heap; + struct vregion *vreg; +}; + +/** + * Allocate memory from a mod_alloc_ctx context. + * + * When the context has a vregion, allocates from the vregion interim + * partition. Coherent memory is used when SOF_MEM_FLAG_COHERENT is set + * in flags. Falls back to sof_heap_alloc() otherwise. + * + * @param ctx Allocation context (heap + optional vregion). + * @param flags Allocation flags (SOF_MEM_FLAG_*). + * @param size Size in bytes. + * @param alignment Required alignment in bytes. + * @return Pointer to allocated memory or NULL on failure. + */ +static inline void *sof_ctx_alloc(struct mod_alloc_ctx *ctx, uint32_t flags, + size_t size, size_t alignment) +{ + if (!ctx || !ctx->vreg) + return sof_heap_alloc(ctx ? ctx->heap : NULL, flags, size, alignment); + + if (flags & SOF_MEM_FLAG_COHERENT) + return vregion_alloc_coherent_align(ctx->vreg, size, alignment); + + return vregion_alloc_align(ctx->vreg, size, alignment); +} + +/** + * Allocate zero-initialized memory from a mod_alloc_ctx context. + * @param ctx Allocation context. + * @param flags Allocation flags (SOF_MEM_FLAG_*). + * @param size Size in bytes. + * @param alignment Required alignment in bytes. + * @return Pointer to allocated memory or NULL on failure. + */ +static inline void *sof_ctx_zalloc(struct mod_alloc_ctx *ctx, uint32_t flags, + size_t size, size_t alignment) +{ + void *ptr = sof_ctx_alloc(ctx, flags, size, alignment); + + if (ptr) + memset(ptr, 0, size); + + return ptr; +} + +/** + * Free memory allocated from a mod_alloc_ctx context. + * @param ctx Allocation context. + * @param ptr Pointer to free. + */ +static inline void sof_ctx_free(struct mod_alloc_ctx *ctx, void *ptr) +{ + if (!ptr) + return; + + if (ctx && ctx->vreg) + vregion_free(ctx->vreg, ptr); + else + sof_heap_free(ctx ? ctx->heap : NULL, ptr); +} + #endif /* __ZEPHYR_RTOS_ALLOC_H__ */ diff --git a/zephyr/include/rtos/idc.h b/zephyr/include/rtos/idc.h index a8616d9e2a11..3e30250344e2 100644 --- a/zephyr/include/rtos/idc.h +++ b/zephyr/include/rtos/idc.h @@ -160,7 +160,7 @@ struct idc_msg { uint32_t extension; /**< extension value */ uint32_t core; /**< core id */ uint32_t size; /**< payload size in bytes */ - void *payload; /**< pointer to payload data */ + const void *payload; /**< pointer to payload data */ }; /** \brief IDC data. */ 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..efe0c1e6acab 100644 --- a/zephyr/include/rtos/sof.h +++ b/zephyr/include/rtos/sof.h @@ -46,8 +46,10 @@ struct sof { int argc; char **argv; +#ifndef CONFIG_SOF_USERSPACE_LL /* ipc */ struct ipc *ipc; +#endif /* system agent */ struct sa *sa; @@ -111,11 +113,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/task.h b/zephyr/include/rtos/task.h index 9fcc202c1e2e..82fedcfff9fa 100644 --- a/zephyr/include/rtos/task.h +++ b/zephyr/include/rtos/task.h @@ -17,6 +17,7 @@ struct comp_dev; struct sof; +struct schedule_data; /** \brief Predefined LL task priorities. */ #define SOF_TASK_PRI_HIGH 0 /* priority level 0 - high */ @@ -56,9 +57,10 @@ struct task { uint64_t start; /**< start time in [ms] since now (LL only) */ const struct sof_uuid_entry *uid; /**< Uuid */ uint16_t type; /**< type of the task (LL or EDF) */ - uint16_t priority; /**< priority of the task (used by LL) */ + int16_t priority; /**< priority of the task (used by LL); lower runs first */ uint16_t core; /**< execution core */ uint16_t flags; /**< custom flags */ + struct schedule_data *sch; /**< scheduler bound to task */ enum task_state state; /**< current state */ void *data; /**< custom data passed to all ops */ struct list_item list; /**< used by schedulers to hold tasks */ diff --git a/zephyr/include/rtos/userspace_helper.h b/zephyr/include/rtos/userspace_helper.h index 254c8a2f96f1..20fb7bd40876 100644 --- a/zephyr/include/rtos/userspace_helper.h +++ b/zephyr/include/rtos/userspace_helper.h @@ -12,25 +12,37 @@ #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 +#define APP_SYSUSER_BSS +#define APP_SYSUSER_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 +50,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 +94,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_cache_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/cpu.h b/zephyr/include/sof/lib/cpu.h index c23405e85121..a47b93898973 100644 --- a/zephyr/include/sof/lib/cpu.h +++ b/zephyr/include/sof/lib/cpu.h @@ -40,13 +40,32 @@ void cpu_notify_state_exit(enum pm_state state); #endif /* CONFIG_PM */ +/* + * cpu_get_id() is exposed as a Zephyr system call so that user-mode + * threads (e.g. user-space LL pipelines) can query the current core + * id. The underlying arch_proc_id() reads a privileged special + * register (e.g. Xtensa PRID) which would fault if executed directly + * from user mode. In supervisor context the generated wrapper inlines + * the z_impl_cpu_get_id() body, so there is no overhead there. + */ +#if defined(CONFIG_SOF_FULL_ZEPHYR_APPLICATION) +__syscall int cpu_get_id(void); +#endif + /* let the compiler optimise when in single core mode */ #if CONFIG_MULTICORE && CONFIG_SMP +#if defined(CONFIG_SOF_FULL_ZEPHYR_APPLICATION) +static inline int z_impl_cpu_get_id(void) +{ + return arch_proc_id(); +} +#else static inline int cpu_get_id(void) { return arch_proc_id(); } +#endif static inline bool cpu_is_primary(int id) { @@ -73,7 +92,11 @@ int cpu_restore_secondary_cores(void); int cpu_secondary_cores_prepare_d0ix(void); #else +#if defined(CONFIG_SOF_FULL_ZEPHYR_APPLICATION) +static inline int z_impl_cpu_get_id(void) { return 0; }; +#else static inline int cpu_get_id(void) { return 0; }; +#endif static inline bool cpu_is_primary(int id) { return 1; }; @@ -93,6 +116,10 @@ static inline int cpu_secondary_cores_prepare_d0ix(void) { return 0; }; #endif /* CONFIG_MULTICORE && CONFIG_SMP */ +#if defined(CONFIG_SOF_FULL_ZEPHYR_APPLICATION) +#include <zephyr/syscalls/cpu.h> +#endif + #endif #endif /* __SOF_LIB_CPU_H__ */ diff --git a/zephyr/include/sof/lib/dma.h b/zephyr/include/sof/lib/dma.h index 3c7f56d7b5f5..5dcc8455579b 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 @@ -80,6 +81,7 @@ struct comp_dev; #define SOF_DMA_CAP_HS_VIRTUAL BIT(7) /**< HS VIRTUAL DMA */ #define SOF_DMA_CAP_HS BIT(8) /**< HS DMA */ #define SOF_DMA_CAP_SW BIT(9) /**< SW DMA */ +#define SOF_DMA_CAP_TDM BIT(10) /**< connectable to ACP TDM I2S */ /* DMA dev type bitmasks used to define the type of DMA */ @@ -99,6 +101,7 @@ struct comp_dev; #define SOF_DMA_DEV_HS BIT(13) /**< connectable to ACP HS I2S */ #define SOF_DMA_DEV_MICFIL BIT(14) /**< connectable to MICFIL fifo */ #define SOF_DMA_DEV_SW BIT(15) /**< connectable to ACP SW */ +#define SOF_DMA_DEV_TDM BIT(16) /**< connectable to ACP TDM I2S */ /* DMA access privilege flag */ #define SOF_DMA_ACCESS_EXCLUSIVE 1 @@ -199,7 +202,9 @@ struct dma_plat_data { }; #ifdef CONFIG_ZEPHYR_NATIVE_DRIVERS +struct k_heap; struct sof_dma { + struct k_heap *heap; #else struct dma { #endif @@ -254,10 +259,11 @@ struct dma_copy { #endif }; -struct audio_stream; -typedef int (*dma_process_func)(const struct audio_stream *source, - uint32_t ioffset, struct audio_stream *sink, - uint32_t ooffset, uint32_t source_samples, uint32_t chmap); +struct cir_buf_source; +struct cir_buf_sink; +typedef int (*dma_process_func)(const struct cir_buf_source *source, + uint32_t src_channels, struct cir_buf_sink *sink, + uint32_t sink_channels, size_t source_samples, uint32_t chmap); /** * \brief API to initialize a platform DMA controllers. @@ -266,24 +272,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 +297,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..2b5a225b7067 100644 --- a/zephyr/include/sof/lib/memory.h +++ b/zephyr/include/sof/lib/memory.h @@ -7,6 +7,7 @@ #define __SOF_LIB_MEMORY_H__ #include <platform/lib/memory.h> +#include <stdbool.h> #ifndef __cold #define __cold @@ -16,15 +17,15 @@ #define __cold_rodata #endif -#if CONFIG_COLD_STORE_EXECUTE_DEBUG -#include <rtos/panic.h> - #ifdef __ZEPHYR__ bool ll_sch_is_current(void); #else #define ll_sch_is_current() false #endif +#if CONFIG_COLD_STORE_EXECUTE_DEBUG +#include <rtos/panic.h> + void dbg_path_hot_start_watching(void); void dbg_path_hot_stop_watching(void); void dbg_path_hot_confirm(void); @@ -32,6 +33,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..621ac0a83749 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 */ @@ -39,7 +37,7 @@ struct vmh_heap *virtual_buffers_heap; #include <zephyr/init.h> #include <zephyr/kernel.h> #include <zephyr/pm/policy.h> -#include <version.h> +#include <zephyr/version.h> #include <zephyr/sys/__assert.h> #include <zephyr/cache.h> @@ -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,19 +149,29 @@ 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) +#if !defined(CONFIG_SOF_NATIVE_SIM_HOST_HEAP) +/** + * 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)); } +#endif + +#if CONFIG_SOF_USERSPACE_USE_SHARED_HEAP +static struct k_heap shared_buffer_heap; /** * Returns the start of HPSRAM Shared memory heap. @@ -174,7 +191,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 +225,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 +240,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 +286,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 +314,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 +342,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 +378,22 @@ 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 +} + +#if !defined(CONFIG_SOF_NATIVE_SIM_HOST_HEAP) static void *heap_alloc_aligned(struct k_heap *h, size_t min_align, size_t bytes) { k_spinlock_key_t key; @@ -376,6 +402,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 +434,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 +450,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)); @@ -437,9 +463,135 @@ static void heap_free(struct k_heap *h, void *mem) k_spin_unlock(&h->lock, key); } +#endif + + +#if defined(CONFIG_SOF_NATIVE_SIM_HOST_HEAP) +#include <nsi_host_trampolines.h> +#include <string.h> + +void *rmalloc_align(uint32_t flags, size_t bytes, uint32_t alignment) +{ + void *ptr; + void *raw; + if (bytes > 16 * 1024 * 1024) { + tr_err(&zephyr_tr, "rmalloc_align: requested %zu bytes exceeds 16MB limit", bytes); + return NULL; + } + + if (alignment < sizeof(void *)) + alignment = sizeof(void *); + + /* the mask-based round-up below is only valid for a power-of-two + * alignment, so round any other value up to the next power of two + */ + if (alignment & (alignment - 1)) { + size_t pot = sizeof(void *); + + while (pot < alignment) + pot <<= 1; + alignment = pot; + } + + /* bytes is capped at 16MB above and this is a 64-bit host build, so + * the sum below cannot overflow size_t + */ + raw = nsi_host_malloc(bytes + alignment + sizeof(void *)); + if (!raw) + return NULL; + + ptr = (void *)(((uintptr_t)raw + sizeof(void *) + alignment - 1) & ~(alignment - 1)); + ((void **)ptr)[-1] = raw; + + return ptr; +} void *rmalloc(uint32_t flags, size_t bytes) +{ + return rmalloc_align(flags, bytes, 0); +} + +void *rbrealloc_align(void *ptr, uint32_t flags, size_t bytes, + size_t old_bytes, uint32_t alignment) +{ + void *new_ptr; + + if (!ptr) + return rmalloc_align(flags, bytes, alignment); + + if (!bytes) { + void *raw = ((void **)ptr)[-1]; + + nsi_host_free(raw); + return NULL; + } + + new_ptr = rmalloc_align(flags, bytes, alignment); + if (!new_ptr) + return NULL; + + if (!(flags & SOF_MEM_FLAG_NO_COPY)) + memcpy_s(new_ptr, bytes, ptr, MIN(bytes, old_bytes)); + + void *raw_old = ((void **)ptr)[-1]; + + nsi_host_free(raw_old); + + return new_ptr; +} + +void *rzalloc(uint32_t flags, size_t bytes) +{ + void *ptr = rmalloc_align(flags, bytes, 0); + + if (ptr) + memset(ptr, 0, bytes); + return ptr; +} + +void *rballoc_align(uint32_t flags, size_t bytes, uint32_t align) +{ + return rmalloc_align(flags, bytes, align); +} + +void rfree(void *ptr) +{ + if (!ptr) + return; + + void *raw = ((void **)ptr)[-1]; + + nsi_host_free(raw); +} + +void *z_impl_sof_heap_alloc(struct k_heap *heap, uint32_t flags, size_t bytes, + size_t alignment) +{ + return rmalloc_align(flags, bytes, alignment); +} + +void z_impl_sof_heap_free(struct k_heap *heap, void *addr) +{ + rfree(addr); +} + +#ifndef CONFIG_SOF_USERSPACE_INTERFACE_ALLOC +void *sof_heap_alloc(struct k_heap *heap, uint32_t flags, size_t bytes, + size_t alignment) +{ + return z_impl_sof_heap_alloc(heap, flags, bytes, alignment); +} + +void sof_heap_free(struct k_heap *heap, void *addr) +{ + z_impl_sof_heap_free(heap, addr); +} +#endif /* CONFIG_SOF_USERSPACE_INTERFACE_ALLOC */ + +#else + +void *rmalloc_align(uint32_t flags, size_t bytes, uint32_t alignment) { void *ptr; struct k_heap *heap; @@ -453,13 +605,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 +620,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 +686,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 +699,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 +729,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 +742,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 +753,99 @@ 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 *z_impl_sof_heap_alloc(struct k_heap *heap, uint32_t flags, size_t bytes, + size_t alignment) +{ +#if CONFIG_SOF_USERSPACE_USE_SHARED_HEAP + /* + * FLAG_USER_SHARED_BUFFER maps to a specific heap, so + * the passed 'heap' can be ignored + */ + if (flags & SOF_MEM_FLAG_USER_SHARED_BUFFER) + return rballoc_align(flags, bytes, alignment); +#endif + + if (!heap) { + /* + * Ensure virtual heap is utilized for large buffers + * if no heap is explicitly passed. rballoc_align() + * has this logic. + */ + if (flags & SOF_MEM_FLAG_LARGE_BUFFER) + return rballoc_align(flags, bytes, alignment); + + 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 z_impl_sof_heap_free(struct k_heap *heap, void *addr) +{ + if (heap && addr && is_heap_pointer(heap, addr)) + heap_free(heap, addr); + else + rfree(addr); +} + +#ifndef CONFIG_SOF_USERSPACE_INTERFACE_ALLOC + +/* + * Putting these as inlines in alloc.h breaks Zephyr native + * ztests like sof/test/ztest/unit/fast-get that include rtos/alloc.h + * but do not link lib/alloc.c . To to keep the tests happy, + * implement the functions here. + */ + +void *sof_heap_alloc(struct k_heap *heap, uint32_t flags, size_t bytes, + size_t alignment) +{ + return z_impl_sof_heap_alloc(heap, flags, bytes, alignment); +} + +void sof_heap_free(struct k_heap *heap, void *addr) +{ + return z_impl_sof_heap_free(heap, addr); +} + +#endif /* CONFIG_SOF_USERSPACE_INTERFACE_ALLOC */ +#endif /* CONFIG_SOF_NATIVE_SIM_HOST_HEAP */ + 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/clk.c b/zephyr/lib/clk.c index 0cc9006e3a11..06cc5979753e 100644 --- a/zephyr/lib/clk.c +++ b/zephyr/lib/clk.c @@ -13,7 +13,7 @@ #include <zephyr/device.h> #include <zephyr/drivers/clock_control.h> -static SHARED_DATA struct clock_info platform_clocks_info[NUM_CLOCKS]; +static struct clock_info platform_clocks_info[NUM_CLOCKS]; static int select_cpu_freq(int clock, int hz) { diff --git a/zephyr/lib/cpu.c b/zephyr/lib/cpu.c index 16a583b2970a..c2f654509a34 100644 --- a/zephyr/lib/cpu.c +++ b/zephyr/lib/cpu.c @@ -27,7 +27,7 @@ static uint32_t mic_disable_status; #include "../audio/copier/copier.h" /* Zephyr includes */ -#include <version.h> +#include <zephyr/version.h> #include <zephyr/kernel.h> #include <zephyr/kernel/smp.h> #include <zephyr/device.h> @@ -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..a478d5069a09 100644 --- a/zephyr/lib/dma.c +++ b/zephyr/lib/dma.c @@ -15,12 +15,13 @@ #include <sof/lib/memory.h> #include <rtos/sof.h> #include <rtos/spinlock.h> +#include <rtos/userspace_helper.h> #include <zephyr/device.h> #define DW_DMA_BUFFER_PERIOD_COUNT 0x4 #define HDA_DMA_BUFFER_PERIOD_COUNT 4 -SHARED_DATA struct sof_dma dma[] = { +APP_SYSUSER_DATA struct sof_dma dma[] = { #if DT_NODE_HAS_STATUS(DT_NODELABEL(lpgpdma0), okay) { /* Low Power GP DMAC 0 */ .plat_data = { @@ -78,13 +79,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 +97,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 +153,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 +213,73 @@ 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) || defined(CONFIG_SOC_ACP_7_X) +{ + .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 = DT_PROP(DT_NODELABEL(acp_sdw_dma), dma_channels), + .period_count = 2, + }, + .z_dev = DEVICE_DT_GET(DT_NODELABEL(acp_sdw_dma)), +}, +{ + .plat_data = { + .dir = SOF_DMA_DIR_MEM_TO_DEV | + SOF_DMA_DIR_DEV_TO_MEM, +#if defined(CONFIG_SOC_ACP_7_X) + .devs = SOF_DMA_DEV_TDM, + .caps = SOF_DMA_CAP_TDM, +#elif defined(CONFIG_SOC_ACP_7_0) + .devs = SOF_DMA_DEV_HS | SOF_DMA_DEV_SP | SOF_DMA_DEV_BT, + .caps = SOF_DMA_CAP_HS | SOF_DMA_CAP_SP | SOF_DMA_CAP_BT, +#endif + .base = DMA0_BASE, + .chan_size = DMA0_SIZE, + .channels = 6, + .period_count = 2, + }, + .z_dev = DEVICE_DT_GET(DT_NODELABEL(acp_tdm_dma)), +}, +#endif +#if DT_HAS_COMPAT_STATUS_OKAY(mediatek_afe_memif_dma) +{ + .plat_data = { + .dir = SOF_DMA_DIR_MEM_TO_DEV | SOF_DMA_DIR_DEV_TO_MEM, + .devs = SOF_DMA_DEV_AFE_MEMIF, + .channels = DT_PROP(DT_NODELABEL(afe_memif_dma), dma_channels), + .period_count = 4, + }, + .z_dev = DEVICE_DT_GET(DT_NODELABEL(afe_memif_dma)), +}, +#endif +#if DT_HAS_COMPAT_STATUS_OKAY(mediatek_sof_host_dma) +{ + .plat_data = { + .dir = SOF_DMA_DIR_HMEM_TO_LMEM | SOF_DMA_DIR_LMEM_TO_HMEM, + .devs = SOF_DMA_DEV_HOST, + .channels = DT_PROP(DT_NODELABEL(host_dma), dma_channels), + .period_count = 4, + }, + .z_dev = DEVICE_DT_GET(DT_NODELABEL(host_dma)), +}, +#endif }; const struct dma_info lib_dma = { @@ -220,10 +288,12 @@ const struct dma_info lib_dma = { }; /* Initialize all platform DMAC's */ -int dmac_init(struct sof *sof) +__cold int dmac_init(struct sof *sof) { int i; + assert_can_be_cold(); + sof->dma_info = &lib_dma; /* early lock initialization for ref counting */ diff --git a/zephyr/lib/fast-get.c b/zephyr/lib/fast-get.c index 3be9475ff90c..cb0a2cfb07c7 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,277 @@ 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; + } + + /* The module driver heap is shared by all instances of a given module. + * Instances can share the allocated buffer. + */ + if (size > FAST_GET_MAX_COPY_SIZE || !IS_ENABLED(CONFIG_USERSPACE) || + IS_ENABLED(CONFIG_SOF_USERSPACE_USE_DRIVER_HEAP)) + 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 + * + * We also reach this point when using the module driver heap. + * Since the heap is already shared across module instances, + * we skip memory domain manipulation. */ - dcache_invalidate_region((__sparse_force void __sparse_cache *)ret, size); + if (current_is_userspace && size > FAST_GET_MAX_COPY_SIZE) { + 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, 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/pm_runtime.c b/zephyr/lib/pm_runtime.c index fcaa03cb7275..21b3e3ae3df5 100644 --- a/zephyr/lib/pm_runtime.c +++ b/zephyr/lib/pm_runtime.c @@ -21,12 +21,12 @@ DECLARE_TR_CTX(power_tr, SOF_UUID(power_uuid), LOG_LEVEL_INFO); #if defined(CONFIG_PM_POLICY_CUSTOM) const struct pm_state_info *pm_policy_next_state(uint8_t cpu, int32_t ticks) { - unsigned int num_cpu_states; + uint8_t num_cpu_states; const struct pm_state_info *cpu_states; num_cpu_states = pm_state_cpu_get_all(cpu, &cpu_states); - for (int i = num_cpu_states - 1; i >= 0; i--) { + for (int i = (int)num_cpu_states - 1; i >= 0; i--) { const struct pm_state_info *state = &cpu_states[i]; uint32_t min_residency, exit_latency; 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..e205fc256308 100644 --- a/zephyr/lib/userspace_helper.c +++ b/zephyr/lib/userspace_helper.c @@ -15,187 +15,197 @@ #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> - -#define MODULE_DRIVER_HEAP_CACHED CONFIG_SOF_ZEPHYR_HEAP_CACHED +#include <sof/lib/mailbox.h> +#include <sof/lib/dai.h> +#include <sof/lib/dma.h> /* 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; - sys_cache_data_invd_range(mem, - sys_heap_usable_size(mod_drv_heap, mem_uncached)); + /* + * 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); - mem = 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; + + ret = k_mem_domain_add_partition(domain, &mem_partition); + if (ret < 0) + return ret; + +#ifdef CONFIG_IPC_MAJOR_4 + /* HOSTBOX partitions for IPC4 module init parameter block reads. + * comp_new_ipc4() accesses MAILBOX_HOSTBOX_BASE directly to get + * the module configuration data sent by the host. + */ + struct k_mem_partition hostbox_partition; + + /* Uncached HOSTBOX partition */ + hostbox_partition.start = + (uintptr_t)sys_cache_uncached_ptr_get((void __sparse_cache *)MAILBOX_HOSTBOX_BASE); + hostbox_partition.size = ALIGN_UP(MAILBOX_HOSTBOX_SIZE, + CONFIG_MMU_PAGE_SIZE); + hostbox_partition.attr = K_MEM_PARTITION_P_RO_U_RO; + + ret = k_mem_domain_add_partition(domain, &hostbox_partition); + if (ret < 0) + return ret; + + /* Cached HOSTBOX partition for cache invalidation path. + * sys_cache_data_invd_range() syscall verification requires + * write access to the region, so use RW instead of RO. + */ + hostbox_partition.start = + (uintptr_t)sys_cache_cached_ptr_get((void *)MAILBOX_HOSTBOX_BASE); + hostbox_partition.size = ALIGN_UP(MAILBOX_HOSTBOX_SIZE, + CONFIG_MMU_PAGE_SIZE); + hostbox_partition.attr = K_MEM_PARTITION_P_RW_U_RW; + + ret = k_mem_domain_add_partition(domain, &hostbox_partition); + if (ret < 0) + return ret; +#else /* 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; - - ret = k_mem_domain_add_partition(comp_dom, &common_partition); - if (ret < 0) - return ret; + const struct dma_info *info = dma_info_get(); + struct sof_dma *d; - 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..e8cfb2347246 --- /dev/null +++ b/zephyr/lib/vregion.c @@ -0,0 +1,569 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright(c) 2025 Intel Corporation. + * + * Author: Liam Girdwood <liam.r.girdwood@linux.intel.com> + */ + +#include <sys/types.h> +#include <zephyr/init.h> +#include <zephyr/kernel.h> +#include <zephyr/app_memory/mem_domain.h> +#include <zephyr/logging/log.h> +#include <sof/lib/vpage.h> +#include <sof/lib/vregion.h> +#include <rtos/alloc.h> +#include <sof/common.h> +#include <sof/schedule/ll_schedule_domain.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; + struct k_mem_domain *domain; + + /* current allocation mode */ + enum vregion_mem_type type; /* LIFETIME at creation, switch to INTERIM */ + + /* interim heap - created lazily on first interim allocation */ + 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 memory size. + * Allocations start in LIFETIME mode. + * + * @param[in] memsize Total size of the virtual region memory. + * @return struct vregion* Pointer to the new virtual region instance, or NULL on failure. + */ +struct vregion *vregion_create(size_t memsize) +{ + struct vregion *vr; + unsigned int pages; + size_t total_size; + uint8_t *vregion_base; + + if (!memsize) { + LOG_ERR("error: invalid vregion memsize %zu", memsize); + return NULL; + } + + /* + * Align up the memory size to nearest page. Lifetime + * allocations growing upward from the base. The interim + * k_heap is created lazily from the remaining space when + * initialization phase is ready. + */ + total_size = ALIGN_UP(memsize, CONFIG_MM_DRV_PAGE_SIZE); + + /* allocate vregion metadata separately to keep it inaccessible to the user */ + vr = rmalloc(0, sizeof(*vr)); + if (!vr) + return NULL; + + /* allocate pages for vregion */ + pages = total_size / CONFIG_MM_DRV_PAGE_SIZE; + vregion_base = vpage_alloc(pages); + if (!vregion_base) { + rfree(vr); + return NULL; + } + + /* init vregion */ + vr->base = vregion_base; + vr->size = total_size; + vr->pages = pages; + vr->domain = NULL; + + /* lifetime linear allocator starts at the beginning of the vregion memory */ + vr->lifetime.base = vregion_base; + vr->lifetime.size = total_size; + vr->lifetime.ptr = vregion_base; + vr->lifetime.used = 0; + vr->lifetime.free_count = 0; + + /* start in lifetime allocation mode */ + vr->type = VREGION_MEM_TYPE_LIFETIME; + + 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 metadata at %p", + (void *)vregion_base, total_size, pages, (void *)vr); + + return vr; +} + +struct vregion *z_impl_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; +} + +struct vregion *z_impl_vregion_create_map(uintptr_t *vreg_start, size_t *vreg_size) +{ + if (!vreg_start || !vreg_size || !*vreg_size) + return NULL; + + struct vregion *vr = vregion_create(*vreg_size); + + if (!vr) + return NULL; + +#if CONFIG_USERSPACE && CONFIG_SOF_USERSPACE_LL + vregion_mem_info(vr, vreg_size, vreg_start); + + /* + * In the userspace LL case allocations are also performed by the + * userspace IPC thread, which is also the one, executing this syscall + */ + struct k_mem_domain *domain = zephyr_ll_mem_domain(); + struct k_mem_partition part = { + .start = *vreg_start, + .size = *vreg_size, + .attr = K_MEM_PARTITION_P_RW_U_RW | XTENSA_MMU_CACHED_WB, + }; + int ret = k_mem_domain_add_partition(domain, &part); + + if (ret < 0) { + vregion_put(vr); + return NULL; + } + + part.start = (uintptr_t)sys_cache_uncached_ptr_get((void *)part.start); + part.attr = K_MEM_PARTITION_P_RW_U_RW; + + ret = k_mem_domain_add_partition(domain, &part); + if (ret < 0) { + vregion_put(vr); + return NULL; + } + + vr->domain = domain; +#endif + + return vr; +} + +static void vregion_unmap(struct vregion *vr) +{ +#if CONFIG_USERSPACE && CONFIG_SOF_USERSPACE_LL + if (!vr->domain) + return; + + struct k_mem_partition part = { + .attr = K_MEM_PARTITION_P_RW_U_RW | XTENSA_MMU_CACHED_WB, + .start = (uintptr_t)vr->base, + .size = vr->size, + }; + + k_mem_domain_remove_partition(vr->domain, &part); + + part.start = (uintptr_t)sys_cache_uncached_ptr_get((void *)part.start); + part.attr = K_MEM_PARTITION_P_RW_U_RW; + + k_mem_domain_remove_partition(vr->domain, &part); +#endif +} + +/** + * @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 *z_impl_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); + + vregion_unmap(vr); + rfree(vr); + + return NULL; +} + +/** + * @brief Initialize the interim heap from remaining vregion space. + * + * Called on first interim allocation. Creates the k_heap from all buffer + * space not yet consumed by lifetime allocations. + * + * @param[in] vr Pointer to the virtual region instance. + */ +static void interim_heap_init(struct vregion *vr) +{ + uint8_t *interim_base; + ssize_t interim_size; + + if (vr->type == VREGION_MEM_TYPE_INTERIM) { + LOG_WRN("vregion %p already in interim mode", (void *)vr); + return; + } + + /* interim heap starts right after current lifetime pointer, page-aligned */ + interim_base = UINT_TO_POINTER(ALIGN_UP(POINTER_TO_UINT(vr->lifetime.ptr), + CONFIG_DCACHE_LINE_SIZE)); + interim_size = (vr->base + vr->size) - interim_base; + /* + * Calling k_heap_init() with too small size causes an assert + * failure. Exact limit is hard to deduce from sys_heap_init() + * code, but 1024 seems to be enough. + */ + if (interim_size < 1024) { + LOG_WRN("Too little memory, %zd bytes, left for interim heap", interim_size); + vr->type = VREGION_MEM_TYPE_INVALID; + return; + } + + LOG_INF("creating interim heap: lifetime used %zu, interim available %zu at %p", + vr->lifetime.used, interim_size, (void *)interim_base); + + /* cap lifetime so no more lifetime allocs can grow into interim */ + vr->lifetime.size = interim_base - vr->base; + + vr->interim.heap.heap.init_mem = interim_base; + vr->interim.heap.heap.init_bytes = interim_size; + + k_heap_init(&vr->interim.heap, interim_base, interim_size); + vr->type = VREGION_MEM_TYPE_INTERIM; + + /* Update lifetime heap with the interim heap usage. */ + vr->lifetime.ptr = (void *)(interim_base + interim_size); + vr->lifetime.used = (uint8_t *)vr->lifetime.ptr - (uint8_t *)vr->lifetime.base; +} + +void z_impl_vregion_set_interim(struct vregion *vr) +{ + if (!vr) + return; + + k_mutex_lock(&vr->lock, K_FOREVER); + + interim_heap_init(vr); + + k_mutex_unlock(&vr->lock); +} + +/** + * @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. + * + * If the interim heap has already been created (i.e., an interim allocation + * was made), log a warning and fall back to interim allocation. + * + * @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 z_impl_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); + + /* Check if pointer is in interim heap (if initialized) */ + if (vr->type == VREGION_MEM_TYPE_INTERIM && + ptr >= (void *)vr->interim.heap.heap.init_mem && + ptr < (void *)((uint8_t *)vr->interim.heap.heap.init_mem + + vr->interim.heap.heap.init_bytes)) + interim_free(&vr->interim, ptr); + else if (ptr >= (void *)vr->lifetime.base && + ptr < (void *)(vr->lifetime.base + vr->lifetime.size)) + /* pointer is in lifetime area - no-op free */ + lifetime_free(&vr->lifetime, ptr); + else + LOG_ERR("error: vregion free invalid pointer %p", ptr); + + k_mutex_unlock(&vr->lock); +} +EXPORT_SYMBOL(z_impl_vregion_free); + +/** + * @brief Allocate memory from the virtual region. + * + * @param[in] vr Pointer to the virtual region instance. + * @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 *z_impl_vregion_alloc_align(struct vregion *vr, + 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 (vr->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", vr->type); + p = NULL; + } + + k_mutex_unlock(&vr->lock); + + return p; +} +EXPORT_SYMBOL(z_impl_vregion_alloc_align); + +void *z_impl_vregion_alloc_coherent_align(struct vregion *vr, 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 = z_impl_vregion_alloc_align(vr, size, alignment); + + if (!p) + return NULL; + + sys_cache_data_invd_range(p, size); + + return sys_cache_uncached_ptr_get(p); +} +EXPORT_SYMBOL(z_impl_vregion_alloc_coherent_align); + +/** + * @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); +} + +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; +} + +#if CONFIG_SOF_VREGIONS && CONFIG_USERSPACE +#include <zephyr/internal/syscall_handler.h> + +bool vregion_verify(struct vregion *vr) +{ + if (!vr) + return false; + + /* vregion instances must not be accessible to the userspace. */ + K_OOPS(!K_SYSCALL_MEMORY_READ(vr, sizeof(*vr))); + + size_t vr_size = 0; + uintptr_t vr_start; + + vregion_mem_info(vr, &vr_size, &vr_start); + if (vr_size) + K_OOPS(K_SYSCALL_MEMORY_WRITE((void *)vr_start, vr_size)); + + return true; +} +#endif diff --git a/zephyr/schedule.c b/zephyr/schedule.c index 75155b5d4913..3874b28ff1f6 100644 --- a/zephyr/schedule.c +++ b/zephyr/schedule.c @@ -10,10 +10,22 @@ #include <sof/schedule/ll_schedule.h> #include <rtos/alloc.h> #include <rtos/symbol.h> +#include <rtos/userspace_helper.h> #include <sof/lib/cpu.h> #include <ipc/topology.h> -static struct schedulers *_schedulers[CONFIG_CORE_COUNT]; +/* Kernel-only scheduler list — depending on how SOF is built, + * either holds all or subset of scheduler types. + * Not accessible from user-space threads. + */ +static struct schedulers *_k_schedulers[CONFIG_CORE_COUNT]; + +#if CONFIG_SOF_USERSPACE_LL +/* User-accessible scheduler list — holds the subset of scheduler + * types that are managed in user-space + */ +static APP_SYSUSER_BSS struct schedulers *_u_schedulers[CONFIG_CORE_COUNT]; +#endif /** * Retrieves registered schedulers. @@ -21,6 +33,45 @@ static struct schedulers *_schedulers[CONFIG_CORE_COUNT]; */ struct schedulers **arch_schedulers_get(void) { - return _schedulers + cpu_get_id(); +#ifdef __ZEPHYR__ + assert(!k_is_user_context()); +#endif + return _k_schedulers + cpu_get_id(); } EXPORT_SYMBOL(arch_schedulers_get); + +/** + * Retrieves registered user schedulers for the current core. + * + * Relies on cpu_get_id(), so it may invoke privileged functions and + * must not be called from a user-space context. User-space callers + * should use arch_user_schedulers_get_for_core() instead. + * + * @return List of registered schedulers. + */ +struct schedulers **arch_user_schedulers_get(void) +{ +#ifdef CONFIG_SOF_USERSPACE_LL + assert(!k_is_user_context()); + return _u_schedulers + cpu_get_id(); +#else + return NULL; +#endif +} + +/** + * Retrieves registered user schedulers for the given core. + * + * Unlike arch_user_schedulers_get(), this takes the core explicitly and + * is therefore safe to call from a user-space context. + * + * @return List of registered schedulers. + */ +struct schedulers **arch_user_schedulers_get_for_core(int core) +{ +#ifdef CONFIG_SOF_USERSPACE_LL + return _u_schedulers + core; +#else + return NULL; +#endif +} 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/alloc.c b/zephyr/syscall/alloc.c new file mode 100644 index 000000000000..04f402f37d09 --- /dev/null +++ b/zephyr/syscall/alloc.c @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// Copyright(c) 2026 Intel Corporation. + +#include <rtos/alloc.h> +#include <sof/schedule/ll_schedule_domain.h> +#include <zephyr/kernel.h> +#include <zephyr/internal/syscall_handler.h> + +static inline void *z_vrfy_sof_heap_alloc(struct k_heap *heap, uint32_t flags, + size_t bytes, size_t alignment) +{ + /* only allow flags that are safe for user-space heap isolation */ + static const uint32_t allowed_flags = + SOF_MEM_FLAG_USER | SOF_MEM_FLAG_COHERENT | SOF_MEM_FLAG_DMA | + SOF_MEM_FLAG_LARGE_BUFFER; + + K_OOPS(flags & ~allowed_flags); + + /* user-space use of sof_heap_alloc() limited to this single heap */ + K_OOPS(!zephyr_ll_user_heap_verify(heap)); + + return z_impl_sof_heap_alloc(heap, flags, bytes, alignment); +} +#include <zephyr/syscalls/sof_heap_alloc_mrsh.c> + +static inline void z_vrfy_sof_heap_free(struct k_heap *heap, void *addr) +{ + /* user-space use of sof_heap_alloc() limited to this single heap */ + K_OOPS(!zephyr_ll_user_heap_verify(heap)); + + if (addr) { + uintptr_t start = (uintptr_t)heap->heap.init_mem; + uintptr_t addr_uc = (uintptr_t)sys_cache_uncached_ptr_get(addr); + K_OOPS(addr_uc < start || addr_uc >= start + heap->heap.init_bytes); + K_OOPS(K_SYSCALL_MEMORY_WRITE(addr, 1)); + } + z_impl_sof_heap_free(heap, addr); +} +#include <zephyr/syscalls/sof_heap_free_mrsh.c> diff --git a/zephyr/syscall/cpu.c b/zephyr/syscall/cpu.c new file mode 100644 index 000000000000..ee1990db8fd4 --- /dev/null +++ b/zephyr/syscall/cpu.c @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// Copyright(c) 2026 Intel Corporation. + +#include <sof/lib/cpu.h> +#include <zephyr/kernel.h> +#include <zephyr/internal/syscall_handler.h> + +/** + * \brief Userspace verification wrapper for cpu_get_id(). + * + * The system call takes no arguments and passes no pointers, so no + * access validation is required; the call is simply forwarded to the + * implementation running in supervisor context. + * + * @return Id of the DSP core executing the call. + */ +static inline int z_vrfy_cpu_get_id(void) +{ + return z_impl_cpu_get_id(); +} +#include <zephyr/syscalls/cpu_get_id_mrsh.c> diff --git a/zephyr/syscall/dai.c b/zephyr/syscall/dai.c new file mode 100644 index 000000000000..2e09187e146b --- /dev/null +++ b/zephyr/syscall/dai.c @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// Copyright(c) 2026 Intel Corporation. + +#include <sof/lib/dai.h> +#include <zephyr/kernel.h> +#include <zephyr/internal/syscall_handler.h> + +static inline const struct device *z_vrfy_dai_get_device(enum sof_ipc_dai_type type, + uint32_t index) +{ + const struct device *dev = z_impl_dai_get_device(type, index); + + if (dev && !k_object_is_valid(dev, K_OBJ_DRIVER_DAI)) + return NULL; + + return dev; +} +#include <zephyr/syscalls/dai_get_device_mrsh.c> diff --git a/zephyr/syscall/sof_dma.c b/zephyr/syscall/sof_dma.c new file mode 100644 index 000000000000..05d6df377c95 --- /dev/null +++ b/zephyr/syscall/sof_dma.c @@ -0,0 +1,293 @@ +// 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> +#include <zephyr/sys/math_extras.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; + struct dma_block_config *kern_next, *user_next; + size_t alloc_size; + uint32_t i; + + if (!cfg->block_count) + return NULL; + + /* + * block_count is user-controlled, so compute the allocation size + * with an overflow check. Without it, a large block_count would + * wrap the product on 32-bit size_t, yield an undersized buffer, + * and let the copy loop below overflow the kernel heap. + */ + if (size_mul_overflow(sizeof(*kern_cfg), cfg->block_count, &alloc_size)) + return NULL; + + kern_cfg = rmalloc(0, alloc_size); + if (!kern_cfg) + return NULL; + + user_next = cfg->head_block; + for (i = 0, kern_next = kern_cfg; i < cfg->block_count; i++, kern_next++) { + /* + * The user list must contain exactly block_count entries. + * A list that terminates early (NULL before the count is + * reached) is rejected so the kernel copy and block_count + * stay consistent and the driver never walks past the copy. + */ + if (!user_next) + 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; + } + + /* + * kern_next->next_block now holds the untrusted user-space + * pointer copied above. Never let the kernel walk it: relink + * every block to the kernel copy so the list can never point + * outside the kern_cfg array. + */ + user_next = kern_next->next_block; + + if (i + 1 < cfg->block_count) { + /* link to the next kernel block */ + kern_next->next_block = kern_next + 1; + } else { + /* + * Last block: the user list must end here, either + * NULL-terminated or cyclic back to the first block. + */ + if (user_next == cfg->head_block) + kern_next->next_block = kern_cfg; + else if (!user_next) + kern_next->next_block = NULL; + else + goto err; + } + } + + /* 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 = { 0 }; + struct dma_config 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/syscall/vregion.c b/zephyr/syscall/vregion.c new file mode 100644 index 000000000000..dd02f854cf82 --- /dev/null +++ b/zephyr/syscall/vregion.c @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// Copyright(c) 2026 Intel Corporation. + +#include <sof/lib/vregion.h> +#include <zephyr/kernel.h> +#include <zephyr/internal/syscall_handler.h> + +static inline void *z_vrfy_vregion_alloc_align(struct vregion *vr, size_t size, size_t alignment) +{ + if (vregion_verify(vr)) + return z_impl_vregion_alloc_align(vr, size, alignment); + + return NULL; +} +#include <zephyr/syscalls/vregion_alloc_align_mrsh.c> + +static inline void *z_vrfy_vregion_alloc_coherent_align(struct vregion *vr, + size_t size, size_t alignment) +{ + if (vregion_verify(vr)) + return z_impl_vregion_alloc_coherent_align(vr, size, alignment); + + return NULL; +} +#include <zephyr/syscalls/vregion_alloc_coherent_align_mrsh.c> + +static inline void z_vrfy_vregion_free(struct vregion *vr, void *ptr) +{ + if (vregion_verify(vr)) + z_impl_vregion_free(vr, ptr); +} +#include <zephyr/syscalls/vregion_free_mrsh.c> + +struct vregion *z_vrfy_vregion_get(struct vregion *vr) +{ + if (vregion_verify(vr)) + return z_impl_vregion_get(vr); + return NULL; +} +#include <zephyr/syscalls/vregion_get_mrsh.c> + +struct vregion *z_vrfy_vregion_put(struct vregion *vr) +{ + if (vregion_verify(vr)) + return z_impl_vregion_put(vr); + return NULL; +} +#include <zephyr/syscalls/vregion_put_mrsh.c> + +struct vregion *z_vrfy_vregion_create_map(uintptr_t *vreg_start, size_t *vreg_size) +{ + K_OOPS(K_SYSCALL_MEMORY_WRITE(vreg_start, sizeof(*vreg_start))); + K_OOPS(K_SYSCALL_MEMORY_WRITE(vreg_size, sizeof(*vreg_size))); + return z_impl_vregion_create_map(vreg_start, vreg_size); +} +#include <zephyr/syscalls/vregion_create_map_mrsh.c> + +void z_vrfy_vregion_set_interim(struct vregion *vr) +{ + if (vregion_verify(vr)) + z_impl_vregion_set_interim(vr); +} +#include <zephyr/syscalls/vregion_set_interim_mrsh.c> diff --git a/zephyr/test/CMakeLists.txt b/zephyr/test/CMakeLists.txt index e0718b8e0754..397e9e31b938 100644 --- a/zephyr/test/CMakeLists.txt +++ b/zephyr/test/CMakeLists.txt @@ -1,5 +1,34 @@ -if (CONFIG_ACE_VERSION_1_5) - zephyr_library_sources_ifdef(CONFIG_SOF_BOOT_TEST - vmh.c - ) +if(CONFIG_SOF_BOOT_TEST) + if(NOT CONFIG_SOF_USERSPACE_LL) + zephyr_library_sources_ifdef(CONFIG_VIRTUAL_HEAP + vmh.c + ) + endif() + + zephyr_library_sources_ifdef(CONFIG_SOF_VREGIONS + vpage.c vregion.c + ) + zephyr_library_sources_ifdef(CONFIG_USERSPACE + userspace/ksem.c + ) + if(CONFIG_USERSPACE AND CONFIG_SOF_USERSPACE_INTERFACE_ALLOC) + zephyr_library_sources(userspace/test_heap_alloc.c) + endif() +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_heap_alloc.c b/zephyr/test/userspace/test_heap_alloc.c new file mode 100644 index 000000000000..e4dcb567b616 --- /dev/null +++ b/zephyr/test/userspace/test_heap_alloc.c @@ -0,0 +1,156 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright(c) 2026 Intel Corporation. + */ + +/* + * Test cases for sof_heap_alloc() / sof_heap_free() use from a Zephyr + * user-space thread. + */ + +#include <sof/boot_test.h> +#include <sof/schedule/ll_schedule_domain.h> +#include <rtos/alloc.h> + +#include <zephyr/kernel.h> +#include <zephyr/ztest.h> +#include <zephyr/logging/log.h> +#include <zephyr/app_memory/app_memdomain.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_APPMEM_PARTITION_DEFINE(heap_alloc_test_part); +K_APP_BMEM(heap_alloc_test_part) static uint8_t non_heap_byte; + +enum heap_alloc_test_case { + HEAP_ALLOC_VALID, + HEAP_ALLOC_NULL_HEAP, + HEAP_ALLOC_FORBIDDEN_FLAGS, + HEAP_FREE_NON_HEAP_POINTER, + HEAP_FREE_INACCESSIBLE_POINTER, +}; + +static void user_function(void *p1, void *p2, void *p3) +{ + struct k_heap *heap = p1; + enum heap_alloc_test_case test_case = (enum heap_alloc_test_case)(uintptr_t)p2; + void *free_ptr = p3; + void *ptr; + + __ASSERT(k_is_user_context(), "isn't user"); + + LOG_INF("SOF thread %s (%s)", + k_is_user_context() ? "UserSpace!" : "privileged mode.", + CONFIG_BOARD_TARGET); + + switch (test_case) { + case HEAP_ALLOC_VALID: + ptr = sof_heap_alloc(heap, SOF_MEM_FLAG_USER, 128, 0); + zassert_not_null(ptr, "sof_heap_alloc returned NULL"); + sof_heap_free(heap, ptr); + return; + case HEAP_ALLOC_NULL_HEAP: + (void)sof_heap_alloc(NULL, SOF_MEM_FLAG_USER, 128, 0); + break; + case HEAP_ALLOC_FORBIDDEN_FLAGS: + (void)sof_heap_alloc(heap, SOF_MEM_FLAG_LARGE_BUFFER, 128, 0); + break; + case HEAP_FREE_NON_HEAP_POINTER: + sof_heap_free(heap, &non_heap_byte); + break; + case HEAP_FREE_INACCESSIBLE_POINTER: + sof_heap_free(heap, free_ptr); + break; + default: + zassert_unreachable("unknown heap allocation test case"); + } + + zassert_unreachable("syscall security check did not fault"); +} + +static void run_user_heap_alloc_case(enum heap_alloc_test_case test_case, struct k_heap *heap, + void *ptr, bool grant_ll_domain, bool expect_fault) +{ + int ret; + + k_thread_create(&user_thread, user_stack, USER_STACKSIZE, + user_function, heap, (void *)(uintptr_t)test_case, ptr, + -1, K_USER, K_FOREVER); + + if (grant_ll_domain) + k_mem_domain_add_thread(zephyr_ll_mem_domain(), &user_thread); + + if (expect_fault) + sof_boot_test_set_fault_valid(&user_thread); + + k_thread_start(&user_thread); + ret = k_thread_join(&user_thread, K_FOREVER); + zassert_equal(ret, 0, "user thread join failed: %d", ret); +} + +static void test_user_thread_heap_alloc(enum heap_alloc_test_case test_case) +{ + bool grant_ll_domain = true; + bool expect_fault = false; + void *ptr = NULL; + struct k_heap *heap; + + heap = zephyr_ll_user_heap(); + zassert_not_null(heap, "user heap not found"); + + switch (test_case) { + case HEAP_ALLOC_VALID: + break; + case HEAP_ALLOC_NULL_HEAP: + case HEAP_ALLOC_FORBIDDEN_FLAGS: + case HEAP_FREE_NON_HEAP_POINTER: + expect_fault = true; + break; + case HEAP_FREE_INACCESSIBLE_POINTER: + ptr = sof_heap_alloc(heap, SOF_MEM_FLAG_USER, 128, 0); + zassert_not_null(ptr, "kernel heap allocation failed"); + grant_ll_domain = false; + expect_fault = true; + break; + default: + zassert_unreachable("unknown heap allocation test case"); + } + + run_user_heap_alloc_case(test_case, heap, ptr, grant_ll_domain, expect_fault); + + sof_heap_free(heap, ptr); +} + +ZTEST(sof_boot, user_space_heap_alloc) +{ + test_user_thread_heap_alloc(HEAP_ALLOC_VALID); +} + +ZTEST(sof_boot, user_space_heap_alloc_rejects_null_heap) +{ + test_user_thread_heap_alloc(HEAP_ALLOC_NULL_HEAP); +} + +ZTEST(sof_boot, user_space_heap_alloc_rejects_forbidden_flags) +{ + test_user_thread_heap_alloc(HEAP_ALLOC_FORBIDDEN_FLAGS); +} + +ZTEST(sof_boot, user_space_heap_free_rejects_non_heap_pointer) +{ + k_mem_domain_add_partition(zephyr_ll_mem_domain(), &heap_alloc_test_part); + + test_user_thread_heap_alloc(HEAP_FREE_NON_HEAP_POINTER); + + k_mem_domain_remove_partition(zephyr_ll_mem_domain(), &heap_alloc_test_part); +} + +ZTEST(sof_boot, user_space_heap_free_rejects_inaccessible_pointer) +{ + test_user_thread_heap_alloc(HEAP_FREE_INACCESSIBLE_POINTER); +} 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..1e31e01538b7 --- /dev/null +++ b/zephyr/test/userspace/test_ll_task.c @@ -0,0 +1,145 @@ +// 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; + int16_t priority = -1; + 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); + zassert_equal(task->priority, priority, "negative priority was not preserved"); + + 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..eb36721d582e --- /dev/null +++ b/zephyr/test/vregion.c @@ -0,0 +1,101 @@ +// 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) +{ + /* + * Use a 3-page vregion so that after two lifetime allocations there is still + * enough remaining space to initialize and exercise the interim heap. + */ + struct vregion *vreg = vregion_create(3 * 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, 6000); + + zassert_not_null(ptr); + + void *ptr_align = vregion_alloc_align(vreg, 5000, 16); + + zassert_not_null(ptr_align); + zassert_equal((uintptr_t)ptr_align & 15, 0); + + /* + * Seal lifetime, switch to interim. The interim heap is created + * lazily from the remaining ~1 page, so a 6000-byte alloc won't fit. + */ + vregion_set_interim(vreg); + + void *ptr_nomem = vregion_alloc(vreg, 6000); + + zassert_is_null(ptr_nomem); + + /* Lifetime frees are no-ops; re-alloc from interim still fails */ + vregion_free(vreg, ptr_align); + vregion_free(vreg, ptr); + + ptr_nomem = vregion_alloc(vreg, 6000); + + zassert_is_null(ptr_nomem); +} + +static void test_vreg_alloc_tmp(struct vregion *vreg) +{ + void *ptr = vregion_alloc(vreg, 20); + + zassert_not_null(ptr); + + void *ptr_align = vregion_alloc_align(vreg, 1000, 16); + + zassert_not_null(ptr_align); + zassert_equal((uintptr_t)ptr_align & 15, 0); + + void *ptr_nomem = vregion_alloc(vreg, 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, 1000); + + 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 lifetime allocations (initial mode), then seal */ + test_vreg_alloc_lifet(vreg); + /* Test interim allocations (already switched by lifet test) */ + test_vreg_alloc_tmp(vreg); + + test_vreg_destroy(vreg); +} diff --git a/zephyr/wrapper.c b/zephyr/wrapper.c index 2e302f22c94b..afdc5b54a2e9 100644 --- a/zephyr/wrapper.c +++ b/zephyr/wrapper.c @@ -6,6 +6,7 @@ */ #include <sof/init.h> +#include <sof/boot_test.h> #include <sof/llext_manager.h> #include <rtos/idc.h> #include <rtos/interrupt.h> @@ -32,7 +33,7 @@ #include <zephyr/kernel_structs.h> #include <zephyr/kernel.h> #include <zephyr/pm/policy.h> -#include <version.h> +#include <zephyr/version.h> #include <zephyr/sys/__assert.h> #include <zephyr/logging/log_ctrl.h> #include <zephyr/logging/log.h> @@ -57,7 +58,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 @@ -176,9 +177,6 @@ int task_main_start(struct sof *sof) /* init default audio components */ sys_comp_init(sof); - /* init pipeline position offsets */ - pipeline_posn_init(sof); - return 0; } @@ -257,15 +255,24 @@ void platform_dai_timestamp(struct comp_dev *dai, posn->flags |= SOF_TIME_DAI_VALID; /* get SSP wallclock - DAI sets this to stream start value */ +#ifndef CONFIG_SOF_USERSPACE_LL posn->wallclock = sof_cycle_get_64() - posn->wallclock; posn->wallclock_hz = sys_cycle_get_64_rate(); +#else + posn->wallclock = k_uptime_ticks() - posn->wallclock; + posn->wallclock_hz = CONFIG_SYS_CLOCK_TICKS_PER_SEC; +#endif posn->flags |= SOF_TIME_WALL_VALID; } /* get current wallclock for componnent */ void platform_dai_wallclock(struct comp_dev *dai, uint64_t *wallclock) { +#ifndef CONFIG_SOF_USERSPACE_LL *wallclock = sof_cycle_get_64(); +#else + *wallclock = k_uptime_ticks(); +#endif } /* @@ -325,15 +332,43 @@ volatile int *_sof_fatal_null = NULL; struct arch_esf; +#if CONFIG_SOF_BOOT_TEST +static struct k_thread *sof_boot_test_fault_thread; + +void sof_boot_test_set_fault_valid(struct k_thread *thread) +{ + sof_boot_test_fault_thread = thread; +} + +static bool sof_boot_test_fault_expected(void) +{ + if (sof_boot_test_fault_thread != k_current_get()) + return false; + + sof_boot_test_fault_thread = NULL; + return true; +} +#endif + void k_sys_fatal_error_handler(unsigned int reason, const struct arch_esf *esf) { ARG_UNUSED(esf); +#if CONFIG_SOF_BOOT_TEST + if (sof_boot_test_fault_expected()) { + LOG_ERR("Expected fatal error as part of boot test"); + return; + } +#endif + /* 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");