aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/sun4i/sun4i_dotclock.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/sun4i/sun4i_dotclock.c')
-rw-r--r--drivers/gpu/drm/sun4i/sun4i_dotclock.c39
1 files changed, 35 insertions, 4 deletions
diff --git a/drivers/gpu/drm/sun4i/sun4i_dotclock.c b/drivers/gpu/drm/sun4i/sun4i_dotclock.c
index 3ff668cb463c..5b3463197c48 100644
--- a/drivers/gpu/drm/sun4i/sun4i_dotclock.c
+++ b/drivers/gpu/drm/sun4i/sun4i_dotclock.c
@@ -72,14 +72,40 @@ static unsigned long sun4i_dclk_recalc_rate(struct clk_hw *hw,
72static long sun4i_dclk_round_rate(struct clk_hw *hw, unsigned long rate, 72static long sun4i_dclk_round_rate(struct clk_hw *hw, unsigned long rate,
73 unsigned long *parent_rate) 73 unsigned long *parent_rate)
74{ 74{
75 return *parent_rate / DIV_ROUND_CLOSEST(*parent_rate, rate); 75 unsigned long best_parent = 0;
76 u8 best_div = 1;
77 int i;
78
79 for (i = 6; i < 127; i++) {
80 unsigned long ideal = rate * i;
81 unsigned long rounded;
82
83 rounded = clk_hw_round_rate(clk_hw_get_parent(hw),
84 ideal);
85
86 if (rounded == ideal) {
87 best_parent = rounded;
88 best_div = i;
89 goto out;
90 }
91
92 if ((rounded < ideal) && (rounded > best_parent)) {
93 best_parent = rounded;
94 best_div = i;
95 }
96 }
97
98out:
99 *parent_rate = best_parent;
100
101 return best_parent / best_div;
76} 102}
77 103
78static int sun4i_dclk_set_rate(struct clk_hw *hw, unsigned long rate, 104static int sun4i_dclk_set_rate(struct clk_hw *hw, unsigned long rate,
79 unsigned long parent_rate) 105 unsigned long parent_rate)
80{ 106{
81 struct sun4i_dclk *dclk = hw_to_dclk(hw); 107 struct sun4i_dclk *dclk = hw_to_dclk(hw);
82 int div = DIV_ROUND_CLOSEST(parent_rate, rate); 108 u8 div = parent_rate / rate;
83 109
84 return regmap_update_bits(dclk->regmap, SUN4I_TCON0_DCLK_REG, 110 return regmap_update_bits(dclk->regmap, SUN4I_TCON0_DCLK_REG,
85 GENMASK(6, 0), div); 111 GENMASK(6, 0), div);
@@ -127,10 +153,14 @@ int sun4i_dclk_create(struct device *dev, struct sun4i_tcon *tcon)
127 const char *clk_name, *parent_name; 153 const char *clk_name, *parent_name;
128 struct clk_init_data init; 154 struct clk_init_data init;
129 struct sun4i_dclk *dclk; 155 struct sun4i_dclk *dclk;
156 int ret;
130 157
131 parent_name = __clk_get_name(tcon->sclk0); 158 parent_name = __clk_get_name(tcon->sclk0);
132 of_property_read_string_index(dev->of_node, "clock-output-names", 0, 159 ret = of_property_read_string_index(dev->of_node,
133 &clk_name); 160 "clock-output-names", 0,
161 &clk_name);
162 if (ret)
163 return ret;
134 164
135 dclk = devm_kzalloc(dev, sizeof(*dclk), GFP_KERNEL); 165 dclk = devm_kzalloc(dev, sizeof(*dclk), GFP_KERNEL);
136 if (!dclk) 166 if (!dclk)
@@ -140,6 +170,7 @@ int sun4i_dclk_create(struct device *dev, struct sun4i_tcon *tcon)
140 init.ops = &sun4i_dclk_ops; 170 init.ops = &sun4i_dclk_ops;
141 init.parent_names = &parent_name; 171 init.parent_names = &parent_name;
142 init.num_parents = 1; 172 init.num_parents = 1;
173 init.flags = CLK_SET_RATE_PARENT;
143 174
144 dclk->regmap = tcon->regs; 175 dclk->regmap = tcon->regs;
145 dclk->hw.init = &init; 176 dclk->hw.init = &init;