diff options
author | Tai Kuo <taikuo@google.com> | 2021-09-09 18:08:33 +0800 |
---|---|---|
committer | TreeHugger Robot <treehugger-gerrit@google.com> | 2021-09-14 03:22:24 +0000 |
commit | 4aa86221a59e3dea6d20b5e2081a22f1ee2ec02d (patch) | |
tree | dd46e11cfd2dddfcd0aeba32da99c2673e19e3c0 | |
parent | a1ef5db43f5188f68d96b56b4872d87aa7fab020 (diff) | |
download | amplifiers-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.c | 1 | ||||
-rw-r--r-- | cs40l26/cl_dsp.h | 2 | ||||
-rw-r--r-- | cs40l26/cs40l26-codec.c | 202 | ||||
-rw-r--r-- | cs40l26/cs40l26-sysfs.c | 30 | ||||
-rw-r--r-- | cs40l26/cs40l26-tables.c | 1 | ||||
-rw-r--r-- | cs40l26/cs40l26.c | 374 | ||||
-rw-r--r-- | cs40l26/cs40l26.h | 76 |
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, ®); 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, ®); 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, ®); + 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, ®); + 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, ®); - 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, ®); + 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); |