]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
ASoC: rsnd: adjust convert rate limitation
authorKuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Wed, 5 Feb 2025 00:20:48 +0000 (00:20 +0000)
committerMark Brown <broonie@kernel.org>
Wed, 5 Feb 2025 12:28:14 +0000 (12:28 +0000)
Current rsnd driver supports Synchronous SRC Mode, but HW allow to update
rate only within 1% from current rate. Adjust to it.

Becially, this feature is used to fine-tune subtle difference that occur
during sampling rate conversion in SRC. So, it should be called within 1%
margin of rate difference.

If there was difference over 1%, it will apply with 1% increments by using
loop without indicating error message.

Cc: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>
Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Reviewed-by: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>
Tested-by: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>
Link: https://patch.msgid.link/871pwd2qe8.wl-kuninori.morimoto.gx@renesas.com
Signed-off-by: Mark Brown <broonie@kernel.org>
sound/soc/renesas/rcar/src.c

index 30991802977224d192285097cd21f0a121f4c13b..7d73b183bda685a11136fd014ea8b61e61bb5ef8 100644 (file)
@@ -35,6 +35,7 @@ struct rsnd_src {
        struct rsnd_mod *dma;
        struct rsnd_kctrl_cfg_s sen;  /* sync convert enable */
        struct rsnd_kctrl_cfg_s sync; /* sync convert */
+       u32 current_sync_rate;
        int irq;
 };
 
@@ -100,7 +101,7 @@ static u32 rsnd_src_convert_rate(struct rsnd_dai_stream *io,
        if (!rsnd_src_sync_is_enabled(mod))
                return rsnd_io_converted_rate(io);
 
-       convert_rate = src->sync.val;
+       convert_rate = src->current_sync_rate;
 
        if (!convert_rate)
                convert_rate = rsnd_io_converted_rate(io);
@@ -201,13 +202,73 @@ static const u32 chan222222[] = {
 static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io,
                                      struct rsnd_mod *mod)
 {
+       struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
        struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
-       struct device *dev = rsnd_priv_to_dev(priv);
+       struct rsnd_src *src = rsnd_mod_to_src(mod);
+       u32 fin, fout, new_rate;
+       int inc, cnt, rate;
+       u64 base, val;
+
+       if (!runtime)
+               return;
+
+       if (!rsnd_src_sync_is_enabled(mod))
+               return;
+
+       fin     = rsnd_src_get_in_rate(priv, io);
+       fout    = rsnd_src_get_out_rate(priv, io);
+
+       new_rate = src->sync.val;
+
+       if (!new_rate)
+               new_rate = fout;
+
+       /* Do nothing if no diff */
+       if (new_rate == src->current_sync_rate)
+               return;
+
+       /*
+        * SRCm_IFSVR::INTIFS can change within 1%
+        * see
+        *      SRCm_IFSVR::INTIFS Note
+        */
+       inc = fout / 100;
+       cnt = abs(new_rate - fout) / inc;
+       if (fout > new_rate)
+               inc *= -1;
+
+       /*
+        * After start running SRC, we can update only SRC_IFSVR
+        * for Synchronous Mode
+        */
+       base = (u64)0x0400000 * fin;
+       rate  = fout;
+       for (int i = 0; i < cnt; i++) {
+               val   = base;
+               rate += inc;
+               do_div(val, rate);
+
+               rsnd_mod_write(mod, SRC_IFSVR, val);
+       }
+       val   = base;
+       do_div(val, new_rate);
+
+       rsnd_mod_write(mod, SRC_IFSVR, val);
+
+       /* update current_sync_rate */
+       src->current_sync_rate = new_rate;
+}
+
+static void rsnd_src_init_convert_rate(struct rsnd_dai_stream *io,
+                                      struct rsnd_mod *mod)
+{
        struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
+       struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+       struct device *dev = rsnd_priv_to_dev(priv);
        int is_play = rsnd_io_is_play(io);
        int use_src = 0;
        u32 fin, fout;
-       u32 ifscr, fsrate, adinr;
+       u32 ifscr, adinr;
        u32 cr, route;
        u32 i_busif, o_busif, tmp;
        const u32 *bsdsr_table;
@@ -245,26 +306,15 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io,
        adinr = rsnd_get_adinr_bit(mod, io) | chan;
 
        /*
-        * SRC_IFSCR / SRC_IFSVR
-        */
-       ifscr = 0;
-       fsrate = 0;
-       if (use_src) {
-               u64 n;
-
-               ifscr = 1;
-               n = (u64)0x0400000 * fin;
-               do_div(n, fout);
-               fsrate = n;
-       }
-
-       /*
+        * SRC_IFSCR
         * SRC_SRCCR / SRC_ROUTE_MODE0
         */
+       ifscr   = 0;
        cr      = 0x00011110;
        route   = 0x0;
        if (use_src) {
                route   = 0x1;
+               ifscr   = 0x1;
 
                if (rsnd_src_sync_is_enabled(mod)) {
                        cr |= 0x1;
@@ -335,7 +385,6 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io,
        rsnd_mod_write(mod, SRC_SRCIR, 1);      /* initialize */
        rsnd_mod_write(mod, SRC_ADINR, adinr);
        rsnd_mod_write(mod, SRC_IFSCR, ifscr);
-       rsnd_mod_write(mod, SRC_IFSVR, fsrate);
        rsnd_mod_write(mod, SRC_SRCCR, cr);
        rsnd_mod_write(mod, SRC_BSDSR, bsdsr_table[idx]);
        rsnd_mod_write(mod, SRC_BSISR, bsisr_table[idx]);
@@ -348,6 +397,9 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io,
 
        rsnd_adg_set_src_timesel_gen2(mod, io, fin, fout);
 
+       /* update SRC_IFSVR */
+       rsnd_src_set_convert_rate(io, mod);
+
        return;
 
 convert_rate_err:
@@ -467,7 +519,8 @@ static int rsnd_src_init(struct rsnd_mod *mod,
        int ret;
 
        /* reset sync convert_rate */
-       src->sync.val = 0;
+       src->sync.val           =
+       src->current_sync_rate  = 0;
 
        ret = rsnd_mod_power_on(mod);
        if (ret < 0)
@@ -475,7 +528,7 @@ static int rsnd_src_init(struct rsnd_mod *mod,
 
        rsnd_src_activation(mod);
 
-       rsnd_src_set_convert_rate(io, mod);
+       rsnd_src_init_convert_rate(io, mod);
 
        rsnd_src_status_clear(mod);
 
@@ -493,7 +546,8 @@ static int rsnd_src_quit(struct rsnd_mod *mod,
        rsnd_mod_power_off(mod);
 
        /* reset sync convert_rate */
-       src->sync.val = 0;
+       src->sync.val           =
+       src->current_sync_rate  = 0;
 
        return 0;
 }
@@ -601,7 +655,7 @@ static int rsnd_src_pcm_new(struct rsnd_mod *mod,
                               "SRC Out Rate Switch" :
                               "SRC In Rate Switch",
                               rsnd_kctrl_accept_anytime,
-                              rsnd_src_set_convert_rate,
+                              rsnd_src_init_convert_rate,
                               &src->sen, 1);
        if (ret < 0)
                return ret;