summaryrefslogtreecommitdiffstats
path: root/sound/soc/codecs/cs42l51.c
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2019-05-06 16:14:09 +0200
committerTakashi Iwai <tiwai@suse.de>2019-05-06 16:14:34 +0200
commitd81645510ce2a140816c4cb37c45b78d810ca63f (patch)
treeedd9464900904d22a23da362bb152669480c5d26 /sound/soc/codecs/cs42l51.c
parent2854cd34fbab5f28a356d3667c26b7856a7b73e2 (diff)
parent378d590c494551a68a824b939c711bb9a280e9ef (diff)
downloadlinux-d81645510ce2a140816c4cb37c45b78d810ca63f.tar.bz2
Merge tag 'asoc-v5.2' of https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound into for-linus
ASoC: Updates for v5.2 This is a pretty huge set of changes, it's been a pretty active release all round but the big thing with this release is the Sound Open Firmware changes from Intel, providing another DSP framework for use with the DSPs in their SoCs. This one works with the firmware of the same name which is free software (unlike the previous DSP firmwares and framework) and there has been some interest in adoption by other systems already so hopefully we will see adoption by other vendors in the future. Other highlights include: - Support for MCLK/sample rate ratio setting in the generic cards. - Support for pin switches in the generic cards. - A big set of improvements to the TLV320AIC32x4 drivers from Annaliese McDermond. - New drivers for Freescale audio mixers, several Intel machines, several Mediatek machines, Meson G12A, Sound Open Firmware and Spreadtrum compressed audio and DMA devices.
Diffstat (limited to 'sound/soc/codecs/cs42l51.c')
-rw-r--r--sound/soc/codecs/cs42l51.c225
1 files changed, 220 insertions, 5 deletions
diff --git a/sound/soc/codecs/cs42l51.c b/sound/soc/codecs/cs42l51.c
index fd2bd74024c1..991e4ebd7a04 100644
--- a/sound/soc/codecs/cs42l51.c
+++ b/sound/soc/codecs/cs42l51.c
@@ -30,7 +30,9 @@
#include <sound/initval.h>
#include <sound/pcm_params.h>
#include <sound/pcm.h>
+#include <linux/gpio/consumer.h>
#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
#include "cs42l51.h"
@@ -40,11 +42,21 @@ enum master_slave_mode {
MODE_MASTER,
};
+static const char * const cs42l51_supply_names[] = {
+ "VL",
+ "VD",
+ "VA",
+ "VAHP",
+};
+
struct cs42l51_private {
unsigned int mclk;
struct clk *mclk_handle;
unsigned int audio_mode; /* The mode (I2S or left-justified) */
enum master_slave_mode func;
+ struct regulator_bulk_data supplies[ARRAY_SIZE(cs42l51_supply_names)];
+ struct gpio_desc *reset_gpio;
+ struct regmap *regmap;
};
#define CS42L51_FORMATS ( \
@@ -111,6 +123,7 @@ static const DECLARE_TLV_DB_SCALE(tone_tlv, -1050, 150, 0);
static const DECLARE_TLV_DB_SCALE(aout_tlv, -10200, 50, 0);
static const DECLARE_TLV_DB_SCALE(boost_tlv, 1600, 1600, 0);
+static const DECLARE_TLV_DB_SCALE(adc_boost_tlv, 2000, 2000, 0);
static const char *chan_mix[] = {
"L R",
"L+R",
@@ -139,6 +152,8 @@ static const struct snd_kcontrol_new cs42l51_snd_controls[] = {
SOC_SINGLE("Zero Cross Switch", CS42L51_DAC_CTL, 0, 0, 0),
SOC_DOUBLE_TLV("Mic Boost Volume",
CS42L51_MIC_CTL, 0, 1, 1, 0, boost_tlv),
+ SOC_DOUBLE_TLV("ADC Boost Volume",
+ CS42L51_MIC_CTL, 5, 6, 1, 0, adc_boost_tlv),
SOC_SINGLE_TLV("Bass Volume", CS42L51_TONE_CTL, 0, 0xf, 1, tone_tlv),
SOC_SINGLE_TLV("Treble Volume", CS42L51_TONE_CTL, 4, 0xf, 1, tone_tlv),
SOC_ENUM_EXT("PCM channel mixer",
@@ -195,7 +210,8 @@ static const struct snd_kcontrol_new cs42l51_adcr_mux_controls =
SOC_DAPM_ENUM("Route", cs42l51_adcr_mux_enum);
static const struct snd_soc_dapm_widget cs42l51_dapm_widgets[] = {
- SND_SOC_DAPM_MICBIAS("Mic Bias", CS42L51_MIC_POWER_CTL, 1, 1),
+ SND_SOC_DAPM_SUPPLY("Mic Bias", CS42L51_MIC_POWER_CTL, 1, 1, NULL,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_PGA_E("Left PGA", CS42L51_POWER_CTL1, 3, 1, NULL, 0,
cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD),
SND_SOC_DAPM_PGA_E("Right PGA", CS42L51_POWER_CTL1, 4, 1, NULL, 0,
@@ -329,6 +345,19 @@ static struct cs42l51_ratios slave_auto_ratios[] = {
{ 256, CS42L51_DSM_MODE, 1 }, { 384, CS42L51_DSM_MODE, 1 },
};
+/*
+ * Master mode mclk/fs ratios.
+ * Recommended configurations are SSM for 4-50khz and DSM for 50-100kHz ranges
+ * The table below provides support of following ratios:
+ * 128: SSM (%128) with div2 disabled
+ * 256: SSM (%128) with div2 enabled
+ * In both cases, if sampling rate is above 50kHz, SSM is overridden
+ * with DSM (%128) configuration
+ */
+static struct cs42l51_ratios master_ratios[] = {
+ { 128, CS42L51_SSM_MODE, 0 }, { 256, CS42L51_SSM_MODE, 1 },
+};
+
static int cs42l51_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
@@ -351,11 +380,13 @@ static int cs42l51_hw_params(struct snd_pcm_substream *substream,
unsigned int ratio;
struct cs42l51_ratios *ratios = NULL;
int nr_ratios = 0;
- int intf_ctl, power_ctl, fmt;
+ int intf_ctl, power_ctl, fmt, mode;
switch (cs42l51->func) {
case MODE_MASTER:
- return -EINVAL;
+ ratios = master_ratios;
+ nr_ratios = ARRAY_SIZE(master_ratios);
+ break;
case MODE_SLAVE:
ratios = slave_ratios;
nr_ratios = ARRAY_SIZE(slave_ratios);
@@ -391,7 +422,16 @@ static int cs42l51_hw_params(struct snd_pcm_substream *substream,
switch (cs42l51->func) {
case MODE_MASTER:
intf_ctl |= CS42L51_INTF_CTL_MASTER;
- power_ctl |= CS42L51_MIC_POWER_CTL_SPEED(ratios[i].speed_mode);
+ mode = ratios[i].speed_mode;
+ /* Force DSM mode if sampling rate is above 50kHz */
+ if (rate > 50000)
+ mode = CS42L51_DSM_MODE;
+ power_ctl |= CS42L51_MIC_POWER_CTL_SPEED(mode);
+ /*
+ * Auto detect mode is not applicable for master mode and has to
+ * be disabled. Otherwise SPEED[1:0] bits will be ignored.
+ */
+ power_ctl &= ~CS42L51_MIC_POWER_CTL_AUTO;
break;
case MODE_SLAVE:
power_ctl |= CS42L51_MIC_POWER_CTL_SPEED(ratios[i].speed_mode);
@@ -464,6 +504,13 @@ static int cs42l51_dai_mute(struct snd_soc_dai *dai, int mute)
return snd_soc_component_write(component, CS42L51_DAC_OUT_CTL, reg);
}
+static int cs42l51_of_xlate_dai_id(struct snd_soc_component *component,
+ struct device_node *endpoint)
+{
+ /* return dai id 0, whatever the endpoint index */
+ return 0;
+}
+
static const struct snd_soc_dai_ops cs42l51_dai_ops = {
.hw_params = cs42l51_hw_params,
.set_sysclk = cs42l51_set_dai_sysclk,
@@ -526,13 +573,113 @@ static const struct snd_soc_component_driver soc_component_device_cs42l51 = {
.num_dapm_widgets = ARRAY_SIZE(cs42l51_dapm_widgets),
.dapm_routes = cs42l51_routes,
.num_dapm_routes = ARRAY_SIZE(cs42l51_routes),
+ .of_xlate_dai_id = cs42l51_of_xlate_dai_id,
.idle_bias_on = 1,
.use_pmdown_time = 1,
.endianness = 1,
.non_legacy_dai_naming = 1,
};
+static bool cs42l51_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS42L51_POWER_CTL1:
+ case CS42L51_MIC_POWER_CTL:
+ case CS42L51_INTF_CTL:
+ case CS42L51_MIC_CTL:
+ case CS42L51_ADC_CTL:
+ case CS42L51_ADC_INPUT:
+ case CS42L51_DAC_OUT_CTL:
+ case CS42L51_DAC_CTL:
+ case CS42L51_ALC_PGA_CTL:
+ case CS42L51_ALC_PGB_CTL:
+ case CS42L51_ADCA_ATT:
+ case CS42L51_ADCB_ATT:
+ case CS42L51_ADCA_VOL:
+ case CS42L51_ADCB_VOL:
+ case CS42L51_PCMA_VOL:
+ case CS42L51_PCMB_VOL:
+ case CS42L51_BEEP_FREQ:
+ case CS42L51_BEEP_VOL:
+ case CS42L51_BEEP_CONF:
+ case CS42L51_TONE_CTL:
+ case CS42L51_AOUTA_VOL:
+ case CS42L51_AOUTB_VOL:
+ case CS42L51_PCM_MIXER:
+ case CS42L51_LIMIT_THRES_DIS:
+ case CS42L51_LIMIT_REL:
+ case CS42L51_LIMIT_ATT:
+ case CS42L51_ALC_EN:
+ case CS42L51_ALC_REL:
+ case CS42L51_ALC_THRES:
+ case CS42L51_NOISE_CONF:
+ case CS42L51_CHARGE_FREQ:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool cs42l51_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS42L51_STATUS:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool cs42l51_readable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS42L51_CHIP_REV_ID:
+ case CS42L51_POWER_CTL1:
+ case CS42L51_MIC_POWER_CTL:
+ case CS42L51_INTF_CTL:
+ case CS42L51_MIC_CTL:
+ case CS42L51_ADC_CTL:
+ case CS42L51_ADC_INPUT:
+ case CS42L51_DAC_OUT_CTL:
+ case CS42L51_DAC_CTL:
+ case CS42L51_ALC_PGA_CTL:
+ case CS42L51_ALC_PGB_CTL:
+ case CS42L51_ADCA_ATT:
+ case CS42L51_ADCB_ATT:
+ case CS42L51_ADCA_VOL:
+ case CS42L51_ADCB_VOL:
+ case CS42L51_PCMA_VOL:
+ case CS42L51_PCMB_VOL:
+ case CS42L51_BEEP_FREQ:
+ case CS42L51_BEEP_VOL:
+ case CS42L51_BEEP_CONF:
+ case CS42L51_TONE_CTL:
+ case CS42L51_AOUTA_VOL:
+ case CS42L51_AOUTB_VOL:
+ case CS42L51_PCM_MIXER:
+ case CS42L51_LIMIT_THRES_DIS:
+ case CS42L51_LIMIT_REL:
+ case CS42L51_LIMIT_ATT:
+ case CS42L51_ALC_EN:
+ case CS42L51_ALC_REL:
+ case CS42L51_ALC_THRES:
+ case CS42L51_NOISE_CONF:
+ case CS42L51_STATUS:
+ case CS42L51_CHARGE_FREQ:
+ return true;
+ default:
+ return false;
+ }
+}
+
const struct regmap_config cs42l51_regmap = {
+ .reg_bits = 8,
+ .reg_stride = 1,
+ .val_bits = 8,
+ .use_single_write = true,
+ .readable_reg = cs42l51_readable_reg,
+ .volatile_reg = cs42l51_volatile_reg,
+ .writeable_reg = cs42l51_writeable_reg,
.max_register = CS42L51_CHARGE_FREQ,
.cache_type = REGCACHE_RBTREE,
};
@@ -542,7 +689,7 @@ int cs42l51_probe(struct device *dev, struct regmap *regmap)
{
struct cs42l51_private *cs42l51;
unsigned int val;
- int ret;
+ int ret, i;
if (IS_ERR(regmap))
return PTR_ERR(regmap);
@@ -553,6 +700,7 @@ int cs42l51_probe(struct device *dev, struct regmap *regmap)
return -ENOMEM;
dev_set_drvdata(dev, cs42l51);
+ cs42l51->regmap = regmap;
cs42l51->mclk_handle = devm_clk_get(dev, "MCLK");
if (IS_ERR(cs42l51->mclk_handle)) {
@@ -561,6 +709,34 @@ int cs42l51_probe(struct device *dev, struct regmap *regmap)
cs42l51->mclk_handle = NULL;
}
+ for (i = 0; i < ARRAY_SIZE(cs42l51->supplies); i++)
+ cs42l51->supplies[i].supply = cs42l51_supply_names[i];
+
+ ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(cs42l51->supplies),
+ cs42l51->supplies);
+ if (ret != 0) {
+ dev_err(dev, "Failed to request supplies: %d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(cs42l51->supplies),
+ cs42l51->supplies);
+ if (ret != 0) {
+ dev_err(dev, "Failed to enable supplies: %d\n", ret);
+ return ret;
+ }
+
+ cs42l51->reset_gpio = devm_gpiod_get_optional(dev, "reset",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(cs42l51->reset_gpio))
+ return PTR_ERR(cs42l51->reset_gpio);
+
+ if (cs42l51->reset_gpio) {
+ dev_dbg(dev, "Release reset gpio\n");
+ gpiod_set_value_cansleep(cs42l51->reset_gpio, 0);
+ mdelay(2);
+ }
+
/* Verify that we have a CS42L51 */
ret = regmap_read(regmap, CS42L51_CHIP_REV_ID, &val);
if (ret < 0) {
@@ -579,11 +755,50 @@ int cs42l51_probe(struct device *dev, struct regmap *regmap)
ret = devm_snd_soc_register_component(dev,
&soc_component_device_cs42l51, &cs42l51_dai, 1);
+ if (ret < 0)
+ goto error;
+
+ return 0;
+
error:
+ regulator_bulk_disable(ARRAY_SIZE(cs42l51->supplies),
+ cs42l51->supplies);
return ret;
}
EXPORT_SYMBOL_GPL(cs42l51_probe);
+int cs42l51_remove(struct device *dev)
+{
+ struct cs42l51_private *cs42l51 = dev_get_drvdata(dev);
+
+ gpiod_set_value_cansleep(cs42l51->reset_gpio, 1);
+
+ return regulator_bulk_disable(ARRAY_SIZE(cs42l51->supplies),
+ cs42l51->supplies);
+}
+EXPORT_SYMBOL_GPL(cs42l51_remove);
+
+int __maybe_unused cs42l51_suspend(struct device *dev)
+{
+ struct cs42l51_private *cs42l51 = dev_get_drvdata(dev);
+
+ regcache_cache_only(cs42l51->regmap, true);
+ regcache_mark_dirty(cs42l51->regmap);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(cs42l51_suspend);
+
+int __maybe_unused cs42l51_resume(struct device *dev)
+{
+ struct cs42l51_private *cs42l51 = dev_get_drvdata(dev);
+
+ regcache_cache_only(cs42l51->regmap, false);
+
+ return regcache_sync(cs42l51->regmap);
+}
+EXPORT_SYMBOL_GPL(cs42l51_resume);
+
const struct of_device_id cs42l51_of_match[] = {
{ .compatible = "cirrus,cs42l51", },
{ }