// 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" #endif /* #ifdef CONFIG_MTK_TINYSYS_SCP_SUPPORT */ #include "vow.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 DEFINE_SPINLOCK(vowdrv_dump_lock); static struct wakeup_source *vow_suspend_lock; static struct wakeup_source *vow_ipi_suspend_lock; static struct dump_package_t dump_package; static int init_flag = -1; static const uint32_t kReadVowDumpSize = 0xA00 * 2; // 320(10ms) x 8 x 2ch= 5120 = 0x1400 /***************************************************************************** * Function Declaration ****************************************************************************/ static void vow_service_getVoiceData(void); static void vow_ipi_reg_ok(short uuid, int confidence_lv, unsigned int extradata_len, unsigned int payloaddump_len); static bool VowDrv_SetFlag(int type, unsigned int set); static int VowDrv_GetHWStatus(void); static void vow_pcm_dump_init(void); static void vow_pcm_dump_deinit(void); static bool VowDrv_SetBargeIn(unsigned int set, unsigned int irq_id); //static int vow_service_SearchSpeakerModelWithUuid(int uuid); static int vow_service_SearchSpeakerModelWithKeyword(int keyword); static int vow_service_SearchSpeakerModelWithId(int id); #ifdef CONFIG_MTK_VOW_1STSTAGE_PCMCALLBACK static void vow_service_ReadPayloadDumpData(unsigned int buf_length); #endif 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 *voicedata_scp_ptr; dma_addr_t voicedata_scp_addr; char *extradata_ptr; dma_addr_t extradata_addr; char *extradata_mem_ptr; unsigned int extradata_bytelen; #ifdef CONFIG_MTK_VOW_1STSTAGE_PCMCALLBACK char *payloaddump_scp_ptr; dma_addr_t payloaddump_scp_addr; unsigned long payloaddump_user_addr; unsigned long payloaddump_user_max_size; unsigned long payloaddump_user_return_size_addr; short *payloaddump_kernel_ptr; unsigned int payloaddump_length; #endif unsigned int kernel_voicedata_idx; bool scp_command_flag; bool recording_flag; int scp_command_id; int scp_command_keywordid; int confidence_level; int eint_status; int pwr_status; bool suspend_lock; bool firstRead; unsigned long voicedata_user_return_size_addr; unsigned int scp_shared_voice_buf_offset; unsigned int scp_shared_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; short *interleave_pcmdata_ptr; bool dump_pcm_flag; bool scp_recovering; bool vow_recovering; unsigned int recog_dump_cnt1; unsigned int vffp_dump_cnt1; unsigned int inputmic_dump_cnt1; bool split_dumpfile_flag; bool mcps_flag; unsigned int scp_dual_mic_switch; unsigned int mtkif_type; unsigned int google_engine_version; unsigned int vow_mic_number; char alexa_engine_version[VOW_ENGINE_INFO_LENGTH_BYTE]; char google_engine_arch[VOW_ENGINE_INFO_LENGTH_BYTE]; unsigned int custom_model_addr; unsigned long custom_model_size; } vowserv; struct vow_dump_info_t { dma_addr_t phy_addr; // address of reseved buffer char *vir_addr; // virtual (kernel) addess of reseved buffer uint32_t size; // size of reseved buffer (bytes) uint32_t scp_dump_offset[VOW_MAX_CH_NUM]; // return data offset from scp uint32_t scp_dump_size[VOW_MAX_CH_NUM]; // return data size from scp short *kernel_dump_addr; // kernel internal buffer address unsigned int kernel_dump_idx; // current index of kernel_dump_addr unsigned int kernel_dump_size; // size of kernel_dump_ptr buffer (bytes) unsigned long user_dump_addr; // addr of user dump buffer unsigned long user_dump_idx; // current index of user_dump_addr unsigned long user_dump_size; // size of user dump buffer unsigned long user_return_size_addr; // addr of return size of user }; #define NUM_DELAY_INFO (2) uint32_t delay_info[NUM_DELAY_INFO]; struct vow_dump_info_t vow_dump_info[NUM_DUMP_DATA]; /***************************************************************************** * DSP IPI HANDELER *****************************************************************************/ static void vow_ipi_rx_handle_data_msg(void *msg_data) { struct vow_ipi_combined_info_t *ipi_ptr; unsigned long flags; ipi_ptr = (struct vow_ipi_combined_info_t *)msg_data; /* IPIMSG_VOW_BARGEIN_DUMP_INFO */ if (ipi_ptr->ipi_type_flag & BARGEIN_DUMP_INFO_IDX_MASK) { delay_info[0] = ipi_ptr->dump_frm_cnt; delay_info[1] = ipi_ptr->voice_sample_delay; VOWDRV_DEBUG("[BargeIn] delay info %d %d\n", delay_info[0], delay_info[1]); vow_dump_info[DUMP_DELAY_INFO].scp_dump_size[0] = sizeof(delay_info); vow_dump_info[DUMP_DELAY_INFO].scp_dump_offset[0] = 0; } if (vowserv.dump_pcm_flag == true) { spin_lock_irqsave(&vowdrv_dump_lock, flags); if ((ipi_ptr->ipi_type_flag & INPUT_DUMP_IDX_MASK)) { if (vow_dump_info[DUMP_INPUT].scp_dump_size[0] != 0) { VOWDRV_DEBUG("%s WARNING dump idx 0x%x %d not handled\n", __func__, INPUT_DUMP_IDX_MASK, vow_dump_info[DUMP_INPUT].scp_dump_size[0]); } vow_dump_info[DUMP_INPUT].scp_dump_size[0] = ipi_ptr->mic_dump_size; vow_dump_info[DUMP_INPUT].scp_dump_offset[0] = ipi_ptr->mic_offset; if (vowserv.vow_mic_number == 2) { vow_dump_info[DUMP_INPUT].scp_dump_size[1] = ipi_ptr->mic_dump_size; vow_dump_info[DUMP_INPUT].scp_dump_offset[1] = ipi_ptr->mic_offset_R; } } /* IPIMSG_VOW_BARGEIN_PCMDUMP_OK */ if ((ipi_ptr->ipi_type_flag & BARGEIN_DUMP_IDX_MASK)) { vow_dump_info[DUMP_BARGEIN].scp_dump_size[0] = ipi_ptr->echo_dump_size; vow_dump_info[DUMP_BARGEIN].scp_dump_offset[0] = ipi_ptr->echo_offset; } if ((ipi_ptr->ipi_type_flag & RECOG_DUMP_IDX_MASK)) { vow_dump_info[DUMP_RECOG].scp_dump_size[0] = ipi_ptr->recog_dump_size; vow_dump_info[DUMP_RECOG].scp_dump_offset[0] = ipi_ptr->recog_dump_offset; if (vowserv.vow_mic_number == 2) { vow_dump_info[DUMP_RECOG].scp_dump_size[1] = ipi_ptr->recog_dump_size; vow_dump_info[DUMP_RECOG].scp_dump_offset[1] = ipi_ptr->recog_dump_offset_R; } } if ((ipi_ptr->ipi_type_flag & VFFP_DUMP_IDX_MASK)) { vow_dump_info[DUMP_VFFP].scp_dump_size[0] = ipi_ptr->vffp_dump_size; vow_dump_info[DUMP_VFFP].scp_dump_offset[0] = ipi_ptr->vffp_dump_offset; /* 1st and 2nd are the same */ vow_dump_info[DUMP_VFFP].scp_dump_size[1] = ipi_ptr->vffp_dump_size; vow_dump_info[DUMP_VFFP].scp_dump_offset[1] = ipi_ptr->vffp_dump_offset_2nd_ch; } dump_package.dump_data_type = (ipi_ptr->ipi_type_flag & SCP_DUMP_DATA_MASK); spin_unlock_irqrestore(&vowdrv_dump_lock, flags); } /* IPIMSG_VOW_DATAREADY */ if ((ipi_ptr->ipi_type_flag & DEBUG_DUMP_IDX_MASK) && (vowserv.recording_flag)) { vowserv.scp_shared_voice_buf_offset = ipi_ptr->voice_buf_offset; vowserv.scp_shared_voice_length = ipi_ptr->voice_length; if (vowserv.scp_shared_voice_length > 320) VOWDRV_DEBUG("vow,v_len=%x\n", vowserv.scp_shared_voice_length); vow_service_getVoiceData(); } } void vow_ipi_rx_internal(unsigned int msg_id, void *msg_data) { switch (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 *)msg_data; /* IPIMSG_VOW_RECOGNIZE_OK */ /*VOWDRV_DEBUG("[vow] IPIMSG_VOW_COMBINED_INFO, flag=0x%x\n",*/ /* ipi_ptr->ipi_type_flag);*/ 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) { /* toggle wakelock for abort suspend flow */ __pm_stay_awake(vow_ipi_suspend_lock); __pm_relax(vow_ipi_suspend_lock); VOWDRV_DEBUG("%s(), receive recog_ok_ipi\n", __func__); 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_keywordid, ipi_ptr->confidence_lv, ipi_ptr->extra_data_len, ipi_ptr->payloaddump_len); } } } // Copy scp shared buffer into kernel internal buffer vow_ipi_rx_handle_data_msg(msg_data); break; } case IPIMSG_VOW_RETURN_VALUE: { unsigned int return_id; unsigned int return_value; unsigned int ipi_value = *(unsigned int *)msg_data; return_id = (ipi_value >> WORD_H); return_value = (ipi_value & WORD_L_MASK); VOWDRV_DEBUG("%s(), IPIMSG_VOW_RETURN_VALUE, id:%d, val:%d\r", __func__, return_id, return_value); 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; } case IPIMSG_VOW_ALEXA_ENGINE_VER: { VOWDRV_DEBUG("%s(), IPIMSG_VOW_ALEXA_ENGINE_VER %s\r", __func__, msg_data); memcpy(vowserv.alexa_engine_version, msg_data, sizeof(vowserv.alexa_engine_version)); } break; case IPIMSG_VOW_GOOGLE_ENGINE_VER: { unsigned int *temp = (unsigned int *)msg_data; 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__, msg_data); memcpy(vowserv.google_engine_arch, msg_data, sizeof(vowserv.google_engine_arch)); } break; default: break; } } bool vow_ipi_rceive_ack(unsigned int msg_id, unsigned int msg_data) { bool result = false; switch (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: case IPIMSG_VOW_SET_FLAG: result = true; break; case IPIMSG_VOW_SET_BARGEIN_ON: case IPIMSG_VOW_SET_BARGEIN_OFF: case IPIMSG_VOW_SET_BIXBY_ENGINE_MODE: result = true; break; default: VOWDRV_DEBUG("%s(), no relate msg id\r", __func__); break; } return result; } static void vow_ipi_reg_ok(short keyword, int confidence_lv, unsigned int extradata_len, unsigned int payloaddump_len) { int slot; vowserv.scp_command_flag = true; /* transfer keyword id to model handle id */ slot = vow_service_SearchSpeakerModelWithKeyword(keyword); if (slot < 0) { VOWDRV_DEBUG("%s(), Fail !! Not keyword event !!, exit\n", __func__); return; } /* vowserv.scp_command_id = vowserv.vow_speaker_model[slot].id; */ vowserv.scp_command_keywordid = keyword; vowserv.confidence_level = confidence_lv; if (extradata_len <= VOW_EXTRA_DATA_SIZE) vowserv.extradata_bytelen = extradata_len; else vowserv.extradata_bytelen = 0; #ifdef CONFIG_MTK_VOW_1STSTAGE_PCMCALLBACK vowserv.payloaddump_length = payloaddump_len; //VOWDRV_DEBUG("[vow PDR] payloaddump_length = 0x%x\n", ipi_ptr->payloaddump_length); #endif /* VOWDRV_DEBUG("%s(), extradata_bytelen = %d\r", */ /* __func__, vowserv.extradata_bytelen); */ VowDrv_Wait_Queue_flag = 1; wake_up_interruptible(&VowDrv_Wait_Queue); } #ifdef CONFIG_MTK_TINYSYS_SCP_SUPPORT 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 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) { 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*/ #ifdef CONFIG_MTK_TINYSYS_SCP_SUPPORT vowserv.voicedata_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; #ifdef CONFIG_MTK_VOW_1STSTAGE_PCMCALLBACK /* use voice data R space to exchange payload data */ vowserv.payloaddump_scp_ptr = (char *)(scp_get_reserve_mem_virt(VOW_MEM_ID)) + VOW_VOICEDATA_OFFSET + VOW_VOICEDATA_SIZE; vowserv.payloaddump_scp_addr = scp_get_reserve_mem_phys(VOW_MEM_ID) + VOW_VOICEDATA_OFFSET + VOW_VOICEDATA_SIZE; #endif #else VOWDRV_DEBUG("%s(), vow: SCP no support\n\r", __func__); #endif vow_ipi_send(IPIMSG_VOW_GET_ALEXA_ENGINE_VER, 0, NULL, VOW_IPI_BYPASS_ACK); vow_ipi_send(IPIMSG_VOW_GET_GOOGLE_ENGINE_VER, 0, NULL, VOW_IPI_BYPASS_ACK); vow_ipi_send(IPIMSG_VOW_GET_GOOGLE_ARCH, 0, NULL, VOW_IPI_BYPASS_ACK); //audio_load_task(TASK_SCENE_VOW); if (init_flag != 1) { /*Initialization*/ VowDrv_Wait_Queue_flag = 0; VoiceData_Wait_Queue_flag = 0; vowserv.recording_flag = false; vowserv.suspend_lock = 0; vowserv.scp_shared_voice_length = 0; vowserv.firstRead = false; vowserv.scp_shared_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; memset((void *)&vowserv.vow_eint_data_struct, 0, sizeof(vowserv.vow_eint_data_struct)); 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; } /* 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); vowserv.extradata_bytelen = 0; #ifdef CONFIG_MTK_VOW_1STSTAGE_PCMCALLBACK /* use voice data R space to exchange payload data */ vowserv.payloaddump_user_addr = 0; vowserv.payloaddump_user_max_size = 0; vowserv.payloaddump_user_return_size_addr = 0; vowserv.payloaddump_kernel_ptr = NULL; vowserv.payloaddump_length = 0; #endif vowserv.voicedata_kernel_ptr = NULL; mutex_lock(&voicedata_mutex); vowserv.kernel_voicedata_idx = 0; mutex_unlock(&voicedata_mutex); memset((void *)vow_dump_info, 0, sizeof(vow_dump_info)); init_flag = 1; vowserv.dump_pcm_flag = false; vowserv.split_dumpfile_flag = false; vowserv.interleave_pcmdata_ptr = NULL; vowserv.custom_model_addr = 0; vowserv.custom_model_size = 0; // update here when vow support more than 2 mic vowserv.vow_mic_number = VOW_MAX_MIC_NUM; vow_pcm_dump_init(); vowserv.scp_dual_mic_switch = VOW_ENABLE_DUAL_MIC; vowserv.mtkif_type = 0; /* set meaningless default value to platform identifier and version */ memset(vowserv.google_engine_arch, 0, VOW_ENGINE_INFO_LENGTH_BYTE); if (sprintf(vowserv.google_engine_arch, "12345678-1234-1234-1234-123456789012") < 0) VOWDRV_DEBUG("%s(), sprintf fail", __func__); vowserv.google_engine_version = DEFAULT_GOOGLE_ENGINE_VER; memset(vowserv.alexa_engine_version, 0, VOW_ENGINE_INFO_LENGTH_BYTE); } else { int ipi_size; for (I = 0; I < MAX_VOW_SPEAKER_MODEL; I++) { if ((vowserv.vow_speaker_model[I].flag > 1) || (vowserv.vow_speaker_model[I].enabled > 1)) { VOWDRV_DEBUG("reset speaker_model[%d]", 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; } } vow_ipi_buf[0] = vowserv.voicedata_scp_addr; vow_ipi_buf[1] = vowserv.extradata_addr; vow_ipi_buf[2] = VOW_EXTRA_DATA_SIZE; ipi_size = 3; #ifdef CONFIG_MTK_VOW_1STSTAGE_PCMCALLBACK vow_ipi_buf[3] = vowserv.payloaddump_scp_addr; ipi_size = 4; #endif ret = vow_ipi_send(IPIMSG_VOW_APREGDATA_ADDR, ipi_size, &vow_ipi_buf[0], VOW_IPI_BYPASS_ACK); 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 } } 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; 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); return -1; } return I; } static int vow_service_SearchSpeakerModelWithKeyword(int keyword) { int I; I = 0; do { if (vowserv.vow_speaker_model[I].keyword == keyword) { VOWDRV_DEBUG("vow Search Speaker Model By Keyword Success !, keyword:%x\n", keyword); break; } I++; } while (I < MAX_VOW_SPEAKER_MODEL); if (I == MAX_VOW_SPEAKER_MODEL) { return -1; } return I; } static bool vow_service_SendSpeakerModel(int slot, bool release_flag) { bool ret = false; #ifdef CONFIG_MTK_TINYSYS_SCP_SUPPORT 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; } 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_ipi_send(IPIMSG_VOW_SET_MODEL, 5, &vow_ipi_buf[0], VOW_IPI_BYPASS_ACK); #else VOWDRV_DEBUG("%s(), vow: SCP no support\n\r", __func__); #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\n", I, vowserv.vow_speaker_model[I].id, vowserv.vow_speaker_model[I].keyword, vowserv.vow_speaker_model[I].uuid, vowserv.vow_speaker_model[I].flag); 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); /* if IPI send fail, then just clean this model information */ if (ret == false) { VOWDRV_DEBUG("vow ipi fail, then ignore this load model\n"); 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; } #else VOWDRV_DEBUG("%s(), vow: SCP no support\n\r", __func__); #endif return ret; } static bool vow_service_SetCustomModel(unsigned long arg) { bool ret = false; struct vow_engine_info_t info; #ifdef CONFIG_MTK_TINYSYS_SCP_SUPPORT struct vow_engine_info_t *p_info = &info; phys_addr_t p_virt; phys_addr_t p_phys; phys_addr_t p_mdl_v; phys_addr_t p_mdl_p; uint32_t data_size = 0; unsigned int vow_ipi_buf[2]; #endif if (copy_from_user((void *)&info, (const void __user *)arg, sizeof(struct vow_engine_info_t))) { VOWDRV_DEBUG("vow get cust info fail\n"); return false; } #ifdef CONFIG_MTK_TINYSYS_SCP_SUPPORT p_virt = scp_get_reserve_mem_virt(VOW_MEM_ID); p_phys = scp_get_reserve_mem_phys(VOW_MEM_ID); p_mdl_v = p_virt + VOW_CUSTOM_MODEL_OFFSET; p_mdl_p = p_phys + VOW_CUSTOM_MODEL_OFFSET; if (copy_from_user((void *)&data_size, (const void __user *)p_info->return_size_addr, sizeof(uint32_t))) { VOWDRV_DEBUG("vow get cust size fail\n"); return false; } if (data_size > VOW_MAX_CUST_MODEL_SIZE || data_size == 0) { VOWDRV_DEBUG("vow set cust model fail, invalid size %ld\n", data_size); return false; } VOWDRV_DEBUG("vow set cust model, size %d\n", data_size); if (copy_from_user((void *)p_mdl_v, (const void __user *)p_info->data_addr, data_size)) { VOWDRV_DEBUG("vow copy cust model fail\n"); return false; } vowserv.custom_model_addr = (unsigned int)p_mdl_p; vowserv.custom_model_size = (unsigned long)data_size; vow_ipi_buf[0] = (unsigned int)p_mdl_p; vow_ipi_buf[1] = (unsigned long)data_size; ret = vow_ipi_send(IPIMSG_VOW_SET_CUSTOM_MODEL, 2, &vow_ipi_buf[0], VOW_IPI_BYPASS_ACK); #else VOWDRV_DEBUG("vow SCP is not supported\n"); #endif return ret; } static bool vow_service_SendModelStatus(int slot, bool enable) { bool ret = false; 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].keyword; vow_ipi_buf[1] = vowserv.vow_speaker_model[slot].confidence_lv; if (enable == VOW_MODEL_STATUS_START) { ret = vow_ipi_send(IPIMSG_VOW_MODEL_START, 2, &vow_ipi_buf[0], VOW_IPI_BYPASS_ACK); } else { /* VOW_MODEL_STATUS_STOP */ ret = vow_ipi_send(IPIMSG_VOW_MODEL_STOP, 2, &vow_ipi_buf[0], VOW_IPI_BYPASS_ACK); } 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("%s(), vow: SCP no support\n\r", __func__); #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("%s(), vow: SCP no support\n\r", __func__); #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, enabled:%d, conf_lv:%d\n", (int)model_start.handle, vowserv.vow_speaker_model[slot].enabled, vowserv.vow_speaker_model[slot].confidence_lv); /* 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_SetApDumpAddr(unsigned long arg) { unsigned long vow_info[MAX_VOW_INFO_LEN]; unsigned long id, flags; if (copy_from_user((void *)(&vow_info[0]), (const void __user *)(arg), sizeof(vowserv.vow_info_apuser))) { VOWDRV_DEBUG("%s()check Ap dump parameter fail\n", __func__); return false; } /* add return condition */ if ((vow_info[2] == 0) || (vow_info[3] != kReadVowDumpSize) || (vow_info[4] == 0) || (vow_info[0] >= NUM_DUMP_DATA)) { VOWDRV_DEBUG("%s(): error id %d, addr_%x, size_%x, addr_%x\n", __func__, (unsigned int)vow_info[0], (unsigned int)vow_info[2], (unsigned int)vow_info[3], (unsigned int)vow_info[4]); return false; } id = vow_info[0]; spin_lock_irqsave(&vowdrv_dump_lock, flags); vow_dump_info[id].user_dump_addr = vow_info[2]; vow_dump_info[id].user_dump_size = vow_info[3]; vow_dump_info[id].user_return_size_addr = vow_info[4]; vow_dump_info[id].user_dump_idx = 0; spin_unlock_irqrestore(&vowdrv_dump_lock, flags); //verb log //VOWDRV_DEBUG("%s(): id %d, addr_%x, size_%x, addr_%x\n", // __func__, // id, // (unsigned int)vow_info[2], // (unsigned int)vow_info[3], // (unsigned int)vow_info[4]); return true; } 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__); ret = vow_ipi_send(IPIMSG_VOW_ENABLE, 0, NULL, VOW_IPI_BYPASS_ACK); VOWDRV_DEBUG("-%s():%d\n", __func__, ret); return ret; } static bool vow_service_Disable(void) { bool ret = false; VOWDRV_DEBUG("+%s()\n", __func__); ret = vow_ipi_send(IPIMSG_VOW_DISABLE, 0, NULL, VOW_IPI_BYPASS_ACK); /* 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("%s(), vow: SCP no support\n\r", __func__); #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_1STSTAGE_PCMCALLBACK static void vow_service_ReadPayloadDumpData(unsigned int buf_length) { unsigned int tx_len; unsigned int ret; VOW_ASSERT(vowserv.payloaddump_kernel_ptr != NULL); // copy from DRAM to get payload data memcpy(&vowserv.payloaddump_kernel_ptr[0], vowserv.payloaddump_scp_ptr, buf_length); //copy to user space tx_len = buf_length; VOWDRV_DEBUG("[VOW PDR] buf_len=0x%x, MAX len=0x%x\n", buf_length, vowserv.payloaddump_user_max_size); if (buf_length > vowserv.payloaddump_user_max_size) tx_len = vowserv.payloaddump_user_max_size; ret = copy_to_user( (void __user *)(vowserv.payloaddump_user_return_size_addr), &tx_len, sizeof(unsigned int)); ret = copy_to_user( (void __user *)vowserv.payloaddump_user_addr, vowserv.payloaddump_kernel_ptr, tx_len); } #endif 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++; } } static int vow_service_ReadVoiceData_Internal(unsigned int buf_offset, unsigned int buf_length) { int stop_condition = 0; if (buf_length != 0) { if ((vowserv.kernel_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.kernel_voicedata_idx, buf_length); VOWDRV_DEBUG( "[vow check] user_size=0x%x(B), scp_shared_voice_buf_offset=0x%x(B)\n", (unsigned int)vowserv.voicedata_user_size, buf_offset); /* VOW_ASSERT(0); */ vowserv.kernel_voicedata_idx = 0; } mutex_lock(&vow_vmalloc_lock); #if defined DUAL_CH_TRANSFER /* start interleaving L+R */ vow_interleaving( &vowserv.voicedata_kernel_ptr[vowserv.kernel_voicedata_idx], (short *)(vowserv.voicedata_scp_ptr + buf_offset), (short *)(vowserv.voicedata_scp_ptr + buf_offset + VOW_VOICEDATA_SIZE), buf_length); /* end interleaving*/ #else memcpy(&vowserv.voicedata_kernel_ptr[vowserv.kernel_voicedata_idx], vowserv.voicedata_scp_ptr + buf_offset, buf_length); #endif 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.kernel_voicedata_idx, buf_length); vowserv.tx_keyword_start = true; } mutex_lock(&voicedata_mutex); #if defined DUAL_CH_TRANSFER /* 2 Channels */ vowserv.kernel_voicedata_idx += buf_length; #else /* 1 Channel */ vowserv.kernel_voicedata_idx += (buf_length >> 1); #endif mutex_unlock(&voicedata_mutex); } if (vowserv.kernel_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.kernel_voicedata_idx >= (vowserv.transfer_length >> 1)) { unsigned int ret; /*VOWDRV_DEBUG("TX Leng:%d, %d, [%d]\n",*/ /* vowserv.kernel_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.kernel_voicedata_idx > (vowserv.transfer_length >> 1)) { unsigned int tmp; unsigned int idx; tmp = (vowserv.kernel_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.kernel_voicedata_idx -= idx; mutex_unlock(&voicedata_mutex); } else { mutex_lock(&voicedata_mutex); vowserv.kernel_voicedata_idx = 0; mutex_unlock(&voicedata_mutex); } /* speed up voicedata transfer to hal */ if (vowserv.kernel_voicedata_idx >= VOW_VOICE_RECORD_THRESHOLD) vow_service_getVoiceData(); stop_condition = 1; } if ((vowserv.tx_keyword_start == true) && (vowserv.kernel_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.kernel_voicedata_idx); } return stop_condition; } static void vow_service_GetVowDumpData(void) { unsigned int size = 0, i = 0, ret = 0, idx = 0, user_size = 0; unsigned long flags; if (vowserv.dump_pcm_flag == false) return; for (i = 0; i < NUM_DUMP_DATA; i++) { //copy scp shared buffer to kernel struct vow_dump_info_t temp_dump_info; temp_dump_info = vow_dump_info[i]; idx = temp_dump_info.kernel_dump_idx; if (temp_dump_info.vir_addr != NULL && temp_dump_info.scp_dump_size[0] != 0) { if (vowserv.vow_mic_number == 2 && temp_dump_info.scp_dump_size[1] != 0) { /* DRAM to kernel buffer and sample interleaving */ if ((idx + (temp_dump_info.scp_dump_size[0] * 2)) > temp_dump_info.kernel_dump_size) { size = temp_dump_info.kernel_dump_size - idx; VOWDRV_DEBUG("%s(), WARNING2 idx %d + %d, %d\n", __func__, idx, temp_dump_info.scp_dump_size[0] * 2, temp_dump_info.kernel_dump_size); } else { size = temp_dump_info.scp_dump_size[0] * 2; } vow_interleaving( &temp_dump_info.kernel_dump_addr[idx], (short *)(temp_dump_info.vir_addr + temp_dump_info.scp_dump_offset[0]), (short *)(temp_dump_info.vir_addr + temp_dump_info.scp_dump_offset[1]), size / 2); idx += size; } else { // DRAM to kernel buffer. (only 1 channel) if ((idx + temp_dump_info.scp_dump_size[0]) > temp_dump_info.kernel_dump_size) { size = temp_dump_info.kernel_dump_size - idx; VOWDRV_DEBUG("%s(), WARNING1 idx %d + %d, %d\n", __func__, idx, temp_dump_info.scp_dump_size[0] * 2, temp_dump_info.kernel_dump_size); } else { size = temp_dump_info.scp_dump_size[0]; } memcpy(&temp_dump_info.kernel_dump_addr[idx], temp_dump_info.vir_addr + temp_dump_info.scp_dump_offset[0], size); idx += size; } //{ // short *outPtr; // outPtr = (short *)temp_dump_info.kernel_dump_addr; // VOWDRV_DEBUG("%s(), %d idx %d size %d 0x%x, %d %d %d %d\n", // __func__, i, idx, size, outPtr, // outPtr[0], outPtr[1], outPtr[2], outPtr[3] // ); //} //copy kernel to user space if ((temp_dump_info.user_dump_idx + idx) > temp_dump_info.user_dump_size) { size = temp_dump_info.user_dump_size - temp_dump_info.user_dump_idx; } else { size = idx; } mutex_lock(&vow_vmalloc_lock); ret = copy_to_user( (void __user *)(temp_dump_info.user_dump_addr + temp_dump_info.user_dump_idx), temp_dump_info.kernel_dump_addr, size); mutex_unlock(&vow_vmalloc_lock); temp_dump_info.user_dump_idx += size; user_size = (unsigned int)temp_dump_info.user_dump_idx; ret = copy_to_user( (void __user *)(temp_dump_info.user_return_size_addr), &user_size, sizeof(unsigned int)); /* if there are left kernel buffer not copied to user buffer, */ /* move them to buffer's head */ if (idx > size) { unsigned int size_left; unsigned int idx_left; size_left = idx - size; vow_check_boundary(size_left, temp_dump_info.kernel_dump_size); idx_left = size; mutex_lock(&vow_vmalloc_lock); memcpy(&temp_dump_info.kernel_dump_addr[0], &temp_dump_info.kernel_dump_addr[idx_left], size_left); mutex_unlock(&vow_vmalloc_lock); temp_dump_info.kernel_dump_idx = size_left; } else temp_dump_info.kernel_dump_idx = 0; spin_lock_irqsave(&vowdrv_dump_lock, flags); vow_dump_info[i].kernel_dump_idx = temp_dump_info.kernel_dump_idx; vow_dump_info[i].user_dump_idx = temp_dump_info.user_dump_idx; vow_dump_info[i].scp_dump_size[0] = 0; vow_dump_info[i].scp_dump_size[1] = 0; spin_unlock_irqrestore(&vowdrv_dump_lock, flags); if (temp_dump_info.kernel_dump_idx != 0) { VOWDRV_DEBUG("-%s(), %d kernel_idx %d\n", __func__, i, vow_dump_info[i].kernel_dump_idx); } } //if (temp_dump_info.scp_dump_size[0] != 0) } // for (i = 0; i < NUM_DUMP_DATA; i++) } static void vow_service_ReadVoiceData(void) { int stop_condition = 0; int ret = 0; /*int rdata;*/ while (1) { if (VoiceData_Wait_Queue_flag == 0) { ret = wait_event_interruptible_timeout(VoiceData_Wait_Queue, VoiceData_Wait_Queue_flag, msecs_to_jiffies(50)); if (!ret) { VOWDRV_DEBUG("%s, timeout,break\n", __func__); break; } } 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.kernel_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.scp_shared_voice_buf_offset, vowserv.scp_shared_voice_length); vowserv.scp_shared_voice_buf_offset = 0; vowserv.scp_shared_voice_length = 0; vow_service_GetVowDumpData(); } if (stop_condition == 1) break; } else { VOWDRV_DEBUG("%s, 50ms timeout,break\n", __func__); break; } } } static void vow_hal_reboot(void) { bool ret = false; VOWDRV_DEBUG("%s(), Send VOW_HAL_REBOOT ipi\n", __func__); ret = vow_ipi_send(IPIMSG_VOW_HAL_REBOOT, 0, NULL, VOW_IPI_BYPASS_ACK); if (ret == 0) VOWDRV_DEBUG("IPIMSG_VOW_HAL_REBOOT ipi send error\n\r"); } static void vow_service_reset(void) { int I; bool need_disable_vow = false; bool ret = false; VOWDRV_DEBUG("+%s()\n", __func__); vow_hal_reboot(); 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__); } #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[7] = {0}; bool ret; /* dump flag */ vow_ipi_buf[1] = vowserv.dump_pcm_flag; /* TOTAL dram resrved size for barge in dump */ vow_ipi_buf[0] = vow_dump_info[DUMP_BARGEIN].size; /* address for SCP using */ vow_ipi_buf[2] = vow_dump_info[DUMP_BARGEIN].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] = vow_dump_info[DUMP_RECOG].size; /* address for SCP using */ vow_ipi_buf[4] = vow_dump_info[DUMP_RECOG].phy_addr; /* TOTAL dram resrved size for vffp data dump */ vow_ipi_buf[5] = vow_dump_info[DUMP_VFFP].size; /* address for SCP using */ vow_ipi_buf[6] = vow_dump_info[DUMP_VFFP].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]); VOWDRV_DEBUG( "[vffp]dump on, dump flag:%d, resv sz:0x%x, addr:0x%x\n", vow_ipi_buf[1], vow_ipi_buf[5], vow_ipi_buf[6]); /* if scp reset happened, need re-send PCM dump IPI to SCP again */ if (enable == true) { ret = vow_ipi_send(IPIMSG_VOW_PCM_DUMP_ON, 7, &vow_ipi_buf[0], VOW_IPI_BYPASS_ACK); if (ret == 0) VOWDRV_DEBUG("PCM_DUMP_ON ipi send error\n"); } else { ret = vow_ipi_send(IPIMSG_VOW_PCM_DUMP_OFF, 7, &vow_ipi_buf[0], VOW_IPI_BYPASS_ACK); if (ret == 0) VOWDRV_DEBUG("PCM_DUMP_OFF ipi send error\n"); } return 0; } static bool vow_service_AllocKernelDumpBuffer(void) { unsigned int I = 0; mutex_lock(&vow_vmalloc_lock); for (I = 0; I < NUM_DUMP_DATA; I++) { if (vow_dump_info[I].kernel_dump_addr != NULL) { vfree(vow_dump_info[I].kernel_dump_addr); vow_dump_info[I].kernel_dump_addr = NULL; } vow_dump_info[I].kernel_dump_addr = vmalloc(kReadVowDumpSize); VOWDRV_DEBUG("%s vow_dump_info[%d].kernel_dump_addr = 0x%x\n", __func__, I, vow_dump_info[I].kernel_dump_addr); VOW_ASSERT(vow_dump_info[I].kernel_dump_addr != NULL); vow_dump_info[I].kernel_dump_idx = 0; vow_dump_info[I].kernel_dump_size = kReadVowDumpSize; } mutex_unlock(&vow_vmalloc_lock); return true; } static bool vow_service_FreeKernelDumpBuffer(void) { unsigned int I = 0; mutex_lock(&vow_vmalloc_lock); for (I = 0; I < NUM_DUMP_DATA; I++) { if (vow_dump_info[I].kernel_dump_addr != NULL) { vfree(vow_dump_info[I].kernel_dump_addr); vow_dump_info[I].kernel_dump_addr = NULL; } vow_dump_info[I].kernel_dump_idx = 0; vow_dump_info[I].kernel_dump_size = 0; } mutex_unlock(&vow_vmalloc_lock); return true; } #endif /* #ifdef CONFIG_MTK_TINYSYS_SCP_SUPPORT */ static int vow_pcm_dump_set(bool enable) { VOWDRV_DEBUG("%s = %d, %d\n", __func__, vowserv.dump_pcm_flag, (unsigned int)enable); #ifdef CONFIG_MTK_TINYSYS_SCP_SUPPORT vow_dump_info[DUMP_DELAY_INFO].vir_addr = (void *)&delay_info[0]; vow_dump_info[DUMP_DELAY_INFO].size = sizeof(delay_info); vow_dump_info[DUMP_BARGEIN].vir_addr = (char *)(scp_get_reserve_mem_virt(VOW_BARGEIN_MEM_ID)) + VOW_BARGEIN_AFE_MEMIF_SIZE; vow_dump_info[DUMP_BARGEIN].phy_addr = scp_get_reserve_mem_phys(VOW_BARGEIN_MEM_ID) + VOW_BARGEIN_AFE_MEMIF_SIZE; vow_dump_info[DUMP_BARGEIN].size = BARGEIN_DUMP_TOTAL_BYTE_CNT; VOWDRV_DEBUG("[Barge]vir: %p, phys: 0x%x\n", vow_dump_info[DUMP_BARGEIN].vir_addr, (unsigned int)vow_dump_info[DUMP_BARGEIN].phy_addr); // input share same address of bargein vow_dump_info[DUMP_INPUT].vir_addr = vow_dump_info[DUMP_BARGEIN].vir_addr; vow_dump_info[DUMP_INPUT].phy_addr = vow_dump_info[DUMP_BARGEIN].phy_addr; vow_dump_info[DUMP_RECOG].vir_addr = (char *)(scp_get_reserve_mem_virt(VOW_MEM_ID)) + VOW_RECOGDATA_OFFSET; vow_dump_info[DUMP_RECOG].phy_addr = scp_get_reserve_mem_phys(VOW_MEM_ID) + VOW_RECOGDATA_OFFSET; vow_dump_info[DUMP_RECOG].size = RECOG_DUMP_TOTAL_BYTE_CNT; VOWDRV_DEBUG("[Recog]vir: %p, phys: 0x%x\n", vow_dump_info[DUMP_RECOG].vir_addr, (unsigned int)vow_dump_info[DUMP_RECOG].phy_addr); vow_dump_info[DUMP_VFFP].vir_addr = (char *)(scp_get_reserve_mem_virt(VOW_MEM_ID)) + VOW_VFFPDATA_OFFSET; vow_dump_info[DUMP_VFFP].phy_addr = scp_get_reserve_mem_phys(VOW_MEM_ID) + VOW_VFFPDATA_OFFSET; vow_dump_info[DUMP_VFFP].size = VFFP_DUMP_TOTAL_BYTE_CNT; VOWDRV_DEBUG("[vffp]vir: %p, phys: 0x%x\n", vow_dump_info[DUMP_VFFP].vir_addr, (unsigned int)vow_dump_info[DUMP_VFFP].phy_addr); if ((vowserv.dump_pcm_flag == false) && (enable == true)) { vowserv.dump_pcm_flag = true; vow_service_AllocKernelDumpBuffer(); 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_FreeKernelDumpBuffer(); } #else VOWDRV_DEBUG("%s(), vow: SCP no support\n\r", __func__); #endif /* #ifdef CONFIG_MTK_TINYSYS_SCP_SUPPORT */ return 0; } static void vow_pcm_dump_init(void) { VOWDRV_DEBUG("[Recog] %s()\n", __func__); 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); } static void vow_pcm_dump_deinit(void) { VOWDRV_DEBUG("[BargeIn] %s()\n", __func__); if (vowserv.interleave_pcmdata_ptr != NULL) { vfree(vowserv.interleave_pcmdata_ptr); vowserv.interleave_pcmdata_ptr = NULL; } } /***************************************************************************** * 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; ret = vow_ipi_send(IPIMSG_VOW_SET_SMART_DEVICE, 2, &vow_ipi_buf[0], VOW_IPI_BYPASS_ACK); if (ret == 0) { VOWDRV_DEBUG( "IPIMSG_VOW_SET_SMART_DEVICE ipi send error\n\r"); } } 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; ret = vow_ipi_send(IPIMSG_VOW_SET_FLAG, 2, &vow_ipi_buf[0], VOW_IPI_BYPASS_ACK); if (ret == 0) VOWDRV_DEBUG("IPIMSG_VOW_SET_FLAG ipi send error\n\r"); 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) { char cstr[35]; int size = sizeof(cstr); if (vowserv.scp_dual_mic_switch == VOW_ENABLE_DUAL_MIC) return snprintf(buf, size, "use Daul mic\n"); else if (vowserv.scp_dual_mic_switch == VOW_ENABLE_SINGLE_MAIN_MIC) return snprintf(buf, size, "use Single mic: main mic\n"); else if (vowserv.scp_dual_mic_switch == VOW_ENABLE_SINGLE_REF_MIC) return snprintf(buf, size, "use Single mic: ref mic\n"); else return snprintf(buf, size, "set mic error\n"); } 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_dual_mic_switch = enable; VowDrv_SetFlag(VOW_FLAG_DUAL_MIC_SWITCH, enable); return n; } DEVICE_ATTR(vow_DualMicSwitch, 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("%s(), set = %d, irq = %d\n", __func__, set, irq_id); if (set == 1) { vow_register_feature(VOW_BARGEIN_FEATURE_ID); ret = vow_ipi_send(IPIMSG_VOW_SET_BARGEIN_ON, 1, &vow_ipi_buf[0], VOW_IPI_NEED_ACK); } else if (set == 0) { ret = vow_ipi_send(IPIMSG_VOW_SET_BARGEIN_OFF, 1, &vow_ipi_buf[0], VOW_IPI_NEED_ACK); 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 ssize_t VowDrv_GetMCPSflag(struct device *kobj, struct device_attribute *attr, char *buf) { unsigned int stat; char cstr[35]; int size = sizeof(cstr); stat = (vowserv.mcps_flag == true) ? 1 : 0; return snprintf(buf, size, "Enable Measure MCPS = %s\n", (stat == 0x1) ? "YES" : "NO"); } static ssize_t VowDrv_SetMCPSflag(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_MCPS, enable); vowserv.mcps_flag = (enable == 1) ? true : false; VOWDRV_DEBUG("%s(),enable=%d\n", __func__, enable); return n; } DEVICE_ATTR(vow_SetMCPS, 0644, /*S_IWUSR | S_IRUGO*/ VowDrv_GetMCPSflag, VowDrv_SetMCPSflag); 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 bool VowDrv_SetWakeupMode(unsigned int mode) { bool ret = false; unsigned int vow_ipi_buf[1]; VOWDRV_DEBUG("%s(), set:%x\n", __func__, mode); vow_ipi_buf[0] = mode; ret = vow_ipi_send(IPIMSG_VOW_SET_BIXBY_ENGINE_MODE, 1, &vow_ipi_buf[0], VOW_IPI_NEED_ACK); if (ret == 0) VOWDRV_DEBUG("IPIMSG_VOW_SET_BIXBY_ENGINE_MODE ipi send error\n\r"); return ret; } 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.kernel_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; case VOWControlCmd_EnableDump: vow_pcm_dump_set(true); break; case VOWControlCmd_DisableDump: vow_pcm_dump_set(false); break; case VOWControlCmd_Mic_Single: vowserv.vow_mic_number = 1; VOWDRV_DEBUG("VOW_SET_CONTROL Set Single Mic VOW"); break; case VOWControlCmd_Mic_Dual: vowserv.vow_mic_number = 2; VOWDRV_DEBUG("VOW_SET_CONTROL Set Dual Mic VOW"); break; 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_DSP_AEC_PARAMETER: VOWDRV_DEBUG("VOW_SET_DSP_AEC_PARAMETER(%lu)", arg); if (!vow_service_SetCustomModel(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: //VOWDRV_DEBUG("VOW_READ_VOW_DUMP_DATA(%lu)", arg); if (!vow_service_SetApDumpAddr(arg)) ret = -EFAULT; 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: if (arg != 0) { VOWDRV_DEBUG("VOW_CHECK_STATUS(%lu) para err, break", arg); break; } /* 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_info_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_info_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; #ifdef CONFIG_MTK_VOW_1STSTAGE_PCMCALLBACK case VOW_SET_PAYLOADDUMP_INFO: { struct vow_payloaddump_info_t payloaddump_temp; copy_from_user((void *)&payloaddump_temp, (const void __user *)arg, sizeof(struct vow_payloaddump_info_t)); vowserv.payloaddump_user_addr = payloaddump_temp.return_payloaddump_addr; vowserv.payloaddump_user_max_size = payloaddump_temp.max_payloaddump_size; vowserv.payloaddump_user_return_size_addr = payloaddump_temp.return_payloaddump_size_addr; pr_debug("-VOW_SET_PAYLOADDUMP_INFO(addr=%lu, sz=%lu)", vowserv.payloaddump_user_addr, vowserv.payloaddump_user_max_size); if (vowserv.payloaddump_kernel_ptr != NULL) { vfree(vowserv.payloaddump_kernel_ptr); vowserv.payloaddump_kernel_ptr = NULL; } if (vowserv.payloaddump_user_max_size > 0) { vowserv.payloaddump_kernel_ptr = vmalloc(vowserv.payloaddump_user_max_size); } else { ret = -EFAULT; } } break; #endif case VOW_SET_WAKEUP_MODE: VOWDRV_DEBUG("VOW_SET_WAKEUP_MODE, irq: %d", (unsigned int)arg); if (!VowDrv_SetWakeupMode((unsigned int)arg)) ret = -EFAULT; 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: case VOW_GET_GOOGLE_ENGINE_VER: case VOW_SET_WAKEUP_MODE: 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_SET_DSP_AEC_PARAMETER: case VOW_GET_GOOGLE_ARCH: case VOW_GET_ALEXA_ENGINE_VER: { struct vow_engine_info_kernel_t __user *data32; struct vow_engine_info_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; case VOW_SET_PAYLOADDUMP_INFO: { struct vow_payloaddump_info_kernel_t __user *data32; struct vow_payloaddump_info_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_payloaddump_addr); err |= put_user(l, &data->return_payloaddump_addr); err |= get_user(l, &data32->return_payloaddump_size_addr); err |= put_user(l, &data->return_payloaddump_size_addr); err |= get_user(l, &data32->max_payloaddump_size); err |= put_user(l, &data->max_payloaddump_size); 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; unsigned int ret_data = 0; int slot = 0; bool dsp_inform_tx_flag = false; VOWDRV_DEBUG("+%s()+\n", __func__); if (count != sizeof(struct vow_eint_data_struct_t)) { VOWDRV_DEBUG( "%s(), cpy incorrect size to user, size=%d, correct size=%d, exit\n", __func__, count, sizeof(struct vow_eint_data_struct_t)); goto exit; } 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()); } } } slot = vow_service_SearchSpeakerModelWithKeyword( vowserv.scp_command_keywordid); if (slot < 0) { /* there is no pair id */ VOWDRV_DEBUG("%s(), search ID fail, not keyword event, eint=%d, exit\n", __func__, VowDrv_QueryVowEINTStatus()); vowserv.scp_command_id = 0; vowserv.confidence_level = 0; goto exit; } else { vowserv.scp_command_id = vowserv.vow_speaker_model[slot].id; } memset((void *)&vowserv.vow_eint_data_struct, 0, sizeof(vowserv.vow_eint_data_struct)); 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; 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; mutex_lock(&vow_extradata_mutex); if (vowserv.extradata_mem_ptr == NULL) { mutex_unlock(&vow_extradata_mutex); goto exit; } if (vowserv.extradata_ptr == NULL) { mutex_unlock(&vow_extradata_mutex); goto exit; } mutex_unlock(&vow_extradata_mutex); 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); #if 0 for (i = 0; i < vowserv.extradata_bytelen; i++) { VOWDRV_DEBUG("data[%d] = %x\n", i, *(vowserv.extradata_ptr + i)); } #endif #ifdef CONFIG_MTK_VOW_1STSTAGE_PCMCALLBACK vow_service_ReadPayloadDumpData(vowserv.payloaddump_length); #endif /* 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); } mutex_lock(&vow_extradata_mutex); ret_data = copy_to_user( (void __user *) vowserv.vow_speaker_model[slot].rx_inform_addr, vowserv.extradata_mem_ptr, vowserv.extradata_bytelen); mutex_unlock(&vow_extradata_mutex); if (ret_data != 0) { /* fail, print the fail size */ VOWDRV_DEBUG("[vow dsp inform2]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) { bool ret = false; VOWDRV_DEBUG("%s(), Send VOW_FLUSH ipi\n", __func__); ret = vow_ipi_send(IPIMSG_VOW_FLUSH, 0, NULL, VOW_IPI_BYPASS_ACK); if (ret == 0) VOWDRV_DEBUG("IPIMSG_VOW_FLUSH ipi send error\n\r"); 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; } bool vow_service_GetScpRecoverStatus(void) { return vowserv.scp_recovering; } bool vow_service_GetVowRecoverStatus(void) { return vowserv.vow_recovering; } /***************************************************************************** * 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; bool ret = false; unsigned int vow_ipi_buf[2]; 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"); } // send AEC custom model vow_ipi_buf[0] = vowserv.custom_model_addr; vow_ipi_buf[1] = vowserv.custom_model_size; ret = vow_ipi_send(IPIMSG_VOW_SET_CUSTOM_MODEL, 2, &vow_ipi_buf[0], VOW_IPI_BYPASS_ACK); if (!ret) VOWDRV_DEBUG("fail: vow_service_SetCustomModel\n"); /* if vow is not enable, then return */ if (VowDrv_GetHWStatus() != VOW_PWR_ON) { vowserv.vow_recovering = false; VOWDRV_DEBUG("fail: vow not enable\n"); 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__); VowDrv_setup_smartdev_eint(dev); return 0; } static int VowDrv_remove(struct platform_device *dev) { VOWDRV_DEBUG("%s()\n", __func__); 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 __init 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_DualMicSwitch); 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; ret = device_create_file(VowDrv_misc_device.this_device, &dev_attr_vow_SetMCPS); if (unlikely(ret != 0)) return ret; /* ipi register */ vow_ipi_register(vow_ipi_rx_internal, vow_ipi_rceive_ack); vow_service_Init(); #ifdef CONFIG_MTK_TINYSYS_SCP_SUPPORT scp_A_register_notify(&vow_scp_recover_notifier); #endif vow_suspend_lock = wakeup_source_register(NULL, "vow wakelock"); vow_ipi_suspend_lock = wakeup_source_register(NULL, "vow ipi wakelock"); if (!vow_suspend_lock) pr_debug("vow wakeup source init failed.\n"); if (!vow_ipi_suspend_lock) pr_debug("vow ipi wakeup source init failed.\n"); VOWDRV_DEBUG("-%s(): Init Audio WakeLock\n", __func__); return 0; } static void __exit VowDrv_mod_exit(void) { VOWDRV_DEBUG("+%s()\n", __func__); wakeup_source_unregister(vow_suspend_lock); wakeup_source_unregister(vow_ipi_suspend_lock); vow_pcm_dump_deinit(); /* 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); VOWDRV_DEBUG("-%s()\n", __func__); } late_initcall(VowDrv_mod_init); module_exit(VowDrv_mod_exit); /***************************************************************************** * License *****************************************************************************/ MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("MediaTek VOW Driver"); MODULE_AUTHOR("Charlie Lu");