Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 #include <linux/clk-provider.h>
0003 #include <linux/io.h>
0004 #include <linux/regulator/consumer.h>
0005 
0006 #include "mcde_drm.h"
0007 #include "mcde_display_regs.h"
0008 
0009 /* The MCDE internal clock dividers for FIFO A and B */
0010 struct mcde_clk_div {
0011     struct clk_hw hw;
0012     struct mcde *mcde;
0013     u32 cr;
0014     u32 cr_div;
0015 };
0016 
0017 static int mcde_clk_div_enable(struct clk_hw *hw)
0018 {
0019     struct mcde_clk_div *cdiv = container_of(hw, struct mcde_clk_div, hw);
0020     struct mcde *mcde = cdiv->mcde;
0021     u32 val;
0022 
0023     spin_lock(&mcde->fifo_crx1_lock);
0024     val = readl(mcde->regs + cdiv->cr);
0025     /*
0026      * Select the PLL72 (LCD) clock as parent
0027      * FIXME: implement other parents.
0028      */
0029     val &= ~MCDE_CRX1_CLKSEL_MASK;
0030     val |= MCDE_CRX1_CLKSEL_CLKPLL72 << MCDE_CRX1_CLKSEL_SHIFT;
0031     /* Internal clock */
0032     val |= MCDE_CRA1_CLKTYPE_TVXCLKSEL1;
0033 
0034     /* Clear then set the divider */
0035     val &= ~(MCDE_CRX1_BCD | MCDE_CRX1_PCD_MASK);
0036     val |= cdiv->cr_div;
0037 
0038     writel(val, mcde->regs + cdiv->cr);
0039     spin_unlock(&mcde->fifo_crx1_lock);
0040 
0041     return 0;
0042 }
0043 
0044 static int mcde_clk_div_choose_div(struct clk_hw *hw, unsigned long rate,
0045                    unsigned long *prate, bool set_parent)
0046 {
0047     int best_div = 1, div;
0048     struct clk_hw *parent = clk_hw_get_parent(hw);
0049     unsigned long best_prate = 0;
0050     unsigned long best_diff = ~0ul;
0051     int max_div = (1 << MCDE_CRX1_PCD_BITS) - 1;
0052 
0053     for (div = 1; div < max_div; div++) {
0054         unsigned long this_prate, div_rate, diff;
0055 
0056         if (set_parent)
0057             this_prate = clk_hw_round_rate(parent, rate * div);
0058         else
0059             this_prate = *prate;
0060         div_rate = DIV_ROUND_UP_ULL(this_prate, div);
0061         diff = abs(rate - div_rate);
0062 
0063         if (diff < best_diff) {
0064             best_div = div;
0065             best_diff = diff;
0066             best_prate = this_prate;
0067         }
0068     }
0069 
0070     *prate = best_prate;
0071     return best_div;
0072 }
0073 
0074 static long mcde_clk_div_round_rate(struct clk_hw *hw, unsigned long rate,
0075                      unsigned long *prate)
0076 {
0077     int div = mcde_clk_div_choose_div(hw, rate, prate, true);
0078 
0079     return DIV_ROUND_UP_ULL(*prate, div);
0080 }
0081 
0082 static unsigned long mcde_clk_div_recalc_rate(struct clk_hw *hw,
0083                            unsigned long prate)
0084 {
0085     struct mcde_clk_div *cdiv = container_of(hw, struct mcde_clk_div, hw);
0086     struct mcde *mcde = cdiv->mcde;
0087     u32 cr;
0088     int div;
0089 
0090     /*
0091      * If the MCDE is not powered we can't access registers.
0092      * It will come up with 0 in the divider register bits, which
0093      * means "divide by 2".
0094      */
0095     if (!regulator_is_enabled(mcde->epod))
0096         return DIV_ROUND_UP_ULL(prate, 2);
0097 
0098     cr = readl(mcde->regs + cdiv->cr);
0099     if (cr & MCDE_CRX1_BCD)
0100         return prate;
0101 
0102     /* 0 in the PCD means "divide by 2", 1 means "divide by 3" etc */
0103     div = cr & MCDE_CRX1_PCD_MASK;
0104     div += 2;
0105 
0106     return DIV_ROUND_UP_ULL(prate, div);
0107 }
0108 
0109 static int mcde_clk_div_set_rate(struct clk_hw *hw, unsigned long rate,
0110                   unsigned long prate)
0111 {
0112     struct mcde_clk_div *cdiv = container_of(hw, struct mcde_clk_div, hw);
0113     int div = mcde_clk_div_choose_div(hw, rate, &prate, false);
0114     u32 cr = 0;
0115 
0116     /*
0117      * We cache the CR bits to set the divide in the state so that
0118      * we can call this before we can even write to the hardware.
0119      */
0120     if (div == 1) {
0121         /* Bypass clock divider */
0122         cr |= MCDE_CRX1_BCD;
0123     } else {
0124         div -= 2;
0125         cr |= div & MCDE_CRX1_PCD_MASK;
0126     }
0127     cdiv->cr_div = cr;
0128 
0129     return 0;
0130 }
0131 
0132 static const struct clk_ops mcde_clk_div_ops = {
0133     .enable = mcde_clk_div_enable,
0134     .recalc_rate = mcde_clk_div_recalc_rate,
0135     .round_rate = mcde_clk_div_round_rate,
0136     .set_rate = mcde_clk_div_set_rate,
0137 };
0138 
0139 int mcde_init_clock_divider(struct mcde *mcde)
0140 {
0141     struct device *dev = mcde->dev;
0142     struct mcde_clk_div *fifoa;
0143     struct mcde_clk_div *fifob;
0144     const char *parent_name;
0145     struct clk_init_data fifoa_init = {
0146         .name = "fifoa",
0147         .ops = &mcde_clk_div_ops,
0148         .parent_names = &parent_name,
0149         .num_parents = 1,
0150         .flags = CLK_SET_RATE_PARENT,
0151     };
0152     struct clk_init_data fifob_init = {
0153         .name = "fifob",
0154         .ops = &mcde_clk_div_ops,
0155         .parent_names = &parent_name,
0156         .num_parents = 1,
0157         .flags = CLK_SET_RATE_PARENT,
0158     };
0159     int ret;
0160 
0161     spin_lock_init(&mcde->fifo_crx1_lock);
0162     parent_name = __clk_get_name(mcde->lcd_clk);
0163 
0164     /* Allocate 2 clocks */
0165     fifoa = devm_kzalloc(dev, sizeof(*fifoa), GFP_KERNEL);
0166     if (!fifoa)
0167         return -ENOMEM;
0168     fifob = devm_kzalloc(dev, sizeof(*fifob), GFP_KERNEL);
0169     if (!fifob)
0170         return -ENOMEM;
0171 
0172     fifoa->mcde = mcde;
0173     fifoa->cr = MCDE_CRA1;
0174     fifoa->hw.init = &fifoa_init;
0175     ret = devm_clk_hw_register(dev, &fifoa->hw);
0176     if (ret) {
0177         dev_err(dev, "error registering FIFO A clock divider\n");
0178         return ret;
0179     }
0180     mcde->fifoa_clk = fifoa->hw.clk;
0181 
0182     fifob->mcde = mcde;
0183     fifob->cr = MCDE_CRB1;
0184     fifob->hw.init = &fifob_init;
0185     ret = devm_clk_hw_register(dev, &fifob->hw);
0186     if (ret) {
0187         dev_err(dev, "error registering FIFO B clock divider\n");
0188         return ret;
0189     }
0190     mcde->fifob_clk = fifob->hw.clk;
0191 
0192     return 0;
0193 }