| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193 |
- // SPDX-License-Identifier: GPL-2.0
- #include <linux/clk-provider.h>
- #include <linux/io.h>
- #include <linux/regulator/consumer.h>
- #include "mcde_drm.h"
- #include "mcde_display_regs.h"
- /* The MCDE internal clock dividers for FIFO A and B */
- struct mcde_clk_div {
- struct clk_hw hw;
- struct mcde *mcde;
- u32 cr;
- u32 cr_div;
- };
- static int mcde_clk_div_enable(struct clk_hw *hw)
- {
- struct mcde_clk_div *cdiv = container_of(hw, struct mcde_clk_div, hw);
- struct mcde *mcde = cdiv->mcde;
- u32 val;
- spin_lock(&mcde->fifo_crx1_lock);
- val = readl(mcde->regs + cdiv->cr);
- /*
- * Select the PLL72 (LCD) clock as parent
- * FIXME: implement other parents.
- */
- val &= ~MCDE_CRX1_CLKSEL_MASK;
- val |= MCDE_CRX1_CLKSEL_CLKPLL72 << MCDE_CRX1_CLKSEL_SHIFT;
- /* Internal clock */
- val |= MCDE_CRA1_CLKTYPE_TVXCLKSEL1;
- /* Clear then set the divider */
- val &= ~(MCDE_CRX1_BCD | MCDE_CRX1_PCD_MASK);
- val |= cdiv->cr_div;
- writel(val, mcde->regs + cdiv->cr);
- spin_unlock(&mcde->fifo_crx1_lock);
- return 0;
- }
- static int mcde_clk_div_choose_div(struct clk_hw *hw, unsigned long rate,
- unsigned long *prate, bool set_parent)
- {
- int best_div = 1, div;
- struct clk_hw *parent = clk_hw_get_parent(hw);
- unsigned long best_prate = 0;
- unsigned long best_diff = ~0ul;
- int max_div = (1 << MCDE_CRX1_PCD_BITS) - 1;
- for (div = 1; div < max_div; div++) {
- unsigned long this_prate, div_rate, diff;
- if (set_parent)
- this_prate = clk_hw_round_rate(parent, rate * div);
- else
- this_prate = *prate;
- div_rate = DIV_ROUND_UP_ULL(this_prate, div);
- diff = abs(rate - div_rate);
- if (diff < best_diff) {
- best_div = div;
- best_diff = diff;
- best_prate = this_prate;
- }
- }
- *prate = best_prate;
- return best_div;
- }
- static long mcde_clk_div_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *prate)
- {
- int div = mcde_clk_div_choose_div(hw, rate, prate, true);
- return DIV_ROUND_UP_ULL(*prate, div);
- }
- static unsigned long mcde_clk_div_recalc_rate(struct clk_hw *hw,
- unsigned long prate)
- {
- struct mcde_clk_div *cdiv = container_of(hw, struct mcde_clk_div, hw);
- struct mcde *mcde = cdiv->mcde;
- u32 cr;
- int div;
- /*
- * If the MCDE is not powered we can't access registers.
- * It will come up with 0 in the divider register bits, which
- * means "divide by 2".
- */
- if (!regulator_is_enabled(mcde->epod))
- return DIV_ROUND_UP_ULL(prate, 2);
- cr = readl(mcde->regs + cdiv->cr);
- if (cr & MCDE_CRX1_BCD)
- return prate;
- /* 0 in the PCD means "divide by 2", 1 means "divide by 3" etc */
- div = cr & MCDE_CRX1_PCD_MASK;
- div += 2;
- return DIV_ROUND_UP_ULL(prate, div);
- }
- static int mcde_clk_div_set_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long prate)
- {
- struct mcde_clk_div *cdiv = container_of(hw, struct mcde_clk_div, hw);
- int div = mcde_clk_div_choose_div(hw, rate, &prate, false);
- u32 cr = 0;
- /*
- * We cache the CR bits to set the divide in the state so that
- * we can call this before we can even write to the hardware.
- */
- if (div == 1) {
- /* Bypass clock divider */
- cr |= MCDE_CRX1_BCD;
- } else {
- div -= 2;
- cr |= div & MCDE_CRX1_PCD_MASK;
- }
- cdiv->cr_div = cr;
- return 0;
- }
- static const struct clk_ops mcde_clk_div_ops = {
- .enable = mcde_clk_div_enable,
- .recalc_rate = mcde_clk_div_recalc_rate,
- .round_rate = mcde_clk_div_round_rate,
- .set_rate = mcde_clk_div_set_rate,
- };
- int mcde_init_clock_divider(struct mcde *mcde)
- {
- struct device *dev = mcde->dev;
- struct mcde_clk_div *fifoa;
- struct mcde_clk_div *fifob;
- const char *parent_name;
- struct clk_init_data fifoa_init = {
- .name = "fifoa",
- .ops = &mcde_clk_div_ops,
- .parent_names = &parent_name,
- .num_parents = 1,
- .flags = CLK_SET_RATE_PARENT,
- };
- struct clk_init_data fifob_init = {
- .name = "fifob",
- .ops = &mcde_clk_div_ops,
- .parent_names = &parent_name,
- .num_parents = 1,
- .flags = CLK_SET_RATE_PARENT,
- };
- int ret;
- spin_lock_init(&mcde->fifo_crx1_lock);
- parent_name = __clk_get_name(mcde->lcd_clk);
- /* Allocate 2 clocks */
- fifoa = devm_kzalloc(dev, sizeof(*fifoa), GFP_KERNEL);
- if (!fifoa)
- return -ENOMEM;
- fifob = devm_kzalloc(dev, sizeof(*fifob), GFP_KERNEL);
- if (!fifob)
- return -ENOMEM;
- fifoa->mcde = mcde;
- fifoa->cr = MCDE_CRA1;
- fifoa->hw.init = &fifoa_init;
- ret = devm_clk_hw_register(dev, &fifoa->hw);
- if (ret) {
- dev_err(dev, "error registering FIFO A clock divider\n");
- return ret;
- }
- mcde->fifoa_clk = fifoa->hw.clk;
- fifob->mcde = mcde;
- fifob->cr = MCDE_CRB1;
- fifob->hw.init = &fifob_init;
- ret = devm_clk_hw_register(dev, &fifob->hw);
- if (ret) {
- dev_err(dev, "error registering FIFO B clock divider\n");
- return ret;
- }
- mcde->fifob_clk = fifob->hw.clk;
- return 0;
- }
|