162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci#include <linux/clk-provider.h>
362306a36Sopenharmony_ci#include <linux/io.h>
462306a36Sopenharmony_ci#include <linux/regulator/consumer.h>
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include "mcde_drm.h"
762306a36Sopenharmony_ci#include "mcde_display_regs.h"
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci/* The MCDE internal clock dividers for FIFO A and B */
1062306a36Sopenharmony_cistruct mcde_clk_div {
1162306a36Sopenharmony_ci	struct clk_hw hw;
1262306a36Sopenharmony_ci	struct mcde *mcde;
1362306a36Sopenharmony_ci	u32 cr;
1462306a36Sopenharmony_ci	u32 cr_div;
1562306a36Sopenharmony_ci};
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_cistatic int mcde_clk_div_enable(struct clk_hw *hw)
1862306a36Sopenharmony_ci{
1962306a36Sopenharmony_ci	struct mcde_clk_div *cdiv = container_of(hw, struct mcde_clk_div, hw);
2062306a36Sopenharmony_ci	struct mcde *mcde = cdiv->mcde;
2162306a36Sopenharmony_ci	u32 val;
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci	spin_lock(&mcde->fifo_crx1_lock);
2462306a36Sopenharmony_ci	val = readl(mcde->regs + cdiv->cr);
2562306a36Sopenharmony_ci	/*
2662306a36Sopenharmony_ci	 * Select the PLL72 (LCD) clock as parent
2762306a36Sopenharmony_ci	 * FIXME: implement other parents.
2862306a36Sopenharmony_ci	 */
2962306a36Sopenharmony_ci	val &= ~MCDE_CRX1_CLKSEL_MASK;
3062306a36Sopenharmony_ci	val |= MCDE_CRX1_CLKSEL_CLKPLL72 << MCDE_CRX1_CLKSEL_SHIFT;
3162306a36Sopenharmony_ci	/* Internal clock */
3262306a36Sopenharmony_ci	val |= MCDE_CRA1_CLKTYPE_TVXCLKSEL1;
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci	/* Clear then set the divider */
3562306a36Sopenharmony_ci	val &= ~(MCDE_CRX1_BCD | MCDE_CRX1_PCD_MASK);
3662306a36Sopenharmony_ci	val |= cdiv->cr_div;
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	writel(val, mcde->regs + cdiv->cr);
3962306a36Sopenharmony_ci	spin_unlock(&mcde->fifo_crx1_lock);
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	return 0;
4262306a36Sopenharmony_ci}
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_cistatic int mcde_clk_div_choose_div(struct clk_hw *hw, unsigned long rate,
4562306a36Sopenharmony_ci				   unsigned long *prate, bool set_parent)
4662306a36Sopenharmony_ci{
4762306a36Sopenharmony_ci	int best_div = 1, div;
4862306a36Sopenharmony_ci	struct clk_hw *parent = clk_hw_get_parent(hw);
4962306a36Sopenharmony_ci	unsigned long best_prate = 0;
5062306a36Sopenharmony_ci	unsigned long best_diff = ~0ul;
5162306a36Sopenharmony_ci	int max_div = (1 << MCDE_CRX1_PCD_BITS) - 1;
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	for (div = 1; div < max_div; div++) {
5462306a36Sopenharmony_ci		unsigned long this_prate, div_rate, diff;
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci		if (set_parent)
5762306a36Sopenharmony_ci			this_prate = clk_hw_round_rate(parent, rate * div);
5862306a36Sopenharmony_ci		else
5962306a36Sopenharmony_ci			this_prate = *prate;
6062306a36Sopenharmony_ci		div_rate = DIV_ROUND_UP_ULL(this_prate, div);
6162306a36Sopenharmony_ci		diff = abs(rate - div_rate);
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci		if (diff < best_diff) {
6462306a36Sopenharmony_ci			best_div = div;
6562306a36Sopenharmony_ci			best_diff = diff;
6662306a36Sopenharmony_ci			best_prate = this_prate;
6762306a36Sopenharmony_ci		}
6862306a36Sopenharmony_ci	}
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	*prate = best_prate;
7162306a36Sopenharmony_ci	return best_div;
7262306a36Sopenharmony_ci}
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_cistatic long mcde_clk_div_round_rate(struct clk_hw *hw, unsigned long rate,
7562306a36Sopenharmony_ci				     unsigned long *prate)
7662306a36Sopenharmony_ci{
7762306a36Sopenharmony_ci	int div = mcde_clk_div_choose_div(hw, rate, prate, true);
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	return DIV_ROUND_UP_ULL(*prate, div);
8062306a36Sopenharmony_ci}
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_cistatic unsigned long mcde_clk_div_recalc_rate(struct clk_hw *hw,
8362306a36Sopenharmony_ci					       unsigned long prate)
8462306a36Sopenharmony_ci{
8562306a36Sopenharmony_ci	struct mcde_clk_div *cdiv = container_of(hw, struct mcde_clk_div, hw);
8662306a36Sopenharmony_ci	struct mcde *mcde = cdiv->mcde;
8762306a36Sopenharmony_ci	u32 cr;
8862306a36Sopenharmony_ci	int div;
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	/*
9162306a36Sopenharmony_ci	 * If the MCDE is not powered we can't access registers.
9262306a36Sopenharmony_ci	 * It will come up with 0 in the divider register bits, which
9362306a36Sopenharmony_ci	 * means "divide by 2".
9462306a36Sopenharmony_ci	 */
9562306a36Sopenharmony_ci	if (!regulator_is_enabled(mcde->epod))
9662306a36Sopenharmony_ci		return DIV_ROUND_UP_ULL(prate, 2);
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	cr = readl(mcde->regs + cdiv->cr);
9962306a36Sopenharmony_ci	if (cr & MCDE_CRX1_BCD)
10062306a36Sopenharmony_ci		return prate;
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	/* 0 in the PCD means "divide by 2", 1 means "divide by 3" etc */
10362306a36Sopenharmony_ci	div = cr & MCDE_CRX1_PCD_MASK;
10462306a36Sopenharmony_ci	div += 2;
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	return DIV_ROUND_UP_ULL(prate, div);
10762306a36Sopenharmony_ci}
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_cistatic int mcde_clk_div_set_rate(struct clk_hw *hw, unsigned long rate,
11062306a36Sopenharmony_ci				  unsigned long prate)
11162306a36Sopenharmony_ci{
11262306a36Sopenharmony_ci	struct mcde_clk_div *cdiv = container_of(hw, struct mcde_clk_div, hw);
11362306a36Sopenharmony_ci	int div = mcde_clk_div_choose_div(hw, rate, &prate, false);
11462306a36Sopenharmony_ci	u32 cr = 0;
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	/*
11762306a36Sopenharmony_ci	 * We cache the CR bits to set the divide in the state so that
11862306a36Sopenharmony_ci	 * we can call this before we can even write to the hardware.
11962306a36Sopenharmony_ci	 */
12062306a36Sopenharmony_ci	if (div == 1) {
12162306a36Sopenharmony_ci		/* Bypass clock divider */
12262306a36Sopenharmony_ci		cr |= MCDE_CRX1_BCD;
12362306a36Sopenharmony_ci	} else {
12462306a36Sopenharmony_ci		div -= 2;
12562306a36Sopenharmony_ci		cr |= div & MCDE_CRX1_PCD_MASK;
12662306a36Sopenharmony_ci	}
12762306a36Sopenharmony_ci	cdiv->cr_div = cr;
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	return 0;
13062306a36Sopenharmony_ci}
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_cistatic const struct clk_ops mcde_clk_div_ops = {
13362306a36Sopenharmony_ci	.enable = mcde_clk_div_enable,
13462306a36Sopenharmony_ci	.recalc_rate = mcde_clk_div_recalc_rate,
13562306a36Sopenharmony_ci	.round_rate = mcde_clk_div_round_rate,
13662306a36Sopenharmony_ci	.set_rate = mcde_clk_div_set_rate,
13762306a36Sopenharmony_ci};
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ciint mcde_init_clock_divider(struct mcde *mcde)
14062306a36Sopenharmony_ci{
14162306a36Sopenharmony_ci	struct device *dev = mcde->dev;
14262306a36Sopenharmony_ci	struct mcde_clk_div *fifoa;
14362306a36Sopenharmony_ci	struct mcde_clk_div *fifob;
14462306a36Sopenharmony_ci	const char *parent_name;
14562306a36Sopenharmony_ci	struct clk_init_data fifoa_init = {
14662306a36Sopenharmony_ci		.name = "fifoa",
14762306a36Sopenharmony_ci		.ops = &mcde_clk_div_ops,
14862306a36Sopenharmony_ci		.parent_names = &parent_name,
14962306a36Sopenharmony_ci		.num_parents = 1,
15062306a36Sopenharmony_ci		.flags = CLK_SET_RATE_PARENT,
15162306a36Sopenharmony_ci	};
15262306a36Sopenharmony_ci	struct clk_init_data fifob_init = {
15362306a36Sopenharmony_ci		.name = "fifob",
15462306a36Sopenharmony_ci		.ops = &mcde_clk_div_ops,
15562306a36Sopenharmony_ci		.parent_names = &parent_name,
15662306a36Sopenharmony_ci		.num_parents = 1,
15762306a36Sopenharmony_ci		.flags = CLK_SET_RATE_PARENT,
15862306a36Sopenharmony_ci	};
15962306a36Sopenharmony_ci	int ret;
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	spin_lock_init(&mcde->fifo_crx1_lock);
16262306a36Sopenharmony_ci	parent_name = __clk_get_name(mcde->lcd_clk);
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	/* Allocate 2 clocks */
16562306a36Sopenharmony_ci	fifoa = devm_kzalloc(dev, sizeof(*fifoa), GFP_KERNEL);
16662306a36Sopenharmony_ci	if (!fifoa)
16762306a36Sopenharmony_ci		return -ENOMEM;
16862306a36Sopenharmony_ci	fifob = devm_kzalloc(dev, sizeof(*fifob), GFP_KERNEL);
16962306a36Sopenharmony_ci	if (!fifob)
17062306a36Sopenharmony_ci		return -ENOMEM;
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	fifoa->mcde = mcde;
17362306a36Sopenharmony_ci	fifoa->cr = MCDE_CRA1;
17462306a36Sopenharmony_ci	fifoa->hw.init = &fifoa_init;
17562306a36Sopenharmony_ci	ret = devm_clk_hw_register(dev, &fifoa->hw);
17662306a36Sopenharmony_ci	if (ret) {
17762306a36Sopenharmony_ci		dev_err(dev, "error registering FIFO A clock divider\n");
17862306a36Sopenharmony_ci		return ret;
17962306a36Sopenharmony_ci	}
18062306a36Sopenharmony_ci	mcde->fifoa_clk = fifoa->hw.clk;
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	fifob->mcde = mcde;
18362306a36Sopenharmony_ci	fifob->cr = MCDE_CRB1;
18462306a36Sopenharmony_ci	fifob->hw.init = &fifob_init;
18562306a36Sopenharmony_ci	ret = devm_clk_hw_register(dev, &fifob->hw);
18662306a36Sopenharmony_ci	if (ret) {
18762306a36Sopenharmony_ci		dev_err(dev, "error registering FIFO B clock divider\n");
18862306a36Sopenharmony_ci		return ret;
18962306a36Sopenharmony_ci	}
19062306a36Sopenharmony_ci	mcde->fifob_clk = fifob->hw.clk;
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	return 0;
19362306a36Sopenharmony_ci}
194