sunxi_ccu_display.c revision 1.3
1/* $NetBSD: sunxi_ccu_display.c,v 1.3 2023/08/03 08:10:40 mrg Exp $ */ 2 3/*- 4 * Copyright (c) 2018 Manuel Bouyer <bouyer@antioche.eu.org> 5 * All rights reserved. 6 * 7 * Copyright (c) 2014 Jared D. McNeill <jmcneill@invisible.ca> 8 * All rights reserved. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 24 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 26 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 27 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32#include <sys/cdefs.h> 33__KERNEL_RCSID(0, "$NetBSD: sunxi_ccu_display.c,v 1.3 2023/08/03 08:10:40 mrg Exp $"); 34 35#include <sys/param.h> 36#include <sys/bus.h> 37 38#include <dev/clk/clk_backend.h> 39 40#include <arm/sunxi/sunxi_ccu.h> 41 42int 43sunxi_ccu_lcdxch0_set_rate(struct sunxi_ccu_softc *sc, 44 struct sunxi_ccu_clk *clk, struct sunxi_ccu_clk *pllclk, 45 struct sunxi_ccu_clk *pllclk_x2, u_int new_rate) 46{ 47 struct clk *clkp; 48 int error; 49 int diff, diff_x2; 50 int rate, rate_x2; 51 52 clkp = &pllclk->base; 53 rate = clk_round_rate(clkp, new_rate); 54 diff = abs(new_rate - rate); 55 56 rate_x2 = (clk_round_rate(clkp, new_rate / 2) * 2); 57 diff_x2 = abs(new_rate - rate_x2); 58 59 if (rate == 0 && rate_x2 == 0) 60 return ERANGE; 61 62 if (diff_x2 < diff) { 63 error = clk_set_rate(clkp, new_rate / 2); 64 KASSERT(error == 0); 65 error = clk_set_parent(&clk->base, &pllclk_x2->base); 66 KASSERT(error == 0); 67 } else { 68 error = clk_set_rate(clkp, new_rate); 69 KASSERT(error == 0); 70 error = clk_set_parent(&clk->base, clkp); 71 KASSERT(error == 0); 72 } 73 (void)error; 74 return 0; 75} 76 77u_int 78sunxi_ccu_lcdxch0_round_rate(struct sunxi_ccu_softc *sc, 79 struct sunxi_ccu_clk * clk, struct sunxi_ccu_clk *pllclk, 80 struct sunxi_ccu_clk *pllclk_x2, u_int try_rate) 81{ 82 struct clk *clkp; 83 int diff, diff_x2; 84 int rate, rate_x2; 85 86 clkp = &pllclk->base; 87 rate = clk_round_rate(clkp, try_rate); 88 diff = abs(try_rate - rate); 89 90 rate_x2 = (clk_round_rate(clkp, try_rate / 2) * 2); 91 diff_x2 = abs(try_rate - rate_x2); 92 93 if (diff_x2 < diff) 94 return rate_x2; 95 return rate; 96} 97 98int 99sunxi_ccu_lcdxch1_set_rate(struct sunxi_ccu_softc *sc, 100 struct sunxi_ccu_clk * clk, struct sunxi_ccu_clk *pllclk, 101 struct sunxi_ccu_clk *pllclk_x2, u_int new_rate) 102{ 103 struct clk *clkp, *pllclkp; 104 int best_diff; 105 int parent_rate, best_parent_rate = 0; 106 uint32_t best_m, best_d; 107 int error; 108 109 pllclkp = clkp = &pllclk->base; 110 best_diff = INT_MAX; 111 best_m = best_d = 0; 112 for (uint32_t d = 1; d <= 2 && best_diff != 0; d++) { 113 for (uint32_t m = 1; m <= 16 && best_diff != 0; m++) { 114 int rate, diff; 115 parent_rate = clk_round_rate(pllclkp, 116 new_rate * m / d); 117 if (parent_rate == 0) 118 continue; 119 rate = parent_rate * d / m; 120 diff = abs(rate - new_rate); 121 if (diff < best_diff) { 122 best_diff = diff; 123 best_m = m; 124 best_d = d; 125 best_parent_rate = parent_rate; 126 } 127 } 128 } 129 if (best_m == 0) 130 return ERANGE; 131 132 if (best_d == 2) 133 clkp = &pllclk_x2->base; 134 135 error = clk_set_rate(pllclkp, best_parent_rate); 136 KASSERT(error == 0); 137 error = clk_set_parent(&clk->base, clkp); 138 KASSERT(error == 0); 139 error = clk_enable(clkp); 140 KASSERT(error == 0); 141 error = sunxi_ccu_div_set_rate(sc, clk, new_rate); 142 KASSERT(error == 0); 143 return error; 144} 145