Home | History | Annotate | Line # | Download | only in sunxi
      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 (at) antioche.eu.org>
      5  * All rights reserved.
      6  *
      7  * Copyright (c) 2014 Jared D. McNeill <jmcneill (at) 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 
     42 int
     43 sunxi_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 
     77 u_int
     78 sunxi_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 
     98 int
     99 sunxi_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