summaryrefslogtreecommitdiff
path: root/cs40l26/cs40l26.c
diff options
context:
space:
mode:
authorTai Kuo <taikuo@google.com>2021-12-23 14:03:04 +0800
committerTai Kuo <taikuo@google.com>2022-02-08 11:34:35 +0800
commitf4c7647aefec271be24e987328fa3e38fef4b874 (patch)
tree9264bc9722f39c11f2effe5445bbe0a0928a69df /cs40l26/cs40l26.c
parent4608d02a2d91a479cde965539431f3701659a5df (diff)
downloadamplifiers-f4c7647aefec271be24e987328fa3e38fef4b874.tar.gz
cs40l26: merge CirrusLogic cs40l26 v3.5.1
Branch: v5.10-cs40l26 Tag: cs40l26-v3.5.1_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 drivers/input/misc/cs40l26.c include/linux/mfd/cs40l26.h sound/soc/codecs/cs40l26.c -> cs40l26-codec.c (No changes) Features: - ASP gain scaling factor - Expose both pm_timeout controls (active + standby) - Add default calibration properties in Devicetree - Support for A2H algorithm version 3 - Unmask critical error interrupts to enable error handling Bug fixes: - Avoid loading irrelevant tuning files for calibration firmware - Some sysfs controls were not re-enabling PM runtime after an error - Formatting fixes caught by checkpatch Commits: 15e0d2c input: cs40l26: Resolve instances of lines > 80 characters 065678b input: cs40l26: Fix error handling for sysfs controls 832ad84 input: cs40l26: Unmask critical error interrupts 426fc10 ASoC: cs40l26: A2H V3 support 3f0a90a input: cs40l26: Add IRQ mask settings to Power On Sequence 122273b input: cs40l26: Improve Readability of Power on Sequence Code f97ff40 input: cs40l26: Fix spelling error 609e98e input: cs40l26: Don't load coeffs using calibration fw (Skip) 24b77ac Documentation: cs40l26: Consolidate PM_TIMER_TIMEOUT code be949bb input: cs40l26: Consolidate PM_TIMER_TIMEOUT code (Skip) f5c7b68 Documentation: cs40l26: Add calib and BST DT properties 4aab662 input: cs40l26: Expose BST and calib settings in DT (Skip) 7bcfb09 Documentation: cs40l26: Add DT property for ASP gain scaling 1d507c1 input: cs40l26: Add DT property for ASP gain scaling Bug: 205688153 Test: idlcli commands. Signed-off-by: Tai Kuo <taikuo@google.com> Change-Id: I64a836e088c38e375a7553a0425c3bf20271f931
Diffstat (limited to 'cs40l26/cs40l26.c')
-rw-r--r--cs40l26/cs40l26.c1005
1 files changed, 540 insertions, 465 deletions
diff --git a/cs40l26/cs40l26.c b/cs40l26/cs40l26.c
index be2e22b..19b1b39 100644
--- a/cs40l26/cs40l26.c
+++ b/cs40l26/cs40l26.c
@@ -176,66 +176,53 @@ int cs40l26_dsp_state_get(struct cs40l26_private *cs40l26, u8 *state)
}
EXPORT_SYMBOL(cs40l26_dsp_state_get);
-static int cs40l26_pm_timer_timeout_ticks4_set(struct cs40l26_private *cs40l26,
- u32 pm_timer_timeout_ticks4)
+static int cs40l26_pm_timeout_ticks_write(struct cs40l26_private *cs40l26,
+ u32 ms, unsigned int lower_offset, unsigned int upper_offset)
{
struct regmap *regmap = cs40l26->regmap;
- u32 lower_val, reg;
+ struct device *dev = cs40l26->dev;
+ u32 lower_val, reg, ticks;
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;
+ if (ms < CS40L26_PM_TIMEOUT_MS_MIN) {
+ dev_warn(dev, "Timeout out of bounds, using minimum\n");
+ ticks = CS40L26_PM_TIMEOUT_MS_MIN * CS40L26_PM_TICKS_MS_DIV;
+ } else if (ms > CS40L26_PM_TIMEOUT_MS_MAX) {
+ dev_warn(dev, "Timeout out of bounds, using maximum\n");
+ ticks = CS40L26_PM_TIMEOUT_MS_MAX * CS40L26_PM_TICKS_MS_DIV;
+ } else {
+ ticks = ms * CS40L26_PM_TICKS_MS_DIV;
+ }
- upper_val = (timeout_ticks >> CS40L26_PM_TIMEOUT_TICKS_UPPER_SHIFT) &
+ upper_val = (ticks >> CS40L26_PM_TIMEOUT_TICKS_UPPER_SHIFT) &
CS40L26_PM_TIMEOUT_TICKS_UPPER_MASK;
- lower_val = timeout_ticks & CS40L26_PM_TIMEOUT_TICKS_LOWER_MASK;
+ lower_val = ticks & 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 = cs40l26_dsp_write(cs40l26, reg + CS40L26_PM_STDBY_TIMEOUT_LOWER_OFFSET,
- lower_val);
- if (ret)
+ ret = regmap_write(regmap, reg + lower_offset, lower_val);
+ if (ret) {
+ dev_err(dev, "Failed to write timeout ticks to 0x%08X\n",
+ reg + lower_offset);
return ret;
+ }
+
+ ret = regmap_write(regmap, reg + upper_offset, upper_val);
+ if (ret)
+ dev_err(dev, "Failed to write timeout ticks to 0x%08X\n",
+ reg + upper_offset);
- return cs40l26_dsp_write(cs40l26, reg + CS40L26_PM_STDBY_TIMEOUT_UPPER_OFFSET,
- upper_val);
+ return ret;
}
-EXPORT_SYMBOL(cs40l26_pm_timeout_ms_set);
-int cs40l26_pm_timeout_ms_get(struct cs40l26_private *cs40l26,
- u32 *timeout_ms)
+static int cs40l26_pm_timeout_ticks_read(struct cs40l26_private *cs40l26,
+ unsigned int lower_offset, unsigned int upper_offset,
+ u32 *ticks)
{
u32 lower_val, upper_val, reg;
int ret = 0;
@@ -249,35 +236,79 @@ int cs40l26_pm_timeout_ms_get(struct cs40l26_private *cs40l26,
if (ret)
return ret;
- ret = regmap_read(cs40l26->regmap, reg +
- CS40L26_PM_STDBY_TIMEOUT_LOWER_OFFSET, &lower_val);
+ ret = regmap_read(cs40l26->regmap, reg + lower_offset, &lower_val);
if (ret)
return ret;
- ret = regmap_read(cs40l26->regmap, reg +
- CS40L26_PM_STDBY_TIMEOUT_UPPER_OFFSET, &upper_val);
+ ret = regmap_read(cs40l26->regmap, reg + upper_offset, &upper_val);
if (ret)
return ret;
- *timeout_ms =
- ((lower_val & CS40L26_PM_TIMEOUT_TICKS_LOWER_MASK) |
+ *ticks = ((lower_val & CS40L26_PM_TIMEOUT_TICKS_LOWER_MASK) |
((upper_val & CS40L26_PM_TIMEOUT_TICKS_UPPER_MASK) <<
- CS40L26_PM_TIMEOUT_TICKS_UPPER_SHIFT)) /
- CS40L26_PM_TICKS_MS_DIV;
+ CS40L26_PM_TIMEOUT_TICKS_UPPER_SHIFT));
return 0;
}
-EXPORT_SYMBOL(cs40l26_pm_timeout_ms_get);
-static int cs40l26_pm_runtime_setup(struct cs40l26_private *cs40l26)
+int cs40l26_pm_active_timeout_ms_set(struct cs40l26_private *cs40l26,
+ u32 timeout_ms)
{
- struct device *dev = cs40l26->dev;
+ return cs40l26_pm_timeout_ticks_write(cs40l26, timeout_ms,
+ CS40L26_PM_ACTIVE_TIMEOUT_LOWER_OFFSET,
+ CS40L26_PM_ACTIVE_TIMEOUT_UPPER_OFFSET);
+}
+EXPORT_SYMBOL(cs40l26_pm_active_timeout_ms_set);
+
+int cs40l26_pm_active_timeout_ms_get(struct cs40l26_private *cs40l26,
+ u32 *timeout_ms)
+{
+ u32 ticks;
+ int ret;
+
+ ret = cs40l26_pm_timeout_ticks_read(cs40l26,
+ CS40L26_PM_ACTIVE_TIMEOUT_LOWER_OFFSET,
+ CS40L26_PM_ACTIVE_TIMEOUT_UPPER_OFFSET, &ticks);
+ if (ret)
+ return ret;
+
+ *timeout_ms = ticks / CS40L26_PM_TICKS_MS_DIV;
+
+ return 0;
+}
+EXPORT_SYMBOL(cs40l26_pm_active_timeout_ms_get);
+
+int cs40l26_pm_stdby_timeout_ms_set(struct cs40l26_private *cs40l26,
+ u32 timeout_ms)
+{
+ return cs40l26_pm_timeout_ticks_write(cs40l26, timeout_ms,
+ CS40L26_PM_STDBY_TIMEOUT_LOWER_OFFSET,
+ CS40L26_PM_STDBY_TIMEOUT_UPPER_OFFSET);
+}
+EXPORT_SYMBOL(cs40l26_pm_stdby_timeout_ms_set);
+
+int cs40l26_pm_stdby_timeout_ms_get(struct cs40l26_private *cs40l26,
+ u32 *timeout_ms)
+{
+ u32 ticks;
int ret;
- ret = cs40l26_pm_timeout_ms_set(cs40l26, CS40L26_PM_TIMEOUT_MS_MIN);
+ ret = cs40l26_pm_timeout_ticks_read(cs40l26,
+ CS40L26_PM_STDBY_TIMEOUT_LOWER_OFFSET,
+ CS40L26_PM_STDBY_TIMEOUT_UPPER_OFFSET, &ticks);
if (ret)
return ret;
+ *timeout_ms = ticks / CS40L26_PM_TICKS_MS_DIV;
+
+ return 0;
+}
+EXPORT_SYMBOL(cs40l26_pm_stdby_timeout_ms_get);
+
+static void cs40l26_pm_runtime_setup(struct cs40l26_private *cs40l26)
+{
+ struct device *dev = cs40l26->dev;
+
pm_runtime_mark_last_busy(dev);
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
@@ -285,8 +316,6 @@ static int cs40l26_pm_runtime_setup(struct cs40l26_private *cs40l26)
pm_runtime_use_autosuspend(dev);
cs40l26->pm_ready = true;
-
- return 0;
}
static void cs40l26_pm_runtime_teardown(struct cs40l26_private *cs40l26)
@@ -318,6 +347,28 @@ static int cs40l26_check_pm_lock(struct cs40l26_private *cs40l26, bool *locked)
return 0;
}
+static void cs40l26_remove_asp_scaling(struct cs40l26_private *cs40l26)
+{
+ struct device *dev = cs40l26->dev;
+ u16 gain;
+
+ if (cs40l26->pdata.asp_scale_pct >= CS40L26_GAIN_FULL_SCALE ||
+ !cs40l26->scaling_applied)
+ return;
+
+ gain = cs40l26->gain_tmp;
+
+ if (gain >= CS40L26_NUM_PCT_MAP_VALUES) {
+ dev_err(dev, "Gain %u%% out of bounds\n", gain);
+ return;
+ }
+
+ cs40l26->gain_pct = gain;
+ cs40l26->scaling_applied = false;
+
+ queue_work(cs40l26->vibe_workqueue, &cs40l26->set_gain_work);
+}
+
int cs40l26_pm_state_transition(struct cs40l26_private *cs40l26,
enum cs40l26_pm_state state)
{
@@ -339,7 +390,8 @@ int cs40l26_pm_state_transition(struct cs40l26_private *cs40l26,
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,
+ ret = cs40l26_ack_write(cs40l26,
+ CS40L26_DSP_VIRTUAL1_MBOX_1,
cmd, CS40L26_DSP_MBOX_RESET);
if (ret)
return ret;
@@ -456,7 +508,7 @@ static int cs40l26_dsp_shutdown(struct cs40l26_private *cs40l26)
u32 timeout_ms;
int ret;
- ret = cs40l26_pm_timeout_ms_get(cs40l26, &timeout_ms);
+ ret = cs40l26_pm_stdby_timeout_ms_get(cs40l26, &timeout_ms);
if (ret)
return ret;
@@ -494,8 +546,7 @@ static int cs40l26_dsp_pre_config(struct cs40l26_private *cs40l26)
return -EINVAL;
}
-
- ret = cs40l26_pm_timeout_ms_get(cs40l26, &timeout_ms);
+ ret = cs40l26_pm_stdby_timeout_ms_get(cs40l26, &timeout_ms);
if (ret)
return ret;
@@ -690,12 +741,16 @@ static int cs40l26_handle_mbox_buffer(struct cs40l26_private *cs40l26)
return 0;
}
-int cs40l26_asp_start(struct cs40l26_private *cs40l26) {
+int cs40l26_asp_start(struct cs40l26_private *cs40l26)
+{
struct device *dev = cs40l26->dev;
bool ack = false;
unsigned int val;
int ret;
+ if (cs40l26->pdata.asp_scale_pct < CS40L26_GAIN_FULL_SCALE)
+ queue_work(cs40l26->vibe_workqueue, &cs40l26->set_gain_work);
+
ret = cs40l26_ack_write(cs40l26, CS40L26_DSP_VIRTUAL1_MBOX_1,
CS40L26_STOP_PLAYBACK, CS40L26_DSP_MBOX_RESET);
if (ret) {
@@ -741,6 +796,7 @@ void cs40l26_vibe_state_update(struct cs40l26_private *cs40l26,
switch (event) {
case CS40L26_VIBE_STATE_EVENT_MBOX_PLAYBACK:
case CS40L26_VIBE_STATE_EVENT_GPIO_TRIGGER:
+ cs40l26_remove_asp_scaling(cs40l26);
cs40l26->effects_in_flight++;
break;
case CS40L26_VIBE_STATE_EVENT_MBOX_COMPLETE:
@@ -757,6 +813,7 @@ void cs40l26_vibe_state_update(struct cs40l26_private *cs40l26,
cs40l26->asp_enable = true;
break;
case CS40L26_VIBE_STATE_EVENT_ASP_STOP:
+ cs40l26_remove_asp_scaling(cs40l26);
cs40l26->asp_enable = false;
break;
default:
@@ -833,105 +890,6 @@ static int cs40l26_error_release(struct cs40l26_private *cs40l26,
return ret;
}
-static int cs40l26_iseq_update(struct cs40l26_private *cs40l26,
- enum cs40l26_iseq update)
-{
- int ret;
- u32 val;
-
- ret = regmap_read(cs40l26->regmap, cs40l26->iseq_table[update].addr,
- &val);
- if (ret) {
- dev_err(cs40l26->dev, "Failed to get IRQ seq. information\n");
- return ret;
- }
-
- cs40l26->iseq_table[update].val = val;
-
- return 0;
-}
-
-static int cs40l26_iseq_init(struct cs40l26_private *cs40l26)
-{
- struct device *dev = cs40l26->dev;
- struct regmap *regmap = cs40l26->regmap;
- int ret, i;
-
- cs40l26->iseq_table[CS40L26_ISEQ_MASK1].addr = CS40L26_IRQ1_MASK_1;
- cs40l26->iseq_table[CS40L26_ISEQ_MASK2].addr = CS40L26_IRQ1_MASK_2;
- cs40l26->iseq_table[CS40L26_ISEQ_EDGE1].addr = CS40L26_IRQ1_EDGE_1;
- cs40l26->iseq_table[CS40L26_ISEQ_POL1].addr = CS40L26_IRQ1_POL_1;
-
- for (i = 0; i < CS40L26_ISEQ_MAX_ENTRIES; i++) {
- ret = regmap_read(regmap, cs40l26->iseq_table[i].addr,
- &cs40l26->iseq_table[i].val);
- if (ret) {
- dev_err(dev, "Failed to read IRQ settings\n");
- return ret;
- }
- }
-
- return ret;
-}
-
-static int cs40l26_iseq_populate(struct cs40l26_private *cs40l26)
-{
- int ret, i;
-
- for (i = 0; i < CS40L26_ISEQ_MAX_ENTRIES; i++) {
- ret = regmap_write(cs40l26->regmap,
- cs40l26->iseq_table[i].addr,
- cs40l26->iseq_table[i].val);
- if (ret) {
- dev_err(cs40l26->dev,
- "Failed to update IRQ settings\n");
- return ret;
- }
- }
-
- return 0;
-}
-
-static int cs40l26_irq_update_mask(struct cs40l26_private *cs40l26, u32 irq_reg,
- u32 bits, bool mask)
-{
- struct regmap *regmap = cs40l26->regmap;
- struct device *dev = cs40l26->dev;
- enum cs40l26_iseq update;
- u32 eint_reg;
- int ret;
-
- switch (irq_reg) {
- case CS40L26_IRQ1_MASK_1:
- update = CS40L26_ISEQ_MASK1;
- eint_reg = CS40L26_IRQ1_EINT_1;
- break;
- case CS40L26_IRQ1_MASK_2:
- update = CS40L26_ISEQ_MASK2;
- eint_reg = CS40L26_IRQ1_EINT_2;
- break;
- default:
- dev_err(dev, "Invalid IRQ mask register 0x%08X\n", irq_reg);
- return -EINVAL;
- }
-
- ret = regmap_write(regmap, eint_reg, bits);
- if (ret) {
- dev_err(dev,
- "Failed to clear status of bits 0x%08X\n",
- eint_reg);
- return ret;
- }
-
- ret = regmap_update_bits(regmap, irq_reg, bits, mask ? bits : ~bits);
- if (ret) {
- dev_err(dev, "Failed to update IRQ mask 0x%08X\n", irq_reg);
- return ret;
- }
-
- return cs40l26_iseq_update(cs40l26, update);
-}
-
static int cs40l26_handle_irq1(struct cs40l26_private *cs40l26,
enum cs40l26_irq1 irq1)
{
@@ -968,10 +926,6 @@ static int cs40l26_handle_irq1(struct cs40l26_private *cs40l26,
case CS40L26_IRQ1_WKSRC_STS_ANY:
dev_dbg(dev, "Wakesource detected (ANY)\n");
- ret = cs40l26_iseq_populate(cs40l26);
- if (ret)
- goto err;
-
ret = regmap_read(cs40l26->regmap, CS40L26_PWRMGT_STS, &val);
if (ret) {
dev_err(dev, "Failed to get Power Management Status\n");
@@ -1330,218 +1284,160 @@ err:
return (irq1_count + irq2_count) ? IRQ_HANDLED : IRQ_NONE;
}
-static int cs40l26_pseq_add_op(struct cs40l26_private *cs40l26,
- int num_words, u32 *words)
+static int cs40l26_pseq_find_end(struct cs40l26_private *cs40l26,
+ struct cs40l26_pseq_op **op_end)
{
- struct regmap *regmap = cs40l26->regmap;
- struct device *dev = cs40l26->dev;
- u32 offset_for_new_op, *op_words;
- int ret = 0;
- struct cs40l26_pseq_op *pseq_op_end, *pseq_op_new, *op;
+ struct cs40l26_pseq_op *op;
- /* get location of the list terminator */
- list_for_each_entry(pseq_op_end, &cs40l26->pseq_op_head, list) {
- if (pseq_op_end->operation == CS40L26_PSEQ_OP_END)
+ list_for_each_entry(op, &cs40l26->pseq_op_head, list) {
+ if (op->operation == CS40L26_PSEQ_OP_END)
break;
}
- if (pseq_op_end->operation != CS40L26_PSEQ_OP_END) {
- dev_err(dev, "Failed to find END_OF_SCRIPT\n");
- return -EINVAL;
- }
-
- offset_for_new_op = pseq_op_end->offset;
-
- /* add new op to list */
- op_words = kzalloc(num_words * CL_DSP_BYTES_PER_WORD, GFP_KERNEL);
- if (!op_words)
- return -ENOMEM;
- memcpy(op_words, words, num_words * CL_DSP_BYTES_PER_WORD);
- pseq_op_new = devm_kzalloc(dev, sizeof(*pseq_op_new), GFP_KERNEL);
- if (!pseq_op_new) {
- ret = -ENOMEM;
- goto err_free;
+ if (op->operation != CS40L26_PSEQ_OP_END) {
+ dev_err(cs40l26->dev, "Failed to find PSEQ list terminator\n");
+ return -ENOENT;
}
- pseq_op_new->size = num_words;
- pseq_op_new->offset = offset_for_new_op;
- pseq_op_new->words = op_words;
- list_add(&pseq_op_new->list, &cs40l26->pseq_op_head);
+ *op_end = op;
- cs40l26->pseq_num_ops++;
+ return 0;
+}
- /* bump end of script offset to accomodate new operation */
- pseq_op_end->offset += num_words * CL_DSP_BYTES_PER_WORD;
+static int cs40l26_pseq_write(struct cs40l26_private *cs40l26, u32 addr,
+ u32 data, u8 op_code)
+{
+ struct device *dev = cs40l26->dev;
+ bool is_new = true;
+ unsigned int l_addr_mask, u_addr_mask, u_data_mask, l_data_mask;
+ unsigned int l_addr_shift, u_addr_shift, u_data_shift;
+ struct cs40l26_pseq_op *op, *op_new, *op_end;
+ unsigned int op_mask;
+ int num_op_words;
+ u32 *op_words;
+ int ret;
- list_for_each_entry(op, &cs40l26->pseq_op_head, list) {
- if (op->offset >= offset_for_new_op) {
- ret = regmap_bulk_write(regmap, cs40l26->pseq_base +
- op->offset,
- op->words,
- op->size);
- if (ret) {
- dev_err(dev, "Failed to write op\n");
- return -EIO;
- }
+ if (op_code == CS40L26_PSEQ_OP_WRITE_FULL) {
+ num_op_words = CS40L26_PSEQ_OP_WRITE_FULL_WORDS;
+ l_addr_shift = CS40L26_PSEQ_WRITE_FULL_LOWER_ADDR_SHIFT;
+ l_addr_mask = CS40L26_PSEQ_WRITE_FULL_LOWER_ADDR_MASK;
+ u_addr_shift = CS40L26_PSEQ_WRITE_FULL_UPPER_ADDR_SHIFT;
+ u_addr_mask = CS40L26_PSEQ_WRITE_FULL_UPPER_ADDR_MASK;
+ u_data_shift = CS40L26_PSEQ_WRITE_FULL_UPPER_DATA_SHIFT;
+ u_data_mask = CS40L26_PSEQ_WRITE_FULL_UPPER_DATA_MASK;
+ l_data_mask = CS40L26_PSEQ_WRITE_FULL_LOWER_DATA_MASK;
+ op_mask = CS40L26_PSEQ_WRITE_FULL_OP_MASK;
+ } else if (op_code == CS40L26_PSEQ_OP_WRITE_H16 ||
+ op_code == CS40L26_PSEQ_OP_WRITE_L16) {
+ if (addr & CS40L26_PSEQ_INVALID_ADDR) {
+ dev_err(dev, "Invalid PSEQ address: 0x%08X\n", addr);
+ return -EINVAL;
}
- }
- return ret;
+ num_op_words = CS40L26_PSEQ_OP_WRITE_X16_WORDS;
+ l_addr_shift = CS40L26_PSEQ_WRITE_X16_LOWER_ADDR_SHIFT;
+ l_addr_mask = CS40L26_PSEQ_WRITE_X16_LOWER_ADDR_MASK;
+ u_addr_shift = CS40L26_PSEQ_WRITE_X16_UPPER_ADDR_SHIFT;
+ u_addr_mask = CS40L26_PSEQ_WRITE_X16_UPPER_ADDR_MASK;
+ u_data_shift = CS40L26_PSEQ_WRITE_X16_UPPER_DATA_SHIFT;
+ u_data_mask = CS40L26_PSEQ_WRITE_X16_UPPER_DATA_MASK;
+ op_mask = CS40L26_PSEQ_WRITE_X16_OP_MASK;
+ } else {
+ dev_err(dev, "Invalid PSEQ OP code: 0x%02X\n", op_code);
+ return -EINVAL;
+ }
-err_free:
- kfree(op_words);
+ op_words = devm_kcalloc(dev, num_op_words, sizeof(u32), GFP_KERNEL);
+ if (!op_words)
+ return -ENOMEM;
- return ret;
-}
+ op_words[0] = (op_code << CS40L26_PSEQ_OP_SHIFT);
+ op_words[0] |= (addr & u_addr_mask) >> u_addr_shift;
+ op_words[1] = (addr & l_addr_mask) << l_addr_shift;
+ op_words[1] |= (data & u_data_mask) >> u_data_shift;
+ if (op_code == CS40L26_PSEQ_OP_WRITE_FULL)
+ op_words[2] = data & l_data_mask;
-static int cs40l26_pseq_add_write_reg_full(struct cs40l26_private *cs40l26,
- u32 addr, u32 data, bool update_if_op_already_in_seq)
-{
- int ret;
- struct cs40l26_pseq_op *op;
- u32 op_words[CS40L26_PSEQ_OP_WRITE_REG_FULL_WORDS];
-
- op_words[0] = (CS40L26_PSEQ_OP_WRITE_REG_FULL <<
- CS40L26_PSEQ_OP_SHIFT);
- op_words[0] |= addr >> 16;
- op_words[1] = (addr & 0x0000FFFF) << 8;
- op_words[1] |= (data & 0xFF000000) >> 24;
- op_words[2] = (data & 0x00FFFFFF);
-
- 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] & 0xFFFFFF00) ==
- (op_words[1] & 0xFFFFFF00))) {
- /* 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;
+ list_for_each_entry(op, &cs40l26->pseq_op_head, list) {
+ if (op->words[0] == op_words[0] && (op->words[1] & op_mask) ==
+ (op_words[1] & op_mask)) {
+ if (op->size != num_op_words) {
+ dev_err(dev, "Failed to replace PSEQ op.\n");
+ ret = -EINVAL;
+ goto op_words_free;
}
+ is_new = false;
+ break;
}
}
- /* if no matching op is found or !update, add the op */
- ret = cs40l26_pseq_add_op(cs40l26,
- CS40L26_PSEQ_OP_WRITE_REG_FULL_WORDS,
- op_words);
-
- return ret;
-}
-
-int cs40l26_pseq_multi_add_write_reg_full(struct cs40l26_private *cs40l26,
- const struct reg_sequence *reg_seq, int num_regs,
- bool update_if_op_already_in_seq)
-{
- int ret, i;
-
- for (i = 0; i < num_regs; i++) {
- ret = cs40l26_pseq_add_write_reg_full(cs40l26,
- reg_seq[i].reg, reg_seq[i].def,
- update_if_op_already_in_seq);
- if (ret)
- return ret;
+ op_new = devm_kzalloc(dev, sizeof(*op_new), GFP_KERNEL);
+ if (!op_new) {
+ ret = -ENOMEM;
+ goto op_words_free;
}
+ op_new->size = num_op_words;
+ op_new->words = op_words;
+ op_new->operation = op_code;
- return 0;
-}
-EXPORT_SYMBOL(cs40l26_pseq_multi_add_write_reg_full);
+ ret = cs40l26_pseq_find_end(cs40l26, &op_end);
+ if (ret)
+ goto op_new_free;
-/* 24-bit address, 16-bit data */
-static int cs40l26_pseq_add_write_reg_h16(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_H16_WORDS];
+ if (is_new) {
+ op_new->offset = op_end->offset;
+ op_end->offset += (num_op_words * CL_DSP_BYTES_PER_WORD);
+ } else {
+ op_new->offset = op->offset;
+ }
- if (addr & 0xFF000000) {
- dev_err(dev, "invalid address for pseq write_reg_h16\n");
- return -EINVAL;
+ ret = regmap_bulk_write(cs40l26->regmap, cs40l26->pseq_base +
+ op_new->offset, op_new->words, op_new->size);
+ if (ret) {
+ dev_err(dev, "Failed to write PSEQ op.\n");
+ goto op_new_free;
}
- op_words[0] = (CS40L26_PSEQ_OP_WRITE_REG_H16 <<
- CS40L26_PSEQ_OP_SHIFT);
- op_words[0] |= (addr & 0x00FFFF00) >> 8;
- op_words[1] = (addr & 0x000000FF) << 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] & 0x00FF0000) ==
- (op_words[1] & 0x00FF0000))) {
- /* 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;
- }
+ if (is_new) {
+ ret = regmap_bulk_write(cs40l26->regmap,
+ cs40l26->pseq_base + op_end->offset,
+ op_end->words, op_end->size);
+ if (ret) {
+ dev_err(dev, "Failed to write PSEQ terminator\n");
+ goto op_new_free;
}
+
+ list_add(&op_new->list, &cs40l26->pseq_op_head);
+ cs40l26->pseq_num_ops++;
+ } else {
+ list_replace(&op->list, &op_new->list);
}
- /* if no matching op is found or !update, add the op */
- ret = cs40l26_pseq_add_op(cs40l26,
- CS40L26_PSEQ_OP_WRITE_REG_H16_WORDS,
- op_words);
+ return 0;
+
+op_new_free:
+ devm_kfree(dev, op_new);
+
+op_words_free:
+ devm_kfree(dev, op_words);
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)
+static int cs40l26_pseq_multi_write(struct cs40l26_private *cs40l26,
+ const struct reg_sequence *reg_seq, int num_regs, bool update,
+ u8 op_code)
{
- 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;
- }
+ int ret, i;
- 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;
- }
- }
+ for (i = 0; i < num_regs; i++) {
+ ret = cs40l26_pseq_write(cs40l26, reg_seq[i].reg,
+ reg_seq[i].def, op_code);
+ if (ret)
+ return ret;
}
- ret = cs40l26_pseq_add_op(cs40l26,
- CS40L26_PSEQ_OP_WRITE_REG_L16_WORDS,
- op_words);
-
- return ret;
+ return 0;
}
static int cs40l26_pseq_init(struct cs40l26_private *cs40l26)
@@ -1599,7 +1495,7 @@ static int cs40l26_pseq_init(struct cs40l26_private *cs40l26)
}
op_words = kzalloc(num_words * CL_DSP_BYTES_PER_WORD,
- GFP_KERNEL);
+ GFP_KERNEL);
if (!op_words)
return -ENOMEM;
memcpy(op_words, &words[i], num_words * CL_DSP_BYTES_PER_WORD);
@@ -1642,31 +1538,69 @@ 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");
+ ret = cs40l26_pseq_write(cs40l26, CS40L26_NGATE1_INPUT,
+ CS40L26_DATA_SRC_DSP1TX4, CS40L26_PSEQ_OP_WRITE_L16);
+ if (ret)
return ret;
- }
- ret = cs40l26_pseq_add_write_reg_full(cs40l26, CS40L26_MIXER_NGATE_CH1_CFG,
- CS40L26_MIXER_NGATE_CH1_CFG_DEFAULT_NEW, true);
+ ret = cs40l26_pseq_write(cs40l26, CS40L26_MIXER_NGATE_CH1_CFG,
+ CS40L26_MIXER_NGATE_CH1_CFG_DEFAULT_NEW,
+ CS40L26_PSEQ_OP_WRITE_FULL);
if (ret) {
- dev_err(dev, "Failed to sequence Mixer Noise Gate register default updates\n");
+ dev_err(dev, "Failed to sequence Mixer Noise Gate\n");
return ret;
}
/* set SPK_DEFAULT_HIZ to 1 */
- ret = cs40l26_pseq_add_write_reg_h16(cs40l26,
- CS40L26_TST_DAC_MSM_CONFIG,
+ ret = cs40l26_pseq_write(cs40l26, CS40L26_TST_DAC_MSM_CONFIG,
CS40L26_TST_DAC_MSM_CONFIG_DEFAULT_CHANGE_VALUE_H16,
- true);
+ CS40L26_PSEQ_OP_WRITE_H16);
if (ret)
dev_err(dev, "Failed to sequence register default updates\n");
return ret;
}
+static int cs40l26_irq_update_mask(struct cs40l26_private *cs40l26, u32 reg,
+ u32 val, u32 bit_mask)
+{
+ u32 eint_reg, cur_mask, new_mask;
+ int ret;
+
+ if (reg == CS40L26_IRQ1_MASK_1) {
+ eint_reg = CS40L26_IRQ1_EINT_1;
+ } else if (reg == CS40L26_IRQ1_MASK_2) {
+ eint_reg = CS40L26_IRQ1_EINT_2;
+ } else {
+ dev_err(cs40l26->dev, "Invalid IRQ mask reg: 0x%08X\n", reg);
+ return -EINVAL;
+ }
+
+ ret = regmap_read(cs40l26->regmap, reg, &cur_mask);
+ if (ret) {
+ dev_err(cs40l26->dev, "Failed to get IRQ mask\n");
+ return ret;
+ }
+
+ new_mask = (cur_mask & ~bit_mask) | val;
+
+ /* Clear interrupt prior to masking/unmasking */
+ ret = regmap_write(cs40l26->regmap, eint_reg, bit_mask);
+ if (ret) {
+ dev_err(cs40l26->dev, "Failed to clear IRQ\n");
+ return ret;
+ }
+
+ ret = regmap_write(cs40l26->regmap, reg, new_mask);
+ if (ret) {
+ dev_err(cs40l26->dev, "Failed to update IRQ mask\n");
+ return ret;
+ }
+
+ return cs40l26_pseq_write(cs40l26, reg,
+ new_mask, CS40L26_PSEQ_OP_WRITE_FULL);
+}
+
static int cs40l26_buzzgen_set(struct cs40l26_private *cs40l26, u16 freq,
u16 level, u16 duration, u8 buzzgen_num)
{
@@ -1790,20 +1724,32 @@ static void cs40l26_set_gain_worker(struct work_struct *work)
{
struct cs40l26_private *cs40l26 =
container_of(work, struct cs40l26_private, set_gain_work);
+ u16 gain;
u32 reg;
int ret;
pm_runtime_get_sync(cs40l26->dev);
mutex_lock(&cs40l26->lock);
+ if (cs40l26->vibe_state == CS40L26_VIBE_STATE_ASP) {
+ gain = (cs40l26->pdata.asp_scale_pct * cs40l26->gain_pct) /
+ CS40L26_GAIN_FULL_SCALE;
+ cs40l26->gain_tmp = cs40l26->gain_pct;
+ cs40l26->gain_pct = gain;
+ cs40l26->scaling_applied = true;
+ } else {
+ gain = cs40l26->gain_pct;
+ }
+
+ dev_dbg(cs40l26->dev, "%s: gain = %u%%\n", __func__, gain);
+
/* Write Q21.2 value to SOURCE_ATTENUATION */
ret = cl_dsp_get_reg(cs40l26->dsp, "SOURCE_ATTENUATION",
CL_DSP_XM_UNPACKED_TYPE, CS40L26_EXT_ALGO_ID, &reg);
if (ret)
goto err_mutex;
- ret = regmap_write(cs40l26->regmap, reg,
- cs40l26_attn_q21_2_vals[cs40l26->gain_pct]);
+ ret = regmap_write(cs40l26->regmap, reg, cs40l26_attn_q21_2_vals[gain]);
if (ret)
dev_err(cs40l26->dev, "Failed to set attenuation\n");
@@ -1940,10 +1886,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);
+ if (gain >= CS40L26_NUM_PCT_MAP_VALUES) {
+ dev_err(cs40l26->dev, "Gain value %u %% out of bounds\n", gain);
return;
}
@@ -3099,12 +3043,23 @@ static int cs40l26_cl_dsp_init(struct cs40l26_private *cs40l26, u32 id)
cs40l26->fw.min_rev = CS40L26_FW_CALIB_MIN_REV;
cs40l26->fw.num_coeff_files = CS40L26_TUNING_FILES_MAX;
- cs40l26->fw.coeff_files = devm_kcalloc(cs40l26->dev,
- CS40L26_TUNING_FILES_MAX, sizeof(char *), GFP_KERNEL);
- for (i = 0; i < CS40L26_TUNING_FILES_MAX; i++)
- cs40l26->fw.coeff_files[i] = devm_kzalloc(cs40l26->dev,
- CS40L26_TUNING_FILE_NAME_MAX_LEN, GFP_KERNEL);
+ if (!cs40l26->fw.coeff_files)
+ cs40l26->fw.coeff_files = devm_kcalloc(cs40l26->dev,
+ CS40L26_TUNING_FILES_MAX, sizeof(char *),
+ GFP_KERNEL);
+
+ for (i = 0; i < CS40L26_TUNING_FILES_MAX; i++) {
+ if (!cs40l26->fw.coeff_files[i]) {
+ cs40l26->fw.coeff_files[i] =
+ devm_kzalloc(cs40l26->dev,
+ CS40L26_TUNING_FILE_NAME_MAX_LEN,
+ GFP_KERNEL);
+ } else {
+ memset(cs40l26->fw.coeff_files[i], 0,
+ CS40L26_TUNING_FILE_NAME_MAX_LEN);
+ }
+ }
strncpy(cs40l26->fw.coeff_files[0], CS40L26_WT_FILE_NAME,
CS40L26_WT_FILE_NAME_LEN);
@@ -3127,37 +3082,29 @@ static int cs40l26_cl_dsp_init(struct cs40l26_private *cs40l26, u32 id)
static int cs40l26_wksrc_config(struct cs40l26_private *cs40l26)
{
- u32 unmask_bits, mask_bits;
- int ret;
-
- unmask_bits = BIT(CS40L26_IRQ1_WKSRC_STS_ANY) |
- BIT(CS40L26_IRQ1_WKSRC_STS_GPIO1) |
- BIT(CS40L26_IRQ1_WKSRC_STS_I2C);
-
- /* SPI support is not yet available */
- mask_bits = BIT(CS40L26_IRQ1_WKSRC_STS_SPI);
+ u8 mask_wksrc = (cs40l26->devid == CS40L26_DEVID_A) ? 1 : 0;
+ u32 val, mask;
- if (cs40l26->devid == CS40L26_DEVID_A)
- mask_bits |= (BIT(CS40L26_IRQ1_WKSRC_STS_GPIO2) |
- BIT(CS40L26_IRQ1_WKSRC_STS_GPIO3) |
- BIT(CS40L26_IRQ1_WKSRC_STS_GPIO4));
- else
- unmask_bits |= (BIT(CS40L26_IRQ1_WKSRC_STS_GPIO2) |
- BIT(CS40L26_IRQ1_WKSRC_STS_GPIO3) |
- BIT(CS40L26_IRQ1_WKSRC_STS_GPIO4));
+ val = BIT(CS40L26_IRQ1_WKSRC_STS_SPI) |
+ (mask_wksrc << CS40L26_IRQ1_WKSRC_STS_GPIO2) |
+ (mask_wksrc << CS40L26_IRQ1_WKSRC_STS_GPIO3) |
+ (mask_wksrc << CS40L26_IRQ1_WKSRC_STS_GPIO4);
- ret = cs40l26_irq_update_mask(cs40l26, CS40L26_IRQ1_MASK_1, mask_bits,
- CS40L26_IRQ_MASK);
- if (ret)
- return ret;
+ mask = BIT(CS40L26_IRQ1_WKSRC_STS_ANY) |
+ BIT(CS40L26_IRQ1_WKSRC_STS_GPIO1) |
+ BIT(CS40L26_IRQ1_WKSRC_STS_I2C) |
+ BIT(CS40L26_IRQ1_WKSRC_STS_SPI) |
+ BIT(CS40L26_IRQ1_WKSRC_STS_GPIO2) |
+ BIT(CS40L26_IRQ1_WKSRC_STS_GPIO3) |
+ BIT(CS40L26_IRQ1_WKSRC_STS_GPIO4);
- return cs40l26_irq_update_mask(cs40l26, CS40L26_IRQ1_MASK_1,
- unmask_bits, CS40L26_IRQ_UNMASK);
+ return cs40l26_irq_update_mask(cs40l26, CS40L26_IRQ1_MASK_1, val, mask);
}
static int cs40l26_gpio_config(struct cs40l26_private *cs40l26)
{
- u32 unmask_bits;
+ u32 mask_gpio = (cs40l26->devid == CS40L26_DEVID_A) ? 1 : 0;
+ u32 val, mask;
int ret;
ret = cl_dsp_get_reg(cs40l26->dsp, "ENT_MAP_TABLE_EVENT_DATA_PACKED",
@@ -3166,27 +3113,29 @@ static int cs40l26_gpio_config(struct cs40l26_private *cs40l26)
if (ret)
return ret;
- unmask_bits = BIT(CS40L26_IRQ1_GPIO1_RISE)
- | BIT(CS40L26_IRQ1_GPIO1_FALL);
+ mask = (u32) (GENMASK(CS40L26_IRQ1_GPIO4_FALL,
+ CS40L26_IRQ1_GPIO1_RISE));
- if (cs40l26->devid == CS40L26_DEVID_B) /* 4 GPIO config */
- unmask_bits |= (u32) (GENMASK(CS40L26_IRQ1_GPIO4_FALL,
- CS40L26_IRQ1_GPIO2_RISE));
+ if (mask_gpio)
+ val = (u32) GENMASK(CS40L26_IRQ1_GPIO4_FALL,
+ CS40L26_IRQ1_GPIO2_RISE);
+ else
+ val = 0;
- return cs40l26_irq_update_mask(cs40l26, CS40L26_IRQ1_MASK_1,
- unmask_bits, CS40L26_IRQ_UNMASK);
+ return cs40l26_irq_update_mask(cs40l26, CS40L26_IRQ1_MASK_1, val, mask);
}
static int cs40l26_brownout_prevention_init(struct cs40l26_private *cs40l26)
{
struct device *dev = cs40l26->dev;
struct regmap *regmap = cs40l26->regmap;
- u32 vbbr_thld = 0, vpbr_thld = 0;
- u32 vbbr_max_att = 0, vpbr_max_att = 0;
u32 vpbr_atk_step = 0, vbbr_atk_step = 0;
u32 vpbr_atk_rate = 0, vbbr_atk_rate = 0;
- u32 vpbr_wait = 0, vbbr_wait = 0;
u32 vpbr_rel_rate = 0, vbbr_rel_rate = 0;
+ u32 vbbr_max_att = 0, vpbr_max_att = 0;
+ u32 vbbr_thld = 0, vpbr_thld = 0;
+ u32 vpbr_wait = 0, vbbr_wait = 0;
+ u32 pseq_val = 0, pseq_mask = 0;
u32 val;
int ret;
@@ -3205,20 +3154,16 @@ static int cs40l26_brownout_prevention_init(struct cs40l26_private *cs40l26)
return ret;
}
- ret = cs40l26_pseq_add_write_reg_full(cs40l26, CS40L26_BLOCK_ENABLES2,
- val, true);
+ ret = cs40l26_pseq_write(cs40l26, CS40L26_BLOCK_ENABLES2,
+ val, CS40L26_PSEQ_OP_WRITE_FULL);
if (ret) {
dev_err(dev, "Failed to sequence brownout prevention\n");
return ret;
}
if (cs40l26->pdata.vbbr_en) {
- ret = cs40l26_irq_update_mask(cs40l26, CS40L26_IRQ1_MASK_2,
- BIT(CS40L26_IRQ2_VBBR_ATT_CLR) |
- BIT(CS40L26_IRQ2_VBBR_FLAG),
- CS40L26_IRQ_UNMASK);
- if (ret)
- return ret;
+ pseq_mask |= BIT(CS40L26_IRQ2_VBBR_ATT_CLR) |
+ BIT(CS40L26_IRQ2_VBBR_FLAG);
ret = regmap_read(regmap, CS40L26_VBBR_CONFIG, &val);
if (ret) {
@@ -3310,8 +3255,8 @@ static int cs40l26_brownout_prevention_init(struct cs40l26_private *cs40l26)
return ret;
}
- ret = cs40l26_pseq_add_write_reg_full(cs40l26,
- CS40L26_VBBR_CONFIG, val, true);
+ ret = cs40l26_pseq_write(cs40l26, CS40L26_VBBR_CONFIG, val,
+ CS40L26_PSEQ_OP_WRITE_FULL);
if (ret)
return ret;
}
@@ -3323,12 +3268,8 @@ static int cs40l26_brownout_prevention_init(struct cs40l26_private *cs40l26)
}
if (cs40l26->pdata.vpbr_en) {
- ret = cs40l26_irq_update_mask(cs40l26, CS40L26_IRQ1_MASK_2,
- BIT(CS40L26_IRQ2_VPBR_ATT_CLR) |
- BIT(CS40L26_IRQ2_VPBR_FLAG),
- CS40L26_IRQ_UNMASK);
- if (ret)
- return ret;
+ pseq_mask |= BIT(CS40L26_IRQ2_VPBR_ATT_CLR) |
+ BIT(CS40L26_IRQ2_VPBR_FLAG);
if (cs40l26->pdata.vpbr_thld) {
if (cs40l26->pdata.vpbr_thld
@@ -3417,13 +3358,14 @@ static int cs40l26_brownout_prevention_init(struct cs40l26_private *cs40l26)
return ret;
}
- ret = cs40l26_pseq_add_write_reg_full(cs40l26,
- CS40L26_VPBR_CONFIG, val, true);
+ ret = cs40l26_pseq_write(cs40l26, CS40L26_VPBR_CONFIG, val,
+ CS40L26_PSEQ_OP_WRITE_FULL);
if (ret)
return ret;
}
- return 0;
+ return cs40l26_irq_update_mask(cs40l26, CS40L26_IRQ1_MASK_2,
+ pseq_val, pseq_mask);
}
static int cs40l26_verify_fw(struct cs40l26_private *cs40l26)
@@ -3483,10 +3425,8 @@ static int cs40l26_asp_config(struct cs40l26_private *cs40l26)
goto err_free;
}
- ret = cs40l26_pseq_multi_add_write_reg_full(cs40l26, dsp1rx_config, 2,
- true);
- if (ret)
- dev_err(cs40l26->dev, "Failed to add ASP config to pseq\n");
+ ret = cs40l26_pseq_multi_write(cs40l26, dsp1rx_config, 2, true,
+ CS40L26_PSEQ_OP_WRITE_L16);
err_free:
kfree(dsp1rx_config);
@@ -3515,13 +3455,107 @@ static int cs40l26_bst_dcm_config(struct cs40l26_private *cs40l26)
return ret;
}
- ret = cs40l26_pseq_add_write_reg_full(cs40l26,
- CS40L26_BST_DCM_CTL, val, true);
+ ret = cs40l26_pseq_write(cs40l26, CS40L26_BST_DCM_CTL,
+ val, CS40L26_PSEQ_OP_WRITE_FULL);
}
return ret;
}
+static int calib_device_tree_config(struct cs40l26_private *cs40l26)
+{
+ int ret = 0;
+ u32 reg, bst_ctl, bst_ctl_cfg;
+
+ if (cs40l26->pdata.f0_default <= CS40L26_F0_EST_MAX &&
+ cs40l26->pdata.f0_default >= CS40L26_F0_EST_MIN) {
+ ret = cl_dsp_get_reg(cs40l26->dsp, "F0_OTP_STORED",
+ CL_DSP_XM_UNPACKED_TYPE,
+ CS40L26_VIBEGEN_ALGO_ID, &reg);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(cs40l26->regmap, reg,
+ cs40l26->pdata.f0_default);
+ if (ret) {
+ dev_err(cs40l26->dev, "Failed to write default f0\n");
+ return ret;
+ }
+ }
+
+ if (cs40l26->pdata.redc_default && cs40l26->pdata.redc_default <=
+ CS40L26_UINT_24_BITS_MAX) {
+ ret = cl_dsp_get_reg(cs40l26->dsp, "REDC_OTP_STORED",
+ CL_DSP_XM_UNPACKED_TYPE,
+ CS40L26_VIBEGEN_ALGO_ID, &reg);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(cs40l26->regmap, reg,
+ cs40l26->pdata.redc_default);
+ if (ret) {
+ dev_err(cs40l26->dev, "Failed to write default ReDC\n");
+ return ret;
+ }
+ }
+
+ if (cs40l26->pdata.q_default <= CS40L26_Q_EST_MAX &&
+ cs40l26->pdata.q_default >= CS40L26_Q_EST_MIN) {
+ ret = cl_dsp_get_reg(cs40l26->dsp, "Q_STORED",
+ CL_DSP_XM_UNPACKED_TYPE,
+ CS40L26_VIBEGEN_ALGO_ID, &reg);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(cs40l26->regmap, reg,
+ cs40l26->pdata.q_default);
+ if (ret) {
+ dev_err(cs40l26->dev, "Failed to write default Q\n");
+ return ret;
+ }
+ }
+
+ if (cs40l26->pdata.boost_ctl <= CS40L26_BST_VOLT_MAX &&
+ cs40l26->pdata.boost_ctl >= CS40L26_BST_VOLT_MIN) {
+ bst_ctl = ((cs40l26->pdata.boost_ctl - CS40L26_BST_VOLT_MIN)
+ / CS40L26_BST_VOLT_STEP) + 1;
+
+ ret = regmap_write(cs40l26->regmap, CS40L26_VBST_CTL_1,
+ bst_ctl);
+ if (ret) {
+ dev_err(cs40l26->dev, "Failed to write VBST limit\n");
+ return ret;
+ }
+
+ ret = cs40l26_pseq_write(cs40l26, CS40L26_VBST_CTL_1, bst_ctl,
+ CS40L26_PSEQ_OP_WRITE_L16);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(cs40l26->regmap, CS40L26_VBST_CTL_2,
+ &bst_ctl_cfg);
+ if (ret) {
+ dev_err(cs40l26->dev, "Failed to get VBST config\n");
+ return ret;
+ }
+
+ bst_ctl_cfg |= (1 << CS40L26_BST_CTL_LIM_EN_SHIFT);
+
+ ret = regmap_write(cs40l26->regmap, CS40L26_VBST_CTL_2,
+ bst_ctl_cfg);
+ if (ret) {
+ dev_err(cs40l26->dev, "Failed to write VBST config\n");
+ return ret;
+ }
+
+ ret = cs40l26_pseq_write(cs40l26, CS40L26_VBST_CTL_2,
+ bst_ctl_cfg, CS40L26_PSEQ_OP_WRITE_FULL);
+ }
+
+ return cs40l26_irq_update_mask(cs40l26, CS40L26_IRQ1_MASK_1, 0,
+ BIT(CS40L26_IRQ1_BST_IPK_FLAG));
+}
+
static int cs40l26_bst_ipk_config(struct cs40l26_private *cs40l26)
{
u32 val, bst_ipk_ma = cs40l26->pdata.bst_ipk / MILLIAMPS_PER_AMPS;
@@ -3532,7 +3566,8 @@ 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 / CS40L26_BST_IPK_CTL_STEP_SIZE) - CS40L26_BST_IPK_CTL_RESERVED;
+ 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);
@@ -3541,8 +3576,8 @@ static int cs40l26_bst_ipk_config(struct cs40l26_private *cs40l26)
return ret;
}
- return cs40l26_pseq_add_write_reg_full(cs40l26, CS40L26_BST_IPK_CTL,
- val, true);
+ return cs40l26_pseq_write(cs40l26, CS40L26_BST_IPK_CTL, val,
+ CS40L26_PSEQ_OP_WRITE_L16);
}
static int cs40l26_owt_setup(struct cs40l26_private *cs40l26)
@@ -3620,12 +3655,16 @@ static int cs40l26_dsp_config(struct cs40l26_private *cs40l26)
if (ret)
return ret;
- ret = cs40l26_iseq_init(cs40l26);
+ ret = cs40l26_irq_update_mask(cs40l26, CS40L26_IRQ1_MASK_1, 0,
+ BIT(CS40L26_IRQ1_VIRTUAL2_MBOX_WR));
if (ret)
return ret;
- ret = cs40l26_irq_update_mask(cs40l26, CS40L26_IRQ1_MASK_1,
- BIT(CS40L26_IRQ1_VIRTUAL2_MBOX_WR), CS40L26_IRQ_UNMASK);
+ ret = cs40l26_irq_update_mask(cs40l26, CS40L26_IRQ1_MASK_1, 0,
+ BIT(CS40L26_IRQ1_AMP_ERR) | BIT(CS40L26_IRQ1_TEMP_ERR) |
+ BIT(CS40L26_IRQ1_BST_SHORT_ERR) |
+ BIT(CS40L26_IRQ1_BST_DCM_UVP_ERR) |
+ BIT(CS40L26_IRQ1_BST_OVP_ERR));
if (ret)
return ret;
@@ -3645,6 +3684,12 @@ static int cs40l26_dsp_config(struct cs40l26_private *cs40l26)
if (ret)
return ret;
+ if (!cs40l26->vibe_init_success) {
+ ret = calib_device_tree_config(cs40l26);
+ if (ret)
+ return ret;
+ }
+
ret = cs40l26_brownout_prevention_init(cs40l26);
if (ret)
return ret;
@@ -3678,9 +3723,7 @@ static int cs40l26_dsp_config(struct cs40l26_private *cs40l26)
if (ret)
return ret;
- ret = cs40l26_pm_runtime_setup(cs40l26);
- if (ret)
- return ret;
+ cs40l26_pm_runtime_setup(cs40l26);
pm_runtime_get_sync(dev);
@@ -3796,6 +3839,11 @@ static void cs40l26_coeff_load(struct cs40l26_private *cs40l26)
int i, ret;
for (i = 0; i < cs40l26->fw.num_coeff_files; i++) {
+ if (strncmp(cs40l26->fw.coeff_files[i], CS40L26_WT_FILE_NAME,
+ CS40L26_WT_FILE_NAME_LEN)
+ && cs40l26->fw.id == CS40L26_FW_CALIB_ID)
+ continue;
+
ret = request_firmware(&coeff, cs40l26->fw.coeff_files[i], dev);
if (ret) {
dev_warn(dev, "Continuing...");
@@ -3814,18 +3862,17 @@ static void cs40l26_coeff_load(struct cs40l26_private *cs40l26)
}
}
-static int cs40l26_change_firmware_control_defaults(
- struct cs40l26_private *cs40l26)
+static int cs40l26_change_fw_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);
+ ret = cs40l26_pm_stdby_timeout_ms_set(cs40l26,
+ cs40l26->pdata.pm_stdby_timeout_ms);
if (ret)
- dev_err(dev, "failed to set pm_timer_timeout_ticks4, %d", ret);
+ return ret;
- return ret;
+ return cs40l26_pm_active_timeout_ms_set(cs40l26,
+ cs40l26->pdata.pm_active_timeout_ms);
}
static int cs40l26_firmware_load(struct cs40l26_private *cs40l26, u32 id)
@@ -3850,13 +3897,7 @@ static int cs40l26_firmware_load(struct cs40l26_private *cs40l26, u32 id)
return ret;
}
- ret = cs40l26_change_firmware_control_defaults(cs40l26);
-
- if (ret)
- dev_err(dev, "changing firmware control defaults failed %d",
- ret);
-
- return ret;
+ return cs40l26_change_fw_control_defaults(cs40l26);
}
static int cs40l26_fw_upload(struct cs40l26_private *cs40l26, u32 id)
@@ -3907,21 +3948,24 @@ int cs40l26_fw_swap(struct cs40l26_private *cs40l26, u32 id)
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,
+ pm_runtime_get_sync(dev);
+ ret = cs40l26_pm_stdby_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;
}
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
+ }
+
+ 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);
}
cs40l26->fw_loaded = false;
@@ -3931,11 +3975,12 @@ int cs40l26_fw_swap(struct cs40l26_private *cs40l26, u32 id)
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);
+ 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);
+ dev_err(dev, "Failed to request threaded IRQ: %d\n",
+ ret);
return ret;
}
} else {
@@ -4129,11 +4174,17 @@ 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;
+ if (!of_property_read_u32(np, "cirrus,pm-stdby-timeout-ms", &val))
+ cs40l26->pdata.pm_stdby_timeout_ms = val;
+ else
+ cs40l26->pdata.pm_stdby_timeout_ms =
+ CS40L26_PM_TIMEOUT_MS_MIN;
+
+ if (!of_property_read_u32(np, "cirrus,pm-active-timeout-ms", &val))
+ cs40l26->pdata.pm_active_timeout_ms = val;
else
- cs40l26->pdata.pm_timer_timeout_ticks4 =
- CS40L26_PM_TIMER_TIMEOUT_TICKS4_DEFAULT;
+ cs40l26->pdata.pm_active_timeout_ms =
+ CS40L26_PM_ACTIVE_TIMEOUT_MS_DEFAULT;
ret = cs40l26_handle_svc_le_nodes(cs40l26);
if (ret < 0)
@@ -4141,6 +4192,30 @@ static int cs40l26_handle_platform_data(struct cs40l26_private *cs40l26)
else
cs40l26->num_svc_le_vals = ret;
+ cs40l26->pdata.asp_scale_pct = CS40L26_GAIN_FULL_SCALE;
+ cs40l26->gain_pct = CS40L26_GAIN_FULL_SCALE;
+ cs40l26->gain_tmp = CS40L26_GAIN_FULL_SCALE;
+ if (!of_property_read_u32(np, "cirrus,asp-gain-scale-pct", &val)) {
+ if (val <= CS40L26_GAIN_FULL_SCALE)
+ cs40l26->pdata.asp_scale_pct = val;
+ else
+ dev_warn(dev, "ASP scaling > 100 %%, using maximum\n");
+ }
+
+ if (!of_property_read_u32(np, "cirrus,boost-ctl-microvolt", &val))
+ cs40l26->pdata.boost_ctl = val | CS40L26_PDATA_PRESENT;
+ else
+ cs40l26->pdata.boost_ctl = CS40L26_BST_CTL_DEFAULT;
+
+ if (!of_property_read_u32(np, "cirrus,f0-default", &val))
+ cs40l26->pdata.f0_default = val;
+
+ if (!of_property_read_u32(np, "cirrus,redc-default", &val))
+ cs40l26->pdata.redc_default = val;
+
+ if (!of_property_read_u32(np, "cirrus,q-default", &val))
+ cs40l26->pdata.q_default = val;
+
return 0;
}
@@ -4226,9 +4301,9 @@ int cs40l26_probe(struct cs40l26_private *cs40l26,
if (ret)
goto err;
- ret = devm_request_threaded_irq(dev, cs40l26->irq, NULL, cs40l26_irq,
- IRQF_ONESHOT | IRQF_SHARED | IRQF_TRIGGER_LOW,
- "cs40l26", cs40l26);
+ 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;