ASoC: wcd9310: force slimbus ports to disconnect during error cases
With shared data channel architecture, SLIMBUS driver
only removes slimbus channel when all clients vote to
have channel removed. In case of subsystem restart,
client such as MDM can go down without withdrawing
vote. During CODEC path shutdown, CODEC driver will
receive slimbus slave interrupt in time indicating
port disconnection because slimbus channel has not
be been voted off. Then, CODEC driver blindly
shutdown rest of CODEC path. This results in
overflow error on Rx path and underflow error on
Tx path. In case of time out waiting for port disconnect
interrupts to arrive, force ports to disconnect
Signed-off-by: SathishKumar Mani <smani@codeaurora.org>
BUG-ID: 7313016
diff --git a/drivers/mfd/wcd9xxx-slimslave.c b/drivers/mfd/wcd9xxx-slimslave.c
index 71c68ac..b4cf435 100644
--- a/drivers/mfd/wcd9xxx-slimslave.c
+++ b/drivers/mfd/wcd9xxx-slimslave.c
@@ -535,7 +535,6 @@
unsigned int ch_cnt)
{
u16 grph = 0;
- u32 sph[SLIM_MAX_RX_PORTS] = {0};
int i = 0 , idx = 0;
int ret = 0;
struct wcd9xxx_slim_sch_rx *rx = sh_ch.rx;
@@ -549,10 +548,9 @@
ret = -EINVAL;
goto err;
}
- sph[i] = rx[idx].sph;
grph = rx[idx].grph;
- pr_debug("%s: ch_num[%d] %d, idx %d, sph[%d] %x, grph %x\n",
- __func__, i, ch_num[i], idx, i, sph[i], grph);
+ pr_debug("%s: ch_num[%d] %d, idx %d, grph %x\n",
+ __func__, i, ch_num[i], idx, grph);
}
/* slim_control_ch (REMOVE) */
@@ -561,12 +559,6 @@
pr_err("%s: slim_control_ch failed ret[%d]\n", __func__, ret);
goto err;
}
- /* slim_disconnect_port */
- ret = slim_disconnect_ports(wcd9xxx->slim, sph, ch_cnt);
- if (ret < 0) {
- pr_err("%s: slim_disconnect_ports failed ret[%d]\n",
- __func__, ret);
- }
for (i = 0; i < ch_cnt; i++) {
idx = (ch_num[i] - BASE_CH_NUM - sh_ch.rx_port_start_offset);
rx[idx].grph = 0;
@@ -580,7 +572,6 @@
unsigned int ch_cnt)
{
u16 grph = 0;
- u32 sph[SLIM_MAX_TX_PORTS] = {0};
int ret = 0;
int i = 0 , idx = 0;
struct wcd9xxx_slim_sch_tx *tx = sh_ch.tx;
@@ -594,15 +585,8 @@
ret = -EINVAL;
goto err;
}
- sph[i] = tx[idx].sph;
grph = tx[idx].grph;
}
- /* slim_disconnect_port */
- ret = slim_disconnect_ports(wcd9xxx->slim, sph, ch_cnt);
- if (ret < 0) {
- pr_err("%s: slim_disconnect_ports failed ret[%d]\n",
- __func__, ret);
- }
/* slim_control_ch (REMOVE) */
ret = slim_control_ch(wcd9xxx->slim, grph, SLIM_CH_REMOVE, true);
if (ret < 0) {
@@ -633,3 +617,48 @@
return ret;
}
EXPORT_SYMBOL_GPL(wcd9xxx_get_slave_port);
+
+int wcd9xxx_disconnect_port(struct wcd9xxx *wcd9xxx, unsigned int *ch_num,
+ unsigned int ch_cnt, unsigned int rx_tx)
+{
+ u32 sph[SLIM_MAX_TX_PORTS] = {0};
+ int i = 0 , idx = 0;
+ int ret = 0;
+ struct wcd9xxx_slim_sch_rx *rx = sh_ch.rx;
+ struct wcd9xxx_slim_sch_tx *tx = sh_ch.tx;
+
+ pr_debug("%s: ch_cnt[%d], rx_tx flag = %d\n", __func__, ch_cnt, rx_tx);
+ for (i = 0; i < ch_cnt; i++) {
+ /* rx_tx will be 1 for rx, 0 for tx */
+ if (rx_tx) {
+ idx = (ch_num[i] - BASE_CH_NUM -
+ sh_ch.rx_port_start_offset);
+ if (idx < 0) {
+ pr_err("%s: Invalid index found for RX = %d\n",
+ __func__, idx);
+ ret = -EINVAL;
+ goto err;
+ }
+ sph[i] = rx[idx].sph;
+ } else {
+ idx = (ch_num[i] - BASE_CH_NUM);
+ if (idx < 0) {
+ pr_err("%s:Invalid index found for TX = %d\n",
+ __func__, idx);
+ ret = -EINVAL;
+ goto err;
+ }
+ sph[i] = tx[idx].sph;
+ }
+ }
+
+ /* slim_disconnect_port */
+ ret = slim_disconnect_ports(wcd9xxx->slim, sph, ch_cnt);
+ if (ret < 0) {
+ pr_err("%s: slim_disconnect_ports failed ret[%d]\n",
+ __func__, ret);
+ }
+err:
+ return ret;
+}
+EXPORT_SYMBOL_GPL(wcd9xxx_disconnect_port);
diff --git a/include/linux/mfd/wcd9xxx/wcd9xxx-slimslave.h b/include/linux/mfd/wcd9xxx/wcd9xxx-slimslave.h
index 9619527..0d5d058 100644
--- a/include/linux/mfd/wcd9xxx/wcd9xxx-slimslave.h
+++ b/include/linux/mfd/wcd9xxx/wcd9xxx-slimslave.h
@@ -116,4 +116,6 @@
unsigned int *rx_ch,
unsigned int *tx_ch);
int wcd9xxx_get_slave_port(unsigned int ch_num);
+int wcd9xxx_disconnect_port(struct wcd9xxx *wcd9xxx, unsigned int *ch_num,
+ unsigned int tot_ch, unsigned int rx_tx);
#endif /* __WCD9310_SLIMSLAVE_H_ */
diff --git a/sound/soc/codecs/wcd9310.c b/sound/soc/codecs/wcd9310.c
index 1bdf167..b8d2c32 100644
--- a/sound/soc/codecs/wcd9310.c
+++ b/sound/soc/codecs/wcd9310.c
@@ -4503,8 +4503,8 @@
pr_err("%s: Slim close tx/rx wait timeout\n",
__func__);
ret = -EINVAL;
- }
- ret = 0;
+ } else
+ ret = 0;
break;
}
return ret;
@@ -4517,7 +4517,7 @@
struct snd_soc_codec *codec = w->codec;
struct tabla_priv *tabla_p = snd_soc_codec_get_drvdata(codec);
u32 j = 0;
- u32 ret = 0;
+ int ret = 0;
codec->control_data = dev_get_drvdata(codec->dev->parent);
tabla = codec->control_data;
@@ -4574,13 +4574,22 @@
ret = wcd9xxx_close_slim_sch_rx(tabla,
tabla_p->dai[j].ch_num,
tabla_p->dai[j].ch_tot);
- tabla_p->dai[j].rate = 0;
- memset(tabla_p->dai[j].ch_num, 0, (sizeof(u32)*
- tabla_p->dai[j].ch_tot));
- tabla_p->dai[j].ch_tot = 0;
ret = tabla_codec_enable_chmask(tabla_p,
SND_SOC_DAPM_POST_PMD,
j);
+ if (ret < 0) {
+ ret = wcd9xxx_disconnect_port(tabla,
+ tabla_p->dai[j].ch_num,
+ tabla_p->dai[j].ch_tot,
+ 1);
+ pr_info("%s: Disconnect RX port ret = %d\n",
+ __func__, ret);
+ }
+ tabla_p->dai[j].rate = 0;
+ memset(tabla_p->dai[j].ch_num, 0, (sizeof(u32)*
+ tabla_p->dai[j].ch_tot));
+ tabla_p->dai[j].ch_tot = 0;
+
if ((tabla != NULL) &&
(tabla->dev != NULL) &&
(tabla->dev->parent != NULL)) {
@@ -4600,7 +4609,7 @@
struct tabla_priv *tabla_p = snd_soc_codec_get_drvdata(codec);
/* index to the DAI ID, for now hardcoding */
u32 j = 0;
- u32 ret = 0;
+ int ret = 0;
codec->control_data = dev_get_drvdata(codec->dev->parent);
tabla = codec->control_data;
@@ -4657,13 +4666,21 @@
ret = wcd9xxx_close_slim_sch_tx(tabla,
tabla_p->dai[j].ch_num,
tabla_p->dai[j].ch_tot);
+ ret = tabla_codec_enable_chmask(tabla_p,
+ SND_SOC_DAPM_POST_PMD,
+ j);
+ if (ret < 0) {
+ ret = wcd9xxx_disconnect_port(tabla,
+ tabla_p->dai[j].ch_num,
+ tabla_p->dai[j].ch_tot, 0);
+ pr_info("%s: Disconnect TX port, ret = %d\n",
+ __func__, ret);
+ }
+
tabla_p->dai[j].rate = 0;
memset(tabla_p->dai[j].ch_num, 0, (sizeof(u32)*
tabla_p->dai[j].ch_tot));
tabla_p->dai[j].ch_tot = 0;
- ret = tabla_codec_enable_chmask(tabla_p,
- SND_SOC_DAPM_POST_PMD,
- j);
if ((tabla != NULL) &&
(tabla->dev != NULL) &&
(tabla->dev->parent != NULL)) {