diff options
author | Tai Kuo <taikuo@google.com> | 2021-11-26 14:11:12 +0800 |
---|---|---|
committer | Tai Kuo <taikuo@google.com> | 2022-02-08 11:34:35 +0800 |
commit | 9a0148defd9807369323af67c0fba52d5cff2d15 (patch) | |
tree | d4781c90a50a63b74f042fbc33a6206d295ef3a9 | |
parent | d1ea3b1e371f0d929aeea176dd409cd1e5f644c7 (diff) | |
download | amplifiers-9a0148defd9807369323af67c0fba52d5cff2d15.tar.gz |
cs40l26: merge CirrusLogic dsp v3.1.6 and cs40l26 v3.4.0
Branch: v5.10-cirrus-dsp-fw
Tag: cl-dsp-fw-v3.1.6_5.10
Files:
drivers/firmware/cirrus/cl_dsp.c
include/linux/firmware/cirrus/cl_dsp.h
Features:
- cl-dsp-fw-v3.1.4_5.10
Add memchunk flush
Bug fixes:
- cl-dsp-fw-v3.1.6_5.10
Fix syntax error caught by checkpatch.pl
- cl_dsp-v3.1.5_5.10
Fix bug that allowed for a potential NULL pointer dereference
- cl-dsp-fw-v3.1.4_5.10
Add empty check for get dsp register.
Commits: 9ee092c firmware: cirrus: Fix code syntax error
16af046 firmware: cirrus: Avoid potential NULL dereference
f916a30 firmware: cirrus: Add cl_dsp_memchunk_flush()
4cf0400 firmware: cirrus: Add empty check in dsp_get_reg
30034b6 firmware: cirrus: arm64 compiler fix
Change-Id: I2efbf1611bad281b67fc035b31b30925e869082b
---
Branch: v5.10-cs40l26
Tag: cs40l26-v3.4.0_5.10
Files:
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
Features:
- cs40l26-v3.4.0_5.10
Improved information from DSP to host mailbox transactions
Additional debug logging
- cs40l26-v3.3.0_5.10
Expose LRADELAYSAMPS to user
Support for DVL tuning
Contingency for DSP start failure on boot
- cs40l26-v3.1.2_5.10
Add Support for Nested Composite Waveform
Improve BUZZGEN GPI trigger support
Support GPI triggers of OWT waveforms
Fix NUM_OF_WAVES race condition
Improve deferred firmware loading
DT parameter to set idle -> hibernate DSP timeout
Bug fixes:
- cs40l26-v3.4.0_5.10
Updated handling of vibe_state
Explicitly stop haptic playback before starting I2S stream
Prevent asynchrnous I2S start message to mailbox from driver
Improved code clarity
Resolution of clang, cppcheck, sparse, and checkpatch warnings
- cs40l26-v3.3.0_5.10
Prevent asynchrnous erase/upload calls
Prevent asynchronous interrupts from affecting code under mutex lock
Code improvements to write sequencer
Static analysis fixes
Memory leak correction
- cs40l26-v3.1.2_5.10
Check for the DSP to be initialized before uploading a waveform
Do not allow stop command only if the ASP is active.
Commits:
ba22584 input: cs40l26: Resolve Warnings caused by DEVICE_ATTR
b1f116d input: cs40l26: Clarify read size in pseq_init()
3447c80 ASoC: cs40l26: Use new vibe_state handling
a3e37ec input: cs40l26: Update handling of vibe_state
8ec6127 ASoC: cs40l26: remove old vibe_state handling
bc9f10c input: cs40l26: Add debug message in erase_effect
dd88832 input: cs40l26: Support new FW event notifiers
4a963ec input: cs40l26: Add support for FW branch
fead3ba input: cs40l26: Stop playback before starting I2S
0827b6c ASoC: cs40l26: Always unlock mutex in cs40l26_pcm_ev
4753f8c input: cs40l26: Resolve clang warnings
e7f25c9 input: cs40l26: Resolve cppcheck warnings
5fcb292 input: cs40l26: Ensure vibe_state_set() called under mutex lock
75ad45a input: cs40l26: Additional defines and logging
6da8378 ASoC: cs40l26: Use asp_start() to start I2S stream
a14e17b input: cs40l26: Replace asp_workqueue with asp_start function
7191239 ASoC: cs40l26: Remove usage of asp_work
5a32ccf input: cs40l26: Don't remove OWT effect from list if delete fails
c7272dc ASoC: cs40l26: Expose delay controls
dd4491a input: cs40l26: Load DVL tuning file
4b84df2 input: cs40l26: Use devm_ for allocation
7163487 input: cs40l26: Set up parameters before dsp start
31b55dd input: cs40l26: Retry if DSP is not in run state
626f83c input: cs40l26: Set timeout to max on fw re-load
f33edc6 ASoC: cs40l26: Fix potential memory leak
aebbb26 input: cs40l26: Fix static analysis errors
7a5489e input: cs40l26: Adding debug prints
ff55e98 input: cs40l26: Add erase and upload operations to ordered workqueue
9c9da81 input: cs40l26: Mutex protect interrupt handler
332bb17 input: cs40l26: Write sequencer updates
c5be6bb input: cs40l26: No stop command during ASP
7e806e4 input: cs40l26: use type-specific min function
f93a999 input: cs40l26: Check for uninitialised DSP in cs40l26_header
c659dec input: cs40l26: Changing GPI map API
f12a21b input: cs40l26: Add delay before stopping playback
3732052 input: cs40l26: Delay registration of IRQ until firmware is loaded
6de4136 input: cs40l26: Add fw load defer
7d5e01b input: cs40l26: Correct memory space calculation for 4-byte words
e8123a3 input: cs40l26: Add Support for Nested Composite Waveforms
aa472ba input: cs40l26: Read NUM_OF_WAVES before each trigger
98ff3b5 input: cs40l26: Add dt parameter for DSP transition
Bug: 205688153
Test: N/A.
Signed-off-by: Tai Kuo <taikuo@google.com>
Change-Id: I606c7d75891f85995eadf367d4b833b682f8e23d
-rw-r--r-- | cs40l26/cl_dsp.c | 41 | ||||
-rw-r--r-- | cs40l26/cl_dsp.h | 1 | ||||
-rw-r--r-- | cs40l26/cs40l26-codec.c | 88 | ||||
-rw-r--r-- | cs40l26/cs40l26-sysfs.c | 263 | ||||
-rw-r--r-- | cs40l26/cs40l26.c | 1600 | ||||
-rw-r--r-- | cs40l26/cs40l26.h | 102 |
6 files changed, 1507 insertions, 588 deletions
diff --git a/cs40l26/cl_dsp.c b/cs40l26/cl_dsp.c index 12e7a2c..db0471e 100644 --- a/cs40l26/cl_dsp.c +++ b/cs40l26/cl_dsp.c @@ -92,6 +92,15 @@ int cl_dsp_memchunk_write(struct cl_dsp_memchunk *ch, int nbits, u32 val) } EXPORT_SYMBOL(cl_dsp_memchunk_write); +int cl_dsp_memchunk_flush(struct cl_dsp_memchunk *ch) +{ + if (!ch->cachebits) + return 0; + + return cl_dsp_memchunk_write(ch, 24 - ch->cachebits, 0); +} +EXPORT_SYMBOL(cl_dsp_memchunk_flush); + int cl_dsp_raw_write(struct cl_dsp *dsp, unsigned int reg, const void *val, size_t val_len, size_t limit) { @@ -120,9 +129,12 @@ int cl_dsp_get_reg(struct cl_dsp *dsp, const char *coeff_name, struct cl_dsp_coeff_desc *coeff_desc; unsigned int mem_region_prefix; - if (!dsp) + if (!dsp) return -EPERM; + if (list_empty(&dsp->coeff_desc_head)) + return -ENOENT; + list_for_each_entry(coeff_desc, &dsp->coeff_desc_head, list) { if (strncmp(coeff_desc->name, coeff_name, CL_DSP_COEFF_NAME_LEN_MAX)) @@ -235,8 +247,8 @@ static int cl_dsp_read_wt(struct cl_dsp *dsp, int pos, int size) if (entry->type == WT_TYPE_TERMINATOR) { dsp->wt_desc->owt.nwaves = i; - dsp->wt_desc->owt.bytes = max((long)ch.bytes, - (void *)max - buf); + dsp->wt_desc->owt.bytes = max(ch.bytes, + (int)((void *)max - buf)); return dsp->wt_desc->owt.bytes; } @@ -320,7 +332,6 @@ static void cl_dsp_coeff_handle_info_text(struct cl_dsp *dsp, const u8 *data, int cl_dsp_coeff_file_parse(struct cl_dsp *dsp, const struct firmware *fw) { unsigned int pos = CL_DSP_COEFF_FILE_HEADER_SIZE; - struct device *dev = dsp->dev; bool wt_found = false; int ret = -EINVAL; struct cl_dsp_coeff_data_block data_block; @@ -328,11 +339,14 @@ int cl_dsp_coeff_file_parse(struct cl_dsp *dsp, const struct firmware *fw) char wt_date[CL_DSP_WMDR_DATE_LEN]; unsigned int reg, wt_reg, algo_rev; u16 algo_id, parent_id; + struct device *dev; int i; - if (!dsp) + if (!dsp) return -EPERM; + dev = dsp->dev; + *wt_date = '\0'; memcpy(wmdr_header.data, fw->data, CL_DSP_COEFF_FILE_HEADER_SIZE); @@ -734,16 +748,19 @@ EXPORT_SYMBOL(cl_dsp_fw_rev_get); static int cl_dsp_coeff_init(struct cl_dsp *dsp) { - struct regmap *regmap = dsp->regmap; - struct device *dev = dsp->dev; - struct cl_dsp_coeff_desc *coeff_desc; unsigned int reg = CL_DSP_HALO_XM_FW_ID_REG; + struct cl_dsp_coeff_desc *coeff_desc; + struct regmap *regmap; + struct device *dev; unsigned int val; int ret, i; - if (!dsp) + if (!dsp) return -EPERM; + dev = dsp->dev; + regmap = dsp->regmap; + ret = regmap_read(regmap, CL_DSP_HALO_NUM_ALGOS_REG, &val); if (ret) { dev_err(dev, "Failed to read number of algorithms\n"); @@ -879,7 +896,7 @@ static void cl_dsp_coeff_free(struct cl_dsp *dsp) { struct cl_dsp_coeff_desc *coeff_desc; - if (!dsp) + if (!dsp) return; while (!list_empty(&dsp->coeff_desc_head)) { @@ -949,15 +966,17 @@ static void cl_dsp_handle_info_text(struct cl_dsp *dsp, int cl_dsp_firmware_parse(struct cl_dsp *dsp, const struct firmware *fw, bool write_fw) { - struct device *dev = dsp->dev; unsigned int pos = CL_DSP_FW_FILE_HEADER_SIZE, reg = 0; struct cl_dsp_data_block data_block; union cl_dsp_wmfw_header wmfw_header; + struct device *dev; int ret; if (!dsp) return -EPERM; + dev = dsp->dev; + memcpy(wmfw_header.data, fw->data, CL_DSP_FW_FILE_HEADER_SIZE); ret = cl_dsp_header_parse(dsp, wmfw_header); diff --git a/cs40l26/cl_dsp.h b/cs40l26/cl_dsp.h index 9ec931b..7c85e27 100644 --- a/cs40l26/cl_dsp.h +++ b/cs40l26/cl_dsp.h @@ -316,6 +316,7 @@ int cl_dsp_get_reg(struct cl_dsp *dsp, const char *coeff_name, struct cl_dsp_memchunk cl_dsp_memchunk_create(void *data, int size); int cl_dsp_memchunk_write(struct cl_dsp_memchunk *ch, int nbits, u32 val); int cl_dsp_memchunk_read(struct cl_dsp_memchunk *ch, int nbits); +int cl_dsp_memchunk_flush(struct cl_dsp_memchunk *ch); int cl_dsp_raw_write(struct cl_dsp *dsp, unsigned int reg, const void *val, size_t val_len, size_t limit); int cl_dsp_fw_id_get(struct cl_dsp *dsp, unsigned int *id); diff --git a/cs40l26/cs40l26-codec.c b/cs40l26/cs40l26-codec.c index d6def2f..001e39e 100644 --- a/cs40l26/cs40l26-codec.c +++ b/cs40l26/cs40l26-codec.c @@ -101,9 +101,12 @@ static int cs40l26_clk_en(struct snd_soc_dapm_widget *w, switch (event) { case SND_SOC_DAPM_POST_PMU: mutex_lock(&cs40l26->lock); - cs40l26->asp_enable = true; - cs40l26_vibe_state_set(cs40l26, CS40L26_VIBE_STATE_ASP); + cs40l26_vibe_state_update(cs40l26, + CS40L26_VIBE_STATE_EVENT_ASP_START); + ret = cs40l26_asp_start(cs40l26); mutex_unlock(&cs40l26->lock); + if (ret) + return ret; ret = cs40l26_swap_ext_clk(codec, CS40L26_PLL_REFCLK_BCLK); if (ret) @@ -115,8 +118,8 @@ static int cs40l26_clk_en(struct snd_soc_dapm_widget *w, return ret; mutex_lock(&cs40l26->lock); - cs40l26->asp_enable = false; - cs40l26_vibe_state_set(cs40l26, CS40L26_VIBE_STATE_STOPPED); + cs40l26_vibe_state_update(cs40l26, + CS40L26_VIBE_STATE_EVENT_ASP_STOP); mutex_unlock(&cs40l26->lock); break; @@ -162,10 +165,11 @@ static int cs40l26_a2h_ev(struct snd_soc_dapm_widget *w, } ret = cl_dsp_coeff_file_parse(cs40l26->dsp, fw); + release_firmware(fw); if (ret) return ret; + codec->tuning_prev = codec->tuning; - release_firmware(fw); ret = cs40l26_ack_write(cs40l26, CS40L26_DSP_VIRTUAL1_MBOX_1, @@ -245,7 +249,7 @@ static int cs40l26_pcm_ev(struct snd_soc_dapm_widget *w, ret = cl_dsp_get_reg(cs40l26->dsp, "SOURCE_INVERT", CL_DSP_XM_UNPACKED_TYPE, CS40L26_EXT_ALGO_ID, ®); if (ret) - return ret; + goto err_mutex; ret = regmap_write(regmap, reg, codec->invert_streaming_data); if (ret) { @@ -253,8 +257,6 @@ static int cs40l26_pcm_ev(struct snd_soc_dapm_widget *w, goto err_mutex; } - queue_work(cs40l26->asp_workqueue, &cs40l26->asp_work); - break; case SND_SOC_DAPM_PRE_PMD: ret = cs40l26_ack_write(cs40l26, CS40L26_DSP_VIRTUAL1_MBOX_1, @@ -561,6 +563,74 @@ static int cs40l26_slots_put( return 0; } +static int cs40l26_a2h_delay_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct cs40l26_codec *codec = + snd_soc_component_get_drvdata(snd_soc_kcontrol_component(kcontrol)); + struct cs40l26_private *cs40l26 = codec->core; + struct regmap *regmap = cs40l26->regmap; + struct device *dev = cs40l26->dev; + unsigned int val = 0, reg; + int ret; + + ret = cl_dsp_get_reg(cs40l26->dsp, "LRADELAYSAMPS", + CL_DSP_XM_UNPACKED_TYPE, CS40L26_A2H_ALGO_ID, ®); + if (ret) + return ret; + + pm_runtime_get_sync(dev); + + ret = regmap_read(regmap, reg, &val); + if (ret) { + dev_err(dev, "Failed to get LRADELAYSAMPS\n"); + goto err; + } + + ucontrol->value.integer.value[0] = val; + +err: + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + + return ret; +} + +static int cs40l26_a2h_delay_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct cs40l26_codec *codec = + snd_soc_component_get_drvdata(snd_soc_kcontrol_component(kcontrol)); + struct cs40l26_private *cs40l26 = codec->core; + struct regmap *regmap = cs40l26->regmap; + struct device *dev = cs40l26->dev; + unsigned int val = 0, reg; + int ret; + + ret = cl_dsp_get_reg(cs40l26->dsp, "LRADELAYSAMPS", + CL_DSP_XM_UNPACKED_TYPE, CS40L26_A2H_ALGO_ID, ®); + if (ret) + return ret; + + if (ucontrol->value.integer.value[0] > CS40L26_A2H_DELAY_MAX) + val = CS40L26_A2H_DELAY_MAX; + else if (ucontrol->value.integer.value[0] < 0) + val = 0; + else + val = ucontrol->value.integer.value[0]; + + pm_runtime_get_sync(dev); + + ret = regmap_write(regmap, reg, val); + if (ret) + dev_err(dev, "Failed to set LRADELAYSAMPS\n"); + + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + + return ret; +} + static const struct snd_kcontrol_new cs40l26_controls[] = { SOC_SINGLE_EXT("A2H Tuning", 0, 0, CS40L26_A2H_MAX_TUNINGS, 0, cs40l26_tuning_get, cs40l26_tuning_put), @@ -576,6 +646,8 @@ static const struct snd_kcontrol_new cs40l26_controls[] = { cs40l26_i2s_vmon_get, NULL), SOC_SINGLE_EXT("DSP Bypass", 0, 0, 1, 0, cs40l26_bypass_get, cs40l26_bypass_put), + SOC_SINGLE_EXT("A2H Delay", 0, 0, CS40L26_A2H_DELAY_MAX, 0, + cs40l26_a2h_delay_get, cs40l26_a2h_delay_put), SOC_DOUBLE_EXT("RX Slots", 0, 0, 1, 63, 0, cs40l26_slots_get, cs40l26_slots_put), }; diff --git a/cs40l26/cs40l26-sysfs.c b/cs40l26/cs40l26-sysfs.c index 38dadf6..c1c2e34 100644 --- a/cs40l26/cs40l26-sysfs.c +++ b/cs40l26/cs40l26-sysfs.c @@ -13,11 +13,10 @@ #include "cs40l26.h" -static ssize_t cs40l26_dsp_state_show(struct device *dev, +static ssize_t dsp_state_show(struct device *dev, struct device_attribute *attr, char *buf) { struct cs40l26_private *cs40l26 = dev_get_drvdata(dev); - char str[CS40L26_DSP_STATE_STR_LEN]; u8 dsp_state; int ret; @@ -27,32 +26,15 @@ static ssize_t cs40l26_dsp_state_show(struct device *dev, if (ret) return ret; - switch (dsp_state) { - case CS40L26_DSP_STATE_HIBERNATE: - strncpy(str, "Hibernate", CS40L26_DSP_STATE_STR_LEN); - break; - case CS40L26_DSP_STATE_SHUTDOWN: - strncpy(str, "Shutdown", CS40L26_DSP_STATE_STR_LEN); - break; - case CS40L26_DSP_STATE_STANDBY: - strncpy(str, "Standby", CS40L26_DSP_STATE_STR_LEN); - break; - case CS40L26_DSP_STATE_ACTIVE: - strncpy(str, "Active", CS40L26_DSP_STATE_STR_LEN); - break; - default: - dev_err(cs40l26->dev, "DSP state %u is invalid\n", dsp_state); - return -EINVAL; - } - pm_runtime_mark_last_busy(cs40l26->dev); pm_runtime_put_autosuspend(cs40l26->dev); - return snprintf(buf, PAGE_SIZE, "DSP state: %s\n", str); + return snprintf(buf, PAGE_SIZE, "%u\n", + (unsigned int) (dsp_state & 0xFF)); } -static DEVICE_ATTR(dsp_state, 0660, cs40l26_dsp_state_show, NULL); +static DEVICE_ATTR_RO(dsp_state); -static ssize_t cs40l26_halo_heartbeat_show(struct device *dev, +static ssize_t halo_heartbeat_show(struct device *dev, struct device_attribute *attr, char *buf) { struct cs40l26_private *cs40l26 = dev_get_drvdata(dev); @@ -75,31 +57,36 @@ static ssize_t cs40l26_halo_heartbeat_show(struct device *dev, return snprintf(buf, PAGE_SIZE, "%d\n", halo_heartbeat); } -static DEVICE_ATTR(halo_heartbeat, 0660, cs40l26_halo_heartbeat_show, NULL); +static DEVICE_ATTR_RO(halo_heartbeat); -static ssize_t cs40l26_fw_mode_show(struct device *dev, +static ssize_t fw_mode_show(struct device *dev, struct device_attribute *attr, char *buf) { struct cs40l26_private *cs40l26 = dev_get_drvdata(dev); + int ret = 0; + unsigned int mode; - pm_runtime_get_sync(cs40l26->dev); + mutex_lock(&cs40l26->lock); if (cs40l26->fw_mode != CS40L26_FW_MODE_ROM && cs40l26->fw_mode != CS40L26_FW_MODE_RAM) { dev_err(cs40l26->dev, "Invalid firmware mode: %u\n", cs40l26->fw_mode); - return -EINVAL; + ret = -EINVAL; + } else { + mode = cs40l26->fw_mode; } - pm_runtime_mark_last_busy(cs40l26->dev); - pm_runtime_put_autosuspend(cs40l26->dev); + mutex_unlock(&cs40l26->lock); - return snprintf(buf, PAGE_SIZE, "Firmware is in %s mode\n", - cs40l26->fw_mode == CS40L26_FW_MODE_ROM ? "ROM" : "RAM"); + if (ret) + return ret; + + return snprintf(buf, PAGE_SIZE, "%u\n", mode); } -static DEVICE_ATTR(fw_mode, 0660, cs40l26_fw_mode_show, NULL); +static DEVICE_ATTR_RO(fw_mode); -static ssize_t cs40l26_pm_timeout_ms_show(struct device *dev, +static ssize_t pm_timeout_ms_show(struct device *dev, struct device_attribute *attr, char *buf) { struct cs40l26_private *cs40l26 = dev_get_drvdata(dev); @@ -119,7 +106,7 @@ static ssize_t cs40l26_pm_timeout_ms_show(struct device *dev, return snprintf(buf, PAGE_SIZE, "%u\n", timeout_ms); } -static ssize_t cs40l26_pm_timeout_ms_store(struct device *dev, +static ssize_t pm_timeout_ms_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct cs40l26_private *cs40l26 = dev_get_drvdata(dev); @@ -142,44 +129,23 @@ static ssize_t cs40l26_pm_timeout_ms_store(struct device *dev, return count; } -static DEVICE_ATTR(pm_timeout_ms, 0660, cs40l26_pm_timeout_ms_show, - cs40l26_pm_timeout_ms_store); +static DEVICE_ATTR_RW(pm_timeout_ms); -static ssize_t cs40l26_vibe_state_show(struct device *dev, +static ssize_t vibe_state_show(struct device *dev, struct device_attribute *attr, char *buf) { struct cs40l26_private *cs40l26 = dev_get_drvdata(dev); - int ret = 0; - char str[10]; + unsigned int state; mutex_lock(&cs40l26->lock); - - switch (cs40l26->vibe_state) { - case CS40L26_VIBE_STATE_STOPPED: - strncpy(str, "Stopped", 10); - break; - case CS40L26_VIBE_STATE_HAPTIC: - strncpy(str, "Haptic", 10); - break; - case CS40L26_VIBE_STATE_ASP: - strncpy(str, "ASP", 10); - break; - default: - dev_err(cs40l26->dev, "Invalid vibe state: %u\n", - cs40l26->vibe_state); - ret = -EINVAL; - } - + state = cs40l26->vibe_state; mutex_unlock(&cs40l26->lock); - if (ret) - return ret; - else - return snprintf(buf, PAGE_SIZE, "Vibe state: %s\n", str); + return snprintf(buf, PAGE_SIZE, "%u\n", state); } -static DEVICE_ATTR(vibe_state, 0660, cs40l26_vibe_state_show, NULL); +static DEVICE_ATTR_RO(vibe_state); -static ssize_t cs40l26_pseq_show(struct device *dev, +static ssize_t power_on_seq_show(struct device *dev, struct device_attribute *attr, char *buf) { struct cs40l26_private *cs40l26 = dev_get_drvdata(dev); @@ -210,9 +176,9 @@ static ssize_t cs40l26_pseq_show(struct device *dev, return snprintf(buf, PAGE_SIZE, "%d\n", cs40l26->pseq_num_ops); } -static DEVICE_ATTR(power_on_seq, 0440, cs40l26_pseq_show, NULL); +static DEVICE_ATTR_RO(power_on_seq); -static ssize_t cs40l26_owt_free_space_show(struct device *dev, +static ssize_t owt_free_space_show(struct device *dev, struct device_attribute *attr, char *buf) { struct cs40l26_private *cs40l26 = dev_get_drvdata(dev); @@ -232,7 +198,7 @@ static ssize_t cs40l26_owt_free_space_show(struct device *dev, goto err_pm; } - ret = snprintf(buf, PAGE_SIZE, "%d\n", words * 3); + ret = snprintf(buf, PAGE_SIZE, "%d\n", words * CL_DSP_BYTES_PER_WORD); err_pm: pm_runtime_mark_last_busy(cs40l26->dev); @@ -240,9 +206,9 @@ err_pm: return ret; } -static DEVICE_ATTR(owt_free_space, 0440, cs40l26_owt_free_space_show, NULL); +static DEVICE_ATTR_RO(owt_free_space); -static ssize_t cs40l26_die_temp_show(struct device *dev, +static ssize_t die_temp_show(struct device *dev, struct device_attribute *attr, char *buf) { struct cs40l26_private *cs40l26 = dev_get_drvdata(dev); @@ -283,9 +249,9 @@ err_pm: return ret; } -static DEVICE_ATTR(die_temp, 0440, cs40l26_die_temp_show, NULL); +static DEVICE_ATTR_RO(die_temp); -static ssize_t cs40l26_num_waves_show(struct device *dev, +static ssize_t num_waves_show(struct device *dev, struct device_attribute *attr, char *buf) { struct cs40l26_private *cs40l26 = dev_get_drvdata(dev); @@ -306,10 +272,10 @@ err_pm: return ret; } -static DEVICE_ATTR(num_waves, 0440, cs40l26_num_waves_show, NULL); +static DEVICE_ATTR_RO(num_waves); /* boost_disable_delay is in units of 125us, e.g. 8 -> 1ms */ -static ssize_t cs40l26_boost_disable_delay_show(struct device *dev, +static ssize_t boost_disable_delay_show(struct device *dev, struct device_attribute *attr, char *buf) { struct cs40l26_private *cs40l26 = dev_get_drvdata(dev); @@ -336,7 +302,7 @@ err_pm: return ret; } -static ssize_t cs40l26_boost_disable_delay_store(struct device *dev, +static ssize_t boost_disable_delay_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct cs40l26_private *cs40l26 = dev_get_drvdata(dev); @@ -370,10 +336,9 @@ err_pm: else return count; } -static DEVICE_ATTR(boost_disable_delay, 0660, cs40l26_boost_disable_delay_show, - cs40l26_boost_disable_delay_store); +static DEVICE_ATTR_RW(boost_disable_delay); -static ssize_t cs40l26_f0_offset_show(struct device *dev, +static ssize_t f0_offset_show(struct device *dev, struct device_attribute *attr, char *buf) { struct cs40l26_private *cs40l26 = dev_get_drvdata(dev); @@ -394,7 +359,7 @@ static ssize_t cs40l26_f0_offset_show(struct device *dev, if (ret) goto err_mutex; - ret = snprintf(buf, PAGE_SIZE, "%d\n", val); + ret = snprintf(buf, PAGE_SIZE, "%u\n", val); err_mutex: mutex_unlock(&cs40l26->lock); @@ -404,7 +369,7 @@ err_mutex: return ret; } -static ssize_t cs40l26_f0_offset_store(struct device *dev, +static ssize_t f0_offset_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct cs40l26_private *cs40l26 = dev_get_drvdata(dev); @@ -440,8 +405,44 @@ err_mutex: return ret; } -static DEVICE_ATTR(f0_offset, 0660, cs40l26_f0_offset_show, - cs40l26_f0_offset_store); +static DEVICE_ATTR_RW(f0_offset); + +static ssize_t delay_before_stop_playback_us_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct cs40l26_private *cs40l26 = dev_get_drvdata(dev); + int ret; + + mutex_lock(&cs40l26->lock); + + ret = snprintf(buf, PAGE_SIZE, "%d\n", + cs40l26->delay_before_stop_playback_us); + + mutex_unlock(&cs40l26->lock); + + return ret; +} + +static ssize_t delay_before_stop_playback_us_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct cs40l26_private *cs40l26 = dev_get_drvdata(dev); + u32 val; + int ret; + + ret = kstrtou32(buf, 10, &val); + if (ret) + return -EINVAL; + + mutex_lock(&cs40l26->lock); + + cs40l26->delay_before_stop_playback_us = val; + + mutex_unlock(&cs40l26->lock); + + return count; +} +static DEVICE_ATTR_RW(delay_before_stop_playback_us); static struct attribute *cs40l26_dev_attrs[] = { &dev_attr_num_waves.attr, @@ -455,6 +456,7 @@ static struct attribute *cs40l26_dev_attrs[] = { &dev_attr_vibe_state.attr, &dev_attr_boost_disable_delay.attr, &dev_attr_f0_offset.attr, + &dev_attr_delay_before_stop_playback_us.attr, NULL, }; @@ -463,7 +465,7 @@ struct attribute_group cs40l26_dev_attr_group = { .attrs = cs40l26_dev_attrs, }; -static ssize_t cs40l26_trigger_calibration_store(struct device *dev, +static ssize_t trigger_calibration_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct cs40l26_private *cs40l26 = dev_get_drvdata(dev); @@ -503,8 +505,9 @@ static ssize_t cs40l26_trigger_calibration_store(struct device *dev, return ret; } +static DEVICE_ATTR_WO(trigger_calibration); -static ssize_t cs40l26_f0_measured_show(struct device *dev, +static ssize_t f0_measured_show(struct device *dev, struct device_attribute *attr, char *buf) { struct cs40l26_private *cs40l26 = dev_get_drvdata(dev); @@ -534,8 +537,9 @@ err_mutex: else return snprintf(buf, PAGE_SIZE, "%08X\n", f0_measured); } +static DEVICE_ATTR_RO(f0_measured); -static ssize_t cs40l26_q_measured_show(struct device *dev, +static ssize_t q_measured_show(struct device *dev, struct device_attribute *attr, char *buf) { struct cs40l26_private *cs40l26 = dev_get_drvdata(dev); @@ -565,8 +569,9 @@ err_mutex: else return snprintf(buf, PAGE_SIZE, "%08X\n", q_measured); } +static DEVICE_ATTR_RO(q_measured); -static ssize_t cs40l26_redc_measured_show(struct device *dev, +static ssize_t redc_measured_show(struct device *dev, struct device_attribute *attr, char *buf) { struct cs40l26_private *cs40l26 = dev_get_drvdata(dev); @@ -596,9 +601,10 @@ err_mutex: else return snprintf(buf, PAGE_SIZE, "%08X\n", redc_measured); } +static DEVICE_ATTR_RO(redc_measured); -static ssize_t cs40l26_redc_est_show(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t redc_est_show(struct device *dev, struct device_attribute *attr, + char *buf) { struct cs40l26_private *cs40l26 = dev_get_drvdata(dev); int ret; @@ -628,8 +634,8 @@ err_mutex: return snprintf(buf, PAGE_SIZE, "%08X\n", redc_est); } -static ssize_t cs40l26_redc_est_store(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) +static ssize_t redc_est_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) { struct cs40l26_private *cs40l26 = dev_get_drvdata(dev); int ret; @@ -664,8 +670,9 @@ err_mutex: else return count; } +static DEVICE_ATTR_RW(redc_est); -static ssize_t cs40l26_f0_stored_show(struct device *dev, +static ssize_t f0_stored_show(struct device *dev, struct device_attribute *attr, char *buf) { struct cs40l26_private *cs40l26 = dev_get_drvdata(dev); @@ -696,7 +703,7 @@ err_mutex: return snprintf(buf, PAGE_SIZE, "%08X\n", f0_stored); } -static ssize_t cs40l26_f0_stored_store(struct device *dev, +static ssize_t f0_stored_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct cs40l26_private *cs40l26 = dev_get_drvdata(dev); @@ -735,9 +742,10 @@ err_mutex: else return count; } +static DEVICE_ATTR_RW(f0_stored); -static ssize_t cs40l26_q_stored_show(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t q_stored_show(struct device *dev, struct device_attribute *attr, + char *buf) { struct cs40l26_private *cs40l26 = dev_get_drvdata(dev); int ret; @@ -767,8 +775,8 @@ err_mutex: return snprintf(buf, PAGE_SIZE, "%08X\n", q_stored); } -static ssize_t cs40l26_q_stored_store(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) +static ssize_t q_stored_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) { struct cs40l26_private *cs40l26 = dev_get_drvdata(dev); int ret; @@ -804,8 +812,9 @@ err_mutex: else return count; } +static DEVICE_ATTR_RW(q_stored); -static ssize_t cs40l26_redc_stored_show(struct device *dev, +static ssize_t redc_stored_show(struct device *dev, struct device_attribute *attr, char *buf) { struct cs40l26_private *cs40l26 = dev_get_drvdata(dev); @@ -836,7 +845,7 @@ err_mutex: return snprintf(buf, PAGE_SIZE, "%08X\n", redc_stored); } -static ssize_t cs40l26_redc_stored_store(struct device *dev, +static ssize_t redc_stored_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct cs40l26_private *cs40l26 = dev_get_drvdata(dev); @@ -872,8 +881,9 @@ err_mutex: else return count; } +static DEVICE_ATTR_RW(redc_stored); -static ssize_t cs40l26_f0_and_q_cal_time_ms_show(struct device *dev, +static ssize_t f0_and_q_cal_time_ms_show(struct device *dev, struct device_attribute *attr, char *buf) { struct cs40l26_private *cs40l26 = dev_get_drvdata(dev); @@ -916,8 +926,9 @@ err_mutex: else return snprintf(buf, PAGE_SIZE, "%d\n", f0_and_q_cal_time_ms); } +static DEVICE_ATTR_RO(f0_and_q_cal_time_ms); -static ssize_t cs40l26_redc_cal_time_ms_show(struct device *dev, +static ssize_t redc_cal_time_ms_show(struct device *dev, struct device_attribute *attr, char *buf) { /* FIRMWARE_STUMPY_CALIB_REDC_PLAYTIME_MS + SVC_INIT + buffer */ @@ -950,8 +961,9 @@ err_mutex: else return snprintf(buf, PAGE_SIZE, "%d\n", redc_total_cal_time_ms); } +static DEVICE_ATTR_RO(redc_cal_time_ms); -static ssize_t cs40l26_logging_en_show(struct device *dev, +static ssize_t logging_en_show(struct device *dev, struct device_attribute *attr, char *buf) { struct cs40l26_private *cs40l26 = dev_get_drvdata(dev); @@ -980,7 +992,7 @@ err_mutex: return ret; } -static ssize_t cs40l26_logging_en_store(struct device *dev, +static ssize_t logging_en_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct cs40l26_private *cs40l26 = dev_get_drvdata(dev); @@ -1050,7 +1062,9 @@ exit_mutex: return count; } -static ssize_t cs40l26_logging_max_reset_store(struct device *dev, +static DEVICE_ATTR_RW(logging_en); + +static ssize_t logging_max_reset_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct cs40l26_private *cs40l26 = dev_get_drvdata(dev); @@ -1074,9 +1088,10 @@ static ssize_t cs40l26_logging_max_reset_store(struct device *dev, return count; } +static DEVICE_ATTR_WO(logging_max_reset); -static ssize_t cs40l26_max_bemf_show(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t max_bemf_show(struct device *dev, struct device_attribute *attr, + char *buf) { struct cs40l26_private *cs40l26 = dev_get_drvdata(dev); @@ -1105,9 +1120,10 @@ err_mutex: return snprintf(buf, PAGE_SIZE, "0x%06X\n", max_bemf); } +static DEVICE_ATTR_RO(max_bemf); -static ssize_t cs40l26_max_vbst_show(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t max_vbst_show(struct device *dev, struct device_attribute *attr, + char *buf) { struct cs40l26_private *cs40l26 = dev_get_drvdata(dev); @@ -1136,8 +1152,9 @@ err_mutex: return snprintf(buf, PAGE_SIZE, "0x%06X\n", max_vbst); } +static DEVICE_ATTR_RO(max_vbst); -static ssize_t cs40l26_calib_fw_load_show(struct device *dev, +static ssize_t calib_fw_load_show(struct device *dev, struct device_attribute *attr, char *buf) { struct cs40l26_private *cs40l26 = dev_get_drvdata(dev); @@ -1146,9 +1163,9 @@ static ssize_t cs40l26_calib_fw_load_show(struct device *dev, mutex_lock(&cs40l26->lock); if (cs40l26->fw.id == CS40L26_FW_ID) - ret = snprintf(buf, PAGE_SIZE, "%u\n", 0); + ret = snprintf(buf, PAGE_SIZE, "%d\n", 0); else if (cs40l26->fw.id == CS40L26_FW_CALIB_ID) - ret = snprintf(buf, PAGE_SIZE, "%u\n", 1); + ret = snprintf(buf, PAGE_SIZE, "%d\n", 1); else ret = -EINVAL; @@ -1157,7 +1174,7 @@ static ssize_t cs40l26_calib_fw_load_show(struct device *dev, return ret; } -static ssize_t cs40l26_calib_fw_load_store(struct device *dev, +static ssize_t calib_fw_load_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct cs40l26_private *cs40l26 = dev_get_drvdata(dev); @@ -1177,35 +1194,7 @@ static ssize_t cs40l26_calib_fw_load_store(struct device *dev, return ret ? ret : count; } - -static DEVICE_ATTR(calib_fw_load, 0660, cs40l26_calib_fw_load_show, - cs40l26_calib_fw_load_store); -static DEVICE_ATTR(max_vbst, 0440, cs40l26_max_vbst_show, NULL); -static DEVICE_ATTR(max_bemf, 0440, cs40l26_max_bemf_show, NULL); -static DEVICE_ATTR(logging_max_reset, - 0220, NULL, cs40l26_logging_max_reset_store); -static DEVICE_ATTR(logging_en, - 0660, cs40l26_logging_en_show, cs40l26_logging_en_store); -static DEVICE_ATTR(trigger_calibration, - 0220, NULL, cs40l26_trigger_calibration_store); -static DEVICE_ATTR(f0_measured, - 0440, cs40l26_f0_measured_show, NULL); -static DEVICE_ATTR(q_measured, - 0440, cs40l26_q_measured_show, NULL); -static DEVICE_ATTR(redc_measured, - 0440, cs40l26_redc_measured_show, NULL); -static DEVICE_ATTR(redc_est, - 0660, cs40l26_redc_est_show, cs40l26_redc_est_store); -static DEVICE_ATTR(f0_stored, - 0660, cs40l26_f0_stored_show, cs40l26_f0_stored_store); -static DEVICE_ATTR(q_stored, - 0660, cs40l26_q_stored_show, cs40l26_q_stored_store); -static DEVICE_ATTR(redc_stored, - 0660, cs40l26_redc_stored_show, cs40l26_redc_stored_store); -static DEVICE_ATTR(f0_and_q_cal_time_ms, - 0440, cs40l26_f0_and_q_cal_time_ms_show, NULL); -static DEVICE_ATTR(redc_cal_time_ms, - 0440, cs40l26_redc_cal_time_ms_show, NULL); +static DEVICE_ATTR_RW(calib_fw_load); static struct attribute *cs40l26_dev_attrs_cal[] = { &dev_attr_calib_fw_load.attr, diff --git a/cs40l26/cs40l26.c b/cs40l26/cs40l26.c index f8c7afd..be2e22b 100644 --- a/cs40l26/cs40l26.c +++ b/cs40l26/cs40l26.c @@ -19,6 +19,29 @@ static inline bool is_owt(unsigned int index) index <= CS40L26_OWT_INDEX_END; } +static inline bool is_buzz(unsigned int index) +{ + return (index >= CS40L26_BUZZGEN_INDEX_START && + index <= CS40L26_BUZZGEN_INDEX_END); +} + +static inline bool is_ram(unsigned int index) +{ + return (index >= CS40L26_RAM_INDEX_START && + index <= CS40L26_RAM_INDEX_END); +} + +static inline bool is_rom(unsigned int index) +{ + return (index >= CS40L26_ROM_INDEX_START && + index <= CS40L26_ROM_INDEX_END); +} + +static inline bool section_complete(struct cs40l26_owt_section *s) +{ + return s->delay ? true : false; +} + static int cs40l26_dsp_read(struct cs40l26_private *cs40l26, u32 reg, u32 *val) { struct regmap *regmap = cs40l26->regmap; @@ -153,15 +176,44 @@ int cs40l26_dsp_state_get(struct cs40l26_private *cs40l26, u8 *state) } EXPORT_SYMBOL(cs40l26_dsp_state_get); -int cs40l26_pm_timeout_ms_set(struct cs40l26_private *cs40l26, - u32 timeout_ms) +static int cs40l26_pm_timer_timeout_ticks4_set(struct cs40l26_private *cs40l26, + u32 pm_timer_timeout_ticks4) { - u32 timeout_ticks = timeout_ms * CS40L26_PM_TICKS_MS_DIV; struct regmap *regmap = cs40l26->regmap; u32 lower_val, reg; u8 upper_val; int ret; + upper_val = (pm_timer_timeout_ticks4 >> + CS40L26_PM_TIMEOUT_TICKS_UPPER_SHIFT) & + CS40L26_PM_TIMEOUT_TICKS_UPPER_MASK; + + lower_val = pm_timer_timeout_ticks4 & + CS40L26_PM_TIMEOUT_TICKS_LOWER_MASK; + + ret = cl_dsp_get_reg(cs40l26->dsp, "PM_TIMER_TIMEOUT_TICKS", + CL_DSP_XM_UNPACKED_TYPE, CS40L26_PM_ALGO_ID, ®); + if (ret) + return ret; + + ret = regmap_write(regmap, + reg + CS40L26_PM_TIMER_TIMEOUT_TICKS4_LOWER_OFFSET, + lower_val); + if (ret) + return ret; + + return regmap_write(regmap, + reg + CS40L26_PM_TIMER_TIMEOUT_TICKS4_UPPER_OFFSET, + upper_val); +} + +int cs40l26_pm_timeout_ms_set(struct cs40l26_private *cs40l26, + u32 timeout_ms) +{ + u32 timeout_ticks = timeout_ms * CS40L26_PM_TICKS_MS_DIV; + u32 lower_val, upper_val, reg; + int ret; + upper_val = (timeout_ticks >> CS40L26_PM_TIMEOUT_TICKS_UPPER_SHIFT) & CS40L26_PM_TIMEOUT_TICKS_UPPER_MASK; @@ -172,12 +224,12 @@ int cs40l26_pm_timeout_ms_set(struct cs40l26_private *cs40l26, if (ret) return ret; - ret = regmap_write(regmap, reg + CS40L26_PM_STDBY_TIMEOUT_LOWER_OFFSET, + ret = cs40l26_dsp_write(cs40l26, reg + CS40L26_PM_STDBY_TIMEOUT_LOWER_OFFSET, lower_val); if (ret) return ret; - return regmap_write(regmap, reg + CS40L26_PM_STDBY_TIMEOUT_UPPER_OFFSET, + return cs40l26_dsp_write(cs40l26, reg + CS40L26_PM_STDBY_TIMEOUT_UPPER_OFFSET, upper_val); } EXPORT_SYMBOL(cs40l26_pm_timeout_ms_set); @@ -248,37 +300,88 @@ static void cs40l26_pm_runtime_teardown(struct cs40l26_private *cs40l26) cs40l26->pm_ready = false; } +static int cs40l26_check_pm_lock(struct cs40l26_private *cs40l26, bool *locked) +{ + int ret; + unsigned int dsp_lock; + + ret = regmap_read(cs40l26->regmap, CS40L26_A1_PM_STATE_LOCKS_STATIC_REG + + CS40L26_DSP_LOCK3_OFFSET, &dsp_lock); + if (ret) + return ret; + + if (dsp_lock & CS40L26_DSP_LOCK3_MASK) + *locked = true; + else + *locked = false; + + return 0; +} + int cs40l26_pm_state_transition(struct cs40l26_private *cs40l26, enum cs40l26_pm_state state) { struct device *dev = cs40l26->dev; u32 cmd; - int ret; + u8 curr_state; + bool dsp_lock; + int ret, i; cmd = (u32) CS40L26_DSP_MBOX_PM_CMD_BASE + state; switch (state) { case CS40L26_PM_STATE_WAKEUP: - /* intentionally fall through */ - case CS40L26_PM_STATE_PREVENT_HIBERNATE: ret = cs40l26_ack_write(cs40l26, CS40L26_DSP_VIRTUAL1_MBOX_1, cmd, CS40L26_DSP_MBOX_RESET); + if (ret) + return ret; + + break; + case CS40L26_PM_STATE_PREVENT_HIBERNATE: + for (i = 0; i < CS40L26_DSP_STATE_ATTEMPTS; i++) { + ret = cs40l26_ack_write(cs40l26, CS40L26_DSP_VIRTUAL1_MBOX_1, + cmd, CS40L26_DSP_MBOX_RESET); + if (ret) + return ret; + + ret = cs40l26_dsp_state_get(cs40l26, &curr_state); + if (ret) + return ret; + + if (curr_state == CS40L26_DSP_STATE_ACTIVE) + break; + + if (curr_state == CS40L26_DSP_STATE_STANDBY) { + ret = cs40l26_check_pm_lock(cs40l26, &dsp_lock); + if (ret) + return ret; + + if (dsp_lock) + break; + } + usleep_range(5000, 5100); + } + + if (i == CS40L26_DSP_STATE_ATTEMPTS) { + dev_err(cs40l26->dev, "DSP not starting\n"); + return -ETIMEDOUT; + } + break; case CS40L26_PM_STATE_ALLOW_HIBERNATE: - /* intentionally fall through */ case CS40L26_PM_STATE_SHUTDOWN: cs40l26->wksrc_sts = 0x00; ret = cs40l26_dsp_write(cs40l26, CS40L26_DSP_VIRTUAL1_MBOX_1, cmd); + if (ret) + return ret; + break; default: dev_err(dev, "Invalid PM state: %u\n", state); return -EINVAL; } - if (ret) - return ret; - cs40l26->pm_state = state; return 0; @@ -287,8 +390,20 @@ int cs40l26_pm_state_transition(struct cs40l26_private *cs40l26, static int cs40l26_dsp_start(struct cs40l26_private *cs40l26) { u8 dsp_state; + unsigned int val; int ret; + + ret = regmap_read(cs40l26->regmap, CS40L26_A1_DSP_REQ_ACTIVE_REG, + &val); + if (ret) { + dev_err(cs40l26->dev, "Can't read REQ ACTIVE %d\n", ret); + return ret; + } + + if (val & CS40L26_DSP_PM_ACTIVE) + dev_warn(cs40l26->dev, "REQ ACTIVE is 0x%x\n", val); + ret = regmap_write(cs40l26->regmap, CS40L26_DSP1_CCM_CORE_CONTROL, CS40L26_DSP_CCM_CORE_RESET); if (ret) { @@ -384,7 +499,7 @@ static int cs40l26_dsp_pre_config(struct cs40l26_private *cs40l26) if (ret) return ret; - for (i = 0; i < 10; i++) { + for (i = 0; i < CS40L26_DSP_SHUTDOWN_MAX_ATTEMPTS; i++) { ret = cs40l26_dsp_state_get(cs40l26, &dsp_state); if (ret) return ret; @@ -399,7 +514,7 @@ static int cs40l26_dsp_pre_config(struct cs40l26_private *cs40l26) CS40L26_MS_TO_US(timeout_ms) + 100); } - if (i == 10) { + if (i == CS40L26_DSP_SHUTDOWN_MAX_ATTEMPTS) { dev_err(cs40l26->dev, "DSP Core could not be shut down\n"); return -EINVAL; } @@ -499,21 +614,33 @@ static int cs40l26_handle_mbox_buffer(struct cs40l26_private *cs40l26) } switch (val) { - case CS40L26_DSP_MBOX_TRIGGER_COMPLETE: - if (cs40l26->vibe_state != CS40L26_VIBE_STATE_ASP) - cs40l26_vibe_state_set(cs40l26, - CS40L26_VIBE_STATE_STOPPED); - dev_dbg(dev, "Trigger Complete\n"); + case CS40L26_DSP_MBOX_COMPLETE_MBOX: + dev_dbg(dev, "Mailbox: COMPLETE_MBOX\n"); + cs40l26_vibe_state_update(cs40l26, + CS40L26_VIBE_STATE_EVENT_MBOX_COMPLETE); + break; + case CS40L26_DSP_MBOX_COMPLETE_GPIO: + dev_dbg(dev, "Mailbox: COMPLETE_GPIO\n"); + cs40l26_vibe_state_update(cs40l26, + CS40L26_VIBE_STATE_EVENT_GPIO_COMPLETE); + break; + case CS40L26_DSP_MBOX_COMPLETE_I2S: + dev_dbg(dev, "Mailbox: COMPLETE_I2S\n"); + break; + case CS40L26_DSP_MBOX_TRIGGER_GPIO: + dev_dbg(dev, "Mailbox: TRIGGER_GPIO\n"); + cs40l26_vibe_state_update(cs40l26, + CS40L26_VIBE_STATE_EVENT_GPIO_TRIGGER); break; case CS40L26_DSP_MBOX_PM_AWAKE: cs40l26->wksrc_sts |= CS40L26_WKSRC_STS_EN; - dev_dbg(dev, "HALO Core is awake\n"); + dev_dbg(dev, "Mailbox: AWAKE\n"); break; case CS40L26_DSP_MBOX_F0_EST_START: - dev_dbg(dev, "F0_EST_START\n"); + dev_dbg(dev, "Mailbox: F0_EST_START\n"); break; case CS40L26_DSP_MBOX_F0_EST_DONE: - dev_dbg(dev, "F0_EST_DONE\n"); + dev_dbg(dev, "Mailbox: F0_EST_DONE\n"); if (cs40l26->cal_requested & CS40L26_CALIBRATION_CONTROL_REQUEST_F0_AND_Q) { cs40l26->cal_requested &= @@ -528,10 +655,10 @@ static int cs40l26_handle_mbox_buffer(struct cs40l26_private *cs40l26) break; case CS40L26_DSP_MBOX_REDC_EST_START: - dev_dbg(dev, "REDC_EST_START\n"); + dev_dbg(dev, "Mailbox: REDC_EST_START\n"); break; case CS40L26_DSP_MBOX_REDC_EST_DONE: - dev_dbg(dev, "REDC_EST_DONE\n"); + dev_dbg(dev, "Mailbox: REDC_EST_DONE\n"); if (cs40l26->cal_requested & CS40L26_CALIBRATION_CONTROL_REQUEST_REDC) { cs40l26->cal_requested &= @@ -545,14 +672,13 @@ static int cs40l26_handle_mbox_buffer(struct cs40l26_private *cs40l26) } break; case CS40L26_DSP_MBOX_LE_EST_START: - dev_dbg(dev, "LE_EST_START\n"); + dev_dbg(dev, "Mailbox: LE_EST_START\n"); break; case CS40L26_DSP_MBOX_LE_EST_DONE: - dev_dbg(dev, "LE_EST_DONE\n"); + dev_dbg(dev, "Mailbox: LE_EST_DONE\n"); break; case CS40L26_DSP_MBOX_SYS_ACK: - dev_err(dev, "Mbox buffer value (0x%X) not supported\n", - val); + dev_err(dev, "Mailbox: ACK\n"); return -EPERM; default: dev_err(dev, "MBOX buffer value (0x%X) is invalid\n", @@ -564,51 +690,94 @@ static int cs40l26_handle_mbox_buffer(struct cs40l26_private *cs40l26) return 0; } -void cs40l26_asp_worker(struct work_struct *work) -{ - struct cs40l26_private *cs40l26 = - container_of(work, struct cs40l26_private, asp_work); +int cs40l26_asp_start(struct cs40l26_private *cs40l26) { + struct device *dev = cs40l26->dev; + bool ack = false; + unsigned int val; + int ret; + + ret = cs40l26_ack_write(cs40l26, CS40L26_DSP_VIRTUAL1_MBOX_1, + CS40L26_STOP_PLAYBACK, CS40L26_DSP_MBOX_RESET); + if (ret) { + dev_err(dev, "Failed to stop playback before I2S start\n"); + return ret; + } + + ret = regmap_write(cs40l26->regmap, CS40L26_DSP_VIRTUAL1_MBOX_1, + CS40L26_DSP_MBOX_CMD_START_I2S); + if (ret) { + dev_err(cs40l26->dev, "Failed to start I2S\n"); + return ret; + } + + while (!ack) { + usleep_range(CS40L26_DSP_TIMEOUT_US_MIN, + CS40L26_DSP_TIMEOUT_US_MAX); - cs40l26_ack_write(cs40l26, CS40L26_DSP_VIRTUAL1_MBOX_1, - CS40L26_DSP_MBOX_CMD_START_I2S, CS40L26_DSP_MBOX_RESET); + ret = regmap_read(cs40l26->regmap, CS40L26_DSP_VIRTUAL1_MBOX_1, + &val); + if (ret) { + dev_err(cs40l26->dev, "Failed to read from MBOX_1\n"); + return ret; + } + + if (val == CS40L26_DSP_MBOX_RESET) + ack = true; + } + + return 0; } -EXPORT_SYMBOL(cs40l26_asp_worker); +EXPORT_SYMBOL(cs40l26_asp_start); -void cs40l26_vibe_state_set(struct cs40l26_private *cs40l26, - enum cs40l26_vibe_state new_state) +void cs40l26_vibe_state_update(struct cs40l26_private *cs40l26, + enum cs40l26_vibe_state_event event) { - if (cs40l26->vibe_state == new_state) + if (!mutex_is_locked(&cs40l26->lock)) { + dev_err(cs40l26->dev, "%s must be called under mutex lock\n", + __func__); return; + } + + switch (event) { + case CS40L26_VIBE_STATE_EVENT_MBOX_PLAYBACK: + case CS40L26_VIBE_STATE_EVENT_GPIO_TRIGGER: + cs40l26->effects_in_flight++; + break; + case CS40L26_VIBE_STATE_EVENT_MBOX_COMPLETE: + case CS40L26_VIBE_STATE_EVENT_GPIO_COMPLETE: + cs40l26->effects_in_flight--; + if (cs40l26->effects_in_flight < 0) + dev_err(cs40l26->dev, "effects_in_flight < 0, %d\n", + cs40l26->effects_in_flight); + if (cs40l26->effects_in_flight == 0 && cs40l26->asp_enable) + if (cs40l26_asp_start(cs40l26)) + return; + break; + case CS40L26_VIBE_STATE_EVENT_ASP_START: + cs40l26->asp_enable = true; + break; + case CS40L26_VIBE_STATE_EVENT_ASP_STOP: + cs40l26->asp_enable = false; + break; + default: + dev_err(cs40l26->dev, "Invalid vibe state event: %d\n", event); + break; + } - if (new_state == CS40L26_VIBE_STATE_STOPPED && cs40l26->asp_enable) - queue_work(cs40l26->asp_workqueue, &cs40l26->asp_work); + if (cs40l26->effects_in_flight) + cs40l26->vibe_state = CS40L26_VIBE_STATE_HAPTIC; + else if (cs40l26->asp_enable) + cs40l26->vibe_state = CS40L26_VIBE_STATE_ASP; + else + cs40l26->vibe_state = CS40L26_VIBE_STATE_STOPPED; - cs40l26->vibe_state = new_state; #if !IS_ENABLED(CONFIG_INPUT_CS40L26_ATTR_UNDER_BUS) sysfs_notify(&cs40l26->input->dev.kobj, "default", "vibe_state"); #else sysfs_notify(&cs40l26->dev->kobj, "default", "vibe_state"); #endif } -EXPORT_SYMBOL(cs40l26_vibe_state_set); - -static int cs40l26_event_count_get(struct cs40l26_private *cs40l26, u32 *count) -{ - unsigned int reg; - int ret; - - ret = cl_dsp_get_reg(cs40l26->dsp, "EVENT_POST_COUNT", - CL_DSP_XM_UNPACKED_TYPE, CS40L26_EVENT_HANDLER_ALGO_ID, - ®); - if (ret) - return ret; - - ret = regmap_read(cs40l26->regmap, reg, count); - if (ret) - dev_err(cs40l26->dev, "Failed to get event count\n"); - - return ret; -} +EXPORT_SYMBOL(cs40l26_vibe_state_update); static int cs40l26_error_release(struct cs40l26_private *cs40l26, unsigned int err_rls, bool bst_err) @@ -764,13 +933,13 @@ static int cs40l26_irq_update_mask(struct cs40l26_private *cs40l26, u32 irq_reg, } static int cs40l26_handle_irq1(struct cs40l26_private *cs40l26, - enum cs40l26_irq1 irq1, bool trigger) + enum cs40l26_irq1 irq1) { struct device *dev = cs40l26->dev; u32 err_rls = 0; unsigned int reg, val; - bool bst_err; - int ret; + bool bst_err = false; + int ret = 0; switch (irq1) { case CS40L26_IRQ1_GPIO1_RISE: @@ -791,10 +960,7 @@ static int cs40l26_handle_irq1(struct cs40l26_private *cs40l26, if (cs40l26->wksrc_sts & CS40L26_WKSRC_STS_EN) { dev_dbg(dev, "GPIO%u %s edge detected\n", (irq1 / 2) + 1, - irq1 % 2 ? "falling" : "rising"); - if (trigger) - cs40l26_vibe_state_set(cs40l26, - CS40L26_VIBE_STATE_HAPTIC); + (irq1 % 2) ? "falling" : "rising"); } cs40l26->wksrc_sts |= CS40L26_WKSRC_STS_EN; @@ -846,9 +1012,6 @@ static int cs40l26_handle_irq1(struct cs40l26_private *cs40l26, dev_dbg(dev, "GPIO%u rising edge detected\n", irq1 - 8); } - if (trigger) - cs40l26_vibe_state_set(cs40l26, - CS40L26_VIBE_STATE_HAPTIC); break; case CS40L26_IRQ1_WKSRC_STS_SPI: dev_dbg(dev, "SPI event woke device from hibernate\n"); @@ -1066,8 +1229,6 @@ static irqreturn_t cs40l26_irq(int irq, void *data) struct regmap *regmap = cs40l26->regmap; struct device *dev = cs40l26->dev; unsigned long num_irq; - u32 event_count; - bool trigger; int ret; if (cs40l26_dsp_read(cs40l26, CS40L26_IRQ1_STATUS, &sts)) { @@ -1081,6 +1242,9 @@ static irqreturn_t cs40l26_irq(int irq, void *data) } ret = pm_runtime_get_sync(dev); + + mutex_lock(&cs40l26->lock); + if (ret < 0) { pm_runtime_set_active(dev); @@ -1108,17 +1272,11 @@ static irqreturn_t cs40l26_irq(int irq, void *data) val = eint & ~mask; if (val) { - ret = cs40l26_event_count_get(cs40l26, &event_count); - if (ret) - goto err; - - trigger = (event_count > cs40l26->event_count); - num_irq = hweight_long(val); i = 0; while (irq1_count < num_irq && i < CS40L26_IRQ1_NUM_IRQS) { if (val & BIT(i)) { - ret = cs40l26_handle_irq1(cs40l26, i, trigger); + ret = cs40l26_handle_irq1(cs40l26, i); if (ret) goto err; else @@ -1158,7 +1316,7 @@ static irqreturn_t cs40l26_irq(int irq, void *data) } err: - cs40l26_event_count_get(cs40l26, &cs40l26->event_count); + mutex_unlock(&cs40l26->lock); pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); @@ -1178,7 +1336,7 @@ static int cs40l26_pseq_add_op(struct cs40l26_private *cs40l26, struct regmap *regmap = cs40l26->regmap; struct device *dev = cs40l26->dev; u32 offset_for_new_op, *op_words; - int ret; + int ret = 0; struct cs40l26_pseq_op *pseq_op_end, *pseq_op_new, *op; /* get location of the list terminator */ @@ -1340,12 +1498,58 @@ static int cs40l26_pseq_add_write_reg_h16(struct cs40l26_private *cs40l26, return ret; } +static int cs40l26_pseq_add_write_reg_l16(struct cs40l26_private *cs40l26, + u32 addr, u16 data, bool update_if_op_already_in_seq) +{ + int ret; + struct device *dev = cs40l26->dev; + struct cs40l26_pseq_op *op; + u32 op_words[CS40L26_PSEQ_OP_WRITE_REG_L16_WORDS]; + + memset(op_words, 0, CS40L26_PSEQ_OP_WRITE_REG_L16_WORDS*sizeof(u32)); + + if (addr & CS40L26_PSEQ_INVALID_ADDR) { + dev_err(dev, "invalid address for pseq write_reg_l16\n"); + return -EINVAL; + } + + op_words[0] = (CS40L26_PSEQ_OP_WRITE_REG_L16 << + CS40L26_PSEQ_OP_SHIFT); + op_words[0] |= (addr & CS40L26_PSEQ_WORD1_MASK) >> 8; + op_words[1] = (addr & CS40L26_PSEQ_WORD2_MASK) << 16; + op_words[1] |= data; + + if (update_if_op_already_in_seq) { + list_for_each_entry(op, &cs40l26->pseq_op_head, list) { + /* check if op with same op and addr already exists */ + if ((op->words[0] == op_words[0]) && + ((op->words[1] & CS40L26_PSEQ_EQ_MASK) == + (op_words[1] & CS40L26_PSEQ_EQ_MASK))) { + /* update data in the existing op and return */ + ret = regmap_bulk_write(cs40l26->regmap, + cs40l26->pseq_base + op->offset, + op_words, op->size); + if (ret) + dev_err(cs40l26->dev, + "Failed to update op\n"); + return ret; + } + } + } + + ret = cs40l26_pseq_add_op(cs40l26, + CS40L26_PSEQ_OP_WRITE_REG_L16_WORDS, + op_words); + + return ret; +} + static int cs40l26_pseq_init(struct cs40l26_private *cs40l26) { struct device *dev = cs40l26->dev; u32 words[CS40L26_PSEQ_MAX_WORDS], *op_words; struct cs40l26_pseq_op *pseq_op; - int ret, i, j, num_words, read_size; + int ret, i, j, num_words, read_size_words; u8 operation; INIT_LIST_HEAD(&cs40l26->pseq_op_head); @@ -1360,18 +1564,20 @@ static int cs40l26_pseq_init(struct cs40l26_private *cs40l26) /* read pseq memory space */ i = 0; while (i < CS40L26_PSEQ_MAX_WORDS) { - read_size = min(CS40L26_MAX_I2C_READ_SIZE_BYTES, - CS40L26_PSEQ_MAX_WORDS - i); + if ((CS40L26_PSEQ_MAX_WORDS - i) > + CS40L26_MAX_I2C_READ_SIZE_WORDS) + read_size_words = CS40L26_MAX_I2C_READ_SIZE_WORDS; + else + read_size_words = CS40L26_PSEQ_MAX_WORDS - i; ret = regmap_bulk_read(cs40l26->regmap, cs40l26->pseq_base + i * CL_DSP_BYTES_PER_WORD, - words + i, - read_size); + words + i, read_size_words); if (ret) { dev_err(dev, "Failed to read from power on seq.\n"); return ret; } - i += read_size; + i += read_size_words; } i = 0; @@ -1436,6 +1642,20 @@ static int cs40l26_update_reg_defaults_via_pseq(struct cs40l26_private *cs40l26) struct device *dev = cs40l26->dev; int ret; + ret = cs40l26_pseq_add_write_reg_l16(cs40l26, CS40L26_NGATE1_INPUT, + CS40L26_DATA_SRC_DSP1TX4, true); + if (ret) { + dev_err(dev, "Failed to sequence Noise Gate Input register default updates\n"); + return ret; + } + + ret = cs40l26_pseq_add_write_reg_full(cs40l26, CS40L26_MIXER_NGATE_CH1_CFG, + CS40L26_MIXER_NGATE_CH1_CFG_DEFAULT_NEW, true); + if (ret) { + dev_err(dev, "Failed to sequence Mixer Noise Gate register default updates\n"); + return ret; + } + /* set SPK_DEFAULT_HIZ to 1 */ ret = cs40l26_pseq_add_write_reg_h16(cs40l26, CS40L26_TST_DAC_MSM_CONFIG, @@ -1448,88 +1668,95 @@ static int cs40l26_update_reg_defaults_via_pseq(struct cs40l26_private *cs40l26) } static int cs40l26_buzzgen_set(struct cs40l26_private *cs40l26, u16 freq, - u16 level, u16 duration, u8 offset) + u16 level, u16 duration, u8 buzzgen_num) { int ret; - unsigned int base_reg, offset_reg; + unsigned int base_reg, freq_reg, level_reg, duration_reg; + /* BUZZ_EFFECTS1_BUZZ_xxx are initially populated by contents of OTP. + * The buzz specified by these controls is triggered by writing + * 0x01800080 to the DSP mailbox + */ ret = cl_dsp_get_reg(cs40l26->dsp, "BUZZ_EFFECTS1_BUZZ_FREQ", CL_DSP_XM_UNPACKED_TYPE, CS40L26_BUZZGEN_ALGO_ID, &base_reg); if (ret) return ret; - offset_reg = base_reg + (offset * 12); + freq_reg = base_reg + + ((buzzgen_num) * CS40L26_BUZZGEN_CONFIG_OFFSET); + level_reg = base_reg + + ((buzzgen_num) * CS40L26_BUZZGEN_CONFIG_OFFSET) + + CS40L26_BUZZGEN_LEVEL_OFFSET; + duration_reg = base_reg + + ((buzzgen_num) * CS40L26_BUZZGEN_CONFIG_OFFSET) + + CS40L26_BUZZGEN_DURATION_OFFSET; - ret = regmap_write(cs40l26->regmap, offset_reg, freq); + ret = regmap_write(cs40l26->regmap, freq_reg, freq); if (ret) { dev_err(cs40l26->dev, "Failed to write BUZZGEN frequency\n"); return ret; } - ret = regmap_write(cs40l26->regmap, offset_reg + 4, - CS40L26_BUZZGEN_LEVEL_DEFAULT); + ret = regmap_write(cs40l26->regmap, level_reg, level); if (ret) { dev_err(cs40l26->dev, "Failed to write BUZZGEN level\n"); return ret; } - ret = regmap_write(cs40l26->regmap, offset_reg + 8, duration / 4); + ret = regmap_write(cs40l26->regmap, duration_reg, duration / 4); if (ret) dev_err(cs40l26->dev, "Failed to write BUZZGEN duration\n"); return ret; } -static int cs40l26_gpio_index_set(struct cs40l26_private *cs40l26, - struct ff_effect *effect) +static int cs40l26_map_gpi_to_haptic(struct cs40l26_private *cs40l26, + u32 index, u8 bank, struct ff_effect *effect) { + int ret; + bool edge, ev_handler_bank_ram, owt; + u32 reg, write_val; u16 button = effect->trigger.button; - u8 bank = (button & CS40L26_BTN_BANK_MASK) >> CS40L26_BTN_BANK_SHIFT; - u8 edge = (button & CS40L26_BTN_EDGE_MASK) >> CS40L26_BTN_EDGE_SHIFT; u8 gpio = (button & CS40L26_BTN_NUM_MASK) >> CS40L26_BTN_NUM_SHIFT; - u8 index = button & CS40L26_BTN_INDEX_MASK; - u8 buzz = button & CS40L26_BTN_BUZZ_MASK; - u16 write_val, freq; - u8 offset; - u32 reg; - int ret; - if (gpio != 1) { - dev_err(cs40l26->dev, "GPIO%u not supported on 0x%02X\n", gpio, - cs40l26->revid); + edge = (button & CS40L26_BTN_EDGE_MASK) >> CS40L26_BTN_EDGE_SHIFT; + + switch (bank) { + case CS40L26_RAM_BANK_ID: + owt = false; + ev_handler_bank_ram = true; + break; + case CS40L26_ROM_BANK_ID: + owt = false; + ev_handler_bank_ram = false; + break; + case CS40L26_OWT_BANK_ID: + owt = true; + ev_handler_bank_ram = true; + break; + default: + dev_err(cs40l26->dev, "Effect bank %u not supported\n", + bank); return -EINVAL; } - if (edge > 1) { - dev_err(cs40l26->dev, "Invalid GPI edge %u\n", edge); + if (gpio != CS40L26_GPIO1) { + dev_err(cs40l26->dev, "GPIO%u not supported on 0x%02X\n", gpio, + cs40l26->revid); return -EINVAL; } - offset = ((edge ^ 1)) * 4; - reg = cs40l26->event_map_base + offset; + reg = cs40l26->event_map_base + (edge ? 0 : 4); + write_val = (index & CS40L26_BTN_INDEX_MASK) | + (ev_handler_bank_ram << CS40L26_BTN_BANK_SHIFT) | + (owt << CS40L26_BTN_OWT_SHIFT); pm_runtime_get_sync(cs40l26->dev); - if (buzz) { - freq = CS40L26_MS_TO_HZ(effect->u.periodic.period); - - ret = cs40l26_buzzgen_set(cs40l26, freq, - CS40L26_BUZZGEN_LEVEL_DEFAULT, - effect->replay.length, 0); - if (ret) - goto pm_err; - - write_val = 1 << CS40L26_BTN_BUZZ_SHIFT; - } else { - write_val = index | (bank << CS40L26_BTN_BANK_SHIFT); - } - - ret = regmap_update_bits(cs40l26->regmap, reg, - CS40L26_EVENT_MAP_INDEX_MASK, write_val); + ret = regmap_write(cs40l26->regmap, reg, write_val); if (ret) dev_err(cs40l26->dev, "Failed to update event map\n"); -pm_err: pm_runtime_mark_last_busy(cs40l26->dev); pm_runtime_put_autosuspend(cs40l26->dev); @@ -1593,26 +1820,32 @@ static void cs40l26_vibe_start_worker(struct work_struct *work) struct device *dev = cs40l26->dev; u32 index = 0; int ret = 0; + struct ff_effect *effect; struct cs40l26_owt *owt; - unsigned int reg, freq; + unsigned int reg; bool invert; u16 duration; dev_dbg(dev, "%s\n", __func__); - if (cs40l26->effect->u.periodic.waveform == FF_CUSTOM) - index = cs40l26->trigger_indices[cs40l26->effect->id]; + pm_runtime_get_sync(dev); + mutex_lock(&cs40l26->lock); + + 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, cs40l26->effect->id); - if (owt == NULL) - return; + owt = cs40l26_owt_find(cs40l26, effect->id); + if (owt == NULL) { + ret = -ENOMEM; + goto err_mutex; + } } - duration = cs40l26->effect->replay.length; - - pm_runtime_get_sync(dev); - mutex_lock(&cs40l26->lock); + duration = effect->replay.length; ret = cl_dsp_get_reg(cs40l26->dsp, "TIMEOUT_MS", CL_DSP_XM_UNPACKED_TYPE, CS40L26_VIBEGEN_ALGO_ID, ®); @@ -1630,7 +1863,7 @@ static void cs40l26_vibe_start_worker(struct work_struct *work) if (ret) goto err_mutex; - switch (cs40l26->effect->direction) { + switch (effect->direction) { case 0x0000: invert = false; break; @@ -1639,7 +1872,7 @@ static void cs40l26_vibe_start_worker(struct work_struct *work) break; default: dev_err(dev, "Invalid ff_effect direction: 0x%X\n", - cs40l26->effect->direction); + effect->direction); ret = -EINVAL; goto err_mutex; } @@ -1648,40 +1881,24 @@ static void cs40l26_vibe_start_worker(struct work_struct *work) if (ret) goto err_mutex; - cs40l26_vibe_state_set(cs40l26, CS40L26_VIBE_STATE_HAPTIC); - - switch (cs40l26->effect->u.periodic.waveform) { + switch (effect->u.periodic.waveform) { case FF_CUSTOM: - ret = cs40l26_ack_write(cs40l26, CS40L26_DSP_VIRTUAL1_MBOX_1, - index, CS40L26_DSP_MBOX_RESET); - if (ret) - goto err_mutex; - break; case FF_SINE: - freq = CS40L26_MS_TO_HZ(cs40l26->effect->u.periodic.period); - - ret = cs40l26_buzzgen_set(cs40l26, freq, - CS40L26_BUZZGEN_LEVEL_DEFAULT, duration, 1); - if (ret) - goto err_mutex; - ret = cs40l26_ack_write(cs40l26, CS40L26_DSP_VIRTUAL1_MBOX_1, - CS40L26_BUZZGEN_INDEX_CP_TRIGGER, - CS40L26_DSP_MBOX_RESET); + index, CS40L26_DSP_MBOX_RESET); if (ret) goto err_mutex; + cs40l26_vibe_state_update(cs40l26, + CS40L26_VIBE_STATE_EVENT_MBOX_PLAYBACK); break; default: dev_err(dev, "Invalid waveform type: 0x%X\n", - cs40l26->effect->u.periodic.waveform); + effect->u.periodic.waveform); ret = -EINVAL; goto err_mutex; } err_mutex: - if (ret) - cs40l26_vibe_state_set(cs40l26, CS40L26_VIBE_STATE_STOPPED); - mutex_unlock(&cs40l26->lock); pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); @@ -1701,6 +1918,11 @@ static void cs40l26_vibe_stop_worker(struct work_struct *work) if (cs40l26->vibe_state != CS40L26_VIBE_STATE_HAPTIC) goto mutex_exit; + /* 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); + ret = cs40l26_ack_write(cs40l26, CS40L26_DSP_VIRTUAL1_MBOX_1, CS40L26_STOP_PLAYBACK, CS40L26_DSP_MBOX_RESET); if (ret) { @@ -1708,8 +1930,6 @@ static void cs40l26_vibe_stop_worker(struct work_struct *work) goto mutex_exit; } - cs40l26_vibe_state_set(cs40l26, CS40L26_VIBE_STATE_STOPPED); - mutex_exit: mutex_unlock(&cs40l26->lock); pm_runtime_mark_last_busy(cs40l26->dev); @@ -1720,6 +1940,8 @@ static void cs40l26_set_gain(struct input_dev *dev, u16 gain) { struct cs40l26_private *cs40l26 = input_get_drvdata(dev); + dev_dbg(cs40l26->dev, "%s: gain = %u\n", __func__, gain); + if (gain < 0 || gain >= CS40L26_NUM_PCT_MAP_VALUES) { dev_err(cs40l26->dev, "Gain value %u out of bounds\n", gain); return; @@ -1745,7 +1967,7 @@ static int cs40l26_playback_effect(struct input_dev *dev, return -EINVAL; } - cs40l26->effect = effect; + cs40l26->trigger_effect = effect; if (val > 0) queue_work(cs40l26->vibe_workqueue, &cs40l26->vibe_start_work); @@ -1785,6 +2007,19 @@ int cs40l26_get_num_waves(struct cs40l26_private *cs40l26, u32 *num_waves) } EXPORT_SYMBOL(cs40l26_get_num_waves); +static struct cl_dsp_owt_header *cs40l26_header(struct cs40l26_private *cs40l26, + u8 index) +{ + if (!cs40l26->dsp || !cs40l26->dsp->wt_desc) + return NULL; + + if (index >= cs40l26->dsp->wt_desc->owt.nwaves) + return NULL; + + return &cs40l26->dsp->wt_desc->owt.waves[index]; + +} + static int cs40l26_owt_get_wlength(struct cs40l26_private *cs40l26, u8 index) { struct device *dev = cs40l26->dev; @@ -1794,7 +2029,9 @@ static int cs40l26_owt_get_wlength(struct cs40l26_private *cs40l26, u8 index) if (index == 0) return 0; - entry = &cs40l26->dsp->wt_desc->owt.waves[index]; + entry = cs40l26_header(cs40l26, index); + if (entry == NULL) + return -EINVAL; switch (entry->type) { case WT_TYPE_V6_PCM_F0_REDC: @@ -1812,6 +2049,26 @@ static int cs40l26_owt_get_wlength(struct cs40l26_private *cs40l26, u8 index) return cl_dsp_memchunk_read(&ch, 24); } +static void cs40l26_owt_set_section_info(struct cs40l26_private *cs40l26, + struct cl_dsp_memchunk *ch, + struct cs40l26_owt_section *sections, u8 nsections) +{ + int i; + + for (i = 0; i < nsections; i++) { + cl_dsp_memchunk_write(ch, 8, sections[i].amplitude); + cl_dsp_memchunk_write(ch, 8, sections[i].index); + cl_dsp_memchunk_write(ch, 8, sections[i].repeat); + cl_dsp_memchunk_write(ch, 8, sections[i].flags); + cl_dsp_memchunk_write(ch, 16, sections[i].delay); + + if (sections[i].flags & CS40L26_WT_TYPE10_COMP_DURATION_FLAG) { + cl_dsp_memchunk_write(ch, 8, 0x00); /* Pad */ + cl_dsp_memchunk_write(ch, 16, sections[i].duration); + } + } +} + static void cs40l26_owt_get_section_info(struct cs40l26_private *cs40l26, struct cl_dsp_memchunk *ch, struct cs40l26_owt_section *sections, u8 nsections) @@ -1819,7 +2076,7 @@ static void cs40l26_owt_get_section_info(struct cs40l26_private *cs40l26, int i; for (i = 0; i < nsections; i++) { - cl_dsp_memchunk_read(ch, 8); /* Skip amplitude */ + sections[i].amplitude = cl_dsp_memchunk_read(ch, 8); sections[i].index = cl_dsp_memchunk_read(ch, 8); sections[i].repeat = cl_dsp_memchunk_read(ch, 8); sections[i].flags = cl_dsp_memchunk_read(ch, 8); @@ -1833,23 +2090,16 @@ static void cs40l26_owt_get_section_info(struct cs40l26_private *cs40l26, } static int cs40l26_owt_calculate_wlength(struct cs40l26_private *cs40l26, - s16 *data, u32 data_size, u32 *owt_wlen) + u8 nsections, u8 global_rep, u8 *data, u32 data_size_bytes, + u32 *owt_wlen) { - u32 data_size_bytes = data_size * 2; struct cl_dsp_memchunk ch; u32 total_len = 0, section_len = 0, loop_len = 0; bool in_loop = false; struct cs40l26_owt_section *sections; int ret = 0, i, wlen_whole; - u8 nsections, global_rep; u32 dlen, wlen; - ch = cl_dsp_memchunk_create((void *) data, data_size_bytes); - - cl_dsp_memchunk_read(&ch, 8); /* Skip padding */ - nsections = cl_dsp_memchunk_read(&ch, 8); - global_rep = cl_dsp_memchunk_read(&ch, 8); - if (nsections < 1) { dev_err(cs40l26->dev, "Not enough sections for composite\n"); return -EINVAL; @@ -1857,11 +2107,10 @@ static int cs40l26_owt_calculate_wlength(struct cs40l26_private *cs40l26, sections = kcalloc(nsections, sizeof(struct cs40l26_owt_section), GFP_KERNEL); - if (!sections) { - dev_err(cs40l26->dev, "Failed to allocate OWT sections\n"); + if (!sections) return -ENOMEM; - } + ch = cl_dsp_memchunk_create((void *) data, data_size_bytes); cs40l26_owt_get_section_info(cs40l26, &ch, sections, nsections); for (i = 0; i < nsections; i++) { @@ -1924,8 +2173,8 @@ err_free: return ret; } -static int cs40l26_owt_add(struct cs40l26_private *cs40l26, u32 wlen, - int effect_id, u32 index) +static int cs40l26_owt_add(struct cs40l26_private *cs40l26, int effect_id, + u32 index) { struct cs40l26_owt *owt_new; @@ -1934,7 +2183,6 @@ static int cs40l26_owt_add(struct cs40l26_private *cs40l26, u32 wlen, return -ENOMEM; owt_new->effect_id = effect_id; - owt_new->wlength = wlen; owt_new->trigger_index = index; list_add(&owt_new->list, &cs40l26->owt_head); @@ -1943,51 +2191,13 @@ static int cs40l26_owt_add(struct cs40l26_private *cs40l26, u32 wlen, return 0; } -static int cs40l26_owt_upload(struct cs40l26_private *cs40l26, s16 *data, - u32 data_size, u32 *wlength) +static int cs40l26_owt_upload(struct cs40l26_private *cs40l26, u8 *data, + u32 data_size_bytes) { - bool pwle = (data[0] == 0x0000) ? false : true; - u32 data_size_bytes = (data_size * 2) + (pwle ? 0 : 4); struct device *dev = cs40l26->dev; struct cl_dsp *dsp = cs40l26->dsp; unsigned int write_reg, reg, wt_offset, wt_size_words, wt_base; - u32 owt_wlength, full_data_size_bytes = 12 + data_size_bytes; - struct cl_dsp_memchunk data_ch; - u8 *full_data; - int ret = 0; - - full_data = kcalloc(full_data_size_bytes, sizeof(u8), GFP_KERNEL); - if (!full_data) - return -ENOMEM; - - data_ch = cl_dsp_memchunk_create((void *) full_data, - full_data_size_bytes); - cl_dsp_memchunk_write(&data_ch, 16, CS40L26_WT_HEADER_DEFAULT_FLAGS); - - if (pwle) - cl_dsp_memchunk_write(&data_ch, 8, WT_TYPE_V6_PWLE); - else - cl_dsp_memchunk_write(&data_ch, 8, WT_TYPE_V6_COMPOSITE); - - cl_dsp_memchunk_write(&data_ch, 24, CS40L26_WT_HEADER_OFFSET); - cl_dsp_memchunk_write(&data_ch, 24, data_size_bytes / - CL_DSP_BYTES_PER_WORD); - - if (!pwle) { /* Wlength is included in PWLE raw data */ - ret = cs40l26_owt_calculate_wlength(cs40l26, data, data_size, - &owt_wlength); - if (ret) - goto err_free; - - cl_dsp_memchunk_write(&data_ch, 24, owt_wlength); - } - - memcpy(full_data + data_ch.bytes, data, data_size_bytes); - - if (pwle) - owt_wlength = (full_data[data_ch.bytes + 1] << 16) | - (full_data[data_ch.bytes + 2] << 8) | - full_data[data_ch.bytes + 3]; + int ret; pm_runtime_get_sync(dev); @@ -2013,7 +2223,7 @@ static int cs40l26_owt_upload(struct cs40l26_private *cs40l26, s16 *data, goto err_pm; } - if ((wt_size_words * 3) < full_data_size_bytes) { + if ((wt_size_words * CL_DSP_BYTES_PER_WORD) < data_size_bytes) { dev_err(dev, "No space for OWT waveform\n"); ret = -ENOSPC; goto err_pm; @@ -2026,8 +2236,8 @@ static int cs40l26_owt_upload(struct cs40l26_private *cs40l26, s16 *data, write_reg = wt_base + (wt_offset * 4); - ret = cl_dsp_raw_write(cs40l26->dsp, write_reg, full_data, - full_data_size_bytes, CL_DSP_MAX_WLEN); + ret = cl_dsp_raw_write(cs40l26->dsp, write_reg, data, data_size_bytes, + CL_DSP_MAX_WLEN); if (ret) { dev_err(dev, "Failed to sync OWT\n"); goto err_pm; @@ -2039,76 +2249,430 @@ static int cs40l26_owt_upload(struct cs40l26_private *cs40l26, s16 *data, goto err_pm; dev_dbg(dev, "Successfully wrote waveform (%u bytes) to 0x%08X\n", - full_data_size_bytes, write_reg); - - *wlength = owt_wlength; + data_size_bytes, write_reg); err_pm: pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); -err_free: - kfree(full_data); + return ret; +} + +static u8 *cs40l26_ncw_amp_scaling(struct cs40l26_private *cs40l26, u8 amp, + u8 nsections, void *in_data, u32 data_bytes) +{ + struct cs40l26_owt_section *sections; + struct cl_dsp_memchunk in_ch, out_ch; + u16 amp_product; + u8 *out_data; + int i; + + if (nsections <= 0) { + dev_err(cs40l26->dev, "Too few sections for NCW\n"); + return NULL; + } + + sections = kcalloc(nsections, sizeof(struct cs40l26_owt_section), + GFP_KERNEL); + if (!sections) + return NULL; + + in_ch = cl_dsp_memchunk_create(in_data, data_bytes); + + cs40l26_owt_get_section_info(cs40l26, &in_ch, sections, nsections); + + for (i = 0; i < nsections; i++) { + if (sections[i].index != 0) { + amp_product = sections[i].amplitude * amp; + sections[i].amplitude = + (u8) DIV_ROUND_UP(amp_product, 100); + } + } + + out_data = kcalloc(data_bytes, sizeof(u8), GFP_KERNEL); + if (!out_data) + goto sections_free; + + out_ch = cl_dsp_memchunk_create((void *) out_data, data_bytes); + cs40l26_owt_set_section_info(cs40l26, &out_ch, sections, nsections); + + +sections_free: + kfree(sections); + + return out_data; +} + +static int cs40l26_owt_comp_data_size(struct cs40l26_private *cs40l26, + u8 nsections, struct cs40l26_owt_section *sections) +{ + int i, size = 0; + struct cl_dsp_owt_header *header; + + for (i = 0; i < nsections; i++) { + if (sections[i].index == 0) { + size += CS40L26_WT_TYPE10_SECTION_BYTES_MIN; + continue; + } + + header = cs40l26_header(cs40l26, sections[i].index); + if (header == NULL) + return -ENOMEM; + + if (header->type == WT_TYPE_V6_COMPOSITE) { + size += (header->size - 2) * 4; + + if (section_complete(§ions[i])) + size += CS40L26_WT_TYPE10_SECTION_BYTES_MIN; + } else { + size += sections[i].duration ? + CS40L26_WT_TYPE10_SECTION_BYTES_MAX : + CS40L26_WT_TYPE10_SECTION_BYTES_MIN; + } + } + + return size; +} + +static int cs40l26_refactor_owt(struct cs40l26_private *cs40l26, s16 *in_data, + u32 in_data_nibbles, bool pwle, u8 **out_data) +{ + u8 nsections, global_rep, out_nsections = 0; + int ret = 0, pos_byte = 0, in_pos_nib = 2; + int in_data_bytes = 2 * in_data_nibbles; + int out_data_bytes = 0, data_bytes = 0; + struct device *dev = cs40l26->dev; + u8 delay_section_data[CS40L26_WT_TYPE10_SECTION_BYTES_MIN]; + u8 ncw_nsections, ncw_global_rep, *data, *ncw_data; + struct cs40l26_owt_section *sections; + struct cl_dsp_memchunk ch, out_ch; + struct cl_dsp_owt_header *header; + u16 section_size_bytes; + u32 ncw_bytes, wlen; + int i; + + if (pwle) { + out_data_bytes = CS40L26_WT_HEADER_PWLE_SIZE + in_data_bytes; + *out_data = kcalloc(out_data_bytes, sizeof(u8), GFP_KERNEL); + if (!*out_data) { + dev_err(dev, "No space for refactored data\n"); + return -ENOMEM; + } + + out_ch = cl_dsp_memchunk_create((void *) *out_data, + out_data_bytes); + cl_dsp_memchunk_write(&out_ch, 16, + CS40L26_WT_HEADER_DEFAULT_FLAGS); + cl_dsp_memchunk_write(&out_ch, 8, WT_TYPE_V6_PWLE); + cl_dsp_memchunk_write(&out_ch, 24, CS40L26_WT_HEADER_OFFSET); + cl_dsp_memchunk_write(&out_ch, 24, in_data_bytes / 4); + + memcpy(*out_data + out_ch.bytes, in_data, in_data_bytes); + + return out_data_bytes; + } + + ch = cl_dsp_memchunk_create((void *) in_data, in_data_bytes); + cl_dsp_memchunk_read(&ch, 8); /* Skip padding */ + nsections = cl_dsp_memchunk_read(&ch, 8); + global_rep = cl_dsp_memchunk_read(&ch, 8); + + sections = kcalloc(nsections, sizeof(struct cs40l26_owt_section), + GFP_KERNEL); + if (!sections) + return -ENOMEM; + + cs40l26_owt_get_section_info(cs40l26, &ch, sections, nsections); + + data_bytes = cs40l26_owt_comp_data_size(cs40l26, nsections, + sections); + if (data_bytes <= 0) { + dev_err(dev, "Failed to get OWT Composite Data Size\n"); + ret = data_bytes; + goto sections_err_free; + } + + data = kcalloc(data_bytes, sizeof(u8), GFP_KERNEL); + if (!data) { + ret = -ENOMEM; + goto sections_err_free; + } + + cl_dsp_memchunk_flush(&ch); + memset(&delay_section_data, 0, CS40L26_WT_TYPE10_SECTION_BYTES_MIN); + + for (i = 0; i < nsections; i++) { + section_size_bytes = sections[i].duration ? + CS40L26_WT_TYPE10_SECTION_BYTES_MAX : + CS40L26_WT_TYPE10_SECTION_BYTES_MIN; + + if (sections[i].index == 0) { + memcpy(data + pos_byte, in_data + in_pos_nib, + section_size_bytes); + pos_byte += section_size_bytes; + in_pos_nib += section_size_bytes / 2; + out_nsections++; + continue; + } + + if (sections[i].repeat != 0) { + dev_err(dev, "Inner repeats not allowed for NCWs\n"); + ret = -EPERM; + goto data_err_free; + } + + header = cs40l26_header(cs40l26, sections[i].index); + if (header == NULL) { + ret = -ENOMEM; + goto data_err_free; + } + + if (header->type == WT_TYPE_V6_COMPOSITE) { + ch = cl_dsp_memchunk_create(header->data, 8); + cl_dsp_memchunk_read(&ch, 24); /* Skip Wlength */ + cl_dsp_memchunk_read(&ch, 8); /* Skip Padding */ + ncw_nsections = cl_dsp_memchunk_read(&ch, 8); + + ncw_global_rep = cl_dsp_memchunk_read(&ch, 8); + if (ncw_global_rep != 0) { + dev_err(dev, + "No NCW support for outer repeat\n"); + ret = -EPERM; + goto data_err_free; + } + + cl_dsp_memchunk_flush(&ch); + + ncw_bytes = (header->size - 2) * 4; + ncw_data = cs40l26_ncw_amp_scaling(cs40l26, + sections[i].amplitude, ncw_nsections, + header->data + 8, ncw_bytes); + if (ncw_data == NULL) { + ret = -ENOMEM; + goto data_err_free; + } + + memcpy(data + pos_byte, ncw_data, ncw_bytes); + pos_byte += ncw_bytes; + out_nsections += ncw_nsections; + kfree(ncw_data); + + if (section_complete(§ions[i])) { + ch = cl_dsp_memchunk_create((void *) + delay_section_data, + CS40L26_WT_TYPE10_SECTION_BYTES_MIN); + + cl_dsp_memchunk_write(&ch, 24, 0x000000); + cl_dsp_memchunk_write(&ch, 8, 0x00); + cl_dsp_memchunk_write(&ch, 16, + sections[i].delay); + + memcpy(data + pos_byte, + delay_section_data, + CS40L26_WT_TYPE10_SECTION_BYTES_MIN); + + cl_dsp_memchunk_flush(&ch); + + pos_byte += + CS40L26_WT_TYPE10_SECTION_BYTES_MIN; + out_nsections++; + } + } else { + memcpy(data + pos_byte, in_data + in_pos_nib, + section_size_bytes); + pos_byte += section_size_bytes; + out_nsections++; + } + in_pos_nib += section_size_bytes / 2; + } + + out_data_bytes = data_bytes + CS40L26_WT_HEADER_COMP_SIZE; + *out_data = kcalloc(out_data_bytes, sizeof(u8), GFP_KERNEL); + if (!*out_data) { + dev_err(dev, "Failed to allocate space for composite\n"); + ret = -ENOMEM; + goto data_err_free; + } + + out_ch = cl_dsp_memchunk_create((void *) *out_data, out_data_bytes); + cl_dsp_memchunk_write(&out_ch, 16, CS40L26_WT_HEADER_DEFAULT_FLAGS); + cl_dsp_memchunk_write(&out_ch, 8, WT_TYPE_V6_COMPOSITE); + cl_dsp_memchunk_write(&out_ch, 24, CS40L26_WT_HEADER_OFFSET); + cl_dsp_memchunk_write(&out_ch, 24, data_bytes / CL_DSP_BYTES_PER_WORD); + + ret = cs40l26_owt_calculate_wlength(cs40l26, out_nsections, global_rep, + data, data_bytes, &wlen); + if (ret) + goto data_err_free; + + cl_dsp_memchunk_write(&out_ch, 24, wlen); + cl_dsp_memchunk_write(&out_ch, 8, 0x00); /* Pad */ + cl_dsp_memchunk_write(&out_ch, 8, out_nsections); + cl_dsp_memchunk_write(&out_ch, 8, global_rep); + + memcpy(*out_data + out_ch.bytes, data, data_bytes); + +data_err_free: + kfree(data); +sections_err_free: + kfree(sections); + + return ret ? ret : out_data_bytes; +} + +static u8 cs40l26_get_lowest_free_buzzgen(struct cs40l26_private *cs40l26) +{ + int buzzgen, i; + + /* 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 (i == FF_MAX_EFFECTS) + break; + } + + return buzzgen; +} + +static int cs40l26_sine_upload(struct cs40l26_private *cs40l26, + struct ff_effect *effect) +{ + 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; + } + + 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; + } + + 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); + + if (lowest_free_buzzgen > CS40L26_BUZZGEN_NUM_CONFIGS) { + dev_err(dev, "Unable to upload buzzgen effect\n"); + 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); return ret; } -static int cs40l26_upload_effect(struct input_dev *dev, - struct ff_effect *effect, struct ff_effect *old) +static void cs40l26_upload_worker(struct work_struct *work) { - struct cs40l26_private *cs40l26 = input_get_drvdata(dev); + struct cs40l26_private *cs40l26 = container_of(work, + struct cs40l26_private, upload_work); struct device *cdev = cs40l26->dev; - s16 *raw_custom_data = NULL; - int ret = 0; - u32 trigger_index, min_index, max_index, nwaves, owt_wlen; + u8 *refactored_data = NULL; + int ret = 0, refactored_size, len; + u32 trigger_index, min_index, max_index, nwaves; + struct ff_effect *effect; u16 index, bank; + bool pwle; + + pm_runtime_get_sync(cdev); + mutex_lock(&cs40l26->lock); + + effect = &cs40l26->upload_effect; if (effect->type != FF_PERIODIC) { dev_err(cdev, "Effect type 0x%X not supported\n", effect->type); - return -EINVAL; - } - - if (effect->replay.length < 0 || - effect->replay.length > CS40L26_TIMEOUT_MS_MAX) { - dev_err(cdev, "Invalid playback duration: %d ms\n", - effect->replay.length); - return -EINVAL; + ret = -EINVAL; + goto out_mutex; } switch (effect->u.periodic.waveform) { case FF_CUSTOM: - raw_custom_data = - kzalloc(sizeof(s16) * - effect->u.periodic.custom_len, GFP_KERNEL); - if (!raw_custom_data) - return -ENOMEM; + pwle = (cs40l26->raw_custom_data[0] == 0x0000) ? false : true; - if (copy_from_user(raw_custom_data, - effect->u.periodic.custom_data, - sizeof(s16) * effect->u.periodic.custom_len)) { - dev_err(cdev, "Failed to get user data\n"); - ret = -EFAULT; - goto out_free; - } + len = effect->u.periodic.custom_len; + + if (len > CS40L26_CUSTOM_DATA_SIZE) { + refactored_size = cs40l26_refactor_owt(cs40l26, + cs40l26->raw_custom_data, len, pwle, + &refactored_data); + + if (refactored_size <= 0) { + dev_err(cdev, + "Failed to refactor OWT waveform\n"); + ret = refactored_size; + goto out_mutex; + } - if (effect->u.periodic.custom_len > CS40L26_CUSTOM_DATA_SIZE) { - ret = cs40l26_owt_upload(cs40l26, raw_custom_data, - effect->u.periodic.custom_len, &owt_wlen); + ret = cs40l26_owt_upload(cs40l26, refactored_data, + refactored_size); if (ret) goto out_free; bank = CS40L26_OWT_BANK_ID; index = cs40l26->num_owt_effects; } else { - bank = ((u16) raw_custom_data[0]); - index = ((u16) raw_custom_data[1]) & + bank = ((u16) cs40l26->raw_custom_data[0]); + index = ((u16) cs40l26->raw_custom_data[1]) & CS40L26_MAX_INDEX_MASK; } + ret = cs40l26_get_num_waves(cs40l26, &nwaves); + if (ret) + goto out_free; + switch (bank) { case CS40L26_RAM_BANK_ID: - min_index = CS40L26_RAM_INDEX_START; - max_index = min_index + cs40l26->num_waves - 1; + 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; @@ -2129,14 +2693,15 @@ static int cs40l26_upload_effect(struct input_dev *dev, if (trigger_index >= min_index && trigger_index <= max_index) { cs40l26->trigger_indices[effect->id] = trigger_index; } else { - dev_err(cdev, "Trigger index (0x%X) out of bounds\n", - trigger_index); + 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; } if (is_owt(trigger_index)) { - ret = cs40l26_owt_add(cs40l26, owt_wlen, effect->id, + ret = cs40l26_owt_add(cs40l26, effect->id, trigger_index); if (ret) goto out_free; @@ -2144,33 +2709,26 @@ static int cs40l26_upload_effect(struct input_dev *dev, dev_dbg(cdev, "%s: ID = %u, index = 0x%08X\n", __func__, effect->id, trigger_index); + + if (effect->trigger.button) { + ret = cs40l26_map_gpi_to_haptic(cs40l26, + trigger_index, bank, effect); + if (ret) + goto out_free; + } + break; case FF_SINE: - if (effect->u.periodic.period) { - if (effect->u.periodic.period - < CS40L26_BUZZGEN_PERIOD_MIN - || effect->u.periodic.period - > CS40L26_BUZZGEN_PERIOD_MAX) { - dev_err(cdev, - "%u ms period not within range (4-10 ms)\n", - effect->u.periodic.period); - return -EINVAL; - } - } else { - dev_err(cdev, "Sine wave period not specified\n"); - return -EINVAL; - } + ret = cs40l26_sine_upload(cs40l26, effect); + if (ret) + goto out_mutex; + break; default: dev_err(cdev, "Periodic waveform type 0x%X not supported\n", effect->u.periodic.waveform); - return -EINVAL; - } - - if (effect->trigger.button) { - ret = cs40l26_gpio_index_set(cs40l26, effect); - if (ret) - goto out_free; + ret = -EINVAL; + goto out_mutex; } ret = cs40l26_get_num_waves(cs40l26, &nwaves); @@ -2179,9 +2737,57 @@ static int cs40l26_upload_effect(struct input_dev *dev, dev_dbg(cdev, "Total number of waveforms = %u\n", nwaves); +out_free: + kfree(refactored_data); + +out_mutex: + mutex_unlock(&cs40l26->lock); + pm_runtime_mark_last_busy(cdev); + pm_runtime_put_autosuspend(cdev); + + cs40l26->upload_ret = ret; +} + +static int cs40l26_upload_effect(struct input_dev *dev, + struct ff_effect *effect, struct ff_effect *old) +{ + struct cs40l26_private *cs40l26 = input_get_drvdata(dev); + int len = effect->u.periodic.custom_len; + int ret; + + dev_dbg(cs40l26->dev, "%s: effect ID = %d\n", __func__, effect->id); + + memcpy(&cs40l26->upload_effect, effect, sizeof(struct ff_effect)); + + if (effect->u.periodic.waveform == FF_CUSTOM) { + cs40l26->raw_custom_data_len = len; + + cs40l26->raw_custom_data = kcalloc(len, sizeof(s16), + GFP_KERNEL); + if (!cs40l26->raw_custom_data) { + ret = -ENOMEM; + goto out_free; + } + + if (copy_from_user(cs40l26->raw_custom_data, + effect->u.periodic.custom_data, + sizeof(s16) * len)) { + dev_err(cs40l26->dev, "Failed to get user data\n"); + ret = -EFAULT; + goto out_free; + } + } + + queue_work(cs40l26->vibe_workqueue, &cs40l26->upload_work); + + /* Wait for upload to finish */ + flush_work(&cs40l26->upload_work); + + ret = cs40l26->upload_ret; out_free: - kfree(raw_custom_data); + memset(&cs40l26->upload_effect, 0, sizeof(struct ff_effect)); + kfree(cs40l26->raw_custom_data); return ret; } @@ -2194,18 +2800,80 @@ const struct attribute_group *cs40l26_dev_attr_groups[] = { }; #endif -static int cs40l26_erase_effect(struct input_dev *dev, int effect_id) +static int cs40l26_clear_gpi_event_reg(struct cs40l26_private *cs40l26, u32 reg) { - struct cs40l26_private *cs40l26 = input_get_drvdata(dev); + struct regmap *regmap = cs40l26->regmap; + int ret; + + ret = regmap_write(regmap, reg, 0); + if (ret) + dev_err(cs40l26->dev, "Failed to clear gpi reg: %08X", reg); + + return ret; +} + +static int cs40l26_erase_gpi_mapping(struct cs40l26_private *cs40l26, + int effect_id) +{ + struct device *dev = cs40l26->dev; u32 index = cs40l26->trigger_indices[effect_id]; + u8 trigger_index, gpi_index; + u32 reg, val; + int i, ret; + + trigger_index = index & 0xFF; + + for (i = 0; i < CS40L26_EVENT_MAP_NUM_GPI_REGS; i++) { + reg = cs40l26->event_map_base + (i * 4); + ret = regmap_read(cs40l26->regmap, reg, &val); + if (ret) { + dev_err(dev, "Failed to read gpi event reg: %u", reg); + return ret; + } + gpi_index = val & 0xFF; + + if (is_buzz(index) || + (is_owt(index) && val & CS40L26_BTN_OWT_MASK) || + (is_ram(index) && val & CS40L26_BTN_BANK_MASK) || + (is_rom(index) && ~val & CS40L26_BTN_BANK_MASK)) { + if (trigger_index == gpi_index) { + ret = cs40l26_clear_gpi_event_reg(cs40l26, reg); + if (ret) + return ret; + } + } + } + + 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) +{ u32 cmd = CS40L26_DSP_MBOX_CMD_OWT_DELETE_BASE; struct cs40l26_owt *owt, *owt_tmp; + u32 index; int ret; - if (!is_owt(index)) - return 0; + owt = cs40l26_owt_find(cs40l26, effect_id); + if (owt == NULL) + return -ENOMEM; + + cmd |= (owt->trigger_index & 0xFF); + + ret = cs40l26_ack_write(cs40l26, CS40L26_DSP_VIRTUAL1_MBOX_1, cmd, + CS40L26_DSP_MBOX_RESET); + if (ret) + 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--; @@ -2213,29 +2881,79 @@ static int cs40l26_erase_effect(struct input_dev *dev, int effect_id) } } - owt = cs40l26_owt_find(cs40l26, effect_id); - if (owt == NULL) - return -ENOMEM; - - cmd |= (owt->trigger_index & 0xFF); - list_del(&owt->list); kfree(owt); + cs40l26->num_owt_effects--; + + return 0; +} + + +static void cs40l26_erase_worker(struct work_struct *work) +{ + struct cs40l26_private *cs40l26 = container_of(work, + struct cs40l26_private, erase_work); + int ret = 0; + int effect_id; + u32 index; + mutex_lock(&cs40l26->lock); pm_runtime_get_sync(cs40l26->dev); - ret = cs40l26_ack_write(cs40l26, CS40L26_DSP_VIRTUAL1_MBOX_1, cmd, - CS40L26_DSP_MBOX_RESET); - if (ret) - goto pm_err; + effect_id = cs40l26->erase_effect->id; + index = cs40l26->trigger_indices[effect_id]; - cs40l26->num_owt_effects--; + dev_dbg(cs40l26->dev, "%s: effect ID = %d\n", __func__, effect_id); -pm_err: + if (is_owt(index)) { + ret = cs40l26_erase_owt(cs40l26, effect_id); + if (ret) + dev_err(cs40l26->dev, "Failed to erase OWT effect: %d", + ret); + goto out_mutex; + } + + if (is_buzz(index)) { + ret = cs40l26_erase_buzz(cs40l26, effect_id); + if (ret) + dev_err(cs40l26->dev, "Failed to erase buzz effect: %d", + ret); + goto out_mutex; + } + + ret = cs40l26_erase_gpi_mapping(cs40l26, effect_id); + if (ret) + dev_err(cs40l26->dev, "Failed to erase gpi mapping: %d", ret); + +out_mutex: + mutex_unlock(&cs40l26->lock); pm_runtime_mark_last_busy(cs40l26->dev); pm_runtime_put_autosuspend(cs40l26->dev); - return ret; + cs40l26->erase_ret = ret; +} + +static int cs40l26_erase_effect(struct input_dev *dev, int effect_id) +{ + struct cs40l26_private *cs40l26 = input_get_drvdata(dev); + struct ff_effect *effect; + + dev_dbg(cs40l26->dev, "%s: effect ID = %d\n", __func__, effect_id); + + effect = &dev->ff->effects[effect_id]; + if (!effect) { + dev_err(cs40l26->dev, "No such effect to erase\n"); + return -EINVAL; + } + + cs40l26->erase_effect = effect; + + queue_work(cs40l26->vibe_workqueue, &cs40l26->erase_work); + + /* Wait for erase to finish */ + flush_work(&cs40l26->erase_work); + + return cs40l26->erase_ret; } static int cs40l26_input_init(struct cs40l26_private *cs40l26) @@ -2396,7 +3114,9 @@ static int cs40l26_cl_dsp_init(struct cs40l26_private *cs40l26, u32 id) strncpy(cs40l26->fw.coeff_files[2], CS40L26_SVC_TUNING_FILE_NAME, CS40L26_SVC_TUNING_FILE_NAME_LEN); - + strncpy(cs40l26->fw.coeff_files[3], + CS40L26_DVL_FILE_NAME, + CS40L26_DVL_FILE_NAME_LEN); ret = cl_dsp_wavetable_create(cs40l26->dsp, CS40L26_VIBEGEN_ALGO_ID, CS40L26_WT_NAME_XM, CS40L26_WT_NAME_YM, CS40L26_WT_FILE_NAME); @@ -2725,7 +3445,7 @@ static int cs40l26_verify_fw(struct cs40l26_private *cs40l26) if (ret) return ret; - if (val < fw->min_rev) { + if ((val & ~CS40L26_FW_BRANCH_MASK) < fw->min_rev) { dev_err(cs40l26->dev, "Invalid firmware revision: %d.%d.%d\n", (int) CL_DSP_GET_MAJOR(val), (int) CL_DSP_GET_MINOR(val), @@ -2804,7 +3524,7 @@ static int cs40l26_bst_dcm_config(struct cs40l26_private *cs40l26) static int cs40l26_bst_ipk_config(struct cs40l26_private *cs40l26) { - u32 val, bst_ipk_ma = cs40l26->pdata.bst_ipk / 1000; + u32 val, bst_ipk_ma = cs40l26->pdata.bst_ipk / MILLIAMPS_PER_AMPS; int ret; if (bst_ipk_ma < CS40L26_BST_IPK_MILLIAMP_MIN || @@ -2812,7 +3532,7 @@ static int cs40l26_bst_ipk_config(struct cs40l26_private *cs40l26) val = CS40L26_BST_IPK_DEFAULT; dev_dbg(cs40l26->dev, "Using default BST_IPK\n"); } else { - val = (bst_ipk_ma / 50) - 16; + val = (bst_ipk_ma / CS40L26_BST_IPK_CTL_STEP_SIZE) - CS40L26_BST_IPK_CTL_RESERVED; } ret = regmap_write(cs40l26->regmap, CS40L26_BST_IPK_CTL, val); @@ -2861,8 +3581,8 @@ static int cs40l26_dsp_config(struct cs40l26_private *cs40l26) struct regmap *regmap = cs40l26->regmap; struct device *dev = cs40l26->dev; unsigned int val; + u32 reg, nwaves; int ret; - u32 reg; ret = cs40l26_verify_fw(cs40l26); if (ret) @@ -2888,15 +3608,15 @@ static int cs40l26_dsp_config(struct cs40l26_private *cs40l26) cs40l26->fw_loaded = true; - ret = cs40l26_dsp_start(cs40l26); + ret = cs40l26_pseq_init(cs40l26); if (ret) return ret; - ret = cs40l26_pseq_init(cs40l26); + ret = cs40l26_update_reg_defaults_via_pseq(cs40l26); if (ret) return ret; - ret = cs40l26_update_reg_defaults_via_pseq(cs40l26); + ret = cs40l26_dsp_start(cs40l26); if (ret) return ret; @@ -2935,6 +3655,13 @@ static int cs40l26_dsp_config(struct cs40l26_private *cs40l26) if (ret) return ret; + /* Send prevent hibernate to ensure we can read HALO STATE */ + ret = cs40l26_pm_state_transition(cs40l26, + CS40L26_PM_STATE_PREVENT_HIBERNATE); + if (ret) + return ret; + + /* ensure firmware running */ ret = regmap_read(regmap, reg, &val); if (ret) { dev_err(dev, "Failed to read HALO_STATE\n"); @@ -2946,6 +3673,11 @@ static int cs40l26_dsp_config(struct cs40l26_private *cs40l26) return ret; } + ret = cs40l26_pm_state_transition(cs40l26, + CS40L26_PM_STATE_ALLOW_HIBERNATE); + if (ret) + return ret; + ret = cs40l26_pm_runtime_setup(cs40l26); if (ret) return ret; @@ -2967,12 +3699,12 @@ static int cs40l26_dsp_config(struct cs40l26_private *cs40l26) if (ret) goto pm_err; - ret = cs40l26_get_num_waves(cs40l26, &cs40l26->num_waves); + ret = cs40l26_get_num_waves(cs40l26, &nwaves); if (ret) goto pm_err; dev_info(dev, "%s loaded with %u RAM waveforms\n", CS40L26_DEV_NAME, - cs40l26->num_waves); + nwaves); ret = cs40l26_owt_setup(cs40l26); if (ret) @@ -3036,7 +3768,7 @@ static int cs40l26_tuning_select_from_svc_le(struct cs40l26_private *cs40l26) break; } - if (i == 2) { + if (i == CS40L26_SVC_LE_MAX_ATTEMPTS) { dev_warn(cs40l26->dev, "Using default tunings\n"); } else { strncat(svc_bin_file, CS40L26_TUNING_FILE_SUFFIX, @@ -3054,8 +3786,6 @@ pm_err: pm_runtime_mark_last_busy(cs40l26->dev); pm_runtime_put_autosuspend(cs40l26->dev); - kfree(cs40l26->svc_le_vals); - return ret; } @@ -3063,23 +3793,41 @@ static void cs40l26_coeff_load(struct cs40l26_private *cs40l26) { struct device *dev = cs40l26->dev; const struct firmware *coeff; - int i; + int i, ret; for (i = 0; i < cs40l26->fw.num_coeff_files; i++) { - request_firmware(&coeff, cs40l26->fw.coeff_files[i], dev); - if (!coeff) { - dev_warn(dev, "Continuing...\n"); + ret = request_firmware(&coeff, cs40l26->fw.coeff_files[i], dev); + if (ret) { + dev_warn(dev, "Continuing..."); continue; } - if (cl_dsp_coeff_file_parse(cs40l26->dsp, coeff)) - dev_warn(dev, "Continuing...\n"); + ret = cl_dsp_coeff_file_parse(cs40l26->dsp, coeff); + if (ret) + dev_warn(dev, "Failed to load, %s, %d. Continuing...", + cs40l26->fw.coeff_files[i], ret); else dev_info(dev, "%s Loaded Successfully\n", cs40l26->fw.coeff_files[i]); + + release_firmware(coeff); } } +static int cs40l26_change_firmware_control_defaults( + struct cs40l26_private *cs40l26) +{ + struct device *dev = cs40l26->dev; + int ret; + + ret = cs40l26_pm_timer_timeout_ticks4_set(cs40l26, + cs40l26->pdata.pm_timer_timeout_ticks4); + if (ret) + dev_err(dev, "failed to set pm_timer_timeout_ticks4, %d", ret); + + return ret; +} + static int cs40l26_firmware_load(struct cs40l26_private *cs40l26, u32 id) { struct device *dev = cs40l26->dev; @@ -3097,45 +3845,104 @@ static int cs40l26_firmware_load(struct cs40l26_private *cs40l26, u32 id) ret = cl_dsp_firmware_parse(cs40l26->dsp, fw, true); release_firmware(fw); + if (ret) { + dev_err(dev, "cl_dsp_firmware_parse failed, %d", ret); + return ret; + } + + ret = cs40l26_change_firmware_control_defaults(cs40l26); + + if (ret) + dev_err(dev, "changing firmware control defaults failed %d", + ret); + return ret; } -int cs40l26_fw_swap(struct cs40l26_private *cs40l26, u32 id) +static int cs40l26_fw_upload(struct cs40l26_private *cs40l26, u32 id) { - struct device *dev = cs40l26->dev; int ret; - if (id == cs40l26->fw.id) { - dev_warn(dev, "Cannot swap to same ID as running firmware\n"); - return 0; - } - - cs40l26_pm_runtime_teardown(cs40l26); - - cs40l26->fw_loaded = false; - - disable_irq(cs40l26->irq); - ret = cs40l26_cl_dsp_init(cs40l26, id); if (ret) - goto irq_exit; + return ret; ret = cs40l26_dsp_pre_config(cs40l26); if (ret) - goto irq_exit; + return ret; ret = cs40l26_firmware_load(cs40l26, id); if (ret) - goto irq_exit; + return ret; + + if (cs40l26->num_svc_le_vals && id != CS40L26_FW_CALIB_ID) { + ret = cs40l26_dsp_config(cs40l26); + if (ret) + return ret; + + ret = cs40l26_tuning_select_from_svc_le(cs40l26); + if (ret) + return ret; + + cs40l26_pm_runtime_teardown(cs40l26); + + ret = cs40l26_dsp_pre_config(cs40l26); + if (ret) + return ret; + } cs40l26_coeff_load(cs40l26); - ret = cs40l26_dsp_config(cs40l26); + return cs40l26_dsp_config(cs40l26); +} -irq_exit: - enable_irq(cs40l26->irq); +int cs40l26_fw_swap(struct cs40l26_private *cs40l26, u32 id) +{ + struct device *dev = cs40l26->dev; + bool register_irq = false; + int ret; - return ret; + if (id == cs40l26->fw.id) { + dev_warn(dev, "Cannot swap to same ID as running firmware\n"); + return 0; + } + + if (cs40l26->fw_mode == CS40L26_FW_MODE_NONE) { + cs40l26->fw_mode = CS40L26_FW_MODE_RAM; + register_irq = true; + } else { + disable_irq(cs40l26->irq); + cs40l26_pm_runtime_teardown(cs40l26); + } + + if (cs40l26->fw_loaded) { + ret = cs40l26_pm_timeout_ms_set(cs40l26, + CS40L26_PM_TIMEOUT_MS_MAX); + if (ret) { + dev_err(cs40l26->dev, "Can't set pm_timeout %d\n", ret); + return ret; + } + } + + cs40l26->fw_loaded = false; + + ret = cs40l26_fw_upload(cs40l26, id); + if (ret) + return ret; + + if (register_irq) { + ret = devm_request_threaded_irq(dev, cs40l26->irq, NULL, cs40l26_irq, + IRQF_ONESHOT | IRQF_SHARED | IRQF_TRIGGER_LOW, + "cs40l26", cs40l26); + if (ret) { + dev_err(dev, "Failed to request threaded IRQ: %d\n", ret); + return ret; + } + } else { + enable_irq(cs40l26->irq); + } + + return 0; } EXPORT_SYMBOL(cs40l26_fw_swap); @@ -3153,7 +3960,7 @@ static int cs40l26_update_reg_defaults(struct cs40l26_private *cs40l26) static int cs40l26_handle_svc_le_nodes(struct cs40l26_private *cs40l26) { struct device *dev = cs40l26->dev; - int i, init_count, node_count = 0; + int i, ret = 0, init_count, node_count = 0; struct fwnode_handle *child; unsigned int min, max, index; const char *node_name; @@ -3162,9 +3969,12 @@ static int cs40l26_handle_svc_le_nodes(struct cs40l26_private *cs40l26) if (!init_count) return 0; - cs40l26->svc_le_vals = kcalloc(init_count, + cs40l26->svc_le_vals = devm_kcalloc(dev, init_count, sizeof(struct cs40l26_svc_le *), GFP_KERNEL); + if (!cs40l26->svc_le_vals) + return -ENOMEM; + device_for_each_child_node(dev, child) { node_name = fwnode_get_name(child); @@ -3202,7 +4012,13 @@ static int cs40l26_handle_svc_le_nodes(struct cs40l26_private *cs40l26) } cs40l26->svc_le_vals[node_count] = - kzalloc(sizeof(struct cs40l26_svc_le), GFP_KERNEL); + devm_kzalloc(dev, sizeof(struct cs40l26_svc_le), + GFP_KERNEL); + + if (!cs40l26->svc_le_vals[node_count]) { + ret = -ENOMEM; + goto err; + } cs40l26->svc_le_vals[node_count]->min = min; cs40l26->svc_le_vals[node_count]->max = max; @@ -3215,6 +4031,10 @@ static int cs40l26_handle_svc_le_nodes(struct cs40l26_private *cs40l26) init_count - node_count); return node_count; + +err: + devm_kfree(dev, cs40l26->svc_le_vals); + return ret; } static int cs40l26_handle_platform_data(struct cs40l26_private *cs40l26) @@ -3240,6 +4060,9 @@ static int cs40l26_handle_platform_data(struct cs40l26_private *cs40l26) else cs40l26->fw_mode = CS40L26_FW_MODE_RAM; + if (of_property_read_bool(np, "cirrus,fw-defer")) + cs40l26->fw_mode = CS40L26_FW_MODE_NONE; + cs40l26->pdata.vbbr_en = of_property_read_bool(np, "cirrus,vbbr-enable"); @@ -3306,6 +4129,12 @@ static int cs40l26_handle_platform_data(struct cs40l26_private *cs40l26) else cs40l26->pdata.bst_ipk = 0; + if (!of_property_read_u32(np, "cirrus,pm-timer-timeout-ticks4", &val)) + cs40l26->pdata.pm_timer_timeout_ticks4 = val; + else + cs40l26->pdata.pm_timer_timeout_ticks4 = + CS40L26_PM_TIMER_TIMEOUT_TICKS4_DEFAULT; + ret = cs40l26_handle_svc_le_nodes(cs40l26); if (ret < 0) cs40l26->num_svc_le_vals = 0; @@ -3333,15 +4162,8 @@ int cs40l26_probe(struct cs40l26_private *cs40l26, INIT_WORK(&cs40l26->vibe_start_work, cs40l26_vibe_start_worker); INIT_WORK(&cs40l26->vibe_stop_work, cs40l26_vibe_stop_worker); INIT_WORK(&cs40l26->set_gain_work, cs40l26_set_gain_worker); - - cs40l26->asp_workqueue = alloc_ordered_workqueue("asp_workqueue", - WQ_HIGHPRI); - if (!cs40l26->asp_workqueue) { - ret = -ENOMEM; - goto err; - } - - INIT_WORK(&cs40l26->asp_work, cs40l26_asp_worker); + INIT_WORK(&cs40l26->upload_work, cs40l26_upload_worker); + INIT_WORK(&cs40l26->erase_work, cs40l26_erase_worker); ret = devm_regulator_bulk_get(dev, CS40L26_NUM_SUPPLIES, cs40l26_supplies); @@ -3396,61 +4218,23 @@ int cs40l26_probe(struct cs40l26_private *cs40l26, goto err; } - ret = devm_request_threaded_irq(dev, cs40l26->irq, NULL, cs40l26_irq, - IRQF_ONESHOT | IRQF_SHARED | IRQF_TRIGGER_LOW, - "cs40l26", cs40l26); - if (ret) { - dev_err(dev, "Failed to request threaded IRQ\n"); - goto err; - } - - /* the /ALERT pin may be asserted prior to firmware initialization. - * Disable the interrupt handler until firmware has downloaded - * so erroneous interrupt requests are ignored - */ - disable_irq(cs40l26->irq); - cs40l26->pm_ready = false; cs40l26->fw_loaded = false; - ret = cs40l26_cl_dsp_init(cs40l26, CS40L26_FW_ID); - if (ret) - goto irq_err; - - ret = cs40l26_dsp_pre_config(cs40l26); - if (ret) - goto irq_err; - - ret = cs40l26_firmware_load(cs40l26, cs40l26->fw.id); - if (ret) - goto irq_err; - - if (cs40l26->num_svc_le_vals) { - ret = cs40l26_dsp_config(cs40l26); - if (ret) - goto irq_err; - - enable_irq(cs40l26->irq); - - ret = cs40l26_tuning_select_from_svc_le(cs40l26); + if (cs40l26->fw_mode != CS40L26_FW_MODE_NONE) { + ret = cs40l26_fw_upload(cs40l26, CS40L26_FW_ID); if (ret) goto err; - disable_irq(cs40l26->irq); - cs40l26_pm_runtime_teardown(cs40l26); - - ret = cs40l26_dsp_pre_config(cs40l26); - if (ret) + ret = devm_request_threaded_irq(dev, cs40l26->irq, NULL, cs40l26_irq, + IRQF_ONESHOT | IRQF_SHARED | IRQF_TRIGGER_LOW, + "cs40l26", cs40l26); + if (ret) { + dev_err(dev, "Failed to request threaded IRQ\n"); goto err; - } - - cs40l26_coeff_load(cs40l26); - - ret = cs40l26_dsp_config(cs40l26); - if (ret) - goto irq_err; + } - enable_irq(cs40l26->irq); + } ret = cs40l26_input_init(cs40l26); if (ret) @@ -3464,9 +4248,6 @@ int cs40l26_probe(struct cs40l26_private *cs40l26, } return 0; - -irq_err: - enable_irq(cs40l26->irq); err: cs40l26_remove(cs40l26); @@ -3492,14 +4273,11 @@ int cs40l26_remove(struct cs40l26_private *cs40l26) cancel_work_sync(&cs40l26->vibe_start_work); cancel_work_sync(&cs40l26->vibe_stop_work); cancel_work_sync(&cs40l26->set_gain_work); + cancel_work_sync(&cs40l26->upload_work); + cancel_work_sync(&cs40l26->erase_work); destroy_workqueue(cs40l26->vibe_workqueue); } - if (cs40l26->asp_workqueue) { - cancel_work_sync(&cs40l26->asp_work); - destroy_workqueue(cs40l26->asp_workqueue); - } - if (vp_consumer) regulator_disable(vp_consumer); diff --git a/cs40l26/cs40l26.h b/cs40l26/cs40l26.h index 291d458..80fcabe 100644 --- a/cs40l26/cs40l26.h +++ b/cs40l26/cs40l26.h @@ -621,7 +621,7 @@ #define CS40L26_DSP1_PROM_30713 0x3C7DFE4 #define CS40L26_DSP1_PROM_30714 0x3C7DFE8 -#define CS40L26_MAX_I2C_READ_SIZE_BYTES 32 +#define CS40L26_MAX_I2C_READ_SIZE_WORDS 32 /* Register default changes */ #define CS40L26_TST_DAC_MSM_CONFIG_DEFAULT_CHANGE_VALUE_FULL 0x11330000 @@ -630,6 +630,9 @@ #define CS40L26_SPK_DEFAULT_HIZ_MASK BIT(28) #define CS40L26_SPK_DEFAULT_HIZ_SHIFT 28 +/* Firmware control defaults */ +#define CS40L26_PM_TIMER_TIMEOUT_TICKS4_DEFAULT 0x00001F40 + /* Device */ #define CS40L26_DEV_NAME "CS40L26" #define CS40L26_INPUT_DEV_NAME "cs40l26_input" @@ -669,10 +672,20 @@ #define CS40L26_DSP_STATE_STR_LEN 10 +#define CS40L26_DSP_STATE_ATTEMPTS 5 + +#define CS40L26_DSP_LOCK3_OFFSET 8 +#define CS40L26_DSP_LOCK3_MASK BIT(1) +#define CS40L26_DSP_PM_ACTIVE BIT(0) + +#define CS40L26_DSP_SHUTDOWN_MAX_ATTEMPTS 10 + /* ROM Controls A1 */ #define CS40L26_A1_PM_CUR_STATE_STATIC_REG 0x02800370 +#define CS40L26_A1_PM_STATE_LOCKS_STATIC_REG 0x02800378 #define CS40L26_A1_PM_TIMEOUT_TICKS_STATIC_REG 0x02800350 #define CS40L26_A1_DSP_HALO_STATE_REG 0x02800fa8 +#define CS40L26_A1_DSP_REQ_ACTIVE_REG 0x02800c08 /* algorithms */ @@ -712,15 +725,22 @@ #define CS40L26_PSEQ_OP_DELAY_WORDS 1 #define CS40L26_PSEQ_OP_END 0xFF #define CS40L26_PSEQ_OP_END_WORDS 1 +#define CS40L26_PSEQ_INVALID_ADDR 0xFF000000 +#define CS40L26_PSEQ_WORD1_MASK 0x00FFFF00 +#define CS40L26_PSEQ_WORD2_MASK 0x000000FF +#define CS40L26_PSEQ_EQ_MASK 0x00FF0000 #define CS40L26_PM_STDBY_TIMEOUT_LOWER_OFFSET 16 #define CS40L26_PM_STDBY_TIMEOUT_UPPER_OFFSET 20 +#define CS40L26_PM_TIMER_TIMEOUT_TICKS4_LOWER_OFFSET 0x18 +#define CS40L26_PM_TIMER_TIMEOUT_TICKS4_UPPER_OFFSET 0x1C #define CS40L26_PM_TIMEOUT_TICKS_LOWER_MASK GENMASK(23, 0) #define CS40L26_PM_TIMEOUT_TICKS_UPPER_MASK GENMASK(7, 0) #define CS40L26_PM_TIMEOUT_TICKS_UPPER_SHIFT 24 #define CS40L26_PM_TICKS_MS_DIV 32 #define CS40L26_PM_TIMEOUT_MS_MIN 100 +#define CS40L26_PM_TIMEOUT_MS_MAX 4880 #define CS40L26_AUTOSUSPEND_DELAY_MS 2000 @@ -770,7 +790,10 @@ #define CS40L26_DSP_MBOX_BUFFER_NUM_REGS 4 -#define CS40L26_DSP_MBOX_TRIGGER_COMPLETE 0x01000000 +#define CS40L26_DSP_MBOX_COMPLETE_MBOX 0x01000000 +#define CS40L26_DSP_MBOX_COMPLETE_GPIO 0x01000001 +#define CS40L26_DSP_MBOX_COMPLETE_I2S 0x01000002 +#define CS40L26_DSP_MBOX_TRIGGER_GPIO 0x01000011 #define CS40L26_DSP_MBOX_PM_AWAKE 0x02000002 #define CS40L26_DSP_MBOX_F0_EST_START 0x07000011 #define CS40L26_DSP_MBOX_F0_EST_DONE 0x07000021 @@ -785,7 +808,7 @@ #define CS40L26_FW_FILE_NAME "cs40l26.wmfw" #define CS40L26_FW_CALIB_NAME "cs40l26-calib.wmfw" -#define CS40L26_TUNING_FILES_MAX 3 +#define CS40L26_TUNING_FILES_MAX 4 #define CS40L26_WT_FILE_NAME "cs40l26.bin" #define CS40L26_WT_FILE_NAME_LEN 12 @@ -801,6 +824,8 @@ #define CS40L26_TUNING_FILE_NAME_MAX_LEN 20 #define CS40L26_TUNING_FILE_SUFFIX ".bin" #define CS40L26_TUNING_FILE_SUFFIX_LEN 4 +#define CS40L26_DVL_FILE_NAME "cs40l26-dvl.bin" +#define CS40L26_DVL_FILE_NAME_LEN 16 #define CS40L26_SVC_LE_MAX_ATTEMPTS 2 #define CS40L26_SVC_DT_PREFIX "svc-le" @@ -808,9 +833,10 @@ #define CS40L26_FW_ID 0x1800D4 #define CS40L26_FW_ROM_MIN_REV 0x040000 #define CS40L26_FW_A0_RAM_MIN_REV 0x050004 -#define CS40L26_FW_A1_RAM_MIN_REV 0x07020A +#define CS40L26_FW_A1_RAM_MIN_REV 0x070212 #define CS40L26_FW_CALIB_ID 0x1800DA #define CS40L26_FW_CALIB_MIN_REV 0x010000 +#define CS40L26_FW_BRANCH_MASK GENMASK(23, 21) #define CS40L26_CCM_CORE_RESET 0x00000200 #define CS40L26_CCM_CORE_ENABLE 0x00000281 @@ -830,8 +856,6 @@ #define CS40L26_CONTROL_PORT_READY_DELAY 3000 /* haptic triggering */ -#define CS40L26_TIMEOUT_MS_MAX 0xFFFF /* ~ 65s */ - #define CS40L26_TRIGGER_EFFECT 1 #define CS40L26_STOP_PLAYBACK 0x05000000 @@ -854,6 +878,9 @@ #define CS40L26_ROM_BANK_ID 1 #define CS40L26_OWT_BANK_ID 2 +#define CS40L26_BUZZGEN_CONFIG_OFFSET 12 +#define CS40L26_BUZZGEN_NUM_CONFIGS (CS40L26_BUZZGEN_INDEX_END - \ + CS40L26_BUZZGEN_INDEX_START) #define CS40L26_BUZZGEN_INDEX_START 0x01800080 #define CS40L26_BUZZGEN_INDEX_CP_TRIGGER 0x01800081 #define CS40L26_BUZZGEN_INDEX_END 0x01800085 @@ -861,20 +888,25 @@ #define CS40L26_BUZZGEN_FREQ_MIN 100 #define CS40L26_BUZZGEN_PERIOD_MAX 10 /* ms */ #define CS40L26_BUZZGEN_PERIOD_MIN 4 -#define CS40L26_BUZZGEN_DURATION_OFFSET 8 +#define CS40L26_BUZZGEN_DURATION_OFFSET 8 #define CS40L26_BUZZGEN_DURATION_DIV_STEP 4 #define CS40L26_BUZZGEN_LEVEL_OFFSET 4 #define CS40L26_BUZZGEN_LEVEL_DEFAULT 0x50 +#define CS40L26_BUZZGEN_LEVEL_MIN 0x00 +#define CS40L26_BUZZGEN_LEVEL_MAX 0xFF + #define CS40L26_AMP_CTRL_VOL_PCM_MASK GENMASK(13, 3) #define CS40L26_AMP_CTRL_VOL_PCM_SHIFT 3 #define CS40L26_AMP_VOL_PCM_MAX 0x07FF /* GPI Triggering */ +#define CS40L26_GPIO1 1 #define CS40L26_EVENT_MAP_INDEX_MASK GENMASK(8, 0) +#define CS40L26_EVENT_MAP_NUM_GPI_REGS 4 -#define CS40L26_BTN_INDEX_MASK GENMASK(6, 0) +#define CS40L26_BTN_INDEX_MASK GENMASK(7, 0) #define CS40L26_BTN_BUZZ_MASK BIT(7) #define CS40L26_BTN_BUZZ_SHIFT 7 #define CS40L26_BTN_BANK_MASK BIT(8) @@ -883,6 +915,8 @@ #define CS40L26_BTN_NUM_SHIFT 12 #define CS40L26_BTN_EDGE_MASK BIT(15) #define CS40L26_BTN_EDGE_SHIFT 15 +#define CS40L26_BTN_OWT_MASK BIT(16) +#define CS40L26_BTN_OWT_SHIFT 16 /* Interrupts */ #define CS40L26_IRQ_STATUS_DEASSERT 0x0 @@ -909,8 +943,11 @@ #define CS40L26_BST_IPK_MILLIAMP_MAX 4800 #define CS40L26_BST_IPK_MILLIAMP_MIN 1600 +#define MILLIAMPS_PER_AMPS 1000 #define CS40L26_BST_IPK_DEFAULT 0x4A +#define CS40L26_BST_IPK_CTL_STEP_SIZE 50 +#define CS40L26_BST_IPK_CTL_RESERVED 16 #define CS40L26_BOOST_DISABLE_DELAY_MIN 0 #define CS40L26_BOOST_DISABLE_DELAY_MAX 8388608 @@ -976,6 +1013,9 @@ #define CS40L26_VXBR_REL_RATE_MASK GENMASK(23, 21) #define CS40L26_VXBR_REL_RATE_SHIFT 21 +/* mixer noise gate */ +#define CS40L26_MIXER_NGATE_CH1_CFG_DEFAULT_NEW 0x00010003 + /* audio */ #define CS40L26_PLL_CLK_CFG_32768 0x00 #define CS40L26_PLL_CLK_CFG_1536000 0x1B @@ -1078,6 +1118,8 @@ #define CS40L26_A2H_VOLUME_MAX 0x7FFFFF +#define CS40L26_A2H_DELAY_MAX 0x190 + #define CS40L26_VMON_DEC_OUT_DATA_MASK GENMASK(23, 0) #define CS40L26_VMON_OVFL_FLAG_MASK BIT(31) #define CS40L26_VMON_DEC_OUT_DATA_MAX CS40L26_VMON_DEC_OUT_DATA_MASK @@ -1093,14 +1135,15 @@ #define CS40L26_WT_INDEF_TIME_VAL 0xFFFF #define CS40L26_WT_MAX_TIME_VAL 16383 /* ms */ -#define CS40L26_WT_WLEN_SIZE 4 #define CS40L26_WT_HEADER_OFFSET 3 #define CS40L26_WT_HEADER_DEFAULT_FLAGS 0x0000 +#define CS40L26_WT_HEADER_PWLE_SIZE 12 +#define CS40L26_WT_HEADER_COMP_SIZE 20 -#define CS40L26_WT_TYPE10_COMP_SEG_LEN_MAX 20 - +#define CS40L26_WT_TYPE10_SECTION_BYTES_MIN 8 +#define CS40L26_WT_TYPE10_SECTION_BYTES_MAX 12 #define CS40L26_WT_TYPE10_WAVELEN_MAX 0x3FFFFF -#define CS40L26_WT_TYPE10_WAVELEN_INDEF 0x400000 +#define CS40L26_WT_TYPE10_WAVELEN_INDEF 0x400000 #define CS40L26_WT_TYPE10_WAVELEN_CALCULATED 0x800000 #define CS40L26_WT_TYPE10_COMP_DURATION_FLAG 0x8 @@ -1152,9 +1195,19 @@ enum cs40l26_vibe_state { CS40L26_VIBE_STATE_ASP, }; +enum cs40l26_vibe_state_event { + CS40L26_VIBE_STATE_EVENT_MBOX_PLAYBACK, + CS40L26_VIBE_STATE_EVENT_MBOX_COMPLETE, + CS40L26_VIBE_STATE_EVENT_GPIO_TRIGGER, + CS40L26_VIBE_STATE_EVENT_GPIO_COMPLETE, + CS40L26_VIBE_STATE_EVENT_ASP_START, + CS40L26_VIBE_STATE_EVENT_ASP_STOP, +}; + enum cs40l26_fw_mode { CS40L26_FW_MODE_ROM, CS40L26_FW_MODE_RAM, + CS40L26_FW_MODE_NONE, }; enum cs40l26_iseq { @@ -1260,6 +1313,7 @@ struct cs40l26_fw { struct cs40l26_owt_section { u8 flags; u8 repeat; + u8 amplitude; u8 index; u16 delay; u16 duration; @@ -1302,11 +1356,11 @@ struct cs40l26_platform_data { u32 vpbr_rel_rate; bool bst_dcm_en; u32 bst_ipk; + u32 pm_timer_timeout_ticks4; }; struct cs40l26_owt { int effect_id; - u32 wlength; u32 trigger_index; struct list_head list; }; @@ -1322,10 +1376,16 @@ struct cs40l26_private { struct input_dev *input; struct cl_dsp *dsp; unsigned int trigger_indices[FF_MAX_EFFECTS]; - struct ff_effect *effect; + struct ff_effect *trigger_effect; + struct ff_effect upload_effect; + struct ff_effect *erase_effect; + s16 *raw_custom_data; + int raw_custom_data_len; struct work_struct vibe_start_work; struct work_struct vibe_stop_work; struct work_struct set_gain_work; + struct work_struct upload_work; + struct work_struct erase_work; struct workqueue_struct *vibe_workqueue; int irq; bool vibe_init_success; @@ -1337,14 +1397,12 @@ struct cs40l26_private { enum cs40l26_fw_mode fw_mode; enum cs40l26_vibe_state vibe_state; int num_loaded_coeff_files; - u32 num_waves; struct cs40l26_fw fw; bool fw_loaded; bool pm_ready; bool asp_enable; u8 last_wksrc_pol; u8 wksrc_sts; - u32 event_count; struct list_head owt_head; int num_owt_effects; int cal_requested; @@ -1352,8 +1410,10 @@ struct cs40l26_private { u32 event_map_base; struct cs40l26_svc_le **svc_le_vals; int num_svc_le_vals; - struct workqueue_struct *asp_workqueue; - struct work_struct asp_work; + u32 delay_before_stop_playback_us; + int upload_ret; + int erase_ret; + int effects_in_flight; }; struct cs40l26_codec { @@ -1379,11 +1439,11 @@ struct cs40l26_pll_sysclk_config { }; /* exported function prototypes */ +int cs40l26_asp_start(struct cs40l26_private *cs40l26); int cs40l26_get_num_waves(struct cs40l26_private *cs40l26, u32 *num_waves); -void cs40l26_asp_worker(struct work_struct *work); int cs40l26_fw_swap(struct cs40l26_private *cs40l26, u32 id); -void cs40l26_vibe_state_set(struct cs40l26_private *cs40l26, - enum cs40l26_vibe_state); +void cs40l26_vibe_state_update(struct cs40l26_private *cs40l26, + enum cs40l26_vibe_state_event event); int cs40l26_pm_timeout_ms_get(struct cs40l26_private *cs40l26, u32 *timeout_ms); int cs40l26_pm_timeout_ms_set(struct cs40l26_private *cs40l26, |