Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 54643897 authored by Mark Brown's avatar Mark Brown
Browse files

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

parents f57019aa 2da1c4bf
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -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.
	 */
+8 −0
Original line number Diff line number Diff line
@@ -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 */
+145 −43
Original line number Diff line number Diff line
@@ -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,6 +2096,8 @@ static int wm8994_get_fll_config(struct fll_div *fll,
	Nmod = freq_out % freq_in;
	pr_debug("Nmod=%d\n", Nmod);

	switch (control->type) {
	case WM8994:
		/* Calculate fractional part - scale up so we can round. */
		Kpart = FIXED_FLL_SIZE * (long long)Nmod;

@@ -2079,8 +2110,18 @@ static int wm8994_get_fll_config(struct fll_div *fll,

		/* 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);
		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]);

+3 −0
Original line number Diff line number Diff line
@@ -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;