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

Commit 02dc14d6 authored by Mark Brown's avatar Mark Brown
Browse files

Merge remote-tracking branches 'asoc/topic/wm8741', 'asoc/topic/wm8753',...

Merge remote-tracking branches 'asoc/topic/wm8741', 'asoc/topic/wm8753', 'asoc/topic/wm8904', 'asoc/topic/wm8960' and 'asoc/topic/wm8983' into asoc-next
Loading
Loading
Loading
Loading
+14 −48
Original line number Diff line number Diff line
@@ -61,25 +61,6 @@ static const struct reg_default wm8741_reg_defaults[] = {
	{ 32, 0x0002 },     /* R32 - ADDITONAL_CONTROL_1 */
};

static bool wm8741_readable(struct device *dev, unsigned int reg)
{
	switch (reg) {
	case WM8741_DACLLSB_ATTENUATION:
	case WM8741_DACLMSB_ATTENUATION:
	case WM8741_DACRLSB_ATTENUATION:
	case WM8741_DACRMSB_ATTENUATION:
	case WM8741_VOLUME_CONTROL:
	case WM8741_FORMAT_CONTROL:
	case WM8741_FILTER_CONTROL:
	case WM8741_MODE_CONTROL_1:
	case WM8741_MODE_CONTROL_2:
	case WM8741_ADDITIONAL_CONTROL_1:
		return true;
	default:
		return false;
	}
}

static int wm8741_reset(struct snd_soc_codec *codec)
{
	return snd_soc_write(codec, WM8741_RESET, 0);
@@ -278,52 +259,39 @@ static int wm8741_set_dai_sysclk(struct snd_soc_dai *codec_dai,
	switch (freq) {
	case 0:
		wm8741->sysclk_constraints = NULL;
		wm8741->sysclk = freq;
		return 0;

		break;
	case 11289600:
		wm8741->sysclk_constraints = &constraints_11289;
		wm8741->sysclk = freq;
		return 0;

		break;
	case 12288000:
		wm8741->sysclk_constraints = &constraints_12288;
		wm8741->sysclk = freq;
		return 0;

		break;
	case 16384000:
		wm8741->sysclk_constraints = &constraints_16384;
		wm8741->sysclk = freq;
		return 0;

		break;
	case 16934400:
		wm8741->sysclk_constraints = &constraints_16934;
		wm8741->sysclk = freq;
		return 0;

		break;
	case 18432000:
		wm8741->sysclk_constraints = &constraints_18432;
		wm8741->sysclk = freq;
		return 0;

		break;
	case 22579200:
	case 33868800:
		wm8741->sysclk_constraints = &constraints_22579;
		wm8741->sysclk = freq;
		return 0;

		break;
	case 24576000:
		wm8741->sysclk_constraints = &constraints_24576;
		wm8741->sysclk = freq;
		return 0;

		break;
	case 36864000:
		wm8741->sysclk_constraints = &constraints_36864;
		break;
	default:
		return -EINVAL;
	}

	wm8741->sysclk = freq;
	return 0;
}
	return -EINVAL;
}

static int wm8741_set_dai_fmt(struct snd_soc_dai *codec_dai,
		unsigned int fmt)
@@ -554,8 +522,6 @@ static const struct regmap_config wm8741_regmap = {
	.reg_defaults = wm8741_reg_defaults,
	.num_reg_defaults = ARRAY_SIZE(wm8741_reg_defaults),
	.cache_type = REGCACHE_RBTREE,

	.readable_reg = wm8741_readable,
};

static int wm8741_set_pdata(struct device *dev, struct wm8741_priv *wm8741)
+0 −6
Original line number Diff line number Diff line
@@ -138,11 +138,6 @@ static bool wm8753_volatile(struct device *dev, unsigned int reg)
	return reg == WM8753_RESET;
}

static bool wm8753_writeable(struct device *dev, unsigned int reg)
{
	return reg <= WM8753_ADCTL2;
}

