diff --git a/src/drivers/intel/cavs/ssp.c b/src/drivers/intel/cavs/ssp.c index 900490750d4c..d5bee5d79906 100644 --- a/src/drivers/intel/cavs/ssp.c +++ b/src/drivers/intel/cavs/ssp.c @@ -170,6 +170,51 @@ static inline int ssp_set_config(struct dai *dai, spin_lock(&dai->lock); + /* unlike the implementation in main branch, here the set config + * command sent from host is just for clock control + */ + switch (config->flags & SOF_DAI_CONFIG_FLAGS_MASK) { + case SOF_DAI_CONFIG_FLAGS_HW_PARAMS: + if ((ssp->params.clks_control & SOF_DAI_INTEL_SSP_CLKCTRL_BCLK_ES) && + !(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); + trace_ssp("ssp_set_config(), SSP%d port enabled", dai->index); + + ssp->clk_active |= SSP_CLK_BCLK_ES_REQ; + } + + goto out; + case SOF_DAI_CONFIG_FLAGS_HW_FREE: + if ((ssp->params.clks_control & SOF_DAI_INTEL_SSP_CLKCTRL_BCLK_ES) && + (ssp->clk_active & SSP_CLK_BCLK_ES_REQ)) { + /* disable SSP port if no users */ + if (ssp->state[SOF_IPC_STREAM_CAPTURE] != COMP_STATE_ACTIVE && + ssp->state[SOF_IPC_STREAM_PLAYBACK] != COMP_STATE_ACTIVE) { + /* clear TRSE/RSRE before SSE */ + ssp_update_bits(dai, SSCR1, + SSCR1_TSRE | SSCR1_RSRE, + 0); + + ssp_update_bits(dai, SSCR0, SSCR0_SSE, 0); + ssp->state[SOF_IPC_STREAM_CAPTURE] = COMP_STATE_PREPARE; + ssp->state[SOF_IPC_STREAM_PLAYBACK] = COMP_STATE_PREPARE; + trace_ssp("ssp_set_config(), SSP%d port disabled", dai->index); + + ssp->clk_active &= ~SSP_CLK_BCLK_ES_REQ; + } + } + + goto out; + default: + break; + } + /* is playback/capture already running */ if (ssp->state[DAI_DIR_PLAYBACK] == COMP_STATE_ACTIVE || ssp->state[DAI_DIR_CAPTURE] == COMP_STATE_ACTIVE) { @@ -189,8 +234,8 @@ static inline int ssp_set_config(struct dai *dai, */ sscr0 = SSCR0_PSP | SSCR0_RIM | SSCR0_TIM; - /* sscr1 dynamic settings are SFRMDIR, SCLKDIR, SCFR */ - sscr1 = SSCR1_TTE | SSCR1_TTELP | SSCR1_TRAIL | SSCR1_RSRE | SSCR1_TSRE; + /* 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; @@ -745,20 +790,26 @@ static void ssp_start(struct dai *dai, int direction) spin_lock(&dai->lock); - /* enable port */ - ssp_update_bits(dai, SSCR0, SSCR0_SSE, SSCR0_SSE); + 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); + trace_ssp("ssp_start(), SSP%d port enabled", dai->index); + } + ssp->state[direction] = COMP_STATE_ACTIVE; trace_ssp("ssp_start()"); /* enable DMA */ - if (direction == DAI_DIR_PLAYBACK) { - ssp_update_bits(dai, SSCR1, SSCR1_TSRE, SSCR1_TSRE); + if (direction == DAI_DIR_PLAYBACK) ssp_update_bits(dai, SSTSA, 0x1 << 8, 0x1 << 8); - } else { - ssp_update_bits(dai, SSCR1, SSCR1_RSRE, SSCR1_RSRE); + else ssp_update_bits(dai, SSRSA, 0x1 << 8, 0x1 << 8); - } spin_unlock(&dai->lock); } @@ -776,7 +827,6 @@ static void ssp_stop(struct dai *dai, int direction) /* stop Rx if neeed */ if (direction == DAI_DIR_CAPTURE && ssp->state[SOF_IPC_STREAM_CAPTURE] == COMP_STATE_ACTIVE) { - ssp_update_bits(dai, SSCR1, SSCR1_RSRE, 0); ssp_update_bits(dai, SSRSA, 0x1 << 8, 0x0 << 8); ssp_empty_rx_fifo(dai); ssp->state[SOF_IPC_STREAM_CAPTURE] = COMP_STATE_PAUSED; @@ -787,19 +837,29 @@ static void ssp_stop(struct dai *dai, int direction) if (direction == DAI_DIR_PLAYBACK && ssp->state[SOF_IPC_STREAM_PLAYBACK] == COMP_STATE_ACTIVE) { ssp_empty_tx_fifo(dai); - ssp_update_bits(dai, SSCR1, SSCR1_TSRE, 0); ssp_update_bits(dai, SSTSA, 0x1 << 8, 0x0 << 8); ssp->state[SOF_IPC_STREAM_PLAYBACK] = COMP_STATE_PAUSED; trace_ssp("ssp_stop(), TX stop"); } + if (ssp->clk_active & SSP_CLK_BCLK_ES_REQ) { + /* port will be disabled in hw_free */ + spin_unlock(&dai->lock); + return; + } + /* disable SSP port if no users */ if (ssp->state[SOF_IPC_STREAM_CAPTURE] != COMP_STATE_ACTIVE && ssp->state[SOF_IPC_STREAM_PLAYBACK] != COMP_STATE_ACTIVE) { + /* clear TRSE/RSRE before SSE */ + ssp_update_bits(dai, SSCR1, + SSCR1_TSRE | SSCR1_RSRE, + 0); + ssp_update_bits(dai, SSCR0, SSCR0_SSE, 0); ssp->state[SOF_IPC_STREAM_CAPTURE] = COMP_STATE_PREPARE; ssp->state[SOF_IPC_STREAM_PLAYBACK] = COMP_STATE_PREPARE; - trace_ssp("ssp_stop(), SSP port disabled"); + trace_ssp("ssp_stop(), SSP%d port disabled", dai->index); } spin_unlock(&dai->lock); diff --git a/src/include/sof/ssp.h b/src/include/sof/ssp.h index 5d1d15029fdf..6364565b8866 100644 --- a/src/include/sof/ssp.h +++ b/src/include/sof/ssp.h @@ -240,12 +240,18 @@ extern const struct dai_driver ssp_driver; #define ssp_irq(ssp) \ ssp->plat_data.irq +#define SSP_CLK_MCLK_ES_REQ BIT(0) +#define SSP_CLK_MCLK_ACTIVE BIT(1) +#define SSP_CLK_BCLK_ES_REQ BIT(2) +#define SSP_CLK_BCLK_ACTIVE BIT(3) + /* 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; completion_t drain_complete; struct sof_ipc_dai_config config; struct sof_ipc_dai_ssp_params params; diff --git a/src/include/uapi/abi.h b/src/include/uapi/abi.h index 5a08f4039497..590873890749 100644 --- a/src/include/uapi/abi.h +++ b/src/include/uapi/abi.h @@ -52,7 +52,7 @@ /** \brief SOF ABI version major, minor and patch numbers */ #define SOF_ABI_MAJOR 3 -#define SOF_ABI_MINOR 7 +#define SOF_ABI_MINOR 8 #define SOF_ABI_PATCH 0 /** \brief SOF ABI version number. Format within 32bit word is MMmmmppp */ diff --git a/src/include/uapi/ipc/dai-intel.h b/src/include/uapi/ipc/dai-intel.h index 9cc21cea2229..c49b4c1efe8b 100644 --- a/src/include/uapi/ipc/dai-intel.h +++ b/src/include/uapi/ipc/dai-intel.h @@ -78,6 +78,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) /* SSP Configuration Request - SOF_IPC_DAI_SSP_CONFIG */ struct sof_ipc_dai_ssp_params { diff --git a/src/include/uapi/ipc/dai.h b/src/include/uapi/ipc/dai.h index ed4d33a3f384..8e42ea3db08e 100644 --- a/src/include/uapi/ipc/dai.h +++ b/src/include/uapi/ipc/dai.h @@ -73,6 +73,13 @@ #define SOF_DAI_FMT_INV_MASK 0x0f00 #define SOF_DAI_FMT_MASTER_MASK 0xf000 +/* DAI_CONFIG flags */ +#define SOF_DAI_CONFIG_FLAGS_MASK 0x3 +#define SOF_DAI_CONFIG_FLAGS_NONE (0 << 0) /**< DAI_CONFIG sent without stage information */ +#define SOF_DAI_CONFIG_FLAGS_HW_PARAMS (1 << 0) /**< DAI_CONFIG sent during hw_params stage */ +#define SOF_DAI_CONFIG_FLAGS_HW_FREE (2 << 0) /**< DAI_CONFIG sent during hw_free stage */ +#define SOF_DAI_CONFIG_FLAGS_RFU (3 << 0) /**< not used, reserved for future use */ + /** \brief Types of DAI */ enum sof_ipc_dai_type { SOF_DAI_INTEL_NONE = 0, /**< None */ @@ -90,7 +97,8 @@ struct sof_ipc_dai_config { /* physical protocol and clocking */ uint16_t format; /**< SOF_DAI_FMT_ */ - uint16_t reserved16; /**< alignment */ + uint8_t group_id; /**< group ID, 0 means no group (ABI 3.17) */ + uint8_t flags; /**< SOF_DAI_CONFIG_FLAGS_ (ABI 3.19) */ /* reserved for future use */ uint32_t reserved[8]; diff --git a/tools/topology/CMakeLists.txt b/tools/topology/CMakeLists.txt index a76657386472..318431ff0cb4 100644 --- a/tools/topology/CMakeLists.txt +++ b/tools/topology/CMakeLists.txt @@ -41,7 +41,8 @@ set(TPLGS "sof-apl-wm8804\;sof-apl-wm8804" "sof-apl-da7219\;sof-apl-da7219" "sof-glk-da7219-kwd\;sof-glk-da7219-kwd" - "sof-glk-da7219\;sof-glk-da7219" + "sof-glk-da7219\;sof-glk-da7219\;-DHEADPHONE=da7219" + "sof-glk-da7219\;sof-glk-cs42l42\;-DHEADPHONE=cs42l42" "sof-glk-rt5682\;sof-glk-rt5682" "sof-icl-nocodec\;sof-icl-nocodec" "sof-apl-eq-pcm512x\;sof-apl-eq-pcm512x" diff --git a/tools/topology/platform/common/ssp.m4 b/tools/topology/platform/common/ssp.m4 index a580e8dc5e75..caee0ce3fa64 100644 --- a/tools/topology/platform/common/ssp.m4 +++ b/tools/topology/platform/common/ssp.m4 @@ -30,14 +30,20 @@ $6 dnl SSP_QUIRK_LBM 64 = (1 << 6) define(`SSP_QUIRK_LBM', 64) -dnl SSP_CONFIG_DATA(type, idx, valid bits, mclk_id, quirks) -dnl mclk_id is optional +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, clks_control) +dnl mclk_id and clks_control are optional define(`SSP_CONFIG_DATA', `SectionVendorTuples."'N_DAI_CONFIG($1$2)`_tuples" {' ` tokens "sof_ssp_tokens"' ` tuples."word" {' ` SOF_TKN_INTEL_SSP_SAMPLE_BITS' STR($3) ` SOF_TKN_INTEL_SSP_QUIRKS' ifelse($5, `', "0", STR($5)) +` SOF_TKN_INTEL_SSP_CLKS_CONTROL' ifelse($6, `', "0", STR($6)) ` }' ` tuples."short" {' ` SOF_TKN_INTEL_SSP_MCLK_ID' ifelse($4, `', "0", STR($4)) diff --git a/tools/topology/sof-glk-da7219.m4 b/tools/topology/sof-glk-da7219.m4 index dd1c875dee8f..bdac7b5dd9ea 100644 --- a/tools/topology/sof-glk-da7219.m4 +++ b/tools/topology/sof-glk-da7219.m4 @@ -1,5 +1,6 @@ # -# Topology for GeminiLake with Dialog7219. +# Topology for GeminiLake with Dialog Semiconductor DA7219 or +# Cirrus Logic CS42L42 # # Include topology builder @@ -22,7 +23,7 @@ include(`platform/intel/dmic.m4') # Define the pipelines # # PCM0 ----> volume (pipe 1) -----> SSP1 (speaker - maxim98357a, BE link 0) -# PCM1 <---> volume (pipe 2,3) <----> SSP2 (headset - da7219, BE link 1) +`# PCM1 <---> volume (pipe 2,3) <----> SSP2 (headset - 'HEADPHONE`, BE link 1)' # PCM99 <---- DMIC0 (dmic capture, BE link 2) # PCM5 ----> volume (pipe 5) -----> iDisp1 (HDMI/DP playback, BE link 3) # PCM6 ----> Volume (pipe 6) -----> iDisp2 (HDMI/DP playback, BE link 4) @@ -86,6 +87,7 @@ DAI_ADD(sof/pipe-dai-playback.m4, PIPELINE_SOURCE_1, 2, s16le, 48, 1000, 0, 0) +ifelse(HEADPHONE, `da7219', ` # playback DAI is SSP2 using 2 periods # Buffers use s16le format, with 48 frame per 1000us on core 0 with priority 0 DAI_ADD(sof/pipe-dai-playback.m4, @@ -99,6 +101,21 @@ DAI_ADD(sof/pipe-dai-capture.m4, 3, SSP, 2, SSP2-Codec, PIPELINE_SINK_3, 2, s16le, 48, 1000, 0, 0) +', HEADPHONE, `cs42l42', ` +# playback DAI is SSP2 using 2 periods +# Buffers use s24le format, with 48 frame per 1000us on core 0 with priority 0 +DAI_ADD(sof/pipe-dai-playback.m4, + 2, SSP, 2, SSP2-Codec, + PIPELINE_SOURCE_2, 2, s24le, + 48, 1000, 0, 0) + +# capture DAI is SSP2 using 2 periods +# Buffers use s24le format, with 48 frame per 1000us on core 0 with priority 0 +DAI_ADD(sof/pipe-dai-capture.m4, + 3, SSP, 2, SSP2-Codec, + PIPELINE_SINK_3, 2, s24le, + 48, 1000, 0, 0) +', ) # capture DAI is DMIC0 using 2 periods # Buffers use s32le format, with 48 frame per 1000us on core 0 with priority 0 @@ -147,6 +164,7 @@ DAI_CONFIG(SSP, 1, 0, SSP1-Codec, SSP_TDM(2, 16, 3, 3), SSP_CONFIG_DATA(SSP, 1, 16, 1))) +ifelse(HEADPHONE, `da7219', ` #SSP 2 (ID: 1) with 19.2 MHz mclk with MCLK_ID 1, 1.92 MHz bclk DAI_CONFIG(SSP, 2, 1, SSP2-Codec, SSP_CONFIG(I2S, SSP_CLOCK(mclk, 19200000, codec_mclk_in), @@ -154,6 +172,15 @@ DAI_CONFIG(SSP, 2, 1, SSP2-Codec, SSP_CLOCK(fsync, 48000, codec_slave), SSP_TDM(2, 20, 3, 3), SSP_CONFIG_DATA(SSP, 2, 16, 1))) +', HEADPHONE, `cs42l42', ` +#SSP 2 (ID: 1) with 19.2 MHz mclk with MCLK_ID 1 (unused), 2.4 MHz bclk +DAI_CONFIG(SSP, 2, 1, SSP2-Codec, + SSP_CONFIG(I2S, SSP_CLOCK(mclk, 19200000, codec_mclk_in), + SSP_CLOCK(bclk, 2400000, codec_slave), + SSP_CLOCK(fsync, 48000, codec_slave), + SSP_TDM(2, 25, 3, 3), + SSP_CONFIG_DATA(SSP, 2, 24, 1, 0, SSP_CC_BCLK_ES))) +', ) # dmic01 (id: 2) DAI_CONFIG(DMIC, 0, 2, dmic01,