4779 lines
130 KiB
C
4779 lines
130 KiB
C
|
|
// SPDX-License-Identifier: GPL-2.0
|
||
|
|
/*
|
||
|
|
* Copyright (c) 2020 Awinic Inc.
|
||
|
|
*/
|
||
|
|
#include <linux/module.h>
|
||
|
|
#include <linux/kernel.h>
|
||
|
|
#include <linux/i2c.h>
|
||
|
|
#include <linux/of_gpio.h>
|
||
|
|
#include <linux/delay.h>
|
||
|
|
#include <linux/device.h>
|
||
|
|
#include <linux/firmware.h>
|
||
|
|
#include <linux/slab.h>
|
||
|
|
#include <linux/version.h>
|
||
|
|
#include <linux/interrupt.h>
|
||
|
|
#include <linux/debugfs.h>
|
||
|
|
#include <linux/miscdevice.h>
|
||
|
|
#include <linux/uaccess.h>
|
||
|
|
#include <linux/syscalls.h>
|
||
|
|
#include <linux/power_supply.h>
|
||
|
|
#include <linux/pm_qos.h>
|
||
|
|
#include <linux/proc_fs.h>
|
||
|
|
#ifdef CONFIG_PM_WAKELOCKS
|
||
|
|
#include <linux/pm_wakeup.h>
|
||
|
|
#else
|
||
|
|
#include <linux/wakelock.h>
|
||
|
|
#endif
|
||
|
|
#include <linux/jiffies.h>
|
||
|
|
#include <linux/vmalloc.h>
|
||
|
|
#include "aw8624.h"
|
||
|
|
#include "aw8624_reg.h"
|
||
|
|
#include "haptic.h"
|
||
|
|
/******************************************************
|
||
|
|
*
|
||
|
|
* Value
|
||
|
|
*
|
||
|
|
******************************************************/
|
||
|
|
static char *aw8624_ram_name = "aw8624_haptic.bin";
|
||
|
|
static char aw8624_rtp_name[][AW8624_RTP_NAME_MAX] = {
|
||
|
|
{"aw8624_osc_rtp_24K_5s.bin"},
|
||
|
|
{"aw8624_rtp.bin"},
|
||
|
|
{"aw8624_rtp_lighthouse.bin"},
|
||
|
|
{"aw8624_rtp_silk.bin"},
|
||
|
|
};
|
||
|
|
struct aw8624_dts_info aw8624_dts_data;
|
||
|
|
struct pm_qos_request aw8624_pm_qos_req_vb;
|
||
|
|
|
||
|
|
/******************************************************
|
||
|
|
*
|
||
|
|
* functions
|
||
|
|
*
|
||
|
|
******************************************************/
|
||
|
|
static void aw8624_interrupt_clear(struct aw8624 *aw8624);
|
||
|
|
static void aw8624_haptic_upload_lra(struct aw8624 *aw8624, unsigned int flag);
|
||
|
|
static int aw8624_haptic_stop(struct aw8624 *aw8624);
|
||
|
|
static int aw8624_analyse_duration_range(struct aw8624 *aw8624);
|
||
|
|
|
||
|
|
/******************************************************
|
||
|
|
*
|
||
|
|
* aw8624 i2c write/read
|
||
|
|
*
|
||
|
|
******************************************************/
|
||
|
|
static int aw8624_i2c_write(struct aw8624 *aw8624,
|
||
|
|
unsigned char reg_addr, unsigned char reg_data)
|
||
|
|
{
|
||
|
|
int ret = -1;
|
||
|
|
unsigned char cnt = 0;
|
||
|
|
|
||
|
|
while (cnt < AW8624_I2C_RETRIES) {
|
||
|
|
ret =
|
||
|
|
i2c_smbus_write_byte_data(aw8624->i2c, reg_addr, reg_data);
|
||
|
|
if (ret < 0) {
|
||
|
|
aw_dev_err(aw8624->dev, "%s: i2c_write cnt=%d error=%d\n",
|
||
|
|
__func__, cnt, ret);
|
||
|
|
} else {
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
cnt++;
|
||
|
|
usleep_range(2000, 3000);
|
||
|
|
}
|
||
|
|
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
int aw8624_i2c_read(struct aw8624 *aw8624,
|
||
|
|
unsigned char reg_addr, unsigned char *reg_data)
|
||
|
|
{
|
||
|
|
int ret = -1;
|
||
|
|
unsigned char cnt = 0;
|
||
|
|
|
||
|
|
while (cnt < AW8624_I2C_RETRIES) {
|
||
|
|
ret = i2c_smbus_read_byte_data(aw8624->i2c, reg_addr);
|
||
|
|
if (ret < 0) {
|
||
|
|
aw_dev_err(aw8624->dev, "%s: i2c_read cnt=%d error=%d\n",
|
||
|
|
__func__, cnt, ret);
|
||
|
|
} else {
|
||
|
|
*reg_data = ret;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
cnt++;
|
||
|
|
usleep_range(2000, 3000);
|
||
|
|
}
|
||
|
|
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
int aw8624_i2c_write_bits(struct aw8624 *aw8624,
|
||
|
|
unsigned char reg_addr, unsigned int mask, unsigned char reg_data)
|
||
|
|
{
|
||
|
|
unsigned char reg_val = 0;
|
||
|
|
|
||
|
|
aw8624_i2c_read(aw8624, reg_addr, ®_val);
|
||
|
|
reg_val &= mask;
|
||
|
|
reg_val |= reg_data;
|
||
|
|
aw8624_i2c_write(aw8624, reg_addr, reg_val);
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
int aw8624_i2c_writes(struct aw8624 *aw8624,
|
||
|
|
unsigned char reg_addr, unsigned char *buf, unsigned int len)
|
||
|
|
{
|
||
|
|
int ret = -1;
|
||
|
|
unsigned char *data;
|
||
|
|
|
||
|
|
data = kmalloc(len+1, GFP_KERNEL);
|
||
|
|
if (data == NULL)
|
||
|
|
return -ENOMEM;
|
||
|
|
|
||
|
|
data[0] = reg_addr;
|
||
|
|
memcpy(&data[1], buf, len);
|
||
|
|
|
||
|
|
ret = i2c_master_send(aw8624->i2c, data, len+1);
|
||
|
|
if (ret < 0)
|
||
|
|
aw_dev_err(aw8624->dev,
|
||
|
|
"%s: i2c master send error\n", __func__);
|
||
|
|
|
||
|
|
kfree(data);
|
||
|
|
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
static void aw8624_interrupt_clear(struct aw8624 *aw8624)
|
||
|
|
{
|
||
|
|
unsigned char reg_val = 0;
|
||
|
|
|
||
|
|
aw8624_i2c_read(aw8624, AW8624_REG_SYSINT, ®_val);
|
||
|
|
aw_dev_info(aw8624->dev, "%s: reg SYSINT=0x%x\n", __func__, reg_val);
|
||
|
|
}
|
||
|
|
|
||
|
|
/*****************************************************
|
||
|
|
*
|
||
|
|
* ram update
|
||
|
|
*
|
||
|
|
*****************************************************/
|
||
|
|
static void aw8624_rtp_loaded(const struct firmware *cont, void *context)
|
||
|
|
{
|
||
|
|
struct aw8624 *aw8624 = context;
|
||
|
|
|
||
|
|
if (!cont) {
|
||
|
|
aw_dev_err(aw8624->dev, "%s: failed to read %s\n",
|
||
|
|
__func__, aw8624_rtp_name[aw8624->rtp_file_num]);
|
||
|
|
release_firmware(cont);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
aw_dev_info(aw8624->dev, "%s: loaded %s - size: %zu\n", __func__,
|
||
|
|
aw8624_rtp_name[aw8624->rtp_file_num], cont ? cont->size : 0);
|
||
|
|
|
||
|
|
/* aw8624 rtp update */
|
||
|
|
aw8624->rtp_container = vmalloc(cont->size+sizeof(int));
|
||
|
|
if (!aw8624->rtp_container) {
|
||
|
|
release_firmware(cont);
|
||
|
|
aw_dev_err(aw8624->dev,
|
||
|
|
"%s: Error allocating memory\n", __func__);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
aw8624->rtp_container->len = cont->size;
|
||
|
|
aw_dev_info(aw8624->dev,
|
||
|
|
"%s: rtp size = %d\n", __func__, aw8624->rtp_container->len);
|
||
|
|
memcpy(aw8624->rtp_container->data, cont->data, cont->size);
|
||
|
|
release_firmware(cont);
|
||
|
|
|
||
|
|
aw8624->rtp_init = 1;
|
||
|
|
aw_dev_info(aw8624->dev, "%s: rtp update complete\n", __func__);
|
||
|
|
}
|
||
|
|
|
||
|
|
static int aw8624_rtp_update(struct aw8624 *aw8624)
|
||
|
|
{
|
||
|
|
aw_dev_info(aw8624->dev, "%s enter\n", __func__);
|
||
|
|
|
||
|
|
return request_firmware_nowait(THIS_MODULE,
|
||
|
|
FW_ACTION_HOTPLUG,
|
||
|
|
aw8624_rtp_name[aw8624->rtp_file_num],
|
||
|
|
aw8624->dev,
|
||
|
|
GFP_KERNEL,
|
||
|
|
aw8624,
|
||
|
|
aw8624_rtp_loaded);
|
||
|
|
}
|
||
|
|
|
||
|
|
static int aw8624_haptic_juge_RTP_is_going_on(struct aw8624 *aw8624)
|
||
|
|
{
|
||
|
|
unsigned char rtp_state = 0;
|
||
|
|
unsigned char mode = 0;
|
||
|
|
unsigned char glb_st = 0;
|
||
|
|
|
||
|
|
aw8624_i2c_read(aw8624, AW8624_REG_SYSCTRL, &mode);
|
||
|
|
aw8624_i2c_read(aw8624, AW8624_REG_GLB_STATE, &glb_st);
|
||
|
|
if ((mode & AW8624_BIT_SYSCTRL_PLAY_MODE_RTP) &&
|
||
|
|
(glb_st == AW8624_BIT_GLBRD5_STATE_RTP_GO)) {
|
||
|
|
rtp_state = 1;
|
||
|
|
}
|
||
|
|
return rtp_state;
|
||
|
|
}
|
||
|
|
|
||
|
|
static void aw8624_haptic_raminit(struct aw8624 *aw8624, bool flag)
|
||
|
|
{
|
||
|
|
if (flag) {
|
||
|
|
aw8624_i2c_write_bits(aw8624, AW8624_REG_SYSCTRL,
|
||
|
|
AW8624_BIT_SYSCTRL_RAMINIT_MASK,
|
||
|
|
AW8624_BIT_SYSCTRL_RAMINIT_EN);
|
||
|
|
} else {
|
||
|
|
aw8624_i2c_write_bits(aw8624, AW8624_REG_SYSCTRL,
|
||
|
|
AW8624_BIT_SYSCTRL_RAMINIT_MASK,
|
||
|
|
AW8624_BIT_SYSCTRL_RAMINIT_OFF);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
static int aw8624_container_update(struct aw8624 *aw8624,
|
||
|
|
struct aw8624_container *aw8624_cont)
|
||
|
|
{
|
||
|
|
unsigned int shift = 0;
|
||
|
|
int i = 0;
|
||
|
|
int ret = 0;
|
||
|
|
#ifdef AW_CHECK_RAM_DATA
|
||
|
|
unsigned short check_sum = 0;
|
||
|
|
unsigned char reg_val = 0;
|
||
|
|
#endif
|
||
|
|
mutex_lock(&aw8624->lock);
|
||
|
|
|
||
|
|
aw8624->ram.baseaddr_shift = 2;
|
||
|
|
aw8624->ram.ram_shift = 4;
|
||
|
|
|
||
|
|
/* RAMINIT Enable */
|
||
|
|
aw8624_haptic_raminit(aw8624, true);
|
||
|
|
|
||
|
|
/* base addr */
|
||
|
|
shift = aw8624->ram.baseaddr_shift;
|
||
|
|
aw8624->ram.base_addr = (unsigned int)((aw8624_cont->data[0+shift]<<8) |
|
||
|
|
(aw8624_cont->data[1+shift]));
|
||
|
|
aw_dev_info(aw8624->dev,
|
||
|
|
"%s: base_addr=0x%4x\n", __func__,
|
||
|
|
aw8624->ram.base_addr);
|
||
|
|
|
||
|
|
aw8624_i2c_write(aw8624,
|
||
|
|
AW8624_REG_BASE_ADDRH,
|
||
|
|
aw8624_cont->data[0+shift]);
|
||
|
|
aw8624_i2c_write(aw8624,
|
||
|
|
AW8624_REG_BASE_ADDRL,
|
||
|
|
aw8624_cont->data[1+shift]);
|
||
|
|
|
||
|
|
aw8624_i2c_write(aw8624,
|
||
|
|
AW8624_REG_FIFO_AEH,
|
||
|
|
(unsigned char)((aw8624->ram.base_addr>>2)>>8));
|
||
|
|
aw8624_i2c_write(aw8624,
|
||
|
|
AW8624_REG_FIFO_AEL,
|
||
|
|
(unsigned char)((aw8624->ram.base_addr>>2)&0x00FF));
|
||
|
|
aw8624_i2c_write(aw8624,
|
||
|
|
AW8624_REG_FIFO_AFH,
|
||
|
|
(unsigned char)((aw8624->ram.base_addr
|
||
|
|
- (aw8624->ram.base_addr>>2))>>8));
|
||
|
|
aw8624_i2c_write(aw8624,
|
||
|
|
AW8624_REG_FIFO_AFL,
|
||
|
|
(unsigned char)((aw8624->ram.base_addr
|
||
|
|
-(aw8624->ram.base_addr>>2))&0x00FF));
|
||
|
|
|
||
|
|
/* ram */
|
||
|
|
shift = aw8624->ram.baseaddr_shift;
|
||
|
|
aw8624_i2c_write(aw8624,
|
||
|
|
AW8624_REG_RAMADDRH, aw8624_cont->data[0+shift]);
|
||
|
|
aw8624_i2c_write(aw8624,
|
||
|
|
AW8624_REG_RAMADDRL, aw8624_cont->data[1+shift]);
|
||
|
|
shift = aw8624->ram.ram_shift;
|
||
|
|
for (i = shift; i < aw8624_cont->len; i++) {
|
||
|
|
aw8624_i2c_write(aw8624,
|
||
|
|
AW8624_REG_RAMDATA, aw8624_cont->data[i]);
|
||
|
|
}
|
||
|
|
|
||
|
|
#ifdef AW_CHECK_RAM_DATA
|
||
|
|
shift = aw8624->ram.baseaddr_shift;
|
||
|
|
aw8624_i2c_write_bits(aw8624, AW8624_REG_RAMADDRH,
|
||
|
|
AW8624_BIT_RAMADDRH_MASK,
|
||
|
|
aw8624_cont->data[0 + shift]);
|
||
|
|
aw8624_i2c_write(aw8624, AW8624_REG_RAMADDRL,
|
||
|
|
aw8624_cont->data[1 + shift]);
|
||
|
|
shift = aw8624->ram.ram_shift;
|
||
|
|
for (i = shift; i < aw8624_cont->len; i++) {
|
||
|
|
aw8624_i2c_read(aw8624, AW8624_REG_RAMDATA, ®_val);
|
||
|
|
/*
|
||
|
|
*aw_dev_info(aw8624->dev,
|
||
|
|
* "%s aw8624_cont->data=0x%02X, ramdata=0x%02X\n",
|
||
|
|
* __func__, aw8624_cont->data[i], reg_val);
|
||
|
|
*/
|
||
|
|
if (reg_val != aw8624_cont->data[i]) {
|
||
|
|
aw_dev_err(aw8624->dev,
|
||
|
|
"%s: ram check error addr=0x%04x, file_data=0x%02X, ram_data=0x%02X\n",
|
||
|
|
__func__, i, aw8624_cont->data[i], reg_val);
|
||
|
|
ret = -ERANGE;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
check_sum += reg_val;
|
||
|
|
}
|
||
|
|
if (!ret) {
|
||
|
|
aw8624_i2c_read(aw8624, AW8624_REG_BASE_ADDRH, ®_val);
|
||
|
|
check_sum += reg_val;
|
||
|
|
aw8624_i2c_read(aw8624, AW8624_REG_BASE_ADDRL, ®_val);
|
||
|
|
check_sum += reg_val;
|
||
|
|
|
||
|
|
if (check_sum != aw8624->ram.check_sum) {
|
||
|
|
aw_dev_err(aw8624->dev, "%s: ram data check sum error, check_sum=0x%04x\n",
|
||
|
|
__func__, check_sum);
|
||
|
|
ret = -ERANGE;
|
||
|
|
} else {
|
||
|
|
aw_dev_info(aw8624->dev, "%s: ram data check sum pass, check_sum=0x%04x\n",
|
||
|
|
__func__, check_sum);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
#endif
|
||
|
|
/* RAMINIT Disable */
|
||
|
|
aw8624_haptic_raminit(aw8624, false);
|
||
|
|
|
||
|
|
mutex_unlock(&aw8624->lock);
|
||
|
|
aw_dev_info(aw8624->dev, "%s exit\n", __func__);
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int aw8624_haptic_get_ram_number(struct aw8624 *aw8624)
|
||
|
|
{
|
||
|
|
unsigned char i = 0;
|
||
|
|
unsigned char reg_val = 0;
|
||
|
|
unsigned char ram_data[3];
|
||
|
|
unsigned int first_wave_addr = 0;
|
||
|
|
|
||
|
|
aw_dev_info(aw8624->dev, "%s enter!\n", __func__);
|
||
|
|
if (!aw8624->ram_init) {
|
||
|
|
aw_dev_err(aw8624->dev,
|
||
|
|
"%s: ram init faild, ram_num = 0!\n",
|
||
|
|
__func__);
|
||
|
|
return -EPERM;
|
||
|
|
}
|
||
|
|
|
||
|
|
mutex_lock(&aw8624->lock);
|
||
|
|
/* RAMINIT Enable */
|
||
|
|
aw8624_haptic_raminit(aw8624, true);
|
||
|
|
aw8624_haptic_stop(aw8624);
|
||
|
|
aw8624_i2c_write(aw8624, AW8624_REG_RAMADDRH,
|
||
|
|
(unsigned char)(aw8624->ram.base_addr >> 8));
|
||
|
|
aw8624_i2c_write(aw8624, AW8624_REG_RAMADDRL,
|
||
|
|
(unsigned char)(aw8624->ram.base_addr & 0x00ff));
|
||
|
|
for (i = 0; i < 3; i++) {
|
||
|
|
aw8624_i2c_read(aw8624, AW8624_REG_RAMDATA, ®_val);
|
||
|
|
ram_data[i] = reg_val;
|
||
|
|
}
|
||
|
|
first_wave_addr = (ram_data[1] << 8 | ram_data[2]);
|
||
|
|
aw8624->ram.ram_num =
|
||
|
|
(first_wave_addr - aw8624->ram.base_addr - 1) / 4;
|
||
|
|
aw_dev_info(aw8624->dev,
|
||
|
|
"%s: ram_version = 0x%02x\n", __func__, ram_data[0]);
|
||
|
|
aw_dev_info(aw8624->dev,
|
||
|
|
"%s: first waveform addr = 0x%04x\n",
|
||
|
|
__func__, first_wave_addr);
|
||
|
|
aw_dev_info(aw8624->dev,
|
||
|
|
"%s: ram_num = %d\n", __func__, aw8624->ram.ram_num);
|
||
|
|
/* RAMINIT Disable */
|
||
|
|
aw8624_haptic_raminit(aw8624, false);
|
||
|
|
mutex_unlock(&aw8624->lock);
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static void aw8624_ram_loaded(const struct firmware *cont, void *context)
|
||
|
|
{
|
||
|
|
struct aw8624 *aw8624 = context;
|
||
|
|
struct aw8624_container *aw8624_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
|
||
|
|
aw_dev_info(aw8624->dev, "%s enter\n", __func__);
|
||
|
|
if (!cont) {
|
||
|
|
aw_dev_err(aw8624->dev,
|
||
|
|
"%s: failed to read %s\n",
|
||
|
|
__func__, aw8624_ram_name);
|
||
|
|
release_firmware(cont);
|
||
|
|
#ifdef AW_READ_BIN_FLEXBALLY
|
||
|
|
if (load_cont <= 20) {
|
||
|
|
schedule_delayed_work(&aw8624->ram_work,
|
||
|
|
msecs_to_jiffies(ram_timer_val));
|
||
|
|
aw_dev_info(aw8624->dev, "%s:start hrtimer: load_cont=%d\n",
|
||
|
|
__func__, load_cont);
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
aw_dev_info(aw8624->dev,
|
||
|
|
"%s: loaded %s - size: %zu\n", __func__, aw8624_ram_name,
|
||
|
|
cont ? cont->size : 0);
|
||
|
|
|
||
|
|
/* 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]))) {
|
||
|
|
aw_dev_info(aw8624->dev,
|
||
|
|
"%s: check sum pass : 0x%04x\n", __func__, check_sum);
|
||
|
|
aw8624->ram.check_sum = check_sum;
|
||
|
|
} else {
|
||
|
|
aw_dev_err(aw8624->dev, "%s: check sum err: check_sum=0x%04x\n",
|
||
|
|
__func__, check_sum);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* aw8624 ram update */
|
||
|
|
aw8624_fw = kzalloc(cont->size+sizeof(int), GFP_KERNEL);
|
||
|
|
if (!aw8624_fw) {
|
||
|
|
release_firmware(cont);
|
||
|
|
aw_dev_err(aw8624->dev,
|
||
|
|
"%s: Error allocating memory\n", __func__);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
aw8624_fw->len = cont->size;
|
||
|
|
memcpy(aw8624_fw->data, cont->data, cont->size);
|
||
|
|
release_firmware(cont);
|
||
|
|
|
||
|
|
ret = aw8624_container_update(aw8624, aw8624_fw);
|
||
|
|
if (ret) {
|
||
|
|
kfree(aw8624_fw);
|
||
|
|
aw8624->ram.len = 0;
|
||
|
|
aw_dev_err(aw8624->dev, "%s: ram firmware update failed!\n",
|
||
|
|
__func__);
|
||
|
|
} else {
|
||
|
|
aw8624->ram_init = 1;
|
||
|
|
aw8624->ram.len = aw8624_fw->len;
|
||
|
|
kfree(aw8624_fw);
|
||
|
|
aw_dev_info(aw8624->dev,
|
||
|
|
"%s: ram firmware update complete\n", __func__);
|
||
|
|
}
|
||
|
|
aw8624_haptic_get_ram_number(aw8624);
|
||
|
|
if (aw8624->IsUsedIRQ)
|
||
|
|
aw8624_rtp_update(aw8624);
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
static int aw8624_ram_update(struct aw8624 *aw8624)
|
||
|
|
{
|
||
|
|
aw8624->ram_init = 0;
|
||
|
|
aw8624->rtp_init = 0;
|
||
|
|
return request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG,
|
||
|
|
aw8624_ram_name, aw8624->dev, GFP_KERNEL,
|
||
|
|
aw8624, aw8624_ram_loaded);
|
||
|
|
}
|
||
|
|
|
||
|
|
static void aw8624_ram_work_routine(struct work_struct *work)
|
||
|
|
{
|
||
|
|
struct aw8624 *aw8624 =
|
||
|
|
container_of(work, struct aw8624, ram_work.work);
|
||
|
|
|
||
|
|
aw8624_ram_update(aw8624);
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
int aw8624_ram_init(struct aw8624 *aw8624)
|
||
|
|
{
|
||
|
|
int ram_timer_val = 8000;
|
||
|
|
|
||
|
|
INIT_DELAYED_WORK(&aw8624->ram_work, aw8624_ram_work_routine);
|
||
|
|
schedule_delayed_work(&aw8624->ram_work,
|
||
|
|
msecs_to_jiffies(ram_timer_val));
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
/*****************************************************
|
||
|
|
*
|
||
|
|
* haptic control
|
||
|
|
*
|
||
|
|
*****************************************************/
|
||
|
|
|
||
|
|
static int aw8624_haptic_play_init(struct aw8624 *aw8624)
|
||
|
|
{
|
||
|
|
if (aw8624->play_mode == AW8624_HAPTIC_CONT_MODE) {
|
||
|
|
aw8624_i2c_write(aw8624,
|
||
|
|
AW8624_REG_SW_BRAKE,
|
||
|
|
(unsigned char)(aw8624_dts_data.aw8624_sw_brake[0]));
|
||
|
|
} else {
|
||
|
|
aw8624_i2c_write(aw8624,
|
||
|
|
AW8624_REG_SW_BRAKE,
|
||
|
|
(unsigned char)(aw8624_dts_data.aw8624_sw_brake[1]));
|
||
|
|
}
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int aw8624_haptic_active(struct aw8624 *aw8624)
|
||
|
|
{
|
||
|
|
aw8624_haptic_play_init(aw8624);
|
||
|
|
aw8624_i2c_write_bits(aw8624,
|
||
|
|
AW8624_REG_SYSCTRL,
|
||
|
|
AW8624_BIT_SYSCTRL_WORK_MODE_MASK,
|
||
|
|
AW8624_BIT_SYSCTRL_ACTIVE);
|
||
|
|
aw8624_interrupt_clear(aw8624);
|
||
|
|
aw8624_i2c_write_bits(aw8624,
|
||
|
|
AW8624_REG_SYSINTM,
|
||
|
|
AW8624_BIT_SYSINTM_UVLO_MASK,
|
||
|
|
AW8624_BIT_SYSINTM_UVLO_EN);
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int aw8624_haptic_play_mode(struct aw8624 *aw8624,
|
||
|
|
unsigned char play_mode)
|
||
|
|
{
|
||
|
|
switch (play_mode) {
|
||
|
|
case AW8624_HAPTIC_STANDBY_MODE:
|
||
|
|
aw_dev_info(aw8624->dev, "%s: enter standby mode\n", __func__);
|
||
|
|
aw8624->play_mode = AW8624_HAPTIC_STANDBY_MODE;
|
||
|
|
aw8624_i2c_write_bits(aw8624, AW8624_REG_SYSINTM,
|
||
|
|
AW8624_BIT_SYSINTM_UVLO_MASK,
|
||
|
|
AW8624_BIT_SYSINTM_UVLO_OFF);
|
||
|
|
aw8624_i2c_write_bits(aw8624,
|
||
|
|
AW8624_REG_SYSCTRL,
|
||
|
|
AW8624_BIT_SYSCTRL_WORK_MODE_MASK,
|
||
|
|
AW8624_BIT_SYSCTRL_STANDBY);
|
||
|
|
break;
|
||
|
|
case AW8624_HAPTIC_RAM_MODE:
|
||
|
|
aw_dev_info(aw8624->dev, "%s: enter ram mode\n", __func__);
|
||
|
|
aw8624->play_mode = AW8624_HAPTIC_RAM_MODE;
|
||
|
|
aw8624_i2c_write_bits(aw8624,
|
||
|
|
AW8624_REG_SYSCTRL,
|
||
|
|
AW8624_BIT_SYSCTRL_PLAY_MODE_MASK,
|
||
|
|
AW8624_BIT_SYSCTRL_PLAY_MODE_RAM);
|
||
|
|
aw8624_haptic_active(aw8624);
|
||
|
|
break;
|
||
|
|
case AW8624_HAPTIC_RAM_LOOP_MODE:
|
||
|
|
aw_dev_info(aw8624->dev, "%s: enter ram loop mode\n", __func__);
|
||
|
|
aw8624->play_mode = AW8624_HAPTIC_RAM_LOOP_MODE;
|
||
|
|
aw8624_i2c_write_bits(aw8624,
|
||
|
|
AW8624_REG_SYSCTRL,
|
||
|
|
AW8624_BIT_SYSCTRL_PLAY_MODE_MASK,
|
||
|
|
AW8624_BIT_SYSCTRL_PLAY_MODE_RAM);
|
||
|
|
aw8624_haptic_active(aw8624);
|
||
|
|
break;
|
||
|
|
case AW8624_HAPTIC_RTP_MODE:
|
||
|
|
aw_dev_info(aw8624->dev, "%s: enter rtp mode\n", __func__);
|
||
|
|
aw8624->play_mode = AW8624_HAPTIC_RTP_MODE;
|
||
|
|
aw8624_i2c_write_bits(aw8624,
|
||
|
|
AW8624_REG_SYSCTRL,
|
||
|
|
AW8624_BIT_SYSCTRL_PLAY_MODE_MASK,
|
||
|
|
AW8624_BIT_SYSCTRL_PLAY_MODE_RTP);
|
||
|
|
aw8624_haptic_active(aw8624);
|
||
|
|
break;
|
||
|
|
case AW8624_HAPTIC_TRIG_MODE:
|
||
|
|
aw_dev_info(aw8624->dev, "%s: enter trig mode\n", __func__);
|
||
|
|
aw8624->play_mode = AW8624_HAPTIC_TRIG_MODE;
|
||
|
|
aw8624_i2c_write_bits(aw8624,
|
||
|
|
AW8624_REG_SYSCTRL,
|
||
|
|
AW8624_BIT_SYSCTRL_PLAY_MODE_MASK,
|
||
|
|
AW8624_BIT_SYSCTRL_PLAY_MODE_RAM);
|
||
|
|
aw8624_haptic_active(aw8624);
|
||
|
|
break;
|
||
|
|
case AW8624_HAPTIC_CONT_MODE:
|
||
|
|
aw_dev_info(aw8624->dev, "%s: enter cont mode\n", __func__);
|
||
|
|
aw8624->play_mode = AW8624_HAPTIC_CONT_MODE;
|
||
|
|
aw8624_i2c_write_bits(aw8624,
|
||
|
|
AW8624_REG_SYSCTRL,
|
||
|
|
AW8624_BIT_SYSCTRL_PLAY_MODE_MASK,
|
||
|
|
AW8624_BIT_SYSCTRL_PLAY_MODE_CONT);
|
||
|
|
aw8624_haptic_active(aw8624);
|
||
|
|
break;
|
||
|
|
default:
|
||
|
|
dev_err(aw8624->dev, "%s: play mode %d err",
|
||
|
|
__func__, play_mode);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int aw8624_haptic_play_go(struct aw8624 *aw8624, bool flag)
|
||
|
|
{
|
||
|
|
aw_dev_dbg(aw8624->dev, "%s enter, flag = %d\n", __func__, flag);
|
||
|
|
if (!flag) {
|
||
|
|
#ifdef KERNEL_VERSION_49
|
||
|
|
do_gettimeofday(&aw8624->current_time);
|
||
|
|
aw8624->interval_us = (aw8624->current_time.tv_sec -
|
||
|
|
aw8624->pre_enter_time.tv_sec) * 1000000 +
|
||
|
|
(aw8624->current_time.tv_usec-aw8624->pre_enter_time.tv_usec);
|
||
|
|
#else
|
||
|
|
|
||
|
|
aw8624->current_time = ktime_get();
|
||
|
|
aw8624->interval_us = ktime_to_us(ktime_sub(aw8624->current_time,
|
||
|
|
aw8624->pre_enter_time));
|
||
|
|
#endif
|
||
|
|
|
||
|
|
|
||
|
|
if (aw8624->interval_us < 2000) {
|
||
|
|
aw_dev_info(aw8624->dev, "%s:aw8624->interval_us=%d\n",
|
||
|
|
__func__, aw8624->interval_us);
|
||
|
|
mdelay(2);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (flag == true) {
|
||
|
|
aw8624_i2c_write_bits(aw8624, AW8624_REG_GO,
|
||
|
|
AW8624_BIT_GO_MASK, AW8624_BIT_GO_ENABLE);
|
||
|
|
#ifdef KERNEL_VERSION_49
|
||
|
|
do_gettimeofday(&aw8624->pre_enter_time);
|
||
|
|
#else
|
||
|
|
aw8624->pre_enter_time = ktime_get();
|
||
|
|
#endif
|
||
|
|
|
||
|
|
} else {
|
||
|
|
aw8624_i2c_write_bits(aw8624, AW8624_REG_GO,
|
||
|
|
AW8624_BIT_GO_MASK, AW8624_BIT_GO_DISABLE);
|
||
|
|
}
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int aw8624_haptic_stop_delay(struct aw8624 *aw8624)
|
||
|
|
{
|
||
|
|
unsigned char reg_val = 0;
|
||
|
|
unsigned int cnt = 100;
|
||
|
|
|
||
|
|
while (cnt--) {
|
||
|
|
aw8624_i2c_read(aw8624, AW8624_REG_GLB_STATE, ®_val);
|
||
|
|
if ((reg_val&0x0f) == 0x00) {
|
||
|
|
aw_dev_info(aw8624->dev,
|
||
|
|
"%s enter standby, reg glb_state=0x%02x\n",
|
||
|
|
__func__, reg_val);
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
mdelay(2);
|
||
|
|
|
||
|
|
aw_dev_info(aw8624->dev,
|
||
|
|
"%s wait for standby, reg glb_state=0x%02x\n",
|
||
|
|
__func__, reg_val);
|
||
|
|
}
|
||
|
|
aw_dev_err(aw8624->dev,
|
||
|
|
"%s do not enter standby automatically\n", __func__);
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int aw8624_haptic_stop(struct aw8624 *aw8624)
|
||
|
|
{
|
||
|
|
aw_dev_info(aw8624->dev, "%s enter\n", __func__);
|
||
|
|
aw8624_haptic_play_go(aw8624, false);
|
||
|
|
aw8624_haptic_stop_delay(aw8624);
|
||
|
|
aw8624_haptic_play_mode(aw8624, AW8624_HAPTIC_STANDBY_MODE);
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int aw8624_haptic_start(struct aw8624 *aw8624)
|
||
|
|
{
|
||
|
|
aw8624_haptic_active(aw8624);
|
||
|
|
aw8624_haptic_play_go(aw8624, true);
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int aw8624_haptic_set_wav_seq(struct aw8624 *aw8624,
|
||
|
|
unsigned char wav, unsigned char seq)
|
||
|
|
{
|
||
|
|
aw8624_i2c_write(aw8624, AW8624_REG_WAVSEQ1+wav, seq);
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int aw8624_haptic_set_wav_loop(struct aw8624 *aw8624,
|
||
|
|
unsigned char wav, unsigned char loop)
|
||
|
|
{
|
||
|
|
unsigned char tmp = 0;
|
||
|
|
|
||
|
|
if (wav%2) {
|
||
|
|
tmp = loop<<0;
|
||
|
|
aw8624_i2c_write_bits(aw8624, AW8624_REG_WAVLOOP1+(wav/2),
|
||
|
|
AW8624_BIT_WAVLOOP_SEQNP1_MASK, tmp);
|
||
|
|
} else {
|
||
|
|
tmp = loop<<4;
|
||
|
|
aw8624_i2c_write_bits(aw8624, AW8624_REG_WAVLOOP1+(wav/2),
|
||
|
|
AW8624_BIT_WAVLOOP_SEQN_MASK, tmp);
|
||
|
|
}
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int
|
||
|
|
aw8624_haptic_set_repeat_wav_seq(struct aw8624 *aw8624, unsigned char seq)
|
||
|
|
{
|
||
|
|
aw8624_haptic_set_wav_seq(aw8624, 0x00, seq);
|
||
|
|
aw8624_haptic_set_wav_loop(aw8624,
|
||
|
|
0x00,
|
||
|
|
AW8624_BIT_WAVLOOP_INIFINITELY);
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int aw8624_haptic_set_gain(struct aw8624 *aw8624, unsigned char gain)
|
||
|
|
{
|
||
|
|
aw8624_i2c_write(aw8624, AW8624_REG_DATDBG, gain);
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int aw8624_haptic_set_pwm(struct aw8624 *aw8624, unsigned char mode)
|
||
|
|
{
|
||
|
|
aw8624->pwm_mode = mode;
|
||
|
|
|
||
|
|
switch (mode) {
|
||
|
|
case AW8624_PWM_48K:
|
||
|
|
aw8624_i2c_write_bits(aw8624,
|
||
|
|
AW8624_REG_PWMDBG,
|
||
|
|
AW8624_BIT_PWMDBG_PWM_MODE_MASK,
|
||
|
|
AW8624_BIT_PWMDBG_PWM_48K);
|
||
|
|
break;
|
||
|
|
case AW8624_PWM_24K:
|
||
|
|
aw8624_i2c_write_bits(aw8624,
|
||
|
|
AW8624_REG_PWMDBG,
|
||
|
|
AW8624_BIT_PWMDBG_PWM_MODE_MASK,
|
||
|
|
AW8624_BIT_PWMDBG_PWM_24K);
|
||
|
|
break;
|
||
|
|
case AW8624_PWM_12K:
|
||
|
|
aw8624_i2c_write_bits(aw8624,
|
||
|
|
AW8624_REG_PWMDBG,
|
||
|
|
AW8624_BIT_PWMDBG_PWM_MODE_MASK,
|
||
|
|
AW8624_BIT_PWMDBG_PWM_12K);
|
||
|
|
break;
|
||
|
|
default:
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int
|
||
|
|
aw8624_haptic_play_repeat_seq(struct aw8624 *aw8624, unsigned char flag)
|
||
|
|
{
|
||
|
|
if (flag) {
|
||
|
|
aw8624_haptic_play_mode(aw8624, AW8624_HAPTIC_RAM_LOOP_MODE);
|
||
|
|
aw8624_haptic_start(aw8624);
|
||
|
|
}
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int aw8624_haptic_play_wav_seq(struct aw8624 *aw8624,
|
||
|
|
unsigned char flag)
|
||
|
|
{
|
||
|
|
aw_dev_info(aw8624->dev, "%s enter\n", __func__);
|
||
|
|
if (flag) {
|
||
|
|
aw8624_haptic_play_mode(aw8624, AW8624_HAPTIC_RAM_MODE);
|
||
|
|
aw8624_haptic_start(aw8624);
|
||
|
|
}
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
static int aw8624_haptic_swicth_motorprotect_config(struct aw8624 *aw8624,
|
||
|
|
unsigned char addr, unsigned char val)
|
||
|
|
{
|
||
|
|
if (addr == 1) {
|
||
|
|
aw8624_i2c_write_bits(aw8624,
|
||
|
|
AW8624_REG_DETCTRL,
|
||
|
|
AW8624_BIT_DETCTRL_PROTECT_MASK,
|
||
|
|
AW8624_BIT_DETCTRL_PROTECT_SHUTDOWN);
|
||
|
|
aw8624_i2c_write_bits(aw8624,
|
||
|
|
AW8624_REG_PWMPRC,
|
||
|
|
AW8624_BIT_PWMPRC_PRC_EN_MASK,
|
||
|
|
AW8624_BIT_PWMPRC_PRC_ENABLE);
|
||
|
|
aw8624_i2c_write_bits(aw8624,
|
||
|
|
AW8624_REG_PRLVL,
|
||
|
|
AW8624_BIT_PRLVL_PR_EN_MASK,
|
||
|
|
AW8624_BIT_PRLVL_PR_ENABLE);
|
||
|
|
} else if (addr == 0) {
|
||
|
|
aw8624_i2c_write_bits(aw8624,
|
||
|
|
AW8624_REG_DETCTRL,
|
||
|
|
AW8624_BIT_DETCTRL_PROTECT_MASK,
|
||
|
|
AW8624_BIT_DETCTRL_PROTECT_NO_ACTION);
|
||
|
|
aw8624_i2c_write_bits(aw8624,
|
||
|
|
AW8624_REG_PWMPRC,
|
||
|
|
AW8624_BIT_PWMPRC_PRC_EN_MASK,
|
||
|
|
AW8624_BIT_PWMPRC_PRC_DISABLE);
|
||
|
|
aw8624_i2c_write_bits(aw8624,
|
||
|
|
AW8624_REG_PRLVL,
|
||
|
|
AW8624_BIT_PRLVL_PR_EN_MASK,
|
||
|
|
AW8624_BIT_PRLVL_PR_DISABLE);
|
||
|
|
} else if (addr == 0x2d) {
|
||
|
|
aw8624_i2c_write_bits(aw8624, AW8624_REG_PWMPRC,
|
||
|
|
AW8624_BIT_PWMPRC_PRCTIME_MASK, val);
|
||
|
|
} else if (addr == 0x3e) {
|
||
|
|
aw8624_i2c_write_bits(aw8624, AW8624_REG_PRLVL,
|
||
|
|
AW8624_BIT_PRLVL_PRLVL_MASK, val);
|
||
|
|
} else if (addr == 0x3f) {
|
||
|
|
aw8624_i2c_write_bits(aw8624, AW8624_REG_PRTIME,
|
||
|
|
AW8624_BIT_PRTIME_PRTIME_MASK, val);
|
||
|
|
} else {
|
||
|
|
/*nothing to do;*/
|
||
|
|
}
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int aw8624_haptic_ram_config(struct aw8624 *aw8624, int duration)
|
||
|
|
{
|
||
|
|
unsigned char wavseq = 0;
|
||
|
|
unsigned char wavloop = 0;
|
||
|
|
int ret = 0;
|
||
|
|
|
||
|
|
if (aw8624->duration_time_flag < 0) {
|
||
|
|
aw_dev_err(aw8624->dev,
|
||
|
|
"%s: duration time error, array size = %d\n",
|
||
|
|
__func__, aw8624->duration_time_size);
|
||
|
|
return -ERANGE;
|
||
|
|
}
|
||
|
|
ret = aw8624_analyse_duration_range(aw8624);
|
||
|
|
if (ret < 0)
|
||
|
|
return ret;
|
||
|
|
if ((duration > 0) && (duration <
|
||
|
|
aw8624_dts_data.aw8624_duration_time[0])) {
|
||
|
|
wavseq = 3; /*3*/
|
||
|
|
wavloop = 0;
|
||
|
|
} else if ((duration >= aw8624_dts_data.aw8624_duration_time[0]) &&
|
||
|
|
(duration < aw8624_dts_data.aw8624_duration_time[1])) {
|
||
|
|
wavseq = 2; /*2*/
|
||
|
|
wavloop = 0;
|
||
|
|
} else if ((duration >= aw8624_dts_data.aw8624_duration_time[1]) &&
|
||
|
|
(duration < aw8624_dts_data.aw8624_duration_time[2])) {
|
||
|
|
wavseq = 1; /*1*/
|
||
|
|
wavloop = 0;
|
||
|
|
} else if (duration >= aw8624_dts_data.aw8624_duration_time[2]) {
|
||
|
|
wavseq = 4; /*4*/
|
||
|
|
wavloop = 15; /*long vibration*/
|
||
|
|
} else {
|
||
|
|
wavseq = 0;
|
||
|
|
wavloop = 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
aw8624_haptic_set_wav_seq(aw8624, 0, wavseq);
|
||
|
|
aw8624_haptic_set_wav_loop(aw8624, 0, wavloop);
|
||
|
|
aw8624_haptic_set_wav_seq(aw8624, 1, 0);
|
||
|
|
aw8624_haptic_set_wav_loop(aw8624, 1, 0);
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int aw8624_haptic_select_pin(struct aw8624 *aw8624, unsigned char pin)
|
||
|
|
{
|
||
|
|
if (pin == TRIG1) {
|
||
|
|
aw8624_i2c_write_bits(aw8624,
|
||
|
|
AW8624_REG_DBGCTRL,
|
||
|
|
AW8624_BIT_DBGCTRL_INTN_TRG_SEL_MASK,
|
||
|
|
AW8624_BIT_DBGCTRL_TRG_SEL_ENABLE);
|
||
|
|
aw8624_i2c_write_bits(aw8624,
|
||
|
|
AW8624_REG_TRG_CFG2,
|
||
|
|
AW8624_BIT_TRGCFG2_TRG1_ENABLE_MASK,
|
||
|
|
AW8624_BIT_TRGCFG2_TRG1_ENABLE);
|
||
|
|
aw_dev_info(aw8624->dev, "%s: select TRIG1 pin\n", __func__);
|
||
|
|
} else if (pin == IRQ) {
|
||
|
|
aw8624_i2c_write_bits(aw8624,
|
||
|
|
AW8624_REG_DBGCTRL,
|
||
|
|
AW8624_BIT_DBGCTRL_INTN_TRG_SEL_MASK,
|
||
|
|
AW8624_BIT_DBGCTRL_INTN_SEL_ENABLE);
|
||
|
|
aw8624_i2c_write_bits(aw8624,
|
||
|
|
AW8624_REG_TRG_CFG2,
|
||
|
|
AW8624_BIT_TRGCFG2_TRG1_ENABLE_MASK,
|
||
|
|
AW8624_BIT_TRGCFG2_TRG1_DISABLE);
|
||
|
|
aw_dev_info(aw8624->dev, "%s: select INIT pin\n", __func__);
|
||
|
|
} else
|
||
|
|
aw_dev_err(aw8624->dev, "%s: There is no such option\n",
|
||
|
|
__func__);
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
static int aw8624_haptic_trig1_param_init(struct aw8624 *aw8624)
|
||
|
|
{
|
||
|
|
if (aw8624->IsUsedIRQ) {
|
||
|
|
aw8624_haptic_select_pin(aw8624, IRQ);
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
aw8624->trig.trig_enable = aw8624_dts_data.trig_config[0];
|
||
|
|
aw8624->trig.trig_edge = aw8624_dts_data.trig_config[1];
|
||
|
|
aw8624->trig.trig_polar = aw8624_dts_data.trig_config[2];
|
||
|
|
aw8624->trig.pos_sequence = aw8624_dts_data.trig_config[3];
|
||
|
|
aw8624->trig.neg_sequence = aw8624_dts_data.trig_config[4];
|
||
|
|
aw_dev_info(aw8624->dev, "%s: trig1 date init ok!\n", __func__);
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
static int aw8624_haptic_tirg1_param_config(struct aw8624 *aw8624)
|
||
|
|
{
|
||
|
|
if (aw8624->IsUsedIRQ) {
|
||
|
|
aw8624_haptic_select_pin(aw8624, IRQ);
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
if (aw8624->trig.trig_enable)
|
||
|
|
aw8624_haptic_select_pin(aw8624, TRIG1);
|
||
|
|
else
|
||
|
|
aw8624_haptic_select_pin(aw8624, IRQ);
|
||
|
|
|
||
|
|
aw8624_i2c_write_bits(aw8624, AW8624_REG_TRG_CFG1,
|
||
|
|
AW8624_BIT_TRGCFG1_TRG1_EDGE_MASK,
|
||
|
|
aw8624->trig.trig_edge);
|
||
|
|
aw8624_i2c_write_bits(aw8624, AW8624_REG_TRG_CFG1,
|
||
|
|
AW8624_BIT_TRGCFG1_TRG1_POLAR_MASK,
|
||
|
|
aw8624->trig.trig_polar << 1);
|
||
|
|
aw8624_i2c_write(aw8624, AW8624_REG_TRG1_SEQP,
|
||
|
|
aw8624->trig.pos_sequence);
|
||
|
|
aw8624_i2c_write(aw8624, AW8624_REG_TRG1_SEQN,
|
||
|
|
aw8624->trig.neg_sequence);
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
static int aw8624_haptic_vbat_mode(struct aw8624 *aw8624, unsigned char flag)
|
||
|
|
{
|
||
|
|
if (flag == AW8624_HAPTIC_VBAT_HW_COMP_MODE) {
|
||
|
|
aw8624_i2c_write_bits(aw8624,
|
||
|
|
AW8624_REG_ADCTEST,
|
||
|
|
AW8624_BIT_DETCTRL_VBAT_MODE_MASK,
|
||
|
|
AW8624_BIT_DETCTRL_VBAT_HW_COMP);
|
||
|
|
} else {
|
||
|
|
aw8624_i2c_write_bits(aw8624,
|
||
|
|
AW8624_REG_ADCTEST,
|
||
|
|
AW8624_BIT_DETCTRL_VBAT_MODE_MASK,
|
||
|
|
AW8624_BIT_DETCTRL_VBAT_SW_COMP);
|
||
|
|
}
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int aw8624_haptic_set_f0_preset(struct aw8624 *aw8624)
|
||
|
|
{
|
||
|
|
unsigned int f0_reg = 0;
|
||
|
|
|
||
|
|
f0_reg = 1000000000/(aw8624->f0_pre*aw8624_dts_data.aw8624_f0_coeff);
|
||
|
|
aw8624_i2c_write(aw8624,
|
||
|
|
AW8624_REG_F_PRE_H,
|
||
|
|
(unsigned char)((f0_reg>>8)&0xff));
|
||
|
|
aw8624_i2c_write(aw8624,
|
||
|
|
AW8624_REG_F_PRE_L,
|
||
|
|
(unsigned char)((f0_reg>>0)&0xff));
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int aw8624_haptic_read_f0(struct aw8624 *aw8624)
|
||
|
|
{
|
||
|
|
int ret = 0;
|
||
|
|
unsigned char reg_val = 0;
|
||
|
|
unsigned int f0_reg = 0;
|
||
|
|
unsigned long f0_tmp = 0;
|
||
|
|
|
||
|
|
ret = aw8624_i2c_read(aw8624, AW8624_REG_F_LRA_F0_H, ®_val);
|
||
|
|
f0_reg = (reg_val<<8);
|
||
|
|
ret = aw8624_i2c_read(aw8624, AW8624_REG_F_LRA_F0_L, ®_val);
|
||
|
|
f0_reg |= (reg_val<<0);
|
||
|
|
if (!f0_reg || !aw8624_dts_data.aw8624_f0_coeff) {
|
||
|
|
aw8624->f0 = 0;
|
||
|
|
aw_dev_info(aw8624->dev, "%s : get f0 failed with the value becoming 0!\n",
|
||
|
|
__func__);
|
||
|
|
return -EPERM;
|
||
|
|
}
|
||
|
|
|
||
|
|
f0_tmp = 1000000000 / (f0_reg * aw8624_dts_data.aw8624_f0_coeff);
|
||
|
|
aw8624->f0 = (unsigned int)f0_tmp;
|
||
|
|
aw_dev_info(aw8624->dev, "%s f0=%d\n", __func__, aw8624->f0);
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int aw8624_haptic_read_cont_f0(struct aw8624 *aw8624)
|
||
|
|
{
|
||
|
|
int ret = 0;
|
||
|
|
unsigned char reg_val = 0;
|
||
|
|
unsigned int f0_reg = 0;
|
||
|
|
unsigned long f0_tmp = 0;
|
||
|
|
|
||
|
|
ret = aw8624_i2c_read(aw8624, AW8624_REG_F_LRA_CONT_H, ®_val);
|
||
|
|
f0_reg = (reg_val<<8);
|
||
|
|
ret = aw8624_i2c_read(aw8624, AW8624_REG_F_LRA_CONT_L, ®_val);
|
||
|
|
f0_reg |= (reg_val<<0);
|
||
|
|
if (!f0_reg) {
|
||
|
|
aw8624->cont_f0 = 0;
|
||
|
|
aw_dev_info(aw8624->dev,
|
||
|
|
"%s: failed to reading cont f0 with 0\n", __func__);
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
f0_tmp = 1000000000/(f0_reg*aw8624_dts_data.aw8624_f0_coeff);
|
||
|
|
aw8624->cont_f0 = (unsigned int)f0_tmp;
|
||
|
|
aw_dev_info(aw8624->dev, "%s cont_f0=%d\n", __func__, aw8624->cont_f0);
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int aw8624_haptic_read_beme(struct aw8624 *aw8624)
|
||
|
|
{
|
||
|
|
int ret = 0;
|
||
|
|
unsigned char reg_val = 0;
|
||
|
|
|
||
|
|
ret = aw8624_i2c_read(aw8624, AW8624_REG_WAIT_VOL_MP, ®_val);
|
||
|
|
aw8624->max_pos_beme = (reg_val<<0);
|
||
|
|
ret = aw8624_i2c_read(aw8624, AW8624_REG_WAIT_VOL_MN, ®_val);
|
||
|
|
aw8624->max_neg_beme = (reg_val<<0);
|
||
|
|
|
||
|
|
aw_dev_info(aw8624->dev,
|
||
|
|
"%s max_pos_beme=%d\n", __func__, aw8624->max_pos_beme);
|
||
|
|
aw_dev_info(aw8624->dev,
|
||
|
|
"%s max_neg_beme=%d\n", __func__, aw8624->max_neg_beme);
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int aw8624_vbat_monitor_detector(struct aw8624 *aw8624)
|
||
|
|
{
|
||
|
|
unsigned char reg_val = 0;
|
||
|
|
unsigned int vbat = 0;
|
||
|
|
|
||
|
|
aw8624_haptic_stop(aw8624);
|
||
|
|
/*step 1:EN_RAMINIT*/
|
||
|
|
aw8624_haptic_raminit(aw8624, true);
|
||
|
|
|
||
|
|
/*step 2 :launch power supply testing */
|
||
|
|
aw8624_i2c_write_bits(aw8624,
|
||
|
|
AW8624_REG_DETCTRL,
|
||
|
|
AW8624_BIT_DETCTRL_VBAT_GO_MASK,
|
||
|
|
AW8624_BIT_DETCTRL_VABT_GO_ENABLE);
|
||
|
|
usleep_range(2000, 2500);
|
||
|
|
|
||
|
|
aw8624_i2c_read(aw8624, AW8624_REG_VBATDET, ®_val);
|
||
|
|
vbat = 6100 * reg_val / 256;
|
||
|
|
aw_dev_info(aw8624->dev, "%s get_vbat=%dmV\n",
|
||
|
|
__func__, vbat);
|
||
|
|
/*step 3: return val*/
|
||
|
|
aw8624_haptic_raminit(aw8624, false);
|
||
|
|
|
||
|
|
return vbat;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int aw8624_lra_resistance_detector(struct aw8624 *aw8624)
|
||
|
|
{
|
||
|
|
unsigned char reg_val = 0;
|
||
|
|
unsigned char reg_val_anactrl = 0;
|
||
|
|
unsigned char reg_val_d2scfg = 0;
|
||
|
|
unsigned int r_lra = 0;
|
||
|
|
|
||
|
|
mutex_lock(&aw8624->lock);
|
||
|
|
aw8624_i2c_read(aw8624, AW8624_REG_ANACTRL, ®_val_anactrl);
|
||
|
|
aw8624_i2c_read(aw8624, AW8624_REG_D2SCFG, ®_val_d2scfg);
|
||
|
|
aw8624_haptic_stop(aw8624);
|
||
|
|
aw8624_haptic_raminit(aw8624, true);
|
||
|
|
|
||
|
|
|
||
|
|
aw8624_i2c_write_bits(aw8624,
|
||
|
|
AW8624_REG_ANACTRL,
|
||
|
|
AW8624_BIT_ANACTRL_EN_IO_PD1_MASK,
|
||
|
|
AW8624_BIT_ANACTRL_EN_IO_PD1_HIGH);
|
||
|
|
|
||
|
|
aw8624_i2c_write_bits(aw8624,
|
||
|
|
AW8624_REG_D2SCFG,
|
||
|
|
AW8624_BIT_D2SCFG_CLK_ADC_MASK,
|
||
|
|
AW8624_BIT_D2SCFG_CLK_ASC_1P5MHZ);
|
||
|
|
|
||
|
|
aw8624_i2c_write_bits(aw8624,
|
||
|
|
AW8624_REG_DETCTRL,
|
||
|
|
AW8624_BIT_DETCTRL_RL_OS_MASK,
|
||
|
|
AW8624_BIT_DETCTRL_RL_DETECT);
|
||
|
|
aw8624_i2c_write_bits(aw8624,
|
||
|
|
AW8624_REG_DETCTRL,
|
||
|
|
AW8624_BIT_DETCTRL_DIAG_GO_MASK,
|
||
|
|
AW8624_BIT_DETCTRL_DIAG_GO_ENABLE);
|
||
|
|
usleep_range(3000, 3500);
|
||
|
|
aw8624_i2c_read(aw8624, AW8624_REG_RLDET, ®_val);
|
||
|
|
r_lra = 298 * reg_val;
|
||
|
|
/*len += snprintf(buf+len, PAGE_SIZE-len, "r_lra=%dmohm\n", r_lra);*/
|
||
|
|
|
||
|
|
aw8624_i2c_write(aw8624, AW8624_REG_D2SCFG, reg_val_d2scfg);
|
||
|
|
aw8624_i2c_write(aw8624, AW8624_REG_ANACTRL, reg_val_anactrl);
|
||
|
|
aw8624_haptic_raminit(aw8624, false);
|
||
|
|
mutex_unlock(&aw8624->lock);
|
||
|
|
|
||
|
|
return r_lra;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int aw8624_haptic_ram_vbat_comp(struct aw8624 *aw8624, bool flag)
|
||
|
|
{
|
||
|
|
int temp_gain = 0;
|
||
|
|
int vbat = 0;
|
||
|
|
|
||
|
|
if (flag) {
|
||
|
|
if (aw8624->ram_vbat_comp ==
|
||
|
|
AW8624_HAPTIC_RAM_VBAT_COMP_ENABLE) {
|
||
|
|
vbat = aw8624_vbat_monitor_detector(aw8624);
|
||
|
|
temp_gain = aw8624->gain * AW8624_VBAT_REFER / vbat;
|
||
|
|
if
|
||
|
|
(temp_gain > (128*AW8624_VBAT_REFER/AW8624_VBAT_MIN)) {
|
||
|
|
temp_gain =
|
||
|
|
128*AW8624_VBAT_REFER/AW8624_VBAT_MIN;
|
||
|
|
aw_dev_dbg(aw8624->dev, "%s gain limit=%d\n",
|
||
|
|
__func__, temp_gain);
|
||
|
|
}
|
||
|
|
aw8624_haptic_set_gain(aw8624, temp_gain);
|
||
|
|
} else {
|
||
|
|
aw8624_haptic_set_gain(aw8624, aw8624->gain);
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
aw8624_haptic_set_gain(aw8624, aw8624->gain);
|
||
|
|
}
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
void aw8624_haptic_set_rtp_aei(struct aw8624 *aw8624, bool flag)
|
||
|
|
{
|
||
|
|
if (flag) {
|
||
|
|
aw8624_i2c_write_bits(aw8624,
|
||
|
|
AW8624_REG_SYSINTM,
|
||
|
|
AW8624_BIT_SYSINTM_FF_AE_MASK,
|
||
|
|
AW8624_BIT_SYSINTM_FF_AE_EN);
|
||
|
|
} else {
|
||
|
|
aw8624_i2c_write_bits(aw8624,
|
||
|
|
AW8624_REG_SYSINTM,
|
||
|
|
AW8624_BIT_SYSINTM_FF_AE_MASK,
|
||
|
|
AW8624_BIT_SYSINTM_FF_AE_OFF);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
static unsigned char aw8624_haptic_rtp_get_fifo_afi(struct aw8624 *aw8624)
|
||
|
|
{
|
||
|
|
unsigned char ret = 0;
|
||
|
|
unsigned char reg_val = 0;
|
||
|
|
|
||
|
|
if (aw8624->osc_cali_flag == 1) {
|
||
|
|
aw8624_i2c_read(aw8624, AW8624_REG_SYSST, ®_val);
|
||
|
|
reg_val &= AW8624_BIT_SYSST_FF_AFS;
|
||
|
|
ret = reg_val >> 3;
|
||
|
|
} else {
|
||
|
|
aw8624_i2c_read(aw8624, AW8624_REG_SYSINT, ®_val);
|
||
|
|
reg_val &= AW8624_BIT_SYSINT_FF_AFI;
|
||
|
|
ret = reg_val >> 3;
|
||
|
|
}
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
unsigned char aw8624_haptic_rtp_get_fifo_afs(struct aw8624 *aw8624)
|
||
|
|
{
|
||
|
|
unsigned char ret = 0;
|
||
|
|
unsigned char reg_val = 0;
|
||
|
|
|
||
|
|
aw8624_i2c_read(aw8624, AW8624_REG_SYSST, ®_val);
|
||
|
|
reg_val &= AW8624_BIT_SYSST_FF_AFS;
|
||
|
|
ret = reg_val >> 3;
|
||
|
|
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int aw8624_haptic_rtp_init(struct aw8624 *aw8624)
|
||
|
|
{
|
||
|
|
unsigned int buf_len = 0;
|
||
|
|
unsigned char glb_st = 0;
|
||
|
|
|
||
|
|
aw_dev_info(aw8624->dev, "%s enter\n", __func__);
|
||
|
|
aw8624->rtp_cnt = 0;
|
||
|
|
mutex_lock(&aw8624->rtp_lock);
|
||
|
|
while ((!aw8624_haptic_rtp_get_fifo_afs(aw8624)) &&
|
||
|
|
(aw8624->play_mode == AW8624_HAPTIC_RTP_MODE)) {
|
||
|
|
aw_dev_info(aw8624->dev,
|
||
|
|
"%s rtp cnt = %d\n", __func__, aw8624->rtp_cnt);
|
||
|
|
if (!aw8624->rtp_container) {
|
||
|
|
aw_dev_info(aw8624->dev,
|
||
|
|
"%s:aw8624_rtp is null break\n", __func__);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (aw8624->rtp_cnt < aw8624->ram.base_addr) {
|
||
|
|
if ((aw8624->rtp_container->len-aw8624->rtp_cnt) <
|
||
|
|
(aw8624->ram.base_addr)) {
|
||
|
|
buf_len = aw8624->rtp_container->len-aw8624->rtp_cnt;
|
||
|
|
} else {
|
||
|
|
buf_len = (aw8624->ram.base_addr);
|
||
|
|
}
|
||
|
|
} else if ((aw8624->rtp_container->len - aw8624->rtp_cnt) <
|
||
|
|
(aw8624->ram.base_addr >> 2)) {
|
||
|
|
buf_len = aw8624->rtp_container->len - aw8624->rtp_cnt;
|
||
|
|
} else {
|
||
|
|
buf_len = (aw8624->ram.base_addr >> 2);
|
||
|
|
}
|
||
|
|
aw_dev_info(aw8624->dev,
|
||
|
|
"%s buf_len = %d\n", __func__, buf_len);
|
||
|
|
aw8624_i2c_writes(aw8624, AW8624_REG_RTP_DATA,
|
||
|
|
&aw8624->rtp_container->data[aw8624->rtp_cnt],
|
||
|
|
buf_len);
|
||
|
|
|
||
|
|
aw8624->rtp_cnt += buf_len;
|
||
|
|
aw8624_i2c_read(aw8624, AW8624_REG_GLB_STATE, &glb_st);
|
||
|
|
if (aw8624->rtp_cnt == aw8624->rtp_container->len ||
|
||
|
|
((glb_st & 0x0f) == 0x00)) {
|
||
|
|
if (aw8624->rtp_cnt == aw8624->rtp_container->len)
|
||
|
|
aw_dev_info(aw8624->dev,
|
||
|
|
"%s: rtp load completely! glb_st=%02x aw8624->rtp_cnt=%d\n",
|
||
|
|
__func__, glb_st, aw8624->rtp_cnt);
|
||
|
|
else
|
||
|
|
aw_dev_err(aw8624->dev,
|
||
|
|
"%s rtp load failed!! glb_st=%02x aw8624->rtp_cnt=%d\n",
|
||
|
|
__func__, glb_st, aw8624->rtp_cnt);
|
||
|
|
aw8624->rtp_cnt = 0;
|
||
|
|
mutex_unlock(&aw8624->rtp_lock);
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (aw8624->play_mode == AW8624_HAPTIC_RTP_MODE)
|
||
|
|
aw8624_haptic_set_rtp_aei(aw8624, true);
|
||
|
|
|
||
|
|
aw_dev_info(aw8624->dev, "%s exit\n", __func__);
|
||
|
|
mutex_unlock(&aw8624->rtp_lock);
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static void aw8624_rtp_work_routine(struct work_struct *work)
|
||
|
|
{
|
||
|
|
const struct firmware *rtp_file;
|
||
|
|
int ret = -1;
|
||
|
|
unsigned char reg_val = 0;
|
||
|
|
bool rtp_work_flag = false;
|
||
|
|
unsigned int cnt = 200;
|
||
|
|
|
||
|
|
struct aw8624 *aw8624 = container_of(work, struct aw8624, rtp_work);
|
||
|
|
|
||
|
|
/* fw loaded */
|
||
|
|
aw_dev_info(aw8624->dev, "%s enter\n", __func__);
|
||
|
|
mutex_lock(&aw8624->rtp_lock);
|
||
|
|
ret = request_firmware(&rtp_file,
|
||
|
|
aw8624_rtp_name[aw8624->rtp_file_num], aw8624->dev);
|
||
|
|
if (ret < 0) {
|
||
|
|
aw_dev_err(aw8624->dev, "%s: failed to read %s\n", __func__,
|
||
|
|
aw8624_rtp_name[aw8624->rtp_file_num]);
|
||
|
|
mutex_unlock(&aw8624->rtp_lock);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
aw8624->rtp_init = 0;
|
||
|
|
vfree(aw8624->rtp_container);
|
||
|
|
aw8624->rtp_container = vmalloc(rtp_file->size+sizeof(int));
|
||
|
|
if (!aw8624->rtp_container) {
|
||
|
|
release_firmware(rtp_file);
|
||
|
|
aw_dev_err(aw8624->dev,
|
||
|
|
"%s: error allocating memory\n", __func__);
|
||
|
|
mutex_unlock(&aw8624->rtp_lock);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
aw8624->rtp_container->len = rtp_file->size;
|
||
|
|
aw_dev_info(aw8624->dev, "%s: rtp file [%s] size = %d\n", __func__,
|
||
|
|
aw8624_rtp_name[aw8624->rtp_file_num], aw8624->rtp_container->len);
|
||
|
|
memcpy(aw8624->rtp_container->data, rtp_file->data, rtp_file->size);
|
||
|
|
mutex_unlock(&aw8624->rtp_lock);
|
||
|
|
release_firmware(rtp_file);
|
||
|
|
mutex_lock(&aw8624->lock);
|
||
|
|
aw8624->rtp_init = 1;
|
||
|
|
if (aw8624->IsUsedIRQ)
|
||
|
|
aw8624_haptic_select_pin(aw8624, IRQ);
|
||
|
|
aw8624_haptic_upload_lra(aw8624, AW8624_HAPTIC_RTP_CALI_LRA);
|
||
|
|
/* rtp mode config */
|
||
|
|
aw8624_haptic_play_mode(aw8624, AW8624_HAPTIC_RTP_MODE);
|
||
|
|
|
||
|
|
/* haptic start */
|
||
|
|
aw8624_haptic_start(aw8624);
|
||
|
|
mutex_unlock(&aw8624->lock);
|
||
|
|
usleep_range(2000, 2500);
|
||
|
|
while (cnt) {
|
||
|
|
aw8624_i2c_read(aw8624, AW8624_REG_GLB_STATE, ®_val);
|
||
|
|
if ((reg_val & 0x0f) == 0x08) {
|
||
|
|
cnt = 0;
|
||
|
|
rtp_work_flag = true;
|
||
|
|
aw_dev_info(aw8624->dev, "%s RTP_GO! glb_state=0x08\n",
|
||
|
|
__func__);
|
||
|
|
} else {
|
||
|
|
cnt--;
|
||
|
|
aw_dev_dbg(aw8624->dev, "%s wait for RTP_GO, glb_state=0x%02X\n",
|
||
|
|
__func__, reg_val);
|
||
|
|
}
|
||
|
|
usleep_range(2000, 2500);
|
||
|
|
}
|
||
|
|
if (rtp_work_flag) {
|
||
|
|
aw8624_haptic_rtp_init(aw8624);
|
||
|
|
} else {
|
||
|
|
/* enter standby mode */
|
||
|
|
aw8624_haptic_stop(aw8624);
|
||
|
|
aw_dev_err(aw8624->dev, "%s failed to enter RTP_GO status!\n",
|
||
|
|
__func__);
|
||
|
|
}
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
/*****************************************************
|
||
|
|
*
|
||
|
|
* haptic - audio
|
||
|
|
*
|
||
|
|
*****************************************************/
|
||
|
|
static enum hrtimer_restart
|
||
|
|
aw8624_haptic_audio_timer_func(struct hrtimer *timer)
|
||
|
|
{
|
||
|
|
struct aw8624 *aw8624 =
|
||
|
|
container_of(timer, struct aw8624, haptic_audio.timer);
|
||
|
|
|
||
|
|
schedule_work(&aw8624->haptic_audio.work);
|
||
|
|
hrtimer_start(&aw8624->haptic_audio.timer,
|
||
|
|
ktime_set(aw8624->haptic_audio.timer_val/1000,
|
||
|
|
(aw8624->haptic_audio.timer_val%1000)*1000000),
|
||
|
|
HRTIMER_MODE_REL);
|
||
|
|
return HRTIMER_NORESTART;
|
||
|
|
}
|
||
|
|
|
||
|
|
static void aw8624_haptic_audio_work_routine(struct work_struct *work)
|
||
|
|
{
|
||
|
|
struct aw8624 *aw8624 =
|
||
|
|
container_of(work, struct aw8624, haptic_audio.work);
|
||
|
|
struct haptic_audio *haptic_audio = NULL;
|
||
|
|
struct haptic_ctr *p_ctr = NULL;
|
||
|
|
struct haptic_ctr *p_ctr_bak = NULL;
|
||
|
|
unsigned int ctr_list_flag = 0;
|
||
|
|
unsigned int ctr_list_input_cnt = 0;
|
||
|
|
unsigned int ctr_list_output_cnt = 0;
|
||
|
|
unsigned int ctr_list_diff_cnt = 0;
|
||
|
|
unsigned int ctr_list_del_cnt = 0;
|
||
|
|
int rtp_is_going_on = 0;
|
||
|
|
|
||
|
|
aw_dev_info(aw8624->dev, "%s enter\n", __func__);
|
||
|
|
haptic_audio = &(aw8624->haptic_audio);
|
||
|
|
mutex_lock(&aw8624->haptic_audio.lock);
|
||
|
|
memset(&aw8624->haptic_audio.ctr, 0, sizeof(struct haptic_ctr));
|
||
|
|
ctr_list_flag = 0;
|
||
|
|
list_for_each_entry_safe_reverse(p_ctr, p_ctr_bak,
|
||
|
|
&(haptic_audio->ctr_list), list) {
|
||
|
|
ctr_list_flag = 1;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
if (ctr_list_flag == 0)
|
||
|
|
aw_dev_info(aw8624->dev, "%s: ctr list empty\n", __func__);
|
||
|
|
if (ctr_list_flag == 1) {
|
||
|
|
list_for_each_entry_safe(p_ctr, p_ctr_bak,
|
||
|
|
&(haptic_audio->ctr_list), list) {
|
||
|
|
ctr_list_input_cnt = p_ctr->cnt;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
list_for_each_entry_safe_reverse(p_ctr, p_ctr_bak,
|
||
|
|
&(haptic_audio->ctr_list), list) {
|
||
|
|
ctr_list_output_cnt = p_ctr->cnt;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
if (ctr_list_input_cnt > ctr_list_output_cnt)
|
||
|
|
ctr_list_diff_cnt = ctr_list_input_cnt - ctr_list_output_cnt;
|
||
|
|
|
||
|
|
if (ctr_list_input_cnt < ctr_list_output_cnt)
|
||
|
|
ctr_list_diff_cnt = 32 + ctr_list_input_cnt - ctr_list_output_cnt;
|
||
|
|
|
||
|
|
if (ctr_list_diff_cnt > 2) {
|
||
|
|
list_for_each_entry_safe_reverse(p_ctr, p_ctr_bak,
|
||
|
|
&(haptic_audio->ctr_list), list) {
|
||
|
|
if ((p_ctr->play == 0) &&
|
||
|
|
(AW8624_HAPTIC_CMD_ENABLE ==
|
||
|
|
(AW8624_HAPTIC_CMD_HAPTIC & p_ctr->cmd))) {
|
||
|
|
list_del(&p_ctr->list);
|
||
|
|
kfree(p_ctr);
|
||
|
|
ctr_list_del_cnt++;
|
||
|
|
}
|
||
|
|
if (ctr_list_del_cnt == ctr_list_diff_cnt)
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/* get the last data from list */
|
||
|
|
list_for_each_entry_safe_reverse(p_ctr, p_ctr_bak,
|
||
|
|
&(haptic_audio->ctr_list), list) {
|
||
|
|
aw8624->haptic_audio.ctr.cnt = p_ctr->cnt;
|
||
|
|
aw8624->haptic_audio.ctr.cmd = p_ctr->cmd;
|
||
|
|
aw8624->haptic_audio.ctr.play = p_ctr->play;
|
||
|
|
aw8624->haptic_audio.ctr.wavseq = p_ctr->wavseq;
|
||
|
|
aw8624->haptic_audio.ctr.loop = p_ctr->loop;
|
||
|
|
aw8624->haptic_audio.ctr.gain = p_ctr->gain;
|
||
|
|
list_del(&p_ctr->list);
|
||
|
|
kfree(p_ctr);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (aw8624->haptic_audio.ctr.play) {
|
||
|
|
aw_dev_info(aw8624->dev, "%s: cnt=%d, cmd=%d, play=%d, wavseq=%d, loop=%d, gain=%d\n",
|
||
|
|
__func__,
|
||
|
|
aw8624->haptic_audio.ctr.cnt,
|
||
|
|
aw8624->haptic_audio.ctr.cmd,
|
||
|
|
aw8624->haptic_audio.ctr.play,
|
||
|
|
aw8624->haptic_audio.ctr.wavseq,
|
||
|
|
aw8624->haptic_audio.ctr.loop,
|
||
|
|
aw8624->haptic_audio.ctr.gain);
|
||
|
|
}
|
||
|
|
|
||
|
|
/* rtp mode jump */
|
||
|
|
rtp_is_going_on = aw8624_haptic_juge_RTP_is_going_on(aw8624);
|
||
|
|
if (rtp_is_going_on) {
|
||
|
|
mutex_unlock(&aw8624->haptic_audio.lock);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
mutex_unlock(&aw8624->haptic_audio.lock);
|
||
|
|
|
||
|
|
if ((AW8624_HAPTIC_CMD_HAPTIC & aw8624->haptic_audio.ctr.cmd) ==
|
||
|
|
AW8624_HAPTIC_CMD_ENABLE) {
|
||
|
|
if (aw8624->haptic_audio.ctr.play ==
|
||
|
|
AW8624_HAPTIC_PLAY_ENABLE) {
|
||
|
|
aw_dev_info(aw8624->dev,
|
||
|
|
"%s: haptic audio play start\n", __func__);
|
||
|
|
mutex_lock(&aw8624->lock);
|
||
|
|
aw8624_haptic_stop(aw8624);
|
||
|
|
|
||
|
|
aw8624_haptic_play_mode(aw8624, AW8624_HAPTIC_RAM_MODE);
|
||
|
|
|
||
|
|
aw8624_haptic_set_wav_seq(aw8624, 0x00,
|
||
|
|
aw8624->haptic_audio.ctr.wavseq);
|
||
|
|
|
||
|
|
aw8624_haptic_set_wav_loop(aw8624, 0x00,
|
||
|
|
aw8624->haptic_audio.ctr.loop);
|
||
|
|
|
||
|
|
aw8624_haptic_set_gain(aw8624,
|
||
|
|
aw8624->haptic_audio.ctr.gain);
|
||
|
|
|
||
|
|
aw8624_haptic_start(aw8624);
|
||
|
|
mutex_unlock(&aw8624->lock);
|
||
|
|
} else if (AW8624_HAPTIC_PLAY_STOP ==
|
||
|
|
aw8624->haptic_audio.ctr.play) {
|
||
|
|
mutex_lock(&aw8624->lock);
|
||
|
|
aw8624_haptic_stop(aw8624);
|
||
|
|
mutex_unlock(&aw8624->lock);
|
||
|
|
|
||
|
|
} else if (AW8624_HAPTIC_PLAY_GAIN ==
|
||
|
|
aw8624->haptic_audio.ctr.play) {
|
||
|
|
mutex_lock(&aw8624->lock);
|
||
|
|
aw8624_haptic_set_gain(aw8624,
|
||
|
|
aw8624->haptic_audio.ctr.gain);
|
||
|
|
mutex_unlock(&aw8624->lock);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/*****************************************************
|
||
|
|
*
|
||
|
|
* haptic cont
|
||
|
|
*
|
||
|
|
*****************************************************/
|
||
|
|
static int aw8624_haptic_cont(struct aw8624 *aw8624)
|
||
|
|
{
|
||
|
|
unsigned char brake0_level = 0;
|
||
|
|
unsigned char en_brake1 = 0;
|
||
|
|
unsigned char brake1_level = 0;
|
||
|
|
unsigned char en_brake2 = 0;
|
||
|
|
unsigned char brake2_level = 0;
|
||
|
|
unsigned char brake2_p_num = 0;
|
||
|
|
unsigned char brake1_p_num = 0;
|
||
|
|
unsigned char brake0_p_num = 0;
|
||
|
|
|
||
|
|
aw_dev_info(aw8624->dev, "%s enter\n.", __func__);
|
||
|
|
/* work mode */
|
||
|
|
aw8624_haptic_active(aw8624);
|
||
|
|
aw8624_haptic_play_mode(aw8624, AW8624_HAPTIC_CONT_MODE);
|
||
|
|
|
||
|
|
/* preset f0 */
|
||
|
|
aw8624->f0_pre = aw8624->f0;
|
||
|
|
aw8624_haptic_set_f0_preset(aw8624);
|
||
|
|
|
||
|
|
/* lpf */
|
||
|
|
aw8624_i2c_write_bits(aw8624,
|
||
|
|
AW8624_REG_DATCTRL,
|
||
|
|
AW8624_BIT_DATCTRL_FC_MASK,
|
||
|
|
AW8624_BIT_DATCTRL_FC_1000HZ);
|
||
|
|
aw8624_i2c_write_bits(aw8624,
|
||
|
|
AW8624_REG_DATCTRL,
|
||
|
|
AW8624_BIT_DATCTRL_LPF_ENABLE_MASK,
|
||
|
|
AW8624_BIT_DATCTRL_LPF_ENABLE);
|
||
|
|
|
||
|
|
/* brake */
|
||
|
|
en_brake1 = aw8624_dts_data.aw8624_cont_brake[0][0];
|
||
|
|
en_brake2 = aw8624_dts_data.aw8624_cont_brake[0][1];
|
||
|
|
brake0_level = aw8624_dts_data.aw8624_cont_brake[0][2];
|
||
|
|
brake1_level = aw8624_dts_data.aw8624_cont_brake[0][3];
|
||
|
|
brake2_level = aw8624_dts_data.aw8624_cont_brake[0][4];
|
||
|
|
brake0_p_num = aw8624_dts_data.aw8624_cont_brake[0][5];
|
||
|
|
brake1_p_num = aw8624_dts_data.aw8624_cont_brake[0][6];
|
||
|
|
brake2_p_num = aw8624_dts_data.aw8624_cont_brake[0][7];
|
||
|
|
|
||
|
|
aw8624_i2c_write(aw8624,
|
||
|
|
AW8624_REG_BRAKE0_CTRL,
|
||
|
|
(brake0_level << 0));
|
||
|
|
aw8624_i2c_write(aw8624,
|
||
|
|
AW8624_REG_BRAKE1_CTRL,
|
||
|
|
(en_brake1 << 7)|(brake1_level << 0));
|
||
|
|
aw8624_i2c_write(aw8624,
|
||
|
|
AW8624_REG_BRAKE2_CTRL,
|
||
|
|
(en_brake2 << 7)|(brake2_level << 0));
|
||
|
|
aw8624_i2c_write(aw8624,
|
||
|
|
AW8624_REG_BRAKE_NUM,
|
||
|
|
((brake2_p_num << 6)|(brake1_p_num << 3) |
|
||
|
|
(brake0_p_num << 0)));
|
||
|
|
|
||
|
|
/* cont config */
|
||
|
|
aw8624_i2c_write_bits(aw8624,
|
||
|
|
AW8624_REG_CONT_CTRL,
|
||
|
|
AW8624_BIT_CONT_CTRL_ZC_DETEC_MASK,
|
||
|
|
AW8624_BIT_CONT_CTRL_ZC_DETEC_ENABLE);
|
||
|
|
aw8624_i2c_write_bits(aw8624,
|
||
|
|
AW8624_REG_CONT_CTRL,
|
||
|
|
AW8624_BIT_CONT_CTRL_WAIT_PERIOD_MASK,
|
||
|
|
AW8624_BIT_CONT_CTRL_WAIT_1PERIOD);
|
||
|
|
|
||
|
|
aw8624_i2c_write_bits(aw8624,
|
||
|
|
AW8624_REG_CONT_CTRL,
|
||
|
|
AW8624_BIT_CONT_CTRL_MODE_MASK,
|
||
|
|
AW8624_BIT_CONT_CTRL_BY_GO_SIGNAL);
|
||
|
|
|
||
|
|
aw8624_i2c_write_bits(aw8624,
|
||
|
|
AW8624_REG_CONT_CTRL,
|
||
|
|
AW8624_BIT_CONT_CTRL_EN_CLOSE_MASK,
|
||
|
|
AW8624_BIT_CONT_CTRL_CLOSE_PLAYBACK);
|
||
|
|
aw8624_i2c_write_bits(aw8624,
|
||
|
|
AW8624_REG_CONT_CTRL,
|
||
|
|
AW8624_BIT_CONT_CTRL_F0_DETECT_MASK,
|
||
|
|
AW8624_BIT_CONT_CTRL_F0_DETECT_DISABLE);
|
||
|
|
aw8624_i2c_write_bits(aw8624,
|
||
|
|
AW8624_REG_CONT_CTRL,
|
||
|
|
AW8624_BIT_CONT_CTRL_O2C_MASK,
|
||
|
|
AW8624_BIT_CONT_CTRL_O2C_DISABLE);
|
||
|
|
|
||
|
|
/* TD time */
|
||
|
|
aw8624_i2c_write(aw8624,
|
||
|
|
AW8624_REG_TD_H,
|
||
|
|
(unsigned char)(aw8624->cont_td>>8));
|
||
|
|
aw8624_i2c_write(aw8624,
|
||
|
|
AW8624_REG_TD_L,
|
||
|
|
(unsigned char)(aw8624->cont_td>>0));
|
||
|
|
|
||
|
|
|
||
|
|
aw8624_i2c_write_bits(aw8624,
|
||
|
|
AW8624_REG_BEMF_NUM,
|
||
|
|
AW8624_BIT_BEMF_NUM_BRK_MASK,
|
||
|
|
aw8624->cont_num_brk);
|
||
|
|
aw8624_i2c_write(aw8624,
|
||
|
|
AW8624_REG_TIME_NZC,
|
||
|
|
0x1f);
|
||
|
|
|
||
|
|
/* f0 driver level */
|
||
|
|
aw8624_i2c_write(aw8624,
|
||
|
|
AW8624_REG_DRV_LVL,
|
||
|
|
aw8624->cont_drv_lvl);
|
||
|
|
aw8624_i2c_write(aw8624,
|
||
|
|
AW8624_REG_DRV_LVL_OV,
|
||
|
|
aw8624->cont_drv_lvl_ov);
|
||
|
|
|
||
|
|
/* cont play go */
|
||
|
|
aw8624_haptic_play_go(aw8624, true);
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
/*****************************************************
|
||
|
|
*
|
||
|
|
* haptic f0 cali
|
||
|
|
*
|
||
|
|
*****************************************************/
|
||
|
|
static void aw8624_haptic_upload_lra(struct aw8624 *aw8624, unsigned int flag)
|
||
|
|
{
|
||
|
|
switch (flag) {
|
||
|
|
case AW8624_HAPTIC_F0_CALI_LRA:
|
||
|
|
aw_dev_info(aw8624->dev, "%s f0_cali_lra=%d\n",
|
||
|
|
__func__, aw8624->f0_calib_data);
|
||
|
|
aw8624_i2c_write(aw8624, AW8624_REG_TRIM_LRA,
|
||
|
|
(char)aw8624->f0_calib_data);
|
||
|
|
break;
|
||
|
|
case AW8624_HAPTIC_RTP_CALI_LRA:
|
||
|
|
aw_dev_info(aw8624->dev, "%s rtp_cali_lra=%d\n",
|
||
|
|
__func__, aw8624->lra_calib_data);
|
||
|
|
aw8624_i2c_write(aw8624, AW8624_REG_TRIM_LRA,
|
||
|
|
(char)aw8624->lra_calib_data);
|
||
|
|
break;
|
||
|
|
case AW8624_HAPTIC_ZERO:
|
||
|
|
aw_dev_info(aw8624->dev,
|
||
|
|
"%s write zero to trim_lra!\n", __func__);
|
||
|
|
aw8624_i2c_write(aw8624, AW8624_REG_TRIM_LRA, 0);
|
||
|
|
break;
|
||
|
|
default:
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
static int aw8624_haptic_get_f0(struct aw8624 *aw8624)
|
||
|
|
{
|
||
|
|
int ret = 0;
|
||
|
|
unsigned char reg_val = 0;
|
||
|
|
unsigned char f0_pre_num = 0;
|
||
|
|
unsigned char f0_wait_num = 0;
|
||
|
|
unsigned char f0_repeat_num = 0;
|
||
|
|
unsigned char f0_trace_num = 0;
|
||
|
|
unsigned int t_f0_ms = 0;
|
||
|
|
unsigned int t_f0_trace_ms = 0;
|
||
|
|
unsigned char i = 0;
|
||
|
|
unsigned int f0_cali_cnt = 50;
|
||
|
|
|
||
|
|
|
||
|
|
aw_dev_info(aw8624->dev, "%s enter\n", __func__);
|
||
|
|
|
||
|
|
aw8624->f0 = aw8624->f0_pre;
|
||
|
|
/* f0 calibrate work mode */
|
||
|
|
aw8624_haptic_stop(aw8624);
|
||
|
|
aw8624_haptic_play_mode(aw8624, AW8624_HAPTIC_CONT_MODE);
|
||
|
|
|
||
|
|
|
||
|
|
aw8624_i2c_write_bits(aw8624,
|
||
|
|
AW8624_REG_CONT_CTRL,
|
||
|
|
AW8624_BIT_CONT_CTRL_EN_CLOSE_MASK,
|
||
|
|
AW8624_BIT_CONT_CTRL_OPEN_PLAYBACK);
|
||
|
|
aw8624_i2c_write_bits(aw8624,
|
||
|
|
AW8624_REG_CONT_CTRL,
|
||
|
|
AW8624_BIT_CONT_CTRL_F0_DETECT_MASK,
|
||
|
|
AW8624_BIT_CONT_CTRL_F0_DETECT_ENABLE);
|
||
|
|
|
||
|
|
/* LPF */
|
||
|
|
aw8624_i2c_write_bits(aw8624,
|
||
|
|
AW8624_REG_DATCTRL,
|
||
|
|
AW8624_BIT_DATCTRL_FC_MASK,
|
||
|
|
AW8624_BIT_DATCTRL_FC_1000HZ);
|
||
|
|
aw8624_i2c_write_bits(aw8624,
|
||
|
|
AW8624_REG_DATCTRL,
|
||
|
|
AW8624_BIT_DATCTRL_LPF_ENABLE_MASK,
|
||
|
|
AW8624_BIT_DATCTRL_LPF_ENABLE);
|
||
|
|
|
||
|
|
/* preset f0 */
|
||
|
|
aw8624_haptic_set_f0_preset(aw8624);
|
||
|
|
/* f0 driver level */
|
||
|
|
aw8624_i2c_write(aw8624, AW8624_REG_DRV_LVL, aw8624->cont_drv_lvl);
|
||
|
|
/* f0 trace parameter */
|
||
|
|
if (!aw8624->f0_pre) {
|
||
|
|
aw_dev_info(aw8624->dev, "%s:fail to get t_f0_ms\n", __func__);
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
f0_pre_num = aw8624_dts_data.aw8624_f0_trace_parameter[0];
|
||
|
|
f0_wait_num = aw8624_dts_data.aw8624_f0_trace_parameter[1];
|
||
|
|
f0_repeat_num = aw8624_dts_data.aw8624_f0_trace_parameter[2];
|
||
|
|
f0_trace_num = aw8624_dts_data.aw8624_f0_trace_parameter[3];
|
||
|
|
aw8624_i2c_write(aw8624,
|
||
|
|
AW8624_REG_NUM_F0_1,
|
||
|
|
(f0_pre_num<<4)|(f0_wait_num<<0));
|
||
|
|
aw8624_i2c_write(aw8624,
|
||
|
|
AW8624_REG_NUM_F0_2,
|
||
|
|
(f0_repeat_num<<0));
|
||
|
|
aw8624_i2c_write(aw8624,
|
||
|
|
AW8624_REG_NUM_F0_3,
|
||
|
|
(f0_trace_num<<0));
|
||
|
|
|
||
|
|
/* clear aw8624 interrupt */
|
||
|
|
ret = aw8624_i2c_read(aw8624, AW8624_REG_SYSINT, ®_val);
|
||
|
|
|
||
|
|
/* play go and start f0 calibration */
|
||
|
|
aw8624_haptic_play_go(aw8624, true);
|
||
|
|
|
||
|
|
/* f0 trace time */
|
||
|
|
t_f0_ms = 1000*10 / aw8624->f0_pre;
|
||
|
|
t_f0_trace_ms =
|
||
|
|
t_f0_ms * (f0_pre_num + f0_wait_num +
|
||
|
|
(f0_trace_num + f0_wait_num) * (f0_repeat_num - 1));
|
||
|
|
aw_dev_info(aw8624->dev, "%s: t_f0_trace_ms = %dms\n",
|
||
|
|
__func__, t_f0_trace_ms);
|
||
|
|
usleep_range(t_f0_trace_ms * 1000, t_f0_trace_ms * 1000 + 500);
|
||
|
|
|
||
|
|
for (i = 0; i < f0_cali_cnt; i++) {
|
||
|
|
ret = aw8624_i2c_read(aw8624, AW8624_REG_GLB_STATE, ®_val);
|
||
|
|
/* f0 calibrate done */
|
||
|
|
if ((reg_val & 0x0f) == 0x00) {
|
||
|
|
aw8624_haptic_read_f0(aw8624);
|
||
|
|
aw8624_haptic_read_beme(aw8624);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
usleep_range(10000, 10500);
|
||
|
|
aw_dev_info(aw8624->dev, "%s f0 cali sleep 10ms,glb_state=0x%x\n",
|
||
|
|
__func__, reg_val);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (i == f0_cali_cnt)
|
||
|
|
ret = -ERANGE;
|
||
|
|
else
|
||
|
|
ret = 0;
|
||
|
|
|
||
|
|
aw8624_i2c_write_bits(aw8624,
|
||
|
|
AW8624_REG_CONT_CTRL,
|
||
|
|
AW8624_BIT_CONT_CTRL_EN_CLOSE_MASK,
|
||
|
|
AW8624_BIT_CONT_CTRL_CLOSE_PLAYBACK);
|
||
|
|
aw8624_i2c_write_bits(aw8624,
|
||
|
|
AW8624_REG_CONT_CTRL,
|
||
|
|
AW8624_BIT_CONT_CTRL_F0_DETECT_MASK,
|
||
|
|
AW8624_BIT_CONT_CTRL_F0_DETECT_DISABLE);
|
||
|
|
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int aw8624_haptic_f0_calibration(struct aw8624 *aw8624)
|
||
|
|
{
|
||
|
|
int ret = 0;
|
||
|
|
unsigned char reg_val = 0;
|
||
|
|
unsigned int f0_limit = 0;
|
||
|
|
char f0_cali_lra = 0;
|
||
|
|
int f0_cali_step = 0;
|
||
|
|
/*int f0_dft_step = 0;*/
|
||
|
|
|
||
|
|
if (aw8624_haptic_get_f0(aw8624)) {
|
||
|
|
aw_dev_err(aw8624->dev,
|
||
|
|
"%s get f0 error, user defafult f0\n", __func__);
|
||
|
|
} else {
|
||
|
|
/* max and min limit */
|
||
|
|
f0_limit = aw8624->f0;
|
||
|
|
if (aw8624->f0*100 < aw8624_dts_data.aw8624_f0_pre *
|
||
|
|
(100-aw8624_dts_data.aw8624_f0_cali_percen)) {
|
||
|
|
f0_limit = aw8624_dts_data.aw8624_f0_pre;
|
||
|
|
}
|
||
|
|
if (aw8624->f0*100 > aw8624_dts_data.aw8624_f0_pre *
|
||
|
|
(100+aw8624_dts_data.aw8624_f0_cali_percen)) {
|
||
|
|
f0_limit = aw8624_dts_data.aw8624_f0_pre;
|
||
|
|
}
|
||
|
|
/* calculate cali step */
|
||
|
|
f0_cali_step =
|
||
|
|
100000*((int)f0_limit-(int)aw8624->f0_pre)/((int)f0_limit*25);
|
||
|
|
|
||
|
|
if (f0_cali_step >= 0) { /*f0_cali_step >= 0*/
|
||
|
|
if (f0_cali_step % 10 >= 5) {
|
||
|
|
f0_cali_step = f0_cali_step/10 + 1 +
|
||
|
|
(aw8624->chipid_flag == 1 ? 32 : 16);
|
||
|
|
} else {
|
||
|
|
f0_cali_step = f0_cali_step/10 +
|
||
|
|
(aw8624->chipid_flag == 1 ? 32 : 16);
|
||
|
|
}
|
||
|
|
} else { /*f0_cali_step < 0*/
|
||
|
|
if (f0_cali_step % 10 <= -5) {
|
||
|
|
f0_cali_step =
|
||
|
|
(aw8624->chipid_flag == 1 ? 32 : 16) +
|
||
|
|
(f0_cali_step/10 - 1);
|
||
|
|
} else {
|
||
|
|
f0_cali_step =
|
||
|
|
(aw8624->chipid_flag == 1 ? 32 : 16) +
|
||
|
|
f0_cali_step/10;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (aw8624->chipid_flag == 1) {
|
||
|
|
if (f0_cali_step > 31)
|
||
|
|
f0_cali_lra = (char)f0_cali_step - 32;
|
||
|
|
else
|
||
|
|
f0_cali_lra = (char)f0_cali_step + 32;
|
||
|
|
} else {
|
||
|
|
if (f0_cali_step < 16 ||
|
||
|
|
(f0_cali_step > 31 && f0_cali_step < 48)) {
|
||
|
|
f0_cali_lra = (char)f0_cali_step + 16;
|
||
|
|
} else {
|
||
|
|
f0_cali_lra = (char)f0_cali_step - 16;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
aw8624->f0_calib_data = (int)f0_cali_lra;
|
||
|
|
/* update cali step */
|
||
|
|
aw8624_haptic_upload_lra(aw8624,
|
||
|
|
AW8624_HAPTIC_F0_CALI_LRA);
|
||
|
|
aw8624_i2c_read(aw8624,
|
||
|
|
AW8624_REG_TRIM_LRA,
|
||
|
|
®_val);
|
||
|
|
aw_dev_info(aw8624->dev,
|
||
|
|
"%s final trim_lra=0x%02x\n", __func__, reg_val);
|
||
|
|
}
|
||
|
|
|
||
|
|
/* if (aw8624_haptic_get_f0(aw8624)) { */
|
||
|
|
/* aw_dev_err(aw8624->dev,*/
|
||
|
|
/* "%s get f0 error, user defafult f0\n", __func__); */
|
||
|
|
/* } */
|
||
|
|
|
||
|
|
aw8624_haptic_play_mode(aw8624, AW8624_HAPTIC_STANDBY_MODE);
|
||
|
|
aw8624_haptic_stop(aw8624);
|
||
|
|
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
/*****************************************************
|
||
|
|
*
|
||
|
|
* haptic fops
|
||
|
|
*
|
||
|
|
*****************************************************/
|
||
|
|
static int aw8624_file_open(struct inode *inode, struct file *file)
|
||
|
|
{
|
||
|
|
if (!try_module_get(THIS_MODULE))
|
||
|
|
return -ENODEV;
|
||
|
|
|
||
|
|
file->private_data = (void *)g_aw8624;
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int aw8624_file_release(struct inode *inode, struct file *file)
|
||
|
|
{
|
||
|
|
file->private_data = (void *)NULL;
|
||
|
|
|
||
|
|
module_put(THIS_MODULE);
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static long aw8624_file_unlocked_ioctl(struct file *file,
|
||
|
|
unsigned int cmd, unsigned long arg)
|
||
|
|
{
|
||
|
|
struct aw8624 *aw8624 = (struct aw8624 *)file->private_data;
|
||
|
|
int ret = 0;
|
||
|
|
|
||
|
|
dev_info(aw8624->dev, "%s: cmd=0x%x, arg=0x%lx\n",
|
||
|
|
__func__, cmd, arg);
|
||
|
|
|
||
|
|
mutex_lock(&aw8624->lock);
|
||
|
|
|
||
|
|
if (_IOC_TYPE(cmd) != AW8624_HAPTIC_IOCTL_MAGIC) {
|
||
|
|
dev_err(aw8624->dev, "%s: cmd magic err\n",
|
||
|
|
__func__);
|
||
|
|
mutex_unlock(&aw8624->lock);
|
||
|
|
return -EINVAL;
|
||
|
|
}
|
||
|
|
|
||
|
|
switch (cmd) {
|
||
|
|
default:
|
||
|
|
dev_err(aw8624->dev, "%s, unknown cmd\n", __func__);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
mutex_unlock(&aw8624->lock);
|
||
|
|
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
static ssize_t aw8624_file_read(struct file *filp,
|
||
|
|
char *buff, size_t len, loff_t *offset)
|
||
|
|
{
|
||
|
|
struct aw8624 *aw8624 = (struct aw8624 *)filp->private_data;
|
||
|
|
int ret = 0;
|
||
|
|
int i = 0;
|
||
|
|
unsigned char reg_val = 0;
|
||
|
|
unsigned char *pbuff = NULL;
|
||
|
|
|
||
|
|
mutex_lock(&aw8624->lock);
|
||
|
|
|
||
|
|
dev_info(aw8624->dev, "%s: len=%zu\n", __func__, len);
|
||
|
|
|
||
|
|
switch (aw8624->fileops.cmd) {
|
||
|
|
case AW8624_HAPTIC_CMD_READ_REG:
|
||
|
|
pbuff = kzalloc(len, GFP_KERNEL);
|
||
|
|
if (pbuff != NULL) {
|
||
|
|
for (i = 0; i < len; i++) {
|
||
|
|
aw8624_i2c_read(aw8624,
|
||
|
|
aw8624->fileops.reg+i,
|
||
|
|
®_val);
|
||
|
|
pbuff[i] = reg_val;
|
||
|
|
}
|
||
|
|
ret = copy_to_user(buff, pbuff, len);
|
||
|
|
if (ret) {
|
||
|
|
dev_err
|
||
|
|
(aw8624->dev, "%s: copy to user fail\n",
|
||
|
|
__func__);
|
||
|
|
}
|
||
|
|
kfree(pbuff);
|
||
|
|
} else {
|
||
|
|
dev_err(aw8624->dev, "%s: alloc memory fail\n",
|
||
|
|
__func__);
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
default:
|
||
|
|
dev_err(aw8624->dev, "%s, unknown cmd %d\n",
|
||
|
|
__func__, aw8624->fileops.cmd);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
mutex_unlock(&aw8624->lock);
|
||
|
|
|
||
|
|
for (i = 0; i < len; i++) {
|
||
|
|
dev_info(aw8624->dev, "%s: buff[%d]=0x%02x\n",
|
||
|
|
__func__, i, buff[i]);
|
||
|
|
}
|
||
|
|
|
||
|
|
return len;
|
||
|
|
}
|
||
|
|
|
||
|
|
static ssize_t aw8624_file_write(struct file *filp,
|
||
|
|
const char *buff, size_t len, loff_t *off)
|
||
|
|
{
|
||
|
|
struct aw8624 *aw8624 = (struct aw8624 *)filp->private_data;
|
||
|
|
int i = 0;
|
||
|
|
int ret = 0;
|
||
|
|
unsigned char *pbuff = NULL;
|
||
|
|
|
||
|
|
|
||
|
|
mutex_lock(&aw8624->lock);
|
||
|
|
|
||
|
|
aw8624->fileops.cmd = buff[0];
|
||
|
|
|
||
|
|
switch (aw8624->fileops.cmd) {
|
||
|
|
case AW8624_HAPTIC_CMD_READ_REG:
|
||
|
|
if (len == 2) {
|
||
|
|
aw8624->fileops.reg = buff[1];
|
||
|
|
} else {
|
||
|
|
dev_err(aw8624->dev,
|
||
|
|
"%s: read cmd len %zu err\n",
|
||
|
|
__func__, len);
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
case AW8624_HAPTIC_CMD_WRITE_REG:
|
||
|
|
if (len > 2) {
|
||
|
|
pbuff = kzalloc(len, GFP_KERNEL);
|
||
|
|
if (pbuff != NULL) {
|
||
|
|
ret = copy_from_user(pbuff, buff, len);
|
||
|
|
if (ret) {
|
||
|
|
dev_err(aw8624->dev,
|
||
|
|
"%s: copy from user fail\n",
|
||
|
|
__func__);
|
||
|
|
} else {
|
||
|
|
for (i = 0; i < len - 2; i++) {
|
||
|
|
dev_info
|
||
|
|
(aw8624->dev,
|
||
|
|
"%s: write reg0x%x = 0x%x\n",
|
||
|
|
__func__,
|
||
|
|
pbuff[1]+i, pbuff[i+2]);
|
||
|
|
aw8624_i2c_write(aw8624,
|
||
|
|
pbuff[1]+i,
|
||
|
|
pbuff[2+i]);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
kfree(pbuff);
|
||
|
|
} else {
|
||
|
|
dev_err(aw8624->dev,
|
||
|
|
"%s: alloc memory fail\n",
|
||
|
|
__func__);
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
dev_err(aw8624->dev,
|
||
|
|
"%s: write cmd len %zu err\n",
|
||
|
|
__func__, len);
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
default:
|
||
|
|
dev_err(aw8624->dev,
|
||
|
|
"%s, unknown cmd %d\n",
|
||
|
|
__func__, aw8624->fileops.cmd);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
mutex_unlock(&aw8624->lock);
|
||
|
|
|
||
|
|
return len;
|
||
|
|
}
|
||
|
|
|
||
|
|
static const struct file_operations fops = {
|
||
|
|
.owner = THIS_MODULE,
|
||
|
|
.read = aw8624_file_read,
|
||
|
|
.write = aw8624_file_write,
|
||
|
|
.unlocked_ioctl = aw8624_file_unlocked_ioctl,
|
||
|
|
.open = aw8624_file_open,
|
||
|
|
.release = aw8624_file_release,
|
||
|
|
};
|
||
|
|
|
||
|
|
struct miscdevice aw8624_haptic_misc = {
|
||
|
|
.minor = MISC_DYNAMIC_MINOR,
|
||
|
|
.name = AW8624_HAPTIC_NAME,
|
||
|
|
.fops = &fops,
|
||
|
|
};
|
||
|
|
|
||
|
|
static int
|
||
|
|
aw8624_haptic_audio_ctr_list_insert(struct haptic_audio *haptic_audio,
|
||
|
|
struct haptic_ctr *haptic_ctr,
|
||
|
|
struct device *dev)
|
||
|
|
{
|
||
|
|
struct haptic_ctr *p_new = NULL;
|
||
|
|
|
||
|
|
p_new = (struct haptic_ctr *)kzalloc(
|
||
|
|
sizeof(struct haptic_ctr), GFP_KERNEL);
|
||
|
|
if (p_new == NULL) {
|
||
|
|
aw_dev_err(dev, "%s: kzalloc memory fail\n", __func__);
|
||
|
|
return -ERANGE;
|
||
|
|
}
|
||
|
|
/* update new list info */
|
||
|
|
p_new->cnt = haptic_ctr->cnt;
|
||
|
|
p_new->cmd = haptic_ctr->cmd;
|
||
|
|
p_new->play = haptic_ctr->play;
|
||
|
|
p_new->wavseq = haptic_ctr->wavseq;
|
||
|
|
p_new->loop = haptic_ctr->loop;
|
||
|
|
p_new->gain = haptic_ctr->gain;
|
||
|
|
|
||
|
|
INIT_LIST_HEAD(&(p_new->list));
|
||
|
|
list_add(&(p_new->list), &(haptic_audio->ctr_list));
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int aw8624_haptic_audio_ctr_list_clear(struct haptic_audio *haptic_audio)
|
||
|
|
{
|
||
|
|
struct haptic_ctr *p_ctr = NULL;
|
||
|
|
struct haptic_ctr *p_ctr_bak = NULL;
|
||
|
|
|
||
|
|
list_for_each_entry_safe_reverse(p_ctr, p_ctr_bak,
|
||
|
|
&(haptic_audio->ctr_list), list) {
|
||
|
|
list_del(&p_ctr->list);
|
||
|
|
kfree(p_ctr);
|
||
|
|
}
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int aw8624_haptic_audio_off(struct aw8624 *aw8624)
|
||
|
|
{
|
||
|
|
aw_dev_info(aw8624->dev, "%s: enter\n", __func__);
|
||
|
|
mutex_lock(&aw8624->lock);
|
||
|
|
aw8624_haptic_set_gain(aw8624, 0x80);
|
||
|
|
aw8624_haptic_stop(aw8624);
|
||
|
|
aw8624->gun_type = 0xff;
|
||
|
|
aw8624->bullet_nr = 0;
|
||
|
|
aw8624_haptic_audio_ctr_list_clear(&aw8624->haptic_audio);
|
||
|
|
mutex_unlock(&aw8624->lock);
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int aw8624_haptic_audio_init(struct aw8624 *aw8624)
|
||
|
|
{
|
||
|
|
|
||
|
|
aw_dev_info(aw8624->dev, "%s: enter\n", __func__);
|
||
|
|
aw8624_haptic_set_wav_seq(aw8624, 0x01, 0x00);
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int aw8624_haptic_offset_calibration(struct aw8624 *aw8624)
|
||
|
|
{
|
||
|
|
unsigned int cont = 2000;
|
||
|
|
unsigned char reg_val = 0;
|
||
|
|
unsigned char reg_val_sysctrl = 0;
|
||
|
|
|
||
|
|
aw_dev_info(aw8624->dev, "%s enter\n", __func__);
|
||
|
|
aw8624_i2c_read(aw8624, AW8624_REG_SYSCTRL, ®_val_sysctrl);
|
||
|
|
aw8624_i2c_write_bits(aw8624,
|
||
|
|
AW8624_REG_SYSCTRL,
|
||
|
|
AW8624_BIT_SYSCTRL_RAMINIT_MASK,
|
||
|
|
AW8624_BIT_SYSCTRL_RAMINIT_EN);
|
||
|
|
aw8624_i2c_write_bits(aw8624,
|
||
|
|
AW8624_REG_DETCTRL,
|
||
|
|
AW8624_BIT_DETCTRL_DIAG_GO_MASK,
|
||
|
|
AW8624_BIT_DETCTRL_DIAG_GO_ENABLE);
|
||
|
|
while (1) {
|
||
|
|
aw8624_i2c_read(aw8624, AW8624_REG_DETCTRL, ®_val);
|
||
|
|
if ((reg_val & 0x01) == 0 || cont == 0)
|
||
|
|
break;
|
||
|
|
cont--;
|
||
|
|
}
|
||
|
|
if (cont == 0)
|
||
|
|
aw_dev_err(aw8624->dev, "%s calibration offset failed!\n",
|
||
|
|
__func__);
|
||
|
|
|
||
|
|
aw8624_i2c_write(aw8624, AW8624_REG_SYSCTRL, reg_val_sysctrl);
|
||
|
|
return 0;
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
int aw8624_haptic_init(struct aw8624 *aw8624)
|
||
|
|
{
|
||
|
|
int ret = 0;
|
||
|
|
unsigned char i = 0;
|
||
|
|
unsigned char reg_val = 0, reg_flag = 0;
|
||
|
|
unsigned char bemf_config = 0;
|
||
|
|
|
||
|
|
ret = misc_register(&aw8624_haptic_misc);
|
||
|
|
if (ret) {
|
||
|
|
dev_err(aw8624->dev, "%s: misc fail: %d\n", __func__, ret);
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* haptic audio */
|
||
|
|
aw8624->haptic_audio.delay_val = 23;
|
||
|
|
aw8624->haptic_audio.timer_val = 23;
|
||
|
|
INIT_LIST_HEAD(&(aw8624->haptic_audio.ctr_list));
|
||
|
|
hrtimer_init(&aw8624->haptic_audio.timer,
|
||
|
|
CLOCK_MONOTONIC, HRTIMER_MODE_REL);
|
||
|
|
aw8624->haptic_audio.timer.function = aw8624_haptic_audio_timer_func;
|
||
|
|
INIT_WORK(&aw8624->haptic_audio.work, aw8624_haptic_audio_work_routine);
|
||
|
|
aw8624->gun_type = 0xff;
|
||
|
|
aw8624->bullet_nr = 0x00;
|
||
|
|
mutex_init(&aw8624->haptic_audio.lock);
|
||
|
|
/* haptic init */
|
||
|
|
mutex_lock(&aw8624->lock);
|
||
|
|
ret = aw8624_i2c_read(aw8624, AW8624_REG_EF_RDATAH, ®_flag);
|
||
|
|
if ((ret >= 0) && ((reg_flag & 0x1) == 1)) {
|
||
|
|
aw8624->chipid_flag = 1;
|
||
|
|
} else {
|
||
|
|
dev_err(aw8624->dev,
|
||
|
|
"%s: to read register AW8624_REG_EF_RDATAH: %d\n",
|
||
|
|
__func__, ret);
|
||
|
|
}
|
||
|
|
|
||
|
|
aw8624->activate_mode = aw8624_dts_data.aw8624_mode;
|
||
|
|
ret = aw8624_i2c_read(aw8624, AW8624_REG_WAVSEQ1, ®_val);
|
||
|
|
aw8624->index = reg_val & 0x7F;
|
||
|
|
ret = aw8624_i2c_read(aw8624, AW8624_REG_DATDBG, ®_val);
|
||
|
|
aw8624->gain = reg_val & 0xFF;
|
||
|
|
for (i = 0; i < AW8624_SEQUENCER_SIZE; i++) {
|
||
|
|
ret = aw8624_i2c_read(aw8624, AW8624_REG_WAVSEQ1+i, ®_val);
|
||
|
|
aw8624->seq[i] = reg_val;
|
||
|
|
}
|
||
|
|
|
||
|
|
aw8624_haptic_play_mode(aw8624, AW8624_HAPTIC_STANDBY_MODE);
|
||
|
|
aw8624_haptic_set_pwm(aw8624, AW8624_PWM_24K);
|
||
|
|
|
||
|
|
aw8624_haptic_swicth_motorprotect_config(aw8624, 0x0, 0x0);
|
||
|
|
/*trig config*/
|
||
|
|
aw8624_haptic_trig1_param_init(aw8624);
|
||
|
|
aw8624_haptic_tirg1_param_config(aw8624);
|
||
|
|
aw8624_haptic_offset_calibration(aw8624);
|
||
|
|
aw8624_haptic_vbat_mode(aw8624, AW8624_HAPTIC_VBAT_HW_COMP_MODE);
|
||
|
|
mutex_unlock(&aw8624->lock);
|
||
|
|
|
||
|
|
/* f0 calibration */
|
||
|
|
aw8624->f0_pre = aw8624_dts_data.aw8624_f0_pre;
|
||
|
|
aw8624->cont_drv_lvl = aw8624_dts_data.aw8624_cont_drv_lvl;
|
||
|
|
aw8624->cont_drv_lvl_ov = aw8624_dts_data.aw8624_cont_drv_lvl_ov;
|
||
|
|
aw8624->cont_td = aw8624_dts_data.aw8624_cont_td;
|
||
|
|
aw8624->cont_zc_thr = aw8624_dts_data.aw8624_cont_zc_thr;
|
||
|
|
aw8624->cont_num_brk = aw8624_dts_data.aw8624_cont_num_brk;
|
||
|
|
aw8624->ram_vbat_comp = AW8624_HAPTIC_RAM_VBAT_COMP_ENABLE;
|
||
|
|
mutex_lock(&aw8624->lock);
|
||
|
|
aw8624_i2c_write_bits(aw8624, AW8624_REG_R_SPARE,
|
||
|
|
AW8624_BIT_R_SPARE_MASK, AW8624_BIT_R_SPARE_ENABLE);
|
||
|
|
/*LRA trim source select register*/
|
||
|
|
aw8624_i2c_write_bits(aw8624,
|
||
|
|
AW8624_REG_ANACTRL,
|
||
|
|
AW8624_BIT_ANACTRL_LRA_SRC_MASK,
|
||
|
|
AW8624_BIT_ANACTRL_LRA_SRC_REG);
|
||
|
|
aw8624_haptic_upload_lra(aw8624, AW8624_HAPTIC_ZERO);
|
||
|
|
aw8624_haptic_f0_calibration(aw8624);
|
||
|
|
mutex_unlock(&aw8624->lock);
|
||
|
|
|
||
|
|
/*brake*/
|
||
|
|
mutex_lock(&aw8624->lock);
|
||
|
|
aw8624_i2c_write(aw8624,
|
||
|
|
AW8624_REG_SW_BRAKE,
|
||
|
|
(unsigned char)(aw8624_dts_data.aw8624_sw_brake[0]));
|
||
|
|
aw8624_i2c_write(aw8624, AW8624_REG_THRS_BRA_END, 0x00);
|
||
|
|
aw8624_i2c_write_bits(aw8624,
|
||
|
|
AW8624_REG_WAVECTRL,
|
||
|
|
AW8624_BIT_WAVECTRL_NUM_OV_DRIVER_MASK,
|
||
|
|
AW8624_BIT_WAVECTRL_NUM_OV_DRIVER);
|
||
|
|
aw8624->f0_value = 20000 / aw8624_dts_data.aw8624_f0_pre + 1;
|
||
|
|
/* zero cross */
|
||
|
|
aw8624_i2c_write(aw8624,
|
||
|
|
AW8624_REG_ZC_THRSH_H,
|
||
|
|
(unsigned char)(aw8624->cont_zc_thr>>8));
|
||
|
|
aw8624_i2c_write(aw8624,
|
||
|
|
AW8624_REG_ZC_THRSH_L,
|
||
|
|
(unsigned char)(aw8624->cont_zc_thr>>0));
|
||
|
|
aw8624_i2c_write(aw8624,
|
||
|
|
AW8624_REG_TSET,
|
||
|
|
aw8624_dts_data.aw8624_tset);
|
||
|
|
|
||
|
|
/* bemf */
|
||
|
|
bemf_config = aw8624_dts_data.aw8624_bemf_config[0];
|
||
|
|
aw8624_i2c_write(aw8624, AW8624_REG_BEMF_VTHH_H, bemf_config);
|
||
|
|
bemf_config = aw8624_dts_data.aw8624_bemf_config[1];
|
||
|
|
aw8624_i2c_write(aw8624, AW8624_REG_BEMF_VTHH_L, bemf_config);
|
||
|
|
bemf_config = aw8624_dts_data.aw8624_bemf_config[2];
|
||
|
|
aw8624_i2c_write(aw8624, AW8624_REG_BEMF_VTHL_H, bemf_config);
|
||
|
|
bemf_config = aw8624_dts_data.aw8624_bemf_config[3];
|
||
|
|
aw8624_i2c_write(aw8624, AW8624_REG_BEMF_VTHL_L, bemf_config);
|
||
|
|
mutex_unlock(&aw8624->lock);
|
||
|
|
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
/*****************************************************
|
||
|
|
*
|
||
|
|
* vibrator
|
||
|
|
*
|
||
|
|
*****************************************************/
|
||
|
|
#ifdef TIMED_OUTPUT
|
||
|
|
static int aw8624_vibrator_get_time(struct timed_output_dev *to_dev)
|
||
|
|
{
|
||
|
|
struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev);
|
||
|
|
|
||
|
|
if (hrtimer_active(&aw8624->timer)) {
|
||
|
|
ktime_t r = hrtimer_get_remaining(&aw8624->timer);
|
||
|
|
|
||
|
|
return ktime_to_ms(r);
|
||
|
|
}
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static void aw8624_vibrator_enable(struct timed_output_dev *to_dev, int value)
|
||
|
|
{
|
||
|
|
struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev);
|
||
|
|
|
||
|
|
aw_dev_info(aw8624->dev, "%s enter\n", __func__);
|
||
|
|
if (!aw8624->ram_init) {
|
||
|
|
aw_dev_err(aw8624->dev, "%s: ram init failed, not allow to play!\n",
|
||
|
|
__func__);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
mutex_lock(&aw8624->lock);
|
||
|
|
aw8624_haptic_stop(aw8624);
|
||
|
|
if (value > 0) {
|
||
|
|
aw8624_haptic_upload_lra(aw8624, AW8624_HAPTIC_F0_CALI_LRA);
|
||
|
|
aw8624_haptic_ram_vbat_comp(aw8624, false);
|
||
|
|
aw8624_haptic_play_wav_seq(aw8624, value);
|
||
|
|
}
|
||
|
|
|
||
|
|
mutex_unlock(&aw8624->lock);
|
||
|
|
}
|
||
|
|
|
||
|
|
#else
|
||
|
|
static void
|
||
|
|
aw8624_vibrator_enable(struct led_classdev *dev, enum led_brightness value)
|
||
|
|
{
|
||
|
|
struct aw8624 *aw8624 = container_of(dev, struct aw8624, cdev);
|
||
|
|
|
||
|
|
aw_dev_info(aw8624->dev, "%s enter\n", __func__);
|
||
|
|
if (!aw8624->ram_init) {
|
||
|
|
aw_dev_err(aw8624->dev, "%s: ram init failed, not allow to play!\n",
|
||
|
|
__func__);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
mutex_lock(&aw8624->lock);
|
||
|
|
aw8624_haptic_stop(aw8624);
|
||
|
|
if (value > 0) {
|
||
|
|
aw8624_haptic_upload_lra(aw8624, AW8624_HAPTIC_F0_CALI_LRA);
|
||
|
|
aw8624_haptic_ram_vbat_comp(aw8624, false);
|
||
|
|
aw8624_haptic_play_wav_seq(aw8624, value);
|
||
|
|
}
|
||
|
|
|
||
|
|
mutex_unlock(&aw8624->lock);
|
||
|
|
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
#endif
|
||
|
|
|
||
|
|
static ssize_t aw8624_state_show(struct device *dev,
|
||
|
|
struct device_attribute *attr, char *buf)
|
||
|
|
{
|
||
|
|
#ifdef TIMED_OUTPUT
|
||
|
|
struct timed_output_dev *to_dev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev);
|
||
|
|
#else
|
||
|
|
struct led_classdev *cdev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(cdev, struct aw8624, cdev);
|
||
|
|
#endif
|
||
|
|
|
||
|
|
|
||
|
|
return snprintf(buf, PAGE_SIZE, "%d\n", aw8624->state);
|
||
|
|
}
|
||
|
|
|
||
|
|
static ssize_t aw8624_state_store(struct device *dev,
|
||
|
|
struct device_attribute *attr, const char *buf, size_t count)
|
||
|
|
{
|
||
|
|
return count;
|
||
|
|
}
|
||
|
|
|
||
|
|
static ssize_t aw8624_duration_show(struct device *dev,
|
||
|
|
struct device_attribute *attr, char *buf)
|
||
|
|
{
|
||
|
|
#ifdef TIMED_OUTPUT
|
||
|
|
struct timed_output_dev *to_dev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev);
|
||
|
|
#else
|
||
|
|
struct led_classdev *cdev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(cdev, struct aw8624, cdev);
|
||
|
|
#endif
|
||
|
|
ktime_t time_rem;
|
||
|
|
s64 time_ms = 0;
|
||
|
|
|
||
|
|
if (hrtimer_active(&aw8624->timer)) {
|
||
|
|
time_rem = hrtimer_get_remaining(&aw8624->timer);
|
||
|
|
time_ms = ktime_to_ms(time_rem);
|
||
|
|
}
|
||
|
|
|
||
|
|
return snprintf(buf, PAGE_SIZE, "%lld\n", time_ms);
|
||
|
|
}
|
||
|
|
|
||
|
|
static ssize_t aw8624_duration_store(struct device *dev,
|
||
|
|
struct device_attribute *attr, const char *buf, size_t count)
|
||
|
|
{
|
||
|
|
#ifdef TIMED_OUTPUT
|
||
|
|
struct timed_output_dev *to_dev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev);
|
||
|
|
#else
|
||
|
|
struct led_classdev *cdev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(cdev, struct aw8624, cdev);
|
||
|
|
#endif
|
||
|
|
unsigned int val = 0;
|
||
|
|
int rc = 0;
|
||
|
|
|
||
|
|
rc = kstrtouint(buf, 0, &val);
|
||
|
|
if (rc < 0)
|
||
|
|
return rc;
|
||
|
|
|
||
|
|
/* setting 0 on duration is NOP for now */
|
||
|
|
if (val <= 0)
|
||
|
|
return count;
|
||
|
|
|
||
|
|
if (val <= aw8624->f0_value)
|
||
|
|
val = aw8624->f0_value;
|
||
|
|
rc = aw8624_haptic_ram_config(aw8624, val);
|
||
|
|
if (rc < 0)
|
||
|
|
return rc;
|
||
|
|
aw8624->duration = val;
|
||
|
|
return count;
|
||
|
|
}
|
||
|
|
|
||
|
|
static ssize_t aw8624_activate_show(struct device *dev,
|
||
|
|
struct device_attribute *attr, char *buf)
|
||
|
|
{
|
||
|
|
#ifdef TIMED_OUTPUT
|
||
|
|
struct timed_output_dev *to_dev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev);
|
||
|
|
#else
|
||
|
|
struct led_classdev *cdev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(cdev, struct aw8624, cdev);
|
||
|
|
#endif
|
||
|
|
|
||
|
|
/* For now nothing to show */
|
||
|
|
return snprintf(buf, PAGE_SIZE, "%d\n", aw8624->state);
|
||
|
|
}
|
||
|
|
|
||
|
|
static ssize_t aw8624_activate_store(struct device *dev,
|
||
|
|
struct device_attribute *attr, const char *buf, size_t count)
|
||
|
|
{
|
||
|
|
#ifdef TIMED_OUTPUT
|
||
|
|
struct timed_output_dev *to_dev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev);
|
||
|
|
#else
|
||
|
|
struct led_classdev *cdev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(cdev, struct aw8624, cdev);
|
||
|
|
#endif
|
||
|
|
unsigned int val = 0;
|
||
|
|
int rc = 0;
|
||
|
|
|
||
|
|
if (!aw8624->ram_init) {
|
||
|
|
aw_dev_err(aw8624->dev, "%s: ram init failed, not allow to play!\n",
|
||
|
|
__func__);
|
||
|
|
return count;
|
||
|
|
}
|
||
|
|
rc = kstrtouint(buf, 0, &val);
|
||
|
|
if (rc < 0)
|
||
|
|
return rc;
|
||
|
|
|
||
|
|
if (val != 0 && val != 1)
|
||
|
|
return count;
|
||
|
|
|
||
|
|
aw_dev_info(aw8624->dev, "%s: value=%d\n", __func__, val);
|
||
|
|
|
||
|
|
mutex_lock(&aw8624->lock);
|
||
|
|
hrtimer_cancel(&aw8624->timer);
|
||
|
|
|
||
|
|
aw8624->state = val;
|
||
|
|
|
||
|
|
/*aw8624_haptic_stop(aw8624);*/
|
||
|
|
|
||
|
|
mutex_unlock(&aw8624->lock);
|
||
|
|
schedule_work(&aw8624->vibrator_work);
|
||
|
|
|
||
|
|
return count;
|
||
|
|
}
|
||
|
|
|
||
|
|
static ssize_t aw8624_activate_mode_show(struct device *dev,
|
||
|
|
struct device_attribute *attr, char *buf)
|
||
|
|
{
|
||
|
|
#ifdef TIMED_OUTPUT
|
||
|
|
struct timed_output_dev *to_dev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev);
|
||
|
|
#else
|
||
|
|
struct led_classdev *cdev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(cdev, struct aw8624, cdev);
|
||
|
|
#endif
|
||
|
|
|
||
|
|
return snprintf(buf, PAGE_SIZE, "%d\n",
|
||
|
|
aw8624->activate_mode);
|
||
|
|
}
|
||
|
|
|
||
|
|
static ssize_t aw8624_activate_mode_store(struct device *dev,
|
||
|
|
struct device_attribute *attr, const char *buf, size_t count)
|
||
|
|
{
|
||
|
|
#ifdef TIMED_OUTPUT
|
||
|
|
struct timed_output_dev *to_dev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev);
|
||
|
|
#else
|
||
|
|
struct led_classdev *cdev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(cdev, struct aw8624, cdev);
|
||
|
|
#endif
|
||
|
|
|
||
|
|
unsigned int val = 0;
|
||
|
|
int rc = 0;
|
||
|
|
|
||
|
|
rc = kstrtouint(buf, 0, &val);
|
||
|
|
if (rc < 0)
|
||
|
|
return rc;
|
||
|
|
|
||
|
|
mutex_lock(&aw8624->lock);
|
||
|
|
aw8624->activate_mode = val;
|
||
|
|
mutex_unlock(&aw8624->lock);
|
||
|
|
return count;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
static ssize_t aw8624_index_show(struct device *dev,
|
||
|
|
struct device_attribute *attr, char *buf)
|
||
|
|
{
|
||
|
|
#ifdef TIMED_OUTPUT
|
||
|
|
struct timed_output_dev *to_dev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev);
|
||
|
|
#else
|
||
|
|
struct led_classdev *cdev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(cdev, struct aw8624, cdev);
|
||
|
|
#endif
|
||
|
|
|
||
|
|
unsigned char reg_val = 0;
|
||
|
|
|
||
|
|
aw8624_i2c_read(aw8624, AW8624_REG_WAVSEQ1, ®_val);
|
||
|
|
aw8624->index = reg_val;
|
||
|
|
|
||
|
|
return snprintf(buf, PAGE_SIZE, "%d\n", aw8624->index);
|
||
|
|
}
|
||
|
|
|
||
|
|
static ssize_t aw8624_index_store(struct device *dev,
|
||
|
|
struct device_attribute *attr, const char *buf, size_t count)
|
||
|
|
{
|
||
|
|
#ifdef TIMED_OUTPUT
|
||
|
|
struct timed_output_dev *to_dev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev);
|
||
|
|
#else
|
||
|
|
struct led_classdev *cdev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(cdev, struct aw8624, cdev);
|
||
|
|
#endif
|
||
|
|
unsigned int val = 0;
|
||
|
|
int rc = 0;
|
||
|
|
|
||
|
|
rc = kstrtouint(buf, 0, &val);
|
||
|
|
if (rc < 0)
|
||
|
|
return rc;
|
||
|
|
if (val > aw8624->ram.ram_num) {
|
||
|
|
aw_dev_err(aw8624->dev,
|
||
|
|
"%s: input value out of range!\n", __func__);
|
||
|
|
return count;
|
||
|
|
}
|
||
|
|
aw_dev_info(aw8624->dev, "%s: value=%d\n", __func__, val);
|
||
|
|
|
||
|
|
mutex_lock(&aw8624->lock);
|
||
|
|
aw8624->index = val;
|
||
|
|
aw8624_haptic_set_repeat_wav_seq(aw8624, aw8624->index);
|
||
|
|
mutex_unlock(&aw8624->lock);
|
||
|
|
return count;
|
||
|
|
}
|
||
|
|
|
||
|
|
static ssize_t aw8624_gain_show(struct device *dev,
|
||
|
|
struct device_attribute *attr, char *buf)
|
||
|
|
{
|
||
|
|
#ifdef TIMED_OUTPUT
|
||
|
|
struct timed_output_dev *to_dev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev);
|
||
|
|
#else
|
||
|
|
struct led_classdev *cdev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(cdev, struct aw8624, cdev);
|
||
|
|
#endif
|
||
|
|
|
||
|
|
return snprintf(buf, PAGE_SIZE, "0x%02x\n", aw8624->gain);
|
||
|
|
}
|
||
|
|
|
||
|
|
static ssize_t aw8624_gain_store(struct device *dev,
|
||
|
|
struct device_attribute *attr, const char *buf, size_t count)
|
||
|
|
{
|
||
|
|
#ifdef TIMED_OUTPUT
|
||
|
|
struct timed_output_dev *to_dev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev);
|
||
|
|
#else
|
||
|
|
struct led_classdev *cdev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(cdev, struct aw8624, cdev);
|
||
|
|
#endif
|
||
|
|
unsigned int val = 0;
|
||
|
|
int rc = 0;
|
||
|
|
|
||
|
|
rc = kstrtouint(buf, 0, &val);
|
||
|
|
if (rc < 0)
|
||
|
|
return rc;
|
||
|
|
|
||
|
|
aw_dev_info(aw8624->dev, "%s: value=%d\n", __func__, val);
|
||
|
|
|
||
|
|
mutex_lock(&aw8624->lock);
|
||
|
|
aw8624->gain = val;
|
||
|
|
aw8624_haptic_set_gain(aw8624, aw8624->gain);
|
||
|
|
mutex_unlock(&aw8624->lock);
|
||
|
|
return count;
|
||
|
|
}
|
||
|
|
|
||
|
|
static ssize_t aw8624_seq_show(struct device *dev,
|
||
|
|
struct device_attribute *attr, char *buf)
|
||
|
|
{
|
||
|
|
#ifdef TIMED_OUTPUT
|
||
|
|
struct timed_output_dev *to_dev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev);
|
||
|
|
#else
|
||
|
|
struct led_classdev *cdev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(cdev, struct aw8624, cdev);
|
||
|
|
#endif
|
||
|
|
size_t count = 0;
|
||
|
|
unsigned char i = 0;
|
||
|
|
unsigned char reg_val = 0;
|
||
|
|
|
||
|
|
for (i = 0; i < AW8624_SEQUENCER_SIZE; i++) {
|
||
|
|
aw8624_i2c_read(aw8624, AW8624_REG_WAVSEQ1+i, ®_val);
|
||
|
|
count += snprintf(buf+count, PAGE_SIZE-count,
|
||
|
|
"seq%d: 0x%02x\n", i+1, reg_val);
|
||
|
|
aw8624->seq[i] |= reg_val;
|
||
|
|
}
|
||
|
|
return count;
|
||
|
|
}
|
||
|
|
|
||
|
|
static ssize_t aw8624_seq_store(struct device *dev,
|
||
|
|
struct device_attribute *attr, const char *buf, size_t count)
|
||
|
|
{
|
||
|
|
#ifdef TIMED_OUTPUT
|
||
|
|
struct timed_output_dev *to_dev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev);
|
||
|
|
#else
|
||
|
|
struct led_classdev *cdev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(cdev, struct aw8624, cdev);
|
||
|
|
#endif
|
||
|
|
unsigned int databuf[2] = {0, 0};
|
||
|
|
|
||
|
|
if (sscanf(buf, "%x %x", &databuf[0], &databuf[1]) == 2) {
|
||
|
|
if (databuf[0] > AW8624_SEQUENCER_SIZE ||
|
||
|
|
databuf[1] > aw8624->ram.ram_num) {
|
||
|
|
aw_dev_err(aw8624->dev, "%s input value out of range\n",
|
||
|
|
__func__);
|
||
|
|
return count;
|
||
|
|
}
|
||
|
|
aw_dev_info(aw8624->dev, "%s: seq%d=0x%x\n",
|
||
|
|
__func__, databuf[0], databuf[1]);
|
||
|
|
mutex_lock(&aw8624->lock);
|
||
|
|
aw8624->seq[databuf[0]] = (unsigned char)databuf[1];
|
||
|
|
aw8624_haptic_set_wav_seq(aw8624, (unsigned char)databuf[0],
|
||
|
|
aw8624->seq[databuf[0]]);
|
||
|
|
mutex_unlock(&aw8624->lock);
|
||
|
|
}
|
||
|
|
return count;
|
||
|
|
}
|
||
|
|
|
||
|
|
static ssize_t aw8624_loop_show(struct device *dev,
|
||
|
|
struct device_attribute *attr, char *buf)
|
||
|
|
{
|
||
|
|
#ifdef TIMED_OUTPUT
|
||
|
|
struct timed_output_dev *to_dev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev);
|
||
|
|
#else
|
||
|
|
struct led_classdev *cdev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(cdev, struct aw8624, cdev);
|
||
|
|
#endif
|
||
|
|
|
||
|
|
size_t count = 0;
|
||
|
|
unsigned char i = 0;
|
||
|
|
unsigned char reg_val = 0;
|
||
|
|
|
||
|
|
for (i = 0; i < AW8624_SEQUENCER_LOOP_SIZE; i++) {
|
||
|
|
aw8624_i2c_read(aw8624, AW8624_REG_WAVLOOP1+i, ®_val);
|
||
|
|
aw8624->loop[i*2+0] = (reg_val>>4)&0x0F;
|
||
|
|
aw8624->loop[i*2+1] = (reg_val>>0)&0x0F;
|
||
|
|
|
||
|
|
count += snprintf(buf+count, PAGE_SIZE-count,
|
||
|
|
"seq%d_loop: 0x%02x\n", i*2+1, aw8624->loop[i*2+0]);
|
||
|
|
count += snprintf(buf+count, PAGE_SIZE-count,
|
||
|
|
"seq%d_loop: 0x%02x\n", i*2+2, aw8624->loop[i*2+1]);
|
||
|
|
}
|
||
|
|
return count;
|
||
|
|
}
|
||
|
|
|
||
|
|
static ssize_t aw8624_loop_store(struct device *dev,
|
||
|
|
struct device_attribute *attr, const char *buf, size_t count)
|
||
|
|
{
|
||
|
|
#ifdef TIMED_OUTPUT
|
||
|
|
struct timed_output_dev *to_dev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev);
|
||
|
|
#else
|
||
|
|
struct led_classdev *cdev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(cdev, struct aw8624, cdev);
|
||
|
|
#endif
|
||
|
|
unsigned int databuf[2] = {0, 0};
|
||
|
|
|
||
|
|
if (sscanf(buf, "%x %x", &databuf[0], &databuf[1]) == 2) {
|
||
|
|
aw_dev_info(aw8624->dev, "%s: seq%d loop=0x%x\n",
|
||
|
|
__func__, databuf[0], databuf[1]);
|
||
|
|
mutex_lock(&aw8624->lock);
|
||
|
|
aw8624->loop[databuf[0]] = (unsigned char)databuf[1];
|
||
|
|
aw8624_haptic_set_wav_loop(aw8624, (unsigned char)databuf[0],
|
||
|
|
aw8624->loop[databuf[0]]);
|
||
|
|
mutex_unlock(&aw8624->lock);
|
||
|
|
}
|
||
|
|
|
||
|
|
return count;
|
||
|
|
}
|
||
|
|
|
||
|
|
static ssize_t aw8624_reg_show(struct device *dev,
|
||
|
|
struct device_attribute *attr, char *buf)
|
||
|
|
{
|
||
|
|
#ifdef TIMED_OUTPUT
|
||
|
|
struct timed_output_dev *to_dev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev);
|
||
|
|
#else
|
||
|
|
struct led_classdev *cdev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(cdev, struct aw8624, cdev);
|
||
|
|
#endif
|
||
|
|
|
||
|
|
ssize_t len = 0;
|
||
|
|
unsigned char i = 0;
|
||
|
|
unsigned char reg_val = 0;
|
||
|
|
|
||
|
|
aw_dev_info(aw8624->dev, "%s enter\n", __func__);
|
||
|
|
for (i = 0; i < AW8624_REG_MAX; i++) {
|
||
|
|
if (!(aw8624_reg_access[i]®_RD_ACCESS))
|
||
|
|
continue;
|
||
|
|
aw8624_i2c_read(aw8624, i, ®_val);
|
||
|
|
len += snprintf(buf+len, PAGE_SIZE-len, "reg:0x%02x=0x%02x\n",
|
||
|
|
i, reg_val);
|
||
|
|
}
|
||
|
|
return len;
|
||
|
|
}
|
||
|
|
|
||
|
|
static ssize_t aw8624_reg_store(struct device *dev,
|
||
|
|
struct device_attribute *attr, const char *buf, size_t count)
|
||
|
|
{
|
||
|
|
#ifdef TIMED_OUTPUT
|
||
|
|
struct timed_output_dev *to_dev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev);
|
||
|
|
#else
|
||
|
|
struct led_classdev *cdev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(cdev, struct aw8624, cdev);
|
||
|
|
#endif
|
||
|
|
|
||
|
|
unsigned int databuf[2] = {0, 0};
|
||
|
|
|
||
|
|
if (sscanf(buf, "%x %x", &databuf[0], &databuf[1]) == 2) {
|
||
|
|
aw8624_i2c_write(aw8624,
|
||
|
|
(unsigned char)databuf[0],
|
||
|
|
(unsigned char)databuf[1]);
|
||
|
|
}
|
||
|
|
|
||
|
|
return count;
|
||
|
|
}
|
||
|
|
|
||
|
|
static ssize_t aw8624_rtp_show(struct device *dev,
|
||
|
|
struct device_attribute *attr, char *buf)
|
||
|
|
{
|
||
|
|
#ifdef TIMED_OUTPUT
|
||
|
|
struct timed_output_dev *to_dev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev);
|
||
|
|
#else
|
||
|
|
struct led_classdev *cdev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(cdev, struct aw8624, cdev);
|
||
|
|
#endif
|
||
|
|
|
||
|
|
ssize_t len = 0;
|
||
|
|
|
||
|
|
len += snprintf(buf+len, PAGE_SIZE-len, "rtp_cnt = %d\n",
|
||
|
|
aw8624->rtp_cnt);
|
||
|
|
|
||
|
|
return len;
|
||
|
|
}
|
||
|
|
|
||
|
|
static ssize_t aw8624_rtp_store(struct device *dev,
|
||
|
|
struct device_attribute *attr, const char *buf, size_t count)
|
||
|
|
{
|
||
|
|
#ifdef TIMED_OUTPUT
|
||
|
|
struct timed_output_dev *to_dev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev);
|
||
|
|
#else
|
||
|
|
struct led_classdev *cdev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(cdev, struct aw8624, cdev);
|
||
|
|
#endif
|
||
|
|
|
||
|
|
|
||
|
|
unsigned int val = 0;
|
||
|
|
int rc = 0;
|
||
|
|
|
||
|
|
if (!(aw8624->IsUsedIRQ))
|
||
|
|
return rc;
|
||
|
|
rc = kstrtouint(buf, 0, &val);
|
||
|
|
if (rc < 0)
|
||
|
|
return rc;
|
||
|
|
|
||
|
|
mutex_lock(&aw8624->lock);
|
||
|
|
aw8624_haptic_stop(aw8624);
|
||
|
|
aw8624_haptic_set_rtp_aei(aw8624, false);
|
||
|
|
aw8624_interrupt_clear(aw8624);
|
||
|
|
mutex_unlock(&aw8624->lock);
|
||
|
|
if (val < (sizeof(aw8624_rtp_name)/AW8624_RTP_NAME_MAX)) {
|
||
|
|
aw8624->rtp_file_num = val;
|
||
|
|
if (val) {
|
||
|
|
aw_dev_info(aw8624->dev,
|
||
|
|
"%s: aw8624_rtp_name[%d]: %s\n", __func__,
|
||
|
|
val, aw8624_rtp_name[val]);
|
||
|
|
schedule_work(&aw8624->rtp_work);
|
||
|
|
} else
|
||
|
|
aw_dev_err(aw8624->dev,
|
||
|
|
"%s: rtp_file_num 0x%02X over max value\n",
|
||
|
|
__func__, aw8624->rtp_file_num);
|
||
|
|
} else {
|
||
|
|
aw_dev_err(aw8624->dev, "%s: rtp_file_num 0x%02x over max value\n",
|
||
|
|
__func__, aw8624->rtp_file_num);
|
||
|
|
}
|
||
|
|
|
||
|
|
return count;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
static ssize_t aw8624_ram_update_show(struct device *dev,
|
||
|
|
struct device_attribute *attr, char *buf)
|
||
|
|
{
|
||
|
|
#ifdef TIMED_OUTPUT
|
||
|
|
struct timed_output_dev *to_dev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev);
|
||
|
|
#else
|
||
|
|
struct led_classdev *cdev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(cdev, struct aw8624, cdev);
|
||
|
|
#endif
|
||
|
|
ssize_t len = 0;
|
||
|
|
unsigned char reg_val_sysctrl = 0;
|
||
|
|
unsigned char reg_val = 0;
|
||
|
|
unsigned int i = 0;
|
||
|
|
|
||
|
|
aw8624_i2c_read(aw8624, AW8624_REG_SYSCTRL, ®_val_sysctrl);
|
||
|
|
/* RAMINIT Enable */
|
||
|
|
aw8624_i2c_write_bits(aw8624,
|
||
|
|
AW8624_REG_SYSCTRL,
|
||
|
|
AW8624_BIT_SYSCTRL_RAMINIT_MASK,
|
||
|
|
AW8624_BIT_SYSCTRL_RAMINIT_EN);
|
||
|
|
aw8624_i2c_write(aw8624, AW8624_REG_RAMADDRH,
|
||
|
|
(unsigned char)(aw8624->ram.base_addr >> 8));
|
||
|
|
aw8624_i2c_write(aw8624, AW8624_REG_RAMADDRL,
|
||
|
|
(unsigned char)(aw8624->ram.base_addr & 0x00ff));
|
||
|
|
len += snprintf(buf + len, PAGE_SIZE - len,
|
||
|
|
"haptic_ram len = %d\n", aw8624->ram.len);
|
||
|
|
for (i = 0; i < aw8624->ram.len; i++) {
|
||
|
|
aw8624_i2c_read(aw8624, AW8624_REG_RAMDATA, ®_val);
|
||
|
|
if (i % 5 == 0)
|
||
|
|
len += snprintf(buf + len,
|
||
|
|
PAGE_SIZE - len,
|
||
|
|
"0x%02X\n", reg_val);
|
||
|
|
else
|
||
|
|
len += snprintf(buf + len,
|
||
|
|
PAGE_SIZE - len,
|
||
|
|
"0x%02X,", reg_val);
|
||
|
|
}
|
||
|
|
len += snprintf(buf + len, PAGE_SIZE - len, "\n");
|
||
|
|
/* RAMINIT Disable */
|
||
|
|
aw8624_i2c_write(aw8624, AW8624_REG_SYSCTRL, reg_val_sysctrl);
|
||
|
|
return len;
|
||
|
|
}
|
||
|
|
|
||
|
|
static ssize_t aw8624_ram_update_store(struct device *dev,
|
||
|
|
struct device_attribute *attr, const char *buf, size_t count)
|
||
|
|
{
|
||
|
|
#ifdef TIMED_OUTPUT
|
||
|
|
struct timed_output_dev *to_dev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev);
|
||
|
|
#else
|
||
|
|
struct led_classdev *cdev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(cdev, struct aw8624, cdev);
|
||
|
|
#endif
|
||
|
|
|
||
|
|
|
||
|
|
unsigned int val = 0;
|
||
|
|
int rc = 0;
|
||
|
|
|
||
|
|
rc = kstrtouint(buf, 0, &val);
|
||
|
|
if (rc < 0)
|
||
|
|
return rc;
|
||
|
|
|
||
|
|
if (val)
|
||
|
|
aw8624_ram_update(aw8624);
|
||
|
|
|
||
|
|
return count;
|
||
|
|
}
|
||
|
|
|
||
|
|
static ssize_t aw8624_f0_show(struct device *dev, struct device_attribute *attr,
|
||
|
|
char *buf)
|
||
|
|
{
|
||
|
|
#ifdef TIMED_OUTPUT
|
||
|
|
struct timed_output_dev *to_dev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev);
|
||
|
|
#else
|
||
|
|
struct led_classdev *cdev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(cdev, struct aw8624, cdev);
|
||
|
|
#endif
|
||
|
|
|
||
|
|
|
||
|
|
ssize_t len = 0;
|
||
|
|
unsigned char temp = 0;
|
||
|
|
|
||
|
|
mutex_lock(&aw8624->lock);
|
||
|
|
aw8624_i2c_read(aw8624, AW8624_REG_TRIM_LRA, &temp);
|
||
|
|
aw8624_haptic_upload_lra(aw8624, AW8624_HAPTIC_ZERO);
|
||
|
|
aw8624_haptic_get_f0(aw8624);
|
||
|
|
aw8624_i2c_write(aw8624, AW8624_REG_TRIM_LRA, temp);
|
||
|
|
mutex_unlock(&aw8624->lock);
|
||
|
|
len += snprintf(buf+len, PAGE_SIZE-len, "%d\n", aw8624->f0);
|
||
|
|
aw_dev_info(aw8624->dev, "len = %zd, buf=%s", len, buf);
|
||
|
|
return len;
|
||
|
|
}
|
||
|
|
|
||
|
|
static ssize_t aw8624_f0_store(struct device *dev,
|
||
|
|
struct device_attribute *attr, const char *buf, size_t count)
|
||
|
|
{
|
||
|
|
unsigned int val = 0;
|
||
|
|
int rc = 0;
|
||
|
|
|
||
|
|
rc = kstrtouint(buf, 0, &val);
|
||
|
|
if (rc < 0)
|
||
|
|
return rc;
|
||
|
|
|
||
|
|
return count;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
static ssize_t aw8624_cali_show(struct device *dev,
|
||
|
|
struct device_attribute *attr, char *buf)
|
||
|
|
{
|
||
|
|
#ifdef TIMED_OUTPUT
|
||
|
|
struct timed_output_dev *to_dev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev);
|
||
|
|
#else
|
||
|
|
struct led_classdev *cdev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(cdev, struct aw8624, cdev);
|
||
|
|
#endif
|
||
|
|
|
||
|
|
ssize_t len = 0;
|
||
|
|
unsigned char temp = 0;
|
||
|
|
|
||
|
|
mutex_lock(&aw8624->lock);
|
||
|
|
aw8624_i2c_read(aw8624, AW8624_REG_TRIM_LRA, &temp);
|
||
|
|
aw8624_haptic_upload_lra(aw8624, AW8624_HAPTIC_F0_CALI_LRA);
|
||
|
|
aw8624_haptic_get_f0(aw8624);
|
||
|
|
aw8624_i2c_write(aw8624, AW8624_REG_TRIM_LRA, temp);
|
||
|
|
mutex_unlock(&aw8624->lock);
|
||
|
|
|
||
|
|
len += snprintf(buf+len, PAGE_SIZE-len, "%d\n", aw8624->f0);
|
||
|
|
return len;
|
||
|
|
}
|
||
|
|
|
||
|
|
static ssize_t
|
||
|
|
aw8624_cali_store(struct device *dev, struct device_attribute *attr,
|
||
|
|
const char *buf, size_t count)
|
||
|
|
{
|
||
|
|
#ifdef TIMED_OUTPUT
|
||
|
|
struct timed_output_dev *to_dev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev);
|
||
|
|
#else
|
||
|
|
struct led_classdev *cdev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(cdev, struct aw8624, cdev);
|
||
|
|
#endif
|
||
|
|
|
||
|
|
|
||
|
|
unsigned int val = 0;
|
||
|
|
int rc = 0;
|
||
|
|
|
||
|
|
rc = kstrtouint(buf, 0, &val);
|
||
|
|
if (rc < 0)
|
||
|
|
return rc;
|
||
|
|
|
||
|
|
if (val) {
|
||
|
|
mutex_lock(&aw8624->lock);
|
||
|
|
aw8624_haptic_upload_lra(aw8624, AW8624_HAPTIC_ZERO);
|
||
|
|
aw8624_haptic_f0_calibration(aw8624);
|
||
|
|
mutex_unlock(&aw8624->lock);
|
||
|
|
}
|
||
|
|
return count;
|
||
|
|
}
|
||
|
|
|
||
|
|
static ssize_t
|
||
|
|
aw8624_cont_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||
|
|
{
|
||
|
|
#ifdef TIMED_OUTPUT
|
||
|
|
struct timed_output_dev *to_dev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev);
|
||
|
|
#else
|
||
|
|
struct led_classdev *cdev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(cdev, struct aw8624, cdev);
|
||
|
|
#endif
|
||
|
|
|
||
|
|
ssize_t len = 0;
|
||
|
|
|
||
|
|
aw8624_haptic_read_cont_f0(aw8624);
|
||
|
|
len += snprintf(buf+len, PAGE_SIZE-len, "%d\n", aw8624->cont_f0);
|
||
|
|
return len;
|
||
|
|
}
|
||
|
|
|
||
|
|
static ssize_t
|
||
|
|
aw8624_cont_store(struct device *dev, struct device_attribute *attr,
|
||
|
|
const char *buf, size_t count)
|
||
|
|
{
|
||
|
|
#ifdef TIMED_OUTPUT
|
||
|
|
struct timed_output_dev *to_dev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev);
|
||
|
|
#else
|
||
|
|
struct led_classdev *cdev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(cdev, struct aw8624, cdev);
|
||
|
|
#endif
|
||
|
|
|
||
|
|
unsigned int val = 0;
|
||
|
|
int rc = 0;
|
||
|
|
|
||
|
|
rc = kstrtouint(buf, 0, &val);
|
||
|
|
if (rc < 0)
|
||
|
|
return rc;
|
||
|
|
|
||
|
|
aw8624_haptic_stop(aw8624);
|
||
|
|
|
||
|
|
if (val)
|
||
|
|
aw8624_haptic_cont(aw8624);
|
||
|
|
return count;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
static ssize_t
|
||
|
|
aw8624_cont_td_show(struct device *dev, struct device_attribute *attr,
|
||
|
|
char *buf)
|
||
|
|
{
|
||
|
|
#ifdef TIMED_OUTPUT
|
||
|
|
struct timed_output_dev *to_dev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev);
|
||
|
|
#else
|
||
|
|
struct led_classdev *cdev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(cdev, struct aw8624, cdev);
|
||
|
|
#endif
|
||
|
|
|
||
|
|
ssize_t len = 0;
|
||
|
|
|
||
|
|
len += snprintf(buf+len, PAGE_SIZE-len,
|
||
|
|
"cont_delay_time = 0x%04x\n",
|
||
|
|
aw8624->cont_td);
|
||
|
|
return len;
|
||
|
|
}
|
||
|
|
|
||
|
|
static ssize_t
|
||
|
|
aw8624_cont_td_store(struct device *dev, struct device_attribute *attr,
|
||
|
|
const char *buf, size_t count)
|
||
|
|
{
|
||
|
|
#ifdef TIMED_OUTPUT
|
||
|
|
struct timed_output_dev *to_dev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev);
|
||
|
|
#else
|
||
|
|
struct led_classdev *cdev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(cdev, struct aw8624, cdev);
|
||
|
|
#endif
|
||
|
|
int err, val;
|
||
|
|
|
||
|
|
err = kstrtoint(buf, 16, &val);
|
||
|
|
if (err != 0) {
|
||
|
|
aw_dev_err(aw8624->dev, "%s format not match!", __func__);
|
||
|
|
return count;
|
||
|
|
}
|
||
|
|
|
||
|
|
aw8624->cont_td = val;
|
||
|
|
aw8624_i2c_write(aw8624,
|
||
|
|
AW8624_REG_TD_H,
|
||
|
|
(unsigned char)(val >> 8));
|
||
|
|
aw8624_i2c_write(aw8624,
|
||
|
|
AW8624_REG_TD_L,
|
||
|
|
(unsigned char)(val >> 0));
|
||
|
|
|
||
|
|
return count;
|
||
|
|
}
|
||
|
|
|
||
|
|
static ssize_t
|
||
|
|
aw8624_cont_drv_show(struct device *dev, struct device_attribute *attr,
|
||
|
|
char *buf)
|
||
|
|
{
|
||
|
|
#ifdef TIMED_OUTPUT
|
||
|
|
struct timed_output_dev *to_dev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev);
|
||
|
|
#else
|
||
|
|
struct led_classdev *cdev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(cdev, struct aw8624, cdev);
|
||
|
|
#endif
|
||
|
|
|
||
|
|
ssize_t len = 0;
|
||
|
|
|
||
|
|
len += snprintf(buf+len, PAGE_SIZE-len,
|
||
|
|
"cont drv level = 0x%02x\n",
|
||
|
|
aw8624->cont_drv_lvl);
|
||
|
|
len += snprintf(buf+len, PAGE_SIZE-len,
|
||
|
|
"cont drv level overdrive= 0x%02x\n",
|
||
|
|
aw8624->cont_drv_lvl_ov);
|
||
|
|
return len;
|
||
|
|
}
|
||
|
|
|
||
|
|
static ssize_t
|
||
|
|
aw8624_cont_drv_store(struct device *dev, struct device_attribute *attr,
|
||
|
|
const char *buf, size_t count)
|
||
|
|
{
|
||
|
|
#ifdef TIMED_OUTPUT
|
||
|
|
struct timed_output_dev *to_dev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev);
|
||
|
|
#else
|
||
|
|
struct led_classdev *cdev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(cdev, struct aw8624, cdev);
|
||
|
|
#endif
|
||
|
|
|
||
|
|
unsigned int databuf[2] = {0, 0};
|
||
|
|
|
||
|
|
if (sscanf(buf, "%x %x", &databuf[0], &databuf[1]) == 2) {
|
||
|
|
aw8624->cont_drv_lvl = databuf[0];
|
||
|
|
aw8624_i2c_write(aw8624,
|
||
|
|
AW8624_REG_DRV_LVL,
|
||
|
|
aw8624->cont_drv_lvl);
|
||
|
|
aw8624->cont_drv_lvl_ov = databuf[1];
|
||
|
|
aw8624_i2c_write(aw8624,
|
||
|
|
AW8624_REG_DRV_LVL_OV,
|
||
|
|
aw8624->cont_drv_lvl_ov);
|
||
|
|
}
|
||
|
|
return count;
|
||
|
|
}
|
||
|
|
|
||
|
|
static ssize_t
|
||
|
|
aw8624_cont_num_brk_show(struct device *dev, struct device_attribute *attr,
|
||
|
|
char *buf)
|
||
|
|
{
|
||
|
|
#ifdef TIMED_OUTPUT
|
||
|
|
struct timed_output_dev *to_dev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev);
|
||
|
|
#else
|
||
|
|
struct led_classdev *cdev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(cdev, struct aw8624, cdev);
|
||
|
|
#endif
|
||
|
|
|
||
|
|
ssize_t len = 0;
|
||
|
|
|
||
|
|
len += snprintf(buf+len, PAGE_SIZE-len,
|
||
|
|
"cont_brk_num = 0x%02x\n",
|
||
|
|
aw8624->cont_num_brk);
|
||
|
|
return len;
|
||
|
|
}
|
||
|
|
|
||
|
|
static ssize_t
|
||
|
|
aw8624_cont_num_brk_store(struct device *dev, struct device_attribute *attr,
|
||
|
|
const char *buf, size_t count)
|
||
|
|
{
|
||
|
|
#ifdef TIMED_OUTPUT
|
||
|
|
struct timed_output_dev *to_dev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev);
|
||
|
|
#else
|
||
|
|
struct led_classdev *cdev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(cdev, struct aw8624, cdev);
|
||
|
|
#endif
|
||
|
|
|
||
|
|
int err, val;
|
||
|
|
|
||
|
|
err = kstrtoint(buf, 16, &val);
|
||
|
|
if (err != 0) {
|
||
|
|
aw_dev_err(aw8624->dev, "%s format not match!", __func__);
|
||
|
|
return count;
|
||
|
|
}
|
||
|
|
|
||
|
|
aw8624->cont_num_brk = val;
|
||
|
|
if (aw8624->cont_num_brk > 7)
|
||
|
|
aw8624->cont_num_brk = 7;
|
||
|
|
|
||
|
|
aw8624_i2c_write_bits(aw8624, AW8624_REG_BEMF_NUM,
|
||
|
|
AW8624_BIT_BEMF_NUM_BRK_MASK, aw8624->cont_num_brk);
|
||
|
|
|
||
|
|
return count;
|
||
|
|
}
|
||
|
|
|
||
|
|
static ssize_t
|
||
|
|
aw8624_cont_zc_thr_show(struct device *dev, struct device_attribute *attr,
|
||
|
|
char *buf)
|
||
|
|
{
|
||
|
|
#ifdef TIMED_OUTPUT
|
||
|
|
struct timed_output_dev *to_dev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev);
|
||
|
|
#else
|
||
|
|
struct led_classdev *cdev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(cdev, struct aw8624, cdev);
|
||
|
|
#endif
|
||
|
|
ssize_t len = 0;
|
||
|
|
|
||
|
|
len += snprintf(buf+len, PAGE_SIZE-len,
|
||
|
|
"cont_zero_cross_thr = 0x%04x\n",
|
||
|
|
aw8624->cont_zc_thr);
|
||
|
|
return len;
|
||
|
|
}
|
||
|
|
|
||
|
|
static ssize_t
|
||
|
|
aw8624_cont_zc_thr_store(struct device *dev, struct device_attribute *attr,
|
||
|
|
const char *buf, size_t count)
|
||
|
|
{
|
||
|
|
#ifdef TIMED_OUTPUT
|
||
|
|
struct timed_output_dev *to_dev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev);
|
||
|
|
#else
|
||
|
|
struct led_classdev *cdev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(cdev, struct aw8624, cdev);
|
||
|
|
#endif
|
||
|
|
|
||
|
|
int err, val;
|
||
|
|
|
||
|
|
err = kstrtoint(buf, 0, &val);
|
||
|
|
if (err != 0) {
|
||
|
|
aw_dev_err(aw8624->dev, "%s format not match!", __func__);
|
||
|
|
return count;
|
||
|
|
}
|
||
|
|
aw_dev_info(aw8624->dev, "%s: val=%d\n", __func__, val);
|
||
|
|
if (val == 1) {
|
||
|
|
aw8624_i2c_write(aw8624,
|
||
|
|
AW8624_REG_ZC_THRSH_H,
|
||
|
|
(unsigned char)(val >> 8));
|
||
|
|
aw8624_i2c_write(aw8624,
|
||
|
|
AW8624_REG_ZC_THRSH_L,
|
||
|
|
(unsigned char)(val >> 0));
|
||
|
|
}
|
||
|
|
return count;
|
||
|
|
}
|
||
|
|
|
||
|
|
static ssize_t
|
||
|
|
aw8624_vbat_monitor_show(struct device *dev, struct device_attribute *attr,
|
||
|
|
char *buf)
|
||
|
|
{
|
||
|
|
#ifdef TIMED_OUTPUT
|
||
|
|
struct timed_output_dev *to_dev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev);
|
||
|
|
#else
|
||
|
|
struct led_classdev *cdev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(cdev, struct aw8624, cdev);
|
||
|
|
#endif
|
||
|
|
|
||
|
|
ssize_t len = 0;
|
||
|
|
unsigned int vbat = 0;
|
||
|
|
|
||
|
|
mutex_lock(&aw8624->lock);
|
||
|
|
vbat = aw8624_vbat_monitor_detector(aw8624);
|
||
|
|
mutex_unlock(&aw8624->lock);
|
||
|
|
len += snprintf(buf+len, PAGE_SIZE-len, "vbat_monitor = %d\n", vbat);
|
||
|
|
|
||
|
|
return len;
|
||
|
|
}
|
||
|
|
|
||
|
|
static ssize_t
|
||
|
|
aw8624_vbat_monitor_store(struct device *dev, struct device_attribute *attr,
|
||
|
|
const char *buf, size_t count)
|
||
|
|
{
|
||
|
|
return count;
|
||
|
|
}
|
||
|
|
|
||
|
|
static ssize_t
|
||
|
|
aw8624_lra_resistance_show(struct device *dev, struct device_attribute *attr,
|
||
|
|
char *buf)
|
||
|
|
{
|
||
|
|
#ifdef TIMED_OUTPUT
|
||
|
|
struct timed_output_dev *to_dev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev);
|
||
|
|
#else
|
||
|
|
struct led_classdev *cdev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(cdev, struct aw8624, cdev);
|
||
|
|
#endif
|
||
|
|
ssize_t len = 0;
|
||
|
|
unsigned int r_lra = 0;
|
||
|
|
|
||
|
|
r_lra = aw8624_lra_resistance_detector(aw8624);
|
||
|
|
|
||
|
|
len += snprintf(buf+len, PAGE_SIZE-len, "lra_resistance = %d\n", r_lra);
|
||
|
|
return len;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
static ssize_t
|
||
|
|
aw8624_lra_resistance_store(struct device *dev, struct device_attribute *attr,
|
||
|
|
const char *buf, size_t count)
|
||
|
|
{
|
||
|
|
return count;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
static ssize_t
|
||
|
|
aw8624_prctmode_show(struct device *dev, struct device_attribute *attr,
|
||
|
|
char *buf)
|
||
|
|
{
|
||
|
|
#ifdef TIMED_OUTPUT
|
||
|
|
struct timed_output_dev *to_dev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev);
|
||
|
|
#else
|
||
|
|
struct led_classdev *cdev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(cdev, struct aw8624, cdev);
|
||
|
|
#endif
|
||
|
|
|
||
|
|
ssize_t len = 0;
|
||
|
|
unsigned char reg_val = 0;
|
||
|
|
|
||
|
|
aw8624_i2c_read(aw8624, AW8624_REG_RLDET, ®_val);
|
||
|
|
|
||
|
|
len += snprintf(buf+len, PAGE_SIZE-len,
|
||
|
|
"prctmode = %d\n", reg_val&0x20);
|
||
|
|
return len;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
static ssize_t
|
||
|
|
aw8624_prctmode_store(struct device *dev, struct device_attribute *attr,
|
||
|
|
const char *buf, size_t count)
|
||
|
|
{
|
||
|
|
#ifdef TIMED_OUTPUT
|
||
|
|
struct timed_output_dev *to_dev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev);
|
||
|
|
#else
|
||
|
|
struct led_classdev *cdev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(cdev, struct aw8624, cdev);
|
||
|
|
#endif
|
||
|
|
unsigned int databuf[2] = {0, 0};
|
||
|
|
unsigned int addr = 0;
|
||
|
|
unsigned int val = 0;
|
||
|
|
|
||
|
|
if (sscanf(buf, "%x %x", &databuf[0], &databuf[1]) == 2) {
|
||
|
|
addr = databuf[0];
|
||
|
|
val = databuf[1];
|
||
|
|
mutex_lock(&aw8624->lock);
|
||
|
|
aw8624_haptic_swicth_motorprotect_config(aw8624, addr, val);
|
||
|
|
mutex_unlock(&aw8624->lock);
|
||
|
|
}
|
||
|
|
return count;
|
||
|
|
}
|
||
|
|
|
||
|
|
static ssize_t
|
||
|
|
aw8624_ram_vbat_comp_show(struct device *dev, struct device_attribute *attr,
|
||
|
|
char *buf)
|
||
|
|
{
|
||
|
|
#ifdef TIMED_OUTPUT
|
||
|
|
struct timed_output_dev *to_dev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev);
|
||
|
|
#else
|
||
|
|
struct led_classdev *cdev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(cdev, struct aw8624, cdev);
|
||
|
|
#endif
|
||
|
|
ssize_t len = 0;
|
||
|
|
|
||
|
|
len += snprintf(buf+len, PAGE_SIZE-len,
|
||
|
|
"ram_vbat_comp = %d\n",
|
||
|
|
aw8624->ram_vbat_comp);
|
||
|
|
|
||
|
|
return len;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
static ssize_t
|
||
|
|
aw8624_ram_vbat_comp_store(struct device *dev, struct device_attribute *attr,
|
||
|
|
const char *buf, size_t count)
|
||
|
|
{
|
||
|
|
#ifdef TIMED_OUTPUT
|
||
|
|
struct timed_output_dev *to_dev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev);
|
||
|
|
#else
|
||
|
|
struct led_classdev *cdev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(cdev, struct aw8624, cdev);
|
||
|
|
#endif
|
||
|
|
|
||
|
|
unsigned int val = 0;
|
||
|
|
int rc = 0;
|
||
|
|
|
||
|
|
rc = kstrtouint(buf, 0, &val);
|
||
|
|
if (rc < 0)
|
||
|
|
return rc;
|
||
|
|
|
||
|
|
mutex_lock(&aw8624->lock);
|
||
|
|
if (val)
|
||
|
|
aw8624->ram_vbat_comp = AW8624_HAPTIC_RAM_VBAT_COMP_ENABLE;
|
||
|
|
else
|
||
|
|
aw8624->ram_vbat_comp = AW8624_HAPTIC_RAM_VBAT_COMP_DISABLE;
|
||
|
|
|
||
|
|
mutex_unlock(&aw8624->lock);
|
||
|
|
|
||
|
|
return count;
|
||
|
|
}
|
||
|
|
|
||
|
|
static ssize_t
|
||
|
|
aw8624_haptic_audio_show(struct device *dev, struct device_attribute *attr,
|
||
|
|
char *buf)
|
||
|
|
{
|
||
|
|
#ifdef TIMED_OUTPUT
|
||
|
|
struct timed_output_dev *to_dev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev);
|
||
|
|
#else
|
||
|
|
struct led_classdev *cdev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(cdev, struct aw8624, cdev);
|
||
|
|
#endif
|
||
|
|
|
||
|
|
ssize_t len = 0;
|
||
|
|
|
||
|
|
len += snprintf(buf+len, PAGE_SIZE-len, "%d\n",
|
||
|
|
aw8624->haptic_audio.ctr.cnt);
|
||
|
|
return len;
|
||
|
|
}
|
||
|
|
|
||
|
|
static ssize_t
|
||
|
|
aw8624_haptic_audio_store(struct device *dev, struct device_attribute *attr,
|
||
|
|
const char *buf, size_t count)
|
||
|
|
{
|
||
|
|
#ifdef TIMED_OUTPUT
|
||
|
|
struct timed_output_dev *to_dev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev);
|
||
|
|
#else
|
||
|
|
struct led_classdev *cdev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(cdev, struct aw8624, cdev);
|
||
|
|
#endif
|
||
|
|
unsigned int databuf[6] = {0};
|
||
|
|
int rtp_is_going_on = 0;
|
||
|
|
struct haptic_ctr *hap_ctr = NULL;
|
||
|
|
|
||
|
|
rtp_is_going_on = aw8624_haptic_juge_RTP_is_going_on(aw8624);
|
||
|
|
if (rtp_is_going_on) {
|
||
|
|
aw_dev_info(aw8624->dev,
|
||
|
|
"%s: RTP is runing, stop audio haptic\n", __func__);
|
||
|
|
return count;
|
||
|
|
}
|
||
|
|
if (!aw8624->ram_init) {
|
||
|
|
aw_dev_err(aw8624->dev,
|
||
|
|
"%s: ram init failed, not allow to play!\n",
|
||
|
|
__func__);
|
||
|
|
return count;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (sscanf(buf, "%d %d %d %d %d %d",
|
||
|
|
&databuf[0], &databuf[1], &databuf[2],
|
||
|
|
&databuf[3], &databuf[4], &databuf[5]) == 6) {
|
||
|
|
if (databuf[2]) {
|
||
|
|
aw_dev_dbg(aw8624->dev,
|
||
|
|
"%s: cnt=%d, cmd=%d, play=%d, wavseq=%d, loop=%d, gain=%d\n",
|
||
|
|
__func__,
|
||
|
|
databuf[0], databuf[1], databuf[2],
|
||
|
|
databuf[3], databuf[4], databuf[5]);
|
||
|
|
hap_ctr = (struct haptic_ctr *)kzalloc(sizeof(struct haptic_ctr),
|
||
|
|
GFP_KERNEL);
|
||
|
|
if (hap_ctr == NULL) {
|
||
|
|
aw_dev_err(aw8624->dev,
|
||
|
|
"%s: kzalloc memory fail\n", __func__);
|
||
|
|
return count;
|
||
|
|
}
|
||
|
|
mutex_lock(&aw8624->haptic_audio.lock);
|
||
|
|
hap_ctr->cnt = (unsigned char)databuf[0];
|
||
|
|
hap_ctr->cmd = (unsigned char)databuf[1];
|
||
|
|
hap_ctr->play = (unsigned char)databuf[2];
|
||
|
|
hap_ctr->wavseq = (unsigned char)databuf[3];
|
||
|
|
hap_ctr->loop = (unsigned char)databuf[4];
|
||
|
|
hap_ctr->gain = (unsigned char)databuf[5];
|
||
|
|
aw8624_haptic_audio_ctr_list_insert(&aw8624->haptic_audio,
|
||
|
|
hap_ctr, aw8624->dev);
|
||
|
|
|
||
|
|
if (hap_ctr->cmd == 0xff) {
|
||
|
|
aw_dev_info(aw8624->dev,
|
||
|
|
"%s: haptic_audio stop\n", __func__);
|
||
|
|
if (hrtimer_active(&aw8624->haptic_audio.timer)) {
|
||
|
|
aw_dev_info(aw8624->dev,
|
||
|
|
"%s: cancel haptic_audio_timer\n",
|
||
|
|
__func__);
|
||
|
|
hrtimer_cancel(&aw8624->haptic_audio.timer);
|
||
|
|
aw8624->haptic_audio.ctr.cnt = 0;
|
||
|
|
aw8624_haptic_audio_off(aw8624);
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
if (hrtimer_active(&aw8624->haptic_audio.timer)) {
|
||
|
|
} else {
|
||
|
|
aw_dev_info(aw8624->dev, "%s: start haptic_audio_timer\n",
|
||
|
|
__func__);
|
||
|
|
aw8624_haptic_audio_init(aw8624);
|
||
|
|
hrtimer_start(&aw8624->haptic_audio.timer,
|
||
|
|
ktime_set(aw8624->haptic_audio.delay_val/1000,
|
||
|
|
(aw8624->haptic_audio.delay_val%1000)*1000000),
|
||
|
|
HRTIMER_MODE_REL);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
mutex_unlock(&aw8624->haptic_audio.lock);
|
||
|
|
kfree(hap_ctr);
|
||
|
|
|
||
|
|
|
||
|
|
}
|
||
|
|
return count;
|
||
|
|
}
|
||
|
|
|
||
|
|
static ssize_t
|
||
|
|
aw8624_haptic_audio_time_show(struct device *dev, struct device_attribute *attr,
|
||
|
|
char *buf)
|
||
|
|
{
|
||
|
|
|
||
|
|
#ifdef TIMED_OUTPUT
|
||
|
|
struct timed_output_dev *to_dev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev);
|
||
|
|
#else
|
||
|
|
struct led_classdev *cdev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(cdev, struct aw8624, cdev);
|
||
|
|
#endif
|
||
|
|
ssize_t len = 0;
|
||
|
|
|
||
|
|
len += snprintf(buf+len, PAGE_SIZE-len,
|
||
|
|
"haptic_audio.delay_val=%dus\n",
|
||
|
|
aw8624->haptic_audio.delay_val);
|
||
|
|
len += snprintf(buf+len, PAGE_SIZE-len,
|
||
|
|
"haptic_audio.timer_val=%dus\n",
|
||
|
|
aw8624->haptic_audio.timer_val);
|
||
|
|
return len;
|
||
|
|
}
|
||
|
|
|
||
|
|
static ssize_t
|
||
|
|
aw8624_haptic_audio_time_store(struct device *dev,
|
||
|
|
struct device_attribute *attr, const char *buf, size_t count)
|
||
|
|
{
|
||
|
|
#ifdef TIMED_OUTPUT
|
||
|
|
struct timed_output_dev *to_dev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev);
|
||
|
|
#else
|
||
|
|
struct led_classdev *cdev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(cdev, struct aw8624, cdev);
|
||
|
|
#endif
|
||
|
|
|
||
|
|
unsigned int databuf[2] = {0};
|
||
|
|
|
||
|
|
if (sscanf(buf, "%d %d", &databuf[0], &databuf[1]) == 2) {
|
||
|
|
aw8624->haptic_audio.delay_val = databuf[0];
|
||
|
|
aw8624->haptic_audio.timer_val = databuf[1];
|
||
|
|
}
|
||
|
|
return count;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int aw8624_clock_OSC_trim_calibration
|
||
|
|
(unsigned long int theory_time, unsigned long int real_time)
|
||
|
|
{
|
||
|
|
unsigned int real_code = 0;
|
||
|
|
unsigned int LRA_TRIM_CODE = 0;
|
||
|
|
unsigned int DFT_LRA_TRIM_CODE = 0;
|
||
|
|
unsigned int Not_need_cali_threshold = 10;/*0.1 percent not need cali*/
|
||
|
|
|
||
|
|
aw_dev_info(g_aw8624->dev, "%s enter\n", __func__);
|
||
|
|
if (theory_time == real_time) {
|
||
|
|
aw_dev_info(g_aw8624->dev,
|
||
|
|
"aw_osctheory_time == real_time:%ld,", real_time);
|
||
|
|
aw_dev_info(g_aw8624->dev,
|
||
|
|
"theory_time = %ld not need to cali\n", theory_time);
|
||
|
|
return 0;
|
||
|
|
} else if (theory_time < real_time) {
|
||
|
|
if ((real_time - theory_time) > (theory_time / 25)) {
|
||
|
|
aw_dev_info(g_aw8624->dev,
|
||
|
|
"%s: failed not to cali\n", __func__);
|
||
|
|
return DFT_LRA_TRIM_CODE;
|
||
|
|
}
|
||
|
|
|
||
|
|
if ((real_time - theory_time) <
|
||
|
|
(Not_need_cali_threshold*theory_time/10000)) {
|
||
|
|
aw_dev_info(g_aw8624->dev, "aw_oscmicrosecond:%ld,theory_time = %ld\n",
|
||
|
|
real_time, theory_time);
|
||
|
|
return DFT_LRA_TRIM_CODE;
|
||
|
|
}
|
||
|
|
|
||
|
|
real_code = ((real_time - theory_time) * 4000) / theory_time;
|
||
|
|
real_code = ((real_code%10 < 5) ? 0 : 1) + real_code/10;
|
||
|
|
real_code = 32 + real_code;
|
||
|
|
} else if (theory_time > real_time) {
|
||
|
|
if ((theory_time - real_time) > (theory_time / 25)) {
|
||
|
|
aw_dev_info(g_aw8624->dev, "failed not to cali\n");
|
||
|
|
return DFT_LRA_TRIM_CODE;
|
||
|
|
}
|
||
|
|
if ((theory_time - real_time) <
|
||
|
|
(Not_need_cali_threshold * theory_time/10000)) {
|
||
|
|
aw_dev_info(g_aw8624->dev, "aw_oscmicrosecond:%ld,theory_time = %ld\n",
|
||
|
|
real_time, theory_time);
|
||
|
|
return DFT_LRA_TRIM_CODE;
|
||
|
|
}
|
||
|
|
real_code = ((theory_time - real_time) * 4000) / theory_time;
|
||
|
|
real_code = ((real_code%10 < 5) ? 0 : 1) + real_code/10;
|
||
|
|
real_code = 32 - real_code;
|
||
|
|
}
|
||
|
|
if (real_code > 31)
|
||
|
|
LRA_TRIM_CODE = real_code - 32;
|
||
|
|
else
|
||
|
|
LRA_TRIM_CODE = real_code + 32;
|
||
|
|
|
||
|
|
aw_dev_info(g_aw8624->dev, "aw_oscmicrosecond:%ld,theory_time = %ld,real_code =0X%02X,",
|
||
|
|
real_time, theory_time, real_code);
|
||
|
|
aw_dev_info(g_aw8624->dev, "LRA_TRIM_CODE 0X%02X\n", LRA_TRIM_CODE);
|
||
|
|
|
||
|
|
return LRA_TRIM_CODE;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int aw8624_rtp_trim_lra_calibration(struct aw8624 *aw8624)
|
||
|
|
{
|
||
|
|
unsigned char reg_val = 0;
|
||
|
|
unsigned int fre_val = 0;
|
||
|
|
unsigned int theory_time = 0;
|
||
|
|
unsigned int lra_rtim_code = 0;
|
||
|
|
|
||
|
|
aw_dev_info(aw8624->dev, "%s enter\n", __func__);
|
||
|
|
aw8624_i2c_read(aw8624, AW8624_REG_PWMDBG, ®_val);
|
||
|
|
fre_val = (reg_val & 0x006f) >> 5;
|
||
|
|
|
||
|
|
if (fre_val == 3)
|
||
|
|
theory_time = (aw8624->rtp_len / 12000) * 1000000; /*12K */
|
||
|
|
if (fre_val == 2)
|
||
|
|
theory_time = (aw8624->rtp_len / 24000) * 1000000; /*24K */
|
||
|
|
if (fre_val == 1 || fre_val == 0)
|
||
|
|
theory_time = (aw8624->rtp_len / 48000) * 1000000; /*48K */
|
||
|
|
|
||
|
|
aw_dev_info(aw8624->dev, "microsecond:%ld theory_time = %d\n",
|
||
|
|
aw8624->microsecond, theory_time);
|
||
|
|
|
||
|
|
lra_rtim_code = aw8624_clock_OSC_trim_calibration(theory_time,
|
||
|
|
aw8624->microsecond);
|
||
|
|
if (lra_rtim_code >= 0) {
|
||
|
|
aw8624->lra_calib_data = lra_rtim_code;
|
||
|
|
aw8624_i2c_write(aw8624, AW8624_REG_TRIM_LRA,
|
||
|
|
(char)lra_rtim_code);
|
||
|
|
}
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static unsigned char aw8624_haptic_osc_read_int(struct aw8624 *aw8624)
|
||
|
|
{
|
||
|
|
unsigned char reg_val = 0;
|
||
|
|
|
||
|
|
aw8624_i2c_read(aw8624, AW8624_REG_DBGSTAT, ®_val);
|
||
|
|
return reg_val;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int aw8624_rtp_osc_calibration(struct aw8624 *aw8624)
|
||
|
|
{
|
||
|
|
const struct firmware *rtp_file;
|
||
|
|
int ret = -1;
|
||
|
|
unsigned int buf_len = 0;
|
||
|
|
unsigned char osc_int_state = 0;
|
||
|
|
|
||
|
|
aw8624->rtp_cnt = 0;
|
||
|
|
aw8624->timeval_flags = 1;
|
||
|
|
aw8624->osc_cali_flag = 1;
|
||
|
|
|
||
|
|
aw_dev_info(aw8624->dev, "%s enter\n", __func__);
|
||
|
|
/* fw loaded */
|
||
|
|
ret = request_firmware(&rtp_file,
|
||
|
|
aw8624_rtp_name[0],/*aw8624->rtp_file_num */
|
||
|
|
aw8624->dev);
|
||
|
|
if (ret < 0) {
|
||
|
|
/*aw8624->rtp_file_num */
|
||
|
|
aw_dev_err(aw8624->dev,
|
||
|
|
"%s: failed to read %s\n",
|
||
|
|
__func__, aw8624_rtp_name[0]);
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
/*awinic add stop,for irq interrupt during calibrate*/
|
||
|
|
aw8624_haptic_stop(aw8624);
|
||
|
|
aw8624->rtp_init = 0;
|
||
|
|
mutex_lock(&aw8624->rtp_lock);
|
||
|
|
vfree(aw8624->rtp_container);
|
||
|
|
aw8624->rtp_container = vmalloc(rtp_file->size + sizeof(int));
|
||
|
|
if (!aw8624->rtp_container) {
|
||
|
|
release_firmware(rtp_file);
|
||
|
|
mutex_unlock(&aw8624->rtp_lock);
|
||
|
|
aw_dev_err(aw8624->dev,
|
||
|
|
"%s: error allocating memory\n", __func__);
|
||
|
|
return -ENOMEM;
|
||
|
|
}
|
||
|
|
aw8624->rtp_container->len = rtp_file->size;
|
||
|
|
aw8624->rtp_len = rtp_file->size;
|
||
|
|
/*aw8624->rtp_file_num */
|
||
|
|
aw_dev_info(aw8624->dev, "%s: rtp file [%s] size = %d\n", __func__,
|
||
|
|
aw8624_rtp_name[0], aw8624->rtp_container->len);
|
||
|
|
memcpy(aw8624->rtp_container->data, rtp_file->data, rtp_file->size);
|
||
|
|
release_firmware(rtp_file);
|
||
|
|
mutex_unlock(&aw8624->rtp_lock);
|
||
|
|
|
||
|
|
/* gain */
|
||
|
|
aw8624_haptic_ram_vbat_comp(aw8624, false);
|
||
|
|
|
||
|
|
/* rtp mode config */
|
||
|
|
aw8624_haptic_play_mode(aw8624, AW8624_HAPTIC_RTP_MODE);
|
||
|
|
|
||
|
|
aw8624_i2c_write_bits(aw8624, AW8624_REG_DBGCTRL,
|
||
|
|
AW8624_BIT_DBGCTRL_INT_MODE_MASK,
|
||
|
|
AW8624_BIT_DBGCTRL_INT_MODE_EDGE);
|
||
|
|
disable_irq(gpio_to_irq(aw8624->irq_gpio));
|
||
|
|
/* haptic start */
|
||
|
|
aw8624_haptic_start(aw8624);
|
||
|
|
pm_qos_add_request(&aw8624_pm_qos_req_vb, PM_QOS_CPU_DMA_LATENCY,
|
||
|
|
AW8624_PM_QOS_VALUE_VB);
|
||
|
|
while (1) {
|
||
|
|
if (!aw8624_haptic_rtp_get_fifo_afi(aw8624)) {
|
||
|
|
aw_dev_info(aw8624->dev,
|
||
|
|
"%s: haptic_rtp_get_fifo_afi, rtp_cnt= %d\n",
|
||
|
|
__func__, aw8624->rtp_cnt);
|
||
|
|
mutex_lock(&aw8624->rtp_lock);
|
||
|
|
if ((aw8624->rtp_container->len - aw8624->rtp_cnt) <
|
||
|
|
(aw8624->ram.base_addr >> 2))
|
||
|
|
buf_len = aw8624->rtp_container->len - aw8624->rtp_cnt;
|
||
|
|
else
|
||
|
|
buf_len = (aw8624->ram.base_addr >> 2);
|
||
|
|
|
||
|
|
if (aw8624->rtp_cnt != aw8624->rtp_container->len) {
|
||
|
|
if (aw8624->timeval_flags == 1) {
|
||
|
|
#ifdef KERNEL_VERSION_49
|
||
|
|
do_gettimeofday(&aw8624->start);
|
||
|
|
#else
|
||
|
|
aw8624->kstart = ktime_get();
|
||
|
|
#endif
|
||
|
|
aw8624->timeval_flags = 0;
|
||
|
|
}
|
||
|
|
aw8624->rtpupdate_flag =
|
||
|
|
aw8624_i2c_writes(aw8624,
|
||
|
|
AW8624_REG_RTP_DATA,
|
||
|
|
&aw8624->rtp_container->data[aw8624->
|
||
|
|
rtp_cnt], buf_len);
|
||
|
|
aw8624->rtp_cnt += buf_len;
|
||
|
|
}
|
||
|
|
mutex_unlock(&aw8624->rtp_lock);
|
||
|
|
}
|
||
|
|
|
||
|
|
osc_int_state = aw8624_haptic_osc_read_int(aw8624);
|
||
|
|
if (osc_int_state&AW8624_BIT_SYSINT_DONEI) {
|
||
|
|
#ifdef KERNEL_VERSION_49
|
||
|
|
do_gettimeofday(&aw8624->end);
|
||
|
|
#else
|
||
|
|
aw8624->kend = ktime_get();
|
||
|
|
#endif
|
||
|
|
aw_dev_info(aw8624->dev,
|
||
|
|
"%s vincent playback aw8624->rtp_cnt= %d\n",
|
||
|
|
__func__, aw8624->rtp_cnt);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
#ifdef KERNEL_VERSION_49
|
||
|
|
do_gettimeofday(&aw8624->end);
|
||
|
|
aw8624->microsecond =
|
||
|
|
(aw8624->end.tv_sec - aw8624->start.tv_sec)*1000000 +
|
||
|
|
(aw8624->end.tv_usec - aw8624->start.tv_usec);
|
||
|
|
|
||
|
|
#else
|
||
|
|
aw8624->kend = ktime_get();
|
||
|
|
aw8624->microsecond = ktime_to_us(ktime_sub(aw8624->kend,
|
||
|
|
aw8624->kstart));
|
||
|
|
|
||
|
|
#endif
|
||
|
|
if (aw8624->microsecond > AW8624_OSC_CALIBRATION_T_LENGTH) {
|
||
|
|
aw_dev_info(aw8624->dev,
|
||
|
|
"%s:vincent time out aw8624->rtp_cnt %d,",
|
||
|
|
__func__, aw8624->rtp_cnt);
|
||
|
|
aw_dev_info(aw8624->dev,
|
||
|
|
"osc_int_state %02x\n", osc_int_state);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
pm_qos_remove_request(&aw8624_pm_qos_req_vb);
|
||
|
|
enable_irq(gpio_to_irq(aw8624->irq_gpio));
|
||
|
|
|
||
|
|
aw8624->osc_cali_flag = 0;
|
||
|
|
#ifdef KERNEL_VERSION_49
|
||
|
|
aw8624->microsecond =
|
||
|
|
(aw8624->end.tv_sec - aw8624->start.tv_sec)*1000000 +
|
||
|
|
(aw8624->end.tv_usec - aw8624->start.tv_usec);
|
||
|
|
|
||
|
|
#else
|
||
|
|
aw8624->microsecond = ktime_to_us(ktime_sub(aw8624->kend,
|
||
|
|
aw8624->kstart));
|
||
|
|
#endif
|
||
|
|
/*calibration osc*/
|
||
|
|
aw_dev_info(aw8624->dev,
|
||
|
|
"%s awinic_microsecond:%ld\n", __func__, aw8624->microsecond);
|
||
|
|
aw_dev_info(aw8624->dev, "%s exit\n", __func__);
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static ssize_t aw8624_osc_cali_show(struct device *dev,
|
||
|
|
struct device_attribute *attr,
|
||
|
|
char *buf)
|
||
|
|
{
|
||
|
|
#ifdef TIMED_OUTPUT
|
||
|
|
struct timed_output_dev *to_dev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev);
|
||
|
|
#else
|
||
|
|
struct led_classdev *cdev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(cdev, struct aw8624, cdev);
|
||
|
|
#endif
|
||
|
|
ssize_t len = 0;
|
||
|
|
|
||
|
|
len +=
|
||
|
|
snprintf(buf + len, PAGE_SIZE - len, "lra_calib_data=%d\n",
|
||
|
|
aw8624->lra_calib_data);
|
||
|
|
|
||
|
|
return len;
|
||
|
|
}
|
||
|
|
|
||
|
|
static ssize_t aw8624_osc_cali_store(struct device *dev,
|
||
|
|
struct device_attribute *attr,
|
||
|
|
const char *buf, size_t count)
|
||
|
|
{
|
||
|
|
#ifdef TIMED_OUTPUT
|
||
|
|
struct timed_output_dev *to_dev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev);
|
||
|
|
#else
|
||
|
|
struct led_classdev *cdev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(cdev, struct aw8624, cdev);
|
||
|
|
#endif
|
||
|
|
unsigned int val = 0;
|
||
|
|
int rc = 0;
|
||
|
|
|
||
|
|
rc = kstrtouint(buf, 0, &val);
|
||
|
|
if (rc < 0)
|
||
|
|
return rc;
|
||
|
|
mutex_lock(&aw8624->lock);
|
||
|
|
/* osc calibration flag start,Other behaviors are forbidden */
|
||
|
|
aw8624->osc_cali_run = 1;
|
||
|
|
if (val == 1) {
|
||
|
|
aw8624_haptic_upload_lra(aw8624, AW8624_HAPTIC_ZERO);
|
||
|
|
aw8624_rtp_osc_calibration(aw8624);
|
||
|
|
aw8624_rtp_trim_lra_calibration(aw8624);
|
||
|
|
} else if (val == 2) {
|
||
|
|
aw8624_haptic_upload_lra(aw8624, AW8624_HAPTIC_RTP_CALI_LRA);
|
||
|
|
aw8624_rtp_osc_calibration(aw8624);
|
||
|
|
} else {
|
||
|
|
aw_dev_err(aw8624->dev,
|
||
|
|
"%s input value out of range\n", __func__);
|
||
|
|
}
|
||
|
|
aw8624->osc_cali_run = 0;
|
||
|
|
/* osc calibration flag end,Other behaviors are permitted */
|
||
|
|
mutex_unlock(&aw8624->lock);
|
||
|
|
|
||
|
|
return count;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
static enum hrtimer_restart aw8624_vibrator_timer_func(struct hrtimer *timer)
|
||
|
|
{
|
||
|
|
struct aw8624 *aw8624 = container_of(timer, struct aw8624, timer);
|
||
|
|
|
||
|
|
aw_dev_info(aw8624->dev, "%s enter\n", __func__);
|
||
|
|
aw8624->state = 0;
|
||
|
|
schedule_work(&aw8624->vibrator_work);
|
||
|
|
|
||
|
|
return HRTIMER_NORESTART;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
static void aw8624_vibrator_work_routine(struct work_struct *work)
|
||
|
|
{
|
||
|
|
struct aw8624 *aw8624 =
|
||
|
|
container_of(work, struct aw8624, vibrator_work);
|
||
|
|
|
||
|
|
aw_dev_info(aw8624->dev, "%s enter\n", __func__);
|
||
|
|
|
||
|
|
mutex_lock(&aw8624->lock);
|
||
|
|
|
||
|
|
aw8624_haptic_stop(aw8624);
|
||
|
|
|
||
|
|
aw8624_haptic_upload_lra(aw8624, AW8624_HAPTIC_F0_CALI_LRA);
|
||
|
|
|
||
|
|
if (aw8624->state) {
|
||
|
|
if (aw8624->activate_mode == AW8624_HAPTIC_ACTIVATE_RAM_MODE) {
|
||
|
|
aw8624_haptic_ram_vbat_comp(aw8624, true);
|
||
|
|
aw8624_haptic_play_repeat_seq(aw8624, true);
|
||
|
|
} else if (aw8624->activate_mode == AW8624_HAPTIC_ACTIVATE_CONT_MODE) {
|
||
|
|
aw8624_haptic_cont(aw8624);
|
||
|
|
} else {
|
||
|
|
/*other mode*/
|
||
|
|
}
|
||
|
|
/* run ms timer */
|
||
|
|
hrtimer_start(&aw8624->timer,
|
||
|
|
ktime_set(aw8624->duration / 1000,
|
||
|
|
(aw8624->duration % 1000) * 1000000),
|
||
|
|
HRTIMER_MODE_REL);
|
||
|
|
#ifdef CONFIG_PM_WAKELOCKS
|
||
|
|
__pm_stay_awake(&aw8624->wk_lock);
|
||
|
|
#else
|
||
|
|
wake_lock(&aw8624->wk_lock);
|
||
|
|
#endif
|
||
|
|
aw8624->wk_lock_flag = 1;
|
||
|
|
} else {
|
||
|
|
if (aw8624->wk_lock_flag == 1) {
|
||
|
|
#ifdef CONFIG_PM_WAKELOCKS
|
||
|
|
__pm_relax(&aw8624->wk_lock);
|
||
|
|
#else
|
||
|
|
wake_unlock(&aw8624->wk_lock);
|
||
|
|
#endif
|
||
|
|
aw8624->wk_lock_flag = 0;
|
||
|
|
}
|
||
|
|
/*aw8624_haptic_stop(aw8624);*/
|
||
|
|
}
|
||
|
|
|
||
|
|
mutex_unlock(&aw8624->lock);
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
/******************************************************
|
||
|
|
*
|
||
|
|
* irq
|
||
|
|
*
|
||
|
|
******************************************************/
|
||
|
|
void aw8624_interrupt_setup(struct aw8624 *aw8624)
|
||
|
|
{
|
||
|
|
unsigned char reg_val = 0;
|
||
|
|
|
||
|
|
aw8624_i2c_read(aw8624, AW8624_REG_SYSINT, ®_val);
|
||
|
|
aw_dev_info(aw8624->dev, "%s: reg SYSINT=0x%x\n", __func__, reg_val);
|
||
|
|
|
||
|
|
aw8624_i2c_write_bits(aw8624, AW8624_REG_DBGCTRL,
|
||
|
|
AW8624_BIT_DBGCTRL_INT_MODE_MASK,
|
||
|
|
AW8624_BIT_DBGCTRL_INT_MODE_EDGE);
|
||
|
|
|
||
|
|
aw8624_i2c_write_bits(aw8624, AW8624_REG_SYSINTM,
|
||
|
|
AW8624_BIT_SYSINTM_UVLO_MASK, AW8624_BIT_SYSINTM_UVLO_EN);
|
||
|
|
aw8624_i2c_write_bits(aw8624, AW8624_REG_SYSINTM,
|
||
|
|
AW8624_BIT_SYSINTM_OCD_MASK, AW8624_BIT_SYSINTM_OCD_EN);
|
||
|
|
aw8624_i2c_write_bits(aw8624, AW8624_REG_SYSINTM,
|
||
|
|
AW8624_BIT_SYSINTM_OT_MASK, AW8624_BIT_SYSINTM_OT_EN);
|
||
|
|
}
|
||
|
|
|
||
|
|
static ssize_t aw8624_gun_type_show(struct device *dev,
|
||
|
|
struct device_attribute *attr,
|
||
|
|
char *buf)
|
||
|
|
{
|
||
|
|
#ifdef TIMED_OUTPUT
|
||
|
|
struct timed_output_dev *to_dev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev);
|
||
|
|
#else
|
||
|
|
struct led_classdev *cdev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(cdev, struct aw8624, cdev);
|
||
|
|
#endif
|
||
|
|
|
||
|
|
return snprintf(buf, PAGE_SIZE, "0x%02x\n", aw8624->gun_type);
|
||
|
|
}
|
||
|
|
|
||
|
|
static ssize_t aw8624_gun_type_store(struct device *dev,
|
||
|
|
struct device_attribute *attr,
|
||
|
|
const char *buf, size_t count)
|
||
|
|
{
|
||
|
|
#ifdef TIMED_OUTPUT
|
||
|
|
struct timed_output_dev *to_dev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev);
|
||
|
|
#else
|
||
|
|
struct led_classdev *cdev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(cdev, struct aw8624, cdev);
|
||
|
|
#endif
|
||
|
|
|
||
|
|
unsigned int val = 0;
|
||
|
|
int rc = 0;
|
||
|
|
|
||
|
|
rc = kstrtouint(buf, 0, &val);
|
||
|
|
if (rc < 0)
|
||
|
|
return rc;
|
||
|
|
|
||
|
|
aw_dev_dbg(aw8624->dev, "%s: value=%d\n", __func__, val);
|
||
|
|
|
||
|
|
mutex_lock(&aw8624->lock);
|
||
|
|
aw8624->gun_type = val;
|
||
|
|
mutex_unlock(&aw8624->lock);
|
||
|
|
return count;
|
||
|
|
}
|
||
|
|
|
||
|
|
static ssize_t aw8624_bullet_nr_show(struct device *dev,
|
||
|
|
struct device_attribute *attr,
|
||
|
|
char *buf)
|
||
|
|
{
|
||
|
|
#ifdef TIMED_OUTPUT
|
||
|
|
struct timed_output_dev *to_dev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev);
|
||
|
|
#else
|
||
|
|
struct led_classdev *cdev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(cdev, struct aw8624, cdev);
|
||
|
|
#endif
|
||
|
|
|
||
|
|
return snprintf(buf, PAGE_SIZE, "0x%02x\n", aw8624->bullet_nr);
|
||
|
|
}
|
||
|
|
|
||
|
|
static ssize_t aw8624_bullet_nr_store(struct device *dev,
|
||
|
|
struct device_attribute *attr,
|
||
|
|
const char *buf, size_t count)
|
||
|
|
{
|
||
|
|
#ifdef TIMED_OUTPUT
|
||
|
|
struct timed_output_dev *to_dev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev);
|
||
|
|
#else
|
||
|
|
struct led_classdev *cdev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(cdev, struct aw8624, cdev);
|
||
|
|
#endif
|
||
|
|
|
||
|
|
unsigned int val = 0;
|
||
|
|
int rc = 0;
|
||
|
|
|
||
|
|
rc = kstrtouint(buf, 0, &val);
|
||
|
|
if (rc < 0)
|
||
|
|
return rc;
|
||
|
|
|
||
|
|
aw_dev_dbg(aw8624->dev, "%s: value=%d\n", __func__, val);
|
||
|
|
|
||
|
|
mutex_lock(&aw8624->lock);
|
||
|
|
aw8624->bullet_nr = val;
|
||
|
|
mutex_unlock(&aw8624->lock);
|
||
|
|
return count;
|
||
|
|
}
|
||
|
|
|
||
|
|
static ssize_t aw8624_trig_show(struct device *dev,
|
||
|
|
struct device_attribute *attr,
|
||
|
|
char *buf)
|
||
|
|
{
|
||
|
|
#ifdef TIMED_OUTPUT
|
||
|
|
struct timed_output_dev *to_dev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev);
|
||
|
|
#else
|
||
|
|
struct led_classdev *cdev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(cdev, struct aw8624, cdev);
|
||
|
|
#endif
|
||
|
|
ssize_t len = 0;
|
||
|
|
|
||
|
|
len += snprintf(buf + len, PAGE_SIZE - len,
|
||
|
|
"trig: trig_enable=%d, trig_edge=%d, trig_polar=%d, pos_sequence=%d, neg_sequence=%d\n",
|
||
|
|
aw8624->trig.trig_enable,
|
||
|
|
aw8624->trig.trig_edge,
|
||
|
|
aw8624->trig.trig_polar,
|
||
|
|
aw8624->trig.pos_sequence,
|
||
|
|
aw8624->trig.neg_sequence);
|
||
|
|
return len;
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
static ssize_t aw8624_trig_store(struct device *dev,
|
||
|
|
struct device_attribute *attr,
|
||
|
|
const char *buf, size_t count)
|
||
|
|
{
|
||
|
|
#ifdef TIMED_OUTPUT
|
||
|
|
struct timed_output_dev *to_dev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev);
|
||
|
|
#else
|
||
|
|
struct led_classdev *cdev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(cdev, struct aw8624, cdev);
|
||
|
|
#endif
|
||
|
|
unsigned int databuf[5] = { 0 };
|
||
|
|
|
||
|
|
if (!aw8624->ram_init) {
|
||
|
|
aw_dev_err(aw8624->dev,
|
||
|
|
"%s: ram init failed, not allow to play!\n",
|
||
|
|
__func__);
|
||
|
|
return count;
|
||
|
|
}
|
||
|
|
if (sscanf(buf, "%d %d %d %d %d",
|
||
|
|
&databuf[0], &databuf[1],
|
||
|
|
&databuf[2], &databuf[3], &databuf[4]) == 5) {
|
||
|
|
if (databuf[0] > 1)
|
||
|
|
databuf[0] = 1;
|
||
|
|
if (databuf[0] < 0)
|
||
|
|
databuf[0] = 0;
|
||
|
|
if (databuf[1] > 1)
|
||
|
|
databuf[0] = 1;
|
||
|
|
if (databuf[1] < 0)
|
||
|
|
databuf[0] = 0;
|
||
|
|
if (databuf[2] > 1)
|
||
|
|
databuf[0] = 1;
|
||
|
|
if (databuf[2] < 0)
|
||
|
|
databuf[0] = 0;
|
||
|
|
if (databuf[3] > aw8624->ram.ram_num ||
|
||
|
|
databuf[4] > aw8624->ram.ram_num) {
|
||
|
|
aw_dev_err(aw8624->dev,
|
||
|
|
"%s: input seq value out of range!\n",
|
||
|
|
__func__);
|
||
|
|
return count;
|
||
|
|
}
|
||
|
|
aw8624->trig.trig_enable = databuf[0];
|
||
|
|
aw8624->trig.trig_edge = databuf[1];
|
||
|
|
aw8624->trig.trig_polar = databuf[2];
|
||
|
|
aw8624->trig.pos_sequence = databuf[3];
|
||
|
|
aw8624->trig.neg_sequence = databuf[4];
|
||
|
|
mutex_lock(&aw8624->lock);
|
||
|
|
aw8624_haptic_tirg1_param_config(aw8624);
|
||
|
|
mutex_unlock(&aw8624->lock);
|
||
|
|
} else
|
||
|
|
aw_dev_err(aw8624->dev,
|
||
|
|
"%s: please input five parameters\n",
|
||
|
|
__func__);
|
||
|
|
return count;
|
||
|
|
}
|
||
|
|
|
||
|
|
static ssize_t aw8624_ram_num_show(struct device *dev,
|
||
|
|
struct device_attribute *attr,
|
||
|
|
char *buf)
|
||
|
|
{
|
||
|
|
#ifdef TIMED_OUTPUT
|
||
|
|
struct timed_output_dev *to_dev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev);
|
||
|
|
#else
|
||
|
|
struct led_classdev *cdev = dev_get_drvdata(dev);
|
||
|
|
struct aw8624 *aw8624 = container_of(cdev, struct aw8624, cdev);
|
||
|
|
#endif
|
||
|
|
ssize_t len = 0;
|
||
|
|
|
||
|
|
aw8624_haptic_get_ram_number(aw8624);
|
||
|
|
|
||
|
|
len += snprintf(buf + len, PAGE_SIZE - len,
|
||
|
|
"ram_num = %d\n", aw8624->ram.ram_num);
|
||
|
|
return len;
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
#if 0
|
||
|
|
/*
|
||
|
|
* schedule_work is low priority.
|
||
|
|
* aw8624 rtp mode can`t be interrupted.
|
||
|
|
*/
|
||
|
|
static irqreturn_t aw8624_irq(int irq, void *data)
|
||
|
|
{
|
||
|
|
struct aw8624 *aw8624 = (struct aw8624 *)data;
|
||
|
|
|
||
|
|
schedule_work(&aw8624->irq_work);
|
||
|
|
return IRQ_HANDLED;
|
||
|
|
}
|
||
|
|
|
||
|
|
#endif
|
||
|
|
/*****************************************************
|
||
|
|
*
|
||
|
|
* device tree
|
||
|
|
*
|
||
|
|
*****************************************************/
|
||
|
|
static DEVICE_ATTR(state, 0664, aw8624_state_show, aw8624_state_store);
|
||
|
|
static DEVICE_ATTR(duration, 0664, aw8624_duration_show, aw8624_duration_store);
|
||
|
|
static DEVICE_ATTR(activate, 0664, aw8624_activate_show, aw8624_activate_store);
|
||
|
|
static DEVICE_ATTR(activate_mode, 0664,
|
||
|
|
aw8624_activate_mode_show, aw8624_activate_mode_store);
|
||
|
|
static DEVICE_ATTR(index, 0664, aw8624_index_show, aw8624_index_store);
|
||
|
|
static DEVICE_ATTR(gain, 0664, aw8624_gain_show, aw8624_gain_store);
|
||
|
|
static DEVICE_ATTR(seq, 0664, aw8624_seq_show, aw8624_seq_store);
|
||
|
|
static DEVICE_ATTR(loop, 0664, aw8624_loop_show, aw8624_loop_store);
|
||
|
|
static DEVICE_ATTR(register, 0664, aw8624_reg_show, aw8624_reg_store);
|
||
|
|
static DEVICE_ATTR(ram_update, 0664,
|
||
|
|
aw8624_ram_update_show, aw8624_ram_update_store);
|
||
|
|
static DEVICE_ATTR(f0, 0664, aw8624_f0_show, aw8624_f0_store);
|
||
|
|
static DEVICE_ATTR(cali, 0664, aw8624_cali_show, aw8624_cali_store);
|
||
|
|
static DEVICE_ATTR(cont, 0664, aw8624_cont_show, aw8624_cont_store);
|
||
|
|
static DEVICE_ATTR(cont_td, 0664, aw8624_cont_td_show, aw8624_cont_td_store);
|
||
|
|
static DEVICE_ATTR(cont_drv, 0664, aw8624_cont_drv_show, aw8624_cont_drv_store);
|
||
|
|
static DEVICE_ATTR(cont_num_brk, 0664,
|
||
|
|
aw8624_cont_num_brk_show, aw8624_cont_num_brk_store);
|
||
|
|
static DEVICE_ATTR(cont_zc_thr, 0664,
|
||
|
|
aw8624_cont_zc_thr_show, aw8624_cont_zc_thr_store);
|
||
|
|
static DEVICE_ATTR(vbat_monitor, 0664,
|
||
|
|
aw8624_vbat_monitor_show, aw8624_vbat_monitor_store);
|
||
|
|
static DEVICE_ATTR(lra_resistance, 0664,
|
||
|
|
aw8624_lra_resistance_show, aw8624_lra_resistance_store);
|
||
|
|
static DEVICE_ATTR(prctmode, 0664, aw8624_prctmode_show, aw8624_prctmode_store);
|
||
|
|
static DEVICE_ATTR(haptic_audio, 0664,
|
||
|
|
aw8624_haptic_audio_show, aw8624_haptic_audio_store);
|
||
|
|
static DEVICE_ATTR(haptic_audio_time, 0664,
|
||
|
|
aw8624_haptic_audio_time_show, aw8624_haptic_audio_time_store);
|
||
|
|
static DEVICE_ATTR(ram_vbat_comp, 0664,
|
||
|
|
aw8624_ram_vbat_comp_show, aw8624_ram_vbat_comp_store);
|
||
|
|
static DEVICE_ATTR(rtp, 0664, aw8624_rtp_show, aw8624_rtp_store);
|
||
|
|
static DEVICE_ATTR(osc_cali, 0664, aw8624_osc_cali_show, aw8624_osc_cali_store);
|
||
|
|
static DEVICE_ATTR(gun_type, 0664, aw8624_gun_type_show, aw8624_gun_type_store);
|
||
|
|
static DEVICE_ATTR(bullet_nr, 0664,
|
||
|
|
aw8624_bullet_nr_show, aw8624_bullet_nr_store);
|
||
|
|
static DEVICE_ATTR(trig, 0664,
|
||
|
|
aw8624_trig_show, aw8624_trig_store);
|
||
|
|
static DEVICE_ATTR(ram_num, 0664, aw8624_ram_num_show, NULL);
|
||
|
|
static struct attribute *aw8624_vibrator_attributes[] = {
|
||
|
|
&dev_attr_state.attr,
|
||
|
|
&dev_attr_duration.attr,
|
||
|
|
&dev_attr_activate.attr,
|
||
|
|
&dev_attr_activate_mode.attr,
|
||
|
|
&dev_attr_index.attr,
|
||
|
|
&dev_attr_gain.attr,
|
||
|
|
&dev_attr_seq.attr,
|
||
|
|
&dev_attr_loop.attr,
|
||
|
|
&dev_attr_register.attr,
|
||
|
|
&dev_attr_ram_update.attr,
|
||
|
|
&dev_attr_f0.attr,
|
||
|
|
&dev_attr_cali.attr,
|
||
|
|
&dev_attr_cont.attr,
|
||
|
|
&dev_attr_cont_td.attr,
|
||
|
|
&dev_attr_cont_drv.attr,
|
||
|
|
&dev_attr_cont_num_brk.attr,
|
||
|
|
&dev_attr_cont_zc_thr.attr,
|
||
|
|
&dev_attr_vbat_monitor.attr,
|
||
|
|
&dev_attr_lra_resistance.attr,
|
||
|
|
&dev_attr_prctmode.attr,
|
||
|
|
&dev_attr_haptic_audio.attr,
|
||
|
|
&dev_attr_ram_vbat_comp.attr,
|
||
|
|
&dev_attr_haptic_audio_time.attr,
|
||
|
|
&dev_attr_rtp.attr,
|
||
|
|
&dev_attr_osc_cali.attr,
|
||
|
|
&dev_attr_gun_type.attr,
|
||
|
|
&dev_attr_bullet_nr.attr,
|
||
|
|
&dev_attr_trig.attr,
|
||
|
|
&dev_attr_ram_num.attr,
|
||
|
|
NULL
|
||
|
|
};
|
||
|
|
|
||
|
|
struct attribute_group aw8624_vibrator_attribute_group = {
|
||
|
|
.attrs = aw8624_vibrator_attributes
|
||
|
|
};
|
||
|
|
|
||
|
|
#ifdef AW_TIKTAP
|
||
|
|
static int aw8624_tiktap_rtp_play(struct aw8624 *aw8624)
|
||
|
|
{
|
||
|
|
unsigned int buf_len = 0;
|
||
|
|
unsigned char glb_state_val = 0;
|
||
|
|
|
||
|
|
aw_dev_info(aw8624->dev, "%s enter\n", __func__);
|
||
|
|
pm_qos_add_request(&aw8624_pm_qos_req_vb, PM_QOS_CPU_DMA_LATENCY,
|
||
|
|
AW8624_PM_QOS_VALUE_VB);
|
||
|
|
aw8624->rtp_cnt = 0;
|
||
|
|
while (true) {
|
||
|
|
if ((aw8624->play_mode != AW8624_HAPTIC_RTP_MODE) ||
|
||
|
|
(aw8624->tiktap_stop_flag == true)) {
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
if (aw8624_haptic_rtp_get_fifo_afs(aw8624)) {
|
||
|
|
mdelay(1);
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
aw_dev_info(aw8624->dev, "%s tiktap rtp_cnt = %d\n", __func__,
|
||
|
|
aw8624->rtp_cnt);
|
||
|
|
aw_dev_info(aw8624->dev, "%s tiktap_rtp->len = %d\n", __func__,
|
||
|
|
aw8624->tiktap_rtp->len);
|
||
|
|
if (!aw8624->tiktap_rtp) {
|
||
|
|
aw_dev_info(aw8624->dev,
|
||
|
|
"%s:tiktap_rtp is null, break!\n",
|
||
|
|
__func__);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
if (aw8624->rtp_cnt < (aw8624->ram.base_addr)) {
|
||
|
|
if ((aw8624->tiktap_rtp->len - aw8624->rtp_cnt) <
|
||
|
|
(aw8624->ram.base_addr)) {
|
||
|
|
buf_len = aw8624->tiktap_rtp->len - aw8624->rtp_cnt;
|
||
|
|
} else {
|
||
|
|
buf_len = aw8624->ram.base_addr;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
else if ((aw8624->tiktap_rtp->len - aw8624->rtp_cnt) <
|
||
|
|
(aw8624->ram.base_addr >> 2)) {
|
||
|
|
buf_len = aw8624->tiktap_rtp->len - aw8624->rtp_cnt;
|
||
|
|
} else {
|
||
|
|
buf_len = aw8624->ram.base_addr >> 2;
|
||
|
|
}
|
||
|
|
aw_dev_info(aw8624->dev, "%s buf_len = %d\n", __func__,
|
||
|
|
buf_len);
|
||
|
|
aw8624_i2c_writes(aw8624, AW8624_REG_RTP_DATA,
|
||
|
|
&aw8624->tiktap_rtp->data[aw8624->rtp_cnt],
|
||
|
|
buf_len);
|
||
|
|
aw8624->rtp_cnt += buf_len;
|
||
|
|
aw8624_i2c_read(aw8624, AW8624_REG_GLB_STATE, &glb_state_val);
|
||
|
|
if ((aw8624->rtp_cnt == aw8624->tiktap_rtp->len) || ((glb_state_val & 0x0f) == 0x00)) {
|
||
|
|
if (aw8624->rtp_cnt == aw8624->tiktap_rtp->len)
|
||
|
|
aw_dev_info(aw8624->dev, "%s: tiktap_rtp update complete!\n",
|
||
|
|
__func__);
|
||
|
|
else
|
||
|
|
aw_dev_err(aw8624->dev, "%s: tiktap_rtp play suspend!\n",
|
||
|
|
__func__);
|
||
|
|
aw8624->rtp_cnt = 0;
|
||
|
|
pm_qos_remove_request(&aw8624_pm_qos_req_vb);
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (aw8624->play_mode == AW8624_HAPTIC_RTP_MODE)
|
||
|
|
aw8624_haptic_set_rtp_aei(aw8624, false);
|
||
|
|
|
||
|
|
aw_dev_info(aw8624->dev, "%s exit\n", __func__);
|
||
|
|
pm_qos_remove_request(&aw8624_pm_qos_req_vb);
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static void aw8624_rtp_work_tiktap(struct work_struct *work)
|
||
|
|
{
|
||
|
|
unsigned int cnt = 200;
|
||
|
|
unsigned char reg_val = 0;
|
||
|
|
bool rtp_work_flag = false;
|
||
|
|
struct aw8624 *aw8624 = container_of(work, struct aw8624, rtp_tiktap);
|
||
|
|
aw_dev_info(aw8624->dev, "%s enter\n", __func__);
|
||
|
|
|
||
|
|
mutex_lock(&aw8624->lock);
|
||
|
|
aw8624_haptic_stop(aw8624);
|
||
|
|
aw8624_haptic_play_mode(aw8624, AW8624_HAPTIC_RTP_MODE);
|
||
|
|
aw8624_haptic_play_go(aw8624, true);
|
||
|
|
//usleep_range(2000, 2500);
|
||
|
|
while (cnt)
|
||
|
|
{
|
||
|
|
aw8624_i2c_read(aw8624, AW8624_REG_GLB_STATE, ®_val);
|
||
|
|
if ((reg_val & 0x0f) == 0x08) {
|
||
|
|
cnt = 0;
|
||
|
|
rtp_work_flag = true;
|
||
|
|
aw_dev_info(aw8624->dev, "%s RTP_GO! glb_state=0x08\n",
|
||
|
|
__func__);
|
||
|
|
} else {
|
||
|
|
cnt--;
|
||
|
|
aw_dev_dbg(aw8624->dev,
|
||
|
|
"%s wait for RTP_GO, glb_state=0x%02X\n",
|
||
|
|
__func__, reg_val);
|
||
|
|
}
|
||
|
|
usleep_range(1000, 1500);
|
||
|
|
}
|
||
|
|
if (!rtp_work_flag) {
|
||
|
|
aw_dev_err(aw8624->dev, "%s failed to enter RTP_GO status!\n",
|
||
|
|
__func__);
|
||
|
|
aw8624_haptic_stop(aw8624);
|
||
|
|
}
|
||
|
|
mutex_unlock(&aw8624->lock);
|
||
|
|
|
||
|
|
mutex_lock(&aw8624->rtp_lock);
|
||
|
|
if (aw8624->tiktap_stop_flag == false) {
|
||
|
|
aw8624_tiktap_rtp_play(aw8624);
|
||
|
|
}
|
||
|
|
mutex_unlock(&aw8624->rtp_lock);
|
||
|
|
}
|
||
|
|
|
||
|
|
static void aw8624_rtp_irq_work_tiktap(struct work_struct *work)
|
||
|
|
{
|
||
|
|
unsigned int cnt = 200;
|
||
|
|
unsigned char reg_val = 0;
|
||
|
|
bool rtp_work_flag = false;
|
||
|
|
struct aw8624 *aw8624 = container_of(work, struct aw8624,
|
||
|
|
rtp_irq_tiktap);
|
||
|
|
aw_dev_info(aw8624->dev, "%s enter\n", __func__);
|
||
|
|
|
||
|
|
mutex_lock(&aw8624->lock);
|
||
|
|
aw8624_haptic_stop(aw8624);
|
||
|
|
aw8624_haptic_set_rtp_aei(aw8624, false);
|
||
|
|
aw8624_interrupt_clear(aw8624);
|
||
|
|
aw8624_haptic_play_mode(aw8624, AW8624_HAPTIC_RTP_MODE);
|
||
|
|
aw8624_haptic_play_go(aw8624, true);
|
||
|
|
//usleep_range(2000, 2500);
|
||
|
|
while (cnt) {
|
||
|
|
aw8624_i2c_read(aw8624, AW8624_REG_GLB_STATE, ®_val);
|
||
|
|
if ((reg_val & 0x0f) == 0x08) {
|
||
|
|
cnt = 0;
|
||
|
|
rtp_work_flag = true;
|
||
|
|
aw_dev_info(aw8624->dev, "%s RTP_GO! glb_state=0x08\n",
|
||
|
|
__func__);
|
||
|
|
} else {
|
||
|
|
cnt--;
|
||
|
|
aw_dev_dbg(aw8624->dev,
|
||
|
|
"%s wait for RTP_GO, glb_state=0x%02X\n",
|
||
|
|
__func__, reg_val);
|
||
|
|
}
|
||
|
|
usleep_range(1000, 1500);
|
||
|
|
}
|
||
|
|
if (!rtp_work_flag) {
|
||
|
|
aw_dev_err(aw8624->dev, "%s failed to enter RTP_GO status!\n",
|
||
|
|
__func__);
|
||
|
|
aw8624_haptic_stop(aw8624);
|
||
|
|
}
|
||
|
|
mutex_unlock(&aw8624->lock);
|
||
|
|
|
||
|
|
if (aw8624->tiktap_stop_flag == false) {
|
||
|
|
aw8624_haptic_rtp_init(aw8624);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
static long aw8624_tiktap_unlocked_ioctl(struct file *file, unsigned int cmd,
|
||
|
|
unsigned long arg)
|
||
|
|
{
|
||
|
|
struct aw8624 *aw8624 = g_aw8624;
|
||
|
|
unsigned char reg_addr = 0;
|
||
|
|
unsigned char reg_data = 0;
|
||
|
|
int ret = 0;
|
||
|
|
unsigned int tmp = 0;
|
||
|
|
unsigned int len = 0;
|
||
|
|
|
||
|
|
switch (cmd) {
|
||
|
|
case TIKTAP_GET_HWINFO:
|
||
|
|
aw_dev_info(aw8624->dev, "%s cmd = TIKTAP_GET_HWINFO!\n", __func__);
|
||
|
|
tmp = aw8624->chipid;
|
||
|
|
if (copy_to_user((void __user *)arg, &tmp, sizeof(unsigned int)))
|
||
|
|
ret = -EFAULT;
|
||
|
|
break;
|
||
|
|
case TIKTAP_SETTING_GAIN:
|
||
|
|
aw_dev_info(aw8624->dev, "%s cmd = TIKTAP_SETTING_GAIN!, arg = 0x%02lx\n", __func__, arg);
|
||
|
|
mutex_lock(&aw8624->lock);
|
||
|
|
aw8624->gain = arg;
|
||
|
|
aw8624_haptic_set_gain(aw8624, aw8624->gain);
|
||
|
|
mutex_unlock(&aw8624->lock);
|
||
|
|
break;
|
||
|
|
case TIKTAP_GET_F0:
|
||
|
|
aw_dev_info(aw8624->dev, "%s cmd = TIKTAP_GET_F0!\n", __func__);
|
||
|
|
tmp = aw8624->f0;
|
||
|
|
if (copy_to_user((void __user *)arg, &tmp, sizeof(unsigned int)))
|
||
|
|
ret = -EFAULT;
|
||
|
|
break;
|
||
|
|
case TIKTAP_WRITE_REG:
|
||
|
|
aw_dev_info(aw8624->dev, "%s cmd = TIKTAP_WRITE_REG!\n", __func__);
|
||
|
|
reg_addr = (arg & 0xFF00) >> 8;
|
||
|
|
reg_data = arg & 0x00FF;
|
||
|
|
aw8624_i2c_write(aw8624, reg_addr, reg_data);
|
||
|
|
break;
|
||
|
|
case TIKTAP_READ_REG:
|
||
|
|
aw_dev_info(aw8624->dev, "%s cmd = TIKTAP_READ_REG!\n", __func__);
|
||
|
|
if (copy_from_user(®_addr, (void __user *)arg, sizeof(unsigned char))) {
|
||
|
|
ret = -EFAULT;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
aw8624_i2c_read(aw8624, reg_addr, ®_data);
|
||
|
|
if (copy_to_user((void __user *)arg, ®_data, sizeof(unsigned char)))
|
||
|
|
ret = -EFAULT;
|
||
|
|
break;
|
||
|
|
case TIKTAP_STOP_RTP_MODE:
|
||
|
|
aw_dev_info(aw8624->dev, "%s cmd = TIKTAP_STOP_MODE!\n", __func__);
|
||
|
|
aw8624->tiktap_stop_flag = true;
|
||
|
|
aw8624->rtp_file_num = 0;
|
||
|
|
schedule_work(&aw8624->rtp_work);
|
||
|
|
break;
|
||
|
|
case TIKTAP_STOP_MODE:
|
||
|
|
aw_dev_info(aw8624->dev, "%s cmd = TIKTAP_STOP_MODE!\n", __func__);
|
||
|
|
aw8624->tiktap_stop_flag = true;
|
||
|
|
mutex_lock(&aw8624->lock);
|
||
|
|
aw8624_haptic_stop(aw8624);
|
||
|
|
mutex_unlock(&aw8624->lock);
|
||
|
|
break;
|
||
|
|
case TIKTAP_ON_MODE:
|
||
|
|
aw_dev_info(aw8624->dev, "%s cmd = TIKTAP_ON_MODE!, arg = %ld\n", __func__, arg);
|
||
|
|
tmp = arg;
|
||
|
|
vfree(aw8624->tiktap_rtp);
|
||
|
|
aw8624->tiktap_rtp = NULL;
|
||
|
|
aw8624->tiktap_rtp = vmalloc(tmp);
|
||
|
|
if (aw8624->tiktap_rtp == NULL) {
|
||
|
|
aw_dev_err(aw8624->dev, "%s malloc tiktap_rtp memory failed\n", __func__);
|
||
|
|
return -ENOMEM;
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
case TIKTAP_OFF_MODE:
|
||
|
|
aw_dev_info(aw8624->dev, "%s cmd = TIKTAP_OFF_MODE!\n", __func__);
|
||
|
|
aw8624->tiktap_stop_flag = true;
|
||
|
|
vfree(aw8624->tiktap_rtp);
|
||
|
|
break;
|
||
|
|
case TIKTAP_RTP_MODE:
|
||
|
|
aw_dev_info(aw8624->dev, "%s cmd = TIKTAP_RTP_MODE!\n", __func__);
|
||
|
|
if (aw8624->tiktap_rtp == NULL) {
|
||
|
|
aw_dev_err(aw8624->dev, "%s tiktap = NULL!\n", __func__);
|
||
|
|
ret = -EFAULT;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
aw8624->tiktap_stop_flag = true;
|
||
|
|
if(copy_from_user(&len, (void __user *)arg, sizeof(int))) {
|
||
|
|
ret = -EFAULT;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
aw_dev_info(aw8624->dev, "%s tiktap->len = %d\n", __func__, len);
|
||
|
|
if(copy_from_user(aw8624->tiktap_rtp->data, (void __user *)arg , len + sizeof(int))) {
|
||
|
|
ret = -EFAULT;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
aw8624->tiktap_rtp->len = len;
|
||
|
|
aw8624->tiktap_stop_flag = false;
|
||
|
|
schedule_work(&aw8624->rtp_tiktap);
|
||
|
|
break;
|
||
|
|
case TIKTAP_SETTING_SPEED:
|
||
|
|
aw_dev_info(aw8624->dev, "%s cmd = TIKTAP_SETTING_SPEED!, arg = 0x%02lx\n", __func__, arg);
|
||
|
|
aw8624_haptic_set_pwm(aw8624, arg);
|
||
|
|
break;
|
||
|
|
case TIKTAP_GET_SPEED:
|
||
|
|
aw_dev_info(aw8624->dev, "%s cmd = TIKTAP_GET_SPEED!\n", __func__);
|
||
|
|
tmp = aw8624->pwm_mode;
|
||
|
|
if (copy_to_user((void __user *)arg, &tmp, sizeof(unsigned int)))
|
||
|
|
ret = -EFAULT;
|
||
|
|
break;
|
||
|
|
default:
|
||
|
|
aw_dev_info(aw8624->dev, "%s unknown cmd = %d\n", __func__, cmd);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
static ssize_t aw_buf_write_proc(struct file *filp,
|
||
|
|
const char __user *buffer,
|
||
|
|
size_t count, loff_t *off)
|
||
|
|
{
|
||
|
|
struct aw8624 *aw8624 = g_aw8624;
|
||
|
|
aw_dev_info(aw8624->dev, "%s, write count %zu\n", __func__, count);
|
||
|
|
|
||
|
|
if (aw8624->ram.base_addr == 0x0000) {
|
||
|
|
aw_dev_info(aw8624->dev, "%s: no set FIFO space for rtp play!\n",
|
||
|
|
__func__);
|
||
|
|
return count;
|
||
|
|
}
|
||
|
|
|
||
|
|
mutex_lock(&aw8624->rtp_lock);
|
||
|
|
aw_dev_info(aw8624->dev, "%s, copy form user start...\n", __func__);
|
||
|
|
aw8624->rtp_init = 0;
|
||
|
|
vfree(aw8624->rtp_container);
|
||
|
|
aw8624->rtp_container = vmalloc(count + sizeof(int));
|
||
|
|
if (!aw8624->rtp_container) {
|
||
|
|
aw_dev_err(aw8624->dev, "%s: error allocating memory\n",
|
||
|
|
__func__);
|
||
|
|
mutex_unlock(&aw8624->rtp_lock);
|
||
|
|
return -EFAULT;
|
||
|
|
}
|
||
|
|
aw8624->rtp_container->len = count;
|
||
|
|
if (copy_from_user(aw8624->rtp_container->data, buffer, count)) {
|
||
|
|
aw_dev_err(aw8624->dev, "Failed copy from user\n");
|
||
|
|
mutex_unlock(&aw8624->rtp_lock);
|
||
|
|
return -EFAULT;
|
||
|
|
}
|
||
|
|
aw8624->rtp_init = 1;
|
||
|
|
aw_dev_info(aw8624->dev, "%s, copy form user end...\n", __func__);
|
||
|
|
mutex_unlock(&aw8624->rtp_lock);
|
||
|
|
aw8624->tiktap_stop_flag = false;
|
||
|
|
schedule_work(&aw8624->rtp_irq_tiktap);
|
||
|
|
return count;
|
||
|
|
}
|
||
|
|
|
||
|
|
static const struct file_operations config_proc_ops = {
|
||
|
|
.owner = THIS_MODULE,
|
||
|
|
.write = aw_buf_write_proc,
|
||
|
|
.unlocked_ioctl = aw8624_tiktap_unlocked_ioctl,
|
||
|
|
};
|
||
|
|
#endif
|
||
|
|
|
||
|
|
int aw8624_vibrator_init(struct aw8624 *aw8624)
|
||
|
|
{
|
||
|
|
int ret = 0;
|
||
|
|
|
||
|
|
aw_dev_info(aw8624->dev, "%s enter\n", __func__);
|
||
|
|
|
||
|
|
#ifdef TIMED_OUTPUT
|
||
|
|
aw8624->to_dev.name = "awinic_vibrator";
|
||
|
|
aw8624->to_dev.get_time = aw8624_vibrator_get_time;
|
||
|
|
aw8624->to_dev.enable = aw8624_vibrator_enable;
|
||
|
|
|
||
|
|
ret = timed_output_dev_register(&(aw8624->to_dev));
|
||
|
|
if (ret < 0) {
|
||
|
|
dev_err(aw8624->dev, "%s: fail to create timed output dev\n",
|
||
|
|
__func__);
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
ret = sysfs_create_group(&aw8624->to_dev.dev->kobj,
|
||
|
|
&aw8624_vibrator_attribute_group);
|
||
|
|
if (ret < 0) {
|
||
|
|
dev_err(aw8624->dev,
|
||
|
|
"%s error creating sysfs attr files\n",
|
||
|
|
__func__);
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
#else
|
||
|
|
aw8624->cdev.name = "awinic_vibrator";
|
||
|
|
aw8624->cdev.brightness_set = aw8624_vibrator_enable;
|
||
|
|
|
||
|
|
|
||
|
|
ret = devm_led_classdev_register(&aw8624->i2c->dev, &(aw8624->cdev));
|
||
|
|
if (ret < 0) {
|
||
|
|
dev_err(aw8624->dev, "%s: fail to create leds dev\n",
|
||
|
|
__func__);
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
ret = sysfs_create_group(&aw8624->cdev.dev->kobj,
|
||
|
|
&aw8624_vibrator_attribute_group);
|
||
|
|
if (ret < 0) {
|
||
|
|
dev_err(aw8624->dev, "%s error creating sysfs attr files\n",
|
||
|
|
__func__);
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
|
||
|
|
#ifdef AW_TIKTAP
|
||
|
|
aw8624->aw_config_proc = NULL;
|
||
|
|
aw8624->aw_config_proc = proc_create(AW_TIKTAP_PROCNAME, 0666,
|
||
|
|
NULL, &config_proc_ops);
|
||
|
|
if (aw8624->aw_config_proc == NULL)
|
||
|
|
dev_err(aw8624->dev, "create_proc_entry %s failed\n",
|
||
|
|
AW_TIKTAP_PROCNAME);
|
||
|
|
else
|
||
|
|
dev_info(aw8624->dev, "create proc entry %s success\n",
|
||
|
|
AW_TIKTAP_PROCNAME);
|
||
|
|
INIT_WORK(&aw8624->rtp_irq_tiktap, aw8624_rtp_irq_work_tiktap);
|
||
|
|
INIT_WORK(&aw8624->rtp_tiktap, aw8624_rtp_work_tiktap);
|
||
|
|
#endif
|
||
|
|
|
||
|
|
hrtimer_init(&aw8624->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
|
||
|
|
aw8624->timer.function = aw8624_vibrator_timer_func;
|
||
|
|
INIT_WORK(&aw8624->vibrator_work, aw8624_vibrator_work_routine);
|
||
|
|
|
||
|
|
if (aw8624->IsUsedIRQ)
|
||
|
|
INIT_WORK(&aw8624->rtp_work, aw8624_rtp_work_routine);
|
||
|
|
|
||
|
|
mutex_init(&aw8624->lock);
|
||
|
|
mutex_init(&aw8624->rtp_lock);
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
irqreturn_t aw8624_irq(int irq, void *data)
|
||
|
|
{
|
||
|
|
struct aw8624 *aw8624 = data;
|
||
|
|
unsigned char reg_val = 0;
|
||
|
|
unsigned char glb_st = 0;
|
||
|
|
unsigned int buf_len = 0;
|
||
|
|
|
||
|
|
aw8624_i2c_read(aw8624, AW8624_REG_SYSINT, ®_val);
|
||
|
|
if (reg_val & AW8624_BIT_SYSINT_UVLI)
|
||
|
|
aw_dev_err(aw8624->dev, "%s chip uvlo int error\n", __func__);
|
||
|
|
if (reg_val & AW8624_BIT_SYSINT_OCDI)
|
||
|
|
aw_dev_err(aw8624->dev, "%s chip over current int error\n",
|
||
|
|
__func__);
|
||
|
|
if (reg_val & AW8624_BIT_SYSINT_OTI)
|
||
|
|
aw_dev_err(aw8624->dev, "%s chip over temperature int error\n",
|
||
|
|
__func__);
|
||
|
|
if (reg_val & AW8624_BIT_SYSINT_DONEI)
|
||
|
|
aw_dev_info(aw8624->dev, "%s chip playback done\n", __func__);
|
||
|
|
|
||
|
|
|
||
|
|
if (reg_val & AW8624_BIT_SYSINT_UVLI) {
|
||
|
|
aw8624_i2c_read(aw8624, AW8624_REG_GLB_STATE, &glb_st);
|
||
|
|
if (glb_st == 0) {
|
||
|
|
aw8624_i2c_write_bits(aw8624,
|
||
|
|
AW8624_REG_SYSINTM,
|
||
|
|
AW8624_BIT_SYSINTM_UVLO_MASK,
|
||
|
|
AW8624_BIT_SYSINTM_UVLO_OFF);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (reg_val & AW8624_BIT_SYSINT_FF_AEI) {
|
||
|
|
aw_dev_info(aw8624->dev, "%s: aw8624 rtp fifo almost empty\n",
|
||
|
|
__func__);
|
||
|
|
if (aw8624->rtp_init) {
|
||
|
|
while ((!aw8624_haptic_rtp_get_fifo_afs(aw8624)) &&
|
||
|
|
(aw8624->play_mode == AW8624_HAPTIC_RTP_MODE)) {
|
||
|
|
mutex_lock(&aw8624->rtp_lock);
|
||
|
|
if (!aw8624->rtp_cnt) {
|
||
|
|
aw_dev_info(aw8624->dev, "%s:aw8624->rtp_cnt is 0!\n",
|
||
|
|
__func__);
|
||
|
|
mutex_unlock(&aw8624->rtp_lock);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
#ifdef AW_ENABLE_RTP_PRINT_LOG
|
||
|
|
aw_dev_info(aw8624->dev,
|
||
|
|
"%s: aw8624 rtp mode fifo update, cnt=%d\n",
|
||
|
|
__func__, aw8624->rtp_cnt);
|
||
|
|
#endif
|
||
|
|
if (!aw8624->rtp_container) {
|
||
|
|
aw_dev_info(aw8624->dev,
|
||
|
|
"%s:aw8624->rtp_container is null, break!\n",
|
||
|
|
__func__);
|
||
|
|
mutex_unlock(&aw8624->rtp_lock);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
if ((aw8624->rtp_container->len-aw8624->rtp_cnt) <
|
||
|
|
(aw8624->ram.base_addr>>3)) {
|
||
|
|
buf_len =
|
||
|
|
aw8624->rtp_container->len-aw8624->rtp_cnt;
|
||
|
|
} else {
|
||
|
|
buf_len = (aw8624->ram.base_addr>>3);
|
||
|
|
}
|
||
|
|
aw8624_i2c_writes(aw8624,
|
||
|
|
AW8624_REG_RTP_DATA,
|
||
|
|
&aw8624->rtp_container->data[aw8624->rtp_cnt],
|
||
|
|
buf_len);
|
||
|
|
aw8624->rtp_cnt += buf_len;
|
||
|
|
aw8624_i2c_read(aw8624, AW8624_REG_GLB_STATE,
|
||
|
|
&glb_st);
|
||
|
|
if ((aw8624->rtp_cnt == aw8624->rtp_container->len) ||
|
||
|
|
((glb_st & 0x0f) == 0x00)) {
|
||
|
|
if (aw8624->rtp_cnt ==
|
||
|
|
aw8624->rtp_container->len)
|
||
|
|
aw_dev_info(aw8624->dev,
|
||
|
|
"%s: rtp load completely! glb_st=%02x aw8624->rtp_cnt=%d\n",
|
||
|
|
__func__, glb_st,
|
||
|
|
aw8624->rtp_cnt);
|
||
|
|
else
|
||
|
|
aw_dev_err(aw8624->dev,
|
||
|
|
"%s rtp load failed!! glb_st=%02x aw8624->rtp_cnt=%d\n",
|
||
|
|
__func__, glb_st,
|
||
|
|
aw8624->rtp_cnt);
|
||
|
|
aw8624_haptic_set_rtp_aei(aw8624,
|
||
|
|
false);
|
||
|
|
aw8624->rtp_cnt = 0;
|
||
|
|
aw8624->rtp_init = 0;
|
||
|
|
mutex_unlock(&aw8624->rtp_lock);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
mutex_unlock(&aw8624->rtp_lock);
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
aw_dev_err(aw8624->dev,
|
||
|
|
"%s: aw8624 rtp init = %d, init error\n",
|
||
|
|
__func__, aw8624->rtp_init);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (reg_val & AW8624_BIT_SYSINT_FF_AFI)
|
||
|
|
aw_dev_info(aw8624->dev,
|
||
|
|
"%s: aw8624 rtp mode fifo full\n", __func__);
|
||
|
|
|
||
|
|
if (aw8624->play_mode != AW8624_HAPTIC_RTP_MODE)
|
||
|
|
aw8624_haptic_set_rtp_aei(aw8624, false);
|
||
|
|
|
||
|
|
/*aw8624_i2c_read(aw8624, AW8624_REG_SYSINT, ®_val);*/
|
||
|
|
/*aw8624_i2c_read(aw8624, AW8624_REG_SYSST, ®_val);*/
|
||
|
|
|
||
|
|
return IRQ_HANDLED;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int aw8624_analyse_duration_range(struct aw8624 *aw8624)
|
||
|
|
{
|
||
|
|
int i = 0;
|
||
|
|
int ret = 0;
|
||
|
|
int len = 0;
|
||
|
|
int *duration_time = NULL;
|
||
|
|
|
||
|
|
len = ARRAY_SIZE(aw8624_dts_data.aw8624_duration_time);
|
||
|
|
duration_time = aw8624_dts_data.aw8624_duration_time;
|
||
|
|
if (len < 2) {
|
||
|
|
aw_dev_err(aw8624->dev, "%s: duration time range error\n",
|
||
|
|
__func__);
|
||
|
|
return -ERANGE;
|
||
|
|
}
|
||
|
|
for (i = (len - 1); i > 0; i--) {
|
||
|
|
if (duration_time[i] > duration_time[i-1])
|
||
|
|
continue;
|
||
|
|
else
|
||
|
|
break;
|
||
|
|
|
||
|
|
}
|
||
|
|
if (i > 0) {
|
||
|
|
aw_dev_err(aw8624->dev, "%s: duration time range error\n",
|
||
|
|
__func__);
|
||
|
|
ret = -ERANGE;
|
||
|
|
}
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int
|
||
|
|
aw8624_analyse_duration_array_size(struct aw8624 *aw8624, struct device_node *np)
|
||
|
|
{
|
||
|
|
int ret = 0;
|
||
|
|
|
||
|
|
ret = of_property_count_elems_of_size(np,
|
||
|
|
"aw8624_vib_duration_time", 4);
|
||
|
|
if (ret < 0) {
|
||
|
|
aw8624->duration_time_flag = -1;
|
||
|
|
aw_dev_info(aw8624->dev,
|
||
|
|
"%s vib_duration_time not found\n", __func__);
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
aw8624->duration_time_size = ret;
|
||
|
|
if (aw8624->duration_time_size > 3) {
|
||
|
|
aw8624->duration_time_flag = -1;
|
||
|
|
aw_dev_info(aw8624->dev,
|
||
|
|
"%s vib_duration_time error, array size = %d\n",
|
||
|
|
__func__, aw8624->duration_time_size);
|
||
|
|
return -ERANGE;
|
||
|
|
}
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
int aw8624_parse_dt(struct aw8624 *aw8624, struct device *dev,
|
||
|
|
struct device_node *np) {
|
||
|
|
unsigned int val = 0;
|
||
|
|
/*unsigned int brake_ram_config[24];*/
|
||
|
|
unsigned int brake_cont_config[24];
|
||
|
|
unsigned int f0_trace_parameter[4];
|
||
|
|
unsigned int bemf_config[4];
|
||
|
|
unsigned int duration_time[3];
|
||
|
|
unsigned int sw_brake[2];
|
||
|
|
unsigned int trig_config_temp[5];
|
||
|
|
int ret = 0;
|
||
|
|
|
||
|
|
val =
|
||
|
|
of_property_read_u32(np, "aw8624_vib_mode",
|
||
|
|
&aw8624_dts_data.aw8624_mode);
|
||
|
|
if (val != 0)
|
||
|
|
aw_dev_info(aw8624->dev, "aw8624_vib_mode not found\n");
|
||
|
|
val =
|
||
|
|
of_property_read_u32(np, "aw8624_vib_f0_pre",
|
||
|
|
&aw8624_dts_data.aw8624_f0_pre);
|
||
|
|
if (val != 0)
|
||
|
|
aw_dev_info(aw8624->dev, "aw8624_vib_f0_pre not found\n");
|
||
|
|
val =
|
||
|
|
of_property_read_u32(np, "aw8624_vib_f0_cali_percen",
|
||
|
|
&aw8624_dts_data.aw8624_f0_cali_percen);
|
||
|
|
if (val != 0)
|
||
|
|
aw_dev_info(aw8624->dev,
|
||
|
|
"aw8624_vib_f0_cali_percen not found\n");
|
||
|
|
val =
|
||
|
|
of_property_read_u32(np, "aw8624_vib_cont_drv_lev",
|
||
|
|
&aw8624_dts_data.aw8624_cont_drv_lvl);
|
||
|
|
if (val != 0)
|
||
|
|
aw_dev_info(aw8624->dev,
|
||
|
|
"aw8624_vib_cont_drv_lev not found\n");
|
||
|
|
val =
|
||
|
|
of_property_read_u32(np, "aw8624_vib_cont_drv_lvl_ov",
|
||
|
|
&aw8624_dts_data.aw8624_cont_drv_lvl_ov);
|
||
|
|
if (val != 0)
|
||
|
|
aw_dev_info(aw8624->dev,
|
||
|
|
"aw8624_vib_cont_drv_lvl_ov not found\n");
|
||
|
|
val =
|
||
|
|
of_property_read_u32(np, "aw8624_vib_cont_td",
|
||
|
|
&aw8624_dts_data.aw8624_cont_td);
|
||
|
|
if (val != 0)
|
||
|
|
aw_dev_info(aw8624->dev, "aw8624_vib_cont_td not found\n");
|
||
|
|
val =
|
||
|
|
of_property_read_u32(np, "aw8624_vib_cont_zc_thr",
|
||
|
|
&aw8624_dts_data.aw8624_cont_zc_thr);
|
||
|
|
if (val != 0)
|
||
|
|
aw_dev_info(aw8624->dev, "aw8624_vib_cont_zc_thr not found\n");
|
||
|
|
val =
|
||
|
|
of_property_read_u32(np, "aw8624_vib_cont_num_brk",
|
||
|
|
&aw8624_dts_data.aw8624_cont_num_brk);
|
||
|
|
if (val != 0)
|
||
|
|
aw_dev_info(aw8624->dev,
|
||
|
|
"aw8624_vib_cont_num_brk not found\n");
|
||
|
|
val =
|
||
|
|
of_property_read_u32(np, "aw8624_vib_f0_coeff",
|
||
|
|
&aw8624_dts_data.aw8624_f0_coeff);
|
||
|
|
if (val != 0)
|
||
|
|
aw_dev_info(aw8624->dev,
|
||
|
|
"aw8624_vib_f0_coeff not found\n");
|
||
|
|
val = of_property_read_u32_array(np, "aw8624_vib_brake_cont_config",
|
||
|
|
brake_cont_config, ARRAY_SIZE(brake_cont_config));
|
||
|
|
if (val != 0)
|
||
|
|
aw_dev_info(aw8624->dev,
|
||
|
|
"%s vib_brake_cont_config not found\n", __func__);
|
||
|
|
memcpy(aw8624_dts_data.aw8624_cont_brake,
|
||
|
|
brake_cont_config, sizeof(brake_cont_config));
|
||
|
|
|
||
|
|
val = of_property_read_u32_array(np, "aw8624_vib_f0_trace_parameter",
|
||
|
|
f0_trace_parameter, ARRAY_SIZE(f0_trace_parameter));
|
||
|
|
if (val != 0)
|
||
|
|
aw_dev_info(aw8624->dev,
|
||
|
|
"%s vib_f0_trace_parameter not found\n", __func__);
|
||
|
|
memcpy(aw8624_dts_data.aw8624_f0_trace_parameter,
|
||
|
|
f0_trace_parameter, sizeof(f0_trace_parameter));
|
||
|
|
|
||
|
|
val = of_property_read_u32_array(np, "aw8624_vib_bemf_config",
|
||
|
|
bemf_config, ARRAY_SIZE(bemf_config));
|
||
|
|
if (val != 0)
|
||
|
|
aw_dev_info(aw8624->dev,
|
||
|
|
"%s vib_bemf_config not found\n", __func__);
|
||
|
|
memcpy(aw8624_dts_data.aw8624_bemf_config,
|
||
|
|
bemf_config, sizeof(bemf_config));
|
||
|
|
|
||
|
|
val =
|
||
|
|
of_property_read_u32_array(np, "aw8624_vib_sw_brake",
|
||
|
|
sw_brake, ARRAY_SIZE(sw_brake));
|
||
|
|
if (val != 0)
|
||
|
|
aw_dev_info(aw8624->dev,
|
||
|
|
"%s vib_wavseq not found\n", __func__);
|
||
|
|
memcpy(aw8624_dts_data.aw8624_sw_brake,
|
||
|
|
sw_brake, sizeof(sw_brake));
|
||
|
|
|
||
|
|
val = of_property_read_u32(np, "aw8624_vib_tset",
|
||
|
|
&aw8624_dts_data.aw8624_tset);
|
||
|
|
if (val != 0)
|
||
|
|
aw_dev_info(aw8624->dev, "%s vib_tset not found\n", __func__);
|
||
|
|
val = of_property_read_u32_array(np, "aw8624_vib_duration_time",
|
||
|
|
duration_time, ARRAY_SIZE(duration_time));
|
||
|
|
if (val != 0)
|
||
|
|
aw_dev_info(aw8624->dev,
|
||
|
|
"%s vib_duration_time not found\n", __func__);
|
||
|
|
ret = aw8624_analyse_duration_array_size(aw8624, np);
|
||
|
|
if (!ret)
|
||
|
|
memcpy(aw8624_dts_data.aw8624_duration_time,
|
||
|
|
duration_time, sizeof(duration_time));
|
||
|
|
val =
|
||
|
|
of_property_read_u32_array(np,
|
||
|
|
"aw8624_vib_trig_config",
|
||
|
|
trig_config_temp,
|
||
|
|
ARRAY_SIZE(trig_config_temp));
|
||
|
|
if (val != 0)
|
||
|
|
aw_dev_info(aw8624->dev, "%s vib_trig_config not found\n",
|
||
|
|
__func__);
|
||
|
|
memcpy(aw8624_dts_data.trig_config, trig_config_temp,
|
||
|
|
sizeof(trig_config_temp));
|
||
|
|
return 0;
|
||
|
|
}
|