diff options
Diffstat (limited to 'firmware/target/mips/ingenic_x1000/aic-x1000.c')
-rw-r--r-- | firmware/target/mips/ingenic_x1000/aic-x1000.c | 119 |
1 files changed, 119 insertions, 0 deletions
diff --git a/firmware/target/mips/ingenic_x1000/aic-x1000.c b/firmware/target/mips/ingenic_x1000/aic-x1000.c new file mode 100644 index 0000000000..a0e509d3b6 --- /dev/null +++ b/firmware/target/mips/ingenic_x1000/aic-x1000.c | |||
@@ -0,0 +1,119 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2021 Aidan MacDonald | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | |||
22 | #include "system.h" | ||
23 | #include "aic-x1000.h" | ||
24 | #include "gpio-x1000.h" | ||
25 | #include "x1000/aic.h" | ||
26 | #include "x1000/cpm.h" | ||
27 | |||
28 | /* Given a rational number m/n < 1, find its representation as a continued | ||
29 | * fraction [0; a1, a2, a3, ..., a_k]. At most "cnt" terms are calculated | ||
30 | * and written out to "buf". Returns the number of terms written; the result | ||
31 | * is complete if this value is less than "cnt", and may be incomplete if it | ||
32 | * is equal to "cnt". (Note the leading zero term is not written to "buf".) | ||
33 | */ | ||
34 | static unsigned cf_derive(unsigned m, unsigned n, unsigned* buf, unsigned cnt) | ||
35 | { | ||
36 | unsigned wrote = 0; | ||
37 | unsigned a = m / n; | ||
38 | while(cnt--) { | ||
39 | unsigned tmp = n; | ||
40 | n = m - n * a; | ||
41 | if(n == 0) | ||
42 | break; | ||
43 | |||
44 | m = tmp; | ||
45 | a = m / n; | ||
46 | *buf++ = a; | ||
47 | wrote++; | ||
48 | } | ||
49 | |||
50 | return wrote; | ||
51 | } | ||
52 | |||
53 | /* Given a finite continued fraction [0; buf[0], buf[1], ..., buf[count-1]], | ||
54 | * calculate the rational number m/n which it represents. Returns m and n. | ||
55 | * If count is zero, then m and n are undefined. | ||
56 | */ | ||
57 | static void cf_expand(const unsigned* buf, unsigned count, | ||
58 | unsigned* m, unsigned* n) | ||
59 | { | ||
60 | if(count == 0) | ||
61 | return; | ||
62 | |||
63 | unsigned i = count - 1; | ||
64 | unsigned mx = 1, nx = buf[i]; | ||
65 | while(i--) { | ||
66 | unsigned tmp = nx; | ||
67 | nx = mx + buf[i] * nx; | ||
68 | mx = tmp; | ||
69 | } | ||
70 | |||
71 | *m = mx; | ||
72 | *n = nx; | ||
73 | } | ||
74 | |||
75 | int aic_i2s_set_mclk(x1000_clk_t clksrc, unsigned fs, unsigned mult) | ||
76 | { | ||
77 | /* get the input clock rate */ | ||
78 | uint32_t src_freq = clk_get(clksrc); | ||
79 | |||
80 | /* reject invalid parameters */ | ||
81 | if(mult % 64 != 0) | ||
82 | return -1; | ||
83 | |||
84 | if(clksrc == X1000_EXCLK_FREQ) { | ||
85 | if(mult != 0) | ||
86 | return -1; | ||
87 | |||
88 | jz_writef(AIC_I2SCR, STPBK(1)); | ||
89 | jz_writef(CPM_I2SCDR, CS(0), CE(0)); | ||
90 | REG_AIC_I2SDIV = X1000_EXCLK_FREQ / 64 / fs; | ||
91 | } else { | ||
92 | if(mult == 0) | ||
93 | return -1; | ||
94 | if(fs*mult > src_freq) | ||
95 | return -1; | ||
96 | |||
97 | /* calculate best rational approximation that fits our constraints */ | ||
98 | unsigned m = 0, n = 0; | ||
99 | unsigned buf[16]; | ||
100 | unsigned cnt = cf_derive(fs*mult, src_freq, &buf[0], 16); | ||
101 | do { | ||
102 | cf_expand(&buf[0], cnt, &m, &n); | ||
103 | cnt -= 1; | ||
104 | } while(cnt > 0 && (m > 512 || n > 8192) && (n >= 2*m)); | ||
105 | |||
106 | /* wrong values */ | ||
107 | if(cnt == 0 || n == 0 || m == 0) | ||
108 | return -1; | ||
109 | |||
110 | jz_writef(AIC_I2SCR, STPBK(1)); | ||
111 | jz_writef(CPM_I2SCDR, PCS(clksrc == X1000_CLK_MPLL ? 1 : 0), | ||
112 | CS(1), CE(1), DIV_M(m), DIV_N(n)); | ||
113 | jz_write(CPM_I2SCDR1, REG_CPM_I2SCDR1); | ||
114 | REG_AIC_I2SDIV = (mult / 64) - 1; | ||
115 | } | ||
116 | |||
117 | jz_writef(AIC_I2SCR, STPBK(0)); | ||
118 | return 0; | ||
119 | } | ||