Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RP1 384kHz I2S audio support #5999

Merged
merged 4 commits into from
Mar 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
141 changes: 127 additions & 14 deletions drivers/clk/clk-rp1.c
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,7 @@
const char * const fc0_ref_clk_name = "clk_slow_sys";

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

/*
Expand Down Expand Up @@ -393,6 +394,18 @@ struct rp1_clock {
unsigned long cached_rate;
};


struct rp1_clk_change {
struct clk_hw *hw;
unsigned long new_rate;
};

struct rp1_clk_change rp1_clk_chg_tree[3];

static struct clk_hw *clk_xosc;
static struct clk_hw *clk_audio;
static struct clk_hw *clk_i2s;

static void rp1_debugfs_regset(struct rp1_clockman *clockman, u32 base,
const struct debugfs_reg32 *regs,
size_t nregs, struct dentry *dentry)
Expand Down Expand Up @@ -749,8 +762,12 @@ static unsigned long rp1_pll_recalc_rate(struct clk_hw *hw,
static long rp1_pll_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate)
{
const struct rp1_clk_change *chg = &rp1_clk_chg_tree[1];
u32 div1, div2;

if (chg->hw == hw && chg->new_rate == rate)
*parent_rate = chg[1].new_rate;

get_pll_prim_dividers(rate, *parent_rate, &div1, &div2);

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

static unsigned long calc_core_pll_rate(struct clk_hw *pll_hw,
unsigned long target_rate,
int *pdiv_prim, int *pdiv_clk)
{
static const int prim_divs[] = {
2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 14, 15, 16,
18, 20, 21, 24, 25, 28, 30, 35, 36, 42, 49,
};
const unsigned long xosc_rate = clk_hw_get_rate(clk_xosc);
const unsigned long core_max = 2400000000;
const unsigned long core_min = xosc_rate * 16;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The datasheet VCO minimum here is a static 600MHz, but the VCO-to-refclk ratio is more restrictive. RP1 probably won't ever be fitted with a different (slower) crystal.

unsigned long best_rate = core_max + 1;
int best_div_prim = 1, best_div_clk = 1;
unsigned long core_rate = 0;
int div_int, div_frac;
u64 div;
int i;

/* Given the target rate, choose a set of divisors/multipliers */
for (i = 0; i < ARRAY_SIZE(prim_divs); i++) {
int div_prim = prim_divs[i];
int div_clk;

for (div_clk = 1; div_clk <= 256; div_clk++) {
core_rate = target_rate * div_clk * div_prim;
if (core_rate >= core_min) {
if (core_rate < best_rate) {
best_rate = core_rate;
best_div_prim = div_prim;
best_div_clk = div_clk;
}
break;
}
}
}

if (best_rate < core_max) {
div = ((best_rate << 24) + xosc_rate / 2) / xosc_rate;
div_int = div >> 24;
div_frac = div % (1 << 24);
core_rate = (xosc_rate * ((div_int << 24) + div_frac) + (1 << 23)) >> 24;
} else {
core_rate = 0;
}

if (pdiv_prim)
*pdiv_prim = best_div_prim;
if (pdiv_clk)
*pdiv_clk = best_div_clk;

return core_rate;
}

