| // SPDX-License-Identifier: GPL-2.0 |
| /* |
| * vow.c -- VoW platform driver |
| * |
| * Copyright (c) 2020 MediaTek Inc. |
| * Author: Michael HSiao <michael.hsiao@mediatek.com> |
| */ |
| |
| /***************************************************************************** |
| * Header Files |
| *****************************************************************************/ |
| |
| #include <linux/init.h> |
| #include <linux/module.h> |
| #include <linux/kernel.h> |
| #include <linux/types.h> |
| #include <linux/device.h> |
| #include <linux/kdev_t.h> |
| #include <linux/fs.h> |
| #include <linux/cdev.h> |
| #include <linux/platform_device.h> |
| #include <linux/dma-mapping.h> |
| #include <linux/mm_types.h> |
| #include <linux/mm.h> |
| #include <linux/jiffies.h> |
| #include <linux/sched.h> |
| #include <linux/uaccess.h> |
| #include <asm/page.h> |
| #include <linux/vmalloc.h> |
| #include <linux/interrupt.h> |
| #include <linux/wait.h> |
| #include <linux/spinlock.h> |
| #include <linux/delay.h> |
| #include <linux/semaphore.h> |
| #include <linux/platform_device.h> |
| #include <linux/slab.h> |
| #include <linux/printk.h> |
| #include <linux/miscdevice.h> |
| /* #include <linux/wakelock.h> */ |
| #include <linux/pm_wakeup.h> |
| #include <linux/compat.h> |
| #include <linux/uaccess.h> |
| #include <linux/sysfs.h> |
| #include <linux/notifier.h> /* FOR SCP REVOCER */ |
| #ifdef SIGTEST |
| #include <asm/siginfo.h> |
| #endif |
| #include <linux/of.h> |
| #include <linux/of_address.h> |
| #include <linux/of_irq.h> |
| #include <linux/kthread.h> |
| #include <linux/pinctrl/consumer.h> |
| #include <uapi/linux/sched/types.h> |
| #if IS_ENABLED(CONFIG_MTK_TINYSYS_SCP_SUPPORT) |
| #include "scp.h" |
| #endif /* #if IS_ENABLED(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 unsigned int DumpData_Wait_Queue_flag; |
| static DECLARE_WAIT_QUEUE_HEAD(VowDrv_Wait_Queue); |
| static DECLARE_WAIT_QUEUE_HEAD(VoiceData_Wait_Queue); |
| static DECLARE_WAIT_QUEUE_HEAD(DumpData_Wait_Queue); |
| static DEFINE_SPINLOCK(vowdrv_lock); |
| static DEFINE_SPINLOCK(vowdrv_dump_lock); |
| static DEFINE_SPINLOCK(vow_rec_queue_lock); |
| static struct wakeup_source *vow_suspend_lock; |
| static struct wakeup_source *vow_ipi_suspend_lock; |
| static struct wakeup_source *vow_dump_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 |
| static const uint32_t kReadVowDumpSize_Big = 0x1F40 * 2; // 320(10ms) x 25 x 2ch= 16000 = 0x3E80 |
| |
| /***************************************************************************** |
| * Function Declaration |
| ****************************************************************************/ |
| static void vow_service_getVoiceData(void); |
| static void vow_service_getDumpData(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 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); |
| #if IS_ENABLED(CONFIG_MTK_VOW_1STSTAGE_PCMCALLBACK) |
| static void vow_service_ReadPayloadDumpData(unsigned int buf_length); |
| static DEFINE_MUTEX(vow_payloaddump_mutex); |
| #endif |
| static DEFINE_MUTEX(vow_vmalloc_lock); |
| static DEFINE_MUTEX(vow_extradata_mutex); |
| static DEFINE_MUTEX(vow_sendspkmdl_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; |
| #if IS_ENABLED(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; |
| short *interleave_pcmdata_ptr; |
| bool dump_pcm_flag; |
| bool scp_recovering; |
| bool vow_recovering; |
| 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; |
| unsigned int vow_speaker_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 reserved buffer |
| char *vir_addr; // virtual (kernel) addess of reserved buffer |
| uint32_t size; // size of reserved 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; |
| unsigned long rec_queue_flags; |
| |
| ipi_ptr = (struct vow_ipi_combined_info_t *)msg_data; |
| |
| 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]); |
| } |
| if ((ipi_ptr->mic_offset < BARGEIN_DUMP_BYTE_CNT_MIC) && |
| (ipi_ptr->mic_dump_size == (BARGEIN_DUMP_BYTE_CNT_MIC >> 1))) { |
| 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) { |
| if (ipi_ptr->mic_offset_R < |
| (BARGEIN_DUMP_BYTE_CNT_MIC << 1)) { |
| 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; |
| } else { |
| VOWDRV_DEBUG("%s mic_offset_R = %x\n", |
| __func__, ipi_ptr->mic_offset_R); |
| return; |
| } |
| } |
| } else { |
| VOWDRV_DEBUG("%s BARGEIN_DUMP_BYTE_CNT_MIC=%x, mic_offset=%x\n", |
| __func__, |
| (unsigned int)BARGEIN_DUMP_BYTE_CNT_MIC, |
| ipi_ptr->mic_offset); |
| VOWDRV_DEBUG("%s (BYTE_CNT_MIC>>1)=%x, mic_dump_size=%x\n", |
| __func__, |
| (unsigned int)(BARGEIN_DUMP_BYTE_CNT_MIC >> 1), |
| ipi_ptr->mic_dump_size); |
| return; |
| } |
| } |
| /* IPIMSG_VOW_BARGEIN_PCMDUMP_OK */ |
| if ((ipi_ptr->ipi_type_flag & BARGEIN_DUMP_IDX_MASK)) { |
| if ((vowserv.vow_mic_number == 2) && |
| (ipi_ptr->echo_offset > BARGEIN_DUMP_TOTAL_BYTE_CNT)) { |
| VOWDRV_DEBUG("%s BARGEIN_DUMP_TOTAL_BYTE_CNT = %x\n", |
| __func__, |
| (unsigned int)BARGEIN_DUMP_TOTAL_BYTE_CNT); |
| VOWDRV_DEBUG("%s VOW_MAX_MIC_NUM = %x, echo_offset = %x\n", |
| __func__, |
| (unsigned int)VOW_MAX_MIC_NUM, |
| ipi_ptr->echo_offset); |
| return; |
| } else if ((vowserv.vow_mic_number == 1) && |
| (ipi_ptr->echo_offset > BARGEIN_DUMP_BYTE_CNT_ECHO)) { |
| VOWDRV_DEBUG("%s BARGEIN_DUMP_BYTE_CNT_ECHO=%x, echo_offset=%x\n", |
| __func__, |
| (unsigned int)BARGEIN_DUMP_BYTE_CNT_ECHO, |
| ipi_ptr->echo_offset); |
| return; |
| } |
| 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 & AECOUT_DUMP_IDX_MASK)) { |
| if ((ipi_ptr->aecout_dump_offset < AECOUT_DUMP_BYTE_CNT) && |
| (ipi_ptr->aecout_dump_size == (AECOUT_DUMP_BYTE_CNT >> 1))) { |
| vow_dump_info[DUMP_AECOUT].scp_dump_size[0] = |
| ipi_ptr->aecout_dump_size; |
| vow_dump_info[DUMP_AECOUT].scp_dump_offset[0] = |
| ipi_ptr->aecout_dump_offset; |
| if (vowserv.vow_mic_number == 2) { |
| if (ipi_ptr->aecout_dump_offset_R < |
| (AECOUT_DUMP_BYTE_CNT << 1)) { |
| vow_dump_info[DUMP_AECOUT].scp_dump_size[1] = |
| ipi_ptr->aecout_dump_size; |
| vow_dump_info[DUMP_AECOUT].scp_dump_offset[1] = |
| ipi_ptr->aecout_dump_offset_R; |
| } else { |
| VOWDRV_DEBUG("%s AECOUT_DUMP_BYTE_CNT = %x\n", |
| __func__, |
| (unsigned int)AECOUT_DUMP_BYTE_CNT); |
| VOWDRV_DEBUG("%s aecout_dump_offset_R = %x\n", |
| __func__, |
| ipi_ptr->aecout_dump_offset_R); |
| return; |
| } |
| } |
| } else { |
| VOWDRV_DEBUG("%s AECOUT_DUMP_BYTE_CNT=%x, aecout_dump_offset=%x", |
| __func__, |
| (unsigned int)AECOUT_DUMP_BYTE_CNT, |
| ipi_ptr->aecout_dump_offset); |
| VOWDRV_DEBUG("%s (AECOUT_DUMP_BYTE>>1)=%x, aecout_size=%x\n", |
| __func__, |
| (unsigned int)(AECOUT_DUMP_BYTE_CNT >> 1), |
| ipi_ptr->aecout_dump_size); |
| return; |
| } |
| } |
| if ((ipi_ptr->ipi_type_flag & VFFPOUT_DUMP_IDX_MASK)) { |
| if ((ipi_ptr->vffpout_dump_offset < VFFPOUT_DUMP_BYTE_CNT) && |
| (ipi_ptr->vffpout_dump_offset_2nd_ch < (VFFPOUT_DUMP_BYTE_CNT << 1)) && |
| (ipi_ptr->vffpout_dump_size == (VFFPOUT_DUMP_BYTE_CNT >> 1))) { |
| vow_dump_info[DUMP_VFFPOUT].scp_dump_size[0] = |
| ipi_ptr->vffpout_dump_size; |
| vow_dump_info[DUMP_VFFPOUT].scp_dump_offset[0] = |
| ipi_ptr->vffpout_dump_offset; |
| /* 1st and 2nd are the same */ |
| vow_dump_info[DUMP_VFFPOUT].scp_dump_size[1] = |
| ipi_ptr->vffpout_dump_size; |
| vow_dump_info[DUMP_VFFPOUT].scp_dump_offset[1] = |
| ipi_ptr->vffpout_dump_offset_2nd_ch; |
| } else { |
| VOWDRV_DEBUG("%s VFFPOUT_DUMP_BYTE=%x, vffpout_offset=%x\n", |
| __func__, |
| (unsigned int)VFFPOUT_DUMP_BYTE_CNT, |
| ipi_ptr->vffpout_dump_offset); |
| VOWDRV_DEBUG("%s vffpout_dump_offset_2nd_ch = %x\n", |
| __func__, ipi_ptr->vffpout_dump_offset_2nd_ch); |
| VOWDRV_DEBUG("%s (VFFPOUT_DUMP_BYTE>>1)=%x, vffpout_size=%x\n", |
| __func__, |
| (unsigned int)(VFFPOUT_DUMP_BYTE_CNT >> 1), |
| ipi_ptr->vffpout_dump_size); |
| return; |
| } |
| } |
| if ((ipi_ptr->ipi_type_flag & VFFPIN_DUMP_IDX_MASK)) { |
| if ((ipi_ptr->vffpin_dump_offset < VFFPIN_DUMP_BYTE_CNT) && |
| (ipi_ptr->vffpin_dump_size == (VFFPIN_DUMP_BYTE_CNT >> 1))) { |
| vow_dump_info[DUMP_VFFPIN].scp_dump_size[0] = |
| ipi_ptr->vffpin_dump_size; |
| vow_dump_info[DUMP_VFFPIN].scp_dump_offset[0] = |
| ipi_ptr->vffpin_dump_offset; |
| if (vowserv.vow_mic_number == 2) { |
| if (ipi_ptr->vffpin_dump_offset_R < |
| (VFFPIN_DUMP_BYTE_CNT << 1)) { |
| vow_dump_info[DUMP_VFFPIN].scp_dump_size[1] = |
| ipi_ptr->vffpin_dump_size; |
| vow_dump_info[DUMP_VFFPIN].scp_dump_offset[1] = |
| ipi_ptr->vffpin_dump_offset_R; |
| } else { |
| VOWDRV_DEBUG("%s VFFPIN_DUMP_BYTE_CNT = %x\n", |
| __func__, |
| (unsigned int)VFFPIN_DUMP_BYTE_CNT); |
| VOWDRV_DEBUG("%s vffpin_dump_offset_R = %x\n", |
| __func__, |
| ipi_ptr->vffpin_dump_offset_R); |
| return; |
| } |
| } |
| } else { |
| VOWDRV_DEBUG("%s VFFPIN_DUMP_BYTE_CNT=%x, vffpin_dump_offset=%x\n", |
| __func__, |
| (unsigned int)VFFPIN_DUMP_BYTE_CNT, |
| ipi_ptr->vffpin_dump_offset); |
| VOWDRV_DEBUG("%s vffpin_dump_offset_R = %x\n", |
| __func__, ipi_ptr->vffpin_dump_offset_R); |
| VOWDRV_DEBUG("%s (VFFPIN_DUMP_BYTE>>1)=%x, vffpin_size=%x\n", |
| __func__, |
| (unsigned int)(VFFPIN_DUMP_BYTE_CNT >> 1), |
| ipi_ptr->vffpin_dump_size); |
| return; |
| } |
| } |
| spin_unlock_irqrestore(&vowdrv_dump_lock, flags); |
| vow_service_getDumpData(); |
| } |
| /* IPIMSG_VOW_DATAREADY */ |
| if ((ipi_ptr->ipi_type_flag & DEBUG_DUMP_IDX_MASK) && |
| (vowserv.recording_flag)) { |
| spin_lock_irqsave(&vow_rec_queue_lock, rec_queue_flags); |
| vowserv.scp_shared_voice_buf_offset = ipi_ptr->voice_buf_offset; |
| vowserv.scp_shared_voice_length = ipi_ptr->voice_length; |
| spin_unlock_irqrestore(&vow_rec_queue_lock, rec_queue_flags); |
| 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__, (char *)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__, (char *)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: |
| 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; |
| #if IS_ENABLED(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); |
| } |
| #if IS_ENABLED(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"); */ |
| } |
| } |
| |
| static void vow_service_getDumpData(void) |
| { |
| if (DumpData_Wait_Queue_flag == 0) { |
| DumpData_Wait_Queue_flag = 1; |
| wake_up_interruptible(&DumpData_Wait_Queue); |
| } else { |
| /* VOWDRV_DEBUG("getDumpData 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]; |
| unsigned long rec_queue_flags; |
| |
| VOWDRV_DEBUG("%s(): %x\n", __func__, init_flag); |
| /* common part */ |
| vowserv.scp_command_flag = false; |
| vowserv.tx_keyword_start = false; |
| /*Initialization*/ |
| #if IS_ENABLED(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; |
| #if IS_ENABLED(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; |
| DumpData_Wait_Queue_flag = 0; |
| vowserv.recording_flag = false; |
| vowserv.suspend_lock = 0; |
| vowserv.firstRead = false; |
| spin_lock_irqsave(&vow_rec_queue_lock, rec_queue_flags); |
| vowserv.scp_shared_voice_buf_offset = 0; |
| vowserv.scp_shared_voice_length = 0; |
| spin_unlock_irqrestore(&vow_rec_queue_lock, rec_queue_flags); |
| 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; |
| #if IS_ENABLED(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; |
| mutex_lock(&vow_payloaddump_mutex); |
| vowserv.payloaddump_kernel_ptr = NULL; |
| mutex_unlock(&vow_payloaddump_mutex); |
| vowserv.payloaddump_length = 0; |
| #endif |
| vowserv.voicedata_kernel_ptr = NULL; |
| mutex_lock(&vow_vmalloc_lock); |
| vowserv.kernel_voicedata_idx = 0; |
| mutex_unlock(&vow_vmalloc_lock); |
| 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; |
| vowserv.vow_speaker_number = VOW_DEFAULT_SPEAKER_NUM; |
| vowserv.scp_dual_mic_switch = VOW_ENABLE_DUAL_MIC; |
| vowserv.mtkif_type = 0; |
| //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, "32fe89be-5205-3d4b-b8cf-55d650d9d200") < 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; |
| #if IS_ENABLED(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", |
| (unsigned int)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%lx, 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%lx, uuid %d, data 0x%lx\n", |
| vowserv.vow_info_apuser[4], |
| vowserv.vow_info_apuser[5], |
| vowserv.vow_info_apuser[6]); |
| |
| return 0; |
| } |
| |
| #if IS_ENABLED(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 /* #if IS_ENABLED(CONFIG_MTK_TINYSYS_SCP_SUPPORT) */ |
| |
| static int vow_service_FindFreeSpeakerModel(void) |
| { |
| int I; |
| |
| I = 0; |
| VOWDRV_DEBUG("vow FMAX_VOW_SPEAKER_MODEL: %d\n", MAX_VOW_SPEAKER_MODEL); |
| 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; |
| #if IS_ENABLED(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("%s(), ready to send model\n", __func__); |
| #if IS_ENABLED(CONFIG_MTK_TINYSYS_SCP_DEBUG_SUPPORT) |
| 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]); |
| #endif |
| |
| 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); |
| |
| mutex_lock(&vow_sendspkmdl_mutex); |
| 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; |
| mutex_unlock(&vow_sendspkmdl_mutex); |
| |
| return ret; |
| } |
| |
| static bool vow_service_SetSpeakerModel(unsigned long arg) |
| { |
| bool ret = false; |
| int I; |
| #if IS_ENABLED(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; |
| #if IS_ENABLED(CONFIG_MTK_TINYSYS_SCP_SUPPORT) |
| mutex_lock(&vow_sendspkmdl_mutex); |
| vowserv.vow_speaker_model[I].model_ptr = |
| (void *)(scp_get_reserve_mem_virt(VOW_MEM_ID)) |
| + (VOW_MODEL_SIZE * I); |
| mutex_unlock(&vow_sendspkmdl_mutex); |
| |
| if (vow_service_CopyModel(I) != 0) |
| return false; |
| |
| mutex_lock(&vow_sendspkmdl_mutex); |
| 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]); |
| mutex_unlock(&vow_sendspkmdl_mutex); |
| |
| 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"); |
| mutex_lock(&vow_sendspkmdl_mutex); |
| 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; |
| mutex_unlock(&vow_sendspkmdl_mutex); |
| } |
| #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; |
| struct vow_engine_info_t *p_info = &info; |
| #if IS_ENABLED(CONFIG_MTK_TINYSYS_SCP_SUPPORT) |
| 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; |
| } |
| #if IS_ENABLED(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 %d\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) |
| { |
| #if IS_ENABLED(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) |
| { |
| #if IS_ENABLED(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[3] != kReadVowDumpSize_Big)) || (vow_info[4] == 0) || |
| (vow_info[0] >= NUM_DUMP_DATA)) { |
| VOWDRV_DEBUG("%s(), vow SetdumpAddr error!\n", __func__); |
| #if IS_ENABLED(CONFIG_MTK_TINYSYS_SCP_DEBUG_SUPPORT) |
| 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]); |
| #endif |
| 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("%s(), vow SetApAddr error!\n", __func__); |
| #if IS_ENABLED(CONFIG_MTK_TINYSYS_SCP_DEBUG_SUPPORT) |
| VOWDRV_DEBUG("%s(), vow SetApAddr:addr_%x, size_%x, addr_%x\n", |
| __func__, |
| (unsigned int)vow_info[2], |
| (unsigned int)vow_info[3], |
| (unsigned int)vow_info[4]); |
| #endif |
| 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("%s(), vow SetVBufAddr error!\n", __func__); |
| #if IS_ENABLED(CONFIG_MTK_TINYSYS_SCP_DEBUG_SUPPORT) |
| 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]); |
| #endif |
| 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); |
| 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); |
| |
| /* 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); |
| } |
| #if IS_ENABLED(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); |
| } |
| } |
| |
| #if IS_ENABLED(CONFIG_MTK_VOW_1STSTAGE_PCMCALLBACK) |
| static void vow_service_ReadPayloadDumpData(unsigned int buf_length) |
| { |
| unsigned int tx_len; |
| unsigned int ret; |
| if (vowserv.payloaddump_kernel_ptr == NULL) { |
| VOWDRV_DEBUG("%s(), payloaddump_kernel_ptr is NULL!!\n", __func__); |
| return; |
| } |
| if (vowserv.payloaddump_user_max_size == 0) { |
| VOWDRV_DEBUG("%s(), MAX len = 0 !!\n", __func__); |
| return; |
| } |
| // copy from DRAM to get payload data |
| mutex_lock(&vow_payloaddump_mutex); |
| memcpy(&vowserv.payloaddump_kernel_ptr[0], |
| vowserv.payloaddump_scp_ptr, buf_length); |
| mutex_unlock(&vow_payloaddump_mutex); |
| |
| //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)); |
| mutex_lock(&vow_payloaddump_mutex); |
| ret = copy_to_user( |
| (void __user *)vowserv.payloaddump_user_addr, |
| vowserv.payloaddump_kernel_ptr, |
| tx_len); |
| mutex_unlock(&vow_payloaddump_mutex); |
| } |
| #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); */ |
| mutex_lock(&vow_vmalloc_lock); |
| vowserv.kernel_voicedata_idx = 0; |
| mutex_unlock(&vow_vmalloc_lock); |
| } |
| mutex_lock(&vow_vmalloc_lock); |
| #if IS_ENABLED(CONFIG_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(&vow_vmalloc_lock); |
| #if IS_ENABLED(CONFIG_DUAL_CH_TRANSFER) |
| /* 2 Channels */ |
| vowserv.kernel_voicedata_idx += buf_length; |
| #else |
| /* 1 Channel */ |
| vowserv.kernel_voicedata_idx += (buf_length >> 1); |
| #endif |
| mutex_unlock(&vow_vmalloc_lock); |
| } |
| 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(&vow_vmalloc_lock); |
| vowserv.kernel_voicedata_idx -= idx; |
| mutex_unlock(&vow_vmalloc_lock); |
| } else { |
| mutex_lock(&vow_vmalloc_lock); |
| vowserv.kernel_voicedata_idx = 0; |
| mutex_unlock(&vow_vmalloc_lock); |
| } |
| |
| /* 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, [scp]offset: 0x%x, sz: 0x%x\n",*/ |
| /* __func__, i, idx, size, outPtr,*/ |
| /* temp_dump_info.scp_dump_offset[0],*/ |
| /* temp_dump_info.scp_dump_size[0]);*/ |
| /*}*/ |
| /* copy kernel to user space */ |
| if ((temp_dump_info.user_dump_size != kReadVowDumpSize) && |
| (temp_dump_info.user_dump_size != kReadVowDumpSize_Big)) { |
| VOWDRV_DEBUG("%s(), return, user_dump_size=0x%x", |
| __func__, |
| (unsigned int)temp_dump_info.user_dump_size); |
| return; |
| } |
| if (((temp_dump_info.user_dump_idx + idx) > temp_dump_info.user_dump_size) |
| && (temp_dump_info.user_dump_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)); |
| /*VOWDRV_DEBUG("%s(), %d, kernel_dump_addr: 0x%x, user_dump_idx: 0x%x\n",*/ |
| /* __func__, i,*/ |
| /* temp_dump_info.kernel_dump_addr,*/ |
| /* temp_dump_info.user_dump_idx);*/ |
| /* 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); |
| memmove(&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_ReadDumpData(void) |
| { |
| if (DumpData_Wait_Queue_flag == 0) |
| wait_event_interruptible_timeout(DumpData_Wait_Queue, |
| DumpData_Wait_Queue_flag, |
| msecs_to_jiffies(100)); |
| |
| if (DumpData_Wait_Queue_flag == 1) { |
| DumpData_Wait_Queue_flag = 0; |
| if ((VowDrv_GetHWStatus() == VOW_PWR_OFF) || |
| (vowserv.dump_pcm_flag == false)) { |
| VOWDRV_DEBUG( |
| "stop read vow dump data: %d, %d\n", |
| VowDrv_GetHWStatus(), |
| vowserv.dump_pcm_flag); |
| } else { |
| /* To Read Dump Data from Kernel to User */ |
| vow_service_GetVowDumpData(); |
| } |
| } else |
| VOWDRV_DEBUG("%s, 100ms timeout,break\n", __func__); |
| } |
| |
| static void vow_service_ReadVoiceData(void) |
| { |
| int stop_condition = 0; |
| unsigned long rec_queue_flags; |
| |
| 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(&vow_vmalloc_lock); |
| vowserv.kernel_voicedata_idx = 0; |
| mutex_unlock(&vow_vmalloc_lock); |
| 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); |
| spin_lock_irqsave(&vow_rec_queue_lock, rec_queue_flags); |
| vowserv.scp_shared_voice_buf_offset = 0; |
| vowserv.scp_shared_voice_length = 0; |
| spin_unlock_irqrestore(&vow_rec_queue_lock, rec_queue_flags); |
| } |
| 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; |
| } |
| } |
| for (I = 0; I < MAX_VOW_SPEAKER_MODEL; I++) { |
| 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__); |
| } |
| |
| #if IS_ENABLED(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; |
| } |
| #endif /* #if IS_ENABLED(CONFIG_MTK_TINYSYS_SCP_SUPPORT) */ |
| |
| static int vow_pcm_dump_notify(bool enable) |
| { |
| #if IS_ENABLED(CONFIG_MTK_TINYSYS_SCP_SUPPORT) |
| unsigned int vow_ipi_buf[9] = {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("%s(), [BargeIn]dump on\n", __func__); |
| #if IS_ENABLED(CONFIG_MTK_TINYSYS_SCP_DEBUG_SUPPORT) |
| 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]); |
| #endif |
| /* TOTAL dram resrved size for aecout data dump */ |
| vow_ipi_buf[3] = vow_dump_info[DUMP_AECOUT].size; |
| /* address for SCP using */ |
| vow_ipi_buf[4] = vow_dump_info[DUMP_AECOUT].phy_addr; |
| /* TOTAL dram resrved size for vffpout data dump */ |
| vow_ipi_buf[5] = vow_dump_info[DUMP_VFFPOUT].size; |
| /* address for SCP using */ |
| vow_ipi_buf[6] = vow_dump_info[DUMP_VFFPOUT].phy_addr; |
| /* TOTAL dram resrved size for vffpin data dump */ |
| vow_ipi_buf[7] = vow_dump_info[DUMP_VFFPIN].size; |
| /* address for SCP using */ |
| vow_ipi_buf[8] = vow_dump_info[DUMP_VFFPIN].phy_addr; |
| |
| VOWDRV_DEBUG("%s(), [aec] & [vffp]dump on\n", __func__); |
| #if IS_ENABLED(CONFIG_MTK_TINYSYS_SCP_DEBUG_SUPPORT) |
| VOWDRV_DEBUG( |
| "[aec]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, [vffp in]resv sz:0x%x, addr:0x%x\n", |
| vow_ipi_buf[1], |
| vow_ipi_buf[5], |
| vow_ipi_buf[6], |
| vow_ipi_buf[7], |
| vow_ipi_buf[8]); |
| #endif |
| |
| /* 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, |
| 9, |
| &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, |
| 9, |
| &vow_ipi_buf[0], |
| VOW_IPI_BYPASS_ACK); |
| if (ret == 0) |
| VOWDRV_DEBUG("PCM_DUMP_OFF ipi send error\n"); |
| } |
| VOWDRV_DEBUG("%s(), end\n\r", __func__); |
| #else |
| VOWDRV_DEBUG("%s(), vow: SCP no support\n\r", __func__); |
| #endif /* #if IS_ENABLED(CONFIG_MTK_TINYSYS_SCP_SUPPORT) */ |
| 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; |
| } |
| if ((I == DUMP_AECOUT) || (I == DUMP_BARGEIN) || (I == DUMP_INPUT)) { |
| vow_dump_info[I].kernel_dump_addr = vmalloc(kReadVowDumpSize); |
| vow_dump_info[I].kernel_dump_size = kReadVowDumpSize; |
| } else { |
| vow_dump_info[I].kernel_dump_addr = vmalloc(kReadVowDumpSize_Big); |
| vow_dump_info[I].kernel_dump_size = kReadVowDumpSize_Big; |
| } |
| #if IS_ENABLED(CONFIG_MTK_TINYSYS_SCP_DEBUG_SUPPORT) |
| VOWDRV_DEBUG("%s vow_dump_info[%d].addr = 0x%p, size = 0x%x\n", |
| __func__, I, |
| vow_dump_info[I].kernel_dump_addr, |
| vow_dump_info[I].kernel_dump_size); |
| #endif |
| VOW_ASSERT(vow_dump_info[I].kernel_dump_addr != NULL); |
| vow_dump_info[I].kernel_dump_idx = 0; |
| } |
| 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; |
| } |
| |
| static int vow_pcm_dump_set(bool enable) |
| { |
| VOWDRV_DEBUG("%s = %d, %d\n", __func__, |
| vowserv.dump_pcm_flag, |
| (unsigned int)enable); |
| #if IS_ENABLED(CONFIG_MTK_TINYSYS_SCP_SUPPORT) |
| 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; |
| #if IS_ENABLED(CONFIG_MTK_TINYSYS_SCP_DEBUG_SUPPORT) |
| 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); |
| #endif |
| // 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_AECOUT].vir_addr = |
| (char *)(scp_get_reserve_mem_virt(VOW_MEM_ID)) |
| + VOW_AECOUTDATA_OFFSET; |
| vow_dump_info[DUMP_AECOUT].phy_addr = |
| scp_get_reserve_mem_phys(VOW_MEM_ID) |
| + VOW_AECOUTDATA_OFFSET; |
| vow_dump_info[DUMP_AECOUT].size = AECOUT_DUMP_TOTAL_BYTE_CNT; |
| #if IS_ENABLED(CONFIG_MTK_TINYSYS_SCP_DEBUG_SUPPORT) |
| VOWDRV_DEBUG("[aecout]vir: %p, phys: 0x%x\n", |
| vow_dump_info[DUMP_AECOUT].vir_addr, |
| (unsigned int)vow_dump_info[DUMP_AECOUT].phy_addr); |
| #endif |
| |
| vow_dump_info[DUMP_VFFPOUT].vir_addr = |
| (char *)(scp_get_reserve_mem_virt(VOW_MEM_ID)) |
| + VOW_VFFPOUTDATA_OFFSET; |
| vow_dump_info[DUMP_VFFPOUT].phy_addr = |
| scp_get_reserve_mem_phys(VOW_MEM_ID) |
| + VOW_VFFPOUTDATA_OFFSET; |
| vow_dump_info[DUMP_VFFPOUT].size = VFFPOUT_DUMP_TOTAL_BYTE_CNT; |
| #if IS_ENABLED(CONFIG_MTK_TINYSYS_SCP_DEBUG_SUPPORT) |
| VOWDRV_DEBUG("[vffp out]vir: %p, phys: 0x%x\n", |
| vow_dump_info[DUMP_VFFPOUT].vir_addr, |
| (unsigned int)vow_dump_info[DUMP_VFFPOUT].phy_addr); |
| #endif |
| |
| vow_dump_info[DUMP_VFFPIN].vir_addr = |
| (char *)(scp_get_reserve_mem_virt(VOW_MEM_ID)) |
| + VOW_VFFPINDATA_OFFSET; |
| vow_dump_info[DUMP_VFFPIN].phy_addr = |
| scp_get_reserve_mem_phys(VOW_MEM_ID) |
| + VOW_VFFPINDATA_OFFSET; |
| vow_dump_info[DUMP_VFFPIN].size = VFFPIN_DUMP_TOTAL_BYTE_CNT; |
| #if IS_ENABLED(CONFIG_MTK_TINYSYS_SCP_DEBUG_SUPPORT) |
| VOWDRV_DEBUG("[vffp in]vir: %p, phys: 0x%x\n", |
| vow_dump_info[DUMP_VFFPIN].vir_addr, |
| (unsigned int)vow_dump_info[DUMP_VFFPIN].phy_addr); |
| #endif |
| VOWDRV_DEBUG("%s(), pcm set finish\n", __func__); |
| |
| if ((vowserv.dump_pcm_flag == false) && (enable == true)) { |
| vowserv.dump_pcm_flag = true; |
| vow_service_AllocKernelDumpBuffer(); |
| vow_pcm_dump_notify(true); |
| /* Let AP will not suspend */ |
| __pm_stay_awake(vow_dump_suspend_lock); |
| } 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(); |
| /* Let AP will suspend */ |
| __pm_relax(vow_dump_suspend_lock); |
| } |
| #else |
| VOWDRV_DEBUG("%s(), vow: SCP no support\n\r", __func__); |
| #endif /* #if IS_ENABLED(CONFIG_MTK_TINYSYS_SCP_SUPPORT) */ |
| return 0; |
| } |
| |
| /***************************************************************************** |
| * 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) { |
| #if IS_ENABLED(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_SetSpeakerNumber(void) |
| { |
| bool ret = false; |
| |
| ret = VowDrv_SetFlag(VOW_FLAG_SPEAKER_NUMBER, vowserv.vow_speaker_number); |
| |
| return ret; |
| } |
| |
| 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) & 0x0F; |
| |
| if (mtkif_type >= VOW_MTKIF_MAX) { |
| VOWDRV_DEBUG("out of VOW_MTKIF_TYPE %d\n\r", mtkif_type); |
| return false; |
| } |
| if (ch_num >= VOW_CH_MAX) { |
| 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; |
| |
| vowserv.force_phase_stage = (enable == 1) ? FORCE_PHASE1 : NO_FORCE; |
| |
| 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; |
| |
| vowserv.force_phase_stage = (enable == 1) ? FORCE_PHASE2 : NO_FORCE; |
| |
| 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; |
| #if IS_ENABLED(CONFIG_MTK_TINYSYS_SCP_SUPPORT) |
| unsigned int vow_ipi_buf[1]; |
| |
| if (irq_id >= VOW_BARGEIN_IRQ_MAX_NUM) { |
| 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); |
| vowserv.swip_log_enable = (enable == 1) ? true : false; |
| VOWDRV_DEBUG("%s(),enable=%d\n", __func__, 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\n", __func__); |
| #if IS_ENABLED(CONFIG_MTK_TINYSYS_SCP_DEBUG_SUPPORT) |
| // no print in user load |
| VOWDRV_DEBUG("%s() do nothing inode:%p, file:%p\n", |
| __func__, inode, fp); |
| #endif |
| return 0; |
| } |
| |
| static int VowDrv_release(struct inode *inode, struct file *fp) |
| { |
| VOWDRV_DEBUG("%s(), enter\n", __func__); |
| #if IS_ENABLED(CONFIG_MTK_TINYSYS_SCP_DEBUG_SUPPORT) |
| VOWDRV_DEBUG("%s() inode:%p, file:%p\n", __func__, inode, fp); |
| #endif |
| |
| 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("%s(), cmd = %u, arg = %lu\n", __func__, 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_EnableHotword: |
| VOWDRV_DEBUG("VOW_SET_CONTROL EnableDebug"); |
| mutex_lock(&vow_vmalloc_lock); |
| vowserv.kernel_voicedata_idx = 0; |
| mutex_unlock(&vow_vmalloc_lock); |
| vowserv.recording_flag = true; |
| vowserv.firstRead = true; |
| /*VowDrv_SetFlag(VOW_FLAG_DEBUG, true);*/ |
| #if IS_ENABLED(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_DisableHotword: |
| 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)); |
| } |
| #if IS_ENABLED(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; |
| case VOWControlCmd_Speaker_Single: |
| vowserv.vow_speaker_number = 1; |
| VOWDRV_DEBUG("VOW_SET_CONTROL Set Single Speaker VOW"); |
| break; |
| case VOWControlCmd_Speaker_Dual: |
| vowserv.vow_speaker_number = 2; |
| VOWDRV_DEBUG("VOW_SET_CONTROL Set Dual Speaker VOW"); |
| break; |
| case VOWControlCmd_GetDump: |
| vow_service_ReadDumpData(); |
| 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_SET_VOW_DUMP_DATA: |
| //VOWDRV_DEBUG("VOW_SET_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_SetSpeakerNumber(); |
| 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; |
| #if IS_ENABLED(CONFIG_MTK_VOW_1STSTAGE_PCMCALLBACK) |
| case VOW_SET_PAYLOADDUMP_INFO: { |
| struct vow_payloaddump_info_t payload = NULL; |
| |
| copy_from_user((void *)&payload, |
| (const void __user *)arg, |
| sizeof(struct vow_payloaddump_info_t)); |
| /* add return condition */ |
| if ((payload.return_payloaddump_addr == 0) || |
| (payload.max_payloaddump_size != VOW_VOICEDATA_SIZE)) { |
| VOWDRV_DEBUG("vow check payload fail: addr_%x, size_%x\n", |
| (unsigned int)payload.return_payloaddump_addr, |
| (unsigned int)payload.max_payloaddump_size); |
| return false; |
| } |
| if (payload.return_payloaddump_size_addr != 0) { |
| VOWDRV_DEBUG("vow size_addr_%x\n", |
| (unsigned int)payload.return_payloaddump_size_addr); |
| } |
| vowserv.payloaddump_user_addr = |
| payload.return_payloaddump_addr; |
| vowserv.payloaddump_user_max_size = |
| payload.max_payloaddump_size; |
| vowserv.payloaddump_user_return_size_addr = |
| payload.return_payloaddump_size_addr; |
| pr_debug("-VOW_SET_PAYLOADDUMP_INFO(addr=%lu, sz=%lu)", |
| vowserv.payloaddump_user_addr, |
| vowserv.payloaddump_user_max_size); |
| mutex_lock(&vow_payloaddump_mutex); |
| 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; |
| } |
| mutex_unlock(&vow_payloaddump_mutex); |
| } |
| break; |
| #endif |
| default: |
| VOWDRV_DEBUG("vow WrongParameter(%lu)", arg); |
| break; |
| } |
| return ret; |
| } |
| |
| #if IS_ENABLED(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: |
| ret = fp->f_op->unlocked_ioctl(fp, cmd, arg); |
| break; |
| case VOW_MODEL_START: |
| case VOW_MODEL_STOP: { |
| struct vow_model_start_kernel_t data32 = {}; |
| struct vow_model_start_t data = {}; |
| long err = -1; |
| |
| err = (long)copy_from_user(&data32, compat_ptr(arg), |
| (unsigned long)sizeof(struct vow_model_start_kernel_t)); |
| |
| if (err != 0L) |
| VOWDRV_DEBUG("%s(), copy data from user fail", __func__); |
| |
| data.handle = data32.handle; |
| data.confidence_level = data32.confidence_level; |
| data.dsp_inform_addr = data32.dsp_inform_addr; |
| data.dsp_inform_size_addr = data32.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_SET_VOW_DUMP_DATA: |
| case VOW_SET_APREG_INFO: { |
| struct vow_model_info_kernel_t data32 = {}; |
| struct vow_model_info_t data = {}; |
| long err = -1; |
| |
| err = (long)copy_from_user(&data32, compat_ptr(arg), |
| (unsigned long)sizeof(struct vow_model_info_kernel_t)); |
| |
| if (err != 0L) |
| VOWDRV_DEBUG("%s(), copy data from user fail", __func__); |
| |
| data.id = data32.id; |
| data.keyword = data32.keyword; |
| data.addr = data32.addr; |
| data.size = data32.size; |
| data.return_size_addr = data32.return_size_addr; |
| data.uuid = data32.uuid; |
| data.data = (uint32_t *)data32.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 data32 = {}; |
| struct vow_engine_info_t data = {}; |
| long err = -1; |
| |
| err = (long)copy_from_user(&data32, compat_ptr(arg), |
| (unsigned long)sizeof(struct vow_engine_info_kernel_t)); |
| |
| if (err != 0L) |
| VOWDRV_DEBUG("%s(), copy data from user fail", __func__); |
| |
| data.return_size_addr = data32.return_size_addr; |
| data.data_addr = data32.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 data32 = {}; |
| struct vow_payloaddump_info_t data = {}; |
| long err = -1; |
| |
| err = (long)copy_from_user(&data32, compat_ptr(arg), |
| (unsigned long)sizeof(struct vow_payloaddump_info_kernel_t)); |
| |
| if (err != 0L) |
| VOWDRV_DEBUG("%s(), copy data from user fail", __func__); |
| |
| data.return_payloaddump_addr = data32.return_payloaddump_addr; |
| data.return_payloaddump_size_addr = data32.return_payloaddump_size_addr; |
| data.max_payloaddump_size = data32.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; |
| } |
| 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; |
| |
| 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); |
| |
| #if IS_ENABLED(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 |
| *****************************************************************************/ |
| #if IS_ENABLED(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 (!VowDrv_SetSpeakerNumber()) |
| VOWDRV_DEBUG("fail: VowDrv_SetSpeakerNumber\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 /* #if IS_ENABLED(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, |
| #if IS_ENABLED(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, |
| }; |
| |
| #if IS_ENABLED(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 = { |
| #if IS_ENABLED(CONFIG_PM) |
| .pm = &VowDrv_pm_ops, |
| #endif |
| .name = "VOW_driver_device", |
| #if IS_ENABLED(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(); |
| #if IS_ENABLED(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"); |
| vow_dump_suspend_lock = wakeup_source_register(NULL, "vow dump 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"); |
| |
| if (!vow_dump_suspend_lock) |
| pr_debug("vow dump 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); |
| wakeup_source_unregister(vow_dump_suspend_lock); |
| VOWDRV_DEBUG("-%s()\n", __func__); |
| } |
| module_init(VowDrv_mod_init); |
| module_exit(VowDrv_mod_exit); |
| |
| MODULE_IMPORT_NS(VFS_internal_I_am_really_a_filesystem_and_am_NOT_a_driver); |
| |
| /***************************************************************************** |
| * License |
| *****************************************************************************/ |
| MODULE_AUTHOR("Michael Hsiao <michael.hsiao@mediatek.com>"); |
| MODULE_DESCRIPTION("MediaTek VoW Driver"); |
| MODULE_LICENSE("GPL v2"); |
| |