forked from Minki/linux
Merge remote-tracking branch 'asoc/topic/wm8994' into asoc-next
This commit is contained in:
commit
5464389755
@ -182,6 +182,11 @@ struct wm8994_pdata {
|
||||
*/
|
||||
int micdet_delay;
|
||||
|
||||
/* Delay between microphone detect completing and reporting on
|
||||
* insert (specified in ms)
|
||||
*/
|
||||
int mic_id_delay;
|
||||
|
||||
/* IRQ for microphone detection if brought out directly as a
|
||||
* signal.
|
||||
*/
|
||||
|
@ -2668,6 +2668,10 @@
|
||||
/*
|
||||
* R772 (0x304) - AIF1ADC LRCLK
|
||||
*/
|
||||
#define WM8958_AIF1_LRCLK_INV 0x1000 /* AIF1_LRCLK_INV */
|
||||
#define WM8958_AIF1_LRCLK_INV_MASK 0x1000 /* AIF1_LRCLK_INV */
|
||||
#define WM8958_AIF1_LRCLK_INV_SHIFT 12 /* AIF1_LRCLK_INV */
|
||||
#define WM8958_AIF1_LRCLK_INV_WIDTH 1 /* AIF1_LRCLK_INV */
|
||||
#define WM8994_AIF1ADC_LRCLK_DIR 0x0800 /* AIF1ADC_LRCLK_DIR */
|
||||
#define WM8994_AIF1ADC_LRCLK_DIR_MASK 0x0800 /* AIF1ADC_LRCLK_DIR */
|
||||
#define WM8994_AIF1ADC_LRCLK_DIR_SHIFT 11 /* AIF1ADC_LRCLK_DIR */
|
||||
@ -2679,6 +2683,10 @@
|
||||
/*
|
||||
* R773 (0x305) - AIF1DAC LRCLK
|
||||
*/
|
||||
#define WM8958_AIF1_LRCLK_INV 0x1000 /* AIF1_LRCLK_INV */
|
||||
#define WM8958_AIF1_LRCLK_INV_MASK 0x1000 /* AIF1_LRCLK_INV */
|
||||
#define WM8958_AIF1_LRCLK_INV_SHIFT 12 /* AIF1_LRCLK_INV */
|
||||
#define WM8958_AIF1_LRCLK_INV_WIDTH 1 /* AIF1_LRCLK_INV */
|
||||
#define WM8994_AIF1DAC_LRCLK_DIR 0x0800 /* AIF1DAC_LRCLK_DIR */
|
||||
#define WM8994_AIF1DAC_LRCLK_DIR_MASK 0x0800 /* AIF1DAC_LRCLK_DIR */
|
||||
#define WM8994_AIF1DAC_LRCLK_DIR_SHIFT 11 /* AIF1DAC_LRCLK_DIR */
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include <linux/init.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/gcd.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
@ -1498,6 +1499,24 @@ static const char *aif1dac_text[] = {
|
||||
"AIF1DACDAT", "AIF3DACDAT",
|
||||
};
|
||||
|
||||
static const char *loopback_text[] = {
|
||||
"None", "ADCDAT",
|
||||
};
|
||||
|
||||
static const struct soc_enum aif1_loopback_enum =
|
||||
SOC_ENUM_SINGLE(WM8994_AIF1_CONTROL_2, WM8994_AIF1_LOOPBACK_SHIFT, 2,
|
||||
loopback_text);
|
||||
|
||||
static const struct snd_kcontrol_new aif1_loopback =
|
||||
SOC_DAPM_ENUM("AIF1 Loopback", aif1_loopback_enum);
|
||||
|
||||
static const struct soc_enum aif2_loopback_enum =
|
||||
SOC_ENUM_SINGLE(WM8994_AIF2_CONTROL_2, WM8994_AIF2_LOOPBACK_SHIFT, 2,
|
||||
loopback_text);
|
||||
|
||||
static const struct snd_kcontrol_new aif2_loopback =
|
||||
SOC_DAPM_ENUM("AIF2 Loopback", aif2_loopback_enum);
|
||||
|
||||
static const struct soc_enum aif1dac_enum =
|
||||
SOC_ENUM_SINGLE(WM8994_POWER_MANAGEMENT_6, 0, 2, aif1dac_text);
|
||||
|
||||
@ -1744,6 +1763,9 @@ SND_SOC_DAPM_ADC("DMIC1R", NULL, WM8994_POWER_MANAGEMENT_4, 2, 0),
|
||||
SND_SOC_DAPM_ADC("ADCL", NULL, SND_SOC_NOPM, 1, 0),
|
||||
SND_SOC_DAPM_ADC("ADCR", NULL, SND_SOC_NOPM, 0, 0),
|
||||
|
||||
SND_SOC_DAPM_MUX("AIF1 Loopback", SND_SOC_NOPM, 0, 0, &aif1_loopback),
|
||||
SND_SOC_DAPM_MUX("AIF2 Loopback", SND_SOC_NOPM, 0, 0, &aif2_loopback),
|
||||
|
||||
SND_SOC_DAPM_POST("Debug log", post_ev),
|
||||
};
|
||||
|
||||
@ -1875,9 +1897,9 @@ static const struct snd_soc_dapm_route intercon[] = {
|
||||
{ "AIF1DAC2L", NULL, "AIF1DAC Mux" },
|
||||
{ "AIF1DAC2R", NULL, "AIF1DAC Mux" },
|
||||
|
||||
{ "AIF1DAC Mux", "AIF1DACDAT", "AIF1DACDAT" },
|
||||
{ "AIF1DAC Mux", "AIF1DACDAT", "AIF1 Loopback" },
|
||||
{ "AIF1DAC Mux", "AIF3DACDAT", "AIF3DACDAT" },
|
||||
{ "AIF2DAC Mux", "AIF2DACDAT", "AIF2DACDAT" },
|
||||
{ "AIF2DAC Mux", "AIF2DACDAT", "AIF2 Loopback" },
|
||||
{ "AIF2DAC Mux", "AIF3DACDAT", "AIF3DACDAT" },
|
||||
{ "AIF2ADC Mux", "AIF2ADCDAT", "AIF2ADCL" },
|
||||
{ "AIF2ADC Mux", "AIF2ADCDAT", "AIF2ADCR" },
|
||||
@ -1928,6 +1950,12 @@ static const struct snd_soc_dapm_route intercon[] = {
|
||||
{ "AIF3ADCDAT", "AIF2DACDAT", "AIF2DACL" },
|
||||
{ "AIF3ADCDAT", "AIF2DACDAT", "AIF2DACR" },
|
||||
|
||||
/* Loopback */
|
||||
{ "AIF1 Loopback", "ADCDAT", "AIF1ADCDAT" },
|
||||
{ "AIF1 Loopback", "None", "AIF1DACDAT" },
|
||||
{ "AIF2 Loopback", "ADCDAT", "AIF2ADCDAT" },
|
||||
{ "AIF2 Loopback", "None", "AIF2DACDAT" },
|
||||
|
||||
/* Sidetone */
|
||||
{ "Left Sidetone", "ADC/DMIC1", "ADCL Mux" },
|
||||
{ "Left Sidetone", "DMIC2", "DMIC2L" },
|
||||
@ -2010,15 +2038,16 @@ struct fll_div {
|
||||
u16 outdiv;
|
||||
u16 n;
|
||||
u16 k;
|
||||
u16 lambda;
|
||||
u16 clk_ref_div;
|
||||
u16 fll_fratio;
|
||||
};
|
||||
|
||||
static int wm8994_get_fll_config(struct fll_div *fll,
|
||||
static int wm8994_get_fll_config(struct wm8994 *control, struct fll_div *fll,
|
||||
int freq_in, int freq_out)
|
||||
{
|
||||
u64 Kpart;
|
||||
unsigned int K, Ndiv, Nmod;
|
||||
unsigned int K, Ndiv, Nmod, gcd_fll;
|
||||
|
||||
pr_debug("FLL input=%dHz, output=%dHz\n", freq_in, freq_out);
|
||||
|
||||
@ -2067,20 +2096,32 @@ static int wm8994_get_fll_config(struct fll_div *fll,
|
||||
Nmod = freq_out % freq_in;
|
||||
pr_debug("Nmod=%d\n", Nmod);
|
||||
|
||||
/* Calculate fractional part - scale up so we can round. */
|
||||
Kpart = FIXED_FLL_SIZE * (long long)Nmod;
|
||||
switch (control->type) {
|
||||
case WM8994:
|
||||
/* Calculate fractional part - scale up so we can round. */
|
||||
Kpart = FIXED_FLL_SIZE * (long long)Nmod;
|
||||
|
||||
do_div(Kpart, freq_in);
|
||||
do_div(Kpart, freq_in);
|
||||
|
||||
K = Kpart & 0xFFFFFFFF;
|
||||
K = Kpart & 0xFFFFFFFF;
|
||||
|
||||
if ((K % 10) >= 5)
|
||||
K += 5;
|
||||
if ((K % 10) >= 5)
|
||||
K += 5;
|
||||
|
||||
/* Move down to proper range now rounding is done */
|
||||
fll->k = K / 10;
|
||||
/* Move down to proper range now rounding is done */
|
||||
fll->k = K / 10;
|
||||
fll->lambda = 0;
|
||||
|
||||
pr_debug("N=%x K=%x\n", fll->n, fll->k);
|
||||
pr_debug("N=%x K=%x\n", fll->n, fll->k);
|
||||
break;
|
||||
|
||||
default:
|
||||
gcd_fll = gcd(freq_out, freq_in);
|
||||
|
||||
fll->k = (freq_out - (freq_in * fll->n)) / gcd_fll;
|
||||
fll->lambda = freq_in / gcd_fll;
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -2144,9 +2185,9 @@ static int _wm8994_set_fll(struct snd_soc_codec *codec, int id, int src,
|
||||
* analysis bugs spewing warnings.
|
||||
*/
|
||||
if (freq_out)
|
||||
ret = wm8994_get_fll_config(&fll, freq_in, freq_out);
|
||||
ret = wm8994_get_fll_config(control, &fll, freq_in, freq_out);
|
||||
else
|
||||
ret = wm8994_get_fll_config(&fll, wm8994->fll[id].in,
|
||||
ret = wm8994_get_fll_config(control, &fll, wm8994->fll[id].in,
|
||||
wm8994->fll[id].out);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
@ -2191,6 +2232,17 @@ static int _wm8994_set_fll(struct snd_soc_codec *codec, int id, int src,
|
||||
WM8994_FLL1_N_MASK,
|
||||
fll.n << WM8994_FLL1_N_SHIFT);
|
||||
|
||||
if (fll.lambda) {
|
||||
snd_soc_update_bits(codec, WM8958_FLL1_EFS_1 + reg_offset,
|
||||
WM8958_FLL1_LAMBDA_MASK,
|
||||
fll.lambda);
|
||||
snd_soc_update_bits(codec, WM8958_FLL1_EFS_2 + reg_offset,
|
||||
WM8958_FLL1_EFS_ENA, WM8958_FLL1_EFS_ENA);
|
||||
} else {
|
||||
snd_soc_update_bits(codec, WM8958_FLL1_EFS_2 + reg_offset,
|
||||
WM8958_FLL1_EFS_ENA, 0);
|
||||
}
|
||||
|
||||
snd_soc_update_bits(codec, WM8994_FLL1_CONTROL_5 + reg_offset,
|
||||
WM8994_FLL1_FRC_NCO | WM8958_FLL1_BYP |
|
||||
WM8994_FLL1_REFCLK_DIV_MASK |
|
||||
@ -2555,17 +2607,24 @@ static int wm8994_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
|
||||
struct wm8994 *control = wm8994->wm8994;
|
||||
int ms_reg;
|
||||
int aif1_reg;
|
||||
int dac_reg;
|
||||
int adc_reg;
|
||||
int ms = 0;
|
||||
int aif1 = 0;
|
||||
int lrclk = 0;
|
||||
|
||||
switch (dai->id) {
|
||||
case 1:
|
||||
ms_reg = WM8994_AIF1_MASTER_SLAVE;
|
||||
aif1_reg = WM8994_AIF1_CONTROL_1;
|
||||
dac_reg = WM8994_AIF1DAC_LRCLK;
|
||||
adc_reg = WM8994_AIF1ADC_LRCLK;
|
||||
break;
|
||||
case 2:
|
||||
ms_reg = WM8994_AIF2_MASTER_SLAVE;
|
||||
aif1_reg = WM8994_AIF2_CONTROL_1;
|
||||
dac_reg = WM8994_AIF1DAC_LRCLK;
|
||||
adc_reg = WM8994_AIF1ADC_LRCLK;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
@ -2584,6 +2643,7 @@ static int wm8994_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
|
||||
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
|
||||
case SND_SOC_DAIFMT_DSP_B:
|
||||
aif1 |= WM8994_AIF1_LRCLK_INV;
|
||||
lrclk |= WM8958_AIF1_LRCLK_INV;
|
||||
case SND_SOC_DAIFMT_DSP_A:
|
||||
aif1 |= 0x18;
|
||||
break;
|
||||
@ -2622,12 +2682,14 @@ static int wm8994_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
|
||||
break;
|
||||
case SND_SOC_DAIFMT_IB_IF:
|
||||
aif1 |= WM8994_AIF1_BCLK_INV | WM8994_AIF1_LRCLK_INV;
|
||||
lrclk |= WM8958_AIF1_LRCLK_INV;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_IB_NF:
|
||||
aif1 |= WM8994_AIF1_BCLK_INV;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_NB_IF:
|
||||
aif1 |= WM8994_AIF1_LRCLK_INV;
|
||||
lrclk |= WM8958_AIF1_LRCLK_INV;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
@ -2658,6 +2720,10 @@ static int wm8994_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
|
||||
aif1);
|
||||
snd_soc_update_bits(codec, ms_reg, WM8994_AIF1_MSTR,
|
||||
ms);
|
||||
snd_soc_update_bits(codec, dac_reg,
|
||||
WM8958_AIF1_LRCLK_INV, lrclk);
|
||||
snd_soc_update_bits(codec, adc_reg,
|
||||
WM8958_AIF1_LRCLK_INV, lrclk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -3096,24 +3162,7 @@ static int wm8994_codec_suspend(struct snd_soc_codec *codec)
|
||||
static int wm8994_codec_resume(struct snd_soc_codec *codec)
|
||||
{
|
||||
struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
|
||||
struct wm8994 *control = wm8994->wm8994;
|
||||
int i, ret;
|
||||
unsigned int val, mask;
|
||||
|
||||
if (control->revision < 4) {
|
||||
/* force a HW read */
|
||||
ret = regmap_read(control->regmap,
|
||||
WM8994_POWER_MANAGEMENT_5, &val);
|
||||
|
||||
/* modify the cache only */
|
||||
codec->cache_only = 1;
|
||||
mask = WM8994_DAC1R_ENA | WM8994_DAC1L_ENA |
|
||||
WM8994_DAC2R_ENA | WM8994_DAC2L_ENA;
|
||||
val &= mask;
|
||||
snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_5,
|
||||
mask, val);
|
||||
codec->cache_only = 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(wm8994->fll); i++) {
|
||||
if (!wm8994->fll_suspend[i].out)
|
||||
@ -3495,6 +3544,31 @@ static void wm8958_button_det(struct snd_soc_codec *codec, u16 status)
|
||||
wm8994->btn_mask);
|
||||
}
|
||||
|
||||
static void wm8958_open_circuit_work(struct work_struct *work)
|
||||
{
|
||||
struct wm8994_priv *wm8994 = container_of(work,
|
||||
struct wm8994_priv,
|
||||
open_circuit_work.work);
|
||||
struct device *dev = wm8994->wm8994->dev;
|
||||
|
||||
wm1811_micd_stop(wm8994->hubs.codec);
|
||||
|
||||
mutex_lock(&wm8994->accdet_lock);
|
||||
|
||||
dev_dbg(dev, "Reporting open circuit\n");
|
||||
|
||||
wm8994->jack_mic = false;
|
||||
wm8994->mic_detecting = true;
|
||||
|
||||
wm8958_micd_set_rate(wm8994->hubs.codec);
|
||||
|
||||
snd_soc_jack_report(wm8994->micdet[0].jack, 0,
|
||||
wm8994->btn_mask |
|
||||
SND_JACK_HEADSET);
|
||||
|
||||
mutex_unlock(&wm8994->accdet_lock);
|
||||
}
|
||||
|
||||
static void wm8958_mic_id(void *data, u16 status)
|
||||
{
|
||||
struct snd_soc_codec *codec = data;
|
||||
@ -3504,16 +3578,9 @@ static void wm8958_mic_id(void *data, u16 status)
|
||||
if (!(status & WM8958_MICD_STS)) {
|
||||
/* If nothing present then clear our statuses */
|
||||
dev_dbg(codec->dev, "Detected open circuit\n");
|
||||
wm8994->jack_mic = false;
|
||||
wm8994->mic_detecting = true;
|
||||
|
||||
wm1811_micd_stop(codec);
|
||||
|
||||
wm8958_micd_set_rate(codec);
|
||||
|
||||
snd_soc_jack_report(wm8994->micdet[0].jack, 0,
|
||||
wm8994->btn_mask |
|
||||
SND_JACK_HEADSET);
|
||||
schedule_delayed_work(&wm8994->open_circuit_work,
|
||||
msecs_to_jiffies(2500));
|
||||
return;
|
||||
}
|
||||
|
||||
@ -3598,6 +3665,8 @@ static irqreturn_t wm1811_jackdet_irq(int irq, void *data)
|
||||
|
||||
pm_runtime_get_sync(codec->dev);
|
||||
|
||||
cancel_delayed_work_sync(&wm8994->mic_complete_work);
|
||||
|
||||
mutex_lock(&wm8994->accdet_lock);
|
||||
|
||||
reg = snd_soc_read(codec, WM1811_JACKDET_CTRL);
|
||||
@ -3780,11 +3849,33 @@ int wm8958_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wm8958_mic_detect);
|
||||
|
||||
static void wm8958_mic_work(struct work_struct *work)
|
||||
{
|
||||
struct wm8994_priv *wm8994 = container_of(work,
|
||||
struct wm8994_priv,
|
||||
mic_complete_work.work);
|
||||
struct snd_soc_codec *codec = wm8994->hubs.codec;
|
||||
|
||||
dev_crit(codec->dev, "MIC WORK %x\n", wm8994->mic_status);
|
||||
|
||||
pm_runtime_get_sync(codec->dev);
|
||||
|
||||
mutex_lock(&wm8994->accdet_lock);
|
||||
|
||||
wm8994->mic_id_cb(wm8994->mic_id_cb_data, wm8994->mic_status);
|
||||
|
||||
mutex_unlock(&wm8994->accdet_lock);
|
||||
|
||||
pm_runtime_put(codec->dev);
|
||||
|
||||
dev_crit(codec->dev, "MIC WORK %x DONE\n", wm8994->mic_status);
|
||||
}
|
||||
|
||||
static irqreturn_t wm8958_mic_irq(int irq, void *data)
|
||||
{
|
||||
struct wm8994_priv *wm8994 = data;
|
||||
struct snd_soc_codec *codec = wm8994->hubs.codec;
|
||||
int reg, count, ret;
|
||||
int reg, count, ret, id_delay;
|
||||
|
||||
/*
|
||||
* Jack detection may have detected a removal simulataneously
|
||||
@ -3794,6 +3885,9 @@ static irqreturn_t wm8958_mic_irq(int irq, void *data)
|
||||
if (!(snd_soc_read(codec, WM8958_MIC_DETECT_1) & WM8958_MICD_ENA))
|
||||
return IRQ_HANDLED;
|
||||
|
||||
cancel_delayed_work_sync(&wm8994->mic_complete_work);
|
||||
cancel_delayed_work_sync(&wm8994->open_circuit_work);
|
||||
|
||||
pm_runtime_get_sync(codec->dev);
|
||||
|
||||
/* We may occasionally read a detection without an impedence
|
||||
@ -3846,8 +3940,12 @@ static irqreturn_t wm8958_mic_irq(int irq, void *data)
|
||||
goto out;
|
||||
}
|
||||
|
||||
wm8994->mic_status = reg;
|
||||
id_delay = wm8994->wm8994->pdata.mic_id_delay;
|
||||
|
||||
if (wm8994->mic_detecting)
|
||||
wm8994->mic_id_cb(wm8994->mic_id_cb_data, reg);
|
||||
schedule_delayed_work(&wm8994->mic_complete_work,
|
||||
msecs_to_jiffies(id_delay));
|
||||
else
|
||||
wm8958_button_det(codec, reg);
|
||||
|
||||
@ -3899,6 +3997,8 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
|
||||
mutex_init(&wm8994->accdet_lock);
|
||||
INIT_DELAYED_WORK(&wm8994->jackdet_bootstrap,
|
||||
wm1811_jackdet_bootstrap);
|
||||
INIT_DELAYED_WORK(&wm8994->open_circuit_work,
|
||||
wm8958_open_circuit_work);
|
||||
|
||||
switch (control->type) {
|
||||
case WM8994:
|
||||
@ -3911,6 +4011,8 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
|
||||
break;
|
||||
}
|
||||
|
||||
INIT_DELAYED_WORK(&wm8994->mic_complete_work, wm8958_mic_work);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(wm8994->fll_locked); i++)
|
||||
init_completion(&wm8994->fll_locked[i]);
|
||||
|
||||
|
@ -134,6 +134,9 @@ struct wm8994_priv {
|
||||
struct mutex accdet_lock;
|
||||
struct wm8994_micdet micdet[2];
|
||||
struct delayed_work mic_work;
|
||||
struct delayed_work open_circuit_work;
|
||||
struct delayed_work mic_complete_work;
|
||||
u16 mic_status;
|
||||
bool mic_detecting;
|
||||
bool jack_mic;
|
||||
int btn_mask;
|
||||
|
Loading…
Reference in New Issue
Block a user