From ef978277038b37517878db7eb3fc0d335def3caf Mon Sep 17 00:00:00 2001 From: Kiran Gunda Date: Mon, 12 Jul 2021 14:58:08 +0530 Subject: [PATCH 1/4] input: qcom-hv-haptics: Enable boot-up F0 calibration for PM5100 In the boot up LRA frequency calibration sequence, CAL_TLRA_CL_STS is read out and stored in SDAM module registers by the bootloader. Later, the HLOS haptics driver can read it from there and use it for calculating LRA frequency. Enable the same for PM5100 PMIC. Change-Id: Ia5e307e5d536a42b29ab05e9888640121188312c Signed-off-by: Kiran Gunda --- 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 f1ca4a207eef..9f7653f41cc5 100644 --- a/drivers/input/misc/qcom-hv-haptics.c +++ b/drivers/input/misc/qcom-hv-haptics.c @@ -1042,7 +1042,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->hw_type == HAP520)) { + if (in_boot && ((chip->hw_type == HAP520) || (chip->hw_type == HAP520_MV))) { if (chip->hap_cfg_nvmem == NULL) { dev_dbg(chip->dev, "nvmem device for hap_cfg is not defined\n"); return -EINVAL; From f6c37c09587a231bf0c1bf3bde0ba22487c99174 Mon Sep 17 00:00:00 2001 From: Kiran Gunda Date: Mon, 21 Jun 2021 17:30:24 +0530 Subject: [PATCH 2/4] input: qcom-hv-haptics: Add support for DeepSleep/Hibernate Free/request the fifo empty interrupt during the DeepSleep and hibernate entry or exit respectively. Also restore the hardware initial configuration done during the boot. Change-Id: I6b0d4838a6d1c48869ca760d4e93788440983b3c Signed-off-by: Kiran Gunda --- drivers/input/misc/qcom-hv-haptics.c | 99 +++++++++++++++++++++++++++- 1 file changed, 97 insertions(+), 2 deletions(-) diff --git a/drivers/input/misc/qcom-hv-haptics.c b/drivers/input/misc/qcom-hv-haptics.c index 9f7653f41cc5..c60185c0aa55 100644 --- a/drivers/input/misc/qcom-hv-haptics.c +++ b/drivers/input/misc/qcom-hv-haptics.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -5356,8 +5357,44 @@ static int haptics_remove(struct platform_device *pdev) return 0; } +static void haptics_ds_suspend_config(struct device *dev) +{ + struct haptics_chip *chip = dev_get_drvdata(dev); + + if (chip->fifo_empty_irq > 0) + devm_free_irq(dev, chip->fifo_empty_irq, chip); +} + +static int haptics_ds_resume_config(struct device *dev) +{ + struct haptics_chip *chip = dev_get_drvdata(dev); + int rc = 0; + + rc = haptics_hw_init(chip); + if (rc < 0) { + dev_err(chip->dev, "Initialize HW failed, rc = %d\n", rc); + return rc; + } + + if (chip->fifo_empty_irq > 0) { + 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; + } + + return rc; +} + #ifdef CONFIG_PM_SLEEP -static int haptics_suspend(struct device *dev) +static int haptics_suspend_config(struct device *dev) { struct haptics_chip *chip = dev_get_drvdata(dev); struct haptics_play_info *play = &chip->play; @@ -5402,16 +5439,74 @@ static int haptics_suspend(struct device *dev) return haptics_module_enable(chip, false); } +static int haptics_suspend(struct device *dev) +{ + struct haptics_chip *chip = dev_get_drvdata(dev); + int rc = 0; + + if (chip->cfg_revision == HAP_CFG_V1) + return 0; + + rc = haptics_suspend_config(dev); + if (rc < 0) + return rc; + +#ifdef CONFIG_DEEPSLEEP + if (mem_sleep_current == PM_SUSPEND_MEM) + haptics_ds_suspend_config(dev); +#endif + + return 0; +} + static int haptics_resume(struct device *dev) { struct haptics_chip *chip = dev_get_drvdata(dev); +#ifdef CONFIG_DEEPSLEEP + if (mem_sleep_current == PM_SUSPEND_MEM) { + int rc = 0; + + rc = haptics_ds_resume_config(dev); + if (rc < 0) + return rc; + } +#endif + return haptics_module_enable(chip, true); } #endif +static int haptics_freeze(struct device *dev) +{ + int rc = 0; + + rc = haptics_suspend_config(dev); + if (rc < 0) + return rc; + + haptics_ds_suspend_config(dev); + + return 0; +} + +static int haptics_restore(struct device *dev) +{ + struct haptics_chip *chip = dev_get_drvdata(dev); + int rc = 0; + + rc = haptics_ds_resume_config(dev); + if (rc < 0) + return rc; + + return haptics_module_enable(chip, true); +} + static const struct dev_pm_ops haptics_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(haptics_suspend, haptics_resume) + .suspend = haptics_suspend, + .resume = haptics_resume, + .freeze = haptics_freeze, + .restore = haptics_restore, }; static const struct of_device_id haptics_match_table[] = { From b17d684b3bbb0dad3cc536b6e0d4295273508134 Mon Sep 17 00:00:00 2001 From: Hui Liu Date: Mon, 11 Apr 2022 18:32:55 +0800 Subject: [PATCH 3/4] input: qcom-hv-haptics: add support for primitive effect Primitive effect is very similar to predefined effect which has been supported and each primitive effect has an unique id defined in "qcom,primitive-id" devicetree property. Add support for it. Change-Id: I4a03dd09e97f1b7988d4ab87e1cf78f075b05101 Signed-off-by: Hui Liu --- drivers/input/misc/qcom-hv-haptics.c | 235 ++++++++++++++++++++++----- 1 file changed, 191 insertions(+), 44 deletions(-) diff --git a/drivers/input/misc/qcom-hv-haptics.c b/drivers/input/misc/qcom-hv-haptics.c index c60185c0aa55..cca0c26f8eae 100644 --- a/drivers/input/misc/qcom-hv-haptics.c +++ b/drivers/input/misc/qcom-hv-haptics.c @@ -579,6 +579,7 @@ struct haptics_chip { struct input_dev *input_dev; struct haptics_hw_config config; struct haptics_effect *effects; + struct haptics_effect *primitives; struct haptics_effect *custom_effect; struct haptics_play_info play; struct haptics_mmap mmap; @@ -594,11 +595,13 @@ struct haptics_chip { int fifo_empty_irq; u32 hpwr_voltage_mv; u32 effects_count; + u32 primitives_count; u32 cfg_addr_base; u32 ptn_addr_base; u32 hbst_addr_base; u32 clamped_vmax_mv; u32 wa_flags; + u32 primitive_duration; u8 cfg_revision; u8 ptn_revision; u8 hpwr_intf_ctl; @@ -734,7 +737,8 @@ static int haptics_masked_write(struct haptics_chip *chip, return rc; } -static void __dump_effects(struct haptics_chip *chip) +static void __dump_effects(struct haptics_chip *chip, + struct haptics_effect *effects, int effects_count) { struct haptics_effect *effect; struct pattern_s *sample; @@ -742,8 +746,8 @@ static void __dump_effects(struct haptics_chip *chip) u32 size, pos; int i, j; - for (i = 0; i < chip->effects_count; i++) { - effect = &chip->effects[i]; + for (i = 0; i < effects_count; i++) { + effect = &effects[i]; if (!effect) return; @@ -2341,13 +2345,12 @@ static int haptics_load_custom_effect(struct haptics_chip *chip, return rc; } -static u32 get_play_length_us(struct haptics_play_info *play) +static u32 get_play_length_effect_us(struct haptics_effect *effect) { - struct haptics_effect *effect = play->effect; u32 length_us = 0; - if (play->brake) - length_us = play->brake->play_length_us; + if (effect->brake) + length_us = effect->brake->play_length_us; if ((effect->src == PATTERN1 || effect->src == PATTERN2) && effect->pattern) @@ -2358,29 +2361,58 @@ static u32 get_play_length_us(struct haptics_play_info *play) return length_us; } +static inline u32 get_play_length_us(struct haptics_play_info *play) +{ + return get_play_length_effect_us(play->effect); +} + +#define PRIMITIVE_EFFECT_ID_BIT BIT(15) +#define PRIMITIVE_EFFECT_ID_MASK GENMASK(14, 0) 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 }; + struct haptics_effect *effects = NULL; + s16 custom_id = 0; + int effects_count = 0; int rc, i; - - if (chip->effects_count == 0) - return -EINVAL; + bool primitive; 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]) + primitive = !!(custom_data[CUSTOM_DATA_EFFECT_IDX] & PRIMITIVE_EFFECT_ID_BIT); + + if (primitive) { + if (chip->primitives_count == 0) + return -EINVAL; + + custom_id = custom_data[CUSTOM_DATA_EFFECT_IDX] & PRIMITIVE_EFFECT_ID_MASK; + effects = chip->primitives; + effects_count = chip->primitives_count; + } else { + if (chip->effects_count == 0) + return -EINVAL; + + custom_id = custom_data[CUSTOM_DATA_EFFECT_IDX]; + effects = chip->effects; + effects_count = chip->effects_count; + } + + for (i = 0; i < effects_count; i++) + if (effects[i].id == custom_id) break; - if (i == chip->effects_count) { + if (i == effects_count) { dev_err(chip->dev, "effect%d is not supported!\n", custom_data[CUSTOM_DATA_EFFECT_IDX]); return -EINVAL; } + dev_dbg(chip->dev, "upload %s effect %d, vmax=%d\n", primitive ? "primitive" : "predefined", + effects[i].id, play->vmax_mv); + mutex_lock(&chip->play.lock); if (chip->play.in_calibration) { dev_err(chip->dev, "calibration in progress, ignore playing predefined effect\n"); @@ -2388,12 +2420,11 @@ static int haptics_load_periodic_effect(struct haptics_chip *chip, goto unlock; } - 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, &chip->effects[i]); + play->vmax_mv = (magnitude * effects[i].vmax_mv) / 0x7fff; + rc = haptics_load_predefined_effect(chip, &effects[i]); if (rc < 0) { dev_err(chip->dev, "Play predefined effect%d failed, rc=%d\n", - chip->effects[i].id, rc); + effects[i].id, rc); goto unlock; } mutex_unlock(&chip->play.lock); @@ -3804,12 +3835,37 @@ static int haptics_add_effects_debugfs(struct haptics_effect *effect, return 0; } -#define EFFECT_NAME_SIZE 12 +#define EFFECT_NAME_SIZE 15 +static int haptics_add_debugfs(struct dentry *hap_dir, struct haptics_effect *effects, + int count, char *effect_name) +{ + struct dentry *effect_dir; + char str[EFFECT_NAME_SIZE] = {0}; + int rc = 0; + int i = 0; + + for (; i < count; i++) { + scnprintf(str, ARRAY_SIZE(str), "%s%d", effect_name, effects[i].id); + effect_dir = debugfs_create_dir(str, hap_dir); + if (IS_ERR(effect_dir)) { + rc = PTR_ERR(effect_dir); + pr_err("create %s debugfs directory failed, rc=%d\n", str, rc); + return rc; + } + rc = haptics_add_effects_debugfs(&effects[i], effect_dir); + if (rc < 0) { + pr_err("create debugfs nodes for %s failed, rc=%d\n", str, rc); + return rc; + } + } + + return rc; +} + 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; + struct dentry *hap_dir, *file; + int rc = 0; hap_dir = debugfs_create_dir("haptics", NULL); if (IS_ERR(hap_dir)) { @@ -3819,24 +3875,13 @@ static int haptics_create_debugfs(struct haptics_chip *chip) 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 (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_debugfs(hap_dir, chip->effects, chip->effects_count, "effect"); + if (rc < 0) + goto exit; - rc = haptics_add_effects_debugfs(&chip->effects[i], effect_dir); - if (rc < 0) { - dev_err(chip->dev, "create debugfs nodes for %s failed, rc=%d\n", - str, rc); - goto exit; - } - } + rc = haptics_add_debugfs(hap_dir, chip->primitives, chip->primitives_count, "primitive"); + if (rc < 0) + goto exit; file = debugfs_create_file_unsafe("preload_effect_idx", 0644, hap_dir, chip, &preload_effect_idx_dbgfs_ops); @@ -4139,6 +4184,54 @@ static int haptics_parse_per_effect_dt(struct haptics_chip *chip, return 0; } +static int haptics_parse_primitives_dt(struct haptics_chip *chip) +{ + struct device_node *node = chip->dev->of_node; + struct device_node *child; + int rc, i = 0, num = 0; + + for_each_available_child_of_node(node, child) { + if (of_find_property(child, "qcom,primitive-id", NULL)) + num++; + } + if (!num) + return 0; + + chip->primitives = devm_kcalloc(chip->dev, num, + sizeof(*chip->effects), GFP_KERNEL); + if (!chip->primitives) + return -ENOMEM; + + for_each_available_child_of_node(node, child) { + if (!of_find_property(child, "qcom,primitive-id", NULL)) + continue; + + rc = of_property_read_u32(child, "qcom,primitive-id", &(chip->primitives[i].id)); + if (rc < 0) { + dev_err(chip->dev, "Read qcom,primitive-id failed, rc=%d\n", + rc); + of_node_put(child); + return rc; + } + + rc = haptics_parse_per_effect_dt(chip, child, + &chip->primitives[i]); + if (rc < 0) { + dev_err(chip->dev, "parse primitive %d failed, rc=%d\n", + i); + of_node_put(child); + return rc; + } + i++; + } + + chip->primitives_count = i; + dev_dbg(chip->dev, "Dump primitive effect settings as following\n"); + __dump_effects(chip, chip->primitives, chip->primitives_count); + + return 0; +} + static int haptics_parse_effects_dt(struct haptics_chip *chip) { struct device_node *node = chip->dev->of_node; @@ -4161,6 +4254,14 @@ static int haptics_parse_effects_dt(struct haptics_chip *chip) if (!of_find_property(child, "qcom,effect-id", NULL)) continue; + rc = of_property_read_u32(child, "qcom,effect-id", &(chip->effects[i].id)); + if (rc < 0) { + dev_err(chip->dev, "Read qcom,effect-id failed, rc=%d\n", + rc); + of_node_put(child); + return rc; + } + rc = haptics_parse_per_effect_dt(chip, child, &chip->effects[i]); if (rc < 0) { @@ -4173,7 +4274,8 @@ static int haptics_parse_effects_dt(struct haptics_chip *chip) } chip->effects_count = i; - __dump_effects(chip); + dev_dbg(chip->dev, "Dump predefined effect settings as following\n"); + __dump_effects(chip, chip->effects, chip->effects_count); return 0; } @@ -4471,6 +4573,13 @@ static int haptics_parse_dt(struct haptics_chip *chip) goto free_pbs; } + rc = haptics_parse_primitives_dt(chip); + if (rc < 0) { + dev_err(chip->dev, "Parse device-tree for primitives failed, rc=%d\n", + rc); + goto free_pbs; + } + return 0; free_pbs: if (chip->pbs_node) { @@ -5154,10 +5263,48 @@ static ssize_t lra_impedance_show(struct class *c, } static CLASS_ATTR_RO(lra_impedance); +static ssize_t primitive_duration_show(struct class *c, + struct class_attribute *attr, char *buf) +{ + struct haptics_chip *chip = container_of(c, + struct haptics_chip, hap_class); + + return scnprintf(buf, PAGE_SIZE, "%d\n", chip->primitive_duration); +} + +static ssize_t primitive_duration_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); + u16 primitive_id = 0; + int i = 0; + + if (kstrtou16(buf, 0, &primitive_id)) + return -EINVAL; + + for (i = 0; i < chip->primitives_count; i++) { + if (chip->primitives[i].id == primitive_id) + break; + } + + if (i == chip->primitives_count) { + pr_err("Primitive id specified is incorrect\n"); + return -EINVAL; + } + + chip->primitive_duration = get_play_length_effect_us(&chip->primitives[i]); + + return count; +} + +static CLASS_ATTR_RW(primitive_duration); + static struct attribute *hap_class_attrs[] = { &class_attr_lra_calibration.attr, &class_attr_lra_frequency_hz.attr, &class_attr_lra_impedance.attr, + &class_attr_primitive_duration.attr, NULL, }; ATTRIBUTE_GROUPS(hap_class); @@ -5288,15 +5435,15 @@ static int haptics_probe(struct platform_device *pdev) input_set_capability(input_dev, EV_FF, FF_CONSTANT); input_set_capability(input_dev, EV_FF, FF_GAIN); - if (chip->effects_count != 0) { + if ((chip->effects_count != 0) || (chip->primitives_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; + if (chip->effects_count + chip->primitives_count > MAX_EFFECT_COUNT) + dev_err(chip->dev, "Effects count cannot be more than %d\n", MAX_EFFECT_COUNT); + + count = min_t(u32, chip->effects_count + chip->primitives_count + 1, MAX_EFFECT_COUNT); rc = input_ff_create(input_dev, count); if (rc < 0) { From 8928f602e07919be72c9bd6901dac44fd78623c4 Mon Sep 17 00:00:00 2001 From: Shyam Kumar Thella Date: Wed, 18 Aug 2021 10:17:42 +0530 Subject: [PATCH 4/4] input: qcom-hv-haptics: Toggle EN_CTL for PM5100 v1.0 Toggle Haptics EN_CTL as workaround for HW issue in PM5100 v1.0. Change-Id: I83e82f261d4185132bb5a721600950f90f71db3f Signed-off-by: Shyam Kumar Thella --- drivers/input/misc/qcom-hv-haptics.c | 68 ++++++++++++++-------------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/drivers/input/misc/qcom-hv-haptics.c b/drivers/input/misc/qcom-hv-haptics.c index cca0c26f8eae..c73c25c35cc6 100644 --- a/drivers/input/misc/qcom-hv-haptics.c +++ b/drivers/input/misc/qcom-hv-haptics.c @@ -1540,6 +1540,40 @@ static int haptics_open_loop_drive_config(struct haptics_chip *chip, bool en) return 0; } +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; + + /* + * 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); +} + #define BOOST_VREG_OFF_DELAY_SECONDS 2 static int haptics_enable_play(struct haptics_chip *chip, bool en) { @@ -1938,40 +1972,6 @@ static void haptics_fifo_empty_irq_config(struct haptics_chip *chip, } } -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; - - /* - * 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); -} - static int haptics_set_fifo(struct haptics_chip *chip, struct fifo_cfg *fifo) { struct fifo_play_status *status = &chip->play.fifo_status;