summaryrefslogtreecommitdiff
path: root/cs40l26/cs40l26-codec.c
diff options
context:
space:
mode:
authorTai Kuo <taikuo@google.com>2021-09-09 18:08:33 +0800
committerchasewu <chasewu@google.com>2021-09-14 16:11:22 +0800
commite74b5ab950c7269d5990d6c83bd9661ce8724792 (patch)
treef59687e433af73bdaff13768501957e7a0ce71be /cs40l26/cs40l26-codec.c
parentdf022b2f60bb8e2b6bf07ed8cbb5d09a747b9a07 (diff)
downloadamplifiers-e74b5ab950c7269d5990d6c83bd9661ce8724792.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
Diffstat (limited to 'cs40l26/cs40l26-codec.c')
-rw-r--r--cs40l26/cs40l26-codec.c202
1 files changed, 153 insertions, 49 deletions
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;
}