Choose clock source automatically if not provided. This will be the case
with eg. audio-graph-card.

Signed-off-by: Michał Mirosław <mirq-li...@rere.qmqm.pl>
---
 sound/soc/codecs/wm8904.c | 42 +++++++++++++++++++++++++++++++++++++--
 1 file changed, 40 insertions(+), 2 deletions(-)

diff --git a/sound/soc/codecs/wm8904.c b/sound/soc/codecs/wm8904.c
index c9318fe34f91..946315d4cecf 100644
--- a/sound/soc/codecs/wm8904.c
+++ b/sound/soc/codecs/wm8904.c
@@ -367,15 +367,34 @@ static int wm8904_enable_sysclk(struct wm8904_priv *priv)
        return err;
 }
 
+static int wm8904_bump_fll_sysclk(unsigned int *rate);
+
 static int wm8904_set_sysclk(struct snd_soc_dai *dai, int clk_id,
                             unsigned int rate, int dir)
 {
        struct snd_soc_component *component = dai->component;
        struct wm8904_priv *wm8904 = snd_soc_component_get_drvdata(component);
        unsigned int clock0, clock2;
-       int err;
+       int err, do_div = false;
 
        switch (clk_id) {
+       case 0:
+               if (rate == clk_round_rate(wm8904->mclk, rate)) {
+                       clk_id = WM8904_CLK_MCLK;
+               } else if (rate * 2 == clk_round_rate(wm8904->mclk, rate * 2)) {
+                       rate *= 2;
+                       clk_id = WM8904_CLK_MCLK;
+                       do_div = true;
+               } else {
+                       clk_id = WM8904_CLK_FLL;
+                       err = wm8904_bump_fll_sysclk(&rate);
+                       if (err) {
+                               dev_dbg(component->dev, "Can't match %u over 
FLL 1406250 Hz minimum\n", rate);
+                               return err;
+                       }
+               }
+               break;
+
        case WM8904_CLK_MCLK:
        case WM8904_CLK_FLL:
                break;
@@ -421,7 +440,9 @@ static int wm8904_set_sysclk(struct snd_soc_dai *dai, int 
clk_id,
        }
 
        /* SYSCLK shouldn't be over 13.5MHz */
-       if (rate > 13500000) {
+       if (rate > 13500000)
+               do_div = true;
+       if (do_div) {
                clock0 = WM8904_MCLK_DIV;
                wm8904->sysclk_rate = rate / 2;
        } else {
@@ -1350,6 +1371,23 @@ static struct {
        { 480, 20 },
 };
 
+static int wm8904_bump_fll_sysclk(unsigned int *rate)
+{
+       int i;
+
+       /* bump SYSCLK rate if below minimal FLL output */
+
+       for (i = 0; i < ARRAY_SIZE(bclk_divs); i++) {
+               if (*rate * bclk_divs[i].div >= 1406250 * 10)
+                       break;
+       }
+
+       if (i == ARRAY_SIZE(bclk_divs))
+               return -ERANGE;
+
+       *rate = (*rate * bclk_divs[i].div) / 10;
+       return 0;
+}
 
 static int wm8904_hw_params(struct snd_pcm_substream *substream,
                            struct snd_pcm_hw_params *params,
-- 
2.20.1

Reply via email to