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; |
