From 2370e83abc93b307a0e1550d9565bc750a62d445 Mon Sep 17 00:00:00 2001 From: Tai Kuo Date: Mon, 2 Aug 2021 15:59:17 +0800 Subject: cs40l26: merge CirrusLogic dsp v3.1.2 and cs40l26 v1.1.0 Branch: v5.10-cs40l26 Tag: cs40l26-v1.1.0_5.10 Files: drivers/input/misc/cs40l26-i2c.c (No changes) drivers/input/misc/cs40l26-spi.c (No changes) drivers/input/misc/cs40l26-sysfs.c drivers/input/misc/cs40l26-tables.c drivers/input/misc/cs40l26.c include/linux/mfd/cs40l26.h sound/soc/codecs/cs40l26.c -> cs40l26-codec.c Features: - Increase A2H Volume Granularity - Add more debug-level statements - sysfs control to get amount of space left in wavetable - DT control to set maximum boost peak current - sysfs control to read die temperature - sysfs control to swap firmware variants at runtime - Load firmware synchronously - Inverted streaming and waveform playback - SVC enable for effects streamed via ASP - Map Haptic Effects to GPI events - Actuator Safe Mode workaround - Remove support for A0 silicon Bug fixes: - Remove unused variables - Cancel worker threads before deleting workqueue commits: c07047bd1087 ASoC: cs40l26: Add control to enable invert streaming data 7d0b08956875 input: cs40l26: Remove A0 silicon support d6c20f8342fd input: cs40l26: Update firmware swap function (Skip) 421500c4eb47 Documentation: cs40l26: Add option to load specified SVC tuning a3fb83ecc3b3 input: cs40l26: Add option to load specified SVC tuning 736aa239110c ASoC: cs40l26: Add control to enable SVC for streaming data 7c060144b291 input: cs40l26: Implement actuator safe mode workaround 9b866d0e0362 input: cs40l26: Map Haptic Effects to GPI Events e28e0a5131cc input: cs40l26: Add ability to invert waveform playback 961e4ba3a4bb input: cs40l26: Load firmware synchronously 23463ef1ab4a input: cs40l26: Add ability to swap firmware variants 05b2d67e3c66 input: cs40l26: Remove ENABLE/DISABLE macros b6bd69389b17 input: cs40l26: Remove unneeded use of variable in cs40l26_pcm_ev 27af6399be8c input: cs40l26: Cancel worker threads before destroying workqueue 1794f854a2d0 input: cs40l26: Add control to read die temperature when DSP is active 50bf4051b3a8 input: cs40l26: Remove silicon rev. checks for algorithm IDs (Skip) 3982e2db9db5 Documentation: cs40l26: Allow user to set peak boost current 756186d689c5 input: cs40l26: Allow user to set peak boost current ed0f6bc72faa input: cs40l26: Control to read remaining space in wavetable 72510182f0f2 input: cs40l26: Use percentage value to set A0 gain 91d781c96e12 input: cs40l26: Make status registers readable via regmap a8beb7e4c41f ASoC: cs40l26: Add dev_dbg statments to ALSA callbacks c5b8320ebbce input: cs40l26: Add debug statements 17b3a65e18d1 input: cs40l26: Control to read Power On Sequence 8c4f68e0194a ASoC: cs40l26: Increase A2H Volume granularity Branch: v5.10-cirrus-dsp-fw Tag: cl-dsp-fw-v3.1.2_5.10 Files: drivers/firmware/cirrus/cl_dsp.c include/linux/firmware/cirrus/cl_dsp.h Allocate array memory for .bin file data dynamically instead of using a hard-coded value to avoid possibility of a kernel crash. Allow firmware to load even if there are incompatibilities between the expected firmware version, the loaded firmware, and the tuning file revisions. commits: 79a4a41e4fc6 firmware: cirrus: Allocate data array dynamically 19140c2d644b firmware: cirrus: Loosen restrictions on firmware loading 8e02f2e42990 firmware: cirrus: Don't explicitly free coefficient parent name Bug: 191658078 Bug: 180110149 Bug: 194540033 Bug: 193793095 Test: Check idlcli vibrator commands. Test: Back EMF for internal calibration. Test: Firmware swap. Signed-off-by: Tai Kuo Change-Id: I7db82ce23663772a83e69490b88cfb47e4bf93f3 --- cs40l26/cs40l26-codec.c | 165 ++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 133 insertions(+), 32 deletions(-) (limited to 'cs40l26/cs40l26-codec.c') diff --git a/cs40l26/cs40l26-codec.c b/cs40l26/cs40l26-codec.c index 9c14b9f..c19d2e9 100644 --- a/cs40l26/cs40l26-codec.c +++ b/cs40l26/cs40l26-codec.c @@ -60,7 +60,7 @@ static int cs40l26_swap_ext_clk(struct cs40l26_codec *codec, u8 clk_src) } ret = regmap_update_bits(regmap, CS40L26_REFCLK_INPUT, - CS40L26_PLL_REFCLK_OPEN_LOOP_MASK, CS40L26_ENABLE << + CS40L26_PLL_REFCLK_OPEN_LOOP_MASK, 1 << CS40L26_PLL_REFCLK_OPEN_LOOP_SHIFT); if (ret) { dev_err(dev, "Failed to set Open-Loop PLL\n"); @@ -77,7 +77,7 @@ static int cs40l26_swap_ext_clk(struct cs40l26_codec *codec, u8 clk_src) } ret = regmap_update_bits(regmap, CS40L26_REFCLK_INPUT, - CS40L26_PLL_REFCLK_OPEN_LOOP_MASK, CS40L26_DISABLE << + CS40L26_PLL_REFCLK_OPEN_LOOP_MASK, 0 << CS40L26_PLL_REFCLK_OPEN_LOOP_SHIFT); if (ret) dev_err(dev, "Failed to close PLL loop\n"); @@ -94,6 +94,9 @@ static int cs40l26_clk_en(struct snd_soc_dapm_widget *w, struct device *dev = cs40l26->dev; int ret; + dev_info(dev, "%s: %s\n", __func__, + event == SND_SOC_DAPM_POST_PMU ? "PMU" : "PMD"); + switch (event) { case SND_SOC_DAPM_POST_PMU: mutex_lock(&cs40l26->lock); @@ -135,6 +138,9 @@ static int cs40l26_a2h_ev(struct snd_soc_dapm_widget *w, int ret; u32 reg; + dev_dbg(dev, "%s: %s\n", __func__, + event == SND_SOC_DAPM_POST_PMU ? "PMU" : "PMD"); + ret = cl_dsp_get_reg(cs40l26->dsp, "A2HEN", CL_DSP_XM_UNPACKED_TYPE, CS40L26_A2H_ALGO_ID, ®); if (ret) @@ -163,9 +169,9 @@ static int cs40l26_a2h_ev(struct snd_soc_dapm_widget *w, if (ret) return ret; } - return regmap_write(cs40l26->regmap, reg, CS40L26_ENABLE); + return regmap_write(cs40l26->regmap, reg, 1); case SND_SOC_DAPM_PRE_PMD: - return regmap_write(cs40l26->regmap, reg, CS40L26_DISABLE); + return regmap_write(cs40l26->regmap, reg, 0); default: dev_err(dev, "Invalid A2H event: %d\n", event); return -EINVAL; @@ -181,9 +187,12 @@ static int cs40l26_pcm_ev(struct snd_soc_dapm_widget *w, struct device *dev = cs40l26->dev; u32 asp_en_mask = CS40L26_ASP_TX1_EN_MASK | CS40L26_ASP_TX2_EN_MASK | CS40L26_ASP_RX1_EN_MASK | CS40L26_ASP_RX2_EN_MASK; - u32 asp_enables; + u32 asp_enables, reg; int ret; + dev_info(dev, "%s: %s\n", __func__, + event == SND_SOC_DAPM_POST_PMU ? "PMU" : "PMD"); + switch (event) { case SND_SOC_DAPM_POST_PMU: ret = regmap_update_bits(regmap, CS40L26_DACPCM1_INPUT, @@ -200,10 +209,9 @@ static int cs40l26_pcm_ev(struct snd_soc_dapm_widget *w, return ret; } - asp_enables = CS40L26_ENABLE | (CS40L26_ENABLE << - CS40L26_ASP_TX2_EN_SHIFT) | (CS40L26_ENABLE << - CS40L26_ASP_RX1_EN_SHIFT) | (CS40L26_ENABLE << - CS40L26_ASP_RX2_EN_SHIFT); + asp_enables = 1 | (1 << CS40L26_ASP_TX2_EN_SHIFT) + | (1 << CS40L26_ASP_RX1_EN_SHIFT) + | (1 << CS40L26_ASP_RX2_EN_SHIFT); ret = regmap_update_bits(regmap, CS40L26_ASP_ENABLES1, asp_en_mask, asp_enables); @@ -224,6 +232,28 @@ static int cs40l26_pcm_ev(struct snd_soc_dapm_widget *w, if (ret) return ret; + ret = cl_dsp_get_reg(cs40l26->dsp, "FLAGS", + CL_DSP_XM_UNPACKED_TYPE, CS40L26_EXT_ALGO_ID, ®); + if (ret) + return ret; + + ret = regmap_write(regmap, reg, codec->svc_for_streaming_data); + if (ret) { + dev_err(dev, "Failed to specify SVC for streaming\n"); + return ret; + } + + ret = cl_dsp_get_reg(cs40l26->dsp, "SOURCE_INVERT", + CL_DSP_XM_UNPACKED_TYPE, CS40L26_EXT_ALGO_ID, ®); + if (ret) + return ret; + + ret = regmap_write(regmap, reg, codec->invert_streaming_data); + if (ret) { + dev_err(dev, "Failed to specify SVC for streaming\n"); + return ret; + } + ret = cs40l26_ack_write(cs40l26, CS40L26_DSP_VIRTUAL1_MBOX_1, CS40L26_DSP_MBOX_CMD_START_I2S, CS40L26_DSP_MBOX_RESET); @@ -239,13 +269,8 @@ static int cs40l26_pcm_ev(struct snd_soc_dapm_widget *w, if (ret) return ret; - asp_enables = CS40L26_DISABLE | (CS40L26_DISABLE << - CS40L26_ASP_TX2_EN_SHIFT) | (CS40L26_DISABLE << - CS40L26_ASP_RX1_EN_SHIFT) | (CS40L26_DISABLE << - CS40L26_ASP_RX2_EN_SHIFT); - ret = regmap_update_bits(regmap, CS40L26_ASP_ENABLES1, - asp_en_mask, asp_enables); + asp_en_mask, 0); if (ret) { dev_err(dev, "Failed to clear ASPTX1 input\n"); return ret; @@ -264,6 +289,83 @@ static int cs40l26_pcm_ev(struct snd_soc_dapm_widget *w, return ret; } +static int cs40l26_svc_for_streaming_data_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct cs40l26_codec *codec = + snd_soc_component_get_drvdata(snd_soc_kcontrol_component(kcontrol)); + struct cs40l26_private *cs40l26 = codec->core; + + mutex_lock(&cs40l26->lock); + + if (codec->svc_for_streaming_data) + ucontrol->value.enumerated.item[0] = 1; + else + ucontrol->value.enumerated.item[0] = 0; + + mutex_unlock(&cs40l26->lock); + + return 0; +} + +static int cs40l26_svc_for_streaming_data_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct cs40l26_codec *codec = + snd_soc_component_get_drvdata(snd_soc_kcontrol_component(kcontrol)); + struct cs40l26_private *cs40l26 = codec->core; + + mutex_lock(&cs40l26->lock); + + if (ucontrol->value.enumerated.item[0]) + codec->svc_for_streaming_data = true; + else + codec->svc_for_streaming_data = false; + + mutex_unlock(&cs40l26->lock); + + return 0; +} + +static int cs40l26_invert_streaming_data_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct cs40l26_codec *codec = + snd_soc_component_get_drvdata(snd_soc_kcontrol_component(kcontrol)); + struct cs40l26_private *cs40l26 = codec->core; + + mutex_lock(&cs40l26->lock); + + if (codec->invert_streaming_data) + ucontrol->value.enumerated.item[0] = 1; + else + ucontrol->value.enumerated.item[0] = 0; + + mutex_unlock(&cs40l26->lock); + + return 0; +} + +static int cs40l26_invert_streaming_data_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct cs40l26_codec *codec = + snd_soc_component_get_drvdata(snd_soc_kcontrol_component(kcontrol)); + struct cs40l26_private *cs40l26 = codec->core; + + mutex_lock(&cs40l26->lock); + + if (ucontrol->value.enumerated.item[0]) + codec->invert_streaming_data = true; + else + codec->invert_streaming_data = false; + + mutex_unlock(&cs40l26->lock); + + return 0; +} + + static int cs40l26_tuning_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -302,7 +404,7 @@ static int cs40l26_tuning_put(struct snd_kcontrol *kcontrol, return 0; } -static int cs40l26_volume_get(struct snd_kcontrol *kcontrol, +static int cs40l26_a2h_volume_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct cs40l26_codec *codec = @@ -321,17 +423,9 @@ static int cs40l26_volume_get(struct snd_kcontrol *kcontrol, pm_runtime_get_sync(dev); ret = regmap_read(regmap, reg, &val); - if (ret) { + if (ret) dev_err(dev, "Failed to get VOLUMELEVEL\n"); - goto err_pm; - } - - if (val == CS40L26_VOLUME_MAX) - val = CS40L26_VOLUME_MAX_STEPS; - else - val /= CS40L26_VOLUME_STEP_SIZE; -err_pm: pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); @@ -340,7 +434,7 @@ err_pm: return ret; } -static int cs40l26_volume_put(struct snd_kcontrol *kcontrol, +static int cs40l26_a2h_volume_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct cs40l26_codec *codec = @@ -356,11 +450,12 @@ static int cs40l26_volume_put(struct snd_kcontrol *kcontrol, if (ret) return ret; - val = ucontrol->value.integer.value[0]; - if (val == CS40L26_VOLUME_MAX_STEPS) - val = CS40L26_VOLUME_MAX; + if (ucontrol->value.integer.value[0] > CS40L26_A2H_VOLUME_MAX) + val = CS40L26_A2H_VOLUME_MAX; + else if (ucontrol->value.integer.value[0] < 0) + val = 0; else - val *= CS40L26_VOLUME_STEP_SIZE; + val = ucontrol->value.integer.value[0]; pm_runtime_get_sync(dev); @@ -377,8 +472,14 @@ static int cs40l26_volume_put(struct snd_kcontrol *kcontrol, static const struct snd_kcontrol_new cs40l26_controls[] = { SOC_SINGLE_EXT("A2H Tuning", 0, 0, CS40L26_A2H_MAX_TUNINGS, 0, cs40l26_tuning_get, cs40l26_tuning_put), - SOC_SINGLE_EXT("A2H Volume", 0, 0, CS40L26_VOLUME_MAX_STEPS, 0, - cs40l26_volume_get, cs40l26_volume_put), + SOC_SINGLE_EXT("A2H Volume", 0, 0, CS40L26_A2H_VOLUME_MAX, 0, + cs40l26_a2h_volume_get, cs40l26_a2h_volume_put), + SOC_SINGLE_EXT("SVC for streaming data", 0, 0, 1, 0, + cs40l26_svc_for_streaming_data_get, + cs40l26_svc_for_streaming_data_put), + SOC_SINGLE_EXT("Invert streaming data", 0, 0, 1, 0, + cs40l26_invert_streaming_data_get, + cs40l26_invert_streaming_data_put), }; static const char * const cs40l26_out_mux_texts[] = { "Off", "PCM", "A2H" }; -- cgit v1.2.3