diff options
-rw-r--r-- | cs40l26/cs40l26-sysfs.c | 143 | ||||
-rw-r--r-- | cs40l26/cs40l26-tables.c | 20 | ||||
-rw-r--r-- | cs40l26/cs40l26.c | 1005 | ||||
-rw-r--r-- | cs40l26/cs40l26.h | 126 |
4 files changed, 732 insertions, 562 deletions
diff --git a/cs40l26/cs40l26-sysfs.c b/cs40l26/cs40l26-sysfs.c index c1c2e34..88c857c 100644 --- a/cs40l26/cs40l26-sysfs.c +++ b/cs40l26/cs40l26-sysfs.c @@ -23,14 +23,15 @@ static ssize_t dsp_state_show(struct device *dev, pm_runtime_get_sync(cs40l26->dev); ret = cs40l26_dsp_state_get(cs40l26, &dsp_state); - if (ret) - return ret; pm_runtime_mark_last_busy(cs40l26->dev); pm_runtime_put_autosuspend(cs40l26->dev); - return snprintf(buf, PAGE_SIZE, "%u\n", - (unsigned int) (dsp_state & 0xFF)); + if (ret) + return ret; + else + return snprintf(buf, PAGE_SIZE, "%u\n", + (unsigned int) (dsp_state & 0xFF)); } static DEVICE_ATTR_RO(dsp_state); @@ -41,20 +42,21 @@ static ssize_t halo_heartbeat_show(struct device *dev, u32 reg, halo_heartbeat; int ret; - pm_runtime_get_sync(cs40l26->dev); - ret = cl_dsp_get_reg(cs40l26->dsp, "HALO_HEARTBEAT", CL_DSP_XM_UNPACKED_TYPE, cs40l26->fw.id, ®); if (ret) return ret; + pm_runtime_get_sync(cs40l26->dev); + ret = regmap_read(cs40l26->regmap, reg, &halo_heartbeat); - if (ret) - return ret; pm_runtime_mark_last_busy(cs40l26->dev); pm_runtime_put_autosuspend(cs40l26->dev); + if (ret) + return ret; + return snprintf(buf, PAGE_SIZE, "%d\n", halo_heartbeat); } static DEVICE_ATTR_RO(halo_heartbeat); @@ -86,7 +88,7 @@ static ssize_t fw_mode_show(struct device *dev, } static DEVICE_ATTR_RO(fw_mode); -static ssize_t pm_timeout_ms_show(struct device *dev, +static ssize_t pm_stdby_timeout_ms_show(struct device *dev, struct device_attribute *attr, char *buf) { struct cs40l26_private *cs40l26 = dev_get_drvdata(dev); @@ -95,7 +97,7 @@ static ssize_t pm_timeout_ms_show(struct device *dev, pm_runtime_get_sync(cs40l26->dev); - ret = cs40l26_pm_timeout_ms_get(cs40l26, &timeout_ms); + ret = cs40l26_pm_stdby_timeout_ms_get(cs40l26, &timeout_ms); pm_runtime_mark_last_busy(cs40l26->dev); pm_runtime_put_autosuspend(cs40l26->dev); @@ -106,7 +108,7 @@ static ssize_t pm_timeout_ms_show(struct device *dev, return snprintf(buf, PAGE_SIZE, "%u\n", timeout_ms); } -static ssize_t pm_timeout_ms_store(struct device *dev, +static ssize_t pm_stdby_timeout_ms_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct cs40l26_private *cs40l26 = dev_get_drvdata(dev); @@ -114,12 +116,12 @@ static ssize_t pm_timeout_ms_store(struct device *dev, int ret; ret = kstrtou32(buf, 10, &timeout_ms); - if (ret || timeout_ms < CS40L26_PM_TIMEOUT_MS_MIN) + if (ret) return -EINVAL; pm_runtime_get_sync(cs40l26->dev); - ret = cs40l26_pm_timeout_ms_set(cs40l26, timeout_ms); + ret = cs40l26_pm_stdby_timeout_ms_set(cs40l26, timeout_ms); pm_runtime_mark_last_busy(cs40l26->dev); pm_runtime_put_autosuspend(cs40l26->dev); @@ -129,7 +131,52 @@ static ssize_t pm_timeout_ms_store(struct device *dev, return count; } -static DEVICE_ATTR_RW(pm_timeout_ms); +static DEVICE_ATTR_RW(pm_stdby_timeout_ms); + +static ssize_t pm_active_timeout_ms_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct cs40l26_private *cs40l26 = dev_get_drvdata(dev); + u32 timeout_ms; + int ret; + + pm_runtime_get_sync(cs40l26->dev); + + ret = cs40l26_pm_active_timeout_ms_get(cs40l26, &timeout_ms); + + pm_runtime_mark_last_busy(cs40l26->dev); + pm_runtime_put_autosuspend(cs40l26->dev); + + if (ret) + return ret; + + return snprintf(buf, PAGE_SIZE, "%u\n", timeout_ms); +} + +static ssize_t pm_active_timeout_ms_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct cs40l26_private *cs40l26 = dev_get_drvdata(dev); + u32 timeout_ms; + int ret; + + ret = kstrtou32(buf, 10, &timeout_ms); + if (ret) + return -EINVAL; + + pm_runtime_get_sync(cs40l26->dev); + + ret = cs40l26_pm_active_timeout_ms_set(cs40l26, timeout_ms); + + pm_runtime_mark_last_busy(cs40l26->dev); + pm_runtime_put_autosuspend(cs40l26->dev); + + if (ret) + return ret; + + return count; +} +static DEVICE_ATTR_RW(pm_active_timeout_ms); static ssize_t vibe_state_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -149,32 +196,57 @@ static ssize_t power_on_seq_show(struct device *dev, struct device_attribute *attr, char *buf) { struct cs40l26_private *cs40l26 = dev_get_drvdata(dev); - struct list_head *op_head = &cs40l26->pseq_op_head; - u32 base = cs40l26->pseq_base; - int i, count = 0; - struct cs40l26_pseq_op *pseq_op; + struct cs40l26_pseq_op *op; + u32 addr, data, base; + int ret; mutex_lock(&cs40l26->lock); - list_for_each_entry_reverse(pseq_op, op_head, list) { - dev_info(cs40l26->dev, "%d: Address: 0x%08X, Size: %d words\n", - count + 1, base + pseq_op->offset, pseq_op->size); - - for (i = 0; i < pseq_op->size; i++) - dev_info(cs40l26->dev, "0x%08X\n", - *(pseq_op->words + i)); - - count++; + base = cs40l26->pseq_base; + + list_for_each_entry_reverse(op, &cs40l26->pseq_op_head, list) { + switch (op->operation) { + case CS40L26_PSEQ_OP_WRITE_FULL: + addr = ((op->words[0] & 0xFFFF) << 16) | + ((op->words[1] & 0x00FFFF00) >> 8); + data = ((op->words[1] & 0xFF) << 24) | + (op->words[2] & 0xFFFFFF); + break; + case CS40L26_PSEQ_OP_WRITE_H16: + case CS40L26_PSEQ_OP_WRITE_L16: + addr = ((op->words[0] & 0xFFFF) << 8) | + ((op->words[1] & 0xFF0000) >> 16); + data = (op->words[1] & 0xFFFF); + + if (op->operation == CS40L26_PSEQ_OP_WRITE_H16) + data <<= 16; + break; + case CS40L26_PSEQ_OP_WRITE_ADDR8: + addr = (op->words[0] & 0xFF00) >> 8; + data = ((op->words[0] & 0xFF) << 24) | + (op->words[1] & 0xFFFFFF); + break; + case CS40L26_PSEQ_OP_END: + addr = CS40L26_PSEQ_OP_END_ADDR; + data = CS40L26_PSEQ_OP_END_DATA; + break; + default: + dev_err(cs40l26->dev, "Unrecognized Op Code: 0x%02X\n", + op->operation); + ret = -EINVAL; + goto err_mutex; + } + + dev_dbg(cs40l26->dev, + "0x%08x: code = 0x%02X, Addr = 0x%08X, Data = 0x%08X\n", + base + op->offset, op->operation, addr, data); } - mutex_unlock(&cs40l26->lock); - - if (count != cs40l26->pseq_num_ops) { - dev_err(cs40l26->dev, "Malformed Power on seq.\n"); - return -EINVAL; - } + ret = snprintf(buf, PAGE_SIZE, "%d\n", cs40l26->pseq_num_ops); - return snprintf(buf, PAGE_SIZE, "%d\n", cs40l26->pseq_num_ops); +err_mutex: + mutex_unlock(&cs40l26->lock); + return ret; } static DEVICE_ATTR_RO(power_on_seq); @@ -452,7 +524,8 @@ static struct attribute *cs40l26_dev_attrs[] = { &dev_attr_dsp_state.attr, &dev_attr_halo_heartbeat.attr, &dev_attr_fw_mode.attr, - &dev_attr_pm_timeout_ms.attr, + &dev_attr_pm_stdby_timeout_ms.attr, + &dev_attr_pm_active_timeout_ms.attr, &dev_attr_vibe_state.attr, &dev_attr_boost_disable_delay.attr, &dev_attr_f0_offset.attr, diff --git a/cs40l26/cs40l26-tables.c b/cs40l26/cs40l26-tables.c index bf115f4..f525e65 100644 --- a/cs40l26/cs40l26-tables.c +++ b/cs40l26/cs40l26-tables.c @@ -42,18 +42,18 @@ const struct dev_pm_ops cs40l26_pm_ops = { }; const u8 cs40l26_pseq_op_sizes[CS40L26_PSEQ_NUM_OPS][2] = { - { CS40L26_PSEQ_OP_WRITE_REG_FULL, - CS40L26_PSEQ_OP_WRITE_REG_FULL_WORDS}, + { CS40L26_PSEQ_OP_WRITE_FULL, + CS40L26_PSEQ_OP_WRITE_FULL_WORDS}, { CS40L26_PSEQ_OP_WRITE_FIELD, CS40L26_PSEQ_OP_WRITE_FIELD_WORDS}, - { CS40L26_PSEQ_OP_WRITE_REG_ADDR8, - CS40L26_PSEQ_OP_WRITE_REG_ADDR8_WORDS}, - { CS40L26_PSEQ_OP_WRITE_REG_INCR, - CS40L26_PSEQ_OP_WRITE_REG_INCR_WORDS}, - { CS40L26_PSEQ_OP_WRITE_REG_L16, - CS40L26_PSEQ_OP_WRITE_REG_L16_WORDS}, - { CS40L26_PSEQ_OP_WRITE_REG_H16, - CS40L26_PSEQ_OP_WRITE_REG_H16_WORDS}, + { CS40L26_PSEQ_OP_WRITE_ADDR8, + CS40L26_PSEQ_OP_WRITE_ADDR8_WORDS}, + { CS40L26_PSEQ_OP_WRITE_INCR, + CS40L26_PSEQ_OP_WRITE_INCR_WORDS}, + { CS40L26_PSEQ_OP_WRITE_L16, + CS40L26_PSEQ_OP_WRITE_X16_WORDS}, + { CS40L26_PSEQ_OP_WRITE_H16, + CS40L26_PSEQ_OP_WRITE_X16_WORDS}, { CS40L26_PSEQ_OP_DELAY, CS40L26_PSEQ_OP_DELAY_WORDS}, { CS40L26_PSEQ_OP_END, 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, ®); - 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, ®); 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, ®); 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, ®); + 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, ®); + 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, ®); + 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; diff --git a/cs40l26/cs40l26.h b/cs40l26/cs40l26.h index 80fcabe..7e60234 100644 --- a/cs40l26/cs40l26.h +++ b/cs40l26/cs40l26.h @@ -630,9 +630,6 @@ #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" @@ -689,7 +686,7 @@ /* algorithms */ -#define CS40L26_A2H_ALGO_ID 0x00010110 +#define CS40L26_A2H_ALGO_ID 0x00040110 #define CS40L26_BUZZGEN_ALGO_ID 0x0001F202 #define CS40L26_DYNAMIC_F0_ALGO_ID 0x0001F21B #define CS40L26_EVENT_HANDLER_ALGO_ID 0x0001F200 @@ -706,41 +703,58 @@ /* power management */ #define CS40L26_PSEQ_MAX_WORDS_PER_OP CS40L26_PSEQ_OP_WRITE_FIELD_WORDS #define CS40L26_PSEQ_MAX_WORDS 129 -#define CS40L26_PSEQ_NUM_OPS 8 -#define CS40L26_PSEQ_OP_MASK GENMASK(23, 16) +#define CS40L26_PSEQ_NUM_OPS 8 +#define CS40L26_PSEQ_OP_MASK GENMASK(23, 16) #define CS40L26_PSEQ_OP_SHIFT 16 -#define CS40L26_PSEQ_OP_WRITE_REG_FULL 0x00 -#define CS40L26_PSEQ_OP_WRITE_REG_FULL_WORDS 3 -#define CS40L26_PSEQ_OP_WRITE_FIELD 0x01 -#define CS40L26_PSEQ_OP_WRITE_FIELD_WORDS 4 -#define CS40L26_PSEQ_OP_WRITE_REG_ADDR8 0x02 -#define CS40L26_PSEQ_OP_WRITE_REG_ADDR8_WORDS 2 -#define CS40L26_PSEQ_OP_WRITE_REG_INCR 0x03 -#define CS40L26_PSEQ_OP_WRITE_REG_INCR_WORDS 2 -#define CS40L26_PSEQ_OP_WRITE_REG_L16 0x04 -#define CS40L26_PSEQ_OP_WRITE_REG_L16_WORDS 2 -#define CS40L26_PSEQ_OP_WRITE_REG_H16 0x05 -#define CS40L26_PSEQ_OP_WRITE_REG_H16_WORDS 2 +#define CS40L26_PSEQ_OP_WRITE_FULL 0x00 +#define CS40L26_PSEQ_OP_WRITE_FULL_WORDS 3 +#define CS40L26_PSEQ_OP_WRITE_FIELD 0x01 +#define CS40L26_PSEQ_OP_WRITE_FIELD_WORDS 4 +#define CS40L26_PSEQ_OP_WRITE_ADDR8 0x02 +#define CS40L26_PSEQ_OP_WRITE_ADDR8_WORDS 2 +#define CS40L26_PSEQ_OP_WRITE_INCR 0x03 +#define CS40L26_PSEQ_OP_WRITE_INCR_WORDS 2 +#define CS40L26_PSEQ_OP_WRITE_L16 0x04 +#define CS40L26_PSEQ_OP_WRITE_H16 0x05 +#define CS40L26_PSEQ_OP_WRITE_X16_WORDS 2 #define CS40L26_PSEQ_OP_DELAY 0xFE -#define CS40L26_PSEQ_OP_DELAY_WORDS 1 -#define CS40L26_PSEQ_OP_END 0xFF -#define CS40L26_PSEQ_OP_END_WORDS 1 +#define CS40L26_PSEQ_OP_DELAY_WORDS 1 +#define CS40L26_PSEQ_OP_END 0xFF +#define CS40L26_PSEQ_OP_END_WORDS 1 +#define CS40L26_PSEQ_OP_END_ADDR 0xFFFFFF +#define CS40L26_PSEQ_OP_END_DATA 0xFFFFFF #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_PSEQ_WRITE_FULL_LOWER_ADDR_SHIFT 8 +#define CS40L26_PSEQ_WRITE_FULL_UPPER_ADDR_SHIFT 16 +#define CS40L26_PSEQ_WRITE_FULL_LOWER_ADDR_MASK GENMASK(15, 0) +#define CS40L26_PSEQ_WRITE_FULL_UPPER_ADDR_MASK GENMASK(31, 0) +#define CS40L26_PSEQ_WRITE_FULL_UPPER_DATA_SHIFT 24 +#define CS40L26_PSEQ_WRITE_FULL_LOWER_DATA_MASK GENMASK(23, 0) +#define CS40L26_PSEQ_WRITE_FULL_UPPER_DATA_MASK GENMASK(31, 24) +#define CS40L26_PSEQ_WRITE_FULL_OP_MASK GENMASK(31, 8) +#define CS40L26_PSEQ_WRITE_X16_LOWER_ADDR_SHIFT 16 +#define CS40L26_PSEQ_WRITE_X16_LOWER_ADDR_MASK GENMASK(7, 0) +#define CS40L26_PSEQ_WRITE_X16_UPPER_ADDR_SHIFT 8 +#define CS40L26_PSEQ_WRITE_X16_UPPER_ADDR_MASK GENMASK(23, 8) +#define CS40L26_PSEQ_WRITE_X16_UPPER_DATA_SHIFT 0 +#define CS40L26_PSEQ_WRITE_X16_UPPER_DATA_MASK GENMASK(31, 0) +#define CS40L26_PSEQ_WRITE_X16_OP_MASK GENMASK(23, 16) #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_STDBY_TIMEOUT_MS_DEFAULT 5000 +#define CS40L26_PM_TIMEOUT_MS_MIN 100 +#define CS40L26_PM_TIMEOUT_MS_MAX 4880 +#define CS40L26_PM_ACTIVE_TIMEOUT_LOWER_OFFSET 24 +#define CS40L26_PM_ACTIVE_TIMEOUT_UPPER_OFFSET 28 +#define CS40L26_PM_ACTIVE_TIMEOUT_MS_DEFAULT 250 #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_PM_TICKS_MS_DIV 32 #define CS40L26_AUTOSUSPEND_DELAY_MS 2000 @@ -922,11 +936,6 @@ #define CS40L26_IRQ_STATUS_DEASSERT 0x0 #define CS40L26_IRQ_STATUS_ASSERT 0x1 -#define CS40L26_ISEQ_MAX_ENTRIES 4 - -#define CS40L26_IRQ_UNMASK 0 -#define CS40L26_IRQ_MASK 1 - #define CS40L26_IRQ_EINT1_ALL_MASK 0xFFDC7FFF #define CS40L26_IRQ_EINT2_ALL_MASK 0x07DE0400 @@ -945,6 +954,16 @@ #define CS40L26_BST_IPK_MILLIAMP_MIN 1600 #define MILLIAMPS_PER_AMPS 1000 +#define CS40L26_BST_VOLT_MIN 2550000 +#define CS40L26_BST_VOLT_MAX 11000000 +#define CS40L26_BST_CTL_DEFAULT 11000000 +#define CS40L26_BST_VOLT_STEP 50000 +#define CS40L26_BST_CTL_MASK GENMASK(7, 0) +#define CS40L26_BST_CTL_SHIFT 0 + +#define CS40L26_BST_CTL_LIM_EN_MASK BIT(2) +#define CS40L26_BST_CTL_LIM_EN_SHIFT 2 + #define CS40L26_BST_IPK_DEFAULT 0x4A #define CS40L26_BST_IPK_CTL_STEP_SIZE 50 #define CS40L26_BST_IPK_CTL_RESERVED 16 @@ -1120,10 +1139,15 @@ #define CS40L26_A2H_DELAY_MAX 0x190 +#define CS40L26_PDATA_PRESENT 0x80000000 +#define CS40L26_PDATA_MASK ~CS40L26_PDATA_PRESENT + #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 +#define CS40L26_GAIN_FULL_SCALE 100 + /* OWT */ #define CS40L26_WT_STR_MAX_LEN 512 #define CS40L26_WT_MAX_SEGS 512 @@ -1171,6 +1195,8 @@ #define CS40L26_LOGGER_SRC_ID_MASK GENMASK(20, 16) #define CS40L26_LOGGER_SRC_ID_SHIFT 16 +#define CS40L26_UINT_24_BITS_MAX 16777215 + /* MFD */ #define CS40L26_NUM_MFD_DEVS 1 @@ -1210,13 +1236,6 @@ enum cs40l26_fw_mode { CS40L26_FW_MODE_NONE, }; -enum cs40l26_iseq { - CS40L26_ISEQ_MASK1, - CS40L26_ISEQ_MASK2, - CS40L26_ISEQ_EDGE1, - CS40L26_ISEQ_POL1, -}; - enum cs40l26_err_rls { CS40L26_RSRVD_ERR_RLS,/* 0 */ CS40L26_AMP_SHORT_ERR_RLS,/* 1 */ @@ -1319,11 +1338,6 @@ struct cs40l26_owt_section { u16 duration; }; -struct cs40l26_iseq_pair { - u32 addr; - u32 val; -}; - struct cs40l26_pseq_op { u8 size; u16 offset; /* offset in bytes from pseq_base */ @@ -1356,7 +1370,13 @@ struct cs40l26_platform_data { u32 vpbr_rel_rate; bool bst_dcm_en; u32 bst_ipk; - u32 pm_timer_timeout_ticks4; + u32 asp_scale_pct; + u32 pm_active_timeout_ms; + u32 pm_stdby_timeout_ms; + u32 f0_default; + u32 redc_default; + u32 q_default; + u32 boost_ctl; }; struct cs40l26_owt { @@ -1389,11 +1409,10 @@ struct cs40l26_private { struct workqueue_struct *vibe_workqueue; int irq; bool vibe_init_success; - unsigned int pseq_num_ops; + int pseq_num_ops; u32 pseq_base; struct list_head pseq_op_head; enum cs40l26_pm_state pm_state; - struct cs40l26_iseq_pair iseq_table[CS40L26_ISEQ_MAX_ENTRIES]; enum cs40l26_fw_mode fw_mode; enum cs40l26_vibe_state vibe_state; int num_loaded_coeff_files; @@ -1407,6 +1426,8 @@ struct cs40l26_private { int num_owt_effects; int cal_requested; u16 gain_pct; + u16 gain_tmp; + bool scaling_applied; u32 event_map_base; struct cs40l26_svc_le **svc_le_vals; int num_svc_le_vals; @@ -1444,17 +1465,18 @@ int cs40l26_get_num_waves(struct cs40l26_private *cs40l26, u32 *num_waves); int cs40l26_fw_swap(struct cs40l26_private *cs40l26, u32 id); 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, +int cs40l26_pm_stdby_timeout_ms_get(struct cs40l26_private *cs40l26, + u32 *timeout_ms); +int cs40l26_pm_stdby_timeout_ms_set(struct cs40l26_private *cs40l26, + u32 timeout_ms); +int cs40l26_pm_active_timeout_ms_get(struct cs40l26_private *cs40l26, u32 *timeout_ms); -int cs40l26_pm_timeout_ms_set(struct cs40l26_private *cs40l26, +int cs40l26_pm_active_timeout_ms_set(struct cs40l26_private *cs40l26, u32 timeout_ms); int cs40l26_pm_state_transition(struct cs40l26_private *cs40l26, enum cs40l26_pm_state state); int cs40l26_ack_write(struct cs40l26_private *cs40l26, u32 reg, u32 write_val, u32 reset_val); -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 cs40l26_resume(struct device *dev); int cs40l26_sys_resume(struct device *dev); int cs40l26_sys_resume_noirq(struct device *dev); |