From f0c7c822524fa7e8c9fa688ccb8e05e456ab4a71 Mon Sep 17 00:00:00 2001 From: Damir Didjusto Date: Wed, 13 Nov 2013 15:26:14 -0800 Subject: [PATCH] ASoC: msm: flush if prior and current backends rate not matching It is found that during device switch from one backend with one sample rate to another backend with another sample rate the command to QDSP6 ADM which maps audio stream session to a particular backend would not get carried out until pending data of audio stream session from previous backend is either read out or flushed. This scenario occurs when application stops providing more buffers to retrieve captured data. Remedy is to flush upon detection of rate mismatching. Change-Id: Ia9d9a1819c6e21a44f2778185420ca43cca30258 Signed-off-by: Damir Didjusto Signed-off-by: Vimal Puthanveed --- include/sound/q6asm-v2.h | 2 + sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c | 60 ++++++++--- sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c | 110 ++++++++++++++++----- sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.h | 14 +++ sound/soc/msm/qdsp6v2/q6asm.c | 6 +- 5 files changed, 154 insertions(+), 38 deletions(-) diff --git a/include/sound/q6asm-v2.h b/include/sound/q6asm-v2.h index e8cef3f8341..b00cfc9576f 100644 --- a/include/sound/q6asm-v2.h +++ b/include/sound/q6asm-v2.h @@ -67,6 +67,8 @@ /* Enable Sample_Rate/Channel_Mode notification event from Decoder */ #define SR_CM_NOTIFY_ENABLE 0x0004 +#define TUN_WRITE_IO_MODE 0x0008 /* tunnel read write mode */ +#define TUN_READ_IO_MODE 0x0004 /* tunnel read write mode */ #define SYNC_IO_MODE 0x0001 #define ASYNC_IO_MODE 0x0002 #define COMPRESSED_IO 0x0040 diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c index dc41948d219..f25f746922c 100644 --- a/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c +++ b/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c @@ -109,6 +109,25 @@ static struct snd_pcm_hw_constraint_list constraints_sample_rates = { .mask = 0, }; +static void msm_pcm_route_event_handler(enum msm_pcm_routing_event event, + void *priv_data) +{ + struct msm_audio *prtd = priv_data; + + BUG_ON(!prtd); + + pr_debug("%s: event %x\n", __func__, event); + + switch (event) { + case MSM_PCM_RT_EVT_BUF_RECFG: + q6asm_cmd(prtd->audio_client, CMD_PAUSE); + q6asm_cmd(prtd->audio_client, CMD_FLUSH); + q6asm_run(prtd->audio_client, 0, 0, 0); + default: + break; + } +} + static void event_handler(uint32_t opcode, uint32_t token, uint32_t *payload, void *priv) { @@ -151,18 +170,31 @@ static void event_handler(uint32_t opcode, pr_debug("token = 0x%08x\n", token); in_frame_info[token][0] = payload[4]; in_frame_info[token][1] = payload[5]; - prtd->pcm_irq_pos += in_frame_info[token][0]; - pr_debug("pcm_irq_pos=%d\n", prtd->pcm_irq_pos); - if (atomic_read(&prtd->start)) - snd_pcm_period_elapsed(substream); - if (atomic_read(&prtd->in_count) <= prtd->periods) - atomic_inc(&prtd->in_count); - wake_up(&the_locks.read_wait); - if (prtd->mmap_flag - && q6asm_is_cpu_buf_avail_nolock(OUT, + /* assume data size = 0 during flushing */ + if (in_frame_info[token][0]) { + prtd->pcm_irq_pos += in_frame_info[token][0]; + pr_debug("pcm_irq_pos=%d\n", prtd->pcm_irq_pos); + if (atomic_read(&prtd->start)) + snd_pcm_period_elapsed(substream); + if (atomic_read(&prtd->in_count) <= prtd->periods) + atomic_inc(&prtd->in_count); + wake_up(&the_locks.read_wait); + if (prtd->mmap_flag && + q6asm_is_cpu_buf_avail_nolock(OUT, prtd->audio_client, &size, &idx)) - q6asm_read_nolock(prtd->audio_client); + q6asm_read_nolock(prtd->audio_client); + } else { + pr_debug("%s: reclaim flushed buf in_count %x\n", + __func__, atomic_read(&prtd->in_count)); + atomic_inc(&prtd->in_count); + if (atomic_read(&prtd->in_count) == prtd->periods) { + pr_info("%s: reclaimed all bufs\n", __func__); + if (atomic_read(&prtd->start)) + snd_pcm_period_elapsed(substream); + wake_up(&the_locks.read_wait); + } + } break; } case APR_BASIC_RSP_RESULT: { @@ -681,6 +713,7 @@ static int msm_pcm_hw_params(struct snd_pcm_substream *substream, int dir, ret; struct msm_plat_data *pdata; uint16_t bits_per_sample = 16; + struct msm_pcm_routing_evt event; pdata = (struct msm_plat_data *) dev_get_drvdata(soc_prtd->platform->dev); @@ -737,9 +770,12 @@ static int msm_pcm_hw_params(struct snd_pcm_substream *substream, pr_debug("%s: session ID %d\n", __func__, prtd->audio_client->session); prtd->session_id = prtd->audio_client->session; - msm_pcm_routing_reg_phy_stream(soc_prtd->dai_link->be_id, + event.event_func = msm_pcm_route_event_handler; + event.priv_data = (void *) prtd; + msm_pcm_routing_reg_phy_stream_v2(soc_prtd->dai_link->be_id, prtd->audio_client->perf_mode, - prtd->session_id, substream->stream); + prtd->session_id, substream->stream, + event); } ret = q6asm_audio_client_buf_alloc_contiguous(dir, diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c index 8a9a8771cb7..63e9b2f30ba 100644 --- a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c +++ b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c @@ -50,6 +50,12 @@ struct msm_pcm_routing_bdai_data { unsigned int format; }; +struct msm_pcm_routing_fdai_data { + u16 be_srate; /* track prior backend sample rate for flushing purpose */ + int strm_id; /* ASM stream ID */ + struct msm_pcm_routing_evt event_info; +}; + #define INVALID_SESSION -1 #define SESSION_TYPE_RX 0 #define SESSION_TYPE_TX 1 @@ -244,25 +250,35 @@ static struct msm_pcm_routing_bdai_data msm_bedais[MSM_BACKEND_DAI_MAX] = { /* Track ASM playback & capture sessions of DAI */ -static int fe_dai_map[MSM_FRONTEND_DAI_MM_SIZE][2] = { +static struct msm_pcm_routing_fdai_data + fe_dai_map[MSM_FRONTEND_DAI_MM_SIZE][2] = { /* MULTIMEDIA1 */ - {INVALID_SESSION, INVALID_SESSION}, + {{0, INVALID_SESSION, {NULL, NULL} }, + {0, INVALID_SESSION, {NULL, NULL} } }, /* MULTIMEDIA2 */ - {INVALID_SESSION, INVALID_SESSION}, + {{0, INVALID_SESSION, {NULL, NULL} }, + {0, INVALID_SESSION, {NULL, NULL} } }, /* MULTIMEDIA3 */ - {INVALID_SESSION, INVALID_SESSION}, + {{0, INVALID_SESSION, {NULL, NULL} }, + {0, INVALID_SESSION, {NULL, NULL} } }, /* MULTIMEDIA4 */ - {INVALID_SESSION, INVALID_SESSION}, + {{0, INVALID_SESSION, {NULL, NULL} }, + {0, INVALID_SESSION, {NULL, NULL} } }, /* MULTIMEDIA5 */ - {INVALID_SESSION, INVALID_SESSION}, + {{0, INVALID_SESSION, {NULL, NULL} }, + {0, INVALID_SESSION, {NULL, NULL} } }, /* MULTIMEDIA6 */ - {INVALID_SESSION, INVALID_SESSION}, - /* MULTIMEDIA7 */ - {INVALID_SESSION, INVALID_SESSION}, + {{0, INVALID_SESSION, {NULL, NULL} }, + {0, INVALID_SESSION, {NULL, NULL} } }, + /* MULTIMEDIA7*/ + {{0, INVALID_SESSION, {NULL, NULL} }, + {0, INVALID_SESSION, {NULL, NULL} } }, /* MULTIMEDIA8 */ - {INVALID_SESSION, INVALID_SESSION}, + {{0, INVALID_SESSION, {NULL, NULL} }, + {0, INVALID_SESSION, {NULL, NULL} } }, /* MULTIMEDIA9 */ - {INVALID_SESSION, INVALID_SESSION}, + {{0, INVALID_SESSION, {NULL, NULL} }, + {0, INVALID_SESSION, {NULL, NULL} } }, }; /* Track performance mode of all front-end multimedia sessions. @@ -328,7 +344,7 @@ void msm_pcm_routing_reg_psthr_stream(int fedai_id, int dspst_id, mutex_lock(&routing_lock); - fe_dai_map[fedai_id][session_type] = dspst_id; + fe_dai_map[fedai_id][session_type].strm_id = dspst_id; for (i = 0; i < MSM_BACKEND_DAI_MAX; i++) { if (!is_be_dai_extproc(i) && (afe_get_port_type(msm_bedais[i].port_id) == port_type) && @@ -370,7 +386,7 @@ void msm_pcm_routing_reg_phy_stream(int fedai_id, bool perf_mode, mutex_lock(&routing_lock); payload.num_copps = 0; /* only RX needs to use payload */ - fe_dai_map[fedai_id][session_type] = dspst_id; + fe_dai_map[fedai_id][session_type].strm_id = dspst_id; fe_dai_perf_mode[fedai_id][session_type] = perf_mode; /* re-enable EQ if active */ @@ -429,6 +445,19 @@ void msm_pcm_routing_reg_phy_stream(int fedai_id, bool perf_mode, mutex_unlock(&routing_lock); } +void msm_pcm_routing_reg_phy_stream_v2(int fedai_id, bool perf_mode, + int dspst_id, int stream_type, + struct msm_pcm_routing_evt event_info) +{ + msm_pcm_routing_reg_phy_stream(fedai_id, perf_mode, dspst_id, + stream_type); + + if (stream_type == SNDRV_PCM_STREAM_PLAYBACK) + fe_dai_map[fedai_id][SESSION_TYPE_RX].event_info = event_info; + else + fe_dai_map[fedai_id][SESSION_TYPE_TX].event_info = event_info; +} + void msm_pcm_routing_dereg_phy_stream(int fedai_id, int stream_type) { int i, port_type, session_type, path_type, topology; @@ -464,8 +493,8 @@ void msm_pcm_routing_dereg_phy_stream(int fedai_id, int stream_type) } } - fe_dai_map[fedai_id][session_type] = INVALID_SESSION; - + fe_dai_map[fedai_id][session_type].strm_id = INVALID_SESSION; + fe_dai_map[fedai_id][session_type].be_srate = 0; mutex_unlock(&routing_lock); } @@ -491,6 +520,7 @@ static void msm_pcm_routing_process_audio(u16 reg, u16 val, int set) int session_type, path_type, port_id, topology; u32 channels; uint16_t bits_per_sample = 16; + struct msm_pcm_routing_fdai_data *fdai; pr_debug("%s: reg %x val %x set %x\n", __func__, reg, val, set); @@ -517,10 +547,23 @@ static void msm_pcm_routing_process_audio(u16 reg, u16 val, int set) (msm_bedais[reg].port_id == VOICE2_PLAYBACK_TX))) voc_start_playback(set, msm_bedais[reg].port_id); set_bit(val, &msm_bedais[reg].fe_sessions); - if (msm_bedais[reg].active && fe_dai_map[val][session_type] != + fdai = &fe_dai_map[val][session_type]; + if (msm_bedais[reg].active && fdai->strm_id != INVALID_SESSION) { channels = msm_bedais[reg].channel; + if (session_type == SESSION_TYPE_TX && + fdai->be_srate && + (fdai->be_srate != msm_bedais[reg].sample_rate)) { + pr_debug("%s: flush strm %d diff BE rates\n", + __func__, fdai->strm_id); + + if (fdai->event_info.event_func) + fdai->event_info.event_func( + MSM_PCM_RT_EVT_BUF_RECFG, + fdai->event_info.priv_data); + fdai->be_srate = 0; /* might not need it */ + } if (msm_bedais[reg].format == SNDRV_PCM_FORMAT_S24_LE) bits_per_sample = 24; @@ -544,7 +587,7 @@ static void msm_pcm_routing_process_audio(u16 reg, u16 val, int set) topology, false, bits_per_sample); msm_pcm_routing_build_matrix(val, - fe_dai_map[val][session_type], path_type, + fdai->strm_id, path_type, fe_dai_perf_mode[val][session_type]); port_id = srs_port_id = msm_bedais[reg].port_id; srs_send_params(srs_port_id, 1, 0); @@ -560,7 +603,8 @@ static void msm_pcm_routing_process_audio(u16 reg, u16 val, int set) (msm_bedais[reg].port_id == VOICE2_PLAYBACK_TX))) voc_start_playback(set, msm_bedais[reg].port_id); clear_bit(val, &msm_bedais[reg].fe_sessions); - if (msm_bedais[reg].active && fe_dai_map[val][session_type] != + fdai = &fe_dai_map[val][session_type]; + if (msm_bedais[reg].active && fdai->strm_id != INVALID_SESSION) { adm_close(msm_bedais[reg].port_id, fe_dai_perf_mode[val][session_type]); @@ -568,7 +612,7 @@ static void msm_pcm_routing_process_audio(u16 reg, u16 val, int set) (fe_dai_perf_mode[val][session_type] == false)) dolby_dap_deinit(msm_bedais[reg].port_id); msm_pcm_routing_build_matrix(val, - fe_dai_map[val][session_type], path_type, + fdai->strm_id, path_type, fe_dai_perf_mode[val][session_type]); } } @@ -1197,12 +1241,12 @@ static int msm_routing_set_srs_trumedia_control_HDMI( static void msm_send_eq_values(int eq_idx) { int result; - struct audio_client *ac = - q6asm_get_audio_client(fe_dai_map[eq_idx][SESSION_TYPE_RX]); + struct audio_client *ac = q6asm_get_audio_client( + fe_dai_map[eq_idx][SESSION_TYPE_RX].strm_id); if (ac == NULL) { pr_err("%s: Could not get audio client for session: %d\n", - __func__, fe_dai_map[eq_idx][SESSION_TYPE_RX]); + __func__, fe_dai_map[eq_idx][SESSION_TYPE_RX].strm_id); goto done; } @@ -3797,7 +3841,9 @@ static int msm_pcm_routing_close(struct snd_pcm_substream *substream) mutex_lock(&routing_lock); topology = get_topology(path_type); for_each_set_bit(i, &bedai->fe_sessions, MSM_FRONTEND_DAI_MM_SIZE) { - if (fe_dai_map[i][session_type] != INVALID_SESSION) { + if (fe_dai_map[i][session_type].strm_id != INVALID_SESSION) { + fe_dai_map[i][session_type].be_srate = + bedai->sample_rate; adm_close(bedai->port_id, fe_dai_perf_mode[i][session_type]); srs_port_id = -1; @@ -3824,6 +3870,7 @@ static int msm_pcm_routing_prepare(struct snd_pcm_substream *substream) u32 channels; bool playback, capture; uint16_t bits_per_sample = 16; + struct msm_pcm_routing_fdai_data *fdai; if (be_id >= MSM_BACKEND_DAI_MAX) { pr_err("%s: unexpected be_id %d\n", __func__, be_id); @@ -3855,8 +3902,21 @@ static int msm_pcm_routing_prepare(struct snd_pcm_substream *substream) capture = substream->stream == SNDRV_PCM_STREAM_CAPTURE; for_each_set_bit(i, &bedai->fe_sessions, MSM_FRONTEND_DAI_MM_SIZE) { - if (fe_dai_map[i][session_type] != INVALID_SESSION) { + fdai = &fe_dai_map[i][session_type]; + if (fdai->strm_id != INVALID_SESSION) { + if (session_type == SESSION_TYPE_TX && + fdai->be_srate && + (fdai->be_srate != bedai->sample_rate)) { + pr_debug("%s: flush strm %d diff BE rates\n", + __func__, + fdai->strm_id); + if (fdai->event_info.event_func) + fdai->event_info.event_func( + MSM_PCM_RT_EVT_BUF_RECFG, + fdai->event_info.priv_data); + fdai->be_srate = 0; /* might not need it */ + } channels = bedai->channel; if (bedai->format == SNDRV_PCM_FORMAT_S24_LE) bits_per_sample = 24; @@ -3883,7 +3943,7 @@ static int msm_pcm_routing_prepare(struct snd_pcm_substream *substream) } msm_pcm_routing_build_matrix(i, - fe_dai_map[i][session_type], path_type, + fdai->strm_id, path_type, fe_dai_perf_mode[i][session_type]); port_id = srs_port_id = bedai->port_id; srs_send_params(srs_port_id, 1, 0); diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.h b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.h index 10be15088a3..f18f7249d69 100644 --- a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.h +++ b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.h @@ -133,6 +133,11 @@ enum { MSM_BACKEND_DAI_MAX, }; +enum msm_pcm_routing_event { + MSM_PCM_RT_EVT_BUF_RECFG, + MSM_PCM_RT_EVT_MAX, +}; + /* dai_id: front-end ID, * dspst_id: DSP audio stream ID * stream_type: playback or capture @@ -142,6 +147,15 @@ void msm_pcm_routing_reg_phy_stream(int fedai_id, bool perf_mode, int dspst_id, void msm_pcm_routing_reg_psthr_stream(int fedai_id, int dspst_id, int stream_type); +struct msm_pcm_routing_evt { + void (*event_func)(enum msm_pcm_routing_event, void *); + void *priv_data; +}; + +void msm_pcm_routing_reg_phy_stream_v2(int fedai_id, bool perf_mode, + int dspst_id, int stream_type, + struct msm_pcm_routing_evt event_info); + void msm_pcm_routing_dereg_phy_stream(int fedai_id, int stream_type); int msm_routing_check_backend_enabled(int fedai_id); diff --git a/sound/soc/msm/qdsp6v2/q6asm.c b/sound/soc/msm/qdsp6v2/q6asm.c index a31fdd2827e..631e9bd83b0 100644 --- a/sound/soc/msm/qdsp6v2/q6asm.c +++ b/sound/soc/msm/qdsp6v2/q6asm.c @@ -1745,6 +1745,7 @@ static int __q6asm_open_read(struct audio_client *ac, rc); goto fail_cmd; } + ac->io_mode |= TUN_READ_IO_MODE; return 0; fail_cmd: return -EINVAL; @@ -1838,6 +1839,7 @@ static int __q6asm_open_write(struct audio_client *ac, uint32_t format, rc); goto fail_cmd; } + ac->io_mode |= TUN_WRITE_IO_MODE; return 0; fail_cmd: return -EINVAL; @@ -4261,9 +4263,11 @@ static void q6asm_reset_buf_state(struct audio_client *ac) { int cnt = 0; int loopcnt = 0; + int used; struct audio_port_data *port = NULL; if (ac->io_mode & SYNC_IO_MODE) { + used = (ac->io_mode & TUN_WRITE_IO_MODE ? 1 : 0); mutex_lock(&ac->cmd_lock); for (loopcnt = 0; loopcnt <= OUT; loopcnt++) { port = &ac->port[loopcnt]; @@ -4273,7 +4277,7 @@ static void q6asm_reset_buf_state(struct audio_client *ac) while (cnt >= 0) { if (!port->buf) continue; - port->buf[cnt].used = 1; + port->buf[cnt].used = used; cnt--; } }