diff --git a/src/audio/dai.c b/src/audio/dai.c index de086eafe723..0bcd19db7025 100644 --- a/src/audio/dai.c +++ b/src/audio/dai.c @@ -628,6 +628,7 @@ static void dai_config_reset(struct comp_dev *dev) notifier_unregister(dev, dd->chan, NOTIFIER_ID_DMA_COPY); } + dai_hw_free(dd->dai); } static int dai_prepare(struct comp_dev *dev) diff --git a/src/drivers/intel/ssp/ssp.c b/src/drivers/intel/ssp/ssp.c index 7af11b4274d8..5a152f8ed1fa 100644 --- a/src/drivers/intel/ssp/ssp.c +++ b/src/drivers/intel/ssp/ssp.c @@ -613,35 +613,49 @@ static int ssp_set_config(struct dai *dai, struct ipc_config_dai *common_config, return ret; } -/* - * 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) +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; - uint32_t sscr0; - uint32_t mdiv; - bool need_ecs = false; - - int ret = 0; - - dai_info(dai, "ssp_pre_start()"); + int ret; - /* SSP active means bclk already configured. */ - 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_MCLK_ACTIVE) return 0; /* MCLK config */ ret = mn_set_mclk(config->ssp.mclk_id, config->ssp.mclk_rate); - if (ret < 0) { + if (ret < 0) dai_err(dai, "invalid mclk_rate = %d for mclk_id = %d", config->ssp.mclk_rate, config->ssp.mclk_id); - goto out; - } + 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); @@ -686,6 +700,51 @@ static int ssp_pre_start(struct dai *dai) dai_info(dai, "ssp_set_config(), 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; +} + +/* + * 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->params.clks_control & + SOF_DAI_INTEL_SSP_CLKCTRL_MCLK_ES)) { + /* MCLK config */ + ret = ssp_mclk_prepare_enable(dai); + if (ret < 0) + return ret; + } + + if (!(ssp->params.clks_control & + SOF_DAI_INTEL_SSP_CLKCTRL_BCLK_ES)) + ret = ssp_bclk_prepare_enable(dai); return ret; } @@ -702,11 +761,16 @@ static void ssp_post_stop(struct dai *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) { - dai_info(dai, "releasing BCLK/MCLK clocks for SSP%d...", dai->index); -#if CONFIG_INTEL_MN - mn_release_bclk(dai->index); -#endif - mn_release_mclk(ssp->config.ssp.mclk_id); + if (!(ssp->params.clks_control & + SOF_DAI_INTEL_SSP_CLKCTRL_BCLK_ES)) { + dai_info(dai, "ssp_post_stop releasing BCLK clocks for SSP%d...", dai->index); + ssp_bclk_disable_unprepare(dai); + } + if (!(ssp->params.clks_control & + SOF_DAI_INTEL_SSP_CLKCTRL_MCLK_ES)) { + dai_info(dai, "ssp_post_stop releasing MCLK clocks for SSP%d...", dai->index); + ssp_mclk_disable_unprepare(dai); + } } } @@ -742,6 +806,78 @@ static int ssp_get_hw_params(struct dai *dai, return 0; } +/* + * enabled clock MCLK/BCLK to support codec that need MCLK/BCLK + * This is called before trigger(COMP_TRIGGER_START) + */ +static int ssp_hw_params(struct dai *dai, + struct sof_ipc_stream_params *params) +{ + struct ssp_pdata *ssp = dai_get_drvdata(dai); + int ret; + + if (ssp->state[SOF_IPC_STREAM_CAPTURE] < COMP_STATE_PREPARE || + ssp->state[SOF_IPC_STREAM_PLAYBACK] < COMP_STATE_PREPARE) { + dai_err(dai, "ssp_hw_params(): Please set config first"); + return -EINVAL; + } + + dai_info(dai, "ssp_hw_params() params.clks_control 0x%x", ssp->params.clks_control); + + spin_lock(&dai->lock); + + if ((ssp->params.clks_control & + SOF_DAI_INTEL_SSP_CLKCTRL_MCLK_ES)) { + ret = ssp_mclk_prepare_enable(dai); + if (ret < 0) + goto out; + } + + if ((ssp->params.clks_control & + SOF_DAI_INTEL_SSP_CLKCTRL_BCLK_ES)) { + ret = ssp_bclk_prepare_enable(dai); + if (ret < 0) + goto out; + /* enable port */ + ssp_update_bits(dai, SSCR0, SSCR0_SSE, SSCR0_SSE); + } +out: + + spin_unlock(&dai->lock); + return 0; +} + +/* + * disabled clock MCLK/BCLK to support codec that need MCLK/BCLK + * This is called after trigger(COMP_TRIGGER_STOP) + */ +static void ssp_hw_free(struct dai *dai) +{ + struct ssp_pdata *ssp = dai_get_drvdata(dai); + + dai_info(dai, "ssp_hw_free()"); + + spin_lock(&dai->lock); + + /* release clocks if SSP is inactive */ + if (ssp->state[SOF_IPC_STREAM_CAPTURE] == COMP_STATE_PREPARE && + ssp->state[SOF_IPC_STREAM_PLAYBACK] == COMP_STATE_PREPARE) { + if ((ssp->params.clks_control & + SOF_DAI_INTEL_SSP_CLKCTRL_BCLK_ES)) { + dai_info(dai, "ssp_hw_free() releasing BCLK clocks for SSP%d...", dai->index); + ssp_update_bits(dai, SSCR0, SSCR0_SSE, 0); + ssp_bclk_disable_unprepare(dai); + } + if ((ssp->params.clks_control & + SOF_DAI_INTEL_SSP_CLKCTRL_MCLK_ES)) { + dai_info(dai, "ssp_hw_free releasing MCLK clocks for SSP%d...", dai->index); + ssp_mclk_disable_unprepare(dai); + } + } + + spin_unlock(&dai->lock); +} + /* start the SSP for either playback or capture */ static void ssp_start(struct dai *dai, int direction) { @@ -752,8 +888,11 @@ static void ssp_start(struct dai *dai, int direction) /* request mclk/bclk */ ssp_pre_start(dai); - /* enable port */ - ssp_update_bits(dai, SSCR0, SSCR0_SSE, SSCR0_SSE); + if (!(ssp->params.clks_control & + SOF_DAI_INTEL_SSP_CLKCTRL_BCLK_ES)) { + /* enable port */ + ssp_update_bits(dai, SSCR0, SSCR0_SSE, SSCR0_SSE); + } ssp->state[direction] = COMP_STATE_ACTIVE; dai_info(dai, "ssp_start()"); @@ -814,8 +953,11 @@ static void ssp_stop(struct dai *dai, int direction) /* 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) { - ssp_update_bits(dai, SSCR0, SSCR0_SSE, 0); - dai_info(dai, "ssp_stop(), SSP port disabled"); + if (!(ssp->params.clks_control & + SOF_DAI_INTEL_SSP_CLKCTRL_BCLK_ES)) { + ssp_update_bits(dai, SSCR0, SSCR0_SSE, 0); + dai_info(dai, "ssp_stop(), SSP port disabled"); + } } ssp_post_stop(dai); @@ -907,14 +1049,10 @@ static int ssp_probe(struct dai *dai) static int ssp_remove(struct dai *dai) { - struct ssp_pdata *ssp = dai_get_drvdata(dai); - pm_runtime_put_sync(SSP_CLK, dai->index); - mn_release_mclk(ssp->config.ssp.mclk_id); -#if CONFIG_INTEL_MN - mn_release_bclk(dai->index); -#endif + ssp_mclk_disable_unprepare(dai); + ssp_bclk_disable_unprepare(dai); /* Disable SSP power */ pm_runtime_put_sync(SSP_POW, dai->index); @@ -949,6 +1087,8 @@ const struct dai_driver ssp_driver = { .get_hw_params = ssp_get_hw_params, .get_handshake = ssp_get_handshake, .get_fifo = ssp_get_fifo, + .hw_params = ssp_hw_params, + .hw_free = ssp_hw_free, .probe = ssp_probe, .remove = ssp_remove, }, diff --git a/src/include/ipc/dai-intel.h b/src/include/ipc/dai-intel.h index 5dd7a00744c3..41e39a47db1f 100644 --- a/src/include/ipc/dai-intel.h +++ b/src/include/ipc/dai-intel.h @@ -56,6 +56,10 @@ #define SOF_DAI_INTEL_SSP_CLKCTRL_FS_KA BIT(4) /* bclk idle */ #define SOF_DAI_INTEL_SSP_CLKCTRL_BCLK_IDLE_HIGH BIT(5) +/* mclk early start */ +#define SOF_DAI_INTEL_SSP_CLKCTRL_MCLK_ES BIT(6) +/* bclk early start */ +#define SOF_DAI_INTEL_SSP_CLKCTRL_BCLK_ES BIT(7) /* DMIC max. four controllers for eight microphone channels */ #define SOF_DAI_INTEL_DMIC_NUM_CTRL 4 diff --git a/src/include/sof/drivers/ssp.h b/src/include/sof/drivers/ssp.h index 051d404e28b6..d672159ca691 100644 --- a/src/include/sof/drivers/ssp.h +++ b/src/include/sof/drivers/ssp.h @@ -224,12 +224,16 @@ extern const struct dai_driver ssp_driver; #define ssp_irq(ssp) \ ssp->plat_data.irq +#define SSP_CLK_MCLK_ACTIVE BIT(0) +#define SSP_CLK_BCLK_ACTIVE BIT(1) + /* SSP private data */ struct ssp_pdata { uint32_t sscr0; uint32_t sscr1; uint32_t psp; uint32_t state[2]; /* SSP_STATE_ for each direction */ + uint32_t clk_active; struct sof_ipc_dai_config config; struct sof_ipc_dai_ssp_params params; }; diff --git a/src/include/sof/lib/dai.h b/src/include/sof/lib/dai.h index 67765b4153d9..24ff63f21108 100644 --- a/src/include/sof/lib/dai.h +++ b/src/include/sof/lib/dai.h @@ -82,6 +82,7 @@ struct dai_ops { int (*get_hw_params)(struct dai *dai, struct sof_ipc_stream_params *params, int dir); int (*hw_params)(struct dai *dai, struct sof_ipc_stream_params *params); + void (*hw_free)(struct dai *dai); int (*get_handshake)(struct dai *dai, int direction, int stream_id); int (*get_fifo)(struct dai *dai, int direction, int stream_id); int (*probe)(struct dai *dai); @@ -423,6 +424,12 @@ static inline int dai_hw_params(struct dai *dai, return ret; } +static inline void dai_hw_free(struct dai *dai) +{ + if (dai->drv->ops.hw_free) + dai->drv->ops.hw_free(dai); +} + /** * \brief Get Digital Audio interface DMA Handshake */ diff --git a/tools/topology/platform/common/ssp.m4 b/tools/topology/platform/common/ssp.m4 index ce1f2c35b876..39a5ba185b0f 100644 --- a/tools/topology/platform/common/ssp.m4 +++ b/tools/topology/platform/common/ssp.m4 @@ -30,6 +30,11 @@ $6 dnl SSP_QUIRK_LBM 64 = (1 << 6) define(`SSP_QUIRK_LBM', 64) +dnl SSP_CC_MCLK_ES 64 = (1 << 6) +define(`SSP_CC_MCLK_ES', 64) +dnl SSP_CC_BCLK_ES 128 = (1 << 7) +define(`SSP_CC_BCLK_ES', 128) + dnl SSP_CONFIG_DATA(type, idx, valid bits, mclk_id, quirks, bclk_delay, dnl clks_control, pulse_width, padding) dnl mclk_id, quirks, bclk_delay clks_control, pulse_width and padding are optional diff --git a/tools/topology/sof-apl-nocodec.m4 b/tools/topology/sof-apl-nocodec.m4 index eb8fe1ddb605..e32bc753bf7d 100644 --- a/tools/topology/sof-apl-nocodec.m4 +++ b/tools/topology/sof-apl-nocodec.m4 @@ -272,42 +272,48 @@ DAI_CONFIG(SSP, 0, 0, NoCodec-0, SSP_CLOCK(fsync, 48000, codec_slave), SSP_TDM(2, 16, 3, 3), dnl SSP_CONFIG_DATA(type, dai_index, valid bits, mclk_id, quirks) - SSP_CONFIG_DATA(SSP, 0, 16, 0, SSP_QUIRK_LBM))) + SSP_CONFIG_DATA(SSP, 0, 16, 0, SSP_QUIRK_LBM, 0, + SSP_CC_MCLK_ES | SSP_CC_BCLK_ES))) DAI_CONFIG(SSP, 1, 1, NoCodec-1, SSP_CONFIG(I2S, SSP_CLOCK(mclk, 24576000, codec_mclk_in), SSP_CLOCK(bclk, 1536000, codec_slave), SSP_CLOCK(fsync, 48000, codec_slave), SSP_TDM(2, 16, 3, 3), - SSP_CONFIG_DATA(SSP, 1, 16, 0, SSP_QUIRK_LBM))) + SSP_CONFIG_DATA(SSP, 1, 16, 0, SSP_QUIRK_LBM, 0, + SSP_CC_MCLK_ES | SSP_CC_BCLK_ES))) #DAI_CONFIG(SSP, 2, 2, NoCodec-2, # SSP_CONFIG(I2S, SSP_CLOCK(mclk, 24576000, codec_mclk_in), # SSP_CLOCK(bclk, 1536000, codec_slave), # SSP_CLOCK(fsync, 48000, codec_slave), # SSP_TDM(2, 16, 3, 3), -# SSP_CONFIG_DATA(SSP, 2, 16, 0, SSP_QUIRK_LBM))) +# SSP_CONFIG_DATA(SSP, 2, 16, 0, SSP_QUIRK_LBM, 0, +# SSP_CC_MCLK_ES | SSP_CC_BCLK_ES))) DAI_CONFIG(SSP, 3, 3, NoCodec-3, SSP_CONFIG(I2S, SSP_CLOCK(mclk, 24576000, codec_mclk_in), SSP_CLOCK(bclk, 1536000, codec_slave), SSP_CLOCK(fsync, 48000, codec_slave), SSP_TDM(2, 16, 3, 3), - SSP_CONFIG_DATA(SSP, 3, 16, 0, SSP_QUIRK_LBM))) + SSP_CONFIG_DATA(SSP, 3, 16, 0, SSP_QUIRK_LBM, 0, + SSP_CC_MCLK_ES | SSP_CC_BCLK_ES))) #DAI_CONFIG(SSP, 4, 4, NoCodec-4, # SSP_CONFIG(I2S, SSP_CLOCK(mclk, 24576000, codec_mclk_in), # SSP_CLOCK(bclk, 1536000, codec_slave), # SSP_CLOCK(fsync, 48000, codec_slave), # SSP_TDM(2, 16, 3, 3), -# SSP_CONFIG_DATA(SSP, 4, 16, 0, SSP_QUIRK_LBM))) +# SSP_CONFIG_DATA(SSP, 4, 16, 0, SSP_QUIRK_LBM, 0, +# SSP_CC_MCLK_ES | SSP_CC_BCLK_ES))) DAI_CONFIG(SSP, 5, 5, NoCodec-5, SSP_CONFIG(I2S, SSP_CLOCK(mclk, 24576000, codec_mclk_in), SSP_CLOCK(bclk, 1536000, codec_slave), SSP_CLOCK(fsync, 48000, codec_slave), SSP_TDM(2, 16, 3, 3), - SSP_CONFIG_DATA(SSP, 5, 16, 0, SSP_QUIRK_LBM))) + SSP_CONFIG_DATA(SSP, 5, 16, 0, SSP_QUIRK_LBM, 0, + SSP_CC_MCLK_ES | SSP_CC_BCLK_ES))) DAI_CONFIG(DMIC, 0, 6, NoCodec-6, dnl DMIC_CONFIG(driver_version, clk_min, clk_mac, duty_min, duty_max, diff --git a/tools/topology/sof-cnl-nocodec.m4 b/tools/topology/sof-cnl-nocodec.m4 index e50c4890f432..c16f52ab8a43 100644 --- a/tools/topology/sof-cnl-nocodec.m4 +++ b/tools/topology/sof-cnl-nocodec.m4 @@ -159,21 +159,24 @@ DAI_CONFIG(SSP, 0, 0, NoCodec-0, SSP_CLOCK(fsync, 48000, codec_slave), SSP_TDM(2, 25, 3, 3), dnl SSP_CONFIG_DATA(type, dai_index, valid bits, mclk_id, quirks) - SSP_CONFIG_DATA(SSP, 0, 24, 0, SSP_QUIRK_LBM))) + SSP_CONFIG_DATA(SSP, 0, 24, 0, SSP_QUIRK_LBM, 0, + SSP_CC_MCLK_ES | SSP_CC_BCLK_ES))) DAI_CONFIG(SSP, 1, 1, NoCodec-1, SSP_CONFIG(I2S, SSP_CLOCK(mclk, 24000000, codec_mclk_in), SSP_CLOCK(bclk, 4800000, codec_slave), SSP_CLOCK(fsync, 48000, codec_slave), SSP_TDM(2, 25, 3, 3), - SSP_CONFIG_DATA(SSP, 1, 24, 0, SSP_QUIRK_LBM))) + SSP_CONFIG_DATA(SSP, 1, 24, 0, SSP_QUIRK_LBM, 0 + SSP_CC_MCLK_ES | SSP_CC_BCLK_ES))) DAI_CONFIG(SSP, 2, 2, NoCodec-2, SSP_CONFIG(I2S, SSP_CLOCK(mclk, 24000000, codec_mclk_in), SSP_CLOCK(bclk, 4800000, codec_slave), SSP_CLOCK(fsync, 48000, codec_slave), SSP_TDM(2, 25, 3, 3), - SSP_CONFIG_DATA(SSP, 2, 24, 0, SSP_QUIRK_LBM))) + SSP_CONFIG_DATA(SSP, 2, 24, 0, SSP_QUIRK_LBM, 0, + SSP_CC_MCLK_ES | SSP_CC_BCLK_ES))) DAI_CONFIG(DMIC, 0, 3, NoCodec-3, DMIC_CONFIG(1, 2400000, 4800000, 40, 60, 48000, diff --git a/tools/topology/sof-glk-da7219.m4 b/tools/topology/sof-glk-da7219.m4 index e170de3b7c09..554e8386e6b9 100644 --- a/tools/topology/sof-glk-da7219.m4 +++ b/tools/topology/sof-glk-da7219.m4 @@ -185,7 +185,7 @@ DAI_CONFIG(SSP, 2, 1, SSP2-Codec, SSP_CLOCK(bclk, 3072000, codec_slave), SSP_CLOCK(fsync, 48000, codec_slave), SSP_TDM(2, 32, 3, 3), - SSP_CONFIG_DATA(SSP, 2, 16, 1, 0, 10))) + SSP_CONFIG_DATA(SSP, 2, 16, 1, 0, 0, SSP_CC_BCLK_ES))) ', ) # dmic01 (id: 2)