From da0a39d62e5cad3d84b9c7ff4071e910151bc5fd Mon Sep 17 00:00:00 2001 From: Viraja Kommaraju Date: Tue, 24 Sep 2019 18:26:10 +0530 Subject: [PATCH] q6asm: retry asm_open_read and asm_open_loopback during session id conflict 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 & capture stream to try all available session ids in case of session id conflict error, to support concurrent playback from multiple virtual machines. Change-Id: Ibdde0b21643f135066b017ab678d999841b23475 Signed-off-by: Viraja Kommaraju --- asoc/msm-pcm-loopback-v2.c | 7 +- asoc/msm-pcm-q6-v2.c | 22 +--- dsp/q6asm.c | 252 +++++++++++++++++++++++++++++++------ include/dsp/q6asm-v2.h | 9 ++ 4 files changed, 230 insertions(+), 60 deletions(-) mode change 100644 => 100755 asoc/msm-pcm-q6-v2.c diff --git a/asoc/msm-pcm-loopback-v2.c b/asoc/msm-pcm-loopback-v2.c index 9f33b5994527..880b0b0894da 100755 --- a/asoc/msm-pcm-loopback-v2.c +++ b/asoc/msm-pcm-loopback-v2.c @@ -345,11 +345,12 @@ static int msm_pcm_open(struct snd_pcm_substream *substream) } pcm->audio_client->fedai_id = rtd->dai_link->id; - pcm->session_id = pcm->audio_client->session; pcm->audio_client->perf_mode = pdata->perf_mode; pcm->audio_client->stream_type = substream->stream; - ret = q6asm_open_loopback_v2(pcm->audio_client, - bits_per_sample); + ret = q6asm_open_loopback_with_retry(pcm->audio_client, + bits_per_sample); + pcm->session_id = pcm->audio_client->session; + if (ret < 0) { dev_err(component->dev, "%s: pcm out open failed\n", __func__); diff --git a/asoc/msm-pcm-q6-v2.c b/asoc/msm-pcm-q6-v2.c old mode 100644 new mode 100755 index 7d8916a11b3b..489ec0b82347 --- a/asoc/msm-pcm-q6-v2.c +++ b/asoc/msm-pcm-q6-v2.c @@ -435,15 +435,8 @@ static int msm_pcm_playback_prepare(struct snd_pcm_substream *substream) return -ENOMEM; } } else { - if ((q6core_get_avcs_api_version_per_service( - APRV2_IDS_SERVICE_ID_ADSP_ASM_V) >= - 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 failed (%d)\n", __func__, ret); @@ -576,16 +569,9 @@ static int msm_pcm_capture_prepare(struct snd_pcm_substream *substream) prtd->audio_client->stream_type = SNDRV_PCM_STREAM_CAPTURE; prtd->audio_client->fedai_id = soc_prtd->dai_link->id; - if ((q6core_get_avcs_api_version_per_service( - APRV2_IDS_SERVICE_ID_ADSP_ASM_V) >= - ADSP_ASM_API_VERSION_V2)) - ret = q6asm_open_read_v5(prtd->audio_client, - FORMAT_LINEAR_PCM, - bits_per_sample, false, ENC_CFG_ID_NONE); - else - ret = q6asm_open_read_v4(prtd->audio_client, - FORMAT_LINEAR_PCM, - bits_per_sample, false, ENC_CFG_ID_NONE); + ret = q6asm_open_read_with_retry(prtd->audio_client, + FORMAT_LINEAR_PCM, + bits_per_sample, false); if (ret < 0) { pr_err("%s: q6asm_open_read failed\n", __func__); q6asm_audio_client_free(prtd->audio_client); diff --git a/dsp/q6asm.c b/dsp/q6asm.c index d041ae2aad26..cefd37795ef6 100644 --- a/dsp/q6asm.c +++ b/dsp/q6asm.c @@ -101,6 +101,7 @@ struct audio_session { struct audio_client *ac; spinlock_t session_lock; struct mutex mutex_lock_per_session; + bool ignore; }; /* session id: 0 reserved */ static struct audio_session session[ASM_ACTIVE_STREAMS_ALLOWED + 1]; @@ -585,6 +586,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; @@ -1225,13 +1307,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__); @@ -1436,34 +1512,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__); + rc = q6asm_session_register(ac); + if (rc < 0) { 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) { - mutex_unlock(&session_lock); - goto fail_mmap; + goto fail_register; } init_waitqueue_head(&ac->cmd_wait); @@ -1486,7 +1539,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); @@ -1494,11 +1547,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; @@ -3428,6 +3479,53 @@ int q6asm_open_read_v5(struct audio_client *ac, uint32_t format, } EXPORT_SYMBOL(q6asm_open_read_v5); +static int q6asm_open_read_version_adaptor(struct audio_client *ac, + uint32_t format, uint16_t bits_per_sample, bool ts_mode) +{ + if (q6core_get_avcs_api_version_per_service( + APRV2_IDS_SERVICE_ID_ADSP_ASM_V) >= + ADSP_ASM_API_VERSION_V2) + return q6asm_open_read_v5(ac, FORMAT_LINEAR_PCM, + bits_per_sample, false, ENC_CFG_ID_NONE); + else + return q6asm_open_read_v4(ac, FORMAT_LINEAR_PCM, + bits_per_sample, false, ENC_CFG_ID_NONE); +} + +/* + * q6asm_open_read_with_retry - Opens audio capture session, with retrying + * in case of session ID conflict + * + * @ac: Client session handle + * @format: encoder format + * @bits_per_sample: bit width of capture session + * @ts_mode: timestamp mode + */ +int q6asm_open_read_with_retry(struct audio_client *ac, uint32_t format, + uint16_t bits_per_sample, bool ts_mode) +{ + int i, rc; + + mutex_lock(&session_lock); + for (i = 0; i < ASM_ACTIVE_STREAMS_ALLOWED; i++) { + rc = q6asm_open_read_version_adaptor(ac, format, + bits_per_sample, ts_mode); + 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_read_with_retry); /** * q6asm_open_write_compressed - @@ -3759,6 +3857,51 @@ int q6asm_open_write_v4(struct audio_client *ac, uint32_t format, } EXPORT_SYMBOL(q6asm_open_write_v4); +static int q6asm_open_write_version_adaptor(struct audio_client *ac, + uint32_t format, uint16_t bits_per_sample) +{ + if (q6core_get_avcs_api_version_per_service( + APRV2_IDS_SERVICE_ID_ADSP_ASM_V) >= + 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) @@ -4171,6 +4314,37 @@ fail_cmd: } EXPORT_SYMBOL(q6asm_open_loopback_v2); +/* + * q6asm_open_loopback_with_retry - Opens audio loopback session, with retrying + * in case of session ID conflict + * + * @ac: Client session handle + * @bits_per_sample: bit width of sample + */ +int q6asm_open_loopback_with_retry(struct audio_client *ac, + uint16_t bits_per_sample) +{ + int i, rc; + + mutex_lock(&session_lock); + for (i = 0; i < ASM_ACTIVE_STREAMS_ALLOWED; i++) { + rc = q6asm_open_loopback_v2(ac, 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_loopback_with_retry); + /** * q6asm_open_transcode_loopback - * command to open ASM in transcode loopback mode diff --git a/include/dsp/q6asm-v2.h b/include/dsp/q6asm-v2.h index 502b964d0516..0b03ac942809 100644 --- a/include/dsp/q6asm-v2.h +++ b/include/dsp/q6asm-v2.h @@ -304,6 +304,9 @@ int q6asm_open_read_v5(struct audio_client *ac, uint32_t format, uint16_t bits_per_sample, bool ts_mode, uint32_t enc_cfg_id); +int q6asm_open_read_with_retry(struct audio_client *ac, uint32_t format, + uint16_t bits_per_sample, bool ts_mode); + int q6asm_open_write(struct audio_client *ac, uint32_t format /*, uint16_t bits_per_sample*/); @@ -323,6 +326,9 @@ 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); @@ -357,6 +363,9 @@ int q6asm_open_read_write_v2(struct audio_client *ac, uint32_t rd_format, int q6asm_open_loopback_v2(struct audio_client *ac, uint16_t bits_per_sample); +int q6asm_open_loopback_with_retry(struct audio_client *ac, + uint16_t bits_per_sample); + int q6asm_open_transcode_loopback(struct audio_client *ac, uint16_t bits_per_sample, uint32_t source_format, uint32_t sink_format);