Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ASoC: Automatically calculate clock ratio for WM8580

Implement set_sysclk() and then rather than assuming 256fs use the
supplied value to calculate and configure the clock ratio for the
currently used sample rate. As a side effect we also end up
implementing clock selection for the ADC path.

In order to avoid confusion remove the existing set_clkdiv() based
configuration of the clock source for the DAC and update the SMDK64xx
driver (which is the only in-tree user of the CODEC).

Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Acked-by: Liam Girdwood <lrg@slimlogic.co.uk>

+90 -34
+79 -22
sound/soc/codecs/wm8580.c
··· 192 192 u16 reg_cache[WM8580_MAX_REGISTER + 1]; 193 193 struct pll_state a; 194 194 struct pll_state b; 195 + int sysclk[2]; 195 196 }; 196 197 197 198 static const DECLARE_TLV_DB_SCALE(dac_tlv, -12750, 50, 1); ··· 465 464 return 0; 466 465 } 467 466 467 + static const int wm8580_sysclk_ratios[] = { 468 + 128, 192, 256, 384, 512, 768, 1152, 469 + }; 470 + 468 471 /* 469 472 * Set PCM DAI bit size and sample rate. 470 473 */ ··· 478 473 { 479 474 struct snd_soc_pcm_runtime *rtd = substream->private_data; 480 475 struct snd_soc_codec *codec = rtd->codec; 476 + struct wm8580_priv *wm8580 = snd_soc_codec_get_drvdata(codec); 477 + u16 paifa = 0; 481 478 u16 paifb = 0; 479 + int i, ratio; 482 480 483 481 /* bit size */ 484 482 switch (params_format(params)) { ··· 500 492 return -EINVAL; 501 493 } 502 494 495 + /* Look up the SYSCLK ratio; accept only exact matches */ 496 + ratio = wm8580->sysclk[dai->id] / params_rate(params); 497 + for (i = 0; i < ARRAY_SIZE(wm8580_sysclk_ratios); i++) 498 + if (ratio == wm8580_sysclk_ratios[i]) 499 + break; 500 + if (i == ARRAY_SIZE(wm8580_sysclk_ratios)) { 501 + dev_err(codec->dev, "Invalid clock ratio %d/%d\n", 502 + wm8580->sysclk[dai->id], params_rate(params)); 503 + return -EINVAL; 504 + } 505 + paifa |= i; 506 + dev_dbg(codec->dev, "Running at %dfs with %dHz clock\n", 507 + wm8580_sysclk_ratios[i], wm8580->sysclk[dai->driver->id]); 508 + 509 + snd_soc_update_bits(codec, WM8580_PAIF1 + dai->driver->id, 510 + WM8580_AIF_RATE_MASK, paifa); 503 511 snd_soc_update_bits(codec, WM8580_PAIF3 + dai->driver->id, 504 512 WM8580_AIF_LENGTH_MASK, paifb); 505 513 return 0; ··· 525 501 unsigned int fmt) 526 502 { 527 503 struct snd_soc_codec *codec = codec_dai->codec; 504 + struct wm8580_priv *wm8580 = snd_soc_codec_get_drvdata(codec); 528 505 unsigned int aifa; 529 506 unsigned int aifb; 530 507 int can_invert_lrclk; 508 + int sysclk; 531 509 532 510 aifa = snd_soc_read(codec, WM8580_PAIF1 + codec_dai->driver->id); 533 511 aifb = snd_soc_read(codec, WM8580_PAIF3 + codec_dai->driver->id); ··· 598 572 return -EINVAL; 599 573 } 600 574 575 + sysclk = wm8580->sysclk[codec_dai->driver->id]; 576 + 601 577 snd_soc_write(codec, WM8580_PAIF1 + codec_dai->driver->id, aifa); 602 578 snd_soc_write(codec, WM8580_PAIF3 + codec_dai->driver->id, aifb); 603 579 ··· 639 611 snd_soc_write(codec, WM8580_PLLB4, reg); 640 612 break; 641 613 642 - case WM8580_DAC_CLKSEL: 643 - reg = snd_soc_read(codec, WM8580_CLKSEL); 644 - reg &= ~WM8580_CLKSEL_DAC_CLKSEL_MASK; 645 - 646 - switch (div) { 647 - case WM8580_CLKSRC_MCLK: 648 - break; 649 - 650 - case WM8580_CLKSRC_PLLA: 651 - reg |= WM8580_CLKSEL_DAC_CLKSEL_PLLA; 652 - break; 653 - 654 - case WM8580_CLKSRC_PLLB: 655 - reg |= WM8580_CLKSEL_DAC_CLKSEL_PLLB; 656 - break; 657 - 658 - default: 659 - return -EINVAL; 660 - } 661 - snd_soc_write(codec, WM8580_CLKSEL, reg); 662 - break; 663 - 664 614 case WM8580_CLKOUTSRC: 665 615 reg = snd_soc_read(codec, WM8580_PLLB4); 666 616 reg &= ~WM8580_PLLB4_CLKOUTSRC_MASK; ··· 670 664 } 671 665 672 666 return 0; 667 + } 668 + 669 + static int wm8580_set_sysclk(struct snd_soc_dai *dai, int clk_id, 670 + unsigned int freq, int dir) 671 + { 672 + struct snd_soc_codec *codec = dai->codec; 673 + struct wm8580_priv *wm8580 = snd_soc_codec_get_drvdata(codec); 674 + int sel, sel_mask, sel_shift; 675 + 676 + switch (dai->driver->id) { 677 + case WM8580_DAI_PAIFTX: 678 + sel_mask = 0x3; 679 + sel_shift = 0; 680 + break; 681 + 682 + case WM8580_DAI_PAIFRX: 683 + sel_mask = 0xc; 684 + sel_shift = 2; 685 + break; 686 + 687 + default: 688 + BUG_ON("Unknown DAI driver ID\n"); 689 + return -EINVAL; 690 + } 691 + 692 + switch (clk_id) { 693 + case WM8580_CLKSRC_ADCMCLK: 694 + if (dai->id != WM8580_DAI_PAIFTX) 695 + return -EINVAL; 696 + sel = 0 << sel_shift; 697 + break; 698 + case WM8580_CLKSRC_PLLA: 699 + sel = 1 << sel_shift; 700 + break; 701 + case WM8580_CLKSRC_PLLB: 702 + sel = 2 << sel_shift; 703 + break; 704 + case WM8580_CLKSRC_MCLK: 705 + sel = 3 << sel_shift; 706 + break; 707 + default: 708 + dev_err(codec->dev, "Unknown clock %d\n", clk_id); 709 + return -EINVAL; 710 + } 711 + 712 + /* We really should validate PLL settings but not yet */ 713 + wm8580->sysclk[dai->id] = freq; 714 + 715 + return snd_soc_update_bits(codec, WM8580_CLKSEL, sel, sel_mask); 673 716 } 674 717 675 718 static int wm8580_digital_mute(struct snd_soc_dai *codec_dai, int mute) ··· 774 719 SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) 775 720 776 721 static struct snd_soc_dai_ops wm8580_dai_ops_playback = { 722 + .set_sysclk = wm8580_set_sysclk, 777 723 .hw_params = wm8580_paif_hw_params, 778 724 .set_fmt = wm8580_set_paif_dai_fmt, 779 725 .set_clkdiv = wm8580_set_dai_clkdiv, ··· 783 727 }; 784 728 785 729 static struct snd_soc_dai_ops wm8580_dai_ops_capture = { 730 + .set_sysclk = wm8580_set_sysclk, 786 731 .hw_params = wm8580_paif_hw_params, 787 732 .set_fmt = wm8580_set_paif_dai_fmt, 788 733 .set_clkdiv = wm8580_set_dai_clkdiv,
+7 -7
sound/soc/codecs/wm8580.h
··· 19 19 #define WM8580_PLLB 2 20 20 21 21 #define WM8580_MCLK 1 22 - #define WM8580_DAC_CLKSEL 2 23 - #define WM8580_CLKOUTSRC 3 22 + #define WM8580_CLKOUTSRC 2 24 23 25 - #define WM8580_CLKSRC_MCLK 1 26 - #define WM8580_CLKSRC_PLLA 2 27 - #define WM8580_CLKSRC_PLLB 3 28 - #define WM8580_CLKSRC_OSC 4 29 - #define WM8580_CLKSRC_NONE 5 24 + #define WM8580_CLKSRC_MCLK 1 25 + #define WM8580_CLKSRC_PLLA 2 26 + #define WM8580_CLKSRC_PLLB 3 27 + #define WM8580_CLKSRC_OSC 4 28 + #define WM8580_CLKSRC_NONE 5 29 + #define WM8580_CLKSRC_ADCMCLK 6 30 30 31 31 #define WM8580_DAI_PAIFRX 0 32 32 #define WM8580_DAI_PAIFTX 1
+4 -5
sound/soc/s3c24xx/smdk64xx_wm8580.c
··· 113 113 if (ret < 0) 114 114 return ret; 115 115 116 - /* Explicitly set WM8580-DAC to source from MCLK */ 117 - ret = snd_soc_dai_set_clkdiv(codec_dai, WM8580_DAC_CLKSEL, 118 - WM8580_CLKSRC_MCLK); 116 + ret = snd_soc_dai_set_pll(codec_dai, WM8580_PLLA, 0, 117 + SMDK64XX_WM8580_FREQ, pll_out); 119 118 if (ret < 0) 120 119 return ret; 121 120 122 - ret = snd_soc_dai_set_pll(codec_dai, WM8580_PLLA, 0, 123 - SMDK64XX_WM8580_FREQ, pll_out); 121 + ret = snd_soc_dai_set_sysclk(codec_dai, WM8580_CLKSRC_PLLA, 122 + pll_out, SND_SOC_CLOCK_IN); 124 123 if (ret < 0) 125 124 return ret; 126 125