Merge tag 'v3.14.25' into backport/v3.14.24-ltsi-rc1+v3.14.25/snapshot-merge.wip
[platform/adaptation/renesas_rcar/renesas_kernel.git] / sound / soc / sh / rcar / adg.c
index a53235c..fc41a0e 100644 (file)
@@ -25,15 +25,183 @@ struct rsnd_adg {
 };
 
 #define for_each_rsnd_clk(pos, adg, i)         \
-       for (i = 0, (pos) = adg->clk[i];        \
-            i < CLKMAX;                        \
-            i++, (pos) = adg->clk[i])
+       for (i = 0;                             \
+            (i < CLKMAX) &&                    \
+            ((pos) = adg->clk[i]);             \
+            i++)
 #define rsnd_priv_to_adg(priv) ((struct rsnd_adg *)(priv)->adg)
 
-static int rsnd_adg_set_convert_clk_gen1(struct rsnd_priv *priv,
-                                        struct rsnd_mod *mod,
-                                        unsigned int src_rate,
-                                        unsigned int dst_rate)
+
+static u32 rsnd_adg_ssi_ws_timing_gen2(struct rsnd_dai_stream *io)
+{
+       struct rsnd_mod *mod = rsnd_io_to_mod_ssi(io);
+       struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+       int id = rsnd_mod_id(mod);
+       int ws = id;
+
+       if (rsnd_ssi_is_pin_sharing(rsnd_ssi_mod_get(priv, id))) {
+               switch (id) {
+               case 1:
+               case 2:
+                       ws = 0;
+                       break;
+               case 4:
+                       ws = 3;
+                       break;
+               case 8:
+                       ws = 7;
+                       break;
+               }
+       }
+
+       return (0x6 + ws) << 8;
+}
+
+int rsnd_adg_set_cmd_timsel_gen2(struct rsnd_dai *rdai,
+                                struct rsnd_mod *mod,
+                                struct rsnd_dai_stream *io)
+{
+       int id = rsnd_mod_id(mod);
+       int shift = (id % 2) ? 16 : 0;
+       u32 mask, val;
+
+       val = rsnd_adg_ssi_ws_timing_gen2(io);
+
+       val  = val      << shift;
+       mask = 0xffff   << shift;
+
+       rsnd_mod_bset(mod, CMDOUT_TIMSEL, mask, val);
+
+       return 0;
+}
+
+static int rsnd_adg_set_src_timsel_gen2(struct rsnd_dai *rdai,
+                                       struct rsnd_mod *mod,
+                                       struct rsnd_dai_stream *io,
+                                       u32 timsel)
+{
+       int is_play = rsnd_dai_is_play(rdai, io);
+       int id = rsnd_mod_id(mod);
+       int shift = (id % 2) ? 16 : 0;
+       u32 mask, ws;
+       u32 in, out;
+
+       ws = rsnd_adg_ssi_ws_timing_gen2(io);
+
+       in  = (is_play) ? timsel : ws;
+       out = (is_play) ? ws     : timsel;
+
+       in   = in       << shift;
+       out  = out      << shift;
+       mask = 0xffff   << shift;
+
+       switch (id / 2) {
+       case 0:
+               rsnd_mod_bset(mod, SRCIN_TIMSEL0,  mask, in);
+               rsnd_mod_bset(mod, SRCOUT_TIMSEL0, mask, out);
+               break;
+       case 1:
+               rsnd_mod_bset(mod, SRCIN_TIMSEL1,  mask, in);
+               rsnd_mod_bset(mod, SRCOUT_TIMSEL1, mask, out);
+               break;
+       case 2:
+               rsnd_mod_bset(mod, SRCIN_TIMSEL2,  mask, in);
+               rsnd_mod_bset(mod, SRCOUT_TIMSEL2, mask, out);
+               break;
+       case 3:
+               rsnd_mod_bset(mod, SRCIN_TIMSEL3,  mask, in);
+               rsnd_mod_bset(mod, SRCOUT_TIMSEL3, mask, out);
+               break;
+       case 4:
+               rsnd_mod_bset(mod, SRCIN_TIMSEL4,  mask, in);
+               rsnd_mod_bset(mod, SRCOUT_TIMSEL4, mask, out);
+               break;
+       }
+
+       return 0;
+}
+
+int rsnd_adg_set_convert_clk_gen2(struct rsnd_mod *mod,
+                                 struct rsnd_dai *rdai,
+                                 struct rsnd_dai_stream *io,
+                                 unsigned int src_rate,
+                                 unsigned int dst_rate)
+{
+       struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+       struct rsnd_adg *adg = rsnd_priv_to_adg(priv);
+       struct device *dev = rsnd_priv_to_dev(priv);
+       int idx, sel, div, step, ret;
+       u32 val, en;
+       unsigned int min, diff;
+       unsigned int sel_rate [] = {
+               clk_get_rate(adg->clk[CLKA]),   /* 0000: CLKA */
+               clk_get_rate(adg->clk[CLKB]),   /* 0001: CLKB */
+               clk_get_rate(adg->clk[CLKC]),   /* 0010: CLKC */
+               adg->rbga_rate_for_441khz_div_6,/* 0011: RBGA */
+               adg->rbgb_rate_for_48khz_div_6, /* 0100: RBGB */
+       };
+
+       min = ~0;
+       val = 0;
+       en = 0;
+       for (sel = 0; sel < ARRAY_SIZE(sel_rate); sel++) {
+               idx = 0;
+               step = 2;
+
+               if (!sel_rate[sel])
+                       continue;
+
+               for (div = 2; div <= 98304; div += step) {
+                       diff = abs(src_rate - sel_rate[sel] / div);
+                       if (min > diff) {
+                               val = (sel << 8) | idx;
+                               min = diff;
+                               en = 1 << (sel + 1); /* fixme */
+                       }
+
+                       /*
+                        * step of 0_0000 / 0_0001 / 0_1101
+                        * are out of order
+                        */
+                       if ((idx > 2) && (idx % 2))
+                               step *= 2;
+                       if (idx == 0x1c) {
+                               div += step;
+                               step *= 2;
+                       }
+                       idx++;
+               }
+       }
+
+       if (min == ~0) {
+               dev_err(dev, "no Input clock\n");
+               return -EIO;
+       }
+
+       ret = rsnd_adg_set_src_timsel_gen2(rdai, mod, io, val);
+       if (ret < 0) {
+               dev_err(dev, "timsel error\n");
+               return ret;
+       }
+
+       rsnd_mod_bset(mod, DIV_EN, en, en);
+
+       return 0;
+}
+
+int rsnd_adg_set_convert_timing_gen2(struct rsnd_mod *mod,
+                                    struct rsnd_dai *rdai,
+                                    struct rsnd_dai_stream *io)
+{
+       u32 val = rsnd_adg_ssi_ws_timing_gen2(io);
+
+       return rsnd_adg_set_src_timsel_gen2(rdai, mod, io, val);
+}
+
+int rsnd_adg_set_convert_clk_gen1(struct rsnd_priv *priv,
+                                 struct rsnd_mod *mod,
+                                 unsigned int src_rate,
+                                 unsigned int dst_rate)
 {
        struct rsnd_adg *adg = rsnd_priv_to_adg(priv);
        struct device *dev = rsnd_priv_to_dev(priv);
@@ -91,18 +259,6 @@ find_rate:
        return 0;
 }
 