static void rp1_clock_choose_div_and_prate(struct clk_hw *hw,
int parent_idx,
unsigned long rate,
Expand All @@ -1199,8 +1269,43 @@ static void rp1_clock_choose_div_and_prate(struct clk_hw *hw,
struct clk_hw *parent;
u32 div;
u64 tmp;
int i;

parent = clk_hw_get_parent_by_index(hw, parent_idx);

for (i = 0; i < ARRAY_SIZE(rp1_clk_chg_tree); i++) {
const struct rp1_clk_change *chg = &rp1_clk_chg_tree[i];

if (chg->hw == hw && chg->new_rate == rate) {
if (i == 2)
*prate = clk_hw_get_rate(clk_xosc);
else if (parent == rp1_clk_chg_tree[i + 1].hw)
*prate = rp1_clk_chg_tree[i + 1].new_rate;
else
continue;
*calc_rate = chg->new_rate;
return;
}
}

if (hw == clk_i2s && parent == clk_audio) {
unsigned long core_rate, audio_rate, i2s_rate;
int div_prim, div_clk;

core_rate = calc_core_pll_rate(parent, rate, &div_prim, &div_clk);
audio_rate = DIV_NEAREST(core_rate, div_prim);
i2s_rate = DIV_NEAREST(audio_rate, div_clk);
rp1_clk_chg_tree[2].hw = clk_hw_get_parent(parent);
rp1_clk_chg_tree[2].new_rate = core_rate;
rp1_clk_chg_tree[1].hw = clk_audio;
rp1_clk_chg_tree[1].new_rate = audio_rate;
rp1_clk_chg_tree[0].hw = clk_i2s;
rp1_clk_chg_tree[0].new_rate = i2s_rate;
*prate = audio_rate;
*calc_rate = i2s_rate;
return;
}

*prate = clk_hw_get_rate(parent);
div = rp1_clock_choose_div(rate, *prate, data);

Expand Down Expand Up @@ -1608,6 +1713,7 @@ static const struct rp1_clk_desc clk_desc_array[] = {
.source_pll = "pll_audio_core",
.ctrl_reg = PLL_AUDIO_PRIM,
.fc0_src = FC_NUM(4, 2),
.flags = CLK_SET_RATE_PARENT,
),

[RP1_PLL_VIDEO] = REGISTER_PLL(
Expand Down Expand Up @@ -1744,7 +1850,7 @@ static const struct rp1_clk_desc clk_desc_array[] = {

[RP1_CLK_PWM0] = REGISTER_CLK(
.name = "clk_pwm0",
.parents = {"pll_audio_pri_ph",
.parents = {"", // "pll_audio_pri_ph",
"pll_video_sec",
"xosc",
"clksrc_gp0",
Expand All @@ -1766,7 +1872,7 @@ static const struct rp1_clk_desc clk_desc_array[] = {

[RP1_CLK_PWM1] = REGISTER_CLK(
.name = "clk_pwm1",
.parents = {"pll_audio_pri_ph",
.parents = {"", // "pll_audio_pri_ph",
"pll_video_sec",
"xosc",
"clksrc_gp0",
Expand All @@ -1788,9 +1894,9 @@ static const struct rp1_clk_desc clk_desc_array[] = {

[RP1_CLK_AUDIO_IN] = REGISTER_CLK(
.name = "clk_audio_in",
.parents = {"pll_audio",
"pll_audio_pri_ph",
"pll_audio_sec",
.parents = {"", //"pll_audio",
"", //"pll_audio_pri_ph",
"", //"pll_audio_sec",
"pll_video_sec",
"xosc",
"clksrc_gp0",
Expand All @@ -1811,8 +1917,8 @@ static const struct rp1_clk_desc clk_desc_array[] = {

[RP1_CLK_AUDIO_OUT] = REGISTER_CLK(
.name = "clk_audio_out",
.parents = {"pll_audio",
"pll_audio_sec",
.parents = {"", //"pll_audio",
"", //"pll_audio_sec",
"pll_video_sec",
"xosc",
"clksrc_gp0",
Expand Down Expand Up @@ -1850,6 +1956,7 @@ static const struct rp1_clk_desc clk_desc_array[] = {
.div_int_max = DIV_INT_8BIT_MAX,
.max_freq = 50 * MHz,
.fc0_src = FC_NUM(4, 4),
.flags = CLK_SET_RATE_PARENT,
),

[RP1_CLK_MIPI0_CFG] = REGISTER_CLK(
Expand Down Expand Up @@ -1902,7 +2009,7 @@ static const struct rp1_clk_desc clk_desc_array[] = {
[RP1_CLK_ADC] = REGISTER_CLK(
.name = "clk_adc",
.parents = {"xosc",
"pll_audio_tern",
"", //"pll_audio_tern",
"clksrc_gp0",
"clksrc_gp1",
"clksrc_gp2",
Expand Down Expand Up @@ -1954,7 +2061,7 @@ static const struct rp1_clk_desc clk_desc_array[] = {
"clksrc_gp4",
"clksrc_gp5",
"pll_sys",
"pll_audio",
"", //"pll_audio",
"",
"",
"clk_i2s",
Expand Down Expand Up @@ -1984,7 +2091,7 @@ static const struct rp1_clk_desc clk_desc_array[] = {
"clksrc_gp4",
"clksrc_gp5",
"pll_sys_pri_ph",
"pll_audio_pri_ph",
"", //"pll_audio_pri_ph",
"",
"",
"clk_adc",
Expand Down Expand Up @@ -2014,7 +2121,7 @@ static const struct rp1_clk_desc clk_desc_array[] = {
"clksrc_gp4",
"clksrc_gp5",
"pll_sys_sec",
"pll_audio_sec",
"", //"pll_audio_sec",
"pll_video",
"clk_audio_in",
"clk_dpi",
Expand Down Expand Up @@ -2073,7 +2180,7 @@ static const struct rp1_clk_desc clk_desc_array[] = {
"clksrc_gp2",
"clksrc_gp3",
"clksrc_gp5",
"pll_audio_tern",
"", //"pll_audio_tern",
"pll_video_sec",
"",
"",
Expand Down Expand Up @@ -2104,7 +2211,7 @@ static const struct rp1_clk_desc clk_desc_array[] = {
"clksrc_gp2",
"clksrc_gp3",
"clksrc_gp4",
"pll_audio_tern",
"", //"pll_audio_tern",
"pll_video_sec",
"clk_eth_tsu",
"",
Expand Down Expand Up @@ -2272,8 +2379,14 @@ static int rp1_clk_probe(struct platform_device *pdev)

for (i = 0; i < asize; i++) {
desc = &clk_desc_array[i];
if (desc->clk_register && desc->data)
if (desc->clk_register && desc->data) {
hws[i] = desc->clk_register(clockman, desc->data);
if (!strcmp(clk_hw_get_name(hws[i]), "clk_i2s")) {
clk_i2s = hws[i];
clk_xosc = clk_hw_get_parent_by_index(clk_i2s, 0);
clk_audio = clk_hw_get_parent_by_index(clk_i2s, 1);
}
}
}

ret = of_clk_add_hw_provider(dev->of_node, of_clk_hw_onecell_get,
Expand Down
2 changes: 1 addition & 1 deletion sound/soc/bcm/allo-boss-dac.c
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,7 @@ static int snd_allo_boss_hw_params(
int ret = 0;
struct snd_soc_pcm_runtime *rtd = substream->private_data;
int channels = params_channels(params);
int width = snd_pcm_format_physical_width(params_format(params));
int width = snd_pcm_format_width(params_format(params));

if (snd_soc_allo_boss_master) {
struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component;
Expand Down
2 changes: 1 addition & 1 deletion sound/soc/bcm/dionaudio_loco.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ static int snd_rpi_dionaudio_loco_hw_params(
struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);

unsigned int sample_bits =
snd_pcm_format_physical_width(params_format(params));
snd_pcm_format_width(params_format(params));

return snd_soc_dai_set_bclk_ratio(cpu_dai, sample_bits * 2);
}
Expand Down
2 changes: 1 addition & 1 deletion sound/soc/bcm/hifiberry_dacplus.c
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,7 @@ static int snd_rpi_hifiberry_dacplus_hw_params(
int ret = 0;
struct snd_soc_pcm_runtime *rtd = substream->private_data;
int channels = params_channels(params);
int width = snd_pcm_format_physical_width(params_format(params));
int width = snd_pcm_format_width(params_format(params));

if (snd_rpi_hifiberry_is_dacpro) {
struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component;
Expand Down
2 changes: 1 addition & 1 deletion sound/soc/bcm/hifiberry_dacplusadc.c
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@ static int snd_rpi_hifiberry_dacplusadc_hw_params(
if (snd_rpi_hifiberry_is_dacpro) {
struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component;

width = snd_pcm_format_physical_width(params_format(params));
width = snd_pcm_format_width(params_format(params));

snd_rpi_hifiberry_dacplusadc_set_sclk(component,
params_rate(params));
Expand Down
2 changes: 1 addition & 1 deletion sound/soc/bcm/hifiberry_dacplusadcpro.c
Original file line number Diff line number Diff line change
Expand Up @@ -383,7 +383,7 @@ static int snd_rpi_hifiberry_dacplusadcpro_hw_params(
int ret = 0;
struct snd_soc_pcm_runtime *rtd = substream->private_data;
int channels = params_channels(params);
int width = snd_pcm_format_physical_width(params_format(params));
int width = snd_pcm_format_width(params_format(params));
struct snd_soc_component *dac = asoc_rtd_to_codec(rtd, 0)->component;
struct snd_soc_dai *dai = asoc_rtd_to_codec(rtd, 0);
struct snd_soc_dai_driver *drv = dai->driver;
Expand Down
2 changes: 1 addition & 1 deletion sound/soc/bcm/i-sabre-q2m.c
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ static int snd_rpi_i_sabre_q2m_hw_params(
struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
int bclk_ratio;

bclk_ratio = snd_pcm_format_physical_width(
bclk_ratio = snd_pcm_format_width(
params_format(params)) * params_channels(params);
return snd_soc_dai_set_bclk_ratio(cpu_dai, bclk_ratio);
}
Expand Down
2 changes: 0 additions & 2 deletions sound/soc/bcm/pifi-40.c
Original file line number Diff line number Diff line change
Expand Up @@ -140,9 +140,7 @@ static int snd_pifi_40_hw_params(struct snd_pcm_substream *substream,
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
unsigned int sample_bits;

sample_bits = snd_pcm_format_physical_width(params_format(params));
return snd_soc_dai_set_bclk_ratio(cpu_dai, 64);
}

Expand Down
2 changes: 1 addition & 1 deletion sound/soc/bcm/pisound.c
Original file line number Diff line number Diff line change
Expand Up @@ -895,7 +895,7 @@ static int pisnd_hw_params(
printd("rate = %d\n", params_rate(params));
printd("ch = %d\n", params_channels(params));
printd("bits = %u\n",
snd_pcm_format_physical_width(params_format(params)));
snd_pcm_format_width(params_format(params)));
printd("format = %d\n", params_format(params));

gpiod_set_value(reset, false);
Expand Down
3 changes: 1 addition & 2 deletions sound/soc/bcm/rpi-cirrus.c
Original file line number Diff line number Diff line change
Expand Up @@ -704,8 +704,7 @@ static int rpi_cirrus_hw_params(struct snd_pcm_substream *substream,

int ret;

unsigned int width = snd_pcm_format_physical_width(
params_format(params));
unsigned int width = snd_pcm_format_width(params_format(params));
unsigned int rate = params_rate(params);
unsigned int clk_freq = calc_sysclk(rate);

Expand Down
2 changes: 1 addition & 1 deletion sound/soc/bcm/rpi-simple-soundcard.c
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ static int snd_rpi_simple_hw_params(struct snd_pcm_substream *substream,
* hard-code this for now. More complex drivers could just replace
* the hw_params routine.
*/
sample_bits = snd_pcm_format_physical_width(params_format(params));
sample_bits = snd_pcm_format_width(params_format(params));
return snd_soc_dai_set_bclk_ratio(cpu_dai, sample_bits * 2);
}

Expand Down
Loading
Loading