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