static int qpnp_hap_mod_enable(struct qpnp_hap *hap, int on) { u8 val; int rc; val = hap->reg_en_ctl; if (on) val |= QPNP_HAP_EN; else 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; }
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 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; }