Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
303 changes: 220 additions & 83 deletions src/drivers/intel/ssp/ssp.c
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,112 @@ static int ssp_context_restore(struct dai *dai)
return 0;
}

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(struct dai *dai, struct ipc_config_dai *common_config,
void *spec_config)
Expand Down Expand Up @@ -180,8 +286,8 @@ static int ssp_set_config(struct dai *dai, struct ipc_config_dai *common_config,
*/
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 @@ -606,6 +712,74 @@ static int ssp_set_config(struct dai *dai, struct ipc_config_dai *common_config,
ssp->state[DAI_DIR_PLAYBACK] = COMP_STATE_PREPARE;
ssp->state[DAI_DIR_CAPTURE] = COMP_STATE_PREPARE;

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_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:
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:

spin_unlock(&dai->lock);
Expand All @@ -621,71 +795,23 @@ static int ssp_set_config(struct dai *dai, struct ipc_config_dai *common_config,
static int ssp_pre_start(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()");

/* 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)
return 0;

/* MCLK config */
ret = mn_set_mclk(config->ssp.mclk_id, config->ssp.mclk_rate);
if (ret < 0) {
dai_err(dai, "invalid mclk_rate = %d for mclk_id = %d",
config->ssp.mclk_rate, config->ssp.mclk_id);
goto out;
}

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, "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, "invalid bclk_rate = %d for dai_index = %d",
config->ssp.bclk_rate, config->dai_index);
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_pre_start(): divisor %d is not within SCR range",
mdiv);
ret = -EINVAL;
goto out;
/*
* 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)) {
/* MCLK config */
ret = ssp_mclk_prepare_enable(dai);
if (ret < 0)
return ret;
}

/* set the SCR divisor */
sscr0 &= ~SSCR0_SCR_MASK;
sscr0 |= SSCR0_SCR(mdiv);

ssp_write(dai, SSCR0, sscr0);

dai_info(dai, "ssp_set_config(), sscr0 = 0x%08x", sscr0);
out:
if (!(ssp->clk_active & SSP_CLK_BCLK_ES_REQ))
ret = ssp_bclk_prepare_enable(dai);

return ret;
}
Expand All @@ -702,11 +828,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->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)) {
dai_info(dai, "ssp_post_stop releasing MCLK clocks for SSP%d...",
dai->index);
ssp_mclk_disable_unprepare(dai);
}
}
}

Expand Down Expand Up @@ -752,8 +883,16 @@ 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->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_start(): SSE set for SSP%d", dai->index);
}
ssp->state[direction] = COMP_STATE_ACTIVE;

dai_info(dai, "ssp_start()");
Expand All @@ -767,13 +906,10 @@ static void ssp_start(struct dai *dai, int direction)
}

/* 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, SSTSA_TXEN, SSTSA_TXEN);
} else {
ssp_update_bits(dai, SSCR1, SSCR1_RSRE, SSCR1_RSRE);
else
ssp_update_bits(dai, SSRSA, SSRSA_RXEN, SSRSA_RXEN);
}

/* wait to get valid fifo status */
wait_delay(PLATFORM_SSP_DELAY);
Expand All @@ -794,7 +930,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_PREPARE) {
ssp_update_bits(dai, SSCR1, SSCR1_RSRE, 0);
ssp_update_bits(dai, SSRSA, SSRSA_RXEN, 0);
ssp_empty_rx_fifo(dai);
ssp->state[SOF_IPC_STREAM_CAPTURE] = COMP_STATE_PREPARE;
Expand All @@ -805,7 +940,6 @@ static void ssp_stop(struct dai *dai, int direction)
if (direction == DAI_DIR_PLAYBACK &&
ssp->state[SOF_IPC_STREAM_PLAYBACK] != COMP_STATE_PREPARE) {
ssp_empty_tx_fifo(dai);
ssp_update_bits(dai, SSCR1, SSCR1_TSRE, 0);
ssp_update_bits(dai, SSTSA, SSTSA_TXEN, 0);
ssp->state[SOF_IPC_STREAM_PLAYBACK] = COMP_STATE_PREPARE;
dai_info(dai, "ssp_stop(), TX stop");
Expand All @@ -814,8 +948,15 @@ 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->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);
Expand Down Expand Up @@ -907,14 +1048,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);
Expand Down
Loading