Messages in this thread | | | Date | Mon, 05 Mar 2012 10:18:23 +0900 | From | Chanwoo Choi <> | Subject | Re: [PATCH 2/2] input: add driver support for MAX8997-haptic |
| |
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<dmitry.torokhov@gmail.com> wrote: >> 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@samsung.com> >>> 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.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@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-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@samsung.com>"); >>> +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@samsung.com> >> >> 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@samsung.com> >> Signed-off-by: MyungJoo Ham<myungjoo.ham@samsung.com> >> Signed-off-by: Kyungmin Park<kyungmin.park@samsung.com> >> Signed-off-by: Dmitry Torokhov<dtor@mail.ru> >> --- >> >> 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@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-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@samsung.com>"); >> +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@vger.kernel.org >> More majordomo info at http://vger.kernel.org/majordomo-info.html >>
-- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
| |