/* sysfs store for wave samples repeat */ static ssize_t qpnp_hap_wf_s_rep_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct timed_output_dev *timed_dev = dev_get_drvdata(dev); struct qpnp_hap *hap = container_of(timed_dev, struct qpnp_hap, timed_dev); int data, rc, temp; u8 reg; if (sscanf(buf, "%d", &data) != 1) return -EINVAL; if (data < QPNP_HAP_WAV_S_REP_MIN) data = QPNP_HAP_WAV_S_REP_MIN; else if (data > QPNP_HAP_WAV_S_REP_MAX) data = QPNP_HAP_WAV_S_REP_MAX; rc = qpnp_hap_read_reg(hap, ®, QPNP_HAP_WAV_REP_REG(hap->base)); if (rc < 0) return rc; reg &= QPNP_HAP_WAV_S_REP_MASK; temp = fls(data) - 1; reg |= temp; rc = qpnp_hap_write_reg(hap, ®, QPNP_HAP_WAV_REP_REG(hap->base)); if (rc) return rc; hap->wave_s_rep_cnt = data; return count; }
/* sysfs store for max volatge */ static ssize_t qpnp_hap_max_volatge_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct timed_output_dev *timed_dev = dev_get_drvdata(dev); struct qpnp_hap *hap = container_of(timed_dev, struct qpnp_hap, timed_dev); int data, rc, temp; u8 reg; if (sscanf(buf, "%d", &data) != 1) return -EINVAL; if (data < QPNP_HAP_VMAX_MIN_MV) data = QPNP_HAP_VMAX_MIN_MV; else if (data > QPNP_HAP_VMAX_MAX_MV) data = QPNP_HAP_VMAX_MAX_MV; rc = qpnp_hap_read_reg(hap, ®, QPNP_HAP_VMAX_REG(hap->base)); if (rc < 0) return rc; reg &= QPNP_HAP_VMAX_MASK; temp = data / QPNP_HAP_VMAX_MIN_MV; reg |= (temp << QPNP_HAP_VMAX_SHIFT); rc = qpnp_hap_write_reg(hap, ®, QPNP_HAP_VMAX_REG(hap->base)); if (rc) return rc; hap->vmax_mv = data; return count; }
/* configuration api for max volatge */ static int qpnp_hap_vmax_config(struct qpnp_hap *hap , int odrb) { u8 reg = 0; int rc, temp; if (hap->vmax_mv < QPNP_HAP_VMAX_MIN_MV) hap->vmax_mv = QPNP_HAP_VMAX_MIN_MV; else if (hap->vmax_mv > QPNP_HAP_VMAX_MAX_MV) { /* */ /* When Over drive or Reverse braking occur , odrb = 1 */ if(odrb) hap->vmax_mv = QPNP_HAP_OV_RB_MV; else hap->vmax_mv = QPNP_HAP_VMAX_MAX_MV; } rc = qpnp_hap_read_reg(hap, ®, QPNP_HAP_VMAX_REG(hap->base)); if (rc < 0) return rc; reg &= QPNP_HAP_VMAX_MASK; temp = hap->vmax_mv / QPNP_HAP_VMAX_MIN_MV; reg |= (temp << QPNP_HAP_VMAX_SHIFT); rc = qpnp_hap_write_reg(hap, ®, QPNP_HAP_VMAX_REG(hap->base)); if (rc) return rc; return 0; }
/* configuration api for buffer mode */ static int qpnp_hap_buffer_config(struct qpnp_hap *hap) { u8 reg = 0; int rc, i, temp; /* Configure the WAVE_REPEAT register */ if (hap->wave_rep_cnt < QPNP_HAP_WAV_REP_MIN) hap->wave_rep_cnt = QPNP_HAP_WAV_REP_MIN; else if (hap->wave_rep_cnt > QPNP_HAP_WAV_REP_MAX) hap->wave_rep_cnt = QPNP_HAP_WAV_REP_MAX; if (hap->wave_s_rep_cnt < QPNP_HAP_WAV_S_REP_MIN) hap->wave_s_rep_cnt = QPNP_HAP_WAV_S_REP_MIN; else if (hap->wave_s_rep_cnt > QPNP_HAP_WAV_S_REP_MAX) hap->wave_s_rep_cnt = QPNP_HAP_WAV_S_REP_MAX; rc = qpnp_hap_read_reg(hap, ®, QPNP_HAP_WAV_REP_REG(hap->base)); if (rc < 0) return rc; reg &= QPNP_HAP_WAV_REP_MASK; temp = fls(hap->wave_rep_cnt) - 1; reg |= (temp << QPNP_HAP_WAV_REP_SHFT); reg &= QPNP_HAP_WAV_S_REP_MASK; temp = fls(hap->wave_s_rep_cnt) - 1; reg |= temp; rc = qpnp_hap_write_reg(hap, ®, QPNP_HAP_WAV_REP_REG(hap->base)); if (rc) return rc; /* Configure WAVE_SAMPLE1 to WAVE_SAMPLE8 register */ for (i = 0, reg = 0; i < QPNP_HAP_WAV_SAMP_LEN; i++) { reg = hap->wave_samp[i]; rc = qpnp_hap_write_reg(hap, ®, QPNP_HAP_WAV_S_REG_BASE(hap->base) + i); if (rc) return rc; } /* setup play irq */ if (hap->use_play_irq) { rc = devm_request_threaded_irq(&hap->spmi->dev, hap->play_irq, NULL, qpnp_hap_play_irq, QPNP_IRQ_FLAGS, "qpnp_play_irq", hap); if (rc < 0) { dev_err(&hap->spmi->dev, "Unable to request play(%d) IRQ(err:%d)\n", hap->play_irq, rc); return rc; } } hap->buffer_cfg_state = true; return 0; }
/* configuration api for pwm */ static int qpnp_hap_pwm_config(struct qpnp_hap *hap) { u8 reg = 0; int rc, temp; /* Configure the EXTERNAL_PWM register */ if (hap->ext_pwm_freq_khz <= QPNP_HAP_EXT_PWM_FREQ_25_KHZ) { hap->ext_pwm_freq_khz = QPNP_HAP_EXT_PWM_FREQ_25_KHZ; temp = 0; } else if (hap->ext_pwm_freq_khz <= QPNP_HAP_EXT_PWM_FREQ_50_KHZ) { hap->ext_pwm_freq_khz = QPNP_HAP_EXT_PWM_FREQ_50_KHZ; temp = 1; } else if (hap->ext_pwm_freq_khz <= QPNP_HAP_EXT_PWM_FREQ_75_KHZ) { hap->ext_pwm_freq_khz = QPNP_HAP_EXT_PWM_FREQ_75_KHZ; temp = 2; } else { hap->ext_pwm_freq_khz = QPNP_HAP_EXT_PWM_FREQ_100_KHZ; temp = 3; } rc = qpnp_hap_read_reg(hap, ®, QPNP_HAP_EXT_PWM_REG(hap->base)); if (rc < 0) return rc; reg &= QPNP_HAP_EXT_PWM_MASK; reg |= temp; rc = qpnp_hap_write_reg(hap, ®, QPNP_HAP_EXT_PWM_REG(hap->base)); if (rc) return rc; hap->pwm_info.pwm_dev = pwm_request(hap->pwm_info.pwm_channel, "qpnp-hap"); if (IS_ERR_OR_NULL(hap->pwm_info.pwm_dev)) { dev_err(&hap->spmi->dev, "hap pwm request failed\n"); return -ENODEV; } rc = pwm_config(hap->pwm_info.pwm_dev, hap->pwm_info.duty_us, hap->pwm_info.period_us); if (rc < 0) { dev_err(&hap->spmi->dev, "hap pwm config failed\n"); pwm_free(hap->pwm_info.pwm_dev); return -ENODEV; } hap->pwm_cfg_state = true; return 0; }
/* configuration api for play mode */ static int qpnp_hap_play_mode_config(struct qpnp_hap *hap) { u8 reg = 0; int rc, temp; rc = qpnp_hap_read_reg(hap, ®, QPNP_HAP_PLAY_MODE_REG(hap->base)); if (rc < 0) return rc; reg &= QPNP_HAP_PLAY_MODE_MASK; temp = hap->play_mode << QPNP_HAP_PLAY_MODE_SHFT; reg |= temp; rc = qpnp_hap_write_reg(hap, ®, QPNP_HAP_PLAY_MODE_REG(hap->base)); if (rc) return rc; return 0; }
/* sysfs store for amp */ static ssize_t qpnp_hap_amp_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct timed_output_dev *timed_dev = dev_get_drvdata(dev); struct qpnp_hap *hap = container_of(timed_dev, struct qpnp_hap, timed_dev); u8 reg = 0; int temp, ret, value; if (sscanf(buf, "%d", &value) != 1) return -EINVAL; if (value < QPNP_HAP_VMAX_MIN_MV) value = QPNP_HAP_VMAX_MIN_MV; else if (value > QPNP_HAP_VMAX_MAX_MV) value = QPNP_HAP_VMAX_MAX_MV; ret = qpnp_hap_read_reg(hap, ®, QPNP_HAP_VMAX_REG(hap->base)); if (ret < 0) { dev_err(&hap->spmi->dev, "Error reading address: %X\n", QPNP_HAP_VMAX_REG(hap->base)); } reg &= QPNP_HAP_VMAX_MASK; /* Vmax Controlled by 116mV step. So we divide our input Voltage by 116 */ temp = value / QPNP_HAP_VMAX_MIN_MV; /* Changed Vmax have to save to hap->vmax_mv and hap->vmax_mv_orig to recover vmax that changed here */ hap->vmax_mv = hap->vmax_mv_orig = temp * QPNP_HAP_VMAX_MIN_MV; reg |= (temp << QPNP_HAP_VMAX_SHIFT); ret = qpnp_hap_write_reg(hap, ®, QPNP_HAP_VMAX_REG(hap->base)); if (ret < 0) { dev_err(&hap->spmi->dev, "Error writing address: %X\n", QPNP_HAP_VMAX_REG(hap->base)); } return count; }
static int qpnp_hap_mod_enable(struct qpnp_hap *hap, int on) { u8 val; int rc, i; val = hap->reg_en_ctl; if (on) { val |= QPNP_HAP_EN; } else { for (i = 0; i < QPNP_HAP_MAX_RETRIES; i++) { rc = qpnp_hap_read_reg(hap, &val, QPNP_HAP_STATUS(hap->base)); dev_dbg(&hap->spmi->dev, "HAP_STATUS=0x%x\n", val); /* wait for QPNP_HAP_CYCLS cycles of play rate */ if (val & QPNP_HAP_STATUS_BUSY) { usleep(QPNP_HAP_CYCLS * hap->wave_play_rate_us); if (hap->play_mode == QPNP_HAP_DIRECT) break; } else break; } if (i >= QPNP_HAP_MAX_RETRIES) dev_dbg(&hap->spmi->dev, "Haptics Busy. Force disable\n"); val &= ~QPNP_HAP_EN; } rc = qpnp_hap_write_reg(hap, &val, QPNP_HAP_EN_CTL_REG(hap->base)); if (rc < 0) return rc; hap->reg_en_ctl = val; return 0; }
/* configuration api for max volatge */ static int qpnp_hap_vmax_config(struct qpnp_hap *hap) { u8 reg = 0; int rc, temp; if (hap->vmax_mv < QPNP_HAP_VMAX_MIN_MV) hap->vmax_mv = QPNP_HAP_VMAX_MIN_MV; else if (hap->vmax_mv > QPNP_HAP_VMAX_MAX_MV) hap->vmax_mv = QPNP_HAP_VMAX_MAX_MV; rc = qpnp_hap_read_reg(hap, ®, QPNP_HAP_VMAX_REG(hap->base)); if (rc < 0) return rc; reg &= QPNP_HAP_VMAX_MASK; temp = hap->vmax_mv / QPNP_HAP_VMAX_MIN_MV; reg |= (temp << QPNP_HAP_VMAX_SHIFT); rc = qpnp_hap_write_reg(hap, ®, QPNP_HAP_VMAX_REG(hap->base)); if (rc) return rc; return 0; }
/* sysfs show debug registers */ static ssize_t qpnp_hap_dump_regs_show(struct device *dev, struct device_attribute *attr, char *buf) { struct timed_output_dev *timed_dev = dev_get_drvdata(dev); struct qpnp_hap *hap = container_of(timed_dev, struct qpnp_hap, timed_dev); int count = 0, i; u8 val; for (i = 0; i < ARRAY_SIZE(qpnp_hap_dbg_regs); i++) { qpnp_hap_read_reg(hap, &val, hap->base + qpnp_hap_dbg_regs[i]); count += snprintf(buf + count, PAGE_SIZE - count, "qpnp_haptics: REG_0x%x = 0x%x\n", hap->base + qpnp_hap_dbg_regs[i], val); if (count >= PAGE_SIZE) return PAGE_SIZE - 1; } return count; }
/* worker to opeate haptics */ static void qpnp_hap_worker(struct work_struct *work) { struct qpnp_hap *hap = container_of(work, struct qpnp_hap, work); u8 reg = 0; int rc; if (hap->play_mode == QPNP_HAP_DIRECT) { if (hap->state) { /* haptic on */ rc = qpnp_hap_read_reg(hap, ®, QPNP_HAP_STATUS(hap->base)); if (rc < 0) return; if ((reg & QPNP_HAP_STATUS_BUSY) == 0) { /* */ #if 0 /* Over Drive : 2 vmax */ hap->vmax_mv = hap->vmax_mv_orig * 2; #endif /* */ hap->vmax_mv =QPNP_HAP_OV_RB_MV; qpnp_hap_vmax_config(hap ,1); qpnp_hap_set(hap, 1); /* */ usleep(2*hap->wave_play_rate_us); /* recover original vmax */ hap->vmax_mv = hap->vmax_mv_orig; qpnp_hap_vmax_config(hap ,0 ); } } } qpnp_hap_set(hap, hap->state); }
/* sysfs show for amp */ static ssize_t qpnp_hap_amp_show(struct device *dev, struct device_attribute *attr, char *buf) { struct timed_output_dev *timed_dev = dev_get_drvdata(dev); struct qpnp_hap *hap = container_of(timed_dev, struct qpnp_hap, timed_dev); u8 reg = 0, temp = 0; int res, ret; ret = qpnp_hap_read_reg(hap, ®, QPNP_HAP_VMAX_REG(hap->base)); if (ret < 0) { dev_err(&hap->spmi->dev, "Error reading address: %X\n", QPNP_HAP_VMAX_REG(hap->base)); return ret; } reg &= ~QPNP_HAP_VMAX_MASK; temp = reg >> QPNP_HAP_VMAX_SHIFT; res = temp * QPNP_HAP_VMAX_MIN_MV; return snprintf(buf, PAGE_SIZE, "%d\n", res); }
/* configuration api for short circuit debounce */ static int qpnp_hap_sc_deb_config(struct qpnp_hap *hap) { u8 reg = 0; int rc, temp; if (hap->sc_deb_cycles < QPNP_HAP_SC_DEB_CYCLES_MIN) hap->sc_deb_cycles = QPNP_HAP_SC_DEB_CYCLES_MIN; else if (hap->sc_deb_cycles > QPNP_HAP_SC_DEB_CYCLES_MAX) hap->sc_deb_cycles = QPNP_HAP_SC_DEB_CYCLES_MAX; rc = qpnp_hap_read_reg(hap, ®, QPNP_HAP_SC_DEB_REG(hap->base)); if (rc < 0) return rc; reg &= QPNP_HAP_SC_DEB_MASK; if (hap->sc_deb_cycles) { temp = fls(hap->sc_deb_cycles) - 1; reg |= temp - QPNP_HAP_SC_DEB_SUB; } rc = qpnp_hap_write_reg(hap, ®, QPNP_HAP_SC_DEB_REG(hap->base)); if (rc) return rc; return 0; }
/* Configuration api for haptics registers */ static int qpnp_hap_config(struct qpnp_hap *hap) { u8 reg = 0; int rc, i, temp; /* Configure the ACTUATOR TYPE register */ rc = qpnp_hap_read_reg(hap, ®, QPNP_HAP_ACT_TYPE_REG(hap->base)); if (rc < 0) return rc; reg &= QPNP_HAP_ACT_TYPE_MASK; reg |= hap->act_type; rc = qpnp_hap_write_reg(hap, ®, QPNP_HAP_ACT_TYPE_REG(hap->base)); if (rc) return rc; /* Configure auto resonance parameters */ if (hap->act_type == QPNP_HAP_LRA) { if (hap->lra_res_cal_period < QPNP_HAP_RES_CAL_PERIOD_MIN) hap->lra_res_cal_period = QPNP_HAP_RES_CAL_PERIOD_MIN; else if (hap->lra_res_cal_period > QPNP_HAP_RES_CAL_PERIOD_MAX) hap->lra_res_cal_period = QPNP_HAP_RES_CAL_PERIOD_MAX; rc = qpnp_hap_read_reg(hap, ®, QPNP_HAP_LRA_AUTO_RES_REG(hap->base)); if (rc < 0) return rc; reg &= QPNP_HAP_AUTO_RES_MODE_MASK; reg |= (hap->auto_res_mode << QPNP_HAP_AUTO_RES_MODE_SHIFT); reg &= QPNP_HAP_LRA_HIGH_Z_MASK; reg |= (hap->lra_high_z << QPNP_HAP_LRA_HIGH_Z_SHIFT); reg &= QPNP_HAP_LRA_RES_CAL_PER_MASK; temp = fls(hap->lra_res_cal_period) - 1; reg |= (temp - 2); rc = qpnp_hap_write_reg(hap, ®, QPNP_HAP_LRA_AUTO_RES_REG(hap->base)); if (rc) return rc; } else { /* disable auto resonance for ERM */ reg = 0x00; rc = qpnp_hap_write_reg(hap, ®, QPNP_HAP_LRA_AUTO_RES_REG(hap->base)); if (rc) return rc; } /* Configure the PLAY MODE register */ rc = qpnp_hap_play_mode_config(hap); if (rc) return rc; /* Configure the VMAX register */ rc = qpnp_hap_vmax_config(hap); if (rc) return rc; /* Configure the ILIM register */ if (hap->ilim_ma < QPNP_HAP_ILIM_MIN_MA) hap->ilim_ma = QPNP_HAP_ILIM_MIN_MA; else if (hap->ilim_ma > QPNP_HAP_ILIM_MAX_MA) hap->ilim_ma = QPNP_HAP_ILIM_MAX_MA; rc = qpnp_hap_read_reg(hap, ®, QPNP_HAP_ILIM_REG(hap->base)); if (rc < 0) return rc; reg &= QPNP_HAP_ILIM_MASK; temp = (hap->ilim_ma / QPNP_HAP_ILIM_MIN_MA) >> 1; reg |= temp; rc = qpnp_hap_write_reg(hap, ®, QPNP_HAP_ILIM_REG(hap->base)); if (rc) return rc; /* Configure the short circuit debounce register */ rc = qpnp_hap_sc_deb_config(hap); if (rc) return rc; /* Configure the INTERNAL_PWM register */ if (hap->int_pwm_freq_khz <= QPNP_HAP_INT_PWM_FREQ_253_KHZ) { hap->int_pwm_freq_khz = QPNP_HAP_INT_PWM_FREQ_253_KHZ; temp = 0; } else if (hap->int_pwm_freq_khz <= QPNP_HAP_INT_PWM_FREQ_505_KHZ) { hap->int_pwm_freq_khz = QPNP_HAP_INT_PWM_FREQ_505_KHZ; temp = 1; } else if (hap->int_pwm_freq_khz <= QPNP_HAP_INT_PWM_FREQ_739_KHZ) { hap->int_pwm_freq_khz = QPNP_HAP_INT_PWM_FREQ_739_KHZ; temp = 2; } else { hap->int_pwm_freq_khz = QPNP_HAP_INT_PWM_FREQ_1076_KHZ; temp = 3; } rc = qpnp_hap_read_reg(hap, ®, QPNP_HAP_INT_PWM_REG(hap->base)); if (rc < 0) return rc; reg &= QPNP_HAP_INT_PWM_MASK; reg |= temp; rc = qpnp_hap_write_reg(hap, ®, QPNP_HAP_INT_PWM_REG(hap->base)); if (rc) return rc; rc = qpnp_hap_read_reg(hap, ®, QPNP_HAP_PWM_CAP_REG(hap->base)); if (rc < 0) return rc; reg &= QPNP_HAP_INT_PWM_MASK; reg |= temp; rc = qpnp_hap_write_reg(hap, ®, QPNP_HAP_PWM_CAP_REG(hap->base)); if (rc) return rc; /* Configure the WAVE SHAPE register */ rc = qpnp_hap_read_reg(hap, ®, QPNP_HAP_WAV_SHAPE_REG(hap->base)); if (rc < 0) return rc; reg &= QPNP_HAP_WAV_SHAPE_MASK; reg |= hap->wave_shape; rc = qpnp_hap_write_reg(hap, ®, QPNP_HAP_WAV_SHAPE_REG(hap->base)); if (rc) return rc; /* Configure RATE_CFG1 and RATE_CFG2 registers */ /* Note: For ERM these registers act as play rate and for LRA these represent resonance period */ if (hap->wave_play_rate_us < QPNP_HAP_WAV_PLAY_RATE_US_MIN) hap->wave_play_rate_us = QPNP_HAP_WAV_PLAY_RATE_US_MIN; else if (hap->wave_play_rate_us > QPNP_HAP_WAV_PLAY_RATE_US_MAX) hap->wave_play_rate_us = QPNP_HAP_WAV_PLAY_RATE_US_MAX; temp = hap->wave_play_rate_us / QPNP_HAP_RATE_CFG_STEP_US; reg = temp & QPNP_HAP_RATE_CFG1_MASK; rc = qpnp_hap_write_reg(hap, ®, QPNP_HAP_RATE_CFG1_REG(hap->base)); if (rc) return rc; rc = qpnp_hap_read_reg(hap, ®, QPNP_HAP_RATE_CFG2_REG(hap->base)); if (rc < 0) return rc; reg &= QPNP_HAP_RATE_CFG2_MASK; temp = temp >> QPNP_HAP_RATE_CFG2_SHFT; reg |= temp; rc = qpnp_hap_write_reg(hap, ®, QPNP_HAP_RATE_CFG2_REG(hap->base)); if (rc) return rc; /* Configure BRAKE register */ rc = qpnp_hap_read_reg(hap, ®, QPNP_HAP_EN_CTL2_REG(hap->base)); if (rc < 0) return rc; reg &= QPNP_HAP_BRAKE_MASK; reg |= hap->en_brake; rc = qpnp_hap_write_reg(hap, ®, QPNP_HAP_EN_CTL2_REG(hap->base)); if (rc) return rc; if (hap->en_brake && hap->sup_brake_pat) { for (i = QPNP_HAP_BRAKE_PAT_LEN - 1, reg = 0; i >= 0; i--) { hap->brake_pat[i] &= QPNP_HAP_BRAKE_PAT_MASK; temp = i << 1; reg |= hap->brake_pat[i] << temp; } rc = qpnp_hap_write_reg(hap, ®, QPNP_HAP_BRAKE_REG(hap->base)); if (rc) return rc; } /* Cache enable control register */ rc = qpnp_hap_read_reg(hap, ®, QPNP_HAP_EN_CTL_REG(hap->base)); if (rc < 0) return rc; hap->reg_en_ctl = reg; /* Cache play register */ rc = qpnp_hap_read_reg(hap, ®, QPNP_HAP_PLAY_REG(hap->base)); if (rc < 0) return rc; hap->reg_play = reg; if (hap->play_mode == QPNP_HAP_BUFFER) rc = qpnp_hap_buffer_config(hap); else if (hap->play_mode == QPNP_HAP_PWM) rc = qpnp_hap_pwm_config(hap); else if (hap->play_mode == QPNP_HAP_AUDIO) rc = qpnp_hap_mod_enable(hap, true); if (rc) return rc; /* setup short circuit irq */ if (hap->use_sc_irq) { rc = devm_request_threaded_irq(&hap->spmi->dev, hap->sc_irq, NULL, qpnp_hap_sc_irq, QPNP_IRQ_FLAGS, "qpnp_sc_irq", hap); if (rc < 0) { dev_err(&hap->spmi->dev, "Unable to request sc(%d) IRQ(err:%d)\n", hap->sc_irq, rc); return rc; } } return rc; }