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