Hi Donggeun, On Thu, Jan 05, 2012 at 07:04:36PM +0900, Donggeun Kim wrote: > The MAX8997-haptic function can be used to control motor. > User can control the haptic driver by using force feedback framework. > This patch supports the haptic function in MAX8997. > Overall looks reasonable, just a few comments below. > Signed-off-by: Donggeun Kim <dg77.kim@xxxxxxxxxxx> > Signed-off-by: MyungJoo Ham <myungjoo.ham@xxxxxxxxxxx> > Signed-off-by: Kyungmin Park <kyungmin.park@xxxxxxxxxxx> > --- > drivers/input/misc/Kconfig | 12 ++ > drivers/input/misc/Makefile | 1 + > drivers/input/misc/max8997_haptic.c | 366 +++++++++++++++++++++++++++++++++++ > 3 files changed, 379 insertions(+), 0 deletions(-) > create mode 100644 drivers/input/misc/max8997_haptic.c > > diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig > index 22d875f..695e1fd 100644 > --- a/drivers/input/misc/Kconfig > +++ b/drivers/input/misc/Kconfig > @@ -134,6 +134,18 @@ config INPUT_MAX8925_ONKEY > To compile this driver as a module, choose M here: the module > will be called max8925_onkey. > > +config INPUT_MAX8997_HAPTIC > + tristate "MAXIM MAX8997 haptic controller support" > + depends on HAVE_PWM && MFD_MAX8997 > + select INPUT_FF_MEMLESS > + help > + This option enables device driver support for the haptic controller > + on MAXIM MAX8997 chip. This driver supports ff-memless interface > + from input framework. > + > + To compile this driver as module, choose M here: the > + module will be called max8997-haptic. > + > config INPUT_MC13783_PWRBUTTON > tristate "MC13783 ON buttons" > depends on MFD_MC13783 > diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile > index a244fc6..083df5a 100644 > --- a/drivers/input/misc/Makefile > +++ b/drivers/input/misc/Makefile > @@ -28,6 +28,7 @@ obj-$(CONFIG_INPUT_KEYSPAN_REMOTE) += keyspan_remote.o > obj-$(CONFIG_INPUT_KXTJ9) += kxtj9.o > obj-$(CONFIG_INPUT_M68K_BEEP) += m68kspkr.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 > obj-$(CONFIG_INPUT_MMA8450) += mma8450.o > obj-$(CONFIG_INPUT_MPU3050) += mpu3050.o > diff --git a/drivers/input/misc/max8997_haptic.c b/drivers/input/misc/max8997_haptic.c > new file mode 100644 > index 0000000..d7114ad > --- /dev/null > +++ b/drivers/input/misc/max8997_haptic.c > @@ -0,0 +1,366 @@ > +/* > + * max8997_haptic.c - MAX8997-haptic controller driver Please no file names in comments - easiter to move files around. > + * > + * Copyright (C) 2012 Samsung Electronics > + * Donggeun Kim <dg77.kim@xxxxxxxxxxx> > + * > + * This program is not provided / owned by Maxim Integrated Products. > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License as published by > + * the Free Software Foundation; either version 2 of the License, or > + * (at your option) any later version. > + * > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + * > + * You should have received a copy of the GNU General Public License > + * along with this program; if not, write to the Free Software > + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA > + * > + */ > + > +#include <linux/module.h> > +#include <linux/init.h> > +#include <linux/slab.h> > +#include <linux/platform_device.h> > +#include <linux/err.h> > +#include <linux/pwm.h> > +#include <linux/input.h> > +#include <linux/mfd/max8997-private.h> > +#include <linux/mfd/max8997.h> > +#include <linux/regulator/consumer.h> > + > +/* Haptic configuration 2 register */ > +#define MAX8997_MOTOR_TYPE_SHIFT 7 > +#define MAX8997_ENABLE_SHIFT 6 > +#define MAX8997_MODE_SHIFT 5 > + > +/* Haptic driver configuration register */ > +#define MAX8997_CYCLE_SHIFT 6 > +#define MAX8997_SIG_PERIOD_SHIFT 4 > +#define MAX8997_SIG_DUTY_SHIFT 2 > +#define MAX8997_PWM_DUTY_SHIFT 0 > + > +struct max8997_haptic { > + struct device *dev; > + struct i2c_client *client; > + struct input_dev *input_dev; > + struct regulator *regulator; > + > + bool enabled; > + > + int level; It looks like this should be unsigned. > + > + struct pwm_device *pwm; > + int pwm_period; > + enum max8997_haptic_pwm_divisor pwm_divisor; > + > + enum max8997_haptic_motor_type type; > + enum max8997_haptic_pulse_mode mode; > + > + int internal_mode_pattern; > + int pattern_cycle; > + int pattern_signal_period; > +}; > + > +static int max8997_haptic_set_duty_cycle(struct max8997_haptic *chip) > +{ > + int duty, i; > + int ret; > + u8 duty_index; > + > + if (chip->mode == MAX8997_EXTERNAL_MODE) { > + duty = chip->pwm_period * chip->level / 100; > + ret = pwm_config(chip->pwm, duty, chip->pwm_period); > + } else { > + for (i = 0; i <= 64; i++) { > + if (chip->level <= i * 100 / 64) { > + duty_index = i; > + break; > + } > + } > + switch (chip->internal_mode_pattern) { > + case 0: > + max8997_write_reg(chip->client, > + MAX8997_HAPTIC_REG_SIGPWMDC1, duty_index); > + break; > + case 1: > + max8997_write_reg(chip->client, > + MAX8997_HAPTIC_REG_SIGPWMDC2, duty_index); > + break; > + case 2: > + max8997_write_reg(chip->client, > + MAX8997_HAPTIC_REG_SIGPWMDC3, duty_index); > + break; > + case 3: > + max8997_write_reg(chip->client, > + MAX8997_HAPTIC_REG_SIGPWMDC4, duty_index); > + break; > + default: > + break; > + } > + } > + return ret; > +} > + > +static void max8997_haptic_configure(struct max8997_haptic *chip) > +{ > + u8 value; > + > + value = chip->type << MAX8997_MOTOR_TYPE_SHIFT | > + chip->enabled << MAX8997_ENABLE_SHIFT | > + chip->mode << MAX8997_MODE_SHIFT | chip->pwm_divisor; > + max8997_write_reg(chip->client, MAX8997_HAPTIC_REG_CONF2, value); > + > + if (chip->mode == MAX8997_INTERNAL_MODE && chip->enabled) { > + value = chip->internal_mode_pattern << MAX8997_CYCLE_SHIFT | > + chip->internal_mode_pattern << MAX8997_SIG_PERIOD_SHIFT | > + chip->internal_mode_pattern << MAX8997_SIG_DUTY_SHIFT | > + chip->internal_mode_pattern << MAX8997_PWM_DUTY_SHIFT; > + max8997_write_reg(chip->client, > + MAX8997_HAPTIC_REG_DRVCONF, value); > + > + switch (chip->internal_mode_pattern) { > + case 0: > + value = chip->pattern_cycle << 4; > + max8997_write_reg(chip->client, > + MAX8997_HAPTIC_REG_CYCLECONF1, value); > + value = chip->pattern_signal_period; > + max8997_write_reg(chip->client, > + MAX8997_HAPTIC_REG_SIGCONF1, value); > + break; > + case 1: > + value = chip->pattern_cycle; > + max8997_write_reg(chip->client, > + MAX8997_HAPTIC_REG_CYCLECONF1, value); > + value = chip->pattern_signal_period; > + max8997_write_reg(chip->client, > + MAX8997_HAPTIC_REG_SIGCONF2, value); > + break; > + case 2: > + value = chip->pattern_cycle << 4; > + max8997_write_reg(chip->client, > + MAX8997_HAPTIC_REG_CYCLECONF2, value); > + value = chip->pattern_signal_period; > + max8997_write_reg(chip->client, > + MAX8997_HAPTIC_REG_SIGCONF3, value); > + break; > + case 3: > + value = chip->pattern_cycle; > + max8997_write_reg(chip->client, > + MAX8997_HAPTIC_REG_CYCLECONF2, value); > + value = chip->pattern_signal_period; > + max8997_write_reg(chip->client, > + MAX8997_HAPTIC_REG_SIGCONF4, value); > + break; > + default: > + break; > + } > + } > +} > + > +static void max8997_haptic_enable(struct max8997_haptic *chip, bool enable) > +{ > + if (chip->enabled == enable) > + return; > + > + chip->enabled = enable; > + > + if (enable) { > + regulator_enable(chip->regulator); > + max8997_haptic_configure(chip); > + if (chip->mode == MAX8997_EXTERNAL_MODE) > + pwm_enable(chip->pwm); > + } else { > + max8997_haptic_configure(chip); > + if (chip->mode == MAX8997_EXTERNAL_MODE) > + pwm_disable(chip->pwm); > + regulator_disable(chip->regulator); > + } > +} > + > +static void max8997_haptic_close(struct input_dev *dev) > +{ > + struct max8997_haptic *chip = input_get_drvdata(dev); > + > + if (chip->enabled) > + max8997_haptic_enable(chip, false); This should not be needed as input core/evdev "flushes" (stops) all FF effects played by a given client when closing the device. > +} > + > +static int max8997_haptic_play_effect(struct input_dev *dev, void *data, > + struct ff_effect *effect) > +{ > + struct max8997_haptic *chip = input_get_drvdata(dev); > + int ret; > + > + chip->level = effect->u.rumble.strong_magnitude; > + if (!chip->level) > + chip->level = effect->u.rumble.weak_magnitude; > + > + if (chip->level) { > + ret = max8997_haptic_set_duty_cycle(chip); > + if (ret) { > + dev_err(chip->dev, "set_pwm_cycle failed\n"); > + return ret; > + } > + max8997_haptic_enable(chip, true); > + } else { > + max8997_haptic_enable(chip, false); > + } > + > + return 0; > +} > + > +static int __devinit max8997_haptic_probe(struct platform_device *pdev) > +{ > + struct max8997_dev *iodev = dev_get_drvdata(pdev->dev.parent); > + struct max8997_platform_data *pdata = dev_get_platdata(iodev->dev); > + struct max8997_haptic_platform_data *haptic_pdata = > + pdata->haptic_pdata; > + struct max8997_haptic *chip; > + struct input_dev *input_dev; > + int ret; > + > + if (!haptic_pdata) { > + dev_err(&pdev->dev, "no haptic platform data\n"); > + return -EINVAL; > + } > + > + chip = kzalloc(sizeof(struct max8997_haptic), GFP_KERNEL); > + input_dev = input_allocate_device(); > + if (!chip || !input_dev) { > + dev_err(&pdev->dev, "unable to allocate memory\n"); > + return -ENOMEM; This leaks memory. > + } > + > + chip->client = iodev->haptic; > + chip->dev = &pdev->dev; > + chip->input_dev = input_dev; > + chip->pwm_period = haptic_pdata->pwm_period; > + chip->type = haptic_pdata->type; > + chip->mode = haptic_pdata->mode; > + chip->pwm_divisor = haptic_pdata->pwm_divisor; > + if (chip->mode == MAX8997_INTERNAL_MODE) { > + chip->internal_mode_pattern = > + haptic_pdata->internal_mode_pattern; > + chip->pattern_cycle = haptic_pdata->pattern_cycle; > + chip->pattern_signal_period = > + haptic_pdata->pattern_signal_period; > + } > + > + if (chip->mode == MAX8997_EXTERNAL_MODE) { > + chip->pwm = pwm_request(haptic_pdata->pwm_channel_id, > + "max8997-haptic"); > + if (IS_ERR(chip->pwm)) { > + dev_err(&pdev->dev, > + "unable to request PWM for haptic\n"); > + ret = PTR_RET(chip->pwm); Why PTR_RET? Should be PTR_ERR (we do know we got an error). > + goto err_pwm; > + } > + } > + > + chip->regulator = regulator_get(&pdev->dev, "inmotor"); > + if (IS_ERR(chip->regulator)) { > + dev_err(&pdev->dev, "unable to get regulator\n"); > + ret = PTR_RET(chip->regulator); PTR_ERR() > + goto err_regulator; > + } > + > + platform_set_drvdata(pdev, chip); > + > + input_dev->name = "max8997-haptic"; > + input_dev->id.version = 1; > + input_dev->dev.parent = &pdev->dev; > + input_dev->close = max8997_haptic_close; Not needed. > + input_set_drvdata(input_dev, chip); > + input_set_capability(input_dev, EV_FF, FF_RUMBLE); > + > + ret = input_ff_create_memless(input_dev, NULL, > + max8997_haptic_play_effect); > + if (ret) { > + dev_err(&pdev->dev, > + "unable to create FF device(ret : %d)\n", ret); > + goto err_ff_memless; > + } > + > + ret = input_register_device(input_dev); > + if (ret) { > + dev_err(&pdev->dev, > + "unable to register input device(ret : %d)\n", ret); > + goto err_input_register; > + } > + > + return 0; > + > +err_input_register: > + input_ff_destroy(input_dev); > +err_ff_memless: > + regulator_put(chip->regulator); > +err_regulator: > + if (chip->mode == MAX8997_EXTERNAL_MODE) > + pwm_free(chip->pwm); > +err_pwm: > + kfree(chip); > + > + return ret; > +} > + > +static int __devexit max8997_haptic_remove(struct platform_device *pdev) > +{ > + struct max8997_haptic *chip = platform_get_drvdata(pdev); > + > + input_unregister_device(chip->input_dev); > + regulator_put(chip->regulator); > + > + if (chip->mode == MAX8997_EXTERNAL_MODE) > + pwm_free(chip->pwm); > + > + kfree(chip); > + > + return 0; > +} > + > +static int max8997_haptic_suspend(struct device *dev) > +{ > + struct platform_device *pdev = to_platform_device(dev); > + struct max8997_haptic *chip = platform_get_drvdata(pdev); > + > + max8997_haptic_enable(chip, false); This may race with max8997_haptic_play_effect() and thus you should take input_dev->event_lock. > + > + return 0; > +} > + > +static int max8997_haptic_resume(struct device *dev) > +{ > + return 0; > +} Not needed. > + > +static SIMPLE_DEV_PM_OPS(max8997_haptic_pm_ops, max8997_haptic_suspend, > + max8997_haptic_resume); > + > +static const struct platform_device_id max8997_haptic_id[] = { > + { "max8997-haptic", 0 }, > + { }, > +}; > +MODULE_DEVICE_TABLE(i2c, max8997_haptic_id); > + > +static struct platform_driver max8997_haptic_driver = { > + .driver = { > + .name = "max8997-haptic", > + .owner = THIS_MODULE, > + .pm = &max8997_haptic_pm_ops, > + }, > + .probe = max8997_haptic_probe, > + .remove = __devexit_p(max8997_haptic_remove), > + .id_table = max8997_haptic_id, > +}; > + > +module_platform_driver(max8997_haptic_driver); > + > +MODULE_ALIAS("platform:max8997-haptic"); > +MODULE_AUTHOR("Donggeun Kim <dg77.kim@xxxxxxxxxxx>"); > +MODULE_DESCRIPTION("max8997_haptic driver"); > +MODULE_LICENSE("GPL"); Does the patch below work for you? Thanks. -- Dmitry Input: add driver support for MAX8997-haptic From: Donggeun Kim <dg77.kim@xxxxxxxxxxx> The MAX8997-haptic function can be used to control motor. User can control the haptic driver by using force feedback framework. Signed-off-by: Donggeun Kim <dg77.kim@xxxxxxxxxxx> Signed-off-by: MyungJoo Ham <myungjoo.ham@xxxxxxxxxxx> Signed-off-by: Kyungmin Park <kyungmin.park@xxxxxxxxxxx> Signed-off-by: Dmitry Torokhov <dtor@xxxxxxx> --- drivers/input/misc/Kconfig | 12 + drivers/input/misc/Makefile | 1 drivers/input/misc/max8997_haptic.c | 367 +++++++++++++++++++++++++++++++++++ include/linux/mfd/max8997.h | 53 +++++ 4 files changed, 432 insertions(+), 1 deletions(-) create mode 100644 drivers/input/misc/max8997_haptic.c diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index 2337c3e..ee077a4 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -134,6 +134,18 @@ config INPUT_MAX8925_ONKEY To compile this driver as a module, choose M here: the module will be called max8925_onkey. +config INPUT_MAX8997_HAPTIC + tristate "MAXIM MAX8997 haptic controller support" + depends on HAVE_PWM && MFD_MAX8997 + select INPUT_FF_MEMLESS + help + This option enables device driver support for the haptic controller + on MAXIM MAX8997 chip. This driver supports ff-memless interface + from input framework. + + To compile this driver as module, choose M here: the + module will be called max8997-haptic. + config INPUT_MC13783_PWRBUTTON tristate "MC13783 ON buttons" depends on MFD_MC13783 diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index a6d8de0..f55cdf4 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -31,6 +31,7 @@ obj-$(CONFIG_INPUT_KEYSPAN_REMOTE) += keyspan_remote.o obj-$(CONFIG_INPUT_KXTJ9) += kxtj9.o obj-$(CONFIG_INPUT_M68K_BEEP) += m68kspkr.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 obj-$(CONFIG_INPUT_MMA8450) += mma8450.o obj-$(CONFIG_INPUT_MPU3050) += mpu3050.o diff --git a/drivers/input/misc/max8997_haptic.c b/drivers/input/misc/max8997_haptic.c new file mode 100644 index 0000000..74cbc13 --- /dev/null +++ b/drivers/input/misc/max8997_haptic.c @@ -0,0 +1,367 @@ +/* + * MAX8997-haptic controller driver + * + * Copyright (C) 2012 Samsung Electronics + * Donggeun Kim <dg77.kim@xxxxxxxxxxx> + * + * This program is not provided / owned by Maxim Integrated Products. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/platform_device.h> +#include <linux/err.h> +#include <linux/pwm.h> +#include <linux/input.h> +#include <linux/mfd/max8997-private.h> +#include <linux/mfd/max8997.h> +#include <linux/regulator/consumer.h> + +/* Haptic configuration 2 register */ +#define MAX8997_MOTOR_TYPE_SHIFT 7 +#define MAX8997_ENABLE_SHIFT 6 +#define MAX8997_MODE_SHIFT 5 + +/* Haptic driver configuration register */ +#define MAX8997_CYCLE_SHIFT 6 +#define MAX8997_SIG_PERIOD_SHIFT 4 +#define MAX8997_SIG_DUTY_SHIFT 2 +#define MAX8997_PWM_DUTY_SHIFT 0 + +struct max8997_haptic { + struct device *dev; + struct i2c_client *client; + struct input_dev *input_dev; + struct regulator *regulator; + + bool enabled; + + unsigned int level; + + struct pwm_device *pwm; + int pwm_period; + enum max8997_haptic_pwm_divisor pwm_divisor; + + enum max8997_haptic_motor_type type; + enum max8997_haptic_pulse_mode mode; + + int internal_mode_pattern; + int pattern_cycle; + int pattern_signal_period; +}; + +static int max8997_haptic_set_duty_cycle(struct max8997_haptic *chip) +{ + int ret = 0; + + if (chip->mode == MAX8997_EXTERNAL_MODE) { + int duty = chip->pwm_period * chip->level / 100; + ret = pwm_config(chip->pwm, duty, chip->pwm_period); + } else { + int i; + u8 duty_index = 0; + + for (i = 0; i <= 64; i++) { + if (chip->level <= i * 100 / 64) { + duty_index = i; + break; + } + } + switch (chip->internal_mode_pattern) { + case 0: + max8997_write_reg(chip->client, + MAX8997_HAPTIC_REG_SIGPWMDC1, duty_index); + break; + case 1: + max8997_write_reg(chip->client, + MAX8997_HAPTIC_REG_SIGPWMDC2, duty_index); + break; + case 2: + max8997_write_reg(chip->client, + MAX8997_HAPTIC_REG_SIGPWMDC3, duty_index); + break; + case 3: + max8997_write_reg(chip->client, + MAX8997_HAPTIC_REG_SIGPWMDC4, duty_index); + break; + default: + break; + } + } + return ret; +} + +static void max8997_haptic_configure(struct max8997_haptic *chip) +{ + u8 value; + + value = chip->type << MAX8997_MOTOR_TYPE_SHIFT | + chip->enabled << MAX8997_ENABLE_SHIFT | + chip->mode << MAX8997_MODE_SHIFT | chip->pwm_divisor; + max8997_write_reg(chip->client, MAX8997_HAPTIC_REG_CONF2, value); + + if (chip->mode == MAX8997_INTERNAL_MODE && chip->enabled) { + value = chip->internal_mode_pattern << MAX8997_CYCLE_SHIFT | + chip->internal_mode_pattern << MAX8997_SIG_PERIOD_SHIFT | + chip->internal_mode_pattern << MAX8997_SIG_DUTY_SHIFT | + chip->internal_mode_pattern << MAX8997_PWM_DUTY_SHIFT; + max8997_write_reg(chip->client, + MAX8997_HAPTIC_REG_DRVCONF, value); + + switch (chip->internal_mode_pattern) { + case 0: + value = chip->pattern_cycle << 4; + max8997_write_reg(chip->client, + MAX8997_HAPTIC_REG_CYCLECONF1, value); + value = chip->pattern_signal_period; + max8997_write_reg(chip->client, + MAX8997_HAPTIC_REG_SIGCONF1, value); + break; + case 1: + value = chip->pattern_cycle; + max8997_write_reg(chip->client, + MAX8997_HAPTIC_REG_CYCLECONF1, value); + value = chip->pattern_signal_period; + max8997_write_reg(chip->client, + MAX8997_HAPTIC_REG_SIGCONF2, value); + break; + case 2: + value = chip->pattern_cycle << 4; + max8997_write_reg(chip->client, + MAX8997_HAPTIC_REG_CYCLECONF2, value); + value = chip->pattern_signal_period; + max8997_write_reg(chip->client, + MAX8997_HAPTIC_REG_SIGCONF3, value); + break; + case 3: + value = chip->pattern_cycle; + max8997_write_reg(chip->client, + MAX8997_HAPTIC_REG_CYCLECONF2, value); + value = chip->pattern_signal_period; + max8997_write_reg(chip->client, + MAX8997_HAPTIC_REG_SIGCONF4, value); + break; + default: + break; + } + } +} + +static void max8997_haptic_enable(struct max8997_haptic *chip) +{ + if (!chip->enabled) { + regulator_enable(chip->regulator); + max8997_haptic_configure(chip); + if (chip->mode == MAX8997_EXTERNAL_MODE) + pwm_enable(chip->pwm); + chip->enabled = true; + } +} + +static void max8997_haptic_disable(struct max8997_haptic *chip) +{ + if (chip->enabled) { + max8997_haptic_configure(chip); + if (chip->mode == MAX8997_EXTERNAL_MODE) + pwm_disable(chip->pwm); + regulator_disable(chip->regulator); + chip->enabled = false; + } +} + + +static int max8997_haptic_play_effect(struct input_dev *dev, void *data, + struct ff_effect *effect) +{ + struct max8997_haptic *chip = input_get_drvdata(dev); + int error; + + chip->level = effect->u.rumble.strong_magnitude; + if (!chip->level) + chip->level = effect->u.rumble.weak_magnitude; + + if (chip->level) { + error = max8997_haptic_set_duty_cycle(chip); + if (error) { + dev_err(chip->dev, "set_pwm_cycle failed\n"); + return error; + } + max8997_haptic_enable(chip); + } else { + max8997_haptic_disable(chip); + } + + return 0; +} + +static int __devinit max8997_haptic_probe(struct platform_device *pdev) +{ + struct max8997_dev *iodev = dev_get_drvdata(pdev->dev.parent); + const struct max8997_platform_data *pdata = + dev_get_platdata(iodev->dev); + const struct max8997_haptic_platform_data *haptic_pdata = + pdata->haptic_pdata; + struct max8997_haptic *chip; + struct input_dev *input_dev; + int error; + + if (!haptic_pdata) { + dev_err(&pdev->dev, "no haptic platform data\n"); + return -EINVAL; + } + + chip = kzalloc(sizeof(struct max8997_haptic), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!chip || !input_dev) { + dev_err(&pdev->dev, "unable to allocate memory\n"); + error = -ENOMEM; + goto err_free_mem; + } + + chip->client = iodev->haptic; + chip->dev = &pdev->dev; + chip->input_dev = input_dev; + chip->pwm_period = haptic_pdata->pwm_period; + chip->type = haptic_pdata->type; + chip->mode = haptic_pdata->mode; + chip->pwm_divisor = haptic_pdata->pwm_divisor; + if (chip->mode == MAX8997_INTERNAL_MODE) { + chip->internal_mode_pattern = + haptic_pdata->internal_mode_pattern; + chip->pattern_cycle = haptic_pdata->pattern_cycle; + chip->pattern_signal_period = + haptic_pdata->pattern_signal_period; + } + + if (chip->mode == MAX8997_EXTERNAL_MODE) { + chip->pwm = pwm_request(haptic_pdata->pwm_channel_id, + "max8997-haptic"); + if (IS_ERR(chip->pwm)) { + error = PTR_ERR(chip->pwm); + dev_err(&pdev->dev, + "unable to request PWM for haptic, error: %d\n", + error); + goto err_free_mem; + } + } + + chip->regulator = regulator_get(&pdev->dev, "inmotor"); + if (IS_ERR(chip->regulator)) { + error = PTR_ERR(chip->regulator); + dev_err(&pdev->dev, + "unable to get regulator, error: %d\n", + error); + goto err_free_pwm; + } + + input_dev->name = "max8997-haptic"; + input_dev->id.version = 1; + input_dev->dev.parent = &pdev->dev; + input_set_drvdata(input_dev, chip); + input_set_capability(input_dev, EV_FF, FF_RUMBLE); + + error = input_ff_create_memless(input_dev, NULL, + max8997_haptic_play_effect); + if (error) { + dev_err(&pdev->dev, + "unable to create FF device, error: %d\n", + error); + goto err_put_regulator; + } + + error = input_register_device(input_dev); + if (error) { + dev_err(&pdev->dev, + "unable to register input device, error: %d\n", + error); + goto err_destroy_ff; + } + + platform_set_drvdata(pdev, chip); + return 0; + +err_destroy_ff: + input_ff_destroy(input_dev); +err_put_regulator: + regulator_put(chip->regulator); +err_free_pwm: + if (chip->mode == MAX8997_EXTERNAL_MODE) + pwm_free(chip->pwm); +err_free_mem: + input_free_device(input_dev); + kfree(chip); + + return error; +} + +static int __devexit max8997_haptic_remove(struct platform_device *pdev) +{ + struct max8997_haptic *chip = platform_get_drvdata(pdev); + + input_unregister_device(chip->input_dev); + regulator_put(chip->regulator); + + if (chip->mode == MAX8997_EXTERNAL_MODE) + pwm_free(chip->pwm); + + kfree(chip); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int max8997_haptic_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct max8997_haptic *chip = platform_get_drvdata(pdev); + struct input_dev *input_dev = chip->input_dev; + unsigned long flags; + + spin_lock_irqsave(&input_dev->event_lock, flags); + max8997_haptic_disable(chip); + spin_unlock_irqrestore(&input_dev->event_lock, flags); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(max8997_haptic_pm_ops, max8997_haptic_suspend, NULL); + +static const struct platform_device_id max8997_haptic_id[] = { + { "max8997-haptic", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, max8997_haptic_id); + +static struct platform_driver max8997_haptic_driver = { + .driver = { + .name = "max8997-haptic", + .owner = THIS_MODULE, + .pm = &max8997_haptic_pm_ops, + }, + .probe = max8997_haptic_probe, + .remove = __devexit_p(max8997_haptic_remove), + .id_table = max8997_haptic_id, +}; +module_platform_driver(max8997_haptic_driver); + +MODULE_ALIAS("platform:max8997-haptic"); +MODULE_AUTHOR("Donggeun Kim <dg77.kim@xxxxxxxxxxx>"); +MODULE_DESCRIPTION("max8997_haptic driver"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/mfd/max8997.h b/include/linux/mfd/max8997.h index fff5905..3a00c95 100644 --- a/include/linux/mfd/max8997.h +++ b/include/linux/mfd/max8997.h @@ -131,6 +131,55 @@ struct max8997_muic_platform_data { int num_init_data; }; +enum max8997_haptic_motor_type { + MAX8997_HAPTIC_ERM, + MAX8997_HAPTIC_LRA, +}; + +enum max8997_haptic_pulse_mode { + MAX8997_EXTERNAL_MODE, + MAX8997_INTERNAL_MODE, +}; + +enum max8997_haptic_pwm_divisor { + MAX8997_PWM_DIVISOR_32, + MAX8997_PWM_DIVISOR_64, + MAX8997_PWM_DIVISOR_128, + MAX8997_PWM_DIVISOR_256, +}; + +/** + * max8997_haptic_platform_data + * @pwm_channel_id: channel number of PWM device + * valid for MAX8997_EXTERNAL_MODE + * @pwm_period: period in nano second for PWM device + * valid for MAX8997_EXTERNAL_MODE + * @type: motor type + * @mode: pulse mode + * MAX8997_EXTERNAL_MODE: external PWM device is used to control motor + * MAX8997_INTERNAL_MODE: internal pulse generator is used to control motor + * @pwm_divisor: divisor for external PWM device + * @internal_mode_pattern: internal mode pattern for internal mode + * [0 - 3]: valid pattern number + * @pattern_cycle: the number of cycles of the waveform + * for the internal mode pattern + * [0 - 15]: available cycles + * @pattern_signal_period: period of the waveform for the internal mode pattern + * [0 - 255]: available period + */ +struct max8997_haptic_platform_data { + int pwm_channel_id; + int pwm_period; + + enum max8997_haptic_motor_type type; + enum max8997_haptic_pulse_mode mode; + enum max8997_haptic_pwm_divisor pwm_divisor; + + int internal_mode_pattern; + int pattern_cycle; + int pattern_signal_period; +}; + enum max8997_led_mode { MAX8997_NONE, MAX8997_FLASH_MODE, @@ -192,7 +241,9 @@ struct max8997_platform_data { /* ---- MUIC ---- */ struct max8997_muic_platform_data *muic_pdata; - /* HAPTIC: Not implemented */ + /* ---- HAPTIC ---- */ + struct max8997_haptic_platform_data *haptic_pdata; + /* RTC: Not implemented */ /* ---- LED ---- */ struct max8997_led_platform_data *led_pdata; -- To unsubscribe from this list: send the line "unsubscribe linux-input" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html