Merge remote-tracking branch 'asoc/topic/wm8994' into asoc-next

This commit is contained in:
Mark Brown 2013-06-17 17:20:32 +01:00
commit 5464389755
4 changed files with 161 additions and 43 deletions

View file

@ -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.
*/

View file

@ -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 */

View file

@ -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]);

View file

@ -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;