lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Date:	Mon, 05 Mar 2012 10:18:23 +0900
From:	Chanwoo Choi <cw00.choi@...sung.com>
To:	Kyungmin Park <kyungmin.park@...sung.com>
Cc:	Dmitry Torokhov <dmitry.torokhov@...il.com>,
	Donggeun Kim <dg77.kim@...sung.com>,
	linux-input@...r.kernel.org, sameo@...ux.intel.com,
	broonie@...nsource.wolfsonmicro.com, linux-kernel@...r.kernel.org,
	myungjoo.ham@...sung.com
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@...il.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@...sung.com>
>>> Signed-off-by: MyungJoo Ham<myungjoo.ham@...sung.com>
>>> Signed-off-by: Kyungmin Park<kyungmin.park@...sung.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@...sung.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@...sung.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@...sung.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@...sung.com>
>> Signed-off-by: MyungJoo Ham<myungjoo.ham@...sung.com>
>> Signed-off-by: Kyungmin Park<kyungmin.park@...sung.com>
>> Signed-off-by: Dmitry Torokhov<dtor@...l.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@...sung.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@...sung.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@...r.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@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