mcde_clk_div.c (4658B)
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}