-int rsnd_adg_set_convert_clk(struct rsnd_priv *priv,
-                            struct rsnd_mod *mod,
-                            unsigned int src_rate,
-                            unsigned int dst_rate)
-{
-       if (rsnd_is_gen1(priv))
-               return rsnd_adg_set_convert_clk_gen1(priv, mod,
-                                                    src_rate, dst_rate);
-
-       return -EINVAL;
-}
-
 static void rsnd_adg_set_ssi_clk(struct rsnd_mod *mod, u32 val)
 {
        int id = rsnd_mod_id(mod);
@@ -254,7 +410,7 @@ static void rsnd_adg_ssi_clk_init(struct rsnd_priv *priv, struct rsnd_adg *adg)
 }
 
 int rsnd_adg_probe(struct platform_device *pdev,
-                  struct rcar_snd_info *info,
+                  const struct rsnd_of_data *of_data,
                   struct rsnd_priv *priv)
 {
        struct rsnd_adg *adg;
@@ -268,16 +424,13 @@ int rsnd_adg_probe(struct platform_device *pdev,
                return -ENOMEM;
        }
 
-       adg->clk[CLKA] = clk_get(NULL, "audio_clk_a");
-       adg->clk[CLKB] = clk_get(NULL, "audio_clk_b");
-       adg->clk[CLKC] = clk_get(NULL, "audio_clk_c");
-       adg->clk[CLKI] = clk_get(NULL, "audio_clk_internal");
-       for_each_rsnd_clk(clk, adg, i) {
-               if (IS_ERR(clk)) {
-                       dev_err(dev, "Audio clock failed\n");
-                       return -EIO;
-               }
-       }
+       adg->clk[CLKA]  = devm_clk_get(dev, "clk_a");
+       adg->clk[CLKB]  = devm_clk_get(dev, "clk_b");
+       adg->clk[CLKC]  = devm_clk_get(dev, "clk_c");
+       adg->clk[CLKI]  = devm_clk_get(dev, "clk_i");
+
+       for_each_rsnd_clk(clk, adg, i)
+               dev_dbg(dev, "clk %d : %p\n", i, clk);
 
        rsnd_adg_ssi_clk_init(priv, adg);
 
@@ -287,14 +440,3 @@ int rsnd_adg_probe(struct platform_device *pdev,
 
        return 0;
 }
-
-void rsnd_adg_remove(struct platform_device *pdev,
-                    struct rsnd_priv *priv)
-{
-       struct rsnd_adg *adg = priv->adg;
-       struct clk *clk;
-       int i;
-
-       for_each_rsnd_clk(clk, adg, i)
-               clk_put(clk);
-}