947a32184b
Signed-off-by: fangpeina <fangpeina@xiaomi.com>
2430 lines
69 KiB
C
2430 lines
69 KiB
C
/****************************************************************************
|
|
* drivers/input/aw86225.c
|
|
*
|
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
* contributor license agreements. See the NOTICE file distributed with
|
|
* this work for additional information regarding copyright ownership. The
|
|
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
|
* "License"); you may not use this file except in compliance with the
|
|
* License. You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
* License for the specific language governing permissions and limitations
|
|
* under the License.
|
|
*
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Included Files
|
|
****************************************************************************/
|
|
|
|
#include <nuttx/config.h>
|
|
|
|
#include <signal.h>
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
#include <errno.h>
|
|
#include <debug.h>
|
|
#include <stdio.h>
|
|
#include <time.h>
|
|
|
|
#include <nuttx/arch.h>
|
|
#include <nuttx/bits.h>
|
|
#include <nuttx/fs/fs.h>
|
|
#include <nuttx/kmalloc.h>
|
|
#include <nuttx/mm/mm.h>
|
|
#include <nuttx/nuttx.h>
|
|
#include <nuttx/timers/timer.h>
|
|
#include <nuttx/timers/watchdog.h>
|
|
#include <nuttx/ioexpander/ioexpander.h>
|
|
#include <nuttx/ioexpander/gpio.h>
|
|
#include <nuttx/input/aw86225.h>
|
|
#include <nuttx/input/ff.h>
|
|
#include <nuttx/irq.h>
|
|
|
|
#include "aw86225_reg.h"
|
|
#include "aw86225_internal.h"
|
|
|
|
/****************************************************************************
|
|
* Private Function Prototypes
|
|
****************************************************************************/
|
|
|
|
static void aw86225_long_vibrate_work_routine(FAR void *arg);
|
|
static void aw86225_ram_work_routine(FAR void *arg);
|
|
|
|
/****************************************************************************
|
|
* Private Data
|
|
****************************************************************************/
|
|
|
|
static FAR const char *g_aw86225_ram_name = "aw86225_haptic.bin";
|
|
static FAR const char g_aw86225_rtp_name[][AW86225_RTP_NAME_MAX] =
|
|
{
|
|
{"door_open_RTP.bin"},
|
|
{"Guitar_RTP.bin"},
|
|
{"aw86225_osc_rtp_24K_5s.bin"},
|
|
{"AcousticGuitar_RTP.bin"},
|
|
{"Carousel_RTP.bin"},
|
|
{"Celesta_RTP.bin"},
|
|
{"Childhood_RTP.bin"},
|
|
{"Country_RTP.bin"},
|
|
{"Cowboy_RTP.bin"},
|
|
{"Echo_RTP.bin"},
|
|
{"Fairyland_RTP.bin"},
|
|
{"Fantasy_RTP.bin"},
|
|
{"Field_Trip_RTP.bin"},
|
|
{"Glee_RTP.bin"},
|
|
{"Glockenspiel_RTP.bin"},
|
|
{"Ice_Latte_RTP.bin"},
|
|
{"Kung_Fu_RTP.bin"},
|
|
{"Leisure_RTP.bin"},
|
|
{"Lollipop_RTP.bin"},
|
|
{"MiMix2_RTP.bin"},
|
|
{"Mi_RTP.bin"},
|
|
{"MiHouse_RTP.bin"},
|
|
{"MiJazz_RTP.bin"},
|
|
{"MiRemix_RTP.bin"},
|
|
{"Mountain_Spring_RTP.bin"},
|
|
{"Orange_RTP.bin"},
|
|
{"WindChime_RTP.bin"},
|
|
{"Space_Age_RTP.bin"},
|
|
{"ToyRobot_RTP.bin"},
|
|
{"Vigor_RTP.bin"},
|
|
{"Bottle_RTP.bin"},
|
|
{"Bubble_RTP.bin"},
|
|
{"Bullfrog_RTP.bin"},
|
|
{"Burst_RTP.bin"},
|
|
{"Chirp_RTP.bin"},
|
|
{"Clank_RTP.bin"},
|
|
{"Crystal_RTP.bin"},
|
|
{"FadeIn_RTP.bin"},
|
|
{"FadeOut_RTP.bin"},
|
|
{"Flute_RTP.bin"},
|
|
{"Fresh_RTP.bin"},
|
|
{"Frog_RTP.bin"},
|
|
{"Guitar_RTP.bin"},
|
|
{"Harp_RTP.bin"},
|
|
{"IncomingMessage_RTP.bin"},
|
|
{"MessageSent_RTP.bin"},
|
|
{"Moment_RTP.bin"},
|
|
{"NotificationXylophone_RTP.bin"},
|
|
{"Potion_RTP.bin"},
|
|
{"Radar_RTP.bin"},
|
|
{"Spring_RTP.bin"},
|
|
{"Swoosh_RTP.bin"},
|
|
{"Gesture_UpSlide_RTP.bin"},
|
|
{"Gesture_UpHold_RTP.bin"},
|
|
{"Charge_Wire_RTP.bin"},
|
|
{"Charge_Wireless_RTP.bin"},
|
|
{"Unlock_Failed_RTP.bin"},
|
|
{"FOD_Motion1_RTP.bin"},
|
|
{"FOD_Motion2_RTP.bin"},
|
|
{"FOD_Motion3_RTP.bin"},
|
|
{"FOD_Motion4_RTP.bin"},
|
|
{"FaceID_Wrong1_RTP.bin"},
|
|
{"FaceID_Wrong2_RTP.bin"},
|
|
{"uninstall_animation_rtp.bin"},
|
|
{"uninstall_dialog_rtp.bin"},
|
|
{"screenshot_rtp.bin"},
|
|
{"lockscreen_camera_entry_rtp.bin"},
|
|
{"launcher_edit_rtp.bin"},
|
|
{"launcher_icon_selection_rtp.bin"},
|
|
{"taskcard_remove_rtp.bin"},
|
|
{"task_cleanall_rtp.bin"},
|
|
{"new_iconfolder_rtp.bin"},
|
|
{"notification_remove_rtp.bin"},
|
|
{"notification_cleanall_rtp.bin"},
|
|
{"notification_setting_rtp.bin"},
|
|
{"game_turbo_rtp.bin"},
|
|
{"NFC_card_rtp.bin"},
|
|
{"wakeup_voice_assistant_rtp.bin"},
|
|
{"NFC_card_slow_rtp.bin"},
|
|
{"POCO_RTP.bin"},
|
|
{"aw86225_rtp.bin"},
|
|
{"offline_countdown_RTP.bin"},
|
|
{"scene_bomb_injury_RTP.bin"},
|
|
{"scene_bomb_RTP.bin"},
|
|
{"door_open_RTP.bin"},
|
|
{"aw86225_rtp.bin"},
|
|
{"scene_step_RTP.bin"},
|
|
{"crawl_RTP.bin"},
|
|
{"scope_on_RTP.bin"},
|
|
{"scope_off_RTP.bin"},
|
|
{"magazine_quick_RTP.bin"},
|
|
{"grenade_RTP.bin"},
|
|
{"scene_getshot_RTP.bin"},
|
|
{"grenade_explosion_RTP.bin"},
|
|
{"punch_RTP.bin"},
|
|
{"pan_RTP.bin"},
|
|
{"bandage_RTP.bin"},
|
|
{"aw86225_rtp.bin"},
|
|
{"scene_jump_RTP.bin"},
|
|
{"vehicle_plane_RTP.bin"},
|
|
{"scene_openparachute_RTP.bin"},
|
|
{"scene_closeparachute_RTP.bin"},
|
|
{"vehicle_collision_RTP.bin"},
|
|
{"vehicle_buggy_RTP.bin"},
|
|
{"vehicle_dacia_RTP.bin"},
|
|
{"vehicle_moto_RTP.bin"},
|
|
{"firearms_akm_RTP.bin"},
|
|
{"firearms_m16a4_RTP.bin"},
|
|
{"aw86225_rtp.bin"},
|
|
{"firearms_awm_RTP.bin"},
|
|
{"firearms_mini14_RTP.bin"},
|
|
{"firearms_vss_RTP.bin"},
|
|
{"firearms_qbz_RTP.bin"},
|
|
{"firearms_ump9_RTP.bin"},
|
|
{"firearms_dp28_RTP.bin"},
|
|
{"firearms_s1897_RTP.bin"},
|
|
{"aw86225_rtp.bin"},
|
|
{"firearms_p18c_RTP.bin"},
|
|
{"aw86225_rtp.bin"},
|
|
{"aw86225_rtp.bin"},
|
|
{"CFM_KillOne_RTP.bin"},
|
|
{"CFM_Headshot_RTP.bin"},
|
|
{"CFM_MultiKill_RTP.bin"},
|
|
{"CFM_KillOne_Strong_RTP.bin"},
|
|
{"CFM_Headshot_Strong_RTP.bin"},
|
|
{"CFM_MultiKill_Strong_RTP.bin"},
|
|
{"CFM_Weapon_Grenade_Explode_RTP.bin"},
|
|
{"CFM_Weapon_Grenade_KillOne_RTP.bin"},
|
|
{"CFM_ImpactFlesh_Normal_RTP.bin"},
|
|
{"CFM_Weapon_C4_Installed_RTP.bin"},
|
|
{"CFM_Hero_Appear_RTP.bin"},
|
|
{"CFM_UI_Reward_OpenBox_RTP.bin"},
|
|
{"CFM_UI_Reward_Task_RTP.bin"},
|
|
{"CFM_Weapon_BLT_Shoot_RTP.bin"},
|
|
{"Atlantis_RTP.bin"},
|
|
{"DigitalUniverse_RTP.bin"},
|
|
{"Reveries_RTP.bin"},
|
|
{"FOD_Motion_Triang_RTP.bin"},
|
|
{"FOD_Motion_Flare_RTP.bin"},
|
|
{"FOD_Motion_Ripple_RTP.bin"},
|
|
{"FOD_Motion_Spiral_RTP.bin"},
|
|
{"gamebox_launch_rtp.bin"},
|
|
{"Gesture_Back_Pull_RTP.bin"},
|
|
{"Gesture_Back_Release_RTP.bin"},
|
|
{"alert_rtp.bin"},
|
|
{"feedback_negative_light_rtp.bin"},
|
|
{"feedback_neutral_rtp.bin"},
|
|
{"feedback_positive_rtp.bin"},
|
|
{"fingerprint_record_rtp.bin"},
|
|
{"lockdown_rtp.bin"},
|
|
{"sliding_damping_rtp.bin"},
|
|
{"todo_alldone_rtp.bin"},
|
|
{"uninstall_animation_icon_rtp.bin"},
|
|
{"signal_button_highlight_rtp.bin"},
|
|
{"signal_button_negative_rtp.bin"},
|
|
{"signal_button_rtp.bin"},
|
|
{"signal_clock_high_rtp.bin"},
|
|
{"signal_clock_rtp.bin"},
|
|
{"signal_clock_unit_rtp.bin"},
|
|
{"signal_inputbox_rtp.bin"},
|
|
{"signal_key_high_rtp.bin"},
|
|
{"signal_key_unit_rtp.bin"},
|
|
{"signal_list_highlight_rtp.bin"},
|
|
{"signal_list_rtp.bin"},
|
|
{"signal_picker_rtp.bin"},
|
|
{"signal_popup_rtp.bin"},
|
|
{"signal_seekbar_rtp.bin"},
|
|
{"signal_switch_rtp.bin"},
|
|
{"signal_tab_rtp.bin"},
|
|
{"signal_text_rtp.bin"},
|
|
{"signal_transition_light_rtp.bin"},
|
|
{"signal_transition_rtp.bin"},
|
|
{"haptics_video_rtp.bin"},
|
|
};
|
|
|
|
/****************************************************************************
|
|
* Private Functions
|
|
****************************************************************************/
|
|
|
|
static int aw86225_i2c_write_cnt(FAR struct aw86225 *aw86225,
|
|
uint8_t reg_addr,
|
|
FAR const uint8_t *reg_data, uint32_t cnt)
|
|
{
|
|
struct i2c_msg_s msg[2];
|
|
int ret = -1;
|
|
int retries = 0;
|
|
|
|
msg[0].frequency = aw86225->freq;
|
|
msg[0].addr = aw86225->addr;
|
|
msg[0].flags = I2C_M_NOSTOP;
|
|
msg[0].buffer = ®_addr;
|
|
msg[0].length = 1;
|
|
|
|
msg[1].frequency = aw86225->freq;
|
|
msg[1].addr = aw86225->addr;
|
|
msg[1].flags = I2C_M_NOSTART;
|
|
msg[1].buffer = (uint8_t *)reg_data;
|
|
msg[1].length = cnt;
|
|
|
|
while (retries < AW86225_I2C_RETRIES)
|
|
{
|
|
ret = I2C_TRANSFER(aw86225->i2c, msg, 2);
|
|
if (ret < 0)
|
|
{
|
|
ierr("ERROR: I2C_TRANSFER failed: %d\n", ret);
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
|
|
retries++;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int aw86225_i2c_write(FAR struct aw86225 *aw86225,
|
|
uint8_t reg_addr, uint8_t reg_data)
|
|
{
|
|
return aw86225_i2c_write_cnt(aw86225, reg_addr, ®_data, 1);
|
|
}
|
|
|
|
static int aw86225_i2c_read_cnt(FAR struct aw86225 *aw86225,
|
|
uint8_t reg_addr,
|
|
FAR const uint8_t *reg_data,
|
|
uint32_t cnt)
|
|
{
|
|
struct i2c_msg_s msg[2];
|
|
int ret = -1;
|
|
int retries = 0;
|
|
|
|
msg[0].frequency = aw86225->freq;
|
|
msg[0].addr = aw86225->addr;
|
|
msg[0].flags = 0;
|
|
msg[0].buffer = ®_addr;
|
|
msg[0].length = 1;
|
|
|
|
msg[1].frequency = aw86225->freq;
|
|
msg[1].addr = aw86225->addr;
|
|
msg[1].flags = I2C_M_READ;
|
|
msg[1].buffer = (uint8_t *)reg_data;
|
|
msg[1].length = cnt;
|
|
|
|
while (retries < AW86225_I2C_RETRIES)
|
|
{
|
|
ret = I2C_TRANSFER(aw86225->i2c, msg, 2);
|
|
if (ret < 0)
|
|
{
|
|
ierr("ERROR: I2C_TRANSFER failed: %d\n", ret);
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
|
|
retries++;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int aw86225_i2c_read(FAR struct aw86225 *aw86225, uint8_t reg_addr,
|
|
FAR uint8_t *reg_data)
|
|
{
|
|
return aw86225_i2c_read_cnt(aw86225, reg_addr, reg_data, 1);
|
|
}
|
|
|
|
static int aw86225_i2c_writes(FAR struct aw86225 *aw86225,
|
|
unsigned char reg_addr,
|
|
FAR unsigned char *buf,
|
|
unsigned int len)
|
|
{
|
|
return aw86225_i2c_write_cnt(aw86225, reg_addr, buf, len);
|
|
}
|
|
|
|
static int aw86225_i2c_write_bits(FAR struct aw86225 *aw86225,
|
|
unsigned char reg_addr, unsigned int mask,
|
|
unsigned char reg_data)
|
|
{
|
|
int ret;
|
|
uint8_t reg_val = 0;
|
|
|
|
ret = aw86225_i2c_read(aw86225, reg_addr, ®_val);
|
|
if (ret < 0)
|
|
{
|
|
ierr("i2c read error, ret: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
reg_val &= mask;
|
|
reg_val |= reg_data;
|
|
ret = aw86225_i2c_write(aw86225, reg_addr, reg_val);
|
|
if (ret < 0)
|
|
{
|
|
ierr("i2c write error, ret: %d\n", ret);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int aw86225_request_firmware(FAR struct aw86225_firmware *fw,
|
|
FAR const char *filename)
|
|
{
|
|
char file_path[PATH_MAX];
|
|
struct file file;
|
|
size_t file_size;
|
|
int ret;
|
|
|
|
snprintf(file_path, sizeof(file_path), "%s/%s", CONFIG_FF_RTP_FILE_PATH,
|
|
filename);
|
|
ret = file_open(&file, file_path, O_RDONLY);
|
|
if (ret < 0)
|
|
{
|
|
ierr("open file failed");
|
|
return ret;
|
|
}
|
|
|
|
file_size = file_seek(&file, 0, SEEK_END);
|
|
file_seek(&file, 0, SEEK_SET);
|
|
|
|
fw->data = kmm_malloc(file_size);
|
|
if (fw->data != NULL)
|
|
{
|
|
fw->size = file_read(&file, (void *)fw->data, file_size);
|
|
}
|
|
|
|
file_close(&file);
|
|
|
|
if (fw->data == NULL || fw->size <= 0)
|
|
{
|
|
kmm_free((void *)fw->data);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void aw86225_release_firmware(FAR struct aw86225_firmware *fw)
|
|
{
|
|
kmm_free((void *)fw->data);
|
|
fw->data = NULL;
|
|
fw->size = 0;
|
|
}
|
|
|
|
static uint8_t aw86225_haptic_rtp_get_fifo_afs(FAR struct aw86225 *aw86225)
|
|
{
|
|
uint8_t reg_val = 0;
|
|
|
|
aw86225_i2c_read(aw86225, AW86225_REG_SYSST, ®_val);
|
|
reg_val &= AW86225_BIT_SYSST_FF_AFS;
|
|
return reg_val >> 3;
|
|
}
|
|
|
|
static void aw86225_haptic_set_rtp_aei(FAR struct aw86225 *aw86225,
|
|
bool flag)
|
|
{
|
|
if (flag)
|
|
{
|
|
aw86225_i2c_write_bits(aw86225, AW86225_REG_SYSINTM,
|
|
AW86225_BIT_SYSINTM_FF_AEM_MASK,
|
|
AW86225_BIT_SYSINTM_FF_AEM_ON);
|
|
}
|
|
else
|
|
{
|
|
aw86225_i2c_write_bits(aw86225, AW86225_REG_SYSINTM,
|
|
AW86225_BIT_SYSINTM_FF_AEM_MASK,
|
|
AW86225_BIT_SYSINTM_FF_AEM_OFF);
|
|
}
|
|
}
|
|
|
|
static void aw86225_haptic_upload_lra(FAR struct aw86225 *aw86225,
|
|
unsigned int flag)
|
|
{
|
|
switch (flag)
|
|
{
|
|
case AW86225_WRITE_ZERO:
|
|
{
|
|
aw86225_i2c_write_bits(aw86225, AW86225_REG_TRIMCFG3,
|
|
AW86225_BIT_TRIMCFG3_TRIM_LRA_MASK,
|
|
0x00);
|
|
break;
|
|
}
|
|
|
|
case AW86225_F0_CALI:
|
|
{
|
|
aw86225_i2c_write_bits(aw86225, AW86225_REG_TRIMCFG3,
|
|
AW86225_BIT_TRIMCFG3_TRIM_LRA_MASK,
|
|
(char)aw86225->f0_cali_data);
|
|
break;
|
|
}
|
|
|
|
case AW86225_OSC_CALI:
|
|
{
|
|
aw86225_i2c_write_bits(aw86225, AW86225_REG_TRIMCFG3,
|
|
AW86225_BIT_TRIMCFG3_TRIM_LRA_MASK,
|
|
(char)aw86225->osc_cali_data);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
ierr("%s write trim_lra error !\n", __func__);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void aw86225_sram_size(FAR struct aw86225 *aw86225,
|
|
int size_flag)
|
|
{
|
|
if (size_flag == AW86225_HAPTIC_SRAM_2K)
|
|
{
|
|
aw86225_i2c_write_bits(aw86225, AW86225_REG_RTPCFG1,
|
|
AW86225_BIT_RTPCFG1_SRAM_SIZE_2K_MASK,
|
|
AW86225_BIT_RTPCFG1_SRAM_SIZE_2K_EN);
|
|
aw86225_i2c_write_bits(aw86225, AW86225_REG_RTPCFG1,
|
|
AW86225_BIT_RTPCFG1_SRAM_SIZE_1K_MASK,
|
|
AW86225_BIT_RTPCFG1_SRAM_SIZE_1K_DIS);
|
|
}
|
|
else if (size_flag == AW86225_HAPTIC_SRAM_1K)
|
|
{
|
|
aw86225_i2c_write_bits(aw86225, AW86225_REG_RTPCFG1,
|
|
AW86225_BIT_RTPCFG1_SRAM_SIZE_2K_MASK,
|
|
AW86225_BIT_RTPCFG1_SRAM_SIZE_2K_DIS);
|
|
aw86225_i2c_write_bits(aw86225, AW86225_REG_RTPCFG1,
|
|
AW86225_BIT_RTPCFG1_SRAM_SIZE_1K_MASK,
|
|
AW86225_BIT_RTPCFG1_SRAM_SIZE_1K_EN);
|
|
}
|
|
else if (size_flag == AW86225_HAPTIC_SRAM_3K)
|
|
{
|
|
aw86225_i2c_write_bits(aw86225, AW86225_REG_RTPCFG1,
|
|
AW86225_BIT_RTPCFG1_SRAM_SIZE_1K_MASK,
|
|
AW86225_BIT_RTPCFG1_SRAM_SIZE_1K_EN);
|
|
aw86225_i2c_write_bits(aw86225, AW86225_REG_RTPCFG1,
|
|
AW86225_BIT_RTPCFG1_SRAM_SIZE_2K_MASK,
|
|
AW86225_BIT_RTPCFG1_SRAM_SIZE_2K_EN);
|
|
}
|
|
}
|
|
|
|
static void aw86225_haptic_stop(FAR struct aw86225 *aw86225)
|
|
{
|
|
unsigned char cnt = 40;
|
|
unsigned char reg_val = 0;
|
|
bool force_flag = true;
|
|
|
|
aw86225->play_mode = AW86225_HAPTIC_STANDBY_MODE;
|
|
aw86225_i2c_write(aw86225, AW86225_REG_PLAYCFG4, 0x02);
|
|
while (cnt)
|
|
{
|
|
aw86225_i2c_read(aw86225, AW86225_REG_GLBRD5, ®_val);
|
|
if ((reg_val & 0x0f) == 0x00 || (reg_val & 0x0f) == 0x0a)
|
|
{
|
|
cnt = 0;
|
|
force_flag = false;
|
|
iinfo("entered standby! glb_state=0x%02X\n", reg_val);
|
|
}
|
|
else
|
|
{
|
|
cnt--;
|
|
iinfo("wait for standby, glb_state=0x%02X\n", reg_val);
|
|
}
|
|
|
|
usleep(2000);
|
|
}
|
|
|
|
if (force_flag)
|
|
{
|
|
iinfo("%s force to enter standby mode!\n", __func__);
|
|
aw86225_i2c_write_bits(aw86225, AW86225_REG_SYSCTRL2,
|
|
AW86225_BIT_SYSCTRL2_STANDBY_MASK,
|
|
AW86225_BIT_SYSCTRL2_STANDBY_ON);
|
|
aw86225_i2c_write_bits(aw86225, AW86225_REG_SYSCTRL2,
|
|
AW86225_BIT_SYSCTRL2_STANDBY_MASK,
|
|
AW86225_BIT_SYSCTRL2_STANDBY_OFF);
|
|
}
|
|
}
|
|
|
|
static void aw86225_haptic_raminit(FAR struct aw86225 *aw86225,
|
|
bool flag)
|
|
{
|
|
if (flag)
|
|
{
|
|
aw86225_i2c_write_bits(aw86225, AW86225_REG_SYSCTRL1,
|
|
AW86225_BIT_SYSCTRL1_RAMINIT_MASK,
|
|
AW86225_BIT_SYSCTRL1_RAMINIT_ON);
|
|
}
|
|
else
|
|
{
|
|
aw86225_i2c_write_bits(aw86225, AW86225_REG_SYSCTRL1,
|
|
AW86225_BIT_SYSCTRL1_RAMINIT_MASK,
|
|
AW86225_BIT_SYSCTRL1_RAMINIT_OFF);
|
|
}
|
|
}
|
|
|
|
static void aw86225_haptic_get_vbat(FAR struct aw86225 *aw86225)
|
|
{
|
|
unsigned char reg_val = 0;
|
|
unsigned int vbat_code = 0;
|
|
|
|
/* unsigned int cont = 2000; */
|
|
|
|
aw86225_haptic_stop(aw86225);
|
|
aw86225_haptic_raminit(aw86225, true);
|
|
aw86225_i2c_write_bits(aw86225, AW86225_REG_DETCFG2,
|
|
AW86225_BIT_DETCFG2_VBAT_GO_MASK,
|
|
AW86225_BIT_DETCFG2_VABT_GO_ON);
|
|
usleep(22500);
|
|
aw86225_i2c_read(aw86225, AW86225_REG_DET_VBAT, ®_val);
|
|
vbat_code = (vbat_code | reg_val) << 2;
|
|
aw86225_i2c_read(aw86225, AW86225_REG_DET_LO, ®_val);
|
|
vbat_code = vbat_code | ((reg_val & 0x30) >> 4);
|
|
aw86225->vbat = 6100 * vbat_code / 1024;
|
|
if (aw86225->vbat > AW86225_VBAT_MAX)
|
|
{
|
|
aw86225->vbat = AW86225_VBAT_MAX;
|
|
}
|
|
|
|
if (aw86225->vbat < AW86225_VBAT_MIN)
|
|
{
|
|
aw86225->vbat = AW86225_VBAT_MIN;
|
|
}
|
|
|
|
iinfo("%s aw86225->vbat=%dmV, vbat_code=0x%02X\n", __func__,
|
|
aw86225->vbat, vbat_code);
|
|
aw86225_haptic_raminit(aw86225, false);
|
|
}
|
|
|
|
static void aw86225_interrupt_clear(FAR struct aw86225 *aw86225)
|
|
{
|
|
unsigned char reg_val = 0;
|
|
aw86225_i2c_read(aw86225, AW86225_REG_SYSINT, ®_val);
|
|
}
|
|
|
|
static int aw86225_haptic_set_gain(FAR struct aw86225 *aw86225,
|
|
unsigned char gain)
|
|
{
|
|
unsigned char comp_gain = 0;
|
|
if (aw86225->ram_vbat_compensate == AW86225_HAPTIC_RAM_VBAT_COMP_ENABLE)
|
|
{
|
|
aw86225_haptic_get_vbat(aw86225);
|
|
comp_gain = aw86225->gain * AW86225_VBAT_REFER / aw86225->vbat;
|
|
if (comp_gain > (128 * AW86225_VBAT_REFER / AW86225_VBAT_MIN))
|
|
{
|
|
comp_gain = 128 * AW86225_VBAT_REFER / AW86225_VBAT_MIN;
|
|
}
|
|
|
|
iinfo("%s: enable vbat comp, level = %x comp level = %x", __func__,
|
|
gain, comp_gain);
|
|
aw86225_i2c_write(aw86225, AW86225_REG_PLAYCFG2, comp_gain);
|
|
}
|
|
else
|
|
{
|
|
iinfo("%s: disable compsensation, vbat=%d, vbat_min=%d, vbat_ref=%d",
|
|
__func__, aw86225->vbat, AW86225_VBAT_MIN, AW86225_VBAT_REFER);
|
|
aw86225_i2c_write(aw86225, AW86225_REG_PLAYCFG2, gain);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int aw86225_haptic_ram_vbat_compensate(FAR struct aw86225 *aw86225,
|
|
bool flag)
|
|
{
|
|
if (flag)
|
|
{
|
|
aw86225->ram_vbat_compensate = AW86225_HAPTIC_RAM_VBAT_COMP_ENABLE;
|
|
}
|
|
else
|
|
{
|
|
aw86225->ram_vbat_compensate = AW86225_HAPTIC_RAM_VBAT_COMP_DISABLE;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int aw86225_haptic_play_mode(FAR struct aw86225 *aw86225,
|
|
unsigned char play_mode)
|
|
{
|
|
switch (play_mode)
|
|
{
|
|
case AW86225_HAPTIC_STANDBY_MODE:
|
|
{
|
|
aw86225->play_mode = AW86225_HAPTIC_STANDBY_MODE;
|
|
aw86225_haptic_stop(aw86225);
|
|
break;
|
|
}
|
|
|
|
case AW86225_HAPTIC_RAM_MODE:
|
|
{
|
|
aw86225->play_mode = AW86225_HAPTIC_RAM_MODE;
|
|
aw86225_i2c_write_bits(aw86225, AW86225_REG_PLAYCFG3,
|
|
AW86225_BIT_PLAYCFG3_PLAY_MODE_MASK,
|
|
AW86225_BIT_PLAYCFG3_PLAY_MODE_RAM);
|
|
break;
|
|
}
|
|
|
|
case AW86225_HAPTIC_RAM_LOOP_MODE:
|
|
{
|
|
aw86225->play_mode = AW86225_HAPTIC_RAM_LOOP_MODE;
|
|
aw86225_i2c_write_bits(aw86225, AW86225_REG_PLAYCFG3,
|
|
AW86225_BIT_PLAYCFG3_PLAY_MODE_MASK,
|
|
AW86225_BIT_PLAYCFG3_PLAY_MODE_RAM);
|
|
break;
|
|
}
|
|
|
|
case AW86225_HAPTIC_RTP_MODE:
|
|
{
|
|
aw86225->play_mode = AW86225_HAPTIC_RTP_MODE;
|
|
aw86225_i2c_write_bits(aw86225, AW86225_REG_PLAYCFG3,
|
|
AW86225_BIT_PLAYCFG3_PLAY_MODE_MASK,
|
|
AW86225_BIT_PLAYCFG3_PLAY_MODE_RTP);
|
|
break;
|
|
}
|
|
|
|
case AW86225_HAPTIC_TRIG_MODE:
|
|
{
|
|
aw86225->play_mode = AW86225_HAPTIC_TRIG_MODE;
|
|
aw86225_i2c_write_bits(aw86225, AW86225_REG_PLAYCFG3,
|
|
AW86225_BIT_PLAYCFG3_PLAY_MODE_MASK,
|
|
AW86225_BIT_PLAYCFG3_PLAY_MODE_RAM);
|
|
break;
|
|
}
|
|
|
|
case AW86225_HAPTIC_CONT_MODE:
|
|
{
|
|
aw86225->play_mode = AW86225_HAPTIC_CONT_MODE;
|
|
aw86225_i2c_write_bits(aw86225, AW86225_REG_PLAYCFG3,
|
|
AW86225_BIT_PLAYCFG3_PLAY_MODE_MASK,
|
|
AW86225_BIT_PLAYCFG3_PLAY_MODE_CONT);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
ierr("%s: play mode %d error", __func__, play_mode);
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int aw86225_haptic_play_go(FAR struct aw86225 *aw86225, bool flag)
|
|
{
|
|
unsigned char reg_val = 0;
|
|
|
|
if (flag == true)
|
|
{
|
|
aw86225_i2c_write(aw86225, AW86225_REG_PLAYCFG4, 0x01);
|
|
usleep(2000);
|
|
}
|
|
else
|
|
{
|
|
aw86225_i2c_write(aw86225, AW86225_REG_PLAYCFG4, 0x02);
|
|
}
|
|
|
|
aw86225_i2c_read(aw86225, AW86225_REG_GLBRD5, ®_val);
|
|
return 0;
|
|
}
|
|
|
|
static int aw86225_haptic_set_wav_seq(FAR struct aw86225 *aw86225,
|
|
unsigned char wav, unsigned char seq)
|
|
{
|
|
aw86225_i2c_write(aw86225, AW86225_REG_WAVCFG1 + wav, seq);
|
|
return 0;
|
|
}
|
|
|
|
static int aw86225_haptic_set_wav_loop(FAR struct aw86225 *aw86225,
|
|
unsigned char wav, unsigned char loop)
|
|
{
|
|
unsigned char tmp = 0;
|
|
|
|
if (wav % 2)
|
|
{
|
|
tmp = loop << 0;
|
|
aw86225_i2c_write_bits(aw86225, AW86225_REG_WAVCFG9 + (wav / 2),
|
|
AW86225_BIT_WAVLOOP_SEQ_EVEN_MASK, tmp);
|
|
}
|
|
else
|
|
{
|
|
tmp = loop << 4;
|
|
aw86225_i2c_write_bits(aw86225, AW86225_REG_WAVCFG9 + (wav / 2),
|
|
AW86225_BIT_WAVLOOP_SEQ_ODD_MASK, tmp);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int aw86225_haptic_read_lra_f0(FAR struct aw86225 *aw86225)
|
|
{
|
|
unsigned char reg_val = 0;
|
|
unsigned int f0_reg = 0;
|
|
unsigned long f0_tmp = 0;
|
|
|
|
/* F_LRA_F0_H */
|
|
|
|
aw86225_i2c_read(aw86225, AW86225_REG_CONTRD14, ®_val);
|
|
f0_reg = (f0_reg | reg_val) << 8;
|
|
|
|
/* F_LRA_F0_L */
|
|
|
|
aw86225_i2c_read(aw86225, AW86225_REG_CONTRD15, ®_val);
|
|
f0_reg |= (reg_val << 0);
|
|
if (!f0_reg)
|
|
{
|
|
ierr("%s didn't get cont f0 because f0_reg value is 0!\n", __func__);
|
|
aw86225->f0 = aw86225->config->f0_ref;
|
|
return -1;
|
|
}
|
|
else
|
|
{
|
|
f0_tmp = 384000 * 10 / f0_reg;
|
|
aw86225->f0 = (unsigned int)f0_tmp;
|
|
iinfo("%s lra_f0=%d\n", __func__, aw86225->f0);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int aw86225_haptic_read_cont_f0(FAR struct aw86225 *aw86225)
|
|
{
|
|
unsigned char reg_val = 0;
|
|
unsigned int f0_reg = 0;
|
|
unsigned long f0_tmp = 0;
|
|
|
|
aw86225_i2c_read(aw86225, AW86225_REG_CONTRD16, ®_val);
|
|
f0_reg = (f0_reg | reg_val) << 8;
|
|
aw86225_i2c_read(aw86225, AW86225_REG_CONTRD17, ®_val);
|
|
f0_reg |= (reg_val << 0);
|
|
if (!f0_reg)
|
|
{
|
|
ierr("%s didn't get cont f0 because f0_reg value is 0!\n", __func__);
|
|
aw86225->cont_f0 = aw86225->config->f0_ref;
|
|
return -1;
|
|
}
|
|
else
|
|
{
|
|
f0_tmp = 384000 * 10 / f0_reg;
|
|
aw86225->cont_f0 = (unsigned int)f0_tmp;
|
|
iinfo("%s cont_f0=%d\n", __func__, aw86225->cont_f0);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int aw86225_haptic_cont_get_f0(FAR struct aw86225 *aw86225)
|
|
{
|
|
int ret = 0;
|
|
unsigned char reg_val = 0;
|
|
unsigned int cnt = 200;
|
|
bool get_f0_flag = false;
|
|
unsigned char brk_en_temp = 0;
|
|
|
|
aw86225->f0 = aw86225->config->f0_ref;
|
|
|
|
/* enter standby mode */
|
|
|
|
aw86225_haptic_stop(aw86225);
|
|
|
|
/* f0 calibrate work mode */
|
|
|
|
aw86225_haptic_play_mode(aw86225, AW86225_HAPTIC_CONT_MODE);
|
|
|
|
/* enable f0 detect */
|
|
|
|
aw86225_i2c_write_bits(aw86225, AW86225_REG_CONTCFG1,
|
|
AW86225_BIT_CONTCFG1_EN_F0_DET_MASK,
|
|
AW86225_BIT_CONTCFG1_F0_DET_ENABLE);
|
|
|
|
/* cont config */
|
|
|
|
aw86225_i2c_write_bits(aw86225, AW86225_REG_CONTCFG6,
|
|
AW86225_BIT_CONTCFG6_TRACK_EN_MASK,
|
|
AW86225_BIT_CONTCFG6_TRACK_ENABLE);
|
|
|
|
/* enable auto brake */
|
|
|
|
aw86225_i2c_read(aw86225, AW86225_REG_PLAYCFG3, ®_val);
|
|
brk_en_temp = 0x04 & reg_val;
|
|
aw86225_i2c_write_bits(aw86225, AW86225_REG_PLAYCFG3,
|
|
AW86225_BIT_PLAYCFG3_BRK_EN_MASK,
|
|
AW86225_BIT_PLAYCFG3_BRK_ENABLE);
|
|
|
|
/* f0 driver level */
|
|
|
|
aw86225_i2c_write_bits(aw86225, AW86225_REG_CONTCFG6,
|
|
AW86225_BIT_CONTCFG6_DRV1_LVL_MASK,
|
|
aw86225->config->cont_drv1_lvl_dt);
|
|
aw86225_i2c_write(aw86225, AW86225_REG_CONTCFG7,
|
|
aw86225->config->cont_drv2_lvl_dt);
|
|
|
|
/* DRV1_TIME */
|
|
|
|
aw86225_i2c_write(aw86225, AW86225_REG_CONTCFG8,
|
|
aw86225->config->cont_drv1_time_dt);
|
|
|
|
/* DRV2_TIME */
|
|
|
|
aw86225_i2c_write(aw86225, AW86225_REG_CONTCFG9,
|
|
aw86225->config->cont_drv2_time_dt);
|
|
|
|
/* TRACK_MARGIN */
|
|
|
|
if (!aw86225->config->cont_track_margin)
|
|
{
|
|
ierr("%s aw86225->config->cont_track_margin = 0!\n", __func__);
|
|
}
|
|
else
|
|
{
|
|
aw86225_i2c_write(aw86225, AW86225_REG_CONTCFG11,
|
|
(unsigned char)aw86225->config->cont_track_margin);
|
|
}
|
|
|
|
/* cont play go */
|
|
|
|
aw86225_haptic_play_go(aw86225, true);
|
|
|
|
/* 300ms */
|
|
|
|
while (cnt)
|
|
{
|
|
aw86225_i2c_read(aw86225, AW86225_REG_GLBRD5, ®_val);
|
|
if ((reg_val & 0x0f) == 0x00)
|
|
{
|
|
cnt = 0;
|
|
get_f0_flag = true;
|
|
iinfo("%s entered standby mode! glb_state=0x%02X\n",
|
|
__func__, reg_val);
|
|
}
|
|
else
|
|
{
|
|
cnt--;
|
|
iinfo("%s waitting for standby, glb_state=0x%02X\n",
|
|
__func__, reg_val);
|
|
}
|
|
|
|
usleep(10000);
|
|
}
|
|
|
|
if (get_f0_flag)
|
|
{
|
|
aw86225_haptic_read_lra_f0(aw86225);
|
|
aw86225_haptic_read_cont_f0(aw86225);
|
|
}
|
|
else
|
|
{
|
|
ierr("%s enter standby mode failed, stop reading f0!\n", __func__);
|
|
}
|
|
|
|
/* restore default config */
|
|
|
|
aw86225_i2c_write_bits(aw86225, AW86225_REG_CONTCFG1,
|
|
AW86225_BIT_CONTCFG1_EN_F0_DET_MASK,
|
|
AW86225_BIT_CONTCFG1_F0_DET_DISABLE);
|
|
|
|
/* recover auto break config */
|
|
|
|
aw86225_i2c_write_bits(aw86225, AW86225_REG_PLAYCFG3,
|
|
AW86225_BIT_PLAYCFG3_BRK_EN_MASK,
|
|
brk_en_temp);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int aw86225_haptic_rtp_init(FAR struct aw86225 *aw86225)
|
|
{
|
|
unsigned int buf_len = 0;
|
|
unsigned char glb_state_val = 0;
|
|
|
|
aw86225->rtp_cnt = 0;
|
|
nxmutex_lock(&aw86225->rtp_lock);
|
|
while ((!aw86225_haptic_rtp_get_fifo_afs(aw86225))
|
|
&& (aw86225->play_mode == AW86225_HAPTIC_RTP_MODE)
|
|
&& !atomic_load(&aw86225->exit_in_rtp_loop))
|
|
{
|
|
if (!aw86225->rtp_container)
|
|
{
|
|
ierr("%s:aw86225->rtp_container is null\n", __func__);
|
|
break;
|
|
}
|
|
|
|
if (aw86225->rtp_cnt < aw86225->ram.base_addr)
|
|
{
|
|
if ((aw86225->rtp_container->len - aw86225->rtp_cnt) <
|
|
aw86225->ram.base_addr)
|
|
{
|
|
buf_len = aw86225->rtp_container->len - aw86225->rtp_cnt;
|
|
}
|
|
else
|
|
{
|
|
buf_len = aw86225->ram.base_addr;
|
|
}
|
|
}
|
|
else if ((aw86225->rtp_container->len - aw86225->rtp_cnt) <
|
|
(aw86225->ram.base_addr >> 2))
|
|
{
|
|
buf_len = aw86225->rtp_container->len - aw86225->rtp_cnt;
|
|
}
|
|
else
|
|
{
|
|
buf_len = aw86225->ram.base_addr >> 2;
|
|
}
|
|
|
|
aw86225_i2c_writes(aw86225, AW86225_REG_RTPDATA,
|
|
&aw86225->rtp_container->data[aw86225->rtp_cnt],
|
|
buf_len);
|
|
aw86225->rtp_cnt += buf_len;
|
|
aw86225_i2c_read(aw86225, AW86225_REG_GLBRD5, &glb_state_val);
|
|
if ((aw86225->rtp_cnt == aw86225->rtp_container->len)
|
|
|| ((glb_state_val & 0x0f) == 0x00))
|
|
{
|
|
if (aw86225->rtp_cnt == aw86225->rtp_container->len)
|
|
{
|
|
iinfo("%s: rtp load completely! glb_state_val=0x%02x \
|
|
aw86225->rtp_cnt=%d\n", \
|
|
__func__, glb_state_val, aw86225->rtp_cnt);
|
|
}
|
|
else
|
|
{
|
|
iinfo("%s rtp load failed!! glb_state_val=0x%02x \
|
|
aw86225->rtp_cnt=%d\n", __func__, glb_state_val, \
|
|
aw86225->rtp_cnt);
|
|
aw86225->rtp_cnt = 0;
|
|
nxmutex_unlock(&aw86225->rtp_lock);
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
nxmutex_unlock(&aw86225->rtp_lock);
|
|
if (aw86225->play_mode == AW86225_HAPTIC_RTP_MODE
|
|
&& !atomic_load(&aw86225->exit_in_rtp_loop))
|
|
{
|
|
aw86225_haptic_set_rtp_aei(aw86225, true);
|
|
}
|
|
|
|
iinfo("%s exit\n", __func__);
|
|
return 0;
|
|
}
|
|
|
|
static int aw86225_haptic_set_repeat_wav_seq(FAR struct aw86225 *aw86225,
|
|
unsigned char seq)
|
|
{
|
|
aw86225_haptic_set_wav_seq(aw86225, 0x00, seq);
|
|
aw86225_haptic_set_wav_loop(aw86225, 0x00,
|
|
AW86225_BIT_WAVLOOP_INIFINITELY);
|
|
return 0;
|
|
}
|
|
|
|
static void aw86225_haptic_set_pwm(FAR struct aw86225 *aw86225,
|
|
unsigned char mode)
|
|
{
|
|
switch (mode)
|
|
{
|
|
case AW86225_PWM_48K:
|
|
{
|
|
aw86225_i2c_write_bits(aw86225, AW86225_REG_SYSCTRL2,
|
|
AW86225_BIT_SYSCTRL2_WAVDAT_MODE_MASK,
|
|
AW86225_BIT_SYSCTRL2_RATE_48K);
|
|
break;
|
|
}
|
|
|
|
case AW86225_PWM_24K:
|
|
{
|
|
aw86225_i2c_write_bits(aw86225, AW86225_REG_SYSCTRL2,
|
|
AW86225_BIT_SYSCTRL2_WAVDAT_MODE_MASK,
|
|
AW86225_BIT_SYSCTRL2_RATE_24K);
|
|
break;
|
|
}
|
|
|
|
case AW86225_PWM_12K:
|
|
{
|
|
aw86225_i2c_write_bits(aw86225, AW86225_REG_SYSCTRL2,
|
|
AW86225_BIT_SYSCTRL2_WAVDAT_MODE_MASK,
|
|
AW86225_BIT_SYSCTRL2_RATE_12K);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static int16_t
|
|
aw86225_haptic_effect_strength(FAR struct aw86225 *aw86225)
|
|
{
|
|
iinfo("aw86225->play.vmax_mv =0x%x\n", aw86225->play.vmax_mv);
|
|
|
|
if (aw86225->play.vmax_mv >= 0x7fff)
|
|
{
|
|
aw86225->level = 0x80;
|
|
}
|
|
else if (aw86225->play.vmax_mv <= 0x3fff)
|
|
{
|
|
aw86225->level = 0x1e;
|
|
}
|
|
else
|
|
{
|
|
aw86225->level = (aw86225->play.vmax_mv - 16383) / 128;
|
|
}
|
|
|
|
if (aw86225->level < 0x1e)
|
|
{
|
|
aw86225->level = 0x1e;
|
|
}
|
|
|
|
iinfo("%s: aw86225->level =0x%x\n", __func__, aw86225->level);
|
|
return 0;
|
|
}
|
|
|
|
static void aw86225_rtp_work_routine(FAR void *arg)
|
|
{
|
|
FAR struct aw86225 *aw86225 = arg;
|
|
struct aw86225_firmware rtp_file;
|
|
int ret = -1;
|
|
unsigned int cnt = 200;
|
|
unsigned char reg_val = 0;
|
|
bool rtp_work_flag = false;
|
|
|
|
iinfo("%s enter\n", __func__);
|
|
|
|
if ((aw86225->effect_id < aw86225->config->effect_id_boundary) &&
|
|
(aw86225->effect_id > aw86225->config->effect_max))
|
|
{
|
|
return;
|
|
}
|
|
|
|
iinfo("%s: effect_id =%d state = %d\n", __func__, aw86225->effect_id,
|
|
aw86225->state);
|
|
nxmutex_lock(&aw86225->lock);
|
|
aw86225_haptic_upload_lra(aw86225, AW86225_OSC_CALI);
|
|
aw86225_haptic_set_rtp_aei(aw86225, false);
|
|
aw86225_interrupt_clear(aw86225);
|
|
|
|
/* wait for irq to exit */
|
|
|
|
atomic_store(&aw86225->exit_in_rtp_loop, 1);
|
|
while (atomic_load(&aw86225->is_in_rtp_loop))
|
|
{
|
|
iinfo("%s: goint to waiting irq exit\n", __func__);
|
|
|
|
ret = nxsem_wait(&aw86225->wait_q);
|
|
|
|
if (ret == -ERESTART)
|
|
{
|
|
atomic_store(&aw86225->exit_in_rtp_loop, 0);
|
|
nxsem_post(&aw86225->stop_wait_q);
|
|
nxmutex_unlock(&aw86225->lock);
|
|
ierr("%s: wake up by signal return erro\n", __func__);
|
|
return;
|
|
}
|
|
}
|
|
|
|
atomic_store(&aw86225->exit_in_rtp_loop, 0);
|
|
nxsem_post(&aw86225->stop_wait_q);
|
|
aw86225_haptic_stop(aw86225);
|
|
|
|
if (aw86225->state)
|
|
{
|
|
aw86225->wk_lock_flag = 1;
|
|
aw86225_haptic_effect_strength(aw86225);
|
|
aw86225->rtp_file_num = aw86225->effect_id -
|
|
aw86225->config->effect_id_boundary;
|
|
iinfo("aw86225->rtp_file_num =%d\n", aw86225->rtp_file_num);
|
|
if (aw86225->rtp_file_num < 0)
|
|
{
|
|
aw86225->rtp_file_num = 0;
|
|
}
|
|
|
|
if (aw86225->rtp_file_num >
|
|
((sizeof(g_aw86225_rtp_name) / AW86225_RTP_NAME_MAX) - 1))
|
|
{
|
|
aw86225->rtp_file_num =
|
|
(sizeof(g_aw86225_rtp_name) / AW86225_RTP_NAME_MAX) - 1;
|
|
}
|
|
|
|
/* fw loaded */
|
|
|
|
ret = aw86225_request_firmware(&rtp_file,
|
|
g_aw86225_rtp_name[aw86225->rtp_file_num]);
|
|
if (ret < 0)
|
|
{
|
|
ierr("%s: failed to read %s\n", __func__,
|
|
g_aw86225_rtp_name[aw86225->rtp_file_num]);
|
|
if (aw86225->wk_lock_flag == 1)
|
|
{
|
|
aw86225->wk_lock_flag = 0;
|
|
}
|
|
|
|
nxmutex_unlock(&aw86225->lock);
|
|
return;
|
|
}
|
|
|
|
aw86225->rtp_init = 0;
|
|
kmm_free(aw86225->rtp_container);
|
|
aw86225->rtp_container = kmm_malloc(rtp_file.size + sizeof(int));
|
|
if (!aw86225->rtp_container)
|
|
{
|
|
aw86225_release_firmware(&rtp_file);
|
|
ierr("%s: error allocating memory\n", __func__);
|
|
if (aw86225->wk_lock_flag == 1)
|
|
{
|
|
aw86225->wk_lock_flag = 0;
|
|
}
|
|
|
|
nxmutex_unlock(&aw86225->lock);
|
|
return;
|
|
}
|
|
|
|
aw86225->rtp_container->len = rtp_file.size;
|
|
iinfo("%s: rtp file:[%s] size = %dbytes\n",
|
|
__func__, g_aw86225_rtp_name[aw86225->rtp_file_num],
|
|
aw86225->rtp_container->len);
|
|
memcpy(aw86225->rtp_container->data, rtp_file.data, rtp_file.size);
|
|
aw86225_release_firmware(&rtp_file);
|
|
aw86225->rtp_init = 1;
|
|
aw86225_haptic_upload_lra(aw86225, AW86225_OSC_CALI);
|
|
aw86225_haptic_set_pwm(aw86225, AW86225_PWM_24K);
|
|
|
|
/* gain */
|
|
|
|
aw86225_haptic_ram_vbat_compensate(aw86225, false);
|
|
aw86225_haptic_set_gain(aw86225, aw86225->level);
|
|
|
|
/* rtp mode config */
|
|
|
|
aw86225_haptic_play_mode(aw86225, AW86225_HAPTIC_RTP_MODE);
|
|
|
|
/* haptic go */
|
|
|
|
aw86225_haptic_play_go(aw86225, true);
|
|
nxmutex_unlock(&aw86225->lock);
|
|
usleep(2000);
|
|
while (cnt)
|
|
{
|
|
aw86225_i2c_read(aw86225, AW86225_REG_GLBRD5, ®_val);
|
|
if ((reg_val & 0x0f) == 0x08)
|
|
{
|
|
cnt = 0;
|
|
rtp_work_flag = true;
|
|
iinfo("%s RTP_GO! glb_state=0x08\n", __func__);
|
|
}
|
|
else
|
|
{
|
|
cnt--;
|
|
iinfo("wait for RTP_GO, glb_state=0x%02X\n", reg_val);
|
|
}
|
|
|
|
usleep(2000);
|
|
}
|
|
|
|
if (rtp_work_flag)
|
|
{
|
|
aw86225_haptic_rtp_init(aw86225);
|
|
}
|
|
else
|
|
{
|
|
aw86225_haptic_stop(aw86225);
|
|
ierr("%s failed to enter RTP_GO status!\n", __func__);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (aw86225->wk_lock_flag == 1)
|
|
{
|
|
aw86225->wk_lock_flag = 0;
|
|
}
|
|
|
|
aw86225->rtp_cnt = 0;
|
|
aw86225->rtp_init = 0;
|
|
nxmutex_unlock(&aw86225->lock);
|
|
}
|
|
}
|
|
|
|
static int aw86225_container_update(FAR struct aw86225 *aw86225,
|
|
FAR struct aw86225_container *aw86225_cont)
|
|
{
|
|
unsigned char reg_val = 0;
|
|
unsigned int shift = 0;
|
|
unsigned int temp = 0;
|
|
int i = 0;
|
|
int ret = 0;
|
|
#ifdef AW_CHECK_RAM_DATA
|
|
unsigned short check_sum = 0;
|
|
#endif
|
|
|
|
iinfo("%s enter\n", __func__);
|
|
nxmutex_lock(&aw86225->lock);
|
|
aw86225->ram.baseaddr_shift = 2;
|
|
aw86225->ram.ram_shift = 4;
|
|
|
|
/* RAMINIT Enable */
|
|
|
|
aw86225_haptic_raminit(aw86225, true);
|
|
|
|
/* Enter standby mode */
|
|
|
|
aw86225_haptic_stop(aw86225);
|
|
|
|
/* base addr */
|
|
|
|
shift = aw86225->ram.baseaddr_shift;
|
|
aw86225->ram.base_addr = (unsigned int)
|
|
((aw86225_cont->data[0 + shift] << 8) |
|
|
(aw86225_cont->data[1 + shift]));
|
|
|
|
/* default 3k SRAM */
|
|
|
|
aw86225_sram_size(aw86225, AW86225_HAPTIC_SRAM_3K);
|
|
|
|
aw86225_i2c_write_bits(aw86225, AW86225_REG_RTPCFG1,
|
|
AW86225_BIT_RTPCFG1_ADDRH_MASK,
|
|
aw86225_cont->data[0 + shift]);
|
|
|
|
aw86225_i2c_write(aw86225, AW86225_REG_RTPCFG2,
|
|
aw86225_cont->data[1 + shift]);
|
|
|
|
/* FIFO_AEH */
|
|
|
|
aw86225_i2c_write_bits(aw86225, AW86225_REG_RTPCFG3,
|
|
AW86225_BIT_RTPCFG3_FIFO_AEH_MASK,
|
|
(unsigned char)
|
|
(((aw86225->ram.base_addr >> 1) >> 4) & 0xf0));
|
|
|
|
/* FIFO AEL */
|
|
|
|
aw86225_i2c_write(aw86225, AW86225_REG_RTPCFG4,
|
|
(unsigned char)
|
|
(((aw86225->ram.base_addr >> 1) & 0x00ff)));
|
|
|
|
/* FIFO_AFH */
|
|
|
|
aw86225_i2c_write_bits(aw86225, AW86225_REG_RTPCFG3,
|
|
AW86225_BIT_RTPCFG3_FIFO_AFH_MASK,
|
|
(unsigned char)(((aw86225->ram.base_addr -
|
|
(aw86225->ram.base_addr >> 2)) >> 8) & 0x0f));
|
|
|
|
/* FIFO_AFL */
|
|
|
|
aw86225_i2c_write(aw86225, AW86225_REG_RTPCFG5,
|
|
(unsigned char)(((aw86225->ram.base_addr -
|
|
(aw86225->ram.base_addr >> 2)) & 0x00ff)));
|
|
|
|
aw86225_i2c_read(aw86225, AW86225_REG_RTPCFG3, ®_val);
|
|
temp = ((reg_val & 0x0f) << 24) | ((reg_val & 0xf0) << 4);
|
|
aw86225_i2c_read(aw86225, AW86225_REG_RTPCFG4, ®_val);
|
|
temp = temp | reg_val;
|
|
iinfo("almost_empty_threshold = %d", (unsigned short)temp);
|
|
aw86225_i2c_read(aw86225, AW86225_REG_RTPCFG5, ®_val);
|
|
temp = temp | (reg_val << 16);
|
|
iinfo("almost_full_threshold = %d\n", temp >> 16);
|
|
|
|
shift = aw86225->ram.baseaddr_shift;
|
|
|
|
aw86225_i2c_write_bits(aw86225, AW86225_REG_RAMADDRH,
|
|
AW86225_BIT_RAMADDRH_MASK,
|
|
aw86225_cont->data[0 + shift]);
|
|
aw86225_i2c_write(aw86225, AW86225_REG_RAMADDRL,
|
|
aw86225_cont->data[1 + shift]);
|
|
shift = aw86225->ram.ram_shift;
|
|
iinfo("%s: ram_len = %d\n", __func__, aw86225_cont->len - shift);
|
|
for (i = shift; i < aw86225_cont->len; i++)
|
|
{
|
|
aw86225->ram_update_flag = aw86225_i2c_write(aw86225,
|
|
AW86225_REG_RAMDATA,
|
|
aw86225_cont->data[i]);
|
|
}
|
|
|
|
#ifdef AW_CHECK_RAM_DATA
|
|
shift = aw86225->ram.baseaddr_shift;
|
|
aw86225_i2c_write_bits(aw86225, AW86225_REG_RAMADDRH,
|
|
AW86225_BIT_RAMADDRH_MASK,
|
|
aw86225_cont->data[0 + shift]);
|
|
aw86225_i2c_write(aw86225, AW86225_REG_RAMADDRL,
|
|
aw86225_cont->data[1 + shift]);
|
|
shift = aw86225->ram.ram_shift;
|
|
for (i = shift; i < aw86225_cont->len; i++)
|
|
{
|
|
aw86225_i2c_read(aw86225, AW86225_REG_RAMDATA, ®_val);
|
|
if (reg_val != aw86225_cont->data[i])
|
|
{
|
|
iinfo("%s: ram check error addr=0x%04x, file_data=0x%02X, \
|
|
ram_data=0x%02X\n", \
|
|
__func__, i, aw86225_cont->data[i], reg_val);
|
|
ret = -1;
|
|
break;
|
|
}
|
|
|
|
check_sum += reg_val;
|
|
}
|
|
|
|
if (!ret)
|
|
{
|
|
aw86225_i2c_read(aw86225, AW86225_REG_RTPCFG1, ®_val);
|
|
check_sum += reg_val & 0x0f;
|
|
aw86225_i2c_read(aw86225, AW86225_REG_RTPCFG2, ®_val);
|
|
check_sum += reg_val;
|
|
|
|
if (check_sum != aw86225->ram.check_sum)
|
|
{
|
|
ierr("ram data check sum error, check_sum=0x%04x\n", check_sum);
|
|
ret = -1;
|
|
}
|
|
else
|
|
{
|
|
iinfo("ram data check sum pass, check_sum=0x%04x\n", check_sum);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* RAMINIT Disable */
|
|
|
|
aw86225_haptic_raminit(aw86225, false);
|
|
nxmutex_unlock(&aw86225->lock);
|
|
iinfo("%s exit\n", __func__);
|
|
return ret;
|
|
}
|
|
|
|
static void aw86225_ram_loaded(FAR struct aw86225_firmware *cont,
|
|
FAR struct aw86225 *aw86225)
|
|
{
|
|
struct aw86225_container *aw86225_fw;
|
|
unsigned short check_sum = 0;
|
|
int i = 0;
|
|
int ret = 0;
|
|
#ifdef AW_READ_BIN_FLEXBALLY
|
|
static unsigned char load_cont;
|
|
int ram_timer_val = 1000;
|
|
|
|
load_cont++;
|
|
#endif
|
|
ierr("%s enter\n", __func__);
|
|
aw86225_request_firmware(cont, g_aw86225_ram_name);
|
|
|
|
if (!cont)
|
|
{
|
|
ierr("%s: failed to read %s\n", __func__, g_aw86225_ram_name);
|
|
aw86225_release_firmware(cont);
|
|
#ifdef AW_READ_BIN_FLEXBALLY
|
|
if (load_cont <= 20)
|
|
{
|
|
work_queue(HPWORK, &aw86225->ram_work,
|
|
aw86225_ram_work_routine,
|
|
aw86225, MSEC2TICK(ram_timer_val));
|
|
ierr("%s:start hrtimer: load_cont=%d\n", __func__, load_cont);
|
|
}
|
|
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
/* check sum */
|
|
|
|
for (i = 2; i < cont->size; i++)
|
|
{
|
|
check_sum += cont->data[i];
|
|
}
|
|
|
|
if (check_sum != (unsigned short)((cont->data[0] << 8) | (cont->data[1])))
|
|
{
|
|
ierr("%s: check sum err: check_sum=0x%04x\n", __func__, check_sum);
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
iinfo("%s: check sum pass: 0x%04x\n", __func__, check_sum);
|
|
aw86225->ram.check_sum = check_sum;
|
|
}
|
|
|
|
/* aw86225 ram update less then 128kB */
|
|
|
|
aw86225_fw = kmm_zalloc(cont->size + sizeof(int));
|
|
if (!aw86225_fw)
|
|
{
|
|
aw86225_release_firmware(cont);
|
|
ierr("%s: Error allocating memory\n", __func__);
|
|
return;
|
|
}
|
|
|
|
aw86225_fw->len = cont->size;
|
|
memcpy(aw86225_fw->data, cont->data, cont->size);
|
|
aw86225_release_firmware(cont);
|
|
ret = aw86225_container_update(aw86225, aw86225_fw);
|
|
if (ret)
|
|
{
|
|
kmm_free(aw86225_fw);
|
|
aw86225->ram.len = 0;
|
|
ierr("%s: ram firmware update failed!\n", __func__);
|
|
}
|
|
else
|
|
{
|
|
aw86225->ram_init = 1;
|
|
aw86225->ram.len = aw86225_fw->len;
|
|
kmm_free(aw86225_fw);
|
|
iinfo("%s: ram firmware update complete!\n", __func__);
|
|
}
|
|
}
|
|
|
|
static void aw86225_vibrator_timer_func(wdparm_t arg)
|
|
{
|
|
struct aw86225 *aw86225 = (struct aw86225 *)arg;
|
|
iinfo("%s enter\n", __func__);
|
|
aw86225->state = 0;
|
|
work_queue(HPWORK, &aw86225->long_vibrate_work,
|
|
aw86225_long_vibrate_work_routine, aw86225, 0);
|
|
}
|
|
|
|
static void
|
|
aw86225_haptic_play_repeat_seq(FAR struct aw86225 *aw86225,
|
|
unsigned char flag)
|
|
{
|
|
if (flag)
|
|
{
|
|
aw86225_haptic_play_mode(aw86225, AW86225_HAPTIC_RAM_LOOP_MODE);
|
|
aw86225_haptic_play_go(aw86225, true);
|
|
}
|
|
}
|
|
|
|
static void aw86225_haptic_trig_config(FAR struct aw86225 *aw86225)
|
|
{
|
|
if (aw86225->is_used_irq == false)
|
|
{
|
|
aw86225_i2c_write_bits(aw86225, AW86225_REG_SYSCTRL2,
|
|
AW86225_BIT_SYSCTRL2_INTN_PIN_MASK,
|
|
AW86225_BIT_SYSCTRL2_TRIG1);
|
|
}
|
|
}
|
|
|
|
static void
|
|
aw86225_haptic_swicth_motor_protect_config(FAR struct aw86225 *aw86225,
|
|
unsigned char addr,
|
|
unsigned char val)
|
|
{
|
|
if (addr == 1)
|
|
{
|
|
aw86225_i2c_write_bits(aw86225, AW86225_REG_DETCFG1,
|
|
AW86225_BIT_DETCFG1_PRCT_MODE_MASK,
|
|
AW86225_BIT_DETCFG1_PRCT_MODE_VALID);
|
|
aw86225_i2c_write_bits(aw86225, AW86225_REG_PWMCFG1,
|
|
AW86225_BIT_PWMCFG1_PRC_EN_MASK,
|
|
AW86225_BIT_PWMCFG1_PRC_ENABLE);
|
|
aw86225_i2c_write_bits(aw86225, AW86225_REG_PWMCFG3,
|
|
AW86225_BIT_PWMCFG3_PR_EN_MASK,
|
|
AW86225_BIT_PWMCFG3_PR_ENABLE);
|
|
}
|
|
else if (addr == 0)
|
|
{
|
|
aw86225_i2c_write_bits(aw86225, AW86225_REG_DETCFG1,
|
|
AW86225_BIT_DETCFG1_PRCT_MODE_MASK,
|
|
AW86225_BIT_DETCFG1_PRCT_MODE_INVALID);
|
|
aw86225_i2c_write_bits(aw86225, AW86225_REG_PWMCFG1,
|
|
AW86225_BIT_PWMCFG1_PRC_EN_MASK,
|
|
AW86225_BIT_PWMCFG1_PRC_DISABLE);
|
|
aw86225_i2c_write_bits(aw86225, AW86225_REG_PWMCFG3,
|
|
AW86225_BIT_PWMCFG3_PR_EN_MASK,
|
|
AW86225_BIT_PWMCFG3_PR_DISABLE);
|
|
}
|
|
else if (addr == 0x2d)
|
|
{
|
|
aw86225_i2c_write_bits(aw86225, AW86225_REG_PWMCFG1,
|
|
AW86225_BIT_PWMCFG1_PRCTIME_MASK, val);
|
|
}
|
|
else if (addr == 0x3e)
|
|
{
|
|
aw86225_i2c_write_bits(aw86225, AW86225_REG_PWMCFG3,
|
|
AW86225_BIT_PWMCFG3_PRLVL_MASK, val);
|
|
}
|
|
else if (addr == 0x3f)
|
|
{
|
|
aw86225_i2c_write(aw86225, AW86225_REG_PWMCFG4, val);
|
|
}
|
|
}
|
|
|
|
static void aw86225_haptic_f0_calibration(FAR struct aw86225 *aw86225)
|
|
{
|
|
unsigned char reg_val = 0;
|
|
unsigned int f0_limit = 0;
|
|
char f0_cali_lra = 0;
|
|
int f0_cali_step = 0;
|
|
unsigned int f0_cali_min = aw86225->config->f0_ref *
|
|
(100 - aw86225->config->f0_cali_percent) / 100;
|
|
unsigned int f0_cali_max = aw86225->config->f0_ref *
|
|
(100 + aw86225->config->f0_cali_percent) / 100;
|
|
|
|
if (aw86225_haptic_cont_get_f0(aw86225))
|
|
{
|
|
ierr("%s get f0 error, user defafult f0\n", __func__);
|
|
}
|
|
else
|
|
{
|
|
/* max and min limit */
|
|
|
|
f0_limit = aw86225->f0;
|
|
iinfo("%s f0_ref = %d, f0_cali_min = %d, f0_cali_max = %d, f0 = %d\n",
|
|
__func__, aw86225->config->f0_ref,
|
|
f0_cali_min, f0_cali_max, aw86225->f0);
|
|
|
|
if ((aw86225->f0 < f0_cali_min) || aw86225->f0 > f0_cali_max)
|
|
{
|
|
ierr("f0 calibration out of range = %d!\n", aw86225->f0);
|
|
f0_limit = aw86225->config->f0_ref;
|
|
return;
|
|
}
|
|
|
|
/* calculate cali step */
|
|
|
|
f0_cali_step = 100000 * ((int)f0_limit -
|
|
(int)aw86225->config->f0_ref) / ((int)f0_limit * 24);
|
|
iinfo("%s f0_cali_step = %d\n", __func__, f0_cali_step);
|
|
if (f0_cali_step >= 0)
|
|
{
|
|
/* f0_cali_step >= 0 */
|
|
|
|
if (f0_cali_step % 10 >= 5)
|
|
{
|
|
f0_cali_step = 32 + (f0_cali_step / 10 + 1);
|
|
}
|
|
else
|
|
{
|
|
f0_cali_step = 32 + f0_cali_step / 10;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* f0_cali_step < 0 */
|
|
|
|
if (f0_cali_step % 10 <= -5)
|
|
{
|
|
f0_cali_step = 32 + (f0_cali_step / 10 - 1);
|
|
}
|
|
else
|
|
{
|
|
f0_cali_step = 32 + f0_cali_step / 10;
|
|
}
|
|
}
|
|
|
|
if (f0_cali_step > 31)
|
|
{
|
|
f0_cali_lra = (char)f0_cali_step - 32;
|
|
}
|
|
else
|
|
{
|
|
f0_cali_lra = (char)f0_cali_step + 32;
|
|
}
|
|
|
|
/* update cali step */
|
|
|
|
aw86225->f0_cali_data = (int)f0_cali_lra;
|
|
aw86225_haptic_upload_lra(aw86225, AW86225_F0_CALI);
|
|
aw86225_i2c_read(aw86225, AW86225_REG_TRIMCFG3, ®_val);
|
|
iinfo("%s final trim_lra=0x%02x\n", __func__, reg_val);
|
|
}
|
|
|
|
/* restore standby work mode */
|
|
|
|
aw86225_haptic_stop(aw86225);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: haptic cont
|
|
****************************************************************************/
|
|
|
|
static int aw86225_haptic_cont_config(FAR struct aw86225 *aw86225)
|
|
{
|
|
/* work mode */
|
|
|
|
aw86225_haptic_play_mode(aw86225, AW86225_HAPTIC_CONT_MODE);
|
|
aw86225_i2c_write_bits(aw86225, AW86225_REG_CONTCFG6,
|
|
AW86225_BIT_CONTCFG6_TRACK_EN_MASK,
|
|
AW86225_BIT_CONTCFG6_TRACK_ENABLE);
|
|
|
|
/* f0 driver level */
|
|
|
|
aw86225_i2c_write_bits(aw86225, AW86225_REG_CONTCFG6,
|
|
AW86225_BIT_CONTCFG6_DRV1_LVL_MASK,
|
|
aw86225->cont_drv1_lvl);
|
|
aw86225_i2c_write(aw86225, AW86225_REG_CONTCFG7,
|
|
aw86225->cont_drv2_lvl);
|
|
|
|
aw86225_i2c_write(aw86225, AW86225_REG_CONTCFG9, 0xff);
|
|
|
|
aw86225_haptic_play_go(aw86225, true);
|
|
return 0;
|
|
}
|
|
|
|
static int aw86225_haptic_activate(FAR struct aw86225 *aw86225)
|
|
{
|
|
aw86225_i2c_write_bits(aw86225, AW86225_REG_SYSCTRL2,
|
|
AW86225_BIT_SYSCTRL2_STANDBY_MASK,
|
|
AW86225_BIT_SYSCTRL2_STANDBY_OFF);
|
|
aw86225_interrupt_clear(aw86225);
|
|
aw86225_i2c_write_bits(aw86225, AW86225_REG_SYSINTM,
|
|
AW86225_BIT_SYSINTM_UVLM_MASK,
|
|
AW86225_BIT_SYSINTM_UVLM_ON);
|
|
return 0;
|
|
}
|
|
|
|
static int aw86225_haptic_start(FAR struct aw86225 *aw86225)
|
|
{
|
|
aw86225_haptic_activate(aw86225);
|
|
aw86225_haptic_play_go(aw86225, true);
|
|
return 0;
|
|
}
|
|
|
|
static int aw86225_haptic_play_effect_seq(FAR struct aw86225 *aw86225,
|
|
unsigned char flag)
|
|
{
|
|
if (aw86225->effect_id > aw86225->config->effect_id_boundary)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
iinfo("%s:effect_id =%d, activate_mode = %d\n",
|
|
__func__, aw86225->effect_id, aw86225->activate_mode);
|
|
if (flag)
|
|
{
|
|
if (aw86225->activate_mode == AW86225_HAPTIC_ACTIVATE_RAM_MODE)
|
|
{
|
|
if (aw86225->effect_id == 5 || aw86225->effect_id == 6)
|
|
{
|
|
aw86225->effect_id = 6;
|
|
}
|
|
else if (aw86225->effect_id == 1)
|
|
{
|
|
aw86225->effect_id = 3;
|
|
}
|
|
else if ((aw86225->effect_id >= 2) && (aw86225->effect_id <= 9))
|
|
{
|
|
aw86225->effect_id = 8;
|
|
}
|
|
|
|
aw86225_haptic_set_wav_seq(aw86225, 0x00,
|
|
(char)aw86225->effect_id + 1);
|
|
aw86225_haptic_set_pwm(aw86225, AW86225_PWM_12K);
|
|
aw86225_haptic_set_wav_seq(aw86225, 0x01, 0x00);
|
|
aw86225_haptic_set_wav_loop(aw86225, 0x00, 0x00);
|
|
aw86225_haptic_play_mode(aw86225, AW86225_HAPTIC_RAM_MODE);
|
|
aw86225_haptic_effect_strength(aw86225);
|
|
aw86225_haptic_set_gain(aw86225, aw86225->level);
|
|
aw86225_haptic_start(aw86225);
|
|
}
|
|
|
|
if (aw86225->activate_mode == AW86225_HAPTIC_ACTIVATE_RAM_LOOP_MODE)
|
|
{
|
|
aw86225_haptic_set_repeat_wav_seq(aw86225,
|
|
(aw86225->config->effect_id_boundary + 1));
|
|
|
|
aw86225_i2c_write_bits(aw86225, AW86225_REG_SYSCTRL7,
|
|
AW86225_BIT_SYSCTRL7_GAIN_BYPASS_MASK,
|
|
AW86225_BIT_SYSCTRL7_GAIN_CHANGEABLE);
|
|
|
|
aw86225_haptic_set_pwm(aw86225, AW86225_PWM_12K);
|
|
aw86225_haptic_set_gain(aw86225, aw86225->level);
|
|
aw86225_haptic_play_repeat_seq(aw86225, true);
|
|
}
|
|
}
|
|
|
|
iinfo("%s: exit\n", __func__);
|
|
return 0;
|
|
}
|
|
|
|
static void aw86225_haptic_misc_para_init(FAR struct aw86225 *aw86225)
|
|
{
|
|
aw86225->cont_drv1_lvl = aw86225->config->cont_drv1_lvl_dt;
|
|
aw86225->cont_drv2_lvl = aw86225->config->cont_drv2_lvl_dt;
|
|
aw86225->cont_drv1_time = aw86225->config->cont_drv1_time_dt;
|
|
aw86225->cont_drv2_time = aw86225->config->cont_drv2_time_dt;
|
|
aw86225->cont_brk_time = aw86225->config->cont_brk_time_dt;
|
|
aw86225->cont_wait_num = aw86225->config->cont_wait_num_dt;
|
|
|
|
/* SIN_H */
|
|
|
|
aw86225_i2c_write(aw86225, AW86225_REG_SYSCTRL3,
|
|
aw86225->config->sine_array[0]);
|
|
|
|
/* SIN_L */
|
|
|
|
aw86225_i2c_write(aw86225, AW86225_REG_SYSCTRL4,
|
|
aw86225->config->sine_array[1]);
|
|
|
|
/* COS_H */
|
|
|
|
aw86225_i2c_write(aw86225, AW86225_REG_SYSCTRL5,
|
|
aw86225->config->sine_array[2]);
|
|
|
|
/* COS_L */
|
|
|
|
aw86225_i2c_write(aw86225, AW86225_REG_SYSCTRL6,
|
|
aw86225->config->sine_array[3]);
|
|
aw86225_i2c_write_bits(aw86225, AW86225_REG_TRGCFG8,
|
|
AW86225_BIT_TRGCFG8_TRG_TRIG1_MODE_MASK,
|
|
AW86225_BIT_TRGCFG8_TRIG1);
|
|
aw86225_i2c_write_bits(aw86225, AW86225_REG_ANACFG8,
|
|
AW86225_BIT_ANACFG8_TRTF_CTRL_HDRV_MASK,
|
|
AW86225_BIT_ANACFG8_TRTF_CTRL_HDRV);
|
|
|
|
/* d2s_gain */
|
|
|
|
if (!aw86225->config->d2s_gain)
|
|
{
|
|
iinfo("%s aw86225->config->d2s_gain = 0!\n", __func__);
|
|
}
|
|
else
|
|
{
|
|
aw86225_i2c_write_bits(aw86225, AW86225_REG_SYSCTRL7,
|
|
AW86225_BIT_SYSCTRL7_D2S_GAIN_MASK,
|
|
aw86225->config->d2s_gain);
|
|
}
|
|
|
|
/* cont_tset */
|
|
|
|
if (!aw86225->config->cont_tset)
|
|
{
|
|
iinfo("%s aw86225->config->cont_tset = 0!\n", __func__);
|
|
}
|
|
else
|
|
{
|
|
aw86225_i2c_write_bits(aw86225, AW86225_REG_CONTCFG13,
|
|
AW86225_BIT_CONTCFG13_TSET_MASK,
|
|
aw86225->config->cont_tset << 4);
|
|
}
|
|
|
|
/* cont_bemf_set */
|
|
|
|
if (!aw86225->config->cont_bemf_set)
|
|
{
|
|
iinfo("%s aw86225->config->cont_bemf_set = 0!\n", __func__);
|
|
}
|
|
else
|
|
{
|
|
aw86225_i2c_write_bits(aw86225, AW86225_REG_CONTCFG13,
|
|
AW86225_BIT_CONTCFG13_BEME_SET_MASK,
|
|
aw86225->config->cont_bemf_set);
|
|
}
|
|
|
|
/* cont_brk_time */
|
|
|
|
if (!aw86225->cont_brk_time)
|
|
{
|
|
iinfo("%s aw86225->cont_brk_time = 0!\n", __func__);
|
|
}
|
|
else
|
|
{
|
|
aw86225_i2c_write(aw86225, AW86225_REG_CONTCFG10,
|
|
aw86225->cont_brk_time);
|
|
}
|
|
|
|
/* cont_brk_gain */
|
|
|
|
if (!aw86225->config->cont_brk_gain)
|
|
{
|
|
iinfo("%s aw86225->config->cont_brk_gain = 0!\n", __func__);
|
|
}
|
|
else
|
|
{
|
|
aw86225_i2c_write_bits(aw86225, AW86225_REG_CONTCFG5,
|
|
AW86225_BIT_CONTCFG5_BRK_GAIN_MASK,
|
|
aw86225->config->cont_brk_gain);
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: offset calibration
|
|
****************************************************************************/
|
|
|
|
static int
|
|
aw86225_haptic_offset_calibration(FAR struct aw86225 *aw86225)
|
|
{
|
|
unsigned int cont = 2000;
|
|
unsigned char reg_val = 0;
|
|
|
|
aw86225_haptic_raminit(aw86225, true);
|
|
aw86225_i2c_write_bits(aw86225, AW86225_REG_DETCFG2,
|
|
AW86225_BIT_DETCFG2_DIAG_GO_MASK,
|
|
AW86225_BIT_DETCFG2_DIAG_GO_ON);
|
|
|
|
while (1)
|
|
{
|
|
aw86225_i2c_read(aw86225, AW86225_REG_DETCFG2, ®_val);
|
|
if ((reg_val & 0x01) == 0 || cont == 0)
|
|
{
|
|
break;
|
|
}
|
|
|
|
cont--;
|
|
}
|
|
|
|
if (cont == 0)
|
|
{
|
|
ierr("%s calibration offset failed!\n", __func__);
|
|
}
|
|
|
|
aw86225_haptic_raminit(aw86225, false);
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
aw86225_haptic_vbat_mode_config(FAR struct aw86225 *aw86225,
|
|
unsigned char flag)
|
|
{
|
|
if (flag == AW86225_HAPTIC_CONT_VBAT_HW_ADJUST_MODE)
|
|
{
|
|
aw86225_i2c_write_bits(aw86225, AW86225_REG_SYSCTRL1,
|
|
AW86225_BIT_SYSCTRL1_VBAT_MODE_MASK,
|
|
AW86225_BIT_SYSCTRL1_VBAT_MODE_HW);
|
|
}
|
|
else
|
|
{
|
|
aw86225_i2c_write_bits(aw86225, AW86225_REG_SYSCTRL1,
|
|
AW86225_BIT_SYSCTRL1_VBAT_MODE_MASK,
|
|
AW86225_BIT_SYSCTRL1_VBAT_MODE_SW);
|
|
}
|
|
}
|
|
|
|
static void aw86225_haptic_auto_bst_enable(FAR struct aw86225 *aw86225,
|
|
unsigned char flag)
|
|
{
|
|
if (flag)
|
|
{
|
|
aw86225_i2c_write_bits(aw86225, AW86225_REG_PLAYCFG3,
|
|
AW86225_BIT_PLAYCFG3_BRK_EN_MASK,
|
|
AW86225_BIT_PLAYCFG3_BRK_ENABLE);
|
|
}
|
|
else
|
|
{
|
|
aw86225_i2c_write_bits(aw86225, AW86225_REG_PLAYCFG3,
|
|
AW86225_BIT_PLAYCFG3_BRK_EN_MASK,
|
|
AW86225_BIT_PLAYCFG3_BRK_DISABLE);
|
|
}
|
|
}
|
|
|
|
static int aw86225_read_chipid(FAR struct aw86225 *aw86225)
|
|
{
|
|
int ret;
|
|
uint8_t devid = 1; /* set nonzero, because chipid is 0! */
|
|
|
|
ret = aw86225_i2c_read(aw86225, 0x00, &devid);
|
|
if (ret < 0 || devid != 0)
|
|
{
|
|
ierr("Wrong Device ID! %02x\n\n", devid);
|
|
ret = -ENODEV;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void aw86225_ram_work_routine(FAR void *arg)
|
|
{
|
|
struct aw86225_firmware rtp_file;
|
|
FAR struct aw86225 *aw86225 = arg;
|
|
|
|
aw86225->ram_init = 0;
|
|
aw86225->rtp_init = 0;
|
|
|
|
aw86225_ram_loaded(&rtp_file, aw86225);
|
|
}
|
|
|
|
static void aw86225_long_vibrate_work_routine(FAR void *arg)
|
|
{
|
|
FAR struct aw86225 *aw86225 = arg;
|
|
|
|
iinfo("effect_id = %d state=%d activate_mode = %d duration = %d\n",
|
|
aw86225->effect_id, aw86225->state,
|
|
aw86225->activate_mode, aw86225->duration);
|
|
|
|
nxmutex_lock(&aw86225->lock);
|
|
|
|
/* Enter standby mode */
|
|
|
|
aw86225_haptic_stop(aw86225);
|
|
aw86225_haptic_upload_lra(aw86225, AW86225_F0_CALI);
|
|
if (aw86225->state)
|
|
{
|
|
if (aw86225->activate_mode == AW86225_HAPTIC_ACTIVATE_RAM_MODE)
|
|
{
|
|
aw86225_haptic_ram_vbat_compensate(aw86225, false);
|
|
aw86225_haptic_play_effect_seq(aw86225, true);
|
|
}
|
|
else if (aw86225->activate_mode ==
|
|
AW86225_HAPTIC_ACTIVATE_RAM_LOOP_MODE)
|
|
{
|
|
aw86225_haptic_ram_vbat_compensate(aw86225, true);
|
|
aw86225_haptic_play_effect_seq(aw86225, true);
|
|
|
|
/* run ms timer */
|
|
|
|
wd_start(&aw86225->timer, MSEC2TICK(aw86225->duration),
|
|
aw86225_vibrator_timer_func, (wdparm_t)aw86225);
|
|
aw86225->wk_lock_flag = 1;
|
|
}
|
|
else if (aw86225->activate_mode == AW86225_HAPTIC_ACTIVATE_CONT_MODE)
|
|
{
|
|
aw86225_haptic_cont_config(aw86225);
|
|
|
|
/* run ms timer */
|
|
|
|
wd_start(&aw86225->timer, MSEC2TICK(aw86225->duration),
|
|
aw86225_vibrator_timer_func, (wdparm_t)aw86225);
|
|
}
|
|
else
|
|
{
|
|
ierr("%s: activate_mode error\n", __func__);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (aw86225->wk_lock_flag == 1)
|
|
{
|
|
aw86225->wk_lock_flag = 0;
|
|
}
|
|
}
|
|
|
|
nxmutex_unlock(&aw86225->lock);
|
|
}
|
|
|
|
static int aw86225_vibrator_init(FAR struct aw86225 *aw86225)
|
|
{
|
|
nxmutex_init(&aw86225->lock);
|
|
nxmutex_init(&aw86225->rtp_lock);
|
|
nxsem_init(&aw86225->wait_q, 0, 0);
|
|
nxsem_init(&aw86225->stop_wait_q, 0, 0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int aw86225_ram_work_init(FAR struct aw86225 *aw86225)
|
|
{
|
|
int ram_timer_val = 8000;
|
|
|
|
work_queue(HPWORK, &aw86225->ram_work,
|
|
aw86225_ram_work_routine, aw86225, MSEC2TICK(ram_timer_val));
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int aw86225_haptic_init(FAR struct aw86225 *aw86225)
|
|
{
|
|
int ret;
|
|
unsigned char i;
|
|
unsigned char reg_val = 0;
|
|
|
|
nxmutex_lock(&aw86225->lock);
|
|
|
|
/* haptic init */
|
|
|
|
aw86225->ram_state = 0;
|
|
aw86225->activate_mode = aw86225->config->mode;
|
|
ret = aw86225_i2c_read(aw86225, AW86225_REG_WAVCFG1, ®_val);
|
|
aw86225->index = reg_val & 0x7f;
|
|
ret = aw86225_i2c_read(aw86225, AW86225_REG_PLAYCFG2, ®_val);
|
|
aw86225->gain = reg_val & 0xff;
|
|
iinfo("%s aw86225->gain =0x%02X\n", __func__, aw86225->gain);
|
|
for (i = 0; i < AW86225_SEQUENCER_SIZE; i++)
|
|
{
|
|
ret = aw86225_i2c_read(aw86225, AW86225_REG_WAVCFG1 + i, ®_val);
|
|
aw86225->seq[i] = reg_val;
|
|
}
|
|
|
|
aw86225_haptic_play_mode(aw86225, AW86225_HAPTIC_STANDBY_MODE);
|
|
aw86225_haptic_set_pwm(aw86225, AW86225_PWM_12K);
|
|
|
|
/* misc value init */
|
|
|
|
aw86225_haptic_misc_para_init(aw86225);
|
|
|
|
/* set motor protect */
|
|
|
|
aw86225_haptic_swicth_motor_protect_config(aw86225, 0x00, 0x00);
|
|
aw86225_haptic_trig_config(aw86225);
|
|
aw86225_haptic_offset_calibration(aw86225);
|
|
|
|
/* config auto_brake */
|
|
|
|
aw86225_haptic_auto_bst_enable(aw86225,
|
|
aw86225->config->is_enabled_auto_bst);
|
|
|
|
/* vbat compensation */
|
|
|
|
aw86225_haptic_vbat_mode_config(aw86225,
|
|
AW86225_HAPTIC_CONT_VBAT_HW_ADJUST_MODE);
|
|
aw86225->ram_vbat_compensate = AW86225_HAPTIC_RAM_VBAT_COMP_ENABLE;
|
|
|
|
/* f0 calibration LRA trim source select register */
|
|
|
|
aw86225_i2c_write_bits(aw86225, AW86225_REG_TRIMCFG1,
|
|
AW86225_BIT_TRIMCFG1_RL_TRIM_SRC_MASK,
|
|
AW86225_BIT_TRIMCFG1_RL_TRIM_SRC_REG);
|
|
aw86225_haptic_upload_lra(aw86225, AW86225_WRITE_ZERO);
|
|
aw86225_haptic_f0_calibration(aw86225);
|
|
nxmutex_unlock(&aw86225->lock);
|
|
return ret;
|
|
}
|
|
|
|
static int aw86225_haptics_upload_effect(FAR struct ff_lowerhalf_s *lower,
|
|
FAR struct ff_effect *effect,
|
|
FAR struct ff_effect *old)
|
|
{
|
|
FAR struct aw86225 *aw86225 = (FAR struct aw86225 *)lower;
|
|
FAR struct aw86225_hap_play_info *play = &aw86225->play;
|
|
int16_t data[AW86225_CUSTOM_DATA_LEN];
|
|
sclock_t time_us;
|
|
int ret;
|
|
|
|
time_us = wd_gettime(&aw86225->timer);
|
|
usleep(time_us);
|
|
|
|
iinfo("%s: effect->type=0x%x,FF_CONSTANT=0x%x,FF_PERIODIC=0x%x\n",
|
|
__func__, effect->type, FF_CONSTANT, FF_PERIODIC);
|
|
|
|
aw86225->effect_type = effect->type;
|
|
nxmutex_lock(&aw86225->lock);
|
|
while (atomic_load(&aw86225->exit_in_rtp_loop))
|
|
{
|
|
iinfo("%s: goint to waiting rtp exit\n", __func__);
|
|
nxmutex_unlock(&aw86225->lock);
|
|
ret = nxsem_post(&aw86225->stop_wait_q);
|
|
iinfo("%s: wakeup \n", __func__);
|
|
if (ret == -ERESTART)
|
|
{
|
|
ierr("%s: wake up by signal return erro\n", __func__);
|
|
return ret;
|
|
}
|
|
|
|
nxmutex_lock(&aw86225->lock);
|
|
}
|
|
|
|
if (aw86225->effect_type == FF_CONSTANT)
|
|
{
|
|
/* cont mode set duration */
|
|
|
|
aw86225->duration = effect->replay.length;
|
|
aw86225->activate_mode = AW86225_HAPTIC_ACTIVATE_RAM_LOOP_MODE;
|
|
aw86225->effect_id = aw86225->config->effect_id_boundary;
|
|
}
|
|
else if (aw86225->effect_type == FF_PERIODIC)
|
|
{
|
|
if (aw86225->effects_count == 0)
|
|
{
|
|
nxmutex_unlock(&aw86225->lock);
|
|
return -EINVAL;
|
|
}
|
|
|
|
memcpy(data, effect->u.periodic.custom_data, sizeof(int16_t) * 3);
|
|
|
|
aw86225->effect_id = data[0];
|
|
if (aw86225->effect_id == 521)
|
|
{
|
|
aw86225->effect_id = 21;
|
|
}
|
|
|
|
iinfo("%s: aw86225->effect_id = %d \n", __func__, aw86225->effect_id);
|
|
play->vmax_mv = effect->u.periodic.magnitude;
|
|
|
|
if (aw86225->effect_id < 0 ||
|
|
aw86225->effect_id > aw86225->config->effect_max)
|
|
{
|
|
nxmutex_unlock(&aw86225->lock);
|
|
return 0;
|
|
}
|
|
|
|
if (aw86225->effect_id < aw86225->config->effect_id_boundary)
|
|
{
|
|
aw86225->activate_mode = AW86225_HAPTIC_ACTIVATE_RAM_MODE;
|
|
data[1] = aw86225->predefined[aw86225->effect_id].play_rate_us
|
|
/ 1000000;
|
|
data[2] = aw86225->predefined[aw86225->effect_id].play_rate_us
|
|
/ 1000;
|
|
iinfo("aw86225->predefined[aw86225->effect_id] \
|
|
.play_rate_us/1000 = %d\n", \
|
|
aw86225->predefined[aw86225->effect_id].play_rate_us
|
|
/ 1000);
|
|
}
|
|
|
|
if (aw86225->effect_id >= aw86225->config->effect_id_boundary)
|
|
{
|
|
aw86225->activate_mode = AW86225_HAPTIC_ACTIVATE_RTP_MODE;
|
|
iinfo("%s: aw86225->effect_id=%d , aw86225->activate_mode = %d\n",
|
|
__func__, aw86225->effect_id, aw86225->activate_mode);
|
|
int budry = aw86225->effect_id -
|
|
aw86225->config->effect_id_boundary;
|
|
data[1] = aw86225->config->rtp_time[budry] / 1000;
|
|
data[2] = aw86225->config->rtp_time[budry] % 1000;
|
|
iinfo("%s: data[1] = %d data[2] = %d, rtp_time %d\n", __func__,
|
|
data[1], data[2],
|
|
aw86225->config->rtp_time[budry]);
|
|
}
|
|
|
|
memcpy(effect->u.periodic.custom_data, data, sizeof(int16_t) * 3);
|
|
}
|
|
else
|
|
{
|
|
ierr("%s: unsupported effect type: %d\n", __func__, effect->type);
|
|
}
|
|
|
|
nxmutex_unlock(&aw86225->lock);
|
|
return 0;
|
|
}
|
|
|
|
static int aw86225_haptics_playback(struct ff_lowerhalf_s *lower,
|
|
int effect_id, int val)
|
|
{
|
|
FAR struct aw86225 *aw86225 = (FAR struct aw86225 *)lower;
|
|
|
|
iinfo("%s: aw86225->effect_id=%d , aw86225->activate_mode = %d\n",
|
|
__func__, aw86225->effect_id, aw86225->activate_mode);
|
|
|
|
/* for osc calibration */
|
|
|
|
if (val > 0)
|
|
{
|
|
aw86225->state = 1;
|
|
}
|
|
|
|
if (val <= 0)
|
|
{
|
|
aw86225->state = 0;
|
|
}
|
|
|
|
wd_cancel(&aw86225->timer);
|
|
if (aw86225->effect_type == FF_CONSTANT &&
|
|
aw86225->activate_mode == AW86225_HAPTIC_ACTIVATE_RAM_LOOP_MODE)
|
|
{
|
|
iinfo("%s: enter ram_loop_mode \n", __func__);
|
|
work_queue(HPWORK, &aw86225->long_vibrate_work,
|
|
aw86225_long_vibrate_work_routine, aw86225, 0);
|
|
}
|
|
else if (aw86225->effect_type == FF_PERIODIC &&
|
|
aw86225->activate_mode == AW86225_HAPTIC_ACTIVATE_RAM_MODE)
|
|
{
|
|
iinfo("%s: enter ram_mode\n", __func__);
|
|
work_queue(HPWORK, &aw86225->long_vibrate_work,
|
|
aw86225_long_vibrate_work_routine, aw86225, 0);
|
|
}
|
|
else if (aw86225->effect_type == FF_PERIODIC &&
|
|
aw86225->activate_mode == AW86225_HAPTIC_ACTIVATE_RTP_MODE)
|
|
{
|
|
iinfo("%s: enter rtp_mode\n", __func__);
|
|
work_queue(HPWORK, &aw86225->rtp_work,
|
|
aw86225_rtp_work_routine, aw86225, 0);
|
|
if (val == 0)
|
|
{
|
|
atomic_store(&aw86225->exit_in_rtp_loop, 1);
|
|
}
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
static int aw86225_haptics_erase(FAR struct ff_lowerhalf_s *lower,
|
|
int effect_id)
|
|
{
|
|
FAR struct aw86225 *aw86225 = (FAR struct aw86225 *)lower;
|
|
|
|
/* for osc calibration */
|
|
|
|
aw86225->effect_type = 0;
|
|
aw86225->duration = 0;
|
|
return OK;
|
|
}
|
|
|
|
static void aw86225_haptics_set_gain_work_routine(FAR void *arg)
|
|
{
|
|
FAR struct aw86225 *aw86225 = arg;
|
|
|
|
if (aw86225->new_gain >= 0x7fff)
|
|
{
|
|
aw86225->level = 0x80;
|
|
}
|
|
else if (aw86225->new_gain <= 0x3fff)
|
|
{
|
|
aw86225->level = 0x1e;
|
|
}
|
|
else
|
|
{
|
|
aw86225->level = (aw86225->new_gain - 16383) / 128;
|
|
}
|
|
|
|
if (aw86225->level < 0x1e)
|
|
{
|
|
aw86225->level = 0x1e;
|
|
}
|
|
|
|
iinfo("%s: set_gain queue work, new_gain = %x level = %x \n", __func__,
|
|
aw86225->new_gain, aw86225->level);
|
|
|
|
if (aw86225->ram_vbat_compensate == AW86225_HAPTIC_RAM_VBAT_COMP_ENABLE
|
|
&& aw86225->vbat)
|
|
{
|
|
unsigned char comp_level =
|
|
aw86225->level * AW86225_VBAT_REFER / aw86225->vbat;
|
|
if (comp_level > (128 * AW86225_VBAT_REFER / AW86225_VBAT_MIN))
|
|
{
|
|
comp_level = 128 * AW86225_VBAT_REFER / AW86225_VBAT_MIN;
|
|
iinfo("%s: comp level limit is %d ", __func__, comp_level);
|
|
}
|
|
|
|
iinfo("%s: enable vbat comp, level = %x comp level = %x", __func__,
|
|
aw86225->level, comp_level);
|
|
aw86225_i2c_write(aw86225, AW86225_REG_PLAYCFG2, comp_level);
|
|
}
|
|
else
|
|
{
|
|
iinfo("%s: disable compsensation, vbat=%d, vbat_min=%d, vbat_ref=%d",
|
|
__func__, aw86225->vbat, AW86225_VBAT_MIN, AW86225_VBAT_REFER);
|
|
aw86225_i2c_write(aw86225, AW86225_REG_PLAYCFG2, aw86225->level);
|
|
}
|
|
}
|
|
|
|
static void aw86225_haptics_set_gain(FAR struct ff_lowerhalf_s *lower,
|
|
uint16_t gain)
|
|
{
|
|
FAR struct aw86225 *aw86225 = (FAR struct aw86225 *)lower;
|
|
|
|
aw86225->new_gain = gain;
|
|
work_queue(HPWORK, &aw86225->set_gain_work,
|
|
aw86225_haptics_set_gain_work_routine, aw86225, 0);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Public Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: aw86225_initialize
|
|
*
|
|
* Description:
|
|
* aw86225 motor driver initialize
|
|
*
|
|
* Input Parameters:
|
|
* master - i2c master param
|
|
* ioedev - io dev pin set
|
|
* config - the board config param of aw86225
|
|
*
|
|
* Returned Value:
|
|
* Zero (OK) on success; a negated errno value on failure.
|
|
*
|
|
****************************************************************************/
|
|
|
|
int aw86225_initialize(FAR struct i2c_master_s *master,
|
|
FAR struct ioexpander_dev_s *ioedev,
|
|
FAR const struct aw86225_board_config *config)
|
|
{
|
|
FAR struct ff_lowerhalf_s *lower = NULL;
|
|
FAR struct aw86225 *aw86225;
|
|
int effect_count_max;
|
|
int ret;
|
|
|
|
DEBUGASSERT(master != NULL && ioedev != NULL && config != NULL);
|
|
|
|
aw86225 = kmm_zalloc(sizeof(struct aw86225));
|
|
if (NULL == aw86225)
|
|
{
|
|
ierr("Failed to allocate instance\n\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
aw86225->effects_count = config->effects_count;
|
|
aw86225->is_used_irq = config->is_used_irq;
|
|
aw86225->predefined = config->predefined;
|
|
aw86225->config = config->config;
|
|
aw86225->irq = config->irq;
|
|
aw86225->i2c = master;
|
|
aw86225->addr = config->addr;
|
|
aw86225->freq = config->freq;
|
|
aw86225->level = 0x80;
|
|
|
|
lower = &aw86225->lower;
|
|
lower->upload = aw86225_haptics_upload_effect;
|
|
lower->erase = aw86225_haptics_erase;
|
|
lower->playback = aw86225_haptics_playback;
|
|
lower->set_gain = aw86225_haptics_set_gain;
|
|
lower->set_autocenter = NULL;
|
|
lower->destroy = NULL;
|
|
|
|
/* Interrupt register. */
|
|
|
|
ret = IOEXP_SETDIRECTION(ioedev, config->rstpin, IOEXPANDER_DIRECTION_OUT);
|
|
if (ret < 0)
|
|
{
|
|
ierr("ioexpander set direction error: %d\n", ret);
|
|
goto err;
|
|
}
|
|
|
|
ret = IOEXP_WRITEPIN(ioedev, config->rstpin, 0);
|
|
if (ret < 0)
|
|
{
|
|
ierr("ioexpander set direction error: %d\n", ret);
|
|
goto err;
|
|
}
|
|
|
|
usleep(2000);
|
|
|
|
ret = IOEXP_WRITEPIN(ioedev, config->rstpin, 1);
|
|
if (ret < 0)
|
|
{
|
|
ierr("ioexpander gpio write failed: %d\n", ret);
|
|
goto err;
|
|
}
|
|
|
|
usleep(5000);
|
|
|
|
/* int pin set */
|
|
|
|
ret = IOEXP_SETDIRECTION(ioedev, config->intpin,
|
|
IOEXPANDER_DIRECTION_IN_PULLUP);
|
|
if (ret < 0)
|
|
{
|
|
ierr("ioexpander set direction error: %d\n", ret);
|
|
goto err;
|
|
}
|
|
|
|
ret = IOEXP_SETDIRECTION(ioedev, config->powerpin,
|
|
IOEXPANDER_DIRECTION_OUT);
|
|
if (ret < 0)
|
|
{
|
|
ierr("ioexpander set direction error: %d\n", ret);
|
|
goto err;
|
|
}
|
|
|
|
ret = IOEXP_WRITEPIN(ioedev, config->powerpin, true);
|
|
if (ret < 0)
|
|
{
|
|
ierr("Error: Failed to write pin: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
ret = aw86225_read_chipid(aw86225);
|
|
if (ret < 0)
|
|
{
|
|
ierr("Check chip id failed!\n\n");
|
|
goto err;
|
|
}
|
|
|
|
aw86225_vibrator_init(aw86225);
|
|
aw86225_haptic_init(aw86225);
|
|
aw86225_ram_work_init(aw86225);
|
|
|
|
__set_bit(FF_CUSTOM, lower->ffbit);
|
|
__set_bit(FF_GAIN, lower->ffbit);
|
|
__set_bit(FF_CONSTANT, lower->ffbit);
|
|
__set_bit(FF_PERIODIC, lower->ffbit);
|
|
|
|
if (aw86225->effects_count + 1 > FF_EFFECT_COUNT_MAX)
|
|
{
|
|
effect_count_max = aw86225->effects_count + 1;
|
|
}
|
|
else
|
|
{
|
|
effect_count_max = FF_EFFECT_COUNT_MAX;
|
|
}
|
|
|
|
ret = ff_register(lower, config->path, effect_count_max);
|
|
if (ret < 0)
|
|
{
|
|
ierr("Failed to register driver:%d\n", ret);
|
|
nxsem_destroy(&aw86225->wait_q);
|
|
nxsem_destroy(&aw86225->stop_wait_q);
|
|
work_cancel(HPWORK, &aw86225->ram_work);
|
|
goto err;
|
|
}
|
|
|
|
return ret;
|
|
|
|
err:
|
|
kmm_free(aw86225);
|
|
return ret;
|
|
}
|