5ccbcf999d
- OTG support - Charger - WiFi 5GHz - RTC bug "fixed" (always-on interrupt - made device overheat!) - Kernel version bumped to 5.2.1 - Memory timings - New WM8994 codec board driver (fixes suspend/resume) - Camera's regulator supported now (saves power at least!) - Updated panel driver (still not in mainline, idk why) - MHL support in kernel (fixes boot times and screen, currently disabled in X, not tested) - Other minor kernel chagnes [ci:skip-build]: already built successfully in CI
698 lines
18 KiB
Diff
698 lines
18 KiB
Diff
From f14f1512693476e0a5256fef0bb4d0ad04e662da Mon Sep 17 00:00:00 2001
|
|
From: ryang <decatf@gmail.com>
|
|
Date: Tue, 31 Oct 2017 12:08:54 -0400
|
|
Subject: [PATCH] leds: Add led class support isa1200 vibration motor
|
|
|
|
(cherry picked from commit e510fd2c344d8f1967e186a3fa030ccee201a4d6)
|
|
Signed-off-by: Sergey Larin <cerg2010cerg2010@mail.ru>
|
|
---
|
|
drivers/leds/Kconfig | 8 +
|
|
drivers/leds/Makefile | 1 +
|
|
drivers/leds/leds-isa1200.c | 565 ++++++++++++++++++++++++++++++++++++
|
|
drivers/leds/leds-isa1200.h | 71 +++++
|
|
4 files changed, 645 insertions(+)
|
|
create mode 100644 drivers/leds/leds-isa1200.c
|
|
create mode 100755 drivers/leds/leds-isa1200.h
|
|
|
|
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
|
|
index 760f73a49c9f..14552a9f2718 100644
|
|
--- a/drivers/leds/Kconfig
|
|
+++ b/drivers/leds/Kconfig
|
|
@@ -784,6 +784,14 @@ config LEDS_NIC78BX
|
|
To compile this driver as a module, choose M here: the module
|
|
will be called leds-nic78bx.
|
|
|
|
+config LEDS_ISA1200
|
|
+ tristate "LES support for the ISA1200 motor"
|
|
+ default n
|
|
+ depends on I2C
|
|
+ help
|
|
+ Say Y to enalbe the ISA1200 IC.
|
|
+
|
|
+
|
|
comment "LED Triggers"
|
|
source "drivers/leds/trigger/Kconfig"
|
|
|
|
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
|
|
index 1e9702ebffee..8f2c88f41701 100644
|
|
--- a/drivers/leds/Makefile
|
|
+++ b/drivers/leds/Makefile
|
|
@@ -81,6 +81,7 @@ obj-$(CONFIG_LEDS_MT6323) += leds-mt6323.o
|
|
obj-$(CONFIG_LEDS_LM3692X) += leds-lm3692x.o
|
|
obj-$(CONFIG_LEDS_SC27XX_BLTC) += leds-sc27xx-bltc.o
|
|
obj-$(CONFIG_LEDS_LM3601X) += leds-lm3601x.o
|
|
+obj-$(CONFIG_LEDS_ISA1200) += leds-isa1200.o
|
|
|
|
# LED SPI Drivers
|
|
obj-$(CONFIG_LEDS_CR0014114) += leds-cr0014114.o
|
|
diff --git a/drivers/leds/leds-isa1200.c b/drivers/leds/leds-isa1200.c
|
|
new file mode 100644
|
|
index 000000000000..3f14a38a4cbd
|
|
--- /dev/null
|
|
+++ b/drivers/leds/leds-isa1200.c
|
|
@@ -0,0 +1,565 @@
|
|
+/*
|
|
+ * drivers/motor/isa1200_vibrator.c
|
|
+ *
|
|
+ * Copyright (C) 2011 Samsung Electronics Co. Ltd. All Rights Reserved.
|
|
+ *
|
|
+ * This software is licensed under the terms of the GNU General Public
|
|
+ * License version 2, as published by the Free Software Foundation, and
|
|
+ * may be copied, distributed, and modified under those terms.
|
|
+ *
|
|
+ * 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.
|
|
+ *
|
|
+ */
|
|
+
|
|
+#include <linux/kernel.h>
|
|
+#include <linux/platform_device.h>
|
|
+#include <linux/leds.h>
|
|
+#include <linux/slab.h>
|
|
+
|
|
+#include <linux/mfd/core.h>
|
|
+#include <linux/module.h>
|
|
+
|
|
+
|
|
+#include <linux/hrtimer.h>
|
|
+#include <linux/err.h>
|
|
+#include <linux/gpio.h>
|
|
+#include <linux/pwm.h>
|
|
+#include <linux/mutex.h>
|
|
+#include <linux/clk.h>
|
|
+#include <linux/workqueue.h>
|
|
+
|
|
+#include <linux/of_gpio.h>
|
|
+#include <linux/platform_device.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/device.h>
|
|
+#include <linux/vmalloc.h>
|
|
+#include <linux/fs.h>
|
|
+#include <linux/mm.h>
|
|
+#include <linux/slab.h>
|
|
+#include <linux/i2c.h>
|
|
+#include <linux/delay.h>
|
|
+#include <linux/pinctrl/consumer.h>
|
|
+
|
|
+#include <asm/mach-types.h>
|
|
+#include "leds-isa1200.h"
|
|
+
|
|
+#define AMPLITUDE_MIN 0
|
|
+#define AMPLITUDE_MAX 254
|
|
+
|
|
+
|
|
+struct isa1200_vibrator_drvdata {
|
|
+ struct i2c_client *client;
|
|
+ struct led_classdev cdev;
|
|
+
|
|
+ struct clk *vib_clk;
|
|
+ struct gpio_desc *enable_gpio;
|
|
+
|
|
+ struct pinctrl* pinctrl;
|
|
+ struct pinctrl_state *on_state;
|
|
+ struct pinctrl_state *off_state;
|
|
+
|
|
+ struct workqueue_struct *wq;
|
|
+ struct delayed_work work;
|
|
+
|
|
+ struct hrtimer timer;
|
|
+ spinlock_t lock;
|
|
+ int timeout;
|
|
+ int max_timeout;
|
|
+
|
|
+ bool running;
|
|
+
|
|
+ u8 amplitude;
|
|
+
|
|
+ u8 ctrl0;
|
|
+ u8 ctrl1;
|
|
+ u8 ctrl2;
|
|
+ u8 ctrl4;
|
|
+ u8 pll;
|
|
+ u8 duty;
|
|
+ u8 period;
|
|
+};
|
|
+
|
|
+/////////////////////////////////////////////////////////////////////////////////////
|
|
+
|
|
+static int amplitude_to_duty(int period, int amplitude)
|
|
+{
|
|
+ int duty = (period * (amplitude + AMPLITUDE_MAX)) /
|
|
+ (2 *(AMPLITUDE_MAX - AMPLITUDE_MIN));
|
|
+ return duty;
|
|
+}
|
|
+
|
|
+static int isa1200_vibrator_i2c_write(struct i2c_client *client,
|
|
+ u8 addr, u8 val)
|
|
+{
|
|
+ int error = 0;
|
|
+ error = i2c_smbus_write_byte_data(client, addr, val);
|
|
+ if (error)
|
|
+ printk(KERN_ERR "[VIB] Failed to write addr=[0x%x], val=[0x%x]\n",
|
|
+ addr, val);
|
|
+
|
|
+ return error;
|
|
+}
|
|
+
|
|
+static void isa1200_vibrator_hw_init(struct isa1200_vibrator_drvdata *vib)
|
|
+{
|
|
+ msleep(20);
|
|
+ isa1200_vibrator_i2c_write(vib->client,
|
|
+ HAPTIC_CONTROL_REG0, vib->ctrl0);
|
|
+ isa1200_vibrator_i2c_write(vib->client,
|
|
+ HAPTIC_CONTROL_REG1, vib->ctrl1);
|
|
+ isa1200_vibrator_i2c_write(vib->client,
|
|
+ HAPTIC_CONTROL_REG2, vib->ctrl2);
|
|
+ isa1200_vibrator_i2c_write(vib->client,
|
|
+ HAPTIC_PLL_REG, vib->pll);
|
|
+ isa1200_vibrator_i2c_write(vib->client,
|
|
+ HAPTIC_CONTROL_REG4, vib->ctrl4);
|
|
+ isa1200_vibrator_i2c_write(vib->client,
|
|
+ HAPTIC_PWM_DUTY_REG, vib->period/2);
|
|
+ isa1200_vibrator_i2c_write(vib->client,
|
|
+ HAPTIC_PWM_PERIOD_REG, vib->period);
|
|
+
|
|
+#ifdef MOTOR_DEBUG
|
|
+ printk(KERN_DEBUG "[VIB] ctrl0 = 0x%x\n", vib->ctrl0);
|
|
+ printk(KERN_DEBUG "[VIB] ctrl1 = 0x%x\n", vib->ctrl1);
|
|
+ printk(KERN_DEBUG "[VIB] ctrl2 = 0x%x\n", vib->ctrl2);
|
|
+ printk(KERN_DEBUG "[VIB] pll = 0x%x\n", vib->pll);
|
|
+ printk(KERN_DEBUG "[VIB] ctrl4 = 0x%x\n", vib->ctrl4);
|
|
+ printk(KERN_DEBUG "[VIB] duty = 0x%x\n", vib->period/2);
|
|
+ printk(KERN_DEBUG "[VIB] period = 0x%x\n", vib->period);
|
|
+#endif
|
|
+
|
|
+}
|
|
+
|
|
+static void isa1200_vibrator_on(struct isa1200_vibrator_drvdata *vib)
|
|
+{
|
|
+ int duty = vib->duty;
|
|
+
|
|
+ pr_debug("%s\n", __func__);
|
|
+
|
|
+ if (vib->duty >= vib->period) {
|
|
+ duty -= 3;
|
|
+ }
|
|
+
|
|
+ isa1200_vibrator_i2c_write(vib->client,
|
|
+ HAPTIC_CONTROL_REG0, vib->ctrl0 | CTL0_NORMAL_OP);
|
|
+ isa1200_vibrator_i2c_write(vib->client,
|
|
+ HAPTIC_PWM_DUTY_REG, vib->duty);
|
|
+#ifdef MOTOR_DEBUG
|
|
+ printk(KERN_DEBUG "[VIB] ctrl0 = 0x%x\n", vib->ctrl0 | CTL0_NORMAL_OP);
|
|
+ printk(KERN_DEBUG "[VIB] duty = 0x%x\n", duty);
|
|
+#endif
|
|
+}
|
|
+
|
|
+static void isa1200_vibrator_off(struct isa1200_vibrator_drvdata *vib)
|
|
+{
|
|
+ pr_debug("%s\n", __func__);
|
|
+ isa1200_vibrator_i2c_write(vib->client,
|
|
+ HAPTIC_PWM_DUTY_REG, vib->period/2);
|
|
+ isa1200_vibrator_i2c_write(vib->client,
|
|
+ HAPTIC_CONTROL_REG0, vib->ctrl0);
|
|
+}
|
|
+
|
|
+static void isa1200_vibrator_work(struct work_struct *work)
|
|
+{
|
|
+ struct isa1200_vibrator_drvdata *vib =
|
|
+ container_of(to_delayed_work(work), struct isa1200_vibrator_drvdata, work);
|
|
+ struct i2c_client* client = vib->client;
|
|
+ int err;
|
|
+
|
|
+ pr_debug("%s\n", __func__);
|
|
+
|
|
+ if (vib->timeout == 0) {
|
|
+ if (!vib->running)
|
|
+ return;
|
|
+
|
|
+ vib->running = false;
|
|
+ isa1200_vibrator_off(vib);
|
|
+ clk_disable_unprepare(vib->vib_clk);
|
|
+
|
|
+ if (vib->pinctrl && vib->off_state) {
|
|
+ err = pinctrl_select_state(vib->pinctrl, vib->off_state);
|
|
+ if (err != 0)
|
|
+ dev_err(&client->dev,
|
|
+ "%s: error setting pinctrl off state. err=%d\n", __func__, err);
|
|
+ }
|
|
+
|
|
+ } else {
|
|
+ if (vib->running)
|
|
+ return;
|
|
+
|
|
+ vib->running = true;
|
|
+
|
|
+ if (vib->pinctrl && vib->on_state) {
|
|
+ err = pinctrl_select_state(vib->pinctrl, vib->on_state);
|
|
+ if (err != 0) {
|
|
+ dev_err(&client->dev,
|
|
+ "%s: error setting pinctrl on state. err=%d\n", __func__, err);
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ clk_prepare_enable(vib->vib_clk);
|
|
+ mdelay(1);
|
|
+ isa1200_vibrator_on(vib);
|
|
+ }
|
|
+}
|
|
+
|
|
+static enum hrtimer_restart isa1200_vibrator_timer_func(struct hrtimer *_timer)
|
|
+{
|
|
+ struct isa1200_vibrator_drvdata *vib =
|
|
+ container_of(_timer, struct isa1200_vibrator_drvdata, timer);
|
|
+
|
|
+ vib->timeout = 0;
|
|
+
|
|
+ queue_delayed_work(vib->wq, &vib->work, 0);
|
|
+ return HRTIMER_NORESTART;
|
|
+}
|
|
+
|
|
+/////////////////////////////////////////////////////////////////////////////////////
|
|
+
|
|
+static void isa1200_brightness_set(struct led_classdev *led_cdev,
|
|
+ enum led_brightness value)
|
|
+{
|
|
+ pr_info("%s: value=%d\n", __func__, value);
|
|
+
|
|
+ led_cdev->brightness = value;
|
|
+}
|
|
+
|
|
+static int isa1200_blink_set(struct led_classdev *cdev,
|
|
+ unsigned long *delay_on,
|
|
+ unsigned long *delay_off)
|
|
+{
|
|
+ pr_info("%s\n", __func__);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static ssize_t enable_store(struct device *dev,
|
|
+ struct device_attribute *attr, const char *buf, size_t size)
|
|
+{
|
|
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
|
|
+ struct isa1200_vibrator_drvdata *vib =
|
|
+ container_of(led_cdev, struct isa1200_vibrator_drvdata, cdev);
|
|
+ unsigned long flags;
|
|
+ int value;
|
|
+
|
|
+ sscanf(buf, "%d", &value);
|
|
+ pr_debug("%s timeout=%d\n", __func__, value);
|
|
+
|
|
+#ifdef MOTOR_DEBUG
|
|
+ printk(KERN_DEBUG "[VIB] time = %dms\n", value);
|
|
+#endif
|
|
+ cancel_delayed_work(&vib->work);
|
|
+ hrtimer_cancel(&vib->timer);
|
|
+ vib->timeout = value;
|
|
+ queue_delayed_work(vib->wq, &vib->work, 0);
|
|
+ spin_lock_irqsave(&vib->lock, flags);
|
|
+ if (value > 0) {
|
|
+ if (value > vib->max_timeout)
|
|
+ value = vib->max_timeout;
|
|
+
|
|
+ hrtimer_start(&vib->timer,
|
|
+ ns_to_ktime((u64)value * NSEC_PER_MSEC),
|
|
+ HRTIMER_MODE_REL);
|
|
+ }
|
|
+ spin_unlock_irqrestore(&vib->lock, flags);
|
|
+
|
|
+ return size;
|
|
+}
|
|
+
|
|
+static ssize_t amplitude_show(struct device *dev,
|
|
+ struct device_attribute *attr, char *buf)
|
|
+{
|
|
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
|
|
+ struct isa1200_vibrator_drvdata *vib =
|
|
+ container_of(led_cdev, struct isa1200_vibrator_drvdata, cdev);
|
|
+
|
|
+ return sprintf(buf, "%d\n", vib->amplitude);
|
|
+}
|
|
+
|
|
+static ssize_t amplitude_store(struct device *dev,
|
|
+ struct device_attribute *attr, const char *buf, size_t size)
|
|
+{
|
|
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
|
|
+ struct isa1200_vibrator_drvdata *vib =
|
|
+ container_of(led_cdev, struct isa1200_vibrator_drvdata, cdev);
|
|
+ int amplitude;
|
|
+
|
|
+ sscanf(buf, "%d", &litude);
|
|
+
|
|
+ if (amplitude > AMPLITUDE_MAX)
|
|
+ amplitude = AMPLITUDE_MAX;
|
|
+ else if (amplitude < AMPLITUDE_MIN)
|
|
+ amplitude = AMPLITUDE_MIN;
|
|
+
|
|
+ vib->duty = amplitude_to_duty(vib->period, amplitude);
|
|
+ vib->amplitude = amplitude;
|
|
+
|
|
+ pr_debug("%s: amplitude=%d duty_cycle=%d\n", __func__, amplitude, vib->duty);
|
|
+
|
|
+ return size;
|
|
+}
|
|
+
|
|
+
|
|
+static struct device_attribute isa1200_device_attrs[] = {
|
|
+ __ATTR(enable, S_IWUSR,
|
|
+ NULL,
|
|
+ enable_store),
|
|
+ __ATTR(amplitude, S_IRUGO | S_IWUSR,
|
|
+ amplitude_show,
|
|
+ amplitude_store),
|
|
+};
|
|
+
|
|
+static int isa1200_init_pinctrl(struct isa1200_vibrator_drvdata *ddata)
|
|
+{
|
|
+ struct i2c_client *client = ddata->client;
|
|
+ struct pinctrl *pinctrl;
|
|
+ struct pinctrl_state *on_state, *off_state;
|
|
+ int err = 0;
|
|
+
|
|
+ pinctrl = devm_pinctrl_get(&client->dev);
|
|
+ if (IS_ERR(pinctrl)) {
|
|
+ dev_info(&client->dev,
|
|
+ "%s: not using pinctrl.\n", __func__);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ off_state = pinctrl_lookup_state(pinctrl, "off");
|
|
+ if (IS_ERR(off_state)) {
|
|
+ dev_err(&client->dev,
|
|
+ "%s: error getting pinctrl off state\n", __func__);
|
|
+ err = -ENODEV;
|
|
+ goto err;
|
|
+ }
|
|
+
|
|
+ on_state = pinctrl_lookup_state(pinctrl, "on");
|
|
+ if (IS_ERR(on_state)) {
|
|
+ dev_err(&client->dev,
|
|
+ "%s: error getting pinctrl on state\n", __func__);
|
|
+ err = -ENODEV;
|
|
+ goto err;
|
|
+ }
|
|
+
|
|
+ err = pinctrl_select_state(pinctrl, off_state);
|
|
+ if (err) {
|
|
+ dev_err(&client->dev,
|
|
+ "%s: error setting pinctrl off state. err=%d\n", __func__, err);
|
|
+ err = -ENODEV;
|
|
+ goto err;
|
|
+ }
|
|
+
|
|
+ ddata->pinctrl = pinctrl;
|
|
+ ddata->off_state = off_state;
|
|
+ ddata->on_state = on_state;
|
|
+
|
|
+ return 0;
|
|
+
|
|
+err:
|
|
+ devm_pinctrl_put(pinctrl);
|
|
+ return err;
|
|
+}
|
|
+
|
|
+#ifdef CONFIG_OF
|
|
+static int isa1200_parse_dt(struct i2c_client *client,
|
|
+ struct isa1200_vibrator_drvdata *drvdata)
|
|
+{
|
|
+ struct device_node *np = client->dev.of_node;
|
|
+ struct clk *vib_clk;
|
|
+ int val, error;
|
|
+
|
|
+ drvdata->enable_gpio = devm_gpiod_get_optional(&client->dev,
|
|
+ "enable", GPIOD_OUT_HIGH);
|
|
+ if (IS_ERR(drvdata->enable_gpio)) {
|
|
+ error = PTR_ERR(drvdata->enable_gpio);
|
|
+ dev_err(&client->dev, "Failed to get enable gpio: %d\n", error);
|
|
+ return error;
|
|
+ }
|
|
+
|
|
+ if (!of_property_read_u32(np, "max-timeout", &val))
|
|
+ drvdata->max_timeout = val;
|
|
+ if (!of_property_read_u32(np, "ctrl0", &val))
|
|
+ drvdata->ctrl0 = val;
|
|
+ if (!of_property_read_u32(np, "ctrl1", &val))
|
|
+ drvdata->ctrl1 = val;
|
|
+ if (!of_property_read_u32(np, "ctrl2", &val))
|
|
+ drvdata->ctrl2 = val;
|
|
+ if (!of_property_read_u32(np, "ctrl4", &val))
|
|
+ drvdata->ctrl4 = val;
|
|
+ if (!of_property_read_u32(np, "pll", &val))
|
|
+ drvdata->pll = val;
|
|
+ if (!of_property_read_u32(np, "duty", &val))
|
|
+ drvdata->duty = val;
|
|
+ if (!of_property_read_u32(np, "period", &val))
|
|
+ drvdata->period = val;
|
|
+
|
|
+ vib_clk = of_clk_get_by_name(np, "vibrator-clk");
|
|
+ if (vib_clk == NULL) {
|
|
+ pr_err("%s: error getting clk.\n", __func__);
|
|
+ return -ENODEV;
|
|
+ }
|
|
+ drvdata->vib_clk = vib_clk;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+#else
|
|
+static int isa1200_parse_dt(struct i2c_client *client,
|
|
+ struct isa1200_vibrator_drvdata *drvdata)
|
|
+{
|
|
+ return -EINVAL;
|
|
+}
|
|
+#endif
|
|
+
|
|
+static int isa1200_vibrator_i2c_probe(struct i2c_client *client,
|
|
+ const struct i2c_device_id *id)
|
|
+{
|
|
+ struct isa1200_vibrator_platform_data *pdata = NULL;
|
|
+ struct isa1200_vibrator_drvdata *ddata;
|
|
+ int i, ret = 0;
|
|
+
|
|
+ printk(KERN_DEBUG "[VIB] %s\n", __func__);
|
|
+
|
|
+ ddata = kzalloc(sizeof(struct isa1200_vibrator_drvdata), GFP_KERNEL);
|
|
+ if (NULL == ddata) {
|
|
+ printk(KERN_ERR "[VIB] Failed to alloc memory\n");
|
|
+ ret = -ENOMEM;
|
|
+ goto err_free_mem;
|
|
+ }
|
|
+
|
|
+ if (client->dev.platform_data) {
|
|
+ pdata = client->dev.platform_data;
|
|
+
|
|
+ ddata->enable_gpio = pdata->enable_gpio;
|
|
+ ddata->vib_clk = pdata->get_clk();
|
|
+ ddata->ctrl0 = pdata->ctrl0;
|
|
+ ddata->ctrl1 = pdata->ctrl1;
|
|
+ ddata->ctrl2 = pdata->ctrl2;
|
|
+ ddata->ctrl4 = pdata->ctrl4;
|
|
+ ddata->pll = pdata->pll;
|
|
+ ddata->duty = pdata->duty;
|
|
+ ddata->period = pdata->period;
|
|
+ } else if (client->dev.of_node) {
|
|
+ ret = isa1200_parse_dt(client, ddata);
|
|
+ if (ret) {
|
|
+ pr_err("%s: error parsing device tree\n", __func__);
|
|
+ goto err_free_mem;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ ddata->client = client;
|
|
+
|
|
+ ddata->cdev.name = "isa1200";
|
|
+ ddata->cdev.flags = LED_CORE_SUSPENDRESUME;
|
|
+ ddata->cdev.brightness_set = isa1200_brightness_set;
|
|
+ ddata->cdev.blink_set = isa1200_blink_set;
|
|
+ ddata->cdev.default_trigger = "none";
|
|
+ i2c_set_clientdata(client, ddata);
|
|
+
|
|
+ ret = isa1200_init_pinctrl(ddata);
|
|
+ if (ret)
|
|
+ goto err_free_mem;
|
|
+
|
|
+ isa1200_vibrator_hw_init(ddata);
|
|
+
|
|
+
|
|
+ hrtimer_init(&ddata->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
|
|
+ ddata->timer.function = isa1200_vibrator_timer_func;
|
|
+
|
|
+ ddata->wq = create_singlethread_workqueue("isa1200");
|
|
+ INIT_DELAYED_WORK(&ddata->work, isa1200_vibrator_work);
|
|
+
|
|
+ ret = led_classdev_register(&client->dev, &ddata->cdev);
|
|
+ if (ret < 0)
|
|
+ goto err_free_mem;
|
|
+
|
|
+ for (i = 0; i < ARRAY_SIZE(isa1200_device_attrs); i++) {
|
|
+ ret = device_create_file(ddata->cdev.dev, &isa1200_device_attrs[i]);
|
|
+ if (ret < 0) {
|
|
+ dev_err(&client->dev,
|
|
+ "%s: failed to create sysfs attributes\n", __func__);
|
|
+ goto err_free_mem;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+
|
|
+err_free_mem:
|
|
+ kfree(ddata);
|
|
+ return ret;
|
|
+
|
|
+}
|
|
+
|
|
+static int isa1200_vibrator_i2c_remove(struct i2c_client *client)
|
|
+{
|
|
+ struct isa1200_vibrator_drvdata *ddata = i2c_get_clientdata(client);
|
|
+ struct led_classdev *led_cdev = &ddata->cdev;
|
|
+ int i;
|
|
+
|
|
+ for (i = 0; i < ARRAY_SIZE(isa1200_device_attrs); i++) {
|
|
+ device_remove_file(led_cdev->dev, &isa1200_device_attrs[i]);
|
|
+ }
|
|
+
|
|
+ led_classdev_unregister(led_cdev);
|
|
+
|
|
+ flush_workqueue(ddata->wq);
|
|
+ destroy_workqueue(ddata->wq);
|
|
+ ddata->wq = NULL;
|
|
+
|
|
+ kfree(ddata);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+#if 0
|
|
+static int isa1200_vibrator_suspend(struct device *dev)
|
|
+{
|
|
+ struct i2c_client *client = to_i2c_client(dev);
|
|
+ struct isa1200_vibrator_drvdata *vib = i2c_get_clientdata(client);
|
|
+ gpio_direction_output(vib->gpio_en, 0);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int isa1200_vibrator_resume(struct device *dev)
|
|
+{
|
|
+ struct i2c_client *client = to_i2c_client(dev);
|
|
+ struct isa1200_vibrator_drvdata *vib = i2c_get_clientdata(client);
|
|
+ // isa1200_vibrator_hw_init(ddata);
|
|
+ gpio_direction_output(vib->gpio_en, 1);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static SIMPLE_DEV_PM_OPS(isa1200_pm,
|
|
+ isa1200_vibrator_suspend, isa1200_vibrator_resume);
|
|
+#define ISA1200_PM &isa1200_pm
|
|
+#else
|
|
+#define ISA1200_PM NULL
|
|
+#endif
|
|
+
|
|
+static const struct i2c_device_id isa1200_vibrator_device_id[] = {
|
|
+ {"isa1200_vibrator", 0},
|
|
+ {}
|
|
+};
|
|
+MODULE_DEVICE_TABLE(i2c, isa1200_vibrator_device_id);
|
|
+
|
|
+#ifdef CONFIG_OF
|
|
+static const struct of_device_id isa1200_dt_match[] = {
|
|
+ { .compatible = "samsung_p3,isa1200_vibrator" },
|
|
+ { },
|
|
+};
|
|
+MODULE_DEVICE_TABLE(of, isa1200_dt_match);
|
|
+#endif
|
|
+
|
|
+static struct i2c_driver isa1200_vibrator_i2c_driver = {
|
|
+ .driver = {
|
|
+ .name = "isa1200_vibrator",
|
|
+ .pm = ISA1200_PM,
|
|
+ .of_match_table = of_match_ptr(isa1200_dt_match),
|
|
+ .owner = THIS_MODULE,
|
|
+ },
|
|
+ .probe = isa1200_vibrator_i2c_probe,
|
|
+ .remove = isa1200_vibrator_i2c_remove,
|
|
+ .id_table = isa1200_vibrator_device_id,
|
|
+};
|
|
+
|
|
+module_i2c_driver(isa1200_vibrator_i2c_driver);
|
|
diff --git a/drivers/leds/leds-isa1200.h b/drivers/leds/leds-isa1200.h
|
|
new file mode 100755
|
|
index 000000000000..50713449003c
|
|
--- /dev/null
|
|
+++ b/drivers/leds/leds-isa1200.h
|
|
@@ -0,0 +1,71 @@
|
|
+/* arch/arm/mach-tegra/sec_vibrator.c
|
|
+ *
|
|
+ * Copyright (C) 2011 Samsung Electronics Co. Ltd. All Rights Reserved.
|
|
+ *
|
|
+ * This software is licensed under the terms of the GNU General Public
|
|
+ * License version 2, as published by the Free Software Foundation, and
|
|
+ * may be copied, distributed, and modified under those terms.
|
|
+ *
|
|
+ * 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.
|
|
+ *
|
|
+ */
|
|
+
|
|
+#ifndef _LINUX_SEC_VIBRATOR_H
|
|
+#define _LINUX_SEC_VIBRATOR_H
|
|
+
|
|
+#define HAPTIC_CONTROL_REG0 0x30
|
|
+#define HAPTIC_CONTROL_REG1 0x31
|
|
+#define HAPTIC_CONTROL_REG2 0x32
|
|
+#define HAPTIC_PLL_REG 0x33
|
|
+#define HAPTIC_CONTROL_REG4 0x34
|
|
+#define HAPTIC_PWM_DUTY_REG 0x35
|
|
+#define HAPTIC_PWM_PERIOD_REG 0x36
|
|
+#define HAPTIC_AMPLITUDE_REG 0x37
|
|
+
|
|
+/* HAPTIC_CONTROL_REG0 */
|
|
+#define CTL0_DIVIDER128 0
|
|
+#define CTL0_DIVIDER256 1
|
|
+#define CTL0_DIVIDER512 2
|
|
+#define CTL0_DIVIDER1024 3
|
|
+#define CTL0_13MHZ 1 << 2
|
|
+#define CTL0_PWM_INPUT 1 << 3
|
|
+#define CTL0_PWM_GEN 2 << 3
|
|
+#define CTL0_WAVE_GEN 3 << 3
|
|
+#define CTL0_HIGH_DRIVE 1 << 5
|
|
+#define CTL0_OVER_DR_EN 1 << 6
|
|
+#define CTL0_NORMAL_OP 1 << 7
|
|
+
|
|
+/* HAPTIC_CONTROL_REG1 */
|
|
+#define CTL1_HAPTICOFF_16U 0
|
|
+#define CTL1_HAPTICOFF_32U 1
|
|
+#define CTL1_HAPTICOFF_64U 2
|
|
+#define CTL1_HAPTICOFF_100U 3
|
|
+#define CTL1_HAPTICON_1U 1 << 2
|
|
+#define CTL1_SMART_EN 1 << 3
|
|
+#define CTL1_PLL_EN 1 << 4
|
|
+#define CTL1_ERM_TYPE 1 << 5
|
|
+#define CTL1_DEFAULT 1 << 6
|
|
+#define CTL1_EXT_CLOCK 1 << 7
|
|
+
|
|
+/* HAPTIC_CONTROL_REG2 */
|
|
+#define CTL2_EFFECT_EN 1
|
|
+#define CTL2_START_EFF_EN 1 << 2
|
|
+#define CTL2_SOFT_RESET_EN 1 << 7
|
|
+
|
|
+struct isa1200_vibrator_platform_data {
|
|
+ struct gpio_desc *enable_gpio;
|
|
+ int max_timeout;
|
|
+ u8 ctrl0;
|
|
+ u8 ctrl1;
|
|
+ u8 ctrl2;
|
|
+ u8 ctrl4;
|
|
+ u8 pll;
|
|
+ u8 duty;
|
|
+ u8 period;
|
|
+ struct clk *(*get_clk) (void);
|
|
+};
|
|
+
|
|
+#endif
|
|
--
|
|
2.22.0
|
|
|