162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * DRV260X haptics driver family 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Author: Dan Murphy <dmurphy@ti.com> 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Copyright: (C) 2014 Texas Instruments, Inc. 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/i2c.h> 1162306a36Sopenharmony_ci#include <linux/input.h> 1262306a36Sopenharmony_ci#include <linux/module.h> 1362306a36Sopenharmony_ci#include <linux/regmap.h> 1462306a36Sopenharmony_ci#include <linux/slab.h> 1562306a36Sopenharmony_ci#include <linux/delay.h> 1662306a36Sopenharmony_ci#include <linux/gpio/consumer.h> 1762306a36Sopenharmony_ci#include <linux/regulator/consumer.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include <dt-bindings/input/ti-drv260x.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#define DRV260X_STATUS 0x0 2262306a36Sopenharmony_ci#define DRV260X_MODE 0x1 2362306a36Sopenharmony_ci#define DRV260X_RT_PB_IN 0x2 2462306a36Sopenharmony_ci#define DRV260X_LIB_SEL 0x3 2562306a36Sopenharmony_ci#define DRV260X_WV_SEQ_1 0x4 2662306a36Sopenharmony_ci#define DRV260X_WV_SEQ_2 0x5 2762306a36Sopenharmony_ci#define DRV260X_WV_SEQ_3 0x6 2862306a36Sopenharmony_ci#define DRV260X_WV_SEQ_4 0x7 2962306a36Sopenharmony_ci#define DRV260X_WV_SEQ_5 0x8 3062306a36Sopenharmony_ci#define DRV260X_WV_SEQ_6 0x9 3162306a36Sopenharmony_ci#define DRV260X_WV_SEQ_7 0xa 3262306a36Sopenharmony_ci#define DRV260X_WV_SEQ_8 0xb 3362306a36Sopenharmony_ci#define DRV260X_GO 0xc 3462306a36Sopenharmony_ci#define DRV260X_OVERDRIVE_OFF 0xd 3562306a36Sopenharmony_ci#define DRV260X_SUSTAIN_P_OFF 0xe 3662306a36Sopenharmony_ci#define DRV260X_SUSTAIN_N_OFF 0xf 3762306a36Sopenharmony_ci#define DRV260X_BRAKE_OFF 0x10 3862306a36Sopenharmony_ci#define DRV260X_A_TO_V_CTRL 0x11 3962306a36Sopenharmony_ci#define DRV260X_A_TO_V_MIN_INPUT 0x12 4062306a36Sopenharmony_ci#define DRV260X_A_TO_V_MAX_INPUT 0x13 4162306a36Sopenharmony_ci#define DRV260X_A_TO_V_MIN_OUT 0x14 4262306a36Sopenharmony_ci#define DRV260X_A_TO_V_MAX_OUT 0x15 4362306a36Sopenharmony_ci#define DRV260X_RATED_VOLT 0x16 4462306a36Sopenharmony_ci#define DRV260X_OD_CLAMP_VOLT 0x17 4562306a36Sopenharmony_ci#define DRV260X_CAL_COMP 0x18 4662306a36Sopenharmony_ci#define DRV260X_CAL_BACK_EMF 0x19 4762306a36Sopenharmony_ci#define DRV260X_FEEDBACK_CTRL 0x1a 4862306a36Sopenharmony_ci#define DRV260X_CTRL1 0x1b 4962306a36Sopenharmony_ci#define DRV260X_CTRL2 0x1c 5062306a36Sopenharmony_ci#define DRV260X_CTRL3 0x1d 5162306a36Sopenharmony_ci#define DRV260X_CTRL4 0x1e 5262306a36Sopenharmony_ci#define DRV260X_CTRL5 0x1f 5362306a36Sopenharmony_ci#define DRV260X_LRA_LOOP_PERIOD 0x20 5462306a36Sopenharmony_ci#define DRV260X_VBAT_MON 0x21 5562306a36Sopenharmony_ci#define DRV260X_LRA_RES_PERIOD 0x22 5662306a36Sopenharmony_ci#define DRV260X_MAX_REG 0x23 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci#define DRV260X_GO_BIT 0x01 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci/* Library Selection */ 6162306a36Sopenharmony_ci#define DRV260X_LIB_SEL_MASK 0x07 6262306a36Sopenharmony_ci#define DRV260X_LIB_SEL_RAM 0x0 6362306a36Sopenharmony_ci#define DRV260X_LIB_SEL_OD 0x1 6462306a36Sopenharmony_ci#define DRV260X_LIB_SEL_40_60 0x2 6562306a36Sopenharmony_ci#define DRV260X_LIB_SEL_60_80 0x3 6662306a36Sopenharmony_ci#define DRV260X_LIB_SEL_100_140 0x4 6762306a36Sopenharmony_ci#define DRV260X_LIB_SEL_140_PLUS 0x5 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci#define DRV260X_LIB_SEL_HIZ_MASK 0x10 7062306a36Sopenharmony_ci#define DRV260X_LIB_SEL_HIZ_EN 0x01 7162306a36Sopenharmony_ci#define DRV260X_LIB_SEL_HIZ_DIS 0 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci/* Mode register */ 7462306a36Sopenharmony_ci#define DRV260X_STANDBY (1 << 6) 7562306a36Sopenharmony_ci#define DRV260X_STANDBY_MASK 0x40 7662306a36Sopenharmony_ci#define DRV260X_INTERNAL_TRIGGER 0x00 7762306a36Sopenharmony_ci#define DRV260X_EXT_TRIGGER_EDGE 0x01 7862306a36Sopenharmony_ci#define DRV260X_EXT_TRIGGER_LEVEL 0x02 7962306a36Sopenharmony_ci#define DRV260X_PWM_ANALOG_IN 0x03 8062306a36Sopenharmony_ci#define DRV260X_AUDIOHAPTIC 0x04 8162306a36Sopenharmony_ci#define DRV260X_RT_PLAYBACK 0x05 8262306a36Sopenharmony_ci#define DRV260X_DIAGNOSTICS 0x06 8362306a36Sopenharmony_ci#define DRV260X_AUTO_CAL 0x07 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci/* Audio to Haptics Control */ 8662306a36Sopenharmony_ci#define DRV260X_AUDIO_HAPTICS_PEAK_10MS (0 << 2) 8762306a36Sopenharmony_ci#define DRV260X_AUDIO_HAPTICS_PEAK_20MS (1 << 2) 8862306a36Sopenharmony_ci#define DRV260X_AUDIO_HAPTICS_PEAK_30MS (2 << 2) 8962306a36Sopenharmony_ci#define DRV260X_AUDIO_HAPTICS_PEAK_40MS (3 << 2) 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci#define DRV260X_AUDIO_HAPTICS_FILTER_100HZ 0x00 9262306a36Sopenharmony_ci#define DRV260X_AUDIO_HAPTICS_FILTER_125HZ 0x01 9362306a36Sopenharmony_ci#define DRV260X_AUDIO_HAPTICS_FILTER_150HZ 0x02 9462306a36Sopenharmony_ci#define DRV260X_AUDIO_HAPTICS_FILTER_200HZ 0x03 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci/* Min/Max Input/Output Voltages */ 9762306a36Sopenharmony_ci#define DRV260X_AUDIO_HAPTICS_MIN_IN_VOLT 0x19 9862306a36Sopenharmony_ci#define DRV260X_AUDIO_HAPTICS_MAX_IN_VOLT 0x64 9962306a36Sopenharmony_ci#define DRV260X_AUDIO_HAPTICS_MIN_OUT_VOLT 0x19 10062306a36Sopenharmony_ci#define DRV260X_AUDIO_HAPTICS_MAX_OUT_VOLT 0xFF 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci/* Feedback register */ 10362306a36Sopenharmony_ci#define DRV260X_FB_REG_ERM_MODE 0x7f 10462306a36Sopenharmony_ci#define DRV260X_FB_REG_LRA_MODE (1 << 7) 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci#define DRV260X_BRAKE_FACTOR_MASK 0x1f 10762306a36Sopenharmony_ci#define DRV260X_BRAKE_FACTOR_2X (1 << 0) 10862306a36Sopenharmony_ci#define DRV260X_BRAKE_FACTOR_3X (2 << 4) 10962306a36Sopenharmony_ci#define DRV260X_BRAKE_FACTOR_4X (3 << 4) 11062306a36Sopenharmony_ci#define DRV260X_BRAKE_FACTOR_6X (4 << 4) 11162306a36Sopenharmony_ci#define DRV260X_BRAKE_FACTOR_8X (5 << 4) 11262306a36Sopenharmony_ci#define DRV260X_BRAKE_FACTOR_16 (6 << 4) 11362306a36Sopenharmony_ci#define DRV260X_BRAKE_FACTOR_DIS (7 << 4) 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci#define DRV260X_LOOP_GAIN_LOW 0xf3 11662306a36Sopenharmony_ci#define DRV260X_LOOP_GAIN_MED (1 << 2) 11762306a36Sopenharmony_ci#define DRV260X_LOOP_GAIN_HIGH (2 << 2) 11862306a36Sopenharmony_ci#define DRV260X_LOOP_GAIN_VERY_HIGH (3 << 2) 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci#define DRV260X_BEMF_GAIN_0 0xfc 12162306a36Sopenharmony_ci#define DRV260X_BEMF_GAIN_1 (1 << 0) 12262306a36Sopenharmony_ci#define DRV260X_BEMF_GAIN_2 (2 << 0) 12362306a36Sopenharmony_ci#define DRV260X_BEMF_GAIN_3 (3 << 0) 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci/* Control 1 register */ 12662306a36Sopenharmony_ci#define DRV260X_AC_CPLE_EN (1 << 5) 12762306a36Sopenharmony_ci#define DRV260X_STARTUP_BOOST (1 << 7) 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci/* Control 2 register */ 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci#define DRV260X_IDISS_TIME_45 0 13262306a36Sopenharmony_ci#define DRV260X_IDISS_TIME_75 (1 << 0) 13362306a36Sopenharmony_ci#define DRV260X_IDISS_TIME_150 (1 << 1) 13462306a36Sopenharmony_ci#define DRV260X_IDISS_TIME_225 0x03 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci#define DRV260X_BLANK_TIME_45 (0 << 2) 13762306a36Sopenharmony_ci#define DRV260X_BLANK_TIME_75 (1 << 2) 13862306a36Sopenharmony_ci#define DRV260X_BLANK_TIME_150 (2 << 2) 13962306a36Sopenharmony_ci#define DRV260X_BLANK_TIME_225 (3 << 2) 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci#define DRV260X_SAMP_TIME_150 (0 << 4) 14262306a36Sopenharmony_ci#define DRV260X_SAMP_TIME_200 (1 << 4) 14362306a36Sopenharmony_ci#define DRV260X_SAMP_TIME_250 (2 << 4) 14462306a36Sopenharmony_ci#define DRV260X_SAMP_TIME_300 (3 << 4) 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci#define DRV260X_BRAKE_STABILIZER (1 << 6) 14762306a36Sopenharmony_ci#define DRV260X_UNIDIR_IN (0 << 7) 14862306a36Sopenharmony_ci#define DRV260X_BIDIR_IN (1 << 7) 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci/* Control 3 Register */ 15162306a36Sopenharmony_ci#define DRV260X_LRA_OPEN_LOOP (1 << 0) 15262306a36Sopenharmony_ci#define DRV260X_ANALOG_IN (1 << 1) 15362306a36Sopenharmony_ci#define DRV260X_LRA_DRV_MODE (1 << 2) 15462306a36Sopenharmony_ci#define DRV260X_RTP_UNSIGNED_DATA (1 << 3) 15562306a36Sopenharmony_ci#define DRV260X_SUPPLY_COMP_DIS (1 << 4) 15662306a36Sopenharmony_ci#define DRV260X_ERM_OPEN_LOOP (1 << 5) 15762306a36Sopenharmony_ci#define DRV260X_NG_THRESH_0 (0 << 6) 15862306a36Sopenharmony_ci#define DRV260X_NG_THRESH_2 (1 << 6) 15962306a36Sopenharmony_ci#define DRV260X_NG_THRESH_4 (2 << 6) 16062306a36Sopenharmony_ci#define DRV260X_NG_THRESH_8 (3 << 6) 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci/* Control 4 Register */ 16362306a36Sopenharmony_ci#define DRV260X_AUTOCAL_TIME_150MS (0 << 4) 16462306a36Sopenharmony_ci#define DRV260X_AUTOCAL_TIME_250MS (1 << 4) 16562306a36Sopenharmony_ci#define DRV260X_AUTOCAL_TIME_500MS (2 << 4) 16662306a36Sopenharmony_ci#define DRV260X_AUTOCAL_TIME_1000MS (3 << 4) 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci/** 16962306a36Sopenharmony_ci * struct drv260x_data - 17062306a36Sopenharmony_ci * @input_dev: Pointer to the input device 17162306a36Sopenharmony_ci * @client: Pointer to the I2C client 17262306a36Sopenharmony_ci * @regmap: Register map of the device 17362306a36Sopenharmony_ci * @work: Work item used to off load the enable/disable of the vibration 17462306a36Sopenharmony_ci * @enable_gpio: Pointer to the gpio used for enable/disabling 17562306a36Sopenharmony_ci * @regulator: Pointer to the regulator for the IC 17662306a36Sopenharmony_ci * @magnitude: Magnitude of the vibration event 17762306a36Sopenharmony_ci * @mode: The operating mode of the IC (LRA_NO_CAL, ERM or LRA) 17862306a36Sopenharmony_ci * @library: The vibration library to be used 17962306a36Sopenharmony_ci * @rated_voltage: The rated_voltage of the actuator 18062306a36Sopenharmony_ci * @overdrive_voltage: The over drive voltage of the actuator 18162306a36Sopenharmony_ci**/ 18262306a36Sopenharmony_cistruct drv260x_data { 18362306a36Sopenharmony_ci struct input_dev *input_dev; 18462306a36Sopenharmony_ci struct i2c_client *client; 18562306a36Sopenharmony_ci struct regmap *regmap; 18662306a36Sopenharmony_ci struct work_struct work; 18762306a36Sopenharmony_ci struct gpio_desc *enable_gpio; 18862306a36Sopenharmony_ci struct regulator *regulator; 18962306a36Sopenharmony_ci u8 magnitude; 19062306a36Sopenharmony_ci u32 mode; 19162306a36Sopenharmony_ci u32 library; 19262306a36Sopenharmony_ci int rated_voltage; 19362306a36Sopenharmony_ci int overdrive_voltage; 19462306a36Sopenharmony_ci}; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci#define DRV260X_DEF_RATED_VOLT 0x90 19762306a36Sopenharmony_ci#define DRV260X_DEF_OD_CLAMP_VOLT 0x90 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci/* 20062306a36Sopenharmony_ci * Rated and Overdriver Voltages: 20162306a36Sopenharmony_ci * Calculated using the formula r = v * 255 / 5.6 20262306a36Sopenharmony_ci * where r is what will be written to the register 20362306a36Sopenharmony_ci * and v is the rated or overdriver voltage of the actuator 20462306a36Sopenharmony_ci */ 20562306a36Sopenharmony_cistatic int drv260x_calculate_voltage(unsigned int voltage) 20662306a36Sopenharmony_ci{ 20762306a36Sopenharmony_ci return (voltage * 255 / 5600); 20862306a36Sopenharmony_ci} 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_cistatic void drv260x_worker(struct work_struct *work) 21162306a36Sopenharmony_ci{ 21262306a36Sopenharmony_ci struct drv260x_data *haptics = container_of(work, struct drv260x_data, work); 21362306a36Sopenharmony_ci int error; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci gpiod_set_value(haptics->enable_gpio, 1); 21662306a36Sopenharmony_ci /* Data sheet says to wait 250us before trying to communicate */ 21762306a36Sopenharmony_ci udelay(250); 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci error = regmap_write(haptics->regmap, 22062306a36Sopenharmony_ci DRV260X_MODE, DRV260X_RT_PLAYBACK); 22162306a36Sopenharmony_ci if (error) { 22262306a36Sopenharmony_ci dev_err(&haptics->client->dev, 22362306a36Sopenharmony_ci "Failed to write set mode: %d\n", error); 22462306a36Sopenharmony_ci } else { 22562306a36Sopenharmony_ci error = regmap_write(haptics->regmap, 22662306a36Sopenharmony_ci DRV260X_RT_PB_IN, haptics->magnitude); 22762306a36Sopenharmony_ci if (error) 22862306a36Sopenharmony_ci dev_err(&haptics->client->dev, 22962306a36Sopenharmony_ci "Failed to set magnitude: %d\n", error); 23062306a36Sopenharmony_ci } 23162306a36Sopenharmony_ci} 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_cistatic int drv260x_haptics_play(struct input_dev *input, void *data, 23462306a36Sopenharmony_ci struct ff_effect *effect) 23562306a36Sopenharmony_ci{ 23662306a36Sopenharmony_ci struct drv260x_data *haptics = input_get_drvdata(input); 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci haptics->mode = DRV260X_LRA_NO_CAL_MODE; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci /* Scale u16 magnitude into u8 register value */ 24162306a36Sopenharmony_ci if (effect->u.rumble.strong_magnitude > 0) 24262306a36Sopenharmony_ci haptics->magnitude = effect->u.rumble.strong_magnitude >> 8; 24362306a36Sopenharmony_ci else if (effect->u.rumble.weak_magnitude > 0) 24462306a36Sopenharmony_ci haptics->magnitude = effect->u.rumble.weak_magnitude >> 8; 24562306a36Sopenharmony_ci else 24662306a36Sopenharmony_ci haptics->magnitude = 0; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci schedule_work(&haptics->work); 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci return 0; 25162306a36Sopenharmony_ci} 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_cistatic void drv260x_close(struct input_dev *input) 25462306a36Sopenharmony_ci{ 25562306a36Sopenharmony_ci struct drv260x_data *haptics = input_get_drvdata(input); 25662306a36Sopenharmony_ci int error; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci cancel_work_sync(&haptics->work); 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci error = regmap_write(haptics->regmap, DRV260X_MODE, DRV260X_STANDBY); 26162306a36Sopenharmony_ci if (error) 26262306a36Sopenharmony_ci dev_err(&haptics->client->dev, 26362306a36Sopenharmony_ci "Failed to enter standby mode: %d\n", error); 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci gpiod_set_value(haptics->enable_gpio, 0); 26662306a36Sopenharmony_ci} 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_cistatic const struct reg_sequence drv260x_lra_cal_regs[] = { 26962306a36Sopenharmony_ci { DRV260X_MODE, DRV260X_AUTO_CAL }, 27062306a36Sopenharmony_ci { DRV260X_CTRL3, DRV260X_NG_THRESH_2 | DRV260X_RTP_UNSIGNED_DATA }, 27162306a36Sopenharmony_ci { DRV260X_FEEDBACK_CTRL, DRV260X_FB_REG_LRA_MODE | 27262306a36Sopenharmony_ci DRV260X_BRAKE_FACTOR_4X | DRV260X_LOOP_GAIN_HIGH }, 27362306a36Sopenharmony_ci}; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_cistatic const struct reg_sequence drv260x_lra_init_regs[] = { 27662306a36Sopenharmony_ci { DRV260X_MODE, DRV260X_RT_PLAYBACK }, 27762306a36Sopenharmony_ci { DRV260X_A_TO_V_CTRL, DRV260X_AUDIO_HAPTICS_PEAK_20MS | 27862306a36Sopenharmony_ci DRV260X_AUDIO_HAPTICS_FILTER_125HZ }, 27962306a36Sopenharmony_ci { DRV260X_A_TO_V_MIN_INPUT, DRV260X_AUDIO_HAPTICS_MIN_IN_VOLT }, 28062306a36Sopenharmony_ci { DRV260X_A_TO_V_MAX_INPUT, DRV260X_AUDIO_HAPTICS_MAX_IN_VOLT }, 28162306a36Sopenharmony_ci { DRV260X_A_TO_V_MIN_OUT, DRV260X_AUDIO_HAPTICS_MIN_OUT_VOLT }, 28262306a36Sopenharmony_ci { DRV260X_A_TO_V_MAX_OUT, DRV260X_AUDIO_HAPTICS_MAX_OUT_VOLT }, 28362306a36Sopenharmony_ci { DRV260X_FEEDBACK_CTRL, DRV260X_FB_REG_LRA_MODE | 28462306a36Sopenharmony_ci DRV260X_BRAKE_FACTOR_2X | DRV260X_LOOP_GAIN_MED | 28562306a36Sopenharmony_ci DRV260X_BEMF_GAIN_3 }, 28662306a36Sopenharmony_ci { DRV260X_CTRL1, DRV260X_STARTUP_BOOST }, 28762306a36Sopenharmony_ci { DRV260X_CTRL2, DRV260X_SAMP_TIME_250 }, 28862306a36Sopenharmony_ci { DRV260X_CTRL3, DRV260X_NG_THRESH_2 | DRV260X_RTP_UNSIGNED_DATA | DRV260X_ANALOG_IN }, 28962306a36Sopenharmony_ci { DRV260X_CTRL4, DRV260X_AUTOCAL_TIME_500MS }, 29062306a36Sopenharmony_ci}; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_cistatic const struct reg_sequence drv260x_erm_cal_regs[] = { 29362306a36Sopenharmony_ci { DRV260X_MODE, DRV260X_AUTO_CAL }, 29462306a36Sopenharmony_ci { DRV260X_A_TO_V_MIN_INPUT, DRV260X_AUDIO_HAPTICS_MIN_IN_VOLT }, 29562306a36Sopenharmony_ci { DRV260X_A_TO_V_MAX_INPUT, DRV260X_AUDIO_HAPTICS_MAX_IN_VOLT }, 29662306a36Sopenharmony_ci { DRV260X_A_TO_V_MIN_OUT, DRV260X_AUDIO_HAPTICS_MIN_OUT_VOLT }, 29762306a36Sopenharmony_ci { DRV260X_A_TO_V_MAX_OUT, DRV260X_AUDIO_HAPTICS_MAX_OUT_VOLT }, 29862306a36Sopenharmony_ci { DRV260X_FEEDBACK_CTRL, DRV260X_BRAKE_FACTOR_3X | 29962306a36Sopenharmony_ci DRV260X_LOOP_GAIN_MED | DRV260X_BEMF_GAIN_2 }, 30062306a36Sopenharmony_ci { DRV260X_CTRL1, DRV260X_STARTUP_BOOST }, 30162306a36Sopenharmony_ci { DRV260X_CTRL2, DRV260X_SAMP_TIME_250 | DRV260X_BLANK_TIME_75 | 30262306a36Sopenharmony_ci DRV260X_IDISS_TIME_75 }, 30362306a36Sopenharmony_ci { DRV260X_CTRL3, DRV260X_NG_THRESH_2 | DRV260X_RTP_UNSIGNED_DATA }, 30462306a36Sopenharmony_ci { DRV260X_CTRL4, DRV260X_AUTOCAL_TIME_500MS }, 30562306a36Sopenharmony_ci}; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_cistatic int drv260x_init(struct drv260x_data *haptics) 30862306a36Sopenharmony_ci{ 30962306a36Sopenharmony_ci int error; 31062306a36Sopenharmony_ci unsigned int cal_buf; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci error = regmap_write(haptics->regmap, 31362306a36Sopenharmony_ci DRV260X_RATED_VOLT, haptics->rated_voltage); 31462306a36Sopenharmony_ci if (error) { 31562306a36Sopenharmony_ci dev_err(&haptics->client->dev, 31662306a36Sopenharmony_ci "Failed to write DRV260X_RATED_VOLT register: %d\n", 31762306a36Sopenharmony_ci error); 31862306a36Sopenharmony_ci return error; 31962306a36Sopenharmony_ci } 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci error = regmap_write(haptics->regmap, 32262306a36Sopenharmony_ci DRV260X_OD_CLAMP_VOLT, haptics->overdrive_voltage); 32362306a36Sopenharmony_ci if (error) { 32462306a36Sopenharmony_ci dev_err(&haptics->client->dev, 32562306a36Sopenharmony_ci "Failed to write DRV260X_OD_CLAMP_VOLT register: %d\n", 32662306a36Sopenharmony_ci error); 32762306a36Sopenharmony_ci return error; 32862306a36Sopenharmony_ci } 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci switch (haptics->mode) { 33162306a36Sopenharmony_ci case DRV260X_LRA_MODE: 33262306a36Sopenharmony_ci error = regmap_register_patch(haptics->regmap, 33362306a36Sopenharmony_ci drv260x_lra_cal_regs, 33462306a36Sopenharmony_ci ARRAY_SIZE(drv260x_lra_cal_regs)); 33562306a36Sopenharmony_ci if (error) { 33662306a36Sopenharmony_ci dev_err(&haptics->client->dev, 33762306a36Sopenharmony_ci "Failed to write LRA calibration registers: %d\n", 33862306a36Sopenharmony_ci error); 33962306a36Sopenharmony_ci return error; 34062306a36Sopenharmony_ci } 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci break; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci case DRV260X_ERM_MODE: 34562306a36Sopenharmony_ci error = regmap_register_patch(haptics->regmap, 34662306a36Sopenharmony_ci drv260x_erm_cal_regs, 34762306a36Sopenharmony_ci ARRAY_SIZE(drv260x_erm_cal_regs)); 34862306a36Sopenharmony_ci if (error) { 34962306a36Sopenharmony_ci dev_err(&haptics->client->dev, 35062306a36Sopenharmony_ci "Failed to write ERM calibration registers: %d\n", 35162306a36Sopenharmony_ci error); 35262306a36Sopenharmony_ci return error; 35362306a36Sopenharmony_ci } 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci error = regmap_update_bits(haptics->regmap, DRV260X_LIB_SEL, 35662306a36Sopenharmony_ci DRV260X_LIB_SEL_MASK, 35762306a36Sopenharmony_ci haptics->library); 35862306a36Sopenharmony_ci if (error) { 35962306a36Sopenharmony_ci dev_err(&haptics->client->dev, 36062306a36Sopenharmony_ci "Failed to write DRV260X_LIB_SEL register: %d\n", 36162306a36Sopenharmony_ci error); 36262306a36Sopenharmony_ci return error; 36362306a36Sopenharmony_ci } 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci break; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci default: 36862306a36Sopenharmony_ci error = regmap_register_patch(haptics->regmap, 36962306a36Sopenharmony_ci drv260x_lra_init_regs, 37062306a36Sopenharmony_ci ARRAY_SIZE(drv260x_lra_init_regs)); 37162306a36Sopenharmony_ci if (error) { 37262306a36Sopenharmony_ci dev_err(&haptics->client->dev, 37362306a36Sopenharmony_ci "Failed to write LRA init registers: %d\n", 37462306a36Sopenharmony_ci error); 37562306a36Sopenharmony_ci return error; 37662306a36Sopenharmony_ci } 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci error = regmap_update_bits(haptics->regmap, DRV260X_LIB_SEL, 37962306a36Sopenharmony_ci DRV260X_LIB_SEL_MASK, 38062306a36Sopenharmony_ci haptics->library); 38162306a36Sopenharmony_ci if (error) { 38262306a36Sopenharmony_ci dev_err(&haptics->client->dev, 38362306a36Sopenharmony_ci "Failed to write DRV260X_LIB_SEL register: %d\n", 38462306a36Sopenharmony_ci error); 38562306a36Sopenharmony_ci return error; 38662306a36Sopenharmony_ci } 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci /* No need to set GO bit here */ 38962306a36Sopenharmony_ci return 0; 39062306a36Sopenharmony_ci } 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci error = regmap_write(haptics->regmap, DRV260X_GO, DRV260X_GO_BIT); 39362306a36Sopenharmony_ci if (error) { 39462306a36Sopenharmony_ci dev_err(&haptics->client->dev, 39562306a36Sopenharmony_ci "Failed to write GO register: %d\n", 39662306a36Sopenharmony_ci error); 39762306a36Sopenharmony_ci return error; 39862306a36Sopenharmony_ci } 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci do { 40162306a36Sopenharmony_ci usleep_range(15000, 15500); 40262306a36Sopenharmony_ci error = regmap_read(haptics->regmap, DRV260X_GO, &cal_buf); 40362306a36Sopenharmony_ci if (error) { 40462306a36Sopenharmony_ci dev_err(&haptics->client->dev, 40562306a36Sopenharmony_ci "Failed to read GO register: %d\n", 40662306a36Sopenharmony_ci error); 40762306a36Sopenharmony_ci return error; 40862306a36Sopenharmony_ci } 40962306a36Sopenharmony_ci } while (cal_buf == DRV260X_GO_BIT); 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci return 0; 41262306a36Sopenharmony_ci} 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_cistatic const struct regmap_config drv260x_regmap_config = { 41562306a36Sopenharmony_ci .reg_bits = 8, 41662306a36Sopenharmony_ci .val_bits = 8, 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci .max_register = DRV260X_MAX_REG, 41962306a36Sopenharmony_ci .cache_type = REGCACHE_NONE, 42062306a36Sopenharmony_ci}; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_cistatic int drv260x_probe(struct i2c_client *client) 42362306a36Sopenharmony_ci{ 42462306a36Sopenharmony_ci struct device *dev = &client->dev; 42562306a36Sopenharmony_ci struct drv260x_data *haptics; 42662306a36Sopenharmony_ci u32 voltage; 42762306a36Sopenharmony_ci int error; 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci haptics = devm_kzalloc(dev, sizeof(*haptics), GFP_KERNEL); 43062306a36Sopenharmony_ci if (!haptics) 43162306a36Sopenharmony_ci return -ENOMEM; 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci error = device_property_read_u32(dev, "mode", &haptics->mode); 43462306a36Sopenharmony_ci if (error) { 43562306a36Sopenharmony_ci dev_err(dev, "Can't fetch 'mode' property: %d\n", error); 43662306a36Sopenharmony_ci return error; 43762306a36Sopenharmony_ci } 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci if (haptics->mode < DRV260X_LRA_MODE || 44062306a36Sopenharmony_ci haptics->mode > DRV260X_ERM_MODE) { 44162306a36Sopenharmony_ci dev_err(dev, "Vibrator mode is invalid: %i\n", haptics->mode); 44262306a36Sopenharmony_ci return -EINVAL; 44362306a36Sopenharmony_ci } 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci error = device_property_read_u32(dev, "library-sel", &haptics->library); 44662306a36Sopenharmony_ci if (error) { 44762306a36Sopenharmony_ci dev_err(dev, "Can't fetch 'library-sel' property: %d\n", error); 44862306a36Sopenharmony_ci return error; 44962306a36Sopenharmony_ci } 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci if (haptics->library < DRV260X_LIB_EMPTY || 45262306a36Sopenharmony_ci haptics->library > DRV260X_ERM_LIB_F) { 45362306a36Sopenharmony_ci dev_err(dev, 45462306a36Sopenharmony_ci "Library value is invalid: %i\n", haptics->library); 45562306a36Sopenharmony_ci return -EINVAL; 45662306a36Sopenharmony_ci } 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci if (haptics->mode == DRV260X_LRA_MODE && 45962306a36Sopenharmony_ci haptics->library != DRV260X_LIB_EMPTY && 46062306a36Sopenharmony_ci haptics->library != DRV260X_LIB_LRA) { 46162306a36Sopenharmony_ci dev_err(dev, "LRA Mode with ERM Library mismatch\n"); 46262306a36Sopenharmony_ci return -EINVAL; 46362306a36Sopenharmony_ci } 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci if (haptics->mode == DRV260X_ERM_MODE && 46662306a36Sopenharmony_ci (haptics->library == DRV260X_LIB_EMPTY || 46762306a36Sopenharmony_ci haptics->library == DRV260X_LIB_LRA)) { 46862306a36Sopenharmony_ci dev_err(dev, "ERM Mode with LRA Library mismatch\n"); 46962306a36Sopenharmony_ci return -EINVAL; 47062306a36Sopenharmony_ci } 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci error = device_property_read_u32(dev, "vib-rated-mv", &voltage); 47362306a36Sopenharmony_ci haptics->rated_voltage = error ? DRV260X_DEF_RATED_VOLT : 47462306a36Sopenharmony_ci drv260x_calculate_voltage(voltage); 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci error = device_property_read_u32(dev, "vib-overdrive-mv", &voltage); 47762306a36Sopenharmony_ci haptics->overdrive_voltage = error ? DRV260X_DEF_OD_CLAMP_VOLT : 47862306a36Sopenharmony_ci drv260x_calculate_voltage(voltage); 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci haptics->regulator = devm_regulator_get(dev, "vbat"); 48162306a36Sopenharmony_ci if (IS_ERR(haptics->regulator)) { 48262306a36Sopenharmony_ci error = PTR_ERR(haptics->regulator); 48362306a36Sopenharmony_ci dev_err(dev, "unable to get regulator, error: %d\n", error); 48462306a36Sopenharmony_ci return error; 48562306a36Sopenharmony_ci } 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci haptics->enable_gpio = devm_gpiod_get_optional(dev, "enable", 48862306a36Sopenharmony_ci GPIOD_OUT_HIGH); 48962306a36Sopenharmony_ci if (IS_ERR(haptics->enable_gpio)) 49062306a36Sopenharmony_ci return PTR_ERR(haptics->enable_gpio); 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci haptics->input_dev = devm_input_allocate_device(dev); 49362306a36Sopenharmony_ci if (!haptics->input_dev) { 49462306a36Sopenharmony_ci dev_err(dev, "Failed to allocate input device\n"); 49562306a36Sopenharmony_ci return -ENOMEM; 49662306a36Sopenharmony_ci } 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci haptics->input_dev->name = "drv260x:haptics"; 49962306a36Sopenharmony_ci haptics->input_dev->close = drv260x_close; 50062306a36Sopenharmony_ci input_set_drvdata(haptics->input_dev, haptics); 50162306a36Sopenharmony_ci input_set_capability(haptics->input_dev, EV_FF, FF_RUMBLE); 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci error = input_ff_create_memless(haptics->input_dev, NULL, 50462306a36Sopenharmony_ci drv260x_haptics_play); 50562306a36Sopenharmony_ci if (error) { 50662306a36Sopenharmony_ci dev_err(dev, "input_ff_create() failed: %d\n", error); 50762306a36Sopenharmony_ci return error; 50862306a36Sopenharmony_ci } 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci INIT_WORK(&haptics->work, drv260x_worker); 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci haptics->client = client; 51362306a36Sopenharmony_ci i2c_set_clientdata(client, haptics); 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci haptics->regmap = devm_regmap_init_i2c(client, &drv260x_regmap_config); 51662306a36Sopenharmony_ci if (IS_ERR(haptics->regmap)) { 51762306a36Sopenharmony_ci error = PTR_ERR(haptics->regmap); 51862306a36Sopenharmony_ci dev_err(dev, "Failed to allocate register map: %d\n", error); 51962306a36Sopenharmony_ci return error; 52062306a36Sopenharmony_ci } 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci error = drv260x_init(haptics); 52362306a36Sopenharmony_ci if (error) { 52462306a36Sopenharmony_ci dev_err(dev, "Device init failed: %d\n", error); 52562306a36Sopenharmony_ci return error; 52662306a36Sopenharmony_ci } 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci error = input_register_device(haptics->input_dev); 52962306a36Sopenharmony_ci if (error) { 53062306a36Sopenharmony_ci dev_err(dev, "couldn't register input device: %d\n", error); 53162306a36Sopenharmony_ci return error; 53262306a36Sopenharmony_ci } 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci return 0; 53562306a36Sopenharmony_ci} 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_cistatic int drv260x_suspend(struct device *dev) 53862306a36Sopenharmony_ci{ 53962306a36Sopenharmony_ci struct drv260x_data *haptics = dev_get_drvdata(dev); 54062306a36Sopenharmony_ci int ret = 0; 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci mutex_lock(&haptics->input_dev->mutex); 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci if (input_device_enabled(haptics->input_dev)) { 54562306a36Sopenharmony_ci ret = regmap_update_bits(haptics->regmap, 54662306a36Sopenharmony_ci DRV260X_MODE, 54762306a36Sopenharmony_ci DRV260X_STANDBY_MASK, 54862306a36Sopenharmony_ci DRV260X_STANDBY); 54962306a36Sopenharmony_ci if (ret) { 55062306a36Sopenharmony_ci dev_err(dev, "Failed to set standby mode\n"); 55162306a36Sopenharmony_ci goto out; 55262306a36Sopenharmony_ci } 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci gpiod_set_value(haptics->enable_gpio, 0); 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci ret = regulator_disable(haptics->regulator); 55762306a36Sopenharmony_ci if (ret) { 55862306a36Sopenharmony_ci dev_err(dev, "Failed to disable regulator\n"); 55962306a36Sopenharmony_ci regmap_update_bits(haptics->regmap, 56062306a36Sopenharmony_ci DRV260X_MODE, 56162306a36Sopenharmony_ci DRV260X_STANDBY_MASK, 0); 56262306a36Sopenharmony_ci } 56362306a36Sopenharmony_ci } 56462306a36Sopenharmony_ciout: 56562306a36Sopenharmony_ci mutex_unlock(&haptics->input_dev->mutex); 56662306a36Sopenharmony_ci return ret; 56762306a36Sopenharmony_ci} 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_cistatic int drv260x_resume(struct device *dev) 57062306a36Sopenharmony_ci{ 57162306a36Sopenharmony_ci struct drv260x_data *haptics = dev_get_drvdata(dev); 57262306a36Sopenharmony_ci int ret = 0; 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci mutex_lock(&haptics->input_dev->mutex); 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci if (input_device_enabled(haptics->input_dev)) { 57762306a36Sopenharmony_ci ret = regulator_enable(haptics->regulator); 57862306a36Sopenharmony_ci if (ret) { 57962306a36Sopenharmony_ci dev_err(dev, "Failed to enable regulator\n"); 58062306a36Sopenharmony_ci goto out; 58162306a36Sopenharmony_ci } 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci ret = regmap_update_bits(haptics->regmap, 58462306a36Sopenharmony_ci DRV260X_MODE, 58562306a36Sopenharmony_ci DRV260X_STANDBY_MASK, 0); 58662306a36Sopenharmony_ci if (ret) { 58762306a36Sopenharmony_ci dev_err(dev, "Failed to unset standby mode\n"); 58862306a36Sopenharmony_ci regulator_disable(haptics->regulator); 58962306a36Sopenharmony_ci goto out; 59062306a36Sopenharmony_ci } 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci gpiod_set_value(haptics->enable_gpio, 1); 59362306a36Sopenharmony_ci } 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ciout: 59662306a36Sopenharmony_ci mutex_unlock(&haptics->input_dev->mutex); 59762306a36Sopenharmony_ci return ret; 59862306a36Sopenharmony_ci} 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_cistatic DEFINE_SIMPLE_DEV_PM_OPS(drv260x_pm_ops, drv260x_suspend, drv260x_resume); 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_cistatic const struct i2c_device_id drv260x_id[] = { 60362306a36Sopenharmony_ci { "drv2605l", 0 }, 60462306a36Sopenharmony_ci { } 60562306a36Sopenharmony_ci}; 60662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, drv260x_id); 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_cistatic const struct of_device_id drv260x_of_match[] = { 60962306a36Sopenharmony_ci { .compatible = "ti,drv2604", }, 61062306a36Sopenharmony_ci { .compatible = "ti,drv2604l", }, 61162306a36Sopenharmony_ci { .compatible = "ti,drv2605", }, 61262306a36Sopenharmony_ci { .compatible = "ti,drv2605l", }, 61362306a36Sopenharmony_ci { } 61462306a36Sopenharmony_ci}; 61562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, drv260x_of_match); 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_cistatic struct i2c_driver drv260x_driver = { 61862306a36Sopenharmony_ci .probe = drv260x_probe, 61962306a36Sopenharmony_ci .driver = { 62062306a36Sopenharmony_ci .name = "drv260x-haptics", 62162306a36Sopenharmony_ci .of_match_table = drv260x_of_match, 62262306a36Sopenharmony_ci .pm = pm_sleep_ptr(&drv260x_pm_ops), 62362306a36Sopenharmony_ci }, 62462306a36Sopenharmony_ci .id_table = drv260x_id, 62562306a36Sopenharmony_ci}; 62662306a36Sopenharmony_cimodule_i2c_driver(drv260x_driver); 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ciMODULE_DESCRIPTION("TI DRV260x haptics driver"); 62962306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 63062306a36Sopenharmony_ciMODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>"); 631