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(lock: &mcde->fifo_crx1_lock); |
24 | val = readl(addr: 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, addr: mcde->regs + cdiv->cr); |
39 | spin_unlock(lock: &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(hw: parent, rate: 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 long mcde_clk_div_round_rate(struct clk_hw *hw, unsigned long rate, |
75 | unsigned long *prate) |
76 | { |
77 | int div = mcde_clk_div_choose_div(hw, rate, prate, set_parent: true); |
78 | |
79 | return DIV_ROUND_UP_ULL(*prate, div); |
80 | } |
81 | |
82 | static unsigned long mcde_clk_div_recalc_rate(struct clk_hw *hw, |
83 | unsigned long prate) |
84 | { |
85 | struct mcde_clk_div *cdiv = container_of(hw, struct mcde_clk_div, hw); |
86 | struct mcde *mcde = cdiv->mcde; |
87 | u32 cr; |
88 | int div; |
89 | |
90 | /* |
91 | * If the MCDE is not powered we can't access registers. |
92 | * It will come up with 0 in the divider register bits, which |
93 | * means "divide by 2". |
94 | */ |
95 | if (!regulator_is_enabled(regulator: mcde->epod)) |
96 | return DIV_ROUND_UP_ULL(prate, 2); |
97 | |
98 | cr = readl(addr: mcde->regs + cdiv->cr); |
99 | if (cr & MCDE_CRX1_BCD) |
100 | return prate; |
101 | |
102 | /* 0 in the PCD means "divide by 2", 1 means "divide by 3" etc */ |
103 | div = cr & MCDE_CRX1_PCD_MASK; |
104 | div += 2; |
105 | |
106 | return DIV_ROUND_UP_ULL(prate, div); |
107 | } |
108 | |
109 | static int mcde_clk_div_set_rate(struct clk_hw *hw, unsigned long rate, |
110 | unsigned long prate) |
111 | { |
112 | struct mcde_clk_div *cdiv = container_of(hw, struct mcde_clk_div, hw); |
113 | int div = mcde_clk_div_choose_div(hw, rate, prate: &prate, set_parent: false); |
114 | u32 cr = 0; |
115 | |
116 | /* |
117 | * We cache the CR bits to set the divide in the state so that |
118 | * we can call this before we can even write to the hardware. |
119 | */ |
120 | if (div == 1) { |
121 | /* Bypass clock divider */ |
122 | cr |= MCDE_CRX1_BCD; |
123 | } else { |
124 | div -= 2; |
125 | cr |= div & MCDE_CRX1_PCD_MASK; |
126 | } |
127 | cdiv->cr_div = cr; |
128 | |
129 | return 0; |
130 | } |
131 | |
132 | static const struct clk_ops mcde_clk_div_ops = { |
133 | .enable = mcde_clk_div_enable, |
134 | .recalc_rate = mcde_clk_div_recalc_rate, |
135 | .round_rate = mcde_clk_div_round_rate, |
136 | .set_rate = mcde_clk_div_set_rate, |
137 | }; |
138 | |
139 | int mcde_init_clock_divider(struct mcde *mcde) |
140 | { |
141 | struct device *dev = mcde->dev; |
142 | struct mcde_clk_div *fifoa; |
143 | struct mcde_clk_div *fifob; |
144 | const char *parent_name; |
145 | struct clk_init_data fifoa_init = { |
146 | .name = "fifoa" , |
147 | .ops = &mcde_clk_div_ops, |
148 | .parent_names = &parent_name, |
149 | .num_parents = 1, |
150 | .flags = CLK_SET_RATE_PARENT, |
151 | }; |
152 | struct clk_init_data fifob_init = { |
153 | .name = "fifob" , |
154 | .ops = &mcde_clk_div_ops, |
155 | .parent_names = &parent_name, |
156 | .num_parents = 1, |
157 | .flags = CLK_SET_RATE_PARENT, |
158 | }; |
159 | int ret; |
160 | |
161 | spin_lock_init(&mcde->fifo_crx1_lock); |
162 | parent_name = __clk_get_name(clk: mcde->lcd_clk); |
163 | |
164 | /* Allocate 2 clocks */ |
165 | fifoa = devm_kzalloc(dev, size: sizeof(*fifoa), GFP_KERNEL); |
166 | if (!fifoa) |
167 | return -ENOMEM; |
168 | fifob = devm_kzalloc(dev, size: sizeof(*fifob), GFP_KERNEL); |
169 | if (!fifob) |
170 | return -ENOMEM; |
171 | |
172 | fifoa->mcde = mcde; |
173 | fifoa->cr = MCDE_CRA1; |
174 | fifoa->hw.init = &fifoa_init; |
175 | ret = devm_clk_hw_register(dev, hw: &fifoa->hw); |
176 | if (ret) { |
177 | dev_err(dev, "error registering FIFO A clock divider\n" ); |
178 | return ret; |
179 | } |
180 | mcde->fifoa_clk = fifoa->hw.clk; |
181 | |
182 | fifob->mcde = mcde; |
183 | fifob->cr = MCDE_CRB1; |
184 | fifob->hw.init = &fifob_init; |
185 | ret = devm_clk_hw_register(dev, hw: &fifob->hw); |
186 | if (ret) { |
187 | dev_err(dev, "error registering FIFO B clock divider\n" ); |
188 | return ret; |
189 | } |
190 | mcde->fifob_clk = fifob->hw.clk; |
191 | |
192 | return 0; |
193 | } |
194 | |