Skip to content
84 changes: 72 additions & 12 deletions src/drivers/intel/cavs/ssp.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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;
Expand Down Expand Up @@ -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);
}
Expand All @@ -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;
Expand All @@ -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);
Expand Down
6 changes: 6 additions & 0 deletions src/include/sof/ssp.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion src/include/uapi/abi.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down
4 changes: 4 additions & 0 deletions src/include/uapi/ipc/dai-intel.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
10 changes: 9 additions & 1 deletion src/include/uapi/ipc/dai.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand All @@ -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];
Expand Down
3 changes: 2 additions & 1 deletion tools/topology/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
10 changes: 8 additions & 2 deletions tools/topology/platform/common/ssp.m4
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
31 changes: 29 additions & 2 deletions tools/topology/sof-glk-da7219.m4
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#
# Topology for GeminiLake with Dialog7219.
# Topology for GeminiLake with Dialog Semiconductor DA7219 or
# Cirrus Logic CS42L42
#

# Include topology builder
Expand All @@ -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)
Expand Down Expand Up @@ -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,
Expand All @@ -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
Expand Down Expand Up @@ -147,13 +164,23 @@ 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),
SSP_CLOCK(bclk, 1920000, codec_slave),
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,
Expand Down