aboutsummaryrefslogtreecommitdiffstats
path: root/sound
diff options
context:
space:
mode:
authorMans Rullgard <mans@mansr.com>2016-01-25 07:36:43 -0500
committerMark Brown <broonie@kernel.org>2016-01-25 10:50:05 -0500
commit51b2bb3f2568e6d9d81a001d38b8d70c2ba4af99 (patch)
tree109b339a097cc34446f01b63119fbce9c5906450 /sound
parent92e963f50fc74041b5e9e744c330dca48e04f08d (diff)
ASoC: wm8974: configure pll and mclk divider automatically
This adds a set_sysclk() DAI op so the card driver can set the input clock frequency. If this is done, the pll and mclk divider are configured to produce the required 256x fs clock when the sample rate is set by hw_params(). These additions make the codec work with the simple-card driver. Card drivers calling set_pll() and set_clkdiv() directly are unaffected. Signed-off-by: Mans Rullgard <mans@mansr.com> Acked-by: Charles Keepax <ckeepax@opensource.wolfsonmicro.com> Signed-off-by: Mark Brown <broonie@kernel.org>
Diffstat (limited to 'sound')
-rw-r--r--sound/soc/codecs/wm8974.c93
1 files changed, 93 insertions, 0 deletions
diff --git a/sound/soc/codecs/wm8974.c b/sound/soc/codecs/wm8974.c
index c284c7b6db8b..dc8c3b1ebb6f 100644
--- a/sound/soc/codecs/wm8974.c
+++ b/sound/soc/codecs/wm8974.c
@@ -28,6 +28,11 @@
28 28
29#include "wm8974.h" 29#include "wm8974.h"
30 30
31struct wm8974_priv {
32 unsigned int mclk;
33 unsigned int fs;
34};
35
31static const struct reg_default wm8974_reg_defaults[] = { 36static const struct reg_default wm8974_reg_defaults[] = {
32 { 0, 0x0000 }, { 1, 0x0000 }, { 2, 0x0000 }, { 3, 0x0000 }, 37 { 0, 0x0000 }, { 1, 0x0000 }, { 2, 0x0000 }, { 3, 0x0000 },
33 { 4, 0x0050 }, { 5, 0x0000 }, { 6, 0x0140 }, { 7, 0x0000 }, 38 { 4, 0x0050 }, { 5, 0x0000 }, { 6, 0x0140 }, { 7, 0x0000 },
@@ -379,6 +384,79 @@ static int wm8974_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
379 return 0; 384 return 0;
380} 385}
381 386
387static unsigned int wm8974_get_mclkdiv(unsigned int f_in, unsigned int f_out,
388 int *mclkdiv)
389{
390 unsigned int ratio = 2 * f_in / f_out;
391
392 if (ratio <= 2) {
393 *mclkdiv = WM8974_MCLKDIV_1;
394 ratio = 2;
395 } else if (ratio == 3) {
396 *mclkdiv = WM8974_MCLKDIV_1_5;
397 } else if (ratio == 4) {
398 *mclkdiv = WM8974_MCLKDIV_2;
399 } else if (ratio <= 6) {
400 *mclkdiv = WM8974_MCLKDIV_3;
401 ratio = 6;
402 } else if (ratio <= 8) {
403 *mclkdiv = WM8974_MCLKDIV_4;
404 ratio = 8;
405 } else if (ratio <= 12) {
406 *mclkdiv = WM8974_MCLKDIV_6;
407 ratio = 12;
408 } else if (ratio <= 16) {
409 *mclkdiv = WM8974_MCLKDIV_8;
410 ratio = 16;
411 } else {
412 *mclkdiv = WM8974_MCLKDIV_12;
413 ratio = 24;
414 }
415
416 return f_out * ratio / 2;
417}
418
419static int wm8974_update_clocks(struct snd_soc_dai *dai)
420{
421 struct snd_soc_codec *codec = dai->codec;
422 struct wm8974_priv *priv = snd_soc_codec_get_drvdata(codec);
423 unsigned int fs256;
424 unsigned int fpll = 0;
425 unsigned int f;
426 int mclkdiv;
427
428 if (!priv->mclk || !priv->fs)
429 return 0;
430
431 fs256 = 256 * priv->fs;
432
433 f = wm8974_get_mclkdiv(priv->mclk, fs256, &mclkdiv);
434
435 if (f != priv->mclk) {
436 /* The PLL performs best around 90MHz */
437 fpll = wm8974_get_mclkdiv(22500000, fs256, &mclkdiv);
438 }
439
440 wm8974_set_dai_pll(dai, 0, 0, priv->mclk, fpll);
441 wm8974_set_dai_clkdiv(dai, WM8974_MCLKDIV, mclkdiv);
442
443 return 0;
444}
445
446static int wm8974_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
447 unsigned int freq, int dir)
448{
449 struct snd_soc_codec *codec = dai->codec;
450 struct wm8974_priv *priv = snd_soc_codec_get_drvdata(codec);
451
452 if (dir != SND_SOC_CLOCK_IN)
453 return -EINVAL;
454
455 priv->mclk = freq;
456
457 return wm8974_update_clocks(dai);
458}
459
382static int wm8974_set_dai_fmt(struct snd_soc_dai *codec_dai, 460static int wm8974_set_dai_fmt(struct snd_soc_dai *codec_dai,
383 unsigned int fmt) 461 unsigned int fmt)
384{ 462{
@@ -441,8 +519,15 @@ static int wm8974_pcm_hw_params(struct snd_pcm_substream *substream,
441 struct snd_soc_dai *dai) 519 struct snd_soc_dai *dai)
442{ 520{
443 struct snd_soc_codec *codec = dai->codec; 521 struct snd_soc_codec *codec = dai->codec;
522 struct wm8974_priv *priv = snd_soc_codec_get_drvdata(codec);
444 u16 iface = snd_soc_read(codec, WM8974_IFACE) & 0x19f; 523 u16 iface = snd_soc_read(codec, WM8974_IFACE) & 0x19f;
445 u16 adn = snd_soc_read(codec, WM8974_ADD) & 0x1f1; 524 u16 adn = snd_soc_read(codec, WM8974_ADD) & 0x1f1;
525 int err;
526
527 priv->fs = params_rate(params);
528 err = wm8974_update_clocks(dai);
529 if (err)
530 return err;
446 531
447 /* bit size */ 532 /* bit size */
448 switch (params_width(params)) { 533 switch (params_width(params)) {
@@ -547,6 +632,7 @@ static const struct snd_soc_dai_ops wm8974_ops = {
547 .set_fmt = wm8974_set_dai_fmt, 632 .set_fmt = wm8974_set_dai_fmt,
548 .set_clkdiv = wm8974_set_dai_clkdiv, 633 .set_clkdiv = wm8974_set_dai_clkdiv,
549 .set_pll = wm8974_set_dai_pll, 634 .set_pll = wm8974_set_dai_pll,
635 .set_sysclk = wm8974_set_dai_sysclk,
550}; 636};
551 637
552static struct snd_soc_dai_driver wm8974_dai = { 638static struct snd_soc_dai_driver wm8974_dai = {
@@ -606,9 +692,16 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8974 = {
606static int wm8974_i2c_probe(struct i2c_client *i2c, 692static int wm8974_i2c_probe(struct i2c_client *i2c,
607 const struct i2c_device_id *id) 693 const struct i2c_device_id *id)
608{ 694{
695 struct wm8974_priv *priv;
609 struct regmap *regmap; 696 struct regmap *regmap;
610 int ret; 697 int ret;
611 698
699 priv = devm_kzalloc(&i2c->dev, sizeof(*priv), GFP_KERNEL);
700 if (!priv)
701 return -ENOMEM;
702
703 i2c_set_clientdata(i2c, priv);
704
612 regmap = devm_regmap_init_i2c(i2c, &wm8974_regmap); 705 regmap = devm_regmap_init_i2c(i2c, &wm8974_regmap);
613 if (IS_ERR(regmap)) 706 if (IS_ERR(regmap))
614 return PTR_ERR(regmap); 707 return PTR_ERR(regmap);