/**************************************************************************** * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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; }