summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTai Kuo <taikuo@google.com>2021-11-26 14:11:12 +0800
committerTai Kuo <taikuo@google.com>2022-02-08 11:34:35 +0800
commit9a0148defd9807369323af67c0fba52d5cff2d15 (patch)
treed4781c90a50a63b74f042fbc33a6206d295ef3a9
parentd1ea3b1e371f0d929aeea176dd409cd1e5f644c7 (diff)
downloadamplifiers-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.c41
-rw-r--r--cs40l26/cl_dsp.h1
-rw-r--r--cs40l26/cs40l26-codec.c88
-rw-r--r--cs40l26/cs40l26-sysfs.c263
-rw-r--r--cs40l26/cs40l26.c1600
-rw-r--r--cs40l26/cs40l26.h102
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, &reg);
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, &reg);
+ 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, &reg);
+ 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, &reg);
+ 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,
- &reg);
- 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, &reg);
@@ -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(&sections[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(&sections[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,