diff options
author | Tai Kuo <taikuo@google.com> | 2022-04-06 18:41:39 +0800 |
---|---|---|
committer | TreeHugger Robot <treehugger-gerrit@google.com> | 2022-04-18 07:33:02 +0000 |
commit | 33811b4ea38472975ac5cb9896dbd8637311e202 (patch) | |
tree | 1c492b8c5e04c0f617033187c36842286453a249 | |
parent | c3f6e596c4fd7dbd73b57be8a6e4ba179396fb35 (diff) | |
download | amplifiers-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.c | 7 | ||||
-rw-r--r-- | cs40l26/cs40l26-i2c.c | 2 | ||||
-rw-r--r-- | cs40l26/cs40l26-spi.c | 2 | ||||
-rw-r--r-- | cs40l26/cs40l26-sysfs.c | 115 | ||||
-rw-r--r-- | cs40l26/cs40l26-tables.c | 2 | ||||
-rw-r--r-- | cs40l26/cs40l26.c | 244 | ||||
-rw-r--r-- | cs40l26/cs40l26.h | 24 |
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, ®); - if (ret) - return ret; + ret = cl_dsp_get_reg(cs40l26->dsp, "CALL_RAM_INIT", + CL_DSP_XM_UNPACKED_TYPE, cs40l26->fw.id, ®); + 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, ®); + 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, ®); - 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 { |