linux/drivers/gpu/drm/mcde/mcde_clk_div.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2#include <linux/clk-provider.h>
   3#include <linux/regulator/consumer.h>
   4
   5#include "mcde_drm.h"
   6#include "mcde_display_regs.h"
   7
   8/* The MCDE internal clock dividers for FIFO A and B */
   9struct mcde_clk_div {
  10        struct clk_hw hw;
  11        struct mcde *mcde;
  12        u32 cr;
  13        u32 cr_div;
  14};
  15
  16static int mcde_clk_div_enable(struct clk_hw *hw)
  17{
  18        struct mcde_clk_div *cdiv = container_of(hw, struct mcde_clk_div, hw);
  19        struct mcde *mcde = cdiv->mcde;
  20        u32 val;
  21
  22        spin_lock(&mcde->fifo_crx1_lock);
  23        val = readl(mcde->regs + cdiv->cr);
  24        /*
  25         * Select the PLL72 (LCD) clock as parent
  26         * FIXME: implement other parents.
  27         */
  28        val &= ~MCDE_CRX1_CLKSEL_MASK;
  29        val |= MCDE_CRX1_CLKSEL_CLKPLL72 << MCDE_CRX1_CLKSEL_SHIFT;
  30        /* Internal clock */
  31        val |= MCDE_CRA1_CLKTYPE_TVXCLKSEL1;
  32
  33        /* Clear then set the divider */
  34        val &= ~(MCDE_CRX1_BCD | MCDE_CRX1_PCD_MASK);
  35        val |= cdiv->cr_div;
  36
  37        writel(val, mcde->regs + cdiv->cr);
  38        spin_unlock(&mcde->fifo_crx1_lock);
  39
  40        return 0;
  41}
  42
  43static int mcde_clk_div_choose_div(struct clk_hw *hw, unsigned long rate,
  44                                   unsigned long *prate, bool set_parent)
  45{
  46        int best_div = 1, div;
  47        struct clk_hw *parent = clk_hw_get_parent(hw);
  48        unsigned long best_prate = 0;
  49        unsigned long best_diff = ~0ul;
  50        int max_div = (1 << MCDE_CRX1_PCD_BITS) - 1;
  51
  52        for (div = 1; div < max_div; div++) {
  53                unsigned long this_prate, div_rate, diff;
  54
  55                if (set_parent)
  56                        this_prate = clk_hw_round_rate(parent, rate * div);
  57                else
  58                        this_prate = *prate;
  59                div_rate = DIV_ROUND_UP_ULL(this_prate, div);
  60                diff = abs(rate - div_rate);
  61
  62                if (diff < best_diff) {
  63                        best_div = div;
  64                        best_diff = diff;
  65                        best_prate = this_prate;
  66                }
  67        }
  68
  69        *prate = best_prate;
  70        return best_div;
  71}
  72
  73static long mcde_clk_div_round_rate(struct clk_hw *hw, unsigned long rate,
  74                                     unsigned long *prate)
  75{
  76        int div = mcde_clk_div_choose_div(hw, rate, prate, true);
  77
  78        return DIV_ROUND_UP_ULL(*prate, div);
  79}
  80
  81static unsigned long mcde_clk_div_recalc_rate(struct clk_hw *hw,
  82                                               unsigned long prate)
  83{
  84        struct mcde_clk_div *cdiv = container_of(hw, struct mcde_clk_div, hw);
  85        struct mcde *mcde = cdiv->mcde;
  86        u32 cr;
  87        int div;
  88
  89        /*
  90         * If the MCDE is not powered we can't access registers.
  91         * It will come up with 0 in the divider register bits, which
  92         * means "divide by 2".
  93         */
  94        if (!regulator_is_enabled(mcde->epod))
  95                return DIV_ROUND_UP_ULL(prate, 2);
  96
  97        cr = readl(mcde->regs + cdiv->cr);
  98        if (cr & MCDE_CRX1_BCD)
  99                return prate;
 100
 101        /* 0 in the PCD means "divide by 2", 1 means "divide by 3" etc */
 102        div = cr & MCDE_CRX1_PCD_MASK;
 103        div += 2;
 104
 105        return DIV_ROUND_UP_ULL(prate, div);
 106}
 107
 108static int mcde_clk_div_set_rate(struct clk_hw *hw, unsigned long rate,
 109                                  unsigned long prate)
 110{
 111        struct mcde_clk_div *cdiv = container_of(hw, struct mcde_clk_div, hw);
 112        int div = mcde_clk_div_choose_div(hw, rate, &prate, false);
 113        u32 cr = 0;
 114
 115        /*
 116         * We cache the CR bits to set the divide in the state so that
 117         * we can call this before we can even write to the hardware.
 118         */
 119        if (div == 1) {
 120                /* Bypass clock divider */
 121                cr |= MCDE_CRX1_BCD;
 122        } else {
 123                div -= 2;
 124                cr |= div & MCDE_CRX1_PCD_MASK;
 125        }
 126        cdiv->cr_div = cr;
 127
 128        return 0;
 129}
 130
 131static const struct clk_ops mcde_clk_div_ops = {
 132        .enable = mcde_clk_div_enable,
 133        .recalc_rate = mcde_clk_div_recalc_rate,
 134        .round_rate = mcde_clk_div_round_rate,
 135        .set_rate = mcde_clk_div_set_rate,
 136};
 137
 138int mcde_init_clock_divider(struct mcde *mcde)
 139{
 140        struct device *dev = mcde->dev;
 141        struct mcde_clk_div *fifoa;
 142        struct mcde_clk_div *fifob;
 143        const char *parent_name;
 144        struct clk_init_data fifoa_init = {
 145                .name = "fifoa",
 146                .ops = &mcde_clk_div_ops,
 147                .parent_names = &parent_name,
 148                .num_parents = 1,
 149                .flags = CLK_SET_RATE_PARENT,
 150        };
 151        struct clk_init_data fifob_init = {
 152                .name = "fifob",
 153                .ops = &mcde_clk_div_ops,
 154                .parent_names = &parent_name,
 155                .num_parents = 1,
 156                .flags = CLK_SET_RATE_PARENT,
 157        };
 158        int ret;
 159
 160        spin_lock_init(&mcde->fifo_crx1_lock);
 161        parent_name = __clk_get_name(mcde->lcd_clk);
 162
 163        /* Allocate 2 clocks */
 164        fifoa = devm_kzalloc(dev, sizeof(*fifoa), GFP_KERNEL);
 165        if (!fifoa)
 166                return -ENOMEM;
 167        fifob = devm_kzalloc(dev, sizeof(*fifob), GFP_KERNEL);
 168        if (!fifob)
 169                return -ENOMEM;
 170
 171        fifoa->mcde = mcde;
 172        fifoa->cr = MCDE_CRA1;
 173        fifoa->hw.init = &fifoa_init;
 174        ret = devm_clk_hw_register(dev, &fifoa->hw);
 175        if (ret) {
 176                dev_err(dev, "error registering FIFO A clock divider\n");
 177                return ret;
 178        }
 179        mcde->fifoa_clk = fifoa->hw.clk;
 180
 181        fifob->mcde = mcde;
 182        fifob->cr = MCDE_CRB1;
 183        fifob->hw.init = &fifob_init;
 184        ret = devm_clk_hw_register(dev, &fifob->hw);
 185        if (ret) {
 186                dev_err(dev, "error registering FIFO B clock divider\n");
 187                return ret;
 188        }
 189        mcde->fifob_clk = fifob->hw.clk;
 190
 191        return 0;
 192}
 193