ASoC: msm8x16: fix the target crash due to dpm timeout

DPM timeout was happening due to the suspend call to the
codec driver being blocked.
This was due to a deadlock by acquiring the same mutex by the
suspend call and the work queue.
Fix by modifying the codeflow so that the deadlock is avoided.

CRs-Fixed: 681986
Change-Id: I96f11ee968a0e9bd267a80d2d11f1fa49985f5a9
Signed-off-by: Aviral Gupta <aviralg@codeaurora.org>
This commit is contained in:
Aviral Gupta 2014-06-20 19:40:46 +05:30
parent e19940c9ea
commit 1d1b1f5e85
3 changed files with 45 additions and 84 deletions

View file

@ -351,7 +351,7 @@ static int __msm8x16_wcd_reg_read(struct snd_soc_codec *codec,
ret = msm8x16_wcd_spmi_read(reg, 1, &temp);
else if (MSM8X16_WCD_IS_DIGITAL_REG(reg)) {
mutex_lock(&pdata->cdc_mclk_mutex);
if (atomic_read(&pdata->dis_work_mclk) == false) {
if (atomic_read(&pdata->mclk_enabled) == false) {
pdata->digital_cdc_clk.clk_val = pdata->mclk_freq;
ret = afe_set_digital_codec_core_clock(
AFE_PORT_ID_PRIMARY_MI2S_RX,
@ -363,8 +363,8 @@ static int __msm8x16_wcd_reg_read(struct snd_soc_codec *codec,
pr_debug("%s: MCLK not enabled\n", __func__);
ret = msm8x16_wcd_ahb_read_device(
msm8x16_wcd, reg, 1, &temp);
atomic_set(&pdata->dis_work_mclk, true);
schedule_delayed_work(&pdata->enable_mclk_work, 50);
atomic_set(&pdata->mclk_enabled, true);
schedule_delayed_work(&pdata->disable_mclk_work, 50);
err:
mutex_unlock(&pdata->cdc_mclk_mutex);
mutex_unlock(&msm8x16_wcd->io_lock);
@ -401,7 +401,7 @@ static int __msm8x16_wcd_reg_write(struct snd_soc_codec *codec,
ret = msm8x16_wcd_spmi_write(reg, 1, &val);
else if (MSM8X16_WCD_IS_DIGITAL_REG(reg)) {
mutex_lock(&pdata->cdc_mclk_mutex);
if (atomic_read(&pdata->dis_work_mclk) == false) {
if (atomic_read(&pdata->mclk_enabled) == false) {
pr_debug("MCLK not enabled %s:\n", __func__);
pdata->digital_cdc_clk.clk_val = pdata->mclk_freq;
ret = afe_set_digital_codec_core_clock(
@ -414,8 +414,8 @@ static int __msm8x16_wcd_reg_write(struct snd_soc_codec *codec,
}
ret = msm8x16_wcd_ahb_write_device(
msm8x16_wcd, reg, &val, 1);
atomic_set(&pdata->dis_work_mclk, true);
schedule_delayed_work(&pdata->enable_mclk_work, 50);
atomic_set(&pdata->mclk_enabled, true);
schedule_delayed_work(&pdata->disable_mclk_work, 50);
err:
mutex_unlock(&pdata->cdc_mclk_mutex);
mutex_unlock(&msm8x16_wcd->io_lock);
@ -3324,37 +3324,23 @@ int msm8x16_wcd_suspend(struct snd_soc_codec *codec)
struct msm8x16_wcd_pdata *msm8x16_pdata = msm8x16->dev->platform_data;
pdata = snd_soc_card_get_drvdata(codec->card);
pr_debug("%s: mclk cnt = %d, dis_work_mclk = %d\n",
pr_debug("%s: mclk cnt = %d, mclk_enabled = %d\n",
__func__, atomic_read(&pdata->mclk_rsc_ref),
atomic_read(&pdata->dis_work_mclk));
pr_debug("%s: mclk_act = %d\n", __func__,
atomic_read(&pdata->mclk_act));
mutex_lock(&pdata->cdc_mclk_mutex);
if ((atomic_read(&pdata->dis_work_mclk) == true) ||
(atomic_read(&pdata->mclk_rsc_ref) > 0)) {
pdata->digital_cdc_clk.clk_val = 0;
afe_set_digital_codec_core_clock(
atomic_read(&pdata->mclk_enabled));
if (atomic_read(&pdata->mclk_enabled) == true) {
cancel_delayed_work_sync(
&pdata->disable_mclk_work);
mutex_lock(&pdata->cdc_mclk_mutex);
if (atomic_read(&pdata->mclk_enabled) == true) {
pdata->digital_cdc_clk.clk_val = 0;
afe_set_digital_codec_core_clock(
AFE_PORT_ID_PRIMARY_MI2S_RX,
&pdata->digital_cdc_clk);
/*
* set mclk activity to resource as
* it will get updated accordingly going further in this
* function.
*/
atomic_set(&pdata->mclk_act, MCLK_SUS_RSC);
if (atomic_read(&pdata->dis_work_mclk) == true) {
cancel_delayed_work_sync(
&pdata->enable_mclk_work);
atomic_set(&pdata->mclk_act, MCLK_SUS_DIS);
atomic_set(&pdata->dis_work_mclk, false);
atomic_set(&pdata->mclk_enabled, false);
}
} else
/*
* mark no activity on mclk in this suspend
*/
atomic_set(&pdata->mclk_act, MCLK_SUS_NO_ACT);
mutex_unlock(&pdata->cdc_mclk_mutex);
}
msm8x16_wcd_disable_static_supplies_to_optimum(msm8x16, msm8x16_pdata);
mutex_unlock(&pdata->cdc_mclk_mutex);
return 0;
}
@ -3366,34 +3352,6 @@ int msm8x16_wcd_resume(struct snd_soc_codec *codec)
pdata = snd_soc_card_get_drvdata(codec->card);
msm8x16_wcd_enable_static_supplies_to_optimum(msm8x16, msm8x16_pdata);
pr_debug("%s: mclk cnt = %d, dis_work_mclk = %d\n",
__func__, atomic_read(&pdata->mclk_rsc_ref),
atomic_read(&pdata->dis_work_mclk));
pr_debug("%s: mclk_act = %d\n", __func__,
atomic_read(&pdata->mclk_act));
if (atomic_read(&pdata->mclk_act) == MCLK_SUS_NO_ACT)
/*
* no activity in suspend just return
*/
return 0;
mutex_lock(&pdata->cdc_mclk_mutex);
if ((atomic_read(&pdata->dis_work_mclk) == false) ||
(atomic_read(&pdata->mclk_rsc_ref) > 0)) {
pdata->digital_cdc_clk.clk_val = pdata->mclk_freq;
afe_set_digital_codec_core_clock(
AFE_PORT_ID_PRIMARY_MI2S_RX,
&pdata->digital_cdc_clk);
if (atomic_read(&pdata->mclk_act) == MCLK_SUS_DIS) {
/*
* MCLK activity marked as the disabled during suspend
* this indicated MCLK was enabled to read and write the
* AHB bus.
*/
atomic_set(&pdata->dis_work_mclk, true);
schedule_delayed_work(&pdata->enable_mclk_work, 50);
}
}
mutex_unlock(&pdata->cdc_mclk_mutex);
return 0;
}

View file

@ -130,10 +130,9 @@ struct msm8916_asoc_mach_data {
int us_euro_gpio;
int mclk_freq;
atomic_t mclk_rsc_ref;
atomic_t dis_work_mclk;
atomic_t mclk_act;
atomic_t mclk_enabled;
struct mutex cdc_mclk_mutex;
struct delayed_work enable_mclk_work;
struct delayed_work disable_mclk_work;
struct afe_digital_clk_cfg digital_cdc_clk;
};

View file

@ -601,29 +601,29 @@ static int msm8x16_enable_codec_ext_clk(struct snd_soc_codec *codec,
atomic_read(&pdata->mclk_rsc_ref));
if (enable) {
if (atomic_inc_return(&pdata->mclk_rsc_ref) == 1) {
cancel_delayed_work_sync(
&pdata->disable_mclk_work);
mutex_lock(&pdata->cdc_mclk_mutex);
if (atomic_read(&pdata->dis_work_mclk) == true) {
cancel_delayed_work_sync(
&pdata->enable_mclk_work);
} else {
if (atomic_read(&pdata->mclk_enabled) == false) {
pdata->digital_cdc_clk.clk_val =
pdata->mclk_freq;
afe_set_digital_codec_core_clock(
AFE_PORT_ID_PRIMARY_MI2S_RX,
&pdata->digital_cdc_clk);
atomic_set(&pdata->dis_work_mclk, true);
atomic_set(&pdata->mclk_enabled, true);
}
mutex_unlock(&pdata->cdc_mclk_mutex);
}
} else {
cancel_delayed_work_sync(&pdata->disable_mclk_work);
mutex_lock(&pdata->cdc_mclk_mutex);
atomic_set(&pdata->mclk_rsc_ref, 0);
cancel_delayed_work_sync(&pdata->enable_mclk_work);
pdata->digital_cdc_clk.clk_val = 0;
afe_set_digital_codec_core_clock(
AFE_PORT_ID_PRIMARY_MI2S_RX,
&pdata->digital_cdc_clk);
atomic_set(&pdata->dis_work_mclk, false);
if (atomic_read(&pdata->mclk_enabled) == true) {
pdata->digital_cdc_clk.clk_val = 0;
afe_set_digital_codec_core_clock(
AFE_PORT_ID_PRIMARY_MI2S_RX,
&pdata->digital_cdc_clk);
atomic_set(&pdata->mclk_enabled, false);
}
mutex_unlock(&pdata->cdc_mclk_mutex);
}
return ret;
@ -1816,26 +1816,30 @@ static struct snd_soc_card bear_cards[MAX_SND_CARDS] = {
},
};
void enable_mclk(struct work_struct *work)
void disable_mclk(struct work_struct *work)
{
struct msm8916_asoc_mach_data *pdata = NULL;
struct delayed_work *dwork;
int ret = 0;
pr_debug("%s:\n", __func__);
dwork = to_delayed_work(work);
pdata = container_of(dwork, struct msm8916_asoc_mach_data,
enable_mclk_work);
disable_mclk_work);
mutex_lock(&pdata->cdc_mclk_mutex);
if (atomic_read(&pdata->dis_work_mclk) == true) {
pr_debug("clock enabled now disable\n");
pr_debug("%s: mclk_enabled %d mclk_rsc_ref %d\n", __func__,
atomic_read(&pdata->mclk_enabled),
atomic_read(&pdata->mclk_rsc_ref));
if (atomic_read(&pdata->mclk_enabled) == true
&& atomic_read(&pdata->mclk_rsc_ref) == 0) {
pr_debug("Disable the mclk\n");
pdata->digital_cdc_clk.clk_val = 0;
ret = afe_set_digital_codec_core_clock(
AFE_PORT_ID_PRIMARY_MI2S_RX,
&pdata->digital_cdc_clk);
if (ret < 0)
pr_err("failed to disable the MCLK\n");
atomic_set(&pdata->dis_work_mclk, false);
pr_err("%s failed to disable the MCLK\n", __func__);
atomic_set(&pdata->mclk_enabled, false);
}
mutex_unlock(&pdata->cdc_mclk_mutex);
}
@ -2170,10 +2174,10 @@ static int msm8x16_asoc_machine_probe(struct platform_device *pdev)
if (ret)
goto err;
/* initialize timer */
INIT_DELAYED_WORK(&pdata->enable_mclk_work, enable_mclk);
INIT_DELAYED_WORK(&pdata->disable_mclk_work, disable_mclk);
mutex_init(&pdata->cdc_mclk_mutex);
atomic_set(&pdata->mclk_rsc_ref, 0);
atomic_set(&pdata->dis_work_mclk, false);
atomic_set(&pdata->mclk_enabled, false);
ret = snd_soc_of_parse_audio_routing(card,
"qcom,audio-routing");