Merge "msm: qpnp-haptic: add additional checks to avoid division by zero."
This commit is contained in:
commit
adb1c6ac95
2 changed files with 291 additions and 81 deletions
|
@ -66,6 +66,40 @@ Optional properties when qcom,actuator-type is "lra"
|
||||||
"none", "opt1", "opt2" and "opt3" (default)
|
"none", "opt1", "opt2" and "opt3" (default)
|
||||||
- qcom,lra-res-cal-period : Auto resonance calibration period. The values range from
|
- qcom,lra-res-cal-period : Auto resonance calibration period. The values range from
|
||||||
4 to 32(default)
|
4 to 32(default)
|
||||||
|
- qcom,perform-lra-auto-resonance-search : boolean, define this property if:
|
||||||
|
a) the underlying PMI chip does not have a register in the MISC block to
|
||||||
|
read the error percentage in RC clock
|
||||||
|
b) the actuator type is LRA
|
||||||
|
Defining this causes the auto resonance search algorithm to be be performed
|
||||||
|
for such devices.
|
||||||
|
c) This property is not defined by default.
|
||||||
|
|
||||||
|
- qcom,drive-period-code-max-limit-percent-variation: RATE_CFG1 and RATE_CFG2 registers will
|
||||||
|
be updated with the values from AUTO_RES_LO and AUTO_RES_HI registers
|
||||||
|
only if the variation from the resonant frequency is within the value
|
||||||
|
mentioned by this property on the higher side.
|
||||||
|
The default value is 25, which means if the drive period code resulting
|
||||||
|
from AUTO_RES register's is more than 25 percent of the existing drive
|
||||||
|
period code, then driver does not update RATE_CFG registers.
|
||||||
|
- qcom,drive-period-code-min-limit-percent-variation: RATE_CFG1 and RATE_CFG2 registers will
|
||||||
|
be updated with the values from AUTO_RES_LO and AUTO_RES_HI registers
|
||||||
|
only if the variation from the resonant frequency is within the value
|
||||||
|
mentioned by this property on the lower side.
|
||||||
|
The default value is 25, which means if the drive period code resulting
|
||||||
|
from AUTO_RES register's is less than 25 percent of the existing drive
|
||||||
|
period code, then driver does not update RATE_CFG registers.
|
||||||
|
|
||||||
|
Optional properties when qcom,lra-auto-res-mode is "qwd"
|
||||||
|
- qcom,time-required-to-generate-back-emf-us: Time period required to generate sufficient
|
||||||
|
back-emf (in case of QWD mode only) in us. For auto resonance
|
||||||
|
detection to work properly,sufficient back-emf has to be
|
||||||
|
generated. In general, back-emf takes some time to build up.
|
||||||
|
When the auto resonance mode is chosen as QWD, high-z will
|
||||||
|
be applied for every LRA cycle and hence there won't be
|
||||||
|
enough back-emf at the start-up. So we need to drive the
|
||||||
|
motor for a few LRA cycles. Hence, auto resonance detection
|
||||||
|
is enabled after this delay period after the PLAY bit is
|
||||||
|
asserted. The default value is 20000us.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
qcom,haptic@c000 {
|
qcom,haptic@c000 {
|
||||||
|
@ -94,4 +128,5 @@ Example:
|
||||||
qcom,lra-high-z = "opt1";
|
qcom,lra-high-z = "opt1";
|
||||||
qcom,lra-auto-res-mode = "qwd";
|
qcom,lra-auto-res-mode = "qwd";
|
||||||
qcom,lra-res-cal-period = <4>;
|
qcom,lra-res-cal-period = <4>;
|
||||||
|
qcom,time-required-to-generate-back-emf-us = <20000>;
|
||||||
};
|
};
|
||||||
|
|
|
@ -141,9 +141,7 @@
|
||||||
#define QPNP_HAP_CYCLS 5
|
#define QPNP_HAP_CYCLS 5
|
||||||
#define QPNP_TEST_TIMER_MS 5
|
#define QPNP_TEST_TIMER_MS 5
|
||||||
|
|
||||||
#define AUTO_RES_ENABLE_TIMEOUT 20000
|
#define QPNP_HAP_TIME_REQ_FOR_BACK_EMF_GEN 20000
|
||||||
#define AUTO_RES_ERR_CAPTURE_RES 5
|
|
||||||
#define AUTO_RES_ERR_MAX 15
|
|
||||||
|
|
||||||
#define MISC_TRIM_ERROR_RC19P2_CLK 0x09F5
|
#define MISC_TRIM_ERROR_RC19P2_CLK 0x09F5
|
||||||
#define MISC_SEC_ACCESS 0x09D0
|
#define MISC_SEC_ACCESS 0x09D0
|
||||||
|
@ -152,8 +150,22 @@
|
||||||
|
|
||||||
#define POLL_TIME_AUTO_RES_ERR_NS (5 * NSEC_PER_MSEC)
|
#define POLL_TIME_AUTO_RES_ERR_NS (5 * NSEC_PER_MSEC)
|
||||||
|
|
||||||
#define LRA_POS_FREQ_COUNT 6
|
#define MAX_POSITIVE_VARIATION_LRA_FREQ 30
|
||||||
int lra_play_rate_code[LRA_POS_FREQ_COUNT];
|
#define MAX_NEGATIVE_VARIATION_LRA_FREQ -30
|
||||||
|
#define FREQ_VARIATION_STEP 5
|
||||||
|
#define AUTO_RES_ERROR_CAPTURE_RES 5
|
||||||
|
#define AUTO_RES_ERROR_MAX 30
|
||||||
|
#define ADJUSTED_LRA_PLAY_RATE_CODE_ARRSIZE \
|
||||||
|
((MAX_POSITIVE_VARIATION_LRA_FREQ - MAX_NEGATIVE_VARIATION_LRA_FREQ) \
|
||||||
|
/ FREQ_VARIATION_STEP)
|
||||||
|
#define LRA_DRIVE_PERIOD_POS_ERR(hap, rc_clk_err_percent) \
|
||||||
|
(hap->init_drive_period_code = (hap->init_drive_period_code * \
|
||||||
|
(1000 + rc_clk_err_percent_x10)) / 1000)
|
||||||
|
#define LRA_DRIVE_PERIOD_NEG_ERR(hap, rc_clk_err_percent) \
|
||||||
|
(hap->init_drive_period_code = (hap->init_drive_period_code * \
|
||||||
|
(1000 - rc_clk_err_percent_x10)) / 1000)
|
||||||
|
|
||||||
|
u32 adjusted_lra_play_rate_code[ADJUSTED_LRA_PLAY_RATE_CODE_ARRSIZE];
|
||||||
|
|
||||||
/* haptic debug register set */
|
/* haptic debug register set */
|
||||||
static u8 qpnp_hap_dbg_regs[] = {
|
static u8 qpnp_hap_dbg_regs[] = {
|
||||||
|
@ -246,10 +258,21 @@ struct qpnp_pwm_info {
|
||||||
* @ pwm_info - pwm info
|
* @ pwm_info - pwm info
|
||||||
* @ lock - mutex lock
|
* @ lock - mutex lock
|
||||||
* @ wf_lock - mutex lock for waveform
|
* @ wf_lock - mutex lock for waveform
|
||||||
|
* @ init_drive_period_code - the initial lra drive period code
|
||||||
|
* @ drive_period_code_max_limit_percent_variation - maximum limit of
|
||||||
|
percentage variation of drive period code
|
||||||
|
* @ drive_period_code_min_limit_percent_variation - minimum limit og
|
||||||
|
percentage variation of drive period code
|
||||||
|
* @ drive_period_code_max_limit - calculated drive period code with
|
||||||
|
percentage variation on the higher side.
|
||||||
|
* @ drive_period_code_min_limit - calculated drive period code with
|
||||||
|
percentage variation on the lower side
|
||||||
* @ play_mode - play mode
|
* @ play_mode - play mode
|
||||||
* @ auto_res_mode - auto resonace mode
|
* @ auto_res_mode - auto resonace mode
|
||||||
* @ lra_high_z - high z option line
|
* @ lra_high_z - high z option line
|
||||||
* @ timeout_ms - max timeout in ms
|
* @ timeout_ms - max timeout in ms
|
||||||
|
* @ time_required_to_generate_back_emf_us - the time required for sufficient
|
||||||
|
back-emf to be generated for auto resonance to be successful
|
||||||
* @ vmax_mv - max voltage in mv
|
* @ vmax_mv - max voltage in mv
|
||||||
* @ ilim_ma - limiting current in ma
|
* @ ilim_ma - limiting current in ma
|
||||||
* @ sc_deb_cycles - short circuit debounce cycles
|
* @ sc_deb_cycles - short circuit debounce cycles
|
||||||
|
@ -280,6 +303,8 @@ struct qpnp_pwm_info {
|
||||||
* @ sup_brake_pat - support custom brake pattern
|
* @ sup_brake_pat - support custom brake pattern
|
||||||
* @ correct_lra_drive_freq - correct LRA Drive Frequency
|
* @ correct_lra_drive_freq - correct LRA Drive Frequency
|
||||||
* @ misc_trim_error_rc19p2_clk_reg_present - if MISC Trim Error reg is present
|
* @ misc_trim_error_rc19p2_clk_reg_present - if MISC Trim Error reg is present
|
||||||
|
* @ perform_lra_auto_resonance_search - whether lra auto resonance search
|
||||||
|
* algorithm should be performed or not.
|
||||||
*/
|
*/
|
||||||
struct qpnp_hap {
|
struct qpnp_hap {
|
||||||
struct platform_device *pdev;
|
struct platform_device *pdev;
|
||||||
|
@ -300,7 +325,9 @@ struct qpnp_hap {
|
||||||
enum qpnp_hap_mode play_mode;
|
enum qpnp_hap_mode play_mode;
|
||||||
enum qpnp_hap_auto_res_mode auto_res_mode;
|
enum qpnp_hap_auto_res_mode auto_res_mode;
|
||||||
enum qpnp_hap_high_z lra_high_z;
|
enum qpnp_hap_high_z lra_high_z;
|
||||||
|
u32 init_drive_period_code;
|
||||||
u32 timeout_ms;
|
u32 timeout_ms;
|
||||||
|
u32 time_required_to_generate_back_emf_us;
|
||||||
u32 vmax_mv;
|
u32 vmax_mv;
|
||||||
u32 ilim_ma;
|
u32 ilim_ma;
|
||||||
u32 sc_deb_cycles;
|
u32 sc_deb_cycles;
|
||||||
|
@ -312,11 +339,15 @@ struct qpnp_hap {
|
||||||
u32 play_irq;
|
u32 play_irq;
|
||||||
u32 sc_irq;
|
u32 sc_irq;
|
||||||
u16 base;
|
u16 base;
|
||||||
|
u16 drive_period_code_max_limit;
|
||||||
|
u16 drive_period_code_min_limit;
|
||||||
|
u8 drive_period_code_max_limit_percent_variation;
|
||||||
|
u8 drive_period_code_min_limit_percent_variation;
|
||||||
u8 act_type;
|
u8 act_type;
|
||||||
u8 wave_shape;
|
u8 wave_shape;
|
||||||
u8 wave_samp[QPNP_HAP_WAV_SAMP_LEN];
|
u8 wave_samp[QPNP_HAP_WAV_SAMP_LEN];
|
||||||
u8 shadow_wave_samp[QPNP_HAP_WAV_SAMP_LEN];
|
u8 shadow_wave_samp[QPNP_HAP_WAV_SAMP_LEN];
|
||||||
u8 brake_pat[QPNP_HAP_BRAKE_PAT_LEN];
|
u8 brake_pat[QPNP_HAP_BRAKE_PAT_LEN];
|
||||||
u8 reg_en_ctl;
|
u8 reg_en_ctl;
|
||||||
u8 reg_play;
|
u8 reg_play;
|
||||||
u8 lra_res_cal_period;
|
u8 lra_res_cal_period;
|
||||||
|
@ -333,6 +364,7 @@ struct qpnp_hap {
|
||||||
bool sup_brake_pat;
|
bool sup_brake_pat;
|
||||||
bool correct_lra_drive_freq;
|
bool correct_lra_drive_freq;
|
||||||
bool misc_trim_error_rc19p2_clk_reg_present;
|
bool misc_trim_error_rc19p2_clk_reg_present;
|
||||||
|
bool perform_lra_auto_resonance_search;
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct qpnp_hap *ghap;
|
static struct qpnp_hap *ghap;
|
||||||
|
@ -1314,29 +1346,62 @@ static struct device_attribute qpnp_hap_attrs[] = {
|
||||||
qpnp_hap_min_max_test_data_store),
|
qpnp_hap_min_max_test_data_store),
|
||||||
};
|
};
|
||||||
|
|
||||||
static void calculate_lra_code(struct qpnp_hap *hap)
|
static int calculate_lra_code(struct qpnp_hap *hap)
|
||||||
{
|
{
|
||||||
u8 play_rate_code_lo, play_rate_code_hi;
|
u8 lra_drive_period_code_lo = 0, lra_drive_period_code_hi = 0;
|
||||||
int play_rate_code, neg_idx = 0, pos_idx = LRA_POS_FREQ_COUNT-1;
|
u32 lra_drive_period_code, lra_drive_frequency_hz, freq_variation;
|
||||||
int lra_init_freq, freq_variation, start_variation = AUTO_RES_ERR_MAX;
|
u8 start_variation = AUTO_RES_ERROR_MAX, i;
|
||||||
|
u8 neg_idx = 0, pos_idx = ADJUSTED_LRA_PLAY_RATE_CODE_ARRSIZE - 1;
|
||||||
|
int rc = 0;
|
||||||
|
|
||||||
qpnp_hap_read_reg(hap, &play_rate_code_lo,
|
rc = qpnp_hap_read_reg(hap, &lra_drive_period_code_lo,
|
||||||
QPNP_HAP_RATE_CFG1_REG(hap->base));
|
QPNP_HAP_RATE_CFG1_REG(hap->base));
|
||||||
qpnp_hap_read_reg(hap, &play_rate_code_hi,
|
if (rc) {
|
||||||
QPNP_HAP_RATE_CFG2_REG(hap->base));
|
dev_err(&hap->pdev->dev,
|
||||||
|
"Error while reading RATE_CFG1 register\n");
|
||||||
play_rate_code = (play_rate_code_hi << 8) | (play_rate_code_lo & 0xff);
|
return rc;
|
||||||
|
|
||||||
lra_init_freq = 200000 / play_rate_code;
|
|
||||||
|
|
||||||
while (start_variation >= AUTO_RES_ERR_CAPTURE_RES) {
|
|
||||||
freq_variation = (lra_init_freq * start_variation) / 100;
|
|
||||||
lra_play_rate_code[neg_idx++] = 200000 / (lra_init_freq -
|
|
||||||
freq_variation);
|
|
||||||
lra_play_rate_code[pos_idx--] = 200000 / (lra_init_freq +
|
|
||||||
freq_variation);
|
|
||||||
start_variation -= AUTO_RES_ERR_CAPTURE_RES;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rc = qpnp_hap_read_reg(hap, &lra_drive_period_code_hi,
|
||||||
|
QPNP_HAP_RATE_CFG2_REG(hap->base));
|
||||||
|
if (rc) {
|
||||||
|
dev_err(&hap->pdev->dev,
|
||||||
|
"Error while reading RATE_CFG2 register\n");
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!lra_drive_period_code_lo && !lra_drive_period_code_hi) {
|
||||||
|
dev_err(&hap->pdev->dev,
|
||||||
|
"Unexpected Error: both RATE_CFG1 and RATE_CFG2 read 0\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
lra_drive_period_code =
|
||||||
|
(lra_drive_period_code_hi << 8) | (lra_drive_period_code_lo & 0xff);
|
||||||
|
lra_drive_frequency_hz = 200000 / lra_drive_period_code;
|
||||||
|
|
||||||
|
while (start_variation >= AUTO_RES_ERROR_CAPTURE_RES) {
|
||||||
|
freq_variation =
|
||||||
|
(lra_drive_frequency_hz * start_variation) / 100;
|
||||||
|
adjusted_lra_play_rate_code[neg_idx++] =
|
||||||
|
200000 / (lra_drive_frequency_hz - freq_variation);
|
||||||
|
adjusted_lra_play_rate_code[pos_idx--] =
|
||||||
|
200000 / (lra_drive_frequency_hz + freq_variation);
|
||||||
|
start_variation -= AUTO_RES_ERROR_CAPTURE_RES;
|
||||||
|
}
|
||||||
|
|
||||||
|
dev_dbg(&hap->pdev->dev,
|
||||||
|
"lra_drive_period_code_lo = 0x%x lra_drive_period_code_hi = 0x%x\n"
|
||||||
|
"lra_drive_period_code = 0x%x, lra_drive_frequency_hz = 0x%x\n"
|
||||||
|
"Calculated play rate code values are :\n",
|
||||||
|
lra_drive_period_code_lo, lra_drive_period_code_hi,
|
||||||
|
lra_drive_period_code, lra_drive_frequency_hz);
|
||||||
|
|
||||||
|
for (i = 0; i < ADJUSTED_LRA_PLAY_RATE_CODE_ARRSIZE; ++i)
|
||||||
|
dev_dbg(&hap->pdev->dev,
|
||||||
|
" 0x%x", adjusted_lra_play_rate_code[i]);
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int qpnp_hap_auto_res_enable(struct qpnp_hap *hap, int enable)
|
static int qpnp_hap_auto_res_enable(struct qpnp_hap *hap, int enable)
|
||||||
|
@ -1369,20 +1434,37 @@ static int qpnp_hap_auto_res_enable(struct qpnp_hap *hap, int enable)
|
||||||
static void update_lra_frequency(struct qpnp_hap *hap)
|
static void update_lra_frequency(struct qpnp_hap *hap)
|
||||||
{
|
{
|
||||||
u8 lra_auto_res_lo = 0, lra_auto_res_hi = 0;
|
u8 lra_auto_res_lo = 0, lra_auto_res_hi = 0;
|
||||||
|
u32 play_rate_code;
|
||||||
|
|
||||||
qpnp_hap_read_reg(hap, &lra_auto_res_lo,
|
qpnp_hap_read_reg(hap, &lra_auto_res_lo,
|
||||||
QPNP_HAP_LRA_AUTO_RES_LO(hap->base));
|
QPNP_HAP_LRA_AUTO_RES_LO(hap->base));
|
||||||
qpnp_hap_read_reg(hap, &lra_auto_res_hi,
|
qpnp_hap_read_reg(hap, &lra_auto_res_hi,
|
||||||
QPNP_HAP_LRA_AUTO_RES_HI(hap->base));
|
QPNP_HAP_LRA_AUTO_RES_HI(hap->base));
|
||||||
|
|
||||||
if (lra_auto_res_lo && lra_auto_res_hi) {
|
play_rate_code =
|
||||||
qpnp_hap_write_reg(hap, &lra_auto_res_lo,
|
(lra_auto_res_hi & 0xF0) << 4 | (lra_auto_res_lo & 0xFF);
|
||||||
QPNP_HAP_RATE_CFG1_REG(hap->base));
|
|
||||||
|
|
||||||
lra_auto_res_hi = lra_auto_res_hi >> 4;
|
dev_dbg(&hap->pdev->dev,
|
||||||
qpnp_hap_write_reg(hap, &lra_auto_res_hi,
|
"lra_auto_res_lo = 0x%x lra_auto_res_hi = 0x%x play_rate_code = 0x%x\n",
|
||||||
QPNP_HAP_RATE_CFG2_REG(hap->base));
|
lra_auto_res_lo, lra_auto_res_hi, play_rate_code);
|
||||||
}
|
|
||||||
|
/*
|
||||||
|
* If the drive period code read from AUTO RES_LO and AUTO_RES_HI
|
||||||
|
* registers is more than the max limit percent variation read from
|
||||||
|
* DT or less than the min limit percent variation read from DT, then
|
||||||
|
* RATE_CFG registers are not uptdated.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if ((play_rate_code <= hap->drive_period_code_min_limit) ||
|
||||||
|
(play_rate_code >= hap->drive_period_code_max_limit))
|
||||||
|
return;
|
||||||
|
|
||||||
|
qpnp_hap_write_reg(hap, &lra_auto_res_lo,
|
||||||
|
QPNP_HAP_RATE_CFG1_REG(hap->base));
|
||||||
|
|
||||||
|
lra_auto_res_hi = lra_auto_res_hi >> 4;
|
||||||
|
qpnp_hap_write_reg(hap, &lra_auto_res_hi,
|
||||||
|
QPNP_HAP_RATE_CFG2_REG(hap->base));
|
||||||
}
|
}
|
||||||
|
|
||||||
static enum hrtimer_restart detect_auto_res_error(struct hrtimer *timer)
|
static enum hrtimer_restart detect_auto_res_error(struct hrtimer *timer)
|
||||||
|
@ -1412,36 +1494,38 @@ static void correct_auto_res_error(struct work_struct *auto_res_err_work)
|
||||||
struct qpnp_hap, auto_res_err_work);
|
struct qpnp_hap, auto_res_err_work);
|
||||||
|
|
||||||
u8 lra_code_lo, lra_code_hi, disable_hap = 0x00;
|
u8 lra_code_lo, lra_code_hi, disable_hap = 0x00;
|
||||||
static int lra_freq_index;
|
static u8 lra_freq_index;
|
||||||
ktime_t currtime, remaining_time;
|
ktime_t currtime = ktime_set(0, 0), remaining_time = ktime_set(0, 0);
|
||||||
int temp, rem = 0, index = lra_freq_index % LRA_POS_FREQ_COUNT;
|
|
||||||
|
|
||||||
if (hrtimer_active(&hap->hap_timer)) {
|
if (hrtimer_active(&hap->hap_timer))
|
||||||
remaining_time = hrtimer_get_remaining(&hap->hap_timer);
|
remaining_time = hrtimer_get_remaining(&hap->hap_timer);
|
||||||
rem = (int)ktime_to_us(remaining_time);
|
|
||||||
}
|
|
||||||
|
|
||||||
qpnp_hap_play(hap, 0);
|
qpnp_hap_play(hap, 0);
|
||||||
qpnp_hap_write_reg(hap, &disable_hap,
|
qpnp_hap_write_reg(hap, &disable_hap,
|
||||||
QPNP_HAP_EN_CTL_REG(hap->base));
|
QPNP_HAP_EN_CTL_REG(hap->base));
|
||||||
|
|
||||||
lra_code_lo = lra_play_rate_code[index] & QPNP_HAP_RATE_CFG1_MASK;
|
if (hap->perform_lra_auto_resonance_search) {
|
||||||
qpnp_hap_write_reg(hap, &lra_code_lo,
|
lra_code_lo =
|
||||||
QPNP_HAP_RATE_CFG1_REG(hap->base));
|
adjusted_lra_play_rate_code[lra_freq_index]
|
||||||
|
& QPNP_HAP_RATE_CFG1_MASK;
|
||||||
|
|
||||||
qpnp_hap_read_reg(hap, &lra_code_hi,
|
qpnp_hap_write_reg(hap, &lra_code_lo,
|
||||||
QPNP_HAP_RATE_CFG2_REG(hap->base));
|
QPNP_HAP_RATE_CFG1_REG(hap->base));
|
||||||
|
|
||||||
lra_code_hi &= QPNP_HAP_RATE_CFG2_MASK;
|
lra_code_hi = adjusted_lra_play_rate_code[lra_freq_index]
|
||||||
temp = lra_play_rate_code[index] >> QPNP_HAP_RATE_CFG2_SHFT;
|
>> QPNP_HAP_RATE_CFG2_SHFT;
|
||||||
lra_code_hi |= temp;
|
|
||||||
|
|
||||||
qpnp_hap_write_reg(hap, &lra_code_hi,
|
qpnp_hap_write_reg(hap, &lra_code_hi,
|
||||||
QPNP_HAP_RATE_CFG2_REG(hap->base));
|
QPNP_HAP_RATE_CFG2_REG(hap->base));
|
||||||
|
|
||||||
lra_freq_index++;
|
lra_freq_index = (lra_freq_index+1) %
|
||||||
|
ADJUSTED_LRA_PLAY_RATE_CODE_ARRSIZE;
|
||||||
|
}
|
||||||
|
|
||||||
if (rem > 0) {
|
dev_dbg(&hap->pdev->dev, "Remaining time is %lld\n",
|
||||||
|
ktime_to_us(remaining_time));
|
||||||
|
|
||||||
|
if ((ktime_to_us(remaining_time)) > 0) {
|
||||||
currtime = ktime_get();
|
currtime = ktime_get();
|
||||||
hap->state = 1;
|
hap->state = 1;
|
||||||
hrtimer_forward(&hap->hap_timer, currtime, remaining_time);
|
hrtimer_forward(&hap->hap_timer, currtime, remaining_time);
|
||||||
|
@ -1455,6 +1539,7 @@ static int qpnp_hap_set(struct qpnp_hap *hap, int on)
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
u8 val = 0;
|
u8 val = 0;
|
||||||
unsigned long timeout_ns = POLL_TIME_AUTO_RES_ERR_NS;
|
unsigned long timeout_ns = POLL_TIME_AUTO_RES_ERR_NS;
|
||||||
|
u32 back_emf_delay_us = hap->time_required_to_generate_back_emf_us;
|
||||||
|
|
||||||
if (hap->play_mode == QPNP_HAP_PWM) {
|
if (hap->play_mode == QPNP_HAP_PWM) {
|
||||||
if (on)
|
if (on)
|
||||||
|
@ -1464,8 +1549,21 @@ static int qpnp_hap_set(struct qpnp_hap *hap, int on)
|
||||||
} else if (hap->play_mode == QPNP_HAP_BUFFER ||
|
} else if (hap->play_mode == QPNP_HAP_BUFFER ||
|
||||||
hap->play_mode == QPNP_HAP_DIRECT) {
|
hap->play_mode == QPNP_HAP_DIRECT) {
|
||||||
if (on) {
|
if (on) {
|
||||||
if (hap->correct_lra_drive_freq ||
|
/*
|
||||||
hap->auto_res_mode == QPNP_HAP_AUTO_RES_QWD)
|
* For auto resonance detection to work properly,
|
||||||
|
* sufficient back-emf has to be generated. In general,
|
||||||
|
* back-emf takes some time to build up. When the auto
|
||||||
|
* resonance mode is chosen as QWD, high-z will be
|
||||||
|
* applied for every LRA cycle and hence there won't be
|
||||||
|
* enough back-emf at the start-up. Hence, the motor
|
||||||
|
* needs to vibrate for few LRA cycles after the PLAY
|
||||||
|
* bit is asserted. So disable the auto resonance here
|
||||||
|
* and enable it after the sleep of
|
||||||
|
* 'time_required_to_generate_back_emf_us' is completed.
|
||||||
|
*/
|
||||||
|
if ((hap->act_type == QPNP_HAP_LRA) &&
|
||||||
|
(hap->correct_lra_drive_freq ||
|
||||||
|
hap->auto_res_mode == QPNP_HAP_AUTO_RES_QWD))
|
||||||
qpnp_hap_auto_res_enable(hap, 0);
|
qpnp_hap_auto_res_enable(hap, 0);
|
||||||
|
|
||||||
rc = qpnp_hap_mod_enable(hap, on);
|
rc = qpnp_hap_mod_enable(hap, on);
|
||||||
|
@ -1474,17 +1572,18 @@ static int qpnp_hap_set(struct qpnp_hap *hap, int on)
|
||||||
|
|
||||||
rc = qpnp_hap_play(hap, on);
|
rc = qpnp_hap_play(hap, on);
|
||||||
|
|
||||||
if ((hap->act_type == QPNP_HAP_LRA &&
|
if ((hap->act_type == QPNP_HAP_LRA) &&
|
||||||
hap->correct_lra_drive_freq) ||
|
(hap->correct_lra_drive_freq ||
|
||||||
hap->auto_res_mode == QPNP_HAP_AUTO_RES_QWD) {
|
hap->auto_res_mode == QPNP_HAP_AUTO_RES_QWD)) {
|
||||||
usleep_range(AUTO_RES_ENABLE_TIMEOUT,
|
usleep_range(back_emf_delay_us,
|
||||||
(AUTO_RES_ENABLE_TIMEOUT + 1));
|
(back_emf_delay_us + 1));
|
||||||
|
|
||||||
rc = qpnp_hap_auto_res_enable(hap, 1);
|
rc = qpnp_hap_auto_res_enable(hap, 1);
|
||||||
if (rc < 0)
|
if (rc < 0)
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
if (hap->correct_lra_drive_freq) {
|
if (hap->act_type == QPNP_HAP_LRA &&
|
||||||
|
hap->correct_lra_drive_freq) {
|
||||||
/*
|
/*
|
||||||
* Start timer to poll Auto Resonance error bit
|
* Start timer to poll Auto Resonance error bit
|
||||||
*/
|
*/
|
||||||
|
@ -1500,7 +1599,8 @@ static int qpnp_hap_set(struct qpnp_hap *hap, int on)
|
||||||
if (rc < 0)
|
if (rc < 0)
|
||||||
return rc;
|
return rc;
|
||||||
|
|
||||||
if (hap->correct_lra_drive_freq) {
|
if (hap->act_type == QPNP_HAP_LRA &&
|
||||||
|
hap->correct_lra_drive_freq) {
|
||||||
rc = qpnp_hap_read_reg(hap, &val,
|
rc = qpnp_hap_read_reg(hap, &val,
|
||||||
QPNP_HAP_STATUS(hap->base));
|
QPNP_HAP_STATUS(hap->base));
|
||||||
if (!(val & AUTO_RES_ERR_BIT))
|
if (!(val & AUTO_RES_ERR_BIT))
|
||||||
|
@ -1511,7 +1611,6 @@ static int qpnp_hap_set(struct qpnp_hap *hap, int on)
|
||||||
if (hap->act_type == QPNP_HAP_LRA &&
|
if (hap->act_type == QPNP_HAP_LRA &&
|
||||||
hap->correct_lra_drive_freq) {
|
hap->correct_lra_drive_freq) {
|
||||||
hrtimer_cancel(&hap->auto_res_err_poll_timer);
|
hrtimer_cancel(&hap->auto_res_err_poll_timer);
|
||||||
calculate_lra_code(hap);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1706,10 +1805,16 @@ static SIMPLE_DEV_PM_OPS(qpnp_haptic_pm_ops, qpnp_haptic_suspend, NULL);
|
||||||
/* Configuration api for haptics registers */
|
/* Configuration api for haptics registers */
|
||||||
static int qpnp_hap_config(struct qpnp_hap *hap)
|
static int qpnp_hap_config(struct qpnp_hap *hap)
|
||||||
{
|
{
|
||||||
u8 reg = 0, unlock_val, error_value;
|
u8 reg = 0, unlock_val;
|
||||||
int rc, i, temp;
|
u32 temp;
|
||||||
|
int rc, i;
|
||||||
uint error_code = 0;
|
uint error_code = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This denotes the percentage error in rc clock multiplied by 10
|
||||||
|
*/
|
||||||
|
u8 rc_clk_err_percent_x10;
|
||||||
|
|
||||||
/* Configure the ACTUATOR TYPE register */
|
/* Configure the ACTUATOR TYPE register */
|
||||||
rc = qpnp_hap_read_reg(hap, ®, QPNP_HAP_ACT_TYPE_REG(hap->base));
|
rc = qpnp_hap_read_reg(hap, ®, QPNP_HAP_ACT_TYPE_REG(hap->base));
|
||||||
if (rc < 0)
|
if (rc < 0)
|
||||||
|
@ -1838,16 +1943,22 @@ static int qpnp_hap_config(struct qpnp_hap *hap)
|
||||||
else if (hap->wave_play_rate_us > QPNP_HAP_WAV_PLAY_RATE_US_MAX)
|
else if (hap->wave_play_rate_us > QPNP_HAP_WAV_PLAY_RATE_US_MAX)
|
||||||
hap->wave_play_rate_us = QPNP_HAP_WAV_PLAY_RATE_US_MAX;
|
hap->wave_play_rate_us = QPNP_HAP_WAV_PLAY_RATE_US_MAX;
|
||||||
|
|
||||||
temp = hap->wave_play_rate_us / QPNP_HAP_RATE_CFG_STEP_US;
|
hap->init_drive_period_code =
|
||||||
|
hap->wave_play_rate_us / QPNP_HAP_RATE_CFG_STEP_US;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The frequency of 19.2Mzhz RC clock is subject to variation.
|
* The frequency of 19.2Mzhz RC clock is subject to variation. Currently
|
||||||
* In PMI8950, TRIM_ERROR_RC19P2_CLK register in MISC module
|
* a few PMI modules have MISC_TRIM_ERROR_RC19P2_CLK register
|
||||||
* holds the frequency error in 19.2Mhz RC clock
|
* present in their MISC block. This register holds the frequency error
|
||||||
|
* in 19.2Mhz RC clock.
|
||||||
*/
|
*/
|
||||||
if ((hap->act_type == QPNP_HAP_LRA) && hap->correct_lra_drive_freq
|
if ((hap->act_type == QPNP_HAP_LRA) && hap->correct_lra_drive_freq
|
||||||
&& hap->misc_trim_error_rc19p2_clk_reg_present) {
|
&& hap->misc_trim_error_rc19p2_clk_reg_present) {
|
||||||
unlock_val = MISC_SEC_UNLOCK;
|
unlock_val = MISC_SEC_UNLOCK;
|
||||||
|
/*
|
||||||
|
* This SID value may change depending on the PMI chip where
|
||||||
|
* the MISC block is present.
|
||||||
|
*/
|
||||||
rc = regmap_write(hap->regmap, MISC_SEC_ACCESS, unlock_val);
|
rc = regmap_write(hap->regmap, MISC_SEC_ACCESS, unlock_val);
|
||||||
if (rc)
|
if (rc)
|
||||||
dev_err(&hap->pdev->dev,
|
dev_err(&hap->pdev->dev,
|
||||||
|
@ -1855,36 +1966,69 @@ static int qpnp_hap_config(struct qpnp_hap *hap)
|
||||||
|
|
||||||
regmap_read(hap->regmap, MISC_TRIM_ERROR_RC19P2_CLK,
|
regmap_read(hap->regmap, MISC_TRIM_ERROR_RC19P2_CLK,
|
||||||
&error_code);
|
&error_code);
|
||||||
|
dev_dbg(&hap->pdev->dev, "TRIM register = 0x%x\n", error_code);
|
||||||
|
|
||||||
error_value = (error_code & 0x0F) * 7;
|
/*
|
||||||
|
* Extract the 4 LSBs and multiply by 7 to get
|
||||||
|
* the %error in RC clock multiplied by 10
|
||||||
|
*/
|
||||||
|
rc_clk_err_percent_x10 = (error_code & 0x0F) * 7;
|
||||||
|
|
||||||
if (error_code & 0x80)
|
/*
|
||||||
temp = (temp * (1000 - error_value)) / 1000;
|
* If the TRIM register holds value less than 0x80,
|
||||||
|
* then there is a positive error in the RC clock.
|
||||||
|
* If the TRIM register holds value greater than or equal to
|
||||||
|
* 0x80, then there is a negative error in the RC clock.
|
||||||
|
*
|
||||||
|
* The adjusted play rate code is calculated as follows:
|
||||||
|
* LRA drive period code (RATE_CFG) =
|
||||||
|
* 200KHz * 1 / LRA drive frequency * ( 1 + %error/ 100)
|
||||||
|
*
|
||||||
|
* This can be rewritten as:
|
||||||
|
* LRA drive period code (RATE_CFG) =
|
||||||
|
* 200KHz * 1/LRA drive frequency *( 1 + %error * 10/1000)
|
||||||
|
*
|
||||||
|
* Since 200KHz * 1/LRA drive frequency is already calculated
|
||||||
|
* above we only do rest of the scaling here.
|
||||||
|
*/
|
||||||
|
if (error_code >= 128)
|
||||||
|
LRA_DRIVE_PERIOD_NEG_ERR(hap, rc_clk_err_percent_x10);
|
||||||
else
|
else
|
||||||
temp = (temp * (1000 + error_value)) / 1000;
|
LRA_DRIVE_PERIOD_POS_ERR(hap, rc_clk_err_percent_x10);
|
||||||
}
|
}
|
||||||
|
|
||||||
reg = temp & QPNP_HAP_RATE_CFG1_MASK;
|
dev_dbg(&hap->pdev->dev,
|
||||||
|
"Play rate code 0x%x\n", hap->init_drive_period_code);
|
||||||
|
|
||||||
|
reg = hap->init_drive_period_code & QPNP_HAP_RATE_CFG1_MASK;
|
||||||
rc = qpnp_hap_write_reg(hap, ®,
|
rc = qpnp_hap_write_reg(hap, ®,
|
||||||
QPNP_HAP_RATE_CFG1_REG(hap->base));
|
QPNP_HAP_RATE_CFG1_REG(hap->base));
|
||||||
if (rc)
|
if (rc)
|
||||||
return rc;
|
return rc;
|
||||||
|
|
||||||
rc = qpnp_hap_read_reg(hap, ®,
|
reg = (hap->init_drive_period_code & 0xF00) >> QPNP_HAP_RATE_CFG2_SHFT;
|
||||||
QPNP_HAP_RATE_CFG2_REG(hap->base));
|
|
||||||
if (rc < 0)
|
|
||||||
return rc;
|
|
||||||
reg &= QPNP_HAP_RATE_CFG2_MASK;
|
|
||||||
temp = temp >> QPNP_HAP_RATE_CFG2_SHFT;
|
|
||||||
reg |= temp;
|
|
||||||
rc = qpnp_hap_write_reg(hap, ®,
|
rc = qpnp_hap_write_reg(hap, ®,
|
||||||
QPNP_HAP_RATE_CFG2_REG(hap->base));
|
QPNP_HAP_RATE_CFG2_REG(hap->base));
|
||||||
if (rc)
|
if (rc)
|
||||||
return rc;
|
return rc;
|
||||||
|
|
||||||
if ((hap->act_type == QPNP_HAP_LRA) && hap->correct_lra_drive_freq)
|
if (hap->act_type == QPNP_HAP_LRA &&
|
||||||
|
hap->perform_lra_auto_resonance_search)
|
||||||
calculate_lra_code(hap);
|
calculate_lra_code(hap);
|
||||||
|
|
||||||
|
if (hap->act_type == QPNP_HAP_LRA && hap->correct_lra_drive_freq) {
|
||||||
|
hap->drive_period_code_max_limit =
|
||||||
|
(hap->init_drive_period_code * 100) /
|
||||||
|
(100 - hap->drive_period_code_max_limit_percent_variation);
|
||||||
|
hap->drive_period_code_min_limit =
|
||||||
|
(hap->init_drive_period_code * 100) /
|
||||||
|
(100 + hap->drive_period_code_min_limit_percent_variation);
|
||||||
|
dev_dbg(&hap->pdev->dev, "Drive period code max limit %x\n"
|
||||||
|
"Drive period code min limit %x\n",
|
||||||
|
hap->drive_period_code_max_limit,
|
||||||
|
hap->drive_period_code_min_limit);
|
||||||
|
}
|
||||||
|
|
||||||
/* Configure BRAKE register */
|
/* Configure BRAKE register */
|
||||||
rc = qpnp_hap_read_reg(hap, ®, QPNP_HAP_EN_CTL2_REG(hap->base));
|
rc = qpnp_hap_read_reg(hap, ®, QPNP_HAP_EN_CTL2_REG(hap->base));
|
||||||
if (rc < 0)
|
if (rc < 0)
|
||||||
|
@ -2031,13 +2175,44 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap)
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hap->perform_lra_auto_resonance_search =
|
||||||
|
of_property_read_bool(pdev->dev.of_node,
|
||||||
|
"qcom,perform-lra-auto-resonance-search");
|
||||||
|
|
||||||
hap->correct_lra_drive_freq =
|
hap->correct_lra_drive_freq =
|
||||||
of_property_read_bool(pdev->dev.of_node,
|
of_property_read_bool(pdev->dev.of_node,
|
||||||
"qcom,correct-lra-drive-freq");
|
"qcom,correct-lra-drive-freq");
|
||||||
|
|
||||||
|
hap->drive_period_code_max_limit_percent_variation = 25;
|
||||||
|
rc = of_property_read_u32(pdev->dev.of_node,
|
||||||
|
"qcom,drive-period-code-max-limit-percent-variation", &temp);
|
||||||
|
if (!rc)
|
||||||
|
hap->drive_period_code_max_limit_percent_variation =
|
||||||
|
(u8) temp;
|
||||||
|
|
||||||
|
hap->drive_period_code_min_limit_percent_variation = 25;
|
||||||
|
rc = of_property_read_u32(pdev->dev.of_node,
|
||||||
|
"qcom,drive-period-code-min-limit-percent-variation", &temp);
|
||||||
|
if (!rc)
|
||||||
|
hap->drive_period_code_min_limit_percent_variation =
|
||||||
|
(u8) temp;
|
||||||
|
|
||||||
hap->misc_trim_error_rc19p2_clk_reg_present =
|
hap->misc_trim_error_rc19p2_clk_reg_present =
|
||||||
of_property_read_bool(pdev->dev.of_node,
|
of_property_read_bool(pdev->dev.of_node,
|
||||||
"qcom,misc-trim-error-rc19p2-clk-reg-present");
|
"qcom,misc-trim-error-rc19p2-clk-reg-present");
|
||||||
|
|
||||||
|
if (hap->auto_res_mode == QPNP_HAP_AUTO_RES_QWD) {
|
||||||
|
hap->time_required_to_generate_back_emf_us =
|
||||||
|
QPNP_HAP_TIME_REQ_FOR_BACK_EMF_GEN;
|
||||||
|
rc = of_property_read_u32(pdev->dev.of_node,
|
||||||
|
"qcom,time-required-to-generate-back-emf-us",
|
||||||
|
&temp);
|
||||||
|
if (!rc)
|
||||||
|
hap->time_required_to_generate_back_emf_us =
|
||||||
|
temp;
|
||||||
|
} else {
|
||||||
|
hap->time_required_to_generate_back_emf_us = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = of_property_read_string(pdev->dev.of_node,
|
rc = of_property_read_string(pdev->dev.of_node,
|
||||||
|
|
Loading…
Add table
Reference in a new issue