summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTai Kuo <taikuo@google.com>2021-09-09 18:08:33 +0800
committerTreeHugger Robot <treehugger-gerrit@google.com>2021-09-14 03:22:24 +0000
commit4aa86221a59e3dea6d20b5e2081a22f1ee2ec02d (patch)
treedd46e11cfd2dddfcd0aeba32da99c2673e19e3c0
parenta1ef5db43f5188f68d96b56b4872d87aa7fab020 (diff)
downloadamplifiers-4aa86221a59e3dea6d20b5e2081a22f1ee2ec02d.tar.gz
cs40l26: merge CirrusLogic dsp v3.1.3 and cs40l26 v2.0.1
Branch: v5.10-cs40l26 Tag: cs40l26-v2.0.1_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: - I2S VMON Mixer Control - DSP bypass streaming - 96 kHz streaming - Support for multiple OWT waveforms - sysfs control for number of waveforms Bug fixes: - Fix conditional Class H handling for SVC streaming - sysfs control owt_free_space shows space in byte commits: 8aa3d7e996ab input: cs40l26: Remove acknowledge debug message d87c12eb9614 ASoC: cs40l26: Do not enable Class H if SVC is enabled e99f87cb6afb input: cs40l26: Handle Class H Corner Cases 4ea6bac22a23 input: cs40l26: Add sysfs control to get number of waveforms 32c7894579fb input: cs40l26: Display OWT free space in bytes 5ff5dc33ad15 input: cs40l26: Handle Multiple OWT Waveforms 30fde7eeb877 ASoC: Adding support for 96kHz fs and DSP bypass eccaf3df6af1 ASoC: cs40l26: Use workqueue to start I2S stream e9d0a46bfbbb input: cs40l26: Use workqueue to start I2S stream 770ab3b80cab ASoC: cs40l26: Display VMON value 487c966bba02 input: cs40l26: Add SPKMON_VMON_DEC_OUT_DATA as readable register Branch: v5.10-cirrus-dsp-fw Tag: cl-dsp-fw-v3.1.3_5.10 Files: drivers/firmware/cirrus/cl_dsp.c include/linux/firmware/cirrus/cl_dsp.h Increase maximum number of waveforms that can be uploaded via .bin file to 254 from 128. commits: d3be11f7f382 firmware: cirrus: Increase maximum number of wavetable entries Bug: 193782625 Bug: 196485489 Test: Check idlcli vibrator commands. Signed-off-by: Tai Kuo <taikuo@google.com> Change-Id: Ifa4828cb32ee342fee31306d288313e5ea7b857a
-rw-r--r--cs40l26/cl_dsp.c1
-rw-r--r--cs40l26/cl_dsp.h2
-rw-r--r--cs40l26/cs40l26-codec.c202
-rw-r--r--cs40l26/cs40l26-sysfs.c30
-rw-r--r--cs40l26/cs40l26-tables.c1
-rw-r--r--cs40l26/cs40l26.c374
-rw-r--r--cs40l26/cs40l26.h76
7 files changed, 499 insertions, 187 deletions
diff --git a/cs40l26/cl_dsp.c b/cs40l26/cl_dsp.c
index f92babc..12e7a2c 100644
--- a/cs40l26/cl_dsp.c
+++ b/cs40l26/cl_dsp.c
@@ -253,6 +253,7 @@ static int cl_dsp_read_wt(struct cl_dsp *dsp, int pos, int size)
}
}
+ dev_err(dsp->dev, "Maximum number of wavetable entries exceeded\n");
return -E2BIG;
}
diff --git a/cs40l26/cl_dsp.h b/cs40l26/cl_dsp.h
index 41d0d25..9ec931b 100644
--- a/cs40l26/cl_dsp.h
+++ b/cs40l26/cl_dsp.h
@@ -137,7 +137,7 @@
#define CL_DSP_ALGO_ENTRY_SIZE 24
/* open wavetable */
-#define CL_DSP_OWT_HEADER_MAX_LEN 128
+#define CL_DSP_OWT_HEADER_MAX_LEN 254
#define CL_DSP_OWT_HEADER_ENTRY_SIZE 12
/* macros */
diff --git a/cs40l26/cs40l26-codec.c b/cs40l26/cs40l26-codec.c
index 7e4ad89..ce8b316 100644
--- a/cs40l26/cs40l26-codec.c
+++ b/cs40l26/cs40l26-codec.c
@@ -7,12 +7,12 @@
#include "cs40l26.h"
static const struct cs40l26_pll_sysclk_config cs40l26_pll_sysclk[] = {
- {CS40L26_PLL_CLK_FRQ0, CS40L26_PLL_CLK_CFG0},
- {CS40L26_PLL_CLK_FRQ1, CS40L26_PLL_CLK_CFG1},
- {CS40L26_PLL_CLK_FRQ2, CS40L26_PLL_CLK_CFG2},
- {CS40L26_PLL_CLK_FRQ3, CS40L26_PLL_CLK_CFG3},
- {CS40L26_PLL_CLK_FRQ4, CS40L26_PLL_CLK_CFG4},
- {CS40L26_PLL_CLK_FRQ5, CS40L26_PLL_CLK_CFG5},
+ {CS40L26_PLL_CLK_FRQ_32768, CS40L26_PLL_CLK_CFG_32768},
+ {CS40L26_PLL_CLK_FRQ_1536000, CS40L26_PLL_CLK_CFG_1536000},
+ {CS40L26_PLL_CLK_FRQ_3072000, CS40L26_PLL_CLK_CFG_3072000},
+ {CS40L26_PLL_CLK_FRQ_6144000, CS40L26_PLL_CLK_CFG_6144000},
+ {CS40L26_PLL_CLK_FRQ_9600000, CS40L26_PLL_CLK_CFG_9600000},
+ {CS40L26_PLL_CLK_FRQ_12288000, CS40L26_PLL_CLK_CFG_12288000},
};
static int cs40l26_get_clk_config(u32 freq, u8 *clk_cfg)
@@ -45,7 +45,8 @@ static int cs40l26_swap_ext_clk(struct cs40l26_codec *codec, u8 clk_src)
case CS40L26_PLL_REFCLK_MCLK:
clk_sel = CS40L26_PLL_CLK_SEL_MCLK;
- ret = cs40l26_get_clk_config(CS40L26_PLL_CLK_FRQ0, &clk_cfg);
+ ret = cs40l26_get_clk_config(CS40L26_PLL_CLK_FRQ_32768,
+ &clk_cfg);
break;
case CS40L26_PLL_REFCLK_FSYNC:
ret = -EPERM;
@@ -107,7 +108,6 @@ static int cs40l26_clk_en(struct snd_soc_dapm_widget *w,
ret = cs40l26_swap_ext_clk(codec, CS40L26_PLL_REFCLK_BCLK);
if (ret)
return ret;
-
break;
case SND_SOC_DAPM_PRE_PMD:
ret = cs40l26_swap_ext_clk(codec, CS40L26_PLL_REFCLK_MCLK);
@@ -141,6 +141,11 @@ static int cs40l26_a2h_ev(struct snd_soc_dapm_widget *w,
dev_dbg(dev, "%s: %s\n", __func__,
event == SND_SOC_DAPM_POST_PMU ? "PMU" : "PMD");
+ if (codec->bypass_dsp) {
+ dev_err(dev, "Cannot apply A2H if DSP is bypassed\n");
+ return -EPERM;
+ }
+
ret = cl_dsp_get_reg(cs40l26->dsp, "A2HEN", CL_DSP_XM_UNPACKED_TYPE,
CS40L26_A2H_ALGO_ID, &reg);
if (ret)
@@ -188,25 +193,37 @@ static int cs40l26_pcm_ev(struct snd_soc_dapm_widget *w,
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, reg;
+ u8 data_src;
int ret;
dev_info(dev, "%s: %s\n", __func__,
event == SND_SOC_DAPM_POST_PMU ? "PMU" : "PMD");
+ mutex_lock(&cs40l26->lock);
+
+ data_src = codec->bypass_dsp ? CS40L26_DATA_SRC_ASPRX1 :
+ CS40L26_DATA_SRC_DSP1TX1;
+
switch (event) {
case SND_SOC_DAPM_POST_PMU:
ret = regmap_update_bits(regmap, CS40L26_DACPCM1_INPUT,
- CS40L26_DATA_SRC_MASK, CS40L26_DATA_SRC_DSP1TX1);
+ CS40L26_DATA_SRC_MASK, data_src);
if (ret) {
dev_err(dev, "Failed to set DAC PCM input\n");
- return ret;
+ goto err_mutex;
}
ret = regmap_update_bits(regmap, CS40L26_ASPTX1_INPUT,
- CS40L26_DATA_SRC_MASK, CS40L26_DATA_SRC_DSP1TX1);
+ CS40L26_DATA_SRC_MASK, data_src);
if (ret) {
dev_err(dev, "Failed to set ASPTX1 input\n");
- return ret;
+ goto err_mutex;
+ }
+
+ if (!codec->bypass_dsp && !codec->svc_for_streaming_data) {
+ ret = cs40l26_class_h_set(cs40l26, true);
+ if (ret)
+ goto err_mutex;
}
asp_enables = 1 | (1 << CS40L26_ASP_TX2_EN_SHIFT)
@@ -217,30 +234,18 @@ static int cs40l26_pcm_ev(struct snd_soc_dapm_widget *w,
asp_en_mask, asp_enables);
if (ret) {
dev_err(dev, "Failed to enable ASP channels\n");
- return ret;
+ goto err_mutex;
}
- ret = regmap_update_bits(regmap, CS40L26_VBST_CTL_2,
- CS40L26_BST_CTL_SEL_MASK,
- CS40L26_BST_CTL_SEL_CLASSH);
- if (ret) {
- dev_err(dev, "Failed to select Class H BST CTRL\n");
- return ret;
- }
-
- ret = cs40l26_class_h_set(cs40l26, true);
- if (ret)
- return ret;
-
ret = cl_dsp_get_reg(cs40l26->dsp, "FLAGS",
CL_DSP_XM_UNPACKED_TYPE, CS40L26_EXT_ALGO_ID, &reg);
if (ret)
- return ret;
+ goto err_mutex;
ret = regmap_write(regmap, reg, codec->svc_for_streaming_data);
if (ret) {
dev_err(dev, "Failed to specify SVC for streaming\n");
- return ret;
+ goto err_mutex;
}
ret = cl_dsp_get_reg(cs40l26->dsp, "SOURCE_INVERT",
@@ -251,29 +256,28 @@ static int cs40l26_pcm_ev(struct snd_soc_dapm_widget *w,
ret = regmap_write(regmap, reg, codec->invert_streaming_data);
if (ret) {
dev_err(dev, "Failed to specify SVC for streaming\n");
- return ret;
+ goto err_mutex;
}
- ret = cs40l26_ack_write(cs40l26, CS40L26_DSP_VIRTUAL1_MBOX_1,
- CS40L26_DSP_MBOX_CMD_START_I2S,
- CS40L26_DSP_MBOX_RESET);
+ queue_work(cs40l26->asp_workqueue, &cs40l26->asp_work);
+
break;
case SND_SOC_DAPM_PRE_PMD:
ret = cs40l26_ack_write(cs40l26, CS40L26_DSP_VIRTUAL1_MBOX_1,
CS40L26_DSP_MBOX_CMD_STOP_I2S,
CS40L26_DSP_MBOX_RESET);
if (ret)
- return ret;
+ goto err_mutex;
ret = cs40l26_class_h_set(cs40l26, false);
if (ret)
- return ret;
+ goto err_mutex;
ret = regmap_update_bits(regmap, CS40L26_ASP_ENABLES1,
asp_en_mask, 0);
if (ret) {
dev_err(dev, "Failed to clear ASPTX1 input\n");
- return ret;
+ goto err_mutex;
}
ret = regmap_update_bits(regmap, CS40L26_ASPTX1_INPUT,
@@ -286,9 +290,84 @@ static int cs40l26_pcm_ev(struct snd_soc_dapm_widget *w,
ret = -EINVAL;
}
+err_mutex:
+ mutex_unlock(&cs40l26->lock);
+
+ return ret;
+}
+
+static int cs40l26_i2s_vmon_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;
+ int ret;
+ u32 val;
+
+ pm_runtime_get_sync(cs40l26->dev);
+
+ ret = regmap_read(cs40l26->regmap, CS40L26_SPKMON_VMON_DEC_OUT_DATA,
+ &val);
+ if (ret) {
+ dev_err(cs40l26->dev, "Failed to get VMON Data for I2S\n");
+ goto pm_err;
+ }
+
+ if (val & CS40L26_VMON_OVFL_FLAG_MASK) {
+ dev_err(cs40l26->dev, "I2S VMON overflow detected\n");
+ ret = -EOVERFLOW;
+ goto pm_err;
+ }
+
+ ucontrol->value.enumerated.item[0] = val &
+ CS40L26_VMON_DEC_OUT_DATA_MASK;
+
+pm_err:
+ pm_runtime_mark_last_busy(cs40l26->dev);
+ pm_runtime_put_autosuspend(cs40l26->dev);
+
return ret;
}
+static int cs40l26_bypass_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->bypass_dsp)
+ ucontrol->value.enumerated.item[0] = 1;
+ else
+ ucontrol->value.enumerated.item[0] = 0;
+
+ mutex_unlock(&cs40l26->lock);
+
+ return 0;
+}
+
+static int cs40l26_bypass_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->bypass_dsp = true;
+ else
+ codec->bypass_dsp = false;
+
+ mutex_unlock(&cs40l26->lock);
+
+ return 0;
+}
+
static int cs40l26_svc_for_streaming_data_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -307,7 +386,6 @@ static int cs40l26_svc_for_streaming_data_get(struct snd_kcontrol *kcontrol,
return 0;
}
-
static int cs40l26_svc_for_streaming_data_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -365,7 +443,6 @@ static int cs40l26_invert_streaming_data_put(struct snd_kcontrol *kcontrol,
return 0;
}
-
static int cs40l26_tuning_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -496,17 +573,21 @@ static int cs40l26_slots_put(
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),
+ cs40l26_tuning_get, cs40l26_tuning_put),
SOC_SINGLE_EXT("A2H Volume", 0, 0, CS40L26_A2H_VOLUME_MAX, 0,
- cs40l26_a2h_volume_get, cs40l26_a2h_volume_put),
+ 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),
+ 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),
+ cs40l26_invert_streaming_data_get,
+ cs40l26_invert_streaming_data_put),
+ SOC_SINGLE_EXT("I2S VMON", 0, 0, CS40L26_VMON_DEC_OUT_DATA_MAX, 0,
+ cs40l26_i2s_vmon_get, NULL),
+ SOC_SINGLE_EXT("DSP Bypass", 0, 0, 1, 0, cs40l26_bypass_get,
+ cs40l26_bypass_put),
SOC_DOUBLE_EXT("RX Slots", 0, 0, 1, 63, 0, cs40l26_slots_get,
- cs40l26_slots_put),
+ cs40l26_slots_put),
};
static const char * const cs40l26_out_mux_texts[] = { "Off", "PCM", "A2H" };
@@ -623,11 +704,32 @@ static int cs40l26_pcm_hw_params(struct snd_pcm_substream *substream,
{
struct cs40l26_codec *codec =
snd_soc_component_get_drvdata(dai->component);
- u8 asp_rx_wl, asp_rx_width;
- int ret;
+ u8 asp_rx_wl, asp_rx_width, global_fs;
+ int ret, lrck;
pm_runtime_get_sync(codec->dev);
+ lrck = params_rate(params);
+ switch (lrck) {
+ case 48000:
+ global_fs = CS40L26_GLOBAL_FS_48K;
+ break;
+ case 96000:
+ global_fs = CS40L26_GLOBAL_FS_96K;
+ break;
+ default:
+ ret = -EINVAL;
+ dev_err(codec->dev, "Invalid sample rate: %d Hz\n", lrck);
+ goto err_pm;
+ }
+
+ ret = regmap_update_bits(codec->regmap, CS40L26_GLOBAL_SAMPLE_RATE,
+ CS40L26_GLOBAL_FS_MASK, global_fs);
+ if (ret) {
+ dev_err(codec->dev, "Failed to write global fs\n");
+ goto err_pm;
+ }
+
asp_rx_wl = (u8) (params_width(params) & 0xFF);
ret = regmap_update_bits(codec->regmap, CS40L26_ASP_DATA_CONTROL5,
CS40L26_ASP_RX_WL_MASK, asp_rx_wl);
@@ -686,7 +788,7 @@ static struct snd_soc_dai_driver cs40l26_dai[] = {
.stream_name = "ASP Playback",
.channels_min = 1,
.channels_max = 2,
- .rates = SNDRV_PCM_RATE_48000,
+ .rates = CS40L26_RATES,
.formats = CS40L26_FORMATS,
},
.ops = &cs40l26_dai_ops,
@@ -706,7 +808,7 @@ static int cs40l26_codec_probe(struct snd_soc_component *component)
snprintf(codec->bin_file, PAGE_SIZE, CS40L26_A2H_TUNING_FILE_NAME);
/* Default audio SCLK frequency */
- codec->sysclk_rate = CS40L26_PLL_CLK_FRQ1;
+ codec->sysclk_rate = CS40L26_PLL_CLK_FRQ_1536000;
codec->tdm_slot[0] = 2;
codec->tdm_slot[1] = 3;
@@ -755,9 +857,11 @@ static int cs40l26_codec_driver_probe(struct platform_device *pdev)
static int cs40l26_codec_driver_remove(struct platform_device *pdev)
{
- pm_runtime_disable(&pdev->dev);
+ struct cs40l26_codec *codec = dev_get_drvdata(&pdev->dev);
+
+ pm_runtime_disable(codec->dev);
- snd_soc_unregister_component(&pdev->dev);
+ snd_soc_unregister_component(codec->dev);
return 0;
}
diff --git a/cs40l26/cs40l26-sysfs.c b/cs40l26/cs40l26-sysfs.c
index 8e5c9dc..b4ab9c5 100644
--- a/cs40l26/cs40l26-sysfs.c
+++ b/cs40l26/cs40l26-sysfs.c
@@ -216,7 +216,7 @@ static ssize_t cs40l26_owt_free_space_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct cs40l26_private *cs40l26 = dev_get_drvdata(dev);
- u32 reg, nbytes;
+ u32 reg, words;
int ret;
pm_runtime_get_sync(cs40l26->dev);
@@ -226,13 +226,13 @@ static ssize_t cs40l26_owt_free_space_show(struct device *dev,
if (ret)
goto err_pm;
- ret = regmap_read(cs40l26->regmap, reg, &nbytes);
+ ret = regmap_read(cs40l26->regmap, reg, &words);
if (ret) {
dev_err(cs40l26->dev, "Failed to get remaining OWT space\n");
goto err_pm;
}
- ret = snprintf(buf, PAGE_SIZE, "%d\n", nbytes);
+ ret = snprintf(buf, PAGE_SIZE, "%d\n", words * 3);
err_pm:
pm_runtime_mark_last_busy(cs40l26->dev);
@@ -285,7 +285,31 @@ err_pm:
}
static DEVICE_ATTR(die_temp, 0440, cs40l26_die_temp_show, NULL);
+static ssize_t cs40l26_num_waves_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct cs40l26_private *cs40l26 = dev_get_drvdata(dev);
+ u32 nwaves;
+ int ret;
+
+ pm_runtime_get_sync(cs40l26->dev);
+
+ ret = cs40l26_get_num_waves(cs40l26, &nwaves);
+ if (ret)
+ goto err_pm;
+
+ ret = snprintf(buf, PAGE_SIZE, "%u\n", nwaves);
+
+err_pm:
+ pm_runtime_mark_last_busy(cs40l26->dev);
+ pm_runtime_put_autosuspend(cs40l26->dev);
+
+ return ret;
+}
+static DEVICE_ATTR(num_waves, 0440, cs40l26_num_waves_show, NULL);
+
static struct attribute *cs40l26_dev_attrs[] = {
+ &dev_attr_num_waves.attr,
&dev_attr_die_temp.attr,
&dev_attr_owt_free_space.attr,
&dev_attr_power_on_seq.attr,
diff --git a/cs40l26/cs40l26-tables.c b/cs40l26/cs40l26-tables.c
index 44b4dda..bf115f4 100644
--- a/cs40l26/cs40l26-tables.c
+++ b/cs40l26/cs40l26-tables.c
@@ -337,6 +337,7 @@ bool cs40l26_readable_reg(struct device *dev, unsigned int reg)
case CS40L26_SPKMON_RATE_SEL:
case CS40L26_MONITOR_FILT:
case CS40L26_IMON_COMP:
+ case CS40L26_SPKMON_VMON_DEC_OUT_DATA:
case CS40L26_WARN_LIMIT_THRESHOLD:
case CS40L26_CONFIGURATION:
case CS40L26_STATUS:
diff --git a/cs40l26/cs40l26.c b/cs40l26/cs40l26.c
index 9b75a6f..3ca45cc 100644
--- a/cs40l26/cs40l26.c
+++ b/cs40l26/cs40l26.c
@@ -13,6 +13,12 @@
#include "cs40l26.h"
+static inline bool is_owt(unsigned int index)
+{
+ return index >= CS40L26_OWT_INDEX_START &&
+ index <= CS40L26_OWT_INDEX_END;
+}
+
static int cs40l26_dsp_read(struct cs40l26_private *cs40l26, u32 reg, u32 *val)
{
struct regmap *regmap = cs40l26->regmap;
@@ -81,9 +87,7 @@ static int cs40l26_ack_read(struct cs40l26_private *cs40l26, u32 reg,
if (ret)
return ret;
- if (val != ack_val)
- dev_dbg(dev, "Ack'ed value not equal to expected\n");
- else
+ if (val == ack_val)
break;
usleep_range(CS40L26_DSP_TIMEOUT_US_MIN,
@@ -116,9 +120,15 @@ int cs40l26_class_h_set(struct cs40l26_private *cs40l26, bool class_h)
{
int ret;
+ ret = regmap_update_bits(cs40l26->regmap, CS40L26_VBST_CTL_2,
+ CS40L26_BST_CTL_SEL_MASK, class_h);
+ if (ret) {
+ dev_err(cs40l26->dev, "Failed to select Class H BST CTRL\n");
+ return ret;
+ }
+
ret = regmap_update_bits(cs40l26->regmap, CS40L26_BLOCK_ENABLES2,
- CS40L26_CLASS_H_EN_MASK, class_h <<
- CS40L26_CLASS_H_EN_SHIFT);
+ CS40L26_CLASS_H_EN_MASK, class_h << CS40L26_CLASS_H_EN_SHIFT);
if (ret)
dev_err(cs40l26->dev, "Failed to update CLASS H tracking\n");
@@ -574,6 +584,16 @@ static int cs40l26_handle_mbox_buffer(struct cs40l26_private *cs40l26)
return 0;
}
+void cs40l26_asp_worker(struct work_struct *work)
+{
+ struct cs40l26_private *cs40l26 =
+ container_of(work, struct cs40l26_private, asp_work);
+
+ cs40l26_ack_write(cs40l26, CS40L26_DSP_VIRTUAL1_MBOX_1,
+ CS40L26_DSP_MBOX_CMD_START_I2S, CS40L26_DSP_MBOX_RESET);
+}
+EXPORT_SYMBOL(cs40l26_asp_worker);
+
void cs40l26_vibe_state_set(struct cs40l26_private *cs40l26,
enum cs40l26_vibe_state new_state)
{
@@ -583,15 +603,11 @@ void cs40l26_vibe_state_set(struct cs40l26_private *cs40l26,
if (new_state == CS40L26_VIBE_STATE_STOPPED && cs40l26->asp_enable) {
/* Re-enable audio stream */
cs40l26_class_h_set(cs40l26, true);
- if (!cs40l26_ack_write(cs40l26, CS40L26_DSP_VIRTUAL1_MBOX_1,
- CS40L26_DSP_MBOX_CMD_START_I2S,
- CS40L26_DSP_MBOX_RESET)) {
- cs40l26->vibe_state = CS40L26_VIBE_STATE_ASP;
- return;
- } else if (new_state == CS40L26_VIBE_STATE_HAPTIC &&
+ queue_work(cs40l26->asp_workqueue, &cs40l26->asp_work);
+
+ } else if (new_state == CS40L26_VIBE_STATE_HAPTIC &&
cs40l26->vibe_state == CS40L26_VIBE_STATE_ASP) {
- cs40l26_class_h_set(cs40l26, false);
- }
+ cs40l26_class_h_set(cs40l26, false);
}
cs40l26->vibe_state = new_state;
@@ -1535,6 +1551,29 @@ pm_err:
return ret;
}
+static struct cs40l26_owt *cs40l26_owt_find(struct cs40l26_private *cs40l26,
+ int id)
+{
+ struct cs40l26_owt *owt;
+
+ if (list_empty(&cs40l26->owt_head)) {
+ dev_err(cs40l26->dev, "OWT list is empty\n");
+ return NULL;
+ }
+
+ list_for_each_entry(owt, &cs40l26->owt_head, list) {
+ if (owt->effect_id == id)
+ break;
+ }
+
+ if (owt->effect_id != id) {
+ dev_err(cs40l26->dev, "OWT effect with ID %d not found\n", id);
+ return NULL;
+ }
+
+ return owt;
+}
+
static void cs40l26_set_gain_worker(struct work_struct *work)
{
struct cs40l26_private *cs40l26 =
@@ -1569,6 +1608,7 @@ static void cs40l26_vibe_start_worker(struct work_struct *work)
struct device *dev = cs40l26->dev;
u32 index = 0;
int ret = 0;
+ struct cs40l26_owt *owt;
unsigned int reg, freq;
bool invert;
u16 duration;
@@ -1578,11 +1618,16 @@ static void cs40l26_vibe_start_worker(struct work_struct *work)
if (cs40l26->effect->u.periodic.waveform == FF_CUSTOM)
index = cs40l26->trigger_indices[cs40l26->effect->id];
- if (index >= CS40L26_OWT_INDEX_START && index <= CS40L26_OWT_INDEX_END)
- duration = CS40L26_SAMPS_TO_MS((cs40l26->owt_wlength &
+ if (is_owt(index)) {
+ owt = cs40l26_owt_find(cs40l26, cs40l26->effect->id);
+ if (owt == NULL)
+ return;
+
+ duration = CS40L26_SAMPS_TO_MS((owt->wlength &
CS40L26_WT_TYPE10_WAVELEN_MAX));
- else
+ } else {
duration = cs40l26->effect->replay.length;
+ }
pm_runtime_get_sync(dev);
mutex_lock(&cs40l26->lock);
@@ -1751,6 +1796,36 @@ static int cs40l26_playback_effect(struct input_dev *dev,
return 0;
}
+int cs40l26_get_num_waves(struct cs40l26_private *cs40l26, u32 *num_waves)
+{
+ int ret;
+ u32 reg, nwaves, nowt;
+
+ ret = cl_dsp_get_reg(cs40l26->dsp, "NUM_OF_WAVES",
+ CL_DSP_XM_UNPACKED_TYPE,
+ CS40L26_VIBEGEN_ALGO_ID, &reg);
+ if (ret)
+ return ret;
+
+ ret = cs40l26_dsp_read(cs40l26, reg, &nwaves);
+ if (ret)
+ return ret;
+
+ ret = cl_dsp_get_reg(cs40l26->dsp, "OWT_NUM_OF_WAVES_XM",
+ CL_DSP_XM_UNPACKED_TYPE, CS40L26_VIBEGEN_ALGO_ID, &reg);
+ if (ret)
+ return ret;
+
+ ret = cs40l26_dsp_read(cs40l26, reg, &nowt);
+ if (ret)
+ return ret;
+
+ *num_waves = nwaves + nowt;
+
+ return 0;
+}
+EXPORT_SYMBOL(cs40l26_get_num_waves);
+
static int cs40l26_owt_get_wlength(struct cs40l26_private *cs40l26, u8 index)
{
struct device *dev = cs40l26->dev;
@@ -1799,8 +1874,10 @@ static void cs40l26_owt_get_section_info(struct cs40l26_private *cs40l26,
}
static int cs40l26_owt_calculate_wlength(struct cs40l26_private *cs40l26,
- struct cl_dsp_memchunk *ch)
+ s16 *data, u32 data_size, u32 *owt_wlen)
{
+ u32 data_size_bytes = data_size * 2;
+ struct cl_dsp_memchunk ch;
u32 total_len = 0, section_len = 0, loop_len = 0;
bool in_loop = false;
struct cs40l26_owt_section *sections;
@@ -1808,9 +1885,11 @@ static int cs40l26_owt_calculate_wlength(struct cs40l26_private *cs40l26,
u8 nsections, global_rep;
u32 dlen, wlen;
- cl_dsp_memchunk_read(ch, 8); /* Skip padding */
- nsections = cl_dsp_memchunk_read(ch, 8);
- global_rep = cl_dsp_memchunk_read(ch, 8);
+ ch = cl_dsp_memchunk_create((void *) data, data_size_bytes);
+
+ cl_dsp_memchunk_read(&ch, 8); /* Skip padding */
+ nsections = cl_dsp_memchunk_read(&ch, 8);
+ global_rep = cl_dsp_memchunk_read(&ch, 8);
if (nsections < 1) {
dev_err(cs40l26->dev, "Not enough sections for composite\n");
@@ -1824,7 +1903,7 @@ static int cs40l26_owt_calculate_wlength(struct cs40l26_private *cs40l26,
return -ENOMEM;
}
- cs40l26_owt_get_section_info(cs40l26, ch, sections, nsections);
+ cs40l26_owt_get_section_info(cs40l26, &ch, sections, nsections);
for (i = 0; i < nsections; i++) {
wlen_whole = cs40l26_owt_get_wlength(cs40l26,
@@ -1877,7 +1956,7 @@ static int cs40l26_owt_calculate_wlength(struct cs40l26_private *cs40l26,
section_len = 0;
}
- cs40l26->owt_wlength = (total_len * (global_rep + 1)) |
+ *owt_wlen = (total_len * (global_rep + 1)) |
CS40L26_WT_TYPE10_WAVELEN_CALCULATED;
err_free:
@@ -1886,65 +1965,70 @@ err_free:
return ret;
}
+static int cs40l26_owt_add(struct cs40l26_private *cs40l26, u32 wlen,
+ int effect_id, u32 index)
+{
+ struct cs40l26_owt *owt_new;
+
+ owt_new = kzalloc(sizeof(*owt_new), GFP_KERNEL);
+ if (!owt_new)
+ return -ENOMEM;
+
+ owt_new->effect_id = effect_id;
+ owt_new->wlength = wlen;
+ owt_new->trigger_index = index;
+ list_add(&owt_new->list, &cs40l26->owt_head);
+
+ cs40l26->num_owt_effects++;
+
+ return 0;
+}
+
static int cs40l26_owt_upload(struct cs40l26_private *cs40l26, s16 *data,
- u32 data_size)
+ u32 data_size, u32 *wlength)
{
bool pwle = (data[0] == 0x0000) ? false : true;
- u32 data_size_bytes = data_size * 2;
+ u32 data_size_bytes = (data_size * 2) + (pwle ? 0 : 4);
struct device *dev = cs40l26->dev;
struct cl_dsp *dsp = cs40l26->dsp;
- u32 full_data_size, header_size = CL_DSP_OWT_HEADER_ENTRY_SIZE;
- unsigned int write_reg, reg, wt_offset, wt_size, wt_base;
- struct cl_dsp_memchunk header_ch, data_ch;
- u8 *full_data, *header;
+ unsigned int write_reg, reg, wt_offset, wt_size_words, wt_base;
+ u32 owt_wlength, full_data_size_bytes = 12 + data_size_bytes;
+ struct cl_dsp_memchunk data_ch;
+ u8 *full_data;
int ret = 0;
- data_ch = cl_dsp_memchunk_create((void *) data, data_size_bytes);
-
- if (pwle) {
- header_size += CS40L26_WT_TERM_SIZE;
-
- cs40l26->owt_wlength = cl_dsp_memchunk_read(&data_ch, 24);
- } else {
- header_size += CS40L26_WT_WLEN_TERM_SIZE;
-
- ret = cs40l26_owt_calculate_wlength(cs40l26, &data_ch);
- if (ret)
- return ret;
- }
- full_data_size = header_size + data_size_bytes;
-
- header = kcalloc(header_size, sizeof(u8), GFP_KERNEL);
- if (!header)
+ full_data = kcalloc(full_data_size_bytes, sizeof(u8), GFP_KERNEL);
+ if (!full_data)
return -ENOMEM;
- header_ch = cl_dsp_memchunk_create((void *) header, header_size);
- /* Header */
- cl_dsp_memchunk_write(&header_ch, 16,
- CS40L26_WT_HEADER_DEFAULT_FLAGS);
+ data_ch = cl_dsp_memchunk_create((void *) full_data,
+ full_data_size_bytes);
+ cl_dsp_memchunk_write(&data_ch, 16, CS40L26_WT_HEADER_DEFAULT_FLAGS);
if (pwle)
- cl_dsp_memchunk_write(&header_ch, 8, WT_TYPE_V6_PWLE);
+ cl_dsp_memchunk_write(&data_ch, 8, WT_TYPE_V6_PWLE);
else
- cl_dsp_memchunk_write(&header_ch, 8, WT_TYPE_V6_COMPOSITE);
-
- cl_dsp_memchunk_write(&header_ch, 24, CS40L26_WT_HEADER_OFFSET);
- cl_dsp_memchunk_write(&header_ch, 24, full_data_size /
- CL_DSP_BYTES_PER_WORD);
+ cl_dsp_memchunk_write(&data_ch, 8, WT_TYPE_V6_COMPOSITE);
- cl_dsp_memchunk_write(&header_ch, 24, CS40L26_WT_HEADER_TERM);
+ cl_dsp_memchunk_write(&data_ch, 24, CS40L26_WT_HEADER_OFFSET);
+ cl_dsp_memchunk_write(&data_ch, 24, data_size_bytes /
+ CL_DSP_BYTES_PER_WORD);
- if (!pwle) /* Wlength is included in PWLE raw data */
- cl_dsp_memchunk_write(&header_ch, 24, cs40l26->owt_wlength);
+ if (!pwle) { /* Wlength is included in PWLE raw data */
+ ret = cs40l26_owt_calculate_wlength(cs40l26, data, data_size,
+ &owt_wlength);
+ if (ret)
+ goto err_free;
- full_data = kcalloc(full_data_size, sizeof(u8), GFP_KERNEL);
- if (!full_data) {
- ret = -ENOMEM;
- goto err_free;
+ cl_dsp_memchunk_write(&data_ch, 24, owt_wlength);
}
- memcpy(full_data, header, header_ch.bytes);
- memcpy(full_data + header_ch.bytes, data, data_size_bytes);
+ memcpy(full_data + data_ch.bytes, data, data_size_bytes);
+
+ if (pwle)
+ owt_wlength = (full_data[data_ch.bytes + 1] << 16) |
+ (full_data[data_ch.bytes + 2] << 8) |
+ full_data[data_ch.bytes + 3];
pm_runtime_get_sync(dev);
@@ -1964,13 +2048,13 @@ static int cs40l26_owt_upload(struct cs40l26_private *cs40l26, s16 *data,
if (ret)
goto err_pm;
- ret = regmap_read(cs40l26->regmap, reg, &wt_size);
+ ret = regmap_read(cs40l26->regmap, reg, &wt_size_words);
if (ret) {
dev_err(dev, "Failed to get available WT size\n");
goto err_pm;
}
- if (wt_size < full_data_size) {
+ if ((wt_size_words * 3) < full_data_size_bytes) {
dev_err(dev, "No space for OWT waveform\n");
ret = -ENOSPC;
goto err_pm;
@@ -1984,7 +2068,7 @@ static int cs40l26_owt_upload(struct cs40l26_private *cs40l26, s16 *data,
write_reg = wt_base + (wt_offset * 4);
ret = cl_dsp_raw_write(cs40l26->dsp, write_reg, full_data,
- full_data_size, CL_DSP_MAX_WLEN);
+ full_data_size_bytes, CL_DSP_MAX_WLEN);
if (ret) {
dev_err(dev, "Failed to sync OWT\n");
goto err_pm;
@@ -1996,14 +2080,15 @@ static int cs40l26_owt_upload(struct cs40l26_private *cs40l26, s16 *data,
goto err_pm;
dev_dbg(dev, "Successfully wrote waveform (%u bytes) to 0x%08X\n",
- full_data_size, write_reg);
+ full_data_size_bytes, write_reg);
+
+ *wlength = owt_wlength;
err_pm:
pm_runtime_mark_last_busy(dev);
pm_runtime_put_autosuspend(dev);
err_free:
- kfree(header);
kfree(full_data);
return ret;
@@ -2016,12 +2101,11 @@ static int cs40l26_upload_effect(struct input_dev *dev,
struct device *cdev = cs40l26->dev;
s16 *raw_custom_data = NULL;
int ret = 0;
- u32 trigger_index, min_index, max_index;
+ u32 trigger_index, min_index, max_index, nwaves, owt_wlen;
u16 index, bank;
if (effect->type != FF_PERIODIC) {
- dev_err(cdev, "Effect type 0x%X not supported\n",
- effect->type);
+ dev_err(cdev, "Effect type 0x%X not supported\n", effect->type);
return -EINVAL;
}
@@ -2049,15 +2133,8 @@ static int cs40l26_upload_effect(struct input_dev *dev,
}
if (effect->u.periodic.custom_len > CS40L26_CUSTOM_DATA_SIZE) {
- ret = cs40l26_ack_write(cs40l26,
- CS40L26_DSP_VIRTUAL1_MBOX_1,
- CS40L26_DSP_MBOX_CMD_OWT_RESET,
- CS40L26_DSP_MBOX_RESET);
- if (ret)
- goto out_free;
-
ret = cs40l26_owt_upload(cs40l26, raw_custom_data,
- effect->u.periodic.custom_len);
+ effect->u.periodic.custom_len, &owt_wlen);
if (ret)
goto out_free;
@@ -2099,6 +2176,13 @@ static int cs40l26_upload_effect(struct input_dev *dev,
goto out_free;
}
+ if (is_owt(trigger_index)) {
+ ret = cs40l26_owt_add(cs40l26, owt_wlen, effect->id,
+ trigger_index);
+ if (ret)
+ goto out_free;
+ }
+
dev_dbg(cdev, "%s: ID = %u, index = 0x%08X\n",
__func__, effect->id, trigger_index);
break;
@@ -2124,8 +2208,17 @@ static int cs40l26_upload_effect(struct input_dev *dev,
return -EINVAL;
}
- if (effect->trigger.button)
+ if (effect->trigger.button) {
ret = cs40l26_gpio_index_set(cs40l26, effect);
+ if (ret)
+ goto out_free;
+ }
+
+ ret = cs40l26_get_num_waves(cs40l26, &nwaves);
+ if (ret)
+ goto out_free;
+
+ dev_dbg(cdev, "Total number of waveforms = %u\n", nwaves);
out_free:
@@ -2142,6 +2235,50 @@ const struct attribute_group *cs40l26_dev_attr_groups[] = {
};
#endif
+static int cs40l26_erase_effect(struct input_dev *dev, int effect_id)
+{
+ struct cs40l26_private *cs40l26 = input_get_drvdata(dev);
+ u32 index = cs40l26->trigger_indices[effect_id];
+ u32 cmd = CS40L26_DSP_MBOX_CMD_OWT_DELETE_BASE;
+ struct cs40l26_owt *owt, *owt_tmp;
+ int ret;
+
+ if (!is_owt(index))
+ return 0;
+
+ /* Update indices for OWT waveforms uploaded after erased effect */
+ list_for_each_entry(owt_tmp, &cs40l26->owt_head, list) {
+ if (owt_tmp->trigger_index > index) {
+ owt_tmp->trigger_index--;
+ cs40l26->trigger_indices[owt_tmp->effect_id]--;
+ }
+ }
+
+ owt = cs40l26_owt_find(cs40l26, effect_id);
+ if (owt == NULL)
+ return -ENOMEM;
+
+ cmd |= (owt->trigger_index & 0xFF);
+
+ list_del(&owt->list);
+ kfree(owt);
+
+ pm_runtime_get_sync(cs40l26->dev);
+
+ ret = cs40l26_ack_write(cs40l26, CS40L26_DSP_VIRTUAL1_MBOX_1, cmd,
+ CS40L26_DSP_MBOX_RESET);
+ if (ret)
+ goto pm_err;
+
+ cs40l26->num_owt_effects--;
+
+pm_err:
+ pm_runtime_mark_last_busy(cs40l26->dev);
+ pm_runtime_put_autosuspend(cs40l26->dev);
+
+ return ret;
+}
+
static int cs40l26_input_init(struct cs40l26_private *cs40l26)
{
int ret;
@@ -2176,6 +2313,7 @@ static int cs40l26_input_init(struct cs40l26_private *cs40l26)
cs40l26->input->ff->upload = cs40l26_upload_effect;
cs40l26->input->ff->playback = cs40l26_playback_effect;
cs40l26->input->ff->set_gain = cs40l26_set_gain;
+ cs40l26->input->ff->erase = cs40l26_erase_effect;
ret = input_register_device(cs40l26->input);
if (ret) {
@@ -2296,8 +2434,6 @@ static int cs40l26_cl_dsp_init(struct cs40l26_private *cs40l26, u32 id)
CS40L26_WT_NAME_YM, CS40L26_WT_FILE_NAME);
}
- cs40l26->num_owt_effects = 0;
-
return ret;
}
@@ -2602,21 +2738,6 @@ static int cs40l26_brownout_prevention_init(struct cs40l26_private *cs40l26)
return 0;
}
-static int cs40l26_get_num_waves(struct cs40l26_private *cs40l26,
- u32 *num_waves)
-{
- int ret;
- u32 reg;
-
- ret = cl_dsp_get_reg(cs40l26->dsp, "NUM_OF_WAVES",
- CL_DSP_XM_UNPACKED_TYPE,
- CS40L26_VIBEGEN_ALGO_ID, &reg);
- if (ret)
- return ret;
-
- return regmap_read(cs40l26->regmap, reg, num_waves);
-}
-
static int cs40l26_verify_fw(struct cs40l26_private *cs40l26)
{
struct cs40l26_fw *fw = &cs40l26->fw;
@@ -2736,6 +2857,37 @@ static int cs40l26_bst_ipk_config(struct cs40l26_private *cs40l26)
val, true);
}
+static int cs40l26_owt_setup(struct cs40l26_private *cs40l26)
+{
+ u32 reg, offset, base;
+ int ret;
+
+ INIT_LIST_HEAD(&cs40l26->owt_head);
+ cs40l26->num_owt_effects = 0;
+
+ ret = cl_dsp_get_reg(cs40l26->dsp, CS40L26_WT_NAME_XM,
+ CL_DSP_XM_UNPACKED_TYPE, CS40L26_VIBEGEN_ALGO_ID, &base);
+ if (ret)
+ return ret;
+
+ ret = cl_dsp_get_reg(cs40l26->dsp, "OWT_NEXT_XM",
+ CL_DSP_XM_UNPACKED_TYPE, CS40L26_VIBEGEN_ALGO_ID, &reg);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(cs40l26->regmap, reg, &offset);
+ if (ret) {
+ dev_err(cs40l26->dev, "Failed to get wavetable offset\n");
+ return ret;
+ }
+
+ ret = regmap_write(cs40l26->regmap, reg, 0xFFFFFF);
+ if (ret)
+ dev_err(cs40l26->dev, "Failed to write OWT terminator\n");
+
+ return ret;
+}
+
static int cs40l26_dsp_config(struct cs40l26_private *cs40l26)
{
struct regmap *regmap = cs40l26->regmap;
@@ -2848,9 +3000,15 @@ static int cs40l26_dsp_config(struct cs40l26_private *cs40l26)
goto pm_err;
ret = cs40l26_get_num_waves(cs40l26, &cs40l26->num_waves);
- if (!ret)
- dev_info(dev, "%s loaded with %u RAM waveforms\n",
- CS40L26_DEV_NAME, cs40l26->num_waves);
+ if (ret)
+ goto pm_err;
+
+ dev_info(dev, "%s loaded with %u RAM waveforms\n", CS40L26_DEV_NAME,
+ cs40l26->num_waves);
+
+ ret = cs40l26_owt_setup(cs40l26);
+ if (ret)
+ goto pm_err;
pm_err:
pm_runtime_mark_last_busy(dev);
@@ -3135,6 +3293,15 @@ int cs40l26_probe(struct cs40l26_private *cs40l26,
INIT_WORK(&cs40l26->vibe_stop_work, cs40l26_vibe_stop_worker);
INIT_WORK(&cs40l26->set_gain_work, cs40l26_set_gain_worker);
+ cs40l26->asp_workqueue = alloc_ordered_workqueue("asp_workqueue",
+ WQ_HIGHPRI);
+ if (!cs40l26->asp_workqueue) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ INIT_WORK(&cs40l26->asp_work, cs40l26_asp_worker);
+
ret = devm_regulator_bulk_get(dev, CS40L26_NUM_SUPPLIES,
cs40l26_supplies);
if (ret) {
@@ -3290,6 +3457,11 @@ int cs40l26_remove(struct cs40l26_private *cs40l26)
destroy_workqueue(cs40l26->vibe_workqueue);
}
+ if (cs40l26->asp_workqueue) {
+ cancel_work_sync(&cs40l26->asp_work);
+ destroy_workqueue(cs40l26->asp_workqueue);
+ }
+
if (vp_consumer)
regulator_disable(vp_consumer);
diff --git a/cs40l26/cs40l26.h b/cs40l26/cs40l26.h
index 0e5c427..856b531 100644
--- a/cs40l26/cs40l26.h
+++ b/cs40l26/cs40l26.h
@@ -192,6 +192,7 @@
#define CS40L26_SPKMON_RATE_SEL 0x4004
#define CS40L26_MONITOR_FILT 0x4008
#define CS40L26_IMON_COMP 0x4010
+#define CS40L26_SPKMON_VMON_DEC_OUT_DATA 0x41B4
#define CS40L26_WARN_LIMIT_THRESHOLD 0x4220
#define CS40L26_CONFIGURATION 0x4224
#define CS40L26_STATUS 0x4300
@@ -645,6 +646,10 @@
#define CS40L26_DSP_CCM_CORE_KILL 0x00000080
#define CS40L26_DSP_CCM_CORE_RESET 0x00000281
+#define CS40L26_GLOBAL_FS_MASK GENMASK(4, 0)
+#define CS40L26_GLOBAL_FS_48K 0x03
+#define CS40L26_GLOBAL_FS_96K 0x04
+
#define CS40L26_MEM_RDY_MASK BIT(1)
#define CS40L26_MEM_RDY_SHIFT 1
@@ -733,7 +738,7 @@
/* DSP mailbox controls */
#define CS40L26_DSP_TIMEOUT_US_MIN 1000
#define CS40L26_DSP_TIMEOUT_US_MAX 1100
-#define CS40L26_DSP_TIMEOUT_COUNT 50
+#define CS40L26_DSP_TIMEOUT_COUNT 100
#define CS40L26_DSP_MBOX_RESET 0x0
@@ -754,6 +759,8 @@
#define CS40L26_DSP_MBOX_CMD_LE_EST 0x07000004
+#define CS40L26_DSP_MBOX_CMD_OWT_DELETE_BASE 0x0D000000
+
#define CS40L26_DSP_MBOX_CMD_INDEX_MASK GENMASK(28, 24)
#define CS40L26_DSP_MBOX_CMD_INDEX_SHIFT 24
@@ -826,7 +833,7 @@
#define CS40L26_ROM_INDEX_END 0x01800026
#define CS40L26_OWT_INDEX_START 0x01400000
-#define CS40L26_OWT_INDEX_END 0x01400005
+#define CS40L26_OWT_INDEX_END 0x01400010
#define CS40L26_RAM_BANK_ID 0
@@ -872,16 +879,6 @@
#define CS40L26_IRQ_UNMASK 0
#define CS40L26_IRQ_MASK 1
-/* output */
-#define CS40L26_GLOBAL_ENABLES2_DEFAULT 0x01000000
-#define CS40L26_BST_CTRL_DEFAULT 0x000000AA
-#define CS40L26_DACPCM1_INPUT_DEFAULT 0x00000032
-#define CS40L26_ASP_ENABLES1_DEFAULT 0x00070003
-#define CS40L26_ASP_CTRL2_DEFAULT 0x20200011
-#define CS40L26_DSP1RX5_INPUT_DEFAULT 0x00000009
-
-#define CS40L26_NUM_OUTPUT_SETUP_WRITES 3
-
/* temp monitoring */
#define CS40L26_TEMPMON_EN_MASK BIT(10)
#define CS40L26_TEMPMON_EN_SHIFT 10
@@ -960,19 +957,19 @@
#define CS40L26_VXBR_REL_RATE_SHIFT 21
/* audio */
-#define CS40L26_PLL_CLK_CFG0 0x00
-#define CS40L26_PLL_CLK_CFG1 0x1B
-#define CS40L26_PLL_CLK_CFG2 0x21
-#define CS40L26_PLL_CLK_CFG3 0x28
-#define CS40L26_PLL_CLK_CFG4 0x30
-#define CS40L26_PLL_CLK_CFG5 0x33
-
-#define CS40L26_PLL_CLK_FRQ0 32768
-#define CS40L26_PLL_CLK_FRQ1 1536000
-#define CS40L26_PLL_CLK_FRQ2 3072000
-#define CS40L26_PLL_CLK_FRQ3 6144000
-#define CS40L26_PLL_CLK_FRQ4 9600000
-#define CS40L26_PLL_CLK_FRQ5 12288000
+#define CS40L26_PLL_CLK_CFG_32768 0x00
+#define CS40L26_PLL_CLK_CFG_1536000 0x1B
+#define CS40L26_PLL_CLK_CFG_3072000 0x21
+#define CS40L26_PLL_CLK_CFG_6144000 0x28
+#define CS40L26_PLL_CLK_CFG_9600000 0x30
+#define CS40L26_PLL_CLK_CFG_12288000 0x33
+
+#define CS40L26_PLL_CLK_FRQ_32768 32768
+#define CS40L26_PLL_CLK_FRQ_1536000 1536000
+#define CS40L26_PLL_CLK_FRQ_3072000 3072000
+#define CS40L26_PLL_CLK_FRQ_6144000 6144000
+#define CS40L26_PLL_CLK_FRQ_9600000 9600000
+#define CS40L26_PLL_CLK_FRQ_12288000 12288000
#define CS40L26_PLL_CLK_SEL_BCLK 0x0
#define CS40L26_PLL_CLK_SEL_FSYNC 0x1
@@ -982,6 +979,7 @@
#define CS40L26_PLL_CLK_CFG_MASK GENMASK(5, 0)
#define CS40L26_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE)
+#define CS40L26_RATES (SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000)
#define CS40L26_ASP_RX_WIDTH_MASK GENMASK(31, 24)
#define CS40L26_ASP_RX_WIDTH_SHIFT 24
@@ -1060,13 +1058,15 @@
#define CS40L26_CLASS_H_EN_SHIFT 4
#define CS40L26_BST_CTL_SEL_MASK GENMASK(1, 0)
-#define CS40L26_BST_CTL_SEL_FIXED 0x0
-#define CS40L26_BST_CTL_SEL_CLASSH 0x1
#define CS40L26_A2H_MAX_TUNINGS 5
#define CS40L26_A2H_VOLUME_MAX 0x7FFFFF
+#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
+
/* OWT */
#define CS40L26_WT_STR_MAX_LEN 512
#define CS40L26_WT_MAX_SEGS 512
@@ -1078,11 +1078,9 @@
#define CS40L26_WT_INDEF_TIME_VAL 0xFFFF
#define CS40L26_WT_MAX_TIME_VAL 16383 /* ms */
-#define CS40L26_WT_TERM_SIZE 4
-#define CS40L26_WT_WLEN_TERM_SIZE 8
-#define CS40L26_WT_HEADER_TERM 0xFFFFFF
-#define CS40L26_WT_HEADER_OFFSET 4
-#define CS40L26_WT_HEADER_DEFAULT_FLAGS 0x0000
+#define CS40L26_WT_WLEN_SIZE 4
+#define CS40L26_WT_HEADER_OFFSET 3
+#define CS40L26_WT_HEADER_DEFAULT_FLAGS 0x0000
#define CS40L26_WT_TYPE10_COMP_SEG_LEN_MAX 20
@@ -1289,6 +1287,13 @@ struct cs40l26_platform_data {
u32 svc_le_is_valid;
};
+struct cs40l26_owt {
+ int effect_id;
+ u32 wlength;
+ u32 trigger_index;
+ struct list_head list;
+};
+
struct cs40l26_private {
struct device *dev;
struct regmap *regmap;
@@ -1324,12 +1329,14 @@ struct cs40l26_private {
u8 last_wksrc_pol;
u8 wksrc_sts;
u32 event_count;
- u32 owt_wlength;
+ struct list_head owt_head;
int num_owt_effects;
int cal_requested;
u16 gain_pct;
u32 event_map_base;
struct cs40l26_svc_le *svc_le;
+ struct workqueue_struct *asp_workqueue;
+ struct work_struct asp_work;
};
struct cs40l26_codec {
@@ -1346,6 +1353,7 @@ struct cs40l26_codec {
int tdm_slot[2];
bool svc_for_streaming_data;
bool invert_streaming_data;
+ bool bypass_dsp;
};
struct cs40l26_pll_sysclk_config {
@@ -1354,6 +1362,8 @@ struct cs40l26_pll_sysclk_config {
};
/* exported function prototypes */
+int cs40l26_get_num_waves(struct cs40l26_private *cs40l26, u32 *num_waves);
+void cs40l26_asp_worker(struct work_struct *work);
int cs40l26_fw_swap(struct cs40l26_private *cs40l26, u32 id);
void cs40l26_vibe_state_set(struct cs40l26_private *cs40l26,
enum cs40l26_vibe_state);