Re: [PATCH v3 13/23] input: add max77705 haptic driver

2024-06-20 Thread Krzysztof Kozlowski
On 18/06/2024 15:59, Dzmitry Sankouski wrote:
> Add support for haptic controller on MAX77705 Multifunction
> device.
> 
> This driver supports external pwm and LRA (Linear Resonant Actuator) motor.
> User can control the haptic device via force feedback framework.
> 
> Signed-off-by: Dzmitry Sankouski 
> ---

> +static int max77705_haptic_bias(struct max77705_haptic *haptic, bool on)
> +{
> + int error;
> +
> + error = regmap_update_bits(haptic->regmap_haptic,
> +
> MAX77705_PMIC_REG_MAINCTRL1,
> +
> MAX77705_MAINCTRL1_BIASEN_MASK,
> +on << 
> MAX77705_MAINCTRL1_BIASEN_SHIFT);
> +
> + if (error) {
> + dev_err(haptic->dev, "failed to %s bias: %d\n",
> + on ? "enable" : "disable", error);
> + return error;
> + }
> +
> + return 0;
> +}
> +
> +static int max77705_haptic_configure(struct max77705_haptic *haptic,
> +  bool enable)
> +{
> + unsigned int value, config_reg;
> + int error;
> +
> + value = ((haptic->type << MAX77705_CONFIG2_MODE_SHIFT) |
> + (enable << MAX77705_CONFIG2_MEN_SHIFT) |
> + (haptic->mode << MAX77705_CONFIG2_HTYP_SHIFT) |
> + MAX77705_HAPTIC_PWM_DIVISOR_128);
> + config_reg = MAX77705_PMIC_REG_MCONFIG;
> +
> + error = regmap_write(haptic->regmap_haptic,
> +  config_reg, value);
> + if (error) {
> + dev_err(haptic->dev,
> + "failed to update haptic config: %d\n", error);
> + return error;
> + }
> +
> + return 0;
> +}
> +
> +static void max77705_haptic_enable(struct max77705_haptic *haptic)
> +{
> + int error;
> +
> + if (haptic->enabled)
> + return;
> +
> + error = pwm_enable(haptic->pwm_dev);
> + if (error) {
> + dev_err(haptic->dev,
> + "failed to enable haptic pwm device: %d\n", error);
> + return;
> + }
> +
> + error = max77705_haptic_configure(haptic, true);
> + if (error)
> + goto err_enable_config;
> +
> + haptic->enabled = true;
> +
> + return;
> +
> +err_enable_config:
> + pwm_disable(haptic->pwm_dev);
> +}
> +
> +static void max77705_haptic_disable(struct max77705_haptic *haptic)
> +{
> + int error;
> +
> + if (!haptic->enabled)
> + return;
> +
> + error = max77705_haptic_configure(haptic, false);
> + if (error)
> + return;
> +
> + pwm_disable(haptic->pwm_dev);
> + haptic->enabled = false;
> +}
> +
> +static void max77705_haptic_play_work(struct work_struct *work)
> +{
> + struct max77705_haptic *haptic =
> + container_of(work, struct max77705_haptic, work);
> + int error;
> +
> + error = max77705_haptic_set_duty_cycle(haptic);
> + if (error) {
> + dev_err(haptic->dev, "failed to set duty cycle: %d\n", error);
> + return;
> + }
> +
> + if (haptic->magnitude)
> + max77705_haptic_enable(haptic);
> + else
> + max77705_haptic_disable(haptic);
> +}
> +
> +static int max77705_haptic_play_effect(struct input_dev *dev, void *data,
> +struct ff_effect *effect)
> +{
> + struct max77705_haptic *haptic = input_get_drvdata(dev);
> + struct pwm_args pargs;
> + u64 period_mag_multi;
> +
> + haptic->magnitude = effect->u.rumble.strong_magnitude;
> + if (!haptic->magnitude)
> + haptic->magnitude = effect->u.rumble.weak_magnitude;
> +
> + /*
> +  * The magnitude comes from force-feedback interface.
> +  * The formula to convert magnitude to pwm_duty as follows:
> +  * - pwm_duty = (magnitude * pwm_period) / MAX_MAGNITUDE(0x)
> +  */
> + pr_info("magnitude: %d(%x)", haptic->magnitude, haptic->magnitude);

Do not use pr_xxx in your drivers. That's a generic comment so please
apply it everywhere. Anyway driver should be silent.


> + pwm_get_args(haptic->pwm_dev, &pargs);
> + period_mag_multi = (u64)pargs.period * haptic->magnitude;
> + haptic->pwm_duty = (unsigned int)(period_mag_multi >>
> + MAX_MAGNITUDE_SHIFT);
> +
> + schedule_work(&haptic->work);
> +
> + return 0;
> +}


