You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
kernel_samsung_sm7125/sound/soc/codecs/tacna.c

4869 lines
129 KiB

/*
* tacna.c - Cirrus Logic Tacna class codecs common support
*
* Copyright 2016-2018 Cirrus Logic, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#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/mfd/tacna/core.h>
#include <linux/mfd/tacna/registers.h>
#include <linux/mfd/tacna/pdata.h>
#include <linux/regulator/machine.h>
#include <linux/regulator/arizona-micsupp.h>
#include <sound/tacna-pdata.h>
#include <dt-bindings/sound/tacna.h>
#include "tacna.h"
#define TACNA_ASP_ENABLES1 0x00
#define TACNA_ASP_CONTROL1 0x04
#define TACNA_ASP_CONTROL2 0x08
#define TACNA_ASP_CONTROL3 0x0c
#define TACNA_ASP_FRAME_CONTROL1 0x10
#define TACNA_ASP_FRAME_CONTROL2 0x14
#define TACNA_ASP_FRAME_CONTROL5 0x20
#define TACNA_ASP_FRAME_CONTROL6 0x24
#define TACNA_ASP_DATA_CONTROL1 0x30
#define TACNA_ASP_DATA_CONTROL5 0x40
#define TACNA_SYSCLK_RATE_6MHZ 0
#define TACNA_SYSCLK_RATE_12MHZ 1
#define TACNA_SYSCLK_RATE_24MHZ 2
#define TACNA_SYSCLK_RATE_49MHZ 3
#define TACNA_SYSCLK_RATE_98MHZ 4
#define TACNA_FLLHJ_INT_MAX_N 1023
#define TACNA_FLLHJ_INT_MIN_N 1
#define TACNA_FLLHJ_FRAC_MAX_N 255
#define TACNA_FLLHJ_FRAC_MIN_N 2
#define TACNA_FLLHJ_LP_INT_MODE_THRESH 100000
#define TACNA_FLLHJ_LOW_THRESH 192000
#define TACNA_FLLHJ_MID_THRESH 1152000
#define TACNA_FLLHJ_MAX_THRESH 13000000
#define TACNA_FLLHJ_LOW_GAINS 0x23f0
#define TACNA_FLLHJ_MID_GAINS 0x22f2
#define TACNA_FLLHJ_HIGH_GAINS 0x21f0
#define CS47L96_AO_FLLHJ_LOW_GAINS 0x23f1
#define TACNA_FLL_MAX_FOUT 50000000
#define TACNA_FLL_MAX_REFDIV 8
#define TACNA_FLL_CONTROL1_OFFS 0x00
#define TACNA_FLL_CONTROL2_OFFS 0x04
#define TACNA_FLL_CONTROL3_OFFS 0x08
#define TACNA_FLL_CONTROL4_OFFS 0x0c
#define TACNA_FLL_CONTROL5_OFFS 0x10
#define TACNA_FLL_CONTROL6_OFFS 0x14
#define TACNA_FLL_DIGITAL_TEST2_OFFS 0x34
#define TACNA_FLL_GPIO_CLOCK_OFFS 0xa0
#define TACNA_DSP_CLOCK_FREQ_OFFS 0x00000
#define TACNA_ASP_FMT_DSP_MODE_A 0
#define TACNA_ASP_FMT_DSP_MODE_B 1
#define TACNA_ASP_FMT_I2S_MODE 2
#define TACNA_ASP_FMT_LEFT_JUSTIFIED_MODE 3
#define TACNA_CHANNEL_STATUS_POLL_US 1000
#define TACNA_CHANNEL_STATUS_POLL_TIMEOUT_US 20000
#define tacna_fll_err(_fll, fmt, ...) \
dev_err(_fll->tacna_priv->dev, "FLL%d: " fmt, _fll->id, ##__VA_ARGS__)
#define tacna_fll_warn(_fll, fmt, ...) \
dev_warn(_fll->tacna_priv->dev, "FLL%d: " fmt, _fll->id, ##__VA_ARGS__)
#define tacna_fll_info(_fll, fmt, ...) \
dev_info(_fll->tacna_priv->dev, "FLL%d: " fmt, _fll->id, ##__VA_ARGS__)
#define tacna_fll_dbg(_fll, fmt, ...) \
dev_dbg(_fll->tacna_priv->dev, "FLL%d: " fmt, _fll->id, ##__VA_ARGS__)
#define tacna_asp_err(_dai, fmt, ...) \
dev_err(_dai->codec->dev, "ASP%d: " fmt, _dai->id, ##__VA_ARGS__)
#define tacna_asp_warn(_dai, fmt, ...) \
dev_warn(_dai->codec->dev, "ASP%d: " fmt, _dai->id, ##__VA_ARGS__)
#define tacna_asp_info(_dai, fmt, ...) \
dev_info(_dai->codec->dev, "ASP%d: " fmt, _dai->id, ##__VA_ARGS__)
#define tacna_asp_dbg(_dai, fmt, ...) \
dev_dbg(_dai->codec->dev, "ASP%d: " fmt, _dai->id, ##__VA_ARGS__)
const char * const tacna_mixer_texts[] = {
"None",
"Tone Generator 1",
"Tone Generator 2",
"Haptics",
"AEC1",
"AEC2",
"Noise Generator",
"IN1L",
"IN1R",
"IN2L",
"IN2R",
"IN3L",
"IN3R",
"IN4L",
"IN4R",
"HP_SENSE_L",
"HP_SENSE_R",
"ASP1RX1",
"ASP1RX2",
"ASP1RX3",
"ASP1RX4",
"ASP1RX5",
"ASP1RX6",
"ASP1RX7",
"ASP1RX8",
"ASP2RX1",
"ASP2RX2",
"ASP2RX3",
"ASP2RX4",
"ASP2RX5",
"ASP2RX6",
"ASP2RX7",
"ASP2RX8",
"ASP3RX1",
"ASP3RX2",
"ASP3RX3",
"ASP3RX4",
"ASP3RX5",
"ASP3RX6",
"ASP3RX7",
"ASP3RX8",
"ASP4RX1",
"ASP4RX2",
"ASP4RX3",
"ASP4RX4",
"ASP4RX5",
"ASP4RX6",
"ASP4RX7",
"ASP4RX8",
"SLIMRX1",
"SLIMRX2",
"SLIMRX3",
"SLIMRX4",
"SLIMRX5",
"SLIMRX6",
"SLIMRX7",
"SLIMRX8",
"ASRC1IN1L",
"ASRC1IN1R",
"ASRC1IN2L",
"ASRC1IN2R",
"ASRC2IN1L",
"ASRC2IN1R",
"ASRC2IN2L",
"ASRC2IN2R",
"ISRC1INT1",
"ISRC1INT2",
"ISRC1INT3",
"ISRC1INT4",
"ISRC1DEC1",
"ISRC1DEC2",
"ISRC1DEC3",
"ISRC1DEC4",
"ISRC2INT1",
"ISRC2INT2",
"ISRC2DEC1",
"ISRC2DEC2",
"ISRC3INT1",
"ISRC3INT2",
"ISRC3DEC1",
"ISRC3DEC2",
"EQ1",
"EQ2",
"EQ3",
"EQ4",
"DRC1L",
"DRC1R",
"DRC2L",
"DRC2R",
"LHPF1",
"LHPF2",
"LHPF3",
"LHPF4",
"DFC1",
"DFC2",
"DFC3",
"DFC4",
"DFC5",
"DFC6",
"DFC7",
"DFC8",
"Ultrasonic 1",
"Ultrasonic 2",
"AOBRIDGE1IN1",
"AOBRIDGE1IN2",
"AOBRIDGE1IN3",
"AOBRIDGE1IN4",
"AOBRIDGE1IN5",
"AOBRIDGE1IN6",
"AOBRIDGE1IN7",
"AOBRIDGE1IN8",
"DSP1.1",
"DSP1.2",
"DSP1.3",
"DSP1.4",
"DSP1.5",
"DSP1.6",
"DSP1.7",
"DSP1.8",
"DSP2.1",
"DSP2.2",
"DSP2.3",
"DSP2.4",
"DSP2.5",
"DSP2.6",
"DSP2.7",
"DSP2.8",
"SWIRE1DP3RX1",
"SWIRE1DP3RX2",
"SWIRE1DP3RX3",
"SWIRE1DP3RX4",
"SWIRE1DP3RX5",
"SWIRE1DP3RX6",
"SWIRE1DP4RX1",
"SWIRE1DP4RX2",
"SWIRE1DP4RX3",
"SWIRE1DP4RX4",
"SWIRE1DP4RX5",
"SWIRE1DP4RX6",
};
EXPORT_SYMBOL_GPL(tacna_mixer_texts);
unsigned int tacna_mixer_values[] = {
0x000, /* Silence (mute) */
0x004, /* Tone generator 1 */
0x005, /* Tone generator 2 */
0x006, /* Haptic Generator */
0x008, /* AEC loopback 1 */
0x009, /* AEC loopback 2 */
0x00C, /* Noise Generator */
0x010, /* IN1L signal path */
0x011, /* IN1R signal path */
0x012, /* IN2L signal path */
0x013, /* IN2R signal path */
0x014, /* IN3L signal path */
0x015, /* IN3R signal path */
0x016, /* IN4L signal path */
0x017, /* IN4R signal path */
0x00E, /* HP_SENSE_L signal path */
0x00F, /* HP_SENSE_R signal path */
0x020, /* ASP1 RX1 */
0x021, /* ASP1 RX2 */
0x022, /* ASP1 RX3 */
0x023, /* ASP1 RX4 */
0x024, /* ASP1 RX5 */
0x025, /* ASP1 RX6 */
0x026, /* ASP1 RX7 */
0x027, /* ASP1 RX8 */
0x030, /* ASP2 RX1 */
0x031, /* ASP2 RX2 */
0x032, /* ASP2 RX3 */
0x033, /* ASP2 RX4 */
0x034, /* ASP2 RX5 */
0x035, /* ASP2 RX6 */
0x036, /* ASP2 RX7 */
0x037, /* ASP2 RX8 */
0x040, /* ASP3 RX1 */
0x041, /* ASP3 RX2 */
0x042, /* ASP3 RX3 */
0x043, /* ASP3 RX4 */
0x044, /* ASP3 RX5 */
0x045, /* ASP3 RX6 */
0x046, /* ASP3 RX7 */
0x047, /* ASP3 RX8 */
0x050, /* ASP4 RX1 */
0x051, /* ASP4 RX2 */
0x052, /* ASP4 RX3 */
0x053, /* ASP4 RX4 */
0x054, /* ASP4 RX5 */
0x055, /* ASP4 RX6 */
0x056, /* ASP4 RX7 */
0x057, /* ASP4 RX8 */
0x060, /* SLIMbus RX1 */
0x061, /* SLIMbus RX2 */
0x062, /* SLIMbus RX3 */
0x063, /* SLIMbus RX4 */
0x064, /* SLIMbus RX5 */
0x065, /* SLIMbus RX6 */
0x066, /* SLIMbus RX7 */
0x067, /* SLIMbus RX8 */
0x088, /* ASRC1 IN1 Left */
0x089, /* ASRC1 IN1 Right */
0x08A, /* ASRC1 IN2 Left */
0x08B, /* ASRC1 IN2 Right */
0x090, /* ASRC2 IN1 Left */
0x091, /* ASRC2 IN1 Right */
0x092, /* ASRC2 IN2 Left */
0x093, /* ASRC2 IN2 Right */
0x098, /* ISRC1 INT1 */
0x099, /* ISRC1 INT2 */
0x09a, /* ISRC1 INT3 */
0x09b, /* ISRC1 INT4 */
0x09C, /* ISRC1 DEC1 */
0x09D, /* ISRC1 DEC2 */
0x09e, /* ISRC1 DEC3 */
0x09f, /* ISRC1 DEC4 */
0x0A0, /* ISRC2 INT1 */
0x0A1, /* ISRC2 INT2 */
0x0A4, /* ISRC2 DEC1 */
0x0A5, /* ISRC2 DEC2 */
0x0A8, /* ISRC3 INT1 */
0x0A9, /* ISRC3 INT2 */
0x0AC, /* ISRC3 DEC1 */
0x0AD, /* ISRC3 DEC2 */
0x0B8, /* EQ1 */
0x0B9, /* EQ2 */
0x0BA, /* EQ3 */
0x0BB, /* EQ4 */
0x0C0, /* DRC1 Left */
0x0C1, /* DRC1 Right */
0x0C2, /* DRC2 Left */
0x0C3, /* DRC2 Right */
0x0C8, /* LHPF1 */
0x0C9, /* LHPF2 */
0x0CA, /* LHPF3 */
0x0CB, /* LHPF4 */
0x0D0, /* DFC1 channel 1 */
0x0D1, /* DFC1 channel 2 */
0x0D2, /* DFC1 channel 3 */
0x0D3, /* DFC1 channel 4 */
0x0D4, /* DFC1 channel 5 */
0x0D5, /* DFC1 channel 6 */
0x0D6, /* DFC1 channel 7 */
0x0D7, /* DFC1 channel 8 */
0x0D8, /* Ultrasonic 1 */
0x0D9, /* Ultrasonic 2 */
0x0e0, /* AO Bridge 1 channel 1 */
0x0e1, /* AO Bridge 1 channel 2 */
0x0e2, /* AO Bridge 1 channel 3 */
0x0e3, /* AO Bridge 1 channel 4 */
0x0e4, /* AO Bridge 1 channel 5 */
0x0e5, /* AO Bridge 1 channel 6 */
0x0e6, /* AO Bridge 1 channel 7 */
0x0e7, /* AO Bridge 1 channel 8 */
0x100, /* DSP1 channel 1 */
0x101, /* DSP1 channel 2 */
0x102, /* DSP1 channel 3 */
0x103, /* DSP1 channel 4 */
0x104, /* DSP1 channel 5 */
0x105, /* DSP1 channel 6 */
0x106, /* DSP1 channel 7 */
0x107, /* DSP1 channel 8 */
0x110, /* DSP2 channel 1 */
0x111, /* DSP2 channel 2 */
0x112, /* DSP2 channel 3 */
0x113, /* DSP2 channel 4 */
0x114, /* DSP2 channel 5 */
0x115, /* DSP2 channel 6 */
0x116, /* DSP2 channel 7 */
0x117, /* DSP2 channel 8 */
0x190, /* SWIRE1 DP3RX1 */
0x191, /* SWIRE1 DP3RX2 */
0x192, /* SWIRE1 DP3RX3 */
0x193, /* SWIRE1 DP3RX4 */
0x194, /* SWIRE1 DP3RX5 */
0x195, /* SWIRE1 DP3RX6 */
0x198, /* SWIRE1 DP4RX1 */
0x199, /* SWIRE1 DP4RX2 */
0x19A, /* SWIRE1 DP4RX3 */
0x19B, /* SWIRE1 DP4RX4 */
0x19C, /* SWIRE1 DP4RX5 */
0x19D, /* SWIRE1 DP4RX6 */
};
EXPORT_SYMBOL_GPL(tacna_mixer_values);
const DECLARE_TLV_DB_SCALE(tacna_ana_tlv, 0, 100, 0);
EXPORT_SYMBOL_GPL(tacna_ana_tlv);
const DECLARE_TLV_DB_SCALE(tacna_eq_tlv, -1200, 100, 0);
EXPORT_SYMBOL_GPL(tacna_eq_tlv);
const DECLARE_TLV_DB_SCALE(tacna_digital_tlv, -6400, 50, 0);
EXPORT_SYMBOL_GPL(tacna_digital_tlv);
const DECLARE_TLV_DB_SCALE(tacna_noise_tlv, -10800, 600, 0);
EXPORT_SYMBOL_GPL(tacna_noise_tlv);
const DECLARE_TLV_DB_SCALE(tacna_mixer_tlv, -3200, 100, 0);
EXPORT_SYMBOL_GPL(tacna_mixer_tlv);
const DECLARE_TLV_DB_SCALE(tacna_us_tlv, 0, 600, 0);
EXPORT_SYMBOL_GPL(tacna_us_tlv);
void tacna_spin_sysclk(struct tacna_priv *priv)
{
struct tacna *tacna = priv->tacna;
unsigned int val;
int ret, i;
/* Skip this if the chip is down */
if (pm_runtime_suspended(tacna->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(tacna->regmap, TACNA_DEVID, &val);
if (ret)
dev_err(priv->dev,
"%s Failed to read register: %d (%d)\n",
__func__, ret, i);
}
udelay(300);
}
EXPORT_SYMBOL_GPL(tacna_spin_sysclk);
const char * const tacna_rate_text[TACNA_RATE_ENUM_SIZE] = {
"Sample Rate 1",
"Sample Rate 2",
"Sample Rate 3",
"Sample Rate 4",
"Async Sample Rate 1",
"Async Sample Rate 2",
};
EXPORT_SYMBOL_GPL(tacna_rate_text);
const unsigned int tacna_rate_val[TACNA_RATE_ENUM_SIZE] = {
0x0, 0x1, 0x2, 0x3, 0x8, 0x9,
};
EXPORT_SYMBOL_GPL(tacna_rate_val);
const char * const tacna_ao_rate_text[TACNA_AO_RATE_ENUM_SIZE] = {
"AO Sample Rate 1",
"AO Sample Rate 2",
"AO Sample Rate 3",
};
EXPORT_SYMBOL_GPL(tacna_ao_rate_text);
const unsigned int tacna_ao_rate_val[TACNA_AO_RATE_ENUM_SIZE] = {
0x0, 0x1, 0x2,
};
EXPORT_SYMBOL_GPL(tacna_ao_rate_val);
static bool tacna_rate_val_is_sync(unsigned int val)
{
return (val < 0x8);
}
int tacna_rate_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct tacna_priv *priv = snd_soc_codec_get_drvdata(codec);
int ret;
/* Prevent any mixer mux changes while we do this */
mutex_lock(&priv->rate_lock);
/* The write must be guarded by a number of SYSCLK cycles */
tacna_spin_sysclk(priv);
ret = snd_soc_put_enum_double(kcontrol, ucontrol);
tacna_spin_sysclk(priv);
mutex_unlock(&priv->rate_lock);
return ret;
}
EXPORT_SYMBOL_GPL(tacna_rate_put);
int tacna_rate_is_sync(struct tacna_priv *priv, unsigned int reg,
unsigned int mask, unsigned int shift)
{
unsigned int val;
int ret;
ret = regmap_read(priv->tacna->regmap, reg, &val);
if (ret) {
dev_err(priv->dev, "Error reading 0x%x (%d)\n", reg, ret);
return ret;
}
val = (val & mask) >> shift;
return tacna_rate_val_is_sync(val);
}
EXPORT_SYMBOL_GPL(tacna_rate_is_sync);
const char * const tacna_sample_rate_text[TACNA_SAMPLE_RATE_ENUM_SIZE] = {
"12kHz",
"24kHz",
"48kHz",
"96kHz",
"192kHz",
"384kHz",
"768kHz",
"11.025kHz",
"22.05kHz",
"44.1kHz",
"88.2kHz",
"176.4kHz",
"352.8kHz",
"705.6kHz",
"8kHz",
"16kHz",
"32kHz",
};
EXPORT_SYMBOL_GPL(tacna_sample_rate_text);
const unsigned int tacna_sample_rate_val[TACNA_SAMPLE_RATE_ENUM_SIZE] = {
0x01, /* 12kHz */
0x02, /* 24kHz */
0x03, /* 48kHz */
0x04, /* 96kHz */
0x05, /* 192kHz */
0x06, /* 384kHz */
0x07, /* 768kHz */
0x09, /* 11.025kHz */
0x0a, /* 22.05kHz */
0x0b, /* 44.1kHz */
0x0c, /* 88.2kHz */
0x0d, /* 176.4kHz */
0x0e, /* 352.8kHz */
0x0f, /* 705.6kHz */
0x11, /* 8kHz */
0x12, /* 16kHz */
0x13, /* 32kHz */
};
EXPORT_SYMBOL_GPL(tacna_sample_rate_val);
const char *tacna_sample_rate_val_to_name(unsigned int rate_val)
{
int i;
for (i = 0; i < ARRAY_SIZE(tacna_sample_rate_val); ++i) {
if (tacna_sample_rate_val[i] == rate_val)
return tacna_sample_rate_text[i];
}
return "Illegal";
}
EXPORT_SYMBOL_GPL(tacna_sample_rate_val_to_name);
const struct soc_enum tacna_sample_rate[] = {
SOC_VALUE_ENUM_SINGLE(TACNA_SAMPLE_RATE1,
TACNA_SAMPLE_RATE_1_SHIFT,
TACNA_SAMPLE_RATE_1_MASK >>
TACNA_SAMPLE_RATE_1_SHIFT,
TACNA_SAMPLE_RATE_ENUM_SIZE,
tacna_sample_rate_text,
tacna_sample_rate_val),
SOC_VALUE_ENUM_SINGLE(TACNA_SAMPLE_RATE2,
TACNA_SAMPLE_RATE_2_SHIFT,
TACNA_SAMPLE_RATE_2_MASK >>
TACNA_SAMPLE_RATE_2_SHIFT,
TACNA_SAMPLE_RATE_ENUM_SIZE,
tacna_sample_rate_text,
tacna_sample_rate_val),
SOC_VALUE_ENUM_SINGLE(TACNA_SAMPLE_RATE3,
TACNA_SAMPLE_RATE_3_SHIFT,
TACNA_SAMPLE_RATE_3_MASK >>
TACNA_SAMPLE_RATE_3_SHIFT,
TACNA_SAMPLE_RATE_ENUM_SIZE,
tacna_sample_rate_text,
tacna_sample_rate_val),
SOC_VALUE_ENUM_SINGLE(TACNA_SAMPLE_RATE4,
TACNA_SAMPLE_RATE_4_SHIFT,
TACNA_SAMPLE_RATE_4_MASK >>
TACNA_SAMPLE_RATE_4_SHIFT,
TACNA_SAMPLE_RATE_ENUM_SIZE,
tacna_sample_rate_text,
tacna_sample_rate_val),
};
EXPORT_SYMBOL_GPL(tacna_sample_rate);
const struct soc_enum tacna_sample_rate_async[] = {
SOC_VALUE_ENUM_SINGLE(TACNA_ASYNC_SAMPLE_RATE1,
TACNA_ASYNC_SAMPLE_RATE_1_SHIFT,
TACNA_ASYNC_SAMPLE_RATE_1_MASK >>
TACNA_ASYNC_SAMPLE_RATE_1_SHIFT,
TACNA_SAMPLE_RATE_ENUM_SIZE,
tacna_sample_rate_text,
tacna_sample_rate_val),
SOC_VALUE_ENUM_SINGLE(TACNA_ASYNC_SAMPLE_RATE2,
TACNA_ASYNC_SAMPLE_RATE_2_SHIFT,
TACNA_ASYNC_SAMPLE_RATE_2_MASK >>
TACNA_ASYNC_SAMPLE_RATE_2_SHIFT,
TACNA_SAMPLE_RATE_ENUM_SIZE,
tacna_sample_rate_text,
tacna_sample_rate_val),
};
EXPORT_SYMBOL_GPL(tacna_sample_rate_async);
static int tacna_inmux_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);
struct snd_soc_dapm_context *dapm =
snd_soc_dapm_kcontrol_dapm(kcontrol);
struct tacna *tacna = dev_get_drvdata(codec->dev->parent);
struct tacna_priv *priv = snd_soc_codec_get_drvdata(codec);
struct soc_enum *e = (struct soc_enum *) kcontrol->private_value;
unsigned int mux, src_val, in_type;
int ret;
mux = ucontrol->value.enumerated.item[0];
if (mux > 1)
return -EINVAL;
switch (e->reg) {
case TACNA_IN1L_CONTROL1:
in_type = tacna->pdata.codec.in_type[0][2 * mux];
break;
case TACNA_IN1R_CONTROL1:
in_type = tacna->pdata.codec.in_type[0][1 + (2 * mux)];
break;
case TACNA_IN2L_CONTROL1:
in_type = tacna->pdata.codec.in_type[1][2 * mux];
break;
case TACNA_IN2R_CONTROL1:
in_type = tacna->pdata.codec.in_type[1][1 + (2 * mux)];
break;
default:
return -EINVAL;
}
src_val = mux << e->shift_l;
if (in_type == TACNA_IN_TYPE_SE)
src_val |= 1 << TACNA_IN1L_SRC_SHIFT;
dev_dbg(priv->dev,
"mux=%u reg=0x%x in_type=0x%x val=0x%x\n",
mux, e->reg, in_type, src_val);
ret = snd_soc_component_update_bits(dapm->component,
e->reg,
TACNA_IN1L_SRC_MASK,
src_val);
if (ret < 0)
return ret;
else if (ret)
return snd_soc_dapm_mux_update_power(dapm, kcontrol,
mux, e, NULL);
else
return 0;
}
static const char * const tacna_inmux_texts[] = {
"Analog 1",
"Analog 2",
};
static SOC_ENUM_SINGLE_DECL(tacna_in1muxl_enum,
TACNA_IN1L_CONTROL1,
TACNA_IN1L_SRC_SHIFT + 1,
tacna_inmux_texts);
static SOC_ENUM_SINGLE_DECL(tacna_in1muxr_enum,
TACNA_IN1R_CONTROL1,
TACNA_IN1R_SRC_SHIFT + 1,
tacna_inmux_texts);
static SOC_ENUM_SINGLE_DECL(tacna_in2muxl_enum,
TACNA_IN2L_CONTROL1,
TACNA_IN2L_SRC_SHIFT + 1,
tacna_inmux_texts);
static SOC_ENUM_SINGLE_DECL(tacna_in2muxr_enum,
TACNA_IN2R_CONTROL1,
TACNA_IN2R_SRC_SHIFT + 1,
tacna_inmux_texts);
const struct snd_kcontrol_new tacna_inmux[] = {
SOC_DAPM_ENUM_EXT("IN1L Mux", tacna_in1muxl_enum,
snd_soc_dapm_get_enum_double, tacna_inmux_put),
SOC_DAPM_ENUM_EXT("IN1R Mux", tacna_in1muxr_enum,
snd_soc_dapm_get_enum_double, tacna_inmux_put),
SOC_DAPM_ENUM_EXT("IN2L Mux", tacna_in2muxl_enum,
snd_soc_dapm_get_enum_double, tacna_inmux_put),
SOC_DAPM_ENUM_EXT("IN2R Mux", tacna_in2muxr_enum,
snd_soc_dapm_get_enum_double, tacna_inmux_put),
};
EXPORT_SYMBOL_GPL(tacna_inmux);
const char * const tacna_dmode_texts[] = {
"Analog",
"Digital",
};
EXPORT_SYMBOL_GPL(tacna_dmode_texts);
static SOC_ENUM_SINGLE_DECL(tacna_in1dmode_enum,
TACNA_INPUT1_CONTROL1,
TACNA_IN1_MODE_SHIFT,
tacna_dmode_texts);
static SOC_ENUM_SINGLE_DECL(tacna_in2dmode_enum,
TACNA_INPUT2_CONTROL1,
TACNA_IN2_MODE_SHIFT,
tacna_dmode_texts);
const struct snd_kcontrol_new tacna_dmode_mux[] = {
SOC_DAPM_ENUM("IN1 Mode", tacna_in1dmode_enum),
SOC_DAPM_ENUM("IN2 Mode", tacna_in2dmode_enum),
};
EXPORT_SYMBOL_GPL(tacna_dmode_mux);
const char * const tacna_us_in_texts[] = {
"IN1L",
"IN1R",
"IN2L",
"IN2R",
"IN3L",
"IN3R",
"IN4L",
"IN4R",
};
EXPORT_SYMBOL_GPL(tacna_us_in_texts);
static SOC_ENUM_SINGLE_DECL(tacna_us1_in_enum,
TACNA_US1_CONTROL,
TACNA_US1_SRC_SHIFT,
tacna_us_in_texts);
static SOC_ENUM_SINGLE_DECL(tacna_us2_in_enum,
TACNA_US2_CONTROL,
TACNA_US2_SRC_SHIFT,
tacna_us_in_texts);
const struct snd_kcontrol_new tacna_us_inmux[2] = {
SOC_DAPM_ENUM("Ultrasonic 1 Input", tacna_us1_in_enum),
SOC_DAPM_ENUM("Ultrasonic 2 Input", tacna_us2_in_enum),
};
EXPORT_SYMBOL_GPL(tacna_us_inmux);
const char * const tacna_us_freq_texts[] = {
"24.5-40.5kHz",
"18-22kHz",
"16-24kHz",
"20-28kHz",
};
EXPORT_SYMBOL_GPL(tacna_us_freq_texts);
const struct soc_enum tacna_us_freq[] = {
SOC_ENUM_SINGLE(TACNA_US1_CONTROL,
TACNA_US1_FREQ_SHIFT,
ARRAY_SIZE(tacna_us_freq_texts),
tacna_us_freq_texts),
SOC_ENUM_SINGLE(TACNA_US2_CONTROL,
TACNA_US2_FREQ_SHIFT,
ARRAY_SIZE(tacna_us_freq_texts),
tacna_us_freq_texts),
};
EXPORT_SYMBOL_GPL(tacna_us_freq);
static const char * const tacna_us_det_thr_texts[] = {
"-6dB",
"-9dB",
"-12dB",
"-15dB",
"-18dB",
"-21dB",
"-24dB",
"-27dB",
};
const struct soc_enum tacna_us_det_thr[] = {
SOC_ENUM_SINGLE(TACNA_US1_DET_CONTROL,
TACNA_US1_DET_THR_SHIFT,
ARRAY_SIZE(tacna_us_det_thr_texts),
tacna_us_det_thr_texts),
SOC_ENUM_SINGLE(TACNA_US2_DET_CONTROL,
TACNA_US2_DET_THR_SHIFT,
ARRAY_SIZE(tacna_us_det_thr_texts),
tacna_us_det_thr_texts),
};
EXPORT_SYMBOL_GPL(tacna_us_det_thr);
static const char * const tacna_us_det_num_texts[] = {
"1 Sample",
"2 Samples",
"4 Samples",
"8 Samples",
"16 Samples",
"32 Samples",
"64 Samples",
"128 Samples",
"256 Samples",
"512 Samples",
"1024 Samples",
"2048 Samples",
"4096 Samples",
"8192 Samples",
"16384 Samples",
"32768 Samples",
};
const struct soc_enum tacna_us_det_num[] = {
SOC_ENUM_SINGLE(TACNA_US1_DET_CONTROL,
TACNA_US1_DET_NUM_SHIFT,
ARRAY_SIZE(tacna_us_det_num_texts),
tacna_us_det_num_texts),
SOC_ENUM_SINGLE(TACNA_US2_DET_CONTROL,
TACNA_US2_DET_NUM_SHIFT,
ARRAY_SIZE(tacna_us_det_num_texts),
tacna_us_det_num_texts),
};
EXPORT_SYMBOL_GPL(tacna_us_det_num);
static const char * const tacna_us_det_hold_texts[] = {
"0 Samples",
"31 Samples",
"63 Samples",
"127 Samples",
"255 Samples",
"511 Samples",
"1023 Samples",
"2047 Samples",
"4095 Samples",
"8191 Samples",
"16383 Samples",
"32767 Samples",
"65535 Samples",
"131071 Samples",
"262143 Samples",
"524287 Samples",
};
const struct soc_enum tacna_us_det_hold[] = {
SOC_ENUM_SINGLE(TACNA_US1_DET_CONTROL,
TACNA_US1_DET_HOLD_SHIFT,
ARRAY_SIZE(tacna_us_det_hold_texts),
tacna_us_det_hold_texts),
SOC_ENUM_SINGLE(TACNA_US2_DET_CONTROL,
TACNA_US2_DET_HOLD_SHIFT,
ARRAY_SIZE(tacna_us_det_hold_texts),
tacna_us_det_hold_texts),
};
EXPORT_SYMBOL_GPL(tacna_us_det_hold);
static const char * const tacna_us_det_dcy_texts[] = {
"0 Samples",
"36 Samples",
"73 Samples",
"146 Samples",
"293 Samples",
"588 Samples",
"1177 Samples",
"2355 Samples",
};
const struct soc_enum tacna_us_det_dcy[] = {
SOC_ENUM_SINGLE(TACNA_US1_DET_CONTROL,
TACNA_US1_DET_DCY_SHIFT,
ARRAY_SIZE(tacna_us_det_dcy_texts),
tacna_us_det_dcy_texts),
SOC_ENUM_SINGLE(TACNA_US2_DET_CONTROL,
TACNA_US2_DET_DCY_SHIFT,
ARRAY_SIZE(tacna_us_det_dcy_texts),
tacna_us_det_dcy_texts),
};
EXPORT_SYMBOL_GPL(tacna_us_det_dcy);
const struct soc_enum tacna_us_output_rate[] = {
SOC_VALUE_ENUM_SINGLE(TACNA_US1_CONTROL,
TACNA_US1_RATE_SHIFT,
TACNA_US1_RATE_MASK >> TACNA_US1_RATE_SHIFT,
TACNA_SYNC_RATE_ENUM_SIZE,
tacna_rate_text,
tacna_rate_val),
SOC_VALUE_ENUM_SINGLE(TACNA_US2_CONTROL,
TACNA_US2_RATE_SHIFT,
TACNA_US2_RATE_MASK >> TACNA_US2_RATE_SHIFT,
TACNA_SYNC_RATE_ENUM_SIZE,
tacna_rate_text,
tacna_rate_val),
};
EXPORT_SYMBOL_GPL(tacna_us_output_rate);
const struct snd_kcontrol_new tacna_us_switch[] = {
SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0),
SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0),
};
EXPORT_SYMBOL_GPL(tacna_us_switch);
irqreturn_t tacna_us1_activity(int irq, void *data)
{
struct tacna *tacna = data;
struct tacna_us_notify_data us_data;
us_data.us_no = 1;
tacna_call_notifiers(tacna, TACNA_NOTIFY_ULTRASONIC, &us_data);
return IRQ_HANDLED;
}
EXPORT_SYMBOL_GPL(tacna_us1_activity);
irqreturn_t tacna_us2_activity(int irq, void *data)
{
struct tacna *tacna = data;
struct tacna_us_notify_data us_data;
us_data.us_no = 2;
tacna_call_notifiers(tacna, TACNA_NOTIFY_ULTRASONIC, &us_data);
return IRQ_HANDLED;
}
EXPORT_SYMBOL_GPL(tacna_us2_activity);
const char * const tacna_vol_ramp_text[TACNA_VOL_RAMP_ENUM_SIZE] = {
"0ms/6dB", "0.5ms/6dB", "1ms/6dB", "2ms/6dB", "4ms/6dB", "8ms/6dB",
"16ms/6dB", "32ms/6dB",
};
EXPORT_SYMBOL_GPL(tacna_vol_ramp_text);
SOC_ENUM_SINGLE_DECL(tacna_in_vd_ramp,
TACNA_INPUT_VOL_CONTROL,
TACNA_IN_VD_RAMP_SHIFT,
tacna_vol_ramp_text);
EXPORT_SYMBOL_GPL(tacna_in_vd_ramp);
SOC_ENUM_SINGLE_DECL(tacna_in_vi_ramp,
TACNA_INPUT_VOL_CONTROL,
TACNA_IN_VI_RAMP_SHIFT,
tacna_vol_ramp_text);
EXPORT_SYMBOL_GPL(tacna_in_vi_ramp);
const char * const tacna_in_hpf_cut_text[TACNA_IN_HPF_CUT_ENUM_SIZE] = {
"2.5Hz", "5Hz", "10Hz", "20Hz", "40Hz"
};
EXPORT_SYMBOL_GPL(tacna_in_hpf_cut_text);
SOC_ENUM_SINGLE_DECL(tacna_in_hpf_cut_enum,
TACNA_INPUT_HPF_CONTROL,
TACNA_IN_HPF_CUT_SHIFT,
tacna_in_hpf_cut_text);
EXPORT_SYMBOL_GPL(tacna_in_hpf_cut_enum);
const char * const tacna_in_dmic_osr_text[TACNA_OSR_ENUM_SIZE] = {
"384kHz", "768kHz", "1.536MHz", "2.048MHz", "2.4576MHz", "3.072MHz",
"6.144MHz",
};
EXPORT_SYMBOL_GPL(tacna_in_dmic_osr_text);
const struct soc_enum tacna_in_dmic_osr[] = {
SOC_ENUM_SINGLE(TACNA_INPUT1_CONTROL1,
TACNA_IN1_OSR_SHIFT,
TACNA_OSR_ENUM_SIZE,
tacna_in_dmic_osr_text),
SOC_ENUM_SINGLE(TACNA_INPUT2_CONTROL1,
TACNA_IN2_OSR_SHIFT,
TACNA_OSR_ENUM_SIZE,
tacna_in_dmic_osr_text),
SOC_ENUM_SINGLE(TACNA_INPUT3_CONTROL1,
TACNA_IN3_OSR_SHIFT,
TACNA_OSR_ENUM_SIZE,
tacna_in_dmic_osr_text),
SOC_ENUM_SINGLE(TACNA_INPUT4_CONTROL1,
TACNA_IN4_OSR_SHIFT,
TACNA_OSR_ENUM_SIZE,
tacna_in_dmic_osr_text),
};
EXPORT_SYMBOL_GPL(tacna_in_dmic_osr);
int tacna_in_rate_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
unsigned int reg, shift;
int ret = 0;
snd_soc_dapm_mutex_lock(dapm);
/* Cannot change rate on an active input */
reg = snd_soc_read(codec, TACNA_INPUT_CONTROL);
shift = (e->reg - TACNA_IN1L_CONTROL1) / 0x20;
shift ^= 0x1; /* Flip bottom bit for channel order */
if ((reg) & (1 << shift)) {
ret = -EBUSY;
goto exit;
}
ret = snd_soc_put_enum_double(kcontrol, ucontrol);
exit:
snd_soc_dapm_mutex_unlock(dapm);
return ret;
}
EXPORT_SYMBOL_GPL(tacna_in_rate_put);
const struct soc_enum tacna_input_rate[] = {
SOC_VALUE_ENUM_SINGLE(TACNA_IN1L_CONTROL1,
TACNA_IN1L_RATE_SHIFT,
TACNA_IN1L_RATE_MASK >> TACNA_IN1L_RATE_SHIFT,
TACNA_SYNC_RATE_ENUM_SIZE,
tacna_rate_text,
tacna_rate_val),
SOC_VALUE_ENUM_SINGLE(TACNA_IN1R_CONTROL1,
TACNA_IN1R_RATE_SHIFT,
TACNA_IN1R_RATE_MASK >> TACNA_IN1R_RATE_SHIFT,
TACNA_SYNC_RATE_ENUM_SIZE,
tacna_rate_text,
tacna_rate_val),
SOC_VALUE_ENUM_SINGLE(TACNA_IN2L_CONTROL1,
TACNA_IN2L_RATE_SHIFT,
TACNA_IN2L_RATE_MASK >> TACNA_IN2L_RATE_SHIFT,
TACNA_SYNC_RATE_ENUM_SIZE,
tacna_rate_text,
tacna_rate_val),
SOC_VALUE_ENUM_SINGLE(TACNA_IN2R_CONTROL1,
TACNA_IN2R_RATE_SHIFT,
TACNA_IN2R_RATE_MASK >> TACNA_IN2R_RATE_SHIFT,
TACNA_SYNC_RATE_ENUM_SIZE,
tacna_rate_text,
tacna_rate_val),
SOC_VALUE_ENUM_SINGLE(TACNA_IN3L_CONTROL1,
TACNA_IN3L_RATE_SHIFT,
TACNA_IN3L_RATE_MASK >> TACNA_IN3L_RATE_SHIFT,
TACNA_SYNC_RATE_ENUM_SIZE,
tacna_rate_text,
tacna_rate_val),
SOC_VALUE_ENUM_SINGLE(TACNA_IN3R_CONTROL1,
TACNA_IN3R_RATE_SHIFT,
TACNA_IN3R_RATE_MASK >> TACNA_IN3R_RATE_SHIFT,
TACNA_SYNC_RATE_ENUM_SIZE,
tacna_rate_text,
tacna_rate_val),
SOC_VALUE_ENUM_SINGLE(TACNA_IN4L_CONTROL1,
TACNA_IN4L_RATE_SHIFT,
TACNA_IN4L_RATE_MASK >> TACNA_IN4L_RATE_SHIFT,
TACNA_SYNC_RATE_ENUM_SIZE,
tacna_rate_text,
tacna_rate_val),
SOC_VALUE_ENUM_SINGLE(TACNA_IN4R_CONTROL1,
TACNA_IN4R_RATE_SHIFT,
TACNA_IN4R_RATE_MASK >> TACNA_IN4R_RATE_SHIFT,
TACNA_SYNC_RATE_ENUM_SIZE,
tacna_rate_text,
tacna_rate_val),
};
EXPORT_SYMBOL_GPL(tacna_input_rate);
int tacna_low_power_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_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
unsigned int reg, mask;
int ret;
snd_soc_dapm_mutex_lock(dapm);
/* Cannot change low power mode on an active input */
reg = snd_soc_read(codec, TACNA_INPUT_CONTROL);
mask = (mc->reg - TACNA_IN1L_CONTROL1) / 0x20;
mask ^= 0x1; /* Flip bottom bit for channel order */
if ((reg) & (1 << mask)) {
ret = -EBUSY;
dev_err(codec->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(tacna_low_power_mode_put);
static const char * const tacna_auxpdm_freq_texts[] = {
"3.072MHz",
"2.048MHz",
"1.536MHz",
"768kHz",
};
SOC_ENUM_SINGLE_DECL(tacna_auxpdm1_freq,
TACNA_AUXPDM1_CONTROL1,
TACNA_AUXPDM1_FREQ_SHIFT,
tacna_auxpdm_freq_texts);
EXPORT_SYMBOL_GPL(tacna_auxpdm1_freq);
SOC_ENUM_SINGLE_DECL(tacna_auxpdm2_freq,
TACNA_AUXPDM2_CONTROL1,
TACNA_AUXPDM2_FREQ_SHIFT,
tacna_auxpdm_freq_texts);
EXPORT_SYMBOL_GPL(tacna_auxpdm2_freq);
SOC_ENUM_SINGLE_DECL(tacna_auxpdm3_freq,
TACNA_AUXPDM3_CONTROL1,
TACNA_AUXPDM3_FREQ_SHIFT,
tacna_auxpdm_freq_texts);
EXPORT_SYMBOL_GPL(tacna_auxpdm3_freq);
const char * const tacna_auxpdm_in_texts[] = {
"IN1L",
"IN1R",
"IN2L",
"IN2R",
"IN3L",
"IN3R",
"IN4L",
"IN4R",
};
EXPORT_SYMBOL_GPL(tacna_auxpdm_in_texts);
static SOC_ENUM_SINGLE_DECL(tacna_auxpdm1_in,
TACNA_AUXPDM1_CONTROL1,
TACNA_AUXPDM1_SRC_SHIFT,
tacna_auxpdm_in_texts);
static SOC_ENUM_SINGLE_DECL(tacna_auxpdm2_in,
TACNA_AUXPDM2_CONTROL1,
TACNA_AUXPDM2_SRC_SHIFT,
tacna_auxpdm_in_texts);
static SOC_ENUM_SINGLE_DECL(tacna_auxpdm3_in,
TACNA_AUXPDM3_CONTROL1,
TACNA_AUXPDM3_SRC_SHIFT,
tacna_auxpdm_in_texts);
const struct snd_kcontrol_new tacna_auxpdm_inmux[] = {
SOC_DAPM_ENUM("AUXPDM1 Input", tacna_auxpdm1_in),
SOC_DAPM_ENUM("AUXPDM2 Input", tacna_auxpdm2_in),
SOC_DAPM_ENUM("AUXPDM3 Input", tacna_auxpdm3_in),
};
EXPORT_SYMBOL_GPL(tacna_auxpdm_inmux);
const struct snd_kcontrol_new tacna_auxpdm_switch[] = {
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(tacna_auxpdm_switch);
const struct soc_enum tacna_output_rate =
SOC_VALUE_ENUM_SINGLE(TACNA_OUTPUT_CONTROL_1,
TACNA_OUT_RATE_SHIFT,
TACNA_OUT_RATE_MASK >> TACNA_OUT_RATE_SHIFT,
TACNA_RATE_ENUM_SIZE,
tacna_rate_text,
tacna_rate_val);
EXPORT_SYMBOL_GPL(tacna_output_rate);
SOC_ENUM_SINGLE_DECL(tacna_out_vd_ramp,
TACNA_OUTPUT_VOLUME_RAMP,
TACNA_OUT_VD_RAMP_SHIFT,
tacna_vol_ramp_text);
EXPORT_SYMBOL_GPL(tacna_out_vd_ramp);
SOC_ENUM_SINGLE_DECL(tacna_out_vi_ramp,
TACNA_OUTPUT_VOLUME_RAMP,
TACNA_OUT_VI_RAMP_SHIFT,
tacna_vol_ramp_text);
EXPORT_SYMBOL_GPL(tacna_out_vi_ramp);
static const char * const tacna_anc_input_src_text[] = {
"None", "IN1", "IN2", "IN3", "IN4",
};
static const char * const tacna_mono_anc_channel_src_text[] = {
"None", "Left", "Right", "Left + Right",
};
const struct soc_enum tacna_mono_anc_input_src[] = {
SOC_ENUM_SINGLE(TACNA_ANC_SRC,
TACNA_IN_ANC_L_SRC_SHIFT,
ARRAY_SIZE(tacna_anc_input_src_text),
tacna_anc_input_src_text),
SOC_ENUM_SINGLE(TACNA_ANC_L_CTRL_2,
TACNA_ANC_L_MIC_SRC_SHIFT,
ARRAY_SIZE(tacna_mono_anc_channel_src_text),
tacna_mono_anc_channel_src_text),
};
EXPORT_SYMBOL_GPL(tacna_mono_anc_input_src);
static const char * const tacna_anc_ng_texts[] = {
"None", "Internal", "External",
};
SOC_ENUM_SINGLE_DECL(tacna_anc_ng_enum, SND_SOC_NOPM, 0, tacna_anc_ng_texts);
EXPORT_SYMBOL_GPL(tacna_anc_ng_enum);
static const char * const tacna_out_anc_src_text[] = {
"None", "ANC Left Channel", "ANC Right Channel",
};
const struct soc_enum tacna_output_anc_src[] = {
SOC_ENUM_SINGLE(TACNA_OUT1L_CONTROL_1,
TACNA_OUT1L_ANC_SRC_SHIFT,
ARRAY_SIZE(tacna_out_anc_src_text),
tacna_out_anc_src_text),
SOC_ENUM_SINGLE(TACNA_OUT1R_CONTROL_1,
TACNA_OUT1R_ANC_SRC_SHIFT,
ARRAY_SIZE(tacna_out_anc_src_text),
tacna_out_anc_src_text),
SOC_ENUM_SINGLE(TACNA_OUT2L_CONTROL_1,
TACNA_OUT2L_ANC_SRC_SHIFT,
ARRAY_SIZE(tacna_out_anc_src_text),
tacna_out_anc_src_text),
SOC_ENUM_SINGLE(TACNA_OUT2R_CONTROL_1,
TACNA_OUT2R_ANC_SRC_SHIFT,
ARRAY_SIZE(tacna_out_anc_src_text),
tacna_out_anc_src_text),
SOC_ENUM_SINGLE(TACNA_OUT5L_CONTROL_1,
TACNA_OUT5L_ANC_SRC_SHIFT,
ARRAY_SIZE(tacna_out_anc_src_text),
tacna_out_anc_src_text),
SOC_ENUM_SINGLE(TACNA_OUT5R_CONTROL_1,
TACNA_OUT5R_ANC_SRC_SHIFT,
ARRAY_SIZE(tacna_out_anc_src_text),
tacna_out_anc_src_text),
};
EXPORT_SYMBOL_GPL(tacna_output_anc_src);
int tacna_frf_bytes_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct soc_bytes *params = (void *)kcontrol->private_value;
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct tacna_priv *priv = snd_soc_codec_get_drvdata(codec);
struct tacna *tacna = priv->tacna;
int ret, len;
void *data;
len = params->num_regs * component->val_bytes;
data = kmemdup(ucontrol->value.bytes.data, len, GFP_KERNEL | GFP_DMA);
if (!data)
return -ENOMEM;
ret = regmap_raw_write(tacna->regmap, params->base, data, len);
kfree(data);
return ret;
}
EXPORT_SYMBOL_GPL(tacna_frf_bytes_put);
const struct soc_enum tacna_asrc1_rate[] = {
SOC_VALUE_ENUM_SINGLE(TACNA_ASRC1_CONTROL1,
TACNA_ASRC1_RATE1_SHIFT,
TACNA_ASRC1_RATE1_MASK >> TACNA_ASRC1_RATE1_SHIFT,
TACNA_RATE_ENUM_SIZE,
tacna_rate_text,
tacna_rate_val),
SOC_VALUE_ENUM_SINGLE(TACNA_ASRC1_CONTROL1,
TACNA_ASRC1_RATE2_SHIFT,
TACNA_ASRC1_RATE2_MASK >> TACNA_ASRC1_RATE2_SHIFT,
TACNA_RATE_ENUM_SIZE,
tacna_rate_text,
tacna_rate_val),
};
EXPORT_SYMBOL_GPL(tacna_asrc1_rate);
const struct soc_enum tacna_asrc2_rate[] = {
SOC_VALUE_ENUM_SINGLE(TACNA_ASRC2_CONTROL1,
TACNA_ASRC2_RATE1_SHIFT,
TACNA_ASRC2_RATE1_MASK >> TACNA_ASRC2_RATE1_SHIFT,
TACNA_RATE_ENUM_SIZE,
tacna_rate_text,
tacna_rate_val),
SOC_VALUE_ENUM_SINGLE(TACNA_ASRC2_CONTROL1,
TACNA_ASRC2_RATE2_SHIFT,
TACNA_ASRC2_RATE2_MASK >> TACNA_ASRC2_RATE2_SHIFT,
TACNA_RATE_ENUM_SIZE,
tacna_rate_text,
tacna_rate_val),
};
EXPORT_SYMBOL_GPL(tacna_asrc2_rate);
const struct soc_enum tacna_isrc_fsh[] = {
SOC_VALUE_ENUM_SINGLE(TACNA_ISRC1_CONTROL1,
TACNA_ISRC1_FSH_SHIFT,
TACNA_ISRC1_FSH_MASK >> TACNA_ISRC1_FSH_SHIFT,
TACNA_RATE_ENUM_SIZE,
tacna_rate_text,
tacna_rate_val),
SOC_VALUE_ENUM_SINGLE(TACNA_ISRC2_CONTROL1,
TACNA_ISRC2_FSH_SHIFT,
TACNA_ISRC2_FSH_MASK >> TACNA_ISRC2_FSH_SHIFT,
TACNA_RATE_ENUM_SIZE,
tacna_rate_text,
tacna_rate_val),
SOC_VALUE_ENUM_SINGLE(TACNA_ISRC3_CONTROL1,
TACNA_ISRC3_FSH_SHIFT,
TACNA_ISRC3_FSH_MASK >> TACNA_ISRC3_FSH_SHIFT,
TACNA_RATE_ENUM_SIZE,
tacna_rate_text,
tacna_rate_val),
};
EXPORT_SYMBOL_GPL(tacna_isrc_fsh);
const struct soc_enum tacna_isrc_fsl[] = {
SOC_VALUE_ENUM_SINGLE(TACNA_ISRC1_CONTROL1,
TACNA_ISRC1_FSL_SHIFT,
TACNA_ISRC1_FSL_MASK >> TACNA_ISRC1_FSL_SHIFT,
TACNA_RATE_ENUM_SIZE,
tacna_rate_text,
tacna_rate_val),
SOC_VALUE_ENUM_SINGLE(TACNA_ISRC2_CONTROL1,
TACNA_ISRC2_FSL_SHIFT,
TACNA_ISRC2_FSL_MASK >> TACNA_ISRC2_FSL_SHIFT,
TACNA_RATE_ENUM_SIZE,
tacna_rate_text,
tacna_rate_val),
SOC_VALUE_ENUM_SINGLE(TACNA_ISRC3_CONTROL1,
TACNA_ISRC3_FSL_SHIFT,
TACNA_ISRC3_FSL_MASK >> TACNA_ISRC3_FSL_SHIFT,
TACNA_RATE_ENUM_SIZE,
tacna_rate_text,
tacna_rate_val),
};
EXPORT_SYMBOL_GPL(tacna_isrc_fsl);
const struct soc_enum tacna_fx_rate =
SOC_VALUE_ENUM_SINGLE(TACNA_FX_SAMPLE_RATE,
TACNA_FX_RATE_SHIFT,
TACNA_FX_RATE_MASK >> TACNA_FX_RATE_SHIFT,
TACNA_RATE_ENUM_SIZE,
tacna_rate_text,
tacna_rate_val);
EXPORT_SYMBOL_GPL(tacna_fx_rate);
static const char * const tacna_lhpf_mode_text[] = {
"Low-pass", "High-pass"
};
SOC_ENUM_SINGLE_DECL(tacna_lhpf1_mode,
TACNA_LHPF_CONTROL2,
TACNA_LHPF1_MODE_SHIFT,
tacna_lhpf_mode_text);
EXPORT_SYMBOL_GPL(tacna_lhpf1_mode);
SOC_ENUM_SINGLE_DECL(tacna_lhpf2_mode,
TACNA_LHPF_CONTROL2,
TACNA_LHPF2_MODE_SHIFT,
tacna_lhpf_mode_text);
EXPORT_SYMBOL_GPL(tacna_lhpf2_mode);
SOC_ENUM_SINGLE_DECL(tacna_lhpf3_mode,
TACNA_LHPF_CONTROL2,
TACNA_LHPF3_MODE_SHIFT,
tacna_lhpf_mode_text);
EXPORT_SYMBOL_GPL(tacna_lhpf3_mode);
SOC_ENUM_SINGLE_DECL(tacna_lhpf4_mode,
TACNA_LHPF_CONTROL2,
TACNA_LHPF4_MODE_SHIFT,
tacna_lhpf_mode_text);
EXPORT_SYMBOL_GPL(tacna_lhpf4_mode);
int tacna_lhpf_coeff_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct tacna_priv *priv = snd_soc_codec_get_drvdata(codec);
__be32 *data = (__be32 *)ucontrol->value.bytes.data;
s16 val = (s16)be32_to_cpu(*data);
if (abs(val) >= 4096) {
dev_err(priv->dev, "Rejecting unstable LHPF coefficients\n");
return -EINVAL;
}
return snd_soc_bytes_put(kcontrol, ucontrol);
}
EXPORT_SYMBOL_GPL(tacna_lhpf_coeff_put);
static const char * const tacna_eq_mode_text[] = {
"Low-pass", "High-pass",
};
const struct soc_enum tacna_eq_mode[] = {
SOC_ENUM_SINGLE(TACNA_EQ_CONTROL2,
TACNA_EQ1_B1_MODE_SHIFT,
ARRAY_SIZE(tacna_eq_mode_text),
tacna_eq_mode_text),
SOC_ENUM_SINGLE(TACNA_EQ_CONTROL2,
TACNA_EQ2_B1_MODE_SHIFT,
ARRAY_SIZE(tacna_eq_mode_text),
tacna_eq_mode_text),
SOC_ENUM_SINGLE(TACNA_EQ_CONTROL2,
TACNA_EQ3_B1_MODE_SHIFT,
ARRAY_SIZE(tacna_eq_mode_text),
tacna_eq_mode_text),
SOC_ENUM_SINGLE(TACNA_EQ_CONTROL2,
TACNA_EQ4_B1_MODE_SHIFT,
ARRAY_SIZE(tacna_eq_mode_text),
tacna_eq_mode_text),
};
EXPORT_SYMBOL_GPL(tacna_eq_mode);
int tacna_eq_mode_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct tacna_priv *priv = snd_soc_codec_get_drvdata(codec);
struct soc_enum *e = (struct soc_enum *) kcontrol->private_value;
unsigned int item;
item = snd_soc_enum_val_to_item(e, priv->eq_mode[e->shift_l]);
ucontrol->value.enumerated.item[0] = item;
return 0;
}
EXPORT_SYMBOL_GPL(tacna_eq_mode_get);
int tacna_eq_mode_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
struct tacna_priv *priv = snd_soc_codec_get_drvdata(codec);
struct soc_enum *e = (struct soc_enum *) kcontrol->private_value;
unsigned int *item = ucontrol->value.enumerated.item;
unsigned int val;
if (item[0] >= e->items)
return -EINVAL;
val = snd_soc_enum_item_to_val(e, item[0]);
snd_soc_dapm_mutex_lock(dapm);
priv->eq_mode[e->shift_l] = val;
snd_soc_dapm_mutex_unlock(dapm);
return 0;
}
EXPORT_SYMBOL_GPL(tacna_eq_mode_put);
int tacna_eq_coeff_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
struct tacna_eq_control *ctl = (void *) kcontrol->private_value;
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 1;
uinfo->value.integer.min = 0;
uinfo->value.integer.max = ctl->max;
return 0;
}
EXPORT_SYMBOL_GPL(tacna_eq_coeff_info);
int tacna_eq_coeff_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct tacna_priv *priv = snd_soc_codec_get_drvdata(codec);
struct tacna_eq_control *params = (void *)kcontrol->private_value;
__be16 *coeffs;
unsigned int coeff_idx;
int block_idx;
block_idx = ((int) params->block_base - (int) TACNA_EQ1_BAND1_COEFF1);
block_idx /= 68;
coeffs = &priv->eq_coefficients[block_idx][0];
coeff_idx = (params->reg - params->block_base) / 2;
coeff_idx += ((params->shift == 0) ? 1 : 0);
ucontrol->value.integer.value[0] = be16_to_cpu(coeffs[coeff_idx]);
return 0;
}
EXPORT_SYMBOL_GPL(tacna_eq_coeff_get);
int tacna_eq_coeff_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
struct tacna_priv *priv = snd_soc_codec_get_drvdata(codec);
struct tacna_eq_control *params = (void *)kcontrol->private_value;
__be16 *coeffs;
unsigned int coeff_idx;
int block_idx;
block_idx = ((int) params->block_base - (int) TACNA_EQ1_BAND1_COEFF1);
block_idx /= 68;
coeffs = &priv->eq_coefficients[block_idx][0];
coeff_idx = (params->reg - params->block_base) / 2;
coeff_idx += ((params->shift == 0) ? 1 : 0);
snd_soc_dapm_mutex_lock(dapm);
coeffs[coeff_idx] = cpu_to_be16(ucontrol->value.integer.value[0]);
snd_soc_dapm_mutex_unlock(dapm);
return 0;
}
EXPORT_SYMBOL_GPL(tacna_eq_coeff_put);
const struct snd_kcontrol_new tacna_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(tacna_drc_activity_output_mux);
const char * const tacna_dfc_width_text[TACNA_DFC_WIDTH_ENUM_SIZE] = {
"8bit",
"9bit",
"10bit",
"11bit",
"12bit",
"13bit",
"14bit",
"15bit",
"16bit",
"17bit",
"18bit",
"19bit",
"20bit",
"21bit",
"22bit",
"23bit",
"24bit",
"25bit",
"26bit",
"27bit",
"28bit",
"29bit",
"30bit",
"31bit",
"32bit",
};
EXPORT_SYMBOL_GPL(tacna_dfc_width_text);
const unsigned int tacna_dfc_width_val[TACNA_DFC_WIDTH_ENUM_SIZE] = {
7, /* 8bit */
8, /* 9bit */
9, /* 10bit */
10, /* 11bit */
11, /* 12bit */
12, /* 13bit */
13, /* 14bit */
14, /* 15bit */
15, /* 16bit */
16, /* 17bit */
17, /* 18bit */
18, /* 19bit */
19, /* 20bit */
20, /* 21bit */
21, /* 22bit */
22, /* 23bit */
23, /* 24bit */
24, /* 25bit */
25, /* 26bit */
26, /* 27bit */
27, /* 28bit */
28, /* 29bit */
29, /* 30bit */
30, /* 31bit */
31, /* 32bit */
};
EXPORT_SYMBOL_GPL(tacna_dfc_width_val);
const char * const tacna_dfc_type_text[TACNA_DFC_TYPE_ENUM_SIZE] = {
"Fixed", "Unsigned Fixed", "Single Precision Floating",
"Half Precision Floating", "Arm Alternative Floating",
};
EXPORT_SYMBOL_GPL(tacna_dfc_type_text);
const unsigned int tacna_dfc_type_val[TACNA_DFC_TYPE_ENUM_SIZE] = {
0, 1, 2, 4, 5,
};
EXPORT_SYMBOL_GPL(tacna_dfc_type_val);
const struct soc_enum tacna_dfc_rate[] = {
SOC_VALUE_ENUM_SINGLE(TACNA_DFC1_CH1_CTRL,
TACNA_DFC1_CH1_RATE_SHIFT,
TACNA_DFC1_CH1_RATE_MASK >>
TACNA_DFC1_CH1_RATE_SHIFT,
TACNA_RATE_ENUM_SIZE,
tacna_rate_text,
tacna_rate_val),
SOC_VALUE_ENUM_SINGLE(TACNA_DFC1_CH2_CTRL,
TACNA_DFC1_CH2_RATE_SHIFT,
TACNA_DFC1_CH2_RATE_MASK >>
TACNA_DFC1_CH2_RATE_SHIFT,
TACNA_RATE_ENUM_SIZE,
tacna_rate_text,
tacna_rate_val),
SOC_VALUE_ENUM_SINGLE(TACNA_DFC1_CH3_CTRL,
TACNA_DFC1_CH3_RATE_SHIFT,
TACNA_DFC1_CH3_RATE_MASK >>
TACNA_DFC1_CH3_RATE_SHIFT,
TACNA_RATE_ENUM_SIZE,
tacna_rate_text,
tacna_rate_val),
SOC_VALUE_ENUM_SINGLE(TACNA_DFC1_CH4_CTRL,
TACNA_DFC1_CH4_RATE_SHIFT,
TACNA_DFC1_CH4_RATE_MASK >>
TACNA_DFC1_CH4_RATE_SHIFT,
TACNA_RATE_ENUM_SIZE,
tacna_rate_text,
tacna_rate_val),
SOC_VALUE_ENUM_SINGLE(TACNA_DFC1_CH5_CTRL,
TACNA_DFC1_CH5_RATE_SHIFT,
TACNA_DFC1_CH5_RATE_MASK >>
TACNA_DFC1_CH5_RATE_SHIFT,
TACNA_RATE_ENUM_SIZE,
tacna_rate_text,
tacna_rate_val),
SOC_VALUE_ENUM_SINGLE(TACNA_DFC1_CH6_CTRL,
TACNA_DFC1_CH6_RATE_SHIFT,
TACNA_DFC1_CH6_RATE_MASK >>
TACNA_DFC1_CH6_RATE_SHIFT,
TACNA_RATE_ENUM_SIZE,
tacna_rate_text,
tacna_rate_val),
SOC_VALUE_ENUM_SINGLE(TACNA_DFC1_CH7_CTRL,
TACNA_DFC1_CH7_RATE_SHIFT,
TACNA_DFC1_CH7_RATE_MASK >>
TACNA_DFC1_CH7_RATE_SHIFT,
TACNA_RATE_ENUM_SIZE,
tacna_rate_text,
tacna_rate_val),
SOC_VALUE_ENUM_SINGLE(TACNA_DFC1_CH8_CTRL,
TACNA_DFC1_CH8_RATE_SHIFT,
TACNA_DFC1_CH8_RATE_MASK >>
TACNA_DFC1_CH8_RATE_SHIFT,
TACNA_RATE_ENUM_SIZE,
tacna_rate_text,
tacna_rate_val),
};
EXPORT_SYMBOL_GPL(tacna_dfc_rate);
const struct soc_enum tacna_dfc_width[] = {
SOC_VALUE_ENUM_SINGLE(TACNA_DFC1_CH1_RX,
TACNA_DFC1_CH1_RX_DATA_WIDTH_SHIFT,
TACNA_DFC1_CH1_RX_DATA_WIDTH_MASK >>
TACNA_DFC1_CH1_RX_DATA_WIDTH_SHIFT,
ARRAY_SIZE(tacna_dfc_width_text),
tacna_dfc_width_text,
tacna_dfc_width_val),
SOC_VALUE_ENUM_SINGLE(TACNA_DFC1_CH1_TX,
TACNA_DFC1_CH1_TX_DATA_WIDTH_SHIFT,
TACNA_DFC1_CH1_TX_DATA_WIDTH_MASK >>
TACNA_DFC1_CH1_TX_DATA_WIDTH_SHIFT,
ARRAY_SIZE(tacna_dfc_width_text),
tacna_dfc_width_text,
tacna_dfc_width_val),
SOC_VALUE_ENUM_SINGLE(TACNA_DFC1_CH2_RX,
TACNA_DFC1_CH2_RX_DATA_WIDTH_SHIFT,
TACNA_DFC1_CH2_RX_DATA_WIDTH_MASK >>
TACNA_DFC1_CH2_RX_DATA_WIDTH_SHIFT,
ARRAY_SIZE(tacna_dfc_width_text),
tacna_dfc_width_text,
tacna_dfc_width_val),
SOC_VALUE_ENUM_SINGLE(TACNA_DFC1_CH2_TX,
TACNA_DFC1_CH2_TX_DATA_WIDTH_SHIFT,
TACNA_DFC1_CH2_TX_DATA_WIDTH_MASK >>
TACNA_DFC1_CH2_TX_DATA_WIDTH_SHIFT,
ARRAY_SIZE(tacna_dfc_width_text),
tacna_dfc_width_text,
tacna_dfc_width_val),
SOC_VALUE_ENUM_SINGLE(TACNA_DFC1_CH3_RX,
TACNA_DFC1_CH3_RX_DATA_WIDTH_SHIFT,
TACNA_DFC1_CH3_RX_DATA_WIDTH_MASK >>
TACNA_DFC1_CH3_RX_DATA_WIDTH_SHIFT,
ARRAY_SIZE(tacna_dfc_width_text),
tacna_dfc_width_text,
tacna_dfc_width_val),
SOC_VALUE_ENUM_SINGLE(TACNA_DFC1_CH3_TX,
TACNA_DFC1_CH3_TX_DATA_WIDTH_SHIFT,
TACNA_DFC1_CH3_TX_DATA_WIDTH_MASK >>
TACNA_DFC1_CH3_TX_DATA_WIDTH_SHIFT,
ARRAY_SIZE(tacna_dfc_width_text),
tacna_dfc_width_text,
tacna_dfc_width_val),
SOC_VALUE_ENUM_SINGLE(TACNA_DFC1_CH4_RX,
TACNA_DFC1_CH4_RX_DATA_WIDTH_SHIFT,
TACNA_DFC1_CH4_RX_DATA_WIDTH_MASK >>
TACNA_DFC1_CH4_RX_DATA_WIDTH_SHIFT,
ARRAY_SIZE(tacna_dfc_width_text),
tacna_dfc_width_text,
tacna_dfc_width_val),
SOC_VALUE_ENUM_SINGLE(TACNA_DFC1_CH4_TX,
TACNA_DFC1_CH4_TX_DATA_WIDTH_SHIFT,
TACNA_DFC1_CH4_TX_DATA_WIDTH_MASK >>
TACNA_DFC1_CH4_TX_DATA_WIDTH_SHIFT,
ARRAY_SIZE(tacna_dfc_width_text),
tacna_dfc_width_text,
tacna_dfc_width_val),
SOC_VALUE_ENUM_SINGLE(TACNA_DFC1_CH5_RX,
TACNA_DFC1_CH5_RX_DATA_WIDTH_SHIFT,
TACNA_DFC1_CH5_RX_DATA_WIDTH_MASK >>
TACNA_DFC1_CH5_RX_DATA_WIDTH_SHIFT,
ARRAY_SIZE(tacna_dfc_width_text),
tacna_dfc_width_text,
tacna_dfc_width_val),
SOC_VALUE_ENUM_SINGLE(TACNA_DFC1_CH5_TX,
TACNA_DFC1_CH5_TX_DATA_WIDTH_SHIFT,
TACNA_DFC1_CH5_TX_DATA_WIDTH_MASK >>
TACNA_DFC1_CH5_TX_DATA_WIDTH_SHIFT,
ARRAY_SIZE(tacna_dfc_width_text),
tacna_dfc_width_text,
tacna_dfc_width_val),
SOC_VALUE_ENUM_SINGLE(TACNA_DFC1_CH6_RX,
TACNA_DFC1_CH6_RX_DATA_WIDTH_SHIFT,
TACNA_DFC1_CH6_RX_DATA_WIDTH_MASK >>
TACNA_DFC1_CH6_RX_DATA_WIDTH_SHIFT,
ARRAY_SIZE(tacna_dfc_width_text),
tacna_dfc_width_text,
tacna_dfc_width_val),
SOC_VALUE_ENUM_SINGLE(TACNA_DFC1_CH6_TX,
TACNA_DFC1_CH6_TX_DATA_WIDTH_SHIFT,
TACNA_DFC1_CH6_TX_DATA_WIDTH_MASK >>
TACNA_DFC1_CH6_TX_DATA_WIDTH_SHIFT,
ARRAY_SIZE(tacna_dfc_width_text),
tacna_dfc_width_text,
tacna_dfc_width_val),
SOC_VALUE_ENUM_SINGLE(TACNA_DFC1_CH7_RX,
TACNA_DFC1_CH7_RX_DATA_WIDTH_SHIFT,
TACNA_DFC1_CH7_RX_DATA_WIDTH_MASK >>
TACNA_DFC1_CH7_RX_DATA_WIDTH_SHIFT,
ARRAY_SIZE(tacna_dfc_width_text),
tacna_dfc_width_text,
tacna_dfc_width_val),
SOC_VALUE_ENUM_SINGLE(TACNA_DFC1_CH7_TX,
TACNA_DFC1_CH7_TX_DATA_WIDTH_SHIFT,
TACNA_DFC1_CH7_TX_DATA_WIDTH_MASK >>
TACNA_DFC1_CH7_TX_DATA_WIDTH_SHIFT,
ARRAY_SIZE(tacna_dfc_width_text),
tacna_dfc_width_text,
tacna_dfc_width_val),
SOC_VALUE_ENUM_SINGLE(TACNA_DFC1_CH8_RX,
TACNA_DFC1_CH8_RX_DATA_WIDTH_SHIFT,
TACNA_DFC1_CH8_RX_DATA_WIDTH_MASK >>
TACNA_DFC1_CH8_RX_DATA_WIDTH_SHIFT,
ARRAY_SIZE(tacna_dfc_width_text),
tacna_dfc_width_text,
tacna_dfc_width_val),
SOC_VALUE_ENUM_SINGLE(TACNA_DFC1_CH8_TX,
TACNA_DFC1_CH8_TX_DATA_WIDTH_SHIFT,
TACNA_DFC1_CH8_TX_DATA_WIDTH_MASK >>
TACNA_DFC1_CH8_TX_DATA_WIDTH_SHIFT,
ARRAY_SIZE(tacna_dfc_width_text),
tacna_dfc_width_text,
tacna_dfc_width_val),
};
EXPORT_SYMBOL_GPL(tacna_dfc_width);
const struct soc_enum tacna_dfc_type[] = {
SOC_VALUE_ENUM_SINGLE(TACNA_DFC1_CH1_RX,
TACNA_DFC1_CH1_RX_DATA_TYPE_SHIFT,
TACNA_DFC1_CH1_RX_DATA_TYPE_MASK >>
TACNA_DFC1_CH1_RX_DATA_TYPE_SHIFT,
ARRAY_SIZE(tacna_dfc_type_text),
tacna_dfc_type_text,
tacna_dfc_type_val),
SOC_VALUE_ENUM_SINGLE(TACNA_DFC1_CH1_TX,
TACNA_DFC1_CH1_TX_DATA_TYPE_SHIFT,
TACNA_DFC1_CH1_TX_DATA_TYPE_MASK >>
TACNA_DFC1_CH1_TX_DATA_TYPE_SHIFT,
ARRAY_SIZE(tacna_dfc_type_text),
tacna_dfc_type_text,
tacna_dfc_type_val),
SOC_VALUE_ENUM_SINGLE(TACNA_DFC1_CH2_RX,
TACNA_DFC1_CH2_RX_DATA_TYPE_SHIFT,
TACNA_DFC1_CH2_RX_DATA_TYPE_MASK >>
TACNA_DFC1_CH2_RX_DATA_TYPE_SHIFT,
ARRAY_SIZE(tacna_dfc_type_text),
tacna_dfc_type_text,
tacna_dfc_type_val),
SOC_VALUE_ENUM_SINGLE(TACNA_DFC1_CH2_TX,
TACNA_DFC1_CH2_TX_DATA_TYPE_SHIFT,
TACNA_DFC1_CH2_TX_DATA_TYPE_MASK >>
TACNA_DFC1_CH2_TX_DATA_TYPE_SHIFT,
ARRAY_SIZE(tacna_dfc_type_text),
tacna_dfc_type_text,
tacna_dfc_type_val),
SOC_VALUE_ENUM_SINGLE(TACNA_DFC1_CH3_RX,
TACNA_DFC1_CH3_RX_DATA_TYPE_SHIFT,
TACNA_DFC1_CH3_RX_DATA_TYPE_MASK >>
TACNA_DFC1_CH3_RX_DATA_TYPE_SHIFT,
ARRAY_SIZE(tacna_dfc_type_text),
tacna_dfc_type_text,
tacna_dfc_type_val),
SOC_VALUE_ENUM_SINGLE(TACNA_DFC1_CH3_TX,
TACNA_DFC1_CH3_TX_DATA_TYPE_SHIFT,
TACNA_DFC1_CH3_TX_DATA_TYPE_MASK >>
TACNA_DFC1_CH3_TX_DATA_TYPE_SHIFT,
ARRAY_SIZE(tacna_dfc_type_text),
tacna_dfc_type_text,
tacna_dfc_type_val),
SOC_VALUE_ENUM_SINGLE(TACNA_DFC1_CH4_RX,
TACNA_DFC1_CH4_RX_DATA_TYPE_SHIFT,
TACNA_DFC1_CH4_RX_DATA_TYPE_MASK >>
TACNA_DFC1_CH4_RX_DATA_TYPE_SHIFT,
ARRAY_SIZE(tacna_dfc_type_text),
tacna_dfc_type_text,
tacna_dfc_type_val),
SOC_VALUE_ENUM_SINGLE(TACNA_DFC1_CH4_TX,
TACNA_DFC1_CH4_TX_DATA_TYPE_SHIFT,
TACNA_DFC1_CH4_TX_DATA_TYPE_MASK >>
TACNA_DFC1_CH4_TX_DATA_TYPE_SHIFT,
ARRAY_SIZE(tacna_dfc_type_text),
tacna_dfc_type_text,
tacna_dfc_type_val),
SOC_VALUE_ENUM_SINGLE(TACNA_DFC1_CH5_RX,
TACNA_DFC1_CH5_RX_DATA_TYPE_SHIFT,
TACNA_DFC1_CH5_RX_DATA_TYPE_MASK >>
TACNA_DFC1_CH5_RX_DATA_TYPE_SHIFT,
ARRAY_SIZE(tacna_dfc_type_text),
tacna_dfc_type_text,
tacna_dfc_type_val),
SOC_VALUE_ENUM_SINGLE(TACNA_DFC1_CH5_TX,
TACNA_DFC1_CH5_TX_DATA_TYPE_SHIFT,
TACNA_DFC1_CH5_TX_DATA_TYPE_MASK >>
TACNA_DFC1_CH5_TX_DATA_TYPE_SHIFT,
ARRAY_SIZE(tacna_dfc_type_text),
tacna_dfc_type_text,
tacna_dfc_type_val),
SOC_VALUE_ENUM_SINGLE(TACNA_DFC1_CH6_RX,
TACNA_DFC1_CH6_RX_DATA_TYPE_SHIFT,
TACNA_DFC1_CH6_RX_DATA_TYPE_MASK >>
TACNA_DFC1_CH6_RX_DATA_TYPE_SHIFT,
ARRAY_SIZE(tacna_dfc_type_text),
tacna_dfc_type_text,
tacna_dfc_type_val),
SOC_VALUE_ENUM_SINGLE(TACNA_DFC1_CH6_TX,
TACNA_DFC1_CH6_TX_DATA_TYPE_SHIFT,
TACNA_DFC1_CH6_TX_DATA_TYPE_MASK >>
TACNA_DFC1_CH6_TX_DATA_TYPE_SHIFT,
ARRAY_SIZE(tacna_dfc_type_text),
tacna_dfc_type_text,
tacna_dfc_type_val),
SOC_VALUE_ENUM_SINGLE(TACNA_DFC1_CH7_RX,
TACNA_DFC1_CH7_RX_DATA_TYPE_SHIFT,
TACNA_DFC1_CH7_RX_DATA_TYPE_MASK >>
TACNA_DFC1_CH7_RX_DATA_TYPE_SHIFT,
ARRAY_SIZE(tacna_dfc_type_text),
tacna_dfc_type_text,
tacna_dfc_type_val),
SOC_VALUE_ENUM_SINGLE(TACNA_DFC1_CH7_TX,
TACNA_DFC1_CH7_TX_DATA_TYPE_SHIFT,
TACNA_DFC1_CH7_TX_DATA_TYPE_MASK >>
TACNA_DFC1_CH7_TX_DATA_TYPE_SHIFT,
ARRAY_SIZE(tacna_dfc_type_text),
tacna_dfc_type_text,
tacna_dfc_type_val),
SOC_VALUE_ENUM_SINGLE(TACNA_DFC1_CH8_RX,
TACNA_DFC1_CH8_RX_DATA_TYPE_SHIFT,
TACNA_DFC1_CH8_RX_DATA_TYPE_MASK >>
TACNA_DFC1_CH8_RX_DATA_TYPE_SHIFT,
ARRAY_SIZE(tacna_dfc_type_text),
tacna_dfc_type_text,
tacna_dfc_type_val),
SOC_VALUE_ENUM_SINGLE(TACNA_DFC1_CH8_TX,
TACNA_DFC1_CH8_TX_DATA_TYPE_SHIFT,
TACNA_DFC1_CH8_TX_DATA_TYPE_MASK >>
TACNA_DFC1_CH8_TX_DATA_TYPE_SHIFT,
ARRAY_SIZE(tacna_dfc_type_text),
tacna_dfc_type_text,
tacna_dfc_type_val),
};
EXPORT_SYMBOL_GPL(tacna_dfc_type);
int tacna_dfc_dith_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
struct soc_mixer_control *mc =
(struct soc_mixer_control *) kcontrol->private_value;
unsigned int val;
int ret;
snd_soc_dapm_mutex_lock(dapm);
/* Cannot change dfc settings when its on */
val = snd_soc_read(codec, mc->reg);
if (val & TACNA_DFC1_CH1_EN) {
ret = -EBUSY;
goto exit;
}
ret = snd_soc_put_volsw(kcontrol, ucontrol);
exit:
snd_soc_dapm_mutex_unlock(dapm);
return ret;
}
EXPORT_SYMBOL_GPL(tacna_dfc_dith_put);
int tacna_dfc_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
unsigned int reg = e->reg;
unsigned int val;
int ret = 0;
reg = TACNA_DFC1_CH1_CTRL + ((reg - TACNA_DFC1_CH1_CTRL) / 12) * 12;
snd_soc_dapm_mutex_lock(dapm);
/* Cannot change dfc settings when its on */
val = snd_soc_read(codec, reg);
if (val & TACNA_DFC1_CH1_EN) {
ret = -EBUSY;
goto exit;
}
ret = snd_soc_put_enum_double(kcontrol, ucontrol);
exit:
snd_soc_dapm_mutex_unlock(dapm);
return ret;
}
EXPORT_SYMBOL_GPL(tacna_dfc_put);
const struct snd_kcontrol_new tacna_dsp_trigger_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(tacna_dsp_trigger_output_mux);
int tacna_dsp_rate_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
struct tacna_priv *priv = snd_soc_codec_get_drvdata(codec);
struct soc_enum *e = (struct soc_enum *) kcontrol->private_value;
unsigned int cached_rate;
const int dsp_num = e->shift_l;
const unsigned int rate_num = e->mask & TACNA_DSP_RATE_CTL_CHAN_MASK;
int item;
snd_soc_dapm_mutex_lock(dapm);
switch (e->mask & TACNA_DSP_RATE_CTL_DIR_MASK) {
case TACNA_DSP_RATE_CTL_DIR_TX:
if (rate_num > priv->dsp[dsp_num].n_tx_channels)
goto ovf_err;
cached_rate = priv->dsp[dsp_num].tx_rate_cache[rate_num];
break;
default:
if (rate_num > priv->dsp[dsp_num].n_rx_channels)
goto ovf_err;
cached_rate = priv->dsp[dsp_num].rx_rate_cache[rate_num];
break;
}
snd_soc_dapm_mutex_unlock(dapm);
item = snd_soc_enum_val_to_item(e, cached_rate);
ucontrol->value.enumerated.item[0] = item;
return 0;
ovf_err:
dev_err(codec->dev, "DSP%u%s %cX rate control exceeds rate array\n",
dsp_num + 1, priv->dsp[dsp_num].suffix,
(e->mask & TACNA_DSP_RATE_CTL_DIR_MASK) ? 'T' : 'R');
return -EINVAL;
}
EXPORT_SYMBOL_GPL(tacna_dsp_rate_get);
int tacna_dsp_rate_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
struct tacna_priv *priv = snd_soc_codec_get_drvdata(codec);
struct soc_enum *e = (struct soc_enum *) kcontrol->private_value;
const int dsp_num = e->shift_l;
const unsigned int rate_num = e->mask & TACNA_DSP_RATE_CTL_CHAN_MASK;
const unsigned int item = ucontrol->value.enumerated.item[0];
if (item >= e->items)
return -EINVAL;
snd_soc_dapm_mutex_lock(dapm);
switch (e->mask & TACNA_DSP_RATE_CTL_DIR_MASK) {
case TACNA_DSP_RATE_CTL_DIR_TX:
if (rate_num > priv->dsp[dsp_num].n_tx_channels)
goto ovf_err;
priv->dsp[dsp_num].tx_rate_cache[rate_num] =
e->values[item];
break;
default:
if (rate_num > priv->dsp[dsp_num].n_rx_channels)
goto ovf_err;
priv->dsp[dsp_num].rx_rate_cache[rate_num] =
e->values[item];
break;
}
snd_soc_dapm_mutex_unlock(dapm);
return 0;
ovf_err:
dev_err(codec->dev, "DSP%u%s %cX rate control exceeds rate array\n",
dsp_num + 1, priv->dsp[dsp_num].suffix,
(e->mask & TACNA_DSP_RATE_CTL_DIR_MASK) ? 'T' : 'R');
return -EINVAL;
}
EXPORT_SYMBOL_GPL(tacna_dsp_rate_put);
const struct soc_enum tacna_dsp1_rx_rate_enum[] = {
SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
0 | TACNA_DSP_RATE_CTL_DIR_RX,
TACNA_RATE_ENUM_SIZE,
tacna_rate_text, tacna_rate_val),
SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
1 | TACNA_DSP_RATE_CTL_DIR_RX,
TACNA_RATE_ENUM_SIZE,
tacna_rate_text, tacna_rate_val),
SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
2 | TACNA_DSP_RATE_CTL_DIR_RX,
TACNA_RATE_ENUM_SIZE,
tacna_rate_text, tacna_rate_val),
SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
3 | TACNA_DSP_RATE_CTL_DIR_RX,
TACNA_RATE_ENUM_SIZE,
tacna_rate_text, tacna_rate_val),
SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
4 | TACNA_DSP_RATE_CTL_DIR_RX,
TACNA_RATE_ENUM_SIZE,
tacna_rate_text, tacna_rate_val),
SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
5 | TACNA_DSP_RATE_CTL_DIR_RX,
TACNA_RATE_ENUM_SIZE,
tacna_rate_text, tacna_rate_val),
SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
6 | TACNA_DSP_RATE_CTL_DIR_RX,
TACNA_RATE_ENUM_SIZE,
tacna_rate_text, tacna_rate_val),
SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
7 | TACNA_DSP_RATE_CTL_DIR_RX,
TACNA_RATE_ENUM_SIZE,
tacna_rate_text, tacna_rate_val),
SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
8 | TACNA_DSP_RATE_CTL_DIR_RX,
TACNA_RATE_ENUM_SIZE,
tacna_rate_text, tacna_rate_val),
SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
9 | TACNA_DSP_RATE_CTL_DIR_RX,
TACNA_RATE_ENUM_SIZE,
tacna_rate_text, tacna_rate_val),
SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
10 | TACNA_DSP_RATE_CTL_DIR_RX,
TACNA_RATE_ENUM_SIZE,
tacna_rate_text, tacna_rate_val),
SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
11 | TACNA_DSP_RATE_CTL_DIR_RX,
TACNA_RATE_ENUM_SIZE,
tacna_rate_text, tacna_rate_val),
};
EXPORT_SYMBOL_GPL(tacna_dsp1_rx_rate_enum);
const struct soc_enum tacna_dsp1_tx_rate_enum[] = {
SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
0 | TACNA_DSP_RATE_CTL_DIR_TX,
TACNA_RATE_ENUM_SIZE,
tacna_rate_text, tacna_rate_val),
SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
1 | TACNA_DSP_RATE_CTL_DIR_TX,
TACNA_RATE_ENUM_SIZE,
tacna_rate_text, tacna_rate_val),
SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
2 | TACNA_DSP_RATE_CTL_DIR_TX,
TACNA_RATE_ENUM_SIZE,
tacna_rate_text, tacna_rate_val),
SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
3 | TACNA_DSP_RATE_CTL_DIR_TX,
TACNA_RATE_ENUM_SIZE,
tacna_rate_text, tacna_rate_val),
SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
4 | TACNA_DSP_RATE_CTL_DIR_TX,
TACNA_RATE_ENUM_SIZE,
tacna_rate_text, tacna_rate_val),
SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
5 | TACNA_DSP_RATE_CTL_DIR_TX,
TACNA_RATE_ENUM_SIZE,
tacna_rate_text, tacna_rate_val),
SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
6 | TACNA_DSP_RATE_CTL_DIR_TX,
TACNA_RATE_ENUM_SIZE,
tacna_rate_text, tacna_rate_val),
SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
7 | TACNA_DSP_RATE_CTL_DIR_TX,
TACNA_RATE_ENUM_SIZE,
tacna_rate_text, tacna_rate_val),
SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
8 | TACNA_DSP_RATE_CTL_DIR_TX,
TACNA_RATE_ENUM_SIZE,
tacna_rate_text, tacna_rate_val),
};
EXPORT_SYMBOL_GPL(tacna_dsp1_tx_rate_enum);
static const struct soc_enum tacna_dsp2_rx_rate_enum[] = {
SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 1,
0 | TACNA_DSP_RATE_CTL_DIR_RX,
TACNA_RATE_ENUM_SIZE,
tacna_rate_text, tacna_rate_val),
SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 1,
1 | TACNA_DSP_RATE_CTL_DIR_RX,
TACNA_RATE_ENUM_SIZE,
tacna_rate_text, tacna_rate_val),
SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 1,
2 | TACNA_DSP_RATE_CTL_DIR_RX,
TACNA_RATE_ENUM_SIZE,
tacna_rate_text, tacna_rate_val),
SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 1,
3 | TACNA_DSP_RATE_CTL_DIR_RX,
TACNA_RATE_ENUM_SIZE,
tacna_rate_text, tacna_rate_val),
SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 1,
4 | TACNA_DSP_RATE_CTL_DIR_RX,
TACNA_RATE_ENUM_SIZE,
tacna_rate_text, tacna_rate_val),
SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 1,
5 | TACNA_DSP_RATE_CTL_DIR_RX,
TACNA_RATE_ENUM_SIZE,
tacna_rate_text, tacna_rate_val),
SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 1,
6 | TACNA_DSP_RATE_CTL_DIR_RX,
TACNA_RATE_ENUM_SIZE,
tacna_rate_text, tacna_rate_val),
SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 1,
7 | TACNA_DSP_RATE_CTL_DIR_RX,
TACNA_RATE_ENUM_SIZE,
tacna_rate_text, tacna_rate_val),
};
static const struct soc_enum tacna_dsp2_tx_rate_enum[] = {
SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 1,
0 | TACNA_DSP_RATE_CTL_DIR_TX,
TACNA_RATE_ENUM_SIZE,
tacna_rate_text, tacna_rate_val),
SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 1,
1 | TACNA_DSP_RATE_CTL_DIR_TX,
TACNA_RATE_ENUM_SIZE,
tacna_rate_text, tacna_rate_val),
SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 1,
2 | TACNA_DSP_RATE_CTL_DIR_TX,
TACNA_RATE_ENUM_SIZE,
tacna_rate_text, tacna_rate_val),
SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 1,
3 | TACNA_DSP_RATE_CTL_DIR_TX,
TACNA_RATE_ENUM_SIZE,
tacna_rate_text, tacna_rate_val),
SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 1,
4 | TACNA_DSP_RATE_CTL_DIR_TX,
TACNA_RATE_ENUM_SIZE,
tacna_rate_text, tacna_rate_val),
SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 1,
5 | TACNA_DSP_RATE_CTL_DIR_TX,
TACNA_RATE_ENUM_SIZE,
tacna_rate_text, tacna_rate_val),
SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 1,
6 | TACNA_DSP_RATE_CTL_DIR_TX,
TACNA_RATE_ENUM_SIZE,
tacna_rate_text, tacna_rate_val),
SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 1,
7 | TACNA_DSP_RATE_CTL_DIR_TX,
TACNA_RATE_ENUM_SIZE,
tacna_rate_text, tacna_rate_val),
};
static const struct snd_kcontrol_new tacna_dsp1_rx_rate_controls[] = {
SOC_ENUM_EXT("DSP1RX1 Rate", tacna_dsp1_rx_rate_enum[0],
tacna_dsp_rate_get, tacna_dsp_rate_put),
SOC_ENUM_EXT("DSP1RX2 Rate", tacna_dsp1_rx_rate_enum[1],
tacna_dsp_rate_get, tacna_dsp_rate_put),
SOC_ENUM_EXT("DSP1RX3 Rate", tacna_dsp1_rx_rate_enum[2],
tacna_dsp_rate_get, tacna_dsp_rate_put),
SOC_ENUM_EXT("DSP1RX4 Rate", tacna_dsp1_rx_rate_enum[3],
tacna_dsp_rate_get, tacna_dsp_rate_put),
SOC_ENUM_EXT("DSP1RX5 Rate", tacna_dsp1_rx_rate_enum[4],
tacna_dsp_rate_get, tacna_dsp_rate_put),
SOC_ENUM_EXT("DSP1RX6 Rate", tacna_dsp1_rx_rate_enum[5],
tacna_dsp_rate_get, tacna_dsp_rate_put),
SOC_ENUM_EXT("DSP1RX7 Rate", tacna_dsp1_rx_rate_enum[6],
tacna_dsp_rate_get, tacna_dsp_rate_put),
SOC_ENUM_EXT("DSP1RX8 Rate", tacna_dsp1_rx_rate_enum[7],
tacna_dsp_rate_get, tacna_dsp_rate_put),
SOC_ENUM_EXT("DSP1RX9 Rate", tacna_dsp1_rx_rate_enum[8],
tacna_dsp_rate_get, tacna_dsp_rate_put),
SOC_ENUM_EXT("DSP1RX10 Rate", tacna_dsp1_rx_rate_enum[9],
tacna_dsp_rate_get, tacna_dsp_rate_put),
SOC_ENUM_EXT("DSP1RX11 Rate", tacna_dsp1_rx_rate_enum[10],
tacna_dsp_rate_get, tacna_dsp_rate_put),
SOC_ENUM_EXT("DSP1RX12 Rate", tacna_dsp1_rx_rate_enum[11],
tacna_dsp_rate_get, tacna_dsp_rate_put),
};
static const struct snd_kcontrol_new tacna_dsp1_tx_rate_controls[] = {
SOC_ENUM_EXT("DSP1TX1 Rate", tacna_dsp1_tx_rate_enum[0],
tacna_dsp_rate_get, tacna_dsp_rate_put),
SOC_ENUM_EXT("DSP1TX2 Rate", tacna_dsp1_tx_rate_enum[1],
tacna_dsp_rate_get, tacna_dsp_rate_put),
SOC_ENUM_EXT("DSP1TX3 Rate", tacna_dsp1_tx_rate_enum[2],
tacna_dsp_rate_get, tacna_dsp_rate_put),
SOC_ENUM_EXT("DSP1TX4 Rate", tacna_dsp1_tx_rate_enum[3],
tacna_dsp_rate_get, tacna_dsp_rate_put),
SOC_ENUM_EXT("DSP1TX5 Rate", tacna_dsp1_tx_rate_enum[4],
tacna_dsp_rate_get, tacna_dsp_rate_put),
SOC_ENUM_EXT("DSP1TX6 Rate", tacna_dsp1_tx_rate_enum[5],
tacna_dsp_rate_get, tacna_dsp_rate_put),
SOC_ENUM_EXT("DSP1TX7 Rate", tacna_dsp1_tx_rate_enum[6],
tacna_dsp_rate_get, tacna_dsp_rate_put),
SOC_ENUM_EXT("DSP1TX8 Rate", tacna_dsp1_tx_rate_enum[7],
tacna_dsp_rate_get, tacna_dsp_rate_put),
SOC_ENUM_EXT("DSP1TX9 Rate", tacna_dsp1_tx_rate_enum[8],
tacna_dsp_rate_get, tacna_dsp_rate_put),
};
static const struct snd_kcontrol_new tacna_dsp2_rx_rate_controls[] = {
SOC_ENUM_EXT("DSP2RX1 Rate", tacna_dsp2_rx_rate_enum[0],
tacna_dsp_rate_get, tacna_dsp_rate_put),
SOC_ENUM_EXT("DSP2RX2 Rate", tacna_dsp2_rx_rate_enum[1],
tacna_dsp_rate_get, tacna_dsp_rate_put),
SOC_ENUM_EXT("DSP2RX3 Rate", tacna_dsp2_rx_rate_enum[2],
tacna_dsp_rate_get, tacna_dsp_rate_put),
SOC_ENUM_EXT("DSP2RX4 Rate", tacna_dsp2_rx_rate_enum[3],
tacna_dsp_rate_get, tacna_dsp_rate_put),
SOC_ENUM_EXT("DSP2RX5 Rate", tacna_dsp2_rx_rate_enum[4],
tacna_dsp_rate_get, tacna_dsp_rate_put),
SOC_ENUM_EXT("DSP2RX6 Rate", tacna_dsp2_rx_rate_enum[5],
tacna_dsp_rate_get, tacna_dsp_rate_put),
SOC_ENUM_EXT("DSP2RX7 Rate", tacna_dsp2_rx_rate_enum[6],
tacna_dsp_rate_get, tacna_dsp_rate_put),
SOC_ENUM_EXT("DSP2RX8 Rate", tacna_dsp2_rx_rate_enum[7],
tacna_dsp_rate_get, tacna_dsp_rate_put),
};
static const struct snd_kcontrol_new tacna_dsp2_tx_rate_controls[] = {
SOC_ENUM_EXT("DSP2TX1 Rate", tacna_dsp2_tx_rate_enum[0],
tacna_dsp_rate_get, tacna_dsp_rate_put),
SOC_ENUM_EXT("DSP2TX2 Rate", tacna_dsp2_tx_rate_enum[1],
tacna_dsp_rate_get, tacna_dsp_rate_put),
SOC_ENUM_EXT("DSP2TX3 Rate", tacna_dsp2_tx_rate_enum[2],
tacna_dsp_rate_get, tacna_dsp_rate_put),
SOC_ENUM_EXT("DSP2TX4 Rate", tacna_dsp2_tx_rate_enum[3],
tacna_dsp_rate_get, tacna_dsp_rate_put),
SOC_ENUM_EXT("DSP2TX5 Rate", tacna_dsp2_tx_rate_enum[4],
tacna_dsp_rate_get, tacna_dsp_rate_put),
SOC_ENUM_EXT("DSP2TX6 Rate", tacna_dsp2_tx_rate_enum[5],
tacna_dsp_rate_get, tacna_dsp_rate_put),
SOC_ENUM_EXT("DSP2TX7 Rate", tacna_dsp2_tx_rate_enum[6],
tacna_dsp_rate_get, tacna_dsp_rate_put),
SOC_ENUM_EXT("DSP2TX8 Rate", tacna_dsp2_tx_rate_enum[7],
tacna_dsp_rate_get, tacna_dsp_rate_put),
};
static const struct snd_kcontrol_new *tacna_rx_rate_controls[] = {
tacna_dsp1_rx_rate_controls,
tacna_dsp2_rx_rate_controls
};
static const struct snd_kcontrol_new *tacna_tx_rate_controls[] = {
tacna_dsp1_tx_rate_controls,
tacna_dsp2_tx_rate_controls
};
int tacna_dsp_add_codec_controls(struct snd_soc_codec *codec,
unsigned int dsp_n)
{
struct tacna_priv *priv = snd_soc_codec_get_drvdata(codec);
int i, ret;
for (i = 0; i < dsp_n; i++) {
ret = snd_soc_add_codec_controls(codec,
tacna_rx_rate_controls[i],
priv->dsp[i].n_rx_channels);
if (ret)
return ret;
ret = snd_soc_add_codec_controls(codec,
tacna_tx_rate_controls[i],
priv->dsp[i].n_tx_channels);
if (ret)
return ret;
}
return 0;
};
EXPORT_SYMBOL_GPL(tacna_dsp_add_codec_controls);
int tacna_dsp_memory_enable(struct tacna_priv *priv,
const struct tacna_dsp_power_regs *regs)
{
struct regmap *regmap = priv->tacna->regmap;
int i, ret;
/* disable power-off */
for (i = 0; i < regs->n_ext; ++i) {
ret = regmap_write(regmap, regs->ext[i], 0x3);
if (ret)
goto err;
}
/* power-up the banks in sequence */
for (i = 0; i < regs->n_pwd; ++i) {
ret = regmap_write(regmap, regs->pwd[i], 0x1);
if (ret)
goto err;
udelay(1); /* allow bank to power-up */
ret = regmap_write(regmap, regs->pwd[i], 0x3);
if (ret)
goto err;
udelay(1); /* allow bank to power-up */
}
return 0;
err:
dev_err(priv->dev, "Failed to write SRAM enables (%d)\n", ret);
tacna_dsp_memory_disable(priv, regs);
return ret;
}
EXPORT_SYMBOL_GPL(tacna_dsp_memory_enable);
void tacna_dsp_memory_disable(struct tacna_priv *priv,
const struct tacna_dsp_power_regs *regs)
{
struct regmap *regmap = priv->tacna->regmap;
int i, ret;
for (i = 0; i < regs->n_pwd; ++i) {
ret = regmap_write(regmap, regs->pwd[i], 0);
if (ret)
dev_warn(priv->dev,
"Failed to write SRAM enables (%d)\n", ret);
}
for (i = 0; i < regs->n_ext; ++i) {
ret = regmap_write(regmap, regs->ext[i], 0);
if (ret)
dev_warn(priv->dev,
"Failed to write SRAM enables (%d)\n", ret);
}
}
EXPORT_SYMBOL_GPL(tacna_dsp_memory_disable);
int tacna_dsp_freq_update(struct snd_soc_dapm_widget *w, unsigned int freq_reg,
unsigned int freqsel_reg)
{
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
struct tacna_priv *priv = snd_soc_codec_get_drvdata(codec);
struct tacna *tacna = priv->tacna;
struct wm_adsp *dsp = &priv->dsp[w->shift];
int ret;
unsigned int freq, freq_sel, freq_sts;
if (!freq_reg)
return -EINVAL;
ret = regmap_read(tacna->regmap, freq_reg, &freq);
if (ret) {
dev_err(codec->dev, "Failed to read 0x%x: %d\n", freq_reg, ret);
return ret;
}
if (freqsel_reg) {
freq_sts = (freq & TACNA_SYSCLK_FREQ_STS_MASK) >>
TACNA_SYSCLK_FREQ_STS_SHIFT;
ret = regmap_read(tacna->regmap, freqsel_reg, &freq_sel);
if (ret) {
dev_err(codec->dev, "Failed to read 0x%x: %d\n",
freqsel_reg, ret);
return ret;
}
freq_sel = (freq_sel & TACNA_SYSCLK_FREQ_MASK) >>
TACNA_SYSCLK_FREQ_SHIFT;
if (freq_sts != freq_sel) {
dev_err(codec->dev,
"SYSCLK FREQ (0x%x) != FREQ STS (0x%x)\n",
freq_sel, freq_sts);
return -ETIMEDOUT;
}
}
freq &= TACNA_DSP_CLK_FREQ_MASK;
freq >>= TACNA_DSP_CLK_FREQ_SHIFT;
ret = regmap_write(dsp->regmap, dsp->base + TACNA_DSP_CLOCK_FREQ_OFFS,
freq);
if (ret) {
dev_err(codec->dev, "Failed to set HALO clock freq: %d\n", ret);
return ret;
}
return 0;
}
EXPORT_SYMBOL_GPL(tacna_dsp_freq_update);
int tacna_dsp_freq_ev(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
switch (event) {
case SND_SOC_DAPM_POST_PMU:
return tacna_dsp_freq_update(w, TACNA_DSP_CLOCK1, 0);
default:
return 0;
}
}
EXPORT_SYMBOL_GPL(tacna_dsp_freq_ev);
static void tacna_check_enable_async(struct tacna_priv *priv,
unsigned int dac_async_rate_pending)
{
struct tacna *tacna = priv->tacna;
unsigned int required = priv->asyncclk_req;
unsigned int val;
int ret;
dev_dbg(priv->dev,
"ASYNCCLK: in: widgets=0x%x pending=0x%x\n",
priv->asyncclk_req, dac_async_rate_pending);
/* ignore pending requirement if the widget is off */
dac_async_rate_pending &= required;
/* clear widget requests if the DAC isn't on async */
if (!dac_async_rate_pending) {
if (required & TACNA_DACRATE1_ASYNCCLK_REQ) {
ret = tacna_rate_is_sync(priv, TACNA_OUTPUT_CONTROL_1,
TACNA_OUT_RATE_MASK,
TACNA_OUT_RATE_SHIFT);
if (ret > 0)
required &= ~TACNA_DACRATE1_ASYNCCLK_REQ;
}
if (required & TACNA_OUTH_ASYNCCLK_REQ) {
ret = tacna_rate_is_sync(priv, TACNA_OUTH_CONFIG_1,
TACNA_OUTH_RATE_MASK,
TACNA_OUTH_RATE_SHIFT);
if (ret > 0)
required &= ~TACNA_OUTH_ASYNCCLK_REQ;
}
}
if (required || dac_async_rate_pending)
val = TACNA_ASYNC_CLK_EN;
else
val = 0;
dev_dbg(priv->dev,
"ASYNCCLK: out: widgets=0x%x required=0x%x pending=0x%x enable:%c\n",
priv->asyncclk_req, required, dac_async_rate_pending,
val ? 'Y' : 'N');
ret = regmap_update_bits(tacna->regmap, TACNA_ASYNC_CLOCK1,
TACNA_ASYNC_CLK_EN_MASK, val);
if (ret)
dev_warn(priv->dev, "Error writing ASYNC_CLK_EN (%d)\n", ret);
}
/*
* The DAC needs ASYNCCLK only if it is on the async domain - if not we don't
* want to enable a clock that isn't needed and might not have an input clock.
* A DAPM clock route can't be conditional, so this handler controls ASYNCCLK
* based on the state of both ASYNCCLK and DAC_ASYNCCLK widgets
*/
int tacna_asyncclk_ev(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
struct tacna_priv *priv = snd_soc_codec_get_drvdata(codec);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
priv->asyncclk_req |= w->shift;
break;
case SND_SOC_DAPM_PRE_PMD:
priv->asyncclk_req &= ~w->shift;
break;
default:
return 0;
}
tacna_check_enable_async(priv, 0);
return 0;
}
EXPORT_SYMBOL_GPL(tacna_asyncclk_ev);
/* A change to the DAC rate could add or remove an ASYNCCLK dependency */
int tacna_dac_rate_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
struct tacna_priv *priv = snd_soc_codec_get_drvdata(codec);
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
unsigned int *item = ucontrol->value.enumerated.item;
unsigned int val = snd_soc_enum_item_to_val(e, item[0]);
unsigned int out_clk_mode = TACNA_OUT_CLK_MODE;
unsigned int req_flag;
int ret;
snd_soc_dapm_mutex_lock(dapm);
/* handle ASYNCCLK enable before changing rate */
if (!tacna_rate_val_is_sync(val)) {
out_clk_mode = 0;
switch (e->reg) {
case TACNA_OUTH_CONFIG_1:
req_flag = TACNA_OUTH_ASYNCCLK_REQ;
break;
default:
req_flag = TACNA_DACRATE1_ASYNCCLK_REQ;
break;
}
tacna_check_enable_async(priv, req_flag);
}
ret = regmap_update_bits(priv->tacna->regmap, TACNA_OUTPUT_CONTROL_1,
TACNA_OUT_CLK_MODE_MASK, out_clk_mode);
if (ret)
dev_warn(priv->dev, "Failed to write 0x%x: %d\n",
TACNA_OUTPUT_CONTROL_1, ret);
ret = tacna_rate_put(kcontrol, ucontrol);
/* handle ASYNCCLK disable after changing rate */
if (tacna_rate_val_is_sync(val))
tacna_check_enable_async(priv, 0);
snd_soc_dapm_mutex_unlock(dapm);
return ret;
}
EXPORT_SYMBOL_GPL(tacna_dac_rate_put);
static const unsigned int tacna_opclk_ref_48k_rates[] = {
6144000,
12288000,
24576000,
49152000,
};
static const unsigned int tacna_opclk_ref_44k1_rates[] = {
5644800,
11289600,
22579200,
45158400,
};
static int tacna_set_opclk(struct snd_soc_codec *codec, unsigned int clk,
unsigned int freq)
{
struct tacna_priv *priv = snd_soc_codec_get_drvdata(codec);
unsigned int reg;
const unsigned int *rates;
int ref, div, refclk;
BUILD_BUG_ON(ARRAY_SIZE(tacna_opclk_ref_48k_rates) !=
ARRAY_SIZE(tacna_opclk_ref_44k1_rates));
switch (clk) {
case TACNA_CLK_OPCLK:
reg = TACNA_OUTPUT_SYS_CLK;
refclk = priv->sysclk;
break;
case TACNA_CLK_ASYNC_OPCLK:
reg = TACNA_OUTPUT_ASYNC_CLK;
refclk = priv->asyncclk;
break;
default:
return -EINVAL;
}
if (refclk % 4000)
rates = tacna_opclk_ref_44k1_rates;
else
rates = tacna_opclk_ref_48k_rates;
for (ref = 0; ref < ARRAY_SIZE(tacna_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(codec->dev, "Configured %dHz OPCLK\n",
freq);
snd_soc_update_bits(codec, reg,
TACNA_OPCLK_DIV_MASK |
TACNA_OPCLK_SEL_MASK,
(div <<
TACNA_OPCLK_DIV_SHIFT) |
ref);
return 0;
}
div += 2;
}
}
dev_err(codec->dev, "Unable to generate %dHz OPCLK\n", freq);
return -EINVAL;
}
static int tacna_set_dacclk(struct snd_soc_codec *codec, int source,
unsigned int freq)
{
struct tacna_priv *priv = snd_soc_codec_get_drvdata(codec);
unsigned int val, div;
int ret;
switch (freq) {
case 5644800:
case 6144000:
val = 0;
div = 0; /* divide by 1 */
break;
case 11289600:
case 12288000:
val = 1 << TACNA_DAC_CLK_SRC_FREQ_SHIFT;
div = 1 << TACNA_OUT_CLK_DIV_SHIFT; /* divide by 2 */
break;
case 22579200:
case 24576000:
val = 2 << TACNA_DAC_CLK_SRC_FREQ_SHIFT;
div = 2 << TACNA_OUT_CLK_DIV_SHIFT; /* divide by 4 */
break;
default:
dev_err(priv->dev, "invalid DACCLK freq %dHz\n", freq);
return -EINVAL;
}
switch (source) {
case TACNA_CLK_SRC_MCLK1:
case TACNA_CLK_SRC_MCLK2:
case TACNA_CLK_SRC_MCLK3:
case TACNA_CLK_SRC_FLL1:
case TACNA_CLK_SRC_FLL2:
case TACNA_CLK_SRC_FLL3:
val |= source << TACNA_DAC_CLK_SRC_SEL_SHIFT;
break;
default:
dev_err(priv->dev, "invalid DACCLK src %d\n", source);
return -EINVAL;
}
ret = regmap_update_bits(priv->tacna->regmap,
TACNA_DAC_CLK_CONTROL1,
TACNA_DAC_CLK_SRC_FREQ_MASK |
TACNA_DAC_CLK_SRC_SEL_MASK,
val);
if (ret) {
dev_err(priv->dev, "Error writing DACCLK control: %d\n", ret);
return ret;
}
ret = regmap_update_bits(priv->tacna->regmap,
TACNA_OUTPUT_CONTROL_1,
TACNA_OUT_CLK_DIV_MASK,
div);
if (ret) {
dev_err(priv->dev, "Error writing OUTPUT_CONTROL_1 %d\n", ret);
return ret;
}
switch (priv->tacna->type) {
case CS47L96:
case CS47L97:
if (freq % 4000)
val = TACNA_OUTH_CLK_FRAC; /* 44.1 group rate */
else
val = 0;
ret = regmap_update_bits(priv->tacna->regmap,
TACNA_OUTH_CONFIG_1,
TACNA_OUTH_CLK_FRAC_MASK,
val);
if (ret) {
dev_err(priv->dev,
"Error writing OUTH_CONFIG_1 %d\n", ret);
return ret;
}
break;
default:
break;
}
return 0;
}
static int tacna_get_dspclk_setting(struct tacna_priv *priv, unsigned int freq,
int src, unsigned int *val)
{
freq /= 15625; /* convert to 1/64ths of 1MHz */
*val |= freq << TACNA_DSP_CLK_FREQ_SHIFT;
return 0;
}
static int tacna_get_sysclk_setting(unsigned int freq)
{
switch (freq) {
case 0:
case 5644800:
case 6144000:
return TACNA_SYSCLK_RATE_6MHZ;
case 11289600:
case 12288000:
return TACNA_SYSCLK_RATE_12MHZ << TACNA_SYSCLK_FREQ_SHIFT;
case 22579200:
case 24576000:
return TACNA_SYSCLK_RATE_24MHZ << TACNA_SYSCLK_FREQ_SHIFT;
case 45158400:
case 49152000:
return TACNA_SYSCLK_RATE_49MHZ << TACNA_SYSCLK_FREQ_SHIFT;
case 90316800:
case 98304000:
return TACNA_SYSCLK_RATE_98MHZ << TACNA_SYSCLK_FREQ_SHIFT;
default:
return -EINVAL;
}
}
static int tacna_set_pdm_fllclk(struct snd_soc_codec *codec, int source)
{
struct tacna_priv *priv = snd_soc_codec_get_drvdata(codec);
struct tacna *tacna = priv->tacna;
unsigned int val;
switch (source) {
case TACNA_PDMCLK_SRC_IN1_PDMCLK:
case TACNA_PDMCLK_SRC_IN2_PDMCLK:
case TACNA_PDMCLK_SRC_IN3_PDMCLK:
case TACNA_PDMCLK_SRC_IN4_PDMCLK:
case TACNA_PDMCLK_SRC_AUXPDM1_CLK:
case TACNA_PDMCLK_SRC_AUXPDM2_CLK:
case TACNA_PDMCLK_SRC_AUXPDM3_CLK:
val = source << TACNA_PDM_FLLCLK_SRC_SHIFT;
break;
default:
dev_err(priv->dev, "Invalid PDM FLLCLK src %d\n", source);
return -EINVAL;
}
return regmap_update_bits(tacna->regmap, TACNA_INPUT_CONTROL2,
TACNA_PDM_FLLCLK_SRC_MASK, val);
}
int tacna_set_sysclk(struct snd_soc_codec *codec, int clk_id, int source,
unsigned int freq, int dir)
{
struct tacna_priv *priv = snd_soc_codec_get_drvdata(codec);
struct tacna *tacna = priv->tacna;
char *name;
unsigned int reg;
unsigned int mask = TACNA_SYSCLK_SRC_MASK;
unsigned int val = source << TACNA_SYSCLK_SRC_SHIFT;
int clk_freq_sel, *clk;
switch (clk_id) {
case TACNA_CLK_SYSCLK_1:
name = "SYSCLK";
reg = TACNA_SYSTEM_CLOCK1;
clk = &priv->sysclk;
clk_freq_sel = tacna_get_sysclk_setting(freq);
mask |= TACNA_SYSCLK_FREQ_MASK | TACNA_SYSCLK_FRAC;
break;
case TACNA_CLK_ASYNCCLK_1:
name = "ASYNCCLK";
reg = TACNA_ASYNC_CLOCK1;
clk = &priv->asyncclk;
clk_freq_sel = tacna_get_sysclk_setting(freq);
mask |= TACNA_SYSCLK_FREQ_MASK;
break;
case TACNA_CLK_DSPCLK:
name = "DSPCLK";
reg = TACNA_DSP_CLOCK1;
clk = &priv->dspclk;
clk_freq_sel = tacna_get_dspclk_setting(priv, freq,
source, &val);
mask |= TACNA_DSP_CLK_FREQ_MASK;
break;
case TACNA_CLK_OPCLK:
case TACNA_CLK_ASYNC_OPCLK:
return tacna_set_opclk(codec, clk_id, freq);
case TACNA_CLK_DACCLK:
return tacna_set_dacclk(codec, source, freq);
case TACNA_CLK_SYSCLKAO:
name = "SYSCLKAO";
reg = TACNA_SYSTEM_CLOCK1AO;
clk = &priv->sysclk;
clk_freq_sel = tacna_get_sysclk_setting(freq);
mask |= TACNA_SYSCLKAO_FREQ_MASK | TACNA_SYSCLKAO_FRAC;
break;
case TACNA_CLK_PDM_FLLCLK:
return tacna_set_pdm_fllclk(codec, source);
default:
return -EINVAL;
}
if (clk_freq_sel < 0) {
dev_err(priv->dev,
"Failed to get %s setting for %dHZ\n", name, freq);
return clk_freq_sel;
}
*clk = freq;
if (freq == 0) {
dev_dbg(priv->dev, "%s cleared\n", name);
return 0;
}
val |= clk_freq_sel;
if (freq % 6144000)
val |= TACNA_SYSCLK_FRAC;
dev_info(priv->dev, "%s set to %uHz", name, freq);
return regmap_update_bits(tacna->regmap, reg, mask, val);
}
EXPORT_SYMBOL_GPL(tacna_set_sysclk);
static int tacna_is_enabled_fll(struct tacna_fll *fll, int base)
{
struct tacna *tacna = fll->tacna_priv->tacna;
unsigned int reg;
int ret;
ret = regmap_read(tacna->regmap, base + TACNA_FLL_CONTROL1_OFFS, &reg);
if (ret != 0) {
tacna_fll_err(fll, "Failed to read current state: %d\n", ret);
return ret;
}
return reg & TACNA_FLL1_EN;
}
static int tacna_wait_for_fll(struct tacna_fll *fll, bool requested)
{
struct tacna *tacna = fll->tacna_priv->tacna;
unsigned int val = 0;
int i;
tacna_fll_dbg(fll, "Waiting for FLL...\n");
for (i = 0; i < 30; i++) {
regmap_read(tacna->regmap, fll->sts_addr, &val);
if (!!(val & fll->sts_mask) == requested)
return 0;
switch (i) {
case 0 ... 5:
usleep_range(75, 125);
break;
case 11 ... 20:
usleep_range(750, 1250);
break;
default:
msleep(20);
break;
}
}
tacna_fll_warn(fll, "Timed out waiting for %s\n",
requested ? "lock" : "unlock");
return -ETIMEDOUT;
}
static int tacna_fllhj_disable(struct tacna_fll *fll)
{
struct tacna *tacna = fll->tacna_priv->tacna;
bool change;
tacna_fll_info(fll, "Disabling FLL\n");
/*
* Disable lockdet, but don't set ctrl_upd update but. This allows the
* lock status bit to clear as normal, but should the FLL be enabled
* again due to a control clock being required, the lock won't re-assert
* as the FLL config registers are automatically applied when the FLL
* enables.
*/
regmap_update_bits(tacna->regmap,
fll->base + TACNA_FLL_CONTROL1_OFFS,
TACNA_FLL1_HOLD_MASK,
TACNA_FLL1_HOLD_MASK);
regmap_update_bits(tacna->regmap,
fll->base + TACNA_FLL_CONTROL2_OFFS,
TACNA_FLL1_LOCKDET_MASK,
0);
regmap_update_bits(tacna->regmap,
fll->base + TACNA_FLL_CONTROL5_OFFS,
TACNA_FLL1_FRC_INTEG_UPD_MASK,
TACNA_FLL1_FRC_INTEG_UPD);
regmap_update_bits_check(tacna->regmap,
fll->base + TACNA_FLL_CONTROL1_OFFS,
TACNA_FLL1_EN_MASK,
0,
&change);
tacna_wait_for_fll(fll, false);
/*
* ctrl_up gates the writes to all the fll's registers, setting it to 0
* here ensures that after a runtime suspend/resume cycle when one
* enables the fll then ctrl_up is the last bit that is configured
* by the fll enable code rather than the cache sync operation which
* would have updated it much earlier before writing out all fll
* registers
*/
regmap_update_bits(tacna->regmap,
fll->base + TACNA_FLL_CONTROL1_OFFS,
TACNA_FLL1_CTRL_UPD_MASK,
0);
if (change)
pm_runtime_put_autosuspend(tacna->dev);
return 0;
}
static int tacna_fllhj_apply(struct tacna_fll *fll, int fin)
{
struct tacna *tacna = fll->tacna_priv->tacna;
int refdiv, fref, fout, lockdet_thr, fbdiv, fllgcd;
bool frac = false;
unsigned int fll_n, min_n, max_n, ratio, theta, lambda, hp;
unsigned int gains, num;
tacna_fll_dbg(fll, "fin=%d, fout=%d\n", fin, fll->fout);
for (refdiv = 0; refdiv < 4; refdiv++)
if ((fin / (1 << refdiv)) <= TACNA_FLLHJ_MAX_THRESH)
break;
fref = fin / (1 << refdiv);
fout = fll->fout;
frac = fout % fref;
if (frac && fll->integer_only) {
tacna_fll_err(fll, "%u:%u not an integer ratio\n", fin, fout);
return -EINVAL;
}
if (fll->max_fref && (fref > fll->max_fref)) {
tacna_fll_err(fll, "fref=%u too high (max %u)\n",
fref, fll->max_fref);
return -EINVAL;
}
/*
* Use simple heuristic approach to find a configuration that
* should work for most input clocks.
*/
if (fref < TACNA_FLLHJ_LOW_THRESH) {
lockdet_thr = 2;
gains = TACNA_FLLHJ_LOW_GAINS;
switch (tacna->type) {
case CS47L96:
case CS47L97:
if (fll->integer_only)
gains = CS47L96_AO_FLLHJ_LOW_GAINS;
break;
default:
break;
}
if (frac)
fbdiv = 256;
else
fbdiv = 4;
} else if (fref < TACNA_FLLHJ_MID_THRESH) {
lockdet_thr = 8;
gains = TACNA_FLLHJ_MID_GAINS;
fbdiv = (frac) ? 16 : 2;
} else {
lockdet_thr = 8;
gains = TACNA_FLLHJ_HIGH_GAINS;
fbdiv = 1;
}
/* Use high performance mode for fractional configurations. */
if (frac) {
hp = 0x3;
min_n = TACNA_FLLHJ_FRAC_MIN_N;
max_n = TACNA_FLLHJ_FRAC_MAX_N;
} else {
if (fll->has_lp && (fref < TACNA_FLLHJ_LP_INT_MODE_THRESH))
hp = 0x0;
else
hp = 0x1;
min_n = TACNA_FLLHJ_INT_MIN_N;
max_n = TACNA_FLLHJ_INT_MAX_N;
}
ratio = fout / fref;
tacna_fll_dbg(fll, "refdiv=%d, fref=%d, frac:%d\n",
refdiv, fref, frac);
while (ratio / fbdiv < min_n) {
fbdiv /= 2;
if (fbdiv < min_n) {
tacna_fll_err(fll, "FBDIV (%u) < minimum N (%u)\n",
fbdiv, min_n);
return -EINVAL;
}
}
while (frac && (ratio / fbdiv > max_n)) {
fbdiv *= 2;
if (fbdiv >= 1024) {
tacna_fll_err(fll, "FBDIV (%u) >= 1024\n", fbdiv);
return -EINVAL;
}
}
tacna_fll_dbg(fll, "lockdet=%d, hp=0x%x, fbdiv:%d\n",
lockdet_thr, hp, fbdiv);
/* Calculate N.K values */
fllgcd = gcd(fout, fbdiv * fref);
num = fout / fllgcd;
lambda = (fref * fbdiv) / fllgcd;
fll_n = num / lambda;
theta = num % lambda;
tacna_fll_dbg(fll, "fll_n=%d, gcd=%d, theta=%d, lambda=%d\n",
fll_n, fllgcd, theta, lambda);
/* Some sanity checks before any registers are written. */
if (fll_n < min_n || fll_n > max_n) {
tacna_fll_err(fll, "N not in valid %s mode range %d-%d: %d\n",
frac ? "fractional" : "integer", min_n, max_n,
fll_n);
return -EINVAL;
}
if (fbdiv < 1 || (frac && fbdiv >= 1024) || (!frac && fbdiv >= 256)) {
tacna_fll_err(fll, "Invalid fbdiv for %s mode (%u)\n",
frac ? "fractional" : "integer", fbdiv);
return -EINVAL;
}
/* clear the ctrl_upd bit to guarantee we write to it later. */
regmap_update_bits(tacna->regmap,
fll->base + TACNA_FLL_CONTROL2_OFFS,
TACNA_FLL1_LOCKDET_THR_MASK |
TACNA_FLL1_PHASEDET_MASK |
TACNA_FLL1_REFCLK_DIV_MASK |
TACNA_FLL1_N_MASK |
TACNA_FLL1_CTRL_UPD_MASK,
(lockdet_thr << TACNA_FLL1_LOCKDET_THR_SHIFT) |
(1 << TACNA_FLL1_PHASEDET_SHIFT) |
(refdiv << TACNA_FLL1_REFCLK_DIV_SHIFT) |
(fll_n << TACNA_FLL1_N_SHIFT));
regmap_update_bits(tacna->regmap,
fll->base + TACNA_FLL_CONTROL3_OFFS,
TACNA_FLL1_LAMBDA_MASK |
TACNA_FLL1_THETA_MASK,
(lambda << TACNA_FLL1_LAMBDA_SHIFT) |
(theta << TACNA_FLL1_THETA_SHIFT));
regmap_update_bits(tacna->regmap,
fll->base + TACNA_FLL_CONTROL4_OFFS,
(0xffff << TACNA_FLL1_FD_GAIN_COARSE_SHIFT) |
TACNA_FLL1_HP_MASK |
TACNA_FLL1_FB_DIV_MASK,
(gains << TACNA_FLL1_FD_GAIN_COARSE_SHIFT) |
(hp << TACNA_FLL1_HP_SHIFT) |
(fbdiv << TACNA_FLL1_FB_DIV_SHIFT));
return 0;
}
static int tacna_fllhj_enable(struct tacna_fll *fll)
{
struct tacna *tacna = fll->tacna_priv->tacna;
int already_enabled = tacna_is_enabled_fll(fll, fll->base);
int ret;
if (already_enabled < 0)
return already_enabled;
if (!already_enabled)
pm_runtime_get_sync(tacna->dev);
tacna_fll_info(fll, "Enabling FLL, initially %s\n",
already_enabled ? "enabled" : "disabled");
/* FLLn_HOLD must be set before configuring any registers */
regmap_update_bits(tacna->regmap,
fll->base + TACNA_FLL_CONTROL1_OFFS,
TACNA_FLL1_HOLD_MASK,
TACNA_FLL1_HOLD_MASK);
/* Apply refclk */
ret = tacna_fllhj_apply(fll, fll->ref_freq);
if (ret) {
tacna_fll_err(fll, "Failed to set FLL: %d\n", ret);
goto out;
}
regmap_update_bits(tacna->regmap,
fll->base + TACNA_FLL_CONTROL2_OFFS,
TACNA_FLL1_REFCLK_SRC_MASK,
fll->ref_src << TACNA_FLL1_REFCLK_SRC_SHIFT);
regmap_update_bits(tacna->regmap,
fll->base + TACNA_FLL_CONTROL1_OFFS,
TACNA_FLL1_EN_MASK,
TACNA_FLL1_EN_MASK);
out:
regmap_update_bits(tacna->regmap,
fll->base + TACNA_FLL_CONTROL2_OFFS,
TACNA_FLL1_LOCKDET_MASK,
TACNA_FLL1_LOCKDET_MASK);
regmap_update_bits(tacna->regmap,
fll->base + TACNA_FLL_CONTROL1_OFFS,
TACNA_FLL1_CTRL_UPD_MASK,
TACNA_FLL1_CTRL_UPD_MASK);
/* Release the hold so that flln locks to external frequency */
regmap_update_bits(tacna->regmap,
fll->base + TACNA_FLL_CONTROL1_OFFS,
TACNA_FLL1_HOLD_MASK,
0);
if (!already_enabled)
tacna_wait_for_fll(fll, true);
return 0;
}
static int tacna_fllhj_validate(struct tacna_fll *fll,
unsigned int ref_in,
unsigned int fout)
{
if (fout && !ref_in) {
tacna_fll_err(fll, "fllout set without valid input clk\n");
return -EINVAL;
}
if (fll->fout && fout != fll->fout) {
tacna_fll_err(fll, "Can't change output on active FLL\n");
return -EINVAL;
}
if (ref_in / TACNA_FLL_MAX_REFDIV > TACNA_FLLHJ_MAX_THRESH) {
tacna_fll_err(fll, "Can't scale %dMHz to <=13MHz\n", ref_in);
return -EINVAL;
}
if (fout > TACNA_FLL_MAX_FOUT) {
tacna_fll_err(fll, "Fout=%dMHz exceeeds maximum %dMHz\n",
fout, TACNA_FLL_MAX_FOUT);
return -EINVAL;
}
return 0;
}
int tacna_fllhj_set_refclk(struct tacna_fll *fll, int source,
unsigned int fin, unsigned int fout)
{
int ret = 0;
if (fll->ref_src == source && fll->ref_freq == fin &&
fll->fout == fout)
return 0;
if (fin && fout && tacna_fllhj_validate(fll, fin, fout))
return -EINVAL;
fll->ref_src = source;
fll->ref_freq = fin;
fll->fout = fout;
if (fout)
ret = tacna_fllhj_enable(fll);
else
tacna_fllhj_disable(fll);
return ret;
}
EXPORT_SYMBOL_GPL(tacna_fllhj_set_refclk);
int tacna_init_fll(struct tacna_fll *fll)
{
fll->ref_src = TACNA_FLL_SRC_NONE;
return 0;
}
EXPORT_SYMBOL_GPL(tacna_init_fll);
static int tacna_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
struct snd_soc_codec *codec = dai->codec;
struct tacna_priv *priv = snd_soc_codec_get_drvdata(codec);
struct tacna *tacna = priv->tacna;
unsigned int val = 0U;
unsigned int base = dai->driver->base;
unsigned int mask = TACNA_ASP1_FMT_MASK | TACNA_ASP1_BCLK_INV_MASK |
TACNA_ASP1_BCLK_MSTR_MASK |
TACNA_ASP1_FSYNC_INV_MASK |
TACNA_ASP1_FSYNC_MSTR_MASK;
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_DSP_A:
val |= (TACNA_ASP_FMT_DSP_MODE_A << TACNA_ASP1_FMT_SHIFT);
break;
case SND_SOC_DAIFMT_DSP_B:
if ((fmt & SND_SOC_DAIFMT_MASTER_MASK)
!= SND_SOC_DAIFMT_CBM_CFM) {
tacna_asp_err(dai, "DSP_B not valid in slave mode\n");
return -EINVAL;
}
val |= (TACNA_ASP_FMT_DSP_MODE_B << TACNA_ASP1_FMT_SHIFT);
break;
case SND_SOC_DAIFMT_I2S:
val |= (TACNA_ASP_FMT_I2S_MODE << TACNA_ASP1_FMT_SHIFT);
break;
case SND_SOC_DAIFMT_LEFT_J:
if ((fmt & SND_SOC_DAIFMT_MASTER_MASK)
!= SND_SOC_DAIFMT_CBM_CFM) {
tacna_asp_err(dai, "LEFT_J not valid in slave mode\n");
return -EINVAL;
}
val |= (TACNA_ASP_FMT_LEFT_JUSTIFIED_MODE <<
TACNA_ASP1_FMT_SHIFT);
break;
default:
tacna_asp_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:
val |= TACNA_ASP1_FSYNC_MSTR;
break;
case SND_SOC_DAIFMT_CBM_CFS:
val |= TACNA_ASP1_BCLK_MSTR;
break;
case SND_SOC_DAIFMT_CBM_CFM:
val |= TACNA_ASP1_BCLK_MSTR;
val |= TACNA_ASP1_FSYNC_MSTR;
break;
default:
tacna_asp_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:
val |= TACNA_ASP1_BCLK_INV;
val |= TACNA_ASP1_FSYNC_INV;
break;
case SND_SOC_DAIFMT_IB_NF:
val |= TACNA_ASP1_BCLK_INV;
break;
case SND_SOC_DAIFMT_NB_IF:
val |= TACNA_ASP1_FSYNC_INV;
break;
default:
return -EINVAL;
}
regmap_update_bits(tacna->regmap,
base + TACNA_ASP_CONTROL2,
mask,
val);
return 0;
}
static const int tacna_sclk_rates[] = {
-1, /* 0 */
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
128000, /* 12 */
176400, /* 13 */
192000, /* 14 */
256000, /* 15 */
352800, /* 16 */
384000, /* 17 */
512000, /* 18 */
705600, /* 19 */
-1,
768000, /* 21 */
-1,
1024000, /* 23 */
-1,
1411200, /* 25 */
-1,
1536000, /* 27 */
-1,
2048000, /* 29 */
-1,
2822400, /* 31 */
-1,
3072000, /* 33 */
-1,
-1,
4096000, /* 36 */
-1,
5644800, /* 38 */
-1,
6144000, /* 40 */
-1,
-1,
-1,
-1,
-1,
-1,
8192000, /* 47 */
-1,
11289600, /* 49 */
-1,
12288000, /* 51 */
-1,
-1,
-1,
-1,
-1,
22579200, /* 57 */
-1,
24576000, /* 59 */
-1,
-1,
-1,
-1,
};
#define TACNA_48K_RATE_MASK 0x0e00fe
#define TACNA_44K1_RATE_MASK 0x00fe00
#define TACNA_RATE_MASK (TACNA_48K_RATE_MASK | TACNA_44K1_RATE_MASK)
static const unsigned int tacna_sr_vals[] = {
0,
12000, /* TACNA_48K_RATE_MASK */
24000, /* TACNA_48K_RATE_MASK */
48000, /* TACNA_48K_RATE_MASK */
96000, /* TACNA_48K_RATE_MASK */
192000, /* TACNA_48K_RATE_MASK */
384000, /* TACNA_48K_RATE_MASK */
768000, /* TACNA_48K_RATE_MASK */
0,
11025, /* TACNA_44K1_RATE_MASK */
22050, /* TACNA_44K1_RATE_MASK */
44100, /* TACNA_44K1_RATE_MASK */
88200, /* TACNA_44K1_RATE_MASK */
176400, /* TACNA_44K1_RATE_MASK */
352800, /* TACNA_44K1_RATE_MASK */
705600, /* TACNA_44K1_RATE_MASK */
0,
8000, /* TACNA_48K_RATE_MASK */
16000, /* TACNA_48K_RATE_MASK */
32000, /* TACNA_48K_RATE_MASK */
};
static const struct snd_pcm_hw_constraint_list tacna_constraint = {
.count = ARRAY_SIZE(tacna_sr_vals),
.list = tacna_sr_vals,
};
static int tacna_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct snd_soc_codec *codec = dai->codec;
struct tacna_priv *priv = snd_soc_codec_get_drvdata(codec);
struct tacna_dai_priv *dai_priv = &priv->dai[dai->id - 1];
unsigned int base_rate;
switch (priv->tacna->type) {
case CS48L33:
return 0;
default:
break;
}
if (!substream->runtime)
return 0;
switch (dai_priv->clk) {
case TACNA_CLK_SYSCLK_1:
case TACNA_CLK_SYSCLK_2:
case TACNA_CLK_SYSCLK_3:
case TACNA_CLK_SYSCLK_4:
base_rate = priv->sysclk;
break;
case TACNA_CLK_ASYNCCLK_1:
case TACNA_CLK_ASYNCCLK_2:
base_rate = priv->asyncclk;
break;
default:
return 0;
}
if (base_rate == 0)
dai_priv->constraint.mask = TACNA_RATE_MASK;
else if (base_rate % 4000)
dai_priv->constraint.mask = TACNA_44K1_RATE_MASK;
else
dai_priv->constraint.mask = TACNA_48K_RATE_MASK;
return snd_pcm_hw_constraint_list(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_RATE,
&dai_priv->constraint);
}
static int tacna_hw_params_rate(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct snd_soc_codec *codec = dai->codec;
struct tacna_priv *priv = snd_soc_codec_get_drvdata(codec);
struct tacna_dai_priv *dai_priv = &priv->dai[dai->id - 1];
unsigned int base = dai->driver->base;
int ret = 0;
unsigned int i, sr_val, sr_reg, sr_mask;
unsigned int cur_asp_rate, tar_asp_rate, rate;
bool change_rate_domain = false;
bool ao_dai = false;
rate = params_rate(params);
for (i = 0; i < ARRAY_SIZE(tacna_sr_vals); i++)
if (tacna_sr_vals[i] == rate)
break;
if (i == ARRAY_SIZE(tacna_sr_vals)) {
tacna_asp_err(dai, "Unsupported sample rate %dHz\n", rate);
return -EINVAL;
}
sr_val = i;
switch (dai_priv->clk) {
case TACNA_CLK_SYSCLK_1:
tar_asp_rate = 0U << TACNA_ASP1_RATE_SHIFT;
sr_reg = TACNA_SAMPLE_RATE1;
sr_mask = TACNA_SAMPLE_RATE_1_MASK;
break;
case TACNA_CLK_SYSCLK_2:
tar_asp_rate = 1U << TACNA_ASP1_RATE_SHIFT;
sr_reg = TACNA_SAMPLE_RATE2;
sr_mask = TACNA_SAMPLE_RATE_2_MASK;
break;
case TACNA_CLK_SYSCLK_3:
tar_asp_rate = 2U << TACNA_ASP1_RATE_SHIFT;
sr_reg = TACNA_SAMPLE_RATE3;
sr_mask = TACNA_SAMPLE_RATE_3_MASK;
break;
case TACNA_CLK_SYSCLK_4:
tar_asp_rate = 3U << TACNA_ASP1_RATE_SHIFT;
sr_reg = TACNA_SAMPLE_RATE4;
sr_mask = TACNA_SAMPLE_RATE_4_MASK;
break;
case TACNA_CLK_ASYNCCLK_1:
tar_asp_rate = 8U << TACNA_ASP1_RATE_SHIFT;
sr_reg = TACNA_ASYNC_SAMPLE_RATE1;
sr_mask = TACNA_ASYNC_SAMPLE_RATE_1_MASK;
break;
case TACNA_CLK_ASYNCCLK_2:
tar_asp_rate = 9U << TACNA_ASP1_RATE_SHIFT;
sr_reg = TACNA_ASYNC_SAMPLE_RATE2;
sr_mask = TACNA_ASYNC_SAMPLE_RATE_2_MASK;
break;
default:
return -EINVAL;
}
if (base == TACNA_ASP1AO_ENABLES1)
ao_dai = true;
if (base) {
ret = regmap_read(priv->tacna->regmap,
base + TACNA_ASP_CONTROL1,
&cur_asp_rate);
if (ret != 0) {
tacna_asp_err(dai, "Failed to check rate: %d\n", ret);
return ret;
}
if ((cur_asp_rate & TACNA_ASP1_RATE_MASK) !=
(tar_asp_rate & TACNA_ASP1_RATE_MASK)) {
change_rate_domain = true;
mutex_lock(&priv->rate_lock);
/* Guard the rate change with SYSCLK cycles */
if (!ao_dai)
tacna_spin_sysclk(priv);
}
}
snd_soc_update_bits(codec, sr_reg, sr_mask, sr_val);
if (base)
snd_soc_update_bits(codec, base + TACNA_ASP_CONTROL1,
TACNA_ASP1_RATE_MASK, tar_asp_rate);
if (change_rate_domain) {
if (!ao_dai)
tacna_spin_sysclk(priv);
mutex_unlock(&priv->rate_lock);
}
return ret;
}
static bool tacna_asp_cfg_changed(struct snd_soc_codec *codec,
unsigned int base, unsigned int sclk,
unsigned int slotws, unsigned int dataw)
{
unsigned int val;
val = snd_soc_read(codec, base + TACNA_ASP_CONTROL1);
if (sclk != (val & TACNA_ASP1_BCLK_FREQ_MASK))
return true;
val = snd_soc_read(codec, base + TACNA_ASP_CONTROL2);
if (slotws != (val & (TACNA_ASP1_RX_WIDTH_MASK |
TACNA_ASP1_TX_WIDTH_MASK)))
return true;
val = snd_soc_read(codec, base + TACNA_ASP_DATA_CONTROL1);
if (dataw != (val & (TACNA_ASP1_TX_WL_MASK)))
return true;
val = snd_soc_read(codec, base + TACNA_ASP_DATA_CONTROL5);
if (dataw != (val & (TACNA_ASP1_RX_WL_MASK)))
return true;
return false;
}
static int tacna_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct snd_soc_codec *codec = dai->codec;
struct tacna_priv *priv = snd_soc_codec_get_drvdata(codec);
struct tacna *tacna = priv->tacna;
int base = dai->driver->base;
const int *rates;
int i, ret, rates_sz;
unsigned int val;
unsigned int rate = params_rate(params);
unsigned int channels = params_channels(params);
unsigned int chan_limit =
tacna->pdata.codec.max_channels_clocked[dai->id - 1];
int dai_id = dai->id - 1;
int tdm_width = priv->tdm_width[dai_id];
int tdm_slots = priv->tdm_slots[dai_id];
int sclk, sclk_target;
bool reconfig;
unsigned int asp_state = 0;
unsigned int slotw, dataw;
rates = &tacna_sclk_rates[0];
rates_sz = ARRAY_SIZE(tacna_sclk_rates);
/*
* NOTE: the following calculations hold only under the assumption that
* symmetric_[rates|channels|samplebits] are set to 1
*/
slotw = snd_pcm_format_physical_width(params_format(params));
dataw = snd_pcm_format_width(params_format(params));
if (tdm_slots) {
tacna_asp_dbg(dai, "Configuring for %d %d bit TDM slots\n",
tdm_slots, tdm_width);
slotw = tdm_width;
channels = tdm_slots;
}
sclk_target = slotw * rate * channels;
if (chan_limit && chan_limit < channels) {
tacna_asp_dbg(dai, "Limiting to %d channels\n", chan_limit);
sclk_target /= channels;
sclk_target *= chan_limit;
}
/* Force multiple of 2 channels for I2S mode */
val = snd_soc_read(codec, base + TACNA_ASP_CONTROL2);
val = (val & TACNA_ASP1_FMT_MASK) >> TACNA_ASP1_FMT_SHIFT;
if ((channels & 1) && (val == TACNA_ASP_FMT_I2S_MODE)) {
tacna_asp_dbg(dai, "Forcing stereo mode\n");
sclk_target /= channels;
sclk_target *= channels + 1;
}
for (i = 0; i < rates_sz; i++) {
if (rates[i] >= sclk_target && rates[i] % rate == 0) {
sclk = i;
break;
}
}
if (i == rates_sz) {
tacna_asp_err(dai, "Unsupported sample rate %dHz\n", rate);
return -EINVAL;
}
tacna_asp_info(dai, "SCLK %dHz\n", rates[sclk]);
slotw = (slotw << TACNA_ASP1_TX_WIDTH_SHIFT) |
(slotw << TACNA_ASP1_RX_WIDTH_SHIFT);
reconfig = tacna_asp_cfg_changed(codec, base, sclk, slotw, dataw);
if (reconfig) {
/* Save ASP TX/RX state */
asp_state = snd_soc_read(codec, base + TACNA_ASP_ENABLES1);
/* Disable ASP TX/RX before reconfiguring it */
regmap_update_bits(tacna->regmap,
base + TACNA_ASP_ENABLES1,
0xff00ff,
0x0);
}
ret = tacna_hw_params_rate(substream, params, dai);
if (ret != 0)
goto restore_asp;
if (reconfig) {
regmap_update_bits_async(tacna->regmap,
base + TACNA_ASP_CONTROL1,
TACNA_ASP1_BCLK_FREQ_MASK,
sclk);
regmap_update_bits_async(tacna->regmap,
base + TACNA_ASP_CONTROL2,
TACNA_ASP1_RX_WIDTH_MASK |
TACNA_ASP1_TX_WIDTH_MASK,
slotw);
regmap_update_bits_async(tacna->regmap,
base + TACNA_ASP_DATA_CONTROL1,
TACNA_ASP1_TX_WL_MASK,
dataw);
regmap_update_bits(tacna->regmap,
base + TACNA_ASP_DATA_CONTROL5,
TACNA_ASP1_RX_WL_MASK,
dataw);
}
restore_asp:
if (reconfig) {
/* Restore ASP TX/RX state */
regmap_update_bits(tacna->regmap,
base + TACNA_ASP_ENABLES1,
0xff00ff,
asp_state);
}
return ret;
}
static bool tacna_is_sysclk(int clk_id)
{
switch (clk_id) {
case TACNA_CLK_SYSCLK_1:
case TACNA_CLK_SYSCLK_2:
case TACNA_CLK_SYSCLK_3:
case TACNA_CLK_SYSCLK_4:
return true;
default:
return false;
}
}
static const char * const tacna_dai_clk_str(int clk_id)
{
switch (clk_id) {
case TACNA_CLK_SYSCLK_1:
case TACNA_CLK_SYSCLK_2:
case TACNA_CLK_SYSCLK_3:
case TACNA_CLK_SYSCLK_4:
return "SYSCLK";
case TACNA_CLK_ASYNCCLK_1:
case TACNA_CLK_ASYNCCLK_2:
return "ASYNCCLK";
default:
return "Unknown clock";
}
}
static int tacna_dai_set_sysclk(struct snd_soc_dai *dai,
int clk_id, unsigned int freq, int dir)
{
struct snd_soc_codec *codec = dai->codec;
struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
struct tacna_priv *priv = snd_soc_codec_get_drvdata(codec);
struct tacna_dai_priv *dai_priv = &priv->dai[dai->id - 1];
struct snd_soc_dapm_route routes[2];
switch (clk_id) {
case TACNA_CLK_SYSCLK_1:
case TACNA_CLK_SYSCLK_2:
case TACNA_CLK_SYSCLK_3:
case TACNA_CLK_SYSCLK_4:
case TACNA_CLK_ASYNCCLK_1:
case TACNA_CLK_ASYNCCLK_2:
break;
default:
return -EINVAL;
}
if (clk_id == dai_priv->clk)
return 0;
if (dai->active) {
tacna_asp_err(dai, "Can't change clock on active DAI\n");
return -EBUSY;
}
tacna_asp_dbg(dai, "Setting to %s\n", tacna_dai_clk_str(clk_id));
/* No need to alter routes if we haven't switched clock domain. */
if (!!tacna_is_sysclk(clk_id) == !!tacna_is_sysclk(dai_priv->clk)) {
dai_priv->clk = clk_id;
return 0;
}
memset(&routes, 0, sizeof(routes));
routes[0].sink = dai->driver->capture.stream_name;
routes[1].sink = dai->driver->playback.stream_name;
switch (clk_id) {
case TACNA_CLK_SYSCLK_1:
case TACNA_CLK_SYSCLK_2:
case TACNA_CLK_SYSCLK_3:
case TACNA_CLK_SYSCLK_4:
routes[0].source = tacna_dai_clk_str(dai_priv->clk);
routes[1].source = tacna_dai_clk_str(dai_priv->clk);
snd_soc_dapm_del_routes(dapm, routes, ARRAY_SIZE(routes));
break;
default:
break;
}
switch (clk_id) {
case TACNA_CLK_ASYNCCLK_1:
case TACNA_CLK_ASYNCCLK_2:
routes[0].source = tacna_dai_clk_str(clk_id);
routes[1].source = tacna_dai_clk_str(clk_id);
snd_soc_dapm_add_routes(dapm, routes, ARRAY_SIZE(routes));
break;
default:
break;
}
dai_priv->clk = clk_id;
return snd_soc_dapm_sync(dapm);
}
static void tacna_set_channels_to_mask(struct snd_soc_dai *dai,
unsigned int base,
int channels, unsigned int mask)
{
struct snd_soc_codec *codec = dai->codec;
struct tacna_priv *priv = snd_soc_codec_get_drvdata(codec);
struct tacna *tacna = priv->tacna;
int slot, i, j = 0, shift;
unsigned int frame_ctls[2] = {0, 0};
for (i = 0; i < channels; ++i) {
slot = ffs(mask) - 1;
if (slot < 0)
return;
if (i - (j * 4) >= 4) {
++j;
if (j >= 2)
break;
}
shift = (8 * (i - j * 4));
frame_ctls[j] |= slot << shift;
mask &= ~(1 << slot); /* ? mask ^= 1 << slot ? */
}
regmap_write(tacna->regmap, base, frame_ctls[0]);
regmap_write(tacna->regmap, base + 0x4, frame_ctls[1]);
if (mask)
tacna_asp_warn(dai, "Too many channels in TDM mask\n");
}
static int tacna_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
unsigned int rx_mask, int slots, int slot_width)
{
struct snd_soc_codec *codec = dai->codec;
struct tacna_priv *priv = snd_soc_codec_get_drvdata(codec);
int base = dai->driver->base;
int rx_max_chan = dai->driver->playback.channels_max;
int tx_max_chan = dai->driver->capture.channels_max;
/* Only support TDM for the physical ASPs */
if (dai->id > TACNA_MAX_ASP)
return -ENOTSUPP;
if (slots == 0) {
tx_mask = (1 << tx_max_chan) - 1;
rx_mask = (1 << rx_max_chan) - 1;
}
tacna_set_channels_to_mask(dai, base + TACNA_ASP_FRAME_CONTROL1,
tx_max_chan, tx_mask);
tacna_set_channels_to_mask(dai, base + TACNA_ASP_FRAME_CONTROL5,
rx_max_chan, rx_mask);
priv->tdm_width[dai->id - 1] = slot_width;
priv->tdm_slots[dai->id - 1] = slots;
return 0;
}
const struct snd_soc_dai_ops tacna_dai_ops = {
.startup = &tacna_startup,
.set_fmt = &tacna_set_fmt,
.set_tdm_slot = &tacna_set_tdm_slot,
.hw_params = &tacna_hw_params,
.set_sysclk = &tacna_dai_set_sysclk,
};
EXPORT_SYMBOL_GPL(tacna_dai_ops);
const struct snd_soc_dai_ops tacna_simple_dai_ops = {
.startup = &tacna_startup,
.hw_params = &tacna_hw_params_rate,
.set_sysclk = &tacna_dai_set_sysclk,
};
EXPORT_SYMBOL_GPL(tacna_simple_dai_ops);
/*
* tacna_set_output_mode - Set the mode of the specified output
*
* @codec: Device to configure
* @output: Output number
* @diff: True to set the output to differential mode
*
* Some systems use external analogue switches to connect more
* analogue devices to the CODEC than are supported by the device. In
* some systems this requires changing the switched output from single
* ended to differential mode dynamically at runtime, an operation
* supported using this function.
*
* Most systems have a single static configuration and should use
* platform data instead.
*/
int tacna_set_output_mode(struct snd_soc_codec *codec, int output, bool diff)
{
unsigned int reg, val;
int ret;
if (output < 1 || output > TACNA_MAX_OUTPUT)
return -EINVAL;
reg = TACNA_OUT1L_CONTROL_1 + (output - 1) * 0x40;
if (diff)
val = TACNA_OUT1_MONO;
else
val = 0;
ret = snd_soc_update_bits(codec, reg, TACNA_OUT1_MONO, val);
if (ret < 0)
return ret;
else
return 0;
}
EXPORT_SYMBOL_GPL(tacna_set_output_mode);
int tacna_get_accdet_for_output(struct snd_soc_codec *codec, int output)
{
struct tacna *tacna = dev_get_drvdata(codec->dev->parent);
int i;
for (i = 0; i < ARRAY_SIZE(tacna->pdata.accdet); i++)
if (tacna->pdata.accdet[i].output == output)
return i;
return -ENODEV;
}
EXPORT_SYMBOL_GPL(tacna_get_accdet_for_output);
int tacna_sysclk_ev(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
struct tacna_priv *priv = snd_soc_codec_get_drvdata(codec);
tacna_spin_sysclk(priv);
return 0;
}
EXPORT_SYMBOL_GPL(tacna_sysclk_ev);
int tacna_in_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol,
int event)
{
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
struct tacna_priv *priv = snd_soc_codec_get_drvdata(codec);
unsigned int reg;
if (w->shift % 2)
reg = TACNA_IN1L_CONTROL2 + ((w->shift / 2) * 0x40);
else
reg = TACNA_IN1R_CONTROL2 + ((w->shift / 2) * 0x40);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
priv->in_up_pending++;
break;
case SND_SOC_DAPM_POST_PMU:
priv->in_up_pending--;
snd_soc_update_bits(codec, reg, TACNA_IN1L_MUTE, 0);
/* Uncached write-only register, no need for update_bits */
if (!priv->in_up_pending)
snd_soc_write(codec, priv->in_vu_reg, TACNA_IN_VU);
break;
case SND_SOC_DAPM_PRE_PMD:
snd_soc_update_bits(codec, reg,
TACNA_IN1L_MUTE, TACNA_IN1L_MUTE);
snd_soc_write(codec, priv->in_vu_reg, TACNA_IN_VU);
break;
default:
break;
}
return 0;
}
EXPORT_SYMBOL_GPL(tacna_in_ev);
int tacna_in_put_volsw(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct tacna_priv *priv = snd_soc_codec_get_drvdata(codec);
int ret;
ret = snd_soc_put_volsw(kcontrol, ucontrol);
if (ret < 0)
return ret;
/*
* Uncached write-only register, no need for update_bits.
* Will fail if codec is off but that will be handled by tacna_in_ev
*/
snd_soc_write(codec, priv->in_vu_reg, TACNA_IN_VU);
return 0;
}
EXPORT_SYMBOL_GPL(tacna_in_put_volsw);
int tacna_wait_for_output_seq(struct tacna_priv *priv, unsigned int mask,
unsigned int target)
{
unsigned int val;
int ret;
dev_dbg(priv->dev, "Polling output status for mask 0x%x = 0x%x\n",
mask, target);
ret = regmap_read_poll_timeout(priv->tacna->regmap,
TACNA_OUTPUT_STATUS_1,
val,
((val & mask) == target),
TACNA_CHANNEL_STATUS_POLL_US,
TACNA_MAX_OUTPUT_CHANNELS *
TACNA_CHANNEL_STATUS_POLL_TIMEOUT_US);
if (ret)
dev_warn(priv->dev,
"Failed to get output sequence bits 0x%x = 0x%x (%d)\n",
mask, val, ret);
return ret;
}
EXPORT_SYMBOL_GPL(tacna_wait_for_output_seq);
static void tacna_out_sync_regs(struct tacna_priv *priv)
{
struct regmap *regmap = priv->tacna->regmap;
unsigned int val;
int ret;
/*
* HP_CTRL is changed by write sequence but we want to keep it cached
* to avoid complicating control of other bits. So sync the cache
* after a write sequence
*/
regcache_drop_region(regmap, TACNA_HP_CTRL, TACNA_HP_CTRL);
ret = regmap_read(regmap, TACNA_HP_CTRL, &val);
if (ret)
dev_warn(priv->dev, "Failed to resync HP_CTRL (%d)\n", ret);
}
int tacna_out_ev(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
struct tacna_priv *priv = snd_soc_codec_get_drvdata(codec);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
switch (w->shift) {
case TACNA_OUT1L_EN_SHIFT:
priv->out_up_mask |= TACNA_OUT1L_STS;
break;
case TACNA_OUT1R_EN_SHIFT:
priv->out_up_mask |= TACNA_OUT1R_STS;
break;
case TACNA_OUT2L_EN_SHIFT:
priv->out_up_mask |= TACNA_OUT2L_STS;
break;
case TACNA_OUT2R_EN_SHIFT:
priv->out_up_mask |= TACNA_OUT2R_STS;
break;
default:
return 0;
}
priv->out_up_pending++;
break;
case SND_SOC_DAPM_POST_PMU:
switch (w->shift) {
case TACNA_OUT1L_EN_SHIFT:
case TACNA_OUT1R_EN_SHIFT:
case TACNA_OUT2L_EN_SHIFT:
case TACNA_OUT2R_EN_SHIFT:
priv->out_up_pending--;
if (priv->out_up_pending == 0) {
tacna_wait_for_output_seq(priv,
priv->out_up_mask,
priv->out_up_mask);
tacna_out_sync_regs(priv);
priv->out_up_mask = 0;
}
break;
default:
break;
}
break;
case SND_SOC_DAPM_PRE_PMD:
switch (w->shift) {
case TACNA_OUT1L_EN_SHIFT:
priv->out_down_mask |= TACNA_OUT1L_STS;
break;
case TACNA_OUT1R_EN_SHIFT:
priv->out_down_mask |= TACNA_OUT1R_STS;
break;
case TACNA_OUT2L_EN_SHIFT:
priv->out_down_mask |= TACNA_OUT2L_STS;
break;
case TACNA_OUT2R_EN_SHIFT:
priv->out_down_mask |= TACNA_OUT2R_STS;
break;
default:
return 0;
}
priv->out_down_pending++;
break;
case SND_SOC_DAPM_POST_PMD:
switch (w->shift) {
case TACNA_OUT1L_EN_SHIFT:
case TACNA_OUT1R_EN_SHIFT:
case TACNA_OUT2L_EN_SHIFT:
case TACNA_OUT2R_EN_SHIFT:
priv->out_down_pending--;
if (priv->out_down_pending == 0) {
tacna_wait_for_output_seq(priv,
priv->out_down_mask,
0);
tacna_out_sync_regs(priv);
priv->out_down_mask = 0;
}
break;
default:
break;
}
break;
default:
break;
}
return 0;
}
EXPORT_SYMBOL_GPL(tacna_out_ev);
int tacna_hp_ev(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
struct tacna_priv *priv = snd_soc_codec_get_drvdata(codec);
struct tacna *tacna = priv->tacna;
unsigned int mask = 1 << w->shift;
unsigned int out_num = (w->shift / 2) + 1;
unsigned int val;
int ret, accdet;
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 tacna_out_ev(w, kcontrol, event);
default:
return 0;
}
/* store desired output state */
tacna->hp_ena = (tacna->hp_ena & ~mask) | val;
/*
* disable output if clamp is active (output state will be applied when
* the clamp is disabled) or a short was detected
*/
accdet = tacna_get_accdet_for_output(codec, out_num);
if (accdet >= 0 &&
(tacna->hpdet_clamp[accdet] || tacna->hpdet_shorted[accdet]))
val = 0;
ret = regmap_update_bits(tacna->regmap, TACNA_OUTPUT_ENABLE_1,
mask, val);
if (ret)
dev_warn(priv->dev, "Failed to write output enable: %d\n", ret);
return tacna_out_ev(w, kcontrol, event);
}
EXPORT_SYMBOL_GPL(tacna_hp_ev);
int tacna_put_out1_demux(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_dapm_context *dapm =
snd_soc_dapm_kcontrol_dapm(kcontrol);
struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);
struct tacna_priv *priv = dev_get_drvdata(codec->dev);
struct tacna *tacna = priv->tacna;
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
unsigned int hp2_sel, mux, mask, cur;
bool change, out_mono;
int ret;
if (ucontrol->value.enumerated.item[0] > e->items - 1)
return -EINVAL;
mux = ucontrol->value.enumerated.item[0];
hp2_sel = mux << e->shift_l;
mask = e->mask << e->shift_l;
snd_soc_dapm_mutex_lock(dapm);
if (!snd_soc_test_bits(codec, e->reg, mask, hp2_sel)) {
snd_soc_dapm_mutex_unlock(dapm);
return 0;
}
ret = regmap_read(tacna->regmap, TACNA_OUTPUT_ENABLE_1, &cur);
if (ret)
dev_warn(codec->dev, "Failed to read OUTPUT_ENABLE_1: %d\n",
ret);
/* Don't change demux and mono settings while OUT1 is enabled */
ret = regmap_update_bits_check(tacna->regmap, TACNA_OUTPUT_ENABLE_1,
TACNA_OUT1L_EN_MASK | TACNA_OUT1R_EN_MASK,
0, &change);
if (ret) {
dev_warn(codec->dev, "Failed to disable outputs: %d\n", ret);
} else if (change) {
tacna_wait_for_output_seq(priv,
TACNA_OUT1L_STS | TACNA_OUT1R_STS,
0);
tacna_out_sync_regs(priv);
}
ret = regmap_update_bits(tacna->regmap, TACNA_HP_CTRL,
TACNA_OUT1_MODE_MASK,
hp2_sel << TACNA_OUT1_MODE_SHIFT);
if (ret) {
dev_warn(codec->dev, "Failed to set OUT1_MODE: %d\n", ret);
} else {
BUILD_BUG_ON(ARRAY_SIZE(tacna->pdata.codec.out_mono) < 3);
out_mono = tacna->pdata.codec.out_mono[mux];
ret = tacna_set_output_mode(codec, 1, out_mono);
if (ret < 0)
dev_warn(codec->dev,
"Failed to set output mode: %d\n", ret);
}
ret = regmap_update_bits_check(tacna->regmap, TACNA_OUTPUT_ENABLE_1,
TACNA_OUT1L_EN_MASK | TACNA_OUT1R_EN_MASK,
cur, &change);
if (ret) {
dev_warn(codec->dev, "Failed to restore outputs: %d\n", ret);
} else if (change) {
tacna_wait_for_output_seq(priv,
TACNA_OUT1L_STS | TACNA_OUT1R_STS,
cur);
tacna_out_sync_regs(priv);
}
snd_soc_dapm_mutex_unlock(dapm);
return snd_soc_dapm_mux_update_power(dapm, kcontrol, mux, e, NULL);
}
EXPORT_SYMBOL_GPL(tacna_put_out1_demux);
int tacna_get_out1_demux(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);
struct tacna_priv *priv = dev_get_drvdata(codec->dev);
struct tacna *tacna = priv->tacna;
unsigned int val;
int ret;
ret = regmap_read(tacna->regmap, TACNA_HP_CTRL, &val);
if (ret)
return ret;
val &= TACNA_OUT1_MODE_MASK;
val >>= TACNA_OUT1_MODE_SHIFT;
ucontrol->value.enumerated.item[0] = val;
return 0;
}
EXPORT_SYMBOL_GPL(tacna_get_out1_demux);
static int tacna_set_force_bypass(struct snd_soc_codec *codec, bool set_bypass)
{
struct tacna *tacna = dev_get_drvdata(codec->dev->parent);
struct arizona_micsupp_forced_bypass *micsupp_bypass =
tacna->micsupp_forced_bypass;
const struct tacna_micbias_pdata *micbias = tacna->pdata.micbias;
struct snd_soc_dapm_context *dapm = tacna->dapm;
const struct regulation_constraints *constraints;
unsigned int i, bypass = 0;
unsigned int num_micbiases;
bool sync = false, bypass_enabled;
if (!micsupp_bypass)
return -ENODEV;
if (set_bypass)
bypass = TACNA_MICB1_BYPASS;
snd_soc_dapm_mutex_lock(dapm);
mutex_lock(&micsupp_bypass->lock);
micsupp_bypass->forced = set_bypass;
if (set_bypass) {
dev_info(tacna->dev, "Set bypass: %d,%d\n",
micsupp_bypass->enabled, micsupp_bypass->regulated);
regmap_update_bits(tacna->regmap,
TACNA_CHARGE_PUMP1,
TACNA_CP2_BYPASS, TACNA_CP2_BYPASS);
if (micsupp_bypass->enabled && micsupp_bypass->regulated) {
snd_soc_dapm_disable_pin_unlocked(tacna->dapm,
"MICSUPP");
sync = true;
}
} else {
dev_info(tacna->dev, "Clear bypass: %d,%d\n",
micsupp_bypass->enabled, micsupp_bypass->regulated);
if (micsupp_bypass->regulated)
regmap_update_bits(tacna->regmap,
TACNA_CHARGE_PUMP1,
TACNA_CP2_BYPASS, 0);
if (micsupp_bypass->enabled && micsupp_bypass->regulated) {
snd_soc_dapm_force_enable_pin_unlocked(tacna->dapm,
"MICSUPP");
sync = true;
}
}
mutex_unlock(&micsupp_bypass->lock);
snd_soc_dapm_mutex_unlock(dapm);
if (sync)
snd_soc_dapm_sync(tacna->dapm);
num_micbiases = tacna_get_num_micbias(tacna);
for (i = 0; i < num_micbiases; i++) {
if (micbias[i].init_data)
constraints = &micbias[i].init_data->constraints;
else
constraints = NULL;
/*
* Bypass is permanently enabled if we have
* REGULATOR_CHANGE_BYPASS set
*/
bypass_enabled = !constraints ||
constraints->valid_ops_mask & REGULATOR_CHANGE_BYPASS;
/*
* Always enter bypass, but leaving bypass is allowed only if
* bypass is normally disabled.
*/
if (set_bypass || !bypass_enabled)
regmap_update_bits(tacna->regmap,
TACNA_MICBIAS_CTRL1 + i,
TACNA_MICB1_BYPASS,
bypass);
}
return 0;
}
int tacna_enable_force_bypass(struct snd_soc_codec *codec)
{
return tacna_set_force_bypass(codec, true);
}
EXPORT_SYMBOL_GPL(tacna_enable_force_bypass);
int tacna_disable_force_bypass(struct snd_soc_codec *codec)
{
return tacna_set_force_bypass(codec, false);
}
EXPORT_SYMBOL_GPL(tacna_disable_force_bypass);
static bool tacna_eq_filter_unstable(bool mode, __be16 in_a, __be16 in_b)
{
s16 a = be16_to_cpu(in_a);
s16 b = be16_to_cpu(in_b);
if (!mode) {
return abs(a) >= 4096;
} else {
if (abs(b) >= 4096)
return true;
return (abs((a << 16) / (4096 - b)) >= 4096 << 4);
}
}
int tacna_eq_ev(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol,
int event)
{
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
struct tacna_priv *priv = snd_soc_codec_get_drvdata(codec);
struct tacna *tacna = priv->tacna;
unsigned int mode = priv->eq_mode[w->shift];
unsigned int reg = TACNA_EQ1_BAND1_COEFF1 + (68 * w->shift);
__be16 *data = &priv->eq_coefficients[w->shift][0];
int ret = 0;
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
if (tacna_eq_filter_unstable(!!mode, data[1], data[0]) ||
tacna_eq_filter_unstable(true, data[7], data[6]) ||
tacna_eq_filter_unstable(true, data[13], data[12]) ||
tacna_eq_filter_unstable(true, data[19], data[18]) ||
tacna_eq_filter_unstable(false, data[25], data[24])) {
dev_err(priv->dev,
"Rejecting unstable EQ coefficients.\n"
"Last stable coefficients will be used.\n");
ret = -EINVAL;
} else {
ret = regmap_raw_write(tacna->regmap, reg, data,
TACNA_EQ_BLOCK_SZ);
if (ret < 0) {
dev_err(priv->dev,
"Error writing EQ coefficients: %d\n",
ret);
goto out;
}
ret = snd_soc_update_bits(codec, TACNA_EQ_CONTROL2,
w->mask, mode << w->shift);
if (ret < 0)
dev_err(priv->dev,
"Error writing EQ mode: %d\n",
ret);
}
break;
default:
break;
}
out:
return ret;
}
EXPORT_SYMBOL_GPL(tacna_eq_ev);
int tacna_anc_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol,
int event)
{
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(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_write(codec, TACNA_ANC_CTRL_1, val);
return 0;
}
EXPORT_SYMBOL_GPL(tacna_anc_ev);
static int tacna_get_variable_u32_array(struct tacna_priv *priv,
const char *propname,
u32 *dest,
int n_max,
int multiple)
{
struct tacna *tacna = priv->tacna;
int n, ret;
n = device_property_read_u32_array(tacna->dev, propname, NULL, 0);
if (n == -EINVAL) {
return 0; /* missing, ignore */
} else if (n < 0) {
dev_warn(priv->dev, "%s malformed (%d)\n", propname, n);
return -EINVAL;
} else if ((n % multiple) != 0) {
dev_warn(priv->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(tacna->dev, propname, dest, n);
if (ret < 0)
return ret;
else
return n;
}
static void tacna_prop_get_in_type(struct tacna_priv *priv)
{
struct tacna *tacna = priv->tacna;
u32 tmp[TACNA_MAX_INPUT * TACNA_MAX_MUXED_IN_CHANNELS];
int n, i, in_idx, ch_idx;
BUILD_BUG_ON(ARRAY_SIZE(tacna->pdata.codec.in_type) != TACNA_MAX_INPUT);
BUILD_BUG_ON(ARRAY_SIZE(tacna->pdata.codec.in_type[0]) !=
TACNA_MAX_MUXED_IN_CHANNELS);
n = tacna_get_variable_u32_array(priv,
"cirrus,in-type",
tmp,
ARRAY_SIZE(tmp),
TACNA_MAX_MUXED_IN_CHANNELS);
if (n < 0)
return;
in_idx = 0;
ch_idx = 0;
for (i = 0; i < n; ++i) {
tacna->pdata.codec.in_type[in_idx][ch_idx] = tmp[i];
if (++ch_idx == TACNA_MAX_MUXED_IN_CHANNELS) {
ch_idx = 0;
++in_idx;
}
}
}
static void tacna_prop_get_pdata(struct tacna_priv *priv)
{
struct tacna *tacna = priv->tacna;
struct tacna_codec_pdata *pdata = &tacna->pdata.codec;
u32 out_mono[ARRAY_SIZE(pdata->out_mono)];
u32 auxpdm_slave_mode[ARRAY_SIZE(pdata->auxpdm_slave_mode)];
u32 auxpdm_falling_edge[ARRAY_SIZE(pdata->auxpdm_falling_edge)];
int i, ret;
ret = tacna_get_variable_u32_array(priv,
"cirrus,max-channels-clocked",
pdata->max_channels_clocked,
ARRAY_SIZE(pdata->max_channels_clocked),
1);
if (ret < 0)
return;
tacna_prop_get_in_type(priv);
ret = device_property_read_u32_array(tacna->dev,
"cirrus,out-mono",
NULL, 0);
if (ret > 0) {
if (ret > ARRAY_SIZE(out_mono))
ret = ARRAY_SIZE(out_mono);
memset(out_mono, 0, sizeof(out_mono));
device_property_read_u32_array(tacna->dev,
"cirrus,out-mono",
out_mono,
ret);
for (i = 0; i < ARRAY_SIZE(out_mono); ++i)
pdata->out_mono[i] = !!out_mono[i];
}
device_property_read_u32(tacna->dev, "cirrus,pdm-fmt", &pdata->pdm_fmt);
device_property_read_u32(tacna->dev, "cirrus,pdm-mute",
&pdata->pdm_mute);
tacna_get_variable_u32_array(priv,
"cirrus,pdm-sup",
pdata->pdm_sup,
ARRAY_SIZE(pdata->pdm_sup),
1);
memset(auxpdm_slave_mode, 0, sizeof(auxpdm_slave_mode));
device_property_read_u32_array(tacna->dev,
"cirrus,auxpdm-slave-mode",
auxpdm_slave_mode,
ARRAY_SIZE(auxpdm_slave_mode));
for (i = 0; i < ARRAY_SIZE(auxpdm_slave_mode); ++i)
pdata->auxpdm_slave_mode[i] = !!auxpdm_slave_mode[i];
memset(auxpdm_falling_edge, 0, sizeof(auxpdm_falling_edge));
device_property_read_u32_array(tacna->dev,
"cirrus,auxpdm-falling-edge",
auxpdm_falling_edge,
ARRAY_SIZE(auxpdm_falling_edge));
for (i = 0; i < ARRAY_SIZE(auxpdm_falling_edge); ++i)
pdata->auxpdm_falling_edge[i] = !!auxpdm_falling_edge[i];
}
int tacna_init_inputs(struct snd_soc_codec *codec)
{
struct tacna_priv *priv = snd_soc_codec_get_drvdata(codec);
struct tacna *tacna = priv->tacna;
unsigned int ana_mode_l, ana_mode_r, dig_mode;
int i;
/*
* 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 < priv->max_analogue_inputs; i++) {
dev_dbg(priv->dev, "IN%d type %u:%u:%u:%u\n", i + 1,
tacna->pdata.codec.in_type[i][0],
tacna->pdata.codec.in_type[i][1],
tacna->pdata.codec.in_type[i][2],
tacna->pdata.codec.in_type[i][3]);
switch (tacna->pdata.codec.in_type[i][0]) {
case TACNA_IN_TYPE_DIFF:
ana_mode_l = 0;
break;
case TACNA_IN_TYPE_SE:
ana_mode_l = 1 << TACNA_IN1L_SRC_SHIFT;
break;
default:
dev_warn(priv->dev,
"IN%dL_1 Illegal in_type %u ignored\n",
i + 1, tacna->pdata.codec.in_type[i][0]);
continue;
}
switch (tacna->pdata.codec.in_type[i][1]) {
case TACNA_IN_TYPE_DIFF:
ana_mode_r = 0;
break;
case TACNA_IN_TYPE_SE:
ana_mode_r = 1 << TACNA_IN1R_SRC_SHIFT;
break;
default:
dev_warn(priv->dev,
"IN%dR_1 Illegal in_type %u ignored\n",
i + 1, tacna->pdata.codec.in_type[i][1]);
continue;
}
dev_dbg(priv->dev,
"IN%d_1 Analogue mode=0x%x,0x%x\n",
i + 1, ana_mode_l, ana_mode_r);
regmap_update_bits(tacna->regmap,
TACNA_IN1L_CONTROL1 + (i * 0x40),
TACNA_IN1L_SRC_MASK,
ana_mode_l);
regmap_update_bits(tacna->regmap,
TACNA_IN1R_CONTROL1 + (i * 0x40),
TACNA_IN1R_SRC_MASK,
ana_mode_r);
}
for (i = 0; i < priv->max_pdm_sup; i++) {
dig_mode = tacna->pdata.codec.pdm_sup[i] <<
TACNA_IN1_PDM_SUP_SHIFT;
dev_dbg(priv->dev,
"IN%d PDM_SUP=0x%x\n", i + 1, dig_mode);
regmap_update_bits(tacna->regmap,
TACNA_INPUT1_CONTROL1 + (i * 0x40),
TACNA_IN1_PDM_SUP_MASK, dig_mode);
}
return 0;
}
EXPORT_SYMBOL_GPL(tacna_init_inputs);
int tacna_init_auxpdm(struct snd_soc_codec *codec, int n_auxpdm)
{
struct tacna_priv *priv = snd_soc_codec_get_drvdata(codec);
struct tacna *tacna = priv->tacna;
const struct tacna_codec_pdata *pdata = &tacna->pdata.codec;
unsigned int val;
int i;
for (i = 0; i < n_auxpdm; ++i)
{
if (pdata->auxpdm_slave_mode[i])
val = 0;
else
val = TACNA_AUXPDM1_MSTR_MASK;
if (pdata->auxpdm_falling_edge[i])
val |= TACNA_AUXPDM1_TXEDGE_MASK;
regmap_update_bits(tacna->regmap,
TACNA_AUXPDM1_CONTROL1 + (i * 0x8),
TACNA_AUXPDM1_TXEDGE_MASK |
TACNA_AUXPDM1_MSTR_MASK,
val);
}
return 0;
}
EXPORT_SYMBOL_GPL(tacna_init_auxpdm);
int tacna_init_outputs(struct snd_soc_codec *codec,
const struct tacna_mono_route *mono_routes,
int n_mono_routes)
{
struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
struct tacna_priv *priv = snd_soc_codec_get_drvdata(codec);
struct tacna *tacna = priv->tacna;
const struct tacna_codec_pdata *pdata = &tacna->pdata.codec;
unsigned int val;
int i, ret;
/* Output clock defaults to sync rate */
ret = regmap_update_bits(tacna->regmap, TACNA_OUTPUT_CONTROL_1,
TACNA_OUT_RATE_MASK | TACNA_OUT_CLK_MODE_MASK,
TACNA_OUT_CLK_MODE);
if (ret)
dev_warn(priv->dev, "Failed to write 0x%x: %d\n",
TACNA_OUTPUT_CONTROL_1, ret);
if (n_mono_routes > ARRAY_SIZE(pdata->out_mono)) {
dev_warn(priv->dev,
"Requested %d mono outputs, using maximum allowed %zu\n",
n_mono_routes, ARRAY_SIZE(pdata->out_mono));
n_mono_routes = ARRAY_SIZE(pdata->out_mono);
}
for (i = 0; i < n_mono_routes; i++) {
if (!mono_routes[i].n_routes)
continue; /* not available or can't do mono */
dev_dbg(priv->dev, "out_mono[%d]=0x%x\n",
i, pdata->out_mono[i]);
/* Default is 0 so no-op with defaults */
if (pdata->out_mono[i]) {
val = TACNA_OUT1_MONO;
ret = snd_soc_dapm_add_routes(dapm,
mono_routes[i].routes,
mono_routes[i].n_routes);
if (ret)
dev_warn(priv->dev,
"Failed to add %s mono routes (%d)\n",
mono_routes[i].routes[0].sink,
ret);
} else {
val = 0;
}
if (mono_routes[i].cfg_reg) {
ret = regmap_update_bits(tacna->regmap,
mono_routes[i].cfg_reg,
TACNA_OUT1_MONO, val);
if (ret)
dev_warn(priv->dev,
"Failed to write mono to 0x%x (%d)\n",
mono_routes[i].cfg_reg, ret);
}
}
dev_dbg(priv->dev, "OUT5 fmt=0x%x mute=0x%x\n",
pdata->pdm_fmt, pdata->pdm_mute);
if (pdata->pdm_mute)
regmap_update_bits(tacna->regmap,
TACNA_OUT5PDM_CONTROL_1,
TACNA_OUT5PDM_MUTE_SEQ_MASK,
pdata->pdm_mute);
if (pdata->pdm_fmt)
regmap_update_bits(tacna->regmap,
TACNA_OUT5PDM_CONTROL_1,
TACNA_OUT5PDM_MUTE_ENDIAN_MASK |
TACNA_OUT5PDM_FMT_MASK,
pdata->pdm_fmt);
return 0;
}
EXPORT_SYMBOL_GPL(tacna_init_outputs);
int tacna_init_dai(struct tacna_priv *priv, int id)
{
struct tacna_dai_priv *dai_priv = &priv->dai[id];
dai_priv->clk = TACNA_CLK_SYSCLK_1;
dai_priv->constraint = tacna_constraint;
return 0;
}
EXPORT_SYMBOL_GPL(tacna_init_dai);
int tacna_init_eq(struct tacna_priv *priv)
{
struct tacna *tacna = priv->tacna;
unsigned int reg = TACNA_EQ1_BAND1_COEFF1, mode;
__be16 *data;
int i, ret;
ret = regmap_read(tacna->regmap, TACNA_EQ_CONTROL2, &mode);
if (ret < 0) {
dev_err(priv->dev, "Error reading EQ mode: %d\n", ret);
goto out;
}
for (i = 0; i < 4; ++i) {
priv->eq_mode[i] = (mode >> i) & 0x1;
data = &priv->eq_coefficients[i][0];
ret = regmap_raw_read(tacna->regmap, reg + (i * 68), data,
TACNA_EQ_BLOCK_SZ);
if (ret < 0) {
dev_err(priv->dev,
"Error reading EQ coefficients: %d\n",
ret);
goto out;
}
}
out:
return ret;
}
EXPORT_SYMBOL_GPL(tacna_init_eq);
int tacna_core_init(struct tacna_priv *priv)
{
BUILD_BUG_ON(ARRAY_SIZE(tacna_mixer_texts) != TACNA_NUM_MIXER_INPUTS);
BUILD_BUG_ON(ARRAY_SIZE(tacna_mixer_values) != TACNA_NUM_MIXER_INPUTS);
BUILD_BUG_ON(tacna_sample_rate_text[TACNA_SAMPLE_RATE_ENUM_SIZE - 1]
== NULL);
BUILD_BUG_ON(tacna_sample_rate_val[TACNA_SAMPLE_RATE_ENUM_SIZE - 1]
== 0);
if (!dev_get_platdata(priv->tacna->dev))
tacna_prop_get_pdata(priv);
mutex_init(&priv->rate_lock);
return 0;
}
EXPORT_SYMBOL_GPL(tacna_core_init);
int tacna_core_destroy(struct tacna_priv *priv)
{
mutex_destroy(&priv->rate_lock);
return 0;
}
EXPORT_SYMBOL_GPL(tacna_core_destroy);
struct regmap *tacna_get_regmap_dsp(struct snd_soc_codec *codec,
unsigned int dsp_no)
{
struct tacna *tacna = dev_get_drvdata(codec->dev->parent);
if (dsp_no < TACNA_MAX_DSPS)
return tacna->dsp_regmap[dsp_no];
else
return NULL;
}
EXPORT_SYMBOL_GPL(tacna_get_regmap_dsp);
MODULE_DESCRIPTION("ASoC Cirrus Logic Tacna codec support");
MODULE_AUTHOR("Charles Keepax <ckeepax@opensource.wolfsonmicro.com>");
MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.wolfsonmicro.com>");
MODULE_AUTHOR("Piotr Stankiewicz <piotrs@opensource.wolfsonmicro.com>");
MODULE_LICENSE("GPL v2");