diff --git a/app/boards/acp_7_x_adsp.conf b/app/boards/acp_7_x_adsp.conf index 6a724bea780b..310d9ac52b9f 100644 --- a/app/boards/acp_7_x_adsp.conf +++ b/app/boards/acp_7_x_adsp.conf @@ -11,9 +11,11 @@ CONFIG_DMA=y CONFIG_DMA_DOMAIN=n CONFIG_ZEPHYR_NATIVE_DRIVERS=y CONFIG_INTC_AMD_ACP=y +CONFIG_DMA_AMD_ACP_DMIC=y CONFIG_DMA_AMD_ACP_HOST=y CONFIG_DMA_AMD_ACP_SDW=y CONFIG_DMA_AMD_ACP_SDW_CHANNEL_COUNT=64 +CONFIG_DAI_AMD_DMIC=y CONFIG_DMA_AMD_ACP_TDM=y CONFIG_DAI_AMD_SDW=y CONFIG_DAI_AMD_TDM=y @@ -63,3 +65,4 @@ CONFIG_LOG_BACKEND_ACP=y CONFIG_LOG_DEFAULT_LEVEL=3 CONFIG_LOG_MAX_LEVEL=3 CONFIG_SOF_LOG_LEVEL_INF=y +CONFIG_SYS_HEAP_BIG_ONLY=y diff --git a/src/audio/dai-zephyr.c b/src/audio/dai-zephyr.c index 7c011e25b0be..785740e36ab2 100644 --- a/src/audio/dai-zephyr.c +++ b/src/audio/dai-zephyr.c @@ -190,6 +190,10 @@ __cold int dai_set_config(struct dai *dai, struct ipc_config_dai *common_config, cfg.type = DAI_IMX_MICFIL; cfg_params = &sof_cfg->micfil; break; + case SOF_DAI_AMD_DMIC: + cfg.type = DAI_AMD_DMIC; + cfg_params = &sof_cfg->acpdmic; + break; case SOF_DAI_AMD_SDW: cfg.type = DAI_AMD_SDW; cfg_params = &sof_cfg->acpsdw; diff --git a/src/ipc/ipc3/dai.c b/src/ipc/ipc3/dai.c index 46d54d2d86e5..a118d5e341d3 100644 --- a/src/ipc/ipc3/dai.c +++ b/src/ipc/ipc3/dai.c @@ -190,6 +190,27 @@ int ipc_dai_data_config(struct dai_data *dd, struct comp_dev *dev) if (dd->dma_buffer) audio_stream_set_frm_fmt(&dd->dma_buffer->stream, dev->ipc_config.frame_fmt); +#if defined(CONFIG_AMD) && !defined(CONFIG_SOC_ACP_6_0) + { + struct acp_dma_dev_data *dmic_data = dd->dma->z_dev->data; + struct dmic_context *dmic_ctx; + + /* Allocate coherent memory for DMIC context shared between + * IPC and DMA contexts. + */ + if (!dmic_data->dai_index_ptr) { + dmic_ctx = rzalloc(SOF_MEM_FLAG_USER | SOF_MEM_FLAG_COHERENT, + sizeof(*dmic_ctx)); + if (!dmic_ctx) { + return -ENOMEM; + } + dmic_data->dai_index_ptr = dmic_ctx; + } else { + dmic_ctx = (struct dmic_context *)dmic_data->dai_index_ptr; + } + dmic_ctx->index = dd->dai->index; + } +#endif break; case SOF_DAI_AMD_BT: case SOF_DAI_AMD_SP: diff --git a/src/lib/dai.c b/src/lib/dai.c index 4c52cd7fe460..65971fec74c5 100644 --- a/src/lib/dai.c +++ b/src/lib/dai.c @@ -187,6 +187,9 @@ const struct device *zephyr_dev[] = { #if CONFIG_DAI_NXP_MICFIL DT_FOREACH_STATUS_OKAY(nxp_dai_micfil, GET_DEVICE_LIST) #endif +#if CONFIG_DAI_AMD_DMIC + DT_FOREACH_STATUS_OKAY(amd_dmic_dai, GET_DEVICE_LIST) +#endif #if CONFIG_DAI_AMD_SDW DT_FOREACH_STATUS_OKAY(amd_acp_sdw_dai, GET_DEVICE_LIST) #endif @@ -318,6 +321,10 @@ static void dai_set_device_params(struct dai *d) d->dma_dev = SOF_DMA_DEV_HS | SOF_DMA_DEV_SP | SOF_DMA_DEV_BT; d->dma_caps = SOF_DMA_CAP_HS | SOF_DMA_CAP_SP | SOF_DMA_CAP_BT; break; + case SOF_DAI_AMD_DMIC: + d->dma_dev = SOF_DMA_DEV_DMIC; + d->dma_caps = SOF_DMA_CAP_DMIC; + break; case SOF_DAI_AMD_TDM: d->dma_dev = SOF_DMA_DEV_TDM; d->dma_caps = SOF_DMA_CAP_TDM; diff --git a/src/platform/amd/acp_7_x/include/platform/lib/memory.h b/src/platform/amd/acp_7_x/include/platform/lib/memory.h index 0a7039e4036b..c3a1f2ee9f3c 100644 --- a/src/platform/amd/acp_7_x/include/platform/lib/memory.h +++ b/src/platform/amd/acp_7_x/include/platform/lib/memory.h @@ -29,7 +29,7 @@ #define DRAM1_BASE 0xE0010000 #define DRAM1_SIZE 0x10000 #define SRAM1_BASE 0x6000C000 -#define SRAM1_SIZE 0x274000 +#define SRAM1_SIZE 0x3F4000 #define DMA0_BASE PU_REGISTER_BASE #define DMA0_SIZE 0x4 @@ -126,12 +126,6 @@ #define EXT_MANIFEST_ELF_BASE (LOG_ENTRY_ELF_BASE + LOG_ENTRY_ELF_SIZE) #define EXT_MANIFEST_ELF_SIZE 0x2000000 -/* Stack configuration */ -#define SOF_STACK_SIZE (0x1000) -#define SOF_STACK_TOTAL_SIZE SOF_STACK_SIZE -#define SOF_STACK_END (DRAM1_BASE + DRAM1_SIZE - SOF_STACK_SIZE) -#define SOF_STACK_BASE (SOF_STACK_END + SOF_STACK_SIZE) - /* Mailbox configuration */ #define SRAM_OUTBOX_BASE SRAM0_BASE #define SRAM_OUTBOX_SIZE 0x400 diff --git a/src/platform/amd/acp_7_x/include/platform/platform_misc.h b/src/platform/amd/acp_7_x/include/platform/platform_misc.h index 7898c6bc24f0..69c03fce9dcb 100644 --- a/src/platform/amd/acp_7_x/include/platform/platform_misc.h +++ b/src/platform/amd/acp_7_x/include/platform/platform_misc.h @@ -20,6 +20,7 @@ #define ACP_DSP_SW_INTR_CNTL 0x1241860 #define ACP_DSP_SW_INTR_STAT 0x1241864 #define ACP_AXI2DAGB_SEM_0 0x12418F4 +#define ACP_ERROR_STATUS 0x1241A88 #define ACP_SRBM_CLIENT_BASE_ADDR 0x1241BF0 #define ACP_SRBM_CLIENT_RDDATA 0x1241BF4 #define ACP_SRBM_CYCLE_STS 0x1241BF8 @@ -36,23 +37,43 @@ #define ACP_7X_SYSTEMPLL2P0_N3_BUS_CSR_SNAP_6_AI_FREQ_CTRL9_BRDS 0x6D20C /* Registers from CLK6 block */ -#define mmCLK6_PLL0_DFS0_CNTL 0x6D014 -#define mmCLK6_PLL0_DFS0_STATUS 0x6D018 -#define mmCLK6_PLL0_DFS1_CNTL 0x6D01C -#define mmCLK6_PLL0_DFS1_STATUS 0x6D020 -#define mmCLK6_CLK0_BYPASS_CNTL 0x6D06C -#define mmCLK6_CLK1_BYPASS_CNTL 0x6D08C -#define mmCLK6_CLK_TICK_CNT_CONFIG_REG 0x6D188 -#define mmCLK6_CLK0_CURRENT_CNT 0x6D190 -#define mmCLK6_CLK1_CURRENT_CNT 0x6D194 - +#define ACP_CLK6_PLL0_DFS0_CNTL 0x6D014 +#define ACP_CLK6_PLL0_DFS0_STATUS 0x6D018 +#define ACP_CLK6_PLL0_DFS1_CNTL 0x6D01C +#define ACP_CLK6_PLL0_DFS1_STATUS 0x6D020 +#define ACP_CLK6_CLK0_BYPASS_CNTL 0x6D06C +#define ACP_CLK6_CLK1_DS_CNTL 0x6D074 +#define ACP_CLK6_CLK1_BYPASS_CNTL 0x6D08C +#define ACP_CLK6_CLK5_POSTDIV_CNTL 0x6D100 +#define ACP_CLK6_CLK5_BYPASS_CNTL 0x6D10C +#define ACP_CLK6_PLL0_DFSBYPASS_CONTROL 0x6D180 +#define ACP_CLK6_CLK_TICK_CNT_CONFIG_REG 0x6D188 +#define ACP_CLK6_CLK0_CURRENT_CNT 0x6D190 +#define ACP_CLK6_CLK1_CURRENT_CNT 0x6D194 + +#define ACP_DFS_BYPASS_ENTER_AUDIOCLK 0x00000001 +#define ACP_DFS_BYPASS_ENTER_ACLK 0x00000002 +#define ACP_DFS_BYPASS_EXIT_AUDIOCLK 0x00010000 +#define ACP_DFS_BYPASS_EXIT_ACLK 0x00020000 +#define ACP_CLK_BYPASS_SEL_AUDIO_PLL 0x0 +#define ACP_CLK_BYPASS_SEL_LPPLL 0x4 +#define ACP_CLK_BYPASS_SEL_REFCLK_24MHZ 0x5 +#define ACP_CLK_DIV_BY_1 0x0 +#define ACP_CLK_DIV_BY_16 0x4 +#define ACP_CLKMUX_SEL 0x124102C +#define ACP_SW_48MHz_CLK_SEL 0x1241030 +#define ACP_ACLK_AUDIOCLK_CTRL 0x1241464 + +#define ACP_DEEPSLEEP_DISABLE 0x0 +#define ACP_DEEPSLEEP_ENABLE 0x3 +#define ACP_SW_CLK_SEL_49152KHZ 0x1F /* CLK6 register aliases without mm prefix for clock management code */ -#define CLK6_PLL0_DFS0_CNTL mmCLK6_PLL0_DFS0_CNTL -#define CLK6_PLL0_DFS1_CNTL mmCLK6_PLL0_DFS1_CNTL -#define CLK6_PLL0_DFS1_STATUS mmCLK6_PLL0_DFS1_STATUS -#define CLK0_CURRENT_CNT mmCLK6_CLK0_CURRENT_CNT -#define CLK1_CURRENT_CNT mmCLK6_CLK1_CURRENT_CNT -#define CLK_TICK_CNT_CONFIG_REG mmCLK6_CLK_TICK_CNT_CONFIG_REG +#define CLK6_PLL0_DFS0_CNTL ACP_CLK6_PLL0_DFS0_CNTL +#define CLK6_PLL0_DFS1_CNTL ACP_CLK6_PLL0_DFS1_CNTL +#define CLK6_PLL0_DFS1_STATUS ACP_CLK6_PLL0_DFS1_STATUS +#define CLK0_CURRENT_CNT ACP_CLK6_CLK0_CURRENT_CNT +#define CLK1_CURRENT_CNT ACP_CLK6_CLK1_CURRENT_CNT +#define CLK_TICK_CNT_CONFIG_REG ACP_CLK6_CLK_TICK_CNT_CONFIG_REG /* SYSTEMPLL2P0 register aliases without ACP_7X prefix */ #define SYSTEMPLL2P0_N3_BUS_CSR_SNAP_6_NON_AI_CTRL0_BRDS ACP_7X_SYSTEMPLL2P0_N3_BUS_CSR_SNAP_6_NON_AI_CTRL0_BRDS @@ -478,6 +499,42 @@ typedef union clk6_clk_bypass_cntl_u { float f32All; } clk6_bypass_cntl_u_t; +typedef union clk6_clk_ds_cntl_u { + struct { + uint32_t CLK_DS_DIV_ID:4; + uint32_t CLK_ALLOW_DS:1; + uint32_t CLK_DS_DEGL_SET:3; + uint32_t CLK_DS_OVERRIDE:2; + uint32_t:22; + } bitfields, bits; + uint32_t u32All; + int32_t i32All; + float f32All; +} clk6_clk_ds_cntl_u_t; + +typedef union clk6_clk_postdiv_cntl_u { + struct { + uint32_t CLK0_POSTDIV_DIV_ID:8; + uint32_t CLK0_POSTDIV_RAMP_DIS:1; + uint32_t:7; + uint32_t CLK0_POSTDIV_RAMP_STEP:4; + uint32_t:12; + } bitfields, bits; + uint32_t u32All; + int32_t i32All; + float f32All; +} clk6_clk_postdiv_cntl_u_t; + +typedef union acp7x_clkmux_sel_u { + struct { + uint32_t DIV_SEL:4; + uint32_t:28; + } bitfields, bits; + uint32_t u32All; + int32_t i32All; + float f32All; +} acp7x_clkmux_sel_t; + typedef union acp_dsp_sw_intr_stat { struct { unsigned int host_to_dsp0_intr1_stat:1; diff --git a/src/platform/amd/acp_7_x/lib/clk.c b/src/platform/amd/acp_7_x/lib/clk.c index a587b433b4f9..a6eb97642711 100644 --- a/src/platform/amd/acp_7_x/lib/clk.c +++ b/src/platform/amd/acp_7_x/lib/clk.c @@ -4,6 +4,7 @@ * * Author: Sneha Voona * Basavaraj Hiregoudar + * DineshKumar Kalva */ #include @@ -18,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -68,6 +70,16 @@ typedef enum _acp_clock_type_ { acp_clock_type_force = 0xFF } acp_clock_type_t; +/* AGT measures CLK6 for 100 us against the 48 MHz reference. A threshold of + * 4800 counts gives the same 0.01 MHz resolution as agt_internal. + */ +#define ACP_CLK_MEASURE_THRESHOLD 4800 +#define ACP_CLK_MEASURE_REFCLK_MHZ 48 +#define ACP_CLK_MEASURE_SETTLE_US 120 +#define CLK_POLL_DELAY_US 10 +#define CLK_POLL_COUNT 10 +#define ACP_DFS_BYPASS_EXIT_RETRIES 4 + /* Static variables */ static struct clock_info platform_clocks_info[NUM_CLOCKS]; @@ -92,6 +104,9 @@ int acp_get_current_clk(acp_clock_type_t clk_type, /* Static function prototypes */ static int acp_reg_read_via_smn(uint32_t reg_offset, uint32_t size); static void acp_reg_write_via_smn(uint32_t reg_offset, uint32_t reg_value, uint32_t size); +static void acp_log_current_aclk(const char *state); +static void acp_log_dfs_dividers(const char *state); +static void acp_log_clk_sources(const char *state); static int acp_reg_read_via_smn(uint32_t reg_offset, uint32_t size) @@ -174,13 +189,39 @@ void acp_cpl_audioclk_poll_dfs_div_req_idle(uint32_t timeout_cnt) do { clk_dfs_status.u32All = acp_reg_read_via_smn( - mmCLK6_PLL0_DFS0_STATUS, sizeof(uint32_t)); + ACP_CLK6_PLL0_DFS0_STATUS, sizeof(uint32_t)); timeout++; } while ((clk_dfs_status.bitfields.PLL0_DFS0_DFS_DIV_REQ_IDLE == 0) & (timeout < timeout_cnt)); } +/** + * \brief Name a PMFW response code read from MP1_C2PMSG_38. + * + * \param resp Response register value. + * \return Printable description of the response. + */ +static const char *mp1_resp_name(uint32_t resp) +{ + switch (resp) { + case 0x0: + return "no response"; + case 0x1: + return "OK"; + case 0xFF: + return "FAIL"; + case 0xFE: + return "UNKNOWN CMD"; + case 0xFD: + return "BAD PREREQ"; + case 0xFC: + return "BUSY"; + default: + return "unexpected"; + } +} + uint32_t mp1_mailbox_send(uint32_t _reg_id, uint32_t _value) { uint32_t ready = 0; @@ -193,6 +234,8 @@ uint32_t mp1_mailbox_send(uint32_t _reg_id, uint32_t _value) } wait_cnt++; } + tr_info(&acp_clk_tr, "MP1 send msg 0x%x arg 0x%x: C2PMSG_38 before 0x%x (%s) after %u polls", + _reg_id, _value, ready, mp1_resp_name(ready), wait_cnt); if (ready == 0) { return 0; } @@ -214,7 +257,9 @@ uint32_t mp1_mailbox_send(uint32_t _reg_id, uint32_t _value) break; } } while (resp == 0); - /* Optionally, read back the argument register if needed (not used here) */ + tr_info(&acp_clk_tr, "PllPowerState msg 0x%x arg 0x%x response 0x%x (%s) after %d polls, " + "expect 0x1 (OK)", + _reg_id, _value, resp, mp1_resp_name(resp), count); return resp; } @@ -226,23 +271,161 @@ void acp_clk_tick_cnt_enable(void) clk_tick_config.u32All = acp_reg_read_via_smn( CLK_TICK_CNT_CONFIG_REG, sizeof(uint32_t)); clk_tick_config.bitfields.TIMER_ENABLE = 1; - clk_tick_config.bitfields.TIMER_THRESHOLD = 0x1E0; + clk_tick_config.bitfields.TIMER_THRESHOLD = ACP_CLK_MEASURE_THRESHOLD; acp_reg_write_via_smn(CLK_TICK_CNT_CONFIG_REG, clk_tick_config.u32All, sizeof(uint32_t)); } +/* Stop waiting for PMFW 500 us after the mailbox request is posted. */ +#define ACP_PMFW_POLL_US 500 + +/** + * \brief Send one PMFW mailbox message and wait up to 500 us for a reply. + * + * \param msg Message id written to MP1_C2PMSG_37. + * \param arg Argument written to MP1_C2PMSG_39. + * \return Last value read from MP1_C2PMSG_38. Non-zero means PMFW answered. + */ +static uint32_t acp_pmfw_poll_response(uint32_t msg, uint32_t arg) +{ + uint32_t resp = 0; + int try = 0; + uint64_t start; + uint64_t deadline; + uint32_t elapsed_us; + + acp_reg_write_via_smn(mmMP1_C2PMSG_38, 0, sizeof(uint32_t)); + acp_reg_write_via_smn(mmMP1_C2PMSG_39, arg, sizeof(uint32_t)); + acp_reg_write_via_smn(mmMP1_C2PMSG_37, msg, sizeof(uint32_t)); + + start = sof_cycle_get_64(); + deadline = start + k_us_to_cyc_ceil64(ACP_PMFW_POLL_US); + + do { + resp = acp_reg_read_via_smn(mmMP1_C2PMSG_38, sizeof(uint32_t)); + try++; + if (resp || sof_cycle_get_64() >= deadline) { + break; + } + wait_delay_us(1000);/*1ms delay to avoid busy loop*/ + } while (sof_cycle_get_64() < deadline); + + elapsed_us = (uint32_t)k_cyc_to_us_floor64(sof_cycle_get_64() - start); + tr_info(&acp_clk_tr, + "PMFW mailbox response: msg=0x%x arg=0x%x resp=0x%x tries=%d elapsed_us=%u limit_us=%u", + msg, arg, resp, try, elapsed_us, ACP_PMFW_POLL_US); + + return resp; +} + +/* ACP_CLKMUX_SEL divides ACLK inside ACP; slice select stays with CLK6. */ +static void acp_clkmux_sel(uint32_t div_val) +{ + acp7x_clkmux_sel_t mux_sel; + + mux_sel.u32All = (uint32_t)io_reg_read(PU_REGISTER_BASE + ACP_CLKMUX_SEL); + mux_sel.bitfields.DIV_SEL = div_val; + io_reg_write(PU_REGISTER_BASE + ACP_CLKMUX_SEL, mux_sel.u32All); +} + +/* Post a DFS bypass request and wait for CLK6 to clear it. */ +static void acp_clk_dfsbypass_request(uint32_t req_bit, uint32_t timeout_cnt) +{ + uint32_t value; + uint32_t count; + + value = acp_reg_read_via_smn(ACP_CLK6_PLL0_DFSBYPASS_CONTROL, sizeof(uint32_t)); + acp_reg_write_via_smn(ACP_CLK6_PLL0_DFSBYPASS_CONTROL, value | req_bit, + sizeof(uint32_t)); + + for (count = 0; count < timeout_cnt; count++) { + value = acp_reg_read_via_smn(ACP_CLK6_PLL0_DFSBYPASS_CONTROL, + sizeof(uint32_t)); + if (!(value & req_bit)) { + return; + } + wait_delay_us(CLK_POLL_DELAY_US); + } + + tr_warn(&acp_clk_tr, "DFS bypass request 0x%x still pending: cntl 0x%x", + req_bit, value); +} + +/* Configure CLK6 slice 5, which feeds S5_REFCLK. */ +static void acp_s5_refclk_config(uint32_t postdiv_id, uint32_t bypass_sel, + uint32_t bypass_div) +{ + clk6_clk_postdiv_cntl_u_t postdiv_cntl; + clk6_bypass_cntl_u_t bypass_cntl; + + postdiv_cntl.u32All = 0; + postdiv_cntl.bitfields.CLK0_POSTDIV_DIV_ID = postdiv_id; + postdiv_cntl.bitfields.CLK0_POSTDIV_RAMP_STEP = 1; + acp_reg_write_via_smn(ACP_CLK6_CLK5_POSTDIV_CNTL, postdiv_cntl.u32All, + sizeof(uint32_t)); + + bypass_cntl.u32All = 0; + bypass_cntl.bitfields.CLK_BYPASS_SEL = bypass_sel; + bypass_cntl.bitfields.CLK_BYPASS_DIV = bypass_div; + acp_reg_write_via_smn(ACP_CLK6_CLK5_BYPASS_CNTL, bypass_cntl.u32All, + sizeof(uint32_t)); +} + +/* Keep ACLK out of deep sleep while a stream is active. */ +static void acp_aclk_deepsleep_disable(void) +{ + clk6_clk_ds_cntl_u_t ds_cntl; + + io_reg_write(PU_REGISTER_BASE + ACP_ACLK_AUDIOCLK_CTRL, ACP_DEEPSLEEP_DISABLE); + + ds_cntl.u32All = acp_reg_read_via_smn(ACP_CLK6_CLK1_DS_CNTL, sizeof(uint32_t)); + ds_cntl.bitfields.CLK_ALLOW_DS = 0; + ds_cntl.bitfields.CLK_DS_DIV_ID = 3; + ds_cntl.bitfields.CLK_DS_DEGL_SET = 1; + acp_reg_write_via_smn(ACP_CLK6_CLK1_DS_CNTL, ds_cntl.u32All, sizeof(uint32_t)); +} + +/* Restore the clock inputs used by PDM and SoundWire consumers. */ +static void acp_io_clk_sel_restore(void) +{ + io_reg_write(PU_REGISTER_BASE + ACP_SW_48MHz_CLK_SEL, ACP_SW_CLK_SEL_49152KHZ); + io_reg_write(PU_REGISTER_BASE + ACP_PDM_CORE_CLK_SEL, 0); + io_reg_write(PU_REGISTER_BASE + ACP_AUDIO_CLK_SEL, 0); +} + +/** + * \brief Power the audio PLL and move AUDIOCLK and ACLK onto it. + * + * Called once from platform_init(). PDM and SoundWire run from these + * clocks, so DMA start and stop must not run this sequence again while + * any stream is active. + * + * \param clock_freq Requested ACLK frequency in Hz. + */ void acp_clk_d0_sequence(uint32_t clock_freq) { - /* Send message to PMFW to power on Audio PLL */ - mp1_mailbox_send(ACPSMC_MSG_PllPowerState, ACP_AUDIOPLL_POWER_ON_REQ); + uint32_t retry; - change_clock_notify(clock_freq); + if (!mp1_mailbox_send(ACPSMC_MSG_PllPowerState, ACP_AUDIOPLL_POWER_ON_REQ)) { + tr_err(&acp_clk_tr, "Audio PLL power on failed, staying on bypass clock"); + return; + } + + /* Take AUDIOCLK (slice 0) and ACLK (slice 1) out of DFS bypass */ + for (retry = 0; retry < ACP_DFS_BYPASS_EXIT_RETRIES; retry++) { + acp_clk_dfsbypass_request(ACP_DFS_BYPASS_EXIT_AUDIOCLK, CLK_POLL_COUNT / 2); + acp_clk_dfsbypass_request(ACP_DFS_BYPASS_EXIT_ACLK, CLK_POLL_COUNT / 2); + } /* Audio PLL DFS Output for AUDIOCLK */ - acp_reg_write_via_smn(mmCLK6_CLK0_BYPASS_CNTL, 0, sizeof(uint32_t)); + acp_reg_write_via_smn(ACP_CLK6_CLK0_BYPASS_CNTL, 0, sizeof(uint32_t)); /* Audio PLL DFS Output for ACLK */ - acp_reg_write_via_smn(mmCLK6_CLK1_BYPASS_CNTL, 0, sizeof(uint32_t)); - + acp_reg_write_via_smn(ACP_CLK6_CLK1_BYPASS_CNTL, 0, sizeof(uint32_t)); + /* Run ACP off the undivided ACLK enable acp_clkmux_sel(ACP_CLK_DIV_BY_1);*/ + /* Only now program the DFS divider: the slice is off bypass */ + change_clock_notify(clock_freq); + /* S5_REFCLK back on the 24 MHz reference with the postdiv reset to enable it acp_s5_refclk_config(0, ACP_CLK_BYPASS_SEL_REFCLK_24MHZ, 0);*/ + /* restore io clk acp_io_clk_sel_restore();*/ } void acp_clk_d3_sequence(void) @@ -253,14 +436,17 @@ void acp_clk_d3_sequence(void) bypass_cntl.bitfields.CLK_BYPASS_SEL = 4; /* SET AUDIOCLK TO LPPLL 196.608MHz */ - acp_reg_write_via_smn(mmCLK6_CLK0_BYPASS_CNTL, bypass_cntl.u32All, sizeof(uint32_t)); + acp_reg_write_via_smn(ACP_CLK6_CLK0_BYPASS_CNTL, bypass_cntl.u32All, sizeof(uint32_t)); bypass_cntl.u32All = 0; bypass_cntl.bitfields.CLK_BYPASS_SEL = 2; /* SET ACLK TO LPPLL 393MHz */ - acp_reg_write_via_smn(mmCLK6_CLK1_BYPASS_CNTL, bypass_cntl.u32All, sizeof(uint32_t)); - /* mp1_mailbox_send(ACPSMC_MSG_PllPowerState, ACP_AUDIOPLL_POWER_OFF_REQ); */ + acp_reg_write_via_smn(ACP_CLK6_CLK1_BYPASS_CNTL, bypass_cntl.u32All, sizeof(uint32_t)); + + /* Both slices must be in DFS bypass before the PLL goes down */ + acp_clk_dfsbypass_request(ACP_DFS_BYPASS_ENTER_AUDIOCLK, CLK_POLL_COUNT); + acp_clk_dfsbypass_request(ACP_DFS_BYPASS_ENTER_ACLK, CLK_POLL_COUNT); /* Send message to PMFW to power off Audio PLL */ mp1_mailbox_send(ACPSMC_MSG_PllPowerState, ACP_AUDIOPLL_POWER_OFF_REQ_WITH_WOV_EN); @@ -272,17 +458,25 @@ void change_clock_notify(uint32_t clock_freq) float did = 0.0f; float fraction_val = 0.0f; uint32_t int_did_val = 0; + uint32_t raw_did_x100; float boot_ref_clk = 0.0f; acp_clock_type_t clock_type = acp_aclk_clock; + uint32_t err_before = io_reg_read(PU_REGISTER_BASE + ACP_ERROR_STATUS); acp_7_x_get_boot_ref_clock(&boot_ref_clk); tr_info(&acp_clk_tr, "acp_change_clock_notify clock_freq : %d clock_type : %d", clock_freq, clock_type); + tr_info(&acp_clk_tr, "boot_ref_clk SMN: ACP_ERROR_STATUS 0x%x -> 0x%x smn_refused %u", + err_before, io_reg_read(PU_REGISTER_BASE + ACP_ERROR_STATUS), + (io_reg_read(PU_REGISTER_BASE + ACP_ERROR_STATUS) >> 20) & 1); fraction_val = (float)(clock_freq / (float)1000000.0f); /*converting Hz to Mhz*/ clock_freq = (clock_freq / 1000000); /*converting Hz to Mhz*/ did = (float)(boot_ref_clk/(float)fraction_val); + raw_did_x100 = (uint32_t)(did * 100.0f); + tr_info(&acp_clk_tr, "aclk req %d MHz boot_ref_clk %d MHz raw_did %d/%dMHz", + clock_freq, (uint32_t)boot_ref_clk, raw_did_x100, clock_freq); if (did > 62.0f) { final_did = 0x7F; } else { @@ -323,6 +517,7 @@ void change_clock_notify(uint32_t clock_freq) } } + tr_info(&acp_clk_tr, "aclk final_did 0x%x", final_did); acp_clk_update_dfs_did((uint32_t)final_did, clock_type); } @@ -347,7 +542,8 @@ uint32_t acp_clk_update_dfs_did(uint32_t did, uint32_t clk_type) } else if (clk_type == acp_sclk_clock) { acp_cpl_audioclk_dfs_did_update(true, 10, did); } - + wait_delay_us(ACP_CLK_MEASURE_SETTLE_US); + acp_log_current_aclk("D0"); acp_get_current_clk(acp_aclk_clock, (float *)&updated_clk); return updated_clk; @@ -438,3 +634,52 @@ int acp_get_current_clk(acp_clock_type_t clk_type, float *clk_value) *clk_value = temp_clk_value / (clk_tick_config.bitfields.TIMER_THRESHOLD / 48.0f); return 0; /* Success */ } + +/** + * \brief Print one CLK6 slice the way agt_internal prints Current : MHz. + * + * Frequency is count * 48 MHz / TIMER_THRESHOLD, shown to 0.01 MHz. + * + * \param state Clock transition that initiated the measurement. + * \param name ACLK or AUDIOCLK. + * \param clk_type CLK6 slice to read. + */ +static void acp_log_agt_clk(const char *state, const char *name, acp_clock_type_t clk_type) +{ + clk_tick_cnt_config_reg_t clk_tick_config; + uint32_t clk_mhz_x100; + uint32_t threshold; + int count; + uint32_t cnt_reg; + + cnt_reg = (clk_type == acp_sclk_clock) ? CLK0_CURRENT_CNT : CLK1_CURRENT_CNT; + clk_tick_config.u32All = acp_reg_read_via_smn(CLK_TICK_CNT_CONFIG_REG, sizeof(uint32_t)); + count = acp_reg_read_via_smn(cnt_reg, sizeof(uint32_t)); + threshold = clk_tick_config.bitfields.TIMER_THRESHOLD; + + if (count < 0 || !threshold) { + tr_warn(&acp_clk_tr, "%s Current %s: measurement unavailable count %d threshold %u", + state, name, count, threshold); + return; + } + + /* AGT: MHz = CURRENT_CNT * 48 MHz / TIMER_THRESHOLD, printed to 0.01 MHz. */ + clk_mhz_x100 = (uint32_t)(((uint64_t)count * ACP_CLK_MEASURE_REFCLK_MHZ * 100U + + (threshold / 2U)) / threshold); + tr_info(&acp_clk_tr, "%s Current %s: %u.%02u MHz (count %u threshold %u)", + state, name, clk_mhz_x100 / 100U, clk_mhz_x100 % 100U, + (uint32_t)count, threshold); +} + +/** + * \brief Log ACLK and AUDIOCLK with the agt_internal tick-counter measurement. + * + * \param state Clock transition that initiated the measurement. + */ +static void acp_log_current_aclk(const char *state) +{ + acp_clk_tick_cnt_enable(); + wait_delay_us(ACP_CLK_MEASURE_SETTLE_US); + acp_log_agt_clk(state, "ACLK", acp_aclk_clock); + acp_log_agt_clk(state, "AUDIOCLK", acp_sclk_clock); +} diff --git a/src/platform/amd/acp_7_x/platform.c b/src/platform/amd/acp_7_x/platform.c index 2e5b28ebfa05..37d6d3d45ed9 100644 --- a/src/platform/amd/acp_7_x/platform.c +++ b/src/platform/amd/acp_7_x/platform.c @@ -150,6 +150,8 @@ int platform_init(struct sof *sof) /*CONFIG_SYSTICK_PERIOD hardcoded as 200000*/ sa_init(sof, 200000); acp_clk_tick_cnt_enable(); + /* Boot-time ACLK setup only. Stream start must not call this. */ + acp_change_clock_notify(CLK_MAX_CPU_HZ); clock_set_freq(CLK_CPU(cpu_get_id()), CLK_MAX_CPU_HZ); /* diff --git a/tools/rimage/config/acp_7_x.toml b/tools/rimage/config/acp_7_x.toml index 7787bb3c6e1c..47db8f7cee38 100644 --- a/tools/rimage/config/acp_7_x.toml +++ b/tools/rimage/config/acp_7_x.toml @@ -16,5 +16,5 @@ host_offset = "0x0" [[adsp.mem_zone]] type = "SRAM" base = "0x6000C000" -size = "0x274000" +size = "0x3F4000" host_offset = "0x0" \ No newline at end of file diff --git a/tools/topology/topology1/CMakeLists.txt b/tools/topology/topology1/CMakeLists.txt index 95d81a01629f..e78a7c2e25ff 100644 --- a/tools/topology/topology1/CMakeLists.txt +++ b/tools/topology/topology1/CMakeLists.txt @@ -125,6 +125,10 @@ set(TPLGS "sof-acp_7_x_sdw\;sof-acp7x-rt721-l0" "sof-acp_7_x_i2s\;sof-acp7x" "sof-acp_7_x_i2s_24bit\;sof-acp7x-24bit" + "sof-acp_7_x_sdw_pdm\;sof-acp7x-sdw-pdm-48k\;-DPDM_INDEX=0\;-DPDM_RATE=48000" + "sof-acp_7_x_sdw_pdm\;sof-acp7x-sdw-pdm1-16k\;-DPDM_INDEX=1\;-DPDM_RATE=16000" + "sof-acp_7_x_sdw_pdm\;sof-acp7x-sdw-pdm1-48k\;-DPDM_INDEX=1\;-DPDM_RATE=48000" + "sof-acp_7_x_sdw_pdm\;sof-acp7x-sdw-pdm1-96k\;-DPDM_INDEX=1\;-DPDM_RATE=96000" ) # This empty 'production/' source subdirectory exists only to create the diff --git a/tools/topology/topology1/sof-acp_7_x_sdw_pdm.m4 b/tools/topology/topology1/sof-acp_7_x_sdw_pdm.m4 new file mode 100644 index 000000000000..1ade5d55be45 --- /dev/null +++ b/tools/topology/topology1/sof-acp_7_x_sdw_pdm.m4 @@ -0,0 +1,122 @@ +#Required Topology for rt722 with ACP DMIC Card for ACP7X. +#Required PDM instance and capture rate: +# -DPDM_INDEX=0 PDM0, PCM name DMIC0, rate 48000 only +# -DPDM_INDEX=1 PDM1, PCM name DMIC1 +# -DPDM_RATE=48000 for PDM0; 16000|48000|96000 for PDM1 +# +# PCM Description DAI LINK DAI BE +# 0 HS Playback 0 SDW0-PIN0-PLAYBACK AUDIO_TX +# 2 Speaker playback 2 SDW0-PIN4-PLAYBACK BT_TX +# 3 ACP PDM DMIC 5 acp-dmic-codec +# +# DMIC capture rate. PDM0 is 48000 only. Playback stays 48000. +ifdef(`PDM_RATE', `', `define(`PDM_RATE', 48000)') + +# DMIC PDM instance (dai_index). 0 is PDM0, 1 is PDM1. +ifdef(`PDM_INDEX', `', `define(`PDM_INDEX', 0)') +ifelse(PDM_INDEX, 0, `ifelse(PDM_RATE, 48000, `', +`errprint(`PDM0 capture rate must be 48000, got 'PDM_RATE` +')m4exit(1)')') + +# +# Define the pipelines +# +# PCM0 ----> buffer ----> AUDIO_TX +# PCM2 ----> buffer ----> BT_TX +# PCM3 <---- buffer <---- acp-dmic-codec + +# Include topology builder +include(`utils.m4') +include(`dai.m4') +include(`pipeline.m4') +include(`acp-sdw.m4') +include(`acp-dmic.m4') + +# Include TLV library +include(`common/tlv.m4') + +# Include Token library +include(`sof/tokens.m4') + +# Include ACP DSP configuration +include(`platform/amd/acp.m4') + +DEBUG_START + +define(DI_SDW0_ACP_SW_AUDIO_0_TX, 0 ) +define(DI_SDW0_ACP_SW_AUDIO_4_TX, 4 ) + +define(DAI_BE_SDW0_ACP_SW_AUDIO_4_TX, SDW0-PIN4-PLAYBACK) +define(DAI_BE_SDW0_ACP_SW_AUDIO_0_TX, SDW0-PIN0-PLAYBACK) + +#pipeline: name of the predefined pipeline +#pipe id: pipeline ID. This should be a unique ID identifying the pipeline +#pcm: PCM ID. This will be used to bind to the correct front end DAI link + +dnl PIPELINE_PCM_ADD(pipeline, +dnl pipe id, pcm, max channels, format, +dnl period, priority, core, +dnl pcm_min_rate, pcm_max_rate, pipeline_rate) + +dnl DAI_ADD(pipeline, +dnl pipe id, dai type, dai_index, dai_be, +dnl buffer, periods, format, +dnl deadline, priority, core, time_domain) + +dnl DAI_CONFIG(type, dai_index, link_id, name, acphs_config/acpdmic_config) +dnl ACPHS_CONFIG(format, mclk, bclk, fsync, tdm, acphs_config_data) +dnl ACP_CLOCK(clock, freq, codec_provider, polarity) +dnl ACPHS_CONFIG_DATA(type, idx, valid bits, mclk_id) +dnl mclk_id is optional + +#/**********************************************************************************/ +# PCM 0, HS Playback, DAI link id 0, Dai index 0(Audio_Tx), BE SW0-PIN0-PLAYBACK +PIPELINE_PCM_ADD(sof/pipe-passthrough-playback.m4, + 0, 0, 2, s16le, + 2000, 0, 0, + 48000, 48000, 48000) + +DAI_ADD(sof/pipe-dai-playback.m4, 0, ACP_SDW, DI_SDW0_ACP_SW_AUDIO_0_TX, DAI_BE_SDW0_ACP_SW_AUDIO_0_TX, +PIPELINE_SOURCE_0, 2, s16le, 2000, 0, 0, SCHEDULE_TIME_DOMAIN_TIMER) + +DAI_CONFIG(ACP_SDW, DI_SDW0_ACP_SW_AUDIO_0_TX, 0, DAI_BE_SDW0_ACP_SW_AUDIO_0_TX, + ACP_SDW_CONFIG(ACP_SDW_CONFIG_DATA(ACP_SDW, DI_SDW0_ACP_SW_AUDIO_0_TX, 48000, 2))) +PCM_PLAYBACK_ADD(ACP-SW0-PIN0-Playback-HS, 0, PIPELINE_PCM_0) + +#/**********************************************************************************/ +#PCM 2, Speaker Playback, PIPE line 2, DAI link id 2, Dai index 4(DI_SDW0_ACP_SW_AUDIO_4_TX), DAI BE SDW0-PIN4-PLAYBACK-SmartAmp +PIPELINE_PCM_ADD(sof/pipe-passthrough-playback.m4, + 2, 2, 2, s16le, + 2000, 0, 0, + 48000, 48000, 48000) + +# playback DAI is ACP soundwire using 2 periods +DAI_ADD(sof/pipe-dai-playback.m4, 2, ACP_SDW, DI_SDW0_ACP_SW_AUDIO_4_TX, DAI_BE_SDW0_ACP_SW_AUDIO_4_TX, + PIPELINE_SOURCE_2, 2, s16le, 2000, 0, 0, SCHEDULE_TIME_DOMAIN_TIMER) + +DAI_CONFIG(ACP_SDW, DI_SDW0_ACP_SW_AUDIO_4_TX, 2, DAI_BE_SDW0_ACP_SW_AUDIO_4_TX, + ACP_SDW_CONFIG(ACP_SDW_CONFIG_DATA(ACP_SDW, DI_SDW0_ACP_SW_AUDIO_4_TX, 48000, 2))) + +PCM_PLAYBACK_ADD(ACP-SW0-PIN4-Playback-SPK, 2, PIPELINE_PCM_2) +#/**********************************************************************************/ +# Capture pipeline 6 on PCM 3 using max 2 channels of s32le. +# DAI index PDM_INDEX (0 = PDM0, 1 = PDM1), BE acp-dmic-codec, DAI link id 5. +PIPELINE_PCM_ADD(sof/pipe-passthrough-capture.m4, + 6, 3, 2, s32le, + 2000, 0, 0, + PDM_RATE, PDM_RATE, PDM_RATE) + +DAI_ADD(sof/pipe-dai-capture.m4, + 6, ACPDMIC, PDM_INDEX, acp-dmic-codec, + PIPELINE_SINK_6, 2, s32le, + 2000, 0, 0, SCHEDULE_TIME_DOMAIN_TIMER) + +#dnl DAI_CONFIG(type, dai_index, link_id, name, acpdmic_config) +DAI_CONFIG(ACPDMIC, PDM_INDEX, 5, acp-dmic-codec, + ACPDMIC_CONFIG(ACPDMIC_CONFIG_DATA(ACPDMIC, PDM_INDEX, PDM_RATE, 2))) + +#========================================================================== +# PCM id 3. Name follows PDM_INDEX: DMIC0 or DMIC1. +PCM_CAPTURE_ADD(DMIC`'PDM_INDEX, 3, PIPELINE_PCM_6) +#/**********************************************************************************/ +DEBUG_END diff --git a/zephyr/lib/dma.c b/zephyr/lib/dma.c index a478d5069a09..ad19a71ea84f 100644 --- a/zephyr/lib/dma.c +++ b/zephyr/lib/dma.c @@ -221,7 +221,11 @@ APP_SYSUSER_DATA struct sof_dma dma[] = { .devs = SOF_DMA_DEV_HOST, .base = DMA0_BASE, .chan_size = DMA0_SIZE, +#if defined(CONFIG_SOC_ACP_7_X) + .channels = 10, +#else .channels = 8, +#endif .period_count = 2, }, .z_dev = DEVICE_DT_GET(DT_NODELABEL(acp_host_dma)), @@ -257,6 +261,19 @@ APP_SYSUSER_DATA struct sof_dma dma[] = { }, .z_dev = DEVICE_DT_GET(DT_NODELABEL(acp_tdm_dma)), }, +{ + .plat_data = { + .dir = SOF_DMA_DIR_MEM_TO_DEV | + SOF_DMA_DIR_DEV_TO_MEM, + .devs = SOF_DMA_DEV_DMIC, + .caps = SOF_DMA_CAP_DMIC, + .base = DMA0_BASE, + .chan_size = DMA0_SIZE, + .channels = 8, + .period_count = 2, + }, + .z_dev = DEVICE_DT_GET(DT_NODELABEL(acp_dmic_dma)), +}, #endif #if DT_HAS_COMPAT_STATUS_OKAY(mediatek_afe_memif_dma) {