summaryrefslogtreecommitdiff
path: root/cs40l26/cs40l26.c
diff options
context:
space:
mode:
authorTai Kuo <taikuo@google.com>2023-01-19 15:44:35 +0800
committerTai Kuo <taikuo@google.com>2023-02-17 02:32:58 +0000
commit8c576c450604b70c7b1170803b039ed0b172b1a4 (patch)
tree13f3091cc92a03d29a9545339a99bcf6bfdf40e5 /cs40l26/cs40l26.c
parentc3163996fedeb44a64f865a23a150b5cdaeb8a5a (diff)
downloadamplifiers-8c576c450604b70c7b1170803b039ed0b172b1a4.tar.gz
cs40l26: merge cs40l26 v5.7.1 and dsp v3.2.0 for firmware RAM19
Merge Cirrus kernel driver (tag cs40l26 v5.7.1 and dsp v3.2.0) for firmware RAM19 (v7.2.49 (runtime) & v1.1.35 (calib)) Branches: v6.1-cirrus-dsp-fw and v5.15-cs40l26 Tags: cl-dsp-fw-v3.2.0_6.1, cs40l26-v5.7.1_5.15 Files: Documentation/devicetree/bindings/input/cs40l26.yaml (New) drivers/firmware/cirrus/cl_dsp-debugfs.c (New) drivers/firmware/cirrus/cl_dsp.c include/linux/firmware/cirrus/cl_dsp.h drivers/input/misc/cs40l26-debugfs.c (New) drivers/input/misc/cs40l26-i2c.c (No changes) drivers/input/misc/cs40l26-spi.c (No changes) drivers/input/misc/cs40l26-sysfs.c drivers/input/misc/cs40l26-tables.c (No changes) drivers/input/misc/cs40l26.c include/linux/mfd/cs40l26.h sound/soc/codecs/cs40l26.c -> cs40l26-codec.c (No changes) ... Tag: cs40l26-v5.7.1_5.15: CS40L26 v5.7.1 Features: - Add device tree parameter for skipping normal delay before stopping playback - Add Debugfs controls for reading FW controls - Add support for debug logger - Add support for DVL coefficient calibration Bug fixes: - Remove unused functions - Fix potential memory leak - Remove terminator write from owt setup Commits: 6ebc38890aba input: cs40l26: Use linked list to track uploaded effects a26249ad215e input: cs40l26: Add dvl_peq_coefficients control 4b3e947f0b4b input: cs40l26: Add support for DVL PEQ cal request 09e8c62468df input: cs40l26: Load cs40l26-dvl.bin for both runtime and cal FW e0176fefb2ba input: cs40l26: Use completions for calibration requests fcec4c470704 input: cs40l26: Add support for cl_dsp debug logger ae8df81dcd84 input: cs40l26: Add Debugfs to read FW controls 52400ada10c5 Documentation: cs40l26: Skip delay for reserved indices e4a7db96e597 input: cs40l26: Skip delay for reserved indices 350990b074eb input: cs40l26: Remove terminator write from owt setup 3dd46703b73c input: cs40l26: Only load tuning if algorithm is present 7e6977e678ae input: cs40l26: Resolve memory leak b98183034193 input: cs40l26: Remove unused functions ... Tag: cl-dsp-fw-v3.2.0_6.1: Cirrus DSP Firmware Driver v3.2.0 New Features: - Support Debugfs Event Logger Improvements: - Use kvmalloc for large data handling - Mask MSB when looking for algorithm ID Commits: 6ebc38890aba input: cs40l26: Use linked list to track uploaded effects 7f373ff54162 firmware: cirrus: Add support for Debugfs and debug logging e7b3c3b3f852 firmware: cirrus: Use kvmalloc() for large memory allocation 00ca52ae9448 firmware: cirrus: Mask bits[31:16] when comparing algorithm ids 021d5954c875 firmware: cirrus: add function to check for algo presence Bug: 261694194 Test: Copy texts and adjust alarm Test: NFC, dumpstate, keyboard vibration Test: idlcli commands Test: Switch firmware continuous Test: Switch firmware and check the first tick effect Test: atest PtsVibratorHalTestSuite \ PtsHapticsTestCases \ VibratorHalCs40l26TestSuite \ VtsHalVibratorManagerTargetTest \ VtsHalVibratorTargetTest \ android.os.cts.VibratorTest \ android.os.cts.VibratorManagerTest \ android.os.cts.VibrationEffectTest \ android.os.cts.VibrationAttributesTest \ android.os.cts.CombinedVibrationTest Change-Id: I10e31d93b0248b32858e0aa07d4b40e20e66b38a Signed-off-by: Tai Kuo <taikuo@google.com>
Diffstat (limited to 'cs40l26/cs40l26.c')
-rw-r--r--cs40l26/cs40l26.c813
1 files changed, 428 insertions, 385 deletions
diff --git a/cs40l26/cs40l26.c b/cs40l26/cs40l26.c
index 2073191..3a0734e 100644
--- a/cs40l26/cs40l26.c
+++ b/cs40l26/cs40l26.c
@@ -686,6 +686,7 @@ static int cs40l26_handle_mbox_buffer(struct cs40l26_private *cs40l26)
{
struct device *dev = cs40l26->dev;
u32 val = 0;
+ int ret;
while (!cs40l26_mbox_buffer_read(cs40l26, &val)) {
if ((val & CS40L26_DSP_MBOX_CMD_INDEX_MASK)
@@ -695,6 +696,17 @@ static int cs40l26_handle_mbox_buffer(struct cs40l26_private *cs40l26)
return -ENOTRECOVERABLE;
}
+ if ((val & CS40L26_DSP_MBOX_CMD_INDEX_MASK) ==
+ CS40L26_DSP_MBOX_WATERMARK) {
+ dev_dbg(dev, "Mailbox: WATERMARK\n");
+
+ ret = cl_dsp_logger_update(cs40l26->cl_dsp_db);
+ if (ret)
+ return ret;
+
+ continue;
+ }
+
switch (val) {
case CS40L26_DSP_MBOX_COMPLETE_MBOX:
ATRACE_END();
@@ -749,33 +761,14 @@ static int cs40l26_handle_mbox_buffer(struct cs40l26_private *cs40l26)
break;
case CS40L26_DSP_MBOX_F0_EST_DONE:
dev_dbg(dev, "Mailbox: 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 */
- cs40l26_pm_exit(cs40l26->dev);
- } else {
- dev_err(dev, "Unexpected mbox msg: %d", val);
- return -EINVAL;
- }
-
+ complete(&cs40l26->cal_f0_cont);
break;
case CS40L26_DSP_MBOX_REDC_EST_START:
dev_dbg(dev, "Mailbox: REDC_EST_START\n");
break;
case CS40L26_DSP_MBOX_REDC_EST_DONE:
dev_dbg(dev, "Mailbox: 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 */
- cs40l26_pm_exit(cs40l26->dev);
- } else {
- dev_err(dev, "Unexpected mbox msg: %d", val);
- return -EINVAL;
- }
+ complete(&cs40l26->cal_redc_cont);
break;
case CS40L26_DSP_MBOX_LE_EST_START:
dev_dbg(dev, "Mailbox: LE_EST_START\n");
@@ -783,6 +776,13 @@ static int cs40l26_handle_mbox_buffer(struct cs40l26_private *cs40l26)
case CS40L26_DSP_MBOX_LE_EST_DONE:
dev_dbg(dev, "Mailbox: LE_EST_DONE\n");
break;
+ case CS40L26_DSP_MBOX_PEQ_CALCULATION_START:
+ dev_dbg(dev, "Mailbox: PEQ_CALCULATION_START\n");
+ break;
+ case CS40L26_DSP_MBOX_PEQ_CALCULATION_DONE:
+ dev_dbg(dev, "Mailbox: PEQ_CALCULATION_DONE\n");
+ complete(&cs40l26->cal_dvl_peq_cont);
+ break;
case CS40L26_DSP_MBOX_SYS_ACK:
dev_err(dev, "Mailbox: ACK\n");
return -EPERM;
@@ -796,6 +796,53 @@ static int cs40l26_handle_mbox_buffer(struct cs40l26_private *cs40l26)
return 0;
}
+int cs40l26_copy_f0_est_to_dvl(struct cs40l26_private *cs40l26)
+{
+ u32 reg, f0_measured_q9_14, global_sample_rate, normalized_f0_q1_23;
+ int ret, sample_rate;
+
+ /* Must be awake and under mutex lock */
+ ret = regmap_read(cs40l26->regmap, CS40L26_GLOBAL_SAMPLE_RATE,
+ &global_sample_rate);
+ if (ret)
+ return ret;
+
+ switch (global_sample_rate & CS40L26_GLOBAL_FS_MASK) {
+ case CS40L26_GLOBAL_FS_48K:
+ sample_rate = 48000;
+ break;
+ case CS40L26_GLOBAL_FS_96K:
+ sample_rate = 96000;
+ break;
+ default:
+ dev_warn(cs40l26->dev, "Invalid GLOBAL_FS, %08X",
+ global_sample_rate);
+ return -EINVAL;
+ }
+
+ ret = cl_dsp_get_reg(cs40l26->dsp, "F0_EST",
+ CL_DSP_XM_UNPACKED_TYPE,
+ CS40L26_F0_EST_ALGO_ID, &reg);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(cs40l26->regmap, reg, &f0_measured_q9_14);
+ if (ret)
+ return ret;
+
+ ret = cl_dsp_get_reg(cs40l26->dsp, "LRA_NORM_F0",
+ CL_DSP_XM_UNPACKED_TYPE,
+ CS40L26_DVL_ALGO_ID, &reg);
+ if (ret)
+ return ret;
+
+ normalized_f0_q1_23 = (f0_measured_q9_14 << 9) / sample_rate;
+ ret = regmap_write(cs40l26->regmap, reg, normalized_f0_q1_23);
+
+ return ret;
+}
+EXPORT_SYMBOL(cs40l26_copy_f0_est_to_dvl);
+
int cs40l26_asp_start(struct cs40l26_private *cs40l26)
{
int ret;
@@ -1668,8 +1715,8 @@ static int cs40l26_irq_update_mask(struct cs40l26_private *cs40l26, u32 reg,
static int cs40l26_buzzgen_set(struct cs40l26_private *cs40l26, u16 freq,
u16 level, u16 duration, u8 buzzgen_num)
{
- int ret;
unsigned int base_reg, freq_reg, level_reg, duration_reg;
+ int ret;
/* BUZZ_EFFECTS1_BUZZ_xxx are initially populated by contents of OTP.
* The buzz specified by these controls is triggered by writing
@@ -1709,7 +1756,8 @@ static int cs40l26_buzzgen_set(struct cs40l26_private *cs40l26, u16 freq,
}
static int cs40l26_map_gpi_to_haptic(struct cs40l26_private *cs40l26,
- u32 index, u8 bank, struct ff_effect *effect)
+ struct ff_effect *effect,
+ struct cs40l26_uploaded_effect *ueffect)
{
u16 button = effect->trigger.button;
u8 gpio = (button & CS40L26_BTN_NUM_MASK) >> CS40L26_BTN_NUM_SHIFT;
@@ -1720,8 +1768,9 @@ static int cs40l26_map_gpi_to_haptic(struct cs40l26_private *cs40l26,
edge = (button & CS40L26_BTN_EDGE_MASK) >> CS40L26_BTN_EDGE_SHIFT;
- switch (bank) {
+ switch (ueffect->wvfrm_bank) {
case CS40L26_RAM_BANK_ID:
+ case CS40L26_BUZ_BANK_ID:
owt = false;
ev_handler_bank_ram = true;
break;
@@ -1735,7 +1784,7 @@ static int cs40l26_map_gpi_to_haptic(struct cs40l26_private *cs40l26,
break;
default:
dev_err(cs40l26->dev, "Effect bank %u not supported\n",
- bank);
+ ueffect->wvfrm_bank);
return -EINVAL;
}
@@ -1746,23 +1795,19 @@ static int cs40l26_map_gpi_to_haptic(struct cs40l26_private *cs40l26,
}
reg = cs40l26->event_map_base + (edge ? 0 : 4);
- write_val = (index & CS40L26_BTN_INDEX_MASK) |
+ write_val = (ueffect->trigger_index & CS40L26_BTN_INDEX_MASK) |
(ev_handler_bank_ram << CS40L26_BTN_BANK_SHIFT) |
(owt << CS40L26_BTN_OWT_SHIFT);
- ret = cs40l26_pm_enter(cs40l26->dev);
- if (ret)
- return ret;
-
ret = regmap_write(cs40l26->regmap, reg, write_val);
if (ret) {
dev_err(cs40l26->dev, "Failed to update event map\n");
- goto pm_exit;
+ return ret;
}
ret = cl_dsp_fw_rev_get(cs40l26->dsp, &fw_rev);
if (ret)
- goto pm_exit;
+ return ret;
use_timeout = (!cs40l26->calib_fw &&
fw_rev >= CS40L26_FW_GPI_TIMEOUT_MIN_REV) ||
@@ -1774,45 +1819,58 @@ static int cs40l26_map_gpi_to_haptic(struct cs40l26_private *cs40l26,
CL_DSP_XM_UNPACKED_TYPE,
CS40L26_VIBEGEN_ALGO_ID, &reg);
if (ret)
- goto pm_exit;
+ return ret;
ret = regmap_write(cs40l26->regmap, reg, effect->replay.length);
if (ret)
- dev_err(cs40l26->dev, "Failed to set GPI timeout\n");
+ dev_warn(cs40l26->dev,
+ "Failed to set GPI timeout, continuing...\n");
}
if (edge)
- cs40l26->gpi_ids[CS40L26_GPIO_MAP_A_PRESS] = effect->id;
+ ueffect->mapping = CS40L26_GPIO_MAP_A_PRESS;
else
- cs40l26->gpi_ids[CS40L26_GPIO_MAP_A_RELEASE] = effect->id;
-
-pm_exit:
- cs40l26_pm_exit(cs40l26->dev);
+ ueffect->mapping = CS40L26_GPIO_MAP_A_RELEASE;
return ret;
}
-static struct cs40l26_owt *cs40l26_owt_find(struct cs40l26_private *cs40l26,
+static struct cs40l26_uploaded_effect
+ *cs40l26_uploaded_effect_find(struct cs40l26_private *cs40l26,
int id)
{
- struct cs40l26_owt *owt;
+ struct list_head *head = &cs40l26->effect_head;
+ struct cs40l26_uploaded_effect *ueffect;
- if (list_empty(&cs40l26->owt_head)) {
- dev_err(cs40l26->dev, "OWT list is empty\n");
- return ERR_PTR(-EINVAL);
+ if (list_empty(head)) {
+ dev_dbg(cs40l26->dev, "Effect list is empty\n");
+ return ERR_PTR(-ENODATA);
}
- list_for_each_entry(owt, &cs40l26->owt_head, list) {
- if (owt->effect_id == id)
+ list_for_each_entry(ueffect, head, list) {
+ if (ueffect->id == id)
break;
}
- if (owt->effect_id != id) {
- dev_err(cs40l26->dev, "OWT effect with ID %d not found\n", id);
- return ERR_PTR(-EINVAL);
+ if (ueffect->id != id) {
+ dev_dbg(cs40l26->dev, "No such effect (ID = %d)\n", id);
+ return ERR_PTR(-ENODEV);
+ }
+
+ return ueffect;
+}
+
+static bool cs40l26_is_no_wait_ram_index(struct cs40l26_private *cs40l26,
+ u32 index)
+{
+ int i;
+
+ for (i = 0; i < cs40l26->num_no_wait_ram_indices; i++) {
+ if (cs40l26->no_wait_ram_indices[i] == index)
+ return true;
}
- return owt;
+ return false;
}
static void cs40l26_set_gain_worker(struct work_struct *work)
@@ -1861,13 +1919,12 @@ static void cs40l26_vibe_start_worker(struct work_struct *work)
struct cs40l26_private *cs40l26 = container_of(work,
struct cs40l26_private, vibe_start_work);
struct device *dev = cs40l26->dev;
- u32 index = 0;
- int ret = 0;
+ struct cs40l26_uploaded_effect *ueffect;
struct ff_effect *effect;
- struct cs40l26_owt *owt;
unsigned int reg;
- bool invert;
u16 duration;
+ bool invert;
+ int ret;
dev_dbg(dev, "%s\n", __func__);
@@ -1879,16 +1936,11 @@ static void cs40l26_vibe_start_worker(struct work_struct *work)
effect = cs40l26->trigger_effect;
- if (effect->u.periodic.waveform == FF_CUSTOM ||
- effect->u.periodic.waveform == FF_SINE)
- index = cs40l26->trigger_indices[effect->id];
-
- if (is_owt(index)) {
- owt = cs40l26_owt_find(cs40l26, effect->id);
- if (IS_ERR(owt)) {
- ret = PTR_ERR(owt);
- goto err_mutex;
- }
+ ueffect = cs40l26_uploaded_effect_find(cs40l26, effect->id);
+ if (IS_ERR_OR_NULL(ueffect)) {
+ dev_err(dev, "No such effect to play back\n");
+ ret = PTR_ERR(ueffect);
+ goto err_mutex;
}
duration = effect->replay.length;
@@ -1931,9 +1983,11 @@ static void cs40l26_vibe_start_worker(struct work_struct *work)
case FF_CUSTOM:
case FF_SINE:
ret = cs40l26_ack_write(cs40l26, CS40L26_DSP_VIRTUAL1_MBOX_1,
- index, CS40L26_DSP_MBOX_RESET);
+ ueffect->trigger_index, CS40L26_DSP_MBOX_RESET);
if (ret)
goto err_mutex;
+
+ cs40l26->cur_index = ueffect->trigger_index;
break;
default:
dev_err(dev, "Invalid waveform type: 0x%X\n",
@@ -1957,6 +2011,8 @@ static void cs40l26_vibe_stop_worker(struct work_struct *work)
{
struct cs40l26_private *cs40l26 = container_of(work,
struct cs40l26_private, vibe_stop_work);
+ bool skip_delay;
+ u32 delay_us;
int ret;
dev_dbg(cs40l26->dev, "%s\n", __func__);
@@ -1965,13 +2021,24 @@ static void cs40l26_vibe_stop_worker(struct work_struct *work)
if (ret)
return;
- /* 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);
+ delay_us = cs40l26->delay_before_stop_playback_us;
+ skip_delay = cs40l26_is_no_wait_ram_index(cs40l26, cs40l26->cur_index);
+
+ if (delay_us && !skip_delay) {
+ mutex_unlock(&cs40l26->lock);
+
+ dev_info(cs40l26->dev, "Applying delay\n");
+
+ /* wait for SVC init phase to complete */
+ usleep_range(delay_us, delay_us + 100);
+
+ mutex_lock(&cs40l26->lock);
+ } else {
+ dev_info(cs40l26->dev, "Skipping delay\n");
+ }
+
if (cs40l26->vibe_state != CS40L26_VIBE_STATE_HAPTIC) {
dev_warn(cs40l26->dev, "Attempted stop when vibe_state = %d\n",
cs40l26->vibe_state);
@@ -2260,24 +2327,6 @@ err_free:
return ret;
}
-static int cs40l26_owt_add(struct cs40l26_private *cs40l26, int effect_id,
- u32 index)
-{
- struct cs40l26_owt *owt_new;
-
- owt_new = kzalloc(sizeof(*owt_new), GFP_KERNEL);
- if (!owt_new)
- return -ENOMEM;
-
- owt_new->effect_id = effect_id;
- owt_new->trigger_index = index;
- list_add(&owt_new->list, &cs40l26->owt_head);
-
- cs40l26->num_owt_effects++;
-
- return 0;
-}
-
static int cs40l26_owt_upload(struct cs40l26_private *cs40l26, u8 *data,
u32 data_size_bytes)
{
@@ -2371,7 +2420,7 @@ static u8 *cs40l26_ncw_amp_scaling(struct cs40l26_private *cs40l26, u8 amp,
nsections);
if (ret) {
dev_err(cs40l26->dev, "Failed to get section info\n");
- return ERR_PTR(ret);
+ goto sections_free;
}
for (i = 0; i < nsections; i++) {
@@ -2384,14 +2433,17 @@ static u8 *cs40l26_ncw_amp_scaling(struct cs40l26_private *cs40l26, u8 amp,
out_data = kcalloc(data_bytes, sizeof(u8), GFP_KERNEL);
if (!out_data) {
- kfree(sections);
- return ERR_PTR(-ENOMEM);
+ ret = -ENOMEM;
+ goto sections_free;
}
out_ch = cl_dsp_memchunk_create((void *) out_data, data_bytes);
cs40l26_owt_set_section_info(cs40l26, &out_ch, sections, nsections);
- return out_data;
+sections_free:
+ kfree(sections);
+
+ return ret ? ERR_PTR(ret) : out_data;
}
static int cs40l26_owt_comp_data_size(struct cs40l26_private *cs40l26,
@@ -2649,62 +2701,44 @@ sections_err_free:
static u8 cs40l26_get_lowest_free_buzzgen(struct cs40l26_private *cs40l26)
{
- int buzzgen, i;
+ u8 buzzgen = 1;
+ struct cs40l26_uploaded_effect *ueffect;
- /* Note that this search starts at RAM_BUZZ1 and does not utilize the
- * OTP buzz
- */
- for (buzzgen = 1; buzzgen <= CS40L26_BUZZGEN_NUM_CONFIGS; buzzgen++) {
- for (i = 0; i < FF_MAX_EFFECTS; i++) {
- if (cs40l26->trigger_indices[i] ==
- CS40L26_BUZZGEN_INDEX_START + buzzgen)
- break;
- }
+ if (list_empty(&cs40l26->effect_head))
+ return buzzgen;
- if (i == FF_MAX_EFFECTS)
- break;
+ list_for_each_entry(ueffect, &cs40l26->effect_head, list) {
+ if (ueffect->wvfrm_bank == CS40L26_BUZ_BANK_ID)
+ buzzgen++;
}
return buzzgen;
}
static int cs40l26_sine_upload(struct cs40l26_private *cs40l26,
- struct ff_effect *effect)
+ struct ff_effect *effect,
+ struct cs40l26_uploaded_effect *ueffect)
{
struct device *dev = cs40l26->dev;
- int ret;
u8 lowest_free_buzzgen, level;
- u16 freq;
- u32 trigger_index;
-
- if (effect->u.periodic.period) {
- if (effect->u.periodic.period < CS40L26_BUZZGEN_PERIOD_MIN ||
- effect->u.periodic.period > CS40L26_BUZZGEN_PERIOD_MAX) {
- dev_err(dev,
- "%u ms period not within range (4-10 ms)\n",
- effect->u.periodic.period);
- return -EINVAL;
- }
- } else {
- dev_err(dev, "Sine wave period not specified\n");
- return -EINVAL;
- }
+ u16 freq, period;
+ int ret;
- if (effect->u.periodic.magnitude) {
- if (effect->u.periodic.magnitude < CS40L26_BUZZGEN_LEVEL_MIN ||
- effect->u.periodic.magnitude > CS40L26_BUZZGEN_LEVEL_MAX) {
- dev_err(dev,
- "%u magnitude not within range (%d-%d)\n",
- effect->u.periodic.magnitude,
- CS40L26_BUZZGEN_LEVEL_MIN,
- CS40L26_BUZZGEN_LEVEL_MAX);
- return -EINVAL;
- }
+ if (effect->u.periodic.period < CS40L26_BUZZGEN_PER_MIN)
+ period = CS40L26_BUZZGEN_PER_MIN;
+ else if (effect->u.periodic.period > CS40L26_BUZZGEN_PER_MAX)
+ period = CS40L26_BUZZGEN_PER_MAX;
+ else
+ period = effect->u.periodic.period;
+
+ freq = CS40L26_MS_TO_HZ(period);
+ if (effect->u.periodic.magnitude < CS40L26_BUZZGEN_LEVEL_MIN)
+ level = CS40L26_BUZZGEN_LEVEL_MIN;
+ else if (effect->u.periodic.magnitude > CS40L26_BUZZGEN_LEVEL_MAX)
+ level = CS40L26_BUZZGEN_LEVEL_MAX;
+ else
level = effect->u.periodic.magnitude;
- } else {
- level = CS40L26_BUZZGEN_LEVEL_DEFAULT;
- }
lowest_free_buzzgen = cs40l26_get_lowest_free_buzzgen(cs40l26);
dev_dbg(dev, "lowest_free_buzzgen: %d", lowest_free_buzzgen);
@@ -2714,172 +2748,184 @@ static int cs40l26_sine_upload(struct cs40l26_private *cs40l26,
return -ENOSPC;
}
- freq = CS40L26_MS_TO_HZ(effect->u.periodic.period);
-
ret = cs40l26_buzzgen_set(cs40l26, freq, level,
effect->replay.length, lowest_free_buzzgen);
if (ret)
return ret;
- trigger_index = CS40L26_BUZZGEN_INDEX_START + lowest_free_buzzgen;
- cs40l26->trigger_indices[effect->id] = trigger_index;
-
- if (effect->trigger.button)
- ret = cs40l26_map_gpi_to_haptic(cs40l26, trigger_index,
- CS40L26_RAM_BANK_ID, effect);
+ ueffect->id = effect->id;
+ ueffect->wvfrm_bank = CS40L26_BUZ_BANK_ID;
+ ueffect->trigger_index = CS40L26_BUZZGEN_INDEX_START +
+ lowest_free_buzzgen;
return ret;
}
-static void cs40l26_upload_worker(struct work_struct *work)
+static int cs40l26_custom_upload(struct cs40l26_private *cs40l26,
+ struct ff_effect *effect,
+ struct cs40l26_uploaded_effect *ueffect)
{
- struct cs40l26_private *cs40l26 = container_of(work,
- struct cs40l26_private, upload_work);
- struct device *cdev = cs40l26->dev;
- u8 *refactored_data = NULL;
- int ret = 0, refactored_size, len;
- u32 trigger_index, min_index, max_index, nwaves;
- struct ff_effect *effect;
+ s16 first = cs40l26->raw_custom_data[0];
+ bool is_pwle = (first != CS40L26_WT_TYPE10_COMP_BUFFER);
+ bool is_svc = (first == CS40L26_SVC_ID);
+ struct device *dev = cs40l26->dev;
+ u32 nwaves, min_index, max_index, trigger_index;
+ int ret, data_len, refactored_data_len;
+ u8 *refactored_data;
u16 index, bank;
- bool pwle, svc_waveform;
- ret = cs40l26_pm_enter(cdev);
- if (ret)
- return;
+ data_len = effect->u.periodic.custom_len;
- mutex_lock(&cs40l26->lock);
+ if (data_len > CS40L26_CUSTOM_DATA_SIZE) {
+ refactored_data_len = cs40l26_refactor_owt(cs40l26,
+ cs40l26->raw_custom_data, data_len, is_pwle,
+ is_svc, &refactored_data);
+ if (refactored_data_len <= 0) {
+ dev_err(cs40l26->dev, "Failed to refactor OWT\n");
+ return -ENOMEM;
+ }
- effect = &cs40l26->upload_effect;
+ ret = cs40l26_owt_upload(cs40l26, refactored_data,
+ refactored_data_len);
+ kfree(refactored_data);
+ if (ret)
+ return ret;
- if (effect->type != FF_PERIODIC) {
- dev_err(cdev, "Effect type 0x%X not supported\n", effect->type);
- ret = -EINVAL;
- goto out_mutex;
+ bank = (u16) CS40L26_OWT_BANK_ID;
+ index = (u16) cs40l26->num_owt_effects;
+ } else {
+ bank = (u16) first;
+ index = (u16) (cs40l26->raw_custom_data[1] &
+ CS40L26_MAX_INDEX_MASK);
}
- if (is_owt(cs40l26->trigger_indices[effect->id])) {
- dev_err(cdev, "Open Wavetable effects cannot be edited\n");
- ret = -EPERM;
- goto out_mutex;
+ ret = cs40l26_get_num_waves(cs40l26, &nwaves);
+ if (ret)
+ return ret;
+
+ switch (bank) {
+ case CS40L26_RAM_BANK_ID:
+ if (nwaves - cs40l26->num_owt_effects == 0) {
+ dev_err(dev, "No waveforms in RAM bank\n");
+ return -EINVAL;
+ }
+
+ min_index = CS40L26_RAM_INDEX_START;
+ max_index = min_index + nwaves - cs40l26->num_owt_effects - 1;
+ break;
+ case CS40L26_ROM_BANK_ID:
+ min_index = CS40L26_ROM_INDEX_START;
+ max_index = CS40L26_ROM_INDEX_END;
+ break;
+ case CS40L26_OWT_BANK_ID:
+ min_index = CS40L26_OWT_INDEX_START;
+ max_index = CS40L26_OWT_INDEX_END;
+ break;
+ default:
+ dev_err(dev, "Bank ID (%u) invalid\n", bank);
+ return -EINVAL;
}
- switch (effect->u.periodic.waveform) {
- case FF_CUSTOM:
- pwle = (cs40l26->raw_custom_data[0] ==
- CS40L26_WT_TYPE10_COMP_BUFFER) ? false : true;
- svc_waveform = (cs40l26->raw_custom_data[0] ==
- CS40L26_SVC_ID) ? true : false;
-
- len = effect->u.periodic.custom_len;
-
- if (len > CS40L26_CUSTOM_DATA_SIZE) {
- refactored_size = cs40l26_refactor_owt(cs40l26,
- cs40l26->raw_custom_data, len,
- pwle, svc_waveform,
- &refactored_data);
-
- if (refactored_size <= 0) {
- dev_err(cdev,
- "Failed to refactor OWT waveform\n");
- ret = refactored_size;
- goto out_mutex;
- }
+ trigger_index = index + min_index;
+ if (trigger_index < min_index || trigger_index > max_index) {
+ dev_err(dev, "Index 0x%X out of bounds (0x%X - 0x%X)\n",
+ trigger_index, min_index, max_index);
+ return -EINVAL;
+ }
+ dev_dbg(dev, "ID = %d, trigger index = 0x%08X\n", effect->id,
+ trigger_index);
- ret = cs40l26_owt_upload(cs40l26, refactored_data,
- refactored_size);
- if (ret)
- goto out_free;
+ if (bank == CS40L26_OWT_BANK_ID)
+ cs40l26->num_owt_effects++;
- bank = CS40L26_OWT_BANK_ID;
- index = cs40l26->num_owt_effects;
- } else {
- bank = ((u16) cs40l26->raw_custom_data[0]);
- index = ((u16) cs40l26->raw_custom_data[1]) &
- CS40L26_MAX_INDEX_MASK;
- }
+ ueffect->id = effect->id;
+ ueffect->wvfrm_bank = bank;
+ ueffect->trigger_index = trigger_index;
+
+ return ret;
+}
+
+static int cs40l26_uploaded_effect_add(struct cs40l26_private *cs40l26,
+ struct ff_effect *effect)
+{
+ struct device *dev = cs40l26->dev;
+ bool is_new = false;
+ struct cs40l26_uploaded_effect *ueffect;
+ int ret;
+
+ ueffect = cs40l26_uploaded_effect_find(cs40l26, effect->id);
+ if (IS_ERR_OR_NULL(ueffect)) {
+ is_new = true;
+ ueffect = kzalloc(sizeof(*ueffect), GFP_KERNEL);
+ if (!ueffect)
+ return -ENOMEM;
+ }
+
+ if (effect->u.periodic.waveform == FF_CUSTOM) {
+ ret = cs40l26_custom_upload(cs40l26, effect, ueffect);
+ } else if (effect->u.periodic.waveform == FF_SINE) {
+ ret = cs40l26_sine_upload(cs40l26, effect, ueffect);
+ } else {
+ dev_err(dev, "Periodic waveform type 0x%X not supported\n",
+ effect->u.periodic.waveform);
+ ret = -EINVAL;
+ }
+
+ if (ret)
+ goto err_free;
- ret = cs40l26_get_num_waves(cs40l26, &nwaves);
+ if (effect->trigger.button) {
+ ret = cs40l26_map_gpi_to_haptic(cs40l26, effect, ueffect);
if (ret)
- goto out_free;
+ goto err_free;
+ } else {
+ ueffect->mapping = CS40L26_GPIO_MAP_INVALID;
+ }
- switch (bank) {
- case CS40L26_RAM_BANK_ID:
- if (nwaves - cs40l26->num_owt_effects == 0) {
- dev_err(cdev, "No waveforms in RAM bank\n");
- ret = -EINVAL;
- goto out_free;
- } else {
- min_index = CS40L26_RAM_INDEX_START;
- max_index = min_index + nwaves - 1 -
- cs40l26->num_owt_effects;
- }
- break;
- case CS40L26_ROM_BANK_ID:
- min_index = CS40L26_ROM_INDEX_START;
- max_index = CS40L26_ROM_INDEX_END;
- break;
- case CS40L26_OWT_BANK_ID:
- min_index = CS40L26_OWT_INDEX_START;
- max_index = CS40L26_OWT_INDEX_END;
- break;
- default:
- dev_err(cdev, "Bank ID (%u) out of bounds\n", bank);
- ret = -EINVAL;
- goto out_free;
- }
+ if (is_new)
+ list_add(&ueffect->list, &cs40l26->effect_head);
- trigger_index = index + min_index;
+ return 0;
+err_free:
+ if (is_new)
+ kfree(ueffect);
- if (trigger_index >= min_index && trigger_index <= max_index) {
- cs40l26->trigger_indices[effect->id] = trigger_index;
- } else {
- dev_err(cdev,
- "Index (0x%X) out of bounds (0x%X - 0x%X)\n",
- trigger_index, min_index, max_index);
- ret = -EINVAL;
- goto out_free;
- }
+ return ret;
+}
- if (is_owt(trigger_index)) {
- ret = cs40l26_owt_add(cs40l26, effect->id,
- trigger_index);
- if (ret)
- goto out_free;
- }
+static void cs40l26_upload_worker(struct work_struct *work)
+{
+ struct cs40l26_private *cs40l26 = container_of(work,
+ struct cs40l26_private, upload_work);
+ struct device *cdev = cs40l26->dev;
+ struct ff_effect *effect;
+ u32 nwaves;
+ int ret;
- dev_dbg(cdev, "%s: ID = %u, index = 0x%08X\n",
- __func__, effect->id, trigger_index);
+ ret = cs40l26_pm_enter(cdev);
+ if (ret)
+ return;
- if (effect->trigger.button) {
- ret = cs40l26_map_gpi_to_haptic(cs40l26,
- trigger_index, bank, effect);
- if (ret)
- goto out_free;
- }
+ mutex_lock(&cs40l26->lock);
- break;
- case FF_SINE:
- ret = cs40l26_sine_upload(cs40l26, effect);
- if (ret)
- goto out_mutex;
+ effect = &cs40l26->upload_effect;
- break;
- default:
- dev_err(cdev, "Periodic waveform type 0x%X not supported\n",
- effect->u.periodic.waveform);
+ if (effect->type != FF_PERIODIC) {
+ dev_err(cdev, "Effect type 0x%X not supported\n", effect->type);
ret = -EINVAL;
goto out_mutex;
}
+ ret = cs40l26_uploaded_effect_add(cs40l26, effect);
+ if (ret)
+ goto out_mutex;
+
ret = cs40l26_get_num_waves(cs40l26, &nwaves);
if (ret)
- goto out_free;
+ goto out_mutex;
dev_dbg(cdev, "Total number of waveforms = %u\n", nwaves);
-out_free:
- kfree(refactored_data);
-
out_mutex:
mutex_unlock(&cs40l26->lock);
@@ -2944,17 +2990,6 @@ const struct attribute_group *cs40l26_dev_attr_groups[] = {
};
#endif
-static enum cs40l26_gpio_map cs40l26_map_get(struct cs40l26_private *cs40l26,
- int effect_id)
-{
- if (cs40l26->gpi_ids[CS40L26_GPIO_MAP_A_PRESS] == effect_id)
- return CS40L26_GPIO_MAP_A_PRESS;
- else if (cs40l26->gpi_ids[CS40L26_GPIO_MAP_A_RELEASE] == effect_id)
- return CS40L26_GPIO_MAP_A_RELEASE;
-
- return CS40L26_GPIO_MAP_INVALID;
-}
-
static int cs40l26_erase_gpi_mapping(struct cs40l26_private *cs40l26,
enum cs40l26_gpio_map mapping)
{
@@ -2980,30 +3015,18 @@ static int cs40l26_erase_gpi_mapping(struct cs40l26_private *cs40l26,
return ret;
}
- cs40l26->gpi_ids[mapping] = -1;
-
- return 0;
-}
-
-static int cs40l26_erase_buzz(struct cs40l26_private *cs40l26, int effect_id)
-{
- cs40l26->trigger_indices[effect_id] = 0;
-
return 0;
}
-static int cs40l26_erase_owt(struct cs40l26_private *cs40l26, int effect_id)
+static int cs40l26_erase_owt(struct cs40l26_private *cs40l26,
+ struct cs40l26_uploaded_effect *ueffect)
{
u32 cmd = CS40L26_DSP_MBOX_CMD_OWT_DELETE_BASE;
- struct cs40l26_owt *owt, *owt_tmp;
- u32 index;
+ u32 index = ueffect->trigger_index;
+ struct cs40l26_uploaded_effect *ueffect_tmp;
int ret;
- owt = cs40l26_owt_find(cs40l26, effect_id);
- if (IS_ERR(owt))
- return PTR_ERR(owt);
-
- cmd |= (owt->trigger_index & 0xFF);
+ cmd |= (index & 0xFF);
ret = cs40l26_ack_write(cs40l26, CS40L26_DSP_VIRTUAL1_MBOX_1, cmd,
CS40L26_DSP_MBOX_RESET);
@@ -3011,18 +3034,12 @@ static int cs40l26_erase_owt(struct cs40l26_private *cs40l26, int effect_id)
return ret;
/* Update indices for OWT waveforms uploaded after erased effect */
- index = cs40l26->trigger_indices[effect_id];
- list_for_each_entry(owt_tmp, &cs40l26->owt_head, list) {
- if (owt_tmp->trigger_index > index) {
- owt_tmp->trigger_index--;
- cs40l26->trigger_indices[owt_tmp->effect_id]--;
- }
+ list_for_each_entry(ueffect_tmp, &cs40l26->effect_head, list) {
+ if (ueffect_tmp->wvfrm_bank == CS40L26_OWT_BANK_ID &&
+ ueffect_tmp->trigger_index > index)
+ ueffect_tmp->trigger_index--;
}
- cs40l26->trigger_indices[effect_id] = 0;
-
- list_del(&owt->list);
- kfree(owt);
cs40l26->num_owt_effects--;
return 0;
@@ -3032,11 +3049,9 @@ static void cs40l26_erase_worker(struct work_struct *work)
{
struct cs40l26_private *cs40l26 = container_of(work,
struct cs40l26_private, erase_work);
- int ret = 0;
- enum cs40l26_gpio_map mapping;
- int effect_id;
+ struct cs40l26_uploaded_effect *ueffect;
+ int effect_id, ret;
u16 duration;
- u32 index;
ret = cs40l26_pm_enter(cs40l26->dev);
if (ret)
@@ -3045,7 +3060,14 @@ static void cs40l26_erase_worker(struct work_struct *work)
mutex_lock(&cs40l26->lock);
effect_id = cs40l26->erase_effect->id;
- index = cs40l26->trigger_indices[effect_id];
+ ueffect = cs40l26_uploaded_effect_find(cs40l26, effect_id);
+ if (IS_ERR_OR_NULL(ueffect)) {
+ dev_err(cs40l26->dev, "No such effect to erase (%d)\n",
+ effect_id);
+ ret = PTR_ERR(ueffect);
+ goto out_mutex;
+ }
+
duration = (cs40l26->erase_effect->replay.length == 0) ?
CS40L26_MAX_WAIT_VIBE_COMPLETE_MS :
cs40l26->erase_effect->replay.length + CS40L26_ERASE_BUFFER_MS;
@@ -3057,8 +3079,8 @@ static void cs40l26_erase_worker(struct work_struct *work)
if (!wait_for_completion_timeout(&cs40l26->erase_cont,
msecs_to_jiffies(duration))) {
ret = -ETIME;
- dev_err(cs40l26->dev, "Failed to erase effect: %d",
- ret);
+ dev_err(cs40l26->dev, "Failed to erase effect (%d)\n",
+ effect_id);
goto pm_err;
}
mutex_lock(&cs40l26->lock);
@@ -3066,20 +3088,23 @@ static void cs40l26_erase_worker(struct work_struct *work)
dev_dbg(cs40l26->dev, "%s: effect ID = %d\n", __func__, effect_id);
- mapping = cs40l26_map_get(cs40l26, effect_id);
- if (mapping != CS40L26_GPIO_MAP_INVALID) {
- ret = cs40l26_erase_gpi_mapping(cs40l26, mapping);
+ if (ueffect->mapping != CS40L26_GPIO_MAP_INVALID) {
+ ret = cs40l26_erase_gpi_mapping(cs40l26, ueffect->mapping);
if (ret)
goto out_mutex;
+ ueffect->mapping = CS40L26_GPIO_MAP_INVALID;
}
- if (is_owt(index))
- ret = cs40l26_erase_owt(cs40l26, effect_id);
- else if (is_buzz(index))
- ret = cs40l26_erase_buzz(cs40l26, effect_id);
+ if (ueffect->wvfrm_bank == CS40L26_OWT_BANK_ID)
+ ret = cs40l26_erase_owt(cs40l26, ueffect);
- if (ret)
+ if (ret) {
dev_err(cs40l26->dev, "Failed to erase effect: %d", ret);
+ goto out_mutex;
+ }
+
+ list_del(&ueffect->list);
+ kfree(ueffect);
out_mutex:
mutex_unlock(&cs40l26->lock);
@@ -3184,6 +3209,10 @@ static int cs40l26_input_init(struct cs40l26_private *cs40l26)
}
#endif
+ #ifdef CONFIG_DEBUG_FS
+ cs40l26_debugfs_init(cs40l26);
+ #endif
+
cs40l26->vibe_init_success = true;
return ret;
@@ -3755,37 +3784,6 @@ static int cs40l26_bst_ipk_config(struct cs40l26_private *cs40l26)
BIT(CS40L26_IRQ1_BST_IPK_FLAG));
}
-static int cs40l26_owt_setup(struct cs40l26_private *cs40l26)
-{
- u32 reg, offset, base;
- int ret;
-
- INIT_LIST_HEAD(&cs40l26->owt_head);
- cs40l26->num_owt_effects = 0;
-
- ret = cl_dsp_get_reg(cs40l26->dsp, CS40L26_WT_NAME_XM,
- CL_DSP_XM_UNPACKED_TYPE, CS40L26_VIBEGEN_ALGO_ID, &base);
- if (ret)
- return ret;
-
- ret = cl_dsp_get_reg(cs40l26->dsp, "OWT_NEXT_XM",
- CL_DSP_XM_UNPACKED_TYPE, CS40L26_VIBEGEN_ALGO_ID, &reg);
- if (ret)
- return ret;
-
- ret = regmap_read(cs40l26->regmap, reg, &offset);
- if (ret) {
- dev_err(cs40l26->dev, "Failed to get wavetable offset\n");
- return ret;
- }
-
- ret = regmap_write(cs40l26->regmap, reg, 0xFFFFFF);
- if (ret)
- dev_err(cs40l26->dev, "Failed to write OWT terminator\n");
-
- return ret;
-}
-
static int cs40l26_lbst_short_test(struct cs40l26_private *cs40l26)
{
struct regmap *regmap = cs40l26->regmap;
@@ -4055,9 +4053,7 @@ static int cs40l26_dsp_config(struct cs40l26_private *cs40l26)
dev_info(dev, "%s loaded with %u RAM waveforms\n", CS40L26_DEV_NAME,
nwaves);
- ret = cs40l26_owt_setup(cs40l26);
- if (ret)
- goto pm_err;
+ cs40l26->num_owt_effects = 0;
value = (cs40l26->comp_enable_redc << CS40L26_COMP_EN_REDC_SHIFT) |
(cs40l26->comp_enable_f0 << CS40L26_COMP_EN_F0_SHIFT);
@@ -4165,64 +4161,70 @@ static int cs40l26_tuning_select_from_svc_le(struct cs40l26_private *cs40l26,
return ret;
}
-static char **cs40l26_get_tuning_names(struct cs40l26_private *cs40l26, int n,
- u32 tuning)
+static char **cs40l26_get_tuning_names(struct cs40l26_private *cs40l26,
+ int *actual_num_files, u32 tuning)
{
- char svc_tuning[CS40L26_TUNING_FILE_NAME_MAX_LEN];
- char wt_tuning[CS40L26_TUNING_FILE_NAME_MAX_LEN];
- char **coeff_files, tuning_str[2];
- int i;
+ char **coeff_files;
+ int i, file_count = 0;
- coeff_files = kcalloc(n, sizeof(char *), GFP_KERNEL);
+ coeff_files = kcalloc(
+ CS40L26_MAX_TUNING_FILES, sizeof(char *), GFP_KERNEL);
if (!coeff_files)
return ERR_PTR(-ENOMEM);
- for (i = 0; i < n; i++) {
+ for (i = 0; i < CS40L26_MAX_TUNING_FILES; i++) {
coeff_files[i] =
kzalloc(CS40L26_TUNING_FILE_NAME_MAX_LEN, GFP_KERNEL);
if (!coeff_files[i])
goto err_free;
}
- strscpy(svc_tuning, CS40L26_SVC_TUNING_FILE_PREFIX,
- CS40L26_TUNING_FILE_NAME_MAX_LEN);
-
- if (tuning) { /* Concatenate tuning number if required */
- strscpy(wt_tuning, CS40L26_WT_FILE_PREFIX,
+ if (tuning) {
+ snprintf(coeff_files[file_count++],
+ CS40L26_TUNING_FILE_NAME_MAX_LEN, "%s%d%s",
+ CS40L26_WT_FILE_PREFIX, tuning,
+ CS40L26_TUNING_FILE_SUFFIX);
+ } else {
+ strscpy(coeff_files[file_count++],
+ CS40L26_WT_FILE_NAME,
CS40L26_TUNING_FILE_NAME_MAX_LEN);
+ }
- snprintf(tuning_str, 2, "%d", tuning);
-
- strncat(wt_tuning, tuning_str, 2);
- strncat(svc_tuning, tuning_str, 2);
-
- strncat(wt_tuning, CS40L26_TUNING_FILE_SUFFIX,
- CS40L26_TUNING_FILE_NAME_MAX_LEN);
+ if (tuning) {
+ snprintf(coeff_files[file_count++],
+ CS40L26_TUNING_FILE_NAME_MAX_LEN, "%s%d%s",
+ CS40L26_SVC_TUNING_FILE_PREFIX, tuning,
+ CS40L26_TUNING_FILE_SUFFIX);
} else {
- strscpy(wt_tuning, CS40L26_WT_FILE_NAME,
- CS40L26_TUNING_FILE_NAME_MAX_LEN);
+ strscpy(coeff_files[file_count++],
+ CS40L26_SVC_TUNING_FILE_NAME,
+ CS40L26_TUNING_FILE_NAME_MAX_LEN);
}
- strncat(svc_tuning, CS40L26_TUNING_FILE_SUFFIX,
+ if (cl_dsp_algo_is_present(cs40l26->dsp, CS40L26_DVL_ALGO_ID))
+ strscpy(coeff_files[file_count++],
+ CS40L26_DVL_FILE_NAME,
CS40L26_TUNING_FILE_NAME_MAX_LEN);
- strscpy(coeff_files[0], wt_tuning, CS40L26_TUNING_FILE_NAME_MAX_LEN);
- strscpy(coeff_files[1], svc_tuning, CS40L26_TUNING_FILE_NAME_MAX_LEN);
-
if (cs40l26->fw_id == CS40L26_FW_ID) {
- strscpy(coeff_files[2], CS40L26_A2H_TUNING_FILE_NAME,
- CS40L26_TUNING_FILE_NAME_MAX_LEN);
- strscpy(coeff_files[3], CS40L26_DVL_FILE_NAME,
- CS40L26_TUNING_FILE_NAME_MAX_LEN);
+ if (cl_dsp_algo_is_present(cs40l26->dsp, CS40L26_A2H_ALGO_ID))
+ strscpy(coeff_files[file_count++],
+ CS40L26_A2H_TUNING_FILE_NAME,
+ CS40L26_TUNING_FILE_NAME_MAX_LEN);
} else {
- strscpy(coeff_files[2], CS40L26_CALIB_BIN_FILE_NAME,
+ strscpy(coeff_files[file_count++],
+ CS40L26_CALIB_BIN_FILE_NAME,
CS40L26_TUNING_FILE_NAME_MAX_LEN);
}
+ *actual_num_files = file_count;
return coeff_files;
err_free:
+ for (; i >= 0; i--)
+ kfree(coeff_files[i]);
kfree(coeff_files);
+ *actual_num_files = 0;
return ERR_PTR(-ENOMEM);
}
@@ -4230,17 +4232,15 @@ static int cs40l26_coeff_load(struct cs40l26_private *cs40l26, u32 tuning)
{
struct device *dev = cs40l26->dev;
const struct firmware *coeff;
- int i, ret, num_files;
+ int i, ret, num_files_to_load;
char **coeff_files;
- num_files = (cs40l26->fw_id == CS40L26_FW_ID) ?
- CS40L26_TUNING_FILES_RUNTIME : CS40L26_TUNING_FILES_CAL;
-
- coeff_files = cs40l26_get_tuning_names(cs40l26, num_files, tuning);
+ coeff_files = cs40l26_get_tuning_names(
+ cs40l26, &num_files_to_load, tuning);
if (IS_ERR(coeff_files))
return PTR_ERR(coeff_files);
- for (i = 0; i < num_files; i++) {
+ for (i = 0; i < num_files_to_load; i++) {
ret = request_firmware(&coeff, coeff_files[i], dev);
if (ret) {
dev_warn(dev, "Continuing...\n");
@@ -4606,6 +4606,40 @@ err:
return ret;
}
+static int cs40l26_no_wait_ram_indices_get(struct cs40l26_private *cs40l26,
+ struct device_node *np)
+{
+ int ret, i;
+
+ cs40l26->num_no_wait_ram_indices = of_property_count_u32_elems(np,
+ "cirrus,no-wait-ram-indices");
+
+ if (cs40l26->num_no_wait_ram_indices <= 0)
+ return 0;
+
+ cs40l26->no_wait_ram_indices = devm_kcalloc(cs40l26->dev,
+ cs40l26->num_no_wait_ram_indices, sizeof(u32),
+ GFP_KERNEL);
+ if (!cs40l26->no_wait_ram_indices)
+ return -ENOMEM;
+
+ ret = of_property_read_u32_array(np, "cirrus,no-wait-ram-indices",
+ cs40l26->no_wait_ram_indices,
+ cs40l26->num_no_wait_ram_indices);
+ if (ret)
+ goto err_free;
+
+ for (i = 0; i < cs40l26->num_no_wait_ram_indices; i++)
+ cs40l26->no_wait_ram_indices[i] += CS40L26_RAM_INDEX_START;
+
+ return 0;
+
+err_free:
+ devm_kfree(cs40l26->dev, cs40l26->no_wait_ram_indices);
+ cs40l26->num_no_wait_ram_indices = 0;
+ return ret;
+}
+
static int cs40l26_handle_platform_data(struct cs40l26_private *cs40l26)
{
struct device *dev = cs40l26->dev;
@@ -4784,7 +4818,7 @@ static int cs40l26_handle_platform_data(struct cs40l26_private *cs40l26)
else
cs40l26->pdata.pwle_zero_cross = false;
- return 0;
+ return cs40l26_no_wait_ram_indices_get(cs40l26, np);
}
int cs40l26_probe(struct cs40l26_private *cs40l26,
@@ -4889,6 +4923,9 @@ int cs40l26_probe(struct cs40l26_private *cs40l26,
init_completion(&cs40l26->i2s_cont);
init_completion(&cs40l26->erase_cont);
+ init_completion(&cs40l26->cal_f0_cont);
+ init_completion(&cs40l26->cal_redc_cont);
+ init_completion(&cs40l26->cal_dvl_peq_cont);
if (!cs40l26->fw_defer) {
ret = cs40l26_fw_upload(cs40l26);
@@ -4908,6 +4945,8 @@ int cs40l26_probe(struct cs40l26_private *cs40l26,
if (ret)
goto err;
+ INIT_LIST_HEAD(&cs40l26->effect_head);
+
ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_AUTO, cs40l26_devs,
CS40L26_NUM_MFD_DEVS, NULL, 0, NULL);
if (ret) {
@@ -4968,6 +5007,10 @@ int cs40l26_remove(struct cs40l26_private *cs40l26)
#endif
}
+ #ifdef CONFIG_DEBUG_FS
+ cs40l26_debugfs_cleanup(cs40l26);
+ #endif
+
if (cs40l26->input)
input_unregister_device(cs40l26->input);