mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-08 04:42:04 -04:00
Merge "input: qcom-hv-haptics: adjust T_LRA for auto RC CLK calibration case"
This commit is contained in:
commit
1e6d5a8199
1 changed files with 418 additions and 87 deletions
|
|
@ -29,7 +29,7 @@
|
|||
#define AUTO_RES_CAL_DONE_BIT BIT(5)
|
||||
#define CAL_TLRA_CL_STS_MSB_MASK GENMASK(4, 0)
|
||||
/* STATUS_DATA_MSB definition in V2 while MOD_STATUS_SEL is 3 */
|
||||
#define LAST_GOOD_TLRA_CL_MASK GENMASK(4, 0)
|
||||
#define LAST_GOOD_TLRA_CL_MASK GENMASK(4, 0)
|
||||
/* STATUS_DATA_MSB definition in V1 while MOD_STATUS_SEL is 5 */
|
||||
#define FIFO_REAL_TIME_FILL_STATUS_MASK_V1 GENMASK(6, 0)
|
||||
/* STATUS DATA_MSB definition in V2 while MOD_STATUS_SEL is 5 */
|
||||
|
|
@ -104,6 +104,14 @@
|
|||
|
||||
#define HAP_CFG_AUTORES_CFG_REG 0x63
|
||||
#define AUTORES_EN_BIT BIT(7)
|
||||
#define AUTORES_EN_DLY_MASK GENMASK(5, 2)
|
||||
#define AUTORES_EN_DLY_1_CYCLE 0x2
|
||||
#define AUTORES_EN_DLY_SHIFT 2
|
||||
#define AUTORES_ERR_WINDOW_MASK GENMASK(1, 0)
|
||||
#define AUTORES_ERR_WINDOW_12P5_PERCENT 0x0
|
||||
#define AUTORES_ERR_WINDOW_25_PERCENT 0x1
|
||||
#define AUTORES_ERR_WINDOW_50_PERCENT 0x2
|
||||
#define AUTORES_ERR_WINDOW_100_PERCENT 0x3
|
||||
|
||||
#define HAP_CFG_AUTORES_ERR_RECOVERY_REG 0x64
|
||||
#define EN_HW_RECOVERY_BIT BIT(1)
|
||||
|
|
@ -117,6 +125,7 @@
|
|||
#define HAP_CFG_VMAX_HDRM_REG 0x67
|
||||
#define VMAX_HDRM_MASK GENMASK(6, 0)
|
||||
#define VMAX_HDRM_STEP_MV 50
|
||||
#define VMAX_HDRM_MAX_MV 6350
|
||||
|
||||
#define HAP_CFG_MOD_STATUS_SEL_REG 0x70
|
||||
#define MOD_STATUS_SEL_CAL_TLRA_CL_STS_VAL 0
|
||||
|
|
@ -128,9 +137,10 @@
|
|||
#define MOD_STATUS_XT_V2_FIFO_FILL_STATUS_VAL 0x80
|
||||
#define MOD_STATUS_XT_SEL_LAST_GOOD_TLRA_VAL 0
|
||||
|
||||
#define HAP_CFG_CAL_EN_REG 0x72
|
||||
#define CAL_RC_CLK_MASK GENMASK(3, 2)
|
||||
#define HAP_CFG_CAL_EN_REG 0x72
|
||||
#define CAL_RC_CLK_MASK GENMASK(3, 2)
|
||||
#define CAL_RC_CLK_SHIFT 2
|
||||
#define CAL_RC_CLK_AUTO_VAL 1
|
||||
#define CAL_RC_CLK_MANUAL_VAL 2
|
||||
|
||||
/* version register definitions for HAPTICS_PATTERN module */
|
||||
|
|
@ -172,6 +182,7 @@
|
|||
|
||||
/* shared registers between V1 and V2 chips */
|
||||
#define HAP_PTN_DIRECT_PLAY_REG 0x26
|
||||
#define DIRECT_PLAY_MAX_AMPLITUDE 0xFF
|
||||
|
||||
#define HAP_PTN_AUTORES_CAL_CFG_REG 0x28
|
||||
|
||||
|
|
@ -364,9 +375,11 @@ struct haptics_play_info {
|
|||
struct haptics_effect *effect;
|
||||
struct brake_cfg *brake;
|
||||
struct fifo_play_status fifo_status;
|
||||
struct mutex lock;
|
||||
u32 vmax_mv;
|
||||
u32 length_us;
|
||||
enum pattern_src pattern_src;
|
||||
bool in_calibration;
|
||||
};
|
||||
|
||||
struct haptics_hw_config {
|
||||
|
|
@ -399,6 +412,7 @@ struct haptics_chip {
|
|||
struct regulator_dev *swr_slave_rdev;
|
||||
struct mutex irq_lock;
|
||||
struct nvmem_cell *cl_brake_nvmem;
|
||||
struct class hap_class;
|
||||
int fifo_empty_irq;
|
||||
u32 effects_count;
|
||||
u32 cfg_addr_base;
|
||||
|
|
@ -489,8 +503,8 @@ static void __dump_effects(struct haptics_chip *chip)
|
|||
pos = 0;
|
||||
pos += scnprintf(str, size, "%s", "FIFO data: ");
|
||||
for (j = 0; j < effect->fifo->num_s; j++)
|
||||
pos += scnprintf(str + pos, size - pos, "%#x ",
|
||||
effect->fifo->samples[j]);
|
||||
pos += scnprintf(str + pos, size - pos, "%d ",
|
||||
(s8)effect->fifo->samples[j]);
|
||||
|
||||
dev_dbg(chip->dev, "%s\n", str);
|
||||
kfree(str);
|
||||
|
|
@ -661,8 +675,28 @@ static int get_brake_play_length_us(struct brake_cfg *brake, u32 t_lra_us)
|
|||
return t_lra_us * (i + 1);
|
||||
}
|
||||
|
||||
#define V1_CL_TLRA_STEP_NS_AUTO_RES_CAL_NOT_DONE 5000
|
||||
#define V1_CL_TLRA_STEP_NS_AUTO_RES_CAL_DONE 3333
|
||||
#define V1_CL_TLRA_STEP_AUTO_RES_CAL_NOT_DONE_NS 5000
|
||||
#define V1_CL_TLRA_STEP_AUTO_RES_CAL_DONE_NS 3333
|
||||
#define AUTO_CAL_CLK_SCALE_DEN 1000
|
||||
#define AUTO_CAL_CLK_SCALE_NUM 1024
|
||||
static int haptics_adjust_lra_period(struct haptics_chip *chip, u32 *t_lra_us)
|
||||
{
|
||||
int rc;
|
||||
u8 val;
|
||||
|
||||
rc = haptics_read(chip, chip->cfg_addr_base,
|
||||
HAP_CFG_CAL_EN_REG, &val, 1);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
if ((val & CAL_RC_CLK_MASK) ==
|
||||
(CAL_RC_CLK_AUTO_VAL << CAL_RC_CLK_SHIFT))
|
||||
*t_lra_us = div_u64((u64)(*t_lra_us) * AUTO_CAL_CLK_SCALE_NUM,
|
||||
AUTO_CAL_CLK_SCALE_DEN);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int haptics_get_closeloop_lra_period_v1(
|
||||
struct haptics_chip *chip)
|
||||
{
|
||||
|
|
@ -694,15 +728,14 @@ static int haptics_get_closeloop_lra_period_v1(
|
|||
*/
|
||||
auto_res_cal_done = !!(val[0] & AUTO_RES_CAL_DONE_BIT);
|
||||
if (auto_res_cal_done)
|
||||
step_ns = V1_CL_TLRA_STEP_NS_AUTO_RES_CAL_DONE;
|
||||
step_ns = V1_CL_TLRA_STEP_AUTO_RES_CAL_DONE_NS;
|
||||
else
|
||||
step_ns = V1_CL_TLRA_STEP_NS_AUTO_RES_CAL_NOT_DONE;
|
||||
step_ns = V1_CL_TLRA_STEP_AUTO_RES_CAL_NOT_DONE_NS;
|
||||
|
||||
tmp = ((val[0] & CAL_TLRA_CL_STS_MSB_MASK) << 8) | val[1];
|
||||
config->cl_t_lra_us = (tmp * step_ns) / 1000;
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
#define CL_TLRA_STEP_NS 1666
|
||||
|
|
@ -752,6 +785,10 @@ static int haptics_get_closeloop_lra_period(struct haptics_chip *chip)
|
|||
return rc;
|
||||
}
|
||||
|
||||
rc = haptics_adjust_lra_period(chip, &chip->config.cl_t_lra_us);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
dev_dbg(chip->dev, "OL_TLRA %u us, CL_TLRA %u us\n",
|
||||
chip->config.t_lra_us, chip->config.cl_t_lra_us);
|
||||
|
||||
|
|
@ -778,6 +815,26 @@ static int haptics_set_vmax_mv(struct haptics_chip *chip, u32 vmax_mv)
|
|||
return rc;
|
||||
}
|
||||
|
||||
static int haptics_set_vmax_headroom_mv(struct haptics_chip *chip, u32 hdrm_mv)
|
||||
{
|
||||
int rc = 0;
|
||||
u8 val;
|
||||
|
||||
if (hdrm_mv > VMAX_HDRM_MAX_MV) {
|
||||
dev_err(chip->dev, "headroom (%d) exceed the max value: %d\n",
|
||||
hdrm_mv, VMAX_HDRM_MAX_MV);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
val = hdrm_mv / VMAX_HDRM_STEP_MV;
|
||||
rc = haptics_write(chip, chip->cfg_addr_base,
|
||||
HAP_CFG_VMAX_HDRM_REG, &val, 1);
|
||||
if (rc < 0)
|
||||
dev_err(chip->dev, "config VMAX_HDRM failed, rc=%d\n", rc);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int haptics_enable_autores(struct haptics_chip *chip, bool en)
|
||||
{
|
||||
int rc;
|
||||
|
|
@ -792,6 +849,18 @@ static int haptics_enable_autores(struct haptics_chip *chip, bool en)
|
|||
return rc;
|
||||
}
|
||||
|
||||
static int haptics_set_direct_play(struct haptics_chip *chip, u8 amplitude)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = haptics_write(chip, chip->ptn_addr_base,
|
||||
HAP_PTN_DIRECT_PLAY_REG, &litude, 1);
|
||||
if (rc < 0)
|
||||
dev_err(chip->dev, "config DIRECT_PLAY failed, rc=%d\n", rc);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int haptics_enable_play(struct haptics_chip *chip, bool en)
|
||||
{
|
||||
struct haptics_play_info *play = &chip->play;
|
||||
|
|
@ -850,6 +919,7 @@ static int haptics_set_pattern(struct haptics_chip *chip,
|
|||
u8 values[SAMPLES_PER_PATTERN * 3] = { 0 };
|
||||
u8 ptn_tlra_addr, ptn_cfg_addr;
|
||||
int i, rc, tmp;
|
||||
u32 play_rate_us;
|
||||
|
||||
if (src != PATTERN1 && src != PATTERN2) {
|
||||
dev_err(chip->dev, "no pattern src specified!\n");
|
||||
|
|
@ -863,8 +933,14 @@ static int haptics_set_pattern(struct haptics_chip *chip,
|
|||
ptn_cfg_addr = HAP_PTN_PTRN2_CFG_REG;
|
||||
}
|
||||
|
||||
/* Adjust T_LRA before programming it into HW */
|
||||
play_rate_us = pattern->play_rate_us;
|
||||
rc = haptics_adjust_lra_period(chip, &play_rate_us);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
/* Configure T_LRA for this pattern */
|
||||
tmp = pattern->play_rate_us / TLRA_STEP_US;
|
||||
tmp = play_rate_us / TLRA_STEP_US;
|
||||
values[0] = (tmp >> 8) & TLRA_OL_MSB_MASK;
|
||||
values[1] = tmp & TLRA_OL_LSB_MASK;
|
||||
rc = haptics_write(chip, chip->ptn_addr_base, ptn_tlra_addr,
|
||||
|
|
@ -1010,8 +1086,7 @@ static int haptics_get_available_fifo_memory(struct haptics_chip *chip)
|
|||
|
||||
static int haptics_adjust_rc_clk(struct haptics_chip *chip)
|
||||
{
|
||||
int rc;
|
||||
int freq_diff, cal_count, f_lra, cl_f_lra;
|
||||
int rc, freq_diff, cal_count, f_lra, cl_f_lra;
|
||||
u8 val[2];
|
||||
|
||||
if (!chip->config.t_lra_us || !chip->config.cl_t_lra_us)
|
||||
|
|
@ -1182,51 +1257,56 @@ static int haptics_set_fifo(struct haptics_chip *chip, struct fifo_cfg *fifo)
|
|||
return haptics_set_fifo_empty_threshold(chip, fifo_thresh);
|
||||
}
|
||||
|
||||
static int haptics_set_direct_play(struct haptics_chip *chip)
|
||||
static int haptics_load_constant_effect(struct haptics_chip *chip, u8 amplitude)
|
||||
{
|
||||
struct haptics_play_info *play = &chip->play;
|
||||
int rc = 0;
|
||||
u8 val;
|
||||
|
||||
mutex_lock(&chip->play.lock);
|
||||
if (chip->play.in_calibration) {
|
||||
dev_err(chip->dev, "calibration in progress, ignore playing constant effect\n");
|
||||
rc = -EBUSY;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
/* configure VMAX in case it was changed in previous effect playing */
|
||||
rc = haptics_set_vmax_mv(chip, chip->config.vmax_mv);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
/* Set DIRECT_PLAY amplitude */
|
||||
val = play->vmax_mv / VMAX_STEP_MV;
|
||||
rc = haptics_write(chip, chip->ptn_addr_base,
|
||||
HAP_PTN_DIRECT_PLAY_REG, &val, 1);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
goto unlock;
|
||||
|
||||
/* Config brake settings if it's necessary */
|
||||
play->brake = &chip->config.brake;
|
||||
if (play->brake) {
|
||||
rc = haptics_set_brake(chip, play->brake);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
rc = haptics_set_direct_play(chip, amplitude);
|
||||
if (rc < 0)
|
||||
goto unlock;
|
||||
|
||||
/* Always enable LRA auto resonance for DIRECT_PLAY */
|
||||
rc = haptics_enable_autores(chip, !chip->config.is_erm);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
goto unlock;
|
||||
|
||||
play->pattern_src = DIRECT_PLAY;
|
||||
return 0;
|
||||
unlock:
|
||||
mutex_unlock(&chip->play.lock);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int haptics_load_predefined_effect(struct haptics_chip *chip,
|
||||
int effect_idx)
|
||||
struct haptics_effect *effect)
|
||||
{
|
||||
struct haptics_play_info *play = &chip->play;
|
||||
int rc;
|
||||
|
||||
if (effect_idx >= chip->effects_count)
|
||||
if (effect == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
play->effect = &chip->effects[effect_idx];
|
||||
play->effect = effect;
|
||||
/* Clamp VMAX for different vibration strength */
|
||||
rc = haptics_set_vmax_mv(chip, play->vmax_mv);
|
||||
if (rc < 0)
|
||||
|
|
@ -1393,12 +1473,19 @@ static int haptics_load_custom_effect(struct haptics_chip *chip,
|
|||
fifo->play_length_us = get_fifo_play_length_us(fifo,
|
||||
chip->custom_effect->t_lra_us);
|
||||
|
||||
mutex_lock(&chip->play.lock);
|
||||
if (chip->play.in_calibration) {
|
||||
dev_err(chip->dev, "calibration in progress, ignore playing custom effect\n");
|
||||
rc = -EBUSY;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
play->effect = chip->custom_effect;
|
||||
play->brake = NULL;
|
||||
play->vmax_mv = (magnitude * chip->custom_effect->vmax_mv) / 0x7fff;
|
||||
rc = haptics_set_vmax_mv(chip, play->vmax_mv);
|
||||
if (rc < 0)
|
||||
goto cleanup;
|
||||
goto unlock;
|
||||
|
||||
rc = haptics_enable_autores(chip, !play->effect->auto_res_disable);
|
||||
if (rc < 0)
|
||||
|
|
@ -1407,9 +1494,12 @@ static int haptics_load_custom_effect(struct haptics_chip *chip,
|
|||
play->pattern_src = FIFO;
|
||||
rc = haptics_set_fifo(chip, play->effect->fifo);
|
||||
if (rc < 0)
|
||||
goto cleanup;
|
||||
goto unlock;
|
||||
|
||||
mutex_unlock(&chip->play.lock);
|
||||
return 0;
|
||||
unlock:
|
||||
mutex_unlock(&chip->play.lock);
|
||||
cleanup:
|
||||
kfree(fifo->samples);
|
||||
fifo->samples = NULL;
|
||||
|
|
@ -1456,16 +1546,25 @@ static int haptics_load_periodic_effect(struct haptics_chip *chip,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
mutex_lock(&chip->play.lock);
|
||||
play->vmax_mv = (magnitude * chip->effects[i].vmax_mv) / 0x7fff;
|
||||
|
||||
dev_dbg(chip->dev, "upload effect %d, vmax_mv=%d\n",
|
||||
chip->effects[i].id, play->vmax_mv);
|
||||
rc = haptics_load_predefined_effect(chip, i);
|
||||
|
||||
if (chip->play.in_calibration) {
|
||||
dev_err(chip->dev, "calibration in progress, ignore playing predefined effect\n");
|
||||
rc = -EBUSY;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
rc = haptics_load_predefined_effect(chip, &chip->effects[i]);
|
||||
if (rc < 0) {
|
||||
dev_err(chip->dev, "Play predefined effect%d failed, rc=%d\n",
|
||||
chip->effects[i].id, rc);
|
||||
return rc;
|
||||
goto unlock;
|
||||
}
|
||||
mutex_unlock(&chip->play.lock);
|
||||
|
||||
play->length_us = get_play_length_us(play);
|
||||
custom_data[CUSTOM_DATA_TIMEOUT_SEC_IDX] =
|
||||
|
|
@ -1477,25 +1576,29 @@ static int haptics_load_periodic_effect(struct haptics_chip *chip,
|
|||
return -EFAULT;
|
||||
|
||||
return 0;
|
||||
unlock:
|
||||
mutex_unlock(&chip->play.lock);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int haptics_upload_effect(struct input_dev *dev,
|
||||
struct ff_effect *effect, struct ff_effect *old)
|
||||
{
|
||||
struct haptics_chip *chip = input_get_drvdata(dev);
|
||||
struct haptics_hw_config *config = &chip->config;
|
||||
struct haptics_play_info *play = &chip->play;
|
||||
u32 length_us, tmp;
|
||||
s16 level;
|
||||
u8 amplitude;
|
||||
int rc = 0;
|
||||
|
||||
switch (effect->type) {
|
||||
case FF_CONSTANT:
|
||||
play->length_us = effect->replay.length * USEC_PER_MSEC;
|
||||
length_us = effect->replay.length * USEC_PER_MSEC;
|
||||
level = effect->u.constant.level;
|
||||
play->vmax_mv = (level * config->vmax_mv) / 0x7fff;
|
||||
dev_dbg(chip->dev, "upload constant effect, length = %dus, vmax_mv = %d\n",
|
||||
play->length_us, play->vmax_mv);
|
||||
haptics_set_direct_play(chip);
|
||||
tmp = level * DIRECT_PLAY_MAX_AMPLITUDE;
|
||||
amplitude = tmp / 0x7fff;
|
||||
dev_dbg(chip->dev, "upload constant effect, length = %dus, amplitude = %d\n",
|
||||
length_us, amplitude);
|
||||
haptics_load_constant_effect(chip, amplitude);
|
||||
if (rc < 0) {
|
||||
dev_err(chip->dev, "set direct play failed, rc=%d\n",
|
||||
rc);
|
||||
|
|
@ -1557,6 +1660,33 @@ static void haptics_fifo_empty_irq_config(struct haptics_chip *chip,
|
|||
mutex_unlock(&chip->irq_lock);
|
||||
}
|
||||
|
||||
static int haptics_stop_fifo_play(struct haptics_chip *chip)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (atomic_read(&chip->play.fifo_status.is_busy) == 0) {
|
||||
dev_dbg(chip->dev, "FIFO playing is not in progress\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
rc = haptics_enable_play(chip, false);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
/* restore FIFO play rate back to T_LRA */
|
||||
rc = haptics_set_fifo_playrate(chip, T_LRA);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
haptics_fifo_empty_irq_config(chip, false);
|
||||
kfree(chip->custom_effect->fifo->samples);
|
||||
chip->custom_effect->fifo->samples = NULL;
|
||||
|
||||
atomic_set(&chip->play.fifo_status.is_busy, 0);
|
||||
dev_dbg(chip->dev, "stopped FIFO playing successfully\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int haptics_playback(struct input_dev *dev, int effect_id, int val)
|
||||
{
|
||||
struct haptics_chip *chip = input_get_drvdata(dev);
|
||||
|
|
@ -1572,7 +1702,8 @@ static int haptics_playback(struct input_dev *dev, int effect_id, int val)
|
|||
if (play->pattern_src == FIFO)
|
||||
haptics_fifo_empty_irq_config(chip, true);
|
||||
} else {
|
||||
if (atomic_read(&play->fifo_status.is_busy)) {
|
||||
if (play->pattern_src == FIFO &&
|
||||
atomic_read(&play->fifo_status.is_busy)) {
|
||||
dev_dbg(chip->dev, "FIFO playing is not done yet, defer stopping in erase\n");
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1591,25 +1722,16 @@ static int haptics_erase(struct input_dev *dev, int effect_id)
|
|||
|
||||
if ((play->pattern_src == FIFO) &&
|
||||
atomic_read(&play->fifo_status.is_busy)) {
|
||||
dev_dbg(chip->dev, "cancelling FIFO playing\n");
|
||||
atomic_set(&play->fifo_status.cancelled, 1);
|
||||
if (atomic_read(&play->fifo_status.written_done) == 0) {
|
||||
dev_dbg(chip->dev, "cancelling FIFO playing\n");
|
||||
atomic_set(&play->fifo_status.cancelled, 1);
|
||||
}
|
||||
|
||||
rc = haptics_enable_play(chip, false);
|
||||
if (rc < 0)
|
||||
rc = haptics_stop_fifo_play(chip);
|
||||
if (rc < 0) {
|
||||
dev_err(chip->dev, "stop FIFO playing failed, rc=%d\n");
|
||||
return rc;
|
||||
|
||||
atomic_set(&play->fifo_status.is_busy, 0);
|
||||
|
||||
/* free custom_effect FIFO samples buffer after stopping play */
|
||||
kfree(chip->custom_effect->fifo->samples);
|
||||
chip->custom_effect->fifo->samples = NULL;
|
||||
|
||||
/* restore FIFO play rate back to T_LRA */
|
||||
rc = haptics_set_fifo_playrate(chip, T_LRA);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
haptics_fifo_empty_irq_config(chip, false);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
@ -1657,12 +1779,31 @@ static int haptics_store_cl_brake_settings(struct haptics_chip *chip)
|
|||
return rc;
|
||||
}
|
||||
|
||||
static int haptics_config_openloop_lra_period(struct haptics_chip *chip,
|
||||
u32 t_lra_us)
|
||||
{
|
||||
u32 tmp;
|
||||
u8 val[2];
|
||||
int rc;
|
||||
|
||||
rc = haptics_adjust_lra_period(chip, &t_lra_us);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
tmp = t_lra_us / TLRA_STEP_US;
|
||||
val[0] = (tmp >> 8) & TLRA_OL_MSB_MASK;
|
||||
val[1] = tmp & TLRA_OL_LSB_MASK;
|
||||
|
||||
return haptics_write(chip, chip->cfg_addr_base,
|
||||
HAP_CFG_TLRA_OL_HIGH_REG, val, 2);
|
||||
}
|
||||
|
||||
static int haptics_hw_init(struct haptics_chip *chip)
|
||||
{
|
||||
struct haptics_hw_config *config = &chip->config;
|
||||
struct haptics_effect *effect;
|
||||
int rc = 0, tmp, i;
|
||||
u8 val[2];
|
||||
int rc = 0, i;
|
||||
u8 val;
|
||||
|
||||
/* Store CL brake settings */
|
||||
rc = haptics_store_cl_brake_settings(chip);
|
||||
|
|
@ -1682,13 +1823,13 @@ static int haptics_hw_init(struct haptics_chip *chip)
|
|||
return rc;
|
||||
|
||||
/* Config brake mode and waveform shape */
|
||||
val[0] = (config->brake.mode << BRAKE_MODE_SHIFT)
|
||||
val = (config->brake.mode << BRAKE_MODE_SHIFT)
|
||||
| config->brake.sine_gain << BRAKE_SINE_GAIN_SHIFT
|
||||
| config->brake.brake_wf;
|
||||
rc = haptics_masked_write(chip, chip->cfg_addr_base,
|
||||
HAP_CFG_BRAKE_MODE_CFG_REG,
|
||||
BRAKE_MODE_MASK | BRAKE_SINE_GAIN_MASK
|
||||
| BRAKE_WF_SEL_MASK, val[0]);
|
||||
| BRAKE_WF_SEL_MASK, val);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
|
|
@ -1700,11 +1841,7 @@ static int haptics_hw_init(struct haptics_chip *chip)
|
|||
return rc;
|
||||
|
||||
/* Config T_LRA */
|
||||
tmp = config->t_lra_us / TLRA_STEP_US;
|
||||
val[0] = (tmp >> 8) & TLRA_OL_MSB_MASK;
|
||||
val[1] = tmp & TLRA_OL_LSB_MASK;
|
||||
rc = haptics_write(chip, chip->cfg_addr_base,
|
||||
HAP_CFG_TLRA_OL_HIGH_REG, val, 2);
|
||||
rc = haptics_config_openloop_lra_period(chip, chip->config.cl_t_lra_us);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
|
|
@ -1743,24 +1880,24 @@ static irqreturn_t fifo_empty_irq_handler(int irq, void *data)
|
|||
int rc, num;
|
||||
|
||||
if (atomic_read(&chip->play.fifo_status.written_done) == 1) {
|
||||
dev_dbg(chip->dev, "FIFO data is done playing\n");
|
||||
rc = haptics_enable_play(chip, false);
|
||||
/*
|
||||
* Check the FIFO real time fill status before stopping
|
||||
* play to make sure that all FIFO samples can be played
|
||||
* successfully. If there are still samples left in FIFO
|
||||
* memory, defer the stop into erase() function.
|
||||
*/
|
||||
num = haptics_get_available_fifo_memory(chip);
|
||||
if (num != MAX_FIFO_SAMPLES(chip)) {
|
||||
dev_dbg(chip->dev, "%d FIFO samples still in playing\n",
|
||||
MAX_FIFO_SAMPLES(chip) - num);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
rc = haptics_stop_fifo_play(chip);
|
||||
if (rc < 0)
|
||||
return IRQ_HANDLED;
|
||||
|
||||
/* restore FIFO play rate back to T_LRA */
|
||||
rc = haptics_set_fifo_playrate(chip, T_LRA);
|
||||
if (rc < 0)
|
||||
return IRQ_HANDLED;
|
||||
|
||||
haptics_fifo_empty_irq_config(chip, false);
|
||||
|
||||
/* free custom_effect FIFO samples after playing is done */
|
||||
kfree(chip->custom_effect->fifo->samples);
|
||||
chip->custom_effect->fifo->samples = NULL;
|
||||
|
||||
atomic_set(&chip->play.fifo_status.written_done, 0);
|
||||
atomic_set(&chip->play.fifo_status.is_busy, 0);
|
||||
dev_dbg(chip->dev, "FIFO playing is done\n");
|
||||
} else {
|
||||
if (atomic_read(&status->cancelled) == 1) {
|
||||
dev_dbg(chip->dev, "FIFO programming got cancelled\n");
|
||||
|
|
@ -1976,7 +2113,7 @@ static int pattern_play_rate_us_dbgfs_write(void *data, u64 val)
|
|||
}
|
||||
DEFINE_DEBUGFS_ATTRIBUTE(pattern_play_rate_dbgfs_ops,
|
||||
pattern_play_rate_us_dbgfs_read,
|
||||
pattern_play_rate_us_dbgfs_write, "%lld\n");
|
||||
pattern_play_rate_us_dbgfs_write, "%llu\n");
|
||||
|
||||
static ssize_t fifo_s_dbgfs_read(struct file *fp,
|
||||
char __user *buf, size_t count, loff_t *ppos)
|
||||
|
|
@ -1994,7 +2131,7 @@ static ssize_t fifo_s_dbgfs_read(struct file *fp,
|
|||
|
||||
for (i = 0; i < fifo->num_s; i++)
|
||||
pos += scnprintf(kbuf + pos, size - pos,
|
||||
"0x%03x ", fifo->samples[i]);
|
||||
"%d ", (s8)fifo->samples[i]);
|
||||
|
||||
pos += scnprintf(kbuf + pos, size - pos, "%s", "\n");
|
||||
rc = simple_read_from_buffer(buf, count, ppos, kbuf, pos);
|
||||
|
|
@ -2010,7 +2147,7 @@ static ssize_t fifo_s_dbgfs_write(struct file *fp,
|
|||
struct fifo_cfg *fifo = effect->fifo;
|
||||
char *kbuf, *token;
|
||||
int rc, i = 0;
|
||||
u32 val;
|
||||
int val;
|
||||
u8 *samples;
|
||||
|
||||
kbuf = kzalloc(count + 1, GFP_KERNEL);
|
||||
|
|
@ -2033,7 +2170,7 @@ static ssize_t fifo_s_dbgfs_write(struct file *fp,
|
|||
}
|
||||
|
||||
while ((token = strsep(&kbuf, " ")) != NULL) {
|
||||
rc = kstrtouint(token, 0, &val);
|
||||
rc = kstrtoint(token, 0, &val);
|
||||
if (rc < 0) {
|
||||
rc = -EINVAL;
|
||||
goto exit2;
|
||||
|
|
@ -2042,7 +2179,7 @@ static ssize_t fifo_s_dbgfs_write(struct file *fp,
|
|||
if (val > 0xff)
|
||||
val = 0xff;
|
||||
|
||||
samples[i++] = val;
|
||||
samples[i++] = (u8)val;
|
||||
/* only support fifo pattern no longer than before */
|
||||
if (i >= fifo->num_s)
|
||||
break;
|
||||
|
|
@ -2100,7 +2237,7 @@ static int fifo_period_dbgfs_write(void *data, u64 val)
|
|||
|
||||
DEFINE_DEBUGFS_ATTRIBUTE(fifo_period_dbgfs_ops,
|
||||
fifo_period_dbgfs_read,
|
||||
fifo_period_dbgfs_write, "%lld\n");
|
||||
fifo_period_dbgfs_write, "%llu\n");
|
||||
|
||||
static ssize_t brake_s_dbgfs_read(struct file *fp,
|
||||
char __user *buf, size_t count, loff_t *ppos)
|
||||
|
|
@ -2245,6 +2382,50 @@ static const struct file_operations brake_mode_dbgfs_ops = {
|
|||
.open = simple_open,
|
||||
};
|
||||
|
||||
static int brake_en_dbgfs_read(void *data, u64 *val)
|
||||
{
|
||||
struct haptics_effect *effect = data;
|
||||
|
||||
*val = !effect->brake->disabled;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int brake_en_dbgfs_write(void *data, u64 val)
|
||||
{
|
||||
struct haptics_effect *effect = data;
|
||||
|
||||
effect->brake->disabled = !val;
|
||||
|
||||
return 0;
|
||||
}
|
||||
DEFINE_DEBUGFS_ATTRIBUTE(brake_en_dbgfs_ops, brake_en_dbgfs_read,
|
||||
brake_en_dbgfs_write, "%llu\n");
|
||||
|
||||
static int brake_sine_gain_dbgfs_read(void *data, u64 *val)
|
||||
{
|
||||
struct haptics_effect *effect = data;
|
||||
|
||||
*val = effect->brake->sine_gain;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int brake_sine_gain_dbgfs_write(void *data, u64 val)
|
||||
{
|
||||
struct haptics_effect *effect = data;
|
||||
|
||||
if (val > BRAKE_SINE_GAIN_X8)
|
||||
return -EINVAL;
|
||||
|
||||
effect->brake->sine_gain = val;
|
||||
|
||||
return 0;
|
||||
}
|
||||
DEFINE_DEBUGFS_ATTRIBUTE(brake_sine_gain_dbgfs_ops,
|
||||
brake_sine_gain_dbgfs_read,
|
||||
brake_sine_gain_dbgfs_write, "%llu\n");
|
||||
|
||||
static int preload_effect_idx_dbgfs_read(void *data, u64 *val)
|
||||
{
|
||||
struct haptics_chip *chip = data;
|
||||
|
|
@ -2291,7 +2472,7 @@ static int preload_effect_idx_dbgfs_write(void *data, u64 val)
|
|||
|
||||
DEFINE_DEBUGFS_ATTRIBUTE(preload_effect_idx_dbgfs_ops,
|
||||
preload_effect_idx_dbgfs_read,
|
||||
preload_effect_idx_dbgfs_write, "%lld\n");
|
||||
preload_effect_idx_dbgfs_write, "%llu\n");
|
||||
|
||||
static int haptics_add_effects_debugfs(struct haptics_effect *effect,
|
||||
struct dentry *dir)
|
||||
|
|
@ -2354,6 +2535,16 @@ static int haptics_add_effects_debugfs(struct haptics_effect *effect,
|
|||
effect, &brake_mode_dbgfs_ops);
|
||||
if (IS_ERR(file))
|
||||
return PTR_ERR(file);
|
||||
|
||||
file = debugfs_create_file_unsafe("enable", 0644, brake_dir,
|
||||
effect, &brake_en_dbgfs_ops);
|
||||
if (IS_ERR(file))
|
||||
return PTR_ERR(file);
|
||||
|
||||
file = debugfs_create_file_unsafe("sine_gain", 0644, brake_dir,
|
||||
effect, &brake_sine_gain_dbgfs_ops);
|
||||
if (IS_ERR(file))
|
||||
return PTR_ERR(file);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
@ -2971,6 +3162,136 @@ static int haptics_init_swr_slave_regulator(struct haptics_chip *chip)
|
|||
return rc;
|
||||
}
|
||||
|
||||
#define LRA_CALIBRATION_VMAX_HDRM_MV 500
|
||||
static int haptics_start_lra_calibration(struct haptics_chip *chip)
|
||||
{
|
||||
int rc;
|
||||
u8 autores_cfg;
|
||||
|
||||
mutex_lock(&chip->play.lock);
|
||||
/*
|
||||
* Ignore calibration if it's in FIFO playing to avoid
|
||||
* messing up the FIFO playing status
|
||||
*/
|
||||
if ((chip->play.pattern_src == FIFO) &&
|
||||
atomic_read(&chip->play.fifo_status.is_busy)) {
|
||||
dev_err(chip->dev, "In FIFO playing, ignore calibration\n");
|
||||
rc = -EBUSY;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
/* Stop other mode playing if there is any */
|
||||
rc = haptics_enable_play(chip, false);
|
||||
if (rc < 0) {
|
||||
dev_err(chip->dev, "Stop playing failed, rc=%d\n", rc);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
chip->play.in_calibration = true;
|
||||
|
||||
rc = haptics_read(chip, chip->cfg_addr_base,
|
||||
HAP_CFG_AUTORES_CFG_REG, &autores_cfg, 1);
|
||||
if (rc < 0) {
|
||||
dev_err(chip->dev, "Read AUTORES_CFG failed, rc=%d\n", rc);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
rc = haptics_masked_write(chip, chip->cfg_addr_base,
|
||||
HAP_CFG_AUTORES_CFG_REG, AUTORES_EN_BIT |
|
||||
AUTORES_EN_DLY_MASK | AUTORES_ERR_WINDOW_MASK,
|
||||
AUTORES_EN_DLY_1_CYCLE << AUTORES_EN_DLY_SHIFT
|
||||
| AUTORES_ERR_WINDOW_25_PERCENT | AUTORES_EN_BIT);
|
||||
if (rc < 0)
|
||||
goto unlock;
|
||||
|
||||
rc = haptics_config_openloop_lra_period(chip, chip->config.t_lra_us);
|
||||
if (rc < 0)
|
||||
goto restore;
|
||||
|
||||
rc = haptics_set_vmax_mv(chip, chip->config.vmax_mv);
|
||||
if (rc < 0)
|
||||
goto restore;
|
||||
|
||||
rc = haptics_set_vmax_headroom_mv(chip, LRA_CALIBRATION_VMAX_HDRM_MV);
|
||||
if (rc < 0)
|
||||
goto restore;
|
||||
|
||||
rc = haptics_set_direct_play(chip, DIRECT_PLAY_MAX_AMPLITUDE);
|
||||
if (rc < 0)
|
||||
goto restore;
|
||||
|
||||
chip->play.pattern_src = DIRECT_PLAY;
|
||||
rc = haptics_enable_play(chip, true);
|
||||
if (rc < 0) {
|
||||
dev_err(chip->dev, "Start calibration failed, rc=%d\n", rc);
|
||||
goto restore;
|
||||
}
|
||||
|
||||
/* wait for ~60ms to get the LRA calibration result */
|
||||
usleep_range(60000, 65000);
|
||||
|
||||
rc = haptics_get_closeloop_lra_period(chip);
|
||||
if (rc < 0)
|
||||
goto restore;
|
||||
|
||||
rc = haptics_enable_play(chip, false);
|
||||
if (rc < 0)
|
||||
goto restore;
|
||||
|
||||
haptics_config_openloop_lra_period(chip, chip->config.cl_t_lra_us);
|
||||
|
||||
restore:
|
||||
rc = haptics_write(chip, chip->cfg_addr_base,
|
||||
HAP_CFG_AUTORES_CFG_REG, &autores_cfg, 1);
|
||||
unlock:
|
||||
chip->play.in_calibration = false;
|
||||
mutex_unlock(&chip->play.lock);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static ssize_t lra_calibration_store(struct class *c,
|
||||
struct class_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
struct haptics_chip *chip = container_of(c,
|
||||
struct haptics_chip, hap_class);
|
||||
bool val;
|
||||
int rc;
|
||||
|
||||
if (kstrtobool(buf, &val))
|
||||
return -EINVAL;
|
||||
|
||||
if (val) {
|
||||
rc = haptics_start_lra_calibration(chip);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
static CLASS_ATTR_WO(lra_calibration);
|
||||
|
||||
static ssize_t lra_frequency_hz_show(struct class *c,
|
||||
struct class_attribute *attr, char *buf)
|
||||
{
|
||||
struct haptics_chip *chip = container_of(c,
|
||||
struct haptics_chip, hap_class);
|
||||
u32 cl_f_lra;
|
||||
|
||||
if (chip->config.cl_t_lra_us == 0)
|
||||
return -EINVAL;
|
||||
|
||||
cl_f_lra = USEC_PER_SEC / chip->config.cl_t_lra_us;
|
||||
return scnprintf(buf, PAGE_SIZE, "%d Hz\n", cl_f_lra);
|
||||
}
|
||||
static CLASS_ATTR_RO(lra_frequency_hz);
|
||||
|
||||
static struct attribute *hap_class_attrs[] = {
|
||||
&class_attr_lra_calibration.attr,
|
||||
&class_attr_lra_frequency_hz.attr,
|
||||
NULL,
|
||||
};
|
||||
ATTRIBUTE_GROUPS(hap_class);
|
||||
|
||||
static int haptics_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct haptics_chip *chip;
|
||||
|
|
@ -3028,6 +3349,7 @@ static int haptics_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
mutex_init(&chip->irq_lock);
|
||||
mutex_init(&chip->play.lock);
|
||||
disable_irq_nosync(chip->fifo_empty_irq);
|
||||
chip->fifo_empty_irq_en = false;
|
||||
|
||||
|
|
@ -3070,6 +3392,14 @@ static int haptics_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
dev_set_drvdata(chip->dev, chip);
|
||||
chip->hap_class.name = "qcom-haptics";
|
||||
chip->hap_class.class_groups = hap_class_groups;
|
||||
rc = class_register(&chip->hap_class);
|
||||
if (rc < 0) {
|
||||
dev_err(chip->dev, "register hap_class failed, rc=%d\n");
|
||||
goto destroy_ff;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
rc = haptics_create_debugfs(chip);
|
||||
if (rc < 0)
|
||||
|
|
@ -3085,6 +3415,7 @@ static int haptics_remove(struct platform_device *pdev)
|
|||
{
|
||||
struct haptics_chip *chip = dev_get_drvdata(&pdev->dev);
|
||||
|
||||
class_unregister(&chip->hap_class);
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
debugfs_remove_recursive(chip->debugfs_dir);
|
||||
#endif
|
||||
|
|
|
|||
Loading…
Reference in a new issue