diff options
author | Tai Kuo <taikuo@google.com> | 2021-05-06 11:49:20 +0800 |
---|---|---|
committer | Tai Kuo <taikuo@google.com> | 2021-05-11 16:43:33 +0800 |
commit | cd837712b970b463ae83d78a9fba576e0a26aa11 (patch) | |
tree | cf55feb1150c6f78b8452414a97e874638ac7565 /cs40l26/cs40l26.c | |
parent | 3b156892467f61287ab0061e6e6552031349ceac (diff) | |
download | amplifiers-cd837712b970b463ae83d78a9fba576e0a26aa11.tar.gz |
cs40l26: merge driver modules from CirrusLogic RC2v1
5897d91abf99 input: cs40l26: Improved gain control method
18ded7ea08ca input: cs40l26: Max Back EMF API
756ce5f138dc input: cs40l26: Add support for calibration
458244dc72ca ASoC: cs40l26: Add support for TDM slot configuration
236237999946 ASoC: cs40l26: Add A2H Volume Control
1573f3fe2d5b input: cs40l26: Use END_PLAYBACK for VIBEGEN effects
346cb8cd67ba input: cs40l26: Use TIMEOUT_MS when duration undefined
40d10f025eba input: cs40l26: Remove unused variable
Bug: 184610991
Test: Driver attribute files show up.
Signed-off-by: Tai Kuo <taikuo@google.com>
Change-Id: I7dc25d526fcc0bcbd10619d46b3b382896dc50ac
Diffstat (limited to 'cs40l26/cs40l26.c')
-rw-r--r-- | cs40l26/cs40l26.c | 191 |
1 files changed, 141 insertions, 50 deletions
diff --git a/cs40l26/cs40l26.c b/cs40l26/cs40l26.c index d757c90..8770a06 100644 --- a/cs40l26/cs40l26.c +++ b/cs40l26/cs40l26.c @@ -296,7 +296,7 @@ static void cs40l26_pm_runtime_teardown(struct cs40l26_private *cs40l26) cs40l26->pm_ready = false; } -static int cs40l26_pm_state_transition(struct cs40l26_private *cs40l26, +int cs40l26_pm_state_transition(struct cs40l26_private *cs40l26, enum cs40l26_pm_state state) { struct device *dev = cs40l26->dev; @@ -554,13 +554,42 @@ static int cs40l26_handle_mbox_buffer(struct cs40l26_private *cs40l26) dev_dbg(dev, "HALO Core is awake\n"); break; case CS40L26_DSP_MBOX_F0_EST_START: - /* intentionally fall through */ + dev_dbg(dev, "F0_EST_START\n"); + break; case CS40L26_DSP_MBOX_F0_EST_DONE: - /* intentionally fall through */ + dev_dbg(dev, "F0_EST_DONE\n"); + if (cs40l26->cal_requested & + CS40L26_CALIBRATION_CONTROL_REQUEST_F0_AND_Q) { + cs40l26->cal_requested &= + ~CS40L26_CALIBRATION_CONTROL_REQUEST_F0_AND_Q; + /* for pm_runtime_get see trigger_calibration */ + pm_runtime_mark_last_busy(cs40l26->dev); + pm_runtime_put_autosuspend(cs40l26->dev); + } + else { + dev_err(dev, "Unexpected mbox msg: %d", val); + return -EINVAL; + } + + break; case CS40L26_DSP_MBOX_REDC_EST_START: - /* intentionally fall through */ + dev_dbg(dev, "REDC_EST_START\n"); + break; case CS40L26_DSP_MBOX_REDC_EST_DONE: - /* intentionally fall through */ + dev_dbg(dev, "REDC_EST_DONE\n"); + if (cs40l26->cal_requested & + CS40L26_CALIBRATION_CONTROL_REQUEST_REDC) { + cs40l26->cal_requested &= + ~CS40L26_CALIBRATION_CONTROL_REQUEST_REDC; + /* for pm_runtime_get see trigger_calibration */ + pm_runtime_mark_last_busy(cs40l26->dev); + pm_runtime_put_autosuspend(cs40l26->dev); + } + else { + dev_err(dev, "Unexpected mbox msg: %d", val); + return -EINVAL; + } + break; case CS40L26_DSP_MBOX_SYS_ACK: dev_err(dev, "Mbox buffer value (0x%X) not supported\n", val); @@ -1604,43 +1633,54 @@ static void cs40l26_set_gain_worker(struct work_struct *work) { struct cs40l26_private *cs40l26 = container_of(work, struct cs40l26_private, set_gain_work); - u32 val; + u16 amp_vol_pcm; + u32 reg, val; int ret; pm_runtime_get_sync(cs40l26->dev); mutex_lock(&cs40l26->lock); - ret = regmap_update_bits(cs40l26->regmap, CS40L26_AMP_CTRL, - CS40L26_AMP_CTRL_VOL_PCM_MASK, - cs40l26->amp_vol_pcm << CS40L26_AMP_CTRL_VOL_PCM_SHIFT); - if (ret) { - dev_err(cs40l26->dev, "Failed to update digtal gain\n"); - goto err_mutex; - } - - ret = regmap_read(cs40l26->regmap, CS40L26_AMP_CTRL, &val); - if (ret) { - dev_err(cs40l26->dev, "Failed to read AMP control\n"); - goto err_mutex; - } - switch (cs40l26->revid) { case CS40L26_REVID_A0: + amp_vol_pcm = CS40L26_AMP_VOL_PCM_MAX & cs40l26->gain_pct; + + ret = regmap_update_bits(cs40l26->regmap, CS40L26_AMP_CTRL, + CS40L26_AMP_CTRL_VOL_PCM_MASK, amp_vol_pcm << + CS40L26_AMP_CTRL_VOL_PCM_SHIFT); + if (ret) { + dev_err(cs40l26->dev, "Failed to update digtal gain\n"); + goto err_mutex; + } + + ret = regmap_read(cs40l26->regmap, CS40L26_AMP_CTRL, &val); + if (ret) { + dev_err(cs40l26->dev, "Failed to read AMP control\n"); + goto err_mutex; + } + ret = cs40l26_pseq_v1_add_pair(cs40l26, CS40L26_AMP_CTRL, val, true); + if (ret) + dev_err(cs40l26->dev, "Failed to set gain in pseq\n"); break; case CS40L26_REVID_A1: - ret = cs40l26_pseq_v2_add_write_reg_full(cs40l26, - CS40L26_AMP_CTRL, val, true); + val = cs40l26_attn_q21_2_vals[cs40l26->gain_pct]; + + /* Write Q21.2 value to SOURCE_ATTENUATION */ + ret = cl_dsp_get_reg(cs40l26->dsp, "SOURCE_ATTENUATION", + CL_DSP_XM_UNPACKED_TYPE, CS40L26_EXT_ALGO_ID, ®); + if (ret) + goto err_mutex; + + ret = regmap_write(cs40l26->regmap, reg, val); + if (ret) + dev_err(cs40l26->dev, "Failed to set attenuation\n"); break; default: dev_err(cs40l26->dev, "Revid ID not supported: %02X\n", cs40l26->revid); } - if (ret) - dev_err(cs40l26->dev, "Failed to set gain in pseq\n"); - err_mutex: mutex_unlock(&cs40l26->lock); pm_runtime_mark_last_busy(cs40l26->dev); @@ -1654,11 +1694,16 @@ static void cs40l26_vibe_start_worker(struct work_struct *work) struct device *dev = cs40l26->dev; int ret = 0; unsigned int reg, freq; - u32 index = 0, algo_id; + u32 index = 0, vibe_id, buzz_id; u16 duration; - pm_runtime_get_sync(dev); - mutex_lock(&cs40l26->lock); + if (cs40l26->fw_mode == CS40L26_FW_MODE_RAM) { + buzz_id = CS40L26_BUZZGEN_ALGO_ID; + vibe_id = CS40L26_VIBEGEN_ALGO_ID; + } else { + buzz_id = CS40L26_BUZZGEN_ROM_ALGO_ID; + vibe_id = CS40L26_VIBEGEN_ROM_ALGO_ID; + } if (cs40l26->effect->u.periodic.waveform == FF_CUSTOM) index = cs40l26->trigger_indices[cs40l26->effect->id]; @@ -1669,9 +1714,25 @@ static void cs40l26_vibe_start_worker(struct work_struct *work) else duration = cs40l26->effect->replay.length; - hrtimer_start(&cs40l26->vibe_timer, + pm_runtime_get_sync(dev); + mutex_lock(&cs40l26->lock); + + if (duration == 0) { /* Handle special case with TIMEOUT_MS */ + ret = cl_dsp_get_reg(cs40l26->dsp, "TIMEOUT_MS", + CL_DSP_XM_UNPACKED_TYPE, vibe_id, ®); + if (ret) + goto err_mutex; + + ret = regmap_write(cs40l26->regmap, reg, 0); + if (ret) { + dev_err(dev, "Failed to set haptic duration\n"); + goto err_mutex; + } + } else { + hrtimer_start(&cs40l26->vibe_timer, ktime_set(CS40L26_MS_TO_SECS(duration), CS40L26_MS_TO_NS(duration % 1000)), HRTIMER_MODE_REL); + } cs40l26_vibe_state_set(cs40l26, CS40L26_VIBE_STATE_HAPTIC); @@ -1683,17 +1744,10 @@ static void cs40l26_vibe_start_worker(struct work_struct *work) goto err_mutex; break; case FF_SINE: - if (cs40l26->fw_mode == CS40L26_FW_MODE_RAM) - algo_id = CS40L26_BUZZGEN_ALGO_ID; - else - algo_id = CS40L26_BUZZGEN_ROM_ALGO_ID; - ret = cl_dsp_get_reg(cs40l26->dsp, "BUZZ_EFFECTS2_BUZZ_FREQ", - CL_DSP_XM_UNPACKED_TYPE, algo_id, ®); - if (ret) { - dev_err(dev, "Failed to find BUZZGEN control\n"); + CL_DSP_XM_UNPACKED_TYPE, buzz_id, ®); + if (ret) goto err_mutex; - } freq = CS40L26_MS_TO_HZ(cs40l26->effect->u.periodic.period); @@ -1739,19 +1793,42 @@ static void cs40l26_vibe_stop_worker(struct work_struct *work) { struct cs40l26_private *cs40l26 = container_of(work, struct cs40l26_private, vibe_stop_work); + unsigned int reg; + u32 algo_id; int ret; pm_runtime_get_sync(cs40l26->dev); mutex_lock(&cs40l26->lock); - if (cs40l26->vibe_state == CS40L26_VIBE_STATE_STOPPED) - goto mutex_exit; + if (cs40l26->effect->u.periodic.waveform == FF_SINE) { + ret = cs40l26_ack_write(cs40l26, CS40L26_DSP_VIRTUAL1_MBOX_1, + CS40L26_STOP_PLAYBACK, CS40L26_DSP_MBOX_RESET); + if (ret) { + dev_err(cs40l26->dev, "Failed to stop playback\n"); + goto mutex_exit; + } + } else { + if (cs40l26->fw_mode == CS40L26_FW_MODE_ROM) + algo_id = CS40L26_VIBEGEN_ROM_ALGO_ID; + else + algo_id = CS40L26_VIBEGEN_ALGO_ID; - ret = cs40l26_ack_write(cs40l26, CS40L26_DSP_VIRTUAL1_MBOX_1, - CS40L26_STOP_PLAYBACK, CS40L26_DSP_MBOX_RESET); - if (ret) { - dev_err(cs40l26->dev, "Failed to stop playback\n"); - goto mutex_exit; + ret = cl_dsp_get_reg(cs40l26->dsp, "END_PLAYBACK", + CL_DSP_XM_UNPACKED_TYPE, algo_id, ®); + if (ret) + goto mutex_exit; + + ret = regmap_write(cs40l26->regmap, reg, 1); + if (ret) { + dev_err(cs40l26->dev, "Failed to end VIBE playback\n"); + goto mutex_exit; + } + + ret = regmap_write(cs40l26->regmap, reg, 0); + if (ret) { + dev_err(cs40l26->dev, "Failed to reset END_PLAYBACK\n"); + goto mutex_exit; + } } cs40l26_vibe_state_set(cs40l26, CS40L26_VIBE_STATE_STOPPED); @@ -1776,7 +1853,7 @@ static void cs40l26_set_gain(struct input_dev *dev, u16 gain) { struct cs40l26_private *cs40l26 = input_get_drvdata(dev); - cs40l26->amp_vol_pcm = CS40L26_AMP_VOL_PCM_MAX & gain; + cs40l26->gain_pct = gain; queue_work(cs40l26->vibe_workqueue, &cs40l26->set_gain_work); } @@ -2214,12 +2291,23 @@ static int cs40l26_input_init(struct cs40l26_private *cs40l26) hrtimer_init(&cs40l26->vibe_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); cs40l26->vibe_timer.function = cs40l26_vibe_timer; + + ret = sysfs_create_group(&cs40l26->input->dev.kobj, &cs40l26_dev_attr_group); - if (ret) + if (ret) { dev_err(dev, "Failed to create sysfs group: %d\n", ret); - else - cs40l26->vibe_init_success = true; + return ret; + } + + ret = sysfs_create_group(&cs40l26->input->dev.kobj, + &cs40l26_dev_attr_cal_group); + if (ret){ + dev_err(dev, "Failed to create cal sysfs group: %d\n", ret); + return ret; + } + + cs40l26->vibe_init_success = true; return ret; } @@ -3096,9 +3184,12 @@ int cs40l26_remove(struct cs40l26_private *cs40l26) if (cs40l26->vibe_timer.function) hrtimer_cancel(&cs40l26->vibe_timer); - if (cs40l26->vibe_init_success) + if (cs40l26->vibe_init_success) { sysfs_remove_group(&cs40l26->input->dev.kobj, &cs40l26_dev_attr_group); + sysfs_remove_group(&cs40l26->input->dev.kobj, + &cs40l26_dev_attr_cal_group); + } if (cs40l26->input) input_unregister_device(cs40l26->input); |