blob: a2c8e76a46f796186bcbd8d6d5b7e024197d06e0 [file] [log] [blame]
/* Copyright (c) 2018, 2020, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/debugfs.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/fs.h>
#include <linux/hrtimer.h>
#include <linux/init.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <linux/pwm.h>
#include <linux/qpnp/qpnp-misc.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/uaccess.h>
enum actutor_type {
ACT_LRA,
ACT_ERM,
};
enum lra_res_sig_shape {
RES_SIG_SINE,
RES_SIG_SQUARE,
};
enum lra_auto_res_mode {
AUTO_RES_MODE_ZXD,
AUTO_RES_MODE_QWD,
};
enum wf_src {
INT_WF_VMAX,
INT_WF_BUFFER,
EXT_WF_AUDIO,
EXT_WF_PWM,
};
enum haptics_custom_effect_param {
CUSTOM_DATA_EFFECT_IDX,
CUSTOM_DATA_TIMEOUT_SEC_IDX,
CUSTOM_DATA_TIMEOUT_MSEC_IDX,
CUSTOM_DATA_LEN,
};
/* common definitions */
#define HAP_BRAKE_PATTERN_MAX 4
#define HAP_WAVEFORM_BUFFER_MAX 8
#define HAP_VMAX_MV_DEFAULT 1800
#define HAP_VMAX_MV_MAX 3596
#define HAP_ILIM_MA_DEFAULT 400
#define HAP_ILIM_MA_MAX 800
#define HAP_PLAY_RATE_US_DEFAULT 5715
#define HAP_PLAY_RATE_US_MAX 20475
#define HAP_PLAY_RATE_US_LSB 5
#define VMAX_MIN_PLAY_TIME_US 20000
#define HAP_SC_DET_MAX_COUNT 5
#define HAP_SC_DET_TIME_US 1000000
#define FF_EFFECT_COUNT_MAX 32
#define HAP_DISABLE_DELAY_USEC 1000
/* haptics module register definitions */
#define REG_HAP_STATUS1 0x0A
#define HAP_SC_DET_BIT BIT(3)
#define HAP_BUSY_BIT BIT(1)
#define REG_HAP_EN_CTL1 0x46
#define HAP_EN_BIT BIT(7)
#define REG_HAP_EN_CTL2 0x48
#define HAP_AUTO_STANDBY_EN_BIT BIT(1)
#define HAP_BRAKE_EN_BIT BIT(0)
#define REG_HAP_EN_CTL3 0x4A
#define HAP_HBRIDGE_EN_BIT BIT(7)
#define HAP_PWM_SIGNAL_EN_BIT BIT(6)
#define HAP_ILIM_EN_BIT BIT(5)
#define HAP_ILIM_CC_EN_BIT BIT(4)
#define HAP_AUTO_RES_RBIAS_EN_BIT BIT(3)
#define HAP_DAC_EN_BIT BIT(2)
#define HAP_ZX_HYST_EN_BIT BIT(1)
#define HAP_PWM_CTL_EN_BIT BIT(0)
#define REG_HAP_AUTO_RES_CTRL 0x4B
#define HAP_AUTO_RES_EN_BIT BIT(7)
#define HAP_SEL_AUTO_RES_PERIOD BIT(6)
#define HAP_AUTO_RES_CNT_ERR_DELTA_MASK GENMASK(5, 4)
#define HAP_AUTO_RES_CNT_ERR_DELTA_SHIFT 4
#define HAP_AUTO_RES_ERR_RECOVERY_BIT BIT(3)
#define HAP_AUTO_RES_EN_DLY_MASK GENMASK(2, 0)
#define AUTO_RES_CNT_ERR_DELTA(x) (x << HAP_AUTO_RES_CNT_ERR_DELTA_SHIFT)
#define AUTO_RES_EN_DLY(x) x
#define REG_HAP_CFG1 0x4C
#define REG_HAP_CFG2 0x4D
#define HAP_LRA_RES_TYPE_BIT BIT(0)
#define REG_HAP_SEL 0x4E
#define HAP_WF_SOURCE_MASK GENMASK(5, 4)
#define HAP_WF_SOURCE_SHIFT 4
#define HAP_WF_TRIGGER_BIT BIT(0)
#define HAP_WF_SOURCE_VMAX (0 << HAP_WF_SOURCE_SHIFT)
#define HAP_WF_SOURCE_BUFFER (1 << HAP_WF_SOURCE_SHIFT)
#define HAP_WF_SOURCE_AUDIO (2 << HAP_WF_SOURCE_SHIFT)
#define HAP_WF_SOURCE_PWM (3 << HAP_WF_SOURCE_SHIFT)
#define REG_HAP_AUTO_RES_CFG 0x4F
#define HAP_AUTO_RES_MODE_BIT BIT(7)
#define HAP_AUTO_RES_MODE_SHIFT 7
#define HAP_AUTO_RES_CAL_DURATON_MASK GENMASK(6, 5)
#define HAP_CAL_EOP_EN_BIT BIT(3)
#define HAP_CAL_PERIOD_MASK GENMASK(2, 0)
#define HAP_CAL_OPT3_EVERY_8_PERIOD 2
#define REG_HAP_SLEW_CFG 0x50
#define REG_HAP_VMAX_CFG 0x51
#define HAP_VMAX_SIGN_BIT BIT(7)
#define HAP_VMAX_OVD_BIT BIT(6)
#define HAP_VMAX_MV_MASK GENMASK(5, 1)
#define HAP_VMAX_MV_SHIFT 1
#define HAP_VMAX_MV_LSB 116
#define REG_HAP_ILIM_CFG 0x52
#define REG_HAP_SC_DEB_CFG 0x53
#define REG_HAP_RATE_CFG1 0x54
#define REG_HAP_RATE_CFG2 0x55
#define REG_HAP_INTERNAL_PWM 0x56
#define REG_HAP_EXTERNAL_PWM 0x57
#define REG_HAP_PWM 0x58
#define REG_HAP_SC_CLR 0x59
#define HAP_SC_CLR_BIT BIT(0)
#define REG_HAP_ZX_CFG 0x5A
#define HAP_ZX_DET_DEB_MASK GENMASK(2, 0)
#define ZX_DET_DEB_10US 0
#define ZX_DET_DEB_20US 1
#define ZX_DET_DEB_40US 2
#define ZX_DET_DEB_80US 3
#define REG_HAP_BRAKE 0x5C
#define HAP_BRAKE_PATTERN_MASK 0x3
#define HAP_BRAKE_PATTERN_SHIFT 2
#define REG_HAP_WF_REPEAT 0x5E
#define HAP_WF_REPEAT_MASK GENMASK(6, 4)
#define HAP_WF_REPEAT_SHIFT 4
#define HAP_WF_S_REPEAT_MASK GENMASK(1, 0)
#define REG_HAP_WF_S1 0x60
#define HAP_WF_SIGN_BIT BIT(7)
#define HAP_WF_OVD_BIT BIT(6)
#define HAP_WF_AMP_BIT GENMASK(5, 1)
#define HAP_WF_AMP_SHIFT 1
#define REG_HAP_PLAY 0x70
#define HAP_PLAY_BIT BIT(7)
#define REG_HAP_SEC_ACCESS 0xD0
#define REG_HAP_PERPH_RESET_CTL3 0xDA
struct qti_hap_effect {
int id;
u8 *pattern;
int pattern_length;
u16 play_rate_us;
u16 vmax_mv;
u8 wf_repeat_n;
u8 wf_s_repeat_n;
u8 brake[HAP_BRAKE_PATTERN_MAX];
int brake_pattern_length;
bool brake_en;
bool lra_auto_res_disable;
};
struct qti_hap_play_info {
struct qti_hap_effect *effect;
u16 vmax_mv;
int length_us;
int playing_pos;
bool playing_pattern;
};
struct qti_hap_config {
enum actutor_type act_type;
enum lra_res_sig_shape lra_shape;
enum lra_auto_res_mode lra_auto_res_mode;
enum wf_src ext_src;
u16 vmax_mv;
u16 ilim_ma;
u16 play_rate_us;
bool lra_allow_variable_play_rate;
bool use_ext_wf_src;
};
struct qti_hap_chip {
struct platform_device *pdev;
struct device *dev;
struct regmap *regmap;
struct input_dev *input_dev;
struct pwm_device *pwm_dev;
struct qti_hap_config config;
struct qti_hap_play_info play;
struct qti_hap_effect *predefined;
struct qti_hap_effect constant;
struct regulator *vdd_supply;
struct hrtimer stop_timer;
struct hrtimer hap_disable_timer;
struct dentry *hap_debugfs;
struct notifier_block twm_nb;
spinlock_t bus_lock;
ktime_t last_sc_time;
int play_irq;
int sc_irq;
int effects_count;
int sc_det_count;
u16 reg_base;
bool perm_disable;
bool play_irq_en;
bool vdd_enabled;
bool twm_state;
bool haptics_ext_pin_twm;
};
struct hap_addr_val {
u16 addr;
u8 value;
};
static struct hap_addr_val twm_ext_cfg[] = {
{REG_HAP_PLAY, 0x00}, /* Stop playing haptics waveform */
{REG_HAP_PERPH_RESET_CTL3, 0x0D}, /* Disable SHUTDOWN1_RB reset */
{REG_HAP_SEL, 0x01}, /* Configure for external-pin mode */
{REG_HAP_EN_CTL1, 0x80}, /* Enable haptics driver */
};
static int wf_repeat[8] = {1, 2, 4, 8, 16, 32, 64, 128};
static int wf_s_repeat[4] = {1, 2, 4, 8};
static int twm_sys_enable;
module_param_named(
haptics_twm, twm_sys_enable, int, 0600
);
static inline bool is_secure(u8 addr)
{
return ((addr & 0xFF) > 0xD0);
}
static int qti_haptics_read(struct qti_hap_chip *chip,
u8 addr, u8 *val, int len)
{
int rc = 0;
unsigned long flags;
spin_lock_irqsave(&chip->bus_lock, flags);
rc = regmap_bulk_read(chip->regmap, chip->reg_base + addr, val, len);
if (rc < 0)
dev_err(chip->dev, "Reading addr 0x%x failed, rc=%d\n",
addr, rc);
spin_unlock_irqrestore(&chip->bus_lock, flags);
return rc;
}
static int qti_haptics_write(struct qti_hap_chip *chip,
u8 addr, u8 *val, int len)
{
int rc = 0, i;
unsigned long flags;
spin_lock_irqsave(&chip->bus_lock, flags);
if (is_secure(addr)) {
for (i = 0; i < len; i++) {
rc = regmap_write(chip->regmap,
chip->reg_base + REG_HAP_SEC_ACCESS,
0xA5);
if (rc < 0) {
dev_err(chip->dev, "write SEC_ACCESS failed, rc=%d\n",
rc);
goto unlock;
}
rc = regmap_write(chip->regmap,
chip->reg_base + addr + i, val[i]);
if (rc < 0) {
dev_err(chip->dev, "write val 0x%x to addr 0x%x failed, rc=%d\n",
val[i], addr + i, rc);
goto unlock;
}
}
} else {
if (len > 1)
rc = regmap_bulk_write(chip->regmap,
chip->reg_base + addr, val, len);
else
rc = regmap_write(chip->regmap,
chip->reg_base + addr, *val);
if (rc < 0)
dev_err(chip->dev, "write addr 0x%x failed, rc=%d\n",
addr, rc);
}
for (i = 0; i < len; i++)
dev_dbg(chip->dev, "Update addr 0x%x to val 0x%x\n",
addr + i, val[i]);
unlock:
spin_unlock_irqrestore(&chip->bus_lock, flags);
return rc;
}
static int qti_haptics_masked_write(struct qti_hap_chip *chip, u8 addr,
u8 mask, u8 val)
{
int rc;
unsigned long flags;
spin_lock_irqsave(&chip->bus_lock, flags);
if (is_secure(addr)) {
rc = regmap_write(chip->regmap,
chip->reg_base + REG_HAP_SEC_ACCESS,
0xA5);
if (rc < 0) {
dev_err(chip->dev, "write SEC_ACCESS failed, rc=%d\n",
rc);
goto unlock;
}
}
rc = regmap_update_bits(chip->regmap, chip->reg_base + addr, mask, val);
if (rc < 0)
dev_err(chip->dev, "Update addr 0x%x to val 0x%x with mask 0x%x failed, rc=%d\n",
addr, val, mask, rc);
dev_dbg(chip->dev, "Update addr 0x%x to val 0x%x with mask 0x%x\n",
addr, val, mask);
unlock:
spin_unlock_irqrestore(&chip->bus_lock, flags);
return rc;
}
static void construct_constant_waveform_in_pattern(
struct qti_hap_play_info *play)
{
struct qti_hap_chip *chip = container_of(play,
struct qti_hap_chip, play);
struct qti_hap_config *config = &chip->config;
struct qti_hap_effect *effect = play->effect;
int total_samples, samples, left, magnitude, i, j, k;
int delta = INT_MAX, delta_min = INT_MAX;
/* Using play_rate_us in config for constant waveform */
effect->play_rate_us = config->play_rate_us;
total_samples = play->length_us / effect->play_rate_us;
left = play->length_us % effect->play_rate_us;
if (total_samples <= HAP_WAVEFORM_BUFFER_MAX) {
effect->pattern_length = total_samples;
effect->wf_s_repeat_n = 0;
effect->wf_repeat_n = 0;
} else {
/*
* Find a closest setting to achieve the constant waveform
* with the required length by using buffer waveform source:
* play_length_us = pattern_length * wf_s_repeat_n
* * wf_repeat_n * play_rate_us
*/
for (i = 0; i < ARRAY_SIZE(wf_repeat); i++) {
for (j = 0; j < ARRAY_SIZE(wf_s_repeat); j++) {
for (k = 1; k <= HAP_WAVEFORM_BUFFER_MAX; k++) {
samples = k * wf_s_repeat[j] *
wf_repeat[i];
delta = abs(total_samples - samples);
if (delta < delta_min) {
delta_min = delta;
effect->pattern_length = k;
effect->wf_s_repeat_n = j;
effect->wf_repeat_n = i;
}
if (samples > total_samples)
break;
}
}
}
}
if (left > 0 && effect->pattern_length < HAP_WAVEFORM_BUFFER_MAX)
effect->pattern_length++;
play->length_us = effect->pattern_length * effect->play_rate_us;
dev_dbg(chip->dev, "total_samples = %d, pattern_length = %d, wf_s_repeat = %d, wf_repeat = %d\n",
total_samples, effect->pattern_length,
wf_s_repeat[effect->wf_s_repeat_n],
wf_repeat[effect->wf_repeat_n]);
for (i = 0; i < effect->pattern_length; i++) {
magnitude = play->vmax_mv / HAP_VMAX_MV_LSB;
effect->pattern[i] = (u8)magnitude << HAP_WF_AMP_SHIFT;
}
}
static int qti_haptics_config_wf_buffer(struct qti_hap_chip *chip)
{
struct qti_hap_play_info *play = &chip->play;
struct qti_hap_effect *effect = play->effect;
u8 addr, pattern[HAP_WAVEFORM_BUFFER_MAX] = {0};
int rc = 0;
size_t len;
if (play->playing_pos == effect->pattern_length) {
dev_dbg(chip->dev, "pattern playing done\n");
return 0;
}
if (effect->pattern_length - play->playing_pos
>= HAP_WAVEFORM_BUFFER_MAX)
len = HAP_WAVEFORM_BUFFER_MAX;
else
len = effect->pattern_length - play->playing_pos;
dev_dbg(chip->dev, "copy %d bytes start from %d\n",
(int)len, play->playing_pos);
memcpy(pattern, &effect->pattern[play->playing_pos], len);
play->playing_pos += len;
addr = REG_HAP_WF_S1;
rc = qti_haptics_write(chip, REG_HAP_WF_S1, pattern,
HAP_WAVEFORM_BUFFER_MAX);
if (rc < 0)
dev_err(chip->dev, "Program WF_SAMPLE failed, rc=%d\n", rc);
return rc;
}
static int qti_haptics_config_wf_repeat(struct qti_hap_chip *chip)
{
struct qti_hap_effect *effect = chip->play.effect;
u8 addr, mask, val;
int rc = 0;
addr = REG_HAP_WF_REPEAT;
mask = HAP_WF_REPEAT_MASK | HAP_WF_S_REPEAT_MASK;
val = effect->wf_repeat_n << HAP_WF_REPEAT_SHIFT;
val |= effect->wf_s_repeat_n;
rc = qti_haptics_masked_write(chip, addr, mask, val);
if (rc < 0)
dev_err(chip->dev, "Program WF_REPEAT failed, rc=%d\n", rc);
return rc;
}
static int qti_haptics_play(struct qti_hap_chip *chip, bool play)
{
int rc = 0;
u8 val = play ? HAP_PLAY_BIT : 0;
rc = qti_haptics_write(chip,
REG_HAP_PLAY, &val, 1);
if (rc < 0)
dev_err(chip->dev, "%s playing haptics failed, rc=%d\n",
play ? "start" : "stop", rc);
return rc;
}
static int qti_haptics_module_en(struct qti_hap_chip *chip, bool en)
{
int rc = 0;
u8 val = en ? HAP_EN_BIT : 0;
rc = qti_haptics_write(chip,
REG_HAP_EN_CTL1, &val, 1);
if (rc < 0)
dev_err(chip->dev, "%s haptics failed, rc=%d\n",
en ? "enable" : "disable", rc);
return rc;
}
static int qti_haptics_config_vmax(struct qti_hap_chip *chip, int vmax_mv)
{
u8 addr, mask, val;
int rc;
addr = REG_HAP_VMAX_CFG;
mask = HAP_VMAX_MV_MASK;
val = (vmax_mv / HAP_VMAX_MV_LSB) << HAP_VMAX_MV_SHIFT;
rc = qti_haptics_masked_write(chip, addr, mask, val);
if (rc < 0)
dev_err(chip->dev, "write VMAX_CFG failed, rc=%d\n",
rc);
return rc;
}
static int qti_haptics_config_wf_src(struct qti_hap_chip *chip,
enum wf_src src)
{
u8 addr, mask, val = 0;
int rc;
addr = REG_HAP_SEL;
mask = HAP_WF_SOURCE_MASK | HAP_WF_TRIGGER_BIT;
val = src << HAP_WF_SOURCE_SHIFT;
if (src == EXT_WF_AUDIO || src == EXT_WF_PWM)
val |= HAP_WF_TRIGGER_BIT;
rc = qti_haptics_masked_write(chip, addr, mask, val);
if (rc < 0)
dev_err(chip->dev, "set HAP_SEL failed, rc=%d\n", rc);
return rc;
}
static int qti_haptics_config_play_rate_us(struct qti_hap_chip *chip,
int play_rate_us)
{
u8 addr, val[2];
int tmp, rc;
addr = REG_HAP_RATE_CFG1;
tmp = play_rate_us / HAP_PLAY_RATE_US_LSB;
val[0] = tmp & 0xff;
val[1] = (tmp >> 8) & 0xf;
rc = qti_haptics_write(chip, addr, val, 2);
if (rc < 0)
dev_err(chip->dev, "write play_rate failed, rc=%d\n", rc);
return rc;
}
static int qti_haptics_brake_enable(struct qti_hap_chip *chip, bool en)
{
u8 addr, mask, val;
int rc;
addr = REG_HAP_EN_CTL2;
mask = HAP_BRAKE_EN_BIT;
val = en ? HAP_BRAKE_EN_BIT : 0;
rc = qti_haptics_masked_write(chip, addr, mask, val);
if (rc < 0)
dev_err(chip->dev, "write BRAKE_EN failed, rc=%d\n", rc);
return rc;
}
static int qti_haptics_config_brake(struct qti_hap_chip *chip, u8 *brake)
{
u8 addr, val;
int i, rc;
addr = REG_HAP_BRAKE;
for (val = 0, i = 0; i < HAP_BRAKE_PATTERN_MAX; i++)
val |= (brake[i] & HAP_BRAKE_PATTERN_MASK) <<
i * HAP_BRAKE_PATTERN_SHIFT;
rc = qti_haptics_write(chip, addr, &val, 1);
if (rc < 0) {
dev_err(chip->dev, "write brake pattern failed, rc=%d\n", rc);
return rc;
}
/*
* Set BRAKE_EN regardless of the brake pattern, this helps to stop
* playing immediately once the valid values in WF_Sx are played.
*/
rc = qti_haptics_brake_enable(chip, true);
return rc;
}
static int qti_haptics_lra_auto_res_enable(struct qti_hap_chip *chip, bool en)
{
int rc;
u8 addr, val, mask;
addr = REG_HAP_AUTO_RES_CTRL;
mask = HAP_AUTO_RES_EN_BIT;
val = en ? HAP_AUTO_RES_EN_BIT : 0;
rc = qti_haptics_masked_write(chip, addr, mask, val);
if (rc < 0)
dev_err(chip->dev, "set AUTO_RES_CTRL failed, rc=%d\n", rc);
return rc;
}
#define HAP_CLEAR_PLAYING_RATE_US 15
static int qti_haptics_clear_settings(struct qti_hap_chip *chip)
{
int rc;
u8 pattern[HAP_WAVEFORM_BUFFER_MAX] = {1, 0, 0, 0, 0, 0, 0, 0};
rc = qti_haptics_brake_enable(chip, false);
if (rc < 0)
return rc;
rc = qti_haptics_lra_auto_res_enable(chip, false);
if (rc < 0)
return rc;
rc = qti_haptics_config_play_rate_us(chip, HAP_CLEAR_PLAYING_RATE_US);
if (rc < 0)
return rc;
rc = qti_haptics_write(chip, REG_HAP_WF_S1, pattern,
HAP_WAVEFORM_BUFFER_MAX);
if (rc < 0)
return rc;
rc = qti_haptics_play(chip, true);
if (rc < 0)
return rc;
rc = qti_haptics_play(chip, false);
if (rc < 0)
return rc;
return 0;
}
static int qti_haptics_load_constant_waveform(struct qti_hap_chip *chip)
{
struct qti_hap_play_info *play = &chip->play;
struct qti_hap_config *config = &chip->config;
int rc = 0;
rc = qti_haptics_config_play_rate_us(chip, config->play_rate_us);
if (rc < 0)
return rc;
/*
* Using VMAX waveform source if playing length is >= 20ms,
* otherwise using buffer waveform source and calculate the
* pattern length and repeating times to achieve accurate
* playing time accuracy.
*/
if (play->length_us >= VMAX_MIN_PLAY_TIME_US) {
rc = qti_haptics_config_vmax(chip, play->vmax_mv);
if (rc < 0)
return rc;
/* Enable Auto-Resonance when VMAX wf-src is selected */
if (config->act_type == ACT_LRA) {
rc = qti_haptics_lra_auto_res_enable(chip, true);
if (rc < 0)
return rc;
}
/* Set WF_SOURCE to VMAX */
rc = qti_haptics_config_wf_src(chip, INT_WF_VMAX);
if (rc < 0)
return rc;
play->playing_pattern = false;
play->effect = NULL;
} else {
rc = qti_haptics_config_vmax(chip, config->vmax_mv);
if (rc < 0)
return rc;
play->effect = &chip->constant;
play->playing_pos = 0;
/* Format and config waveform in patterns */
construct_constant_waveform_in_pattern(play);
rc = qti_haptics_config_wf_buffer(chip);
if (rc < 0)
return rc;
rc = qti_haptics_config_wf_repeat(chip);
if (rc < 0)
return rc;
/* Set WF_SOURCE to buffer */
rc = qti_haptics_config_wf_src(chip, INT_WF_BUFFER);
if (rc < 0)
return rc;
play->playing_pattern = true;
}
return 0;
}
static int qti_haptics_load_predefined_effect(struct qti_hap_chip *chip,
int effect_idx)
{
struct qti_hap_play_info *play = &chip->play;
struct qti_hap_config *config = &chip->config;
int rc = 0;
if (effect_idx >= chip->effects_count)
return -EINVAL;
play->effect = &chip->predefined[effect_idx];
play->playing_pos = 0;
rc = qti_haptics_config_vmax(chip, play->vmax_mv);
if (rc < 0)
return rc;
rc = qti_haptics_config_play_rate_us(chip, play->effect->play_rate_us);
if (rc < 0)
return rc;
if (config->act_type == ACT_LRA) {
rc = qti_haptics_lra_auto_res_enable(chip,
!play->effect->lra_auto_res_disable);
if (rc < 0)
return rc;
}
/* Set brake pattern in the effect */
rc = qti_haptics_config_brake(chip, play->effect->brake);
if (rc < 0)
return rc;
rc = qti_haptics_config_wf_buffer(chip);
if (rc < 0)
return rc;
rc = qti_haptics_config_wf_repeat(chip);
if (rc < 0)
return rc;
/* Set WF_SOURCE to buffer */
rc = qti_haptics_config_wf_src(chip, INT_WF_BUFFER);
if (rc < 0)
return rc;
play->playing_pattern = true;
return 0;
}
static irqreturn_t qti_haptics_play_irq_handler(int irq, void *data)
{
struct qti_hap_chip *chip = (struct qti_hap_chip *)data;
struct qti_hap_play_info *play = &chip->play;
struct qti_hap_effect *effect = play->effect;
int rc;
dev_dbg(chip->dev, "play_irq triggered\n");
if (play->playing_pos == effect->pattern_length) {
dev_dbg(chip->dev, "waveform playing done\n");
if (chip->play_irq_en) {
disable_irq_nosync(chip->play_irq);
chip->play_irq_en = false;
}
goto handled;
}
/* Config to play remaining patterns */
rc = qti_haptics_config_wf_repeat(chip);
if (rc < 0)
goto handled;
rc = qti_haptics_config_wf_buffer(chip);
if (rc < 0)
goto handled;
handled:
return IRQ_HANDLED;
}
static irqreturn_t qti_haptics_sc_irq_handler(int irq, void *data)
{
struct qti_hap_chip *chip = (struct qti_hap_chip *)data;
u8 addr, val;
ktime_t temp;
s64 sc_delta_time_us;
int rc;
dev_dbg(chip->dev, "sc_irq triggered\n");
addr = REG_HAP_STATUS1;
rc = qti_haptics_read(chip, addr, &val, 1);
if (rc < 0) {
dev_err(chip->dev, "read HAP_STATUS1 failed, rc=%d\n", rc);
goto handled;
}
if (!(val & HAP_SC_DET_BIT))
goto handled;
temp = ktime_get();
sc_delta_time_us = ktime_us_delta(temp, chip->last_sc_time);
chip->last_sc_time = temp;
if (sc_delta_time_us > HAP_SC_DET_TIME_US)
chip->sc_det_count = 0;
else
chip->sc_det_count++;
addr = REG_HAP_SC_CLR;
val = HAP_SC_CLR_BIT;
rc = qti_haptics_write(chip, addr, &val, 1);
if (rc < 0) {
dev_err(chip->dev, "write SC_CLR failed, rc=%d\n", rc);
goto handled;
}
if (chip->sc_det_count > HAP_SC_DET_MAX_COUNT) {
rc = qti_haptics_module_en(chip, false);
if (rc < 0)
goto handled;
dev_crit(chip->dev, "Short circuit persists, disable haptics\n");
chip->perm_disable = true;
}
handled:
return IRQ_HANDLED;
}
static inline void get_play_length(struct qti_hap_play_info *play,
int *length_us)
{
struct qti_hap_effect *effect = play->effect;
int tmp;
tmp = effect->pattern_length * effect->play_rate_us;
tmp *= wf_s_repeat[effect->wf_s_repeat_n];
tmp *= wf_repeat[effect->wf_repeat_n];
if (effect->brake_en)
tmp += effect->play_rate_us * effect->brake_pattern_length;
*length_us = tmp;
}
static int qti_haptics_upload_effect(struct input_dev *dev,
struct ff_effect *effect, struct ff_effect *old)
{
struct qti_hap_chip *chip = input_get_drvdata(dev);
struct qti_hap_config *config = &chip->config;
struct qti_hap_play_info *play = &chip->play;
int rc = 0, tmp, i;
s16 level, data[CUSTOM_DATA_LEN];
ktime_t rem;
s64 time_us;
if (hrtimer_active(&chip->hap_disable_timer)) {
rem = hrtimer_get_remaining(&chip->hap_disable_timer);
time_us = ktime_to_us(rem);
dev_dbg(chip->dev, "waiting for playing clear sequence: %lld us\n",
time_us);
usleep_range(time_us, time_us + 100);
}
switch (effect->type) {
case FF_CONSTANT:
play->length_us = effect->replay.length * USEC_PER_MSEC;
level = effect->u.constant.level;
tmp = level * config->vmax_mv;
play->vmax_mv = tmp / 0x7fff;
dev_dbg(chip->dev, "upload constant effect, length = %dus, vmax_mv=%d\n",
play->length_us, play->vmax_mv);
rc = qti_haptics_load_constant_waveform(chip);
if (rc < 0) {
dev_err(chip->dev, "Play constant waveform failed, rc=%d\n",
rc);
return rc;
}
break;
case FF_PERIODIC:
if (chip->effects_count == 0)
return -EINVAL;
if (effect->u.periodic.waveform != FF_CUSTOM) {
dev_err(chip->dev, "Only accept custom waveforms\n");
return -EINVAL;
}
if (copy_from_user(data, effect->u.periodic.custom_data,
sizeof(s16) * CUSTOM_DATA_LEN))
return -EFAULT;
for (i = 0; i < chip->effects_count; i++)
if (chip->predefined[i].id ==
data[CUSTOM_DATA_EFFECT_IDX])
break;
if (i == chip->effects_count) {
dev_err(chip->dev, "predefined effect %d is NOT supported\n",
data[0]);
return -EINVAL;
}
level = effect->u.periodic.magnitude;
tmp = level * chip->predefined[i].vmax_mv;
play->vmax_mv = tmp / 0x7fff;
dev_dbg(chip->dev, "upload effect %d, vmax_mv=%d\n",
chip->predefined[i].id, play->vmax_mv);
rc = qti_haptics_load_predefined_effect(chip, i);
if (rc < 0) {
dev_err(chip->dev, "Play predefined effect %d failed, rc=%d\n",
chip->predefined[i].id, rc);
return rc;
}
get_play_length(play, &play->length_us);
data[CUSTOM_DATA_TIMEOUT_SEC_IDX] =
play->length_us / USEC_PER_SEC;
data[CUSTOM_DATA_TIMEOUT_MSEC_IDX] =
(play->length_us % USEC_PER_SEC) / USEC_PER_MSEC;
/*
* Copy the custom data contains the play length back to
* userspace so that the userspace client can wait and
* send stop playing command after it's done.
*/
if (copy_to_user(effect->u.periodic.custom_data, data,
sizeof(s16) * CUSTOM_DATA_LEN))
return -EFAULT;
break;
default:
dev_err(chip->dev, "Unsupported effect type: %d\n",
effect->type);
return -EINVAL;
}
if (chip->vdd_supply && !chip->vdd_enabled) {
rc = regulator_enable(chip->vdd_supply);
if (rc < 0) {
dev_err(chip->dev, "Enable VDD supply failed, rc=%d\n",
rc);
return rc;
}
chip->vdd_enabled = true;
}
return 0;
}
static int qti_haptics_playback(struct input_dev *dev, int effect_id, int val)
{
struct qti_hap_chip *chip = input_get_drvdata(dev);
struct qti_hap_play_info *play = &chip->play;
s64 secs;
unsigned long nsecs;
int rc = 0;
dev_dbg(chip->dev, "playback, val = %d\n", val);
if (!!val) {
rc = qti_haptics_module_en(chip, true);
if (rc < 0)
return rc;
rc = qti_haptics_play(chip, true);
if (rc < 0)
return rc;
if (play->playing_pattern) {
if (!chip->play_irq_en) {
enable_irq(chip->play_irq);
chip->play_irq_en = true;
}
/* Toggle PLAY when playing pattern */
rc = qti_haptics_play(chip, false);
if (rc < 0)
return rc;
} else {
if (chip->play_irq_en) {
disable_irq_nosync(chip->play_irq);
chip->play_irq_en = false;
}
secs = play->length_us / USEC_PER_SEC;
nsecs = (play->length_us % USEC_PER_SEC) *
NSEC_PER_USEC;
hrtimer_start(&chip->stop_timer, ktime_set(secs, nsecs),
HRTIMER_MODE_REL);
}
} else {
play->length_us = 0;
rc = qti_haptics_play(chip, false);
if (rc < 0)
return rc;
if (chip->play_irq_en) {
disable_irq_nosync(chip->play_irq);
chip->play_irq_en = false;
}
}
return rc;
}
static int qti_haptics_erase(struct input_dev *dev, int effect_id)
{
struct qti_hap_chip *chip = input_get_drvdata(dev);
int delay_us, rc = 0;
if (chip->vdd_supply && chip->vdd_enabled) {
rc = regulator_disable(chip->vdd_supply);
if (rc < 0) {
dev_err(chip->dev, "Disable VDD supply failed, rc=%d\n",
rc);
return rc;
}
chip->vdd_enabled = false;
}
rc = qti_haptics_clear_settings(chip);
if (rc < 0) {
dev_err(chip->dev, "clear setting failed, rc=%d\n", rc);
return rc;
}
if (chip->play.effect)
delay_us = chip->play.effect->play_rate_us;
else
delay_us = chip->config.play_rate_us;
delay_us += HAP_DISABLE_DELAY_USEC;
hrtimer_start(&chip->hap_disable_timer,
ktime_set(0, delay_us * NSEC_PER_USEC),
HRTIMER_MODE_REL);
return rc;
}
static void qti_haptics_set_gain(struct input_dev *dev, u16 gain)
{
struct qti_hap_chip *chip = input_get_drvdata(dev);
struct qti_hap_config *config = &chip->config;
struct qti_hap_play_info *play = &chip->play;
if (gain == 0)
return;
if (gain > 0x7fff)
gain = 0x7fff;
play->vmax_mv = ((u32)(gain * config->vmax_mv)) / 0x7fff;
qti_haptics_config_vmax(chip, play->vmax_mv);
}
static int qti_haptics_twm_config(struct qti_hap_chip *chip)
{
int rc, i;
for (i = 0; i < ARRAY_SIZE(twm_ext_cfg); i++) {
rc = qti_haptics_write(chip, twm_ext_cfg[i].addr,
&twm_ext_cfg[i].value, 1);
if (rc < 0) {
dev_err(chip->dev, "Haptics TWM config failed, rc=%d\n",
rc);
return rc;
}
}
pr_debug("Enabled haptics for TWM mode\n");
return 0;
}
static int qti_haptics_hw_init(struct qti_hap_chip *chip)
{
struct qti_hap_config *config = &chip->config;
u8 addr, val, mask;
int rc = 0;
/* Config actuator type */
addr = REG_HAP_CFG1;
val = config->act_type;
rc = qti_haptics_write(chip, addr, &val, 1);
if (rc < 0) {
dev_err(chip->dev, "write actuator type failed, rc=%d\n", rc);
return rc;
}
/* Config ilim_ma */
addr = REG_HAP_ILIM_CFG;
val = config->ilim_ma == 400 ? 0 : 1;
rc = qti_haptics_write(chip, addr, &val, 1);
if (rc < 0) {
dev_err(chip->dev, "write ilim_ma failed, rc=%d\n", rc);
return rc;
}
/* Set HAP_EN_CTL3 */
addr = REG_HAP_EN_CTL3;
val = HAP_HBRIDGE_EN_BIT | HAP_PWM_SIGNAL_EN_BIT | HAP_ILIM_EN_BIT |
HAP_ILIM_CC_EN_BIT | HAP_AUTO_RES_RBIAS_EN_BIT |
HAP_DAC_EN_BIT | HAP_PWM_CTL_EN_BIT;
rc = qti_haptics_write(chip, addr, &val, 1);
if (rc < 0) {
dev_err(chip->dev, "set EN_CTL3 failed, rc=%d\n", rc);
return rc;
}
/* Set ZX_CFG */
addr = REG_HAP_ZX_CFG;
mask = HAP_ZX_DET_DEB_MASK;
val = ZX_DET_DEB_80US;
rc = qti_haptics_masked_write(chip, addr, mask, val);
if (rc < 0) {
dev_err(chip->dev, "write ZX_CFG failed, rc=%d\n", rc);
return rc;
}
/*
* Config play rate: this is the resonance period for LRA,
* or the play duration of each waveform sample for ERM.
*/
rc = qti_haptics_config_play_rate_us(chip, config->play_rate_us);
if (rc < 0)
return rc;
/* Set external waveform source if it's used */
if (config->use_ext_wf_src) {
rc = qti_haptics_config_wf_src(chip, config->ext_src);
if (rc < 0)
return rc;
}
/*
* Skip configurations below for ERM actuator
* as they're only for LRA actuators
*/
if (config->act_type == ACT_ERM)
return 0;
addr = REG_HAP_CFG2;
val = config->lra_shape;
rc = qti_haptics_write(chip, addr, &val, 1);
if (rc < 0) {
dev_err(chip->dev, "write lra_sig_shape failed, rc=%d\n", rc);
return rc;
}
addr = REG_HAP_AUTO_RES_CFG;
mask = HAP_AUTO_RES_MODE_BIT | HAP_CAL_EOP_EN_BIT | HAP_CAL_PERIOD_MASK;
val = config->lra_auto_res_mode << HAP_AUTO_RES_MODE_SHIFT;
val |= HAP_CAL_EOP_EN_BIT | HAP_CAL_OPT3_EVERY_8_PERIOD;
rc = qti_haptics_masked_write(chip, addr, mask, val);
if (rc < 0) {
dev_err(chip->dev, "set AUTO_RES_CFG failed, rc=%d\n", rc);
return rc;
}
addr = REG_HAP_AUTO_RES_CTRL;
val = HAP_AUTO_RES_EN_BIT | HAP_SEL_AUTO_RES_PERIOD |
AUTO_RES_CNT_ERR_DELTA(2) | HAP_AUTO_RES_ERR_RECOVERY_BIT |
AUTO_RES_EN_DLY(4);
rc = qti_haptics_write(chip, addr, &val, 1);
if (rc < 0) {
dev_err(chip->dev, "set AUTO_RES_CTRL failed, rc=%d\n",
rc);
return rc;
}
return 0;
}
static enum hrtimer_restart qti_hap_stop_timer(struct hrtimer *timer)
{
struct qti_hap_chip *chip = container_of(timer, struct qti_hap_chip,
stop_timer);
int rc;
chip->play.length_us = 0;
rc = qti_haptics_play(chip, false);
if (rc < 0)
dev_err(chip->dev, "Stop playing failed, rc=%d\n", rc);
return HRTIMER_NORESTART;
}
static enum hrtimer_restart qti_hap_disable_timer(struct hrtimer *timer)
{
struct qti_hap_chip *chip = container_of(timer, struct qti_hap_chip,
hap_disable_timer);
int rc;
rc = qti_haptics_module_en(chip, false);
if (rc < 0)
dev_err(chip->dev, "Disable haptics module failed, rc=%d\n",
rc);
return HRTIMER_NORESTART;
}
static void verify_brake_setting(struct qti_hap_effect *effect)
{
int i = effect->brake_pattern_length - 1;
u8 val = 0;
for (; i >= 0; i--) {
if (effect->brake[i] != 0)
break;
effect->brake_pattern_length--;
}
for (i = 0; i < effect->brake_pattern_length; i++) {
effect->brake[i] &= HAP_BRAKE_PATTERN_MASK;
val |= effect->brake[i] << (i * HAP_BRAKE_PATTERN_SHIFT);
}
effect->brake_en = (val != 0);
}
static int twm_notifier_cb(struct notifier_block *nb,
unsigned long action, void *data)
{
struct qti_hap_chip *chip = container_of(nb,
struct qti_hap_chip, twm_nb);
if (action != PMIC_TWM_CLEAR &&
action != PMIC_TWM_ENABLE)
pr_debug("Unsupported option %lu\n", action);
else
chip->twm_state = (u8)action;
return NOTIFY_OK;
}
static int qti_haptics_parse_dt(struct qti_hap_chip *chip)
{
struct qti_hap_config *config = &chip->config;
const struct device_node *node = chip->dev->of_node;
struct device_node *child_node;
struct qti_hap_effect *effect;
const char *str;
int rc = 0, tmp, i = 0, j, m;
rc = of_property_read_u32(node, "reg", &tmp);
if (rc < 0) {
dev_err(chip->dev, "Failed to reg base, rc=%d\n", rc);
return rc;
}
chip->reg_base = (u16)tmp;
chip->sc_irq = platform_get_irq_byname(chip->pdev, "hap-sc-irq");
if (chip->sc_irq < 0) {
dev_err(chip->dev, "Failed to get hap-sc-irq\n");
return chip->sc_irq;
}
chip->play_irq = platform_get_irq_byname(chip->pdev, "hap-play-irq");
if (chip->play_irq < 0) {
dev_err(chip->dev, "Failed to get hap-play-irq\n");
return chip->play_irq;
}
config->act_type = ACT_LRA;
rc = of_property_read_string(node, "qcom,actuator-type", &str);
if (!rc) {
if (strcmp(str, "erm") == 0) {
config->act_type = ACT_ERM;
} else if (strcmp(str, "lra") == 0) {
config->act_type = ACT_LRA;
} else {
dev_err(chip->dev, "Invalid actuator type: %s\n",
str);
return -EINVAL;
}
}
config->vmax_mv = HAP_VMAX_MV_DEFAULT;
rc = of_property_read_u32(node, "qcom,vmax-mv", &tmp);
if (!rc)
config->vmax_mv = (tmp > HAP_VMAX_MV_MAX) ?
HAP_VMAX_MV_MAX : tmp;
config->ilim_ma = HAP_ILIM_MA_DEFAULT;
rc = of_property_read_u32(node, "qcom,ilim-ma", &tmp);
if (!rc)
config->ilim_ma = (tmp >= HAP_ILIM_MA_MAX) ?
HAP_ILIM_MA_MAX : HAP_ILIM_MA_DEFAULT;
config->play_rate_us = HAP_PLAY_RATE_US_DEFAULT;
rc = of_property_read_u32(node, "qcom,play-rate-us", &tmp);
if (!rc)
config->play_rate_us = (tmp >= HAP_PLAY_RATE_US_MAX) ?
HAP_PLAY_RATE_US_MAX : tmp;
chip->haptics_ext_pin_twm = of_property_read_bool(node,
"qcom,haptics-ext-pin-twm");
if (of_find_property(node, "qcom,external-waveform-source", NULL)) {
if (!of_property_read_string(node,
"qcom,external-waveform-source", &str)) {
if (strcmp(str, "audio") == 0) {
config->ext_src = EXT_WF_AUDIO;
} else if (strcmp(str, "pwm") == 0) {
config->ext_src = EXT_WF_PWM;
} else {
dev_err(chip->dev, "Invalid external waveform source: %s\n",
str);
return -EINVAL;
}
}
config->use_ext_wf_src = true;
}
if (of_find_property(node, "vdd-supply", NULL)) {
chip->vdd_supply = devm_regulator_get(chip->dev, "vdd");
if (IS_ERR(chip->vdd_supply)) {
rc = PTR_ERR(chip->vdd_supply);
if (rc != -EPROBE_DEFER)
dev_err(chip->dev, "Failed to get vdd regulator");
return rc;
}
}
if (config->act_type == ACT_LRA) {
config->lra_shape = RES_SIG_SINE;
rc = of_property_read_string(node,
"qcom,lra-resonance-sig-shape", &str);
if (!rc) {
if (strcmp(str, "sine") == 0) {
config->lra_shape = RES_SIG_SINE;
} else if (strcmp(str, "square") == 0) {
config->lra_shape = RES_SIG_SQUARE;
} else {
dev_err(chip->dev, "Invalid resonance signal shape: %s\n",
str);
return -EINVAL;
}
}
config->lra_allow_variable_play_rate = of_property_read_bool(
node, "qcom,lra-allow-variable-play-rate");
config->lra_auto_res_mode = AUTO_RES_MODE_ZXD;
rc = of_property_read_string(node,
"qcom,lra-auto-resonance-mode", &str);
if (!rc) {
if (strcmp(str, "zxd") == 0) {
config->lra_auto_res_mode = AUTO_RES_MODE_ZXD;
} else if (strcmp(str, "qwd") == 0) {
config->lra_auto_res_mode = AUTO_RES_MODE_QWD;
} else {
dev_err(chip->dev, "Invalid auto resonance mode: %s\n",
str);
return -EINVAL;
}
}
}
chip->constant.pattern = devm_kcalloc(chip->dev,
HAP_WAVEFORM_BUFFER_MAX,
sizeof(u8), GFP_KERNEL);
if (!chip->constant.pattern)
return -ENOMEM;
tmp = of_get_available_child_count(node);
if (tmp == 0)
return 0;
chip->predefined = devm_kcalloc(chip->dev, tmp,
sizeof(*chip->predefined), GFP_KERNEL);
if (!chip->predefined)
return -ENOMEM;
chip->effects_count = tmp;
for_each_available_child_of_node(node, child_node) {
effect = &chip->predefined[i++];
rc = of_property_read_u32(child_node, "qcom,effect-id",
&effect->id);
if (rc < 0) {
dev_err(chip->dev, "Read qcom,effect-id failed, rc=%d\n",
rc);
return rc;
}
effect->vmax_mv = config->vmax_mv;
rc = of_property_read_u32(child_node, "qcom,wf-vmax-mv", &tmp);
if (rc < 0)
dev_dbg(chip->dev, "Read qcom,wf-vmax-mv failed, rc=%d\n",
rc);
else
effect->vmax_mv = (tmp > HAP_VMAX_MV_MAX) ?
HAP_VMAX_MV_MAX : tmp;
rc = of_property_count_elems_of_size(child_node,
"qcom,wf-pattern", sizeof(u8));
if (rc < 0) {
dev_err(chip->dev, "Count qcom,wf-pattern property failed, rc=%d\n",
rc);
return rc;
} else if (rc == 0) {
dev_dbg(chip->dev, "qcom,wf-pattern has no data\n");
return -EINVAL;
}
effect->pattern_length = rc;
effect->pattern = devm_kcalloc(chip->dev,
effect->pattern_length, sizeof(u8), GFP_KERNEL);
if (!effect->pattern)
return -ENOMEM;
rc = of_property_read_u8_array(child_node, "qcom,wf-pattern",
effect->pattern, effect->pattern_length);
if (rc < 0) {
dev_err(chip->dev, "Read qcom,wf-pattern property failed, rc=%d\n",
rc);
return rc;
}
effect->play_rate_us = config->play_rate_us;
rc = of_property_read_u32(child_node, "qcom,wf-play-rate-us",
&tmp);
if (rc < 0)
dev_dbg(chip->dev, "Read qcom,wf-play-rate-us failed, rc=%d\n",
rc);
else
effect->play_rate_us = tmp;
if (config->act_type == ACT_LRA &&
!config->lra_allow_variable_play_rate &&
config->play_rate_us != effect->play_rate_us) {
dev_warn(chip->dev, "play rate should match with LRA resonance frequency\n");
effect->play_rate_us = config->play_rate_us;
}
rc = of_property_read_u32(child_node, "qcom,wf-repeat-count",
&tmp);
if (rc < 0) {
dev_dbg(chip->dev, "Read qcom,wf-repeat-count failed, rc=%d\n",
rc);
} else {
for (j = 0; j < ARRAY_SIZE(wf_repeat); j++)
if (tmp <= wf_repeat[j])
break;
effect->wf_repeat_n = j;
}
rc = of_property_read_u32(child_node, "qcom,wf-s-repeat-count",
&tmp);
if (rc < 0) {
dev_dbg(chip->dev, "Read qcom,wf-s-repeat-count failed, rc=%d\n",
rc);
} else {
for (j = 0; j < ARRAY_SIZE(wf_s_repeat); j++)
if (tmp <= wf_s_repeat[j])
break;
effect->wf_s_repeat_n = j;
}
effect->lra_auto_res_disable = of_property_read_bool(child_node,
"qcom,lra-auto-resonance-disable");
tmp = of_property_count_elems_of_size(child_node,
"qcom,wf-brake-pattern", sizeof(u8));
if (tmp <= 0)
continue;
if (tmp > HAP_BRAKE_PATTERN_MAX) {
dev_err(chip->dev, "wf-brake-pattern shouldn't be more than %d bytes\n",
HAP_BRAKE_PATTERN_MAX);
return -EINVAL;
}
rc = of_property_read_u8_array(child_node,
"qcom,wf-brake-pattern", effect->brake, tmp);
if (rc < 0) {
dev_err(chip->dev, "Failed to get wf-brake-pattern, rc=%d\n",
rc);
return rc;
}
effect->brake_pattern_length = tmp;
verify_brake_setting(effect);
}
for (j = 0; j < i; j++) {
dev_dbg(chip->dev, "effect: %d\n", chip->predefined[j].id);
dev_dbg(chip->dev, " vmax: %d mv\n",
chip->predefined[j].vmax_mv);
dev_dbg(chip->dev, " play_rate: %d us\n",
chip->predefined[j].play_rate_us);
for (m = 0; m < chip->predefined[j].pattern_length; m++)
dev_dbg(chip->dev, " pattern[%d]: 0x%x\n",
m, chip->predefined[j].pattern[m]);
for (m = 0; m < chip->predefined[j].brake_pattern_length; m++)
dev_dbg(chip->dev, " brake_pattern[%d]: 0x%x\n",
m, chip->predefined[j].brake[m]);
dev_dbg(chip->dev, " brake_en: %d\n",
chip->predefined[j].brake_en);
dev_dbg(chip->dev, " wf_repeat_n: %d\n",
chip->predefined[j].wf_repeat_n);
dev_dbg(chip->dev, " wf_s_repeat_n: %d\n",
chip->predefined[j].wf_s_repeat_n);
dev_dbg(chip->dev, " lra_auto_res_disable: %d\n",
chip->predefined[j].lra_auto_res_disable);
}
return 0;
}
#ifdef CONFIG_DEBUG_FS
static int play_rate_dbgfs_read(void *data, u64 *val)
{
struct qti_hap_effect *effect = (struct qti_hap_effect *)data;
*val = effect->play_rate_us;
return 0;
}
static int play_rate_dbgfs_write(void *data, u64 val)
{
struct qti_hap_effect *effect = (struct qti_hap_effect *)data;
if (val > HAP_PLAY_RATE_US_MAX)
val = HAP_PLAY_RATE_US_MAX;
effect->play_rate_us = val;
return 0;
}
static int vmax_dbgfs_read(void *data, u64 *val)
{
struct qti_hap_effect *effect = (struct qti_hap_effect *)data;
*val = effect->vmax_mv;
return 0;
}
static int vmax_dbgfs_write(void *data, u64 val)
{
struct qti_hap_effect *effect = (struct qti_hap_effect *)data;
if (val > HAP_VMAX_MV_MAX)
val = HAP_VMAX_MV_MAX;
effect->vmax_mv = val;
return 0;
}
static int wf_repeat_n_dbgfs_read(void *data, u64 *val)
{
struct qti_hap_effect *effect = (struct qti_hap_effect *)data;
*val = wf_repeat[effect->wf_repeat_n];
return 0;
}
static int wf_repeat_n_dbgfs_write(void *data, u64 val)
{
struct qti_hap_effect *effect = (struct qti_hap_effect *)data;
int i;
for (i = 0; i < ARRAY_SIZE(wf_repeat); i++)
if (val == wf_repeat[i])
break;
if (i == ARRAY_SIZE(wf_repeat))
pr_err("wf_repeat value %llu is invalid\n", val);
else
effect->wf_repeat_n = i;
return 0;
}
static int wf_s_repeat_n_dbgfs_read(void *data, u64 *val)
{
struct qti_hap_effect *effect = (struct qti_hap_effect *)data;
*val = wf_s_repeat[effect->wf_s_repeat_n];
return 0;
}
static int wf_s_repeat_n_dbgfs_write(void *data, u64 val)
{
struct qti_hap_effect *effect = (struct qti_hap_effect *)data;
int i;
for (i = 0; i < ARRAY_SIZE(wf_s_repeat); i++)
if (val == wf_s_repeat[i])
break;
if (i == ARRAY_SIZE(wf_s_repeat))
pr_err("wf_s_repeat value %llu is invalid\n", val);
else
effect->wf_s_repeat_n = i;
return 0;
}
static int auto_res_dbgfs_read(void *data, u64 *val)
{
struct qti_hap_effect *effect = (struct qti_hap_effect *)data;
*val = !effect->lra_auto_res_disable;
return 0;
}
static int auto_res_dbgfs_write(void *data, u64 val)
{
struct qti_hap_effect *effect = (struct qti_hap_effect *)data;
effect->lra_auto_res_disable = !val;
return 0;
}
DEFINE_SIMPLE_ATTRIBUTE(play_rate_debugfs_ops, play_rate_dbgfs_read,
play_rate_dbgfs_write, "%llu\n");
DEFINE_SIMPLE_ATTRIBUTE(vmax_debugfs_ops, vmax_dbgfs_read,
vmax_dbgfs_write, "%llu\n");
DEFINE_SIMPLE_ATTRIBUTE(wf_repeat_n_debugfs_ops, wf_repeat_n_dbgfs_read,
wf_repeat_n_dbgfs_write, "%llu\n");
DEFINE_SIMPLE_ATTRIBUTE(wf_s_repeat_n_debugfs_ops, wf_s_repeat_n_dbgfs_read,
wf_s_repeat_n_dbgfs_write, "%llu\n");
DEFINE_SIMPLE_ATTRIBUTE(auto_res_debugfs_ops, auto_res_dbgfs_read,
auto_res_dbgfs_write, "%llu\n");
#define CHAR_PER_PATTERN 8
static ssize_t brake_pattern_dbgfs_read(struct file *filep,
char __user *buf, size_t count, loff_t *ppos)
{
struct qti_hap_effect *effect =
(struct qti_hap_effect *)filep->private_data;
char *kbuf, *tmp;
int rc, length, i, len;
kbuf = kcalloc(CHAR_PER_PATTERN, HAP_BRAKE_PATTERN_MAX, GFP_KERNEL);
if (!kbuf)
return -ENOMEM;
tmp = kbuf;
for (length = 0, i = 0; i < HAP_BRAKE_PATTERN_MAX; i++) {
len = snprintf(tmp, CHAR_PER_PATTERN, "0x%x ",
effect->brake[i]);
tmp += len;
length += len;
}
kbuf[length++] = '\n';
kbuf[length++] = '\0';
rc = simple_read_from_buffer(buf, count, ppos, kbuf, length);
kfree(kbuf);
return rc;
}
static ssize_t brake_pattern_dbgfs_write(struct file *filep,
const char __user *buf, size_t count, loff_t *ppos)
{
struct qti_hap_effect *effect =
(struct qti_hap_effect *)filep->private_data;
char *kbuf, *token;
int rc = 0, i = 0, j;
u32 val;
kbuf = kmalloc(count + 1, GFP_KERNEL);
if (!kbuf)
return -ENOMEM;
rc = copy_from_user(kbuf, buf, count);
if (rc > 0) {
rc = -EFAULT;
goto err;
}
kbuf[count] = '\0';
*ppos += count;
while ((token = strsep(&kbuf, " ")) != NULL) {
rc = kstrtouint(token, 0, &val);
if (rc < 0) {
rc = -EINVAL;
goto err;
}
effect->brake[i++] = val & HAP_BRAKE_PATTERN_MASK;
if (i >= HAP_BRAKE_PATTERN_MAX)
break;
}
for (j = i; j < HAP_BRAKE_PATTERN_MAX; j++)
effect->brake[j] = 0;
effect->brake_pattern_length = i;
verify_brake_setting(effect);
rc = count;
err:
kfree(kbuf);
return rc;
}
static const struct file_operations brake_pattern_dbgfs_ops = {
.read = brake_pattern_dbgfs_read,
.write = brake_pattern_dbgfs_write,
.owner = THIS_MODULE,
.open = simple_open,
};
static ssize_t pattern_dbgfs_read(struct file *filep,
char __user *buf, size_t count, loff_t *ppos)
{
struct qti_hap_effect *effect =
(struct qti_hap_effect *)filep->private_data;
char *kbuf, *tmp;
int rc, length, i, len;
kbuf = kcalloc(CHAR_PER_PATTERN, effect->pattern_length, GFP_KERNEL);
if (!kbuf)
return -ENOMEM;
tmp = kbuf;
for (length = 0, i = 0; i < effect->pattern_length; i++) {
len = snprintf(tmp, CHAR_PER_PATTERN, "0x%x ",
effect->pattern[i]);
tmp += len;
length += len;
}
kbuf[length++] = '\n';
kbuf[length++] = '\0';
rc = simple_read_from_buffer(buf, count, ppos, kbuf, length);
kfree(kbuf);
return rc;
}
static ssize_t pattern_dbgfs_write(struct file *filep,
const char __user *buf, size_t count, loff_t *ppos)
{
struct qti_hap_effect *effect =
(struct qti_hap_effect *)filep->private_data;
char *kbuf, *token;
int rc = 0, i = 0, j;
u32 val;
kbuf = kmalloc(count + 1, GFP_KERNEL);
if (!kbuf)
return -ENOMEM;
rc = copy_from_user(kbuf, buf, count);
if (rc > 0) {
rc = -EFAULT;
goto err;
}
kbuf[count] = '\0';
*ppos += count;
while ((token = strsep(&kbuf, " ")) != NULL) {
rc = kstrtouint(token, 0, &val);
if (rc < 0) {
rc = -EINVAL;
goto err;
}
effect->pattern[i++] = val & 0xff;
if (i >= effect->pattern_length)
break;
}
for (j = i; j < effect->pattern_length; j++)
effect->pattern[j] = 0;
rc = count;
err:
kfree(kbuf);
return rc;
}
static const struct file_operations pattern_dbgfs_ops = {
.read = pattern_dbgfs_read,
.write = pattern_dbgfs_write,
.owner = THIS_MODULE,
.open = simple_open,
};
static int create_effect_debug_files(struct qti_hap_effect *effect,
struct dentry *dir)
{
struct dentry *file;
file = debugfs_create_file("play_rate_us", 0644, dir,
effect, &play_rate_debugfs_ops);
if (!file) {
pr_err("create play-rate debugfs node failed\n");
return -ENOMEM;
}
file = debugfs_create_file("vmax_mv", 0644, dir,
effect, &vmax_debugfs_ops);
if (!file) {
pr_err("create vmax debugfs node failed\n");
return -ENOMEM;
}
file = debugfs_create_file("wf_repeat_n", 0644, dir,
effect, &wf_repeat_n_debugfs_ops);
if (!file) {
pr_err("create wf-repeat debugfs node failed\n");
return -ENOMEM;
}
file = debugfs_create_file("wf_s_repeat_n", 0644, dir,
effect, &wf_s_repeat_n_debugfs_ops);
if (!file) {
pr_err("create wf-s-repeat debugfs node failed\n");
return -ENOMEM;
}
file = debugfs_create_file("lra_auto_res_en", 0644, dir,
effect, &auto_res_debugfs_ops);
if (!file) {
pr_err("create lra-auto-res-en debugfs node failed\n");
return -ENOMEM;
}
file = debugfs_create_file("brake", 0644, dir,
effect, &brake_pattern_dbgfs_ops);
if (!file) {
pr_err("create brake debugfs node failed\n");
return -ENOMEM;
}
file = debugfs_create_file("pattern", 0644, dir,
effect, &pattern_dbgfs_ops);
if (!file) {
pr_err("create pattern debugfs node failed\n");
return -ENOMEM;
}
return 0;
}
static int qti_haptics_add_debugfs(struct qti_hap_chip *chip)
{
struct dentry *hap_dir, *effect_dir;
char str[12] = {0};
int i, rc = 0;
hap_dir = debugfs_create_dir("haptics", NULL);
if (!hap_dir) {
pr_err("create haptics debugfs directory failed\n");
return -ENOMEM;
}
for (i = 0; i < chip->effects_count; i++) {
snprintf(str, ARRAY_SIZE(str), "effect%d", i);
effect_dir = debugfs_create_dir(str, hap_dir);
if (!effect_dir) {
pr_err("create %s debugfs directory failed\n", str);
rc = -ENOMEM;
goto cleanup;
}
rc = create_effect_debug_files(&chip->predefined[i],
effect_dir);
if (rc < 0) {
rc = -ENOMEM;
goto cleanup;
}
}
chip->hap_debugfs = hap_dir;
return 0;
cleanup:
debugfs_remove_recursive(hap_dir);
return rc;
}
#endif
static int qti_haptics_probe(struct platform_device *pdev)
{
struct qti_hap_chip *chip;
struct input_dev *input_dev;
struct ff_device *ff;
int rc = 0, effect_count_max;
chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
if (!chip)
return -ENOMEM;
input_dev = devm_input_allocate_device(&pdev->dev);
if (!input_dev)
return -ENOMEM;
chip->pdev = pdev;
chip->dev = &pdev->dev;
chip->regmap = dev_get_regmap(chip->dev->parent, NULL);
if (!chip->regmap) {
dev_err(chip->dev, "Failed to get regmap handle\n");
return -ENXIO;
}
rc = qti_haptics_parse_dt(chip);
if (rc < 0) {
dev_err(chip->dev, "parse device-tree failed, rc=%d\n", rc);
return rc;
}
spin_lock_init(&chip->bus_lock);
rc = qti_haptics_hw_init(chip);
if (rc < 0) {
dev_err(chip->dev, "parse device-tree failed, rc=%d\n", rc);
return rc;
}
rc = devm_request_threaded_irq(chip->dev, chip->play_irq, NULL,
qti_haptics_play_irq_handler,
IRQF_ONESHOT, "hap_play_irq", chip);
if (rc < 0) {
dev_err(chip->dev, "request play-irq failed, rc=%d\n", rc);
return rc;
}
disable_irq(chip->play_irq);
chip->play_irq_en = false;
rc = devm_request_threaded_irq(chip->dev, chip->sc_irq, NULL,
qti_haptics_sc_irq_handler,
IRQF_ONESHOT, "hap_sc_irq", chip);
if (rc < 0) {
dev_err(chip->dev, "request sc-irq failed, rc=%d\n", rc);
return rc;
}
chip->twm_nb.notifier_call = twm_notifier_cb;
rc = qpnp_misc_twm_notifier_register(&chip->twm_nb);
if (rc < 0)
pr_err("Failed to register twm_notifier_cb rc=%d\n", rc);
hrtimer_init(&chip->stop_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
chip->stop_timer.function = qti_hap_stop_timer;
hrtimer_init(&chip->hap_disable_timer, CLOCK_MONOTONIC,
HRTIMER_MODE_REL);
chip->hap_disable_timer.function = qti_hap_disable_timer;
input_dev->name = "qti-haptics";
input_set_drvdata(input_dev, chip);
chip->input_dev = input_dev;
input_set_capability(input_dev, EV_FF, FF_CONSTANT);
input_set_capability(input_dev, EV_FF, FF_GAIN);
if (chip->effects_count != 0) {
input_set_capability(input_dev, EV_FF, FF_PERIODIC);
input_set_capability(input_dev, EV_FF, FF_CUSTOM);
}
if (chip->effects_count + 1 > FF_EFFECT_COUNT_MAX)
effect_count_max = chip->effects_count + 1;
else
effect_count_max = FF_EFFECT_COUNT_MAX;
rc = input_ff_create(input_dev, effect_count_max);
if (rc < 0) {
dev_err(chip->dev, "create FF input device failed, rc=%d\n",
rc);
return rc;
}
ff = input_dev->ff;
ff->upload = qti_haptics_upload_effect;
ff->playback = qti_haptics_playback;
ff->erase = qti_haptics_erase;
ff->set_gain = qti_haptics_set_gain;
rc = input_register_device(input_dev);
if (rc < 0) {
dev_err(chip->dev, "register input device failed, rc=%d\n",
rc);
goto destroy_ff;
}
dev_set_drvdata(chip->dev, chip);
#ifdef CONFIG_DEBUG_FS
rc = qti_haptics_add_debugfs(chip);
if (rc < 0)
dev_dbg(chip->dev, "create debugfs failed, rc=%d\n", rc);
#endif
return 0;
destroy_ff:
input_ff_destroy(chip->input_dev);
qpnp_misc_twm_notifier_unregister(&chip->twm_nb);
return rc;
}
static int qti_haptics_remove(struct platform_device *pdev)
{
struct qti_hap_chip *chip = dev_get_drvdata(&pdev->dev);
#ifdef CONFIG_DEBUG_FS
debugfs_remove_recursive(chip->hap_debugfs);
#endif
input_ff_destroy(chip->input_dev);
qpnp_misc_twm_notifier_unregister(&chip->twm_nb);
dev_set_drvdata(chip->dev, NULL);
return 0;
}
static void qti_haptics_shutdown(struct platform_device *pdev)
{
struct qti_hap_chip *chip = dev_get_drvdata(&pdev->dev);
int rc;
bool enable_haptics_twm;
dev_dbg(chip->dev, "Shutdown!\n");
qti_haptics_module_en(chip, false);
if (chip->vdd_supply && chip->vdd_enabled) {
rc = regulator_disable(chip->vdd_supply);
if (rc < 0) {
dev_err(chip->dev, "Disable VDD supply failed, rc=%d\n",
rc);
return;
}
chip->vdd_enabled = false;
}
enable_haptics_twm = chip->haptics_ext_pin_twm && twm_sys_enable;
if (chip->twm_state == PMIC_TWM_ENABLE && enable_haptics_twm) {
rc = qti_haptics_twm_config(chip);
if (rc < 0)
pr_err("Haptics TWM config failed rc=%d\n", rc);
}
}
static const struct of_device_id haptics_match_table[] = {
{ .compatible = "qcom,haptics" },
{ .compatible = "qcom,pm660-haptics" },
{ .compatible = "qcom,pm8150b-haptics" },
{},
};
static struct platform_driver qti_haptics_driver = {
.driver = {
.name = "qcom,haptics",
.owner = THIS_MODULE,
.of_match_table = haptics_match_table,
},
.probe = qti_haptics_probe,
.remove = qti_haptics_remove,
.shutdown = qti_haptics_shutdown,
};
module_platform_driver(qti_haptics_driver);
MODULE_DESCRIPTION("QTI haptics driver");
MODULE_LICENSE("GPL v2");