summaryrefslogtreecommitdiff
path: root/cs40l26/cs40l26.c
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 /cs40l26/cs40l26.c
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
Diffstat (limited to 'cs40l26/cs40l26.c')
-rw-r--r--cs40l26/cs40l26.c244
1 files changed, 122 insertions, 122 deletions
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;