From 6905ecf0279ef601dfe8cf757b3c006332f7ee6f Mon Sep 17 00:00:00 2001 From: Vignesh Kulothungan Date: Tue, 13 Mar 2018 16:05:20 -0700 Subject: [PATCH] dsp: add support for mic break detection Add capability to receive notifications from voice service when there is a change in mic break status during a voice call. On receiving notification, send an uevent with mic status to userspace. CRs-Fixed: 2211324 Change-Id: Ie55bd1490fd8cead7261b70169ae36eba95e4011 Signed-off-by: Vignesh Kulothungan --- asoc/msm-pcm-voice-v2.c | 18 ++++ dsp/q6voice.c | 211 ++++++++++++++++++++++++++++++++++++++++ include/dsp/q6voice.h | 48 +++++++++ 3 files changed, 277 insertions(+) diff --git a/asoc/msm-pcm-voice-v2.c b/asoc/msm-pcm-voice-v2.c index 0a4aa0528d95..38da7e7d765a 100644 --- a/asoc/msm-pcm-voice-v2.c +++ b/asoc/msm-pcm-voice-v2.c @@ -519,6 +519,22 @@ done: return ret; } +static int msm_voice_mbd_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = voc_get_mbd_enable(); + return 0; +} + +static int msm_voice_mbd_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + bool enable = ucontrol->value.integer.value[0]; + + voc_set_mbd_enable(enable); + + return 0; +} static const char * const tty_mode[] = {"OFF", "HCO", "VCO", "FULL"}; @@ -664,6 +680,8 @@ static struct snd_kcontrol_new msm_voice_controls[] = { }, SOC_SINGLE_MULTI_EXT("Voice Sidetone Enable", SND_SOC_NOPM, 0, 1, 0, 1, msm_voice_sidetone_get, msm_voice_sidetone_put), + SOC_SINGLE_BOOL_EXT("Voice Mic Break Enable", 0, msm_voice_mbd_get, + msm_voice_mbd_put), }; static const struct snd_pcm_ops msm_pcm_ops = { diff --git a/dsp/q6voice.c b/dsp/q6voice.c index 32e85f952c9b..ce86564b151f 100644 --- a/dsp/q6voice.c +++ b/dsp/q6voice.c @@ -72,6 +72,9 @@ static int voice_send_mvm_unmap_memory_physical_cmd(struct voice_data *v, static int voice_send_mvm_cal_network_cmd(struct voice_data *v); static int voice_send_mvm_media_type_cmd(struct voice_data *v); static int voice_send_mvm_cvd_version_cmd(struct voice_data *v); +static int voice_send_mvm_event_class_cmd(struct voice_data *v, + uint32_t event_id, + uint32_t class_id); static int voice_send_cvs_data_exchange_mode_cmd(struct voice_data *v); static int voice_send_cvs_packet_exchange_config_cmd(struct voice_data *v); static int voice_set_packet_exchange_mode_and_config(uint32_t session_id, @@ -767,6 +770,70 @@ done: return ret; } +static int voice_send_mvm_event_class_cmd(struct voice_data *v, + uint32_t event_id, + uint32_t class_id) +{ + struct vss_inotify_cmd_event_class_t mvm_event; + int ret = 0; + void *apr_mvm = NULL; + u16 mvm_handle = 0; + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + return -EINVAL; + } + + apr_mvm = common.apr_q6_mvm; + if (!apr_mvm) { + pr_err("%s: apr_mvm is NULL.\n", __func__); + return -EINVAL; + } + + memset(&mvm_event, 0, sizeof(mvm_event)); + mvm_handle = voice_get_mvm_handle(v); + mvm_event.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + mvm_event.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(mvm_event) - APR_HDR_SIZE); + mvm_event.hdr.src_port = + voice_get_idx_for_session(v->session_id); + mvm_event.hdr.dest_port = mvm_handle; + mvm_event.hdr.token = 0; + mvm_event.hdr.opcode = event_id; + mvm_event.class_id = class_id; + + v->mvm_state = CMD_STATUS_FAIL; + v->async_err = 0; + ret = apr_send_pkt(apr_mvm, (uint32_t *) &mvm_event); + if (ret < 0) { + pr_err("%s: Error %d sending %x event\n", __func__, ret, + event_id); + goto fail; + } + + ret = wait_event_timeout(v->mvm_wait, + (v->mvm_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout %d\n", __func__, ret); + ret = -ETIMEDOUT; + goto fail; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto fail; + } + return 0; +fail: + return ret; +} + static int voice_send_dual_control_cmd(struct voice_data *v) { int ret = 0; @@ -4243,6 +4310,23 @@ done: return ret; } +static void voice_mic_break_work_fn(struct work_struct *work) +{ + int ret = 0; + char event[25] = ""; + struct voice_data *v = container_of(work, struct voice_data, + voice_mic_break_work); + + snprintf(event, sizeof(event), "MIC_BREAK_STATUS=%s", + v->mic_break_status ? "TRUE" : "FALSE"); + + mutex_lock(&common.common_lock); + ret = q6core_send_uevent(common.uevent_data, event); + if (ret) + pr_err("%s: Send UEvent %s failed :%d\n", __func__, event, ret); + mutex_unlock(&common.common_lock); +} + static int voice_setup_vocproc(struct voice_data *v) { struct module_instance_info mod_inst_info; @@ -4341,6 +4425,11 @@ static int voice_setup_vocproc(struct voice_data *v) if (v->hd_enable) voice_send_hd_cmd(v, v->hd_enable); + if (common.mic_break_enable) + voice_send_mvm_event_class_cmd(v, + VSS_INOTIFY_CMD_LISTEN_FOR_EVENT_CLASS, + VSS_ICOMMON_EVENT_CLASS_VOICE_ACTIVITY_UPDATE); + rtac_add_voice(voice_get_cvs_handle(v), voice_get_cvp_handle(v), v->dev_rx.port_id, v->dev_tx.port_id, @@ -5034,6 +5123,11 @@ static int voice_destroy_vocproc(struct voice_data *v) /* Unload topology modules */ voice_unload_topo_modules(); + if (common.mic_break_enable) + voice_send_mvm_event_class_cmd(v, + VSS_INOTIFY_CMD_CANCEL_EVENT_CLASS, + VSS_ICOMMON_EVENT_CLASS_VOICE_ACTIVITY_UPDATE); + /* destrop cvp session */ cvp_destroy_session_cmd.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, APR_HDR_LEN(APR_HDR_SIZE), @@ -6625,6 +6719,70 @@ uint8_t voc_get_route_flag(uint32_t session_id, uint8_t path_dir) } EXPORT_SYMBOL(voc_get_route_flag); +/** + * voc_get_mbd_enable - + * Retrieve mic break detection enable state + * + * Returns true if mic break detection is enabled or false if disabled + */ +bool voc_get_mbd_enable(void) +{ + bool enable = false; + + mutex_lock(&common.common_lock); + enable = common.mic_break_enable; + mutex_unlock(&common.common_lock); + + return enable; +} +EXPORT_SYMBOL(voc_get_mbd_enable); + +/** + * voc_set_mbd_enable - + * Set mic break detection enable state + * + * @enable: mic break detection state to set + * + * Returns 0 + */ +uint8_t voc_set_mbd_enable(bool enable) +{ + struct voice_data *v = NULL; + struct voice_session_itr itr; + bool check_and_send_event = false; + uint32_t event_id = VSS_INOTIFY_CMD_LISTEN_FOR_EVENT_CLASS; + uint32_t class_id = VSS_ICOMMON_EVENT_CLASS_VOICE_ACTIVITY_UPDATE; + + mutex_lock(&common.common_lock); + if (common.mic_break_enable != enable) + check_and_send_event = true; + common.mic_break_enable = enable; + mutex_unlock(&common.common_lock); + + if (!check_and_send_event) + return 0; + + if (!enable) + event_id = VSS_INOTIFY_CMD_CANCEL_EVENT_CLASS; + + memset(&itr, 0, sizeof(itr)); + + voice_itr_init(&itr, ALL_SESSION_VSID); + while (voice_itr_get_next_session(&itr, &v)) { + if (v != NULL) { + mutex_lock(&v->lock); + if (is_voc_state_active(v->voc_state)) { + voice_send_mvm_event_class_cmd(v, event_id, + class_id); + } + mutex_unlock(&v->lock); + } + } + + return 0; +} +EXPORT_SYMBOL(voc_set_mbd_enable); + /** * voc_end_voice_call - * command to end voice call @@ -7171,6 +7329,7 @@ static int32_t qdsp_mvm_callback(struct apr_client_data *data, void *priv) uint32_t *ptr = NULL; struct common_data *c = NULL; struct voice_data *v = NULL; + struct vss_evt_voice_activity *voice_act_update = NULL; int i = 0; struct vss_iversion_rsp_get_t *version_rsp = NULL; @@ -7276,6 +7435,8 @@ static int32_t qdsp_mvm_callback(struct apr_client_data *data, void *priv) case VSS_IMVM_CMD_STANDBY_VOICE: case VSS_IHDVOICE_CMD_ENABLE: case VSS_IHDVOICE_CMD_DISABLE: + case VSS_INOTIFY_CMD_LISTEN_FOR_EVENT_CLASS: + case VSS_INOTIFY_CMD_CANCEL_EVENT_CLASS: pr_debug("%s: cmd = 0x%x\n", __func__, ptr[0]); v->mvm_state = CMD_STATUS_SUCCESS; v->async_err = ptr[1]; @@ -7380,7 +7541,33 @@ static int32_t qdsp_mvm_callback(struct apr_client_data *data, void *priv) v->mvm_state = CMD_STATUS_SUCCESS; wake_up(&v->mvm_wait); } + } else if (data->opcode == VSS_ICOMMON_EVT_VOICE_ACTIVITY_UPDATE) { + if (data->payload_size == sizeof(struct vss_evt_voice_activity)) { + voice_act_update = + (struct vss_evt_voice_activity *) + data->payload; + + /* Drop notifications other than Mic Break */ + if ((voice_act_update->activity + != VSS_ICOMMON_VOICE_ACTIVITY_MIC_BREAK) + && (voice_act_update->activity + != VSS_ICOMMON_VOICE_ACITIVTY_MIC_UNBREAK)) + return 0; + + switch (voice_act_update->activity) { + case VSS_ICOMMON_VOICE_ACTIVITY_MIC_BREAK: + v->mic_break_status = true; + break; + case VSS_ICOMMON_VOICE_ACITIVTY_MIC_UNBREAK: + v->mic_break_status = false; + break; + } + + if (c->mic_break_enable) + schedule_work(&(v->voice_mic_break_work)); + } } + return 0; } @@ -9642,6 +9829,14 @@ done: return ret; } +static void voc_release_uevent_data(struct kobject *kobj) +{ + struct audio_uevent_data *data = container_of(kobj, + struct audio_uevent_data, + kobj); + kfree(data); +} + /** * is_voc_initialized: * @@ -9694,6 +9889,18 @@ int __init voice_init(void) mutex_init(&common.common_lock); + common.uevent_data = kzalloc(sizeof(*(common.uevent_data)), GFP_KERNEL); + if (!common.uevent_data) + return -ENOMEM; + + /* + * Set release function to cleanup memory related to kobject + * before initializing the kobject. + */ + common.uevent_data->ktype.release = voc_release_uevent_data; + q6core_init_uevent_data(common.uevent_data, "q6voice_uevent"); + common.mic_break_enable = false; + /* Initialize session id with vsid */ init_session_id(); @@ -9734,6 +9941,9 @@ int __init voice_init(void) common.voice[i].voc_state = VOC_INIT; + INIT_WORK(&common.voice[i].voice_mic_break_work, + voice_mic_break_work_fn); + init_waitqueue_head(&common.voice[i].mvm_wait); init_waitqueue_head(&common.voice[i].cvs_wait); init_waitqueue_head(&common.voice[i].cvp_wait); @@ -9754,6 +9964,7 @@ int __init voice_init(void) void voice_exit(void) { + q6core_destroy_uevent_data(common.uevent_data); voice_delete_cal_data(); free_cal_map_table(); } diff --git a/include/dsp/q6voice.h b/include/dsp/q6voice.h index d0ba99001d1d..ca4250872591 100644 --- a/include/dsp/q6voice.h +++ b/include/dsp/q6voice.h @@ -15,6 +15,7 @@ #include #include #include +#include #include #define MAX_VOC_PKT_SIZE 642 @@ -494,6 +495,19 @@ struct vss_icommon_cmd_set_param { #define VSS_IHDVOICE_CMD_ENABLE 0x000130A2 #define VSS_IHDVOICE_CMD_DISABLE 0x000130A3 +/* To listen for events from MVM module */ +#define VSS_INOTIFY_CMD_LISTEN_FOR_EVENT_CLASS 0x00012E56 +/* To cancel listening for events from MVM module */ +#define VSS_INOTIFY_CMD_CANCEL_EVENT_CLASS 0x00012E57 +/* To receive ongoing voice activity notification */ +#define VSS_ICOMMON_EVENT_CLASS_VOICE_ACTIVITY_UPDATE 0x000131EF +/* Voice activity notification from MVM */ +#define VSS_ICOMMON_EVT_VOICE_ACTIVITY_UPDATE 0x000131F0 +/* Mic path is broken. */ +#define VSS_ICOMMON_VOICE_ACTIVITY_MIC_BREAK 0x000131F3 +/* Mic path is restored. */ +#define VSS_ICOMMON_VOICE_ACITIVTY_MIC_UNBREAK 0x000131F4 + enum msm_audio_voc_rate { VOC_0_RATE, /* Blank frame */ VOC_8_RATE, /* 1/8 rate */ @@ -719,6 +733,33 @@ struct vss_imemory_cmd_unmap_t { uint32_t mem_handle; } __packed; +/* + * Payload structure for VSS_INOTIFY_CMD_LISTEN_FOR_EVENT_CLASS and + * VSS_INOTIFY_CMD_CANCEL_EVENT_CLASS commands. + */ +struct vss_inotify_cmd_event_class_t { + struct apr_hdr hdr; + /* Event class ID to listen for. */ + uint32_t class_id; +} __packed; + +/* Payload structure for the VSS_ICOMMOM_EVT_VOICE_ACTIVITY_UPDATE event. */ +struct vss_evt_voice_activity { + uint32_t activity; + /* + * Specifies the voice acitivity. + * @values + * #VSS_ICOMMON_VOICE_ACTIVITY_VPTX_MUTE + * #VSS_ICOMMON_VOICE_ACTIVITY_VPTX_UNMUTE + * #VSS_ICOMMON_VOICE_ACTIVITY_MIC_BREAK + * #VSS_ICOMMON_VOICE_ACITIVTY_MIC_UNBREAK + * #VSS_ICOMMON_VOICE_ACTIVITY_UI_STREAM_TX_MUTE + * #VSS_ICOMMON_VOICE_ACTIVITY_UI_STREAM_TX_UNMUTE + * #VSS_ICOMMON_VOICE_ACTIVITY_UI_VOCPROC_TX_MUTE + * #VSS_ICOMMON_VOICE_ACTIVITY_UI_VOCPROC_TX_UNMUTE + */ +} __packed; + /* TO CVS commands */ #define VSS_ISTREAM_CMD_CREATE_PASSIVE_CONTROL_SESSION 0x00011140 /**< Wait for APRV2_IBASIC_RSP_RESULT response. */ @@ -1854,6 +1895,9 @@ struct voice_data { struct incall_music_info music_info; struct voice_rec_route_state rec_route_state; + + bool mic_break_status; + struct work_struct voice_mic_break_work; }; #define MAX_VOC_SESSIONS 8 @@ -1907,6 +1951,8 @@ struct common_data { struct shared_mem_info source_tracking_sh_mem; struct vss_isourcetrack_activity_data_t sourceTrackingResponse; bool sidetone_enable; + bool mic_break_enable; + struct audio_uevent_data *uevent_data; }; struct voice_session_itr { @@ -1998,6 +2044,8 @@ int voc_set_device_mute(uint32_t session_id, uint32_t dir, uint32_t mute, int voc_get_rx_device_mute(uint32_t session_id); int voc_set_route_flag(uint32_t session_id, uint8_t path_dir, uint8_t set); uint8_t voc_get_route_flag(uint32_t session_id, uint8_t path_dir); +bool voc_get_mbd_enable(void); +uint8_t voc_set_mbd_enable(bool enable); int voc_enable_dtmf_rx_detection(uint32_t session_id, uint32_t enable); void voc_disable_dtmf_det_on_active_sessions(void); int voc_alloc_cal_shared_memory(void);