Skip to content

Commit 0a3245f

Browse files
pelwellpopcornmix
authored andcommitted
clk: rp1: Allow clk_i2s to change the audio PLLs
Add dedicated code allowing the audio PLLs to be changed, enabling perfect I2S clock generation. The slowest legal pll_audio_core and pll_audio will be selected that leads to the required clk_i2s rate. Signed-off-by: Phil Elwell <[email protected]>
1 parent f3dc78e commit 0a3245f

File tree

1 file changed

+114
-1
lines changed

1 file changed

+114
-1
lines changed

drivers/clk/clk-rp1.c

Lines changed: 114 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,7 @@
254254
const char * const fc0_ref_clk_name = "clk_slow_sys";
255255

256256
#define ABS_DIFF(a, b) ((a) > (b) ? (a) - (b) : (b) - (a))
257+
#define DIV_NEAREST(a, b) (((a) + ((b) >> 1)) / (b))
257258
#define DIV_U64_NEAREST(a, b) div_u64(((a) + ((b) >> 1)), (b))
258259

259260
/*
@@ -393,6 +394,18 @@ struct rp1_clock {
393394
unsigned long cached_rate;
394395
};
395396

397+
398+
struct rp1_clk_change {
399+
struct clk_hw *hw;
400+
unsigned long new_rate;
401+
};
402+
403+
struct rp1_clk_change rp1_clk_chg_tree[3];
404+
405+
static struct clk_hw *clk_xosc;
406+
static struct clk_hw *clk_audio;
407+
static struct clk_hw *clk_i2s;
408+
396409
static void rp1_debugfs_regset(struct rp1_clockman *clockman, u32 base,
397410
const struct debugfs_reg32 *regs,
398411
size_t nregs, struct dentry *dentry)
@@ -749,8 +762,12 @@ static unsigned long rp1_pll_recalc_rate(struct clk_hw *hw,
749762
static long rp1_pll_round_rate(struct clk_hw *hw, unsigned long rate,
750763
unsigned long *parent_rate)
751764
{
765+
const struct rp1_clk_change *chg = &rp1_clk_chg_tree[1];
752766
u32 div1, div2;
753767

768+
if (chg->hw == hw && chg->new_rate == rate)
769+
*parent_rate = chg[1].new_rate;
770+
754771
get_pll_prim_dividers(rate, *parent_rate, &div1, &div2);
755772

756773
return DIV_ROUND_CLOSEST(*parent_rate, div1 * div2);
@@ -1188,6 +1205,59 @@ static int rp1_clock_set_rate(struct clk_hw *hw, unsigned long rate,
11881205
return rp1_clock_set_rate_and_parent(hw, rate, parent_rate, 0xff);
11891206
}
11901207

1208+
static unsigned long calc_core_pll_rate(struct clk_hw *pll_hw,
1209+
unsigned long target_rate,
1210+
int *pdiv_prim, int *pdiv_clk)
1211+
{
1212+
static const int prim_divs[] = {
1213+
2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 14, 15, 16,
1214+
18, 20, 21, 24, 25, 28, 30, 35, 36, 42, 49,
1215+
};
1216+
const unsigned long xosc_rate = clk_hw_get_rate(clk_xosc);
1217+
const unsigned long core_max = 2400000000;
1218+
const unsigned long core_min = xosc_rate * 16;
1219+
unsigned long best_rate = core_max + 1;
1220+
int best_div_prim = 1, best_div_clk = 1;
1221+
unsigned long core_rate = 0;
1222+
int div_int, div_frac;
1223+
u64 div;
1224+
int i;
1225+
1226+
/* Given the target rate, choose a set of divisors/multipliers */
1227+
for (i = 0; i < ARRAY_SIZE(prim_divs); i++) {
1228+
int div_prim = prim_divs[i];
1229+
int div_clk;
1230+
1231+
for (div_clk = 1; div_clk <= 256; div_clk++) {
1232+
core_rate = target_rate * div_clk * div_prim;
1233+
if (core_rate >= core_min) {
1234+
if (core_rate < best_rate) {
1235+
best_rate = core_rate;
1236+
best_div_prim = div_prim;
1237+
best_div_clk = div_clk;
1238+
}
1239+
break;
1240+
}
1241+
}
1242+
}
1243+
1244+
if (best_rate < core_max) {
1245+
div = ((best_rate << 24) + xosc_rate / 2) / xosc_rate;
1246+
div_int = div >> 24;
1247+
div_frac = div % (1 << 24);
1248+
core_rate = (xosc_rate * ((div_int << 24) + div_frac) + (1 << 23)) >> 24;
1249+
} else {
1250+
core_rate = 0;
1251+
}
1252+
1253+
if (pdiv_prim)
1254+
*pdiv_prim = best_div_prim;
1255+
if (pdiv_clk)
1256+
*pdiv_clk = best_div_clk;
1257+
1258+
return core_rate;
1259+
}
1260+
11911261
static void rp1_clock_choose_div_and_prate(struct clk_hw *hw,
11921262
int parent_idx,
11931263
unsigned long rate,
@@ -1199,8 +1269,43 @@ static void rp1_clock_choose_div_and_prate(struct clk_hw *hw,
11991269
struct clk_hw *parent;
12001270
u32 div;
12011271
u64 tmp;
1272+
int i;
12021273

12031274
parent = clk_hw_get_parent_by_index(hw, parent_idx);
1275+
1276+
for (i = 0; i < ARRAY_SIZE(rp1_clk_chg_tree); i++) {
1277+
const struct rp1_clk_change *chg = &rp1_clk_chg_tree[i];
1278+
1279+
if (chg->hw == hw && chg->new_rate == rate) {
1280+
if (i == 2)
1281+
*prate = clk_hw_get_rate(clk_xosc);
1282+
else if (parent == rp1_clk_chg_tree[i + 1].hw)
1283+
*prate = rp1_clk_chg_tree[i + 1].new_rate;
1284+
else
1285+
continue;
1286+
*calc_rate = chg->new_rate;
1287+
return;
1288+
}
1289+
}
1290+
1291+
if (hw == clk_i2s && parent == clk_audio) {
1292+
unsigned long core_rate, audio_rate, i2s_rate;
1293+
int div_prim, div_clk;
1294+
1295+
core_rate = calc_core_pll_rate(parent, rate, &div_prim, &div_clk);
1296+
audio_rate = DIV_NEAREST(core_rate, div_prim);
1297+
i2s_rate = DIV_NEAREST(audio_rate, div_clk);
1298+
rp1_clk_chg_tree[2].hw = clk_hw_get_parent(parent);
1299+
rp1_clk_chg_tree[2].new_rate = core_rate;
1300+
rp1_clk_chg_tree[1].hw = clk_audio;
1301+
rp1_clk_chg_tree[1].new_rate = audio_rate;
1302+
rp1_clk_chg_tree[0].hw = clk_i2s;
1303+
rp1_clk_chg_tree[0].new_rate = i2s_rate;
1304+
*prate = audio_rate;
1305+
*calc_rate = i2s_rate;
1306+
return;
1307+
}
1308+
12041309
*prate = clk_hw_get_rate(parent);
12051310
div = rp1_clock_choose_div(rate, *prate, data);
12061311

@@ -1608,6 +1713,7 @@ static const struct rp1_clk_desc clk_desc_array[] = {
16081713
.source_pll = "pll_audio_core",
16091714
.ctrl_reg = PLL_AUDIO_PRIM,
16101715
.fc0_src = FC_NUM(4, 2),
1716+
.flags = CLK_SET_RATE_PARENT,
16111717
),
16121718

16131719
[RP1_PLL_VIDEO] = REGISTER_PLL(
@@ -1850,6 +1956,7 @@ static const struct rp1_clk_desc clk_desc_array[] = {
18501956
.div_int_max = DIV_INT_8BIT_MAX,
18511957
.max_freq = 50 * MHz,
18521958
.fc0_src = FC_NUM(4, 4),
1959+
.flags = CLK_SET_RATE_PARENT,
18531960
),
18541961

18551962
[RP1_CLK_MIPI0_CFG] = REGISTER_CLK(
@@ -2272,8 +2379,14 @@ static int rp1_clk_probe(struct platform_device *pdev)
22722379

22732380
for (i = 0; i < asize; i++) {
22742381
desc = &clk_desc_array[i];
2275-
if (desc->clk_register && desc->data)
2382+
if (desc->clk_register && desc->data) {
22762383
hws[i] = desc->clk_register(clockman, desc->data);
2384+
if (!strcmp(clk_hw_get_name(hws[i]), "clk_i2s")) {
2385+
clk_i2s = hws[i];
2386+
clk_xosc = clk_hw_get_parent_by_index(clk_i2s, 0);
2387+
clk_audio = clk_hw_get_parent_by_index(clk_i2s, 1);
2388+
}
2389+
}
22772390
}
22782391

22792392
ret = of_clk_add_hw_provider(dev->of_node, of_clk_hw_onecell_get,

0 commit comments

Comments
 (0)