Skip to contentSkip to conversation

haptic: ISA1200 haptic device support

PATCH In review Kyungmin Park 2 participants 3 messages Oct 7, 2009 – Oct 12, 2009

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

PATCH 6/6 haptic: ISA1200 haptic device support

I2C based ISA1200 haptic driver support.
This chip supports both internal and external.
But only external configuration are implemented.

---
 drivers/haptic/Kconfig      |    6 +
 drivers/haptic/Makefile     |    1 +
 drivers/haptic/isa1200.c    |  429 +++++++++++++++++++++++++++++++++++++++++++
 include/linux/i2c/isa1200.h |  103 +++++++++++
 4 files changed, 539 insertions(+), 0 deletions(-)
 create mode 100644 drivers/haptic/isa1200.c
 create mode 100644 include/linux/i2c/isa1200.h
drivers/haptic/Kconfig +6 -0
index 1e9849c..30ae507 100644
@@ -20,4 +20,10 @@ config HAPTIC_SAMSUNG_PWM
 	  This options enables support for haptic connected to GPIO lines
 	  controlled by a PWM timer on SAMSUNG CPUs.
 
+config HAPTIC_ISA1200
+	tristate "ISA1200 haptic support"
+	depends on HAPTIC_CLASS && I2C
+	help
+	  The ISA1200 is a high performance enhanced haptic driver.
+
 endif	# HAPTIC
drivers/haptic/Makefile +1 -0
index c691f7d..3eca352 100644
@@ -3,3 +3,4 @@ obj-$(CONFIG_HAPTIC_CLASS)		+= haptic-class.o
 
 # Drivers
 obj-$(CONFIG_HAPTIC_SAMSUNG_PWM)	+= haptic-samsung-pwm.o
+obj-$(CONFIG_HAPTIC_ISA1200)		+= isa1200.o
drivers/haptic/isa1200.c new file +429 -0

429 lines not on this page · raw message

include/linux/i2c/isa1200.h new file +103 -0

103 lines not on this page · raw message

Signature · 1 line
1.5.3.3
#1 lore raw

Hi Kyungmin,

Adding linux-i2c as it is i2c client driver and Ben Dooks for samsung pwm driver API usage.

Quoted 3 lines · Kyungmin Park

I2C based ISA1200 haptic driver support.
This chip supports both internal and external.
But only external configuration are implemented.

Probably following text would look better:

Add Imagis ISA1200 haptics chip driver support. This chip supports internal and external PWM configuration. This patch only supports external PWM configuration.

Quoted 45 lines

new file mode 100644
index 0000000..51c4ea4

drivers/haptic/isa1200.c new file +40 -0
@@ -0,0 +1,429 @@
+/*
+ *  isa1200.c - Haptic Motor
+ *
+ *  Copyright (C) 2009 Samsung Electronics
+ *  Kyungmin Park <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/haptic.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/pwm.h>
+#include <linux/ctype.h>
+#include <linux/workqueue.h>
+#include <linux/i2c/isa1200.h>
+#include "haptic.h"
+
+struct isa1200_chip {
+       struct i2c_client *client;
+       struct pwm_device *pwm;
+       struct haptic_classdev cdev;
+       struct work_struct work;
+       struct timer_list timer;
+
+       unsigned int    len;            /* LDO enable */
+       unsigned int    hen;            /* Haptic enable */
+
+       int enable;
+       int powered;
+
+       int level;
+       int level_max;
+

drivers/haptic/isa1200.c line 50

