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