summaryrefslogtreecommitdiff
path: root/sound/soc/codecs/ssm2602.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/codecs/ssm2602.c')
-rw-r--r--sound/soc/codecs/ssm2602.c121
1 files changed, 78 insertions, 43 deletions
diff --git a/sound/soc/codecs/ssm2602.c b/sound/soc/codecs/ssm2602.c
index 501a4e73b185..fccd2eacd7a6 100644
--- a/sound/soc/codecs/ssm2602.c
+++ b/sound/soc/codecs/ssm2602.c
@@ -1,31 +1,17 @@
-/*
- * File: sound/soc/codecs/ssm2602.c
- * Author: Cliff Cai <Cliff.Cai@analog.com>
- *
- * Created: Tue June 06 2008
- * Description: Driver for ssm2602 sound chip
- *
- * Modified:
- * Copyright 2008 Analog Devices Inc.
- *
- * Bugs: Enter bugs at http://blackfin.uclinux.org/
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see the file COPYING, or write
- * to the Free Software Foundation, Inc.,
- * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
+// SPDX-License-Identifier: GPL-2.0-or-later
+//
+// File: sound/soc/codecs/ssm2602.c
+// Author: Cliff Cai <Cliff.Cai@analog.com>
+//
+// Created: Tue June 06 2008
+// Description: Driver for ssm2602 sound chip
+//
+// Modified:
+// Copyright 2008 Analog Devices Inc.
+//
+// Bugs: Enter bugs at http://blackfin.uclinux.org/
+
+#include <linux/delay.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/slab.h>
@@ -67,6 +53,18 @@ static const struct reg_default ssm2602_reg[SSM2602_CACHEREGNUM] = {
{ .reg = 0x09, .def = 0x0000 }
};
+/*
+ * ssm2602 register patch
+ * Workaround for playback distortions after power up: activates digital
+ * core, and then powers on output, DAC, and whole chip at the same time
+ */
+
+static const struct reg_sequence ssm2602_patch[] = {
+ { SSM2602_ACTIVE, 0x01 },
+ { SSM2602_PWR, 0x07 },
+ { SSM2602_RESET, 0x00 },
+};
+
/*Appending several "None"s just for OSS mixer use*/
static const char *ssm2602_input_select[] = {
@@ -111,7 +109,6 @@ SOC_SINGLE_TLV("Sidetone Playback Volume", SSM2602_APANA, 6, 3, 1,
SOC_SINGLE("Mic Boost (+20dB)", SSM2602_APANA, 0, 1, 0),
SOC_SINGLE("Mic Boost2 (+20dB)", SSM2602_APANA, 8, 1, 0),
-SOC_SINGLE("Mic Switch", SSM2602_APANA, 1, 1, 1),
};
/* Output Mixer */
@@ -121,10 +118,31 @@ SOC_DAPM_SINGLE("HiFi Playback Switch", SSM2602_APANA, 4, 1, 0),
SOC_DAPM_SINGLE("Mic Sidetone Switch", SSM2602_APANA, 5, 1, 0),
};
+static const struct snd_kcontrol_new mic_ctl =
+ SOC_DAPM_SINGLE("Switch", SSM2602_APANA, 1, 1, 1);
+
/* Input mux */
static const struct snd_kcontrol_new ssm2602_input_mux_controls =
SOC_DAPM_ENUM("Input Select", ssm2602_enum[0]);
+static int ssm2602_mic_switch_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ /*
+ * According to the ssm2603 data sheet (control register sequencing),
+ * the digital core should be activated only after all necessary bits
+ * in the power register are enabled, and a delay determined by the
+ * decoupling capacitor on the VMID pin has passed. If the digital core
+ * is activated too early, or even before the ADC is powered up, audible
+ * artifacts appear at the beginning and end of the recorded signal.
+ *
+ * In practice, audible artifacts disappear well over 500 ms.
+ */
+ msleep(500);
+
+ return 0;
+}
+
static const struct snd_soc_dapm_widget ssm260x_dapm_widgets[] = {
SND_SOC_DAPM_DAC("DAC", "HiFi Playback", SSM2602_PWR, 3, 1),
SND_SOC_DAPM_ADC("ADC", "HiFi Capture", SSM2602_PWR, 2, 1),
@@ -146,6 +164,9 @@ SND_SOC_DAPM_MIXER("Output Mixer", SSM2602_PWR, 4, 1,
SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM, 0, 0, &ssm2602_input_mux_controls),
SND_SOC_DAPM_MICBIAS("Mic Bias", SSM2602_PWR, 1, 1),
+SND_SOC_DAPM_SWITCH_E("Mic Switch", SSM2602_APANA, 1, 1, &mic_ctl,
+ ssm2602_mic_switch_event, SND_SOC_DAPM_PRE_PMU),
+
SND_SOC_DAPM_OUTPUT("LHPOUT"),
SND_SOC_DAPM_OUTPUT("RHPOUT"),
SND_SOC_DAPM_INPUT("MICIN"),
@@ -178,9 +199,11 @@ static const struct snd_soc_dapm_route ssm2602_routes[] = {
{"LHPOUT", NULL, "Output Mixer"},
{"Input Mux", "Line", "Line Input"},
- {"Input Mux", "Mic", "Mic Bias"},
+ {"Input Mux", "Mic", "Mic Switch"},
{"ADC", NULL, "Input Mux"},
+ {"Mic Switch", NULL, "Mic Bias"},
+
{"Mic Bias", NULL, "MICIN"},
};
@@ -269,9 +292,12 @@ static inline int ssm2602_get_coeff(int mclk, int rate)
int i;
for (i = 0; i < ARRAY_SIZE(ssm2602_coeff_table); i++) {
- if (ssm2602_coeff_table[i].rate == rate &&
- ssm2602_coeff_table[i].mclk == mclk)
- return ssm2602_coeff_table[i].srate;
+ if (ssm2602_coeff_table[i].rate == rate) {
+ if (ssm2602_coeff_table[i].mclk == mclk)
+ return ssm2602_coeff_table[i].srate;
+ if (ssm2602_coeff_table[i].mclk == mclk / 2)
+ return ssm2602_coeff_table[i].srate | SRATE_CORECLK_DIV2;
+ }
}
return -EINVAL;
}
@@ -327,7 +353,7 @@ static int ssm2602_startup(struct snd_pcm_substream *substream,
return 0;
}
-static int ssm2602_mute(struct snd_soc_dai *dai, int mute)
+static int ssm2602_mute(struct snd_soc_dai *dai, int mute, int direction)
{
struct ssm2602_priv *ssm2602 = snd_soc_component_get_drvdata(dai->component);
@@ -354,18 +380,24 @@ static int ssm2602_set_dai_sysclk(struct snd_soc_dai *codec_dai,
switch (freq) {
case 12288000:
case 18432000:
+ case 24576000:
+ case 36864000:
ssm2602->sysclk_constraints = &ssm2602_constraints_12288000;
break;
case 11289600:
case 16934400:
+ case 22579200:
+ case 33868800:
ssm2602->sysclk_constraints = &ssm2602_constraints_11289600;
break;
case 12000000:
+ case 24000000:
ssm2602->sysclk_constraints = NULL;
break;
default:
return -EINVAL;
}
+
ssm2602->sysclk = freq;
} else {
unsigned int mask;
@@ -400,11 +432,11 @@ static int ssm2602_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int iface = 0;
/* set master/slave audio interface */
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
iface |= 0x0040;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
break;
default:
return -EINVAL;
@@ -494,9 +526,10 @@ static int ssm2602_set_bias_level(struct snd_soc_component *component,
static const struct snd_soc_dai_ops ssm2602_dai_ops = {
.startup = ssm2602_startup,
.hw_params = ssm2602_hw_params,
- .digital_mute = ssm2602_mute,
+ .mute_stream = ssm2602_mute,
.set_sysclk = ssm2602_set_dai_sysclk,
.set_fmt = ssm2602_set_dai_fmt,
+ .no_capture_mute = 1,
};
static struct snd_soc_dai_driver ssm2602_dai = {
@@ -514,8 +547,8 @@ static struct snd_soc_dai_driver ssm2602_dai = {
.rates = SSM2602_RATES,
.formats = SSM2602_FORMATS,},
.ops = &ssm2602_dai_ops,
- .symmetric_rates = 1,
- .symmetric_samplebits = 1,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
};
static int ssm2602_resume(struct snd_soc_component *component)
@@ -529,7 +562,7 @@ static int ssm2602_resume(struct snd_soc_component *component)
static int ssm2602_component_probe(struct snd_soc_component *component)
{
- struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
struct ssm2602_priv *ssm2602 = snd_soc_component_get_drvdata(component);
int ret;
@@ -554,7 +587,7 @@ static int ssm2602_component_probe(struct snd_soc_component *component)
static int ssm2604_component_probe(struct snd_soc_component *component)
{
- struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
int ret;
ret = snd_soc_dapm_new_controls(dapm, ssm2604_dapm_widgets,
@@ -577,6 +610,9 @@ static int ssm260x_component_probe(struct snd_soc_component *component)
return ret;
}
+ regmap_register_patch(ssm2602->regmap, ssm2602_patch,
+ ARRAY_SIZE(ssm2602_patch));
+
/* set the update bits */
regmap_update_bits(ssm2602->regmap, SSM2602_LINVOL,
LINVOL_LRIN_BOTH, LINVOL_LRIN_BOTH);
@@ -612,7 +648,6 @@ static const struct snd_soc_component_driver soc_component_dev_ssm2602 = {
.idle_bias_on = 1,
.use_pmdown_time = 1,
.endianness = 1,
- .non_legacy_dai_naming = 1,
};
static bool ssm2602_register_volatile(struct device *dev, unsigned int reg)