From 5ef7f0d84fe94a792e89df473d2b9c34b027c11e Mon Sep 17 00:00:00 2001 From: Fenglin Wu Date: Wed, 27 Nov 2019 14:40:37 +0800 Subject: [PATCH 01/76] input: misc: Add initial driver to support QTI HV haptics QTI HV (High Voltage) haptics is a module which can play haptics effects for LRA or ERM with drive voltage up to 10 V. It has several pattern sources built inside and each pattern source can be used for playing different vibration effects. Add the driver to support QTI HV haptics module, which will be registered as an input FF device and can be controlled by calling read/write/ ioctl APIs on the input device node. Change-Id: I8323bdf3f7a631570604577719d5b7876fc65ef1 Signed-off-by: Fenglin Wu --- drivers/input/misc/Kconfig | 11 + drivers/input/misc/Makefile | 1 + drivers/input/misc/qcom-hv-haptics.c | 1746 ++++++++++++++++++++++++++ 3 files changed, 1758 insertions(+) create mode 100644 drivers/input/misc/qcom-hv-haptics.c diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index dd5227cf8696..26016454e5ab 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -186,6 +186,17 @@ config INPUT_PMIC8XXX_PWRKEY To compile this driver as a module, choose M here: the module will be called pmic8xxx-pwrkey. +config INPUT_QCOM_HV_HAPTICS + tristate "QTI High Voltage Haptics support" + depends on MFD_SPMI_PMIC + help + This option enables device driver support for the high voltage haptics + peripheral found on Qualcomm Technologies, Inc. PMICs. The high voltage + haptics peripheral is capable of driving either LRA or ERM vibrators with + drive voltage up to 10 V, and its 5 integrated pattern sources can be used + for playing different vibration effects. To compile this driver as a module, + choose M here: the module will be called qcom-hv-haptics. + config INPUT_SPARCSPKR tristate "SPARC Speaker support" depends on PCI && SPARC64 diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index b92c53a6b5ae..ebd67c86cf58 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -61,6 +61,7 @@ obj-$(CONFIG_INPUT_PCSPKR) += pcspkr.o obj-$(CONFIG_INPUT_PM8941_PWRKEY) += pm8941-pwrkey.o obj-$(CONFIG_INPUT_PM8XXX_VIBRATOR) += pm8xxx-vibrator.o obj-$(CONFIG_INPUT_PMIC8XXX_PWRKEY) += pmic8xxx-pwrkey.o +obj-$(CONFIG_INPUT_QCOM_HV_HAPTICS) += qcom-hv-haptics.o obj-$(CONFIG_INPUT_POWERMATE) += powermate.o obj-$(CONFIG_INPUT_PWM_BEEPER) += pwm-beeper.o obj-$(CONFIG_INPUT_PWM_VIBRA) += pwm-vibra.o diff --git a/drivers/input/misc/qcom-hv-haptics.c b/drivers/input/misc/qcom-hv-haptics.c new file mode 100644 index 000000000000..5b8d9d57c306 --- /dev/null +++ b/drivers/input/misc/qcom-hv-haptics.c @@ -0,0 +1,1746 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* status register definitions in HAPTICS_CFG module */ +#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) +#define CAL_TLRA_CL_STS_MSB_MASK GENMASK(4, 0) + +#define HAP_CFG_STATUS_DATA_LSB_REG 0x0A +/* STATUS_DATA_MSB definitions while MOD_STATUS_SEL is 0 */ +#define CAL_TLRA_CL_STS_LSB_MASK GENMASK(7, 0) + +#define HAP_CFG_FAULT_STATUS_REG 0x0C +#define SC_FLAG_BIT BIT(2) +#define AUTO_RES_ERROR_BIT BIT(1) +#define HPRW_RDY_FAULT_BIT BIT(0) + +/* config register definitions in HAPTICS_CFG module */ +#define HAP_CFG_DRV_CTRL_REG 0x47 +#define PSTG_DLY_MASK GENMASK(7, 6) +#define DRV_SLEW_RATE_MASK GENMASK(2, 0) + +#define HAP_CFG_VMAX_REG 0x48 +#define VMAX_STEP_MV 50 +#define MAX_VMAX_MV 11000 +#define DEFAULT_VMAX_MV 5000 + +#define HAP_CFG_DRV_WF_SEL_REG 0x49 +#define DRV_WF_SEL_MASK GENMASK(1, 0) + +#define HAP_CFG_AUTO_SHUTDOWN_CFG_REG 0x4A +#define HAP_CFG_TRIG_PRIORITY_REG 0x4B + +#define HAP_CFG_SPMI_PLAY_REG 0x4C +#define PLAY_EN_BIT BIT(7) +#define BRAKE_EN_BIT BIT(3) +#define PAT_SRC_MASK GENMASK(2, 0) + +#define HAP_CFG_EXT_TRIG_REG 0x4D + +#define HAP_CFG_SWR_ACCESS_REG 0x4E +#define SWR_PAT_CFG_EN_BIT BIT(7) +#define SWR_PAT_INPUT_EN BIT(6) +#define SWR_PAT_RES_N BIT(5) + +#define HAP_CFG_BRAKE_MODE_CFG_REG 0x50 +#define BRAKE_MODE_MASK GENMASK(7, 6) +#define BRAKE_MODE_SHIFT 6 +#define BRAKE_SINE_GAIN_MASK GENMASK(3, 2) +#define BRAKE_SINE_GAIN_SHIFT 2 +#define BRAKE_WF_SEL_MASK GENMASK(1, 0) + +#define HAP_CFG_CL_BRAKE_CFG_REG 0x51 +#define HAP_CFG_CL_BRAKE_CAL_PARAM_REG 0x52 +#define HAP_CFG_CL_BRAKE_RSET_REG 0x53 +#define HAP_CFG_PWM_CFG_REG 0x5A + +#define HAP_CFG_TLRA_OL_HIGH_REG 0x5C +#define TLRA_OL_MSB_MASK GENMASK(3, 0) +#define HAP_CFG_TLRA_OL_LOW_REG 0x5D +#define TLRA_OL_LSB_MASK GENMASK(7, 0) +#define TLRA_STEP_US 5 +#define TLRA_MAX_US 20475 + +#define HAP_CFG_RC_CLK_CAL_COUNT_MSB_REG 0x5E +#define RC_CLK_CAL_COUNT_MSB_MASK GENMASK(1, 0) +#define HAP_CFG_RC_CLK_CAL_COUNT_LSB_REG 0x5F +#define RC_CLK_CAL_COUNT_LSB_MASK GENMASK(7, 0) + +#define HAP_CFG_DRV_DUTY_CFG_REG 0x60 +#define HAP_CFG_ADT_DRV_DUTY_CFG_REG 0x61 +#define HAP_CFG_ZX_WIND_CFG_REG 0x62 + +#define HAP_CFG_AUTORES_CFG_REG 0x63 +#define AUTORES_EN_BIT BIT(7) + +#define HAP_CFG_AUTORES_ERR_RECOVERY_REG 0x64 +#define EN_HW_RECOVERY_BIT BIT(1) +#define SW_ERR_DRV_FREQ_BIT BIT(0) + +#define HAP_CFG_FAULT_CLR_REG 0x66 +#define SC_CLR_BIT BIT(2) +#define AUTO_RES_ERR_CLR_BIT BIT(1) +#define HPWR_RDY_FAULT_CLR_BIT BIT(0) + +#define HAP_CFG_VMAX_HDRM_REG 0x67 +#define VMAX_HDRM_MASK GENMASK(6, 0) +#define VMAX_HDRM_STEP_MV 50 + +/* status register definition for HAPTICS_PATTERN module */ +#define HAP_PTN_FIFO_READY_STS_REG 0x08 +#define FIFO_READY_BIT BIT(0) + +#define HAP_PTN_NUM_PAT_REG 0x09 + +/* config register definition for HAPTICS_PATTERN module */ +#define HAP_PTN_FIFO_DIN_MSB_REG 0x20 +#define HAP_PTN_FIFO_DIN_MSB_BIT BIT(0) +#define HAP_PTN_FIFO_DIN_LSB_REG 0x21 +#define HAP_PTN_FIFO_DIN_LSB_MASK GENMASK(7, 0) + +#define HAP_PTN_FIFO_PLAY_RATE_REG 0x22 +#define FIFO_PLAY_RATE_MASK GENMASK(3, 0) + +#define HAP_PTN_FIFO_EMPTY_CFG_REG 0x23 +#define EMPTY_THRESH_MASK GENMASK(3, 0) + +#define HAP_PTN_FIFO_DEPTH_CFG_REG 0x24 + +#define HAP_PTN_DIRECT_PLAY_REG 0x26 + +#define HAP_PTN_AUTORES_CAL_CFG_REG 0x28 + +#define HAP_PTN_PTRN1_TLRA_MSB_REG 0x30 +#define HAP_PTN_PTRN1_TLRA_LSB_REG 0x31 +#define HAP_PTN_PTRN2_TLRA_MSB_REG 0x32 +#define HAP_PTN_PTRN2_TLRA_LSB_REG 0x33 + +#define HAP_PTN_PTRN1_CFG_REG 0x34 +#define PTRN_FLRA2X_SHIFT 7 +#define PTRN_SAMPLE_PER_MASK GENMASK(2, 0) + +#define PTRN_AMP_MSB_MASK BIT(0) +#define PTRN_AMP_LSB_MASK GENMASK(7, 0) + +#define HAP_PTN_PTRN2_CFG_REG 0x50 + +#define HAP_PTN_BRAKE_AMP_REG 0x70 + +/* constant parameters */ +#define SAMPLES_PER_PATTERN 8 +#define BRAKE_SAMPLE_COUNT 8 +#define MAX_FIFO_SAMPLES 104 +#define DEFAULT_ERM_PLAY_RATE_US 5000 +#define MAX_EFFECT_COUNT 64 +#define FIFO_EMPTY_THRESHOLD 48 +#define FIFO_READY_TIMEOUT_MS 1000 +#define CHAR_PER_PATTERN_S 48 +#define CHAR_PER_SAMPLE 8 +#define CHAR_MSG_HEADER 16 +#define HW_BRAKE_CYCLES 5 + +enum drv_sig_shape { + WF_SQUARE, + WF_SINE, + WF_NO_MODULATION, + WF_RESERVED, +}; + +enum brake_mode { + OL_BRAKE, + CL_BRAKE, + PREDICT_BRAKE, + AUTO_BRAKE, +}; + +enum brake_sine_gain { + BRAKE_SINE_GAIN_X1, + BRAKE_SINE_GAIN_X2, + BRAKE_SINE_GAIN_X4, + BRAKE_SINE_GAIN_X8, +}; + +enum pattern_src { + FIFO, + DIRECT_PLAY, + PATTERN1, + PATTERN2, + SWR, + SRC_RESERVED, +}; + +enum s_period { + T_LRA = 0, + T_LRA_DIV_2, + T_LRA_DIV_4, + T_LRA_DIV_8, + T_LRA_X_2, + T_LRA_X_4, + T_LRA_X_8, + T_RESERVED, + /* F_xKHZ definitions are for FIFO only */ + F_8KHZ, + F_16HKZ, + F_24KHZ, + F_32KHZ, + F_44P1KHZ, + F_48KHZ, + F_RESERVED, +}; + +enum custom_effect_param { + CUSTOM_DATA_EFFECT_IDX, + CUSTOM_DATA_TIMEOUT_SEC_IDX, + CUSTOM_DATA_TIMEOUT_MSEC_IDX, + CUSTOM_DATA_LEN, +}; + +static const char * const src_str[] = { + "FIFO", + "DIRECT_PLAY", + "PATTERN1", + "PATTERN2", + "SWR", + "reserved", +}; + +static const char * const brake_str[] = { + "open-loop-brake", + "close-loop-brake", + "predictive-brake", + "auto-brake", +}; + +static const char * const period_str[] = { + "T_LRA", + "T_LRA_DIV_2", + "T_LRA_DIV_4", + "T_LRA_DIV_8", + "T_LRA_X_2", + "T_LRA_X_4", + "T_LRA_X_8", + "reserved_1", + "F_8KHZ", + "F_16HKZ", + "F_24KHZ", + "F_32KHZ", + "F_44P1KHZ", + "F_48KHZ", + "reserved_2", +}; + +struct pattern_s { + u16 amplitude; + enum s_period period; + bool f_lra_x2; +}; + +struct pattern_cfg { + struct pattern_s samples[SAMPLES_PER_PATTERN]; + u32 play_rate_us; + u32 play_length_us; + bool preload; +}; + +struct fifo_cfg { + u16 *samples; + u32 num_s; + enum s_period period_per_s; + u32 play_length_us; +}; + +struct brake_cfg { + u8 samples[BRAKE_SAMPLE_COUNT]; + enum brake_mode mode; + enum drv_sig_shape brake_wf; + enum brake_sine_gain sine_gain; + u32 play_length_us; + bool disabled; +}; + +struct haptics_effect { + struct pattern_cfg *pattern; + struct fifo_cfg *fifo; + struct brake_cfg *brake; + u32 id; + u32 vmax_mv; + u32 t_lra_us; + enum pattern_src src; + bool auto_res_disable; +}; + +struct fifo_play_status { + struct completion fifo_ready; + u32 samples_written; + atomic_t written_done; + atomic_t is_busy; +}; + +struct haptics_play_info { + struct haptics_effect *effect; + struct brake_cfg *brake; + struct fifo_play_status fifo_status; + u32 vmax_mv; + u32 length_us; + enum pattern_src pattern_src; +}; + +struct haptics_hw_config { + struct brake_cfg brake; + u32 vmax_mv; + u32 t_lra_us; + u32 preload_effect; + enum drv_sig_shape drv_wf; + bool is_erm; +}; + +struct haptics_chip { + struct device *dev; + struct regmap *regmap; + struct input_dev *input_dev; + struct haptics_hw_config config; + struct haptics_effect *effects; + struct haptics_play_info play; + struct work_struct fifo_work; + int fifo_empty_irq; + u32 effects_count; + u32 cfg_addr_base; + u32 ptn_addr_base; + bool fifo_empty_irq_en; +}; + +static int haptics_read(struct haptics_chip *chip, + u16 base, u8 offset, u8 *val, u32 length) +{ + int rc = 0; + u16 addr = base + offset; + + rc = regmap_bulk_read(chip->regmap, addr, val, length); + if (rc < 0) + dev_err(chip->dev, "read addr %d failed, rc=%d\n", addr, rc); + + return rc; +} + +static int haptics_write(struct haptics_chip *chip, + u16 base, u8 offset, u8 *val, u32 length) +{ + int rc = 0; + u16 addr = base + offset; + + rc = regmap_bulk_write(chip->regmap, addr, val, length); + if (rc < 0) + dev_err(chip->dev, "write addr %d failed, rc=%d\n", addr, rc); + + return rc; +} + +static int haptics_masked_write(struct haptics_chip *chip, + u16 base, u8 offset, u8 mask, u8 val) +{ + int rc = 0; + u16 addr = base + offset; + + regmap_update_bits(chip->regmap, addr, mask, val); + if (rc < 0) + dev_err(chip->dev, "update addr %d failed, rc=%d\n", addr, rc); + + return rc; +} + +static void __dump_effects(struct haptics_chip *chip) +{ + struct haptics_effect *effect; + struct pattern_s *sample; + char *str; + u32 size, pos; + int i, j; + + for (i = 0; i < chip->effects_count; i++) { + effect = &chip->effects[i]; + if (!effect) + return; + + dev_dbg(chip->dev, "effect %d\n", effect->id); + dev_dbg(chip->dev, "vmax_mv = %d\n", effect->vmax_mv); + if (effect->pattern) { + for (j = 0; j < SAMPLES_PER_PATTERN; j++) { + sample = &effect->pattern->samples[j]; + dev_dbg(chip->dev, "pattern = %d, period = %s, f_lra_x2 = %d\n", + sample->amplitude, + period_str[sample->period], + sample->f_lra_x2); + } + + dev_dbg(chip->dev, "pattern play_rate_us = %d\n", + effect->pattern->play_rate_us); + dev_dbg(chip->dev, "pattern play_length_us = %d\n", + effect->pattern->play_length_us); + dev_dbg(chip->dev, "pattern preload = %d\n", + effect->pattern->preload); + } + + if (effect->fifo) { + size = effect->fifo->num_s * CHAR_PER_SAMPLE + + CHAR_MSG_HEADER; + str = kzalloc(size, GFP_KERNEL); + if (str == NULL) + return; + + pos = 0; + pos += scnprintf(str, size, "%s", "FIFO data: "); + for (j = 0; j < effect->fifo->num_s; j++) + pos += scnprintf(str + pos, size - pos, "%#x ", + effect->fifo->samples[j]); + + dev_dbg(chip->dev, "%s\n", str); + kfree(str); + dev_dbg(chip->dev, "FIFO data play rate: %s\n", + period_str[effect->fifo->period_per_s]); + dev_dbg(chip->dev, "FIFO data play length: %dus\n", + effect->fifo->play_length_us); + } + + if (effect->brake && !effect->brake->disabled) { + size = BRAKE_SAMPLE_COUNT * CHAR_PER_SAMPLE + + CHAR_MSG_HEADER; + str = kzalloc(size, GFP_KERNEL); + if (str == NULL) + return; + + pos = 0; + pos += scnprintf(str, size, "%s", "brake pattern: "); + for (j = 0; j < BRAKE_SAMPLE_COUNT; j++) + pos += scnprintf(str + pos, size - pos, "%#x ", + effect->brake->samples[j]); + + dev_dbg(chip->dev, "%s\n", str); + kfree(str); + dev_dbg(chip->dev, "brake mode: %s\n", + brake_str[effect->brake->mode]); + dev_dbg(chip->dev, "brake play length: %dus\n", + effect->brake->play_length_us); + } + + dev_dbg(chip->dev, "pattern src: %s\n", src_str[effect->src]); + dev_dbg(chip->dev, "auto resonance %s\n", + effect->auto_res_disable ? + "disabled" : "enabled"); + } +} + +static void verify_brake_samples(struct brake_cfg *brake) +{ + int i; + + if (brake->mode == PREDICT_BRAKE || brake->mode == AUTO_BRAKE) + return; + + for (i = BRAKE_SAMPLE_COUNT - 1; i > 0; i--) { + if (brake->samples[i] != 0) { + brake->disabled = false; + return; + } + } + + brake->disabled = true; +} + +static int get_pattern_play_length_us(struct pattern_cfg *pattern) +{ + int i = SAMPLES_PER_PATTERN - 1, j; + u32 us_per_sample, total_length_us = 0; + + if (!pattern) + return -EINVAL; + + for (; i >= 0; i--) + if (pattern->samples[i].amplitude != 0) + break; + + for (j = 0; j < i; j++) { + us_per_sample = pattern->play_rate_us; + switch (pattern->samples[j].period) { + case T_LRA: + break; + case T_LRA_DIV_2: + us_per_sample /= 2; + break; + case T_LRA_DIV_4: + us_per_sample /= 4; + break; + case T_LRA_DIV_8: + us_per_sample /= 8; + break; + case T_LRA_X_2: + us_per_sample *= 2; + break; + case T_LRA_X_4: + us_per_sample *= 4; + break; + case T_LRA_X_8: + us_per_sample *= 8; + break; + default: + return -EINVAL; + } + + if (pattern->samples[j].f_lra_x2) + us_per_sample /= 2; + + total_length_us += us_per_sample; + } + + return total_length_us; +} + +static int get_fifo_play_length_us(struct fifo_cfg *fifo, u32 t_lra_us) +{ + u32 length_us; + int i; + + if (!fifo) + return -EINVAL; + + for (i = fifo->num_s; i > 0; i--) + if (fifo->samples[i] != 0) + break; + + length_us = (i + 1) * t_lra_us; + switch (fifo->period_per_s) { + case T_LRA: + break; + case T_LRA_DIV_2: + length_us /= 2; + break; + case T_LRA_DIV_4: + length_us /= 4; + break; + case T_LRA_DIV_8: + length_us /= 8; + break; + case F_8KHZ: + length_us = 1000 * fifo->num_s / 8; + break; + case F_16HKZ: + length_us = 1000 * fifo->num_s / 16; + break; + case F_24KHZ: + length_us = 1000 * fifo->num_s / 24; + break; + case F_32KHZ: + length_us = 1000 * fifo->num_s / 32; + break; + case F_44P1KHZ: + length_us = 10000 * fifo->num_s / 441; + break; + case F_48KHZ: + length_us = 1000 * fifo->num_s / 48; + break; + default: + length_us = -EINVAL; + break; + } + + return length_us; +} + +static int get_brake_play_length_us(struct brake_cfg *brake, u32 t_lra_us) +{ + int i = BRAKE_SAMPLE_COUNT - 1; + + if (!brake || brake->disabled) + return 0; + + if (brake->mode == PREDICT_BRAKE || brake->mode == AUTO_BRAKE) + return HW_BRAKE_CYCLES * t_lra_us; + + for (; i >= 0; i--) + if (brake->samples[i] != 0) + break; + + return t_lra_us * (i + 1); +} + +static int haptics_set_vmax_mv(struct haptics_chip *chip, u32 vmax_mv) +{ + int rc = 0; + u8 val; + + if (vmax_mv > MAX_VMAX_MV) { + dev_err(chip->dev, "vmax (%d) exceed the max value: %d\n", + vmax_mv, MAX_VMAX_MV); + return -EINVAL; + } + + val = vmax_mv / VMAX_STEP_MV; + rc = haptics_write(chip, chip->cfg_addr_base, + HAP_CFG_VMAX_REG, &val, 1); + if (rc < 0) + dev_err(chip->dev, "config VMAX failed, rc=%d\n", rc); + + return rc; +} + +static int haptics_enable_autores(struct haptics_chip *chip, bool en) +{ + int rc; + + rc = haptics_masked_write(chip, chip->cfg_addr_base, + HAP_CFG_AUTORES_CFG_REG, AUTORES_EN_BIT, + en ? AUTORES_EN_BIT : 0); + if (rc < 0) + dev_err(chip->dev, "%s auto resonance failed, rc=%d\n", + en ? "enable" : "disable", rc); + + return rc; +} + +static int haptics_enable_play(struct haptics_chip *chip, bool en) +{ + struct haptics_play_info *play = &chip->play; + int rc; + u8 val; + + val = play->pattern_src; + if (play->brake && !play->brake->disabled) + val |= BRAKE_EN_BIT; + + if (en) + val |= PLAY_EN_BIT; + + rc = haptics_write(chip, chip->cfg_addr_base, + HAP_CFG_SPMI_PLAY_REG, &val, 1); + if (rc < 0) + dev_err(chip->dev, "Write SPMI_PLAY failed, rc=%d\n", rc); + + return rc; +} + +static int haptics_set_brake(struct haptics_chip *chip, struct brake_cfg *brake) +{ + int rc = 0; + u8 zero_samples[BRAKE_SAMPLE_COUNT] = {0}, val; + + if (brake->disabled) + return 0; + + val = brake->mode << BRAKE_MODE_SHIFT | + brake->sine_gain << BRAKE_SINE_GAIN_SHIFT; + rc = haptics_masked_write(chip, chip->cfg_addr_base, + HAP_CFG_BRAKE_MODE_CFG_REG, + BRAKE_MODE_MASK | BRAKE_SINE_GAIN_MASK, val); + if (rc < 0) { + dev_err(chip->dev, "set brake CFG failed, rc=%d\n", rc); + return rc; + } + + rc = haptics_write(chip, chip->ptn_addr_base, HAP_PTN_BRAKE_AMP_REG, + (brake->mode == OL_BRAKE || brake->mode == CL_BRAKE) ? + brake->samples : zero_samples, BRAKE_SAMPLE_COUNT); + if (rc < 0) { + dev_err(chip->dev, "set brake pattern failed, rc=%d\n", rc); + return rc; + } + + return 0; +} + +static int haptics_set_pattern(struct haptics_chip *chip, + struct pattern_cfg *pattern, + enum pattern_src src) +{ + struct pattern_s *sample; + u8 values[SAMPLES_PER_PATTERN * 3] = { 0 }; + u8 ptn_tlra_addr, ptn_cfg_addr; + int i, rc, tmp; + + if (src != PATTERN1 && src != PATTERN2) { + dev_err(chip->dev, "no pattern src specified!\n"); + return -EINVAL; + } + + ptn_tlra_addr = HAP_PTN_PTRN1_TLRA_MSB_REG; + ptn_cfg_addr = HAP_PTN_PTRN1_CFG_REG; + if (src == PATTERN2) { + ptn_tlra_addr = HAP_PTN_PTRN2_TLRA_MSB_REG; + ptn_cfg_addr = HAP_PTN_PTRN2_CFG_REG; + } + + /* Configure T_LRA for this pattern */ + tmp = pattern->play_rate_us / TLRA_STEP_US; + values[0] = (tmp >> 8) & TLRA_OL_MSB_MASK; + values[1] = tmp & TLRA_OL_LSB_MASK; + rc = haptics_write(chip, chip->ptn_addr_base, ptn_tlra_addr, + values, 2); + if (rc < 0) { + dev_err(chip->dev, "update pattern TLRA failed, rc=%d\n", rc); + return rc; + } + + /* Configure pattern registers */ + for (i = 0; i < SAMPLES_PER_PATTERN; i++) { + sample = &pattern->samples[i]; + values[i * 3] = sample->f_lra_x2 << PTRN_FLRA2X_SHIFT; + values[i * 3] |= sample->period & PTRN_SAMPLE_PER_MASK; + values[i * 3 + 1] = + (sample->amplitude >> 8) & PTRN_AMP_MSB_MASK; + values[i * 3 + 2] = sample->amplitude & PTRN_AMP_LSB_MASK; + } + + rc = haptics_write(chip, chip->ptn_addr_base, ptn_cfg_addr, + values, SAMPLES_PER_PATTERN * 3); + if (rc < 0) { + dev_err(chip->dev, "write pattern data failed, rc=%d\n", rc); + return rc; + } + + return 0; + +} + +static int haptics_update_fifo_sample(struct haptics_chip *chip, u16 sample) +{ + int rc = 0; + u8 val; + bool ready; + + rc = haptics_read(chip, chip->ptn_addr_base, + HAP_PTN_FIFO_READY_STS_REG, &val, 1); + if (rc < 0) { + dev_err(chip->dev, "read FIFO_READY_STS failed, rc=%d\n", + rc); + return rc; + } + + ready = !!(val & FIFO_READY_BIT); + /* sleep no more than 10us if FIFO memory is not ready */ + if (!ready) + usleep_range(1, 10); + + /* + * Fill FIFO_DIN registers to update FIFO memory, + * need to fill LSB first then MSB + */ + val = sample & HAP_PTN_FIFO_DIN_LSB_MASK; + rc = haptics_write(chip, chip->ptn_addr_base, + HAP_PTN_FIFO_DIN_LSB_REG, &val, 1); + if (rc < 0) { + dev_err(chip->dev, "write FIFO LSB failed, rc=%d\n", + rc); + return rc; + } + + val = (sample >> 8) & HAP_PTN_FIFO_DIN_MSB_BIT; + rc = haptics_write(chip, chip->ptn_addr_base, + HAP_PTN_FIFO_DIN_MSB_REG, &val, 1); + if (rc < 0) { + dev_err(chip->dev, "write FIFO MSB failed, rc=%d\n", + rc); + return rc; + } + + return 0; +} + +static int haptics_set_fifo(struct haptics_chip *chip, struct fifo_cfg *fifo) +{ + struct fifo_play_status *status = &chip->play.fifo_status; + u32 num, fifo_thresh; + int rc, i; + + if (atomic_read(&status->is_busy) == 1) { + dev_err(chip->dev, "FIFO is busy\n"); + return -EBUSY; + } + + /* Configure FIFO play rate */ + rc = haptics_masked_write(chip, chip->ptn_addr_base, + HAP_PTN_FIFO_PLAY_RATE_REG, + FIFO_PLAY_RATE_MASK, fifo->period_per_s); + if (rc < 0) + return rc; + + atomic_set(&status->written_done, 0); + status->samples_written = 0; + + /* + * Write the 1st set of the data into FIFO if there are + * more than 104 samples, the rest will be written if + * any FIFO memory is available after playing. + */ + if (fifo->num_s > MAX_FIFO_SAMPLES) + num = MAX_FIFO_SAMPLES; + else + num = fifo->num_s; + + for (i = 0; i < num; i++) { + rc = haptics_update_fifo_sample(chip, fifo->samples[i]); + if (rc < 0) + return rc; + } + + atomic_set(&status->is_busy, 1); + status->samples_written = num; + if (num == fifo->num_s) { + fifo_thresh = 0; + atomic_set(&status->written_done, 1); + } else { + reinit_completion(&status->fifo_ready); + fifo_thresh = FIFO_EMPTY_THRESHOLD; + } + + /* + * Set FIFO empty threshold and enable FIFO empty IRQ, + * more data can be written into FIFO memory after + * the IRQ is triggered. + */ + rc = haptics_masked_write(chip, chip->ptn_addr_base, + HAP_PTN_FIFO_EMPTY_CFG_REG, EMPTY_THRESH_MASK, + fifo_thresh / 4); + if (rc < 0) + return rc; + + if (!chip->fifo_empty_irq_en) { + enable_irq(chip->fifo_empty_irq); + chip->fifo_empty_irq_en = true; + } + + return 0; +} + +static int haptics_set_direct_play(struct haptics_chip *chip) +{ + struct haptics_play_info *play = &chip->play; + int rc = 0; + u8 val; + + /* configure VMAX in case it was changed in previous effect playing */ + rc = haptics_set_vmax_mv(chip, chip->config.vmax_mv); + if (rc < 0) + return rc; + + /* Set DIRECT_PLAY amplitude */ + val = play->vmax_mv / VMAX_STEP_MV; + rc = haptics_write(chip, chip->ptn_addr_base, + HAP_PTN_DIRECT_PLAY_REG, &val, 1); + if (rc < 0) + return rc; + + /* Config brake settings if it's necessary */ + play->brake = &chip->config.brake; + if (play->brake) { + rc = haptics_set_brake(chip, play->brake); + if (rc < 0) + return rc; + } + + /* Always enable LRA auto resonance for DIRECT_PLAY */ + rc = haptics_enable_autores(chip, !chip->config.is_erm); + if (rc < 0) + return rc; + + play->pattern_src = DIRECT_PLAY; + return 0; +} + +static int haptics_load_predefined_effect(struct haptics_chip *chip, + int effect_idx) +{ + struct haptics_play_info *play = &chip->play; + int rc; + + if (effect_idx >= chip->effects_count) + return -EINVAL; + + play->effect = &chip->effects[effect_idx]; + /* Clamp VMAX for different vibration strength */ + rc = haptics_set_vmax_mv(chip, play->vmax_mv); + if (rc < 0) + return rc; + + play->pattern_src = play->effect->src; + if (play->pattern_src != PATTERN1 || + play->pattern_src != PATTERN2 || + play->pattern_src != FIFO) { + dev_err(chip->dev, "pattern src %d can't be used for predefined effect\n", + play->pattern_src); + return -EINVAL; + } + + play->brake = play->effect->brake; + /* Config brake settings if it's necessary */ + if (play->brake) { + rc = haptics_set_brake(chip, play->brake); + if (rc < 0) + return rc; + } + + if (play->pattern_src == PATTERN1 || play->pattern_src == PATTERN2) { + if (play->effect->pattern->preload) { + dev_dbg(chip->dev, "Ignore preloaded effect: %d\n", + play->effect->id); + return 0; + } + + rc = haptics_set_pattern(chip, play->effect->pattern, + play->pattern_src); + if (rc < 0) + return rc; + } + + if (play->pattern_src == FIFO) { + rc = haptics_set_fifo(chip, play->effect->fifo); + if (rc < 0) + return rc; + } + + return 0; +} + +static u32 get_play_length_us(struct haptics_play_info *play) +{ + struct haptics_effect *effect = play->effect; + u32 length_us = 0; + + if (play->brake) + length_us = play->brake->play_length_us; + + if ((effect->src == PATTERN1 || effect->src == PATTERN2) + && effect->pattern) + length_us += effect->pattern->play_length_us; + else if (effect->src == FIFO && effect->fifo) + length_us += effect->fifo->play_length_us; + + return length_us; +} + +static int haptics_upload_effect(struct input_dev *dev, + struct ff_effect *effect, struct ff_effect *old) +{ + struct haptics_chip *chip = input_get_drvdata(dev); + struct haptics_hw_config *config = &chip->config; + struct haptics_play_info *play = &chip->play; + s16 level, data[CUSTOM_DATA_LEN]; + int rc = 0, tmp, i; + + 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); + haptics_set_direct_play(chip); + if (rc < 0) { + dev_err(chip->dev, "set direct play 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 support custom waveforms\n"); + return -EINVAL; + } + + if (copy_from_user(data, effect->u.periodic.custom_data, + sizeof(data))) + return -EFAULT; + + for (i = 0; i < chip->effects_count; i++) + if (chip->effects[i].id == data[CUSTOM_DATA_EFFECT_IDX]) + break; + + if (i == chip->effects_count) { + dev_err(chip->dev, "effect%d is not supported!\n", + data[CUSTOM_DATA_EFFECT_IDX]); + return -EINVAL; + } + + level = effect->u.periodic.magnitude; + tmp = level * chip->effects[i].vmax_mv; + play->vmax_mv = tmp / 0x7fff; + + dev_dbg(chip->dev, "upload effect %d, vmax_mv=%d\n", + chip->effects[i].id, play->vmax_mv); + rc = haptics_load_predefined_effect(chip, i); + if (rc < 0) { + dev_err(chip->dev, "Play predefined effect%d failed, rc=%d\n", + chip->effects[i].id, rc); + return rc; + } + + play->length_us = get_play_length_us(play); + 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; + + if (copy_to_user(effect->u.periodic.custom_data, data, + sizeof(s16) * CUSTOM_DATA_LEN)) + return -EFAULT; + break; + default: + dev_err(chip->dev, "%d effect is not supported\n", + effect->type); + return -EINVAL; + } + + return 0; +} + +static int haptics_playback(struct input_dev *dev, int effect_id, int val) +{ + struct haptics_chip *chip = input_get_drvdata(dev); + struct haptics_play_info *play = &chip->play; + int rc; + + dev_dbg(chip->dev, "playback val = %d\n", val); + if (!!val) { + rc = haptics_enable_play(chip, true); + if (rc < 0) + return rc; + + if ((atomic_read(&play->fifo_status.written_done) == 0) + && play->pattern_src == FIFO) + schedule_work(&chip->fifo_work); + } else { + rc = haptics_enable_play(chip, false); + if (rc < 0) + return rc; + + if (chip->fifo_empty_irq_en) { + disable_irq_nosync(chip->fifo_empty_irq); + chip->fifo_empty_irq_en = false; + } + } + + return 0; +} + +static int haptics_erase(struct input_dev *dev, int effect_id) +{ + return 0; +} + +static void haptics_set_gain(struct input_dev *dev, u16 gain) +{ + struct haptics_chip *chip = input_get_drvdata(dev); + struct haptics_hw_config *config = &chip->config; + struct haptics_play_info *play = &chip->play; + + if (gain == 0) + return; + + if (gain > 0x7fff) + gain = 0x7fff; + + play->vmax_mv = ((u32)(gain * config->vmax_mv)) / 0x7fff; + haptics_set_vmax_mv(chip, play->vmax_mv); +} + +static int haptics_hw_init(struct haptics_chip *chip) +{ + struct haptics_hw_config *config = &chip->config; + struct haptics_effect *effect; + int rc = 0, tmp, i; + u8 val[2]; + + /* Config VMAX */ + rc = haptics_set_vmax_mv(chip, config->vmax_mv); + if (rc < 0) + return rc; + + /* Config driver waveform shape */ + rc = haptics_masked_write(chip, chip->cfg_addr_base, + HAP_CFG_DRV_WF_SEL_REG, + DRV_WF_SEL_MASK, config->drv_wf); + if (rc < 0) + return rc; + + /* Config brake mode and waveform shape */ + val[0] = (config->brake.mode << BRAKE_MODE_SHIFT) + | config->brake.sine_gain << BRAKE_SINE_GAIN_SHIFT + | config->brake.brake_wf; + rc = haptics_masked_write(chip, chip->cfg_addr_base, + HAP_CFG_BRAKE_MODE_CFG_REG, + BRAKE_MODE_MASK | BRAKE_SINE_GAIN_MASK + | BRAKE_WF_SEL_MASK, val[0]); + if (rc < 0) + return rc; + + if (config->is_erm) + return 0; + + /* Config T_LRA */ + tmp = config->t_lra_us / TLRA_STEP_US; + val[0] = (tmp >> 8) & TLRA_OL_MSB_MASK; + val[1] = tmp & TLRA_OL_LSB_MASK; + rc = haptics_write(chip, chip->cfg_addr_base, + HAP_CFG_TLRA_OL_HIGH_REG, val, 2); + if (rc < 0) + return rc; + + /* preload effect */ + if (config->preload_effect != -EINVAL) { + for (i = 0; i < chip->effects_count; i++) + if (chip->effects[i].id == config->preload_effect) + break; + + if (i == chip->effects_count) { + dev_err(chip->dev, "preload effect %d is not found\n", + config->preload_effect); + return -EINVAL; + } + + effect = &chip->effects[i]; + + rc = haptics_set_pattern(chip, effect->pattern, effect->src); + if (rc < 0) { + dev_err(chip->dev, "Preload effect failed, rc=%d\n", + rc); + return rc; + } + } + + return rc; +} + +static void update_fifo_work(struct work_struct *work) +{ + struct haptics_chip *chip = container_of(work, + struct haptics_chip, fifo_work); + struct fifo_cfg *fifo = chip->play.effect->fifo; + struct fifo_play_status *status = &chip->play.fifo_status; + u32 num, samples_written, samples_left; + int rc, i; + + samples_written = status->samples_written; + num = MAX_FIFO_SAMPLES - FIFO_EMPTY_THRESHOLD; + samples_left = fifo->num_s - samples_written; + + while (samples_left > 0) { + /* Waiting on FIFO empty IRQ triggered */ + rc = wait_for_completion_timeout(&status->fifo_ready, + msecs_to_jiffies(FIFO_READY_TIMEOUT_MS)); + if (!rc) { + dev_err(chip->dev, "Timeout on waiting FIFO ready!\n"); + return; + } + + if (samples_left <= num) + num = samples_left; + else + reinit_completion(&status->fifo_ready); + + /* Write more pattern data into FIFO memory */ + for (i = 0; i < num; i++) { + rc = haptics_update_fifo_sample(chip, + fifo->samples[samples_written + i]); + if (rc < 0) + return; + } + + samples_written += num; + samples_left -= num; + dev_dbg(chip->dev, "FIFO %d samples written, %d samples left\n", + samples_written, samples_left); + } + + /* + * If all pattern data is written, set FIFO empty + * threshold to 0 so that FIFO empty IRQ can be used + * for detecting FIFO playing done event. + */ + dev_dbg(chip->dev, "FIFO programmed done\n"); + atomic_set(&chip->play.fifo_status.written_done, 1); + rc = haptics_masked_write(chip, chip->ptn_addr_base, + HAP_PTN_FIFO_EMPTY_CFG_REG, + EMPTY_THRESH_MASK, 0); + if (rc < 0) + dev_err(chip->dev, "set FIFO empty threshold to 0 failed, rc=%d\n", + rc); +} + +static irqreturn_t fifo_empty_irq_handler(int irq, void *data) +{ + struct haptics_chip *chip = data; + int rc; + + if (atomic_read(&chip->play.fifo_status.written_done) == 1) { + dev_dbg(chip->dev, "FIFO data is done playing\n"); + rc = haptics_enable_play(chip, false); + if (rc < 0) + return IRQ_HANDLED; + + if (chip->fifo_empty_irq_en) { + disable_irq_nosync(chip->fifo_empty_irq); + chip->fifo_empty_irq_en = false; + } + + atomic_set(&chip->play.fifo_status.written_done, 0); + atomic_set(&chip->play.fifo_status.is_busy, 0); + } else { + complete(&chip->play.fifo_status.fifo_ready); + } + + return IRQ_HANDLED; +} + +static int haptics_parse_per_effect_dt(struct haptics_chip *chip, + struct device_node *node, struct haptics_effect *effect) +{ + struct haptics_hw_config *config = &chip->config; + u32 data[SAMPLES_PER_PATTERN * 3]; + int rc, tmp, i; + + if (!effect) + return -EINVAL; + + rc = of_property_read_u32(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(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; + + if (effect->vmax_mv > MAX_VMAX_MV) { + dev_err(chip->dev, "qcom,wf-vmax-mv (%d) exceed the max value: %d\n", + effect->vmax_mv, MAX_VMAX_MV); + return -EINVAL; + } + + effect->t_lra_us = config->t_lra_us; + tmp = of_property_count_elems_of_size(node, + "qcom,wf-pattern-data", sizeof(u32)); + if (tmp > SAMPLES_PER_PATTERN * 3) { + dev_err(chip->dev, "Pattern src can only play 8 samples at max\n"); + return -EINVAL; + } + + if (tmp > 0) { + effect->pattern = devm_kzalloc(chip->dev, + sizeof(*effect->pattern), GFP_KERNEL); + if (!effect->pattern) + return -ENOMEM; + + rc = of_property_read_u32_array(node, + "qcom,wf-pattern-data", data, tmp); + if (rc < 0) { + dev_err(chip->dev, "Read wf-pattern-data failed, rc=%d\n", + rc); + return rc; + } + + for (i = 0; i < tmp / 3; i++) { + if (data[3 * i] > 0x1ff || data[3 * i + 1] > T_LRA_X_8 + || data[3 * i + 2] > 1) { + dev_err(chip->dev, "allowed tuples: [amplitude(<= 0x1ff) period(<=6(T_LRA_X_8)) f_lra_x2(0,1)]\n"); + return -EINVAL; + } + + effect->pattern->samples[i].amplitude = + (u16)data[3 * i]; + effect->pattern->samples[i].period = + (enum s_period)data[3 * i + 1]; + effect->pattern->samples[i].f_lra_x2 = + (bool)data[3 * i + 2]; + } + + effect->pattern->preload = of_property_read_bool(node, + "qcom,wf-pattern-preload"); + /* + * Use PATTERN1 src by default, effect with preloaded + * pattern will use PATTERN2 by default and only the + * 1st preloaded pattern will be served. + */ + effect->src = PATTERN1; + if (effect->pattern->preload) { + if (config->preload_effect != -EINVAL) { + dev_err(chip->dev, "effect %d has been defined as preloaded\n", + config->preload_effect); + effect->pattern->preload = false; + } else { + config->preload_effect = effect->id; + effect->src = PATTERN2; + } + } + } + + tmp = of_property_count_u16_elems(node, "qcom,wf-fifo-data"); + if (tmp > 0) { + effect->fifo = devm_kzalloc(chip->dev, + sizeof(*effect->fifo), GFP_KERNEL); + if (!effect->fifo) + return -ENOMEM; + + effect->fifo->samples = devm_kcalloc(chip->dev, + tmp, sizeof(u16), GFP_KERNEL); + if (!effect->fifo->samples) + return -ENOMEM; + + rc = of_property_read_u16_array(node, "qcom,wf-fifo-data", + effect->fifo->samples, tmp); + if (rc < 0) { + dev_err(chip->dev, "Read wf-fifo-data failed, rc=%d\n", + rc); + return rc; + } + + effect->fifo->num_s = tmp; + } + + if (!effect->pattern && !effect->fifo) { + dev_err(chip->dev, "no pattern specified for effect %d\n", + effect->id); + return -EINVAL; + } + + if (effect->pattern) { + if (config->is_erm) + effect->pattern->play_rate_us = + DEFAULT_ERM_PLAY_RATE_US; + else + effect->pattern->play_rate_us = config->t_lra_us; + + rc = of_property_read_u32(node, "qcom,wf-pattern-period-us", + &tmp); + if (rc < 0) + dev_dbg(chip->dev, "Read qcom,wf-pattern-period-us failed, rc=%d\n", + rc); + else + effect->pattern->play_rate_us = tmp; + + if (effect->pattern->play_rate_us > TLRA_MAX_US) { + dev_err(chip->dev, "qcom,wf-pattern-period-us (%d) exceed the max value: %d\n", + effect->pattern->play_rate_us, + TLRA_MAX_US); + return -EINVAL; + } + + effect->pattern->play_length_us = + get_pattern_play_length_us(effect->pattern); + if (effect->pattern->play_length_us == -EINVAL) { + dev_err(chip->dev, "get pattern play length failed\n"); + return -EINVAL; + } + + if (effect->fifo) + dev_dbg(chip->dev, "Ignore FIFO data if pattern is specified!\n"); + + } else if (effect->fifo) { + effect->fifo->period_per_s = T_LRA; + rc = of_property_read_u32(node, "qcom,wf-fifo-period", &tmp); + if (tmp > F_48KHZ) { + dev_err(chip->dev, "FIFO playing period %d is not supported\n", + tmp); + return -EINVAL; + } else if (!rc) { + effect->fifo->period_per_s = tmp; + } + + effect->fifo->play_length_us = + get_fifo_play_length_us(effect->fifo, config->t_lra_us); + if (effect->fifo->play_length_us == -EINVAL) { + dev_err(chip->dev, "get fifo play length failed\n"); + return -EINVAL; + } + + effect->src = FIFO; + } + + effect->brake = devm_kzalloc(chip->dev, + sizeof(*effect->brake), GFP_KERNEL); + if (!effect->brake) + return -ENOMEM; + + memcpy(effect->brake, &config->brake, sizeof(*effect->brake)); + + of_property_read_u32(node, "qcom,wf-brake-mode", &effect->brake->mode); + if (effect->brake->mode > AUTO_BRAKE) { + dev_err(chip->dev, "can't support brake mode: %d\n", + effect->brake->mode); + return -EINVAL; + } + + if (effect->brake->brake_wf == WF_SINE) { + of_property_read_u32(node, "qcom,wf-brake-sine-gain", + &effect->brake->sine_gain); + if (effect->brake->sine_gain > BRAKE_SINE_GAIN_X8) { + dev_err(chip->dev, "can't support brake sine gain: %d\n", + effect->brake->sine_gain); + return -EINVAL; + } + } + + effect->brake->disabled = + of_property_read_bool(node, "qcom,wf-brake-disable"); + tmp = of_property_count_u8_elems(node, "qcom,wf-brake-pattern"); + if (tmp > BRAKE_SAMPLE_COUNT) { + dev_err(chip->dev, "more than %d brake samples\n", + BRAKE_SAMPLE_COUNT); + return -EINVAL; + } + + if (tmp > 0) { + memset(effect->brake->samples, 0, + sizeof(u8) * BRAKE_SAMPLE_COUNT); + rc = of_property_read_u8_array(node, "qcom,wf-brake-pattern", + effect->brake->samples, tmp); + if (rc < 0) { + dev_err(chip->dev, "Read wf-brake-pattern failed, rc=%d\n", + rc); + return rc; + } + verify_brake_samples(effect->brake); + } else { + if (effect->brake->mode == OL_BRAKE || + effect->brake->mode == CL_BRAKE) + effect->brake->disabled = true; + } + + effect->brake->play_length_us = + get_brake_play_length_us(effect->brake, config->t_lra_us); + + if (config->is_erm) + return 0; + + /* LRA specific per-effect settings are parsed below */ + effect->auto_res_disable = of_property_read_bool(node, + "qcom,wf-auto-res-disable"); + + return 0; +} + +static int haptics_parse_effects_dt(struct haptics_chip *chip) +{ + struct device_node *node = chip->dev->of_node; + struct device_node *child; + int rc, i = 0, num; + + num = of_get_available_child_count(node); + if (num == 0) + return 0; + + chip->effects = devm_kcalloc(chip->dev, num, + sizeof(*chip->effects), GFP_KERNEL); + if (!chip->effects) + return -ENOMEM; + + for_each_available_child_of_node(node, child) { + rc = haptics_parse_per_effect_dt(chip, child, + &chip->effects[i]); + if (rc < 0) { + dev_err(chip->dev, "parse effect %d failed, rc=%d\n", + i); + of_node_put(child); + return rc; + } + i++; + } + + chip->effects_count = i; + __dump_effects(chip); + + return 0; +} + +static int haptics_parse_lra_dt(struct haptics_chip *chip) +{ + struct haptics_hw_config *config = &chip->config; + struct device_node *node = chip->dev->of_node; + int rc; + + rc = of_property_read_u32(node, "qcom,lra-period-us", + &config->t_lra_us); + if (rc < 0) { + dev_err(chip->dev, "Read T-LRA failed, rc=%d\n", rc); + return rc; + } + + if (config->t_lra_us > TLRA_MAX_US) { + dev_err(chip->dev, "qcom,lra-period-us (%d) exceed the max value: %d\n", + config->t_lra_us, TLRA_MAX_US); + return -EINVAL; + } + + config->drv_wf = WF_SINE; + of_property_read_u32(node, "qcom,drv-sig-shape", &config->drv_wf); + if (config->drv_wf >= WF_RESERVED) { + dev_err(chip->dev, "Can't support drive shape: %d\n", + config->drv_wf); + return -EINVAL; + } + + config->brake.brake_wf = WF_SINE; + of_property_read_u32(node, "qcom,brake-sig-shape", + &config->brake.brake_wf); + if (config->brake.brake_wf >= WF_RESERVED) { + dev_err(chip->dev, "Can't support brake shape: %d\n", + config->brake.brake_wf); + return -EINVAL; + } + + if (config->brake.brake_wf == WF_SINE) { + config->brake.sine_gain = BRAKE_SINE_GAIN_X1; + of_property_read_u32(node, "qcom,brake-sine-gain", + &config->brake.sine_gain); + if (config->brake.sine_gain > BRAKE_SINE_GAIN_X8) { + dev_err(chip->dev, "Can't support brake sine gain: %d\n", + config->brake.sine_gain); + return -EINVAL; + } + } + + return 0; +} + +static int haptics_parse_dt(struct haptics_chip *chip) +{ + struct haptics_hw_config *config = &chip->config; + struct device_node *node = chip->dev->of_node; + struct platform_device *pdev = to_platform_device(chip->dev); + const __be32 *addr; + int rc = 0, tmp; + + addr = of_get_address(node, 0, NULL, NULL); + if (!addr) { + dev_err(chip->dev, "Read HAPTICS_CFG address failed, rc = %d\n", + rc); + return rc; + } + + 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, rc = %d\n", + rc); + return rc; + } + + chip->ptn_addr_base = be32_to_cpu(*addr); + 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; + } + + config->vmax_mv = DEFAULT_VMAX_MV; + of_property_read_u32(node, "qcom,vmax-mv", &config->vmax_mv); + 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; + } + + config->brake.mode = AUTO_BRAKE; + of_property_read_u32(node, "qcom,brake-mode", &config->brake.mode); + if (config->brake.mode > AUTO_BRAKE) { + dev_err(chip->dev, "Can't support brake mode: %d\n", + config->brake.mode); + return -EINVAL; + } + + config->brake.disabled = + of_property_read_bool(node, "qcom,brake-disable"); + tmp = of_property_count_u8_elems(node, "qcom,brake-pattern"); + if (tmp > BRAKE_SAMPLE_COUNT) { + dev_err(chip->dev, "more than %d brake samples\n", + BRAKE_SAMPLE_COUNT); + return -EINVAL; + } + + if (tmp > 0) { + rc = of_property_read_u8_array(node, "qcom,brake-pattern", + config->brake.samples, tmp); + if (rc < 0) { + dev_err(chip->dev, "Read brake-pattern failed, rc=%d\n", + rc); + return rc; + } + verify_brake_samples(&config->brake); + } else { + if (config->brake.mode == OL_BRAKE || + config->brake.mode == CL_BRAKE) + config->brake.disabled = true; + } + + config->is_erm = of_property_read_bool(node, "qcom,use-erm"); + if (config->is_erm) { + config->drv_wf = WF_NO_MODULATION; + config->brake.brake_wf = WF_NO_MODULATION; + } else { + rc = haptics_parse_lra_dt(chip); + if (rc < 0) { + dev_err(chip->dev, "Parse device-tree for LRA failed, rc=%d\n", + rc); + return rc; + } + } + + config->preload_effect = -EINVAL; + rc = haptics_parse_effects_dt(chip); + if (rc < 0) { + dev_err(chip->dev, "Parse device-tree for effects failed, rc=%d\n", + rc); + return rc; + } + + return 0; +} + +static int haptics_probe(struct platform_device *pdev) +{ + struct haptics_chip *chip; + struct input_dev *input_dev; + struct ff_device *ff_dev; + int rc, count; + + 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->dev = &pdev->dev; + chip->regmap = dev_get_regmap(chip->dev->parent, NULL); + if (!chip->regmap) { + dev_err(chip->dev, "Get regmap failed\n"); + return -ENXIO; + } + + rc = haptics_parse_dt(chip); + if (rc < 0) { + dev_err(chip->dev, "Parse device-tree failed, rc = %d\n", rc); + return rc; + } + + rc = haptics_hw_init(chip); + if (rc < 0) { + dev_err(chip->dev, "Initialize HW failed, rc = %d\n", rc); + return rc; + } + + rc = devm_request_threaded_irq(chip->dev, chip->fifo_empty_irq, + NULL, fifo_empty_irq_handler, + IRQF_ONESHOT, "fifo-empty", chip); + if (rc < 0) { + dev_err(chip->dev, "request fifo-empty IRQ failed, rc=%d\n", + rc); + return rc; + } + + disable_irq_nosync(chip->fifo_empty_irq); + chip->fifo_empty_irq_en = false; + + init_completion(&chip->play.fifo_status.fifo_ready); + atomic_set(&chip->play.fifo_status.is_busy, 0); + atomic_set(&chip->play.fifo_status.written_done, 0); + INIT_WORK(&chip->fifo_work, update_fifo_work); + input_dev->name = "qcom-hv-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 < MAX_EFFECT_COUNT) + count = chip->effects_count + 1; + else + count = MAX_EFFECT_COUNT; + + rc = input_ff_create(input_dev, count); + if (rc < 0) { + dev_err(chip->dev, "create input FF device failed, rc=%d\n", + rc); + return rc; + } + + ff_dev = input_dev->ff; + ff_dev->upload = haptics_upload_effect; + ff_dev->playback = haptics_playback; + ff_dev->erase = haptics_erase; + ff_dev->set_gain = 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); + return 0; +destroy_ff: + input_ff_destroy(chip->input_dev); + return rc; +} + +static int haptics_remove(struct platform_device *pdev) +{ + struct haptics_chip *chip = dev_get_drvdata(&pdev->dev); + + input_ff_destroy(chip->input_dev); + dev_set_drvdata(chip->dev, NULL); + + return 0; +} + +static const struct of_device_id haptics_match_table[] = { + { .compatible = "qcom,hv-haptics" }, + { .compatible = "qcom,pm8350b-haptics" }, + {}, +}; + +static struct platform_driver haptics_driver = { + .driver = { + .name = "qcom-hv-haptics", + .of_match_table = haptics_match_table, + }, + .probe = haptics_probe, + .remove = haptics_remove, +}; +module_platform_driver(haptics_driver); + +MODULE_DESCRIPTION("Qualcomm Technologies, Inc. High-Voltage Haptics driver"); +MODULE_LICENSE("GPL v2"); From d42848ae195f549e1199228a096403b1999058f9 Mon Sep 17 00:00:00 2001 From: Fenglin Wu Date: Tue, 26 Nov 2019 15:03:25 +0800 Subject: [PATCH 02/76] input: qcom,hv-haptics: Add DT definitions Add DT definitions for a few properties that are used to specify haptics hardware configuration and vibration effect settings. Change-Id: Icd9da49e87eca9c865e7f6add5638842c20a3bd6 Signed-off-by: Fenglin Wu --- include/dt-bindings/input/qcom,hv-haptics.h | 37 +++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 include/dt-bindings/input/qcom,hv-haptics.h diff --git a/include/dt-bindings/input/qcom,hv-haptics.h b/include/dt-bindings/input/qcom,hv-haptics.h new file mode 100644 index 000000000000..b39c24d5ccb8 --- /dev/null +++ b/include/dt-bindings/input/qcom,hv-haptics.h @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2020 The Linux Foundation. All rights reserved. + */ + +/* definitions for drive waveform shape */ +#define WF_SQUARE 0 /* LRA only */ +#define WF_SINE 1 /* LRA only */ +#define WF_NO_MODULATION 2 /* ERM only */ + +/* definitions for brake mode */ +#define BRAKE_OPEN_LOOP 0 +#define BRAKE_CLOSE_LOOP 1 +#define BRAKE_PREDICTIVE 2 +#define BRAKE_AUTO 3 + +/* definitions for brake sine signal gain */ +#define BRAKE_SINE_GAIN_X1 0 +#define BRAKE_SINE_GAIN_X2 1 +#define BRAKE_SINE_GAIN_X4 2 +#define BRAKE_SINE_GAIN_X8 3 + +/* definitions for pattern sample period */ +#define S_PERIOD_T_LRA 0 +#define S_PERIOD_T_LRA_DIV_2 1 +#define S_PERIOD_T_LRA_DIV_4 2 +#define S_PERIOD_T_LRA_DIV_8 3 +#define S_PERIOD_T_LRA_X_2 4 +#define S_PERIOD_T_LRA_X_4 5 +#define S_PERIOD_T_LRA_X_8 6 +/* F_8KHZ to F_48KHZ periods can only be specified for FIFO based effects */ +#define S_PERIOD_F_8KHZ 8 +#define S_PERIOD_F_16HKZ 9 +#define S_PERIOD_F_24KHZ 10 +#define S_PERIOD_F_32KHZ 11 +#define S_PERIOD_F_44P1KHZ 12 +#define S_PERIOD_F_48KHZ 13 From 0460c5ea34690fa9e918443c3619718e57f8bdff Mon Sep 17 00:00:00 2001 From: Fenglin Wu Date: Sun, 19 Jan 2020 15:27:06 +0800 Subject: [PATCH 03/76] input: qti-hv-haptics: Add debugfs parameters to configure effects Add debugfs parameters to configure different settings per haptics effect which is useful for testing. Change-Id: Iace6dab1ca11cad20d5b44eaf81e704053bc3e0b Signed-off-by: Fenglin Wu --- drivers/input/misc/qcom-hv-haptics.c | 621 +++++++++++++++++++++++++++ 1 file changed, 621 insertions(+) diff --git a/drivers/input/misc/qcom-hv-haptics.c b/drivers/input/misc/qcom-hv-haptics.c index 5b8d9d57c306..f2de3443779d 100644 --- a/drivers/input/misc/qcom-hv-haptics.c +++ b/drivers/input/misc/qcom-hv-haptics.c @@ -4,6 +4,7 @@ */ #include +#include #include #include #include @@ -158,6 +159,7 @@ #define CHAR_PER_PATTERN_S 48 #define CHAR_PER_SAMPLE 8 #define CHAR_MSG_HEADER 16 +#define CHAR_BRAKE_MODE 24 #define HW_BRAKE_CYCLES 5 enum drv_sig_shape { @@ -323,6 +325,7 @@ struct haptics_chip { struct haptics_effect *effects; struct haptics_play_info play; struct work_struct fifo_work; + struct dentry *debugfs_dir; int fifo_empty_irq; u32 effects_count; u32 cfg_addr_base; @@ -1214,6 +1217,616 @@ static irqreturn_t fifo_empty_irq_handler(int irq, void *data) return IRQ_HANDLED; } +#ifdef CONFIG_DEBUG_FS +static int vmax_dbgfs_read(void *data, u64 *val) +{ + struct haptics_effect *effect = data; + + *val = effect->vmax_mv; + + return 0; +} + +static int vmax_dbgfs_write(void *data, u64 val) +{ + struct haptics_effect *effect = data; + + if (val > MAX_VMAX_MV) + val = MAX_VMAX_MV; + + effect->vmax_mv = (u32) val; + + return 0; +} +DEFINE_DEBUGFS_ATTRIBUTE(vmax_debugfs_ops, vmax_dbgfs_read, + vmax_dbgfs_write, "%llu\n"); + +static int auto_res_en_dbgfs_read(void *data, u64 *val) +{ + struct haptics_effect *effect = data; + + *val = !effect->auto_res_disable; + + return 0; +} + +static int auto_res_en_dbgfs_write(void *data, u64 val) +{ + struct haptics_effect *effect = data; + + effect->auto_res_disable = !val; + + return 0; +} +DEFINE_DEBUGFS_ATTRIBUTE(auto_res_en_debugfs_ops, auto_res_en_dbgfs_read, + auto_res_en_dbgfs_write, "%llu\n"); + +static ssize_t pattern_s_dbgfs_read(struct file *fp, char __user *buf, + size_t count, loff_t *ppos) +{ + struct haptics_effect *effect = fp->private_data; + u32 pos = 0, size = CHAR_PER_PATTERN_S * SAMPLES_PER_PATTERN; + char *str; + int i = 0, rc; + + if (!effect->pattern) + return 0; + + str = kzalloc(size, GFP_KERNEL); + if (!str) + return -ENOMEM; + + for (i = 0; i < SAMPLES_PER_PATTERN; i++) { + pos += scnprintf(str + pos, size - pos, "0x%03x ", + effect->pattern->samples[i].amplitude); + pos += scnprintf(str + pos, size - pos, "%s(0x%02x) ", + period_str[effect->pattern->samples[i].period], + effect->pattern->samples[i].period); + pos += scnprintf(str + pos, size - pos, "F_LRA_X2(%1d)\n", + effect->pattern->samples[i].f_lra_x2); + } + + rc = simple_read_from_buffer(buf, count, ppos, str, pos); + kfree(str); + + return rc; +} + +static ssize_t pattern_s_dbgfs_write(struct file *fp, + const char __user *buf, size_t count, loff_t *ppos) +{ + struct haptics_effect *effect = fp->private_data; + struct pattern_s patterns[SAMPLES_PER_PATTERN] = {{0, 0, 0},}; + char *str, *token; + u32 val, tmp[3 * SAMPLES_PER_PATTERN] = {0}; + int rc, i = 0, j = 0; + + if (count > CHAR_PER_PATTERN_S * SAMPLES_PER_PATTERN) + return -EINVAL; + + str = kzalloc(CHAR_PER_PATTERN_S * SAMPLES_PER_PATTERN + 1, GFP_KERNEL); + if (!str) + return -ENOMEM; + + rc = copy_from_user(str, buf, count); + if (rc > 0) { + rc = -EFAULT; + goto exit; + } + + str[count] = '\0'; + *ppos += count; + + while ((token = strsep((char **)&str, " ")) != NULL) { + rc = kstrtouint(token, 0, &val); + if (rc < 0) { + rc = -EINVAL; + goto exit; + } + + tmp[i++] = val; + } + + if (i % 3) + pr_warn("Tuple should be having 3 elements, discarding tuple %d\n", + i / 3); + + for (j = 0; j < i / 3; j++) { + if (tmp[3 * j] > 0x1ff || tmp[3 * j + 1] > T_LRA_X_8 || + tmp[3 * j + 2] > 1) { + pr_err("allowed tuples: [amplitude(<= 0x1ff) period(<=6(T_LRA_X_8)) f_lra_x2(0,1)]\n"); + rc = -EINVAL; + goto exit; + } + + patterns[j].amplitude = (u16)tmp[3 * j]; + patterns[j].period = (enum s_period)tmp[3 * j + 1]; + patterns[j].f_lra_x2 = !!tmp[3 * j + 2]; + } + + memcpy(effect->pattern->samples, patterns, + sizeof(effect->pattern->samples)); + + /* recalculate the play length */ + effect->pattern->play_length_us = + get_pattern_play_length_us(effect->pattern); + if (effect->pattern->play_length_us == -EINVAL) { + pr_err("get pattern play length failed\n"); + rc = -EINVAL; + goto exit; + } + + rc = count; +exit: + kfree(str); + return rc; +} + +static const struct file_operations pattern_s_dbgfs_ops = { + .read = pattern_s_dbgfs_read, + .write = pattern_s_dbgfs_write, + .open = simple_open, +}; + +static int pattern_play_rate_us_dbgfs_read(void *data, u64 *val) +{ + struct haptics_effect *effect = data; + + *val = effect->pattern->play_rate_us; + + return 0; +} + +static int pattern_play_rate_us_dbgfs_write(void *data, u64 val) +{ + struct haptics_effect *effect = data; + + if (val > TLRA_MAX_US) + val = TLRA_MAX_US; + + effect->pattern->play_rate_us = (u32)val; + /* recalculate the play length */ + effect->pattern->play_length_us = + get_pattern_play_length_us(effect->pattern); + if (effect->pattern->play_length_us == -EINVAL) { + pr_err("get pattern play length failed\n"); + return -EINVAL; + } + + return 0; +} +DEFINE_DEBUGFS_ATTRIBUTE(pattern_play_rate_dbgfs_ops, + pattern_play_rate_us_dbgfs_read, + pattern_play_rate_us_dbgfs_write, "%lld\n"); + +static ssize_t fifo_s_dbgfs_read(struct file *fp, + char __user *buf, size_t count, loff_t *ppos) +{ + struct haptics_effect *effect = fp->private_data; + struct fifo_cfg *fifo = effect->fifo; + char *kbuf; + int rc, i; + u32 size, pos = 0; + + size = CHAR_PER_SAMPLE * fifo->num_s + 1; + kbuf = kzalloc(size, GFP_KERNEL); + if (!kbuf) + return -ENOMEM; + + for (i = 0; i < fifo->num_s; i++) + pos += scnprintf(kbuf + pos, size - pos, + "0x%03x ", fifo->samples[i]); + + pos += scnprintf(kbuf + pos, size - pos, "%s", "\n"); + rc = simple_read_from_buffer(buf, count, ppos, kbuf, pos); + kfree(kbuf); + + return rc; +} + +static ssize_t fifo_s_dbgfs_write(struct file *fp, + const char __user *buf, size_t count, loff_t *ppos) +{ + struct haptics_effect *effect = fp->private_data; + struct fifo_cfg *fifo = effect->fifo; + char *kbuf, *token; + int rc, i = 0; + u32 val; + u16 *samples; + + kbuf = kzalloc(count + 1, GFP_KERNEL); + if (!kbuf) + return -ENOMEM; + + rc = copy_from_user(kbuf, buf, count); + if (rc > 0) { + rc = -EFAULT; + goto exit; + } + + kbuf[count] = '\0'; + *ppos += count; + + samples = kcalloc(fifo->num_s, sizeof(u16), GFP_KERNEL); + if (!samples) { + rc = -ENOMEM; + goto exit; + } + + while ((token = strsep(&kbuf, " ")) != NULL) { + rc = kstrtouint(token, 0, &val); + if (rc < 0) { + rc = -EINVAL; + goto exit2; + } + + if (val > 0x1ff) + val = 0x1ff; + + samples[i++] = (u16)val; + /* only support fifo pattern no longer than before */ + if (i >= fifo->num_s) + break; + } + + memcpy(fifo->samples, samples, sizeof(*fifo->samples) * fifo->num_s); + fifo->play_length_us = get_fifo_play_length_us(fifo, effect->t_lra_us); + if (fifo->play_length_us == -EINVAL) { + pr_err("get fifo play length failed\n"); + rc = -EINVAL; + goto exit2; + } + + rc = count; +exit2: + kfree(samples); +exit: + kfree(kbuf); + return rc; +} + +static const struct file_operations fifo_s_dbgfs_ops = { + .read = fifo_s_dbgfs_read, + .write = fifo_s_dbgfs_write, + .owner = THIS_MODULE, + .open = simple_open, +}; + +static int fifo_period_dbgfs_read(void *data, u64 *val) +{ + struct haptics_effect *effect = data; + + *val = effect->fifo->period_per_s; + + return 0; +} + +static int fifo_period_dbgfs_write(void *data, u64 val) +{ + struct haptics_effect *effect = data; + struct fifo_cfg *fifo = effect->fifo; + + if (val > F_48KHZ) + return -EINVAL; + + fifo->period_per_s = (enum s_period)val; + fifo->play_length_us = get_fifo_play_length_us(fifo, effect->t_lra_us); + if (fifo->play_length_us == -EINVAL) { + pr_err("get fifo play length failed\n"); + return -EINVAL; + } + + return 0; +} + +DEFINE_DEBUGFS_ATTRIBUTE(fifo_period_dbgfs_ops, + fifo_period_dbgfs_read, + fifo_period_dbgfs_write, "%lld\n"); + +static ssize_t brake_s_dbgfs_read(struct file *fp, + char __user *buf, size_t count, loff_t *ppos) +{ + struct haptics_effect *effect = fp->private_data; + struct brake_cfg *brake = effect->brake; + char *str; + int rc, i; + u32 size, pos = 0; + + size = CHAR_PER_SAMPLE * BRAKE_SAMPLE_COUNT + 1; + str = kzalloc(size, GFP_KERNEL); + if (!str) + return -ENOMEM; + + for (i = 0; i < BRAKE_SAMPLE_COUNT; i++) + pos += scnprintf(str + pos, size - pos, "0x%02x ", + brake->samples[i]); + + pos += scnprintf(str + pos, size - pos, "%s", "\n"); + rc = simple_read_from_buffer(buf, count, ppos, str, pos); + kfree(str); + + return rc; +} + +static ssize_t brake_s_dbgfs_write(struct file *fp, + const char __user *buf, size_t count, loff_t *ppos) +{ + struct haptics_effect *effect = fp->private_data; + struct brake_cfg *brake = effect->brake; + char *str, *token; + int rc, i = 0; + u32 val; + u8 samples[BRAKE_SAMPLE_COUNT] = {0}; + + if (count > CHAR_PER_SAMPLE * BRAKE_SAMPLE_COUNT) + return -EINVAL; + + str = kzalloc(CHAR_PER_SAMPLE * BRAKE_SAMPLE_COUNT + 1, GFP_KERNEL); + if (!str) + return -ENOMEM; + + rc = copy_from_user(str, buf, count); + if (rc > 0) { + rc = -EFAULT; + goto exit; + } + + str[count] = '\0'; + *ppos += count; + + while ((token = strsep((char **)&str, " ")) != NULL) { + rc = kstrtouint(token, 0, &val); + if (rc < 0) { + rc = -EINVAL; + goto exit; + } + + if (val > 0xff) + val = 0xff; + + samples[i++] = (u8)val; + if (i >= BRAKE_SAMPLE_COUNT) + break; + } + + memcpy(brake->samples, samples, BRAKE_SAMPLE_COUNT); + verify_brake_samples(brake); + brake->play_length_us = + get_brake_play_length_us(brake, effect->t_lra_us); + + rc = count; +exit: + kfree(str); + return rc; +} + +static const struct file_operations brake_s_dbgfs_ops = { + .read = brake_s_dbgfs_read, + .write = brake_s_dbgfs_write, + .open = simple_open, +}; + +static ssize_t brake_mode_dbgfs_read(struct file *fp, + char __user *buf, size_t count, loff_t *ppos) +{ + struct haptics_effect *effect = fp->private_data; + struct brake_cfg *brake = effect->brake; + char str[CHAR_BRAKE_MODE] = {0}; + u32 size; + int rc; + + size = scnprintf(str, ARRAY_SIZE(str), "%s\n", brake_str[brake->mode]); + rc = simple_read_from_buffer(buf, count, ppos, str, size); + + return rc; +} + +static ssize_t brake_mode_dbgfs_write(struct file *fp, + const char __user *buf, size_t count, loff_t *ppos) +{ + struct haptics_effect *effect = fp->private_data; + struct brake_cfg *brake = effect->brake; + char *kbuf; + int rc; + + kbuf = kzalloc(count + 1, GFP_KERNEL); + if (!kbuf) + return -ENOMEM; + + rc = copy_from_user(kbuf, buf, count); + if (rc > 0) { + rc = -EFAULT; + goto exit; + } + + kbuf[count] = '\0'; + *ppos += count; + rc = count; + if (strcmp(kbuf, "open-loop") == 0) { + brake->mode = OL_BRAKE; + } else if (strcmp(kbuf, "close-loop") == 0) { + brake->mode = CL_BRAKE; + } else if (strcmp(kbuf, "predictive") == 0) { + brake->mode = PREDICT_BRAKE; + } else if (strcmp(kbuf, "auto") == 0) { + brake->mode = AUTO_BRAKE; + } else { + pr_err("%s brake mode is not supported\n", kbuf); + rc = -EINVAL; + } + +exit: + kfree(kbuf); + return rc; +} + +static const struct file_operations brake_mode_dbgfs_ops = { + .read = brake_mode_dbgfs_read, + .write = brake_mode_dbgfs_write, + .open = simple_open, +}; + +static int preload_effect_idx_dbgfs_read(void *data, u64 *val) +{ + struct haptics_chip *chip = data; + + *val = chip->config.preload_effect; + + return 0; +} + +static int preload_effect_idx_dbgfs_write(void *data, u64 val) +{ + struct haptics_chip *chip = data; + struct haptics_effect *new, *old; + int rc, i; + + for (i = 0; i < chip->effects_count; i++) + if (chip->effects[i].id == val) + break; + + if (i == chip->effects_count) + return -EINVAL; + + new = &chip->effects[i]; + + for (i = 0; i < chip->effects_count; i++) + if (chip->effects[i].id == chip->config.preload_effect) + break; + + old = &chip->effects[i]; + + chip->config.preload_effect = (u32)val; + + new->pattern->preload = true; + new->src = PATTERN2; + rc = haptics_set_pattern(chip, new->pattern, new->src); + if (rc < 0) + return rc; + + old->src = PATTERN1; + old->pattern->preload = false; + + return 0; +} + +DEFINE_DEBUGFS_ATTRIBUTE(preload_effect_idx_dbgfs_ops, + preload_effect_idx_dbgfs_read, + preload_effect_idx_dbgfs_write, "%lld\n"); + +static int haptics_add_effects_debugfs(struct haptics_effect *effect, + struct dentry *dir) +{ + struct dentry *file, *pattern_dir, *fifo_dir, *brake_dir; + + file = debugfs_create_file_unsafe("vmax_mv", 0644, dir, + effect, &vmax_debugfs_ops); + if (!file) + return -ENOMEM; + + file = debugfs_create_file_unsafe("lra_auto_res_en", 0644, dir, + effect, &auto_res_en_debugfs_ops); + if (!file) + return -ENOMEM; + + /* effect can have either pattern or FIFO */ + if (effect->pattern) { + pattern_dir = debugfs_create_dir("pattern", dir); + if (!pattern_dir) + return -ENOMEM; + + file = debugfs_create_file("samples", 0644, pattern_dir, + effect, &pattern_s_dbgfs_ops); + if (!file) + return -ENOMEM; + + file = debugfs_create_file_unsafe("play_rate_us", 0644, + pattern_dir, effect, + &pattern_play_rate_dbgfs_ops); + if (!file) + return -ENOMEM; + } else if (effect->fifo) { + fifo_dir = debugfs_create_dir("fifo", dir); + if (!fifo_dir) + return -ENOMEM; + + file = debugfs_create_file("samples", 0644, fifo_dir, + effect, &fifo_s_dbgfs_ops); + if (!file) + return -ENOMEM; + + file = debugfs_create_file_unsafe("period", 0644, fifo_dir, + effect, &fifo_period_dbgfs_ops); + if (!file) + return -ENOMEM; + } + + if (effect->brake) { + brake_dir = debugfs_create_dir("brake", dir); + if (!brake_dir) + return -ENOMEM; + + file = debugfs_create_file("samples", 0644, brake_dir, + effect, &brake_s_dbgfs_ops); + if (!file) + return -ENOMEM; + + file = debugfs_create_file("mode", 0644, brake_dir, + effect, &brake_mode_dbgfs_ops); + if (!file) + return -ENOMEM; + } + + return 0; +} + +#define EFFECT_NAME_SIZE 12 +static int haptics_create_debugfs(struct haptics_chip *chip) +{ + struct dentry *hap_dir, *effect_dir, *file; + char str[EFFECT_NAME_SIZE] = {0}; + int rc, i; + + hap_dir = debugfs_create_dir("haptics", NULL); + if (!hap_dir) { + dev_err(chip->dev, "create haptics debugfs directory failed\n"); + return -ENOMEM; + } + + for (i = 0; i < chip->effects_count; i++) { + scnprintf(str, ARRAY_SIZE(str), "effect%d", + chip->effects[i].id); + effect_dir = debugfs_create_dir(str, hap_dir); + if (!effect_dir) { + dev_err(chip->dev, "create %s debugfs directory failed\n", + str); + rc = -ENOMEM; + goto exit; + } + + rc = haptics_add_effects_debugfs(&chip->effects[i], effect_dir); + if (rc < 0) { + rc = -ENOMEM; + goto exit; + } + } + + file = debugfs_create_file_unsafe("preload_effect_idx", 0644, hap_dir, + chip, &preload_effect_idx_dbgfs_ops); + if (!file) { + rc = -ENOMEM; + goto exit; + } + + chip->debugfs_dir = hap_dir; + return 0; + +exit: + debugfs_remove_recursive(hap_dir); + return rc; +} +#endif + static int haptics_parse_per_effect_dt(struct haptics_chip *chip, struct device_node *node, struct haptics_effect *effect) { @@ -1710,6 +2323,11 @@ static int haptics_probe(struct platform_device *pdev) } dev_set_drvdata(chip->dev, chip); +#ifdef CONFIG_DEBUG_FS + rc = haptics_create_debugfs(chip); + if (rc < 0) + dev_err(chip->dev, "Creating debugfs failed, rc=%d\n", rc); +#endif return 0; destroy_ff: input_ff_destroy(chip->input_dev); @@ -1720,6 +2338,9 @@ static int haptics_remove(struct platform_device *pdev) { struct haptics_chip *chip = dev_get_drvdata(&pdev->dev); +#ifdef CONFIG_DEBUG_FS + debugfs_remove_recursive(chip->debugfs_dir); +#endif input_ff_destroy(chip->input_dev); dev_set_drvdata(chip->dev, NULL); From 9f3a5f2187da077f3644e85e33ab3a4e5f94e076 Mon Sep 17 00:00:00 2001 From: Fenglin Wu Date: Thu, 6 Feb 2020 13:36:36 +0800 Subject: [PATCH 04/76] input: qcom-hv-haptics: correct pattern source checking logic The pattern source check before loading predefined effect is incorrect. Correct it. Change-Id: I8026f6ee24ce17dfe43bc514eeb526300af99746 Signed-off-by: Fenglin Wu --- drivers/input/misc/qcom-hv-haptics.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/input/misc/qcom-hv-haptics.c b/drivers/input/misc/qcom-hv-haptics.c index f2de3443779d..7b4283d757bc 100644 --- a/drivers/input/misc/qcom-hv-haptics.c +++ b/drivers/input/misc/qcom-hv-haptics.c @@ -884,8 +884,8 @@ static int haptics_load_predefined_effect(struct haptics_chip *chip, return rc; play->pattern_src = play->effect->src; - if (play->pattern_src != PATTERN1 || - play->pattern_src != PATTERN2 || + if (play->pattern_src != PATTERN1 && + play->pattern_src != PATTERN2 && play->pattern_src != FIFO) { dev_err(chip->dev, "pattern src %d can't be used for predefined effect\n", play->pattern_src); From 0bfa6b4fc7e5f29294697cbf3ebd25d095586091 Mon Sep 17 00:00:00 2001 From: Fenglin Wu Date: Fri, 14 Feb 2020 12:43:33 +0800 Subject: [PATCH 05/76] input: qcom-hv-haptics: ignore parsing non-effect subnodes Only parse effect subnodes which have qcom,effect-id property. Change-Id: I70b2c1401bbf7d5238de818ac5b6ed55943a19f6 Signed-off-by: Fenglin Wu --- drivers/input/misc/qcom-hv-haptics.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/input/misc/qcom-hv-haptics.c b/drivers/input/misc/qcom-hv-haptics.c index 7b4283d757bc..a287ba0b8e2f 100644 --- a/drivers/input/misc/qcom-hv-haptics.c +++ b/drivers/input/misc/qcom-hv-haptics.c @@ -2064,9 +2064,12 @@ static int haptics_parse_effects_dt(struct haptics_chip *chip) { struct device_node *node = chip->dev->of_node; struct device_node *child; - int rc, i = 0, num; + int rc, i = 0, num = 0; - num = of_get_available_child_count(node); + for_each_available_child_of_node(node, child) { + if (of_find_property(child, "qcom,effect-id", NULL)) + num++; + } if (num == 0) return 0; @@ -2076,6 +2079,9 @@ static int haptics_parse_effects_dt(struct haptics_chip *chip) return -ENOMEM; for_each_available_child_of_node(node, child) { + if (!of_find_property(child, "qcom,effect-id", NULL)) + continue; + rc = haptics_parse_per_effect_dt(chip, child, &chip->effects[i]); if (rc < 0) { From 8f9d499001859fa8a5ac8f2b3eb2f62509f6fd68 Mon Sep 17 00:00:00 2001 From: Fenglin Wu Date: Thu, 13 Feb 2020 14:28:01 +0800 Subject: [PATCH 06/76] input: qcom-hv-haptics: Add a regulator device to control SWR slave Add a regulator device for swr-haptics driver to control the reset behavior of the SWR slave in haptics module. Change-Id: I810428e7f8aad3ff73a31b20067d434b3ac01da2 Signed-off-by: Fenglin Wu --- drivers/input/misc/qcom-hv-haptics.c | 105 ++++++++++++++++++++++++++- 1 file changed, 103 insertions(+), 2 deletions(-) diff --git a/drivers/input/misc/qcom-hv-haptics.c b/drivers/input/misc/qcom-hv-haptics.c index a287ba0b8e2f..dad901fdb42f 100644 --- a/drivers/input/misc/qcom-hv-haptics.c +++ b/drivers/input/misc/qcom-hv-haptics.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -61,8 +62,8 @@ #define HAP_CFG_SWR_ACCESS_REG 0x4E #define SWR_PAT_CFG_EN_BIT BIT(7) -#define SWR_PAT_INPUT_EN BIT(6) -#define SWR_PAT_RES_N BIT(5) +#define SWR_PAT_INPUT_EN_BIT BIT(6) +#define SWR_PAT_RES_N_BIT BIT(5) #define HAP_CFG_BRAKE_MODE_CFG_REG 0x50 #define BRAKE_MODE_MASK GENMASK(7, 6) @@ -326,11 +327,13 @@ struct haptics_chip { struct haptics_play_info play; struct work_struct fifo_work; struct dentry *debugfs_dir; + struct regulator_dev *swr_slave_rdev; int fifo_empty_irq; u32 effects_count; u32 cfg_addr_base; u32 ptn_addr_base; bool fifo_empty_irq_en; + bool swr_slave_enabled; }; static int haptics_read(struct haptics_chip *chip, @@ -2243,6 +2246,97 @@ static int haptics_parse_dt(struct haptics_chip *chip) return 0; } +static int swr_slave_reg_enable(struct regulator_dev *rdev) +{ + struct haptics_chip *chip = rdev_get_drvdata(rdev); + int rc; + u8 mask = SWR_PAT_INPUT_EN_BIT | SWR_PAT_RES_N_BIT | SWR_PAT_CFG_EN_BIT; + + rc = haptics_masked_write(chip, chip->cfg_addr_base, + HAP_CFG_SWR_ACCESS_REG, mask, mask); + if (rc < 0) { + dev_err(chip->dev, "Failed to enable SWR_PAT, rc=%d\n", + rc); + return rc; + } + + chip->swr_slave_enabled = true; + return 0; +} + +static int swr_slave_reg_disable(struct regulator_dev *rdev) +{ + struct haptics_chip *chip = rdev_get_drvdata(rdev); + int rc; + + rc = haptics_masked_write(chip, chip->cfg_addr_base, + HAP_CFG_SWR_ACCESS_REG, + SWR_PAT_INPUT_EN_BIT | SWR_PAT_RES_N_BIT | + SWR_PAT_CFG_EN_BIT, 0); + if (rc < 0) { + dev_err(chip->dev, "Failed to disable SWR_PAT, rc=%d\n", rc); + return rc; + } + + chip->swr_slave_enabled = false; + return 0; +} + +static int swr_slave_reg_is_enabled(struct regulator_dev *rdev) +{ + struct haptics_chip *chip = rdev_get_drvdata(rdev); + + return chip->swr_slave_enabled; +} + +static const struct regulator_ops swr_slave_reg_ops = { + .enable = swr_slave_reg_enable, + .disable = swr_slave_reg_disable, + .is_enabled = swr_slave_reg_is_enabled, +}; + +static struct regulator_desc swr_slave_reg_rdesc = { + .owner = THIS_MODULE, + .of_match = "qcom,hap-swr-slave-reg", + .name = "hap-swr-slave-reg", + .type = REGULATOR_VOLTAGE, + .ops = &swr_slave_reg_ops, +}; + +static int haptics_init_swr_slave_regulator(struct haptics_chip *chip) +{ + struct regulator_config cfg = {}; + u8 val, mask; + int rc = 0; + + rc = haptics_read(chip, chip->cfg_addr_base, + HAP_CFG_SWR_ACCESS_REG, &val, 1); + if (rc < 0) { + dev_err(chip->dev, "Failed to read SWR_ACCESS, rc=%d\n", + rc); + return rc; + } + + mask = SWR_PAT_INPUT_EN_BIT | SWR_PAT_RES_N_BIT | SWR_PAT_CFG_EN_BIT; + val &= mask; + chip->swr_slave_enabled = ((val == mask) ? true : false); + + cfg.dev = chip->dev; + cfg.driver_data = chip; + chip->swr_slave_rdev = devm_regulator_register(chip->dev, + &swr_slave_reg_rdesc, &cfg); + if (IS_ERR(chip->swr_slave_rdev)) { + rc = PTR_ERR(chip->swr_slave_rdev); + chip->swr_slave_rdev = NULL; + + if (rc != -EPROBE_DEFER) + dev_err(chip->dev, "register swr-slave-reg regulator failed, rc=%d\n", + rc); + } + + return rc; +} + static int haptics_probe(struct platform_device *pdev) { struct haptics_chip *chip; @@ -2277,6 +2371,13 @@ static int haptics_probe(struct platform_device *pdev) return rc; } + rc = haptics_init_swr_slave_regulator(chip); + if (rc < 0) { + dev_err(chip->dev, "Initialize swr slave regulator failed, rc = %d\n", + rc); + return rc; + } + rc = devm_request_threaded_irq(chip->dev, chip->fifo_empty_irq, NULL, fifo_empty_irq_handler, IRQF_ONESHOT, "fifo-empty", chip); From 08e6fb57aacb8e62bfc6a54d58b36b0a4b0567f3 Mon Sep 17 00:00:00 2001 From: Fenglin Wu Date: Mon, 2 Mar 2020 16:03:49 +0800 Subject: [PATCH 07/76] input: misc: qcom-hv-haptics: Add support for V2 HW module HV-haptics module with V2 revision supports 640 bytes FIFO memory and each byte contains a FIFO sample. There are 4 adjacent SPMI registers which can be used for filling 4 FIFO samples together by using SPMI burst writes and also a single register for filling FIFO samples while the samples are less than 4. Also update the logic of writing FIFO samples: Check the available FIFO memory first and then write FIFO with samples no more than the available FIFO memory space. With this new logic, it's not necessory to read FIFO_READY_STS register before writing FIFO samples and this helps on reducing FIFO writing latency. Change-Id: If65a81327dfd2c6af0d58055c0ae93ad6c1dcba0 Signed-off-by: Fenglin Wu --- drivers/input/misc/qcom-hv-haptics.c | 308 +++++++++++++++++++++------ 1 file changed, 241 insertions(+), 67 deletions(-) diff --git a/drivers/input/misc/qcom-hv-haptics.c b/drivers/input/misc/qcom-hv-haptics.c index dad901fdb42f..3175a2f4c2c6 100644 --- a/drivers/input/misc/qcom-hv-haptics.c +++ b/drivers/input/misc/qcom-hv-haptics.c @@ -27,10 +27,16 @@ /* STATUS_DATA_MSB definitions while MOD_STATUS_SEL is 0 */ #define AUTO_RES_CAL_DONE_BIT BIT(5) #define CAL_TLRA_CL_STS_MSB_MASK GENMASK(4, 0) +/* STATUS_DATA_MSB definition in V1 while MOD_STATUS_SEL is 5 */ +#define FIFO_REAL_TIME_FILL_STATUS_MASK_V1 GENMASK(6, 0) +/* STATUS DATA_MSB definition in V2 while MOD_STATUS_SEL is 5 */ +#define FIFO_REAL_TIME_FILL_STATUS_MSB_MASK_V2 GENMASK(1, 0) #define HAP_CFG_STATUS_DATA_LSB_REG 0x0A -/* STATUS_DATA_MSB definitions while MOD_STATUS_SEL is 0 */ +/* STATUS_DATA_MSB definition while MOD_STATUS_SEL is 0 */ #define CAL_TLRA_CL_STS_LSB_MASK GENMASK(7, 0) +/* STATUS_DATA_LSB definition in V2 while MOD_STATUS_SEL is 5 */ +#define FIFO_REAL_TIME_FILL_STATUS_LSB_MASK_V2 GENMASK(7, 0) #define HAP_CFG_FAULT_STATUS_REG 0x0C #define SC_FLAG_BIT BIT(2) @@ -109,6 +115,17 @@ #define VMAX_HDRM_MASK GENMASK(6, 0) #define VMAX_HDRM_STEP_MV 50 +#define HAP_CFG_MOD_STATUS_SEL_REG 0x70 +#define MOD_STATUS_SEL_FIFO_FILL_STATUS_VAL 5 + +#define HAP_CFG_MOD_STATUS_XT_V2_REG 0x71 +#define MOD_STATUS_XT_V2_FIFO_FILL_STATUS_VAL 0x80 + +/* version register definitions for HAPTICS_PATTERN module */ +#define HAP_PTN_REVISION2_REG 0x01 +#define HAP_PTN_V1 0x1 +#define HAP_PTN_V2 0x2 + /* status register definition for HAPTICS_PATTERN module */ #define HAP_PTN_FIFO_READY_STS_REG 0x08 #define FIFO_READY_BIT BIT(0) @@ -116,19 +133,32 @@ #define HAP_PTN_NUM_PAT_REG 0x09 /* config register definition for HAPTICS_PATTERN module */ -#define HAP_PTN_FIFO_DIN_MSB_REG 0x20 -#define HAP_PTN_FIFO_DIN_MSB_BIT BIT(0) -#define HAP_PTN_FIFO_DIN_LSB_REG 0x21 -#define HAP_PTN_FIFO_DIN_LSB_MASK GENMASK(7, 0) +/* FIFO configuration registers in V1 chip */ +#define HAP_PTN_V1_FIFO_DIN_MSB_REG 0x20 +#define HAP_PTN_V1_FIFO_DIN_MSB_BIT BIT(0) +#define HAP_PTN_V1_FIFO_DIN_LSB_REG 0x21 +#define HAP_PTN_V1_FIFO_DIN_LSB_MASK GENMASK(7, 0) -#define HAP_PTN_FIFO_PLAY_RATE_REG 0x22 +#define HAP_PTN_V1_FIFO_PLAY_RATE_REG 0x22 #define FIFO_PLAY_RATE_MASK GENMASK(3, 0) -#define HAP_PTN_FIFO_EMPTY_CFG_REG 0x23 +#define HAP_PTN_V1_FIFO_EMPTY_CFG_REG 0x23 #define EMPTY_THRESH_MASK GENMASK(3, 0) +#define HAP_PTN_V1_FIFO_THRESH_LSB 4 -#define HAP_PTN_FIFO_DEPTH_CFG_REG 0x24 +#define HAP_PTN_V1_FIFO_DEPTH_CFG_REG 0x24 +/* FIFO configuration registers in V2 chip */ +#define HAP_PTN_V2_FIFO_DIN_0_REG 0x20 +#define HAP_PTN_V2_FIFO_DIN_NUM 4 + +#define HAP_PTN_V2_FIFO_PLAY_RATE_REG 0x24 +#define HAP_PTN_V2_FIFO_EMPTY_CFG_REG 0x2A +#define HAP_PTN_V2_FIFO_THRESH_LSB 40 +#define HAP_PTN_V2_FIFO_DEPTH_CFG_REG 0x2B +#define HAP_PTN_V2_FIFO_DIN_1B_REG 0x2C + +/* shared registers between V1 and V2 chips */ #define HAP_PTN_DIRECT_PLAY_REG 0x26 #define HAP_PTN_AUTORES_CAL_CFG_REG 0x28 @@ -152,10 +182,8 @@ /* constant parameters */ #define SAMPLES_PER_PATTERN 8 #define BRAKE_SAMPLE_COUNT 8 -#define MAX_FIFO_SAMPLES 104 #define DEFAULT_ERM_PLAY_RATE_US 5000 #define MAX_EFFECT_COUNT 64 -#define FIFO_EMPTY_THRESHOLD 48 #define FIFO_READY_TIMEOUT_MS 1000 #define CHAR_PER_PATTERN_S 48 #define CHAR_PER_SAMPLE 8 @@ -163,6 +191,11 @@ #define CHAR_BRAKE_MODE 24 #define HW_BRAKE_CYCLES 5 +#define MAX_FIFO_SAMPLES(chip) \ + ((chip)->ptn_revision == HAP_PTN_V1 ? 104 : 640) +#define FIFO_EMPTY_THRESHOLD(chip) \ + ((chip)->ptn_revision == HAP_PTN_V1 ? 48 : 280) + enum drv_sig_shape { WF_SQUARE, WF_SINE, @@ -267,7 +300,7 @@ struct pattern_cfg { }; struct fifo_cfg { - u16 *samples; + u8 *samples; u32 num_s; enum s_period period_per_s; u32 play_length_us; @@ -332,6 +365,7 @@ struct haptics_chip { u32 effects_count; u32 cfg_addr_base; u32 ptn_addr_base; + u8 ptn_revision; bool fifo_empty_irq_en; bool swr_slave_enabled; }; @@ -726,41 +760,30 @@ static int haptics_set_pattern(struct haptics_chip *chip, } -static int haptics_update_fifo_sample(struct haptics_chip *chip, u16 sample) +static int haptics_update_fifo_sample_v1(struct haptics_chip *chip, u8 sample) { int rc = 0; u8 val; - bool ready; - - rc = haptics_read(chip, chip->ptn_addr_base, - HAP_PTN_FIFO_READY_STS_REG, &val, 1); - if (rc < 0) { - dev_err(chip->dev, "read FIFO_READY_STS failed, rc=%d\n", - rc); - return rc; - } - - ready = !!(val & FIFO_READY_BIT); - /* sleep no more than 10us if FIFO memory is not ready */ - if (!ready) - usleep_range(1, 10); /* * Fill FIFO_DIN registers to update FIFO memory, - * need to fill LSB first then MSB + * need to fill LSB first then MSB. + * The FIFO memory width in V1 chip is 9-bit so shift + * 1 bit to left on the 8-bit FIFO sample to achieve a + * 9-bit data and fill it into the FIFO_DIN registers. */ - val = sample & HAP_PTN_FIFO_DIN_LSB_MASK; + val = (sample << 1) & HAP_PTN_V1_FIFO_DIN_LSB_MASK; rc = haptics_write(chip, chip->ptn_addr_base, - HAP_PTN_FIFO_DIN_LSB_REG, &val, 1); + HAP_PTN_V1_FIFO_DIN_LSB_REG, &val, 1); if (rc < 0) { dev_err(chip->dev, "write FIFO LSB failed, rc=%d\n", rc); return rc; } - val = (sample >> 8) & HAP_PTN_FIFO_DIN_MSB_BIT; + val = (sample >> 7) & HAP_PTN_V1_FIFO_DIN_MSB_BIT; rc = haptics_write(chip, chip->ptn_addr_base, - HAP_PTN_FIFO_DIN_MSB_REG, &val, 1); + HAP_PTN_V1_FIFO_DIN_MSB_REG, &val, 1); if (rc < 0) { dev_err(chip->dev, "write FIFO MSB failed, rc=%d\n", rc); @@ -770,11 +793,127 @@ static int haptics_update_fifo_sample(struct haptics_chip *chip, u16 sample) return 0; } +static int haptics_update_fifo_sample_v2(struct haptics_chip *chip, + u8 *samples, u32 num) +{ + int rc, i; + + if (num > HAP_PTN_V2_FIFO_DIN_NUM) + return -EINVAL; + + if (num == HAP_PTN_V2_FIFO_DIN_NUM) { + rc = haptics_write(chip, chip->ptn_addr_base, + HAP_PTN_V2_FIFO_DIN_0_REG, samples, num); + if (rc < 0) { + dev_err(chip->dev, "bulk write FIFO_DIN failed, rc=%d\n", + rc); + return rc; + } + } else if (num < HAP_PTN_V2_FIFO_DIN_NUM) { + for (i = 0; i < num; i++) { + rc = haptics_write(chip, chip->ptn_addr_base, + HAP_PTN_V2_FIFO_DIN_1B_REG, + (samples + i), 1); + if (rc < 0) { + dev_err(chip->dev, "write FIFO_DIN_1B failed, rc=%d\n", + rc); + return rc; + } + } + } + + return 0; +} + +static int haptics_get_available_fifo_memory(struct haptics_chip *chip) +{ + int rc; + u8 val[2]; + u32 fill, available; + + val[0] = MOD_STATUS_SEL_FIFO_FILL_STATUS_VAL; + if (chip->ptn_revision == HAP_PTN_V1) { + rc = haptics_write(chip, chip->cfg_addr_base, + HAP_CFG_MOD_STATUS_SEL_REG, val, 1); + if (rc < 0) + return rc; + + rc = haptics_read(chip, chip->cfg_addr_base, + HAP_CFG_STATUS_DATA_MSB_REG, val, 1); + if (rc < 0) + return rc; + + fill = val[0] & FIFO_REAL_TIME_FILL_STATUS_MASK_V1; + } else { + val[1] = MOD_STATUS_XT_V2_FIFO_FILL_STATUS_VAL; + rc = haptics_write(chip, chip->cfg_addr_base, + HAP_CFG_MOD_STATUS_SEL_REG, val, 2); + if (rc < 0) + return rc; + + rc = haptics_read(chip, chip->cfg_addr_base, + HAP_CFG_STATUS_DATA_MSB_REG, val, 2); + if (rc < 0) + return rc; + + fill = ((val[0] & FIFO_REAL_TIME_FILL_STATUS_MSB_MASK_V2) + << 8) | val[1]; + } + + if (fill > MAX_FIFO_SAMPLES(chip)) { + dev_err(chip->dev, "Filled FIFO number %d exceed the max %d\n", + fill, MAX_FIFO_SAMPLES(chip)); + return -EINVAL; + } else if (fill == MAX_FIFO_SAMPLES(chip)) { + dev_err(chip->dev, "no FIFO space available\n"); + return -EBUSY; + } + + available = MAX_FIFO_SAMPLES(chip) - fill; + dev_dbg(chip->dev, "Available FIFO memory: %d bytes\n", available); + return available; +} + +static int haptics_update_fifo_samples(struct haptics_chip *chip, + u8 *samples, u32 length) +{ + int rc, count, i; + + if (chip->ptn_revision == HAP_PTN_V1) { + for (i = 0; i < length; i++) { + rc = haptics_update_fifo_sample_v1(chip, samples[i]); + if (rc < 0) + return rc; + } + } else { + count = length / HAP_PTN_V2_FIFO_DIN_NUM; + for (i = 0; i < count; i++) { + rc = haptics_update_fifo_sample_v2(chip, + samples, HAP_PTN_V2_FIFO_DIN_NUM); + if (rc < 0) + return rc; + + samples += HAP_PTN_V2_FIFO_DIN_NUM; + } + + if (length % HAP_PTN_V2_FIFO_DIN_NUM) { + rc = haptics_update_fifo_sample_v2(chip, + samples, + length % HAP_PTN_V2_FIFO_DIN_NUM); + if (rc < 0) + return rc; + } + } + + return 0; +} + static int haptics_set_fifo(struct haptics_chip *chip, struct fifo_cfg *fifo) { struct fifo_play_status *status = &chip->play.fifo_status; u32 num, fifo_thresh; - int rc, i; + int rc, thresh_per_bit, available; + u8 reg; if (atomic_read(&status->is_busy) == 1) { dev_err(chip->dev, "FIFO is busy\n"); @@ -782,9 +921,10 @@ static int haptics_set_fifo(struct haptics_chip *chip, struct fifo_cfg *fifo) } /* Configure FIFO play rate */ + reg = (chip->ptn_revision == HAP_PTN_V1) ? + HAP_PTN_V1_FIFO_PLAY_RATE_REG : HAP_PTN_V2_FIFO_PLAY_RATE_REG; rc = haptics_masked_write(chip, chip->ptn_addr_base, - HAP_PTN_FIFO_PLAY_RATE_REG, - FIFO_PLAY_RATE_MASK, fifo->period_per_s); + reg, FIFO_PLAY_RATE_MASK, fifo->period_per_s); if (rc < 0) return rc; @@ -793,18 +933,19 @@ static int haptics_set_fifo(struct haptics_chip *chip, struct fifo_cfg *fifo) /* * Write the 1st set of the data into FIFO if there are - * more than 104 samples, the rest will be written if - * any FIFO memory is available after playing. + * more than MAX_FIFO_SAMPLES samples, the rest will be + * written if any FIFO memory is available after playing. */ - if (fifo->num_s > MAX_FIFO_SAMPLES) - num = MAX_FIFO_SAMPLES; - else - num = fifo->num_s; + num = min_t(u32, fifo->num_s, MAX_FIFO_SAMPLES(chip)); + available = haptics_get_available_fifo_memory(chip); + if (available < 0) + return available; - for (i = 0; i < num; i++) { - rc = haptics_update_fifo_sample(chip, fifo->samples[i]); - if (rc < 0) - return rc; + num = min_t(u32, available, num); + rc = haptics_update_fifo_samples(chip, fifo->samples, num); + if (rc < 0) { + dev_err(chip->dev, "write FIFO samples failed, rc=%d\n", rc); + return rc; } atomic_set(&status->is_busy, 1); @@ -814,7 +955,7 @@ static int haptics_set_fifo(struct haptics_chip *chip, struct fifo_cfg *fifo) atomic_set(&status->written_done, 1); } else { reinit_completion(&status->fifo_ready); - fifo_thresh = FIFO_EMPTY_THRESHOLD; + fifo_thresh = FIFO_EMPTY_THRESHOLD(chip); } /* @@ -822,9 +963,12 @@ static int haptics_set_fifo(struct haptics_chip *chip, struct fifo_cfg *fifo) * more data can be written into FIFO memory after * the IRQ is triggered. */ + reg = (chip->ptn_revision == HAP_PTN_V1) ? + HAP_PTN_V1_FIFO_EMPTY_CFG_REG : HAP_PTN_V2_FIFO_EMPTY_CFG_REG; + thresh_per_bit = (chip->ptn_revision == HAP_PTN_V1) ? + HAP_PTN_V1_FIFO_THRESH_LSB : HAP_PTN_V2_FIFO_THRESH_LSB; rc = haptics_masked_write(chip, chip->ptn_addr_base, - HAP_PTN_FIFO_EMPTY_CFG_REG, EMPTY_THRESH_MASK, - fifo_thresh / 4); + reg, EMPTY_THRESH_MASK, fifo_thresh / thresh_per_bit); if (rc < 0) return rc; @@ -1145,11 +1289,12 @@ static void update_fifo_work(struct work_struct *work) struct haptics_chip, fifo_work); struct fifo_cfg *fifo = chip->play.effect->fifo; struct fifo_play_status *status = &chip->play.fifo_status; - u32 num, samples_written, samples_left; - int rc, i; + u32 samples_written, samples_left; + u8 *samples; + u8 reg; + int rc, num; samples_written = status->samples_written; - num = MAX_FIFO_SAMPLES - FIFO_EMPTY_THRESHOLD; samples_left = fifo->num_s - samples_written; while (samples_left > 0) { @@ -1161,17 +1306,23 @@ static void update_fifo_work(struct work_struct *work) return; } + num = haptics_get_available_fifo_memory(chip); + if (num < 0) + return; + if (samples_left <= num) num = samples_left; else reinit_completion(&status->fifo_ready); - /* Write more pattern data into FIFO memory */ - for (i = 0; i < num; i++) { - rc = haptics_update_fifo_sample(chip, - fifo->samples[samples_written + i]); - if (rc < 0) - return; + samples = fifo->samples + samples_written; + + /* Write more pattern data into FIFO memory. */ + rc = haptics_update_fifo_samples(chip, samples, num); + if (rc < 0) { + dev_err(chip->dev, "Update FIFO samples failed in fifo_work, rc=%d\n", + rc); + return; } samples_written += num; @@ -1187,9 +1338,10 @@ static void update_fifo_work(struct work_struct *work) */ dev_dbg(chip->dev, "FIFO programmed done\n"); atomic_set(&chip->play.fifo_status.written_done, 1); + reg = (chip->ptn_revision == HAP_PTN_V1) ? + HAP_PTN_V1_FIFO_EMPTY_CFG_REG : HAP_PTN_V2_FIFO_EMPTY_CFG_REG; rc = haptics_masked_write(chip, chip->ptn_addr_base, - HAP_PTN_FIFO_EMPTY_CFG_REG, - EMPTY_THRESH_MASK, 0); + reg, EMPTY_THRESH_MASK, 0); if (rc < 0) dev_err(chip->dev, "set FIFO empty threshold to 0 failed, rc=%d\n", rc); @@ -1435,7 +1587,7 @@ static ssize_t fifo_s_dbgfs_write(struct file *fp, char *kbuf, *token; int rc, i = 0; u32 val; - u16 *samples; + u8 *samples; kbuf = kzalloc(count + 1, GFP_KERNEL); if (!kbuf) @@ -1450,7 +1602,7 @@ static ssize_t fifo_s_dbgfs_write(struct file *fp, kbuf[count] = '\0'; *ppos += count; - samples = kcalloc(fifo->num_s, sizeof(u16), GFP_KERNEL); + samples = kcalloc(fifo->num_s, sizeof(*samples), GFP_KERNEL); if (!samples) { rc = -ENOMEM; goto exit; @@ -1463,16 +1615,16 @@ static ssize_t fifo_s_dbgfs_write(struct file *fp, goto exit2; } - if (val > 0x1ff) - val = 0x1ff; + if (val > 0xff) + val = 0xff; - samples[i++] = (u16)val; + samples[i++] = val; /* only support fifo pattern no longer than before */ if (i >= fifo->num_s) break; } - memcpy(fifo->samples, samples, sizeof(*fifo->samples) * fifo->num_s); + memcpy(fifo->samples, samples, fifo->num_s); fifo->play_length_us = get_fifo_play_length_us(fifo, effect->t_lra_us); if (fifo->play_length_us == -EINVAL) { pr_err("get fifo play length failed\n"); @@ -1918,7 +2070,7 @@ static int haptics_parse_per_effect_dt(struct haptics_chip *chip, } } - tmp = of_property_count_u16_elems(node, "qcom,wf-fifo-data"); + tmp = of_property_count_u8_elems(node, "qcom,wf-fifo-data"); if (tmp > 0) { effect->fifo = devm_kzalloc(chip->dev, sizeof(*effect->fifo), GFP_KERNEL); @@ -1926,11 +2078,11 @@ static int haptics_parse_per_effect_dt(struct haptics_chip *chip, return -ENOMEM; effect->fifo->samples = devm_kcalloc(chip->dev, - tmp, sizeof(u16), GFP_KERNEL); + tmp, sizeof(u8), GFP_KERNEL); if (!effect->fifo->samples) return -ENOMEM; - rc = of_property_read_u16_array(node, "qcom,wf-fifo-data", + rc = of_property_read_u8_array(node, "qcom,wf-fifo-data", effect->fifo->samples, tmp); if (rc < 0) { dev_err(chip->dev, "Read wf-fifo-data failed, rc=%d\n", @@ -2152,6 +2304,22 @@ static int haptics_parse_lra_dt(struct haptics_chip *chip) return 0; } +static int haptics_get_revision(struct haptics_chip *chip) +{ + int rc; + u8 val; + + rc = haptics_read(chip, chip->ptn_addr_base, + HAP_PTN_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); + return 0; +} + static int haptics_parse_dt(struct haptics_chip *chip) { struct haptics_hw_config *config = &chip->config; @@ -2176,6 +2344,12 @@ static int haptics_parse_dt(struct haptics_chip *chip) } chip->ptn_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; + } + 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"); From e64d358465f128c39b686885d9f66f400939c184 Mon Sep 17 00:00:00 2001 From: Fenglin Wu Date: Wed, 25 Mar 2020 20:23:56 +0800 Subject: [PATCH 08/76] input: qcom-hv-haptics: Add a property to specify FIFO empty threshold In FIFO mode playing, when the number of samples in the FIFO memory is less than the FIFO empty threshold, an interrupt is triggered for notifying the driver to refill the FIFO memory if there is still any samples pending to be played. FIFO empty threshold can be a tuning parameter for the driver to refill the FIFO memory timely before the hardware drains out the FIFO samples to avoid an intermittent pause in vibration effect. Add a DT property to specify FIFO empty threshold. Also, expose it under debugfs for modifying it runtime. Meanwhile, return proper values when creating debugfs nodes failed. Change-Id: I3ac9e6210173ab6878b4487959bc4b3fc3f20ab0 Signed-off-by: Fenglin Wu --- drivers/input/misc/qcom-hv-haptics.c | 90 ++++++++++++++++++---------- 1 file changed, 57 insertions(+), 33 deletions(-) diff --git a/drivers/input/misc/qcom-hv-haptics.c b/drivers/input/misc/qcom-hv-haptics.c index 3175a2f4c2c6..9f591023352f 100644 --- a/drivers/input/misc/qcom-hv-haptics.c +++ b/drivers/input/misc/qcom-hv-haptics.c @@ -347,6 +347,7 @@ struct haptics_hw_config { u32 vmax_mv; u32 t_lra_us; u32 preload_effect; + u32 fifo_empty_thresh; enum drv_sig_shape drv_wf; bool is_erm; }; @@ -955,7 +956,7 @@ static int haptics_set_fifo(struct haptics_chip *chip, struct fifo_cfg *fifo) atomic_set(&status->written_done, 1); } else { reinit_completion(&status->fifo_ready); - fifo_thresh = FIFO_EMPTY_THRESHOLD(chip); + fifo_thresh = chip->config.fifo_empty_thresh; } /* @@ -1876,60 +1877,60 @@ static int haptics_add_effects_debugfs(struct haptics_effect *effect, file = debugfs_create_file_unsafe("vmax_mv", 0644, dir, effect, &vmax_debugfs_ops); - if (!file) - return -ENOMEM; + if (IS_ERR(file)) + return PTR_ERR(file); file = debugfs_create_file_unsafe("lra_auto_res_en", 0644, dir, effect, &auto_res_en_debugfs_ops); - if (!file) - return -ENOMEM; + if (IS_ERR(file)) + return PTR_ERR(file); /* effect can have either pattern or FIFO */ if (effect->pattern) { pattern_dir = debugfs_create_dir("pattern", dir); - if (!pattern_dir) - return -ENOMEM; + if (IS_ERR(pattern_dir)) + return PTR_ERR(pattern_dir); file = debugfs_create_file("samples", 0644, pattern_dir, effect, &pattern_s_dbgfs_ops); - if (!file) - return -ENOMEM; + if (IS_ERR(file)) + return PTR_ERR(file); file = debugfs_create_file_unsafe("play_rate_us", 0644, pattern_dir, effect, &pattern_play_rate_dbgfs_ops); - if (!file) - return -ENOMEM; + if (IS_ERR(file)) + return PTR_ERR(file); } else if (effect->fifo) { fifo_dir = debugfs_create_dir("fifo", dir); - if (!fifo_dir) - return -ENOMEM; + if (IS_ERR(fifo_dir)) + return PTR_ERR(fifo_dir); file = debugfs_create_file("samples", 0644, fifo_dir, effect, &fifo_s_dbgfs_ops); - if (!file) - return -ENOMEM; + if (IS_ERR(file)) + return PTR_ERR(file); file = debugfs_create_file_unsafe("period", 0644, fifo_dir, effect, &fifo_period_dbgfs_ops); - if (!file) - return -ENOMEM; + if (IS_ERR(file)) + return PTR_ERR(file); } if (effect->brake) { brake_dir = debugfs_create_dir("brake", dir); - if (!brake_dir) - return -ENOMEM; + if (IS_ERR(brake_dir)) + return PTR_ERR(brake_dir); file = debugfs_create_file("samples", 0644, brake_dir, effect, &brake_s_dbgfs_ops); - if (!file) - return -ENOMEM; + if (IS_ERR(file)) + return PTR_ERR(file); file = debugfs_create_file("mode", 0644, brake_dir, effect, &brake_mode_dbgfs_ops); - if (!file) - return -ENOMEM; + if (IS_ERR(file)) + return PTR_ERR(file); } return 0; @@ -1943,33 +1944,47 @@ static int haptics_create_debugfs(struct haptics_chip *chip) int rc, i; hap_dir = debugfs_create_dir("haptics", NULL); - if (!hap_dir) { - dev_err(chip->dev, "create haptics debugfs directory failed\n"); - return -ENOMEM; + if (IS_ERR(hap_dir)) { + rc = PTR_ERR(hap_dir); + dev_err(chip->dev, "create haptics debugfs directory failed, rc=%d\n", + rc); + return rc; } for (i = 0; i < chip->effects_count; i++) { scnprintf(str, ARRAY_SIZE(str), "effect%d", chip->effects[i].id); effect_dir = debugfs_create_dir(str, hap_dir); - if (!effect_dir) { - dev_err(chip->dev, "create %s debugfs directory failed\n", - str); - rc = -ENOMEM; + if (IS_ERR(effect_dir)) { + rc = PTR_ERR(effect_dir); + dev_err(chip->dev, "create %s debugfs directory failed, rc=%d\n", + str, rc); goto exit; } rc = haptics_add_effects_debugfs(&chip->effects[i], effect_dir); if (rc < 0) { - rc = -ENOMEM; + dev_err(chip->dev, "create debugfs nodes for %s failed, rc=%d\n", + str, rc); goto exit; } } file = debugfs_create_file_unsafe("preload_effect_idx", 0644, hap_dir, chip, &preload_effect_idx_dbgfs_ops); - if (!file) { - rc = -ENOMEM; + if (IS_ERR(file)) { + rc = PTR_ERR(file); + dev_err(chip->dev, "create preload_effect_idx debugfs failed, rc=%d\n", + rc); + goto exit; + } + + file = debugfs_create_u32("fifo_empty_thresh", 0600, hap_dir, + &chip->config.fifo_empty_thresh); + if (IS_ERR(file)) { + rc = PTR_ERR(file); + dev_err(chip->dev, "create fifo_empty_thresh debugfs failed, rc=%d\n", + rc); goto exit; } @@ -2364,6 +2379,15 @@ static int haptics_parse_dt(struct haptics_chip *chip) return -EINVAL; } + config->fifo_empty_thresh = FIFO_EMPTY_THRESHOLD(chip); + of_property_read_u32(node, "qcom,fifo-empty-threshold", + &config->fifo_empty_thresh); + 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; + } + config->brake.mode = AUTO_BRAKE; of_property_read_u32(node, "qcom,brake-mode", &config->brake.mode); if (config->brake.mode > AUTO_BRAKE) { From 6679b3b4e6fb9ce21877d97a005652c3852e2182 Mon Sep 17 00:00:00 2001 From: Fenglin Wu Date: Fri, 3 Apr 2020 11:12:52 +0800 Subject: [PATCH 09/76] input: qcom-hv-haptics: update FIFO samples in IRQ thread Instead of sending a completion from the IRQ thread to notify a worker thread to update the FIFO samples, update the samples in the IRQ thread directly. This should help achieve lower latency which is good for FIFO playing. Meanwhile, following changes are made for FIFO playing: 1) Defer stopping command into erase() function if it's coming before all FIFO samples are played. This gives the hardware a little more time to play before stopping. 2) Restore FIFO play rate to T_LRA after playing is done according to hardware recommendation. Change-Id: I7b21589641d753d45b1261ce0e342373c249f4e5 Signed-off-by: Fenglin Wu --- drivers/input/misc/qcom-hv-haptics.c | 254 +++++++++++++++------------ 1 file changed, 144 insertions(+), 110 deletions(-) diff --git a/drivers/input/misc/qcom-hv-haptics.c b/drivers/input/misc/qcom-hv-haptics.c index 9f591023352f..d71b4e78aefd 100644 --- a/drivers/input/misc/qcom-hv-haptics.c +++ b/drivers/input/misc/qcom-hv-haptics.c @@ -326,11 +326,23 @@ struct haptics_effect { bool auto_res_disable; }; +/** + * struct fifo_play_status - Data used for recording the FIFO playing status + * + * @samples_written: The number of the samples that has been written into + * FIFO memory. + * @written_done: The flag to indicate if all of the FIFO samples has + * been written to the FIFO memory. + * @is_busy: The flag to indicate if it's in the middle of FIFO + * playing. + * @cancelled: The flag to indicate if FIFO playing is cancelled due + * to a stopping command arrived in the middle of playing. + */ struct fifo_play_status { - struct completion fifo_ready; u32 samples_written; atomic_t written_done; atomic_t is_busy; + atomic_t cancelled; }; struct haptics_play_info { @@ -359,9 +371,9 @@ struct haptics_chip { struct haptics_hw_config config; struct haptics_effect *effects; struct haptics_play_info play; - struct work_struct fifo_work; struct dentry *debugfs_dir; struct regulator_dev *swr_slave_rdev; + struct mutex irq_lock; int fifo_empty_irq; u32 effects_count; u32 cfg_addr_base; @@ -871,7 +883,6 @@ static int haptics_get_available_fifo_memory(struct haptics_chip *chip) } available = MAX_FIFO_SAMPLES(chip) - fill; - dev_dbg(chip->dev, "Available FIFO memory: %d bytes\n", available); return available; } @@ -909,12 +920,46 @@ static int haptics_update_fifo_samples(struct haptics_chip *chip, return 0; } +static int haptics_set_fifo_playrate(struct haptics_chip *chip, + enum s_period period_per_s) +{ + int rc; + u8 reg; + + reg = (chip->ptn_revision == HAP_PTN_V1) ? + HAP_PTN_V1_FIFO_PLAY_RATE_REG : HAP_PTN_V2_FIFO_PLAY_RATE_REG; + rc = haptics_masked_write(chip, chip->ptn_addr_base, + reg, FIFO_PLAY_RATE_MASK, period_per_s); + if (rc < 0) + dev_err(chip->dev, "Set FIFO play rate failed, rc=%d\n", rc); + + return rc; +} + +static int haptics_set_fifo_empty_threshold(struct haptics_chip *chip, + u32 thresh) +{ + u8 reg, thresh_per_bit; + int rc; + + reg = (chip->ptn_revision == HAP_PTN_V1) ? + HAP_PTN_V1_FIFO_EMPTY_CFG_REG : HAP_PTN_V2_FIFO_EMPTY_CFG_REG; + thresh_per_bit = (chip->ptn_revision == HAP_PTN_V1) ? + HAP_PTN_V1_FIFO_THRESH_LSB : HAP_PTN_V2_FIFO_THRESH_LSB; + rc = haptics_masked_write(chip, chip->ptn_addr_base, + reg, EMPTY_THRESH_MASK, thresh / thresh_per_bit); + if (rc < 0) + dev_err(chip->dev, "Set FIFO empty threshold failed, rc=%d\n", + rc); + + return rc; +} + static int haptics_set_fifo(struct haptics_chip *chip, struct fifo_cfg *fifo) { struct fifo_play_status *status = &chip->play.fifo_status; u32 num, fifo_thresh; - int rc, thresh_per_bit, available; - u8 reg; + int rc, available; if (atomic_read(&status->is_busy) == 1) { dev_err(chip->dev, "FIFO is busy\n"); @@ -922,14 +967,12 @@ static int haptics_set_fifo(struct haptics_chip *chip, struct fifo_cfg *fifo) } /* Configure FIFO play rate */ - reg = (chip->ptn_revision == HAP_PTN_V1) ? - HAP_PTN_V1_FIFO_PLAY_RATE_REG : HAP_PTN_V2_FIFO_PLAY_RATE_REG; - rc = haptics_masked_write(chip, chip->ptn_addr_base, - reg, FIFO_PLAY_RATE_MASK, fifo->period_per_s); + rc = haptics_set_fifo_playrate(chip, fifo->period_per_s); if (rc < 0) return rc; atomic_set(&status->written_done, 0); + atomic_set(&status->cancelled, 0); status->samples_written = 0; /* @@ -955,30 +998,17 @@ static int haptics_set_fifo(struct haptics_chip *chip, struct fifo_cfg *fifo) fifo_thresh = 0; atomic_set(&status->written_done, 1); } else { - reinit_completion(&status->fifo_ready); fifo_thresh = chip->config.fifo_empty_thresh; } /* - * Set FIFO empty threshold and enable FIFO empty IRQ, - * more data can be written into FIFO memory after - * the IRQ is triggered. + * Set FIFO empty threshold here. FIFO empty IRQ will + * be enabled after playing FIFO samples so that more + * FIFO samples can be written (if available) when + * FIFO empty IRQ is triggered. */ - reg = (chip->ptn_revision == HAP_PTN_V1) ? - HAP_PTN_V1_FIFO_EMPTY_CFG_REG : HAP_PTN_V2_FIFO_EMPTY_CFG_REG; - thresh_per_bit = (chip->ptn_revision == HAP_PTN_V1) ? - HAP_PTN_V1_FIFO_THRESH_LSB : HAP_PTN_V2_FIFO_THRESH_LSB; - rc = haptics_masked_write(chip, chip->ptn_addr_base, - reg, EMPTY_THRESH_MASK, fifo_thresh / thresh_per_bit); - if (rc < 0) - return rc; - if (!chip->fifo_empty_irq_en) { - enable_irq(chip->fifo_empty_irq); - chip->fifo_empty_irq_en = true; - } - - return 0; + return haptics_set_fifo_empty_threshold(chip, fifo_thresh); } static int haptics_set_direct_play(struct haptics_chip *chip) @@ -1167,6 +1197,20 @@ static int haptics_upload_effect(struct input_dev *dev, return 0; } +static void haptics_fifo_empty_irq_config(struct haptics_chip *chip, + bool enable) +{ + mutex_lock(&chip->irq_lock); + if (!chip->fifo_empty_irq_en && enable) { + enable_irq(chip->fifo_empty_irq); + chip->fifo_empty_irq_en = true; + } else if (chip->fifo_empty_irq_en && !enable) { + disable_irq_nosync(chip->fifo_empty_irq); + chip->fifo_empty_irq_en = false; + } + mutex_unlock(&chip->irq_lock); +} + static int haptics_playback(struct input_dev *dev, int effect_id, int val) { struct haptics_chip *chip = input_get_drvdata(dev); @@ -1179,25 +1223,45 @@ static int haptics_playback(struct input_dev *dev, int effect_id, int val) if (rc < 0) return rc; - if ((atomic_read(&play->fifo_status.written_done) == 0) - && play->pattern_src == FIFO) - schedule_work(&chip->fifo_work); + if (play->pattern_src == FIFO) + haptics_fifo_empty_irq_config(chip, true); } else { - rc = haptics_enable_play(chip, false); - if (rc < 0) - return rc; - - if (chip->fifo_empty_irq_en) { - disable_irq_nosync(chip->fifo_empty_irq); - chip->fifo_empty_irq_en = false; + if (atomic_read(&play->fifo_status.is_busy)) { + dev_dbg(chip->dev, "FIFO playing is not done yet, defer stopping in erase\n"); + return 0; } + + rc = haptics_enable_play(chip, false); } - return 0; + return rc; } static int haptics_erase(struct input_dev *dev, int effect_id) { + struct haptics_chip *chip = input_get_drvdata(dev); + struct haptics_play_info *play = &chip->play; + int rc; + + if ((play->pattern_src == FIFO) && + atomic_read(&play->fifo_status.is_busy)) { + dev_dbg(chip->dev, "cancelling FIFO playing\n"); + atomic_set(&play->fifo_status.cancelled, 1); + + rc = haptics_enable_play(chip, false); + if (rc < 0) + return rc; + + atomic_set(&play->fifo_status.is_busy, 0); + + /* restore FIFO play rate back to T_LRA */ + rc = haptics_set_fifo_playrate(chip, T_LRA); + if (rc < 0) + return rc; + + haptics_fifo_empty_irq_config(chip, false); + } + return 0; } @@ -1284,74 +1348,14 @@ static int haptics_hw_init(struct haptics_chip *chip) return rc; } -static void update_fifo_work(struct work_struct *work) -{ - struct haptics_chip *chip = container_of(work, - struct haptics_chip, fifo_work); - struct fifo_cfg *fifo = chip->play.effect->fifo; - struct fifo_play_status *status = &chip->play.fifo_status; - u32 samples_written, samples_left; - u8 *samples; - u8 reg; - int rc, num; - - samples_written = status->samples_written; - samples_left = fifo->num_s - samples_written; - - while (samples_left > 0) { - /* Waiting on FIFO empty IRQ triggered */ - rc = wait_for_completion_timeout(&status->fifo_ready, - msecs_to_jiffies(FIFO_READY_TIMEOUT_MS)); - if (!rc) { - dev_err(chip->dev, "Timeout on waiting FIFO ready!\n"); - return; - } - - num = haptics_get_available_fifo_memory(chip); - if (num < 0) - return; - - if (samples_left <= num) - num = samples_left; - else - reinit_completion(&status->fifo_ready); - - samples = fifo->samples + samples_written; - - /* Write more pattern data into FIFO memory. */ - rc = haptics_update_fifo_samples(chip, samples, num); - if (rc < 0) { - dev_err(chip->dev, "Update FIFO samples failed in fifo_work, rc=%d\n", - rc); - return; - } - - samples_written += num; - samples_left -= num; - dev_dbg(chip->dev, "FIFO %d samples written, %d samples left\n", - samples_written, samples_left); - } - - /* - * If all pattern data is written, set FIFO empty - * threshold to 0 so that FIFO empty IRQ can be used - * for detecting FIFO playing done event. - */ - dev_dbg(chip->dev, "FIFO programmed done\n"); - atomic_set(&chip->play.fifo_status.written_done, 1); - reg = (chip->ptn_revision == HAP_PTN_V1) ? - HAP_PTN_V1_FIFO_EMPTY_CFG_REG : HAP_PTN_V2_FIFO_EMPTY_CFG_REG; - rc = haptics_masked_write(chip, chip->ptn_addr_base, - reg, EMPTY_THRESH_MASK, 0); - if (rc < 0) - dev_err(chip->dev, "set FIFO empty threshold to 0 failed, rc=%d\n", - rc); -} - static irqreturn_t fifo_empty_irq_handler(int irq, void *data) { struct haptics_chip *chip = data; - int rc; + struct fifo_cfg *fifo = chip->play.effect->fifo; + struct fifo_play_status *status = &chip->play.fifo_status; + u32 samples_left; + u8 *samples; + int rc, num; if (atomic_read(&chip->play.fifo_status.written_done) == 1) { dev_dbg(chip->dev, "FIFO data is done playing\n"); @@ -1359,15 +1363,45 @@ static irqreturn_t fifo_empty_irq_handler(int irq, void *data) if (rc < 0) return IRQ_HANDLED; - if (chip->fifo_empty_irq_en) { - disable_irq_nosync(chip->fifo_empty_irq); - chip->fifo_empty_irq_en = false; - } + /* restore FIFO play rate back to T_LRA */ + rc = haptics_set_fifo_playrate(chip, T_LRA); + if (rc < 0) + return IRQ_HANDLED; + + haptics_fifo_empty_irq_config(chip, false); atomic_set(&chip->play.fifo_status.written_done, 0); atomic_set(&chip->play.fifo_status.is_busy, 0); } else { - complete(&chip->play.fifo_status.fifo_ready); + if (atomic_read(&status->cancelled) == 1) { + dev_dbg(chip->dev, "FIFO programming got cancelled\n"); + return IRQ_HANDLED; + } + + samples_left = fifo->num_s - status->samples_written; + num = haptics_get_available_fifo_memory(chip); + if (num < 0) + return IRQ_HANDLED; + + if (samples_left <= num) + num = samples_left; + + samples = fifo->samples + status->samples_written; + + /* Write more pattern data into FIFO memory. */ + rc = haptics_update_fifo_samples(chip, samples, num); + if (rc < 0) { + dev_err(chip->dev, "Update FIFO samples failed, rc=%d\n", + rc); + return IRQ_HANDLED; + } + + status->samples_written += num; + if (status->samples_written == fifo->num_s) { + dev_dbg(chip->dev, "FIFO programming is done\n"); + atomic_set(&chip->play.fifo_status.written_done, 1); + haptics_set_fifo_empty_threshold(chip, 0); + } } return IRQ_HANDLED; @@ -2585,13 +2619,13 @@ static int haptics_probe(struct platform_device *pdev) return rc; } + mutex_init(&chip->irq_lock); disable_irq_nosync(chip->fifo_empty_irq); chip->fifo_empty_irq_en = false; - init_completion(&chip->play.fifo_status.fifo_ready); atomic_set(&chip->play.fifo_status.is_busy, 0); atomic_set(&chip->play.fifo_status.written_done, 0); - INIT_WORK(&chip->fifo_work, update_fifo_work); + atomic_set(&chip->play.fifo_status.cancelled, 0); input_dev->name = "qcom-hv-haptics"; input_set_drvdata(input_dev, chip); chip->input_dev = input_dev; From 326c01c11c48110933b2fd6b5fb37719c81f577d Mon Sep 17 00:00:00 2001 From: Fenglin Wu Date: Fri, 3 Apr 2020 12:53:15 +0800 Subject: [PATCH 10/76] input: qcom-hv-haptics: limit play rate for PM8350B v1 hardware Due to a hardware limitation, PM8350B v1 cannot play more than 8 KHz as it has a FIFO size of 104 bytes. Hence limit the play rate for PM8350B v1 to 8 KHz. Change-Id: Ibeabd4c0229a54e2d99023c8fbc5970b1a7a1e64 Signed-off-by: Fenglin Wu --- drivers/input/misc/qcom-hv-haptics.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/input/misc/qcom-hv-haptics.c b/drivers/input/misc/qcom-hv-haptics.c index d71b4e78aefd..c3239256857b 100644 --- a/drivers/input/misc/qcom-hv-haptics.c +++ b/drivers/input/misc/qcom-hv-haptics.c @@ -966,6 +966,13 @@ static int haptics_set_fifo(struct haptics_chip *chip, struct fifo_cfg *fifo) return -EBUSY; } + if (chip->ptn_revision == HAP_PTN_V1 && + fifo->period_per_s > F_8KHZ && + fifo->num_s > MAX_FIFO_SAMPLES(chip)) { + dev_err(chip->dev, "PM8350B v1 doesn't support playing long FIFO pattern higher than 8 KHz play rate\n"); + return -EINVAL; + } + /* Configure FIFO play rate */ rc = haptics_set_fifo_playrate(chip, fifo->period_per_s); if (rc < 0) From 7cec6589d2303952653f2e1d2f0b0eb62a67872d Mon Sep 17 00:00:00 2001 From: Fenglin Wu Date: Mon, 30 Mar 2020 16:08:20 +0800 Subject: [PATCH 11/76] input: qcom-hv-haptics: store closed-loop brake settings into SDAM Read the closed-loop brake calibration settings from status register and store them into a SDAM register for hardware to re-use them after system reboot. Change-Id: I9068559fffba58927eefc140ed58afc054257b22 Signed-off-by: Fenglin Wu --- drivers/input/misc/qcom-hv-haptics.c | 45 ++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/drivers/input/misc/qcom-hv-haptics.c b/drivers/input/misc/qcom-hv-haptics.c index c3239256857b..16eaca9b35a0 100644 --- a/drivers/input/misc/qcom-hv-haptics.c +++ b/drivers/input/misc/qcom-hv-haptics.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -117,6 +118,7 @@ #define HAP_CFG_MOD_STATUS_SEL_REG 0x70 #define MOD_STATUS_SEL_FIFO_FILL_STATUS_VAL 5 +#define MOD_STATUS_SEL_BRAKE_CAL_RNAT_RCAL_VAL 6 #define HAP_CFG_MOD_STATUS_XT_V2_REG 0x71 #define MOD_STATUS_XT_V2_FIFO_FILL_STATUS_VAL 0x80 @@ -374,6 +376,7 @@ struct haptics_chip { struct dentry *debugfs_dir; struct regulator_dev *swr_slave_rdev; struct mutex irq_lock; + struct nvmem_cell *cl_brake_nvmem; int fifo_empty_irq; u32 effects_count; u32 cfg_addr_base; @@ -1288,6 +1291,30 @@ static void haptics_set_gain(struct input_dev *dev, u16 gain) haptics_set_vmax_mv(chip, play->vmax_mv); } +static int haptics_store_cl_brake_settings(struct haptics_chip *chip) +{ + int rc = 0; + u8 val; + + val = MOD_STATUS_SEL_BRAKE_CAL_RNAT_RCAL_VAL; + rc = haptics_write(chip, chip->cfg_addr_base, + HAP_CFG_MOD_STATUS_SEL_REG, &val, 1); + if (rc < 0) + return rc; + + rc = haptics_read(chip, chip->cfg_addr_base, + HAP_CFG_STATUS_DATA_LSB_REG, &val, 1); + if (rc < 0) + return rc; + + rc = nvmem_cell_write(chip->cl_brake_nvmem, &val, sizeof(val)); + if (rc < 0) + dev_err(chip->dev, "store RNAT/RCAL to SDAM failed, rc=%d\n"); + + return rc; +} + + static int haptics_hw_init(struct haptics_chip *chip) { struct haptics_hw_config *config = &chip->config; @@ -1295,6 +1322,11 @@ static int haptics_hw_init(struct haptics_chip *chip) int rc = 0, tmp, i; u8 val[2]; + /* Store CL brake settings */ + rc = haptics_store_cl_brake_settings(chip); + if (rc < 0) + return rc; + /* Config VMAX */ rc = haptics_set_vmax_mv(chip, config->vmax_mv); if (rc < 0) @@ -2384,6 +2416,19 @@ static int haptics_parse_dt(struct haptics_chip *chip) const __be32 *addr; int rc = 0, tmp; + if (of_find_property(node, "nvmem-cells", NULL)) { + chip->cl_brake_nvmem = devm_nvmem_cell_get(chip->dev, + "hap_cl_brake"); + if (IS_ERR(chip->cl_brake_nvmem)) { + rc = PTR_ERR(chip->cl_brake_nvmem); + if (rc != -EPROBE_DEFER) + dev_err(chip->dev, "Failed to get nvmem-cells, rc=%d\n", + rc); + + return rc; + } + } + addr = of_get_address(node, 0, NULL, NULL); if (!addr) { dev_err(chip->dev, "Read HAPTICS_CFG address failed, rc = %d\n", From 1f89f3b09d76671855b96e900dc9765a38893a94 Mon Sep 17 00:00:00 2001 From: Fenglin Wu Date: Thu, 9 Apr 2020 09:27:15 +0800 Subject: [PATCH 12/76] input: misc: qcom-hv-haptics: Add support to play custom waveform Custom waveform with FIFO data samples and play rate setting would be passed down from userspace through custom_data memory in periodic effect data structure. Load the waveform into FIFO memory and play it using FIFO mode. Change-Id: I26b78df809efbe398c91c89394152caebafbe1b6 Signed-off-by: Fenglin Wu --- drivers/input/misc/qcom-hv-haptics.c | 280 ++++++++++++++++++++++----- 1 file changed, 236 insertions(+), 44 deletions(-) diff --git a/drivers/input/misc/qcom-hv-haptics.c b/drivers/input/misc/qcom-hv-haptics.c index 16eaca9b35a0..39020391c0c8 100644 --- a/drivers/input/misc/qcom-hv-haptics.c +++ b/drivers/input/misc/qcom-hv-haptics.c @@ -192,11 +192,14 @@ #define CHAR_MSG_HEADER 16 #define CHAR_BRAKE_MODE 24 #define HW_BRAKE_CYCLES 5 +#define F_LRA_VARIATION_HZ 5 #define MAX_FIFO_SAMPLES(chip) \ ((chip)->ptn_revision == HAP_PTN_V1 ? 104 : 640) #define FIFO_EMPTY_THRESHOLD(chip) \ ((chip)->ptn_revision == HAP_PTN_V1 ? 48 : 280) +#define is_between(val, min, max) \ + (((min) <= (max)) && ((min) <= (val)) && ((val) <= (max))) enum drv_sig_shape { WF_SQUARE, @@ -239,7 +242,7 @@ enum s_period { T_RESERVED, /* F_xKHZ definitions are for FIFO only */ F_8KHZ, - F_16HKZ, + F_16KHZ, F_24KHZ, F_32KHZ, F_44P1KHZ, @@ -366,12 +369,20 @@ struct haptics_hw_config { bool is_erm; }; +struct custom_fifo_data { + u32 idx; + u32 length; + u32 play_rate_hz; + u8 *data; +}; + struct haptics_chip { struct device *dev; struct regmap *regmap; struct input_dev *input_dev; struct haptics_hw_config config; struct haptics_effect *effects; + struct haptics_effect *custom_effect; struct haptics_play_info play; struct dentry *debugfs_dir; struct regulator_dev *swr_slave_rdev; @@ -599,7 +610,7 @@ static int get_fifo_play_length_us(struct fifo_cfg *fifo, u32 t_lra_us) case F_8KHZ: length_us = 1000 * fifo->num_s / 8; break; - case F_16HKZ: + case F_16KHZ: length_us = 1000 * fifo->num_s / 16; break; case F_24KHZ: @@ -1110,6 +1121,143 @@ static int haptics_load_predefined_effect(struct haptics_chip *chip, return 0; } +static int haptics_init_custom_effect(struct haptics_chip *chip) +{ + chip->custom_effect = devm_kzalloc(chip->dev, + sizeof(*chip->custom_effect), GFP_KERNEL); + if (!chip->custom_effect) + return -ENOMEM; + + chip->custom_effect->fifo = devm_kzalloc(chip->dev, + sizeof(*chip->custom_effect->fifo), GFP_KERNEL); + if (!chip->custom_effect->fifo) + return -ENOMEM; + + /* custom effect will be played in FIFO mode without brake */ + chip->custom_effect->pattern = NULL; + chip->custom_effect->brake = NULL; + chip->custom_effect->id = UINT_MAX; + chip->custom_effect->vmax_mv = chip->config.vmax_mv; + chip->custom_effect->t_lra_us = chip->config.t_lra_us; + chip->custom_effect->src = FIFO; + chip->custom_effect->auto_res_disable = false; + + return 0; +} + +static int haptics_convert_sample_period(struct haptics_chip *chip, + u32 play_rate_hz) +{ + enum s_period period; + u32 f_lra, f_lra_min, f_lra_max; + + if (chip->config.t_lra_us == 0) + return -EINVAL; + + f_lra = USEC_PER_SEC / chip->config.t_lra_us; + if (f_lra == 0 || f_lra < F_LRA_VARIATION_HZ) + return -EINVAL; + + f_lra_min = f_lra - F_LRA_VARIATION_HZ; + f_lra_max = f_lra + F_LRA_VARIATION_HZ; + + if (play_rate_hz == 8000) + period = F_8KHZ; + else if (play_rate_hz == 16000) + period = F_16KHZ; + else if (play_rate_hz == 24000) + period = F_24KHZ; + else if (play_rate_hz == 32000) + period = F_32KHZ; + else if (play_rate_hz == 44100) + period = F_44P1KHZ; + else if (play_rate_hz == 48000) + period = F_48KHZ; + else if (is_between(play_rate_hz, f_lra_min, f_lra_max)) + period = T_LRA; + else if (is_between(play_rate_hz / 2, f_lra_min, f_lra_max)) + period = T_LRA_DIV_2; + else if (is_between(play_rate_hz / 4, f_lra_min, f_lra_max)) + period = T_LRA_DIV_4; + else if (is_between(play_rate_hz / 8, f_lra_min, f_lra_max)) + period = T_LRA_DIV_8; + else if (is_between(play_rate_hz * 2, f_lra_min, f_lra_max)) + period = T_LRA_X_2; + else if (is_between(play_rate_hz * 4, f_lra_min, f_lra_max)) + period = T_LRA_X_4; + else if (is_between(play_rate_hz * 8, f_lra_min, f_lra_max)) + period = T_LRA_X_8; + else + return -EINVAL; + + return period; +} + +static int haptics_load_custom_effect(struct haptics_chip *chip, + s16 __user *data, u32 length, s16 magnitude) +{ + struct haptics_play_info *play = &chip->play; + struct custom_fifo_data custom_data = {}; + struct fifo_cfg *fifo; + int rc; + + if (!chip->custom_effect || !chip->custom_effect->fifo) + return -ENOMEM; + + fifo = chip->custom_effect->fifo; + if (copy_from_user(&custom_data, data, sizeof(custom_data))) + return -EFAULT; + + dev_dbg(chip->dev, "custom data length %d with play-rate %d Hz\n", + custom_data.length, custom_data.play_rate_hz); + rc = haptics_convert_sample_period(chip, custom_data.play_rate_hz); + if (rc < 0) { + dev_err(chip->dev, "Can't support play rate: %d Hz\n", + custom_data.play_rate_hz); + return rc; + } + + fifo->period_per_s = rc; + /* + * Before allocating samples buffer, free the old sample + * buffer first if it's not been freed. + */ + kfree(fifo->samples); + fifo->samples = kcalloc(custom_data.length, sizeof(u8), GFP_KERNEL); + if (!fifo->samples) + return -ENOMEM; + + if (copy_from_user(fifo->samples, + (u8 __user *)custom_data.data, + custom_data.length)) { + rc = -EFAULT; + goto cleanup; + } + + dev_dbg(chip->dev, "Copy custom FIFO samples successfully\n"); + fifo->num_s = custom_data.length; + fifo->play_length_us = get_fifo_play_length_us(fifo, + chip->custom_effect->t_lra_us); + + play->effect = chip->custom_effect; + play->brake = NULL; + play->vmax_mv = (magnitude * chip->custom_effect->vmax_mv) / 0x7fff; + rc = haptics_set_vmax_mv(chip, play->vmax_mv); + if (rc < 0) + goto cleanup; + + play->pattern_src = FIFO; + rc = haptics_set_fifo(chip, play->effect->fifo); + if (rc < 0) + goto cleanup; + + return 0; +cleanup: + kfree(fifo->samples); + fifo->samples = NULL; + return rc; +} + static u32 get_play_length_us(struct haptics_play_info *play) { struct haptics_effect *effect = play->effect; @@ -1127,21 +1275,66 @@ static u32 get_play_length_us(struct haptics_play_info *play) return length_us; } +static int haptics_load_periodic_effect(struct haptics_chip *chip, + s16 __user *data, u32 length, s16 magnitude) +{ + struct haptics_play_info *play = &chip->play; + s16 custom_data[CUSTOM_DATA_LEN] = { 0 }; + int rc, i; + + if (chip->effects_count == 0) + return -EINVAL; + + if (copy_from_user(custom_data, data, sizeof(custom_data))) + return -EFAULT; + + for (i = 0; i < chip->effects_count; i++) + if (chip->effects[i].id == custom_data[CUSTOM_DATA_EFFECT_IDX]) + break; + + if (i == chip->effects_count) { + dev_err(chip->dev, "effect%d is not supported!\n", + custom_data[CUSTOM_DATA_EFFECT_IDX]); + return -EINVAL; + } + + 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); + rc = haptics_load_predefined_effect(chip, i); + if (rc < 0) { + dev_err(chip->dev, "Play predefined effect%d failed, rc=%d\n", + chip->effects[i].id, rc); + return rc; + } + + play->length_us = get_play_length_us(play); + custom_data[CUSTOM_DATA_TIMEOUT_SEC_IDX] = + play->length_us / USEC_PER_SEC; + custom_data[CUSTOM_DATA_TIMEOUT_MSEC_IDX] = + (play->length_us % USEC_PER_SEC) / USEC_PER_MSEC; + + if (copy_to_user(data, custom_data, length)) + return -EFAULT; + + return 0; +} + static int haptics_upload_effect(struct input_dev *dev, struct ff_effect *effect, struct ff_effect *old) { struct haptics_chip *chip = input_get_drvdata(dev); struct haptics_hw_config *config = &chip->config; struct haptics_play_info *play = &chip->play; - s16 level, data[CUSTOM_DATA_LEN]; - int rc = 0, tmp, i; + s16 level; + int rc = 0; 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; + play->vmax_mv = (level * config->vmax_mv) / 0x7fff; dev_dbg(chip->dev, "upload constant effect, length = %dus, vmax_mv = %d\n", play->length_us, play->vmax_mv); haptics_set_direct_play(chip); @@ -1150,53 +1343,38 @@ static int haptics_upload_effect(struct input_dev *dev, 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 support custom waveforms\n"); return -EINVAL; } - if (copy_from_user(data, effect->u.periodic.custom_data, - sizeof(data))) - return -EFAULT; - - for (i = 0; i < chip->effects_count; i++) - if (chip->effects[i].id == data[CUSTOM_DATA_EFFECT_IDX]) - break; - - if (i == chip->effects_count) { - dev_err(chip->dev, "effect%d is not supported!\n", - data[CUSTOM_DATA_EFFECT_IDX]); - return -EINVAL; + if (effect->u.periodic.custom_len == + sizeof(struct custom_fifo_data)) { + rc = haptics_load_custom_effect(chip, + effect->u.periodic.custom_data, + effect->u.periodic.custom_len, + effect->u.periodic.magnitude); + if (rc < 0) { + dev_err(chip->dev, "Upload custom FIFO data failed\n", + rc); + return rc; + } + } else if (effect->u.periodic.custom_len == + sizeof(s16) * CUSTOM_DATA_LEN) { + rc = haptics_load_periodic_effect(chip, + effect->u.periodic.custom_data, + effect->u.periodic.custom_len, + effect->u.periodic.magnitude); + if (rc < 0) { + dev_err(chip->dev, "Upload periodic effect failed\n", + rc); + return rc; + } } - level = effect->u.periodic.magnitude; - tmp = level * chip->effects[i].vmax_mv; - play->vmax_mv = tmp / 0x7fff; - - dev_dbg(chip->dev, "upload effect %d, vmax_mv=%d\n", - chip->effects[i].id, play->vmax_mv); - rc = haptics_load_predefined_effect(chip, i); - if (rc < 0) { - dev_err(chip->dev, "Play predefined effect%d failed, rc=%d\n", - chip->effects[i].id, rc); - return rc; - } - - play->length_us = get_play_length_us(play); - 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; - - if (copy_to_user(effect->u.periodic.custom_data, data, - sizeof(s16) * CUSTOM_DATA_LEN)) - return -EFAULT; break; default: dev_err(chip->dev, "%d effect is not supported\n", @@ -1264,6 +1442,10 @@ static int haptics_erase(struct input_dev *dev, int effect_id) atomic_set(&play->fifo_status.is_busy, 0); + /* free custom_effect FIFO samples buffer after stopping play */ + kfree(chip->custom_effect->fifo->samples); + chip->custom_effect->fifo->samples = NULL; + /* restore FIFO play rate back to T_LRA */ rc = haptics_set_fifo_playrate(chip, T_LRA); if (rc < 0) @@ -1409,6 +1591,10 @@ static irqreturn_t fifo_empty_irq_handler(int irq, void *data) haptics_fifo_empty_irq_config(chip, false); + /* free custom_effect FIFO samples after playing is done */ + kfree(chip->custom_effect->fifo->samples); + chip->custom_effect->fifo->samples = NULL; + atomic_set(&chip->play.fifo_status.written_done, 0); atomic_set(&chip->play.fifo_status.is_busy, 0); } else { @@ -2649,6 +2835,12 @@ static int haptics_probe(struct platform_device *pdev) return rc; } + rc = haptics_init_custom_effect(chip); + if (rc < 0) { + dev_err(chip->dev, "Init custom effect failed, rc=%d\n", rc); + return rc; + } + rc = haptics_hw_init(chip); if (rc < 0) { dev_err(chip->dev, "Initialize HW failed, rc = %d\n", rc); From d768902aed2926dbf2eed4cfc8ef3bf06d0746e5 Mon Sep 17 00:00:00 2001 From: Shyam Kumar Thella Date: Wed, 22 Apr 2020 21:17:38 +0530 Subject: [PATCH 13/76] input: qcom-hv-haptics: add RC clock calibration for FIFO mode As per the hardware documentation, adjust T_LRA using RC clock calibration when playing in FIFO mode with play rate not less than 8 KHz. Based on the closed loop T_LRA, adjust the open loop T_LRA specified in the device tree and use it in the calculation. Change-Id: I5197bd04051c2aaab38c5ff5f37a47e0ef4b48d3 Signed-off-by: Shyam Kumar Thella --- drivers/input/misc/qcom-hv-haptics.c | 155 ++++++++++++++++++++++++++- 1 file changed, 154 insertions(+), 1 deletion(-) diff --git a/drivers/input/misc/qcom-hv-haptics.c b/drivers/input/misc/qcom-hv-haptics.c index 39020391c0c8..235e6c6888f8 100644 --- a/drivers/input/misc/qcom-hv-haptics.c +++ b/drivers/input/misc/qcom-hv-haptics.c @@ -28,6 +28,8 @@ /* STATUS_DATA_MSB definitions while MOD_STATUS_SEL is 0 */ #define AUTO_RES_CAL_DONE_BIT BIT(5) #define CAL_TLRA_CL_STS_MSB_MASK GENMASK(4, 0) +/* STATUS_DATA_MSB definition in V2 while MOD_STATUS_SEL is 3 */ +#define LAST_GOOD_TLRA_CL_MASK GENMASK(4, 0) /* STATUS_DATA_MSB definition in V1 while MOD_STATUS_SEL is 5 */ #define FIFO_REAL_TIME_FILL_STATUS_MASK_V1 GENMASK(6, 0) /* STATUS DATA_MSB definition in V2 while MOD_STATUS_SEL is 5 */ @@ -117,11 +119,19 @@ #define VMAX_HDRM_STEP_MV 50 #define HAP_CFG_MOD_STATUS_SEL_REG 0x70 +#define MOD_STATUS_SEL_CAL_TLRA_CL_STS_VAL 0 +#define MOD_STATUS_SEL_LAST_GOOD_TLRA_VAL 3 #define MOD_STATUS_SEL_FIFO_FILL_STATUS_VAL 5 #define MOD_STATUS_SEL_BRAKE_CAL_RNAT_RCAL_VAL 6 #define HAP_CFG_MOD_STATUS_XT_V2_REG 0x71 #define MOD_STATUS_XT_V2_FIFO_FILL_STATUS_VAL 0x80 +#define MOD_STATUS_XT_SEL_LAST_GOOD_TLRA_VAL 0 + +#define HAP_CFG_CAL_EN_REG 0x72 +#define CAL_RC_CLK_MASK GENMASK(3, 2) +#define CAL_RC_CLK_SHIFT 2 +#define CAL_RC_CLK_MANUAL_VAL 2 /* version register definitions for HAPTICS_PATTERN module */ #define HAP_PTN_REVISION2_REG 0x01 @@ -363,6 +373,7 @@ struct haptics_hw_config { struct brake_cfg brake; u32 vmax_mv; u32 t_lra_us; + u32 cl_t_lra_us; u32 preload_effect; u32 fifo_empty_thresh; enum drv_sig_shape drv_wf; @@ -650,6 +661,103 @@ static int get_brake_play_length_us(struct brake_cfg *brake, u32 t_lra_us) return t_lra_us * (i + 1); } +#define V1_CL_TLRA_STEP_NS_AUTO_RES_CAL_NOT_DONE 5000 +#define V1_CL_TLRA_STEP_NS_AUTO_RES_CAL_DONE 3333 +static int haptics_get_closeloop_lra_period_v1( + struct haptics_chip *chip) +{ + struct haptics_hw_config *config = &chip->config; + int rc; + u8 val[2]; + u32 tmp, step_ns; + bool auto_res_cal_done; + + val[0] = MOD_STATUS_SEL_CAL_TLRA_CL_STS_VAL; + rc = haptics_write(chip, chip->cfg_addr_base, + HAP_CFG_MOD_STATUS_SEL_REG, val, 1); + if (rc < 0) + return rc; + + rc = haptics_read(chip, chip->cfg_addr_base, + HAP_CFG_STATUS_DATA_MSB_REG, val, 2); + if (rc < 0) + return rc; + + /* + * Calculate the closed loop T_LRA with the following equations + * for PM8350B V1: + * LAST_GOOD_TLRA_CL_STS[12:0] = STATUS_DATA_MSB[4:0] | + * STATUS_DATA_LSB[7:0] + * CL_TLRA_STEP_US = STATUS_DATA_MSB[5] ? 3.333 us : 5 us + * LAST_GOOD_TLRA_CL = LAST_GOOD_TLRA_CL_STS[12:0] * + * CL_TLRA_STEP_US + */ + auto_res_cal_done = !!(val[0] & AUTO_RES_CAL_DONE_BIT); + if (auto_res_cal_done) + step_ns = V1_CL_TLRA_STEP_NS_AUTO_RES_CAL_DONE; + else + step_ns = V1_CL_TLRA_STEP_NS_AUTO_RES_CAL_NOT_DONE; + + tmp = ((val[0] & CAL_TLRA_CL_STS_MSB_MASK) << 8) | val[1]; + config->cl_t_lra_us = (tmp * step_ns) / 1000; + + return 0; + +} + +#define CL_TLRA_STEP_NS 1666 +static int haptics_get_closeloop_lra_period_v2( + struct haptics_chip *chip) +{ + struct haptics_hw_config *config = &chip->config; + int rc; + u8 val[2]; + u32 tmp; + + val[0] = MOD_STATUS_SEL_LAST_GOOD_TLRA_VAL; + val[1] = MOD_STATUS_XT_SEL_LAST_GOOD_TLRA_VAL; + rc = haptics_write(chip, chip->cfg_addr_base, + HAP_CFG_MOD_STATUS_SEL_REG, val, 2); + if (rc < 0) + return rc; + + rc = haptics_read(chip, chip->cfg_addr_base, + HAP_CFG_STATUS_DATA_MSB_REG, val, 2); + if (rc < 0) + return rc; + /* + * Calculate the closed loop T_LRA with the following equations + * for PM8350B V2: + * LAST_GOOD_TLRA_CL_STS[12:0] = STATUS_DATA_MSB[4:0] | + * STATUS_DATA_LSB[7:0] + * LAST_GOOD_TLRA_CL = LAST_GOOD_TLRA_CL_STS[12:0] * 1.666 us + */ + tmp = ((val[0] & LAST_GOOD_TLRA_CL_MASK) << 8) | val[1]; + config->cl_t_lra_us = (tmp * CL_TLRA_STEP_NS) / 1000; + + return 0; +} + +static int haptics_get_closeloop_lra_period(struct haptics_chip *chip) +{ + int rc = 0; + + if (chip->ptn_revision == HAP_PTN_V1) + rc = haptics_get_closeloop_lra_period_v1(chip); + else + rc = haptics_get_closeloop_lra_period_v2(chip); + + if (rc < 0) { + dev_err(chip->dev, "get close loop T LRA failed, rc=%d\n", rc); + return rc; + } + + dev_dbg(chip->dev, "OL_TLRA %u us, CL_TLRA %u us\n", + chip->config.t_lra_us, chip->config.cl_t_lra_us); + + return 0; +} + static int haptics_set_vmax_mv(struct haptics_chip *chip, u32 vmax_mv) { int rc = 0; @@ -900,6 +1008,39 @@ static int haptics_get_available_fifo_memory(struct haptics_chip *chip) return available; } +static int haptics_adjust_rc_clk(struct haptics_chip *chip) +{ + int rc; + int freq_diff, cal_count, f_lra, cl_f_lra; + u8 val[2]; + + if (!chip->config.t_lra_us || !chip->config.cl_t_lra_us) + return -EINVAL; + + f_lra = USEC_PER_SEC / chip->config.t_lra_us; + if (!f_lra) + return -EINVAL; + + cl_f_lra = USEC_PER_SEC / chip->config.cl_t_lra_us; + freq_diff = cl_f_lra - f_lra; + + /* RC_CLK_CAL_COUNT = 600*(1-(clk_adjustment/Fifo_pat_freq)) */ + cal_count = 600 - ((600 * freq_diff) / f_lra); + dev_dbg(chip->dev, "RC clock calibration count:%#x\n", cal_count); + + /* Set FIFO pattern frequency adjustment */ + val[0] = (cal_count >> 8) & RC_CLK_CAL_COUNT_MSB_MASK; + val[1] = cal_count & RC_CLK_CAL_COUNT_LSB_MASK; + rc = haptics_write(chip, chip->cfg_addr_base, + HAP_CFG_RC_CLK_CAL_COUNT_MSB_REG, val, 2); + if (rc < 0) + return rc; + + val[0] = CAL_RC_CLK_MANUAL_VAL << CAL_RC_CLK_SHIFT; + return haptics_masked_write(chip, chip->cfg_addr_base, + HAP_CFG_CAL_EN_REG, CAL_RC_CLK_MASK, val[0]); +} + static int haptics_update_fifo_samples(struct haptics_chip *chip, u8 *samples, u32 length) { @@ -992,6 +1133,15 @@ static int haptics_set_fifo(struct haptics_chip *chip, struct fifo_cfg *fifo) if (rc < 0) return rc; + if (fifo->period_per_s >= F_8KHZ) { + rc = haptics_adjust_rc_clk(chip); + if (rc < 0) { + dev_err(chip->dev, "RC CLK adjustment failed, rc=%d\n", + rc); + return rc; + } + } + atomic_set(&status->written_done, 0); atomic_set(&status->cancelled, 0); status->samples_written = 0; @@ -1496,7 +1646,6 @@ static int haptics_store_cl_brake_settings(struct haptics_chip *chip) return rc; } - static int haptics_hw_init(struct haptics_chip *chip) { struct haptics_hw_config *config = &chip->config; @@ -1535,6 +1684,10 @@ static int haptics_hw_init(struct haptics_chip *chip) if (config->is_erm) return 0; + rc = haptics_get_closeloop_lra_period(chip); + if (rc < 0) + return rc; + /* Config T_LRA */ tmp = config->t_lra_us / TLRA_STEP_US; val[0] = (tmp >> 8) & TLRA_OL_MSB_MASK; From 6992edc015987ad82b2d1c07f384d32a469ca74c Mon Sep 17 00:00:00 2001 From: Fenglin Wu Date: Tue, 28 Apr 2020 09:00:52 +0800 Subject: [PATCH 14/76] input: misc: qcom-hv-haptics: check nvmem before using it Currently, nvmem device is used to store closed loop brake settings. If nvmem-cells is not specified for closed loop brake, then nvmem device can be NULL. For such case, skip updating closed loop brake settings by not writing to the nvmem device. Change-Id: I18d455814f3731af519949f21a3a553bd1692548 Signed-off-by: Fenglin Wu --- drivers/input/misc/qcom-hv-haptics.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/input/misc/qcom-hv-haptics.c b/drivers/input/misc/qcom-hv-haptics.c index 235e6c6888f8..f2bd026db697 100644 --- a/drivers/input/misc/qcom-hv-haptics.c +++ b/drivers/input/misc/qcom-hv-haptics.c @@ -1628,6 +1628,9 @@ static int haptics_store_cl_brake_settings(struct haptics_chip *chip) int rc = 0; u8 val; + if (!chip->cl_brake_nvmem) + return 0; + val = MOD_STATUS_SEL_BRAKE_CAL_RNAT_RCAL_VAL; rc = haptics_write(chip, chip->cfg_addr_base, HAP_CFG_MOD_STATUS_SEL_REG, &val, 1); From 1af05bc261581274fac0d1027533bc86d6cc9335 Mon Sep 17 00:00:00 2001 From: Fenglin Wu Date: Wed, 29 Apr 2020 14:05:59 +0800 Subject: [PATCH 15/76] input: qcom-hv-haptics: set auto resonance when loading effects Set auto resonance when loading effect according to the effect settings. Change-Id: I8dc9ac99a57e396d67da28ae7b1eb31813ab3362 Signed-off-by: Fenglin Wu --- drivers/input/misc/qcom-hv-haptics.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/input/misc/qcom-hv-haptics.c b/drivers/input/misc/qcom-hv-haptics.c index f2bd026db697..522608034ada 100644 --- a/drivers/input/misc/qcom-hv-haptics.c +++ b/drivers/input/misc/qcom-hv-haptics.c @@ -1232,6 +1232,10 @@ static int haptics_load_predefined_effect(struct haptics_chip *chip, if (rc < 0) return rc; + rc = haptics_enable_autores(chip, !play->effect->auto_res_disable); + if (rc < 0) + return rc; + play->pattern_src = play->effect->src; if (play->pattern_src != PATTERN1 && play->pattern_src != PATTERN2 && @@ -1396,6 +1400,10 @@ static int haptics_load_custom_effect(struct haptics_chip *chip, if (rc < 0) goto cleanup; + rc = haptics_enable_autores(chip, !play->effect->auto_res_disable); + if (rc < 0) + goto cleanup; + play->pattern_src = FIFO; rc = haptics_set_fifo(chip, play->effect->fifo); if (rc < 0) From 8dce088a1439014d1fef43e641ade67ef8bc5ad1 Mon Sep 17 00:00:00 2001 From: Fenglin Wu Date: Fri, 1 May 2020 19:31:10 +0800 Subject: [PATCH 16/76] input: qcom-hv-haptics: Add LRA frequency calibration interfaces Add interfaces to run calibration sequence and read out the closed loop LRA frequency. With this change, the closed loop LRA frequency can be read out using following commands: echo 1 > /sys/class/qcom-haptics/lra_calibration cat /sys/class/qcom-haptics/lra_frequency_hz Meanwhile, correct the setting for direct play, it expects a 8-bit value to represent the proportion of vmax instead of the actual vmax voltage. Change-Id: I9f3bfb978240ca853446dee4877d6ece4b465058 Signed-off-by: Fenglin Wu --- drivers/input/misc/qcom-hv-haptics.c | 312 +++++++++++++++++++++++---- 1 file changed, 267 insertions(+), 45 deletions(-) diff --git a/drivers/input/misc/qcom-hv-haptics.c b/drivers/input/misc/qcom-hv-haptics.c index 522608034ada..a3f57d40ed2e 100644 --- a/drivers/input/misc/qcom-hv-haptics.c +++ b/drivers/input/misc/qcom-hv-haptics.c @@ -29,7 +29,7 @@ #define AUTO_RES_CAL_DONE_BIT BIT(5) #define CAL_TLRA_CL_STS_MSB_MASK GENMASK(4, 0) /* STATUS_DATA_MSB definition in V2 while MOD_STATUS_SEL is 3 */ -#define LAST_GOOD_TLRA_CL_MASK GENMASK(4, 0) +#define LAST_GOOD_TLRA_CL_MASK GENMASK(4, 0) /* STATUS_DATA_MSB definition in V1 while MOD_STATUS_SEL is 5 */ #define FIFO_REAL_TIME_FILL_STATUS_MASK_V1 GENMASK(6, 0) /* STATUS DATA_MSB definition in V2 while MOD_STATUS_SEL is 5 */ @@ -104,6 +104,14 @@ #define HAP_CFG_AUTORES_CFG_REG 0x63 #define AUTORES_EN_BIT BIT(7) +#define AUTORES_EN_DLY_MASK GENMASK(5, 2) +#define AUTORES_EN_DLY_1_CYCLE 0x2 +#define AUTORES_EN_DLY_SHIFT 2 +#define AUTORES_ERR_WINDOW_MASK GENMASK(1, 0) +#define AUTORES_ERR_WINDOW_12P5_PERCENT 0x0 +#define AUTORES_ERR_WINDOW_25_PERCENT 0x1 +#define AUTORES_ERR_WINDOW_50_PERCENT 0x2 +#define AUTORES_ERR_WINDOW_100_PERCENT 0x3 #define HAP_CFG_AUTORES_ERR_RECOVERY_REG 0x64 #define EN_HW_RECOVERY_BIT BIT(1) @@ -117,6 +125,7 @@ #define HAP_CFG_VMAX_HDRM_REG 0x67 #define VMAX_HDRM_MASK GENMASK(6, 0) #define VMAX_HDRM_STEP_MV 50 +#define VMAX_HDRM_MAX_MV 6350 #define HAP_CFG_MOD_STATUS_SEL_REG 0x70 #define MOD_STATUS_SEL_CAL_TLRA_CL_STS_VAL 0 @@ -128,8 +137,8 @@ #define MOD_STATUS_XT_V2_FIFO_FILL_STATUS_VAL 0x80 #define MOD_STATUS_XT_SEL_LAST_GOOD_TLRA_VAL 0 -#define HAP_CFG_CAL_EN_REG 0x72 -#define CAL_RC_CLK_MASK GENMASK(3, 2) +#define HAP_CFG_CAL_EN_REG 0x72 +#define CAL_RC_CLK_MASK GENMASK(3, 2) #define CAL_RC_CLK_SHIFT 2 #define CAL_RC_CLK_MANUAL_VAL 2 @@ -172,6 +181,7 @@ /* shared registers between V1 and V2 chips */ #define HAP_PTN_DIRECT_PLAY_REG 0x26 +#define DIRECT_PLAY_MAX_AMPLITUDE 0xFF #define HAP_PTN_AUTORES_CAL_CFG_REG 0x28 @@ -364,9 +374,11 @@ struct haptics_play_info { struct haptics_effect *effect; struct brake_cfg *brake; struct fifo_play_status fifo_status; + struct mutex lock; u32 vmax_mv; u32 length_us; enum pattern_src pattern_src; + bool in_calibration; }; struct haptics_hw_config { @@ -399,6 +411,7 @@ struct haptics_chip { struct regulator_dev *swr_slave_rdev; struct mutex irq_lock; struct nvmem_cell *cl_brake_nvmem; + struct class hap_class; int fifo_empty_irq; u32 effects_count; u32 cfg_addr_base; @@ -661,8 +674,8 @@ static int get_brake_play_length_us(struct brake_cfg *brake, u32 t_lra_us) return t_lra_us * (i + 1); } -#define V1_CL_TLRA_STEP_NS_AUTO_RES_CAL_NOT_DONE 5000 -#define V1_CL_TLRA_STEP_NS_AUTO_RES_CAL_DONE 3333 +#define V1_CL_TLRA_STEP_AUTO_RES_CAL_NOT_DONE_NS 5000 +#define V1_CL_TLRA_STEP_AUTO_RES_CAL_DONE_NS 3333 static int haptics_get_closeloop_lra_period_v1( struct haptics_chip *chip) { @@ -694,9 +707,9 @@ static int haptics_get_closeloop_lra_period_v1( */ auto_res_cal_done = !!(val[0] & AUTO_RES_CAL_DONE_BIT); if (auto_res_cal_done) - step_ns = V1_CL_TLRA_STEP_NS_AUTO_RES_CAL_DONE; + step_ns = V1_CL_TLRA_STEP_AUTO_RES_CAL_DONE_NS; else - step_ns = V1_CL_TLRA_STEP_NS_AUTO_RES_CAL_NOT_DONE; + step_ns = V1_CL_TLRA_STEP_AUTO_RES_CAL_NOT_DONE_NS; tmp = ((val[0] & CAL_TLRA_CL_STS_MSB_MASK) << 8) | val[1]; config->cl_t_lra_us = (tmp * step_ns) / 1000; @@ -778,6 +791,26 @@ static int haptics_set_vmax_mv(struct haptics_chip *chip, u32 vmax_mv) return rc; } +static int haptics_set_vmax_headroom_mv(struct haptics_chip *chip, u32 hdrm_mv) +{ + int rc = 0; + u8 val; + + if (hdrm_mv > VMAX_HDRM_MAX_MV) { + dev_err(chip->dev, "headroom (%d) exceed the max value: %d\n", + hdrm_mv, VMAX_HDRM_MAX_MV); + return -EINVAL; + } + + val = hdrm_mv / VMAX_HDRM_STEP_MV; + rc = haptics_write(chip, chip->cfg_addr_base, + HAP_CFG_VMAX_HDRM_REG, &val, 1); + if (rc < 0) + dev_err(chip->dev, "config VMAX_HDRM failed, rc=%d\n", rc); + + return rc; +} + static int haptics_enable_autores(struct haptics_chip *chip, bool en) { int rc; @@ -792,6 +825,18 @@ static int haptics_enable_autores(struct haptics_chip *chip, bool en) return rc; } +static int haptics_set_direct_play(struct haptics_chip *chip, u8 amplitude) +{ + int rc; + + rc = haptics_write(chip, chip->ptn_addr_base, + HAP_PTN_DIRECT_PLAY_REG, &litude, 1); + if (rc < 0) + dev_err(chip->dev, "config DIRECT_PLAY failed, rc=%d\n", rc); + + return rc; +} + static int haptics_enable_play(struct haptics_chip *chip, bool en) { struct haptics_play_info *play = &chip->play; @@ -1010,8 +1055,7 @@ static int haptics_get_available_fifo_memory(struct haptics_chip *chip) static int haptics_adjust_rc_clk(struct haptics_chip *chip) { - int rc; - int freq_diff, cal_count, f_lra, cl_f_lra; + int rc, freq_diff, cal_count, f_lra, cl_f_lra; u8 val[2]; if (!chip->config.t_lra_us || !chip->config.cl_t_lra_us) @@ -1182,51 +1226,56 @@ static int haptics_set_fifo(struct haptics_chip *chip, struct fifo_cfg *fifo) return haptics_set_fifo_empty_threshold(chip, fifo_thresh); } -static int haptics_set_direct_play(struct haptics_chip *chip) +static int haptics_load_constant_effect(struct haptics_chip *chip, u8 amplitude) { struct haptics_play_info *play = &chip->play; int rc = 0; - u8 val; + + mutex_lock(&chip->play.lock); + if (chip->play.in_calibration) { + dev_err(chip->dev, "calibration in progress, ignore playing constant effect\n"); + rc = -EBUSY; + goto unlock; + } /* configure VMAX in case it was changed in previous effect playing */ rc = haptics_set_vmax_mv(chip, chip->config.vmax_mv); if (rc < 0) - return rc; - - /* Set DIRECT_PLAY amplitude */ - val = play->vmax_mv / VMAX_STEP_MV; - rc = haptics_write(chip, chip->ptn_addr_base, - HAP_PTN_DIRECT_PLAY_REG, &val, 1); - if (rc < 0) - return rc; + goto unlock; /* Config brake settings if it's necessary */ play->brake = &chip->config.brake; if (play->brake) { rc = haptics_set_brake(chip, play->brake); if (rc < 0) - return rc; + goto unlock; } + rc = haptics_set_direct_play(chip, amplitude); + if (rc < 0) + goto unlock; + /* Always enable LRA auto resonance for DIRECT_PLAY */ rc = haptics_enable_autores(chip, !chip->config.is_erm); if (rc < 0) - return rc; + goto unlock; play->pattern_src = DIRECT_PLAY; - return 0; +unlock: + mutex_unlock(&chip->play.lock); + return rc; } static int haptics_load_predefined_effect(struct haptics_chip *chip, - int effect_idx) + struct haptics_effect *effect) { struct haptics_play_info *play = &chip->play; int rc; - if (effect_idx >= chip->effects_count) + if (effect == NULL) return -EINVAL; - play->effect = &chip->effects[effect_idx]; + play->effect = effect; /* Clamp VMAX for different vibration strength */ rc = haptics_set_vmax_mv(chip, play->vmax_mv); if (rc < 0) @@ -1393,12 +1442,19 @@ static int haptics_load_custom_effect(struct haptics_chip *chip, fifo->play_length_us = get_fifo_play_length_us(fifo, chip->custom_effect->t_lra_us); + mutex_lock(&chip->play.lock); + if (chip->play.in_calibration) { + dev_err(chip->dev, "calibration in progress, ignore playing custom effect\n"); + rc = -EBUSY; + goto unlock; + } + play->effect = chip->custom_effect; play->brake = NULL; play->vmax_mv = (magnitude * chip->custom_effect->vmax_mv) / 0x7fff; rc = haptics_set_vmax_mv(chip, play->vmax_mv); if (rc < 0) - goto cleanup; + goto unlock; rc = haptics_enable_autores(chip, !play->effect->auto_res_disable); if (rc < 0) @@ -1407,9 +1463,12 @@ static int haptics_load_custom_effect(struct haptics_chip *chip, play->pattern_src = FIFO; rc = haptics_set_fifo(chip, play->effect->fifo); if (rc < 0) - goto cleanup; + goto unlock; + mutex_unlock(&chip->play.lock); return 0; +unlock: + mutex_unlock(&chip->play.lock); cleanup: kfree(fifo->samples); fifo->samples = NULL; @@ -1456,16 +1515,25 @@ static int haptics_load_periodic_effect(struct haptics_chip *chip, return -EINVAL; } + 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); - rc = haptics_load_predefined_effect(chip, i); + + if (chip->play.in_calibration) { + dev_err(chip->dev, "calibration in progress, ignore playing predefined effect\n"); + rc = -EBUSY; + goto unlock; + } + + rc = haptics_load_predefined_effect(chip, &chip->effects[i]); if (rc < 0) { dev_err(chip->dev, "Play predefined effect%d failed, rc=%d\n", chip->effects[i].id, rc); - return rc; + goto unlock; } + mutex_unlock(&chip->play.lock); play->length_us = get_play_length_us(play); custom_data[CUSTOM_DATA_TIMEOUT_SEC_IDX] = @@ -1477,25 +1545,29 @@ static int haptics_load_periodic_effect(struct haptics_chip *chip, return -EFAULT; return 0; +unlock: + mutex_unlock(&chip->play.lock); + return rc; } static int haptics_upload_effect(struct input_dev *dev, struct ff_effect *effect, struct ff_effect *old) { struct haptics_chip *chip = input_get_drvdata(dev); - struct haptics_hw_config *config = &chip->config; - struct haptics_play_info *play = &chip->play; + u32 length_us, tmp; s16 level; + u8 amplitude; int rc = 0; switch (effect->type) { case FF_CONSTANT: - play->length_us = effect->replay.length * USEC_PER_MSEC; + length_us = effect->replay.length * USEC_PER_MSEC; level = effect->u.constant.level; - play->vmax_mv = (level * config->vmax_mv) / 0x7fff; - dev_dbg(chip->dev, "upload constant effect, length = %dus, vmax_mv = %d\n", - play->length_us, play->vmax_mv); - haptics_set_direct_play(chip); + tmp = level * DIRECT_PLAY_MAX_AMPLITUDE; + amplitude = tmp / 0x7fff; + dev_dbg(chip->dev, "upload constant effect, length = %dus, amplitude = %d\n", + length_us, amplitude); + haptics_load_constant_effect(chip, amplitude); if (rc < 0) { dev_err(chip->dev, "set direct play failed, rc=%d\n", rc); @@ -1657,12 +1729,26 @@ static int haptics_store_cl_brake_settings(struct haptics_chip *chip) return rc; } +static int haptics_config_openloop_lra_period(struct haptics_chip *chip, + u32 t_lra_us) +{ + u32 tmp; + u8 val[2]; + + tmp = t_lra_us / TLRA_STEP_US; + val[0] = (tmp >> 8) & TLRA_OL_MSB_MASK; + val[1] = tmp & TLRA_OL_LSB_MASK; + + return haptics_write(chip, chip->cfg_addr_base, + HAP_CFG_TLRA_OL_HIGH_REG, val, 2); +} + static int haptics_hw_init(struct haptics_chip *chip) { struct haptics_hw_config *config = &chip->config; struct haptics_effect *effect; - int rc = 0, tmp, i; - u8 val[2]; + int rc = 0, i; + u8 val; /* Store CL brake settings */ rc = haptics_store_cl_brake_settings(chip); @@ -1682,13 +1768,13 @@ static int haptics_hw_init(struct haptics_chip *chip) return rc; /* Config brake mode and waveform shape */ - val[0] = (config->brake.mode << BRAKE_MODE_SHIFT) + val = (config->brake.mode << BRAKE_MODE_SHIFT) | config->brake.sine_gain << BRAKE_SINE_GAIN_SHIFT | config->brake.brake_wf; rc = haptics_masked_write(chip, chip->cfg_addr_base, HAP_CFG_BRAKE_MODE_CFG_REG, BRAKE_MODE_MASK | BRAKE_SINE_GAIN_MASK - | BRAKE_WF_SEL_MASK, val[0]); + | BRAKE_WF_SEL_MASK, val); if (rc < 0) return rc; @@ -1700,11 +1786,7 @@ static int haptics_hw_init(struct haptics_chip *chip) return rc; /* Config T_LRA */ - tmp = config->t_lra_us / TLRA_STEP_US; - val[0] = (tmp >> 8) & TLRA_OL_MSB_MASK; - val[1] = tmp & TLRA_OL_LSB_MASK; - rc = haptics_write(chip, chip->cfg_addr_base, - HAP_CFG_TLRA_OL_HIGH_REG, val, 2); + rc = haptics_config_openloop_lra_period(chip, chip->config.cl_t_lra_us); if (rc < 0) return rc; @@ -2971,6 +3053,136 @@ static int haptics_init_swr_slave_regulator(struct haptics_chip *chip) return rc; } +#define LRA_CALIBRATION_VMAX_HDRM_MV 500 +static int haptics_start_lra_calibration(struct haptics_chip *chip) +{ + int rc; + u8 autores_cfg; + + mutex_lock(&chip->play.lock); + /* + * Ignore calibration if it's in FIFO playing to avoid + * messing up the FIFO playing status + */ + if ((chip->play.pattern_src == FIFO) && + atomic_read(&chip->play.fifo_status.is_busy)) { + dev_err(chip->dev, "In FIFO playing, ignore calibration\n"); + rc = -EBUSY; + goto unlock; + } + + /* Stop other mode playing if there is any */ + rc = haptics_enable_play(chip, false); + if (rc < 0) { + dev_err(chip->dev, "Stop playing failed, rc=%d\n", rc); + goto unlock; + } + + chip->play.in_calibration = true; + + 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); + 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_25_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); + if (rc < 0) { + dev_err(chip->dev, "Start calibration failed, rc=%d\n", rc); + goto restore; + } + + /* wait for ~60ms to get the LRA calibration result */ + usleep_range(60000, 65000); + + 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); + return rc; +} + +static ssize_t lra_calibration_store(struct class *c, + struct class_attribute *attr, const char *buf, size_t count) +{ + struct haptics_chip *chip = container_of(c, + struct haptics_chip, hap_class); + bool val; + int rc; + + if (kstrtobool(buf, &val)) + return -EINVAL; + + if (val) { + rc = haptics_start_lra_calibration(chip); + if (rc < 0) + return rc; + } + + return count; +} +static CLASS_ATTR_WO(lra_calibration); + +static ssize_t lra_frequency_hz_show(struct class *c, + struct class_attribute *attr, char *buf) +{ + struct haptics_chip *chip = container_of(c, + struct haptics_chip, hap_class); + u32 cl_f_lra; + + if (chip->config.cl_t_lra_us == 0) + return -EINVAL; + + cl_f_lra = USEC_PER_SEC / chip->config.cl_t_lra_us; + return scnprintf(buf, PAGE_SIZE, "%d Hz\n", cl_f_lra); +} +static CLASS_ATTR_RO(lra_frequency_hz); + +static struct attribute *hap_class_attrs[] = { + &class_attr_lra_calibration.attr, + &class_attr_lra_frequency_hz.attr, + NULL, +}; +ATTRIBUTE_GROUPS(hap_class); + static int haptics_probe(struct platform_device *pdev) { struct haptics_chip *chip; @@ -3028,6 +3240,7 @@ static int haptics_probe(struct platform_device *pdev) } mutex_init(&chip->irq_lock); + mutex_init(&chip->play.lock); disable_irq_nosync(chip->fifo_empty_irq); chip->fifo_empty_irq_en = false; @@ -3070,6 +3283,14 @@ static int haptics_probe(struct platform_device *pdev) } dev_set_drvdata(chip->dev, chip); + chip->hap_class.name = "qcom-haptics"; + chip->hap_class.class_groups = hap_class_groups; + rc = class_register(&chip->hap_class); + if (rc < 0) { + dev_err(chip->dev, "register hap_class failed, rc=%d\n"); + goto destroy_ff; + } + #ifdef CONFIG_DEBUG_FS rc = haptics_create_debugfs(chip); if (rc < 0) @@ -3085,6 +3306,7 @@ static int haptics_remove(struct platform_device *pdev) { struct haptics_chip *chip = dev_get_drvdata(&pdev->dev); + class_unregister(&chip->hap_class); #ifdef CONFIG_DEBUG_FS debugfs_remove_recursive(chip->debugfs_dir); #endif From 0368a68e2e99b5dbb269801864debdfe85c0ee2f Mon Sep 17 00:00:00 2001 From: Fenglin Wu Date: Wed, 13 May 2020 17:10:48 +0800 Subject: [PATCH 17/76] input: qcom-hv-haptics: Add debugfs files for more brake settings Add debugfs parameters per effect to enable brake and brake sine waveform gain setting. Meanwhile, change to print the FIFO samples with signed magnitude values so that they can be used offline to plot pattern waveform. Change-Id: Iaeeeedbba570333c8c4fa9793e2b4e9c1b4af09a Signed-off-by: Fenglin Wu --- drivers/input/misc/qcom-hv-haptics.c | 72 ++++++++++++++++++++++++---- 1 file changed, 63 insertions(+), 9 deletions(-) diff --git a/drivers/input/misc/qcom-hv-haptics.c b/drivers/input/misc/qcom-hv-haptics.c index a3f57d40ed2e..d7df52f1265b 100644 --- a/drivers/input/misc/qcom-hv-haptics.c +++ b/drivers/input/misc/qcom-hv-haptics.c @@ -502,8 +502,8 @@ static void __dump_effects(struct haptics_chip *chip) pos = 0; pos += scnprintf(str, size, "%s", "FIFO data: "); for (j = 0; j < effect->fifo->num_s; j++) - pos += scnprintf(str + pos, size - pos, "%#x ", - effect->fifo->samples[j]); + pos += scnprintf(str + pos, size - pos, "%d ", + (s8)effect->fifo->samples[j]); dev_dbg(chip->dev, "%s\n", str); kfree(str); @@ -2058,7 +2058,7 @@ static int pattern_play_rate_us_dbgfs_write(void *data, u64 val) } DEFINE_DEBUGFS_ATTRIBUTE(pattern_play_rate_dbgfs_ops, pattern_play_rate_us_dbgfs_read, - pattern_play_rate_us_dbgfs_write, "%lld\n"); + pattern_play_rate_us_dbgfs_write, "%llu\n"); static ssize_t fifo_s_dbgfs_read(struct file *fp, char __user *buf, size_t count, loff_t *ppos) @@ -2076,7 +2076,7 @@ static ssize_t fifo_s_dbgfs_read(struct file *fp, for (i = 0; i < fifo->num_s; i++) pos += scnprintf(kbuf + pos, size - pos, - "0x%03x ", fifo->samples[i]); + "%d ", (s8)fifo->samples[i]); pos += scnprintf(kbuf + pos, size - pos, "%s", "\n"); rc = simple_read_from_buffer(buf, count, ppos, kbuf, pos); @@ -2092,7 +2092,7 @@ static ssize_t fifo_s_dbgfs_write(struct file *fp, struct fifo_cfg *fifo = effect->fifo; char *kbuf, *token; int rc, i = 0; - u32 val; + int val; u8 *samples; kbuf = kzalloc(count + 1, GFP_KERNEL); @@ -2115,7 +2115,7 @@ static ssize_t fifo_s_dbgfs_write(struct file *fp, } while ((token = strsep(&kbuf, " ")) != NULL) { - rc = kstrtouint(token, 0, &val); + rc = kstrtoint(token, 0, &val); if (rc < 0) { rc = -EINVAL; goto exit2; @@ -2124,7 +2124,7 @@ static ssize_t fifo_s_dbgfs_write(struct file *fp, if (val > 0xff) val = 0xff; - samples[i++] = val; + samples[i++] = (u8)val; /* only support fifo pattern no longer than before */ if (i >= fifo->num_s) break; @@ -2182,7 +2182,7 @@ static int fifo_period_dbgfs_write(void *data, u64 val) DEFINE_DEBUGFS_ATTRIBUTE(fifo_period_dbgfs_ops, fifo_period_dbgfs_read, - fifo_period_dbgfs_write, "%lld\n"); + fifo_period_dbgfs_write, "%llu\n"); static ssize_t brake_s_dbgfs_read(struct file *fp, char __user *buf, size_t count, loff_t *ppos) @@ -2327,6 +2327,50 @@ static const struct file_operations brake_mode_dbgfs_ops = { .open = simple_open, }; +static int brake_en_dbgfs_read(void *data, u64 *val) +{ + struct haptics_effect *effect = data; + + *val = !effect->brake->disabled; + + return 0; +} + +static int brake_en_dbgfs_write(void *data, u64 val) +{ + struct haptics_effect *effect = data; + + effect->brake->disabled = !val; + + return 0; +} +DEFINE_DEBUGFS_ATTRIBUTE(brake_en_dbgfs_ops, brake_en_dbgfs_read, + brake_en_dbgfs_write, "%llu\n"); + +static int brake_sine_gain_dbgfs_read(void *data, u64 *val) +{ + struct haptics_effect *effect = data; + + *val = effect->brake->sine_gain; + + return 0; +} + +static int brake_sine_gain_dbgfs_write(void *data, u64 val) +{ + struct haptics_effect *effect = data; + + if (val > BRAKE_SINE_GAIN_X8) + return -EINVAL; + + effect->brake->sine_gain = val; + + return 0; +} +DEFINE_DEBUGFS_ATTRIBUTE(brake_sine_gain_dbgfs_ops, + brake_sine_gain_dbgfs_read, + brake_sine_gain_dbgfs_write, "%llu\n"); + static int preload_effect_idx_dbgfs_read(void *data, u64 *val) { struct haptics_chip *chip = data; @@ -2373,7 +2417,7 @@ static int preload_effect_idx_dbgfs_write(void *data, u64 val) DEFINE_DEBUGFS_ATTRIBUTE(preload_effect_idx_dbgfs_ops, preload_effect_idx_dbgfs_read, - preload_effect_idx_dbgfs_write, "%lld\n"); + preload_effect_idx_dbgfs_write, "%llu\n"); static int haptics_add_effects_debugfs(struct haptics_effect *effect, struct dentry *dir) @@ -2436,6 +2480,16 @@ static int haptics_add_effects_debugfs(struct haptics_effect *effect, effect, &brake_mode_dbgfs_ops); if (IS_ERR(file)) return PTR_ERR(file); + + file = debugfs_create_file_unsafe("enable", 0644, brake_dir, + effect, &brake_en_dbgfs_ops); + if (IS_ERR(file)) + return PTR_ERR(file); + + file = debugfs_create_file_unsafe("sine_gain", 0644, brake_dir, + effect, &brake_sine_gain_dbgfs_ops); + if (IS_ERR(file)) + return PTR_ERR(file); } return 0; From c834c967bf8182c1623f995a35f5670c6b57889a Mon Sep 17 00:00:00 2001 From: Fenglin Wu Date: Fri, 15 May 2020 13:59:36 +0800 Subject: [PATCH 18/76] input: qcom-hv-haptics: check FIFO fill status before stopping play Check the FIFO real time fill status and only stop FIFO playing in FIFO empty interrupt handler after confirming all FIFO samples have been played. Change-Id: I634f424c960893e22456734f30ae28f5030ca9e1 Signed-off-by: Fenglin Wu --- drivers/input/misc/qcom-hv-haptics.c | 85 +++++++++++++++++----------- 1 file changed, 52 insertions(+), 33 deletions(-) diff --git a/drivers/input/misc/qcom-hv-haptics.c b/drivers/input/misc/qcom-hv-haptics.c index d7df52f1265b..2ae879327dde 100644 --- a/drivers/input/misc/qcom-hv-haptics.c +++ b/drivers/input/misc/qcom-hv-haptics.c @@ -1629,6 +1629,33 @@ static void haptics_fifo_empty_irq_config(struct haptics_chip *chip, mutex_unlock(&chip->irq_lock); } +static int haptics_stop_fifo_play(struct haptics_chip *chip) +{ + int rc; + + if (atomic_read(&chip->play.fifo_status.is_busy) == 0) { + dev_dbg(chip->dev, "FIFO playing is not in progress\n"); + return 0; + } + + rc = haptics_enable_play(chip, false); + if (rc < 0) + return rc; + + /* restore FIFO play rate back to T_LRA */ + rc = haptics_set_fifo_playrate(chip, T_LRA); + if (rc < 0) + return rc; + + haptics_fifo_empty_irq_config(chip, false); + kfree(chip->custom_effect->fifo->samples); + chip->custom_effect->fifo->samples = NULL; + + atomic_set(&chip->play.fifo_status.is_busy, 0); + dev_dbg(chip->dev, "stopped FIFO playing successfully\n"); + return 0; +} + static int haptics_playback(struct input_dev *dev, int effect_id, int val) { struct haptics_chip *chip = input_get_drvdata(dev); @@ -1644,7 +1671,8 @@ static int haptics_playback(struct input_dev *dev, int effect_id, int val) if (play->pattern_src == FIFO) haptics_fifo_empty_irq_config(chip, true); } else { - if (atomic_read(&play->fifo_status.is_busy)) { + if (play->pattern_src == FIFO && + atomic_read(&play->fifo_status.is_busy)) { dev_dbg(chip->dev, "FIFO playing is not done yet, defer stopping in erase\n"); return 0; } @@ -1663,25 +1691,16 @@ static int haptics_erase(struct input_dev *dev, int effect_id) if ((play->pattern_src == FIFO) && atomic_read(&play->fifo_status.is_busy)) { - dev_dbg(chip->dev, "cancelling FIFO playing\n"); - atomic_set(&play->fifo_status.cancelled, 1); + if (atomic_read(&play->fifo_status.written_done) == 0) { + dev_dbg(chip->dev, "cancelling FIFO playing\n"); + atomic_set(&play->fifo_status.cancelled, 1); + } - rc = haptics_enable_play(chip, false); - if (rc < 0) + rc = haptics_stop_fifo_play(chip); + if (rc < 0) { + dev_err(chip->dev, "stop FIFO playing failed, rc=%d\n"); return rc; - - atomic_set(&play->fifo_status.is_busy, 0); - - /* free custom_effect FIFO samples buffer after stopping play */ - kfree(chip->custom_effect->fifo->samples); - chip->custom_effect->fifo->samples = NULL; - - /* restore FIFO play rate back to T_LRA */ - rc = haptics_set_fifo_playrate(chip, T_LRA); - if (rc < 0) - return rc; - - haptics_fifo_empty_irq_config(chip, false); + } } return 0; @@ -1825,24 +1844,24 @@ static irqreturn_t fifo_empty_irq_handler(int irq, void *data) int rc, num; if (atomic_read(&chip->play.fifo_status.written_done) == 1) { - dev_dbg(chip->dev, "FIFO data is done playing\n"); - rc = haptics_enable_play(chip, false); + /* + * Check the FIFO real time fill status before stopping + * play to make sure that all FIFO samples can be played + * successfully. If there are still samples left in FIFO + * memory, defer the stop into erase() function. + */ + num = haptics_get_available_fifo_memory(chip); + if (num != MAX_FIFO_SAMPLES(chip)) { + dev_dbg(chip->dev, "%d FIFO samples still in playing\n", + MAX_FIFO_SAMPLES(chip) - num); + return IRQ_HANDLED; + } + + rc = haptics_stop_fifo_play(chip); if (rc < 0) return IRQ_HANDLED; - /* restore FIFO play rate back to T_LRA */ - rc = haptics_set_fifo_playrate(chip, T_LRA); - if (rc < 0) - return IRQ_HANDLED; - - haptics_fifo_empty_irq_config(chip, false); - - /* free custom_effect FIFO samples after playing is done */ - kfree(chip->custom_effect->fifo->samples); - chip->custom_effect->fifo->samples = NULL; - - atomic_set(&chip->play.fifo_status.written_done, 0); - atomic_set(&chip->play.fifo_status.is_busy, 0); + dev_dbg(chip->dev, "FIFO playing is done\n"); } else { if (atomic_read(&status->cancelled) == 1) { dev_dbg(chip->dev, "FIFO programming got cancelled\n"); From 6daa29c301fc9068b46c438eaee546c6c41e5924 Mon Sep 17 00:00:00 2001 From: Fenglin Wu Date: Wed, 20 May 2020 12:14:43 +0800 Subject: [PATCH 19/76] input: qcom-hv-haptics: adjust T_LRA for auto RC CLK calibration case When auto RC clock calibration mode is selected, the 32.768 KHz sleep clock is used as the reference clock but the hardware module assumes the reference clock is 32 KHz. This results in inaccuracy on output drive frequency. Correct this by scaling the LRA period using a factor of 1.024 in auto RC clock calibration case. Change-Id: I1d4f6cb90c00ffbd9932928e64666a2122d8d4af Signed-off-by: Fenglin Wu --- drivers/input/misc/qcom-hv-haptics.c | 40 ++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/drivers/input/misc/qcom-hv-haptics.c b/drivers/input/misc/qcom-hv-haptics.c index 2ae879327dde..3193746e3858 100644 --- a/drivers/input/misc/qcom-hv-haptics.c +++ b/drivers/input/misc/qcom-hv-haptics.c @@ -140,6 +140,7 @@ #define HAP_CFG_CAL_EN_REG 0x72 #define CAL_RC_CLK_MASK GENMASK(3, 2) #define CAL_RC_CLK_SHIFT 2 +#define CAL_RC_CLK_AUTO_VAL 1 #define CAL_RC_CLK_MANUAL_VAL 2 /* version register definitions for HAPTICS_PATTERN module */ @@ -676,6 +677,26 @@ static int get_brake_play_length_us(struct brake_cfg *brake, u32 t_lra_us) #define V1_CL_TLRA_STEP_AUTO_RES_CAL_NOT_DONE_NS 5000 #define V1_CL_TLRA_STEP_AUTO_RES_CAL_DONE_NS 3333 +#define AUTO_CAL_CLK_SCALE_DEN 1000 +#define AUTO_CAL_CLK_SCALE_NUM 1024 +static int haptics_adjust_lra_period(struct haptics_chip *chip, u32 *t_lra_us) +{ + int rc; + u8 val; + + rc = haptics_read(chip, chip->cfg_addr_base, + HAP_CFG_CAL_EN_REG, &val, 1); + if (rc < 0) + return rc; + + if ((val & CAL_RC_CLK_MASK) == + (CAL_RC_CLK_AUTO_VAL << CAL_RC_CLK_SHIFT)) + *t_lra_us = div_u64((u64)(*t_lra_us) * AUTO_CAL_CLK_SCALE_NUM, + AUTO_CAL_CLK_SCALE_DEN); + + return 0; +} + static int haptics_get_closeloop_lra_period_v1( struct haptics_chip *chip) { @@ -715,7 +736,6 @@ static int haptics_get_closeloop_lra_period_v1( config->cl_t_lra_us = (tmp * step_ns) / 1000; return 0; - } #define CL_TLRA_STEP_NS 1666 @@ -765,6 +785,10 @@ static int haptics_get_closeloop_lra_period(struct haptics_chip *chip) return rc; } + rc = haptics_adjust_lra_period(chip, &chip->config.cl_t_lra_us); + if (rc < 0) + return rc; + dev_dbg(chip->dev, "OL_TLRA %u us, CL_TLRA %u us\n", chip->config.t_lra_us, chip->config.cl_t_lra_us); @@ -895,6 +919,7 @@ static int haptics_set_pattern(struct haptics_chip *chip, u8 values[SAMPLES_PER_PATTERN * 3] = { 0 }; u8 ptn_tlra_addr, ptn_cfg_addr; int i, rc, tmp; + u32 play_rate_us; if (src != PATTERN1 && src != PATTERN2) { dev_err(chip->dev, "no pattern src specified!\n"); @@ -908,8 +933,14 @@ static int haptics_set_pattern(struct haptics_chip *chip, ptn_cfg_addr = HAP_PTN_PTRN2_CFG_REG; } + /* Adjust T_LRA before programming it into HW */ + play_rate_us = pattern->play_rate_us; + rc = haptics_adjust_lra_period(chip, &play_rate_us); + if (rc < 0) + return rc; + /* Configure T_LRA for this pattern */ - tmp = pattern->play_rate_us / TLRA_STEP_US; + tmp = play_rate_us / TLRA_STEP_US; values[0] = (tmp >> 8) & TLRA_OL_MSB_MASK; values[1] = tmp & TLRA_OL_LSB_MASK; rc = haptics_write(chip, chip->ptn_addr_base, ptn_tlra_addr, @@ -1753,6 +1784,11 @@ static int haptics_config_openloop_lra_period(struct haptics_chip *chip, { u32 tmp; u8 val[2]; + int rc; + + rc = haptics_adjust_lra_period(chip, &t_lra_us); + if (rc < 0) + return rc; tmp = t_lra_us / TLRA_STEP_US; val[0] = (tmp >> 8) & TLRA_OL_MSB_MASK; From c6ebd39a1e95084a66d817d34c49c7deaeb6bb55 Mon Sep 17 00:00:00 2001 From: Fenglin Wu Date: Thu, 21 May 2020 15:06:19 +0800 Subject: [PATCH 20/76] input: qcom-hv-haptics: detect 5V variant and update gain setting PM8350B has a variant which only supports haptics with Vmax up to 5V. Detect this variant and update Vmax settings accordingly to make sure the gain setting change can be correctly reflected. Change-Id: I916fba21d8a441b9e95edeef5af715d451ba70cf Signed-off-by: Fenglin Wu --- drivers/input/misc/qcom-hv-haptics.c | 63 +++++++++++++++++++++++----- 1 file changed, 52 insertions(+), 11 deletions(-) diff --git a/drivers/input/misc/qcom-hv-haptics.c b/drivers/input/misc/qcom-hv-haptics.c index 3193746e3858..1d950dbddc78 100644 --- a/drivers/input/misc/qcom-hv-haptics.c +++ b/drivers/input/misc/qcom-hv-haptics.c @@ -54,6 +54,7 @@ #define HAP_CFG_VMAX_REG 0x48 #define VMAX_STEP_MV 50 #define MAX_VMAX_MV 11000 +#define CLAMPED_VMAX_MV 5000 #define DEFAULT_VMAX_MV 5000 #define HAP_CFG_DRV_WF_SEL_REG 0x49 @@ -202,6 +203,16 @@ #define HAP_PTN_BRAKE_AMP_REG 0x70 +/* register in HBOOST module */ +#define HAP_BOOST_REVISION1 0x00 +#define HAP_BOOST_REVISION2 0x01 +#define HAP_BOOST_V0P0 0x0000 +#define HAP_BOOST_V0P1 0x0001 + +#define HAP_BOOST_V0P0_CLAMP_REG 0xF1 +#define HAP_BOOST_V0P1_CLAMP_REG 0x70 +#define CLAMP_5V_BIT BIT(0) + /* constant parameters */ #define SAMPLES_PER_PATTERN 8 #define BRAKE_SAMPLE_COUNT 8 @@ -221,6 +232,9 @@ ((chip)->ptn_revision == HAP_PTN_V1 ? 48 : 280) #define is_between(val, min, max) \ (((min) <= (max)) && ((min) <= (val)) && ((val) <= (max))) +#define HAP_BOOST_CLAMP_5V_REG_OFFSET(chip) \ + ((chip)->hbst_revision == HAP_BOOST_V0P0 ? \ + HAP_BOOST_V0P0_CLAMP_REG : HAP_BOOST_V0P1_CLAMP_REG) enum drv_sig_shape { WF_SQUARE, @@ -417,9 +431,12 @@ struct haptics_chip { u32 effects_count; u32 cfg_addr_base; u32 ptn_addr_base; + u32 hbst_addr_base; u8 ptn_revision; + u16 hbst_revision; bool fifo_empty_irq_en; bool swr_slave_enabled; + bool clamp_at_5v; }; static int haptics_read(struct haptics_chip *chip, @@ -806,6 +823,9 @@ static int haptics_set_vmax_mv(struct haptics_chip *chip, u32 vmax_mv) return -EINVAL; } + if (chip->clamp_at_5v && (vmax_mv > CLAMPED_VMAX_MV)) + vmax_mv = CLAMPED_VMAX_MV; + val = vmax_mv / VMAX_STEP_MV; rc = haptics_write(chip, chip->cfg_addr_base, HAP_CFG_VMAX_REG, &val, 1); @@ -1740,8 +1760,8 @@ static int haptics_erase(struct input_dev *dev, int effect_id) static void haptics_set_gain(struct input_dev *dev, u16 gain) { struct haptics_chip *chip = input_get_drvdata(dev); - struct haptics_hw_config *config = &chip->config; struct haptics_play_info *play = &chip->play; + u32 vmax_mv; if (gain == 0) return; @@ -1749,7 +1769,11 @@ static void haptics_set_gain(struct input_dev *dev, u16 gain) if (gain > 0x7fff) gain = 0x7fff; - play->vmax_mv = ((u32)(gain * config->vmax_mv)) / 0x7fff; + vmax_mv = play->effect->vmax_mv; + if (chip->clamp_at_5v && (vmax_mv > CLAMPED_VMAX_MV)) + vmax_mv = CLAMPED_VMAX_MV; + + play->vmax_mv = ((u32)(gain * vmax_mv)) / 0x7fff; haptics_set_vmax_mv(chip, play->vmax_mv); } @@ -1803,13 +1827,25 @@ static int haptics_hw_init(struct haptics_chip *chip) struct haptics_hw_config *config = &chip->config; struct haptics_effect *effect; int rc = 0, i; - u8 val; + u8 val[2]; /* Store CL brake settings */ rc = haptics_store_cl_brake_settings(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) + return rc; + + chip->clamp_at_5v = val[0] & CLAMP_5V_BIT; /* Config VMAX */ rc = haptics_set_vmax_mv(chip, config->vmax_mv); if (rc < 0) @@ -1823,13 +1859,13 @@ static int haptics_hw_init(struct haptics_chip *chip) return rc; /* Config brake mode and waveform shape */ - val = (config->brake.mode << BRAKE_MODE_SHIFT) + val[0] = (config->brake.mode << BRAKE_MODE_SHIFT) | config->brake.sine_gain << BRAKE_SINE_GAIN_SHIFT | config->brake.brake_wf; rc = haptics_masked_write(chip, chip->cfg_addr_base, HAP_CFG_BRAKE_MODE_CFG_REG, BRAKE_MODE_MASK | BRAKE_SINE_GAIN_MASK - | BRAKE_WF_SEL_MASK, val); + | BRAKE_WF_SEL_MASK, val[0]); if (rc < 0) return rc; @@ -2972,20 +3008,25 @@ static int haptics_parse_dt(struct haptics_chip *chip) addr = of_get_address(node, 0, NULL, NULL); if (!addr) { - dev_err(chip->dev, "Read HAPTICS_CFG address failed, rc = %d\n", - rc); - return rc; + dev_err(chip->dev, "Read HAPTICS_CFG address failed\n"); + return -EINVAL; } 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, rc = %d\n", - rc); - return rc; + dev_err(chip->dev, "Read HAPTICS_PATTERN address failed\n"); + return -EINVAL; } 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; + } + + 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); From 1f31666769ca25f645e6f9daf0f491db347c0688 Mon Sep 17 00:00:00 2001 From: Fenglin Wu Date: Tue, 26 May 2020 15:08:46 +0800 Subject: [PATCH 21/76] input: qcom-hv-haptics: update drive waveform data format Use 2's complement data format for driving pattern waveform because the pattern/FIFO samples used in the driver are all having unsigned data type. Change-Id: Ib0f7c1c7fe272068177237821debbd3cc1ad4ec8 Signed-off-by: Fenglin Wu --- drivers/input/misc/qcom-hv-haptics.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/input/misc/qcom-hv-haptics.c b/drivers/input/misc/qcom-hv-haptics.c index 1d950dbddc78..7e31d7665552 100644 --- a/drivers/input/misc/qcom-hv-haptics.c +++ b/drivers/input/misc/qcom-hv-haptics.c @@ -58,6 +58,7 @@ #define DEFAULT_VMAX_MV 5000 #define HAP_CFG_DRV_WF_SEL_REG 0x49 +#define DRV_WF_FMT_BIT BIT(4) #define DRV_WF_SEL_MASK GENMASK(1, 0) #define HAP_CFG_AUTO_SHUTDOWN_CFG_REG 0x4A @@ -1881,6 +1882,12 @@ static int haptics_hw_init(struct haptics_chip *chip) if (rc < 0) return rc; + /* Config to use 2's complement values for sample data */ + rc = haptics_masked_write(chip, chip->cfg_addr_base, + HAP_CFG_DRV_WF_SEL_REG, DRV_WF_FMT_BIT, 0); + if (rc < 0) + return rc; + /* preload effect */ if (config->preload_effect != -EINVAL) { for (i = 0; i < chip->effects_count; i++) From bb4c542b495aad863e8323a9edae6fbbabbb8fe9 Mon Sep 17 00:00:00 2001 From: Fenglin Wu Date: Thu, 4 Jun 2020 15:12:37 +0800 Subject: [PATCH 22/76] input: qcom-hv-haptics: Deglitch fifo-empty interrupt Check INT_RT_STS in FIFO empty IRQ handler and ignore the interrupt if FIFO_EMPTY real time status bit is not set. Change-Id: I7a276fa1d8c79d530fc26f74263e0608096a99f8 Signed-off-by: Fenglin Wu --- drivers/input/misc/qcom-hv-haptics.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/drivers/input/misc/qcom-hv-haptics.c b/drivers/input/misc/qcom-hv-haptics.c index 7e31d7665552..6965030a02d9 100644 --- a/drivers/input/misc/qcom-hv-haptics.c +++ b/drivers/input/misc/qcom-hv-haptics.c @@ -46,6 +46,9 @@ #define AUTO_RES_ERROR_BIT BIT(1) #define HPRW_RDY_FAULT_BIT BIT(0) +#define HAP_CFG_INT_RT_STS_REG 0x10 +#define FIFO_EMPTY_BIT BIT(1) + /* config register definitions in HAPTICS_CFG module */ #define HAP_CFG_DRV_CTRL_REG 0x47 #define PSTG_DLY_MASK GENMASK(7, 6) @@ -1919,9 +1922,20 @@ static irqreturn_t fifo_empty_irq_handler(int irq, void *data) struct fifo_cfg *fifo = chip->play.effect->fifo; struct fifo_play_status *status = &chip->play.fifo_status; u32 samples_left; - u8 *samples; + u8 *samples, val; int rc, num; + rc = haptics_read(chip, chip->cfg_addr_base, + HAP_CFG_INT_RT_STS_REG, &val, 1); + if (rc < 0) + return IRQ_HANDLED; + + if (!(val & FIFO_EMPTY_BIT)) { + dev_dbg(chip->dev, "Ignore spurious/falling IRQ, INT_RT_STS = %#x\n", + val); + return IRQ_HANDLED; + } + if (atomic_read(&chip->play.fifo_status.written_done) == 1) { /* * Check the FIFO real time fill status before stopping From 55eb147a56cf8b438fa564b3ecd3cbadfcfcdafe Mon Sep 17 00:00:00 2001 From: Fenglin Wu Date: Thu, 4 Jun 2020 15:57:39 +0800 Subject: [PATCH 23/76] input: qcom-hv-haptics: restore RC calibration mode after FIFO playing Auto RC clock calibration is used as default configuration. When playing FIFO streaming data, manual RC clock calibration is used for adjusting the FIFO pattern based on the detected LRA frequency. Restore back to use auto RC clock calibration after FIFO pattern is played. Change-Id: I335552cfeafe559aa35a76bf6aa218a92b62d3db Signed-off-by: Fenglin Wu --- drivers/input/misc/qcom-hv-haptics.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/input/misc/qcom-hv-haptics.c b/drivers/input/misc/qcom-hv-haptics.c index 6965030a02d9..d309b6cd7143 100644 --- a/drivers/input/misc/qcom-hv-haptics.c +++ b/drivers/input/misc/qcom-hv-haptics.c @@ -1687,6 +1687,7 @@ static void haptics_fifo_empty_irq_config(struct haptics_chip *chip, static int haptics_stop_fifo_play(struct haptics_chip *chip) { int rc; + u8 val; if (atomic_read(&chip->play.fifo_status.is_busy) == 0) { dev_dbg(chip->dev, "FIFO playing is not in progress\n"); @@ -1707,6 +1708,18 @@ static int haptics_stop_fifo_play(struct haptics_chip *chip) chip->custom_effect->fifo->samples = NULL; atomic_set(&chip->play.fifo_status.is_busy, 0); + + /* + * All other playing modes would use AUTO mode RC + * calibration except FIFO streaming mode, so restore + * back to AUTO RC calibration after FIFO playing. + */ + val = CAL_RC_CLK_AUTO_VAL << CAL_RC_CLK_SHIFT; + rc = haptics_masked_write(chip, chip->cfg_addr_base, + HAP_CFG_CAL_EN_REG, CAL_RC_CLK_MASK, val); + if (rc < 0) + return rc; + dev_dbg(chip->dev, "stopped FIFO playing successfully\n"); return 0; } From 50982ecf58a9d64beb9c1e535ab7f7e5b32fa88e Mon Sep 17 00:00:00 2001 From: Subbaraman Narayanamurthy Date: Fri, 12 Jun 2020 15:42:07 -0700 Subject: [PATCH 24/76] input: qcom-hv-haptics: Fix a possible NULL pointer dereference Use Vmax from play effect in haptics_set_gain() only if the effect is valid. Otherwise, use Vmax from the haptics configuration instead. Change-Id: I7ec0f8c869096bccb26f7fda0057e3db3a51225d Signed-off-by: Subbaraman Narayanamurthy --- drivers/input/misc/qcom-hv-haptics.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/input/misc/qcom-hv-haptics.c b/drivers/input/misc/qcom-hv-haptics.c index d309b6cd7143..c30669fe12bb 100644 --- a/drivers/input/misc/qcom-hv-haptics.c +++ b/drivers/input/misc/qcom-hv-haptics.c @@ -1777,6 +1777,7 @@ static int haptics_erase(struct input_dev *dev, int effect_id) static void haptics_set_gain(struct input_dev *dev, u16 gain) { struct haptics_chip *chip = input_get_drvdata(dev); + struct haptics_hw_config *config = &chip->config; struct haptics_play_info *play = &chip->play; u32 vmax_mv; @@ -1786,7 +1787,10 @@ static void haptics_set_gain(struct input_dev *dev, u16 gain) if (gain > 0x7fff) gain = 0x7fff; - vmax_mv = play->effect->vmax_mv; + vmax_mv = config->vmax_mv; + if (play->effect) + vmax_mv = play->effect->vmax_mv; + if (chip->clamp_at_5v && (vmax_mv > CLAMPED_VMAX_MV)) vmax_mv = CLAMPED_VMAX_MV; From 9794c4dbeb7d9acfc5050ee5f6fca6f819fabbdf Mon Sep 17 00:00:00 2001 From: Fenglin Wu Date: Tue, 9 Jun 2020 08:54:31 +0800 Subject: [PATCH 25/76] input: qcom-hv-haptics: adjust frequency calibration sequence Adjust following settings in frequency calibration sequence according to HW recommendation: 1) Use 50% auto resonance detection error window. 2) Play for 150 ms before reading the calibration result. Change-Id: Id43376f6878daedaf98b1d9065a3b0b363314add Signed-off-by: Fenglin Wu --- drivers/input/misc/qcom-hv-haptics.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/input/misc/qcom-hv-haptics.c b/drivers/input/misc/qcom-hv-haptics.c index c30669fe12bb..bd73ac348607 100644 --- a/drivers/input/misc/qcom-hv-haptics.c +++ b/drivers/input/misc/qcom-hv-haptics.c @@ -3279,7 +3279,7 @@ static int haptics_start_lra_calibration(struct haptics_chip *chip) 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_25_PERCENT | AUTORES_EN_BIT); + | AUTORES_ERR_WINDOW_50_PERCENT | AUTORES_EN_BIT); if (rc < 0) goto unlock; @@ -3306,8 +3306,8 @@ static int haptics_start_lra_calibration(struct haptics_chip *chip) goto restore; } - /* wait for ~60ms to get the LRA calibration result */ - usleep_range(60000, 65000); + /* wait for ~150ms to get the LRA calibration result */ + usleep_range(150000, 155000); rc = haptics_get_closeloop_lra_period(chip); if (rc < 0) From e678995484fc1c97fd1622653b6173bc41765211 Mon Sep 17 00:00:00 2001 From: Fenglin Wu Date: Tue, 7 Jul 2020 08:30:54 +0800 Subject: [PATCH 26/76] input: qcom-hv-haptics: clear HW faults before enabling play If there was any HW fault generated in previous playing, it would be retained until writing the FAULT_CLR register. Clear the HW faults before triggering play to make sure it won't be started with any retained fault status. Change-Id: Ibac0e1ed3c745efeb5048845a073605642d8407d Signed-off-by: Fenglin Wu --- drivers/input/misc/qcom-hv-haptics.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/input/misc/qcom-hv-haptics.c b/drivers/input/misc/qcom-hv-haptics.c index bd73ac348607..d0e013b8db35 100644 --- a/drivers/input/misc/qcom-hv-haptics.c +++ b/drivers/input/misc/qcom-hv-haptics.c @@ -891,6 +891,15 @@ static int haptics_enable_play(struct haptics_chip *chip, bool en) int rc; u8 val; + if (en) { + val = SC_CLR_BIT | AUTO_RES_ERR_CLR_BIT | + HPWR_RDY_FAULT_CLR_BIT; + rc = haptics_write(chip, chip->cfg_addr_base, + HAP_CFG_FAULT_CLR_REG, &val, 1); + if (rc < 0) + return rc; + } + val = play->pattern_src; if (play->brake && !play->brake->disabled) val |= BRAKE_EN_BIT; From 6050776464e00870bc5204b78ab488e531bd8bf7 Mon Sep 17 00:00:00 2001 From: Fenglin Wu Date: Wed, 24 Jun 2020 11:10:28 +0800 Subject: [PATCH 27/76] input: qcom-hv-haptics: Update sequence of reading STATUS_DATA Per HW recommendation, when reading HAP_CFG_STATUS_DATA registers, MOD_STATUS_XT.SEL need to be written first to select the right mux, then update MOD_STATUS_SEL register so haptics module can present the desired status values into STATUS_DATA registers. Change-Id: I24205072074ff75b25b42c752333b5f99d83d067 Signed-off-by: Fenglin Wu --- drivers/input/misc/qcom-hv-haptics.c | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/drivers/input/misc/qcom-hv-haptics.c b/drivers/input/misc/qcom-hv-haptics.c index d0e013b8db35..c875acf01ddf 100644 --- a/drivers/input/misc/qcom-hv-haptics.c +++ b/drivers/input/misc/qcom-hv-haptics.c @@ -768,10 +768,15 @@ static int haptics_get_closeloop_lra_period_v2( u8 val[2]; u32 tmp; - val[0] = MOD_STATUS_SEL_LAST_GOOD_TLRA_VAL; - val[1] = MOD_STATUS_XT_SEL_LAST_GOOD_TLRA_VAL; + val[0] = MOD_STATUS_XT_SEL_LAST_GOOD_TLRA_VAL; rc = haptics_write(chip, chip->cfg_addr_base, - HAP_CFG_MOD_STATUS_SEL_REG, val, 2); + HAP_CFG_MOD_STATUS_XT_V2_REG, val, 1); + if (rc < 0) + return rc; + + val[0] = MOD_STATUS_SEL_LAST_GOOD_TLRA_VAL; + rc = haptics_write(chip, chip->cfg_addr_base, + HAP_CFG_MOD_STATUS_SEL_REG, val, 1); if (rc < 0) return rc; @@ -1075,8 +1080,8 @@ static int haptics_get_available_fifo_memory(struct haptics_chip *chip) u8 val[2]; u32 fill, available; - val[0] = MOD_STATUS_SEL_FIFO_FILL_STATUS_VAL; if (chip->ptn_revision == HAP_PTN_V1) { + val[0] = MOD_STATUS_SEL_FIFO_FILL_STATUS_VAL; rc = haptics_write(chip, chip->cfg_addr_base, HAP_CFG_MOD_STATUS_SEL_REG, val, 1); if (rc < 0) @@ -1089,9 +1094,15 @@ static int haptics_get_available_fifo_memory(struct haptics_chip *chip) fill = val[0] & FIFO_REAL_TIME_FILL_STATUS_MASK_V1; } else { - val[1] = MOD_STATUS_XT_V2_FIFO_FILL_STATUS_VAL; + val[0] = MOD_STATUS_XT_V2_FIFO_FILL_STATUS_VAL; rc = haptics_write(chip, chip->cfg_addr_base, - HAP_CFG_MOD_STATUS_SEL_REG, val, 2); + HAP_CFG_MOD_STATUS_XT_V2_REG, val, 1); + if (rc < 0) + return rc; + + val[0] = MOD_STATUS_SEL_FIFO_FILL_STATUS_VAL; + rc = haptics_write(chip, chip->cfg_addr_base, + HAP_CFG_MOD_STATUS_SEL_REG, val, 1); if (rc < 0) return rc; From 5850a67068ec44919c1b9d2ffa87dac3fa6795d3 Mon Sep 17 00:00:00 2001 From: Fenglin Wu Date: Wed, 24 Jun 2020 11:20:01 +0800 Subject: [PATCH 28/76] input: qcom-hv-haptics: set effect to NULL in constant playing In constant waveform playing, there is no need to use an effect data structure to capture the current playing effect, hence set it to NULL. This also helps the set_gain() callback to use the correct Vmax when it's called in constant playing case. Also add a debug message to show the Vmax setting. Change-Id: I763ec7407b8696300dbe93f5e6935900544e75a8 Signed-off-by: Fenglin Wu --- drivers/input/misc/qcom-hv-haptics.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/input/misc/qcom-hv-haptics.c b/drivers/input/misc/qcom-hv-haptics.c index c875acf01ddf..b766e6fd2a6b 100644 --- a/drivers/input/misc/qcom-hv-haptics.c +++ b/drivers/input/misc/qcom-hv-haptics.c @@ -840,6 +840,8 @@ static int haptics_set_vmax_mv(struct haptics_chip *chip, u32 vmax_mv) HAP_CFG_VMAX_REG, &val, 1); if (rc < 0) dev_err(chip->dev, "config VMAX failed, rc=%d\n", rc); + else + dev_dbg(chip->dev, "Set Vmax to %u mV\n", vmax_mv); return rc; } @@ -1313,6 +1315,9 @@ static int haptics_load_constant_effect(struct haptics_chip *chip, u8 amplitude) goto unlock; } + /* No effect data when playing constant waveform */ + play->effect = NULL; + /* configure VMAX in case it was changed in previous effect playing */ rc = haptics_set_vmax_mv(chip, chip->config.vmax_mv); if (rc < 0) From 8a322540270160c84b1ae11e197a38e4b511fe76 Mon Sep 17 00:00:00 2001 From: Fenglin Wu Date: Wed, 15 Jul 2020 15:20:16 +0800 Subject: [PATCH 29/76] input: qcom-hv-haptics: notify HBOOST to keep VREG on during play HAPTICS module controls the output voltage of HAPTICS_BOOST module through hardware signals when playing a vibration. During FIFO mode playing, HAPTICS module would request HAPTICS_BOOST module to turn on/off the output based on FIFO empty status. HAPTICS_BOOST is turned off when FIFO is empty and it's turned on again when more FIFO samples are filled. During this voltage transition request (off->on), HAPTICS_BOOST module may have a race condition if this requests comes in rapid succession. This can lead to its FSM getting stuck. To avoid this race condition, notify HAPTICS_BOOST module to keep the output enabled until the playing is stopped. This is done by triggering a PBS sequence through a SDAM module. Change-Id: I5cef13df7a1fa4608dd64d509c802e0bcfde6b92 Signed-off-by: Fenglin Wu --- drivers/input/misc/qcom-hv-haptics.c | 58 +++++++++++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-) diff --git a/drivers/input/misc/qcom-hv-haptics.c b/drivers/input/misc/qcom-hv-haptics.c index b766e6fd2a6b..4346e9bd556c 100644 --- a/drivers/input/misc/qcom-hv-haptics.c +++ b/drivers/input/misc/qcom-hv-haptics.c @@ -430,6 +430,7 @@ struct haptics_chip { struct regulator_dev *swr_slave_rdev; struct mutex irq_lock; struct nvmem_cell *cl_brake_nvmem; + struct nvmem_device *hap_cfg_nvmem; struct class hap_class; int fifo_empty_irq; u32 effects_count; @@ -892,6 +893,40 @@ static int haptics_set_direct_play(struct haptics_chip *chip, u8 amplitude) return rc; } +#define PBS_ARG_REG 0x42 +#define HAP_VREG_ON_VAL 0x1 +#define HAP_VREG_OFF_VAL 0x2 +#define PBS_TRIG_SET_REG 0xE5 +#define PBS_TRIG_SET_VAL 0x1 +static int haptics_boost_vreg_enable(struct haptics_chip *chip, bool en) +{ + int rc; + u8 val; + + if (chip->hap_cfg_nvmem == NULL) { + dev_dbg(chip->dev, "nvmem device for hap_cfg is not defined\n"); + return 0; + } + + val = en ? HAP_VREG_ON_VAL : HAP_VREG_OFF_VAL; + rc = nvmem_device_write(chip->hap_cfg_nvmem, + PBS_ARG_REG, 1, &val); + if (rc < 0) { + dev_err(chip->dev, "write SDAM %#x failed, rc=%d\n", + PBS_ARG_REG, rc); + return rc; + } + + val = PBS_TRIG_SET_VAL; + rc = nvmem_device_write(chip->hap_cfg_nvmem, + PBS_TRIG_SET_REG, 1, &val); + if (rc < 0) + dev_err(chip->dev, "Write SDAM %#x failed, rc=%d\n", + PBS_TRIG_SET_REG, rc); + + return rc; +} + static int haptics_enable_play(struct haptics_chip *chip, bool en) { struct haptics_play_info *play = &chip->play; @@ -916,8 +951,17 @@ static int haptics_enable_play(struct haptics_chip *chip, bool en) rc = haptics_write(chip, chip->cfg_addr_base, HAP_CFG_SPMI_PLAY_REG, &val, 1); - if (rc < 0) + if (rc < 0) { dev_err(chip->dev, "Write SPMI_PLAY failed, rc=%d\n", rc); + return rc; + } + + if (play->pattern_src == FIFO) { + rc = haptics_boost_vreg_enable(chip, en); + if (rc < 0) + dev_err(chip->dev, "Notify vreg %s failed, rc=%d\n", + en ? "enabling" : "disabling", rc); + } return rc; } @@ -3069,6 +3113,18 @@ static int haptics_parse_dt(struct haptics_chip *chip) } } + if (of_find_property(node, "nvmem", NULL)) { + chip->hap_cfg_nvmem = + devm_nvmem_device_get(chip->dev, "hap_cfg_sdam"); + 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", + rc); + return rc; + } + } + addr = of_get_address(node, 0, NULL, NULL); if (!addr) { dev_err(chip->dev, "Read HAPTICS_CFG address failed\n"); From ab3f9346d3ceaccef24661d14a29dee068e7f544 Mon Sep 17 00:00:00 2001 From: Fenglin Wu Date: Tue, 7 Jul 2020 15:04:17 +0800 Subject: [PATCH 30/76] input: qcom-hv-haptics: update LRA period calculations There is a new scheme recommended in PM8350B 2.0 haptics module for getting close-loop LRA period. There are 4 situations need to be considered depending on if auto mode RC CLK calibration is used and if auto resonance calibration is done during the playing. In each situation, different status values and equations are used for calculating close-loop LRA period. Update the driver to support this. Change-Id: I8f5fdcdd3ebae71af333a0b05fb4860e1c43ce10 Signed-off-by: Fenglin Wu --- drivers/input/misc/qcom-hv-haptics.c | 147 ++++++++++++++++++++++----- 1 file changed, 122 insertions(+), 25 deletions(-) diff --git a/drivers/input/misc/qcom-hv-haptics.c b/drivers/input/misc/qcom-hv-haptics.c index 4346e9bd556c..b6084716706f 100644 --- a/drivers/input/misc/qcom-hv-haptics.c +++ b/drivers/input/misc/qcom-hv-haptics.c @@ -29,7 +29,9 @@ #define AUTO_RES_CAL_DONE_BIT BIT(5) #define CAL_TLRA_CL_STS_MSB_MASK GENMASK(4, 0) /* STATUS_DATA_MSB definition in V2 while MOD_STATUS_SEL is 3 */ -#define LAST_GOOD_TLRA_CL_MASK GENMASK(4, 0) +#define LAST_GOOD_TLRA_CL_MSB_MASK GENMASK(4, 0) +/* STATUS_DATA_MSB definition in V2 while MOD_STATUS_SEL is 4 */ +#define TLRA_CL_ERR_MSB_MASK GENMASK(4, 0) /* STATUS_DATA_MSB definition in V1 while MOD_STATUS_SEL is 5 */ #define FIFO_REAL_TIME_FILL_STATUS_MASK_V1 GENMASK(6, 0) /* STATUS DATA_MSB definition in V2 while MOD_STATUS_SEL is 5 */ @@ -135,6 +137,7 @@ #define HAP_CFG_MOD_STATUS_SEL_REG 0x70 #define MOD_STATUS_SEL_CAL_TLRA_CL_STS_VAL 0 #define MOD_STATUS_SEL_LAST_GOOD_TLRA_VAL 3 +#define MOD_STATUS_SEL_TLRA_CL_ERR_STS_VAL 4 #define MOD_STATUS_SEL_FIFO_FILL_STATUS_VAL 5 #define MOD_STATUS_SEL_BRAKE_CAL_RNAT_RCAL_VAL 6 @@ -145,6 +148,7 @@ #define HAP_CFG_CAL_EN_REG 0x72 #define CAL_RC_CLK_MASK GENMASK(3, 2) #define CAL_RC_CLK_SHIFT 2 +#define CAL_RC_CLK_DISABLED_VAL 0 #define CAL_RC_CLK_AUTO_VAL 1 #define CAL_RC_CLK_MANUAL_VAL 2 @@ -757,25 +761,48 @@ static int haptics_get_closeloop_lra_period_v1( tmp = ((val[0] & CAL_TLRA_CL_STS_MSB_MASK) << 8) | val[1]; config->cl_t_lra_us = (tmp * step_ns) / 1000; - return 0; + return haptics_adjust_lra_period(chip, &config->cl_t_lra_us); } -#define CL_TLRA_STEP_NS 1666 +#define TLRA_AUTO_RES_ERR_NO_CAL_STEP_PSEC 1667000 +#define TLRA_AUTO_RES_NO_CAL_STEP_PSEC 3333000 +#define TLRA_AUTO_RES_ERR_AUTO_CAL_STEP_PSEC 1627700 +#define TLRA_AUTO_RES_AUTO_CAL_STEP_PSEC 813850 static int haptics_get_closeloop_lra_period_v2( struct haptics_chip *chip) { struct haptics_hw_config *config = &chip->config; + u16 cal_tlra_cl_sts, tlra_cl_err_sts, tlra_ol, last_good_tlra_cl_sts; + u8 val[2], rc_clk_cal; + bool auto_res_done; + u64 tmp; int rc; - u8 val[2]; - u32 tmp; - val[0] = MOD_STATUS_XT_SEL_LAST_GOOD_TLRA_VAL; - rc = haptics_write(chip, chip->cfg_addr_base, - HAP_CFG_MOD_STATUS_XT_V2_REG, val, 1); + /* read RC_CLK_CAL enabling mode */ + rc = haptics_read(chip, chip->cfg_addr_base, + HAP_CFG_CAL_EN_REG, val, 1); if (rc < 0) return rc; - val[0] = MOD_STATUS_SEL_LAST_GOOD_TLRA_VAL; + rc_clk_cal = ((val[0] & CAL_RC_CLK_MASK) >> CAL_RC_CLK_SHIFT); + /* read auto resonance calibration result */ + val[0] = MOD_STATUS_SEL_CAL_TLRA_CL_STS_VAL; + rc = haptics_write(chip, chip->cfg_addr_base, + HAP_CFG_MOD_STATUS_SEL_REG, val, 1); + if (rc < 0) + return rc; + + rc = haptics_read(chip, chip->cfg_addr_base, + HAP_CFG_STATUS_DATA_MSB_REG, val, 2); + if (rc < 0) + return rc; + + auto_res_done = !!(val[0] & AUTO_RES_CAL_DONE_BIT); + cal_tlra_cl_sts = + ((val[0] & CAL_TLRA_CL_STS_MSB_MASK) << 8) | val[1]; + + /* read auto resonance calibration error status */ + val[0] = MOD_STATUS_SEL_TLRA_CL_ERR_STS_VAL; rc = haptics_write(chip, chip->cfg_addr_base, HAP_CFG_MOD_STATUS_SEL_REG, val, 1); if (rc < 0) @@ -785,15 +812,77 @@ static int haptics_get_closeloop_lra_period_v2( HAP_CFG_STATUS_DATA_MSB_REG, val, 2); if (rc < 0) return rc; - /* - * Calculate the closed loop T_LRA with the following equations - * for PM8350B V2: - * LAST_GOOD_TLRA_CL_STS[12:0] = STATUS_DATA_MSB[4:0] | - * STATUS_DATA_LSB[7:0] - * LAST_GOOD_TLRA_CL = LAST_GOOD_TLRA_CL_STS[12:0] * 1.666 us - */ - tmp = ((val[0] & LAST_GOOD_TLRA_CL_MASK) << 8) | val[1]; - config->cl_t_lra_us = (tmp * CL_TLRA_STEP_NS) / 1000; + + tlra_cl_err_sts = + ((val[0] & TLRA_CL_ERR_MSB_MASK) << 8) | val[1]; + + dev_dbg(chip->dev, "rc_clk_cal = %u, auto_res_done = %d\n", + rc_clk_cal, auto_res_done); + + if (rc_clk_cal == CAL_RC_CLK_DISABLED_VAL && !auto_res_done) { + /* TLRA_CL_ERR(us) = TLRA_CL_ERR_STS * 1.667 us */ + tmp = tlra_cl_err_sts * TLRA_AUTO_RES_ERR_NO_CAL_STEP_PSEC; + } else if (rc_clk_cal == CAL_RC_CLK_DISABLED_VAL && auto_res_done) { + /* + * CAL_TLRA_CL_STS_NO_CAL = CAL_TLRA_CL_STS + * TLRA_AUTO_RES(us) = CAL_TLRA_CL_STS_NO_CAL * 3.333 us + */ + tmp = cal_tlra_cl_sts * TLRA_AUTO_RES_NO_CAL_STEP_PSEC; + } else if (rc_clk_cal == CAL_RC_CLK_AUTO_VAL && !auto_res_done) { + /* + * CAL_TLRA_OL = CAL_TLRA_CL_STS; + * TLRA_CL_ERR(us) = TLRA_CL_ERR_STS * + * (TLRA_OL / CAL_TLRA_OL) * 1.6277 us + */ + + /* read the TLRA_OL setting */ + rc = haptics_read(chip, chip->cfg_addr_base, + HAP_CFG_TLRA_OL_HIGH_REG, val, 2); + if (rc < 0) + return rc; + + tlra_ol = (val[0] & TLRA_OL_MSB_MASK) << 8 | val[1]; + tmp = tlra_cl_err_sts * tlra_ol; + tmp *= TLRA_AUTO_RES_ERR_AUTO_CAL_STEP_PSEC; + tmp = div_u64(tmp, cal_tlra_cl_sts); + } else if (rc_clk_cal == CAL_RC_CLK_AUTO_VAL && auto_res_done) { + /* + * CAL_TLRA_CL_STS_W_CAL = CAL_TLRA_CL_STS; + * TLRA_AUTO_RES(us) = LAST_GOOD_TLRA_CL_STS * 0.81385 us * + * (LAST_GOOD_TLRA_CL_STS / CAL_TLRA_CL_STS_AUTO_CAL) + */ + + /* read LAST_GOOD_TLRA_CL_STS */ + val[0] = MOD_STATUS_XT_SEL_LAST_GOOD_TLRA_VAL; + rc = haptics_write(chip, chip->cfg_addr_base, + HAP_CFG_MOD_STATUS_XT_V2_REG, val, 1); + if (rc < 0) + return rc; + + val[0] = MOD_STATUS_SEL_LAST_GOOD_TLRA_VAL; + rc = haptics_write(chip, chip->cfg_addr_base, + HAP_CFG_MOD_STATUS_SEL_REG, val, 1); + if (rc < 0) + return rc; + + rc = haptics_read(chip, chip->cfg_addr_base, + HAP_CFG_STATUS_DATA_MSB_REG, val, 2); + if (rc < 0) + return rc; + + last_good_tlra_cl_sts = + ((val[0] & LAST_GOOD_TLRA_CL_MSB_MASK) << 8) | val[1]; + + tmp = last_good_tlra_cl_sts * last_good_tlra_cl_sts; + tmp *= TLRA_AUTO_RES_AUTO_CAL_STEP_PSEC; + tmp = div_u64(tmp, cal_tlra_cl_sts); + } else { + dev_err(chip->dev, "Can't get close-loop LRA period in rc_clk_cal mode %u\n", + rc_clk_cal); + return -EINVAL; + } + + config->cl_t_lra_us = div_u64(tmp, 1000000); return 0; } @@ -808,17 +897,13 @@ static int haptics_get_closeloop_lra_period(struct haptics_chip *chip) rc = haptics_get_closeloop_lra_period_v2(chip); if (rc < 0) { - dev_err(chip->dev, "get close loop T LRA failed, rc=%d\n", rc); + dev_err(chip->dev, "get close loop T LRA failed, rc=%d\n", + rc); return rc; } - rc = haptics_adjust_lra_period(chip, &chip->config.cl_t_lra_us); - if (rc < 0) - return rc; - dev_dbg(chip->dev, "OL_TLRA %u us, CL_TLRA %u us\n", chip->config.t_lra_us, chip->config.cl_t_lra_us); - return 0; } @@ -1918,6 +2003,7 @@ static int haptics_hw_init(struct haptics_chip *chip) struct haptics_effect *effect; int rc = 0, i; u8 val[2]; + u32 t_lra_us; /* Store CL brake settings */ rc = haptics_store_cl_brake_settings(chip); @@ -1962,12 +2048,23 @@ static int haptics_hw_init(struct haptics_chip *chip) if (config->is_erm) return 0; + /* set AUTO_mode RC CLK calibration by default */ + val[0] = CAL_RC_CLK_AUTO_VAL << CAL_RC_CLK_SHIFT; + rc = haptics_masked_write(chip, chip->cfg_addr_base, + HAP_CFG_CAL_EN_REG, CAL_RC_CLK_MASK, val[0]); + if (rc < 0) + return rc; + rc = haptics_get_closeloop_lra_period(chip); if (rc < 0) return rc; /* Config T_LRA */ - rc = haptics_config_openloop_lra_period(chip, chip->config.cl_t_lra_us); + t_lra_us = chip->config.t_lra_us; + if (chip->config.cl_t_lra_us != 0) + t_lra_us = chip->config.cl_t_lra_us; + + rc = haptics_config_openloop_lra_period(chip, t_lra_us); if (rc < 0) return rc; From bc5fd27b2bd8b7fe791706fb489309a33c7b28d7 Mon Sep 17 00:00:00 2001 From: Fenglin Wu Date: Fri, 24 Jul 2020 11:01:48 +0800 Subject: [PATCH 31/76] input: qcom-hv-haptics: update RC_CLK_CAL_COUNT calculation RC_CLK_CAL_COUNT is the calibration count used in manual RC_CLK_CAL mode when playing FIFO patterns. For haptics module in PM8350B 2.0, there are new equations recommended to calculate RC_CLK_CAL_COUNT based on CAL_TLRA_CL_STS and LAST_GOOD_TLRA_CL_STS status to achieve better accuracy. Update the driver to support this. Change-Id: I983c1b1f284006847549d2b3d23a0c1039f8b4c1 Signed-off-by: Fenglin Wu --- drivers/input/misc/qcom-hv-haptics.c | 105 ++++++++++++++++++++------- 1 file changed, 77 insertions(+), 28 deletions(-) diff --git a/drivers/input/misc/qcom-hv-haptics.c b/drivers/input/misc/qcom-hv-haptics.c index b6084716706f..f952123672be 100644 --- a/drivers/input/misc/qcom-hv-haptics.c +++ b/drivers/input/misc/qcom-hv-haptics.c @@ -411,6 +411,7 @@ struct haptics_hw_config { u32 cl_t_lra_us; u32 preload_effect; u32 fifo_empty_thresh; + u16 rc_clk_cal_count; enum drv_sig_shape drv_wf; bool is_erm; }; @@ -727,7 +728,7 @@ static int haptics_get_closeloop_lra_period_v1( struct haptics_chip *chip) { struct haptics_hw_config *config = &chip->config; - int rc; + int rc, freq_diff, f_lra, cl_f_lra; u8 val[2]; u32 tmp, step_ns; bool auto_res_cal_done; @@ -761,7 +762,25 @@ static int haptics_get_closeloop_lra_period_v1( tmp = ((val[0] & CAL_TLRA_CL_STS_MSB_MASK) << 8) | val[1]; config->cl_t_lra_us = (tmp * step_ns) / 1000; - return haptics_adjust_lra_period(chip, &config->cl_t_lra_us); + rc = haptics_adjust_lra_period(chip, &config->cl_t_lra_us); + if (rc < 0) + return rc; + + /* calculate RC_CLK_CAL_COUNT */ + if (!config->t_lra_us || !config->cl_t_lra_us) + return -EINVAL; + + f_lra = USEC_PER_SEC / config->t_lra_us; + if (!f_lra) + return -EINVAL; + + cl_f_lra = USEC_PER_SEC / config->cl_t_lra_us; + freq_diff = cl_f_lra - f_lra; + + /* RC_CLK_CAL_COUNT = 600*(1-(clk_adjustment/Fifo_pat_freq)) */ + config->rc_clk_cal_count = 600 - ((600 * freq_diff) / f_lra); + + return 0; } #define TLRA_AUTO_RES_ERR_NO_CAL_STEP_PSEC 1667000 @@ -822,12 +841,16 @@ static int haptics_get_closeloop_lra_period_v2( if (rc_clk_cal == CAL_RC_CLK_DISABLED_VAL && !auto_res_done) { /* TLRA_CL_ERR(us) = TLRA_CL_ERR_STS * 1.667 us */ tmp = tlra_cl_err_sts * TLRA_AUTO_RES_ERR_NO_CAL_STEP_PSEC; + dev_dbg(chip->dev, "tlra_cl_err_sts = %#x\n", tlra_cl_err_sts); + config->cl_t_lra_us = div_u64(tmp, 1000000); } else if (rc_clk_cal == CAL_RC_CLK_DISABLED_VAL && auto_res_done) { /* * CAL_TLRA_CL_STS_NO_CAL = CAL_TLRA_CL_STS * TLRA_AUTO_RES(us) = CAL_TLRA_CL_STS_NO_CAL * 3.333 us */ tmp = cal_tlra_cl_sts * TLRA_AUTO_RES_NO_CAL_STEP_PSEC; + dev_dbg(chip->dev, "cal_tlra_cl_sts = %#x\n", cal_tlra_cl_sts); + config->cl_t_lra_us = div_u64(tmp, 1000000); } else if (rc_clk_cal == CAL_RC_CLK_AUTO_VAL && !auto_res_done) { /* * CAL_TLRA_OL = CAL_TLRA_CL_STS; @@ -842,9 +865,26 @@ static int haptics_get_closeloop_lra_period_v2( return rc; tlra_ol = (val[0] & TLRA_OL_MSB_MASK) << 8 | val[1]; + dev_dbg(chip->dev, "tlra_ol = %#x, tlra_cl_err_sts = %#x, cal_tlra_cl_sts = %#x\n", + tlra_ol, tlra_cl_err_sts, cal_tlra_cl_sts); + tmp = tlra_cl_err_sts * tlra_ol; tmp *= TLRA_AUTO_RES_ERR_AUTO_CAL_STEP_PSEC; tmp = div_u64(tmp, cal_tlra_cl_sts); + config->cl_t_lra_us = div_u64(tmp, 1000000); + + /* calculate RC_CLK_CAL_COUNT */ + if (!config->t_lra_us || !config->cl_t_lra_us) + return -EINVAL; + /* + * RC_CLK_CAL_COUNT = 600 * (CAL_TLRA_OL / TLRA_OL) + * * (600 / 586) * (CL_T_TLRA_US / OL_T_LRA_US) + */ + tmp = 360000; + tmp *= cal_tlra_cl_sts * config->cl_t_lra_us; + tmp = div_u64(tmp, tlra_ol); + tmp = div_u64(tmp, 586); + config->rc_clk_cal_count = div_u64(tmp, config->t_lra_us); } else if (rc_clk_cal == CAL_RC_CLK_AUTO_VAL && auto_res_done) { /* * CAL_TLRA_CL_STS_W_CAL = CAL_TLRA_CL_STS; @@ -873,17 +913,34 @@ static int haptics_get_closeloop_lra_period_v2( last_good_tlra_cl_sts = ((val[0] & LAST_GOOD_TLRA_CL_MSB_MASK) << 8) | val[1]; + dev_dbg(chip->dev, "last_good_tlra_cl_sts = %#x, cal_tlra_cl_sts = %#x\n", + last_good_tlra_cl_sts, cal_tlra_cl_sts); + tmp = last_good_tlra_cl_sts * last_good_tlra_cl_sts; tmp *= TLRA_AUTO_RES_AUTO_CAL_STEP_PSEC; tmp = div_u64(tmp, cal_tlra_cl_sts); + config->cl_t_lra_us = div_u64(tmp, 1000000); + + /* calculate RC_CLK_CAL_COUNT */ + if (!config->t_lra_us || !config->cl_t_lra_us) + return -EINVAL; + + /* + * RC_CLK_CAL_COUNT = + * 600 * (CAL_TLRA_CL_STS_AUTO_CAL / LAST_GOOD_TLRA_CL_STS) + * * (600 / 293) * (CL_T_TLRA_US / OL_T_LRA_US) + */ + tmp = 360000; + tmp *= cal_tlra_cl_sts * config->cl_t_lra_us; + tmp = div_u64(tmp, last_good_tlra_cl_sts); + tmp = div_u64(tmp, 293); + config->rc_clk_cal_count = div_u64(tmp, config->t_lra_us); } else { dev_err(chip->dev, "Can't get close-loop LRA period in rc_clk_cal mode %u\n", rc_clk_cal); return -EINVAL; } - config->cl_t_lra_us = div_u64(tmp, 1000000); - return 0; } @@ -902,8 +959,9 @@ static int haptics_get_closeloop_lra_period(struct haptics_chip *chip) return rc; } - dev_dbg(chip->dev, "OL_TLRA %u us, CL_TLRA %u us\n", - chip->config.t_lra_us, chip->config.cl_t_lra_us); + dev_dbg(chip->dev, "OL_TLRA %u us, CL_TLRA %u us, RC_CLK_CAL_COUNT %#x\n", + chip->config.t_lra_us, chip->config.cl_t_lra_us, + chip->config.rc_clk_cal_count); return 0; } @@ -1259,26 +1317,17 @@ static int haptics_get_available_fifo_memory(struct haptics_chip *chip) return available; } -static int haptics_adjust_rc_clk(struct haptics_chip *chip) +static int haptics_set_manual_rc_clk_cal(struct haptics_chip *chip) { - int rc, freq_diff, cal_count, f_lra, cl_f_lra; + int rc; + u16 cal_count = chip->config.rc_clk_cal_count; u8 val[2]; - if (!chip->config.t_lra_us || !chip->config.cl_t_lra_us) - return -EINVAL; + if (cal_count == 0) { + dev_dbg(chip->dev, "Ignore setting RC_CLK_CAL_COUNT\n"); + return 0; + } - f_lra = USEC_PER_SEC / chip->config.t_lra_us; - if (!f_lra) - return -EINVAL; - - cl_f_lra = USEC_PER_SEC / chip->config.cl_t_lra_us; - freq_diff = cl_f_lra - f_lra; - - /* RC_CLK_CAL_COUNT = 600*(1-(clk_adjustment/Fifo_pat_freq)) */ - cal_count = 600 - ((600 * freq_diff) / f_lra); - dev_dbg(chip->dev, "RC clock calibration count:%#x\n", cal_count); - - /* Set FIFO pattern frequency adjustment */ val[0] = (cal_count >> 8) & RC_CLK_CAL_COUNT_MSB_MASK; val[1] = cal_count & RC_CLK_CAL_COUNT_LSB_MASK; rc = haptics_write(chip, chip->cfg_addr_base, @@ -1288,7 +1337,8 @@ static int haptics_adjust_rc_clk(struct haptics_chip *chip) val[0] = CAL_RC_CLK_MANUAL_VAL << CAL_RC_CLK_SHIFT; return haptics_masked_write(chip, chip->cfg_addr_base, - HAP_CFG_CAL_EN_REG, CAL_RC_CLK_MASK, val[0]); + HAP_CFG_CAL_EN_REG, CAL_RC_CLK_MASK, + val[0]); } static int haptics_update_fifo_samples(struct haptics_chip *chip, @@ -1384,12 +1434,10 @@ static int haptics_set_fifo(struct haptics_chip *chip, struct fifo_cfg *fifo) return rc; if (fifo->period_per_s >= F_8KHZ) { - rc = haptics_adjust_rc_clk(chip); - if (rc < 0) { - dev_err(chip->dev, "RC CLK adjustment failed, rc=%d\n", - rc); + /* Set manual RC CLK CAL when playing FIFO */ + rc = haptics_set_manual_rc_clk_cal(chip); + if (rc < 0) return rc; - } } atomic_set(&status->written_done, 0); @@ -2055,6 +2103,7 @@ static int haptics_hw_init(struct haptics_chip *chip) if (rc < 0) return rc; + /* get calibrated close loop period */ rc = haptics_get_closeloop_lra_period(chip); if (rc < 0) return rc; From 8c72c8feadce3b8f56602dd4165b19d766733bb8 Mon Sep 17 00:00:00 2001 From: Fenglin Wu Date: Mon, 27 Jul 2020 12:11:16 +0800 Subject: [PATCH 32/76] input: qcom-hv-haptics: read CAL_TLRA_CL_STS from SDAM after boot up During LRA frequency calibration, reading CAL_TLRA_CL_STS has to be done before de-asserting play. In the boot up LRA frequency calibration sequence, CAL_TLRA_CL_STS was read out and stored in SDAM module registers by the bootloader. Later, HLOS haptics driver can read it from there and use it for calculating LRA frequency. This boot up LRA frequency calibration sequence is only available for PM8350B 2.0 chip. Update the driver to support this. Change-Id: I0b81e79bad57160f9c79e01151e2d4060b4bbd41 Signed-off-by: Fenglin Wu --- drivers/input/misc/qcom-hv-haptics.c | 50 +++++++++++++++++++--------- 1 file changed, 35 insertions(+), 15 deletions(-) diff --git a/drivers/input/misc/qcom-hv-haptics.c b/drivers/input/misc/qcom-hv-haptics.c index f952123672be..eb05bf0cecd5 100644 --- a/drivers/input/misc/qcom-hv-haptics.c +++ b/drivers/input/misc/qcom-hv-haptics.c @@ -34,7 +34,7 @@ #define TLRA_CL_ERR_MSB_MASK GENMASK(4, 0) /* STATUS_DATA_MSB definition in V1 while MOD_STATUS_SEL is 5 */ #define FIFO_REAL_TIME_FILL_STATUS_MASK_V1 GENMASK(6, 0) -/* STATUS DATA_MSB definition in V2 while MOD_STATUS_SEL is 5 */ +/* STATUS_DATA_MSB definition in V2 while MOD_STATUS_SEL is 5 */ #define FIFO_REAL_TIME_FILL_STATUS_MSB_MASK_V2 GENMASK(1, 0) #define HAP_CFG_STATUS_DATA_LSB_REG 0x0A @@ -783,12 +783,16 @@ static int haptics_get_closeloop_lra_period_v1( return 0; } +/* The offset of SDAM register which saves STATUS_DATA_MSB value */ +#define HAP_STATUS_DATA_MSB_SDAM_OFFSET 0x46 + +/* constant definitions for calculating TLRA */ #define TLRA_AUTO_RES_ERR_NO_CAL_STEP_PSEC 1667000 #define TLRA_AUTO_RES_NO_CAL_STEP_PSEC 3333000 #define TLRA_AUTO_RES_ERR_AUTO_CAL_STEP_PSEC 1627700 #define TLRA_AUTO_RES_AUTO_CAL_STEP_PSEC 813850 static int haptics_get_closeloop_lra_period_v2( - struct haptics_chip *chip) + struct haptics_chip *chip, bool in_boot) { struct haptics_hw_config *config = &chip->config; u16 cal_tlra_cl_sts, tlra_cl_err_sts, tlra_ol, last_good_tlra_cl_sts; @@ -805,16 +809,31 @@ static int haptics_get_closeloop_lra_period_v2( rc_clk_cal = ((val[0] & CAL_RC_CLK_MASK) >> CAL_RC_CLK_SHIFT); /* read auto resonance calibration result */ - val[0] = MOD_STATUS_SEL_CAL_TLRA_CL_STS_VAL; - rc = haptics_write(chip, chip->cfg_addr_base, - HAP_CFG_MOD_STATUS_SEL_REG, val, 1); - if (rc < 0) - return rc; + if (in_boot) { + if (chip->hap_cfg_nvmem == NULL) { + dev_dbg(chip->dev, "nvmem device for hap_cfg is not defined\n"); + return -EINVAL; + } - rc = haptics_read(chip, chip->cfg_addr_base, - HAP_CFG_STATUS_DATA_MSB_REG, val, 2); - if (rc < 0) - return rc; + rc = nvmem_device_read(chip->hap_cfg_nvmem, + HAP_STATUS_DATA_MSB_SDAM_OFFSET, 2, val); + if (rc < 0) { + dev_err(chip->dev, "read SDAM %#x failed, rc=%d\n", + HAP_STATUS_DATA_MSB_SDAM_OFFSET, rc); + return rc; + } + } else { + val[0] = MOD_STATUS_SEL_CAL_TLRA_CL_STS_VAL; + rc = haptics_write(chip, chip->cfg_addr_base, + HAP_CFG_MOD_STATUS_SEL_REG, val, 1); + if (rc < 0) + return rc; + + rc = haptics_read(chip, chip->cfg_addr_base, + HAP_CFG_STATUS_DATA_MSB_REG, val, 2); + if (rc < 0) + return rc; + } auto_res_done = !!(val[0] & AUTO_RES_CAL_DONE_BIT); cal_tlra_cl_sts = @@ -944,14 +963,15 @@ static int haptics_get_closeloop_lra_period_v2( return 0; } -static int haptics_get_closeloop_lra_period(struct haptics_chip *chip) +static int haptics_get_closeloop_lra_period(struct haptics_chip *chip, + bool in_boot) { int rc = 0; if (chip->ptn_revision == HAP_PTN_V1) rc = haptics_get_closeloop_lra_period_v1(chip); else - rc = haptics_get_closeloop_lra_period_v2(chip); + rc = haptics_get_closeloop_lra_period_v2(chip, in_boot); if (rc < 0) { dev_err(chip->dev, "get close loop T LRA failed, rc=%d\n", @@ -2104,7 +2124,7 @@ static int haptics_hw_init(struct haptics_chip *chip) return rc; /* get calibrated close loop period */ - rc = haptics_get_closeloop_lra_period(chip); + rc = haptics_get_closeloop_lra_period(chip, true); if (rc < 0) return rc; @@ -3536,7 +3556,7 @@ static int haptics_start_lra_calibration(struct haptics_chip *chip) /* wait for ~150ms to get the LRA calibration result */ usleep_range(150000, 155000); - rc = haptics_get_closeloop_lra_period(chip); + rc = haptics_get_closeloop_lra_period(chip, false); if (rc < 0) goto restore; From cdd93c2f17a0a5a3b7571adfd810aa004c577020 Mon Sep 17 00:00:00 2001 From: Fenglin Wu Date: Wed, 5 Aug 2020 16:45:18 +0800 Subject: [PATCH 33/76] input: qcom-hv-haptics: toggle CAL_EN mode if HBST is in open loop When RC_CLK_CAL_EN is set in auto mode while haptics boost is working in open loop, the first cycle of the vibration would be played incorrectly because the haptics module couldn't get enough clock count before the play gets triggered. Fix this by toggling the RC clock calibration mode from disabled to auto mode. Change-Id: I80b3c48b9bb508b12841a679eafa2c618771edf2 Signed-off-by: Fenglin Wu --- drivers/input/misc/qcom-hv-haptics.c | 53 ++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/drivers/input/misc/qcom-hv-haptics.c b/drivers/input/misc/qcom-hv-haptics.c index eb05bf0cecd5..e5fdfb1089a2 100644 --- a/drivers/input/misc/qcom-hv-haptics.c +++ b/drivers/input/misc/qcom-hv-haptics.c @@ -217,6 +217,14 @@ #define HAP_BOOST_V0P0 0x0000 #define HAP_BOOST_V0P1 0x0001 +#define HAP_BOOST_HW_CTRL_FOLLOW_REG 0x41 +#define FOLLOW_HW_EN_BIT BIT(7) +#define FOLLOW_HW_CCM_BIT BIT(6) +#define FOLLOW_HW_VSET_BIT BIT(5) + +#define HAP_BOOST_VREG_EN_REG 0x46 +#define VREG_EN_BIT BIT(7) + #define HAP_BOOST_V0P0_CLAMP_REG 0xF1 #define HAP_BOOST_V0P1_CLAMP_REG 0x70 #define CLAMP_5V_BIT BIT(0) @@ -1090,6 +1098,24 @@ static int haptics_boost_vreg_enable(struct haptics_chip *chip, bool en) return rc; } +static bool is_boost_vreg_enabled_in_open_loop(struct haptics_chip *chip) +{ + int rc; + u8 val; + + rc = haptics_read(chip, chip->hbst_addr_base, + HAP_BOOST_HW_CTRL_FOLLOW_REG, &val, 1); + 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)) + return true; + } + + dev_dbg(chip->dev, "HBoost is not enabled in open loop condition\n"); + return false; +} + static int haptics_enable_play(struct haptics_chip *chip, bool en) { struct haptics_play_info *play = &chip->play; @@ -1103,6 +1129,33 @@ static int haptics_enable_play(struct haptics_chip *chip, bool en) HAP_CFG_FAULT_CLR_REG, &val, 1); if (rc < 0) return rc; + + /* + * Toggle RC_CLK_CAL_EN if it's in auto mode and + * haptics boost is working in open loop + */ + rc = haptics_read(chip, chip->cfg_addr_base, + HAP_CFG_CAL_EN_REG, &val, 1); + if (rc < 0) + return rc; + + if (((val & CAL_RC_CLK_MASK) == + CAL_RC_CLK_AUTO_VAL << CAL_RC_CLK_SHIFT) + && is_boost_vreg_enabled_in_open_loop(chip)) { + rc = haptics_masked_write(chip, chip->cfg_addr_base, + HAP_CFG_CAL_EN_REG, CAL_RC_CLK_MASK, + CAL_RC_CLK_DISABLED_VAL); + if (rc < 0) + return rc; + + rc = haptics_masked_write(chip, chip->cfg_addr_base, + HAP_CFG_CAL_EN_REG, CAL_RC_CLK_MASK, + CAL_RC_CLK_AUTO_VAL); + if (rc < 0) + return rc; + + dev_dbg(chip->dev, "Toggle CAL_EN in open-loop-VREG playing\n"); + } } val = play->pattern_src; From 368406daae211c76ddd354dc8effe055c18062cd Mon Sep 17 00:00:00 2001 From: Fenglin Wu Date: Wed, 24 Jun 2020 11:44:41 +0800 Subject: [PATCH 34/76] input: qcom-hv-haptics: add LRA impedance detection support Add support for detecting LRA impedance. The detection is done using following procedure: set ISC to 250 mA and Vmax to 10 V, then sweep the duty cycle using binary approach based on the SC_FAULT status until reaching to the last step. The impedance can be only detected between a range and the resolution depends on the sweeping steps. Currently, total number of steps used for calculating LRA impedance using approximation is 5 which can achieve an accuracy of 1.25 Ohms. This detection is only available for PM8350B 2.0 chip and it can be triggered using command: echo 1 > /sys/class/qcom-haptics/lra_calibration After that, the result can be read out using command: cat /sys/class/qcom-haptics/lra_impedance Change-Id: I7edc00ecdb019ccf7ca5af459dec8105834706f5 Signed-off-by: Fenglin Wu --- drivers/input/misc/qcom-hv-haptics.c | 381 +++++++++++++++++++++------ 1 file changed, 302 insertions(+), 79 deletions(-) diff --git a/drivers/input/misc/qcom-hv-haptics.c b/drivers/input/misc/qcom-hv-haptics.c index e5fdfb1089a2..347403232f81 100644 --- a/drivers/input/misc/qcom-hv-haptics.c +++ b/drivers/input/misc/qcom-hv-haptics.c @@ -22,8 +22,13 @@ #include #include #include +#include /* 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) { @@ -1108,11 +1122,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; } @@ -1846,8 +1861,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); @@ -1857,6 +1870,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", @@ -2131,12 +2145,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) @@ -3298,16 +3306,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; } @@ -3338,43 +3358,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; @@ -3382,7 +3414,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); @@ -3391,7 +3424,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; @@ -3399,7 +3433,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 = @@ -3408,7 +3443,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) { @@ -3417,7 +3453,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 { @@ -3435,7 +3471,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; } } @@ -3444,10 +3480,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) @@ -3541,12 +3584,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, false); + 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 @@ -3567,61 +3808,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, false); - 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); @@ -3640,7 +3840,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; } @@ -3664,9 +3864,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); @@ -3794,6 +4014,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); From 75cf6e2792852539fdfa479e47deeaed05113171 Mon Sep 17 00:00:00 2001 From: Fenglin Wu Date: Tue, 1 Sep 2020 16:01:44 +0800 Subject: [PATCH 35/76] input: misc: qcom-hv-haptics: disable auto resonance for FIFO streaming Auto resonance needs to be disabled for FIFO streaming mode. Hence disable it when playing custom FIFO data. Change-Id: I11bb2e944bebf642b780b964bdea7907cae3c843 Signed-off-by: Fenglin Wu --- drivers/input/misc/qcom-hv-haptics.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/misc/qcom-hv-haptics.c b/drivers/input/misc/qcom-hv-haptics.c index 347403232f81..f655a05418ee 100644 --- a/drivers/input/misc/qcom-hv-haptics.c +++ b/drivers/input/misc/qcom-hv-haptics.c @@ -1688,7 +1688,7 @@ static int haptics_init_custom_effect(struct haptics_chip *chip) chip->custom_effect->vmax_mv = chip->config.vmax_mv; chip->custom_effect->t_lra_us = chip->config.t_lra_us; chip->custom_effect->src = FIFO; - chip->custom_effect->auto_res_disable = false; + chip->custom_effect->auto_res_disable = true; return 0; } From 155e0147ff92e953c4bc5bbb0efc7cb7d26840ca Mon Sep 17 00:00:00 2001 From: Subbaraman Narayanamurthy Date: Thu, 3 Sep 2020 17:39:57 -0700 Subject: [PATCH 36/76] input: qcom-hv-haptics: Disable haptics module during suspend To help with power savings, disable haptics module when the device enters suspend and enable it when it exits suspend. Change-Id: I8956be849b876a2b514c1de49f8ff447267d28a1 Signed-off-by: Subbaraman Narayanamurthy Signed-off-by: Fenglin Wu --- drivers/input/misc/qcom-hv-haptics.c | 54 ++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/drivers/input/misc/qcom-hv-haptics.c b/drivers/input/misc/qcom-hv-haptics.c index f655a05418ee..47ee2fa58fec 100644 --- a/drivers/input/misc/qcom-hv-haptics.c +++ b/drivers/input/misc/qcom-hv-haptics.c @@ -57,6 +57,9 @@ #define FIFO_EMPTY_BIT BIT(1) /* config register definitions in HAPTICS_CFG module */ +#define HAP_CFG_EN_CTL_REG 0x46 +#define HAPTICS_EN_BIT BIT(7) + #define HAP_CFG_DRV_CTRL_REG 0x47 #define PSTG_DLY_MASK GENMASK(7, 6) #define DRV_SLEW_RATE_MASK GENMASK(2, 0) @@ -4027,6 +4030,56 @@ static int haptics_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_PM_SLEEP +static int haptics_suspend(struct device *dev) +{ + struct haptics_chip *chip = dev_get_drvdata(dev); + struct haptics_play_info *play = &chip->play; + u8 val = 0; + int rc; + + if (chip->cfg_revision == HAP_CFG_V1) + return 0; + + if ((play->pattern_src == FIFO) && + atomic_read(&play->fifo_status.is_busy)) { + if (atomic_read(&play->fifo_status.written_done) == 0) { + dev_dbg(chip->dev, "cancelling FIFO playing\n"); + atomic_set(&play->fifo_status.cancelled, 1); + } + + rc = haptics_stop_fifo_play(chip); + if (rc < 0) { + dev_err(chip->dev, "stop FIFO playing failed, rc=%d\n"); + return rc; + } + } else { + rc = haptics_enable_play(chip, false); + if (rc < 0) + return rc; + } + + return haptics_write(chip, chip->cfg_addr_base, + HAP_CFG_EN_CTL_REG, &val, 1); +} + +static int haptics_resume(struct device *dev) +{ + struct haptics_chip *chip = dev_get_drvdata(dev); + u8 val = HAPTICS_EN_BIT; + + if (chip->cfg_revision == HAP_CFG_V1) + return 0; + + return haptics_write(chip, chip->cfg_addr_base, + HAP_CFG_EN_CTL_REG, &val, 1); +} +#endif + +static const struct dev_pm_ops haptics_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(haptics_suspend, haptics_resume) +}; + static const struct of_device_id haptics_match_table[] = { { .compatible = "qcom,hv-haptics" }, { .compatible = "qcom,pm8350b-haptics" }, @@ -4037,6 +4090,7 @@ static struct platform_driver haptics_driver = { .driver = { .name = "qcom-hv-haptics", .of_match_table = haptics_match_table, + .pm = &haptics_pm_ops, }, .probe = haptics_probe, .remove = haptics_remove, From 01f0d334a8e44a04d19f70dcba8b570164af5e42 Mon Sep 17 00:00:00 2001 From: Fenglin Wu Date: Thu, 22 Oct 2020 11:41:44 +0800 Subject: [PATCH 37/76] input: qcom-hv-haptics: only use 4-byte writes when refilling FIFO The haptics module supports writing FIFO samples using 4-byte burst mode as well as 1-byte mode. During FIFO refilling, if a 1-byte write was used, then the hardware would fill zeros automatically after that to keep the FIFO data 4-byte aligned. When this happened in the middle of playing, the hardware inserted 0 values would cause spurs on the haptics output. To fix this, only use 4-byte burst writes during FIFO refilling until the end of the play. Change-Id: I0441942585e0e0d5da62ec4a7123b2763b292739 Signed-off-by: Fenglin Wu --- drivers/input/misc/qcom-hv-haptics.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/input/misc/qcom-hv-haptics.c b/drivers/input/misc/qcom-hv-haptics.c index 47ee2fa58fec..1363d2ea80d7 100644 --- a/drivers/input/misc/qcom-hv-haptics.c +++ b/drivers/input/misc/qcom-hv-haptics.c @@ -2282,8 +2282,19 @@ static irqreturn_t fifo_empty_irq_handler(int irq, void *data) if (num < 0) return IRQ_HANDLED; + /* + * With HAPTICS_PATTERN module revision 2.0 and above, if use + * 1-byte write before 4-byte write, the hardware would insert + * zeros in between to keep the FIFO samples 4-byte aligned, and + * the inserted 0 values would cause HW stop driving hence spurs + * will be seen on the haptics output. So only use 1-byte write + * at the end of FIFO streaming. + */ if (samples_left <= num) num = samples_left; + else if ((chip->ptn_revision >= HAP_PTN_V2) && + (num % HAP_PTN_V2_FIFO_DIN_NUM)) + num -= (num % HAP_PTN_V2_FIFO_DIN_NUM); samples = fifo->samples + status->samples_written; From be0172dabf2e05625b267c088a77a8a196bb1221 Mon Sep 17 00:00:00 2001 From: Fenglin Wu Date: Mon, 26 Oct 2020 07:47:09 +0800 Subject: [PATCH 38/76] input: qcom-hv-haptics: correct register values when toggling RC_CLK_CAL Correct register values when toggling RC_CLK_CAL for playing haptics in HBoost open loop case. Change-Id: Ia48ab3af93181c61ec719716634e94a819353c8b Signed-off-by: Fenglin Wu --- drivers/input/misc/qcom-hv-haptics.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/input/misc/qcom-hv-haptics.c b/drivers/input/misc/qcom-hv-haptics.c index 1363d2ea80d7..d2c5aaff2582 100644 --- a/drivers/input/misc/qcom-hv-haptics.c +++ b/drivers/input/misc/qcom-hv-haptics.c @@ -1160,15 +1160,17 @@ static int haptics_enable_play(struct haptics_chip *chip, bool en) if (((val & CAL_RC_CLK_MASK) == CAL_RC_CLK_AUTO_VAL << CAL_RC_CLK_SHIFT) && is_boost_vreg_enabled_in_open_loop(chip)) { + val = CAL_RC_CLK_DISABLED_VAL << CAL_RC_CLK_SHIFT; rc = haptics_masked_write(chip, chip->cfg_addr_base, HAP_CFG_CAL_EN_REG, CAL_RC_CLK_MASK, - CAL_RC_CLK_DISABLED_VAL); + val); if (rc < 0) return rc; + val = CAL_RC_CLK_AUTO_VAL << CAL_RC_CLK_SHIFT; rc = haptics_masked_write(chip, chip->cfg_addr_base, HAP_CFG_CAL_EN_REG, CAL_RC_CLK_MASK, - CAL_RC_CLK_AUTO_VAL); + val); if (rc < 0) return rc; From 19152cdd28b46f2a6f77e70298a255545516ca69 Mon Sep 17 00:00:00 2001 From: Fenglin Wu Date: Wed, 28 Oct 2020 15:45:56 +0800 Subject: [PATCH 39/76] input: qcom-hv-haptics: switch API to trigger PBS for ISC configuration SW trigger for PBS to configure ISC in LRA detection doesn't need to pass any parameter or wait on any condition including checking status. Hence switch using qpnp_pbs_trigger_single_event() instead of qpnp_pbs_trigger_event() to trigger PBS. Change-Id: I8b252e293265994976924435f8a6a11d9b463119 Signed-off-by: Fenglin Wu --- drivers/input/misc/qcom-hv-haptics.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/input/misc/qcom-hv-haptics.c b/drivers/input/misc/qcom-hv-haptics.c index d2c5aaff2582..4dcaec169f07 100644 --- a/drivers/input/misc/qcom-hv-haptics.c +++ b/drivers/input/misc/qcom-hv-haptics.c @@ -3600,7 +3600,6 @@ 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; @@ -3610,7 +3609,7 @@ static int haptics_pbs_trigger_isc_config(struct haptics_chip *chip) return -ENODEV; } - rc = qpnp_pbs_trigger_event(chip->pbs_node, PBS_SW_TRG_LRA_ISC_CFG_BIT); + rc = qpnp_pbs_trigger_single_event(chip->pbs_node); if (rc < 0) dev_err(chip->dev, "Trigger PBS to config ISC failed, rc=%d\n", rc); From f1cd3fe57e48bd610d4f37ed868c68bb623caae5 Mon Sep 17 00:00:00 2001 From: Fenglin Wu Date: Mon, 26 Oct 2020 09:50:44 +0800 Subject: [PATCH 40/76] input: qcom-hv-haptics: add support to operate from non-HBoost regulator By default, haptics module is supplied from HBoost module and it talks to HBoost module in hardware to get an appropriate operating voltage. Haptics module can also be supplied from non-HBoost regulator and it requires software to request the voltage. Add support to operate haptics under a fixed voltage from a non-HBoost regulator. Make the following modifications: 1) Set voltage and enable the regulator before enabling the play. 2) Force VREG_RDY signal in haptics module before enabling the play. 3) Scale the DIRECT_PLAY amplitude to achieve appropriate output voltage in constant playing. 4) Scale the DIRECT_PLAY amplitude instead of Vmax setting for gain setting in constant playing. 5) Adjust the LRA impedance detectability according to the regulator output voltage. Change-Id: If832b6512d7641ad3d271bb68f4bdb7dbb90a5c5 Signed-off-by: Fenglin Wu --- drivers/input/misc/qcom-hv-haptics.c | 287 ++++++++++++++++++++++++--- 1 file changed, 256 insertions(+), 31 deletions(-) diff --git a/drivers/input/misc/qcom-hv-haptics.c b/drivers/input/misc/qcom-hv-haptics.c index 4dcaec169f07..9618dd819c50 100644 --- a/drivers/input/misc/qcom-hv-haptics.c +++ b/drivers/input/misc/qcom-hv-haptics.c @@ -142,6 +142,9 @@ #define VMAX_HDRM_STEP_MV 50 #define VMAX_HDRM_MAX_MV 6350 +#define HAP_CFG_VSET_CFG_REG 0x68 +#define FORCE_VREG_RDY_BIT BIT(0) + #define HAP_CFG_MOD_STATUS_SEL_REG 0x70 #define MOD_STATUS_SEL_CAL_TLRA_CL_STS_VAL 0 #define MOD_STATUS_SEL_LAST_GOOD_TLRA_VAL 3 @@ -249,6 +252,7 @@ #define CHAR_BRAKE_MODE 24 #define HW_BRAKE_CYCLES 5 #define F_LRA_VARIATION_HZ 5 +#define NON_HBOOST_MAX_VMAX_MV 4000 #define MAX_FIFO_SAMPLES(chip) \ ((chip)->ptn_revision == HAP_PTN_V1 ? 104 : 640) @@ -427,6 +431,7 @@ struct haptics_hw_config { u32 cl_t_lra_us; u32 lra_min_mohms; u32 lra_max_mohms; + u32 lra_open_mohms; u32 preload_effect; u32 fifo_empty_thresh; u16 rc_clk_cal_count; @@ -456,7 +461,9 @@ struct haptics_chip { struct nvmem_device *hap_cfg_nvmem; struct device_node *pbs_node; struct class hap_class; + struct regulator *hpwr_vreg; int fifo_empty_irq; + u32 hpwr_voltage_mv; u32 effects_count; u32 cfg_addr_base; u32 ptn_addr_base; @@ -467,6 +474,7 @@ struct haptics_chip { bool fifo_empty_irq_en; bool swr_slave_enabled; bool clamp_at_5v; + bool hpwr_vreg_enabled; }; struct haptics_reg_info { @@ -1055,6 +1063,23 @@ static int haptics_set_vmax_headroom_mv(struct haptics_chip *chip, u32 hdrm_mv) return rc; } +static int haptics_get_vmax_headroom_mv(struct haptics_chip *chip, u32 *hdrm_mv) +{ + int rc; + u8 val; + + rc = haptics_read(chip, chip->cfg_addr_base, + HAP_CFG_VMAX_HDRM_REG, &val, 1); + if (rc < 0) { + dev_err(chip->dev, "Get Vmax HDRM failed, rc=%d\n", + rc); + return rc; + } + + *hdrm_mv = (val & VMAX_HDRM_MASK) * VMAX_HDRM_STEP_MV; + return 0; +} + static int haptics_enable_autores(struct haptics_chip *chip, bool en) { int rc; @@ -1134,32 +1159,73 @@ static bool is_boost_vreg_enabled_in_open_loop(struct haptics_chip *chip) return false; } -static int haptics_enable_play(struct haptics_chip *chip, bool en) +static int haptics_enable_hpwr_vreg(struct haptics_chip *chip, bool en) +{ + int rc; + + if (chip->hpwr_vreg == NULL || chip->hpwr_vreg_enabled == en) + return 0; + + if (en) { + rc = regulator_set_voltage(chip->hpwr_vreg, + chip->hpwr_voltage_mv * 1000, INT_MAX); + if (rc < 0) { + dev_err(chip->dev, "Set hpwr voltage failed, rc=%d\n", + rc); + return rc; + } + + rc = regulator_enable(chip->hpwr_vreg); + if (rc < 0) { + dev_err(chip->dev, "Enable hpwr failed, rc=%d\n", + rc); + regulator_set_voltage(chip->hpwr_vreg, 0, INT_MAX); + return rc; + } + } else { + rc = regulator_disable(chip->hpwr_vreg); + if (rc < 0) { + dev_err(chip->dev, "Disable hpwr failed, rc=%d\n", + rc); + return rc; + } + + rc = regulator_set_voltage(chip->hpwr_vreg, 0, INT_MAX); + if (rc < 0) { + dev_err(chip->dev, "Set hpwr voltage failed, rc=%d\n", + rc); + return rc; + } + } + + dev_dbg(chip->dev, "%s hpwr vreg\n", en ? "enabled" : "disabled"); + chip->hpwr_vreg_enabled = en; + return 0; +} + +static int haptics_open_loop_drive_config(struct haptics_chip *chip, bool en) { - struct haptics_play_info *play = &chip->play; int rc; u8 val; - if (en) { - val = SC_CLR_BIT | AUTO_RES_ERR_CLR_BIT | - HPWR_RDY_FAULT_CLR_BIT; - rc = haptics_write(chip, chip->cfg_addr_base, - HAP_CFG_FAULT_CLR_REG, &val, 1); + if ((is_boost_vreg_enabled_in_open_loop(chip) || + chip->hpwr_vreg != NULL) && en) { + /* Force VREG_RDY */ + val = FORCE_VREG_RDY_BIT; + rc = haptics_masked_write(chip, chip->cfg_addr_base, + HAP_CFG_VSET_CFG_REG, FORCE_VREG_RDY_BIT, + FORCE_VREG_RDY_BIT); if (rc < 0) return rc; - /* - * Toggle RC_CLK_CAL_EN if it's in auto mode and - * haptics boost is working in open loop - */ + /* Toggle RC_CLK_CAL_EN if it's in auto mode */ rc = haptics_read(chip, chip->cfg_addr_base, HAP_CFG_CAL_EN_REG, &val, 1); if (rc < 0) return rc; - if (((val & CAL_RC_CLK_MASK) == - CAL_RC_CLK_AUTO_VAL << CAL_RC_CLK_SHIFT) - && is_boost_vreg_enabled_in_open_loop(chip)) { + if ((val & CAL_RC_CLK_MASK) == + CAL_RC_CLK_AUTO_VAL << CAL_RC_CLK_SHIFT) { val = CAL_RC_CLK_DISABLED_VAL << CAL_RC_CLK_SHIFT; rc = haptics_masked_write(chip, chip->cfg_addr_base, HAP_CFG_CAL_EN_REG, CAL_RC_CLK_MASK, @@ -1176,8 +1242,35 @@ static int haptics_enable_play(struct haptics_chip *chip, bool en) dev_dbg(chip->dev, "Toggle CAL_EN in open-loop-VREG playing\n"); } + } else { + val = en ? FORCE_VREG_RDY_BIT : 0; + rc = haptics_masked_write(chip, chip->cfg_addr_base, + HAP_CFG_VSET_CFG_REG, + FORCE_VREG_RDY_BIT, val); } + return rc; +} + +static int haptics_enable_play(struct haptics_chip *chip, bool en) +{ + struct haptics_play_info *play = &chip->play; + int rc; + u8 val; + + if (en) { + val = SC_CLR_BIT | AUTO_RES_ERR_CLR_BIT | + HPWR_RDY_FAULT_CLR_BIT; + rc = haptics_write(chip, chip->cfg_addr_base, + HAP_CFG_FAULT_CLR_REG, &val, 1); + if (rc < 0) + return rc; + } + + rc = haptics_open_loop_drive_config(chip, en); + if (rc < 0) + return rc; + val = play->pattern_src; if (play->brake && !play->brake->disabled) val |= BRAKE_EN_BIT; @@ -1576,6 +1669,7 @@ static int haptics_set_fifo(struct haptics_chip *chip, struct fifo_cfg *fifo) static int haptics_load_constant_effect(struct haptics_chip *chip, u8 amplitude) { struct haptics_play_info *play = &chip->play; + u32 hdrm_mv, vmax_mv = chip->config.vmax_mv; int rc = 0; mutex_lock(&chip->play.lock); @@ -1588,8 +1682,17 @@ static int haptics_load_constant_effect(struct haptics_chip *chip, u8 amplitude) /* No effect data when playing constant waveform */ play->effect = NULL; + /* Fix Vmax to (hpwr_vreg_mv - hdrm_mv) in non-HBOOST regulator case */ + if (chip->hpwr_vreg) { + rc = haptics_get_vmax_headroom_mv(chip, &hdrm_mv); + if (rc < 0) + goto unlock; + + vmax_mv = chip->hpwr_voltage_mv - hdrm_mv; + } + /* configure VMAX in case it was changed in previous effect playing */ - rc = haptics_set_vmax_mv(chip, chip->config.vmax_mv); + rc = haptics_set_vmax_mv(chip, vmax_mv); if (rc < 0) goto unlock; @@ -1899,6 +2002,26 @@ static int haptics_load_periodic_effect(struct haptics_chip *chip, return rc; } +static u8 get_direct_play_max_amplitude(struct haptics_chip *chip) +{ + u32 amplitude = DIRECT_PLAY_MAX_AMPLITUDE, hdrm_mv; + int rc; + + if (chip->hpwr_vreg) { + rc = haptics_get_vmax_headroom_mv(chip, &hdrm_mv); + if (rc < 0) + return 0; + + amplitude *= chip->config.vmax_mv; + amplitude /= (chip->hpwr_voltage_mv - hdrm_mv); + if (amplitude > DIRECT_PLAY_MAX_AMPLITUDE) + amplitude = DIRECT_PLAY_MAX_AMPLITUDE; + } + + dev_dbg(chip->dev, "max amplitude for direct play: %#x\n", amplitude); + return (u8)amplitude; +} + static int haptics_upload_effect(struct input_dev *dev, struct ff_effect *effect, struct ff_effect *old) { @@ -1912,9 +2035,10 @@ static int haptics_upload_effect(struct input_dev *dev, case FF_CONSTANT: length_us = effect->replay.length * USEC_PER_MSEC; level = effect->u.constant.level; - tmp = level * DIRECT_PLAY_MAX_AMPLITUDE; + tmp = get_direct_play_max_amplitude(chip); + tmp *= level; amplitude = tmp / 0x7fff; - dev_dbg(chip->dev, "upload constant effect, length = %dus, amplitude = %d\n", + dev_dbg(chip->dev, "upload constant effect, length = %dus, amplitude = %#x\n", length_us, amplitude); haptics_load_constant_effect(chip, amplitude); if (rc < 0) { @@ -1961,7 +2085,11 @@ static int haptics_upload_effect(struct input_dev *dev, return -EINVAL; } - return 0; + rc = haptics_enable_hpwr_vreg(chip, true); + if (rc < 0) + dev_err(chip->dev, "enable hpwr_vreg failed, rc=%d\n"); + + return rc; } static void haptics_fifo_empty_irq_config(struct haptics_chip *chip, @@ -2065,7 +2193,11 @@ static int haptics_erase(struct input_dev *dev, int effect_id) } } - return 0; + rc = haptics_enable_hpwr_vreg(chip, false); + if (rc < 0) + dev_err(chip->dev, "disable hpwr_vreg failed, rc=%d\n"); + + return rc; } static void haptics_set_gain(struct input_dev *dev, u16 gain) @@ -2073,7 +2205,7 @@ static void haptics_set_gain(struct input_dev *dev, u16 gain) struct haptics_chip *chip = input_get_drvdata(dev); struct haptics_hw_config *config = &chip->config; struct haptics_play_info *play = &chip->play; - u32 vmax_mv; + u32 vmax_mv, amplitude; if (gain == 0) return; @@ -2081,6 +2213,20 @@ static void haptics_set_gain(struct input_dev *dev, u16 gain) if (gain > 0x7fff) gain = 0x7fff; + dev_dbg(chip->dev, "Set gain: %#x\n", gain); + + /* scale amplitude when playing in DIRECT_PLAY mode */ + if (chip->play.pattern_src == DIRECT_PLAY) { + amplitude = get_direct_play_max_amplitude(chip); + amplitude *= gain; + amplitude /= 0x7fff; + + dev_dbg(chip->dev, "Set amplitude: %#x\n", amplitude); + haptics_set_direct_play(chip, (u8)amplitude); + return; + } + + /* scale Vmax when playing in other modes */ vmax_mv = config->vmax_mv; if (play->effect) vmax_mv = play->effect->vmax_mv; @@ -3347,6 +3493,38 @@ static int haptics_get_revision(struct haptics_chip *chip) return 0; } +static int haptics_parse_hpwr_vreg_dt(struct haptics_chip *chip) +{ + struct device_node *node = chip->dev->of_node; + int rc; + + if (!of_find_property(node, "qcom,hpwr-supply", NULL)) + return 0; + + chip->hpwr_vreg = devm_regulator_get(chip->dev, "qcom,hpwr"); + if (IS_ERR(chip->hpwr_vreg)) { + rc = PTR_ERR(chip->hpwr_vreg); + if (rc != -EPROBE_DEFER) + dev_err(chip->dev, "Failed to get qcom,hpwr-supply, rc=%d\n", + rc); + return rc; + } + + rc = of_property_read_u32(node, "qcom,hpwr-voltage-mv", + &chip->hpwr_voltage_mv); + if (rc < 0) { + dev_err(chip->dev, "Failed to read qcom,hpwr-voltage-mv, rc=%d\n", + rc); + return rc; + } + + if (chip->hpwr_voltage_mv == 0 || + chip->hpwr_voltage_mv > NON_HBOOST_MAX_VMAX_MV) + return -EINVAL; + + return 0; +} + static int haptics_parse_dt(struct haptics_chip *chip) { struct haptics_hw_config *config = &chip->config; @@ -3355,6 +3533,10 @@ static int haptics_parse_dt(struct haptics_chip *chip) const __be32 *addr; int rc = 0, tmp; + rc = haptics_parse_hpwr_vreg_dt(chip); + if (rc < 0) + return rc; + if (of_find_property(node, "nvmem-cells", NULL)) { chip->cl_brake_nvmem = devm_nvmem_cell_get(chip->dev, "hap_cl_brake"); @@ -3619,9 +3801,24 @@ static int haptics_pbs_trigger_isc_config(struct haptics_chip *chip) #define MAX_SWEEP_STEPS 5 #define MAX_IMPEDANCE_MOHM 40000 +#define MIN_ISC_MA 250 #define MIN_DUTY_MILLI_PCT 0 #define MAX_DUTY_MILLI_PCT 100000 #define LRA_CONFIG_REGS 3 +static u32 get_lra_impedance_capable_max(struct haptics_chip *chip) +{ + u32 mohms = MAX_IMPEDANCE_MOHM; + + if (chip->clamp_at_5v) + mohms = MAX_IMPEDANCE_MOHM / 2; + + if (chip->hpwr_vreg) + mohms = (chip->hpwr_voltage_mv * 1000) / MIN_ISC_MA; + + dev_dbg(chip->dev, "LRA impedance capable max: %u mohms\n", mohms); + return mohms; +} + static int haptics_detect_lra_impedance(struct haptics_chip *chip) { int rc, i; @@ -3633,7 +3830,7 @@ static int haptics_detect_lra_impedance(struct haptics_chip *chip) 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; + u32 amplitude, lra_min_mohms, lra_max_mohms, capability_mohms; if (chip->cfg_revision == HAP_CFG_V1) { dev_dbg(chip->dev, "HAP_CFG V1.0 doesn't support impedance detection\n"); @@ -3662,6 +3859,10 @@ static int haptics_detect_lra_impedance(struct haptics_chip *chip) goto restore; } + rc = haptics_enable_hpwr_vreg(chip, true); + 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 */ @@ -3704,11 +3905,12 @@ static int haptics_detect_lra_impedance(struct haptics_chip *chip) usleep_range(4000, 5000); } - lra_min_mohms = low_milli_pct * MAX_IMPEDANCE_MOHM / 100000; - lra_max_mohms = high_milli_pct * MAX_IMPEDANCE_MOHM / 100000; + capability_mohms = get_lra_impedance_capable_max(chip); + lra_min_mohms = low_milli_pct * capability_mohms / 100000; + lra_max_mohms = high_milli_pct * capability_mohms / 100000; if (lra_min_mohms == 0) dev_warn(chip->dev, "Short circuit detected!\n"); - else if (lra_max_mohms == MAX_IMPEDANCE_MOHM) + else if (lra_max_mohms >= capability_mohms) dev_warn(chip->dev, "Open circuit detected!\n"); else dev_dbg(chip->dev, "LRA impedance is between %u - %u mohms\n", @@ -3716,9 +3918,14 @@ static int haptics_detect_lra_impedance(struct haptics_chip *chip) chip->config.lra_min_mohms = lra_min_mohms; chip->config.lra_max_mohms = lra_max_mohms; + chip->config.lra_open_mohms = capability_mohms; restore: /* Disable play in case it's not been disabled */ haptics_enable_play(chip, false); + rc = haptics_enable_hpwr_vreg(chip, false); + if (rc < 0) + return rc; + /* Trigger PBS to restore 1500mA ISC setting */ rc = haptics_pbs_trigger_isc_config(chip); if (rc < 0) @@ -3739,7 +3946,8 @@ static int haptics_detect_lra_impedance(struct haptics_chip *chip) static int haptics_detect_lra_frequency(struct haptics_chip *chip) { int rc; - u8 autores_cfg; + u8 autores_cfg, amplitude; + u32 vmax_mv = chip->config.vmax_mv; rc = haptics_read(chip, chip->cfg_addr_base, HAP_CFG_AUTORES_CFG_REG, &autores_cfg, 1); @@ -3760,15 +3968,24 @@ static int haptics_detect_lra_frequency(struct haptics_chip *chip) 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); + /* Fix Vmax to (hpwr_vreg_mv - hdrm_mv) in non-HBOOST regulator case */ + if (chip->hpwr_vreg) + vmax_mv = chip->hpwr_voltage_mv - LRA_CALIBRATION_VMAX_HDRM_MV; + + rc = haptics_set_vmax_mv(chip, vmax_mv); + if (rc < 0) + goto restore; + + amplitude = get_direct_play_max_amplitude(chip); + rc = haptics_set_direct_play(chip, amplitude); + if (rc < 0) + goto restore; + + rc = haptics_enable_hpwr_vreg(chip, true); if (rc < 0) goto restore; @@ -3793,6 +4010,10 @@ static int haptics_detect_lra_frequency(struct haptics_chip *chip) restore: /* Disable play in case it's not been disabled */ haptics_enable_play(chip, false); + rc = haptics_enable_hpwr_vreg(chip, false); + if (rc < 0) + return rc; + rc = haptics_write(chip, chip->cfg_addr_base, HAP_CFG_AUTORES_CFG_REG, &autores_cfg, 1); @@ -3889,7 +4110,7 @@ static ssize_t lra_impedance_show(struct class *c, 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) + else if (chip->config.lra_max_mohms >= chip->config.lra_open_mohms) return scnprintf(buf, PAGE_SIZE, "%s\n", "Open circuit"); else return scnprintf(buf, PAGE_SIZE, "%u ~ %u mohms\n", @@ -4071,6 +4292,10 @@ static int haptics_suspend(struct device *dev) return rc; } + rc = haptics_enable_hpwr_vreg(chip, false); + if (rc < 0) + return rc; + return haptics_write(chip, chip->cfg_addr_base, HAP_CFG_EN_CTL_REG, &val, 1); } From 883ca135016468150332c76b518ab27e0b94fa31 Mon Sep 17 00:00:00 2001 From: Fenglin Wu Date: Wed, 18 Nov 2020 21:35:27 +0800 Subject: [PATCH 41/76] input: qcom-hv-haptics: add mutex lock for FIFO play When stop command comes during FIFO refill, it would free custom_effect FIFO sample memory and set the pointer to NULL, and this would cause the NULL pointer dereference during FIFO refill. To fix this, add a mutex lock between FIFO stop and FIFO refill sequence to avoid a race. Change-Id: I8792c2183f06e84fca3a7c4dddfed667b25f2528 Signed-off-by: Fenglin Wu --- drivers/input/misc/qcom-hv-haptics.c | 51 ++++++++++++++++++++-------- 1 file changed, 37 insertions(+), 14 deletions(-) diff --git a/drivers/input/misc/qcom-hv-haptics.c b/drivers/input/misc/qcom-hv-haptics.c index 9618dd819c50..28fa714075ba 100644 --- a/drivers/input/misc/qcom-hv-haptics.c +++ b/drivers/input/misc/qcom-hv-haptics.c @@ -1532,6 +1532,11 @@ static int haptics_update_fifo_samples(struct haptics_chip *chip, { int rc, count, i; + if (samples == NULL) { + dev_err(chip->dev, "no FIFO samples available\n"); + return -EINVAL; + } + if (chip->ptn_revision == HAP_PTN_V1) { for (i = 0; i < length; i++) { rc = haptics_update_fifo_sample_v1(chip, samples[i]); @@ -1873,6 +1878,7 @@ static int haptics_load_custom_effect(struct haptics_chip *chip, return rc; } + mutex_lock(&chip->play.lock); fifo->period_per_s = rc; /* * Before allocating samples buffer, free the old sample @@ -1880,8 +1886,10 @@ static int haptics_load_custom_effect(struct haptics_chip *chip, */ kfree(fifo->samples); fifo->samples = kcalloc(custom_data.length, sizeof(u8), GFP_KERNEL); - if (!fifo->samples) - return -ENOMEM; + if (!fifo->samples) { + rc = -ENOMEM; + goto unlock; + } if (copy_from_user(fifo->samples, (u8 __user *)custom_data.data, @@ -1895,11 +1903,10 @@ static int haptics_load_custom_effect(struct haptics_chip *chip, fifo->play_length_us = get_fifo_play_length_us(fifo, chip->custom_effect->t_lra_us); - mutex_lock(&chip->play.lock); if (chip->play.in_calibration) { dev_err(chip->dev, "calibration in progress, ignore playing custom effect\n"); rc = -EBUSY; - goto unlock; + goto cleanup; } play->effect = chip->custom_effect; @@ -1907,7 +1914,7 @@ static int haptics_load_custom_effect(struct haptics_chip *chip, play->vmax_mv = (magnitude * chip->custom_effect->vmax_mv) / 0x7fff; rc = haptics_set_vmax_mv(chip, play->vmax_mv); if (rc < 0) - goto unlock; + goto cleanup; rc = haptics_enable_autores(chip, !play->effect->auto_res_disable); if (rc < 0) @@ -1916,15 +1923,15 @@ static int haptics_load_custom_effect(struct haptics_chip *chip, play->pattern_src = FIFO; rc = haptics_set_fifo(chip, play->effect->fifo); if (rc < 0) - goto unlock; + goto cleanup; mutex_unlock(&chip->play.lock); return 0; -unlock: - mutex_unlock(&chip->play.lock); cleanup: kfree(fifo->samples); fifo->samples = NULL; +unlock: + mutex_unlock(&chip->play.lock); return rc; } @@ -2179,6 +2186,7 @@ static int haptics_erase(struct input_dev *dev, int effect_id) struct haptics_play_info *play = &chip->play; int rc; + mutex_lock(&play->lock); if ((play->pattern_src == FIFO) && atomic_read(&play->fifo_status.is_busy)) { if (atomic_read(&play->fifo_status.written_done) == 0) { @@ -2189,9 +2197,11 @@ static int haptics_erase(struct input_dev *dev, int effect_id) rc = haptics_stop_fifo_play(chip); if (rc < 0) { dev_err(chip->dev, "stop FIFO playing failed, rc=%d\n"); + mutex_unlock(&play->lock); return rc; } } + mutex_unlock(&play->lock); rc = haptics_enable_hpwr_vreg(chip, false); if (rc < 0) @@ -2400,6 +2410,7 @@ static irqreturn_t fifo_empty_irq_handler(int irq, void *data) return IRQ_HANDLED; } + mutex_lock(&chip->play.lock); if (atomic_read(&chip->play.fifo_status.written_done) == 1) { /* * Check the FIFO real time fill status before stopping @@ -2411,24 +2422,29 @@ static irqreturn_t fifo_empty_irq_handler(int irq, void *data) if (num != MAX_FIFO_SAMPLES(chip)) { dev_dbg(chip->dev, "%d FIFO samples still in playing\n", MAX_FIFO_SAMPLES(chip) - num); - return IRQ_HANDLED; + goto unlock; } rc = haptics_stop_fifo_play(chip); if (rc < 0) - return IRQ_HANDLED; + goto unlock; dev_dbg(chip->dev, "FIFO playing is done\n"); } else { if (atomic_read(&status->cancelled) == 1) { dev_dbg(chip->dev, "FIFO programming got cancelled\n"); - return IRQ_HANDLED; + goto unlock; + } + + if (!fifo || !fifo->samples) { + dev_err(chip->dev, "no FIFO samples available\n"); + goto unlock; } samples_left = fifo->num_s - status->samples_written; num = haptics_get_available_fifo_memory(chip); if (num < 0) - return IRQ_HANDLED; + goto unlock; /* * With HAPTICS_PATTERN module revision 2.0 and above, if use @@ -2451,7 +2467,7 @@ static irqreturn_t fifo_empty_irq_handler(int irq, void *data) if (rc < 0) { dev_err(chip->dev, "Update FIFO samples failed, rc=%d\n", rc); - return IRQ_HANDLED; + goto unlock; } status->samples_written += num; @@ -2462,6 +2478,8 @@ static irqreturn_t fifo_empty_irq_handler(int irq, void *data) } } +unlock: + mutex_unlock(&chip->play.lock); return IRQ_HANDLED; } @@ -4274,6 +4292,7 @@ static int haptics_suspend(struct device *dev) if (chip->cfg_revision == HAP_CFG_V1) return 0; + mutex_lock(&play->lock); if ((play->pattern_src == FIFO) && atomic_read(&play->fifo_status.is_busy)) { if (atomic_read(&play->fifo_status.written_done) == 0) { @@ -4284,13 +4303,17 @@ static int haptics_suspend(struct device *dev) rc = haptics_stop_fifo_play(chip); if (rc < 0) { dev_err(chip->dev, "stop FIFO playing failed, rc=%d\n"); + mutex_unlock(&play->lock); return rc; } } else { rc = haptics_enable_play(chip, false); - if (rc < 0) + if (rc < 0) { + mutex_unlock(&play->lock); return rc; + } } + mutex_unlock(&play->lock); rc = haptics_enable_hpwr_vreg(chip, false); if (rc < 0) From 19b7ad347f16ff958ed3214f0a2fc3f5ad96e286 Mon Sep 17 00:00:00 2001 From: Fenglin Wu Date: Tue, 8 Dec 2020 16:49:43 +0800 Subject: [PATCH 42/76] input: qcom-hv-haptics: fix out of bound of FIFO samples array When calculating play length for FIFO play, it is out of bound of the FIFO samples array. Fix it. Change-Id: I8ad458802a0070f493ac53160ab90a6cf648d9e7 Signed-off-by: Fenglin Wu --- drivers/input/misc/qcom-hv-haptics.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/misc/qcom-hv-haptics.c b/drivers/input/misc/qcom-hv-haptics.c index 28fa714075ba..c6294c374a41 100644 --- a/drivers/input/misc/qcom-hv-haptics.c +++ b/drivers/input/misc/qcom-hv-haptics.c @@ -675,7 +675,7 @@ static int get_fifo_play_length_us(struct fifo_cfg *fifo, u32 t_lra_us) if (!fifo) return -EINVAL; - for (i = fifo->num_s; i > 0; i--) + for (i = fifo->num_s - 1; i > 0; i--) if (fifo->samples[i] != 0) break; From 72e5413d75e2342d1d490a245b2dbb1d7ff1d41f Mon Sep 17 00:00:00 2001 From: Fenglin Wu Date: Mon, 14 Dec 2020 11:00:04 +0800 Subject: [PATCH 43/76] input: qcom-hv-haptics: keep FORCE_VREG_RDY always set in non-HBoost case FORCE_VREG_RDY bit can be always set when non-HBoost regulator is used and don't clear it when stopping play in non-HBoost case. Also set this bit in driver initialization in non-HBoost case so that swr-haptics driver can take the advantage of triggering SWR play without setting this bit. Change-Id: I6283396d9d5a4f0b87d87932d6db2d8e7bb7a623 Signed-off-by: Fenglin Wu --- drivers/input/misc/qcom-hv-haptics.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/drivers/input/misc/qcom-hv-haptics.c b/drivers/input/misc/qcom-hv-haptics.c index c6294c374a41..2f95f454618c 100644 --- a/drivers/input/misc/qcom-hv-haptics.c +++ b/drivers/input/misc/qcom-hv-haptics.c @@ -1205,13 +1205,12 @@ static int haptics_enable_hpwr_vreg(struct haptics_chip *chip, bool en) static int haptics_open_loop_drive_config(struct haptics_chip *chip, bool en) { - int rc; + int rc = 0; u8 val; if ((is_boost_vreg_enabled_in_open_loop(chip) || chip->hpwr_vreg != NULL) && en) { /* Force VREG_RDY */ - val = FORCE_VREG_RDY_BIT; rc = haptics_masked_write(chip, chip->cfg_addr_base, HAP_CFG_VSET_CFG_REG, FORCE_VREG_RDY_BIT, FORCE_VREG_RDY_BIT); @@ -1242,11 +1241,10 @@ static int haptics_open_loop_drive_config(struct haptics_chip *chip, bool en) dev_dbg(chip->dev, "Toggle CAL_EN in open-loop-VREG playing\n"); } - } else { - val = en ? FORCE_VREG_RDY_BIT : 0; + } else if (chip->hpwr_vreg == NULL) { rc = haptics_masked_write(chip, chip->cfg_addr_base, HAP_CFG_VSET_CFG_REG, - FORCE_VREG_RDY_BIT, val); + FORCE_VREG_RDY_BIT, 0); } return rc; @@ -2335,6 +2333,15 @@ static int haptics_hw_init(struct haptics_chip *chip) if (rc < 0) return rc; + /* Force VREG_RDY if non-HBoost is used for powering haptics */ + if (chip->hpwr_vreg) { + rc = haptics_masked_write(chip, chip->cfg_addr_base, + HAP_CFG_VSET_CFG_REG, FORCE_VREG_RDY_BIT, + FORCE_VREG_RDY_BIT); + if (rc < 0) + return rc; + } + if (config->is_erm) return 0; From 2d78e72322a560e70233e80bf2e65c70629afd5f Mon Sep 17 00:00:00 2001 From: Fenglin Wu Date: Mon, 14 Dec 2020 10:14:16 +0800 Subject: [PATCH 44/76] input: qcom-hv-haptics: toggle HAPTICS_EN bit before playing FIFO When FIFO samples are played back to back along with SWR mode, there is a chance that FIFO samples won't get flushed out if they're not fully played before FIFO mode is stopped. When this happens, FIFO samples gets accumulated leading to overflow after which FIFO mode stops working. Only way to recover from this is to toggle HAPTICS_EN_BIT before starting FIFO mode playing again. While at it, add logs to print out FIFO real time fill status which is helpful for debugging. Change-Id: I07cd9ac34ad5ccfe7011850db554ba727f568482 Signed-off-by: Fenglin Wu --- drivers/input/misc/qcom-hv-haptics.c | 77 +++++++++++++++++++++++----- 1 file changed, 63 insertions(+), 14 deletions(-) diff --git a/drivers/input/misc/qcom-hv-haptics.c b/drivers/input/misc/qcom-hv-haptics.c index 2f95f454618c..b787aa9ec4bb 100644 --- a/drivers/input/misc/qcom-hv-haptics.c +++ b/drivers/input/misc/qcom-hv-haptics.c @@ -41,6 +41,8 @@ #define FIFO_REAL_TIME_FILL_STATUS_MASK_V1 GENMASK(6, 0) /* STATUS_DATA_MSB definition in V2 while MOD_STATUS_SEL is 5 */ #define FIFO_REAL_TIME_FILL_STATUS_MSB_MASK_V2 GENMASK(1, 0) +#define FIFO_EMPTY_FLAG_BIT_V2 BIT(6) +#define FIFO_FULL_FLAG_BIT_V2 BIT(5) #define HAP_CFG_STATUS_DATA_LSB_REG 0x0A /* STATUS_DATA_MSB definition while MOD_STATUS_SEL is 0 */ @@ -1447,11 +1449,11 @@ static int haptics_update_fifo_sample_v2(struct haptics_chip *chip, return 0; } -static int haptics_get_available_fifo_memory(struct haptics_chip *chip) +static int haptics_get_fifo_fill_status(struct haptics_chip *chip, u32 *fill) { int rc; u8 val[2]; - u32 fill, available; + bool empty = false, full = false; if (chip->ptn_revision == HAP_PTN_V1) { val[0] = MOD_STATUS_SEL_FIFO_FILL_STATUS_VAL; @@ -1465,7 +1467,7 @@ static int haptics_get_available_fifo_memory(struct haptics_chip *chip) if (rc < 0) return rc; - fill = val[0] & FIFO_REAL_TIME_FILL_STATUS_MASK_V1; + *fill = val[0] & FIFO_REAL_TIME_FILL_STATUS_MASK_V1; } else { val[0] = MOD_STATUS_XT_V2_FIFO_FILL_STATUS_VAL; rc = haptics_write(chip, chip->cfg_addr_base, @@ -1484,10 +1486,25 @@ static int haptics_get_available_fifo_memory(struct haptics_chip *chip) if (rc < 0) return rc; - fill = ((val[0] & FIFO_REAL_TIME_FILL_STATUS_MSB_MASK_V2) + *fill = ((val[0] & FIFO_REAL_TIME_FILL_STATUS_MSB_MASK_V2) << 8) | val[1]; + empty = !!(val[0] & FIFO_EMPTY_FLAG_BIT_V2); + full = !!(val[0] & FIFO_FULL_FLAG_BIT_V2); } + dev_dbg(chip->dev, "filled=%d, full=%d, empty=%d\n", *fill, full, empty); + return 0; +} + +static int haptics_get_available_fifo_memory(struct haptics_chip *chip) +{ + int rc; + u32 fill, available; + + rc = haptics_get_fifo_fill_status(chip, &fill); + if (rc < 0) + return rc; + if (fill > MAX_FIFO_SAMPLES(chip)) { dev_err(chip->dev, "Filled FIFO number %d exceed the max %d\n", fill, MAX_FIFO_SAMPLES(chip)); @@ -1599,6 +1616,33 @@ static int haptics_set_fifo_empty_threshold(struct haptics_chip *chip, return rc; } +static int haptics_module_enable(struct haptics_chip *chip, bool enable) +{ + u8 val; + int rc; + + val = enable ? HAPTICS_EN_BIT : 0; + rc = haptics_write(chip, chip->cfg_addr_base, + HAP_CFG_EN_CTL_REG, &val, 1); + if (rc < 0) + return rc; + + dev_dbg(chip->dev, "haptics module %s\n", + enable ? "enabled" : "disabled"); + return 0; +} + +static int haptics_toggle_module_enable(struct haptics_chip *chip) +{ + int rc; + + rc = haptics_module_enable(chip, false); + if (rc < 0) + return rc; + + return haptics_module_enable(chip, true); +} + static int haptics_set_fifo(struct haptics_chip *chip, struct fifo_cfg *fifo) { struct fifo_play_status *status = &chip->play.fifo_status; @@ -1732,6 +1776,13 @@ static int haptics_load_predefined_effect(struct haptics_chip *chip, return -EINVAL; play->effect = effect; + if (play->pattern_src == FIFO) { + /* Toggle HAPTICS_EN for a clear start point of FIFO playing */ + rc = haptics_toggle_module_enable(chip); + if (rc < 0) + return rc; + } + /* Clamp VMAX for different vibration strength */ rc = haptics_set_vmax_mv(chip, play->vmax_mv); if (rc < 0) @@ -1910,6 +1961,11 @@ static int haptics_load_custom_effect(struct haptics_chip *chip, play->effect = chip->custom_effect; play->brake = NULL; play->vmax_mv = (magnitude * chip->custom_effect->vmax_mv) / 0x7fff; + /* Toggle HAPTICS_EN for a clear start point of FIFO playing */ + rc = haptics_toggle_module_enable(chip); + if (rc < 0) + goto cleanup; + rc = haptics_set_vmax_mv(chip, play->vmax_mv); if (rc < 0) goto cleanup; @@ -2411,11 +2467,8 @@ static irqreturn_t fifo_empty_irq_handler(int irq, void *data) if (rc < 0) return IRQ_HANDLED; - if (!(val & FIFO_EMPTY_BIT)) { - dev_dbg(chip->dev, "Ignore spurious/falling IRQ, INT_RT_STS = %#x\n", - val); + if (!(val & FIFO_EMPTY_BIT)) return IRQ_HANDLED; - } mutex_lock(&chip->play.lock); if (atomic_read(&chip->play.fifo_status.written_done) == 1) { @@ -4293,7 +4346,6 @@ static int haptics_suspend(struct device *dev) { struct haptics_chip *chip = dev_get_drvdata(dev); struct haptics_play_info *play = &chip->play; - u8 val = 0; int rc; if (chip->cfg_revision == HAP_CFG_V1) @@ -4326,20 +4378,17 @@ static int haptics_suspend(struct device *dev) if (rc < 0) return rc; - return haptics_write(chip, chip->cfg_addr_base, - HAP_CFG_EN_CTL_REG, &val, 1); + return haptics_module_enable(chip, false); } static int haptics_resume(struct device *dev) { struct haptics_chip *chip = dev_get_drvdata(dev); - u8 val = HAPTICS_EN_BIT; if (chip->cfg_revision == HAP_CFG_V1) return 0; - return haptics_write(chip, chip->cfg_addr_base, - HAP_CFG_EN_CTL_REG, &val, 1); + return haptics_module_enable(chip, true); } #endif From 1116978168f8e62e66dd0b70b9da23408570ad46 Mon Sep 17 00:00:00 2001 From: Fenglin Wu Date: Tue, 5 Jan 2021 12:52:53 +0800 Subject: [PATCH 45/76] input: qcom-hv-haptics: config interrupt in haptics_set_fifo() Currently, haptics_fifo_empty_irq_config() uses a mutex lock and it's called from playback() of input framework which is from an atomic context. Fix this by moving it out of haptics_playback() and call it from haptics_set_fifo() which gets eventually called in upload(). With this change, the irq_lock mutex is no longer needed because all the calling paths of haptics_fifo_empty_irq_config() have been protected by the mutex lock defined in haptics_play_info structure, hence remove it. Change-Id: I4e4d353f8427fb279fd3f4d6a28737acbdb22a0a Signed-off-by: Fenglin Wu --- drivers/input/misc/qcom-hv-haptics.c | 40 +++++++++++++--------------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/drivers/input/misc/qcom-hv-haptics.c b/drivers/input/misc/qcom-hv-haptics.c index b787aa9ec4bb..856c37a7f56a 100644 --- a/drivers/input/misc/qcom-hv-haptics.c +++ b/drivers/input/misc/qcom-hv-haptics.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2020, The Linux Foundation. All rights reserved. + * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved. */ #include @@ -458,7 +458,6 @@ struct haptics_chip { struct haptics_play_info play; struct dentry *debugfs_dir; struct regulator_dev *swr_slave_rdev; - struct mutex irq_lock; struct nvmem_cell *cl_brake_nvmem; struct nvmem_device *hap_cfg_nvmem; struct device_node *pbs_node; @@ -1616,6 +1615,18 @@ static int haptics_set_fifo_empty_threshold(struct haptics_chip *chip, return rc; } +static void haptics_fifo_empty_irq_config(struct haptics_chip *chip, + bool enable) +{ + if (!chip->fifo_empty_irq_en && enable) { + enable_irq(chip->fifo_empty_irq); + chip->fifo_empty_irq_en = true; + } else if (chip->fifo_empty_irq_en && !enable) { + disable_irq_nosync(chip->fifo_empty_irq); + chip->fifo_empty_irq_en = false; + } +} + static int haptics_module_enable(struct haptics_chip *chip, bool enable) { u8 val; @@ -1709,8 +1720,13 @@ static int haptics_set_fifo(struct haptics_chip *chip, struct fifo_cfg *fifo) * FIFO samples can be written (if available) when * FIFO empty IRQ is triggered. */ + rc = haptics_set_fifo_empty_threshold(chip, fifo_thresh); + if (rc < 0) + return rc; - return haptics_set_fifo_empty_threshold(chip, fifo_thresh); + haptics_fifo_empty_irq_config(chip, true); + + return 0; } static int haptics_load_constant_effect(struct haptics_chip *chip, u8 amplitude) @@ -2153,20 +2169,6 @@ static int haptics_upload_effect(struct input_dev *dev, return rc; } -static void haptics_fifo_empty_irq_config(struct haptics_chip *chip, - bool enable) -{ - mutex_lock(&chip->irq_lock); - if (!chip->fifo_empty_irq_en && enable) { - enable_irq(chip->fifo_empty_irq); - chip->fifo_empty_irq_en = true; - } else if (chip->fifo_empty_irq_en && !enable) { - disable_irq_nosync(chip->fifo_empty_irq); - chip->fifo_empty_irq_en = false; - } - mutex_unlock(&chip->irq_lock); -} - static int haptics_stop_fifo_play(struct haptics_chip *chip) { int rc; @@ -2218,9 +2220,6 @@ static int haptics_playback(struct input_dev *dev, int effect_id, int val) rc = haptics_enable_play(chip, true); if (rc < 0) return rc; - - if (play->pattern_src == FIFO) - haptics_fifo_empty_irq_config(chip, true); } else { if (play->pattern_src == FIFO && atomic_read(&play->fifo_status.is_busy)) { @@ -4261,7 +4260,6 @@ static int haptics_probe(struct platform_device *pdev) return rc; } - mutex_init(&chip->irq_lock); mutex_init(&chip->play.lock); disable_irq_nosync(chip->fifo_empty_irq); chip->fifo_empty_irq_en = false; From 6e93432c876cba6e93f442ac5e936c04a98bb343 Mon Sep 17 00:00:00 2001 From: Fenglin Wu Date: Fri, 8 Jan 2021 13:41:23 +0800 Subject: [PATCH 46/76] input: qcom-hv-haptics: ensure HBoost is ready before the play HBoost is required to be in disabled state before triggering the play. Check this through DTEST1 signal and ensure the HBoost is ready before serving a new vibration request. Change-Id: I0d4b73fef39aa6da15abbb832369579221665d07 Signed-off-by: Fenglin Wu --- drivers/input/misc/qcom-hv-haptics.c | 53 +++++++++++++++++++++++++--- 1 file changed, 49 insertions(+), 4 deletions(-) diff --git a/drivers/input/misc/qcom-hv-haptics.c b/drivers/input/misc/qcom-hv-haptics.c index 856c37a7f56a..2a8fd50a0bb1 100644 --- a/drivers/input/misc/qcom-hv-haptics.c +++ b/drivers/input/misc/qcom-hv-haptics.c @@ -230,6 +230,9 @@ #define HAP_BOOST_V0P0 0x0000 #define HAP_BOOST_V0P1 0x0001 +#define HAP_BOOST_STATUS4_REG 0x0B +#define BOOST_DTEST1_STATUS_BIT BIT(0) + #define HAP_BOOST_HW_CTRL_FOLLOW_REG 0x41 #define FOLLOW_HW_EN_BIT BIT(7) #define FOLLOW_HW_CCM_BIT BIT(6) @@ -1160,6 +1163,45 @@ static bool is_boost_vreg_enabled_in_open_loop(struct haptics_chip *chip) return false; } +#define HBOOST_WAIT_READY_COUNT 100 +#define HBOOST_WAIT_READY_INTERVAL_US 200 +static int haptics_wait_hboost_ready(struct haptics_chip *chip) +{ + int i, rc; + u8 val; + + /* + * Wait ~20ms until hBoost is ready, otherwise + * bail out and return -EBUSY + */ + for (i = 0; i < HBOOST_WAIT_READY_COUNT; i++) { + /* HBoost is always ready when working in open loop mode */ + if (is_boost_vreg_enabled_in_open_loop(chip)) + return 0; + + /* Check if HBoost is in standby (disabled) state */ + rc = haptics_read(chip, chip->hbst_addr_base, + HAP_BOOST_VREG_EN_REG, &val, 1); + if (!rc && !(val & VREG_EN_BIT)) { + rc = haptics_read(chip, chip->hbst_addr_base, + HAP_BOOST_STATUS4_REG, &val, 1); + if (!rc && !(val & BOOST_DTEST1_STATUS_BIT)) + return 0; + } + + dev_dbg(chip->dev, "hBoost is busy, wait %d\n", i); + usleep_range(HBOOST_WAIT_READY_INTERVAL_US, + HBOOST_WAIT_READY_INTERVAL_US + 1); + } + + if (i == HBOOST_WAIT_READY_COUNT) { + dev_err(chip->dev, "hboost is not ready for haptics play\n"); + return -EBUSY; + } + + return 0; +} + static int haptics_enable_hpwr_vreg(struct haptics_chip *chip, bool en) { int rc; @@ -2163,10 +2205,12 @@ static int haptics_upload_effect(struct input_dev *dev, } rc = haptics_enable_hpwr_vreg(chip, true); - if (rc < 0) - dev_err(chip->dev, "enable hpwr_vreg failed, rc=%d\n"); + if (rc < 0) { + dev_err(chip->dev, "enable hpwr_vreg failed, rc=%d\n", rc); + return rc; + } - return rc; + return haptics_wait_hboost_ready(chip); } static int haptics_stop_fifo_play(struct haptics_chip *chip) @@ -2249,7 +2293,8 @@ static int haptics_erase(struct input_dev *dev, int effect_id) rc = haptics_stop_fifo_play(chip); if (rc < 0) { - dev_err(chip->dev, "stop FIFO playing failed, rc=%d\n"); + dev_err(chip->dev, "stop FIFO playing failed, rc=%d\n", + rc); mutex_unlock(&play->lock); return rc; } From f0d2102bf138fc97919ae912c5832a8483db3d2d Mon Sep 17 00:00:00 2001 From: Fenglin Wu Date: Mon, 11 Jan 2021 08:07:02 +0800 Subject: [PATCH 47/76] input: qcom-hv-haptics: check playing effect pointer in IRQ handler When uploading a constant effect after stopping FIFO play, the effect pointer in haptics_play_info structure will be set to NULL. If there is any pending fifo-empty IRQ gets served after that, it will trigger a NULL pointer dereference issue. To avoid this, check the playing effect pointer before use it in the IRQ handler. Change-Id: Iacfea5e48d0a9519fa2c930ec821e395c43b6281 Signed-off-by: Fenglin Wu --- drivers/input/misc/qcom-hv-haptics.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/input/misc/qcom-hv-haptics.c b/drivers/input/misc/qcom-hv-haptics.c index 2a8fd50a0bb1..0eed6cadbec4 100644 --- a/drivers/input/misc/qcom-hv-haptics.c +++ b/drivers/input/misc/qcom-hv-haptics.c @@ -2500,8 +2500,8 @@ static int haptics_hw_init(struct haptics_chip *chip) static irqreturn_t fifo_empty_irq_handler(int irq, void *data) { struct haptics_chip *chip = data; - struct fifo_cfg *fifo = chip->play.effect->fifo; - struct fifo_play_status *status = &chip->play.fifo_status; + struct fifo_cfg *fifo; + struct fifo_play_status *status; u32 samples_left; u8 *samples, val; int rc, num; @@ -2515,7 +2515,8 @@ static irqreturn_t fifo_empty_irq_handler(int irq, void *data) return IRQ_HANDLED; mutex_lock(&chip->play.lock); - if (atomic_read(&chip->play.fifo_status.written_done) == 1) { + status = &chip->play.fifo_status; + if (atomic_read(&status->written_done) == 1) { /* * Check the FIFO real time fill status before stopping * play to make sure that all FIFO samples can be played @@ -2540,6 +2541,10 @@ static irqreturn_t fifo_empty_irq_handler(int irq, void *data) goto unlock; } + if (!chip->play.effect) + goto unlock; + + fifo = chip->play.effect->fifo; if (!fifo || !fifo->samples) { dev_err(chip->dev, "no FIFO samples available\n"); goto unlock; From 23895eba212ba3ceba7d23388ab2641cd50d22d1 Mon Sep 17 00:00:00 2001 From: Fenglin Wu Date: Mon, 1 Mar 2021 13:35:31 +0800 Subject: [PATCH 48/76] input: qcom-hv-haptics: update LRA frequency detection sequence Update following settings in LRA frequency detection sequence according to HW recommendation: 1) Increase to 6 drive cycles before enabling High-Z period for auto resonance config. 2) Disable adaptive drive duty function. Change-Id: I551974603c99fcdad5d38fdef6edf15746c0033e Signed-off-by: Fenglin Wu --- drivers/input/misc/qcom-hv-haptics.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/drivers/input/misc/qcom-hv-haptics.c b/drivers/input/misc/qcom-hv-haptics.c index 0eed6cadbec4..264c9afe8809 100644 --- a/drivers/input/misc/qcom-hv-haptics.c +++ b/drivers/input/misc/qcom-hv-haptics.c @@ -116,13 +116,16 @@ #define RC_CLK_CAL_COUNT_LSB_MASK GENMASK(7, 0) #define HAP_CFG_DRV_DUTY_CFG_REG 0x60 +#define ADT_DRV_DUTY_EN_BIT BIT(7) + #define HAP_CFG_ADT_DRV_DUTY_CFG_REG 0x61 #define HAP_CFG_ZX_WIND_CFG_REG 0x62 #define HAP_CFG_AUTORES_CFG_REG 0x63 #define AUTORES_EN_BIT BIT(7) #define AUTORES_EN_DLY_MASK GENMASK(5, 2) -#define AUTORES_EN_DLY_1_CYCLE 0x2 +#define AUTORES_EN_DLY(cycles) ((cycles) * 2) +#define AUTORES_EN_DLY_6_CYCLES AUTORES_EN_DLY(6) #define AUTORES_EN_DLY_SHIFT 2 #define AUTORES_ERR_WINDOW_MASK GENMASK(1, 0) #define AUTORES_ERR_WINDOW_12P5_PERCENT 0x0 @@ -4086,11 +4089,16 @@ static int haptics_detect_lra_frequency(struct haptics_chip *chip) 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_EN_DLY_6_CYCLES << AUTORES_EN_DLY_SHIFT | AUTORES_ERR_WINDOW_50_PERCENT | AUTORES_EN_BIT); if (rc < 0) return rc; + rc = haptics_masked_write(chip, chip->cfg_addr_base, + HAP_CFG_DRV_DUTY_CFG_REG, ADT_DRV_DUTY_EN_BIT, 0); + if (rc < 0) + goto restore; + rc = haptics_config_openloop_lra_period(chip, chip->config.t_lra_us); if (rc < 0) goto restore; @@ -4143,6 +4151,12 @@ static int haptics_detect_lra_frequency(struct haptics_chip *chip) rc = haptics_write(chip, chip->cfg_addr_base, HAP_CFG_AUTORES_CFG_REG, &autores_cfg, 1); + if (rc < 0) + return rc; + + rc = haptics_masked_write(chip, chip->cfg_addr_base, + HAP_CFG_DRV_DUTY_CFG_REG, ADT_DRV_DUTY_EN_BIT, + ADT_DRV_DUTY_EN_BIT); return rc; } From d4607df4381ae66d2a6d67f7d1d7c64fc7d20230 Mon Sep 17 00:00:00 2001 From: Fenglin Wu Date: Mon, 22 Feb 2021 14:49:10 +0800 Subject: [PATCH 49/76] input: qcom-hv-haptics: Ignore checking HBoost status when SWR is playing HBoost would be kept on when triggering a non-FIFO play with SWR playing in the background, there is no need to wait HBoost to be ready hence bypass the HBoost ready check in this case. Change-Id: I68c4a97d1c5953fb5dc0dd53fa8792a02df4b3de Signed-off-by: Fenglin Wu --- drivers/input/misc/qcom-hv-haptics.c | 71 +++++++++++++++++++++++++++- 1 file changed, 69 insertions(+), 2 deletions(-) diff --git a/drivers/input/misc/qcom-hv-haptics.c b/drivers/input/misc/qcom-hv-haptics.c index 264c9afe8809..1ead64e0c110 100644 --- a/drivers/input/misc/qcom-hv-haptics.c +++ b/drivers/input/misc/qcom-hv-haptics.c @@ -39,7 +39,7 @@ #define TLRA_CL_ERR_MSB_MASK GENMASK(4, 0) /* STATUS_DATA_MSB definition in V1 while MOD_STATUS_SEL is 5 */ #define FIFO_REAL_TIME_FILL_STATUS_MASK_V1 GENMASK(6, 0) -/* STATUS_DATA_MSB definition in V2 while MOD_STATUS_SEL is 5 */ +/* STATUS_DATA_MSB in V2 when MOD_STATUS_SEL is 5 and MOD_STATUS_XT.SEL is 1 */ #define FIFO_REAL_TIME_FILL_STATUS_MSB_MASK_V2 GENMASK(1, 0) #define FIFO_EMPTY_FLAG_BIT_V2 BIT(6) #define FIFO_FULL_FLAG_BIT_V2 BIT(5) @@ -47,8 +47,10 @@ #define HAP_CFG_STATUS_DATA_LSB_REG 0x0A /* STATUS_DATA_MSB definition while MOD_STATUS_SEL is 0 */ #define CAL_TLRA_CL_STS_LSB_MASK GENMASK(7, 0) -/* STATUS_DATA_LSB definition in V2 while MOD_STATUS_SEL is 5 */ +/* STATUS_DATA_LSB in V2 when MOD_STATUS_SEL is 5 and MOD_STATUS_XT.SEL is 1 */ #define FIFO_REAL_TIME_FILL_STATUS_LSB_MASK_V2 GENMASK(7, 0) +/* STATUS_DATA_LSB in V2 when MOD_STATUS_SEL is 5 and MOD_STATUS_XT.SEL is 0 */ +#define HAP_DRV_PATTERN_SRC_STATUS_MASK_V2 GENMASK(2, 0) #define HAP_CFG_FAULT_STATUS_REG 0x0C #define SC_FLAG_BIT BIT(2) @@ -272,6 +274,19 @@ ((chip)->hbst_revision == HAP_BOOST_V0P0 ? \ HAP_BOOST_V0P0_CLAMP_REG : HAP_BOOST_V0P1_CLAMP_REG) +enum hap_status_sel_v2 { + CAL_TLRA_CL_STS = 0x00, + T_WIND_STS, + T_WIND_STS_PREV, + LAST_GOOD_TLRA_CL_STS, + TLRA_CL_ERR_STS, + HAP_DRV_STS, + RNAT_RCAL_INT, + BRAKE_CAL_SCALAR = 0x07, + CLAMPED_DUTY_CYCLE_STS = 0x8003, + FIFO_REAL_TIME_STS = 0x8005, +}; + enum drv_sig_shape { WF_SQUARE, WF_SINE, @@ -742,6 +757,29 @@ static int get_brake_play_length_us(struct brake_cfg *brake, u32 t_lra_us) return t_lra_us * (i + 1); } +static int haptics_get_status_data(struct haptics_chip *chip, + enum hap_status_sel_v2 sel, u8 data[]) +{ + int rc; + u8 mod_sel_val[2]; + + mod_sel_val[0] = sel & 0xff; + mod_sel_val[1] = (sel >> 8) & 0xff; + rc = haptics_write(chip, chip->cfg_addr_base, + HAP_CFG_MOD_STATUS_SEL_REG, mod_sel_val, 2); + if (rc < 0) + return rc; + + rc = haptics_read(chip, chip->cfg_addr_base, + HAP_CFG_STATUS_DATA_MSB_REG, data, 2); + if (rc < 0) + return rc; + + dev_dbg(chip->dev, "Get status data[%x] = (%#x, %#x)\n", + sel, data[0], data[1]); + return 0; +} + #define V1_CL_TLRA_STEP_AUTO_RES_CAL_NOT_DONE_NS 5000 #define V1_CL_TLRA_STEP_AUTO_RES_CAL_DONE_NS 3333 #define AUTO_CAL_CLK_SCALE_DEN 1000 @@ -1147,6 +1185,21 @@ static int haptics_boost_vreg_enable(struct haptics_chip *chip, bool en) return rc; } +static bool is_swr_play_enabled(struct haptics_chip *chip) +{ + int rc; + u8 val[2]; + + rc = haptics_get_status_data(chip, HAP_DRV_STS, val); + if (rc < 0) + return false; + + if ((val[1] & HAP_DRV_PATTERN_SRC_STATUS_MASK_V2) == SWR) + return true; + + return false; +} + static bool is_boost_vreg_enabled_in_open_loop(struct haptics_chip *chip) { int rc; @@ -1182,6 +1235,20 @@ static int haptics_wait_hboost_ready(struct haptics_chip *chip) if (is_boost_vreg_enabled_in_open_loop(chip)) return 0; + /* + * If the coming request is not FIFO play and there is + * already a SWR play in the background, then HBoost will + * be kept as on always hence no need to wait its ready. + */ + mutex_lock(&chip->play.lock); + if (chip->play.pattern_src != FIFO && + is_swr_play_enabled(chip)) { + dev_dbg(chip->dev, "Ignore waiting hBoost when SWR play is in progress\n"); + mutex_unlock(&chip->play.lock); + return 0; + } + mutex_unlock(&chip->play.lock); + /* Check if HBoost is in standby (disabled) state */ rc = haptics_read(chip, chip->hbst_addr_base, HAP_BOOST_VREG_EN_REG, &val, 1); From a7691a0f8e3c10caa366d40b08432bb5e27bee89 Mon Sep 17 00:00:00 2001 From: Fenglin Wu Date: Thu, 18 Mar 2021 17:52:22 +0800 Subject: [PATCH 50/76] input: qcom-hv-haptics: reset haptics module if it's stuck in SWR mode The haptics module would get stuck in SWR mode if SWR port is disconnected during SWR play, and this can happen if ADSP SSR (subsystem reset) is triggered when playing in SWR mode. This stuck condition can be detected by checking if haptics module has already been in SWR mode when swr-haptics driver requests to enable SWR haptics. When it's detected, ignore SWR mode temporarily and toggle HAPTICS_EN for a HW reset. When SWR play is attempted again i.e. when swr_haptics driver request to turn on SWR slave device, SWR mode would get re-enabled. Change-Id: Ie55d331e3c689972b42dd9688f0d2b182b3fd282 Signed-off-by: Fenglin Wu --- drivers/input/misc/qcom-hv-haptics.c | 30 ++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/drivers/input/misc/qcom-hv-haptics.c b/drivers/input/misc/qcom-hv-haptics.c index 1ead64e0c110..e363aff6c515 100644 --- a/drivers/input/misc/qcom-hv-haptics.c +++ b/drivers/input/misc/qcom-hv-haptics.c @@ -79,7 +79,9 @@ #define DRV_WF_SEL_MASK GENMASK(1, 0) #define HAP_CFG_AUTO_SHUTDOWN_CFG_REG 0x4A + #define HAP_CFG_TRIG_PRIORITY_REG 0x4B +#define SWR_IGNORE_BIT BIT(4) #define HAP_CFG_SPMI_PLAY_REG 0x4C #define PLAY_EN_BIT BIT(7) @@ -3902,6 +3904,34 @@ static int swr_slave_reg_enable(struct regulator_dev *rdev) return rc; } + /* + * If haptics has already been in SWR mode when enabling the SWR + * slave, it means that the haptics module was stuck in prevous + * SWR play. Then toggle HAPTICS_EN to reset haptics module and + * ignore SWR mode until next SWR slave enable request is coming. + */ + if (is_swr_play_enabled(chip)) { + rc = haptics_masked_write(chip, chip->cfg_addr_base, + HAP_CFG_TRIG_PRIORITY_REG, + SWR_IGNORE_BIT, SWR_IGNORE_BIT); + if (rc < 0) { + dev_err(chip->dev, "Failed to enable SWR_IGNORE, rc=%d\n", rc); + return rc; + } + + rc = haptics_toggle_module_enable(chip); + if (rc < 0) + return rc; + } else { + rc = haptics_masked_write(chip, chip->cfg_addr_base, + HAP_CFG_TRIG_PRIORITY_REG, + SWR_IGNORE_BIT, 0); + if (rc < 0) { + dev_err(chip->dev, "Failed to disable SWR_IGNORE, rc=%d\n", rc); + return rc; + } + } + chip->swr_slave_enabled = true; return 0; } From f49f146a302adddb5f15327a5e14e08a14a4d246 Mon Sep 17 00:00:00 2001 From: Fenglin Wu Date: Fri, 7 May 2021 09:19:30 +0800 Subject: [PATCH 51/76] input: qcom-hv-haptics: ensure valid pointer when calling kfree In haptics pattern/fifo/brake debugfs file write operation functions, strsep() is used iteratively to split the user-passed string into tokens for setting parameters. However, strsep() modifies the pointer passed and hence the same pointer cannot be used in kfree(). Fix this by copying the pointer originally allocated by kzalloc() and use it in kfree() as well. Change-Id: I2baf8db4f7c39e7ab0a83411717e46a29a2e849b Signed-off-by: Fenglin Wu --- drivers/input/misc/qcom-hv-haptics.c | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/drivers/input/misc/qcom-hv-haptics.c b/drivers/input/misc/qcom-hv-haptics.c index e363aff6c515..a0480403bc6f 100644 --- a/drivers/input/misc/qcom-hv-haptics.c +++ b/drivers/input/misc/qcom-hv-haptics.c @@ -2744,17 +2744,19 @@ static ssize_t pattern_s_dbgfs_write(struct file *fp, { struct haptics_effect *effect = fp->private_data; struct pattern_s patterns[SAMPLES_PER_PATTERN] = {{0, 0, 0},}; - char *str, *token; + char *str, *kbuf, *token; u32 val, tmp[3 * SAMPLES_PER_PATTERN] = {0}; int rc, i = 0, j = 0; if (count > CHAR_PER_PATTERN_S * SAMPLES_PER_PATTERN) return -EINVAL; - str = kzalloc(CHAR_PER_PATTERN_S * SAMPLES_PER_PATTERN + 1, GFP_KERNEL); - if (!str) + kbuf = kzalloc(CHAR_PER_PATTERN_S * SAMPLES_PER_PATTERN + 1, + GFP_KERNEL); + if (!kbuf) return -ENOMEM; + str = kbuf; rc = copy_from_user(str, buf, count); if (rc > 0) { rc = -EFAULT; @@ -2805,7 +2807,7 @@ static ssize_t pattern_s_dbgfs_write(struct file *fp, rc = count; exit: - kfree(str); + kfree(kbuf); return rc; } @@ -2876,7 +2878,7 @@ static ssize_t fifo_s_dbgfs_write(struct file *fp, { struct haptics_effect *effect = fp->private_data; struct fifo_cfg *fifo = effect->fifo; - char *kbuf, *token; + char *str, *kbuf, *token; int rc, i = 0; int val; u8 *samples; @@ -2885,13 +2887,14 @@ static ssize_t fifo_s_dbgfs_write(struct file *fp, if (!kbuf) return -ENOMEM; - rc = copy_from_user(kbuf, buf, count); + str = kbuf; + rc = copy_from_user(str, buf, count); if (rc > 0) { rc = -EFAULT; goto exit; } - kbuf[count] = '\0'; + str[count] = '\0'; *ppos += count; samples = kcalloc(fifo->num_s, sizeof(*samples), GFP_KERNEL); @@ -2900,7 +2903,7 @@ static ssize_t fifo_s_dbgfs_write(struct file *fp, goto exit; } - while ((token = strsep(&kbuf, " ")) != NULL) { + while ((token = strsep(&str, " ")) != NULL) { rc = kstrtoint(token, 0, &val); if (rc < 0) { rc = -EINVAL; @@ -3000,7 +3003,7 @@ static ssize_t brake_s_dbgfs_write(struct file *fp, { struct haptics_effect *effect = fp->private_data; struct brake_cfg *brake = effect->brake; - char *str, *token; + char *str, *kbuf, *token; int rc, i = 0; u32 val; u8 samples[BRAKE_SAMPLE_COUNT] = {0}; @@ -3008,10 +3011,11 @@ static ssize_t brake_s_dbgfs_write(struct file *fp, if (count > CHAR_PER_SAMPLE * BRAKE_SAMPLE_COUNT) return -EINVAL; - str = kzalloc(CHAR_PER_SAMPLE * BRAKE_SAMPLE_COUNT + 1, GFP_KERNEL); - if (!str) + kbuf = kzalloc(CHAR_PER_SAMPLE * BRAKE_SAMPLE_COUNT + 1, GFP_KERNEL); + if (!kbuf) return -ENOMEM; + str = kbuf; rc = copy_from_user(str, buf, count); if (rc > 0) { rc = -EFAULT; @@ -3043,7 +3047,7 @@ static ssize_t brake_s_dbgfs_write(struct file *fp, rc = count; exit: - kfree(str); + kfree(kbuf); return rc; } From e9318ded525fd14aa874e222f39a953d05688de9 Mon Sep 17 00:00:00 2001 From: Fenglin Wu Date: Tue, 27 Jul 2021 15:43:26 +0800 Subject: [PATCH 52/76] input: qcom-hv-haptics: ignore pattern_src when checking hBoost ready Commit b610aa2d70a3 ("input: qcom-hv-haptics: Ignore checking HBoost status when SWR is playing") is added to ignore checking hBoost ready status when triggering a non-FIFO play with SWR playing in the background. This should be also applicable when triggering a FIFO play, so remove the pattern_src check as the HBoost should be always ready when triggering the play with SWR playing in the background. Change-Id: If3d5ff7138e189c81991775d7814229c4d0e8c0a Signed-off-by: Fenglin Wu --- drivers/input/misc/qcom-hv-haptics.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/input/misc/qcom-hv-haptics.c b/drivers/input/misc/qcom-hv-haptics.c index a0480403bc6f..1709d2af7ef7 100644 --- a/drivers/input/misc/qcom-hv-haptics.c +++ b/drivers/input/misc/qcom-hv-haptics.c @@ -1238,13 +1238,11 @@ static int haptics_wait_hboost_ready(struct haptics_chip *chip) return 0; /* - * If the coming request is not FIFO play and there is - * already a SWR play in the background, then HBoost will - * be kept as on always hence no need to wait its ready. + * If there is already a SWR play in the background, then HBoost + * will be kept as on hence no need to wait its ready. */ mutex_lock(&chip->play.lock); - if (chip->play.pattern_src != FIFO && - is_swr_play_enabled(chip)) { + if (is_swr_play_enabled(chip)) { dev_dbg(chip->dev, "Ignore waiting hBoost when SWR play is in progress\n"); mutex_unlock(&chip->play.lock); return 0; From 6f5ee642088c10f1e7f29386ce103e12e3adb0c2 Mon Sep 17 00:00:00 2001 From: Shyam Kumar Thella Date: Thu, 25 Mar 2021 14:24:24 +0530 Subject: [PATCH 53/76] input: qcom-hv-haptics: Don't initialize swr regulator for HAP_CFG V3 Since HAP_CFG V3 doesn't support soundwire haptics, skip initializing swr regulator so that swr_haptics cannot use it. Change-Id: I64d783aabcf699233c68a96af5ffd922d361fb24 Signed-off-by: Shyam Kumar Thella --- drivers/input/misc/qcom-hv-haptics.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/drivers/input/misc/qcom-hv-haptics.c b/drivers/input/misc/qcom-hv-haptics.c index 1709d2af7ef7..4ba08ec6e05d 100644 --- a/drivers/input/misc/qcom-hv-haptics.c +++ b/drivers/input/misc/qcom-hv-haptics.c @@ -28,6 +28,7 @@ #define HAP_CFG_REVISION2_REG 0x01 #define HAP_CFG_V1 0x1 #define HAP_CFG_V2 0x2 +#define HAP_CFG_V3 0x3 #define HAP_CFG_STATUS_DATA_MSB_REG 0x09 /* STATUS_DATA_MSB definitions while MOD_STATUS_SEL is 0 */ @@ -4367,6 +4368,15 @@ static struct attribute *hap_class_attrs[] = { }; ATTRIBUTE_GROUPS(hap_class); +static bool is_swr_supported(struct haptics_chip *chip) +{ + /* Haptics config version 3 does not support soundwire */ + if (chip->cfg_revision == HAP_CFG_V3) + return false; + + return true; +} + static int haptics_probe(struct platform_device *pdev) { struct haptics_chip *chip; @@ -4407,11 +4417,13 @@ static int haptics_probe(struct platform_device *pdev) return rc; } - rc = haptics_init_swr_slave_regulator(chip); - if (rc < 0) { - dev_err(chip->dev, "Initialize swr slave regulator failed, rc = %d\n", - rc); - return rc; + if (is_swr_supported(chip)) { + rc = haptics_init_swr_slave_regulator(chip); + if (rc < 0) { + dev_err(chip->dev, "Initialize swr slave regulator failed, rc = %d\n", + rc); + return rc; + } } rc = devm_request_threaded_irq(chip->dev, chip->fifo_empty_irq, From ad265e91b03b9f11ed69b577a4642d764569f1c7 Mon Sep 17 00:00:00 2001 From: Kiran Gunda Date: Thu, 3 Jun 2021 21:09:51 +0530 Subject: [PATCH 54/76] input: qcom-hv-haptics: Add support for non-hboost haptics Some PMICs such as PM5100 does not have the HBOOST peripheral for haptics input supply. Add a new compatible string to avoid accessing the non-existent HBOOST peripheral and also make the necessary configurations required for hpwr supply. Change-Id: I84301115706ee59984f7b051bc467d1537bbad29 Signed-off-by: Kiran Gunda --- drivers/input/misc/qcom-hv-haptics.c | 239 ++++++++++++++++++++++----- 1 file changed, 195 insertions(+), 44 deletions(-) diff --git a/drivers/input/misc/qcom-hv-haptics.c b/drivers/input/misc/qcom-hv-haptics.c index 4ba08ec6e05d..63d20ab9f12f 100644 --- a/drivers/input/misc/qcom-hv-haptics.c +++ b/drivers/input/misc/qcom-hv-haptics.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -173,10 +174,20 @@ #define CAL_RC_CLK_AUTO_VAL 1 #define CAL_RC_CLK_MANUAL_VAL 2 +/* These registers are only applicable for PM5100 */ +#define HAP_CFG_HPWR_INTF_CTL_REG 0x80 +#define INTF_CTL_MASK GENMASK(1, 0) +#define INTF_CTL_BOB 1 +#define INTF_CTL_BHARGER 2 + +#define HAP_CFG_VHPWR_REG 0x84 +#define VHPWR_STEP_MV 32 + /* version register definitions for HAPTICS_PATTERN module */ #define HAP_PTN_REVISION2_REG 0x01 #define HAP_PTN_V1 0x1 #define HAP_PTN_V2 0x2 +#define HAP_PTN_V3 0x3 /* status register definition for HAPTICS_PATTERN module */ #define HAP_PTN_FIFO_READY_STS_REG 0x08 @@ -267,10 +278,6 @@ #define F_LRA_VARIATION_HZ 5 #define NON_HBOOST_MAX_VMAX_MV 4000 -#define MAX_FIFO_SAMPLES(chip) \ - ((chip)->ptn_revision == HAP_PTN_V1 ? 104 : 640) -#define FIFO_EMPTY_THRESHOLD(chip) \ - ((chip)->ptn_revision == HAP_PTN_V1 ? 48 : 280) #define is_between(val, min, max) \ (((min) <= (max)) && ((min) <= (val)) && ((val) <= (max))) #define HAP_BOOST_CLAMP_5V_REG_OFFSET(chip) \ @@ -346,6 +353,11 @@ enum custom_effect_param { CUSTOM_DATA_LEN, }; +enum pmic_type { + PM8350B, + PM5100, +}; + static const char * const src_str[] = { "FIFO", "DIRECT_PLAY", @@ -495,7 +507,9 @@ struct haptics_chip { u32 hbst_addr_base; u8 cfg_revision; u8 ptn_revision; + u8 hpwr_intf_ctl; u16 hbst_revision; + enum pmic_type pmic_type; bool fifo_empty_irq_en; bool swr_slave_enabled; bool clamp_at_5v; @@ -507,6 +521,86 @@ struct haptics_reg_info { u8 val; }; +static inline int get_max_fifo_samples(struct haptics_chip *chip) +{ + int val = 0; + + switch (chip->ptn_revision) { + case HAP_PTN_V1: + val = 104; + break; + case HAP_PTN_V2: + val = 640; + break; + case HAP_PTN_V3: + val = 1024; + break; + default: + pr_err("Invalid pattern revision\n"); + break; + } + + return val; +} + +static int get_fifo_empty_threshold(struct haptics_chip *chip) +{ + int val = 0; + + switch (chip->ptn_revision) { + case HAP_PTN_V1: + val = 48; + break; + case HAP_PTN_V2: + val = 280; + break; + case HAP_PTN_V3: + val = 288; + break; + default: + pr_err("Invalid pattern revision\n"); + break; + } + + return val; +} + +static int get_fifo_threshold_per_bit(struct haptics_chip *chip) +{ + int val = -EINVAL; + + switch (chip->ptn_revision) { + case HAP_PTN_V1: + val = 4; + break; + case HAP_PTN_V2: + val = 40; + break; + case HAP_PTN_V3: + val = 32; + break; + default: + pr_err("Invalid pattern revision\n"); + break; + } + + return val; +} + +static bool is_haptics_external_powered(struct haptics_chip *chip) +{ + /* SW based explicit vote */ + if (chip->hpwr_vreg) + return true; + + /* Implicit voting by HW */ + if (chip->pmic_type == PM5100) + return true; + + /* Powered by HBOOST */ + return false; +} + static int haptics_read(struct haptics_chip *chip, u16 base, u8 offset, u8 *val, u32 length) { @@ -890,7 +984,7 @@ static int haptics_get_closeloop_lra_period_v2( rc_clk_cal = ((val[0] & CAL_RC_CLK_MASK) >> CAL_RC_CLK_SHIFT); /* read auto resonance calibration result */ - if (in_boot) { + if (in_boot && (chip->pmic_type == PM8350B)) { if (chip->hap_cfg_nvmem == NULL) { dev_dbg(chip->dev, "nvmem device for hap_cfg is not defined\n"); return -EINVAL; @@ -1164,6 +1258,9 @@ static int haptics_boost_vreg_enable(struct haptics_chip *chip, bool en) int rc; u8 val; + if (is_haptics_external_powered(chip)) + return 0; + if (chip->hap_cfg_nvmem == NULL) { dev_dbg(chip->dev, "nvmem device for hap_cfg is not defined\n"); return 0; @@ -1208,6 +1305,9 @@ static bool is_boost_vreg_enabled_in_open_loop(struct haptics_chip *chip) int rc; u8 val; + if (is_haptics_external_powered(chip)) + return false; + rc = haptics_read(chip, chip->hbst_addr_base, HAP_BOOST_HW_CTRL_FOLLOW_REG, &val, 1); if (!rc && !(val & FOLLOW_HW_EN_BIT)) { @@ -1229,6 +1329,8 @@ static int haptics_wait_hboost_ready(struct haptics_chip *chip) int i, rc; u8 val; + if (is_haptics_external_powered(chip)) + return 0; /* * Wait ~20ms until hBoost is ready, otherwise * bail out and return -EBUSY @@ -1323,7 +1425,7 @@ static int haptics_open_loop_drive_config(struct haptics_chip *chip, bool en) u8 val; if ((is_boost_vreg_enabled_in_open_loop(chip) || - chip->hpwr_vreg != NULL) && en) { + is_haptics_external_powered(chip)) && en) { /* Force VREG_RDY */ rc = haptics_masked_write(chip, chip->cfg_addr_base, HAP_CFG_VSET_CFG_REG, FORCE_VREG_RDY_BIT, @@ -1355,7 +1457,7 @@ static int haptics_open_loop_drive_config(struct haptics_chip *chip, bool en) dev_dbg(chip->dev, "Toggle CAL_EN in open-loop-VREG playing\n"); } - } else if (chip->hpwr_vreg == NULL) { + } else if (!is_haptics_external_powered(chip)) { rc = haptics_masked_write(chip, chip->cfg_addr_base, HAP_CFG_VSET_CFG_REG, FORCE_VREG_RDY_BIT, 0); @@ -1617,16 +1719,16 @@ static int haptics_get_available_fifo_memory(struct haptics_chip *chip) if (rc < 0) return rc; - if (fill > MAX_FIFO_SAMPLES(chip)) { + if (fill > get_max_fifo_samples(chip)) { dev_err(chip->dev, "Filled FIFO number %d exceed the max %d\n", - fill, MAX_FIFO_SAMPLES(chip)); + fill, get_max_fifo_samples(chip)); return -EINVAL; - } else if (fill == MAX_FIFO_SAMPLES(chip)) { + } else if (fill == get_max_fifo_samples(chip)) { dev_err(chip->dev, "no FIFO space available\n"); return -EBUSY; } - available = MAX_FIFO_SAMPLES(chip) - fill; + available = get_max_fifo_samples(chip) - fill; return available; } @@ -1715,12 +1817,16 @@ static int haptics_set_fifo_empty_threshold(struct haptics_chip *chip, u8 reg, thresh_per_bit; int rc; + rc = get_fifo_threshold_per_bit(chip); + if (rc < 0) + return rc; + + thresh_per_bit = rc; + reg = (chip->ptn_revision == HAP_PTN_V1) ? HAP_PTN_V1_FIFO_EMPTY_CFG_REG : HAP_PTN_V2_FIFO_EMPTY_CFG_REG; - thresh_per_bit = (chip->ptn_revision == HAP_PTN_V1) ? - HAP_PTN_V1_FIFO_THRESH_LSB : HAP_PTN_V2_FIFO_THRESH_LSB; - rc = haptics_masked_write(chip, chip->ptn_addr_base, - reg, EMPTY_THRESH_MASK, thresh / thresh_per_bit); + rc = haptics_masked_write(chip, chip->ptn_addr_base, reg, + EMPTY_THRESH_MASK, (thresh / thresh_per_bit)); if (rc < 0) dev_err(chip->dev, "Set FIFO empty threshold failed, rc=%d\n", rc); @@ -1780,7 +1886,7 @@ static int haptics_set_fifo(struct haptics_chip *chip, struct fifo_cfg *fifo) if (chip->ptn_revision == HAP_PTN_V1 && fifo->period_per_s > F_8KHZ && - fifo->num_s > MAX_FIFO_SAMPLES(chip)) { + fifo->num_s > get_max_fifo_samples(chip)) { dev_err(chip->dev, "PM8350B v1 doesn't support playing long FIFO pattern higher than 8 KHz play rate\n"); return -EINVAL; } @@ -1806,7 +1912,7 @@ static int haptics_set_fifo(struct haptics_chip *chip, struct fifo_cfg *fifo) * more than MAX_FIFO_SAMPLES samples, the rest will be * written if any FIFO memory is available after playing. */ - num = min_t(u32, fifo->num_s, MAX_FIFO_SAMPLES(chip)); + num = min_t(u32, fifo->num_s, get_max_fifo_samples(chip)); available = haptics_get_available_fifo_memory(chip); if (available < 0) return available; @@ -1859,7 +1965,7 @@ static int haptics_load_constant_effect(struct haptics_chip *chip, u8 amplitude) play->effect = NULL; /* Fix Vmax to (hpwr_vreg_mv - hdrm_mv) in non-HBOOST regulator case */ - if (chip->hpwr_vreg) { + if (is_haptics_external_powered(chip)) { rc = haptics_get_vmax_headroom_mv(chip, &hdrm_mv); if (rc < 0) goto unlock; @@ -2197,7 +2303,7 @@ static u8 get_direct_play_max_amplitude(struct haptics_chip *chip) u32 amplitude = DIRECT_PLAY_MAX_AMPLITUDE, hdrm_mv; int rc; - if (chip->hpwr_vreg) { + if (is_haptics_external_powered(chip)) { rc = haptics_get_vmax_headroom_mv(chip, &hdrm_mv); if (rc < 0) return 0; @@ -2475,12 +2581,15 @@ static int haptics_hw_init(struct haptics_chip *chip) if (rc < 0) return rc; - rc = haptics_read(chip, chip->hbst_addr_base, - HAP_BOOST_CLAMP_5V_REG_OFFSET(chip), val, 1); - if (rc < 0) - return rc; + if (!is_haptics_external_powered(chip)) { + rc = haptics_read(chip, chip->hbst_addr_base, + HAP_BOOST_CLAMP_5V_REG_OFFSET(chip), val, 1); + if (rc < 0) + return rc; + + chip->clamp_at_5v = val[0] & CLAMP_5V_BIT; + } - chip->clamp_at_5v = val[0] & CLAMP_5V_BIT; /* Config VMAX */ rc = haptics_set_vmax_mv(chip, config->vmax_mv); if (rc < 0) @@ -2504,8 +2613,29 @@ static int haptics_hw_init(struct haptics_chip *chip) if (rc < 0) return rc; - /* Force VREG_RDY if non-HBoost is used for powering haptics */ - if (chip->hpwr_vreg) { + if ((chip->pmic_type == PM5100) && !chip->hpwr_vreg) { + /* Indicates if HPWR is BOB or Bharger */ + rc = haptics_read(chip, chip->cfg_addr_base, + HAP_CFG_HPWR_INTF_CTL_REG, val, 1); + if (rc < 0) + return rc; + + chip->hpwr_intf_ctl = val[0] & INTF_CTL_MASK; + + /* Read HPWR voltage to adjust the VMAX */ + if (chip->hpwr_voltage_mv == 0 && + chip->hpwr_intf_ctl == INTF_CTL_BOB) { + rc = haptics_read(chip, chip->cfg_addr_base, + HAP_CFG_VHPWR_REG, val, 1); + if (rc < 0) + return rc; + + chip->hpwr_voltage_mv = val[0] * VHPWR_STEP_MV; + } + } + + if (is_haptics_external_powered(chip)) { + /* Force VREG_RDY if non-HBoost is used for powering haptics */ rc = haptics_masked_write(chip, chip->cfg_addr_base, HAP_CFG_VSET_CFG_REG, FORCE_VREG_RDY_BIT, FORCE_VREG_RDY_BIT); @@ -2595,9 +2725,9 @@ static irqreturn_t fifo_empty_irq_handler(int irq, void *data) * memory, defer the stop into erase() function. */ num = haptics_get_available_fifo_memory(chip); - if (num != MAX_FIFO_SAMPLES(chip)) { + if (num != get_max_fifo_samples(chip)) { dev_dbg(chip->dev, "%d FIFO samples still in playing\n", - MAX_FIFO_SAMPLES(chip) - num); + get_max_fifo_samples(chip) - num); goto unlock; } @@ -3684,14 +3814,21 @@ static int haptics_get_revision(struct haptics_chip *chip) 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); + if (is_haptics_external_powered(chip)) { + dev_dbg(chip->dev, "haptics revision: HAP_CFG %#x, HAP_PTN %#x\n", + chip->cfg_revision, chip->ptn_revision); + } else { + 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; } @@ -3789,13 +3926,14 @@ static int haptics_parse_dt(struct haptics_chip *chip) chip->ptn_addr_base = be32_to_cpu(*addr); addr = of_get_address(node, 2, NULL, NULL); - if (!addr) { + if (!addr && !is_haptics_external_powered(chip)) { dev_err(chip->dev, "Read HAPTICS_HBOOST address failed\n"); rc = -EINVAL; goto free_pbs; + } else if (addr != NULL) { + chip->hbst_addr_base = be32_to_cpu(*addr); } - 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); @@ -3818,12 +3956,12 @@ static int haptics_parse_dt(struct haptics_chip *chip) goto free_pbs; } - config->fifo_empty_thresh = FIFO_EMPTY_THRESHOLD(chip); + config->fifo_empty_thresh = get_fifo_empty_threshold(chip); of_property_read_u32(node, "qcom,fifo-empty-threshold", &config->fifo_empty_thresh); - if (config->fifo_empty_thresh >= MAX_FIFO_SAMPLES(chip)) { + if (config->fifo_empty_thresh >= get_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)); + config->fifo_empty_thresh, get_max_fifo_samples(chip)); rc = -EINVAL; goto free_pbs; } @@ -4042,7 +4180,7 @@ static u32 get_lra_impedance_capable_max(struct haptics_chip *chip) if (chip->clamp_at_5v) mohms = MAX_IMPEDANCE_MOHM / 2; - if (chip->hpwr_vreg) + if (is_haptics_external_powered(chip)) mohms = (chip->hpwr_voltage_mv * 1000) / MIN_ISC_MA; dev_dbg(chip->dev, "LRA impedance capable max: %u mohms\n", mohms); @@ -4208,7 +4346,7 @@ static int haptics_detect_lra_frequency(struct haptics_chip *chip) goto restore; /* Fix Vmax to (hpwr_vreg_mv - hdrm_mv) in non-HBOOST regulator case */ - if (chip->hpwr_vreg) + if (is_haptics_external_powered(chip)) vmax_mv = chip->hpwr_voltage_mv - LRA_CALIBRATION_VMAX_HDRM_MV; rc = haptics_set_vmax_mv(chip, vmax_mv); @@ -4399,6 +4537,9 @@ static int haptics_probe(struct platform_device *pdev) return -ENXIO; } + chip->pmic_type = + (enum pmic_type)(uintptr_t)of_device_get_match_data(chip->dev); + rc = haptics_parse_dt(chip); if (rc < 0) { dev_err(chip->dev, "Parse device-tree failed, rc = %d\n", rc); @@ -4570,8 +4711,18 @@ static const struct dev_pm_ops haptics_pm_ops = { }; static const struct of_device_id haptics_match_table[] = { - { .compatible = "qcom,hv-haptics" }, - { .compatible = "qcom,pm8350b-haptics" }, + { + .compatible = "qcom,hv-haptics", + .data = (void *)PM8350B, + }, + { + .compatible = "qcom,pm8350b-haptics", + .data = (void *)PM8350B, + }, + { + .compatible = "qcom,pm5100-haptics", + .data = (void *)PM5100, + }, {}, }; From 78596a946cb343580f6d5c98dcdc197ef3384443 Mon Sep 17 00:00:00 2001 From: Kiran Gunda Date: Fri, 25 Jun 2021 18:42:10 +0530 Subject: [PATCH 55/76] input: qcom-hv-haptics: Update the VMAX configuration The VMAX_STEP value is different for HV and MV haptics. Update the VMAX configuration accordingly. Change-Id: I5d0f00e9a07e76b01b16539d79897675ddd63117 Signed-off-by: Kiran Gunda --- drivers/input/misc/qcom-hv-haptics.c | 34 ++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/drivers/input/misc/qcom-hv-haptics.c b/drivers/input/misc/qcom-hv-haptics.c index 63d20ab9f12f..f9c679a0808d 100644 --- a/drivers/input/misc/qcom-hv-haptics.c +++ b/drivers/input/misc/qcom-hv-haptics.c @@ -71,8 +71,11 @@ #define DRV_SLEW_RATE_MASK GENMASK(2, 0) #define HAP_CFG_VMAX_REG 0x48 -#define VMAX_STEP_MV 50 +#define VMAX_HV_STEP_MV 50 +#define VMAX_MV_STEP_MV 32 #define MAX_VMAX_MV 11000 +#define MAX_HV_VMAX_MV 10000 +#define MAX_MV_VMAX_MV 6000 #define CLAMPED_VMAX_MV 5000 #define DEFAULT_VMAX_MV 5000 @@ -175,6 +178,9 @@ #define CAL_RC_CLK_MANUAL_VAL 2 /* These registers are only applicable for PM5100 */ +#define HAP_CFG_HW_CONFIG_REG 0x0D +#define HV_HAP_DRIVER_BIT BIT(1) + #define HAP_CFG_HPWR_INTF_CTL_REG 0x80 #define INTF_CTL_MASK GENMASK(1, 0) #define INTF_CTL_BOB 1 @@ -509,11 +515,13 @@ struct haptics_chip { u8 ptn_revision; u8 hpwr_intf_ctl; u16 hbst_revision; + u16 max_vmax_mv; enum pmic_type pmic_type; bool fifo_empty_irq_en; bool swr_slave_enabled; bool clamp_at_5v; bool hpwr_vreg_enabled; + bool is_hv_haptics; }; struct haptics_reg_info { @@ -1163,18 +1171,20 @@ static int haptics_get_closeloop_lra_period(struct haptics_chip *chip, static int haptics_set_vmax_mv(struct haptics_chip *chip, u32 vmax_mv) { int rc = 0; - u8 val; + u8 val, vmax_step; - if (vmax_mv > MAX_VMAX_MV) { + if (vmax_mv > chip->max_vmax_mv) { dev_err(chip->dev, "vmax (%d) exceed the max value: %d\n", - vmax_mv, MAX_VMAX_MV); + vmax_mv, chip->max_vmax_mv); return -EINVAL; } if (chip->clamp_at_5v && (vmax_mv > CLAMPED_VMAX_MV)) vmax_mv = CLAMPED_VMAX_MV; - val = vmax_mv / VMAX_STEP_MV; + vmax_step = (chip->is_hv_haptics) ? + VMAX_HV_STEP_MV : VMAX_MV_STEP_MV; + val = vmax_mv / vmax_step; rc = haptics_write(chip, chip->cfg_addr_base, HAP_CFG_VMAX_REG, &val, 1); if (rc < 0) @@ -2590,6 +2600,20 @@ static int haptics_hw_init(struct haptics_chip *chip) chip->clamp_at_5v = val[0] & CLAMP_5V_BIT; } + chip->is_hv_haptics = true; + chip->max_vmax_mv = MAX_VMAX_MV; + + if (chip->pmic_type == PM5100) { + rc = haptics_read(chip, chip->cfg_addr_base, + HAP_CFG_HW_CONFIG_REG, val, 1); + if (rc < 0) + return rc; + + chip->is_hv_haptics = val[0] & HV_HAP_DRIVER_BIT; + chip->max_vmax_mv = (chip->is_hv_haptics) ? + MAX_HV_VMAX_MV : MAX_MV_VMAX_MV; + } + /* Config VMAX */ rc = haptics_set_vmax_mv(chip, config->vmax_mv); if (rc < 0) From e773cf38b84636c91480ea428a803067da1d3cc9 Mon Sep 17 00:00:00 2001 From: Kiran Gunda Date: Fri, 25 Jun 2021 12:12:56 +0530 Subject: [PATCH 56/76] input: qcom-hv-haptics: Toggle RC_CAL_EN mode only for PM8350B The incorrect first cycle vibration issue is applicable only for PM8350B PMIC. Hence, make the SW W/A to toggle the CAL_EN mode from disabled to auto mode specific for PM8350B PMIC. Introduce a "wa_flags" variable to handle the SW W/A in a cleaner way. Change-Id: I8f0c31fd21681274081a4d58d589b4ed9a188f34 Signed-off-by: Kiran Gunda --- drivers/input/misc/qcom-hv-haptics.c | 31 ++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/drivers/input/misc/qcom-hv-haptics.c b/drivers/input/misc/qcom-hv-haptics.c index f9c679a0808d..d752658b2c93 100644 --- a/drivers/input/misc/qcom-hv-haptics.c +++ b/drivers/input/misc/qcom-hv-haptics.c @@ -364,6 +364,10 @@ enum pmic_type { PM5100, }; +enum wa_flags { + TOGGLE_CAL_RC_CLK = BIT(0), +}; + static const char * const src_str[] = { "FIFO", "DIRECT_PLAY", @@ -511,6 +515,7 @@ struct haptics_chip { u32 cfg_addr_base; u32 ptn_addr_base; u32 hbst_addr_base; + u32 wa_flags; u8 cfg_revision; u8 ptn_revision; u8 hpwr_intf_ctl; @@ -1449,8 +1454,9 @@ static int haptics_open_loop_drive_config(struct haptics_chip *chip, bool en) if (rc < 0) return rc; - if ((val & CAL_RC_CLK_MASK) == - CAL_RC_CLK_AUTO_VAL << CAL_RC_CLK_SHIFT) { + if ((chip->wa_flags & TOGGLE_CAL_RC_CLK) && + ((val & CAL_RC_CLK_MASK) == + CAL_RC_CLK_AUTO_VAL << CAL_RC_CLK_SHIFT)) { val = CAL_RC_CLK_DISABLED_VAL << CAL_RC_CLK_SHIFT; rc = haptics_masked_write(chip, chip->cfg_addr_base, HAP_CFG_CAL_EN_REG, CAL_RC_CLK_MASK, @@ -2578,6 +2584,23 @@ static int haptics_config_openloop_lra_period(struct haptics_chip *chip, HAP_CFG_TLRA_OL_HIGH_REG, val, 2); } +static int haptics_config_wa(struct haptics_chip *chip) +{ + switch (chip->pmic_type) { + case PM8350B: + chip->wa_flags |= TOGGLE_CAL_RC_CLK; + break; + case PM5100: + break; + default: + dev_err(chip->dev, "PMIC type %d does not match\n", + chip->pmic_type); + return -EINVAL; + } + + return 0; +} + static int haptics_hw_init(struct haptics_chip *chip) { struct haptics_hw_config *config = &chip->config; @@ -2586,6 +2609,10 @@ static int haptics_hw_init(struct haptics_chip *chip) u8 val[2]; u32 t_lra_us; + rc = haptics_config_wa(chip); + if (rc < 0) + return rc; + /* Store CL brake settings */ rc = haptics_store_cl_brake_settings(chip); if (rc < 0) From 9335fe8f0193631d0a431a4034859ad4d43f3077 Mon Sep 17 00:00:00 2001 From: Kiran Gunda Date: Wed, 28 Jul 2021 19:37:57 +0530 Subject: [PATCH 57/76] input: qcom-hv-haptics: update FIFO real time fill status mask FIFO_REAL_TIME_FILL_STATUS_MASK is different for haptics HW version V3. Update it. Change-Id: Ida8326d89678af34ae2c3a35164ae1a7ad5f6b7b Signed-off-by: Kiran Gunda --- drivers/input/misc/qcom-hv-haptics.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/input/misc/qcom-hv-haptics.c b/drivers/input/misc/qcom-hv-haptics.c index d752658b2c93..4bc278bf9546 100644 --- a/drivers/input/misc/qcom-hv-haptics.c +++ b/drivers/input/misc/qcom-hv-haptics.c @@ -43,6 +43,7 @@ #define FIFO_REAL_TIME_FILL_STATUS_MASK_V1 GENMASK(6, 0) /* STATUS_DATA_MSB in V2 when MOD_STATUS_SEL is 5 and MOD_STATUS_XT.SEL is 1 */ #define FIFO_REAL_TIME_FILL_STATUS_MSB_MASK_V2 GENMASK(1, 0) +#define FIFO_REAL_TIME_FILL_STATUS_MSB_MASK_V3 GENMASK(2, 0) #define FIFO_EMPTY_FLAG_BIT_V2 BIT(6) #define FIFO_FULL_FLAG_BIT_V2 BIT(5) @@ -1682,7 +1683,7 @@ static int haptics_update_fifo_sample_v2(struct haptics_chip *chip, static int haptics_get_fifo_fill_status(struct haptics_chip *chip, u32 *fill) { int rc; - u8 val[2]; + u8 val[2], fill_status_mask; bool empty = false, full = false; if (chip->ptn_revision == HAP_PTN_V1) { @@ -1716,8 +1717,10 @@ static int haptics_get_fifo_fill_status(struct haptics_chip *chip, u32 *fill) if (rc < 0) return rc; - *fill = ((val[0] & FIFO_REAL_TIME_FILL_STATUS_MSB_MASK_V2) - << 8) | val[1]; + fill_status_mask = (chip->cfg_revision == HAP_CFG_V2) ? + FIFO_REAL_TIME_FILL_STATUS_MSB_MASK_V2 : + FIFO_REAL_TIME_FILL_STATUS_MSB_MASK_V3; + *fill = ((val[0] & fill_status_mask) << 8) | val[1]; empty = !!(val[0] & FIFO_EMPTY_FLAG_BIT_V2); full = !!(val[0] & FIFO_FULL_FLAG_BIT_V2); } From 42bc856797f3ff3dc1b7c7a41c3382f173433941 Mon Sep 17 00:00:00 2001 From: Fenglin Wu Date: Wed, 22 Sep 2021 09:33:54 +0800 Subject: [PATCH 58/76] input: qcom-hv-haptics: remove return value for debugfs_create_u32() Remove the return value when creating "fifo_empty_thresh" debugfs node because debugfs_create_u32() API no longer supports return value. Change-Id: I7b39ed79a9148c2815b8656222dc6f61540b2129 Signed-off-by: Fenglin Wu --- drivers/input/misc/qcom-hv-haptics.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/drivers/input/misc/qcom-hv-haptics.c b/drivers/input/misc/qcom-hv-haptics.c index 4bc278bf9546..846dde8ffb41 100644 --- a/drivers/input/misc/qcom-hv-haptics.c +++ b/drivers/input/misc/qcom-hv-haptics.c @@ -3511,15 +3511,8 @@ static int haptics_create_debugfs(struct haptics_chip *chip) goto exit; } - file = debugfs_create_u32("fifo_empty_thresh", 0600, hap_dir, + debugfs_create_u32("fifo_empty_thresh", 0600, hap_dir, &chip->config.fifo_empty_thresh); - if (IS_ERR(file)) { - rc = PTR_ERR(file); - dev_err(chip->dev, "create fifo_empty_thresh debugfs failed, rc=%d\n", - rc); - goto exit; - } - chip->debugfs_dir = hap_dir; return 0; From 1cb307ba4832fa4ca3ed162956dadcd51492ade5 Mon Sep 17 00:00:00 2001 From: Fenglin Wu Date: Thu, 22 Apr 2021 19:26:22 +0800 Subject: [PATCH 59/76] input: qcom-hv-haptics: remove support for haptics in PM8350B V1 chip PM8350B V1 chip is no longer used for new targets so clean up the driver to remove the support for haptics module inside PM8350B V1 chip. Change-Id: If8a6dfd025753a768aa067958f10d613b574ee3f Signed-off-by: Fenglin Wu --- drivers/input/misc/qcom-hv-haptics.c | 402 ++++++--------------------- 1 file changed, 83 insertions(+), 319 deletions(-) diff --git a/drivers/input/misc/qcom-hv-haptics.c b/drivers/input/misc/qcom-hv-haptics.c index 846dde8ffb41..49dc43417936 100644 --- a/drivers/input/misc/qcom-hv-haptics.c +++ b/drivers/input/misc/qcom-hv-haptics.c @@ -35,25 +35,23 @@ /* STATUS_DATA_MSB definitions while MOD_STATUS_SEL is 0 */ #define AUTO_RES_CAL_DONE_BIT BIT(5) #define CAL_TLRA_CL_STS_MSB_MASK GENMASK(4, 0) -/* STATUS_DATA_MSB definition in V2 while MOD_STATUS_SEL is 3 */ -#define LAST_GOOD_TLRA_CL_MSB_MASK GENMASK(4, 0) -/* STATUS_DATA_MSB definition in V2 while MOD_STATUS_SEL is 4 */ +/* STATUS_DATA_MSB definition while MOD_STATUS_SEL is 3 */ +#define LAST_GOOD_TLRA_CL_MSB_MASK GENMASK(4, 0) +/* STATUS_DATA_MSB definition while MOD_STATUS_SEL is 4 */ #define TLRA_CL_ERR_MSB_MASK GENMASK(4, 0) -/* STATUS_DATA_MSB definition in V1 while MOD_STATUS_SEL is 5 */ -#define FIFO_REAL_TIME_FILL_STATUS_MASK_V1 GENMASK(6, 0) -/* STATUS_DATA_MSB in V2 when MOD_STATUS_SEL is 5 and MOD_STATUS_XT.SEL is 1 */ +/* STATUS_DATA_MSB when MOD_STATUS_SEL is 5 and MOD_STATUS_XT.SEL is 1 */ #define FIFO_REAL_TIME_FILL_STATUS_MSB_MASK_V2 GENMASK(1, 0) #define FIFO_REAL_TIME_FILL_STATUS_MSB_MASK_V3 GENMASK(2, 0) -#define FIFO_EMPTY_FLAG_BIT_V2 BIT(6) -#define FIFO_FULL_FLAG_BIT_V2 BIT(5) +#define FIFO_EMPTY_FLAG_BIT BIT(6) +#define FIFO_FULL_FLAG_BIT BIT(5) #define HAP_CFG_STATUS_DATA_LSB_REG 0x0A -/* STATUS_DATA_MSB definition while MOD_STATUS_SEL is 0 */ +/* STATUS_DATA_LSB definition while MOD_STATUS_SEL is 0 */ #define CAL_TLRA_CL_STS_LSB_MASK GENMASK(7, 0) -/* STATUS_DATA_LSB in V2 when MOD_STATUS_SEL is 5 and MOD_STATUS_XT.SEL is 1 */ -#define FIFO_REAL_TIME_FILL_STATUS_LSB_MASK_V2 GENMASK(7, 0) -/* STATUS_DATA_LSB in V2 when MOD_STATUS_SEL is 5 and MOD_STATUS_XT.SEL is 0 */ -#define HAP_DRV_PATTERN_SRC_STATUS_MASK_V2 GENMASK(2, 0) +/* STATUS_DATA_LSB when MOD_STATUS_SEL is 5 and MOD_STATUS_XT.SEL is 1 */ +#define FIFO_REAL_TIME_FILL_STATUS_LSB_MASK GENMASK(7, 0) +/* STATUS_DATA_LSB when MOD_STATUS_SEL is 5 and MOD_STATUS_XT.SEL is 0 */ +#define HAP_DRV_PATTERN_SRC_STATUS_MASK GENMASK(2, 0) #define HAP_CFG_FAULT_STATUS_REG 0x0C #define SC_FLAG_BIT BIT(2) @@ -161,15 +159,6 @@ #define FORCE_VREG_RDY_BIT BIT(0) #define HAP_CFG_MOD_STATUS_SEL_REG 0x70 -#define MOD_STATUS_SEL_CAL_TLRA_CL_STS_VAL 0 -#define MOD_STATUS_SEL_LAST_GOOD_TLRA_VAL 3 -#define MOD_STATUS_SEL_TLRA_CL_ERR_STS_VAL 4 -#define MOD_STATUS_SEL_FIFO_FILL_STATUS_VAL 5 -#define MOD_STATUS_SEL_BRAKE_CAL_RNAT_RCAL_VAL 6 - -#define HAP_CFG_MOD_STATUS_XT_V2_REG 0x71 -#define MOD_STATUS_XT_V2_FIFO_FILL_STATUS_VAL 0x80 -#define MOD_STATUS_XT_SEL_LAST_GOOD_TLRA_VAL 0 #define HAP_CFG_CAL_EN_REG 0x72 #define CAL_RC_CLK_MASK GENMASK(3, 2) @@ -203,32 +192,19 @@ #define HAP_PTN_NUM_PAT_REG 0x09 /* config register definition for HAPTICS_PATTERN module */ -/* FIFO configuration registers in V1 chip */ -#define HAP_PTN_V1_FIFO_DIN_MSB_REG 0x20 -#define HAP_PTN_V1_FIFO_DIN_MSB_BIT BIT(0) -#define HAP_PTN_V1_FIFO_DIN_LSB_REG 0x21 -#define HAP_PTN_V1_FIFO_DIN_LSB_MASK GENMASK(7, 0) +#define HAP_PTN_FIFO_DIN_0_REG 0x20 +#define HAP_PTN_FIFO_DIN_NUM 4 -#define HAP_PTN_V1_FIFO_PLAY_RATE_REG 0x22 +#define HAP_PTN_FIFO_PLAY_RATE_REG 0x24 #define FIFO_PLAY_RATE_MASK GENMASK(3, 0) -#define HAP_PTN_V1_FIFO_EMPTY_CFG_REG 0x23 +#define HAP_PTN_FIFO_EMPTY_CFG_REG 0x2A #define EMPTY_THRESH_MASK GENMASK(3, 0) -#define HAP_PTN_V1_FIFO_THRESH_LSB 4 +#define HAP_PTN_FIFO_THRESH_LSB 40 -#define HAP_PTN_V1_FIFO_DEPTH_CFG_REG 0x24 +#define HAP_PTN_FIFO_DEPTH_CFG_REG 0x2B +#define HAP_PTN_FIFO_DIN_1B_REG 0x2C -/* FIFO configuration registers in V2 chip */ -#define HAP_PTN_V2_FIFO_DIN_0_REG 0x20 -#define HAP_PTN_V2_FIFO_DIN_NUM 4 - -#define HAP_PTN_V2_FIFO_PLAY_RATE_REG 0x24 -#define HAP_PTN_V2_FIFO_EMPTY_CFG_REG 0x2A -#define HAP_PTN_V2_FIFO_THRESH_LSB 40 -#define HAP_PTN_V2_FIFO_DEPTH_CFG_REG 0x2B -#define HAP_PTN_V2_FIFO_DIN_1B_REG 0x2C - -/* shared registers between V1 and V2 chips */ #define HAP_PTN_DIRECT_PLAY_REG 0x26 #define DIRECT_PLAY_MAX_AMPLITUDE 0xFF @@ -267,8 +243,7 @@ #define HAP_BOOST_VREG_EN_REG 0x46 #define VREG_EN_BIT BIT(7) -#define HAP_BOOST_V0P0_CLAMP_REG 0xF1 -#define HAP_BOOST_V0P1_CLAMP_REG 0x70 +#define HAP_BOOST_CLAMP_REG 0x70 #define CLAMP_5V_BIT BIT(0) /* constant parameters */ @@ -287,11 +262,8 @@ #define is_between(val, min, max) \ (((min) <= (max)) && ((min) <= (val)) && ((val) <= (max))) -#define HAP_BOOST_CLAMP_5V_REG_OFFSET(chip) \ - ((chip)->hbst_revision == HAP_BOOST_V0P0 ? \ - HAP_BOOST_V0P0_CLAMP_REG : HAP_BOOST_V0P1_CLAMP_REG) -enum hap_status_sel_v2 { +enum hap_status_sel { CAL_TLRA_CL_STS = 0x00, T_WIND_STS, T_WIND_STS_PREV, @@ -540,9 +512,6 @@ static inline int get_max_fifo_samples(struct haptics_chip *chip) int val = 0; switch (chip->ptn_revision) { - case HAP_PTN_V1: - val = 104; - break; case HAP_PTN_V2: val = 640; break; @@ -562,9 +531,6 @@ static int get_fifo_empty_threshold(struct haptics_chip *chip) int val = 0; switch (chip->ptn_revision) { - case HAP_PTN_V1: - val = 48; - break; case HAP_PTN_V2: val = 280; break; @@ -584,9 +550,6 @@ static int get_fifo_threshold_per_bit(struct haptics_chip *chip) int val = -EINVAL; switch (chip->ptn_revision) { - case HAP_PTN_V1: - val = 4; - break; case HAP_PTN_V2: val = 40; break; @@ -869,7 +832,7 @@ static int get_brake_play_length_us(struct brake_cfg *brake, u32 t_lra_us) } static int haptics_get_status_data(struct haptics_chip *chip, - enum hap_status_sel_v2 sel, u8 data[]) + enum hap_status_sel sel, u8 data[]) { int rc; u8 mod_sel_val[2]; @@ -891,10 +854,8 @@ static int haptics_get_status_data(struct haptics_chip *chip, return 0; } -#define V1_CL_TLRA_STEP_AUTO_RES_CAL_NOT_DONE_NS 5000 -#define V1_CL_TLRA_STEP_AUTO_RES_CAL_DONE_NS 3333 -#define AUTO_CAL_CLK_SCALE_DEN 1000 -#define AUTO_CAL_CLK_SCALE_NUM 1024 +#define AUTO_CAL_CLK_SCALE_DEN 1000 +#define AUTO_CAL_CLK_SCALE_NUM 1024 static int haptics_adjust_lra_period(struct haptics_chip *chip, u32 *t_lra_us) { int rc; @@ -913,65 +874,6 @@ static int haptics_adjust_lra_period(struct haptics_chip *chip, u32 *t_lra_us) return 0; } -static int haptics_get_closeloop_lra_period_v1( - struct haptics_chip *chip) -{ - struct haptics_hw_config *config = &chip->config; - int rc, freq_diff, f_lra, cl_f_lra; - u8 val[2]; - u32 tmp, step_ns; - bool auto_res_cal_done; - - val[0] = MOD_STATUS_SEL_CAL_TLRA_CL_STS_VAL; - rc = haptics_write(chip, chip->cfg_addr_base, - HAP_CFG_MOD_STATUS_SEL_REG, val, 1); - if (rc < 0) - return rc; - - rc = haptics_read(chip, chip->cfg_addr_base, - HAP_CFG_STATUS_DATA_MSB_REG, val, 2); - if (rc < 0) - return rc; - - /* - * Calculate the closed loop T_LRA with the following equations - * for PM8350B V1: - * LAST_GOOD_TLRA_CL_STS[12:0] = STATUS_DATA_MSB[4:0] | - * STATUS_DATA_LSB[7:0] - * CL_TLRA_STEP_US = STATUS_DATA_MSB[5] ? 3.333 us : 5 us - * LAST_GOOD_TLRA_CL = LAST_GOOD_TLRA_CL_STS[12:0] * - * CL_TLRA_STEP_US - */ - auto_res_cal_done = !!(val[0] & AUTO_RES_CAL_DONE_BIT); - if (auto_res_cal_done) - step_ns = V1_CL_TLRA_STEP_AUTO_RES_CAL_DONE_NS; - else - step_ns = V1_CL_TLRA_STEP_AUTO_RES_CAL_NOT_DONE_NS; - - tmp = ((val[0] & CAL_TLRA_CL_STS_MSB_MASK) << 8) | val[1]; - config->cl_t_lra_us = (tmp * step_ns) / 1000; - - rc = haptics_adjust_lra_period(chip, &config->cl_t_lra_us); - if (rc < 0) - return rc; - - /* calculate RC_CLK_CAL_COUNT */ - if (!config->t_lra_us || !config->cl_t_lra_us) - return -EINVAL; - - f_lra = USEC_PER_SEC / config->t_lra_us; - if (!f_lra) - return -EINVAL; - - cl_f_lra = USEC_PER_SEC / config->cl_t_lra_us; - freq_diff = cl_f_lra - f_lra; - - /* RC_CLK_CAL_COUNT = 600*(1-(clk_adjustment/Fifo_pat_freq)) */ - config->rc_clk_cal_count = 600 - ((600 * freq_diff) / f_lra); - - return 0; -} - /* The offset of SDAM register which saves STATUS_DATA_MSB value */ #define HAP_STATUS_DATA_MSB_SDAM_OFFSET 0x46 @@ -980,7 +882,7 @@ static int haptics_get_closeloop_lra_period_v1( #define TLRA_AUTO_RES_NO_CAL_STEP_PSEC 3333000 #define TLRA_AUTO_RES_ERR_AUTO_CAL_STEP_PSEC 1627700 #define TLRA_AUTO_RES_AUTO_CAL_STEP_PSEC 813850 -static int haptics_get_closeloop_lra_period_v2( +static int haptics_get_closeloop_lra_period( struct haptics_chip *chip, bool in_boot) { struct haptics_hw_config *config = &chip->config; @@ -1012,14 +914,7 @@ static int haptics_get_closeloop_lra_period_v2( return rc; } } else { - val[0] = MOD_STATUS_SEL_CAL_TLRA_CL_STS_VAL; - rc = haptics_write(chip, chip->cfg_addr_base, - HAP_CFG_MOD_STATUS_SEL_REG, val, 1); - if (rc < 0) - return rc; - - rc = haptics_read(chip, chip->cfg_addr_base, - HAP_CFG_STATUS_DATA_MSB_REG, val, 2); + rc = haptics_get_status_data(chip, CAL_TLRA_CL_STS, val); if (rc < 0) return rc; } @@ -1029,14 +924,7 @@ static int haptics_get_closeloop_lra_period_v2( ((val[0] & CAL_TLRA_CL_STS_MSB_MASK) << 8) | val[1]; /* read auto resonance calibration error status */ - val[0] = MOD_STATUS_SEL_TLRA_CL_ERR_STS_VAL; - rc = haptics_write(chip, chip->cfg_addr_base, - HAP_CFG_MOD_STATUS_SEL_REG, val, 1); - if (rc < 0) - return rc; - - rc = haptics_read(chip, chip->cfg_addr_base, - HAP_CFG_STATUS_DATA_MSB_REG, val, 2); + rc = haptics_get_status_data(chip, TLRA_CL_ERR_STS, val); if (rc < 0) return rc; @@ -1101,20 +989,7 @@ static int haptics_get_closeloop_lra_period_v2( */ /* read LAST_GOOD_TLRA_CL_STS */ - val[0] = MOD_STATUS_XT_SEL_LAST_GOOD_TLRA_VAL; - rc = haptics_write(chip, chip->cfg_addr_base, - HAP_CFG_MOD_STATUS_XT_V2_REG, val, 1); - if (rc < 0) - return rc; - - val[0] = MOD_STATUS_SEL_LAST_GOOD_TLRA_VAL; - rc = haptics_write(chip, chip->cfg_addr_base, - HAP_CFG_MOD_STATUS_SEL_REG, val, 1); - if (rc < 0) - return rc; - - rc = haptics_read(chip, chip->cfg_addr_base, - HAP_CFG_STATUS_DATA_MSB_REG, val, 2); + rc = haptics_get_status_data(chip, LAST_GOOD_TLRA_CL_STS, val); if (rc < 0) return rc; @@ -1149,25 +1024,6 @@ static int haptics_get_closeloop_lra_period_v2( return -EINVAL; } - return 0; -} - -static int haptics_get_closeloop_lra_period(struct haptics_chip *chip, - bool in_boot) -{ - int rc = 0; - - if (chip->ptn_revision == HAP_PTN_V1) - rc = haptics_get_closeloop_lra_period_v1(chip); - else - rc = haptics_get_closeloop_lra_period_v2(chip, in_boot); - - if (rc < 0) { - dev_err(chip->dev, "get close loop T LRA failed, rc=%d\n", - rc); - return rc; - } - dev_dbg(chip->dev, "OL_TLRA %u us, CL_TLRA %u us, RC_CLK_CAL_COUNT %#x\n", chip->config.t_lra_us, chip->config.cl_t_lra_us, chip->config.rc_clk_cal_count); @@ -1310,7 +1166,7 @@ static bool is_swr_play_enabled(struct haptics_chip *chip) if (rc < 0) return false; - if ((val[1] & HAP_DRV_PATTERN_SRC_STATUS_MASK_V2) == SWR) + if ((val[1] & HAP_DRV_PATTERN_SRC_STATUS_MASK) == SWR) return true; return false; @@ -1615,59 +1471,26 @@ static int haptics_set_pattern(struct haptics_chip *chip, } -static int haptics_update_fifo_sample_v1(struct haptics_chip *chip, u8 sample) -{ - int rc = 0; - u8 val; - - /* - * Fill FIFO_DIN registers to update FIFO memory, - * need to fill LSB first then MSB. - * The FIFO memory width in V1 chip is 9-bit so shift - * 1 bit to left on the 8-bit FIFO sample to achieve a - * 9-bit data and fill it into the FIFO_DIN registers. - */ - val = (sample << 1) & HAP_PTN_V1_FIFO_DIN_LSB_MASK; - rc = haptics_write(chip, chip->ptn_addr_base, - HAP_PTN_V1_FIFO_DIN_LSB_REG, &val, 1); - if (rc < 0) { - dev_err(chip->dev, "write FIFO LSB failed, rc=%d\n", - rc); - return rc; - } - - val = (sample >> 7) & HAP_PTN_V1_FIFO_DIN_MSB_BIT; - rc = haptics_write(chip, chip->ptn_addr_base, - HAP_PTN_V1_FIFO_DIN_MSB_REG, &val, 1); - if (rc < 0) { - dev_err(chip->dev, "write FIFO MSB failed, rc=%d\n", - rc); - return rc; - } - - return 0; -} - -static int haptics_update_fifo_sample_v2(struct haptics_chip *chip, +static int haptics_update_fifo_sample(struct haptics_chip *chip, u8 *samples, u32 num) { int rc, i; - if (num > HAP_PTN_V2_FIFO_DIN_NUM) + if (num > HAP_PTN_FIFO_DIN_NUM) return -EINVAL; - if (num == HAP_PTN_V2_FIFO_DIN_NUM) { + if (num == HAP_PTN_FIFO_DIN_NUM) { rc = haptics_write(chip, chip->ptn_addr_base, - HAP_PTN_V2_FIFO_DIN_0_REG, samples, num); + HAP_PTN_FIFO_DIN_0_REG, samples, num); if (rc < 0) { dev_err(chip->dev, "bulk write FIFO_DIN failed, rc=%d\n", rc); return rc; } - } else if (num < HAP_PTN_V2_FIFO_DIN_NUM) { + } else if (num < HAP_PTN_FIFO_DIN_NUM) { for (i = 0; i < num; i++) { rc = haptics_write(chip, chip->ptn_addr_base, - HAP_PTN_V2_FIFO_DIN_1B_REG, + HAP_PTN_FIFO_DIN_1B_REG, (samples + i), 1); if (rc < 0) { dev_err(chip->dev, "write FIFO_DIN_1B failed, rc=%d\n", @@ -1686,44 +1509,16 @@ static int haptics_get_fifo_fill_status(struct haptics_chip *chip, u32 *fill) u8 val[2], fill_status_mask; bool empty = false, full = false; - if (chip->ptn_revision == HAP_PTN_V1) { - val[0] = MOD_STATUS_SEL_FIFO_FILL_STATUS_VAL; - rc = haptics_write(chip, chip->cfg_addr_base, - HAP_CFG_MOD_STATUS_SEL_REG, val, 1); - if (rc < 0) - return rc; + rc = haptics_get_status_data(chip, FIFO_REAL_TIME_STS, val); + if (rc < 0) + return rc; - rc = haptics_read(chip, chip->cfg_addr_base, - HAP_CFG_STATUS_DATA_MSB_REG, val, 1); - if (rc < 0) - return rc; - - *fill = val[0] & FIFO_REAL_TIME_FILL_STATUS_MASK_V1; - } else { - val[0] = MOD_STATUS_XT_V2_FIFO_FILL_STATUS_VAL; - rc = haptics_write(chip, chip->cfg_addr_base, - HAP_CFG_MOD_STATUS_XT_V2_REG, val, 1); - if (rc < 0) - return rc; - - val[0] = MOD_STATUS_SEL_FIFO_FILL_STATUS_VAL; - rc = haptics_write(chip, chip->cfg_addr_base, - HAP_CFG_MOD_STATUS_SEL_REG, val, 1); - if (rc < 0) - return rc; - - rc = haptics_read(chip, chip->cfg_addr_base, - HAP_CFG_STATUS_DATA_MSB_REG, val, 2); - if (rc < 0) - return rc; - - fill_status_mask = (chip->cfg_revision == HAP_CFG_V2) ? - FIFO_REAL_TIME_FILL_STATUS_MSB_MASK_V2 : - FIFO_REAL_TIME_FILL_STATUS_MSB_MASK_V3; - *fill = ((val[0] & fill_status_mask) << 8) | val[1]; - empty = !!(val[0] & FIFO_EMPTY_FLAG_BIT_V2); - full = !!(val[0] & FIFO_FULL_FLAG_BIT_V2); - } + fill_status_mask = (chip->cfg_revision == HAP_CFG_V2) ? + FIFO_REAL_TIME_FILL_STATUS_MSB_MASK_V2 : + FIFO_REAL_TIME_FILL_STATUS_MSB_MASK_V3; + *fill = ((val[0] & fill_status_mask) << 8) | val[1]; + empty = !!(val[0] & FIFO_EMPTY_FLAG_BIT); + full = !!(val[0] & FIFO_FULL_FLAG_BIT); dev_dbg(chip->dev, "filled=%d, full=%d, empty=%d\n", *fill, full, empty); return 0; @@ -1785,30 +1580,22 @@ static int haptics_update_fifo_samples(struct haptics_chip *chip, return -EINVAL; } - if (chip->ptn_revision == HAP_PTN_V1) { - for (i = 0; i < length; i++) { - rc = haptics_update_fifo_sample_v1(chip, samples[i]); - if (rc < 0) - return rc; - } - } else { - count = length / HAP_PTN_V2_FIFO_DIN_NUM; - for (i = 0; i < count; i++) { - rc = haptics_update_fifo_sample_v2(chip, - samples, HAP_PTN_V2_FIFO_DIN_NUM); - if (rc < 0) - return rc; + count = length / HAP_PTN_FIFO_DIN_NUM; + for (i = 0; i < count; i++) { + rc = haptics_update_fifo_sample(chip, + samples, HAP_PTN_FIFO_DIN_NUM); + if (rc < 0) + return rc; - samples += HAP_PTN_V2_FIFO_DIN_NUM; - } + samples += HAP_PTN_FIFO_DIN_NUM; + } - if (length % HAP_PTN_V2_FIFO_DIN_NUM) { - rc = haptics_update_fifo_sample_v2(chip, - samples, - length % HAP_PTN_V2_FIFO_DIN_NUM); - if (rc < 0) - return rc; - } + if (length % HAP_PTN_FIFO_DIN_NUM) { + rc = haptics_update_fifo_sample(chip, + samples, + length % HAP_PTN_FIFO_DIN_NUM); + if (rc < 0) + return rc; } return 0; @@ -1818,12 +1605,10 @@ static int haptics_set_fifo_playrate(struct haptics_chip *chip, enum s_period period_per_s) { int rc; - u8 reg; - reg = (chip->ptn_revision == HAP_PTN_V1) ? - HAP_PTN_V1_FIFO_PLAY_RATE_REG : HAP_PTN_V2_FIFO_PLAY_RATE_REG; rc = haptics_masked_write(chip, chip->ptn_addr_base, - reg, FIFO_PLAY_RATE_MASK, period_per_s); + HAP_PTN_FIFO_PLAY_RATE_REG, + FIFO_PLAY_RATE_MASK, period_per_s); if (rc < 0) dev_err(chip->dev, "Set FIFO play rate failed, rc=%d\n", rc); @@ -1833,7 +1618,7 @@ static int haptics_set_fifo_playrate(struct haptics_chip *chip, static int haptics_set_fifo_empty_threshold(struct haptics_chip *chip, u32 thresh) { - u8 reg, thresh_per_bit; + u8 thresh_per_bit; int rc; rc = get_fifo_threshold_per_bit(chip); @@ -1841,10 +1626,8 @@ static int haptics_set_fifo_empty_threshold(struct haptics_chip *chip, return rc; thresh_per_bit = rc; - - reg = (chip->ptn_revision == HAP_PTN_V1) ? - HAP_PTN_V1_FIFO_EMPTY_CFG_REG : HAP_PTN_V2_FIFO_EMPTY_CFG_REG; - rc = haptics_masked_write(chip, chip->ptn_addr_base, reg, + rc = haptics_masked_write(chip, chip->ptn_addr_base, + HAP_PTN_FIFO_EMPTY_CFG_REG, EMPTY_THRESH_MASK, (thresh / thresh_per_bit)); if (rc < 0) dev_err(chip->dev, "Set FIFO empty threshold failed, rc=%d\n", @@ -1903,13 +1686,6 @@ static int haptics_set_fifo(struct haptics_chip *chip, struct fifo_cfg *fifo) return -EBUSY; } - if (chip->ptn_revision == HAP_PTN_V1 && - fifo->period_per_s > F_8KHZ && - fifo->num_s > get_max_fifo_samples(chip)) { - dev_err(chip->dev, "PM8350B v1 doesn't support playing long FIFO pattern higher than 8 KHz play rate\n"); - return -EINVAL; - } - /* Configure FIFO play rate */ rc = haptics_set_fifo_playrate(chip, fifo->period_per_s); if (rc < 0) @@ -2545,23 +2321,16 @@ static void haptics_set_gain(struct input_dev *dev, u16 gain) static int haptics_store_cl_brake_settings(struct haptics_chip *chip) { int rc = 0; - u8 val; + u8 val[2]; if (!chip->cl_brake_nvmem) return 0; - val = MOD_STATUS_SEL_BRAKE_CAL_RNAT_RCAL_VAL; - rc = haptics_write(chip, chip->cfg_addr_base, - HAP_CFG_MOD_STATUS_SEL_REG, &val, 1); + rc = haptics_get_status_data(chip, RNAT_RCAL_INT, val); if (rc < 0) return rc; - rc = haptics_read(chip, chip->cfg_addr_base, - HAP_CFG_STATUS_DATA_LSB_REG, &val, 1); - if (rc < 0) - return rc; - - rc = nvmem_cell_write(chip->cl_brake_nvmem, &val, sizeof(val)); + rc = nvmem_cell_write(chip->cl_brake_nvmem, &val[1], 1); if (rc < 0) dev_err(chip->dev, "store RNAT/RCAL to SDAM failed, rc=%d\n"); @@ -2623,7 +2392,7 @@ static int haptics_hw_init(struct haptics_chip *chip) if (!is_haptics_external_powered(chip)) { rc = haptics_read(chip, chip->hbst_addr_base, - HAP_BOOST_CLAMP_5V_REG_OFFSET(chip), val, 1); + HAP_BOOST_CLAMP_REG, val, 1); if (rc < 0) return rc; @@ -2811,18 +2580,16 @@ static irqreturn_t fifo_empty_irq_handler(int irq, void *data) goto unlock; /* - * With HAPTICS_PATTERN module revision 2.0 and above, if use - * 1-byte write before 4-byte write, the hardware would insert - * zeros in between to keep the FIFO samples 4-byte aligned, and - * the inserted 0 values would cause HW stop driving hence spurs - * will be seen on the haptics output. So only use 1-byte write - * at the end of FIFO streaming. + * If 1-byte write is done before a 4-byte write, the hardware + * would insert zeros in between to keep the FIFO samples + * 4-byte aligned, and the inserted 0 values would cause HW + * stop driving hence spurs will be seen on the haptics output. + * So only use 1-byte write at the end of FIFO streaming. */ if (samples_left <= num) num = samples_left; - else if ((chip->ptn_revision >= HAP_PTN_V2) && - (num % HAP_PTN_V2_FIFO_DIN_NUM)) - num -= (num % HAP_PTN_V2_FIFO_DIN_NUM); + else if (num % HAP_PTN_FIFO_DIN_NUM) + num -= (num % HAP_PTN_FIFO_DIN_NUM); samples = fifo->samples + status->samples_written; @@ -3863,7 +3630,7 @@ static int haptics_get_revision(struct haptics_chip *chip) chip->ptn_revision = val[0]; if (is_haptics_external_powered(chip)) { - dev_dbg(chip->dev, "haptics revision: HAP_CFG %#x, HAP_PTN %#x\n", + dev_info(chip->dev, "haptics revision: HAP_CFG %#x, HAP_PTN %#x\n", chip->cfg_revision, chip->ptn_revision); } else { rc = haptics_read(chip, chip->hbst_addr_base, @@ -3872,8 +3639,16 @@ static int haptics_get_revision(struct haptics_chip *chip) 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", + dev_info(chip->dev, "haptics revision: HAP_CFG %#x, HAP_PTN %#x, HAP_HBST %#x\n", chip->cfg_revision, chip->ptn_revision, chip->hbst_revision); + + } + + if (chip->cfg_revision == HAP_CFG_V1 || + chip->ptn_revision == HAP_PTN_V1 || + chip->hbst_revision == HAP_BOOST_V0P0) { + dev_err(chip->dev, "haptics revision is not supported\n"); + return -EOPNOTSUPP; } return 0; @@ -4247,11 +4022,6 @@ static int haptics_detect_lra_impedance(struct haptics_chip *chip) u32 duty_milli_pct, low_milli_pct, high_milli_pct; u32 amplitude, lra_min_mohms, lra_max_mohms, capability_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++) { @@ -4709,9 +4479,6 @@ static int haptics_suspend(struct device *dev) struct haptics_play_info *play = &chip->play; int rc; - if (chip->cfg_revision == HAP_CFG_V1) - return 0; - mutex_lock(&play->lock); if ((play->pattern_src == FIFO) && atomic_read(&play->fifo_status.is_busy)) { @@ -4746,9 +4513,6 @@ static int haptics_resume(struct device *dev) { struct haptics_chip *chip = dev_get_drvdata(dev); - if (chip->cfg_revision == HAP_CFG_V1) - return 0; - return haptics_module_enable(chip, true); } #endif From ee656b6cb1e52b516ebedadb262963b73e95a832 Mon Sep 17 00:00:00 2001 From: Fenglin Wu Date: Tue, 14 Sep 2021 11:58:41 +0800 Subject: [PATCH 60/76] input: qcom-hv-haptics: change to use module type to differentiate HW Currently, the name of the PMIC along with haptics module is used to differentiate the HW. Modify it to use only HW type of the haptics module. The HW type can be detected by reading HAPTICS_CFG and HAPTICS_PATTERN module revision register. Add the logic for HW type detection. Also, unify the code places that use module revisions, instead, use HW type for differentiation. Change-Id: I8428cc74988e88c1057af8b41ed46979aa6009cb Signed-off-by: Fenglin Wu --- drivers/input/misc/qcom-hv-haptics.c | 78 ++++++++++++++-------------- 1 file changed, 40 insertions(+), 38 deletions(-) diff --git a/drivers/input/misc/qcom-hv-haptics.c b/drivers/input/misc/qcom-hv-haptics.c index 49dc43417936..5ce3520125bf 100644 --- a/drivers/input/misc/qcom-hv-haptics.c +++ b/drivers/input/misc/qcom-hv-haptics.c @@ -167,7 +167,7 @@ #define CAL_RC_CLK_AUTO_VAL 1 #define CAL_RC_CLK_MANUAL_VAL 2 -/* These registers are only applicable for PM5100 */ +/* These registers are only applicable for HAP520_MV */ #define HAP_CFG_HW_CONFIG_REG 0x0D #define HV_HAP_DRIVER_BIT BIT(1) @@ -332,9 +332,13 @@ enum custom_effect_param { CUSTOM_DATA_LEN, }; -enum pmic_type { - PM8350B, - PM5100, +/* + * HW type of the haptics module, the type value follows the + * revision value of the HAPTICS_CFG/HAPTICS_PATTERN modules + */ +enum haptics_hw_type { + HAP520 = 0x2, /* PM8350B */ + HAP520_MV = 0x3, /* PM5100 */ }; enum wa_flags { @@ -494,7 +498,7 @@ struct haptics_chip { u8 hpwr_intf_ctl; u16 hbst_revision; u16 max_vmax_mv; - enum pmic_type pmic_type; + enum haptics_hw_type hw_type; bool fifo_empty_irq_en; bool swr_slave_enabled; bool clamp_at_5v; @@ -511,15 +515,15 @@ static inline int get_max_fifo_samples(struct haptics_chip *chip) { int val = 0; - switch (chip->ptn_revision) { - case HAP_PTN_V2: + switch (chip->hw_type) { + case HAP520: val = 640; break; - case HAP_PTN_V3: + case HAP520_MV: val = 1024; break; default: - pr_err("Invalid pattern revision\n"); + pr_err("Invalid HW type\n"); break; } @@ -530,15 +534,15 @@ static int get_fifo_empty_threshold(struct haptics_chip *chip) { int val = 0; - switch (chip->ptn_revision) { - case HAP_PTN_V2: + switch (chip->hw_type) { + case HAP520: val = 280; break; - case HAP_PTN_V3: + case HAP520_MV: val = 288; break; default: - pr_err("Invalid pattern revision\n"); + pr_err("Invalid HW type\n"); break; } @@ -549,15 +553,15 @@ static int get_fifo_threshold_per_bit(struct haptics_chip *chip) { int val = -EINVAL; - switch (chip->ptn_revision) { - case HAP_PTN_V2: + switch (chip->hw_type) { + case HAP520: val = 40; break; - case HAP_PTN_V3: + case HAP520_MV: val = 32; break; default: - pr_err("Invalid pattern revision\n"); + pr_err("Invalid HW type\n"); break; } @@ -571,7 +575,7 @@ static bool is_haptics_external_powered(struct haptics_chip *chip) return true; /* Implicit voting by HW */ - if (chip->pmic_type == PM5100) + if (chip->hw_type == HAP520_MV) return true; /* Powered by HBOOST */ @@ -900,7 +904,7 @@ static int haptics_get_closeloop_lra_period( rc_clk_cal = ((val[0] & CAL_RC_CLK_MASK) >> CAL_RC_CLK_SHIFT); /* read auto resonance calibration result */ - if (in_boot && (chip->pmic_type == PM8350B)) { + if (in_boot && (chip->hw_type == HAP520)) { if (chip->hap_cfg_nvmem == NULL) { dev_dbg(chip->dev, "nvmem device for hap_cfg is not defined\n"); return -EINVAL; @@ -1513,7 +1517,7 @@ static int haptics_get_fifo_fill_status(struct haptics_chip *chip, u32 *fill) if (rc < 0) return rc; - fill_status_mask = (chip->cfg_revision == HAP_CFG_V2) ? + fill_status_mask = (chip->hw_type == HAP520) ? FIFO_REAL_TIME_FILL_STATUS_MSB_MASK_V2 : FIFO_REAL_TIME_FILL_STATUS_MSB_MASK_V3; *fill = ((val[0] & fill_status_mask) << 8) | val[1]; @@ -2358,15 +2362,15 @@ static int haptics_config_openloop_lra_period(struct haptics_chip *chip, static int haptics_config_wa(struct haptics_chip *chip) { - switch (chip->pmic_type) { - case PM8350B: + switch (chip->hw_type) { + case HAP520: chip->wa_flags |= TOGGLE_CAL_RC_CLK; break; - case PM5100: + case HAP520_MV: break; default: - dev_err(chip->dev, "PMIC type %d does not match\n", - chip->pmic_type); + dev_err(chip->dev, "HW type %d does not match\n", + chip->hw_type); return -EINVAL; } @@ -2402,7 +2406,7 @@ static int haptics_hw_init(struct haptics_chip *chip) chip->is_hv_haptics = true; chip->max_vmax_mv = MAX_VMAX_MV; - if (chip->pmic_type == PM5100) { + if (chip->hw_type == HAP520_MV) { rc = haptics_read(chip, chip->cfg_addr_base, HAP_CFG_HW_CONFIG_REG, val, 1); if (rc < 0) @@ -2436,7 +2440,7 @@ static int haptics_hw_init(struct haptics_chip *chip) if (rc < 0) return rc; - if ((chip->pmic_type == PM5100) && !chip->hpwr_vreg) { + if ((chip->hw_type == HAP520_MV) && !chip->hpwr_vreg) { /* Indicates if HPWR is BOB or Bharger */ rc = haptics_read(chip, chip->cfg_addr_base, HAP_CFG_HPWR_INTF_CTL_REG, val, 1); @@ -3644,9 +3648,13 @@ static int haptics_get_revision(struct haptics_chip *chip) } - if (chip->cfg_revision == HAP_CFG_V1 || - chip->ptn_revision == HAP_PTN_V1 || - chip->hbst_revision == HAP_BOOST_V0P0) { + if ((chip->cfg_revision == HAP_CFG_V2) && + (chip->ptn_revision == HAP_PTN_V2)) { + chip->hw_type = HAP520; + } else if ((chip->cfg_revision == HAP_CFG_V3) && + (chip->ptn_revision == HAP_PTN_V3)) { + chip->hw_type = HAP520_MV; + } else { dev_err(chip->dev, "haptics revision is not supported\n"); return -EOPNOTSUPP; } @@ -4325,8 +4333,8 @@ ATTRIBUTE_GROUPS(hap_class); static bool is_swr_supported(struct haptics_chip *chip) { - /* Haptics config version 3 does not support soundwire */ - if (chip->cfg_revision == HAP_CFG_V3) + /* HAP520_MV does not support soundwire */ + if (chip->hw_type == HAP520_MV) return false; return true; @@ -4354,9 +4362,6 @@ static int haptics_probe(struct platform_device *pdev) return -ENXIO; } - chip->pmic_type = - (enum pmic_type)(uintptr_t)of_device_get_match_data(chip->dev); - rc = haptics_parse_dt(chip); if (rc < 0) { dev_err(chip->dev, "Parse device-tree failed, rc = %d\n", rc); @@ -4524,15 +4529,12 @@ static const struct dev_pm_ops haptics_pm_ops = { static const struct of_device_id haptics_match_table[] = { { .compatible = "qcom,hv-haptics", - .data = (void *)PM8350B, }, { .compatible = "qcom,pm8350b-haptics", - .data = (void *)PM8350B, }, { .compatible = "qcom,pm5100-haptics", - .data = (void *)PM5100, }, {}, }; From 2e5deec278e620f942f2fd1a2dc8e39f663632a5 Mon Sep 17 00:00:00 2001 From: Fenglin Wu Date: Mon, 11 Oct 2021 09:51:09 +0800 Subject: [PATCH 61/76] input: qcom-hv-haptics: Add a flag to include hBoost control logic SW_CTRL_HBST flag is added to include the hBoost control logic which is required for HAP520 haptics module. Change-Id: I6615e6cff90f4cfb8d266752c61b3daf06dfe571 Signed-off-by: Fenglin Wu --- drivers/input/misc/qcom-hv-haptics.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/input/misc/qcom-hv-haptics.c b/drivers/input/misc/qcom-hv-haptics.c index 5ce3520125bf..3b2d63b31c8e 100644 --- a/drivers/input/misc/qcom-hv-haptics.c +++ b/drivers/input/misc/qcom-hv-haptics.c @@ -343,6 +343,7 @@ enum haptics_hw_type { enum wa_flags { TOGGLE_CAL_RC_CLK = BIT(0), + SW_CTRL_HBST = BIT(1), }; static const char * const src_str[] = { @@ -1137,6 +1138,9 @@ static int haptics_boost_vreg_enable(struct haptics_chip *chip, bool en) if (is_haptics_external_powered(chip)) return 0; + if (!(chip->wa_flags & SW_CTRL_HBST)) + return 0; + if (chip->hap_cfg_nvmem == NULL) { dev_dbg(chip->dev, "nvmem device for hap_cfg is not defined\n"); return 0; @@ -1207,6 +1211,9 @@ static int haptics_wait_hboost_ready(struct haptics_chip *chip) if (is_haptics_external_powered(chip)) return 0; + + if (!(chip->wa_flags & SW_CTRL_HBST)) + return 0; /* * Wait ~20ms until hBoost is ready, otherwise * bail out and return -EBUSY @@ -2364,7 +2371,7 @@ static int haptics_config_wa(struct haptics_chip *chip) { switch (chip->hw_type) { case HAP520: - chip->wa_flags |= TOGGLE_CAL_RC_CLK; + chip->wa_flags |= TOGGLE_CAL_RC_CLK | SW_CTRL_HBST; break; case HAP520_MV: break; From 4f82125ce4c2f74ad8879f58d9890778571cf167 Mon Sep 17 00:00:00 2001 From: Fenglin Wu Date: Mon, 11 Oct 2021 09:59:33 +0800 Subject: [PATCH 62/76] input: qcom-hv-haptics: Add HAP525_HV HW type support Haptics with HW type HAP525_HV is having revision value 4 in both HAPTICS_CFG and HAPTICS_PATTERN modules. Add this HW type support. Change-Id: Icb1f2291aaaffd0790f133146ea9601b0182989b Signed-off-by: Fenglin Wu --- drivers/input/misc/qcom-hv-haptics.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/input/misc/qcom-hv-haptics.c b/drivers/input/misc/qcom-hv-haptics.c index 3b2d63b31c8e..3471969a883d 100644 --- a/drivers/input/misc/qcom-hv-haptics.c +++ b/drivers/input/misc/qcom-hv-haptics.c @@ -30,6 +30,7 @@ #define HAP_CFG_V1 0x1 #define HAP_CFG_V2 0x2 #define HAP_CFG_V3 0x3 +#define HAP_CFG_V4 0x4 #define HAP_CFG_STATUS_DATA_MSB_REG 0x09 /* STATUS_DATA_MSB definitions while MOD_STATUS_SEL is 0 */ @@ -184,6 +185,7 @@ #define HAP_PTN_V1 0x1 #define HAP_PTN_V2 0x2 #define HAP_PTN_V3 0x3 +#define HAP_PTN_V4 0x4 /* status register definition for HAPTICS_PATTERN module */ #define HAP_PTN_FIFO_READY_STS_REG 0x08 @@ -339,6 +341,7 @@ enum custom_effect_param { enum haptics_hw_type { HAP520 = 0x2, /* PM8350B */ HAP520_MV = 0x3, /* PM5100 */ + HAP525_HV = 0x4, /* PMIC for kalama */ }; enum wa_flags { @@ -2374,6 +2377,7 @@ static int haptics_config_wa(struct haptics_chip *chip) chip->wa_flags |= TOGGLE_CAL_RC_CLK | SW_CTRL_HBST; break; case HAP520_MV: + case HAP525_HV: break; default: dev_err(chip->dev, "HW type %d does not match\n", @@ -3661,6 +3665,9 @@ static int haptics_get_revision(struct haptics_chip *chip) } else if ((chip->cfg_revision == HAP_CFG_V3) && (chip->ptn_revision == HAP_PTN_V3)) { chip->hw_type = HAP520_MV; + } else if ((chip->cfg_revision == HAP_CFG_V4) && + (chip->ptn_revision == HAP_PTN_V4)) { + chip->hw_type = HAP525_HV; } else { dev_err(chip->dev, "haptics revision is not supported\n"); return -EOPNOTSUPP; From 8898a26e7f1a84b7fd662e4c60bb5d91cce91110 Mon Sep 17 00:00:00 2001 From: Anjelique Melendez Date: Mon, 15 Nov 2021 14:22:14 -0800 Subject: [PATCH 63/76] input: qcom-hv-haptics: update LRA period adjustment Haptics clock calibration is based on SLEEP_CLK which is a 32.768 KHz clock however HAP520 module in PM8350B assumes SLEEP_CLK is 32 KHz exactly. Adjust LRA period and coefficients for calculations only when PM8350B haptics is present. Change-Id: If217937405aed2c2d93883f3645fe1bd5e7f6d8c Signed-off-by: Anjelique Melendez --- drivers/input/misc/qcom-hv-haptics.c | 32 +++++++++++++++++----------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/drivers/input/misc/qcom-hv-haptics.c b/drivers/input/misc/qcom-hv-haptics.c index 3471969a883d..cab3e5df29d6 100644 --- a/drivers/input/misc/qcom-hv-haptics.c +++ b/drivers/input/misc/qcom-hv-haptics.c @@ -347,6 +347,7 @@ enum haptics_hw_type { enum wa_flags { TOGGLE_CAL_RC_CLK = BIT(0), SW_CTRL_HBST = BIT(1), + SLEEP_CLK_32K_SCALE = BIT(2), }; static const char * const src_str[] = { @@ -869,15 +870,17 @@ static int haptics_adjust_lra_period(struct haptics_chip *chip, u32 *t_lra_us) int rc; u8 val; - rc = haptics_read(chip, chip->cfg_addr_base, - HAP_CFG_CAL_EN_REG, &val, 1); - if (rc < 0) - return rc; + if (chip->wa_flags & SLEEP_CLK_32K_SCALE) { + rc = haptics_read(chip, chip->cfg_addr_base, + HAP_CFG_CAL_EN_REG, &val, 1); + if (rc < 0) + return rc; - if ((val & CAL_RC_CLK_MASK) == - (CAL_RC_CLK_AUTO_VAL << CAL_RC_CLK_SHIFT)) - *t_lra_us = div_u64((u64)(*t_lra_us) * AUTO_CAL_CLK_SCALE_NUM, - AUTO_CAL_CLK_SCALE_DEN); + if ((val & CAL_RC_CLK_MASK) == + (CAL_RC_CLK_AUTO_VAL << CAL_RC_CLK_SHIFT)) + *t_lra_us = div_u64((u64)(*t_lra_us) * AUTO_CAL_CLK_SCALE_NUM, + AUTO_CAL_CLK_SCALE_DEN); + } return 0; } @@ -888,8 +891,10 @@ static int haptics_adjust_lra_period(struct haptics_chip *chip, u32 *t_lra_us) /* constant definitions for calculating TLRA */ #define TLRA_AUTO_RES_ERR_NO_CAL_STEP_PSEC 1667000 #define TLRA_AUTO_RES_NO_CAL_STEP_PSEC 3333000 -#define TLRA_AUTO_RES_ERR_AUTO_CAL_STEP_PSEC 1627700 -#define TLRA_AUTO_RES_AUTO_CAL_STEP_PSEC 813850 +#define TLRA_AUTO_RES_ERR_AUTO_CAL_STEP_PSEC \ + ((chip->wa_flags & SLEEP_CLK_32K_SCALE) ? 1627700 : 1666667) +#define TLRA_AUTO_RES_AUTO_CAL_STEP_PSEC \ + ((chip->wa_flags & SLEEP_CLK_32K_SCALE) ? 813850 : 833333) static int haptics_get_closeloop_lra_period( struct haptics_chip *chip, bool in_boot) { @@ -959,7 +964,7 @@ static int haptics_get_closeloop_lra_period( /* * CAL_TLRA_OL = CAL_TLRA_CL_STS; * TLRA_CL_ERR(us) = TLRA_CL_ERR_STS * - * (TLRA_OL / CAL_TLRA_OL) * 1.6277 us + * (TLRA_OL / CAL_TLRA_OL) * TLRA_AUTO_RES_ERR_AUTO_CAL_STEP_US */ /* read the TLRA_OL setting */ @@ -992,7 +997,8 @@ static int haptics_get_closeloop_lra_period( } else if (rc_clk_cal == CAL_RC_CLK_AUTO_VAL && auto_res_done) { /* * CAL_TLRA_CL_STS_W_CAL = CAL_TLRA_CL_STS; - * TLRA_AUTO_RES(us) = LAST_GOOD_TLRA_CL_STS * 0.81385 us * + * TLRA_AUTO_RES(us) = LAST_GOOD_TLRA_CL_STS * + * TLRA_AUTO_RES_AUTO_CAL_STEP_US * * (LAST_GOOD_TLRA_CL_STS / CAL_TLRA_CL_STS_AUTO_CAL) */ @@ -2374,7 +2380,7 @@ static int haptics_config_wa(struct haptics_chip *chip) { switch (chip->hw_type) { case HAP520: - chip->wa_flags |= TOGGLE_CAL_RC_CLK | SW_CTRL_HBST; + chip->wa_flags |= TOGGLE_CAL_RC_CLK | SW_CTRL_HBST | SLEEP_CLK_32K_SCALE; break; case HAP520_MV: case HAP525_HV: From c49cead0c077dbae9011765867557706c740100a Mon Sep 17 00:00:00 2001 From: Fenglin Wu Date: Fri, 12 Nov 2021 10:36:52 +0800 Subject: [PATCH 64/76] input: qcom-hv-haptics: split haptics_hw_init() into small functions Split haptics_hw_init() into small functions for better readability. Change-Id: I34a6d48d0760d3dc834f6e8d2763ff8735f13f83 Signed-off-by: Fenglin Wu --- drivers/input/misc/qcom-hv-haptics.c | 268 +++++++++++++++------------ 1 file changed, 147 insertions(+), 121 deletions(-) diff --git a/drivers/input/misc/qcom-hv-haptics.c b/drivers/input/misc/qcom-hv-haptics.c index cab3e5df29d6..c33698d6612e 100644 --- a/drivers/input/misc/qcom-hv-haptics.c +++ b/drivers/input/misc/qcom-hv-haptics.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved. + * Copyright (c) 2021 Qualcomm Innovation Center, Inc. All rights reserved. */ #include @@ -2376,6 +2377,145 @@ static int haptics_config_openloop_lra_period(struct haptics_chip *chip, HAP_CFG_TLRA_OL_HIGH_REG, val, 2); } +static int haptics_init_preload_pattern_effect(struct haptics_chip *chip) +{ + struct haptics_hw_config *config = &chip->config; + struct haptics_effect *effect; + int i; + + if (config->preload_effect == -EINVAL) + return 0; + + for (i = 0; i < chip->effects_count; i++) + if (chip->effects[i].id == config->preload_effect) + break; + + if (i == chip->effects_count) { + dev_err(chip->dev, "preload effect %d is not found\n", + config->preload_effect); + return -EINVAL; + } + + effect = &chip->effects[i]; + return haptics_set_pattern(chip, effect->pattern, effect->src); +} + +static int haptics_init_lra_period_config(struct haptics_chip *chip) +{ + int rc = 0; + u8 val; + u32 t_lra_us; + + /* set AUTO_mode RC CLK calibration by default */ + val = FIELD_PREP(CAL_RC_CLK_MASK, CAL_RC_CLK_AUTO_VAL); + rc = haptics_masked_write(chip, chip->cfg_addr_base, + HAP_CFG_CAL_EN_REG, CAL_RC_CLK_MASK, val); + if (rc < 0) + return rc; + + /* get calibrated close loop period */ + rc = haptics_get_closeloop_lra_period(chip, true); + if (rc < 0) + return rc; + + /* Config T_LRA */ + t_lra_us = chip->config.t_lra_us; + if (chip->config.cl_t_lra_us != 0) + t_lra_us = chip->config.cl_t_lra_us; + + return haptics_config_openloop_lra_period(chip, t_lra_us); +} + +static int haptics_init_hpwr_config(struct haptics_chip *chip) +{ + int rc; + u8 val; + + if ((chip->hw_type == HAP520_MV) && !chip->hpwr_vreg) { + /* Indicates if HPWR is BOB or Bharger */ + rc = haptics_read(chip, chip->cfg_addr_base, + HAP_CFG_HPWR_INTF_CTL_REG, &val, 1); + if (rc < 0) + return rc; + + chip->hpwr_intf_ctl = val & INTF_CTL_MASK; + + /* Read HPWR voltage to adjust the VMAX */ + if (chip->hpwr_voltage_mv == 0 && + chip->hpwr_intf_ctl == INTF_CTL_BOB) { + rc = haptics_read(chip, chip->cfg_addr_base, + HAP_CFG_VHPWR_REG, &val, 1); + if (rc < 0) + return rc; + + chip->hpwr_voltage_mv = val * VHPWR_STEP_MV; + } + } + + /* Force VREG_RDY if non-HBoost is used for powering haptics */ + if (is_haptics_external_powered(chip)) + return haptics_masked_write(chip, chip->cfg_addr_base, + HAP_CFG_VSET_CFG_REG, FORCE_VREG_RDY_BIT, + FORCE_VREG_RDY_BIT); + + return 0; +} + +static int haptics_init_drive_config(struct haptics_chip *chip) +{ + struct haptics_hw_config *config = &chip->config; + int rc; + u8 val; + + /* Config driver waveform shape and use 2's complement data format */ + rc = haptics_masked_write(chip, chip->cfg_addr_base, + HAP_CFG_DRV_WF_SEL_REG, + DRV_WF_SEL_MASK | DRV_WF_FMT_BIT, config->drv_wf); + if (rc < 0) + return rc; + + /* Config brake mode and waveform shape */ + val = FIELD_PREP(BRAKE_MODE_MASK, config->brake.mode); + val |= FIELD_PREP(BRAKE_SINE_GAIN_MASK, config->brake.sine_gain); + val |= FIELD_PREP(BRAKE_WF_SEL_MASK, config->brake.brake_wf); + + return haptics_masked_write(chip, chip->cfg_addr_base, + HAP_CFG_BRAKE_MODE_CFG_REG, + BRAKE_MODE_MASK | BRAKE_SINE_GAIN_MASK + | BRAKE_WF_SEL_MASK, val); +} + +static int haptics_init_vmax_config(struct haptics_chip *chip) +{ + int rc; + u8 val; + + if (!is_haptics_external_powered(chip)) { + rc = haptics_read(chip, chip->hbst_addr_base, + HAP_BOOST_CLAMP_REG, &val, 1); + if (rc < 0) + return rc; + + chip->clamp_at_5v = val & CLAMP_5V_BIT; + } + + chip->is_hv_haptics = true; + chip->max_vmax_mv = MAX_VMAX_MV; + if (chip->hw_type == HAP520_MV) { + rc = haptics_read(chip, chip->cfg_addr_base, + HAP_CFG_HW_CONFIG_REG, &val, 1); + if (rc < 0) + return rc; + + chip->is_hv_haptics = val & HV_HAP_DRIVER_BIT; + chip->max_vmax_mv = (chip->is_hv_haptics) ? + MAX_HV_VMAX_MV : MAX_MV_VMAX_MV; + } + + /* Config VMAX */ + return haptics_set_vmax_mv(chip, chip->config.vmax_mv); +} + static int haptics_config_wa(struct haptics_chip *chip) { switch (chip->hw_type) { @@ -2396,150 +2536,36 @@ static int haptics_config_wa(struct haptics_chip *chip) static int haptics_hw_init(struct haptics_chip *chip) { - struct haptics_hw_config *config = &chip->config; - struct haptics_effect *effect; - int rc = 0, i; - u8 val[2]; - u32 t_lra_us; + int rc; rc = haptics_config_wa(chip); if (rc < 0) return rc; - /* Store CL brake settings */ rc = haptics_store_cl_brake_settings(chip); if (rc < 0) return rc; - if (!is_haptics_external_powered(chip)) { - rc = haptics_read(chip, chip->hbst_addr_base, - HAP_BOOST_CLAMP_REG, val, 1); - if (rc < 0) - return rc; - - chip->clamp_at_5v = val[0] & CLAMP_5V_BIT; - } - - chip->is_hv_haptics = true; - chip->max_vmax_mv = MAX_VMAX_MV; - - if (chip->hw_type == HAP520_MV) { - rc = haptics_read(chip, chip->cfg_addr_base, - HAP_CFG_HW_CONFIG_REG, val, 1); - if (rc < 0) - return rc; - - chip->is_hv_haptics = val[0] & HV_HAP_DRIVER_BIT; - chip->max_vmax_mv = (chip->is_hv_haptics) ? - MAX_HV_VMAX_MV : MAX_MV_VMAX_MV; - } - - /* Config VMAX */ - rc = haptics_set_vmax_mv(chip, config->vmax_mv); + rc = haptics_init_vmax_config(chip); if (rc < 0) return rc; - /* Config driver waveform shape */ - rc = haptics_masked_write(chip, chip->cfg_addr_base, - HAP_CFG_DRV_WF_SEL_REG, - DRV_WF_SEL_MASK, config->drv_wf); + rc = haptics_init_drive_config(chip); if (rc < 0) return rc; - /* Config brake mode and waveform shape */ - val[0] = (config->brake.mode << BRAKE_MODE_SHIFT) - | config->brake.sine_gain << BRAKE_SINE_GAIN_SHIFT - | config->brake.brake_wf; - rc = haptics_masked_write(chip, chip->cfg_addr_base, - HAP_CFG_BRAKE_MODE_CFG_REG, - BRAKE_MODE_MASK | BRAKE_SINE_GAIN_MASK - | BRAKE_WF_SEL_MASK, val[0]); + rc = haptics_init_hpwr_config(chip); if (rc < 0) return rc; - if ((chip->hw_type == HAP520_MV) && !chip->hpwr_vreg) { - /* Indicates if HPWR is BOB or Bharger */ - rc = haptics_read(chip, chip->cfg_addr_base, - HAP_CFG_HPWR_INTF_CTL_REG, val, 1); - if (rc < 0) - return rc; - - chip->hpwr_intf_ctl = val[0] & INTF_CTL_MASK; - - /* Read HPWR voltage to adjust the VMAX */ - if (chip->hpwr_voltage_mv == 0 && - chip->hpwr_intf_ctl == INTF_CTL_BOB) { - rc = haptics_read(chip, chip->cfg_addr_base, - HAP_CFG_VHPWR_REG, val, 1); - if (rc < 0) - return rc; - - chip->hpwr_voltage_mv = val[0] * VHPWR_STEP_MV; - } - } - - if (is_haptics_external_powered(chip)) { - /* Force VREG_RDY if non-HBoost is used for powering haptics */ - rc = haptics_masked_write(chip, chip->cfg_addr_base, - HAP_CFG_VSET_CFG_REG, FORCE_VREG_RDY_BIT, - FORCE_VREG_RDY_BIT); - if (rc < 0) - return rc; - } - - if (config->is_erm) + if (chip->config.is_erm) return 0; - /* set AUTO_mode RC CLK calibration by default */ - val[0] = CAL_RC_CLK_AUTO_VAL << CAL_RC_CLK_SHIFT; - rc = haptics_masked_write(chip, chip->cfg_addr_base, - HAP_CFG_CAL_EN_REG, CAL_RC_CLK_MASK, val[0]); + rc = haptics_init_lra_period_config(chip); if (rc < 0) return rc; - /* get calibrated close loop period */ - rc = haptics_get_closeloop_lra_period(chip, true); - if (rc < 0) - return rc; - - /* Config T_LRA */ - t_lra_us = chip->config.t_lra_us; - if (chip->config.cl_t_lra_us != 0) - t_lra_us = chip->config.cl_t_lra_us; - - rc = haptics_config_openloop_lra_period(chip, t_lra_us); - if (rc < 0) - return rc; - - /* Config to use 2's complement values for sample data */ - rc = haptics_masked_write(chip, chip->cfg_addr_base, - HAP_CFG_DRV_WF_SEL_REG, DRV_WF_FMT_BIT, 0); - if (rc < 0) - return rc; - - /* preload effect */ - if (config->preload_effect != -EINVAL) { - for (i = 0; i < chip->effects_count; i++) - if (chip->effects[i].id == config->preload_effect) - break; - - if (i == chip->effects_count) { - dev_err(chip->dev, "preload effect %d is not found\n", - config->preload_effect); - return -EINVAL; - } - - effect = &chip->effects[i]; - - rc = haptics_set_pattern(chip, effect->pattern, effect->src); - if (rc < 0) { - dev_err(chip->dev, "Preload effect failed, rc=%d\n", - rc); - return rc; - } - } - - return rc; + return haptics_init_preload_pattern_effect(chip); } static irqreturn_t fifo_empty_irq_handler(int irq, void *data) From be18d8d3b5ccc100281f5735acd5b31e4e924ce7 Mon Sep 17 00:00:00 2001 From: Fenglin Wu Date: Tue, 23 Nov 2021 13:49:30 +0800 Subject: [PATCH 65/76] input: qcom-hv-haptics: add PATx_MEM mode support for HAP525_HV HAP525_HV haptics module has 2048 bytes of SRAM which can be allocated for 1 FIFO partition and 4 PATx_MEM partitions. The PATx_MEM memory space is allocated following the FIFO memory and the size for either FIFO or PATx_MEM can be configured differently. Normally, memory allocation can be started at the end and the space is sequentially allocated to PAT4_MEM through PAT1_MEM, and the leftover space is for FIFO. Any PATx_MEM with memory size 0 will not be used/enabled. Also, it's suggested to cofigure FIFO memory size not less than 640 bytes to ensure the normal function of FIFO streaming mode. For each PATx_MEM memory partition, it has a corresponding PATx_MEM mode which can read data from its space as the pattern source for a vibration effect. This is very similar to FIFO mode but just PATx_MEM mode can't be played in a loop with data refilling. Add "qcom,wf-fifo-preload" property to allow use PATx_MEM mode to implement preload FIFO effect. If this property is specified, during driver probe, PATx_MEM partition with enough memory will be allocated to store the preload effect data, and the data will be remain unchanged until a device reboot. Later, whenever a play request with such effect is issued, it will be simply triggered by writing to the play register. If there is not free PATx_MEM partition or no big enough memory for storing the preload effect data during driver probe, the preload effect will be treated as normal FIFO streaming effect and it will be loaded and played at runtime. Change-Id: Ic165e396f22f56e61b68bbc5cfc05c461f0f77d6 Signed-off-by: Fenglin Wu --- drivers/input/misc/qcom-hv-haptics.c | 410 +++++++++++++++++++++++---- 1 file changed, 361 insertions(+), 49 deletions(-) diff --git a/drivers/input/misc/qcom-hv-haptics.c b/drivers/input/misc/qcom-hv-haptics.c index c33698d6612e..029c856cde07 100644 --- a/drivers/input/misc/qcom-hv-haptics.c +++ b/drivers/input/misc/qcom-hv-haptics.c @@ -44,6 +44,7 @@ /* STATUS_DATA_MSB when MOD_STATUS_SEL is 5 and MOD_STATUS_XT.SEL is 1 */ #define FIFO_REAL_TIME_FILL_STATUS_MSB_MASK_V2 GENMASK(1, 0) #define FIFO_REAL_TIME_FILL_STATUS_MSB_MASK_V3 GENMASK(2, 0) +#define FIFO_REAL_TIME_FILL_STATUS_MSB_MASK_V4 GENMASK(3, 0) #define FIFO_EMPTY_FLAG_BIT BIT(6) #define FIFO_FULL_FLAG_BIT BIT(5) @@ -91,6 +92,7 @@ #define HAP_CFG_SPMI_PLAY_REG 0x4C #define PLAY_EN_BIT BIT(7) +#define PATX_MEM_SEL_MASK GENMASK(5, 4) /* This is only for HAP525_HV */ #define BRAKE_EN_BIT BIT(3) #define PAT_SRC_MASK GENMASK(2, 0) @@ -229,6 +231,23 @@ #define HAP_PTN_BRAKE_AMP_REG 0x70 +/* HAPTICS_PATTERN registers only present in HAP525_HV */ +#define HAP_PTN_MEM_OP_ACCESS_REG 0x2D +#define MEM_PAT_ACCESS_BIT BIT(7) +#define MEM_PAT_RW_SEL_MASK GENMASK(5, 4) +#define MEM_FLUSH_RELOAD_BIT BIT(0) + +#define HAP_PTN_MMAP_FIFO_REG 0xA0 +#define MMAP_FIFO_EXIST_BIT BIT(7) +#define MMAP_FIFO_LEN_MASK GENMASK(3, 0) +#define MMAP_FIFO_LEN_PER_LSB 128 + +#define HAP_PTN_MMAP_PAT1_REG 0xA1 +#define MMAP_PAT_LEN_PER_LSB 32 +#define MMAP_PAT1_LEN_MASK GENMASK(6, 0) +#define MMAP_PAT2_LEN_MASK GENMASK(5, 0) +#define MMAP_PAT3_PAT4_LEN_MASK GENMASK(4, 0) + /* register in HBOOST module */ #define HAP_BOOST_REVISION1 0x00 #define HAP_BOOST_REVISION2 0x01 @@ -262,6 +281,9 @@ #define HW_BRAKE_CYCLES 5 #define F_LRA_VARIATION_HZ 5 #define NON_HBOOST_MAX_VMAX_MV 4000 +/* below definitions are only for HAP525_HV */ +#define MMAP_NUM_BYTES 2048 +#define MMAP_FIFO_MIN_SIZE 640 #define is_between(val, min, max) \ (((min) <= (max)) && ((min) <= (val)) && ((val) <= (max))) @@ -300,12 +322,21 @@ enum brake_sine_gain { BRAKE_SINE_GAIN_X8, }; +enum pat_mem_sel { + PAT1_MEM, + PAT2_MEM, + PAT3_MEM, + PAT4_MEM, + PAT_MEM_MAX, +}; + enum pattern_src { FIFO, DIRECT_PLAY, PATTERN1, PATTERN2, SWR, + PATTERN_MEM, SRC_RESERVED, }; @@ -403,6 +434,7 @@ struct fifo_cfg { u32 num_s; enum s_period period_per_s; u32 play_length_us; + bool preload; }; struct brake_cfg { @@ -422,9 +454,21 @@ struct haptics_effect { u32 vmax_mv; u32 t_lra_us; enum pattern_src src; + enum pat_mem_sel pat_sel; bool auto_res_disable; }; +struct mmap_partition { + u32 max_size; + u32 length; + bool in_use; +}; + +struct haptics_mmap { + struct mmap_partition fifo_mmap; + struct mmap_partition pat_sel_mmap[PAT_MEM_MAX]; +}; + /** * struct fifo_play_status - Data used for recording the FIFO playing status * @@ -485,6 +529,7 @@ struct haptics_chip { struct haptics_effect *effects; struct haptics_effect *custom_effect; struct haptics_play_info play; + struct haptics_mmap mmap; struct dentry *debugfs_dir; struct regulator_dev *swr_slave_rdev; struct nvmem_cell *cl_brake_nvmem; @@ -528,6 +573,10 @@ static inline int get_max_fifo_samples(struct haptics_chip *chip) case HAP520_MV: val = 1024; break; + case HAP525_HV: + val = chip->mmap.fifo_mmap.length ? + chip->mmap.fifo_mmap.length : MMAP_FIFO_MIN_SIZE; + break; default: pr_err("Invalid HW type\n"); break; @@ -545,6 +594,7 @@ static int get_fifo_empty_threshold(struct haptics_chip *chip) val = 280; break; case HAP520_MV: + case HAP525_HV: val = 288; break; default: @@ -564,6 +614,7 @@ static int get_fifo_threshold_per_bit(struct haptics_chip *chip) val = 40; break; case HAP520_MV: + case HAP525_HV: val = 32; break; default: @@ -1380,6 +1431,9 @@ static int haptics_enable_play(struct haptics_chip *chip, bool en) return rc; val = play->pattern_src; + if (chip->hw_type == HAP525_HV && play->pattern_src == PATTERN_MEM) + val |= FIELD_PREP(PATX_MEM_SEL_MASK, play->effect->pat_sel); + if (play->brake && !play->brake->disabled) val |= BRAKE_EN_BIT; @@ -1492,27 +1546,32 @@ static int haptics_set_pattern(struct haptics_chip *chip, } -static int haptics_update_fifo_sample(struct haptics_chip *chip, - u8 *samples, u32 num) +static int haptics_update_memory_data(struct haptics_chip *chip, + u8 *data, u32 length) { - int rc, i; + int rc, count, i; + u32 left; - if (num > HAP_PTN_FIFO_DIN_NUM) - return -EINVAL; - - if (num == HAP_PTN_FIFO_DIN_NUM) { + count = length / HAP_PTN_FIFO_DIN_NUM; + for (i = 0; i < count; i++) { rc = haptics_write(chip, chip->ptn_addr_base, - HAP_PTN_FIFO_DIN_0_REG, samples, num); + HAP_PTN_FIFO_DIN_0_REG, data, + HAP_PTN_FIFO_DIN_NUM); if (rc < 0) { dev_err(chip->dev, "bulk write FIFO_DIN failed, rc=%d\n", rc); return rc; } - } else if (num < HAP_PTN_FIFO_DIN_NUM) { - for (i = 0; i < num; i++) { + + data += HAP_PTN_FIFO_DIN_NUM; + } + + left = length % HAP_PTN_FIFO_DIN_NUM; + if (left) { + for (i = 0; i < left; i++) { rc = haptics_write(chip, chip->ptn_addr_base, HAP_PTN_FIFO_DIN_1B_REG, - (samples + i), 1); + (data + i), 1); if (rc < 0) { dev_err(chip->dev, "write FIFO_DIN_1B failed, rc=%d\n", rc); @@ -1524,6 +1583,68 @@ static int haptics_update_fifo_sample(struct haptics_chip *chip, return 0; } +static int haptics_update_fifo_samples(struct haptics_chip *chip, + u8 *samples, u32 length) +{ + int rc; + u8 val; + + if (!samples) { + dev_err(chip->dev, "no data available to update FIFO\n"); + return -EINVAL; + } + + if (chip->hw_type == HAP525_HV) { + val = MEM_FLUSH_RELOAD_BIT; + rc = haptics_write(chip, chip->ptn_addr_base, + HAP_PTN_MEM_OP_ACCESS_REG, &val, 1); + if (rc < 0) + return rc; + + val = 0; + rc = haptics_write(chip, chip->ptn_addr_base, + HAP_PTN_MEM_OP_ACCESS_REG, &val, 1); + if (rc < 0) + return rc; + } + + return haptics_update_memory_data(chip, samples, length); +} + +static int haptics_update_pat_mem_samples(struct haptics_chip *chip, + enum pat_mem_sel pat_sel, u8 *samples, u32 length) +{ + int rc; + u8 val; + + if (!samples) { + dev_err(chip->dev, "no data available to update PAT_MEM\n"); + return -EINVAL; + } + + /* only HAP525_HV supports PATx_MEM pattern source */ + if (chip->hw_type != HAP525_HV) { + dev_dbg(chip->dev, "HW type %d doesn't support PATx_MEM pattern source\n", + chip->hw_type); + return 0; + } + + val = FIELD_PREP(MEM_PAT_RW_SEL_MASK, pat_sel); + val |= MEM_PAT_ACCESS_BIT | MEM_FLUSH_RELOAD_BIT; + rc = haptics_write(chip, chip->ptn_addr_base, + HAP_PTN_MEM_OP_ACCESS_REG, &val, 1); + if (rc < 0) + return rc; + + val &= ~MEM_FLUSH_RELOAD_BIT; + rc = haptics_write(chip, chip->ptn_addr_base, + HAP_PTN_MEM_OP_ACCESS_REG, &val, 1); + if (rc < 0) + return rc; + + return haptics_update_memory_data(chip, samples, length); +} + static int haptics_get_fifo_fill_status(struct haptics_chip *chip, u32 *fill) { int rc; @@ -1534,9 +1655,22 @@ static int haptics_get_fifo_fill_status(struct haptics_chip *chip, u32 *fill) if (rc < 0) return rc; - fill_status_mask = (chip->hw_type == HAP520) ? - FIFO_REAL_TIME_FILL_STATUS_MSB_MASK_V2 : - FIFO_REAL_TIME_FILL_STATUS_MSB_MASK_V3; + switch (chip->hw_type) { + case HAP520: + fill_status_mask = FIFO_REAL_TIME_FILL_STATUS_MSB_MASK_V2; + break; + case HAP520_MV: + fill_status_mask = FIFO_REAL_TIME_FILL_STATUS_MSB_MASK_V3; + break; + case HAP525_HV: + fill_status_mask = FIFO_REAL_TIME_FILL_STATUS_MSB_MASK_V4; + break; + default: + dev_err(chip->dev, "HW type %d is not supported\n", + chip->hw_type); + return -EINVAL; + } + *fill = ((val[0] & fill_status_mask) << 8) | val[1]; empty = !!(val[0] & FIFO_EMPTY_FLAG_BIT); full = !!(val[0] & FIFO_FULL_FLAG_BIT); @@ -1591,37 +1725,6 @@ static int haptics_set_manual_rc_clk_cal(struct haptics_chip *chip) val[0]); } -static int haptics_update_fifo_samples(struct haptics_chip *chip, - u8 *samples, u32 length) -{ - int rc, count, i; - - if (samples == NULL) { - dev_err(chip->dev, "no FIFO samples available\n"); - return -EINVAL; - } - - count = length / HAP_PTN_FIFO_DIN_NUM; - for (i = 0; i < count; i++) { - rc = haptics_update_fifo_sample(chip, - samples, HAP_PTN_FIFO_DIN_NUM); - if (rc < 0) - return rc; - - samples += HAP_PTN_FIFO_DIN_NUM; - } - - if (length % HAP_PTN_FIFO_DIN_NUM) { - rc = haptics_update_fifo_sample(chip, - samples, - length % HAP_PTN_FIFO_DIN_NUM); - if (rc < 0) - return rc; - } - - return 0; -} - static int haptics_set_fifo_playrate(struct haptics_chip *chip, enum s_period period_per_s) { @@ -1844,9 +1947,8 @@ static int haptics_load_predefined_effect(struct haptics_chip *chip, return rc; play->pattern_src = play->effect->src; - if (play->pattern_src != PATTERN1 && - play->pattern_src != PATTERN2 && - play->pattern_src != FIFO) { + if (play->pattern_src == DIRECT_PLAY || + play->pattern_src == SWR) { dev_err(chip->dev, "pattern src %d can't be used for predefined effect\n", play->pattern_src); return -EINVAL; @@ -1879,6 +1981,25 @@ static int haptics_load_predefined_effect(struct haptics_chip *chip, return rc; } + /* + * PATTERN_MEM sources (PATx_MEM) introduced in HAP525_HV haptics + * module are used for preload effects. The pattern for the preload + * effect should have been programmed during boot up and it will be + * retained until device is powered off, so it doesn't need to be + * programmed at runtime. + */ + if (chip->hw_type == HAP525_HV && + play->pattern_src == PATTERN_MEM) { + if (!play->effect->fifo->preload) { + dev_err(chip->dev, "effect %d has PAT_MEM src but not preloaded\n", + play->effect->id); + return -EINVAL; + } + + dev_dbg(chip->dev, "Ignore loading data for preload FIFO effect: %d\n", + play->effect->id); + } + return 0; } @@ -2377,6 +2498,191 @@ static int haptics_config_openloop_lra_period(struct haptics_chip *chip, HAP_CFG_TLRA_OL_HIGH_REG, val, 2); } +static int haptics_config_preload_fifo_effect(struct haptics_chip *chip, + struct haptics_effect *effect) +{ + int rc; + + if (!effect->fifo->preload) { + dev_err(chip->dev, "effect %d doesn't support preload\n", + effect->id); + return -EINVAL; + } + + if (effect->src != PATTERN_MEM) { + dev_err(chip->dev, "effect %d pattern_src is not PATTERN_MEM\n", + effect->id); + return -EINVAL; + } + + rc = haptics_update_pat_mem_samples(chip, effect->pat_sel, + effect->fifo->samples, effect->fifo->num_s); + if (!rc) + dev_dbg(chip->dev, "effect %d is preloaded in PAT_MEM %d\n", + effect->id, effect->pat_sel); + return rc; +} + +static int haptics_mmap_config(struct haptics_chip *chip) +{ + int rc, i, left; + u8 val[4]; + + /* + * Make the FIFO memory size 128-byte aligned, and append + * the leftover memory bytes to PAT1_MEM. + */ + left = chip->mmap.fifo_mmap.length % MMAP_FIFO_LEN_PER_LSB; + if (left) { + chip->mmap.fifo_mmap.length -= left; + chip->mmap.pat_sel_mmap[PAT1_MEM].length += left; + } + + /* config MMAP_FIFO */ + val[0] = chip->mmap.fifo_mmap.length / MMAP_FIFO_LEN_PER_LSB; + if (val[0]-- == 0) { + dev_err(chip->dev, "fifo length %d is less than %d\n", + chip->mmap.fifo_mmap.length, + MMAP_FIFO_LEN_PER_LSB); + return -EINVAL; + } + + val[0] &= MMAP_FIFO_LEN_MASK; + val[0] |= MMAP_FIFO_EXIST_BIT; + rc = haptics_write(chip, chip->ptn_addr_base, + HAP_PTN_MMAP_FIFO_REG, val, 1); + if (rc < 0) + return rc; + + /* config MMAP_PAT1/2/3/4 */ + for (i = PAT1_MEM; i <= PAT4_MEM; i++) + val[i] = min(chip->mmap.pat_sel_mmap[i].length, chip->mmap.pat_sel_mmap[i].max_size) + / MMAP_PAT_LEN_PER_LSB; + + rc = haptics_write(chip, chip->ptn_addr_base, + HAP_PTN_MMAP_PAT1_REG, val, 4); + if (rc < 0) + return rc; + + dev_dbg(chip->dev, "haptics memory map configured with FIFO: %d, PAT1:%d, PAT2: %d, PAT3: %d, PAT4: %d\n", + chip->mmap.fifo_mmap.length, + chip->mmap.pat_sel_mmap[PAT1_MEM].length, + chip->mmap.pat_sel_mmap[PAT2_MEM].length, + chip->mmap.pat_sel_mmap[PAT3_MEM].length, + chip->mmap.pat_sel_mmap[PAT4_MEM].length); + return 0; +} + +static int haptics_mmap_preload_fifo_effect(struct haptics_chip *chip, + struct haptics_effect *effect) +{ + int i; + u32 length, fifo_length; + + if (!effect->fifo->preload) { + dev_err(chip->dev, "effect %d doesn't support preload\n"); + return -EINVAL; + } + + length = effect->fifo->num_s; + if (length % MMAP_PAT_LEN_PER_LSB) + length = (length / MMAP_PAT_LEN_PER_LSB + 1) * MMAP_PAT_LEN_PER_LSB; + + fifo_length = chip->mmap.fifo_mmap.length - length; + if (fifo_length < MMAP_FIFO_MIN_SIZE) { + dev_warn(chip->dev, "not enough space for preload FIFO effect\n"); + goto mmap_failed; + } + + for (i = PAT4_MEM; i >= PAT1_MEM; i--) { + if (chip->mmap.pat_sel_mmap[i].in_use) + continue; + if (length > chip->mmap.pat_sel_mmap[i].max_size) + continue; + } + + if (i < PAT1_MEM) { + dev_warn(chip->dev, "no PAT_MEM source available\n"); + effect->fifo->preload = false; + goto mmap_failed; + } + + /* update the mmap configuration */ + chip->mmap.pat_sel_mmap[i].in_use = true; + chip->mmap.pat_sel_mmap[i].length = length; + chip->mmap.fifo_mmap.length = fifo_length; + + /* Update the effect pattern_src and pat_sel for the preload effect */ + effect->src = PATTERN_MEM; + effect->pat_sel = i; + return 0; + + /* if mmap is failed then the effect couldn't be preloaded */ +mmap_failed: + effect->fifo->preload = false; + return -ENOSPC; +} + +static void haptics_mmap_init(struct haptics_chip *chip) +{ + u32 max_pat_mem_size = MMAP_NUM_BYTES - MMAP_FIFO_MIN_SIZE; + + /* Assume that all memory space is used for FIFO mode by default */ + chip->mmap.fifo_mmap.in_use = true; + chip->mmap.fifo_mmap.length = MMAP_NUM_BYTES; + chip->mmap.fifo_mmap.max_size = MMAP_NUM_BYTES; + + /* different PATx_MEM partition has different maximum size */ + chip->mmap.pat_sel_mmap[PAT1_MEM].max_size = min(max_pat_mem_size, + (u32)MMAP_PAT1_LEN_MASK * MMAP_PAT_LEN_PER_LSB); + chip->mmap.pat_sel_mmap[PAT2_MEM].max_size = min(max_pat_mem_size, + (u32)MMAP_PAT2_LEN_MASK * MMAP_PAT_LEN_PER_LSB); + chip->mmap.pat_sel_mmap[PAT3_MEM].max_size = min(max_pat_mem_size, + (u32)MMAP_PAT3_PAT4_LEN_MASK * MMAP_PAT_LEN_PER_LSB); + chip->mmap.pat_sel_mmap[PAT4_MEM].max_size = min(max_pat_mem_size, + (u32)MMAP_PAT3_PAT4_LEN_MASK * MMAP_PAT_LEN_PER_LSB); +} + +static int haptics_init_fifo_memory(struct haptics_chip *chip) +{ + struct haptics_effect *effect; + int rc, i; + + if (chip->hw_type != HAP525_HV) { + dev_dbg(chip->dev, "HW type %d doesn't support mmap\n", + chip->hw_type); + return 0; + } + + haptics_mmap_init(chip); + + for (i = 0; i < chip->effects_count; i++) { + effect = &chip->effects[i]; + if (!effect->fifo->preload) + continue; + + rc = haptics_mmap_preload_fifo_effect(chip, effect); + if (rc < 0 && rc != -ENOSPC) + return rc; + } + + rc = haptics_mmap_config(chip); + if (rc < 0) + return rc; + + for (i = 0; i < chip->effects_count; i++) { + effect = &chip->effects[i]; + if (!effect->fifo->preload) + continue; + + rc = haptics_config_preload_fifo_effect(chip, effect); + if (rc < 0) + return rc; + } + + return 0; +} + static int haptics_init_preload_pattern_effect(struct haptics_chip *chip) { struct haptics_hw_config *config = &chip->config; @@ -2565,7 +2871,11 @@ static int haptics_hw_init(struct haptics_chip *chip) if (rc < 0) return rc; - return haptics_init_preload_pattern_effect(chip); + rc = haptics_init_preload_pattern_effect(chip); + if (rc < 0) + return rc; + + return haptics_init_fifo_memory(chip); } static irqreturn_t fifo_empty_irq_handler(int irq, void *data) @@ -3504,6 +3814,8 @@ static int haptics_parse_per_effect_dt(struct haptics_chip *chip, } effect->src = FIFO; + effect->fifo->preload = of_property_read_bool(node, + "qcom,wf-fifo-preload"); } effect->brake = devm_kzalloc(chip->dev, From e37bf7e664a06ee6b6872f5f3d2ed02ff0be81d8 Mon Sep 17 00:00:00 2001 From: Fenglin Wu Date: Wed, 22 Dec 2021 12:31:01 +0800 Subject: [PATCH 66/76] input: qcom-hv-haptics: use OL_TLRA if getting CL_TLRA failed If getting close-loop TLRA failed in driver probe, such as if there is no valid value in SDAM register for CAL_TLRA_CL_STS because of the bootloader had not run the frequency calibration successfully. Use open-loop TLRA to program the hardware so the vibrator can still be functional but just lose some efficiency because of unadjusted frequency. Change-Id: I8b8dd0906aa6b3fa882e2044cdbe579c746714a3 Signed-off-by: Fenglin Wu --- drivers/input/misc/qcom-hv-haptics.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/input/misc/qcom-hv-haptics.c b/drivers/input/misc/qcom-hv-haptics.c index 029c856cde07..6ae671a6b73b 100644 --- a/drivers/input/misc/qcom-hv-haptics.c +++ b/drivers/input/misc/qcom-hv-haptics.c @@ -2720,15 +2720,14 @@ static int haptics_init_lra_period_config(struct haptics_chip *chip) return rc; /* get calibrated close loop period */ + t_lra_us = chip->config.t_lra_us; rc = haptics_get_closeloop_lra_period(chip, true); - if (rc < 0) - return rc; + if (!rc && chip->config.cl_t_lra_us != 0) + t_lra_us = chip->config.cl_t_lra_us; + else + dev_warn(chip->dev, "get closeloop LRA period failed, rc=%d\n", rc); /* Config T_LRA */ - t_lra_us = chip->config.t_lra_us; - if (chip->config.cl_t_lra_us != 0) - t_lra_us = chip->config.cl_t_lra_us; - return haptics_config_openloop_lra_period(chip, t_lra_us); } From cd6f6c7ba3605a0192caaeab99a271b917d7164e Mon Sep 17 00:00:00 2001 From: Fenglin Wu Date: Wed, 22 Dec 2021 12:24:26 +0800 Subject: [PATCH 67/76] input: qcom-hv-haptics: try with vmalloc() if allocate custom data failed Reallocate memory using vmalloc() for custom FIFO data if the memory allocation using kcalloc() is failed. This may happen if the vibrator HAL is passing down a vibration effect with a very big chunk of FIFO data and system is running out of memory. Change-Id: I3612208df42e46a44ca0fd43ec9b4c6ca862620e Signed-off-by: Fenglin Wu --- drivers/input/misc/qcom-hv-haptics.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/drivers/input/misc/qcom-hv-haptics.c b/drivers/input/misc/qcom-hv-haptics.c index 6ae671a6b73b..d61ddbb0f797 100644 --- a/drivers/input/misc/qcom-hv-haptics.c +++ b/drivers/input/misc/qcom-hv-haptics.c @@ -2105,11 +2105,14 @@ static int haptics_load_custom_effect(struct haptics_chip *chip, * Before allocating samples buffer, free the old sample * buffer first if it's not been freed. */ - kfree(fifo->samples); + kvfree(fifo->samples); fifo->samples = kcalloc(custom_data.length, sizeof(u8), GFP_KERNEL); if (!fifo->samples) { - rc = -ENOMEM; - goto unlock; + fifo->samples = vmalloc(custom_data.length); + if (!fifo->samples) { + rc = -ENOMEM; + goto unlock; + } } if (copy_from_user(fifo->samples, @@ -2154,7 +2157,7 @@ static int haptics_load_custom_effect(struct haptics_chip *chip, mutex_unlock(&chip->play.lock); return 0; cleanup: - kfree(fifo->samples); + kvfree(fifo->samples); fifo->samples = NULL; unlock: mutex_unlock(&chip->play.lock); @@ -2347,7 +2350,7 @@ static int haptics_stop_fifo_play(struct haptics_chip *chip) return rc; haptics_fifo_empty_irq_config(chip, false); - kfree(chip->custom_effect->fifo->samples); + kvfree(chip->custom_effect->fifo->samples); chip->custom_effect->fifo->samples = NULL; atomic_set(&chip->play.fifo_status.is_busy, 0); From 44ec5b55d0f2d52f431476de9715bdf942514d93 Mon Sep 17 00:00:00 2001 From: Fenglin Wu Date: Mon, 10 Jan 2022 11:05:55 +0800 Subject: [PATCH 68/76] input: qcom-hv-haptics: reset FIFO play logic when hBoost is busy Currently, when waiting for hBoost ready times out, error code -EBUSY is returned. In addition to that, reset the FIFO play logic so that the following FIFO requests can still be served. Meanwhile, add 100us before updating HAPTICS_EN for hBoost to have enough time to handle its power transition as per the hardware recommendation. Change-Id: Iebfc98b589bda747e4c2b6fdf6ff78d2f46ece5d Signed-off-by: Fenglin Wu --- drivers/input/misc/qcom-hv-haptics.c | 100 ++++++++++++++++----------- 1 file changed, 61 insertions(+), 39 deletions(-) diff --git a/drivers/input/misc/qcom-hv-haptics.c b/drivers/input/misc/qcom-hv-haptics.c index d61ddbb0f797..05b9ab328b81 100644 --- a/drivers/input/misc/qcom-hv-haptics.c +++ b/drivers/input/misc/qcom-hv-haptics.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved. - * Copyright (c) 2021 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2022, Qualcomm Innovation Center, Inc. All rights reserved. */ #include @@ -1792,10 +1792,17 @@ static int haptics_toggle_module_enable(struct haptics_chip *chip) { int rc; + /* + * Updating HAPTICS_EN would vote hBoost enable status. Add 100us + * delay before updating HAPTICS_EN for hBoost to have enough time + * to handle its power transition. + */ + usleep_range(100, 101); rc = haptics_module_enable(chip, false); if (rc < 0) return rc; + usleep_range(100, 101); return haptics_module_enable(chip, true); } @@ -2258,6 +2265,46 @@ static u8 get_direct_play_max_amplitude(struct haptics_chip *chip) return (u8)amplitude; } +static int haptics_stop_fifo_play(struct haptics_chip *chip) +{ + int rc; + u8 val; + + if (atomic_read(&chip->play.fifo_status.is_busy) == 0) { + dev_dbg(chip->dev, "FIFO playing is not in progress\n"); + return 0; + } + + rc = haptics_enable_play(chip, false); + if (rc < 0) + return rc; + + /* restore FIFO play rate back to T_LRA */ + rc = haptics_set_fifo_playrate(chip, T_LRA); + if (rc < 0) + return rc; + + haptics_fifo_empty_irq_config(chip, false); + kvfree(chip->custom_effect->fifo->samples); + chip->custom_effect->fifo->samples = NULL; + + atomic_set(&chip->play.fifo_status.is_busy, 0); + + /* + * All other playing modes would use AUTO mode RC + * calibration except FIFO streaming mode, so restore + * back to AUTO RC calibration after FIFO playing. + */ + val = CAL_RC_CLK_AUTO_VAL << CAL_RC_CLK_SHIFT; + rc = haptics_masked_write(chip, chip->cfg_addr_base, + HAP_CFG_CAL_EN_REG, CAL_RC_CLK_MASK, val); + if (rc < 0) + return rc; + + dev_dbg(chip->dev, "stopped FIFO playing successfully\n"); + return 0; +} + static int haptics_upload_effect(struct input_dev *dev, struct ff_effect *effect, struct ff_effect *old) { @@ -2327,46 +2374,21 @@ static int haptics_upload_effect(struct input_dev *dev, return rc; } - return haptics_wait_hboost_ready(chip); -} - -static int haptics_stop_fifo_play(struct haptics_chip *chip) -{ - int rc; - u8 val; - - if (atomic_read(&chip->play.fifo_status.is_busy) == 0) { - dev_dbg(chip->dev, "FIFO playing is not in progress\n"); - return 0; + rc = haptics_wait_hboost_ready(chip); + if (rc < 0 && chip->play.pattern_src == FIFO) { + /* + * Call haptics_stop_fifo_play(chip) explicitly if hBoost is + * not ready for the FIFO play. This drops current FIFO play + * but it restores the SW back to initial status so that the + * following FIFO play requests can still be served. + */ + dev_dbg(chip->dev, "stop FIFO play explicitly to restore SW status\n"); + mutex_lock(&chip->play.lock); + haptics_stop_fifo_play(chip); + mutex_unlock(&chip->play.lock); + return rc; } - rc = haptics_enable_play(chip, false); - if (rc < 0) - return rc; - - /* restore FIFO play rate back to T_LRA */ - rc = haptics_set_fifo_playrate(chip, T_LRA); - if (rc < 0) - return rc; - - haptics_fifo_empty_irq_config(chip, false); - kvfree(chip->custom_effect->fifo->samples); - chip->custom_effect->fifo->samples = NULL; - - atomic_set(&chip->play.fifo_status.is_busy, 0); - - /* - * All other playing modes would use AUTO mode RC - * calibration except FIFO streaming mode, so restore - * back to AUTO RC calibration after FIFO playing. - */ - val = CAL_RC_CLK_AUTO_VAL << CAL_RC_CLK_SHIFT; - rc = haptics_masked_write(chip, chip->cfg_addr_base, - HAP_CFG_CAL_EN_REG, CAL_RC_CLK_MASK, val); - if (rc < 0) - return rc; - - dev_dbg(chip->dev, "stopped FIFO playing successfully\n"); return 0; } From 614c8a90b1ecf07f237032c48adcd670edaf32f9 Mon Sep 17 00:00:00 2001 From: Subbaraman Narayanamurthy Date: Fri, 4 Feb 2022 17:16:08 -0800 Subject: [PATCH 69/76] input: qcom-hv-haptics: Fix haptics_init_fifo_memory() "fifo" is allocated per effect only when "qcom,wf-fifo-data" DT property is specified. Fix haptics_init_fifo_memory() so that it doesn't access that when parsing per effect data structure for configuration. Change-Id: I4f48e5c43c3b289e47d9a9a0934a642172e65555 Signed-off-by: Subbaraman Narayanamurthy --- drivers/input/misc/qcom-hv-haptics.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/input/misc/qcom-hv-haptics.c b/drivers/input/misc/qcom-hv-haptics.c index 05b9ab328b81..2c8d31ecf2f3 100644 --- a/drivers/input/misc/qcom-hv-haptics.c +++ b/drivers/input/misc/qcom-hv-haptics.c @@ -2683,7 +2683,7 @@ static int haptics_init_fifo_memory(struct haptics_chip *chip) for (i = 0; i < chip->effects_count; i++) { effect = &chip->effects[i]; - if (!effect->fifo->preload) + if (!effect->fifo || !effect->fifo->preload) continue; rc = haptics_mmap_preload_fifo_effect(chip, effect); @@ -2697,7 +2697,7 @@ static int haptics_init_fifo_memory(struct haptics_chip *chip) for (i = 0; i < chip->effects_count; i++) { effect = &chip->effects[i]; - if (!effect->fifo->preload) + if (!effect->fifo || !effect->fifo->preload) continue; rc = haptics_config_preload_fifo_effect(chip, effect); From e9a98dc14c629abe35ae92879a87c9b12387b3d5 Mon Sep 17 00:00:00 2001 From: Fenglin Wu Date: Fri, 14 Jan 2022 11:55:39 +0800 Subject: [PATCH 70/76] input: qcom-hv-haptics: update sequence to get status data As per the hardware recommendation, MOD_STATUS_XT need to be programmed before programming MOD_STATUS_SEL to get the correct status data in all cases. Update it. Change-Id: Ie66b12cdc702f32ebfad78a72396ad3ac4d14adf Signed-off-by: Fenglin Wu --- drivers/input/misc/qcom-hv-haptics.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/input/misc/qcom-hv-haptics.c b/drivers/input/misc/qcom-hv-haptics.c index 2c8d31ecf2f3..16a425e5ab9b 100644 --- a/drivers/input/misc/qcom-hv-haptics.c +++ b/drivers/input/misc/qcom-hv-haptics.c @@ -163,6 +163,7 @@ #define FORCE_VREG_RDY_BIT BIT(0) #define HAP_CFG_MOD_STATUS_SEL_REG 0x70 +#define HAP_CFG_MOD_STATUS_XT_REG 0x71 #define HAP_CFG_CAL_EN_REG 0x72 #define CAL_RC_CLK_MASK GENMASK(3, 2) @@ -901,7 +902,12 @@ static int haptics_get_status_data(struct haptics_chip *chip, mod_sel_val[0] = sel & 0xff; mod_sel_val[1] = (sel >> 8) & 0xff; rc = haptics_write(chip, chip->cfg_addr_base, - HAP_CFG_MOD_STATUS_SEL_REG, mod_sel_val, 2); + HAP_CFG_MOD_STATUS_XT_REG, &mod_sel_val[1], 1); + if (rc < 0) + return rc; + + rc = haptics_write(chip, chip->cfg_addr_base, + HAP_CFG_MOD_STATUS_SEL_REG, mod_sel_val, 1); if (rc < 0) return rc; From 156f44c6bd0218865b1362a97cd5c016f63b068b Mon Sep 17 00:00:00 2001 From: Fenglin Wu Date: Mon, 17 Jan 2022 10:17:34 +0800 Subject: [PATCH 71/76] input: qcom-hv-haptics: set Vmax before toggling HAPTICS_EN in FIFO play Currently, HAPTICS_EN bit is toggled whenever a FIFO pattern is played followed by setting Vmax. However, this would issue a SWR_VMAX handshake and then follow a SPMI_VMAX handshake in HW if haptics effect is played over SWR bus in the background. This back to back Vmax handshake would potentially cause the HW not responding and no vibration being played. To address this, set the Vmax before toggling HAPTICS_EN bit to avoid back to back Vmax handshakes when SWR and FIFO are played concurrently. Change-Id: I0b8924ab7f369db446458fe2510a0da01519a978 Signed-off-by: Fenglin Wu --- drivers/input/misc/qcom-hv-haptics.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/drivers/input/misc/qcom-hv-haptics.c b/drivers/input/misc/qcom-hv-haptics.c index 16a425e5ab9b..92d253a1a179 100644 --- a/drivers/input/misc/qcom-hv-haptics.c +++ b/drivers/input/misc/qcom-hv-haptics.c @@ -1943,22 +1943,11 @@ static int haptics_load_predefined_effect(struct haptics_chip *chip, return -EINVAL; play->effect = effect; - if (play->pattern_src == FIFO) { - /* Toggle HAPTICS_EN for a clear start point of FIFO playing */ - rc = haptics_toggle_module_enable(chip); - if (rc < 0) - return rc; - } - /* Clamp VMAX for different vibration strength */ rc = haptics_set_vmax_mv(chip, play->vmax_mv); if (rc < 0) return rc; - rc = haptics_enable_autores(chip, !play->effect->auto_res_disable); - if (rc < 0) - return rc; - play->pattern_src = play->effect->src; if (play->pattern_src == DIRECT_PLAY || play->pattern_src == SWR) { @@ -1967,6 +1956,17 @@ static int haptics_load_predefined_effect(struct haptics_chip *chip, return -EINVAL; } + if (play->pattern_src == FIFO) { + /* Toggle HAPTICS_EN for a clear start point of FIFO playing */ + rc = haptics_toggle_module_enable(chip); + if (rc < 0) + return rc; + } + + rc = haptics_enable_autores(chip, !play->effect->auto_res_disable); + if (rc < 0) + return rc; + play->brake = play->effect->brake; /* Config brake settings if it's necessary */ if (play->brake) { @@ -2149,12 +2149,12 @@ static int haptics_load_custom_effect(struct haptics_chip *chip, play->effect = chip->custom_effect; play->brake = NULL; play->vmax_mv = (magnitude * chip->custom_effect->vmax_mv) / 0x7fff; - /* Toggle HAPTICS_EN for a clear start point of FIFO playing */ - rc = haptics_toggle_module_enable(chip); + rc = haptics_set_vmax_mv(chip, play->vmax_mv); if (rc < 0) goto cleanup; - rc = haptics_set_vmax_mv(chip, play->vmax_mv); + /* Toggle HAPTICS_EN for a clear start point of FIFO playing */ + rc = haptics_toggle_module_enable(chip); if (rc < 0) goto cleanup; From 542ceba17b3d45db3e91d45e91b6db9ceb32d055 Mon Sep 17 00:00:00 2001 From: Fenglin Wu Date: Wed, 26 Jan 2022 09:01:43 +0800 Subject: [PATCH 72/76] input: qcom-hv-haptics: add flags to include fixes only for HAP520 Add TOGGLE_EN_TO_FLUSH_FIFO and RECOVER_SWR_SLAVE flags for the SW fixes only applicable for HAP520 haptics module. Change-Id: I8b5e1ca8e4d734dd4ed8c05edb88fa6642460e83 Signed-off-by: Fenglin Wu --- drivers/input/misc/qcom-hv-haptics.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/drivers/input/misc/qcom-hv-haptics.c b/drivers/input/misc/qcom-hv-haptics.c index 92d253a1a179..8555ff8d3ed5 100644 --- a/drivers/input/misc/qcom-hv-haptics.c +++ b/drivers/input/misc/qcom-hv-haptics.c @@ -381,6 +381,8 @@ enum wa_flags { TOGGLE_CAL_RC_CLK = BIT(0), SW_CTRL_HBST = BIT(1), SLEEP_CLK_32K_SCALE = BIT(2), + TOGGLE_EN_TO_FLUSH_FIFO = BIT(3), + RECOVER_SWR_SLAVE = BIT(4), }; static const char * const src_str[] = { @@ -1956,7 +1958,7 @@ static int haptics_load_predefined_effect(struct haptics_chip *chip, return -EINVAL; } - if (play->pattern_src == FIFO) { + if ((play->pattern_src == FIFO) && (chip->wa_flags & TOGGLE_EN_TO_FLUSH_FIFO)) { /* Toggle HAPTICS_EN for a clear start point of FIFO playing */ rc = haptics_toggle_module_enable(chip); if (rc < 0) @@ -2154,9 +2156,11 @@ static int haptics_load_custom_effect(struct haptics_chip *chip, goto cleanup; /* Toggle HAPTICS_EN for a clear start point of FIFO playing */ - rc = haptics_toggle_module_enable(chip); - if (rc < 0) - goto cleanup; + if (chip->wa_flags & TOGGLE_EN_TO_FLUSH_FIFO) { + rc = haptics_toggle_module_enable(chip); + if (rc < 0) + goto cleanup; + } rc = haptics_enable_autores(chip, !play->effect->auto_res_disable); if (rc < 0) @@ -2856,7 +2860,8 @@ static int haptics_config_wa(struct haptics_chip *chip) { switch (chip->hw_type) { case HAP520: - chip->wa_flags |= TOGGLE_CAL_RC_CLK | SW_CTRL_HBST | SLEEP_CLK_32K_SCALE; + chip->wa_flags |= TOGGLE_CAL_RC_CLK | SW_CTRL_HBST | SLEEP_CLK_32K_SCALE | + TOGGLE_EN_TO_FLUSH_FIFO | RECOVER_SWR_SLAVE; break; case HAP520_MV: case HAP525_HV: @@ -4263,6 +4268,9 @@ static int swr_slave_reg_enable(struct regulator_dev *rdev) return rc; } + if (!(chip->wa_flags & RECOVER_SWR_SLAVE)) + goto done; + /* * If haptics has already been in SWR mode when enabling the SWR * slave, it means that the haptics module was stuck in prevous @@ -4290,7 +4298,7 @@ static int swr_slave_reg_enable(struct regulator_dev *rdev) return rc; } } - +done: chip->swr_slave_enabled = true; return 0; } From d502b78e6be8f4a9dd0c32bb7669907ad28f15d9 Mon Sep 17 00:00:00 2001 From: Fenglin Wu Date: Wed, 9 Feb 2022 15:42:41 +0800 Subject: [PATCH 73/76] input: qcom-hv-haptics: update mmap logic for preload FIFO effect Correct the logic of finding an available PAT_MEMx source to store preload FIFO effect. Also add a debug print for preload property when dumping FIFO effect settings. Change-Id: If328e8bdb32f28495ac893a5a31f2be48fedd2b9 Signed-off-by: Fenglin Wu --- drivers/input/misc/qcom-hv-haptics.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/drivers/input/misc/qcom-hv-haptics.c b/drivers/input/misc/qcom-hv-haptics.c index 8555ff8d3ed5..08e7349cff83 100644 --- a/drivers/input/misc/qcom-hv-haptics.c +++ b/drivers/input/misc/qcom-hv-haptics.c @@ -728,9 +728,9 @@ static void __dump_effects(struct haptics_chip *chip) dev_dbg(chip->dev, "%s\n", str); kfree(str); - dev_dbg(chip->dev, "FIFO data play rate: %s\n", - period_str[effect->fifo->period_per_s]); - dev_dbg(chip->dev, "FIFO data play length: %dus\n", + dev_dbg(chip->dev, "FIFO data preload: %d, play rate: %s, play length: %uus\n", + effect->fifo->preload, + period_str[effect->fifo->period_per_s], effect->fifo->play_length_us); } @@ -2630,10 +2630,9 @@ static int haptics_mmap_preload_fifo_effect(struct haptics_chip *chip, } for (i = PAT4_MEM; i >= PAT1_MEM; i--) { - if (chip->mmap.pat_sel_mmap[i].in_use) - continue; - if (length > chip->mmap.pat_sel_mmap[i].max_size) - continue; + if (!chip->mmap.pat_sel_mmap[i].in_use && + (length <= chip->mmap.pat_sel_mmap[i].max_size)) + break; } if (i < PAT1_MEM) { From a1bb4c1670f2d06cf8bad7cd4c8ea322e2f35c05 Mon Sep 17 00:00:00 2001 From: Fenglin Wu Date: Tue, 15 Feb 2022 19:14:11 +0800 Subject: [PATCH 74/76] input: qcom-hv-haptics: set play rate for PATx_MEM mode PATx_MEM mode has a different play rate setting with FIFO mode and all PATx_MEM sources share the same play rate. Set the play rate when configuring the mmap configuration for preload FIFO effect. Change-Id: I1c0dbf80977b864264e4580d476cf25b4202f757 Signed-off-by: Fenglin Wu --- drivers/input/misc/qcom-hv-haptics.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/drivers/input/misc/qcom-hv-haptics.c b/drivers/input/misc/qcom-hv-haptics.c index 08e7349cff83..834a02731dba 100644 --- a/drivers/input/misc/qcom-hv-haptics.c +++ b/drivers/input/misc/qcom-hv-haptics.c @@ -202,6 +202,7 @@ #define HAP_PTN_FIFO_DIN_NUM 4 #define HAP_PTN_FIFO_PLAY_RATE_REG 0x24 +#define PAT_MEM_PLAY_RATE_MASK GENMASK(7, 4) #define FIFO_PLAY_RATE_MASK GENMASK(3, 0) #define HAP_PTN_FIFO_EMPTY_CFG_REG 0x2A @@ -470,6 +471,7 @@ struct mmap_partition { struct haptics_mmap { struct mmap_partition fifo_mmap; struct mmap_partition pat_sel_mmap[PAT_MEM_MAX]; + enum s_period pat_play_rate; }; /** @@ -2599,6 +2601,15 @@ static int haptics_mmap_config(struct haptics_chip *chip) if (rc < 0) return rc; + /* config PATx_PLAY_RATE */ + rc = haptics_masked_write(chip, chip->ptn_addr_base, + HAP_PTN_FIFO_PLAY_RATE_REG, PAT_MEM_PLAY_RATE_MASK, + FIELD_PREP(PAT_MEM_PLAY_RATE_MASK, chip->mmap.pat_play_rate)); + if (rc < 0) { + dev_err(chip->dev, "Set pat_mem play rate failed, rc=%d\n", rc); + return rc; + } + dev_dbg(chip->dev, "haptics memory map configured with FIFO: %d, PAT1:%d, PAT2: %d, PAT3: %d, PAT4: %d\n", chip->mmap.fifo_mmap.length, chip->mmap.pat_sel_mmap[PAT1_MEM].length, @@ -2619,6 +2630,13 @@ static int haptics_mmap_preload_fifo_effect(struct haptics_chip *chip, return -EINVAL; } + if (chip->mmap.pat_play_rate == F_RESERVED) { + chip->mmap.pat_play_rate = effect->fifo->period_per_s; + } else if (chip->mmap.pat_play_rate != effect->fifo->period_per_s) { + dev_warn(chip->dev, "PATx_MEM sources only support one play rate\n"); + goto mmap_failed; + } + length = effect->fifo->num_s; if (length % MMAP_PAT_LEN_PER_LSB) length = (length / MMAP_PAT_LEN_PER_LSB + 1) * MMAP_PAT_LEN_PER_LSB; @@ -2675,6 +2693,7 @@ static void haptics_mmap_init(struct haptics_chip *chip) (u32)MMAP_PAT3_PAT4_LEN_MASK * MMAP_PAT_LEN_PER_LSB); chip->mmap.pat_sel_mmap[PAT4_MEM].max_size = min(max_pat_mem_size, (u32)MMAP_PAT3_PAT4_LEN_MASK * MMAP_PAT_LEN_PER_LSB); + chip->mmap.pat_play_rate = F_RESERVED; } static int haptics_init_fifo_memory(struct haptics_chip *chip) From e8ed28a66ed3477e378483599bf66691c8c4e6f8 Mon Sep 17 00:00:00 2001 From: Fenglin Wu Date: Mon, 6 Dec 2021 15:08:26 +0800 Subject: [PATCH 75/76] input: qcom-hv-haptics: Set FORCE_VSET_ACK in open-loop mode Based on hardware recommendation, set FORCE_VSET_ACK when HAP525_HV module is working in open-loop mode. Change-Id: I7f9bb45bc25b0923743fad0c58410523e278d1e7 Signed-off-by: Fenglin Wu --- drivers/input/misc/qcom-hv-haptics.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/drivers/input/misc/qcom-hv-haptics.c b/drivers/input/misc/qcom-hv-haptics.c index 834a02731dba..c7785beb6bf6 100644 --- a/drivers/input/misc/qcom-hv-haptics.c +++ b/drivers/input/misc/qcom-hv-haptics.c @@ -160,6 +160,7 @@ #define VMAX_HDRM_MAX_MV 6350 #define HAP_CFG_VSET_CFG_REG 0x68 +#define FORCE_VSET_ACK_BIT BIT(1) /* This is only for HAP525_HV */ #define FORCE_VREG_RDY_BIT BIT(0) #define HAP_CFG_MOD_STATUS_SEL_REG 0x70 @@ -1376,14 +1377,18 @@ static int haptics_enable_hpwr_vreg(struct haptics_chip *chip, bool en) static int haptics_open_loop_drive_config(struct haptics_chip *chip, bool en) { int rc = 0; - u8 val; + u8 mask, val; + + mask = FORCE_VREG_RDY_BIT; + if (chip->hw_type == HAP525_HV) + mask |= FORCE_VSET_ACK_BIT; if ((is_boost_vreg_enabled_in_open_loop(chip) || is_haptics_external_powered(chip)) && en) { /* Force VREG_RDY */ + val = mask; rc = haptics_masked_write(chip, chip->cfg_addr_base, - HAP_CFG_VSET_CFG_REG, FORCE_VREG_RDY_BIT, - FORCE_VREG_RDY_BIT); + HAP_CFG_VSET_CFG_REG, mask, val); if (rc < 0) return rc; @@ -1414,8 +1419,7 @@ static int haptics_open_loop_drive_config(struct haptics_chip *chip, bool en) } } else if (!is_haptics_external_powered(chip)) { rc = haptics_masked_write(chip, chip->cfg_addr_base, - HAP_CFG_VSET_CFG_REG, - FORCE_VREG_RDY_BIT, 0); + HAP_CFG_VSET_CFG_REG, mask, 0); } return rc; From ede62fd6fb0bd9f614c90fe840d65882c2cb7ca9 Mon Sep 17 00:00:00 2001 From: Fenglin Wu Date: Wed, 16 Feb 2022 18:58:22 +0800 Subject: [PATCH 76/76] input: qcom-hv-haptics: add high-voltage check for HAP525_HV module HAP525_HV module Vmax setting follows the HV_HAP_DRIVER defined in HW_CONFIG, so add the check to make sure the Vmax can be set correctly. Change-Id: I116e068c774dea47648f345cc3cf89a98f2eee52 Signed-off-by: Fenglin Wu --- drivers/input/misc/qcom-hv-haptics.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/misc/qcom-hv-haptics.c b/drivers/input/misc/qcom-hv-haptics.c index c7785beb6bf6..483d03512523 100644 --- a/drivers/input/misc/qcom-hv-haptics.c +++ b/drivers/input/misc/qcom-hv-haptics.c @@ -2863,7 +2863,7 @@ static int haptics_init_vmax_config(struct haptics_chip *chip) chip->is_hv_haptics = true; chip->max_vmax_mv = MAX_VMAX_MV; - if (chip->hw_type == HAP520_MV) { + if (chip->hw_type == HAP520_MV || chip->hw_type == HAP525_HV) { rc = haptics_read(chip, chip->cfg_addr_base, HAP_CFG_HW_CONFIG_REG, &val, 1); if (rc < 0)