Quoted 10 lines
drivers/haptic/isa1200.c new file +10 -0
+       int ldo_level;
+};
+
+
+static void isa1200_chip_power_on(struct isa1200_chip *haptic)
+{
+       if (haptic->powered)
+               return;
+       haptic->powered = 1;
+       /* Use smart mode enable control */

You mean here that, enabling smart mode control by writing ISA1200 register over I2C will be added later, right?

drivers/haptic/isa1200.c line 256

Quoted 10 lines
drivers/haptic/isa1200.c new file +10 -0
+
+static void isa1200_setup(struct i2c_client *client)
+{
+       struct isa1200_chip *chip = i2c_get_clientdata(client);
+       int value;
+
+       gpio_set_value(chip->len, 1);
+       udelay(250);
+       gpio_set_value(chip->len, 1);
+

Please clarify:

In your hardware configuration, you are enabling internal LDO above? It may not be true for all the hardware configuration and it might be grounded. If true, please make this as platform data so that it can be selected run time.

drivers/haptic/isa1200.c line 266

Quoted 10 lines
drivers/haptic/isa1200.c new file +10 -0
+       value = isa1200_read_reg(client, ISA1200_SCTRL0);
+       value &= ~ISA1200_LDOADJ_MASK;
+       value |= chip->ldo_level;
+       isa1200_write_reg(client, ISA1200_SCTRL0, value);
+
+       value = ISA1200_HAPDREN | ISA1200_OVERHL | ISA1200_HAPDIGMOD_PWM_IN |
+               ISA1200_PWMMOD_DIVIDER_128;
+       isa1200_write_reg(client, ISA1200_HCTRL0, value);
+
+       value = ISA1200_EXTCLKSEL | ISA1200_BIT6_ON | ISA1200_MOTTYP_LRA |

Probably motor type could be different too. Please see what other parameters you could become as platform data for this chip and can be tuned by h/w designer for the product design.

drivers/haptic/isa1200.c line 267

Quoted 1 line
drivers/haptic/isa1200.c new file +1 -0
+               ISA1200_SMARTEN | ISA1200_SMARTOFFT_64;

Oh. You are enabling smart control here.

drivers/haptic/isa1200.c line 276

Quoted 9 lines
drivers/haptic/isa1200.c new file +9 -0
+       isa1200_write_reg(client, ISA1200_HCTRL1, value);
+
+       value = isa1200_read_reg(client, ISA1200_HCTRL2);
+       value |= ISA1200_SEEN;
+       isa1200_write_reg(client, ISA1200_HCTRL2, value);
+       isa1200_chip_power_off(chip);
+       isa1200_chip_power_on(chip);
+
+       /* TODO */

?? some todo text?

drivers/haptic/isa1200.c line 285

Quoted 9 lines
drivers/haptic/isa1200.c new file +9 -0
+}
+
+static int __devinit isa1200_probe(struct i2c_client *client,
+                       const struct i2c_device_id *id)
+{
+       struct isa1200_chip *chip;
+       struct haptic_platform_data *pdata;
+       int ret;
+

You need to add i2c_check_functionality call with smbus_byte_data.

Quoted 38 lines
drivers/haptic/isa1200.c new file +38 -0
+       pdata = client->dev.platform_data;
+       if (!pdata) {
+               dev_err(&client->dev, "%s: no platform data\n", __func__);
+               return -EINVAL;
+       }
+
+       chip = kzalloc(sizeof(struct isa1200_chip), GFP_KERNEL);
+       if (!chip)
+               return -ENOMEM;
+
+       chip->client = client;
+       chip->cdev.set = isa1200_chip_set;
+       chip->cdev.get = isa1200_chip_get;
+       chip->cdev.show_enable = isa1200_chip_show_enable;
+       chip->cdev.store_enable = isa1200_chip_store_enable;
+       chip->cdev.store_oneshot = isa1200_chip_store_oneshot;
+       chip->cdev.show_level = isa1200_chip_show_level;
+       chip->cdev.store_level = isa1200_chip_store_level;
+       chip->cdev.show_level_max = isa1200_chip_show_level_max;
+       chip->cdev.name = pdata->name;
+       chip->enable = 0;
+       chip->level = PWM_HAPTIC_DEFAULT_LEVEL;
+       chip->level_max = PWM_HAPTIC_DEFAULT_LEVEL;
+       chip->ldo_level = pdata->ldo_level;
+
+       if (pdata->setup_pin)
+               pdata->setup_pin();
+       chip->len = pdata->gpio;
+       chip->hen = pdata->gpio;
+       chip->pwm = pwm_request(pdata->pwm_timer, "haptic");
+       if (IS_ERR(chip->pwm)) {
+               dev_err(&client->dev, "unable to request PWM for haptic.\n");
+               ret = PTR_ERR(chip->pwm);
+               goto error_pwm;
+       }
+
+       INIT_WORK(&chip->work, isa1200_chip_work);
+

drivers/haptic/isa1200.c line 333

Quoted 10 lines
drivers/haptic/isa1200.c new file +10 -0
+       /* register our new haptic device */
+       ret = haptic_classdev_register(&client->dev, &chip->cdev);
+       if (ret < 0) {
+               dev_err(&client->dev, "haptic_classdev_register failed\n");
+               goto error_classdev;
+       }
+
+       ret = sysfs_create_group(&chip->cdev.dev->kobj, &haptic_group);
+       if (ret)
+               goto error_enable;

Why the user of haptics class has to call this? I assume that once the user of haptics class registers with it, the class itself should create the necessary sysfs files and user driver doesn't have to worry about it except providing necessary hooks.

drivers/haptic/isa1200.c line 337

Quoted 4 lines
drivers/haptic/isa1200.c new file +4 -0
+
+       init_timer(&chip->timer);
+       chip->timer.data = (unsigned long)chip;
+       chip->timer.function = &isa1200_chip_timer;

like to use setup_timer?

Quoted 26 lines
drivers/haptic/isa1200.c new file +26 -0
+
+       i2c_set_clientdata(client, chip);
+
+       if (gpio_is_valid(pdata->gpio)) {
+               ret = gpio_request(pdata->gpio, "haptic enable");
+               if (ret)
+                       goto error_gpio;
+               gpio_direction_output(pdata->gpio, 1);
+       }
+
+       isa1200_setup(client);
+
+       printk(KERN_INFO "isa1200 %s registered\n", pdata->name);
+       return 0;
+
+error_gpio:
+       gpio_free(pdata->gpio);
+error_enable:
+       sysfs_remove_group(&chip->cdev.dev->kobj, &haptic_group);
+error_classdev:
+       haptic_classdev_unregister(&chip->cdev);
+error_pwm:
+       pwm_free(chip->pwm);
+       kfree(chip);
+       return ret;
+}

drivers/haptic/isa1200.c line 373

Quoted 10 lines
drivers/haptic/isa1200.c new file +10 -0
+
+static int __devexit isa1200_remove(struct i2c_client *client)
+{
+       struct isa1200_chip *chip = i2c_get_clientdata(client);
+
+       if (gpio_is_valid(chip->len))
+               gpio_free(chip->len);
+
+       sysfs_remove_group(&chip->cdev.dev->kobj, &haptic_group);
+       haptic_classdev_unregister(&chip->cdev);

Where is del_timer_sync and cancel_work ?

Quoted 46 lines
drivers/haptic/isa1200.c new file +46 -0
+       pwm_free(chip->pwm);
+       kfree(chip);
+       return 0;
+}
+
+#ifdef CONFIG_PM
+static int isa1200_suspend(struct i2c_client *client, pm_message_t mesg)
+{
+       struct isa1200_chip *chip = i2c_get_clientdata(client);
+       isa1200_chip_power_off(chip);
+       return 0;
+}
+
+static int isa1200_resume(struct i2c_client *client)
+{
+       isa1200_setup(client);
+       return 0;
+}
+#else
+#define isa1200_suspend                NULL
+#define isa1200_resume         NULL
+#endif
+
+static const struct i2c_device_id isa1200_id[] = {
+       { "isa1200", 0 },
+       { },
+};
+MODULE_DEVICE_TABLE(i2c, isa1200_id);
+
+static struct i2c_driver isa1200_driver = {
+       .driver = {
+               .name   = "isa1200",
+       },
+       .probe          = isa1200_probe,
+       .remove         = __devexit_p(isa1200_remove),
+       .suspend        = isa1200_suspend,
+       .resume         = isa1200_resume,
+       .id_table       = isa1200_id,
+};
+
+static int __init isa1200_init(void)
+{
+       return i2c_add_driver(&isa1200_driver);
+}
+
+static void __exit isa1200_exit(void)

drivers/haptic/isa1200.c line 429

Quoted 10 lines
drivers/haptic/isa1200.c new file +10 -0
+{
+       i2c_del_driver(&isa1200_driver);
+}
+
+module_init(isa1200_init);
+module_exit(isa1200_exit);
+
+MODULE_AUTHOR("Kyungmin Park <[email protected]>");
+MODULE_DESCRIPTION("ISA1200 Haptic Motor driver");
+MODULE_LICENSE("GPL");

---Trilok Soni
http://triloksoni.wordpress.com
http://www.linkedin.com/in/triloksoni

#2 lore raw

Hi Kyungmin,

Quoted 254 lines · Trilok Soni

Hi Kyungmin,

Adding linux-i2c as it is i2c client driver and Ben Dooks for samsung pwm driver API usage.

Quoted 3 lines · Kyungmin Park

I2C based ISA1200 haptic driver support.
This chip supports both internal and external.
But only external configuration are implemented.

Probably following text would look better:

Add Imagis ISA1200 haptics chip driver support. This chip supports internal and external PWM configuration. This patch only supports external PWM configuration.

Quoted 45 lines

new file mode 100644
index 0000000..51c4ea4

drivers/haptic/isa1200.c new file +40 -0
@@ -0,0 +1,429 @@
+/*
+ *  isa1200.c - Haptic Motor
+ *
+ *  Copyright (C) 2009 Samsung Electronics
+ *  Kyungmin Park <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/haptic.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/pwm.h>
+#include <linux/ctype.h>
+#include <linux/workqueue.h>
+#include <linux/i2c/isa1200.h>
+#include "haptic.h"
+
+struct isa1200_chip {
+       struct i2c_client *client;
+       struct pwm_device *pwm;
+       struct haptic_classdev cdev;
+       struct work_struct work;
+       struct timer_list timer;
+
+       unsigned int    len;            /* LDO enable */
+       unsigned int    hen;            /* Haptic enable */
+
+       int enable;
+       int powered;
+
+       int level;
+       int level_max;
+

drivers/haptic/isa1200.c line 50

Quoted 10 lines
drivers/haptic/isa1200.c new file +10 -0
+       int ldo_level;
+};
+
+
+static void isa1200_chip_power_on(struct isa1200_chip *haptic)
+{
+       if (haptic->powered)
+               return;
+       haptic->powered = 1;
+       /* Use smart mode enable control */

You mean here that, enabling smart mode control by writing ISA1200 register over I2C will be added later, right?

drivers/haptic/isa1200.c line 256

Quoted 10 lines
drivers/haptic/isa1200.c new file +10 -0
+
+static void isa1200_setup(struct i2c_client *client)
+{
+       struct isa1200_chip *chip = i2c_get_clientdata(client);
+       int value;
+
+       gpio_set_value(chip->len, 1);
+       udelay(250);
+       gpio_set_value(chip->len, 1);
+

Please clarify:

In your hardware configuration, you are enabling internal LDO above? It may not be true for all the hardware configuration and it might be grounded. If true, please make this as platform data so that it can be selected run time.

drivers/haptic/isa1200.c line 266

Quoted 10 lines
drivers/haptic/isa1200.c new file +10 -0
+       value = isa1200_read_reg(client, ISA1200_SCTRL0);
+       value &= ~ISA1200_LDOADJ_MASK;
+       value |= chip->ldo_level;
+       isa1200_write_reg(client, ISA1200_SCTRL0, value);
+
+       value = ISA1200_HAPDREN | ISA1200_OVERHL | ISA1200_HAPDIGMOD_PWM_IN |
+               ISA1200_PWMMOD_DIVIDER_128;
+       isa1200_write_reg(client, ISA1200_HCTRL0, value);
+
+       value = ISA1200_EXTCLKSEL | ISA1200_BIT6_ON | ISA1200_MOTTYP_LRA |

Probably motor type could be different too. Please see what other parameters you could become as platform data for this chip and can be tuned by h/w designer for the product design.

drivers/haptic/isa1200.c line 267

Quoted 1 line
drivers/haptic/isa1200.c new file +1 -0
+               ISA1200_SMARTEN | ISA1200_SMARTOFFT_64;

Oh. You are enabling smart control here.

drivers/haptic/isa1200.c line 276

Quoted 9 lines
drivers/haptic/isa1200.c new file +9 -0
+       isa1200_write_reg(client, ISA1200_HCTRL1, value);
+
+       value = isa1200_read_reg(client, ISA1200_HCTRL2);
+       value |= ISA1200_SEEN;
+       isa1200_write_reg(client, ISA1200_HCTRL2, value);
+       isa1200_chip_power_off(chip);
+       isa1200_chip_power_on(chip);
+
+       /* TODO */

?? some todo text?

drivers/haptic/isa1200.c line 285

Quoted 9 lines
drivers/haptic/isa1200.c new file +9 -0
+}
+
+static int __devinit isa1200_probe(struct i2c_client *client,
+                       const struct i2c_device_id *id)
+{
+       struct isa1200_chip *chip;
+       struct haptic_platform_data *pdata;
+       int ret;
+

You need to add i2c_check_functionality call with smbus_byte_data.

Quoted 38 lines
drivers/haptic/isa1200.c new file +38 -0
+       pdata = client->dev.platform_data;
+       if (!pdata) {
+               dev_err(&client->dev, "%s: no platform data\n", __func__);
+               return -EINVAL;
+       }
+
+       chip = kzalloc(sizeof(struct isa1200_chip), GFP_KERNEL);
+       if (!chip)
+               return -ENOMEM;
+
+       chip->client = client;
+       chip->cdev.set = isa1200_chip_set;
+       chip->cdev.get = isa1200_chip_get;
+       chip->cdev.show_enable = isa1200_chip_show_enable;
+       chip->cdev.store_enable = isa1200_chip_store_enable;
+       chip->cdev.store_oneshot = isa1200_chip_store_oneshot;
+       chip->cdev.show_level = isa1200_chip_show_level;
+       chip->cdev.store_level = isa1200_chip_store_level;
+       chip->cdev.show_level_max = isa1200_chip_show_level_max;
+       chip->cdev.name = pdata->name;
+       chip->enable = 0;
+       chip->level = PWM_HAPTIC_DEFAULT_LEVEL;
+       chip->level_max = PWM_HAPTIC_DEFAULT_LEVEL;
+       chip->ldo_level = pdata->ldo_level;
+
+       if (pdata->setup_pin)
+               pdata->setup_pin();
+       chip->len = pdata->gpio;
+       chip->hen = pdata->gpio;
+       chip->pwm = pwm_request(pdata->pwm_timer, "haptic");
+       if (IS_ERR(chip->pwm)) {
+               dev_err(&client->dev, "unable to request PWM for haptic.\n");
+               ret = PTR_ERR(chip->pwm);
+               goto error_pwm;
+       }
+
+       INIT_WORK(&chip->work, isa1200_chip_work);
+

drivers/haptic/isa1200.c line 333

Quoted 10 lines
drivers/haptic/isa1200.c new file +10 -0
+       /* register our new haptic device */
+       ret = haptic_classdev_register(&client->dev, &chip->cdev);
+       if (ret < 0) {
+               dev_err(&client->dev, "haptic_classdev_register failed\n");
+               goto error_classdev;
+       }
+
+       ret = sysfs_create_group(&chip->cdev.dev->kobj, &haptic_group);
+       if (ret)
+               goto error_enable;

Why the user of haptics class has to call this? I assume that once the user of haptics class registers with it, the class itself should create the necessary sysfs files and user driver doesn't have to worry about it except providing necessary hooks.

drivers/haptic/isa1200.c line 337

Quoted 4 lines
drivers/haptic/isa1200.c new file +4 -0
+
+       init_timer(&chip->timer);
+       chip->timer.data = (unsigned long)chip;
+       chip->timer.function = &isa1200_chip_timer;

like to use setup_timer?

Quoted 26 lines
drivers/haptic/isa1200.c new file +26 -0
+
+       i2c_set_clientdata(client, chip);
+
+       if (gpio_is_valid(pdata->gpio)) {
+               ret = gpio_request(pdata->gpio, "haptic enable");
+               if (ret)
+                       goto error_gpio;
+               gpio_direction_output(pdata->gpio, 1);
+       }
+
+       isa1200_setup(client);
+
+       printk(KERN_INFO "isa1200 %s registered\n", pdata->name);
+       return 0;
+
+error_gpio:
+       gpio_free(pdata->gpio);
+error_enable:
+       sysfs_remove_group(&chip->cdev.dev->kobj, &haptic_group);
+error_classdev:
+       haptic_classdev_unregister(&chip->cdev);
+error_pwm:
+       pwm_free(chip->pwm);
+       kfree(chip);
+       return ret;
+}

drivers/haptic/isa1200.c line 373

Quoted 10 lines
drivers/haptic/isa1200.c new file +10 -0
+
+static int __devexit isa1200_remove(struct i2c_client *client)
+{
+       struct isa1200_chip *chip = i2c_get_clientdata(client);
+
+       if (gpio_is_valid(chip->len))
+               gpio_free(chip->len);
+
+       sysfs_remove_group(&chip->cdev.dev->kobj, &haptic_group);
+       haptic_classdev_unregister(&chip->cdev);

Where is del_timer_sync and cancel_work ?

Quoted 17 lines
drivers/haptic/isa1200.c new file +17 -0
+       pwm_free(chip->pwm);
+       kfree(chip);
+       return 0;
+}
+
+#ifdef CONFIG_PM
+static int isa1200_suspend(struct i2c_client *client, pm_message_t mesg)
+{
+       struct isa1200_chip *chip = i2c_get_clientdata(client);
+       isa1200_chip_power_off(chip);
+       return 0;
+}
+
+static int isa1200_resume(struct i2c_client *client)
+{
+       isa1200_setup(client);
+       return 0;
Quoted 8 lines

drivers/haptic/isa1200.c line 398

Quoted 8 lines
drivers/haptic/isa1200.c new file +8 -0
+}
+#else
+#define isa1200_suspend                NULL
+#define isa1200_resume         NULL
+#endif
+
+static const struct i2c_device_id isa1200_id[] = {
+       { "isa1200", 0 },

For haptics use-case it is very natural to have multiple instance of these chips to driver different say top-bottom or right-and-left mounted motors to create various patterns. I hope haptics class and this isa1200 is safe for such usage including sysfs naming conventions.

Signature · 7 lines
---Trilok Soni
http://triloksoni.wordpress.com
http://www.linkedin.com/in/triloksoni
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html