ASoC: msm: fall back to have AFE port started at prepare
After upgrading to kernel 3.4, there is 5 second delay
at the closing of PCM playback. The delay is due to missing
EOS from QDSP6 audio session manager causing pcm close function
of PCM platform driver to wait for 5 seconds. The root cause
for missing EOS is that ALSA dynmic PCM shutdown sequence has
changed. Now, trigger stop is called on the back-end DAI-LINK.
Furthermore, back-end trigger stop is called before front-end
trigger stop. Since sink stops rendering data, data at source
will never get consumed. EOS event will not arrive. As trigger
operation has to be atomic, it is very difficult to guarantee
sequence on shutting down various modules in QDSP6. The decision
is to abandon starting and stopping QDSP6 AFE port in trigger
function. This decision is considered acceptable as playback
and capture over SLIMBUS is no longer subject to strict sequence
which Q6 AFE port must be started after CODEC configuration.
Change-Id: I0cc1d8b7d058052d7fae55c84b6be46b5b0678e9
CRs-fixed: 373966
Signed-off-by: Patrick Lai <plai@codeaurora.org>
diff --git a/include/sound/q6afe.h b/include/sound/q6afe.h
index f93af1f..a7264e8 100644
--- a/include/sound/q6afe.h
+++ b/include/sound/q6afe.h
@@ -95,8 +95,7 @@
int afe_unregister_get_events(u16 port_id);
int afe_rt_proxy_port_write(u32 buf_addr_p, int bytes);
int afe_rt_proxy_port_read(u32 buf_addr_p, int bytes);
-int afe_port_start_nowait(u16 port_id, union afe_port_config *afe_config,
- u32 rate);
+int afe_port_start(u16 port_id, union afe_port_config *afe_config, u32 rate);
int afe_port_stop_nowait(int port_id);
int afe_apply_gain(u16 port_id, u16 gain);
int afe_q6_interface_prepare(void);
diff --git a/sound/soc/msm/msm-dai-q6-hdmi.c b/sound/soc/msm/msm-dai-q6-hdmi.c
index dfb090e..c082ed7 100644
--- a/sound/soc/msm/msm-dai-q6-hdmi.c
+++ b/sound/soc/msm/msm-dai-q6-hdmi.c
@@ -158,54 +158,19 @@
int rc = 0;
if (!test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
- /* PORT START should be set if prepare called in active state */
- rc = afe_q6_interface_prepare();
+ rc = afe_port_start(dai->id, &dai_data->port_config,
+ dai_data->rate);
if (IS_ERR_VALUE(rc))
- dev_err(dai->dev, "fail to open AFE APR\n");
+ dev_err(dai->dev, "fail to open AFE port %x\n",
+ dai->id);
+ else
+ set_bit(STATUS_PORT_STARTED,
+ dai_data->status_mask);
}
+
return rc;
}
-static int msm_dai_q6_hdmi_trigger(struct snd_pcm_substream *substream, int cmd,
- struct snd_soc_dai *dai)
-{
- struct msm_dai_q6_hdmi_dai_data *dai_data = dev_get_drvdata(dai->dev);
-
- /* Start/stop port without waiting for Q6 AFE response. Need to have
- * native q6 AFE driver propagates AFE response in order to handle
- * port start/stop command error properly if error does arise.
- */
- pr_debug("%s:port:%d cmd:%d dai_data->status_mask = %ld",
- __func__, dai->id, cmd, *dai_data->status_mask);
-
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- case SNDRV_PCM_TRIGGER_RESUME:
- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- if (!test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
- afe_port_start_nowait(dai->id, &dai_data->port_config,
- dai_data->rate);
-
- set_bit(STATUS_PORT_STARTED, dai_data->status_mask);
- }
- break;
- case SNDRV_PCM_TRIGGER_STOP:
- case SNDRV_PCM_TRIGGER_SUSPEND:
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- if (test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
- afe_port_stop_nowait(dai->id);
- clear_bit(STATUS_PORT_STARTED, dai_data->status_mask);
- }
- break;
-
- default:
- dev_err(dai->dev, "invalid Trigger command = %d\n", cmd);
- return -EINVAL;
- }
-
- return 0;
-}
-
static int msm_dai_q6_hdmi_dai_probe(struct snd_soc_dai *dai)
{
struct msm_dai_q6_hdmi_dai_data *dai_data;
@@ -253,7 +218,6 @@
static struct snd_soc_dai_ops msm_dai_q6_hdmi_ops = {
.prepare = msm_dai_q6_hdmi_prepare,
- .trigger = msm_dai_q6_hdmi_trigger,
.hw_params = msm_dai_q6_hdmi_hw_params,
.shutdown = msm_dai_q6_hdmi_shutdown,
};
diff --git a/sound/soc/msm/msm-dai-q6.c b/sound/soc/msm/msm-dai-q6.c
index 147316e..fb74c0a 100644
--- a/sound/soc/msm/msm-dai-q6.c
+++ b/sound/soc/msm/msm-dai-q6.c
@@ -407,55 +407,21 @@
(substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
&mi2s_dai_data->rx_dai.mi2s_dai_data :
&mi2s_dai_data->tx_dai.mi2s_dai_data);
+ u16 port_id = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
+ MI2S_RX : MI2S_TX);
int rc = 0;
if (!test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
/* PORT START should be set if prepare called in active state */
- rc = afe_q6_interface_prepare();
+ rc = afe_port_start(port_id, &dai_data->port_config,
+ dai_data->rate);
+
if (IS_ERR_VALUE(rc))
- dev_err(dai->dev, "fail to open AFE APR\n");
- }
- return rc;
-}
-
-static int msm_dai_q6_mi2s_trigger(struct snd_pcm_substream *substream, int cmd,
- struct snd_soc_dai *dai)
-{
- struct msm_dai_q6_mi2s_dai_data *mi2s_dai_data =
- dev_get_drvdata(dai->dev);
- struct msm_dai_q6_dai_data *dai_data =
- (substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
- &mi2s_dai_data->rx_dai.mi2s_dai_data :
- &mi2s_dai_data->tx_dai.mi2s_dai_data);
- u16 port_id = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
- MI2S_RX : MI2S_TX);
- int rc = 0;
-
- dev_dbg(dai->dev, "%s: cmd:%d dai_data->status_mask = %ld",
- __func__, cmd, *dai_data->status_mask);
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- case SNDRV_PCM_TRIGGER_RESUME:
- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- if (!test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
- afe_port_start_nowait(port_id,
- &dai_data->port_config, dai_data->rate);
+ dev_err(dai->dev, "fail to open AFE port %x\n",
+ dai->id);
+ else
set_bit(STATUS_PORT_STARTED,
dai_data->status_mask);
- }
- break;
- case SNDRV_PCM_TRIGGER_STOP:
- case SNDRV_PCM_TRIGGER_SUSPEND:
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- if (test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
- afe_port_stop_nowait(port_id);
- clear_bit(STATUS_PORT_STARTED,
- dai_data->status_mask);
- }
- break;
-
- default:
- rc = -EINVAL;
}
return rc;
@@ -906,21 +872,20 @@
/*
* For AUX PCM Interface the below sequence of clk
- * settings and afe_open is a strict requirement.
- *
- * Also using afe_open instead of afe_port_start_nowait
- * to make sure the port is open before deasserting the
- * clock line. This is required because pcm register is
- * not written before clock deassert. Hence the hw does
- * not get updated with new setting if the below clock
- * assert/deasset and afe_open sequence is not followed.
+ * settings and opening of afe port is a strict requirement.
+ * afe_port_start is called to make sure to make sure the port
+ * is open before deasserting the clock line. This is
+ * required because pcm register is not written before
+ * clock deassert. Hence the hw does not get updated with
+ * new setting if the below clock assert/deasset and afe_port_start
+ * sequence is not followed.
*/
clk_reset(pcm_clk, CLK_RESET_ASSERT);
- afe_open(PCM_RX, &dai_data->port_config, dai_data->rate);
+ afe_port_start(PCM_RX, &dai_data->port_config, dai_data->rate);
- afe_open(PCM_TX, &dai_data->port_config, dai_data->rate);
+ afe_port_start(PCM_TX, &dai_data->port_config, dai_data->rate);
if (dai_data->rate == 8000) {
pcm_clk_rate = auxpcm_pdata->mode_8k.pcm_clk_rate;
} else if (dai_data->rate == 16000) {
@@ -988,21 +953,22 @@
/*
* For AUX PCM Interface the below sequence of clk
- * settings and afe_open is a strict requirement.
- *
- * Also using afe_open instead of afe_port_start_nowait
- * to make sure the port is open before deasserting the
- * clock line. This is required because pcm register is
- * not written before clock deassert. Hence the hw does
- * not get updated with new setting if the below clock
- * assert/deasset and afe_open sequence is not followed.
+ * settings and opening of afe port is a strict requirement.
+ * afe_port_start is called to make sure to make sure the port
+ * is open before deasserting the clock line. This is
+ * required because pcm register is not written before
+ * clock deassert. Hence the hw does not get updated with
+ * new setting if the below clock assert/deasset and afe_port_start
+ * sequence is not followed.
*/
clk_reset(sec_pcm_clk, CLK_RESET_ASSERT);
- afe_open(SECONDARY_PCM_RX, &dai_data->port_config, dai_data->rate);
+ afe_port_start(SECONDARY_PCM_RX, &dai_data->port_config,
+ dai_data->rate);
- afe_open(SECONDARY_PCM_TX, &dai_data->port_config, dai_data->rate);
+ afe_port_start(SECONDARY_PCM_TX, &dai_data->port_config,
+ dai_data->rate);
if (dai_data->rate == 8000) {
pcm_clk_rate = auxpcm_pdata->mode_8k.pcm_clk_rate;
} else if (dai_data->rate == 16000) {
@@ -1034,11 +1000,24 @@
int rc = 0;
if (!test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
- /* PORT START should be set if prepare called in active state */
- rc = afe_q6_interface_prepare();
+ switch (dai->id) {
+ case VOICE_PLAYBACK_TX:
+ case VOICE_RECORD_TX:
+ case VOICE_RECORD_RX:
+ rc = afe_start_pseudo_port(dai->id);
+ default:
+ rc = afe_port_start(dai->id, &dai_data->port_config,
+ dai_data->rate);
+ }
+
if (IS_ERR_VALUE(rc))
- dev_err(dai->dev, "fail to open AFE APR\n");
+ dev_err(dai->dev, "fail to open AFE port %x\n",
+ dai->id);
+ else
+ set_bit(STATUS_PORT_STARTED,
+ dai_data->status_mask);
}
+
return rc;
}
@@ -1071,63 +1050,6 @@
}
-static int msm_dai_q6_trigger(struct snd_pcm_substream *substream, int cmd,
- struct snd_soc_dai *dai)
-{
- struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev);
- int rc = 0;
-
- /* Start/stop port without waiting for Q6 AFE response. Need to have
- * native q6 AFE driver propagates AFE response in order to handle
- * port start/stop command error properly if error does arise.
- */
- pr_debug("%s:port:%d cmd:%d dai_data->status_mask = %ld",
- __func__, dai->id, cmd, *dai_data->status_mask);
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- case SNDRV_PCM_TRIGGER_RESUME:
- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- if (!test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
- switch (dai->id) {
- case VOICE_PLAYBACK_TX:
- case VOICE_RECORD_TX:
- case VOICE_RECORD_RX:
- afe_pseudo_port_start_nowait(dai->id);
- break;
- default:
- afe_port_start_nowait(dai->id,
- &dai_data->port_config, dai_data->rate);
- break;
- }
- set_bit(STATUS_PORT_STARTED,
- dai_data->status_mask);
- }
- break;
- case SNDRV_PCM_TRIGGER_STOP:
- case SNDRV_PCM_TRIGGER_SUSPEND:
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- if (test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
- switch (dai->id) {
- case VOICE_PLAYBACK_TX:
- case VOICE_RECORD_TX:
- case VOICE_RECORD_RX:
- afe_pseudo_port_stop_nowait(dai->id);
- break;
- default:
- afe_port_stop_nowait(dai->id);
- break;
- }
- clear_bit(STATUS_PORT_STARTED,
- dai_data->status_mask);
- }
- break;
-
- default:
- rc = -EINVAL;
- }
-
- return rc;
-}
static int msm_dai_q6_dai_auxpcm_probe(struct snd_soc_dai *dai)
{
struct msm_dai_q6_dai_data *dai_data;
@@ -1535,7 +1457,6 @@
static struct snd_soc_dai_ops msm_dai_q6_mi2s_ops = {
.startup = msm_dai_q6_mi2s_startup,
.prepare = msm_dai_q6_mi2s_prepare,
- .trigger = msm_dai_q6_mi2s_trigger,
.hw_params = msm_dai_q6_mi2s_hw_params,
.shutdown = msm_dai_q6_mi2s_shutdown,
.set_fmt = msm_dai_q6_mi2s_set_fmt,
@@ -1543,7 +1464,6 @@
static struct snd_soc_dai_ops msm_dai_q6_ops = {
.prepare = msm_dai_q6_prepare,
- .trigger = msm_dai_q6_trigger,
.hw_params = msm_dai_q6_hw_params,
.shutdown = msm_dai_q6_shutdown,
.set_fmt = msm_dai_q6_set_fmt,
diff --git a/sound/soc/msm/qdsp6/q6afe.c b/sound/soc/msm/qdsp6/q6afe.c
index 7b16adb..2f6772d 100644
--- a/sound/soc/msm/qdsp6/q6afe.c
+++ b/sound/soc/msm/qdsp6/q6afe.c
@@ -376,11 +376,10 @@
if ((afe_cal_addr[path].cal_paddr != cal_block.cal_paddr) ||
(cal_block.cal_size > afe_cal_addr[path].cal_size)) {
if (afe_cal_addr[path].cal_paddr != 0)
- afe_cmd_memory_unmap_nowait(
+ afe_cmd_memory_unmap(
afe_cal_addr[path].cal_paddr);
- afe_cmd_memory_map_nowait(cal_block.cal_paddr,
- cal_block.cal_size);
+ afe_cmd_memory_map(cal_block.cal_paddr, cal_block.cal_size);
afe_cal_addr[path].cal_paddr = cal_block.cal_paddr;
afe_cal_addr[path].cal_size = cal_block.cal_size;
}
@@ -400,12 +399,21 @@
"cal size = %d, cal addr = 0x%x\n", __func__,
port_id, path, cal_block.cal_size, cal_block.cal_paddr);
+ atomic_set(&this_afe.state, 1);
result = apr_send_pkt(this_afe.apr, (uint32_t *) &afe_cal);
if (result < 0) {
pr_err("%s: AFE cal for port %d failed\n",
__func__, port_id);
}
+ result = wait_event_timeout(this_afe.wait,
+ (atomic_read(&this_afe.state) == 0),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!result) {
+ pr_err("%s: wait_event timeout SET AFE CAL\n", __func__);
+ goto done;
+ }
+
pr_debug("%s: AFE cal sent for path %d device!\n", __func__, path);
done:
return;
@@ -421,8 +429,11 @@
afe_send_cal_block(RX_CAL, port_id);
}
-int afe_port_start_nowait(u16 port_id, union afe_port_config *afe_config,
- u32 rate) /* This function is no blocking */
+/* This function sends multi-channel HDMI configuration command and AFE
+ * calibration which is only supported by QDSP6 on 8960 and onward.
+ */
+int afe_port_start(u16 port_id, union afe_port_config *afe_config,
+ u32 rate)
{
struct afe_port_start_command start;
struct afe_audioif_config_command config;
@@ -442,11 +453,9 @@
(port_id == RT_PROXY_DAI_001_TX))
port_id = VIRTUAL_ID_TO_PORTID(port_id);
- if (this_afe.apr == NULL) {
- pr_err("%s: AFE APR is not registered\n", __func__);
- ret = -ENODEV;
+ ret = afe_q6_interface_prepare();
+ if (IS_ERR_VALUE(ret))
return ret;
- }
if (port_id == HDMI_RX) {
config.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
@@ -513,6 +522,8 @@
config.port_id = port_id;
config.port = *afe_config;
+ atomic_set(&this_afe.state, 1);
+ atomic_set(&this_afe.status, 0);
ret = apr_send_pkt(this_afe.apr, (uint32_t *) &config);
if (ret < 0) {
pr_err("%s: AFE enable for port %d failed\n", __func__,
@@ -521,6 +532,21 @@
goto fail_cmd;
}
+ ret = wait_event_timeout(this_afe.wait,
+ (atomic_read(&this_afe.state) == 0),
+ msecs_to_jiffies(TIMEOUT_MS));
+
+ if (!ret) {
+ pr_err("%s: wait_event timeout IF CONFIG\n", __func__);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+ if (atomic_read(&this_afe.status) != 0) {
+ pr_err("%s: config cmd failed\n", __func__);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+
/* send AFE cal */
afe_send_cal(port_id);
@@ -535,6 +561,7 @@
start.gain = 0x2000;
start.sample_rate = rate;
+ atomic_set(&this_afe.state, 1);
ret = apr_send_pkt(this_afe.apr, (uint32_t *) &start);
if (IS_ERR_VALUE(ret)) {
@@ -544,6 +571,15 @@
goto fail_cmd;
}
+ ret = wait_event_timeout(this_afe.wait,
+ (atomic_read(&this_afe.state) == 0),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: wait_event timeout PORT START\n", __func__);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+
if (this_afe.task != current)
this_afe.task = current;
@@ -555,6 +591,7 @@
return ret;
}
+/* This function should be used by 8660 exclusively */
int afe_open(u16 port_id, union afe_port_config *afe_config, int rate)
{
struct afe_port_start_command start;