/* set wled to a level of brightness */ static int qpnp_wled_set_level(struct qpnp_wled *wled, int level) { int i, rc; u8 reg; if (wled->calc_curr && wled->curr_scale != QPNP_WLED_CURR_SCALE_MAX) level = (level * wled->curr_scale) / QPNP_WLED_CURR_SCALE_MAX; if (wled->bl_scale_enabled && (wled->bl_scale > 0) && (wled->bl_scale < QPNP_WLED_BL_SCALE_MAX)) level = (level * wled->bl_scale) / QPNP_WLED_BL_SCALE_MAX; pr_debug("%s: brightness=%d level=%d\n", __func__, wled->cdev.brightness, level); /* set brightness registers */ for (i = 0; i < wled->num_strings; i++) { reg = level & QPNP_WLED_BRIGHT_LSB_MASK; rc = qpnp_wled_write_reg(wled, ®, QPNP_WLED_BRIGHT_LSB_REG(wled->sink_base, wled->strings[i])); if (rc < 0) return rc; reg = level >> QPNP_WLED_BRIGHT_MSB_SHIFT; reg = reg & QPNP_WLED_BRIGHT_MSB_MASK; rc = qpnp_wled_write_reg(wled, ®, QPNP_WLED_BRIGHT_MSB_REG(wled->sink_base, wled->strings[i])); if (rc < 0) return rc; } /* sync */ reg = QPNP_WLED_SYNC; rc = qpnp_wled_write_reg(wled, ®, QPNP_WLED_SYNC_REG(wled->sink_base)); if (rc < 0) return rc; reg = QPNP_WLED_SYNC_RESET; rc = qpnp_wled_write_reg(wled, ®, QPNP_WLED_SYNC_REG(wled->sink_base)); if (rc < 0) return rc; return 0; }
/* sysfs store function for full scale current in ua*/ static ssize_t qpnp_wled_fs_curr_ua_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct qpnp_wled *wled = dev_get_drvdata(dev); int data, i, rc, temp; u8 reg; if (sscanf(buf, "%d", &data) != 1) return -EINVAL; for (i = 0; i < wled->num_strings; i++) { if (data < QPNP_WLED_FS_CURR_MIN_UA) data = QPNP_WLED_FS_CURR_MIN_UA; else if (data > QPNP_WLED_FS_CURR_MAX_UA) data = QPNP_WLED_FS_CURR_MAX_UA; rc = qpnp_wled_read_reg(wled, ®, QPNP_WLED_FS_CURR_REG(wled->sink_base, wled->strings[i])); if (rc < 0) return rc; reg &= QPNP_WLED_FS_CURR_MASK; temp = data / QPNP_WLED_FS_CURR_STEP_UA; reg |= temp; rc = qpnp_wled_write_reg(wled, ®, QPNP_WLED_FS_CURR_REG(wled->sink_base, wled->strings[i])); if (rc) return rc; } wled->fs_curr_ua = data; return count; }
static int qpnp_wled_sec_access(struct qpnp_wled *wled, u16 base_addr) { int rc; u8 reg = QPNP_WLED_SEC_UNLOCK; rc = qpnp_wled_write_reg(wled, ®, QPNP_WLED_SEC_ACCESS_REG(base_addr)); if (rc) return rc; return 0; }
/* set wled to a level of brightness */ static int qpnp_wled_set_level(struct qpnp_wled *wled, int level) { int i, rc; u8 reg; /* set brightness registers */ for (i = 0; i < wled->num_strings; i++) { reg = level & QPNP_WLED_BRIGHT_LSB_MASK; rc = qpnp_wled_write_reg(wled, ®, QPNP_WLED_BRIGHT_LSB_REG(wled->sink_base, wled->strings[i])); if (rc < 0) return rc; reg = level >> QPNP_WLED_BRIGHT_MSB_SHIFT; reg = reg & QPNP_WLED_BRIGHT_MSB_MASK; rc = qpnp_wled_write_reg(wled, ®, QPNP_WLED_BRIGHT_MSB_REG(wled->sink_base, wled->strings[i])); if (rc < 0) return rc; } /* sync */ reg = QPNP_WLED_SYNC; rc = qpnp_wled_write_reg(wled, ®, QPNP_WLED_SYNC_REG(wled->sink_base)); if (rc < 0) return rc; reg = QPNP_WLED_SYNC_RESET; rc = qpnp_wled_write_reg(wled, ®, QPNP_WLED_SYNC_REG(wled->sink_base)); if (rc < 0) return rc; return 0; }
/* sysfs store function for dim mode*/ static ssize_t qpnp_wled_dim_mode_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct qpnp_wled *wled = dev_get_drvdata(dev); char str[QPNP_WLED_STR_SIZE + 1]; int rc, temp; u8 reg; if (snprintf(str, QPNP_WLED_STR_SIZE, "%s", buf) > QPNP_WLED_STR_SIZE) return -EINVAL; if (strcmp(str, "analog") == 0) temp = QPNP_WLED_DIM_ANALOG; else if (strcmp(str, "digital") == 0) temp = QPNP_WLED_DIM_DIGITAL; else temp = QPNP_WLED_DIM_HYBRID; if (temp == wled->dim_mode) return count; rc = qpnp_wled_read_reg(wled, ®, QPNP_WLED_MOD_REG(wled->sink_base)); if (rc < 0) return rc; if (temp == QPNP_WLED_DIM_HYBRID) { reg &= QPNP_WLED_DIM_HYB_MASK; reg |= (1 << QPNP_WLED_DIM_HYB_SHIFT); } else { reg &= QPNP_WLED_DIM_HYB_MASK; reg |= (0 << QPNP_WLED_DIM_HYB_SHIFT); reg &= QPNP_WLED_DIM_ANA_MASK; reg |= temp; } rc = qpnp_wled_write_reg(wled, ®, QPNP_WLED_MOD_REG(wled->sink_base)); if (rc) return rc; wled->dim_mode = temp; return count; }
static int qpnp_wled_mod_rdy(struct qpnp_wled *wled, u16 base_addr, bool state) { int rc; u8 reg; rc = qpnp_wled_read_reg(wled, ®, QPNP_WLED_MODULE_RDY_REG(base_addr)); if (rc < 0) return rc; reg &= QPNP_WLED_MODULE_RDY_MASK; reg |= (state << QPNP_WLED_MODULE_RDY_SHIFT); rc = qpnp_wled_write_reg(wled, ®, QPNP_WLED_MODULE_RDY_REG(base_addr)); if (rc) return rc; return 0; }
static int qpnp_wled_set_disp(struct qpnp_wled *wled, u16 base_addr) { int rc; u8 reg; /* display type */ rc = qpnp_wled_read_reg(wled, ®, QPNP_WLED_DISP_SEL_REG(base_addr)); if (rc < 0) return rc; reg &= QPNP_WLED_DISP_SEL_MASK; reg |= (wled->disp_type_amoled << QPNP_WLED_DISP_SEL_SHIFT); rc = qpnp_wled_write_reg(wled, ®, QPNP_WLED_DISP_SEL_REG(base_addr)); if (rc) return rc; return 0; }
int qpnp_ibb_enable(bool state) { int rc; u8 reg; if (!gwled) { pr_err("%s: wled is not initialized yet\n", __func__); return -EAGAIN; } /* enable lab */ if (gwled->ibb_bias_active) { rc = qpnp_wled_module_en(gwled, gwled->lab_base, state); if (rc < 0) return rc; usleep_range(QPNP_WLED_LAB_START_DLY_US, QPNP_WLED_LAB_START_DLY_US + 1); } else { rc = qpnp_wled_read_reg(gwled, ®, QPNP_WLED_LAB_IBB_RDY_REG(gwled->lab_base)); if (rc < 0) return rc; reg &= QPNP_WLED_MODULE_EN_MASK; reg |= (state << QPNP_WLED_MODULE_EN_SHIFT); rc = qpnp_wled_write_reg(gwled, ®, QPNP_WLED_LAB_IBB_RDY_REG(gwled->lab_base)); if (rc) return rc; } rc = qpnp_wled_module_en(gwled, gwled->ibb_base, state); if (rc < 0) return rc; return 0; }
/* Configure WLED registers */ static int qpnp_wled_config(struct qpnp_wled *wled) { int rc, i, temp; u8 reg = 0; /* Configure display type */ rc = qpnp_wled_set_disp(wled, wled->ctrl_base); if (rc < 0) return rc; /* Configure the FEEDBACK OUTPUT register */ rc = qpnp_wled_read_reg(wled, ®, QPNP_WLED_FDBK_OP_REG(wled->ctrl_base)); if (rc < 0) return rc; reg &= QPNP_WLED_FDBK_OP_MASK; reg |= wled->fdbk_op; rc = qpnp_wled_write_reg(wled, ®, QPNP_WLED_FDBK_OP_REG(wled->ctrl_base)); if (rc) return rc; /* Configure the VREF register */ if (wled->vref_mv < QPNP_WLED_VREF_MIN_MV) wled->vref_mv = QPNP_WLED_VREF_MIN_MV; else if (wled->vref_mv > QPNP_WLED_VREF_MAX_MV) wled->vref_mv = QPNP_WLED_VREF_MAX_MV; rc = qpnp_wled_read_reg(wled, ®, QPNP_WLED_VREF_REG(wled->ctrl_base)); if (rc < 0) return rc; reg &= QPNP_WLED_VREF_MASK; temp = wled->vref_mv - QPNP_WLED_VREF_MIN_MV; reg |= (temp / QPNP_WLED_VREF_STEP_MV); rc = qpnp_wled_write_reg(wled, ®, QPNP_WLED_VREF_REG(wled->ctrl_base)); if (rc) return rc; /* Configure the ILIM register */ if (wled->ilim_ma < QPNP_WLED_ILIM_MIN_MA) wled->ilim_ma = QPNP_WLED_ILIM_MIN_MA; else if (wled->ilim_ma > QPNP_WLED_ILIM_MAX_MA) wled->ilim_ma = QPNP_WLED_ILIM_MAX_MA; rc = qpnp_wled_read_reg(wled, ®, QPNP_WLED_ILIM_REG(wled->ctrl_base)); if (rc < 0) return rc; reg &= QPNP_WLED_ILIM_MASK; reg |= (wled->ilim_ma / QPNP_WLED_ILIM_STEP_MA); rc = qpnp_wled_write_reg(wled, ®, QPNP_WLED_ILIM_REG(wled->ctrl_base)); if (rc) return rc; /* Configure the MAX BOOST DUTY register */ if (wled->boost_duty_ns < QPNP_WLED_BOOST_DUTY_MIN_NS) wled->boost_duty_ns = QPNP_WLED_BOOST_DUTY_MIN_NS; else if (wled->boost_duty_ns > QPNP_WLED_BOOST_DUTY_MAX_NS) wled->boost_duty_ns = QPNP_WLED_BOOST_DUTY_MAX_NS; rc = qpnp_wled_read_reg(wled, ®, QPNP_WLED_BOOST_DUTY_REG(wled->ctrl_base)); if (rc < 0) return rc; reg &= QPNP_WLED_BOOST_DUTY_MASK; reg |= (wled->boost_duty_ns / QPNP_WLED_BOOST_DUTY_STEP_NS); rc = qpnp_wled_write_reg(wled, ®, QPNP_WLED_BOOST_DUTY_REG(wled->ctrl_base)); if (rc) return rc; /* Configure the SWITCHING FREQ register */ if (wled->switch_freq_khz == QPNP_WLED_SWITCH_FREQ_1600_KHZ) temp = QPNP_WLED_SWITCH_FREQ_1600_KHZ_CODE; else temp = QPNP_WLED_SWITCH_FREQ_800_KHZ_CODE; rc = qpnp_wled_read_reg(wled, ®, QPNP_WLED_SWITCH_FREQ_REG(wled->ctrl_base)); if (rc < 0) return rc; reg &= QPNP_WLED_SWITCH_FREQ_MASK; reg |= temp; rc = qpnp_wled_write_reg(wled, ®, QPNP_WLED_SWITCH_FREQ_REG(wled->ctrl_base)); if (rc) return rc; /* Configure the OVP register */ if (wled->ovp_mv <= QPNP_WLED_OVP_17800_MV) { wled->ovp_mv = QPNP_WLED_OVP_17800_MV; temp = 3; } else if (wled->ovp_mv <= QPNP_WLED_OVP_19400_MV) { wled->ovp_mv = QPNP_WLED_OVP_19400_MV; temp = 2; } else if (wled->ovp_mv <= QPNP_WLED_OVP_29500_MV) { wled->ovp_mv = QPNP_WLED_OVP_29500_MV; temp = 1; } else { wled->ovp_mv = QPNP_WLED_OVP_31000_MV; temp = 0; } rc = qpnp_wled_read_reg(wled, ®, QPNP_WLED_OVP_REG(wled->ctrl_base)); if (rc < 0) return rc; reg &= QPNP_WLED_OVP_MASK; reg |= temp; rc = qpnp_wled_write_reg(wled, ®, QPNP_WLED_OVP_REG(wled->ctrl_base)); if (rc) return rc; /* Configure the MODULATION register */ if (wled->mod_freq_khz <= QPNP_WLED_MOD_FREQ_1200_KHZ) { wled->mod_freq_khz = QPNP_WLED_MOD_FREQ_1200_KHZ; temp = 3; } else if (wled->mod_freq_khz <= QPNP_WLED_MOD_FREQ_2400_KHZ) { wled->mod_freq_khz = QPNP_WLED_MOD_FREQ_2400_KHZ; temp = 2; } else if (wled->mod_freq_khz <= QPNP_WLED_MOD_FREQ_9600_KHZ) { wled->mod_freq_khz = QPNP_WLED_MOD_FREQ_9600_KHZ; temp = 1; } else if (wled->mod_freq_khz <= QPNP_WLED_MOD_FREQ_19200_KHZ) { wled->mod_freq_khz = QPNP_WLED_MOD_FREQ_19200_KHZ; temp = 0; } else { wled->mod_freq_khz = QPNP_WLED_MOD_FREQ_9600_KHZ; temp = 1; } rc = qpnp_wled_read_reg(wled, ®, QPNP_WLED_MOD_REG(wled->sink_base)); if (rc < 0) return rc; reg &= QPNP_WLED_MOD_FREQ_MASK; reg |= (temp << QPNP_WLED_MOD_FREQ_SHIFT); reg &= QPNP_WLED_PHASE_STAG_MASK; reg |= (wled->en_phase_stag << QPNP_WLED_PHASE_STAG_SHIFT); reg &= QPNP_WLED_ACC_CLK_FREQ_MASK; reg |= (temp << QPNP_WLED_ACC_CLK_FREQ_SHIFT); reg &= QPNP_WLED_DIM_RES_MASK; reg |= (wled->en_9b_dim_res << QPNP_WLED_DIM_RES_SHIFT); if (wled->dim_mode == QPNP_WLED_DIM_HYBRID) { reg &= QPNP_WLED_DIM_HYB_MASK; reg |= (1 << QPNP_WLED_DIM_HYB_SHIFT); } else { reg &= QPNP_WLED_DIM_HYB_MASK; reg |= (0 << QPNP_WLED_DIM_HYB_SHIFT); reg &= QPNP_WLED_DIM_ANA_MASK; reg |= wled->dim_mode; } rc = qpnp_wled_write_reg(wled, ®, QPNP_WLED_MOD_REG(wled->sink_base)); if (rc) return rc; /* Configure the HYBRID THRESHOLD register */ if (wled->hyb_thres < QPNP_WLED_HYB_THRES_MIN) wled->hyb_thres = QPNP_WLED_HYB_THRES_MIN; else if (wled->hyb_thres > QPNP_WLED_HYB_THRES_MAX) wled->hyb_thres = QPNP_WLED_HYB_THRES_MAX; rc = qpnp_wled_read_reg(wled, ®, QPNP_WLED_HYB_THRES_REG(wled->sink_base)); if (rc < 0) return rc; reg &= QPNP_WLED_HYB_THRES_MASK; temp = fls(wled->hyb_thres / QPNP_WLED_HYB_THRES_MIN) - 1; reg |= temp; rc = qpnp_wled_write_reg(wled, ®, QPNP_WLED_HYB_THRES_REG(wled->sink_base)); if (rc) return rc; /* Configure TEST5 register */ if (wled->dim_mode == QPNP_WLED_DIM_DIGITAL) reg = QPNP_WLED_SINK_TEST5_DIG; else reg = QPNP_WLED_SINK_TEST5_HYB; rc = qpnp_wled_sec_access(wled, wled->sink_base); if (rc) return rc; rc = qpnp_wled_write_reg(wled, ®, QPNP_WLED_SINK_TEST5_REG(wled->sink_base)); if (rc) return rc; /* disable all current sinks and enable selected strings */ reg = 0x00; rc = qpnp_wled_write_reg(wled, ®, QPNP_WLED_CURR_SINK_REG(wled->sink_base)); for (i = 0; i < wled->num_strings; i++) { if (wled->strings[i] >= QPNP_WLED_MAX_STRINGS) { dev_err(&wled->spmi->dev, "Invalid string number\n"); return -EINVAL; } /* MODULATOR */ rc = qpnp_wled_read_reg(wled, ®, QPNP_WLED_MOD_EN_REG(wled->sink_base, wled->strings[i])); if (rc < 0) return rc; reg &= QPNP_WLED_MOD_EN_MASK; reg |= (QPNP_WLED_MOD_EN << QPNP_WLED_MOD_EN_SHFT); if (wled->dim_mode == QPNP_WLED_DIM_HYBRID) reg &= QPNP_WLED_GATE_DRV_MASK; else reg |= ~QPNP_WLED_GATE_DRV_MASK; rc = qpnp_wled_write_reg(wled, ®, QPNP_WLED_MOD_EN_REG(wled->sink_base, wled->strings[i])); if (rc) return rc; /* SYNC DELAY */ if (wled->sync_dly_us < QPNP_WLED_SYNC_DLY_MIN_US) wled->sync_dly_us = QPNP_WLED_SYNC_DLY_MIN_US; else if (wled->sync_dly_us > QPNP_WLED_SYNC_DLY_MAX_US) wled->sync_dly_us = QPNP_WLED_SYNC_DLY_MAX_US; rc = qpnp_wled_read_reg(wled, ®, QPNP_WLED_SYNC_DLY_REG(wled->sink_base, wled->strings[i])); if (rc < 0) return rc; reg &= QPNP_WLED_SYNC_DLY_MASK; temp = wled->sync_dly_us / QPNP_WLED_SYNC_DLY_STEP_US; reg |= temp; rc = qpnp_wled_write_reg(wled, ®, QPNP_WLED_SYNC_DLY_REG(wled->sink_base, wled->strings[i])); if (rc) return rc; /* FULL SCALE CURRENT */ if (wled->fs_curr_ua < QPNP_WLED_FS_CURR_MIN_UA) wled->fs_curr_ua = QPNP_WLED_FS_CURR_MIN_UA; else if (wled->fs_curr_ua > QPNP_WLED_FS_CURR_MAX_UA) wled->fs_curr_ua = QPNP_WLED_FS_CURR_MAX_UA; rc = qpnp_wled_read_reg(wled, ®, QPNP_WLED_FS_CURR_REG(wled->sink_base, wled->strings[i])); if (rc < 0) return rc; reg &= QPNP_WLED_FS_CURR_MASK; temp = wled->fs_curr_ua / QPNP_WLED_FS_CURR_STEP_UA; reg |= temp; rc = qpnp_wled_write_reg(wled, ®, QPNP_WLED_FS_CURR_REG(wled->sink_base, wled->strings[i])); if (rc) return rc; /* CABC */ rc = qpnp_wled_read_reg(wled, ®, QPNP_WLED_CABC_REG(wled->sink_base, wled->strings[i])); if (rc < 0) return rc; reg &= QPNP_WLED_CABC_MASK; reg |= (wled->en_cabc << QPNP_WLED_CABC_SHIFT); rc = qpnp_wled_write_reg(wled, ®, QPNP_WLED_CABC_REG(wled->sink_base, wled->strings[i])); if (rc) return rc; /* Enable CURRENT SINK */ rc = qpnp_wled_read_reg(wled, ®, QPNP_WLED_CURR_SINK_REG(wled->sink_base)); if (rc < 0) return rc; temp = wled->strings[i] + QPNP_WLED_CURR_SINK_SHIFT; reg |= (1 << temp); rc = qpnp_wled_write_reg(wled, ®, QPNP_WLED_CURR_SINK_REG(wled->sink_base)); if (rc) return rc; } /* LAB fast precharge */ rc = qpnp_wled_read_reg(wled, ®, QPNP_WLED_LAB_FAST_PC_REG(wled->lab_base)); if (rc < 0) return rc; reg &= QPNP_WLED_LAB_FAST_PC_MASK; reg |= (wled->lab_fast_precharge << QPNP_WLED_LAB_FAST_PC_SHIFT); rc = qpnp_wled_write_reg(wled, ®, QPNP_WLED_LAB_FAST_PC_REG(wled->lab_base)); if (rc) return rc; /* Configure lab display type */ rc = qpnp_wled_set_disp(wled, wled->lab_base); if (rc < 0) return rc; /* make LAB module ready */ rc = qpnp_wled_mod_rdy(wled, wled->lab_base, true); if (rc < 0) return rc; /* IBB active bias */ if (wled->ibb_pwrup_dly_ms < QPNP_WLED_IBB_PWRUP_DLY_MIN_MS) wled->ibb_pwrup_dly_ms = QPNP_WLED_IBB_PWRUP_DLY_MIN_MS; else if (wled->ibb_pwrup_dly_ms > QPNP_WLED_IBB_PWRUP_DLY_MAX_MS) wled->ibb_pwrup_dly_ms = QPNP_WLED_IBB_PWRUP_DLY_MAX_MS; rc = qpnp_wled_read_reg(wled, ®, QPNP_WLED_IBB_BIAS_REG(wled->ibb_base)); if (rc < 0) return rc; reg &= QPNP_WLED_IBB_BIAS_MASK; reg |= (!wled->ibb_bias_active << QPNP_WLED_IBB_BIAS_SHIFT); temp = fls(wled->ibb_pwrup_dly_ms) - 1; reg &= QPNP_WLED_IBB_PWRUP_DLY_MASK; reg |= (temp << QPNP_WLED_IBB_PWRUP_DLY_SHIFT); rc = qpnp_wled_sec_access(wled, wled->ibb_base); if (rc) return rc; rc = qpnp_wled_write_reg(wled, ®, QPNP_WLED_IBB_BIAS_REG(wled->ibb_base)); if (rc) return rc; /* Configure ibb display type */ rc = qpnp_wled_set_disp(wled, wled->ibb_base); if (rc < 0) return rc; /* make IBB module ready */ rc = qpnp_wled_mod_rdy(wled, wled->ibb_base, true); if (rc < 0) return rc; /* setup ovp and sc irqs */ if (wled->ovp_irq >= 0) { rc = devm_request_threaded_irq(&wled->spmi->dev, wled->ovp_irq, NULL, qpnp_wled_ovp_irq, QPNP_IRQ_FLAGS, "qpnp_wled_ovp_irq", wled); if (rc < 0) { dev_err(&wled->spmi->dev, "Unable to request ovp(%d) IRQ(err:%d)\n", wled->ovp_irq, rc); return rc; } } if (wled->sc_irq >= 0) { wled->sc_cnt = 0; rc = devm_request_threaded_irq(&wled->spmi->dev, wled->sc_irq, NULL, qpnp_wled_sc_irq, QPNP_IRQ_FLAGS, "qpnp_wled_sc_irq", wled); if (rc < 0) { dev_err(&wled->spmi->dev, "Unable to request sc(%d) IRQ(err:%d)\n", wled->sc_irq, rc); return rc; } rc = qpnp_wled_read_reg(wled, ®, QPNP_WLED_SC_PRO_REG(wled->ctrl_base)); if (rc < 0) return rc; reg &= QPNP_WLED_EN_SC_MASK; reg |= 1 << QPNP_WLED_EN_SC_SHIFT; rc = qpnp_wled_write_reg(wled, ®, QPNP_WLED_SC_PRO_REG(wled->ctrl_base)); if (rc) return rc; if (wled->en_ext_pfet_sc_pro) { rc = qpnp_wled_sec_access(wled, wled->ctrl_base); if (rc) return rc; reg = QPNP_WLED_EXT_FET_DTEST2; rc = qpnp_wled_write_reg(wled, ®, QPNP_WLED_TEST_REG(wled->ctrl_base)); if (rc) return rc; } } return 0; }