OSDN Git Service

q6asm: retry asm open in case of session id conflict
authorHan Lu <hanlu@codeaurora.org>
Tue, 9 Jan 2018 12:24:47 +0000 (20:24 +0800)
committerGerrit - the friendly Code Review server <code-review@localhost>
Mon, 4 Jun 2018 07:32:36 +0000 (00:32 -0700)
The ADSP asm session ids are managed by the kernel driver. It's all right
for native use cases. But for virtualization use cases, there's no way to
synchronize the session ids' status among virtual machines.  Playing back
in one virtual machine is probably failed, because the session id may had
been occupied by a use case in another virtual machine.

The patch allowed audio playback stream to try all available session ids
in case of session id conflict error, to support concurrent playback from
multiple virtual machines.

Change-Id: If98d05898cec3f05d80c750ec8a014add3b1f34c
Signed-off-by: Han Lu <hanlu@codeaurora.org>
include/sound/q6asm-v2.h
sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c
sound/soc/msm/qdsp6v2/q6asm.c

index 9df3e77..3523fac 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -309,6 +309,10 @@ int q6asm_open_write_v4(struct audio_client *ac, uint32_t format,
 
 int q6asm_open_write_v5(struct audio_client *ac, uint32_t format,
                        uint16_t bits_per_sample);
+
+int q6asm_open_write_with_retry(struct audio_client *ac, uint32_t format,
+                       uint16_t bits_per_sample);
+
 int q6asm_stream_open_write_v2(struct audio_client *ac, uint32_t format,
                               uint16_t bits_per_sample, int32_t stream_id,
                               bool is_gapless_mode);
index 280c665..9ab17d8 100644 (file)
@@ -385,16 +385,10 @@ static int msm_pcm_playback_prepare(struct snd_pcm_substream *substream)
                        return -ENOMEM;
                }
        } else {
-               if (q6asm_get_svc_version(APR_SVC_ASM) >=
-                               ADSP_ASM_API_VERSION_V2)
-                       ret = q6asm_open_write_v5(prtd->audio_client,
+               ret = q6asm_open_write_with_retry(prtd->audio_client,
                                fmt_type, bits_per_sample);
-               else
-                       ret = q6asm_open_write_v4(prtd->audio_client,
-                               fmt_type, bits_per_sample);
-
                if (ret < 0) {
-                       pr_err("%s: q6asm_open_write_v4 failed (%d)\n",
+                       pr_err("%s: q6asm_open_write_with_retry failed (%d)\n",
                        __func__, ret);
                        q6asm_audio_client_free(prtd->audio_client);
                        prtd->audio_client = NULL;
index 567534e..9d3fa1a 100644 (file)
@@ -98,6 +98,7 @@ static struct asm_mmap this_mmap;
 struct audio_session {
        struct audio_client *ac;
        spinlock_t session_lock;
+       bool ignore;
 };
 /* session id: 0 reserved */
 static struct audio_session session[ASM_ACTIVE_STREAMS_ALLOWED + 1];
@@ -597,6 +598,7 @@ int q6asm_mmap_apr_dereg(void)
 static int q6asm_session_alloc(struct audio_client *ac)
 {
        int n;
+
        for (n = 1; n <= ASM_ACTIVE_STREAMS_ALLOWED; n++) {
                if (!(session[n].ac)) {
                        session[n].ac = ac;
@@ -607,6 +609,87 @@ static int q6asm_session_alloc(struct audio_client *ac)
        return -ENOMEM;
 }
 
+static void q6asm_session_set_ignore(int id)
+{
+       session[id].ignore = true;
+}
+
+static void q6asm_session_clean_ignore(void)
+{
+       int i;
+
+       for (i = 1; i <= ASM_ACTIVE_STREAMS_ALLOWED; i++)
+               session[i].ignore = false;
+}
+
+static void q6asm_session_deregister(struct audio_client *ac)
+{
+       rtac_set_asm_handle(ac->session, NULL);
+       apr_deregister(ac->apr2);
+       apr_deregister(ac->apr);
+       q6asm_mmap_apr_dereg();
+       ac->apr2 = NULL;
+       ac->apr = NULL;
+       ac->mmap_apr = NULL;
+}
+
+static int q6asm_session_register(struct audio_client *ac)
+{
+       ac->apr = apr_register("ADSP", "ASM",
+                       (apr_fn)q6asm_callback,
+                       ((ac->session) << 8 | 0x0001),
+                       ac);
+       if (ac->apr == NULL) {
+               pr_err("%s: Registration with APR failed\n", __func__);
+               goto fail_apr1;
+       }
+
+       ac->apr2 = apr_register("ADSP", "ASM",
+                       (apr_fn)q6asm_callback,
+                       ((ac->session) << 8 | 0x0002),
+                       ac);
+       if (ac->apr2 == NULL) {
+               pr_err("%s: Registration with APR-2 failed\n", __func__);
+               goto fail_apr2;
+       }
+
+       rtac_set_asm_handle(ac->session, ac->apr);
+
+       pr_debug("%s: Registering the common port with APR\n", __func__);
+       ac->mmap_apr = q6asm_mmap_apr_reg();
+       if (ac->mmap_apr == NULL)
+               goto fail_mmap;
+
+       return 0;
+
+fail_mmap:
+       apr_deregister(ac->apr2);
+fail_apr2:
+       apr_deregister(ac->apr);
+fail_apr1:
+       return -EINVAL;
+}
+
+static int q6asm_session_try_next(struct audio_client *ac)
+{
+       int n;
+       int s = ac->session;
+
+       for (n = 1; n <= ASM_ACTIVE_STREAMS_ALLOWED; n++) {
+               if (++s > ASM_ACTIVE_STREAMS_ALLOWED)
+                       s -= ASM_ACTIVE_STREAMS_ALLOWED;
+               if (!session[s].ignore && !session[s].ac) {
+                       q6asm_session_deregister(ac);
+                       session[ac->session].ac = NULL;
+                       session[s].ac = ac;
+                       ac->session = s;
+                       return q6asm_session_register(ac);
+               }
+       }
+       pr_debug("%s: session not available\n", __func__);
+       return -EINVAL;
+}
+
 static int q6asm_get_session_id_from_audio_client(struct audio_client *ac)
 {
        int n;
@@ -1139,13 +1222,7 @@ void q6asm_audio_client_free(struct audio_client *ac)
                }
        }
 
-       rtac_set_asm_handle(ac->session, NULL);
-       apr_deregister(ac->apr2);
-       apr_deregister(ac->apr);
-       q6asm_mmap_apr_dereg();
-       ac->apr2 = NULL;
-       ac->apr = NULL;
-       ac->mmap_apr = NULL;
+       q6asm_session_deregister(ac);
        q6asm_session_free(ac);
 
        pr_debug("%s: APR De-Register\n", __func__);
@@ -1307,34 +1384,11 @@ struct audio_client *q6asm_audio_client_alloc(app_cb cb, void *priv)
        ac->fptr_cache_ops = NULL;
        /* DSP expects stream id from 1 */
        ac->stream_id = 1;
-       ac->apr = apr_register("ADSP", "ASM", \
-                       (apr_fn)q6asm_callback,\
-                       ((ac->session) << 8 | 0x0001),\
-                       ac);
-
-       if (ac->apr == NULL) {
-               pr_err("%s: Registration with APR failed\n", __func__);
-               mutex_unlock(&session_lock);
-               goto fail_apr1;
-       }
-       ac->apr2 = apr_register("ADSP", "ASM",
-                       (apr_fn)q6asm_callback,
-                       ((ac->session) << 8 | 0x0002),
-                       ac);
 
-       if (ac->apr2 == NULL) {
-               pr_err("%s: Registration with APR-2 failed\n", __func__);
-               mutex_unlock(&session_lock);
-               goto fail_apr2;
-       }
-
-       rtac_set_asm_handle(n, ac->apr);
-
-       pr_debug("%s: Registering the common port with APR\n", __func__);
-       ac->mmap_apr = q6asm_mmap_apr_reg();
-       if (ac->mmap_apr == NULL) {
+       rc = q6asm_session_register(ac);
+       if (rc < 0) {
                mutex_unlock(&session_lock);
-               goto fail_mmap;
+               goto fail_register;
        }
 
        init_waitqueue_head(&ac->cmd_wait);
@@ -1357,7 +1411,7 @@ struct audio_client *q6asm_audio_client_alloc(app_cb cb, void *priv)
        rc = send_asm_custom_topology(ac);
        if (rc < 0) {
                mutex_unlock(&session_lock);
-               goto fail_mmap;
+               goto fail_send;
        }
 
        pr_debug("%s: session[%d]\n", __func__, ac->session);
@@ -1365,11 +1419,9 @@ struct audio_client *q6asm_audio_client_alloc(app_cb cb, void *priv)
        mutex_unlock(&session_lock);
 
        return ac;
-fail_mmap:
-       apr_deregister(ac->apr2);
-fail_apr2:
-       apr_deregister(ac->apr);
-fail_apr1:
+fail_send:
+       q6asm_session_deregister(ac);
+fail_register:
        q6asm_session_free(ac);
 fail_session:
        return NULL;
@@ -3241,6 +3293,49 @@ int q6asm_open_write_v5(struct audio_client *ac, uint32_t format,
 }
 EXPORT_SYMBOL(q6asm_open_write_v5);
 
+static int q6asm_open_write_version_adaptor(struct audio_client *ac,
+                       uint32_t format, uint16_t bits_per_sample)
+{
+       if (q6asm_get_svc_version(APR_SVC_ASM) >= ADSP_ASM_API_VERSION_V2)
+               return q6asm_open_write_v5(ac, format, bits_per_sample);
+       else
+               return q6asm_open_write_v4(ac, format, bits_per_sample);
+}
+
+/*
+ * q6asm_open_write_with_retry - Opens audio playback session, with retrying
+ * in case of session ID conflict
+ *
+ * @ac: Client session handle
+ * @format: decoder format
+ * @bits_per_sample: bit width of playback session
+ */
+int q6asm_open_write_with_retry(struct audio_client *ac, uint32_t format,
+                       uint16_t bits_per_sample)
+{
+       int i, rc;
+
+       mutex_lock(&session_lock);
+       for (i = 0; i < ASM_ACTIVE_STREAMS_ALLOWED; i++) {
+               rc = q6asm_open_write_version_adaptor(ac, format,
+                                               bits_per_sample);
+               if (rc != -EALREADY)
+                       break;
+
+               pr_debug("%s: session %d is occupied, try next\n",
+                               __func__, ac->session);
+               q6asm_session_set_ignore(ac->session);
+               rc = q6asm_session_try_next(ac);
+               if (rc < 0)
+                       break;
+       }
+       q6asm_session_clean_ignore();
+       mutex_unlock(&session_lock);
+
+       return rc;
+}
+EXPORT_SYMBOL(q6asm_open_write_with_retry);
+
 int q6asm_stream_open_write_v2(struct audio_client *ac, uint32_t format,
                               uint16_t bits_per_sample, int32_t stream_id,
                               bool is_gapless_mode)