GNU Linux-libre 5.19-rc6-gnu
[releases.git] / drivers / gpu / drm / mcde / mcde_clk_div.c
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 */
9 struct mcde_clk_div {
10         struct clk_hw hw;
11         struct mcde *mcde;
12         u32 cr;
13         u32 cr_div;
14 };
15
16 static 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
43 static 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
73 static 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
81 static 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
108 static 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
131 static 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
138 int 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 }