#define GENERATED_SOURCE_MAX   6
 #define GENERATED_MAX_DIV      255
 
+#define GCK_ID_SSC0            43
+#define GCK_ID_SSC1            44
+#define GCK_ID_I2S0            54
+#define GCK_ID_I2S1            55
+#define GCK_ID_CLASSD          59
+#define GCK_INDEX_DT_AUDIO_PLL 5
+
 struct clk_generated {
        struct clk_hw hw;
        struct regmap *regmap;
        u32 id;
        u32 gckdiv;
        u8 parent_id;
+       bool audio_pll_allowed;
 };
 
 #define to_clk_generated(hw) \
 {
        struct clk_generated *gck = to_clk_generated(hw);
        struct clk_hw *parent = NULL;
+       struct clk_rate_request req_parent = *req;
        long best_rate = -EINVAL;
-       unsigned long min_rate;
+       unsigned long min_rate, parent_rate;
        int best_diff = -1;
        int i;
+       u32 div;
 
-       for (i = 0; i < clk_hw_get_num_parents(hw); i++) {
-               u32 div;
-               unsigned long parent_rate;
-
+       for (i = 0; i < clk_hw_get_num_parents(hw) - 1; i++) {
                parent = clk_hw_get_parent_by_index(hw, i);
                if (!parent)
                        continue;
                clk_generated_best_diff(req, parent, parent_rate, div,
                                        &best_diff, &best_rate);
 
+               if (!best_diff)
+                       break;
+       }
+
+       /*
+        * The audio_pll rate can be modified, unlike the five others clocks
+        * that should never be altered.
+        * The audio_pll can technically be used by multiple consumers. However,
+        * with the rate locking, the first consumer to enable to clock will be
+        * the one definitely setting the rate of the clock.
+        * Since audio IPs are most likely to request the same rate, we enforce
+        * that the only clks able to modify gck rate are those of audio IPs.
+        */
+
+       if (!gck->audio_pll_allowed)
+               goto end;
+
+       parent = clk_hw_get_parent_by_index(hw, GCK_INDEX_DT_AUDIO_PLL);
+       if (!parent)
+               goto end;
+
+       for (div = 1; div < GENERATED_MAX_DIV + 2; div++) {
+               req_parent.rate = req->rate * div;
+               __clk_determine_rate(parent, &req_parent);
+               clk_generated_best_diff(req, parent, req_parent.rate, div,
+                                       &best_diff, &best_rate);
 
                if (!best_diff)
                        break;
        }
 
+end:
        pr_debug("GCLK: %s, best_rate = %ld, parent clk: %s @ %ld\n",
                 __func__, best_rate,
                 __clk_get_name((req->best_parent_hw)->clk),
        init.ops = &generated_ops;
        init.parent_names = parent_names;
        init.num_parents = num_parents;
-       init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE;
+       init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE |
+               CLK_SET_RATE_PARENT;
 
        gck->id = id;
        gck->hw.init = &init;
        struct device_node *gcknp;
        struct clk_range range = CLK_RANGE(0, 0);
        struct regmap *regmap;
+       struct clk_generated *gck;
 
        num_parents = of_clk_get_parent_count(np);
        if (num_parents == 0 || num_parents > GENERATED_SOURCE_MAX)
                hw = at91_clk_register_generated(regmap, &pmc_pcr_lock, name,
                                                  parent_names, num_parents,
                                                  id, &range);
+
+               gck = to_clk_generated(hw);
+
+               if (of_device_is_compatible(np,
+                                           "atmel,sama5d2-clk-generated")) {
+                       if (gck->id == GCK_ID_SSC0 || gck->id == GCK_ID_SSC1 ||
+                           gck->id == GCK_ID_I2S0 || gck->id == GCK_ID_I2S1 ||
+                           gck->id == GCK_ID_CLASSD)
+                               gck->audio_pll_allowed = true;
+                       else
+                               gck->audio_pll_allowed = false;
+               } else {
+                       gck->audio_pll_allowed = false;
+               }
+
                if (IS_ERR(hw))
                        continue;