From 1c0dfaa06e7481458be96a7812479dd8f816e54c Mon Sep 17 00:00:00 2001 From: Kai Vehmanen Date: Wed, 16 Aug 2023 19:16:01 +0300 Subject: [PATCH] drivers: Intel: remove Intel XTOS drivers Now that Intel cAVS2.5 has been migrated to use native Zephyr drivers, we have no need to keep the Intel specific XTOS drivers in the tree anymore. Adjust board configuration files to not refer to removed Kconfig options. Signed-off-by: Kai Vehmanen --- app/boards/intel_adsp_ace15_mtpm.conf | 5 - app/boards/intel_adsp_ace20_lnl.conf | 5 - app/boards/intel_adsp_cavs25.conf | 4 - app/boards/intel_adsp_cavs25_tgph.conf | 3 - app/overlays/tgl/ipc4_overlay.conf | 1 - src/drivers/CMakeLists.txt | 4 - src/drivers/Kconfig | 2 - src/drivers/intel/CMakeLists.txt | 21 - src/drivers/intel/Kconfig | 211 ---- src/drivers/intel/alh.c | 179 ---- src/drivers/intel/cavs/CMakeLists.txt | 12 - src/drivers/intel/cavs/idc.c | 325 ------- src/drivers/intel/cavs/interrupt.c | 312 ------ src/drivers/intel/cavs/ipc.c | 316 ------ src/drivers/intel/cavs/timer.c | 254 ----- src/drivers/intel/cavs/timestamp.c | 270 ------ src/drivers/intel/dmic/CMakeLists.txt | 10 - src/drivers/intel/dmic/dmic.c | 646 ------------- src/drivers/intel/dmic/dmic_computed.c | 1002 ------------------- src/drivers/intel/dmic/dmic_nhlt.c | 584 ----------- src/drivers/intel/hda/CMakeLists.txt | 3 - src/drivers/intel/hda/hda-dma.c | 1088 --------------------- src/drivers/intel/hda/hda.c | 131 --- src/drivers/intel/pmc-ipc.c | 149 --- src/drivers/intel/ssp/CMakeLists.txt | 7 - src/drivers/intel/ssp/mn.c | 627 ------------ src/drivers/intel/ssp/ssp.c | 1229 ------------------------ 27 files changed, 7400 deletions(-) delete mode 100644 src/drivers/intel/CMakeLists.txt delete mode 100644 src/drivers/intel/Kconfig delete mode 100644 src/drivers/intel/alh.c delete mode 100644 src/drivers/intel/cavs/CMakeLists.txt delete mode 100644 src/drivers/intel/cavs/idc.c delete mode 100644 src/drivers/intel/cavs/interrupt.c delete mode 100644 src/drivers/intel/cavs/ipc.c delete mode 100644 src/drivers/intel/cavs/timer.c delete mode 100644 src/drivers/intel/cavs/timestamp.c delete mode 100644 src/drivers/intel/dmic/CMakeLists.txt delete mode 100644 src/drivers/intel/dmic/dmic.c delete mode 100644 src/drivers/intel/dmic/dmic_computed.c delete mode 100644 src/drivers/intel/dmic/dmic_nhlt.c delete mode 100644 src/drivers/intel/hda/CMakeLists.txt delete mode 100644 src/drivers/intel/hda/hda-dma.c delete mode 100644 src/drivers/intel/hda/hda.c delete mode 100644 src/drivers/intel/pmc-ipc.c delete mode 100644 src/drivers/intel/ssp/CMakeLists.txt delete mode 100644 src/drivers/intel/ssp/mn.c delete mode 100644 src/drivers/intel/ssp/ssp.c diff --git a/app/boards/intel_adsp_ace15_mtpm.conf b/app/boards/intel_adsp_ace15_mtpm.conf index 17a88291436e..e96a566c9826 100644 --- a/app/boards/intel_adsp_ace15_mtpm.conf +++ b/app/boards/intel_adsp_ace15_mtpm.conf @@ -1,11 +1,6 @@ CONFIG_METEORLAKE=y CONFIG_IPC_MAJOR_4=y -# turn off SOF drivers -CONFIG_INTEL_DMIC=n -CONFIG_INTEL_DMIC_NHLT=n -CONFIG_INTEL_SSP=n -CONFIG_INTEL_ALH=n CONFIG_COMP_SRC=y CONFIG_COMP_SRC_IPC4_FULL_MATRIX=y CONFIG_COMP_DRC=y diff --git a/app/boards/intel_adsp_ace20_lnl.conf b/app/boards/intel_adsp_ace20_lnl.conf index 8ee046402e04..b8affcf55f61 100644 --- a/app/boards/intel_adsp_ace20_lnl.conf +++ b/app/boards/intel_adsp_ace20_lnl.conf @@ -1,11 +1,6 @@ CONFIG_LUNARLAKE=y CONFIG_IPC_MAJOR_4=y -# turn off SOF drivers -CONFIG_INTEL_DMIC=n -CONFIG_INTEL_DMIC_NHLT=n -CONFIG_INTEL_SSP=n -CONFIG_INTEL_ALH=n CONFIG_COMP_SRC=y CONFIG_COMP_SRC_IPC4_FULL_MATRIX=y diff --git a/app/boards/intel_adsp_cavs25.conf b/app/boards/intel_adsp_cavs25.conf index 7dc84e97b636..033f35f6925c 100644 --- a/app/boards/intel_adsp_cavs25.conf +++ b/app/boards/intel_adsp_cavs25.conf @@ -1,10 +1,6 @@ CONFIG_TIGERLAKE=y -CONFIG_INTEL_DMIC=y CONFIG_COMP_CHAIN_DMA=y -CONFIG_DMIC_HW_IOCLK=38400000 -CONFIG_INTEL_SSP=y CONFIG_AMS=y -CONFIG_INTEL_ALH=y CONFIG_LP_MEMORY_BANKS=1 CONFIG_HP_MEMORY_BANKS=30 CONFIG_RIMAGE_SIGNING_SCHEMA="tgl" diff --git a/app/boards/intel_adsp_cavs25_tgph.conf b/app/boards/intel_adsp_cavs25_tgph.conf index 4cda521f0272..05d19d8f6618 100644 --- a/app/boards/intel_adsp_cavs25_tgph.conf +++ b/app/boards/intel_adsp_cavs25_tgph.conf @@ -1,9 +1,6 @@ CONFIG_TIGERLAKE=y -CONFIG_INTEL_DMIC=y CONFIG_COMP_CHAIN_DMA=y CONFIG_DMIC_HW_IOCLK=38400000 -CONFIG_INTEL_SSP=y -CONFIG_INTEL_ALH=y CONFIG_LP_MEMORY_BANKS=1 CONFIG_HP_MEMORY_BANKS=30 CONFIG_RIMAGE_SIGNING_SCHEMA="tgl-h" diff --git a/app/overlays/tgl/ipc4_overlay.conf b/app/overlays/tgl/ipc4_overlay.conf index 546407ce5340..fe0460bcf5b8 100644 --- a/app/overlays/tgl/ipc4_overlay.conf +++ b/app/overlays/tgl/ipc4_overlay.conf @@ -1,5 +1,4 @@ CONFIG_IPC_MAJOR_4=y -CONFIG_INTEL_DMIC_NHLT=y CONFIG_RIMAGE_SIGNING_SCHEMA="tgl-cavs" CONFIG_PCM_CONVERTER_FORMAT_S16LE=y CONFIG_PCM_CONVERTER_FORMAT_S24LE=y diff --git a/src/drivers/CMakeLists.txt b/src/drivers/CMakeLists.txt index 80267d220df1..dc82ed66a737 100644 --- a/src/drivers/CMakeLists.txt +++ b/src/drivers/CMakeLists.txt @@ -4,10 +4,6 @@ if(CONFIG_IMX) add_subdirectory(imx) endif() -if(CONFIG_INTEL) - add_subdirectory(intel) -endif() - if(CONFIG_AMD) add_subdirectory(amd) endif() diff --git a/src/drivers/Kconfig b/src/drivers/Kconfig index ad3be8d1a564..43fdc40023cd 100644 --- a/src/drivers/Kconfig +++ b/src/drivers/Kconfig @@ -2,8 +2,6 @@ menu "Drivers" -rsource "intel/Kconfig" - rsource "dw/Kconfig" rsource "imx/Kconfig" diff --git a/src/drivers/intel/CMakeLists.txt b/src/drivers/intel/CMakeLists.txt deleted file mode 100644 index ca84ad500a82..000000000000 --- a/src/drivers/intel/CMakeLists.txt +++ /dev/null @@ -1,21 +0,0 @@ -# SPDX-License-Identifier: BSD-3-Clause - -if(CONFIG_CAVS) - add_subdirectory(cavs) -endif() - -if(CONFIG_INTEL_HDA) - add_subdirectory(hda) -endif() - -if(CONFIG_INTEL_SSP) - add_subdirectory(ssp) -endif() - -if(CONFIG_INTEL_ALH) - add_local_sources(sof alh.c) -endif() - -if(CONFIG_INTEL_DMIC) - add_subdirectory(dmic) -endif() diff --git a/src/drivers/intel/Kconfig b/src/drivers/intel/Kconfig deleted file mode 100644 index 311d23f86991..000000000000 --- a/src/drivers/intel/Kconfig +++ /dev/null @@ -1,211 +0,0 @@ -# SPDX-License-Identifier: BSD-3-Clause - -config INTEL_HDA - bool "Intel HDA driver" - depends on CAVS - default n - help - Select this to enable Intel HDA driver. The HDA driver provides - interface for host and HD-Audio data transfers. - -config INTEL_MN - bool - depends on CAVS - default n - help - Select this if the platform supports M/N dividers. - -config INTEL_MCLK - bool - depends on CAVS - default n - help - Select this to enable driver for Intel MCLK and M/N dividers. - -config INTEL_SSP - bool "Intel SSP driver" - depends on CAVS - select INTEL_MCLK - default n - help - Select this to enable Intel Synchronous Serial Port (SSP) driver. - -config INTEL_ALH - bool "Intel ALH driver" - depends on CAVS - default n - help - Select this to enable Intel ALH driver. - The ALH is an intermediary device, which acts as a hub and provides an - abstracted support for numerous sound interfaces (e.g. SoundWire). - -config INTEL_DMIC - bool "Intel DMIC driver" - depends on CAVS - select NUMBERS_NORM - select NUMBERS_VECTOR_FIND - select MATH_DECIBELS - default n - help - Select this to enable Intel DMIC driver. The DMIC driver provides - as DAI the SoC direct attach digital microphones interface. - -if INTEL_DMIC - -config DMIC_HW_IOCLK - int "Set DMIC hw IO clock" - default 0 - help - Hardware specific DMIC IO clock speed defined by each platform. - May be overridden from hardware values for simulation purposes. - -choice - prompt "Driver operation mode" - default INTEL_DMIC_TPLG_PARAMS - help - The driver can support two operation modes. - - A HW registers dump blob that is passed via IPC - - DAI tokens those describe the use case PCM format - and PDM bus and microphone parameters - -config INTEL_DMIC_NHLT - bool "Use NHLT DMIC blob" - help - All registers configuration is retrieved from blob. The - number of channels, sample rate, and PCM format are - defined in the blob and there are no runtime made - configuration choices. - -config INTEL_DMIC_TPLG_PARAMS - bool "Use parameters from topology" - help - All registers confifguration is computed on the fly - based on use case and microphone datasheet parameters - and topology defined PCM format. The parameters are - easy to to customize in the topology. - -endchoice - -if INTEL_DMIC_TPLG_PARAMS - -choice - prompt "FIR decimation coefficients set" - default INTEL_DMIC_FIR_FULL - -config INTEL_DMIC_FIR_FULL - bool "Full set" - select INTEL_DMIC_FIR_DECIMATE_BY_2 - select INTEL_DMIC_FIR_DECIMATE_BY_3 - select INTEL_DMIC_FIR_DECIMATE_BY_4 - select INTEL_DMIC_FIR_DECIMATE_BY_5 - select INTEL_DMIC_FIR_DECIMATE_BY_6 - select INTEL_DMIC_FIR_DECIMATE_BY_8 - select INTEL_DMIC_FIR_DECIMATE_BY_10 - select INTEL_DMIC_FIR_DECIMATE_BY_12 - help - This option adds to previous all currently defined FIR - coefficients sets to support sample rates 8 - 96 kHz with - several microphone clock rates. The tables increase the size - of the driver so this option should not be used in minimal - systems. - -config INTEL_DMIC_FIR_LOW_MEMORY - bool "Small set" - select INTEL_DMIC_FIR_DECIMATE_BY_2 - select INTEL_DMIC_FIR_DECIMATE_BY_6 - help - This option is used to minimize driver footprint but - preserve support for 48 kHz and 16 kHz sample rates - at typical 2.4 MHz microphone clock rate. The option - enables decimation factors 2 and 6. - -config INTEL_DMIC_FIR_CUSTOM - bool "Custom set" - help - This option is used to select each supported decimation - factor. - -endchoice - -menu "Decimation factors" - visible if INTEL_DMIC_FIR_CUSTOM - -config INTEL_DMIC_FIR_DECIMATE_BY_2 - bool "FIR decimate by 2" - default n - help - This includes FIR coefficients to decimate by 2 into the build. It - is commonly used for 48 kHz capture with 2.4 MHz microphone clock. - Decimate by 2 in FIR gives good bandwidth vs. Nyquist and narrow - transition region due to lower FIR order need compared to higher - decimation factors. - -config INTEL_DMIC_FIR_DECIMATE_BY_3 - bool "FIR decimate by 3" - default n - help - This includes FIR coefficients to decimate by 3 into the build. - Decimation by 3 in FIR is useful with microphone clock and sample - rate combinations where a 3 is the lowest factor of the oversampling - ratio. Having this low prime decimation factor for FIR enabled is - useful when decimation by 2 is not possible. - -config INTEL_DMIC_FIR_DECIMATE_BY_4 - bool "FIR decimate by 4" - default n - help - This includes FIR coefficients to decimate by 4 into the build. - Decimation by 4 in FIR is useful in some cases with high microphone - clock rates due to max. decimation factor limit of CIC. In such - cases decimation by 2 may not be usable. - -config INTEL_DMIC_FIR_DECIMATE_BY_5 - bool "FIR decimate by 5" - default n - help - This includes FIR coefficients to decimate by 5 into the build. - Decimation by 5 in FIR is useful with microphone clock and sample - rate combinations where a 5 is the lowest factor of the oversampling - ratio. - -config INTEL_DMIC_FIR_DECIMATE_BY_6 - bool "FIR decimate by 6" - default n - help - This includes FIR coefficients to decimate by 6 into the build. It - is commonly used for 16 kHz capture from secondary FIFO while - primary FIFO is configured for 48 kHz rate. - -config INTEL_DMIC_FIR_DECIMATE_BY_8 - bool "FIR decimate by 8" - default n - help - This includes FIR coefficients to decimate by 8 into the build. - The high FIR decimation factors are needed for high microphone clock - rates due to max. decimation factor limitation of CIC. Also dual - FIFO configurations may need for one FIR decimation such high - decimation factor. - -config INTEL_DMIC_FIR_DECIMATE_BY_10 - bool "FIR decimate by 10" - default n - help - This includes FIR coefficients to decimate by 10 into the build. - The high FIR decimation factors are needed for high microphone clock - rates due to max. decimation factor limitation of CIC. Also dual - FIFO configurations may need for one FIR decimation such high - decimation factor. - -config INTEL_DMIC_FIR_DECIMATE_BY_12 - bool "FIR decimate by 12" - default n - help - This includes FIR coefficients to decimate by 12 into the build. It - is used for 16 kHz capture for secondary FIFO while the primary - FIFO is configured for 96 kHz. - -endmenu # "Decimation factors" - -endif - -endif # INTEL_DMIC diff --git a/src/drivers/intel/alh.c b/src/drivers/intel/alh.c deleted file mode 100644 index 1c16feaade59..000000000000 --- a/src/drivers/intel/alh.c +++ /dev/null @@ -1,179 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -// -// Copyright(c) 2019 Intel Corporation. All rights reserved. -// -// Author: Slawomir Blauciak - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -LOG_MODULE_REGISTER(alh_dai, CONFIG_SOF_LOG_LEVEL); - -/* a8e4218c-e863-4c93-84e7-5c27d2504501 */ -DECLARE_SOF_UUID("alh-dai", alh_uuid, 0xa8e4218c, 0xe863, 0x4c93, - 0x84, 0xe7, 0x5c, 0x27, 0xd2, 0x50, 0x45, 0x01); - -DECLARE_TR_CTX(alh_tr, SOF_UUID(alh_uuid), LOG_LEVEL_INFO); - -static int alh_trigger(struct dai *dai, int cmd, int direction) -{ - dai_info(dai, "alh_trigger() cmd %d", cmd); - - return 0; -} - -static int alh_set_config_tplg(struct dai *dai, struct ipc_config_dai *common_config, - const void *spec_config) -{ - struct alh_pdata *alh = dai_get_drvdata(dai); - const struct sof_ipc_dai_config *config = spec_config; - - dai_info(dai, "alh_set_config_tplg() config->format = 0x%4x", config->format); - - if (config->alh.channels || config->alh.rate) { - alh->params.channels = config->alh.channels; - alh->params.rate = config->alh.rate; - dai_info(dai, "alh_set_config() channels %d rate %d", - config->alh.channels, config->alh.rate); - } - - alh->params.stream_id = config->alh.stream_id; - - return 0; -} - -static int alh_set_config_blob(struct dai *dai, struct ipc_config_dai *common_config, - const void *spec_config) -{ - struct alh_pdata *alh = dai_get_drvdata(dai); - const struct sof_alh_configuration_blob *blob = spec_config; - const struct ipc4_alh_multi_gtw_cfg *alh_cfg = &blob->alh_cfg; - int i; - - dai_info(dai, "alh_set_config_blob()"); - - alh->params.rate = common_config->sampling_frequency; - - for (i = 0; i < alh_cfg->count; i++) { - /* the LSB 8bits are for stream id */ - int alh_id = alh_cfg->mapping[i].alh_id & 0xff; - - if (IPC4_ALH_DAI_INDEX(alh_id) == dai->index) { - alh->params.stream_id = alh_id; - alh->params.channels = popcount(alh_cfg->mapping[i].channel_mask); - break; - } - } - - return 0; -} - -static int alh_set_config(struct dai *dai, struct ipc_config_dai *common_config, - const void *spec_config) -{ - if (!common_config->is_config_blob) - return alh_set_config_tplg(dai, common_config, spec_config); - else - return alh_set_config_blob(dai, common_config, spec_config); -} - -/* get ALH hw params */ -static int alh_get_hw_params(struct dai *dai, - struct sof_ipc_stream_params *params, int dir) -{ - struct alh_pdata *alh = dai_get_drvdata(dai); - - params->rate = alh->params.rate; - params->channels = alh->params.channels; - - /* 0 means variable */ - params->buffer_fmt = 0; - - /* FIFO format is static */ - params->frame_fmt = SOF_IPC_FRAME_S32_LE; - - return 0; -} - -static int alh_probe(struct dai *dai) -{ - struct alh_pdata *alh; - - dai_info(dai, "alh_probe()"); - - if (dai_get_drvdata(dai)) - return -EEXIST; - - alh = rzalloc(SOF_MEM_ZONE_RUNTIME_SHARED, 0, SOF_MEM_CAPS_RAM, sizeof(*alh)); - if (!alh) { - dai_err(dai, "alh_probe() error: alloc failed"); - return -ENOMEM; - } - dai_set_drvdata(dai, alh); - - return 0; -} - -static int alh_remove(struct dai *dai) -{ - dai_info(dai, "alh_remove()"); - - rfree(dai_get_drvdata(dai)); - dai_set_drvdata(dai, NULL); - - return 0; -} - -static int alh_get_handshake(struct dai *dai, int direction, int stream_id) -{ - if (stream_id >= ARRAY_SIZE(alh_handshake_map)) { - dai_err(dai, "alh_get_handshake(): " - "stream_id %d out of range", stream_id); - - return -1; - } - - return alh_handshake_map[stream_id]; -} - -static int alh_get_fifo(struct dai *dai, int direction, int stream_id) -{ - uint32_t offset = direction == SOF_IPC_STREAM_PLAYBACK ? - ALH_TXDA_OFFSET : ALH_RXDA_OFFSET; - - return ALH_BASE + offset + ALH_STREAM_OFFSET * stream_id; -} - -static int alh_get_fifo_depth(struct dai *dai, int direction) -{ - return dai->plat_data.fifo[direction].depth; -} - -const struct dai_driver alh_driver = { - .type = SOF_DAI_INTEL_ALH, - .uid = SOF_UUID(alh_uuid), - .tctx = &alh_tr, - .dma_caps = DMA_CAP_GP_LP | DMA_CAP_GP_HP, - .dma_dev = DMA_DEV_ALH, - .ops = { - .trigger = alh_trigger, - .set_config = alh_set_config, - .get_hw_params = alh_get_hw_params, - .get_handshake = alh_get_handshake, - .get_fifo = alh_get_fifo, - .get_fifo_depth = alh_get_fifo_depth, - .probe = alh_probe, - .remove = alh_remove, - }, -}; diff --git a/src/drivers/intel/cavs/CMakeLists.txt b/src/drivers/intel/cavs/CMakeLists.txt deleted file mode 100644 index 89f0db585eae..000000000000 --- a/src/drivers/intel/cavs/CMakeLists.txt +++ /dev/null @@ -1,12 +0,0 @@ -# SPDX-License-Identifier: BSD-3-Clause - -add_local_sources(sof - interrupt.c - ipc.c - timer.c - timestamp.c -) - -if(CONFIG_MULTICORE) - add_local_sources(sof idc.c) -endif() diff --git a/src/drivers/intel/cavs/idc.c b/src/drivers/intel/cavs/idc.c deleted file mode 100644 index bb433feca9c5..000000000000 --- a/src/drivers/intel/cavs/idc.c +++ /dev/null @@ -1,325 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -// -// Copyright(c) 2018 Intel Corporation. All rights reserved. -// -// Author: Tomasz Lauda - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/** - * \brief Enables IDC interrupts. - * \param[in] target_core Target core id. - * \param[in] source_core Source core id. - */ -void idc_enable_interrupts(int target_core, int source_core) -{ - struct idc *idc = *idc_get(); - - idc_write(IPC_IDCCTL, target_core, - IPC_IDCCTL_IDCTBIE(source_core)); - interrupt_unmask(idc->irq, target_core); -} - -/** - * \brief IDC interrupt handler. - * \param[in,out] arg Pointer to IDC data. - */ -static void idc_irq_handler(void *arg) -{ - struct idc *idc = arg; - int core = cpu_get_id(); - uint32_t idctfc; - uint32_t idctefc; - uint32_t i; - - tr_dbg(&idc_tr, "idc_irq_handler()"); - - for (i = 0; i < CONFIG_CORE_COUNT; i++) { - /* skip current core */ - if (core == i) - continue; - - idctfc = idc_read(IPC_IDCTFC(i), core); - - if (idctfc & IPC_IDCTFC_BUSY) { - tr_info(&idc_tr, "idc_irq_handler(), IPC_IDCTFC_BUSY"); - - /* disable BUSY interrupt */ - idc_write(IPC_IDCCTL, core, 0); - - idc->received_msg.core = i; - idc->received_msg.header = - idctfc & IPC_IDCTFC_MSG_MASK; - - idctefc = idc_read(IPC_IDCTEFC(i), core); - idc->received_msg.extension = - idctefc & IPC_IDCTEFC_MSG_MASK; - - schedule_task(&idc->idc_task, 0, IDC_DEADLINE); - } - } -} - -/** - * \brief Checks IDC registers whether message has been received. - * \param[in] target_core Id of the core receiving the message. - * \return True if message received, false otherwise. - */ -static bool idc_is_received(int target_core) -{ - return idc_read(IPC_IDCIETC(target_core), cpu_get_id()) & - IPC_IDCIETC_DONE; -} - -/** - * \brief Checks core status register. - * \param[in] target_core Id of the core powering up. - * \return True if core powered up, false otherwise. - */ -static bool idc_is_powered_up(int target_core) -{ -#if CONFIG_IPC_MAJOR_4 - return cpu_is_core_enabled(target_core); -#else - return mailbox_sw_reg_read(PLATFORM_TRACEP_SECONDARY_CORE(target_core)) == - TRACE_BOOT_PLATFORM; -#endif -} - -/** - * \brief Checks core status register. - * \param[in] target_core Id of the core powering up. - * \return True if core powered up, false otherwise. - */ -static bool idc_is_powered_down(int target_core) -{ -#if CONFIG_IPC_MAJOR_4 - return !cpu_is_core_enabled(target_core); -#else - return mailbox_sw_reg_read(PLATFORM_TRACEP_SECONDARY_CORE(target_core)) == 0; -#endif -} - -/** - * \brief Sends IDC message. - * \param[in,out] msg Pointer to IDC message. - * \param[in] mode Is message blocking or not. - * \return Error code. - */ -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); - int core = cpu_get_id(); - uint32_t idcietc; - int ret = 0; - - tr_dbg(&idc_tr, "arch_idc_send_msg()"); - - /* clear any previous messages */ - idcietc = idc_read(IPC_IDCIETC(msg->core), core); - if (idcietc & IPC_IDCIETC_DONE) - idc_write(IPC_IDCIETC(msg->core), core, idcietc); - - /* copy payload if available */ - if (msg->payload) { - ret = memcpy_s(payload->data, IDC_MAX_PAYLOAD_SIZE, - msg->payload, msg->size); - assert(!ret); - } - - idc_write(IPC_IDCIETC(msg->core), core, msg->extension); - idc_write(IPC_IDCITC(msg->core), core, msg->header | IPC_IDCITC_BUSY); - - switch (mode) { - case IDC_BLOCKING: - ret = idc_wait_in_blocking_mode(msg->core, idc_is_received); - if (ret < 0) { - tr_err(&idc_tr, "idc_send_msg(), blocking msg 0x%x failed for core %d", - msg->header, msg->core); - return ret; - } - - idc_write(IPC_IDCIETC(msg->core), core, - idc_read(IPC_IDCIETC(msg->core), core) | - IPC_IDCIETC_DONE); - - ret = idc_msg_status_get(msg->core); - break; - - case IDC_POWER_UP: - ret = idc_wait_in_blocking_mode(msg->core, idc_is_powered_up); - if (ret < 0) { -#if CONFIG_IPC_MAJOR_4 - tr_err(&idc_tr, "idc_send_msg(), power up core %d failed", - msg->core); -#else - tr_err(&idc_tr, "idc_send_msg(), power up core %d failed, reason 0x%x", - msg->core, - mailbox_sw_reg_read(PLATFORM_TRACEP_SECONDARY_CORE(msg->core))); -#endif - } - break; - - case IDC_POWER_DOWN: - ret = idc_wait_in_blocking_mode(msg->core, idc_is_powered_down); - if (ret < 0) { -#if CONFIG_IPC_MAJOR_4 - tr_err(&idc_tr, "idc_send_msg(), power down core %d failed", - msg->core); -#else - tr_err(&idc_tr, "idc_send_msg(), power down core %d failed, reason 0x%x", - msg->core, - mailbox_sw_reg_read(PLATFORM_TRACEP_SECONDARY_CORE(msg->core))); -#endif - } - break; - } - - return ret; -} - -/** - * \brief Handles received IDC message. - * \param[in,out] data Pointer to IDC data. - */ -enum task_state idc_do_cmd(void *data) -{ - struct idc *idc = data; - int core = cpu_get_id(); - int initiator = idc->received_msg.core; - - tr_info(&idc_tr, "idc_do_cmd()"); - - idc_cmd(&idc->received_msg); - - /* clear BUSY bit */ - idc_write(IPC_IDCTFC(initiator), core, - idc_read(IPC_IDCTFC(initiator), core) | IPC_IDCTFC_BUSY); - - /* enable BUSY interrupt */ - idc_write(IPC_IDCCTL, core, idc->busy_bit_mask); - - return SOF_TASK_STATE_COMPLETED; -} - -/** - * \brief Returns BUSY interrupt mask based on core id. - * \param[in] core Core id. - * \return BUSY interrupt mask. - */ -static uint32_t idc_get_busy_bit_mask(int core) -{ - uint32_t busy_mask = 0; - int i; - - for (i = 0; i < CONFIG_CORE_COUNT; i++) { - if (i != core) - busy_mask |= IPC_IDCCTL_IDCTBIE(i); - } - - return busy_mask; -} - -/** - * \brief Initializes IDC data and registers for interrupt. - */ -int platform_idc_init(void) -{ - struct idc *idc = *idc_get(); - int core = cpu_get_id(); - int ret; - - /* initialize idc data */ - idc->busy_bit_mask = idc_get_busy_bit_mask(core); - - /* configure interrupt */ - idc->irq = interrupt_get_irq(PLATFORM_IDC_INTERRUPT, - PLATFORM_IDC_INTERRUPT_NAME); - if (idc->irq < 0) - return idc->irq; - ret = interrupt_register(idc->irq, idc_irq_handler, idc); - if (ret < 0) - return ret; - interrupt_enable(idc->irq, idc); - - /* enable BUSY interrupt */ - idc_write(IPC_IDCCTL, core, idc->busy_bit_mask); - - return 0; -} - -/** - * \brief Restores IDC interrupt. During D0->D0ix/D0ix->D0 flow primary core - * disables all secondary cores - this is not cold boot process, because - * memory has not been powered off. In that case, we should only enable - * idc interrupts, because all required structures alreade exist. - */ -int platform_idc_restore(void) -{ - struct idc *idc = *idc_get(); - int core = cpu_get_id(); - int ret; - - idc->irq = interrupt_get_irq(PLATFORM_IDC_INTERRUPT, - PLATFORM_IDC_INTERRUPT_NAME); - if (idc->irq < 0) { - tr_err(&idc_tr, "platform_idc_restore(): getting irq failed."); - return idc->irq; - } - - ret = interrupt_register(idc->irq, idc_irq_handler, idc); - if (ret < 0) { - tr_err(&idc_tr, "platform_idc_restore(): registering irq failed."); - return ret; - } - - interrupt_enable(idc->irq, idc); - - /* enable BUSY interrupt */ - idc_write(IPC_IDCCTL, core, idc->busy_bit_mask); - - return 0; -} - -/** - * \brief Frees IDC data and unregisters interrupt. - */ -void idc_free(uint32_t flags) -{ - struct idc *idc = *idc_get(); - int core = cpu_get_id(); - int i = 0; - uint32_t idctfc; - - tr_info(&idc_tr, "idc_free()"); - - /* disable and unregister interrupt */ - interrupt_disable(idc->irq, idc); - interrupt_unregister(idc->irq, idc); - - /* clear BUSY bits */ - for (i = 0; i < CONFIG_CORE_COUNT; i++) { - idctfc = idc_read(IPC_IDCTFC(i), core); - if (idctfc & IPC_IDCTFC_BUSY) - idc_write(IPC_IDCTFC(i), core, idctfc); - } - - if (flags & IDC_FREE_IRQ_ONLY) - return; - - schedule_task_free(&idc->idc_task); -} diff --git a/src/drivers/intel/cavs/interrupt.c b/src/drivers/intel/cavs/interrupt.c deleted file mode 100644 index 8498f07812c1..000000000000 --- a/src/drivers/intel/cavs/interrupt.c +++ /dev/null @@ -1,312 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -// -// Copyright(c) 2018 Intel Corporation. All rights reserved. -// -// Author: Keyon Jie -// Liam Girdwood -// Rander Wang -// Janusz Jankowski - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -/* f6448dbf-a8ec-4660-ada2-08a0011a7a86 */ -DECLARE_SOF_UUID("irq-cavs", irq_cavs_uuid, 0xf6448dbf, 0xa8ec, 0x4660, - 0xad, 0xa2, 0x08, 0xa0, 0x01, 0x1a, 0x7a, 0x86); - -DECLARE_TR_CTX(irq_c_tr, SOF_UUID(irq_cavs_uuid), LOG_LEVEL_INFO); - -/* - * Number of status reload tries before warning the user we are in an IRQ - * storm where some device(s) are repeatedly interrupting and cannot be - * cleared. - */ -#define LVL2_MAX_TRIES 1000 - -#if CONFIG_XT_INTERRUPT_LEVEL_2 -const char irq_name_level2[] = "level2"; -#endif -#if CONFIG_XT_INTERRUPT_LEVEL_3 -const char irq_name_level3[] = "level3"; -#endif -#if CONFIG_XT_INTERRUPT_LEVEL_4 -const char irq_name_level4[] = "level4"; -#endif -#if CONFIG_XT_INTERRUPT_LEVEL_5 -const char irq_name_level5[] = "level5"; -#endif - -/* - * The level2 handler attempts to try and fairly service interrupt sources by - * servicing on first come first served basis. If two or more IRQs arrive at the - * same time then they are serviced in order of ascending status bit. - */ -static inline void irq_lvl2_handler(void *data, int level, uint32_t ilxsd, - uint32_t ilxmsd) -{ - struct irq_desc *parent = data; - struct irq_cascade_desc *cascade = container_of(parent, - struct irq_cascade_desc, desc); - struct irq_desc *child = NULL; - int core = cpu_get_id(); - struct list_item *clist; - uint32_t status; - uint32_t tries = LVL2_MAX_TRIES; - k_spinlock_key_t key; - - /* read active interrupt status */ - status = irq_read(ilxsd); - if (!status) - return; - - /* handle each child */ - for (;;) { - unsigned int bit = ffs(status) - 1; - bool handled = false; - - status &= ~(1 << bit); - - key = k_spin_lock(&cascade->lock); - - /* get child if any and run handler */ - list_for_item(clist, &cascade->child[bit].list) { - child = container_of(clist, struct irq_desc, irq_list); - - if (child->handler && (child->cpu_mask & 1 << core)) { - /* run handler in non atomic context */ - k_spin_unlock(&cascade->lock, key); - child->handler(child->handler_arg); - k_spin_lock(&cascade->lock); - - handled = true; - } - } - - k_spin_unlock(&cascade->lock, key); - - if (!handled) { - /* nobody cared ? */ - tr_err(&irq_c_tr, "irq_lvl2_handler(): nobody cared level %d bit %d", - level, bit); - /* now mask it */ - irq_write(ilxmsd, 0x1 << bit); - } - - /* are all IRQs serviced from last status ? */ - if (status) - continue; - - /* yes, so reload the new status and service again */ - status = irq_read(ilxsd); - if (!status) - break; - - /* any devices continually interrupting / can't be cleared ? */ - if (!--tries) { - tries = LVL2_MAX_TRIES; - tr_err(&irq_c_tr, "irq_lvl2_handler(): IRQ storm at level %d status %08X", - level, irq_read(ilxsd)); - } - } -} - -#define IRQ_LVL2_HANDLER(n) int core = cpu_get_id(); \ - irq_lvl2_handler(data, \ - IRQ_NUM_EXT_LEVEL##n, \ - REG_IRQ_IL##n##SD(core), \ - REG_IRQ_IL##n##MSD(core)) - -#if CONFIG_XT_INTERRUPT_LEVEL_2 -static void irq_lvl2_level2_handler(void *data) -{ - IRQ_LVL2_HANDLER(2); -} -#endif - -#if CONFIG_XT_INTERRUPT_LEVEL_3 -static void irq_lvl2_level3_handler(void *data) -{ - IRQ_LVL2_HANDLER(3); -} -#endif - -#if CONFIG_XT_INTERRUPT_LEVEL_4 -static void irq_lvl2_level4_handler(void *data) -{ - IRQ_LVL2_HANDLER(4); -} -#endif - -#if CONFIG_XT_INTERRUPT_LEVEL_5 -static void irq_lvl2_level5_handler(void *data) -{ - IRQ_LVL2_HANDLER(5); -} -#endif - -uint32_t platform_interrupt_get_enabled(void) -{ - return 0; -} - -void interrupt_mask(uint32_t irq, unsigned int cpu) -{ - struct irq_cascade_desc *cascade = interrupt_get_parent(irq); - - if (cascade && cascade->ops->mask) - cascade->ops->mask(&cascade->desc, irq - cascade->irq_base, - cpu); - -} - -void interrupt_unmask(uint32_t irq, unsigned int cpu) -{ - struct irq_cascade_desc *cascade = interrupt_get_parent(irq); - - if (cascade && cascade->ops->unmask) - cascade->ops->unmask(&cascade->desc, irq - cascade->irq_base, - cpu); - -} - -static void irq_mask(struct irq_desc *desc, uint32_t irq, unsigned int core) -{ - /* mask external interrupt bit */ - switch (desc->irq) { -#if CONFIG_XT_INTERRUPT_LEVEL_5 - case IRQ_NUM_EXT_LEVEL5: - irq_write(REG_IRQ_IL5MSD(core), 1 << irq); - break; -#endif -#if CONFIG_XT_INTERRUPT_LEVEL_4 - case IRQ_NUM_EXT_LEVEL4: - irq_write(REG_IRQ_IL4MSD(core), 1 << irq); - break; -#endif -#if CONFIG_XT_INTERRUPT_LEVEL_3 - case IRQ_NUM_EXT_LEVEL3: - irq_write(REG_IRQ_IL3MSD(core), 1 << irq); - break; -#endif -#if CONFIG_XT_INTERRUPT_LEVEL_2 - case IRQ_NUM_EXT_LEVEL2: - irq_write(REG_IRQ_IL2MSD(core), 1 << irq); - break; -#endif - } - -} - -static void irq_unmask(struct irq_desc *desc, uint32_t irq, unsigned int core) -{ - /* unmask external interrupt bit */ - switch (desc->irq) { -#if CONFIG_XT_INTERRUPT_LEVEL_5 - case IRQ_NUM_EXT_LEVEL5: - irq_write(REG_IRQ_IL5MCD(core), 1 << irq); - break; -#endif -#if CONFIG_XT_INTERRUPT_LEVEL_4 - case IRQ_NUM_EXT_LEVEL4: - irq_write(REG_IRQ_IL4MCD(core), 1 << irq); - break; -#endif -#if CONFIG_XT_INTERRUPT_LEVEL_3 - case IRQ_NUM_EXT_LEVEL3: - irq_write(REG_IRQ_IL3MCD(core), 1 << irq); - break; -#endif -#if CONFIG_XT_INTERRUPT_LEVEL_2 - case IRQ_NUM_EXT_LEVEL2: - irq_write(REG_IRQ_IL2MCD(core), 1 << irq); - break; -#endif - } - -} - -static const struct irq_cascade_ops irq_ops = { - .mask = irq_mask, - .unmask = irq_unmask, -}; - -/* DSP internal interrupts */ -static const struct irq_cascade_tmpl dsp_irq[] = { -#if CONFIG_XT_INTERRUPT_LEVEL_2 - { - .name = irq_name_level2, - .irq = IRQ_NUM_EXT_LEVEL2, - .handler = irq_lvl2_level2_handler, - .ops = &irq_ops, - .global_mask = false, - }, -#endif -#if CONFIG_XT_INTERRUPT_LEVEL_3 - { - .name = irq_name_level3, - .irq = IRQ_NUM_EXT_LEVEL3, - .handler = irq_lvl2_level3_handler, - .ops = &irq_ops, - .global_mask = false, - }, -#endif -#if CONFIG_XT_INTERRUPT_LEVEL_4 - { - .name = irq_name_level4, - .irq = IRQ_NUM_EXT_LEVEL4, - .handler = irq_lvl2_level4_handler, - .ops = &irq_ops, - .global_mask = false, - }, -#endif -#if CONFIG_XT_INTERRUPT_LEVEL_5 - { - .name = irq_name_level5, - .irq = IRQ_NUM_EXT_LEVEL5, - .handler = irq_lvl2_level5_handler, - .ops = &irq_ops, - .global_mask = false, - }, -#endif -}; - -void platform_interrupt_set(uint32_t irq) -{ - if (interrupt_is_dsp_direct(irq)) - arch_interrupt_set(irq); -} - -void platform_interrupt_clear(uint32_t irq, uint32_t mask) -{ - if (interrupt_is_dsp_direct(irq)) - arch_interrupt_clear(irq); -} - -/* Called on each core: from platform_init() and from secondary_core_init() */ -void platform_interrupt_init(void) -{ - int i; - int core = cpu_get_id(); - - /* mask all external IRQs by default */ - irq_write(REG_IRQ_IL2MSD(core), REG_IRQ_IL2MD_ALL); - irq_write(REG_IRQ_IL3MSD(core), REG_IRQ_IL3MD_ALL); - irq_write(REG_IRQ_IL4MSD(core), REG_IRQ_IL4MD_ALL); - irq_write(REG_IRQ_IL5MSD(core), REG_IRQ_IL5MD_ALL); - - if (core != PLATFORM_PRIMARY_CORE_ID) - return; - - for (i = 0; i < ARRAY_SIZE(dsp_irq); i++) - interrupt_cascade_register(dsp_irq + i); -} diff --git a/src/drivers/intel/cavs/ipc.c b/src/drivers/intel/cavs/ipc.c deleted file mode 100644 index 26810730c601..000000000000 --- a/src/drivers/intel/cavs/ipc.c +++ /dev/null @@ -1,316 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -// -// Copyright(c) 2016 Intel Corporation. All rights reserved. -// -// Author: Liam Girdwood -// Keyon Jie -// Rander Wang - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -LOG_MODULE_REGISTER(ipc_task, CONFIG_SOF_LOG_LEVEL); - -/* 8fa1d42f-bc6f-464b-867f-547af08834da */ -DECLARE_SOF_UUID("ipc-task", ipc_task_uuid, 0x8fa1d42f, 0xbc6f, 0x464b, - 0x86, 0x7f, 0x54, 0x7a, 0xf0, 0x88, 0x34, 0xda); - -/* No private data for IPC */ - -#if CONFIG_DEBUG_IPC_COUNTERS -static inline void increment_ipc_received_counter(void) -{ - static uint32_t ipc_received_counter; - - mailbox_sw_reg_write(SRAM_REG_FW_IPC_RECEIVED_COUNT, - ipc_received_counter++); -} - -static inline void increment_ipc_processed_counter(void) -{ - static uint32_t ipc_processed_counter; - uint32_t *uncache_counter = cache_to_uncache(&ipc_processed_counter); - - mailbox_sw_reg_write(SRAM_REG_FW_IPC_PROCESSED_COUNT, - (*uncache_counter)++); -} -#endif - -/* test code to check working IRQ */ -static void ipc_irq_handler(void *arg) -{ - struct ipc *ipc = arg; - uint32_t dipcctl; - uint32_t dipctdr; - uint32_t dipcida; - k_spinlock_key_t key; - - key = k_spin_lock(&ipc->lock); - - dipctdr = ipc_read(IPC_DIPCTDR); - dipcida = ipc_read(IPC_DIPCIDA); - dipcctl = ipc_read(IPC_DIPCCTL); - - tr_dbg(&ipc_tr, "ipc: irq dipctdr 0x%x dipcida 0x%x dipcctl 0x%x", - dipctdr, dipcida, dipcctl); - - /* new message from host */ - if (dipctdr & IPC_DIPCTDR_BUSY && dipcctl & IPC_DIPCCTL_IPCTBIE) { - /* mask Busy interrupt */ - ipc_write(IPC_DIPCCTL, dipcctl & ~IPC_DIPCCTL_IPCTBIE); - -#if CONFIG_DEBUG_IPC_COUNTERS - increment_ipc_received_counter(); -#endif - - ipc_schedule_process(ipc); - } - - /* reply message(done) from host */ - if (dipcida & IPC_DIPCIDA_DONE) { - /* mask Done interrupt */ - ipc_write(IPC_DIPCCTL, - ipc_read(IPC_DIPCCTL) & ~IPC_DIPCCTL_IPCIDIE); - - /* clear DONE bit - tell host we have completed the operation */ - ipc_write(IPC_DIPCIDA, - ipc_read(IPC_DIPCIDA) | IPC_DIPCIDA_DONE); - - ipc->is_notification_pending = false; - - /* unmask Done interrupt */ - ipc_write(IPC_DIPCCTL, - ipc_read(IPC_DIPCCTL) | IPC_DIPCCTL_IPCIDIE); - } - - k_spin_unlock(&ipc->lock, key); -} - -int ipc_platform_compact_read_msg(struct ipc_cmd_hdr *hdr, int words) -{ - uint32_t *chdr = (uint32_t *)hdr; - - /* compact messages are 2 words on CAVS 1.8 onwards */ - if (words != 2) - return 0; - - chdr[0] = ipc_read(IPC_DIPCTDR); - chdr[1] = ipc_read(IPC_DIPCTDD); - - return 2; /* number of words read */ -} - -int ipc_platform_compact_write_msg(struct ipc_cmd_hdr *hdr, int words) -{ - uint32_t *chdr = (uint32_t *)hdr; - - /* compact messages are 2 words on CAVS 1.8 onwards */ - if (words != 2) - return 0; - - /* command complete will set the busy/done bits */ - ipc_write(IPC_DIPCTDR, chdr[0] & ~IPC_DIPCTDR_BUSY); - ipc_write(IPC_DIPCTDD, chdr[1]); - - return 2; /* number of words written */ -} - -enum task_state ipc_platform_do_cmd(struct ipc *ipc) -{ - struct ipc_cmd_hdr *hdr; - - hdr = ipc_compact_read_msg(); - - /* perform command */ - ipc_cmd(hdr); - - /* are we about to enter D3 ? */ - if (ipc->pm_prepare_D3) { - - /* no return - memory will be powered off and IPC sent */ - platform_pm_runtime_power_off(); - } - - return SOF_TASK_STATE_COMPLETED; -} - -void ipc_platform_complete_cmd(struct ipc *ipc) -{ - /* write 1 to clear busy, and trigger interrupt to host*/ - ipc_write(IPC_DIPCTDR, ipc_read(IPC_DIPCTDR) | IPC_DIPCTDR_BUSY); - ipc_write(IPC_DIPCTDA, ipc_read(IPC_DIPCTDA) | IPC_DIPCTDA_DONE); - -#if CONFIG_DEBUG_IPC_COUNTERS - increment_ipc_processed_counter(); -#endif - - /* unmask Busy interrupt */ - ipc_write(IPC_DIPCCTL, ipc_read(IPC_DIPCCTL) | IPC_DIPCCTL_IPCTBIE); -} - -int ipc_platform_send_msg(const struct ipc_msg *msg) -{ - struct ipc *ipc = ipc_get(); - struct ipc_cmd_hdr *hdr; - - if (ipc->is_notification_pending || - ipc_read(IPC_DIPCIDR) & IPC_DIPCIDR_BUSY || - ipc_read(IPC_DIPCIDA) & IPC_DIPCIDA_DONE) - return -EBUSY; - - tr_dbg(&ipc_tr, "ipc: msg tx -> 0x%x", msg->header); - - ipc->is_notification_pending = true; - - /* prepare the message and copy to mailbox */ - hdr = ipc_prepare_to_send(msg); - - /* now interrupt host to tell it we have message sent */ -#if CONFIG_IPC_MAJOR_3 - ipc_write(IPC_DIPCIDD, hdr->dat[1]); - ipc_write(IPC_DIPCIDR, IPC_DIPCIDR_BUSY | hdr->dat[0]); -#elif CONFIG_IPC_MAJOR_4 - ipc_write(IPC_DIPCIDD, hdr->ext); - ipc_write(IPC_DIPCIDR, IPC_DIPCIDR_BUSY | hdr->pri); -#endif - - return 0; -} - -void ipc_platform_send_msg_direct(const struct ipc_msg *msg) -{ - /* TODO: add support */ -} - -int platform_ipc_init(struct ipc *ipc) -{ - int irq; - - ipc_set_drvdata(ipc, NULL); - - /* schedule */ - schedule_task_init_edf(&ipc->ipc_task, SOF_UUID(ipc_task_uuid), - &ipc_task_ops, ipc, 0, 0); - - /* configure interrupt */ - irq = interrupt_get_irq(PLATFORM_IPC_INTERRUPT, - PLATFORM_IPC_INTERRUPT_NAME); - if (irq < 0) - return irq; - interrupt_register(irq, ipc_irq_handler, ipc); - interrupt_enable(irq, ipc); - - /* enable IPC interrupts from host */ - ipc_write(IPC_DIPCCTL, IPC_DIPCCTL_IPCIDIE | IPC_DIPCCTL_IPCTBIE); - - return 0; -} - -#if CONFIG_IPC_POLLING - -int ipc_platform_poll_init(void) -{ - return 0; -} - -/* tell host we have completed command */ -void ipc_platform_poll_set_cmd_done(void) -{ - - /* write 1 to clear busy, and trigger interrupt to host*/ - ipc_write(IPC_DIPCTDR, ipc_read(IPC_DIPCTDR) | IPC_DIPCTDR_BUSY); - ipc_write(IPC_DIPCTDA, ipc_read(IPC_DIPCTDA) | IPC_DIPCTDA_DONE); - - /* unmask Busy interrupt */ - ipc_write(IPC_DIPCCTL, ipc_read(IPC_DIPCCTL) | IPC_DIPCCTL_IPCTBIE); -} - -/* read the IPC register for any new command messages */ -int ipc_platform_poll_is_cmd_pending(void) -{ - uint32_t dipcctl; - uint32_t dipctdr; - - dipctdr = ipc_read(IPC_DIPCTDR); - dipcctl = ipc_read(IPC_DIPCCTL); - - /* new message from host */ - if (dipctdr & IPC_DIPCTDR_BUSY && dipcctl & IPC_DIPCCTL_IPCTBIE) { - /* mask Busy interrupt */ - ipc_write(IPC_DIPCCTL, dipcctl & ~IPC_DIPCCTL_IPCTBIE); - - /* new message */ - return 1; - } - - /* no new message */ - return 0; -} - -int ipc_platform_poll_is_host_ready(void) -{ - uint32_t dipcida; - - dipcida = ipc_read(IPC_DIPCIDA); - - /* reply message(done) from host */ - if (dipcida & IPC_DIPCIDA_DONE) { - /* mask Done interrupt */ - ipc_write(IPC_DIPCCTL, - ipc_read(IPC_DIPCCTL) & ~IPC_DIPCCTL_IPCIDIE); - - /* clear DONE bit - tell host we have completed the operation */ - ipc_write(IPC_DIPCIDA, - ipc_read(IPC_DIPCIDA) | IPC_DIPCIDA_DONE); - - /* unmask Done interrupt */ - ipc_write(IPC_DIPCCTL, - ipc_read(IPC_DIPCCTL) | IPC_DIPCCTL_IPCIDIE); - - /* host has completed */ - return 1; - } - - /* host still pending */ - return 0; -} - -int ipc_platform_poll_tx_host_msg(struct ipc_msg *msg) -{ - - if (ipc_read(IPC_DIPCIDR) & IPC_DIPCIDR_BUSY || - ipc_read(IPC_DIPCIDA) & IPC_DIPCIDA_DONE) - /* cant send message atm */ - return 0; - - /* now send the message */ - mailbox_dspbox_write(0, msg->tx_data, msg->tx_size); - - /* now interrupt host to tell it we have message sent */ - ipc_write(IPC_DIPCIDD, 0); - ipc_write(IPC_DIPCIDR, IPC_DIPCIDR_BUSY | msg->header); - - /* message sent */ - return 1; -} - -#endif diff --git a/src/drivers/intel/cavs/timer.c b/src/drivers/intel/cavs/timer.c deleted file mode 100644 index d1b28f4b2a6b..000000000000 --- a/src/drivers/intel/cavs/timer.c +++ /dev/null @@ -1,254 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -// -// Copyright(c) 2018 Intel Corporation. All rights reserved. -// -// Author: Liam Girdwood -// Keyon Jie -// Rander Wang -// Janusz Jankowski - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/** \brief Minimum number of timer recovery cycles in case of delay. */ -#define TIMER_MIN_RECOVER_CYCLES 240 /* ~10us at 24.576MHz */ - -#ifndef __ZEPHYR__ -void platform_timer_start(struct timer *timer) -{ - /* run timer */ - shim_write64(SHIM_DSPWCT0C, 0); - shim_write(SHIM_DSPWCTCS, - shim_read(SHIM_DSPWCTCS) | SHIM_DSPWCTCS_T0A); -} - -void platform_timer_stop(struct timer *timer) -{ - /* stop timer */ - shim_write64(SHIM_DSPWCT0C, 0); - shim_write(SHIM_DSPWCTCS, - shim_read(SHIM_DSPWCTCS) & ~SHIM_DSPWCTCS_T0A); -} - -int64_t platform_timer_set(struct timer *timer, uint64_t ticks) -{ - uint64_t ticks_now; - uint32_t flags; - - /* a tick value of 0 will not generate an IRQ */ - if (ticks == 0) - ticks = 1; - - irq_local_disable(flags); - - ticks_now = platform_timer_get(timer); - - /* Check if requested time is not past time and include the - * overhead of changing the timer - */ - if (ticks > ticks_now + TIMER_MIN_RECOVER_CYCLES) { - shim_write64(SHIM_DSPWCT0C, ticks); - } else { - ticks = ticks_now + TIMER_MIN_RECOVER_CYCLES; - if (ticks == 0) - ticks = 1; - - shim_write64(SHIM_DSPWCT0C, ticks); - } - - /* Enable IRQ */ - shim_write(SHIM_DSPWCTCS, SHIM_DSPWCTCS_T0A); - - irq_local_enable(flags); - - return shim_read64(SHIM_DSPWCT0C); -} - -void platform_timer_clear(struct timer *timer) -{ - /* write 1 to clear the timer interrupt */ - shim_write(SHIM_DSPWCTCS, SHIM_DSPWCTCS_T0T); -} - -uint64_t platform_timer_get(struct timer *timer) -{ - uint32_t hi0; - uint32_t hi1; - uint32_t lo; - uint64_t ticks_now; - - /* 64bit reads are non atomic on xtensa so we must - * read a stable value where there is no bit 32 flipping. - * A large delta between reads[0..1] means we have flipped - * and that the value read back in either 0..1] is invalid. - */ - do { - hi0 = shim_read(SHIM_DSPWCH); - lo = shim_read(SHIM_DSPWCL); - hi1 = shim_read(SHIM_DSPWCH); - - /* worst case is we perform this twice so 6 * 32b clock reads */ - } while (hi0 != hi1); - - ticks_now = (((uint64_t)hi0) << 32) | lo; - - return ticks_now; -} - -uint64_t platform_timer_get_atomic(struct timer *timer) -{ - uint32_t flags; - uint64_t ticks_now; - - irq_local_disable(flags); - ticks_now = platform_timer_get(timer); - irq_local_enable(flags); - - return ticks_now; -} -#endif /* __ZEPHYR__ */ - -/* get timestamp for host stream DMA position */ -void platform_host_timestamp(struct comp_dev *host, - struct sof_ipc_stream_posn *posn) -{ - int err; - - /* get host position */ - err = comp_position(host, posn); - if (err == 0) - posn->flags |= SOF_TIME_HOST_VALID; -} - -/* get timestamp for DAI stream DMA position */ -void platform_dai_timestamp(struct comp_dev *dai, - struct sof_ipc_stream_posn *posn) -{ - int err; - - /* get DAI position */ - err = comp_position(dai, posn); - if (err == 0) - posn->flags |= SOF_TIME_DAI_VALID; - - /* get SSP wallclock - DAI sets this to stream start value */ - posn->wallclock = sof_cycle_get_64() - posn->wallclock; - posn->wallclock_hz = clock_get_freq(PLATFORM_DEFAULT_CLOCK); - posn->flags |= SOF_TIME_WALL_VALID; -} - -/* get current wallclock for componnent */ -void platform_dai_wallclock(struct comp_dev *dai, uint64_t *wallclock) -{ - *wallclock = sof_cycle_get_64(); -} - -#ifndef __ZEPHYR__ -static int platform_timer_register(struct timer *timer, - void (*handler)(void *arg), void *arg) -{ - int err; - - /* register timer interrupt */ - timer->logical_irq = interrupt_get_irq(timer->irq, timer->irq_name); - if (timer->logical_irq < 0) - return timer->logical_irq; - - err = interrupt_register(timer->logical_irq, handler, arg); - if (err < 0) - return err; - - /* enable timer interrupt */ - interrupt_enable(timer->logical_irq, arg); - - return err; -} - -int timer_register(struct timer *timer, void (*handler)(void *arg), void *arg) -{ - int ret; - - switch (timer->id) { - case TIMER0: - case TIMER1: - case TIMER2: - /* arch timers have no children, so HW IRQ is logical IRQ */ - timer->logical_irq = timer->irq; - ret = arch_timer_register(timer, handler, arg); - break; - case TIMER3: - case TIMER4: - ret = platform_timer_register(timer, handler, arg); - break; - default: - ret = -EINVAL; - break; - } - - return ret; -} - -static void platform_timer_unregister(struct timer *timer, void *arg) -{ - /* disable timer interrupt */ - interrupt_disable(timer->logical_irq, arg); - - /* unregister timer interrupt */ - interrupt_unregister(timer->logical_irq, arg); -} - -void timer_unregister(struct timer *timer, void *arg) -{ - switch (timer->id) { - case TIMER0: - case TIMER1: - case TIMER2: - interrupt_unregister(timer->logical_irq, arg); - break; - case TIMER3: - case TIMER4: - platform_timer_unregister(timer, arg); - break; - } - -} - -void timer_enable(struct timer *timer, void *arg, int core) -{ - switch (timer->id) { - case TIMER0: - case TIMER1: - case TIMER2: - interrupt_enable(timer->logical_irq, arg); - break; - case TIMER3: - case TIMER4: - interrupt_unmask(timer->logical_irq, core); - break; - } - -} - -void timer_disable(struct timer *timer, void *arg, int core) -{ - switch (timer->id) { - case TIMER0: - case TIMER1: - case TIMER2: - interrupt_disable(timer->logical_irq, arg); - break; - case TIMER3: - case TIMER4: - interrupt_mask(timer->logical_irq, core); - break; - } - -} -#endif /* __ZEPHYR__ */ diff --git a/src/drivers/intel/cavs/timestamp.c b/src/drivers/intel/cavs/timestamp.c deleted file mode 100644 index d554936871f9..000000000000 --- a/src/drivers/intel/cavs/timestamp.c +++ /dev/null @@ -1,270 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -// -// Copyright(c) 2020 Intel Corporation. All rights reserved. -// -// Author: Tomasz Lauda - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -LOG_MODULE_REGISTER(dai_ts, CONFIG_SOF_LOG_LEVEL); - -int timestamp_hda_config(struct dai *dai, struct timestamp_cfg *cfg) -{ - int i; - - if (cfg->type != SOF_DAI_INTEL_HDA) { - dai_err(dai, "dmic_ts_config(): Illegal DAI type"); - return -EINVAL; - } - - cfg->walclk_rate = 0; - for (i = 0; i < NUM_SSP_FREQ; i++) { - if (ssp_freq_sources[i] == SSP_CLOCK_XTAL_OSCILLATOR) - cfg->walclk_rate = ssp_freq[i].freq; - } - - return 0; -} - -int timestamp_hda_start(struct dai *dai, struct timestamp_cfg *cfg) -{ - /* Set HDA timestamp registers */ - uint32_t addr = TIMESTAMP_BASE + TS_HDA_LOCAL_TSCTRL; - uint32_t cdmas; - - /* Set HDA timestamp registers */ - - /* Set CDMAS(4:0) to match DMA engine index and direction - * also clear NTK to be sure there is no old timestamp. - */ - cdmas = TS_LOCAL_TSCTRL_CDMAS(cfg->dma_chan_index | - (cfg->direction == SOF_IPC_STREAM_PLAYBACK ? BIT(4) : 0)); - io_reg_write(addr, TS_LOCAL_TSCTRL_NTK_BIT | cdmas); - - /* Request on demand timestamp */ - io_reg_write(addr, TS_LOCAL_TSCTRL_ODTS_BIT | cdmas); - - return 0; -} - -int timestamp_hda_stop(struct dai *dai, struct timestamp_cfg *cfg) -{ - /* Clear NTK and write zero to CDMAS */ - io_reg_write(TIMESTAMP_BASE + TS_HDA_LOCAL_TSCTRL, - TS_LOCAL_TSCTRL_NTK_BIT); - return 0; -} - -int timestamp_hda_get(struct dai *dai, struct timestamp_cfg *cfg, - struct timestamp_data *tsd) -{ - /* Read HDA timestamp registers */ - uint32_t tsctrl = TIMESTAMP_BASE + TS_HDA_LOCAL_TSCTRL; - uint32_t ntk; - - ntk = io_reg_read(tsctrl) & TS_LOCAL_TSCTRL_NTK_BIT; - if (!ntk) - goto out; - - /* NTK was set, get wall clock */ - tsd->walclk = io_reg_read64(TIMESTAMP_BASE + TS_HDA_LOCAL_WALCLK); - - /* Sample */ - tsd->sample = io_reg_read64(TIMESTAMP_BASE + TS_HDA_LOCAL_SAMPLE); - - /* Clear NTK to enable successive timestamps */ - io_reg_write(tsctrl, TS_LOCAL_TSCTRL_NTK_BIT); - -out: - tsd->walclk_rate = cfg->walclk_rate; - if (!ntk) - return -ENODATA; - - return 0; -} - -#if CONFIG_INTEL_DMIC - -int timestamp_dmic_config(struct dai *dai, struct timestamp_cfg *cfg) -{ - if (cfg->type != SOF_DAI_INTEL_DMIC) { - dai_err(dai, "dmic_ts_config(): Illegal DAI type"); - return -EINVAL; - } - - cfg->walclk_rate = CONFIG_DMIC_HW_IOCLK; - - return 0; -} - -int timestamp_dmic_start(struct dai *dai, struct timestamp_cfg *cfg) -{ - uint32_t addr = TIMESTAMP_BASE + TS_DMIC_LOCAL_TSCTRL; - uint32_t cdmas; - - /* Set DMIC timestamp registers */ - - /* First point CDMAS to GPDMA channel that is used by DMIC - * also clear NTK to be sure there is no old timestamp. - */ - cdmas = TS_LOCAL_TSCTRL_CDMAS(cfg->dma_chan_index + - cfg->dma_chan_count * cfg->dma_id); - io_reg_write(addr, TS_LOCAL_TSCTRL_NTK_BIT | cdmas); - - /* Request on demand timestamp */ - io_reg_write(addr, TS_LOCAL_TSCTRL_ODTS_BIT | cdmas); - - return 0; -} - -int timestamp_dmic_stop(struct dai *dai, struct timestamp_cfg *cfg) -{ - /* Clear NTK and write zero to CDMAS */ - io_reg_write(TIMESTAMP_BASE + TS_DMIC_LOCAL_TSCTRL, - TS_LOCAL_TSCTRL_NTK_BIT); - return 0; -} - -int timestamp_dmic_get(struct dai *dai, struct timestamp_cfg *cfg, - struct timestamp_data *tsd) -{ - /* Read DMIC timestamp registers */ - uint32_t tsctrl = TIMESTAMP_BASE + TS_DMIC_LOCAL_TSCTRL; - uint32_t ntk; - - /* Read SSP timestamp registers */ - ntk = io_reg_read(tsctrl) & TS_LOCAL_TSCTRL_NTK_BIT; - if (!ntk) - goto out; - - /* NTK was set, get wall clock */ - tsd->walclk = io_reg_read64(TIMESTAMP_BASE + TS_DMIC_LOCAL_WALCLK); - - /* Sample */ - tsd->sample = io_reg_read64(TIMESTAMP_BASE + TS_DMIC_LOCAL_SAMPLE); - - /* Clear NTK to enable successive timestamps */ - io_reg_write(tsctrl, TS_LOCAL_TSCTRL_NTK_BIT); - -out: - tsd->walclk_rate = cfg->walclk_rate; - if (!ntk) - return -ENODATA; - - return 0; -} - -#endif /* CONFIG_INTEL_DMIC */ - -#if CONFIG_INTEL_SSP - -static uint32_t ssp_ts_local_tsctrl_addr(int index) -{ - return TIMESTAMP_BASE + TS_I2S_LOCAL_TSCTRL(index); -} - -static uint32_t ssp_ts_local_sample_addr(int index) -{ - return TIMESTAMP_BASE + TS_I2S_LOCAL_SAMPLE(index); -} - -static uint32_t ssp_ts_local_walclk_addr(int index) -{ - return TIMESTAMP_BASE + TS_I2S_LOCAL_WALCLK(index); -} - -int timestamp_ssp_config(struct dai *dai, struct timestamp_cfg *cfg) -{ - int i; - - if (cfg->type != SOF_DAI_INTEL_SSP) { - dai_err(dai, "ssp_ts_config(): Illegal DAI type"); - return -EINVAL; - } - - if (cfg->index > DAI_NUM_SSP_BASE + DAI_NUM_SSP_EXT - 1) { - dai_err(dai, "ssp_ts_config(): Illegal DAI index"); - return -EINVAL; - } - - cfg->walclk_rate = 0; - for (i = 0; i < NUM_SSP_FREQ; i++) { - if (ssp_freq_sources[i] == SSP_CLOCK_XTAL_OSCILLATOR) - cfg->walclk_rate = ssp_freq[i].freq; - } - - if (!cfg->walclk_rate) { - dai_err(dai, "ssp_ts_config(): No XTAL frequency defined"); - return -EINVAL; - } - - return 0; -} - -int timestamp_ssp_start(struct dai *dai, struct timestamp_cfg *cfg) -{ - uint32_t cdmas; - uint32_t addr = ssp_ts_local_tsctrl_addr(cfg->index); - - /* Set SSP timestamp registers */ - - /* First point CDMAS to GPDMA channel that is used by this SSP, - * also clear NTK to be sure there is no old timestamp. - */ - cdmas = TS_LOCAL_TSCTRL_CDMAS(cfg->dma_chan_index + - cfg->dma_chan_count * cfg->dma_id); - io_reg_write(addr, TS_LOCAL_TSCTRL_NTK_BIT | cdmas); - - /* Request on demand timestamp */ - io_reg_write(addr, TS_LOCAL_TSCTRL_ODTS_BIT | cdmas); - - return 0; -} - -int timestamp_ssp_stop(struct dai *dai, struct timestamp_cfg *cfg) -{ - /* Clear NTK and write zero to CDMAS */ - io_reg_write(ssp_ts_local_tsctrl_addr(cfg->index), - TS_LOCAL_TSCTRL_NTK_BIT); - return 0; -} - -int timestamp_ssp_get(struct dai *dai, struct timestamp_cfg *cfg, - struct timestamp_data *tsd) -{ - uint32_t ntk; - uint32_t tsctrl = ssp_ts_local_tsctrl_addr(cfg->index); - - /* Read SSP timestamp registers */ - ntk = io_reg_read(tsctrl) & TS_LOCAL_TSCTRL_NTK_BIT; - if (!ntk) - goto out; - - /* NTK was set, get wall clock */ - tsd->walclk = io_reg_read64(ssp_ts_local_walclk_addr(cfg->index)); - - /* Sample */ - tsd->sample = io_reg_read64(ssp_ts_local_sample_addr(cfg->index)); - - /* Clear NTK to enable successive timestamps */ - io_reg_write(tsctrl, TS_LOCAL_TSCTRL_NTK_BIT); - -out: - tsd->walclk_rate = cfg->walclk_rate; - if (!ntk) - return -ENODATA; - - return 0; -} - -#endif /* CONFIG_INTEL_SSP */ diff --git a/src/drivers/intel/dmic/CMakeLists.txt b/src/drivers/intel/dmic/CMakeLists.txt deleted file mode 100644 index 8bb406f49133..000000000000 --- a/src/drivers/intel/dmic/CMakeLists.txt +++ /dev/null @@ -1,10 +0,0 @@ -# SPDX-License-Identifier: BSD-3-Clause - -add_local_sources(sof dmic.c) -if(CONFIG_INTEL_DMIC_TPLG_PARAMS) - add_local_sources(sof dmic_computed.c) -endif() - -if(CONFIG_INTEL_DMIC_NHLT) - add_local_sources(sof dmic_nhlt.c) -endif() diff --git a/src/drivers/intel/dmic/dmic.c b/src/drivers/intel/dmic/dmic.c deleted file mode 100644 index 09f8ddb818b1..000000000000 --- a/src/drivers/intel/dmic/dmic.c +++ /dev/null @@ -1,646 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -// -// Copyright(c) 2017 Intel Corporation. All rights reserved. -// -// Author: Seppo Ingalsuo - -#if CONFIG_INTEL_DMIC - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -LOG_MODULE_REGISTER(dmic_dai, CONFIG_SOF_LOG_LEVEL); - -/* aafc26fe-3b8d-498d-8bd6-248fc72efa31 */ -DECLARE_SOF_UUID("dmic-dai", dmic_uuid, 0xaafc26fe, 0x3b8d, 0x498d, - 0x8b, 0xd6, 0x24, 0x8f, 0xc7, 0x2e, 0xfa, 0x31); - -DECLARE_TR_CTX(dmic_tr, SOF_UUID(dmic_uuid), LOG_LEVEL_INFO); - -/* Configuration ABI version, increment if not compatible with previous - * version. - */ -#define DMIC_IPC_VERSION 1 - -/* Base addresses (in PDM scope) of 2ch PDM controllers and coefficient RAM. */ -static const uint32_t base[4] = {PDM0, PDM1, PDM2, PDM3}; - -/* Global configuration request and state for DMIC */ -static SHARED_DATA struct dmic_global_shared dmic_global; - -/* this ramps volume changes over time */ -static void dmic_gain_ramp(struct dai *dai) -{ - struct dmic_pdata *dmic = dai_get_drvdata(dai); - k_spinlock_key_t key; - int32_t gval; - uint32_t val; - int i; - - /* Currently there's no DMIC HW internal mutings and wait times - * applied into this start sequence. It can be implemented here if - * start of audio capture would contain clicks and/or noise and it - * is not suppressed by gain ramp somewhere in the capture pipe. - */ - - dai_dbg(dai, "dmic_gain_ramp()"); - - /* - * At run-time dmic->gain is only changed in this function, and this - * function runs in the pipeline task context, so it cannot run - * concurrently on multiple cores, since there's always only one - * task associated with each DAI, so we don't need to hold the lock to - * read the value here. - */ - if (dmic->gain == DMIC_HW_FIR_GAIN_MAX << 11) - return; - - key = k_spin_lock(&dai->lock); - - /* Increment gain with logarithmic step. - * Gain is Q2.30 and gain modifier is Q12.20. - */ - dmic->startcount++; - dmic->gain = q_multsr_sat_32x32(dmic->gain, dmic->gain_coef, Q_SHIFT_GAIN_X_GAIN_COEF); - - /* Gain is stored as Q2.30, while HW register is Q1.19 so shift - * the value right by 11. - */ - gval = dmic->gain >> 11; - - /* Note that DMIC gain value zero has a special purpose. Value zero - * sets gain bypass mode in HW. Zero value will be applied after ramp - * is complete. It is because exact 1.0 gain is not possible with Q1.19. - */ - if (gval > DMIC_HW_FIR_GAIN_MAX) { - gval = 0; - dmic->gain = DMIC_HW_FIR_GAIN_MAX << 11; - } - - /* Write gain to registers */ - for (i = 0; i < DMIC_HW_CONTROLLERS; i++) { - if (!dmic->enable[i]) - continue; - - if (dmic->startcount == DMIC_UNMUTE_CIC) - dai_update_bits(dai, base[i] + CIC_CONTROL, - CIC_CONTROL_MIC_MUTE_BIT, 0); - - if (dmic->startcount == DMIC_UNMUTE_FIR) { - switch (dai->index) { - case 0: - dai_update_bits(dai, base[i] + FIR_CONTROL_A, - FIR_CONTROL_A_MUTE_BIT, 0); - break; - case 1: - dai_update_bits(dai, base[i] + FIR_CONTROL_B, - FIR_CONTROL_B_MUTE_BIT, 0); - break; - } - } - switch (dai->index) { - case 0: - val = OUT_GAIN_LEFT_A_GAIN(gval); - dai_write(dai, base[i] + OUT_GAIN_LEFT_A, val); - dai_write(dai, base[i] + OUT_GAIN_RIGHT_A, val); - break; - case 1: - val = OUT_GAIN_LEFT_B_GAIN(gval); - dai_write(dai, base[i] + OUT_GAIN_LEFT_B, val); - dai_write(dai, base[i] + OUT_GAIN_RIGHT_B, val); - break; - } - } - - - k_spin_unlock(&dai->lock, key); -} - -/* get DMIC hw params */ -static int dmic_get_hw_params(struct dai *dai, - struct sof_ipc_stream_params *params, int dir) -{ -#if CONFIG_INTEL_DMIC_TPLG_PARAMS - return dmic_get_hw_params_computed(dai, params, dir); - -#elif CONFIG_INTEL_DMIC_NHLT - return dmic_get_hw_params_nhlt(dai, params, dir); - -#else - return -EINVAL; -#endif -} - -static int dmic_set_config(struct dai *dai, struct ipc_config_dai *common_config, - const void *spec_config) -{ - struct dmic_pdata *dmic = dai_get_drvdata(dai); - int ret = 0; - int di = dai->index; - k_spinlock_key_t key; -#if CONFIG_INTEL_DMIC_TPLG_PARAMS - const struct sof_ipc_dai_config *config = spec_config; - int i; - int j; -#endif - - dai_info(dai, "dmic_set_config()"); - - if (di >= DMIC_HW_FIFOS) { - dai_err(dai, "dmic_set_config(): DAI index exceeds number of FIFOs"); - return -EINVAL; - } - - if (!spec_config) { - dai_err(dai, "dmic_set_config(): NULL config"); - return -EINVAL; - } - - assert(dmic); - key = k_spin_lock(&dai->lock); - -#if CONFIG_INTEL_DMIC_TPLG_PARAMS - /* - * "config" might contain pdm controller params for only - * the active controllers - * "prm" is initialized with default params for all HW controllers - */ - if (config->dmic.driver_ipc_version != DMIC_IPC_VERSION) { - dai_err(dai, "dmic_set_config(): wrong ipc version"); - ret = -EINVAL; - goto out; - } - - if (config->dmic.num_pdm_active > DMIC_HW_CONTROLLERS) { - dai_err(dai, "dmic_set_config(): the requested PDM controllers count exceeds platform capability"); - ret = -EINVAL; - goto out; - } - - /* Get unmute gain ramp duration. Use the value from topology - * if it is non-zero, otherwise use default length. - */ - if (config->dmic.unmute_ramp_time) - dmic->unmute_ramp_time_ms = config->dmic.unmute_ramp_time; - else - dmic->unmute_ramp_time_ms = dmic_get_unmute_ramp_from_samplerate( - config->dmic.fifo_fs); - - if (dmic->unmute_ramp_time_ms < LOGRAMP_TIME_MIN_MS || - dmic->unmute_ramp_time_ms > LOGRAMP_TIME_MAX_MS) { - dai_err(dai, "dmic_set_config(): Illegal ramp time = %d", - dmic->unmute_ramp_time_ms); - ret = -EINVAL; - goto out; - } - - /* Copy the new DMIC params header (all but not pdm[]) to persistent. - * The last arrived request determines the parameters. - */ - ret = memcpy_s(&dmic->global->prm[di], sizeof(dmic->global->prm[di]), &config->dmic, - offsetof(struct sof_ipc_dai_dmic_params, pdm)); - assert(!ret); - - /* copy the pdm controller params from ipc */ - for (i = 0; i < DMIC_HW_CONTROLLERS; i++) { - dmic->global->prm[di].pdm[i].id = i; - for (j = 0; j < config->dmic.num_pdm_active; j++) { - /* copy the pdm controller params id the id's match */ - if (dmic->global->prm[di].pdm[i].id == config->dmic.pdm[j].id) { - ret = memcpy_s(&dmic->global->prm[di].pdm[i], - sizeof(dmic->global->prm[di].pdm[i]), - &config->dmic.pdm[j], - sizeof(struct sof_ipc_dai_dmic_pdm_ctrl)); - assert(!ret); - } - } - } - - ret = dmic_set_config_computed(dai); - -#elif CONFIG_INTEL_DMIC_NHLT - ret = dmic_set_config_nhlt(dai, spec_config); - - /* There's no unmute ramp duration in blob, so the default rate dependent is used. */ - dmic->unmute_ramp_time_ms = dmic_get_unmute_ramp_from_samplerate(dmic->dai_rate); -#else - ret = -EINVAL; -#endif - - if (ret < 0) { - dai_err(dai, "dmic_set_config(): Failed to set the requested configuration."); - goto out; - } - - dai_info(dai, "dmic_set_config(): unmute_ramp_time_ms = %d", dmic->unmute_ramp_time_ms); - - dmic->state = COMP_STATE_PREPARE; - -out: - k_spin_unlock(&dai->lock, key); - return ret; -} - -/* start the DMIC for capture */ -static void dmic_start(struct dai *dai) -{ - struct dmic_pdata *dmic = dai_get_drvdata(dai); - k_spinlock_key_t key; - int32_t step_db; - int i; - int mic_a; - int mic_b; - int fir_a; - int fir_b; - - /* enable port */ - key = k_spin_lock(&dai->lock); - dai_dbg(dai, "dmic_start()"); - dmic->startcount = 0; - - /* - * Compute unmute ramp gain update coefficient, based on DAI processing - * period in microseconds. - */ - step_db = dai->dd->dai_dev->period * (int64_t)-LOGRAMP_START_DB / - (1000 * dmic->unmute_ramp_time_ms); - dmic->gain_coef = db2lin_fixed(step_db); - - /* Initial gain value, convert Q12.20 to Q2.30 */ - dmic->gain = Q_SHIFT_LEFT(db2lin_fixed(LOGRAMP_START_DB), 20, 30); - - switch (dai->index) { - case 0: - dai_info(dai, "dmic_start(), dmic->fifo_a"); - /* Clear FIFO A initialize, Enable interrupts to DSP, - * Start FIFO A packer. - */ - dai_update_bits(dai, OUTCONTROL0, - OUTCONTROL0_FINIT_BIT | OUTCONTROL0_SIP_BIT, - OUTCONTROL0_SIP_BIT); - break; - case 1: - dai_info(dai, "dmic_start(), dmic->fifo_b"); - /* Clear FIFO B initialize, Enable interrupts to DSP, - * Start FIFO B packer. - */ - dai_update_bits(dai, OUTCONTROL1, - OUTCONTROL1_FINIT_BIT | OUTCONTROL1_SIP_BIT, - OUTCONTROL1_SIP_BIT); - } - - for (i = 0; i < DMIC_HW_CONTROLLERS; i++) { - mic_a = dmic->enable[i] & 1; - mic_b = (dmic->enable[i] & 2) >> 1; - fir_a = (dmic->enable[i] > 0) ? 1 : 0; -#if DMIC_HW_FIFOS > 1 - fir_b = (dmic->enable[i] > 0) ? 1 : 0; -#else - fir_b = 0; -#endif - dai_info(dai, "dmic_start(), pdm%d mic_a = %u, mic_b = %u", i, mic_a, mic_b); - - /* If both microphones are needed start them simultaneously - * to start them in sync. The reset may be cleared for another - * FIFO already. If only one mic, start them independently. - * This makes sure we do not clear start/en for another DAI. - */ - if (mic_a && mic_b) { - dai_update_bits(dai, base[i] + CIC_CONTROL, - CIC_CONTROL_CIC_START_A_BIT | - CIC_CONTROL_CIC_START_B_BIT, - CIC_CONTROL_CIC_START_A(1) | - CIC_CONTROL_CIC_START_B(1)); - dai_update_bits(dai, base[i] + MIC_CONTROL, - MIC_CONTROL_PDM_EN_A_BIT | - MIC_CONTROL_PDM_EN_B_BIT, - MIC_CONTROL_PDM_EN_A(1) | - MIC_CONTROL_PDM_EN_B(1)); - } else if (mic_a) { - dai_update_bits(dai, base[i] + CIC_CONTROL, - CIC_CONTROL_CIC_START_A_BIT, - CIC_CONTROL_CIC_START_A(1)); - dai_update_bits(dai, base[i] + MIC_CONTROL, - MIC_CONTROL_PDM_EN_A_BIT, - MIC_CONTROL_PDM_EN_A(1)); - } else if (mic_b) { - dai_update_bits(dai, base[i] + CIC_CONTROL, - CIC_CONTROL_CIC_START_B_BIT, - CIC_CONTROL_CIC_START_B(1)); - dai_update_bits(dai, base[i] + MIC_CONTROL, - MIC_CONTROL_PDM_EN_B_BIT, - MIC_CONTROL_PDM_EN_B(1)); - } - - switch (dai->index) { - case 0: - dai_update_bits(dai, base[i] + FIR_CONTROL_A, - FIR_CONTROL_A_START_BIT, - FIR_CONTROL_A_START(fir_a)); - break; - case 1: - dai_update_bits(dai, base[i] + FIR_CONTROL_B, - FIR_CONTROL_B_START_BIT, - FIR_CONTROL_B_START(fir_b)); - break; - } - } - - /* Clear soft reset for all/used PDM controllers. This should - * start capture in sync. - */ - for (i = 0; i < DMIC_HW_CONTROLLERS; i++) { - dai_update_bits(dai, base[i] + CIC_CONTROL, - CIC_CONTROL_SOFT_RESET_BIT, 0); - } - - /* Set bit dai->index */ - dmic->global->active_fifos_mask |= BIT(dai->index); - dmic->global->pause_mask &= ~BIT(dai->index); - - dmic->state = COMP_STATE_ACTIVE; - k_spin_unlock(&dai->lock, key); - - dai_info(dai, "dmic_start(), dmic_active_fifos_mask = 0x%x", - dmic->global->active_fifos_mask); -} - -static void dmic_stop_fifo_packers(struct dai *dai, int fifo_index) -{ - /* Stop FIFO packers and set FIFO initialize bits */ - switch (fifo_index) { - case 0: - dai_update_bits(dai, OUTCONTROL0, - OUTCONTROL0_SIP_BIT | OUTCONTROL0_FINIT_BIT, - OUTCONTROL0_FINIT_BIT); - break; - case 1: - dai_update_bits(dai, OUTCONTROL1, - OUTCONTROL1_SIP_BIT | OUTCONTROL1_FINIT_BIT, - OUTCONTROL1_FINIT_BIT); - break; - } -} - -/* stop the DMIC for capture */ -static void dmic_stop(struct dai *dai, bool stop_is_pause) -{ - struct dmic_pdata *dmic = dai_get_drvdata(dai); - k_spinlock_key_t key; - int i; - - dai_dbg(dai, "dmic_stop()"); - key = k_spin_lock(&dai->lock); - - dmic_stop_fifo_packers(dai, dai->index); - - /* Set soft reset and mute on for all PDM controllers. - */ - dai_info(dai, "dmic_stop(), dmic_active_fifos_mask = 0x%x", - dmic->global->active_fifos_mask); - - /* Clear bit dai->index for active FIFO. If stop for pause, set pause mask bit. - * If stop is not for pausing, it is safe to clear the pause bit. - */ - dmic->global->active_fifos_mask &= ~BIT(dai->index); - if (stop_is_pause) - dmic->global->pause_mask |= BIT(dai->index); - else - dmic->global->pause_mask &= ~BIT(dai->index); - - for (i = 0; i < DMIC_HW_CONTROLLERS; i++) { - /* Don't stop CIC yet if one FIFO remains active */ - if (dmic->global->active_fifos_mask == 0) { - dai_update_bits(dai, base[i] + CIC_CONTROL, - CIC_CONTROL_SOFT_RESET_BIT | - CIC_CONTROL_MIC_MUTE_BIT, - CIC_CONTROL_SOFT_RESET_BIT | - CIC_CONTROL_MIC_MUTE_BIT); - } - switch (dai->index) { - case 0: - dai_update_bits(dai, base[i] + FIR_CONTROL_A, - FIR_CONTROL_A_MUTE_BIT, - FIR_CONTROL_A_MUTE_BIT); - break; - case 1: - dai_update_bits(dai, base[i] + FIR_CONTROL_B, - FIR_CONTROL_B_MUTE_BIT, - FIR_CONTROL_B_MUTE_BIT); - break; - } - } - - k_spin_unlock(&dai->lock, key); -} - -static int dmic_trigger(struct dai *dai, int cmd, int direction) -{ - struct dmic_pdata *dmic = dai_get_drvdata(dai); - - dai_dbg(dai, "dmic_trigger()"); - - /* dai private is set in dmic_probe(), error if not set */ - if (!dmic) { - dai_err(dai, "dmic_trigger(): dai not set"); - return -EINVAL; - } - - if (direction != DAI_DIR_CAPTURE) { - dai_err(dai, "dmic_trigger(): direction != DAI_DIR_CAPTURE"); - return -EINVAL; - } - - switch (cmd) { - case COMP_TRIGGER_RELEASE: - case COMP_TRIGGER_START: - if (dmic->state == COMP_STATE_PREPARE || - dmic->state == COMP_STATE_PAUSED) { - dmic_start(dai); - } else { - dai_err(dai, "dmic_trigger(): state is not prepare or paused, dmic->state = %u", - dmic->state); - } - break; - case COMP_TRIGGER_STOP: - dmic->state = COMP_STATE_PREPARE; - dmic_stop(dai, false); - break; - case COMP_TRIGGER_PAUSE: - dmic->state = COMP_STATE_PAUSED; - dmic_stop(dai, true); - break; - } - - - return 0; -} - -/* On DMIC IRQ event trace the status register that contains the status and - * error bit fields. - */ -static void dmic_irq_handler(void *data) -{ - struct dai *dai = data; - uint32_t val0; - uint32_t val1; - - /* Trace OUTSTAT0 register */ - val0 = dai_read(dai, OUTSTAT0); - val1 = dai_read(dai, OUTSTAT1); - dai_info(dai, "dmic_irq_handler(), OUTSTAT0 = 0x%x, OUTSTAT1 = 0x%x", val0, val1); - - if (val0 & OUTSTAT0_ROR_BIT) { - dai_err(dai, "dmic_irq_handler(): full fifo A or PDM overrun"); - dai_write(dai, OUTSTAT0, val0); - dmic_stop_fifo_packers(dai, 0); - } - - if (val1 & OUTSTAT1_ROR_BIT) { - dai_err(dai, "dmic_irq_handler(): full fifo B or PDM overrun"); - dai_write(dai, OUTSTAT1, val1); - dmic_stop_fifo_packers(dai, 1); - } -} - -static int dmic_probe(struct dai *dai) -{ - int irq = dmic_irq(dai); - struct dmic_pdata *dmic; - int ret; - - dai_info(dai, "dmic_probe()"); - - if (dai_get_drvdata(dai)) - return -EEXIST; /* already created */ - - dmic = rzalloc(SOF_MEM_ZONE_RUNTIME_SHARED, 0, SOF_MEM_CAPS_RAM, sizeof(*dmic)); - if (!dmic) { - dai_err(dai, "dmic_probe(): alloc failed"); - return -ENOMEM; - } - dai_set_drvdata(dai, dmic); - - /* Common shared data for all DMIC DAI instances */ - dmic->global = platform_shared_get(&dmic_global, sizeof(dmic_global)); - - /* Set state, note there is no playback direction support */ - dmic->state = COMP_STATE_READY; - - /* register our IRQ handler */ - dmic->irq = interrupt_get_irq(irq, dmic_irq_name(dai)); - if (dmic->irq < 0) { - ret = dmic->irq; - rfree(dmic); - return ret; - } - - ret = interrupt_register(dmic->irq, dmic_irq_handler, dai); - if (ret < 0) { - dai_err(dai, "dmic failed to allocate IRQ"); - rfree(dmic); - return ret; - } - - /* Enable DMIC power */ - pm_runtime_get_sync(DMIC_POW, dai->index); - - /* Disable dynamic clock gating for dmic before touching any reg */ - pm_runtime_get_sync(DMIC_CLK, dai->index); - interrupt_enable(dmic->irq, dai); - return 0; -} - -static int dmic_remove(struct dai *dai) -{ - struct dmic_pdata *dmic = dai_get_drvdata(dai); - uint32_t active_fifos_mask = dmic->global->active_fifos_mask; - uint32_t pause_mask = dmic->global->pause_mask; - - dai_info(dai, "dmic_remove()"); - - interrupt_disable(dmic->irq, dai); - interrupt_unregister(dmic->irq, dai); - - dai_info(dai, "dmic_remove(), dmic_active_fifos_mask = 0x%x, dmic_pause_mask = 0x%x", - active_fifos_mask, pause_mask); - dai_set_drvdata(dai, NULL); - rfree(dmic); - - /* The next end tasks must be passed if another DAI FIFO still runs. - * Note: dai_put() function that calls remove() applies the spinlock - * so it is not needed here to protect access to mask bits. - */ - if (active_fifos_mask || pause_mask) - return 0; - - /* Disable DMIC clock and power */ - pm_runtime_put_sync(DMIC_CLK, dai->index); - pm_runtime_put_sync(DMIC_POW, dai->index); - return 0; -} - -static int dmic_get_handshake(struct dai *dai, int direction, int stream_id) -{ - return dai->plat_data.fifo[SOF_IPC_STREAM_CAPTURE].handshake; -} - -static int dmic_get_fifo(struct dai *dai, int direction, int stream_id) -{ - return dai->plat_data.fifo[SOF_IPC_STREAM_CAPTURE].offset; -} - -static int dmic_get_fifo_depth(struct dai *dai, int direction) -{ - return dai->plat_data.fifo[SOF_IPC_STREAM_CAPTURE].depth; -} - -const struct dai_driver dmic_driver = { - .type = SOF_DAI_INTEL_DMIC, - .uid = SOF_UUID(dmic_uuid), - .tctx = &dmic_tr, - .dma_caps = DMA_CAP_GP_LP | DMA_CAP_GP_HP, - .dma_dev = DMA_DEV_DMIC, - .ops = { - .trigger = dmic_trigger, - .set_config = dmic_set_config, - .get_hw_params = dmic_get_hw_params, - .get_handshake = dmic_get_handshake, - .get_fifo = dmic_get_fifo, - .get_fifo_depth = dmic_get_fifo_depth, - .probe = dmic_probe, - .remove = dmic_remove, - .copy = dmic_gain_ramp, - }, - .ts_ops = { - .ts_config = timestamp_dmic_config, - .ts_start = timestamp_dmic_start, - .ts_get = timestamp_dmic_get, - .ts_stop = timestamp_dmic_stop, - }, -}; - -#endif diff --git a/src/drivers/intel/dmic/dmic_computed.c b/src/drivers/intel/dmic/dmic_computed.c deleted file mode 100644 index d6b1a1e82c89..000000000000 --- a/src/drivers/intel/dmic/dmic_computed.c +++ /dev/null @@ -1,1002 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -// -// Copyright(c) 2017-2021 Intel Corporation. All rights reserved. -// -// Author: Seppo Ingalsuo - -#include -#include -#include -#include -#include - -/* Decimation filter struct */ -#include - -/* Decimation filters */ -#include - -LOG_MODULE_DECLARE(dmic_dai, CONFIG_SOF_LOG_LEVEL); - -/* Base addresses (in PDM scope) of 2ch PDM controllers and coefficient RAM. */ -static const uint32_t base[4] = {PDM0, PDM1, PDM2, PDM3}; -static const uint32_t coef_base_a[4] = {PDM0_COEFFICIENT_A, PDM1_COEFFICIENT_A, - PDM2_COEFFICIENT_A, PDM3_COEFFICIENT_A}; -static const uint32_t coef_base_b[4] = {PDM0_COEFFICIENT_B, PDM1_COEFFICIENT_B, - PDM2_COEFFICIENT_B, PDM3_COEFFICIENT_B}; - -/* This function returns a raw list of potential microphone clock and decimation - * modes for achieving requested sample rates. The search is constrained by - * decimation HW capabililies and setup parameters. The parameters such as - * microphone clock min/max and duty cycle requirements need be checked from - * used microphone component datasheet. - */ -static void find_modes(struct dai *dai, - struct decim_modes *modes, uint32_t fs, int di) -{ - struct dmic_pdata *dmic = dai_get_drvdata(dai); - int clkdiv_min; - int clkdiv_max; - int clkdiv; - int c1; - int du_min; - int du_max; - int pdmclk; - int osr; - int mfir; - int mcic; - int ioclk_test; - int osr_min = DMIC_MIN_OSR; - int j; - int i = 0; - - /* Defaults, empty result */ - modes->num_of_modes = 0; - - /* The FIFO is not requested if sample rate is set to zero. Just - * return in such case with num_of_modes as zero. - */ - if (fs == 0) - return; - - /* Override DMIC_MIN_OSR for very high sample rates, use as minimum - * the nominal clock for the high rates. - */ - if (fs >= DMIC_HIGH_RATE_MIN_FS) - osr_min = DMIC_HIGH_RATE_OSR_MIN; - - /* Check for sane pdm clock, min 100 kHz, max ioclk/2 */ - if (dmic->global->prm[di].pdmclk_max < DMIC_HW_PDM_CLK_MIN || - dmic->global->prm[di].pdmclk_max > CONFIG_DMIC_HW_IOCLK / 2) { - dai_err(dai, "find_modes(): pdm clock max not in range"); - return; - } - if (dmic->global->prm[di].pdmclk_min < DMIC_HW_PDM_CLK_MIN || - dmic->global->prm[di].pdmclk_min > dmic->global->prm[di].pdmclk_max) { - dai_err(dai, "find_modes(): pdm clock min not in range"); - return; - } - - /* Check for sane duty cycle */ - if (dmic->global->prm[di].duty_min > dmic->global->prm[di].duty_max) { - dai_err(dai, "find_modes(): duty cycle min > max"); - return; - } - if (dmic->global->prm[di].duty_min < DMIC_HW_DUTY_MIN || - dmic->global->prm[di].duty_min > DMIC_HW_DUTY_MAX) { - dai_err(dai, "find_modes(): pdm clock min not in range"); - return; - } - if (dmic->global->prm[di].duty_max < DMIC_HW_DUTY_MIN || - dmic->global->prm[di].duty_max > DMIC_HW_DUTY_MAX) { - dai_err(dai, "find_modes(): pdm clock max not in range"); - return; - } - - /* Min and max clock dividers */ - clkdiv_min = ceil_divide(CONFIG_DMIC_HW_IOCLK, dmic->global->prm[di].pdmclk_max); - clkdiv_min = MAX(clkdiv_min, DMIC_HW_CIC_DECIM_MIN); - clkdiv_max = CONFIG_DMIC_HW_IOCLK / dmic->global->prm[di].pdmclk_min; - - /* Loop possible clock dividers and check based on resulting - * oversampling ratio that CIC and FIR decimation ratios are - * feasible. The ratios need to be integers. Also the mic clock - * duty cycle need to be within limits. - */ - for (clkdiv = clkdiv_min; clkdiv <= clkdiv_max; clkdiv++) { - /* Calculate duty cycle for this clock divider. Note that - * odd dividers cause non-50% duty cycle. - */ - c1 = clkdiv >> 1; - du_min = 100 * c1 / clkdiv; - du_max = 100 - du_min; - - /* Calculate PDM clock rate and oversampling ratio. */ - pdmclk = CONFIG_DMIC_HW_IOCLK / clkdiv; - osr = pdmclk / fs; - - /* Check that OSR constraints is met and clock duty cycle does - * not exceed microphone specification. If exceed proceed to - * next clkdiv. - */ - if (osr < osr_min || du_min < dmic->global->prm[di].duty_min || - du_max > dmic->global->prm[di].duty_max) - continue; - - /* Loop FIR decimation factors candidates. If the - * integer divided decimation factors and clock dividers - * as multiplied with sample rate match the IO clock - * rate the division was exact and such decimation mode - * is possible. Then check that CIC decimation constraints - * are met. The passed decimation modes are added to array. - */ - for (j = 0; fir_list[j]; j++) { - mfir = fir_list[j]->decim_factor; - - /* Skip if previous decimation factor was the same */ - if (j != 0 && fir_list[j - 1]->decim_factor == mfir) - continue; - - mcic = osr / mfir; - ioclk_test = fs * mfir * mcic * clkdiv; - - if (ioclk_test == CONFIG_DMIC_HW_IOCLK && - mcic >= DMIC_HW_CIC_DECIM_MIN && - mcic <= DMIC_HW_CIC_DECIM_MAX && - i < DMIC_MAX_MODES) { - modes->clkdiv[i] = clkdiv; - modes->mcic[i] = mcic; - modes->mfir[i] = mfir; - i++; - } - } - } - - modes->num_of_modes = i; -} - -/* The previous raw modes list contains sane configuration possibilities. When - * there is request for both FIFOs A and B operation this function returns - * list of compatible settings. - */ -static void match_modes(struct matched_modes *c, struct decim_modes *a, - struct decim_modes *b) -{ - int16_t idx[DMIC_MAX_MODES]; - int idx_length; - int i; - int n; - int m; - - /* Check if previous search got results. */ - c->num_of_modes = 0; - if (a->num_of_modes == 0 && b->num_of_modes == 0) { - /* Nothing to do */ - return; - } - - /* Ensure that num_of_modes is sane. */ - if (a->num_of_modes > DMIC_MAX_MODES || - b->num_of_modes > DMIC_MAX_MODES) - return; - - /* Check for request only for FIFO A or B. In such case pass list for - * A or B as such. - */ - if (b->num_of_modes == 0) { - c->num_of_modes = a->num_of_modes; - for (i = 0; i < a->num_of_modes; i++) { - c->clkdiv[i] = a->clkdiv[i]; - c->mcic[i] = a->mcic[i]; - c->mfir_a[i] = a->mfir[i]; - c->mfir_b[i] = 0; /* Mark FIR B as non-used */ - } - return; - } - - if (a->num_of_modes == 0) { - c->num_of_modes = b->num_of_modes; - for (i = 0; i < b->num_of_modes; i++) { - c->clkdiv[i] = b->clkdiv[i]; - c->mcic[i] = b->mcic[i]; - c->mfir_b[i] = b->mfir[i]; - c->mfir_a[i] = 0; /* Mark FIR A as non-used */ - } - return; - } - - /* Merge a list of compatible modes */ - i = 0; - for (n = 0; n < a->num_of_modes; n++) { - /* Find all indices of values a->clkdiv[n] in b->clkdiv[] */ - idx_length = find_equal_int16(idx, b->clkdiv, a->clkdiv[n], - b->num_of_modes, 0); - for (m = 0; m < idx_length; m++) { - if (b->mcic[idx[m]] == a->mcic[n]) { - c->clkdiv[i] = a->clkdiv[n]; - c->mcic[i] = a->mcic[n]; - c->mfir_a[i] = a->mfir[n]; - c->mfir_b[i] = b->mfir[idx[m]]; - i++; - } - } - c->num_of_modes = i; - } -} - -/* Finds a suitable FIR decimation filter from the included set */ -static struct pdm_decim *get_fir(struct dai *dai, - struct dmic_configuration *cfg, int mfir) -{ - int i; - int fs; - int cic_fs; - int fir_max_length; - struct pdm_decim *fir = NULL; - - if (mfir <= 0) - return fir; - - cic_fs = CONFIG_DMIC_HW_IOCLK / cfg->clkdiv / cfg->mcic; - fs = cic_fs / mfir; - /* FIR max. length depends on available cycles and coef RAM - * length. Exceeding this length sets HW overrun status and - * overwrite of other register. - */ - fir_max_length = MIN(DMIC_HW_FIR_LENGTH_MAX, - CONFIG_DMIC_HW_IOCLK / fs / 2 - - DMIC_FIR_PIPELINE_OVERHEAD); - - i = 0; - /* Loop until NULL */ - while (fir_list[i]) { - if (fir_list[i]->decim_factor == mfir) { - if (fir_list[i]->length <= fir_max_length) { - /* Store pointer, break from loop to avoid a - * Possible other mode with lower FIR length. - */ - fir = fir_list[i]; - break; - } - dai_info(dai, "get_fir(), Note length=%d exceeds max=%d", - fir_list[i]->length, fir_max_length); - } - i++; - } - - return fir; -} - -/* Calculate scale and shift to use for FIR coefficients. Scale is applied - * before write to HW coef RAM. Shift will be programmed to HW register. - */ -static int fir_coef_scale(int32_t *fir_scale, int *fir_shift, int add_shift, - const int32_t coef[], int coef_length, int32_t gain) -{ - int32_t amax; - int32_t new_amax; - int32_t fir_gain; - int shift; - - /* Multiply gain passed from CIC with output full scale. */ - fir_gain = Q_MULTSR_32X32((int64_t)gain, DMIC_HW_SENS_Q28, - DMIC_FIR_SCALE_Q, 28, DMIC_FIR_SCALE_Q); - - /* Find the largest FIR coefficient value. */ - amax = find_max_abs_int32((int32_t *)coef, coef_length); - - /* Scale max. tap value with FIR gain. */ - new_amax = Q_MULTSR_32X32((int64_t)amax, fir_gain, 31, - DMIC_FIR_SCALE_Q, DMIC_FIR_SCALE_Q); - if (new_amax <= 0) - return -EINVAL; - - /* Get left shifts count to normalize the fractional value as 32 bit. - * We need right shifts count for scaling so need to invert. The - * difference of Q31 vs. used Q format is added to get the correct - * normalization right shift value. - */ - shift = 31 - DMIC_FIR_SCALE_Q - norm_int32(new_amax); - - /* Add to shift for coef raw Q31 format shift and store to - * configuration. Ensure range (fail should not happen with OK - * coefficient set). - */ - *fir_shift = -shift + add_shift; - if (*fir_shift < DMIC_HW_FIR_SHIFT_MIN || - *fir_shift > DMIC_HW_FIR_SHIFT_MAX) - return -EINVAL; - - /* Compensate shift into FIR coef scaler and store as Q4.20. */ - if (shift < 0) - *fir_scale = fir_gain << -shift; - else - *fir_scale = fir_gain >> shift; - - return 0; -} - -/* This function selects with a simple criteria one mode to set up the - * decimator. For the settings chosen for FIFOs A and B output a lookup - * is done for FIR coefficients from the included coefficients tables. - * For some decimation factors there may be several length coefficient sets. - * It is due to possible restruction of decimation engine cycles per given - * sample rate. If the coefficients length is exceeded the lookup continues. - * Therefore the list of coefficient set must present the filters for a - * decimation factor in decreasing length order. - * - * Note: If there is no filter available an error is returned. The parameters - * should be reviewed for such case. If still a filter is missing it should be - * added into the included set. FIR decimation with a high factor usually - * needs compromizes into specifications and is not desirable. - */ -static int select_mode(struct dai *dai, - struct dmic_configuration *cfg, - struct matched_modes *modes) -{ - int32_t g_cic; - int32_t fir_in_max; - int32_t cic_out_max; - int32_t gain_to_fir; - int16_t idx[DMIC_MAX_MODES]; - int16_t *mfir; - int mcic; - int bits_cic; - int ret; - int n; - int found = 0; - - /* If there are more than one possibilities select a mode with a preferred - * FIR decimation factor. If there are several select mode with highest - * ioclk divider to minimize microphone power consumption. The highest - * clock divisors are in the end of list so select the last of list. - * The minimum OSR criteria used in previous ensures that quality in - * the candidates should be sufficient. - */ - if (modes->num_of_modes == 0) { - dai_err(dai, "select_mode(): no modes available"); - return -EINVAL; - } - - /* Valid modes presence is indicated with non-zero decimation - * factor in 1st element. If FIR A is not used get decimation factors - * from FIR B instead. - */ - if (modes->mfir_a[0] > 0) - mfir = modes->mfir_a; - else - mfir = modes->mfir_b; - - /* Search fir_list[] decimation factors from start towards end. The found - * last configuration entry with searched decimation factor will be used. - */ - for (n = 0; fir_list[n]; n++) { - found = find_equal_int16(idx, mfir, fir_list[n]->decim_factor, - modes->num_of_modes, 0); - if (found) - break; - } - - if (!found) { - dai_err(dai, "select_mode(): No filter for decimation found"); - return -EINVAL; - } - n = idx[found - 1]; /* Option with highest clock divisor and lowest mic clock rate */ - - /* Get microphone clock and decimation parameters for used mode from - * the list. - */ - cfg->clkdiv = modes->clkdiv[n]; - cfg->mfir_a = modes->mfir_a[n]; - cfg->mfir_b = modes->mfir_b[n]; - cfg->mcic = modes->mcic[n]; - cfg->fir_a = NULL; - cfg->fir_b = NULL; - - /* Find raw FIR coefficients to match the decimation factors of FIR - * A and B. - */ - if (cfg->mfir_a > 0) { - cfg->fir_a = get_fir(dai, cfg, cfg->mfir_a); - if (!cfg->fir_a) { - dai_err(dai, "select_mode(): cannot find FIR coefficients, mfir_a = %u", - cfg->mfir_a); - return -EINVAL; - } - } - - if (cfg->mfir_b > 0) { - cfg->fir_b = get_fir(dai, cfg, cfg->mfir_b); - if (!cfg->fir_b) { - dai_err(dai, "select_mode(): cannot find FIR coefficients, mfir_b = %u", - cfg->mfir_b); - return -EINVAL; - } - } - - /* Calculate CIC shift from the decimation factor specific gain. The - * gain of HW decimator equals decimation factor to power of 5. - */ - mcic = cfg->mcic; - g_cic = mcic * mcic * mcic * mcic * mcic; - if (g_cic < 0) { - /* Erroneous decimation factor and CIC gain */ - dai_err(dai, "select_mode(): erroneous decimation factor and CIC gain"); - return -EINVAL; - } - - bits_cic = 32 - norm_int32(g_cic); - cfg->cic_shift = bits_cic - DMIC_HW_BITS_FIR_INPUT; - - /* Calculate remaining gain to FIR in Q format used for gain - * values. - */ - fir_in_max = INT_MAX_FOR_NUMBER_OF_BITS(DMIC_HW_BITS_FIR_INPUT); - if (cfg->cic_shift >= 0) - cic_out_max = g_cic >> cfg->cic_shift; - else - cic_out_max = g_cic << -cfg->cic_shift; - - gain_to_fir = (int32_t)((((int64_t)fir_in_max) << DMIC_FIR_SCALE_Q) / - cic_out_max); - - /* Calculate FIR scale and shift */ - if (cfg->mfir_a > 0) { - cfg->fir_a_length = cfg->fir_a->length; - ret = fir_coef_scale(&cfg->fir_a_scale, &cfg->fir_a_shift, - cfg->fir_a->shift, cfg->fir_a->coef, - cfg->fir_a->length, gain_to_fir); - if (ret < 0) { - /* Invalid coefficient set found, should not happen. */ - dai_err(dai, "select_mode(): invalid coefficient set found"); - return -EINVAL; - } - } else { - cfg->fir_a_scale = 0; - cfg->fir_a_shift = 0; - cfg->fir_a_length = 0; - } - - if (cfg->mfir_b > 0) { - cfg->fir_b_length = cfg->fir_b->length; - ret = fir_coef_scale(&cfg->fir_b_scale, &cfg->fir_b_shift, - cfg->fir_b->shift, cfg->fir_b->coef, - cfg->fir_b->length, gain_to_fir); - if (ret < 0) { - /* Invalid coefficient set found, should not happen. */ - dai_err(dai, "select_mode(): invalid coefficient set found"); - return -EINVAL; - } - } else { - cfg->fir_b_scale = 0; - cfg->fir_b_shift = 0; - cfg->fir_b_length = 0; - } - - return 0; -} - -/* The FIFO input packer mode (IPM) settings are somewhat different in - * HW versions. This helper function returns a suitable IPM bit field - * value to use. - */ -#ifdef DMIC_IPM_VER1 - -static void ipm_helper1(struct dmic_pdata *dmic, int *ipm, int di) -{ - int pdm[DMIC_HW_CONTROLLERS]; - int i; - - /* Loop number of PDM controllers in the configuration. If mic A - * or B is enabled then a pdm controller is marked as active for - * this DAI. - */ - for (i = 0; i < DMIC_HW_CONTROLLERS; i++) { - if (dmic->global->prm[di].pdm[i].enable_mic_a || - dmic->global->prm[di].pdm[i].enable_mic_b) - pdm[i] = 1; - else - pdm[i] = 0; - } - - /* Set IPM to match active pdm controllers. */ - *ipm = 0; - - if (pdm[0] == 0 && pdm[1] > 0) - *ipm = 1; - - if (pdm[0] > 0 && pdm[1] > 0) - *ipm = 2; -} - -#endif - -#ifdef DMIC_IPM_VER2 - -static inline void ipm_helper2(struct dmic_pdata *dmic, int source[], int *ipm, int di) -{ - int pdm[DMIC_HW_CONTROLLERS]; - int i; - int n = 0; - - for (i = 0; i < OUTCONTROLX_IPM_NUMSOURCES; i++) - source[i] = 0; - - /* Loop number of PDM controllers in the configuration. If mic A - * or B is enabled then a pdm controller is marked as active. - * The function returns in array source[] the indice of enabled - * pdm controllers to be used for IPM configuration. - */ - for (i = 0; i < DMIC_HW_CONTROLLERS; i++) { - if (dmic->global->prm[di].pdm[i].enable_mic_a || - dmic->global->prm[di].pdm[i].enable_mic_b) { - pdm[i] = 1; - source[n] = i; - n++; - } else { - pdm[i] = 0; - } - } - - /* IPM bit field is set to count of active pdm controllers. */ - *ipm = pdm[0]; - for (i = 1; i < DMIC_HW_CONTROLLERS; i++) - *ipm += pdm[i]; -} -#endif - -/* Loop number of PDM controllers in the configuration. The function - * checks if the controller should operate as stereo or mono left (A) - * or mono right (B) mode. Mono right mode is setup as channel - * swapped mono left. - */ -static int stereo_helper(struct dmic_pdata *dmic, int stereo[], int swap[]) -{ - int cnt; - int i; - int swap_check; - int ret = 0; - - for (i = 0; i < DMIC_HW_CONTROLLERS; i++) { - cnt = 0; - if (dmic->global->prm[0].pdm[i].enable_mic_a || - dmic->global->prm[1].pdm[i].enable_mic_a) - cnt++; - - if (dmic->global->prm[0].pdm[i].enable_mic_b || - dmic->global->prm[1].pdm[i].enable_mic_b) - cnt++; - - /* Set stereo mode if both mic A anc B are enabled. */ - cnt >>= 1; - stereo[i] = cnt; - - /* Swap channels if only mic B is used for mono processing. */ - swap[i] = (dmic->global->prm[0].pdm[i].enable_mic_b || - dmic->global->prm[1].pdm[i].enable_mic_b) && !cnt; - - /* Check that swap does not conflict with other DAI request */ - swap_check = dmic->global->prm[1].pdm[i].enable_mic_a || - dmic->global->prm[0].pdm[i].enable_mic_a; - - if (swap_check && swap[i]) - ret = -EINVAL; - } - return ret; -} - -static int configure_registers(struct dai *dai, - struct dmic_configuration *cfg) -{ - struct dmic_pdata *dmic = dai_get_drvdata(dai); - int stereo[DMIC_HW_CONTROLLERS]; - int swap[DMIC_HW_CONTROLLERS]; - uint32_t val; - uint32_t ref; - int32_t ci; - uint32_t cu; -#if defined(DMIC_IPM_VER1) || defined(DMIC_IPM_VER2) - int ipm; -#endif - int of0; - int of1; - int fir_decim; - int fir_length; - int length; - int edge; - int soft_reset; - int cic_mute; - int fir_mute; - int i; - int j; - int ret; - int pol_a; - int pol_b; - int di = dai->index; - int dccomp = 1; - int array_a = 0; - int array_b = 0; - int bfth = 3; /* Should be 3 for 8 entries, 1 is 2 entries */ - int th = 0; /* Used with TIE=1 */ - - /* Normal start sequence */ - soft_reset = 1; - cic_mute = 1; - fir_mute = 1; - -#ifdef DMIC_IPM_VER2 - int source[OUTCONTROLX_IPM_NUMSOURCES]; -#endif - - /* pdata is set by dmic_probe(), error if it has not been set */ - if (!dmic) { - dai_err(dai, "configure_registers(): pdata not set"); - return -EINVAL; - } - - /* Pass 2^BFTH to plat_data fifo depth. It will be used later in DMA - * configuration - */ - dai->plat_data.fifo->depth = 1 << bfth; - - dai_info(dai, "configuring registers"); - - /* OUTCONTROL0 and OUTCONTROL1 */ - of0 = (dmic->global->prm[0].fifo_bits == 32) ? 2 : 0; - -#if DMIC_HW_FIFOS > 1 - of1 = (dmic->global->prm[1].fifo_bits == 32) ? 2 : 0; -#else - of1 = 0; -#endif - -#ifdef DMIC_IPM_VER1 - if (di == 0) { - ipm_helper1(dmic, &ipm, 0); - val = OUTCONTROL0_TIE(0) | - OUTCONTROL0_SIP(0) | - OUTCONTROL0_FINIT(1) | - OUTCONTROL0_FCI(0) | - OUTCONTROL0_BFTH(bfth) | - OUTCONTROL0_OF(of0) | - OUTCONTROL0_IPM(ipm) | - OUTCONTROL0_TH(th); - dai_write(dai, OUTCONTROL0, val); - dai_dbg(dai, "configure_registers(), OUTCONTROL0 = %08x", val); - } else { - ipm_helper1(dmic, &ipm, 1); - val = OUTCONTROL1_TIE(0) | - OUTCONTROL1_SIP(0) | - OUTCONTROL1_FINIT(1) | - OUTCONTROL1_FCI(0) | - OUTCONTROL1_BFTH(bfth) | - OUTCONTROL1_OF(of1) | - OUTCONTROL1_IPM(ipm) | - OUTCONTROL1_TH(th); - dai_write(dai, OUTCONTROL1, val); - dai_dbg(dai, "configure_registers(), OUTCONTROL1 = %08x", val); - } -#endif - -#ifdef DMIC_IPM_VER2 - if (di == 0) { - ipm_helper2(dmic, source, &ipm, 0); - val = OUTCONTROL0_TIE(0) | - OUTCONTROL0_SIP(0) | - OUTCONTROL0_FINIT(1) | - OUTCONTROL0_FCI(0) | - OUTCONTROL0_BFTH(bfth) | - OUTCONTROL0_OF(of0) | - OUTCONTROL0_IPM(ipm) | - OUTCONTROL0_IPM_SOURCE_1(source[0]) | - OUTCONTROL0_IPM_SOURCE_2(source[1]) | - OUTCONTROL0_IPM_SOURCE_3(source[2]) | - OUTCONTROL0_IPM_SOURCE_4(source[3]) | - OUTCONTROL0_TH(th); - dai_write(dai, OUTCONTROL0, val); - dai_dbg(dai, "configure_registers(), OUTCONTROL0 = %08x", val); - } else { - ipm_helper2(dmic, source, &ipm, 1); - val = OUTCONTROL1_TIE(0) | - OUTCONTROL1_SIP(0) | - OUTCONTROL1_FINIT(1) | - OUTCONTROL1_FCI(0) | - OUTCONTROL1_BFTH(bfth) | - OUTCONTROL1_OF(of1) | - OUTCONTROL1_IPM(ipm) | - OUTCONTROL1_IPM_SOURCE_1(source[0]) | - OUTCONTROL1_IPM_SOURCE_2(source[1]) | - OUTCONTROL1_IPM_SOURCE_3(source[2]) | - OUTCONTROL1_IPM_SOURCE_4(source[3]) | - OUTCONTROL1_TH(th); - dai_write(dai, OUTCONTROL1, val); - dai_dbg(dai, "configure_registers(), OUTCONTROL1 = %08x", val); - } -#endif - - /* Mark enabled microphones into private data to be later used - * for starting correct parts of the HW. - */ - for (i = 0; i < DMIC_HW_CONTROLLERS; i++) { - dmic->enable[i] = (dmic->global->prm[di].pdm[i].enable_mic_b << 1) | - dmic->global->prm[di].pdm[i].enable_mic_a; - } - - - ret = stereo_helper(dmic, stereo, swap); - if (ret < 0) { - dai_err(dai, "configure_registers(): enable conflict"); - return ret; - } - - /* Note about accessing dmic_active_fifos_mask: the dai spinlock has been set in - * the calling function dmic_set_config(). - */ - for (i = 0; i < DMIC_HW_CONTROLLERS; i++) { - if (dmic->global->active_fifos_mask == 0) { - /* CIC */ - pol_a = dmic->global->prm[di].pdm[i].polarity_mic_a; - pol_b = dmic->global->prm[di].pdm[i].polarity_mic_b; - val = CIC_CONTROL_SOFT_RESET(soft_reset) | - CIC_CONTROL_CIC_START_B(0) | - CIC_CONTROL_CIC_START_A(0) | - CIC_CONTROL_MIC_B_POLARITY(pol_b) | - CIC_CONTROL_MIC_A_POLARITY(pol_a) | - CIC_CONTROL_MIC_MUTE(cic_mute) | - CIC_CONTROL_STEREO_MODE(stereo[i]); - dai_write(dai, base[i] + CIC_CONTROL, val); - dai_dbg(dai, "configure_registers(), CIC_CONTROL = %08x", val); - - val = CIC_CONFIG_CIC_SHIFT(cfg->cic_shift + 8) | - CIC_CONFIG_COMB_COUNT(cfg->mcic - 1); - dai_write(dai, base[i] + CIC_CONFIG, val); - dai_dbg(dai, "configure_registers(), CIC_CONFIG = %08x", val); - - /* Mono right channel mic usage requires swap of PDM channels - * since the mono decimation is done with only left channel - * processing active. - */ - edge = dmic->global->prm[di].pdm[i].clk_edge; - if (swap[i]) - edge = !edge; - - val = MIC_CONTROL_PDM_CLKDIV(cfg->clkdiv - 2) | - MIC_CONTROL_PDM_SKEW(dmic->global->prm[di].pdm[i].skew) | - MIC_CONTROL_CLK_EDGE(edge) | - MIC_CONTROL_PDM_EN_B(0) | - MIC_CONTROL_PDM_EN_A(0); - dai_write(dai, base[i] + MIC_CONTROL, val); - dai_dbg(dai, "configure_registers(), MIC_CONTROL = %08x", val); - } else { - /* Check that request is compatible with running configuration: - * CIC decimation factor and shift value check - */ - val = dai_read(dai, base[i] + CIC_CONFIG); - ref = CIC_CONFIG_CIC_SHIFT(cfg->cic_shift + 8) | - CIC_CONFIG_COMB_COUNT(cfg->mcic - 1); - if ((val & - (CIC_CONFIG_CIC_SHIFT_MASK | CIC_CONFIG_COMB_COUNT_MASK)) != ref) { - dai_err(dai, "configure_registers(): CIC_CONFIG %08x block", val); - return -EINVAL; - } - - /* Clock divider check */ - val = dai_read(dai, base[i] + MIC_CONTROL); - ref = MIC_CONTROL_PDM_CLKDIV(cfg->clkdiv - 2); - if ((val & MIC_CONTROL_PDM_CLKDIV_MASK) != ref) { - dai_err(dai, "configure_registers(): MIC_CONTROL %08x block", val); - return -EINVAL; - } - } - - if (di == 0) { - /* FIR A */ - fir_decim = MAX(cfg->mfir_a - 1, 0); - fir_length = MAX(cfg->fir_a_length - 1, 0); - val = FIR_CONTROL_A_START(0) | - FIR_CONTROL_A_ARRAY_START_EN(array_a) | - FIR_CONTROL_A_DCCOMP(dccomp) | - FIR_CONTROL_A_MUTE(fir_mute) | - FIR_CONTROL_A_STEREO(stereo[i]); - dai_write(dai, base[i] + FIR_CONTROL_A, val); - dai_dbg(dai, "configure_registers(), FIR_CONTROL_A = %08x", val); - - val = FIR_CONFIG_A_FIR_DECIMATION(fir_decim) | - FIR_CONFIG_A_FIR_SHIFT(cfg->fir_a_shift) | - FIR_CONFIG_A_FIR_LENGTH(fir_length); - dai_write(dai, base[i] + FIR_CONFIG_A, val); - dai_dbg(dai, "configure_registers(), FIR_CONFIG_A = %08x", val); - - val = DC_OFFSET_LEFT_A_DC_OFFS(DCCOMP_TC0); - dai_write(dai, base[i] + DC_OFFSET_LEFT_A, val); - dai_dbg(dai, "configure_registers(), DC_OFFSET_LEFT_A = %08x", val); - - val = DC_OFFSET_RIGHT_A_DC_OFFS(DCCOMP_TC0); - dai_write(dai, base[i] + DC_OFFSET_RIGHT_A, val); - dai_dbg(dai, "configure_registers(), DC_OFFSET_RIGHT_A = %08x", val); - - val = OUT_GAIN_LEFT_A_GAIN(0); - dai_write(dai, base[i] + OUT_GAIN_LEFT_A, val); - dai_dbg(dai, "configure_registers(), OUT_GAIN_LEFT_A = %08x", val); - - val = OUT_GAIN_RIGHT_A_GAIN(0); - dai_write(dai, base[i] + OUT_GAIN_RIGHT_A, val); - dai_dbg(dai, "configure_registers(), OUT_GAIN_RIGHT_A = %08x", val); - - /* Write coef RAM A with scaled coefficient in reverse order */ - length = cfg->fir_a_length; - for (j = 0; j < length; j++) { - ci = (int32_t)Q_MULTSR_32X32((int64_t)cfg->fir_a->coef[j], - cfg->fir_a_scale, 31, - DMIC_FIR_SCALE_Q, DMIC_HW_FIR_COEF_Q); - cu = FIR_COEF_A(ci); - dai_write(dai, coef_base_a[i] + ((length - j - 1) << 2), cu); - } - } - - if (di == 1) { - /* FIR B */ - fir_decim = MAX(cfg->mfir_b - 1, 0); - fir_length = MAX(cfg->fir_b_length - 1, 0); - val = FIR_CONTROL_B_START(0) | - FIR_CONTROL_B_ARRAY_START_EN(array_b) | - FIR_CONTROL_B_DCCOMP(dccomp) | - FIR_CONTROL_B_MUTE(fir_mute) | - FIR_CONTROL_B_STEREO(stereo[i]); - dai_write(dai, base[i] + FIR_CONTROL_B, val); - dai_dbg(dai, "configure_registers(), FIR_CONTROL_B = %08x", val); - - val = FIR_CONFIG_B_FIR_DECIMATION(fir_decim) | - FIR_CONFIG_B_FIR_SHIFT(cfg->fir_b_shift) | - FIR_CONFIG_B_FIR_LENGTH(fir_length); - dai_write(dai, base[i] + FIR_CONFIG_B, val); - dai_dbg(dai, "configure_registers(), FIR_CONFIG_B = %08x", val); - - val = DC_OFFSET_LEFT_B_DC_OFFS(DCCOMP_TC0); - dai_write(dai, base[i] + DC_OFFSET_LEFT_B, val); - dai_dbg(dai, "configure_registers(), DC_OFFSET_LEFT_B = %08x", val); - - val = DC_OFFSET_RIGHT_B_DC_OFFS(DCCOMP_TC0); - dai_write(dai, base[i] + DC_OFFSET_RIGHT_B, val); - dai_dbg(dai, "configure_registers(), DC_OFFSET_RIGHT_B = %08x", val); - - val = OUT_GAIN_LEFT_B_GAIN(0); - dai_write(dai, base[i] + OUT_GAIN_LEFT_B, val); - dai_dbg(dai, "configure_registers(), OUT_GAIN_LEFT_B = %08x", val); - - val = OUT_GAIN_RIGHT_B_GAIN(0); - dai_write(dai, base[i] + OUT_GAIN_RIGHT_B, val); - dai_dbg(dai, "configure_registers(), OUT_GAIN_RIGHT_B = %08x", val); - - /* Write coef RAM B with scaled coefficient in reverse order */ - length = cfg->fir_b_length; - for (j = 0; j < length; j++) { - ci = (int32_t)Q_MULTSR_32X32((int64_t)cfg->fir_b->coef[j], - cfg->fir_b_scale, 31, - DMIC_FIR_SCALE_Q, DMIC_HW_FIR_COEF_Q); - cu = FIR_COEF_B(ci); - dai_write(dai, coef_base_b[i] + ((length - j - 1) << 2), cu); - } - } - } - - return 0; -} - -/* get DMIC hw params */ -int dmic_get_hw_params_computed(struct dai *dai, struct sof_ipc_stream_params *params, int dir) -{ - struct dmic_pdata *dmic = dai_get_drvdata(dai); - int di = dai->index; - - if (!dmic) { - dai_err(dai, "dmic_get_hw_params(): dai %d not configured!", di); - return -EINVAL; - } - params->rate = dmic->global->prm[di].fifo_fs; - params->buffer_fmt = 0; - - switch (dmic->global->prm[di].num_pdm_active) { - case 1: - params->channels = 2; - break; - case 2: - params->channels = 4; - break; - default: - dai_info(dai, "dmic_get_hw_params(): not supported PDM active count %d", - dmic->global->prm[di].num_pdm_active); - return -EINVAL; - } - - switch (dmic->global->prm[di].fifo_bits) { - case 16: - params->frame_fmt = SOF_IPC_FRAME_S16_LE; - break; - case 32: - params->frame_fmt = SOF_IPC_FRAME_S32_LE; - break; - default: - dai_err(dai, "dmic_get_hw_params(): not supported format"); - return -EINVAL; - } - - return 0; -} - -int dmic_set_config_computed(struct dai *dai) -{ - struct dmic_pdata *dmic = dai_get_drvdata(dai); - struct matched_modes modes_ab; - struct dmic_configuration cfg; - struct decim_modes modes_a; - struct decim_modes modes_b; - int ret; - int di = dai->index; - - dai_info(dai, "dmic_set_config(), prm config->dmic.num_pdm_active = %u", - dmic->global->prm[di].num_pdm_active); - dai_info(dai, "dmic_set_config(), prm pdmclk_min = %u, pdmclk_max = %u", - dmic->global->prm[di].pdmclk_min, dmic->global->prm[di].pdmclk_max); - dai_info(dai, "dmic_set_config(), prm duty_min = %u, duty_max = %u", - dmic->global->prm[di].duty_min, dmic->global->prm[di].duty_max); - dai_info(dai, "dmic_set_config(), prm fifo_fs = %u, fifo_bits = %u", - dmic->global->prm[di].fifo_fs, dmic->global->prm[di].fifo_bits); - - switch (dmic->global->prm[di].fifo_bits) { - case 0: - case 16: - case 32: - break; - default: - dai_err(dai, "dmic_set_config_computed(): invalid fifo_bits"); - return -EINVAL; - } - - /* Match and select optimal decimators configuration for FIFOs A and B - * paths. This setup phase is still abstract. Successful completion - * points struct cfg to FIR coefficients and contains the scale value - * to use for FIR coefficient RAM write as well as the CIC and FIR - * shift values. - */ - find_modes(dai, &modes_a, dmic->global->prm[0].fifo_fs, di); - if (modes_a.num_of_modes == 0 && dmic->global->prm[0].fifo_fs > 0) { - dai_err(dai, "dmic_set_config(): No modes found found for FIFO A"); - return -EINVAL; - } - - find_modes(dai, &modes_b, dmic->global->prm[1].fifo_fs, di); - if (modes_b.num_of_modes == 0 && dmic->global->prm[1].fifo_fs > 0) { - dai_err(dai, "dmic_set_config(): No modes found for FIFO B"); - return -EINVAL; - } - - match_modes(&modes_ab, &modes_a, &modes_b); - ret = select_mode(dai, &cfg, &modes_ab); - if (ret < 0) { - dai_err(dai, "dmic_set_config(): select_mode() failed"); - return -EINVAL; - } - - dai_info(dai, "dmic_set_config(), cfg clkdiv = %u, mcic = %u", - cfg.clkdiv, cfg.mcic); - dai_info(dai, "dmic_set_config(), cfg mfir_a = %u, mfir_b = %u", - cfg.mfir_a, cfg.mfir_b); - dai_info(dai, "dmic_set_config(), cfg cic_shift = %u", - cfg.cic_shift); - dai_info(dai, "dmic_set_config(), cfg fir_a_shift = %u, cfg.fir_b_shift = %u", - cfg.fir_a_shift, cfg.fir_b_shift); - dai_info(dai, "dmic_set_config(), cfg fir_a_length = %u, fir_b_length = %u", - cfg.fir_a_length, cfg.fir_b_length); - - /* Determine register bits configuration from decimator configuration and - * the requested parameters. - */ - ret = configure_registers(dai, &cfg); - if (ret < 0) { - dai_err(dai, "dmic_set_config(): cannot configure registers"); - ret = -EINVAL; - } - - return ret; -} diff --git a/src/drivers/intel/dmic/dmic_nhlt.c b/src/drivers/intel/dmic/dmic_nhlt.c deleted file mode 100644 index 58f6253f7cf1..000000000000 --- a/src/drivers/intel/dmic/dmic_nhlt.c +++ /dev/null @@ -1,584 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -// -// Copyright(c) 2021 Intel Corporation. All rights reserved. -// -// Author: Seppo Ingalsuo - -#include -#include -#include -#include -#include - -LOG_MODULE_DECLARE(dmic_dai, CONFIG_SOF_LOG_LEVEL); - -/* Base addresses (in PDM scope) of 2ch PDM controllers and coefficient RAM. */ -static const uint32_t base[4] = {PDM0, PDM1, PDM2, PDM3}; -static const uint32_t coef_base_a[4] = {PDM0_COEFFICIENT_A, PDM1_COEFFICIENT_A, - PDM2_COEFFICIENT_A, PDM3_COEFFICIENT_A}; -static const uint32_t coef_base_b[4] = {PDM0_COEFFICIENT_B, PDM1_COEFFICIENT_B, - PDM2_COEFFICIENT_B, PDM3_COEFFICIENT_B}; - -#if defined DMIC_IPM_VER1 -static int nhlt_dmic_dai_params_get(struct dai *dai, uint32_t *outcontrol, - struct nhlt_pdm_ctrl_cfg **pdm_cfg, - struct nhlt_pdm_ctrl_fir_cfg **fir_cfg) -{ - struct dmic_pdata *dmic = dai_get_drvdata(dai); - int fir_stereo[2]; - int mic_swap; - - switch (OUTCONTROL0_OF_GET(outcontrol[dai->index])) { - case 0: - case 1: - dmic->dai_format = SOF_IPC_FRAME_S16_LE; - break; - case 2: - dmic->dai_format = SOF_IPC_FRAME_S32_LE; - break; - default: - dai_err(dai, "nhlt_dmic_dai_params_get(): Illegal OF bit field"); - return -EINVAL; - } - - switch (OUTCONTROL0_IPM_GET(outcontrol[dai->index])) { - case 0: - if (!fir_cfg[0]) - return -EINVAL; - - fir_stereo[0] = FIR_CONTROL_A_STEREO_GET(fir_cfg[0]->fir_control); - if (fir_stereo[0]) { - dmic->dai_channels = 2; - dmic->enable[0] = 0x3; /* PDM0 MIC A and B */ - dmic->enable[1] = 0x0; /* PDM1 none */ - - } else { - dmic->dai_channels = 1; - mic_swap = MIC_CONTROL_PDM_CLK_EDGE_GET(pdm_cfg[0]->mic_control); - dmic->enable[0] = mic_swap ? 0x2 : 0x1; /* PDM0 MIC B or MIC A */ - dmic->enable[1] = 0x0; /* PDM1 */ - } - break; - case 1: - if (!fir_cfg[1]) - return -EINVAL; - - fir_stereo[1] = FIR_CONTROL_A_STEREO_GET(fir_cfg[1]->fir_control); - if (fir_stereo[1]) { - dmic->dai_channels = 2; - dmic->enable[0] = 0x0; /* PDM0 none */ - dmic->enable[1] = 0x3; /* PDM1 MIC A and B */ - - } else { - dmic->dai_channels = 1; - dmic->enable[0] = 0x0; /* PDM0 none */ - mic_swap = MIC_CONTROL_PDM_CLK_EDGE_GET(pdm_cfg[1]->mic_control); - dmic->enable[1] = mic_swap ? 0x2 : 0x1; /* PDM1 MIC B or MIC A */ - } - break; - case 2: - if (!fir_cfg[0] || !fir_cfg[0]) - return -EINVAL; - - fir_stereo[0] = FIR_CONTROL_A_STEREO_GET(fir_cfg[0]->fir_control); - fir_stereo[1] = FIR_CONTROL_A_STEREO_GET(fir_cfg[1]->fir_control); - if (fir_stereo[0] == fir_stereo[1]) { - dmic->dai_channels = 4; - dmic->enable[0] = 0x3; /* PDM0 MIC A and B */ - dmic->enable[1] = 0x3; /* PDM1 MIC A and B */ - } else { - dai_err(dai, "nhlt_dmic_dai_params_get(): Illegal 4ch configuration"); - return -EINVAL; - } - break; - default: - dai_err(dai, "nhlt_dmic_dai_params_get(): Illegal OF bit field"); - return -EINVAL; - } - - return 0; -} -#endif - -#if defined DMIC_IPM_VER2 - -static int ipm_source_to_enable(struct dmic_pdata *dmic, struct nhlt_pdm_ctrl_cfg **pdm_cfg, - int *count, int pdm_count, int stereo, int source_pdm) -{ - int mic_swap; - - if (source_pdm >= DMIC_HW_CONTROLLERS) - return -EINVAL; - - if (*count < pdm_count) { - (*count)++; - mic_swap = MIC_CONTROL_PDM_CLK_EDGE_GET(pdm_cfg[source_pdm]->mic_control); - if (stereo) - dmic->enable[source_pdm] = 0x3; /* PDMi MIC A and B */ - else - dmic->enable[source_pdm] = mic_swap ? 0x2 : 0x1; /* PDMi MIC B or MIC A */ - } - - return 0; -} - -static int nhlt_dmic_dai_params_get(struct dai *dai, uint32_t *outcontrol, - struct nhlt_pdm_ctrl_cfg **pdm_cfg, - struct nhlt_pdm_ctrl_fir_cfg **fir_cfg) -{ - struct dmic_pdata *dmic = dai_get_drvdata(dai); - uint32_t outcontrol_val = outcontrol[dai->index]; - int num_pdm; - int source_pdm; - int ret; - int n; - bool stereo_pdm; - - switch (OUTCONTROL0_OF_GET(outcontrol_val)) { - case 0: - case 1: - dmic->dai_format = SOF_IPC_FRAME_S16_LE; - break; - case 2: - dmic->dai_format = SOF_IPC_FRAME_S32_LE; - break; - default: - dai_err(dai, "nhlt_dmic_dai_params_get(): Illegal OF bit field"); - return -EINVAL; - } - - num_pdm = OUTCONTROL0_IPM_GET(outcontrol_val); - if (num_pdm > DMIC_HW_CONTROLLERS) { - dai_err(dai, "nhlt_dmic_dai_params_get(): Illegal IPM PDM controllers count"); - return -EINVAL; - } - - stereo_pdm = 1; - - dmic->dai_channels = (stereo_pdm + 1) * num_pdm; - for (n = 0; n < DMIC_HW_CONTROLLERS; n++) - dmic->enable[n] = 0; - - n = 0; - source_pdm = OUTCONTROL0_IPM_SOURCE_1_GET(outcontrol_val); - ret = ipm_source_to_enable(dmic, pdm_cfg, &n, num_pdm, stereo_pdm, source_pdm); - if (ret) { - dai_err(dai, "nhlt_dmic_dai_params_get(): Illegal IPM_SOURCE_1"); - return -EINVAL; - } - - source_pdm = OUTCONTROL0_IPM_SOURCE_2_GET(outcontrol_val); - ret = ipm_source_to_enable(dmic, pdm_cfg, &n, num_pdm, stereo_pdm, source_pdm); - if (ret) { - dai_err(dai, "nhlt_dmic_dai_params_get(): Illegal IPM_SOURCE_2"); - return -EINVAL; - } - - source_pdm = OUTCONTROL0_IPM_SOURCE_3_GET(outcontrol_val); - ret = ipm_source_to_enable(dmic, pdm_cfg, &n, num_pdm, stereo_pdm, source_pdm); - if (ret) { - dai_err(dai, "nhlt_dmic_dai_params_get(): Illegal IPM_SOURCE_3"); - return -EINVAL; - } - - source_pdm = OUTCONTROL0_IPM_SOURCE_4_GET(outcontrol_val); - ret = ipm_source_to_enable(dmic, pdm_cfg, &n, num_pdm, stereo_pdm, source_pdm); - if (ret) { - dai_err(dai, "nhlt_dmic_dai_params_get(): Illegal IPM_SOURCE_4"); - return -EINVAL; - } - - return 0; -} -#endif - -int dmic_set_config_nhlt(struct dai *dai, const void *spec_config) -{ - struct dmic_pdata *dmic = dai_get_drvdata(dai); - struct nhlt_pdm_ctrl_cfg *pdm_cfg[DMIC_HW_CONTROLLERS_MAX]; - struct nhlt_pdm_ctrl_fir_cfg *fir_cfg_a[DMIC_HW_CONTROLLERS_MAX]; - struct nhlt_pdm_ctrl_fir_cfg *fir_cfg_b[DMIC_HW_CONTROLLERS_MAX]; - struct nhlt_pdm_fir_coeffs *fir_a[DMIC_HW_CONTROLLERS_MAX] = {NULL}; - struct nhlt_pdm_fir_coeffs *fir_b[DMIC_HW_CONTROLLERS_MAX]; - uint32_t out_control[DMIC_HW_FIFOS_MAX] = {0}; - uint32_t channel_ctrl_mask; - uint32_t fir_control; - uint32_t pdm_ctrl_mask; - uint32_t ref = 0; - uint32_t val; - const uint8_t *p = spec_config; - int num_fifos; - int num_pdm; - int fir_length_a; - int fir_length_b; - int n; - int i; - int rate_div; - int clk_div; - int comb_count; - int fir_decimation, fir_shift, fir_length; - int bf1, bf2, bf3, bf4, bf5, bf6, bf7, bf8; -#if defined DMIC_IPM_VER2 - int bf9, bf10, bf11, bf12, bf13; -#endif - int bfth; - int ret; - int p_mcic = 0; - int p_mfira = 0; - int p_mfirb = 0; - int p_clkdiv = 0; - - if (dai->index >= DMIC_HW_FIFOS_MAX) { - dai_err(dai, "dmic_set_config_nhlt(): illegal DAI index %d", dai->index); - return -EINVAL; - } - - /* Skip not used headers */ - p += sizeof(struct nhlt_dmic_gateway_attributes); - p += sizeof(struct nhlt_dmic_ts_group); - p += sizeof(struct nhlt_dmic_clock_on_delay); - - /* Channel_ctlr_mask bits indicate the FIFOs enabled*/ - channel_ctrl_mask = ((struct nhlt_dmic_channel_ctrl_mask *)p)->channel_ctrl_mask; - num_fifos = popcount(channel_ctrl_mask); /* Count set bits */ - p += sizeof(struct nhlt_dmic_channel_ctrl_mask); - dai_dbg(dai, "dmic_set_config_nhlt(): channel_ctrl_mask = %d", channel_ctrl_mask); - - /* Get OUTCONTROLx configuration */ - if (num_fifos < 1 || num_fifos > DMIC_HW_FIFOS_MAX) { - dai_err(dai, "dmic_set_config_nhlt(): illegal number of FIFOs %d", num_fifos); - return -EINVAL; - } - - for (n = 0; n < DMIC_HW_FIFOS_MAX; n++) { - if (!(channel_ctrl_mask & (1 << n))) - continue; - - val = *(uint32_t *)p; - out_control[n] = val; - bf1 = OUTCONTROL0_TIE_GET(val); - bf2 = OUTCONTROL0_SIP_GET(val); - bf3 = OUTCONTROL0_FINIT_GET(val); - bf4 = OUTCONTROL0_FCI_GET(val); - bf5 = OUTCONTROL0_BFTH_GET(val); - bf6 = OUTCONTROL0_OF_GET(val); - bf7 = OUTCONTROL0_IPM_GET(val); - bf8 = OUTCONTROL0_TH_GET(val); - dai_info(dai, "dmic_set_config_nhlt(): OUTCONTROL%d = %08x", n, out_control[n]); - dai_info(dai, " tie=%d, sip=%d, finit=%d, fci=%d", bf1, bf2, bf3, bf4); - dai_info(dai, " bfth=%d, of=%d, ipm=%d, th=%d", bf5, bf6, bf7, bf8); - if (bf5 > OUTCONTROL0_BFTH_MAX) { - dai_err(dai, "dmic_set_config_nhlt(): illegal BFTH value"); - return -EINVAL; - } - -#if defined DMIC_IPM_VER1 - ref = OUTCONTROL0_TIE(bf1) | OUTCONTROL0_SIP(bf2) | OUTCONTROL0_FINIT(bf3) | - OUTCONTROL0_FCI(bf4) | OUTCONTROL0_BFTH(bf5) | OUTCONTROL0_OF(bf6) | - OUTCONTROL0_IPM(bf7) | OUTCONTROL0_TH(bf8); -#elif defined DMIC_IPM_VER2 - bf9 = OUTCONTROL0_IPM_SOURCE_1_GET(val); - bf10 = OUTCONTROL0_IPM_SOURCE_2_GET(val); - bf11 = OUTCONTROL0_IPM_SOURCE_3_GET(val); - bf12 = OUTCONTROL0_IPM_SOURCE_4_GET(val); - bf13 = OUTCONTROL0_IPM_SOURCE_MODE_GET(val); - dai_info(dai, " ipms1=%d, ipms2=%d, ipms3=%d, ipms4=%d", bf9, bf10, bf11, bf12); - dai_info(dai, " ipms_mode=%d", bf13); - ref = OUTCONTROL0_TIE(bf1) | OUTCONTROL0_SIP(bf2) | OUTCONTROL0_FINIT(bf3) | - OUTCONTROL0_FCI(bf4) | OUTCONTROL0_BFTH(bf5) | OUTCONTROL0_OF(bf6) | - OUTCONTROL0_IPM(bf7) | OUTCONTROL0_IPM_SOURCE_1(bf9) | - OUTCONTROL0_IPM_SOURCE_2(bf10) | OUTCONTROL0_IPM_SOURCE_3(bf11) | - OUTCONTROL0_IPM_SOURCE_4(bf12) | OUTCONTROL0_TH(bf8) | - OUTCONTROL0_IPM_SOURCE_MODE(bf13); -#endif - if (ref != val) { - dai_err(dai, "dmic_set_config_nhlt(): illegal OUTCONTROL%d = 0x%08x", - n, val); - return -EINVAL; - } - - p += sizeof(uint32_t); - } - - /* Write the FIFO control registers. The clear/set of bits is the same for - * all DMIC_HW_VERSION - */ - /* Clear TIE, SIP, FCI, set FINIT, the rest of bits as such */ - val = (out_control[dai->index] & - ~(OUTCONTROL0_TIE_BIT | OUTCONTROL0_SIP_BIT | OUTCONTROL0_FCI_BIT)) | - OUTCONTROL0_FINIT_BIT; - if (dai->index == 0) - dai_write(dai, OUTCONTROL0, val); - else - dai_write(dai, OUTCONTROL1, val); - - dai_info(dai, "dmic_set_config_nhlt(): OUTCONTROL%d = %08x", dai->index, val); - - /* Pass 2^BFTH to plat_data fifo depth. It will be used later in DMA - * configuration - */ - bfth = OUTCONTROL0_BFTH_GET(val); - dai->plat_data.fifo[SOF_IPC_STREAM_CAPTURE].depth = 1 << bfth; - - /* Get PDMx registers */ - pdm_ctrl_mask = ((struct nhlt_pdm_ctrl_mask *)p)->pdm_ctrl_mask; - num_pdm = popcount(pdm_ctrl_mask); /* Count set bits */ - p += sizeof(struct nhlt_pdm_ctrl_mask); - dai_dbg(dai, "dmic_set_config_nhlt(): pdm_ctrl_mask = %d", pdm_ctrl_mask); - if (num_pdm < 1 || num_pdm > DMIC_HW_CONTROLLERS) { - dai_err(dai, "dmic_set_config_nhlt(): illegal number of PDMs %d", num_pdm); - return -EINVAL; - } - - for (n = 0; n < DMIC_HW_CONTROLLERS; n++) { - fir_cfg_a[n] = NULL; - fir_cfg_b[n] = NULL; - if (!(pdm_ctrl_mask & (1 << n))) - continue; - - dai_dbg(dai, "dmic_set_config_nhlt(): PDM%d", n); - - /* Get CIC configuration */ - pdm_cfg[n] = (struct nhlt_pdm_ctrl_cfg *)p; - p += sizeof(struct nhlt_pdm_ctrl_cfg); - - comb_count = CIC_CONFIG_COMB_COUNT_GET(pdm_cfg[n]->cic_config); - p_mcic = comb_count + 1; - clk_div = MIC_CONTROL_PDM_CLKDIV_GET(pdm_cfg[n]->mic_control); - p_clkdiv = clk_div + 2; - if (dmic->global->active_fifos_mask == 0) { - val = pdm_cfg[n]->cic_control; - bf1 = CIC_CONTROL_SOFT_RESET_GET(val); - bf2 = CIC_CONTROL_CIC_START_B_GET(val); - bf3 = CIC_CONTROL_CIC_START_A_GET(val); - bf4 = CIC_CONTROL_MIC_B_POLARITY_GET(val); - bf5 = CIC_CONTROL_MIC_A_POLARITY_GET(val); - bf6 = CIC_CONTROL_MIC_MUTE_GET(val); - bf7 = CIC_CONTROL_STEREO_MODE_GET(val); - dai_dbg(dai, "dmic_set_config_nhlt(): CIC_CONTROL = %08x", val); - dai_dbg(dai, " soft_reset=%d, cic_start_b=%d, cic_start_a=%d", - bf1, bf2, bf3); - dai_dbg(dai, " mic_b_polarity=%d, mic_a_polarity=%d, mic_mute=%d", - bf4, bf5, bf6); - ref = CIC_CONTROL_SOFT_RESET(bf1) | CIC_CONTROL_CIC_START_B(bf2) | - CIC_CONTROL_CIC_START_A(bf3) | CIC_CONTROL_MIC_B_POLARITY(bf4) | - CIC_CONTROL_MIC_A_POLARITY(bf5) | CIC_CONTROL_MIC_MUTE(bf6) | - CIC_CONTROL_STEREO_MODE(bf7); - dai_dbg(dai, " stereo_mode=%d", bf7); - if (ref != val) { - dai_err(dai, "dmic_set_config_nhlt(): illegal CIC_CONTROL = 0x%08x", - val); - return -EINVAL; - } - - /* Clear CIC_START_A and CIC_START_B, set SOF_RESET and MIC_MUTE*/ - val = (val & ~(CIC_CONTROL_CIC_START_A_BIT | CIC_CONTROL_CIC_START_A_BIT)) | - CIC_CONTROL_SOFT_RESET_BIT | CIC_CONTROL_MIC_MUTE_BIT; - dai_write(dai, base[n] + CIC_CONTROL, val); - dai_dbg(dai, "dmic_set_config_nhlt(): CIC_CONTROL = %08x", val); - - val = pdm_cfg[n]->cic_config; - bf1 = CIC_CONFIG_CIC_SHIFT_GET(val); - dai_dbg(dai, "dmic_set_config_nhlt(): CIC_CONFIG = %08x", val); - dai_dbg(dai, " cic_shift=%d, comb_count=%d", bf1, comb_count); - - /* Use CIC_CONFIG as such */ - dai_write(dai, base[n] + CIC_CONFIG, val); - dai_dbg(dai, "dmic_set_config_nhlt(): CIC_CONFIG = %08x", val); - - val = pdm_cfg[n]->mic_control; - bf1 = MIC_CONTROL_PDM_SKEW_GET(val); - bf2 = MIC_CONTROL_PDM_CLK_EDGE_GET(val); - bf3 = MIC_CONTROL_PDM_EN_B_GET(val); - bf4 = MIC_CONTROL_PDM_EN_A_GET(val); - dai_dbg(dai, "dmic_set_config_nhlt(): MIC_CONTROL = %08x", val); - dai_dbg(dai, " clkdiv=%d, skew=%d, clk_edge=%d", clk_div, bf1, bf2); - dai_dbg(dai, " en_b=%d, en_a=%d", bf3, bf4); - - /* Clear PDM_EN_A and PDM_EN_B */ - val &= ~(MIC_CONTROL_PDM_EN_A_BIT | MIC_CONTROL_PDM_EN_B_BIT); - dai_write(dai, base[n] + MIC_CONTROL, val); - dai_dbg(dai, "dmic_set_config_nhlt(): MIC_CONTROL = %08x", val); - } - - /* FIR A */ - fir_cfg_a[n] = (struct nhlt_pdm_ctrl_fir_cfg *)p; - p += sizeof(struct nhlt_pdm_ctrl_fir_cfg); - val = fir_cfg_a[n]->fir_config; - fir_length = FIR_CONFIG_A_FIR_LENGTH_GET(val); - fir_length_a = fir_length + 1; /* Need for parsing */ - fir_decimation = FIR_CONFIG_A_FIR_DECIMATION_GET(val); - p_mfira = fir_decimation + 1; - if (dai->index == 0) { - fir_shift = FIR_CONFIG_A_FIR_SHIFT_GET(val); - dai_dbg(dai, "dmic_set_config_nhlt(): FIR_CONFIG_A = %08x", val); - dai_dbg(dai, " fir_decimation=%d, fir_shift=%d, fir_length=%d", - fir_decimation, fir_shift, fir_length); - - /* Use FIR_CONFIG_A as such */ - dai_write(dai, base[n] + FIR_CONFIG_A, val); - dai_dbg(dai, "configure_registers(), FIR_CONFIG_A = %08x", val); - - val = fir_cfg_a[n]->fir_control; - bf1 = FIR_CONTROL_A_START_GET(val); - bf2 = FIR_CONTROL_A_ARRAY_START_EN_GET(val); - bf3 = FIR_CONTROL_A_DCCOMP_GET(val); - bf4 = FIR_CONTROL_A_MUTE_GET(val); - bf5 = FIR_CONTROL_A_STEREO_GET(val); - dai_dbg(dai, "dmic_set_config_nhlt(): FIR_CONTROL_A = %08x", val); - dai_dbg(dai, " start=%d, array_start_en=%d, dccomp=%d", bf1, bf2, bf3); - dai_dbg(dai, " mute=%d, stereo=%d", bf4, bf5); - ref = FIR_CONTROL_A_START(bf1) | FIR_CONTROL_A_ARRAY_START_EN(bf2) | - FIR_CONTROL_A_DCCOMP(bf3) | FIR_CONTROL_A_MUTE(bf4) | - FIR_CONTROL_A_STEREO(bf5); - - if (ref != val) { - dai_err(dai, "dmic_set_config_nhlt(): illegal FIR_CONTROL = 0x%08x", - val); - return -EINVAL; - } - - /* Clear START, set MUTE */ - fir_control = (val & ~FIR_CONTROL_A_START_BIT) | FIR_CONTROL_A_MUTE_BIT; - dai_write(dai, base[n] + FIR_CONTROL_A, fir_control); - dai_dbg(dai, "dmic_set_config_nhlt(): FIR_CONTROL_A = %08x", fir_control); - - /* Use DC_OFFSET and GAIN as such */ - val = fir_cfg_a[n]->dc_offset_left; - dai_write(dai, base[n] + DC_OFFSET_LEFT_A, val); - dai_dbg(dai, "dmic_set_config_nhlt(): DC_OFFSET_LEFT_A = %08x", val); - - val = fir_cfg_a[n]->dc_offset_right; - dai_write(dai, base[n] + DC_OFFSET_RIGHT_A, val); - dai_dbg(dai, "dmic_set_config_nhlt(): DC_OFFSET_RIGHT_A = %08x", val); - - val = fir_cfg_a[n]->out_gain_left; - dai_write(dai, base[n] + OUT_GAIN_LEFT_A, val); - dai_dbg(dai, "dmic_set_config_nhlt(): OUT_GAIN_LEFT_A = %08x", val); - - val = fir_cfg_a[n]->out_gain_right; - dai_write(dai, base[n] + OUT_GAIN_RIGHT_A, val); - dai_dbg(dai, "dmic_set_config_nhlt(): OUT_GAIN_RIGHT_A = %08x", val); - } - - /* FIR B */ - fir_cfg_b[n] = (struct nhlt_pdm_ctrl_fir_cfg *)p; - p += sizeof(struct nhlt_pdm_ctrl_fir_cfg); - val = fir_cfg_b[n]->fir_config; - fir_length = FIR_CONFIG_B_FIR_LENGTH_GET(val); - fir_length_b = fir_length + 1; /* Need for parsing */ - fir_decimation = FIR_CONFIG_B_FIR_DECIMATION_GET(val); - p_mfirb = fir_decimation + 1; - if (dai->index == 1) { - fir_shift = FIR_CONFIG_B_FIR_SHIFT_GET(val); - dai_dbg(dai, "dmic_set_config_nhlt(): FIR_CONFIG_B = %08x", val); - dai_dbg(dai, " fir_decimation=%d, fir_shift=%d, fir_length=%d", - fir_decimation, fir_shift, fir_length); - - /* Use FIR_CONFIG_B as such */ - dai_write(dai, base[n] + FIR_CONFIG_B, val); - dai_dbg(dai, "configure_registers(), FIR_CONFIG_B = %08x", val); - - val = fir_cfg_b[n]->fir_control; - bf1 = FIR_CONTROL_B_START_GET(val); - bf2 = FIR_CONTROL_B_ARRAY_START_EN_GET(val); - bf3 = FIR_CONTROL_B_DCCOMP_GET(val); - bf5 = FIR_CONTROL_B_MUTE_GET(val); - bf6 = FIR_CONTROL_B_STEREO_GET(val); - dai_dbg(dai, "dmic_set_config_nhlt(): FIR_CONTROL_B = %08x", val); - dai_dbg(dai, " start=%d, array_start_en=%d, dccomp=%d", bf1, bf2, bf3); - dai_dbg(dai, " mute=%d, stereo=%d", bf5, bf6); - - /* Clear START, set MUTE */ - fir_control = (val & ~FIR_CONTROL_B_START_BIT) | FIR_CONTROL_B_MUTE_BIT; - dai_write(dai, base[n] + FIR_CONTROL_B, fir_control); - dai_dbg(dai, "dmic_set_config_nhlt(): FIR_CONTROL_B = %08x", fir_control); - - /* Use DC_OFFSET and GAIN as such */ - val = fir_cfg_b[n]->dc_offset_left; - dai_write(dai, base[n] + DC_OFFSET_LEFT_B, val); - dai_dbg(dai, "dmic_set_config_nhlt(): DC_OFFSET_LEFT_B = %08x", val); - - val = fir_cfg_b[n]->dc_offset_right; - dai_write(dai, base[n] + DC_OFFSET_RIGHT_B, val); - dai_dbg(dai, "dmic_set_config_nhlt(): DC_OFFSET_RIGHT_B = %08x", val); - - val = fir_cfg_b[n]->out_gain_left; - dai_write(dai, base[n] + OUT_GAIN_LEFT_B, val); - dai_dbg(dai, "dmic_set_config_nhlt(): OUT_GAIN_LEFT_B = %08x", val); - - val = fir_cfg_b[n]->out_gain_right; - dai_write(dai, base[n] + OUT_GAIN_RIGHT_B, val); - dai_dbg(dai, "dmic_set_config_nhlt(): OUT_GAIN_RIGHT_B = %08x", val); - } - - /* Set up FIR coefficients RAM */ - val = pdm_cfg[n]->reuse_fir_from_pdm; - if (val == 0) { - fir_a[n] = (struct nhlt_pdm_fir_coeffs *)p; - p += sizeof(int32_t) * fir_length_a; - fir_b[n] = (struct nhlt_pdm_fir_coeffs *)p; - p += sizeof(int32_t) * fir_length_b; - } else { - val--; - if (val >= n) { - dai_err(dai, "dmic_set_config_nhlt(): Illegal FIR reuse 0x%x", val); - return -EINVAL; - } - - if (!fir_a[val]) { - dai_err(dai, "dmic_set_config_nhlt(): PDM%d FIR reuse from %d fail", - n, val); - return -EINVAL; - } - - fir_a[n] = fir_a[val]; - fir_b[n] = fir_b[val]; - } - - if (dai->index == 0) { - dai_info(dai, "dmic_set_config_nhlt(): clkdiv = %d, mcic = %d, mfir_a = %d, length = %d", - p_clkdiv, p_mcic, p_mfira, fir_length_a); - for (i = 0; i < fir_length_a; i++) - dai_write(dai, coef_base_a[n] + (i << 2), fir_a[n]->fir_coeffs[i]); - } else { - dai_info(dai, "dmic_set_config_nhlt(): clkdiv = %d, mcic = %d, mfir_b = %d, length = %d", - p_clkdiv, p_mcic, p_mfirb, fir_length_b); - for (i = 0; i < fir_length_b; i++) - dai_write(dai, coef_base_b[n] + (i << 2), fir_b[n]->fir_coeffs[i]); - } - } - - if (dai->index == 0) - ret = nhlt_dmic_dai_params_get(dai, out_control, pdm_cfg, fir_cfg_a); - else - ret = nhlt_dmic_dai_params_get(dai, out_control, pdm_cfg, fir_cfg_b); - - if (ret) - return ret; - - if (dai->index == 0) - rate_div = p_clkdiv * p_mcic * p_mfira; - else - rate_div = p_clkdiv * p_mcic * p_mfirb; - - if (!rate_div) { - dai_err(dai, "dmic_set_config_nhlt(): zero clock divide or decimation factor"); - return -EINVAL; - } - - dmic->dai_rate = CONFIG_DMIC_HW_IOCLK / rate_div; - dai_info(dai, "dmic_set_config_nhlt(): rate = %d, channels = %d, format = %d", - dmic->dai_rate, dmic->dai_channels, dmic->dai_format); - return 0; -} - -int dmic_get_hw_params_nhlt(struct dai *dai, struct sof_ipc_stream_params *params, int dir) -{ - struct dmic_pdata *dmic = dai_get_drvdata(dai); - - params->frame_fmt = dmic->dai_format; - params->channels = dmic->dai_channels; - params->rate = dmic->dai_rate; - return 0; -} diff --git a/src/drivers/intel/hda/CMakeLists.txt b/src/drivers/intel/hda/CMakeLists.txt deleted file mode 100644 index e2aac5c7b521..000000000000 --- a/src/drivers/intel/hda/CMakeLists.txt +++ /dev/null @@ -1,3 +0,0 @@ -# SPDX-License-Identifier: BSD-3-Clause - -add_local_sources(sof hda-dma.c hda.c) diff --git a/src/drivers/intel/hda/hda-dma.c b/src/drivers/intel/hda/hda-dma.c deleted file mode 100644 index 205a1b48e380..000000000000 --- a/src/drivers/intel/hda/hda-dma.c +++ /dev/null @@ -1,1088 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -// -// Copyright(c) 2018 Intel Corporation. All rights reserved. -// -// Author: Keyon Jie -// Liam Girdwood - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * HD-Audio DMA programming sequence - * - * START stream (Playback): - * 1. Host sends the DAI_CONFIG IPC with SOF_DAI_CONFIG_FLAGS_2_STEP_STOP_PAUSE flag along with - * SOF_DAI_CONFIG_FLAGS_HW_PARAMS - * 2. Host sets DGCS.RUN bit for link DMA. This step would be skipped if link DMA is already - * running with mixer-based pipelines - * 3. Host sets DGCS.RUN bit for host DMA - * 4. Host sends the STREAM_TRIG_START IPC to the DSP - * 5. FW starts the pipeline and sets DGCS.GEN bit to 1 - * - * START stream (Capture): - * 1. Host sends the DAI_CONFIG IPC with SOF_DAI_CONFIG_FLAGS_2_STEP_STOP_PAUSE flag along with - * SOF_DAI_CONFIG_FLAGS_HW_PARAMS - * 2. Host sets DGCS.RUN bit for host DMA - * 3. Host sends the STREAM_TRIG_START IPC to the DSP - * 4. FW starts the pipeline and sets DGCS.GEN bit to 1 - * 5. Host sets DGCS.RUN bit for link DMA - * - * PAUSE_PUSH an active stream (Playback): - * 1. Host sends the STREAM_TRIG_PAUSE IPC to the DSP - * 2. FW pauses the pipeline but keeps the DGCS.GEN bit set to 1 for host DMA - * 3. Host clears DGCS.RUN bit for host DMA - * 4. Host clears DGCS.RUN bit for link DMA - * 5. Host sends the DAI_CONFIG IPC with only the SOF_DAI_CONFIG_FLAGS_PAUSE command flag - * 6. FW clears the DGCS.GEN bit for link DMA - * - * PAUSE_PUSH an active stream (Capture): - * 1. Host clears DGCS.RUN bit for link DMA - * 2. Host sends the DAI_CONFIG IPC with only the SOF_DAI_CONFIG_FLAGS_PAUSE command flag - * 3. FW clears the DGCS.GEN bit for link DMA - * 4. Host sends the STREAM_TRIG_PAUSE IPC to the DSP - * 5. FW pauses the pipeline but keeps DGCS.GEN bit set to 1 for host DMA - * 6. Host clears DGCS.RUN bit for host DMA - * - * PAUSE_RELEASE a paused stream (Playback): - * 1. Host sets DGCS.RUN bit for link DMA - * 2. Host sets DGCS.RUN bit for host DMA - * 3. Host sends the STREAM_TRIG_PAUSE_RELEASE IPC to the DSP - * 4. FW releases the pipeline and sets the DGCS.GEN bit to 1 - * - * PAUSE_RELEASE a paused stream (Capture): - * 1. Host sets DGCS.RUN bit for host DMA - * 2. Host sends the STREAM_TRIG_PAUSE_RELEASE IPC to the DSP - * 3. FW releases the pipeline and sets the DGCS.GEN bit to 1 - * 4. Host sets DGCS.RUN bit for link DMA - * - * STOP an active/paused stream (Playback): - * 1. Host sends the STREAM_TRIG_STOP IPC to the DSP - * 2. FW stops the pipeline - * 3. Host clears the DGCS.RUN bit for host DMA - * 4. Host sends the PCM_FREE IPC - * 5. FW clears DGCS.GEN bit for host DMA during host component reset and checks DGCS.GBUSY bit - * to ensure DMA is idle - * 6. Host clears DGCS.RUN bit for link DMA - * 7. Host sends the DAI_CONFIG IPC with the SOF_DAI_CONFIG_FLAGS_HW_FREE flag - * 8. FW clears the DGCS.GEN bit for link DMA and checks DGCS.GBUSY bit to ensure DMA is idle - * - * STOP an active/paused stream (Capture): - * 1. Host clears DGCS.RUN bit for link DMA - * 2. Host sends the DAI_CONFIG IPC with the SOF_DAI_CONFIG_FLAGS_HW_FREE flag - * 3. FW clears the DGCS.GEN bit for link DMA and checks GBUSY bit to ensure DMA is idle - * 4. Host sends the STREAM_TRIG_STOP IPC to the DSP - * 5. FW stops the pipeline - * 6. Host clears the DGCS.RUN bit for host DMA - * 7. Host sends the PCM_FREE IPC - * 8. FW clears DGCS.GEN bit for host DMA during host component reset and checks DGCS.GBUSY bit - * to ensure DMA is idle - */ - -LOG_MODULE_REGISTER(hda_dma, CONFIG_SOF_LOG_LEVEL); - -/* ee12fa71-4579-45d7-bde2-b32c6893a122 */ -DECLARE_SOF_UUID("hda-dma", hda_dma_uuid, 0xee12fa71, 0x4579, 0x45d7, - 0xbd, 0xe2, 0xb3, 0x2c, 0x68, 0x93, 0xa1, 0x22); - -DECLARE_TR_CTX(hdma_tr, SOF_UUID(hda_dma_uuid), LOG_LEVEL_INFO); - -/* Gateway Stream Registers */ -#define DGCS 0x00 -#define DGBBA 0x04 -#define DGBS 0x08 -#define DGBFPI 0x0c /* firmware need to update this when DGCS.FWCB=1 */ -#define DGBRP 0x10 /* Read Only, read pointer */ -#define DGBWP 0x14 /* Read Only, write pointer */ -#define DGBSP 0x18 -#define DGMBS 0x1c -#define DGLLPI 0x24 -#define DGLPIBI 0x28 - -/* DGCS */ -#define DGCS_SCS BIT(31) -#define DGCS_GEN BIT(26) -#define DGCS_FWCB BIT(23) -#define DGCS_BSC BIT(11) -/* NOTE: both XRUN bits are the same, just direction is different */ -#define DGCS_BOR BIT(10) /* buffer overrun (input streams) */ -#define DGCS_BUR BIT(10) /* buffer underrun (output streams) */ -#define DGCS_BF BIT(9) /* buffer full */ -#define DGCS_BNE BIT(8) /* buffer not empty */ -#define DGCS_FIFORDY BIT(5) /* enable FIFO */ -#define DGCS_GBUSY BIT(15) /* indicates if the DMA channel is idle or not */ - -/* DGBBA */ -#define DGBBA_MASK 0xffff80 - -/* DGBS */ -#define DGBS_MASK 0xfffff0 - -#define HDA_STATE_RELEASE BIT(0) - -/* DGMBS align value */ -#define HDA_DMA_BUFFER_ALIGNMENT 0x20 -#define HDA_DMA_COPY_ALIGNMENT 0x20 -#define HDA_DMA_BUFFER_ADDRESS_ALIGNMENT 0x80 - -/* DMA host transfer timeout in microseconds */ -#define HDA_DMA_TIMEOUT 200 - -/* DMA number of buffer periods */ -#define HDA_DMA_BUFFER_PERIOD_COUNT 2 - -/* - * DMA Pointer Trace - * - * - * DMA pointer trace will output hardware DMA pointers and the BNE flag - * for n samples after stream start. - * It will also show current values on start/stop. - * Additionally values after the last copy will be output on stop. - * - * The trace will output three 32-bit values and context info, - * looking like this: - * hda-dma-ptr-trace AAAAooBC DDDDEEEE FFFFGGGG - * where: - * o - unused - * A - indicates the direction of the transfer - * B - will be 1 if BNE was set before an operation - * C - will be 1 if BNE was set after an operation - * D - hardware write pointer before an operation - * E - hardware write pointer after an operation - * F - hardware read pointer before an operation - * G - hardware read pointer after an operation - */ - -#define HDA_DMA_PTR_DBG 0 /* trace DMA pointers */ -#define HDA_DMA_PTR_DBG_NUM_CP 32 /* number of traces to output after start */ - -#if HDA_DMA_PTR_DBG - -enum hda_dbg_src { - HDA_DBG_HOST = 0, /* enables dma pointer traces for host */ - HDA_DBG_LINK, /* enables dma pointer traces for link */ - HDA_DBG_BOTH /* enables dma pointer traces for host and link */ -}; - -#define HDA_DBG_SRC HDA_DBG_BOTH - -enum hda_dbg_sample { - HDA_DBG_PRE = 0, - HDA_DBG_POST, - - HDA_DBG_MAX_SAMPLES -}; - -struct hda_dbg_data { - uint16_t cur_sample; - uint16_t last_wp[HDA_DBG_MAX_SAMPLES]; - uint16_t last_rp[HDA_DBG_MAX_SAMPLES]; - uint8_t last_bne[HDA_DBG_MAX_SAMPLES]; -}; -#endif - -struct hda_chan_data { - uint32_t stream_id; - uint32_t state; /* hda specific additional state */ - uint32_t desc_avail; - - uint32_t period_bytes; - uint32_t buffer_bytes; - - bool irq_disabled; /**< indicates whether channel is used by the - * pipeline scheduled on DMA - */ - bool l1_exit_needed; /**< indicates if l1 exit needed at ll - * scheduler post run - */ - -#if HDA_DMA_PTR_DBG - struct hda_dbg_data dbg_data; -#endif -}; - -static int hda_dma_stop_common(struct dma_chan_data *channel); - -static inline void hda_dma_inc_fp(struct dma_chan_data *chan, - uint32_t value) -{ - dma_chan_reg_write(chan, DGBFPI, value); - /* TODO: wp update, not rp should inc LLPI and LPIBI in the - * coupled input DMA - */ - dma_chan_reg_write(chan, DGLLPI, value); - dma_chan_reg_write(chan, DGLPIBI, value); -} - -static inline void hda_dma_inc_link_fp(struct dma_chan_data *chan, - uint32_t value) -{ - dma_chan_reg_write(chan, DGBFPI, value); - /* TODO: wp update should inc LLPI and LPIBI in the input DMA */ -} - -#if HDA_DMA_PTR_DBG - -static void hda_dma_dbg_count_reset(struct dma_chan_data *chan) -{ - struct hda_chan_data *hda_chan = dma_chan_get_data(chan); - - hda_chan->dbg_data.cur_sample = 0; -} - -static void hda_dma_get_dbg_vals(struct dma_chan_data *chan, - enum hda_dbg_sample sample, - enum hda_dbg_src src) -{ - struct hda_chan_data *hda_chan = dma_chan_get_data(chan); - struct hda_dbg_data *dbg_data = &hda_chan->dbg_data; - - if ((HDA_DBG_SRC == HDA_DBG_BOTH) || (src == HDA_DBG_BOTH) || - (src == HDA_DBG_SRC)) { - dbg_data->last_wp[sample] = dma_chan_reg_read(chan, DGBWP); - dbg_data->last_rp[sample] = dma_chan_reg_read(chan, DGBRP); - dbg_data->last_bne[sample] = (dma_chan_reg_read(chan, DGCS) & - DGCS_BNE) ? 1 : 0; - } -} - -#define hda_dma_ptr_trace(chan, postfix, src) \ - do { \ - struct hda_chan_data *hda_chan = dma_chan_get_data(chan); \ - struct hda_dbg_data *dbg_data = &(hda_chan)->dbg_data; \ - if ((HDA_DBG_SRC == HDA_DBG_BOTH) || (src == HDA_DBG_BOTH) || \ - (src == HDA_DBG_SRC)) { \ - if (dbg_data->cur_sample < HDA_DMA_PTR_DBG_NUM_CP) { \ - uint32_t bne = merge_4b4b(\ - dbg_data->last_bne[HDA_DBG_PRE], \ - dbg_data->last_bne[HDA_DBG_POST]); \ - uint32_t info = \ - merge_16b16b((chan)->direction, bne); \ - uint32_t wp = merge_16b16b(\ - dbg_data->last_wp[HDA_DBG_PRE], \ - dbg_data->last_wp[HDA_DBG_POST]); \ - uint32_t rp = merge_16b16b(\ - dbg_data->last_rp[HDA_DBG_PRE], \ - dbg_data->last_rp[HDA_DBG_POST]); \ - tr_info(&hdma_tr, \ - "hda-dma-ptr-trace %08X %08X %08X " \ - postfix, info, wp, rp); \ - ++dbg_data->cur_sample; \ - } \ - } \ - } while (0) -#else /* HDA_DMA_PTR_DBG */ -#define hda_dma_dbg_count_reset(...) -#define hda_dma_get_dbg_vals(...) -#define hda_dma_ptr_trace(...) -#endif - -static void hda_dma_l1_exit_notify(void *arg, enum notify_id type, void *data) -{ - struct hda_chan_data *hda_chan = arg; - - /* Force Host DMA to exit L1 if needed */ - if (hda_chan->l1_exit_needed) { - pm_runtime_put(PM_RUNTIME_HOST_DMA_L1, 0); - hda_chan->l1_exit_needed = false; - } -} - -static inline int hda_dma_is_buffer_full(struct dma_chan_data *chan) -{ - return dma_chan_reg_read(chan, DGCS) & DGCS_BF; -} - -static inline int hda_dma_is_buffer_empty(struct dma_chan_data *chan) -{ - return !(dma_chan_reg_read(chan, DGCS) & DGCS_BNE); -} - -static int hda_dma_wait_for_buffer_full(struct dma_chan_data *chan) -{ - uint64_t deadline = sof_cycle_get_64() + k_us_to_cyc_ceil64(HDA_DMA_TIMEOUT); - - while (!hda_dma_is_buffer_full(chan)) { - if (deadline < sof_cycle_get_64()) { - /* safe check in case we've got preempted after read */ - if (hda_dma_is_buffer_full(chan)) - return 0; - - tr_err(&hdma_tr, "hda-dmac: %d wait for buffer full timeout rp 0x%x wp 0x%x", - chan->dma->plat_data.id, - dma_chan_reg_read(chan, DGBRP), - dma_chan_reg_read(chan, DGBWP)); - return -ETIME; - } - } - - return 0; -} - -static int hda_dma_wait_for_buffer_empty(struct dma_chan_data *chan) -{ - uint64_t deadline = sof_cycle_get_64() + k_us_to_cyc_ceil64(HDA_DMA_TIMEOUT); - - while (!hda_dma_is_buffer_empty(chan)) { - if (deadline < sof_cycle_get_64()) { - /* safe check in case we've got preempted after read */ - if (hda_dma_is_buffer_empty(chan)) - return 0; - - tr_err(&hdma_tr, "hda-dmac: %d wait for buffer empty timeout rp 0x%x wp 0x%x", - chan->dma->plat_data.id, - dma_chan_reg_read(chan, DGBRP), - dma_chan_reg_read(chan, DGBWP)); - return -ETIME; - } - } - - return 0; -} - -static void hda_dma_post_copy(struct dma_chan_data *chan, int bytes) -{ - struct hda_chan_data *hda_chan = dma_chan_get_data(chan); - struct dma_cb_data next = { - .channel = chan, - .elem = { .size = bytes }, - }; - - notifier_event(chan, NOTIFIER_ID_DMA_COPY, - NOTIFIER_TARGET_CORE_LOCAL, &next, sizeof(next)); - - if (chan->direction == DMA_DIR_HMEM_TO_LMEM || - chan->direction == DMA_DIR_LMEM_TO_HMEM) { - /* set BFPI to let host gateway know we have read size, - * which will trigger next copy start. - */ - hda_dma_inc_fp(chan, bytes); - - /* - * Force Host DMA to exit L1 if scheduled on DMA, - * otherwise, perform L1 exit at LL scheduler powt run. - */ - if (!hda_chan->irq_disabled) - pm_runtime_put(PM_RUNTIME_HOST_DMA_L1, 0); - else if (bytes) - hda_chan->l1_exit_needed = true; - } else { - /* - * set BFPI to let link gateway know we have read size, - * which will trigger next copy start. - */ - hda_dma_inc_link_fp(chan, bytes); - } -} - -static int hda_dma_link_copy_ch(struct dma_chan_data *chan, int bytes) -{ - tr_dbg(&hdma_tr, "hda-dmac: %d channel %d -> copy 0x%x bytes", - chan->dma->plat_data.id, chan->index, bytes); - - hda_dma_get_dbg_vals(chan, HDA_DBG_PRE, HDA_DBG_LINK); - - hda_dma_post_copy(chan, bytes); - - hda_dma_get_dbg_vals(chan, HDA_DBG_POST, HDA_DBG_LINK); - hda_dma_ptr_trace(chan, "link copy", HDA_DBG_LINK); - - return 0; -} - -static int hda_dma_host_start(struct dma_chan_data *channel) -{ - struct hda_chan_data *hda_chan = dma_chan_get_data(channel); - int ret = 0; - - /* Force Host DMA to exit L1 on start */ - pm_runtime_put(PM_RUNTIME_HOST_DMA_L1, 0); - - if (!hda_chan->irq_disabled) - return ret; - - /* Register common L1 exit for all channels */ - ret = notifier_register(hda_chan, scheduler_get_data(SOF_SCHEDULE_LL_TIMER), - NOTIFIER_ID_LL_POST_RUN, hda_dma_l1_exit_notify, - NOTIFIER_FLAG_AGGREGATE); - if (ret < 0) - tr_err(&hdma_tr, "hda-dmac: %d channel %d, cannot register notification %d", - channel->dma->plat_data.id, channel->index, - ret); - - return ret; -} - -static void hda_dma_host_stop(struct dma_chan_data *channel) -{ - struct hda_chan_data *hda_chan = dma_chan_get_data(channel); - - if (!hda_chan->irq_disabled) - return; - - /* Unregister L1 exit */ - notifier_unregister(NULL, scheduler_get_data(SOF_SCHEDULE_LL_TIMER), - NOTIFIER_ID_LL_POST_RUN); -} - -/* lock should be held by caller */ -static int hda_dma_enable_unlock(struct dma_chan_data *channel) -{ - struct hda_chan_data *hda_chan; - int ret; - - tr_dbg(&hdma_tr, "hda-dmac: %d channel %d -> enable", - channel->dma->plat_data.id, channel->index); - - hda_dma_get_dbg_vals(channel, HDA_DBG_PRE, HDA_DBG_BOTH); - - /* full buffer is copied at startup */ - hda_chan = dma_chan_get_data(channel); - hda_chan->desc_avail = channel->desc_count; - - /* enable the channel */ - if (channel->direction == DMA_DIR_HMEM_TO_LMEM || - channel->direction == DMA_DIR_LMEM_TO_HMEM) { - dma_chan_reg_update_bits(channel, DGCS, DGCS_GEN | DGCS_FIFORDY, - DGCS_FIFORDY | DGCS_GEN); - pm_runtime_get(PM_RUNTIME_HOST_DMA_L1, 0); - ret = hda_dma_host_start(channel); - if (ret < 0) - return ret; - } else { - dma_chan_reg_update_bits(channel, DGCS, DGCS_GEN, DGCS_GEN); - } - - /* start link output transfer now */ - if (channel->direction == DMA_DIR_MEM_TO_DEV && - !(hda_chan->state & HDA_STATE_RELEASE)) - hda_dma_inc_link_fp(channel, hda_chan->buffer_bytes); - - hda_chan->state &= ~HDA_STATE_RELEASE; - - hda_dma_get_dbg_vals(channel, HDA_DBG_POST, HDA_DBG_BOTH); - hda_dma_ptr_trace(channel, "enable", HDA_DBG_BOTH); - - return 0; -} - -/* notify DMA to copy bytes */ -static int hda_dma_link_copy(struct dma_chan_data *channel, int bytes, - uint32_t flags) -{ - return hda_dma_link_copy_ch(channel, bytes); -} - -/* notify DMA to copy bytes */ -static int hda_dma_host_copy(struct dma_chan_data *channel, int bytes, - uint32_t flags) -{ - int ret; - - tr_dbg(&hdma_tr, "hda-dmac: %d channel %d -> copy 0x%x bytes", - channel->dma->plat_data.id, channel->index, bytes); - - hda_dma_get_dbg_vals(channel, HDA_DBG_PRE, HDA_DBG_HOST); - - /* Register Host DMA usage */ - pm_runtime_get(PM_RUNTIME_HOST_DMA_L1, 0); - - /* blocking mode copy */ - if (flags & DMA_COPY_BLOCKING) { - ret = channel->direction == DMA_DIR_HMEM_TO_LMEM ? - hda_dma_wait_for_buffer_full(channel) : - hda_dma_wait_for_buffer_empty(channel); - if (ret < 0) - return ret; - } - - hda_dma_post_copy(channel, bytes); - - hda_dma_get_dbg_vals(channel, HDA_DBG_POST, HDA_DBG_HOST); - hda_dma_ptr_trace(channel, "host copy", HDA_DBG_HOST); - - return 0; -} - -/* acquire the specific DMA channel */ -static struct dma_chan_data *hda_dma_channel_get(struct dma *dma, - unsigned int channel) -{ - k_spinlock_key_t key; - - if (channel >= dma->plat_data.channels) { - tr_err(&hdma_tr, "hda-dmac: %d invalid channel %d", - dma->plat_data.id, channel); - return NULL; - } - - key = k_spin_lock(&dma->lock); - - tr_dbg(&hdma_tr, "hda-dmac: %d channel %d -> get", dma->plat_data.id, channel); - - /* use channel if it's free */ - if (dma->chan[channel].status == COMP_STATE_INIT) { - dma->chan[channel].status = COMP_STATE_READY; - - atomic_add(&dma->num_channels_busy, 1); - - /* return channel */ - k_spin_unlock(&dma->lock, key); - return &dma->chan[channel]; - } - - /* DMAC has no free channels */ - k_spin_unlock(&dma->lock, key); - tr_err(&hdma_tr, "hda-dmac: %d no free channel %d", dma->plat_data.id, - channel); - return NULL; -} - -/* channel must not be running when this is called */ -static void hda_dma_channel_put_unlocked(struct dma_chan_data *channel) -{ - struct hda_chan_data *hda_chan = dma_chan_get_data(channel); - - /* set new state */ - channel->status = COMP_STATE_INIT; - hda_chan->state = 0; - hda_chan->period_bytes = 0; - hda_chan->buffer_bytes = 0; - - /* Make sure that all callbacks to this channel are freed */ - notifier_unregister_all(NULL, channel); -} - -/* channel must not be running when this is called */ -static void hda_dma_channel_put(struct dma_chan_data *channel) -{ - struct dma *dma = channel->dma; - k_spinlock_key_t key; - - key = k_spin_lock(&dma->lock); - hda_dma_channel_put_unlocked(channel); - k_spin_unlock(&dma->lock, key); - - atomic_sub(&dma->num_channels_busy, 1); -} - -static int hda_dma_start(struct dma_chan_data *channel) -{ - uint32_t flags; - uint32_t dgcs; - int ret = 0; - - irq_local_disable(flags); - - tr_dbg(&hdma_tr, "hda-dmac: %d channel %d -> start", - channel->dma->plat_data.id, channel->index); - - hda_dma_dbg_count_reset(channel); - - /* is channel idle, disabled and ready ? */ - dgcs = dma_chan_reg_read(channel, DGCS); - if (channel->status != COMP_STATE_PREPARE || (dgcs & DGCS_GEN)) { - ret = -EBUSY; - tr_err(&hdma_tr, "hda-dmac: %d channel %d busy. dgcs 0x%x status %d", - channel->dma->plat_data.id, - channel->index, dgcs, channel->status); - goto out; - } - - ret = hda_dma_enable_unlock(channel); - if (ret < 0) - goto out; - - channel->status = COMP_STATE_ACTIVE; - channel->core = cpu_get_id(); - -out: - irq_local_enable(flags); - return ret; -} - -static int hda_dma_release(struct dma_chan_data *channel) -{ - struct hda_chan_data *hda_chan = dma_chan_get_data(channel); - uint32_t flags; - - irq_local_disable(flags); - - tr_dbg(&hdma_tr, "hda-dmac: %d channel %d -> release", - channel->dma->plat_data.id, channel->index); - - hda_chan->state |= HDA_STATE_RELEASE; - - irq_local_enable(flags); - return 0; -} - -static int hda_dma_stop_common(struct dma_chan_data *channel) -{ - struct dai_data *dd = channel->dev_data; - struct hda_chan_data *hda_chan; - uint32_t flags, dgcs; - - irq_local_disable(flags); - - hda_dma_dbg_count_reset(channel); - hda_dma_ptr_trace(channel, "last-copy", HDA_DBG_BOTH); - hda_dma_get_dbg_vals(channel, HDA_DBG_PRE, HDA_DBG_BOTH); - - tr_dbg(&hdma_tr, "hda-dmac: %d channel %d -> stop", - channel->dma->plat_data.id, channel->index); - - /* disable the channel */ - if (channel->direction == DMA_DIR_HMEM_TO_LMEM || - channel->direction == DMA_DIR_LMEM_TO_HMEM) { - hda_dma_host_stop(channel); - - dma_chan_reg_update_bits(channel, DGCS, DGCS_FIFORDY | DGCS_GEN, 0); - } else { - dma_chan_reg_update_bits(channel, DGCS, DGCS_GEN, 0); - } - - /* check if channel is idle. No need to wait after clearing the GEN bit */ - if (dd && dd->delayed_dma_stop) { - dgcs = dma_chan_reg_read(channel, DGCS); - if (dgcs & DGCS_GBUSY) { - tr_err(&hdma_tr, "hda-dmac: %d channel %d not idle after stop", - channel->dma->plat_data.id, channel->index); - irq_local_enable(flags); - return -EBUSY; - } - } - channel->status = COMP_STATE_PREPARE; - hda_chan = dma_chan_get_data(channel); - hda_chan->state = 0; - - hda_dma_get_dbg_vals(channel, HDA_DBG_POST, HDA_DBG_BOTH); - hda_dma_ptr_trace(channel, "stop", HDA_DBG_BOTH); - - irq_local_enable(flags); - return 0; -} - -static int hda_dma_link_stop(struct dma_chan_data *channel) -{ - struct dai_data *dd = channel->dev_data; - - if (!dd) { - tr_err(&hdma_tr, "hda-dmac: %d channel %d no device data", - channel->dma->plat_data.id, channel->index); - return -EINVAL; - } - - /* - * The delayed_dma_stop flag will be set with the newer kernel and DMA will be stopped during - * reset. With older kernel, the DMA will be stopped during the stop/pause trigger. - */ - if (!dd->delayed_dma_stop) - return hda_dma_stop_common(channel); - - return 0; -} - -static int hda_dma_link_pause(struct dma_chan_data *channel) -{ - /* - * with two-step pause, DMA will be stopped when the DAI_CONFIG IPC is sent with the - * SOF_DAI_CONFIG_FLAGS_TWO_STEP_STOP flag - */ - return hda_dma_link_stop(channel); -} - -static int hda_dma_stop_delayed(struct dma_chan_data *channel) -{ - return hda_dma_stop_common(channel); -} - -/* fill in "status" with current DMA channel state and position */ -static int hda_dma_status(struct dma_chan_data *channel, - struct dma_chan_status *status, uint8_t direction) -{ - status->state = channel->status; - status->r_pos = dma_chan_reg_read(channel, DGBRP); - status->w_pos = dma_chan_reg_read(channel, DGBWP); - status->timestamp = sof_cycle_get_64(); - - return 0; -} - -/* set the DMA channel configuration, source/target address, buffer sizes */ -static int hda_dma_set_config(struct dma_chan_data *channel, - struct dma_sg_config *config) -{ - struct dma *dma = channel->dma; - struct hda_chan_data *hda_chan; - struct dma_sg_elem *sg_elem; - uint32_t buffer_addr = 0; - uint32_t period_bytes = 0; - uint32_t buffer_bytes = 0; - uint32_t flags; - uint32_t addr; - uint32_t dgcs; - int i; - int ret = 0; - - if (channel->status == COMP_STATE_ACTIVE) - return 0; - - irq_local_disable(flags); - - tr_dbg(&hdma_tr, "hda-dmac: %d channel %d -> config", dma->plat_data.id, channel->index); - - if (!config->elem_array.count) { - tr_err(&hdma_tr, "hda-dmac: %d channel %d no DMA descriptors", - dma->plat_data.id, channel->index); - ret = -EINVAL; - goto out; - } - - if ((config->direction & (DMA_DIR_MEM_TO_DEV | DMA_DIR_DEV_TO_MEM)) && - !config->irq_disabled) { - tr_err(&hdma_tr, "hda-dmac: %d channel %d HDA Link DMA doesn't support irq scheduling", - dma->plat_data.id, channel->index); - ret = -EINVAL; - goto out; - } - - /* default channel config */ - channel->direction = config->direction; - channel->desc_count = config->elem_array.count; - channel->is_scheduling_source = config->is_scheduling_source; - channel->period = config->period; - - /* validate - HDA only supports continuous elems of same size */ - for (i = 0; i < config->elem_array.count; i++) { - sg_elem = config->elem_array.elems + i; - - if (config->direction == DMA_DIR_HMEM_TO_LMEM || - config->direction == DMA_DIR_DEV_TO_MEM) - addr = sg_elem->dest; - else - addr = sg_elem->src; - - /* make sure elem is continuous */ - if (buffer_addr && (buffer_addr + buffer_bytes) != addr) { - tr_err(&hdma_tr, "hda-dmac: %d chan %d - non continuous elem", - dma->plat_data.id, channel->index); - tr_err(&hdma_tr, " addr 0x%x buffer 0x%x size 0x%x", - addr, buffer_addr, buffer_bytes); - ret = -EINVAL; - goto out; - } - - /* make sure period_bytes are constant */ - if (period_bytes && period_bytes != sg_elem->size) { - tr_err(&hdma_tr, "hda-dmac: %d chan %d - period size not constant %d", - dma->plat_data.id, - channel->index, period_bytes); - ret = -EINVAL; - goto out; - } - - /* update counters */ - period_bytes = sg_elem->size; - buffer_bytes += period_bytes; - - if (buffer_addr == 0) - buffer_addr = addr; - } - - /* buffer size must be multiple of hda dma burst size */ - if (buffer_bytes % HDA_DMA_BUFFER_ALIGNMENT) { - tr_err(&hdma_tr, "hda-dmac: %d chan %d - buffer size not DMA aligned 0x%x", - dma->plat_data.id, - channel->index, buffer_bytes); - ret = -EINVAL; - goto out; - } - /* buffer base address must be correctly aligned */ - if (buffer_addr % HDA_DMA_BUFFER_ADDRESS_ALIGNMENT) { - tr_err(&hdma_tr, "hda-dmac: %d chan %d - buffer address not DMA aligned 0x%x", - dma->plat_data.id, - channel->index, buffer_addr); - ret = -EINVAL; - goto out; - } - hda_chan = dma_chan_get_data(channel); - hda_chan->period_bytes = period_bytes; - hda_chan->buffer_bytes = buffer_bytes; - hda_chan->irq_disabled = config->irq_disabled; - - /* init channel in HW */ - dma_chan_reg_write(channel, DGBBA, buffer_addr); - dma_chan_reg_write(channel, DGBS, buffer_bytes); - - if (config->direction == DMA_DIR_LMEM_TO_HMEM || - config->direction == DMA_DIR_HMEM_TO_LMEM) - dma_chan_reg_write(channel, DGMBS, - ALIGN_UP_COMPILE(buffer_bytes, - HDA_DMA_BUFFER_ALIGNMENT)); - - /* firmware control buffer */ - dgcs = DGCS_FWCB; - - /* set DGCS.SCS bit to 1 for 16bit(2B) container - * S24_3LE stream is treated as 16bit or 8bit - * stream in host side - */ - if ((config->direction & (DMA_DIR_HMEM_TO_LMEM | DMA_DIR_DEV_TO_MEM) && - config->dest_width <= 3) || - (config->direction & (DMA_DIR_LMEM_TO_HMEM | DMA_DIR_MEM_TO_DEV) && - config->src_width <= 3)) - dgcs |= DGCS_SCS; - - /* set DGCS.FIFORDY for input/output host DMA only. It is not relevant for link DMA's */ - if (config->direction == DMA_DIR_HMEM_TO_LMEM || config->direction == DMA_DIR_LMEM_TO_HMEM) - dgcs |= DGCS_FIFORDY; - - dma_chan_reg_write(channel, DGCS, dgcs); - - channel->status = COMP_STATE_PREPARE; -out: - irq_local_enable(flags); - return ret; -} - -static int hda_dma_probe(struct dma *dma) -{ - struct hda_chan_data *hda_chan; - int i; - struct dma_chan_data *chan; - - tr_info(&hdma_tr, "hda-dmac :%d -> probe", dma->plat_data.id); - - if (dma->chan) - return -EEXIST; /* already created */ - - dma->chan = rzalloc(SOF_MEM_ZONE_RUNTIME_SHARED, 0, SOF_MEM_CAPS_RAM, - sizeof(struct dma_chan_data) * dma->plat_data.channels); - - if (!dma->chan) { - tr_err(&hdma_tr, "hda-dmac: %d channels alloc failed", - dma->plat_data.id); - return -ENOMEM; - } - - hda_chan = rzalloc(SOF_MEM_ZONE_RUNTIME_SHARED, 0, SOF_MEM_CAPS_RAM, - sizeof(struct hda_chan_data) * dma->plat_data.channels); - if (!hda_chan) { - tr_err(&hdma_tr, "hda-dma: %d private data alloc failed", - dma->plat_data.id); - goto out; - } - - /* init channel status */ - for (i = 0, chan = dma->chan; i < dma->plat_data.channels; i++, chan++) { - chan->dma = dma; - chan->index = i; - chan->status = COMP_STATE_INIT; - chan->core = DMA_CORE_INVALID; - - dma_chan_set_data(chan, hda_chan + i); - } - - /* init number of channels draining */ - atomic_init(&dma->num_channels_busy, 0); - - return 0; - -out: - rfree(dma->chan); - dma->chan = NULL; - - return -ENOMEM; -} - -static int hda_dma_remove(struct dma *dma) -{ - tr_info(&hdma_tr, "hda-dmac :%d -> remove", dma->plat_data.id); - - rfree(dma_chan_get_data(&dma->chan[0])); - - rfree(dma->chan); - dma->chan = NULL; - - return 0; -} - -static int hda_dma_link_check_xrun(struct dma_chan_data *chan) -{ - uint32_t dgcs = dma_chan_reg_read(chan, DGCS); - - if (chan->direction == DMA_DIR_MEM_TO_DEV && dgcs & DGCS_BUR) { - tr_err(&hdma_tr, "hda_dma_link_check_xrun(): underrun detected"); - dma_chan_reg_update_bits(chan, DGCS, DGCS_BUR, DGCS_BUR); - } else if (chan->direction == DMA_DIR_DEV_TO_MEM && dgcs & DGCS_BOR) { - tr_err(&hdma_tr, "hda_dma_link_check_xrun(): overrun detected"); - dma_chan_reg_update_bits(chan, DGCS, DGCS_BOR, DGCS_BOR); - } - - return 0; -} - -static int hda_dma_avail_data_size(struct dma_chan_data *chan) -{ - struct hda_chan_data *hda_chan = dma_chan_get_data(chan); - uint32_t status; - int32_t read_ptr; - int32_t write_ptr; - int size; - - status = dma_chan_reg_read(chan, DGCS); - - if (status & DGCS_BF) - return hda_chan->buffer_bytes; - - if (!(status & DGCS_BNE)) - return 0; - - read_ptr = dma_chan_reg_read(chan, DGBRP); - write_ptr = dma_chan_reg_read(chan, DGBWP); - - size = write_ptr - read_ptr; - if (size <= 0) - size += hda_chan->buffer_bytes; - - return size; -} - -static int hda_dma_free_data_size(struct dma_chan_data *chan) -{ - struct hda_chan_data *hda_chan = dma_chan_get_data(chan); - uint32_t status; - int32_t read_ptr; - int32_t write_ptr; - int size; - - status = dma_chan_reg_read(chan, DGCS); - - if (status & DGCS_BF) - return 0; - - if (!(status & DGCS_BNE)) - return hda_chan->buffer_bytes; - - read_ptr = dma_chan_reg_read(chan, DGBRP); - write_ptr = dma_chan_reg_read(chan, DGBWP); - - size = read_ptr - write_ptr; - if (size <= 0) - size += hda_chan->buffer_bytes; - - return size; -} - -static int hda_dma_data_size(struct dma_chan_data *channel, - uint32_t *avail, uint32_t *free) -{ - uint32_t flags; - int ret = 0; - - tr_dbg(&hdma_tr, "hda-dmac: %d channel %d -> get_data_size", - channel->dma->plat_data.id, channel->index); - - irq_local_disable(flags); - - ret = hda_dma_link_check_xrun(channel); - if (ret < 0) - goto unlock; - - if (channel->direction == DMA_DIR_HMEM_TO_LMEM || - channel->direction == DMA_DIR_DEV_TO_MEM) - *avail = hda_dma_avail_data_size(channel); - else - *free = hda_dma_free_data_size(channel); - -unlock: - irq_local_enable(flags); - - return ret; -} - -static int hda_dma_get_attribute(struct dma *dma, uint32_t type, - uint32_t *value) -{ - switch (type) { - case DMA_ATTR_BUFFER_ALIGNMENT: - *value = HDA_DMA_BUFFER_ALIGNMENT; - break; - case DMA_ATTR_COPY_ALIGNMENT: - *value = HDA_DMA_COPY_ALIGNMENT; - break; - case DMA_ATTR_BUFFER_ADDRESS_ALIGNMENT: - *value = HDA_DMA_BUFFER_ADDRESS_ALIGNMENT; - break; - case DMA_ATTR_BUFFER_PERIOD_COUNT: - *value = HDA_DMA_BUFFER_PERIOD_COUNT; - break; - default: - return -EINVAL; - } - - return 0; -} - -static int hda_dma_interrupt(struct dma_chan_data *channel, - enum dma_irq_cmd cmd) -{ - /* HDA-DMA doesn't support interrupts */ - return -EINVAL; -} - -const struct dma_ops hda_host_dma_ops = { - .channel_get = hda_dma_channel_get, - .channel_put = hda_dma_channel_put, - .start = hda_dma_start, - .stop_delayed = hda_dma_stop_delayed, - .copy = hda_dma_host_copy, - .status = hda_dma_status, - .set_config = hda_dma_set_config, - .probe = hda_dma_probe, - .remove = hda_dma_remove, - .get_data_size = hda_dma_data_size, - .get_attribute = hda_dma_get_attribute, - .interrupt = hda_dma_interrupt, -}; - -const struct dma_ops hda_link_dma_ops = { - .channel_get = hda_dma_channel_get, - .channel_put = hda_dma_channel_put, - .start = hda_dma_start, - .stop = hda_dma_link_stop, - .stop_delayed = hda_dma_stop_delayed, - .copy = hda_dma_link_copy, - .pause = hda_dma_link_pause, - .release = hda_dma_release, - .status = hda_dma_status, - .set_config = hda_dma_set_config, - .probe = hda_dma_probe, - .remove = hda_dma_remove, - .get_data_size = hda_dma_data_size, - .get_attribute = hda_dma_get_attribute, - .interrupt = hda_dma_interrupt, -}; diff --git a/src/drivers/intel/hda/hda.c b/src/drivers/intel/hda/hda.c deleted file mode 100644 index 88d8304ac47d..000000000000 --- a/src/drivers/intel/hda/hda.c +++ /dev/null @@ -1,131 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -// -// Copyright(c) 2018 Intel Corporation. All rights reserved. -// -// Author: Marcin Maka - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -LOG_MODULE_REGISTER(hda_dai, CONFIG_SOF_LOG_LEVEL); - -/* bc9ebe20-4577-41bb-9eed-d0cb236328da */ -DECLARE_SOF_UUID("hda-dai", hda_uuid, 0xbc9ebe20, 0x4577, 0x41bb, - 0x9e, 0xed, 0xd0, 0xcb, 0x23, 0x63, 0x28, 0xda); - -DECLARE_TR_CTX(hda_tr, SOF_UUID(hda_uuid), LOG_LEVEL_INFO); - -static int hda_trigger(struct dai *dai, int cmd, int direction) -{ - return 0; -} - -static int hda_set_config(struct dai *dai, struct ipc_config_dai *common_config, - const void *spec_config) -{ - struct hda_pdata *hda = dai_get_drvdata(dai); - const struct sof_ipc_dai_config *dai_config = spec_config; - const struct sof_ipc_dai_hda_params *params = &dai_config->hda; - - /* no params in blob which only includes lp mode setting */ - if (common_config->is_config_blob) - return 0; - - dai_info(dai, "hda_set_config(): channels %u rate %u", params->channels, - params->rate); - - if (params->channels) - hda->params.channels = params->channels; - if (params->rate) - hda->params.rate = params->rate; - - return 0; -} - -/* get HDA hw params */ -static int hda_get_hw_params(struct dai *dai, - struct sof_ipc_stream_params *params, int dir) -{ - struct hda_pdata *hda = dai_get_drvdata(dai); - - dai_info(dai, "hda_get_hw_params(): channels %u rate %u", hda->params.channels, - hda->params.rate); - - params->rate = hda->params.rate; - params->channels = hda->params.channels; - - /* 0 means variable */ - params->buffer_fmt = 0; - params->frame_fmt = 0; - - return 0; -} - -static int hda_probe(struct dai *dai) -{ - struct hda_pdata *hda; - - dai_info(dai, "hda_probe()"); - - if (dai_get_drvdata(dai)) - return -EEXIST; - - hda = rzalloc(SOF_MEM_ZONE_RUNTIME_SHARED, 0, SOF_MEM_CAPS_RAM, sizeof(*hda)); - if (!hda) { - dai_err(dai, "hda_probe() error: alloc failed"); - return -ENOMEM; - } - dai_set_drvdata(dai, hda); - - return 0; -} - -static int hda_remove(struct dai *dai) -{ - dai_info(dai, "hda_remove()"); - - rfree(dai_get_drvdata(dai)); - dai_set_drvdata(dai, NULL); - - return 0; -} - -static int hda_get_handshake(struct dai *dai, int direction, int stream_id) -{ - return 0; -} - -static int hda_get_fifo(struct dai *dai, int direction, int stream_id) -{ - return 0; -} - -const struct dai_driver hda_driver = { - .type = SOF_DAI_INTEL_HDA, - .uid = SOF_UUID(hda_uuid), - .tctx = &hda_tr, - .dma_caps = DMA_CAP_HDA, - .dma_dev = DMA_DEV_HDA, - .ops = { - .trigger = hda_trigger, - .set_config = hda_set_config, - .get_hw_params = hda_get_hw_params, - .get_handshake = hda_get_handshake, - .get_fifo = hda_get_fifo, - .probe = hda_probe, - .remove = hda_remove, - }, - .ts_ops = { - .ts_config = timestamp_hda_config, - .ts_start = timestamp_hda_start, - .ts_get = timestamp_hda_get, - .ts_stop = timestamp_hda_stop, - }, -}; diff --git a/src/drivers/intel/pmc-ipc.c b/src/drivers/intel/pmc-ipc.c deleted file mode 100644 index 7c9d1b5603af..000000000000 --- a/src/drivers/intel/pmc-ipc.c +++ /dev/null @@ -1,149 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -// -// Copyright(c) 2016 Intel Corporation. All rights reserved. -// -// Author: Liam Girdwood - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* private data for IPC */ -struct intel_ipc_pmc_data { - uint32_t msg_l; - uint32_t msg_h; - uint32_t pending; -}; - -static struct intel_ipc_pmc_data *_pmc; - -static void do_cmd(void) -{ - uint32_t ipcsc; - uint32_t status = 0; - - tr_info(&ipc_tr, "pmc: tx -> 0x%x", _pmc->msg_l); - - _pmc->pending = 0; - - /* clear BUSY bit and set DONE bit - accept new messages */ - ipcsc = shim_read(SHIM_IPCSCH); - ipcsc &= ~SHIM_IPCSCH_BUSY; - ipcsc |= SHIM_IPCSCH_DONE | status; - shim_write(SHIM_IPCSCH, ipcsc); - - /* unmask busy interrupt */ - shim_write(SHIM_IMRLPESC, - shim_read(SHIM_IMRLPESC) & ~SHIM_IMRLPESC_BUSY); -} - -/* process current message */ -int pmc_process_msg_queue(void) -{ - if (_pmc->pending) - do_cmd(); - return 0; -} - -static void do_notify(void) -{ - tr_info(&ipc_tr, "pmc: not rx"); - - /* clear DONE bit */ - shim_write(SHIM_IPCLPESCH, - shim_read(SHIM_IPCLPESCH) & ~SHIM_IPCLPESCH_DONE); - - /* unmask Done interrupt */ - shim_write(SHIM_IMRLPESC, - shim_read(SHIM_IMRLPESC) & ~SHIM_IMRLPESC_DONE); -} - -static void irq_handler(void *arg) -{ - uint32_t isrlpesc; - - /* Interrupt arrived, check src */ - isrlpesc = shim_read(SHIM_ISRLPESC); - - tr_dbg(&ipc_tr, "pmc: irq isrlpesc 0x%x", isrlpesc); - - if (isrlpesc & SHIM_ISRLPESC_DONE) { - /* Mask Done interrupt before return */ - shim_write(SHIM_IMRLPESC, - shim_read(SHIM_IMRLPESC) | SHIM_IMRLPESC_DONE); - interrupt_clear(IRQ_NUM_EXT_PMC); - do_notify(); - } - - if (isrlpesc & SHIM_ISRLPESC_BUSY) { - /* Mask Busy interrupt before return */ - shim_write(SHIM_IMRLPESC, - shim_read(SHIM_IMRLPESC) | SHIM_IMRLPESC_BUSY); - interrupt_clear(IRQ_NUM_EXT_PMC); - - /* place message in Q and process later */ - _pmc->msg_l = shim_read(SHIM_IPCSCL); - _pmc->msg_h = shim_read(SHIM_IPCSCH); - _pmc->pending = 1; - } -} - -int ipc_pmc_send_msg(uint32_t message) -{ - uint32_t ipclpesch; - int ret; - - tr_dbg(&ipc_tr, "pmc: msg tx -> 0x%x", message); - - ipclpesch = shim_read(SHIM_IPCLPESCH); - - /* we can only send new messages if the SC is not busy */ - if (ipclpesch & SHIM_IPCLPESCH_BUSY) { - tr_err(&ipc_tr, "pmc: busy 0x%x", ipclpesch); - return -EAGAIN; - } - - /* send the new message */ - shim_write(SHIM_IPCLPESCL, 0); - shim_write(SHIM_IPCLPESCH, SHIM_IPCLPESCH_BUSY | message); - - /* wait for idle status */ - ret = poll_for_register_delay(SHIM_BASE + SHIM_IPCLPESCH, - SHIM_IPCLPESCH_BUSY, 0, - PLATFORM_LPE_DELAY); - - /* did command succeed */ - if (ret < 0) { - tr_err(&ipc_tr, "pmc: command 0x%x failed", message); - return -EINVAL; - } - - return 0; -} - -int platform_ipc_pmc_init(void) -{ - uint32_t imrlpesc; - - /* init ipc data */ - _pmc = rmalloc(SOF_MEM_ZONE_SYS, 0, SOF_MEM_CAPS_RAM, - sizeof(struct intel_ipc_pmc_data)); - - /* configure interrupt */ - interrupt_register(IRQ_NUM_EXT_PMC, irq_handler, _pmc); - interrupt_enable(IRQ_NUM_EXT_PMC, _pmc); - - /* Unmask Busy and Done interrupts */ - imrlpesc = shim_read(SHIM_IMRLPESC); - imrlpesc &= ~(SHIM_IMRLPESC_BUSY | SHIM_IMRLPESC_DONE); - shim_write(SHIM_IMRLPESC, imrlpesc); - - return 0; -} diff --git a/src/drivers/intel/ssp/CMakeLists.txt b/src/drivers/intel/ssp/CMakeLists.txt deleted file mode 100644 index 566aae053fd8..000000000000 --- a/src/drivers/intel/ssp/CMakeLists.txt +++ /dev/null @@ -1,7 +0,0 @@ -# SPDX-License-Identifier: BSD-3-Clause - -add_local_sources(sof ssp.c) - -if(CONFIG_INTEL_MCLK) - add_local_sources(sof mn.c) -endif() diff --git a/src/drivers/intel/ssp/mn.c b/src/drivers/intel/ssp/mn.c deleted file mode 100644 index 2dcecf6ff240..000000000000 --- a/src/drivers/intel/ssp/mn.c +++ /dev/null @@ -1,627 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -// -// Copyright(c) 2020 Intel Corporation. All rights reserved. -// -// Author: Janusz Jankowski - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include - -/* tracing */ -LOG_MODULE_REGISTER(mn, CONFIG_SOF_LOG_LEVEL); - -/* fa3b3763-759c-4c64-82b6-3dd239c89f58 */ -DECLARE_SOF_UUID("mn", mn_uuid, 0xfa3b3763, 0x759c, 0x4c64, - 0x82, 0xb6, 0x3d, 0xd2, 0x39, 0xc8, 0x9f, 0x58); - -DECLARE_TR_CTX(mn_tr, SOF_UUID(mn_uuid), LOG_LEVEL_INFO); - -#if CONFIG_INTEL_MN -/** \brief BCLKs can be driven by multiple sources - M/N or XTAL directly. - * Even in the case of M/N, the actual clock source can be XTAL, - * Audio cardinal clock (24.576) or 96 MHz PLL. - * The MN block is not really the source of clocks, but rather - * an intermediate component. - * Input for source is shared by all outputs coming from that source - * and once it's in use, it can be adjusted only with dividers. - * In order to change input, the source should not be in use, that's why - * it's necessary to keep track of BCLKs sources to know when it's safe - * to change shared input clock. - */ -enum bclk_source { - MN_BCLK_SOURCE_NONE = 0, /**< port is not using any clock */ - MN_BCLK_SOURCE_MN, /**< port is using clock driven by M/N */ - MN_BCLK_SOURCE_XTAL, /**< port is using XTAL directly */ -}; -#endif - -struct mn { - /**< keep track of which MCLKs are in use to know when it's safe to - * change shared clock - */ - int mclk_sources_ref[DAI_NUM_SSP_MCLK]; - int mclk_rate[DAI_NUM_SSP_MCLK]; - int mclk_source_clock; - -#if CONFIG_INTEL_MN - enum bclk_source bclk_sources[(DAI_NUM_SSP_BASE + DAI_NUM_SSP_EXT)]; - int bclk_source_mn_clock; -#endif - - struct k_spinlock lock; /**< lock mechanism */ -}; - -static SHARED_DATA struct mn mn_shared; - -void mn_init(struct sof *sof) -{ - int i; - - sof->mn = platform_shared_get(&mn_shared, sizeof(mn_shared)); - - sof->mn->mclk_source_clock = 0; - - /* initialize the ref counts for mclk */ - for (i = 0; i < DAI_NUM_SSP_MCLK; i++) { - sof->mn->mclk_sources_ref[i] = 0; - sof->mn->mclk_rate[i] = 0; - } - -#if CONFIG_INTEL_MN - for (i = 0; i < ARRAY_SIZE(sof->mn->bclk_sources); i++) - sof->mn->bclk_sources[i] = MN_BCLK_SOURCE_NONE; -#endif - - k_spinlock_init(&sof->mn->lock); -} - -/** - * \brief Checks if given clock is used as source for any MCLK. - * - * \return true if any port use given clock source, false otherwise. - */ -static inline bool is_mclk_source_in_use(void) -{ - struct mn *mn = mn_get(); - bool ret = false; - int i; - - for (i = 0; i < ARRAY_SIZE(mn->mclk_sources_ref); i++) { - if (mn->mclk_sources_ref[i] > 0) { - ret = true; - break; - } - } - - return ret; -} - -/** - * \brief Configures source clock for MCLK. - * All MCLKs share the same source, so it should be changed - * only if there are no other ports using it already. - * \param[in] mclk_rate main clock frequency. - * \return 0 on success, error code otherwise. - */ -static inline int setup_initial_mclk_source(uint32_t mclk_id, - uint32_t mclk_rate) -{ - struct mn *mn = mn_get(); - int i; - int clk_index = -1; - uint32_t mdivc; - int ret = 0; - - if (mclk_id >= DAI_NUM_SSP_MCLK) { - tr_err(&mn_tr, "can't configure MCLK %d, only %d mclk[s] existed!", - mclk_id, DAI_NUM_SSP_MCLK); - ret = -EINVAL; - goto out; - } - - /* searching the smallest possible mclk source */ - for (i = 0; i <= MAX_SSP_FREQ_INDEX; i++) { - if (ssp_freq[i].freq % mclk_rate == 0) { - clk_index = i; - break; - } - } - - if (clk_index < 0) { - tr_err(&mn_tr, "MCLK %d, no valid source", mclk_rate); - ret = -EINVAL; - goto out; - } - - mn->mclk_source_clock = clk_index; - - mdivc = mn_reg_read(MN_MDIVCTRL, mclk_id); - - /* enable MCLK divider */ - mdivc |= MN_MDIVCTRL_M_DIV_ENABLE(mclk_id); - - /* clear source mclk clock - bits 17-16 */ - mdivc &= ~MCDSS(MN_SOURCE_CLKS_MASK); - - /* select source clock */ - mdivc |= MCDSS(ssp_freq_sources[clk_index]); - - mn_reg_write(MN_MDIVCTRL, mclk_id, mdivc); - - mn->mclk_sources_ref[mclk_id]++; -out: - - return ret; -} - -/** - * \brief Checks if requested MCLK can be achieved with current source. - * \param[in] mclk_rate main clock frequency. - * \return 0 on success, error code otherwise. - */ -static inline int check_current_mclk_source(uint16_t mclk_id, uint32_t mclk_rate) -{ - struct mn *mn = mn_get(); - uint32_t mdivc; - int ret = 0; - - tr_info(&mn_tr, "MCLK %d, source = %d", mclk_rate, mn->mclk_source_clock); - - if (ssp_freq[mn->mclk_source_clock].freq % mclk_rate != 0) { - tr_err(&mn_tr, "MCLK %d, no valid configuration for already selected source = %d", - mclk_rate, mn->mclk_source_clock); - ret = -EINVAL; - } - - /* if the mclk is already used, can't change its divider, just increase ref count */ - if (mn->mclk_sources_ref[mclk_id] > 0) { - if (mn->mclk_rate[mclk_id] != mclk_rate) { - tr_err(&mn_tr, "Can't set MCLK %d to %d, it is already configured to %d", - mclk_id, mclk_rate, mn->mclk_rate[mclk_id]); - return -EINVAL; - } - - mn->mclk_sources_ref[mclk_id]++; - } else { - mdivc = mn_reg_read(MN_MDIVCTRL, mclk_id); - - /* enable MCLK divider */ - mdivc |= MN_MDIVCTRL_M_DIV_ENABLE(mclk_id); - mn_reg_write(MN_MDIVCTRL, mclk_id, mdivc); - - mn->mclk_sources_ref[mclk_id]++; - } - - return ret; -} - -/** - * \brief Sets MCLK divider to given value. - * \param[in] mclk_id ID of MCLK. - * \param[in] mdivr_val divider value. - * \return 0 on success, error code otherwise. - */ -static inline int set_mclk_divider(uint16_t mclk_id, uint32_t mdivr_val) -{ - uint32_t mdivr; - - tr_info(&mn_tr, "mclk_id %d mdivr_val %d", mclk_id, mdivr_val); - switch (mdivr_val) { - case 1: - mdivr = 0x00000fff; /* bypass divider for MCLK */ - break; - case 2 ... 8: - mdivr = mdivr_val - 2; /* 1/n */ - break; - default: - tr_err(&mn_tr, "invalid mdivr_val %d", mdivr_val); - return -EINVAL; - } - - mn_reg_write(MN_MDIVR(mclk_id), mclk_id, mdivr); - return 0; -} - -int mn_set_mclk(uint16_t mclk_id, uint32_t mclk_rate) -{ - struct mn *mn = mn_get(); - k_spinlock_key_t key; - int ret = 0; - - if (mclk_id >= DAI_NUM_SSP_MCLK) { - tr_err(&mn_tr, "mclk ID (%d) >= %d", mclk_id, DAI_NUM_SSP_MCLK); - return -EINVAL; - } - - key = k_spin_lock(&mn->lock); - - if (is_mclk_source_in_use()) - ret = check_current_mclk_source(mclk_id, mclk_rate); - else - ret = setup_initial_mclk_source(mclk_id, mclk_rate); - - if (ret < 0) - goto out; - - tr_info(&mn_tr, "mclk_rate %d, mclk_source_clock %d", - mclk_rate, mn->mclk_source_clock); - - ret = set_mclk_divider(mclk_id, - ssp_freq[mn->mclk_source_clock].freq / - mclk_rate); - if (!ret) - mn->mclk_rate[mclk_id] = mclk_rate; - -out: - - k_spin_unlock(&mn->lock, key); - - return ret; -} - -int mn_set_mclk_blob(uint32_t mdivc, uint32_t mdivr) -{ - mn_reg_write(MN_MDIVCTRL, 0, mdivc); - mn_reg_write(MN_MDIVR(0), 0, mdivr); - - return 0; -} - -void mn_release_mclk(uint32_t mclk_id) -{ - struct mn *mn = mn_get(); - uint32_t mdivc; - k_spinlock_key_t key; - - key = k_spin_lock(&mn->lock); - - mn->mclk_sources_ref[mclk_id]--; - - /* disable MCLK divider if nobody use it */ - if (!mn->mclk_sources_ref[mclk_id]) { - mdivc = mn_reg_read(MN_MDIVCTRL, mclk_id); - - mdivc &= ~MN_MDIVCTRL_M_DIV_ENABLE(mclk_id); - mn_reg_write(MN_MDIVCTRL, mclk_id, mdivc); - } - - /* release the clock source if all mclks are released */ - if (!is_mclk_source_in_use()) { - mdivc = mn_reg_read(MN_MDIVCTRL, mclk_id); - - /* clear source mclk clock - bits 17-16 */ - mdivc &= ~MCDSS(MN_SOURCE_CLKS_MASK); - - mn_reg_write(MN_MDIVCTRL, mclk_id, mdivc); - - mn->mclk_source_clock = 0; - } - k_spin_unlock(&mn->lock, key); -} - -#if CONFIG_INTEL_MN -/** - * \brief Finds valid M/(N * SCR) values for given frequencies. - * \param[in] freq SSP clock frequency. - * \param[in] bclk Bit clock frequency. - * \param[out] out_scr_div SCR divisor. - * \param[out] out_m M value of M/N divider. - * \param[out] out_n N value of M/N divider. - * \return true if found suitable values, false otherwise. - */ -static bool find_mn(uint32_t freq, uint32_t bclk, - uint32_t *out_scr_div, uint32_t *out_m, uint32_t *out_n) -{ - uint32_t m, n, mn_div; - uint32_t scr_div = freq / bclk; - - tr_info(&mn_tr, "find_mn for freq %d bclk %d", freq, bclk); - /* check if just SCR is enough */ - if (freq % bclk == 0 && scr_div < (SSCR0_SCR_MASK >> 8) + 1) { - *out_scr_div = scr_div; - *out_m = 1; - *out_n = 1; - - return true; - } - - /* M/(N * scr_div) has to be less than 1/2 */ - if ((bclk * 2) >= freq) - return false; - - /* odd SCR gives lower duty cycle */ - if (scr_div > 1 && scr_div % 2 != 0) - --scr_div; - - /* clamp to valid SCR range */ - scr_div = MIN(scr_div, (SSCR0_SCR_MASK >> 8) + 1); - - /* find highest even divisor */ - while (scr_div > 1 && freq % scr_div != 0) - scr_div -= 2; - - /* compute M/N with smallest dividend and divisor */ - mn_div = gcd(bclk, freq / scr_div); - - m = bclk / mn_div; - n = freq / scr_div / mn_div; - - /* M/N values can be up to 24 bits */ - if (n & (~0xffffff)) - return false; - - *out_scr_div = scr_div; - *out_m = m; - *out_n = n; - - tr_info(&mn_tr, "find_mn m %d n %d", m, n); - return true; -} - -/** - * \brief Finds index of clock valid for given BCLK rate. - * Clock that can use just SCR is preferred. - * M/N other than 1/1 is used only if there are no other possibilities. - * \param[in] bclk Bit clock frequency. - * \param[out] scr_div SCR divisor. - * \param[out] m M value of M/N divider. - * \param[out] n N value of M/N divider. - * \return index of suitable clock if could find it, -EINVAL otherwise. - */ -static int find_bclk_source(uint32_t bclk, - uint32_t *scr_div, uint32_t *m, uint32_t *n) -{ - struct mn *mn = mn_get(); - int i; - - /* check if we can use MCLK source clock */ - if (is_mclk_source_in_use()) { - if (find_mn(ssp_freq[mn->mclk_source_clock].freq, bclk, scr_div, m, n)) - return mn->mclk_source_clock; - - tr_warn(&mn_tr, "BCLK %d warning: cannot use MCLK source %d", - bclk, ssp_freq[mn->mclk_source_clock].freq); - } - - /* searching the smallest possible bclk source */ - for (i = 0; i <= MAX_SSP_FREQ_INDEX; i++) - if (ssp_freq[i].freq % bclk == 0) { - *scr_div = ssp_freq[i].freq / bclk; - return i; - } - - /* check if we can get target BCLK with M/N */ - for (i = 0; i <= MAX_SSP_FREQ_INDEX; i++) - if (find_mn(ssp_freq[i].freq, bclk, - scr_div, m, n)) - return i; - - return -EINVAL; -} - -/** - * \brief Finds index of SSP clock with the given clock source encoded index. - * \return the index in ssp_freq if could find it, -EINVAL otherwise. - */ -static int find_clk_ssp_index(uint32_t src_enc) -{ - int i; - - /* searching for the encode value matched bclk source */ - for (i = 0; i <= MAX_SSP_FREQ_INDEX; i++) - if (ssp_freq_sources[i] == src_enc) - return i; - - return -EINVAL; -} - -/** - * \brief Checks if given clock is used as source for any BCLK. - * \param[in] clk_src Bit clock source. - * \return true if any port use given clock source, false otherwise. - */ -static inline bool is_bclk_source_in_use(enum bclk_source clk_src) -{ - struct mn *mn = mn_get(); - bool ret = false; - int i; - - for (i = 0; i < ARRAY_SIZE(mn->bclk_sources); i++) { - if (mn->bclk_sources[i] == clk_src) { - ret = true; - break; - } - } - - return ret; -} - -/** - * \brief Configures M/N source clock for BCLK. - * All ports that use M/N share the same source, so it should be changed - * only if there are no other ports using M/N already. - * \param[in] bclk Bit clock frequency. - * \param[out] scr_div SCR divisor. - * \param[out] m M value of M/N divider. - * \param[out] n N value of M/N divider. - * \return 0 on success, error code otherwise. - */ -static inline int setup_initial_bclk_mn_source(uint32_t bclk, uint32_t *scr_div, - uint32_t *m, uint32_t *n) -{ - struct mn *mn = mn_get(); - uint32_t mdivc; - int clk_index = find_bclk_source(bclk, scr_div, m, n); - - if (clk_index < 0) { - tr_err(&mn_tr, "BCLK %d, no valid source", bclk); - return -EINVAL; - } - - mn->bclk_source_mn_clock = clk_index; - - mdivc = mn_reg_read(MN_MDIVCTRL, 0); - - /* clear source bclk clock - 21-20 bits */ - mdivc &= ~MNDSS(MN_SOURCE_CLKS_MASK); - - /* select source clock */ - mdivc |= MNDSS(ssp_freq_sources[clk_index]); - - mn_reg_write(MN_MDIVCTRL, 0, mdivc); - - return 0; -} - -/** - * \brief Reset M/N source clock for BCLK. - * If no port is using bclk, reset to use SSP_CLOCK_XTAL_OSCILLATOR - * as the default clock source. - */ -static inline void reset_bclk_mn_source(void) -{ - struct mn *mn = mn_get(); - uint32_t mdivc; - int clk_index = find_clk_ssp_index(SSP_CLOCK_XTAL_OSCILLATOR); - - if (clk_index < 0) { - tr_err(&mn_tr, "BCLK reset failed, no SSP_CLOCK_XTAL_OSCILLATOR source!"); - return; - } - - mdivc = mn_reg_read(MN_MDIVCTRL, 0); - - /* reset to use XTAL Oscillator */ - mdivc &= ~MNDSS(MN_SOURCE_CLKS_MASK); - mdivc |= MNDSS(SSP_CLOCK_XTAL_OSCILLATOR); - - mn_reg_write(MN_MDIVCTRL, 0, mdivc); - - mn->bclk_source_mn_clock = clk_index; - -} - -/** - * \brief Finds valid M/(N * SCR) values for source clock that is already locked - * because other ports use it. - * \param[in] bclk Bit clock frequency. - * \param[out] scr_div SCR divisor. - * \param[out] m M value of M/N divider. - * \param[out] n N value of M/N divider. - * \return 0 on success, error code otherwise. - */ -static inline int setup_current_bclk_mn_source(uint32_t bclk, uint32_t *scr_div, - uint32_t *m, uint32_t *n) -{ - struct mn *mn = mn_get(); - int ret = 0; - - /* source for M/N is already set, no need to do it */ - if (find_mn(ssp_freq[mn->bclk_source_mn_clock].freq, bclk, scr_div, m, - n)) - goto out; - - tr_err(&mn_tr, "BCLK %d, no valid configuration for already selected source = %d", - bclk, mn->bclk_source_mn_clock); - ret = -EINVAL; - -out: - - return ret; -} - -static inline bool check_bclk_xtal_source(uint32_t bclk, bool mn_in_use, - uint32_t *scr_div) -{ - /* since cAVS 2.0 bypassing XTAL (ECS=0) is not supported */ - return false; -} - -int mn_set_bclk(uint32_t dai_index, uint32_t bclk_rate, - uint32_t *out_scr_div, bool *out_need_ecs) -{ - struct mn *mn = mn_get(); - uint32_t m = 1; - uint32_t n = 1; - int ret = 0; - bool mn_in_use; - k_spinlock_key_t key; - - key = k_spin_lock(&mn->lock); - - mn->bclk_sources[dai_index] = MN_BCLK_SOURCE_NONE; - - mn_in_use = is_bclk_source_in_use(MN_BCLK_SOURCE_MN); - - if (check_bclk_xtal_source(bclk_rate, mn_in_use, out_scr_div)) { - mn->bclk_sources[dai_index] = MN_BCLK_SOURCE_XTAL; - *out_need_ecs = false; - goto out; - } - - *out_need_ecs = true; - - if (mn_in_use) - ret = setup_current_bclk_mn_source(bclk_rate, - out_scr_div, &m, &n); - else - ret = setup_initial_bclk_mn_source(bclk_rate, - out_scr_div, &m, &n); - - if (ret >= 0) { - mn->bclk_sources[dai_index] = MN_BCLK_SOURCE_MN; - - tr_info(&mn_tr, "bclk_rate %d, *out_scr_div %d, m %d, n %d", - bclk_rate, *out_scr_div, m, n); - - mn_reg_write(MN_MDIV_M_VAL(dai_index), dai_index, m); - mn_reg_write(MN_MDIV_N_VAL(dai_index), dai_index, n); - } - -out: - - k_spin_unlock(&mn->lock, key); - - return ret; -} - -void mn_release_bclk(uint32_t dai_index) -{ - struct mn *mn = mn_get(); - bool mn_in_use; - k_spinlock_key_t key; - - key = k_spin_lock(&mn->lock); - mn->bclk_sources[dai_index] = MN_BCLK_SOURCE_NONE; - - mn_in_use = is_bclk_source_in_use(MN_BCLK_SOURCE_MN); - /* release the M/N clock source if not used */ - if (!mn_in_use) - reset_bclk_mn_source(); - k_spin_unlock(&mn->lock, key); -} - -void mn_reset_bclk_divider(uint32_t dai_index) -{ - struct mn *mn = mn_get(); - k_spinlock_key_t key; - - key = k_spin_lock(&mn->lock); - mn_reg_write(MN_MDIV_M_VAL(dai_index), dai_index, 1); - mn_reg_write(MN_MDIV_N_VAL(dai_index), dai_index, 1); - k_spin_unlock(&mn->lock, key); -} - -#endif diff --git a/src/drivers/intel/ssp/ssp.c b/src/drivers/intel/ssp/ssp.c deleted file mode 100644 index e654161181ed..000000000000 --- a/src/drivers/intel/ssp/ssp.c +++ /dev/null @@ -1,1229 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -// -// Copyright(c) 2016 Intel Corporation. All rights reserved. -// -// Author: Liam Girdwood -// Keyon Jie -// Rander Wang - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -LOG_MODULE_REGISTER(ssp_dai, CONFIG_SOF_LOG_LEVEL); - -/* 31458125-95c4-4085-8f3f-497434cb2daf */ -DECLARE_SOF_UUID("ssp-dai", ssp_uuid, 0x31458125, 0x95c4, 0x4085, - 0x8f, 0x3f, 0x49, 0x74, 0x34, 0xcb, 0x2d, 0xaf); - -DECLARE_TR_CTX(ssp_tr, SOF_UUID(ssp_uuid), LOG_LEVEL_INFO); - -/* empty SSP transmit FIFO */ -static void ssp_empty_tx_fifo(struct dai *dai) -{ - int ret; - uint32_t sssr; - - /* - * SSSR_TNF is cleared when TX FIFO is empty or full, - * so wait for set TNF then for TFL zero - order matter. - */ - ret = poll_for_register_delay(dai_base(dai) + SSSR, SSSR_TNF, SSSR_TNF, - SSP_MAX_SEND_TIME_PER_SAMPLE); - ret |= poll_for_register_delay(dai_base(dai) + SSCR3, SSCR3_TFL_MASK, 0, - SSP_MAX_SEND_TIME_PER_SAMPLE * - (SSP_FIFO_DEPTH - 1) / 2); - - if (ret) - dai_warn(dai, "ssp_empty_tx_fifo() warning: timeout"); - - sssr = ssp_read(dai, SSSR); - - /* clear interrupt */ - if (sssr & SSSR_TUR) - ssp_write(dai, SSSR, sssr); -} - -/* empty SSP receive FIFO */ -static void ssp_empty_rx_fifo(struct dai *dai) -{ - struct ssp_pdata *ssp = dai_get_drvdata(dai); - uint64_t sample_ticks = clock_ticks_per_sample(PLATFORM_DEFAULT_CLOCK, - ssp->params.fsync_rate); - uint32_t retry = SSP_RX_FLUSH_RETRY_MAX; - bool direct_reads = ssp->state[DAI_DIR_CAPTURE] <= COMP_STATE_PREPARE; - uint32_t entries; - uint32_t i; - -#if CONFIG_DMA_SUSPEND_DRAIN - /* - * In drain mode, DMA is stopped before DAI, so flush must be - * always done with direct register read. - */ - direct_reads = true; -#endif - - /* - * To make sure all the RX FIFO entries are read out for the flushing, - * we need to wait a minimal SSP port delay after entries are all read, - * and then re-check to see if there is any subsequent entries written - * to the FIFO. This will help to make sure there is no sample mismatched - * issue for the next run with the SSP RX. - */ - while ((ssp_read(dai, SSSR) & SSSR_RNE) && retry--) { - entries = SSCR3_RFL_VAL(ssp_read(dai, SSCR3)); - dai_dbg(dai, "ssp_empty_rx_fifo(), before flushing, entries %d", entries); - - /* let DMA consume data or read RX FIFO directly */ - if (direct_reads) { - for (i = 0; i < entries + 1; i++) - ssp_read(dai, SSDR); - } - - /* wait to get valid fifo status and re-check */ - wait_delay(sample_ticks); - entries = SSCR3_RFL_VAL(ssp_read(dai, SSCR3)); - dai_dbg(dai, "ssp_empty_rx_fifo(), after flushing, entries %d", entries); - } - - /* clear interrupt */ - ssp_update_bits(dai, SSSR, SSSR_ROR, SSSR_ROR); -} - -static int ssp_mclk_prepare_enable(struct dai *dai) -{ - struct ssp_pdata *ssp = dai_get_drvdata(dai); - struct sof_ipc_dai_config *config = &ssp->config; - int ret; - - if (ssp->clk_active & SSP_CLK_MCLK_ACTIVE) - return 0; - - /* MCLK config */ - ret = mn_set_mclk(config->ssp.mclk_id, config->ssp.mclk_rate); - if (ret < 0) - dai_err(dai, "ssp_mclk_prepare_enable(): invalid mclk_rate = %d for mclk_id = %d", - config->ssp.mclk_rate, config->ssp.mclk_id); - else - ssp->clk_active |= SSP_CLK_MCLK_ACTIVE; - - return ret; -} - -static void ssp_mclk_disable_unprepare(struct dai *dai) -{ - struct ssp_pdata *ssp = dai_get_drvdata(dai); - - if (!(ssp->clk_active & SSP_CLK_MCLK_ACTIVE)) - return; - - mn_release_mclk(ssp->config.ssp.mclk_id); - - ssp->clk_active &= ~SSP_CLK_MCLK_ACTIVE; -} - -static int ssp_bclk_prepare_enable(struct dai *dai) -{ - struct ssp_pdata *ssp = dai_get_drvdata(dai); - struct sof_ipc_dai_config *config = &ssp->config; - uint32_t sscr0; - uint32_t mdiv; - bool need_ecs = false; - int ret = 0; - - if (ssp->clk_active & SSP_CLK_BCLK_ACTIVE) - return 0; - - sscr0 = ssp_read(dai, SSCR0); - -#if CONFIG_INTEL_MN - /* BCLK config */ - ret = mn_set_bclk(config->dai_index, config->ssp.bclk_rate, - &mdiv, &need_ecs); - if (ret < 0) { - dai_err(dai, "ssp_bclk_prepare_enable(): invalid bclk_rate = %d for dai_index = %d", - config->ssp.bclk_rate, config->dai_index); - goto out; - } -#else - if (ssp_freq[SSP_DEFAULT_IDX].freq % config->ssp.bclk_rate != 0) { - dai_err(dai, "ssp_bclk_prepare_enable(): invalid bclk_rate = %d for dai_index = %d", - config->ssp.bclk_rate, config->dai_index); - ret = -EINVAL; - goto out; - } - - mdiv = ssp_freq[SSP_DEFAULT_IDX].freq / config->ssp.bclk_rate; -#endif - - if (need_ecs) - sscr0 |= SSCR0_ECS; - - /* clock divisor is SCR + 1 */ - mdiv -= 1; - - /* divisor must be within SCR range */ - if (mdiv > (SSCR0_SCR_MASK >> 8)) { - dai_err(dai, "ssp_bclk_prepare_enable(): divisor %d is not within SCR range", - mdiv); - ret = -EINVAL; - goto out; - } - - /* set the SCR divisor */ - sscr0 &= ~SSCR0_SCR_MASK; - sscr0 |= SSCR0_SCR(mdiv); - - ssp_write(dai, SSCR0, sscr0); - - dai_info(dai, "ssp_bclk_prepare_enable(): sscr0 = 0x%08x", sscr0); -out: - if (!ret) - ssp->clk_active |= SSP_CLK_BCLK_ACTIVE; - - return ret; -} - -static void ssp_bclk_disable_unprepare(struct dai *dai) -{ - struct ssp_pdata *ssp = dai_get_drvdata(dai); - - if (!(ssp->clk_active & SSP_CLK_BCLK_ACTIVE)) - return; -#if CONFIG_INTEL_MN - mn_release_bclk(dai->index); -#endif - ssp->clk_active &= ~SSP_CLK_BCLK_ACTIVE; -} - -/* Digital Audio interface formatting */ -static int ssp_set_config_tplg(struct dai *dai, struct ipc_config_dai *common_config, - const void *spec_config) -{ - struct ssp_pdata *ssp = dai_get_drvdata(dai); - const struct sof_ipc_dai_config *config = spec_config; - uint32_t sscr0; - uint32_t sscr1; - uint32_t sscr2; - uint32_t sscr3; - uint32_t sspsp; - uint32_t sspsp2; - uint32_t sstsa; - uint32_t ssrsa; - uint32_t ssto; - uint32_t ssioc; - uint32_t bdiv; - uint32_t data_size; - uint32_t frame_end_padding; - uint32_t slot_end_padding; - uint32_t frame_len = 0; - uint32_t bdiv_min; - uint32_t tft; - uint32_t rft; - uint32_t active_tx_slots = 2; - uint32_t active_rx_slots = 2; - uint32_t sample_width = 2; - k_spinlock_key_t key; - - bool inverted_bclk = false; - bool inverted_frame = false; - bool cfs = false; - bool start_delay = false; - - int ret = 0; - - key = k_spin_lock(&dai->lock); - - /* ignore config if SSP is already configured */ - if (ssp->state[DAI_DIR_PLAYBACK] > COMP_STATE_READY || - ssp->state[DAI_DIR_CAPTURE] > COMP_STATE_READY) { - if (!memcmp(&ssp->params, &config->ssp, sizeof(ssp->params))) { - dai_info(dai, "ssp_set_config(): Already configured. Ignore config"); - goto clk; - } - - if (ssp->clk_active & (SSP_CLK_MCLK_ACTIVE | SSP_CLK_BCLK_ACTIVE)) { - dai_warn(dai, "ssp_set_config(): SSP active, cannot change config"); - goto clk; - } - - /* safe to proceed and change HW config */ - } - - dai_info(dai, "ssp_set_config(), config->format = 0x%4x", - common_config->format); - - /* reset SSP settings */ - /* sscr0 dynamic settings are DSS, EDSS, SCR, FRDC, ECS */ - /* - * FIXME: MOD, ACS, NCS are not set, - * no support for network mode for now - */ - sscr0 = SSCR0_PSP | SSCR0_RIM | SSCR0_TIM; - - /* sscr1 dynamic settings are SFRMDIR, SCLKDIR, SCFR, RSRE, TSRE */ - sscr1 = SSCR1_TTE | SSCR1_TTELP | SSCR1_TRAIL; - - /* sscr2 dynamic setting is LJDFD */ - sscr2 = SSCR2_SDFD | SSCR2_TURM1; - - /* sscr3 dynamic settings are TFT, RFT */ - sscr3 = 0; - - /* sspsp dynamic settings are SCMODE, SFRMP, DMYSTRT, SFRMWDTH */ - sspsp = 0; - - ssp->config = *config; - ssp->params = config->ssp; - - /* sspsp2 no dynamic setting */ - sspsp2 = 0x0; - - /* ssioc dynamic setting is SFCR */ - ssioc = SSIOC_SCOE; - - /* ssto no dynamic setting */ - ssto = 0x0; - - /* sstsa dynamic setting is TTSA, default 2 slots */ - sstsa = SSTSA_SSTSA(config->ssp.tx_slots); - - /* ssrsa dynamic setting is RTSA, default 2 slots */ - ssrsa = SSRSA_SSRSA(config->ssp.rx_slots); - - switch (config->format & SOF_DAI_FMT_CLOCK_PROVIDER_MASK) { - case SOF_DAI_FMT_CBP_CFP: - sscr1 |= SSCR1_SCLKDIR | SSCR1_SFRMDIR; - break; - case SOF_DAI_FMT_CBC_CFC: - sscr1 |= SSCR1_SCFR; - cfs = true; - break; - case SOF_DAI_FMT_CBP_CFC: - sscr1 |= SSCR1_SCLKDIR; - /* FIXME: this mode has not been tested */ - - cfs = true; - break; - case SOF_DAI_FMT_CBC_CFP: - sscr1 |= SSCR1_SCFR | SSCR1_SFRMDIR; - /* FIXME: this mode has not been tested */ - break; - default: - dai_err(dai, "ssp_set_config(): format & PROVIDER_MASK EINVAL"); - ret = -EINVAL; - goto out; - } - - /* clock signal polarity */ - switch (config->format & SOF_DAI_FMT_INV_MASK) { - case SOF_DAI_FMT_NB_NF: - break; - case SOF_DAI_FMT_NB_IF: - inverted_frame = true; /* handled later with format */ - break; - case SOF_DAI_FMT_IB_IF: - inverted_bclk = true; /* handled later with bclk idle */ - inverted_frame = true; /* handled later with format */ - break; - case SOF_DAI_FMT_IB_NF: - inverted_bclk = true; /* handled later with bclk idle */ - break; - default: - dai_err(dai, "ssp_set_config(): format & INV_MASK EINVAL"); - ret = -EINVAL; - goto out; - } - - /* supporting bclk idle state */ - if (ssp->params.clks_control & - SOF_DAI_INTEL_SSP_CLKCTRL_BCLK_IDLE_HIGH) { - /* bclk idle state high */ - sspsp |= SSPSP_SCMODE((inverted_bclk ^ 0x3) & 0x3); - } else { - /* bclk idle state low */ - sspsp |= SSPSP_SCMODE(inverted_bclk); - } - - sscr0 |= SSCR0_MOD | SSCR0_ACS; - - /* Additional hardware settings */ - - /* Receiver Time-out Interrupt Disabled/Enabled */ - sscr1 |= (ssp->params.quirks & SOF_DAI_INTEL_SSP_QUIRK_TINTE) ? - SSCR1_TINTE : 0; - - /* Peripheral Trailing Byte Interrupts Disable/Enable */ - sscr1 |= (ssp->params.quirks & SOF_DAI_INTEL_SSP_QUIRK_PINTE) ? - SSCR1_PINTE : 0; - - /* Enable/disable internal loopback. Output of transmit serial - * shifter connected to input of receive serial shifter, internally. - */ - sscr1 |= (ssp->params.quirks & SOF_DAI_INTEL_SSP_QUIRK_LBM) ? - SSCR1_LBM : 0; - - /* Transmit data are driven at the same/opposite clock edge specified - * in SSPSP.SCMODE[1:0] - */ - sscr2 |= (ssp->params.quirks & SOF_DAI_INTEL_SSP_QUIRK_SMTATF) ? - SSCR2_SMTATF : 0; - - /* Receive data are sampled at the same/opposite clock edge specified - * in SSPSP.SCMODE[1:0] - */ - sscr2 |= (ssp->params.quirks & SOF_DAI_INTEL_SSP_QUIRK_MMRATF) ? - SSCR2_MMRATF : 0; - - /* Enable/disable the fix for PSP consumer mode TXD wait for frame - * de-assertion before starting the second channel - */ - sscr2 |= (ssp->params.quirks & SOF_DAI_INTEL_SSP_QUIRK_PSPSTWFDFD) ? - SSCR2_PSPSTWFDFD : 0; - - /* Enable/disable the fix for PSP provider mode FSRT with dummy stop & - * frame end padding capability - */ - sscr2 |= (ssp->params.quirks & SOF_DAI_INTEL_SSP_QUIRK_PSPSRWFDFD) ? - SSCR2_PSPSRWFDFD : 0; - - if (!config->ssp.mclk_rate || - config->ssp.mclk_rate > ssp_freq[MAX_SSP_FREQ_INDEX].freq) { - dai_err(dai, "ssp_set_config(): invalid MCLK = %d Hz (valid < %d)", - config->ssp.mclk_rate, - ssp_freq[MAX_SSP_FREQ_INDEX].freq); - ret = -EINVAL; - goto out; - } - - if (!config->ssp.bclk_rate || - config->ssp.bclk_rate > config->ssp.mclk_rate) { - dai_err(dai, "ssp_set_config(): BCLK %d Hz = 0 or > MCLK %d Hz", - config->ssp.bclk_rate, config->ssp.mclk_rate); - ret = -EINVAL; - goto out; - } - - /* calc frame width based on BCLK and rate - must be divisable */ - if (config->ssp.bclk_rate % config->ssp.fsync_rate) { - dai_err(dai, "ssp_set_config(): BCLK %d is not divisable by rate %d", - config->ssp.bclk_rate, config->ssp.fsync_rate); - ret = -EINVAL; - goto out; - } - - /* must be enough BCLKs for data */ - bdiv = config->ssp.bclk_rate / config->ssp.fsync_rate; - if (bdiv < config->ssp.tdm_slot_width * config->ssp.tdm_slots) { - dai_err(dai, "ssp_set_config(): not enough BCLKs need %d", - config->ssp.tdm_slot_width * - config->ssp.tdm_slots); - ret = -EINVAL; - goto out; - } - - /* tdm_slot_width must be <= 38 for SSP */ - if (config->ssp.tdm_slot_width > 38) { - dai_err(dai, "ssp_set_config(): tdm_slot_width %d > 38", - config->ssp.tdm_slot_width); - ret = -EINVAL; - goto out; - } - - bdiv_min = config->ssp.tdm_slots * - (config->ssp.tdm_per_slot_padding_flag ? - config->ssp.tdm_slot_width : config->ssp.sample_valid_bits); - if (bdiv < bdiv_min) { - dai_err(dai, "ssp_set_config(): bdiv(%d) < bdiv_min(%d)", - bdiv, bdiv_min); - ret = -EINVAL; - goto out; - } - - frame_end_padding = bdiv - bdiv_min; - if (frame_end_padding > SSPSP2_FEP_MASK) { - dai_err(dai, "ssp_set_config(): frame_end_padding too big: %u", - frame_end_padding); - ret = -EINVAL; - goto out; - } - - /* format */ - switch (config->format & SOF_DAI_FMT_FORMAT_MASK) { - case SOF_DAI_FMT_I2S: - - start_delay = true; - - sscr0 |= SSCR0_FRDC(config->ssp.tdm_slots); - - if (bdiv % 2) { - dai_err(dai, "ssp_set_config(): bdiv %d is not divisible by 2", - bdiv); - ret = -EINVAL; - goto out; - } - - /* set asserted frame length to half frame length */ - frame_len = bdiv / 2; - - /* - * handle frame polarity, I2S default is falling/active low, - * non-inverted(inverted_frame=0) -- active low(SFRMP=0), - * inverted(inverted_frame=1) -- rising/active high(SFRMP=1), - * so, we should set SFRMP to inverted_frame. - */ - sspsp |= SSPSP_SFRMP(inverted_frame); - - /* - * for I2S/LEFT_J, the padding has to happen at the end - * of each slot - */ - if (frame_end_padding % 2) { - dai_err(dai, "ssp_set_config(): frame_end_padding %d is not divisible by 2", - frame_end_padding); - ret = -EINVAL; - goto out; - } - - slot_end_padding = frame_end_padding / 2; - - if (slot_end_padding > SOF_DAI_INTEL_SSP_SLOT_PADDING_MAX) { - /* too big padding */ - dai_err(dai, "ssp_set_config(): slot_end_padding > %d", - SOF_DAI_INTEL_SSP_SLOT_PADDING_MAX); - ret = -EINVAL; - goto out; - } - - sspsp |= SSPSP_DMYSTOP(slot_end_padding); - slot_end_padding >>= SSPSP_DMYSTOP_BITS; - sspsp |= SSPSP_EDMYSTOP(slot_end_padding); - - break; - - case SOF_DAI_FMT_LEFT_J: - - /* default start_delay value is set to false */ - - sscr0 |= SSCR0_FRDC(config->ssp.tdm_slots); - - /* LJDFD enable */ - sscr2 &= ~SSCR2_LJDFD; - - if (bdiv % 2) { - dai_err(dai, "ssp_set_config(): bdiv %d is not divisible by 2", - bdiv); - ret = -EINVAL; - goto out; - } - - /* set asserted frame length to half frame length */ - frame_len = bdiv / 2; - - /* - * handle frame polarity, LEFT_J default is rising/active high, - * non-inverted(inverted_frame=0) -- active high(SFRMP=1), - * inverted(inverted_frame=1) -- falling/active low(SFRMP=0), - * so, we should set SFRMP to !inverted_frame. - */ - sspsp |= SSPSP_SFRMP(!inverted_frame); - - /* - * for I2S/LEFT_J, the padding has to happen at the end - * of each slot - */ - if (frame_end_padding % 2) { - dai_err(dai, "ssp_set_config(): frame_end_padding %d is not divisible by 2", - frame_end_padding); - ret = -EINVAL; - goto out; - } - - slot_end_padding = frame_end_padding / 2; - - if (slot_end_padding > 15) { - /* can't handle padding over 15 bits */ - dai_err(dai, "ssp_set_config(): slot_end_padding %d > 15 bits", - slot_end_padding); - ret = -EINVAL; - goto out; - } - - sspsp |= SSPSP_DMYSTOP(slot_end_padding); - slot_end_padding >>= SSPSP_DMYSTOP_BITS; - sspsp |= SSPSP_EDMYSTOP(slot_end_padding); - - break; - case SOF_DAI_FMT_DSP_A: - - start_delay = true; - - /* fallthrough */ - - case SOF_DAI_FMT_DSP_B: - - /* default start_delay value is set to false */ - - sscr0 |= SSCR0_MOD | SSCR0_FRDC(config->ssp.tdm_slots); - - /* set asserted frame length */ - frame_len = 1; /* default */ - - if (cfs && ssp->params.frame_pulse_width > 0 && - ssp->params.frame_pulse_width <= - SOF_DAI_INTEL_SSP_FRAME_PULSE_WIDTH_MAX) { - frame_len = ssp->params.frame_pulse_width; - } - - /* frame_pulse_width must less or equal 38 */ - if (ssp->params.frame_pulse_width > - SOF_DAI_INTEL_SSP_FRAME_PULSE_WIDTH_MAX) { - dai_err(dai, "ssp_set_config(): frame_pulse_width > %d", - SOF_DAI_INTEL_SSP_FRAME_PULSE_WIDTH_MAX); - ret = -EINVAL; - goto out; - } - /* - * handle frame polarity, DSP_B default is rising/active high, - * non-inverted(inverted_frame=0) -- active high(SFRMP=1), - * inverted(inverted_frame=1) -- falling/active low(SFRMP=0), - * so, we should set SFRMP to !inverted_frame. - */ - sspsp |= SSPSP_SFRMP(!inverted_frame); - - active_tx_slots = popcount(config->ssp.tx_slots); - active_rx_slots = popcount(config->ssp.rx_slots); - - /* - * handle TDM mode, TDM mode has padding at the end of - * each slot. The amount of padding is equal to result of - * subtracting slot width and valid bits per slot. - */ - if (ssp->params.tdm_per_slot_padding_flag) { - frame_end_padding = bdiv - config->ssp.tdm_slots * - config->ssp.tdm_slot_width; - - slot_end_padding = config->ssp.tdm_slot_width - - config->ssp.sample_valid_bits; - - if (slot_end_padding > - SOF_DAI_INTEL_SSP_SLOT_PADDING_MAX) { - dai_err(dai, "ssp_set_config(): slot_end_padding > %d", - SOF_DAI_INTEL_SSP_SLOT_PADDING_MAX); - ret = -EINVAL; - goto out; - } - - sspsp |= SSPSP_DMYSTOP(slot_end_padding); - slot_end_padding >>= SSPSP_DMYSTOP_BITS; - sspsp |= SSPSP_EDMYSTOP(slot_end_padding); - } - - sspsp2 |= (frame_end_padding & SSPSP2_FEP_MASK); - - break; - default: - dai_err(dai, "ssp_set_config(): invalid format 0x%04x", - config->format); - ret = -EINVAL; - goto out; - } - - if (start_delay) - sspsp |= SSPSP_FSRT; - - sspsp |= SSPSP_SFRMWDTH(frame_len); - - data_size = config->ssp.sample_valid_bits; - - if (data_size > 16) - sscr0 |= (SSCR0_EDSS | SSCR0_DSIZE(data_size - 16)); - else - sscr0 |= SSCR0_DSIZE(data_size); - - /* setting TFT and RFT */ - switch (config->ssp.sample_valid_bits) { - case 16: - /* use 2 bytes for each slot */ - sample_width = 2; - break; - case 24: - case 32: - /* use 4 bytes for each slot */ - sample_width = 4; - break; - default: - dai_err(dai, "ssp_set_config(): sample_valid_bits %d", - config->ssp.sample_valid_bits); - ret = -EINVAL; - goto out; - } - - tft = MIN(SSP_FIFO_DEPTH - SSP_FIFO_WATERMARK, - sample_width * active_tx_slots); - rft = MIN(SSP_FIFO_DEPTH - SSP_FIFO_WATERMARK, - sample_width * active_rx_slots); - - sscr3 |= SSCR3_TX(tft) | SSCR3_RX(rft); - - ssp_write(dai, SSCR0, sscr0); - ssp_write(dai, SSCR1, sscr1); - ssp_write(dai, SSCR2, sscr2); - ssp_write(dai, SSCR3, sscr3); - ssp_write(dai, SSPSP, sspsp); - ssp_write(dai, SSPSP2, sspsp2); - ssp_write(dai, SSIOC, ssioc); - ssp_write(dai, SSTO, ssto); - ssp_write(dai, SSTSA, sstsa); - ssp_write(dai, SSRSA, ssrsa); - - dai_info(dai, "ssp_set_config(), sscr0 = 0x%08x, sscr1 = 0x%08x, ssto = 0x%08x, sspsp = 0x%0x", - sscr0, sscr1, ssto, sspsp); - dai_info(dai, "ssp_set_config(), sscr2 = 0x%08x, sspsp2 = 0x%08x, sscr3 = 0x%08x, ssioc = 0x%08x", - sscr2, sspsp2, sscr3, ssioc); - dai_info(dai, "ssp_set_config(), ssrsa = 0x%08x, sstsa = 0x%08x", - ssrsa, sstsa); - - ssp->state[DAI_DIR_PLAYBACK] = COMP_STATE_PREPARE; - ssp->state[DAI_DIR_CAPTURE] = COMP_STATE_PREPARE; - -clk: - /* MCLK always-on: turn on mclk and never turn it off */ - if (ssp->params.clks_control & SOF_DAI_INTEL_SSP_CLKCTRL_MCLK_AON) { - ret = ssp_mclk_prepare_enable(dai); - if (ret < 0) - goto out; - - ssp->clk_active |= SSP_CLK_MCLK_AON_REQ; - - dai_info(dai, "ssp_set_config(): enable MCLK for SSP%d", dai->index); - } - - switch (config->flags & SOF_DAI_CONFIG_FLAGS_CMD_MASK) { - case SOF_DAI_CONFIG_FLAGS_HW_PARAMS: - if (ssp->params.clks_control & SOF_DAI_INTEL_SSP_CLKCTRL_MCLK_ES) { - ret = ssp_mclk_prepare_enable(dai); - if (ret < 0) - goto out; - - ssp->clk_active |= SSP_CLK_MCLK_ES_REQ; - - dai_info(dai, "ssp_set_config(): hw_params stage: enabled MCLK clocks for SSP%d...", - dai->index); - } - - if (ssp->params.clks_control & SOF_DAI_INTEL_SSP_CLKCTRL_BCLK_ES) { - bool enable_sse = false; - - if (!(ssp->clk_active & SSP_CLK_BCLK_ACTIVE)) - enable_sse = true; - - ret = ssp_bclk_prepare_enable(dai); - if (ret < 0) - goto out; - - ssp->clk_active |= SSP_CLK_BCLK_ES_REQ; - - if (enable_sse) { - - /* enable TRSE/RSRE before SSE */ - ssp_update_bits(dai, SSCR1, - SSCR1_TSRE | SSCR1_RSRE, - SSCR1_TSRE | SSCR1_RSRE); - - /* enable port */ - ssp_update_bits(dai, SSCR0, SSCR0_SSE, SSCR0_SSE); - - dai_info(dai, "ssp_set_config(): SSE set for SSP%d", dai->index); - } - - dai_info(dai, "ssp_set_config(): hw_params stage: enabled BCLK clocks for SSP%d...", - dai->index); - } - break; - case SOF_DAI_CONFIG_FLAGS_HW_FREE: - /* disable SSP port if no users */ - if (ssp->state[SOF_IPC_STREAM_CAPTURE] != COMP_STATE_PREPARE || - ssp->state[SOF_IPC_STREAM_PLAYBACK] != COMP_STATE_PREPARE) { - dai_info(dai, "ssp_set_config(): hw_free stage: ignore since SSP%d still in use", - dai->index); - break; - } - - if (ssp->params.clks_control & SOF_DAI_INTEL_SSP_CLKCTRL_BCLK_ES) { - dai_info(dai, "ssp_set_config(): hw_free stage: releasing BCLK clocks for SSP%d...", - dai->index); - if (ssp->clk_active & SSP_CLK_BCLK_ACTIVE) { - /* clear TRSE/RSRE before SSE */ - ssp_update_bits(dai, SSCR1, - SSCR1_TSRE | SSCR1_RSRE, - 0); - - ssp_update_bits(dai, SSCR0, SSCR0_SSE, 0); - dai_info(dai, "ssp_set_config(): SSE clear for SSP%d", dai->index); - } - ssp_bclk_disable_unprepare(dai); - ssp->clk_active &= ~SSP_CLK_BCLK_ES_REQ; - } - if (ssp->params.clks_control & SOF_DAI_INTEL_SSP_CLKCTRL_MCLK_ES) { - dai_info(dai, "ssp_set_config: hw_free stage: releasing MCLK clocks for SSP%d...", - dai->index); - ssp_mclk_disable_unprepare(dai); - ssp->clk_active &= ~SSP_CLK_MCLK_ES_REQ; - } - break; - default: - break; - } -out: - - k_spin_unlock(&dai->lock, key); - - return ret; -} - -static int ssp_set_config_blob(struct dai *dai, struct ipc_config_dai *common_config, - const void *spec_config) -{ - const struct ipc4_ssp_configuration_blob *blob = spec_config; - struct ssp_pdata *ssp = dai_get_drvdata(dai); - uint32_t ssc0, sstsa, ssrsa; - - /* set config only once for playback or capture */ - if (dai->sref > 1) - return 0; - - ssc0 = blob->i2s_driver_config.i2s_config.ssc0; - sstsa = blob->i2s_driver_config.i2s_config.sstsa; - ssrsa = blob->i2s_driver_config.i2s_config.ssrsa; - - ssp_write(dai, SSCR0, ssc0); - ssp_write(dai, SSCR1, blob->i2s_driver_config.i2s_config.ssc1); - ssp_write(dai, SSCR2, blob->i2s_driver_config.i2s_config.ssc2); - ssp_write(dai, SSCR3, blob->i2s_driver_config.i2s_config.ssc3); - ssp_write(dai, SSPSP, blob->i2s_driver_config.i2s_config.sspsp); - ssp_write(dai, SSPSP2, blob->i2s_driver_config.i2s_config.sspsp2); - ssp_write(dai, SSIOC, blob->i2s_driver_config.i2s_config.ssioc); - ssp_write(dai, SSTO, blob->i2s_driver_config.i2s_config.sscto); - ssp_write(dai, SSTSA, sstsa); - ssp_write(dai, SSRSA, ssrsa); - - dai_info(dai, "ssp_set_config(), sscr0 = 0x%08x, sscr1 = 0x%08x, ssto = 0x%08x, sspsp = 0x%0x", - ssc0, blob->i2s_driver_config.i2s_config.ssc1, - blob->i2s_driver_config.i2s_config.sscto, - blob->i2s_driver_config.i2s_config.sspsp); - dai_info(dai, "ssp_set_config(), sscr2 = 0x%08x, sspsp2 = 0x%08x, sscr3 = 0x%08x", - blob->i2s_driver_config.i2s_config.ssc2, blob->i2s_driver_config.i2s_config.sspsp2, - blob->i2s_driver_config.i2s_config.ssc3); - dai_err(dai, "ssp_set_config(), ssioc = 0x%08x, ssrsa = 0x%08x, sstsa = 0x%08x", - blob->i2s_driver_config.i2s_config.ssioc, ssrsa, sstsa); - - ssp->config.ssp.sample_valid_bits = SSCR0_DSIZE_GET(ssc0); - if (ssc0 & SSCR0_EDSS) - ssp->config.ssp.sample_valid_bits += 16; - - ssp->config.ssp.tdm_slots = SSCR0_FRDC_GET(ssc0); - ssp->config.ssp.tx_slots = SSTSA_GET(sstsa); - ssp->config.ssp.rx_slots = SSRSA_GET(ssrsa); - ssp->config.ssp.fsync_rate = common_config->sampling_frequency; - ssp->params = ssp->config.ssp; - - ssp->state[DAI_DIR_PLAYBACK] = COMP_STATE_PREPARE; - ssp->state[DAI_DIR_CAPTURE] = COMP_STATE_PREPARE; - - /* ssp blob is set by pcm_hw_params for ipc4 stream, so enable - * mclk and bclk at this time. - */ - mn_set_mclk_blob(blob->i2s_driver_config.mclk_config.mdivc, - blob->i2s_driver_config.mclk_config.mdivr); - ssp->clk_active |= SSP_CLK_MCLK_ES_REQ; - - /* enable TRSE/RSRE before SSE */ - ssp_update_bits(dai, SSCR1, SSCR1_TSRE | SSCR1_RSRE, SSCR1_TSRE | SSCR1_RSRE); - - /* enable port */ - ssp_update_bits(dai, SSCR0, SSCR0_SSE, SSCR0_SSE); - ssp->clk_active |= SSP_CLK_BCLK_ES_REQ; - - return 0; -} - -static int ssp_set_config(struct dai *dai, struct ipc_config_dai *common_config, - const void *spec_config) -{ - if (!common_config->is_config_blob) - return ssp_set_config_tplg(dai, common_config, spec_config); - else - return ssp_set_config_blob(dai, common_config, spec_config); -} - -/* - * Portion of the SSP configuration should be applied just before the - * SSP dai is activated, for either power saving or params runtime - * configurable flexibility. - */ -static int ssp_pre_start(struct dai *dai) -{ - struct ssp_pdata *ssp = dai_get_drvdata(dai); - int ret = 0; - - dai_info(dai, "ssp_pre_start()"); - - /* - * We will test if mclk/bclk is configured in - * ssp_mclk/bclk_prepare_enable/disable functions - */ - if (!(ssp->clk_active & SSP_CLK_MCLK_ES_REQ) && - !(ssp->clk_active & SSP_CLK_MCLK_AON_REQ)) { - /* MCLK config */ - ret = ssp_mclk_prepare_enable(dai); - if (ret < 0) - return ret; - } - - if (!(ssp->clk_active & SSP_CLK_BCLK_ES_REQ)) - ret = ssp_bclk_prepare_enable(dai); - - return ret; -} - -/* - * For power saving, we should do kinds of power release when the SSP - * dai is changed to inactive, though the runtime param configuration - * don't have to be reset. - */ -static void ssp_post_stop(struct dai *dai) -{ - struct ssp_pdata *ssp = dai_get_drvdata(dai); - - /* release clocks if SSP is inactive */ - if (ssp->state[SOF_IPC_STREAM_PLAYBACK] != COMP_STATE_ACTIVE && - ssp->state[SOF_IPC_STREAM_CAPTURE] != COMP_STATE_ACTIVE) { - if (!(ssp->clk_active & SSP_CLK_BCLK_ES_REQ)) { - dai_info(dai, "ssp_post_stop releasing BCLK clocks for SSP%d...", - dai->index); - ssp_bclk_disable_unprepare(dai); - } - if (!(ssp->clk_active & SSP_CLK_MCLK_ES_REQ) && - !(ssp->clk_active & SSP_CLK_MCLK_AON_REQ)) { - dai_info(dai, "ssp_post_stop releasing MCLK clocks for SSP%d...", - dai->index); - ssp_mclk_disable_unprepare(dai); - } - } -} - -/* get SSP hw params */ -static int ssp_get_hw_params(struct dai *dai, - struct sof_ipc_stream_params *params, int dir) -{ - struct ssp_pdata *ssp = dai_get_drvdata(dai); - - params->rate = ssp->params.fsync_rate; - params->buffer_fmt = 0; - - if (dir == SOF_IPC_STREAM_PLAYBACK) - params->channels = popcount(ssp->params.tx_slots); - else - params->channels = popcount(ssp->params.rx_slots); - - switch (ssp->params.sample_valid_bits) { - case 16: - params->frame_fmt = SOF_IPC_FRAME_S16_LE; - break; - case 24: - params->frame_fmt = SOF_IPC_FRAME_S24_4LE; - break; - case 32: - params->frame_fmt = SOF_IPC_FRAME_S32_LE; - break; - default: - dai_err(dai, "ssp_get_hw_params(): not supported format"); - return -EINVAL; - } - - return 0; -} - -static void ssp_early_start(struct dai *dai, int direction) -{ - struct ssp_pdata *ssp = dai_get_drvdata(dai); - k_spinlock_key_t key; - - key = k_spin_lock(&dai->lock); - - /* RX fifo must be cleared before start */ - if (direction == DAI_DIR_CAPTURE) - ssp_empty_rx_fifo(dai); - - /* request mclk/bclk */ - ssp_pre_start(dai); - - if (!(ssp->clk_active & SSP_CLK_BCLK_ES_REQ)) { - /* enable TRSE/RSRE before SSE */ - ssp_update_bits(dai, SSCR1, - SSCR1_TSRE | SSCR1_RSRE, - SSCR1_TSRE | SSCR1_RSRE); - - /* enable port */ - ssp_update_bits(dai, SSCR0, SSCR0_SSE, SSCR0_SSE); - dai_info(dai, "ssp_early_start(): SSE set for SSP%d", dai->index); - } - - - k_spin_unlock(&dai->lock, key); -} - -/* start the SSP for either playback or capture */ -static void ssp_start(struct dai *dai, int direction) -{ - struct ssp_pdata *ssp = dai_get_drvdata(dai); - k_spinlock_key_t key; - - key = k_spin_lock(&dai->lock); - - dai_info(dai, "ssp_start()"); - - /* enable DMA */ - if (direction == DAI_DIR_PLAYBACK) - ssp_update_bits(dai, SSTSA, SSTSA_TXEN, SSTSA_TXEN); - else - ssp_update_bits(dai, SSRSA, SSRSA_RXEN, SSRSA_RXEN); - - ssp->state[direction] = COMP_STATE_ACTIVE; - - /* - * Wait to get valid fifo status in clock consumer mode. TODO it's - * uncertain which SSP clock consumer modes need the delay atm, but - * these can be added here when confirmed. - */ - switch (ssp->config.format & SOF_DAI_FMT_CLOCK_PROVIDER_MASK) { - case SOF_DAI_FMT_CBC_CFC: - break; - default: - /* delay for all SSP consumed clocks atm - see above */ - wait_delay(PLATFORM_SSP_DELAY); - break; - } - - k_spin_unlock(&dai->lock, key); -} - -/* stop the SSP for either playback or capture */ -static void ssp_stop(struct dai *dai, int direction) -{ - struct ssp_pdata *ssp = dai_get_drvdata(dai); - k_spinlock_key_t key; - - key = k_spin_lock(&dai->lock); - - /* - * Wait to get valid fifo status in clock consumer mode. TODO it's - * uncertain which SSP clock consumer modes need the delay atm, but - * these can be added here when confirmed. - */ - switch (ssp->config.format & SOF_DAI_FMT_CLOCK_PROVIDER_MASK) { - case SOF_DAI_FMT_CBC_CFC: - break; - default: - /* delay for all SSP consumed clocks atm - see above */ - wait_delay(PLATFORM_SSP_DELAY); - break; - } - - /* stop Rx if neeed */ - if (direction == DAI_DIR_CAPTURE && - ssp->state[SOF_IPC_STREAM_CAPTURE] != COMP_STATE_PREPARE) { - ssp_update_bits(dai, SSRSA, SSRSA_RXEN, 0); - ssp_empty_rx_fifo(dai); - ssp->state[SOF_IPC_STREAM_CAPTURE] = COMP_STATE_PREPARE; - dai_info(dai, "ssp_stop(), RX stop"); - } - - /* stop Tx if needed */ - if (direction == DAI_DIR_PLAYBACK && - ssp->state[SOF_IPC_STREAM_PLAYBACK] != COMP_STATE_PREPARE) { - ssp_empty_tx_fifo(dai); - ssp_update_bits(dai, SSTSA, SSTSA_TXEN, 0); - ssp->state[SOF_IPC_STREAM_PLAYBACK] = COMP_STATE_PREPARE; - dai_info(dai, "ssp_stop(), TX stop"); - } - - /* disable SSP port if no users */ - if (ssp->state[SOF_IPC_STREAM_CAPTURE] == COMP_STATE_PREPARE && - ssp->state[SOF_IPC_STREAM_PLAYBACK] == COMP_STATE_PREPARE) { - if (!(ssp->clk_active & SSP_CLK_BCLK_ES_REQ)) { - /* clear TRSE/RSRE before SSE */ - ssp_update_bits(dai, SSCR1, - SSCR1_TSRE | SSCR1_RSRE, - 0); - - ssp_update_bits(dai, SSCR0, SSCR0_SSE, 0); - dai_info(dai, "ssp_stop(): SSE clear SSP%d", dai->index); - } - } - - ssp_post_stop(dai); - - k_spin_unlock(&dai->lock, key); -} - -static void ssp_pause(struct dai *dai, int direction) -{ - struct ssp_pdata *ssp = dai_get_drvdata(dai); - - if (direction == SOF_IPC_STREAM_CAPTURE) - dai_info(dai, "ssp_pause(), RX"); - else - dai_info(dai, "ssp_pause(), TX"); - - ssp->state[direction] = COMP_STATE_PAUSED; -} - -static int ssp_trigger(struct dai *dai, int cmd, int direction) -{ - struct ssp_pdata *ssp = dai_get_drvdata(dai); - - dai_info(dai, "ssp_trigger() cmd %d", cmd); - - switch (cmd) { - case COMP_TRIGGER_START: - case COMP_TRIGGER_RELEASE: - if (ssp->state[direction] == COMP_STATE_PAUSED || - ssp->state[direction] == COMP_STATE_PREPARE) - ssp_start(dai, direction); - break; - case COMP_TRIGGER_STOP: - ssp_stop(dai, direction); - break; - case COMP_TRIGGER_PAUSE: - ssp_pause(dai, direction); - break; - case COMP_TRIGGER_PRE_START: - case COMP_TRIGGER_PRE_RELEASE: - ssp_early_start(dai, direction); - break; - } - - return 0; -} - -static int ssp_probe(struct dai *dai) -{ - struct ssp_pdata *ssp; - - if (dai_get_drvdata(dai)) - return -EEXIST; /* already created */ - - /* allocate private data */ - ssp = rzalloc(SOF_MEM_ZONE_RUNTIME_SHARED, 0, SOF_MEM_CAPS_RAM, sizeof(*ssp)); - if (!ssp) { - dai_err(dai, "ssp_probe(): alloc failed"); - return -ENOMEM; - } - dai_set_drvdata(dai, ssp); - - ssp->state[DAI_DIR_PLAYBACK] = COMP_STATE_READY; - ssp->state[DAI_DIR_CAPTURE] = COMP_STATE_READY; - -#if CONFIG_INTEL_MN - /* Reset M/N, power-gating functions need it */ - mn_reset_bclk_divider(dai->index); -#endif - - /* Enable SSP power */ - pm_runtime_get_sync(SSP_POW, dai->index); - - /* Disable dynamic clock gating before touching any register */ - pm_runtime_get_sync(SSP_CLK, dai->index); - - ssp_empty_rx_fifo(dai); - - return 0; -} - -static int ssp_remove(struct dai *dai) -{ - pm_runtime_put_sync(SSP_CLK, dai->index); - - ssp_mclk_disable_unprepare(dai); - ssp_bclk_disable_unprepare(dai); - - /* Disable SSP power */ - pm_runtime_put_sync(SSP_POW, dai->index); - - rfree(dai_get_drvdata(dai)); - dai_set_drvdata(dai, NULL); - - return 0; -} - -static int ssp_get_handshake(struct dai *dai, int direction, int stream_id) -{ - return dai->plat_data.fifo[direction].handshake; -} - -static int ssp_get_fifo(struct dai *dai, int direction, int stream_id) -{ - return dai->plat_data.fifo[direction].offset; -} - -static uint32_t ssp_get_init_delay_ms(struct dai *dai) -{ - struct ssp_pdata *ssp = dai_get_drvdata(dai); - - if (ssp->clk_active & SSP_CLK_BCLK_ACTIVE) - return 0; - - /* drive BCLK early for guaranteed time, - * before first FSYNC, it is required by some codecs - */ - return ssp->params.bclk_delay; -} - -const struct dai_driver ssp_driver = { - .type = SOF_DAI_INTEL_SSP, - .uid = SOF_UUID(ssp_uuid), - .tctx = &ssp_tr, - .dma_caps = DMA_CAP_GP_LP | DMA_CAP_GP_HP, - .dma_dev = DMA_DEV_SSP, - .ops = { - .trigger = ssp_trigger, - .set_config = ssp_set_config, - .get_hw_params = ssp_get_hw_params, - .get_handshake = ssp_get_handshake, - .get_fifo = ssp_get_fifo, - .get_init_delay_ms = ssp_get_init_delay_ms, - .probe = ssp_probe, - .remove = ssp_remove, - }, - .ts_ops = { - .ts_config = timestamp_ssp_config, - .ts_start = timestamp_ssp_start, - .ts_get = timestamp_ssp_get, - .ts_stop = timestamp_ssp_stop, - }, -};