0001
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
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
0027
0028
0029 val &= ~MCDE_CRX1_CLKSEL_MASK;
0030 val |= MCDE_CRX1_CLKSEL_CLKPLL72 << MCDE_CRX1_CLKSEL_SHIFT;
0031
0032 val |= MCDE_CRA1_CLKTYPE_TVXCLKSEL1;
0033
0034
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
0092
0093
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
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
0118
0119
0120 if (div == 1) {
0121
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
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 }