xref: /linux/drivers/gpu/drm/mcde/mcde_clk_div.c (revision 0be3ff0c)
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 }
193