/* codec private data */
struct wm8753_priv {
	struct regmap *regmap;
@@ -1509,7 +1504,6 @@ static const struct regmap_config wm8753_regmap = {
	.val_bits = 9,

	.max_register = WM8753_ADCTL2,
	.writeable_reg = wm8753_writeable,
	.volatile_reg = wm8753_volatile,

	.cache_type = REGCACHE_RBTREE,
+3 −1
Original line number Diff line number Diff line
@@ -1837,7 +1837,9 @@ static int wm8904_set_bias_level(struct snd_soc_codec *codec,

	switch (level) {
	case SND_SOC_BIAS_ON:
		clk_prepare_enable(wm8904->mclk);
		ret = clk_prepare_enable(wm8904->mclk);
		if (ret)
			return ret;
		break;

	case SND_SOC_BIAS_PREPARE:
+179 −41
Original line number Diff line number Diff line
@@ -48,6 +48,9 @@
#define WM8960_DISOP     0x40
#define WM8960_DRES_MASK 0x30

static bool is_pll_freq_available(unsigned int source, unsigned int target);
static int wm8960_set_pll(struct snd_soc_codec *codec,
		unsigned int freq_in, unsigned int freq_out);
/*
 * wm8960 register cache
 * We can't read the WM8960 register space when we are
@@ -126,9 +129,12 @@ struct wm8960_priv {
	struct snd_soc_dapm_widget *rout1;
	struct snd_soc_dapm_widget *out3;
	bool deemph;
	int playback_fs;
	int lrclk;
	int bclk;
	int sysclk;
	int clk_id;
	int freq_in;
	bool is_stream_in_use[2];
	struct wm8960_data pdata;
};

@@ -164,8 +170,8 @@ static int wm8960_set_deemph(struct snd_soc_codec *codec)
	if (wm8960->deemph) {
		best = 1;
		for (i = 2; i < ARRAY_SIZE(deemph_settings); i++) {
			if (abs(deemph_settings[i] - wm8960->playback_fs) <
			    abs(deemph_settings[best] - wm8960->playback_fs))
			if (abs(deemph_settings[i] - wm8960->lrclk) <
			    abs(deemph_settings[best] - wm8960->lrclk))
				best = i;
		}

@@ -565,6 +571,9 @@ static struct {
	{  8000, 5 },
};

/* -1 for reserved value */
static const int sysclk_divs[] = { 1, -1, 2, -1 };

/* Multiply 256 for internal 256 div */
static const int dac_divs[] = { 256, 384, 512, 768, 1024, 1408, 1536 };

@@ -574,61 +583,110 @@ static const int bclk_divs[] = {
	120, 160, 220, 240, 320, 320, 320
};

static void wm8960_configure_clocking(struct snd_soc_codec *codec,
		bool tx, int lrclk)
static int wm8960_configure_clocking(struct snd_soc_codec *codec)
{
	struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
	int sysclk, bclk, lrclk, freq_out, freq_in;
	u16 iface1 = snd_soc_read(codec, WM8960_IFACE1);
	u16 iface2 = snd_soc_read(codec, WM8960_IFACE2);
	u32 sysclk;
	int i, j;
	int i, j, k;

	if (!(iface1 & (1<<6))) {
		dev_dbg(codec->dev,
			"Codec is slave mode, no need to configure clock\n");
		return;
		return 0;
	}

	if (!wm8960->sysclk) {
		dev_dbg(codec->dev, "No SYSCLK configured\n");
		return;
	if (wm8960->clk_id != WM8960_SYSCLK_MCLK && !wm8960->freq_in) {
		dev_err(codec->dev, "No MCLK configured\n");
		return -EINVAL;
	}

	if (!wm8960->bclk || !lrclk) {
		dev_dbg(codec->dev, "No audio clocks configured\n");
		return;
	freq_in = wm8960->freq_in;
	bclk = wm8960->bclk;
	lrclk = wm8960->lrclk;
	/*
	 * If it's sysclk auto mode, check if the MCLK can provide sysclk or
	 * not. If MCLK can provide sysclk, using MCLK to provide sysclk
	 * directly. Otherwise, auto select a available pll out frequency
	 * and set PLL.
	 */
	if (wm8960->clk_id == WM8960_SYSCLK_AUTO) {
		/* disable the PLL and using MCLK to provide sysclk */
		wm8960_set_pll(codec, 0, 0);
		freq_out = freq_in;
	} else if (wm8960->sysclk) {
		freq_out = wm8960->sysclk;
	} else {
		dev_err(codec->dev, "No SYSCLK configured\n");
		return -EINVAL;
	}

	for (i = 0; i < ARRAY_SIZE(dac_divs); ++i) {
		if (wm8960->sysclk == lrclk * dac_divs[i]) {
			for (j = 0; j < ARRAY_SIZE(bclk_divs); ++j) {
				sysclk = wm8960->bclk * bclk_divs[j] / 10;
				if (wm8960->sysclk == sysclk)
	/* check if the sysclk frequency is available. */
	for (i = 0; i < ARRAY_SIZE(sysclk_divs); ++i) {
		if (sysclk_divs[i] == -1)
			continue;
		sysclk = freq_out / sysclk_divs[i];
		for (j = 0; j < ARRAY_SIZE(dac_divs); ++j) {
			if (sysclk == dac_divs[j] * lrclk) {
				for (k = 0; k < ARRAY_SIZE(bclk_divs); ++k)
					if (sysclk == bclk * bclk_divs[k] / 10)
						break;
				if (k != ARRAY_SIZE(bclk_divs))
					break;
			}
			if(j != ARRAY_SIZE(bclk_divs))
		}
		if (j != ARRAY_SIZE(dac_divs))
			break;
	}

	if (i != ARRAY_SIZE(sysclk_divs)) {
		goto configure_clock;
	} else if (wm8960->clk_id != WM8960_SYSCLK_AUTO) {
		dev_err(codec->dev, "failed to configure clock\n");
		return -EINVAL;
	}
	/* get a available pll out frequency and set pll */
	for (i = 0; i < ARRAY_SIZE(sysclk_divs); ++i) {
		if (sysclk_divs[i] == -1)
			continue;
		for (j = 0; j < ARRAY_SIZE(dac_divs); ++j) {
			sysclk = lrclk * dac_divs[j];
			freq_out = sysclk * sysclk_divs[i];

			for (k = 0; k < ARRAY_SIZE(bclk_divs); ++k) {
				if (sysclk == bclk * bclk_divs[k] / 10 &&
				    is_pll_freq_available(freq_in, freq_out)) {
					wm8960_set_pll(codec,
						       freq_in, freq_out);
					break;
				} else {
					continue;
				}
			}
			if (k != ARRAY_SIZE(bclk_divs))
				break;
		}
		if (j != ARRAY_SIZE(dac_divs))
			break;
	}

	if (i == ARRAY_SIZE(dac_divs)) {
		dev_err(codec->dev, "Unsupported sysclk %d\n", wm8960->sysclk);
		return;
	if (i == ARRAY_SIZE(sysclk_divs)) {
		dev_err(codec->dev, "failed to configure clock\n");
		return -EINVAL;
	}

	/*
	 * configure frame clock. If ADCLRC configure as GPIO pin, DACLRC
	 * pin is used as a frame clock for ADCs and DACs.
	 */
	if (iface2 & (1<<6))
		snd_soc_update_bits(codec, WM8960_CLOCK1, 0x7 << 3, i << 3);
	else if (tx)
		snd_soc_update_bits(codec, WM8960_CLOCK1, 0x7 << 3, i << 3);
	else if (!tx)
		snd_soc_update_bits(codec, WM8960_CLOCK1, 0x7 << 6, i << 6);
configure_clock:
	/* configure sysclk clock */
	snd_soc_update_bits(codec, WM8960_CLOCK1, 3 << 1, i << 1);

	/* configure frame clock */
	snd_soc_update_bits(codec, WM8960_CLOCK1, 0x7 << 3, j << 3);
	snd_soc_update_bits(codec, WM8960_CLOCK1, 0x7 << 6, j << 6);

	/* configure bit clock */
	snd_soc_update_bits(codec, WM8960_CLOCK2, 0xf, j);
	snd_soc_update_bits(codec, WM8960_CLOCK2, 0xf, k);

	return 0;
}

static int wm8960_hw_params(struct snd_pcm_substream *substream,
@@ -667,9 +725,9 @@ static int wm8960_hw_params(struct snd_pcm_substream *substream,
		return -EINVAL;
	}

	wm8960->lrclk = params_rate(params);
	/* Update filters for the new rate */
	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
		wm8960->playback_fs = params_rate(params);
	if (tx) {
		wm8960_set_deemph(codec);
	} else {
		for (i = 0; i < ARRAY_SIZE(alc_rates); i++)
@@ -682,7 +740,23 @@ static int wm8960_hw_params(struct snd_pcm_substream *substream,
	/* set iface */
	snd_soc_write(codec, WM8960_IFACE1, iface);

	wm8960_configure_clocking(codec, tx, params_rate(params));
	wm8960->is_stream_in_use[tx] = true;

	if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_ON &&
	    !wm8960->is_stream_in_use[!tx])
		return wm8960_configure_clocking(codec);

	return 0;
}

static int wm8960_hw_free(struct snd_pcm_substream *substream,
		struct snd_soc_dai *dai)
{
	struct snd_soc_codec *codec = dai->codec;
	struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
	bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;

	wm8960->is_stream_in_use[tx] = false;

	return 0;
}
@@ -702,6 +776,7 @@ static int wm8960_set_bias_level_out3(struct snd_soc_codec *codec,
				      enum snd_soc_bias_level level)
{
	struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
	u16 pm2 = snd_soc_read(codec, WM8960_POWER2);
	int ret;

	switch (level) {
@@ -721,11 +796,22 @@ static int wm8960_set_bias_level_out3(struct snd_soc_codec *codec,
				}
			}

			ret = wm8960_configure_clocking(codec);
			if (ret)
				return ret;

			/* Set VMID to 2x50k */
			snd_soc_update_bits(codec, WM8960_POWER1, 0x180, 0x80);
			break;

		case SND_SOC_BIAS_ON:
			/*
			 * If it's sysclk auto mode, and the pll is enabled,
			 * disable the pll
			 */
			if (wm8960->clk_id == WM8960_SYSCLK_AUTO && (pm2 & 0x1))
				wm8960_set_pll(codec, 0, 0);

			if (!IS_ERR(wm8960->mclk))
				clk_disable_unprepare(wm8960->mclk);
			break;
@@ -780,6 +866,7 @@ static int wm8960_set_bias_level_capless(struct snd_soc_codec *codec,
					 enum snd_soc_bias_level level)
{
	struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
	u16 pm2 = snd_soc_read(codec, WM8960_POWER2);
	int reg, ret;

	switch (level) {
@@ -831,9 +918,21 @@ static int wm8960_set_bias_level_capless(struct snd_soc_codec *codec,
					return ret;
				}
			}

			ret = wm8960_configure_clocking(codec);
			if (ret)
				return ret;

			break;

		case SND_SOC_BIAS_ON:
			/*
			 * If it's sysclk auto mode, and the pll is enabled,
			 * disable the pll
			 */
			if (wm8960->clk_id == WM8960_SYSCLK_AUTO && (pm2 & 0x1))
				wm8960_set_pll(codec, 0, 0);

			if (!IS_ERR(wm8960->mclk))
				clk_disable_unprepare(wm8960->mclk);

@@ -892,6 +991,28 @@ struct _pll_div {
	u32 k:24;
};

static bool is_pll_freq_available(unsigned int source, unsigned int target)
{
	unsigned int Ndiv;

	if (source == 0 || target == 0)
		return false;

	/* Scale up target to PLL operating frequency */
	target *= 4;
	Ndiv = target / source;

	if (Ndiv < 6) {
		source >>= 1;
		Ndiv = target / source;
	}

	if ((Ndiv < 6) || (Ndiv > 12))
		return false;

	return true;
}

/* The size in bits of the pll divide multiplied by 10
 * to allow rounding later */
#define FIXED_PLL_SIZE ((1 << 24) * 10)
@@ -943,10 +1064,9 @@ static int pll_factors(unsigned int source, unsigned int target,
	return 0;
}

static int wm8960_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
		int source, unsigned int freq_in, unsigned int freq_out)
static int wm8960_set_pll(struct snd_soc_codec *codec,
		unsigned int freq_in, unsigned int freq_out)
{
	struct snd_soc_codec *codec = codec_dai->codec;
	u16 reg;
	static struct _pll_div pll_div;
	int ret;
@@ -986,6 +1106,20 @@ static int wm8960_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
	return 0;
}

static int wm8960_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
		int source, unsigned int freq_in, unsigned int freq_out)
{
	struct snd_soc_codec *codec = codec_dai->codec;
	struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);

	wm8960->freq_in = freq_in;

	if (pll_id == WM8960_SYSCLK_AUTO)
		return 0;

	return wm8960_set_pll(codec, freq_in, freq_out);
}

static int wm8960_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
		int div_id, int div)
{
@@ -1043,11 +1177,14 @@ static int wm8960_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
		snd_soc_update_bits(codec, WM8960_CLOCK1,
					0x1, WM8960_SYSCLK_PLL);
		break;
	case WM8960_SYSCLK_AUTO:
		break;
	default:
		return -EINVAL;
	}

	wm8960->sysclk = freq;
	wm8960->clk_id = clk_id;

	return 0;
}
@@ -1060,6 +1197,7 @@ static int wm8960_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,

static const struct snd_soc_dai_ops wm8960_dai_ops = {
	.hw_params = wm8960_hw_params,
	.hw_free = wm8960_hw_free,
	.digital_mute = wm8960_mute,
	.set_fmt = wm8960_set_dai_fmt,
	.set_clkdiv = wm8960_set_dai_clkdiv,
+1 −0
Original line number Diff line number Diff line
@@ -82,6 +82,7 @@

#define WM8960_SYSCLK_MCLK		(0 << 0)
#define WM8960_SYSCLK_PLL		(1 << 0)
#define WM8960_SYSCLK_AUTO		(2 << 0)

#define WM8960_DAC_DIV_1		(0 << 3)
#define WM8960_DAC_DIV_1_5		(1 << 3)
Loading