summaryrefslogtreecommitdiff
path: root/sound/soc/codecs/cs42l42.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/codecs/cs42l42.c')
-rw-r--r--sound/soc/codecs/cs42l42.c133
1 files changed, 89 insertions, 44 deletions
diff --git a/sound/soc/codecs/cs42l42.c b/sound/soc/codecs/cs42l42.c
index 2fefbcf7bd13..e3edaa1a2761 100644
--- a/sound/soc/codecs/cs42l42.c
+++ b/sound/soc/codecs/cs42l42.c
@@ -20,6 +20,7 @@
#include <linux/slab.h>
#include <linux/acpi.h>
#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
#include <linux/property.h>
#include <linux/regulator/consumer.h>
#include <linux/gpio/consumer.h>
@@ -293,6 +294,7 @@ bool cs42l42_readable_register(struct device *dev, unsigned int reg)
case CS42L42_SPDIF_SW_CTL1:
case CS42L42_SRC_SDIN_FS:
case CS42L42_SRC_SDOUT_FS:
+ case CS42L42_SOFT_RESET_REBOOT:
case CS42L42_SPDIF_CTL1:
case CS42L42_SPDIF_CTL2:
case CS42L42_SPDIF_CTL3:
@@ -358,6 +360,7 @@ bool cs42l42_volatile_register(struct device *dev, unsigned int reg)
case CS42L42_LOAD_DET_DONE:
case CS42L42_DET_STATUS1:
case CS42L42_DET_STATUS2:
+ case CS42L42_SOFT_RESET_REBOOT:
return true;
default:
return false;
@@ -523,6 +526,10 @@ static const struct snd_soc_dapm_widget cs42l42_dapm_widgets[] = {
/* Playback/Capture Requirements */
SND_SOC_DAPM_SUPPLY("SCLK", CS42L42_ASP_CLK_CFG, CS42L42_ASP_SCLK_EN_SHIFT, 0, NULL, 0),
+
+ /* Soundwire SRC power control */
+ SND_SOC_DAPM_PGA("DACSRC", CS42L42_PWR_CTL2, CS42L42_DAC_SRC_PDNB_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("ADCSRC", CS42L42_PWR_CTL2, CS42L42_ADC_SRC_PDNB_SHIFT, 0, NULL, 0),
};
static const struct snd_soc_dapm_route cs42l42_audio_map[] = {
@@ -590,7 +597,6 @@ const struct snd_soc_component_driver cs42l42_soc_component = {
.num_dapm_routes = ARRAY_SIZE(cs42l42_audio_map),
.controls = cs42l42_snd_controls,
.num_controls = ARRAY_SIZE(cs42l42_snd_controls),
- .idle_bias_on = 1,
.endianness = 1,
};
EXPORT_SYMBOL_NS_GPL(cs42l42_soc_component, SND_SOC_CS42L42_CORE);
@@ -651,11 +657,11 @@ static const struct cs42l42_pll_params pll_ratio_table[] = {
{ 24576000, 1, 0x03, 0x40, 0x000000, 0x03, 0x10, 12288000, 128, 1}
};
-static int cs42l42_pll_config(struct snd_soc_component *component, unsigned int clk)
+int cs42l42_pll_config(struct snd_soc_component *component, unsigned int clk,
+ unsigned int sample_rate)
{
struct cs42l42_private *cs42l42 = snd_soc_component_get_drvdata(component);
int i;
- u32 fsync;
/* Don't reconfigure if there is an audio stream running */
if (cs42l42->stream_use) {
@@ -666,6 +672,10 @@ static int cs42l42_pll_config(struct snd_soc_component *component, unsigned int
}
for (i = 0; i < ARRAY_SIZE(pll_ratio_table); i++) {
+ /* MCLKint must be a multiple of the sample rate */
+ if (pll_ratio_table[i].mclk_int % sample_rate)
+ continue;
+
if (pll_ratio_table[i].sclk == clk) {
cs42l42->pll_config = i;
@@ -677,40 +687,6 @@ static int cs42l42_pll_config(struct snd_soc_component *component, unsigned int
(pll_ratio_table[i].mclk_int !=
24000000)) <<
CS42L42_INTERNAL_FS_SHIFT);
-
- /* Set up the LRCLK */
- fsync = clk / cs42l42->srate;
- if (((fsync * cs42l42->srate) != clk)
- || ((fsync % 2) != 0)) {
- dev_err(component->dev,
- "Unsupported sclk %d/sample rate %d\n",
- clk,
- cs42l42->srate);
- return -EINVAL;
- }
- /* Set the LRCLK period */
- snd_soc_component_update_bits(component,
- CS42L42_FSYNC_P_LOWER,
- CS42L42_FSYNC_PERIOD_MASK,
- CS42L42_FRAC0_VAL(fsync - 1) <<
- CS42L42_FSYNC_PERIOD_SHIFT);
- snd_soc_component_update_bits(component,
- CS42L42_FSYNC_P_UPPER,
- CS42L42_FSYNC_PERIOD_MASK,
- CS42L42_FRAC1_VAL(fsync - 1) <<
- CS42L42_FSYNC_PERIOD_SHIFT);
- /* Set the LRCLK to 50% duty cycle */
- fsync = fsync / 2;
- snd_soc_component_update_bits(component,
- CS42L42_FSYNC_PW_LOWER,
- CS42L42_FSYNC_PULSE_WIDTH_MASK,
- CS42L42_FRAC0_VAL(fsync - 1) <<
- CS42L42_FSYNC_PULSE_WIDTH_SHIFT);
- snd_soc_component_update_bits(component,
- CS42L42_FSYNC_PW_UPPER,
- CS42L42_FSYNC_PULSE_WIDTH_MASK,
- CS42L42_FRAC1_VAL(fsync - 1) <<
- CS42L42_FSYNC_PULSE_WIDTH_SHIFT);
if (pll_ratio_table[i].mclk_src_sel == 0) {
/* Pass the clock straight through */
snd_soc_component_update_bits(component,
@@ -768,8 +744,9 @@ static int cs42l42_pll_config(struct snd_soc_component *component, unsigned int
return -EINVAL;
}
+EXPORT_SYMBOL_NS_GPL(cs42l42_pll_config, SND_SOC_CS42L42_CORE);
-static void cs42l42_src_config(struct snd_soc_component *component, unsigned int sample_rate)
+void cs42l42_src_config(struct snd_soc_component *component, unsigned int sample_rate)
{
struct cs42l42_private *cs42l42 = snd_soc_component_get_drvdata(component);
unsigned int fs;
@@ -801,6 +778,47 @@ static void cs42l42_src_config(struct snd_soc_component *component, unsigned int
CS42L42_CLK_OASRC_SEL_MASK,
fs << CS42L42_CLK_OASRC_SEL_SHIFT);
}
+EXPORT_SYMBOL_NS_GPL(cs42l42_src_config, SND_SOC_CS42L42_CORE);
+
+static int cs42l42_asp_config(struct snd_soc_component *component,
+ unsigned int sclk, unsigned int sample_rate)
+{
+ u32 fsync = sclk / sample_rate;
+
+ /* Set up the LRCLK */
+ if (((fsync * sample_rate) != sclk) || ((fsync % 2) != 0)) {
+ dev_err(component->dev,
+ "Unsupported sclk %d/sample rate %d\n",
+ sclk,
+ sample_rate);
+ return -EINVAL;
+ }
+ /* Set the LRCLK period */
+ snd_soc_component_update_bits(component,
+ CS42L42_FSYNC_P_LOWER,
+ CS42L42_FSYNC_PERIOD_MASK,
+ CS42L42_FRAC0_VAL(fsync - 1) <<
+ CS42L42_FSYNC_PERIOD_SHIFT);
+ snd_soc_component_update_bits(component,
+ CS42L42_FSYNC_P_UPPER,
+ CS42L42_FSYNC_PERIOD_MASK,
+ CS42L42_FRAC1_VAL(fsync - 1) <<
+ CS42L42_FSYNC_PERIOD_SHIFT);
+ /* Set the LRCLK to 50% duty cycle */
+ fsync = fsync / 2;
+ snd_soc_component_update_bits(component,
+ CS42L42_FSYNC_PW_LOWER,
+ CS42L42_FSYNC_PULSE_WIDTH_MASK,
+ CS42L42_FRAC0_VAL(fsync - 1) <<
+ CS42L42_FSYNC_PULSE_WIDTH_SHIFT);
+ snd_soc_component_update_bits(component,
+ CS42L42_FSYNC_PW_UPPER,
+ CS42L42_FSYNC_PULSE_WIDTH_MASK,
+ CS42L42_FRAC1_VAL(fsync - 1) <<
+ CS42L42_FSYNC_PULSE_WIDTH_SHIFT);
+
+ return 0;
+}
static int cs42l42_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
{
@@ -891,13 +909,12 @@ static int cs42l42_pcm_hw_params(struct snd_pcm_substream *substream,
struct cs42l42_private *cs42l42 = snd_soc_component_get_drvdata(component);
unsigned int channels = params_channels(params);
unsigned int width = (params_width(params) / 8) - 1;
+ unsigned int sample_rate = params_rate(params);
unsigned int slot_width = 0;
unsigned int val = 0;
unsigned int bclk;
int ret;
- cs42l42->srate = params_rate(params);
-
if (cs42l42->bclk_ratio) {
/* machine driver has set the BCLK/samp-rate ratio */
bclk = cs42l42->bclk_ratio * params_rate(params);
@@ -954,11 +971,15 @@ static int cs42l42_pcm_hw_params(struct snd_pcm_substream *substream,
break;
}
- ret = cs42l42_pll_config(component, bclk);
+ ret = cs42l42_pll_config(component, bclk, sample_rate);
if (ret)
return ret;
- cs42l42_src_config(component, params_rate(params));
+ ret = cs42l42_asp_config(component, bclk, sample_rate);
+ if (ret)
+ return ret;
+
+ cs42l42_src_config(component, sample_rate);
return 0;
}
@@ -998,7 +1019,7 @@ static int cs42l42_set_bclk_ratio(struct snd_soc_dai *dai,
return 0;
}
-static int cs42l42_mute_stream(struct snd_soc_dai *dai, int mute, int stream)
+int cs42l42_mute_stream(struct snd_soc_dai *dai, int mute, int stream)
{
struct snd_soc_component *component = dai->component;
struct cs42l42_private *cs42l42 = snd_soc_component_get_drvdata(component);
@@ -1091,6 +1112,7 @@ static int cs42l42_mute_stream(struct snd_soc_dai *dai, int mute, int stream)
return 0;
}
+EXPORT_SYMBOL_NS_GPL(cs42l42_mute_stream, SND_SOC_CS42L42_CORE);
#define CS42L42_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
SNDRV_PCM_FMTBIT_S24_LE |\
@@ -1633,7 +1655,7 @@ static const struct cs42l42_irq_params irq_params_table[] = {
CS42L42_TSRS_PLUG_VAL_MASK}
};
-static irqreturn_t cs42l42_irq_thread(int irq, void *data)
+irqreturn_t cs42l42_irq_thread(int irq, void *data)
{
struct cs42l42_private *cs42l42 = (struct cs42l42_private *)data;
unsigned int stickies[12];
@@ -1642,9 +1664,11 @@ static irqreturn_t cs42l42_irq_thread(int irq, void *data)
unsigned int current_button_status;
unsigned int i;
+ pm_runtime_get_sync(cs42l42->dev);
mutex_lock(&cs42l42->irq_lock);
if (cs42l42->suspended || !cs42l42->init_done) {
mutex_unlock(&cs42l42->irq_lock);
+ pm_runtime_put_autosuspend(cs42l42->dev);
return IRQ_NONE;
}
@@ -1747,9 +1771,12 @@ static irqreturn_t cs42l42_irq_thread(int irq, void *data)
}
mutex_unlock(&cs42l42->irq_lock);
+ pm_runtime_mark_last_busy(cs42l42->dev);
+ pm_runtime_put_autosuspend(cs42l42->dev);
return IRQ_HANDLED;
}
+EXPORT_SYMBOL_NS_GPL(cs42l42_irq_thread, SND_SOC_CS42L42_CORE);
static void cs42l42_set_interrupt_masks(struct cs42l42_private *cs42l42)
{
@@ -2125,6 +2152,9 @@ int cs42l42_suspend(struct device *dev)
u8 save_regs[ARRAY_SIZE(cs42l42_shutdown_seq)];
int i, ret;
+ if (!cs42l42->init_done)
+ return 0;
+
/*
* Wait for threaded irq handler to be idle and stop it processing
* future interrupts. This ensures a safe disable if the interrupt
@@ -2185,6 +2215,9 @@ int cs42l42_resume(struct device *dev)
struct cs42l42_private *cs42l42 = dev_get_drvdata(dev);
int ret;
+ if (!cs42l42->init_done)
+ return 0;
+
/*
* If jack was unplugged and re-plugged during suspend it could
* have changed type but the tip-sense state hasn't changed.
@@ -2369,6 +2402,18 @@ int cs42l42_init(struct cs42l42_private *cs42l42)
if (ret != 0)
goto err_shutdown;
+ /*
+ * SRC power is linked to ASP power so doesn't work in Soundwire mode.
+ * Override it and use DAPM to control SRC power for Soundwire.
+ */
+ if (cs42l42->sdw_peripheral) {
+ regmap_update_bits(cs42l42->regmap, CS42L42_PWR_CTL2,
+ CS42L42_SRC_PDN_OVERRIDE_MASK |
+ CS42L42_DAC_SRC_PDNB_MASK |
+ CS42L42_ADC_SRC_PDNB_MASK,
+ CS42L42_SRC_PDN_OVERRIDE_MASK);
+ }
+
/* Setup headset detection */
cs42l42_setup_hs_type_detect(cs42l42);