diff --git a/app/boards/intel_adsp_ace15_mtpm.conf b/app/boards/intel_adsp_ace15_mtpm.conf index 2fd25da7db9f..8f190af50a40 100644 --- a/app/boards/intel_adsp_ace15_mtpm.conf +++ b/app/boards/intel_adsp_ace15_mtpm.conf @@ -20,6 +20,8 @@ CONFIG_PM_POLICY_CUSTOM=y CONFIG_POWER_DOMAIN=y CONFIG_POWER_DOMAIN_INTEL_ADSP=y +CONFIG_ADSP_IMR_CONTEXT_SAVE=y + # enable Zephyr drivers CONFIG_ZEPHYR_NATIVE_DRIVERS=y CONFIG_DAI=y @@ -73,7 +75,9 @@ CONFIG_LL_WATCHDOG=y # Temporary disabled options CONFIG_TRACE=n +CONFIG_HOST_DMA_RELOAD_DELAY_ENABLE=n + CONFIG_COMP_KPB=y CONFIG_COMP_ARIA=y CONFIG_CLOCK_CONTROL_ADSP=y -CONFIG_CLOCK_CONTROL=y +CONFIG_CLOCK_CONTROL=y \ No newline at end of file diff --git a/lmdk/README.md b/lmdk/README.md new file mode 100644 index 000000000000..5d8b4b3037c2 --- /dev/null +++ b/lmdk/README.md @@ -0,0 +1,15 @@ +# Loadable Modules Dev Kit + +***TODO: add link to the documentation repo!*** + +To build dummy loadable library execute: + + cd libraries/dummy + mkdir build + cd build + + cmake -DRIMAGE_COMMAND="/path/to/rimage" -DSIGNING_KEY="/path/to/signing/key.pem" .. + cmake --build . + +Here RIMAGE_COMMAND is path to rimage executable binary, SIGNING_KEY is path to +signing key for rimage. diff --git a/lmdk/cmake/build.cmake b/lmdk/cmake/build.cmake new file mode 100644 index 000000000000..ba6d5d0427e0 --- /dev/null +++ b/lmdk/cmake/build.cmake @@ -0,0 +1,53 @@ +# This file is intended to be included from project's CMakeLists.txt. +# Prior to include, MODULES_LIST variable should be initialised with list +# of modules (subdirectories in modules dir) that should be built into +# project's loadable library. + +if(NOT DEFINED MODULES_LIST) + message(FATAL_ERROR "Please define MODULES_LIST: list of modules to be built into loadable library") +endif() + +include(${CMAKE_CURRENT_LIST_DIR}/config.cmake) + +foreach(MODULE ${MODULES_LIST}) + add_executable(${MODULE}) + add_subdirectory(${LMDK_BASE}/modules/${MODULE} ${MODULE}_module) + +### set_target_properties(${MODULE} PROPERTIES OUTPUT_NAME ${MODULE}.mod) + + target_include_directories(${MODULE} PRIVATE + "${LMDK_BASE}/include" + "${RIMAGE_INCLUDE_DIR}" + ) + + # generate linker script + get_target_property(HPSRAM_ADDR ${MODULE} HPSRAM_ADDR) + + if(NOT DEFINED HPSRAM_ADDR) + message(FATAL_ERROR "Please define HPSRAM_ADDR for module ${MODULE}") + endif() + + add_custom_command(TARGET ${MODULE} PRE_LINK + COMMAND ${CMAKE_COMMAND} + -DMODULE=${MODULE} + -DHPSRAM_ADDR=${HPSRAM_ADDR} + -P ${CMAKE_CURRENT_LIST_DIR}/ldscripts.cmake + ) + + target_link_options(${MODULE} PRIVATE + "-nostdlib" "-nodefaultlibs" + "-Wl,--no-undefined" "-Wl,--unresolved-symbols=report-all" "-Wl,--error-unresolved-symbols" + #"-Wl,--gc-sections" # may remove .bss and that will result in rimage error, do not use for now + "-Wl,-Map,$.map" # optional: just for debug + "-T" "${MODULE}_ldscripts/elf32xtensa.x" + ) +endforeach() + +set(RIMAGE_OUTPUT_FILE ${PROJECT_NAME}_noextmft) +set(OUTPUT_FILE ${PROJECT_NAME}.bin) + +add_custom_target(${PROJECT_NAME}_target ALL + DEPENDS ${MODULES_LIST} + COMMAND ${RIMAGE_COMMAND} -k ${SIGNING_KEY} -f 2.0.0 -b 1 -o ${RIMAGE_OUTPUT_FILE} -c ${TOML} -e ${MODULES_LIST} + COMMAND ${CMAKE_COMMAND} -E cat ${RIMAGE_OUTPUT_FILE}.xman ${RIMAGE_OUTPUT_FILE} > ${OUTPUT_FILE} +) diff --git a/lmdk/cmake/config.cmake b/lmdk/cmake/config.cmake new file mode 100644 index 000000000000..a326df87ea4d --- /dev/null +++ b/lmdk/cmake/config.cmake @@ -0,0 +1,25 @@ + +if(NOT DEFINED RIMAGE_COMMAND) + message(FATAL_ERROR + " Please define RIMAGE_COMMAND: path to rimage executable.\n" + " E.g. using cmake -DRIMAGE_COMMAND=/path/rimage command line parameter." + ) +endif() + +if(NOT DEFINED SIGNING_KEY) + message(FATAL_ERROR + " Please define SIGNING_KEY: path to signing key for rimage.\n" + " E.g. using cmake -DSIGNING_KEY=/path/to/key.pem command line parameter." + ) +endif() + +# This Loadable Modules Dev Kit root dir +set(LMDK_BASE ${CMAKE_CURRENT_LIST_DIR}/..) +cmake_path(ABSOLUTE_PATH LMDK_BASE NORMALIZE) + +# thesofproject root dir +set(SOF_BASE ${LMDK_BASE}/..) +cmake_path(ABSOLUTE_PATH SOF_BASE NORMALIZE) + +set(RIMAGE_INCLUDE_DIR ${SOF_BASE}/rimage/src/include) +cmake_path(ABSOLUTE_PATH RIMAGE_INCLUDE_DIR NORMALIZE) diff --git a/lmdk/cmake/ldscripts.cmake b/lmdk/cmake/ldscripts.cmake new file mode 100644 index 000000000000..917dd4b02d09 --- /dev/null +++ b/lmdk/cmake/ldscripts.cmake @@ -0,0 +1,49 @@ +# Linker scripts generator + +# These linker scripts are based on those found in https://github.com/thesofproject/converged-sof-modules +# +# There are few things these scripts ensure: +# +# (1) Each module has its own reserved (virtual) address space. This is specified as +# HPSRAM_ADDR in module's CMakeLists.txt and goes to memory_header_linker_script.txt. +# +# (2) .buildinfo section must be put at 0 offset of .text section. .buildinfo contains +# module loadable libraries API version. That API version is verified by base FW. +# Base FW accesses it simply as 0 offset from module's code. +# +# (3) .module section contains module manifest description. This section is used by +# rimage to generate module manifest. Scripts ensure that this section is not garbage +# collected by linker. + +# Required parameters MODULE and HPSRAM_ADDR should be specified in command line. + +if(NOT DEFINED MODULE) + message(FATAL_ERROR "MODULE not defined") +endif() + +if(NOT DEFINED HPSRAM_ADDR) + message(FATAL_ERROR "HPSRAM_ADDR not defined") +endif() + +# reserve space for manifest? +math(EXPR HPSRAM "${HPSRAM_ADDR} + 9 * 4096" OUTPUT_FORMAT HEXADECIMAL) + +set(LDSCRIPTS_DIR ${MODULE}_ldscripts) +set(LDSCRIPT_FILE ${LDSCRIPTS_DIR}/elf32xtensa.x) + +file(MAKE_DIRECTORY ${LDSCRIPTS_DIR}) +file(WRITE ${LDSCRIPT_FILE} "") + +file(APPEND ${LDSCRIPT_FILE} "INCLUDE ${CMAKE_CURRENT_BINARY_DIR}/${LDSCRIPTS_DIR}/memory_header_linker_script.txt\n") +configure_file( + ${CMAKE_CURRENT_LIST_DIR}/ldscripts/memory_header_linker_script.txt.in + ${CMAKE_CURRENT_BINARY_DIR}/${LDSCRIPTS_DIR}/memory_header_linker_script.txt +) + +file(APPEND ${LDSCRIPT_FILE} "INCLUDE ${CMAKE_CURRENT_LIST_DIR}/ldscripts/text_linker_script.txt\n") +file(APPEND ${LDSCRIPT_FILE} "INCLUDE ${CMAKE_CURRENT_LIST_DIR}/ldscripts/common_text_linker_script.txt\n") +file(APPEND ${LDSCRIPT_FILE} "INCLUDE ${CMAKE_CURRENT_LIST_DIR}/ldscripts/data_linker_script.txt\n") +file(APPEND ${LDSCRIPT_FILE} "INCLUDE ${CMAKE_CURRENT_LIST_DIR}/ldscripts/common_rodata_linker_script.txt\n") +file(APPEND ${LDSCRIPT_FILE} "INCLUDE ${CMAKE_CURRENT_LIST_DIR}/ldscripts/bss_linker_script.txt\n") +file(APPEND ${LDSCRIPT_FILE} "INCLUDE ${CMAKE_CURRENT_LIST_DIR}/ldscripts/xt_linker_script.txt\n") +file(APPEND ${LDSCRIPT_FILE} "INCLUDE ${CMAKE_CURRENT_LIST_DIR}/ldscripts/guard_linker_script.txt\n") diff --git a/lmdk/cmake/ldscripts/bss_linker_script.txt b/lmdk/cmake/ldscripts/bss_linker_script.txt new file mode 100644 index 000000000000..c214d4662978 --- /dev/null +++ b/lmdk/cmake/ldscripts/bss_linker_script.txt @@ -0,0 +1,20 @@ + +PHDRS { + bss_phdr PT_LOAD; +} + +SECTIONS { + max_instances = 1; + max_instances-1 = max_instances - 1; + + .bss (NOLOAD) : ALIGN(4096) { + _first_start = ABSOLUTE(.); + *(.first) + _first_end = ABSOLUTE(.); + _next_start = ABSOLUTE(.); + . += (_first_end - _first_start) * max_instances-1; + _next_end = ABSOLUTE(.); + *(.bss) + *(.bss.*) + } >HPSRAM_seg : bss_phdr +} diff --git a/lmdk/cmake/ldscripts/common_rodata_linker_script.txt b/lmdk/cmake/ldscripts/common_rodata_linker_script.txt new file mode 100644 index 000000000000..2f3e4bfad4d9 --- /dev/null +++ b/lmdk/cmake/ldscripts/common_rodata_linker_script.txt @@ -0,0 +1,38 @@ + +PHDRS { + rodata_phdr PT_LOAD; +} + +SECTIONS { + .common.rodata : ALIGN(4096) { + _rodata_start = ABSOLUTE(.); + *(.rodata) + *(.rodata.*) + *(.rodata1) + __XT_EXCEPTION_TABLE__ = ABSOLUTE(.); + KEEP (*(.xt_except_table)) + KEEP (*(.gcc_except_table)) + *(.gnu.linkonce.e.*) + *(.gnu.version_r) + KEEP (*(.eh_frame)) + /* C++ constructor and destructor tables properly ordered: */ + KEEP (*crtbegin.o(.ctors)) + KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + KEEP (*crtbegin.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + /* C++ exception handlers table: */ + __XT_EXCEPTION_DESCS__ = ABSOLUTE(.); + *(.xt_except_desc) + *(.gnu.linkonce.h.*) + __XT_EXCEPTION_DESCS_END__ = ABSOLUTE(.); + *(.xt_except_desc_end) + *(.dynamic) + *(.gnu.version_d) + . = ALIGN(4); /* this table MUST be 4-byte aligned */ + _rodata_end = ABSOLUTE(.); + } >HPSRAM_seg : rodata_phdr +} diff --git a/lmdk/cmake/ldscripts/common_text_linker_script.txt b/lmdk/cmake/ldscripts/common_text_linker_script.txt new file mode 100644 index 000000000000..10468dc4bf3a --- /dev/null +++ b/lmdk/cmake/ldscripts/common_text_linker_script.txt @@ -0,0 +1,24 @@ + +PHDRS { + text_phdr PT_LOAD; +} + +SECTIONS { + .common.text : ALIGN(4096){ + _common_text_start = ABSOLUTE(.); + *(.entry.text) + *(.init.literal) + KEEP(*(.init)) + *(.literal .text) + *(.literal.* .text.*) + *(.stub) + *(.gnu.warning) + *(.gnu.linkonce.literal*) + *(.gnu.linkonce.t.*.literal*) + *(.gnu.linkonce.t*) + *(.fini.literal) + KEEP(*(.fini)) + *(.gnu.version) + _common_text_end = ABSOLUTE(.); + } >HPSRAM_seg : text_phdr +} diff --git a/lmdk/cmake/ldscripts/data_linker_script.txt b/lmdk/cmake/ldscripts/data_linker_script.txt new file mode 100644 index 000000000000..08b75bbf5b53 --- /dev/null +++ b/lmdk/cmake/ldscripts/data_linker_script.txt @@ -0,0 +1,30 @@ + +PHDRS { + data_phdr PT_LOAD; + rodata_phdr PT_LOAD; +} + +EXTERN(HPSRAM) + +SECTIONS { + .data : ALIGN(4096) { + _data_start = ABSOLUTE(.); + *(.data) + *(.data.*) + _data_end = ABSOLUTE(.); + } >HPSRAM_seg : data_phdr + + .rodata : ALIGN(4096) { + _rodata_start = ABSOLUTE(.); + *(.gnu.linkonce.r.*) + *(.rodata) + *(.rodata.*) + KEEP (*(.eh_frame)) + _rodata_end = ABSOLUTE(.); + } >HPSRAM_seg : rodata_phdr + + /* Module manifest is here */ + .module : ALIGN(4096) { + KEEP(*(.module)) + } >HPSRAM_seg : rodata_phdr +} diff --git a/lmdk/cmake/ldscripts/guard_linker_script.txt b/lmdk/cmake/ldscripts/guard_linker_script.txt new file mode 100644 index 000000000000..4d0ea5f03dbe --- /dev/null +++ b/lmdk/cmake/ldscripts/guard_linker_script.txt @@ -0,0 +1,12 @@ + +PHDRS { + guard_phdr PT_LOAD; +} + +SECTIONS { + .guard : ALIGN(4096) { + _guard_section_start = ABSOLUTE(.); + *(.*) + _guard_section_end = ABSOLUTE(.); + } >HPSRAM_seg : guard_phdr +} diff --git a/lmdk/cmake/ldscripts/memory_header_linker_script.txt.in b/lmdk/cmake/ldscripts/memory_header_linker_script.txt.in new file mode 100644 index 000000000000..c505a503c2fb --- /dev/null +++ b/lmdk/cmake/ldscripts/memory_header_linker_script.txt.in @@ -0,0 +1,8 @@ + +MEMORY { + HPSRAM_seg : org = ${HPSRAM}, len = 0xFFFFFFFF +} + +PHDRS { + HPSRAM_phdr PT_LOAD; +} diff --git a/lmdk/cmake/ldscripts/text_linker_script.txt b/lmdk/cmake/ldscripts/text_linker_script.txt new file mode 100644 index 000000000000..cd4412efccae --- /dev/null +++ b/lmdk/cmake/ldscripts/text_linker_script.txt @@ -0,0 +1,31 @@ + +/* EXTERN(${PACKAGE_ENTRY_POINT}) */ + +PHDRS { + text_phdr PT_LOAD; + cmi_text_phdr PT_LOAD; +} + +/* .buildinfo should be put at the beginning of .text segment! API version is defined there. */ + +SECTIONS { + .text : ALIGN(4096) { + _text_start = ABSOLUTE(.); + *(.buildinfo) + *(.gnu.linkonce.literal.*) + *(.gnu.linkonce.lit4) + *(.literal) + *(.literal.*) + *(.gnu.linkonce.t*) + *(.text) + *(.text.*) + *(.cmi.literal) + _text_end = ABSOLUTE(.); + } >HPSRAM_seg : text_phdr + + .cmi.text : ALIGN(4096) { + _cmi_text_start = ABSOLUTE(.); + *(.cmi.text) + _cmi_text_end = ABSOLUTE(.); + } >HPSRAM_seg : cmi_text_phdr +} diff --git a/lmdk/cmake/ldscripts/xt_linker_script.txt b/lmdk/cmake/ldscripts/xt_linker_script.txt new file mode 100644 index 000000000000..75137467376f --- /dev/null +++ b/lmdk/cmake/ldscripts/xt_linker_script.txt @@ -0,0 +1,55 @@ + +SECTIONS { + .debug 0 : { *(.debug) } + .line 0 : { *(.line) } + .xtensa.info 0 : { *(.xtensa.info) } + .comment 0 : { *(.comment) } + .debug_ranges 0 : { *(.debug_ranges) } + .debug_srcinfo 0 : { *(.debug_srcinfo) } + .debug_sfnames 0 : { *(.debug_sfnames) } + .debug_aranges 0 : { *(.debug_aranges) } + .debug_pubnames 0 : { *(.debug_pubnames) } + .debug_info 0 : { *(.debug_info) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_line 0 : { *(.debug_line) } + .debug_frame 0 : { *(.debug_frame) } + .debug_str 0 : { *(.debug_str) } + .debug_loc 0 : { *(.debug_loc) } + .debug_macinfo 0 : { *(.debug_macinfo) } + .debug_weaknames 0 : { *(.debug_weaknames) } + .debug_funcnames 0 : { *(.debug_funcnames) } + .debug_typenames 0 : { *(.debug_typenames) } + .debug_varnames 0 : { *(.debug_varnames) } + .xt.insn 0 : + { + KEEP (*(.xt.insn)) + KEEP (*(.gnu.linkonce.x.*)) + } + .xt.prop 0 : + { + KEEP (*(.xt.prop)) + KEEP (*(.xt.prop.*)) + KEEP (*(.gnu.linkonce.prop.*)) + } + .xt.lit 0 : + { + KEEP (*(.xt.lit)) + KEEP (*(.xt.lit.*)) + KEEP (*(.gnu.linkonce.p.*)) + } + .xt.profile_range 0 : + { + KEEP (*(.xt.profile_range)) + KEEP (*(.gnu.linkonce.profile_range.*)) + } + .xt.profile_ranges 0 : + { + KEEP (*(.xt.profile_ranges)) + KEEP (*(.gnu.linkonce.xt.profile_ranges.*)) + } + .xt.profile_files 0 : + { + KEEP (*(.xt.profile_files)) + KEEP (*(.gnu.linkonce.xt.profile_files.*)) + } +} diff --git a/lmdk/cmake/xtensa-toolchain.cmake b/lmdk/cmake/xtensa-toolchain.cmake new file mode 100644 index 000000000000..6cda6274aeb8 --- /dev/null +++ b/lmdk/cmake/xtensa-toolchain.cmake @@ -0,0 +1,25 @@ +# Xtensa CMake toolchain file. Apply it using CMAKE_TOOLCHAIN_FILE variable. + +set(CMAKE_SYSTEM_NAME Generic) +set(CMAKE_SYSTEM_VERSION 1) + +cmake_path(CONVERT $ENV{XTENSA_TOOLCHAIN_PATH} TO_CMAKE_PATH_LIST XTENSA_TOOLCHAIN_PATH) +set(TOOLCHAIN_BASE ${XTENSA_TOOLCHAIN_PATH}/XtensaTools) +set(CROSS_COMPILE ${TOOLCHAIN_BASE}/bin/xt-) + +# clang or xcc +find_program(CMAKE_C_COMPILER NAMES ${CROSS_COMPILE}xcc NO_DEFAULT_PATH) +# clang++ or xc++ +find_program(CMAKE_CXX_COMPILER NAMES ${CROSS_COMPILE}xc++ NO_DEFAULT_PATH) + +find_program(CMAKE_LD NAMES ${CROSS_COMPILE}ld NO_DEFAULT_PATH) +find_program(CMAKE_AR NAMES ${CROSS_COMPILE}ar NO_DEFAULT_PATH) +find_program(CMAKE_RANLIB NAMES ${CROSS_COMPILE}ranlib NO_DEFAULT_PATH) +find_program(CMAKE_OBJCOPY NAMES ${CROSS_COMPILE}objcopy NO_DEFAULT_PATH) +find_program(CMAKE_OBJDUMP NAMES ${CROSS_COMPILE}objdump NO_DEFAULT_PATH) +find_program(CMAKE_NM NAMES ${CROSS_COMPILE}nm NO_DEFAULT_PATH) + +set(CMAKE_FIND_ROOT_PATH ${TOOLCHAIN_BASE}/xtensa-elf) +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) diff --git a/lmdk/include/todo.txt b/lmdk/include/todo.txt new file mode 100644 index 000000000000..83011e8c8236 --- /dev/null +++ b/lmdk/include/todo.txt @@ -0,0 +1 @@ +TODO: add SOF Loadable Modules Dev Kit headers here \ No newline at end of file diff --git a/lmdk/libraries/dummy/CMakeLists.txt b/lmdk/libraries/dummy/CMakeLists.txt new file mode 100644 index 000000000000..32cbd134dfae --- /dev/null +++ b/lmdk/libraries/dummy/CMakeLists.txt @@ -0,0 +1,19 @@ + +cmake_minimum_required(VERSION 3.20) +set(CMAKE_TOOLCHAIN_FILE "${CMAKE_CURRENT_LIST_DIR}/../../cmake/xtensa-toolchain.cmake") + +project(dummy) + +# list of modules to be built and included into this loadable library +set(MODULES_LIST dummy) + +# toml file for rimage to generate manifets +set(TOML "${CMAKE_CURRENT_LIST_DIR}/dummy_mtl.toml") + +# TODO: Move it somewhere?! This probably should be defined in some API header file! +# SOF loadable modules API version +add_definitions(-DMAJOR_IADSP_API_VERSION=5) +add_definitions(-DMIDDLE_IADSP_API_VERSION=0) +add_definitions(-DMINOR_IADSP_API_VERSION=0) + +include(../../cmake/build.cmake) diff --git a/lmdk/libraries/dummy/dummy_mtl.toml b/lmdk/libraries/dummy/dummy_mtl.toml new file mode 100644 index 000000000000..7f984d2a2382 --- /dev/null +++ b/lmdk/libraries/dummy/dummy_mtl.toml @@ -0,0 +1,86 @@ +version = [3, 0] + +[adsp] +name = "mtl" +image_size = "0x2C0000" # (22) bank * 128KB +alias_mask = "0xE0000000" + +[[adsp.mem_zone]] +type = "ROM" +base = "0x1FF80000" +size = "0x400" +[[adsp.mem_zone]] +type = "IMR" +base = "0xA104A000" +size = "0x2000" +[[adsp.mem_zone]] +type = "SRAM" +base = "0xa00f0000" +size = "0x100000" + +[[adsp.mem_alias]] +type = "uncached" +base = "0x40000000" +[[adsp.mem_alias]] +type = "cached" +base = "0xA0000000" + +[cse] +partition_name = "ADSP" +[[cse.entry]] +name = "ADSP.man" +offset = "0x5c" +length = "0x464" +[[cse.entry]] +name = "ADSP.met" +offset = "0x4c0" +length = "0x70" +[[cse.entry]] +name = "ADSP" +offset = "0x540" +length = "0x0" # calculated by rimage + +[css] + +[signed_pkg] +name = "ADSP" +partition_usage = "0x23" +[[signed_pkg.module]] +name = "ADSP.met" + +[adsp_file] +[[adsp_file.comp]] +base_offset = "0x2000" + +[fw_desc.header] +name = "ADSPFW" +load_offset = "0x40000" + +[module] +count = 1 + + [[module.entry]] + name = "DUMMY" + uuid = "01010101-0101-0101-0101-010101010101" + affinity_mask = "0x1" + instance_count = "15" + domain_types = "0" + load_type = "0" + module_type = "5" + auto_start = "0" + sched_caps = [1, 0x00008000] + + # pin = [dir, type, sample rate, size, container, channel-cfg] + pin = [0, 0, 0xffff, 0xc, 0x8, 0x05ff, + 1, 0, 0xffff, 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, 216, 706000, 12, 16, 0, 0, 0, + 1, 0, 0, 0, 216, 1271000, 8, 8, 0, 0, 0, + 2, 0, 0, 0, 216, 1839000, 89, 118, 0, 0, 0, + 3, 0, 0, 0, 216, 2435000, 48, 64, 0, 0, 0, + 4, 0, 0, 0, 216, 3343000, 192, 192, 0, 0, 0, + 5, 0, 0, 0, 216, 3961000, 177, 177, 0, 0, 0, + 6, 0, 0, 0, 216, 4238000, 192, 256, 0, 0, 0, + 7, 0, 0, 0, 216, 6691000, 192, 256, 0, 0, 0] + diff --git a/lmdk/modules/dummy/CMakeLists.txt b/lmdk/modules/dummy/CMakeLists.txt new file mode 100644 index 000000000000..a4903aa0e39d --- /dev/null +++ b/lmdk/modules/dummy/CMakeLists.txt @@ -0,0 +1,6 @@ + +target_sources(dummy PRIVATE dummy.c) + +set_target_properties(dummy PROPERTIES + HPSRAM_ADDR "0xa06a1000" +) diff --git a/lmdk/modules/dummy/dummy.c b/lmdk/modules/dummy/dummy.c new file mode 100644 index 000000000000..3d8b3e787f07 --- /dev/null +++ b/lmdk/modules/dummy/dummy.c @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// Copyright 2023 Intel Corporation. All rights reserved. + +#include + +/* rimage fails if no .bss section found */ +int bss_workaround; + +#define ADSP_BUILD_INFO_FORMAT 0 + +/* these supposed to be defined in some API header file which is not yet available */ +union adsp_api_version { + uint32_t full; + struct { + uint32_t minor : 10; + uint32_t middle : 10; + uint32_t major : 10; + uint32_t reserved : 2; + } fields; +}; + +struct adsp_build_info { + uint32_t format; + union adsp_api_version api_version_number; +}; + +struct adsp_build_info dummy_build_info __attribute__((section(".buildinfo"))) = { + ADSP_BUILD_INFO_FORMAT, + { + ((0x3FF & MAJOR_IADSP_API_VERSION) << 20) | + ((0x3FF & MIDDLE_IADSP_API_VERSION) << 10) | + ((0x3FF & MINOR_IADSP_API_VERSION) << 0) + } +}; + +__attribute__((section(".cmi.text"))) +void *dummyPackageEntryPoint(int a, int b) +{ + /* supposed to return here a pointer to module interface implementation */ + return (void *)0x12345678; +} + +__attribute__((section(".module"))) +const struct sof_man_module_manifest dummy_module_manifest = { + .module = { + .name = "DUMMY", + .uuid = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, + .entry_point = (uint32_t)dummyPackageEntryPoint, + .type = { + .load_type = SOF_MAN_MOD_TYPE_MODULE, + .domain_ll = 1 + }, + .affinity_mask = 3, + } +}; diff --git a/rimage b/rimage index ab0429fdbe56..a0b0187ce1e9 160000 --- a/rimage +++ b/rimage @@ -1 +1 @@ -Subproject commit ab0429fdbe563ef6abe499c69b2483e96c4762d0 +Subproject commit a0b0187ce1e945869fd5343097282411b7118654 diff --git a/src/audio/copier/copier.c b/src/audio/copier/copier.c index 3f8139f44330..12cc0a9dd96a 100644 --- a/src/audio/copier/copier.c +++ b/src/audio/copier/copier.c @@ -19,7 +19,6 @@ #include #include #include -#include #include #include #include @@ -37,6 +36,11 @@ #include #include #include +#include + +#if CONFIG_ZEPHYR_NATIVE_DRIVERS +#include +#endif static const struct comp_driver comp_copier; @@ -293,6 +297,79 @@ static enum sof_ipc_stream_direction } } +static int init_dai_single(struct comp_dev *parent_dev, + const struct comp_driver *drv, + struct comp_ipc_config *config, + const struct ipc4_copier_module_cfg *copier, + struct pipeline *pipeline, + struct ipc_config_dai *dai, + enum ipc4_gateway_type type) +{ + struct copier_data *cd; + struct dai_data *dd; + int ret; + + cd = comp_get_drvdata(parent_dev); + + if (cd->direction == SOF_IPC_STREAM_PLAYBACK) { + enum sof_ipc_frame out_frame_fmt, out_valid_fmt; + + audio_stream_fmt_conversion(copier->out_fmt.depth, + copier->out_fmt.valid_bit_depth, + &out_frame_fmt, + &out_valid_fmt, + copier->out_fmt.s_type); + config->frame_fmt = out_frame_fmt; + pipeline->sink_comp = parent_dev; + } else { + enum sof_ipc_frame in_frame_fmt, in_valid_fmt; + + audio_stream_fmt_conversion(copier->base.audio_fmt.depth, + copier->base.audio_fmt.valid_bit_depth, + &in_frame_fmt, &in_valid_fmt, + copier->base.audio_fmt.s_type); + config->frame_fmt = in_frame_fmt; + pipeline->source_comp = parent_dev; + } + + parent_dev->ipc_config.frame_fmt = config->frame_fmt; + + dd = rzalloc(SOF_MEM_ZONE_RUNTIME_SHARED, 0, SOF_MEM_CAPS_RAM, sizeof(*dd)); + if (!dd) + return -ENOMEM; + + ret = dai_zephyr_new(dd, parent_dev, dai); + if (ret < 0) + goto e_dd; + + pipeline->sched_id = config->id; + + ret = comp_dai_config(dd, parent_dev, dai, copier); + if (ret < 0) + goto e_zephyr; + + cd->converter[IPC4_COPIER_GATEWAY_PIN] = + get_converter_func(&copier->base.audio_fmt, &copier->out_fmt, type, + IPC4_DIRECTION(dai->direction)); + if (!cd->converter[IPC4_COPIER_GATEWAY_PIN]) { + comp_err(parent_dev, "failed to get converter type %d, dir %d", + type, dai->direction); + ret = -EINVAL; + goto e_zephyr; + } + + cd->endpoint_num++; + cd->dd[0] = dd; + + return 0; + +e_zephyr: + dai_zephyr_free(dd); +e_dd: + rfree(dd); + return ret; +} + static int init_dai(struct comp_dev *parent_dev, const struct comp_driver *drv, struct comp_ipc_config *config, @@ -300,12 +377,15 @@ static int init_dai(struct comp_dev *parent_dev, struct pipeline *pipeline, struct ipc_config_dai *dai, enum ipc4_gateway_type type, - int index) + int index, int dai_count) { struct comp_dev *dev; struct copier_data *cd; int ret; + if (dai_count == 1) + return init_dai_single(parent_dev, drv, config, copier, pipeline, dai, type); + cd = comp_get_drvdata(parent_dev); ret = create_endpoint_buffer(parent_dev, cd, config, copier, type, false, index); if (ret < 0) @@ -313,7 +393,7 @@ static int init_dai(struct comp_dev *parent_dev, dev = drv->ops.create(drv, config, dai); if (!dev) { - ret = -EINVAL; + ret = -ENOMEM; goto e_buf; } @@ -327,9 +407,10 @@ static int init_dai(struct comp_dev *parent_dev, list_init(&dev->bsource_list); list_init(&dev->bsink_list); - ret = comp_dai_config(dev, dai, copier); + cd->dd[index] = comp_get_drvdata(dev); + ret = comp_dai_config(cd->dd[index], dev, dai, copier); if (ret < 0) - goto e_buf; + goto free_dev; if (dai->direction == SOF_IPC_STREAM_PLAYBACK) { comp_buffer_connect(dev, config->core, cd->endpoint_buffer[cd->endpoint_num], @@ -347,13 +428,15 @@ static int init_dai(struct comp_dev *parent_dev, if (!cd->converter[IPC4_COPIER_GATEWAY_PIN]) { comp_err(parent_dev, "failed to get converter type %d, dir %d", type, dai->direction); - return -EINVAL; + ret = -EINVAL; + goto free_dev; } cd->endpoint[cd->endpoint_num++] = dev; return 0; - +free_dev: + drv->ops.free(dev); e_buf: buffer_free(cd->endpoint_buffer[cd->endpoint_num]); return ret; @@ -467,7 +550,8 @@ static int create_dai(struct comp_dev *parent_dev, struct copier_data *cd, int ret; dai.dai_index = dai_index[i]; - ret = init_dai(parent_dev, drv, config, copier, pipeline, &dai, type, i); + ret = init_dai(parent_dev, drv, config, copier, pipeline, &dai, type, i, + dai_count); if (ret) { comp_err(parent_dev, "failed to create dai"); return ret; @@ -710,18 +794,37 @@ static void copier_free(struct comp_dev *dev) struct copier_data *cd = comp_get_drvdata(dev); int i; - if (dev->ipc_config.type == SOF_COMP_HOST && !cd->ipc_gtw) { - host_zephyr_free(cd->hd); - rfree(cd->hd); - } - - for (i = 0; i < cd->endpoint_num; i++) { - if (dev->ipc_config.type != SOF_COMP_HOST || cd->ipc_gtw) { - cd->endpoint[i]->drv->ops.free(cd->endpoint[i]); - buffer_free(cd->endpoint_buffer[i]); + switch (dev->ipc_config.type) { + case SOF_COMP_HOST: + if (!cd->ipc_gtw) { + host_zephyr_free(cd->hd); + rfree(cd->hd); + } else { + /* handle gtw case */ + for (i = 0; i < cd->endpoint_num; i++) { + cd->endpoint[i]->drv->ops.free(cd->endpoint[i]); + buffer_free(cd->endpoint_buffer[i]); + } } + break; + case SOF_COMP_DAI: + if (cd->endpoint_num == 1) { + dai_zephyr_free(cd->dd[0]); + rfree(cd->dd[0]); + } else { + for (i = 0; i < cd->endpoint_num; i++) { + cd->endpoint[i]->drv->ops.free(cd->endpoint[i]); + buffer_free(cd->endpoint_buffer[i]); + } + } + break; + default: + break; } + if (cd->multi_endpoint_buffer) + buffer_free(cd->multi_endpoint_buffer); + rfree(cd); rfree(dev); } @@ -857,6 +960,12 @@ static int copier_prepare(struct comp_dev *dev) return 0; } + if (dev->ipc_config.type == SOF_COMP_DAI && cd->endpoint_num == 1) { + ret = dai_zephyr_config_prepare(cd->dd[0], dev); + if (ret < 0) + return ret; + } + ret = comp_set_state(dev, COMP_TRIGGER_PREPARE); if (ret < 0) return ret; @@ -864,18 +973,36 @@ static int copier_prepare(struct comp_dev *dev) if (ret == COMP_STATUS_STATE_ALREADY_SET) return PPL_STATUS_PATH_STOP; - if (dev->ipc_config.type == SOF_COMP_HOST && !cd->ipc_gtw) { - ret = host_zephyr_prepare(cd->hd); - if (ret < 0) - return ret; - } - - for (i = 0; i < cd->endpoint_num; i++) { - if (dev->ipc_config.type != SOF_COMP_HOST || cd->ipc_gtw) { - ret = cd->endpoint[i]->drv->ops.prepare(cd->endpoint[i]); + switch (dev->ipc_config.type) { + case SOF_COMP_HOST: + if (!cd->ipc_gtw) { + ret = host_zephyr_prepare(cd->hd); if (ret < 0) return ret; + } else { + /* handle gtw case */ + for (i = 0; i < cd->endpoint_num; i++) { + ret = cd->endpoint[i]->drv->ops.prepare(cd->endpoint[i]); + if (ret < 0) + return ret; + } } + break; + case SOF_COMP_DAI: + if (cd->endpoint_num == 1) { + ret = dai_zephyr_prepare(cd->dd[0], dev); + if (ret < 0) + return ret; + } else { + for (i = 0; i < cd->endpoint_num; i++) { + ret = cd->endpoint[i]->drv->ops.prepare(cd->endpoint[i]); + if (ret < 0) + return ret; + } + } + break; + default: + break; } if (!cd->endpoint_num) { @@ -921,27 +1048,38 @@ static int copier_reset(struct comp_dev *dev) { struct copier_data *cd = comp_get_drvdata(dev); struct ipc4_pipeline_registers pipe_reg; - int ret = 0; - int i; + int i, ret = 0; comp_dbg(dev, "copier_reset()"); cd->input_total_data_processed = 0; cd->output_total_data_processed = 0; - if (dev->ipc_config.type == SOF_COMP_HOST && !cd->ipc_gtw) { - if (cd->hd->chan) - notifier_unregister(dev, - cd->hd->chan, NOTIFIER_ID_DMA_COPY); - host_zephyr_reset(cd->hd, dev->state); - } - - for (i = 0; i < cd->endpoint_num; i++) { - if (dev->ipc_config.type != SOF_COMP_HOST || cd->ipc_gtw) { - ret = cd->endpoint[i]->drv->ops.reset(cd->endpoint[i]); - if (ret < 0) - break; + switch (dev->ipc_config.type) { + case SOF_COMP_HOST: + if (!cd->ipc_gtw) { + host_zephyr_reset(cd->hd, dev->state); + } else { + for (i = 0; i < cd->endpoint_num; i++) { + ret = cd->endpoint[i]->drv->ops.reset(cd->endpoint[i]); + if (ret < 0) + break; + } + } + break; + case SOF_COMP_DAI: + if (cd->endpoint_num == 1) { + dai_zephyr_reset(cd->dd[0], dev); + } else { + for (i = 0; i < cd->endpoint_num; i++) { + ret = cd->endpoint[i]->drv->ops.reset(cd->endpoint[i]); + if (ret < 0) + break; + } } + break; + default: + break; } if (cd->pipeline_reg_offset) { @@ -960,7 +1098,6 @@ static int copier_comp_trigger(struct comp_dev *dev, int cmd) struct copier_data *cd = comp_get_drvdata(dev); struct sof_ipc_stream_posn posn; struct comp_dev *dai_copier; - struct copier_data *dai_cd; struct comp_buffer *buffer; struct comp_buffer __sparse_cache *buffer_c; uint32_t latency; @@ -968,30 +1105,54 @@ static int copier_comp_trigger(struct comp_dev *dev, int cmd) comp_dbg(dev, "copier_comp_trigger()"); - ret = comp_set_state(dev, cmd); - if (ret < 0) - return ret; - - if (ret == COMP_STATUS_STATE_ALREADY_SET) - return PPL_STATUS_PATH_STOP; - - if (dev->ipc_config.type == SOF_COMP_HOST && !cd->ipc_gtw) { - ret = host_zephyr_trigger(cd->hd, dev, cmd); + /* + * do not modify the comp state in case of single endpoint DAI, it will be done in + * dai_zephyr_trigger() + */ + if (!(dev->ipc_config.type == SOF_COMP_DAI && cd->endpoint_num == 1)) { + ret = comp_set_state(dev, cmd); if (ret < 0) return ret; + + if (ret == COMP_STATUS_STATE_ALREADY_SET) + return PPL_STATUS_PATH_STOP; } - for (i = 0; i < cd->endpoint_num; i++) { - if (dev->ipc_config.type != SOF_COMP_HOST || cd->ipc_gtw) { - ret = cd->endpoint[i]->drv->ops.trigger(cd->endpoint[i], cmd); + switch (dev->ipc_config.type) { + case SOF_COMP_HOST: + if (!cd->ipc_gtw) { + ret = host_zephyr_trigger(cd->hd, dev, cmd); if (ret < 0) - break; + return ret; + } else { + /* handle gtw case */ + for (i = 0; i < cd->endpoint_num; i++) { + ret = cd->endpoint[i]->drv->ops.trigger(cd->endpoint[i], cmd); + if (ret < 0) + return ret; + } } + break; + case SOF_COMP_DAI: + if (cd->endpoint_num == 1) { + ret = dai_zephyr_trigger(cd->dd[0], dev, cmd); + if (ret < 0) + return ret; + } else { + for (i = 0; i < cd->endpoint_num; i++) { + ret = cd->endpoint[i]->drv->ops.trigger(cd->endpoint[i], cmd); + if (ret < 0) + return ret; + } + } + break; + default: + break; } /* For capture cd->pipeline_reg_offset == 0 */ - if (ret < 0 || !cd->endpoint_num || !cd->pipeline_reg_offset) - return ret; + if (!cd->endpoint_num || !cd->pipeline_reg_offset) + return 0; dai_copier = pipeline_get_dai_comp_latency(dev->pipeline->pipeline_id, &latency); if (!dai_copier) { @@ -1004,10 +1165,8 @@ static int copier_comp_trigger(struct comp_dev *dev, int cmd) return 0; } - dai_cd = comp_get_drvdata(dai_copier); /* dai is in another pipeline and it is not prepared or active */ - if (dai_copier->state <= COMP_STATE_READY || - dai_cd->endpoint[IPC4_COPIER_GATEWAY_PIN]->state <= COMP_STATE_READY) { + if (dai_copier->state <= COMP_STATE_READY) { struct ipc4_pipeline_registers pipe_reg; comp_warn(dev, "dai is not ready"); @@ -1188,9 +1347,12 @@ static int mux_into_multi_endpoint_buffer(struct copier_data *cd) return 0; } +static void copier_dma_cb(struct comp_dev *dev, size_t bytes); + static int do_endpoint_copy(struct comp_dev *dev) { struct copier_data *cd = comp_get_drvdata(dev); + if (cd->multi_endpoint_buffer) { int i; int ret = 0; @@ -1214,9 +1376,18 @@ static int do_endpoint_copy(struct comp_dev *dev) return ret; } else { - if (dev->ipc_config.type == SOF_COMP_HOST && !cd->ipc_gtw) - return host_zephyr_copy(cd->hd, dev); - + switch (dev->ipc_config.type) { + case SOF_COMP_HOST: + if (!cd->ipc_gtw) + return host_zephyr_copy(cd->hd, dev, copier_dma_cb); + break; + case SOF_COMP_DAI: + if (cd->endpoint_num == 1) + return dai_zephyr_copy(cd->dd[0], dev, cd->converter); + break; + default: + break; + } return cd->endpoint[0]->drv->ops.copy(cd->endpoint[0]); } } @@ -1278,8 +1449,18 @@ static int copier_copy(struct comp_dev *dev) comp_dbg(dev, "copier_copy()"); - if (dev->ipc_config.type == SOF_COMP_HOST && !cd->ipc_gtw) - return do_endpoint_copy(dev); + switch (dev->ipc_config.type) { + case SOF_COMP_HOST: + if (!cd->ipc_gtw) + return do_endpoint_copy(dev); + break; + case SOF_COMP_DAI: + if (cd->endpoint_num == 1) + return do_endpoint_copy(dev); + break; + default: + break; + } processed_data.source_bytes = 0; @@ -1382,13 +1563,10 @@ static void update_buffer_format(struct comp_buffer __sparse_cache *buf_c, /* This is called by DMA driver every time when DMA completes its current * transfer between host and DSP. */ -static void copier_dma_cb(void *arg, enum notify_id type, void *data) +static void copier_dma_cb(struct comp_dev *dev, size_t bytes) { - struct dma_cb_data *next = data; - struct comp_dev *dev = arg; struct copier_data *cd = comp_get_drvdata(dev); struct comp_buffer __sparse_cache *sink; - uint32_t bytes = next->elem.size; int ret, frames; comp_dbg(dev, "copier_dma_cb() %p", dev); @@ -1419,15 +1597,25 @@ static void copier_dma_cb(void *arg, enum notify_id type, void *data) } } +static void copier_notifier_cb(void *arg, enum notify_id type, void *data) +{ + struct dma_cb_data *next = data; + uint32_t bytes = next->elem.size; + + copier_dma_cb(arg, bytes); +} + /* configure the DMA params */ static int copier_params(struct comp_dev *dev, struct sof_ipc_stream_params *params) { struct copier_data *cd = comp_get_drvdata(dev); + const struct ipc4_audio_format *in_fmt = &cd->config.base.audio_fmt; + const struct ipc4_audio_format *out_fmt = &cd->config.out_fmt; struct comp_buffer *sink, *source; struct comp_buffer __sparse_cache *sink_c, *source_c; struct list_item *sink_list; - int ret = 0; - int i; + enum sof_ipc_frame in_bits, in_valid_bits, out_bits, out_valid_bits; + int i, ret = 0; comp_dbg(dev, "copier_params()"); @@ -1475,8 +1663,27 @@ static int copier_params(struct comp_dev *dev, struct sof_ipc_stream_params *par buffer_release(source_c); } + /* update params for the DMA buffer */ + switch (dev->ipc_config.type) { + case SOF_COMP_HOST: + if (cd->ipc_gtw || params->direction == SOF_IPC_STREAM_PLAYBACK) + break; + COMPILER_FALLTHROUGH; + case SOF_COMP_DAI: + if (dev->ipc_config.type == SOF_COMP_DAI && + (cd->endpoint_num > 1 || params->direction == SOF_IPC_STREAM_CAPTURE)) + break; + params->buffer.size = cd->config.base.obs; + params->sample_container_bytes = cd->out_fmt->depth / 8; + params->sample_valid_bytes = cd->out_fmt->valid_bit_depth / 8; + break; + default: + break; + } + for (i = 0; i < cd->endpoint_num; i++) { - update_internal_comp(dev, cd->endpoint[i]); + if (cd->endpoint[i]) + update_internal_comp(dev, cd->endpoint[i]); /* For ALH multi-gateway case, params->channels is a total multiplexed * number of channels. Demultiplexed number of channels for each individual @@ -1493,25 +1700,50 @@ static int copier_params(struct comp_dev *dev, struct sof_ipc_stream_params *par ret = cd->endpoint[i]->drv->ops.params(cd->endpoint[i], &demuxed_params); } else { - if (dev->ipc_config.type == SOF_COMP_HOST && !cd->ipc_gtw) { - component_set_nearest_period_frames(dev, params->rate); - if (params->direction == SOF_IPC_STREAM_CAPTURE) { - params->buffer.size = cd->config.base.obs; - params->sample_container_bytes = cd->out_fmt->depth / 8; - params->sample_valid_bytes = - cd->out_fmt->valid_bit_depth / 8; + switch (dev->ipc_config.type) { + case SOF_COMP_HOST: + if (!cd->ipc_gtw) { + component_set_nearest_period_frames(dev, params->rate); + ret = host_zephyr_params(cd->hd, dev, params, + copier_notifier_cb); + + cd->hd->process = cd->converter[IPC4_COPIER_GATEWAY_PIN]; + } else { + /* handle gtw case */ + ret = cd->endpoint[i]->drv->ops.params(cd->endpoint[i], + params); } - - ret = host_zephyr_params(cd->hd, dev, params); - if (ret >= 0) - /* set up callback */ - notifier_register(dev, cd->hd->chan, - NOTIFIER_ID_DMA_COPY, copier_dma_cb, 0); - - cd->hd->process = cd->converter[IPC4_COPIER_GATEWAY_PIN]; - } else { - ret = cd->endpoint[i]->drv->ops.params(cd->endpoint[i], - params); + break; + case SOF_COMP_DAI: + if (cd->endpoint_num == 1) { + ret = dai_zephyr_params(cd->dd[0], dev, params); + + /* + * dai_zephyr_params assigns the conversion function + * based on the input/output formats but does not take + * the valid bits into account. So change the conversion + * function if the valid bits are different from the + * container size. + */ + audio_stream_fmt_conversion(in_fmt->depth, + in_fmt->valid_bit_depth, + &in_bits, &in_valid_bits, + in_fmt->s_type); + audio_stream_fmt_conversion(out_fmt->depth, + out_fmt->valid_bit_depth, + &out_bits, &out_valid_bits, + out_fmt->s_type); + + if (in_bits != in_valid_bits || out_bits != out_valid_bits) + cd->dd[0]->process = + cd->converter[IPC4_COPIER_GATEWAY_PIN]; + } else { + ret = cd->endpoint[i]->drv->ops.params(cd->endpoint[i], + params); + } + break; + default: + break; } } if (ret < 0) @@ -1647,11 +1879,17 @@ static int copier_get_large_config(struct comp_dev *dev, uint32_t param_id, struct sof_ipc_stream_posn posn; struct ipc4_llp_reading_extended llp_ext; struct ipc4_llp_reading llp; + struct comp_dev *temp_dev; + + if (dev->ipc_config.type == SOF_COMP_DAI && cd->endpoint_num == 1) + temp_dev = dev; + else + temp_dev = cd->endpoint[IPC4_COPIER_GATEWAY_PIN]; switch (param_id) { case IPC4_COPIER_MODULE_CFG_PARAM_LLP_READING: if (!cd->endpoint_num || - comp_get_endpoint_type(cd->endpoint[IPC4_COPIER_GATEWAY_PIN]) != + comp_get_endpoint_type(temp_dev) != COMP_ENDPOINT_DAI) { comp_err(dev, "Invalid component type"); return -EINVAL; @@ -1665,13 +1903,13 @@ static int copier_get_large_config(struct comp_dev *dev, uint32_t param_id, *data_offset = sizeof(struct ipc4_llp_reading); memset(&llp, 0, sizeof(llp)); - if (cd->endpoint[IPC4_COPIER_GATEWAY_PIN]->state != COMP_STATE_ACTIVE) { + if (temp_dev->state != COMP_STATE_ACTIVE) { memcpy_s(data, sizeof(llp), &llp, sizeof(llp)); return 0; } /* get llp from dai */ - comp_position(cd->endpoint[IPC4_COPIER_GATEWAY_PIN], &posn); + comp_position(temp_dev, &posn); 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); @@ -1681,7 +1919,7 @@ static int copier_get_large_config(struct comp_dev *dev, uint32_t param_id, case IPC4_COPIER_MODULE_CFG_PARAM_LLP_READING_EXTENDED: if (!cd->endpoint_num || - comp_get_endpoint_type(cd->endpoint[IPC4_COPIER_GATEWAY_PIN]) != + comp_get_endpoint_type(temp_dev) != COMP_ENDPOINT_DAI) { comp_err(dev, "Invalid component type"); return -EINVAL; @@ -1695,13 +1933,13 @@ static int copier_get_large_config(struct comp_dev *dev, uint32_t param_id, *data_offset = sizeof(struct ipc4_llp_reading_extended); memset(&llp_ext, 0, sizeof(llp_ext)); - if (cd->endpoint[IPC4_COPIER_GATEWAY_PIN]->state != COMP_STATE_ACTIVE) { + if (temp_dev->state != COMP_STATE_ACTIVE) { memcpy_s(data, sizeof(llp_ext), &llp_ext, sizeof(llp_ext)); return 0; } /* get llp from dai */ - comp_position(cd->endpoint[IPC4_COPIER_GATEWAY_PIN], &posn); + comp_position(temp_dev, &posn); convert_u64_to_u32s(posn.comp_posn, &llp_ext.llp_reading.llp_l, &llp_ext.llp_reading.llp_u); @@ -1725,6 +1963,7 @@ static uint64_t copier_get_processed_data(struct comp_dev *dev, uint32_t stream_ { struct copier_data *cd = comp_get_drvdata(dev); uint64_t ret = 0; + bool source; /* * total data processed is calculated as: accumulate all input data in bytes @@ -1737,14 +1976,28 @@ static uint64_t copier_get_processed_data(struct comp_dev *dev, uint32_t stream_ */ if (cd->endpoint_num) { if (stream_no < cd->endpoint_num) { - if (dev->ipc_config.type == SOF_COMP_HOST && !cd->ipc_gtw) { - bool source = dev->direction == SOF_IPC_STREAM_PLAYBACK; - - if (source == input) + switch (dev->ipc_config.type) { + case SOF_COMP_HOST: + source = dev->direction == SOF_IPC_STREAM_PLAYBACK; + if (cd->ipc_gtw) { + struct comp_dev *host_dev; + + host_dev = cd->endpoint[stream_no]; + ret = comp_get_total_data_processed(host_dev, + 0, input); + } else if (source == input) { ret = cd->hd->total_data_processed; - } else { + } + break; + case SOF_COMP_DAI: + source = dev->direction == SOF_IPC_STREAM_CAPTURE; + if (source == input) + ret = cd->dd[0]->total_data_processed; + break; + default: ret = comp_get_total_data_processed(cd->endpoint[stream_no], 0, input); + break; } } } else { @@ -1777,22 +2030,86 @@ static int copier_get_attribute(struct comp_dev *dev, uint32_t type, void *value static int copier_position(struct comp_dev *dev, struct sof_ipc_stream_posn *posn) { struct copier_data *cd = comp_get_drvdata(dev); - int ret; + int ret = 0; /* Exit if no endpoints */ if (!cd->endpoint_num) return -EINVAL; - if (dev->ipc_config.type == SOF_COMP_HOST && !cd->ipc_gtw) { - posn->host_posn = cd->hd->local_pos; - ret = posn->host_posn; - } else { - ret = comp_position(cd->endpoint[IPC4_COPIER_GATEWAY_PIN], posn); + switch (dev->ipc_config.type) { + case SOF_COMP_HOST: + if (!cd->ipc_gtw) { + posn->host_posn = cd->hd->local_pos; + ret = posn->host_posn; + } else { + /* handle gtw case */ + ret = comp_position(cd->endpoint[IPC4_COPIER_GATEWAY_PIN], posn); + } + break; + case SOF_COMP_DAI: + if (cd->endpoint_num == 1) + ret = dai_zephyr_position(cd->dd[0], dev, posn); + else + ret = dai_zephyr_position(cd->dd[0], cd->endpoint[IPC4_COPIER_GATEWAY_PIN], + posn); + break; + default: + break; } /* Return position from the default gateway pin */ return ret; } +static int copier_dai_ts_config_op(struct comp_dev *dev) +{ + struct copier_data *cd = comp_get_drvdata(dev); + struct dai_data *dd = cd->dd[0]; + + return dai_zephyr_ts_config_op(dd, dev); +} + +static int copier_dai_ts_start_op(struct comp_dev *dev) +{ + struct copier_data *cd = comp_get_drvdata(dev); + struct dai_data *dd = cd->dd[0]; + + comp_dbg(dev, "dai_ts_start()"); + + return dai_zephyr_ts_start(dd, dev); +} + +static int copier_dai_ts_get_op(struct comp_dev *dev, struct timestamp_data *tsd) +{ + struct copier_data *cd = comp_get_drvdata(dev); + struct dai_data *dd = cd->dd[0]; + + comp_dbg(dev, "dai_ts_get()"); + + return dai_zephyr_ts_get(dd, dev, tsd); +} + +static int copier_dai_ts_stop_op(struct comp_dev *dev) +{ + struct copier_data *cd = comp_get_drvdata(dev); + struct dai_data *dd = cd->dd[0]; + + comp_dbg(dev, "dai_ts_stop()"); + + return dai_zephyr_ts_stop(dd, dev); +} + +static int copier_get_hw_params(struct comp_dev *dev, struct sof_ipc_stream_params *params, + int dir) +{ + struct copier_data *cd = comp_get_drvdata(dev); + struct dai_data *dd = cd->dd[0]; + + if (dev->ipc_config.type != SOF_COMP_DAI) + return -EINVAL; + + return dai_zephyr_get_hw_params(dd, dev, params, dir); +} + static const struct comp_driver comp_copier = { .uid = SOF_RT_UUID(copier_comp_uuid), .tctx = &copier_comp_tr, @@ -1809,6 +2126,11 @@ static const struct comp_driver comp_copier = { .get_total_data_processed = copier_get_processed_data, .get_attribute = copier_get_attribute, .position = copier_position, + .dai_ts_config = copier_dai_ts_config_op, + .dai_ts_start = copier_dai_ts_start_op, + .dai_ts_stop = copier_dai_ts_stop_op, + .dai_ts_get = copier_dai_ts_get_op, + .dai_get_hw_params = copier_get_hw_params, }, }; diff --git a/src/audio/dai-legacy.c b/src/audio/dai-legacy.c index dafefecda260..98d1c1e99673 100644 --- a/src/audio/dai-legacy.c +++ b/src/audio/dai-legacy.c @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -46,7 +47,7 @@ DECLARE_TR_CTX(dai_comp_tr, SOF_UUID(dai_comp_uuid), LOG_LEVEL_INFO); #if CONFIG_COMP_DAI_GROUP -static int dai_comp_trigger_internal(struct comp_dev *dev, int cmd); +static int dai_comp_trigger_internal(struct dai_data *dd, struct comp_dev *dev, int cmd); static void dai_atomic_trigger(void *arg, enum notify_id type, void *data) { @@ -55,14 +56,12 @@ static void dai_atomic_trigger(void *arg, enum notify_id type, void *data) struct dai_group *group = dd->group; /* Atomic context set by the last DAI to receive trigger command */ - group->trigger_ret = dai_comp_trigger_internal(dev, group->trigger_cmd); + group->trigger_ret = dai_comp_trigger_internal(dd, dev, group->trigger_cmd); } /* Assign DAI to a group */ -int dai_assign_group(struct comp_dev *dev, uint32_t group_id) +int dai_assign_group(struct dai_data *dd, struct comp_dev *dev, uint32_t group_id) { - struct dai_data *dd = comp_get_drvdata(dev); - if (dd->group) { if (dd->group->group_id != group_id) { comp_err(dev, "dai_assign_group(), DAI already in group %d, requested %d", @@ -160,34 +159,14 @@ static void dai_dma_cb(void *arg, enum notify_id type, void *data) buffer_release(dma_buf); } -static struct comp_dev *dai_new(const struct comp_driver *drv, - const struct comp_ipc_config *config, - const void *spec) +int dai_zephyr_new(struct dai_data *dd, struct comp_dev *dev, const struct ipc_config_dai *dai) { - struct comp_dev *dev; - const struct ipc_config_dai *dai = spec; - struct dai_data *dd; uint32_t dir, caps, dma_dev; - comp_cl_dbg(&comp_dai, "dai_new()"); - - dev = comp_alloc(drv, sizeof(*dev)); - if (!dev) - return NULL; - dev->ipc_config = *config; - - dd = rzalloc(SOF_MEM_ZONE_RUNTIME_SHARED, 0, SOF_MEM_CAPS_RAM, sizeof(*dd)); - if (!dd) { - rfree(dev); - return NULL; - } - - comp_set_drvdata(dev, dd); - dd->dai = dai_get(dai->type, dai->dai_index, DAI_CREAT); if (!dd->dai) { comp_cl_err(&comp_dai, "dai_new(): dai_get() failed to create DAI."); - goto error; + return -ENODEV; } dd->dai->dd = dd; dd->ipc_config = *dai; @@ -202,13 +181,44 @@ static struct comp_dev *dai_new(const struct comp_driver *drv, dd->dma = dma_get(dir, caps, dma_dev, DMA_ACCESS_SHARED); if (!dd->dma) { comp_cl_err(&comp_dai, "dai_new(): dma_get() failed to get shared access to DMA."); - goto error; + return -ENODEV; } dma_sg_init(&dd->config.elem_array); dd->xrun = 0; dd->chan = NULL; + return 0; +} + +static struct comp_dev *dai_new(const struct comp_driver *drv, + const struct comp_ipc_config *config, + const void *spec) +{ + struct comp_dev *dev; + const struct ipc_config_dai *dai = spec; + struct dai_data *dd; + int ret; + + comp_cl_dbg(&comp_dai, "dai_new()"); + + dev = comp_alloc(drv, sizeof(*dev)); + if (!dev) + return NULL; + dev->ipc_config = *config; + + dd = rzalloc(SOF_MEM_ZONE_RUNTIME_SHARED, 0, SOF_MEM_CAPS_RAM, sizeof(*dd)); + if (!dd) { + rfree(dev); + return NULL; + } + + comp_set_drvdata(dev, dd); + + ret = dai_zephyr_new(dd, dev, dai); + if (ret < 0) + goto error; + dev->state = COMP_STATE_READY; return dev; @@ -218,40 +228,46 @@ static struct comp_dev *dai_new(const struct comp_driver *drv, return NULL; } -static void dai_free(struct comp_dev *dev) +void dai_zephyr_free(struct dai_data *dd) { - struct dai_data *dd = comp_get_drvdata(dev); - - if (dd->group) { - notifier_unregister(dev, dd->group, NOTIFIER_ID_DAI_TRIGGER); + if (dd->group) dai_group_put(dd->group); - } if (dd->chan) { - notifier_unregister(dev, dd->chan, NOTIFIER_ID_DMA_COPY); dd->chan->dev_data = NULL; dma_channel_put_legacy(dd->chan); } dma_put(dd->dma); - dai_release_llp_slot(dev); + dai_release_llp_slot(dd); dai_put(dd->dai); if (dd->dai_spec_config) rfree(dd->dai_spec_config); +} + +static void dai_free(struct comp_dev *dev) +{ + struct dai_data *dd = comp_get_drvdata(dev); + + if (dd->group) + notifier_unregister(dev, dd->group, NOTIFIER_ID_DAI_TRIGGER); + + if (dd->chan) + notifier_unregister(dev, dd->chan, NOTIFIER_ID_DMA_COPY); + + dai_zephyr_free(dd); rfree(dd); rfree(dev); } -static int dai_comp_get_hw_params(struct comp_dev *dev, - struct sof_ipc_stream_params *params, - int dir) +int dai_zephyr_get_hw_params(struct dai_data *dd, struct comp_dev *dev, + struct sof_ipc_stream_params *params, int dir) { - struct dai_data *dd = comp_get_drvdata(dev); - int ret = 0; + int ret; comp_dbg(dev, "dai_hw_params()"); @@ -274,6 +290,14 @@ static int dai_comp_get_hw_params(struct comp_dev *dev, return 0; } +static int dai_comp_get_hw_params(struct comp_dev *dev, + struct sof_ipc_stream_params *params, int dir) +{ + struct dai_data *dd = comp_get_drvdata(dev); + + return dai_zephyr_get_hw_params(dd, dev, params, dir); +} + static int dai_comp_hw_params(struct comp_dev *dev, struct sof_ipc_stream_params *params) { @@ -298,7 +322,7 @@ static int dai_verify_params(struct comp_dev *dev, { struct sof_ipc_stream_params hw_params; - dai_comp_get_hw_params(dev, &hw_params, params->direction); + comp_dai_get_hw_params(dev, &hw_params, params->direction); /* checks whether pcm parameters match hardware DAI parameter set * during dai_set_config(). If hardware parameter is equal to 0, it @@ -460,11 +484,10 @@ static int dai_capture_params(struct comp_dev *dev, uint32_t period_bytes, return err; } -static int dai_params(struct comp_dev *dev, +int dai_zephyr_params(struct dai_data *dd, struct comp_dev *dev, struct sof_ipc_stream_params *params) { struct sof_ipc_stream_params hw_params = *params; - struct dai_data *dd = comp_get_drvdata(dev); struct comp_buffer __sparse_cache *buffer_c; uint32_t frame_size; uint32_t period_count; @@ -477,7 +500,7 @@ static int dai_params(struct comp_dev *dev, comp_dbg(dev, "dai_params()"); /* configure dai_data first */ - err = ipc_dai_data_config(dev); + err = ipc_dai_data_config(dd, dev); if (err < 0) return err; @@ -598,14 +621,22 @@ static int dai_params(struct comp_dev *dev, dai_capture_params(dev, period_bytes, period_count); } -static int dai_config_prepare(struct comp_dev *dev) +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()"); + + return dai_zephyr_params(dd, dev, params); +} + +int dai_zephyr_config_prepare(struct dai_data *dd, struct comp_dev *dev) +{ int channel = 0; /* cannot configure DAI while active */ if (dev->state == COMP_STATE_ACTIVE) { - comp_info(dev, "dai_config_prepare(): Component is in active state."); + comp_info(dev, "dai_zephyr_config_prepare(): Component is in active state."); return 0; } @@ -615,13 +646,13 @@ static int dai_config_prepare(struct comp_dev *dev) } if (dd->chan) { - comp_info(dev, "dai_config_prepare(): dma channel index %d already configured", + comp_info(dev, "dai_zephyr_config_prepare(): dma channel index %d already configured", dd->chan->index); return 0; } - channel = dai_config_dma_channel(dev, dd->dai_spec_config); - comp_info(dev, "dai_config_prepare(), channel = %d", channel); + channel = dai_config_dma_channel(dd, dev, dd->dai_spec_config); + comp_info(dev, "dai_zephyr_config_prepare(), channel = %d", channel); /* do nothing for asking for channel free, for compatibility. */ if (channel == DMA_CHAN_INVALID) { @@ -632,14 +663,14 @@ static int dai_config_prepare(struct comp_dev *dev) /* allocate DMA channel */ dd->chan = dma_channel_get_legacy(dd->dma, channel); if (!dd->chan) { - comp_err(dev, "dai_config_prepare(): dma_channel_get() failed"); + comp_err(dev, "dai_zephyr_config_prepare(): dma_channel_get() failed"); dd->chan = NULL; return -EIO; } dd->chan->dev_data = dd; - comp_info(dev, "dai_config_prepare(): new configured dma channel index %d", + comp_info(dev, "dai_zephyr_config_prepare(): new configured dma channel index %d", dd->chan->index); /* setup callback */ @@ -649,24 +680,10 @@ static int dai_config_prepare(struct comp_dev *dev) return 0; } -static int dai_prepare(struct comp_dev *dev) +int dai_zephyr_prepare(struct dai_data *dd, struct comp_dev *dev) { - struct dai_data *dd = comp_get_drvdata(dev); struct comp_buffer __sparse_cache *buffer_c; - int ret = 0; - - comp_info(dev, "dai_prepare()"); - - ret = dai_config_prepare(dev); - if (ret < 0) - return ret; - - 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; + int ret; dd->total_data_processed = 0; @@ -691,7 +708,7 @@ static int dai_prepare(struct comp_dev *dev) if (dd->xrun) { /* after prepare, we have recovered from xrun */ dd->xrun = 0; - return ret; + return 0; } ret = dma_set_config_legacy(dd->chan, &dd->config); @@ -701,19 +718,37 @@ static int dai_prepare(struct comp_dev *dev) return ret; } -static int dai_reset(struct comp_dev *dev) +static int dai_prepare(struct comp_dev *dev) { struct dai_data *dd = comp_get_drvdata(dev); - struct dma_sg_config *config = &dd->config; + int ret; - comp_info(dev, "dai_reset()"); + comp_info(dev, "dai_prepare()"); + + ret = dai_zephyr_config_prepare(dd, dev); + if (ret < 0) + return ret; + + 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; + + return dai_zephyr_prepare(dd, dev); +} + +void dai_zephyr_reset(struct dai_data *dd, struct comp_dev *dev) +{ + struct dma_sg_config *config = &dd->config; /* * DMA channel release should be skipped now for DAI's that support the two-step stop option. * It will be done when the host sends the DAI_CONFIG IPC during hw_free. */ if (!dd->delayed_dma_stop) - dai_dma_release(dev); + dai_dma_release(dd, dev); dma_sg_free(&config->elem_array); @@ -725,23 +760,24 @@ static int dai_reset(struct comp_dev *dev) dd->wallclock = 0; dd->total_data_processed = 0; dd->xrun = 0; - comp_set_state(dev, COMP_TRIGGER_RESET); - - return 0; } -static void dai_update_start_position(struct comp_dev *dev) +static int dai_reset(struct comp_dev *dev) { struct dai_data *dd = comp_get_drvdata(dev); - /* update starting wallclock */ - platform_dai_wallclock(dev, &dd->wallclock); + comp_info(dev, "dai_reset()"); + + dai_zephyr_reset(dd, dev); + + comp_set_state(dev, COMP_TRIGGER_RESET); + + return 0; } /* used to pass standard and bespoke command (with data) to component */ -static int dai_comp_trigger_internal(struct comp_dev *dev, int cmd) +static int dai_comp_trigger_internal(struct dai_data *dd, struct comp_dev *dev, int cmd) { - struct dai_data *dd = comp_get_drvdata(dev); int ret; comp_dbg(dev, "dai_comp_trigger_internal(), command = %u", cmd); @@ -768,7 +804,7 @@ static int dai_comp_trigger_internal(struct comp_dev *dev, int cmd) dd->xrun = 0; } - dai_update_start_position(dev); + platform_dai_wallclock(dev, &dd->wallclock); break; case COMP_TRIGGER_RELEASE: /* before release, we clear the buffer data to 0s, @@ -799,7 +835,7 @@ static int dai_comp_trigger_internal(struct comp_dev *dev, int cmd) dd->xrun = 0; } - dai_update_start_position(dev); + platform_dai_wallclock(dev, &dd->wallclock); break; case COMP_TRIGGER_XRUN: comp_info(dev, "dai_comp_trigger_internal(), XRUN"); @@ -842,17 +878,16 @@ static int dai_comp_trigger_internal(struct comp_dev *dev, int cmd) return ret; } -static int dai_comp_trigger(struct comp_dev *dev, int cmd) +int dai_zephyr_trigger(struct dai_data *dd, struct comp_dev *dev, int cmd) { - struct dai_data *dd = comp_get_drvdata(dev); struct dai_group *group = dd->group; uint32_t irq_flags; int ret = 0; /* DAI not in a group, use normal trigger */ if (!group) { - comp_dbg(dev, "dai_comp_trigger(), non-atomic trigger"); - return dai_comp_trigger_internal(dev, cmd); + comp_dbg(dev, "dai_zephyr_trigger(), non-atomic trigger"); + return dai_comp_trigger_internal(dd, dev, cmd); } /* DAI is grouped, so only trigger when the entire group is ready */ @@ -861,13 +896,13 @@ static int dai_comp_trigger(struct comp_dev *dev, int cmd) /* First DAI to receive the trigger command, * prepare for atomic trigger */ - comp_dbg(dev, "dai_comp_trigger(), begin atomic trigger for group %d", + comp_dbg(dev, "dai_zephyr_trigger(), 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_comp_trigger(), already processing atomic trigger"); + comp_err(dev, "dai_zephyr_trigger(), already processing atomic trigger"); ret = -EAGAIN; } else { /* Count down the number of remaining DAIs required @@ -875,7 +910,7 @@ static int dai_comp_trigger(struct comp_dev *dev, int cmd) * takes place */ group->trigger_counter--; - comp_dbg(dev, "dai_comp_trigger(), trigger counter %d, group %d", + comp_dbg(dev, "dai_zephyr_trigger(), trigger counter %d, group %d", group->trigger_counter, group->group_id); if (!group->trigger_counter) { @@ -898,6 +933,13 @@ static int dai_comp_trigger(struct comp_dev *dev, int cmd) return ret; } +static int dai_comp_trigger(struct comp_dev *dev, int cmd) +{ + struct dai_data *dd = comp_get_drvdata(dev); + + return dai_zephyr_trigger(dd, dev, cmd); +} + /* report xrun occurrence */ static void dai_report_xrun(struct comp_dev *dev, uint32_t bytes) { @@ -916,9 +958,8 @@ static void dai_report_xrun(struct comp_dev *dev, uint32_t bytes) } /* copy and process stream data from source to sink buffers */ -static int dai_copy(struct comp_dev *dev) +int dai_zephyr_copy(struct dai_data *dd, struct comp_dev *dev, pcm_converter_func *converter) { - struct dai_data *dd = comp_get_drvdata(dev); uint32_t dma_fmt; uint32_t sampling; struct comp_buffer __sparse_cache *buf_c; @@ -930,8 +971,6 @@ static int dai_copy(struct comp_dev *dev) uint32_t samples; int ret; - comp_dbg(dev, "dai_copy()"); - /* get data sizes from DMA */ ret = dma_get_data_size_legacy(dd->chan, &avail_bytes, &free_bytes); if (ret < 0) { @@ -966,7 +1005,7 @@ static int dai_copy(struct comp_dev *dev) copy_bytes = samples * sampling; - comp_dbg(dev, "dai_copy(), dir: %d copy_bytes= 0x%x, frames= %d", + comp_dbg(dev, "dai_zephyr_copy(), dir: %d copy_bytes= 0x%x, frames= %d", dev->direction, copy_bytes, samples / buf_c->stream.channels); @@ -975,16 +1014,16 @@ static int dai_copy(struct comp_dev *dev) /* Check possibility of glitch occurrence */ if (dev->direction == SOF_IPC_STREAM_PLAYBACK && copy_bytes + avail_bytes < dd->period_bytes) - comp_warn(dev, "dai_copy(): Copy_bytes %d + avail bytes %d < period bytes %d, possible glitch", + comp_warn(dev, "dai_zephyr_copy(): Copy_bytes %d + avail bytes %d < period bytes %d, possible glitch", copy_bytes, avail_bytes, dd->period_bytes); else if (dev->direction == SOF_IPC_STREAM_CAPTURE && copy_bytes + free_bytes < dd->period_bytes) - comp_warn(dev, "dai_copy(): Copy_bytes %d + free bytes %d < period bytes %d, possible glitch", + comp_warn(dev, "dai_zephyr_copy(): Copy_bytes %d + free bytes %d < period bytes %d, possible glitch", copy_bytes, free_bytes, dd->period_bytes); /* return if nothing to copy */ if (!copy_bytes) { - comp_warn(dev, "dai_copy(): nothing to copy"); + comp_warn(dev, "dai_zephyr_copy(): nothing to copy"); return 0; } @@ -997,11 +1036,24 @@ static int dai_copy(struct comp_dev *dev) return ret; } - dai_dma_position_update(dev); + dai_dma_position_update(dd, dev); return ret; } +static int dai_copy(struct comp_dev *dev) +{ + struct dai_data *dd = comp_get_drvdata(dev); + + comp_dbg(dev, "dai_copy()"); + + /* + * DAI devices will only ever have 1 sink, so no need to pass an array of PCM converter + * functions. The default one to use is set in dd->process. + */ + return dai_zephyr_copy(dd, dev, NULL); +} + /** * \brief Get DAI parameters and configure timestamping * \param[in, out] dev DAI device. @@ -1012,9 +1064,8 @@ static int dai_copy(struct comp_dev *dev) * DAI must be prepared before this function is used (for DMA information). If not, an error * is returned. */ -static int dai_ts_config(struct comp_dev *dev) +int dai_zephyr_ts_config_op(struct dai_data *dd, struct comp_dev *dev) { - struct dai_data *dd = comp_get_drvdata(dev); struct timestamp_cfg *cfg = &dd->ts_config; struct ipc_config_dai *dai = &dd->ipc_config; @@ -1036,39 +1087,64 @@ static int dai_ts_config(struct comp_dev *dev) return dd->dai->drv->ts_ops.ts_config(dd->dai, cfg); } -static int dai_ts_start(struct comp_dev *dev) +static int dai_ts_config(struct comp_dev *dev) { struct dai_data *dd = comp_get_drvdata(dev); - comp_dbg(dev, "dai_ts_start()"); + return dai_zephyr_ts_config_op(dd, dev); +} + +int dai_zephyr_ts_start(struct dai_data *dd, struct comp_dev *dev) +{ if (!dd->dai->drv->ts_ops.ts_start) return -ENXIO; return dd->dai->drv->ts_ops.ts_start(dd->dai, &dd->ts_config); } -static int dai_ts_stop(struct comp_dev *dev) +static int dai_ts_start(struct comp_dev *dev) { struct dai_data *dd = comp_get_drvdata(dev); - comp_dbg(dev, "dai_ts_stop()"); + comp_dbg(dev, "dai_ts_start()"); + + return dai_zephyr_ts_start(dd, dev); +} + +int dai_zephyr_ts_stop(struct dai_data *dd, struct comp_dev *dev) +{ if (!dd->dai->drv->ts_ops.ts_stop) return -ENXIO; return dd->dai->drv->ts_ops.ts_stop(dd->dai, &dd->ts_config); } -static int dai_ts_get(struct comp_dev *dev, struct timestamp_data *tsd) +static int dai_ts_stop(struct comp_dev *dev) { struct dai_data *dd = comp_get_drvdata(dev); - comp_dbg(dev, "dai_ts_get()"); + comp_dbg(dev, "dai_ts_stop()"); + + return dai_zephyr_ts_stop(dd, dev); +} + +int dai_zephyr_ts_get(struct dai_data *dd, struct comp_dev *dev, struct timestamp_data *tsd) +{ if (!dd->dai->drv->ts_ops.ts_get) return -ENXIO; return dd->dai->drv->ts_ops.ts_get(dd->dai, &dd->ts_config, tsd); } +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()"); + + return dai_zephyr_ts_get(dd, dev, tsd); +} + static uint64_t dai_get_processed_data(struct comp_dev *dev, uint32_t stream_no, bool input) { struct dai_data *dd = comp_get_drvdata(dev); diff --git a/src/audio/dai-zephyr.c b/src/audio/dai-zephyr.c index 92a16501a327..68b9baf4c26d 100644 --- a/src/audio/dai-zephyr.c +++ b/src/audio/dai-zephyr.c @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -51,7 +52,7 @@ DECLARE_TR_CTX(dai_comp_tr, SOF_UUID(dai_comp_uuid), LOG_LEVEL_INFO); #if CONFIG_COMP_DAI_GROUP -static int dai_comp_trigger_internal(struct comp_dev *dev, int cmd); +static int dai_comp_trigger_internal(struct dai_data *dd, struct comp_dev *dev, int cmd); static void dai_atomic_trigger(void *arg, enum notify_id type, void *data) { @@ -60,14 +61,12 @@ static void dai_atomic_trigger(void *arg, enum notify_id type, void *data) struct dai_group *group = dd->group; /* Atomic context set by the last DAI to receive trigger command */ - group->trigger_ret = dai_comp_trigger_internal(dev, group->trigger_cmd); + group->trigger_ret = dai_comp_trigger_internal(dd, dev, group->trigger_cmd); } /* Assign DAI to a group */ -int dai_assign_group(struct comp_dev *dev, uint32_t group_id) +int dai_assign_group(struct dai_data *dd, struct comp_dev *dev, uint32_t group_id) { - struct dai_data *dd = comp_get_drvdata(dev); - if (dd->group) { if (dd->group->group_id != group_id) { comp_err(dev, "dai_assign_group(), DAI already in group %d, requested %d", @@ -219,9 +218,10 @@ static int dai_get_fifo(struct dai *dai, int direction, int stream_id) } /* this is called by DMA driver every time descriptor has completed */ -static enum dma_cb_status dai_dma_cb(struct comp_dev *dev, uint32_t bytes) +static enum dma_cb_status +dai_dma_cb(struct dai_data *dd, struct comp_dev *dev, uint32_t bytes, + pcm_converter_func *converter) { - struct dai_data *dd = comp_get_drvdata(dev); struct comp_buffer __sparse_cache *local_buf, *dma_buf; enum dma_cb_status dma_status = DMA_CB_STATUS_RELOAD; int ret; @@ -252,13 +252,69 @@ static enum dma_cb_status dai_dma_cb(struct comp_dev *dev, uint32_t bytes) local_buf = buffer_acquire(dd->local_buffer); - if (dev->direction == SOF_IPC_STREAM_PLAYBACK) + if (dev->direction == SOF_IPC_STREAM_PLAYBACK) { ret = dma_buffer_copy_to(local_buf, dma_buf, dd->process, bytes); - else - ret = dma_buffer_copy_from(dma_buf, local_buf, - dd->process, bytes); + buffer_release(local_buf); + } else { + struct list_item *sink_list; + + audio_stream_invalidate(&dma_buf->stream, bytes); + /* + * The PCM converter functions used during DMA buffer copy can never fail, + * so no need to check the return value of dma_buffer_copy_from_no_consume(). + */ + ret = dma_buffer_copy_from_no_consume(dma_buf, local_buf, dd->process, bytes); + buffer_release(local_buf); +#if CONFIG_IPC_MAJOR_4 + /* Skip in case of endpoint DAI devices created by the copier */ + if (converter) { + /* + * copy from DMA buffer to all sink buffers using the right PCM converter + * function + */ + list_for_item(sink_list, &dev->bsink_list) { + struct comp_buffer __sparse_cache *sink_c; + struct comp_dev *sink_dev; + struct comp_buffer *sink; + int j; + + sink = container_of(sink_list, struct comp_buffer, source_list); + + /* this has been handled above already */ + if (sink == dd->local_buffer) + continue; + + sink_c = buffer_acquire(sink); + sink_dev = sink_c->sink; + + j = IPC4_SINK_QUEUE_ID(sink_c->id); + 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; + goto err; + } + + if (!converter[j]) { + comp_err(dev, "No PCM converter for sink queue %d\n", j); + ret = -EINVAL; + goto err; + } + + if (sink_dev && sink_dev->state == COMP_STATE_ACTIVE) + ret = dma_buffer_copy_from_no_consume(dma_buf, sink_c, + converter[j], bytes); +err: + buffer_release(sink_c); + } + } +#endif + audio_stream_consume(&dma_buf->stream, bytes); + } + + local_buf = buffer_acquire(dd->local_buffer); /* assert dma_buffer_copy succeed */ if (ret < 0) { struct comp_buffer __sparse_cache *source_c, *sink_c; @@ -284,34 +340,15 @@ static enum dma_cb_status dai_dma_cb(struct comp_dev *dev, uint32_t bytes) return dma_status; } -static struct comp_dev *dai_new(const struct comp_driver *drv, - const struct comp_ipc_config *config, - const void *spec) +int dai_zephyr_new(struct dai_data *dd, struct comp_dev *dev, + const struct ipc_config_dai *dai_cfg) { - struct comp_dev *dev; - const struct ipc_config_dai *dai_cfg = spec; - struct dai_data *dd; uint32_t dir; - comp_cl_dbg(&comp_dai, "dai_new()"); - - dev = comp_alloc(drv, sizeof(*dev)); - if (!dev) - return NULL; - dev->ipc_config = *config; - - dd = rzalloc(SOF_MEM_ZONE_RUNTIME_SHARED, 0, SOF_MEM_CAPS_RAM, sizeof(*dd)); - if (!dd) { - rfree(dev); - return NULL; - } - - comp_set_drvdata(dev, dd); - dd->dai = dai_get(dai_cfg->type, dai_cfg->dai_index, DAI_CREAT); if (!dd->dai) { - comp_cl_err(&comp_dai, "dai_new(): dai_get() failed to create DAI."); - goto error; + comp_err(dev, "dai_new(): dai_get() failed to create DAI."); + return -ENODEV; } dd->ipc_config = *dai_cfg; @@ -322,8 +359,9 @@ static struct comp_dev *dai_new(const struct comp_driver *drv, dd->dma = dma_get(dir, dd->dai->dma_caps, dd->dai->dma_dev, DMA_ACCESS_SHARED); if (!dd->dma) { - comp_cl_err(&comp_dai, "dai_new(): dma_get() failed to get shared access to DMA."); - goto error; + dai_put(dd->dai); + comp_err(dev, "dai_new(): dma_get() failed to get shared access to DMA."); + return -ENODEV; } k_spinlock_init(&dd->dai->lock); @@ -332,23 +370,51 @@ static struct comp_dev *dai_new(const struct comp_driver *drv, dd->xrun = 0; dd->chan = NULL; + return 0; +} + +static struct comp_dev *dai_new(const struct comp_driver *drv, + const struct comp_ipc_config *config, + const void *spec) +{ + struct comp_dev *dev; + const struct ipc_config_dai *dai_cfg = spec; + struct dai_data *dd; + int ret; + + comp_cl_dbg(&comp_dai, "dai_new()"); + + dev = comp_alloc(drv, sizeof(*dev)); + if (!dev) + return NULL; + + dev->ipc_config = *config; + + dd = rzalloc(SOF_MEM_ZONE_RUNTIME_SHARED, 0, SOF_MEM_CAPS_RAM, sizeof(*dd)); + if (!dd) + goto e_data; + + comp_set_drvdata(dev, dd); + + ret = dai_zephyr_new(dd, dev, dai_cfg); + if (ret < 0) + goto error; + dev->state = COMP_STATE_READY; + return dev; error: rfree(dd); +e_data: rfree(dev); return NULL; } -static void dai_free(struct comp_dev *dev) +void dai_zephyr_free(struct dai_data *dd) { - struct dai_data *dd = comp_get_drvdata(dev); - - if (dd->group) { - notifier_unregister(dev, dd->group, NOTIFIER_ID_DAI_TRIGGER); + if (dd->group) dai_group_put(dd->group); - } if (dd->chan) { dma_release_channel(dd->dma->z_dev, dd->chan->index); @@ -357,24 +423,33 @@ static void dai_free(struct comp_dev *dev) dma_put(dd->dma); - dai_release_llp_slot(dev); + dai_release_llp_slot(dd); dai_put(dd->dai); rfree(dd->dai_spec_config); +} + +static void dai_free(struct comp_dev *dev) +{ + struct dai_data *dd = comp_get_drvdata(dev); + + if (dd->group) + notifier_unregister(dev, dd->group, NOTIFIER_ID_DAI_TRIGGER); + + dai_zephyr_free(dd); + rfree(dd); rfree(dev); } -static int dai_comp_get_hw_params(struct comp_dev *dev, - struct sof_ipc_stream_params *params, - int dir) +int dai_zephyr_get_hw_params(struct dai_data *dd, struct comp_dev *dev, + struct sof_ipc_stream_params *params, int dir) { - struct dai_data *dd = comp_get_drvdata(dev); struct dai_config cfg; int ret; - comp_dbg(dev, "dai_hw_params()"); + comp_dbg(dev, "dai_zephyr_get_hw_params()"); ret = dai_config_get(dd->dai->dev, &cfg, dir); if (ret) @@ -395,12 +470,21 @@ static int dai_comp_get_hw_params(struct comp_dev *dev, return ret; } +static int dai_comp_get_hw_params(struct comp_dev *dev, + struct sof_ipc_stream_params *params, + int dir) +{ + struct dai_data *dd = comp_get_drvdata(dev); + + return dai_zephyr_get_hw_params(dd, dev, params, dir); +} + static int dai_verify_params(struct comp_dev *dev, struct sof_ipc_stream_params *params) { struct sof_ipc_stream_params hw_params; int ret; - ret = dai_comp_get_hw_params(dev, &hw_params, params->direction); + ret = comp_dai_get_hw_params(dev, &hw_params, params->direction); if (ret < 0) { comp_err(dev, "dai_verify_params(): dai_verify_params failed ret %d", ret); return ret; @@ -431,10 +515,9 @@ static int dai_verify_params(struct comp_dev *dev, struct sof_ipc_stream_params } /* set component audio SSP and DMA configuration */ -static int dai_playback_params(struct comp_dev *dev, uint32_t period_bytes, - uint32_t period_count) +static int dai_playback_params(struct dai_data *dd, struct comp_dev *dev, uint32_t period_bytes, + int32_t period_count) { - struct dai_data *dd = comp_get_drvdata(dev); struct dma_sg_config *config = &dd->config; struct dma_config *dma_cfg; struct dma_block_config *dma_block_cfg; @@ -569,10 +652,9 @@ static int dai_playback_params(struct comp_dev *dev, uint32_t period_bytes, return err; } -static int dai_capture_params(struct comp_dev *dev, uint32_t period_bytes, +static int dai_capture_params(struct dai_data *dd, struct comp_dev *dev, uint32_t period_bytes, uint32_t period_count) { - struct dai_data *dd = comp_get_drvdata(dev); struct dma_sg_config *config = &dd->config; struct dma_config *dma_cfg; struct dma_block_config *dma_block_cfg; @@ -724,10 +806,10 @@ static int dai_capture_params(struct comp_dev *dev, uint32_t period_bytes, return err; } -static int dai_params(struct comp_dev *dev, struct sof_ipc_stream_params *params) +int dai_zephyr_params(struct dai_data *dd, struct comp_dev *dev, + struct sof_ipc_stream_params *params) { struct sof_ipc_stream_params hw_params = *params; - struct dai_data *dd = comp_get_drvdata(dev); struct comp_buffer __sparse_cache *buffer_c; uint32_t frame_size; uint32_t period_count; @@ -737,16 +819,14 @@ static int dai_params(struct comp_dev *dev, struct sof_ipc_stream_params *params uint32_t align; int err; - comp_dbg(dev, "dai_params()"); - /* configure dai_data first */ - err = ipc_dai_data_config(dev); + err = ipc_dai_data_config(dd, dev); if (err < 0) return err; err = dai_verify_params(dev, params); if (err < 0) { - comp_err(dev, "dai_params(): pcm params verification failed."); + comp_err(dev, "dai_zephyr_params(): pcm params verification failed."); return -EINVAL; } @@ -761,13 +841,13 @@ static int dai_params(struct comp_dev *dev, struct sof_ipc_stream_params *params /* check if already configured */ if (dev->state == COMP_STATE_PREPARE) { - comp_info(dev, "dai_params() component has been already configured."); + comp_info(dev, "dai_zephyr_params() component has been already configured."); return 0; } /* can set params on only init state */ if (dev->state != COMP_STATE_READY) { - comp_err(dev, "dai_params(): Component is in state %d, expected COMP_STATE_READY.", + comp_err(dev, "dai_zephyr_params(): Component is in state %d, expected COMP_STATE_READY.", dev->state); return -EINVAL; } @@ -775,21 +855,21 @@ static int dai_params(struct comp_dev *dev, struct sof_ipc_stream_params *params err = dma_get_attribute(dd->dma->z_dev, DMA_ATTR_BUFFER_ADDRESS_ALIGNMENT, &addr_align); if (err < 0) { - comp_err(dev, "dai_params(): could not get dma buffer address alignment, err = %d", + comp_err(dev, "dai_zephyr_params(): could not get dma buffer address alignment, err = %d", err); return err; } err = dma_get_attribute(dd->dma->z_dev, DMA_ATTR_BUFFER_SIZE_ALIGNMENT, &align); if (err < 0 || !align) { - comp_err(dev, "dai_params(): no valid dma buffer alignment, err = %d, align = %u", + comp_err(dev, "dai_zephyr_params(): no valid dma buffer alignment, err = %d, align = %u", err, align); return -EINVAL; } period_count = dd->dma->plat_data.period_count; if (!period_count) { - comp_err(dev, "dai_params(): no valid dma buffer period count"); + comp_err(dev, "dai_zephyr_params(): no valid dma buffer period count"); return -EINVAL; } @@ -804,7 +884,7 @@ static int dai_params(struct comp_dev *dev, struct sof_ipc_stream_params *params /* calculate period size */ period_bytes = dev->frames * frame_size; if (!period_bytes) { - comp_err(dev, "dai_params(): invalid period_bytes."); + comp_err(dev, "dai_zephyr_params(): invalid period_bytes."); return -EINVAL; } @@ -822,7 +902,7 @@ static int dai_params(struct comp_dev *dev, struct sof_ipc_stream_params *params buffer_release(buffer_c); if (err < 0) { - comp_err(dev, "dai_params(): buffer_set_size() failed, buffer_size = %u", + comp_err(dev, "dai_zephyr_params(): buffer_set_size() failed, buffer_size = %u", buffer_size); return err; } @@ -830,7 +910,7 @@ static int dai_params(struct comp_dev *dev, struct sof_ipc_stream_params *params dd->dma_buffer = buffer_alloc(buffer_size, SOF_MEM_CAPS_DMA, addr_align); if (!dd->dma_buffer) { - comp_err(dev, "dai_params(): failed to alloc dma buffer"); + comp_err(dev, "dai_zephyr_params(): failed to alloc dma buffer"); return -ENOMEM; } @@ -848,18 +928,26 @@ static int dai_params(struct comp_dev *dev, struct sof_ipc_stream_params *params } return dev->direction == SOF_IPC_STREAM_PLAYBACK ? - dai_playback_params(dev, period_bytes, period_count) : - dai_capture_params(dev, period_bytes, period_count); + dai_playback_params(dd, dev, period_bytes, period_count) : + dai_capture_params(dd, dev, period_bytes, period_count); } -static int dai_config_prepare(struct comp_dev *dev) +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()"); + + return dai_zephyr_params(dd, dev, params); +} + +int dai_zephyr_config_prepare(struct dai_data *dd, struct comp_dev *dev) +{ int channel; /* cannot configure DAI while active */ if (dev->state == COMP_STATE_ACTIVE) { - comp_info(dev, "dai_config_prepare(): Component is in active state."); + comp_info(dev, "dai_zephyr_config_prepare(): Component is in active state."); return 0; } @@ -869,13 +957,13 @@ static int dai_config_prepare(struct comp_dev *dev) } if (dd->chan) { - comp_info(dev, "dai_config_prepare(): dma channel index %d already configured", + comp_info(dev, "dai_zephyr_config_prepare(): dma channel index %d already configured", dd->chan->index); return 0; } - channel = dai_config_dma_channel(dev, dd->dai_spec_config); - comp_dbg(dev, "dai_config_prepare(), channel = %d", channel); + channel = dai_config_dma_channel(dd, dev, dd->dai_spec_config); + comp_dbg(dev, "dai_zephyr_config_prepare(), channel = %d", channel); /* do nothing for asking for channel free, for compatibility. */ if (channel == DMA_CHAN_INVALID) { @@ -886,7 +974,7 @@ static int dai_config_prepare(struct comp_dev *dev) /* get DMA channel */ channel = dma_request_channel(dd->dma->z_dev, &channel); if (channel < 0) { - comp_err(dev, "dai_config_prepare(): dma_request_channel() failed"); + comp_err(dev, "dai_zephyr_config_prepare(): dma_request_channel() failed"); dd->chan = NULL; return -EIO; } @@ -894,41 +982,27 @@ static int dai_config_prepare(struct comp_dev *dev) dd->chan = &dd->dma->chan[channel]; dd->chan->dev_data = dd; - comp_dbg(dev, "dai_config_prepare(): new configured dma channel index %d", + comp_dbg(dev, "dai_zephyr_config_prepare(): new configured dma channel index %d", dd->chan->index); return 0; } -static int dai_prepare(struct comp_dev *dev) +int dai_zephyr_prepare(struct dai_data *dd, struct comp_dev *dev) { - struct dai_data *dd = comp_get_drvdata(dev); struct comp_buffer __sparse_cache *buffer_c; int ret; - comp_dbg(dev, "dai_prepare()"); - - ret = dai_config_prepare(dev); - if (ret < 0) - return ret; - - 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; - dd->total_data_processed = 0; if (!dd->chan) { - comp_err(dev, "dai_prepare(): Missing dd->chan."); + comp_err(dev, "dai_zephyr_prepare(): Missing dd->chan."); comp_set_state(dev, COMP_TRIGGER_RESET); return -EINVAL; } if (!dd->config.elem_array.elems) { - comp_err(dev, "dai_prepare(): Missing dd->config.elem_array.elems."); + comp_err(dev, "dai_zephyr_prepare(): Missing dd->config.elem_array.elems."); comp_set_state(dev, COMP_TRIGGER_RESET); return -EINVAL; } @@ -942,7 +1016,7 @@ static int dai_prepare(struct comp_dev *dev) if (dd->xrun) { /* after prepare, we have recovered from xrun */ dd->xrun = 0; - return ret; + return 0; } ret = dma_config(dd->chan->dma->z_dev, dd->chan->index, dd->z_config); @@ -952,19 +1026,37 @@ static int dai_prepare(struct comp_dev *dev) return ret; } -static int dai_reset(struct comp_dev *dev) +static int dai_prepare(struct comp_dev *dev) { struct dai_data *dd = comp_get_drvdata(dev); - struct dma_sg_config *config = &dd->config; + int ret; - comp_dbg(dev, "dai_reset()"); + comp_dbg(dev, "dai_prepare()"); + + ret = dai_zephyr_config_prepare(dd, dev); + if (ret < 0) + return ret; + + 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; + + return dai_zephyr_prepare(dd, dev); +} + +void dai_zephyr_reset(struct dai_data *dd, struct comp_dev *dev) +{ + struct dma_sg_config *config = &dd->config; /* * DMA channel release should be skipped now for DAI's that support the two-step stop * option. It will be done when the host sends the DAI_CONFIG IPC during hw_free. */ if (!dd->delayed_dma_stop) - dai_dma_release(dev); + dai_dma_release(dd, dev); dma_sg_free(&config->elem_array); if (dd->z_config) { @@ -981,23 +1073,24 @@ static int dai_reset(struct comp_dev *dev) dd->wallclock = 0; dd->total_data_processed = 0; dd->xrun = 0; - comp_set_state(dev, COMP_TRIGGER_RESET); - - return 0; } -static void dai_update_start_position(struct comp_dev *dev) +static int dai_reset(struct comp_dev *dev) { struct dai_data *dd = comp_get_drvdata(dev); - /* update starting wallclock */ - platform_dai_wallclock(dev, &dd->wallclock); + comp_dbg(dev, "dai_reset()"); + + dai_zephyr_reset(dd, dev); + + comp_set_state(dev, COMP_TRIGGER_RESET); + + return 0; } /* used to pass standard and bespoke command (with data) to component */ -static int dai_comp_trigger_internal(struct comp_dev *dev, int cmd) +static int dai_comp_trigger_internal(struct dai_data *dd, struct comp_dev *dev, int cmd) { - struct dai_data *dd = comp_get_drvdata(dev); int prev_state = dev->state; int ret; @@ -1026,7 +1119,7 @@ static int dai_comp_trigger_internal(struct comp_dev *dev, int cmd) dd->xrun = 0; } - dai_update_start_position(dev); + platform_dai_wallclock(dev, &dd->wallclock); break; case COMP_TRIGGER_RELEASE: /* before release, we clear the buffer data to 0s, @@ -1065,7 +1158,7 @@ static int dai_comp_trigger_internal(struct comp_dev *dev, int cmd) dd->xrun = 0; } - dai_update_start_position(dev); + platform_dai_wallclock(dev, &dd->wallclock); break; case COMP_TRIGGER_XRUN: comp_info(dev, "dai_comp_trigger_internal(), XRUN"); @@ -1127,17 +1220,16 @@ static int dai_comp_trigger_internal(struct comp_dev *dev, int cmd) return ret; } -static int dai_comp_trigger(struct comp_dev *dev, int cmd) +int dai_zephyr_trigger(struct dai_data *dd, struct comp_dev *dev, int cmd) { - struct dai_data *dd = comp_get_drvdata(dev); struct dai_group *group = dd->group; uint32_t irq_flags; int ret = 0; /* DAI not in a group, use normal trigger */ if (!group) { - comp_dbg(dev, "dai_comp_trigger(), non-atomic trigger"); - return dai_comp_trigger_internal(dev, cmd); + comp_dbg(dev, "dai_zephyr_trigger(), non-atomic trigger"); + return dai_comp_trigger_internal(dd, dev, cmd); } /* DAI is grouped, so only trigger when the entire group is ready */ @@ -1146,13 +1238,13 @@ static int dai_comp_trigger(struct comp_dev *dev, int cmd) /* First DAI to receive the trigger command, * prepare for atomic trigger */ - comp_dbg(dev, "dai_comp_trigger(), begin atomic trigger for group %d", + comp_dbg(dev, "dai_zephyr_trigger(), 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_comp_trigger(), already processing atomic trigger"); + comp_err(dev, "dai_zephyr_trigger(), already processing atomic trigger"); ret = -EAGAIN; } else { /* Count down the number of remaining DAIs required @@ -1160,7 +1252,7 @@ static int dai_comp_trigger(struct comp_dev *dev, int cmd) * takes place */ group->trigger_counter--; - comp_dbg(dev, "dai_comp_trigger(), trigger counter %d, group %d", + comp_dbg(dev, "dai_zephyr_trigger(), trigger counter %d, group %d", group->trigger_counter, group->group_id); if (!group->trigger_counter) { @@ -1183,10 +1275,16 @@ static int dai_comp_trigger(struct comp_dev *dev, int cmd) return ret; } -/* report xrun occurrence */ -static void dai_report_xrun(struct comp_dev *dev, uint32_t bytes) +static int dai_comp_trigger(struct comp_dev *dev, int cmd) { struct dai_data *dd = comp_get_drvdata(dev); + + return dai_zephyr_trigger(dd, dev, cmd); +} + +/* report xrun occurrence */ +static void dai_report_xrun(struct dai_data *dd, struct comp_dev *dev, uint32_t bytes) +{ struct comp_buffer __sparse_cache *buf_c = buffer_acquire(dd->local_buffer); if (dev->direction == SOF_IPC_STREAM_PLAYBACK) { @@ -1201,9 +1299,8 @@ static void dai_report_xrun(struct comp_dev *dev, uint32_t bytes) } /* copy and process stream data from source to sink buffers */ -static int dai_copy(struct comp_dev *dev) +int dai_zephyr_copy(struct dai_data *dd, struct comp_dev *dev, pcm_converter_func *converter) { - struct dai_data *dd = comp_get_drvdata(dev); uint32_t dma_fmt; uint32_t sampling; struct comp_buffer __sparse_cache *buf_c; @@ -1213,11 +1310,9 @@ static int dai_copy(struct comp_dev *dev) uint32_t copy_bytes = 0; uint32_t src_samples; uint32_t sink_samples; - uint32_t samples; + uint32_t samples = UINT32_MAX; int ret; - comp_dbg(dev, "dai_copy()"); - /* get data sizes from DMA */ ret = dma_get_status(dd->chan->dma->z_dev, dd->chan->index, &stat); switch (ret) { @@ -1226,11 +1321,11 @@ static int dai_copy(struct comp_dev *dev) case -EPIPE: /* DMA status can return -EPIPE and current status content if xrun occurs */ if (dev->direction == SOF_IPC_STREAM_PLAYBACK) - comp_dbg(dev, "dai_copy(): dma_get_status() underrun occurred, ret = %u", - ret); + comp_dbg(dev, "dai_zephyr_copy(): dma_get_status() underrun occurred, ret = %u", + ret); else - comp_dbg(dev, "dai_copy(): dma_get_status() overrun occurred, ret = %u", - ret); + comp_dbg(dev, "dai_zephyr_copy(): dma_get_status() overrun occurred, ret = %u", + ret); break; default: return ret; @@ -1246,17 +1341,52 @@ static int dai_copy(struct comp_dev *dev) buffer_release(buf_c); - buf_c = buffer_acquire(dd->local_buffer); /* calculate minimum size to copy */ if (dev->direction == SOF_IPC_STREAM_PLAYBACK) { + buf_c = buffer_acquire(dd->local_buffer); src_samples = audio_stream_get_avail_samples(&buf_c->stream); sink_samples = free_bytes / sampling; samples = MIN(src_samples, sink_samples); + buffer_release(buf_c); } else { + struct list_item *sink_list; + src_samples = avail_bytes / sampling; - sink_samples = audio_stream_get_free_samples(&buf_c->stream); - samples = MIN(src_samples, sink_samples); + + /* + * there's only one sink buffer in the case of endpoint DAI devices created by + * a DAI copier and it is chosen as the dd->local buffer + */ + if (!converter) { + buf_c = buffer_acquire(dd->local_buffer); + sink_samples = audio_stream_get_free_samples(&buf_c->stream); + samples = MIN(samples, sink_samples); + buffer_release(buf_c); + } else { + /* + * In the case of capture DAI's with multiple sink buffers, compute the + * minimum number of samples based on the DMA avail_bytes and the free + * samples in all active sink buffers. + */ + list_for_item(sink_list, &dev->bsink_list) { + struct comp_dev *sink_dev; + struct comp_buffer *sink; + + sink = container_of(sink_list, struct comp_buffer, source_list); + buf_c = buffer_acquire(sink); + sink_dev = buf_c->sink; + + if (sink_dev && sink_dev->state == COMP_STATE_ACTIVE) { + sink_samples = + audio_stream_get_free_samples(&buf_c->stream); + samples = MIN(samples, sink_samples); + } + buffer_release(buf_c); + } + } + + samples = MIN(samples, src_samples); } /* limit bytes per copy to one period for the whole pipeline @@ -1268,47 +1398,56 @@ static int dai_copy(struct comp_dev *dev) copy_bytes = samples * sampling; - comp_dbg(dev, "dai_copy(), dir: %d copy_bytes= 0x%x, frames= %d", - dev->direction, copy_bytes, - samples / buf_c->stream.channels); - - buffer_release(buf_c); + comp_dbg(dev, "dai_zephyr_copy(), dir: %d copy_bytes= 0x%x", + dev->direction, copy_bytes); /* Check possibility of glitch occurrence */ if (dev->direction == SOF_IPC_STREAM_PLAYBACK && copy_bytes + avail_bytes < dd->period_bytes) - comp_warn(dev, "dai_copy(): Copy_bytes %d + avail bytes %d < period bytes %d, possible glitch", + comp_warn(dev, "dai_zephyr_copy(): Copy_bytes %d + avail bytes %d < period bytes %d, possible glitch", copy_bytes, avail_bytes, dd->period_bytes); else if (dev->direction == SOF_IPC_STREAM_CAPTURE && copy_bytes + free_bytes < dd->period_bytes) - comp_warn(dev, "dai_copy(): Copy_bytes %d + free bytes %d < period bytes %d, possible glitch", + comp_warn(dev, "dai_zephyr_copy(): Copy_bytes %d + free bytes %d < period bytes %d, possible glitch", copy_bytes, free_bytes, dd->period_bytes); /* return if nothing to copy */ if (!copy_bytes) { - comp_warn(dev, "dai_copy(): nothing to copy"); + comp_warn(dev, "dai_zephyr_copy(): nothing to copy"); + dma_reload(dd->chan->dma->z_dev, dd->chan->index, 0, 0, 0); return 0; } /* trigger optional DAI_TRIGGER_COPY which prepares dai to copy */ ret = dai_trigger(dd->dai->dev, dev->direction, DAI_TRIGGER_COPY); if (ret < 0) - comp_warn(dev, "dai_copy(): dai trigger copy failed"); + comp_warn(dev, "dai_zephyr_copy(): dai trigger copy failed"); - if (dai_dma_cb(dev, copy_bytes) == DMA_CB_STATUS_END) + if (dai_dma_cb(dd, dev, copy_bytes, converter) == DMA_CB_STATUS_END) dma_stop(dd->chan->dma->z_dev, dd->chan->index); ret = dma_reload(dd->chan->dma->z_dev, dd->chan->index, 0, 0, copy_bytes); if (ret < 0) { - dai_report_xrun(dev, copy_bytes); + dai_report_xrun(dd, dev, copy_bytes); return ret; } - dai_dma_position_update(dev); + dai_dma_position_update(dd, dev); return ret; } +static int dai_copy(struct comp_dev *dev) +{ + struct dai_data *dd = comp_get_drvdata(dev); + + /* + * DAI devices will only ever have 1 sink, so no need to pass an array of PCM converter + * functions. The default one to use is set in dd->process. + */ + return dai_zephyr_copy(dd, dev, NULL); +} + /** * \brief Get DAI parameters and configure timestamping * \param[in, out] dev DAI device. @@ -1319,9 +1458,8 @@ static int dai_copy(struct comp_dev *dev) * DAI must be prepared before this function is used (for DMA information). If not, an error * is returned. */ -static int dai_ts_config_op(struct comp_dev *dev) +int dai_zephyr_ts_config_op(struct dai_data *dd, struct comp_dev *dev) { - struct dai_data *dd = comp_get_drvdata(dev); struct ipc_config_dai *dai = &dd->ipc_config; struct dai_ts_cfg cfg; @@ -1355,43 +1493,60 @@ static int dai_ts_config_op(struct comp_dev *dev) return dai_ts_config(dd->dai->dev, &cfg); } -static int dai_ts_start_op(struct comp_dev *dev) +static int dai_ts_config_op(struct comp_dev *dev) { struct dai_data *dd = comp_get_drvdata(dev); - struct dai_ts_cfg cfg; - comp_dbg(dev, "dai_ts_start()"); + return dai_zephyr_ts_config_op(dd, dev); +} + +int dai_zephyr_ts_start(struct dai_data *dd, struct comp_dev *dev) +{ + struct dai_ts_cfg cfg; return dai_ts_start(dd->dai->dev, &cfg); } -static int dai_ts_get_op(struct comp_dev *dev, struct timestamp_data *tsd) +static int dai_ts_start_op(struct comp_dev *dev) { struct dai_data *dd = comp_get_drvdata(dev); + + comp_dbg(dev, "dai_ts_start()"); + return dai_zephyr_ts_start(dd, dev); +} + +int dai_zephyr_ts_get(struct dai_data *dd, struct comp_dev *dev, struct timestamp_data *tsd) +{ struct dai_ts_data tsdata; struct dai_ts_cfg cfg; - int ret; - comp_dbg(dev, "dai_ts_get()"); + /* TODO: convert to timestamp_data */ + return dai_ts_get(dd->dai->dev, &cfg, &tsdata); +} - ret = dai_ts_get(dd->dai->dev, &cfg, &tsdata); +static int dai_ts_get_op(struct comp_dev *dev, struct timestamp_data *tsd) +{ + struct dai_data *dd = comp_get_drvdata(dev); - if (ret < 0) - return ret; + comp_dbg(dev, "dai_ts_get()"); + + return dai_zephyr_ts_get(dd, dev, tsd); +} - /* todo convert to timestamp_data */ +int dai_zephyr_ts_stop(struct dai_data *dd, struct comp_dev *dev) +{ + struct dai_ts_cfg cfg; - return ret; + return dai_ts_stop(dd->dai->dev, &cfg); } static int dai_ts_stop_op(struct comp_dev *dev) { struct dai_data *dd = comp_get_drvdata(dev); - struct dai_ts_cfg cfg; comp_dbg(dev, "dai_ts_stop()"); - return dai_ts_stop(dd->dai->dev, &cfg); + return dai_zephyr_ts_stop(dd, dev); } uint32_t dai_get_init_delay_ms(struct dai *dai) diff --git a/src/audio/host-legacy.c b/src/audio/host-legacy.c index 490394b6a876..d127d9423c0b 100644 --- a/src/audio/host-legacy.c +++ b/src/audio/host-legacy.c @@ -128,7 +128,7 @@ static uint32_t host_get_copy_bytes_one_shot(struct host_data *hd, struct comp_d * @param dev Host component device. * @return 0 if succeeded, error code otherwise. */ -static int host_copy_one_shot(struct host_data *hd, struct comp_dev *dev) +static int host_copy_one_shot(struct host_data *hd, struct comp_dev *dev, copy_callback_t cb) { uint32_t copy_bytes = 0; uint32_t split_value = 0; @@ -200,7 +200,7 @@ static uint32_t host_get_copy_bytes_one_shot(struct host_data *hd, struct comp_d * @param dev Host component device. * @return 0 if succeeded, error code otherwise. */ -static int host_copy_one_shot(struct host_data *hd, struct comp_dev *dev) +static int host_copy_one_shot(struct host_data *hd, struct comp_dev *dev, copy_callback_t cb) { uint32_t copy_bytes = 0; int ret = 0; @@ -355,7 +355,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_cl_dbg(&comp_host, "host_dma_cb() %p", &comp_host); + comp_dbg(dev, "host_dma_cb() %p", &comp_host); /* update position */ host_update_position(hd, dev, bytes); @@ -421,7 +421,7 @@ static uint32_t host_get_copy_bytes_normal(struct host_data *hd, struct comp_dev * @param dev Host component device. * @return 0 if succeeded, error code otherwise. */ -static int host_copy_normal(struct host_data *hd, struct comp_dev *dev) +static int host_copy_normal(struct host_data *hd, struct comp_dev *dev, copy_callback_t cb) { uint32_t copy_bytes = 0; uint32_t flags = 0; @@ -687,7 +687,7 @@ static int host_verify_params(struct comp_dev *dev, /* configure the DMA params and descriptors for host buffer IO */ int host_zephyr_params(struct host_data *hd, struct comp_dev *dev, - struct sof_ipc_stream_params *params) + struct sof_ipc_stream_params *params, notifier_callback_t cb) { struct dma_sg_config *config = &hd->config; struct comp_buffer __sparse_cache *host_buf_c; @@ -856,6 +856,14 @@ int host_zephyr_params(struct host_data *hd, struct comp_dev *dev, out: buffer_release(host_buf_c); + + hd->cb_dev = dev; + + if (err >= 0) + /* set up callback */ + notifier_register(dev, hd->chan, NOTIFIER_ID_DMA_COPY, + cb ? : host_dma_cb, 0); + return err; } @@ -873,12 +881,7 @@ static int host_params(struct comp_dev *dev, return err; } - err = host_zephyr_params(hd, dev, params); - if (err >= 0) - /* set up callback */ - notifier_register(dev, hd->chan, NOTIFIER_ID_DMA_COPY, host_dma_cb, 0); - - return err; + return host_zephyr_params(hd, dev, params, NULL); } int host_zephyr_prepare(struct host_data *hd) @@ -924,6 +927,9 @@ void host_zephyr_reset(struct host_data *hd, uint16_t state) if (hd->chan) { dma_stop_delayed_legacy(hd->chan); + /* remove callback */ + notifier_unregister(hd->cb_dev, hd->chan, NOTIFIER_ID_DMA_COPY); + dma_channel_put_legacy(hd->chan); hd->chan = NULL; } @@ -958,10 +964,6 @@ static int host_reset(struct comp_dev *dev) comp_dbg(dev, "host_reset()"); - /* remove callback */ - if (hd->chan) - notifier_unregister(dev, hd->chan, NOTIFIER_ID_DMA_COPY); - host_zephyr_reset(hd, dev->state); dev->state = COMP_STATE_READY; @@ -969,9 +971,9 @@ static int host_reset(struct comp_dev *dev) } /* copy and process stream data from source to sink buffers */ -int host_zephyr_copy(struct host_data *hd, struct comp_dev *dev) +int host_zephyr_copy(struct host_data *hd, struct comp_dev *dev, copy_callback_t cb) { - return hd->copy(hd, dev); + return hd->copy(hd, dev, cb); } static int host_copy(struct comp_dev *dev) @@ -981,7 +983,7 @@ static int host_copy(struct comp_dev *dev) if (dev->state != COMP_STATE_ACTIVE) return 0; - return host_zephyr_copy(hd, dev); + return host_zephyr_copy(hd, dev, NULL); } static int host_get_attribute(struct comp_dev *dev, uint32_t type, diff --git a/src/audio/host-zephyr.c b/src/audio/host-zephyr.c index cda7cf8a79f3..119d2e353eba 100644 --- a/src/audio/host-zephyr.c +++ b/src/audio/host-zephyr.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -74,7 +75,8 @@ static uint32_t host_dma_get_split(struct host_data *hd, uint32_t bytes) #if CONFIG_FORCE_DMA_COPY_WHOLE_BLOCK -static int host_dma_set_config_and_copy(struct host_data *hd, struct comp_dev *dev, uint32_t bytes) +static int host_dma_set_config_and_copy(struct host_data *hd, struct comp_dev *dev, uint32_t bytes, + copy_callback_t cb) { struct dma_sg_elem *local_elem = hd->config.elem_array.elems; int ret; @@ -89,12 +91,8 @@ static int host_dma_set_config_and_copy(struct host_data *hd, struct comp_dev *d return ret; } - struct dma_cb_data next = { - .channel = hd->chan, - .elem = { .size = bytes }, - }; - notifier_event(hd->chan, NOTIFIER_ID_DMA_COPY, - NOTIFIER_TARGET_CORE_LOCAL, &next, sizeof(next)); + cb(dev, bytes); + ret = dma_reload(hd->chan->dma->z_dev, hd->chan->index, 0, 0, bytes); if (ret < 0) { comp_err(dev, "host_dma_set_config_and_copy(): dma_copy() failed, ret = %d", @@ -138,7 +136,7 @@ static uint32_t host_get_copy_bytes_one_shot(struct host_data *hd) * @param dev Host component device. * @return 0 if succeeded, error code otherwise. */ -static int host_copy_one_shot(struct host_data *hd, struct comp_dev *dev) +static int host_copy_one_shot(struct host_data *hd, struct comp_dev *dev, copy_callback_t cb) { uint32_t copy_bytes; uint32_t split_value; @@ -157,7 +155,7 @@ static int host_copy_one_shot(struct host_data *hd, struct comp_dev *dev) split_value = host_dma_get_split(hd, copy_bytes); copy_bytes -= split_value; - ret = host_dma_set_config_and_copy(hd, dev, copy_bytes); + ret = host_dma_set_config_and_copy(hd, dev, copy_bytes, cb); if (ret < 0) return ret; @@ -213,7 +211,7 @@ static uint32_t host_get_copy_bytes_one_shot(struct host_data *hd) * @param dev Host component device. * @return 0 if succeeded, error code otherwise. */ -static int host_copy_one_shot(struct host_data *hd, struct comp_dev *dev) +static int host_copy_one_shot(struct host_data *hd, struct comp_dev *dev, copy_callback_t cb) { uint32_t copy_bytes; int ret = 0; @@ -233,12 +231,8 @@ static int host_copy_one_shot(struct host_data *hd, struct comp_dev *dev) return ret; } - struct dma_cb_data next = { - .channel = hd->chan, - .elem = { .size = copy_bytes }, - }; - notifier_event(hd->chan, NOTIFIER_ID_DMA_COPY, - NOTIFIER_TARGET_CORE_LOCAL, &next, sizeof(next)); + cb(dev, copy_bytes); + ret = dma_reload(hd->chan->dma->z_dev, hd->chan->index, 0, 0, copy_bytes); if (ret < 0) comp_err(dev, "host_copy_one_shot(): dma_copy() failed, ret = %u", ret); @@ -365,12 +359,9 @@ void host_one_shot_cb(struct host_data *hd, uint32_t bytes) /* This is called by DMA driver every time when DMA completes its current * transfer between host and DSP. */ -static void host_dma_cb(void *arg, enum notify_id type, void *data) +static void host_dma_cb(struct comp_dev *dev, size_t bytes) { - struct dma_cb_data *next = data; - struct comp_dev *dev = arg; struct host_data *hd = comp_get_drvdata(dev); - uint32_t bytes = next->elem.size; comp_cl_dbg(&comp_host, "host_dma_cb() %p", &comp_host); @@ -416,11 +407,11 @@ static uint32_t host_get_copy_bytes_normal(struct host_data *hd, struct comp_dev /* calculate minimum size to copy */ if (dev->direction == SOF_IPC_STREAM_PLAYBACK) { - avail_samples = dma_stat.pending_length / dma_sample_bytes; + avail_samples = (dma_stat.pending_length - hd->partial_size) / dma_sample_bytes; free_samples = audio_stream_get_free_samples(&buffer_c->stream); } else { avail_samples = audio_stream_get_avail_samples(&buffer_c->stream); - free_samples = dma_stat.free / dma_sample_bytes; + free_samples = (dma_stat.free - hd->partial_size) / dma_sample_bytes; } dma_copy_bytes = MIN(avail_samples, free_samples) * dma_sample_bytes; @@ -450,7 +441,7 @@ static uint32_t host_get_copy_bytes_normal(struct host_data *hd, struct comp_dev * @param dev Host component device. * @return 0 if succeeded, error code otherwise. */ -static int host_copy_normal(struct host_data *hd, struct comp_dev *dev) +static int host_copy_normal(struct host_data *hd, struct comp_dev *dev, copy_callback_t cb) { struct comp_buffer __sparse_cache *buffer_c; uint32_t copy_bytes; @@ -466,13 +457,11 @@ static int host_copy_normal(struct host_data *hd, struct comp_dev *dev) copy_bytes = host_get_copy_bytes_normal(hd, dev); if (!copy_bytes) return 0; - - struct dma_cb_data next = { - .channel = hd->chan, - .elem = { .size = copy_bytes }, - }; - notifier_event(hd->chan, NOTIFIER_ID_DMA_COPY, - NOTIFIER_TARGET_CORE_LOCAL, &next, sizeof(next)); +#if !CONFIG_DMA_INTEL_ADSP_HDA_TIMING_L1_EXIT + /* Register Host DMA usage */ + pm_runtime_get(PM_RUNTIME_HOST_DMA_L1, 0); +#endif + cb(dev, copy_bytes); hd->partial_size += copy_bytes; buffer_c = buffer_acquire(hd->dma_buffer); @@ -537,6 +526,12 @@ static int create_local_elems(struct host_data *hd, struct comp_dev *dev, uint32 return 0; } +static void hda_dma_l1_exit_notify(void *arg, enum notify_id type, void *data) +{ + /* Force Host DMA to exit L1 if needed */ + pm_runtime_put(PM_RUNTIME_HOST_DMA_L1, 0); +} + /** * \brief Command handler. * \param[in,out] dev Device @@ -569,6 +564,12 @@ int host_zephyr_trigger(struct host_data *hd, struct comp_dev *dev, int cmd) if (ret < 0) comp_err(dev, "host_trigger(): dma_start() failed, ret = %u", ret); +#if !CONFIG_DMA_INTEL_ADSP_HDA_TIMING_L1_EXIT + /* Register common L1 exit for all channels */ + ret = notifier_register(NULL, scheduler_get_data(SOF_SCHEDULE_LL_TIMER), + NOTIFIER_ID_LL_POST_RUN, hda_dma_l1_exit_notify, + NOTIFIER_FLAG_AGGREGATE); +#endif break; case COMP_TRIGGER_STOP: case COMP_TRIGGER_XRUN: @@ -577,6 +578,11 @@ int host_zephyr_trigger(struct host_data *hd, struct comp_dev *dev, int cmd) if (ret < 0) comp_err(dev, "host_trigger(): dma stop failed: %d", ret); +#if !CONFIG_DMA_INTEL_ADSP_HDA_TIMING_L1_EXIT + /* Unregister L1 exit */ + notifier_unregister(NULL, scheduler_get_data(SOF_SCHEDULE_LL_TIMER), + NOTIFIER_ID_LL_POST_RUN); +#endif } break; @@ -745,7 +751,7 @@ static int host_verify_params(struct comp_dev *dev, /* configure the DMA params and descriptors for host buffer IO */ int host_zephyr_params(struct host_data *hd, struct comp_dev *dev, - struct sof_ipc_stream_params *params) + struct sof_ipc_stream_params *params, notifier_callback_t cb) { struct dma_sg_config *config = &hd->config; struct dma_sg_elem *sg_elem; @@ -984,12 +990,7 @@ static int host_params(struct comp_dev *dev, return err; } - err = host_zephyr_params(hd, dev, params); - if (err >= 0) - /* set up callback */ - notifier_register(dev, hd->chan, NOTIFIER_ID_DMA_COPY, host_dma_cb, 0); - - return err; + return host_zephyr_params(hd, dev, params, NULL); } int host_zephyr_prepare(struct host_data *hd) @@ -1033,8 +1034,11 @@ static int host_position(struct comp_dev *dev, void host_zephyr_reset(struct host_data *hd, uint16_t state) { if (hd->chan) { - if (state == COMP_STATE_ACTIVE) - dma_stop(hd->chan->dma->z_dev, hd->chan->index); + dma_stop(hd->chan->dma->z_dev, hd->chan->index); + /* Unregister L1 exit */ + notifier_unregister(NULL, scheduler_get_data(SOF_SCHEDULE_LL_TIMER), + NOTIFIER_ID_LL_POST_RUN); + dma_release_channel(hd->dma->z_dev, hd->chan->index); hd->chan = NULL; } @@ -1065,9 +1069,6 @@ static int host_reset(struct comp_dev *dev) struct host_data *hd = comp_get_drvdata(dev); comp_dbg(dev, "host_reset()"); - /* remove callback first for host reset */ - if (hd->chan) - notifier_unregister(dev, hd->chan, NOTIFIER_ID_DMA_COPY); host_zephyr_reset(hd, dev->state); dev->state = COMP_STATE_READY; @@ -1076,9 +1077,9 @@ static int host_reset(struct comp_dev *dev) } /* copy and process stream data from source to sink buffers */ -int host_zephyr_copy(struct host_data *hd, struct comp_dev *dev) +int host_zephyr_copy(struct host_data *hd, struct comp_dev *dev, copy_callback_t cb) { - return hd->copy(hd, dev); + return hd->copy(hd, dev, cb); } static int host_copy(struct comp_dev *dev) @@ -1088,7 +1089,7 @@ static int host_copy(struct comp_dev *dev) if (dev->state != COMP_STATE_ACTIVE) return 0; - return host_zephyr_copy(hd, dev); + return host_zephyr_copy(hd, dev, host_dma_cb); } static int host_get_attribute(struct comp_dev *dev, uint32_t type, diff --git a/src/audio/ipcgtw.c b/src/audio/ipcgtw.c index 7d96aec94d54..28d83c44f171 100644 --- a/src/audio/ipcgtw.c +++ b/src/audio/ipcgtw.c @@ -180,8 +180,9 @@ int ipcgtw_process_cmd(const struct ipc4_ipcgtw_cmd *cmd, dev = find_ipcgtw_by_node_id(in->node_id); if (!dev) { + uint32_t nodeid_connector_val = in->node_id.dw; comp_cl_err(&comp_ipcgtw, "ipcgtw_process_cmd(): node_id not found: %x", - in->node_id.dw); + nodeid_connector_val); return -EINVAL; } diff --git a/src/audio/mixin_mixout/mixin_mixout.c b/src/audio/mixin_mixout/mixin_mixout.c index f549e631ba22..fd17fdf22057 100644 --- a/src/audio/mixin_mixout/mixin_mixout.c +++ b/src/audio/mixin_mixout/mixin_mixout.c @@ -46,24 +46,6 @@ 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 -/* - * Unfortunately, if we have to support a topology with a single mixin - * connected to multiple mixouts, we cannot use simple implementation as in - * mixer component. We either need to use intermediate buffer between mixin and - * mixout, or use a more complex implementation as described below. - * - * This implementation does not use buffer between mixin and mixout. Mixed - * data is written directly to mixout sink buffer. Most of the mixing is done - * by mixins in mixin_process(). Simply speaking, if no data present in mixout - * sink -- mixin just copies its source data to mixout sink. If mixout sink - * has some data (written there previously by some other mixin) -- mixin reads - * data from mixout sink, mixes it with its source data and writes back to - * mixout sink. - * - * Such implementation has less buffer reads/writes than simple implementation - * using intermediate buffer between mixin and mixout. - */ - struct mixin_sink_config { enum ipc4_mixer_mode mixer_mode; uint32_t output_channel_count; @@ -74,24 +56,16 @@ struct mixin_sink_config { /* mixin component private data */ struct mixin_data { - normal_mix_func normal_mix_channel; - remap_mix_func remap_mix_channel; - mute_func mute_channel; struct mixin_sink_config sink_config[MIXIN_MAX_SINKS]; + + void (*gain_func)(struct audio_stream *stream, uint32_t sample_count, + uint16_t gain_mul, uint8_t gain_shift); }; -/* mixout component private data. This can be accessed from different cores. */ +/* mixout component private data */ struct mixout_data { - /* number of currently mixed frames in mixout sink buffer */ - uint32_t mixed_frames; - /* - * Source data is consumed by mixins in mixin_process() but sink data cannot be - * immediately produced. Sink data is produced by mixout in mixout_process() after - * ensuring all connected mixins have mixed their data into mixout sink buffer. - * So for each connected mixin, mixout keeps knowledge of data already consumed - * by mixin but not yet produced in mixout. - */ - uint32_t pending_frames[MIXOUT_MAX_SOURCES]; + void (* mix_func)(const struct audio_stream *source, struct audio_stream *sink, + uint32_t sample_count); }; static int mixin_init(struct processing_module *mod) @@ -122,15 +96,14 @@ static int mixin_init(struct processing_module *mod) dev->ipc_config.frame_fmt = frame_fmt; mod->simple_copy = true; - mod->skip_src_buffer_invalidate = true; return 0; } static int mixout_init(struct processing_module *mod) { - struct module_source_info __sparse_cache *mod_source_info; struct comp_dev *dev = mod->dev; + struct module_data *mod_data = &mod->priv; struct mixout_data *mo_data; enum sof_ipc_frame __sparse_cache frame_fmt, valid_fmt; @@ -140,9 +113,7 @@ static int mixout_init(struct processing_module *mod) if (!mo_data) return -ENOMEM; - mod_source_info = module_source_info_acquire(mod->source_info); - mod_source_info->private = mo_data; - module_source_info_release(mod_source_info); + mod_data->private = mo_data; audio_stream_fmt_conversion(mod->priv.cfg.base_cfg.audio_fmt.depth, mod->priv.cfg.base_cfg.audio_fmt.valid_bit_depth, @@ -151,7 +122,6 @@ static int mixout_init(struct processing_module *mod) dev->ipc_config.frame_fmt = frame_fmt; mod->simple_copy = true; - mod->skip_sink_buffer_writeback = true; return 0; } @@ -169,21 +139,17 @@ static int mixin_free(struct processing_module *mod) static int mixout_free(struct processing_module *mod) { - struct module_source_info __sparse_cache *mod_source_info; + struct mixout_data *md = module_get_private_data(mod); comp_dbg(mod->dev, "mixout_free()"); - - mod_source_info = module_source_info_acquire(mod->source_info); - rfree(mod_source_info->private); - mod_source_info->private = NULL; - module_source_info_release(mod_source_info); + rfree(md); return 0; } -static int mix_and_remap(struct comp_dev *dev, const struct mixin_data *mixin_data, +/* Currently only does copy with gain. Remapping is not implemented. */ +static int copy_and_remap(struct comp_dev *dev, const struct mixin_data *mixin_data, uint16_t sink_index, struct audio_stream __sparse_cache *sink, - uint32_t start_frame, uint32_t mixed_frames, const struct audio_stream __sparse_cache *source, uint32_t frame_count) { const struct mixin_sink_config *sink_config; @@ -197,37 +163,14 @@ static int mix_and_remap(struct comp_dev *dev, const struct mixin_data *mixin_da sink_config = &mixin_data->sink_config[sink_index]; if (sink_config->mixer_mode == IPC4_MIXER_NORMAL_MODE) { - /* Mix streams. mix_channel() is reused here to mix streams, not individual - * channels. To do so, (multichannel) stream is treated as single channel: - * channel count is passed as 1, channel index is 0, frame indices (start_frame - * and mixed_frame) and frame count are multiplied by real stream channel count. - */ - mixin_data->normal_mix_channel(sink, start_frame * sink->channels, - mixed_frames * sink->channels, source, - frame_count * sink->channels, sink_config->gain); + audio_stream_copy(source, 0, sink, 0, frame_count * sink->channels); + + if (sink_config->gain != IPC4_MIXIN_UNITY_GAIN) + mixin_data->gain_func(sink, frame_count * sink->channels, + sink_config->gain, IPC4_MIXIN_GAIN_SHIFT); } else if (sink_config->mixer_mode == IPC4_MIXER_CHANNEL_REMAPPING_MODE) { - int i; - - for (i = 0; i < sink->channels; i++) { - uint8_t source_channel = - (sink_config->output_channel_map >> (i * 4)) & 0xf; - - if (source_channel == 0xf) { - mixin_data->mute_channel(sink, i, start_frame, mixed_frames, - frame_count); - } else { - if (source_channel >= source->channels) { - comp_err(dev, "Out of range chmap: 0x%x, src channels: %u", - sink_config->output_channel_map, - source->channels); - return -EINVAL; - } - mixin_data->remap_mix_channel(sink, i, sink->channels, start_frame, - mixed_frames, source, source_channel, - source->channels, frame_count, - sink_config->gain); - } - } + comp_err(dev, "Remapping mode is not implemented!"); + return -EINVAL; } else { comp_err(dev, "Unexpected mixer mode: %d", sink_config->mixer_mode); return -EINVAL; @@ -236,49 +179,6 @@ static int mix_and_remap(struct comp_dev *dev, const struct mixin_data *mixin_da return 0; } -/* mix silence into stream, i.e. set not yet mixed data in stream to zero */ -static void silence(struct audio_stream __sparse_cache *stream, uint32_t start_frame, - uint32_t mixed_frames, uint32_t frame_count) -{ - uint32_t skip_mixed_frames; - uint8_t *ptr; - uint32_t size; - int n; - - assert(mixed_frames >= start_frame); - skip_mixed_frames = mixed_frames - start_frame; - - if (frame_count <= skip_mixed_frames) - return; - - size = audio_stream_period_bytes(stream, frame_count - skip_mixed_frames); - ptr = (uint8_t *)stream->w_ptr + audio_stream_period_bytes(stream, mixed_frames); - - while (size) { - ptr = audio_stream_wrap(stream, ptr); - n = MIN(audio_stream_bytes_without_wrap(stream, ptr), size); - memset(ptr, 0, n); - size -= n; - ptr += n; - } -} - -/* Most of the mixing is done here on mixin side. mixin mixes its source data - * into each connected mixout sink buffer. Basically, if mixout sink buffer has - * no data, mixin copies its source data into mixout sink buffer. If mixout sink - * buffer has some data (written there by other mixin), mixin reads mixout sink - * buffer data, mixes it with its source data and writes back to mixout sink - * buffer. So after all mixin mixin_process() calls, mixout sink buffer contains - * mixed data. Every mixin calls xxx_consume() on its processed source data, but - * they do not call xxx_produce(). That is done on mixout side in mixout_process(). - * - * Since there is no garantie that mixout processing is done in time we have - * to account for a possibility having not yet produced data in mixout sink - * buffer that was written there on previous run(s) of mixin_process(). So for each - * mixin <--> mixout pair we track consumed_yet_not_produced data amount. - * That value is also used in mixout_process() to calculate how many data was - * actually mixed and so xxx_produce() is called for that amount. - */ static int mixin_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) @@ -286,11 +186,9 @@ static int mixin_process(struct processing_module *mod, struct mixin_data *mixin_data = module_get_private_data(mod); struct comp_dev *dev = mod->dev; uint32_t source_avail_frames, sinks_free_frames; - struct comp_dev *active_mixouts[MIXIN_MAX_SINKS]; uint16_t sinks_ids[MIXIN_MAX_SINKS]; uint32_t bytes_to_consume_from_source_buf; uint32_t frames_to_copy; - int source_index; int i, ret; comp_dbg(dev, "mixin_process()"); @@ -310,255 +208,224 @@ static int mixin_process(struct processing_module *mod, /* first, let's find out how many frames can be now processed -- * it is a nimimal value among frames available in source buffer - * and frames free in each connected mixout sink buffer. + * and frames free in each connected mixin sink buffer. */ for (i = 0; i < num_output_buffers; i++) { - struct comp_buffer __sparse_cache *unused_in_between_buf_c; - struct comp_dev *mixout; - uint16_t sink_id; - struct comp_buffer *sink; - struct mixout_data *mixout_data; - struct processing_module *mixout_mod; - struct module_source_info __sparse_cache *mod_source_info; struct comp_buffer __sparse_cache *sink_c; - uint32_t free_frames, pending_frames; - /* unused buffer between mixin and mixout */ - unused_in_between_buf_c = attr_container_of(output_buffers[i].data, + sink_c = attr_container_of(output_buffers[i].data, struct comp_buffer __sparse_cache, stream, __sparse_cache); - mixout = unused_in_between_buf_c->sink; - sink_id = IPC4_SRC_QUEUE_ID(unused_in_between_buf_c->id); - - active_mixouts[i] = mixout; - sinks_ids[i] = sink_id; - - sink = list_first_item(&mixout->bsink_list, struct comp_buffer, source_list); - - mixout_mod = comp_get_drvdata(mixout); - mod_source_info = module_source_info_acquire(mixout_mod->source_info); - mixout_data = mod_source_info->private; - source_index = find_module_source_index(mod_source_info, dev); - if (source_index < 0) { - comp_err(dev, "No source info"); - module_source_info_release(mod_source_info); - return -EINVAL; - } - - sink_c = buffer_acquire(sink); + sinks_ids[i] = IPC4_SRC_QUEUE_ID(sink_c->id); /* Normally this should never happen as we checked above - * that mixout is in active state and so its sink buffer - * should be already initialized in mixout .params(). + * that mixin is in active state and so its sink buffer + * should be already initialized in mixin .params(). */ if (!sink_c->hw_params_configured) { - comp_err(dev, "Uninitialized mixout sink buffer!"); - buffer_release(sink_c); - module_source_info_release(mod_source_info); + comp_err(dev, "Uninitialized mixin sink buffer!"); return -EINVAL; } - free_frames = audio_stream_get_free_frames(&sink_c->stream); - - /* mixout sink buffer may still have not yet produced data -- data - * consumed and written there by mixin on previous mixin_process() run. - * We do NOT want to overwrite that data. - */ - pending_frames = mixout_data->pending_frames[source_index]; - assert(free_frames >= pending_frames); - sinks_free_frames = MIN(sinks_free_frames, free_frames - pending_frames); - - buffer_release(sink_c); - module_source_info_release(mod_source_info); + sinks_free_frames = MIN(audio_stream_get_free_frames(output_buffers[i].data), + sinks_free_frames); } if (source_avail_frames > 0) { - struct comp_buffer __sparse_cache *source_c; - frames_to_copy = MIN(source_avail_frames, sinks_free_frames); bytes_to_consume_from_source_buf = audio_stream_period_bytes(input_buffers[0].data, frames_to_copy); - if (bytes_to_consume_from_source_buf > 0) { - input_buffers[0].consumed = bytes_to_consume_from_source_buf; - source_c = attr_container_of(input_buffers[0].data, - struct comp_buffer __sparse_cache, - stream, __sparse_cache); - buffer_stream_invalidate(source_c, bytes_to_consume_from_source_buf); - } + input_buffers[0].consumed = bytes_to_consume_from_source_buf; } else { /* if source does not produce any data -- do NOT block mixing but generate * silence as that source output. * * here frames_to_copy is silence size. */ + /* FIXME: This will not work with 44.1 kHz !!! */ frames_to_copy = MIN(dev->frames, sinks_free_frames); } - /* iterate over all connected mixouts and mix source data into each mixout sink buffer */ for (i = 0; i < num_output_buffers; i++) { - struct comp_dev *mixout; - struct comp_buffer *sink; - struct mixout_data *mixout_data; - struct module_source_info __sparse_cache *mod_source_info; - struct processing_module *mixout_mod; - uint32_t start_frame; - struct comp_buffer __sparse_cache *sink_c; - uint32_t writeback_size; - - mixout = active_mixouts[i]; - sink = list_first_item(&mixout->bsink_list, struct comp_buffer, source_list); - - mixout_mod = comp_get_drvdata(mixout); - mod_source_info = module_source_info_acquire(mixout_mod->source_info); - mixout_data = mod_source_info->private; - source_index = find_module_source_index(mod_source_info, dev); - if (source_index < 0) { - comp_err(dev, "No source info"); - module_source_info_release(mod_source_info); - return -EINVAL; - } - - /* Skip data from previous run(s) not yet produced in mixout_process(). - * Normally start_frame would be 0 unless mixout pipeline has serious - * performance problems with processing data on time in mixout. - */ - start_frame = mixout_data->pending_frames[source_index]; - - sink_c = buffer_acquire(sink); - /* if source does not produce any data but mixin is in active state -- generate * silence instead of that source data */ if (source_avail_frames == 0) { /* generate silence */ - silence(&sink_c->stream, start_frame, mixout_data->mixed_frames, - frames_to_copy); + audio_stream_set_zero(output_buffers[i].data, + audio_stream_period_bytes(output_buffers[i].data, + frames_to_copy)); } else { - /* basically, if sink buffer has no data -- copy source data there, if - * sink buffer has some data (written by another mixin) mix that data - * with source data. - */ - ret = mix_and_remap(dev, mixin_data, sinks_ids[i], &sink_c->stream, - start_frame, mixout_data->mixed_frames, + ret = copy_and_remap(dev, mixin_data, sinks_ids[i], output_buffers[i].data, input_buffers[0].data, frames_to_copy); if (ret < 0) { - buffer_release(sink_c); - module_source_info_release(mod_source_info); return ret; } } - /* it would be better to writeback memory region starting from start_frame and - * of frames_to_copy size (converted to bytes, of course). However, seems - * there is no appropreate API. Anyway, start_frame would be 0 most of the time. - */ - writeback_size = audio_stream_period_bytes(&sink_c->stream, - frames_to_copy + start_frame); - if (writeback_size > 0) - buffer_stream_writeback(sink_c, writeback_size); - buffer_release(sink_c); + output_buffers[i].size = audio_stream_period_bytes(output_buffers[i].data, + frames_to_copy); + } - mixout_data->pending_frames[source_index] += frames_to_copy; + return 0; +} - if (frames_to_copy + start_frame > mixout_data->mixed_frames) - mixout_data->mixed_frames = frames_to_copy + start_frame; +static void apply_gain_s16(struct audio_stream *stream, uint32_t sample_count, + uint16_t gain_mul, uint8_t gain_shift) +{ + int16_t *ptr = stream->w_ptr; - module_source_info_release(mod_source_info); + while (sample_count > 0) { + uint32_t i, n; + + ptr = audio_stream_wrap(stream, ptr); + n = MIN(audio_stream_samples_without_wrap_s16(stream, ptr), sample_count); + + for (i = 0; i < n; i++) { + *ptr = ((int32_t)*ptr * gain_mul) >> gain_shift; + ptr++; + } + + sample_count -= n; } +} - return 0; +static void apply_gain_s32(struct audio_stream *stream, uint32_t sample_count, + uint16_t gain_mul, uint8_t gain_shift) +{ + int32_t *ptr = stream->w_ptr; + + while (sample_count > 0) { + uint32_t i, n; + + ptr = audio_stream_wrap(stream, ptr); + n = MIN(audio_stream_samples_without_wrap_s32(stream, ptr), sample_count); + + for (i = 0; i < n; i++) { + *ptr = ((int64_t)*ptr * gain_mul) >> gain_shift; + ptr++; + } + + sample_count -= n; + } +} + +static void mix_s16(const struct audio_stream *source, struct audio_stream *sink, + uint32_t sample_count) +{ + int16_t *src = source->r_ptr; + int16_t *dst = sink->w_ptr; + + while (sample_count > 0) { + uint32_t i, n; + + src = audio_stream_wrap(source, src); + dst = audio_stream_wrap(sink, dst); + + n = MIN(audio_stream_samples_without_wrap_s16(source, src), + audio_stream_samples_without_wrap_s16(sink, dst)); + n = MIN(n, sample_count); + + for (i = 0; i < n; i++) { + *dst = sat_int16((int32_t)*src + (int32_t)*dst); + src++; + dst++; + } + + sample_count -= n; + } +} + +static void mix_s24(const struct audio_stream *source, struct audio_stream *sink, + uint32_t sample_count) +{ + int32_t *src = source->r_ptr; + int32_t *dst = sink->w_ptr; + + while (sample_count > 0) { + uint32_t i, n; + + src = audio_stream_wrap(source, src); + dst = audio_stream_wrap(sink, dst); + + n = MIN(audio_stream_samples_without_wrap_s32(source, src), + audio_stream_samples_without_wrap_s32(sink, dst)); + n = MIN(n, sample_count); + + for (i = 0; i < n; i++) { + *dst = sat_int24(*src + *dst); + src++; + dst++; + } + + sample_count -= n; + } +} + +static void mix_s32(const struct audio_stream *source, struct audio_stream *sink, + uint32_t sample_count) +{ + int32_t *src = source->r_ptr; + int32_t *dst = sink->w_ptr; + + while (sample_count > 0) { + uint32_t i, n; + + src = audio_stream_wrap(source, src); + dst = audio_stream_wrap(sink, dst); + + n = MIN(audio_stream_samples_without_wrap_s32(source, src), + audio_stream_samples_without_wrap_s32(sink, dst)); + n = MIN(n, sample_count); + + for (i = 0; i < n; i++) { + *dst = sat_int32((int64_t)*src + (int64_t)*dst); + src++; + dst++; + } + + sample_count -= n; + } } -/* mixout just calls xxx_produce() on data mixed into its sink buffer by - * mixins. - */ static int mixout_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 module_source_info __sparse_cache *mod_source_info; struct comp_dev *dev = mod->dev; - struct mixout_data *md; - uint32_t frames_to_produce = INT32_MAX; - uint32_t pending_frames; - uint32_t sink_bytes; + struct mixout_data *md = module_get_private_data(mod); + uint32_t avail_samples = INT32_MAX; int i; comp_dbg(dev, "mixout_process()"); - mod_source_info = module_source_info_acquire(mod->source_info); - md = mod_source_info->private; - - /* iterate over all connected mixins to find minimal value of frames they consumed - * (i.e., mixed into mixout sink buffer). That is the amount that can/should be - * produced now. - */ for (i = 0; i < num_input_buffers; i++) { - const struct audio_stream __sparse_cache *source_stream; - struct comp_buffer __sparse_cache *unused_in_between_buf; - struct comp_dev *source; - int source_index; - - source_stream = input_buffers[i].data; - unused_in_between_buf = attr_container_of(source_stream, - struct comp_buffer __sparse_cache, - stream, __sparse_cache); - - source = unused_in_between_buf->source; + avail_samples = MIN(audio_stream_get_avail_samples(input_buffers[i].data), + avail_samples); + } - source_index = find_module_source_index(mod_source_info, source); - /* this shouldn't happen but skip even if it does and move to the next source */ - if (source_index < 0) - continue; + if (avail_samples > 0 && avail_samples < INT32_MAX) { + uint32_t samples_to_mix = MIN(audio_stream_get_free_samples(output_buffers[0].data), avail_samples); + uint32_t bytes_to_mix = samples_to_mix * audio_stream_sample_bytes(output_buffers[0].data); - pending_frames = md->pending_frames[source_index]; + audio_stream_copy(input_buffers[0].data, 0, output_buffers[0].data, 0, samples_to_mix); + input_buffers[0].consumed = bytes_to_mix; - if (source->state == COMP_STATE_ACTIVE || pending_frames) - frames_to_produce = MIN(frames_to_produce, pending_frames); - } - - if (frames_to_produce > 0 && frames_to_produce < INT32_MAX) { - for (i = 0; i < num_input_buffers; i++) { - const struct audio_stream __sparse_cache *source_stream; - struct comp_buffer __sparse_cache *unused_in_between_buf; - struct comp_dev *source; - int source_index; - uint32_t pending_frames; - - source_stream = input_buffers[i].data; - unused_in_between_buf = attr_container_of(source_stream, - struct comp_buffer __sparse_cache, - stream, __sparse_cache); - - source = unused_in_between_buf->source; - - source_index = find_module_source_index(mod_source_info, source); - if (source_index < 0) - continue; - - pending_frames = md->pending_frames[source_index]; - if (pending_frames >= frames_to_produce) - md->pending_frames[source_index] -= frames_to_produce; - else - md->pending_frames[source_index] = 0; + for (i = 1; i < num_input_buffers; i++) { + md->mix_func(input_buffers[i].data, output_buffers[0].data, samples_to_mix); + input_buffers[i].consumed = bytes_to_mix; } - assert(md->mixed_frames >= frames_to_produce); - md->mixed_frames -= frames_to_produce; - - sink_bytes = frames_to_produce * - audio_stream_frame_bytes(output_buffers[0].data); - output_buffers[0].size = sink_bytes; + output_buffers[0].size = bytes_to_mix; } else { - sink_bytes = dev->frames * audio_stream_frame_bytes(output_buffers[0].data); + /* FIXME: That will not work with 44.1 kHz !!! */ + uint32_t sink_bytes = MIN(audio_stream_get_free_frames(output_buffers[0].data), dev->frames) * audio_stream_frame_bytes(output_buffers[0].data); if (!audio_stream_set_zero(output_buffers[0].data, sink_bytes)) output_buffers[0].size = sink_bytes; else output_buffers[0].size = 0; } - module_source_info_release(mod_source_info); - return 0; } @@ -569,20 +436,21 @@ static int mixin_reset(struct processing_module *mod) comp_dbg(dev, "mixin_reset()"); - mixin_data->normal_mix_channel = NULL; - mixin_data->remap_mix_channel = NULL; - mixin_data->mute_channel = NULL; + mixin_data->gain_func = NULL; return 0; } static int mixout_reset(struct processing_module *mod) { + struct mixout_data *mixout_data = module_get_private_data(mod); struct comp_dev *dev = mod->dev; struct list_item *blist; comp_dbg(dev, "mixout_reset()"); + mixout_data->mix_func = NULL; + /* FIXME: move this to module_adapter_reset() */ if (dev->pipeline->source_comp->direction == SOF_IPC_STREAM_PLAYBACK) { list_for_item(blist, &dev->bsource_list) { @@ -622,9 +490,6 @@ static int mixin_params(struct processing_module *mod) base_module_cfg_to_stream_params(&mod->priv.cfg.base_cfg, params); - /* Buffers between mixins and mixouts are not used (mixin writes data directly to mixout - * sink). But, anyway, let's setup these buffers properly just in case. - */ list_for_item(blist, &dev->bsink_list) { struct comp_buffer *sink; struct comp_buffer __sparse_cache *sink_c; @@ -701,22 +566,17 @@ static int mixin_prepare(struct processing_module *mod) /* currently inactive so setup mixer */ switch (fmt) { case SOF_IPC_FRAME_S16_LE: + md->gain_func = apply_gain_s16; + break; case SOF_IPC_FRAME_S24_4LE: case SOF_IPC_FRAME_S32_LE: - md->normal_mix_channel = normal_mix_get_processing_function(fmt); - md->remap_mix_channel = remap_mix_get_processing_function(fmt); - md->mute_channel = mute_mix_get_processing_function(fmt); + md->gain_func = apply_gain_s32; break; default: comp_err(dev, "unsupported data format %d", fmt); return -EINVAL; } - if (!md->normal_mix_channel || !md->remap_mix_channel || !md->mute_channel) { - comp_err(dev, "have not found the suitable processing function"); - return -EINVAL; - } - return 0; } @@ -796,10 +656,12 @@ static int mixout_params(struct processing_module *mod) static int mixout_prepare(struct processing_module *mod) { - struct module_source_info __sparse_cache *mod_source_info; struct comp_dev *dev = mod->dev; - struct mixout_data *md; - int ret, i; + struct mixout_data *md = module_get_private_data(mod); + struct comp_buffer *sink; + struct comp_buffer __sparse_cache *sink_c; + enum sof_ipc_frame fmt; + int ret; ret = mixout_params(mod); if (ret < 0) @@ -807,17 +669,26 @@ static int mixout_prepare(struct processing_module *mod) comp_dbg(dev, "mixout_prepare()"); - /* - * Since mixout sink buffer stream is reset on .prepare(), let's - * reset counters for not yet produced frames in that buffer. - */ - mod_source_info = module_source_info_acquire(mod->source_info); - md = mod_source_info->private; - md->mixed_frames = 0; + sink = list_first_item(&dev->bsink_list, struct comp_buffer, source_list); + sink_c = buffer_acquire(sink); + fmt = sink_c->stream.valid_sample_fmt; + buffer_release(sink_c); - for (i = 0; i < MIXOUT_MAX_SOURCES; i++) - md->pending_frames[i] = 0; - module_source_info_release(mod_source_info); + /* currently inactive so setup mixout */ + switch (fmt) { + case SOF_IPC_FRAME_S16_LE: + md->mix_func = mix_s16; + break; + case SOF_IPC_FRAME_S24_4LE: + md->mix_func = mix_s24; + break; + case SOF_IPC_FRAME_S32_LE: + md->mix_func = mix_s32; + break; + default: + comp_err(dev, "unsupported data format %d", fmt); + return -EINVAL; + } return 0; } diff --git a/src/audio/module_adapter/iadk/system_agent.cpp b/src/audio/module_adapter/iadk/system_agent.cpp index 6e03a065d49f..b739501017a5 100644 --- a/src/audio/module_adapter/iadk/system_agent.cpp +++ b/src/audio/module_adapter/iadk/system_agent.cpp @@ -63,7 +63,7 @@ namespace system { /* Structure storing handles to system service operations */ -AdspSystemService SystemAgent::system_service_ = { +adsp_system_service SystemAgent::system_service_ = { SystemServiceLogMessage, SystemServiceSafeMemcpy, SystemServiceSafeMemmove, diff --git a/src/audio/module_adapter/library/native_system_agent.c b/src/audio/module_adapter/library/native_system_agent.c new file mode 100644 index 000000000000..8d932320ef05 --- /dev/null +++ b/src/audio/module_adapter/library/native_system_agent.c @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright(c) 2023 Intel Corporation. All rights reserved. + * + * Author: Pawel Dobrowolski + */ + +#include +#include +#include +#include + +/* The create_instance_f is a function call type known in module. The module entry_point + * points to this type of function which starts module creation. + */ + +void SystemServiceLogMessage(AdspLogPriority log_priority, uint32_t log_entry, + AdspLogHandle const *log_handle, uint32_t param1, + uint32_t param2, uint32_t param3, uint32_t param4); + +AdspErrorCode SystemServiceSafeMemcpy(void *RESTRICT dst, size_t maxlen, + const void *RESTRICT src, size_t len); + +AdspErrorCode SystemServiceSafeMemmove(void *dst, size_t maxlen, + const void *src, size_t len); + +void *SystemServiceVecMemset(void *dst, int c, size_t len); + +AdspErrorCode SystemServiceCreateNotification(NotificationParams *params, + uint8_t *notification_buffer, + uint32_t notification_buffer_size, + AdspNotificationHandle *handle); + +AdspErrorCode SystemServiceSendNotificationMessage(NotificationTarget notification_target, + AdspNotificationHandle message, + uint32_t actual_payload_size); + +AdspErrorCode SystemServiceGetInterface(AdspIfaceId id, SystemServiceIface **iface); + +typedef void* (*native_create_instance_f)(void *mod_cfg, void *parent_ppl, + void **mod_ptr); + +struct native_system_agent native_sys_agent = { + .system_service = { + .LogMessage = SystemServiceLogMessage, + .SafeMemcpy = SystemServiceSafeMemcpy, + .SafeMemmove = SystemServiceSafeMemmove, + .VecMemset = SystemServiceVecMemset, + .NotificationCreate = SystemServiceCreateNotification, + .NotificationSend = SystemServiceSendNotificationMessage, + .GetInterface = SystemServiceGetInterface + }, + }; + +void *native_system_agent_start(struct adsp_system_service **sys_service, uint32_t entry_point, + uint32_t module_id, uint32_t instance_id, + uint32_t core_id, uint32_t log_handle, void *mod_cfg) +{ + 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; + + void *system_agent_p = &native_sys_agent; + + *sys_service = &native_sys_agent.system_service; + + native_create_instance_f ci = (native_create_instance_f)(entry_point); + + return ci(mod_cfg, NULL, &system_agent_p); +} diff --git a/src/audio/module_adapter/module/iadk_modules.c b/src/audio/module_adapter/module/iadk_modules.c index 8c728bba5594..5e915fce6380 100644 --- a/src/audio/module_adapter/module/iadk_modules.c +++ b/src/audio/module_adapter/module/iadk_modules.c @@ -8,7 +8,10 @@ #include #include #include +#include #include +#include +#include /* Intel module adapter is an extension to SOF module adapter component that allows to integrate * modules developed under IADK (Intel Audio Development Kit) Framework. IADK modules uses uniform @@ -23,9 +26,9 @@ * Therefore C++ function, structures and variables definition are here kept with original form from * IADK Framework. This provides binary compatibility for already developed 3rd party modules. * - * Since IADK modules uses ProcessingModuleInterface to control/data transfer and AdspSystemService - * to use base FW services from internal module code, there is a communication shim layer defined - * in intel directory. + * Since IADK modules uses ProcessingModuleInterface to control/data transfer + * and adsp_system_service to use base FW services from internal module code, + * there is a communication shim layer defined in intel directory. * * Since ProcessingModuleInterface consists of virtual functions, there are C++ -> C wrappers * defined to access the interface calls from SOF code. @@ -80,8 +83,32 @@ static int iadk_modules_init(struct processing_module *mod) uint32_t instance_id = IPC4_INST_ID(mod->dev->ipc_config.id); uint32_t log_handle = (uint32_t) mod->dev->drv->tctx; /* Connect loadable module interfaces with module adapter entity. */ - void *mod_adp = system_agent_start(md->module_entry_point, module_id, - instance_id, 0, log_handle, &mod_cfg); + /* Check if native Zephyr lib is loaded */ + struct sof_man_fw_desc *desc; + + desc = lib_manager_get_library_module_desc(module_id); + if (!desc) { + comp_err(dev, "iadk_modules_init(): Failed to load manifest"); + return -ENOMEM; + } + struct sof_man_module *module_entry = + (struct sof_man_module *)((char *)desc + SOF_MAN_MODULE_OFFSET(0)); + + sof_module_build_info *mod_buildinfo = + (sof_module_build_info *)(module_entry->segment[SOF_MAN_SEGMENT_TEXT].v_base_addr); + + void *mod_adp; + + /* Check if module is FDK*/ + if (mod_buildinfo->api_version_number.fields.major < _MAJOR_API_MODULE_VERSION) { + mod_adp = system_agent_start(md->module_entry_point, module_id, + instance_id, 0, log_handle, &mod_cfg); + } else { + /* If not start agent for sof loadable */ + mod->is_native_sof = true; + mod_adp = native_system_agent_start(&mod->sys_service, md->module_entry_point, module_id, instance_id, + 0, log_handle, &mod_cfg); + } md->module_adapter = mod_adp; @@ -102,7 +129,14 @@ static int iadk_modules_init(struct processing_module *mod) md->mpd.out_buff_size = src_cfg->obs; /* Call module specific init function if exists. */ - ret = iadk_wrapper_init(md->module_adapter); + if (mod->is_native_sof) { + struct module_interface *mod_in = + (struct module_interface *)md->module_adapter; + + ret = mod_in->init(mod); + } else { + ret = iadk_wrapper_init(md->module_adapter); + } return ret; } @@ -125,8 +159,15 @@ static int iadk_modules_prepare(struct processing_module *mod) comp_info(dev, "iadk_modules_prepare()"); /* Call module specific prepare function if exists. */ - ret = iadk_wrapper_prepare(mod->priv.module_adapter); - return 0; + if (mod->is_native_sof) { + struct module_interface *mod_in = + (struct module_interface *)mod->priv.module_adapter; + + ret = mod_in->prepare(mod); + } else { + ret = iadk_wrapper_prepare(mod->priv.module_adapter); + } + return ret; } static int iadk_modules_init_process(struct processing_module *mod) @@ -171,10 +212,17 @@ static int iadk_modules_process(struct processing_module *mod, i++; } /* Call module specific process function. */ - ret = iadk_wrapper_process(mod->priv.module_adapter, - input_buffers, num_input_buffers, - output_buffers, num_output_buffers); - + if (mod->is_native_sof) { + struct module_interface *mod_in = + (struct module_interface *)mod->priv.module_adapter; + + ret = mod_in->process(mod, input_buffers, num_input_buffers, output_buffers, + num_output_buffers); + } else { + ret = iadk_wrapper_process(mod->priv.module_adapter, input_buffers, + num_input_buffers, output_buffers, + num_output_buffers); + } return ret; } @@ -193,7 +241,14 @@ static int iadk_modules_free(struct processing_module *mod) int ret = 0; comp_info(dev, "iadk_modules_free()"); - ret = iadk_wrapper_free(mod->priv.module_adapter); + if (mod->is_native_sof) { + struct module_interface *mod_in = + (struct module_interface *)mod->priv.module_adapter; + + ret = mod_in->free(mod); + } else { + ret = iadk_wrapper_free(mod->priv.module_adapter); + } rfree(md->mpd.in_buff); rfree(md->mpd.out_buff); @@ -226,6 +281,13 @@ static int iadk_modules_set_configuration(struct processing_module *mod, uint32_ size_t fragment_size, uint8_t *response, size_t response_size) { + if (mod->is_native_sof) { + struct module_interface *mod_in = + (struct module_interface *)mod->priv.module_adapter; + + return mod_in->set_configuration(mod, config_id, pos, data_offset_size, fragment, + fragment_size, response, response_size); + } return iadk_wrapper_set_configuration(mod->priv.module_adapter, config_id, pos, data_offset_size, fragment, fragment_size, response, response_size); @@ -247,6 +309,13 @@ static int iadk_modules_get_configuration(struct processing_module *mod, uint32_ uint32_t *data_offset_size, uint8_t *fragment, size_t fragment_size) { + if (mod->is_native_sof) { + struct module_interface *mod_in = + (struct module_interface *)mod->priv.module_adapter; + + return mod_in->get_configuration(mod, config_id, data_offset_size, + fragment, fragment_size); + } return iadk_wrapper_get_configuration(mod->priv.module_adapter, config_id, MODULE_CFG_FRAGMENT_SINGLE, *data_offset_size, fragment, fragment_size); @@ -262,6 +331,12 @@ static int iadk_modules_get_configuration(struct processing_module *mod, uint32_ static int iadk_modules_set_processing_mode(struct processing_module *mod, enum module_processing_mode mode) { + if (mod->is_native_sof) { + struct module_interface *mod_in = + (struct module_interface *)mod->priv.module_adapter; + + return mod_in->set_processing_mode(mod, mode); + } return iadk_wrapper_set_processing_mode(mod->priv.module_adapter, mode); } @@ -285,6 +360,12 @@ static enum module_processing_mode iadk_modules_get_processing_mode(struct proce */ static int iadk_modules_reset(struct processing_module *mod) { + if (mod->is_native_sof) { + struct module_interface *mod_in = + (struct module_interface *)mod->priv.module_adapter; + + return mod_in->reset(mod); + } return iadk_wrapper_reset(mod->priv.module_adapter); } diff --git a/src/audio/module_adapter/module_adapter.c b/src/audio/module_adapter/module_adapter.c index 869221c83460..e53eda4212e8 100644 --- a/src/audio/module_adapter/module_adapter.c +++ b/src/audio/module_adapter/module_adapter.c @@ -55,7 +55,7 @@ struct comp_dev *module_adapter_new(const struct comp_driver *drv, dev->ipc_config = *config; dev->drv = drv; - mod = rzalloc(SOF_MEM_ZONE_RUNTIME, 0, SOF_MEM_CAPS_RAM, sizeof(*mod)); + mod = rzalloc(SOF_MEM_ZONE_RUNTIME_SHARED, 0, SOF_MEM_CAPS_RAM, sizeof(*mod)); if (!mod) { comp_err(dev, "module_adapter_new(), failed to allocate memory for module"); rfree(dev); diff --git a/src/audio/pcm_converter/pcm_converter_generic.c b/src/audio/pcm_converter/pcm_converter_generic.c index 1cc7dc38685d..9f32dc68ac1d 100644 --- a/src/audio/pcm_converter/pcm_converter_generic.c +++ b/src/audio/pcm_converter/pcm_converter_generic.c @@ -843,12 +843,13 @@ 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, - ipc4_gtw_all & ~(ipc4_gtw_link | ipc4_gtw_alh | ipc4_gtw_host), ipc4_bidirection, - audio_stream_copy}, + ipc4_gtw_all & ~(ipc4_gtw_link | ipc4_gtw_alh | ipc4_gtw_host | ipc4_gtw_dmic), + ipc4_bidirection, audio_stream_copy}, { SOF_IPC_FRAME_S32_LE, SOF_IPC_FRAME_S24_4LE, SOF_IPC_FRAME_S32_LE, SOF_IPC_FRAME_S24_4LE, ipc4_gtw_link | ipc4_gtw_alh, ipc4_playback, pcm_convert_s24_to_s32 }, { SOF_IPC_FRAME_S32_LE, SOF_IPC_FRAME_S24_4LE, SOF_IPC_FRAME_S32_LE, SOF_IPC_FRAME_S24_4LE, - ipc4_gtw_link | ipc4_gtw_alh, ipc4_capture, pcm_convert_s32_to_s24 }, + ipc4_gtw_link | ipc4_gtw_alh | ipc4_gtw_dmic, ipc4_capture, + pcm_convert_s32_to_s24 }, { SOF_IPC_FRAME_S32_LE, SOF_IPC_FRAME_S24_4LE, SOF_IPC_FRAME_S32_LE, SOF_IPC_FRAME_S24_4LE, ipc4_gtw_host, ipc4_playback, pcm_convert_s32_to_s24 }, { SOF_IPC_FRAME_S32_LE, SOF_IPC_FRAME_S24_4LE, SOF_IPC_FRAME_S32_LE, SOF_IPC_FRAME_S24_4LE, diff --git a/src/audio/pcm_converter/pcm_converter_hifi3.c b/src/audio/pcm_converter/pcm_converter_hifi3.c index b727fdae209b..e89b25649945 100644 --- a/src/audio/pcm_converter/pcm_converter_hifi3.c +++ b/src/audio/pcm_converter/pcm_converter_hifi3.c @@ -1237,12 +1237,13 @@ 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, - ipc4_gtw_all & ~(ipc4_gtw_link | ipc4_gtw_alh | ipc4_gtw_host), ipc4_bidirection, - audio_stream_copy}, + ipc4_gtw_all & ~(ipc4_gtw_link | ipc4_gtw_alh | ipc4_gtw_host | ipc4_gtw_dmic), + ipc4_bidirection, audio_stream_copy}, { SOF_IPC_FRAME_S32_LE, SOF_IPC_FRAME_S24_4LE, SOF_IPC_FRAME_S32_LE, SOF_IPC_FRAME_S24_4LE, ipc4_gtw_link | ipc4_gtw_alh, ipc4_playback, pcm_convert_s24_to_s32 }, { SOF_IPC_FRAME_S32_LE, SOF_IPC_FRAME_S24_4LE, SOF_IPC_FRAME_S32_LE, SOF_IPC_FRAME_S24_4LE, - ipc4_gtw_link | ipc4_gtw_alh, ipc4_capture, pcm_convert_s32_to_s24 }, + ipc4_gtw_link | ipc4_gtw_alh | ipc4_gtw_dmic, ipc4_capture, + pcm_convert_s32_to_s24 }, { SOF_IPC_FRAME_S32_LE, SOF_IPC_FRAME_S24_4LE, SOF_IPC_FRAME_S32_LE, SOF_IPC_FRAME_S24_4LE, ipc4_gtw_host, ipc4_playback, pcm_convert_s32_to_s24 }, { SOF_IPC_FRAME_S32_LE, SOF_IPC_FRAME_S24_4LE, SOF_IPC_FRAME_S32_LE, SOF_IPC_FRAME_S24_4LE, diff --git a/src/audio/pipeline/pipeline-graph.c b/src/audio/pipeline/pipeline-graph.c index 809ab7c49d40..bf22fbdf3b23 100644 --- a/src/audio/pipeline/pipeline-graph.c +++ b/src/audio/pipeline/pipeline-graph.c @@ -122,7 +122,7 @@ struct pipeline *pipeline_new(uint32_t pipeline_id, uint32_t priority, uint32_t heap_trace_all(0); /* allocate new pipeline */ - p = rzalloc(SOF_MEM_ZONE_RUNTIME, 0, SOF_MEM_CAPS_RAM, sizeof(*p)); + p = rzalloc(SOF_MEM_ZONE_RUNTIME_SHARED, 0, SOF_MEM_CAPS_RAM, sizeof(*p)); if (!p) { pipe_cl_err("pipeline_new(): Out of Memory"); return NULL; diff --git a/src/audio/pipeline/pipeline-stream.c b/src/audio/pipeline/pipeline-stream.c index ab60c11b3c16..3203d53df675 100644 --- a/src/audio/pipeline/pipeline-stream.c +++ b/src/audio/pipeline/pipeline-stream.c @@ -15,8 +15,11 @@ #include #include #include +#include #include #include +#include +#include #include #include @@ -24,6 +27,7 @@ #include LOG_MODULE_DECLARE(pipe, CONFIG_SOF_LOG_LEVEL); +// LOG_MODULE_REGISTER(cpsBudget, CONFIG_SOF_LOG_LEVEL); /* * Check whether pipeline is incapable of acquiring data for capture. @@ -266,24 +270,118 @@ static void pipeline_trigger_xrun(struct pipeline *p, struct comp_dev **host) } while (true); } +static int add_pipeline_cps_consumption(struct comp_dev *current, + struct comp_buffer *calling_buf, + struct pipeline_walk_context *ctx, int dir) +{ + struct pipeline_data *ppl_data = ctx->comp_data; + struct ipc4_base_module_cfg *cd = NULL; + + pipe_dbg(ppl_data->p, "pipeline_comp_complete(), current->comp.id = %u, 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"); + return 0; + } + + /* complete component init */ + current->pipeline = ppl_data->p; + + /* modules created throug module adapter have different priv_data */ + if(current->drv->type != SOF_COMP_MODULE_ADAPTER) { + cd = comp_get_drvdata(current); + } else { + struct processing_module *mod = comp_get_drvdata(current); + struct module_data *md = &mod->priv; + cd = &md->cfg.base_cfg; + } + + if (cd->cpc == 0) { + /* Use maximum clock budget, assume 1ms chunk size */ + cd->cpc = CLK_MAX_CPU_HZ / 1000; + tr_warn(pipe, + "0 CPS requested for module: %#x, core: %d using safe max CPC: %u", + current->ipc_config.id, ppl_data->p->core, cd->cpc); + } + + int kcps = cd->cpc * 1000 / ppl_data->p->period; + tr_info(pipe, "Registering KCPS consumption: %d, core: %d", kcps, ppl_data->p->core); + core_kcps_adjust(ppl_data->p->core, kcps); + + int summary_cps = core_kcps_get(ppl_data->p->core); + tr_info(pipe, "Sum of KCPS consumption: %d, core: %d", summary_cps, ppl_data->p->core); + return pipeline_for_each_comp(current, ctx, dir); +} + +static int remove_pipeline_cps_consumption(struct comp_dev *current, + struct comp_buffer *calling_buf, + struct pipeline_walk_context *ctx, int dir) +{ + struct pipeline_data *ppl_data = ctx->comp_data; + struct ipc4_base_module_cfg *cd = NULL; + + pipe_dbg(ppl_data->p, "pipeline_comp_complete(), current->comp.id = %u, 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"); + return 0; + } + + /* complete component init */ + current->pipeline = ppl_data->p; + + /* modules created throug module adapter have different priv_data */ + if(current->drv->type != SOF_COMP_MODULE_ADAPTER) { + cd = comp_get_drvdata(current); + } else { + struct processing_module *mod = comp_get_drvdata(current); + struct module_data *md = &mod->priv; + cd = &md->cfg.base_cfg; + } + + int kcps = cd->cpc * 1000/ppl_data->p->period; + core_kcps_adjust(ppl_data->p->core, -kcps); /* 1000 chunks per second, so cpc value matches kcps? */ + tr_info(pipe, "Unregistering KCPS consumption: %d, core: %d", kcps, ppl_data->p->core); + int summary_cps = core_kcps_get(ppl_data->p->core); + tr_info(pipe, "Sum of KCPS consumption: %d, core: %d", summary_cps, ppl_data->p->core); + return pipeline_for_each_comp(current, ctx, dir); +} + + + + /* trigger pipeline in IPC context */ int pipeline_trigger(struct pipeline *p, struct comp_dev *host, int cmd) { int ret; - + bool clocks_handled = false; + struct pipeline_data data; + struct pipeline_walk_context walk_ctx = { + .comp_func = remove_pipeline_cps_consumption, + .comp_data = &data, + }; pipe_info(p, "pipe trigger cmd %d", cmd); p->trigger.aborted = false; switch (cmd) { case COMP_TRIGGER_PAUSE: + /* dirty - dont add kcps on fallthrough */ + clocks_handled = true; + /* setup walking ctx for adding consumption */ + data.start = p->source_comp; + data.p = p; + walk_ctx.comp_func = remove_pipeline_cps_consumption; + + ret = walk_ctx.comp_func(p->source_comp, NULL, &walk_ctx, PPL_DIR_DOWNSTREAM); case COMP_TRIGGER_STOP: if (p->status == COMP_STATE_PAUSED || p->xrun_bytes) { /* The task isn't running, trigger inline */ ret = pipeline_trigger_run(p, host, cmd); return ret < 0 ? ret : 0; } - COMPILER_FALLTHROUGH; case COMP_TRIGGER_XRUN: if (cmd == COMP_TRIGGER_XRUN) @@ -293,6 +391,13 @@ int pipeline_trigger(struct pipeline *p, struct comp_dev *host, int cmd) case COMP_TRIGGER_PRE_RELEASE: case COMP_TRIGGER_PRE_START: /* Add all connected pipelines to the list and trigger them all */ + /* setup walking ctx for removing consumption */ + data.start = p->source_comp; + data.p = p; + walk_ctx.comp_func = add_pipeline_cps_consumption; + + if(!clocks_handled) + ret = walk_ctx.comp_func(p->source_comp, NULL, &walk_ctx, PPL_DIR_DOWNSTREAM); ret = pipeline_trigger_list(p, host, cmd); if (ret < 0) return ret; @@ -325,16 +430,17 @@ static int pipeline_comp_trigger(struct comp_dev *current, * Initialization delay is only used with SSP, where we * don't use more than one DAI per copier */ - struct comp_dev *dai = comp_get_dai(current, 0); - - if (dai) { - struct dai_data *dd = comp_get_drvdata(dai); - - ppl_data->delay_ms = dai_get_init_delay_ms(dd->dai); - } else { - /* Chain DMA case */ - ppl_data->delay_ms = 0; - } + struct dai_data *dd; +#if CONFIG_IPC_MAJOR_3 + dd = comp_get_drvdata(current); +#elif CONFIG_IPC_MAJOR_4 + struct copier_data *cd = comp_get_drvdata(current); + + dd = cd->dd[0]; +#else +#error Unknown IPC major version +#endif + ppl_data->delay_ms = dai_get_init_delay_ms(dd->dai); } break; default: diff --git a/src/idc/idc.c b/src/idc/idc.c index 3f5ef1f57f02..9796fce1922b 100644 --- a/src/idc/idc.c +++ b/src/idc/idc.c @@ -265,6 +265,33 @@ static int idc_reset(uint32_t comp_id) return ret; } +/** + * \brief Executes IDC pipeline set state message. + * \param[in] ppl_id Pipeline id to be triggered. + * \return Error code. + */ +static int idc_ppl_state(uint32_t ppl_id) +{ + struct ipc *ipc = ipc_get(); + struct ipc_comp_dev *ipc_dev; + struct idc *idc = *idc_get(); + struct idc_payload *payload = idc_payload_get(idc, cpu_get_id()); + struct ipc_comp_dev *ppl_icd; + uint32_t cmd = *(uint32_t *)payload; + bool delayed = false; + int ret; + + ppl_icd = ipc_get_comp_by_ppl_id(ipc, COMP_TYPE_PIPELINE, ppl_id); + if (!ppl_icd) { + tr_err(&idc_tr, "idc: comp %d not found", ppl_id); + return IPC4_INVALID_RESOURCE_ID; + } + + ret = set_pipeline_state(ppl_icd, cmd, &delayed); + + return ret; +} + static void idc_prepare_d0ix(void) { /* set prepare_d0ix flag, which indicates that in the next @@ -339,6 +366,9 @@ void idc_cmd(struct idc_msg *msg) case iTS(IDC_MSG_RESET): ret = idc_reset(msg->extension); break; + case iTS(IDC_MSG_PPL_STATE): + ret = idc_ppl_state(msg->extension); + break; case iTS(IDC_MSG_PREPARE_D0ix): idc_prepare_d0ix(); break; diff --git a/src/idc/zephyr_idc.c b/src/idc/zephyr_idc.c index 769e1a32d4ca..72fd455c4041 100644 --- a/src/idc/zephyr_idc.c +++ b/src/idc/zephyr_idc.c @@ -48,6 +48,9 @@ void idc_init_thread(void) K_P4WQ_ARRAY_DEFINE(q_zephyr_idc, CONFIG_CORE_COUNT, SOF_STACK_SIZE, K_P4WQ_USER_CPU_MASK); +#define WQ_PER_C 30 +static int w_idx = 0; + struct zephyr_idc_msg { struct k_p4wq_work work; struct idc_msg msg; @@ -96,14 +99,14 @@ 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 */ -static struct zephyr_idc_msg idc_work[CONFIG_CORE_COUNT]; +static struct zephyr_idc_msg idc_work[CONFIG_CORE_COUNT * WQ_PER_C]; 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 * WQ_PER_C + w_idx; struct idc_msg *msg_cp = &zmsg->msg; struct k_p4wq_work *work = &zmsg->work; int ret; @@ -141,6 +144,9 @@ int idc_send_msg(struct idc_msg *msg, uint32_t mode) ret = 0; } + if (++w_idx >= WQ_PER_C) + w_idx = 0; + return ret; } diff --git a/src/include/ipc4/alh.h b/src/include/ipc4/alh.h index a5a15b19be48..8a296973cf32 100644 --- a/src/include/ipc4/alh.h +++ b/src/include/ipc4/alh.h @@ -25,6 +25,7 @@ #define __SOF_IPC4_ALH_H__ #include +#include #include #define IPC4_ALH_MAX_NUMBER_OF_GTW 16 diff --git a/src/include/ipc4/copier.h b/src/include/ipc4/copier.h index 02c467cd1cfd..10eeccc656d3 100644 --- a/src/include/ipc4/copier.h +++ b/src/include/ipc4/copier.h @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -263,6 +264,7 @@ struct copier_data { uint64_t output_total_data_processed; struct host_data *hd; bool ipc_gtw; + struct dai_data *dd[IPC4_ALH_MAX_NUMBER_OF_GTW]; }; int apply_attenuation(struct comp_dev *dev, struct copier_data *cd, diff --git a/src/include/sof/audio/audio_stream.h b/src/include/sof/audio/audio_stream.h index 61f17fe5f113..37fe900b3563 100644 --- a/src/include/sof/audio/audio_stream.h +++ b/src/include/sof/audio/audio_stream.h @@ -176,6 +176,9 @@ struct audio_stream { #define audio_stream_get_frag(buffer, ptr, idx, sample_size) \ audio_stream_wrap(buffer, (char *)(ptr) + ((idx) * (sample_size))) +static inline void audio_stream_init_alignment_constants(const uint32_t byte_align, + const uint32_t frame_align_req, + struct audio_stream __sparse_cache *stream); /** * Applies parameters to the buffer. * @param buffer Buffer. @@ -192,6 +195,10 @@ static inline int audio_stream_set_params(struct audio_stream __sparse_cache *bu buffer->rate = params->rate; buffer->channels = params->channels; + /* FIXME: WORKAROUND!!! */ + if (buffer->frame_align_shift == 0) + audio_stream_init_alignment_constants(1, 1, buffer); + return 0; } diff --git a/src/include/sof/audio/component.h b/src/include/sof/audio/component.h index 5a06857b188e..96ec168f28e9 100644 --- a/src/include/sof/audio/component.h +++ b/src/include/sof/audio/component.h @@ -347,8 +347,8 @@ struct comp_ops { * * Mandatory for components that allocate DAI. */ - int (*dai_config)(struct comp_dev *dev, struct ipc_config_dai *dai_config, - const void *dai_spec_config); + int (*dai_config)(struct dai_data *dd, struct comp_dev *dev, + struct ipc_config_dai *dai_config, const void *dai_spec_config); /** * Used to pass standard and bespoke commands (with optional data). diff --git a/src/include/sof/audio/component_ext.h b/src/include/sof/audio/component_ext.h index 339b479b3ff2..efa8e1b7767b 100644 --- a/src/include/sof/audio/component_ext.h +++ b/src/include/sof/audio/component_ext.h @@ -252,16 +252,27 @@ static inline int comp_reset(struct comp_dev *dev) return 0; } +#if CONFIG_IPC_MAJOR_3 /** See comp_ops::dai_config */ static inline int comp_dai_config(struct comp_dev *dev, struct ipc_config_dai *config, const void *spec_config) { + struct dai_data *dd = comp_get_drvdata(dev); + if (dev->drv->ops.dai_config) - return dev->drv->ops.dai_config(dev, config, spec_config); + return dev->drv->ops.dai_config(dd, dev, config, spec_config); return 0; } - +#elif CONFIG_IPC_MAJOR_4 +static inline int comp_dai_config(struct dai_data *dd, struct comp_dev *dev, + struct ipc_config_dai *config, const void *spec_config) +{ + return dai_config(dd, dev, config, spec_config); +} +#else +#error Unknown IPC major version +#endif /** See comp_ops::position */ static inline int comp_position(struct comp_dev *dev, struct sof_ipc_stream_posn *posn) @@ -351,26 +362,6 @@ static inline int comp_get_endpoint_type(struct comp_dev *dev) } } -#if CONFIG_IPC_MAJOR_4 -#include -static inline struct comp_dev *comp_get_dai(struct comp_dev *parent, int index) -{ - struct copier_data *cd = comp_get_drvdata(parent); - - if (index >= ARRAY_SIZE(cd->endpoint)) - return NULL; - - return cd->endpoint[index]; -} -#elif CONFIG_IPC_MAJOR_3 -static inline struct comp_dev *comp_get_dai(struct comp_dev *parent, int index) -{ - return parent; -} -#else -#error Unknown IPC major version -#endif - /** * Called to check whether component schedules its pipeline. * @param dev Component device. diff --git a/src/include/sof/audio/dai_copier.h b/src/include/sof/audio/dai_copier.h new file mode 100644 index 000000000000..56dde5bda85e --- /dev/null +++ b/src/include/sof/audio/dai_copier.h @@ -0,0 +1,51 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * + * Copyright(c) 2023 Intel Corporation. All rights reserved. + * + * Author: Baofeng Tian + */ + +/** + * \file audio/dai_copier.h + * \brief dai copier shared header file + * \authors Baofeng Tian + */ + +#ifndef __SOF_LIB_DAI_COPIER_H__ +#define __SOF_LIB_DAI_COPIER_H__ + +struct ipc_config_dai; +struct comp_dev; +struct dai_data; +int dai_zephyr_new(struct dai_data *dd, struct comp_dev *dev, + const struct ipc_config_dai *dai_cfg); + +void dai_zephyr_free(struct dai_data *dd); + +int dai_zephyr_config_prepare(struct dai_data *dd, struct comp_dev *dev); + +int dai_zephyr_prepare(struct dai_data *dd, struct comp_dev *dev); + +void dai_zephyr_reset(struct dai_data *dd, struct comp_dev *dev); + +int dai_zephyr_trigger(struct dai_data *dd, struct comp_dev *dev, int cmd); + +int dai_zephyr_position(struct dai_data *dd, struct comp_dev *dev, + struct sof_ipc_stream_posn *posn); + +int dai_zephyr_params(struct dai_data *dd, struct comp_dev *dev, + struct sof_ipc_stream_params *params); + +int dai_zephyr_copy(struct dai_data *dd, struct comp_dev *dev, pcm_converter_func *converter); + +int dai_zephyr_ts_config_op(struct dai_data *dd, struct comp_dev *dev); + +int dai_zephyr_ts_start(struct dai_data *dd, struct comp_dev *dev); + +int dai_zephyr_ts_stop(struct dai_data *dd, struct comp_dev *dev); + +int dai_zephyr_ts_get(struct dai_data *dd, struct comp_dev *dev, struct timestamp_data *tsd); + +int dai_zephyr_get_hw_params(struct dai_data *dd, struct comp_dev *dev, + struct sof_ipc_stream_params *params, int dir); +#endif /* __SOF_LIB_DAI_COPIER_H__ */ diff --git a/src/include/sof/audio/host_copier.h b/src/include/sof/audio/host_copier.h index 9e886b89a9fc..b4857af80595 100644 --- a/src/include/sof/audio/host_copier.h +++ b/src/include/sof/audio/host_copier.h @@ -21,9 +21,11 @@ #include #include +typedef void (*copy_callback_t)(struct comp_dev *dev, size_t bytes); + struct host_data; /** \brief Host copy function interface. */ -typedef int (*host_copy_func)(struct host_data *hd, struct comp_dev *dev); +typedef int (*host_copy_func)(struct host_data *hd, struct comp_dev *dev, copy_callback_t cb); /** * \brief Host buffer info. @@ -51,6 +53,8 @@ struct host_data { #ifdef __ZEPHYR__ struct dma_config z_config; #endif + struct comp_dev *cb_dev; + struct comp_buffer *dma_buffer; struct comp_buffer *local_buffer; @@ -100,8 +104,8 @@ int host_zephyr_prepare(struct host_data *hd); void host_zephyr_reset(struct host_data *hd, uint16_t state); int host_zephyr_trigger(struct host_data *hd, struct comp_dev *dev, int cmd); int host_zephyr_params(struct host_data *hd, struct comp_dev *dev, - struct sof_ipc_stream_params *params); -int host_zephyr_copy(struct host_data *hd, struct comp_dev *dev); + struct sof_ipc_stream_params *params, notifier_callback_t cb); +int host_zephyr_copy(struct host_data *hd, struct comp_dev *dev, copy_callback_t cb); void host_update_position(struct host_data *hd, struct comp_dev *dev, uint32_t bytes); void host_one_shot_cb(struct host_data *hd, uint32_t bytes); diff --git a/src/include/sof/audio/module_adapter/iadk/logger.h b/src/include/sof/audio/module_adapter/iadk/logger.h index dc97f321ae06..89f8666595ec 100644 --- a/src/include/sof/audio/module_adapter/iadk/logger.h +++ b/src/include/sof/audio/module_adapter/iadk/logger.h @@ -21,7 +21,7 @@ class Logger { public: /*! \cond INTERNAL */ - Logger(AdspSystemService const &system_service, AdspLogHandle const &log_handle) + Logger(adsp_system_service const &system_service, AdspLogHandle const &log_handle) : system_service_(system_service), log_handle_(log_handle) @@ -38,7 +38,7 @@ class Logger /*! \endcond INTERNAL */ private: - AdspSystemService const &system_service_; + adsp_system_service const &system_service_; AdspLogHandle const &log_handle_; }; 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 7c44845f044e..d5f32730b1b8 100644 --- a/src/include/sof/audio/module_adapter/iadk/system_agent.h +++ b/src/include/sof/audio/module_adapter/iadk/system_agent.h @@ -55,7 +55,7 @@ namespace system } private: - static AdspSystemService system_service_; + static adsp_system_service system_service_; uint32_t log_handle_; uint32_t const core_id_; uint32_t module_id_; diff --git a/src/include/sof/audio/module_adapter/iadk/system_service.h b/src/include/sof/audio/module_adapter/iadk/system_service.h index 63cf84913612..1b6d15bbdefe 100644 --- a/src/include/sof/audio/module_adapter/iadk/system_service.h +++ b/src/include/sof/audio/module_adapter/iadk/system_service.h @@ -167,14 +167,14 @@ typedef struct _SystemServiceIface {} SystemServiceIface; */ typedef AdspErrorCode (*SystemServiceGetInterfaceFct) (AdspIfaceId id, SystemServiceIface **iface); -/*! \brief The AdspSystemService is actually a set of C-function pointers gathered in a C-struct +/*! \brief The adsp_system_service is actually a set of C-function pointers gathered in a C-struct * which brings some basic functionalities to module in runtime. * * The system service can be retrieved with help of either the * intel_adsp::ProcessingModuleFactory::GetSystemService() method * or the intel_adsp::ProcessingModule::GetSysstemService() method. */ -typedef struct AdspSystemService { +typedef struct adsp_system_service { /*! The SystemService::LogMessage function provides capability to send some log message to * the host for debugging purposes. This log can be caught by the FDK Tools and displayed * in the Debug window. The prototype of this function is given by the @@ -223,14 +223,14 @@ typedef struct AdspSystemService { */ const SystemServiceGetInterfaceFct GetInterface; -} AdspSystemService; +} adsp_system_service; #ifdef __cplusplus namespace intel_adsp { -/*! \brief Alias type of AdspSystemService which can be used in C++. +/*! \brief Alias type of adsp_system_service which can be used in C++. */ -struct SystemService : public AdspSystemService {}; +struct SystemService : public adsp_system_service {}; } #endif diff --git a/src/include/sof/audio/module_adapter/library/module_api_ver.h b/src/include/sof/audio/module_adapter/library/module_api_ver.h new file mode 100644 index 000000000000..842f3a42e3f1 --- /dev/null +++ b/src/include/sof/audio/module_adapter/library/module_api_ver.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * Copyright(c) 2023 Intel Corporation. All rights reserved. + * + * Author: Pawel Dobrowolski + */ + +#ifndef __MODULE_API_VER_H__ +#define __MODULE_API_VER_H__ + +/* + * Api version 5.0.0 for sof loadable modules + */ + +#define _MAJOR_API_MODULE_VERSION 5 +#define _MIDDLE_API_MODULE_VERSION 0 +#define _MINOR_API_MODULE_VERSION 0 + +typedef union sof_module_api_version { + uint32_t full; + struct { + uint32_t minor : 10; + uint32_t middle : 10; + uint32_t major : 10; + uint32_t reserved : 2; + } fields; +} sof_module_api_version; + +typedef const struct { + uint32_t format; + sof_module_api_version api_version_number; +} sof_module_build_info; + +#endif /* __MODULE_API_VER_H__ */ 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 new file mode 100644 index 000000000000..edaa8ad0d040 --- /dev/null +++ b/src/include/sof/audio/module_adapter/library/native_system_agent.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * Copyright(c) 2023 Intel Corporation. All rights reserved. + * + * Author: Pawel Dobrowolski + */ + +#ifndef __NATIVE_SYSTEM_AGENT_H__ +#define __NATIVE_SYSTEM_AGENT_H__ + +#include +#include + +struct native_system_agent { + adsp_system_service system_service; + uint32_t log_handle; + uint32_t core_id; + uint32_t module_id; + uint32_t instance_id; + uint32_t module_size; +}; + +void *native_system_agent_start(struct adsp_system_service **sys_service, uint32_t entry_point, + uint32_t module_id, uint32_t instance_id, uint32_t core_id, + uint32_t log_handle, void *mod_cfg); + +#endif /* __NATIVE_SYSTEM_AGENT_H__ */ diff --git a/src/include/sof/audio/module_adapter/module/generic.h b/src/include/sof/audio/module_adapter/module/generic.h index a12e7f857f4e..b8e885e41576 100644 --- a/src/include/sof/audio/module_adapter/module/generic.h +++ b/src/include/sof/audio/module_adapter/module/generic.h @@ -207,6 +207,12 @@ struct processing_module { */ bool skip_src_buffer_invalidate; + /* flag to insure that module is loadable */ + bool is_native_sof; + + /* pointer to system services for loadable modules */ + struct adsp_system_service *sys_service; + /* table containing the list of connected sources */ struct module_source_info *source_info; }; diff --git a/src/include/sof/audio/module_adapter/module/iadk_modules.h b/src/include/sof/audio/module_adapter/module/iadk_modules.h index 712b85d55d44..3acdb40581b8 100644 --- a/src/include/sof/audio/module_adapter/module/iadk_modules.h +++ b/src/include/sof/audio/module_adapter/module/iadk_modules.h @@ -11,13 +11,15 @@ #include /* Intel module adapter is an extension to SOF module adapter component that allows to integrate - * modules developed under IADK (Intel Audio Development Kit) Framework. IADK modules uses uniform + * modules developed under IADK (Intel Audio Development Kit) + * and LMDK (Loadable Modules Dev Kit) Framework. Modules uses uniform * set of interfaces and are linked into separate library. These modules are loaded in runtime * through library_manager and then after registration into SOF component infrastructure are * interfaced through module adapter API. - * Since IADK modules uses ProcessingModuleInterface to control/data transfer and AdspSystemService - * to use base FW services from internal module code, there is a communication shim layer defined - * in intel directory. + * + * Since IADK modules uses ProcessingModuleInterface to control/data transfer + * and adsp_system_service to use base FW services from internal module code, + * there is a communication shim layer defined in intel directory. * * Since ProcessingModuleInterface consists of virtual functions, there are C++ -> C wrappers * defined to access the interface calls from SOF code. @@ -32,6 +34,10 @@ * connect both sides of ProcessingModuleInterface and System Service. * - System Service - exposes of SOF base FW services to the module. * - Processing Module Adapter - SOF base FW side of ProcessingModuleInterface API + * + * Using the same philosofy loadable modules are using module adapter to interact with SOF FW. + * Module recognision is done by checking module API version defined in module_api_ver.h + * with version read from elf file. */ diff --git a/src/include/sof/ipc/common.h b/src/include/sof/ipc/common.h index 360e8417f4a6..8e659c5b4ae6 100644 --- a/src/include/sof/ipc/common.h +++ b/src/include/sof/ipc/common.h @@ -141,11 +141,12 @@ int ipc_dma_trace_send_position(void); */ void ipc_send_buffer_status_notify(void); +struct dai_data; /** * \brief Configure DAI. * @return 0 on success. */ -int ipc_dai_data_config(struct comp_dev *dev); +int ipc_dai_data_config(struct dai_data *dd, struct comp_dev *dev); /** * \brief create a IPC boot complete message. diff --git a/src/include/sof/lib/dai-legacy.h b/src/include/sof/lib/dai-legacy.h index dcce8238719d..b72e176d10ac 100644 --- a/src/include/sof/lib/dai-legacy.h +++ b/src/include/sof/lib/dai-legacy.h @@ -543,23 +543,23 @@ static inline const struct dai_info *dai_info_get(void) /** * \brief Configure DMA channel for DAI */ -int dai_config_dma_channel(struct comp_dev *dev, const void *config); +int dai_config_dma_channel(struct dai_data *dd, struct comp_dev *dev, const void *config); /** * \brief Reset DAI DMA config */ -void dai_dma_release(struct comp_dev *dev); +void dai_dma_release(struct dai_data *dd, struct comp_dev *dev); /** * \brief Configure DAI physical interface. */ -int dai_config(struct comp_dev *dev, struct ipc_config_dai *common_config, +int dai_config(struct dai_data *dd, struct comp_dev *dev, struct ipc_config_dai *common_config, const void *spec_config); /** * \brief Assign DAI to a group for simultaneous triggering. */ -int dai_assign_group(struct comp_dev *dev, uint32_t group_id); +int dai_assign_group(struct dai_data *dd, struct comp_dev *dev, uint32_t group_id); /** * \brief dai position for host driver. @@ -569,12 +569,12 @@ int dai_position(struct comp_dev *dev, struct sof_ipc_stream_posn *posn); /** * \brief update dai dma position for host driver. */ -void dai_dma_position_update(struct comp_dev *dev); +void dai_dma_position_update(struct dai_data *dd, struct comp_dev *dev); /** * \brief release llp slot */ -void dai_release_llp_slot(struct comp_dev *dev); +void dai_release_llp_slot(struct dai_data *dd); /** @}*/ #endif /* __SOF_LIB_DAI_LEGACY_H__ */ diff --git a/src/include/sof/lib/dai-zephyr.h b/src/include/sof/lib/dai-zephyr.h index e383db19e782..639fdce5849f 100644 --- a/src/include/sof/lib/dai-zephyr.h +++ b/src/include/sof/lib/dai-zephyr.h @@ -253,22 +253,23 @@ int dai_get_stream_id(struct dai *dai, int direction); /** * \brief Configure DMA channel for DAI */ -int dai_config_dma_channel(struct comp_dev *dev, const void *config); +int dai_config_dma_channel(struct dai_data *dd, struct comp_dev *dev, const void *config); /** * \brief Reset DAI DMA config */ -void dai_dma_release(struct comp_dev *dev); +void dai_dma_release(struct dai_data *dd, struct comp_dev *dev); /** * \brief Configure DAI physical interface. */ -int dai_config(struct comp_dev *dev, struct ipc_config_dai *common_cfg, const void *spec_cfg); +int dai_config(struct dai_data *dd, struct comp_dev *dev, + struct ipc_config_dai *common_cfg, const void *spec_cfg); /** * \brief Assign DAI to a group for simultaneous triggering. */ -int dai_assign_group(struct comp_dev *dev, uint32_t group_id); +int dai_assign_group(struct dai_data *dd, struct comp_dev *dev, uint32_t group_id); /** * \brief dai position for host driver. @@ -278,12 +279,12 @@ int dai_position(struct comp_dev *dev, struct sof_ipc_stream_posn *posn); /** * \brief update dai dma position for host driver. */ -void dai_dma_position_update(struct comp_dev *dev); +void dai_dma_position_update(struct dai_data *dd, struct comp_dev *dev); /** * \brief release llp slot */ -void dai_release_llp_slot(struct comp_dev *dev); +void dai_release_llp_slot(struct dai_data *dd); /** @}*/ #endif /* __SOF_LIB_DAI_ZEPHYR_H__ */ diff --git a/src/include/sof/lib/notifier.h b/src/include/sof/lib/notifier.h index 99d11648f056..abf893281f70 100644 --- a/src/include/sof/lib/notifier.h +++ b/src/include/sof/lib/notifier.h @@ -58,6 +58,9 @@ struct notify_data { struct notify **arch_notify_get(void); +typedef void (*notifier_callback_t)(void *receiver_data, enum notify_id event_type, + void *caller_data); + /** Register a callback to be run when event 'type' happens. * * The identifier for un-registration is the tuple (receiver_data, @@ -74,9 +77,7 @@ struct notify **arch_notify_get(void); * @param flags see NOTIFIER_FLAG_* above */ int notifier_register(void *receiver_data, void *caller_id_filter, enum notify_id event_type, - void (*callback)(void *receiver_data, enum notify_id event_type, - void *caller_data), - uint32_t flags); + notifier_callback_t callback, uint32_t flags); /** Unregister all callbacks matching that arguments tuple. NULL acts * as a wildcard. diff --git a/src/include/sof/samples/audio/smart_amp_test.h b/src/include/sof/samples/audio/smart_amp_test.h index 56b0c01f275a..7f24b5c37de8 100644 --- a/src/include/sof/samples/audio/smart_amp_test.h +++ b/src/include/sof/samples/audio/smart_amp_test.h @@ -31,11 +31,6 @@ struct smart_amp_model_data { uint32_t data_pos; }; -typedef int(*smart_amp_proc)(struct comp_dev *dev, - const struct audio_stream __sparse_cache *source, - const struct audio_stream __sparse_cache *sink, uint32_t frames, - int8_t *chan_map); - /* Each channel map specifies which channel from input (buffer between host * and smart amp - source_chan_map[] or feedback buffer between smart amp and * demux - feedback_chan_map[]) will be copied to specific smart amp output @@ -99,7 +94,6 @@ struct sof_smart_amp_config { #define SMART_AMP_NUM_OUT_PINS 1 struct sof_smart_amp_ipc4_config { - struct ipc4_base_module_cfg base; struct ipc4_input_pin_format input_pins[SMART_AMP_NUM_IN_PINS]; struct ipc4_output_pin_format output_pin; }; diff --git a/src/ipc/ipc-zephyr.c b/src/ipc/ipc-zephyr.c index 975fa503ee4c..06f1fa3988bd 100644 --- a/src/ipc/ipc-zephyr.c +++ b/src/ipc/ipc-zephyr.c @@ -17,11 +17,15 @@ #include #include #include -#if defined(CONFIG_PM_POLICY_CUSTOM) +#if defined(CONFIG_PM) #include +#include +#include +#include +#include #else #include -#endif +#endif /* CONFIG_PM */ #include #include #include @@ -79,6 +83,51 @@ static bool message_handler(const struct device *dev, void *arg, uint32_t data, return false; } +#ifdef CONFIG_PM_DEVICE +/** + * @brief IPC device suspend handler callback function. + * Checks whether device power state should be actually changed. + * + * @param dev IPC device. + * @param arg IPC struct pointer. + */ +static int ipc_device_suspend_handler(const struct device *dev, void *arg) +{ + struct ipc *ipc = (struct ipc *)arg; + + /* we are not entering D3 - return error code bad message */ + if (!(ipc->task_mask & IPC_TASK_POWERDOWN)) + return -EBADMSG; + + return 0; +} + +/** + * @brief IPC device resume handler callback function. + * Resets IPC control after context restore. + * + * @param dev IPC device. + * @param arg IPC struct pointer. + */ +static int ipc_device_resume_handler(const struct device *dev, void *arg) +{ + struct ipc *ipc = (struct ipc *)arg; + + ipc_set_drvdata(ipc, NULL); + 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 */ + schedule_task_init_edf(&ipc->ipc_task, SOF_UUID(ipc_task_uuid), + &ipc_task_ops, ipc, 0, 0); + + return 0; +} +#endif /* CONFIG_PM_DEVICE */ + #if CONFIG_DEBUG_IPC_COUNTERS static inline void increment_ipc_received_counter(void) @@ -130,7 +179,7 @@ enum task_state ipc_platform_do_cmd(struct ipc *ipc) if (ipc->task_mask & IPC_TASK_POWERDOWN || ipc_get()->pm_prepare_D3) { -#if defined(CONFIG_PM_POLICY_CUSTOM) +#if defined(CONFIG_PM) /** * @note For primary core this function * will only force set lower power state @@ -145,7 +194,7 @@ enum task_state ipc_platform_do_cmd(struct ipc *ipc) * powered off and IPC sent. */ platform_pm_runtime_power_off(); -#endif /* CONFIG_PM_POLICY_CUSTOM */ +#endif /* CONFIG_PM */ } return SOF_TASK_STATE_COMPLETED; @@ -188,6 +237,12 @@ int platform_ipc_init(struct ipc *ipc) /* attach handlers */ intel_adsp_ipc_set_message_handler(INTEL_ADSP_IPC_HOST_DEV, message_handler, ipc); +#ifdef CONFIG_PM + 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, + ipc_device_resume_handler, ipc); +#endif return 0; } diff --git a/src/ipc/ipc3/dai.c b/src/ipc/ipc3/dai.c index bffe3192eb05..a33e9ad680f0 100644 --- a/src/ipc/ipc3/dai.c +++ b/src/ipc/ipc3/dai.c @@ -29,9 +29,8 @@ LOG_MODULE_DECLARE(ipc, CONFIG_SOF_LOG_LEVEL); -int dai_config_dma_channel(struct comp_dev *dev, const void *spec_config) +int dai_config_dma_channel(struct dai_data *dd, struct comp_dev *dev, const void *spec_config) { - struct dai_data *dd = comp_get_drvdata(dev); const struct sof_ipc_dai_config *config = spec_config; struct ipc_config_dai *dai = &dd->ipc_config; int channel; @@ -95,9 +94,8 @@ int dai_config_dma_channel(struct comp_dev *dev, const void *spec_config) return channel; } -int ipc_dai_data_config(struct comp_dev *dev) +int ipc_dai_data_config(struct dai_data *dd, struct comp_dev *dev) { - struct dai_data *dd = comp_get_drvdata(dev); struct ipc_config_dai *dai = &dd->ipc_config; struct sof_ipc_dai_config *config = ipc_from_dai_config(dd->dai_spec_config); struct comp_buffer __sparse_cache *buffer_c; @@ -263,10 +261,8 @@ int ipc_comp_dai_config(struct ipc *ipc, struct ipc_config_dai *common_config, return ret; } -void dai_dma_release(struct comp_dev *dev) +void dai_dma_release(struct dai_data *dd, struct comp_dev *dev) { - struct dai_data *dd = comp_get_drvdata(dev); - /* cannot configure DAI while active */ if (dev->state == COMP_STATE_ACTIVE) { comp_info(dev, "dai_config(): Component is in active state. Ignore resetting"); @@ -287,11 +283,10 @@ void dai_dma_release(struct comp_dev *dev) } } -int dai_config(struct comp_dev *dev, struct ipc_config_dai *common_config, +int dai_config(struct dai_data *dd, struct comp_dev *dev, struct ipc_config_dai *common_config, const void *spec_config) { const struct sof_ipc_dai_config *config = spec_config; - struct dai_data *dd = comp_get_drvdata(dev); int ret; /* ignore if message not for this DAI id/type */ @@ -336,7 +331,7 @@ int dai_config(struct comp_dev *dev, struct ipc_config_dai *common_config, if (ret < 0) return ret; - dai_dma_release(dev); + dai_dma_release(dd, dev); } return 0; @@ -353,14 +348,14 @@ int dai_config(struct comp_dev *dev, struct ipc_config_dai *common_config, } #if CONFIG_COMP_DAI_GROUP if (config->group_id) { - ret = dai_assign_group(dev, config->group_id); + ret = dai_assign_group(dd, dev, config->group_id); if (ret) return ret; } #endif /* do nothing for asking for channel free, for compatibility. */ - if (dai_config_dma_channel(dev, spec_config) == DMA_CHAN_INVALID) + if (dai_config_dma_channel(dd, dev, spec_config) == DMA_CHAN_INVALID) return 0; /* allocated dai_config if not yet */ @@ -396,6 +391,6 @@ int dai_position(struct comp_dev *dev, struct sof_ipc_stream_posn *posn) return 0; } -void dai_dma_position_update(struct comp_dev *dev) { } +void dai_dma_position_update(struct dai_data *dd, struct comp_dev *dev) { } -void dai_release_llp_slot(struct comp_dev *dev) { } +void dai_release_llp_slot(struct dai_data *dd) { } diff --git a/src/ipc/ipc4/dai.c b/src/ipc/ipc4/dai.c index be2f10eb08b5..63cce5774833 100644 --- a/src/ipc/ipc4/dai.c +++ b/src/ipc/ipc4/dai.c @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -30,10 +31,9 @@ LOG_MODULE_DECLARE(ipc, CONFIG_SOF_LOG_LEVEL); -int dai_config_dma_channel(struct comp_dev *dev, const void *spec_config) +int dai_config_dma_channel(struct dai_data *dd, struct comp_dev *dev, const void *spec_config) { const struct ipc4_copier_module_cfg *copier_cfg = spec_config; - struct dai_data *dd = comp_get_drvdata(dev); struct ipc_config_dai *dai = &dd->ipc_config; int channel; @@ -62,9 +62,8 @@ int dai_config_dma_channel(struct comp_dev *dev, const void *spec_config) return channel; } -int ipc_dai_data_config(struct comp_dev *dev) +int ipc_dai_data_config(struct dai_data *dd, struct comp_dev *dev) { - struct dai_data *dd = comp_get_drvdata(dev); struct ipc_config_dai *dai = &dd->ipc_config; struct ipc4_copier_module_cfg *copier_cfg = dd->dai_spec_config; struct dai *dai_p = dd->dai; @@ -138,10 +137,8 @@ int ipc_comp_dai_config(struct ipc *ipc, struct ipc_config_dai *common_config, return 0; } -void dai_dma_release(struct comp_dev *dev) +void dai_dma_release(struct dai_data *dd, struct comp_dev *dev) { - struct dai_data *dd = comp_get_drvdata(dev); - /* cannot configure DAI while active */ if (dev->state == COMP_STATE_ACTIVE) { comp_info(dev, "dai_config(): Component is in active state. Ignore resetting"); @@ -174,14 +171,9 @@ void dai_dma_release(struct comp_dev *dev) if (dev->state != COMP_STATE_PAUSED) dma_stop(dd->chan->dma->z_dev, dd->chan->index); - /* remove callback */ - notifier_unregister(dev, dd->chan, NOTIFIER_ID_DMA_COPY); dma_release_channel(dd->chan->dma->z_dev, dd->chan->index); #else dma_stop_legacy(dd->chan); - - /* remove callback */ - notifier_unregister(dev, dd->chan, NOTIFIER_ID_DMA_COPY); dma_channel_put_legacy(dd->chan); #endif dd->chan->dev_data = NULL; @@ -189,9 +181,8 @@ void dai_dma_release(struct comp_dev *dev) } } -void dai_release_llp_slot(struct comp_dev *dev) +void dai_release_llp_slot(struct dai_data *dd) { - struct dai_data *dd = comp_get_drvdata(dev); struct ipc4_llp_reading_slot slot; k_spinlock_key_t key; @@ -253,9 +244,8 @@ static int dai_get_unused_llp_slot(struct comp_dev *dev, return offset; } -static int dai_init_llp_info(struct comp_dev *dev) +static int dai_init_llp_info(struct dai_data *dd, struct comp_dev *dev) { - struct dai_data *dd = comp_get_drvdata(dev); struct ipc4_copier_module_cfg *copier_cfg; union ipc4_connector_node_id node; int ret; @@ -283,11 +273,10 @@ static int dai_init_llp_info(struct comp_dev *dev) return 0; } -int dai_config(struct comp_dev *dev, struct ipc_config_dai *common_config, +int dai_config(struct dai_data *dd, struct comp_dev *dev, struct ipc_config_dai *common_config, const void *spec_config) { const struct ipc4_copier_module_cfg *copier_cfg = spec_config; - struct dai_data *dd = comp_get_drvdata(dev); int size; int ret; @@ -313,14 +302,14 @@ int dai_config(struct comp_dev *dev, struct ipc_config_dai *common_config, #if CONFIG_COMP_DAI_GROUP if (common_config->group_id) { - ret = dai_assign_group(dev, common_config->group_id); + ret = dai_assign_group(dd, dev, common_config->group_id); if (ret) return ret; } #endif /* do nothing for asking for channel free, for compatibility. */ - if (dai_config_dma_channel(dev, spec_config) == DMA_CHAN_INVALID) + if (dai_config_dma_channel(dd, dev, spec_config) == DMA_CHAN_INVALID) return 0; dd->dai_dev = dev; @@ -342,7 +331,7 @@ int dai_config(struct comp_dev *dev, struct ipc_config_dai *common_config, } } - ret = dai_init_llp_info(dev); + ret = dai_init_llp_info(dd, dev); if (ret < 0) return ret; @@ -350,9 +339,9 @@ int dai_config(struct comp_dev *dev, struct ipc_config_dai *common_config, } #if CONFIG_ZEPHYR_NATIVE_DRIVERS -int dai_position(struct comp_dev *dev, struct sof_ipc_stream_posn *posn) +int dai_zephyr_position(struct dai_data *dd, struct comp_dev *dev, + struct sof_ipc_stream_posn *posn) { - struct dai_data *dd = comp_get_drvdata(dev); struct dma_status status; int ret; @@ -371,9 +360,15 @@ int dai_position(struct comp_dev *dev, struct sof_ipc_stream_posn *posn) return 0; } -void dai_dma_position_update(struct comp_dev *dev) +int dai_position(struct comp_dev *dev, struct sof_ipc_stream_posn *posn) { struct dai_data *dd = comp_get_drvdata(dev); + + return dai_zephyr_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_status status; int ret; @@ -396,9 +391,9 @@ void dai_dma_position_update(struct comp_dev *dev) mailbox_sw_regs_write(dd->slot_info.reg_offset, &slot, sizeof(slot)); } #else -int dai_position(struct comp_dev *dev, struct sof_ipc_stream_posn *posn) +int dai_zephyr_position(struct dai_data *dd, struct comp_dev *dev, + struct sof_ipc_stream_posn *posn) { - struct dai_data *dd = comp_get_drvdata(dev); struct dma_chan_status status; /* total processed bytes count */ @@ -413,9 +408,15 @@ int dai_position(struct comp_dev *dev, struct sof_ipc_stream_posn *posn) return 0; } -void dai_dma_position_update(struct comp_dev *dev) +int dai_position(struct comp_dev *dev, struct sof_ipc_stream_posn *posn) { struct dai_data *dd = comp_get_drvdata(dev); + + return dai_zephyr_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]; diff --git a/src/ipc/ipc4/handler.c b/src/ipc/ipc4/handler.c index bc03ee6ab37b..54b30f29b96e 100644 --- a/src/ipc/ipc4/handler.c +++ b/src/ipc/ipc4/handler.c @@ -206,7 +206,7 @@ static bool is_any_ppl_active(void) * ERROR Stop EOS |______\ SAVE * / */ -static int set_pipeline_state(struct ipc_comp_dev *ppl_icd, uint32_t cmd, +int set_pipeline_state(struct ipc_comp_dev *ppl_icd, uint32_t cmd, bool *delayed) { struct ipc_comp_dev *host = NULL; @@ -395,6 +395,8 @@ static int ipc4_set_pipeline_state(struct ipc4_message_request *ipc4) struct ipc *ipc = ipc_get(); uint32_t cmd, ppl_count, id; const uint32_t *ppl_id; + bool use_idc = false; + uint32_t idx; int ret = 0; int i; @@ -416,6 +418,21 @@ static int ipc4_set_pipeline_state(struct ipc4_message_request *ipc4) ppl_id = &id; } + for (i = 0; i < ppl_count; i++) { + ppl_icd = ipc_get_comp_by_ppl_id(ipc, COMP_TYPE_PIPELINE, ppl_id[i]); + if (!ppl_icd) { + tr_err(&ipc_tr, "ipc: comp %d not found", ppl_id[i]); + return IPC4_INVALID_RESOURCE_ID; + } + + if (i) { + if (ppl_icd->core != idx) + use_idc = true; + } else { + idx = ppl_icd->core; + } + } + for (i = 0; i < ppl_count; i++) { bool delayed = false; @@ -426,15 +443,23 @@ static int ipc4_set_pipeline_state(struct ipc4_message_request *ipc4) } /* Pass IPC to target core - * Note: current implementation supports only a case with - * all pipelines in cmd allocated on the same core + * or use idc if more than one core used */ - if (!cpu_is_me(ppl_icd->core)) - return ipc4_process_on_core(ppl_icd->core, false); - - ipc_compound_pre_start(state.primary.r.type); - ret = set_pipeline_state(ppl_icd, cmd, &delayed); - ipc_compound_post_start(state.primary.r.type, ret, delayed); + if (!cpu_is_me(ppl_icd->core)) { + if (use_idc) { + struct idc_msg msg = { IDC_MSG_PPL_STATE, + IDC_MSG_PPL_STATE_EXT(ppl_id[i]), ppl_icd->core, sizeof(cmd), + &cmd, }; + + ret = idc_send_msg(&msg, IDC_BLOCKING); + } else { + return ipc4_process_on_core(ppl_icd->core, false); + } + } else { + ipc_compound_pre_start(state.primary.r.type); + ret = set_pipeline_state(ppl_icd, cmd, &delayed); + ipc_compound_post_start(state.primary.r.type, ret, delayed); + } if (ret != 0) return ret; diff --git a/src/ipc/ipc4/helper.c b/src/ipc/ipc4/helper.c index fa89c7abc35e..c3fc7bf96ccd 100644 --- a/src/ipc/ipc4/helper.c +++ b/src/ipc/ipc4/helper.c @@ -601,6 +601,8 @@ int ipc4_pipeline_complete(struct ipc *ipc, uint32_t comp_id) int ret; ipc_pipe = ipc_get_comp_by_id(ipc, comp_id); + if(!ipc_pipe) + return -EINVAL; /* Pass IPC to target core */ if (!cpu_is_me(ipc_pipe->core)) diff --git a/src/lib/clk.c b/src/lib/clk.c index a3439b2a01ae..6a57966b9be5 100644 --- a/src/lib/clk.c +++ b/src/lib/clk.c @@ -28,8 +28,6 @@ DECLARE_TR_CTX(clock_tr, SOF_UUID(clock_uuid), LOG_LEVEL_INFO); SHARED_DATA struct k_spinlock clk_lock; -struct clock_notify_data clk_notify_data; - static inline uint32_t clock_get_nearest_freq_idx(const struct freq_table *tab, uint32_t size, uint32_t hz) { @@ -59,39 +57,21 @@ void clock_set_freq(int clock, uint32_t hz) uint32_t idx; k_spinlock_key_t key; - clk_notify_data.old_freq = - clk_info->freqs[clk_info->current_freq_idx].freq; - clk_notify_data.old_ticks_per_msec = - clk_info->freqs[clk_info->current_freq_idx].ticks_per_msec; - /* atomic context for changing clocks */ key = clock_lock(); /* get nearest frequency that is >= requested Hz */ idx = clock_get_nearest_freq_idx(clk_info->freqs, clk_info->freqs_num, hz); - clk_notify_data.freq = clk_info->freqs[idx].freq; tr_info(&clock_tr, "clock %d set freq %dHz freq_idx %d", clock, hz, idx); - /* tell anyone interested we are about to change freq */ - clk_notify_data.message = CLOCK_NOTIFY_PRE; - notifier_event(clk_info, clk_info->notification_id, - clk_info->notification_mask, &clk_notify_data, - sizeof(clk_notify_data)); - if (!clk_info->set_freq || clk_info->set_freq(clock, idx) == 0) /* update clock frequency */ clk_info->current_freq_idx = idx; - /* tell anyone interested we have now changed freq */ - clk_notify_data.message = CLOCK_NOTIFY_POST; - notifier_event(clk_info, clk_info->notification_id, - clk_info->notification_mask, &clk_notify_data, - sizeof(clk_notify_data)); - clock_unlock(key); } diff --git a/src/lib/dma.c b/src/lib/dma.c index 58aa64dc841e..6115cfba1e85 100644 --- a/src/lib/dma.c +++ b/src/lib/dma.c @@ -378,3 +378,24 @@ int dma_buffer_copy_to(struct comp_buffer __sparse_cache *source, return ret; } + +int dma_buffer_copy_from_no_consume(struct comp_buffer __sparse_cache *source, + struct comp_buffer __sparse_cache *sink, + dma_process_func process, uint32_t source_bytes) +{ + struct audio_stream __sparse_cache *istream = &source->stream; + uint32_t samples = source_bytes / + audio_stream_sample_bytes(istream); + uint32_t sink_bytes = audio_stream_sample_bytes(&sink->stream) * + samples; + int ret; + + /* process data */ + ret = process(istream, 0, &sink->stream, 0, samples); + + buffer_stream_writeback(sink, sink_bytes); + + comp_update_buffer_produce(sink, sink_bytes); + + return ret; +} diff --git a/src/platform/intel/ace/platform.c b/src/platform/intel/ace/platform.c index 86afbfa80fef..cc811a8503d0 100644 --- a/src/platform/intel/ace/platform.c +++ b/src/platform/intel/ace/platform.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -24,6 +25,15 @@ #include #include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include @@ -74,6 +84,104 @@ int platform_boot_complete(uint32_t boot_message) return ipc_platform_send_msg(&msg); } +/* address where zephyr PM will save memory during D3 transition */ +#ifdef CONFIG_ADSP_IMR_CONTEXT_SAVE +extern void *global_imr_ram_storage; +#endif + +/* Reports error message during power state transitions */ +static void power_state_failure_report(int ret, bool enter, enum pm_state state) +{ + const char *action_name = enter ? "enter" : "leave"; + + __ASSERT(!ret, "Failed to %s power state: %d. Error: %d", action_name, state, ret); +} + +/** + * @brief Notifier called before every power state transition. + * Works on Primary Core only. + * @param state Power state being entered. + */ +static void notify_pm_state_entry(enum pm_state state) +{ + if (!cpu_is_primary(arch_proc_id())) + return; + + if (state == PM_STATE_SOFT_OFF) { +#ifdef CONFIG_ADSP_IMR_CONTEXT_SAVE + size_t storage_buffer_size; + + /* allocate IMR global_imr_ram_storage */ + const struct device *tlb_dev = DEVICE_DT_GET(DT_NODELABEL(tlb)); + + __ASSERT_NO_MSG(tlb_dev); + const struct intel_adsp_tlb_api *tlb_api = + (struct intel_adsp_tlb_api *)tlb_dev->api; + + /* get HPSRAM storage buffer size */ + storage_buffer_size = tlb_api->get_storage_size(); + + /* add space for LPSRAM */ + storage_buffer_size += LP_SRAM_SIZE; + + /* allocate IMR buffer and store it in the global pointer */ + global_imr_ram_storage = rmalloc(SOF_MEM_ZONE_SYS, + 0, + SOF_MEM_CAPS_L3, + storage_buffer_size); + + /* change power state and check if IPC subsystem is prepared to enter D3 */ + int ret = pm_device_action_run(INTEL_ADSP_IPC_HOST_DEV, PM_DEVICE_ACTION_SUSPEND); + + if (ret) + power_state_failure_report(ret, true, PM_STATE_SOFT_OFF); + +#endif /* CONFIG_ADSP_IMR_CONTEXT_SAVE */ + } +} + +/** + * @brief Notifier called after every power state transition. + * Works on Primary Core only. + * @param state Power state being exited. + */ +static void notify_pm_state_exit(enum pm_state state) +{ + if (!cpu_is_primary(arch_proc_id())) { + cpu_notify_state_exit(state); + return; + } + + if (state == PM_STATE_SOFT_OFF) { +#ifdef CONFIG_ADSP_IMR_CONTEXT_SAVE + /* free global_imr_ram_storage */ + rfree(global_imr_ram_storage); + global_imr_ram_storage = NULL; + + int ret = pm_device_action_run(INTEL_ADSP_IPC_HOST_DEV, PM_DEVICE_ACTION_RESUME); + + if (ret) + power_state_failure_report(ret, false, PM_STATE_SOFT_OFF); + + enum pm_device_state ipc_state = PM_DEVICE_STATE_SUSPENDING; + + pm_device_state_get(INTEL_ADSP_IPC_HOST_DEV, &ipc_state); + __ASSERT_NO_MSG(ipc_state == PM_DEVICE_STATE_ACTIVE); + + /* sends fw-ready message signalling successful exit from D3 state */ + platform_boot_complete(0); +#endif + } +} + +static struct pm_notifier pm_state_notifier = { + .state_entry = notify_pm_state_entry, + .state_exit = notify_pm_state_exit, +}; + +/* Value to be determined experimentaly */ +#define BASE_CPS_USAGE 10000 + /* Runs on the primary core only */ int platform_init(struct sof *sof) { @@ -81,13 +189,8 @@ int platform_init(struct sof *sof) trace_point(TRACE_BOOT_PLATFORM_CLOCK); platform_clock_init(sof); - - /* Set DSP clock to MAX using KCPS API. Value should be lowered when KCPS API - * for modules is implemented - */ - ret = core_kcps_adjust(cpu_get_id(), CLK_MAX_CPU_HZ / 1000); - if (ret < 0) - return ret; + kcps_budget_init(); + core_kcps_adjust(0, BASE_CPS_USAGE); trace_point(TRACE_BOOT_PLATFORM_SCHED); scheduler_init_edf(); @@ -114,6 +217,9 @@ int platform_init(struct sof *sof) if (ret < 0) return ret; + /* register power states entry / exit notifiers */ + pm_notifier_register(&pm_state_notifier); + /* initialize the host IPC mechanisms */ trace_point(TRACE_BOOT_PLATFORM_IPC); ipc_init(sof); diff --git a/src/platform/meteorlake/include/platform/lib/clk.h b/src/platform/meteorlake/include/platform/lib/clk.h index 2eb1732aad55..e76a626e8a1a 100644 --- a/src/platform/meteorlake/include/platform/lib/clk.h +++ b/src/platform/meteorlake/include/platform/lib/clk.h @@ -24,7 +24,7 @@ #define CPU_LOWEST_FREQ_IDX CPU_WOVCRO_FREQ_IDX -#define CPU_DEFAULT_IDX CPU_HPRO_FREQ_IDX +#define CPU_DEFAULT_IDX CPU_LOWEST_FREQ_IDX #define SSP_DEFAULT_IDX 1 diff --git a/src/samples/audio/CMakeLists.txt b/src/samples/audio/CMakeLists.txt index 51bc3f5d1023..328070c2bb52 100644 --- a/src/samples/audio/CMakeLists.txt +++ b/src/samples/audio/CMakeLists.txt @@ -1,9 +1,14 @@ # SPDX-License-Identifier: BSD-3-Clause - if(CONFIG_SAMPLE_SMART_AMP) - add_local_sources(sof - smart_amp_test.c - ) + if(CONFIG_IPC_MAJOR_3) + add_local_sources(sof + smart_amp_test_ipc3.c + ) + elseif(CONFIG_IPC_MAJOR_4) + add_local_sources(sof + smart_amp_test_ipc4.c + ) + endif() endif() if(CONFIG_SAMPLE_KEYPHRASE) diff --git a/src/samples/audio/smart_amp_test.c b/src/samples/audio/smart_amp_test_ipc3.c similarity index 64% rename from src/samples/audio/smart_amp_test.c rename to src/samples/audio/smart_amp_test_ipc3.c index 10eb2e6f31e2..a8c9e0898dde 100644 --- a/src/samples/audio/smart_amp_test.c +++ b/src/samples/audio/smart_amp_test_ipc3.c @@ -16,10 +16,6 @@ #include #endif -#if CONFIG_IPC_MAJOR_4 -#include -#endif - static const struct comp_driver comp_smart_amp; LOG_MODULE_REGISTER(smart_amp_test, CONFIG_SOF_LOG_LEVEL); @@ -31,10 +27,12 @@ DECLARE_SOF_RT_UUID("smart_amp-test", smart_amp_comp_uuid, 0x167a961e, 0x8ae4, DECLARE_TR_CTX(smart_amp_comp_tr, SOF_UUID(smart_amp_comp_uuid), LOG_LEVEL_INFO); +typedef int(*smart_amp_proc)(struct comp_dev *dev, + const struct audio_stream __sparse_cache *source, + const struct audio_stream __sparse_cache *sink, uint32_t frames, + int8_t *chan_map); + struct smart_amp_data { -#if CONFIG_IPC_MAJOR_4 - struct sof_smart_amp_ipc4_config ipc4_cfg; -#endif struct sof_smart_amp_config config; struct comp_data_blob_handler *model_handler; void *data_blob; @@ -44,8 +42,6 @@ struct smart_amp_data { struct comp_buffer *feedback_buf; /**< feedback source buffer */ struct comp_buffer *sink_buf; /**< sink buffer */ - struct k_mutex lock; /**< protect feedback_buf updated */ - smart_amp_proc process; uint32_t in_channels; @@ -56,12 +52,8 @@ static struct comp_dev *smart_amp_new(const struct comp_driver *drv, const struct comp_ipc_config *config, const void *spec) { -#if CONFIG_IPC_MAJOR_3 const struct ipc_config_process *ipc_sa = spec; const struct sof_smart_amp_config *cfg; -#else - const struct ipc4_base_module_extended_cfg *base_cfg = spec; -#endif struct smart_amp_data *sad; struct comp_dev *dev; size_t bs; @@ -82,35 +74,14 @@ static struct comp_dev *smart_amp_new(const struct comp_driver *drv, if (!sad->model_handler) goto sad_fail; - k_mutex_init(&sad->lock); - -#if CONFIG_IPC_MAJOR_4 - if (base_cfg->base_cfg_ext.nb_input_pins != SMART_AMP_NUM_IN_PINS || - base_cfg->base_cfg_ext.nb_output_pins != SMART_AMP_NUM_OUT_PINS) { - comp_err(dev, "smart_amp_new(): Invalid pin configuration"); - goto sad_fail; - } - - /* Copy the base_cfg */ - memcpy_s(&sad->ipc4_cfg.base, sizeof(sad->ipc4_cfg.base), - &base_cfg->base_cfg, sizeof(base_cfg->base_cfg)); - /* Copy the pin formats */ - bs = sizeof(sad->ipc4_cfg.input_pins) + sizeof(sad->ipc4_cfg.output_pin); - memcpy_s(sad->ipc4_cfg.input_pins, bs, - base_cfg->base_cfg_ext.pin_formats, bs); -#else cfg = (struct sof_smart_amp_config *)ipc_sa->data; bs = ipc_sa->size; - - if ((bs > 0) && (bs < sizeof(struct sof_smart_amp_config))) { + if (bs > 0 && bs < sizeof(struct sof_smart_amp_config)) { comp_err(dev, "smart_amp_new(): failed to apply config"); goto sad_fail; } - memcpy_s(&sad->config, sizeof(struct sof_smart_amp_config), cfg, bs); -#endif - dev->state = COMP_STATE_READY; return dev; @@ -123,196 +94,10 @@ static struct comp_dev *smart_amp_new(const struct comp_driver *drv, return NULL; } -#if CONFIG_IPC_MAJOR_4 -static void smart_amp_set_params(struct comp_dev *dev, - struct sof_ipc_stream_params *params) -{ - struct smart_amp_data *sad = comp_get_drvdata(dev); - struct comp_buffer *sink; - struct comp_buffer __sparse_cache *sink_c; - - comp_dbg(dev, "smart_amp_set_params()"); - - memset(params, 0, sizeof(*params)); - params->channels = sad->ipc4_cfg.base.audio_fmt.channels_count; - params->rate = sad->ipc4_cfg.base.audio_fmt.sampling_frequency; - params->sample_container_bytes = sad->ipc4_cfg.base.audio_fmt.depth / 8; - params->sample_valid_bytes = - sad->ipc4_cfg.base.audio_fmt.valid_bit_depth / 8; - params->buffer_fmt = sad->ipc4_cfg.base.audio_fmt.interleaving_style; - params->buffer.size = sad->ipc4_cfg.base.ibs; - - /* update sink format */ - if (!list_is_empty(&dev->bsink_list)) { - struct ipc4_output_pin_format *sink_fmt = &sad->ipc4_cfg.output_pin; - struct ipc4_audio_format out_fmt = sink_fmt->audio_fmt; - - sink = list_first_item(&dev->bsink_list, struct comp_buffer, source_list); - sink_c = buffer_acquire(sink); - sink_c->stream.channels = out_fmt.channels_count; - sink_c->stream.rate = out_fmt.sampling_frequency; - - audio_stream_fmt_conversion(out_fmt.depth, - out_fmt.valid_bit_depth, - &sink_c->stream.frame_fmt, - &sink_c->stream.valid_sample_fmt, - out_fmt.s_type); - - sink_c->buffer_fmt = out_fmt.interleaving_style; - params->frame_fmt = sink_c->stream.frame_fmt; - - sink_c->hw_params_configured = true; - - buffer_release(sink_c); - } -} - -static inline int smart_amp_set_config(struct comp_dev *dev, const char *data, - uint32_t data_size) -{ - struct smart_amp_data *sad = comp_get_drvdata(dev); - struct sof_smart_amp_config *cfg; - uint32_t cfg_size; - - cfg = (struct sof_smart_amp_config *)data; - cfg_size = data_size; - - if (cfg_size != sizeof(struct sof_smart_amp_config)) { - comp_err(dev, "smart_amp_set_config(): invalid config size %u, expect %u", - cfg_size, sizeof(struct sof_smart_amp_config)); - return -EINVAL; - } - - comp_dbg(dev, "smart_amp_set_config(): config size = %u", cfg_size); - - memcpy_s(&sad->config, sizeof(struct sof_smart_amp_config), cfg, - sizeof(struct sof_smart_amp_config)); - - return 0; -} - -static inline int smart_amp_get_config(struct comp_dev *dev, char *data, - uint32_t *data_size) -{ - struct smart_amp_data *sad = comp_get_drvdata(dev); - uint32_t cfg_size; - - cfg_size = sizeof(struct sof_smart_amp_config); - - if (cfg_size > *data_size) { - comp_err(dev, "smart_amp_get_config(): wrong config size %d", - *data_size); - } - - *data_size = cfg_size; - - return memcpy_s(data, cfg_size, &sad->config, cfg_size); -} - -static int smart_amp_set_large_config(struct comp_dev *dev, uint32_t param_id, bool first_block, - bool last_block, uint32_t data_offset, const char *data) -{ - struct smart_amp_data *sad = comp_get_drvdata(dev); - - comp_dbg(dev, "smart_amp_set_large_config()"); - - switch (param_id) { - case SMART_AMP_SET_MODEL: - return ipc4_comp_data_blob_set(sad->model_handler, - first_block, - last_block, - data_offset, - data); - case SMART_AMP_SET_CONFIG: - return smart_amp_set_config(dev, data, data_offset); - default: - return -EINVAL; - } -} - -static int smart_amp_get_large_config(struct comp_dev *dev, uint32_t param_id, bool first_block, - bool last_block, uint32_t *data_offset, char *data) -{ - comp_dbg(dev, "smart_amp_get_large_config()"); - - switch (param_id) { - case SMART_AMP_GET_CONFIG: - return smart_amp_get_config(dev, data, data_offset); - default: - return -EINVAL; - } -} - -static int smart_amp_get_attribute(struct comp_dev *dev, uint32_t type, - void *value) -{ - struct smart_amp_data *sad = comp_get_drvdata(dev); - - comp_dbg(dev, "smart_amp_get_attribute()"); - - switch (type) { - case COMP_ATTR_BASE_CONFIG: - *(struct ipc4_base_module_cfg *)value = sad->ipc4_cfg.base; - return 0; - default: - return -EINVAL; - } -} - -static int smart_amp_bind(struct comp_dev *dev, void *data) -{ - struct smart_amp_data *sad = comp_get_drvdata(dev); - struct comp_buffer __sparse_cache *buffer_c; - struct comp_buffer *source_buffer; - struct list_item *blist; - - comp_dbg(dev, "smart_amp_bind()"); - - /* searching for feedback source buffers */ - list_for_item(blist, &dev->bsource_list) { - source_buffer = container_of(blist, struct comp_buffer, sink_list); - - k_mutex_lock(&sad->lock, K_FOREVER); - buffer_c = buffer_acquire(source_buffer); - if (IPC4_SINK_QUEUE_ID(buffer_c->id) == SOF_SMART_AMP_FEEDBACK_QUEUE_ID) { - sad->feedback_buf = source_buffer; - buffer_c->stream.channels = sad->config.feedback_channels; - buffer_c->stream.rate = sad->ipc4_cfg.base.audio_fmt.sampling_frequency; - - buffer_release(buffer_c); - k_mutex_unlock(&sad->lock); - break; - } - - buffer_release(buffer_c); - k_mutex_unlock(&sad->lock); - } - - return 0; -} - -static int smart_amp_unbind(struct comp_dev *dev, void *data) -{ - struct smart_amp_data *sad = comp_get_drvdata(dev); - struct ipc4_module_bind_unbind *bu = data; - - comp_dbg(dev, "smart_amp_unbind()"); - - if (bu->extension.r.dst_queue == SOF_SMART_AMP_FEEDBACK_QUEUE_ID) { - k_mutex_lock(&sad->lock, K_FOREVER); - sad->feedback_buf = NULL; - k_mutex_unlock(&sad->lock); - } - - return 0; -} - -#else - static void smart_amp_set_params(struct comp_dev *dev, struct sof_ipc_stream_params *params) { - //nothing to do + /* nothing to do */ } static int smart_amp_set_config(struct comp_dev *dev, @@ -347,9 +132,9 @@ static int smart_amp_get_config(struct comp_dev *dev, { struct smart_amp_data *sad = comp_get_drvdata(dev); size_t bs; - int ret = 0; + int ret; - // Copy back to user space + /* Copy back to user space */ bs = sad->config.size; comp_dbg(dev, "smart_amp_set_config(), actual blob size = %u, expected blob size = %u", @@ -372,49 +157,40 @@ static int smart_amp_ctrl_get_bin_data(struct comp_dev *dev, int size) { struct smart_amp_data *sad = comp_get_drvdata(dev); - int ret = 0; assert(sad); switch (cdata->data->type) { case SOF_SMART_AMP_CONFIG: - ret = smart_amp_get_config(dev, cdata, size); - break; + return smart_amp_get_config(dev, cdata, size); case SOF_SMART_AMP_MODEL: - ret = comp_data_blob_get_cmd(sad->model_handler, cdata, size); - break; + return comp_data_blob_get_cmd(sad->model_handler, cdata, size); default: - comp_err(dev, "smart_amp_ctrl_get_bin_data(): unknown binary data type"); + comp_warn(dev, "smart_amp_ctrl_get_bin_data(): unknown binary data type"); break; } - return ret; + return 0; } static int smart_amp_ctrl_get_data(struct comp_dev *dev, struct sof_ipc_ctrl_data *cdata, int size) { - int ret = 0; - comp_info(dev, "smart_amp_ctrl_get_data() size: %d", size); 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(dev, cdata, size); default: comp_err(dev, "smart_amp_ctrl_get_data(): invalid cdata->cmd"); return -EINVAL; } - - return ret; } static int smart_amp_ctrl_set_bin_data(struct comp_dev *dev, struct sof_ipc_ctrl_data *cdata) { struct smart_amp_data *sad = comp_get_drvdata(dev); - int ret = 0; assert(sad); @@ -425,24 +201,20 @@ 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); - break; + return smart_amp_set_config(dev, cdata); case SOF_SMART_AMP_MODEL: - ret = comp_data_blob_set_cmd(sad->model_handler, cdata); - break; + return comp_data_blob_set_cmd(sad->model_handler, cdata); default: - comp_err(dev, "smart_amp_ctrl_set_bin_data(): unknown binary data type"); + comp_warn(dev, "smart_amp_ctrl_set_bin_data(): unknown binary data type"); break; } - return ret; + return 0; } static int smart_amp_ctrl_set_data(struct comp_dev *dev, struct sof_ipc_ctrl_data *cdata) { - int ret = 0; - /* 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"); @@ -455,15 +227,13 @@ static int smart_amp_ctrl_set_data(struct comp_dev *dev, break; case SOF_CTRL_CMD_BINARY: comp_info(dev, "smart_amp_ctrl_set_data(), SOF_CTRL_CMD_BINARY"); - ret = smart_amp_ctrl_set_bin_data(dev, cdata); - break; + return smart_amp_ctrl_set_bin_data(dev, cdata); default: comp_err(dev, "smart_amp_ctrl_set_data(): invalid cdata->cmd"); - ret = -EINVAL; - break; + return -EINVAL; } - return ret; + return 0; } /* used to pass standard and bespoke commands (with data) to component */ @@ -484,8 +254,6 @@ static int smart_amp_cmd(struct comp_dev *dev, int cmd, void *data, } } -#endif /* CONFIG_IPC_MAJOR_4 */ - static void smart_amp_free(struct comp_dev *dev) { struct smart_amp_data *sad = comp_get_drvdata(dev); @@ -526,7 +294,7 @@ static int smart_amp_params(struct comp_dev *dev, err = smart_amp_verify_params(dev, params); if (err < 0) { comp_err(dev, "smart_amp_params(): pcm params verification failed."); - return -EINVAL; + return err; } return 0; @@ -542,18 +310,16 @@ static int smart_amp_trigger(struct comp_dev *dev, int cmd) ret = comp_set_state(dev, cmd); if (ret == COMP_STATUS_STATE_ALREADY_SET) - ret = PPL_STATUS_PATH_STOP; + return PPL_STATUS_PATH_STOP; switch (cmd) { case COMP_TRIGGER_START: case COMP_TRIGGER_RELEASE: - k_mutex_lock(&sad->lock, K_FOREVER); if (sad->feedback_buf) { struct comp_buffer __sparse_cache *buf = buffer_acquire(sad->feedback_buf); buffer_zero(buf); buffer_release(buf); } - k_mutex_unlock(&sad->lock); break; case COMP_TRIGGER_PAUSE: case COMP_TRIGGER_STOP: @@ -592,7 +358,7 @@ static int smart_amp_process_s16(struct comp_dev *dev, } out_frag++; } - in_frag += source->channels; + in_frag += audio_stream_get_channels(source); } return 0; } @@ -624,7 +390,7 @@ static int smart_amp_process_s32(struct comp_dev *dev, } out_frag++; } - in_frag += source->channels; + in_frag += audio_stream_get_channels(source); } return 0; @@ -633,7 +399,7 @@ static int smart_amp_process_s32(struct comp_dev *dev, static smart_amp_proc get_smart_amp_process(struct comp_dev *dev, struct comp_buffer __sparse_cache *buf) { - switch (buf->stream.frame_fmt) { + switch (audio_stream_get_frm_fmt(&buf->stream)) { case SOF_IPC_FRAME_S16_LE: return smart_amp_process_s16; case SOF_IPC_FRAME_S24_4LE: @@ -664,7 +430,6 @@ static int smart_amp_copy(struct comp_dev *dev) audio_stream_avail_frames(&source_buf->stream, &sink_buf->stream); - k_mutex_lock(&sad->lock, K_FOREVER); if (sad->feedback_buf) { struct comp_buffer __sparse_cache *buf = buffer_acquire(sad->feedback_buf); @@ -692,7 +457,6 @@ static int smart_amp_copy(struct comp_dev *dev) buffer_release(buf); } - k_mutex_unlock(&sad->lock); if (!avail_frames) avail_frames = avail_passthrough_frames; @@ -751,12 +515,10 @@ static int smart_amp_prepare(struct comp_dev *dev) sink_list); buffer_c = buffer_acquire(source_buffer); -#if CONFIG_IPC_MAJOR_3 /* FIXME: how often can this loop be run? */ if (buffer_c->source->ipc_config.type == SOF_COMP_DEMUX) sad->feedback_buf = source_buffer; else -#endif sad->source_buf = source_buffer; buffer_release(buffer_c); @@ -766,21 +528,19 @@ static int smart_amp_prepare(struct comp_dev *dev) source_list); buffer_c = buffer_acquire(sad->sink_buf); - sad->out_channels = buffer_c->stream.channels; + sad->out_channels = audio_stream_get_channels(&buffer_c->stream); buffer_release(buffer_c); buffer_c = buffer_acquire(sad->source_buf); - sad->in_channels = buffer_c->stream.channels; + sad->in_channels = audio_stream_get_channels(&buffer_c->stream); - k_mutex_lock(&sad->lock, K_FOREVER); if (sad->feedback_buf) { struct comp_buffer __sparse_cache *buf = buffer_acquire(sad->feedback_buf); buf->stream.channels = sad->config.feedback_channels; - buf->stream.rate = buffer_c->stream.rate; + buf->stream.rate = audio_stream_get_rate(&buffer_c->stream); buffer_release(buf); } - k_mutex_unlock(&sad->lock); sad->process = get_smart_amp_process(dev, buffer_c); if (!sad->process) { @@ -802,15 +562,7 @@ static const struct comp_driver comp_smart_amp = { .free = smart_amp_free, .params = smart_amp_params, .prepare = smart_amp_prepare, -#if CONFIG_IPC_MAJOR_4 - .set_large_config = smart_amp_set_large_config, - .get_large_config = smart_amp_get_large_config, - .get_attribute = smart_amp_get_attribute, - .bind = smart_amp_bind, - .unbind = smart_amp_unbind, -#else .cmd = smart_amp_cmd, -#endif /* CONFIG_IPC_MAJOR_4 */ .trigger = smart_amp_trigger, .copy = smart_amp_copy, .reset = smart_amp_reset, diff --git a/src/samples/audio/smart_amp_test_ipc4.c b/src/samples/audio/smart_amp_test_ipc4.c new file mode 100644 index 000000000000..59b6c3a2fc9a --- /dev/null +++ b/src/samples/audio/smart_amp_test_ipc4.c @@ -0,0 +1,427 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// Copyright(c) 2020 Intel Corporation. All rights reserved. +// +// Author: Bartosz Kokoszko +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef __ZEPHYR__ +#include +#endif +#include + +LOG_MODULE_REGISTER(smart_amp_test, CONFIG_SOF_LOG_LEVEL); + +/* 167a961e-8ae4-11ea-89f1-000c29ce1635 */ +DECLARE_SOF_RT_UUID("smart_amp-test", smart_amp_comp_uuid, 0x167a961e, 0x8ae4, + 0x11ea, 0x89, 0xf1, 0x00, 0x0c, 0x29, 0xce, 0x16, 0x35); + +DECLARE_TR_CTX(smart_amp_comp_tr, SOF_UUID(smart_amp_comp_uuid), + LOG_LEVEL_INFO); +typedef int(*smart_amp_proc)(struct processing_module *mod, + struct input_stream_buffer *bsource, + struct output_stream_buffer *bsink, uint32_t frames, + int8_t *chan_map); +struct smart_amp_data { + struct sof_smart_amp_ipc4_config ipc4_cfg; + struct sof_smart_amp_config config; + struct comp_data_blob_handler *model_handler; + void *data_blob; + size_t data_blob_size; + smart_amp_proc process; + uint32_t out_channels; +}; + +static int smart_amp_init(struct processing_module *mod) +{ + struct smart_amp_data *sad; + struct comp_dev *dev = mod->dev; + struct module_data *mod_data = &mod->priv; + const uint8_t *src; + size_t copy_size; + int ret; + const struct ipc4_base_module_extended_cfg *base_cfg = mod_data->cfg.init_data; + + comp_dbg(dev, "smart_amp_init()"); + sad = rzalloc(SOF_MEM_ZONE_RUNTIME, 0, SOF_MEM_CAPS_RAM, sizeof(*sad)); + if (!sad) + return -ENOMEM; + + mod_data->private = sad; + + /* component model data handler */ + sad->model_handler = comp_data_blob_handler_new(dev); + if (!sad->model_handler) { + ret = -ENOMEM; + goto sad_fail; + } + + if (base_cfg->base_cfg_ext.nb_input_pins != SMART_AMP_NUM_IN_PINS || + base_cfg->base_cfg_ext.nb_output_pins != SMART_AMP_NUM_OUT_PINS) { + comp_err(dev, "smart_amp_init(): Invalid pin configuration"); + ret = -EINVAL; + goto sad_fail; + } + + /* + * Copy the pin formats. + * First the 2 x input formats then the following 1 x output format + */ + copy_size = sizeof(sad->ipc4_cfg.input_pins); + src = base_cfg->base_cfg_ext.pin_formats; + memcpy_s(sad->ipc4_cfg.input_pins, copy_size, src, copy_size); + + src += copy_size; + copy_size = sizeof(sad->ipc4_cfg.output_pin); + memcpy_s(&sad->ipc4_cfg.output_pin, copy_size, src, copy_size); + + mod->simple_copy = true; + + return 0; + +sad_fail: + comp_data_blob_handler_free(sad->model_handler); + rfree(sad); + return ret; +} + +static void smart_amp_set_params(struct processing_module *mod) +{ + const struct ipc4_audio_format *audio_fmt = &mod->priv.cfg.base_cfg.audio_fmt; + struct sof_ipc_stream_params *params = mod->stream_params; + struct comp_dev *dev = mod->dev; + struct smart_amp_data *sad = module_get_private_data(mod); + struct comp_buffer *sink; + struct comp_buffer __sparse_cache *sink_c; + enum sof_ipc_frame frame_fmt, valid_fmt; + int i; + + memset(params, 0, sizeof(*params)); + 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; + params->buffer.size = mod->priv.cfg.base_cfg.ibs; + + for (i = 0; i < SOF_IPC_MAX_CHANNELS; i++) + params->chmap[i] = (audio_fmt->ch_map >> i * 4) & 0xf; + + /* update sink format */ + if (!list_is_empty(&dev->bsink_list)) { + struct ipc4_output_pin_format *sink_fmt = &sad->ipc4_cfg.output_pin; + struct ipc4_audio_format out_fmt = sink_fmt->audio_fmt; + + sink = list_first_item(&dev->bsink_list, struct comp_buffer, source_list); + sink_c = buffer_acquire(sink); + sink_c->stream.channels = out_fmt.channels_count; + sink_c->stream.rate = out_fmt.sampling_frequency; + + audio_stream_fmt_conversion(out_fmt.depth, + out_fmt.valid_bit_depth, + &sink_c->stream.frame_fmt, + &sink_c->stream.valid_sample_fmt, + out_fmt.s_type); + + sink_c->buffer_fmt = out_fmt.interleaving_style; + params->frame_fmt = sink_c->stream.frame_fmt; + + sink_c->hw_params_configured = true; + buffer_release(sink_c); + } +} + +static int smart_amp_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 comp_dev *dev = mod->dev; + struct smart_amp_data *sad = module_get_private_data(mod); + + comp_dbg(dev, "smart_amp_set_config()"); + + switch (config_id) { + case SMART_AMP_SET_MODEL: + return comp_data_blob_set(sad->model_handler, pos, + 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", + fragment_size, sizeof(struct sof_smart_amp_config)); + return -EINVAL; + } + comp_dbg(dev, "smart_amp_set_config(): config size = %u", fragment_size); + memcpy_s(&sad->config, sizeof(sad->config), fragment, fragment_size); + return 0; + default: + return -EINVAL; + } +} + +static inline int smart_amp_get_config(struct processing_module *mod, + uint32_t config_id, uint32_t *data_offset_size, + uint8_t *fragment, size_t fragment_size) +{ + struct smart_amp_data *sad = module_get_private_data(mod); + struct comp_dev *dev = mod->dev; + int ret; + + comp_dbg(dev, "smart_amp_get_config()"); + + 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", + fragment_size); + return ret; + } + *data_offset_size = sizeof(sad->config); + return 0; + default: + return -EINVAL; + } +} + +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); + return 0; +} + +static int smart_amp_params(struct processing_module *mod) +{ + struct sof_ipc_stream_params *params = mod->stream_params; + struct comp_dev *dev = mod->dev; + int ret; + + comp_dbg(dev, "smart_amp_params()"); + smart_amp_set_params(mod); + ret = comp_verify_params(dev, BUFF_PARAMS_CHANNELS, params); + if (ret < 0) { + comp_err(dev, "smart_amp_params(): pcm params verification failed."); + return -EINVAL; + } + return 0; +} + +static int smart_amp_process_s16(struct processing_module *mod, + struct input_stream_buffer *bsource, + struct output_stream_buffer *bsink, + uint32_t frames, int8_t *chan_map) +{ + struct smart_amp_data *sad = module_get_private_data(mod); + struct audio_stream __sparse_cache *source = bsource->data; + struct audio_stream __sparse_cache *sink = bsink->data; + int16_t *src; + int16_t *dest; + uint32_t in_frag = 0; + uint32_t out_frag = 0; + int i; + int j; + + bsource->consumed += frames * source->channels * sizeof(int16_t); + for (i = 0; i < frames; i++) { + for (j = 0 ; j < sad->out_channels; j++) { + if (chan_map[j] != -1) { + src = audio_stream_read_frag_s16(source, + in_frag + + chan_map[j]); + dest = audio_stream_write_frag_s16(sink, + out_frag); + *dest = *src; + } + out_frag++; + } + in_frag += source->channels; + } + return 0; +} + +static int smart_amp_process_s32(struct processing_module *mod, + struct input_stream_buffer *bsource, + struct output_stream_buffer *bsink, + uint32_t frames, int8_t *chan_map) +{ + struct smart_amp_data *sad = module_get_private_data(mod); + struct audio_stream __sparse_cache *source = bsource->data; + struct audio_stream __sparse_cache *sink = bsink->data; + int32_t *src; + int32_t *dest; + uint32_t in_frag = 0; + uint32_t out_frag = 0; + int i; + int j; + + bsource->consumed += frames * source->channels * sizeof(int32_t); + for (i = 0; i < frames; i++) { + for (j = 0 ; j < sad->out_channels; j++) { + if (chan_map[j] != -1) { + src = audio_stream_read_frag_s32(source, + in_frag + + chan_map[j]); + dest = audio_stream_write_frag_s32(sink, + out_frag); + *dest = *src; + } + out_frag++; + } + in_frag += source->channels; + } + + return 0; +} + +static smart_amp_proc get_smart_amp_process(struct comp_dev *dev, + struct comp_buffer __sparse_cache *buf) +{ + switch (buf->stream.frame_fmt) { + case SOF_IPC_FRAME_S16_LE: + return smart_amp_process_s16; + case SOF_IPC_FRAME_S24_4LE: + case SOF_IPC_FRAME_S32_LE: + return smart_amp_process_s32; + default: + comp_err(dev, "smart_amp_process() error: not supported frame format"); + return NULL; + } +} + +static int smart_amp_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 smart_amp_data *sad = module_get_private_data(mod); + struct comp_dev *dev = mod->dev; + struct comp_buffer __sparse_cache *fb_buf_c; + struct comp_buffer __sparse_cache *buf; + struct module_source_info __sparse_cache *mod_source_info; + struct input_stream_buffer *fb_input = NULL; + /* if there is only one input stream, it should be the source input */ + struct input_stream_buffer *src_input = &input_buffers[0]; + uint32_t avail_passthrough_frames; + uint32_t avail_frames = 0; + uint32_t sink_bytes; + uint32_t i; + + mod_source_info = module_source_info_acquire(mod->source_info); + + if (num_input_buffers == SMART_AMP_NUM_IN_PINS) + for (i = 0; i < num_input_buffers; i++) { + buf = attr_container_of(input_buffers[i].data, + struct comp_buffer __sparse_cache, + stream, __sparse_cache); + + if (IPC4_SINK_QUEUE_ID(buf->id) == SOF_SMART_AMP_FEEDBACK_QUEUE_ID) { + fb_input = &input_buffers[i]; + fb_buf_c = buf; + } else { + src_input = &input_buffers[i]; + } + } + + avail_passthrough_frames = src_input->size; + + if (fb_input) { + if (fb_buf_c->source && comp_get_state(dev, fb_buf_c->source) == dev->state) { + /* feedback */ + avail_frames = MIN(avail_passthrough_frames, + fb_input->size); + + sad->process(mod, fb_input, &output_buffers[0], + avail_frames, sad->config.feedback_ch_map); + } + } + + if (!avail_frames) + avail_frames = avail_passthrough_frames; + + /* bytes calculation */ + sink_bytes = avail_frames * + audio_stream_frame_bytes(output_buffers[0].data); + + /* process data */ + sad->process(mod, src_input, &output_buffers[0], + avail_frames, sad->config.source_ch_map); + + output_buffers[0].size = sink_bytes; + + module_source_info_release(mod_source_info); + return 0; +} + +static int smart_amp_reset(struct processing_module *mod) +{ + struct comp_dev *dev = mod->dev; + + comp_dbg(dev, "smart_amp_reset()"); + + return 0; +} + +static int smart_amp_prepare(struct processing_module *mod) +{ + struct smart_amp_data *sad = module_get_private_data(mod); + struct comp_dev *dev = mod->dev; + struct comp_buffer *source_buffer; + struct comp_buffer *sink_buffer; + struct comp_buffer __sparse_cache *buffer_c; + struct list_item *blist; + int ret; + + ret = smart_amp_params(mod); + if (ret < 0) + return ret; + + comp_dbg(dev, "smart_amp_prepare()"); + /* searching for stream and feedback source buffers */ + list_for_item(blist, &dev->bsource_list) { + source_buffer = container_of(blist, struct comp_buffer, + sink_list); + buffer_c = buffer_acquire(source_buffer); + audio_stream_init_alignment_constants(1, 1, &buffer_c->stream); + if (IPC4_SINK_QUEUE_ID(buffer_c->id) == SOF_SMART_AMP_FEEDBACK_QUEUE_ID) { + buffer_c->stream.channels = sad->config.feedback_channels; + buffer_c->stream.rate = mod->priv.cfg.base_cfg.audio_fmt.sampling_frequency; + } + buffer_release(buffer_c); + } + + sink_buffer = list_first_item(&dev->bsink_list, struct comp_buffer, source_list); + buffer_c = buffer_acquire(sink_buffer); + sad->out_channels = buffer_c->stream.channels; + audio_stream_init_alignment_constants(1, 1, &buffer_c->stream); + sad->process = get_smart_amp_process(dev, buffer_c); + buffer_release(buffer_c); + + if (!sad->process) { + comp_err(dev, "smart_amp_prepare(): get_smart_amp_process failed"); + ret = -EINVAL; + } + return ret; +} + +static struct module_interface smart_amp_interface = { + .init = smart_amp_init, + .prepare = smart_amp_prepare, + .process = smart_amp_process, + .set_configuration = smart_amp_set_config, + .get_configuration = smart_amp_get_config, + .reset = smart_amp_reset, + .free = smart_amp_free +}; +DECLARE_MODULE_ADAPTER(smart_amp_interface, smart_amp_comp_uuid, smart_amp_comp_tr); +SOF_MODULE_INIT(smart_amp_test, sys_comp_module_smart_amp_interface_init); diff --git a/tools/topology/topology2/cavs-nocodec-multicore.conf b/tools/topology/topology2/cavs-nocodec-multicore.conf new file mode 100644 index 000000000000..b1dc28426617 --- /dev/null +++ b/tools/topology/topology2/cavs-nocodec-multicore.conf @@ -0,0 +1,473 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Define { + MCLK 24576000 + PLATFORM "none" + SSP0_CORE_ID 0 + SSP1_CORE_ID 1 + SSP2_CORE_ID 2 +} + +# override defaults with platform-specific config +IncludeByKey.PLATFORM { + "tgl" "platform/intel/tgl.conf" + "adl" "platform/intel/tgl.conf" + "mtl" "platform/intel/mtl.conf" +} + +# +# List of all DAIs +# +Object.Dai.SSP [ + { + id 0 + dai_index 0 + direction "duplex" + name NoCodec-0 + default_hw_conf_id 0 + sample_bits 32 + quirks "lbm_mode" + io_clk $MCLK + + Object.Base.hw_config.1 { + name "SSP0" + id 0 + bclk_freq 3072000 + tdm_slot_width 32 + # TODO: remove this. Needs alsaptlg change. + Object.Base.link_config.1 { + clock_source 1 + } + } + } + { + id 2 + dai_index 2 + direction "duplex" + name NoCodec-2 + default_hw_conf_id 0 + sample_bits 32 + quirks "lbm_mode" + io_clk $MCLK + + Object.Base.hw_config.1 { + name "SSP2" + id 0 + bclk_freq 3072000 + tdm_slot_width 32 + # TODO: remove this. Needs alsaptlg change. + Object.Base.link_config.1 { + clock_source 1 + } + } + } +] + +# +# Pipeline definitions +# +# PCM0 ---> gain ---> Mixin ---> Mixout ---> gain ---> SSP0 (core SSP0_CORE_ID) +# PCM1 ---> gain ---> Mixin ---> Mixout ---> gain ---> SSP1 (core SSP1_CORE_ID) +# PCM2 ---> gain ---> Mixin ---> Mixout ---> gain ---> SSP2 (core SSP2_CORE_ID) +# SSP0 ---> PCM0 +# SSP1 ---> PCM1 +# SSP2 ---> PCM2 + +# Pipeline ID:1 PCM ID: 0 +Object.Pipeline.host-copier-gain-mixin-playback [ + { + index 1 + + core $SSP0_CORE_ID + Object.Widget.copier.1 { + core_id $SSP0_CORE_ID + stream_name 'SSP0 Playback' + } + Object.Widget.gain.1 { + core_id $SSP0_CORE_ID + Object.Control.mixer.1 { + name 'Playback Volume 1' + } + } + } + { + index 5 + + core $SSP2_CORE_ID + Object.Widget.copier.1 { + core_id $SSP2_CORE_ID + stream_name 'SSP2 Playback' + } + Object.Widget.gain.1 { + core_id $SSP2_CORE_ID + Object.Control.mixer.1 { + name 'Playback Volume 5' + } + } + Object.Widget.mixin.1 { + core_id $SSP2_CORE_ID + } + } +] + +Object.Pipeline.mixout-gain-dai-copier-playback [ + { + index 14 + + core $SSP0_CORE_ID + Object.Widget.copier.1 { + core_id $SSP0_CORE_ID + dai_index 0 + dai_type "SSP" + copier_type "SSP" + stream_name "NoCodec-0" + node_type $I2S_LINK_OUTPUT_CLASS + } + + Object.Widget.gain.1 { + core_id $SSP0_CORE_ID + Object.Control.mixer.1 { + name 'Main Playback Volume 14' + } + } + } + { + index 6 + + core $SSP2_CORE_ID + Object.Widget.copier.1 { + core_id $SSP2_CORE_ID + dai_index 2 + dai_type "SSP" + copier_type "SSP" + stream_name "NoCodec-2" + node_type $I2S_LINK_OUTPUT_CLASS + } + + Object.Widget.gain.1 { + core_id $SSP2_CORE_ID + Object.Control.mixer.1 { + name 'Main Playback Volume 6' + } + } + Object.Widget.mixout.1 { + core_id $SSP2_CORE_ID + } + } +] + +Object.Pipeline.host-gateway-capture [ + { + index 7 + + Object.Widget.copier.1 { + stream_name 'SSP0 Capture' + } + } + { + index 11 + + Object.Widget.copier.1 { + stream_name 'SSP2 Capture' + } + } +] + +Object.Pipeline.io-gateway-capture [ + { + index 8 + direction capture + + Object.Widget.copier."1" { + dai_index 1 + dai_type "SSP" + type dai_out + copier_type "SSP" + stream_name "NoCodec-0" + node_type $I2S_LINK_INPUT_CLASS + Object.Base.audio_format.1 { + in_bit_depth 32 + in_valid_bit_depth 32 + out_bit_depth 32 + out_valid_bit_depth 32 + } + } + } + { + index 12 + direction capture + + Object.Widget.copier."1" { + dai_index 2 + dai_type "SSP" + type dai_out + copier_type "SSP" + stream_name "NoCodec-2" + node_type $I2S_LINK_INPUT_CLASS + Object.Base.audio_format.1 { + in_bit_depth 32 + in_valid_bit_depth 32 + out_bit_depth 32 + out_valid_bit_depth 32 + } + } + } +] + +Object.PCM.pcm [ + { + name "Port0" + id 0 + direction "duplex" + Object.Base.fe_dai.1 { + name "Port0" + } + + Object.PCM.pcm_caps.1 { + direction "playback" + name "SSP0 Playback" + formats 'S16_LE,S24_LE,S32_LE' + } + + Object.PCM.pcm_caps.2 { + direction "capture" + name "SSP0 Capture" + formats 'S16_LE,S24_LE,S32_LE' + } + } + { + name "Port2" + id 2 + direction "duplex" + Object.Base.fe_dai.1 { + name "Port2" + } + + Object.PCM.pcm_caps.1 { + direction "playback" + name "SSP2 Playback" + formats 'S16_LE,S24_LE,S32_LE' + } + + Object.PCM.pcm_caps.2 { + direction "capture" + name "SSP2 Capture" + formats 'S16_LE,S24_LE,S32_LE' + } + } +] + +Object.Base.route [ + { + source "gain.14.1" + sink "copier.SSP.14.1" + } + { + source "mixin.1.1" + sink "mixout.14.1" + } + { + source "gain.6.1" + sink "copier.SSP.6.1" + } + { + source "mixin.5.1" + sink "mixout.6.1" + } + { + source "copier.SSP.8.1" + sink "copier.host.7.1" + } + { + source "copier.SSP.12.1" + sink "copier.host.11.1" + } +] + +# There is pinmux conflict between SSP1 and DMIC on MTL RVP, +# so include SSP1 pipelines conditionally. +IncludeByKey.SSP1_ENABLED { + "true" { + Object.Dai.SSP [ + { + id 1 + dai_index 1 + direction "duplex" + name NoCodec-1 + default_hw_conf_id 0 + sample_bits 32 + quirks "lbm_mode" + io_clk $MCLK + + Object.Base.hw_config.1 { + name "SSP1" + id 0 + bclk_freq 3072000 + tdm_slot_width 32 + # TODO: remove this. Needs alsaptlg change. + Object.Base.link_config.1 { + clock_source 1 + } + } + } + ] + + Object.Pipeline.host-copier-gain-mixin-playback [ + { + index 3 + + core $SSP1_CORE_ID + Object.Widget.copier.1 { + core_id $SSP1_CORE_ID + stream_name 'SSP1 Playback' + } + Object.Widget.gain.1 { + core_id $SSP1_CORE_ID + Object.Control.mixer.1 { + name 'Playback Volume 3' + } + } + Object.Widget.mixin.1 { + core_id $SSP1_CORE_ID + } + } + ] + + Object.Pipeline.mixout-gain-dai-copier-playback [ + { + index 4 + + core $SSP1_CORE_ID + Object.Widget.copier.1 { + core_id $SSP1_CORE_ID + dai_index 1 + dai_type "SSP" + copier_type "SSP" + stream_name "NoCodec-1" + node_type $I2S_LINK_OUTPUT_CLASS + } + + Object.Widget.gain.1 { + core_id $SSP1_CORE_ID + Object.Control.mixer.1 { + name 'Main Playback Volume 4' + } + } + Object.Widget.mixout.1 { + core_id $SSP1_CORE_ID + } + } + ] + + Object.Pipeline.host-gateway-capture [ + { + index 9 + + Object.Widget.copier.1 { + stream_name 'SSP1 Capture' + } + } + ] + + Object.Pipeline.io-gateway-capture [ + { + index 10 + direction capture + + Object.Widget.copier."1" { + dai_index 1 + dai_type "SSP" + type dai_out + copier_type "SSP" + stream_name "NoCodec-1" + node_type $I2S_LINK_INPUT_CLASS + Object.Base.audio_format.1 { + in_bit_depth 32 + in_valid_bit_depth 32 + out_bit_depth 32 + out_valid_bit_depth 32 + } + } + } + ] + + Object.PCM.pcm [ + { + name "Port1" + id 1 + direction "duplex" + Object.Base.fe_dai.1 { + name "Port1" + } + + Object.PCM.pcm_caps.1 { + direction "playback" + name "SSP1 Playback" + formats 'S16_LE,S24_LE,S32_LE' + } + + Object.PCM.pcm_caps.2 { + direction "capture" + name "SSP1 Capture" + formats 'S16_LE,S24_LE,S32_LE' + } + } + ] + + Object.Base.route [ + { + source "mixin.3.1" + sink "mixout.4.1" + } + { + source "gain.4.1" + sink "copier.SSP.4.1" + } + { + source "copier.SSP.10.1" + sink "copier.host.9.1" + } + ] + } +} diff --git a/tools/topology/topology2/cavs-nocodec.conf b/tools/topology/topology2/cavs-nocodec.conf index bf3d240019b6..0d5ab45ca022 100644 --- a/tools/topology/topology2/cavs-nocodec.conf +++ b/tools/topology/topology2/cavs-nocodec.conf @@ -213,7 +213,7 @@ Object.Pipeline.mixout-gain-smart-amp-dai-copier-playback [ } Object.Base.input_pin_binding.2 { - input_pin_binding_name "copier.SSP.8.1" + input_pin_binding_name "module-copier.8.1" } Object.Control.bytes."1" { @@ -345,7 +345,9 @@ Object.Pipeline.dai-copier-gain-module-copier-capture [ out_bit_depth 32 out_valid_bit_depth 32 } + } + Object.Widget.module-copier.1 { Object.Base.output_pin_binding.1 { output_pin_binding_name "gain.8.1" } @@ -634,7 +636,7 @@ Object.Base.route [ } { source "copier.SSP.8.1" - sink "gain.8.1" + sink "module-copier.8.1" } { source "copier.SSP.12.1" @@ -661,7 +663,7 @@ Object.Base.route [ sink gain.20.1 } { - source "copier.SSP.8.1" + source "module-copier.8.1" sink "smart_amp.2.1" } ] diff --git a/tools/topology/topology2/cavs-sdw.conf b/tools/topology/topology2/cavs-sdw.conf index 198e37953167..7708cfb422e5 100644 --- a/tools/topology/topology2/cavs-sdw.conf +++ b/tools/topology/topology2/cavs-sdw.conf @@ -37,6 +37,8 @@ + + @@ -69,6 +71,8 @@ Define { SDW_JACK_IN_BE_ID 1 NUM_SDW_AMP_LINKS 0 SDW_DMIC 0 + SDW_JACK true + SDW_JACK_CAPTURE_CH 2 } # override defaults with platform-specific config @@ -90,11 +94,6 @@ IncludeByKey.NUM_HDMIS { "[3-4]" "platform/intel/hdmi-generic.conf" } -# include deep buffer config if buffer size is in 1 - 1000 ms. -IncludeByKey.DEEPBUFFER_FW_DMA_MS{ - "[1-1000]" "platform/intel/deep-buffer.conf" -} - IncludeByKey.NUM_SDW_AMP_LINKS { "[1-2]" "platform/intel/sdw-amp-generic.conf" } @@ -103,159 +102,7 @@ IncludeByKey.SDW_DMIC { "1" "platform/intel/sdw-dmic-generic.conf" } -# -# List of all DAIs -# -#ALH Index: 0, Direction: duplex -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 48000 - 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 1 - rate 48000 - channels 2 - - Object.Base.hw_config.1 { - id 1 - name "ALH3" - } - } -] - -# -# Pipeline definitions -# - -# Pipeline ID:1 PCM ID: 0 -Object.Pipeline { - host-copier-gain-mixin-playback [ - { - index 0 - - Object.Widget.copier.1 { - stream_name "volume playback 0" - } - Object.Widget.gain.1 { - Object.Control.mixer.1 { - name '1 Playback Volume 0' - } - } - } - ] - - mixout-gain-dai-copier-playback [ - { - index 1 - - Object.Widget.copier.1 { - stream_name $SDW_JACK_OUT_STREAM - dai_type "ALH" - copier_type "ALH" - node_type $ALH_LINK_OUTPUT_CLASS - } - Object.Widget.gain.1 { - Object.Control.mixer.1 { - name '2 Main Playback Volume' - } - } - } - ] - - host-gateway-capture [ - { - index 10 - - Object.Widget.copier.1.stream_name "Passthrough Capture 0" - Object.Widget.copier.1.Object.Base.audio_format.1 { - # 32/32 -> 16/16 bits conversion is done here - in_bit_depth 32 - in_valid_bit_depth 32 - } - } - ] - - highpass-capture-be [ - { - direction "capture" - index 11 - copier_type "ALH" - - Object.Widget.copier.1 { - stream_name $SDW_JACK_IN_STREAM - dai_type "ALH" - copier_type "ALH" - type "dai_out" - node_type $ALH_LINK_INPUT_CLASS - } - Object.Widget.eqiir.1 { - Object.Control.bytes."1" { - name '4 Main capture Iir Eq' - } - } - } - ] +IncludeByKey.SDW_JACK { +"true" "platform/intel/sdw-jack-generic.conf" } -Object.PCM.pcm [ - { - name "Jack out" - id 0 - direction "playback" - Object.Base.fe_dai.1 { - name "Jack out" - } - - Object.PCM.pcm_caps.1 { - name "volume playback 0" - formats 'S16_LE,S32_LE' - } - } - { - name "Jack in" - id 1 - direction "capture" - Object.Base.fe_dai.1 { - name "Jack in" - } - - Object.PCM.pcm_caps.1 { - name "Passthrough Capture 0" - formats 'S16_LE,S32_LE' - } - } -] - -Object.Base.route [ - { - source "gain.1.1" - sink "copier.ALH.1.1" - } - { - source "mixin.0.1" - sink "mixout.1.1" - } - { - source "copier.ALH.11.1" - sink "eqiir.11.1" - } - { - source "eqiir.11.1" - sink "copier.host.10.1" - } -] diff --git a/tools/topology/topology2/development/tplg-targets.cmake b/tools/topology/topology2/development/tplg-targets.cmake index faed8382d4a1..0592f8b4a5fd 100644 --- a/tools/topology/topology2/development/tplg-targets.cmake +++ b/tools/topology/topology2/development/tplg-targets.cmake @@ -28,9 +28,14 @@ PLATFORM=adl" # SSP topology for MTL "cavs-nocodec\;sof-mtl-nocodec\;PLATFORM=mtl,NUM_DMICS=4,PDM1_MIC_A_ENABLE=1,PDM1_MIC_B_ENABLE=1,\ -PREPROCESS_PLUGINS=nhlt,NHLT_BIN=nhlt-sof-mtl-nocodec.bin,DEEPBUFFER_FW_DMA_MS=100" +PREPROCESS_PLUGINS=nhlt,NHLT_BIN=nhlt-sof-mtl-nocodec.bin,DEEPBUFFER_FW_DMA_MS=100,\ +DEEPBUFFER_D0I3_COMPATIBLE=true" "cavs-nocodec\;sof-mtl-nocodec-ssp0-ssp2\;PLATFORM=mtl,NUM_DMICS=2,SSP1_ENABLED=false,\ -PREPROCESS_PLUGINS=nhlt,NHLT_BIN=nhlt-sof-mtl-nocodec.bin,DEEPBUFFER_FW_DMA_MS=100" +PREPROCESS_PLUGINS=nhlt,NHLT_BIN=nhlt-sof-mtl-nocodec.bin,DEEPBUFFER_FW_DMA_MS=100,\ +DEEPBUFFER_D0I3_COMPATIBLE=true" + +"cavs-nocodec-multicore\;sof-mtl-nocodec-multicore\;PLATFORM=mtl,SSP1_ENABLED=true,SSP0_CORE_ID=0,\ +SSP1_CORE_ID=1,SSP2_CORE_ID=2,PREPROCESS_PLUGINS=nhlt,NHLT_BIN=nhlt-sof-mtl-nocodec.bin" # CAVS HDA topology with mixer-based efx eq pipelines for HDA and passthrough pipelines for HDMI "sof-hda-generic\;sof-hda-efx-generic\;HDA_CONFIG=efx,USE_CHAIN_DMA=true,DEEPBUFFER_FW_DMA_MS=100,\ diff --git a/tools/topology/topology2/include/components/src_format.conf b/tools/topology/topology2/include/components/src_format.conf index bb261d23d72b..6fc582f0cf0b 100644 --- a/tools/topology/topology2/include/components/src_format.conf +++ b/tools/topology/topology2/include/components/src_format.conf @@ -1,369 +1,238 @@ #src format array num_input_audio_formats 42 - num_output_audio_formats 42 - # 8khz input - Object.Base.audio_format.1 { - in_rate 8000 - in_bit_depth 16 - in_valid_bit_depth 16 - out_bit_depth 32 - out_valid_bit_depth 24 - } - - Object.Base.audio_format.2 { - in_rate 8000 - in_bit_depth 32 - in_valid_bit_depth 24 - out_bit_depth 32 - out_valid_bit_depth 24 - } - - Object.Base.audio_format.3{ - in_rate 8000 - in_bit_depth 32 - in_valid_bit_depth 32 - out_bit_depth 32 - out_valid_bit_depth 32 - } - - # 11.025 khz input - Object.Base.audio_format.4 { - in_rate 11025 - in_bit_depth 16 - in_valid_bit_depth 16 - out_bit_depth 32 - out_valid_bit_depth 24 - # for 11.025k, 22.05k, 44.1k, 88.2k and 176.4k, extra 4 sample size is needed - obs "$[($out_channels * (($[($out_rate + 999)] / 1000) + 4)) * ($out_bit_depth / 8)]" - } - - Object.Base.audio_format.5 { - in_rate 11025 - in_bit_depth 32 - in_valid_bit_depth 24 - out_bit_depth 32 - out_valid_bit_depth 24 - obs "$[($out_channels * (($[($out_rate + 999)] / 1000) + 4)) * ($out_bit_depth / 8)]" - } - - Object.Base.audio_format.6{ - in_rate 11025 - in_bit_depth 32 - in_valid_bit_depth 32 - out_bit_depth 32 - out_valid_bit_depth 32 - obs "$[($out_channels * (($[($out_rate + 999)] / 1000) + 4)) * ($out_bit_depth / 8)]" - } - - # 12khz input - Object.Base.audio_format.7 { - in_rate 12000 - in_bit_depth 16 - in_valid_bit_depth 16 - out_bit_depth 32 - out_valid_bit_depth 24 - } - - Object.Base.audio_format.8 { - in_rate 12000 - in_bit_depth 32 - in_valid_bit_depth 24 - out_bit_depth 32 - out_valid_bit_depth 24 - } - - Object.Base.audio_format.9{ - in_rate 12000 - in_bit_depth 32 - in_valid_bit_depth 32 - out_bit_depth 32 - out_valid_bit_depth 32 - } - - # 16khz input - Object.Base.audio_format.10 { - in_rate 16000 - in_bit_depth 16 - in_valid_bit_depth 16 - out_bit_depth 32 - out_valid_bit_depth 24 - } - - Object.Base.audio_format.11 { - in_rate 16000 - in_bit_depth 32 - in_valid_bit_depth 24 - out_bit_depth 32 - out_valid_bit_depth 24 - } - - Object.Base.audio_format.12{ - in_rate 16000 - in_bit_depth 32 - in_valid_bit_depth 32 - out_bit_depth 32 - out_valid_bit_depth 32 - } - - # 22.05khz input - Object.Base.audio_format.13 { - in_rate 22050 - in_bit_depth 16 - in_valid_bit_depth 16 - out_bit_depth 32 - out_valid_bit_depth 24 - obs "$[($out_channels * (($[($out_rate + 999)] / 1000) + 4)) * ($out_bit_depth / 8)]" - } - - Object.Base.audio_format.14 { - in_rate 22050 - in_bit_depth 32 - in_valid_bit_depth 24 - out_bit_depth 32 - out_valid_bit_depth 24 - obs "$[($out_channels * (($[($out_rate + 999)] / 1000) + 4)) * ($out_bit_depth / 8)]" - } - - Object.Base.audio_format.15{ - in_rate 22050 - in_bit_depth 32 - in_valid_bit_depth 32 - out_bit_depth 32 - out_valid_bit_depth 32 - obs "$[($out_channels * (($[($out_rate + 999)] / 1000) + 4)) * ($out_bit_depth / 8)]" - } - - - # 24khz input - Object.Base.audio_format.16 { - in_rate 24000 - in_bit_depth 16 - in_valid_bit_depth 16 - out_bit_depth 32 - out_valid_bit_depth 24 - } - - Object.Base.audio_format.17 { - in_rate 24000 - in_bit_depth 32 - in_valid_bit_depth 24 - out_bit_depth 32 - out_valid_bit_depth 24 - } - - Object.Base.audio_format.18{ - in_rate 24000 - in_bit_depth 32 - in_valid_bit_depth 32 - out_bit_depth 32 - out_valid_bit_depth 32 - } - - # 32khz input - Object.Base.audio_format.19 { - in_rate 32000 - in_bit_depth 16 - in_valid_bit_depth 16 - out_bit_depth 32 - out_valid_bit_depth 24 - } - - Object.Base.audio_format.20 { - in_rate 32000 - in_bit_depth 32 - in_valid_bit_depth 24 - out_bit_depth 32 - out_valid_bit_depth 24 - } - - Object.Base.audio_format.21 { - in_rate 32000 - in_bit_depth 32 - in_valid_bit_depth 32 - out_bit_depth 32 - out_valid_bit_depth 32 - } - - # 44.1khz input - Object.Base.audio_format.22 { - in_rate 44100 - in_bit_depth 16 - in_valid_bit_depth 16 - out_bit_depth 32 - out_valid_bit_depth 24 - obs "$[($out_channels * (($[($out_rate + 999)] / 1000) + 4)) * ($out_bit_depth / 8)]" - } - - Object.Base.audio_format.23 { - in_rate 44100 - in_bit_depth 32 - in_valid_bit_depth 24 - out_bit_depth 32 - out_valid_bit_depth 24 - obs "$[($out_channels * (($[($out_rate + 999)] / 1000) + 4)) * ($out_bit_depth / 8)]" - } - - Object.Base.audio_format.24{ - in_rate 44100 - in_bit_depth 32 - in_valid_bit_depth 32 - out_bit_depth 32 - out_valid_bit_depth 32 - obs "$[($out_channels * (($[($out_rate + 999)] / 1000) + 4)) * ($out_bit_depth / 8)]" - } - - # 48khz input - Object.Base.audio_format.25 { - in_rate 48000 - in_bit_depth 16 - in_valid_bit_depth 16 - out_bit_depth 32 - out_valid_bit_depth 24 - } - - Object.Base.audio_format.26 { - in_rate 48000 - in_bit_depth 32 - in_valid_bit_depth 24 - out_bit_depth 32 - out_valid_bit_depth 24 - } - - Object.Base.audio_format.27 { - in_rate 48000 - in_bit_depth 32 - in_valid_bit_depth 32 - out_bit_depth 32 - out_valid_bit_depth 32 - } - - # 64khz input - Object.Base.audio_format.28 { - in_rate 64000 - in_bit_depth 16 - in_valid_bit_depth 16 - out_bit_depth 32 - out_valid_bit_depth 24 - } - - Object.Base.audio_format.29 { - in_rate 64000 - in_bit_depth 32 - in_valid_bit_depth 24 - out_bit_depth 32 - out_valid_bit_depth 24 - } - - Object.Base.audio_format.30 { - in_rate 64000 - in_bit_depth 32 - in_valid_bit_depth 32 - out_bit_depth 32 - out_valid_bit_depth 32 - } - # 88.2khz input - Object.Base.audio_format.31 { - in_rate 88200 - in_bit_depth 16 - in_valid_bit_depth 16 - out_bit_depth 32 - out_valid_bit_depth 24 - obs "$[($out_channels * (($[($out_rate + 999)] / 1000) + 4)) * ($out_bit_depth / 8)]" - } - - Object.Base.audio_format.32 { - in_rate 88200 - in_bit_depth 32 - in_valid_bit_depth 24 - out_bit_depth 32 - out_valid_bit_depth 24 - obs "$[($out_channels * (($[($out_rate + 999)] / 1000) + 4)) * ($out_bit_depth / 8)]" - } - - Object.Base.audio_format.33 { - in_rate 88200 - in_bit_depth 32 - in_valid_bit_depth 32 - out_bit_depth 32 - out_valid_bit_depth 32 - obs "$[($out_channels * (($[($out_rate + 999)] / 1000) + 4)) * ($out_bit_depth / 8)]" - } - - # 96khz input - Object.Base.audio_format.34 { - in_rate 96000 - in_bit_depth 16 - in_valid_bit_depth 16 - out_bit_depth 32 - out_valid_bit_depth 24 - } - - Object.Base.audio_format.35 { - in_rate 96000 - in_bit_depth 32 - in_valid_bit_depth 24 - out_bit_depth 32 - out_valid_bit_depth 24 - } - - Object.Base.audio_format.36 { - in_rate 96000 - in_bit_depth 32 - in_valid_bit_depth 32 - out_bit_depth 32 - out_valid_bit_depth 32 - } - - # 176.4khz input - Object.Base.audio_format.37 { - in_rate 176400 - in_bit_depth 16 - in_valid_bit_depth 16 - out_bit_depth 32 - out_valid_bit_depth 24 - obs "$[($out_channels * (($[($out_rate + 999)] / 1000) + 4)) * ($out_bit_depth / 8)]" - } - - Object.Base.audio_format.38 { - in_rate 176400 - in_bit_depth 32 - in_valid_bit_depth 24 - out_bit_depth 32 - out_valid_bit_depth 24 - obs "$[($out_channels * (($[($out_rate + 999)] / 1000) + 4)) * ($out_bit_depth / 8)]" - } - - Object.Base.audio_format.39 { - in_rate 176400 - in_bit_depth 32 - in_valid_bit_depth 32 - out_bit_depth 32 - out_valid_bit_depth 32 - obs "$[($out_channels * (($[($out_rate + 999)] / 1000) + 4)) * ($out_bit_depth / 8)]" - } - - # 192khz input - Object.Base.audio_format.40 { - in_rate 192000 - in_bit_depth 16 - in_valid_bit_depth 16 - out_bit_depth 32 - out_valid_bit_depth 24 - } - - Object.Base.audio_format.41 { - in_rate 192000 - in_bit_depth 32 - in_valid_bit_depth 24 - out_bit_depth 32 - out_valid_bit_depth 24 - } - - Object.Base.audio_format.42 { - in_rate 192000 - in_bit_depth 32 - in_valid_bit_depth 32 - out_bit_depth 32 - out_valid_bit_depth 32 - } + Object.Base.input_audio_format [ + # 8khz input + { + in_rate 8000 + in_bit_depth 16 + in_valid_bit_depth 16 + } + { + in_rate 8000 + in_bit_depth 32 + in_valid_bit_depth 24 + } + { + in_bit_depth 32 + in_valid_bit_depth 32 + } + # 11.025 khz input + { + in_rate 11025 + in_bit_depth 16 + in_valid_bit_depth 16 + } + { + in_rate 11025 + in_bit_depth 32 + in_valid_bit_depth 24 + } + { + in_rate 11025 + in_bit_depth 32 + in_valid_bit_depth 32 + } + # 12khz input + { + in_rate 12000 + in_bit_depth 16 + in_valid_bit_depth 16 + } + { + in_rate 12000 + in_bit_depth 32 + in_valid_bit_depth 24 + } + { + in_rate 12000 + in_bit_depth 32 + in_valid_bit_depth 32 + } + # 16khz input + { + in_rate 16000 + 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 + } + # 22.05khz input + { + in_rate 22050 + in_bit_depth 16 + in_valid_bit_depth 16 + } + { + in_rate 22050 + in_bit_depth 32 + in_valid_bit_depth 24 + } + { + in_rate 22050 + in_bit_depth 32 + in_valid_bit_depth 32 + } + # 24khz input + { + in_rate 24000 + in_bit_depth 16 + in_valid_bit_depth 16 + } + { + in_rate 24000 + in_bit_depth 32 + in_valid_bit_depth 24 + } + { + in_rate 24000 + in_bit_depth 32 + in_valid_bit_depth 32 + } + # 32khz input + { + in_rate 32000 + in_bit_depth 16 + in_valid_bit_depth 16 + } + { + in_rate 32000 + in_bit_depth 32 + in_valid_bit_depth 24 + } + { + in_rate 32000 + in_bit_depth 32 + in_valid_bit_depth 32 + } + # 44.1khz input + { + in_rate 44100 + in_bit_depth 16 + in_valid_bit_depth 16 + } + { + in_rate 44100 + in_bit_depth 32 + in_valid_bit_depth 24 + } + { + in_rate 44100 + in_bit_depth 32 + in_valid_bit_depth 32 + } + # 48khz input + { + in_rate 48000 + in_bit_depth 16 + in_valid_bit_depth 16 + } + { + in_rate 48000 + in_bit_depth 32 + in_valid_bit_depth 24 + } + { + in_rate 48000 + in_bit_depth 32 + in_valid_bit_depth 32 + } + # 64khz input + { + in_rate 64000 + in_bit_depth 16 + in_valid_bit_depth 16 + } + { + in_rate 64000 + in_bit_depth 32 + in_valid_bit_depth 24 + } + { + in_rate 64000 + in_bit_depth 32 + in_valid_bit_depth 32 + } + # 88.2khz input + { + in_rate 88200 + in_bit_depth 16 + in_valid_bit_depth 16 + } + { + in_rate 88200 + in_bit_depth 32 + in_valid_bit_depth 24 + } + { + in_rate 88200 + in_bit_depth 32 + in_valid_bit_depth 32 + } + # 96khz input + { + in_rate 96000 + in_bit_depth 16 + in_valid_bit_depth 16 + } + { + in_rate 96000 + in_bit_depth 32 + in_valid_bit_depth 24 + } + { + in_rate 96000 + in_bit_depth 32 + in_valid_bit_depth 32 + } + # 176.4khz input + { + in_rate 176400 + in_bit_depth 16 + in_valid_bit_depth 16 + } + { + in_rate 176400 + in_bit_depth 32 + in_valid_bit_depth 24 + } + { + in_rate 176400 + in_bit_depth 32 + in_valid_bit_depth 32 + } + # 192khz input + { + in_rate 192000 + in_bit_depth 16 + in_valid_bit_depth 16 + } + { + in_rate 192000 + in_bit_depth 32 + in_valid_bit_depth 24 + } + { + in_rate 192000 + 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 + } + ] diff --git a/tools/topology/topology2/include/pipelines/cavs/dai-copier-gain-module-copier-capture.conf b/tools/topology/topology2/include/pipelines/cavs/dai-copier-gain-module-copier-capture.conf index dfa3c2752064..ad2259d47898 100644 --- a/tools/topology/topology2/include/pipelines/cavs/dai-copier-gain-module-copier-capture.conf +++ b/tools/topology/topology2/include/pipelines/cavs/dai-copier-gain-module-copier-capture.conf @@ -57,6 +57,16 @@ Class.Pipeline."dai-copier-gain-module-copier-capture" { out_valid_bit_depth 32 } } + module-copier."1" { + num_input_audio_formats 1 + num_output_audio_formats 1 + Object.Base.audio_format.1 { + in_bit_depth 32 + in_valid_bit_depth 32 + out_bit_depth 32 + out_valid_bit_depth 32 + } + } module-copier."2" { num_input_audio_formats 1 @@ -92,6 +102,10 @@ Class.Pipeline."dai-copier-gain-module-copier-capture" { source gain.$index.1 sink module-copier.$index.2 } + route.2 { + source module-copier.$index.1 + sink gain.$index.1 + } } direction "capture" diff --git a/tools/topology/topology2/include/pipelines/cavs/host-copier-gain-src-mixin-playback.conf b/tools/topology/topology2/include/pipelines/cavs/host-copier-gain-src-mixin-playback.conf index 6139b5574bee..292e5b1686c0 100644 --- a/tools/topology/topology2/include/pipelines/cavs/host-copier-gain-src-mixin-playback.conf +++ b/tools/topology/topology2/include/pipelines/cavs/host-copier-gain-src-mixin-playback.conf @@ -16,7 +16,8 @@ # Where N is the unique pipeline ID within the same alsaconf node. # - + + diff --git a/tools/topology/topology2/include/pipelines/cavs/src-gain-mixin-playback.conf b/tools/topology/topology2/include/pipelines/cavs/src-gain-mixin-playback.conf index 1e1e9cca2aa4..d2ae1e08fa0c 100644 --- a/tools/topology/topology2/include/pipelines/cavs/src-gain-mixin-playback.conf +++ b/tools/topology/topology2/include/pipelines/cavs/src-gain-mixin-playback.conf @@ -15,6 +15,8 @@ # # Where N is the unique pipeline ID within the same alsaconf node. # + + diff --git a/tools/topology/topology2/platform/intel/common_definitions.conf b/tools/topology/topology2/platform/intel/common_definitions.conf index f4d49718f559..9c684c7968c0 100644 --- a/tools/topology/topology2/platform/intel/common_definitions.conf +++ b/tools/topology/topology2/platform/intel/common_definitions.conf @@ -58,6 +58,7 @@ Define { SPI_INPUT_CLASS 26 # SPI Input (DSP <-) DEEPBUFFER_FW_DMA_MS 100 # 100 ms copier dma size + DEEPBUFFER_D0I3_COMPATIBLE false # Deep buffer PCM is not D0I3 compatible SSP_BLOB_VERSION_1_0 0x100 SSP_BLOB_VERSION_1_5 0x105 diff --git a/tools/topology/topology2/platform/intel/deep-buffer.conf b/tools/topology/topology2/platform/intel/deep-buffer.conf index 7bf828638906..f66b5d62e39f 100644 --- a/tools/topology/topology2/platform/intel/deep-buffer.conf +++ b/tools/topology/topology2/platform/intel/deep-buffer.conf @@ -19,7 +19,7 @@ Object.PCM.pcm [ name $DEEP_BUFFER_PCM_NAME id $DEEP_BUFFER_PCM_ID direction playback - playback_compatible_d0i3 true + playback_compatible_d0i3 $DEEPBUFFER_D0I3_COMPATIBLE Object.Base.fe_dai.1 { name "DeepBuffer" diff --git a/tools/topology/topology2/platform/intel/dmic-default.conf b/tools/topology/topology2/platform/intel/dmic-default.conf index d5bfb7d11f1e..f23f930b3cf1 100644 --- a/tools/topology/topology2/platform/intel/dmic-default.conf +++ b/tools/topology/topology2/platform/intel/dmic-default.conf @@ -33,5 +33,5 @@ Define { WOV_UUID '1f:d5:a8:eb:27:78:b5:47:82:ee:de:6e:77:43:af:67' WOV_CPC '360000' DMIC1_PCM_CAPS 'Passthrough Capture 18' - + DMIC_CORE_ID 0 } diff --git a/tools/topology/topology2/platform/intel/dmic-generic.conf b/tools/topology/topology2/platform/intel/dmic-generic.conf index 179ad75422ee..5d7747d360c1 100644 --- a/tools/topology/topology2/platform/intel/dmic-generic.conf +++ b/tools/topology/topology2/platform/intel/dmic-generic.conf @@ -73,6 +73,7 @@ Object.Pipeline.gain-capture [ Object.Widget.copier.1 { stream_name $DMIC0_PCM_CAPS + core_id $DMIC_CORE_ID num_input_audio_formats 2 num_output_audio_formats 2 Object.Base.audio_format.1 { @@ -95,6 +96,7 @@ Object.Pipeline.gain-capture [ } } Object.Widget.gain.1 { + core_id $DMIC_CORE_ID num_input_audio_formats 2 num_output_audio_formats 2 Object.Base.audio_format.1 { @@ -121,6 +123,7 @@ Object.Pipeline.gain-capture [ } Object.Widget.module-copier."2" { + core_id $DMIC_CORE_ID num_input_audio_formats 2 num_output_audio_formats 2 Object.Base.audio_format.1 { @@ -142,6 +145,9 @@ Object.Pipeline.gain-capture [ out_ch_map $CHANNEL_MAP_3_POINT_1 } } + Object.Widget.pipeline."1" { + core $DMIC_CORE_ID + } } ] @@ -150,6 +156,7 @@ Object.Pipeline.dai-copier-eqiir-module-copier-capture [ index $DMIC0_DAI_PIPELINE_ID Object.Widget.copier.1 { + core_id $DMIC_CORE_ID dai_index 0 dai_type "DMIC" copier_type "DMIC" @@ -179,6 +186,7 @@ Object.Pipeline.dai-copier-eqiir-module-copier-capture [ } Object.Widget.module-copier."2" { + core_id $DMIC_CORE_ID stream_name $DMIC0_NAME num_input_audio_formats 2 num_output_audio_formats 2 @@ -203,6 +211,7 @@ Object.Pipeline.dai-copier-eqiir-module-copier-capture [ } Object.Widget.eqiir.1 { + core_id $DMIC_CORE_ID num_input_audio_formats 2 num_output_audio_formats 2 Object.Base.audio_format.1 { @@ -223,6 +232,9 @@ Object.Pipeline.dai-copier-eqiir-module-copier-capture [ name 'DMIC0 capture Iir Eq' } } + Object.Widget.pipeline."1" { + core $DMIC_CORE_ID + } } ] diff --git a/tools/topology/topology2/platform/intel/sdw-jack-generic.conf b/tools/topology/topology2/platform/intel/sdw-jack-generic.conf new file mode 100644 index 000000000000..f58267dfb74e --- /dev/null +++ b/tools/topology/topology2/platform/intel/sdw-jack-generic.conf @@ -0,0 +1,206 @@ +# include deep buffer config if buffer size is in 1 - 1000 ms. +IncludeByKey.DEEPBUFFER_FW_DMA_MS{ + "[1-1000]" "platform/intel/deep-buffer.conf" +} + +# +# 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 48000 + 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 48000 + channels 2 + + Object.Base.hw_config.1 { + id 0 + name "ALH3" + } + } +] + +# +# Pipeline definitions +# + +Object.Pipeline { + host-copier-gain-mixin-playback [ + { + index 0 + + Object.Widget.copier.1 { + stream_name "volume playback 0" + } + Object.Widget.gain.1 { + Object.Control.mixer.1 { + name '1 Playback Volume 0' + } + } + } + ] + + mixout-gain-dai-copier-playback [ + { + index 1 + + Object.Widget.copier.1 { + stream_name $SDW_JACK_OUT_STREAM + dai_type "ALH" + copier_type "ALH" + node_type $ALH_LINK_OUTPUT_CLASS + } + Object.Widget.gain.1 { + Object.Control.mixer.1 { + name '2 Main Playback Volume' + } + } + } + ] + + host-gateway-capture [ + { + index 10 + + Object.Widget.copier.1.stream_name "Passthrough Capture 0" + Object.Widget.copier.1.num_input_audio_formats 9 + Object.Widget.copier.1.num_output_audio_formats 9 + Object.Widget.copier.1.Object.Base.audio_format.1 { + # 32/32 -> 16/16 bits conversion is done here + in_bit_depth 32 + in_valid_bit_depth 32 + } + # 32-bit 48KHz 1ch + Object.Widget.copier.1.Object.Base.input_audio_format.7 { + in_channels 1 + in_bit_depth 32 + in_valid_bit_depth 32 + in_ch_cfg $CHANNEL_CONFIG_MONO + in_ch_map $CHANNEL_MAP_MONO + } + Object.Widget.copier.1.Object.Base.output_audio_format.7 { + out_bit_depth 32 + out_valid_bit_depth 32 + out_channels 1 + out_ch_cfg $CHANNEL_CONFIG_MONO + out_ch_map $CHANNEL_MAP_MONO + } + # 24-bit 48KHz 1ch + Object.Widget.copier.1.Object.Base.input_audio_format.8 { + in_channels 1 + in_bit_depth 32 + in_valid_bit_depth 32 + in_ch_cfg $CHANNEL_CONFIG_MONO + in_ch_map $CHANNEL_MAP_MONO +} + Object.Widget.copier.1.Object.Base.output_audio_format.8 { + out_bit_depth 32 + out_valid_bit_depth 24 + out_channels 1 + out_ch_cfg $CHANNEL_CONFIG_MONO + out_ch_map $CHANNEL_MAP_MONO + } + # 16-bit 48KHz 1ch + Object.Widget.copier.1.Object.Base.input_audio_format.9 { + in_channels 1 + in_bit_depth 32 + in_valid_bit_depth 32 + in_ch_cfg $CHANNEL_CONFIG_MONO + in_ch_map $CHANNEL_MAP_MONO + } + Object.Widget.copier.1.Object.Base.output_audio_format.9 { + out_channels 1 + out_ch_cfg $CHANNEL_CONFIG_MONO + out_ch_map $CHANNEL_MAP_MONO + } + } + ] + + highpass-capture-be [ + { + direction "capture" + index 11 + copier_type "ALH" + + Object.Widget.copier.1 { + stream_name $SDW_JACK_IN_STREAM + dai_type "ALH" + copier_type "ALH" + type "dai_out" + node_type $ALH_LINK_INPUT_CLASS + } + Object.Widget.eqiir.1 { + Object.Control.bytes."1" { + name '4 Main capture Iir Eq' + } + } + } + ] +} + +Object.PCM.pcm [ + { + name "Jack out" + id 0 + direction "playback" + Object.Base.fe_dai.1 { + name "Jack out" + } + + Object.PCM.pcm_caps.1 { + name "volume playback 0" + formats 'S16_LE,S24_LE,S32_LE' + } + } + { + name "Jack in" + id 1 + direction "capture" + Object.Base.fe_dai.1 { + name "Jack in" + } + + Object.PCM.pcm_caps.1 { + name "Passthrough Capture 0" + formats 'S16_LE,S24_LE,S32_LE' + channels_min $SDW_JACK_CAPTURE_CH + channels_max $SDW_JACK_CAPTURE_CH + } + } +] + +Object.Base.route [ + { + source "gain.1.1" + sink "copier.ALH.1.1" + } + { + source "mixin.0.1" + sink "mixout.1.1" + } + { + source "copier.ALH.11.1" + sink "eqiir.11.1" + } + { + source "eqiir.11.1" + sink "copier.host.10.1" + } +] diff --git a/tools/topology/topology2/sof-ace-tplg/tplg-targets.cmake b/tools/topology/topology2/sof-ace-tplg/tplg-targets.cmake index f3267f84ee01..092a8fe839a7 100644 --- a/tools/topology/topology2/sof-ace-tplg/tplg-targets.cmake +++ b/tools/topology/topology2/sof-ace-tplg/tplg-targets.cmake @@ -3,31 +3,48 @@ # Array of "input-file-name;output-file-name;comma separated pre-processor variables" set(TPLGS # HDMI only topology with passthrough pipelines -"sof-hda-generic\;sof-hda-generic-idisp\;USE_CHAIN_DMA=true,DEEPBUFFER_FW_DMA_MS=100" +"sof-hda-generic\;sof-hda-generic-idisp\;USE_CHAIN_DMA=true,DEEPBUFFER_FW_DMA_MS=100,\ +DEEPBUFFER_D0I3_COMPATIBLE=true" # HDA topology with mixer-based pipelines for HDA and passthrough pipelines for HDMI -"sof-hda-generic\;sof-hda-generic\;HDA_CONFIG=mix,USE_CHAIN_DMA=true,DEEPBUFFER_FW_DMA_MS=100" +"sof-hda-generic\;sof-hda-generic\;HDA_CONFIG=mix,USE_CHAIN_DMA=true,DEEPBUFFER_FW_DMA_MS=100,\ +DEEPBUFFER_D0I3_COMPATIBLE=true" # If the alsatplg plugins for NHLT are not available, the NHLT blobs will not be added to the # topologies below. "sof-hda-generic\;sof-hda-generic-4ch\;PLATFORM=mtl,\ 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-4ch.bin,USE_CHAIN_DMA=true,DEEPBUFFER_FW_DMA_MS=100" +NHLT_BIN=nhlt-sof-hda-generic-4ch.bin,USE_CHAIN_DMA=true,DEEPBUFFER_FW_DMA_MS=100,\ +DEEPBUFFER_D0I3_COMPATIBLE=true" "sof-hda-generic\;sof-hda-generic-2ch\;PLATFORM=mtl,\ HDA_CONFIG=mix,NUM_DMICS=2,PREPROCESS_PLUGINS=nhlt,NHLT_BIN=nhlt-sof-hda-generic-2ch.bin,\ -USE_CHAIN_DMA=true,DEEPBUFFER_FW_DMA_MS=100" +USE_CHAIN_DMA=true,DEEPBUFFER_FW_DMA_MS=100,DEEPBUFFER_D0I3_COMPATIBLE=true" # SDW + DMIC topology with passthrough pipelines # We will change NUM_HDMIS to 3 once HDMI is enabled on MTL RVP -"cavs-sdw\;sof-mtl-rt711-4ch\;PLATFORM=mtl,NUM_DMICS=4,DMIC0_ID=2,DMIC1_ID=3,NUM_HDMIS=0,\ -PREPROCESS_PLUGINS=nhlt,NHLT_BIN=nhlt-sof-mtl-rt711-4ch.bin,DEEPBUFFER_FW_DMA_MS=100" +"cavs-sdw\;sof-mtl-rt711-4ch\;PLATFORM=mtl,NUM_DMICS=4,PDM1_MIC_A_ENABLE=1,PDM1_MIC_B_ENABLE=1,\ +DMIC0_ID=2,DMIC1_ID=3,NUM_HDMIS=0,PREPROCESS_PLUGINS=nhlt,NHLT_BIN=nhlt-sof-mtl-rt711-4ch.bin,\ +DEEPBUFFER_FW_DMA_MS=100,DEEPBUFFER_D0I3_COMPATIBLE=true" + +"cavs-sdw\;sof-mtl-rt711-l0-rt1316-l23-rt714-l1\;PLATFORM=mtl,NUM_SDW_AMP_LINKS=2,SDW_DMIC=1,\ +NUM_HDMIS=0,SDW_SPK_STREAM=SDW2-Playback,SDW_SPK_IN_STREAM=SDW2-Capture,SDW_DMIC_STREAM=SDW1-Capture" + +# Below topologies are used on Chromebooks "cavs-rt5682\;sof-mtl-max98357a-rt5682\;PLATFORM=mtl,NUM_DMICS=4,PDM1_MIC_A_ENABLE=1,\ PDM1_MIC_B_ENABLE=1,DMIC0_PCM_ID=99,PREPROCESS_PLUGINS=nhlt,\ NHLT_BIN=nhlt-sof-mtl-max98357a-rt5682.bin,DEEPBUFFER_FW_DMA_MS=10,INCLUDE_ECHO_REF=true,\ -BT_NAME=SSP2-BT,BT_ID=8,BT_PCM_NAME=Bluetooth,USE_CHAIN_DMA=true" +BT_NAME=SSP2-BT,BT_ID=8,BT_PCM_NAME=Bluetooth,USE_CHAIN_DMA=true,DEEPBUFFER_D0I3_COMPATIBLE=true" "cavs-rt5682\;sof-mtl-max98357a-rt5682-ssp2-ssp0\;PLATFORM=mtl,NUM_DMICS=4,PDM1_MIC_A_ENABLE=1,\ PDM1_MIC_B_ENABLE=1,DMIC0_PCM_ID=99,PREPROCESS_PLUGINS=nhlt,\ NHLT_BIN=nhlt-sof-mtl-max98357a-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,\ -BT_NAME=SSP1-BT,BT_INDEX=1,BT_ID=8,BT_PCM_NAME=Bluetooth,INCLUDE_ECHO_REF=true,USE_CHAIN_DMA=true" +BT_NAME=SSP1-BT,BT_INDEX=1,BT_ID=8,BT_PCM_NAME=Bluetooth,INCLUDE_ECHO_REF=true,USE_CHAIN_DMA=true,\ +DEEPBUFFER_D0I3_COMPATIBLE=true" + +# SDW + DMIC + HDMI +"cavs-sdw\;sof-mtl-sdw-cs42l42-l0-max98363-l2\;PLATFORM=mtl,NUM_DMICS=4,PDM1_MIC_A_ENABLE=1,\ +PDM1_MIC_B_ENABLE=1,DMIC0_ID=3,DMIC1_ID=4,USE_CHAIN_DMA=true,\ +PREPROCESS_PLUGINS=nhlt,NHLT_BIN=nhlt-sof-mtl-sdw-cs42l42-l0-max98363-l2.bin,\ +NUM_SDW_AMP_LINKS=1,SDW_SPK_STREAM=SDW2-Playback,SDW_AMP_FEEDBACK=false,\ +SDW_JACK_CAPTURE_CH=1,DEEPBUFFER_FW_DMA_MS=100,DEEPBUFFER_D0I3_COMPATIBLE=true" ) diff --git a/west.yml b/west.yml index 5aa993135b7e..4e2bc7a5bc34 100644 --- a/west.yml +++ b/west.yml @@ -34,7 +34,7 @@ manifest: - name: rimage repo-path: rimage path: sof/rimage - revision: ab0429fdbe563ef6abe499c69b2483e96c4762d0 + revision: a0b0187ce1e945869fd5343097282411b7118654 - name: tomlc99 repo-path: tomlc99 @@ -43,8 +43,8 @@ manifest: - name: zephyr repo-path: zephyr - revision: e59e65dc75e4fc7c359712cd7ad7387c0323eb75 - remote: zephyrproject + revision: c31e683295ebd77789f6b4e95c68b073b53b0112 + remote: thesofproject # Import some projects listed in zephyr/west.yml@revision # diff --git a/xtos/include/sof/lib/dma.h b/xtos/include/sof/lib/dma.h index bd207ce42025..30fa889699bb 100644 --- a/xtos/include/sof/lib/dma.h +++ b/xtos/include/sof/lib/dma.h @@ -532,6 +532,15 @@ int dma_buffer_copy_from(struct comp_buffer __sparse_cache *source, struct comp_buffer __sparse_cache *sink, dma_process_func process, uint32_t source_bytes); +/* + * Used when copying DMA buffer bytes into multiple sink buffers, one at a time using the provided + * conversion function. DMA buffer consume should be performed after the data has been copied + * to all sinks. + */ +int dma_buffer_copy_from_no_consume(struct comp_buffer __sparse_cache *source, + struct comp_buffer __sparse_cache *sink, + dma_process_func process, uint32_t source_bytes); + /* copies data to DMA buffer using provided processing function */ int dma_buffer_copy_to(struct comp_buffer __sparse_cache *source, struct comp_buffer __sparse_cache *sink, diff --git a/zephyr/CMakeLists.txt b/zephyr/CMakeLists.txt index 8e8ec41bde37..4114371e8d61 100644 --- a/zephyr/CMakeLists.txt +++ b/zephyr/CMakeLists.txt @@ -539,6 +539,7 @@ zephyr_library_sources_ifdef(CONFIG_LIBRARY_MANAGER zephyr_include_directories_ifdef(CONFIG_INTEL_MODULES ${SOF_SRC_PATH}/include/sof/audio/module_adapter/iadk/ + ${SOF_SRC_PATH}/include/sof/audio/module_adapter/library/ ) zephyr_library_sources_ifdef(CONFIG_INTEL_MODULES @@ -547,6 +548,7 @@ zephyr_library_sources_ifdef(CONFIG_INTEL_MODULES ${SOF_AUDIO_PATH}/module_adapter/iadk/module_initial_settings_concrete.cpp ${SOF_AUDIO_PATH}/module_adapter/iadk/iadk_module_adapter.cpp ${SOF_AUDIO_PATH}/module_adapter/iadk/system_agent.cpp + ${SOF_AUDIO_PATH}/module_adapter/library/native_system_agent.c ) if (CONFIG_COMP_MODULE_ADAPTER) @@ -635,9 +637,15 @@ zephyr_library_sources_ifdef(CONFIG_COMP_RTNR ${SOF_AUDIO_PATH}/rtnr/rtnr.c ) -zephyr_library_sources_ifdef(CONFIG_SAMPLE_SMART_AMP - ${SOF_SAMPLES_PATH}/audio/smart_amp_test.c -) +if(CONFIG_IPC_MAJOR_3) + zephyr_library_sources_ifdef(CONFIG_SAMPLE_SMART_AMP + ${SOF_SAMPLES_PATH}/audio/smart_amp_test_ipc3.c + ) +elseif(CONFIG_IPC_MAJOR_4) + zephyr_library_sources_ifdef(CONFIG_SAMPLE_SMART_AMP + ${SOF_SAMPLES_PATH}/audio/smart_amp_test_ipc4.c + ) +endif() zephyr_library_sources_ifdef(CONFIG_COMP_TDFB ${SOF_AUDIO_PATH}/tdfb/tdfb.c @@ -702,6 +710,29 @@ zephyr_library_sources_ifdef(CONFIG_DW_DMA ${SOF_DRIVERS_PATH}/dw/dma.c ) +if(CONFIG_COMP_GOOGLE_RTC_AUDIO_PROCESSING) + zephyr_library_sources(${SOF_AUDIO_PATH}/google/google_rtc_audio_processing.c) + zephyr_library_sources_ifdef(CONFIG_GOOGLE_RTC_AUDIO_PROCESSING_MOCK + ${SOF_AUDIO_PATH}/google/google_rtc_audio_processing_mock.c) + zephyr_include_directories(../third_party/include) + target_link_directories(SOF INTERFACE ../third_party/lib) + target_link_libraries(SOF INTERFACE google_rtc_audio_processing) + + # This isn't quite right, Zephyr should be providing a C++ + # linkage environment for the toolchain, but the xcc/xt-clang + # integration isn't yet. Note we need to specify libc/libm + # because the Cadence C++ standard library is tightly coupled + # to their own libc (which is just a newlib). And I have NO + # idea why libgcc isn't on the link currently, that's a Zephyr + # mistake and I guess I'm surprised nothing else has broken. + # Needs to be cleaned up Zephyr-side. + target_link_libraries(SOF INTERFACE c++) + target_link_libraries(SOF INTERFACE c++abi) + target_link_libraries(SOF INTERFACE m) + target_link_libraries(SOF INTERFACE c) + target_link_libraries(SOF INTERFACE gcc) +endif() + zephyr_library_link_libraries(SOF) target_link_libraries(SOF INTERFACE zephyr_interface) diff --git a/zephyr/include/rtos/idc.h b/zephyr/include/rtos/idc.h index e5e8f1f8a072..8734687aebea 100644 --- a/zephyr/include/rtos/idc.h +++ b/zephyr/include/rtos/idc.h @@ -102,6 +102,10 @@ #define IDC_HEADER_TO_AMS_SLOT_MASK(x) (x & 0xFFFF) +/** \brief IDC pipeline set state message. */ +#define IDC_MSG_PPL_STATE IDC_TYPE(0xC) +#define IDC_MSG_PPL_STATE_EXT(x) IDC_EXTENSION(x) + /** \brief IDC_MSG_SECONDARY_CORE_CRASHED header fields. */ #define IDC_SCC_CORE_SHIFT 0 #define IDC_SCC_CORE_MASK 0xff diff --git a/zephyr/include/sof/lib/cpu.h b/zephyr/include/sof/lib/cpu.h index 41328ee9ebe4..81a8840742b8 100644 --- a/zephyr/include/sof/lib/cpu.h +++ b/zephyr/include/sof/lib/cpu.h @@ -20,10 +20,16 @@ #include -//#include - #include +#if CONFIG_PM + +#include + +void cpu_notify_state_exit(enum pm_state state); + +#endif /* CONFIG_PM */ + /* let the compiler optimise when in single core mode */ #if CONFIG_MULTICORE && CONFIG_SMP diff --git a/zephyr/lib/alloc.c b/zephyr/lib/alloc.c index 8b0dad31da38..35bd322a4e22 100644 --- a/zephyr/lib/alloc.c +++ b/zephyr/lib/alloc.c @@ -369,4 +369,15 @@ static int heap_init(void) return 0; } +/* This is a weak stub for the Cadence libc's allocator (which is just + * a newlib build). It's traditionally been provided like this in SOF + * for the benefit of C++ code where the standard library needs to + * link to a working malloc() even if it will never call it. + */ +struct _reent; +__weak void *_sbrk_r(struct _reent *ptr, ptrdiff_t incr) +{ + k_panic(); +} + SYS_INIT(heap_init, PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_OBJECTS); diff --git a/zephyr/lib/cpu.c b/zephyr/lib/cpu.c index 882a7c8baf47..448697bfd6b8 100644 --- a/zephyr/lib/cpu.c +++ b/zephyr/lib/cpu.c @@ -59,12 +59,26 @@ static FUNC_NORETURN void secondary_init(void *arg) #if CONFIG_ZEPHYR_NATIVE_DRIVERS #include #include -#include LOG_MODULE_DECLARE(zephyr, CONFIG_SOF_LOG_LEVEL); extern struct tr_ctx zephyr_tr; +/* notifier called after every power state transition */ +void cpu_notify_state_exit(enum pm_state state) +{ + if (state == PM_STATE_SOFT_OFF) { +#if CONFIG_MULTICORE + if (!cpu_is_primary(arch_proc_id())) { + /* Notifying primary core that secondary core successfully exit the D3 + * state and is back in the Idle thread. + */ + atomic_set(&ready_flag, 1); + } +#endif + } +} + int cpu_enable_core(int id) { /* only called from single core, no RMW lock */ @@ -79,7 +93,13 @@ int cpu_enable_core(int id) return 0; #if ZEPHYR_VERSION(3, 0, 99) <= ZEPHYR_VERSION_CODE - z_init_cpu(id); + /* During kernel initialization, the next pm state is set to ACTIVE. By checking this + * value, we determine if this is the first core boot, if not, we need to skip idle thread + * initialization. By reinitializing the idle thread, we would overwrite the kernel structs + * and the idle thread stack. + */ + if (pm_state_next_get(id)->state == PM_STATE_ACTIVE) + z_init_cpu(id); #endif atomic_clear(&start_flag); diff --git a/zephyr/lib/pm_runtime.c b/zephyr/lib/pm_runtime.c index ab8adea565c0..36ff254b9d9d 100644 --- a/zephyr/lib/pm_runtime.c +++ b/zephyr/lib/pm_runtime.c @@ -9,6 +9,12 @@ #include #include #include +#include +#include +#include + +#include +#include #include LOG_MODULE_REGISTER(power, CONFIG_SOF_LOG_LEVEL); @@ -19,6 +25,11 @@ DECLARE_SOF_UUID("power", power_uuid, 0x76cc9773, 0x440c, 0x4df9, DECLARE_TR_CTX(power_tr, SOF_UUID(power_uuid), LOG_LEVEL_INFO); +/** \brief ACE specific runtime power management data. */ +struct ace_pm_runtime_data { + int host_dma_l1_sref; /**< ref counter for Host DMA accesses */ +}; + #if defined(CONFIG_PM_POLICY_CUSTOM) const struct pm_state_info *pm_policy_next_state(uint8_t cpu, int32_t ticks) { @@ -93,16 +104,78 @@ void platform_pm_runtime_disable(uint32_t context, uint32_t index) } } +/** + * \brief Registers Host DMA usage that should not trigger + * transition to L0 via forced L1 exit. + */ +static void ace_pm_runtime_host_dma_l1_get(void) +{ + struct pm_runtime_data *prd = pm_runtime_data_get(); + struct ace_pm_runtime_data *pprd = prd->platform_data; + k_spinlock_key_t key; + + key = k_spin_lock(&prd->lock); + + pprd->host_dma_l1_sref++; + + k_spin_unlock(&prd->lock, key); +} + +/** + * \brief Releases Host DMA usage preventing L1 exit. If this + * the last user, forced L1 exit is performed. + */ +static inline void ace_pm_runtime_host_dma_l1_put(void) +{ + struct pm_runtime_data *prd = pm_runtime_data_get(); + struct ace_pm_runtime_data *pprd = prd->platform_data; + k_spinlock_key_t key; + + key = k_spin_lock(&prd->lock); + + if (pprd->host_dma_l1_sref) { + ACE_DfPMCCH.svcfg |= ADSP_FORCE_DECOUPLED_HDMA_L1_EXIT_BIT; + wait_delay(ADSP_FORCE_L1_EXIT_TIME); + ACE_DfPMCCH.svcfg &= ~(ADSP_FORCE_DECOUPLED_HDMA_L1_EXIT_BIT); + } + pprd->host_dma_l1_sref = 0; + + k_spin_unlock(&prd->lock, key); +} + void platform_pm_runtime_init(struct pm_runtime_data *prd) -{ } +{ + struct ace_pm_runtime_data *pprd; + + pprd = rzalloc(SOF_MEM_ZONE_SYS_SHARED, 0, SOF_MEM_CAPS_RAM, sizeof(*pprd)); + prd->platform_data = pprd; +} void platform_pm_runtime_get(enum pm_runtime_context context, uint32_t index, uint32_t flags) -{ } +{ + /* Action based on context */ + switch (context) { + case PM_RUNTIME_HOST_DMA_L1: + ace_pm_runtime_host_dma_l1_get(); + break; + default: + break; + } +} void platform_pm_runtime_put(enum pm_runtime_context context, uint32_t index, uint32_t flags) -{ } +{ + /* Action based on context */ + switch (context) { + case PM_RUNTIME_HOST_DMA_L1: + ace_pm_runtime_host_dma_l1_put(); + break; + default: + break; + } +} void platform_pm_runtime_prepare_d0ix_en(uint32_t index) { }