> +
> +static DEFINE_SIMPLE_DEV_PM_OPS(max77705_haptic_pm_ops,
> + max77705_haptic_suspend,
> + max77705_haptic_resume);
> +
> +static const struct of_device_id of_max77705_haptic_dt_match[] = {
> + { .compatible = "maxim,max77705-haptic", },
> + { /* sentinel */ },
> +};
> +MODULE_DEVICE_TABLE(of, of_max77705_haptic_dt_match);
> +
> +static struct platform_driver max77705_haptic_driver = {
> + .driver = {
> + .name   = "max77705-haptic",
> +   

[PATCH v3 13/23] input: add max77705 haptic driver

2024-06-18 Thread Dzmitry Sankouski
Add support for haptic controller on MAX77705 Multifunction
device.

This driver supports external pwm and LRA (Linear Resonant Actuator) motor.
User can control the haptic device via force feedback framework.

Signed-off-by: Dzmitry Sankouski 
---
 drivers/input/misc/Kconfig   |  11 +
 drivers/input/misc/Makefile  |   1 +
 drivers/input/misc/max77705-haptic.c | 378 +++
 3 files changed, 390 insertions(+)

diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index 6ba984d7f0b1..26f7b25a0b42 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -233,6 +233,17 @@ config INPUT_MAX77693_HAPTIC
  To compile this driver as module, choose M here: the
  module will be called max77693-haptic.
 
+config INPUT_MAX77705_HAPTIC
+   tristate "MAXIM MAX77705 haptic controller support"
+   depends on MFD_MAX77705 && PWM
+   select INPUT_FF_MEMLESS
+   help
+ This option enables support for the haptic controller on
+ MAXIM MAX77705 chip.
+
+ To compile this driver as module, choose M here: the
+ module will be called max77705-haptic.
+
 config INPUT_MAX8925_ONKEY
tristate "MAX8925 ONKEY support"
depends on MFD_MAX8925
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index 04296a4abe8e..4dea2720b757 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -51,6 +51,7 @@ obj-$(CONFIG_INPUT_KXTJ9) += kxtj9.o
 obj-$(CONFIG_INPUT_M68K_BEEP)  += m68kspkr.o
 obj-$(CONFIG_INPUT_MAX77650_ONKEY) += max77650-onkey.o
 obj-$(CONFIG_INPUT_MAX77693_HAPTIC)+= max77693-haptic.o
+obj-$(CONFIG_INPUT_MAX77705_HAPTIC)+= max77705-haptic.o
 obj-$(CONFIG_INPUT_MAX8925_ONKEY)  += max8925_onkey.o
 obj-$(CONFIG_INPUT_MAX8997_HAPTIC) += max8997_haptic.o
 obj-$(CONFIG_INPUT_MC13783_PWRBUTTON)  += mc13783-pwrbutton.o
diff --git a/drivers/input/misc/max77705-haptic.c 
b/drivers/input/misc/max77705-haptic.c
new file mode 100644
index ..e92dcb4fff37
--- /dev/null
+++ b/drivers/input/misc/max77705-haptic.c
@@ -0,0 +1,378 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Based on max77693-haptic.c:
+ * Copyright (C) 2014,2015 Samsung Electronics
+ *  Jaewon Kim 
+ *  Krzysztof Kozlowski 
+ *
+ * Copyright (C) 2024 Dzmitry Sankouski 
+ *
+ * This program is not provided / owned by Maxim Integrated Products.
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#define MAX_MAGNITUDE_SHIFT16
+
+enum max77705_haptic_motor_type {
+   MAX77705_HAPTIC_ERM = 0,
+   MAX77705_HAPTIC_LRA,
+};
+
+enum max77705_haptic_pulse_mode {
+   MAX77705_HAPTIC_EXTERNAL_MODE = 0,
+   MAX77705_HAPTIC_INTERNAL_MODE,
+};
+
+enum max77705_haptic_pwm_divisor {
+   MAX77705_HAPTIC_PWM_DIVISOR_32 = 0,
+   MAX77705_HAPTIC_PWM_DIVISOR_64,
+   MAX77705_HAPTIC_PWM_DIVISOR_128,
+   MAX77705_HAPTIC_PWM_DIVISOR_256,
+};
+
+struct max77705_haptic {
+   enum max77705_types dev_type;
+
+   struct regmap *regmap_pmic;
+   struct regmap *regmap_haptic;
+   struct device *dev;
+   struct input_dev *input_dev;
+   struct pwm_device *pwm_dev;
+   struct regulator *motor_reg;
+
+   bool enabled;
+   bool suspend_state;
+   unsigned int magnitude;
+   unsigned int pwm_duty;
+   enum max77705_haptic_motor_type type;
+   enum max77705_haptic_pulse_mode mode;
+
+   struct work_struct work;
+};
+
+static int max77705_haptic_set_duty_cycle(struct max77705_haptic *haptic)
+{
+   struct pwm_args pargs;
+   int delta;
+   int error;
+
+   pwm_get_args(haptic->pwm_dev, &pargs);
+   delta = (pargs.period + haptic->pwm_duty) / 2;
+   error = pwm_config(haptic->pwm_dev, delta, pargs.period);
+   if (error) {
+   dev_err(haptic->dev, "failed to configure pwm: %d\n", error);
+   return error;
+   }
+
+   return 0;
+}
+
+static int max77705_haptic_bias(struct max77705_haptic *haptic, bool on)
+{
+   int error;
+
+   error = regmap_update_bits(haptic->regmap_haptic,
+  
MAX77705_PMIC_REG_MAINCTRL1,
+  
MAX77705_MAINCTRL1_BIASEN_MASK,
+  on << 
MAX77705_MAINCTRL1_BIASEN_SHIFT);
+
+   if (error) {
+   dev_err(haptic->dev, "failed to %s bias: %d\n",
+   on ? "enable" : "disable", error);
+   return error;
+   }
+
+   return 0;
+}
+
+static int max77705_haptic_configure(struct max77705_haptic *haptic,
+bool enable)
+{
+   unsigned int value, config_reg;
+   int error;
+
+   value = ((haptic->type << MAX77705_CONFIG2_MODE_SHIFT)