diff --git a/include/sound/q6asm.h b/include/sound/q6asm.h index 257026e10019..c97521b59e52 100644 --- a/include/sound/q6asm.h +++ b/include/sound/q6asm.h @@ -71,10 +71,12 @@ /* Enable Sample_Rate/Channel_Mode notification event from Decoder */ #define SR_CM_NOTIFY_ENABLE 0x0004 -#define ASYNC_IO_MODE 0x0002 -#define SYNC_IO_MODE 0x0001 -#define NO_TIMESTAMP 0xFF00 -#define SET_TIMESTAMP 0x0000 +#define TUN_WRITE_IO_MODE 0x0008 /* tunnel read write mode */ +#define TUN_READ_IO_MODE 0x0004 /* tunnel read write mode */ +#define ASYNC_IO_MODE 0x0002 +#define SYNC_IO_MODE 0x0001 +#define NO_TIMESTAMP 0xFF00 +#define SET_TIMESTAMP 0x0000 #define SOFT_PAUSE_ENABLE 1 #define SOFT_PAUSE_DISABLE 0 diff --git a/sound/soc/msm/msm-pcm-q6.c b/sound/soc/msm/msm-pcm-q6.c index 62deb63f83de..2a31dd0ece11 100644 --- a/sound/soc/msm/msm-pcm-q6.c +++ b/sound/soc/msm/msm-pcm-q6.c @@ -100,6 +100,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) { @@ -146,18 +165,33 @@ static void event_handler(uint32_t opcode, pr_debug("cmd[%d]=0x%08x\n", i, *ptrmem); in_frame_info[token][0] = payload[2]; in_frame_info[token][1] = payload[3]; - 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: { @@ -642,6 +676,7 @@ static int msm_pcm_hw_params(struct snd_pcm_substream *substream, struct audio_buffer *buf; int dir, ret; int format = FORMAT_LINEAR_PCM; + struct msm_pcm_routing_evt event; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) dir = IN; @@ -665,10 +700,13 @@ 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, - prtd->audio_client->perf_mode, - prtd->session_id, substream->stream); - } + 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, event); + } if (dir == OUT) { ret = q6asm_audio_client_buf_alloc_contiguous(dir, diff --git a/sound/soc/msm/msm-pcm-routing.c b/sound/soc/msm/msm-pcm-routing.c index 81f8e026f959..27ac47cc6036 100644 --- a/sound/soc/msm/msm-pcm-routing.c +++ b/sound/soc/msm/msm-pcm-routing.c @@ -41,6 +41,12 @@ struct msm_pcm_routing_bdai_data { bool perf_mode; }; +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 @@ -194,24 +200,32 @@ 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}, + {{0, INVALID_SESSION, {NULL, NULL} }, + {0, INVALID_SESSION, {NULL, NULL} } }, /* MULTIMEDIA7*/ - {INVALID_SESSION, INVALID_SESSION}, + {{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} } }, }; static uint8_t is_be_dai_extproc(int be_dai) @@ -272,7 +286,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) && @@ -318,7 +332,7 @@ void msm_pcm_routing_reg_phy_stream(int fedai_id, bool perf_mode, int dspst_id, 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; /* re-enable EQ if active */ if (eq_data[fedai_id].enable) msm_send_eq_values(fedai_id); @@ -368,6 +382,19 @@ void msm_pcm_routing_reg_phy_stream(int fedai_id, bool perf_mode, int dspst_id, 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) { @@ -397,8 +424,8 @@ void msm_pcm_routing_dereg_phy_stream(int fedai_id, int stream_type) adm_close(msm_bedais[i].port_id); } - 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); } @@ -423,6 +450,7 @@ static void msm_pcm_routing_process_audio(u16 reg, u16 val, int set) { int session_type, path_type; u32 channels; + struct msm_pcm_routing_fdai_data *fdai; pr_debug("%s: reg %x val %x set %x\n", __func__, reg, val, set); @@ -449,11 +477,24 @@ static void msm_pcm_routing_process_audio(u16 reg, u16 val, int set) voc_start_playback(set); 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 due 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 ((session_type == SESSION_TYPE_RX) && ((channels == 1) || (channels == 2)) && msm_bedais[reg].perf_mode) { @@ -481,7 +522,7 @@ static void msm_pcm_routing_process_audio(u16 reg, u16 val, int set) msm_pcm_routing_build_matrix(val, - fe_dai_map[val][session_type], path_type); + fdai->strm_id, path_type); srs_port_id = msm_bedais[reg].port_id; srs_send_params(srs_port_id, 1, 0); } @@ -490,11 +531,13 @@ static void msm_pcm_routing_process_audio(u16 reg, u16 val, int set) (msm_bedais[reg].port_id == VOICE_PLAYBACK_TX)) voc_start_playback(set); 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) { + fdai->be_srate = msm_bedais[reg].sample_rate; adm_close(msm_bedais[reg].port_id); msm_pcm_routing_build_matrix(val, - fe_dai_map[val][session_type], path_type); + fdai->strm_id, path_type); } } if ((msm_bedais[reg].port_id == VOICE_RECORD_RX) @@ -970,12 +1013,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; } @@ -2640,7 +2683,9 @@ static int msm_pcm_routing_close(struct snd_pcm_substream *substream) mutex_lock(&routing_lock); 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); srs_port_id = -1; } @@ -2663,6 +2708,7 @@ static int msm_pcm_routing_prepare(struct snd_pcm_substream *substream) struct msm_pcm_routing_bdai_data *bedai; u32 channels; bool playback, capture; + 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); @@ -2694,7 +2740,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 due 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 ((playback || capture) && ((channels == 2) || (channels == 1)) && @@ -2721,7 +2781,7 @@ static int msm_pcm_routing_prepare(struct snd_pcm_substream *substream) DEFAULT_COPP_TOPOLOGY); msm_pcm_routing_build_matrix(i, - fe_dai_map[i][session_type], path_type); + fdai->strm_id, path_type); srs_port_id = bedai->port_id; srs_send_params(srs_port_id, 1, 0); } diff --git a/sound/soc/msm/msm-pcm-routing.h b/sound/soc/msm/msm-pcm-routing.h index 6be665aaed7f..b9645e703b8d 100644 --- a/sound/soc/msm/msm-pcm-routing.h +++ b/sound/soc/msm/msm-pcm-routing.h @@ -107,6 +107,10 @@ 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 @@ -116,6 +120,15 @@ void msm_pcm_routing_reg_phy_stream(int fedai_id, bool perf_mode, void msm_pcm_routing_reg_psthr_stream(int fedai_id, int dspst_id, int stream_type, int enable); +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 lpa_set_volume(unsigned volume); diff --git a/sound/soc/msm/qdsp6/q6asm.c b/sound/soc/msm/qdsp6/q6asm.c index 7ca7f75a3421..2cde92a22240 100644 --- a/sound/soc/msm/qdsp6/q6asm.c +++ b/sound/soc/msm/qdsp6/q6asm.c @@ -220,7 +220,7 @@ int q6asm_audio_client_buf_free(unsigned int dir, int rc = 0; pr_debug("%s: Session id %d\n", __func__, ac->session); mutex_lock(&ac->cmd_lock); - if (ac->io_mode == SYNC_IO_MODE) { + if (ac->io_mode & SYNC_IO_MODE) { port = &ac->port[dir]; if (!port->buf) { mutex_unlock(&ac->cmd_lock); @@ -351,7 +351,7 @@ void q6asm_audio_client_free(struct audio_client *ac) if (!ac || !ac->session) return; pr_debug("%s: Session id %d\n", __func__, ac->session); - if (ac->io_mode == SYNC_IO_MODE) { + if (ac->io_mode & SYNC_IO_MODE) { for (loopcnt = 0; loopcnt <= OUT; loopcnt++) { port = &ac->port[loopcnt]; if (!port->buf) @@ -386,14 +386,20 @@ int q6asm_set_io_mode(struct audio_client *ac, uint32_t mode) pr_err("%s APR handle NULL\n", __func__); return -EINVAL; } - if ((mode == ASYNC_IO_MODE) || (mode == SYNC_IO_MODE)) { - ac->io_mode = mode; - pr_debug("%s:Set Mode to %d\n", __func__, ac->io_mode); - return 0; + + if (mode == ASYNC_IO_MODE) { + ac->io_mode &= ~SYNC_IO_MODE; + ac->io_mode |= ASYNC_IO_MODE; + } else if (mode == SYNC_IO_MODE) { + ac->io_mode &= ~ASYNC_IO_MODE; + ac->io_mode |= SYNC_IO_MODE; } else { pr_err("%s:Not an valid IO Mode:%d\n", __func__, ac->io_mode); return -EINVAL; } + + pr_debug("%s:Set Mode to %d\n", __func__, ac->io_mode); + return 0; } struct audio_client *q6asm_audio_client_alloc(app_cb cb, void *priv) @@ -497,7 +503,7 @@ int q6asm_audio_client_buf_alloc(unsigned int dir, if (ac->session <= 0 || ac->session > 8) goto fail; - if (ac->io_mode == SYNC_IO_MODE) { + if (ac->io_mode & SYNC_IO_MODE) { if (ac->port[dir].buf) { pr_debug("%s: buffer already allocated\n", __func__); return 0; @@ -922,7 +928,7 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv) pr_debug("%s: Rxed opcode[0x%x] status[0x%x] token[%d]", __func__, payload[0], payload[1], data->token); - if (ac->io_mode == SYNC_IO_MODE) { + if (ac->io_mode & SYNC_IO_MODE) { if (port->buf == NULL) { pr_err("%s: Unexpected Write Done\n", __func__); @@ -1004,7 +1010,7 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv) if (in_enable_flag) in_cont_index++; #endif - if (ac->io_mode == SYNC_IO_MODE) { + if (ac->io_mode & SYNC_IO_MODE) { if (port->buf == NULL) { pr_err("%s: Unexpected Write Done\n", __func__); return -EINVAL; @@ -1073,7 +1079,7 @@ void *q6asm_is_cpu_buf_avail(int dir, struct audio_client *ac, uint32_t *size, if (!ac || ((dir != IN) && (dir != OUT))) return NULL; - if (ac->io_mode == SYNC_IO_MODE) { + if (ac->io_mode & SYNC_IO_MODE) { port = &ac->port[dir]; mutex_lock(&port->lock); @@ -1167,7 +1173,7 @@ int q6asm_is_dsp_buf_avail(int dir, struct audio_client *ac) if (!ac || (dir != OUT)) return ret; - if (ac->io_mode == SYNC_IO_MODE) { + if (ac->io_mode & SYNC_IO_MODE) { port = &ac->port[dir]; mutex_lock(&port->lock); @@ -1298,6 +1304,9 @@ 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; @@ -1574,6 +1583,9 @@ int q6asm_open_write(struct audio_client *ac, uint32_t format) pr_err("%s: format = %x not supported\n", __func__, format); goto fail_cmd; } + + ac->io_mode |= TUN_WRITE_IO_MODE; + return 0; fail_cmd: return -EINVAL; @@ -3322,7 +3334,7 @@ int q6asm_read(struct audio_client *ac) pr_err("APR handle NULL\n"); return -EINVAL; } - if (ac->io_mode == SYNC_IO_MODE) { + if (ac->io_mode & SYNC_IO_MODE) { port = &ac->port[OUT]; q6asm_add_hdr(ac, &read.hdr, sizeof(read), FALSE); @@ -3374,7 +3386,7 @@ int q6asm_read_nolock(struct audio_client *ac) pr_err("APR handle NULL\n"); return -EINVAL; } - if (ac->io_mode == SYNC_IO_MODE) { + if (ac->io_mode & SYNC_IO_MODE) { port = &ac->port[OUT]; q6asm_add_hdr_async(ac, &read.hdr, sizeof(read), FALSE); @@ -3398,7 +3410,7 @@ int q6asm_read_nolock(struct audio_client *ac) read.hdr.token = port->dsp_buf; port->dsp_buf = (port->dsp_buf + 1) & (port->max_buf_cnt - 1); - pr_debug("%s:buf add[0x%x] token[%d] uid[%d]\n", __func__, + pr_info("%s:buf add[0x%x] token[%d] uid[%d]\n", __func__, read.buf_add, read.hdr.token, read.uid); @@ -3559,7 +3571,7 @@ int q6asm_write(struct audio_client *ac, uint32_t len, uint32_t msw_ts, return -EINVAL; } pr_debug("%s: session[%d] len=%d", __func__, ac->session, len); - if (ac->io_mode == SYNC_IO_MODE) { + if (ac->io_mode & SYNC_IO_MODE) { port = &ac->port[IN]; q6asm_add_hdr(ac, &write.hdr, sizeof(write), @@ -3641,7 +3653,7 @@ int q6asm_write_nolock(struct audio_client *ac, uint32_t len, uint32_t msw_ts, return -EINVAL; } pr_debug("%s: session[%d] len=%d", __func__, ac->session, len); - if (ac->io_mode == SYNC_IO_MODE) { + if (ac->io_mode & SYNC_IO_MODE) { port = &ac->port[IN]; q6asm_add_hdr_async(ac, &write.hdr, sizeof(write), @@ -3845,9 +3857,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) { + 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]; @@ -3857,7 +3871,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--; } }