// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2017 MediaTek Inc. */ /***************************************************************************** * Header Files *****************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* #include */ #include #include #include #include #include /* FOR SCP REVOCER */ #ifdef SIGTEST #include #endif #include #include #include #include #include #include #ifdef CONFIG_MTK_TINYSYS_SCP_SUPPORT #include "scp.h" #include "audio_task_manager.h" #include "audio_ipi_queue.h" #endif /* #ifdef CONFIG_MTK_TINYSYS_SCP_SUPPORT */ #include "vow.h" #include "vow_scp.h" #include "vow_assert.h" /***************************************************************************** * Variable Definition ****************************************************************************/ static unsigned int VowDrv_Wait_Queue_flag; static unsigned int VoiceData_Wait_Queue_flag; static DECLARE_WAIT_QUEUE_HEAD(VowDrv_Wait_Queue); static DECLARE_WAIT_QUEUE_HEAD(VoiceData_Wait_Queue); static DEFINE_SPINLOCK(vowdrv_lock); static struct wakeup_source *vow_suspend_lock; static int init_flag = -1; // PCM dump function, need CONFIG_MTK_TINYSYS_SCP_SUPPORT #ifdef CONFIG_MTK_TINYSYS_SCP_SUPPORT static struct file *file_recog_data; static uint32_t recog_dump_data_routine_cnt_pass; static struct wakeup_source *pcm_dump_wake_lock; static struct dump_queue_t *dump_queue; static struct task_struct *pcm_dump_task; static bool b_enable_dump; static struct dump_work_t dump_work[NUM_DUMP_DATA]; static struct workqueue_struct *dump_workqueue[NUM_DUMP_DATA]; static wait_queue_head_t wq_dump_pcm; struct pcm_dump_t { char decode_pcm[FRAME_BUF_SIZE]; }; #define DUMP_PCM_DATA_PATH "/data/vendor/audiohal/audio_dump" static struct file *file_bargein_pcm_input; static struct file *file_bargein_echo_ref; static struct file *file_bargein_delay_info; static uint32_t bargein_dump_data_routine_cnt_pass; static bool bargein_dump_info_flag; static bool file_bargein_pcm_input_open; static bool file_bargein_echo_ref_open; static bool file_bargein_delay_info_open; static bool file_recog_data_open; #endif /* #ifdef CONFIG_MTK_TINYSYS_SCP_SUPPORT */ /***************************************************************************** * Function Declaration ****************************************************************************/ static void vow_service_getVoiceData(void); #ifdef CONFIG_MTK_TINYSYS_SCP_SUPPORT static void vow_ipi_reg_ok(short uuid, int confidence_lv, unsigned int extradata_len); static bool vow_IPICmd_Send(uint8_t data_type, uint8_t ack_type, uint16_t msg_id, uint32_t param1, uint32_t param2, char *payload); static void vow_IPICmd_Received(struct ipi_msg_t *ipi_msg); static bool vow_IPICmd_ReceiveAck(struct ipi_msg_t *ipi_msg); static void vow_Task_Unloaded_Handling(void); static void vow_service_OpenDumpFile(void); static void vow_service_CloseDumpFile(void); static void vow_service_OpenDumpFile_internal(void); static void vow_service_CloseDumpFile_internal(void); static void vow_pcm_dump_init(void); static void vow_pcm_dump_deinit(void); static int vow_pcm_dump_kthread(void *data); static void bargein_dump_routine(struct work_struct *ws); static void recog_dump_routine(struct work_struct *ws); static int vow_service_SearchSpeakerModelWithUuid(int uuid); #endif /* #ifdef CONFIG_MTK_TINYSYS_SCP_SUPPORT */ static bool VowDrv_SetFlag(int type, unsigned int set); static int VowDrv_GetHWStatus(void); static bool VowDrv_SetBargeIn(unsigned int set, unsigned int irq_id); static int vow_service_SearchSpeakerModelWithId(int id); static DEFINE_MUTEX(vow_vmalloc_lock); static DEFINE_MUTEX(vow_extradata_mutex); static DEFINE_MUTEX(voicedata_mutex); /***************************************************************************** * VOW SERVICES *****************************************************************************/ static struct { struct vow_speaker_model_t vow_speaker_model[MAX_VOW_SPEAKER_MODEL]; unsigned long vow_info_apuser[MAX_VOW_INFO_LEN]; unsigned long voicedata_user_addr; unsigned long voicedata_user_size; short *voicedata_kernel_ptr; char *voicddata_scp_ptr; dma_addr_t voicedata_scp_addr; char *extradata_ptr; dma_addr_t extradata_addr; char *extradata_mem_ptr; unsigned int extradata_bytelen; unsigned int voicedata_idx; bool scp_command_flag; bool recording_flag; int scp_command_id; int confidence_level; int eint_status; int pwr_status; bool suspend_lock; bool firstRead; unsigned long voicedata_user_return_size_addr; unsigned int voice_buf_offset; unsigned int voice_length; unsigned int transfer_length; struct device_node *node; struct pinctrl *pinctrl; struct pinctrl_state *pins_eint_on; struct pinctrl_state *pins_eint_off; bool bypass_enter_phase3; unsigned int enter_phase3_cnt; unsigned int force_phase_stage; bool swip_log_enable; struct vow_eint_data_struct_t vow_eint_data_struct; unsigned long long scp_recognize_ok_cycle; unsigned long long ap_received_ipi_cycle; bool tx_keyword_start; unsigned int dump_frm_cnt; unsigned int voice_sample_delay; unsigned int bargein_dump_cnt1; unsigned int bargein_dump_cnt2; #ifdef CONFIG_MTK_VOW_DUAL_MIC_SUPPORT short *interleave_pcmdata_ptr; #endif /* #ifdef CONFIG_MTK_VOW_DUAL_MIC_SUPPORT */ bool dump_pcm_flag; bool scp_vow_lch; bool scp_recovering; bool vow_recovering; unsigned int recog_dump_cnt1; unsigned int recog_dump_cnt2; bool split_dumpfile_flag; unsigned int mtkif_type; unsigned int google_engine_version; char alexa_engine_version[VOW_ENGINE_INFO_LENGTH_BYTE]; char google_engine_arch[VOW_ENGINE_INFO_LENGTH_BYTE]; } vowserv; #ifdef CONFIG_MTK_TINYSYS_SCP_SUPPORT static struct { dma_addr_t phy_addr; char *vir_addr; uint32_t size; } bargein_resv_dram; static struct { dma_addr_t phy_addr; char *vir_addr; uint32_t size; } recog_resv_dram; /***************************************************************************** * DSP IPI HANDELER *****************************************************************************/ static void vow_Task_Unloaded_Handling(void) { VOWDRV_DEBUG("%s()\n", __func__); } static bool vow_IPICmd_ReceiveAck(struct ipi_msg_t *ipi_msg) { bool result = false; switch (ipi_msg->msg_id) { case IPIMSG_VOW_ENABLE: case IPIMSG_VOW_DISABLE: case IPIMSG_VOW_SET_MODEL: case IPIMSG_VOW_SET_SMART_DEVICE: case IPIMSG_VOW_APREGDATA_ADDR: case IPIMSG_VOW_PCM_DUMP_ON: case IPIMSG_VOW_PCM_DUMP_OFF: if (ipi_msg->param1 == VOW_IPI_SUCCESS) result = true; break; case IPIMSG_VOW_SET_BARGEIN_ON: case IPIMSG_VOW_SET_BARGEIN_OFF: if (ipi_msg->param1 == VOW_IPI_SUCCESS) result = true; break; case IPIMSG_VOW_SET_FLAG: if (ipi_msg->param1 == VOW_IPI_SUCCESS) { unsigned int return_id; unsigned int return_value; result = true; return_id = (ipi_msg->param2 >> WORD_H); return_value = (ipi_msg->param2 & WORD_L_MASK); switch (return_id) { case VOW_FLAG_FORCE_PHASE1_DEBUG: case VOW_FLAG_FORCE_PHASE2_DEBUG: vowserv.force_phase_stage = return_value; break; case VOW_FLAG_SWIP_LOG_PRINT: vowserv.swip_log_enable = return_value; break; default: break; } } break; default: break; } return result; } static void vow_IPICmd_Received(struct ipi_msg_t *ipi_msg) { unsigned int *ptr32; switch (ipi_msg->msg_id) { case IPIMSG_VOW_COMBINED_INFO: { struct vow_ipi_combined_info_t *ipi_ptr; bool bypass_flag; ipi_ptr = (struct vow_ipi_combined_info_t *)ipi_msg->payload; /* IPIMSG_VOW_RECOGNIZE_OK */ /*VOWDRV_DEBUG("[vow] IPIMSG_VOW_COMBINED_INFO\n");*/ bypass_flag = false; if (ipi_ptr->ipi_type_flag & RECOG_OK_IDX_MASK) { if ((vowserv.recording_flag == true) && (vowserv.tx_keyword_start == true)) { VOWDRV_DEBUG("%s(), bypass this recog ok\n", __func__); bypass_flag = true; } if (bypass_flag == false) { vowserv.ap_received_ipi_cycle = get_cycles(); vowserv.scp_recognize_ok_cycle = ipi_ptr->recog_ok_os_timer; vowserv.enter_phase3_cnt++; if (vowserv.bypass_enter_phase3 == false) { vow_ipi_reg_ok( (short)ipi_ptr->recog_ok_uuid, ipi_ptr->confidence_lv, ipi_ptr->extra_data_len); } } } /* IPIMSG_VOW_DATAREADY */ if ((ipi_ptr->ipi_type_flag & DEBUG_DUMP_IDX_MASK) && (vowserv.recording_flag)) { ptr32 = (unsigned int *)ipi_msg->payload; vowserv.voice_buf_offset = ipi_ptr->voice_buf_offset; vowserv.voice_length = ipi_ptr->voice_length; if (vowserv.voice_length > 320) VOWDRV_DEBUG("vow,v_len=%x\n", vowserv.voice_length); vow_service_getVoiceData(); } /* IPIMSG_VOW_BARGEIN_DUMP_INFO */ if (ipi_ptr->ipi_type_flag & BARGEIN_DUMP_INFO_IDX_MASK) { vowserv.dump_frm_cnt = ipi_ptr->dump_frm_cnt; vowserv.voice_sample_delay = ipi_ptr->voice_sample_delay; VOWDRV_DEBUG("[BargeIn] dump write0 %d %d\n", vowserv.dump_frm_cnt, vowserv.voice_sample_delay); bargein_dump_info_flag = true; } /* IPIMSG_VOW_BARGEIN_PCMDUMP_OK */ if ((ipi_ptr->ipi_type_flag & BARGEIN_DUMP_IDX_MASK) && (vowserv.dump_pcm_flag)) { int ret = 0; uint8_t idx = 0; /* dump_data_t */ idx = DUMP_BARGEIN; dump_work[idx].mic_data_size = ipi_ptr->mic_dump_size; dump_work[idx].mic_offset = ipi_ptr->mic_offset; #ifdef CONFIG_MTK_VOW_DUAL_MIC_SUPPORT dump_work[idx].mic_data_size_R = ipi_ptr->mic_dump_size_R; dump_work[idx].mic_offset_R = ipi_ptr->mic_offset_R; #endif /* #ifdef CONFIG_MTK_VOW_DUAL_MIC_SUPPORT */ dump_work[idx].echo_data_size = ipi_ptr->echo_dump_size; dump_work[idx].echo_offset = ipi_ptr->echo_offset; ret = queue_work(dump_workqueue[idx], &dump_work[idx].work); if (ret == 0) bargein_dump_data_routine_cnt_pass++; } if ((ipi_ptr->ipi_type_flag & RECOG_DUMP_IDX_MASK) && (vowserv.dump_pcm_flag)) { int ret = 0; uint8_t idx = 0; /* dump_data_t */ idx = DUMP_RECOG; dump_work[idx].recog_data_size = ipi_ptr->recog_dump_size; dump_work[idx].recog_data_offset = ipi_ptr->recog_dump_offset; #ifdef CONFIG_MTK_VOW_DUAL_MIC_SUPPORT dump_work[idx].recog_data_size_R = ipi_ptr->recog_dump_size_R; dump_work[idx].recog_data_offset_R = ipi_ptr->recog_dump_offset_R; #endif /* #ifdef CONFIG_MTK_VOW_DUAL_MIC_SUPPORT */ ret = queue_work(dump_workqueue[idx], &dump_work[idx].work); if (ret == 0) recog_dump_data_routine_cnt_pass++; } break; } case IPIMSG_VOW_ALEXA_ENGINE_VER: { VOWDRV_DEBUG("%s(), IPIMSG_VOW_ALEXA_ENGINE_VER %s\r", __func__, ipi_msg->payload); memcpy(vowserv.alexa_engine_version, ipi_msg->payload, sizeof(vowserv.alexa_engine_version)); } break; case IPIMSG_VOW_GOOGLE_ENGINE_VER: { unsigned int *temp = (unsigned int *)ipi_msg->payload; VOWDRV_DEBUG("%s(), IPIMSG_VOW_GOOGLE_ENGINE_VER 0x%x\r", __func__, temp[0]); vowserv.google_engine_version = temp[0]; } break; case IPIMSG_VOW_GOOGLE_ARCH: { VOWDRV_DEBUG("%s(), IPIMSG_VOW_GOOGLE_ARCH %s\r", __func__, ipi_msg->payload); memcpy(vowserv.google_engine_arch, ipi_msg->payload, sizeof(vowserv.google_engine_arch)); } break; default: break; } } static bool vow_IPICmd_Send(uint8_t data_type, uint8_t ack_type, uint16_t msg_id, uint32_t param1, uint32_t param2, char *payload) { bool ret = false; struct ipi_msg_t ipi_msg; int ipi_result = -1; unsigned int retry_time = VOW_IPI_SEND_CNT_TIMEOUT; unsigned int retry_cnt; if (!vow_check_scp_status()) { VOWDRV_DEBUG("SCP is off, bypass send ipi id(%d)\n", msg_id); return false; } if (vowserv.scp_recovering == true) { VOWDRV_DEBUG("scp is recovering, then break\n"); return false; } for (retry_cnt = 0; retry_cnt <= retry_time; retry_cnt++) { ipi_result = audio_send_ipi_msg(&ipi_msg, TASK_SCENE_VOW, AUDIO_IPI_LAYER_TO_DSP, data_type, ack_type, msg_id, param1, param2, payload); if (ipi_result == 0) break; if (vowserv.scp_recovering == true) { VOWDRV_DEBUG("scp is recovering, then break\n"); break; } VOW_ASSERT(retry_cnt != retry_time); msleep(VOW_WAITCHECK_INTERVAL_MS); } if (ipi_result == 0) { /* ipi send pass */ if (ipi_msg.ack_type == AUDIO_IPI_MSG_ACK_BACK) ret = vow_IPICmd_ReceiveAck(&ipi_msg); else ret = true; } return ret; } static void vow_ipi_reg_ok(short uuid, int confidence_lv, unsigned int extradata_len) { int slot; vowserv.scp_command_flag = true; /* transfer uuid to model handle id */ slot = vow_service_SearchSpeakerModelWithUuid(uuid); if (slot < 0) { VOWDRV_DEBUG("%s(), Fail !! Not uuid event !!, exit\n", __func__); return; } vowserv.scp_command_id = vowserv.vow_speaker_model[slot].id; vowserv.confidence_level = confidence_lv; if (extradata_len <= VOW_EXTRA_DATA_SIZE) vowserv.extradata_bytelen = extradata_len; else vowserv.extradata_bytelen = 0; /*VOWDRV_DEBUG("%s(), extradata_bytelen = %d\r", */ /* __func__, vowserv.extradata_bytelen);*/ VowDrv_Wait_Queue_flag = 1; wake_up_interruptible(&VowDrv_Wait_Queue); } static void vow_register_feature(enum feature_id id) { if (!vow_check_scp_status()) { VOWDRV_DEBUG("SCP is off, bypass register id(%d)\n", id); return; } scp_register_feature(id); } static void vow_deregister_feature(enum feature_id id) { if (!vow_check_scp_status()) { VOWDRV_DEBUG("SCP is off, bypass deregister id(%d)\n", id); return; } scp_deregister_feature(id); } #endif /* #ifdef CONFIG_MTK_TINYSYS_SCP_SUPPORT */ static void vow_service_getVoiceData(void) { if (VoiceData_Wait_Queue_flag == 0) { VoiceData_Wait_Queue_flag = 1; wake_up_interruptible(&VoiceData_Wait_Queue); } else { /* VOWDRV_DEBUG("getVoiceData but no one wait for it, */ /* may lost it!!\n"); */ } } /***************************************************************************** * DSP SERVICE FUNCTIONS *****************************************************************************/ static void vow_service_Init(void) { #ifdef CONFIG_MTK_TINYSYS_SCP_SUPPORT int I; bool ret; unsigned int vow_ipi_buf[3]; VOWDRV_DEBUG("%s():%x\n", __func__, init_flag); /* common part */ vowserv.scp_command_flag = false; vowserv.tx_keyword_start = false; /*Initialization*/ vowserv.voicddata_scp_ptr = (char *)(scp_get_reserve_mem_virt(VOW_MEM_ID)) + VOW_VOICEDATA_OFFSET; vowserv.voicedata_scp_addr = scp_get_reserve_mem_phys(VOW_MEM_ID) + VOW_VOICEDATA_OFFSET; /*Extra data*/ vowserv.extradata_ptr = (char *)(scp_get_reserve_mem_virt(VOW_MEM_ID)) + VOW_EXTRA_DATA_OFFSET; vowserv.extradata_addr = scp_get_reserve_mem_phys(VOW_MEM_ID) + VOW_EXTRA_DATA_OFFSET; vow_IPICmd_Send(AUDIO_IPI_MSG_ONLY, AUDIO_IPI_MSG_BYPASS_ACK, IPIMSG_VOW_GET_ALEXA_ENGINE_VER, 0, 0, NULL); vow_IPICmd_Send(AUDIO_IPI_MSG_ONLY, AUDIO_IPI_MSG_BYPASS_ACK, IPIMSG_VOW_GET_GOOGLE_ENGINE_VER, 0, 0, NULL); vow_IPICmd_Send(AUDIO_IPI_MSG_ONLY, AUDIO_IPI_MSG_BYPASS_ACK, IPIMSG_VOW_GET_GOOGLE_ARCH, 0, 0, NULL); audio_load_task(TASK_SCENE_VOW); if (init_flag != 1) { /*register IPI handler*/ audio_task_register_callback(TASK_SCENE_VOW, vow_IPICmd_Received, vow_Task_Unloaded_Handling); /*Initialization*/ VowDrv_Wait_Queue_flag = 0; VoiceData_Wait_Queue_flag = 0; vowserv.recording_flag = false; vowserv.suspend_lock = 0; vowserv.voice_length = 0; vowserv.firstRead = false; vowserv.voice_buf_offset = 0; vowserv.bypass_enter_phase3 = false; vowserv.enter_phase3_cnt = 0; vowserv.scp_recovering = false; vowserv.vow_recovering = false; spin_lock(&vowdrv_lock); vowserv.pwr_status = VOW_PWR_OFF; vowserv.eint_status = VOW_EINT_DISABLE; spin_unlock(&vowdrv_lock); vowserv.force_phase_stage = NO_FORCE; vowserv.swip_log_enable = true; vowserv.voicedata_user_addr = 0; vowserv.voicedata_user_size = 0; vowserv.voicedata_user_return_size_addr = 0; for (I = 0; I < MAX_VOW_SPEAKER_MODEL; I++) { vowserv.vow_speaker_model[I].model_ptr = NULL; vowserv.vow_speaker_model[I].id = -1; vowserv.vow_speaker_model[I].keyword = -1; vowserv.vow_speaker_model[I].uuid = 0; vowserv.vow_speaker_model[I].flag = 0; vowserv.vow_speaker_model[I].enabled = 0; } mutex_lock(&vow_extradata_mutex); vowserv.extradata_mem_ptr = NULL; mutex_unlock(&vow_extradata_mutex); vowserv.extradata_bytelen = 0; vowserv.voicedata_kernel_ptr = NULL; mutex_lock(&voicedata_mutex); vowserv.voicedata_idx = 0; mutex_unlock(&voicedata_mutex); init_flag = 1; vowserv.dump_pcm_flag = false; vowserv.split_dumpfile_flag = false; dump_queue = NULL; vow_pcm_dump_init(); bargein_dump_info_flag = false; vowserv.scp_vow_lch = true; #ifdef CONFIG_MTK_VOW_DUAL_MIC_SUPPORT vowserv.interleave_pcmdata_ptr = NULL; #endif //set default value to platform identifier and version memset(vowserv.google_engine_arch, 0, VOW_ENGINE_INFO_LENGTH_BYTE); if (sprintf(vowserv.google_engine_arch, "dd906fa0-d329-3961-8b53-5e2072ca1ff6") < 0) VOWDRV_DEBUG("%s(), sprintf failed\n", __func__); vowserv.google_engine_version = DEFAULT_GOOGLE_ENGINE_VER; memset(vowserv.alexa_engine_version, 0, VOW_ENGINE_INFO_LENGTH_BYTE); } else { vow_ipi_buf[0] = vowserv.voicedata_scp_addr; vow_ipi_buf[1] = vowserv.extradata_addr; vow_ipi_buf[2] = VOW_EXTRA_DATA_SIZE; ret = vow_IPICmd_Send(AUDIO_IPI_PAYLOAD, AUDIO_IPI_MSG_BYPASS_ACK, IPIMSG_VOW_APREGDATA_ADDR, sizeof(unsigned int) * 3, 0, (char *)&vow_ipi_buf[0]); if (ret == 0) { VOWDRV_DEBUG( "IPIMSG_VOW_APREGDATA_ADDR ipi send error\n"); } #if VOW_PRE_LEARN_MODE VowDrv_SetFlag(VOW_FLAG_PRE_LEARN, true); #endif } #else /* #ifdef CONFIG_MTK_TINYSYS_SCP_SUPPORT */ VOWDRV_DEBUG("%s():%x, SCP no support\n", __func__, init_flag); #endif /* #ifdef CONFIG_MTK_TINYSYS_SCP_SUPPORT */ } static int vow_service_GetParameter(unsigned long arg) { unsigned long vow_info_ap[MAX_VOW_INFO_LEN]; if (copy_from_user((void *)(&vow_info_ap[0]), (const void __user *)(arg), sizeof(vow_info_ap))) { VOWDRV_DEBUG("vow get parameter fail\n"); return -EFAULT; } if (vow_info_ap[3] > VOW_MODEL_SIZE || vow_info_ap[3] < VOW_MODEL_SIZE_THRES) { VOWDRV_DEBUG("vow Modle Size is incorrect %d\n", vow_info_ap[3]); return -EFAULT; } memcpy(vowserv.vow_info_apuser, vow_info_ap, sizeof(vow_info_ap)); VOWDRV_DEBUG( "vow get parameter: id %lu, keyword %lu, mdl_ptr 0x%x, mdl_sz %lu\n", vowserv.vow_info_apuser[0], vowserv.vow_info_apuser[1], vowserv.vow_info_apuser[2], vowserv.vow_info_apuser[3]); VOWDRV_DEBUG( "vow get parameter: return size addr 0x%x, uuid %d, data 0x%x\n", vowserv.vow_info_apuser[4], vowserv.vow_info_apuser[5], vowserv.vow_info_apuser[6]); return 0; } #ifdef CONFIG_MTK_TINYSYS_SCP_SUPPORT static int vow_service_CopyModel(int slot) { if (slot >= MAX_VOW_SPEAKER_MODEL || slot < 0) { VOWDRV_DEBUG("%s(), slot id=%d, over range\n", __func__, slot); return -EDOM; } if (copy_from_user((void *)(vowserv.vow_speaker_model[slot].model_ptr), (const void __user *)(vowserv.vow_info_apuser[2]), vowserv.vow_info_apuser[3])) { VOWDRV_DEBUG("vow Copy Speaker Model Fail\n"); return -EFAULT; } vowserv.vow_speaker_model[slot].flag = 1; vowserv.vow_speaker_model[slot].enabled = 0; vowserv.vow_speaker_model[slot].id = vowserv.vow_info_apuser[0]; vowserv.vow_speaker_model[slot].keyword = vowserv.vow_info_apuser[1]; vowserv.vow_speaker_model[slot].uuid = vowserv.vow_info_apuser[5]; vowserv.vow_speaker_model[slot].model_size = vowserv.vow_info_apuser[3]; return 0; } #endif /* #ifdef CONFIG_MTK_TINYSYS_SCP_SUPPORT */ static int vow_service_FindFreeSpeakerModel(void) { int I; I = 0; do { if (vowserv.vow_speaker_model[I].flag == 0) break; I++; } while (I < MAX_VOW_SPEAKER_MODEL); VOWDRV_DEBUG("vow FindFreeSpeakerModel:%d\n", I); if (I == MAX_VOW_SPEAKER_MODEL) { VOWDRV_DEBUG("vow Find Free Speaker Model Fail\n"); return -1; } return I; } static int vow_service_SearchSpeakerModelWithId(int id) { int I, J; I = 0; do { if (vowserv.vow_speaker_model[I].id == id) break; I++; } while (I < MAX_VOW_SPEAKER_MODEL); if (I == MAX_VOW_SPEAKER_MODEL) { VOWDRV_DEBUG("vow Search Speaker Model By ID Fail:%x\n", id); for (J = 0; J < MAX_VOW_SPEAKER_MODEL; J++) { /* print all id */ VOWDRV_DEBUG("id[%d]=%d\n", J, vowserv.vow_speaker_model[J].id); } return -1; } return I; } #ifdef CONFIG_MTK_TINYSYS_SCP_SUPPORT static int vow_service_SearchSpeakerModelWithUuid(int uuid) { int I, J; I = 0; do { if (vowserv.vow_speaker_model[I].uuid == uuid) { VOWDRV_DEBUG("vow Search Speaker Model By UUID Success!, uuid:%x\n", uuid); break; } I++; } while (I < MAX_VOW_SPEAKER_MODEL); if (I == MAX_VOW_SPEAKER_MODEL) { VOWDRV_DEBUG("vow Search Speaker Model By UUID Fail:%x\n", uuid); for (J = 0; J < MAX_VOW_SPEAKER_MODEL; J++) { /* print all uuid */ VOWDRV_DEBUG("uuid[%d]=%d\n", J, vowserv.vow_speaker_model[J].uuid); } return -1; } return I; } #endif static bool vow_service_SendSpeakerModel(int slot, bool release_flag) { bool ret = false; unsigned int vow_ipi_buf[5]; if (slot >= MAX_VOW_SPEAKER_MODEL || slot < 0) { VOWDRV_DEBUG("%s(), slot id=%d, over range\n", __func__, slot); return ret; } if (release_flag == VOW_CLEAN_MODEL) { if (vowserv.vow_speaker_model[slot].flag == 0) { VOWDRV_DEBUG("%s(), slot:%d, no model need to clean\n", __func__, slot); return ret; } vow_ipi_buf[0] = VOW_MODEL_CLEAR; } else { /* VOW_SET_MODEL */ if ((vowserv.vow_speaker_model[slot].flag == 0) || (vowserv.vow_speaker_model[slot].id == -1) || (vowserv.vow_speaker_model[slot].keyword == -1) || (vowserv.vow_speaker_model[slot].uuid == 0) || (vowserv.vow_speaker_model[slot].model_size == 0)) { VOWDRV_DEBUG("%s(), slot:%d, no model need to set\n", __func__, slot); return ret; } vow_ipi_buf[0] = VOW_MODEL_SPEAKER; } #ifdef CONFIG_MTK_TINYSYS_SCP_SUPPORT vow_ipi_buf[1] = vowserv.vow_speaker_model[slot].uuid; vow_ipi_buf[2] = scp_get_reserve_mem_phys(VOW_MEM_ID) + (VOW_MODEL_SIZE * slot); vow_ipi_buf[3] = vowserv.vow_speaker_model[slot].model_size; vow_ipi_buf[4] = vowserv.vow_speaker_model[slot].keyword; VOWDRV_DEBUG( "Model:slot_%d, model_%x, addr_%x, id_%x, size_%x, keyword_%x\n", slot, vow_ipi_buf[0], vow_ipi_buf[2], vow_ipi_buf[1], vow_ipi_buf[3], vow_ipi_buf[4]); ret = vow_IPICmd_Send(AUDIO_IPI_PAYLOAD, AUDIO_IPI_MSG_BYPASS_ACK, IPIMSG_VOW_SET_MODEL, sizeof(unsigned int) * 4, 0, (char *)&vow_ipi_buf[0]); #endif return ret; } static bool vow_service_ReleaseSpeakerModel(int id) { int I; bool ret = false; I = vow_service_SearchSpeakerModelWithId(id); if (I == -1) { VOWDRV_DEBUG("vow release Speaker Model Fail, id:%x\n", id); return false; } VOWDRV_DEBUG("vow ReleaseSpeakerModel:id_%x, slot_%d\n", id, I); ret = vow_service_SendSpeakerModel(I, VOW_CLEAN_MODEL); vowserv.vow_speaker_model[I].model_ptr = NULL; vowserv.vow_speaker_model[I].uuid = 0; vowserv.vow_speaker_model[I].id = -1; vowserv.vow_speaker_model[I].keyword = -1; vowserv.vow_speaker_model[I].flag = 0; vowserv.vow_speaker_model[I].enabled = 0; return ret; } static bool vow_service_SetSpeakerModel(unsigned long arg) { bool ret = false; int I; #ifdef CONFIG_MTK_TINYSYS_SCP_SUPPORT char *ptr8; #endif I = vow_service_FindFreeSpeakerModel(); if (I == -1) return false; if (vow_service_GetParameter(arg) != 0) return false; #ifdef CONFIG_MTK_TINYSYS_SCP_SUPPORT vowserv.vow_speaker_model[I].model_ptr = (void *)(scp_get_reserve_mem_virt(VOW_MEM_ID)) + (VOW_MODEL_SIZE * I); if (vow_service_CopyModel(I) != 0) return false; ptr8 = (char *)vowserv.vow_speaker_model[I].model_ptr; VOWDRV_DEBUG("SetSPKModel:slot: %d, ID: %x, UUID: %x, flag: %x, keyword: %x\n", I, vowserv.vow_speaker_model[I].id, vowserv.vow_speaker_model[I].uuid, vowserv.vow_speaker_model[I].flag, vowserv.vow_speaker_model[I].keyword); VOWDRV_DEBUG("vow SetSPKModel:CheckValue:%x %x %x %x %x %x\n", *(char *)&ptr8[0], *(char *)&ptr8[1], *(char *)&ptr8[2], *(char *)&ptr8[3], *(short *)&ptr8[160], *(int *)&ptr8[7960]); ret = vow_service_SendSpeakerModel(I, VOW_SET_MODEL); #else VOWDRV_DEBUG("vow:SCP no support\n\r"); #endif return ret; } static bool vow_service_SendModelStatus(int slot, bool enable) { bool ret = false; #ifdef CONFIG_MTK_TINYSYS_SCP_SUPPORT unsigned int vow_ipi_buf[2]; if (slot >= MAX_VOW_SPEAKER_MODEL || slot < 0) { VOWDRV_DEBUG("%s(), slot id=%d, over range\n", __func__, slot); return ret; } if (!vowserv.vow_speaker_model[slot].flag) { VOWDRV_DEBUG("%s(), this speaker model isn't load\n", __func__); return ret; } vow_ipi_buf[0] = vowserv.vow_speaker_model[slot].uuid; vow_ipi_buf[1] = vowserv.vow_speaker_model[slot].confidence_lv; if (enable == VOW_MODEL_STATUS_START) { ret = vow_IPICmd_Send(AUDIO_IPI_PAYLOAD, AUDIO_IPI_MSG_BYPASS_ACK, IPIMSG_VOW_MODEL_START, sizeof(unsigned int) * 2, 0, (char *)&vow_ipi_buf[0]); } else { /* VOW_MODEL_STATUS_STOP */ ret = vow_IPICmd_Send(AUDIO_IPI_PAYLOAD, AUDIO_IPI_MSG_BYPASS_ACK, IPIMSG_VOW_MODEL_STOP, sizeof(unsigned int) * 2, 0, (char *)&vow_ipi_buf[0]); } #else (void)enable; (void)slot; VOWDRV_DEBUG("vow:SCP no support\n\r"); #endif return ret; } static void vow_register_vendor_feature(int uuid) { #ifdef CONFIG_MTK_TINYSYS_SCP_SUPPORT switch (uuid) { case VENDOR_ID_MTK: vow_register_feature(VOW_VENDOR_M_FEATURE_ID); break; case VENDOR_ID_AMAZON: vow_register_feature(VOW_VENDOR_A_FEATURE_ID); break; case VENDOR_ID_OTHERS: vow_register_feature(VOW_VENDOR_G_FEATURE_ID); break; default: VOWDRV_DEBUG("VENDOR ID not support\n"); break; } #else VOWDRV_DEBUG("vow:SCP no support\n\r"); #endif } static void vow_deregister_vendor_feature(int uuid) { #ifdef CONFIG_MTK_TINYSYS_SCP_SUPPORT switch (uuid) { case VENDOR_ID_MTK: vow_deregister_feature(VOW_VENDOR_M_FEATURE_ID); break; case VENDOR_ID_AMAZON: vow_deregister_feature(VOW_VENDOR_A_FEATURE_ID); break; case VENDOR_ID_OTHERS: vow_deregister_feature(VOW_VENDOR_G_FEATURE_ID); break; default: VOWDRV_DEBUG("VENDOR ID not support\n"); break; } #else VOWDRV_DEBUG("vow:SCP no support\n\r"); #endif } static bool vow_service_SetModelStatus(bool enable, unsigned long arg) { bool ret = false; int slot; struct vow_model_start_t model_start; if (copy_from_user((void *)&model_start, (const void __user *)(arg), sizeof(struct vow_model_start_t))) { VOWDRV_DEBUG("vow get vow_model_start data fail\n"); return false; } if (model_start.handle > INT_MAX || model_start.handle < INT_MIN) { VOWDRV_DEBUG("%s(), model_start.handle will cause truncated value\n", __func__); return false; } slot = vow_service_SearchSpeakerModelWithId(model_start.handle); if (slot < 0) { VOWDRV_DEBUG("%s(), no match id\n", __func__); return false; } if (enable == VOW_MODEL_STATUS_START) { if (vowserv.vow_speaker_model[slot].enabled == 0) { int uuid; uuid = vowserv.vow_speaker_model[slot].uuid; vow_register_vendor_feature(uuid); } vowserv.vow_speaker_model[slot].enabled = 1; vowserv.vow_speaker_model[slot].confidence_lv = model_start.confidence_level; vowserv.vow_speaker_model[slot].rx_inform_addr = model_start.dsp_inform_addr; vowserv.vow_speaker_model[slot].rx_inform_size_addr = model_start.dsp_inform_size_addr; VOWDRV_DEBUG("VOW_MODEL_START, id:%d\n", (int)model_start.handle); /* send model status to scp */ ret = vow_service_SendModelStatus(slot, enable); if (ret == false) VOWDRV_DEBUG("vow_service_SendModelStatus, err\n"); } else { /* VOW_MODEL_STATUS_STOP */ vowserv.vow_speaker_model[slot].confidence_lv = model_start.confidence_level; vowserv.vow_speaker_model[slot].rx_inform_addr = model_start.dsp_inform_addr; vowserv.vow_speaker_model[slot].rx_inform_size_addr = model_start.dsp_inform_size_addr; /* send model status to scp */ ret = vow_service_SendModelStatus(slot, enable); if (ret == false) VOWDRV_DEBUG("vow_service_SendModelStatus, err\n"); if (vowserv.vow_speaker_model[slot].enabled == 1) { int uuid; uuid = vowserv.vow_speaker_model[slot].uuid; vow_deregister_vendor_feature(uuid); } vowserv.vow_speaker_model[slot].enabled = 0; VOWDRV_DEBUG("VOW_MODEL_STOP, id:%d\n", (int)model_start.handle); } return ret; } static bool vow_service_SetApAddr(unsigned long arg) { unsigned long vow_info[MAX_VOW_INFO_LEN]; if (copy_from_user((void *)(&vow_info[0]), (const void __user *)(arg), sizeof(vow_info))) { VOWDRV_DEBUG("vow check parameter fail\n"); return false; } /* add return condition */ if ((vow_info[2] == 0) || (vow_info[3] != VOW_VBUF_LENGTH) || (vow_info[4] == 0)) { VOWDRV_DEBUG("vow SetVBufAddr:addr_%x, size_%x, addr_%x\n", (unsigned int)vow_info[2], (unsigned int)vow_info[3], (unsigned int)vow_info[4]); return false; } vowserv.voicedata_user_addr = vow_info[2]; vowserv.voicedata_user_size = vow_info[3]; vowserv.voicedata_user_return_size_addr = vow_info[4]; return true; } static bool vow_service_SetVBufAddr(unsigned long arg) { unsigned long vow_info[MAX_VOW_INFO_LEN]; if (copy_from_user((void *)(&vow_info[0]), (const void __user *)(arg), sizeof(vowserv.vow_info_apuser))) { VOWDRV_DEBUG("vow check parameter fail\n"); return false; } /* add return condition */ if ((vow_info[2] == 0) || (vow_info[3] != VOW_VBUF_LENGTH) || (vow_info[4] == 0)) { VOWDRV_DEBUG("vow SetVBufAddr:addr_%x, size_%x, addr_%x\n", (unsigned int)vow_info[2], (unsigned int)vow_info[3], (unsigned int)vow_info[4]); return false; } mutex_lock(&vow_vmalloc_lock); if (vowserv.voicedata_kernel_ptr != NULL) { vfree(vowserv.voicedata_kernel_ptr); vowserv.voicedata_kernel_ptr = NULL; } if (vow_info[3] != VOW_VBUF_LENGTH) { mutex_unlock(&vow_vmalloc_lock); return false; } vowserv.voicedata_kernel_ptr = vmalloc(VOW_VBUF_LENGTH); mutex_unlock(&vow_vmalloc_lock); return true; } static bool vow_service_Enable(void) { bool ret = false; VOWDRV_DEBUG("+%s()\n", __func__); /* extra data memory locate */ mutex_lock(&vow_extradata_mutex); if (vowserv.extradata_mem_ptr == NULL) { vowserv.extradata_mem_ptr = vmalloc(VOW_EXTRA_DATA_SIZE); } mutex_unlock(&vow_extradata_mutex); #ifdef CONFIG_MTK_TINYSYS_SCP_SUPPORT ret = vow_IPICmd_Send(AUDIO_IPI_MSG_ONLY, AUDIO_IPI_MSG_BYPASS_ACK, IPIMSG_VOW_ENABLE, 0, 0, NULL); #else VOWDRV_DEBUG("vow:SCP no support\n\r"); #endif VOWDRV_DEBUG("-%s():%d\n", __func__, ret); return ret; } static bool vow_service_Disable(void) { bool ret = false; VOWDRV_DEBUG("+%s()\n", __func__); #ifdef CONFIG_MTK_TINYSYS_SCP_SUPPORT ret = vow_IPICmd_Send(AUDIO_IPI_MSG_ONLY, AUDIO_IPI_MSG_BYPASS_ACK, IPIMSG_VOW_DISABLE, 0, 0, NULL); #else VOWDRV_DEBUG("vow:SCP no support\n\r"); #endif /* extra data memory release */ mutex_lock(&vow_extradata_mutex); if (vowserv.extradata_mem_ptr != NULL) { vfree(vowserv.extradata_mem_ptr); vowserv.extradata_mem_ptr = NULL; } mutex_unlock(&vow_extradata_mutex); /* release lock */ if (vowserv.suspend_lock == 1) { vowserv.suspend_lock = 0; /* Let AP will suspend */ __pm_relax(vow_suspend_lock); } #ifdef CONFIG_MTK_TINYSYS_SCP_SUPPORT vow_deregister_feature(VOW_FEATURE_ID); #else VOWDRV_DEBUG("vow:SCP no support\n\r"); #endif return ret; } static void vow_check_boundary(unsigned int copy_len, unsigned int bound_len) { if (copy_len > bound_len) { VOWDRV_DEBUG("[vow check]copy_len=0x%x, bound_len=0x%x\n", copy_len, bound_len); VOW_ASSERT(0); } } #ifdef CONFIG_MTK_VOW_DUAL_MIC_SUPPORT static void vow_interleaving(short *out_buf, short *l_sample, short *r_sample, unsigned int buf_length) { int i; int smpl_max; smpl_max = buf_length / 2; for (i = 0; i < smpl_max; i++) { *out_buf++ = *l_sample++; *out_buf++ = *r_sample++; } } #endif static int vow_service_ReadVoiceData_Internal(unsigned int buf_offset, unsigned int buf_length) { int stop_condition = 0; if (buf_length != 0) { if ((vowserv.voicedata_idx + (buf_length >> 1)) > (vowserv.voicedata_user_size >> 1)) { VOWDRV_DEBUG( "[vow check]data_idx=0x%x(W),buf_length=0x%x(B)\n", vowserv.voicedata_idx, buf_length); VOWDRV_DEBUG( "[vow check] user_size=0x%x(B), voice_buf_offset=0x%x(B)\n", (unsigned int)vowserv.voicedata_user_size, buf_offset); /* VOW_ASSERT(0); */ mutex_lock(&voicedata_mutex); vowserv.voicedata_idx = 0; mutex_unlock(&voicedata_mutex); } mutex_lock(&vow_vmalloc_lock); #ifdef CONFIG_MTK_VOW_DUAL_MIC_SUPPORT /* start interleaving L+R */ vow_interleaving( &vowserv.voicedata_kernel_ptr[vowserv.voicedata_idx], (short *)(vowserv.voicddata_scp_ptr + buf_offset), (short *)(vowserv.voicddata_scp_ptr + buf_offset + VOW_VOICEDATA_SIZE), buf_length); /* end interleaving*/ #else /* #ifdef CONFIG_MTK_VOW_DUAL_MIC_SUPPORT */ memcpy(&vowserv.voicedata_kernel_ptr[vowserv.voicedata_idx], vowserv.voicddata_scp_ptr + buf_offset, buf_length); #endif /* #ifdef CONFIG_MTK_VOW_DUAL_MIC_SUPPORT */ mutex_unlock(&vow_vmalloc_lock); if (buf_length > VOW_VOICE_RECORD_BIG_THRESHOLD) { /* means now is start to transfer */ /* keyword buffer(64kB) to AP */ VOWDRV_DEBUG("%s(), start tx keyword, 0x%x/0x%x\n", __func__, vowserv.voicedata_idx, buf_length); vowserv.tx_keyword_start = true; } mutex_lock(&voicedata_mutex); #ifdef CONFIG_MTK_VOW_DUAL_MIC_SUPPORT /* 2 Channels */ vowserv.voicedata_idx += buf_length; #else /* 1 Channel */ vowserv.voicedata_idx += (buf_length >> 1); #endif mutex_unlock(&voicedata_mutex); } if (vowserv.voicedata_idx >= (VOW_VOICE_RECORD_BIG_THRESHOLD >> 1)) vowserv.transfer_length = VOW_VOICE_RECORD_BIG_THRESHOLD; else vowserv.transfer_length = VOW_VOICE_RECORD_THRESHOLD; if (vowserv.voicedata_idx >= (vowserv.transfer_length >> 1)) { unsigned int ret; /*VOWDRV_DEBUG("TX Leng:%d, %d, [%d]\n",*/ /* vowserv.voicedata_idx,*/ /* buf_length,*/ /* vowserv.transfer_length);*/ ret = copy_to_user( (void __user *)(vowserv.voicedata_user_return_size_addr), &vowserv.transfer_length, sizeof(unsigned int)); mutex_lock(&vow_vmalloc_lock); ret = copy_to_user( (void __user *)vowserv.voicedata_user_addr, vowserv.voicedata_kernel_ptr, vowserv.transfer_length); mutex_unlock(&vow_vmalloc_lock); /* move left data to buffer's head */ if (vowserv.voicedata_idx > (vowserv.transfer_length >> 1)) { unsigned int tmp; unsigned int idx; tmp = (vowserv.voicedata_idx << 1) - vowserv.transfer_length; vow_check_boundary(tmp, vowserv.voicedata_user_size); idx = (vowserv.transfer_length >> 1); mutex_lock(&vow_vmalloc_lock); memcpy(&vowserv.voicedata_kernel_ptr[0], &vowserv.voicedata_kernel_ptr[idx], tmp); mutex_unlock(&vow_vmalloc_lock); mutex_lock(&voicedata_mutex); vowserv.voicedata_idx -= idx; mutex_unlock(&voicedata_mutex); } else { mutex_lock(&voicedata_mutex); vowserv.voicedata_idx = 0; mutex_unlock(&voicedata_mutex); } /* speed up voicedata transfer to hal */ if (vowserv.voicedata_idx >= VOW_VOICE_RECORD_THRESHOLD) vow_service_getVoiceData(); stop_condition = 1; } if ((vowserv.tx_keyword_start == true) && (vowserv.voicedata_idx < VOW_VOICE_RECORD_THRESHOLD)) { /* means now is end of transfer keyword buffer(64kB) to AP */ vowserv.tx_keyword_start = false; VOWDRV_DEBUG("%s(), end tx keyword, 0x%x\n", __func__, vowserv.voicedata_idx); } return stop_condition; } static void vow_service_ReadVoiceData(void) { int stop_condition = 0; /*int rdata;*/ while (1) { if (VoiceData_Wait_Queue_flag == 0) wait_event_interruptible_timeout(VoiceData_Wait_Queue, VoiceData_Wait_Queue_flag, msecs_to_jiffies(50)); if (VoiceData_Wait_Queue_flag == 1) { VoiceData_Wait_Queue_flag = 0; if ((VowDrv_GetHWStatus() == VOW_PWR_OFF) || (vowserv.recording_flag == false)) { mutex_lock(&voicedata_mutex); vowserv.voicedata_idx = 0; mutex_unlock(&voicedata_mutex); stop_condition = 1; VOWDRV_DEBUG( "stop read vow voice data: %d, %d\n", VowDrv_GetHWStatus(), vowserv.recording_flag); } else { /* To Read Voice Data from Kernel to User */ stop_condition = vow_service_ReadVoiceData_Internal( vowserv.voice_buf_offset, vowserv.voice_length); vowserv.voice_buf_offset = 0; vowserv.voice_length = 0; } if (stop_condition == 1) break; } else { VOWDRV_DEBUG("%s, 50ms timeout,break\n", __func__); break; } } } static void vow_service_reset(void) { int I; bool need_disable_vow = false; bool ret = false; VOWDRV_DEBUG("+%s()\n", __func__); for (I = 0; I < MAX_VOW_SPEAKER_MODEL; I++) { if (vowserv.vow_speaker_model[I].enabled == 1) { int uuid; /* need to close it */ ret = vow_service_SendModelStatus( I, VOW_MODEL_STATUS_STOP); if (ret == false) VOWDRV_DEBUG("%s(), err\n", __func__); uuid = vowserv.vow_speaker_model[I].uuid; vow_deregister_vendor_feature(uuid); vowserv.vow_speaker_model[I].enabled = 0; need_disable_vow = true; } if (vowserv.vow_speaker_model[I].flag == 1) { int id; /* need to clear model */ id = vowserv.vow_speaker_model[I].id; vow_service_ReleaseSpeakerModel(id); } } if (need_disable_vow) vow_service_Disable(); VOWDRV_DEBUG("-%s()\n", __func__); } /***************************************************************************** * VOW DUMP FUNCTIONS need CONFIG_MTK_TINYSYS_SCP_SUPPORT *****************************************************************************/ #ifdef CONFIG_MTK_TINYSYS_SCP_SUPPORT static bool vow_stop_dump_wait(void) { int timeout = 0; while (1) { msleep(VOW_WAITCHECK_INTERVAL_MS); if (timeout++ >= VOW_STOP_DUMP_WAIT) return false; } return true; } static int vow_pcm_dump_notify(bool enable) { unsigned int vow_ipi_buf[5] = {0}; bool ret; /* if scp reset happened, need re-send PCM dump IPI to SCP again */ if (enable == true) { /* dump flag */ vow_ipi_buf[1] = vowserv.dump_pcm_flag; /* TOTAL dram resrved size for barge in dump */ vow_ipi_buf[0] = bargein_resv_dram.size; /* address for SCP using */ vow_ipi_buf[2] = bargein_resv_dram.phy_addr; VOWDRV_DEBUG( "[BargeIn]dump on, dump flag:%d, resv sz:0x%x, addr:0x%x\n", vow_ipi_buf[1], vow_ipi_buf[0], vow_ipi_buf[2]); /* TOTAL dram resrved size for recog data dump */ vow_ipi_buf[3] = recog_resv_dram.size; /* address for SCP using */ vow_ipi_buf[4] = recog_resv_dram.phy_addr; VOWDRV_DEBUG( "[Recog]dump on, dump flag:%d, resv sz:0x%x, addr:0x%x\n", vow_ipi_buf[1], vow_ipi_buf[3], vow_ipi_buf[4]); ret = vow_IPICmd_Send(AUDIO_IPI_PAYLOAD, AUDIO_IPI_MSG_BYPASS_ACK, IPIMSG_VOW_PCM_DUMP_ON, sizeof(unsigned int) * 5, 0, (char *)&vow_ipi_buf[0]); if (ret == 0) VOWDRV_DEBUG("PCM_DUMP_ON ipi send error\n"); } else { /* dump flag */ vow_ipi_buf[1] = vowserv.dump_pcm_flag; /* TOTAL dram resrved size for barge in dump */ vow_ipi_buf[0] = bargein_resv_dram.size; /* address for SCP using */ vow_ipi_buf[2] = bargein_resv_dram.phy_addr; VOWDRV_DEBUG( "[BargeIn]dump off, dump flag:%d, resv sz:0x%x, addr:0x%x\n", vow_ipi_buf[1], vow_ipi_buf[0], vow_ipi_buf[2]); /* TOTAL dram resrved size for recog data dump */ vow_ipi_buf[3] = recog_resv_dram.size; /* address for SCP using */ vow_ipi_buf[4] = recog_resv_dram.phy_addr; VOWDRV_DEBUG( "[Recog]dump off, dump flag:%d, resv sz:0x%x, addr:0x%x\n", vow_ipi_buf[1], vow_ipi_buf[3], vow_ipi_buf[4]); ret = vow_IPICmd_Send(AUDIO_IPI_PAYLOAD, AUDIO_IPI_MSG_BYPASS_ACK, IPIMSG_VOW_PCM_DUMP_OFF, sizeof(unsigned int) * 5, 0, (char *)&vow_ipi_buf[0]); if (ret == 0) VOWDRV_DEBUG("PCM_DUMP_OFF ipi send error\n"); } return 0; } static int vow_pcm_dump_set(bool enable) { VOWDRV_DEBUG("%s = %d, %d\n", __func__, vowserv.dump_pcm_flag, (unsigned int)enable); bargein_resv_dram.vir_addr = (char *)(scp_get_reserve_mem_virt(VOW_BARGEIN_MEM_ID)) + VOW_BARGEIN_DUMP_OFFSET; bargein_resv_dram.phy_addr = scp_get_reserve_mem_phys(VOW_BARGEIN_MEM_ID) + VOW_BARGEIN_DUMP_OFFSET; bargein_resv_dram.size = VOW_BARGEIN_DUMP_SIZE; VOWDRV_DEBUG("[Barge]vir: %p, phys: 0x%x\n", bargein_resv_dram.vir_addr, (unsigned int)bargein_resv_dram.phy_addr); recog_resv_dram.vir_addr = (char *)(scp_get_reserve_mem_virt(VOW_MEM_ID)) + VOW_RECOGDATA_OFFSET; recog_resv_dram.phy_addr = scp_get_reserve_mem_phys(VOW_MEM_ID) + VOW_RECOGDATA_OFFSET; recog_resv_dram.size = VOW_RECOGDATA_SIZE; VOWDRV_DEBUG("[Recog]vir: %p, phys: 0x%x\n", recog_resv_dram.vir_addr, (unsigned int)recog_resv_dram.phy_addr); if ((vowserv.dump_pcm_flag == false) && (enable == true)) { vowserv.dump_pcm_flag = true; vow_service_OpenDumpFile(); vow_pcm_dump_notify(true); } else if ((vowserv.dump_pcm_flag == true) && (enable == false)) { vowserv.dump_pcm_flag = false; vow_pcm_dump_notify(false); vow_stop_dump_wait(); vow_service_CloseDumpFile(); } return 0; } static void vow_service_OpenDumpFile(void) { VOWDRV_DEBUG("+%s() %d\n", __func__, b_enable_dump); /* only enable when debug pcm dump on */ __pm_stay_awake(pcm_dump_wake_lock); vow_service_OpenDumpFile_internal(); if (dump_queue == NULL) { dump_queue = kmalloc(sizeof(struct dump_queue_t), GFP_KERNEL); if (dump_queue != NULL) memset_io(dump_queue, 0, sizeof(struct dump_queue_t)); } if (!pcm_dump_task) { pcm_dump_task = kthread_create(vow_pcm_dump_kthread, NULL, "vow_pcm_dump_kthread"); if (IS_ERR(pcm_dump_task)) VOWDRV_DEBUG("can not create pcm dump kthread\n"); b_enable_dump = true; wake_up_process(pcm_dump_task); } bargein_dump_data_routine_cnt_pass = 0; vowserv.bargein_dump_cnt1 = 0; vowserv.bargein_dump_cnt2 = 0; recog_dump_data_routine_cnt_pass = 0; vowserv.recog_dump_cnt1 = 0; vowserv.recog_dump_cnt2 = 0; VOWDRV_DEBUG("-%s() %d\n", __func__, b_enable_dump); } static void vow_service_CloseDumpFile(void) { VOWDRV_DEBUG("+%s() %d\n", __func__, b_enable_dump); if (b_enable_dump == false) return; b_enable_dump = false; if (pcm_dump_task) { kthread_stop(pcm_dump_task); pcm_dump_task = NULL; } VOWDRV_DEBUG("[Recog] dump_queue = %p\n", dump_queue); VOWDRV_DEBUG("[BargeIn] bargein_pass: %d\n", bargein_dump_data_routine_cnt_pass); VOWDRV_DEBUG("[BargeIn] bargein dump cnt %d %d\n", vowserv.bargein_dump_cnt1, vowserv.bargein_dump_cnt2); VOWDRV_DEBUG("[Recog] recog_pass: %d\n", recog_dump_data_routine_cnt_pass); VOWDRV_DEBUG("[Recog] recog dump cnt %d %d\n", vowserv.recog_dump_cnt1, vowserv.recog_dump_cnt2); if (dump_queue != NULL) { kfree(dump_queue); dump_queue = NULL; } vow_service_CloseDumpFile_internal(); __pm_relax(pcm_dump_wake_lock); VOWDRV_DEBUG("-%s() %d\n", __func__, b_enable_dump); } static void vow_service_OpenDumpFile_internal(void) { struct timespec curr_tm; char string_time[16]; char string_input_pcm[16] = "input_pcm.pcm"; char string_echo_pcm[16] = "echo_ref.pcm"; char string_delay_info[16] = "delay_info"; char path_input_pcm[64]; char path_echo_ref[64]; char path_delay_info[64]; char string_recog[16] = "recog.pcm"; char path_recog[64]; VOWDRV_DEBUG("+%s()\n", __func__); memset(&curr_tm, 0, sizeof(struct timespec)); getnstimeofday(&curr_tm); memset(string_time, '\0', 16); if (sprintf(string_time, "%.2lu_%.2lu_%.2lu_%.3lu", (8 + (curr_tm.tv_sec / 3600)) % (24), (curr_tm.tv_sec / 60) % (60), (curr_tm.tv_sec % 60), (curr_tm.tv_nsec % 1000)) < 0) { VOWDRV_DEBUG("%s(), sprintf failed\n", __func__); } if (sprintf(path_input_pcm, "%s/%s_%s", DUMP_PCM_DATA_PATH, string_time, string_input_pcm) < 0) { VOWDRV_DEBUG("%s(), sprintf failed\n", __func__); } VOWDRV_DEBUG("[BargeIn] %s path_input_pcm= %s\n", __func__, path_input_pcm); if (sprintf(path_echo_ref, "%s/%s_%s", DUMP_PCM_DATA_PATH, string_time, string_echo_pcm) < 0) { VOWDRV_DEBUG("%s(), sprintf failed\n", __func__); } VOWDRV_DEBUG("[BargeIn] %s path_input_pcm= %s\n", __func__, path_echo_ref); if (sprintf(path_delay_info, "%s/%s_%s", DUMP_PCM_DATA_PATH, string_time, string_delay_info) < 0) { VOWDRV_DEBUG("%s(), sprintf failed\n", __func__); } VOWDRV_DEBUG("[BargeIn] %s path_input_pcm= %s\n", __func__, path_delay_info); if (sprintf(path_recog, "%s/%s_%s", DUMP_PCM_DATA_PATH, string_time, string_recog) < 0) { VOWDRV_DEBUG("%s(), sprintf failed\n", __func__); } VOWDRV_DEBUG("[Recog] %s path_recog= %s\n", __func__, path_recog); file_recog_data = NULL; file_recog_data_open = false; file_bargein_pcm_input = NULL; file_bargein_pcm_input_open = false; file_bargein_echo_ref = NULL; file_bargein_echo_ref_open = false; file_bargein_delay_info = NULL; file_bargein_delay_info_open = false; file_bargein_pcm_input = filp_open(path_input_pcm, O_CREAT | O_WRONLY | O_LARGEFILE, 0); if (IS_ERR(file_bargein_pcm_input)) { VOWDRV_DEBUG("[BargeIn] pcm_input:%d, path_input_pcm=%s\n", (int)PTR_ERR(file_bargein_pcm_input), path_input_pcm); return; } file_bargein_pcm_input_open = true; file_bargein_echo_ref = filp_open(path_echo_ref, O_CREAT | O_WRONLY | O_LARGEFILE, 0); if (IS_ERR(file_bargein_echo_ref)) { VOWDRV_DEBUG("[BargeIn] echo_ref:%d, path_echo_ref=%s\n", (int)PTR_ERR(file_bargein_echo_ref), path_echo_ref); return; } file_bargein_echo_ref_open = true; file_bargein_delay_info = filp_open(path_delay_info, O_CREAT | O_WRONLY | O_LARGEFILE, 0); if (IS_ERR(file_bargein_delay_info)) { VOWDRV_DEBUG( "[BargeIn] file_bargein_delay_info:%d, path_delay_info = %s\n", (int)PTR_ERR(file_bargein_delay_info), path_delay_info); return; } file_bargein_delay_info_open = true; file_recog_data = filp_open(path_recog, O_CREAT | O_WRONLY | O_LARGEFILE, 0); if (IS_ERR(file_recog_data)) { VOWDRV_DEBUG( "[BargeIn] file_recog_data:%d, path_recog = %s\n", (int)PTR_ERR(file_recog_data), path_recog); return; } file_recog_data_open = true; VOWDRV_DEBUG("-%s()\n", __func__); } static void vow_service_CloseDumpFile_internal(void) { VOWDRV_DEBUG("+%s()\n", __func__); if (file_bargein_pcm_input_open) { file_bargein_pcm_input_open = false; if (!IS_ERR(file_bargein_pcm_input)) { filp_close(file_bargein_pcm_input, NULL); file_bargein_pcm_input = NULL; } } if (file_bargein_echo_ref_open) { file_bargein_echo_ref_open = false; if (!IS_ERR(file_bargein_echo_ref)) { filp_close(file_bargein_echo_ref, NULL); file_bargein_echo_ref = NULL; } } if (file_bargein_delay_info_open) { file_bargein_delay_info_open = false; if (!IS_ERR(file_bargein_delay_info)) { filp_close(file_bargein_delay_info, NULL); file_bargein_delay_info = NULL; } } if (file_recog_data_open) { file_recog_data_open = false; if (!IS_ERR(file_recog_data)) { filp_close(file_recog_data, NULL); file_recog_data = NULL; } } VOWDRV_DEBUG("-%s()\n", __func__); } static int vow_pcm_dump_kthread(void *data) { int ret = 0; int size = 0, writedata = 0; uint8_t current_idx = 0; struct pcm_dump_t *pcm_dump = NULL; struct dump_package_t *dump_package = NULL; mm_segment_t old_fs; struct sched_param param = {.sched_priority = 85 }; sched_setscheduler(current, SCHED_RR, ¶m); /* VOWDRV_DEBUG("[BargeIn] dump_queue = %p\n", dump_queue); */ while (b_enable_dump && !kthread_should_stop()) { spin_lock(&vowdrv_lock); if (dump_queue->idx_r != dump_queue->idx_w) { current_idx = dump_queue->idx_r; dump_queue->idx_r++; spin_unlock(&vowdrv_lock); } else { spin_unlock(&vowdrv_lock); ret = wait_event_interruptible( wq_dump_pcm, (dump_queue->idx_r != dump_queue->idx_w) || (b_enable_dump == false)); if (ret == -ERESTARTSYS) { ret = -EINTR; break; } if (b_enable_dump == false) break; current_idx = dump_queue->idx_r; dump_queue->idx_r++; } if (vowserv.split_dumpfile_flag) { vow_service_CloseDumpFile_internal(); vow_service_OpenDumpFile_internal(); VOWDRV_DEBUG("Split audio dump done\n"); vowserv.split_dumpfile_flag = false; } dump_package = &dump_queue->dump_package[current_idx]; /* VOWDRV_DEBUG("[BargeIn] current_idx = %d\n", current_idx); */ switch (dump_package->dump_data_type) { case DUMP_BARGEIN: { #ifdef CONFIG_MTK_VOW_DUAL_MIC_SUPPORT short *out_buf; /* DRAM to kernel buffer and sample interleaving */ vow_interleaving( vowserv.interleave_pcmdata_ptr, (short *)(bargein_resv_dram.vir_addr + dump_package->mic_offset), (short *)(bargein_resv_dram.vir_addr + dump_package->mic_offset_R), dump_package->mic_data_size); size = (dump_package->mic_data_size) * 2; writedata = size; out_buf = vowserv.interleave_pcmdata_ptr; if (size <= 0) VOWDRV_DEBUG("[VOW]dump size error %d\n"); while (size > 0) { if (file_bargein_pcm_input_open && !IS_ERR(file_bargein_pcm_input)) { old_fs = get_fs(); set_fs(KERNEL_DS); ret = vfs_write(file_bargein_pcm_input, (char __user *)out_buf, writedata, &file_bargein_pcm_input->f_pos); set_fs(old_fs); if (ret < 0) { VOWDRV_DEBUG( "[Bargein]vfs write failed\n"); } } size -= writedata; pcm_dump++; } #else /* #ifdef CONFIG_MTK_VOW_DUAL_MIC_SUPPORT */ /* Bargein dump Mic input data */ size = dump_package->mic_data_size; writedata = size; pcm_dump = (struct pcm_dump_t *) (bargein_resv_dram.vir_addr + dump_package->mic_offset); if (size <= 0) VOWDRV_DEBUG("[VOW]dump size error %d\n"); while (size > 0) { if (file_bargein_pcm_input_open && !IS_ERR(file_bargein_pcm_input)) { old_fs = get_fs(); set_fs(KERNEL_DS); ret = vfs_write(file_bargein_pcm_input, (char __user *)pcm_dump->decode_pcm, writedata, &file_bargein_pcm_input->f_pos); set_fs(old_fs); if (ret < 0) { VOWDRV_DEBUG( "[Bargein]vfs write failed\n"); } } size -= writedata; pcm_dump++; } #endif /* #ifdef CONFIG_MTK_VOW_DUAL_MIC_SUPPORT */ /* Bargein dump echo data */ size = dump_package->echo_data_size; writedata = size; pcm_dump = (struct pcm_dump_t *) (bargein_resv_dram.vir_addr + dump_package->echo_offset); vowserv.bargein_dump_cnt2++; if (size <= 0) VOWDRV_DEBUG("[VOW]dump size error %d\n"); while (size > 0) { if (file_bargein_echo_ref_open && !IS_ERR(file_bargein_echo_ref)) { old_fs = get_fs(); set_fs(KERNEL_DS); ret = vfs_write(file_bargein_echo_ref, (char __user *)pcm_dump->decode_pcm, writedata, &file_bargein_echo_ref->f_pos); set_fs(old_fs); if (ret < 0) { VOWDRV_DEBUG( "[Bargein]vfs write failed\n"); } } size -= writedata; pcm_dump++; } } if (bargein_dump_info_flag) { if (file_bargein_delay_info_open && !IS_ERR(file_bargein_delay_info)) { uint32_t *ptr32; old_fs = get_fs(); set_fs(KERNEL_DS); ptr32 = &vowserv.dump_frm_cnt; ret = vfs_write(file_bargein_delay_info, (char __user *)ptr32, sizeof(uint32_t), &file_bargein_delay_info->f_pos); if (ret < 0) VOWDRV_DEBUG("vfs write failed\n"); ptr32 = &vowserv.voice_sample_delay; ret = vfs_write(file_bargein_delay_info, (char __user *)ptr32, sizeof(uint32_t), &file_bargein_delay_info->f_pos); if (ret < 0) VOWDRV_DEBUG("vfs write failed\n"); set_fs(old_fs); bargein_dump_info_flag = false; } } break; case DUMP_RECOG: { #ifdef CONFIG_MTK_VOW_DUAL_MIC_SUPPORT short *out_buf; /* DRAM to kernel buffer and sample interleaving */ vow_interleaving( vowserv.interleave_pcmdata_ptr, (short *)(recog_resv_dram.vir_addr + dump_package->recog_data_offset), (short *)(recog_resv_dram.vir_addr + dump_package->recog_data_offset_R), dump_package->recog_data_size); size = (dump_package->recog_data_size) * 2; writedata = size; out_buf = vowserv.interleave_pcmdata_ptr; if (size <= 0) VOWDRV_DEBUG("[VOW]dump size error %d\n"); while (size > 0) { if (file_recog_data_open && !IS_ERR(file_recog_data)) { old_fs = get_fs(); set_fs(KERNEL_DS); ret = vfs_write(file_recog_data, (char __user *)out_buf, writedata, &file_recog_data->f_pos); set_fs(old_fs); if (ret < 0) { VOWDRV_DEBUG( "[Recog]vfs write failed\n"); } } size -= writedata; pcm_dump++; } #else /* #ifdef CONFIG_MTK_VOW_DUAL_MIC_SUPPORT */ /* Recog dump data */ size = dump_package->recog_data_size; writedata = size; pcm_dump = (struct pcm_dump_t *) (recog_resv_dram.vir_addr + dump_package->recog_data_offset); if (size <= 0) VOWDRV_DEBUG("[VOW]dump size error %d\n"); while (size > 0) { if (file_recog_data_open && !IS_ERR(file_recog_data)) { old_fs = get_fs(); set_fs(KERNEL_DS); ret = vfs_write(file_recog_data, (char __user *)pcm_dump->decode_pcm, writedata, &file_recog_data->f_pos); set_fs(old_fs); if (ret < 0) { VOWDRV_DEBUG( "[Recog]vfs write failed\n"); } } size -= writedata; pcm_dump++; } #endif /* #ifdef CONFIG_MTK_VOW_DUAL_MIC_SUPPORT */ } break; default: break; } } VOWDRV_DEBUG("%s, exit\n", __func__); return 0; } static void recog_dump_routine(struct work_struct *ws) { struct dump_work_t *dump_work = NULL; uint32_t offset = 0; uint32_t data_size = 0; #ifdef CONFIG_MTK_VOW_DUAL_MIC_SUPPORT uint32_t offset_R = 0; uint32_t data_size_R = 0; #endif /* #ifdef CONFIG_MTK_VOW_DUAL_MIC_SUPPORT */ dump_work = container_of(ws, struct dump_work_t, work); offset = dump_work->recog_data_offset; data_size = dump_work->recog_data_size; #ifdef CONFIG_MTK_VOW_DUAL_MIC_SUPPORT offset_R = dump_work->recog_data_offset_R; data_size_R = dump_work->recog_data_size_R; #endif /* #ifdef CONFIG_MTK_VOW_DUAL_MIC_SUPPORT */ spin_lock(&vowdrv_lock); dump_queue->dump_package[dump_queue->idx_w].dump_data_type = DUMP_RECOG; dump_queue->dump_package[dump_queue->idx_w].recog_data_offset = offset; dump_queue->dump_package[dump_queue->idx_w].recog_data_size = data_size; #ifdef CONFIG_MTK_VOW_DUAL_MIC_SUPPORT dump_queue->dump_package[dump_queue->idx_w].recog_data_offset_R = offset_R; dump_queue->dump_package[dump_queue->idx_w].recog_data_size_R = data_size_R; #endif /* #ifdef CONFIG_MTK_VOW_DUAL_MIC_SUPPORT */ dump_queue->idx_w++; spin_unlock(&vowdrv_lock); vowserv.recog_dump_cnt1++; wake_up_interruptible(&wq_dump_pcm); } static void bargein_dump_routine(struct work_struct *ws) { struct dump_work_t *dump_work = NULL; uint32_t mic_offset = 0; uint32_t mic_data_size = 0; #ifdef CONFIG_MTK_VOW_DUAL_MIC_SUPPORT uint32_t mic_offset_R = 0; uint32_t mic_data_size_R = 0; #endif /* #ifdef CONFIG_MTK_VOW_DUAL_MIC_SUPPORT */ uint32_t echo_offset = 0; uint32_t echo_data_size = 0; dump_work = container_of(ws, struct dump_work_t, work); mic_offset = dump_work->mic_offset; mic_data_size = dump_work->mic_data_size; #ifdef CONFIG_MTK_VOW_DUAL_MIC_SUPPORT mic_offset_R = dump_work->mic_offset_R; mic_data_size_R = dump_work->mic_data_size_R; #endif /* #ifdef CONFIG_MTK_VOW_DUAL_MIC_SUPPORT */ echo_offset = dump_work->echo_offset; echo_data_size = dump_work->echo_data_size; spin_lock(&vowdrv_lock); dump_queue->dump_package[dump_queue->idx_w].dump_data_type = DUMP_BARGEIN; dump_queue->dump_package[dump_queue->idx_w].mic_offset = mic_offset; dump_queue->dump_package[dump_queue->idx_w].mic_data_size = mic_data_size; #ifdef CONFIG_MTK_VOW_DUAL_MIC_SUPPORT dump_queue->dump_package[dump_queue->idx_w].mic_offset_R = mic_offset_R; dump_queue->dump_package[dump_queue->idx_w].mic_data_size_R = mic_data_size_R; #endif /* #ifdef CONFIG_MTK_VOW_DUAL_MIC_SUPPORT */ dump_queue->dump_package[dump_queue->idx_w].echo_offset = echo_offset; dump_queue->dump_package[dump_queue->idx_w].echo_data_size = echo_data_size; dump_queue->idx_w++; spin_unlock(&vowdrv_lock); vowserv.bargein_dump_cnt1++; wake_up_interruptible(&wq_dump_pcm); } static void vow_pcm_dump_init(void) { VOWDRV_DEBUG("[Recog] %s()\n", __func__); dump_workqueue[DUMP_RECOG] = create_workqueue("dump_recog_data"); if (dump_workqueue[DUMP_RECOG] == NULL) { VOWDRV_DEBUG("[Recog] dump_workqueue[DUMP_RECOG] = %p\n", dump_workqueue[DUMP_RECOG]); } VOW_ASSERT(dump_workqueue[DUMP_RECOG] != NULL); dump_workqueue[DUMP_BARGEIN] = create_workqueue("dump_bargein_recho_ref"); if (dump_workqueue[DUMP_BARGEIN] == NULL) { VOWDRV_DEBUG("[BargeIn] dump_workqueue[recho_ref] = %p\n", dump_workqueue[DUMP_BARGEIN]); } VOW_ASSERT(dump_workqueue[DUMP_BARGEIN] != NULL); INIT_WORK(&dump_work[DUMP_BARGEIN].work, bargein_dump_routine); INIT_WORK(&dump_work[DUMP_RECOG].work, recog_dump_routine); init_waitqueue_head(&wq_dump_pcm); pcm_dump_task = NULL; #ifdef CONFIG_MTK_VOW_DUAL_MIC_SUPPORT if (vowserv.interleave_pcmdata_ptr != NULL) { vfree(vowserv.interleave_pcmdata_ptr); vowserv.interleave_pcmdata_ptr = NULL; } /* Temp buffer for doing DUALMIC L/R channels interleave */ vowserv.interleave_pcmdata_ptr = vmalloc(VOW_PCM_DUMP_BYTE_SIZE << 1); #endif /* #ifdef CONFIG_MTK_VOW_DUAL_MIC_SUPPORT */ } static void vow_pcm_dump_deinit(void) { int i = 0; VOWDRV_DEBUG("[BargeIn] %s()\n", __func__); for (i = 0; i < NUM_DUMP_DATA; i++) { if (dump_workqueue[i]) { flush_workqueue(dump_workqueue[i]); destroy_workqueue(dump_workqueue[i]); dump_workqueue[i] = NULL; } } #ifdef CONFIG_MTK_VOW_DUAL_MIC_SUPPORT if (vowserv.interleave_pcmdata_ptr != NULL) { vfree(vowserv.interleave_pcmdata_ptr); vowserv.interleave_pcmdata_ptr = NULL; } #endif /* #ifdef CONFIG_MTK_VOW_DUAL_MIC_SUPPORT */ } #endif /* #ifdef CONFIG_MTK_TINYSYS_SCP_SUPPORT */ /***************************************************************************** * VOW CONTROL FUNCTIONS *****************************************************************************/ static int VowDrv_SetHWStatus(int status) { int ret = 0; VOWDRV_DEBUG("%s():set:%x, cur:%x\n", __func__, status, vowserv.pwr_status); if ((status < NUM_OF_VOW_PWR_STATUS) && (status >= VOW_PWR_OFF)) { spin_lock(&vowdrv_lock); vowserv.pwr_status = status; spin_unlock(&vowdrv_lock); } else { VOWDRV_DEBUG("error input:%d\n", status); ret = -1; } return ret; } static int VowDrv_GetHWStatus(void) { int ret = 0; spin_lock(&vowdrv_lock); ret = vowserv.pwr_status; spin_unlock(&vowdrv_lock); return ret; } int VowDrv_EnableHW(int status) { int ret = 0; int pwr_status = 0; VOWDRV_DEBUG("%s():%x\n", __func__, status); if (status < 0) { VOWDRV_DEBUG("%s() error input:%x\n", __func__, status); ret = -1; } else { pwr_status = (status == 0)?VOW_PWR_OFF : VOW_PWR_ON; if (pwr_status == VOW_PWR_OFF) { /* reset the transfer limitation to */ /* avoid obstructing phase2.5 transferring */ if (vowserv.tx_keyword_start == true) vowserv.tx_keyword_start = false; } if (pwr_status == VOW_PWR_ON) { #ifdef CONFIG_MTK_TINYSYS_SCP_SUPPORT vow_register_feature(VOW_FEATURE_ID); #else VOWDRV_DEBUG("%s(), vow: SCP no support\n\r", __func__); #endif /* clear enter_phase3_cnt */ vowserv.enter_phase3_cnt = 0; } VowDrv_SetHWStatus(pwr_status); } return ret; } int VowDrv_ChangeStatus(void) { VowDrv_Wait_Queue_flag = 1; wake_up_interruptible(&VowDrv_Wait_Queue); return 0; } void VowDrv_SetSmartDevice(bool enable) { unsigned int eint_num; unsigned int ints[2] = {0, 0}; unsigned int vow_ipi_buf[2]; bool ret; if (!vow_check_scp_status()) { VOWDRV_DEBUG("SCP is off, do not support VOW\n"); return; } VOWDRV_DEBUG("%s():%x\n", __func__, enable); if (vowserv.node) { /* query eint number from device tree */ ret = of_property_read_u32_array(vowserv.node, "debounce", ints, ARRAY_SIZE(ints)); if (ret != 0) { VOWDRV_DEBUG("%s(), no debounce node, ret=%d\n", __func__, ret); return; } eint_num = ints[0]; if (enable == false) eint_num = 0xFF; vow_ipi_buf[0] = enable; vow_ipi_buf[1] = eint_num; #ifdef CONFIG_MTK_TINYSYS_SCP_SUPPORT ret = vow_IPICmd_Send(AUDIO_IPI_PAYLOAD, AUDIO_IPI_MSG_BYPASS_ACK, IPIMSG_VOW_SET_SMART_DEVICE, sizeof(unsigned int) * 2, 0, (char *)&vow_ipi_buf[0]); if (ret == 0) { VOWDRV_DEBUG( "IPIMSG_VOW_SET_SMART_DEVICE ipi send error\n\r"); } #else VOWDRV_DEBUG("vow:SCP no support\n\r"); #endif } else { /* no node here */ VOWDRV_DEBUG("there is no node\n"); } } void VowDrv_SetSmartDevice_GPIO(bool enable) { int ret = 0; if (vowserv.node) { if (enable == false) { VOWDRV_DEBUG("VowDrv_SetSmartDev_gpio:OFF\n"); ret = pinctrl_select_state(vowserv.pinctrl, vowserv.pins_eint_off); if (ret) { /* pinctrl setting error */ VOWDRV_DEBUG( "error, can not set gpio vow pins_eint_off\n"); } } else { VOWDRV_DEBUG("VowDrv_SetSmartDev_gpio:ON\n"); ret = pinctrl_select_state(vowserv.pinctrl, vowserv.pins_eint_on); if (ret) { /* pinctrl setting error */ VOWDRV_DEBUG( "error, can not set gpio vow pins_eint_on\n"); } } } else { /* no node here */ VOWDRV_DEBUG("there is no node\n"); } } static bool VowDrv_SetFlag(int type, unsigned int set) { bool ret = false; unsigned int vow_ipi_buf[2]; VOWDRV_DEBUG("%s(), type:%x, set:%x\n", __func__, type, set); vow_ipi_buf[0] = type; vow_ipi_buf[1] = set; #ifdef CONFIG_MTK_TINYSYS_SCP_SUPPORT ret = vow_IPICmd_Send(AUDIO_IPI_PAYLOAD, AUDIO_IPI_MSG_NEED_ACK, IPIMSG_VOW_SET_FLAG, sizeof(unsigned int) * 2, 0, (char *)&vow_ipi_buf[0]); if (ret == 0) VOWDRV_DEBUG("IPIMSG_VOW_SET_FLAG ipi send error\n\r"); #else VOWDRV_DEBUG("vow:SCP no support\n\r"); #endif return ret; } void VowDrv_SetDmicLowPower(bool enable) { VowDrv_SetFlag(VOW_FLAG_DMIC_LOWPOWER, enable); } static bool VowDrv_SetMtkifType(unsigned int type) { bool ret = false; if (type == 0) VOWDRV_DEBUG("error, wrong MTKIF Type\n\r"); vowserv.mtkif_type = type; ret = VowDrv_SetFlag(VOW_FLAG_MTKIF_TYPE, type); return ret; } static bool VowDrv_CheckMtkifType(unsigned int type) { unsigned int mtkif_type = type & 0x0F; unsigned int ch_num = type >> 4; if (mtkif_type >= VOW_MTKIF_MAX || mtkif_type < 0) { VOWDRV_DEBUG("out of VOW_MTKIF_TYPE %d\n\r", mtkif_type); return false; } if (ch_num >= VOW_CH_MAX || ch_num < 0) { VOWDRV_DEBUG("out of VOW_MIC_NUM %d\n\r", ch_num); return false; } return true; } void VowDrv_SetPeriodicEnable(bool enable) { VowDrv_SetFlag(VOW_FLAG_PERIODIC_ENABLE, enable); } static ssize_t VowDrv_GetPhase1Debug(struct device *kobj, struct device_attribute *attr, char *buf) { unsigned int stat; char cstr[35]; int size = sizeof(cstr); stat = (vowserv.force_phase_stage == FORCE_PHASE1) ? 1 : 0; return snprintf(buf, size, "Force Phase1 Setting = %s\n", (stat == 0x1) ? "YES" : "NO"); } static ssize_t VowDrv_SetPhase1Debug(struct device *kobj, struct device_attribute *attr, const char *buf, size_t n) { unsigned int enable = 0; if (kstrtouint(buf, 0, &enable) != 0) return -EINVAL; VowDrv_SetFlag(VOW_FLAG_FORCE_PHASE1_DEBUG, enable); return n; } DEVICE_ATTR(vow_SetPhase1, 0644, /*S_IWUSR | S_IRUGO*/ VowDrv_GetPhase1Debug, VowDrv_SetPhase1Debug); static ssize_t VowDrv_GetPhase2Debug(struct device *kobj, struct device_attribute *attr, char *buf) { unsigned int stat; char cstr[35]; int size = sizeof(cstr); stat = (vowserv.force_phase_stage == FORCE_PHASE2) ? 1 : 0; return snprintf(buf, size, "Force Phase2 Setting = %s\n", (stat == 0x1) ? "YES" : "NO"); } static ssize_t VowDrv_SetPhase2Debug(struct device *kobj, struct device_attribute *attr, const char *buf, size_t n) { unsigned int enable = 0; if (kstrtouint(buf, 0, &enable) != 0) return -EINVAL; VowDrv_SetFlag(VOW_FLAG_FORCE_PHASE2_DEBUG, enable); return n; } DEVICE_ATTR(vow_SetPhase2, 0644, /*S_IWUSR | S_IRUGO*/ VowDrv_GetPhase2Debug, VowDrv_SetPhase2Debug); static ssize_t VowDrv_GetDualMicDebug(struct device *kobj, struct device_attribute *attr, char *buf) { unsigned int stat; char cstr[35]; int size = sizeof(cstr); stat = (vowserv.scp_vow_lch == true) ? 1 : 0; return snprintf(buf, size, "Dual mic L channel = %s\n", (stat == 0x1) ? "YES" : "NO"); } static ssize_t VowDrv_SetDualMicDebug(struct device *kobj, struct device_attribute *attr, const char *buf, size_t n) { unsigned int enable = 0; if (kstrtouint(buf, 0, &enable) != 0) return -EINVAL; vowserv.scp_vow_lch = (enable == 1) ? true : false; VowDrv_SetFlag(VOW_FLAG_DUAL_MIC_LCH, enable); return n; } DEVICE_ATTR(vow_DualMicLch, 0644, /*S_IWUSR | S_IRUGO*/ VowDrv_GetDualMicDebug, VowDrv_SetDualMicDebug); static ssize_t VowDrv_GetSplitDumpFile(struct device *kobj, struct device_attribute *attr, char *buf) { unsigned int stat; char cstr[35]; int size = sizeof(cstr); stat = (vowserv.split_dumpfile_flag == true) ? 1 : 0; return snprintf(buf, size, "Split dump file = %s\n", (stat == 0x1) ? "YES" : "NO"); } static ssize_t VowDrv_SetSplitDumpFile(struct device *kobj, struct device_attribute *attr, const char *buf, size_t n) { unsigned int enable = 0; if (kstrtouint(buf, 0, &enable) != 0) return -EINVAL; VOWDRV_DEBUG("%s(),enable=%d\n", __func__, enable); vowserv.split_dumpfile_flag = (enable == 1) ? true : false; return n; } DEVICE_ATTR(vow_SplitDumpFile, 0644, /*S_IWUSR | S_IRUGO*/ VowDrv_GetSplitDumpFile, VowDrv_SetSplitDumpFile); static ssize_t VowDrv_SetBargeInDebug(struct device *kobj, struct device_attribute *attr, const char *buf, size_t n) { unsigned int enable = 0; if (kstrtouint(buf, 0, &enable) != 0) return -EINVAL; VowDrv_SetBargeIn(enable, 1); /* temp fix irq */ return n; } DEVICE_ATTR(vow_SetBargeIn, 0200, /*S_IWUSR*/ NULL, VowDrv_SetBargeInDebug); static bool VowDrv_SetBargeIn(unsigned int set, unsigned int irq_id) { bool ret = false; #ifdef CONFIG_MTK_TINYSYS_SCP_SUPPORT unsigned int vow_ipi_buf[1]; if (irq_id >= VOW_BARGEIN_IRQ_MAX_NUM || irq_id < 0) { VOWDRV_DEBUG("out of vow bargein irq range %d", irq_id); return ret; } vow_ipi_buf[0] = irq_id; VOWDRV_DEBUG("VowDrv_Debug_SetBargeIn = %d, irq = %d\n", set, irq_id); if (set == 1) { vow_register_feature(VOW_BARGEIN_FEATURE_ID); ret = vow_IPICmd_Send(AUDIO_IPI_PAYLOAD, AUDIO_IPI_MSG_NEED_ACK, IPIMSG_VOW_SET_BARGEIN_ON, sizeof(unsigned int) * 1, 0, (char *)&vow_ipi_buf[0]); } else if (set == 0) { ret = vow_IPICmd_Send(AUDIO_IPI_PAYLOAD, AUDIO_IPI_MSG_NEED_ACK, IPIMSG_VOW_SET_BARGEIN_OFF, sizeof(unsigned int) * 1, 0, (char *)&vow_ipi_buf[0]); vow_deregister_feature(VOW_BARGEIN_FEATURE_ID); } else { VOWDRV_DEBUG("Adb comment error\n\r"); } if (ret == 0) VOWDRV_DEBUG("IPIMSG_BARGE_IN(%d) ipi send error\n", set); #else VOWDRV_DEBUG("%s(), vow: SCP no support\n\r", __func__); #endif return ret; } static ssize_t VowDrv_GetBypassPhase3Flag(struct device *kobj, struct device_attribute *attr, char *buf) { unsigned int stat; char cstr[35]; int size = sizeof(cstr); stat = (vowserv.bypass_enter_phase3 == true) ? 1 : 0; return snprintf(buf, size, "Enter Phase3 Setting is %s\n", (stat == 0x1) ? "Bypass" : "Allow"); } static ssize_t VowDrv_SetBypassPhase3Flag(struct device *kobj, struct device_attribute *attr, const char *buf, size_t n) { unsigned int enable = 0; if (kstrtouint(buf, 0, &enable) != 0) return -EINVAL; if (enable == 0) { VOWDRV_DEBUG("Allow enter phase3\n"); vowserv.bypass_enter_phase3 = false; } else { VOWDRV_DEBUG("Bypass enter phase3\n"); vowserv.bypass_enter_phase3 = true; } return n; } DEVICE_ATTR(vow_SetBypassPhase3, 0644, /*S_IWUSR | S_IRUGO*/ VowDrv_GetBypassPhase3Flag, VowDrv_SetBypassPhase3Flag); static ssize_t VowDrv_GetEnterPhase3Counter(struct device *kobj, struct device_attribute *attr, char *buf) { char cstr[35]; int size = sizeof(cstr); return snprintf(buf, size, "Enter Phase3 Counter is %u\n", vowserv.enter_phase3_cnt); } DEVICE_ATTR(vow_GetEnterPhase3Counter, 0444, /*S_IRUGO*/ VowDrv_GetEnterPhase3Counter, NULL); static ssize_t VowDrv_GetSWIPLog(struct device *kobj, struct device_attribute *attr, char *buf) { unsigned int stat; char cstr[20]; int size = sizeof(cstr); stat = (vowserv.swip_log_enable == true) ? 1 : 0; return snprintf(buf, size, "SWIP LOG is %s\n", (stat == true) ? "YES" : "NO"); } static ssize_t VowDrv_SetSWIPLog(struct device *kobj, struct device_attribute *attr, const char *buf, size_t n) { unsigned int enable = 0; if (kstrtouint(buf, 0, &enable) != 0) return -EINVAL; VowDrv_SetFlag(VOW_FLAG_SWIP_LOG_PRINT, enable); return n; } DEVICE_ATTR(vow_SetLibLog, 0644, /*S_IWUSR | S_IRUGO*/ VowDrv_GetSWIPLog, VowDrv_SetSWIPLog); static ssize_t VowDrv_SetEnableHW(struct device *kobj, struct device_attribute *attr, const char *buf, size_t n) { unsigned int enable = 0; if (kstrtouint(buf, 0, &enable) != 0) return -EINVAL; VowDrv_EnableHW(enable); VowDrv_ChangeStatus(); return n; } DEVICE_ATTR(vow_SetEnableHW, 0200, /*S_IWUSR*/ NULL, VowDrv_SetEnableHW); static int VowDrv_SetVowEINTStatus(int status) { int ret = 0; int wakeup_event = 0; if ((status < NUM_OF_VOW_EINT_STATUS) && (status >= VOW_EINT_DISABLE)) { spin_lock(&vowdrv_lock); if ((vowserv.eint_status != VOW_EINT_PASS) && (status == VOW_EINT_PASS)) wakeup_event = 1; vowserv.eint_status = status; spin_unlock(&vowdrv_lock); } else { VOWDRV_DEBUG("%s() error input:%x\n", __func__, status); ret = -1; } return ret; } static int VowDrv_QueryVowEINTStatus(void) { int ret = 0; spin_lock(&vowdrv_lock); ret = vowserv.eint_status; spin_unlock(&vowdrv_lock); VOWDRV_DEBUG("%s():%d\n", __func__, ret); return ret; } static int VowDrv_open(struct inode *inode, struct file *fp) { VOWDRV_DEBUG("%s() do nothing inode:%p, file:%p\n", __func__, inode, fp); return 0; } static int VowDrv_release(struct inode *inode, struct file *fp) { VOWDRV_DEBUG("%s() inode:%p, file:%p\n", __func__, inode, fp); if (!(fp->f_mode & FMODE_WRITE || fp->f_mode & FMODE_READ)) return -ENODEV; return 0; } static long VowDrv_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) { int ret = 0; int timeout = 0; timeout = 0; while (vowserv.vow_recovering) { msleep(VOW_WAITCHECK_INTERVAL_MS); timeout++; VOW_ASSERT(timeout != VOW_RECOVERY_WAIT); } /* VOWDRV_DEBUG("VowDrv_ioctl cmd = %u arg = %lu\n", cmd, arg); */ switch ((unsigned int)cmd) { case VOW_SET_CONTROL: switch (arg) { case VOWControlCmd_Init: VOWDRV_DEBUG("VOW_SET_CONTROL Init"); vow_service_Init(); break; case VOWControlCmd_Reset: VOWDRV_DEBUG("VOW_SET_CONTROL Reset"); vow_service_reset(); break; case VOWControlCmd_EnableDebug: VOWDRV_DEBUG("VOW_SET_CONTROL EnableDebug"); mutex_lock(&voicedata_mutex); vowserv.voicedata_idx = 0; mutex_unlock(&voicedata_mutex); vowserv.recording_flag = true; vowserv.firstRead = true; /*VowDrv_SetFlag(VOW_FLAG_DEBUG, true);*/ #ifdef CONFIG_MTK_TINYSYS_SCP_SUPPORT vow_register_feature(VOW_DUMP_FEATURE_ID); #else VOWDRV_DEBUG("%s(), vow: SCP no support\n\r", __func__); #endif if (vowserv.suspend_lock == 0) { vowserv.suspend_lock = 1; /* Let AP will not suspend */ __pm_stay_awake(vow_suspend_lock); VOWDRV_DEBUG("==VOW DEBUG MODE START==\n"); } break; case VOWControlCmd_DisableDebug: VOWDRV_DEBUG("VOW_SET_CONTROL DisableDebug"); VowDrv_SetFlag(VOW_FLAG_DEBUG, false); vowserv.recording_flag = false; /* force stop vow_service_ReadVoiceData() 20180906 */ vow_service_getVoiceData(); if (vowserv.suspend_lock == 1) { vowserv.suspend_lock = 0; /* Let AP will suspend */ __pm_relax(vow_suspend_lock); /* lock 1sec for screen on */ VOWDRV_DEBUG("==VOW DEBUG MODE STOP==\n"); __pm_wakeup_event(vow_suspend_lock, jiffies_to_msecs(HZ)); } #ifdef CONFIG_MTK_TINYSYS_SCP_SUPPORT vow_deregister_feature(VOW_DUMP_FEATURE_ID); #else VOWDRV_DEBUG("%s(), vow: SCP no support\n\r", __func__); #endif break; case VOWControlCmd_EnableSeamlessRecord: VOWDRV_DEBUG("VOW_SET_CONTROL EnableSeamlessRecord"); VowDrv_SetFlag(VOW_FLAG_SEAMLESS, true); break; #ifdef CONFIG_MTK_TINYSYS_SCP_SUPPORT case VOWControlCmd_EnableDump: vow_pcm_dump_set(true); break; case VOWControlCmd_DisableDump: vow_pcm_dump_set(false); break; #endif default: VOWDRV_DEBUG("VOW_SET_CONTROL no such command = %lu", arg); break; } break; case VOW_READ_VOICE_DATA: if (!vow_service_SetApAddr(arg)) ret = -EFAULT; if ((vowserv.recording_flag == true) && (vowserv.firstRead == true)) { vowserv.firstRead = false; VowDrv_SetFlag(VOW_FLAG_DEBUG, true); } vow_service_ReadVoiceData(); break; case VOW_SET_SPEAKER_MODEL: VOWDRV_DEBUG("VOW_SET_SPEAKER_MODEL(%lu)", arg); if (!vow_service_SetSpeakerModel(arg)) ret = -EFAULT; break; case VOW_CLR_SPEAKER_MODEL: VOWDRV_DEBUG("VOW_CLR_SPEAKER_MODEL(%lu)", arg); if (!vow_service_ReleaseSpeakerModel((int)arg)) ret = -EFAULT; break; case VOW_SET_APREG_INFO: VOWDRV_DEBUG("VOW_SET_APREG_INFO(%lu)", arg); if (!vow_service_SetVBufAddr(arg)) ret = -EFAULT; break; case VOW_READ_VOW_DUMP_DATA: //Do nothing break; case VOW_BARGEIN_ON: VOWDRV_DEBUG("VOW_BARGEIN_ON, irq: %d", (unsigned int)arg); if (!VowDrv_SetBargeIn(1, (unsigned int)arg)) ret = -EFAULT; break; case VOW_BARGEIN_OFF: VOWDRV_DEBUG("VOW_BARGEIN_OFF, irq: %d", (unsigned int)arg); if (!VowDrv_SetBargeIn(0, (unsigned int)arg)) ret = -EFAULT; break; case VOW_CHECK_STATUS: /* VOW disable already, then bypass second one */ VowDrv_ChangeStatus(); VOWDRV_DEBUG("VOW_CHECK_STATUS(%lu)", arg); break; case VOW_RECOG_ENABLE: pr_debug("+VOW_RECOG_ENABLE(%lu)+", arg); pr_debug("KERNEL_VOW_DRV_VER %s", KERNEL_VOW_DRV_VER); if (!VowDrv_CheckMtkifType((unsigned int)arg)) { pr_debug("+VOW_RECOG_ENABLE fail"); break; } VowDrv_SetMtkifType((unsigned int)arg); VowDrv_EnableHW(1); VowDrv_ChangeStatus(); vow_service_Enable(); pr_debug("-VOW_RECOG_ENABLE(%lu)-", arg); break; case VOW_RECOG_DISABLE: pr_debug("+VOW_RECOG_DISABLE(%lu)+", arg); if (!VowDrv_CheckMtkifType((unsigned int)arg)) { pr_debug("+VOW_RECOG_DISABLE fail"); break; } VowDrv_SetMtkifType((unsigned int)arg); VowDrv_EnableHW(0); VowDrv_ChangeStatus(); vow_service_Disable(); pr_debug("-VOW_RECOG_DISABLE(%lu)-", arg); break; case VOW_MODEL_START: vow_service_SetModelStatus(VOW_MODEL_STATUS_START, arg); break; case VOW_MODEL_STOP: vow_service_SetModelStatus(VOW_MODEL_STATUS_STOP, arg); break; case VOW_GET_GOOGLE_ENGINE_VER: { if (copy_to_user((void __user *)arg, &vowserv.google_engine_version, sizeof(vowserv.google_engine_version))) { VOWDRV_DEBUG("%s(), copy_to_user fail in VOW_GET_GOOGLE_ENGINE_VER", __func__); } } break; case VOW_GET_GOOGLE_ARCH: case VOW_GET_ALEXA_ENGINE_VER: { struct vow_engine_version_t engine_ver_temp; unsigned int length = VOW_ENGINE_INFO_LENGTH_BYTE; if (copy_from_user((void *)&engine_ver_temp, (const void __user *)arg, sizeof(struct vow_engine_version_t))) { VOWDRV_DEBUG("%s(), copy_from_user fail", __func__); } if ((unsigned int)cmd == VOW_GET_ALEXA_ENGINE_VER) { pr_debug("VOW_GET_ALEXA_ENGINE_VER = %s, %lu, %lu, %d", vowserv.alexa_engine_version, engine_ver_temp.data_addr, engine_ver_temp.return_size_addr, length); if (copy_to_user((void __user *)engine_ver_temp.data_addr, vowserv.alexa_engine_version, length)) { VOWDRV_DEBUG("%s(), copy_to_user fail", __func__); } } else if ((unsigned int)cmd == VOW_GET_GOOGLE_ARCH) { pr_debug("VOW_GET_GOOGLE_ARCH = %s, %lu, %lu, %d", vowserv.google_engine_arch, engine_ver_temp.data_addr, engine_ver_temp.return_size_addr, length); if (copy_to_user((void __user *)engine_ver_temp.data_addr, vowserv.google_engine_arch, length)) { VOWDRV_DEBUG("%s(), copy_to_user fail", __func__); } } if (copy_to_user((void __user *)engine_ver_temp.return_size_addr, &length, sizeof(unsigned int))) { VOWDRV_DEBUG("%s(), copy_to_user fail", __func__); } } break; default: VOWDRV_DEBUG("vow WrongParameter(%lu)", arg); break; } return ret; } #ifdef CONFIG_COMPAT static long VowDrv_compat_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) { long ret = 0; /* VOWDRV_DEBUG("++VowDrv_compat_ioctl cmd = %u, arg = %lu\n" */ /* , cmd, arg); */ if (!fp->f_op || !fp->f_op->unlocked_ioctl) { (void)ret; return -ENOTTY; } switch (cmd) { case VOW_CLR_SPEAKER_MODEL: case VOW_SET_CONTROL: case VOW_CHECK_STATUS: case VOW_RECOG_ENABLE: case VOW_RECOG_DISABLE: case VOW_BARGEIN_ON: case VOW_BARGEIN_OFF: ret = fp->f_op->unlocked_ioctl(fp, cmd, arg); break; case VOW_GET_GOOGLE_ENGINE_VER: ret = fp->f_op->unlocked_ioctl(fp, cmd, arg); break; case VOW_MODEL_START: case VOW_MODEL_STOP: { struct vow_model_start_kernel_t __user *data32; struct vow_model_start_t __user *data; int err; compat_size_t l; data32 = compat_ptr(arg); data = compat_alloc_user_space(sizeof(*data)); err = get_user(l, &data32->handle); err |= put_user(l, &data->handle); err |= get_user(l, &data32->confidence_level); err |= put_user(l, &data->confidence_level); err |= get_user(l, &data32->dsp_inform_addr); err |= put_user(l, &data->dsp_inform_addr); err |= get_user(l, &data32->dsp_inform_size_addr); err |= put_user(l, &data->dsp_inform_size_addr); ret = fp->f_op->unlocked_ioctl(fp, cmd, (unsigned long)data); } break; case VOW_READ_VOICE_DATA: case VOW_SET_SPEAKER_MODEL: case VOW_READ_VOW_DUMP_DATA: case VOW_SET_APREG_INFO: { struct vow_model_info_kernel_t __user *data32; struct vow_model_info_t __user *data; int err; compat_size_t l; compat_uptr_t p; data32 = compat_ptr(arg); data = compat_alloc_user_space(sizeof(*data)); err = get_user(l, &data32->id); err |= put_user(l, &data->id); err |= get_user(l, &data32->keyword); err |= put_user(l, &data->keyword); err |= get_user(l, &data32->addr); err |= put_user(l, &data->addr); err |= get_user(l, &data32->size); err |= put_user(l, &data->size); err |= get_user(l, &data32->return_size_addr); err |= put_user(l, &data->return_size_addr); err |= get_user(l, &data32->uuid); err |= put_user(l, &data->uuid); err |= get_user(p, (compat_uptr_t *)&data32->data); err |= put_user(p, (compat_uptr_t *)&data->data); ret = fp->f_op->unlocked_ioctl(fp, cmd, (unsigned long)data); } break; case VOW_GET_GOOGLE_ARCH: case VOW_GET_ALEXA_ENGINE_VER: { struct vow_engine_version_kernel_t __user *data32; struct vow_engine_version_t __user *data; int err; compat_size_t l; data32 = compat_ptr(arg); data = compat_alloc_user_space(sizeof(*data)); err = get_user(l, &data32->return_size_addr); err |= put_user(l, &data->return_size_addr); err |= get_user(l, &data32->data_addr); err |= put_user(l, &data->data_addr); ret = fp->f_op->unlocked_ioctl(fp, cmd, (unsigned long)data); } break; default: break; } /* VOWDRV_DEBUG("--VowDrv_compat_ioctl\n"); */ return ret; } #endif static ssize_t VowDrv_write(struct file *fp, const char __user *data, size_t count, loff_t *offset) { /* VOWDRV_DEBUG("+VowDrv_write = %p count = %d\n",fp ,count); */ return 0; } static ssize_t VowDrv_read(struct file *fp, char __user *data, size_t count, loff_t *offset) { unsigned int read_count = 0; unsigned int time_diff_scp_ipi = 0; unsigned int time_diff_ipi_read = 0; unsigned long long vow_read_cycle = 0; int ret = 0; int slot = 0; bool dsp_inform_tx_flag = false; unsigned int ret_data = 0; VOWDRV_DEBUG("+%s()+\n", __func__); VowDrv_SetVowEINTStatus(VOW_EINT_RETRY); if (VowDrv_Wait_Queue_flag == 0) ret = wait_event_interruptible(VowDrv_Wait_Queue, VowDrv_Wait_Queue_flag); if (VowDrv_Wait_Queue_flag == 1) { VowDrv_Wait_Queue_flag = 0; if (VowDrv_GetHWStatus() == VOW_PWR_OFF) { VOWDRV_DEBUG("vow Enter_phase3_cnt = %d\n", vowserv.enter_phase3_cnt); vowserv.scp_command_flag = false; VowDrv_SetVowEINTStatus(VOW_EINT_DISABLE); } else { if (vowserv.scp_command_flag) { VowDrv_SetVowEINTStatus(VOW_EINT_PASS); vow_read_cycle = get_cycles(); time_diff_scp_ipi = (unsigned int)CYCLE_TO_NS * (unsigned int)(vowserv.ap_received_ipi_cycle - vowserv.scp_recognize_ok_cycle); time_diff_ipi_read = (unsigned int)CYCLE_TO_NS * (unsigned int)(vow_read_cycle - vowserv.ap_received_ipi_cycle); VOWDRV_DEBUG("vow Wakeup by SCP\n"); VOWDRV_DEBUG("SCP->IPI:%d(ns),IPI->AP:%d(ns)\n", time_diff_scp_ipi, time_diff_ipi_read); if (vowserv.suspend_lock == 0) { /* lock 1sec for screen on */ __pm_wakeup_event(vow_suspend_lock, jiffies_to_msecs(HZ)); } vowserv.scp_command_flag = false; if (vowserv.extradata_bytelen > 0) dsp_inform_tx_flag = true; } else { VOWDRV_DEBUG("vow Wakeup by other(%d,%d)\n", VowDrv_Wait_Queue_flag, VowDrv_GetHWStatus()); } } } vowserv.vow_eint_data_struct.id = vowserv.scp_command_id; vowserv.vow_eint_data_struct.eint_status = VowDrv_QueryVowEINTStatus(); vowserv.vow_eint_data_struct.data[0] = (char)vowserv.confidence_level; //reset data vowserv.confidence_level = 0; read_count = copy_to_user((void __user *)data, &vowserv.vow_eint_data_struct, sizeof(struct vow_eint_data_struct_t)); if (dsp_inform_tx_flag) { /* int i; */ dsp_inform_tx_flag = false; if (vowserv.extradata_mem_ptr == NULL) goto exit; if (vowserv.extradata_ptr == NULL) goto exit; if (vowserv.vow_speaker_model[slot].rx_inform_size_addr == 0) goto exit; if (vowserv.vow_speaker_model[slot].rx_inform_addr == 0) goto exit; VOWDRV_DEBUG("%s(), copy to user, extra data len=%d\n", __func__, vowserv.extradata_bytelen); /* copy extra data from DRAM */ mutex_lock(&vow_extradata_mutex); memcpy(vowserv.extradata_mem_ptr, vowserv.extradata_ptr, vowserv.extradata_bytelen); mutex_unlock(&vow_extradata_mutex); /* copy extra data into user space */ ret_data = copy_to_user( (void __user *) vowserv.vow_speaker_model[slot].rx_inform_size_addr, &vowserv.extradata_bytelen, sizeof(unsigned int)); if (ret_data != 0) { /* fail, print the fail size */ VOWDRV_DEBUG("[vow dsp inform1]CopytoUser fail sz:%d\n", ret_data); } ret_data = copy_to_user( (void __user *) vowserv.vow_speaker_model[slot].rx_inform_addr, vowserv.extradata_mem_ptr, vowserv.extradata_bytelen); if (ret_data != 0) { /* fail, print the fail size */ VOWDRV_DEBUG("[vow dsp inform1]CopytoUser fail sz:%d\n", ret_data); } } exit: VOWDRV_DEBUG("+%s()-, recog id: %d, confidence_lv=%d\n", __func__, vowserv.scp_command_id, vowserv.confidence_level); return read_count; } static int VowDrv_flush(struct file *flip, fl_owner_t id) { VOWDRV_DEBUG("%s()\n", __func__); return 0; } static int VowDrv_fasync(int fd, struct file *flip, int mode) { VOWDRV_DEBUG("%s()\n", __func__); /*return fasync_helper(fd, flip, mode, &VowDrv_fasync);*/ return 0; } static int VowDrv_remap_mmap(struct file *flip, struct vm_area_struct *vma) { VOWDRV_DEBUG("%s()\n", __func__); return -1; } int VowDrv_setup_smartdev_eint(struct platform_device *pdev) { int ret; unsigned int ints[2] = {0, 0}; /* gpio setting */ vowserv.pinctrl = devm_pinctrl_get(&pdev->dev); if (IS_ERR(vowserv.pinctrl)) { ret = PTR_ERR(vowserv.pinctrl); VOWDRV_DEBUG("Cannot find Vow pinctrl!\n"); return ret; } vowserv.pins_eint_on = pinctrl_lookup_state(vowserv.pinctrl, "vow_smartdev_eint_on"); if (IS_ERR(vowserv.pins_eint_on)) { ret = PTR_ERR(vowserv.pins_eint_on); VOWDRV_DEBUG("Cannot find vow pinctrl eint_on!\n"); return ret; } vowserv.pins_eint_off = pinctrl_lookup_state(vowserv.pinctrl, "vow_smartdev_eint_off"); if (IS_ERR(vowserv.pins_eint_off)) { ret = PTR_ERR(vowserv.pins_eint_off); VOWDRV_DEBUG("Cannot find vow pinctrl eint_off!\n"); return ret; } /* eint setting */ vowserv.node = pdev->dev.of_node; if (vowserv.node) { ret = of_property_read_u32_array(vowserv.node, "debounce", ints, ARRAY_SIZE(ints)); if (ret != 0) { VOWDRV_DEBUG("%s(), no debounce node, ret=%d\n", __func__, ret); return ret; } VOWDRV_DEBUG("VOW EINT ID: %x, %x\n", ints[0], ints[1]); } else { /* no node here */ VOWDRV_DEBUG("%s(), there is no this node\n", __func__); } return 0; } /***************************************************************************** * SCP Recovery Register *****************************************************************************/ #ifdef CONFIG_MTK_TINYSYS_SCP_SUPPORT static int vow_scp_recover_event(struct notifier_block *this, unsigned long event, void *ptr) { switch (event) { case SCP_EVENT_READY: { int I; msleep(500); vowserv.vow_recovering = true; vowserv.scp_recovering = false; VOWDRV_DEBUG("%s(), SCP_EVENT_READY\n", __func__); if (!vow_check_scp_status()) { VOWDRV_DEBUG("SCP is Off, don't recover VOW\n"); return NOTIFY_DONE; } if (vowserv.scp_recovering) { vowserv.vow_recovering = false; VOWDRV_DEBUG("fail: vow recover1\n"); break; } vow_service_Init(); if (vowserv.scp_recovering) { vowserv.vow_recovering = false; VOWDRV_DEBUG("fail: vow recover2\n"); break; } for (I = 0; I < MAX_VOW_SPEAKER_MODEL; I++) { if (!vow_service_SendSpeakerModel(I, VOW_SET_MODEL)) VOWDRV_DEBUG("fail: SendSpeakerModel\n"); } /* if vow is not enable, then return */ if (VowDrv_GetHWStatus() != VOW_PWR_ON) { vowserv.vow_recovering = false; break; } if (vowserv.scp_recovering) { vowserv.vow_recovering = false; VOWDRV_DEBUG("fail: vow recover3\n"); break; } /* pcm dump recover */ VOWDRV_DEBUG("recording_flag = %d, dump_pcm_flag = %d\n", vowserv.recording_flag, vowserv.dump_pcm_flag); if (vowserv.recording_flag == true) VowDrv_SetFlag(VOW_FLAG_DEBUG, true); if (vowserv.dump_pcm_flag == true) vow_pcm_dump_notify(true); if (vowserv.scp_recovering) { vowserv.vow_recovering = false; VOWDRV_DEBUG("fail: vow recover4\n"); break; } if (!VowDrv_SetMtkifType(vowserv.mtkif_type)) VOWDRV_DEBUG("fail: vow_SetMtkifType\n"); if (!vow_service_Enable()) VOWDRV_DEBUG("fail: vow_service_Enable\n"); for (I = 0; I < MAX_VOW_SPEAKER_MODEL; I++) { if (vowserv.vow_speaker_model[I].enabled) { vow_service_SendModelStatus( I, VOW_MODEL_STATUS_START); VOWDRV_DEBUG("send Model start, slot%d\n", I); } } vowserv.vow_recovering = false; break; } case SCP_EVENT_STOP: vowserv.scp_recovering = true; VOWDRV_DEBUG("%s(), SCP_EVENT_STOP\n", __func__); /* Check if VOW is running phase2.5, then stop this */ break; } return NOTIFY_DONE; } static struct notifier_block vow_scp_recover_notifier = { .notifier_call = vow_scp_recover_event, }; #endif /* #ifdef CONFIG_MTK_TINYSYS_SCP_SUPPORT */ /***************************************************************************** * VOW platform driver Registration *****************************************************************************/ static int VowDrv_probe(struct platform_device *dev) { VOWDRV_DEBUG("%s()\n", __func__); vow_suspend_lock = wakeup_source_register(NULL, "vow wakelock"); if (!vow_suspend_lock) pr_warn("wakeup source init failed.\n"); #ifdef CONFIG_MTK_TINYSYS_SCP_SUPPORT pcm_dump_wake_lock = wakeup_source_register(NULL, "vow pcm dump wakelock"); if (!vow_suspend_lock) pr_warn("pcm dump wakelock source init failed.\n"); #endif VowDrv_setup_smartdev_eint(dev); return 0; } static int VowDrv_remove(struct platform_device *dev) { VOWDRV_DEBUG("%s()\n", __func__); wakeup_source_unregister(vow_suspend_lock); #ifdef CONFIG_MTK_TINYSYS_SCP_SUPPORT wakeup_source_unregister(pcm_dump_wake_lock); #endif return 0; } static void VowDrv_shutdown(struct platform_device *dev) { VOWDRV_DEBUG("%s()\n", __func__); } static int VowDrv_suspend(struct platform_device *dev, pm_message_t state) { /* only one suspend mode */ VOWDRV_DEBUG("%s()\n", __func__); return 0; } static int VowDrv_resume(struct platform_device *dev) /* wake up */ { VOWDRV_DEBUG("%s()\n", __func__); return 0; } static const struct file_operations VOW_fops = { .owner = THIS_MODULE, .open = VowDrv_open, .release = VowDrv_release, .unlocked_ioctl = VowDrv_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = VowDrv_compat_ioctl, #endif .write = VowDrv_write, .read = VowDrv_read, .flush = VowDrv_flush, .fasync = VowDrv_fasync, .mmap = VowDrv_remap_mmap }; static struct miscdevice VowDrv_misc_device = { .minor = MISC_DYNAMIC_MINOR, .name = VOW_DEVNAME, .fops = &VOW_fops, }; const struct dev_pm_ops VowDrv_pm_ops = { .suspend = NULL, .resume = NULL, .freeze = NULL, .thaw = NULL, .poweroff = NULL, .restore = NULL, .restore_noirq = NULL, }; #ifdef CONFIG_OF static const struct of_device_id vow_of_match[] = { {.compatible = "mediatek,vow"}, {}, }; #endif static struct platform_driver VowDrv_driver = { .probe = VowDrv_probe, .remove = VowDrv_remove, .shutdown = VowDrv_shutdown, .suspend = VowDrv_suspend, .resume = VowDrv_resume, .driver = { #ifdef CONFIG_PM .pm = &VowDrv_pm_ops, #endif .name = "VOW_driver_device", #ifdef CONFIG_OF .of_match_table = vow_of_match, #endif }, }; static int VowDrv_mod_init(void) { int ret = 0; VOWDRV_DEBUG("+%s()\n", __func__); /* Register platform DRIVER */ ret = platform_driver_register(&VowDrv_driver); if (ret != 0) { VOWDRV_DEBUG("VowDrv Fail:%d - Register DRIVER\n", ret); return ret; } /* register MISC device */ ret = misc_register(&VowDrv_misc_device); if (ret != 0) { VOWDRV_DEBUG("VowDrv_probe misc_register Fail:%d\n", ret); return ret; } ret = device_create_file(VowDrv_misc_device.this_device, &dev_attr_vow_SetPhase1); if (unlikely(ret != 0)) return ret; ret = device_create_file(VowDrv_misc_device.this_device, &dev_attr_vow_SetPhase2); if (unlikely(ret != 0)) return ret; ret = device_create_file(VowDrv_misc_device.this_device, &dev_attr_vow_SetBypassPhase3); if (unlikely(ret != 0)) return ret; ret = device_create_file(VowDrv_misc_device.this_device, &dev_attr_vow_GetEnterPhase3Counter); if (unlikely(ret != 0)) return ret; ret = device_create_file(VowDrv_misc_device.this_device, &dev_attr_vow_SetLibLog); if (unlikely(ret != 0)) return ret; ret = device_create_file(VowDrv_misc_device.this_device, &dev_attr_vow_SetEnableHW); if (unlikely(ret != 0)) return ret; ret = device_create_file(VowDrv_misc_device.this_device, &dev_attr_vow_SetBargeIn); if (unlikely(ret != 0)) return ret; ret = device_create_file(VowDrv_misc_device.this_device, &dev_attr_vow_DualMicLch); if (unlikely(ret != 0)) return ret; ret = device_create_file(VowDrv_misc_device.this_device, &dev_attr_vow_SplitDumpFile); if (unlikely(ret != 0)) return ret; vow_service_Init(); #ifdef CONFIG_MTK_TINYSYS_SCP_SUPPORT scp_A_register_notify(&vow_scp_recover_notifier); #endif VOWDRV_DEBUG("-%s(): Init Audio WakeLock\n", __func__); return 0; } static void VowDrv_mod_exit(void) { VOWDRV_DEBUG("+%s()\n", __func__); #ifdef CONFIG_MTK_TINYSYS_SCP_SUPPORT vow_pcm_dump_deinit(); #endif VOWDRV_DEBUG("-%s()\n", __func__); } module_init(VowDrv_mod_init); module_exit(VowDrv_mod_exit); /***************************************************************************** * License *****************************************************************************/ MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("MediaTek VOW Driver"); MODULE_AUTHOR("Charlie Lu");