summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTai Kuo <taikuo@google.com>2022-04-06 18:41:39 +0800
committerTreeHugger Robot <treehugger-gerrit@google.com>2022-04-18 07:33:02 +0000
commit33811b4ea38472975ac5cb9896dbd8637311e202 (patch)
tree1c492b8c5e04c0f617033187c36842286453a249
parentc3f6e596c4fd7dbd73b57be8a6e4ba179396fb35 (diff)
downloadamplifiers-33811b4ea38472975ac5cb9896dbd8637311e202.tar.gz
cs40l26: merge cs40l26 v5.0.0
Branch: v5.10-cs40l26 Tag: cs40l26-v5.0.0_5.10 Files: drivers/input/misc/cs40l26-i2c.c drivers/input/misc/cs40l26-spi.c 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 - Skip cs40l26_set_tdm_slot() since removed. Features: - Support for 1.2V variant CS40L27 - Support for SVC calibration period in ASP waveforms Bug fixes: - Resolve race condition between IRQ clear write and DSP to host mailbox messages. - Avoid I2C timeouts by preventing hibernation directly after the DSP starts at probe then allowing hibernation once initialization is complete. Major API changes: - The sysfs control "fw_mode" which resided in "default" has been removed. The driver does not support a full-featured ROM mode so this control served no purpose. - The sysfs control "calib_fw_load" has moved from the "calibration" directory to the "default" directory and has been renamed "swap_firmware". The behavior of the control is unchanged. Commits: 733da2 input: cs40l26: Change firmware loading sysfs control 3120e4 input: cs40l26: Remove support for firmware mode 7db090 input: cs40l26: Print error register as hex f0c156 ASoC: cs40l26: Wait for SVC calibration 9bf4b8 input: cs40l26: Add event for end of SVC calibration c32679 input: cs40l26: Clear EINT before servicing mailbox (Skip) 9df7d5 Documentation: cs40l26: Add "cs40l27" compatible name 13710b input: cs40l26: Add support for CS40L27 and rev. B0 756dbc input: cs40l26: Prevent hibernation after DSP start 00e050 ASoC: cs40l26: Remove check of dai->id in set_tdm_slot() 80b821 input: cs40l26: Ensure write to PSEQ is acknowledged Bug: 228290494 Test: Copy texts and adjust alarm Test: NFC, dumpstate, keyboard vibration Test: idlcli commands Test: Switch firmware continuous Test: Switch firmware and check the first tick effect Signed-off-by: Tai Kuo <taikuo@google.com> Change-Id: I1961844366065acc2951c386f1516877648aaaf9
-rw-r--r--cs40l26/cs40l26-codec.c7
-rw-r--r--cs40l26/cs40l26-i2c.c2
-rw-r--r--cs40l26/cs40l26-spi.c2
-rw-r--r--cs40l26/cs40l26-sysfs.c115
-rw-r--r--cs40l26/cs40l26-tables.c2
-rw-r--r--cs40l26/cs40l26.c244
-rw-r--r--cs40l26/cs40l26.h24
7 files changed, 192 insertions, 204 deletions
diff --git a/cs40l26/cs40l26-codec.c b/cs40l26/cs40l26-codec.c
index 80dd854..4cf74bd 100644
--- a/cs40l26/cs40l26-codec.c
+++ b/cs40l26/cs40l26-codec.c
@@ -108,6 +108,13 @@ static int cs40l26_clk_en(struct snd_soc_dapm_widget *w,
if (ret)
return ret;
+ if (!completion_done(&cs40l26->i2s_cont)) {
+ if (!wait_for_completion_timeout(&cs40l26->i2s_cont,
+ msecs_to_jiffies(CS40L26_ASP_START_TIMEOUT)))
+ dev_warn(codec->dev,
+ "SVC calibration not complete\n");
+ }
+
ret = cs40l26_swap_ext_clk(codec, CS40L26_PLL_REFCLK_BCLK);
if (ret)
return ret;
diff --git a/cs40l26/cs40l26-i2c.c b/cs40l26/cs40l26-i2c.c
index 9e7b614..fac18be 100644
--- a/cs40l26/cs40l26-i2c.c
+++ b/cs40l26/cs40l26-i2c.c
@@ -15,6 +15,8 @@
static const struct i2c_device_id cs40l26_id_i2c[] = {
{"cs40l26a", 0},
{"cs40l26b", 1},
+ {"cs40l27a", 2},
+ {"cs40l27b", 3},
{}
};
diff --git a/cs40l26/cs40l26-spi.c b/cs40l26/cs40l26-spi.c
index 7c86d37..695cab8 100644
--- a/cs40l26/cs40l26-spi.c
+++ b/cs40l26/cs40l26-spi.c
@@ -15,6 +15,8 @@
static const struct spi_device_id cs40l26_id_spi[] = {
{"cs40l26a", 0},
{"cs40l26b", 1},
+ {"cs40l27a", 2},
+ {"cs40l27b", 3},
{}
};
diff --git a/cs40l26/cs40l26-sysfs.c b/cs40l26/cs40l26-sysfs.c
index 77ee9f5..1e3c086 100644
--- a/cs40l26/cs40l26-sysfs.c
+++ b/cs40l26/cs40l26-sysfs.c
@@ -69,33 +69,6 @@ static ssize_t halo_heartbeat_show(struct device *dev,
}
static DEVICE_ATTR_RO(halo_heartbeat);
-static ssize_t fw_mode_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct cs40l26_private *cs40l26 = dev_get_drvdata(dev);
- int ret = 0;
- unsigned int mode;
-
- mutex_lock(&cs40l26->lock);
-
- if (cs40l26->fw_mode != CS40L26_FW_MODE_ROM
- && cs40l26->fw_mode != CS40L26_FW_MODE_RAM) {
- dev_err(cs40l26->dev, "Invalid firmware mode: %u\n",
- cs40l26->fw_mode);
- ret = -EINVAL;
- } else {
- mode = cs40l26->fw_mode;
- }
-
- mutex_unlock(&cs40l26->lock);
-
- if (ret)
- return ret;
-
- return snprintf(buf, PAGE_SIZE, "%u\n", mode);
-}
-static DEVICE_ATTR_RO(fw_mode);
-
static ssize_t pm_stdby_timeout_ms_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -734,6 +707,49 @@ err_mutex:
}
static DEVICE_ATTR_RW(redc_comp_enable);
+static ssize_t swap_firmware_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct cs40l26_private *cs40l26 = dev_get_drvdata(dev);
+ int ret;
+
+ mutex_lock(&cs40l26->lock);
+
+ if (cs40l26->fw.id == CS40L26_FW_ID)
+ ret = snprintf(buf, PAGE_SIZE, "%d\n", 0);
+ else if (cs40l26->fw.id == CS40L26_FW_CALIB_ID)
+ ret = snprintf(buf, PAGE_SIZE, "%d\n", 1);
+ else
+ ret = -EINVAL;
+
+ mutex_unlock(&cs40l26->lock);
+
+ return ret;
+}
+
+static ssize_t swap_firmware_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct cs40l26_private *cs40l26 = dev_get_drvdata(dev);
+ int ret;
+ unsigned int variant;
+
+ ret = kstrtou32(buf, 10, &variant);
+ if (ret)
+ return ret;
+
+ if (variant == 0)
+ ret = cs40l26_fw_swap(cs40l26, CS40L26_FW_ID);
+ else if (variant == 1)
+ ret = cs40l26_fw_swap(cs40l26, CS40L26_FW_CALIB_ID);
+ else
+ ret = -EINVAL;
+
+ return ret ? ret : count;
+}
+static DEVICE_ATTR_RW(swap_firmware);
+
static struct attribute *cs40l26_dev_attrs[] = {
&dev_attr_num_waves.attr,
&dev_attr_die_temp.attr,
@@ -741,7 +757,6 @@ static struct attribute *cs40l26_dev_attrs[] = {
&dev_attr_power_on_seq.attr,
&dev_attr_dsp_state.attr,
&dev_attr_halo_heartbeat.attr,
- &dev_attr_fw_mode.attr,
&dev_attr_pm_stdby_timeout_ms.attr,
&dev_attr_pm_active_timeout_ms.attr,
&dev_attr_vibe_state.attr,
@@ -750,6 +765,7 @@ static struct attribute *cs40l26_dev_attrs[] = {
&dev_attr_delay_before_stop_playback_us.attr,
&dev_attr_f0_comp_enable.attr,
&dev_attr_redc_comp_enable.attr,
+ &dev_attr_swap_firmware.attr,
NULL,
};
@@ -1837,50 +1853,7 @@ err_mutex:
}
static DEVICE_ATTR_RO(max_vmon);
-static ssize_t calib_fw_load_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct cs40l26_private *cs40l26 = dev_get_drvdata(dev);
- int ret;
-
- mutex_lock(&cs40l26->lock);
-
- if (cs40l26->fw.id == CS40L26_FW_ID)
- ret = snprintf(buf, PAGE_SIZE, "%d\n", 0);
- else if (cs40l26->fw.id == CS40L26_FW_CALIB_ID)
- ret = snprintf(buf, PAGE_SIZE, "%d\n", 1);
- else
- ret = -EINVAL;
-
- mutex_unlock(&cs40l26->lock);
-
- return ret;
-}
-
-static ssize_t calib_fw_load_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
-{
- struct cs40l26_private *cs40l26 = dev_get_drvdata(dev);
- int ret;
- unsigned int variant;
-
- ret = kstrtou32(buf, 10, &variant);
- if (ret)
- return ret;
-
- if (variant == 0)
- ret = cs40l26_fw_swap(cs40l26, CS40L26_FW_ID);
- else if (variant == 1)
- ret = cs40l26_fw_swap(cs40l26, CS40L26_FW_CALIB_ID);
- else
- ret = -EINVAL;
-
- return ret ? ret : count;
-}
-static DEVICE_ATTR_RW(calib_fw_load);
-
static struct attribute *cs40l26_dev_attrs_cal[] = {
- &dev_attr_calib_fw_load.attr,
&dev_attr_max_vbst.attr,
&dev_attr_max_bemf.attr,
&dev_attr_max_vmon.attr,
diff --git a/cs40l26/cs40l26-tables.c b/cs40l26/cs40l26-tables.c
index eb40114..c1de6c4 100644
--- a/cs40l26/cs40l26-tables.c
+++ b/cs40l26/cs40l26-tables.c
@@ -16,6 +16,8 @@
const struct of_device_id cs40l26_of_match[CS40L26_NUM_DEVS + 1] = {
{ .compatible = "cirrus,cs40l26a" },
{ .compatible = "cirrus,cs40l26b" },
+ { .compatible = "cirrus,cs40l27a" },
+ { .compatible = "cirrus,cs40l27b" },
{ }
};
MODULE_DEVICE_TABLE(of, cs40l26_of_match);
diff --git a/cs40l26/cs40l26.c b/cs40l26/cs40l26.c
index af905d6..c123910 100644
--- a/cs40l26/cs40l26.c
+++ b/cs40l26/cs40l26.c
@@ -709,6 +709,13 @@ static int cs40l26_handle_mbox_buffer(struct cs40l26_private *cs40l26)
break;
case CS40L26_DSP_MBOX_COMPLETE_I2S:
dev_dbg(dev, "Mailbox: COMPLETE_I2S\n");
+ /* ASP is interrupted */
+ if (cs40l26->asp_enable)
+ complete(&cs40l26->i2s_cont);
+ break;
+ case CS40L26_DSP_MBOX_TRIGGER_I2S:
+ dev_dbg(dev, "Mailbox: TRIGGER_I2S\n");
+ complete(&cs40l26->i2s_cont);
break;
case CS40L26_DSP_MBOX_TRIGGER_CP:
if (!cs40l26->pdata.vibe_state_reporting) {
@@ -785,7 +792,6 @@ static int cs40l26_handle_mbox_buffer(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;
@@ -796,10 +802,13 @@ int cs40l26_asp_start(struct cs40l26_private *cs40l26)
ret = cs40l26_ack_write(cs40l26, CS40L26_DSP_VIRTUAL1_MBOX_1,
CS40L26_STOP_PLAYBACK, CS40L26_DSP_MBOX_RESET);
if (ret) {
- dev_err(dev, "Failed to stop playback before I2S start\n");
+ dev_err(cs40l26->dev,
+ "Failed to stop playback before I2S start\n");
return ret;
}
+ reinit_completion(&cs40l26->i2s_cont);
+
ret = regmap_write(cs40l26->regmap, CS40L26_DSP_VIRTUAL1_MBOX_1,
CS40L26_DSP_MBOX_CMD_START_I2S);
if (ret) {
@@ -1058,10 +1067,13 @@ static int cs40l26_handle_irq1(struct cs40l26_private *cs40l26,
dev_dbg(dev, "Virtual 1 MBOX write occurred\n");
break;
case CS40L26_IRQ1_VIRTUAL2_MBOX_WR:
- ret = cs40l26_handle_mbox_buffer(cs40l26);
- if (ret)
+ ret = regmap_write(cs40l26->regmap, CS40L26_IRQ1_EINT_1, BIT(irq1));
+ if (ret) {
+ dev_err(dev, "Failed to clear Mailbox IRQ\n");
goto err;
- break;
+ }
+
+ return cs40l26_handle_mbox_buffer(cs40l26);
default:
dev_err(dev, "Unrecognized IRQ1 EINT1 status\n");
return -EINVAL;
@@ -2819,7 +2831,8 @@ static int cs40l26_erase_gpi_mapping(struct cs40l26_private *cs40l26,
reg = cs40l26->event_map_base + (i * 4);
ret = regmap_read(cs40l26->regmap, reg, &val);
if (ret) {
- dev_err(dev, "Failed to read gpi event reg: %u", reg);
+ dev_err(dev, "Failed to read gpi event reg: 0x%08X",
+ reg);
return ret;
}
gpi_index = val & 0xFF;
@@ -3039,7 +3052,8 @@ static int cs40l26_part_num_resolve(struct cs40l26_private *cs40l26)
}
val &= CS40L26_DEVID_MASK;
- if (val != CS40L26_DEVID_A && val != CS40L26_DEVID_B) {
+ if (val != CS40L26_DEVID_A && val != CS40L26_DEVID_B && val !=
+ CS40L26_DEVID_L27_A && val != CS40L26_DEVID_L27_B) {
dev_err(dev, "Invalid device ID: 0x%06X\n", val);
return -EINVAL;
}
@@ -3053,7 +3067,7 @@ static int cs40l26_part_num_resolve(struct cs40l26_private *cs40l26)
}
val &= CS40L26_REVID_MASK;
- if (val == CS40L26_REVID_A1) {
+ if (val == CS40L26_REVID_A1 || val == CS40L26_REVID_B0) {
cs40l26->revid = val;
} else {
dev_err(dev, "Invalid device revision: 0x%02X\n", val);
@@ -3086,74 +3100,74 @@ static int cs40l26_cl_dsp_init(struct cs40l26_private *cs40l26, u32 id)
return -ENOMEM;
}
- if (cs40l26->fw_mode == CS40L26_FW_MODE_ROM) {
- cs40l26->fw.id = CS40L26_FW_ID;
- cs40l26->fw.min_rev = CS40L26_FW_ROM_MIN_REV;
- cs40l26->fw.num_coeff_files = 0;
- } else {
- cs40l26->fw.id = id;
-
- if (id == CS40L26_FW_ID) {
- cs40l26->fw.min_rev = CS40L26_FW_A1_RAM_MIN_REV;
- cs40l26->fw.num_coeff_files = CS40L26_TUNING_FILES_RT;
- } else if (id == CS40L26_FW_CALIB_ID) {
- cs40l26->fw.min_rev = CS40L26_FW_CALIB_MIN_REV;
- cs40l26->fw.num_coeff_files = CS40L26_TUNING_FILES_CAL;
- } else {
- dev_err(cs40l26->dev, "Invalid firmware ID 0x%06X\n",
- id);
- return -EINVAL;
- }
+ cs40l26->fw.id = id;
- if (!cs40l26->fw.coeff_files)
- cs40l26->fw.coeff_files = devm_kcalloc(cs40l26->dev,
- cs40l26->fw.num_coeff_files, sizeof(char *),
- GFP_KERNEL);
+ if (id == CS40L26_FW_ID) {
+ cs40l26->fw.min_rev = CS40L26_FW_A1_RAM_MIN_REV;
+ cs40l26->fw.num_coeff_files = CS40L26_TUNING_FILES_RT;
+ } else if (id == CS40L26_FW_CALIB_ID) {
+ cs40l26->fw.min_rev = CS40L26_FW_CALIB_MIN_REV;
+ cs40l26->fw.num_coeff_files = CS40L26_TUNING_FILES_CAL;
+ } else {
+ dev_err(cs40l26->dev, "Invalid firmware ID 0x%06X\n",
+ id);
+ return -EINVAL;
+ }
- for (i = 0; i < cs40l26->fw.num_coeff_files; 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);
- }
- }
+ if (!cs40l26->fw.coeff_files)
+ cs40l26->fw.coeff_files = devm_kcalloc(cs40l26->dev,
+ cs40l26->fw.num_coeff_files, sizeof(char *),
+ GFP_KERNEL);
- strscpy(cs40l26->fw.coeff_files[0], CS40L26_WT_FILE_NAME,
- CS40L26_WT_FILE_NAME_LEN);
-
- if (id == CS40L26_FW_ID) {
- strscpy(cs40l26->fw.coeff_files[1],
- CS40L26_A2H_TUNING_FILE_NAME,
- CS40L26_A2H_TUNING_FILE_NAME_LEN);
- strscpy(cs40l26->fw.coeff_files[2],
- CS40L26_SVC_TUNING_FILE_NAME,
- CS40L26_SVC_TUNING_FILE_NAME_LEN);
- strscpy(cs40l26->fw.coeff_files[3],
- CS40L26_DVL_FILE_NAME,
- CS40L26_DVL_FILE_NAME_LEN);
+ for (i = 0; i < cs40l26->fw.num_coeff_files; 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 {
- strscpy(cs40l26->fw.coeff_files[1],
- CS40L26_CALIB_BIN_FILE_NAME,
- CS40L26_CALIB_BIN_FILE_NAME_LEN);
+ memset(cs40l26->fw.coeff_files[i], 0,
+ CS40L26_TUNING_FILE_NAME_MAX_LEN);
}
+ }
+
+ strscpy(cs40l26->fw.coeff_files[0], CS40L26_WT_FILE_NAME,
+ CS40L26_WT_FILE_NAME_LEN);
- ret = cl_dsp_wavetable_create(cs40l26->dsp,
- CS40L26_VIBEGEN_ALGO_ID, CS40L26_WT_NAME_XM,
- CS40L26_WT_NAME_YM, CS40L26_WT_FILE_NAME);
+ if (id == CS40L26_FW_ID) {
+ strscpy(cs40l26->fw.coeff_files[1],
+ CS40L26_A2H_TUNING_FILE_NAME,
+ CS40L26_A2H_TUNING_FILE_NAME_LEN);
+ strscpy(cs40l26->fw.coeff_files[2],
+ CS40L26_SVC_TUNING_FILE_NAME,
+ CS40L26_SVC_TUNING_FILE_NAME_LEN);
+ strscpy(cs40l26->fw.coeff_files[3],
+ CS40L26_DVL_FILE_NAME,
+ CS40L26_DVL_FILE_NAME_LEN);
+ } else {
+ strscpy(cs40l26->fw.coeff_files[1],
+ CS40L26_CALIB_BIN_FILE_NAME,
+ CS40L26_CALIB_BIN_FILE_NAME_LEN);
}
+ ret = cl_dsp_wavetable_create(cs40l26->dsp,
+ CS40L26_VIBEGEN_ALGO_ID, CS40L26_WT_NAME_XM,
+ CS40L26_WT_NAME_YM, CS40L26_WT_FILE_NAME);
+
return ret;
}
static int cs40l26_wksrc_config(struct cs40l26_private *cs40l26)
{
- u8 mask_wksrc = (cs40l26->devid == CS40L26_DEVID_A) ? 1 : 0;
+ u8 mask_wksrc;
u32 val, mask;
+ if (cs40l26->devid == CS40L26_DEVID_A ||
+ cs40l26->devid == CS40L26_DEVID_L27_A)
+ mask_wksrc = 1;
+ else
+ mask_wksrc = 0;
+
val = BIT(CS40L26_IRQ1_WKSRC_STS_SPI) |
(mask_wksrc << CS40L26_IRQ1_WKSRC_STS_GPIO2) |
(mask_wksrc << CS40L26_IRQ1_WKSRC_STS_GPIO3) |
@@ -3172,10 +3186,16 @@ static int cs40l26_wksrc_config(struct cs40l26_private *cs40l26)
static int cs40l26_gpio_config(struct cs40l26_private *cs40l26)
{
- u32 mask_gpio = (cs40l26->devid == CS40L26_DEVID_A) ? 1 : 0;
u32 val, mask;
+ u8 mask_gpio;
int ret;
+ if (cs40l26->devid == CS40L26_DEVID_A ||
+ cs40l26->devid == CS40L26_DEVID_L27_A)
+ mask_gpio = 1;
+ else
+ mask_gpio = 0;
+
ret = cl_dsp_get_reg(cs40l26->dsp, "ENT_MAP_TABLE_EVENT_DATA_PACKED",
CL_DSP_XM_UNPACKED_TYPE, CS40L26_EVENT_HANDLER_ALGO_ID,
&cs40l26->event_map_base);
@@ -3796,16 +3816,14 @@ static int cs40l26_dsp_config(struct cs40l26_private *cs40l26)
return ret;
}
- if (cs40l26->fw_mode == CS40L26_FW_MODE_RAM) {
- ret = cl_dsp_get_reg(cs40l26->dsp, "CALL_RAM_INIT",
- CL_DSP_XM_UNPACKED_TYPE, cs40l26->fw.id, &reg);
- if (ret)
- return ret;
+ ret = cl_dsp_get_reg(cs40l26->dsp, "CALL_RAM_INIT",
+ CL_DSP_XM_UNPACKED_TYPE, cs40l26->fw.id, &reg);
+ if (ret)
+ return ret;
- ret = cs40l26_dsp_write(cs40l26, reg, 1);
- if (ret)
- return ret;
- }
+ ret = cs40l26_dsp_write(cs40l26, reg, 1);
+ if (ret)
+ return ret;
cs40l26->fw_loaded = true;
@@ -3826,11 +3844,28 @@ static int cs40l26_dsp_config(struct cs40l26_private *cs40l26)
if (ret)
return ret;
- ret = cs40l26_pm_stdby_timeout_ms_set(cs40l26,
- CS40L26_PM_TIMEOUT_MS_MAX);
+ ret = cs40l26_pm_state_transition(cs40l26,
+ CS40L26_PM_STATE_PREVENT_HIBERNATE);
if (ret)
return ret;
+ /* ensure firmware running */
+ ret = cl_dsp_get_reg(cs40l26->dsp, "HALO_STATE",
+ CL_DSP_XM_UNPACKED_TYPE, cs40l26->fw.id, &reg);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(regmap, reg, &val);
+ if (ret) {
+ dev_err(dev, "Failed to read HALO_STATE\n");
+ return ret;
+ }
+
+ if (val != CS40L26_DSP_HALO_STATE_RUN) {
+ dev_err(dev, "Firmware in unexpected state: 0x%X\n", val);
+ return ret;
+ }
+
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) |
@@ -3866,35 +3901,6 @@ static int cs40l26_dsp_config(struct cs40l26_private *cs40l26)
if (ret)
return ret;
- ret = cs40l26_pm_stdby_timeout_ms_set(cs40l26,
- cs40l26->pdata.pm_stdby_timeout_ms);
- if (ret)
- return ret;
-
- /* ensure firmware running */
- ret = cl_dsp_get_reg(cs40l26->dsp, "HALO_STATE",
- CL_DSP_XM_UNPACKED_TYPE, cs40l26->fw.id, &reg);
- if (ret)
- return ret;
-
- /* Send prevent hibernate to ensure we can read HALO STATE */
- ret = cs40l26_pm_state_transition(cs40l26,
- CS40L26_PM_STATE_PREVENT_HIBERNATE);
- if (ret)
- return ret;
-
- /* ensure firmware running */
- ret = regmap_read(regmap, reg, &val);
- if (ret) {
- dev_err(dev, "Failed to read HALO_STATE\n");
- return ret;
- }
-
- if (val != CS40L26_DSP_HALO_STATE_RUN) {
- dev_err(dev, "Firmware in unexpected state: 0x%X\n", val);
- return ret;
- }
-
ret = cs40l26_pm_state_transition(cs40l26,
CS40L26_PM_STATE_ALLOW_HIBERNATE);
if (ret)
@@ -4163,7 +4169,7 @@ int cs40l26_fw_swap(struct cs40l26_private *cs40l26, u32 id)
{
struct device *dev = cs40l26->dev;
bool register_irq = false;
- u32 pseq_rom_end_of_script_offset_words;
+ u32 pseq_rom_end_of_script_loc;
int ret;
if (cs40l26->fw_loaded) {
@@ -4183,8 +4189,8 @@ int cs40l26_fw_swap(struct cs40l26_private *cs40l26, u32 id)
pm_runtime_put_autosuspend(dev);
}
- if (cs40l26->fw_mode == CS40L26_FW_MODE_NONE) {
- cs40l26->fw_mode = CS40L26_FW_MODE_RAM;
+ if (cs40l26->fw_defer) {
+ cs40l26->fw_defer = false;
register_irq = true;
} else {
disable_irq(cs40l26->irq);
@@ -4193,20 +4199,17 @@ int cs40l26_fw_swap(struct cs40l26_private *cs40l26, u32 id)
cs40l26->fw_loaded = false;
- /* reset pseq END_OF_SCRIPT to location from ROM */
- if (cs40l26->revid == CS40L26_REVID_A1) {
- pseq_rom_end_of_script_offset_words =
- CS40L26_PSEQ_ROM_OFFSET_WORDS_A1;
- } else {
+ if (cs40l26->revid != CS40L26_REVID_A1 &&
+ cs40l26->revid != CS40L26_REVID_B0) {
dev_err(dev, "pseq unrecognized revid: %d\n", cs40l26->revid);
return -EINVAL;
}
- ret = regmap_write(cs40l26->regmap,
- cs40l26->pseq_base +
- pseq_rom_end_of_script_offset_words *
- CL_DSP_BYTES_PER_WORD,
- CS40L26_PSEQ_OP_END << CS40L26_PSEQ_OP_SHIFT);
+ /* reset pseq END_OF_SCRIPT to location from ROM */
+ pseq_rom_end_of_script_loc = CS40L26_PSEQ_ROM_END_OF_SCRIPT;
+
+ ret = cs40l26_dsp_write(cs40l26, pseq_rom_end_of_script_loc,
+ CS40L26_PSEQ_OP_END << CS40L26_PSEQ_OP_SHIFT);
if (ret) {
dev_err(dev, "Failed to reset pseq END_OF_SCRIPT %d\n", ret);
return ret;
@@ -4356,13 +4359,8 @@ static int cs40l26_handle_platform_data(struct cs40l26_private *cs40l26)
else
cs40l26->pdata.device_name = CS40L26_INPUT_DEV_NAME;
- if (of_property_read_bool(np, "cirrus,basic-config"))
- cs40l26->fw_mode = CS40L26_FW_MODE_ROM;
- else
- cs40l26->fw_mode = CS40L26_FW_MODE_RAM;
-
if (of_property_read_bool(np, "cirrus,fw-defer"))
- cs40l26->fw_mode = CS40L26_FW_MODE_NONE;
+ cs40l26->fw_defer = true;
if (of_property_read_bool(np, "cirrus,vibe-state"))
cs40l26->pdata.vibe_state_reporting = true;
@@ -4562,7 +4560,9 @@ int cs40l26_probe(struct cs40l26_private *cs40l26,
cs40l26->pm_ready = false;
cs40l26->fw_loaded = false;
- if (cs40l26->fw_mode != CS40L26_FW_MODE_NONE) {
+ init_completion(&cs40l26->i2s_cont);
+
+ if (!cs40l26->fw_defer) {
ret = cs40l26_fw_upload(cs40l26, CS40L26_FW_ID);
if (ret)
goto err;
diff --git a/cs40l26/cs40l26.h b/cs40l26/cs40l26.h
index 5ac0a19..8a99371 100644
--- a/cs40l26/cs40l26.h
+++ b/cs40l26/cs40l26.h
@@ -30,6 +30,7 @@
#include <linux/uaccess.h>
#include <linux/regulator/consumer.h>
#include <linux/delay.h>
+#include <linux/completion.h>
#include <linux/firmware.h>
#include <linux/sysfs.h>
#include <linux/bitops.h>
@@ -636,10 +637,13 @@
#define CS40L26_INPUT_DEV_NAME "cs40l26_input"
#define CS40L26_DEVID_A 0x40A260
#define CS40L26_DEVID_B 0x40A26B
+#define CS40L26_DEVID_L27_A 0x40A270
+#define CS40L26_DEVID_L27_B 0x40A27B
#define CS40L26_DEVID_MASK GENMASK(23, 0)
-#define CS40L26_NUM_DEVS 2
+#define CS40L26_NUM_DEVS 4
#define CS40L26_REVID_A1 0xA1
+#define CS40L26_REVID_B0 0xB0
#define CS40L26_REVID_MASK GENMASK(7, 0)
#define CS40L26_GLOBAL_EN_MASK BIT(0)
@@ -703,8 +707,8 @@
#define CS40L26_EXT_ALGO_ID 0x0004013C
/* power management */
-#define CS40L26_PSEQ_ROM_OFFSET_WORDS_A1 24
-#define CS40L26_PSEQ_MAX_WORDS_PER_OP CS40L26_PSEQ_OP_WRITE_FIELD_WORDS
+#define CS40L26_PSEQ_ROM_END_OF_SCRIPT 0x028003E8
+#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)
@@ -811,6 +815,7 @@
#define CS40L26_DSP_MBOX_COMPLETE_I2S 0x01000002
#define CS40L26_DSP_MBOX_TRIGGER_CP 0x01000010
#define CS40L26_DSP_MBOX_TRIGGER_GPIO 0x01000011
+#define CS40L26_DSP_MBOX_TRIGGER_I2S 0x01000012
#define CS40L26_DSP_MBOX_PM_AWAKE 0x02000002
#define CS40L26_DSP_MBOX_F0_EST_START 0x07000011
#define CS40L26_DSP_MBOX_F0_EST_DONE 0x07000021
@@ -854,7 +859,7 @@
#define CS40L26_FW_ID 0x1800D4
#define CS40L26_FW_ROM_MIN_REV 0x040000
#define CS40L26_FW_A0_RAM_MIN_REV 0x050004
-#define CS40L26_FW_A1_RAM_MIN_REV 0x070218
+#define CS40L26_FW_A1_RAM_MIN_REV 0x07021C
#define CS40L26_FW_CALIB_ID 0x1800DA
#define CS40L26_FW_CALIB_MIN_REV 0x010000
#define CS40L26_FW_BRANCH_MASK GENMASK(23, 21)
@@ -1088,6 +1093,8 @@
#define CS40L26_ASP_FMT_I2S 0x2
#define CS40L26_ASP_FMT_TDM1P5 0x4
+#define CS40L26_ASP_START_TIMEOUT 50 /* milliseconds */
+
#define CS40L26_PLL_REFCLK_BCLK 0x0
#define CS40L26_PLL_REFCLK_FSYNC 0x1
#define CS40L26_PLL_REFCLK_MCLK 0x5
@@ -1292,12 +1299,6 @@ enum cs40l26_vibe_state_event {
CS40L26_VIBE_STATE_EVENT_ASP_STOP,
};
-enum cs40l26_fw_mode {
- CS40L26_FW_MODE_ROM,
- CS40L26_FW_MODE_RAM,
- CS40L26_FW_MODE_NONE,
-};
-
enum cs40l26_err_rls {
CS40L26_RSRVD_ERR_RLS,/* 0 */
CS40L26_AMP_SHORT_ERR_RLS,/* 1 */
@@ -1479,7 +1480,7 @@ struct cs40l26_private {
u32 pseq_base;
struct list_head pseq_op_head;
enum cs40l26_pm_state pm_state;
- enum cs40l26_fw_mode fw_mode;
+ bool fw_defer;
enum cs40l26_vibe_state vibe_state;
int num_loaded_coeff_files;
struct cs40l26_fw fw;
@@ -1504,6 +1505,7 @@ struct cs40l26_private {
bool comp_enable_pend;
bool comp_enable_redc;
bool comp_enable_f0;
+ struct completion i2s_cont;
};
struct cs40l26_codec {