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