blob: 272041c6236a996a11ffc615b7d37cc269a4fc6a [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0-only
//
// Cirrus Logic Madera class codecs common support
//
// Copyright (C) 2015-2019 Cirrus Logic, Inc. and
// Cirrus Logic International Semiconductor Ltd.
//
#include <linux/delay.h>
#include <linux/gcd.h>
#include <linux/module.h>
#include <linux/pm_runtime.h>
#include <linux/slab.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/tlv.h>
#include <linux/irqchip/irq-madera.h>
#include <linux/mfd/madera/core.h>
#include <linux/mfd/madera/registers.h>
#include <linux/mfd/madera/pdata.h>
#include <sound/madera-pdata.h>
#include <dt-bindings/sound/madera.h>
#include "madera.h"
#define MADERA_AIF_BCLK_CTRL 0x00
#define MADERA_AIF_TX_PIN_CTRL 0x01
#define MADERA_AIF_RX_PIN_CTRL 0x02
#define MADERA_AIF_RATE_CTRL 0x03
#define MADERA_AIF_FORMAT 0x04
#define MADERA_AIF_RX_BCLK_RATE 0x06
#define MADERA_AIF_FRAME_CTRL_1 0x07
#define MADERA_AIF_FRAME_CTRL_2 0x08
#define MADERA_AIF_FRAME_CTRL_3 0x09
#define MADERA_AIF_FRAME_CTRL_4 0x0A
#define MADERA_AIF_FRAME_CTRL_5 0x0B
#define MADERA_AIF_FRAME_CTRL_6 0x0C
#define MADERA_AIF_FRAME_CTRL_7 0x0D
#define MADERA_AIF_FRAME_CTRL_8 0x0E
#define MADERA_AIF_FRAME_CTRL_9 0x0F
#define MADERA_AIF_FRAME_CTRL_10 0x10
#define MADERA_AIF_FRAME_CTRL_11 0x11
#define MADERA_AIF_FRAME_CTRL_12 0x12
#define MADERA_AIF_FRAME_CTRL_13 0x13
#define MADERA_AIF_FRAME_CTRL_14 0x14
#define MADERA_AIF_FRAME_CTRL_15 0x15
#define MADERA_AIF_FRAME_CTRL_16 0x16
#define MADERA_AIF_FRAME_CTRL_17 0x17
#define MADERA_AIF_FRAME_CTRL_18 0x18
#define MADERA_AIF_TX_ENABLES 0x19
#define MADERA_AIF_RX_ENABLES 0x1A
#define MADERA_AIF_FORCE_WRITE 0x1B
#define MADERA_DSP_CONFIG_1_OFFS 0x00
#define MADERA_DSP_CONFIG_2_OFFS 0x02
#define MADERA_DSP_CLK_SEL_MASK 0x70000
#define MADERA_DSP_CLK_SEL_SHIFT 16
#define MADERA_DSP_RATE_MASK 0x7800
#define MADERA_DSP_RATE_SHIFT 11
#define MADERA_SYSCLK_6MHZ 0
#define MADERA_SYSCLK_12MHZ 1
#define MADERA_SYSCLK_24MHZ 2
#define MADERA_SYSCLK_49MHZ 3
#define MADERA_SYSCLK_98MHZ 4
#define MADERA_DSPCLK_9MHZ 0
#define MADERA_DSPCLK_18MHZ 1
#define MADERA_DSPCLK_36MHZ 2
#define MADERA_DSPCLK_73MHZ 3
#define MADERA_DSPCLK_147MHZ 4
#define MADERA_FLL_VCO_CORNER 141900000
#define MADERA_FLL_MAX_FREF 13500000
#define MADERA_FLL_MAX_N 1023
#define MADERA_FLL_MIN_FOUT 90000000
#define MADERA_FLL_MAX_FOUT 100000000
#define MADERA_FLL_MAX_FRATIO 16
#define MADERA_FLL_MAX_REFDIV 8
#define MADERA_FLL_OUTDIV 3
#define MADERA_FLL_VCO_MULT 3
#define MADERA_FLLAO_MAX_FREF 12288000
#define MADERA_FLLAO_MIN_N 4
#define MADERA_FLLAO_MAX_N 1023
#define MADERA_FLLAO_MAX_FBDIV 254
#define MADERA_FLLHJ_INT_MAX_N 1023
#define MADERA_FLLHJ_INT_MIN_N 1
#define MADERA_FLLHJ_FRAC_MAX_N 255
#define MADERA_FLLHJ_FRAC_MIN_N 4
#define MADERA_FLLHJ_LOW_THRESH 192000
#define MADERA_FLLHJ_MID_THRESH 1152000
#define MADERA_FLLHJ_MAX_THRESH 13000000
#define MADERA_FLLHJ_LOW_GAINS 0x23f0
#define MADERA_FLLHJ_MID_GAINS 0x22f2
#define MADERA_FLLHJ_HIGH_GAINS 0x21f0
#define MADERA_FLL_SYNCHRONISER_OFFS 0x10
#define CS47L35_FLL_SYNCHRONISER_OFFS 0xE
#define MADERA_FLL_CONTROL_1_OFFS 0x1
#define MADERA_FLL_CONTROL_2_OFFS 0x2
#define MADERA_FLL_CONTROL_3_OFFS 0x3
#define MADERA_FLL_CONTROL_4_OFFS 0x4
#define MADERA_FLL_CONTROL_5_OFFS 0x5
#define MADERA_FLL_CONTROL_6_OFFS 0x6
#define MADERA_FLL_GAIN_OFFS 0x8
#define MADERA_FLL_CONTROL_7_OFFS 0x9
#define MADERA_FLL_EFS_2_OFFS 0xA
#define MADERA_FLL_SYNCHRONISER_1_OFFS 0x1
#define MADERA_FLL_SYNCHRONISER_2_OFFS 0x2
#define MADERA_FLL_SYNCHRONISER_3_OFFS 0x3
#define MADERA_FLL_SYNCHRONISER_4_OFFS 0x4
#define MADERA_FLL_SYNCHRONISER_5_OFFS 0x5
#define MADERA_FLL_SYNCHRONISER_6_OFFS 0x6
#define MADERA_FLL_SYNCHRONISER_7_OFFS 0x7
#define MADERA_FLL_SPREAD_SPECTRUM_OFFS 0x9
#define MADERA_FLL_GPIO_CLOCK_OFFS 0xA
#define MADERA_FLL_CONTROL_10_OFFS 0xA
#define MADERA_FLL_CONTROL_11_OFFS 0xB
#define MADERA_FLL1_DIGITAL_TEST_1_OFFS 0xD
#define MADERA_FLLAO_CONTROL_1_OFFS 0x1
#define MADERA_FLLAO_CONTROL_2_OFFS 0x2
#define MADERA_FLLAO_CONTROL_3_OFFS 0x3
#define MADERA_FLLAO_CONTROL_4_OFFS 0x4
#define MADERA_FLLAO_CONTROL_5_OFFS 0x5
#define MADERA_FLLAO_CONTROL_6_OFFS 0x6
#define MADERA_FLLAO_CONTROL_7_OFFS 0x8
#define MADERA_FLLAO_CONTROL_8_OFFS 0xA
#define MADERA_FLLAO_CONTROL_9_OFFS 0xB
#define MADERA_FLLAO_CONTROL_10_OFFS 0xC
#define MADERA_FLLAO_CONTROL_11_OFFS 0xD
#define MADERA_FMT_DSP_MODE_A 0
#define MADERA_FMT_DSP_MODE_B 1
#define MADERA_FMT_I2S_MODE 2
#define MADERA_FMT_LEFT_JUSTIFIED_MODE 3
#define madera_fll_err(_fll, fmt, ...) \
dev_err(_fll->madera->dev, "FLL%d: " fmt, _fll->id, ##__VA_ARGS__)
#define madera_fll_warn(_fll, fmt, ...) \
dev_warn(_fll->madera->dev, "FLL%d: " fmt, _fll->id, ##__VA_ARGS__)
#define madera_fll_dbg(_fll, fmt, ...) \
dev_dbg(_fll->madera->dev, "FLL%d: " fmt, _fll->id, ##__VA_ARGS__)
#define madera_aif_err(_dai, fmt, ...) \
dev_err(_dai->dev, "AIF%d: " fmt, _dai->id, ##__VA_ARGS__)
#define madera_aif_warn(_dai, fmt, ...) \
dev_warn(_dai->dev, "AIF%d: " fmt, _dai->id, ##__VA_ARGS__)
#define madera_aif_dbg(_dai, fmt, ...) \
dev_dbg(_dai->dev, "AIF%d: " fmt, _dai->id, ##__VA_ARGS__)
static const int madera_dsp_bus_error_irqs[MADERA_MAX_ADSP] = {
MADERA_IRQ_DSP1_BUS_ERR,
MADERA_IRQ_DSP2_BUS_ERR,
MADERA_IRQ_DSP3_BUS_ERR,
MADERA_IRQ_DSP4_BUS_ERR,
MADERA_IRQ_DSP5_BUS_ERR,
MADERA_IRQ_DSP6_BUS_ERR,
MADERA_IRQ_DSP7_BUS_ERR,
};
int madera_clk_ev(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
struct madera_priv *priv = snd_soc_component_get_drvdata(component);
struct madera *madera = priv->madera;
unsigned int val;
int clk_idx;
int ret;
ret = regmap_read(madera->regmap, w->reg, &val);
if (ret) {
dev_err(madera->dev, "Failed to check clock source: %d\n", ret);
return ret;
}
switch ((val & MADERA_SYSCLK_SRC_MASK) >> MADERA_SYSCLK_SRC_SHIFT) {
case MADERA_CLK_SRC_MCLK1:
clk_idx = MADERA_MCLK1;
break;
case MADERA_CLK_SRC_MCLK2:
clk_idx = MADERA_MCLK2;
break;
case MADERA_CLK_SRC_MCLK3:
clk_idx = MADERA_MCLK3;
break;
default:
return 0;
}
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
return clk_prepare_enable(madera->mclk[clk_idx].clk);
case SND_SOC_DAPM_POST_PMD:
clk_disable_unprepare(madera->mclk[clk_idx].clk);
return 0;
default:
return 0;
}
}
EXPORT_SYMBOL_GPL(madera_clk_ev);
static void madera_spin_sysclk(struct madera_priv *priv)
{
struct madera *madera = priv->madera;
unsigned int val;
int ret, i;
/* Skip this if the chip is down */
if (pm_runtime_suspended(madera->dev))
return;
/*
* Just read a register a few times to ensure the internal
* oscillator sends out a few clocks.
*/
for (i = 0; i < 4; i++) {
ret = regmap_read(madera->regmap, MADERA_SOFTWARE_RESET, &val);
if (ret)
dev_err(madera->dev,
"Failed to read sysclk spin %d: %d\n", i, ret);
}
udelay(300);
}
int madera_sysclk_ev(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
struct madera_priv *priv = snd_soc_component_get_drvdata(component);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
case SND_SOC_DAPM_PRE_PMD:
madera_spin_sysclk(priv);
break;
default:
break;
}
return madera_clk_ev(w, kcontrol, event);
}
EXPORT_SYMBOL_GPL(madera_sysclk_ev);
static int madera_check_speaker_overheat(struct madera *madera,
bool *warn, bool *shutdown)
{
unsigned int val;
int ret;
ret = regmap_read(madera->regmap, MADERA_IRQ1_RAW_STATUS_15, &val);
if (ret) {
dev_err(madera->dev, "Failed to read thermal status: %d\n",
ret);
return ret;
}
*warn = val & MADERA_SPK_OVERHEAT_WARN_STS1;
*shutdown = val & MADERA_SPK_OVERHEAT_STS1;
return 0;
}
int madera_spk_ev(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
struct madera_priv *priv = snd_soc_component_get_drvdata(component);
struct madera *madera = priv->madera;
bool warn, shutdown;
int ret;
switch (event) {
case SND_SOC_DAPM_POST_PMU:
ret = madera_check_speaker_overheat(madera, &warn, &shutdown);
if (ret)
return ret;
if (shutdown) {
dev_crit(madera->dev,
"Speaker not enabled due to temperature\n");
return -EBUSY;
}
regmap_update_bits(madera->regmap, MADERA_OUTPUT_ENABLES_1,
1 << w->shift, 1 << w->shift);
break;
case SND_SOC_DAPM_PRE_PMD:
regmap_update_bits(madera->regmap, MADERA_OUTPUT_ENABLES_1,
1 << w->shift, 0);
break;
default:
break;
}
return 0;
}
EXPORT_SYMBOL_GPL(madera_spk_ev);
static irqreturn_t madera_thermal_warn(int irq, void *data)
{
struct madera *madera = data;
bool warn, shutdown;
int ret;
ret = madera_check_speaker_overheat(madera, &warn, &shutdown);
if (ret || shutdown) { /* for safety attempt to shutdown on error */
dev_crit(madera->dev, "Thermal shutdown\n");
ret = regmap_update_bits(madera->regmap,
MADERA_OUTPUT_ENABLES_1,
MADERA_OUT4L_ENA |
MADERA_OUT4R_ENA, 0);
if (ret != 0)
dev_crit(madera->dev,
"Failed to disable speaker outputs: %d\n",
ret);
} else if (warn) {
dev_alert(madera->dev, "Thermal warning\n");
} else {
dev_info(madera->dev, "Spurious thermal warning\n");
return IRQ_NONE;
}
return IRQ_HANDLED;
}
int madera_init_overheat(struct madera_priv *priv)
{
struct madera *madera = priv->madera;
struct device *dev = madera->dev;
int ret;
ret = madera_request_irq(madera, MADERA_IRQ_SPK_OVERHEAT_WARN,
"Thermal warning", madera_thermal_warn,
madera);
if (ret)
dev_err(dev, "Failed to get thermal warning IRQ: %d\n", ret);
ret = madera_request_irq(madera, MADERA_IRQ_SPK_OVERHEAT,
"Thermal shutdown", madera_thermal_warn,
madera);
if (ret)
dev_err(dev, "Failed to get thermal shutdown IRQ: %d\n", ret);
return 0;
}
EXPORT_SYMBOL_GPL(madera_init_overheat);
int madera_free_overheat(struct madera_priv *priv)
{
struct madera *madera = priv->madera;
madera_free_irq(madera, MADERA_IRQ_SPK_OVERHEAT_WARN, madera);
madera_free_irq(madera, MADERA_IRQ_SPK_OVERHEAT, madera);
return 0;
}
EXPORT_SYMBOL_GPL(madera_free_overheat);
static int madera_get_variable_u32_array(struct device *dev,
const char *propname,
u32 *dest, int n_max,
int multiple)
{
int n, ret;
n = device_property_count_u32(dev, propname);
if (n < 0) {
if (n == -EINVAL)
return 0; /* missing, ignore */
dev_warn(dev, "%s malformed (%d)\n", propname, n);
return n;
} else if ((n % multiple) != 0) {
dev_warn(dev, "%s not a multiple of %d entries\n",
propname, multiple);
return -EINVAL;
}
if (n > n_max)
n = n_max;
ret = device_property_read_u32_array(dev, propname, dest, n);
if (ret < 0)
return ret;
return n;
}
static void madera_prop_get_inmode(struct madera_priv *priv)
{
struct madera *madera = priv->madera;
struct madera_codec_pdata *pdata = &madera->pdata.codec;
u32 tmp[MADERA_MAX_INPUT * MADERA_MAX_MUXED_CHANNELS];
int n, i, in_idx, ch_idx;
BUILD_BUG_ON(ARRAY_SIZE(pdata->inmode) != MADERA_MAX_INPUT);
BUILD_BUG_ON(ARRAY_SIZE(pdata->inmode[0]) != MADERA_MAX_MUXED_CHANNELS);
n = madera_get_variable_u32_array(madera->dev, "cirrus,inmode",
tmp, ARRAY_SIZE(tmp),
MADERA_MAX_MUXED_CHANNELS);
if (n < 0)
return;
in_idx = 0;
ch_idx = 0;
for (i = 0; i < n; ++i) {
pdata->inmode[in_idx][ch_idx] = tmp[i];
if (++ch_idx == MADERA_MAX_MUXED_CHANNELS) {
ch_idx = 0;
++in_idx;
}
}
}
static void madera_prop_get_pdata(struct madera_priv *priv)
{
struct madera *madera = priv->madera;
struct madera_codec_pdata *pdata = &madera->pdata.codec;
u32 out_mono[ARRAY_SIZE(pdata->out_mono)];
int i, n;
madera_prop_get_inmode(priv);
n = madera_get_variable_u32_array(madera->dev, "cirrus,out-mono",
out_mono, ARRAY_SIZE(out_mono), 1);
if (n > 0)
for (i = 0; i < n; ++i)
pdata->out_mono[i] = !!out_mono[i];
madera_get_variable_u32_array(madera->dev,
"cirrus,max-channels-clocked",
pdata->max_channels_clocked,
ARRAY_SIZE(pdata->max_channels_clocked),
1);
madera_get_variable_u32_array(madera->dev, "cirrus,pdm-fmt",
pdata->pdm_fmt,
ARRAY_SIZE(pdata->pdm_fmt), 1);
madera_get_variable_u32_array(madera->dev, "cirrus,pdm-mute",
pdata->pdm_mute,
ARRAY_SIZE(pdata->pdm_mute), 1);
madera_get_variable_u32_array(madera->dev, "cirrus,dmic-ref",
pdata->dmic_ref,
ARRAY_SIZE(pdata->dmic_ref), 1);
}
int madera_core_init(struct madera_priv *priv)
{
int i;
/* trap undersized array initializers */
BUILD_BUG_ON(!madera_mixer_texts[MADERA_NUM_MIXER_INPUTS - 1]);
BUILD_BUG_ON(!madera_mixer_values[MADERA_NUM_MIXER_INPUTS - 1]);
if (!dev_get_platdata(priv->madera->dev))
madera_prop_get_pdata(priv);
mutex_init(&priv->rate_lock);
for (i = 0; i < MADERA_MAX_HP_OUTPUT; i++)
priv->madera->out_clamp[i] = true;
return 0;
}
EXPORT_SYMBOL_GPL(madera_core_init);
int madera_core_free(struct madera_priv *priv)
{
mutex_destroy(&priv->rate_lock);
return 0;
}
EXPORT_SYMBOL_GPL(madera_core_free);
static void madera_debug_dump_domain_groups(const struct madera_priv *priv)
{
struct madera *madera = priv->madera;
int i;
for (i = 0; i < ARRAY_SIZE(priv->domain_group_ref); ++i)
dev_dbg(madera->dev, "domain_grp_ref[%d]=%d\n", i,
priv->domain_group_ref[i]);
}
int madera_domain_clk_ev(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol,
int event)
{
struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
struct madera_priv *priv = snd_soc_component_get_drvdata(component);
int dom_grp = w->shift;
if (dom_grp >= ARRAY_SIZE(priv->domain_group_ref)) {
WARN(true, "%s dom_grp exceeds array size\n", __func__);
return -EINVAL;
}
/*
* We can't rely on the DAPM mutex for locking because we need a lock
* that can safely be called in hw_params
*/
mutex_lock(&priv->rate_lock);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
dev_dbg(priv->madera->dev, "Inc ref on domain group %d\n",
dom_grp);
++priv->domain_group_ref[dom_grp];
break;
case SND_SOC_DAPM_POST_PMD:
dev_dbg(priv->madera->dev, "Dec ref on domain group %d\n",
dom_grp);
--priv->domain_group_ref[dom_grp];
break;
default:
break;
}
madera_debug_dump_domain_groups(priv);
mutex_unlock(&priv->rate_lock);
return 0;
}
EXPORT_SYMBOL_GPL(madera_domain_clk_ev);
int madera_out1_demux_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component =
snd_soc_dapm_kcontrol_component(kcontrol);
struct snd_soc_dapm_context *dapm =
snd_soc_dapm_kcontrol_dapm(kcontrol);
struct madera_priv *priv = snd_soc_component_get_drvdata(component);
struct madera *madera = priv->madera;
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
unsigned int ep_sel, mux, change;
bool out_mono;
int ret;
if (ucontrol->value.enumerated.item[0] > e->items - 1)
return -EINVAL;
mux = ucontrol->value.enumerated.item[0];
snd_soc_dapm_mutex_lock(dapm);
ep_sel = mux << MADERA_EP_SEL_SHIFT;
change = snd_soc_component_test_bits(component, MADERA_OUTPUT_ENABLES_1,
MADERA_EP_SEL_MASK,
ep_sel);
if (!change)
goto end;
/* EP_SEL should not be modified while HP or EP driver is enabled */
ret = regmap_update_bits(madera->regmap, MADERA_OUTPUT_ENABLES_1,
MADERA_OUT1L_ENA | MADERA_OUT1R_ENA, 0);
if (ret)
dev_warn(madera->dev, "Failed to disable outputs: %d\n", ret);
usleep_range(2000, 3000); /* wait for wseq to complete */
/* change demux setting */
ret = 0;
if (madera->out_clamp[0])
ret = regmap_update_bits(madera->regmap,
MADERA_OUTPUT_ENABLES_1,
MADERA_EP_SEL_MASK, ep_sel);
if (ret) {
dev_err(madera->dev, "Failed to set OUT1 demux: %d\n", ret);
} else {
/* apply correct setting for mono mode */
if (!ep_sel && !madera->pdata.codec.out_mono[0])
out_mono = false; /* stereo HP */
else
out_mono = true; /* EP or mono HP */
ret = madera_set_output_mode(component, 1, out_mono);
if (ret)
dev_warn(madera->dev,
"Failed to set output mode: %d\n", ret);
}
/*
* if HPDET has disabled the clamp while switching to HPOUT
* OUT1 should remain disabled
*/
if (ep_sel ||
(madera->out_clamp[0] && !madera->out_shorted[0])) {
ret = regmap_update_bits(madera->regmap,
MADERA_OUTPUT_ENABLES_1,
MADERA_OUT1L_ENA | MADERA_OUT1R_ENA,
madera->hp_ena);
if (ret)
dev_warn(madera->dev,
"Failed to restore earpiece outputs: %d\n",
ret);
else if (madera->hp_ena)
msleep(34); /* wait for enable wseq */
else
usleep_range(2000, 3000); /* wait for disable wseq */
}
end:
snd_soc_dapm_mutex_unlock(dapm);
return snd_soc_dapm_mux_update_power(dapm, kcontrol, mux, e, NULL);
}
EXPORT_SYMBOL_GPL(madera_out1_demux_put);
int madera_out1_demux_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component =
snd_soc_dapm_kcontrol_component(kcontrol);
unsigned int val;
val = snd_soc_component_read(component, MADERA_OUTPUT_ENABLES_1);
val &= MADERA_EP_SEL_MASK;
val >>= MADERA_EP_SEL_SHIFT;
ucontrol->value.enumerated.item[0] = val;
return 0;
}
EXPORT_SYMBOL_GPL(madera_out1_demux_get);
static int madera_inmux_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component =
snd_soc_dapm_kcontrol_component(kcontrol);
struct snd_soc_dapm_context *dapm =
snd_soc_dapm_kcontrol_dapm(kcontrol);
struct madera_priv *priv = snd_soc_component_get_drvdata(component);
struct madera *madera = priv->madera;
struct regmap *regmap = madera->regmap;
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
unsigned int mux, val, mask;
unsigned int inmode;
bool changed;
int ret;
mux = ucontrol->value.enumerated.item[0];
if (mux > 1)
return -EINVAL;
val = mux << e->shift_l;
mask = (e->mask << e->shift_l) | MADERA_IN1L_SRC_SE_MASK;
switch (e->reg) {
case MADERA_ADC_DIGITAL_VOLUME_1L:
inmode = madera->pdata.codec.inmode[0][2 * mux];
break;
case MADERA_ADC_DIGITAL_VOLUME_1R:
inmode = madera->pdata.codec.inmode[0][1 + (2 * mux)];
break;
case MADERA_ADC_DIGITAL_VOLUME_2L:
inmode = madera->pdata.codec.inmode[1][2 * mux];
break;
case MADERA_ADC_DIGITAL_VOLUME_2R:
inmode = madera->pdata.codec.inmode[1][1 + (2 * mux)];
break;
default:
return -EINVAL;
}
if (inmode & MADERA_INMODE_SE)
val |= 1 << MADERA_IN1L_SRC_SE_SHIFT;
dev_dbg(madera->dev, "mux=%u reg=0x%x inmode=0x%x mask=0x%x val=0x%x\n",
mux, e->reg, inmode, mask, val);
ret = regmap_update_bits_check(regmap, e->reg, mask, val, &changed);
if (ret < 0)
return ret;
if (changed)
return snd_soc_dapm_mux_update_power(dapm, kcontrol,
mux, e, NULL);
else
return 0;
}
static const char * const madera_inmux_texts[] = {
"A",
"B",
};
static SOC_ENUM_SINGLE_DECL(madera_in1muxl_enum,
MADERA_ADC_DIGITAL_VOLUME_1L,
MADERA_IN1L_SRC_SHIFT,
madera_inmux_texts);
static SOC_ENUM_SINGLE_DECL(madera_in1muxr_enum,
MADERA_ADC_DIGITAL_VOLUME_1R,
MADERA_IN1R_SRC_SHIFT,
madera_inmux_texts);
static SOC_ENUM_SINGLE_DECL(madera_in2muxl_enum,
MADERA_ADC_DIGITAL_VOLUME_2L,
MADERA_IN2L_SRC_SHIFT,
madera_inmux_texts);
static SOC_ENUM_SINGLE_DECL(madera_in2muxr_enum,
MADERA_ADC_DIGITAL_VOLUME_2R,
MADERA_IN2R_SRC_SHIFT,
madera_inmux_texts);
const struct snd_kcontrol_new madera_inmux[] = {
SOC_DAPM_ENUM_EXT("IN1L Mux", madera_in1muxl_enum,
snd_soc_dapm_get_enum_double, madera_inmux_put),
SOC_DAPM_ENUM_EXT("IN1R Mux", madera_in1muxr_enum,
snd_soc_dapm_get_enum_double, madera_inmux_put),
SOC_DAPM_ENUM_EXT("IN2L Mux", madera_in2muxl_enum,
snd_soc_dapm_get_enum_double, madera_inmux_put),
SOC_DAPM_ENUM_EXT("IN2R Mux", madera_in2muxr_enum,
snd_soc_dapm_get_enum_double, madera_inmux_put),
};
EXPORT_SYMBOL_GPL(madera_inmux);
static const char * const madera_dmode_texts[] = {
"Analog",
"Digital",
};
static SOC_ENUM_SINGLE_DECL(madera_in1dmode_enum,
MADERA_IN1L_CONTROL,
MADERA_IN1_MODE_SHIFT,
madera_dmode_texts);
static SOC_ENUM_SINGLE_DECL(madera_in2dmode_enum,
MADERA_IN2L_CONTROL,
MADERA_IN2_MODE_SHIFT,
madera_dmode_texts);
static SOC_ENUM_SINGLE_DECL(madera_in3dmode_enum,
MADERA_IN3L_CONTROL,
MADERA_IN3_MODE_SHIFT,
madera_dmode_texts);
const struct snd_kcontrol_new madera_inmode[] = {
SOC_DAPM_ENUM("IN1 Mode", madera_in1dmode_enum),
SOC_DAPM_ENUM("IN2 Mode", madera_in2dmode_enum),
SOC_DAPM_ENUM("IN3 Mode", madera_in3dmode_enum),
};
EXPORT_SYMBOL_GPL(madera_inmode);
static bool madera_can_change_grp_rate(const struct madera_priv *priv,
unsigned int reg)
{
int count;
switch (reg) {
case MADERA_FX_CTRL1:
count = priv->domain_group_ref[MADERA_DOM_GRP_FX];
break;
case MADERA_ASRC1_RATE1:
case MADERA_ASRC1_RATE2:
count = priv->domain_group_ref[MADERA_DOM_GRP_ASRC1];
break;
case MADERA_ASRC2_RATE1:
case MADERA_ASRC2_RATE2:
count = priv->domain_group_ref[MADERA_DOM_GRP_ASRC2];
break;
case MADERA_ISRC_1_CTRL_1:
case MADERA_ISRC_1_CTRL_2:
count = priv->domain_group_ref[MADERA_DOM_GRP_ISRC1];
break;
case MADERA_ISRC_2_CTRL_1:
case MADERA_ISRC_2_CTRL_2:
count = priv->domain_group_ref[MADERA_DOM_GRP_ISRC2];
break;
case MADERA_ISRC_3_CTRL_1:
case MADERA_ISRC_3_CTRL_2:
count = priv->domain_group_ref[MADERA_DOM_GRP_ISRC3];
break;
case MADERA_ISRC_4_CTRL_1:
case MADERA_ISRC_4_CTRL_2:
count = priv->domain_group_ref[MADERA_DOM_GRP_ISRC4];
break;
case MADERA_OUTPUT_RATE_1:
count = priv->domain_group_ref[MADERA_DOM_GRP_OUT];
break;
case MADERA_SPD1_TX_CONTROL:
count = priv->domain_group_ref[MADERA_DOM_GRP_SPD];
break;
case MADERA_DSP1_CONFIG_1:
case MADERA_DSP1_CONFIG_2:
count = priv->domain_group_ref[MADERA_DOM_GRP_DSP1];
break;
case MADERA_DSP2_CONFIG_1:
case MADERA_DSP2_CONFIG_2:
count = priv->domain_group_ref[MADERA_DOM_GRP_DSP2];
break;
case MADERA_DSP3_CONFIG_1:
case MADERA_DSP3_CONFIG_2:
count = priv->domain_group_ref[MADERA_DOM_GRP_DSP3];
break;
case MADERA_DSP4_CONFIG_1:
case MADERA_DSP4_CONFIG_2:
count = priv->domain_group_ref[MADERA_DOM_GRP_DSP4];
break;
case MADERA_DSP5_CONFIG_1:
case MADERA_DSP5_CONFIG_2:
count = priv->domain_group_ref[MADERA_DOM_GRP_DSP5];
break;
case MADERA_DSP6_CONFIG_1:
case MADERA_DSP6_CONFIG_2:
count = priv->domain_group_ref[MADERA_DOM_GRP_DSP6];
break;
case MADERA_DSP7_CONFIG_1:
case MADERA_DSP7_CONFIG_2:
count = priv->domain_group_ref[MADERA_DOM_GRP_DSP7];
break;
case MADERA_AIF1_RATE_CTRL:
count = priv->domain_group_ref[MADERA_DOM_GRP_AIF1];
break;
case MADERA_AIF2_RATE_CTRL:
count = priv->domain_group_ref[MADERA_DOM_GRP_AIF2];
break;
case MADERA_AIF3_RATE_CTRL:
count = priv->domain_group_ref[MADERA_DOM_GRP_AIF3];
break;
case MADERA_AIF4_RATE_CTRL:
count = priv->domain_group_ref[MADERA_DOM_GRP_AIF4];
break;
case MADERA_SLIMBUS_RATES_1:
case MADERA_SLIMBUS_RATES_2:
case MADERA_SLIMBUS_RATES_3:
case MADERA_SLIMBUS_RATES_4:
case MADERA_SLIMBUS_RATES_5:
case MADERA_SLIMBUS_RATES_6:
case MADERA_SLIMBUS_RATES_7:
case MADERA_SLIMBUS_RATES_8:
count = priv->domain_group_ref[MADERA_DOM_GRP_SLIMBUS];
break;
case MADERA_PWM_DRIVE_1:
count = priv->domain_group_ref[MADERA_DOM_GRP_PWM];
break;
default:
return false;
}
dev_dbg(priv->madera->dev, "Rate reg 0x%x group ref %d\n", reg, count);
if (count)
return false;
else
return true;
}
static int madera_adsp_rate_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component =
snd_soc_kcontrol_component(kcontrol);
struct madera_priv *priv = snd_soc_component_get_drvdata(component);
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
unsigned int cached_rate;
const int adsp_num = e->shift_l;
int item;
mutex_lock(&priv->rate_lock);
cached_rate = priv->adsp_rate_cache[adsp_num];
mutex_unlock(&priv->rate_lock);
item = snd_soc_enum_val_to_item(e, cached_rate);
ucontrol->value.enumerated.item[0] = item;
return 0;
}
static int madera_adsp_rate_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component =
snd_soc_kcontrol_component(kcontrol);
struct madera_priv *priv = snd_soc_component_get_drvdata(component);
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
const int adsp_num = e->shift_l;
const unsigned int item = ucontrol->value.enumerated.item[0];
int ret;
if (item >= e->items)
return -EINVAL;
/*
* We don't directly write the rate register here but we want to
* maintain consistent behaviour that rate domains cannot be changed
* while in use since this is a hardware requirement
*/
mutex_lock(&priv->rate_lock);
if (!madera_can_change_grp_rate(priv, priv->adsp[adsp_num].cs_dsp.base)) {
dev_warn(priv->madera->dev,
"Cannot change '%s' while in use by active audio paths\n",
kcontrol->id.name);
ret = -EBUSY;
} else {
/* Volatile register so defer until the codec is powered up */
priv->adsp_rate_cache[adsp_num] = e->values[item];
ret = 0;
}
mutex_unlock(&priv->rate_lock);
return ret;
}
static const struct soc_enum madera_adsp_rate_enum[] = {
SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0, 0xf, MADERA_RATE_ENUM_SIZE,
madera_rate_text, madera_rate_val),
SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 1, 0xf, MADERA_RATE_ENUM_SIZE,
madera_rate_text, madera_rate_val),
SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 2, 0xf, MADERA_RATE_ENUM_SIZE,
madera_rate_text, madera_rate_val),
SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 3, 0xf, MADERA_RATE_ENUM_SIZE,
madera_rate_text, madera_rate_val),
SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 4, 0xf, MADERA_RATE_ENUM_SIZE,
madera_rate_text, madera_rate_val),
SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 5, 0xf, MADERA_RATE_ENUM_SIZE,
madera_rate_text, madera_rate_val),
SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 6, 0xf, MADERA_RATE_ENUM_SIZE,
madera_rate_text, madera_rate_val),
};
const struct snd_kcontrol_new madera_adsp_rate_controls[] = {
SOC_ENUM_EXT("DSP1 Rate", madera_adsp_rate_enum[0],
madera_adsp_rate_get, madera_adsp_rate_put),
SOC_ENUM_EXT("DSP2 Rate", madera_adsp_rate_enum[1],
madera_adsp_rate_get, madera_adsp_rate_put),
SOC_ENUM_EXT("DSP3 Rate", madera_adsp_rate_enum[2],
madera_adsp_rate_get, madera_adsp_rate_put),
SOC_ENUM_EXT("DSP4 Rate", madera_adsp_rate_enum[3],
madera_adsp_rate_get, madera_adsp_rate_put),
SOC_ENUM_EXT("DSP5 Rate", madera_adsp_rate_enum[4],
madera_adsp_rate_get, madera_adsp_rate_put),
SOC_ENUM_EXT("DSP6 Rate", madera_adsp_rate_enum[5],
madera_adsp_rate_get, madera_adsp_rate_put),
SOC_ENUM_EXT("DSP7 Rate", madera_adsp_rate_enum[6],
madera_adsp_rate_get, madera_adsp_rate_put),
};
EXPORT_SYMBOL_GPL(madera_adsp_rate_controls);
static int madera_write_adsp_clk_setting(struct madera_priv *priv,
struct wm_adsp *dsp,
unsigned int freq)
{
unsigned int val;
unsigned int mask = MADERA_DSP_RATE_MASK;
int ret;
val = priv->adsp_rate_cache[dsp->cs_dsp.num - 1] << MADERA_DSP_RATE_SHIFT;
switch (priv->madera->type) {
case CS47L35:
case CS47L85:
case WM1840:
/* use legacy frequency registers */
mask |= MADERA_DSP_CLK_SEL_MASK;
val |= (freq << MADERA_DSP_CLK_SEL_SHIFT);
break;
default:
/* Configure exact dsp frequency */
dev_dbg(priv->madera->dev, "Set DSP frequency to 0x%x\n", freq);
ret = regmap_write(dsp->cs_dsp.regmap,
dsp->cs_dsp.base + MADERA_DSP_CONFIG_2_OFFS, freq);
if (ret)
goto err;
break;
}
ret = regmap_update_bits(dsp->cs_dsp.regmap,
dsp->cs_dsp.base + MADERA_DSP_CONFIG_1_OFFS,
mask, val);
if (ret)
goto err;
dev_dbg(priv->madera->dev, "Set DSP clocking to 0x%x\n", val);
return 0;
err:
dev_err(dsp->cs_dsp.dev, "Failed to set DSP%d clock: %d\n", dsp->cs_dsp.num, ret);
return ret;
}
int madera_set_adsp_clk(struct madera_priv *priv, int dsp_num,
unsigned int freq)
{
struct wm_adsp *dsp = &priv->adsp[dsp_num];
struct madera *madera = priv->madera;
unsigned int cur, new;
int ret;
/*
* This is called at a higher DAPM priority than the mux widgets so
* the muxes are still off at this point and it's safe to change
* the rate domain control.
* Also called at a lower DAPM priority than the domain group widgets
* so locking the reads of adsp_rate_cache is not necessary as we know
* changes are locked out by the domain_group_ref reference count.
*/
ret = regmap_read(dsp->cs_dsp.regmap, dsp->cs_dsp.base, &cur);
if (ret) {
dev_err(madera->dev,
"Failed to read current DSP rate: %d\n", ret);
return ret;
}
cur &= MADERA_DSP_RATE_MASK;
new = priv->adsp_rate_cache[dsp->cs_dsp.num - 1] << MADERA_DSP_RATE_SHIFT;
if (new == cur) {
dev_dbg(madera->dev, "DSP rate not changed\n");
return madera_write_adsp_clk_setting(priv, dsp, freq);
} else {
dev_dbg(madera->dev, "DSP rate changed\n");
/* The write must be guarded by a number of SYSCLK cycles */
madera_spin_sysclk(priv);
ret = madera_write_adsp_clk_setting(priv, dsp, freq);
madera_spin_sysclk(priv);
return ret;
}
}
EXPORT_SYMBOL_GPL(madera_set_adsp_clk);
int madera_rate_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component =
snd_soc_kcontrol_component(kcontrol);
struct madera_priv *priv = snd_soc_component_get_drvdata(component);
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
unsigned int item = ucontrol->value.enumerated.item[0];
unsigned int val;
int ret;
if (item >= e->items)
return -EINVAL;
/*
* Prevent the domain powering up while we're checking whether it's
* safe to change rate domain
*/
mutex_lock(&priv->rate_lock);
val = snd_soc_component_read(component, e->reg);
val >>= e->shift_l;
val &= e->mask;
if (snd_soc_enum_item_to_val(e, item) == val) {
ret = 0;
goto out;
}
if (!madera_can_change_grp_rate(priv, e->reg)) {
dev_warn(priv->madera->dev,
"Cannot change '%s' while in use by active audio paths\n",
kcontrol->id.name);
ret = -EBUSY;
} else {
/* The write must be guarded by a number of SYSCLK cycles */
madera_spin_sysclk(priv);
ret = snd_soc_put_enum_double(kcontrol, ucontrol);
madera_spin_sysclk(priv);
}
out:
mutex_unlock(&priv->rate_lock);
return ret;
}
EXPORT_SYMBOL_GPL(madera_rate_put);
static void madera_configure_input_mode(struct madera *madera)
{
unsigned int dig_mode, ana_mode_l, ana_mode_r;
int max_analogue_inputs, max_dmic_sup, i;
switch (madera->type) {
case CS47L15:
max_analogue_inputs = 1;
max_dmic_sup = 2;
break;
case CS47L35:
max_analogue_inputs = 2;
max_dmic_sup = 2;
break;
case CS47L85:
case WM1840:
max_analogue_inputs = 3;
max_dmic_sup = 3;
break;
case CS47L90:
case CS47L91:
max_analogue_inputs = 2;
max_dmic_sup = 2;
break;
default:
max_analogue_inputs = 2;
max_dmic_sup = 4;
break;
}
/*
* Initialize input modes from the A settings. For muxed inputs the
* B settings will be applied if the mux is changed
*/
for (i = 0; i < max_dmic_sup; i++) {
dev_dbg(madera->dev, "IN%d mode %u:%u:%u:%u\n", i + 1,
madera->pdata.codec.inmode[i][0],
madera->pdata.codec.inmode[i][1],
madera->pdata.codec.inmode[i][2],
madera->pdata.codec.inmode[i][3]);
dig_mode = madera->pdata.codec.dmic_ref[i] <<
MADERA_IN1_DMIC_SUP_SHIFT;
switch (madera->pdata.codec.inmode[i][0]) {
case MADERA_INMODE_DIFF:
ana_mode_l = 0;
break;
case MADERA_INMODE_SE:
ana_mode_l = 1 << MADERA_IN1L_SRC_SE_SHIFT;
break;
default:
dev_warn(madera->dev,
"IN%dAL Illegal inmode %u ignored\n",
i + 1, madera->pdata.codec.inmode[i][0]);
continue;
}
switch (madera->pdata.codec.inmode[i][1]) {
case MADERA_INMODE_DIFF:
ana_mode_r = 0;
break;
case MADERA_INMODE_SE:
ana_mode_r = 1 << MADERA_IN1R_SRC_SE_SHIFT;
break;
default:
dev_warn(madera->dev,
"IN%dAR Illegal inmode %u ignored\n",
i + 1, madera->pdata.codec.inmode[i][1]);
continue;
}
dev_dbg(madera->dev,
"IN%dA DMIC mode=0x%x Analogue mode=0x%x,0x%x\n",
i + 1, dig_mode, ana_mode_l, ana_mode_r);
regmap_update_bits(madera->regmap,
MADERA_IN1L_CONTROL + (i * 8),
MADERA_IN1_DMIC_SUP_MASK, dig_mode);
if (i >= max_analogue_inputs)
continue;
regmap_update_bits(madera->regmap,
MADERA_ADC_DIGITAL_VOLUME_1L + (i * 8),
MADERA_IN1L_SRC_SE_MASK, ana_mode_l);
regmap_update_bits(madera->regmap,
MADERA_ADC_DIGITAL_VOLUME_1R + (i * 8),
MADERA_IN1R_SRC_SE_MASK, ana_mode_r);
}
}
int madera_init_inputs(struct snd_soc_component *component)
{
struct madera_priv *priv = snd_soc_component_get_drvdata(component);
struct madera *madera = priv->madera;
madera_configure_input_mode(madera);
return 0;
}
EXPORT_SYMBOL_GPL(madera_init_inputs);
static const struct snd_soc_dapm_route madera_mono_routes[] = {
{ "OUT1R", NULL, "OUT1L" },
{ "OUT2R", NULL, "OUT2L" },
{ "OUT3R", NULL, "OUT3L" },
{ "OUT4R", NULL, "OUT4L" },
{ "OUT5R", NULL, "OUT5L" },
{ "OUT6R", NULL, "OUT6L" },
};
int madera_init_outputs(struct snd_soc_component *component,
const struct snd_soc_dapm_route *routes,
int n_mono_routes, int n_real)
{
struct snd_soc_dapm_context *dapm =
snd_soc_component_get_dapm(component);
struct madera_priv *priv = snd_soc_component_get_drvdata(component);
struct madera *madera = priv->madera;
const struct madera_codec_pdata *pdata = &madera->pdata.codec;
unsigned int val;
int i;
if (n_mono_routes > MADERA_MAX_OUTPUT) {
dev_warn(madera->dev,
"Requested %d mono outputs, using maximum allowed %d\n",
n_mono_routes, MADERA_MAX_OUTPUT);
n_mono_routes = MADERA_MAX_OUTPUT;
}
if (!routes)
routes = madera_mono_routes;
for (i = 0; i < n_mono_routes; i++) {
/* Default is 0 so noop with defaults */
if (pdata->out_mono[i]) {
val = MADERA_OUT1_MONO;
snd_soc_dapm_add_routes(dapm, &routes[i], 1);
} else {
val = 0;
}
if (i >= n_real)
continue;
regmap_update_bits(madera->regmap,
MADERA_OUTPUT_PATH_CONFIG_1L + (i * 8),
MADERA_OUT1_MONO, val);
dev_dbg(madera->dev, "OUT%d mono=0x%x\n", i + 1, val);
}
for (i = 0; i < MADERA_MAX_PDM_SPK; i++) {
dev_dbg(madera->dev, "PDM%d fmt=0x%x mute=0x%x\n", i + 1,
pdata->pdm_fmt[i], pdata->pdm_mute[i]);
if (pdata->pdm_mute[i])
regmap_update_bits(madera->regmap,
MADERA_PDM_SPK1_CTRL_1 + (i * 2),
MADERA_SPK1_MUTE_ENDIAN_MASK |
MADERA_SPK1_MUTE_SEQ1_MASK,
pdata->pdm_mute[i]);
if (pdata->pdm_fmt[i])
regmap_update_bits(madera->regmap,
MADERA_PDM_SPK1_CTRL_2 + (i * 2),
MADERA_SPK1_FMT_MASK,
pdata->pdm_fmt[i]);
}
return 0;
}
EXPORT_SYMBOL_GPL(madera_init_outputs);
int madera_init_bus_error_irq(struct madera_priv *priv, int dsp_num,
irq_handler_t handler)
{
struct madera *madera = priv->madera;
int ret;
ret = madera_request_irq(madera,
madera_dsp_bus_error_irqs[dsp_num],
"ADSP2 bus error",
handler,
&priv->adsp[dsp_num]);
if (ret)
dev_err(madera->dev,
"Failed to request DSP Lock region IRQ: %d\n", ret);
return ret;
}
EXPORT_SYMBOL_GPL(madera_init_bus_error_irq);
void madera_free_bus_error_irq(struct madera_priv *priv, int dsp_num)
{
struct madera *madera = priv->madera;
madera_free_irq(madera,
madera_dsp_bus_error_irqs[dsp_num],
&priv->adsp[dsp_num]);
}
EXPORT_SYMBOL_GPL(madera_free_bus_error_irq);
const char * const madera_mixer_texts[] = {
"None",
"Tone Generator 1",
"Tone Generator 2",
"Haptics",
"AEC1",
"AEC2",
"Mic Mute Mixer",
"Noise Generator",
"IN1L",
"IN1R",
"IN2L",
"IN2R",
"IN3L",
"IN3R",
"IN4L",
"IN4R",
"IN5L",
"IN5R",
"IN6L",
"IN6R",
"AIF1RX1",
"AIF1RX2",
"AIF1RX3",
"AIF1RX4",
"AIF1RX5",
"AIF1RX6",
"AIF1RX7",
"AIF1RX8",
"AIF2RX1",
"AIF2RX2",
"AIF2RX3",
"AIF2RX4",
"AIF2RX5",
"AIF2RX6",
"AIF2RX7",
"AIF2RX8",
"AIF3RX1",
"AIF3RX2",
"AIF3RX3",
"AIF3RX4",
"AIF4RX1",
"AIF4RX2",
"SLIMRX1",
"SLIMRX2",
"SLIMRX3",
"SLIMRX4",
"SLIMRX5",
"SLIMRX6",
"SLIMRX7",
"SLIMRX8",
"EQ1",
"EQ2",
"EQ3",
"EQ4",
"DRC1L",
"DRC1R",
"DRC2L",
"DRC2R",
"LHPF1",
"LHPF2",
"LHPF3",
"LHPF4",
"DSP1.1",
"DSP1.2",
"DSP1.3",
"DSP1.4",
"DSP1.5",
"DSP1.6",
"DSP2.1",
"DSP2.2",
"DSP2.3",
"DSP2.4",
"DSP2.5",
"DSP2.6",
"DSP3.1",
"DSP3.2",
"DSP3.3",
"DSP3.4",
"DSP3.5",
"DSP3.6",
"DSP4.1",
"DSP4.2",
"DSP4.3",
"DSP4.4",
"DSP4.5",
"DSP4.6",
"DSP5.1",
"DSP5.2",
"DSP5.3",
"DSP5.4",
"DSP5.5",
"DSP5.6",
"DSP6.1",
"DSP6.2",
"DSP6.3",
"DSP6.4",
"DSP6.5",
"DSP6.6",
"DSP7.1",
"DSP7.2",
"DSP7.3",
"DSP7.4",
"DSP7.5",
"DSP7.6",
"ASRC1IN1L",
"ASRC1IN1R",
"ASRC1IN2L",
"ASRC1IN2R",
"ASRC2IN1L",
"ASRC2IN1R",
"ASRC2IN2L",
"ASRC2IN2R",
"ISRC1INT1",
"ISRC1INT2",
"ISRC1INT3",
"ISRC1INT4",
"ISRC1DEC1",
"ISRC1DEC2",
"ISRC1DEC3",
"ISRC1DEC4",
"ISRC2INT1",
"ISRC2INT2",
"ISRC2INT3",
"ISRC2INT4",
"ISRC2DEC1",
"ISRC2DEC2",
"ISRC2DEC3",
"ISRC2DEC4",
"ISRC3INT1",
"ISRC3INT2",
"ISRC3INT3",
"ISRC3INT4",
"ISRC3DEC1",
"ISRC3DEC2",
"ISRC3DEC3",
"ISRC3DEC4",
"ISRC4INT1",
"ISRC4INT2",
"ISRC4DEC1",
"ISRC4DEC2",
"DFC1",
"DFC2",
"DFC3",
"DFC4",
"DFC5",
"DFC6",
"DFC7",
"DFC8",
};
EXPORT_SYMBOL_GPL(madera_mixer_texts);
const unsigned int madera_mixer_values[] = {
0x00, /* None */
0x04, /* Tone Generator 1 */
0x05, /* Tone Generator 2 */
0x06, /* Haptics */
0x08, /* AEC */
0x09, /* AEC2 */
0x0c, /* Noise mixer */
0x0d, /* Comfort noise */
0x10, /* IN1L */
0x11,
0x12,
0x13,
0x14,
0x15,
0x16,
0x17,
0x18,
0x19,
0x1A,
0x1B,
0x20, /* AIF1RX1 */
0x21,
0x22,
0x23,
0x24,
0x25,
0x26,
0x27,
0x28, /* AIF2RX1 */
0x29,
0x2a,
0x2b,
0x2c,
0x2d,
0x2e,
0x2f,
0x30, /* AIF3RX1 */
0x31,
0x32,
0x33,
0x34, /* AIF4RX1 */
0x35,
0x38, /* SLIMRX1 */
0x39,
0x3a,
0x3b,
0x3c,
0x3d,
0x3e,
0x3f,
0x50, /* EQ1 */
0x51,
0x52,
0x53,
0x58, /* DRC1L */
0x59,
0x5a,
0x5b,
0x60, /* LHPF1 */
0x61,
0x62,
0x63,
0x68, /* DSP1.1 */
0x69,
0x6a,
0x6b,
0x6c,
0x6d,
0x70, /* DSP2.1 */
0x71,
0x72,
0x73,
0x74,
0x75,
0x78, /* DSP3.1 */
0x79,
0x7a,
0x7b,
0x7c,
0x7d,
0x80, /* DSP4.1 */
0x81,
0x82,
0x83,
0x84,
0x85,
0x88, /* DSP5.1 */
0x89,
0x8a,
0x8b,
0x8c,
0x8d,
0xc0, /* DSP6.1 */
0xc1,
0xc2,
0xc3,
0xc4,
0xc5,
0xc8, /* DSP7.1 */
0xc9,
0xca,
0xcb,
0xcc,
0xcd,
0x90, /* ASRC1IN1L */
0x91,
0x92,
0x93,
0x94, /* ASRC2IN1L */
0x95,
0x96,
0x97,
0xa0, /* ISRC1INT1 */
0xa1,
0xa2,
0xa3,
0xa4, /* ISRC1DEC1 */
0xa5,
0xa6,
0xa7,
0xa8, /* ISRC2DEC1 */
0xa9,
0xaa,
0xab,
0xac, /* ISRC2INT1 */
0xad,
0xae,
0xaf,
0xb0, /* ISRC3DEC1 */
0xb1,
0xb2,
0xb3,
0xb4, /* ISRC3INT1 */
0xb5,
0xb6,
0xb7,
0xb8, /* ISRC4INT1 */
0xb9,
0xbc, /* ISRC4DEC1 */
0xbd,
0xf8, /* DFC1 */
0xf9,
0xfa,
0xfb,
0xfc,
0xfd,
0xfe,
0xff, /* DFC8 */
};
EXPORT_SYMBOL_GPL(madera_mixer_values);
const DECLARE_TLV_DB_SCALE(madera_ana_tlv, 0, 100, 0);
EXPORT_SYMBOL_GPL(madera_ana_tlv);
const DECLARE_TLV_DB_SCALE(madera_eq_tlv, -1200, 100, 0);
EXPORT_SYMBOL_GPL(madera_eq_tlv);
const DECLARE_TLV_DB_SCALE(madera_digital_tlv, -6400, 50, 0);
EXPORT_SYMBOL_GPL(madera_digital_tlv);
const DECLARE_TLV_DB_SCALE(madera_noise_tlv, -13200, 600, 0);
EXPORT_SYMBOL_GPL(madera_noise_tlv);
const DECLARE_TLV_DB_SCALE(madera_ng_tlv, -12000, 600, 0);
EXPORT_SYMBOL_GPL(madera_ng_tlv);
const DECLARE_TLV_DB_SCALE(madera_mixer_tlv, -3200, 100, 0);
EXPORT_SYMBOL_GPL(madera_mixer_tlv);
const char * const madera_rate_text[MADERA_RATE_ENUM_SIZE] = {
"SYNCCLK rate 1", "SYNCCLK rate 2", "SYNCCLK rate 3",
"ASYNCCLK rate 1", "ASYNCCLK rate 2",
};
EXPORT_SYMBOL_GPL(madera_rate_text);
const unsigned int madera_rate_val[MADERA_RATE_ENUM_SIZE] = {
0x0, 0x1, 0x2, 0x8, 0x9,
};
EXPORT_SYMBOL_GPL(madera_rate_val);
static const char * const madera_dfc_width_text[MADERA_DFC_WIDTH_ENUM_SIZE] = {
"8 bit", "16 bit", "20 bit", "24 bit", "32 bit",
};
static const unsigned int madera_dfc_width_val[MADERA_DFC_WIDTH_ENUM_SIZE] = {
7, 15, 19, 23, 31,
};
static const char * const madera_dfc_type_text[MADERA_DFC_TYPE_ENUM_SIZE] = {
"Fixed", "Unsigned Fixed", "Single Precision Floating",
"Half Precision Floating", "Arm Alternative Floating",
};
static const unsigned int madera_dfc_type_val[MADERA_DFC_TYPE_ENUM_SIZE] = {
0, 1, 2, 4, 5,
};
const struct soc_enum madera_dfc_width[] = {
SOC_VALUE_ENUM_SINGLE(MADERA_DFC1_RX,
MADERA_DFC1_RX_DATA_WIDTH_SHIFT,
MADERA_DFC1_RX_DATA_WIDTH_MASK >>
MADERA_DFC1_RX_DATA_WIDTH_SHIFT,
ARRAY_SIZE(madera_dfc_width_text),
madera_dfc_width_text,
madera_dfc_width_val),
SOC_VALUE_ENUM_SINGLE(MADERA_DFC1_TX,
MADERA_DFC1_TX_DATA_WIDTH_SHIFT,
MADERA_DFC1_TX_DATA_WIDTH_MASK >>
MADERA_DFC1_TX_DATA_WIDTH_SHIFT,
ARRAY_SIZE(madera_dfc_width_text),
madera_dfc_width_text,
madera_dfc_width_val),
SOC_VALUE_ENUM_SINGLE(MADERA_DFC2_RX,
MADERA_DFC1_RX_DATA_WIDTH_SHIFT,
MADERA_DFC1_RX_DATA_WIDTH_MASK >>
MADERA_DFC1_RX_DATA_WIDTH_SHIFT,
ARRAY_SIZE(madera_dfc_width_text),
madera_dfc_width_text,
madera_dfc_width_val),
SOC_VALUE_ENUM_SINGLE(MADERA_DFC2_TX,
MADERA_DFC1_TX_DATA_WIDTH_SHIFT,
MADERA_DFC1_TX_DATA_WIDTH_MASK >>
MADERA_DFC1_TX_DATA_WIDTH_SHIFT,
ARRAY_SIZE(madera_dfc_width_text),
madera_dfc_width_text,
madera_dfc_width_val),
SOC_VALUE_ENUM_SINGLE(MADERA_DFC3_RX,
MADERA_DFC1_RX_DATA_WIDTH_SHIFT,
MADERA_DFC1_RX_DATA_WIDTH_MASK >>
MADERA_DFC1_RX_DATA_WIDTH_SHIFT,
ARRAY_SIZE(madera_dfc_width_text),
madera_dfc_width_text,
madera_dfc_width_val),
SOC_VALUE_ENUM_SINGLE(MADERA_DFC3_TX,
MADERA_DFC1_TX_DATA_WIDTH_SHIFT,
MADERA_DFC1_TX_DATA_WIDTH_MASK >>
MADERA_DFC1_TX_DATA_WIDTH_SHIFT,
ARRAY_SIZE(madera_dfc_width_text),
madera_dfc_width_text,
madera_dfc_width_val),
SOC_VALUE_ENUM_SINGLE(MADERA_DFC4_RX,
MADERA_DFC1_RX_DATA_WIDTH_SHIFT,
MADERA_DFC1_RX_DATA_WIDTH_MASK >>
MADERA_DFC1_RX_DATA_WIDTH_SHIFT,
ARRAY_SIZE(madera_dfc_width_text),
madera_dfc_width_text,
madera_dfc_width_val),
SOC_VALUE_ENUM_SINGLE(MADERA_DFC4_TX,
MADERA_DFC1_TX_DATA_WIDTH_SHIFT,
MADERA_DFC1_TX_DATA_WIDTH_MASK >>
MADERA_DFC1_TX_DATA_WIDTH_SHIFT,
ARRAY_SIZE(madera_dfc_width_text),
madera_dfc_width_text,
madera_dfc_width_val),
SOC_VALUE_ENUM_SINGLE(MADERA_DFC5_RX,
MADERA_DFC1_RX_DATA_WIDTH_SHIFT,
MADERA_DFC1_RX_DATA_WIDTH_MASK >>
MADERA_DFC1_RX_DATA_WIDTH_SHIFT,
ARRAY_SIZE(madera_dfc_width_text),
madera_dfc_width_text,
madera_dfc_width_val),
SOC_VALUE_ENUM_SINGLE(MADERA_DFC5_TX,
MADERA_DFC1_TX_DATA_WIDTH_SHIFT,
MADERA_DFC1_TX_DATA_WIDTH_MASK >>
MADERA_DFC1_TX_DATA_WIDTH_SHIFT,
ARRAY_SIZE(madera_dfc_width_text),
madera_dfc_width_text,
madera_dfc_width_val),
SOC_VALUE_ENUM_SINGLE(MADERA_DFC6_RX,
MADERA_DFC1_RX_DATA_WIDTH_SHIFT,
MADERA_DFC1_RX_DATA_WIDTH_MASK >>
MADERA_DFC1_RX_DATA_WIDTH_SHIFT,
ARRAY_SIZE(madera_dfc_width_text),
madera_dfc_width_text,
madera_dfc_width_val),
SOC_VALUE_ENUM_SINGLE(MADERA_DFC6_TX,
MADERA_DFC1_TX_DATA_WIDTH_SHIFT,
MADERA_DFC1_TX_DATA_WIDTH_MASK >>
MADERA_DFC1_TX_DATA_WIDTH_SHIFT,
ARRAY_SIZE(madera_dfc_width_text),
madera_dfc_width_text,
madera_dfc_width_val),
SOC_VALUE_ENUM_SINGLE(MADERA_DFC7_RX,
MADERA_DFC1_RX_DATA_WIDTH_SHIFT,
MADERA_DFC1_RX_DATA_WIDTH_MASK >>
MADERA_DFC1_RX_DATA_WIDTH_SHIFT,
ARRAY_SIZE(madera_dfc_width_text),
madera_dfc_width_text,
madera_dfc_width_val),
SOC_VALUE_ENUM_SINGLE(MADERA_DFC7_TX,
MADERA_DFC1_TX_DATA_WIDTH_SHIFT,
MADERA_DFC1_TX_DATA_WIDTH_MASK >>
MADERA_DFC1_TX_DATA_WIDTH_SHIFT,
ARRAY_SIZE(madera_dfc_width_text),
madera_dfc_width_text,
madera_dfc_width_val),
SOC_VALUE_ENUM_SINGLE(MADERA_DFC8_RX,
MADERA_DFC1_RX_DATA_WIDTH_SHIFT,
MADERA_DFC1_RX_DATA_WIDTH_MASK >>
MADERA_DFC1_RX_DATA_WIDTH_SHIFT,
ARRAY_SIZE(madera_dfc_width_text),
madera_dfc_width_text,
madera_dfc_width_val),
SOC_VALUE_ENUM_SINGLE(MADERA_DFC8_TX,
MADERA_DFC1_TX_DATA_WIDTH_SHIFT,
MADERA_DFC1_TX_DATA_WIDTH_MASK >>
MADERA_DFC1_TX_DATA_WIDTH_SHIFT,
ARRAY_SIZE(madera_dfc_width_text),
madera_dfc_width_text,
madera_dfc_width_val),
};
EXPORT_SYMBOL_GPL(madera_dfc_width);
const struct soc_enum madera_dfc_type[] = {
SOC_VALUE_ENUM_SINGLE(MADERA_DFC1_RX,
MADERA_DFC1_RX_DATA_TYPE_SHIFT,
MADERA_DFC1_RX_DATA_TYPE_MASK >>
MADERA_DFC1_RX_DATA_TYPE_SHIFT,
ARRAY_SIZE(madera_dfc_type_text),
madera_dfc_type_text,
madera_dfc_type_val),
SOC_VALUE_ENUM_SINGLE(MADERA_DFC1_TX,
MADERA_DFC1_TX_DATA_TYPE_SHIFT,
MADERA_DFC1_TX_DATA_TYPE_MASK >>
MADERA_DFC1_TX_DATA_TYPE_SHIFT,
ARRAY_SIZE(madera_dfc_type_text),
madera_dfc_type_text,
madera_dfc_type_val),
SOC_VALUE_ENUM_SINGLE(MADERA_DFC2_RX,
MADERA_DFC1_RX_DATA_TYPE_SHIFT,
MADERA_DFC1_RX_DATA_TYPE_MASK >>
MADERA_DFC1_RX_DATA_TYPE_SHIFT,
ARRAY_SIZE(madera_dfc_type_text),
madera_dfc_type_text,
madera_dfc_type_val),
SOC_VALUE_ENUM_SINGLE(MADERA_DFC2_TX,
MADERA_DFC1_TX_DATA_TYPE_SHIFT,
MADERA_DFC1_TX_DATA_TYPE_MASK >>
MADERA_DFC1_TX_DATA_TYPE_SHIFT,
ARRAY_SIZE(madera_dfc_type_text),
madera_dfc_type_text,
madera_dfc_type_val),
SOC_VALUE_ENUM_SINGLE(MADERA_DFC3_RX,
MADERA_DFC1_RX_DATA_TYPE_SHIFT,
MADERA_DFC1_RX_DATA_TYPE_MASK >>
MADERA_DFC1_RX_DATA_TYPE_SHIFT,
ARRAY_SIZE(madera_dfc_type_text),
madera_dfc_type_text,
madera_dfc_type_val),
SOC_VALUE_ENUM_SINGLE(MADERA_DFC3_TX,
MADERA_DFC1_TX_DATA_TYPE_SHIFT,
MADERA_DFC1_TX_DATA_TYPE_MASK >>
MADERA_DFC1_TX_DATA_TYPE_SHIFT,
ARRAY_SIZE(madera_dfc_type_text),
madera_dfc_type_text,
madera_dfc_type_val),
SOC_VALUE_ENUM_SINGLE(MADERA_DFC4_RX,
MADERA_DFC1_RX_DATA_TYPE_SHIFT,
MADERA_DFC1_RX_DATA_TYPE_MASK >>
MADERA_DFC1_RX_DATA_TYPE_SHIFT,
ARRAY_SIZE(madera_dfc_type_text),
madera_dfc_type_text,
madera_dfc_type_val),
SOC_VALUE_ENUM_SINGLE(MADERA_DFC4_TX,
MADERA_DFC1_TX_DATA_TYPE_SHIFT,
MADERA_DFC1_TX_DATA_TYPE_MASK >>
MADERA_DFC1_TX_DATA_TYPE_SHIFT,
ARRAY_SIZE(madera_dfc_type_text),
madera_dfc_type_text,
madera_dfc_type_val),
SOC_VALUE_ENUM_SINGLE(MADERA_DFC5_RX,
MADERA_DFC1_RX_DATA_TYPE_SHIFT,
MADERA_DFC1_RX_DATA_TYPE_MASK >>
MADERA_DFC1_RX_DATA_TYPE_SHIFT,
ARRAY_SIZE(madera_dfc_type_text),
madera_dfc_type_text,
madera_dfc_type_val),
SOC_VALUE_ENUM_SINGLE(MADERA_DFC5_TX,
MADERA_DFC1_TX_DATA_TYPE_SHIFT,
MADERA_DFC1_TX_DATA_TYPE_MASK >>
MADERA_DFC1_TX_DATA_TYPE_SHIFT,
ARRAY_SIZE(madera_dfc_type_text),
madera_dfc_type_text,
madera_dfc_type_val),
SOC_VALUE_ENUM_SINGLE(MADERA_DFC6_RX,
MADERA_DFC1_RX_DATA_TYPE_SHIFT,
MADERA_DFC1_RX_DATA_TYPE_MASK >>
MADERA_DFC1_RX_DATA_TYPE_SHIFT,
ARRAY_SIZE(madera_dfc_type_text),
madera_dfc_type_text,
madera_dfc_type_val),
SOC_VALUE_ENUM_SINGLE(MADERA_DFC6_TX,
MADERA_DFC1_TX_DATA_TYPE_SHIFT,
MADERA_DFC1_TX_DATA_TYPE_MASK >>
MADERA_DFC1_TX_DATA_TYPE_SHIFT,
ARRAY_SIZE(madera_dfc_type_text),
madera_dfc_type_text,
madera_dfc_type_val),
SOC_VALUE_ENUM_SINGLE(MADERA_DFC7_RX,
MADERA_DFC1_RX_DATA_TYPE_SHIFT,
MADERA_DFC1_RX_DATA_TYPE_MASK >>
MADERA_DFC1_RX_DATA_TYPE_SHIFT,
ARRAY_SIZE(madera_dfc_type_text),
madera_dfc_type_text,
madera_dfc_type_val),
SOC_VALUE_ENUM_SINGLE(MADERA_DFC7_TX,
MADERA_DFC1_TX_DATA_TYPE_SHIFT,
MADERA_DFC1_TX_DATA_TYPE_MASK >>
MADERA_DFC1_TX_DATA_TYPE_SHIFT,
ARRAY_SIZE(madera_dfc_type_text),
madera_dfc_type_text,
madera_dfc_type_val),
SOC_VALUE_ENUM_SINGLE(MADERA_DFC8_RX,
MADERA_DFC1_RX_DATA_TYPE_SHIFT,
MADERA_DFC1_RX_DATA_TYPE_MASK >>
MADERA_DFC1_RX_DATA_TYPE_SHIFT,
ARRAY_SIZE(madera_dfc_type_text),
madera_dfc_type_text,
madera_dfc_type_val),
SOC_VALUE_ENUM_SINGLE(MADERA_DFC8_TX,
MADERA_DFC1_TX_DATA_TYPE_SHIFT,
MADERA_DFC1_TX_DATA_TYPE_MASK >>
MADERA_DFC1_TX_DATA_TYPE_SHIFT,
ARRAY_SIZE(madera_dfc_type_text),
madera_dfc_type_text,
madera_dfc_type_val),
};
EXPORT_SYMBOL_GPL(madera_dfc_type);
const struct soc_enum madera_isrc_fsh[] = {
SOC_VALUE_ENUM_SINGLE(MADERA_ISRC_1_CTRL_1,
MADERA_ISRC1_FSH_SHIFT, 0xf,
MADERA_RATE_ENUM_SIZE,
madera_rate_text, madera_rate_val),
SOC_VALUE_ENUM_SINGLE(MADERA_ISRC_2_CTRL_1,
MADERA_ISRC2_FSH_SHIFT, 0xf,
MADERA_RATE_ENUM_SIZE,
madera_rate_text, madera_rate_val),
SOC_VALUE_ENUM_SINGLE(MADERA_ISRC_3_CTRL_1,
MADERA_ISRC3_FSH_SHIFT, 0xf,
MADERA_RATE_ENUM_SIZE,
madera_rate_text, madera_rate_val),
SOC_VALUE_ENUM_SINGLE(MADERA_ISRC_4_CTRL_1,
MADERA_ISRC4_FSH_SHIFT, 0xf,
MADERA_RATE_ENUM_SIZE,
madera_rate_text, madera_rate_val),
};
EXPORT_SYMBOL_GPL(madera_isrc_fsh);
const struct soc_enum madera_isrc_fsl[] = {
SOC_VALUE_ENUM_SINGLE(MADERA_ISRC_1_CTRL_2,
MADERA_ISRC1_FSL_SHIFT, 0xf,
MADERA_RATE_ENUM_SIZE,
madera_rate_text, madera_rate_val),
SOC_VALUE_ENUM_SINGLE(MADERA_ISRC_2_CTRL_2,
MADERA_ISRC2_FSL_SHIFT, 0xf,
MADERA_RATE_ENUM_SIZE,
madera_rate_text, madera_rate_val),
SOC_VALUE_ENUM_SINGLE(MADERA_ISRC_3_CTRL_2,
MADERA_ISRC3_FSL_SHIFT, 0xf,
MADERA_RATE_ENUM_SIZE,
madera_rate_text, madera_rate_val),
SOC_VALUE_ENUM_SINGLE(MADERA_ISRC_4_CTRL_2,
MADERA_ISRC4_FSL_SHIFT, 0xf,
MADERA_RATE_ENUM_SIZE,
madera_rate_text, madera_rate_val),
};
EXPORT_SYMBOL_GPL(madera_isrc_fsl);
const struct soc_enum madera_asrc1_rate[] = {
SOC_VALUE_ENUM_SINGLE(MADERA_ASRC1_RATE1,
MADERA_ASRC1_RATE1_SHIFT, 0xf,
MADERA_SYNC_RATE_ENUM_SIZE,
madera_rate_text, madera_rate_val),
SOC_VALUE_ENUM_SINGLE(MADERA_ASRC1_RATE2,
MADERA_ASRC1_RATE1_SHIFT, 0xf,
MADERA_ASYNC_RATE_ENUM_SIZE,
madera_rate_text + MADERA_SYNC_RATE_ENUM_SIZE,
madera_rate_val + MADERA_SYNC_RATE_ENUM_SIZE),
};
EXPORT_SYMBOL_GPL(madera_asrc1_rate);
const struct soc_enum madera_asrc1_bidir_rate[] = {
SOC_VALUE_ENUM_SINGLE(MADERA_ASRC1_RATE1,
MADERA_ASRC1_RATE1_SHIFT, 0xf,
MADERA_RATE_ENUM_SIZE,
madera_rate_text, madera_rate_val),
SOC_VALUE_ENUM_SINGLE(MADERA_ASRC1_RATE2,
MADERA_ASRC1_RATE2_SHIFT, 0xf,
MADERA_RATE_ENUM_SIZE,
madera_rate_text, madera_rate_val),
};
EXPORT_SYMBOL_GPL(madera_asrc1_bidir_rate);
const struct soc_enum madera_asrc2_rate[] = {
SOC_VALUE_ENUM_SINGLE(MADERA_ASRC2_RATE1,
MADERA_ASRC2_RATE1_SHIFT, 0xf,
MADERA_SYNC_RATE_ENUM_SIZE,
madera_rate_text, madera_rate_val),
SOC_VALUE_ENUM_SINGLE(MADERA_ASRC2_RATE2,
MADERA_ASRC2_RATE2_SHIFT, 0xf,
MADERA_ASYNC_RATE_ENUM_SIZE,
madera_rate_text + MADERA_SYNC_RATE_ENUM_SIZE,
madera_rate_val + MADERA_SYNC_RATE_ENUM_SIZE),
};
EXPORT_SYMBOL_GPL(madera_asrc2_rate);
static const char * const madera_vol_ramp_text[] = {
"0ms/6dB", "0.5ms/6dB", "1ms/6dB", "2ms/6dB", "4ms/6dB", "8ms/6dB",
"15ms/6dB", "30ms/6dB",
};
SOC_ENUM_SINGLE_DECL(madera_in_vd_ramp,
MADERA_INPUT_VOLUME_RAMP,
MADERA_IN_VD_RAMP_SHIFT,
madera_vol_ramp_text);
EXPORT_SYMBOL_GPL(madera_in_vd_ramp);
SOC_ENUM_SINGLE_DECL(madera_in_vi_ramp,
MADERA_INPUT_VOLUME_RAMP,
MADERA_IN_VI_RAMP_SHIFT,
madera_vol_ramp_text);
EXPORT_SYMBOL_GPL(madera_in_vi_ramp);
SOC_ENUM_SINGLE_DECL(madera_out_vd_ramp,
MADERA_OUTPUT_VOLUME_RAMP,
MADERA_OUT_VD_RAMP_SHIFT,
madera_vol_ramp_text);
EXPORT_SYMBOL_GPL(madera_out_vd_ramp);
SOC_ENUM_SINGLE_DECL(madera_out_vi_ramp,
MADERA_OUTPUT_VOLUME_RAMP,
MADERA_OUT_VI_RAMP_SHIFT,
madera_vol_ramp_text);
EXPORT_SYMBOL_GPL(madera_out_vi_ramp);
static const char * const madera_lhpf_mode_text[] = {
"Low-pass", "High-pass"
};
SOC_ENUM_SINGLE_DECL(madera_lhpf1_mode,
MADERA_HPLPF1_1,
MADERA_LHPF1_MODE_SHIFT,
madera_lhpf_mode_text);
EXPORT_SYMBOL_GPL(madera_lhpf1_mode);
SOC_ENUM_SINGLE_DECL(madera_lhpf2_mode,
MADERA_HPLPF2_1,
MADERA_LHPF2_MODE_SHIFT,
madera_lhpf_mode_text);
EXPORT_SYMBOL_GPL(madera_lhpf2_mode);
SOC_ENUM_SINGLE_DECL(madera_lhpf3_mode,
MADERA_HPLPF3_1,
MADERA_LHPF3_MODE_SHIFT,
madera_lhpf_mode_text);
EXPORT_SYMBOL_GPL(madera_lhpf3_mode);
SOC_ENUM_SINGLE_DECL(madera_lhpf4_mode,
MADERA_HPLPF4_1,
MADERA_LHPF4_MODE_SHIFT,
madera_lhpf_mode_text);
EXPORT_SYMBOL_GPL(madera_lhpf4_mode);
static const char * const madera_ng_hold_text[] = {
"30ms", "120ms", "250ms", "500ms",
};
SOC_ENUM_SINGLE_DECL(madera_ng_hold,
MADERA_NOISE_GATE_CONTROL,
MADERA_NGATE_HOLD_SHIFT,
madera_ng_hold_text);
EXPORT_SYMBOL_GPL(madera_ng_hold);
static const char * const madera_in_hpf_cut_text[] = {
"2.5Hz", "5Hz", "10Hz", "20Hz", "40Hz"
};
SOC_ENUM_SINGLE_DECL(madera_in_hpf_cut_enum,
MADERA_HPF_CONTROL,
MADERA_IN_HPF_CUT_SHIFT,
madera_in_hpf_cut_text);
EXPORT_SYMBOL_GPL(madera_in_hpf_cut_enum);
static const char * const madera_in_dmic_osr_text[MADERA_OSR_ENUM_SIZE] = {
"384kHz", "768kHz", "1.536MHz", "3.072MHz", "6.144MHz",
};
static const unsigned int madera_in_dmic_osr_val[MADERA_OSR_ENUM_SIZE] = {
2, 3, 4, 5, 6,
};
const struct soc_enum madera_in_dmic_osr[] = {
SOC_VALUE_ENUM_SINGLE(MADERA_DMIC1L_CONTROL, MADERA_IN1_OSR_SHIFT,
0x7, MADERA_OSR_ENUM_SIZE,
madera_in_dmic_osr_text, madera_in_dmic_osr_val),
SOC_VALUE_ENUM_SINGLE(MADERA_DMIC2L_CONTROL, MADERA_IN2_OSR_SHIFT,
0x7, MADERA_OSR_ENUM_SIZE,
madera_in_dmic_osr_text, madera_in_dmic_osr_val),
SOC_VALUE_ENUM_SINGLE(MADERA_DMIC3L_CONTROL, MADERA_IN3_OSR_SHIFT,
0x7, MADERA_OSR_ENUM_SIZE,
madera_in_dmic_osr_text, madera_in_dmic_osr_val),
SOC_VALUE_ENUM_SINGLE(MADERA_DMIC4L_CONTROL, MADERA_IN4_OSR_SHIFT,
0x7, MADERA_OSR_ENUM_SIZE,
madera_in_dmic_osr_text, madera_in_dmic_osr_val),
SOC_VALUE_ENUM_SINGLE(MADERA_DMIC5L_CONTROL, MADERA_IN5_OSR_SHIFT,
0x7, MADERA_OSR_ENUM_SIZE,
madera_in_dmic_osr_text, madera_in_dmic_osr_val),
SOC_VALUE_ENUM_SINGLE(MADERA_DMIC6L_CONTROL, MADERA_IN6_OSR_SHIFT,
0x7, MADERA_OSR_ENUM_SIZE,
madera_in_dmic_osr_text, madera_in_dmic_osr_val),
};
EXPORT_SYMBOL_GPL(madera_in_dmic_osr);
static const char * const madera_anc_input_src_text[] = {
"None", "IN1", "IN2", "IN3", "IN4", "IN5", "IN6",
};
static const char * const madera_anc_channel_src_text[] = {
"None", "Left", "Right", "Combine",
};
const struct soc_enum madera_anc_input_src[] = {
SOC_ENUM_SINGLE(MADERA_ANC_SRC,
MADERA_IN_RXANCL_SEL_SHIFT,
ARRAY_SIZE(madera_anc_input_src_text),
madera_anc_input_src_text),
SOC_ENUM_SINGLE(MADERA_FCL_ADC_REFORMATTER_CONTROL,
MADERA_FCL_MIC_MODE_SEL_SHIFT,
ARRAY_SIZE(madera_anc_channel_src_text),
madera_anc_channel_src_text),
SOC_ENUM_SINGLE(MADERA_ANC_SRC,
MADERA_IN_RXANCR_SEL_SHIFT,
ARRAY_SIZE(madera_anc_input_src_text),
madera_anc_input_src_text),
SOC_ENUM_SINGLE(MADERA_FCR_ADC_REFORMATTER_CONTROL,
MADERA_FCR_MIC_MODE_SEL_SHIFT,
ARRAY_SIZE(madera_anc_channel_src_text),
madera_anc_channel_src_text),
};
EXPORT_SYMBOL_GPL(madera_anc_input_src);
static const char * const madera_anc_ng_texts[] = {
"None", "Internal", "External",
};
SOC_ENUM_SINGLE_DECL(madera_anc_ng_enum, SND_SOC_NOPM, 0, madera_anc_ng_texts);
EXPORT_SYMBOL_GPL(madera_anc_ng_enum);
static const char * const madera_out_anc_src_text[] = {
"None", "RXANCL", "RXANCR",
};
const struct soc_enum madera_output_anc_src[] = {
SOC_ENUM_SINGLE(MADERA_OUTPUT_PATH_CONFIG_1L,
MADERA_OUT1L_ANC_SRC_SHIFT,
ARRAY_SIZE(madera_out_anc_src_text),
madera_out_anc_src_text),
SOC_ENUM_SINGLE(MADERA_OUTPUT_PATH_CONFIG_1R,
MADERA_OUT1R_ANC_SRC_SHIFT,
ARRAY_SIZE(madera_out_anc_src_text),
madera_out_anc_src_text),
SOC_ENUM_SINGLE(MADERA_OUTPUT_PATH_CONFIG_2L,
MADERA_OUT2L_ANC_SRC_SHIFT,
ARRAY_SIZE(madera_out_anc_src_text),
madera_out_anc_src_text),
SOC_ENUM_SINGLE(MADERA_OUTPUT_PATH_CONFIG_2R,
MADERA_OUT2R_ANC_SRC_SHIFT,
ARRAY_SIZE(madera_out_anc_src_text),
madera_out_anc_src_text),
SOC_ENUM_SINGLE(MADERA_OUTPUT_PATH_CONFIG_3L,
MADERA_OUT3L_ANC_SRC_SHIFT,
ARRAY_SIZE(madera_out_anc_src_text),
madera_out_anc_src_text),
SOC_ENUM_SINGLE(MADERA_OUTPUT_PATH_CONFIG_3R,
MADERA_OUT3R_ANC_SRC_SHIFT,
ARRAY_SIZE(madera_out_anc_src_text),
madera_out_anc_src_text),
SOC_ENUM_SINGLE(MADERA_OUTPUT_PATH_CONFIG_4L,
MADERA_OUT4L_ANC_SRC_SHIFT,
ARRAY_SIZE(madera_out_anc_src_text),
madera_out_anc_src_text),
SOC_ENUM_SINGLE(MADERA_OUTPUT_PATH_CONFIG_4R,
MADERA_OUT4R_ANC_SRC_SHIFT,
ARRAY_SIZE(madera_out_anc_src_text),
madera_out_anc_src_text),
SOC_ENUM_SINGLE(MADERA_OUTPUT_PATH_CONFIG_5L,
MADERA_OUT5L_ANC_SRC_SHIFT,
ARRAY_SIZE(madera_out_anc_src_text),
madera_out_anc_src_text),
SOC_ENUM_SINGLE(MADERA_OUTPUT_PATH_CONFIG_5R,
MADERA_OUT5R_ANC_SRC_SHIFT,
ARRAY_SIZE(madera_out_anc_src_text),
madera_out_anc_src_text),
SOC_ENUM_SINGLE(MADERA_OUTPUT_PATH_CONFIG_6L,
MADERA_OUT6L_ANC_SRC_SHIFT,
ARRAY_SIZE(madera_out_anc_src_text),
madera_out_anc_src_text),
SOC_ENUM_SINGLE(MADERA_OUTPUT_PATH_CONFIG_6R,
MADERA_OUT6R_ANC_SRC_SHIFT,
ARRAY_SIZE(madera_out_anc_src_text),
madera_out_anc_src_text),
};
EXPORT_SYMBOL_GPL(madera_output_anc_src);
int madera_dfc_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component =
snd_soc_kcontrol_component(kcontrol);
struct snd_soc_dapm_context *dapm =
snd_soc_component_get_dapm(component);
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
unsigned int reg = e->reg;
unsigned int val;
int ret = 0;
reg = ((reg / 6) * 6) - 2;
snd_soc_dapm_mutex_lock(dapm);
val = snd_soc_component_read(component, reg);
if (val & MADERA_DFC1_ENA) {
ret = -EBUSY;
dev_err(component->dev, "Can't change mode on an active DFC\n");
goto exit;
}
ret = snd_soc_put_enum_double(kcontrol, ucontrol);
exit:
snd_soc_dapm_mutex_unlock(dapm);
return ret;
}
EXPORT_SYMBOL_GPL(madera_dfc_put);
int madera_lp_mode_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
struct snd_soc_component *component =
snd_soc_kcontrol_component(kcontrol);
struct snd_soc_dapm_context *dapm =
snd_soc_component_get_dapm(component);
unsigned int val, mask;
int ret;
snd_soc_dapm_mutex_lock(dapm);
/* Cannot change lp mode on an active input */
val = snd_soc_component_read(component, MADERA_INPUT_ENABLES);
mask = (mc->reg - MADERA_ADC_DIGITAL_VOLUME_1L) / 4;
mask ^= 0x1; /* Flip bottom bit for channel order */
if (val & (1 << mask)) {
ret = -EBUSY;
dev_err(component->dev,
"Can't change lp mode on an active input\n");
goto exit;
}
ret = snd_soc_put_volsw(kcontrol, ucontrol);
exit:
snd_soc_dapm_mutex_unlock(dapm);
return ret;
}
EXPORT_SYMBOL_GPL(madera_lp_mode_put);
const struct snd_kcontrol_new madera_dsp_trigger_output_mux[] = {
SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0),
SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0),
SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0),
SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0),
SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0),
SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0),
SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0),
};
EXPORT_SYMBOL_GPL(madera_dsp_trigger_output_mux);
const struct snd_kcontrol_new madera_drc_activity_output_mux[] = {
SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0),
SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0),
};
EXPORT_SYMBOL_GPL(madera_drc_activity_output_mux);
static void madera_in_set_vu(struct madera_priv *priv, bool enable)
{
unsigned int val;
int i, ret;
if (enable)
val = MADERA_IN_VU;
else
val = 0;
for (i = 0; i < priv->num_inputs; i++) {
ret = regmap_update_bits(priv->madera->regmap,
MADERA_ADC_DIGITAL_VOLUME_1L + (i * 4),
MADERA_IN_VU, val);
if (ret)
dev_warn(priv->madera->dev,
"Failed to modify VU bits: %d\n", ret);
}
}
int madera_in_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol,
int event)
{
struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
struct madera_priv *priv = snd_soc_component_get_drvdata(component);
unsigned int reg, val;
if (w->shift % 2)
reg = MADERA_ADC_DIGITAL_VOLUME_1L + ((w->shift / 2) * 8);
else
reg = MADERA_ADC_DIGITAL_VOLUME_1R + ((w->shift / 2) * 8);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
priv->in_pending++;
break;
case SND_SOC_DAPM_POST_PMU:
priv->in_pending--;
snd_soc_component_update_bits(component, reg,
MADERA_IN1L_MUTE, 0);
/* If this is the last input pending then allow VU */
if (priv->in_pending == 0) {
usleep_range(1000, 3000);
madera_in_set_vu(priv, true);
}
break;
case SND_SOC_DAPM_PRE_PMD:
snd_soc_component_update_bits(component, reg,
MADERA_IN1L_MUTE | MADERA_IN_VU,
MADERA_IN1L_MUTE | MADERA_IN_VU);
break;
case SND_SOC_DAPM_POST_PMD:
/* Disable volume updates if no inputs are enabled */
val = snd_soc_component_read(component, MADERA_INPUT_ENABLES);
if (!val)
madera_in_set_vu(priv, false);
break;
default:
break;
}
return 0;
}
EXPORT_SYMBOL_GPL(madera_in_ev);
int madera_out_ev(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
struct madera_priv *priv = snd_soc_component_get_drvdata(component);
struct madera *madera = priv->madera;
int out_up_delay;
switch (madera->type) {
case CS47L90:
case CS47L91:
case CS42L92:
case CS47L92:
case CS47L93:
out_up_delay = 6;
break;
default:
out_up_delay = 17;
break;
}
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
switch (w->shift) {
case MADERA_OUT1L_ENA_SHIFT:
case MADERA_OUT1R_ENA_SHIFT:
case MADERA_OUT2L_ENA_SHIFT:
case MADERA_OUT2R_ENA_SHIFT:
case MADERA_OUT3L_ENA_SHIFT:
case MADERA_OUT3R_ENA_SHIFT:
priv->out_up_pending++;
priv->out_up_delay += out_up_delay;
break;
default:
break;
}
break;
case SND_SOC_DAPM_POST_PMU:
switch (w->shift) {
case MADERA_OUT1L_ENA_SHIFT:
case MADERA_OUT1R_ENA_SHIFT:
case MADERA_OUT2L_ENA_SHIFT:
case MADERA_OUT2R_ENA_SHIFT:
case MADERA_OUT3L_ENA_SHIFT:
case MADERA_OUT3R_ENA_SHIFT:
priv->out_up_pending--;
if (!priv->out_up_pending) {
msleep(priv->out_up_delay);
priv->out_up_delay = 0;
}
break;
default:
break;
}
break;
case SND_SOC_DAPM_PRE_PMD:
switch (w->shift) {
case MADERA_OUT1L_ENA_SHIFT:
case MADERA_OUT1R_ENA_SHIFT:
case MADERA_OUT2L_ENA_SHIFT:
case MADERA_OUT2R_ENA_SHIFT:
case MADERA_OUT3L_ENA_SHIFT:
case MADERA_OUT3R_ENA_SHIFT:
priv->out_down_pending++;
priv->out_down_delay++;
break;
default:
break;
}
break;
case SND_SOC_DAPM_POST_PMD:
switch (w->shift) {
case MADERA_OUT1L_ENA_SHIFT:
case MADERA_OUT1R_ENA_SHIFT:
case MADERA_OUT2L_ENA_SHIFT:
case MADERA_OUT2R_ENA_SHIFT:
case MADERA_OUT3L_ENA_SHIFT:
case MADERA_OUT3R_ENA_SHIFT:
priv->out_down_pending--;
if (!priv->out_down_pending) {
msleep(priv->out_down_delay);
priv->out_down_delay = 0;
}
break;
default:
break;
}
break;
default:
break;
}
return 0;
}
EXPORT_SYMBOL_GPL(madera_out_ev);
int madera_hp_ev(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
struct madera_priv *priv = snd_soc_component_get_drvdata(component);
struct madera *madera = priv->madera;
unsigned int mask = 1 << w->shift;
unsigned int out_num = w->shift / 2;
unsigned int val;
unsigned int ep_sel = 0;
switch (event) {
case SND_SOC_DAPM_POST_PMU:
val = mask;
break;
case SND_SOC_DAPM_PRE_PMD:
val = 0;
break;
case SND_SOC_DAPM_PRE_PMU:
case SND_SOC_DAPM_POST_PMD:
return madera_out_ev(w, kcontrol, event);
default:
return 0;
}
/* Store the desired state for the HP outputs */
madera->hp_ena &= ~mask;
madera->hp_ena |= val;
switch (madera->type) {
case CS42L92:
case CS47L92:
case CS47L93:
break;
default:
/* if OUT1 is routed to EPOUT, ignore HP clamp and impedance */
regmap_read(madera->regmap, MADERA_OUTPUT_ENABLES_1, &ep_sel);
ep_sel &= MADERA_EP_SEL_MASK;
break;
}
/* Force off if HPDET has disabled the clamp for this output */
if (!ep_sel &&
(!madera->out_clamp[out_num] || madera->out_shorted[out_num]))
val = 0;
regmap_update_bits(madera->regmap, MADERA_OUTPUT_ENABLES_1, mask, val);
return madera_out_ev(w, kcontrol, event);
}
EXPORT_SYMBOL_GPL(madera_hp_ev);
int madera_anc_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol,
int event)
{
struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
unsigned int val;
switch (event) {
case SND_SOC_DAPM_POST_PMU:
val = 1 << w->shift;
break;
case SND_SOC_DAPM_PRE_PMD:
val = 1 << (w->shift + 1);
break;
default:
return 0;
}
snd_soc_component_write(component, MADERA_CLOCK_CONTROL, val);
return 0;
}
EXPORT_SYMBOL_GPL(madera_anc_ev);
static const unsigned int madera_opclk_ref_48k_rates[] = {
6144000,
12288000,
24576000,
49152000,
};
static const unsigned int madera_opclk_ref_44k1_rates[] = {
5644800,
11289600,
22579200,
45158400,
};
static int madera_set_opclk(struct snd_soc_component *component,
unsigned int clk, unsigned int freq)
{
struct madera_priv *priv = snd_soc_component_get_drvdata(component);
unsigned int mask = MADERA_OPCLK_DIV_MASK | MADERA_OPCLK_SEL_MASK;
unsigned int reg, val;
const unsigned int *rates;
int ref, div, refclk;
BUILD_BUG_ON(ARRAY_SIZE(madera_opclk_ref_48k_rates) !=
ARRAY_SIZE(madera_opclk_ref_44k1_rates));
switch (clk) {
case MADERA_CLK_OPCLK:
reg = MADERA_OUTPUT_SYSTEM_CLOCK;
refclk = priv->sysclk;
break;
case MADERA_CLK_ASYNC_OPCLK:
reg = MADERA_OUTPUT_ASYNC_CLOCK;
refclk = priv->asyncclk;
break;
default:
return -EINVAL;
}
if (refclk % 4000)
rates = madera_opclk_ref_44k1_rates;
else
rates = madera_opclk_ref_48k_rates;
for (ref = 0; ref < ARRAY_SIZE(madera_opclk_ref_48k_rates); ++ref) {
if (rates[ref] > refclk)
continue;
div = 2;
while ((rates[ref] / div >= freq) && (div <= 30)) {
if (rates[ref] / div == freq) {
dev_dbg(component->dev, "Configured %dHz OPCLK\n",
freq);
val = (div << MADERA_OPCLK_DIV_SHIFT) | ref;
snd_soc_component_update_bits(component, reg,
mask, val);
return 0;
}
div += 2;
}
}
dev_err(component->dev, "Unable to generate %dHz OPCLK\n", freq);
return -EINVAL;
}
static int madera_get_sysclk_setting(unsigned int freq)
{
switch (freq) {
case 0:
case 5644800:
case 6144000:
return 0;
case 11289600:
case 12288000:
return MADERA_SYSCLK_12MHZ << MADERA_SYSCLK_FREQ_SHIFT;
case 22579200:
case 24576000:
return MADERA_SYSCLK_24MHZ << MADERA_SYSCLK_FREQ_SHIFT;
case 45158400:
case 49152000:
return MADERA_SYSCLK_49MHZ << MADERA_SYSCLK_FREQ_SHIFT;
case 90316800:
case 98304000:
return MADERA_SYSCLK_98MHZ << MADERA_SYSCLK_FREQ_SHIFT;
default:
return -EINVAL;
}
}
static int madera_get_legacy_dspclk_setting(struct madera *madera,
unsigned int freq)
{
switch (freq) {
case 0:
return 0;
case 45158400:
case 49152000:
switch (madera->type) {
case CS47L85:
case WM1840:
if (madera->rev < 3)
return -EINVAL;
else
return MADERA_SYSCLK_49MHZ <<
MADERA_SYSCLK_FREQ_SHIFT;
default:
return -EINVAL;
}
case 135475200:
case 147456000:
return MADERA_DSPCLK_147MHZ << MADERA_DSP_CLK_FREQ_LEGACY_SHIFT;
default:
return -EINVAL;
}
}
static int madera_get_dspclk_setting(struct madera *madera,
unsigned int freq,
unsigned int *clock_2_val)
{
switch (madera->type) {
case CS47L35:
case CS47L85:
case WM1840:
*clock_2_val = 0; /* don't use MADERA_DSP_CLOCK_2 */
return madera_get_legacy_dspclk_setting(madera, freq);
default:
if (freq > 150000000)
return -EINVAL;
/* Use new exact frequency control */
*clock_2_val = freq / 15625; /* freq * (2^6) / (10^6) */
return 0;
}
}
static int madera_set_outclk(struct snd_soc_component *component,
unsigned int source, unsigned int freq)
{
int div, div_inc, rate;
switch (source) {
case MADERA_OUTCLK_SYSCLK:
dev_dbg(component->dev, "Configured OUTCLK to SYSCLK\n");
snd_soc_component_update_bits(component, MADERA_OUTPUT_RATE_1,
MADERA_OUT_CLK_SRC_MASK, source);
return 0;
case MADERA_OUTCLK_ASYNCCLK:
dev_dbg(component->dev, "Configured OUTCLK to ASYNCCLK\n");
snd_soc_component_update_bits(component, MADERA_OUTPUT_RATE_1,
MADERA_OUT_CLK_SRC_MASK, source);
return 0;
case MADERA_OUTCLK_MCLK1:
case MADERA_OUTCLK_MCLK2:
case MADERA_OUTCLK_MCLK3:
break;
default:
return -EINVAL;
}
if (freq % 4000)
rate = 5644800;
else
rate = 6144000;
div = 1;
div_inc = 0;
while (div <= 8) {
if (freq / div == rate && !(freq % div)) {
dev_dbg(component->dev, "Configured %dHz OUTCLK\n", rate);
snd_soc_component_update_bits(component,
MADERA_OUTPUT_RATE_1,
MADERA_OUT_EXT_CLK_DIV_MASK |
MADERA_OUT_CLK_SRC_MASK,
(div_inc << MADERA_OUT_EXT_CLK_DIV_SHIFT) |
source);
return 0;
}
div_inc++;
div *= 2;
}
dev_err(component->dev,
"Unable to generate %dHz OUTCLK from %dHz MCLK\n",
rate, freq);
return -EINVAL;
}
int madera_set_sysclk(struct snd_soc_component *component, int clk_id,
int source, unsigned int freq, int dir)
{
struct madera_priv *priv = snd_soc_component_get_drvdata(component);
struct madera *madera = priv->madera;
char *name;
unsigned int reg, clock_2_val = 0;
unsigned int mask = MADERA_SYSCLK_FREQ_MASK | MADERA_SYSCLK_SRC_MASK;
unsigned int val = source << MADERA_SYSCLK_SRC_SHIFT;
int clk_freq_sel, *clk;
int ret = 0;
switch (clk_id) {
case MADERA_CLK_SYSCLK_1:
name = "SYSCLK";
reg = MADERA_SYSTEM_CLOCK_1;
clk = &priv->sysclk;
clk_freq_sel = madera_get_sysclk_setting(freq);
mask |= MADERA_SYSCLK_FRAC;
break;
case MADERA_CLK_ASYNCCLK_1:
name = "ASYNCCLK";
reg = MADERA_ASYNC_CLOCK_1;
clk = &priv->asyncclk;
clk_freq_sel = madera_get_sysclk_setting(freq);
break;
case MADERA_CLK_DSPCLK:
name = "DSPCLK";
reg = MADERA_DSP_CLOCK_1;
clk = &priv->dspclk;
clk_freq_sel = madera_get_dspclk_setting(madera, freq,
&clock_2_val);
break;
case MADERA_CLK_OPCLK:
case MADERA_CLK_ASYNC_OPCLK:
return madera_set_opclk(component, clk_id, freq);
case MADERA_CLK_OUTCLK:
return madera_set_outclk(component, source, freq);
default:
return -EINVAL;
}
if (clk_freq_sel < 0) {
dev_err(madera->dev,
"Failed to get clk setting for %dHZ\n", freq);
return clk_freq_sel;
}
*clk = freq;
if (freq == 0) {
dev_dbg(madera->dev, "%s cleared\n", name);
return 0;
}
val |= clk_freq_sel;
if (clock_2_val) {
ret = regmap_write(madera->regmap, MADERA_DSP_CLOCK_2,
clock_2_val);
if (ret) {
dev_err(madera->dev,
"Failed to write DSP_CONFIG2: %d\n", ret);
return ret;
}
/*
* We're using the frequency setting in MADERA_DSP_CLOCK_2 so
* don't change the frequency select bits in MADERA_DSP_CLOCK_1
*/
mask = MADERA_SYSCLK_SRC_MASK;
}
if (freq % 6144000)
val |= MADERA_SYSCLK_FRAC;
dev_dbg(madera->dev, "%s set to %uHz\n", name, freq);
return regmap_update_bits(madera->regmap, reg, mask, val);
}
EXPORT_SYMBOL_GPL(madera_set_sysclk);
static int madera_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
struct snd_soc_component *component = dai->component;
struct madera_priv *priv = snd_soc_component_get_drvdata(component);
struct madera *madera = priv->madera;
int lrclk, bclk, mode, base;
base = dai->driver->base;
lrclk = 0;
bclk = 0;
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_DSP_A:
mode = MADERA_FMT_DSP_MODE_A;
break;
case SND_SOC_DAIFMT_DSP_B:
if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) !=
SND_SOC_DAIFMT_CBM_CFM) {
madera_aif_err(dai, "DSP_B not valid in slave mode\n");
return -EINVAL;
}
mode = MADERA_FMT_DSP_MODE_B;
break;
case SND_SOC_DAIFMT_I2S:
mode = MADERA_FMT_I2S_MODE;
break;
case SND_SOC_DAIFMT_LEFT_J:
if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) !=
SND_SOC_DAIFMT_CBM_CFM) {
madera_aif_err(dai, "LEFT_J not valid in slave mode\n");
return -EINVAL;
}
mode = MADERA_FMT_LEFT_JUSTIFIED_MODE;
break;
default:
madera_aif_err(dai, "Unsupported DAI format %d\n",
fmt & SND_SOC_DAIFMT_FORMAT_MASK);
return -EINVAL;
}
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBS_CFS:
break;
case SND_SOC_DAIFMT_CBS_CFM:
lrclk |= MADERA_AIF1TX_LRCLK_MSTR;
break;
case SND_SOC_DAIFMT_CBM_CFS:
bclk |= MADERA_AIF1_BCLK_MSTR;
break;
case SND_SOC_DAIFMT_CBM_CFM:
bclk |= MADERA_AIF1_BCLK_MSTR;
lrclk |= MADERA_AIF1TX_LRCLK_MSTR;
break;
default:
madera_aif_err(dai, "Unsupported master mode %d\n",
fmt & SND_SOC_DAIFMT_MASTER_MASK);
return -EINVAL;
}
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_NB_NF:
break;
case SND_SOC_DAIFMT_IB_IF:
bclk |= MADERA_AIF1_BCLK_INV;
lrclk |= MADERA_AIF1TX_LRCLK_INV;
break;
case SND_SOC_DAIFMT_IB_NF:
bclk |= MADERA_AIF1_BCLK_INV;
break;
case SND_SOC_DAIFMT_NB_IF:
lrclk |= MADERA_AIF1TX_LRCLK_INV;
break;
default:
madera_aif_err(dai, "Unsupported invert mode %d\n",
fmt & SND_SOC_DAIFMT_INV_MASK);
return -EINVAL;
}
regmap_update_bits(madera->regmap, base + MADERA_AIF_BCLK_CTRL,
MADERA_AIF1_BCLK_INV | MADERA_AIF1_BCLK_MSTR,
bclk);
regmap_update_bits(madera->regmap, base + MADERA_AIF_TX_PIN_CTRL,
MADERA_AIF1TX_LRCLK_INV | MADERA_AIF1TX_LRCLK_MSTR,
lrclk);
regmap_update_bits(madera->regmap, base + MADERA_AIF_RX_PIN_CTRL,
MADERA_AIF1RX_LRCLK_INV | MADERA_AIF1RX_LRCLK_MSTR,
lrclk);
regmap_update_bits(madera->regmap, base + MADERA_AIF_FORMAT,
MADERA_AIF1_FMT_MASK, mode);
return 0;
}
static const int madera_48k_bclk_rates[] = {
-1,
48000,
64000,
96000,
128000,
192000,
256000,
384000,
512000,
768000,
1024000,
1536000,
2048000,
3072000,
4096000,
6144000,
8192000,
12288000,
24576000,
};
static const int madera_44k1_bclk_rates[] = {
-1,
44100,
58800,
88200,
117600,
177640,
235200,
352800,
470400,
705600,
940800,
1411200,
1881600,
2822400,
3763200,
5644800,
7526400,
11289600,
22579200,
};
static const unsigned int madera_sr_vals[] = {
0,
12000,
24000,
48000,
96000,
192000,
384000,
768000,
0,
11025,
22050,
44100,
88200,
176400,
352800,
705600,
4000,
8000,
16000,
32000,
64000,
128000,
256000,
512000,
};
#define MADERA_192K_48K_RATE_MASK 0x0F003E
#define MADERA_192K_44K1_RATE_MASK 0x003E00
#define MADERA_192K_RATE_MASK (MADERA_192K_48K_RATE_MASK | \
MADERA_192K_44K1_RATE_MASK)
#define MADERA_384K_48K_RATE_MASK 0x0F007E
#define MADERA_384K_44K1_RATE_MASK 0x007E00
#define MADERA_384K_RATE_MASK (MADERA_384K_48K_RATE_MASK | \
MADERA_384K_44K1_RATE_MASK)
static const struct snd_pcm_hw_constraint_list madera_constraint = {
.count = ARRAY_SIZE(madera_sr_vals),
.list = madera_sr_vals,
};
static int madera_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct snd_soc_component *component = dai->component;
struct madera_priv *priv = snd_soc_component_get_drvdata(component);
struct madera_dai_priv *dai_priv = &priv->dai[dai->id - 1];
struct madera *madera = priv->madera;
unsigned int base_rate;
if (!substream->runtime)
return 0;
switch (dai_priv->clk) {
case MADERA_CLK_SYSCLK_1:
case MADERA_CLK_SYSCLK_2:
case MADERA_CLK_SYSCLK_3:
base_rate = priv->sysclk;
break;
case MADERA_CLK_ASYNCCLK_1:
case MADERA_CLK_ASYNCCLK_2:
base_rate = priv->asyncclk;
break;
default:
return 0;
}
switch (madera->type) {
case CS42L92:
case CS47L92:
case CS47L93:
if (base_rate == 0)
dai_priv->constraint.mask = MADERA_384K_RATE_MASK;
else if (base_rate % 4000)
dai_priv->constraint.mask = MADERA_384K_44K1_RATE_MASK;
else
dai_priv->constraint.mask = MADERA_384K_48K_RATE_MASK;
break;
default:
if (base_rate == 0)
dai_priv->constraint.mask = MADERA_192K_RATE_MASK;
else if (base_rate % 4000)
dai_priv->constraint.mask = MADERA_192K_44K1_RATE_MASK;
else
dai_priv->constraint.mask = MADERA_192K_48K_RATE_MASK;
break;
}
return snd_pcm_hw_constraint_list(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_RATE,
&dai_priv->constraint);
}
static int madera_hw_params_rate(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct snd_soc_component *component = dai->component;
struct madera_priv *priv = snd_soc_component_get_drvdata(component);
struct madera_dai_priv *dai_priv = &priv->dai[dai->id - 1];
int base = dai->driver->base;
int i, sr_val;
unsigned int reg, cur, tar;
int ret;
for (i = 0; i < ARRAY_SIZE(madera_sr_vals); i++)
if (madera_sr_vals[i] == params_rate(params))
break;
if (i == ARRAY_SIZE(madera_sr_vals)) {
madera_aif_err(dai, "Unsupported sample rate %dHz\n",
params_rate(params));
return -EINVAL;
}
sr_val = i;
switch (dai_priv->clk) {
case MADERA_CLK_SYSCLK_1:
reg = MADERA_SAMPLE_RATE_1;
tar = 0 << MADERA_AIF1_RATE_SHIFT;
break;
case MADERA_CLK_SYSCLK_2:
reg = MADERA_SAMPLE_RATE_2;
tar = 1 << MADERA_AIF1_RATE_SHIFT;
break;
case MADERA_CLK_SYSCLK_3:
reg = MADERA_SAMPLE_RATE_3;
tar = 2 << MADERA_AIF1_RATE_SHIFT;
break;
case MADERA_CLK_ASYNCCLK_1:
reg = MADERA_ASYNC_SAMPLE_RATE_1;
tar = 8 << MADERA_AIF1_RATE_SHIFT;
break;
case MADERA_CLK_ASYNCCLK_2:
reg = MADERA_ASYNC_SAMPLE_RATE_2;
tar = 9 << MADERA_AIF1_RATE_SHIFT;
break;
default:
madera_aif_err(dai, "Invalid clock %d\n", dai_priv->clk);
return -EINVAL;
}
snd_soc_component_update_bits(component, reg, MADERA_SAMPLE_RATE_1_MASK,
sr_val);
if (!base)
return 0;
ret = regmap_read(priv->madera->regmap,
base + MADERA_AIF_RATE_CTRL, &cur);
if (ret != 0) {
madera_aif_err(dai, "Failed to check rate: %d\n", ret);
return ret;
}
if ((cur & MADERA_AIF1_RATE_MASK) == (tar & MADERA_AIF1_RATE_MASK))
return 0;
mutex_lock(&priv->rate_lock);
if (!madera_can_change_grp_rate(priv, base + MADERA_AIF_RATE_CTRL)) {
madera_aif_warn(dai, "Cannot change rate while active\n");
ret = -EBUSY;
goto out;
}
/* Guard the rate change with SYSCLK cycles */
madera_spin_sysclk(priv);
snd_soc_component_update_bits(component, base + MADERA_AIF_RATE_CTRL,
MADERA_AIF1_RATE_MASK, tar);
madera_spin_sysclk(priv);
out:
mutex_unlock(&priv->rate_lock);
return ret;
}
static int madera_aif_cfg_changed(struct snd_soc_component *component,
int base, int bclk, int lrclk, int frame)
{
unsigned int val;
val = snd_soc_component_read(component, base + MADERA_AIF_BCLK_CTRL);
if (bclk != (val & MADERA_AIF1_BCLK_FREQ_MASK))
return 1;
val = snd_soc_component_read(component, base + MADERA_AIF_RX_BCLK_RATE);
if (lrclk != (val & MADERA_AIF1RX_BCPF_MASK))
return 1;
val = snd_soc_component_read(component, base + MADERA_AIF_FRAME_CTRL_1);
if (frame != (val & (MADERA_AIF1TX_WL_MASK |
MADERA_AIF1TX_SLOT_LEN_MASK)))
return 1;
return 0;
}
static int madera_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct snd_soc_component *component = dai->component;
struct madera_priv *priv = snd_soc_component_get_drvdata(component);
struct madera *madera = priv->madera;
int base = dai->driver->base;
const int *rates;
int i, ret;
unsigned int val;
unsigned int channels = params_channels(params);
unsigned int rate = params_rate(params);
unsigned int chan_limit =
madera->pdata.codec.max_channels_clocked[dai->id - 1];
int tdm_width = priv->tdm_width[dai->id - 1];
int tdm_slots = priv->tdm_slots[dai->id - 1];
int bclk, lrclk, wl, frame, bclk_target, num_rates;
int reconfig;
unsigned int aif_tx_state = 0, aif_rx_state = 0;
if (rate % 4000) {
rates = &madera_44k1_bclk_rates[0];
num_rates = ARRAY_SIZE(madera_44k1_bclk_rates);
} else {
rates = &madera_48k_bclk_rates[0];
num_rates = ARRAY_SIZE(madera_48k_bclk_rates);
}
wl = snd_pcm_format_width(params_format(params));
if (tdm_slots) {
madera_aif_dbg(dai, "Configuring for %d %d bit TDM slots\n",
tdm_slots, tdm_width);
bclk_target = tdm_slots * tdm_width * rate;
channels = tdm_slots;
} else {
bclk_target = snd_soc_params_to_bclk(params);
tdm_width = wl;
}
if (chan_limit && chan_limit < channels) {
madera_aif_dbg(dai, "Limiting to %d channels\n", chan_limit);
bclk_target /= channels;
bclk_target *= chan_limit;
}
/* Force multiple of 2 channels for I2S mode */
val = snd_soc_component_read(component, base + MADERA_AIF_FORMAT);
val &= MADERA_AIF1_FMT_MASK;
if ((channels & 1) && val == MADERA_FMT_I2S_MODE) {
madera_aif_dbg(dai, "Forcing stereo mode\n");
bclk_target /= channels;
bclk_target *= channels + 1;
}
for (i = 0; i < num_rates; i++) {
if (rates[i] >= bclk_target && rates[i] % rate == 0) {
bclk = i;
break;
}
}
if (i == num_rates) {
madera_aif_err(dai, "Unsupported sample rate %dHz\n", rate);
return -EINVAL;
}
lrclk = rates[bclk] / rate;
madera_aif_dbg(dai, "BCLK %dHz LRCLK %dHz\n",
rates[bclk], rates[bclk] / lrclk);
frame = wl << MADERA_AIF1TX_WL_SHIFT | tdm_width;
reconfig = madera_aif_cfg_changed(component, base, bclk, lrclk, frame);
if (reconfig < 0)
return reconfig;
if (reconfig) {
/* Save AIF TX/RX state */
regmap_read(madera->regmap, base + MADERA_AIF_TX_ENABLES,
&aif_tx_state);
regmap_read(madera->regmap, base + MADERA_AIF_RX_ENABLES,
&aif_rx_state);
/* Disable AIF TX/RX before reconfiguring it */
regmap_update_bits(madera->regmap,
base + MADERA_AIF_TX_ENABLES, 0xff, 0x0);
regmap_update_bits(madera->regmap,
base +