Merge "input: qcom-hv-haptics: add LRA impedance detection support"

This commit is contained in:
qctecmdr 2020-09-02 19:51:40 -07:00 • committed by Gerrit - the friendly Code Review server
commit af9e463028

View file

@ -22,8 +22,13 @@
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/uaccess.h>
#include <linux/qpnp/qpnp-pbs.h>
/* status register definitions in HAPTICS_CFG module */
#define HAP_CFG_REVISION2_REG 0x01
#define HAP_CFG_V1 0x1
#define HAP_CFG_V2 0x2
#define HAP_CFG_STATUS_DATA_MSB_REG 0x09
/* STATUS_DATA_MSB definitions while MOD_STATUS_SEL is 0 */
#define AUTO_RES_CAL_DONE_BIT BIT(5)
@ -417,6 +422,8 @@ struct haptics_hw_config {
u32 vmax_mv;
u32 t_lra_us;
u32 cl_t_lra_us;
u32 lra_min_mohms;
u32 lra_max_mohms;
u32 preload_effect;
u32 fifo_empty_thresh;
u16 rc_clk_cal_count;
@ -444,12 +451,14 @@ struct haptics_chip {
struct mutex irq_lock;
struct nvmem_cell *cl_brake_nvmem;
struct nvmem_device *hap_cfg_nvmem;
struct device_node *pbs_node;
struct class hap_class;
int fifo_empty_irq;
u32 effects_count;
u32 cfg_addr_base;
u32 ptn_addr_base;
u32 hbst_addr_base;
u8 cfg_revision;
u8 ptn_revision;
u16 hbst_revision;
bool fifo_empty_irq_en;
@ -457,6 +466,11 @@ struct haptics_chip {
bool clamp_at_5v;
};
struct haptics_reg_info {
u8 addr;
u8 val;
};
static int haptics_read(struct haptics_chip *chip,
u16 base, u8 offset, u8 *val, u32 length)
{
@ -1088,11 +1102,12 @@ static bool is_boost_vreg_enabled_in_open_loop(struct haptics_chip *chip)
if (!rc && !(val & FOLLOW_HW_EN_BIT)) {
rc = haptics_read(chip, chip->hbst_addr_base,
HAP_BOOST_VREG_EN_REG, &val, 1);
if (!rc && (val & VREG_EN_BIT))
if (!rc && (val & VREG_EN_BIT)) {
dev_dbg(chip->dev, "HBoost is enabled in open loop condition\n");
return true;
}
}
dev_dbg(chip->dev, "HBoost is not enabled in open loop condition\n");
return false;
}
@ -1826,8 +1841,6 @@ static int haptics_load_periodic_effect(struct haptics_chip *chip,
}
mutex_lock(&chip->play.lock);
play->vmax_mv = (magnitude * chip->effects[i].vmax_mv) / 0x7fff;
dev_dbg(chip->dev, "upload effect %d, vmax_mv=%d\n",
chip->effects[i].id, play->vmax_mv);
@ -1837,6 +1850,7 @@ static int haptics_load_periodic_effect(struct haptics_chip *chip,
goto unlock;
}
play->vmax_mv = (magnitude * chip->effects[i].vmax_mv) / 0x7fff;
rc = haptics_load_predefined_effect(chip, &chip->effects[i]);
if (rc < 0) {
dev_err(chip->dev, "Play predefined effect%d failed, rc=%d\n",
@ -2111,12 +2125,6 @@ static int haptics_hw_init(struct haptics_chip *chip)
if (rc < 0)
return rc;
rc = haptics_read(chip, chip->hbst_addr_base,
HAP_BOOST_REVISION1, val, 2);
if (rc < 0)
return rc;
chip->hbst_revision = (val[1] << 8) | val[0];
rc = haptics_read(chip, chip->hbst_addr_base,
HAP_BOOST_CLAMP_5V_REG_OFFSET(chip), val, 1);
if (rc < 0)
@ -3278,16 +3286,28 @@ static int haptics_parse_lra_dt(struct haptics_chip *chip)
static int haptics_get_revision(struct haptics_chip *chip)
{
int rc;
u8 val;
u8 val[2];
rc = haptics_read(chip, chip->ptn_addr_base,
HAP_PTN_REVISION2_REG, &val, 1);
rc = haptics_read(chip, chip->cfg_addr_base,
HAP_CFG_REVISION2_REG, val, 1);
if (rc < 0)
return rc;
chip->ptn_revision = val;
dev_dbg(chip->dev, "haptics ptn module revision: %#x\n",
chip->ptn_revision);
chip->cfg_revision = val[0];
rc = haptics_read(chip, chip->ptn_addr_base,
HAP_PTN_REVISION2_REG, val, 1);
if (rc < 0)
return rc;
chip->ptn_revision = val[0];
rc = haptics_read(chip, chip->hbst_addr_base,
HAP_BOOST_REVISION1, val, 2);
if (rc < 0)
return rc;
chip->hbst_revision = (val[1] << 8) | val[0];
dev_dbg(chip->dev, "haptics revision: HAP_CFG %#x, HAP_PTN %#x, HAP_HBST %#x\n",
chip->cfg_revision, chip->ptn_revision, chip->hbst_revision);
return 0;
}
@ -3318,43 +3338,55 @@ static int haptics_parse_dt(struct haptics_chip *chip)
if (IS_ERR(chip->hap_cfg_nvmem)) {
rc = PTR_ERR(chip->hap_cfg_nvmem);
if (rc != -EPROBE_DEFER)
dev_err(chip->dev, "Failed to get nvmem device, rc=%d\n",
dev_err(chip->dev, "Failed to get hap_cfg nvmem device, rc=%d\n",
rc);
return rc;
}
}
if (of_find_property(node, "qcom,pbs-client", NULL)) {
chip->pbs_node = of_parse_phandle(node, "qcom,pbs-client", 0);
if (!chip->pbs_node) {
dev_err(chip->dev, "Failed to get PBS client\n");
return -ENODEV;
}
}
addr = of_get_address(node, 0, NULL, NULL);
if (!addr) {
dev_err(chip->dev, "Read HAPTICS_CFG address failed\n");
return -EINVAL;
rc = -EINVAL;
goto free_pbs;
}
chip->cfg_addr_base = be32_to_cpu(*addr);
addr = of_get_address(node, 1, NULL, NULL);
if (!addr) {
dev_err(chip->dev, "Read HAPTICS_PATTERN address failed\n");
return -EINVAL;
rc = -EINVAL;
goto free_pbs;
}
chip->ptn_addr_base = be32_to_cpu(*addr);
addr = of_get_address(node, 2, NULL, NULL);
if (!addr) {
dev_err(chip->dev, "Read HAPTICS_HBOOST address failed\n");
return -EINVAL;
rc = -EINVAL;
goto free_pbs;
}
chip->hbst_addr_base = be32_to_cpu(*addr);
rc = haptics_get_revision(chip);
if (rc < 0) {
dev_err(chip->dev, "Get revision failed, rc=%d\n", rc);
return rc;
goto free_pbs;
}
chip->fifo_empty_irq = platform_get_irq_byname(pdev, "fifo-empty");
if (!chip->fifo_empty_irq) {
dev_err(chip->dev, "Get fifo-empty IRQ failed\n");
return -EINVAL;
rc = -EINVAL;
goto free_pbs;
}
config->vmax_mv = DEFAULT_VMAX_MV;
@ -3362,7 +3394,8 @@ static int haptics_parse_dt(struct haptics_chip *chip)
if (config->vmax_mv >= MAX_VMAX_MV) {
dev_err(chip->dev, "qcom,vmax-mv (%d) exceed the max value: %d\n",
config->vmax_mv, MAX_VMAX_MV);
return -EINVAL;
rc = -EINVAL;
goto free_pbs;
}
config->fifo_empty_thresh = FIFO_EMPTY_THRESHOLD(chip);
@ -3371,7 +3404,8 @@ static int haptics_parse_dt(struct haptics_chip *chip)
if (config->fifo_empty_thresh >= MAX_FIFO_SAMPLES(chip)) {
dev_err(chip->dev, "FIFO empty threshold (%d) should be less than %d\n",
config->fifo_empty_thresh, MAX_FIFO_SAMPLES(chip));
return -EINVAL;
rc = -EINVAL;
goto free_pbs;
}
config->brake.mode = AUTO_BRAKE;
@ -3379,7 +3413,8 @@ static int haptics_parse_dt(struct haptics_chip *chip)
if (config->brake.mode > AUTO_BRAKE) {
dev_err(chip->dev, "Can't support brake mode: %d\n",
config->brake.mode);
return -EINVAL;
rc = -EINVAL;
goto free_pbs;
}
config->brake.disabled =
@ -3388,7 +3423,8 @@ static int haptics_parse_dt(struct haptics_chip *chip)
if (tmp > BRAKE_SAMPLE_COUNT) {
dev_err(chip->dev, "more than %d brake samples\n",
BRAKE_SAMPLE_COUNT);
return -EINVAL;
rc = -EINVAL;
goto free_pbs;
}
if (tmp > 0) {
@ -3397,7 +3433,7 @@ static int haptics_parse_dt(struct haptics_chip *chip)
if (rc < 0) {
dev_err(chip->dev, "Read brake-pattern failed, rc=%d\n",
rc);
return rc;
goto free_pbs;
}
verify_brake_samples(&config->brake);
} else {
@ -3415,7 +3451,7 @@ static int haptics_parse_dt(struct haptics_chip *chip)
if (rc < 0) {
dev_err(chip->dev, "Parse device-tree for LRA failed, rc=%d\n",
rc);
return rc;
goto free_pbs;
}
}
@ -3424,10 +3460,17 @@ static int haptics_parse_dt(struct haptics_chip *chip)
if (rc < 0) {
dev_err(chip->dev, "Parse device-tree for effects failed, rc=%d\n",
rc);
return rc;
goto free_pbs;
}
return 0;
free_pbs:
if (chip->pbs_node) {
of_node_put(chip->pbs_node);
chip->pbs_node = NULL;
}
return rc;
}
static int swr_slave_reg_enable(struct regulator_dev *rdev)
@ -3521,12 +3564,210 @@ static int haptics_init_swr_slave_regulator(struct haptics_chip *chip)
return rc;
}
#define PBS_SW_TRG_LRA_ISC_CFG_BIT BIT(7)
static int haptics_pbs_trigger_isc_config(struct haptics_chip *chip)
{
int rc;
if (chip->pbs_node == NULL) {
dev_err(chip->dev, "PBS device is not defined\n");
return -ENODEV;
}
rc = qpnp_pbs_trigger_event(chip->pbs_node, PBS_SW_TRG_LRA_ISC_CFG_BIT);
if (rc < 0)
dev_err(chip->dev, "Trigger PBS to config ISC failed, rc=%d\n",
rc);
return rc;
}
#define MAX_SWEEP_STEPS 5
#define MAX_IMPEDANCE_MOHM 40000
#define MIN_DUTY_MILLI_PCT 0
#define MAX_DUTY_MILLI_PCT 100000
#define LRA_CONFIG_REGS 3
static int haptics_detect_lra_impedance(struct haptics_chip *chip)
{
int rc, i;
struct haptics_reg_info lra_config[LRA_CONFIG_REGS] = {
{ HAP_CFG_DRV_WF_SEL_REG, 0x10 },
{ HAP_CFG_VMAX_REG, 0xC8 },
{ HAP_CFG_VMAX_HDRM_REG, 0x00 },
};
struct haptics_reg_info backup[LRA_CONFIG_REGS];
u8 val;
u32 duty_milli_pct, low_milli_pct, high_milli_pct;
u32 amplitude, lra_min_mohms, lra_max_mohms;
if (chip->cfg_revision == HAP_CFG_V1) {
dev_dbg(chip->dev, "HAP_CFG V1.0 doesn't support impedance detection\n");
return 0;
}
/* Backup default register values */
memcpy(backup, lra_config, sizeof(backup));
for (i = 0; i < LRA_CONFIG_REGS; i++) {
rc = haptics_read(chip, chip->cfg_addr_base,
backup[i].addr, &backup[i].val, 1);
if (rc < 0)
return rc;
}
/* Trigger PBS to config 250mA ISC setting */
rc = haptics_pbs_trigger_isc_config(chip);
if (rc < 0)
return rc;
/* Set square drive waveform, 10V Vmax, no HDRM */
for (i = 0; i < LRA_CONFIG_REGS; i++) {
rc = haptics_write(chip, chip->cfg_addr_base,
lra_config[i].addr, &lra_config[i].val, 1);
if (rc < 0)
goto restore;
}
low_milli_pct = MIN_DUTY_MILLI_PCT;
high_milli_pct = MAX_DUTY_MILLI_PCT;
/* Sweep duty cycle using binary approach */
for (i = 0; i < MAX_SWEEP_STEPS; i++) {
/* Set direct play amplitude */
duty_milli_pct = (low_milli_pct + high_milli_pct) / 2;
amplitude = (duty_milli_pct * DIRECT_PLAY_MAX_AMPLITUDE)
/ 100000;
rc = haptics_set_direct_play(chip, (u8)amplitude);
if (rc < 0)
goto restore;
dev_dbg(chip->dev, "sweeping milli_pct %u, amplitude %#x\n",
duty_milli_pct, amplitude);
/* Enable play */
chip->play.pattern_src = DIRECT_PLAY;
rc = haptics_enable_play(chip, true);
if (rc < 0)
goto restore;
/* Play a cycle then read SC fault status */
usleep_range(chip->config.t_lra_us,
chip->config.t_lra_us + 1000);
rc = haptics_read(chip, chip->cfg_addr_base,
HAP_CFG_FAULT_STATUS_REG, &val, 1);
if (rc < 0)
goto restore;
if (val & SC_FLAG_BIT)
high_milli_pct = duty_milli_pct;
else
low_milli_pct = duty_milli_pct;
/* Disable play */
rc = haptics_enable_play(chip, false);
if (rc < 0)
goto restore;
/* Sleep 4ms */
usleep_range(4000, 5000);
}
lra_min_mohms = low_milli_pct * MAX_IMPEDANCE_MOHM / 100000;
lra_max_mohms = high_milli_pct * MAX_IMPEDANCE_MOHM / 100000;
if (lra_min_mohms == 0)
dev_warn(chip->dev, "Short circuit detected!\n");
else if (lra_max_mohms == MAX_IMPEDANCE_MOHM)
dev_warn(chip->dev, "Open circuit detected!\n");
else
dev_dbg(chip->dev, "LRA impedance is between %u - %u mohms\n",
lra_min_mohms, lra_max_mohms);
chip->config.lra_min_mohms = lra_min_mohms;
chip->config.lra_max_mohms = lra_max_mohms;
restore:
/* Disable play in case it's not been disabled */
haptics_enable_play(chip, false);
/* Trigger PBS to restore 1500mA ISC setting */
rc = haptics_pbs_trigger_isc_config(chip);
if (rc < 0)
return rc;
/* Restore driver waveform, Vmax, HDRM settings */
for (i = 0; i < LRA_CONFIG_REGS; i++) {
rc = haptics_write(chip, chip->cfg_addr_base,
backup[i].addr, &backup[i].val, 1);
if (rc < 0)
break;
}
return rc;
}
#define LRA_CALIBRATION_VMAX_HDRM_MV 500
static int haptics_start_lra_calibration(struct haptics_chip *chip)
static int haptics_detect_lra_frequency(struct haptics_chip *chip)
{
int rc;
u8 autores_cfg;
rc = haptics_read(chip, chip->cfg_addr_base,
HAP_CFG_AUTORES_CFG_REG, &autores_cfg, 1);
if (rc < 0) {
dev_err(chip->dev, "Read AUTORES_CFG failed, rc=%d\n", rc);
return rc;
}
rc = haptics_masked_write(chip, chip->cfg_addr_base,
HAP_CFG_AUTORES_CFG_REG, AUTORES_EN_BIT |
AUTORES_EN_DLY_MASK | AUTORES_ERR_WINDOW_MASK,
AUTORES_EN_DLY_1_CYCLE << AUTORES_EN_DLY_SHIFT
| AUTORES_ERR_WINDOW_50_PERCENT | AUTORES_EN_BIT);
if (rc < 0)
return rc;
rc = haptics_config_openloop_lra_period(chip, chip->config.t_lra_us);
if (rc < 0)
goto restore;
rc = haptics_set_vmax_mv(chip, chip->config.vmax_mv);
if (rc < 0)
goto restore;
rc = haptics_set_vmax_headroom_mv(chip, LRA_CALIBRATION_VMAX_HDRM_MV);
if (rc < 0)
goto restore;
rc = haptics_set_direct_play(chip, DIRECT_PLAY_MAX_AMPLITUDE);
if (rc < 0)
goto restore;
chip->play.pattern_src = DIRECT_PLAY;
rc = haptics_enable_play(chip, true);
if (rc < 0)
goto restore;
/* wait for ~150ms to get the LRA calibration result */
usleep_range(150000, 155000);
rc = haptics_get_closeloop_lra_period(chip);
if (rc < 0)
goto restore;
rc = haptics_enable_play(chip, false);
if (rc < 0)
goto restore;
haptics_config_openloop_lra_period(chip, chip->config.cl_t_lra_us);
restore:
/* Disable play in case it's not been disabled */
haptics_enable_play(chip, false);
rc = haptics_write(chip, chip->cfg_addr_base,
HAP_CFG_AUTORES_CFG_REG, &autores_cfg, 1);
return rc;
}
static int haptics_start_lra_calibrate(struct haptics_chip *chip)
{
int rc;
mutex_lock(&chip->play.lock);
/*
* Ignore calibration if it's in FIFO playing to avoid
@ -3547,61 +3788,20 @@ static int haptics_start_lra_calibration(struct haptics_chip *chip)
}
chip->play.in_calibration = true;
rc = haptics_read(chip, chip->cfg_addr_base,
HAP_CFG_AUTORES_CFG_REG, &autores_cfg, 1);
rc = haptics_detect_lra_frequency(chip);
if (rc < 0) {
dev_err(chip->dev, "Read AUTORES_CFG failed, rc=%d\n", rc);
dev_err(chip->dev, "Detect LRA frequency failed, rc=%d\n", rc);
goto unlock;
}
rc = haptics_masked_write(chip, chip->cfg_addr_base,
HAP_CFG_AUTORES_CFG_REG, AUTORES_EN_BIT |
AUTORES_EN_DLY_MASK | AUTORES_ERR_WINDOW_MASK,
AUTORES_EN_DLY_1_CYCLE << AUTORES_EN_DLY_SHIFT
| AUTORES_ERR_WINDOW_50_PERCENT | AUTORES_EN_BIT);
if (rc < 0)
goto unlock;
rc = haptics_config_openloop_lra_period(chip, chip->config.t_lra_us);
if (rc < 0)
goto restore;
rc = haptics_set_vmax_mv(chip, chip->config.vmax_mv);
if (rc < 0)
goto restore;
rc = haptics_set_vmax_headroom_mv(chip, LRA_CALIBRATION_VMAX_HDRM_MV);
if (rc < 0)
goto restore;
rc = haptics_set_direct_play(chip, DIRECT_PLAY_MAX_AMPLITUDE);
if (rc < 0)
goto restore;
chip->play.pattern_src = DIRECT_PLAY;
rc = haptics_enable_play(chip, true);
/* Sleep at least 4ms to stabilize the LRA from frequency detection */
usleep_range(4000, 5000);
rc = haptics_detect_lra_impedance(chip);
if (rc < 0) {
dev_err(chip->dev, "Start calibration failed, rc=%d\n", rc);
goto restore;
dev_err(chip->dev, "Detect LRA impedance failed, rc=%d\n", rc);
goto unlock;
}
/* wait for ~150ms to get the LRA calibration result */
usleep_range(150000, 155000);
rc = haptics_get_closeloop_lra_period(chip);
if (rc < 0)
goto restore;
rc = haptics_enable_play(chip, false);
if (rc < 0)
goto restore;
haptics_config_openloop_lra_period(chip, chip->config.cl_t_lra_us);
restore:
rc = haptics_write(chip, chip->cfg_addr_base,
HAP_CFG_AUTORES_CFG_REG, &autores_cfg, 1);
unlock:
chip->play.in_calibration = false;
mutex_unlock(&chip->play.lock);
@ -3620,7 +3820,7 @@ static ssize_t lra_calibration_store(struct class *c,
return -EINVAL;
if (val) {
rc = haptics_start_lra_calibration(chip);
rc = haptics_start_lra_calibrate(chip);
if (rc < 0)
return rc;
}
@ -3644,9 +3844,29 @@ static ssize_t lra_frequency_hz_show(struct class *c,
}
static CLASS_ATTR_RO(lra_frequency_hz);
static ssize_t lra_impedance_show(struct class *c,
struct class_attribute *attr, char *buf)
{
struct haptics_chip *chip = container_of(c,
struct haptics_chip, hap_class);
if (chip->config.lra_min_mohms == 0 && chip->config.lra_max_mohms == 0)
return -EINVAL;
else if (chip->config.lra_min_mohms == 0)
return scnprintf(buf, PAGE_SIZE, "%s\n", "Short circuit");
else if (chip->config.lra_max_mohms == MAX_IMPEDANCE_MOHM)
return scnprintf(buf, PAGE_SIZE, "%s\n", "Open circuit");
else
return scnprintf(buf, PAGE_SIZE, "%u ~ %u mohms\n",
chip->config.lra_min_mohms,
chip->config.lra_max_mohms);
}
static CLASS_ATTR_RO(lra_impedance);
static struct attribute *hap_class_attrs[] = {
&class_attr_lra_calibration.attr,
&class_attr_lra_frequency_hz.attr,
&class_attr_lra_impedance.attr,
NULL,
};
ATTRIBUTE_GROUPS(hap_class);
@ -3774,6 +3994,9 @@ static int haptics_remove(struct platform_device *pdev)
{
struct haptics_chip *chip = dev_get_drvdata(&pdev->dev);
if (chip->pbs_node)
of_node_put(chip->pbs_node);
class_unregister(&chip->hap_class);
#ifdef CONFIG_DEBUG_FS
debugfs_remove_recursive(chip->debugfs_dir);