greybus: audio: Cleanup GB protocol connections in case of abrupt codec removal
authorVaibhav Agarwal <vaibhav.agarwal@linaro.org>
Wed, 13 Jan 2016 21:07:55 +0000 (14:07 -0700)
committerGreg Kroah-Hartman <gregkh@google.com>
Thu, 14 Jan 2016 00:48:49 +0000 (16:48 -0800)
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 <vaibhav.agarwal@linaro.org>
Signed-off-by: Mark Greer <mgreer@animalcreek.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
drivers/staging/greybus/audio_codec.c
drivers/staging/greybus/audio_codec.h

index 93906c8128dea772a74f23ba8c5603dc22033191..2b4fcbedd161f7277ee8d2b98d0f52c78f553ddd 100644 (file)
@@ -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);
index fcf3a060217685ee56700c48f71fcef13ddf13e7..0b587ef74d168bb6ae575bc73821993306110cac 100644 (file)
@@ -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;