3307 lines
92 KiB
C
3307 lines
92 KiB
C
|
// SPDX-License-Identifier: GPL-2.0
|
||
|
/*
|
||
|
* Copyright (c) 2017 MediaTek Inc.
|
||
|
*/
|
||
|
|
||
|
/*****************************************************************************
|
||
|
* 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>
|
||
|
#ifdef CONFIG_MTK_TINYSYS_SCP_SUPPORT
|
||
|
#include "scp.h"
|
||
|
#endif /* #ifdef CONFIG_MTK_TINYSYS_SCP_SUPPORT */
|
||
|
#include "vow.h"
|
||
|
#include "vow_assert.h"
|
||
|
|
||
|
/*****************************************************************************
|
||
|
* Variable Definition
|
||
|
****************************************************************************/
|
||
|
static unsigned int VowDrv_Wait_Queue_flag;
|
||
|
static unsigned int VoiceData_Wait_Queue_flag;
|
||
|
static DECLARE_WAIT_QUEUE_HEAD(VowDrv_Wait_Queue);
|
||
|
static DECLARE_WAIT_QUEUE_HEAD(VoiceData_Wait_Queue);
|
||
|
static DEFINE_SPINLOCK(vowdrv_lock);
|
||
|
static DEFINE_SPINLOCK(vowdrv_dump_lock);
|
||
|
static struct wakeup_source *vow_suspend_lock;
|
||
|
static struct wakeup_source *vow_ipi_suspend_lock;
|
||
|
static struct dump_package_t dump_package;
|
||
|
static int init_flag = -1;
|
||
|
static const uint32_t kReadVowDumpSize = 0xA00 * 2; // 320(10ms) x 8 x 2ch= 5120 = 0x1400
|
||
|
/*****************************************************************************
|
||
|
* Function Declaration
|
||
|
****************************************************************************/
|
||
|
static void vow_service_getVoiceData(void);
|
||
|
static void vow_ipi_reg_ok(short uuid,
|
||
|
int confidence_lv,
|
||
|
unsigned int extradata_len,
|
||
|
unsigned int payloaddump_len);
|
||
|
static bool VowDrv_SetFlag(int type, unsigned int set);
|
||
|
static int VowDrv_GetHWStatus(void);
|
||
|
static void vow_pcm_dump_init(void);
|
||
|
static void vow_pcm_dump_deinit(void);
|
||
|
static bool VowDrv_SetBargeIn(unsigned int set, unsigned int irq_id);
|
||
|
//static int vow_service_SearchSpeakerModelWithUuid(int uuid);
|
||
|
static int vow_service_SearchSpeakerModelWithKeyword(int keyword);
|
||
|
static int vow_service_SearchSpeakerModelWithId(int id);
|
||
|
#ifdef CONFIG_MTK_VOW_1STSTAGE_PCMCALLBACK
|
||
|
static void vow_service_ReadPayloadDumpData(unsigned int buf_length);
|
||
|
#endif
|
||
|
static DEFINE_MUTEX(vow_vmalloc_lock);
|
||
|
static DEFINE_MUTEX(vow_extradata_mutex);
|
||
|
static DEFINE_MUTEX(voicedata_mutex);
|
||
|
|
||
|
/*****************************************************************************
|
||
|
* VOW SERVICES
|
||
|
*****************************************************************************/
|
||
|
|
||
|
static struct
|
||
|
{
|
||
|
struct vow_speaker_model_t vow_speaker_model[MAX_VOW_SPEAKER_MODEL];
|
||
|
unsigned long vow_info_apuser[MAX_VOW_INFO_LEN];
|
||
|
unsigned long voicedata_user_addr;
|
||
|
unsigned long voicedata_user_size;
|
||
|
short *voicedata_kernel_ptr;
|
||
|
char *voicedata_scp_ptr;
|
||
|
dma_addr_t voicedata_scp_addr;
|
||
|
char *extradata_ptr;
|
||
|
dma_addr_t extradata_addr;
|
||
|
char *extradata_mem_ptr;
|
||
|
unsigned int extradata_bytelen;
|
||
|
#ifdef CONFIG_MTK_VOW_1STSTAGE_PCMCALLBACK
|
||
|
char *payloaddump_scp_ptr;
|
||
|
dma_addr_t payloaddump_scp_addr;
|
||
|
unsigned long payloaddump_user_addr;
|
||
|
unsigned long payloaddump_user_max_size;
|
||
|
unsigned long payloaddump_user_return_size_addr;
|
||
|
short *payloaddump_kernel_ptr;
|
||
|
unsigned int payloaddump_length;
|
||
|
#endif
|
||
|
unsigned int kernel_voicedata_idx;
|
||
|
bool scp_command_flag;
|
||
|
bool recording_flag;
|
||
|
int scp_command_id;
|
||
|
int scp_command_keywordid;
|
||
|
int confidence_level;
|
||
|
int eint_status;
|
||
|
int pwr_status;
|
||
|
bool suspend_lock;
|
||
|
bool firstRead;
|
||
|
unsigned long voicedata_user_return_size_addr;
|
||
|
unsigned int scp_shared_voice_buf_offset;
|
||
|
unsigned int scp_shared_voice_length;
|
||
|
unsigned int transfer_length;
|
||
|
struct device_node *node;
|
||
|
struct pinctrl *pinctrl;
|
||
|
struct pinctrl_state *pins_eint_on;
|
||
|
struct pinctrl_state *pins_eint_off;
|
||
|
bool bypass_enter_phase3;
|
||
|
unsigned int enter_phase3_cnt;
|
||
|
unsigned int force_phase_stage;
|
||
|
bool swip_log_enable;
|
||
|
struct vow_eint_data_struct_t vow_eint_data_struct;
|
||
|
unsigned long long scp_recognize_ok_cycle;
|
||
|
unsigned long long ap_received_ipi_cycle;
|
||
|
bool tx_keyword_start;
|
||
|
unsigned int dump_frm_cnt;
|
||
|
unsigned int voice_sample_delay;
|
||
|
unsigned int bargein_dump_cnt1;
|
||
|
unsigned int bargein_dump_cnt2;
|
||
|
short *interleave_pcmdata_ptr;
|
||
|
bool dump_pcm_flag;
|
||
|
bool scp_recovering;
|
||
|
bool vow_recovering;
|
||
|
unsigned int recog_dump_cnt1;
|
||
|
unsigned int vffp_dump_cnt1;
|
||
|
unsigned int inputmic_dump_cnt1;
|
||
|
bool split_dumpfile_flag;
|
||
|
bool mcps_flag;
|
||
|
unsigned int scp_dual_mic_switch;
|
||
|
unsigned int mtkif_type;
|
||
|
unsigned int google_engine_version;
|
||
|
unsigned int vow_mic_number;
|
||
|
char alexa_engine_version[VOW_ENGINE_INFO_LENGTH_BYTE];
|
||
|
char google_engine_arch[VOW_ENGINE_INFO_LENGTH_BYTE];
|
||
|
unsigned int custom_model_addr;
|
||
|
unsigned long custom_model_size;
|
||
|
} vowserv;
|
||
|
|
||
|
struct vow_dump_info_t {
|
||
|
dma_addr_t phy_addr; // address of reseved buffer
|
||
|
char *vir_addr; // virtual (kernel) addess of reseved buffer
|
||
|
uint32_t size; // size of reseved buffer (bytes)
|
||
|
uint32_t scp_dump_offset[VOW_MAX_CH_NUM]; // return data offset from scp
|
||
|
uint32_t scp_dump_size[VOW_MAX_CH_NUM]; // return data size from scp
|
||
|
short *kernel_dump_addr; // kernel internal buffer address
|
||
|
unsigned int kernel_dump_idx; // current index of kernel_dump_addr
|
||
|
unsigned int kernel_dump_size; // size of kernel_dump_ptr buffer (bytes)
|
||
|
unsigned long user_dump_addr; // addr of user dump buffer
|
||
|
unsigned long user_dump_idx; // current index of user_dump_addr
|
||
|
unsigned long user_dump_size; // size of user dump buffer
|
||
|
unsigned long user_return_size_addr; // addr of return size of user
|
||
|
};
|
||
|
#define NUM_DELAY_INFO (2)
|
||
|
uint32_t delay_info[NUM_DELAY_INFO];
|
||
|
struct vow_dump_info_t vow_dump_info[NUM_DUMP_DATA];
|
||
|
|
||
|
/*****************************************************************************
|
||
|
* DSP IPI HANDELER
|
||
|
*****************************************************************************/
|
||
|
static void vow_ipi_rx_handle_data_msg(void *msg_data)
|
||
|
{
|
||
|
struct vow_ipi_combined_info_t *ipi_ptr;
|
||
|
unsigned long flags;
|
||
|
|
||
|
ipi_ptr = (struct vow_ipi_combined_info_t *)msg_data;
|
||
|
/* IPIMSG_VOW_BARGEIN_DUMP_INFO */
|
||
|
if (ipi_ptr->ipi_type_flag & BARGEIN_DUMP_INFO_IDX_MASK) {
|
||
|
delay_info[0] = ipi_ptr->dump_frm_cnt;
|
||
|
delay_info[1] = ipi_ptr->voice_sample_delay;
|
||
|
VOWDRV_DEBUG("[BargeIn] delay info %d %d\n",
|
||
|
delay_info[0],
|
||
|
delay_info[1]);
|
||
|
vow_dump_info[DUMP_DELAY_INFO].scp_dump_size[0] = sizeof(delay_info);
|
||
|
vow_dump_info[DUMP_DELAY_INFO].scp_dump_offset[0] = 0;
|
||
|
}
|
||
|
if (vowserv.dump_pcm_flag == true) {
|
||
|
spin_lock_irqsave(&vowdrv_dump_lock, flags);
|
||
|
if ((ipi_ptr->ipi_type_flag & INPUT_DUMP_IDX_MASK)) {
|
||
|
if (vow_dump_info[DUMP_INPUT].scp_dump_size[0] != 0) {
|
||
|
VOWDRV_DEBUG("%s WARNING dump idx 0x%x %d not handled\n",
|
||
|
__func__,
|
||
|
INPUT_DUMP_IDX_MASK,
|
||
|
vow_dump_info[DUMP_INPUT].scp_dump_size[0]);
|
||
|
}
|
||
|
vow_dump_info[DUMP_INPUT].scp_dump_size[0] = ipi_ptr->mic_dump_size;
|
||
|
vow_dump_info[DUMP_INPUT].scp_dump_offset[0] = ipi_ptr->mic_offset;
|
||
|
if (vowserv.vow_mic_number == 2) {
|
||
|
vow_dump_info[DUMP_INPUT].scp_dump_size[1] =
|
||
|
ipi_ptr->mic_dump_size;
|
||
|
vow_dump_info[DUMP_INPUT].scp_dump_offset[1] =
|
||
|
ipi_ptr->mic_offset_R;
|
||
|
}
|
||
|
}
|
||
|
/* IPIMSG_VOW_BARGEIN_PCMDUMP_OK */
|
||
|
if ((ipi_ptr->ipi_type_flag & BARGEIN_DUMP_IDX_MASK)) {
|
||
|
vow_dump_info[DUMP_BARGEIN].scp_dump_size[0] = ipi_ptr->echo_dump_size;
|
||
|
vow_dump_info[DUMP_BARGEIN].scp_dump_offset[0] = ipi_ptr->echo_offset;
|
||
|
}
|
||
|
if ((ipi_ptr->ipi_type_flag & RECOG_DUMP_IDX_MASK)) {
|
||
|
vow_dump_info[DUMP_RECOG].scp_dump_size[0] =
|
||
|
ipi_ptr->recog_dump_size;
|
||
|
vow_dump_info[DUMP_RECOG].scp_dump_offset[0] =
|
||
|
ipi_ptr->recog_dump_offset;
|
||
|
if (vowserv.vow_mic_number == 2) {
|
||
|
vow_dump_info[DUMP_RECOG].scp_dump_size[1] =
|
||
|
ipi_ptr->recog_dump_size;
|
||
|
vow_dump_info[DUMP_RECOG].scp_dump_offset[1] =
|
||
|
ipi_ptr->recog_dump_offset_R;
|
||
|
}
|
||
|
}
|
||
|
if ((ipi_ptr->ipi_type_flag & VFFP_DUMP_IDX_MASK)) {
|
||
|
vow_dump_info[DUMP_VFFP].scp_dump_size[0] =
|
||
|
ipi_ptr->vffp_dump_size;
|
||
|
vow_dump_info[DUMP_VFFP].scp_dump_offset[0] =
|
||
|
ipi_ptr->vffp_dump_offset;
|
||
|
/* 1st and 2nd are the same */
|
||
|
vow_dump_info[DUMP_VFFP].scp_dump_size[1] =
|
||
|
ipi_ptr->vffp_dump_size;
|
||
|
vow_dump_info[DUMP_VFFP].scp_dump_offset[1] =
|
||
|
ipi_ptr->vffp_dump_offset_2nd_ch;
|
||
|
}
|
||
|
dump_package.dump_data_type =
|
||
|
(ipi_ptr->ipi_type_flag & SCP_DUMP_DATA_MASK);
|
||
|
spin_unlock_irqrestore(&vowdrv_dump_lock, flags);
|
||
|
}
|
||
|
/* IPIMSG_VOW_DATAREADY */
|
||
|
if ((ipi_ptr->ipi_type_flag & DEBUG_DUMP_IDX_MASK) &&
|
||
|
(vowserv.recording_flag)) {
|
||
|
vowserv.scp_shared_voice_buf_offset = ipi_ptr->voice_buf_offset;
|
||
|
vowserv.scp_shared_voice_length = ipi_ptr->voice_length;
|
||
|
if (vowserv.scp_shared_voice_length > 320)
|
||
|
VOWDRV_DEBUG("vow,v_len=%x\n",
|
||
|
vowserv.scp_shared_voice_length);
|
||
|
vow_service_getVoiceData();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void vow_ipi_rx_internal(unsigned int msg_id,
|
||
|
void *msg_data)
|
||
|
{
|
||
|
switch (msg_id) {
|
||
|
case IPIMSG_VOW_COMBINED_INFO: {
|
||
|
struct vow_ipi_combined_info_t *ipi_ptr;
|
||
|
bool bypass_flag;
|
||
|
|
||
|
ipi_ptr = (struct vow_ipi_combined_info_t *)msg_data;
|
||
|
/* IPIMSG_VOW_RECOGNIZE_OK */
|
||
|
/*VOWDRV_DEBUG("[vow] IPIMSG_VOW_COMBINED_INFO, flag=0x%x\n",*/
|
||
|
/* ipi_ptr->ipi_type_flag);*/
|
||
|
bypass_flag = false;
|
||
|
if (ipi_ptr->ipi_type_flag & RECOG_OK_IDX_MASK) {
|
||
|
if ((vowserv.recording_flag == true) &&
|
||
|
(vowserv.tx_keyword_start == true)) {
|
||
|
VOWDRV_DEBUG("%s(), bypass this recog ok\n",
|
||
|
__func__);
|
||
|
bypass_flag = true;
|
||
|
}
|
||
|
if (bypass_flag == false) {
|
||
|
/* toggle wakelock for abort suspend flow */
|
||
|
__pm_stay_awake(vow_ipi_suspend_lock);
|
||
|
__pm_relax(vow_ipi_suspend_lock);
|
||
|
VOWDRV_DEBUG("%s(), receive recog_ok_ipi\n",
|
||
|
__func__);
|
||
|
vowserv.ap_received_ipi_cycle =
|
||
|
get_cycles();
|
||
|
vowserv.scp_recognize_ok_cycle =
|
||
|
ipi_ptr->recog_ok_os_timer;
|
||
|
vowserv.enter_phase3_cnt++;
|
||
|
if (vowserv.bypass_enter_phase3 == false) {
|
||
|
vow_ipi_reg_ok(
|
||
|
(short)ipi_ptr->recog_ok_keywordid,
|
||
|
ipi_ptr->confidence_lv,
|
||
|
ipi_ptr->extra_data_len,
|
||
|
ipi_ptr->payloaddump_len);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
// Copy scp shared buffer into kernel internal buffer
|
||
|
vow_ipi_rx_handle_data_msg(msg_data);
|
||
|
break;
|
||
|
}
|
||
|
case IPIMSG_VOW_RETURN_VALUE: {
|
||
|
unsigned int return_id;
|
||
|
unsigned int return_value;
|
||
|
unsigned int ipi_value = *(unsigned int *)msg_data;
|
||
|
|
||
|
return_id = (ipi_value >> WORD_H);
|
||
|
return_value = (ipi_value & WORD_L_MASK);
|
||
|
VOWDRV_DEBUG("%s(), IPIMSG_VOW_RETURN_VALUE, id:%d, val:%d\r",
|
||
|
__func__, return_id, return_value);
|
||
|
switch (return_id) {
|
||
|
case VOW_FLAG_FORCE_PHASE1_DEBUG:
|
||
|
case VOW_FLAG_FORCE_PHASE2_DEBUG:
|
||
|
vowserv.force_phase_stage = return_value;
|
||
|
break;
|
||
|
case VOW_FLAG_SWIP_LOG_PRINT:
|
||
|
vowserv.swip_log_enable = return_value;
|
||
|
break;
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
case IPIMSG_VOW_ALEXA_ENGINE_VER: {
|
||
|
VOWDRV_DEBUG("%s(), IPIMSG_VOW_ALEXA_ENGINE_VER %s\r",
|
||
|
__func__, msg_data);
|
||
|
memcpy(vowserv.alexa_engine_version, msg_data,
|
||
|
sizeof(vowserv.alexa_engine_version));
|
||
|
}
|
||
|
break;
|
||
|
case IPIMSG_VOW_GOOGLE_ENGINE_VER: {
|
||
|
unsigned int *temp = (unsigned int *)msg_data;
|
||
|
|
||
|
VOWDRV_DEBUG("%s(), IPIMSG_VOW_GOOGLE_ENGINE_VER 0x%x\r",
|
||
|
__func__, temp[0]);
|
||
|
vowserv.google_engine_version = temp[0];
|
||
|
}
|
||
|
break;
|
||
|
case IPIMSG_VOW_GOOGLE_ARCH: {
|
||
|
VOWDRV_DEBUG("%s(), IPIMSG_VOW_GOOGLE_ARCH %s\r",
|
||
|
__func__, msg_data);
|
||
|
memcpy(vowserv.google_engine_arch, msg_data,
|
||
|
sizeof(vowserv.google_engine_arch));
|
||
|
}
|
||
|
break;
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool vow_ipi_rceive_ack(unsigned int msg_id,
|
||
|
unsigned int msg_data)
|
||
|
{
|
||
|
bool result = false;
|
||
|
|
||
|
switch (msg_id) {
|
||
|
case IPIMSG_VOW_ENABLE:
|
||
|
case IPIMSG_VOW_DISABLE:
|
||
|
case IPIMSG_VOW_SET_MODEL:
|
||
|
case IPIMSG_VOW_SET_SMART_DEVICE:
|
||
|
case IPIMSG_VOW_APREGDATA_ADDR:
|
||
|
case IPIMSG_VOW_PCM_DUMP_ON:
|
||
|
case IPIMSG_VOW_PCM_DUMP_OFF:
|
||
|
case IPIMSG_VOW_SET_FLAG:
|
||
|
result = true;
|
||
|
break;
|
||
|
case IPIMSG_VOW_SET_BARGEIN_ON:
|
||
|
case IPIMSG_VOW_SET_BARGEIN_OFF:
|
||
|
case IPIMSG_VOW_SET_BIXBY_ENGINE_MODE:
|
||
|
result = true;
|
||
|
break;
|
||
|
default:
|
||
|
VOWDRV_DEBUG("%s(), no relate msg id\r", __func__);
|
||
|
break;
|
||
|
}
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
static void vow_ipi_reg_ok(short keyword,
|
||
|
int confidence_lv,
|
||
|
unsigned int extradata_len,
|
||
|
unsigned int payloaddump_len)
|
||
|
{
|
||
|
int slot;
|
||
|
|
||
|
vowserv.scp_command_flag = true;
|
||
|
/* transfer keyword id to model handle id */
|
||
|
slot = vow_service_SearchSpeakerModelWithKeyword(keyword);
|
||
|
if (slot < 0) {
|
||
|
VOWDRV_DEBUG("%s(), Fail !! Not keyword event !!, exit\n",
|
||
|
__func__);
|
||
|
return;
|
||
|
}
|
||
|
/* vowserv.scp_command_id = vowserv.vow_speaker_model[slot].id; */
|
||
|
vowserv.scp_command_keywordid = keyword;
|
||
|
vowserv.confidence_level = confidence_lv;
|
||
|
if (extradata_len <= VOW_EXTRA_DATA_SIZE)
|
||
|
vowserv.extradata_bytelen = extradata_len;
|
||
|
else
|
||
|
vowserv.extradata_bytelen = 0;
|
||
|
#ifdef CONFIG_MTK_VOW_1STSTAGE_PCMCALLBACK
|
||
|
vowserv.payloaddump_length = payloaddump_len;
|
||
|
//VOWDRV_DEBUG("[vow PDR] payloaddump_length = 0x%x\n", ipi_ptr->payloaddump_length);
|
||
|
#endif
|
||
|
|
||
|
/* VOWDRV_DEBUG("%s(), extradata_bytelen = %d\r", */
|
||
|
/* __func__, vowserv.extradata_bytelen); */
|
||
|
VowDrv_Wait_Queue_flag = 1;
|
||
|
wake_up_interruptible(&VowDrv_Wait_Queue);
|
||
|
}
|
||
|
#ifdef CONFIG_MTK_TINYSYS_SCP_SUPPORT
|
||
|
static void vow_register_feature(enum feature_id id)
|
||
|
{
|
||
|
if (!vow_check_scp_status()) {
|
||
|
VOWDRV_DEBUG("SCP is off, bypass register id(%d)\n", id);
|
||
|
return;
|
||
|
}
|
||
|
scp_register_feature(id);
|
||
|
}
|
||
|
|
||
|
static void vow_deregister_feature(enum feature_id id)
|
||
|
{
|
||
|
if (!vow_check_scp_status()) {
|
||
|
VOWDRV_DEBUG("SCP is off, bypass deregister id(%d)\n", id);
|
||
|
return;
|
||
|
}
|
||
|
scp_deregister_feature(id);
|
||
|
}
|
||
|
#endif
|
||
|
static void vow_service_getVoiceData(void)
|
||
|
{
|
||
|
if (VoiceData_Wait_Queue_flag == 0) {
|
||
|
VoiceData_Wait_Queue_flag = 1;
|
||
|
wake_up_interruptible(&VoiceData_Wait_Queue);
|
||
|
} else {
|
||
|
/* VOWDRV_DEBUG("getVoiceData but no one wait for it, */
|
||
|
/* may lost it!!\n"); */
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*****************************************************************************
|
||
|
* DSP SERVICE FUNCTIONS
|
||
|
*****************************************************************************/
|
||
|
static void vow_service_Init(void)
|
||
|
{
|
||
|
int I;
|
||
|
bool ret;
|
||
|
unsigned int vow_ipi_buf[3];
|
||
|
|
||
|
VOWDRV_DEBUG("%s():%x\n", __func__, init_flag);
|
||
|
/* common part */
|
||
|
vowserv.scp_command_flag = false;
|
||
|
vowserv.tx_keyword_start = false;
|
||
|
/*Initialization*/
|
||
|
#ifdef CONFIG_MTK_TINYSYS_SCP_SUPPORT
|
||
|
vowserv.voicedata_scp_ptr =
|
||
|
(char *)(scp_get_reserve_mem_virt(VOW_MEM_ID))
|
||
|
+ VOW_VOICEDATA_OFFSET;
|
||
|
vowserv.voicedata_scp_addr =
|
||
|
scp_get_reserve_mem_phys(VOW_MEM_ID)
|
||
|
+ VOW_VOICEDATA_OFFSET;
|
||
|
/*Extra data*/
|
||
|
vowserv.extradata_ptr =
|
||
|
(char *)(scp_get_reserve_mem_virt(VOW_MEM_ID))
|
||
|
+ VOW_EXTRA_DATA_OFFSET;
|
||
|
vowserv.extradata_addr =
|
||
|
scp_get_reserve_mem_phys(VOW_MEM_ID)
|
||
|
+ VOW_EXTRA_DATA_OFFSET;
|
||
|
|
||
|
#ifdef CONFIG_MTK_VOW_1STSTAGE_PCMCALLBACK
|
||
|
/* use voice data R space to exchange payload data */
|
||
|
vowserv.payloaddump_scp_ptr =
|
||
|
(char *)(scp_get_reserve_mem_virt(VOW_MEM_ID))
|
||
|
+ VOW_VOICEDATA_OFFSET + VOW_VOICEDATA_SIZE;
|
||
|
vowserv.payloaddump_scp_addr =
|
||
|
scp_get_reserve_mem_phys(VOW_MEM_ID)
|
||
|
+ VOW_VOICEDATA_OFFSET + VOW_VOICEDATA_SIZE;
|
||
|
#endif
|
||
|
#else
|
||
|
VOWDRV_DEBUG("%s(), vow: SCP no support\n\r", __func__);
|
||
|
#endif
|
||
|
vow_ipi_send(IPIMSG_VOW_GET_ALEXA_ENGINE_VER, 0, NULL,
|
||
|
VOW_IPI_BYPASS_ACK);
|
||
|
vow_ipi_send(IPIMSG_VOW_GET_GOOGLE_ENGINE_VER, 0, NULL,
|
||
|
VOW_IPI_BYPASS_ACK);
|
||
|
vow_ipi_send(IPIMSG_VOW_GET_GOOGLE_ARCH, 0, NULL,
|
||
|
VOW_IPI_BYPASS_ACK);
|
||
|
|
||
|
//audio_load_task(TASK_SCENE_VOW);
|
||
|
|
||
|
if (init_flag != 1) {
|
||
|
/*Initialization*/
|
||
|
VowDrv_Wait_Queue_flag = 0;
|
||
|
VoiceData_Wait_Queue_flag = 0;
|
||
|
vowserv.recording_flag = false;
|
||
|
vowserv.suspend_lock = 0;
|
||
|
vowserv.scp_shared_voice_length = 0;
|
||
|
vowserv.firstRead = false;
|
||
|
vowserv.scp_shared_voice_buf_offset = 0;
|
||
|
vowserv.bypass_enter_phase3 = false;
|
||
|
vowserv.enter_phase3_cnt = 0;
|
||
|
vowserv.scp_recovering = false;
|
||
|
vowserv.vow_recovering = false;
|
||
|
spin_lock(&vowdrv_lock);
|
||
|
vowserv.pwr_status = VOW_PWR_OFF;
|
||
|
vowserv.eint_status = VOW_EINT_DISABLE;
|
||
|
spin_unlock(&vowdrv_lock);
|
||
|
vowserv.force_phase_stage = NO_FORCE;
|
||
|
vowserv.swip_log_enable = true;
|
||
|
memset((void *)&vowserv.vow_eint_data_struct, 0,
|
||
|
sizeof(vowserv.vow_eint_data_struct));
|
||
|
vowserv.voicedata_user_addr = 0;
|
||
|
vowserv.voicedata_user_size = 0;
|
||
|
vowserv.voicedata_user_return_size_addr = 0;
|
||
|
for (I = 0; I < MAX_VOW_SPEAKER_MODEL; I++) {
|
||
|
vowserv.vow_speaker_model[I].model_ptr = NULL;
|
||
|
vowserv.vow_speaker_model[I].id = -1;
|
||
|
vowserv.vow_speaker_model[I].keyword = -1;
|
||
|
vowserv.vow_speaker_model[I].uuid = 0;
|
||
|
vowserv.vow_speaker_model[I].flag = 0;
|
||
|
vowserv.vow_speaker_model[I].enabled = 0;
|
||
|
}
|
||
|
/* extra data memory locate */
|
||
|
mutex_lock(&vow_extradata_mutex);
|
||
|
if (vowserv.extradata_mem_ptr == NULL)
|
||
|
vowserv.extradata_mem_ptr = vmalloc(VOW_EXTRA_DATA_SIZE);
|
||
|
mutex_unlock(&vow_extradata_mutex);
|
||
|
vowserv.extradata_bytelen = 0;
|
||
|
#ifdef CONFIG_MTK_VOW_1STSTAGE_PCMCALLBACK
|
||
|
/* use voice data R space to exchange payload data */
|
||
|
vowserv.payloaddump_user_addr = 0;
|
||
|
vowserv.payloaddump_user_max_size = 0;
|
||
|
vowserv.payloaddump_user_return_size_addr = 0;
|
||
|
vowserv.payloaddump_kernel_ptr = NULL;
|
||
|
vowserv.payloaddump_length = 0;
|
||
|
#endif
|
||
|
vowserv.voicedata_kernel_ptr = NULL;
|
||
|
mutex_lock(&voicedata_mutex);
|
||
|
vowserv.kernel_voicedata_idx = 0;
|
||
|
mutex_unlock(&voicedata_mutex);
|
||
|
memset((void *)vow_dump_info, 0, sizeof(vow_dump_info));
|
||
|
init_flag = 1;
|
||
|
vowserv.dump_pcm_flag = false;
|
||
|
vowserv.split_dumpfile_flag = false;
|
||
|
vowserv.interleave_pcmdata_ptr = NULL;
|
||
|
vowserv.custom_model_addr = 0;
|
||
|
vowserv.custom_model_size = 0;
|
||
|
// update here when vow support more than 2 mic
|
||
|
vowserv.vow_mic_number = VOW_MAX_MIC_NUM;
|
||
|
vow_pcm_dump_init();
|
||
|
vowserv.scp_dual_mic_switch = VOW_ENABLE_DUAL_MIC;
|
||
|
vowserv.mtkif_type = 0;
|
||
|
/* set meaningless default value to platform identifier and version */
|
||
|
memset(vowserv.google_engine_arch, 0, VOW_ENGINE_INFO_LENGTH_BYTE);
|
||
|
if (sprintf(vowserv.google_engine_arch, "12345678-1234-1234-1234-123456789012") < 0)
|
||
|
VOWDRV_DEBUG("%s(), sprintf fail", __func__);
|
||
|
vowserv.google_engine_version = DEFAULT_GOOGLE_ENGINE_VER;
|
||
|
memset(vowserv.alexa_engine_version, 0, VOW_ENGINE_INFO_LENGTH_BYTE);
|
||
|
} else {
|
||
|
int ipi_size;
|
||
|
for (I = 0; I < MAX_VOW_SPEAKER_MODEL; I++) {
|
||
|
if ((vowserv.vow_speaker_model[I].flag > 1) ||
|
||
|
(vowserv.vow_speaker_model[I].enabled > 1)) {
|
||
|
VOWDRV_DEBUG("reset speaker_model[%d]", I);
|
||
|
vowserv.vow_speaker_model[I].model_ptr = NULL;
|
||
|
vowserv.vow_speaker_model[I].id = -1;
|
||
|
vowserv.vow_speaker_model[I].keyword = -1;
|
||
|
vowserv.vow_speaker_model[I].uuid = 0;
|
||
|
vowserv.vow_speaker_model[I].flag = 0;
|
||
|
vowserv.vow_speaker_model[I].enabled = 0;
|
||
|
}
|
||
|
}
|
||
|
vow_ipi_buf[0] = vowserv.voicedata_scp_addr;
|
||
|
vow_ipi_buf[1] = vowserv.extradata_addr;
|
||
|
vow_ipi_buf[2] = VOW_EXTRA_DATA_SIZE;
|
||
|
ipi_size = 3;
|
||
|
#ifdef CONFIG_MTK_VOW_1STSTAGE_PCMCALLBACK
|
||
|
vow_ipi_buf[3] = vowserv.payloaddump_scp_addr;
|
||
|
ipi_size = 4;
|
||
|
#endif
|
||
|
ret = vow_ipi_send(IPIMSG_VOW_APREGDATA_ADDR,
|
||
|
ipi_size,
|
||
|
&vow_ipi_buf[0],
|
||
|
VOW_IPI_BYPASS_ACK);
|
||
|
if (ret == 0) {
|
||
|
VOWDRV_DEBUG(
|
||
|
"IPIMSG_VOW_APREGDATA_ADDR ipi send error\n");
|
||
|
}
|
||
|
#if VOW_PRE_LEARN_MODE
|
||
|
VowDrv_SetFlag(VOW_FLAG_PRE_LEARN, true);
|
||
|
#endif
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static int vow_service_GetParameter(unsigned long arg)
|
||
|
{
|
||
|
unsigned long vow_info_ap[MAX_VOW_INFO_LEN];
|
||
|
|
||
|
if (copy_from_user((void *)(&vow_info_ap[0]),
|
||
|
(const void __user *)(arg),
|
||
|
sizeof(vow_info_ap))) {
|
||
|
VOWDRV_DEBUG("vow get parameter fail\n");
|
||
|
return -EFAULT;
|
||
|
}
|
||
|
if (vow_info_ap[3] > VOW_MODEL_SIZE ||
|
||
|
vow_info_ap[3] < VOW_MODEL_SIZE_THRES) {
|
||
|
VOWDRV_DEBUG("vow Modle Size is incorrect %d\n",
|
||
|
vow_info_ap[3]);
|
||
|
return -EFAULT;
|
||
|
}
|
||
|
memcpy(vowserv.vow_info_apuser, vow_info_ap,
|
||
|
sizeof(vow_info_ap));
|
||
|
VOWDRV_DEBUG(
|
||
|
"vow get parameter: id %lu, keyword %lu, mdl_ptr 0x%x, mdl_sz %lu\n",
|
||
|
vowserv.vow_info_apuser[0],
|
||
|
vowserv.vow_info_apuser[1],
|
||
|
vowserv.vow_info_apuser[2],
|
||
|
vowserv.vow_info_apuser[3]);
|
||
|
VOWDRV_DEBUG(
|
||
|
"vow get parameter: return size addr 0x%x, uuid %d, data 0x%x\n",
|
||
|
vowserv.vow_info_apuser[4],
|
||
|
vowserv.vow_info_apuser[5],
|
||
|
vowserv.vow_info_apuser[6]);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
#ifdef CONFIG_MTK_TINYSYS_SCP_SUPPORT
|
||
|
static int vow_service_CopyModel(int slot)
|
||
|
{
|
||
|
if (slot >= MAX_VOW_SPEAKER_MODEL || slot < 0) {
|
||
|
VOWDRV_DEBUG("%s(), slot id=%d, over range\n", __func__, slot);
|
||
|
return -EDOM;
|
||
|
}
|
||
|
if (copy_from_user((void *)(vowserv.vow_speaker_model[slot].model_ptr),
|
||
|
(const void __user *)(vowserv.vow_info_apuser[2]),
|
||
|
vowserv.vow_info_apuser[3])) {
|
||
|
VOWDRV_DEBUG("vow Copy Speaker Model Fail\n");
|
||
|
return -EFAULT;
|
||
|
}
|
||
|
|
||
|
vowserv.vow_speaker_model[slot].flag = 1;
|
||
|
vowserv.vow_speaker_model[slot].enabled = 0;
|
||
|
vowserv.vow_speaker_model[slot].id = vowserv.vow_info_apuser[0];
|
||
|
vowserv.vow_speaker_model[slot].keyword = vowserv.vow_info_apuser[1];
|
||
|
vowserv.vow_speaker_model[slot].uuid = vowserv.vow_info_apuser[5];
|
||
|
vowserv.vow_speaker_model[slot].model_size = vowserv.vow_info_apuser[3];
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
#endif /* #ifdef CONFIG_MTK_TINYSYS_SCP_SUPPORT */
|
||
|
|
||
|
static int vow_service_FindFreeSpeakerModel(void)
|
||
|
{
|
||
|
int I;
|
||
|
|
||
|
I = 0;
|
||
|
do {
|
||
|
if (vowserv.vow_speaker_model[I].flag == 0)
|
||
|
break;
|
||
|
I++;
|
||
|
} while (I < MAX_VOW_SPEAKER_MODEL);
|
||
|
|
||
|
VOWDRV_DEBUG("vow FindFreeSpeakerModel:%d\n", I);
|
||
|
|
||
|
if (I == MAX_VOW_SPEAKER_MODEL) {
|
||
|
VOWDRV_DEBUG("vow Find Free Speaker Model Fail\n");
|
||
|
return -1;
|
||
|
}
|
||
|
return I;
|
||
|
}
|
||
|
|
||
|
static int vow_service_SearchSpeakerModelWithId(int id)
|
||
|
{
|
||
|
int I;
|
||
|
|
||
|
I = 0;
|
||
|
do {
|
||
|
if (vowserv.vow_speaker_model[I].id == id)
|
||
|
break;
|
||
|
I++;
|
||
|
} while (I < MAX_VOW_SPEAKER_MODEL);
|
||
|
|
||
|
if (I == MAX_VOW_SPEAKER_MODEL) {
|
||
|
VOWDRV_DEBUG("vow Search Speaker Model By ID Fail:%x\n", id);
|
||
|
return -1;
|
||
|
}
|
||
|
return I;
|
||
|
}
|
||
|
|
||
|
static int vow_service_SearchSpeakerModelWithKeyword(int keyword)
|
||
|
{
|
||
|
int I;
|
||
|
|
||
|
I = 0;
|
||
|
do {
|
||
|
if (vowserv.vow_speaker_model[I].keyword == keyword) {
|
||
|
VOWDRV_DEBUG("vow Search Speaker Model By Keyword Success !, keyword:%x\n",
|
||
|
keyword);
|
||
|
break;
|
||
|
}
|
||
|
I++;
|
||
|
} while (I < MAX_VOW_SPEAKER_MODEL);
|
||
|
|
||
|
if (I == MAX_VOW_SPEAKER_MODEL) {
|
||
|
return -1;
|
||
|
}
|
||
|
return I;
|
||
|
}
|
||
|
|
||
|
static bool vow_service_SendSpeakerModel(int slot, bool release_flag)
|
||
|
{
|
||
|
bool ret = false;
|
||
|
#ifdef CONFIG_MTK_TINYSYS_SCP_SUPPORT
|
||
|
unsigned int vow_ipi_buf[5];
|
||
|
|
||
|
if (slot >= MAX_VOW_SPEAKER_MODEL || slot < 0) {
|
||
|
VOWDRV_DEBUG("%s(), slot id=%d, over range\n", __func__, slot);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
if (release_flag == VOW_CLEAN_MODEL) {
|
||
|
if (vowserv.vow_speaker_model[slot].flag == 0) {
|
||
|
VOWDRV_DEBUG("%s(), slot:%d, no model need to clean\n",
|
||
|
__func__, slot);
|
||
|
return ret;
|
||
|
}
|
||
|
vow_ipi_buf[0] = VOW_MODEL_CLEAR;
|
||
|
} else { /* VOW_SET_MODEL */
|
||
|
if ((vowserv.vow_speaker_model[slot].flag == 0) ||
|
||
|
(vowserv.vow_speaker_model[slot].id == -1) ||
|
||
|
(vowserv.vow_speaker_model[slot].keyword == -1) ||
|
||
|
(vowserv.vow_speaker_model[slot].uuid == 0) ||
|
||
|
(vowserv.vow_speaker_model[slot].model_size == 0)) {
|
||
|
VOWDRV_DEBUG("%s(), slot:%d, no model need to set\n",
|
||
|
__func__, slot);
|
||
|
return ret;
|
||
|
}
|
||
|
vow_ipi_buf[0] = VOW_MODEL_SPEAKER;
|
||
|
}
|
||
|
vow_ipi_buf[1] = vowserv.vow_speaker_model[slot].uuid;
|
||
|
vow_ipi_buf[2] = scp_get_reserve_mem_phys(VOW_MEM_ID) +
|
||
|
(VOW_MODEL_SIZE * slot);
|
||
|
vow_ipi_buf[3] = vowserv.vow_speaker_model[slot].model_size;
|
||
|
vow_ipi_buf[4] = vowserv.vow_speaker_model[slot].keyword;
|
||
|
|
||
|
VOWDRV_DEBUG(
|
||
|
"Model:slot_%d, model_%x, addr_%x, id_%x, size_%x, keyword_%x\n",
|
||
|
slot,
|
||
|
vow_ipi_buf[0],
|
||
|
vow_ipi_buf[2],
|
||
|
vow_ipi_buf[1],
|
||
|
vow_ipi_buf[3],
|
||
|
vow_ipi_buf[4]);
|
||
|
|
||
|
ret = vow_ipi_send(IPIMSG_VOW_SET_MODEL,
|
||
|
5,
|
||
|
&vow_ipi_buf[0],
|
||
|
VOW_IPI_BYPASS_ACK);
|
||
|
#else
|
||
|
VOWDRV_DEBUG("%s(), vow: SCP no support\n\r", __func__);
|
||
|
#endif
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static bool vow_service_ReleaseSpeakerModel(int id)
|
||
|
{
|
||
|
int I;
|
||
|
bool ret = false;
|
||
|
|
||
|
I = vow_service_SearchSpeakerModelWithId(id);
|
||
|
|
||
|
if (I == -1) {
|
||
|
VOWDRV_DEBUG("vow release Speaker Model Fail, id:%x\n", id);
|
||
|
return false;
|
||
|
}
|
||
|
VOWDRV_DEBUG("vow ReleaseSpeakerModel:id_%x, slot_%d\n", id, I);
|
||
|
|
||
|
ret = vow_service_SendSpeakerModel(I, VOW_CLEAN_MODEL);
|
||
|
|
||
|
vowserv.vow_speaker_model[I].model_ptr = NULL;
|
||
|
vowserv.vow_speaker_model[I].uuid = 0;
|
||
|
vowserv.vow_speaker_model[I].id = -1;
|
||
|
vowserv.vow_speaker_model[I].keyword = -1;
|
||
|
vowserv.vow_speaker_model[I].flag = 0;
|
||
|
vowserv.vow_speaker_model[I].enabled = 0;
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static bool vow_service_SetSpeakerModel(unsigned long arg)
|
||
|
{
|
||
|
bool ret = false;
|
||
|
int I;
|
||
|
#ifdef CONFIG_MTK_TINYSYS_SCP_SUPPORT
|
||
|
char *ptr8;
|
||
|
#endif
|
||
|
|
||
|
I = vow_service_FindFreeSpeakerModel();
|
||
|
if (I == -1)
|
||
|
return false;
|
||
|
|
||
|
if (vow_service_GetParameter(arg) != 0)
|
||
|
return false;
|
||
|
#ifdef CONFIG_MTK_TINYSYS_SCP_SUPPORT
|
||
|
vowserv.vow_speaker_model[I].model_ptr =
|
||
|
(void *)(scp_get_reserve_mem_virt(VOW_MEM_ID))
|
||
|
+ (VOW_MODEL_SIZE * I);
|
||
|
|
||
|
if (vow_service_CopyModel(I) != 0)
|
||
|
return false;
|
||
|
|
||
|
ptr8 = (char *)vowserv.vow_speaker_model[I].model_ptr;
|
||
|
VOWDRV_DEBUG("SetSPKModel:slot: %d, ID: %x, UUID: %x, flag: %x\n",
|
||
|
I,
|
||
|
vowserv.vow_speaker_model[I].id,
|
||
|
vowserv.vow_speaker_model[I].keyword,
|
||
|
vowserv.vow_speaker_model[I].uuid,
|
||
|
vowserv.vow_speaker_model[I].flag);
|
||
|
VOWDRV_DEBUG("vow SetSPKModel:CheckValue:%x %x %x %x %x %x\n",
|
||
|
*(char *)&ptr8[0], *(char *)&ptr8[1],
|
||
|
*(char *)&ptr8[2], *(char *)&ptr8[3],
|
||
|
*(short *)&ptr8[160], *(int *)&ptr8[7960]);
|
||
|
|
||
|
ret = vow_service_SendSpeakerModel(I, VOW_SET_MODEL);
|
||
|
/* if IPI send fail, then just clean this model information */
|
||
|
if (ret == false) {
|
||
|
VOWDRV_DEBUG("vow ipi fail, then ignore this load model\n");
|
||
|
vowserv.vow_speaker_model[I].model_ptr = NULL;
|
||
|
vowserv.vow_speaker_model[I].uuid = 0;
|
||
|
vowserv.vow_speaker_model[I].id = -1;
|
||
|
vowserv.vow_speaker_model[I].keyword = -1;
|
||
|
vowserv.vow_speaker_model[I].flag = 0;
|
||
|
vowserv.vow_speaker_model[I].enabled = 0;
|
||
|
}
|
||
|
#else
|
||
|
VOWDRV_DEBUG("%s(), vow: SCP no support\n\r", __func__);
|
||
|
#endif
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static bool vow_service_SetCustomModel(unsigned long arg)
|
||
|
{
|
||
|
bool ret = false;
|
||
|
struct vow_engine_info_t info;
|
||
|
#ifdef CONFIG_MTK_TINYSYS_SCP_SUPPORT
|
||
|
struct vow_engine_info_t *p_info = &info;
|
||
|
phys_addr_t p_virt;
|
||
|
phys_addr_t p_phys;
|
||
|
phys_addr_t p_mdl_v;
|
||
|
phys_addr_t p_mdl_p;
|
||
|
uint32_t data_size = 0;
|
||
|
unsigned int vow_ipi_buf[2];
|
||
|
#endif
|
||
|
|
||
|
if (copy_from_user((void *)&info,
|
||
|
(const void __user *)arg,
|
||
|
sizeof(struct vow_engine_info_t))) {
|
||
|
VOWDRV_DEBUG("vow get cust info fail\n");
|
||
|
return false;
|
||
|
}
|
||
|
#ifdef CONFIG_MTK_TINYSYS_SCP_SUPPORT
|
||
|
p_virt = scp_get_reserve_mem_virt(VOW_MEM_ID);
|
||
|
p_phys = scp_get_reserve_mem_phys(VOW_MEM_ID);
|
||
|
p_mdl_v = p_virt + VOW_CUSTOM_MODEL_OFFSET;
|
||
|
p_mdl_p = p_phys + VOW_CUSTOM_MODEL_OFFSET;
|
||
|
if (copy_from_user((void *)&data_size,
|
||
|
(const void __user *)p_info->return_size_addr,
|
||
|
sizeof(uint32_t))) {
|
||
|
VOWDRV_DEBUG("vow get cust size fail\n");
|
||
|
return false;
|
||
|
}
|
||
|
if (data_size > VOW_MAX_CUST_MODEL_SIZE || data_size == 0) {
|
||
|
VOWDRV_DEBUG("vow set cust model fail, invalid size %ld\n", data_size);
|
||
|
return false;
|
||
|
}
|
||
|
VOWDRV_DEBUG("vow set cust model, size %d\n", data_size);
|
||
|
if (copy_from_user((void *)p_mdl_v,
|
||
|
(const void __user *)p_info->data_addr,
|
||
|
data_size)) {
|
||
|
VOWDRV_DEBUG("vow copy cust model fail\n");
|
||
|
return false;
|
||
|
}
|
||
|
vowserv.custom_model_addr = (unsigned int)p_mdl_p;
|
||
|
vowserv.custom_model_size = (unsigned long)data_size;
|
||
|
vow_ipi_buf[0] = (unsigned int)p_mdl_p;
|
||
|
vow_ipi_buf[1] = (unsigned long)data_size;
|
||
|
ret = vow_ipi_send(IPIMSG_VOW_SET_CUSTOM_MODEL,
|
||
|
2,
|
||
|
&vow_ipi_buf[0],
|
||
|
VOW_IPI_BYPASS_ACK);
|
||
|
#else
|
||
|
VOWDRV_DEBUG("vow SCP is not supported\n");
|
||
|
#endif
|
||
|
return ret;
|
||
|
}
|
||
|
static bool vow_service_SendModelStatus(int slot, bool enable)
|
||
|
{
|
||
|
bool ret = false;
|
||
|
unsigned int vow_ipi_buf[2];
|
||
|
|
||
|
if (slot >= MAX_VOW_SPEAKER_MODEL || slot < 0) {
|
||
|
VOWDRV_DEBUG("%s(), slot id=%d, over range\n", __func__, slot);
|
||
|
return ret;
|
||
|
}
|
||
|
if (!vowserv.vow_speaker_model[slot].flag) {
|
||
|
VOWDRV_DEBUG("%s(), this speaker model isn't load\n", __func__);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
vow_ipi_buf[0] = vowserv.vow_speaker_model[slot].keyword;
|
||
|
vow_ipi_buf[1] = vowserv.vow_speaker_model[slot].confidence_lv;
|
||
|
|
||
|
if (enable == VOW_MODEL_STATUS_START) {
|
||
|
ret = vow_ipi_send(IPIMSG_VOW_MODEL_START,
|
||
|
2,
|
||
|
&vow_ipi_buf[0],
|
||
|
VOW_IPI_BYPASS_ACK);
|
||
|
} else { /* VOW_MODEL_STATUS_STOP */
|
||
|
ret = vow_ipi_send(IPIMSG_VOW_MODEL_STOP,
|
||
|
2,
|
||
|
&vow_ipi_buf[0],
|
||
|
VOW_IPI_BYPASS_ACK);
|
||
|
}
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static void vow_register_vendor_feature(int uuid)
|
||
|
{
|
||
|
#ifdef CONFIG_MTK_TINYSYS_SCP_SUPPORT
|
||
|
switch (uuid) {
|
||
|
case VENDOR_ID_MTK:
|
||
|
vow_register_feature(VOW_VENDOR_M_FEATURE_ID);
|
||
|
break;
|
||
|
case VENDOR_ID_AMAZON:
|
||
|
vow_register_feature(VOW_VENDOR_A_FEATURE_ID);
|
||
|
break;
|
||
|
case VENDOR_ID_OTHERS:
|
||
|
vow_register_feature(VOW_VENDOR_G_FEATURE_ID);
|
||
|
break;
|
||
|
default:
|
||
|
VOWDRV_DEBUG("VENDOR ID not support\n");
|
||
|
break;
|
||
|
}
|
||
|
#else
|
||
|
VOWDRV_DEBUG("%s(), vow: SCP no support\n\r", __func__);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
static void vow_deregister_vendor_feature(int uuid)
|
||
|
{
|
||
|
#ifdef CONFIG_MTK_TINYSYS_SCP_SUPPORT
|
||
|
switch (uuid) {
|
||
|
case VENDOR_ID_MTK:
|
||
|
vow_deregister_feature(VOW_VENDOR_M_FEATURE_ID);
|
||
|
break;
|
||
|
case VENDOR_ID_AMAZON:
|
||
|
vow_deregister_feature(VOW_VENDOR_A_FEATURE_ID);
|
||
|
break;
|
||
|
case VENDOR_ID_OTHERS:
|
||
|
vow_deregister_feature(VOW_VENDOR_G_FEATURE_ID);
|
||
|
break;
|
||
|
default:
|
||
|
VOWDRV_DEBUG("VENDOR ID not support\n");
|
||
|
break;
|
||
|
}
|
||
|
#else
|
||
|
VOWDRV_DEBUG("%s(), vow: SCP no support\n\r", __func__);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
static bool vow_service_SetModelStatus(bool enable, unsigned long arg)
|
||
|
{
|
||
|
bool ret = false;
|
||
|
int slot;
|
||
|
struct vow_model_start_t model_start;
|
||
|
|
||
|
if (copy_from_user((void *)&model_start,
|
||
|
(const void __user *)(arg),
|
||
|
sizeof(struct vow_model_start_t))) {
|
||
|
VOWDRV_DEBUG("vow get vow_model_start data fail\n");
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if (model_start.handle > INT_MAX || model_start.handle < INT_MIN) {
|
||
|
VOWDRV_DEBUG("%s(), model_start.handle will cause truncated value\n",
|
||
|
__func__);
|
||
|
return false;
|
||
|
}
|
||
|
slot = vow_service_SearchSpeakerModelWithId(model_start.handle);
|
||
|
if (slot < 0) {
|
||
|
VOWDRV_DEBUG("%s(), no match id\n", __func__);
|
||
|
return false;
|
||
|
}
|
||
|
if (enable == VOW_MODEL_STATUS_START) {
|
||
|
if (vowserv.vow_speaker_model[slot].enabled == 0) {
|
||
|
int uuid;
|
||
|
|
||
|
uuid = vowserv.vow_speaker_model[slot].uuid;
|
||
|
vow_register_vendor_feature(uuid);
|
||
|
}
|
||
|
vowserv.vow_speaker_model[slot].enabled = 1;
|
||
|
vowserv.vow_speaker_model[slot].confidence_lv =
|
||
|
model_start.confidence_level;
|
||
|
vowserv.vow_speaker_model[slot].rx_inform_addr =
|
||
|
model_start.dsp_inform_addr;
|
||
|
vowserv.vow_speaker_model[slot].rx_inform_size_addr =
|
||
|
model_start.dsp_inform_size_addr;
|
||
|
VOWDRV_DEBUG("VOW_MODEL_START, id:%d, enabled:%d, conf_lv:%d\n",
|
||
|
(int)model_start.handle,
|
||
|
vowserv.vow_speaker_model[slot].enabled,
|
||
|
vowserv.vow_speaker_model[slot].confidence_lv);
|
||
|
/* send model status to scp */
|
||
|
ret = vow_service_SendModelStatus(slot, enable);
|
||
|
if (ret == false)
|
||
|
VOWDRV_DEBUG("vow_service_SendModelStatus, err\n");
|
||
|
} else { /* VOW_MODEL_STATUS_STOP */
|
||
|
vowserv.vow_speaker_model[slot].confidence_lv =
|
||
|
model_start.confidence_level;
|
||
|
vowserv.vow_speaker_model[slot].rx_inform_addr =
|
||
|
model_start.dsp_inform_addr;
|
||
|
vowserv.vow_speaker_model[slot].rx_inform_size_addr =
|
||
|
model_start.dsp_inform_size_addr;
|
||
|
/* send model status to scp */
|
||
|
ret = vow_service_SendModelStatus(slot, enable);
|
||
|
if (ret == false)
|
||
|
VOWDRV_DEBUG("vow_service_SendModelStatus, err\n");
|
||
|
if (vowserv.vow_speaker_model[slot].enabled == 1) {
|
||
|
int uuid;
|
||
|
|
||
|
uuid = vowserv.vow_speaker_model[slot].uuid;
|
||
|
vow_deregister_vendor_feature(uuid);
|
||
|
}
|
||
|
vowserv.vow_speaker_model[slot].enabled = 0;
|
||
|
VOWDRV_DEBUG("VOW_MODEL_STOP, id:%d\n",
|
||
|
(int)model_start.handle);
|
||
|
}
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static bool vow_service_SetApDumpAddr(unsigned long arg)
|
||
|
{
|
||
|
unsigned long vow_info[MAX_VOW_INFO_LEN];
|
||
|
unsigned long id, flags;
|
||
|
|
||
|
if (copy_from_user((void *)(&vow_info[0]), (const void __user *)(arg),
|
||
|
sizeof(vowserv.vow_info_apuser))) {
|
||
|
VOWDRV_DEBUG("%s()check Ap dump parameter fail\n", __func__);
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/* add return condition */
|
||
|
if ((vow_info[2] == 0) || (vow_info[3] != kReadVowDumpSize) ||
|
||
|
(vow_info[4] == 0) || (vow_info[0] >= NUM_DUMP_DATA)) {
|
||
|
VOWDRV_DEBUG("%s(): error id %d, addr_%x, size_%x, addr_%x\n",
|
||
|
__func__,
|
||
|
(unsigned int)vow_info[0],
|
||
|
(unsigned int)vow_info[2],
|
||
|
(unsigned int)vow_info[3],
|
||
|
(unsigned int)vow_info[4]);
|
||
|
return false;
|
||
|
}
|
||
|
id = vow_info[0];
|
||
|
spin_lock_irqsave(&vowdrv_dump_lock, flags);
|
||
|
vow_dump_info[id].user_dump_addr = vow_info[2];
|
||
|
vow_dump_info[id].user_dump_size = vow_info[3];
|
||
|
vow_dump_info[id].user_return_size_addr = vow_info[4];
|
||
|
vow_dump_info[id].user_dump_idx = 0;
|
||
|
spin_unlock_irqrestore(&vowdrv_dump_lock, flags);
|
||
|
//verb log
|
||
|
//VOWDRV_DEBUG("%s(): id %d, addr_%x, size_%x, addr_%x\n",
|
||
|
// __func__,
|
||
|
// id,
|
||
|
// (unsigned int)vow_info[2],
|
||
|
// (unsigned int)vow_info[3],
|
||
|
// (unsigned int)vow_info[4]);
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
static bool vow_service_SetApAddr(unsigned long arg)
|
||
|
{
|
||
|
unsigned long vow_info[MAX_VOW_INFO_LEN];
|
||
|
|
||
|
if (copy_from_user((void *)(&vow_info[0]), (const void __user *)(arg),
|
||
|
sizeof(vow_info))) {
|
||
|
VOWDRV_DEBUG("vow check parameter fail\n");
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/* add return condition */
|
||
|
if ((vow_info[2] == 0) || (vow_info[3] != VOW_VBUF_LENGTH) ||
|
||
|
(vow_info[4] == 0)) {
|
||
|
VOWDRV_DEBUG("vow SetVBufAddr:addr_%x, size_%x, addr_%x\n",
|
||
|
(unsigned int)vow_info[2],
|
||
|
(unsigned int)vow_info[3],
|
||
|
(unsigned int)vow_info[4]);
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
vowserv.voicedata_user_addr = vow_info[2];
|
||
|
vowserv.voicedata_user_size = vow_info[3];
|
||
|
vowserv.voicedata_user_return_size_addr = vow_info[4];
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
static bool vow_service_SetVBufAddr(unsigned long arg)
|
||
|
{
|
||
|
unsigned long vow_info[MAX_VOW_INFO_LEN];
|
||
|
|
||
|
if (copy_from_user((void *)(&vow_info[0]), (const void __user *)(arg),
|
||
|
sizeof(vowserv.vow_info_apuser))) {
|
||
|
VOWDRV_DEBUG("vow check parameter fail\n");
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/* add return condition */
|
||
|
if ((vow_info[2] == 0) || (vow_info[3] != VOW_VBUF_LENGTH) ||
|
||
|
(vow_info[4] == 0)) {
|
||
|
VOWDRV_DEBUG("vow SetVBufAddr:addr_%x, size_%x, addr_%x\n",
|
||
|
(unsigned int)vow_info[2],
|
||
|
(unsigned int)vow_info[3],
|
||
|
(unsigned int)vow_info[4]);
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
mutex_lock(&vow_vmalloc_lock);
|
||
|
if (vowserv.voicedata_kernel_ptr != NULL) {
|
||
|
vfree(vowserv.voicedata_kernel_ptr);
|
||
|
vowserv.voicedata_kernel_ptr = NULL;
|
||
|
}
|
||
|
|
||
|
if (vow_info[3] != VOW_VBUF_LENGTH) {
|
||
|
mutex_unlock(&vow_vmalloc_lock);
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
vowserv.voicedata_kernel_ptr = vmalloc(VOW_VBUF_LENGTH);
|
||
|
mutex_unlock(&vow_vmalloc_lock);
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
static bool vow_service_Enable(void)
|
||
|
{
|
||
|
bool ret = false;
|
||
|
|
||
|
VOWDRV_DEBUG("+%s()\n", __func__);
|
||
|
ret = vow_ipi_send(IPIMSG_VOW_ENABLE,
|
||
|
0,
|
||
|
NULL,
|
||
|
VOW_IPI_BYPASS_ACK);
|
||
|
|
||
|
VOWDRV_DEBUG("-%s():%d\n", __func__, ret);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static bool vow_service_Disable(void)
|
||
|
{
|
||
|
bool ret = false;
|
||
|
|
||
|
VOWDRV_DEBUG("+%s()\n", __func__);
|
||
|
|
||
|
ret = vow_ipi_send(IPIMSG_VOW_DISABLE,
|
||
|
0,
|
||
|
NULL,
|
||
|
VOW_IPI_BYPASS_ACK);
|
||
|
|
||
|
/* release lock */
|
||
|
if (vowserv.suspend_lock == 1) {
|
||
|
vowserv.suspend_lock = 0;
|
||
|
/* Let AP will suspend */
|
||
|
__pm_relax(vow_suspend_lock);
|
||
|
}
|
||
|
#ifdef CONFIG_MTK_TINYSYS_SCP_SUPPORT
|
||
|
vow_deregister_feature(VOW_FEATURE_ID);
|
||
|
#else
|
||
|
VOWDRV_DEBUG("%s(), vow: SCP no support\n\r", __func__);
|
||
|
#endif
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static void vow_check_boundary(unsigned int copy_len, unsigned int bound_len)
|
||
|
{
|
||
|
if (copy_len > bound_len) {
|
||
|
VOWDRV_DEBUG("[vow check]copy_len=0x%x, bound_len=0x%x\n",
|
||
|
copy_len, bound_len);
|
||
|
VOW_ASSERT(0);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#ifdef CONFIG_MTK_VOW_1STSTAGE_PCMCALLBACK
|
||
|
static void vow_service_ReadPayloadDumpData(unsigned int buf_length)
|
||
|
{
|
||
|
unsigned int tx_len;
|
||
|
unsigned int ret;
|
||
|
|
||
|
VOW_ASSERT(vowserv.payloaddump_kernel_ptr != NULL);
|
||
|
|
||
|
// copy from DRAM to get payload data
|
||
|
memcpy(&vowserv.payloaddump_kernel_ptr[0],
|
||
|
vowserv.payloaddump_scp_ptr, buf_length);
|
||
|
|
||
|
//copy to user space
|
||
|
tx_len = buf_length;
|
||
|
VOWDRV_DEBUG("[VOW PDR] buf_len=0x%x, MAX len=0x%x\n",
|
||
|
buf_length, vowserv.payloaddump_user_max_size);
|
||
|
|
||
|
if (buf_length > vowserv.payloaddump_user_max_size)
|
||
|
tx_len = vowserv.payloaddump_user_max_size;
|
||
|
|
||
|
ret = copy_to_user(
|
||
|
(void __user *)(vowserv.payloaddump_user_return_size_addr),
|
||
|
&tx_len,
|
||
|
sizeof(unsigned int));
|
||
|
ret = copy_to_user(
|
||
|
(void __user *)vowserv.payloaddump_user_addr,
|
||
|
vowserv.payloaddump_kernel_ptr,
|
||
|
tx_len);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
static void vow_interleaving(short *out_buf,
|
||
|
short *l_sample,
|
||
|
short *r_sample,
|
||
|
unsigned int buf_length)
|
||
|
{
|
||
|
int i;
|
||
|
int smpl_max;
|
||
|
|
||
|
smpl_max = buf_length / 2;
|
||
|
for (i = 0; i < smpl_max; i++) {
|
||
|
*out_buf++ = *l_sample++;
|
||
|
*out_buf++ = *r_sample++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static int vow_service_ReadVoiceData_Internal(unsigned int buf_offset,
|
||
|
unsigned int buf_length)
|
||
|
{
|
||
|
int stop_condition = 0;
|
||
|
|
||
|
if (buf_length != 0) {
|
||
|
if ((vowserv.kernel_voicedata_idx + (buf_length >> 1))
|
||
|
> (vowserv.voicedata_user_size >> 1)) {
|
||
|
VOWDRV_DEBUG(
|
||
|
"[vow check]data_idx=0x%x(W),buf_length=0x%x(B)\n",
|
||
|
vowserv.kernel_voicedata_idx, buf_length);
|
||
|
VOWDRV_DEBUG(
|
||
|
"[vow check] user_size=0x%x(B), scp_shared_voice_buf_offset=0x%x(B)\n",
|
||
|
(unsigned int)vowserv.voicedata_user_size,
|
||
|
buf_offset);
|
||
|
/* VOW_ASSERT(0); */
|
||
|
vowserv.kernel_voicedata_idx = 0;
|
||
|
}
|
||
|
mutex_lock(&vow_vmalloc_lock);
|
||
|
#if defined DUAL_CH_TRANSFER
|
||
|
/* start interleaving L+R */
|
||
|
vow_interleaving(
|
||
|
&vowserv.voicedata_kernel_ptr[vowserv.kernel_voicedata_idx],
|
||
|
(short *)(vowserv.voicedata_scp_ptr + buf_offset),
|
||
|
(short *)(vowserv.voicedata_scp_ptr + buf_offset +
|
||
|
VOW_VOICEDATA_SIZE),
|
||
|
buf_length);
|
||
|
/* end interleaving*/
|
||
|
#else
|
||
|
memcpy(&vowserv.voicedata_kernel_ptr[vowserv.kernel_voicedata_idx],
|
||
|
vowserv.voicedata_scp_ptr + buf_offset, buf_length);
|
||
|
#endif
|
||
|
mutex_unlock(&vow_vmalloc_lock);
|
||
|
|
||
|
if (buf_length > VOW_VOICE_RECORD_BIG_THRESHOLD) {
|
||
|
/* means now is start to transfer */
|
||
|
/* keyword buffer(64kB) to AP */
|
||
|
VOWDRV_DEBUG("%s(), start tx keyword, 0x%x/0x%x\n",
|
||
|
__func__,
|
||
|
vowserv.kernel_voicedata_idx,
|
||
|
buf_length);
|
||
|
vowserv.tx_keyword_start = true;
|
||
|
}
|
||
|
mutex_lock(&voicedata_mutex);
|
||
|
#if defined DUAL_CH_TRANSFER
|
||
|
/* 2 Channels */
|
||
|
vowserv.kernel_voicedata_idx += buf_length;
|
||
|
#else
|
||
|
/* 1 Channel */
|
||
|
vowserv.kernel_voicedata_idx += (buf_length >> 1);
|
||
|
#endif
|
||
|
mutex_unlock(&voicedata_mutex);
|
||
|
}
|
||
|
if (vowserv.kernel_voicedata_idx >= (VOW_VOICE_RECORD_BIG_THRESHOLD >> 1))
|
||
|
vowserv.transfer_length = VOW_VOICE_RECORD_BIG_THRESHOLD;
|
||
|
else
|
||
|
vowserv.transfer_length = VOW_VOICE_RECORD_THRESHOLD;
|
||
|
|
||
|
if (vowserv.kernel_voicedata_idx >= (vowserv.transfer_length >> 1)) {
|
||
|
unsigned int ret;
|
||
|
|
||
|
/*VOWDRV_DEBUG("TX Leng:%d, %d, [%d]\n",*/
|
||
|
/* vowserv.kernel_voicedata_idx,*/
|
||
|
/* buf_length,*/
|
||
|
/* vowserv.transfer_length);*/
|
||
|
|
||
|
ret = copy_to_user(
|
||
|
(void __user *)(vowserv.voicedata_user_return_size_addr),
|
||
|
&vowserv.transfer_length,
|
||
|
sizeof(unsigned int));
|
||
|
|
||
|
mutex_lock(&vow_vmalloc_lock);
|
||
|
ret = copy_to_user(
|
||
|
(void __user *)vowserv.voicedata_user_addr,
|
||
|
vowserv.voicedata_kernel_ptr,
|
||
|
vowserv.transfer_length);
|
||
|
mutex_unlock(&vow_vmalloc_lock);
|
||
|
|
||
|
/* move left data to buffer's head */
|
||
|
if (vowserv.kernel_voicedata_idx > (vowserv.transfer_length >> 1)) {
|
||
|
unsigned int tmp;
|
||
|
unsigned int idx;
|
||
|
|
||
|
tmp = (vowserv.kernel_voicedata_idx << 1)
|
||
|
- vowserv.transfer_length;
|
||
|
vow_check_boundary(tmp, vowserv.voicedata_user_size);
|
||
|
idx = (vowserv.transfer_length >> 1);
|
||
|
mutex_lock(&vow_vmalloc_lock);
|
||
|
memcpy(&vowserv.voicedata_kernel_ptr[0],
|
||
|
&vowserv.voicedata_kernel_ptr[idx],
|
||
|
tmp);
|
||
|
mutex_unlock(&vow_vmalloc_lock);
|
||
|
mutex_lock(&voicedata_mutex);
|
||
|
vowserv.kernel_voicedata_idx -= idx;
|
||
|
mutex_unlock(&voicedata_mutex);
|
||
|
} else {
|
||
|
mutex_lock(&voicedata_mutex);
|
||
|
vowserv.kernel_voicedata_idx = 0;
|
||
|
mutex_unlock(&voicedata_mutex);
|
||
|
}
|
||
|
|
||
|
/* speed up voicedata transfer to hal */
|
||
|
if (vowserv.kernel_voicedata_idx >= VOW_VOICE_RECORD_THRESHOLD)
|
||
|
vow_service_getVoiceData();
|
||
|
|
||
|
stop_condition = 1;
|
||
|
}
|
||
|
if ((vowserv.tx_keyword_start == true)
|
||
|
&& (vowserv.kernel_voicedata_idx < VOW_VOICE_RECORD_THRESHOLD)) {
|
||
|
/* means now is end of transfer keyword buffer(64kB) to AP */
|
||
|
vowserv.tx_keyword_start = false;
|
||
|
VOWDRV_DEBUG("%s(), end tx keyword, 0x%x\n",
|
||
|
__func__,
|
||
|
vowserv.kernel_voicedata_idx);
|
||
|
}
|
||
|
|
||
|
return stop_condition;
|
||
|
}
|
||
|
|
||
|
static void vow_service_GetVowDumpData(void)
|
||
|
{
|
||
|
unsigned int size = 0, i = 0, ret = 0, idx = 0, user_size = 0;
|
||
|
unsigned long flags;
|
||
|
|
||
|
if (vowserv.dump_pcm_flag == false)
|
||
|
return;
|
||
|
for (i = 0; i < NUM_DUMP_DATA; i++) {
|
||
|
//copy scp shared buffer to kernel
|
||
|
struct vow_dump_info_t temp_dump_info;
|
||
|
|
||
|
temp_dump_info = vow_dump_info[i];
|
||
|
idx = temp_dump_info.kernel_dump_idx;
|
||
|
if (temp_dump_info.vir_addr != NULL &&
|
||
|
temp_dump_info.scp_dump_size[0] != 0) {
|
||
|
if (vowserv.vow_mic_number == 2 &&
|
||
|
temp_dump_info.scp_dump_size[1] != 0) {
|
||
|
/* DRAM to kernel buffer and sample interleaving */
|
||
|
if ((idx +
|
||
|
(temp_dump_info.scp_dump_size[0] * 2)) >
|
||
|
temp_dump_info.kernel_dump_size) {
|
||
|
size = temp_dump_info.kernel_dump_size - idx;
|
||
|
VOWDRV_DEBUG("%s(), WARNING2 idx %d + %d, %d\n",
|
||
|
__func__,
|
||
|
idx, temp_dump_info.scp_dump_size[0] * 2,
|
||
|
temp_dump_info.kernel_dump_size);
|
||
|
} else {
|
||
|
size = temp_dump_info.scp_dump_size[0] * 2;
|
||
|
}
|
||
|
vow_interleaving(
|
||
|
&temp_dump_info.kernel_dump_addr[idx],
|
||
|
(short *)(temp_dump_info.vir_addr +
|
||
|
temp_dump_info.scp_dump_offset[0]),
|
||
|
(short *)(temp_dump_info.vir_addr +
|
||
|
temp_dump_info.scp_dump_offset[1]),
|
||
|
size / 2);
|
||
|
idx += size;
|
||
|
} else { // DRAM to kernel buffer. (only 1 channel)
|
||
|
if ((idx +
|
||
|
temp_dump_info.scp_dump_size[0]) >
|
||
|
temp_dump_info.kernel_dump_size) {
|
||
|
size = temp_dump_info.kernel_dump_size - idx;
|
||
|
VOWDRV_DEBUG("%s(), WARNING1 idx %d + %d, %d\n",
|
||
|
__func__,
|
||
|
idx, temp_dump_info.scp_dump_size[0] * 2,
|
||
|
temp_dump_info.kernel_dump_size);
|
||
|
} else {
|
||
|
size = temp_dump_info.scp_dump_size[0];
|
||
|
}
|
||
|
memcpy(&temp_dump_info.kernel_dump_addr[idx],
|
||
|
temp_dump_info.vir_addr +
|
||
|
temp_dump_info.scp_dump_offset[0],
|
||
|
size);
|
||
|
idx += size;
|
||
|
}
|
||
|
//{
|
||
|
// short *outPtr;
|
||
|
// outPtr = (short *)temp_dump_info.kernel_dump_addr;
|
||
|
// VOWDRV_DEBUG("%s(), %d idx %d size %d 0x%x, %d %d %d %d\n",
|
||
|
// __func__, i, idx, size, outPtr,
|
||
|
// outPtr[0], outPtr[1], outPtr[2], outPtr[3]
|
||
|
// );
|
||
|
//}
|
||
|
//copy kernel to user space
|
||
|
if ((temp_dump_info.user_dump_idx + idx) >
|
||
|
temp_dump_info.user_dump_size) {
|
||
|
size = temp_dump_info.user_dump_size -
|
||
|
temp_dump_info.user_dump_idx;
|
||
|
} else {
|
||
|
size = idx;
|
||
|
}
|
||
|
|
||
|
mutex_lock(&vow_vmalloc_lock);
|
||
|
ret = copy_to_user(
|
||
|
(void __user *)(temp_dump_info.user_dump_addr +
|
||
|
temp_dump_info.user_dump_idx),
|
||
|
temp_dump_info.kernel_dump_addr,
|
||
|
size);
|
||
|
mutex_unlock(&vow_vmalloc_lock);
|
||
|
temp_dump_info.user_dump_idx += size;
|
||
|
user_size = (unsigned int)temp_dump_info.user_dump_idx;
|
||
|
ret = copy_to_user(
|
||
|
(void __user *)(temp_dump_info.user_return_size_addr),
|
||
|
&user_size,
|
||
|
sizeof(unsigned int));
|
||
|
/* if there are left kernel buffer not copied to user buffer, */
|
||
|
/* move them to buffer's head */
|
||
|
if (idx > size) {
|
||
|
unsigned int size_left;
|
||
|
unsigned int idx_left;
|
||
|
|
||
|
size_left = idx - size;
|
||
|
vow_check_boundary(size_left, temp_dump_info.kernel_dump_size);
|
||
|
idx_left = size;
|
||
|
mutex_lock(&vow_vmalloc_lock);
|
||
|
memcpy(&temp_dump_info.kernel_dump_addr[0],
|
||
|
&temp_dump_info.kernel_dump_addr[idx_left],
|
||
|
size_left);
|
||
|
mutex_unlock(&vow_vmalloc_lock);
|
||
|
temp_dump_info.kernel_dump_idx = size_left;
|
||
|
} else
|
||
|
temp_dump_info.kernel_dump_idx = 0;
|
||
|
spin_lock_irqsave(&vowdrv_dump_lock, flags);
|
||
|
vow_dump_info[i].kernel_dump_idx = temp_dump_info.kernel_dump_idx;
|
||
|
vow_dump_info[i].user_dump_idx = temp_dump_info.user_dump_idx;
|
||
|
vow_dump_info[i].scp_dump_size[0] = 0;
|
||
|
vow_dump_info[i].scp_dump_size[1] = 0;
|
||
|
spin_unlock_irqrestore(&vowdrv_dump_lock, flags);
|
||
|
if (temp_dump_info.kernel_dump_idx != 0) {
|
||
|
VOWDRV_DEBUG("-%s(), %d kernel_idx %d\n",
|
||
|
__func__, i, vow_dump_info[i].kernel_dump_idx);
|
||
|
}
|
||
|
} //if (temp_dump_info.scp_dump_size[0] != 0)
|
||
|
} // for (i = 0; i < NUM_DUMP_DATA; i++)
|
||
|
}
|
||
|
static void vow_service_ReadVoiceData(void)
|
||
|
{
|
||
|
int stop_condition = 0;
|
||
|
int ret = 0;
|
||
|
/*int rdata;*/
|
||
|
while (1) {
|
||
|
if (VoiceData_Wait_Queue_flag == 0) {
|
||
|
ret = wait_event_interruptible_timeout(VoiceData_Wait_Queue,
|
||
|
VoiceData_Wait_Queue_flag, msecs_to_jiffies(50));
|
||
|
if (!ret) {
|
||
|
VOWDRV_DEBUG("%s, timeout,break\n", __func__);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (VoiceData_Wait_Queue_flag == 1) {
|
||
|
VoiceData_Wait_Queue_flag = 0;
|
||
|
if ((VowDrv_GetHWStatus() == VOW_PWR_OFF)
|
||
|
|| (vowserv.recording_flag == false)) {
|
||
|
mutex_lock(&voicedata_mutex);
|
||
|
vowserv.kernel_voicedata_idx = 0;
|
||
|
mutex_unlock(&voicedata_mutex);
|
||
|
stop_condition = 1;
|
||
|
VOWDRV_DEBUG(
|
||
|
"stop read vow voice data: %d, %d\n",
|
||
|
VowDrv_GetHWStatus(),
|
||
|
vowserv.recording_flag);
|
||
|
} else {
|
||
|
/* To Read Voice Data from Kernel to User */
|
||
|
stop_condition =
|
||
|
vow_service_ReadVoiceData_Internal(
|
||
|
vowserv.scp_shared_voice_buf_offset,
|
||
|
vowserv.scp_shared_voice_length);
|
||
|
vowserv.scp_shared_voice_buf_offset = 0;
|
||
|
vowserv.scp_shared_voice_length = 0;
|
||
|
vow_service_GetVowDumpData();
|
||
|
}
|
||
|
if (stop_condition == 1)
|
||
|
break;
|
||
|
} else {
|
||
|
VOWDRV_DEBUG("%s, 50ms timeout,break\n", __func__);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void vow_hal_reboot(void)
|
||
|
{
|
||
|
bool ret = false;
|
||
|
|
||
|
VOWDRV_DEBUG("%s(), Send VOW_HAL_REBOOT ipi\n", __func__);
|
||
|
|
||
|
ret = vow_ipi_send(IPIMSG_VOW_HAL_REBOOT, 0, NULL,
|
||
|
VOW_IPI_BYPASS_ACK);
|
||
|
if (ret == 0)
|
||
|
VOWDRV_DEBUG("IPIMSG_VOW_HAL_REBOOT ipi send error\n\r");
|
||
|
}
|
||
|
|
||
|
static void vow_service_reset(void)
|
||
|
{
|
||
|
int I;
|
||
|
bool need_disable_vow = false;
|
||
|
bool ret = false;
|
||
|
|
||
|
VOWDRV_DEBUG("+%s()\n", __func__);
|
||
|
vow_hal_reboot();
|
||
|
for (I = 0; I < MAX_VOW_SPEAKER_MODEL; I++) {
|
||
|
if (vowserv.vow_speaker_model[I].enabled == 1) {
|
||
|
int uuid;
|
||
|
|
||
|
/* need to close it */
|
||
|
ret = vow_service_SendModelStatus(
|
||
|
I, VOW_MODEL_STATUS_STOP);
|
||
|
if (ret == false)
|
||
|
VOWDRV_DEBUG("%s(), err\n", __func__);
|
||
|
|
||
|
uuid = vowserv.vow_speaker_model[I].uuid;
|
||
|
vow_deregister_vendor_feature(uuid);
|
||
|
vowserv.vow_speaker_model[I].enabled = 0;
|
||
|
need_disable_vow = true;
|
||
|
}
|
||
|
if (vowserv.vow_speaker_model[I].flag == 1) {
|
||
|
int id;
|
||
|
|
||
|
/* need to clear model */
|
||
|
id = vowserv.vow_speaker_model[I].id;
|
||
|
vow_service_ReleaseSpeakerModel(id);
|
||
|
}
|
||
|
}
|
||
|
if (need_disable_vow)
|
||
|
vow_service_Disable();
|
||
|
|
||
|
VOWDRV_DEBUG("-%s()\n", __func__);
|
||
|
}
|
||
|
|
||
|
#ifdef CONFIG_MTK_TINYSYS_SCP_SUPPORT
|
||
|
static bool vow_stop_dump_wait(void)
|
||
|
{
|
||
|
int timeout = 0;
|
||
|
|
||
|
while (1) {
|
||
|
msleep(VOW_WAITCHECK_INTERVAL_MS);
|
||
|
if (timeout++ >= VOW_STOP_DUMP_WAIT)
|
||
|
return false;
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
static int vow_pcm_dump_notify(bool enable)
|
||
|
{
|
||
|
unsigned int vow_ipi_buf[7] = {0};
|
||
|
bool ret;
|
||
|
|
||
|
/* dump flag */
|
||
|
vow_ipi_buf[1] = vowserv.dump_pcm_flag;
|
||
|
/* TOTAL dram resrved size for barge in dump */
|
||
|
vow_ipi_buf[0] = vow_dump_info[DUMP_BARGEIN].size;
|
||
|
/* address for SCP using */
|
||
|
vow_ipi_buf[2] = vow_dump_info[DUMP_BARGEIN].phy_addr;
|
||
|
|
||
|
VOWDRV_DEBUG(
|
||
|
"[BargeIn]dump on, dump flag:%d, resv sz:0x%x, addr:0x%x\n",
|
||
|
vow_ipi_buf[1],
|
||
|
vow_ipi_buf[0],
|
||
|
vow_ipi_buf[2]);
|
||
|
/* TOTAL dram resrved size for recog data dump */
|
||
|
vow_ipi_buf[3] = vow_dump_info[DUMP_RECOG].size;
|
||
|
/* address for SCP using */
|
||
|
vow_ipi_buf[4] = vow_dump_info[DUMP_RECOG].phy_addr;
|
||
|
/* TOTAL dram resrved size for vffp data dump */
|
||
|
vow_ipi_buf[5] = vow_dump_info[DUMP_VFFP].size;
|
||
|
/* address for SCP using */
|
||
|
vow_ipi_buf[6] = vow_dump_info[DUMP_VFFP].phy_addr;
|
||
|
|
||
|
VOWDRV_DEBUG(
|
||
|
"[Recog]dump on, dump flag:%d, resv sz:0x%x, addr:0x%x\n",
|
||
|
vow_ipi_buf[1],
|
||
|
vow_ipi_buf[3],
|
||
|
vow_ipi_buf[4]);
|
||
|
VOWDRV_DEBUG(
|
||
|
"[vffp]dump on, dump flag:%d, resv sz:0x%x, addr:0x%x\n",
|
||
|
vow_ipi_buf[1],
|
||
|
vow_ipi_buf[5],
|
||
|
vow_ipi_buf[6]);
|
||
|
|
||
|
/* if scp reset happened, need re-send PCM dump IPI to SCP again */
|
||
|
if (enable == true) {
|
||
|
ret = vow_ipi_send(IPIMSG_VOW_PCM_DUMP_ON,
|
||
|
7,
|
||
|
&vow_ipi_buf[0],
|
||
|
VOW_IPI_BYPASS_ACK);
|
||
|
|
||
|
if (ret == 0)
|
||
|
VOWDRV_DEBUG("PCM_DUMP_ON ipi send error\n");
|
||
|
} else {
|
||
|
ret = vow_ipi_send(IPIMSG_VOW_PCM_DUMP_OFF,
|
||
|
7,
|
||
|
&vow_ipi_buf[0],
|
||
|
VOW_IPI_BYPASS_ACK);
|
||
|
if (ret == 0)
|
||
|
VOWDRV_DEBUG("PCM_DUMP_OFF ipi send error\n");
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static bool vow_service_AllocKernelDumpBuffer(void)
|
||
|
{
|
||
|
unsigned int I = 0;
|
||
|
|
||
|
mutex_lock(&vow_vmalloc_lock);
|
||
|
for (I = 0; I < NUM_DUMP_DATA; I++) {
|
||
|
if (vow_dump_info[I].kernel_dump_addr != NULL) {
|
||
|
vfree(vow_dump_info[I].kernel_dump_addr);
|
||
|
vow_dump_info[I].kernel_dump_addr = NULL;
|
||
|
}
|
||
|
vow_dump_info[I].kernel_dump_addr = vmalloc(kReadVowDumpSize);
|
||
|
VOWDRV_DEBUG("%s vow_dump_info[%d].kernel_dump_addr = 0x%x\n",
|
||
|
__func__, I,
|
||
|
vow_dump_info[I].kernel_dump_addr);
|
||
|
VOW_ASSERT(vow_dump_info[I].kernel_dump_addr != NULL);
|
||
|
vow_dump_info[I].kernel_dump_idx = 0;
|
||
|
vow_dump_info[I].kernel_dump_size = kReadVowDumpSize;
|
||
|
}
|
||
|
mutex_unlock(&vow_vmalloc_lock);
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
static bool vow_service_FreeKernelDumpBuffer(void)
|
||
|
{
|
||
|
unsigned int I = 0;
|
||
|
|
||
|
mutex_lock(&vow_vmalloc_lock);
|
||
|
for (I = 0; I < NUM_DUMP_DATA; I++) {
|
||
|
if (vow_dump_info[I].kernel_dump_addr != NULL) {
|
||
|
vfree(vow_dump_info[I].kernel_dump_addr);
|
||
|
vow_dump_info[I].kernel_dump_addr = NULL;
|
||
|
}
|
||
|
vow_dump_info[I].kernel_dump_idx = 0;
|
||
|
vow_dump_info[I].kernel_dump_size = 0;
|
||
|
}
|
||
|
mutex_unlock(&vow_vmalloc_lock);
|
||
|
return true;
|
||
|
}
|
||
|
#endif /* #ifdef CONFIG_MTK_TINYSYS_SCP_SUPPORT */
|
||
|
|
||
|
static int vow_pcm_dump_set(bool enable)
|
||
|
{
|
||
|
VOWDRV_DEBUG("%s = %d, %d\n", __func__,
|
||
|
vowserv.dump_pcm_flag,
|
||
|
(unsigned int)enable);
|
||
|
#ifdef CONFIG_MTK_TINYSYS_SCP_SUPPORT
|
||
|
vow_dump_info[DUMP_DELAY_INFO].vir_addr =
|
||
|
(void *)&delay_info[0];
|
||
|
vow_dump_info[DUMP_DELAY_INFO].size =
|
||
|
sizeof(delay_info);
|
||
|
|
||
|
vow_dump_info[DUMP_BARGEIN].vir_addr =
|
||
|
(char *)(scp_get_reserve_mem_virt(VOW_BARGEIN_MEM_ID))
|
||
|
+ VOW_BARGEIN_AFE_MEMIF_SIZE;
|
||
|
vow_dump_info[DUMP_BARGEIN].phy_addr =
|
||
|
scp_get_reserve_mem_phys(VOW_BARGEIN_MEM_ID)
|
||
|
+ VOW_BARGEIN_AFE_MEMIF_SIZE;
|
||
|
vow_dump_info[DUMP_BARGEIN].size = BARGEIN_DUMP_TOTAL_BYTE_CNT;
|
||
|
VOWDRV_DEBUG("[Barge]vir: %p, phys: 0x%x\n",
|
||
|
vow_dump_info[DUMP_BARGEIN].vir_addr,
|
||
|
(unsigned int)vow_dump_info[DUMP_BARGEIN].phy_addr);
|
||
|
// input share same address of bargein
|
||
|
vow_dump_info[DUMP_INPUT].vir_addr =
|
||
|
vow_dump_info[DUMP_BARGEIN].vir_addr;
|
||
|
vow_dump_info[DUMP_INPUT].phy_addr =
|
||
|
vow_dump_info[DUMP_BARGEIN].phy_addr;
|
||
|
|
||
|
vow_dump_info[DUMP_RECOG].vir_addr =
|
||
|
(char *)(scp_get_reserve_mem_virt(VOW_MEM_ID))
|
||
|
+ VOW_RECOGDATA_OFFSET;
|
||
|
vow_dump_info[DUMP_RECOG].phy_addr =
|
||
|
scp_get_reserve_mem_phys(VOW_MEM_ID)
|
||
|
+ VOW_RECOGDATA_OFFSET;
|
||
|
vow_dump_info[DUMP_RECOG].size = RECOG_DUMP_TOTAL_BYTE_CNT;
|
||
|
VOWDRV_DEBUG("[Recog]vir: %p, phys: 0x%x\n",
|
||
|
vow_dump_info[DUMP_RECOG].vir_addr,
|
||
|
(unsigned int)vow_dump_info[DUMP_RECOG].phy_addr);
|
||
|
|
||
|
vow_dump_info[DUMP_VFFP].vir_addr =
|
||
|
(char *)(scp_get_reserve_mem_virt(VOW_MEM_ID))
|
||
|
+ VOW_VFFPDATA_OFFSET;
|
||
|
vow_dump_info[DUMP_VFFP].phy_addr =
|
||
|
scp_get_reserve_mem_phys(VOW_MEM_ID)
|
||
|
+ VOW_VFFPDATA_OFFSET;
|
||
|
vow_dump_info[DUMP_VFFP].size = VFFP_DUMP_TOTAL_BYTE_CNT;
|
||
|
VOWDRV_DEBUG("[vffp]vir: %p, phys: 0x%x\n",
|
||
|
vow_dump_info[DUMP_VFFP].vir_addr,
|
||
|
(unsigned int)vow_dump_info[DUMP_VFFP].phy_addr);
|
||
|
|
||
|
if ((vowserv.dump_pcm_flag == false) && (enable == true)) {
|
||
|
vowserv.dump_pcm_flag = true;
|
||
|
vow_service_AllocKernelDumpBuffer();
|
||
|
vow_pcm_dump_notify(true);
|
||
|
} else if ((vowserv.dump_pcm_flag == true) && (enable == false)) {
|
||
|
vowserv.dump_pcm_flag = false;
|
||
|
vow_pcm_dump_notify(false);
|
||
|
vow_stop_dump_wait();
|
||
|
vow_service_FreeKernelDumpBuffer();
|
||
|
}
|
||
|
#else
|
||
|
VOWDRV_DEBUG("%s(), vow: SCP no support\n\r", __func__);
|
||
|
#endif /* #ifdef CONFIG_MTK_TINYSYS_SCP_SUPPORT */
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static void vow_pcm_dump_init(void)
|
||
|
{
|
||
|
VOWDRV_DEBUG("[Recog] %s()\n", __func__);
|
||
|
if (vowserv.interleave_pcmdata_ptr != NULL) {
|
||
|
vfree(vowserv.interleave_pcmdata_ptr);
|
||
|
vowserv.interleave_pcmdata_ptr = NULL;
|
||
|
}
|
||
|
/* Temp buffer for doing DUALMIC L/R channels interleave */
|
||
|
vowserv.interleave_pcmdata_ptr =
|
||
|
vmalloc(VOW_PCM_DUMP_BYTE_SIZE << 1);
|
||
|
}
|
||
|
|
||
|
static void vow_pcm_dump_deinit(void)
|
||
|
{
|
||
|
VOWDRV_DEBUG("[BargeIn] %s()\n", __func__);
|
||
|
|
||
|
if (vowserv.interleave_pcmdata_ptr != NULL) {
|
||
|
vfree(vowserv.interleave_pcmdata_ptr);
|
||
|
vowserv.interleave_pcmdata_ptr = NULL;
|
||
|
}
|
||
|
}
|
||
|
/*****************************************************************************
|
||
|
* VOW CONTROL FUNCTIONS
|
||
|
*****************************************************************************/
|
||
|
|
||
|
static int VowDrv_SetHWStatus(int status)
|
||
|
{
|
||
|
int ret = 0;
|
||
|
|
||
|
VOWDRV_DEBUG("%s(): set:%x, cur:%x\n",
|
||
|
__func__, status, vowserv.pwr_status);
|
||
|
if ((status < NUM_OF_VOW_PWR_STATUS) && (status >= VOW_PWR_OFF)) {
|
||
|
spin_lock(&vowdrv_lock);
|
||
|
vowserv.pwr_status = status;
|
||
|
spin_unlock(&vowdrv_lock);
|
||
|
} else {
|
||
|
VOWDRV_DEBUG("error input: %d\n", status);
|
||
|
ret = -1;
|
||
|
}
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static int VowDrv_GetHWStatus(void)
|
||
|
{
|
||
|
int ret = 0;
|
||
|
|
||
|
spin_lock(&vowdrv_lock);
|
||
|
ret = vowserv.pwr_status;
|
||
|
spin_unlock(&vowdrv_lock);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
int VowDrv_EnableHW(int status)
|
||
|
{
|
||
|
int ret = 0;
|
||
|
int pwr_status = 0;
|
||
|
|
||
|
VOWDRV_DEBUG("%s(): %x\n", __func__, status);
|
||
|
|
||
|
if (status < 0) {
|
||
|
VOWDRV_DEBUG("%s(), error input: %x\n", __func__, status);
|
||
|
ret = -1;
|
||
|
} else {
|
||
|
pwr_status = (status == 0) ? VOW_PWR_OFF : VOW_PWR_ON;
|
||
|
|
||
|
if (pwr_status == VOW_PWR_OFF) {
|
||
|
/* reset the transfer limitation to */
|
||
|
/* avoid obstructing phase2.5 transferring */
|
||
|
if (vowserv.tx_keyword_start == true)
|
||
|
vowserv.tx_keyword_start = false;
|
||
|
}
|
||
|
if (pwr_status == VOW_PWR_ON) {
|
||
|
#ifdef CONFIG_MTK_TINYSYS_SCP_SUPPORT
|
||
|
vow_register_feature(VOW_FEATURE_ID);
|
||
|
#else
|
||
|
VOWDRV_DEBUG("%s(), vow: SCP no support\n\r", __func__);
|
||
|
#endif
|
||
|
/* clear enter_phase3_cnt */
|
||
|
vowserv.enter_phase3_cnt = 0;
|
||
|
}
|
||
|
VowDrv_SetHWStatus(pwr_status);
|
||
|
}
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
int VowDrv_ChangeStatus(void)
|
||
|
{
|
||
|
VowDrv_Wait_Queue_flag = 1;
|
||
|
wake_up_interruptible(&VowDrv_Wait_Queue);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
void VowDrv_SetSmartDevice(bool enable)
|
||
|
{
|
||
|
unsigned int eint_num;
|
||
|
unsigned int ints[2] = {0, 0};
|
||
|
unsigned int vow_ipi_buf[2];
|
||
|
bool ret;
|
||
|
|
||
|
if (!vow_check_scp_status()) {
|
||
|
VOWDRV_DEBUG("SCP is off, do not support VOW\n");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
VOWDRV_DEBUG("%s():%x\n", __func__, enable);
|
||
|
if (vowserv.node) {
|
||
|
/* query eint number from device tree */
|
||
|
ret = of_property_read_u32_array(vowserv.node,
|
||
|
"debounce",
|
||
|
ints,
|
||
|
ARRAY_SIZE(ints));
|
||
|
if (ret != 0) {
|
||
|
VOWDRV_DEBUG("%s(), no debounce node, ret=%d\n",
|
||
|
__func__, ret);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
eint_num = ints[0];
|
||
|
|
||
|
if (enable == false)
|
||
|
eint_num = 0xFF;
|
||
|
|
||
|
vow_ipi_buf[0] = enable;
|
||
|
vow_ipi_buf[1] = eint_num;
|
||
|
ret = vow_ipi_send(IPIMSG_VOW_SET_SMART_DEVICE,
|
||
|
2,
|
||
|
&vow_ipi_buf[0],
|
||
|
VOW_IPI_BYPASS_ACK);
|
||
|
if (ret == 0) {
|
||
|
VOWDRV_DEBUG(
|
||
|
"IPIMSG_VOW_SET_SMART_DEVICE ipi send error\n\r");
|
||
|
}
|
||
|
} else {
|
||
|
/* no node here */
|
||
|
VOWDRV_DEBUG("there is no node\n");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void VowDrv_SetSmartDevice_GPIO(bool enable)
|
||
|
{
|
||
|
int ret = 0;
|
||
|
|
||
|
if (vowserv.node) {
|
||
|
if (enable == false) {
|
||
|
VOWDRV_DEBUG("VowDrv_SetSmartDev_gpio:OFF\n");
|
||
|
ret = pinctrl_select_state(vowserv.pinctrl,
|
||
|
vowserv.pins_eint_off);
|
||
|
if (ret) {
|
||
|
/* pinctrl setting error */
|
||
|
VOWDRV_DEBUG(
|
||
|
"error, can not set gpio vow pins_eint_off\n");
|
||
|
}
|
||
|
} else {
|
||
|
VOWDRV_DEBUG("VowDrv_SetSmartDev_gpio:ON\n");
|
||
|
ret = pinctrl_select_state(vowserv.pinctrl,
|
||
|
vowserv.pins_eint_on);
|
||
|
if (ret) {
|
||
|
/* pinctrl setting error */
|
||
|
VOWDRV_DEBUG(
|
||
|
"error, can not set gpio vow pins_eint_on\n");
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
/* no node here */
|
||
|
VOWDRV_DEBUG("there is no node\n");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static bool VowDrv_SetFlag(int type, unsigned int set)
|
||
|
{
|
||
|
bool ret = false;
|
||
|
unsigned int vow_ipi_buf[2];
|
||
|
|
||
|
VOWDRV_DEBUG("%s(), type:%x, set:%x\n", __func__, type, set);
|
||
|
vow_ipi_buf[0] = type;
|
||
|
vow_ipi_buf[1] = set;
|
||
|
|
||
|
ret = vow_ipi_send(IPIMSG_VOW_SET_FLAG,
|
||
|
2,
|
||
|
&vow_ipi_buf[0],
|
||
|
VOW_IPI_BYPASS_ACK);
|
||
|
if (ret == 0)
|
||
|
VOWDRV_DEBUG("IPIMSG_VOW_SET_FLAG ipi send error\n\r");
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
void VowDrv_SetDmicLowPower(bool enable)
|
||
|
{
|
||
|
VowDrv_SetFlag(VOW_FLAG_DMIC_LOWPOWER, enable);
|
||
|
}
|
||
|
|
||
|
static bool VowDrv_SetMtkifType(unsigned int type)
|
||
|
{
|
||
|
bool ret = false;
|
||
|
|
||
|
if (type == 0)
|
||
|
VOWDRV_DEBUG("error, wrong MTKIF Type\n\r");
|
||
|
vowserv.mtkif_type = type;
|
||
|
ret = VowDrv_SetFlag(VOW_FLAG_MTKIF_TYPE, type);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static bool VowDrv_CheckMtkifType(unsigned int type)
|
||
|
{
|
||
|
unsigned int mtkif_type = type & 0x0F;
|
||
|
unsigned int ch_num = type >> 4;
|
||
|
|
||
|
if (mtkif_type >= VOW_MTKIF_MAX || mtkif_type < 0) {
|
||
|
VOWDRV_DEBUG("out of VOW_MTKIF_TYPE %d\n\r", mtkif_type);
|
||
|
return false;
|
||
|
}
|
||
|
if (ch_num >= VOW_CH_MAX || ch_num < 0) {
|
||
|
VOWDRV_DEBUG("out of VOW_MIC_NUM %d\n\r", ch_num);
|
||
|
return false;
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
void VowDrv_SetPeriodicEnable(bool enable)
|
||
|
{
|
||
|
VowDrv_SetFlag(VOW_FLAG_PERIODIC_ENABLE, enable);
|
||
|
}
|
||
|
|
||
|
static ssize_t VowDrv_GetPhase1Debug(struct device *kobj,
|
||
|
struct device_attribute *attr,
|
||
|
char *buf)
|
||
|
{
|
||
|
unsigned int stat;
|
||
|
char cstr[35];
|
||
|
int size = sizeof(cstr);
|
||
|
|
||
|
stat = (vowserv.force_phase_stage == FORCE_PHASE1) ? 1 : 0;
|
||
|
|
||
|
return snprintf(buf, size, "Force Phase1 Setting = %s\n",
|
||
|
(stat == 0x1) ? "YES" : "NO");
|
||
|
}
|
||
|
|
||
|
static ssize_t VowDrv_SetPhase1Debug(struct device *kobj,
|
||
|
struct device_attribute *attr,
|
||
|
const char *buf,
|
||
|
size_t n)
|
||
|
{
|
||
|
unsigned int enable = 0;
|
||
|
|
||
|
if (kstrtouint(buf, 0, &enable) != 0)
|
||
|
return -EINVAL;
|
||
|
|
||
|
VowDrv_SetFlag(VOW_FLAG_FORCE_PHASE1_DEBUG, enable);
|
||
|
return n;
|
||
|
}
|
||
|
DEVICE_ATTR(vow_SetPhase1,
|
||
|
0644, /*S_IWUSR | S_IRUGO*/
|
||
|
VowDrv_GetPhase1Debug,
|
||
|
VowDrv_SetPhase1Debug);
|
||
|
|
||
|
static ssize_t VowDrv_GetPhase2Debug(struct device *kobj,
|
||
|
struct device_attribute *attr,
|
||
|
char *buf)
|
||
|
{
|
||
|
unsigned int stat;
|
||
|
char cstr[35];
|
||
|
int size = sizeof(cstr);
|
||
|
|
||
|
stat = (vowserv.force_phase_stage == FORCE_PHASE2) ? 1 : 0;
|
||
|
|
||
|
return snprintf(buf, size, "Force Phase2 Setting = %s\n",
|
||
|
(stat == 0x1) ? "YES" : "NO");
|
||
|
}
|
||
|
|
||
|
static ssize_t VowDrv_SetPhase2Debug(struct device *kobj,
|
||
|
struct device_attribute *attr,
|
||
|
const char *buf,
|
||
|
size_t n)
|
||
|
{
|
||
|
unsigned int enable = 0;
|
||
|
|
||
|
if (kstrtouint(buf, 0, &enable) != 0)
|
||
|
return -EINVAL;
|
||
|
|
||
|
VowDrv_SetFlag(VOW_FLAG_FORCE_PHASE2_DEBUG, enable);
|
||
|
return n;
|
||
|
}
|
||
|
DEVICE_ATTR(vow_SetPhase2,
|
||
|
0644, /*S_IWUSR | S_IRUGO*/
|
||
|
VowDrv_GetPhase2Debug,
|
||
|
VowDrv_SetPhase2Debug);
|
||
|
|
||
|
static ssize_t VowDrv_GetDualMicDebug(struct device *kobj,
|
||
|
struct device_attribute *attr,
|
||
|
char *buf)
|
||
|
{
|
||
|
char cstr[35];
|
||
|
int size = sizeof(cstr);
|
||
|
|
||
|
if (vowserv.scp_dual_mic_switch == VOW_ENABLE_DUAL_MIC)
|
||
|
return snprintf(buf, size, "use Daul mic\n");
|
||
|
else if (vowserv.scp_dual_mic_switch == VOW_ENABLE_SINGLE_MAIN_MIC)
|
||
|
return snprintf(buf, size, "use Single mic: main mic\n");
|
||
|
else if (vowserv.scp_dual_mic_switch == VOW_ENABLE_SINGLE_REF_MIC)
|
||
|
return snprintf(buf, size, "use Single mic: ref mic\n");
|
||
|
else
|
||
|
return snprintf(buf, size, "set mic error\n");
|
||
|
}
|
||
|
|
||
|
static ssize_t VowDrv_SetDualMicDebug(struct device *kobj,
|
||
|
struct device_attribute *attr,
|
||
|
const char *buf,
|
||
|
size_t n)
|
||
|
{
|
||
|
unsigned int enable = 0;
|
||
|
|
||
|
if (kstrtouint(buf, 0, &enable) != 0)
|
||
|
return -EINVAL;
|
||
|
|
||
|
vowserv.scp_dual_mic_switch = enable;
|
||
|
|
||
|
VowDrv_SetFlag(VOW_FLAG_DUAL_MIC_SWITCH, enable);
|
||
|
return n;
|
||
|
}
|
||
|
DEVICE_ATTR(vow_DualMicSwitch,
|
||
|
0644, /*S_IWUSR | S_IRUGO*/
|
||
|
VowDrv_GetDualMicDebug,
|
||
|
VowDrv_SetDualMicDebug);
|
||
|
|
||
|
static ssize_t VowDrv_GetSplitDumpFile(struct device *kobj,
|
||
|
struct device_attribute *attr,
|
||
|
char *buf)
|
||
|
{
|
||
|
unsigned int stat;
|
||
|
char cstr[35];
|
||
|
int size = sizeof(cstr);
|
||
|
|
||
|
stat = (vowserv.split_dumpfile_flag == true) ? 1 : 0;
|
||
|
|
||
|
return snprintf(buf, size, "Split dump file = %s\n",
|
||
|
(stat == 0x1) ? "YES" : "NO");
|
||
|
}
|
||
|
|
||
|
static ssize_t VowDrv_SetSplitDumpFile(struct device *kobj,
|
||
|
struct device_attribute *attr,
|
||
|
const char *buf,
|
||
|
size_t n)
|
||
|
{
|
||
|
unsigned int enable = 0;
|
||
|
|
||
|
if (kstrtouint(buf, 0, &enable) != 0)
|
||
|
return -EINVAL;
|
||
|
|
||
|
VOWDRV_DEBUG("%s(), enable = %d\n", __func__, enable);
|
||
|
vowserv.split_dumpfile_flag = (enable == 1) ? true : false;
|
||
|
|
||
|
return n;
|
||
|
}
|
||
|
DEVICE_ATTR(vow_SplitDumpFile,
|
||
|
0644, /*S_IWUSR | S_IRUGO*/
|
||
|
VowDrv_GetSplitDumpFile,
|
||
|
VowDrv_SetSplitDumpFile);
|
||
|
|
||
|
|
||
|
static ssize_t VowDrv_SetBargeInDebug(struct device *kobj,
|
||
|
struct device_attribute *attr,
|
||
|
const char *buf,
|
||
|
size_t n)
|
||
|
{
|
||
|
unsigned int enable = 0;
|
||
|
|
||
|
if (kstrtouint(buf, 0, &enable) != 0)
|
||
|
return -EINVAL;
|
||
|
|
||
|
VowDrv_SetBargeIn(enable, 1); /* temp fix irq */
|
||
|
return n;
|
||
|
}
|
||
|
DEVICE_ATTR(vow_SetBargeIn,
|
||
|
0200, /*S_IWUSR*/
|
||
|
NULL,
|
||
|
VowDrv_SetBargeInDebug);
|
||
|
|
||
|
static bool VowDrv_SetBargeIn(unsigned int set, unsigned int irq_id)
|
||
|
{
|
||
|
bool ret = false;
|
||
|
#ifdef CONFIG_MTK_TINYSYS_SCP_SUPPORT
|
||
|
unsigned int vow_ipi_buf[1];
|
||
|
|
||
|
if (irq_id >= VOW_BARGEIN_IRQ_MAX_NUM || irq_id < 0) {
|
||
|
VOWDRV_DEBUG("out of vow bargein irq range %d", irq_id);
|
||
|
return ret;
|
||
|
}
|
||
|
vow_ipi_buf[0] = irq_id;
|
||
|
|
||
|
VOWDRV_DEBUG("%s(), set = %d, irq = %d\n", __func__, set, irq_id);
|
||
|
if (set == 1) {
|
||
|
vow_register_feature(VOW_BARGEIN_FEATURE_ID);
|
||
|
|
||
|
ret = vow_ipi_send(IPIMSG_VOW_SET_BARGEIN_ON,
|
||
|
1,
|
||
|
&vow_ipi_buf[0],
|
||
|
VOW_IPI_NEED_ACK);
|
||
|
} else if (set == 0) {
|
||
|
|
||
|
ret = vow_ipi_send(IPIMSG_VOW_SET_BARGEIN_OFF,
|
||
|
1,
|
||
|
&vow_ipi_buf[0],
|
||
|
VOW_IPI_NEED_ACK);
|
||
|
vow_deregister_feature(VOW_BARGEIN_FEATURE_ID);
|
||
|
} else {
|
||
|
VOWDRV_DEBUG("Adb comment error\n\r");
|
||
|
}
|
||
|
if (ret == 0)
|
||
|
VOWDRV_DEBUG("IPIMSG_BARGE_IN(%d) ipi send error\n", set);
|
||
|
#else
|
||
|
VOWDRV_DEBUG("%s(), vow: SCP no support\n\r", __func__);
|
||
|
#endif
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static ssize_t VowDrv_GetBypassPhase3Flag(struct device *kobj,
|
||
|
struct device_attribute *attr,
|
||
|
char *buf)
|
||
|
{
|
||
|
unsigned int stat;
|
||
|
char cstr[35];
|
||
|
int size = sizeof(cstr);
|
||
|
|
||
|
stat = (vowserv.bypass_enter_phase3 == true) ? 1 : 0;
|
||
|
|
||
|
return snprintf(buf, size, "Enter Phase3 Setting is %s\n",
|
||
|
(stat == 0x1) ? "Bypass" : "Allow");
|
||
|
}
|
||
|
|
||
|
static ssize_t VowDrv_SetBypassPhase3Flag(struct device *kobj,
|
||
|
struct device_attribute *attr,
|
||
|
const char *buf,
|
||
|
size_t n)
|
||
|
{
|
||
|
unsigned int enable = 0;
|
||
|
|
||
|
if (kstrtouint(buf, 0, &enable) != 0)
|
||
|
return -EINVAL;
|
||
|
|
||
|
if (enable == 0) {
|
||
|
VOWDRV_DEBUG("Allow enter phase3\n");
|
||
|
vowserv.bypass_enter_phase3 = false;
|
||
|
} else {
|
||
|
VOWDRV_DEBUG("Bypass enter phase3\n");
|
||
|
vowserv.bypass_enter_phase3 = true;
|
||
|
}
|
||
|
return n;
|
||
|
}
|
||
|
DEVICE_ATTR(vow_SetBypassPhase3,
|
||
|
0644, /*S_IWUSR | S_IRUGO*/
|
||
|
VowDrv_GetBypassPhase3Flag,
|
||
|
VowDrv_SetBypassPhase3Flag);
|
||
|
|
||
|
static ssize_t VowDrv_GetEnterPhase3Counter(struct device *kobj,
|
||
|
struct device_attribute *attr,
|
||
|
char *buf)
|
||
|
{
|
||
|
char cstr[35];
|
||
|
int size = sizeof(cstr);
|
||
|
|
||
|
return snprintf(buf, size, "Enter Phase3 Counter is %u\n",
|
||
|
vowserv.enter_phase3_cnt);
|
||
|
}
|
||
|
DEVICE_ATTR(vow_GetEnterPhase3Counter,
|
||
|
0444, /*S_IRUGO*/
|
||
|
VowDrv_GetEnterPhase3Counter,
|
||
|
NULL);
|
||
|
|
||
|
static ssize_t VowDrv_GetSWIPLog(struct device *kobj,
|
||
|
struct device_attribute *attr,
|
||
|
char *buf)
|
||
|
{
|
||
|
unsigned int stat;
|
||
|
char cstr[20];
|
||
|
int size = sizeof(cstr);
|
||
|
|
||
|
stat = (vowserv.swip_log_enable == true) ? 1 : 0;
|
||
|
return snprintf(buf, size, "SWIP LOG is %s\n",
|
||
|
(stat == true) ? "YES" : "NO");
|
||
|
}
|
||
|
|
||
|
static ssize_t VowDrv_SetSWIPLog(struct device *kobj,
|
||
|
struct device_attribute *attr,
|
||
|
const char *buf,
|
||
|
size_t n)
|
||
|
{
|
||
|
unsigned int enable = 0;
|
||
|
|
||
|
if (kstrtouint(buf, 0, &enable) != 0)
|
||
|
return -EINVAL;
|
||
|
|
||
|
VowDrv_SetFlag(VOW_FLAG_SWIP_LOG_PRINT, enable);
|
||
|
return n;
|
||
|
}
|
||
|
DEVICE_ATTR(vow_SetLibLog,
|
||
|
0644, /*S_IWUSR | S_IRUGO*/
|
||
|
VowDrv_GetSWIPLog,
|
||
|
VowDrv_SetSWIPLog);
|
||
|
|
||
|
static ssize_t VowDrv_SetEnableHW(struct device *kobj,
|
||
|
struct device_attribute *attr,
|
||
|
const char *buf,
|
||
|
size_t n)
|
||
|
{
|
||
|
unsigned int enable = 0;
|
||
|
|
||
|
if (kstrtouint(buf, 0, &enable) != 0)
|
||
|
return -EINVAL;
|
||
|
|
||
|
VowDrv_EnableHW(enable);
|
||
|
VowDrv_ChangeStatus();
|
||
|
return n;
|
||
|
}
|
||
|
DEVICE_ATTR(vow_SetEnableHW,
|
||
|
0200, /*S_IWUSR*/
|
||
|
NULL,
|
||
|
VowDrv_SetEnableHW);
|
||
|
|
||
|
static ssize_t VowDrv_GetMCPSflag(struct device *kobj,
|
||
|
struct device_attribute *attr,
|
||
|
char *buf)
|
||
|
{
|
||
|
unsigned int stat;
|
||
|
char cstr[35];
|
||
|
int size = sizeof(cstr);
|
||
|
|
||
|
stat = (vowserv.mcps_flag == true) ? 1 : 0;
|
||
|
|
||
|
return snprintf(buf, size, "Enable Measure MCPS = %s\n",
|
||
|
(stat == 0x1) ? "YES" : "NO");
|
||
|
}
|
||
|
|
||
|
static ssize_t VowDrv_SetMCPSflag(struct device *kobj,
|
||
|
struct device_attribute *attr,
|
||
|
const char *buf,
|
||
|
size_t n)
|
||
|
{
|
||
|
unsigned int enable = 0;
|
||
|
|
||
|
if (kstrtouint(buf, 0, &enable) != 0)
|
||
|
return -EINVAL;
|
||
|
|
||
|
VowDrv_SetFlag(VOW_FLAG_MCPS, enable);
|
||
|
vowserv.mcps_flag = (enable == 1) ? true : false;
|
||
|
VOWDRV_DEBUG("%s(),enable=%d\n", __func__, enable);
|
||
|
return n;
|
||
|
}
|
||
|
DEVICE_ATTR(vow_SetMCPS,
|
||
|
0644, /*S_IWUSR | S_IRUGO*/
|
||
|
VowDrv_GetMCPSflag,
|
||
|
VowDrv_SetMCPSflag);
|
||
|
|
||
|
static int VowDrv_SetVowEINTStatus(int status)
|
||
|
{
|
||
|
int ret = 0;
|
||
|
int wakeup_event = 0;
|
||
|
|
||
|
if ((status < NUM_OF_VOW_EINT_STATUS)
|
||
|
&& (status >= VOW_EINT_DISABLE)) {
|
||
|
spin_lock(&vowdrv_lock);
|
||
|
if ((vowserv.eint_status != VOW_EINT_PASS)
|
||
|
&& (status == VOW_EINT_PASS))
|
||
|
wakeup_event = 1;
|
||
|
vowserv.eint_status = status;
|
||
|
spin_unlock(&vowdrv_lock);
|
||
|
} else {
|
||
|
VOWDRV_DEBUG("%s() error input: %x\n",
|
||
|
__func__, status);
|
||
|
ret = -1;
|
||
|
}
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static int VowDrv_QueryVowEINTStatus(void)
|
||
|
{
|
||
|
int ret = 0;
|
||
|
|
||
|
spin_lock(&vowdrv_lock);
|
||
|
ret = vowserv.eint_status;
|
||
|
spin_unlock(&vowdrv_lock);
|
||
|
VOWDRV_DEBUG("%s():%d\n", __func__, ret);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static int VowDrv_open(struct inode *inode, struct file *fp)
|
||
|
{
|
||
|
VOWDRV_DEBUG("%s() do nothing inode:%p, file:%p\n",
|
||
|
__func__, inode, fp);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int VowDrv_release(struct inode *inode, struct file *fp)
|
||
|
{
|
||
|
VOWDRV_DEBUG("%s() inode:%p, file:%p\n", __func__, inode, fp);
|
||
|
|
||
|
if (!(fp->f_mode & FMODE_WRITE || fp->f_mode & FMODE_READ))
|
||
|
return -ENODEV;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static bool VowDrv_SetWakeupMode(unsigned int mode)
|
||
|
{
|
||
|
bool ret = false;
|
||
|
unsigned int vow_ipi_buf[1];
|
||
|
|
||
|
VOWDRV_DEBUG("%s(), set:%x\n", __func__, mode);
|
||
|
vow_ipi_buf[0] = mode;
|
||
|
|
||
|
ret = vow_ipi_send(IPIMSG_VOW_SET_BIXBY_ENGINE_MODE,
|
||
|
1,
|
||
|
&vow_ipi_buf[0],
|
||
|
VOW_IPI_NEED_ACK);
|
||
|
if (ret == 0)
|
||
|
VOWDRV_DEBUG("IPIMSG_VOW_SET_BIXBY_ENGINE_MODE ipi send error\n\r");
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static long VowDrv_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
|
||
|
{
|
||
|
int ret = 0;
|
||
|
int timeout = 0;
|
||
|
|
||
|
timeout = 0;
|
||
|
while (vowserv.vow_recovering) {
|
||
|
msleep(VOW_WAITCHECK_INTERVAL_MS);
|
||
|
timeout++;
|
||
|
VOW_ASSERT(timeout != VOW_RECOVERY_WAIT);
|
||
|
}
|
||
|
|
||
|
/* VOWDRV_DEBUG("VowDrv_ioctl cmd = %u arg = %lu\n", cmd, arg); */
|
||
|
switch ((unsigned int)cmd) {
|
||
|
case VOW_SET_CONTROL:
|
||
|
switch (arg) {
|
||
|
case VOWControlCmd_Init:
|
||
|
VOWDRV_DEBUG("VOW_SET_CONTROL Init");
|
||
|
vow_service_Init();
|
||
|
break;
|
||
|
case VOWControlCmd_Reset:
|
||
|
VOWDRV_DEBUG("VOW_SET_CONTROL Reset");
|
||
|
vow_service_reset();
|
||
|
break;
|
||
|
case VOWControlCmd_EnableDebug:
|
||
|
VOWDRV_DEBUG("VOW_SET_CONTROL EnableDebug");
|
||
|
mutex_lock(&voicedata_mutex);
|
||
|
vowserv.kernel_voicedata_idx = 0;
|
||
|
mutex_unlock(&voicedata_mutex);
|
||
|
vowserv.recording_flag = true;
|
||
|
vowserv.firstRead = true;
|
||
|
/*VowDrv_SetFlag(VOW_FLAG_DEBUG, true);*/
|
||
|
#ifdef CONFIG_MTK_TINYSYS_SCP_SUPPORT
|
||
|
vow_register_feature(VOW_DUMP_FEATURE_ID);
|
||
|
#else
|
||
|
VOWDRV_DEBUG("%s(), vow: SCP no support\n\r", __func__);
|
||
|
#endif
|
||
|
if (vowserv.suspend_lock == 0) {
|
||
|
vowserv.suspend_lock = 1;
|
||
|
/* Let AP will not suspend */
|
||
|
__pm_stay_awake(vow_suspend_lock);
|
||
|
VOWDRV_DEBUG("==VOW DEBUG MODE START==\n");
|
||
|
}
|
||
|
break;
|
||
|
case VOWControlCmd_DisableDebug:
|
||
|
VOWDRV_DEBUG("VOW_SET_CONTROL DisableDebug");
|
||
|
VowDrv_SetFlag(VOW_FLAG_DEBUG, false);
|
||
|
vowserv.recording_flag = false;
|
||
|
/* force stop vow_service_ReadVoiceData() 20180906 */
|
||
|
vow_service_getVoiceData();
|
||
|
if (vowserv.suspend_lock == 1) {
|
||
|
vowserv.suspend_lock = 0;
|
||
|
/* Let AP will suspend */
|
||
|
__pm_relax(vow_suspend_lock);
|
||
|
/* lock 1sec for screen on */
|
||
|
VOWDRV_DEBUG("==VOW DEBUG MODE STOP==\n");
|
||
|
__pm_wakeup_event(vow_suspend_lock,
|
||
|
jiffies_to_msecs(HZ));
|
||
|
}
|
||
|
#ifdef CONFIG_MTK_TINYSYS_SCP_SUPPORT
|
||
|
vow_deregister_feature(VOW_DUMP_FEATURE_ID);
|
||
|
#else
|
||
|
VOWDRV_DEBUG("%s(), vow: SCP no support\n\r", __func__);
|
||
|
#endif
|
||
|
break;
|
||
|
case VOWControlCmd_EnableSeamlessRecord:
|
||
|
VOWDRV_DEBUG("VOW_SET_CONTROL EnableSeamlessRecord");
|
||
|
VowDrv_SetFlag(VOW_FLAG_SEAMLESS, true);
|
||
|
break;
|
||
|
case VOWControlCmd_EnableDump:
|
||
|
vow_pcm_dump_set(true);
|
||
|
break;
|
||
|
case VOWControlCmd_DisableDump:
|
||
|
vow_pcm_dump_set(false);
|
||
|
break;
|
||
|
case VOWControlCmd_Mic_Single:
|
||
|
vowserv.vow_mic_number = 1;
|
||
|
VOWDRV_DEBUG("VOW_SET_CONTROL Set Single Mic VOW");
|
||
|
break;
|
||
|
case VOWControlCmd_Mic_Dual:
|
||
|
vowserv.vow_mic_number = 2;
|
||
|
VOWDRV_DEBUG("VOW_SET_CONTROL Set Dual Mic VOW");
|
||
|
break;
|
||
|
default:
|
||
|
VOWDRV_DEBUG("VOW_SET_CONTROL no such command = %lu",
|
||
|
arg);
|
||
|
break;
|
||
|
}
|
||
|
break;
|
||
|
case VOW_READ_VOICE_DATA:
|
||
|
if (!vow_service_SetApAddr(arg))
|
||
|
ret = -EFAULT;
|
||
|
if ((vowserv.recording_flag == true)
|
||
|
&& (vowserv.firstRead == true)) {
|
||
|
vowserv.firstRead = false;
|
||
|
VowDrv_SetFlag(VOW_FLAG_DEBUG, true);
|
||
|
}
|
||
|
vow_service_ReadVoiceData();
|
||
|
break;
|
||
|
case VOW_SET_SPEAKER_MODEL:
|
||
|
VOWDRV_DEBUG("VOW_SET_SPEAKER_MODEL(%lu)", arg);
|
||
|
if (!vow_service_SetSpeakerModel(arg))
|
||
|
ret = -EFAULT;
|
||
|
break;
|
||
|
case VOW_CLR_SPEAKER_MODEL:
|
||
|
VOWDRV_DEBUG("VOW_CLR_SPEAKER_MODEL(%lu)", arg);
|
||
|
if (!vow_service_ReleaseSpeakerModel((int)arg))
|
||
|
ret = -EFAULT;
|
||
|
break;
|
||
|
case VOW_SET_DSP_AEC_PARAMETER:
|
||
|
VOWDRV_DEBUG("VOW_SET_DSP_AEC_PARAMETER(%lu)", arg);
|
||
|
if (!vow_service_SetCustomModel(arg))
|
||
|
ret = -EFAULT;
|
||
|
break;
|
||
|
case VOW_SET_APREG_INFO:
|
||
|
VOWDRV_DEBUG("VOW_SET_APREG_INFO(%lu)", arg);
|
||
|
if (!vow_service_SetVBufAddr(arg))
|
||
|
ret = -EFAULT;
|
||
|
break;
|
||
|
case VOW_READ_VOW_DUMP_DATA:
|
||
|
//VOWDRV_DEBUG("VOW_READ_VOW_DUMP_DATA(%lu)", arg);
|
||
|
if (!vow_service_SetApDumpAddr(arg))
|
||
|
ret = -EFAULT;
|
||
|
break;
|
||
|
case VOW_BARGEIN_ON:
|
||
|
VOWDRV_DEBUG("VOW_BARGEIN_ON, irq: %d", (unsigned int)arg);
|
||
|
if (!VowDrv_SetBargeIn(1, (unsigned int)arg))
|
||
|
ret = -EFAULT;
|
||
|
break;
|
||
|
case VOW_BARGEIN_OFF:
|
||
|
VOWDRV_DEBUG("VOW_BARGEIN_OFF, irq: %d", (unsigned int)arg);
|
||
|
if (!VowDrv_SetBargeIn(0, (unsigned int)arg))
|
||
|
ret = -EFAULT;
|
||
|
break;
|
||
|
case VOW_CHECK_STATUS:
|
||
|
if (arg != 0) {
|
||
|
VOWDRV_DEBUG("VOW_CHECK_STATUS(%lu) para err, break", arg);
|
||
|
break;
|
||
|
}
|
||
|
/* VOW disable already, then bypass second one */
|
||
|
VowDrv_ChangeStatus();
|
||
|
VOWDRV_DEBUG("VOW_CHECK_STATUS(%lu)", arg);
|
||
|
break;
|
||
|
case VOW_RECOG_ENABLE:
|
||
|
pr_debug("+VOW_RECOG_ENABLE(%lu)+", arg);
|
||
|
pr_debug("KERNEL_VOW_DRV_VER %s", KERNEL_VOW_DRV_VER);
|
||
|
if (!VowDrv_CheckMtkifType((unsigned int)arg)) {
|
||
|
pr_debug("+VOW_RECOG_ENABLE fail");
|
||
|
break;
|
||
|
}
|
||
|
VowDrv_SetMtkifType((unsigned int)arg);
|
||
|
VowDrv_EnableHW(1);
|
||
|
VowDrv_ChangeStatus();
|
||
|
vow_service_Enable();
|
||
|
pr_debug("-VOW_RECOG_ENABLE(%lu)-", arg);
|
||
|
break;
|
||
|
case VOW_RECOG_DISABLE:
|
||
|
pr_debug("+VOW_RECOG_DISABLE(%lu)+", arg);
|
||
|
if (!VowDrv_CheckMtkifType((unsigned int)arg)) {
|
||
|
pr_debug("+VOW_RECOG_DISABLE fail");
|
||
|
break;
|
||
|
}
|
||
|
VowDrv_SetMtkifType((unsigned int)arg);
|
||
|
VowDrv_EnableHW(0);
|
||
|
VowDrv_ChangeStatus();
|
||
|
vow_service_Disable();
|
||
|
pr_debug("-VOW_RECOG_DISABLE(%lu)-", arg);
|
||
|
break;
|
||
|
case VOW_MODEL_START:
|
||
|
vow_service_SetModelStatus(VOW_MODEL_STATUS_START, arg);
|
||
|
break;
|
||
|
case VOW_MODEL_STOP:
|
||
|
vow_service_SetModelStatus(VOW_MODEL_STATUS_STOP, arg);
|
||
|
break;
|
||
|
case VOW_GET_GOOGLE_ENGINE_VER: {
|
||
|
if (copy_to_user((void __user *)arg,
|
||
|
&vowserv.google_engine_version,
|
||
|
sizeof(vowserv.google_engine_version))) {
|
||
|
VOWDRV_DEBUG("%s(), copy_to_user fail in VOW_GET_GOOGLE_ENGINE_VER",
|
||
|
__func__);
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
case VOW_GET_GOOGLE_ARCH:
|
||
|
case VOW_GET_ALEXA_ENGINE_VER: {
|
||
|
struct vow_engine_info_t engine_ver_temp;
|
||
|
unsigned int length = VOW_ENGINE_INFO_LENGTH_BYTE;
|
||
|
|
||
|
if (copy_from_user((void *)&engine_ver_temp,
|
||
|
(const void __user *)arg,
|
||
|
sizeof(struct vow_engine_info_t))) {
|
||
|
VOWDRV_DEBUG("%s(), copy_from_user fail", __func__);
|
||
|
}
|
||
|
if ((unsigned int)cmd == VOW_GET_ALEXA_ENGINE_VER) {
|
||
|
pr_debug("VOW_GET_ALEXA_ENGINE_VER = %s, %lu, %lu, %d",
|
||
|
vowserv.alexa_engine_version,
|
||
|
engine_ver_temp.data_addr,
|
||
|
engine_ver_temp.return_size_addr,
|
||
|
length);
|
||
|
if (copy_to_user((void __user *)engine_ver_temp.data_addr,
|
||
|
vowserv.alexa_engine_version,
|
||
|
length)) {
|
||
|
VOWDRV_DEBUG("%s(), copy_to_user fail", __func__);
|
||
|
}
|
||
|
} else if ((unsigned int)cmd == VOW_GET_GOOGLE_ARCH) {
|
||
|
pr_debug("VOW_GET_GOOGLE_ARCH = %s, %lu, %lu, %d",
|
||
|
vowserv.google_engine_arch,
|
||
|
engine_ver_temp.data_addr,
|
||
|
engine_ver_temp.return_size_addr,
|
||
|
length);
|
||
|
if (copy_to_user((void __user *)engine_ver_temp.data_addr,
|
||
|
vowserv.google_engine_arch,
|
||
|
length)) {
|
||
|
VOWDRV_DEBUG("%s(), copy_to_user fail", __func__);
|
||
|
}
|
||
|
}
|
||
|
if (copy_to_user((void __user *)engine_ver_temp.return_size_addr,
|
||
|
&length,
|
||
|
sizeof(unsigned int))) {
|
||
|
VOWDRV_DEBUG("%s(), copy_to_user fail", __func__);
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
#ifdef CONFIG_MTK_VOW_1STSTAGE_PCMCALLBACK
|
||
|
case VOW_SET_PAYLOADDUMP_INFO: {
|
||
|
struct vow_payloaddump_info_t payloaddump_temp;
|
||
|
|
||
|
copy_from_user((void *)&payloaddump_temp,
|
||
|
(const void __user *)arg,
|
||
|
sizeof(struct vow_payloaddump_info_t));
|
||
|
vowserv.payloaddump_user_addr =
|
||
|
payloaddump_temp.return_payloaddump_addr;
|
||
|
vowserv.payloaddump_user_max_size =
|
||
|
payloaddump_temp.max_payloaddump_size;
|
||
|
vowserv.payloaddump_user_return_size_addr =
|
||
|
payloaddump_temp.return_payloaddump_size_addr;
|
||
|
pr_debug("-VOW_SET_PAYLOADDUMP_INFO(addr=%lu, sz=%lu)",
|
||
|
vowserv.payloaddump_user_addr,
|
||
|
vowserv.payloaddump_user_max_size);
|
||
|
if (vowserv.payloaddump_kernel_ptr != NULL) {
|
||
|
vfree(vowserv.payloaddump_kernel_ptr);
|
||
|
vowserv.payloaddump_kernel_ptr = NULL;
|
||
|
}
|
||
|
if (vowserv.payloaddump_user_max_size > 0) {
|
||
|
vowserv.payloaddump_kernel_ptr =
|
||
|
vmalloc(vowserv.payloaddump_user_max_size);
|
||
|
} else {
|
||
|
ret = -EFAULT;
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
#endif
|
||
|
case VOW_SET_WAKEUP_MODE:
|
||
|
VOWDRV_DEBUG("VOW_SET_WAKEUP_MODE, irq: %d", (unsigned int)arg);
|
||
|
if (!VowDrv_SetWakeupMode((unsigned int)arg))
|
||
|
ret = -EFAULT;
|
||
|
break;
|
||
|
default:
|
||
|
VOWDRV_DEBUG("vow WrongParameter(%lu)", arg);
|
||
|
break;
|
||
|
}
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
#ifdef CONFIG_COMPAT
|
||
|
static long VowDrv_compat_ioctl(struct file *fp,
|
||
|
unsigned int cmd,
|
||
|
unsigned long arg)
|
||
|
{
|
||
|
long ret = 0;
|
||
|
|
||
|
/* VOWDRV_DEBUG("++VowDrv_compat_ioctl cmd = %u, arg = %lu\n" */
|
||
|
/* , cmd, arg); */
|
||
|
if (!fp->f_op || !fp->f_op->unlocked_ioctl) {
|
||
|
(void)ret;
|
||
|
return -ENOTTY;
|
||
|
}
|
||
|
switch (cmd) {
|
||
|
case VOW_CLR_SPEAKER_MODEL:
|
||
|
case VOW_SET_CONTROL:
|
||
|
case VOW_CHECK_STATUS:
|
||
|
case VOW_RECOG_ENABLE:
|
||
|
case VOW_RECOG_DISABLE:
|
||
|
case VOW_BARGEIN_ON:
|
||
|
case VOW_BARGEIN_OFF:
|
||
|
case VOW_GET_GOOGLE_ENGINE_VER:
|
||
|
case VOW_SET_WAKEUP_MODE:
|
||
|
ret = fp->f_op->unlocked_ioctl(fp, cmd, arg);
|
||
|
break;
|
||
|
case VOW_MODEL_START:
|
||
|
case VOW_MODEL_STOP: {
|
||
|
struct vow_model_start_kernel_t __user *data32;
|
||
|
struct vow_model_start_t __user *data;
|
||
|
int err;
|
||
|
compat_size_t l;
|
||
|
|
||
|
data32 = compat_ptr(arg);
|
||
|
data = compat_alloc_user_space(sizeof(*data));
|
||
|
|
||
|
err = get_user(l, &data32->handle);
|
||
|
err |= put_user(l, &data->handle);
|
||
|
err |= get_user(l, &data32->confidence_level);
|
||
|
err |= put_user(l, &data->confidence_level);
|
||
|
err |= get_user(l, &data32->dsp_inform_addr);
|
||
|
err |= put_user(l, &data->dsp_inform_addr);
|
||
|
err |= get_user(l, &data32->dsp_inform_size_addr);
|
||
|
err |= put_user(l, &data->dsp_inform_size_addr);
|
||
|
|
||
|
ret = fp->f_op->unlocked_ioctl(fp, cmd, (unsigned long)data);
|
||
|
}
|
||
|
break;
|
||
|
case VOW_READ_VOICE_DATA:
|
||
|
case VOW_SET_SPEAKER_MODEL:
|
||
|
case VOW_READ_VOW_DUMP_DATA:
|
||
|
case VOW_SET_APREG_INFO: {
|
||
|
struct vow_model_info_kernel_t __user *data32;
|
||
|
struct vow_model_info_t __user *data;
|
||
|
int err;
|
||
|
compat_size_t l;
|
||
|
compat_uptr_t p;
|
||
|
|
||
|
data32 = compat_ptr(arg);
|
||
|
data = compat_alloc_user_space(sizeof(*data));
|
||
|
|
||
|
err = get_user(l, &data32->id);
|
||
|
err |= put_user(l, &data->id);
|
||
|
err |= get_user(l, &data32->keyword);
|
||
|
err |= put_user(l, &data->keyword);
|
||
|
err |= get_user(l, &data32->addr);
|
||
|
err |= put_user(l, &data->addr);
|
||
|
err |= get_user(l, &data32->size);
|
||
|
err |= put_user(l, &data->size);
|
||
|
err |= get_user(l, &data32->return_size_addr);
|
||
|
err |= put_user(l, &data->return_size_addr);
|
||
|
err |= get_user(l, &data32->uuid);
|
||
|
err |= put_user(l, &data->uuid);
|
||
|
err |= get_user(p, (compat_uptr_t *)&data32->data);
|
||
|
err |= put_user(p, (compat_uptr_t *)&data->data);
|
||
|
|
||
|
ret = fp->f_op->unlocked_ioctl(fp, cmd, (unsigned long)data);
|
||
|
}
|
||
|
break;
|
||
|
case VOW_SET_DSP_AEC_PARAMETER:
|
||
|
case VOW_GET_GOOGLE_ARCH:
|
||
|
case VOW_GET_ALEXA_ENGINE_VER: {
|
||
|
struct vow_engine_info_kernel_t __user *data32;
|
||
|
struct vow_engine_info_t __user *data;
|
||
|
int err;
|
||
|
compat_size_t l;
|
||
|
|
||
|
data32 = compat_ptr(arg);
|
||
|
data = compat_alloc_user_space(sizeof(*data));
|
||
|
|
||
|
err = get_user(l, &data32->return_size_addr);
|
||
|
err |= put_user(l, &data->return_size_addr);
|
||
|
err |= get_user(l, &data32->data_addr);
|
||
|
err |= put_user(l, &data->data_addr);
|
||
|
|
||
|
ret = fp->f_op->unlocked_ioctl(fp, cmd, (unsigned long)data);
|
||
|
}
|
||
|
break;
|
||
|
case VOW_SET_PAYLOADDUMP_INFO: {
|
||
|
struct vow_payloaddump_info_kernel_t __user *data32;
|
||
|
struct vow_payloaddump_info_t __user *data;
|
||
|
int err;
|
||
|
compat_size_t l;
|
||
|
|
||
|
data32 = compat_ptr(arg);
|
||
|
data = compat_alloc_user_space(sizeof(*data));
|
||
|
|
||
|
err = get_user(l, &data32->return_payloaddump_addr);
|
||
|
err |= put_user(l, &data->return_payloaddump_addr);
|
||
|
err |= get_user(l, &data32->return_payloaddump_size_addr);
|
||
|
err |= put_user(l, &data->return_payloaddump_size_addr);
|
||
|
err |= get_user(l, &data32->max_payloaddump_size);
|
||
|
err |= put_user(l, &data->max_payloaddump_size);
|
||
|
|
||
|
ret = fp->f_op->unlocked_ioctl(fp, cmd, (unsigned long)data);
|
||
|
}
|
||
|
break;
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
/* VOWDRV_DEBUG("--VowDrv_compat_ioctl\n"); */
|
||
|
return ret;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
static ssize_t VowDrv_write(struct file *fp,
|
||
|
const char __user *data,
|
||
|
size_t count,
|
||
|
loff_t *offset)
|
||
|
{
|
||
|
/* VOWDRV_DEBUG("+VowDrv_write = %p count = %d\n",fp ,count); */
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static ssize_t VowDrv_read(struct file *fp,
|
||
|
char __user *data,
|
||
|
size_t count,
|
||
|
loff_t *offset)
|
||
|
{
|
||
|
unsigned int read_count = 0;
|
||
|
unsigned int time_diff_scp_ipi = 0;
|
||
|
unsigned int time_diff_ipi_read = 0;
|
||
|
unsigned long long vow_read_cycle = 0;
|
||
|
int ret = 0;
|
||
|
unsigned int ret_data = 0;
|
||
|
int slot = 0;
|
||
|
bool dsp_inform_tx_flag = false;
|
||
|
|
||
|
VOWDRV_DEBUG("+%s()+\n", __func__);
|
||
|
if (count != sizeof(struct vow_eint_data_struct_t)) {
|
||
|
VOWDRV_DEBUG(
|
||
|
"%s(), cpy incorrect size to user, size=%d, correct size=%d, exit\n",
|
||
|
__func__,
|
||
|
count,
|
||
|
sizeof(struct vow_eint_data_struct_t));
|
||
|
goto exit;
|
||
|
}
|
||
|
VowDrv_SetVowEINTStatus(VOW_EINT_RETRY);
|
||
|
|
||
|
if (VowDrv_Wait_Queue_flag == 0)
|
||
|
ret = wait_event_interruptible(VowDrv_Wait_Queue,
|
||
|
VowDrv_Wait_Queue_flag);
|
||
|
if (VowDrv_Wait_Queue_flag == 1) {
|
||
|
VowDrv_Wait_Queue_flag = 0;
|
||
|
if (VowDrv_GetHWStatus() == VOW_PWR_OFF) {
|
||
|
VOWDRV_DEBUG("vow Enter_phase3_cnt = %d\n",
|
||
|
vowserv.enter_phase3_cnt);
|
||
|
vowserv.scp_command_flag = false;
|
||
|
VowDrv_SetVowEINTStatus(VOW_EINT_DISABLE);
|
||
|
} else {
|
||
|
if (vowserv.scp_command_flag) {
|
||
|
VowDrv_SetVowEINTStatus(VOW_EINT_PASS);
|
||
|
vow_read_cycle = get_cycles();
|
||
|
time_diff_scp_ipi =
|
||
|
(unsigned int)CYCLE_TO_NS *
|
||
|
(unsigned int)(vowserv.ap_received_ipi_cycle
|
||
|
- vowserv.scp_recognize_ok_cycle);
|
||
|
time_diff_ipi_read =
|
||
|
(unsigned int)CYCLE_TO_NS *
|
||
|
(unsigned int)(vow_read_cycle
|
||
|
- vowserv.ap_received_ipi_cycle);
|
||
|
VOWDRV_DEBUG("vow Wakeup by SCP\n");
|
||
|
VOWDRV_DEBUG("SCP->IPI:%d(ns),IPI->AP:%d(ns)\n",
|
||
|
time_diff_scp_ipi,
|
||
|
time_diff_ipi_read);
|
||
|
if (vowserv.suspend_lock == 0) {
|
||
|
/* lock 1sec for screen on */
|
||
|
__pm_wakeup_event(vow_suspend_lock,
|
||
|
jiffies_to_msecs(HZ));
|
||
|
}
|
||
|
vowserv.scp_command_flag = false;
|
||
|
if (vowserv.extradata_bytelen > 0)
|
||
|
dsp_inform_tx_flag = true;
|
||
|
} else {
|
||
|
VOWDRV_DEBUG("vow Wakeup by other(%d,%d)\n",
|
||
|
VowDrv_Wait_Queue_flag,
|
||
|
VowDrv_GetHWStatus());
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
slot = vow_service_SearchSpeakerModelWithKeyword(
|
||
|
vowserv.scp_command_keywordid);
|
||
|
if (slot < 0) {
|
||
|
/* there is no pair id */
|
||
|
VOWDRV_DEBUG("%s(), search ID fail, not keyword event, eint=%d, exit\n",
|
||
|
__func__,
|
||
|
VowDrv_QueryVowEINTStatus());
|
||
|
vowserv.scp_command_id = 0;
|
||
|
vowserv.confidence_level = 0;
|
||
|
goto exit;
|
||
|
} else {
|
||
|
vowserv.scp_command_id = vowserv.vow_speaker_model[slot].id;
|
||
|
}
|
||
|
|
||
|
memset((void *)&vowserv.vow_eint_data_struct, 0,
|
||
|
sizeof(vowserv.vow_eint_data_struct));
|
||
|
vowserv.vow_eint_data_struct.id = vowserv.scp_command_id;
|
||
|
vowserv.vow_eint_data_struct.eint_status = VowDrv_QueryVowEINTStatus();
|
||
|
vowserv.vow_eint_data_struct.data[0] = (char)vowserv.confidence_level;
|
||
|
vowserv.confidence_level = 0;
|
||
|
read_count = copy_to_user((void __user *)data,
|
||
|
&vowserv.vow_eint_data_struct,
|
||
|
sizeof(struct vow_eint_data_struct_t));
|
||
|
if (dsp_inform_tx_flag) {
|
||
|
/* int i; */
|
||
|
|
||
|
dsp_inform_tx_flag = false;
|
||
|
|
||
|
mutex_lock(&vow_extradata_mutex);
|
||
|
if (vowserv.extradata_mem_ptr == NULL) {
|
||
|
mutex_unlock(&vow_extradata_mutex);
|
||
|
goto exit;
|
||
|
}
|
||
|
if (vowserv.extradata_ptr == NULL) {
|
||
|
mutex_unlock(&vow_extradata_mutex);
|
||
|
goto exit;
|
||
|
}
|
||
|
mutex_unlock(&vow_extradata_mutex);
|
||
|
if (vowserv.vow_speaker_model[slot].rx_inform_size_addr == 0)
|
||
|
goto exit;
|
||
|
if (vowserv.vow_speaker_model[slot].rx_inform_addr == 0)
|
||
|
goto exit;
|
||
|
VOWDRV_DEBUG("%s(), copy to user, extra data len=%d\n",
|
||
|
__func__, vowserv.extradata_bytelen);
|
||
|
#if 0
|
||
|
for (i = 0; i < vowserv.extradata_bytelen; i++) {
|
||
|
VOWDRV_DEBUG("data[%d] = %x\n",
|
||
|
i, *(vowserv.extradata_ptr + i));
|
||
|
}
|
||
|
#endif
|
||
|
#ifdef CONFIG_MTK_VOW_1STSTAGE_PCMCALLBACK
|
||
|
vow_service_ReadPayloadDumpData(vowserv.payloaddump_length);
|
||
|
#endif
|
||
|
/* copy extra data from DRAM */
|
||
|
mutex_lock(&vow_extradata_mutex);
|
||
|
memcpy(vowserv.extradata_mem_ptr,
|
||
|
vowserv.extradata_ptr,
|
||
|
vowserv.extradata_bytelen);
|
||
|
mutex_unlock(&vow_extradata_mutex);
|
||
|
/* copy extra data into user space */
|
||
|
ret_data = copy_to_user(
|
||
|
(void __user *)
|
||
|
vowserv.vow_speaker_model[slot].rx_inform_size_addr,
|
||
|
&vowserv.extradata_bytelen,
|
||
|
sizeof(unsigned int));
|
||
|
if (ret_data != 0) {
|
||
|
/* fail, print the fail size */
|
||
|
VOWDRV_DEBUG("[vow dsp inform1]CopytoUser fail sz:%d\n",
|
||
|
ret_data);
|
||
|
}
|
||
|
mutex_lock(&vow_extradata_mutex);
|
||
|
ret_data = copy_to_user(
|
||
|
(void __user *)
|
||
|
vowserv.vow_speaker_model[slot].rx_inform_addr,
|
||
|
vowserv.extradata_mem_ptr,
|
||
|
vowserv.extradata_bytelen);
|
||
|
mutex_unlock(&vow_extradata_mutex);
|
||
|
if (ret_data != 0) {
|
||
|
/* fail, print the fail size */
|
||
|
VOWDRV_DEBUG("[vow dsp inform2]CopytoUser fail sz:%d\n",
|
||
|
ret_data);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
exit:
|
||
|
VOWDRV_DEBUG("+%s()-, recog id: %d, confidence_lv=%d\n",
|
||
|
__func__,
|
||
|
vowserv.scp_command_id,
|
||
|
vowserv.confidence_level);
|
||
|
return read_count;
|
||
|
}
|
||
|
|
||
|
static int VowDrv_flush(struct file *flip, fl_owner_t id)
|
||
|
{
|
||
|
bool ret = false;
|
||
|
|
||
|
VOWDRV_DEBUG("%s(), Send VOW_FLUSH ipi\n", __func__);
|
||
|
|
||
|
ret = vow_ipi_send(IPIMSG_VOW_FLUSH, 0, NULL, VOW_IPI_BYPASS_ACK);
|
||
|
if (ret == 0)
|
||
|
VOWDRV_DEBUG("IPIMSG_VOW_FLUSH ipi send error\n\r");
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int VowDrv_fasync(int fd, struct file *flip, int mode)
|
||
|
{
|
||
|
VOWDRV_DEBUG("%s()\n", __func__);
|
||
|
/*return fasync_helper(fd, flip, mode, &VowDrv_fasync);*/
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int VowDrv_remap_mmap(struct file *flip, struct vm_area_struct *vma)
|
||
|
{
|
||
|
VOWDRV_DEBUG("%s()\n", __func__);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
int VowDrv_setup_smartdev_eint(struct platform_device *pdev)
|
||
|
{
|
||
|
int ret;
|
||
|
unsigned int ints[2] = {0, 0};
|
||
|
|
||
|
/* gpio setting */
|
||
|
vowserv.pinctrl = devm_pinctrl_get(&pdev->dev);
|
||
|
if (IS_ERR(vowserv.pinctrl)) {
|
||
|
ret = PTR_ERR(vowserv.pinctrl);
|
||
|
VOWDRV_DEBUG("Cannot find Vow pinctrl!\n");
|
||
|
return ret;
|
||
|
}
|
||
|
vowserv.pins_eint_on = pinctrl_lookup_state(vowserv.pinctrl,
|
||
|
"vow_smartdev_eint_on");
|
||
|
if (IS_ERR(vowserv.pins_eint_on)) {
|
||
|
ret = PTR_ERR(vowserv.pins_eint_on);
|
||
|
VOWDRV_DEBUG("Cannot find vow pinctrl eint_on!\n");
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
vowserv.pins_eint_off = pinctrl_lookup_state(vowserv.pinctrl,
|
||
|
"vow_smartdev_eint_off");
|
||
|
if (IS_ERR(vowserv.pins_eint_off)) {
|
||
|
ret = PTR_ERR(vowserv.pins_eint_off);
|
||
|
VOWDRV_DEBUG("Cannot find vow pinctrl eint_off!\n");
|
||
|
return ret;
|
||
|
}
|
||
|
/* eint setting */
|
||
|
vowserv.node = pdev->dev.of_node;
|
||
|
if (vowserv.node) {
|
||
|
ret = of_property_read_u32_array(vowserv.node,
|
||
|
"debounce",
|
||
|
ints,
|
||
|
ARRAY_SIZE(ints));
|
||
|
if (ret != 0) {
|
||
|
VOWDRV_DEBUG("%s(), no debounce node, ret=%d\n",
|
||
|
__func__, ret);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
VOWDRV_DEBUG("VOW EINT ID: %x, %x\n", ints[0], ints[1]);
|
||
|
} else {
|
||
|
/* no node here */
|
||
|
VOWDRV_DEBUG("%s(), there is no this node\n", __func__);
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
bool vow_service_GetScpRecoverStatus(void)
|
||
|
{
|
||
|
return vowserv.scp_recovering;
|
||
|
}
|
||
|
|
||
|
bool vow_service_GetVowRecoverStatus(void)
|
||
|
{
|
||
|
return vowserv.vow_recovering;
|
||
|
}
|
||
|
|
||
|
/*****************************************************************************
|
||
|
* SCP Recovery Register
|
||
|
*****************************************************************************/
|
||
|
#ifdef CONFIG_MTK_TINYSYS_SCP_SUPPORT
|
||
|
static int vow_scp_recover_event(struct notifier_block *this,
|
||
|
unsigned long event,
|
||
|
void *ptr)
|
||
|
{
|
||
|
switch (event) {
|
||
|
case SCP_EVENT_READY: {
|
||
|
int I;
|
||
|
bool ret = false;
|
||
|
unsigned int vow_ipi_buf[2];
|
||
|
|
||
|
vowserv.vow_recovering = true;
|
||
|
vowserv.scp_recovering = false;
|
||
|
VOWDRV_DEBUG("%s(), SCP_EVENT_READY\n", __func__);
|
||
|
if (!vow_check_scp_status()) {
|
||
|
VOWDRV_DEBUG("SCP is Off, don't recover VOW\n");
|
||
|
return NOTIFY_DONE;
|
||
|
}
|
||
|
if (vowserv.scp_recovering) {
|
||
|
vowserv.vow_recovering = false;
|
||
|
VOWDRV_DEBUG("fail: vow recover1\n");
|
||
|
break;
|
||
|
}
|
||
|
vow_service_Init();
|
||
|
if (vowserv.scp_recovering) {
|
||
|
vowserv.vow_recovering = false;
|
||
|
VOWDRV_DEBUG("fail: vow recover2\n");
|
||
|
break;
|
||
|
}
|
||
|
for (I = 0; I < MAX_VOW_SPEAKER_MODEL; I++) {
|
||
|
if (!vow_service_SendSpeakerModel(I, VOW_SET_MODEL))
|
||
|
VOWDRV_DEBUG("fail: SendSpeakerModel\n");
|
||
|
}
|
||
|
// send AEC custom model
|
||
|
vow_ipi_buf[0] = vowserv.custom_model_addr;
|
||
|
vow_ipi_buf[1] = vowserv.custom_model_size;
|
||
|
ret = vow_ipi_send(IPIMSG_VOW_SET_CUSTOM_MODEL,
|
||
|
2, &vow_ipi_buf[0], VOW_IPI_BYPASS_ACK);
|
||
|
if (!ret)
|
||
|
VOWDRV_DEBUG("fail: vow_service_SetCustomModel\n");
|
||
|
|
||
|
/* if vow is not enable, then return */
|
||
|
if (VowDrv_GetHWStatus() != VOW_PWR_ON) {
|
||
|
vowserv.vow_recovering = false;
|
||
|
VOWDRV_DEBUG("fail: vow not enable\n");
|
||
|
break;
|
||
|
}
|
||
|
if (vowserv.scp_recovering) {
|
||
|
vowserv.vow_recovering = false;
|
||
|
VOWDRV_DEBUG("fail: vow recover3\n");
|
||
|
break;
|
||
|
}
|
||
|
/* pcm dump recover */
|
||
|
VOWDRV_DEBUG("recording_flag = %d, dump_pcm_flag = %d\n",
|
||
|
vowserv.recording_flag, vowserv.dump_pcm_flag);
|
||
|
if (vowserv.recording_flag == true)
|
||
|
VowDrv_SetFlag(VOW_FLAG_DEBUG, true);
|
||
|
|
||
|
if (vowserv.dump_pcm_flag == true)
|
||
|
vow_pcm_dump_notify(true);
|
||
|
|
||
|
if (vowserv.scp_recovering) {
|
||
|
vowserv.vow_recovering = false;
|
||
|
VOWDRV_DEBUG("fail: vow recover4\n");
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (!VowDrv_SetMtkifType(vowserv.mtkif_type))
|
||
|
VOWDRV_DEBUG("fail: vow_SetMtkifType\n");
|
||
|
|
||
|
if (!vow_service_Enable())
|
||
|
VOWDRV_DEBUG("fail: vow_service_Enable\n");
|
||
|
|
||
|
for (I = 0; I < MAX_VOW_SPEAKER_MODEL; I++) {
|
||
|
if (vowserv.vow_speaker_model[I].enabled) {
|
||
|
vow_service_SendModelStatus(
|
||
|
I, VOW_MODEL_STATUS_START);
|
||
|
VOWDRV_DEBUG("send Model start, slot%d\n", I);
|
||
|
}
|
||
|
}
|
||
|
vowserv.vow_recovering = false;
|
||
|
break;
|
||
|
}
|
||
|
case SCP_EVENT_STOP:
|
||
|
vowserv.scp_recovering = true;
|
||
|
VOWDRV_DEBUG("%s(), SCP_EVENT_STOP\n", __func__);
|
||
|
/* Check if VOW is running phase2.5, then stop this */
|
||
|
break;
|
||
|
}
|
||
|
return NOTIFY_DONE;
|
||
|
}
|
||
|
|
||
|
static struct notifier_block vow_scp_recover_notifier = {
|
||
|
.notifier_call = vow_scp_recover_event,
|
||
|
};
|
||
|
#endif /* #ifdef CONFIG_MTK_TINYSYS_SCP_SUPPORT */
|
||
|
|
||
|
/*****************************************************************************
|
||
|
* VOW platform driver Registration
|
||
|
*****************************************************************************/
|
||
|
static int VowDrv_probe(struct platform_device *dev)
|
||
|
{
|
||
|
VOWDRV_DEBUG("%s()\n", __func__);
|
||
|
VowDrv_setup_smartdev_eint(dev);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int VowDrv_remove(struct platform_device *dev)
|
||
|
{
|
||
|
VOWDRV_DEBUG("%s()\n", __func__);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static void VowDrv_shutdown(struct platform_device *dev)
|
||
|
{
|
||
|
VOWDRV_DEBUG("%s()\n", __func__);
|
||
|
}
|
||
|
|
||
|
static int VowDrv_suspend(struct platform_device *dev, pm_message_t state)
|
||
|
{
|
||
|
/* only one suspend mode */
|
||
|
VOWDRV_DEBUG("%s()\n", __func__);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int VowDrv_resume(struct platform_device *dev) /* wake up */
|
||
|
{
|
||
|
VOWDRV_DEBUG("%s()\n", __func__);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static const struct file_operations VOW_fops = {
|
||
|
.owner = THIS_MODULE,
|
||
|
.open = VowDrv_open,
|
||
|
.release = VowDrv_release,
|
||
|
.unlocked_ioctl = VowDrv_ioctl,
|
||
|
#ifdef CONFIG_COMPAT
|
||
|
.compat_ioctl = VowDrv_compat_ioctl,
|
||
|
#endif
|
||
|
.write = VowDrv_write,
|
||
|
.read = VowDrv_read,
|
||
|
.flush = VowDrv_flush,
|
||
|
.fasync = VowDrv_fasync,
|
||
|
.mmap = VowDrv_remap_mmap
|
||
|
};
|
||
|
|
||
|
static struct miscdevice VowDrv_misc_device = {
|
||
|
.minor = MISC_DYNAMIC_MINOR,
|
||
|
.name = VOW_DEVNAME,
|
||
|
.fops = &VOW_fops,
|
||
|
};
|
||
|
|
||
|
const struct dev_pm_ops VowDrv_pm_ops = {
|
||
|
.suspend = NULL,
|
||
|
.resume = NULL,
|
||
|
.freeze = NULL,
|
||
|
.thaw = NULL,
|
||
|
.poweroff = NULL,
|
||
|
.restore = NULL,
|
||
|
.restore_noirq = NULL,
|
||
|
};
|
||
|
|
||
|
#ifdef CONFIG_OF
|
||
|
static const struct of_device_id vow_of_match[] = {
|
||
|
{.compatible = "mediatek,vow"},
|
||
|
{},
|
||
|
};
|
||
|
#endif
|
||
|
|
||
|
static struct platform_driver VowDrv_driver = {
|
||
|
.probe = VowDrv_probe,
|
||
|
.remove = VowDrv_remove,
|
||
|
.shutdown = VowDrv_shutdown,
|
||
|
.suspend = VowDrv_suspend,
|
||
|
.resume = VowDrv_resume,
|
||
|
.driver = {
|
||
|
#ifdef CONFIG_PM
|
||
|
.pm = &VowDrv_pm_ops,
|
||
|
#endif
|
||
|
.name = "VOW_driver_device",
|
||
|
#ifdef CONFIG_OF
|
||
|
.of_match_table = vow_of_match,
|
||
|
#endif
|
||
|
},
|
||
|
};
|
||
|
|
||
|
static int __init VowDrv_mod_init(void)
|
||
|
{
|
||
|
int ret = 0;
|
||
|
|
||
|
VOWDRV_DEBUG("+%s()\n", __func__);
|
||
|
|
||
|
/* Register platform DRIVER */
|
||
|
ret = platform_driver_register(&VowDrv_driver);
|
||
|
if (ret != 0) {
|
||
|
VOWDRV_DEBUG("VowDrv Fail:%d - Register DRIVER\n", ret);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
/* register MISC device */
|
||
|
ret = misc_register(&VowDrv_misc_device);
|
||
|
if (ret != 0) {
|
||
|
VOWDRV_DEBUG("VowDrv_probe misc_register Fail:%d\n", ret);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
ret = device_create_file(VowDrv_misc_device.this_device,
|
||
|
&dev_attr_vow_SetPhase1);
|
||
|
if (unlikely(ret != 0))
|
||
|
return ret;
|
||
|
ret = device_create_file(VowDrv_misc_device.this_device,
|
||
|
&dev_attr_vow_SetPhase2);
|
||
|
if (unlikely(ret != 0))
|
||
|
return ret;
|
||
|
ret = device_create_file(VowDrv_misc_device.this_device,
|
||
|
&dev_attr_vow_SetBypassPhase3);
|
||
|
if (unlikely(ret != 0))
|
||
|
return ret;
|
||
|
ret = device_create_file(VowDrv_misc_device.this_device,
|
||
|
&dev_attr_vow_GetEnterPhase3Counter);
|
||
|
if (unlikely(ret != 0))
|
||
|
return ret;
|
||
|
ret = device_create_file(VowDrv_misc_device.this_device,
|
||
|
&dev_attr_vow_SetLibLog);
|
||
|
if (unlikely(ret != 0))
|
||
|
return ret;
|
||
|
ret = device_create_file(VowDrv_misc_device.this_device,
|
||
|
&dev_attr_vow_SetEnableHW);
|
||
|
if (unlikely(ret != 0))
|
||
|
return ret;
|
||
|
ret = device_create_file(VowDrv_misc_device.this_device,
|
||
|
&dev_attr_vow_SetBargeIn);
|
||
|
if (unlikely(ret != 0))
|
||
|
return ret;
|
||
|
ret = device_create_file(VowDrv_misc_device.this_device,
|
||
|
&dev_attr_vow_DualMicSwitch);
|
||
|
if (unlikely(ret != 0))
|
||
|
return ret;
|
||
|
|
||
|
ret = device_create_file(VowDrv_misc_device.this_device,
|
||
|
&dev_attr_vow_SplitDumpFile);
|
||
|
if (unlikely(ret != 0))
|
||
|
return ret;
|
||
|
|
||
|
ret = device_create_file(VowDrv_misc_device.this_device,
|
||
|
&dev_attr_vow_SetMCPS);
|
||
|
if (unlikely(ret != 0))
|
||
|
return ret;
|
||
|
/* ipi register */
|
||
|
vow_ipi_register(vow_ipi_rx_internal, vow_ipi_rceive_ack);
|
||
|
|
||
|
vow_service_Init();
|
||
|
#ifdef CONFIG_MTK_TINYSYS_SCP_SUPPORT
|
||
|
scp_A_register_notify(&vow_scp_recover_notifier);
|
||
|
#endif
|
||
|
vow_suspend_lock = wakeup_source_register(NULL, "vow wakelock");
|
||
|
vow_ipi_suspend_lock = wakeup_source_register(NULL, "vow ipi wakelock");
|
||
|
|
||
|
if (!vow_suspend_lock)
|
||
|
pr_debug("vow wakeup source init failed.\n");
|
||
|
|
||
|
if (!vow_ipi_suspend_lock)
|
||
|
pr_debug("vow ipi wakeup source init failed.\n");
|
||
|
VOWDRV_DEBUG("-%s(): Init Audio WakeLock\n", __func__);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static void __exit VowDrv_mod_exit(void)
|
||
|
{
|
||
|
VOWDRV_DEBUG("+%s()\n", __func__);
|
||
|
wakeup_source_unregister(vow_suspend_lock);
|
||
|
wakeup_source_unregister(vow_ipi_suspend_lock);
|
||
|
vow_pcm_dump_deinit();
|
||
|
/* extra data memory release */
|
||
|
mutex_lock(&vow_extradata_mutex);
|
||
|
if (vowserv.extradata_mem_ptr != NULL) {
|
||
|
vfree(vowserv.extradata_mem_ptr);
|
||
|
vowserv.extradata_mem_ptr = NULL;
|
||
|
}
|
||
|
mutex_unlock(&vow_extradata_mutex);
|
||
|
VOWDRV_DEBUG("-%s()\n", __func__);
|
||
|
}
|
||
|
late_initcall(VowDrv_mod_init);
|
||
|
module_exit(VowDrv_mod_exit);
|
||
|
|
||
|
|
||
|
/*****************************************************************************
|
||
|
* License
|
||
|
*****************************************************************************/
|
||
|
|
||
|
MODULE_LICENSE("GPL");
|
||
|
MODULE_DESCRIPTION("MediaTek VOW Driver");
|
||
|
MODULE_AUTHOR("Charlie Lu<charlie.lu@mediatek.com>");
|