| From d29fb19931c596af4937aba09e4152bdf433f679 Mon Sep 17 00:00:00 2001 |
| From: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> |
| Date: Tue, 12 Mar 2019 18:18:17 +0200 |
| Subject: drm: rcar-du: lvds: Fix post-DLL divider calculation |
| |
| [ Upstream commit 167e535438ecc73d299340bb1269616432020dfb ] |
| |
| The PLL parameters are computed by looping over the range of acceptable |
| M, N and E values, and selecting the combination that produces the |
| output frequency closest to the target. The internal frequency |
| constraints are taken into account by restricting the tested values for |
| the PLL parameters, reducing the search space. The target frequency, |
| however, is only taken into account when computing the post-PLL divider, |
| which can result in a 0 value for the divider when the PLL output |
| frequency being tested is lower than half of the target frequency. |
| Subsequent loops will produce a better set of PLL parameters, but for |
| some of the iterations this can result in a division by 0. |
| |
| Fix it by clamping the divider value. We could instead restrict the E |
| values being tested in the inner loop, but that would require additional |
| calculation that would likely be less efficient as the E parameter can |
| only take three different values. |
| |
| Fixes: c25c01361199 ("drm: rcar-du: lvds: D3/E3 support") |
| Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> |
| Reviewed-by: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| drivers/gpu/drm/rcar-du/rcar_lvds.c | 2 +- |
| 1 file changed, 1 insertion(+), 1 deletion(-) |
| |
| diff --git a/drivers/gpu/drm/rcar-du/rcar_lvds.c b/drivers/gpu/drm/rcar-du/rcar_lvds.c |
| index f0314790333ba..033f44e46daf4 100644 |
| --- a/drivers/gpu/drm/rcar-du/rcar_lvds.c |
| +++ b/drivers/gpu/drm/rcar-du/rcar_lvds.c |
| @@ -283,7 +283,7 @@ static void rcar_lvds_d3_e3_pll_calc(struct rcar_lvds *lvds, struct clk *clk, |
| * divider. |
| */ |
| fout = fvco / (1 << e) / div7; |
| - div = DIV_ROUND_CLOSEST(fout, target); |
| + div = max(1UL, DIV_ROUND_CLOSEST(fout, target)); |
| diff = abs(fout / div - target); |
| |
| if (diff < pll->diff) { |
| -- |
| 2.20.1 |
| |