From: Vaibhav Agarwal Date: Wed, 13 Jan 2016 21:07:55 +0000 (-0700) Subject: greybus: audio: Cleanup GB protocol connections in case of abrupt codec removal X-Git-Url: https://git.stricted.de/?a=commitdiff_plain;h=25de3491f11064845a45606fa5828a200ecf8c53;p=GitHub%2FLineageOS%2Fandroid_kernel_motorola_exynos9610.git greybus: audio: Cleanup GB protocol connections in case of abrupt codec removal We need to clean up GB protocl connections, otherwise successive codec insertions fails repeatedly. NOTE: As per suggestion, since codec is already removed, one should not trigger any GB command. It'll cause a delay of atleast TIMEOUT value. HOwever, failing to cleanup GB protocol, causes successive module insertion to fail Signed-off-by: Vaibhav Agarwal Signed-off-by: Mark Greer Signed-off-by: Greg Kroah-Hartman --- diff --git a/drivers/staging/greybus/audio_codec.c b/drivers/staging/greybus/audio_codec.c index 93906c8128de..2b4fcbedd161 100644 --- a/drivers/staging/greybus/audio_codec.c +++ b/drivers/staging/greybus/audio_codec.c @@ -56,6 +56,9 @@ static int gbcodec_startup(struct snd_pcm_substream *substream, dev_dbg(dai->dev, "Register %s:%d DAI, ret:%d\n", dai->name, cportid, ret); + if (!ret) + atomic_inc(&gb->users); + return ret; } @@ -83,6 +86,8 @@ static void gbcodec_shutdown(struct snd_pcm_substream *substream, return; } + atomic_dec(&gb->users); + /* deactivate rx/tx */ cportid = gb_dai->connection->intf_cport_id; @@ -428,6 +433,7 @@ static struct snd_soc_codec_driver soc_codec_dev_gbcodec = { .reg_word_size = 1, .idle_bias_off = true, + .ignore_pmdown_time = 1, }; /* @@ -573,6 +579,50 @@ struct device_driver gb_codec_driver = { .owner = THIS_MODULE, }; +/* XXX + * since BE DAI path is not yet properly closed from above layer, + * dsp dai.mi2s_dai_data.status_mask is still set to STATUS_PORT_STARTED + * this causes immediate playback/capture to fail in case relevant mixer + * control is not turned OFF + * user need to try once again after failure to recover DSP state. + */ +static void gb_audio_cleanup(struct gbaudio_codec_info *gb) +{ + int cportid, ret; + struct gbaudio_dai *gb_dai; + struct gb_connection *connection; + struct device *dev = gb->dev; + + list_for_each_entry(gb_dai, &gb->dai_list, list) { + /* + * In case of BE dailink, need to deactivate APBridge + * manually + */ + if (gbaudio_dailink.no_pcm && atomic_read(&gb->users)) { + connection = gb_dai->connection; + /* PB active */ + ret = gb_audio_apbridgea_stop_tx(connection, 0); + if (ret) + dev_info(dev, "%d:Failed during APBridge stop_tx\n", + ret); + cportid = connection->intf_cport_id; + ret = gb_audio_gb_deactivate_tx(gb->mgmt_connection, + cportid); + if (ret) + dev_info(dev, + "%d:Failed during deactivate_tx\n", + ret); + cportid = connection->hd_cport_id; + ret = gb_audio_apbridgea_unregister_cport(connection, 0, + cportid); + if (ret) + dev_info(dev, "%d:Failed during unregister cport\n", + ret); + atomic_dec(&gb->users); + } + } +} + static int gbaudio_codec_probe(struct gb_connection *connection) { int ret, i; @@ -641,6 +691,9 @@ static int gbaudio_codec_probe(struct gb_connection *connection) mutex_lock(&gbcodec->lock); gbcodec->codec_registered = 1; + /* codec cleanup related */ + atomic_set(&gbcodec->users, 0); + /* inform above layer for uevent */ if (!gbcodec->set_uevent && (gbcodec->dai_added == gbcodec->num_dais)) { @@ -696,6 +749,11 @@ static void gbaudio_codec_remove(struct gb_connection *connection) } mutex_unlock(&gbcodec->lock); + if (atomic_read(&gbcodec->users)) { + dev_err(dev, "Cleanup Error: BE stream not yet closed\n"); + gb_audio_cleanup(gbcodec); + } + msm8994_remove_dailink("msm8994-tomtom-mtp-snd-card", &gbaudio_dailink, 1); gbaudio_remove_dailinks(gbcodec); diff --git a/drivers/staging/greybus/audio_codec.h b/drivers/staging/greybus/audio_codec.h index fcf3a0602176..0b587ef74d16 100644 --- a/drivers/staging/greybus/audio_codec.h +++ b/drivers/staging/greybus/audio_codec.h @@ -131,6 +131,9 @@ struct gbaudio_codec_info { struct snd_soc_dapm_route *routes; struct snd_soc_dai_driver *dais; + /* codec users */ + atomic_t users; + /* lists */ struct list_head dai_list; struct list_head widget_list;