-
Notifications
You must be signed in to change notification settings - Fork 350
Start ssp clock earlier and stop later #4219
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
6f2f3db
7feb456
93379cd
3ffe417
8c269e5
0250a67
6e9ff5a
ae5ee43
ac19390
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit-pick: for consistency with the rest of the code, use if (ret < 0) ?
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The if (ret < 0) in
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. if my above suspicion is correct and |
||
| 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; | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. here the initialisation is indeed redundant |
||
|
|
||
| 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)) { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. wrong indentation here and below |
||
| /* 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); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. have you checked that the hw_params is called after set_config? if not, the values will be uninitialized. |
||
| 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; | ||
|
bardliao marked this conversation as resolved.
Outdated
|
||
| } | ||
|
|
||
|
bardliao marked this conversation as resolved.
Outdated
|
||
| dai_info(dai, "ssp_hw_params() params.clks_control 0x%x", ssp->params.clks_control); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should not set mclk/bclk in case already set, do we need some similar (but not same) logic as below?
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
@keyonjie Thanks for pointing it out, but we may not be able to use
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @keyonjie updated. |
||
|
|
||
| spin_lock(&dai->lock); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we state what we are doing in comments at the start of each function. i.e. this function enabled clock x, y , z to support codec that need x, y z. This is called before triiger (x) and after trigger(y) etc
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @bardliao we can use /* SSP Configuration Request - SOF_IPC_DAI_SSP_CONFIG */
struct sof_ipc_dai_ssp_params {
uint32_t reserved0;
uint16_t reserved1;
uint16_t mclk_id;
uint32_t mclk_rate; /* mclk frequency in Hz */
uint32_t fsync_rate; /* fsync frequency in Hz */
uint32_t bclk_rate; /* bclk frequency in Hz */
/* TDM */
uint32_t tdm_slots;
uint32_t rx_slots;
uint32_t tx_slots;
/* data */
uint32_t sample_valid_bits;
uint16_t tdm_slot_width;
uint16_t reserved2; /* alignment */ <<<< add a flag here, 1 means start BCKL early, 0 menas no change.
/* MCLK */
uint32_t mclk_direction;
uint16_t frame_pulse_width;
uint16_t tdm_per_slot_padding_flag;
uint32_t clks_control;
uint32_t quirks;
uint32_t bclk_delay; /* guaranteed time (ms) for which BCLK
* will be driven, before sending data
*/
} __attribute__((packed, aligned(4)));
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. how about using quirks? it's just a flag to the ssp driver.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I don't know if it is a quirk. @lgirdwood what do you think?
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, it's not a quirk but rather an operating mode. - We can use any of the reserved fields above as they are all 0 today and we could set a bit(s) for enabling the clock(s). |
||
|
|
||
|
plbossart marked this conversation as resolved.
Outdated
|
||
| 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); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Im getting super confused here. We do the same things in ssp_hw_params() and ssp_pre_start(). Why do we need to do these twice?
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
They are not called twice. We do them in ssp_hw_params() if |
||
| 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); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. don't we need to check for the direction to mirror what happens line 876? if the playback and capture are started separately we need to stop the SSP when both are stopped.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why do we need this here again? we're doing this already in hw_params() too |
||
| } | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we should enable MCLK and BCLK separately. They are not logically connected. |
||
| 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, | ||
| }, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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 | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it's incomplete...we need to modify the SSP_CONFIG_DATA macro here
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @plbossart I think the issue is that 636cbef is already on the main branch, but @brentlu was testing with glk-012-stable-branch branch where 636cbef is not there. |
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no need to init?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need to init because
ret = mn_set_bclk()is only called ifCONFIG_INTEL_MNThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
there's something weird there: in the
#elsecase below if the condition isn't satisfied we print an error message and jump toout, but we don't setret... Is that correct?