diff --git a/arch/arm64/boot/dts/exynos/exynos9810-crownlte_eur_open_26.dts b/arch/arm64/boot/dts/exynos/exynos9810-crownlte_eur_open_26.dts index 6927b570865b..87191a8238ab 100755 --- a/arch/arm64/boot/dts/exynos/exynos9810-crownlte_eur_open_26.dts +++ b/arch/arm64/boot/dts/exynos/exynos9810-crownlte_eur_open_26.dts @@ -12911,16 +12911,25 @@ haptic { status = "okay"; - haptic,max_timeout = <0x2710>; - haptic,multi_frequency = <0x7>; - haptic,duty = <0x9ede 0xbebc 0x9896 0x7270 0x7f27 0x90f4 0x90f4>; - haptic,period = <0xbf68 0xfe50 0xcb73 0x9896 0xa98a 0x9896 0x9896>; + haptic,max_timeout = <10000>; + /* for multi-frequency */ + haptic,multi_frequency = <7>; + haptic,frequency = <1595 1200 1500 2000 1800 2000 2000>; + /* frequency alert low mid high 0 press release */ + haptic,duty = <40527 54036 43228 32421 36023 37108 37108>; + haptic,period = <48828 65104 52083 39062 43402 39062 39062>; haptic,reg2 = <0x82>; - haptic,pwm_id = <0x1>; + haptic,pwm_id = <1>; haptic,regulator_name = "VDD_MOTOR_3P3"; - haptic,normal_ratio = <0x4b>; - haptic,overdrive_ratio = <0x5f>; - haptic,type = "LINEAR_1040"; + haptic,normal_ratio = <83>; + haptic,overdrive_ratio = <95>; + haptic,high_temp_ratio = <60>; + haptic,high_temp_ref = <55>; + pwms = <0xffffffff 0x00 0xf4240 0x00>; + haptic,motor_type = "LINEAR_1040"; + samsung,steps = <6>; + samsung,intensities = <0 3000 4000 6000 8000 10000>; + samsung,haptic_intensities = <0 2000 4000 6000 8000 10000>; }; sound { diff --git a/arch/arm64/boot/dts/exynos/exynos9810-crownlte_kor_26.dts b/arch/arm64/boot/dts/exynos/exynos9810-crownlte_kor_26.dts index c17c6ed386a0..02cf532c4fe2 100644 --- a/arch/arm64/boot/dts/exynos/exynos9810-crownlte_kor_26.dts +++ b/arch/arm64/boot/dts/exynos/exynos9810-crownlte_kor_26.dts @@ -13015,16 +13015,25 @@ haptic { status = "okay"; - haptic,max_timeout = <0x2710>; - haptic,multi_frequency = <0x7>; - haptic,duty = <0x9ede 0xbebc 0x9896 0x7270 0x7f27 0x90f4 0x90f4>; - haptic,period = <0xbf68 0xfe50 0xcb73 0x9896 0xa98a 0x9896 0x9896>; + haptic,max_timeout = <10000>; + /* for multi-frequency */ + haptic,multi_frequency = <7>; + haptic,frequency = <1595 1200 1500 2000 1800 2000 2000>; + /* frequency alert low mid high 0 press release */ + haptic,duty = <40527 54036 43228 32421 36023 37108 37108>; + haptic,period = <48828 65104 52083 39062 43402 39062 39062>; haptic,reg2 = <0x82>; - haptic,pwm_id = <0x1>; + haptic,pwm_id = <1>; haptic,regulator_name = "VDD_MOTOR_3P3"; - haptic,normal_ratio = <0x4b>; - haptic,overdrive_ratio = <0x5f>; - haptic,type = "LINEAR_1040"; + haptic,normal_ratio = <83>; + haptic,overdrive_ratio = <95>; + haptic,high_temp_ratio = <60>; + haptic,high_temp_ref = <55>; + pwms = <0xffffffff 0x00 0xf4240 0x00>; + haptic,motor_type = "LINEAR_1040"; + samsung,steps = <6>; + samsung,intensities = <0 3000 4000 6000 8000 10000>; + samsung,haptic_intensities = <0 2000 4000 6000 8000 10000>; }; sound { diff --git a/arch/arm64/boot/dts/exynos/exynos9810-star2lte_eur_open_26.dts b/arch/arm64/boot/dts/exynos/exynos9810-star2lte_eur_open_26.dts index 7533c3bde16e..82f176efce6a 100755 --- a/arch/arm64/boot/dts/exynos/exynos9810-star2lte_eur_open_26.dts +++ b/arch/arm64/boot/dts/exynos/exynos9810-star2lte_eur_open_26.dts @@ -12877,16 +12877,25 @@ haptic { status = "okay"; - haptic,max_timeout = <0x2710>; - haptic,multi_frequency = <0x7>; - haptic,duty = <0x9ede 0xbebc 0x9896 0x7270 0x7f27 0x90f4 0x90f4>; - haptic,period = <0xbf68 0xfe50 0xcb73 0x9896 0xa98a 0x9896 0x9896>; + haptic,max_timeout = <10000>; + /* for multi-frequency */ + haptic,multi_frequency = <7>; + haptic,frequency = <1595 1200 1500 2000 1800 2000 2000>; + /* frequency alert low mid high 0 press release */ + haptic,duty = <36621 48828 39062 29296 32551 37108 37108>; + haptic,period = <48828 65104 52083 39062 43402 39062 39062>; haptic,reg2 = <0x82>; - haptic,pwm_id = <0x1>; + haptic,pwm_id = <1>; haptic,regulator_name = "VDD_MOTOR_3P3"; - haptic,normal_ratio = <0x4b>; - haptic,overdrive_ratio = <0x5f>; - haptic,type = "LINEAR_1040"; + haptic,normal_ratio = <75>; + haptic,overdrive_ratio = <95>; + haptic,high_temp_ratio = <60>; + haptic,high_temp_ref = <55>; + pwms = <0xffffffff 0x00 0xf4240 0x00>; + haptic,motor_type = "LINEAR_1040"; + samsung,steps = <6>; + samsung,intensities = <0 3000 4000 6000 8000 10000>; + samsung,haptic_intensities = <0 2000 4000 6000 8000 10000>; }; sound { diff --git a/arch/arm64/boot/dts/exynos/exynos9810-star2lte_kor_26.dts b/arch/arm64/boot/dts/exynos/exynos9810-star2lte_kor_26.dts index 31cd67487210..d6e3716821f7 100755 --- a/arch/arm64/boot/dts/exynos/exynos9810-star2lte_kor_26.dts +++ b/arch/arm64/boot/dts/exynos/exynos9810-star2lte_kor_26.dts @@ -12961,16 +12961,25 @@ haptic { status = "okay"; - haptic,max_timeout = <0x2710>; - haptic,multi_frequency = <0x7>; - haptic,duty = <0x9ede 0xbebc 0x9896 0x7270 0x7f27 0x90f4 0x90f4>; - haptic,period = <0xbf68 0xfe50 0xcb73 0x9896 0xa98a 0x9896 0x9896>; + haptic,max_timeout = <10000>; + /* for multi-frequency */ + haptic,multi_frequency = <7>; + haptic,frequency = <1595 1200 1500 2000 1800 2000 2000>; + /* frequency alert low mid high 0 press release */ + haptic,duty = <36621 48828 39062 29296 32551 37108 37108>; + haptic,period = <48828 65104 52083 39062 43402 39062 39062>; haptic,reg2 = <0x82>; - haptic,pwm_id = <0x1>; + haptic,pwm_id = <1>; haptic,regulator_name = "VDD_MOTOR_3P3"; - haptic,normal_ratio = <0x4b>; - haptic,overdrive_ratio = <0x5f>; - haptic,type = "LINEAR_1040"; + haptic,normal_ratio = <75>; + haptic,overdrive_ratio = <95>; + haptic,high_temp_ratio = <60>; + haptic,high_temp_ref = <55>; + pwms = <0xffffffff 0x00 0xf4240 0x00>; + haptic,motor_type = "LINEAR_1040"; + samsung,steps = <6>; + samsung,intensities = <0 3000 4000 6000 8000 10000>; + samsung,haptic_intensities = <0 2000 4000 6000 8000 10000>; }; sound { diff --git a/arch/arm64/boot/dts/exynos/exynos9810-starlte_eur_open_26.dts b/arch/arm64/boot/dts/exynos/exynos9810-starlte_eur_open_26.dts index 173e5773c993..6042d40edb81 100755 --- a/arch/arm64/boot/dts/exynos/exynos9810-starlte_eur_open_26.dts +++ b/arch/arm64/boot/dts/exynos/exynos9810-starlte_eur_open_26.dts @@ -12940,16 +12940,25 @@ haptic { status = "okay"; - haptic,max_timeout = <0x2710>; - haptic,multi_frequency = <0x7>; - haptic,duty = <0x9ede 0xbebc 0x9896 0x7270 0x7f27 0x90f4 0x90f4>; - haptic,period = <0xbf68 0xfe50 0xcb73 0x9896 0xa98a 0x9896 0x9896>; + haptic,max_timeout = <10000>; + /* for multi-frequency */ + haptic,multi_frequency = <7>; + haptic,frequency = <1595 1200 1500 2000 1800 2000 2000>; + /* frequency alert low mid high 0 press release */ + haptic,duty = <34179 45572 36458 27343 30381 37108 37108>; + haptic,period = <48828 65104 52083 39062 43402 39062 39062>; haptic,reg2 = <0x82>; - haptic,pwm_id = <0x1>; + haptic,pwm_id = <1>; haptic,regulator_name = "VDD_MOTOR_3P3"; - haptic,normal_ratio = <0x4b>; - haptic,overdrive_ratio = <0x5f>; - haptic,type = "LINEAR_1040"; + haptic,normal_ratio = <70>; + haptic,overdrive_ratio = <95>; + haptic,high_temp_ratio = <60>; + haptic,high_temp_ref = <55>; + pwms = <0xffffffff 0x00 0xf4240 0x00>; + haptic,motor_type = "LINEAR_1040"; + samsung,steps = <6>; + samsung,intensities = <0 3000 4000 6000 8000 10000>; + samsung,haptic_intensities = <0 2000 4000 6000 8000 10000>; }; sound { diff --git a/arch/arm64/boot/dts/exynos/exynos9810-starlte_kor_26.dts b/arch/arm64/boot/dts/exynos/exynos9810-starlte_kor_26.dts index b3292683d982..1387403aecbd 100755 --- a/arch/arm64/boot/dts/exynos/exynos9810-starlte_kor_26.dts +++ b/arch/arm64/boot/dts/exynos/exynos9810-starlte_kor_26.dts @@ -12998,16 +12998,25 @@ haptic { status = "okay"; - haptic,max_timeout = <0x2710>; - haptic,multi_frequency = <0x7>; - haptic,duty = <0x9ede 0xbebc 0x9896 0x7270 0x7f27 0x90f4 0x90f4>; - haptic,period = <0xbf68 0xfe50 0xcb73 0x9896 0xa98a 0x9896 0x9896>; + haptic,max_timeout = <10000>; + /* for multi-frequency */ + haptic,multi_frequency = <7>; + haptic,frequency = <1595 1200 1500 2000 1800 2000 2000>; + /* frequency alert low mid high 0 press release */ + haptic,duty = <34179 45572 36458 27343 30381 37108 37108>; + haptic,period = <48828 65104 52083 39062 43402 39062 39062>; haptic,reg2 = <0x82>; - haptic,pwm_id = <0x1>; + haptic,pwm_id = <1>; haptic,regulator_name = "VDD_MOTOR_3P3"; - haptic,normal_ratio = <0x4b>; - haptic,overdrive_ratio = <0x5f>; - haptic,type = "LINEAR_1040"; + haptic,normal_ratio = <70>; + haptic,overdrive_ratio = <95>; + haptic,high_temp_ratio = <60>; + haptic,high_temp_ref = <55>; + pwms = <0xffffffff 0x00 0xf4240 0x00>; + haptic,motor_type = "LINEAR_1040"; + samsung,steps = <6>; + samsung,intensities = <0 3000 4000 6000 8000 10000>; + samsung,haptic_intensities = <0 2000 4000 6000 8000 10000>; }; sound { diff --git a/arch/arm64/configs/exynos9810_defconfig b/arch/arm64/configs/exynos9810_defconfig index 1c17a9ac1dba..2ffdb1ed226e 100755 --- a/arch/arm64/configs/exynos9810_defconfig +++ b/arch/arm64/configs/exynos9810_defconfig @@ -622,7 +622,7 @@ CONFIG_COREDUMP=y CONFIG_COMPAT=y CONFIG_KEYS_COMPAT=y CONFIG_KUSER_HELPERS=y -# CONFIG_COMPAT_VDSO is not set +CONFIG_COMPAT_VDSO=y CONFIG_CROSS_COMPILE_ARM32="" # @@ -5216,8 +5216,13 @@ CONFIG_TRUSTONIC_TRUSTED_UI_FB_BLANK=y # CONFIG_SECURE_OS_SUPPORT_MCT_DISABLE is not set CONFIG_TRUSTED_UI_TOUCH_ENABLE=y # CONFIG_TEE is not set -CONFIG_MOTOR_DRV_MAX77705=y -CONFIG_SEC_HAPTIC=y +# CONFIG_MOTOR_DRV_MAX77865 is not set +# CONFIG_MOTOR_DRV_MAX77705 is not set +# CONFIG_SEC_HAPTIC is not set +CONFIG_MAX77705_VIBRATOR=y +# CONFIG_MAX77705_VIB_FOLD_MODEL is not set +CONFIG_SEC_VIBRATOR=y +CONFIG_SEC_VIB_NOTIFIER=y CONFIG_SENSORS_FINGERPRINT=y # CONFIG_SENSORS_FPRINT_SECURE is not set # CONFIG_SENSORS_FINGERPRINT_32BITS_PLATFORM_ONLY is not set diff --git a/arch/arm64/configs/star2lte_defconfig b/arch/arm64/configs/star2lte_defconfig index dff705528b3f..b514607fd84d 100644 --- a/arch/arm64/configs/star2lte_defconfig +++ b/arch/arm64/configs/star2lte_defconfig @@ -33,5 +33,5 @@ CONFIG_CHARGER_MAX77865=y # CONFIG_SENSORS_SSP_LIGHT_MAX_GAIN_2BYTE is not set CONFIG_SENSORS_SSP_STAR=y # CONFIG_SENSORS_SSP_CROWN is not set -CONFIG_MOTOR_DRV_MAX77865=y +# CONFIG_MOTOR_DRV_MAX77865 is not set diff --git a/arch/arm64/configs/starlte_defconfig b/arch/arm64/configs/starlte_defconfig index 488cfd056159..70a41063d0e6 100644 --- a/arch/arm64/configs/starlte_defconfig +++ b/arch/arm64/configs/starlte_defconfig @@ -33,4 +33,4 @@ CONFIG_CHARGER_MAX77865=y # CONFIG_SENSORS_SSP_LIGHT_MAX_GAIN_2BYTE is not set CONFIG_SENSORS_SSP_STAR=y # CONFIG_SENSORS_SSP_CROWN is not set -CONFIG_MOTOR_DRV_MAX77865=y +# CONFIG_MOTOR_DRV_MAX77865 is not set diff --git a/drivers/Kconfig b/drivers/Kconfig index eb61b91f07b2..242bac5180df 100755 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -232,6 +232,8 @@ source "drivers/tee/Kconfig" source "drivers/motor/Kconfig" +source "drivers/vibrator/Kconfig" + source "drivers/fingerprint/Kconfig" source "drivers/gator_5.27/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index c345849e8883..459cedc0b767 100755 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -200,6 +200,9 @@ obj-$(CONFIG_GPS_BCMxxxxx) += gps/ # Motor obj-y += motor/ +# Vibrator +obj-y += vibrator/ + # CCIC obj-y += ccic/ diff --git a/drivers/input/touchscreen/sec_ts/sec_ts.h b/drivers/input/touchscreen/sec_ts/sec_ts.h index 583d00f251fd..381967cd05f1 100755 --- a/drivers/input/touchscreen/sec_ts/sec_ts.h +++ b/drivers/input/touchscreen/sec_ts/sec_ts.h @@ -979,7 +979,7 @@ extern int get_lcd_attached(char *mode); extern int get_lcd_info(char *arg); #endif -#if defined(CONFIG_MOTOR_DRV_MAX77865) || defined(CONFIG_SS_VIBRATOR) +#if defined(CONFIG_MOTOR_DRV_MAX77705) || defined(CONFIG_MAX77705_VIBRATOR) extern int haptic_homekey_press(void); extern int haptic_homekey_release(void); #else diff --git a/drivers/input/touchscreen/sec_ts/y771/sec_ts.h b/drivers/input/touchscreen/sec_ts/y771/sec_ts.h index cedf5457db1e..f5ecdbe79b42 100755 --- a/drivers/input/touchscreen/sec_ts/y771/sec_ts.h +++ b/drivers/input/touchscreen/sec_ts/y771/sec_ts.h @@ -1043,7 +1043,7 @@ extern int get_lcd_attached(char *mode); extern int get_lcd_info(char *arg); #endif -#if defined(CONFIG_MOTOR_DRV_MAX77705) || defined(CONFIG_MOTOR_DRV_MAX77865) || defined(CONFIG_SS_VIBRATOR) +#if defined(CONFIG_MOTOR_DRV_MAX77705) || defined(CONFIG_MAX77705_VIBRATOR) extern int haptic_homekey_press(void); extern int haptic_homekey_release(void); #else diff --git a/drivers/mfd/max77705.c b/drivers/mfd/max77705.c index 50034e8faab7..ba3151608879 100755 --- a/drivers/mfd/max77705.c +++ b/drivers/mfd/max77705.c @@ -64,7 +64,7 @@ static struct mfd_cell max77705_devs[] = { #if defined(CONFIG_CHARGER_MAX77705) { .name = "max77705-charger", }, #endif -#if defined(CONFIG_MOTOR_DRV_MAX77705) +#if defined(CONFIG_MOTOR_DRV_MAX77705) || defined(CONFIG_MAX77705_VIBRATOR) { .name = "max77705-haptic", }, #endif /* CONFIG_MAX77705_HAPTIC */ #if defined(CONFIG_LEDS_MAX77705_RGB) diff --git a/drivers/vibrator/Kconfig b/drivers/vibrator/Kconfig new file mode 100644 index 000000000000..f210dab1b96a --- /dev/null +++ b/drivers/vibrator/Kconfig @@ -0,0 +1,34 @@ +config MAX77705_VIBRATOR + tristate "Maxim MAX77705 vibrator" + default n + depends on MFD_MAX77705 + help + If you say yes here you will get support for the + motor of Maxim MAX77705 PMIC. + To enable this driver, MFD_MAX77705 should be enabled and + MFD_MAX77705 calls this driver. + +config MAX77705_VIB_FOLD_MODEL + tristate "get support for folder status " + default n + depends on MAX77705_VIBRATOR + help + If you enable this feature, + you will get event for folder status + through event_cmd sysfs + +config SEC_VIBRATOR + tristate "sec vibrator" + default n + help + If you say yes here you will get support for the + sec vibrator driver + +config SEC_VIB_NOTIFIER + tristate "use sec vibrator notifier" + default n + depends on SEC_VIBRATOR + help + Enable notifier functions to get motor enable status + and timeout value by register notifiy + If you unsure, please select n diff --git a/drivers/vibrator/Makefile b/drivers/vibrator/Makefile new file mode 100644 index 000000000000..7ff34d58a7f6 --- /dev/null +++ b/drivers/vibrator/Makefile @@ -0,0 +1,6 @@ +# +# Makefile for the vibrator driver ICs with vibrator driver +# + +obj-$(CONFIG_MAX77705_VIBRATOR) += max77705_vibrator.o +obj-$(CONFIG_SEC_VIBRATOR) += sec_vibrator.o diff --git a/drivers/vibrator/max77705_vibrator.c b/drivers/vibrator/max77705_vibrator.c new file mode 100644 index 000000000000..64364794e1d0 --- /dev/null +++ b/drivers/vibrator/max77705_vibrator.c @@ -0,0 +1,733 @@ +/* + * haptic motor driver for max77705_vibrator.c + * + * 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. + */ + +#define pr_fmt(fmt) "[VIB] max77705_vib: " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MOTOR_LRA (1<<7) +#define MOTOR_EN (1<<6) +#define EXT_PWM (0<<5) +#define DIVIDER_128 (1<<1) +#define MRDBTMER_MASK (0x7) +#define MREN (1 << 3) +#define BIASEN (1 << 7) + +/* 1s / 128(divider value) * 10(150Hz -> 1500 so multiply 10) = 78125000 ns */ +#define FREQ_DIVIDER 78125000 + +struct max77705_vibrator_drvdata { + struct max77705_dev *max77705; + struct sec_vibrator_drvdata sec_vib_ddata; + struct i2c_client *i2c; + struct max77705_vibrator_pdata *pdata; + struct pwm_device *pwm; + struct regulator *regulator; + int ratio; + int max_duty; + int period; + int duty; + bool is_high_temp; +#if defined(CONFIG_MAX77705_VIB_FOLD_MODEL) + int event_idx; +#endif +}; + +static int max77705_vib_set_ratio_with_temp(struct device *dev, int temperature) +{ + struct max77705_vibrator_drvdata *ddata = dev_get_drvdata(dev); + struct max77705_vibrator_pdata *pdata = ddata->pdata; + + pr_info("temperature is %d\n", temperature); + + if (temperature >= pdata->high_temp_ref) + ddata->is_high_temp = true; + else + ddata->is_high_temp = false; + + return 0; +} + +static int max77705_vibrator_i2c_enable(struct max77705_vibrator_drvdata *ddata, + bool en) +{ + int ret = max77705_update_reg(ddata->i2c, + MAX77705_PMIC_REG_MCONFIG, + en ? 0xff : 0x0, MOTOR_LRA | MOTOR_EN); + if (ret) + pr_err("i2c LRA and EN update error %d\n", ret); + + return ret; +} + +static void max77705_vibrator_init_reg( + struct max77705_vibrator_drvdata *ddata) +{ + int ret = 0; + + ret = max77705_update_reg(ddata->i2c, + MAX77705_PMIC_REG_MAINCTRL1, 0xff, BIASEN); + if (ret) + pr_err("i2c REG_BIASEN update error %d\n", ret); + + ret = max77705_update_reg(ddata->i2c, + MAX77705_PMIC_REG_MCONFIG, 0xff, MOTOR_LRA); + if (ret) + pr_err("i2c MOTOR_LPA update error %d\n", ret); + + ret = max77705_update_reg(ddata->i2c, + MAX77705_PMIC_REG_MCONFIG, 0xff, DIVIDER_128); + if (ret) + pr_err("i2c DIVIDER_128 update error %d\n", ret); +} + +static int max77705_vibrator_enable(struct device *dev, bool en) +{ + struct max77705_vibrator_drvdata *ddata = dev_get_drvdata(dev); + int ret = 0; + + ret = max77705_vibrator_i2c_enable(ddata, en); + + return ret; +} + +static int max77705_vib_set_intensity(struct device *dev, int intensity) +{ + struct max77705_vibrator_drvdata *ddata = dev_get_drvdata(dev); + int duty = ddata->period >> 1; + int ret = 0; + + if (intensity < -(MAX_INTENSITY) || MAX_INTENSITY < intensity) { + pr_err("%s out of range %d\n", __func__, intensity); + return -EINVAL; + } + + if (intensity == MAX_INTENSITY) + duty = ddata->max_duty; + else if (intensity == -(MAX_INTENSITY)) + duty = ddata->period - ddata->max_duty; + else if (intensity != 0) + duty += (ddata->max_duty - duty) * intensity / MAX_INTENSITY; + + ddata->duty = duty; + + ret = pwm_config(ddata->pwm, duty, ddata->period); + if (ret < 0) { + pr_err("failed to config pwm %d\n", ret); + return ret; + } + if (duty != ddata->period >> 1) { + ret = pwm_enable(ddata->pwm); + if (ret < 0) + pr_err("failed to enable pwm %d\n", ret); + } else { + pwm_disable(ddata->pwm); + } + + return ret; +} + +#if defined(CONFIG_MAX77705_VIB_FOLD_MODEL) +static int set_fold_model_ratio(struct max77705_vibrator_drvdata *ddata) +{ + int ratio = 0; + + switch (ddata->event_idx) { + case EVENT_CMD_FOLDER_OPEN: + ratio = ddata->pdata->fold_open_ratio; + break; + case EVENT_CMD_FOLDER_CLOSE: + ratio = ddata->pdata->fold_close_ratio; + break; + case EVENT_CMD_ACCESSIBILITY_BOOST_ON: + case EVENT_CMD_ACCESSIBILITY_BOOST_OFF: + case EVENT_CMD_NONE: + default: + ratio = ddata->pdata->normal_ratio; + break; + } + + return ratio; +} +#endif + +static int max77705_vib_set_ratio(struct max77705_vibrator_drvdata *ddata) +{ + int ratio; + + if (ddata->is_high_temp) + ratio = ddata->pdata->high_temp_ratio; + else { +#if defined(CONFIG_MAX77705_VIB_FOLD_MODEL) + ratio = set_fold_model_ratio(ddata); +#else + ratio = ddata->pdata->normal_ratio; +#endif + } + + pr_info("ratio set to %d\n", ratio); + + return ratio; +} + +static int max77705_vib_set_frequency(struct device *dev, int num) +{ + struct max77705_vibrator_drvdata *ddata = dev_get_drvdata(dev); + + ddata->ratio = max77705_vib_set_ratio(ddata); + + if (num >= 0 && num < ddata->pdata->freq_nums) { + ddata->period = FREQ_DIVIDER / ddata->pdata->freq_array[num]; + ddata->duty = ddata->max_duty = + (ddata->period * ddata->ratio) / 100; + } else if (num >= HAPTIC_ENGINE_FREQ_MIN && + num <= HAPTIC_ENGINE_FREQ_MAX) { + ddata->period = FREQ_DIVIDER / num; + ddata->duty = ddata->max_duty = + (ddata->period * ddata->ratio) / 100; + } else { + pr_err("%s out of range %d\n", __func__, num); + return -EINVAL; + } + + return 0; +} + +static int max77705_vib_set_overdrive(struct device *dev, bool en) +{ + struct max77705_vibrator_drvdata *ddata = dev_get_drvdata(dev); + + if (ddata->pdata->overdrive_ratio) + ddata->ratio = en ? ddata->pdata->overdrive_ratio : + ddata->pdata->normal_ratio; + + return 0; +} + +static int max77705_vib_get_motor_type(struct device *dev, char *buf) +{ + struct max77705_vibrator_drvdata *ddata = dev_get_drvdata(dev); + + return snprintf(buf, VIB_BUFSIZE, "%s\n", ddata->pdata->motor_type); +} + +#if defined(CONFIG_MAX77705_VIB_FOLD_MODEL) +static int max77705_vib_set_event_cmd(struct device *dev, int idx) +{ + struct max77705_vibrator_drvdata *ddata = dev_get_drvdata(dev); + + ddata->event_idx = idx; + + return 0; +} +#endif + +#if defined(CONFIG_SEC_VIBRATOR) +static bool max77705_get_calibration(struct device *dev) +{ + struct max77705_vibrator_drvdata *ddata = dev_get_drvdata(dev); + struct max77705_vibrator_pdata *pdata = ddata->pdata; + + return pdata->calibration; +} + +static int max77705_get_step_size(struct device *dev, int *step_size) +{ + struct max77705_vibrator_drvdata *ddata = dev_get_drvdata(dev); + struct max77705_vibrator_pdata *pdata = ddata->pdata; + + pr_info("%s step_size=%d\n", __func__, pdata->steps); + + if (pdata->steps == 0) + return -ENODATA; + + *step_size = pdata->steps; + + return 0; +} + + +static int max77705_get_intensities(struct device *dev, int *buf) +{ + struct max77705_vibrator_drvdata *ddata = dev_get_drvdata(dev); + struct max77705_vibrator_pdata *pdata = ddata->pdata; + int i; + + if (pdata->intensities[1] == 0) + return -ENODATA; + + for (i = 0; i < pdata->steps; i++) + buf[i] = pdata->intensities[i]; + + return 0; +} + +static int max77705_set_intensities(struct device *dev, int *buf) +{ + struct max77705_vibrator_drvdata *ddata = dev_get_drvdata(dev); + struct max77705_vibrator_pdata *pdata = ddata->pdata; + int i; + + for (i = 0; i < pdata->steps; i++) + pdata->intensities[i] = buf[i]; + + return 0; +} + +static int max77705_get_haptic_intensities(struct device *dev, int *buf) +{ + struct max77705_vibrator_drvdata *ddata = dev_get_drvdata(dev); + struct max77705_vibrator_pdata *pdata = ddata->pdata; + int i; + + if (pdata->haptic_intensities[1] == 0) + return -ENODATA; + + for (i = 0; i < pdata->steps; i++) + buf[i] = pdata->haptic_intensities[i]; + + return 0; +} + +static int max77705_set_haptic_intensities(struct device *dev, int *buf) +{ + struct max77705_vibrator_drvdata *ddata = dev_get_drvdata(dev); + struct max77705_vibrator_pdata *pdata = ddata->pdata; + int i; + + for (i = 0; i < pdata->steps; i++) + pdata->haptic_intensities[i] = buf[i]; + + return 0; +} +#endif /* if defined(CONFIG_SEC_VIBRATOR) */ + +static const struct sec_vibrator_ops max77705_multi_freq_vib_ops = { + .enable = max77705_vibrator_enable, + .set_intensity = max77705_vib_set_intensity, + .set_frequency = max77705_vib_set_frequency, + .set_overdrive = max77705_vib_set_overdrive, + .set_force_touch_intensity = max77705_vib_set_intensity, + .get_motor_type = max77705_vib_get_motor_type, + .set_tuning_with_temp = max77705_vib_set_ratio_with_temp, +#if defined(CONFIG_MAX77705_VIB_FOLD_MODEL) + .set_event_cmd = max77705_vib_set_event_cmd, +#endif +#if defined(CONFIG_SEC_VIBRATOR) + .get_calibration = max77705_get_calibration, + .get_step_size = max77705_get_step_size, + .get_intensities = max77705_get_intensities, + .set_intensities = max77705_set_intensities, + .get_haptic_intensities = max77705_get_haptic_intensities, + .set_haptic_intensities = max77705_set_haptic_intensities, +#endif +}; + +static const struct sec_vibrator_ops max77705_single_freq_vib_ops = { + .enable = max77705_vibrator_enable, + .set_intensity = max77705_vib_set_intensity, + .get_motor_type = max77705_vib_get_motor_type, + .set_tuning_with_temp = max77705_vib_set_ratio_with_temp, +#if defined(CONFIG_MAX77705_VIB_FOLD_MODEL) + .set_event_cmd = max77705_vib_set_event_cmd, +#endif +#if defined(CONFIG_SEC_VIBRATOR) + .get_calibration = max77705_get_calibration, + .get_step_size = max77705_get_step_size, + .get_intensities = max77705_get_intensities, + .set_intensities = max77705_set_intensities, + .get_haptic_intensities = max77705_get_haptic_intensities, + .set_haptic_intensities = max77705_set_haptic_intensities, +#endif +}; + +#if defined(CONFIG_SEC_VIBRATOR) +static int of_sec_vibrator_dt(struct max77705_vibrator_pdata *pdata, struct device_node *np) +{ + int ret = 0; + int i; + unsigned int val = 0; + int *intensities = NULL; + + pr_info("%s\n", __func__); + pdata->calibration = false; + + /* number of steps */ + ret = of_property_read_u32(np, "samsung,steps", &val); + if (ret) { + pr_err("%s out of range(%d)\n", __func__, val); + return -EINVAL; + } + pdata->steps = (int)val; + + /* allocate memory for intensities */ + pdata->intensities = kmalloc_array(pdata->steps, sizeof(int), GFP_KERNEL); + if (!pdata->intensities) + return -ENOMEM; + intensities = pdata->intensities; + + /* intensities */ + ret = of_property_read_u32_array(np, "samsung,intensities", intensities, pdata->steps); + if (ret) { + pr_err("intensities are not specified\n"); + ret = -EINVAL; + goto err_getting_int; + } + + for (i = 0; i < pdata->steps; i++) { + if ((intensities[i] < 0) || (intensities[i] > MAX_INTENSITY)) { + pr_err("%s out of range(%d)\n", __func__, intensities[i]); + ret = -EINVAL; + goto err_getting_int; + } + } + intensities = NULL; + + /* allocate memory for haptic_intensities */ + pdata->haptic_intensities = kmalloc_array(pdata->steps, sizeof(int), GFP_KERNEL); + if (!pdata->haptic_intensities) { + ret = -ENOMEM; + goto err_alloc_haptic; + } + intensities = pdata->haptic_intensities; + + /* haptic intensities */ + ret = of_property_read_u32_array(np, "samsung,haptic_intensities", intensities, pdata->steps); + if (ret) { + pr_err("haptic_intensities are not specified\n"); + ret = -EINVAL; + goto err_haptic; + } + for (i = 0; i < pdata->steps; i++) { + if ((intensities[i] < 0) || (intensities[i] > MAX_INTENSITY)) { + pr_err("%s out of range(%d)\n", __func__, intensities[i]); + ret = -EINVAL; + goto err_haptic; + } + } + + /* update calibration statue */ + pdata->calibration = true; + + return ret; + +err_haptic: + kfree(pdata->haptic_intensities); +err_alloc_haptic: + pdata->haptic_intensities = NULL; +err_getting_int: + kfree(pdata->intensities); + pdata->intensities = NULL; + pdata->steps = 0; + + return ret; +} +#endif /* if defined(CONFIG_SEC_VIBRATOR) */ + +#if defined(CONFIG_OF) +static struct max77705_vibrator_pdata *of_max77705_vibrator_dt( + struct device *dev) +{ + struct device_node *np_root = dev->parent->of_node; + struct device_node *np; + struct max77705_vibrator_pdata *pdata; + u32 temp; + int ret, i; + + pdata = kzalloc(sizeof(struct max77705_vibrator_pdata), + GFP_KERNEL); + if (!pdata) + return NULL; + + np = of_find_node_by_name(np_root, + "haptic"); + if (np == NULL) { + pr_err("%s: error to get dt node\n", __func__); + goto err_parsing_dt; + } + + ret = of_property_read_u32(np, "haptic,multi_frequency", &temp); + if (ret) { + pr_info("%s: multi_frequency isn't used\n", __func__); + pdata->freq_nums = 0; + } else + pdata->freq_nums = (int)temp; + + if (pdata->freq_nums) { + pdata->freq_array = devm_kzalloc(dev, + sizeof(u32)*pdata->freq_nums, GFP_KERNEL); + if (!pdata->freq_array) { + pr_err("%s: failed to allocate freq_array data\n", + __func__); + goto err_parsing_dt; + } + + ret = of_property_read_u32_array(np, "haptic,frequency", + pdata->freq_array, pdata->freq_nums); + if (ret) { + pr_err("%s: error to get dt node frequency\n", + __func__); + goto err_parsing_dt; + } + + pdata->freq = pdata->freq_array[0]; + } else { + ret = of_property_read_u32(np, + "haptic,frequency", &temp); + if (ret) { + pr_err("%s: error to get dt node frequency\n", + __func__); + goto err_parsing_dt; + } else + pdata->freq = (int)temp; + } + + ret = of_property_read_u32(np, + "haptic,normal_ratio", &temp); + if (ret) { + pr_err("%s: error to get dt node normal_ratio\n", __func__); + goto err_parsing_dt; + } else + pdata->normal_ratio = (int)temp; + + ret = of_property_read_u32(np, + "haptic,overdrive_ratio", &temp); + if (ret) + pr_info("%s: overdrive_ratio isn't used\n", __func__); + else + pdata->overdrive_ratio = (int)temp; + + ret = of_property_read_u32(np, + "haptic,high_temp_ref", &temp); + if (ret) + pr_info("%s: high temperature concept isn't used\n", __func__); + else + pdata->high_temp_ref = (int)temp * 10; + + ret = of_property_read_u32(np, + "haptic,high_temp_ratio", &temp); + if (ret) + pr_info("%s: high_temp_ratio isn't used\n", __func__); + else + pdata->high_temp_ratio = (int)temp; + +#if defined(CONFIG_MAX77705_VIB_FOLD_MODEL) + ret = of_property_read_u32(np, + "haptic,fold_open_ratio", &temp); + if (ret) { + pr_err("%s: error to get dt node fold_open_ratio\n", __func__); + goto err_parsing_dt; + } else + pdata->fold_open_ratio = (int)temp; + + ret = of_property_read_u32(np, + "haptic,fold_close_ratio", &temp); + if (ret) { + pr_err("%s: error to get dt node fold_close_ratio\n", __func__); + goto err_parsing_dt; + } else + pdata->fold_close_ratio = (int)temp; +#endif + + ret = of_property_read_u32(np, + "haptic,pwm_id", &temp); + if (ret) { + pdata->pwm_id = 0; + pdata->pwm = devm_of_pwm_get(dev, np, NULL); + if (IS_ERR(pdata->pwm)) { + pr_err("%s: error to get pwms\n", __func__); + goto err_parsing_dt; + } + } else + pdata->pwm_id = (u16)temp; + + ret = of_property_read_string(np, "haptic,motor_type", + &pdata->motor_type); + if (ret) + pr_err("%s: motor_type is undefined\n", __func__); + + if (pdata->freq_nums) { + pr_info("multi frequency: %d\n", pdata->freq_nums); + for (i = 0; i < pdata->freq_nums; i++) + pr_info("frequency[%d]: %d.%dHz\n", i, + pdata->freq_array[i]/10, + pdata->freq_array[i]%10); + } else { + pr_info("frequency: %d.%dHz\n", pdata->freq/10, pdata->freq%10); + } + +#if defined(CONFIG_SEC_VIBRATOR) + ret = of_sec_vibrator_dt(pdata, np); + if (ret < 0) + pr_err("sec_vibrator dt read fail\n"); +#endif + + pr_info("normal_ratio: %d\n", pdata->normal_ratio); + pr_info("overdrive_ratio: %d\n", pdata->overdrive_ratio); + pr_info("high temperature reference: %d\n", pdata->high_temp_ref); + pr_info("high temperature ratio: %d\n", pdata->high_temp_ratio); +#if defined(CONFIG_MAX77705_VIB_FOLD_MODEL) + pr_info("fold open ratio: %d\n", pdata->fold_open_ratio); + pr_info("fold close ratio: %d\n", pdata->fold_close_ratio); +#endif + pr_info("motor_type: %s\n", pdata->motor_type); + pr_info("pwm_id = %d\n", pdata->pwm_id); + + return pdata; + +err_parsing_dt: + kfree(pdata); + return NULL; +} +#endif + +static int max77705_vibrator_probe(struct platform_device *pdev) +{ + struct max77705_dev *max77705 = dev_get_drvdata(pdev->dev.parent); + struct max77705_platform_data *max77705_pdata + = dev_get_platdata(max77705->dev); + struct max77705_vibrator_pdata *pdata + = max77705_pdata->vibrator_data; + struct max77705_vibrator_drvdata *ddata; + +#if defined(CONFIG_OF) + if (pdata == NULL) { + pdata = of_max77705_vibrator_dt(&pdev->dev); + if (unlikely(!pdata)) { + pr_err("max77705-haptic : %s not found haptic dt!\n", + __func__); + return -ENODEV; + } + } +#else + if (unlikely(!pdata)) { + pr_err("%s: no pdata\n", __func__); + return -ENODEV; + } +#endif /* CONFIG_OF */ + + ddata = kzalloc(sizeof(struct max77705_vibrator_drvdata), GFP_KERNEL); + if (unlikely(!ddata)) { + pr_err("%s: no ddata\n", __func__); + return -ENODEV; + } + + platform_set_drvdata(pdev, ddata); + ddata->max77705 = max77705; + ddata->i2c = max77705->i2c; + ddata->pdata = pdata; + ddata->period = FREQ_DIVIDER / pdata->freq; + ddata->ratio = pdata->normal_ratio; + ddata->max_duty = ddata->duty = ddata->period * ddata->ratio / 100; + + ddata->pwm = pwm_request(pdata->pwm_id, "vibrator"); + if (IS_ERR(ddata->pwm)) + ddata->pwm = pdata->pwm; + + if (IS_ERR(ddata->pwm)) { + pr_err("%s: Failed to get pwm\n", __func__); + return -EFAULT; + } else + pwm_config(ddata->pwm, ddata->duty, ddata->period); + + max77705_vibrator_init_reg(ddata); + max77705_vibrator_i2c_enable(ddata, false); + + ddata->sec_vib_ddata.dev = &pdev->dev; + if (pdata->freq_nums) + ddata->sec_vib_ddata.vib_ops = &max77705_multi_freq_vib_ops; + else + ddata->sec_vib_ddata.vib_ops = &max77705_single_freq_vib_ops; +#if defined(CONFIG_MAX77705_VIB_FOLD_MODEL) + sscanf("FOLD", "%s", ddata->sec_vib_ddata.event_cmd); +#endif + sec_vibrator_register(&ddata->sec_vib_ddata); + + return 0; +} + +static int max77705_vibrator_remove(struct platform_device *pdev) +{ + struct max77705_vibrator_drvdata *ddata + = platform_get_drvdata(pdev); + + sec_vibrator_unregister(&ddata->sec_vib_ddata); + pwm_free(ddata->pwm); + max77705_vibrator_i2c_enable(ddata, false); + return 0; +} + +static int max77705_vibrator_suspend(struct platform_device *pdev, + pm_message_t state) +{ + struct max77705_vibrator_drvdata *ddata + = platform_get_drvdata(pdev); + + if (ddata && &ddata->sec_vib_ddata) { + kthread_flush_worker(&ddata->sec_vib_ddata.kworker); + hrtimer_cancel(&ddata->sec_vib_ddata.timer); + } + + return 0; +} + +static int max77705_vibrator_resume(struct platform_device *pdev) +{ + struct max77705_vibrator_drvdata *ddata + = platform_get_drvdata(pdev); + + max77705_vibrator_init_reg(ddata); + + return 0; +} + +static void max77705_vibrator_shutdown(struct platform_device *pdev) +{ +} + +static struct platform_driver max77705_vibrator_driver = { + .probe = max77705_vibrator_probe, + .remove = max77705_vibrator_remove, + .suspend = max77705_vibrator_suspend, + .resume = max77705_vibrator_resume, + .shutdown = max77705_vibrator_shutdown, + .driver = { + .name = "max77705-haptic", // max77705_vibrator + .owner = THIS_MODULE, + }, +}; + +static int __init max77705_vibrator_init(void) +{ + return platform_driver_register(&max77705_vibrator_driver); +} +module_init(max77705_vibrator_init); + +static void __exit max77705_vibrator_exit(void) +{ + platform_driver_unregister(&max77705_vibrator_driver); +} +module_exit(max77705_vibrator_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("max77705 vibrator driver"); \ No newline at end of file diff --git a/drivers/vibrator/sec_vibrator.c b/drivers/vibrator/sec_vibrator.c new file mode 100644 index 000000000000..4b9ab22b0801 --- /dev/null +++ b/drivers/vibrator/sec_vibrator.c @@ -0,0 +1,1468 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2019-2021 Samsung Electronics Co. Ltd. + */ + +#define pr_fmt(fmt) "[VIB] " fmt +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if defined(CONFIG_SSP_MOTOR_CALLBACK) +#include +#endif +#if IS_ENABLED(CONFIG_SEC_VIB_NOTIFIER) +#include +#endif +#if IS_ENABLED(CONFIG_BATTERY_SAMSUNG_V2) +#include "../battery_v2/include/sec_charging_common.h" +#else +#include "../battery/include/sec_charging_common.h" +#endif + +static const int kMaxBufSize = 7; +static const int kMaxHapticStepSize = 7; +static const char *str_newline = "\n"; + +static struct sec_vibrator_drvdata *g_ddata; + +static char vib_event_cmd[MAX_STR_LEN_EVENT_CMD]; + +static const char sec_vib_event_cmd[EVENT_CMD_MAX][MAX_STR_LEN_EVENT_CMD] = { + [EVENT_CMD_NONE] = "NONE", + [EVENT_CMD_FOLDER_CLOSE] = "FOLDER_CLOSE", + [EVENT_CMD_FOLDER_OPEN] = "FOLDER_OPEN", + [EVENT_CMD_ACCESSIBILITY_BOOST_ON] = "ACCESSIBILITY_BOOST_ON", + [EVENT_CMD_ACCESSIBILITY_BOOST_OFF] = "ACCESSIBILITY_BOOST_OFF", + [EVENT_CMD_TENT_CLOSE] = "FOLDER_TENT_CLOSE", + [EVENT_CMD_TENT_OPEN] = "FOLDER_TENT_OPEN", +}; + +#if IS_ENABLED(CONFIG_SEC_VIB_NOTIFIER) +static struct vib_notifier_context vib_notifier; +static struct blocking_notifier_head sec_vib_nb_head = BLOCKING_NOTIFIER_INIT(sec_vib_nb_head); + +int sec_vib_notifier_register(struct notifier_block *noti_block) +{ + int ret = 0; + + pr_info("%s\n", __func__); + + if (!noti_block) + return -EINVAL; + + ret = blocking_notifier_chain_register(&sec_vib_nb_head, noti_block); + if (ret < 0) + pr_err("%s: failed(%d)\n", __func__, ret); + + return ret; +} +EXPORT_SYMBOL_GPL(sec_vib_notifier_register); + +int sec_vib_notifier_unregister(struct notifier_block *noti_block) +{ + int ret = 0; + + pr_info("%s\n", __func__); + + if (!noti_block) + return -EINVAL; + + ret = blocking_notifier_chain_unregister(&sec_vib_nb_head, noti_block); + if (ret < 0) + pr_err("%s: failed(%d)\n", __func__, ret); + + return ret; +} +EXPORT_SYMBOL_GPL(sec_vib_notifier_unregister); + +static int sec_vib_notifier_notify(int en, struct sec_vibrator_drvdata *ddata) +{ + int ret = 0; + + if (!ddata) { + pr_err("%s : ddata is NULL\n", __func__); + return -ENODATA; + } + + vib_notifier.index = ddata->index; + vib_notifier.timeout = ddata->timeout; + + pr_info("%s: %s, idx: %d timeout: %d\n", __func__, en ? "ON" : "OFF", + vib_notifier.index, vib_notifier.timeout); + + ret = blocking_notifier_call_chain(&sec_vib_nb_head, en, &vib_notifier); + + switch (ret) { + case NOTIFY_DONE: + case NOTIFY_OK: + pr_info("%s done(0x%x)\n", __func__, ret); + break; + default: + pr_info("%s failed(0x%x)\n", __func__, ret); + break; + } + + return ret; +} +#endif /* if IS_ENABLED(CONFIG_SEC_VIB_NOTIFIER) */ + +#if IS_ENABLED(CONFIG_BATTERY_SAMSUNG) +static int sec_vibrator_check_temp(struct sec_vibrator_drvdata *ddata) +{ + int ret = 0; + union power_supply_propval value = {0, }; + + if (!ddata) { + pr_err("%s : ddata is NULL\n", __func__); + return -ENODATA; + } + + if (!ddata->vib_ops->set_tuning_with_temp) + return -ENOSYS; + + psy_do_property("battery", get, POWER_SUPPLY_PROP_TEMP, value); + + ret = ddata->vib_ops->set_tuning_with_temp(ddata->dev, value.intval); + + if (ret) + pr_err("%s error(%d)\n", __func__, ret); + + return ret; +} +#endif +static int sec_vibrator_set_enable(struct sec_vibrator_drvdata *ddata, bool en) +{ + int ret = 0; + + if (!ddata) { + pr_err("%s : ddata is NULL\n", __func__); + return -ENODATA; + } + + if (!ddata->vib_ops->enable) + return -ENOSYS; + + ret = ddata->vib_ops->enable(ddata->dev, en); + if (ret) + pr_err("%s error(%d)\n", __func__, ret); + +#if defined(CONFIG_SSP_MOTOR_CALLBACK) + if (en && ddata->intensity > 0) + setSensorCallback(true, ddata->timeout); + else + setSensorCallback(false, 0); +#endif +#if IS_ENABLED(CONFIG_SEC_VIB_NOTIFIER) + sec_vib_notifier_notify(en, ddata); +#endif + return ret; +} + +static int sec_vibrator_set_intensity(struct sec_vibrator_drvdata *ddata, int intensity) +{ + int ret = 0; + + if (!ddata) { + pr_err("%s : ddata is NULL\n", __func__); + return -ENODATA; + } + + if (!ddata->vib_ops->set_intensity) + return -ENOSYS; + + if ((intensity < -(MAX_INTENSITY)) || (intensity > MAX_INTENSITY)) { + pr_err("%s out of range(%d)\n", __func__, intensity); + return -EINVAL; + } + + ret = ddata->vib_ops->set_intensity(ddata->dev, intensity); + + if (ret) + pr_err("%s error(%d)\n", __func__, ret); + + return ret; +} + +static int sec_vibrator_set_frequency(struct sec_vibrator_drvdata *ddata, int frequency) +{ + int ret = 0; + + if (!ddata) { + pr_err("%s : ddata is NULL\n", __func__); + return -ENODATA; + } + + if (!ddata->vib_ops->set_frequency) + return -ENOSYS; + + if ((frequency < FREQ_ALERT) || + ((frequency >= FREQ_MAX) && (frequency < HAPTIC_ENGINE_FREQ_MIN)) || + (frequency > HAPTIC_ENGINE_FREQ_MAX)) { + pr_err("%s out of range(%d)\n", __func__, frequency); + return -EINVAL; + } + + ret = ddata->vib_ops->set_frequency(ddata->dev, frequency); + + if (ret) + pr_err("%s error(%d)\n", __func__, ret); + + return ret; +} + +static int sec_vibrator_set_overdrive(struct sec_vibrator_drvdata *ddata, bool en) +{ + int ret = 0; + + if (!ddata) { + pr_err("%s : ddata is NULL\n", __func__); + return -ENODATA; + } + + if (!ddata->vib_ops->set_overdrive) + return -ENOSYS; + + ret = ddata->vib_ops->set_overdrive(ddata->dev, en); + if (ret) + pr_err("%s error(%d)\n", __func__, ret); + + return ret; +} + +static void sec_vibrator_haptic_enable(struct sec_vibrator_drvdata *ddata) +{ + if (!ddata) { + pr_err("%s : ddata is NULL\n", __func__); + return; + } + +#if IS_ENABLED(CONFIG_BATTERY_SAMSUNG) + sec_vibrator_check_temp(ddata); +#endif + sec_vibrator_set_frequency(ddata, ddata->frequency); + sec_vibrator_set_intensity(ddata, ddata->intensity); + sec_vibrator_set_enable(ddata, true); + + if (ddata->vib_ops->set_frequency) + pr_info("freq:%d, intensity:%d, %dms\n", ddata->frequency, ddata->intensity, ddata->timeout); + else if (ddata->vib_ops->set_intensity) + pr_info("intensity:%d, %dms\n", ddata->intensity, ddata->timeout); + else + pr_info("%dms\n", ddata->timeout); +} + +static void sec_vibrator_haptic_disable(struct sec_vibrator_drvdata *ddata) +{ + if (!ddata) { + pr_err("%s : ddata is NULL\n", __func__); + return; + } + + /* clear common variables */ + ddata->index = 0; + + /* clear haptic engine variables */ + ddata->f_packet_en = false; + ddata->packet_cnt = 0; + ddata->packet_size = 0; + + sec_vibrator_set_enable(ddata, false); + sec_vibrator_set_overdrive(ddata, false); + sec_vibrator_set_frequency(ddata, FREQ_ALERT); + sec_vibrator_set_intensity(ddata, 0); + + if (ddata->timeout > 0) + pr_info("timeout, off\n"); + else + pr_info("off\n"); +} + +static void sec_vibrator_engine_run_packet(struct sec_vibrator_drvdata *ddata, struct vib_packet packet) +{ + int frequency = packet.freq; + int intensity = packet.intensity; + int overdrive = packet.overdrive; + + if (!ddata) { + pr_err("%s : ddata is NULL\n", __func__); + return; + } + + if (!ddata->f_packet_en) { + pr_err("haptic packet is empty\n"); + return; + } + + sec_vibrator_set_overdrive(ddata, overdrive); + sec_vibrator_set_frequency(ddata, frequency); + if (intensity) { + sec_vibrator_set_intensity(ddata, intensity); + if (!ddata->packet_running) { + pr_info("[haptic engine] motor run\n"); + sec_vibrator_set_enable(ddata, true); + } + ddata->packet_running = true; + } else { + if (ddata->packet_running) { + pr_info("[haptic engine] motor stop\n"); + sec_vibrator_set_enable(ddata, false); + } + ddata->packet_running = false; + sec_vibrator_set_intensity(ddata, intensity); + } + + pr_info("%s [%d] freq:%d, intensity:%d, time:%d overdrive: %d\n", + __func__, ddata->packet_cnt, frequency, intensity, ddata->timeout, overdrive); +} + +static void timed_output_enable(struct sec_vibrator_drvdata *ddata, unsigned int value) +{ + struct hrtimer *timer = &ddata->timer; + int ret = 0; + + if (!ddata) { + pr_err("%s : ddata is NULL\n", __func__); + return; + } + + ret = hrtimer_cancel(timer); + kthread_flush_worker(&ddata->kworker); + + mutex_lock(&ddata->vib_mutex); + + value = min_t(int, value, MAX_TIMEOUT); + ddata->timeout = value; + + if (value) { + if (ddata->f_packet_en) { + ddata->packet_running = false; + ddata->timeout = ddata->vib_pac[0].time; + sec_vibrator_engine_run_packet(ddata, ddata->vib_pac[0]); + } else { + sec_vibrator_haptic_enable(ddata); + } + + if (!ddata->index) + hrtimer_start(timer, ns_to_ktime((u64)ddata->timeout * NSEC_PER_MSEC), HRTIMER_MODE_REL); + } else { + sec_vibrator_haptic_disable(ddata); + } + + mutex_unlock(&ddata->vib_mutex); +} + +#ifdef CONFIG_WAKE_GESTURES +void vib_trigger(int value) +{ + struct sec_vibrator_drvdata *ddata = g_ddata; + + if (!ddata) + pr_err("%s : ddata is NULL\n", __func__); + + timed_output_enable(ddata, value); +} +#endif + +static enum hrtimer_restart haptic_timer_func(struct hrtimer *timer) +{ + struct sec_vibrator_drvdata *ddata; + + if (!timer) { + pr_err("%s : timer is NULL\n", __func__); + return -ENODATA; + } + + ddata = container_of(timer, struct sec_vibrator_drvdata, timer); + + if (!ddata) { + pr_err("%s : ddata is NULL\n", __func__); + return -ENODATA; + } + + pr_info("%s\n", __func__); + kthread_queue_work(&ddata->kworker, &ddata->kwork); + return HRTIMER_NORESTART; +} + +static void sec_vibrator_work(struct kthread_work *work) +{ + struct sec_vibrator_drvdata *ddata; + struct hrtimer *timer; + + if (!work) { + pr_err("%s : work is NULL\n", __func__); + return; + } + + ddata = container_of(work, struct sec_vibrator_drvdata, kwork); + + if (!ddata) { + pr_err("%s : ddata is NULL\n", __func__); + return; + } + + timer = &ddata->timer; + + if (!timer) { + pr_err("%s : timer is NULL\n", __func__); + return; + } + + mutex_lock(&ddata->vib_mutex); + + if (ddata->f_packet_en) { + ddata->packet_cnt++; + if (ddata->packet_cnt < ddata->packet_size) { + ddata->timeout = ddata->vib_pac[ddata->packet_cnt].time; + sec_vibrator_engine_run_packet(ddata, ddata->vib_pac[ddata->packet_cnt]); + hrtimer_start(timer, ns_to_ktime((u64)ddata->timeout * NSEC_PER_MSEC), HRTIMER_MODE_REL); + goto unlock_without_vib_off; + } else { + ddata->f_packet_en = false; + ddata->packet_cnt = 0; + ddata->packet_size = 0; + } + } + + sec_vibrator_haptic_disable(ddata); + +unlock_without_vib_off: + mutex_unlock(&ddata->vib_mutex); +} + +static inline bool is_valid_params(struct device *dev, struct device_attribute *attr, + const char *buf, struct sec_vibrator_drvdata *ddata) +{ + if (!dev) { + pr_err("%s : dev is NULL\n", __func__); + return false; + } + if (!attr) { + pr_err("%s : attr is NULL\n", __func__); + return false; + } + if (!buf) { + pr_err("%s : buf is NULL\n", __func__); + return false; + } + if (!ddata) { + pr_err("%s : ddata is NULL\n", __func__); + return false; + } + return true; +} + +static ssize_t intensity_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct sec_vibrator_drvdata *ddata = g_ddata; + int intensity = 0, ret = 0; + + if (!is_valid_params(dev, attr, buf, ddata)) + return -ENODATA; + + ret = kstrtoint(buf, 0, &intensity); + if (ret) { + pr_err("%s : fail to get intensity\n", __func__); + return -EINVAL; + } + + pr_info("%s %d\n", __func__, intensity); + + if ((intensity < 0) || (intensity > MAX_INTENSITY)) { + pr_err("[VIB]: %s out of range\n", __func__); + return -EINVAL; + } + + ddata->intensity = intensity; + + return count; +} + +static ssize_t intensity_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct sec_vibrator_drvdata *ddata = g_ddata; + + if (!is_valid_params(dev, attr, buf, ddata)) + return -ENODATA; + return snprintf(buf, VIB_BUFSIZE, "intensity: %u\n", ddata->intensity); +} + +static ssize_t force_touch_intensity_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct sec_vibrator_drvdata *ddata = g_ddata; + int intensity = 0, ret = 0; + + if (!is_valid_params(dev, attr, buf, ddata)) + return -ENODATA; + + ret = kstrtoint(buf, 0, &intensity); + if (ret) { + pr_err("fail to get intensity\n"); + return -EINVAL; + } + + pr_debug("%s %d\n", __func__, intensity); + + if ((intensity < 0) || (intensity > MAX_INTENSITY)) { + pr_err("[VIB]: %s out of range\n", __func__); + return -EINVAL; + } + + ddata->force_touch_intensity = intensity; + + return count; +} +static ssize_t force_touch_intensity_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sec_vibrator_drvdata *ddata = g_ddata; + + if (!is_valid_params(dev, attr, buf, ddata)) + return -ENODATA; + return snprintf(buf, VIB_BUFSIZE, + "force touch intensity: %u\n", ddata->force_touch_intensity); +} + +static ssize_t multi_freq_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct sec_vibrator_drvdata *ddata = g_ddata; + int num, ret; + + if (!is_valid_params(dev, attr, buf, ddata)) + return -ENODATA; + + ret = kstrtoint(buf, 0, &num); + if (ret) { + pr_err("fail to get frequency\n"); + return -EINVAL; + } + + pr_info("%s %d\n", __func__, num); + + ddata->frequency = num; + + return count; +} + +static ssize_t multi_freq_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct sec_vibrator_drvdata *ddata = g_ddata; + + if (!is_valid_params(dev, attr, buf, ddata)) + return -ENODATA; + return snprintf(buf, VIB_BUFSIZE, "frequency: %d\n", ddata->frequency); +} + +// TODO: need to update +static ssize_t haptic_engine_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct sec_vibrator_drvdata *ddata = g_ddata; + int index = 0, _data = 0, tmp = 0; + + if (!is_valid_params(dev, attr, buf, ddata)) + return -ENODATA; + + if (sscanf(buf, "%6d", &_data) != 1) + return count; + + if (_data > PACKET_MAX_SIZE * VIB_PACKET_MAX) { + pr_info("%s, [%d] packet size over\n", __func__, _data); + return count; + } + ddata->packet_size = _data / VIB_PACKET_MAX; + ddata->packet_cnt = 0; + ddata->f_packet_en = true; + buf = strstr(buf, " "); + + for (index = 0; index < ddata->packet_size; index++) { + for (tmp = 0; tmp < VIB_PACKET_MAX; tmp++) { + if (buf == NULL) { + pr_err("%s, buf is NULL, Please check packet data again\n", __func__); + ddata->f_packet_en = false; + return count; + } + + if (sscanf(buf++, "%6d", &_data) != 1) { + pr_err("%s, packet data error, Please check packet data again\n", __func__); + ddata->f_packet_en = false; + return count; + } + + switch (tmp) { + case VIB_PACKET_TIME: + ddata->vib_pac[index].time = _data; + break; + case VIB_PACKET_INTENSITY: + ddata->vib_pac[index].intensity = _data; + break; + case VIB_PACKET_FREQUENCY: + ddata->vib_pac[index].freq = _data; + break; + case VIB_PACKET_OVERDRIVE: + ddata->vib_pac[index].overdrive = _data; + break; + } + buf = strstr(buf, " "); + } + } + + return count; +} + +static ssize_t haptic_engine_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct sec_vibrator_drvdata *ddata = g_ddata; + int index = 0; + size_t size = 0; + + if (!is_valid_params(dev, attr, buf, ddata)) + return -ENODATA; + + for (index = 0; index < ddata->packet_size && ddata->f_packet_en && + ((4 * VIB_BUFSIZE + size) < PAGE_SIZE); index++) { + size += snprintf(&buf[size], VIB_BUFSIZE, "%u,", ddata->vib_pac[index].time); + size += snprintf(&buf[size], VIB_BUFSIZE, "%u,", ddata->vib_pac[index].intensity); + size += snprintf(&buf[size], VIB_BUFSIZE, "%u,", ddata->vib_pac[index].freq); + size += snprintf(&buf[size], VIB_BUFSIZE, "%u,", ddata->vib_pac[index].overdrive); + } + + return size; +} + +static ssize_t cp_trigger_index_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct sec_vibrator_drvdata *ddata = g_ddata; + int ret = 0; + + if (!is_valid_params(dev, attr, buf, ddata)) + return -ENODATA; + + if (!ddata->vib_ops->get_cp_trigger_index) + return -ENOSYS; + + ret = ddata->vib_ops->get_cp_trigger_index(ddata->dev, buf); + + return ret; +} + +static ssize_t cp_trigger_index_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct sec_vibrator_drvdata *ddata = g_ddata; + int ret = 0; + unsigned int index; + + if (!is_valid_params(dev, attr, buf, ddata)) + return -ENODATA; + + if (!ddata->vib_ops->set_cp_trigger_index) + return -ENOSYS; + + ret = kstrtou32(buf, 10, &index); + if (ret) + return -EINVAL; + + ddata->index = index; + + ret = ddata->vib_ops->set_cp_trigger_index(ddata->dev, buf); + if (ret < 0) + pr_err("%s error(%d)\n", __func__, ret); + + return count; +} + +static ssize_t cp_trigger_queue_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct sec_vibrator_drvdata *ddata = g_ddata; + int ret = 0; + + if (!is_valid_params(dev, attr, buf, ddata)) + return -ENODATA; + + if (!ddata->vib_ops->get_cp_trigger_queue) + return -ENOSYS; + + ret = ddata->vib_ops->get_cp_trigger_queue(ddata->dev, buf); + if (ret < 0) + pr_err("%s error(%d)\n", __func__, ret); + + return ret; +} + +static ssize_t cp_trigger_queue_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct sec_vibrator_drvdata *ddata = g_ddata; + int ret = 0; + + if (!is_valid_params(dev, attr, buf, ddata)) + return -ENODATA; + + if (!ddata->vib_ops->set_cp_trigger_queue) + return -ENOSYS; + + ret = ddata->vib_ops->set_cp_trigger_queue(ddata->dev, buf); + if (ret < 0) + pr_err("%s error(%d)\n", __func__, ret); + + return count; +} + +static ssize_t pwle_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct sec_vibrator_drvdata *ddata = g_ddata; + int ret = 0; + + if (!is_valid_params(dev, attr, buf, ddata)) + return -ENODATA; + + if (!ddata->vib_ops->get_pwle) + return -ENOSYS; + + ret = ddata->vib_ops->get_pwle(ddata->dev, buf); + if (ret < 0) + pr_err("%s error(%d)\n", __func__, ret); + + return ret; +} + +static ssize_t pwle_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct sec_vibrator_drvdata *ddata = g_ddata; + int ret = 0; + + if (!is_valid_params(dev, attr, buf, ddata)) + return -ENODATA; + + if (!ddata->vib_ops->set_pwle) + return -ENOSYS; + + ret = ddata->vib_ops->set_pwle(ddata->dev, buf); + if (ret < 0) + pr_err("%s error(%d)\n", __func__, ret); + + return count; +} + +static ssize_t virtual_composite_indexes_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct sec_vibrator_drvdata *ddata = g_ddata; + int ret = 0; + + if (!is_valid_params(dev, attr, buf, ddata)) + return -ENODATA; + + if (!ddata->vib_ops->get_virtual_composite_indexes) + return -ENOSYS; + + ret = ddata->vib_ops->get_virtual_composite_indexes(ddata->dev, buf); + if (ret < 0) + pr_err("%s error(%d)\n", __func__, ret); + + return ret; +} + +static ssize_t virtual_pwle_indexes_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct sec_vibrator_drvdata *ddata = g_ddata; + int ret = 0; + + if (!is_valid_params(dev, attr, buf, ddata)) + return -ENODATA; + + if (!ddata->vib_ops->get_virtual_pwle_indexes) + return -ENOSYS; + + ret = ddata->vib_ops->get_virtual_pwle_indexes(ddata->dev, buf); + if (ret < 0) + pr_err("%s error(%d)\n", __func__, ret); + + return ret; +} + +static ssize_t enable_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct sec_vibrator_drvdata *ddata = g_ddata; + struct hrtimer *timer = &ddata->timer; + int remaining = 0; + + if (!is_valid_params(dev, attr, buf, ddata)) + return -ENODATA; + + if (hrtimer_active(timer)) { + ktime_t remain = hrtimer_get_remaining(timer); + struct timeval t = ktime_to_timeval(remain); + + remaining = t.tv_sec * 1000 + t.tv_usec / 1000; + } + return sprintf(buf, "%d\n", remaining); +} + +static ssize_t enable_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t size) +{ + struct sec_vibrator_drvdata *ddata = g_ddata; + int value; + int ret; + + if (!is_valid_params(dev, attr, buf, ddata)) + return -ENODATA; + + ret = kstrtoint(buf, 0, &value); + if (ret != 0) + return -EINVAL; + + timed_output_enable(ddata, value); + return size; +} + +static ssize_t motor_type_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct sec_vibrator_drvdata *ddata = g_ddata; + int ret = 0; + + if (!is_valid_params(dev, attr, buf, ddata)) + return -ENODATA; + + if (!ddata->vib_ops->get_motor_type) + return snprintf(buf, VIB_BUFSIZE, "NONE\n"); + + ret = ddata->vib_ops->get_motor_type(ddata->dev, buf); + + return ret; +} + +static ssize_t use_sep_index_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t size) +{ + struct sec_vibrator_drvdata *ddata = g_ddata; + int ret = 0; + bool use_sep_index; + + if (!is_valid_params(dev, attr, buf, ddata)) + return -ENODATA; + + ret = kstrtobool(buf, &use_sep_index); + if (ret < 0) { + pr_err("%s kstrtobool error : %d\n", __func__, ret); + goto err; + } + + pr_info("%s use_sep_index:%d\n", __func__, use_sep_index); + + if (ddata->vib_ops->set_use_sep_index) { + ret = ddata->vib_ops->set_use_sep_index(ddata->dev, use_sep_index); + if (ret) { + pr_err("%s set_use_sep_index error : %d\n", __func__, ret); + goto err; + } + } else { + pr_info("%s this model doesn't need use_sep_index\n", __func__); + } +err: + return ret ? ret : size; +} + +static ssize_t num_waves_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct sec_vibrator_drvdata *ddata = g_ddata; + int ret = 0; + + if (!is_valid_params(dev, attr, buf, ddata)) + return -ENODATA; + + if (!ddata->vib_ops->get_num_waves) + return -ENOSYS; + + ret = ddata->vib_ops->get_num_waves(ddata->dev, buf); + + return ret; +} + +static ssize_t array2str(char *buf, int *arr_intensity, int size) +{ + int index, ret = 0; + char *str_buf = NULL; + struct sec_vibrator_drvdata *ddata = g_ddata; + + if (!buf || !ddata) + return -ENODATA; + + if (!arr_intensity || ((size < 1) && (size > kMaxHapticStepSize))) + return -EINVAL; + + str_buf = kzalloc(kMaxBufSize, GFP_KERNEL); + if (!str_buf) + return -ENOMEM; + + mutex_lock(&ddata->vib_mutex); + for (index = 0; index < size; index++) { + if (index < (size - 1)) + snprintf(str_buf, kMaxBufSize, "%u,", arr_intensity[index]); + else + snprintf(str_buf, (kMaxBufSize - 1), "%u", arr_intensity[index]); + strncat(buf, str_buf, strlen(str_buf)); + } + strncat(buf, str_newline, strlen(str_newline)); + ret = strlen(buf); + mutex_unlock(&ddata->vib_mutex); + + kfree(str_buf); + return ret; +} + +static ssize_t intensities_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sec_vibrator_drvdata *ddata = g_ddata; + int *arr_intensity = NULL; + int ret = 0, step_size = 0; + + pr_info("%s\n", __func__); + + if (!is_valid_params(dev, attr, buf, ddata)) + return -ENODATA; + + if (!ddata->vib_ops->get_step_size || !ddata->vib_ops->get_intensities) + return -EOPNOTSUPP; + + ret = ddata->vib_ops->get_step_size(ddata->dev, &step_size); + if (ret) + return -EINVAL; + + arr_intensity = kmalloc_array(kMaxHapticStepSize, sizeof(int), GFP_KERNEL); + if (!arr_intensity) + return -ENOMEM; + + if ((step_size > 0) && (step_size < kMaxHapticStepSize)) { + ret = ddata->vib_ops->get_intensities(ddata->dev, arr_intensity); + if (ret) { + ret = -EINVAL; + goto err_arr_alloc; + } + ret = array2str(buf, arr_intensity, step_size); + } else { + ret = -EINVAL; + } + +err_arr_alloc: + kfree(arr_intensity); + return ret; +} + +static ssize_t haptic_intensities_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sec_vibrator_drvdata *ddata = g_ddata; + int *arr_intensity = NULL; + int ret = 0, step_size = 0; + + pr_info("%s\n", __func__); + + if (!is_valid_params(dev, attr, buf, ddata)) + return -ENODATA; + + if (!ddata->vib_ops->get_step_size || !ddata->vib_ops->get_haptic_intensities) + return -EOPNOTSUPP; + + ret = ddata->vib_ops->get_step_size(ddata->dev, &step_size); + if (ret) + return -EINVAL; + + arr_intensity = kmalloc_array(kMaxHapticStepSize, sizeof(int), GFP_KERNEL); + if (!arr_intensity) + return -ENOMEM; + + if ((step_size > 0) && (step_size < kMaxHapticStepSize)) { + ret = ddata->vib_ops->get_haptic_intensities(ddata->dev, arr_intensity); + if (ret) { + ret = -EINVAL; + goto err_arr_alloc; + } + ret = array2str(buf, arr_intensity, step_size); + } else { + ret = -EINVAL; + } + +err_arr_alloc: + kfree(arr_intensity); + return ret; +} + +static ssize_t haptic_durations_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sec_vibrator_drvdata *ddata = g_ddata; + int *arr_duration = NULL; + int ret = 0, step_size = 0; + + pr_info("%s\n", __func__); + + if (!is_valid_params(dev, attr, buf, ddata)) + return -ENODATA; + + if (!ddata->vib_ops->get_step_size || !ddata->vib_ops->get_haptic_durations) + return -EOPNOTSUPP; + + ret = ddata->vib_ops->get_step_size(ddata->dev, &step_size); + if (ret) + return -EINVAL; + + arr_duration = kmalloc_array(kMaxHapticStepSize, sizeof(int), GFP_KERNEL); + if (!arr_duration) + return -ENOMEM; + + if ((step_size > 0) && (step_size < kMaxHapticStepSize)) { + ret = ddata->vib_ops->get_haptic_durations(ddata->dev, arr_duration); + if (ret) { + ret = -EINVAL; + goto err_arr_alloc; + } + ret = array2str(buf, arr_duration, step_size); + } else { + ret = -EINVAL; + } + +err_arr_alloc: + kfree(arr_duration); + return ret; +} + +static int get_event_index_by_command(char *cur_cmd) +{ + int cmd_idx; + + for (cmd_idx = 0; cmd_idx < EVENT_CMD_MAX; cmd_idx++) { + if (!strcmp(cur_cmd, sec_vib_event_cmd[cmd_idx])) + return cmd_idx; + } + + return EVENT_CMD_NONE; +} + +static ssize_t event_cmd_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sec_vibrator_drvdata *ddata = g_ddata; + + pr_info("%s event: %s\n", __func__, vib_event_cmd); + + if (!is_valid_params(dev, attr, buf, ddata)) + return -ENODATA; + + return snprintf(buf, MAX_STR_LEN_EVENT_CMD, "%s\n", vib_event_cmd); +} + +static ssize_t event_cmd_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct sec_vibrator_drvdata *ddata = g_ddata; + char *cmd; + int ret = 0; + int event_idx; + + if (!is_valid_params(dev, attr, buf, ddata)) + return -ENODATA; + + if (!ddata->vib_ops->set_event_cmd) + return -ENOSYS; + + if (count > MAX_STR_LEN_EVENT_CMD) { + pr_err("%s: size(%zu) is too long.\n", __func__, count); + goto error; + } + + cmd = kzalloc(count + 1, GFP_KERNEL); + if (!cmd) + goto error; + + ret = sscanf(buf, "%s", cmd); + if (ret != 1) + goto error1; + + event_idx = get_event_index_by_command(cmd); + + pr_info("%s: event: %s(%d)\n", __func__, cmd, event_idx); + + ret = ddata->vib_ops->set_event_cmd(ddata->dev, event_idx); + if (ret) + pr_err("%s error(%d)\n", __func__, ret); + + sscanf(cmd, "%s", vib_event_cmd); + +error1: + kfree(cmd); +error: + return count; +} + +static DEVICE_ATTR_RW(haptic_engine); +static DEVICE_ATTR_RW(multi_freq); +static DEVICE_ATTR_RW(intensity); +static DEVICE_ATTR_RW(force_touch_intensity); +static DEVICE_ATTR_RW(cp_trigger_index); +static DEVICE_ATTR_RW(cp_trigger_queue); +static DEVICE_ATTR_RW(pwle); +static DEVICE_ATTR_RO(virtual_composite_indexes); +static DEVICE_ATTR_RO(virtual_pwle_indexes); +static DEVICE_ATTR_RW(enable); +static DEVICE_ATTR_RO(motor_type); +static DEVICE_ATTR_WO(use_sep_index); +static DEVICE_ATTR_RO(num_waves); +static DEVICE_ATTR_RO(intensities); +static DEVICE_ATTR_RO(haptic_intensities); +static DEVICE_ATTR_RO(haptic_durations); +static DEVICE_ATTR_RW(event_cmd); + +static struct attribute *sec_vibrator_attributes[] = { + &dev_attr_enable.attr, + &dev_attr_motor_type.attr, + &dev_attr_use_sep_index.attr, + NULL, +}; + +static struct attribute_group sec_vibrator_attr_group = { + .attrs = sec_vibrator_attributes, +}; + +static struct attribute *multi_freq_attributes[] = { + &dev_attr_haptic_engine.attr, + &dev_attr_multi_freq.attr, + NULL, +}; + +static struct attribute_group multi_freq_attr_group = { + .attrs = multi_freq_attributes, +}; + +static struct attribute *cp_trigger_attributes[] = { + &dev_attr_haptic_engine.attr, + &dev_attr_num_waves.attr, + &dev_attr_cp_trigger_index.attr, + &dev_attr_cp_trigger_queue.attr, + NULL, +}; + +static struct attribute_group cp_trigger_attr_group = { + .attrs = cp_trigger_attributes, +}; + +static struct attribute *pwle_attributes[] = { + &dev_attr_pwle.attr, + &dev_attr_virtual_composite_indexes.attr, + &dev_attr_virtual_pwle_indexes.attr, + NULL, +}; + +static struct attribute_group pwle_attr_group = { + .attrs = pwle_attributes, +}; + +int sec_vibrator_register(struct sec_vibrator_drvdata *ddata) +{ + struct task_struct *kworker_task; + int ret = 0; + + if (!ddata) { + pr_err("%s no ddata\n", __func__); + return -ENODEV; + } + + g_ddata = ddata; + + mutex_init(&ddata->vib_mutex); + kthread_init_worker(&ddata->kworker); + kworker_task = kthread_run(kthread_worker_fn, &ddata->kworker, "sec_vibrator"); + + if (IS_ERR(kworker_task)) { + pr_err("Failed to create message pump task\n"); + ret = -ENOMEM; + goto err_kthread; + } + + kthread_init_work(&ddata->kwork, sec_vibrator_work); + hrtimer_init(&ddata->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + ddata->timer.function = haptic_timer_func; + + /* create /sys/class/timed_output/vibrator */ + ddata->to_class = class_create(THIS_MODULE, "timed_output"); + if (IS_ERR(ddata->to_class)) { + ret = PTR_ERR(ddata->to_class); + goto err_class_create; + } + ddata->to_dev = device_create(ddata->to_class, NULL, MKDEV(0, 0), ddata, "vibrator"); + if (IS_ERR(ddata->to_dev)) { + ret = PTR_ERR(ddata->to_dev); + goto err_device_create; + } + + ret = sysfs_create_group(&ddata->to_dev->kobj, &sec_vibrator_attr_group); + if (ret) { + ret = -ENODEV; + pr_err("Failed to create sysfs1 %d\n", ret); + goto err_sysfs1; + } + + if (ddata->vib_ops->set_intensity) { + ret = sysfs_create_file(&ddata->to_dev->kobj, &dev_attr_intensity.attr); + if (ret) { + ret = -ENODEV; + pr_err("Failed to create sysfs2 %d\n", ret); + goto err_sysfs2; + } + + ddata->intensity = MAX_INTENSITY; + + ret = sysfs_create_file(&ddata->to_dev->kobj, &dev_attr_force_touch_intensity.attr); + if (ret) { + ret = -ENODEV; + pr_err("Failed to create sysfs3 %d\n", ret); + goto err_sysfs3; + } + + ddata->force_touch_intensity = MAX_INTENSITY; + } + + if (ddata->vib_ops->set_frequency) { + ret = sysfs_create_group(&ddata->to_dev->kobj, &multi_freq_attr_group); + if (ret) { + ret = -ENODEV; + pr_err("Failed to create sysfs4 %d\n", ret); + goto err_sysfs4; + } + + ddata->frequency = FREQ_ALERT; + } + + if (ddata->vib_ops->set_cp_trigger_index) { + ret = sysfs_create_group(&ddata->to_dev->kobj, &cp_trigger_attr_group); + if (ret) { + ret = -ENODEV; + pr_err("Failed to create sysfs5 %d\n", ret); + goto err_sysfs5; + } + + } + + if (ddata->vib_ops->set_pwle) { + ret = sysfs_create_group(&ddata->to_dev->kobj, &pwle_attr_group); + if (ret) { + ret = -ENODEV; + pr_err("Failed to create sysfs6 %d\n", ret); + goto err_sysfs6; + } + + } + + if (ddata->vib_ops->set_event_cmd) { + /* initialize event_cmd string for HAL init */ + sscanf(ddata->event_cmd, "%s", vib_event_cmd); + pr_info("%s: vib_event_cmd: %s\n", __func__, vib_event_cmd); + + ret = sysfs_create_file(&ddata->to_dev->kobj, &dev_attr_event_cmd.attr); + if (ret) { + ret = -ENODEV; + pr_err("Failed to create sysfs7 %d\n", ret); + goto err_sysfs7; + } + } + + if (ddata->vib_ops->get_calibration && ddata->vib_ops->get_calibration(ddata->dev)) { + if (ddata->vib_ops->get_intensities) { + ret = sysfs_create_file(&ddata->to_dev->kobj, &dev_attr_intensities.attr); + if (ret) { + ret = -ENODEV; + pr_err("Failed to create intensities %d\n", ret); + goto err_cal1; + } + } + + if (ddata->vib_ops->get_haptic_intensities) { + ret = sysfs_create_file(&ddata->to_dev->kobj, &dev_attr_haptic_intensities.attr); + if (ret) { + ret = -ENODEV; + pr_err("Failed to create haptic_intensities %d\n", ret); + goto err_cal2; + } + } + + if (ddata->vib_ops->get_haptic_durations) { + ret = sysfs_create_file(&ddata->to_dev->kobj, &dev_attr_haptic_durations.attr); + if (ret) { + ret = -ENODEV; + pr_err("Failed to create haptic_intensities %d\n", ret); + goto err_cal3; + } + } + } + + pr_info("%s done\n", __func__); + + ddata->is_registered = true; + + return ret; + +err_cal3: + if (ddata->vib_ops->get_calibration && ddata->vib_ops->get_calibration(ddata->dev)) { + if (ddata->vib_ops->get_haptic_intensities) + sysfs_remove_file(&ddata->to_dev->kobj, &dev_attr_haptic_intensities.attr); + } +err_cal2: + if (ddata->vib_ops->get_calibration && ddata->vib_ops->get_calibration(ddata->dev)) { + if (ddata->vib_ops->get_intensities) + sysfs_remove_file(&ddata->to_dev->kobj, &dev_attr_intensities.attr); + } +err_cal1: + if (ddata->vib_ops->set_event_cmd) + sysfs_remove_file(&ddata->to_dev->kobj, &dev_attr_event_cmd.attr); +err_sysfs7: + if (ddata->vib_ops->set_pwle) + sysfs_remove_group(&ddata->to_dev->kobj, &pwle_attr_group); +err_sysfs6: + if (ddata->vib_ops->set_cp_trigger_index) + sysfs_remove_group(&ddata->to_dev->kobj, &cp_trigger_attr_group); +err_sysfs5: + if (ddata->vib_ops->set_frequency) + sysfs_remove_group(&ddata->to_dev->kobj, &multi_freq_attr_group); +err_sysfs4: + if (ddata->vib_ops->set_intensity) + sysfs_remove_file(&ddata->to_dev->kobj, &dev_attr_force_touch_intensity.attr); +err_sysfs3: + if (ddata->vib_ops->set_intensity) + sysfs_remove_file(&ddata->to_dev->kobj, &dev_attr_intensity.attr); +err_sysfs2: + sysfs_remove_group(&ddata->to_dev->kobj, &sec_vibrator_attr_group); +err_sysfs1: + device_destroy(ddata->to_class, MKDEV(0, 0)); +err_device_create: + class_destroy(ddata->to_class); +err_class_create: +err_kthread: + mutex_destroy(&ddata->vib_mutex); + return ret; +} +EXPORT_SYMBOL_GPL(sec_vibrator_register); + +int sec_vibrator_unregister(struct sec_vibrator_drvdata *ddata) +{ + if (!ddata || !ddata->is_registered) { + pr_info("%s: sec_vibrator not registered, just return\n", __func__); + return -ENODEV; + } + + sec_vibrator_haptic_disable(ddata); + g_ddata = NULL; + + if (ddata->vib_ops->get_calibration && ddata->vib_ops->get_calibration(ddata->dev)) { + if (ddata->vib_ops->get_haptic_intensities) + sysfs_remove_file(&ddata->to_dev->kobj, &dev_attr_haptic_intensities.attr); + if (ddata->vib_ops->get_intensities) + sysfs_remove_file(&ddata->to_dev->kobj, &dev_attr_intensities.attr); + } + if (ddata->vib_ops->set_event_cmd) + sysfs_remove_file(&ddata->to_dev->kobj, &dev_attr_event_cmd.attr); + if (ddata->vib_ops->set_pwle) + sysfs_remove_group(&ddata->to_dev->kobj, &pwle_attr_group); + if (ddata->vib_ops->set_cp_trigger_index) + sysfs_remove_group(&ddata->to_dev->kobj, &cp_trigger_attr_group); + if (ddata->vib_ops->set_frequency) + sysfs_remove_group(&ddata->to_dev->kobj, &multi_freq_attr_group); + if (ddata->vib_ops->set_intensity) + sysfs_remove_file(&ddata->to_dev->kobj, &dev_attr_intensity.attr); + + sysfs_remove_group(&ddata->to_dev->kobj, &sec_vibrator_attr_group); + + device_destroy(ddata->to_class, MKDEV(0, 0)); + class_destroy(ddata->to_class); + mutex_destroy(&ddata->vib_mutex); + return 0; +} +EXPORT_SYMBOL_GPL(sec_vibrator_unregister); + +extern int haptic_homekey_press(void) +{ + struct sec_vibrator_drvdata *ddata = g_ddata; + struct hrtimer *timer; + int intensity; + + if (!ddata) { + pr_err("%s : ddata is NULL\n", __func__); + return -ENODATA; + } + + timer = &ddata->timer; + + intensity = ddata->force_touch_intensity; + if (intensity > HOMEKEY_PRESS_FREQ) { + mutex_lock(&ddata->vib_mutex); + ddata->timeout = HOMEKEY_DURATION; +#if IS_ENABLED(CONFIG_BATTERY_SAMSUNG) + sec_vibrator_check_temp(ddata); +#endif + sec_vibrator_set_overdrive(ddata, true); + sec_vibrator_set_frequency(ddata, HOMEKEY_PRESS_FREQ); + sec_vibrator_set_intensity(ddata, intensity); + sec_vibrator_set_enable(ddata, true); + + pr_debug("%s freq:%d, intensity:%d, time:%d\n", __func__, + HOMEKEY_PRESS_FREQ, intensity, ddata->timeout); + mutex_unlock(&ddata->vib_mutex); + + hrtimer_start(timer, + ns_to_ktime((u64)ddata->timeout * NSEC_PER_MSEC), + HRTIMER_MODE_REL); + } + + return 0; +} + +extern int haptic_homekey_release(void) +{ + struct sec_vibrator_drvdata *ddata = g_ddata; + struct hrtimer *timer; + int intensity; + + if (!ddata) { + pr_err("%s : ddata is NULL\n", __func__); + return -ENODATA; + } + + timer = &ddata->timer; + + intensity = ddata->force_touch_intensity; + if (intensity > HOMEKEY_PRESS_FREQ) { + mutex_lock(&ddata->vib_mutex); + ddata->timeout = HOMEKEY_DURATION; +#if IS_ENABLED(CONFIG_BATTERY_SAMSUNG) + sec_vibrator_check_temp(ddata); +#endif + sec_vibrator_set_overdrive(ddata, true); + sec_vibrator_set_frequency(ddata, HOMEKEY_RELEASE_FREQ); + sec_vibrator_set_intensity(ddata, intensity); + sec_vibrator_set_enable(ddata, true); + + pr_debug("%s freq:%d, intensity:%d, time:%d\n", __func__, + HOMEKEY_RELEASE_FREQ, intensity, ddata->timeout); + mutex_unlock(&ddata->vib_mutex); + + hrtimer_start(timer, ns_to_ktime((u64)ddata->timeout * NSEC_PER_MSEC), HRTIMER_MODE_REL); + } + + return 0; +} + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("sec vibrator driver"); \ No newline at end of file diff --git a/include/linux/mfd/max77705.h b/include/linux/mfd/max77705.h index 8b09dea497db..402f040f6cf0 100755 --- a/include/linux/mfd/max77705.h +++ b/include/linux/mfd/max77705.h @@ -43,7 +43,6 @@ struct max77705_haptic_pdata { int gpio; char *regulator_name; unsigned int pwm_id; - const char *vib_type; /* for multi-frequency */ int multi_frequency; @@ -55,6 +54,34 @@ struct max77705_haptic_pdata { }; #endif +#if defined(CONFIG_MAX77705_VIBRATOR) +struct max77705_vibrator_pdata { + int gpio; + char *regulator_name; + struct pwm_device *pwm; + unsigned int pwm_id; + const char *motor_type; + + int freq; + /* for multi-frequency */ + int freq_nums; + u32 *freq_array; + u32 *ratio_array; /* not used now */ + int normal_ratio; + int overdrive_ratio; + int high_temp_ratio; + int high_temp_ref; + int fold_open_ratio; + int fold_close_ratio; +#if defined(CONFIG_SEC_VIBRATOR) + bool calibration; + int steps; + int *intensities; + int *haptic_intensities; +#endif +}; +#endif + struct max77705_regulator_data { int id; struct regulator_init_data *initdata; @@ -73,6 +100,9 @@ struct max77705_platform_data { struct max77705_regulator_data *regulators; #if defined(CONFIG_MOTOR_DRV_MAX77705) struct max77705_haptic_pdata *haptic_data; +#endif +#if defined(CONFIG_MAX77705_VIBRATOR) + struct max77705_vibrator_pdata *vibrator_data; #endif struct mfd_cell *sub_devices; int num_subdevs; @@ -83,5 +113,4 @@ struct max77705 { struct regmap *regmap; }; -#endif /* __MAX77705_H__ */ - +#endif /* __MAX77705_H__ */ \ No newline at end of file diff --git a/include/linux/vibrator/sec_vibrator.h b/include/linux/vibrator/sec_vibrator.h new file mode 100644 index 000000000000..8e3d2240f219 --- /dev/null +++ b/include/linux/vibrator/sec_vibrator.h @@ -0,0 +1,125 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2019 Samsung Electronics + */ + +#ifndef SEC_VIBRATOR_H +#define SEC_VIBRATOR_H + +#include +#include +#include + +#define MAX_INTENSITY 10000 +#define MAX_TIMEOUT 10000 +#define PACKET_MAX_SIZE 1000 + +#define HAPTIC_ENGINE_FREQ_MIN 1200 +#define HAPTIC_ENGINE_FREQ_MAX 3500 + +#define VIB_BUFSIZE 30 + +#define HOMEKEY_PRESS_FREQ 5 +#define HOMEKEY_RELEASE_FREQ 6 +#define HOMEKEY_DURATION 7 + +struct vib_packet { + int time; + int intensity; + int freq; + int overdrive; +}; + +enum { + VIB_PACKET_TIME = 0, + VIB_PACKET_INTENSITY, + VIB_PACKET_FREQUENCY, + VIB_PACKET_OVERDRIVE, + VIB_PACKET_MAX, +}; + +enum { + FREQ_ALERT = 0, /* 157.5Hz */ + FREQ_ZERO, /* 180Hz */ + FREQ_LOW, /* 120Hz */ + FREQ_MID, /* 150Hz */ + FREQ_HIGH, /* 200Hz */ + FREQ_PRESS, /* force touch press */ + FREQ_RELEASE, /* force touch release */ + FREQ_MAX, +}; + +enum EVENT_CMD { + EVENT_CMD_NONE = 0, + EVENT_CMD_FOLDER_CLOSE, + EVENT_CMD_FOLDER_OPEN, + EVENT_CMD_ACCESSIBILITY_BOOST_ON, + EVENT_CMD_ACCESSIBILITY_BOOST_OFF, + EVENT_CMD_TENT_CLOSE, + EVENT_CMD_TENT_OPEN, + EVENT_CMD_MAX, +}; + +#define MAX_STR_LEN_VIB_TYPE 32 +#define MAX_STR_LEN_EVENT_CMD 32 + +struct sec_vibrator_ops { + int (*enable)(struct device *dev, bool en); + int (*set_intensity)(struct device *dev, int intensity); + int (*set_frequency)(struct device *dev, int frequency); + int (*set_overdrive)(struct device *dev, bool en); + int (*get_motor_type)(struct device *dev, char *buf); + int (*set_use_sep_index)(struct device *dev, bool use_sep_index); + ssize_t (*get_num_waves)(struct device *dev, char *buf); + ssize_t (*set_cp_trigger_index)(struct device *dev, const char *buf); + ssize_t (*get_cp_trigger_index)(struct device *dev, char *buf); + ssize_t (*set_cp_trigger_queue)(struct device *dev, const char *buf); + ssize_t (*get_cp_trigger_queue)(struct device *dev, char *buf); + int (*set_force_touch_intensity)(struct device *dev, int intensity); + int (*set_tuning_with_temp)(struct device *dev, int temperature); + int (*set_event_cmd)(struct device *dev, int event_idx); + bool (*get_calibration)(struct device *dev); + int (*get_step_size)(struct device *dev, int *step_size); + int (*get_intensities)(struct device *dev, int *buf); + int (*set_intensities)(struct device *dev, int *buf); + int (*get_haptic_intensities)(struct device *dev, int *buf); + int (*set_haptic_intensities)(struct device *dev, int *buf); + int (*get_haptic_durations)(struct device *dev, int *buf); + int (*set_haptic_durations)(struct device *dev, int *buf); + ssize_t (*set_pwle)(struct device *dev, const char *buf); + ssize_t (*get_pwle)(struct device *dev, char *buf); + ssize_t (*get_virtual_composite_indexes)(struct device *dev, char *buf); + ssize_t (*get_virtual_pwle_indexes)(struct device *dev, char *buf); +}; + +struct sec_vibrator_drvdata { + struct class *to_class; + struct device *to_dev; + struct device *dev; + struct hrtimer timer; + struct kthread_worker kworker; + struct kthread_work kwork; + struct mutex vib_mutex; + struct vib_packet vib_pac[PACKET_MAX_SIZE]; + const struct sec_vibrator_ops *vib_ops; + + bool f_packet_en; + bool packet_running; + int packet_size; + int packet_cnt; + unsigned int index; + + int force_touch_intensity; + int intensity; + int frequency; + bool overdrive; + + int timeout; + + char event_cmd[MAX_STR_LEN_EVENT_CMD]; + + bool is_registered; +}; + +extern int sec_vibrator_register(struct sec_vibrator_drvdata *ddata); +extern int sec_vibrator_unregister(struct sec_vibrator_drvdata *ddata); +#endif /* SEC_VIBRATOR_H */ diff --git a/include/linux/vibrator/sec_vibrator_notifier.h b/include/linux/vibrator/sec_vibrator_notifier.h new file mode 100644 index 000000000000..1b31ce93f3c6 --- /dev/null +++ b/include/linux/vibrator/sec_vibrator_notifier.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2019 Samsung Electronics + */ + +#ifndef __SEC_VIB_NOTIFIER_H__ +#define __SEC_VIB_NOTIFIER_H__ + +#include + +enum SEC_VIB_NOTIFIER { + SEC_VIB_NOTIFIER_OFF = 0, + SEC_VIB_NOTIFIER_ON, +}; + +struct vib_notifier_context { + int index; + int timeout; +}; + +extern int sec_vib_notifier_register(struct notifier_block *n); +extern int sec_vib_notifier_unregister(struct notifier_block *nb); +#endif /* __SEC_VIB_NOTIFIER_H__ */