Re: [PATCH 2/2] input: add driver support for MAX8997-haptic
From: Chanwoo Choi <cw00.choi@samsung.com>
Date: 2012-03-05 01:18:26
Also in:
lkml
2012년 03월 05일 09:56, Kyungmin Park 쓴 글:
Hi Dmitry, As Mr. Kim Donggeun leaved the company as personal reason. Mr. Choi will follow up the remaining works. To Mr. Choi, Can you test the below patch?
I will test modified patch according to Dmitry comment and resend this patch. Thank you, Chanwoo Choi
Thank you, Kyungmin Park On 3/5/12, Dmitry Torokhov[off-list ref] wrote:quoted
Hi Donggeun, On Thu, Jan 05, 2012 at 07:04:36PM +0900, Donggeun Kim wrote:quoted
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.quoted
Signed-off-by: Donggeun Kim<redacted> Signed-off-by: MyungJoo Ham<myungjoo.ham@samsung.com> Signed-off-by: Kyungmin Park<kyungmin.park@samsung.com> --- 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.cdiff --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_MC13783diff --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.odiff --git a/drivers/input/misc/max8997_haptic.cb/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 driverPlease no file names in comments - easiter to move files around.quoted
+ * + * Copyright (C) 2012 Samsung Electronics + * Donggeun Kim[off-list ref] + * + * 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.quoted
+ + 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.quoted
+} + +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.quoted
+ } + + 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).quoted
+ 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()quoted
+ 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.quoted
+ 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.quoted
+ + return 0; +} + +static int max8997_haptic_resume(struct device *dev) +{ + return 0; +}Not needed.quoted
+ +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[off-list ref]"); +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<redacted> 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<redacted> Signed-off-by: MyungJoo Ham<myungjoo.ham@samsung.com> Signed-off-by: Kyungmin Park<kyungmin.park@samsung.com> Signed-off-by: Dmitry Torokhov<redacted> --- 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.cdiff --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_MC13783diff --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.odiff --git a/drivers/input/misc/max8997_haptic.cb/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@samsung.com> + * + * 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-1307USA + * + */ + +#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[off-list ref]"); +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 controlmotor + * @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@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
-- To unsubscribe from this list: send the line "unsubscribe linux-input" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html