diff options
Diffstat (limited to 'drivers/gpu/drm/sun4i/sun4i_dotclock.c')
-rw-r--r-- | drivers/gpu/drm/sun4i/sun4i_dotclock.c | 39 |
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, | |||
72 | static long sun4i_dclk_round_rate(struct clk_hw *hw, unsigned long rate, | 72 | static 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 | |||
98 | out: | ||
99 | *parent_rate = best_parent; | ||
100 | |||
101 | return best_parent / best_div; | ||
76 | } | 102 | } |
77 | 103 | ||
78 | static int sun4i_dclk_set_rate(struct clk_hw *hw, unsigned long rate, | 104 | static 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; |