diff options
Diffstat (limited to 'cs40l26/cs40l26.c')
-rw-r--r-- | cs40l26/cs40l26.c | 332 |
1 files changed, 238 insertions, 94 deletions
diff --git a/cs40l26/cs40l26.c b/cs40l26/cs40l26.c index 3be1832..93fe669 100644 --- a/cs40l26/cs40l26.c +++ b/cs40l26/cs40l26.c @@ -176,6 +176,35 @@ int cs40l26_dsp_state_get(struct cs40l26_private *cs40l26, u8 *state) } EXPORT_SYMBOL(cs40l26_dsp_state_get); +int cs40l26_set_pll_loop(struct cs40l26_private *cs40l26, unsigned int pll_loop) +{ + int ret, i; + + if (pll_loop != CS40L26_PLL_REFCLK_SET_OPEN_LOOP && + pll_loop != CS40L26_PLL_REFCLK_SET_CLOSED_LOOP) { + dev_err(cs40l26->dev, "Invalid PLL Loop setting: %u\n", + pll_loop); + return -EINVAL; + } + + /* Retry in case DSP is hibernating */ + for (i = 0; i < CS40L26_PLL_REFCLK_SET_ATTEMPTS; i++) { + ret = regmap_update_bits(cs40l26->regmap, CS40L26_REFCLK_INPUT, + CS40L26_PLL_REFCLK_LOOP_MASK, pll_loop << + CS40L26_PLL_REFCLK_LOOP_SHIFT); + if (!ret) + break; + } + + if (i == CS40L26_PLL_REFCLK_SET_ATTEMPTS) { + dev_err(cs40l26->dev, "Failed to configure PLL\n"); + return -ETIMEDOUT; + } + + return 0; +} +EXPORT_SYMBOL(cs40l26_set_pll_loop); + int cs40l26_dbc_get(struct cs40l26_private *cs40l26, enum cs40l26_dbc dbc, unsigned int *val) { @@ -185,7 +214,7 @@ int cs40l26_dbc_get(struct cs40l26_private *cs40l26, enum cs40l26_dbc dbc, ret = pm_runtime_get_sync(dev); if (ret < 0) { - cs40l26_resume_error_handle(dev); + cs40l26_resume_error_handle(dev, ret); return ret; } @@ -566,7 +595,7 @@ static int cs40l26_dsp_pre_config(struct cs40l26_private *cs40l26) return -EINVAL; } - ret = cs40l26_pm_stdby_timeout_ms_get(cs40l26, &timeout_ms); + ret = cs40l26_pm_active_timeout_ms_get(cs40l26, &timeout_ms); if (ret) return ret; @@ -1199,8 +1228,9 @@ static irqreturn_t cs40l26_irq(int irq, void *data) unsigned long num_irq; int ret; - if (pm_runtime_get_sync(dev) < 0) { - cs40l26_resume_error_handle(dev); + ret = pm_runtime_get_sync(dev); + if (ret < 0) { + cs40l26_resume_error_handle(dev, ret); dev_err(dev, "Interrupts missed\n"); @@ -1318,7 +1348,7 @@ static int cs40l26_pseq_find_end(struct cs40l26_private *cs40l26, return 0; } -static int cs40l26_pseq_write(struct cs40l26_private *cs40l26, u32 addr, +int cs40l26_pseq_write(struct cs40l26_private *cs40l26, u32 addr, u32 data, bool update, u8 op_code) { struct device *dev = cs40l26->dev; @@ -1331,6 +1361,22 @@ static int cs40l26_pseq_write(struct cs40l26_private *cs40l26, u32 addr, u32 *op_words; int ret; + + /* + * Due to a bug in the DSP ROM, if data[23] = 1 for a WRITE_FULL + * operation, then, when the DSP power-on write sequencer + * actually applies these writes when coming out of hibernate, + * the DSP sign-extends bit 23 to bits[31:24]. So, warn if it + * appears the PSEQ will not function as expected. + */ + if ((op_code == CS40L26_PSEQ_OP_WRITE_FULL) && + (data & BIT(23)) && + (((data & GENMASK(31, 24)) >> 24) != 0xFF)) { + dev_warn(dev, + "PSEQ to set data[31:24] to 0xFF reg: %08X, data: %08X", + addr, data); + } + if (op_code == CS40L26_PSEQ_OP_WRITE_FULL) { num_op_words = CS40L26_PSEQ_OP_WRITE_FULL_WORDS; l_addr_shift = CS40L26_PSEQ_WRITE_FULL_LOWER_ADDR_SHIFT; @@ -1444,6 +1490,7 @@ op_words_free: return ret; } +EXPORT_SYMBOL(cs40l26_pseq_write); static int cs40l26_pseq_multi_write(struct cs40l26_private *cs40l26, const struct reg_sequence *reg_seq, int num_regs, bool update, @@ -1619,8 +1666,27 @@ static int cs40l26_irq_update_mask(struct cs40l26_private *cs40l26, u32 reg, return ret; } - return cs40l26_pseq_write(cs40l26, reg, - new_mask, true, CS40L26_PSEQ_OP_WRITE_FULL); + if (bit_mask & GENMASK(31, 16)) { + ret = cs40l26_pseq_write(cs40l26, reg, + (new_mask & GENMASK(31, 16)) >> 16, + true, CS40L26_PSEQ_OP_WRITE_H16); + if (ret) { + dev_err(cs40l26->dev, "Failed to update IRQ mask H16"); + return ret; + } + } + + if (bit_mask & GENMASK(15, 0)) { + ret = cs40l26_pseq_write(cs40l26, reg, + (new_mask & GENMASK(15, 0)), + true, CS40L26_PSEQ_OP_WRITE_L16); + if (ret) { + dev_err(cs40l26->dev, "Failed to update IRQ mask L16"); + return ret; + } + } + + return ret; } static int cs40l26_buzzgen_set(struct cs40l26_private *cs40l26, u16 freq, @@ -1709,7 +1775,7 @@ static int cs40l26_map_gpi_to_haptic(struct cs40l26_private *cs40l26, ret = pm_runtime_get_sync(cs40l26->dev); if (ret < 0) { - cs40l26_resume_error_handle(cs40l26->dev); + cs40l26_resume_error_handle(cs40l26->dev, ret); return ret; } @@ -1754,8 +1820,9 @@ static void cs40l26_set_gain_worker(struct work_struct *work) u32 reg; int ret; - if (pm_runtime_get_sync(cs40l26->dev) < 0) - return cs40l26_resume_error_handle(cs40l26->dev); + ret = pm_runtime_get_sync(cs40l26->dev); + if (ret < 0) + return cs40l26_resume_error_handle(cs40l26->dev, ret); mutex_lock(&cs40l26->lock); @@ -1802,8 +1869,9 @@ static void cs40l26_vibe_start_worker(struct work_struct *work) dev_dbg(dev, "%s\n", __func__); - if (pm_runtime_get_sync(dev) < 0) - return cs40l26_resume_error_handle(dev); + ret = pm_runtime_get_sync(dev); + if (ret < 0) + return cs40l26_resume_error_handle(dev, ret); mutex_lock(&cs40l26->lock); @@ -1889,20 +1957,20 @@ static void cs40l26_vibe_stop_worker(struct work_struct *work) dev_dbg(cs40l26->dev, "%s\n", __func__); - if (pm_runtime_get_sync(cs40l26->dev) < 0) - return cs40l26_resume_error_handle(cs40l26->dev); - - mutex_lock(&cs40l26->lock); - - if (cs40l26->vibe_state != CS40L26_VIBE_STATE_HAPTIC && - !cs40l26->vibe_state_reporting) - goto mutex_exit; + ret = pm_runtime_get_sync(cs40l26->dev); + if (ret < 0) + return cs40l26_resume_error_handle(cs40l26->dev, ret); /* wait for SVC init phase to complete */ if (cs40l26->delay_before_stop_playback_us) usleep_range(cs40l26->delay_before_stop_playback_us, cs40l26->delay_before_stop_playback_us + 100); + mutex_lock(&cs40l26->lock); + + if (cs40l26->vibe_state != CS40L26_VIBE_STATE_HAPTIC) + goto mutex_exit; + ret = cs40l26_ack_write(cs40l26, CS40L26_DSP_VIRTUAL1_MBOX_1, CS40L26_STOP_PLAYBACK, CS40L26_DSP_MBOX_RESET); if (ret) { @@ -2179,7 +2247,7 @@ static int cs40l26_owt_upload(struct cs40l26_private *cs40l26, u8 *data, ret = pm_runtime_get_sync(dev); if (ret < 0) { - cs40l26_resume_error_handle(dev); + cs40l26_resume_error_handle(dev, ret); return ret; } @@ -2602,8 +2670,9 @@ static void cs40l26_upload_worker(struct work_struct *work) u16 index, bank; bool pwle, svc_waveform; - if (pm_runtime_get_sync(cdev) < 0) - return cs40l26_resume_error_handle(cdev); + ret = pm_runtime_get_sync(cdev); + if (ret < 0) + return cs40l26_resume_error_handle(cdev, ret); mutex_lock(&cs40l26->lock); @@ -2890,8 +2959,9 @@ static void cs40l26_erase_worker(struct work_struct *work) int effect_id; u32 index; - if (pm_runtime_get_sync(cs40l26->dev) < 0) - return cs40l26_resume_error_handle(cs40l26->dev); + ret = pm_runtime_get_sync(cs40l26->dev); + if (ret < 0) + return cs40l26_resume_error_handle(cs40l26->dev, ret); mutex_lock(&cs40l26->lock); @@ -3171,15 +3241,15 @@ static int cs40l26_brownout_prevention_init(struct cs40l26_private *cs40l26) return ret; } - if (cs40l26->pdata.vbbr_thld) { - if (cs40l26->pdata.vbbr_thld + if (cs40l26->pdata.vbbr_thld_mv) { + if (cs40l26->pdata.vbbr_thld_mv >= CS40L26_VBBR_THLD_MV_MAX) vbbr_thld = CS40L26_VBBR_THLD_MAX; - else if (cs40l26->pdata.vbbr_thld + else if (cs40l26->pdata.vbbr_thld_mv <= CS40L26_VBBR_THLD_MV_MIN) vbbr_thld = CS40L26_VBBR_THLD_MIN; else - vbbr_thld = cs40l26->pdata.vbbr_thld / + vbbr_thld = cs40l26->pdata.vbbr_thld_mv / CS40L26_VBBR_THLD_MV_STEP; val &= ~CS40L26_VBBR_THLD_MASK; @@ -3255,8 +3325,15 @@ static int cs40l26_brownout_prevention_init(struct cs40l26_private *cs40l26) return ret; } - ret = cs40l26_pseq_write(cs40l26, CS40L26_VBBR_CONFIG, val, - true, CS40L26_PSEQ_OP_WRITE_FULL); + ret = cs40l26_pseq_write(cs40l26, CS40L26_VBBR_CONFIG, + (val & GENMASK(31, 16)) >> 16, + true, CS40L26_PSEQ_OP_WRITE_H16); + if (ret) + return ret; + + ret = cs40l26_pseq_write(cs40l26, CS40L26_VBBR_CONFIG, + (val & GENMASK(15, 0)), + true, CS40L26_PSEQ_OP_WRITE_L16); if (ret) return ret; } @@ -3271,17 +3348,20 @@ static int cs40l26_brownout_prevention_init(struct cs40l26_private *cs40l26) pseq_mask |= BIT(CS40L26_IRQ2_VPBR_ATT_CLR) | BIT(CS40L26_IRQ2_VPBR_FLAG); - if (cs40l26->pdata.vpbr_thld) { - if (cs40l26->pdata.vpbr_thld - >= CS40L26_VPBR_THLD_MV_MAX) + if (cs40l26->pdata.vpbr_thld_mv) { + if (cs40l26->pdata.vpbr_thld_mv + >= CS40L26_VPBR_THLD_MV_MAX) { vpbr_thld = CS40L26_VPBR_THLD_MAX; - else if (cs40l26->pdata.vpbr_thld - <= CS40L26_VPBR_THLD_MV_MIN) + } else if (cs40l26->pdata.vpbr_thld_mv + <= CS40L26_VPBR_THLD_MV_MIN) { vpbr_thld = CS40L26_VPBR_THLD_MIN; - else - vpbr_thld = (cs40l26->pdata.vpbr_thld / + } else { + vpbr_thld = (cs40l26->pdata.vpbr_thld_mv / CS40L26_VPBR_THLD_MV_DIV) - CS40L26_VPBR_THLD_OFFSET; + } + + cs40l26->vpbr_thld = vpbr_thld & CS40L26_VPBR_THLD_MASK; val &= ~CS40L26_VPBR_THLD_MASK; val |= (vpbr_thld & CS40L26_VPBR_THLD_MASK); @@ -3358,8 +3438,15 @@ static int cs40l26_brownout_prevention_init(struct cs40l26_private *cs40l26) return ret; } - ret = cs40l26_pseq_write(cs40l26, CS40L26_VPBR_CONFIG, val, - true, CS40L26_PSEQ_OP_WRITE_FULL); + ret = cs40l26_pseq_write(cs40l26, CS40L26_VPBR_CONFIG, + (val & GENMASK(31, 16)) >> 16, + true, CS40L26_PSEQ_OP_WRITE_H16); + if (ret) + return ret; + + ret = cs40l26_pseq_write(cs40l26, CS40L26_VPBR_CONFIG, + (val & GENMASK(15, 0)), + true, CS40L26_PSEQ_OP_WRITE_L16); if (ret) return ret; } @@ -3843,16 +3930,16 @@ static int cs40l26_dsp_config(struct cs40l26_private *cs40l26) if (ret) return ret; + cs40l26_pm_runtime_setup(cs40l26); + ret = cs40l26_pm_state_transition(cs40l26, CS40L26_PM_STATE_ALLOW_HIBERNATE); if (ret) return ret; - cs40l26_pm_runtime_setup(cs40l26); - ret = pm_runtime_get_sync(dev); if (ret < 0) { - cs40l26_resume_error_handle(dev); + cs40l26_resume_error_handle(dev, ret); return ret; } @@ -3926,54 +4013,73 @@ static void cs40l26_gain_adjust(struct cs40l26_private *cs40l26, s32 adjust) cs40l26->pdata.asp_scale_pct = total; } -static int cs40l26_tuning_select_from_svc_le(struct cs40l26_private *cs40l26, - u32 *tuning_num) +int cs40l26_svc_le_estimate(struct cs40l26_private *cs40l26, unsigned int *le) { - unsigned int reg, le = 0; - int ret, i, j; - - ret = pm_runtime_get_sync(cs40l26->dev); - if (ret < 0) { - cs40l26_resume_error_handle(cs40l26->dev); - return ret; - } + struct device *dev = cs40l26->dev; + unsigned int reg, le_est = 0; + int ret, i; ret = cs40l26_ack_write(cs40l26, CS40L26_DSP_VIRTUAL1_MBOX_1, CS40L26_DSP_MBOX_CMD_LE_EST, CS40L26_DSP_MBOX_RESET); if (ret) - goto pm_err; + return ret; ret = cl_dsp_get_reg(cs40l26->dsp, "LE_EST_STATUS", CL_DSP_YM_UNPACKED_TYPE, CS40l26_SVC_ALGO_ID, ®); if (ret) - goto pm_err; + return ret; for (i = 0; i < CS40L26_SVC_LE_MAX_ATTEMPTS; i++) { - usleep_range(5000, 5100); - ret = regmap_read(cs40l26->regmap, reg, &le); + usleep_range(CS40L26_SVC_LE_EST_TIME_US, + CS40L26_SVC_LE_EST_TIME_US + 100); + ret = regmap_read(cs40l26->regmap, reg, &le_est); if (ret) { - dev_err(cs40l26->dev, "Failed to get LE_EST_STATUS\n"); - goto pm_err; + dev_err(dev, "Failed to get LE_EST_STATUS\n"); + return ret; } - dev_dbg(cs40l26->dev, "Measured LE estimate = 0x%08X\n", le); + dev_info(dev, "Measured Le Estimation = %u\n", le_est); - for (j = 0; j < cs40l26->num_svc_le_vals; j++) { - if (le >= cs40l26->svc_le_vals[j]->min && - le <= cs40l26->svc_le_vals[j]->max) { - *tuning_num = cs40l26->svc_le_vals[j]->n; + if (le_est) + break; + } - cs40l26_gain_adjust(cs40l26, - cs40l26->svc_le_vals[j]->gain_adjust); + *le = le_est; + + return 0; +} +EXPORT_SYMBOL(cs40l26_svc_le_estimate); + +static int cs40l26_tuning_select_from_svc_le(struct cs40l26_private *cs40l26, + u32 *tuning_num) +{ + unsigned int le; + int ret, i; + + ret = pm_runtime_get_sync(cs40l26->dev); + if (ret < 0) { + cs40l26_resume_error_handle(cs40l26->dev, ret); + return ret; + } + ret = cs40l26_svc_le_estimate(cs40l26, &le); + if (ret) + goto pm_err; + + if (le) { + for (i = 0; i < cs40l26->num_svc_le_vals; i++) { + if (le >= cs40l26->svc_le_vals[i]->min && + le <= cs40l26->svc_le_vals[i]->max) { + *tuning_num = cs40l26->svc_le_vals[i]->n; + + cs40l26_gain_adjust(cs40l26, + cs40l26->svc_le_vals[i]->gain_adjust); break; } } - if (j < cs40l26->num_svc_le_vals) - break; } - if (i == CS40L26_SVC_LE_MAX_ATTEMPTS) + if (!le || i == cs40l26->num_svc_le_vals) dev_warn(cs40l26->dev, "Using default tunings\n"); pm_err: @@ -4097,8 +4203,17 @@ static int cs40l26_change_fw_control_defaults(struct cs40l26_private *cs40l26) static int cs40l26_get_fw_params(struct cs40l26_private *cs40l26) { + u32 id, min_rev, rev, branch; int ret, maj, min, patch; - u32 id, min_rev, rev; + + ret = cl_dsp_fw_rev_get(cs40l26->dsp, &rev); + if (ret) + return ret; + + branch = CL_DSP_GET_MAJOR(rev); + maj = (int) branch; + min = (int) CL_DSP_GET_MINOR(rev); + patch = (int) CL_DSP_GET_PATCH(rev); ret = cl_dsp_fw_id_get(cs40l26->dsp, &id); if (ret) @@ -4106,35 +4221,43 @@ static int cs40l26_get_fw_params(struct cs40l26_private *cs40l26) switch (id) { case CS40L26_FW_ID: - min_rev = CS40L26_FW_A1_RAM_MIN_REV; + if (branch == CS40L26_FW_BRANCH) { + min_rev = CS40L26_FW_MIN_REV; + cs40l26->vibe_state_reporting = true; + } else if (branch == CS40L26_FW_MAINT_BRANCH) { + min_rev = CS40L26_FW_MAINT_MIN_REV; + cs40l26->vibe_state_reporting = false; + } else { + ret = -EINVAL; + } break; case CS40L26_FW_CALIB_ID: - min_rev = CS40L26_FW_CALIB_MIN_REV; + if (branch == CS40L26_FW_CALIB_BRANCH) { + min_rev = CS40L26_FW_CALIB_MIN_REV; + cs40l26->vibe_state_reporting = true; + } else if (branch == CS40L26_FW_MAINT_CALIB_BRANCH) { + min_rev = CS40L26_FW_MAINT_CALIB_MIN_REV; + cs40l26->vibe_state_reporting = false; + } else { + ret = -EINVAL; + } break; default: dev_err(cs40l26->dev, "Invalid FW ID: 0x%06X\n", id); + return -EINVAL; } - ret = cl_dsp_fw_rev_get(cs40l26->dsp, &rev); - if (ret) + if (ret) { + dev_err(cs40l26->dev, "Rev. Branch 0x%02X invalid\n", maj); return ret; + } - maj = (int) CL_DSP_GET_MAJOR(rev); - min = (int) CL_DSP_GET_MINOR(rev); - patch = (int) CL_DSP_GET_PATCH(rev); - - if ((rev & ~CS40L26_FW_BRANCH_MASK) < min_rev) { + if (rev < min_rev) { dev_err(cs40l26->dev, "Invalid firmware revision: %d.%d.%d\n", maj, min, patch); return -EINVAL; } - if ((rev & CS40L26_FW_BRANCH_MASK) == - (CS40L26_FW_A1_RAM_MIN_REV & CS40L26_FW_BRANCH_MASK)) - cs40l26->vibe_state_reporting = true; - else - cs40l26->vibe_state_reporting = false; - cs40l26->fw_id = id; dev_info(cs40l26->dev, "Firmware revision %d.%d.%d\n", maj, min, patch); @@ -4172,14 +4295,13 @@ static int cs40l26_fw_upload(struct cs40l26_private *cs40l26) const struct firmware *fw; int ret; -start: cs40l26->fw_loaded = false; ret = cs40l26_cl_dsp_reinit(cs40l26); if (ret) return ret; - if (svc_le_required || cs40l26->calib_fw) + if (cs40l26->calib_fw) ret = request_firmware(&fw, CS40L26_FW_CALIB_NAME, dev); else ret = request_firmware(&fw, CS40L26_FW_FILE_NAME, dev); @@ -4207,8 +4329,6 @@ start: return ret; if (svc_le_required) { - svc_le_required = false; - ret = cs40l26_dsp_config(cs40l26); if (ret) return ret; @@ -4218,7 +4338,10 @@ start: return ret; cs40l26_pm_runtime_teardown(cs40l26); - goto start; + + ret = cs40l26_dsp_pre_config(cs40l26); + if (ret) + return ret; } ret = cs40l26_coeff_load(cs40l26, tuning_num); @@ -4429,7 +4552,7 @@ static int cs40l26_handle_platform_data(struct cs40l26_private *cs40l26) of_property_read_bool(np, "cirrus,vbbr-enable"); if (!of_property_read_u32(np, "cirrus,vbbr-thld-mv", &val)) - cs40l26->pdata.vbbr_thld = val; + cs40l26->pdata.vbbr_thld_mv = val; if (!of_property_read_u32(np, "cirrus,vbbr-max-att-db", &val)) cs40l26->pdata.vbbr_max_att = val; @@ -4456,7 +4579,7 @@ static int cs40l26_handle_platform_data(struct cs40l26_private *cs40l26) of_property_read_bool(np, "cirrus,vpbr-enable"); if (!of_property_read_u32(np, "cirrus,vpbr-thld-mv", &val)) - cs40l26->pdata.vpbr_thld = val; + cs40l26->pdata.vpbr_thld_mv = val; if (!of_property_read_u32(np, "cirrus,vpbr-max-att-db", &val)) cs40l26->pdata.vpbr_max_att = val; @@ -4641,6 +4764,27 @@ int cs40l26_probe(struct cs40l26_private *cs40l26, usleep_range(CS40L26_CONTROL_PORT_READY_DELAY, CS40L26_CONTROL_PORT_READY_DELAY + 100); + /* + * The DSP may lock up if a haptic effect is triggered via + * GPI event or control port and the PLL is set to closed-loop. + * + * Set PLL to open-loop and remove any default GPI mappings + * to prevent this while the driver is loading and configuring RAM + * firmware. + */ + + ret = cs40l26_set_pll_loop(cs40l26, CS40L26_PLL_REFCLK_SET_OPEN_LOOP); + if (ret) + return ret; + + ret = cs40l26_clear_gpi_event_reg(cs40l26, CS40L26_A1_EVENT_MAP_1); + if (ret) + return ret; + + ret = cs40l26_clear_gpi_event_reg(cs40l26, CS40L26_A1_EVENT_MAP_2); + if (ret) + return ret; + ret = cs40l26_part_num_resolve(cs40l26); if (ret) goto err; @@ -4781,9 +4925,9 @@ int cs40l26_sys_suspend_noirq(struct device *dev) } EXPORT_SYMBOL(cs40l26_sys_suspend_noirq); -void cs40l26_resume_error_handle(struct device *dev) +void cs40l26_resume_error_handle(struct device *dev, int ret) { - dev_err(dev, "PM Runtime Resume failed\n"); + dev_err(dev, "PM Runtime Resume failed: %d\n", ret); pm_runtime_set_active(dev); |